From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- sd/source/core/CustomAnimationCloner.cxx | 307 ++ sd/source/core/CustomAnimationEffect.cxx | 3559 +++++++++++++++++ sd/source/core/CustomAnimationPreset.cxx | 514 +++ sd/source/core/EffectMigration.cxx | 1439 +++++++ sd/source/core/PageListWatcher.cxx | 217 ++ sd/source/core/PageListWatcher.hxx | 87 + sd/source/core/TransitionPreset.cxx | 385 ++ sd/source/core/anminfo.cxx | 128 + sd/source/core/annotations/Annotation.cxx | 480 +++ .../core/annotations/AnnotationEnumeration.cxx | 85 + sd/source/core/cusshow.cxx | 101 + sd/source/core/drawdoc.cxx | 1206 ++++++ sd/source/core/drawdoc2.cxx | 1382 +++++++ sd/source/core/drawdoc3.cxx | 1873 +++++++++ sd/source/core/drawdoc4.cxx | 1399 +++++++ sd/source/core/drawdoc_animations.cxx | 54 + sd/source/core/pglink.cxx | 128 + sd/source/core/sdiocmpt.cxx | 117 + sd/source/core/sdpage.cxx | 3157 +++++++++++++++ sd/source/core/sdpage2.cxx | 651 ++++ sd/source/core/sdpage_animations.cxx | 160 + sd/source/core/shapelist.cxx | 140 + sd/source/core/stlfamily.cxx | 513 +++ sd/source/core/stlpool.cxx | 1395 +++++++ sd/source/core/stlsheet.cxx | 1459 +++++++ sd/source/core/text/textapi.cxx | 278 ++ sd/source/core/typemap.cxx | 143 + sd/source/core/undo/undofactory.cxx | 55 + sd/source/core/undo/undomanager.cxx | 58 + sd/source/core/undo/undoobjects.cxx | 392 ++ sd/source/core/undoanim.cxx | 280 ++ sd/source/filter/cgm/sdcgmfilter.cxx | 137 + sd/source/filter/eppt/eppt.cxx | 1464 +++++++ sd/source/filter/eppt/eppt.hxx | 232 ++ sd/source/filter/eppt/epptbase.hxx | 412 ++ sd/source/filter/eppt/epptdef.hxx | 145 + sd/source/filter/eppt/epptooxml.hxx | 189 + sd/source/filter/eppt/epptso.cxx | 3361 ++++++++++++++++ sd/source/filter/eppt/escherex.cxx | 266 ++ sd/source/filter/eppt/escherex.hxx | 64 + sd/source/filter/eppt/grouptable.hxx | 69 + sd/source/filter/eppt/pptexanimations.cxx | 2150 +++++++++++ sd/source/filter/eppt/pptexanimations.hxx | 134 + sd/source/filter/eppt/pptexsoundcollection.cxx | 213 ++ sd/source/filter/eppt/pptexsoundcollection.hxx | 71 + sd/source/filter/eppt/pptx-animations.cxx | 1539 ++++++++ sd/source/filter/eppt/pptx-animations.hxx | 25 + sd/source/filter/eppt/pptx-epptbase.cxx | 1000 +++++ sd/source/filter/eppt/pptx-epptooxml.cxx | 2594 +++++++++++++ sd/source/filter/eppt/pptx-grouptable.cxx | 85 + sd/source/filter/eppt/pptx-stylesheet.cxx | 489 +++ sd/source/filter/eppt/pptx-text.cxx | 1400 +++++++ sd/source/filter/eppt/text.hxx | 254 ++ sd/source/filter/grf/sdgrffilter.cxx | 304 ++ sd/source/filter/html/HtmlOptionsDialog.cxx | 203 + sd/source/filter/html/buttonset.cxx | 290 ++ sd/source/filter/html/buttonset.hxx | 46 + sd/source/filter/html/htmlattr.cxx | 72 + sd/source/filter/html/htmlattr.hxx | 40 + sd/source/filter/html/htmlex.cxx | 3186 ++++++++++++++++ sd/source/filter/html/htmlex.hxx | 237 ++ sd/source/filter/html/htmlpublishmode.hxx | 31 + sd/source/filter/html/pubdlg.cxx | 1539 ++++++++ sd/source/filter/html/sdhtmlfilter.cxx | 51 + sd/source/filter/pdf/sdpdffilter.cxx | 201 + sd/source/filter/ppt/ppt97animations.cxx | 682 ++++ sd/source/filter/ppt/ppt97animations.hxx | 156 + sd/source/filter/ppt/pptanimations.hxx | 209 + sd/source/filter/ppt/pptatom.cxx | 104 + sd/source/filter/ppt/pptatom.hxx | 106 + sd/source/filter/ppt/pptin.cxx | 2821 ++++++++++++++ sd/source/filter/ppt/pptin.hxx | 92 + sd/source/filter/ppt/pptinanimations.cxx | 3294 ++++++++++++++++ sd/source/filter/ppt/pptinanimations.hxx | 115 + sd/source/filter/ppt/propread.cxx | 615 +++ sd/source/filter/ppt/propread.hxx | 151 + sd/source/filter/sdfilter.cxx | 108 + sd/source/filter/sdpptwrp.cxx | 377 ++ sd/source/filter/xml/sdtransform.cxx | 368 ++ sd/source/filter/xml/sdtransform.hxx | 28 + sd/source/filter/xml/sdxmlwrp.cxx | 1056 ++++++ sd/source/helper/simplereferencecomponent.cxx | 72 + .../accessibility/AccessibleDocumentViewBase.cxx | 773 ++++ .../accessibility/AccessibleDrawDocumentView.cxx | 777 ++++ .../accessibility/AccessibleOutlineEditSource.cxx | 199 + .../ui/accessibility/AccessibleOutlineView.cxx | 238 ++ sd/source/ui/accessibility/AccessiblePageShape.cxx | 261 ++ .../AccessiblePresentationGraphicShape.cxx | 76 + .../AccessiblePresentationOLEShape.cxx | 84 + .../accessibility/AccessiblePresentationShape.cxx | 146 + .../accessibility/AccessibleSlideSorterObject.cxx | 429 +++ .../ui/accessibility/AccessibleSlideSorterView.cxx | 950 +++++ .../ui/accessibility/AccessibleViewForwarder.cxx | 104 + sd/source/ui/accessibility/SdShapeTypes.cxx | 132 + sd/source/ui/animations/CustomAnimationDialog.cxx | 2090 ++++++++++ sd/source/ui/animations/CustomAnimationDialog.hxx | 141 + sd/source/ui/animations/CustomAnimationList.cxx | 1231 ++++++ sd/source/ui/animations/CustomAnimationPane.cxx | 2578 +++++++++++++ sd/source/ui/animations/STLPropertySet.cxx | 113 + sd/source/ui/animations/STLPropertySet.hxx | 73 + sd/source/ui/animations/SlideTransitionPane.cxx | 1155 ++++++ sd/source/ui/animations/motionpathtag.cxx | 1200 ++++++ sd/source/ui/animations/motionpathtag.hxx | 114 + sd/source/ui/annotations/annotationmanager.cxx | 1220 ++++++ sd/source/ui/annotations/annotationmanagerimpl.hxx | 141 + sd/source/ui/annotations/annotationtag.cxx | 662 ++++ sd/source/ui/annotations/annotationtag.hxx | 89 + sd/source/ui/annotations/annotationwindow.cxx | 802 ++++ sd/source/ui/annotations/annotationwindow.hxx | 143 + sd/source/ui/app/optsitem.cxx | 1407 +++++++ sd/source/ui/app/scalectrl.cxx | 108 + sd/source/ui/app/sddll.cxx | 269 ++ sd/source/ui/app/sdmod.cxx | 216 ++ sd/source/ui/app/sdmod1.cxx | 638 ++++ sd/source/ui/app/sdmod2.cxx | 809 ++++ sd/source/ui/app/sdpopup.cxx | 318 ++ sd/source/ui/app/sdxfer.cxx | 807 ++++ sd/source/ui/app/tmplctrl.cxx | 110 + sd/source/ui/controller/displaymodecontroller.cxx | 264 ++ sd/source/ui/controller/slidelayoutcontroller.cxx | 380 ++ sd/source/ui/controller/slidelayoutcontroller.hxx | 47 + sd/source/ui/dlg/AnimationChildWindow.cxx | 50 + sd/source/ui/dlg/BulletAndPositionDlg.cxx | 1293 +++++++ sd/source/ui/dlg/LayerTabBar.cxx | 437 +++ sd/source/ui/dlg/NavigatorChildWindow.cxx | 100 + sd/source/ui/dlg/PaneChildWindows.cxx | 107 + sd/source/ui/dlg/PaneDockingWindow.cxx | 127 + sd/source/ui/dlg/PaneShells.cxx | 79 + sd/source/ui/dlg/PhotoAlbumDialog.cxx | 775 ++++ sd/source/ui/dlg/PhotoAlbumDialog.hxx | 91 + sd/source/ui/dlg/RemoteDialog.cxx | 49 + sd/source/ui/dlg/RemoteDialog.hxx | 32 + sd/source/ui/dlg/RemoteDialogClientBox.cxx | 134 + sd/source/ui/dlg/RemoteDialogClientBox.hxx | 85 + sd/source/ui/dlg/SpellDialogChildWindow.cxx | 172 + sd/source/ui/dlg/TemplateScanner.cxx | 342 ++ sd/source/ui/dlg/animobjs.cxx | 1134 ++++++ sd/source/ui/dlg/assclass.cxx | 160 + sd/source/ui/dlg/brkdlg.cxx | 156 + sd/source/ui/dlg/copydlg.cxx | 263 ++ sd/source/ui/dlg/custsdlg.cxx | 478 +++ sd/source/ui/dlg/diactrl.cxx | 185 + sd/source/ui/dlg/dlgchar.cxx | 70 + sd/source/ui/dlg/dlgfield.cxx | 301 ++ sd/source/ui/dlg/dlgolbul.cxx | 172 + sd/source/ui/dlg/dlgpage.cxx | 116 + sd/source/ui/dlg/dlgsnap.cxx | 185 + sd/source/ui/dlg/filedlg.cxx | 267 ++ sd/source/ui/dlg/gluectrl.cxx | 200 + sd/source/ui/dlg/headerfooterdlg.cxx | 759 ++++ sd/source/ui/dlg/ins_paste.cxx | 34 + sd/source/ui/dlg/inspagob.cxx | 126 + sd/source/ui/dlg/layeroptionsdlg.cxx | 62 + sd/source/ui/dlg/masterlayoutdlg.cxx | 133 + sd/source/ui/dlg/morphdlg.cxx | 107 + sd/source/ui/dlg/navigatr.cxx | 735 ++++ sd/source/ui/dlg/paragr.cxx | 169 + sd/source/ui/dlg/present.cxx | 323 ++ sd/source/ui/dlg/prltempl.cxx | 305 ++ sd/source/ui/dlg/prntopts.cxx | 235 ++ sd/source/ui/dlg/sdabstdlg.cxx | 55 + sd/source/ui/dlg/sddlgfact.cxx | 739 ++++ sd/source/ui/dlg/sddlgfact.hxx | 448 +++ sd/source/ui/dlg/sdpreslt.cxx | 267 ++ sd/source/ui/dlg/sdtreelb.cxx | 1206 ++++++ sd/source/ui/dlg/sduiexp.cxx | 33 + sd/source/ui/dlg/tabtempl.cxx | 160 + sd/source/ui/dlg/titledockwin.cxx | 261 ++ sd/source/ui/dlg/tpaction.cxx | 801 ++++ sd/source/ui/dlg/tpoption.cxx | 618 +++ sd/source/ui/dlg/unchss.cxx | 119 + sd/source/ui/dlg/vectdlg.cxx | 336 ++ sd/source/ui/docshell/docshel2.cxx | 416 ++ sd/source/ui/docshell/docshel3.cxx | 443 +++ sd/source/ui/docshell/docshel4.cxx | 1002 +++++ sd/source/ui/docshell/docshell.cxx | 515 +++ sd/source/ui/docshell/grdocsh.cxx | 61 + sd/source/ui/docshell/sdclient.cxx | 184 + .../framework/configuration/ChangeRequestQueue.cxx | 28 + .../framework/configuration/ChangeRequestQueue.hxx | 48 + .../configuration/ChangeRequestQueueProcessor.cxx | 180 + .../configuration/ChangeRequestQueueProcessor.hxx | 126 + .../ui/framework/configuration/Configuration.cxx | 311 ++ .../configuration/ConfigurationClassifier.cxx | 167 + .../configuration/ConfigurationClassifier.hxx | 165 + .../configuration/ConfigurationController.cxx | 541 +++ .../ConfigurationControllerBroadcaster.cxx | 192 + .../ConfigurationControllerBroadcaster.hxx | 138 + .../ConfigurationControllerResourceManager.cxx | 303 ++ .../ConfigurationControllerResourceManager.hxx | 141 + .../configuration/ConfigurationTracer.cxx | 73 + .../configuration/ConfigurationTracer.hxx | 58 + .../configuration/ConfigurationUpdater.cxx | 376 ++ .../configuration/ConfigurationUpdater.hxx | 209 + .../GenericConfigurationChangeRequest.cxx | 81 + .../GenericConfigurationChangeRequest.hxx | 98 + .../configuration/ResourceFactoryManager.cxx | 197 + .../configuration/ResourceFactoryManager.hxx | 120 + .../ui/framework/configuration/ResourceId.cxx | 503 +++ .../ui/framework/configuration/UpdateRequest.cxx | 47 + .../ui/framework/configuration/UpdateRequest.hxx | 70 + .../ui/framework/configuration/debugtrace.hxx | 15 + .../ui/framework/factories/BasicPaneFactory.cxx | 432 +++ .../ui/framework/factories/BasicPaneFactory.hxx | 131 + .../ui/framework/factories/BasicToolBarFactory.cxx | 161 + .../ui/framework/factories/BasicToolBarFactory.hxx | 84 + .../ui/framework/factories/BasicViewFactory.cxx | 518 +++ .../ui/framework/factories/BasicViewFactory.hxx | 129 + .../ui/framework/factories/ChildWindowPane.cxx | 219 ++ .../ui/framework/factories/ChildWindowPane.hxx | 101 + .../ui/framework/factories/FrameWindowPane.cxx | 39 + .../ui/framework/factories/FrameWindowPane.hxx | 50 + .../ui/framework/factories/FullScreenPane.cxx | 226 ++ .../ui/framework/factories/FullScreenPane.hxx | 85 + sd/source/ui/framework/factories/Pane.cxx | 178 + .../ui/framework/factories/PresentationFactory.cxx | 192 + .../ui/framework/factories/ViewShellWrapper.cxx | 252 ++ .../ui/framework/module/CenterViewFocusModule.cxx | 151 + .../ui/framework/module/CenterViewFocusModule.hxx | 90 + sd/source/ui/framework/module/DrawModule.cxx | 41 + sd/source/ui/framework/module/ImpressModule.cxx | 51 + sd/source/ui/framework/module/ModuleController.cxx | 244 ++ .../ui/framework/module/PresentationModule.cxx | 36 + sd/source/ui/framework/module/ShellStackGuard.cxx | 150 + sd/source/ui/framework/module/ShellStackGuard.hxx | 94 + .../ui/framework/module/SlideSorterModule.cxx | 313 ++ .../ui/framework/module/SlideSorterModule.hxx | 97 + sd/source/ui/framework/module/ToolBarModule.cxx | 191 + sd/source/ui/framework/module/ToolBarModule.hxx | 81 + sd/source/ui/framework/module/ViewTabBarModule.cxx | 180 + sd/source/ui/framework/module/ViewTabBarModule.hxx | 83 + sd/source/ui/framework/tools/FrameworkHelper.cxx | 952 +++++ sd/source/ui/func/bulmaper.cxx | 104 + sd/source/ui/func/fuarea.cxx | 99 + sd/source/ui/func/fubullet.cxx | 330 ++ sd/source/ui/func/fuchar.cxx | 139 + sd/source/ui/func/fucon3d.cxx | 474 +++ sd/source/ui/func/fuconarc.cxx | 254 ++ sd/source/ui/func/fuconbez.cxx | 556 +++ sd/source/ui/func/fuconcs.cxx | 261 ++ sd/source/ui/func/fuconnct.cxx | 71 + sd/source/ui/func/fuconrec.cxx | 1096 ++++++ sd/source/ui/func/fuconstr.cxx | 414 ++ sd/source/ui/func/fuconuno.cxx | 150 + sd/source/ui/func/fucopy.cxx | 288 ++ sd/source/ui/func/fucushow.cxx | 91 + sd/source/ui/func/fudraw.cxx | 820 ++++ sd/source/ui/func/fudspord.cxx | 131 + sd/source/ui/func/fuediglu.cxx | 471 +++ sd/source/ui/func/fuexecuteinteraction.cxx | 237 ++ sd/source/ui/func/fuexpand.cxx | 256 ++ sd/source/ui/func/fuformatpaintbrush.cxx | 276 ++ sd/source/ui/func/fuhhconv.cxx | 256 ++ sd/source/ui/func/fuinsert.cxx | 767 ++++ sd/source/ui/func/fuinsfil.cxx | 725 ++++ sd/source/ui/func/fuline.cxx | 109 + sd/source/ui/func/fulinend.cxx | 154 + sd/source/ui/func/fulink.cxx | 65 + sd/source/ui/func/fumeasur.cxx | 72 + sd/source/ui/func/fumorph.cxx | 508 +++ sd/source/ui/func/funavig.cxx | 154 + sd/source/ui/func/fuoaprms.cxx | 800 ++++ sd/source/ui/func/fuolbull.cxx | 340 ++ sd/source/ui/func/fuoltext.cxx | 305 ++ sd/source/ui/func/fupage.cxx | 648 ++++ sd/source/ui/func/fuparagr.cxx | 162 + sd/source/ui/func/fupoor.cxx | 1135 ++++++ sd/source/ui/func/fuprlout.cxx | 277 ++ sd/source/ui/func/fuprobjs.cxx | 154 + sd/source/ui/func/fuscale.cxx | 179 + sd/source/ui/func/fusearch.cxx | 140 + sd/source/ui/func/fusel.cxx | 1328 +++++++ sd/source/ui/func/fusldlg.cxx | 226 ++ sd/source/ui/func/fusnapln.cxx | 196 + sd/source/ui/func/fusumry.cxx | 229 ++ sd/source/ui/func/futempl.cxx | 638 ++++ sd/source/ui/func/futext.cxx | 1464 +++++++ sd/source/ui/func/futhes.cxx | 132 + sd/source/ui/func/futransf.cxx | 132 + sd/source/ui/func/futxtatt.cxx | 80 + sd/source/ui/func/fuvect.cxx | 87 + sd/source/ui/func/fuzoom.cxx | 219 ++ sd/source/ui/func/sdundogr.cxx | 66 + sd/source/ui/func/smarttag.cxx | 333 ++ sd/source/ui/func/undoback.cxx | 105 + sd/source/ui/func/undoheaderfooter.cxx | 53 + sd/source/ui/func/undolayer.cxx | 78 + sd/source/ui/func/undopage.cxx | 99 + sd/source/ui/func/unmovss.cxx | 95 + sd/source/ui/func/unoaprms.cxx | 96 + sd/source/ui/func/unprlout.cxx | 73 + sd/source/ui/inc/AccessibleDocumentViewBase.hxx | 324 ++ sd/source/ui/inc/AccessibleDrawDocumentView.hxx | 165 + sd/source/ui/inc/AccessibleOutlineEditSource.hxx | 90 + sd/source/ui/inc/AccessibleOutlineView.hxx | 119 + sd/source/ui/inc/AccessiblePageShape.hxx | 117 + .../ui/inc/AccessiblePresentationGraphicShape.hxx | 60 + .../ui/inc/AccessiblePresentationOLEShape.hxx | 57 + sd/source/ui/inc/AccessiblePresentationShape.hxx | 61 + sd/source/ui/inc/AccessibleSlideSorterObject.hxx | 189 + sd/source/ui/inc/AccessibleSlideSorterView.hxx | 255 ++ sd/source/ui/inc/AccessibleViewForwarder.hxx | 92 + sd/source/ui/inc/AnimationChildWindow.hxx | 45 + sd/source/ui/inc/BezierObjectBar.hxx | 51 + sd/source/ui/inc/BreakDlg.hxx | 64 + sd/source/ui/inc/BulletAndPositionDlg.hxx | 157 + sd/source/ui/inc/Client.hxx | 45 + sd/source/ui/inc/ClientView.hxx | 43 + sd/source/ui/inc/CustomAnimationList.hxx | 169 + sd/source/ui/inc/CustomAnimationPane.hxx | 179 + sd/source/ui/inc/DocumentRenderer.hxx | 63 + sd/source/ui/inc/DrawController.hxx | 327 ++ sd/source/ui/inc/DrawDocShell.hxx | 235 ++ sd/source/ui/inc/DrawSubController.hxx | 46 + sd/source/ui/inc/DrawViewShell.hxx | 513 +++ sd/source/ui/inc/EventMultiplexer.hxx | 172 + sd/source/ui/inc/FormShellManager.hxx | 139 + sd/source/ui/inc/FrameView.hxx | 213 ++ sd/source/ui/inc/GraphicDocShell.hxx | 54 + sd/source/ui/inc/GraphicObjectBar.hxx | 54 + sd/source/ui/inc/GraphicViewShell.hxx | 72 + sd/source/ui/inc/GraphicViewShellBase.hxx | 50 + sd/source/ui/inc/ImpressViewShellBase.hxx | 50 + sd/source/ui/inc/LayerTabBar.hxx | 108 + sd/source/ui/inc/MasterPageObserver.hxx | 119 + sd/source/ui/inc/MediaObjectBar.hxx | 56 + sd/source/ui/inc/NavigatorChildWindow.hxx | 40 + sd/source/ui/inc/OutlineBulletDlg.hxx | 51 + sd/source/ui/inc/OutlineView.hxx | 230 ++ sd/source/ui/inc/OutlineViewShell.hxx | 163 + sd/source/ui/inc/OutlineViewShellBase.hxx | 43 + sd/source/ui/inc/OutlinerIteratorImpl.hxx | 239 ++ sd/source/ui/inc/PaneChildWindows.hxx | 65 + sd/source/ui/inc/PaneDockingWindow.hxx | 66 + sd/source/ui/inc/PaneShells.hxx | 63 + sd/source/ui/inc/PresentationViewShell.hxx | 70 + sd/source/ui/inc/PresentationViewShellBase.hxx | 46 + sd/source/ui/inc/PreviewRenderer.hxx | 141 + sd/source/ui/inc/RemoteServer.hxx | 88 + sd/source/ui/inc/Ruler.hxx | 62 + sd/source/ui/inc/SdUnoDrawView.hxx | 116 + sd/source/ui/inc/SdUnoOutlineView.hxx | 82 + sd/source/ui/inc/SdUnoSlideView.hxx | 82 + sd/source/ui/inc/ShellFactory.hxx | 52 + sd/source/ui/inc/SlideSorter.hxx | 248 ++ sd/source/ui/inc/SlideSorterViewShell.hxx | 232 ++ sd/source/ui/inc/SlideSorterViewShellBase.hxx | 43 + sd/source/ui/inc/SlideTransitionPane.hxx | 137 + sd/source/ui/inc/SpellDialogChildWindow.hxx | 86 + sd/source/ui/inc/TabControl.hxx | 107 + sd/source/ui/inc/TableDesignPane.hxx | 118 + sd/source/ui/inc/TemplateScanner.hxx | 175 + sd/source/ui/inc/TextObjectBar.hxx | 58 + sd/source/ui/inc/ToolBarManager.hxx | 273 ++ sd/source/ui/inc/View.hxx | 300 ++ sd/source/ui/inc/ViewClipboard.hxx | 78 + sd/source/ui/inc/ViewShell.hxx | 559 +++ sd/source/ui/inc/ViewShellBase.hxx | 246 ++ sd/source/ui/inc/ViewShellHint.hxx | 57 + sd/source/ui/inc/ViewShellImplementation.hxx | 150 + sd/source/ui/inc/ViewShellManager.hxx | 195 + sd/source/ui/inc/ViewTabBar.hxx | 184 + sd/source/ui/inc/Window.hxx | 213 ++ sd/source/ui/inc/WindowUpdater.hxx | 124 + sd/source/ui/inc/animobjs.hxx | 163 + sd/source/ui/inc/annotationmanager.hxx | 46 + sd/source/ui/inc/assclass.hxx | 68 + sd/source/ui/inc/bulmaper.hxx | 37 + sd/source/ui/inc/copydlg.hxx | 67 + sd/source/ui/inc/createtableobjectbar.hxx | 37 + sd/source/ui/inc/custsdlg.hxx | 91 + sd/source/ui/inc/diactrl.hxx | 68 + sd/source/ui/inc/dlg_char.hxx | 41 + sd/source/ui/inc/dlgfield.hxx | 56 + sd/source/ui/inc/dlgpage.hxx | 48 + sd/source/ui/inc/dlgsnap.hxx | 66 + sd/source/ui/inc/drawview.hxx | 72 + sd/source/ui/inc/filedlg.hxx | 57 + sd/source/ui/inc/framework/Configuration.hxx | 181 + .../ui/inc/framework/ConfigurationController.hxx | 180 + sd/source/ui/inc/framework/DrawModule.hxx | 46 + sd/source/ui/inc/framework/FrameworkHelper.hxx | 340 ++ sd/source/ui/inc/framework/ImpressModule.hxx | 46 + sd/source/ui/inc/framework/ModuleController.hxx | 114 + sd/source/ui/inc/framework/Pane.hxx | 141 + sd/source/ui/inc/framework/PresentationFactory.hxx | 77 + sd/source/ui/inc/framework/PresentationModule.hxx | 46 + sd/source/ui/inc/framework/ResourceId.hxx | 213 ++ sd/source/ui/inc/framework/ViewShellWrapper.hxx | 131 + sd/source/ui/inc/fuarea.hxx | 48 + sd/source/ui/inc/fubullet.hxx | 54 + sd/source/ui/inc/fuchar.hxx | 49 + sd/source/ui/inc/fucon3d.hxx | 61 + sd/source/ui/inc/fuconarc.hxx | 54 + sd/source/ui/inc/fuconbez.hxx | 76 + sd/source/ui/inc/fuconcs.hxx | 64 + sd/source/ui/inc/fuconnct.hxx | 46 + sd/source/ui/inc/fuconrec.hxx | 71 + sd/source/ui/inc/fuconstr.hxx | 64 + sd/source/ui/inc/fuconuno.hxx | 64 + sd/source/ui/inc/fucopy.hxx | 47 + sd/source/ui/inc/fucushow.hxx | 45 + sd/source/ui/inc/fudraw.hxx | 85 + sd/source/ui/inc/fudspord.hxx | 62 + sd/source/ui/inc/fuediglu.hxx | 64 + sd/source/ui/inc/fuexecuteinteraction.hxx | 44 + sd/source/ui/inc/fuexpand.hxx | 45 + sd/source/ui/inc/fuformatpaintbrush.hxx | 61 + sd/source/ui/inc/fuhhconv.hxx | 58 + sd/source/ui/inc/fuinsert.hxx | 112 + sd/source/ui/inc/fuinsfil.hxx | 60 + sd/source/ui/inc/fuline.hxx | 49 + sd/source/ui/inc/fulinend.hxx | 49 + sd/source/ui/inc/fulink.hxx | 46 + sd/source/ui/inc/fumeasur.hxx | 46 + sd/source/ui/inc/fumorph.hxx | 90 + sd/source/ui/inc/funavig.hxx | 46 + sd/source/ui/inc/fuoaprms.hxx | 46 + sd/source/ui/inc/fuolbull.hxx | 62 + sd/source/ui/inc/fuoltext.hxx | 76 + sd/source/ui/inc/fupage.hxx | 73 + sd/source/ui/inc/fuparagr.hxx | 48 + sd/source/ui/inc/fupoor.hxx | 180 + sd/source/ui/inc/fuprlout.hxx | 51 + sd/source/ui/inc/fuprobjs.hxx | 51 + sd/source/ui/inc/fuscale.hxx | 45 + sd/source/ui/inc/fusearch.hxx | 56 + sd/source/ui/inc/fusel.hxx | 104 + sd/source/ui/inc/fusldlg.hxx | 45 + sd/source/ui/inc/fusnapln.hxx | 48 + sd/source/ui/inc/fusumry.hxx | 45 + sd/source/ui/inc/futempl.hxx | 48 + sd/source/ui/inc/futext.hxx | 97 + sd/source/ui/inc/futhes.hxx | 45 + sd/source/ui/inc/futransf.hxx | 45 + sd/source/ui/inc/futxtatt.hxx | 45 + sd/source/ui/inc/fuvect.hxx | 46 + sd/source/ui/inc/fuzoom.hxx | 64 + sd/source/ui/inc/gluectrl.hxx | 68 + sd/source/ui/inc/headerfooterdlg.hxx | 70 + sd/source/ui/inc/ins_paste.hxx | 37 + sd/source/ui/inc/inspagob.hxx | 57 + sd/source/ui/inc/layeroptionsdlg.hxx | 48 + sd/source/ui/inc/masterlayoutdlg.hxx | 61 + sd/source/ui/inc/morphdlg.hxx | 49 + sd/source/ui/inc/navigatr.hxx | 205 + sd/source/ui/inc/optsitem.hxx | 580 +++ sd/source/ui/inc/paragr.hxx | 36 + sd/source/ui/inc/pgjump.hxx | 31 + sd/source/ui/inc/present.hxx | 90 + sd/source/ui/inc/prltempl.hxx | 64 + sd/source/ui/inc/prntopts.hxx | 69 + sd/source/ui/inc/pubdlg.hxx | 205 + sd/source/ui/inc/registerinterfaces.hxx | 30 + sd/source/ui/inc/scalectrl.hxx | 39 + sd/source/ui/inc/sdpopup.hxx | 47 + sd/source/ui/inc/sdpreslt.hxx | 70 + sd/source/ui/inc/sdtreelb.hxx | 395 ++ sd/source/ui/inc/sdundogr.hxx | 46 + sd/source/ui/inc/sdxfer.hxx | 148 + sd/source/ui/inc/slideshow.hxx | 216 ++ sd/source/ui/inc/smarttag.hxx | 171 + sd/source/ui/inc/tablefunction.hxx | 32 + sd/source/ui/inc/tabtempl.hxx | 57 + sd/source/ui/inc/titledockwin.hxx | 94 + sd/source/ui/inc/tmplctrl.hxx | 40 + sd/source/ui/inc/tools/AsynchronousCall.hxx | 77 + sd/source/ui/inc/tools/AsynchronousTask.hxx | 49 + sd/source/ui/inc/tools/ConfigurationAccess.hxx | 144 + sd/source/ui/inc/tools/GraphicSizeCheck.hxx | 116 + sd/source/ui/inc/tools/IconCache.hxx | 70 + sd/source/ui/inc/tools/IdleDetection.hxx | 89 + sd/source/ui/inc/tools/PropertySet.hxx | 114 + .../ui/inc/tools/SdGlobalResourceContainer.hxx | 105 + sd/source/ui/inc/tools/SlotStateListener.hxx | 138 + sd/source/ui/inc/tools/TimerBasedTaskExecution.hxx | 89 + sd/source/ui/inc/tpaction.hxx | 104 + sd/source/ui/inc/tpoption.hxx | 144 + sd/source/ui/inc/uiobject.hxx | 35 + sd/source/ui/inc/unchss.hxx | 47 + sd/source/ui/inc/undoback.hxx | 58 + sd/source/ui/inc/undoheaderfooter.hxx | 45 + sd/source/ui/inc/undolayer.hxx | 56 + sd/source/ui/inc/undopage.hxx | 161 + sd/source/ui/inc/unmodpg.hxx | 74 + sd/source/ui/inc/unmovss.hxx | 44 + sd/source/ui/inc/unoaprms.hxx | 148 + sd/source/ui/inc/unokywds.hxx | 119 + sd/source/ui/inc/unomodel.hxx | 406 ++ sd/source/ui/inc/unopage.hxx | 304 ++ sd/source/ui/inc/unoprnms.hxx | 73 + sd/source/ui/inc/unosrch.hxx | 134 + sd/source/ui/inc/unprlout.hxx | 55 + sd/source/ui/inc/vectdlg.hxx | 81 + sd/source/ui/inc/view/viewoverlaymanager.hxx | 71 + sd/source/ui/inc/zoomlist.hxx | 50 + sd/source/ui/presenter/CanvasUpdateRequester.cxx | 131 + sd/source/ui/presenter/CanvasUpdateRequester.hxx | 72 + sd/source/ui/presenter/PresenterCanvas.cxx | 790 ++++ sd/source/ui/presenter/PresenterCanvas.hxx | 320 ++ sd/source/ui/presenter/PresenterHelper.cxx | 466 +++ sd/source/ui/presenter/PresenterHelper.hxx | 93 + sd/source/ui/presenter/PresenterPreviewCache.cxx | 360 ++ sd/source/ui/presenter/PresenterPreviewCache.hxx | 97 + sd/source/ui/presenter/PresenterTextView.cxx | 466 +++ sd/source/ui/presenter/PresenterTextView.hxx | 71 + sd/source/ui/presenter/SlideRenderer.cxx | 201 + sd/source/ui/presenter/SlideRenderer.hxx | 94 + sd/source/ui/remotecontrol/AvahiNetworkService.cxx | 209 + sd/source/ui/remotecontrol/AvahiNetworkService.hxx | 25 + sd/source/ui/remotecontrol/BluetoothServer.cxx | 1521 ++++++++ sd/source/ui/remotecontrol/BluetoothServer.hxx | 61 + sd/source/ui/remotecontrol/BluetoothServer.mm | 1 + .../ui/remotecontrol/BluetoothServiceRecord.hxx | 75 + .../ui/remotecontrol/BufferedStreamSocket.cxx | 130 + .../ui/remotecontrol/BufferedStreamSocket.hxx | 66 + sd/source/ui/remotecontrol/Communicator.cxx | 154 + sd/source/ui/remotecontrol/Communicator.hxx | 52 + sd/source/ui/remotecontrol/DiscoveryService.cxx | 186 + sd/source/ui/remotecontrol/DiscoveryService.hxx | 40 + sd/source/ui/remotecontrol/DiscoveryService.mm | 1 + sd/source/ui/remotecontrol/IBluetoothSocket.hxx | 42 + sd/source/ui/remotecontrol/ImagePreparer.cxx | 255 ++ sd/source/ui/remotecontrol/ImagePreparer.hxx | 45 + sd/source/ui/remotecontrol/Listener.cxx | 133 + sd/source/ui/remotecontrol/Listener.hxx | 62 + sd/source/ui/remotecontrol/OSXBluetooth.h | 30 + sd/source/ui/remotecontrol/OSXBluetooth.mm | 53 + sd/source/ui/remotecontrol/OSXBluetoothWrapper.hxx | 38 + sd/source/ui/remotecontrol/OSXNetworkService.h | 30 + sd/source/ui/remotecontrol/OSXNetworkService.hxx | 43 + sd/source/ui/remotecontrol/OSXNetworkService.mm | 43 + sd/source/ui/remotecontrol/Receiver.cxx | 207 + sd/source/ui/remotecontrol/Receiver.hxx | 37 + sd/source/ui/remotecontrol/Server.cxx | 373 ++ sd/source/ui/remotecontrol/Transmitter.cxx | 86 + sd/source/ui/remotecontrol/Transmitter.hxx | 55 + sd/source/ui/remotecontrol/WINNetworkService.cxx | 19 + sd/source/ui/remotecontrol/WINNetworkService.hxx | 23 + sd/source/ui/remotecontrol/ZeroconfService.hxx | 49 + sd/source/ui/sidebar/AllMasterPagesSelector.cxx | 180 + sd/source/ui/sidebar/AllMasterPagesSelector.hxx | 80 + .../ui/sidebar/CurrentMasterPagesSelector.cxx | 263 ++ .../ui/sidebar/CurrentMasterPagesSelector.hxx | 77 + sd/source/ui/sidebar/DocumentHelper.cxx | 536 +++ sd/source/ui/sidebar/DocumentHelper.hxx | 108 + sd/source/ui/sidebar/IDisposable.hxx | 31 + sd/source/ui/sidebar/ISidebarReceiver.hxx | 31 + sd/source/ui/sidebar/LayoutMenu.cxx | 728 ++++ sd/source/ui/sidebar/LayoutMenu.hxx | 157 + sd/source/ui/sidebar/MasterPageContainer.cxx | 958 +++++ sd/source/ui/sidebar/MasterPageContainer.hxx | 199 + sd/source/ui/sidebar/MasterPageContainerFiller.cxx | 168 + sd/source/ui/sidebar/MasterPageContainerFiller.hxx | 92 + .../ui/sidebar/MasterPageContainerProviders.cxx | 205 + .../ui/sidebar/MasterPageContainerProviders.hxx | 175 + sd/source/ui/sidebar/MasterPageContainerQueue.cxx | 263 ++ sd/source/ui/sidebar/MasterPageContainerQueue.hxx | 131 + sd/source/ui/sidebar/MasterPageDescriptor.cxx | 341 ++ sd/source/ui/sidebar/MasterPageDescriptor.hxx | 231 ++ sd/source/ui/sidebar/MasterPageObserver.cxx | 317 ++ sd/source/ui/sidebar/MasterPagesSelector.cxx | 620 +++ sd/source/ui/sidebar/MasterPagesSelector.hxx | 180 + sd/source/ui/sidebar/NavigatorWrapper.cxx | 49 + sd/source/ui/sidebar/NavigatorWrapper.hxx | 57 + sd/source/ui/sidebar/PageMarginUtils.hxx | 159 + sd/source/ui/sidebar/PanelFactory.cxx | 141 + sd/source/ui/sidebar/PanelFactory.hxx | 49 + sd/source/ui/sidebar/PreviewValueSet.cxx | 127 + sd/source/ui/sidebar/PreviewValueSet.hxx | 59 + sd/source/ui/sidebar/RecentMasterPagesSelector.cxx | 138 + sd/source/ui/sidebar/RecentMasterPagesSelector.hxx | 71 + sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx | 366 ++ sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx | 125 + sd/source/ui/sidebar/SlideBackground.cxx | 1286 +++++++ sd/source/ui/sidebar/SlideBackground.hxx | 180 + sd/source/ui/slideshow/PaneHider.cxx | 99 + sd/source/ui/slideshow/PaneHider.hxx | 66 + sd/source/ui/slideshow/SlideShowRestarter.cxx | 156 + sd/source/ui/slideshow/SlideShowRestarter.hxx | 88 + sd/source/ui/slideshow/showwin.cxx | 629 +++ sd/source/ui/slideshow/showwindow.hxx | 110 + sd/source/ui/slideshow/slideshow.cxx | 1191 ++++++ sd/source/ui/slideshow/slideshowimpl.cxx | 3349 ++++++++++++++++ sd/source/ui/slideshow/slideshowimpl.hxx | 342 ++ sd/source/ui/slideshow/slideshowviewimpl.cxx | 626 +++ sd/source/ui/slideshow/slideshowviewimpl.hxx | 182 + sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx | 550 +++ sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx | 208 + .../ui/slidesorter/cache/SlsBitmapCompressor.cxx | 197 + .../ui/slidesorter/cache/SlsBitmapCompressor.hxx | 138 + .../ui/slidesorter/cache/SlsBitmapFactory.cxx | 71 + .../ui/slidesorter/cache/SlsBitmapFactory.hxx | 46 + .../ui/slidesorter/cache/SlsCacheCompactor.cxx | 189 + .../ui/slidesorter/cache/SlsCacheCompactor.hxx | 87 + .../ui/slidesorter/cache/SlsCacheConfiguration.cxx | 144 + .../ui/slidesorter/cache/SlsCacheConfiguration.hxx | 68 + .../ui/slidesorter/cache/SlsGenericPageCache.cxx | 278 ++ .../ui/slidesorter/cache/SlsGenericPageCache.hxx | 152 + sd/source/ui/slidesorter/cache/SlsPageCache.cxx | 109 + .../ui/slidesorter/cache/SlsPageCacheManager.cxx | 420 ++ .../ui/slidesorter/cache/SlsQueueProcessor.cxx | 176 + .../ui/slidesorter/cache/SlsQueueProcessor.hxx | 98 + .../ui/slidesorter/cache/SlsRequestFactory.cxx | 50 + .../ui/slidesorter/cache/SlsRequestFactory.hxx | 36 + .../slidesorter/cache/SlsRequestPriorityClass.hxx | 44 + sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx | 275 ++ sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx | 122 + .../controller/SlideSorterController.cxx | 910 +++++ .../controller/SlsAnimationFunction.cxx | 129 + .../ui/slidesorter/controller/SlsAnimator.cxx | 280 ++ .../ui/slidesorter/controller/SlsClipboard.cxx | 918 +++++ .../controller/SlsCurrentSlideManager.cxx | 256 ++ .../controller/SlsDragAndDropContext.cxx | 120 + .../controller/SlsDragAndDropContext.hxx | 68 + .../ui/slidesorter/controller/SlsFocusManager.cxx | 245 ++ .../controller/SlsInsertionIndicatorHandler.cxx | 243 ++ .../ui/slidesorter/controller/SlsListener.cxx | 597 +++ .../ui/slidesorter/controller/SlsListener.hxx | 164 + .../ui/slidesorter/controller/SlsPageSelector.cxx | 386 ++ .../ui/slidesorter/controller/SlsProperties.cxx | 106 + .../slidesorter/controller/SlsScrollBarManager.cxx | 608 +++ .../controller/SlsSelectionFunction.cxx | 1485 ++++++++ .../slidesorter/controller/SlsSelectionManager.cxx | 309 ++ .../controller/SlsSelectionObserver.cxx | 139 + .../ui/slidesorter/controller/SlsSlotManager.cxx | 1284 +++++++ .../slidesorter/controller/SlsTransferableData.cxx | 86 + .../controller/SlsVisibleAreaManager.cxx | 234 ++ .../ui/slidesorter/inc/cache/SlsCacheContext.hxx | 98 + .../ui/slidesorter/inc/cache/SlsPageCache.hxx | 141 + .../slidesorter/inc/cache/SlsPageCacheManager.hxx | 155 + .../inc/controller/SlideSorterController.hxx | 327 ++ .../inc/controller/SlsAnimationFunction.hxx | 77 + .../ui/slidesorter/inc/controller/SlsAnimator.hxx | 122 + .../ui/slidesorter/inc/controller/SlsClipboard.hxx | 208 + .../inc/controller/SlsCurrentSlideManager.hxx | 112 + .../slidesorter/inc/controller/SlsFocusManager.hxx | 211 ++ .../controller/SlsInsertionIndicatorHandler.hxx | 138 + .../slidesorter/inc/controller/SlsPageSelector.hxx | 219 ++ .../slidesorter/inc/controller/SlsProperties.hxx | 125 + .../inc/controller/SlsScrollBarManager.hxx | 248 ++ .../inc/controller/SlsSelectionFunction.hxx | 145 + .../inc/controller/SlsSelectionManager.hxx | 139 + .../inc/controller/SlsSelectionObserver.hxx | 77 + .../slidesorter/inc/controller/SlsSlotManager.hxx | 98 + .../inc/controller/SlsTransferableData.hxx | 78 + .../inc/controller/SlsVisibleAreaManager.hxx | 90 + .../ui/slidesorter/inc/model/SlideSorterModel.hxx | 227 ++ .../ui/slidesorter/inc/model/SlsEnumeration.hxx | 44 + .../ui/slidesorter/inc/model/SlsPageDescriptor.hxx | 144 + .../slidesorter/inc/model/SlsPageEnumeration.hxx | 95 + .../inc/model/SlsPageEnumerationProvider.hxx | 51 + .../inc/model/SlsSharedPageDescriptor.hxx | 32 + .../ui/slidesorter/inc/model/SlsVisualState.hxx | 47 + .../ui/slidesorter/inc/view/SlideSorterView.hxx | 225 ++ .../ui/slidesorter/inc/view/SlsILayerPainter.hxx | 53 + .../ui/slidesorter/inc/view/SlsInsertAnimator.hxx | 59 + .../inc/view/SlsInsertionIndicatorOverlay.hxx | 101 + sd/source/ui/slidesorter/inc/view/SlsLayouter.hxx | 237 ++ .../slidesorter/inc/view/SlsPageObjectLayouter.hxx | 144 + .../slidesorter/inc/view/SlsPageObjectPainter.hxx | 119 + sd/source/ui/slidesorter/inc/view/SlsTheme.hxx | 135 + sd/source/ui/slidesorter/inc/view/SlsToolTip.hxx | 75 + .../ui/slidesorter/model/SlideSorterModel.cxx | 676 ++++ .../ui/slidesorter/model/SlsPageDescriptor.cxx | 226 ++ .../ui/slidesorter/model/SlsPageEnumeration.cxx | 202 + .../model/SlsPageEnumerationProvider.cxx | 81 + sd/source/ui/slidesorter/model/SlsVisualState.cxx | 40 + sd/source/ui/slidesorter/shell/SlideSorter.cxx | 456 +++ .../ui/slidesorter/shell/SlideSorterService.cxx | 412 ++ .../ui/slidesorter/shell/SlideSorterService.hxx | 153 + .../ui/slidesorter/shell/SlideSorterViewShell.cxx | 924 +++++ sd/source/ui/slidesorter/view/SlideSorterView.cxx | 856 +++++ sd/source/ui/slidesorter/view/SlsFramePainter.cxx | 225 ++ sd/source/ui/slidesorter/view/SlsFramePainter.hxx | 109 + .../ui/slidesorter/view/SlsInsertAnimator.cxx | 428 +++ .../view/SlsInsertionIndicatorOverlay.cxx | 360 ++ sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx | 491 +++ sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx | 84 + sd/source/ui/slidesorter/view/SlsLayouter.cxx | 1225 ++++++ .../ui/slidesorter/view/SlsPageObjectLayouter.cxx | 259 ++ .../ui/slidesorter/view/SlsPageObjectPainter.cxx | 442 +++ sd/source/ui/slidesorter/view/SlsTheme.cxx | 239 ++ sd/source/ui/slidesorter/view/SlsToolTip.cxx | 160 + .../ui/slidesorter/view/SlsViewCacheContext.cxx | 117 + .../ui/slidesorter/view/SlsViewCacheContext.hxx | 61 + sd/source/ui/table/TableDesignPane.cxx | 763 ++++ sd/source/ui/table/tablefunction.cxx | 292 ++ sd/source/ui/table/tableobjectbar.cxx | 224 ++ sd/source/ui/table/tableobjectbar.hxx | 56 + sd/source/ui/tools/AsynchronousCall.cxx | 56 + sd/source/ui/tools/ConfigurationAccess.cxx | 173 + sd/source/ui/tools/EventMultiplexer.cxx | 661 ++++ sd/source/ui/tools/GraphicSizeCheck.cxx | 221 ++ sd/source/ui/tools/IconCache.cxx | 106 + sd/source/ui/tools/IdleDetection.cxx | 103 + sd/source/ui/tools/PreviewRenderer.cxx | 532 +++ sd/source/ui/tools/PropertySet.cxx | 158 + sd/source/ui/tools/SdGlobalResourceContainer.cxx | 197 + sd/source/ui/tools/SlotStateListener.cxx | 153 + sd/source/ui/tools/TimerBasedTaskExecution.cxx | 130 + sd/source/ui/uitest/uiobject.cxx | 183 + sd/source/ui/unoidl/DrawController.cxx | 815 ++++ sd/source/ui/unoidl/SdUnoDrawView.cxx | 548 +++ sd/source/ui/unoidl/SdUnoOutlineView.cxx | 156 + sd/source/ui/unoidl/SdUnoSlideView.cxx | 172 + sd/source/ui/unoidl/UnoDocumentSettings.cxx | 1431 +++++++ sd/source/ui/unoidl/UnoDocumentSettings.hxx | 37 + sd/source/ui/unoidl/randomnode.cxx | 573 +++ sd/source/ui/unoidl/sddetect.cxx | 160 + sd/source/ui/unoidl/sddetect.hxx | 48 + sd/source/ui/unoidl/unocpres.cxx | 450 +++ sd/source/ui/unoidl/unocpres.hxx | 147 + sd/source/ui/unoidl/unodoc.cxx | 73 + sd/source/ui/unoidl/unolayer.cxx | 707 ++++ sd/source/ui/unoidl/unolayer.hxx | 169 + sd/source/ui/unoidl/unomodel.cxx | 3491 +++++++++++++++++ sd/source/ui/unoidl/unomodule.cxx | 132 + sd/source/ui/unoidl/unomodule.hxx | 57 + sd/source/ui/unoidl/unoobj.cxx | 1627 ++++++++ sd/source/ui/unoidl/unoobj.hxx | 100 + sd/source/ui/unoidl/unopage.cxx | 3056 +++++++++++++++ sd/source/ui/unoidl/unopback.cxx | 410 ++ sd/source/ui/unoidl/unopback.hxx | 89 + sd/source/ui/unoidl/unopool.cxx | 89 + sd/source/ui/unoidl/unopool.hxx | 29 + sd/source/ui/unoidl/unosrch.cxx | 778 ++++ sd/source/ui/unoidl/unowcntr.cxx | 99 + sd/source/ui/unoidl/unowcntr.hxx | 47 + sd/source/ui/view/DocumentRenderer.cxx | 2256 +++++++++++ sd/source/ui/view/FormShellManager.cxx | 319 ++ sd/source/ui/view/GraphicObjectBar.cxx | 141 + sd/source/ui/view/GraphicViewShellBase.cxx | 93 + sd/source/ui/view/ImpressViewShellBase.cxx | 97 + sd/source/ui/view/MediaObjectBar.cxx | 77 + sd/source/ui/view/OutlineViewShellBase.cxx | 66 + sd/source/ui/view/Outliner.cxx | 2066 ++++++++++ sd/source/ui/view/OutlinerIterator.cxx | 798 ++++ sd/source/ui/view/PresentationViewShellBase.cxx | 94 + sd/source/ui/view/SlideSorterViewShellBase.cxx | 68 + sd/source/ui/view/ToolBarManager.cxx | 1375 +++++++ sd/source/ui/view/ViewClipboard.cxx | 240 ++ sd/source/ui/view/ViewShellBase.cxx | 1456 +++++++ sd/source/ui/view/ViewShellHint.cxx | 31 + sd/source/ui/view/ViewShellImplementation.cxx | 379 ++ sd/source/ui/view/ViewShellManager.cxx | 1168 ++++++ sd/source/ui/view/ViewTabBar.cxx | 561 +++ sd/source/ui/view/WindowUpdater.cxx | 131 + sd/source/ui/view/clview.cxx | 62 + sd/source/ui/view/drawview.cxx | 634 ++++ sd/source/ui/view/drbezob.cxx | 320 ++ sd/source/ui/view/drtxtob.cxx | 625 +++ sd/source/ui/view/drtxtob1.cxx | 865 +++++ sd/source/ui/view/drviews1.cxx | 1360 +++++++ sd/source/ui/view/drviews2.cxx | 4004 ++++++++++++++++++++ sd/source/ui/view/drviews3.cxx | 1106 ++++++ sd/source/ui/view/drviews4.cxx | 982 +++++ sd/source/ui/view/drviews5.cxx | 650 ++++ sd/source/ui/view/drviews6.cxx | 339 ++ sd/source/ui/view/drviews7.cxx | 1991 ++++++++++ sd/source/ui/view/drviews8.cxx | 135 + sd/source/ui/view/drviews9.cxx | 886 +++++ sd/source/ui/view/drviewsa.cxx | 848 +++++ sd/source/ui/view/drviewsb.cxx | 205 + sd/source/ui/view/drviewsc.cxx | 72 + sd/source/ui/view/drviewsd.cxx | 193 + sd/source/ui/view/drviewse.cxx | 1701 +++++++++ sd/source/ui/view/drviewsf.cxx | 826 ++++ sd/source/ui/view/drviewsg.cxx | 232 ++ sd/source/ui/view/drviewsh.cxx | 203 + sd/source/ui/view/drviewsi.cxx | 165 + sd/source/ui/view/drviewsj.cxx | 567 +++ sd/source/ui/view/drviewsk.cxx | 37 + sd/source/ui/view/drvwshrg.cxx | 110 + sd/source/ui/view/frmview.cxx | 916 +++++ sd/source/ui/view/grviewsh.cxx | 88 + sd/source/ui/view/outlnvs2.cxx | 636 ++++ sd/source/ui/view/outlnvsh.cxx | 1883 +++++++++ sd/source/ui/view/outlview.cxx | 1720 +++++++++ sd/source/ui/view/presvish.cxx | 172 + sd/source/ui/view/sdruler.cxx | 148 + sd/source/ui/view/sdview.cxx | 1395 +++++++ sd/source/ui/view/sdview2.cxx | 908 +++++ sd/source/ui/view/sdview3.cxx | 1596 ++++++++ sd/source/ui/view/sdview4.cxx | 645 ++++ sd/source/ui/view/sdview5.cxx | 118 + sd/source/ui/view/sdwindow.cxx | 1097 ++++++ sd/source/ui/view/tabcontr.cxx | 358 ++ sd/source/ui/view/unmodpg.cxx | 210 + sd/source/ui/view/viewoverlaymanager.cxx | 546 +++ sd/source/ui/view/viewshe2.cxx | 958 +++++ sd/source/ui/view/viewshe3.cxx | 383 ++ sd/source/ui/view/viewshel.cxx | 1634 ++++++++ sd/source/ui/view/zoomlist.cxx | 94 + 794 files changed, 258337 insertions(+) create mode 100644 sd/source/core/CustomAnimationCloner.cxx create mode 100644 sd/source/core/CustomAnimationEffect.cxx create mode 100644 sd/source/core/CustomAnimationPreset.cxx create mode 100644 sd/source/core/EffectMigration.cxx create mode 100644 sd/source/core/PageListWatcher.cxx create mode 100644 sd/source/core/PageListWatcher.hxx create mode 100644 sd/source/core/TransitionPreset.cxx create mode 100644 sd/source/core/anminfo.cxx create mode 100644 sd/source/core/annotations/Annotation.cxx create mode 100644 sd/source/core/annotations/AnnotationEnumeration.cxx create mode 100644 sd/source/core/cusshow.cxx create mode 100644 sd/source/core/drawdoc.cxx create mode 100644 sd/source/core/drawdoc2.cxx create mode 100644 sd/source/core/drawdoc3.cxx create mode 100644 sd/source/core/drawdoc4.cxx create mode 100644 sd/source/core/drawdoc_animations.cxx create mode 100644 sd/source/core/pglink.cxx create mode 100644 sd/source/core/sdiocmpt.cxx create mode 100644 sd/source/core/sdpage.cxx create mode 100644 sd/source/core/sdpage2.cxx create mode 100644 sd/source/core/sdpage_animations.cxx create mode 100644 sd/source/core/shapelist.cxx create mode 100644 sd/source/core/stlfamily.cxx create mode 100644 sd/source/core/stlpool.cxx create mode 100644 sd/source/core/stlsheet.cxx create mode 100644 sd/source/core/text/textapi.cxx create mode 100644 sd/source/core/typemap.cxx create mode 100644 sd/source/core/undo/undofactory.cxx create mode 100644 sd/source/core/undo/undomanager.cxx create mode 100644 sd/source/core/undo/undoobjects.cxx create mode 100644 sd/source/core/undoanim.cxx create mode 100644 sd/source/filter/cgm/sdcgmfilter.cxx create mode 100644 sd/source/filter/eppt/eppt.cxx create mode 100644 sd/source/filter/eppt/eppt.hxx create mode 100644 sd/source/filter/eppt/epptbase.hxx create mode 100644 sd/source/filter/eppt/epptdef.hxx create mode 100644 sd/source/filter/eppt/epptooxml.hxx create mode 100644 sd/source/filter/eppt/epptso.cxx create mode 100644 sd/source/filter/eppt/escherex.cxx create mode 100644 sd/source/filter/eppt/escherex.hxx create mode 100644 sd/source/filter/eppt/grouptable.hxx create mode 100644 sd/source/filter/eppt/pptexanimations.cxx create mode 100644 sd/source/filter/eppt/pptexanimations.hxx create mode 100644 sd/source/filter/eppt/pptexsoundcollection.cxx create mode 100644 sd/source/filter/eppt/pptexsoundcollection.hxx create mode 100644 sd/source/filter/eppt/pptx-animations.cxx create mode 100644 sd/source/filter/eppt/pptx-animations.hxx create mode 100644 sd/source/filter/eppt/pptx-epptbase.cxx create mode 100644 sd/source/filter/eppt/pptx-epptooxml.cxx create mode 100644 sd/source/filter/eppt/pptx-grouptable.cxx create mode 100644 sd/source/filter/eppt/pptx-stylesheet.cxx create mode 100644 sd/source/filter/eppt/pptx-text.cxx create mode 100644 sd/source/filter/eppt/text.hxx create mode 100644 sd/source/filter/grf/sdgrffilter.cxx create mode 100644 sd/source/filter/html/HtmlOptionsDialog.cxx create mode 100644 sd/source/filter/html/buttonset.cxx create mode 100644 sd/source/filter/html/buttonset.hxx create mode 100644 sd/source/filter/html/htmlattr.cxx create mode 100644 sd/source/filter/html/htmlattr.hxx create mode 100644 sd/source/filter/html/htmlex.cxx create mode 100644 sd/source/filter/html/htmlex.hxx create mode 100644 sd/source/filter/html/htmlpublishmode.hxx create mode 100644 sd/source/filter/html/pubdlg.cxx create mode 100644 sd/source/filter/html/sdhtmlfilter.cxx create mode 100644 sd/source/filter/pdf/sdpdffilter.cxx create mode 100644 sd/source/filter/ppt/ppt97animations.cxx create mode 100644 sd/source/filter/ppt/ppt97animations.hxx create mode 100644 sd/source/filter/ppt/pptanimations.hxx create mode 100644 sd/source/filter/ppt/pptatom.cxx create mode 100644 sd/source/filter/ppt/pptatom.hxx create mode 100644 sd/source/filter/ppt/pptin.cxx create mode 100644 sd/source/filter/ppt/pptin.hxx create mode 100644 sd/source/filter/ppt/pptinanimations.cxx create mode 100644 sd/source/filter/ppt/pptinanimations.hxx create mode 100644 sd/source/filter/ppt/propread.cxx create mode 100644 sd/source/filter/ppt/propread.hxx create mode 100644 sd/source/filter/sdfilter.cxx create mode 100644 sd/source/filter/sdpptwrp.cxx create mode 100644 sd/source/filter/xml/sdtransform.cxx create mode 100644 sd/source/filter/xml/sdtransform.hxx create mode 100644 sd/source/filter/xml/sdxmlwrp.cxx create mode 100644 sd/source/helper/simplereferencecomponent.cxx create mode 100644 sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx create mode 100644 sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx create mode 100644 sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx create mode 100644 sd/source/ui/accessibility/AccessibleOutlineView.cxx create mode 100644 sd/source/ui/accessibility/AccessiblePageShape.cxx create mode 100644 sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx create mode 100644 sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx create mode 100644 sd/source/ui/accessibility/AccessiblePresentationShape.cxx create mode 100644 sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx create mode 100644 sd/source/ui/accessibility/AccessibleSlideSorterView.cxx create mode 100644 sd/source/ui/accessibility/AccessibleViewForwarder.cxx create mode 100644 sd/source/ui/accessibility/SdShapeTypes.cxx create mode 100644 sd/source/ui/animations/CustomAnimationDialog.cxx create mode 100644 sd/source/ui/animations/CustomAnimationDialog.hxx create mode 100644 sd/source/ui/animations/CustomAnimationList.cxx create mode 100644 sd/source/ui/animations/CustomAnimationPane.cxx create mode 100644 sd/source/ui/animations/STLPropertySet.cxx create mode 100644 sd/source/ui/animations/STLPropertySet.hxx create mode 100644 sd/source/ui/animations/SlideTransitionPane.cxx create mode 100644 sd/source/ui/animations/motionpathtag.cxx create mode 100644 sd/source/ui/animations/motionpathtag.hxx create mode 100644 sd/source/ui/annotations/annotationmanager.cxx create mode 100644 sd/source/ui/annotations/annotationmanagerimpl.hxx create mode 100644 sd/source/ui/annotations/annotationtag.cxx create mode 100644 sd/source/ui/annotations/annotationtag.hxx create mode 100644 sd/source/ui/annotations/annotationwindow.cxx create mode 100644 sd/source/ui/annotations/annotationwindow.hxx create mode 100644 sd/source/ui/app/optsitem.cxx create mode 100644 sd/source/ui/app/scalectrl.cxx create mode 100644 sd/source/ui/app/sddll.cxx create mode 100644 sd/source/ui/app/sdmod.cxx create mode 100644 sd/source/ui/app/sdmod1.cxx create mode 100644 sd/source/ui/app/sdmod2.cxx create mode 100644 sd/source/ui/app/sdpopup.cxx create mode 100644 sd/source/ui/app/sdxfer.cxx create mode 100644 sd/source/ui/app/tmplctrl.cxx create mode 100644 sd/source/ui/controller/displaymodecontroller.cxx create mode 100644 sd/source/ui/controller/slidelayoutcontroller.cxx create mode 100644 sd/source/ui/controller/slidelayoutcontroller.hxx create mode 100644 sd/source/ui/dlg/AnimationChildWindow.cxx create mode 100644 sd/source/ui/dlg/BulletAndPositionDlg.cxx create mode 100644 sd/source/ui/dlg/LayerTabBar.cxx create mode 100644 sd/source/ui/dlg/NavigatorChildWindow.cxx create mode 100644 sd/source/ui/dlg/PaneChildWindows.cxx create mode 100644 sd/source/ui/dlg/PaneDockingWindow.cxx create mode 100644 sd/source/ui/dlg/PaneShells.cxx create mode 100644 sd/source/ui/dlg/PhotoAlbumDialog.cxx create mode 100644 sd/source/ui/dlg/PhotoAlbumDialog.hxx create mode 100644 sd/source/ui/dlg/RemoteDialog.cxx create mode 100644 sd/source/ui/dlg/RemoteDialog.hxx create mode 100644 sd/source/ui/dlg/RemoteDialogClientBox.cxx create mode 100644 sd/source/ui/dlg/RemoteDialogClientBox.hxx create mode 100644 sd/source/ui/dlg/SpellDialogChildWindow.cxx create mode 100644 sd/source/ui/dlg/TemplateScanner.cxx create mode 100644 sd/source/ui/dlg/animobjs.cxx create mode 100644 sd/source/ui/dlg/assclass.cxx create mode 100644 sd/source/ui/dlg/brkdlg.cxx create mode 100644 sd/source/ui/dlg/copydlg.cxx create mode 100644 sd/source/ui/dlg/custsdlg.cxx create mode 100644 sd/source/ui/dlg/diactrl.cxx create mode 100644 sd/source/ui/dlg/dlgchar.cxx create mode 100644 sd/source/ui/dlg/dlgfield.cxx create mode 100644 sd/source/ui/dlg/dlgolbul.cxx create mode 100644 sd/source/ui/dlg/dlgpage.cxx create mode 100644 sd/source/ui/dlg/dlgsnap.cxx create mode 100644 sd/source/ui/dlg/filedlg.cxx create mode 100644 sd/source/ui/dlg/gluectrl.cxx create mode 100644 sd/source/ui/dlg/headerfooterdlg.cxx create mode 100644 sd/source/ui/dlg/ins_paste.cxx create mode 100644 sd/source/ui/dlg/inspagob.cxx create mode 100644 sd/source/ui/dlg/layeroptionsdlg.cxx create mode 100644 sd/source/ui/dlg/masterlayoutdlg.cxx create mode 100644 sd/source/ui/dlg/morphdlg.cxx create mode 100644 sd/source/ui/dlg/navigatr.cxx create mode 100644 sd/source/ui/dlg/paragr.cxx create mode 100644 sd/source/ui/dlg/present.cxx create mode 100644 sd/source/ui/dlg/prltempl.cxx create mode 100644 sd/source/ui/dlg/prntopts.cxx create mode 100644 sd/source/ui/dlg/sdabstdlg.cxx create mode 100644 sd/source/ui/dlg/sddlgfact.cxx create mode 100644 sd/source/ui/dlg/sddlgfact.hxx create mode 100644 sd/source/ui/dlg/sdpreslt.cxx create mode 100644 sd/source/ui/dlg/sdtreelb.cxx create mode 100644 sd/source/ui/dlg/sduiexp.cxx create mode 100644 sd/source/ui/dlg/tabtempl.cxx create mode 100644 sd/source/ui/dlg/titledockwin.cxx create mode 100644 sd/source/ui/dlg/tpaction.cxx create mode 100644 sd/source/ui/dlg/tpoption.cxx create mode 100644 sd/source/ui/dlg/unchss.cxx create mode 100644 sd/source/ui/dlg/vectdlg.cxx create mode 100644 sd/source/ui/docshell/docshel2.cxx create mode 100644 sd/source/ui/docshell/docshel3.cxx create mode 100644 sd/source/ui/docshell/docshel4.cxx create mode 100644 sd/source/ui/docshell/docshell.cxx create mode 100644 sd/source/ui/docshell/grdocsh.cxx create mode 100644 sd/source/ui/docshell/sdclient.cxx create mode 100644 sd/source/ui/framework/configuration/ChangeRequestQueue.cxx create mode 100644 sd/source/ui/framework/configuration/ChangeRequestQueue.hxx create mode 100644 sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx create mode 100644 sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx create mode 100644 sd/source/ui/framework/configuration/Configuration.cxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationClassifier.cxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationClassifier.hxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationController.cxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationTracer.cxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationTracer.hxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationUpdater.cxx create mode 100644 sd/source/ui/framework/configuration/ConfigurationUpdater.hxx create mode 100644 sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx create mode 100644 sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx create mode 100644 sd/source/ui/framework/configuration/ResourceFactoryManager.cxx create mode 100644 sd/source/ui/framework/configuration/ResourceFactoryManager.hxx create mode 100644 sd/source/ui/framework/configuration/ResourceId.cxx create mode 100644 sd/source/ui/framework/configuration/UpdateRequest.cxx create mode 100644 sd/source/ui/framework/configuration/UpdateRequest.hxx create mode 100644 sd/source/ui/framework/configuration/debugtrace.hxx create mode 100644 sd/source/ui/framework/factories/BasicPaneFactory.cxx create mode 100644 sd/source/ui/framework/factories/BasicPaneFactory.hxx create mode 100644 sd/source/ui/framework/factories/BasicToolBarFactory.cxx create mode 100644 sd/source/ui/framework/factories/BasicToolBarFactory.hxx create mode 100644 sd/source/ui/framework/factories/BasicViewFactory.cxx create mode 100644 sd/source/ui/framework/factories/BasicViewFactory.hxx create mode 100644 sd/source/ui/framework/factories/ChildWindowPane.cxx create mode 100644 sd/source/ui/framework/factories/ChildWindowPane.hxx create mode 100644 sd/source/ui/framework/factories/FrameWindowPane.cxx create mode 100644 sd/source/ui/framework/factories/FrameWindowPane.hxx create mode 100644 sd/source/ui/framework/factories/FullScreenPane.cxx create mode 100644 sd/source/ui/framework/factories/FullScreenPane.hxx create mode 100644 sd/source/ui/framework/factories/Pane.cxx create mode 100644 sd/source/ui/framework/factories/PresentationFactory.cxx create mode 100644 sd/source/ui/framework/factories/ViewShellWrapper.cxx create mode 100644 sd/source/ui/framework/module/CenterViewFocusModule.cxx create mode 100644 sd/source/ui/framework/module/CenterViewFocusModule.hxx create mode 100644 sd/source/ui/framework/module/DrawModule.cxx create mode 100644 sd/source/ui/framework/module/ImpressModule.cxx create mode 100644 sd/source/ui/framework/module/ModuleController.cxx create mode 100644 sd/source/ui/framework/module/PresentationModule.cxx create mode 100644 sd/source/ui/framework/module/ShellStackGuard.cxx create mode 100644 sd/source/ui/framework/module/ShellStackGuard.hxx create mode 100644 sd/source/ui/framework/module/SlideSorterModule.cxx create mode 100644 sd/source/ui/framework/module/SlideSorterModule.hxx create mode 100644 sd/source/ui/framework/module/ToolBarModule.cxx create mode 100644 sd/source/ui/framework/module/ToolBarModule.hxx create mode 100644 sd/source/ui/framework/module/ViewTabBarModule.cxx create mode 100644 sd/source/ui/framework/module/ViewTabBarModule.hxx create mode 100644 sd/source/ui/framework/tools/FrameworkHelper.cxx create mode 100644 sd/source/ui/func/bulmaper.cxx create mode 100644 sd/source/ui/func/fuarea.cxx create mode 100644 sd/source/ui/func/fubullet.cxx create mode 100644 sd/source/ui/func/fuchar.cxx create mode 100644 sd/source/ui/func/fucon3d.cxx create mode 100644 sd/source/ui/func/fuconarc.cxx create mode 100644 sd/source/ui/func/fuconbez.cxx create mode 100644 sd/source/ui/func/fuconcs.cxx create mode 100644 sd/source/ui/func/fuconnct.cxx create mode 100644 sd/source/ui/func/fuconrec.cxx create mode 100644 sd/source/ui/func/fuconstr.cxx create mode 100644 sd/source/ui/func/fuconuno.cxx create mode 100644 sd/source/ui/func/fucopy.cxx create mode 100644 sd/source/ui/func/fucushow.cxx create mode 100644 sd/source/ui/func/fudraw.cxx create mode 100644 sd/source/ui/func/fudspord.cxx create mode 100644 sd/source/ui/func/fuediglu.cxx create mode 100644 sd/source/ui/func/fuexecuteinteraction.cxx create mode 100644 sd/source/ui/func/fuexpand.cxx create mode 100644 sd/source/ui/func/fuformatpaintbrush.cxx create mode 100644 sd/source/ui/func/fuhhconv.cxx create mode 100644 sd/source/ui/func/fuinsert.cxx create mode 100644 sd/source/ui/func/fuinsfil.cxx create mode 100644 sd/source/ui/func/fuline.cxx create mode 100644 sd/source/ui/func/fulinend.cxx create mode 100644 sd/source/ui/func/fulink.cxx create mode 100644 sd/source/ui/func/fumeasur.cxx create mode 100644 sd/source/ui/func/fumorph.cxx create mode 100644 sd/source/ui/func/funavig.cxx create mode 100644 sd/source/ui/func/fuoaprms.cxx create mode 100644 sd/source/ui/func/fuolbull.cxx create mode 100644 sd/source/ui/func/fuoltext.cxx create mode 100644 sd/source/ui/func/fupage.cxx create mode 100644 sd/source/ui/func/fuparagr.cxx create mode 100644 sd/source/ui/func/fupoor.cxx create mode 100644 sd/source/ui/func/fuprlout.cxx create mode 100644 sd/source/ui/func/fuprobjs.cxx create mode 100644 sd/source/ui/func/fuscale.cxx create mode 100644 sd/source/ui/func/fusearch.cxx create mode 100644 sd/source/ui/func/fusel.cxx create mode 100644 sd/source/ui/func/fusldlg.cxx create mode 100644 sd/source/ui/func/fusnapln.cxx create mode 100644 sd/source/ui/func/fusumry.cxx create mode 100644 sd/source/ui/func/futempl.cxx create mode 100644 sd/source/ui/func/futext.cxx create mode 100644 sd/source/ui/func/futhes.cxx create mode 100644 sd/source/ui/func/futransf.cxx create mode 100644 sd/source/ui/func/futxtatt.cxx create mode 100644 sd/source/ui/func/fuvect.cxx create mode 100644 sd/source/ui/func/fuzoom.cxx create mode 100644 sd/source/ui/func/sdundogr.cxx create mode 100644 sd/source/ui/func/smarttag.cxx create mode 100644 sd/source/ui/func/undoback.cxx create mode 100644 sd/source/ui/func/undoheaderfooter.cxx create mode 100644 sd/source/ui/func/undolayer.cxx create mode 100644 sd/source/ui/func/undopage.cxx create mode 100644 sd/source/ui/func/unmovss.cxx create mode 100644 sd/source/ui/func/unoaprms.cxx create mode 100644 sd/source/ui/func/unprlout.cxx create mode 100644 sd/source/ui/inc/AccessibleDocumentViewBase.hxx create mode 100644 sd/source/ui/inc/AccessibleDrawDocumentView.hxx create mode 100644 sd/source/ui/inc/AccessibleOutlineEditSource.hxx create mode 100644 sd/source/ui/inc/AccessibleOutlineView.hxx create mode 100644 sd/source/ui/inc/AccessiblePageShape.hxx create mode 100644 sd/source/ui/inc/AccessiblePresentationGraphicShape.hxx create mode 100644 sd/source/ui/inc/AccessiblePresentationOLEShape.hxx create mode 100644 sd/source/ui/inc/AccessiblePresentationShape.hxx create mode 100644 sd/source/ui/inc/AccessibleSlideSorterObject.hxx create mode 100644 sd/source/ui/inc/AccessibleSlideSorterView.hxx create mode 100644 sd/source/ui/inc/AccessibleViewForwarder.hxx create mode 100644 sd/source/ui/inc/AnimationChildWindow.hxx create mode 100644 sd/source/ui/inc/BezierObjectBar.hxx create mode 100644 sd/source/ui/inc/BreakDlg.hxx create mode 100644 sd/source/ui/inc/BulletAndPositionDlg.hxx create mode 100644 sd/source/ui/inc/Client.hxx create mode 100644 sd/source/ui/inc/ClientView.hxx create mode 100644 sd/source/ui/inc/CustomAnimationList.hxx create mode 100644 sd/source/ui/inc/CustomAnimationPane.hxx create mode 100644 sd/source/ui/inc/DocumentRenderer.hxx create mode 100644 sd/source/ui/inc/DrawController.hxx create mode 100644 sd/source/ui/inc/DrawDocShell.hxx create mode 100644 sd/source/ui/inc/DrawSubController.hxx create mode 100644 sd/source/ui/inc/DrawViewShell.hxx create mode 100644 sd/source/ui/inc/EventMultiplexer.hxx create mode 100644 sd/source/ui/inc/FormShellManager.hxx create mode 100644 sd/source/ui/inc/FrameView.hxx create mode 100644 sd/source/ui/inc/GraphicDocShell.hxx create mode 100644 sd/source/ui/inc/GraphicObjectBar.hxx create mode 100644 sd/source/ui/inc/GraphicViewShell.hxx create mode 100644 sd/source/ui/inc/GraphicViewShellBase.hxx create mode 100644 sd/source/ui/inc/ImpressViewShellBase.hxx create mode 100644 sd/source/ui/inc/LayerTabBar.hxx create mode 100644 sd/source/ui/inc/MasterPageObserver.hxx create mode 100644 sd/source/ui/inc/MediaObjectBar.hxx create mode 100644 sd/source/ui/inc/NavigatorChildWindow.hxx create mode 100644 sd/source/ui/inc/OutlineBulletDlg.hxx create mode 100644 sd/source/ui/inc/OutlineView.hxx create mode 100644 sd/source/ui/inc/OutlineViewShell.hxx create mode 100644 sd/source/ui/inc/OutlineViewShellBase.hxx create mode 100644 sd/source/ui/inc/OutlinerIteratorImpl.hxx create mode 100644 sd/source/ui/inc/PaneChildWindows.hxx create mode 100644 sd/source/ui/inc/PaneDockingWindow.hxx create mode 100644 sd/source/ui/inc/PaneShells.hxx create mode 100644 sd/source/ui/inc/PresentationViewShell.hxx create mode 100644 sd/source/ui/inc/PresentationViewShellBase.hxx create mode 100644 sd/source/ui/inc/PreviewRenderer.hxx create mode 100644 sd/source/ui/inc/RemoteServer.hxx create mode 100644 sd/source/ui/inc/Ruler.hxx create mode 100644 sd/source/ui/inc/SdUnoDrawView.hxx create mode 100644 sd/source/ui/inc/SdUnoOutlineView.hxx create mode 100644 sd/source/ui/inc/SdUnoSlideView.hxx create mode 100644 sd/source/ui/inc/ShellFactory.hxx create mode 100644 sd/source/ui/inc/SlideSorter.hxx create mode 100644 sd/source/ui/inc/SlideSorterViewShell.hxx create mode 100644 sd/source/ui/inc/SlideSorterViewShellBase.hxx create mode 100644 sd/source/ui/inc/SlideTransitionPane.hxx create mode 100644 sd/source/ui/inc/SpellDialogChildWindow.hxx create mode 100644 sd/source/ui/inc/TabControl.hxx create mode 100644 sd/source/ui/inc/TableDesignPane.hxx create mode 100644 sd/source/ui/inc/TemplateScanner.hxx create mode 100644 sd/source/ui/inc/TextObjectBar.hxx create mode 100644 sd/source/ui/inc/ToolBarManager.hxx create mode 100644 sd/source/ui/inc/View.hxx create mode 100644 sd/source/ui/inc/ViewClipboard.hxx create mode 100644 sd/source/ui/inc/ViewShell.hxx create mode 100644 sd/source/ui/inc/ViewShellBase.hxx create mode 100644 sd/source/ui/inc/ViewShellHint.hxx create mode 100644 sd/source/ui/inc/ViewShellImplementation.hxx create mode 100644 sd/source/ui/inc/ViewShellManager.hxx create mode 100644 sd/source/ui/inc/ViewTabBar.hxx create mode 100644 sd/source/ui/inc/Window.hxx create mode 100644 sd/source/ui/inc/WindowUpdater.hxx create mode 100644 sd/source/ui/inc/animobjs.hxx create mode 100644 sd/source/ui/inc/annotationmanager.hxx create mode 100644 sd/source/ui/inc/assclass.hxx create mode 100644 sd/source/ui/inc/bulmaper.hxx create mode 100644 sd/source/ui/inc/copydlg.hxx create mode 100644 sd/source/ui/inc/createtableobjectbar.hxx create mode 100644 sd/source/ui/inc/custsdlg.hxx create mode 100644 sd/source/ui/inc/diactrl.hxx create mode 100644 sd/source/ui/inc/dlg_char.hxx create mode 100644 sd/source/ui/inc/dlgfield.hxx create mode 100644 sd/source/ui/inc/dlgpage.hxx create mode 100644 sd/source/ui/inc/dlgsnap.hxx create mode 100644 sd/source/ui/inc/drawview.hxx create mode 100644 sd/source/ui/inc/filedlg.hxx create mode 100644 sd/source/ui/inc/framework/Configuration.hxx create mode 100644 sd/source/ui/inc/framework/ConfigurationController.hxx create mode 100644 sd/source/ui/inc/framework/DrawModule.hxx create mode 100644 sd/source/ui/inc/framework/FrameworkHelper.hxx create mode 100644 sd/source/ui/inc/framework/ImpressModule.hxx create mode 100644 sd/source/ui/inc/framework/ModuleController.hxx create mode 100644 sd/source/ui/inc/framework/Pane.hxx create mode 100644 sd/source/ui/inc/framework/PresentationFactory.hxx create mode 100644 sd/source/ui/inc/framework/PresentationModule.hxx create mode 100644 sd/source/ui/inc/framework/ResourceId.hxx create mode 100644 sd/source/ui/inc/framework/ViewShellWrapper.hxx create mode 100644 sd/source/ui/inc/fuarea.hxx create mode 100644 sd/source/ui/inc/fubullet.hxx create mode 100644 sd/source/ui/inc/fuchar.hxx create mode 100644 sd/source/ui/inc/fucon3d.hxx create mode 100644 sd/source/ui/inc/fuconarc.hxx create mode 100644 sd/source/ui/inc/fuconbez.hxx create mode 100644 sd/source/ui/inc/fuconcs.hxx create mode 100644 sd/source/ui/inc/fuconnct.hxx create mode 100644 sd/source/ui/inc/fuconrec.hxx create mode 100644 sd/source/ui/inc/fuconstr.hxx create mode 100644 sd/source/ui/inc/fuconuno.hxx create mode 100644 sd/source/ui/inc/fucopy.hxx create mode 100644 sd/source/ui/inc/fucushow.hxx create mode 100644 sd/source/ui/inc/fudraw.hxx create mode 100644 sd/source/ui/inc/fudspord.hxx create mode 100644 sd/source/ui/inc/fuediglu.hxx create mode 100644 sd/source/ui/inc/fuexecuteinteraction.hxx create mode 100644 sd/source/ui/inc/fuexpand.hxx create mode 100644 sd/source/ui/inc/fuformatpaintbrush.hxx create mode 100644 sd/source/ui/inc/fuhhconv.hxx create mode 100644 sd/source/ui/inc/fuinsert.hxx create mode 100644 sd/source/ui/inc/fuinsfil.hxx create mode 100644 sd/source/ui/inc/fuline.hxx create mode 100644 sd/source/ui/inc/fulinend.hxx create mode 100644 sd/source/ui/inc/fulink.hxx create mode 100644 sd/source/ui/inc/fumeasur.hxx create mode 100644 sd/source/ui/inc/fumorph.hxx create mode 100644 sd/source/ui/inc/funavig.hxx create mode 100644 sd/source/ui/inc/fuoaprms.hxx create mode 100644 sd/source/ui/inc/fuolbull.hxx create mode 100644 sd/source/ui/inc/fuoltext.hxx create mode 100644 sd/source/ui/inc/fupage.hxx create mode 100644 sd/source/ui/inc/fuparagr.hxx create mode 100644 sd/source/ui/inc/fupoor.hxx create mode 100644 sd/source/ui/inc/fuprlout.hxx create mode 100644 sd/source/ui/inc/fuprobjs.hxx create mode 100644 sd/source/ui/inc/fuscale.hxx create mode 100644 sd/source/ui/inc/fusearch.hxx create mode 100644 sd/source/ui/inc/fusel.hxx create mode 100644 sd/source/ui/inc/fusldlg.hxx create mode 100644 sd/source/ui/inc/fusnapln.hxx create mode 100644 sd/source/ui/inc/fusumry.hxx create mode 100644 sd/source/ui/inc/futempl.hxx create mode 100644 sd/source/ui/inc/futext.hxx create mode 100644 sd/source/ui/inc/futhes.hxx create mode 100644 sd/source/ui/inc/futransf.hxx create mode 100644 sd/source/ui/inc/futxtatt.hxx create mode 100644 sd/source/ui/inc/fuvect.hxx create mode 100644 sd/source/ui/inc/fuzoom.hxx create mode 100644 sd/source/ui/inc/gluectrl.hxx create mode 100644 sd/source/ui/inc/headerfooterdlg.hxx create mode 100644 sd/source/ui/inc/ins_paste.hxx create mode 100644 sd/source/ui/inc/inspagob.hxx create mode 100644 sd/source/ui/inc/layeroptionsdlg.hxx create mode 100644 sd/source/ui/inc/masterlayoutdlg.hxx create mode 100644 sd/source/ui/inc/morphdlg.hxx create mode 100644 sd/source/ui/inc/navigatr.hxx create mode 100644 sd/source/ui/inc/optsitem.hxx create mode 100644 sd/source/ui/inc/paragr.hxx create mode 100644 sd/source/ui/inc/pgjump.hxx create mode 100644 sd/source/ui/inc/present.hxx create mode 100644 sd/source/ui/inc/prltempl.hxx create mode 100644 sd/source/ui/inc/prntopts.hxx create mode 100644 sd/source/ui/inc/pubdlg.hxx create mode 100644 sd/source/ui/inc/registerinterfaces.hxx create mode 100644 sd/source/ui/inc/scalectrl.hxx create mode 100644 sd/source/ui/inc/sdpopup.hxx create mode 100644 sd/source/ui/inc/sdpreslt.hxx create mode 100644 sd/source/ui/inc/sdtreelb.hxx create mode 100644 sd/source/ui/inc/sdundogr.hxx create mode 100644 sd/source/ui/inc/sdxfer.hxx create mode 100644 sd/source/ui/inc/slideshow.hxx create mode 100644 sd/source/ui/inc/smarttag.hxx create mode 100644 sd/source/ui/inc/tablefunction.hxx create mode 100644 sd/source/ui/inc/tabtempl.hxx create mode 100644 sd/source/ui/inc/titledockwin.hxx create mode 100644 sd/source/ui/inc/tmplctrl.hxx create mode 100644 sd/source/ui/inc/tools/AsynchronousCall.hxx create mode 100644 sd/source/ui/inc/tools/AsynchronousTask.hxx create mode 100644 sd/source/ui/inc/tools/ConfigurationAccess.hxx create mode 100644 sd/source/ui/inc/tools/GraphicSizeCheck.hxx create mode 100644 sd/source/ui/inc/tools/IconCache.hxx create mode 100644 sd/source/ui/inc/tools/IdleDetection.hxx create mode 100644 sd/source/ui/inc/tools/PropertySet.hxx create mode 100644 sd/source/ui/inc/tools/SdGlobalResourceContainer.hxx create mode 100644 sd/source/ui/inc/tools/SlotStateListener.hxx create mode 100644 sd/source/ui/inc/tools/TimerBasedTaskExecution.hxx create mode 100644 sd/source/ui/inc/tpaction.hxx create mode 100644 sd/source/ui/inc/tpoption.hxx create mode 100644 sd/source/ui/inc/uiobject.hxx create mode 100644 sd/source/ui/inc/unchss.hxx create mode 100644 sd/source/ui/inc/undoback.hxx create mode 100644 sd/source/ui/inc/undoheaderfooter.hxx create mode 100644 sd/source/ui/inc/undolayer.hxx create mode 100644 sd/source/ui/inc/undopage.hxx create mode 100644 sd/source/ui/inc/unmodpg.hxx create mode 100644 sd/source/ui/inc/unmovss.hxx create mode 100644 sd/source/ui/inc/unoaprms.hxx create mode 100644 sd/source/ui/inc/unokywds.hxx create mode 100644 sd/source/ui/inc/unomodel.hxx create mode 100644 sd/source/ui/inc/unopage.hxx create mode 100644 sd/source/ui/inc/unoprnms.hxx create mode 100644 sd/source/ui/inc/unosrch.hxx create mode 100644 sd/source/ui/inc/unprlout.hxx create mode 100644 sd/source/ui/inc/vectdlg.hxx create mode 100644 sd/source/ui/inc/view/viewoverlaymanager.hxx create mode 100644 sd/source/ui/inc/zoomlist.hxx create mode 100644 sd/source/ui/presenter/CanvasUpdateRequester.cxx create mode 100644 sd/source/ui/presenter/CanvasUpdateRequester.hxx create mode 100644 sd/source/ui/presenter/PresenterCanvas.cxx create mode 100644 sd/source/ui/presenter/PresenterCanvas.hxx create mode 100644 sd/source/ui/presenter/PresenterHelper.cxx create mode 100644 sd/source/ui/presenter/PresenterHelper.hxx create mode 100644 sd/source/ui/presenter/PresenterPreviewCache.cxx create mode 100644 sd/source/ui/presenter/PresenterPreviewCache.hxx create mode 100644 sd/source/ui/presenter/PresenterTextView.cxx create mode 100644 sd/source/ui/presenter/PresenterTextView.hxx create mode 100644 sd/source/ui/presenter/SlideRenderer.cxx create mode 100644 sd/source/ui/presenter/SlideRenderer.hxx create mode 100644 sd/source/ui/remotecontrol/AvahiNetworkService.cxx create mode 100644 sd/source/ui/remotecontrol/AvahiNetworkService.hxx create mode 100644 sd/source/ui/remotecontrol/BluetoothServer.cxx create mode 100644 sd/source/ui/remotecontrol/BluetoothServer.hxx create mode 100644 sd/source/ui/remotecontrol/BluetoothServer.mm create mode 100644 sd/source/ui/remotecontrol/BluetoothServiceRecord.hxx create mode 100644 sd/source/ui/remotecontrol/BufferedStreamSocket.cxx create mode 100644 sd/source/ui/remotecontrol/BufferedStreamSocket.hxx create mode 100644 sd/source/ui/remotecontrol/Communicator.cxx create mode 100644 sd/source/ui/remotecontrol/Communicator.hxx create mode 100644 sd/source/ui/remotecontrol/DiscoveryService.cxx create mode 100644 sd/source/ui/remotecontrol/DiscoveryService.hxx create mode 100644 sd/source/ui/remotecontrol/DiscoveryService.mm create mode 100644 sd/source/ui/remotecontrol/IBluetoothSocket.hxx create mode 100644 sd/source/ui/remotecontrol/ImagePreparer.cxx create mode 100644 sd/source/ui/remotecontrol/ImagePreparer.hxx create mode 100644 sd/source/ui/remotecontrol/Listener.cxx create mode 100644 sd/source/ui/remotecontrol/Listener.hxx create mode 100644 sd/source/ui/remotecontrol/OSXBluetooth.h create mode 100644 sd/source/ui/remotecontrol/OSXBluetooth.mm create mode 100644 sd/source/ui/remotecontrol/OSXBluetoothWrapper.hxx create mode 100644 sd/source/ui/remotecontrol/OSXNetworkService.h create mode 100644 sd/source/ui/remotecontrol/OSXNetworkService.hxx create mode 100644 sd/source/ui/remotecontrol/OSXNetworkService.mm create mode 100644 sd/source/ui/remotecontrol/Receiver.cxx create mode 100644 sd/source/ui/remotecontrol/Receiver.hxx create mode 100644 sd/source/ui/remotecontrol/Server.cxx create mode 100644 sd/source/ui/remotecontrol/Transmitter.cxx create mode 100644 sd/source/ui/remotecontrol/Transmitter.hxx create mode 100644 sd/source/ui/remotecontrol/WINNetworkService.cxx create mode 100644 sd/source/ui/remotecontrol/WINNetworkService.hxx create mode 100644 sd/source/ui/remotecontrol/ZeroconfService.hxx create mode 100644 sd/source/ui/sidebar/AllMasterPagesSelector.cxx create mode 100644 sd/source/ui/sidebar/AllMasterPagesSelector.hxx create mode 100644 sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx create mode 100644 sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx create mode 100644 sd/source/ui/sidebar/DocumentHelper.cxx create mode 100644 sd/source/ui/sidebar/DocumentHelper.hxx create mode 100644 sd/source/ui/sidebar/IDisposable.hxx create mode 100644 sd/source/ui/sidebar/ISidebarReceiver.hxx create mode 100644 sd/source/ui/sidebar/LayoutMenu.cxx create mode 100644 sd/source/ui/sidebar/LayoutMenu.hxx create mode 100644 sd/source/ui/sidebar/MasterPageContainer.cxx create mode 100644 sd/source/ui/sidebar/MasterPageContainer.hxx create mode 100644 sd/source/ui/sidebar/MasterPageContainerFiller.cxx create mode 100644 sd/source/ui/sidebar/MasterPageContainerFiller.hxx create mode 100644 sd/source/ui/sidebar/MasterPageContainerProviders.cxx create mode 100644 sd/source/ui/sidebar/MasterPageContainerProviders.hxx create mode 100644 sd/source/ui/sidebar/MasterPageContainerQueue.cxx create mode 100644 sd/source/ui/sidebar/MasterPageContainerQueue.hxx create mode 100644 sd/source/ui/sidebar/MasterPageDescriptor.cxx create mode 100644 sd/source/ui/sidebar/MasterPageDescriptor.hxx create mode 100644 sd/source/ui/sidebar/MasterPageObserver.cxx create mode 100644 sd/source/ui/sidebar/MasterPagesSelector.cxx create mode 100644 sd/source/ui/sidebar/MasterPagesSelector.hxx create mode 100644 sd/source/ui/sidebar/NavigatorWrapper.cxx create mode 100644 sd/source/ui/sidebar/NavigatorWrapper.hxx create mode 100644 sd/source/ui/sidebar/PageMarginUtils.hxx create mode 100644 sd/source/ui/sidebar/PanelFactory.cxx create mode 100644 sd/source/ui/sidebar/PanelFactory.hxx create mode 100644 sd/source/ui/sidebar/PreviewValueSet.cxx create mode 100644 sd/source/ui/sidebar/PreviewValueSet.hxx create mode 100644 sd/source/ui/sidebar/RecentMasterPagesSelector.cxx create mode 100644 sd/source/ui/sidebar/RecentMasterPagesSelector.hxx create mode 100644 sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx create mode 100644 sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx create mode 100644 sd/source/ui/sidebar/SlideBackground.cxx create mode 100644 sd/source/ui/sidebar/SlideBackground.hxx create mode 100644 sd/source/ui/slideshow/PaneHider.cxx create mode 100644 sd/source/ui/slideshow/PaneHider.hxx create mode 100644 sd/source/ui/slideshow/SlideShowRestarter.cxx create mode 100644 sd/source/ui/slideshow/SlideShowRestarter.hxx create mode 100644 sd/source/ui/slideshow/showwin.cxx create mode 100644 sd/source/ui/slideshow/showwindow.hxx create mode 100644 sd/source/ui/slideshow/slideshow.cxx create mode 100644 sd/source/ui/slideshow/slideshowimpl.cxx create mode 100644 sd/source/ui/slideshow/slideshowimpl.hxx create mode 100644 sd/source/ui/slideshow/slideshowviewimpl.cxx create mode 100644 sd/source/ui/slideshow/slideshowviewimpl.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsPageCache.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx create mode 100644 sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx create mode 100644 sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx create mode 100644 sd/source/ui/slidesorter/controller/SlideSorterController.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsAnimationFunction.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsAnimator.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsClipboard.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsDragAndDropContext.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsDragAndDropContext.hxx create mode 100644 sd/source/ui/slidesorter/controller/SlsFocusManager.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsInsertionIndicatorHandler.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsListener.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsListener.hxx create mode 100644 sd/source/ui/slidesorter/controller/SlsPageSelector.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsProperties.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsScrollBarManager.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsSelectionObserver.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsSlotManager.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsTransferableData.cxx create mode 100644 sd/source/ui/slidesorter/controller/SlsVisibleAreaManager.cxx create mode 100644 sd/source/ui/slidesorter/inc/cache/SlsCacheContext.hxx create mode 100644 sd/source/ui/slidesorter/inc/cache/SlsPageCache.hxx create mode 100644 sd/source/ui/slidesorter/inc/cache/SlsPageCacheManager.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlideSorterController.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsAnimationFunction.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsAnimator.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsCurrentSlideManager.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsFocusManager.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsInsertionIndicatorHandler.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsPageSelector.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsProperties.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsScrollBarManager.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsSelectionFunction.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsSelectionManager.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsSelectionObserver.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsSlotManager.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsTransferableData.hxx create mode 100644 sd/source/ui/slidesorter/inc/controller/SlsVisibleAreaManager.hxx create mode 100644 sd/source/ui/slidesorter/inc/model/SlideSorterModel.hxx create mode 100644 sd/source/ui/slidesorter/inc/model/SlsEnumeration.hxx create mode 100644 sd/source/ui/slidesorter/inc/model/SlsPageDescriptor.hxx create mode 100644 sd/source/ui/slidesorter/inc/model/SlsPageEnumeration.hxx create mode 100644 sd/source/ui/slidesorter/inc/model/SlsPageEnumerationProvider.hxx create mode 100644 sd/source/ui/slidesorter/inc/model/SlsSharedPageDescriptor.hxx create mode 100644 sd/source/ui/slidesorter/inc/model/SlsVisualState.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlideSorterView.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlsILayerPainter.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlsInsertAnimator.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlsInsertionIndicatorOverlay.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlsLayouter.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlsPageObjectLayouter.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlsPageObjectPainter.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlsTheme.hxx create mode 100644 sd/source/ui/slidesorter/inc/view/SlsToolTip.hxx create mode 100644 sd/source/ui/slidesorter/model/SlideSorterModel.cxx create mode 100644 sd/source/ui/slidesorter/model/SlsPageDescriptor.cxx create mode 100644 sd/source/ui/slidesorter/model/SlsPageEnumeration.cxx create mode 100644 sd/source/ui/slidesorter/model/SlsPageEnumerationProvider.cxx create mode 100644 sd/source/ui/slidesorter/model/SlsVisualState.cxx create mode 100644 sd/source/ui/slidesorter/shell/SlideSorter.cxx create mode 100644 sd/source/ui/slidesorter/shell/SlideSorterService.cxx create mode 100644 sd/source/ui/slidesorter/shell/SlideSorterService.hxx create mode 100644 sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx create mode 100644 sd/source/ui/slidesorter/view/SlideSorterView.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsFramePainter.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsFramePainter.hxx create mode 100644 sd/source/ui/slidesorter/view/SlsInsertAnimator.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx create mode 100644 sd/source/ui/slidesorter/view/SlsLayouter.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsPageObjectLayouter.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsTheme.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsToolTip.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsViewCacheContext.cxx create mode 100644 sd/source/ui/slidesorter/view/SlsViewCacheContext.hxx create mode 100644 sd/source/ui/table/TableDesignPane.cxx create mode 100644 sd/source/ui/table/tablefunction.cxx create mode 100644 sd/source/ui/table/tableobjectbar.cxx create mode 100644 sd/source/ui/table/tableobjectbar.hxx create mode 100644 sd/source/ui/tools/AsynchronousCall.cxx create mode 100644 sd/source/ui/tools/ConfigurationAccess.cxx create mode 100644 sd/source/ui/tools/EventMultiplexer.cxx create mode 100644 sd/source/ui/tools/GraphicSizeCheck.cxx create mode 100644 sd/source/ui/tools/IconCache.cxx create mode 100644 sd/source/ui/tools/IdleDetection.cxx create mode 100644 sd/source/ui/tools/PreviewRenderer.cxx create mode 100644 sd/source/ui/tools/PropertySet.cxx create mode 100644 sd/source/ui/tools/SdGlobalResourceContainer.cxx create mode 100644 sd/source/ui/tools/SlotStateListener.cxx create mode 100644 sd/source/ui/tools/TimerBasedTaskExecution.cxx create mode 100644 sd/source/ui/uitest/uiobject.cxx create mode 100644 sd/source/ui/unoidl/DrawController.cxx create mode 100644 sd/source/ui/unoidl/SdUnoDrawView.cxx create mode 100644 sd/source/ui/unoidl/SdUnoOutlineView.cxx create mode 100644 sd/source/ui/unoidl/SdUnoSlideView.cxx create mode 100644 sd/source/ui/unoidl/UnoDocumentSettings.cxx create mode 100644 sd/source/ui/unoidl/UnoDocumentSettings.hxx create mode 100644 sd/source/ui/unoidl/randomnode.cxx create mode 100644 sd/source/ui/unoidl/sddetect.cxx create mode 100644 sd/source/ui/unoidl/sddetect.hxx create mode 100644 sd/source/ui/unoidl/unocpres.cxx create mode 100644 sd/source/ui/unoidl/unocpres.hxx create mode 100644 sd/source/ui/unoidl/unodoc.cxx create mode 100644 sd/source/ui/unoidl/unolayer.cxx create mode 100644 sd/source/ui/unoidl/unolayer.hxx create mode 100644 sd/source/ui/unoidl/unomodel.cxx create mode 100644 sd/source/ui/unoidl/unomodule.cxx create mode 100644 sd/source/ui/unoidl/unomodule.hxx create mode 100644 sd/source/ui/unoidl/unoobj.cxx create mode 100644 sd/source/ui/unoidl/unoobj.hxx create mode 100644 sd/source/ui/unoidl/unopage.cxx create mode 100644 sd/source/ui/unoidl/unopback.cxx create mode 100644 sd/source/ui/unoidl/unopback.hxx create mode 100644 sd/source/ui/unoidl/unopool.cxx create mode 100644 sd/source/ui/unoidl/unopool.hxx create mode 100644 sd/source/ui/unoidl/unosrch.cxx create mode 100644 sd/source/ui/unoidl/unowcntr.cxx create mode 100644 sd/source/ui/unoidl/unowcntr.hxx create mode 100644 sd/source/ui/view/DocumentRenderer.cxx create mode 100644 sd/source/ui/view/FormShellManager.cxx create mode 100644 sd/source/ui/view/GraphicObjectBar.cxx create mode 100644 sd/source/ui/view/GraphicViewShellBase.cxx create mode 100644 sd/source/ui/view/ImpressViewShellBase.cxx create mode 100644 sd/source/ui/view/MediaObjectBar.cxx create mode 100644 sd/source/ui/view/OutlineViewShellBase.cxx create mode 100644 sd/source/ui/view/Outliner.cxx create mode 100644 sd/source/ui/view/OutlinerIterator.cxx create mode 100644 sd/source/ui/view/PresentationViewShellBase.cxx create mode 100644 sd/source/ui/view/SlideSorterViewShellBase.cxx create mode 100644 sd/source/ui/view/ToolBarManager.cxx create mode 100644 sd/source/ui/view/ViewClipboard.cxx create mode 100644 sd/source/ui/view/ViewShellBase.cxx create mode 100644 sd/source/ui/view/ViewShellHint.cxx create mode 100644 sd/source/ui/view/ViewShellImplementation.cxx create mode 100644 sd/source/ui/view/ViewShellManager.cxx create mode 100644 sd/source/ui/view/ViewTabBar.cxx create mode 100644 sd/source/ui/view/WindowUpdater.cxx create mode 100644 sd/source/ui/view/clview.cxx create mode 100644 sd/source/ui/view/drawview.cxx create mode 100644 sd/source/ui/view/drbezob.cxx create mode 100644 sd/source/ui/view/drtxtob.cxx create mode 100644 sd/source/ui/view/drtxtob1.cxx create mode 100644 sd/source/ui/view/drviews1.cxx create mode 100644 sd/source/ui/view/drviews2.cxx create mode 100644 sd/source/ui/view/drviews3.cxx create mode 100644 sd/source/ui/view/drviews4.cxx create mode 100644 sd/source/ui/view/drviews5.cxx create mode 100644 sd/source/ui/view/drviews6.cxx create mode 100644 sd/source/ui/view/drviews7.cxx create mode 100644 sd/source/ui/view/drviews8.cxx create mode 100644 sd/source/ui/view/drviews9.cxx create mode 100644 sd/source/ui/view/drviewsa.cxx create mode 100644 sd/source/ui/view/drviewsb.cxx create mode 100644 sd/source/ui/view/drviewsc.cxx create mode 100644 sd/source/ui/view/drviewsd.cxx create mode 100644 sd/source/ui/view/drviewse.cxx create mode 100644 sd/source/ui/view/drviewsf.cxx create mode 100644 sd/source/ui/view/drviewsg.cxx create mode 100644 sd/source/ui/view/drviewsh.cxx create mode 100644 sd/source/ui/view/drviewsi.cxx create mode 100644 sd/source/ui/view/drviewsj.cxx create mode 100644 sd/source/ui/view/drviewsk.cxx create mode 100644 sd/source/ui/view/drvwshrg.cxx create mode 100644 sd/source/ui/view/frmview.cxx create mode 100644 sd/source/ui/view/grviewsh.cxx create mode 100644 sd/source/ui/view/outlnvs2.cxx create mode 100644 sd/source/ui/view/outlnvsh.cxx create mode 100644 sd/source/ui/view/outlview.cxx create mode 100644 sd/source/ui/view/presvish.cxx create mode 100644 sd/source/ui/view/sdruler.cxx create mode 100644 sd/source/ui/view/sdview.cxx create mode 100644 sd/source/ui/view/sdview2.cxx create mode 100644 sd/source/ui/view/sdview3.cxx create mode 100644 sd/source/ui/view/sdview4.cxx create mode 100644 sd/source/ui/view/sdview5.cxx create mode 100644 sd/source/ui/view/sdwindow.cxx create mode 100644 sd/source/ui/view/tabcontr.cxx create mode 100644 sd/source/ui/view/unmodpg.cxx create mode 100644 sd/source/ui/view/viewoverlaymanager.cxx create mode 100644 sd/source/ui/view/viewshe2.cxx create mode 100644 sd/source/ui/view/viewshe3.cxx create mode 100644 sd/source/ui/view/viewshel.cxx create mode 100644 sd/source/ui/view/zoomlist.cxx (limited to 'sd/source') diff --git a/sd/source/core/CustomAnimationCloner.cxx b/sd/source/core/CustomAnimationCloner.cxx new file mode 100644 index 000000000..ea42e3bac --- /dev/null +++ b/sd/source/core/CustomAnimationCloner.cxx @@ -0,0 +1,307 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::container; + +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::beans::NamedValue; + +namespace sd +{ + namespace { + + class CustomAnimationClonerImpl + { + public: + CustomAnimationClonerImpl(); + Reference< XAnimationNode > Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSource, const SdPage* pTarget ); + + private: + void transformNode( const Reference< XAnimationNode >& xNode ); + Any transformValue( const Any& rValue ); + + Reference< XShape > getClonedShape( const Reference< XShape >& xSource ) const; + Reference< XAnimationNode > getClonedNode( const Reference< XAnimationNode >& xSource ) const; + + mutable ::std::map< Reference< XShape >, Reference< XShape > > maShapeMap; + std::vector< Reference< XAnimationNode > > maSourceNodeVector; + std::vector< Reference< XAnimationNode > > maCloneNodeVector; + }; + + } + + CustomAnimationClonerImpl::CustomAnimationClonerImpl() + { + } + + Reference< XAnimationNode > Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSource, const SdPage* pTarget ) + { + CustomAnimationClonerImpl aCloner; + return aCloner.Clone( xSourceNode, pSource, pTarget ); + } + + Reference< XAnimationNode > CustomAnimationClonerImpl::Clone( const Reference< XAnimationNode >& xSourceNode, const SdPage* pSourcePage, const SdPage* pTargetPage ) + { + try + { + // clone animation hierarchy + Reference< css::util::XCloneable > xClonable( xSourceNode, UNO_QUERY_THROW ); + Reference< XAnimationNode > xCloneNode( xClonable->createClone(), UNO_QUERY_THROW ); + + // create a dictionary to map source to cloned shapes + if( pSourcePage && pTargetPage ) + { + SdrObjListIter aSourceIter( pSourcePage, SdrIterMode::DeepWithGroups ); + SdrObjListIter aTargetIter( pTargetPage, SdrIterMode::DeepWithGroups ); + + while( aSourceIter.IsMore() && aTargetIter.IsMore() ) + { + SdrObject* pSource = aSourceIter.Next(); + SdrObject* pTarget = aTargetIter.Next(); + + if( pSource && pTarget) + { + Reference< XShape > xSource( pSource->getUnoShape(), UNO_QUERY ); + Reference< XShape > xTarget( pTarget->getUnoShape(), UNO_QUERY ); + if( xSource.is() && xTarget.is() ) + { + maShapeMap[xSource] = xTarget; + } + } + } + } + + // create a dictionary to map source to cloned nodes + ::anim::create_deep_vector( xSourceNode, maSourceNodeVector ); + ::anim::create_deep_vector( xCloneNode, maCloneNodeVector ); + + transformNode( xCloneNode ); + + return xCloneNode; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationClonerImpl::Clone()" ); + Reference< XAnimationNode > xEmpty; + return xEmpty; + } + } + + void CustomAnimationClonerImpl::transformNode( const Reference< XAnimationNode >& xNode ) + { + try + { + xNode->setBegin( transformValue( xNode->getBegin() ) ); + xNode->setEnd( transformValue( xNode->getEnd() ) ); + + sal_Int16 nNodeType( xNode->getType() ); + switch( nNodeType ) + { + case AnimationNodeType::ITERATE: + { + Reference< XIterateContainer > xIter( xNode, UNO_QUERY_THROW ); + xIter->setTarget( transformValue( xIter->getTarget() ) ); + [[fallthrough]]; + } + case AnimationNodeType::PAR: + case AnimationNodeType::SEQ: + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + transformNode( xChildNode ); + } + } + break; + + case AnimationNodeType::ANIMATE: + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATEMOTION: + case AnimationNodeType::ANIMATEPHYSICS: + case AnimationNodeType::ANIMATECOLOR: + case AnimationNodeType::ANIMATETRANSFORM: + case AnimationNodeType::TRANSITIONFILTER: + { + Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW ); + xAnimate->setTarget( transformValue( xAnimate->getTarget() ) ); + } + break; + + case AnimationNodeType::COMMAND: + { + Reference< XCommand > xCommand( xNode, UNO_QUERY_THROW ); + xCommand->setTarget( transformValue( xCommand->getTarget() ) ); + } + break; + + case AnimationNodeType::AUDIO: + { + Reference< XAudio > xAudio( xNode, UNO_QUERY_THROW ); + xAudio->setSource( transformValue( xAudio->getSource() ) ); + } + break; + } + + Sequence< NamedValue > aUserData( xNode->getUserData() ); + if( aUserData.hasElements() ) + { + for( NamedValue & namedValue : asNonConstRange(aUserData) ) + { + namedValue.Value = transformValue( namedValue.Value ); + } + + xNode->setUserData( aUserData ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationClonerImpl::transformNode()" ); + } + } + + Any CustomAnimationClonerImpl::transformValue( const Any& rValue ) + { + if( rValue.hasValue() ) try + { + if( rValue.getValueType() == cppu::UnoType::get() ) + { + ValuePair aValuePair; + rValue >>= aValuePair; + + aValuePair.First = transformValue( aValuePair.First ); + aValuePair.Second = transformValue( aValuePair.Second ); + + return Any( aValuePair ); + } + else if( rValue.getValueType() == cppu::UnoType< Sequence >::get() ) + { + Sequence aSequence; + rValue >>= aSequence; + + for( Any& rAny : asNonConstRange(aSequence) ) + rAny = transformValue( rAny ); + + return Any( aSequence ); + } + else if( rValue.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference< XShape > xShape; + rValue >>= xShape; + if( xShape.is() ) + { + return Any( getClonedShape( xShape ) ); + } + else + { + Reference< XAnimationNode > xNode; + rValue >>= xNode; + if( xNode.is() ) + return Any( getClonedNode( xNode ) ); + } + } + else if( rValue.getValueType() == cppu::UnoType::get() ) + { + ParagraphTarget aParaTarget; + rValue >>= aParaTarget; + + aParaTarget.Shape = getClonedShape( aParaTarget.Shape ); + + return Any( aParaTarget ); + } + else if( rValue.getValueType() == cppu::UnoType::get() ) + { + Event aEvent; + rValue >>= aEvent; + + aEvent.Source = transformValue( aEvent.Source ); + + return Any( aEvent ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationClonerImpl::transformValue()" ); + } + + return rValue; + } + + Reference< XShape > CustomAnimationClonerImpl::getClonedShape( const Reference< XShape >& xSource ) const + { + if( xSource.is() ) + { + if( maShapeMap.find(xSource) != maShapeMap.end() ) + { + return maShapeMap[xSource]; + } + + DBG_ASSERT( maShapeMap.empty(), "sd::CustomAnimationClonerImpl::getClonedShape() failed!" ); + } + return xSource; + } + + Reference< XAnimationNode > CustomAnimationClonerImpl::getClonedNode( const Reference< XAnimationNode >& xSource ) const + { + std::size_t nNodeCount = maSourceNodeVector.size(); + std::size_t nCloneNodeCount = maCloneNodeVector.size(); + + if (nNodeCount != nCloneNodeCount) + SAL_WARN("sd.core", "Sizes of maSourceNodeVector and maCloneNodeVector mismatch!"); + + for( std::size_t nNode = 0; nNode < nNodeCount && nNode < nCloneNodeCount; ++nNode ) + { + if( maSourceNodeVector[nNode] == xSource ) + return maCloneNodeVector[nNode]; + } + + OSL_FAIL( "sd::CustomAnimationClonerImpl::getClonedNode() failed!" ); + return xSource; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/CustomAnimationEffect.cxx b/sd/source/core/CustomAnimationEffect.cxx new file mode 100644 index 000000000..b1816784f --- /dev/null +++ b/sd/source/core/CustomAnimationEffect.cxx @@ -0,0 +1,3559 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::animations; + +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::container::XChild; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::lang::XInitialization; +using ::com::sun::star::text::XText; +using ::com::sun::star::text::XTextRange; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::util::XCloneable; +using ::com::sun::star::lang::Locale; +using ::com::sun::star::util::XChangesNotifier; +using ::com::sun::star::util::XChangesListener; + +namespace sd +{ +class MainSequenceChangeGuard +{ +public: + explicit MainSequenceChangeGuard( EffectSequenceHelper* pSequence ) + { + mpMainSequence = dynamic_cast< MainSequence* >( pSequence ); + if( mpMainSequence == nullptr ) + { + InteractiveSequence* pI = dynamic_cast< InteractiveSequence* >( pSequence ); + if( pI ) + mpMainSequence = pI->mpMainSequence; + } + DBG_ASSERT( mpMainSequence, "sd::MainSequenceChangeGuard::MainSequenceChangeGuard(), no main sequence to guard!" ); + + if( mpMainSequence ) + mpMainSequence->mbIgnoreChanges++; + } + + ~MainSequenceChangeGuard() + { + if( mpMainSequence ) + mpMainSequence->mbIgnoreChanges++; + } + +private: + MainSequence* mpMainSequence; +}; + +CustomAnimationEffect::CustomAnimationEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +: mnNodeType(-1), + mnPresetClass(-1), + mnFill(AnimationFill::HOLD), + mfBegin(-1.0), + mfDuration(-1.0), + mfAbsoluteDuration(-1.0), + mnGroupId(-1), + mnIterateType(0), + mfIterateInterval(0.0), + mnParaDepth( -1 ), + mbHasText(false), + mfAcceleration( 1.0 ), + mfDecelerate( 1.0 ), + mbAutoReverse(false), + mnTargetSubItem(0), + mnCommand(0), + mpEffectSequence( nullptr ), + mbHasAfterEffect(false), + mbAfterEffectOnNextEffect(false) +{ + setNode( xNode ); +} + +void CustomAnimationEffect::setNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +{ + mxNode = xNode; + mxAudio.clear(); + mnCommand = 0; + + const Sequence< NamedValue > aUserData( mxNode->getUserData() ); + + for( const NamedValue& rProp : aUserData ) + { + if ( rProp.Name == "node-type" ) + { + rProp.Value >>= mnNodeType; + } + else if ( rProp.Name == "preset-id" ) + { + rProp.Value >>= maPresetId; + } + else if ( rProp.Name == "preset-sub-type" ) + { + rProp.Value >>= maPresetSubType; + } + else if ( rProp.Name == "preset-class" ) + { + rProp.Value >>= mnPresetClass; + } + else if ( rProp.Name == "preset-property" ) + { + rProp.Value >>= maProperty; + } + else if ( rProp.Name == "group-id" ) + { + rProp.Value >>= mnGroupId; + } + } + + // get effect start time + mxNode->getBegin() >>= mfBegin; + + mfAcceleration = mxNode->getAcceleration(); + mfDecelerate = mxNode->getDecelerate(); + mbAutoReverse = mxNode->getAutoReverse(); + + mnFill = mxNode->getFill(); + + // get iteration data + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + if( xIter.is() ) + { + mfIterateInterval = xIter->getIterateInterval(); + mnIterateType = xIter->getIterateType(); + maTarget = xIter->getTarget(); + mnTargetSubItem = xIter->getSubItem(); + } + else + { + mfIterateInterval = 0.0f; + mnIterateType = 0; + } + + // calculate effect duration and get target shape + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( !xChildNode.is() ) + continue; + + if( xChildNode->getType() == AnimationNodeType::AUDIO ) + { + mxAudio.set( xChildNode, UNO_QUERY ); + } + else if( xChildNode->getType() == AnimationNodeType::COMMAND ) + { + Reference< XCommand > xCommand( xChildNode, UNO_QUERY ); + if( xCommand.is() ) + { + mnCommand = xCommand->getCommand(); + if( !maTarget.hasValue() ) + maTarget = xCommand->getTarget(); + } + } + else + { + double fBegin = 0.0; + double fDuration = 0.0; + xChildNode->getBegin() >>= fBegin; + xChildNode->getDuration() >>= fDuration; + + fDuration += fBegin; + if( fDuration > mfDuration ) + mfDuration = fDuration; + + // no target shape yet? + if( !maTarget.hasValue() ) + { + // go get it boys! + Reference< XAnimate > xAnimate( xChildNode, UNO_QUERY ); + if( xAnimate.is() ) + { + maTarget = xAnimate->getTarget(); + mnTargetSubItem = xAnimate->getSubItem(); + } + } + } + } + } + } + + mfAbsoluteDuration = mfDuration; + double fRepeatCount = 1.0; + if( (mxNode->getRepeatCount()) >>= fRepeatCount ) + mfAbsoluteDuration *= fRepeatCount; + + checkForText(); +} + +sal_Int32 CustomAnimationEffect::getNumberOfSubitems( const Any& aTarget, sal_Int16 nIterateType ) +{ + sal_Int32 nSubItems = 0; + + try + { + // first get target text + sal_Int32 nOnlyPara = -1; + + Reference< XText > xShape; + aTarget >>= xShape; + if( !xShape.is() ) + { + ParagraphTarget aParaTarget; + if( aTarget >>= aParaTarget ) + { + xShape.set( aParaTarget.Shape, UNO_QUERY ); + nOnlyPara = aParaTarget.Paragraph; + } + } + + // now use the break iterator to iterate over the given text + // and count the sub items + + if( xShape.is() ) + { + // TODO/LATER: Optimize this, don't create a break iterator each time + Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference < i18n::XBreakIterator > xBI = i18n::BreakIterator::create(xContext); + + Reference< XEnumerationAccess > xEA( xShape, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEA->createEnumeration(), UNO_SET_THROW ); + css::lang::Locale aLocale; + static const OUStringLiteral aStrLocaleName( u"CharLocale" ); + Reference< XTextRange > xParagraph; + + sal_Int32 nPara = 0; + while( xEnumeration->hasMoreElements() ) + { + xEnumeration->nextElement() >>= xParagraph; + + // skip this if it's not the only paragraph we want to count + if( (nOnlyPara != -1) && (nOnlyPara != nPara ) ) + continue; + + if( nIterateType == TextAnimationType::BY_PARAGRAPH ) + { + nSubItems++; + } + else + { + const OUString aText( xParagraph->getString() ); + Reference< XPropertySet > xSet( xParagraph, UNO_QUERY_THROW ); + xSet->getPropertyValue( aStrLocaleName ) >>= aLocale; + + sal_Int32 nPos; + const sal_Int32 nEndPos = aText.getLength(); + + if( nIterateType == TextAnimationType::BY_WORD ) + { + for( nPos = 0; nPos < nEndPos; nPos++ ) + { + nPos = xBI->getWordBoundary(aText, nPos, aLocale, i18n::WordType::ANY_WORD, true).endPos; + nSubItems++; + } + break; + } + else + { + sal_Int32 nDone; + for( nPos = 0; nPos < nEndPos; nPos++ ) + { + nPos = xBI->nextCharacters(aText, nPos, aLocale, i18n::CharacterIteratorMode::SKIPCELL, 0, nDone); + nSubItems++; + } + } + } + + if( nPara == nOnlyPara ) + break; + + nPara++; + } + } + } + catch( Exception& ) + { + nSubItems = 0; + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getNumberOfSubitems(), exception caught!" ); + } + + return nSubItems; +} + +CustomAnimationEffect::~CustomAnimationEffect() +{ +} + +CustomAnimationEffectPtr CustomAnimationEffect::clone() const +{ + Reference< XCloneable > xCloneable( mxNode, UNO_QUERY_THROW ); + Reference< XAnimationNode > xNode( xCloneable->createClone(), UNO_QUERY_THROW ); + CustomAnimationEffectPtr pEffect = std::make_shared( xNode ); + pEffect->setEffectSequence( getEffectSequence() ); + return pEffect; +} + +sal_Int32 CustomAnimationEffect::get_node_type( const Reference< XAnimationNode >& xNode ) +{ + sal_Int16 nNodeType = -1; + + if( xNode.is() ) + { + const Sequence< NamedValue > aUserData( xNode->getUserData() ); + if( aUserData.hasElements() ) + { + const NamedValue* pProp = std::find_if(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "node-type"; }); + if (pProp != aUserData.end()) + pProp->Value >>= nNodeType; + } + } + + return nNodeType; +} + +void CustomAnimationEffect::setPresetClassAndId( sal_Int16 nPresetClass, const OUString& rPresetId ) +{ + if( mnPresetClass == nPresetClass && maPresetId == rPresetId ) + return; + + mnPresetClass = nPresetClass; + maPresetId = rPresetId; + if( !mxNode.is() ) + return; + + // first try to find a "preset-class" entry in the user data + // and change it + Sequence< NamedValue > aUserData( mxNode->getUserData() ); + sal_Int32 nLength = aUserData.getLength(); + bool bFoundPresetClass = false; + bool bFoundPresetId = false; + if( nLength ) + { + auto [begin, end] = asNonConstRange(aUserData); + NamedValue* pProp = std::find_if(begin, end, + [](const NamedValue& rProp) { return rProp.Name == "preset-class"; }); + if (pProp != end) + { + pProp->Value <<= mnPresetClass; + bFoundPresetClass = true; + } + + pProp = std::find_if(begin, end, + [](const NamedValue& rProp) { return rProp.Name == "preset-id"; }); + if (pProp != end) + { + pProp->Value <<= mnPresetClass; + bFoundPresetId = true; + } + } + + // no "preset-class" entry inside user data, so add it + if( !bFoundPresetClass ) + { + aUserData.realloc( nLength + 1); + auto& el = aUserData.getArray()[nLength]; + el.Name = "preset-class"; + el.Value <<= mnPresetClass; + ++nLength; + } + + if( !bFoundPresetId && maPresetId.getLength() > 0 ) + { + aUserData.realloc( nLength + 1); + auto& el = aUserData.getArray()[nLength]; + el.Name = "preset-id"; + el.Value <<= maPresetId; + } + + mxNode->setUserData( aUserData ); +} + +void CustomAnimationEffect::setNodeType( sal_Int16 nNodeType ) +{ + if( mnNodeType == nNodeType ) + return; + + mnNodeType = nNodeType; + if( !mxNode.is() ) + return; + + // first try to find a "node-type" entry in the user data + // and change it + Sequence< NamedValue > aUserData( mxNode->getUserData() ); + sal_Int32 nLength = aUserData.getLength(); + bool bFound = false; + if( nLength ) + { + auto [begin, end] = asNonConstRange(aUserData); + NamedValue* pProp = std::find_if(begin, end, + [](const NamedValue& rProp) { return rProp.Name == "node-type"; }); + if (pProp != end) + { + pProp->Value <<= mnNodeType; + bFound = true; + } + } + + // no "node-type" entry inside user data, so add it + if( !bFound ) + { + aUserData.realloc( nLength + 1); + auto& el = aUserData.getArray()[nLength]; + el.Name = "node-type"; + el.Value <<= mnNodeType; + } + + mxNode->setUserData( aUserData ); +} + +void CustomAnimationEffect::setGroupId( sal_Int32 nGroupId ) +{ + mnGroupId = nGroupId; + if( !mxNode.is() ) + return; + + // first try to find a "group-id" entry in the user data + // and change it + Sequence< NamedValue > aUserData( mxNode->getUserData() ); + sal_Int32 nLength = aUserData.getLength(); + bool bFound = false; + if( nLength ) + { + auto [begin, end] = asNonConstRange(aUserData); + NamedValue* pProp = std::find_if(begin, end, + [](const NamedValue& rProp) { return rProp.Name == "group-id"; }); + if (pProp != end) + { + pProp->Value <<= mnGroupId; + bFound = true; + } + } + + // no "group-id" entry inside user data, so add it + if( !bFound ) + { + aUserData.realloc( nLength + 1); + auto& el = aUserData.getArray()[nLength]; + el.Name = "group-id"; + el.Value <<= mnGroupId; + } + + mxNode->setUserData( aUserData ); +} + +/** checks if the text for this effect has changed and updates internal flags. + returns true if something changed. +*/ +bool CustomAnimationEffect::checkForText( const std::vector* paragraphNumberingLevel ) +{ + bool bChange = false; + + Reference< XText > xText; + + if( maTarget.getValueType() == ::cppu::UnoType::get() ) + { + // calc para depth + ParagraphTarget aParaTarget; + maTarget >>= aParaTarget; + + xText.set( aParaTarget.Shape, UNO_QUERY ); + + // get paragraph + if( xText.is() ) + { + sal_Int32 nPara = aParaTarget.Paragraph; + + bool bHasText = false; + sal_Int32 nParaDepth = 0; + + if ( paragraphNumberingLevel ) + { + bHasText = !paragraphNumberingLevel->empty(); + if (nPara >= 0 && o3tl::make_unsigned(nPara) < paragraphNumberingLevel->size()) + nParaDepth = paragraphNumberingLevel->at(nPara); + } + else + { + Reference< XEnumerationAccess > xEA( xText, UNO_QUERY ); + if( xEA.is() ) + { + Reference< XEnumeration > xEnumeration = xEA->createEnumeration(); + if( xEnumeration.is() ) + { + bHasText = xEnumeration->hasMoreElements(); + + while( xEnumeration->hasMoreElements() && nPara-- ) + xEnumeration->nextElement(); + + if( xEnumeration->hasMoreElements() ) + { + Reference< XPropertySet > xParaSet; + xEnumeration->nextElement() >>= xParaSet; + if( xParaSet.is() ) + { + xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth; + } + } + } + } + } + + if( bHasText ) + { + bChange |= bHasText != mbHasText; + mbHasText = bHasText; + + bChange |= nParaDepth != mnParaDepth; + mnParaDepth = nParaDepth; + } + } + } + else + { + maTarget >>= xText; + bool bHasText = xText.is() && !xText->getString().isEmpty(); + bChange |= bHasText != mbHasText; + mbHasText = bHasText; + } + + bChange |= calculateIterateDuration(); + return bChange; +} + +bool CustomAnimationEffect::calculateIterateDuration() +{ + bool bChange = false; + + // if we have an iteration, we must also calculate the + // 'true' container duration, that is + // ( ( is form animated ) ? [contained effects duration] : 0 ) + + // ( [number of animated children] - 1 ) * [interval-delay] + [contained effects duration] + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + if( xIter.is() ) + { + double fDuration = mfDuration; + const double fSubEffectDuration = mfDuration; + + if( mnTargetSubItem != ShapeAnimationSubType::ONLY_BACKGROUND ) // does not make sense for iterate container but better check + { + const sal_Int32 nSubItems = getNumberOfSubitems( maTarget, mnIterateType ); + if( nSubItems ) + { + const double f = (nSubItems-1) * mfIterateInterval; + fDuration += f; + } + } + + // if we also animate the form first, we have to add the + // sub effect duration to the whole effect duration + if( mnTargetSubItem == ShapeAnimationSubType::AS_WHOLE ) + fDuration += fSubEffectDuration; + + bChange |= fDuration != mfAbsoluteDuration; + mfAbsoluteDuration = fDuration; + } + + return bChange; +} + +void CustomAnimationEffect::setTarget( const css::uno::Any& rTarget ) +{ + try + { + maTarget = rTarget; + + // first, check special case for random node + Reference< XInitialization > xInit( mxNode, UNO_QUERY ); + if( xInit.is() ) + { + const Sequence< Any > aArgs( &maTarget, 1 ); + xInit->initialize( aArgs ); + } + else + { + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + if( xIter.is() ) + { + xIter->setTarget(maTarget); + } + else + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + const Any aElem( xEnumeration->nextElement() ); + Reference< XAnimate > xAnimate( aElem, UNO_QUERY ); + if( xAnimate.is() ) + xAnimate->setTarget( rTarget ); + else + { + Reference< XCommand > xCommand( aElem, UNO_QUERY ); + if( xCommand.is() ) + xCommand->setTarget( rTarget ); + } + } + } + } + } + } + checkForText(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTarget()" ); + } +} + +void CustomAnimationEffect::setTargetSubItem( sal_Int16 nSubItem ) +{ + try + { + mnTargetSubItem = nSubItem; + + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + if( xIter.is() ) + { + xIter->setSubItem(mnTargetSubItem); + } + else + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( xAnimate.is() ) + xAnimate->setSubItem( mnTargetSubItem ); + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTargetSubItem()" ); + } +} + +void CustomAnimationEffect::setDuration( double fDuration ) +{ + if( (mfDuration == -1.0) || (mfDuration == fDuration) ) + return; + + try + { + double fScale = fDuration / mfDuration; + mfDuration = fDuration; + double fRepeatCount = 1.0; + getRepeatCount() >>= fRepeatCount; + mfAbsoluteDuration = mfDuration * fRepeatCount; + + // calculate effect duration and get target shape + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( !xChildNode.is() ) + continue; + + double fChildBegin = 0.0; + xChildNode->getBegin() >>= fChildBegin; + if( fChildBegin != 0.0 ) + { + fChildBegin *= fScale; + xChildNode->setBegin( Any( fChildBegin ) ); + } + + double fChildDuration = 0.0; + xChildNode->getDuration() >>= fChildDuration; + if( fChildDuration != 0.0 ) + { + fChildDuration *= fScale; + xChildNode->setDuration( Any( fChildDuration ) ); + } + } + } + } + calculateIterateDuration(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setDuration()" ); + } +} + +void CustomAnimationEffect::setBegin( double fBegin ) +{ + if( mxNode.is() ) try + { + mfBegin = fBegin; + mxNode->setBegin( Any( fBegin ) ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setBegin()" ); + } +} + +void CustomAnimationEffect::setAcceleration( double fAcceleration ) +{ + if( mxNode.is() ) try + { + mfAcceleration = fAcceleration; + mxNode->setAcceleration( fAcceleration ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAcceleration()" ); + } +} + +void CustomAnimationEffect::setDecelerate( double fDecelerate ) +{ + if( mxNode.is() ) try + { + mfDecelerate = fDecelerate; + mxNode->setDecelerate( fDecelerate ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setDecelerate()" ); + } +} + +void CustomAnimationEffect::setAutoReverse( bool bAutoReverse ) +{ + if( mxNode.is() ) try + { + mbAutoReverse = bAutoReverse; + mxNode->setAutoReverse( bAutoReverse ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAutoReverse()" ); + } +} + +void CustomAnimationEffect::replaceNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +{ + sal_Int16 nNodeType = mnNodeType; + Any aTarget = maTarget; + + sal_Int16 nFill = mnFill; + double fBegin = mfBegin; + double fDuration = mfDuration; + double fAcceleration = mfAcceleration; + double fDecelerate = mfDecelerate ; + bool bAutoReverse = mbAutoReverse; + Reference< XAudio > xAudio( mxAudio ); + sal_Int16 nIterateType = mnIterateType; + double fIterateInterval = mfIterateInterval; + sal_Int16 nSubItem = mnTargetSubItem; + + setNode( xNode ); + + setAudio( xAudio ); + setNodeType( nNodeType ); + setTarget( aTarget ); + setTargetSubItem( nSubItem ); + setDuration( fDuration ); + setBegin( fBegin ); + setFill( nFill ); + + setAcceleration( fAcceleration ); + setDecelerate( fDecelerate ); + setAutoReverse( bAutoReverse ); + + if( nIterateType != mnIterateType ) + setIterateType( nIterateType ); + + if( mnIterateType && ( fIterateInterval != mfIterateInterval ) ) + setIterateInterval( fIterateInterval ); +} + +Reference< XShape > CustomAnimationEffect::getTargetShape() const +{ + Reference< XShape > xShape; + maTarget >>= xShape; + if( !xShape.is() ) + { + ParagraphTarget aParaTarget; + if( maTarget >>= aParaTarget ) + xShape = aParaTarget.Shape; + } + + return xShape; +} + +Any CustomAnimationEffect::getRepeatCount() const +{ + if( mxNode.is() ) + { + return mxNode->getRepeatCount(); + } + else + { + Any aAny; + return aAny; + } +} + +Any CustomAnimationEffect::getEnd() const +{ + if( mxNode.is() ) + { + return mxNode->getEnd(); + } + else + { + Any aAny; + return aAny; + } +} + +void CustomAnimationEffect::setRepeatCount( const Any& rRepeatCount ) +{ + if( mxNode.is() ) + { + mxNode->setRepeatCount( rRepeatCount ); + double fRepeatCount = 1.0; + rRepeatCount >>= fRepeatCount; + mfAbsoluteDuration = mfDuration * fRepeatCount; + } +} + +void CustomAnimationEffect::setEnd( const Any& rEnd ) +{ + if( mxNode.is() ) + mxNode->setEnd( rEnd ); +} + +void CustomAnimationEffect::setFill( sal_Int16 nFill ) +{ + if (mxNode.is()) + { + mnFill = nFill; + mxNode->setFill( nFill ); + } +} + +Reference< XAnimationNode > CustomAnimationEffect::createAfterEffectNode() const +{ + DBG_ASSERT( mbHasAfterEffect, "sd::CustomAnimationEffect::createAfterEffectNode(), this node has no after effect!" ); + + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + + Reference< XAnimate > xAnimate; + if( maDimColor.hasValue() ) + xAnimate = AnimateColor::create( xContext ); + else + xAnimate = AnimateSet::create( xContext ); + + Any aTo; + OUString aAttributeName; + + if( maDimColor.hasValue() ) + { + aTo = maDimColor; + aAttributeName = "DimColor"; + } + else + { + aTo <<= false; + aAttributeName = "Visibility"; + } + + Any aBegin; + if( !mbAfterEffectOnNextEffect ) // sameClick + { + Event aEvent; + + aEvent.Source <<= getNode(); + aEvent.Trigger = EventTrigger::END_EVENT; + aEvent.Repeat = 0; + + aBegin <<= aEvent; + } + else + { + aBegin <<= 0.0; + } + + xAnimate->setBegin( aBegin ); + xAnimate->setTo( aTo ); + xAnimate->setAttributeName( aAttributeName ); + + xAnimate->setDuration( Any( 0.001 ) ); + xAnimate->setFill( AnimationFill::HOLD ); + xAnimate->setTarget( maTarget ); + + return xAnimate; +} + +void CustomAnimationEffect::setIterateType( sal_Int16 nIterateType ) +{ + if( mnIterateType == nIterateType ) + return; + + try + { + // do we need to exchange the container node? + if( (mnIterateType == 0) || (nIterateType == 0) ) + { + sal_Int16 nTargetSubItem = mnTargetSubItem; + + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XTimeContainer > xNewContainer; + if(nIterateType) + { + xNewContainer.set( IterateContainer::create( xContext ) ); + } + else + xNewContainer.set( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW ); + + Reference< XTimeContainer > xOldContainer( mxNode, UNO_QUERY_THROW ); + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + xOldContainer->removeChild( xChildNode ); + xNewContainer->appendChild( xChildNode ); + } + + xNewContainer->setBegin( mxNode->getBegin() ); + xNewContainer->setDuration( mxNode->getDuration() ); + xNewContainer->setEnd( mxNode->getEnd() ); + xNewContainer->setEndSync( mxNode->getEndSync() ); + xNewContainer->setRepeatCount( mxNode->getRepeatCount() ); + xNewContainer->setFill( mxNode->getFill() ); + xNewContainer->setFillDefault( mxNode->getFillDefault() ); + xNewContainer->setRestart( mxNode->getRestart() ); + xNewContainer->setRestartDefault( mxNode->getRestartDefault() ); + xNewContainer->setAcceleration( mxNode->getAcceleration() ); + xNewContainer->setDecelerate( mxNode->getDecelerate() ); + xNewContainer->setAutoReverse( mxNode->getAutoReverse() ); + xNewContainer->setRepeatDuration( mxNode->getRepeatDuration() ); + xNewContainer->setEndSync( mxNode->getEndSync() ); + xNewContainer->setRepeatCount( mxNode->getRepeatCount() ); + xNewContainer->setUserData( mxNode->getUserData() ); + + mxNode = xNewContainer; + + Any aTarget; + if( nIterateType ) + { + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW ); + xIter->setTarget(maTarget); + xIter->setSubItem( nTargetSubItem ); + } + else + { + aTarget = maTarget; + } + + Reference< XEnumerationAccess > xEA( mxNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xE( xEA->createEnumeration(), UNO_SET_THROW ); + while( xE->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xE->nextElement(), UNO_QUERY ); + if( xAnimate.is() ) + { + xAnimate->setTarget( aTarget ); + xAnimate->setSubItem( nTargetSubItem ); + } + } + } + + mnIterateType = nIterateType; + + // if we have an iteration container, we must set its type + if( mnIterateType ) + { + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY_THROW ); + xIter->setIterateType( nIterateType ); + } + + checkForText(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setIterateType()" ); + } +} + +void CustomAnimationEffect::setIterateInterval( double fIterateInterval ) +{ + if( mfIterateInterval == fIterateInterval ) + return; + + Reference< XIterateContainer > xIter( mxNode, UNO_QUERY ); + + DBG_ASSERT( xIter.is(), "sd::CustomAnimationEffect::setIterateInterval(), not an iteration node" ); + if( xIter.is() ) + { + mfIterateInterval = fIterateInterval; + xIter->setIterateInterval( fIterateInterval ); + } + + calculateIterateDuration(); +} + +OUString CustomAnimationEffect::getPath() const +{ + OUString aPath; + + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY ); + if( xMotion.is() ) + { + xMotion->getPath() >>= aPath; + break; + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getPath()" ); + } + + return aPath; +} + +void CustomAnimationEffect::setPath( const OUString& rPath ) +{ + if( !mxNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimateMotion > xMotion( xEnumeration->nextElement(), UNO_QUERY ); + if( xMotion.is() ) + { + + MainSequenceChangeGuard aGuard( mpEffectSequence ); + xMotion->setPath( Any( rPath ) ); + break; + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setPath()" ); + } +} + +Any CustomAnimationEffect::getProperty( sal_Int32 nNodeType, std::u16string_view rAttributeName, EValue eValue ) +{ + Any aProperty; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() && !aProperty.hasValue() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( !xAnimate.is() ) + continue; + + if( xAnimate->getType() == nNodeType ) + { + if( xAnimate->getAttributeName() == rAttributeName ) + { + switch( eValue ) + { + case EValue::To: aProperty = xAnimate->getTo(); break; + case EValue::By: aProperty = xAnimate->getBy(); break; + } + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getProperty()" ); + } + + return aProperty; +} + +bool CustomAnimationEffect::setProperty( sal_Int32 nNodeType, std::u16string_view rAttributeName, EValue eValue, const Any& rValue ) +{ + bool bChanged = false; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( !xAnimate.is() ) + continue; + + if( xAnimate->getType() == nNodeType ) + { + if( xAnimate->getAttributeName() == rAttributeName ) + { + switch( eValue ) + { + case EValue::To: + if( xAnimate->getTo() != rValue ) + { + xAnimate->setTo( rValue ); + bChanged = true; + } + break; + case EValue::By: + if( xAnimate->getTo() != rValue ) + { + xAnimate->setBy( rValue ); + bChanged = true; + } + break; + } + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setProperty()" ); + } + + return bChanged; +} + +static bool implIsColorAttribute( std::u16string_view rAttributeName ) +{ + return rAttributeName == u"FillColor" || rAttributeName == u"LineColor" || rAttributeName == u"CharColor"; +} + +Any CustomAnimationEffect::getColor( sal_Int32 nIndex ) +{ + Any aColor; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() && !aColor.hasValue() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( !xAnimate.is() ) + continue; + + switch( xAnimate->getType() ) + { + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATE: + if( !implIsColorAttribute( xAnimate->getAttributeName() ) ) + break; + [[fallthrough]]; + case AnimationNodeType::ANIMATECOLOR: + Sequence aValues( xAnimate->getValues() ); + if( aValues.hasElements() ) + { + if( aValues.getLength() > nIndex ) + aColor = aValues[nIndex]; + } + else if( nIndex == 0 ) + aColor = xAnimate->getFrom(); + else + aColor = xAnimate->getTo(); + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getColor()" ); + } + + return aColor; +} + +void CustomAnimationEffect::setColor( sal_Int32 nIndex, const Any& rColor ) +{ + if( !mxNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( !xAnimate.is() ) + continue; + + switch( xAnimate->getType() ) + { + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATE: + if( !implIsColorAttribute( xAnimate->getAttributeName() ) ) + break; + [[fallthrough]]; + case AnimationNodeType::ANIMATECOLOR: + { + Sequence aValues( xAnimate->getValues() ); + if( aValues.hasElements() ) + { + if( aValues.getLength() > nIndex ) + { + aValues.getArray()[nIndex] = rColor; + xAnimate->setValues( aValues ); + } + } + else if( (nIndex == 0) && xAnimate->getFrom().hasValue() ) + xAnimate->setFrom(rColor); + else if( (nIndex == 1) && xAnimate->getTo().hasValue() ) + xAnimate->setTo(rColor); + } + break; + + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setColor()" ); + } +} + +Any CustomAnimationEffect::getTransformationProperty( sal_Int32 nTransformType, EValue eValue ) +{ + Any aProperty; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() && !aProperty.hasValue() ) + { + Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY ); + if( !xTransform.is() ) + continue; + + if( xTransform->getTransformType() == nTransformType ) + { + switch( eValue ) + { + case EValue::To: aProperty = xTransform->getTo(); break; + case EValue::By: aProperty = xTransform->getBy(); break; + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::getTransformationProperty()" ); + } + + return aProperty; +} + +bool CustomAnimationEffect::setTransformationProperty( sal_Int32 nTransformType, EValue eValue, const Any& rValue ) +{ + bool bChanged = false; + if( mxNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimateTransform > xTransform( xEnumeration->nextElement(), UNO_QUERY ); + if( !xTransform.is() ) + continue; + + if( xTransform->getTransformType() == nTransformType ) + { + switch( eValue ) + { + case EValue::To: + if( xTransform->getTo() != rValue ) + { + xTransform->setTo( rValue ); + bChanged = true; + } + break; + case EValue::By: + if( xTransform->getBy() != rValue ) + { + xTransform->setBy( rValue ); + bChanged = true; + } + break; + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setTransformationProperty()" ); + } + + return bChanged; +} + +void CustomAnimationEffect::createAudio( const css::uno::Any& rSource ) +{ + DBG_ASSERT( !mxAudio.is(), "sd::CustomAnimationEffect::createAudio(), node already has an audio!" ); + + if( mxAudio.is() ) + return; + + try + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XAudio > xAudio( Audio::create( xContext ) ); + xAudio->setSource( rSource ); + xAudio->setVolume( 1.0 ); + setAudio( xAudio ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::createAudio()" ); + } +} + +static Reference< XCommand > findCommandNode( const Reference< XAnimationNode >& xRootNode ) +{ + Reference< XCommand > xCommand; + + if( xRootNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( !xCommand.is() && xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xNode.is() && (xNode->getType() == AnimationNodeType::COMMAND) ) + xCommand.set( xNode, UNO_QUERY_THROW ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::findCommandNode()" ); + } + + return xCommand; +} + +void CustomAnimationEffect::removeAudio() +{ + try + { + Reference< XAnimationNode > xChild; + + if( mxAudio.is() ) + { + xChild = mxAudio; + mxAudio.clear(); + } + else if( mnCommand == EffectCommands::STOPAUDIO ) + { + xChild = findCommandNode( mxNode ); + mnCommand = 0; + } + + if( xChild.is() ) + { + Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY ); + if( xContainer.is() ) + xContainer->removeChild( xChild ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::removeAudio()" ); + } + +} + +void CustomAnimationEffect::setAudio( const Reference< css::animations::XAudio >& xAudio ) +{ + if( mxAudio == xAudio ) + return; + + try + { + removeAudio(); + mxAudio = xAudio; + Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY ); + if( xContainer.is() && mxAudio.is() ) + xContainer->appendChild( mxAudio ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setAudio()" ); + } +} + +void CustomAnimationEffect::setStopAudio() +{ + if( mnCommand == EffectCommands::STOPAUDIO ) + return; + + try + { + if( mxAudio.is() ) + removeAudio(); + + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + Reference< XCommand > xCommand( Command::create( xContext ) ); + + xCommand->setCommand( EffectCommands::STOPAUDIO ); + + Reference< XTimeContainer > xContainer( mxNode, UNO_QUERY_THROW ); + xContainer->appendChild( xCommand ); + + mnCommand = EffectCommands::STOPAUDIO; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationEffect::setStopAudio()" ); + } +} + +bool CustomAnimationEffect::getStopAudio() const +{ + return mnCommand == EffectCommands::STOPAUDIO; +} + +SdrPathObj* CustomAnimationEffect::createSdrPathObjFromPath(SdrModel& rTargetModel) +{ + SdrPathObj * pPathObj = new SdrPathObj(rTargetModel, SdrObjKind::PathLine); + updateSdrPathObjFromPath( *pPathObj ); + return pPathObj; +} + +void CustomAnimationEffect::updateSdrPathObjFromPath( SdrPathObj& rPathObj ) +{ + ::basegfx::B2DPolyPolygon aPolyPoly; + if( ::basegfx::utils::importFromSvgD( aPolyPoly, getPath(), true, nullptr ) ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(getTargetShape()); + if( pObj ) + { + SdrPage* pPage = pObj->getSdrPageFromSdrObject(); + if( pPage ) + { + const Size aPageSize( pPage->GetSize() ); + aPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix(static_cast(aPageSize.Width()), static_cast(aPageSize.Height()))); + } + + const ::tools::Rectangle aBoundRect( pObj->GetCurrentBoundRect() ); + const Point aCenter( aBoundRect.Center() ); + aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(aCenter.X(), aCenter.Y())); + } + } + + rPathObj.SetPathPoly( aPolyPoly ); +} + +void CustomAnimationEffect::updatePathFromSdrPathObj( const SdrPathObj& rPathObj ) +{ + ::basegfx::B2DPolyPolygon aPolyPoly( rPathObj.GetPathPoly() ); + + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(getTargetShape()); + if( pObj ) + { + ::tools::Rectangle aBoundRect(0,0,0,0); + + drawinglayer::primitive2d::Primitive2DContainer xPrimitives; + pObj->GetViewContact().getViewIndependentPrimitive2DContainer(xPrimitives); + const drawinglayer::geometry::ViewInformation2D aViewInformation2D; + const basegfx::B2DRange aRange(xPrimitives.getB2DRange(aViewInformation2D)); + + if(!aRange.isEmpty()) + { + aBoundRect = ::tools::Rectangle( + static_cast(floor(aRange.getMinX())), static_cast(floor(aRange.getMinY())), + static_cast(ceil(aRange.getMaxX())), static_cast(ceil(aRange.getMaxY()))); + } + + const Point aCenter( aBoundRect.Center() ); + + aPolyPoly.transform(basegfx::utils::createTranslateB2DHomMatrix(-aCenter.X(), -aCenter.Y())); + + SdrPage* pPage = pObj->getSdrPageFromSdrObject(); + if( pPage ) + { + const Size aPageSize( pPage->GetSize() ); + aPolyPoly.transform(basegfx::utils::createScaleB2DHomMatrix( + 1.0 / static_cast(aPageSize.Width()), 1.0 / static_cast(aPageSize.Height()))); + } + } + + setPath( ::basegfx::utils::exportToSvgD( aPolyPoly, true, true, true) ); +} + +EffectSequenceHelper::EffectSequenceHelper() +: mnSequenceType( EffectNodeType::DEFAULT ) +{ +} + +EffectSequenceHelper::EffectSequenceHelper( const css::uno::Reference< css::animations::XTimeContainer >& xSequenceRoot ) +: mxSequenceRoot( xSequenceRoot ), mnSequenceType( EffectNodeType::DEFAULT ) +{ + Reference< XAnimationNode > xNode( mxSequenceRoot, UNO_QUERY_THROW ); + create( xNode ); +} + +EffectSequenceHelper::~EffectSequenceHelper() +{ + reset(); +} + +void EffectSequenceHelper::reset() +{ + for( CustomAnimationEffectPtr& pEffect : maEffects ) + { + pEffect->setEffectSequence(nullptr); + } + maEffects.clear(); +} + +Reference< XAnimationNode > EffectSequenceHelper::getRootNode() +{ + return mxSequenceRoot; +} + +void EffectSequenceHelper::append( const CustomAnimationEffectPtr& pEffect ) +{ + pEffect->setEffectSequence( this ); + maEffects.push_back(pEffect); + rebuild(); +} + +CustomAnimationEffectPtr EffectSequenceHelper::append( const CustomAnimationPresetPtr& pPreset, const Any& rTarget, double fDuration /* = -1.0 */ ) +{ + CustomAnimationEffectPtr pEffect; + + if( pPreset ) + { + Reference< XAnimationNode > xNode( pPreset->create( "" ) ); + if( xNode.is() ) + { + // first, filter all only ui relevant user data + std::vector< NamedValue > aNewUserData; + Sequence< NamedValue > aUserData( xNode->getUserData() ); + + std::copy_if(std::cbegin(aUserData), std::cend(aUserData), std::back_inserter(aNewUserData), + [](const NamedValue& rProp) { return rProp.Name != "text-only" && rProp.Name != "preset-property"; }); + + if( !aNewUserData.empty() ) + { + aUserData = ::comphelper::containerToSequence( aNewUserData ); + xNode->setUserData( aUserData ); + } + + // check target, maybe we need to force it to text + sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE; + + if( rTarget.getValueType() == ::cppu::UnoType::get() ) + { + nSubItem = ShapeAnimationSubType::ONLY_TEXT; + } + else if( pPreset->isTextOnly() ) + { + Reference< XShape > xShape; + rTarget >>= xShape; + if( xShape.is() ) + { + // that's bad, we target a shape here but the effect is only for text + // so change subitem + nSubItem = ShapeAnimationSubType::ONLY_TEXT; + } + } + + // now create effect from preset + pEffect = std::make_shared( xNode ); + pEffect->setEffectSequence( this ); + pEffect->setTarget( rTarget ); + pEffect->setTargetSubItem( nSubItem ); + if( fDuration != -1.0 ) + pEffect->setDuration( fDuration ); + + maEffects.push_back(pEffect); + + rebuild(); + } + } + + DBG_ASSERT( pEffect, "sd::EffectSequenceHelper::append(), failed!" ); + return pEffect; +} + +CustomAnimationEffectPtr EffectSequenceHelper::append( const SdrPathObj& rPathObj, const Any& rTarget, double fDuration /* = -1.0 */, const OUString& rPresetId ) +{ + CustomAnimationEffectPtr pEffect; + + if( fDuration <= 0.0 ) + fDuration = 2.0; + + try + { + Reference< XTimeContainer > xEffectContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW ); + Reference< XAnimationNode > xAnimateMotion( AnimateMotion::create( ::comphelper::getProcessComponentContext() ) ); + + xAnimateMotion->setDuration( Any( fDuration ) ); + xAnimateMotion->setFill( AnimationFill::HOLD ); + xEffectContainer->appendChild( xAnimateMotion ); + + sal_Int16 nSubItem = ShapeAnimationSubType::AS_WHOLE; + + if( rTarget.getValueType() == ::cppu::UnoType::get() ) + nSubItem = ShapeAnimationSubType::ONLY_TEXT; + + pEffect = std::make_shared( xEffectContainer ); + pEffect->setEffectSequence( this ); + pEffect->setTarget( rTarget ); + pEffect->setTargetSubItem( nSubItem ); + pEffect->setNodeType( css::presentation::EffectNodeType::ON_CLICK ); + pEffect->setPresetClassAndId( css::presentation::EffectPresetClass::MOTIONPATH, rPresetId ); + pEffect->setAcceleration( 0.5 ); + pEffect->setDecelerate( 0.5 ); + pEffect->setFill( AnimationFill::HOLD ); + pEffect->setBegin( 0.0 ); + pEffect->updatePathFromSdrPathObj( rPathObj ); + if( fDuration != -1.0 ) + pEffect->setDuration( fDuration ); + + maEffects.push_back(pEffect); + + rebuild(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::append()" ); + } + + return pEffect; +} + +void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, const OUString& rPresetSubType, double fDuration /* = -1.0 */ ) +{ + if( !(pEffect && pPreset) ) + return; + + try + { + Reference< XAnimationNode > xNewNode( pPreset->create( rPresetSubType ) ); + if( xNewNode.is() ) + { + pEffect->replaceNode( xNewNode ); + if( fDuration != -1.0 ) + pEffect->setDuration( fDuration ); + } + + rebuild(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::replace()" ); + } +} + +void EffectSequenceHelper::replace( const CustomAnimationEffectPtr& pEffect, const CustomAnimationPresetPtr& pPreset, double fDuration /* = -1.0 */ ) +{ + replace( pEffect, pPreset, "", fDuration ); +} + +void EffectSequenceHelper::remove( const CustomAnimationEffectPtr& pEffect ) +{ + if( pEffect ) + { + pEffect->setEffectSequence( nullptr ); + maEffects.remove( pEffect ); + } + + rebuild(); +} + +void EffectSequenceHelper::moveToBeforeEffect( const CustomAnimationEffectPtr& pEffect, const CustomAnimationEffectPtr& pInsertBefore) +{ + if ( pEffect ) + { + maEffects.remove( pEffect ); + EffectSequence::iterator aInsertIter( find( pInsertBefore ) ); + + // aInsertIter being end() is OK: pInsertBefore could be null, so put at end. + maEffects.insert( aInsertIter, pEffect ); + + rebuild(); + } +} + +void EffectSequenceHelper::rebuild() +{ + implRebuild(); +} + +void EffectSequenceHelper::implRebuild() +{ + try + { + // first we delete all time containers on the first two levels + Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + Reference< XTimeContainer > xChildContainer( xChildNode, UNO_QUERY_THROW ); + + Reference< XEnumerationAccess > xChildEnumerationAccess( xChildNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xChildEnumeration( xChildEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xChildEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xNode( xChildEnumeration->nextElement(), UNO_QUERY_THROW ); + xChildContainer->removeChild( xNode ); + } + + mxSequenceRoot->removeChild( xChildNode ); + } + + // second, rebuild main sequence + EffectSequence::iterator aIter( maEffects.begin() ); + EffectSequence::iterator aEnd( maEffects.end() ); + if( aIter != aEnd ) + { + std::vector< sd::AfterEffectNode > aAfterEffects; + + CustomAnimationEffectPtr pEffect = *aIter++; + + bool bFirst = true; + do + { + // create a par container for the next click node and all following with and after effects + Reference< XTimeContainer > xOnClickContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW ); + + Event aEvent; + if( mxEventSource.is() ) + { + aEvent.Source <<= mxEventSource; + aEvent.Trigger = EventTrigger::ON_CLICK; + } + else + { + aEvent.Trigger = EventTrigger::ON_NEXT; + } + aEvent.Repeat = 0; + + Any aBegin( aEvent ); + if( bFirst ) + { + // if the first node is not a click action, this click container + // must not have INDEFINITE begin but start at 0s + bFirst = false; + if( pEffect->getNodeType() != EffectNodeType::ON_CLICK ) + aBegin <<= 0.0; + } + + xOnClickContainer->setBegin( aBegin ); + + mxSequenceRoot->appendChild( xOnClickContainer ); + + double fBegin = 0.0; + + do + { + // create a par container for the current click or after effect node and all following with effects + Reference< XTimeContainer > xWithContainer( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW ); + xWithContainer->setBegin( Any( fBegin ) ); + xOnClickContainer->appendChild( xWithContainer ); + + double fDuration = 0.0; + do + { + Reference< XAnimationNode > xEffectNode( pEffect->getNode() ); + xWithContainer->appendChild( xEffectNode ); + + if( pEffect->hasAfterEffect() ) + { + Reference< XAnimationNode > xAfterEffect( pEffect->createAfterEffectNode() ); + AfterEffectNode a( xAfterEffect, xEffectNode, pEffect->IsAfterEffectOnNext() ); + aAfterEffects.push_back( a ); + } + + double fTemp = pEffect->getBegin() + pEffect->getAbsoluteDuration(); + if( fTemp > fDuration ) + fDuration = fTemp; + + if( aIter != aEnd ) + pEffect = *aIter++; + else + pEffect.reset(); + } + while( pEffect && (pEffect->getNodeType() == EffectNodeType::WITH_PREVIOUS) ); + + fBegin += fDuration; + } + while( pEffect && (pEffect->getNodeType() != EffectNodeType::ON_CLICK) ); + } + while( pEffect ); + + // process after effect nodes + std::for_each( aAfterEffects.begin(), aAfterEffects.end(), stl_process_after_effect_node_func ); + + updateTextGroups(); + + // reset duration, might have been altered (see below) + mxSequenceRoot->setDuration( Any() ); + } + else + { + // empty sequence, set duration to 0.0 explicitly + // (otherwise, this sequence will never end) + mxSequenceRoot->setDuration( Any(0.0) ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::rebuild()" ); + } +} + +stl_CustomAnimationEffect_search_node_predict::stl_CustomAnimationEffect_search_node_predict( const css::uno::Reference< css::animations::XAnimationNode >& xSearchNode ) +: mxSearchNode( xSearchNode ) +{ +} + +bool stl_CustomAnimationEffect_search_node_predict::operator()( const CustomAnimationEffectPtr& pEffect ) const +{ + return pEffect->getNode() == mxSearchNode; +} + +/// @throws Exception +static bool implFindNextContainer( Reference< XTimeContainer > const & xParent, Reference< XTimeContainer > const & xCurrent, Reference< XTimeContainer >& xNext ) +{ + Reference< XEnumerationAccess > xEnumerationAccess( xParent, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration() ); + if( xEnumeration.is() ) + { + Reference< XInterface > x; + while( xEnumeration->hasMoreElements() && !xNext.is() ) + { + if( (xEnumeration->nextElement() >>= x) && (x == xCurrent) ) + { + if( xEnumeration->hasMoreElements() ) + xEnumeration->nextElement() >>= xNext; + } + } + } + return xNext.is(); +} + +void stl_process_after_effect_node_func(AfterEffectNode const & rNode) +{ + try + { + if( rNode.mxNode.is() && rNode.mxMaster.is() ) + { + // set master node + Reference< XAnimationNode > xMasterNode( rNode.mxMaster, UNO_SET_THROW ); + Sequence< NamedValue > aUserData( rNode.mxNode->getUserData() ); + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "master-element"; + pUserData[nSize].Value <<= xMasterNode; + rNode.mxNode->setUserData( aUserData ); + + // insert after effect node into timeline + Reference< XTimeContainer > xContainer( rNode.mxMaster->getParent(), UNO_QUERY_THROW ); + + if( !rNode.mbOnNextEffect ) // sameClick + { + // insert the aftereffect after its effect is animated + xContainer->insertAfter( rNode.mxNode, rNode.mxMaster ); + } + else // nextClick + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + // insert the aftereffect in the next group + + Reference< XTimeContainer > xClickContainer( xContainer->getParent(), UNO_QUERY_THROW ); + Reference< XTimeContainer > xSequenceContainer( xClickContainer->getParent(), UNO_QUERY_THROW ); + + Reference< XTimeContainer > xNextContainer; + + // first try if we have an after effect container + if( !implFindNextContainer( xClickContainer, xContainer, xNextContainer ) ) + { + Reference< XTimeContainer > xNextClickContainer; + // if not, try to find the next click effect container + if( implFindNextContainer( xSequenceContainer, xClickContainer, xNextClickContainer ) ) + { + Reference< XEnumerationAccess > xEnumerationAccess( xNextClickContainer, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + if( xEnumeration->hasMoreElements() ) + { + // the next container is the first child container + xEnumeration->nextElement() >>= xNextContainer; + } + else + { + // this does not yet have a child container, create one + xNextContainer.set( ParallelTimeContainer::create(xContext), UNO_QUERY_THROW ); + + xNextContainer->setBegin( Any( 0.0 ) ); + xNextClickContainer->appendChild( xNextContainer ); + } + DBG_ASSERT( xNextContainer.is(), "ppt::stl_process_after_effect_node_func::operator(), could not find/create container!" ); + } + } + + // if we don't have a next container, we add one to the sequence container + if( !xNextContainer.is() ) + { + Reference< XTimeContainer > xNewClickContainer( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW ); + + Event aEvent; + aEvent.Trigger = EventTrigger::ON_NEXT; + aEvent.Repeat = 0; + xNewClickContainer->setBegin( Any( aEvent ) ); + + xSequenceContainer->insertAfter( xNewClickContainer, xClickContainer ); + + xNextContainer.set( ParallelTimeContainer::create( xContext ), UNO_QUERY_THROW ); + + xNextContainer->setBegin( Any( 0.0 ) ); + xNewClickContainer->appendChild( xNextContainer ); + } + + if( xNextContainer.is() ) + { + // find begin time of first element + Reference< XEnumerationAccess > xEnumerationAccess( xNextContainer, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + if( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChild; + // the next container is the first child container + xEnumeration->nextElement() >>= xChild; + if( xChild.is() ) + { + Any aBegin( xChild->getBegin() ); + double fBegin = 0.0; + if( (aBegin >>= fBegin) && (fBegin >= 0.0)) + rNode.mxNode->setBegin( aBegin ); + } + } + + xNextContainer->appendChild( rNode.mxNode ); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "ppt::stl_process_after_effect_node_func::operator()" ); + } +} + +EffectSequence::iterator EffectSequenceHelper::find( const CustomAnimationEffectPtr& pEffect ) +{ + return std::find( maEffects.begin(), maEffects.end(), pEffect ); +} + +CustomAnimationEffectPtr EffectSequenceHelper::findEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const +{ + CustomAnimationEffectPtr pEffect; + + EffectSequence::const_iterator aIter = std::find_if(maEffects.begin(), maEffects.end(), + [&xNode](const CustomAnimationEffectPtr& rxEffect) { return rxEffect->getNode() == xNode; }); + if (aIter != maEffects.end()) + pEffect = *aIter; + + return pEffect; +} + +sal_Int32 EffectSequenceHelper::getOffsetFromEffect( const CustomAnimationEffectPtr& xEffect ) const +{ + auto aIter = std::find(maEffects.begin(), maEffects.end(), xEffect); + if (aIter != maEffects.end()) + return static_cast(std::distance(maEffects.begin(), aIter)); + + return -1; +} + +CustomAnimationEffectPtr EffectSequenceHelper::getEffectFromOffset( sal_Int32 nOffset ) const +{ + EffectSequence::const_iterator aIter( maEffects.begin() ); + nOffset = std::min(nOffset, static_cast(maEffects.size())); + std::advance(aIter, nOffset); + + CustomAnimationEffectPtr pEffect; + if( aIter != maEffects.end() ) + pEffect = *aIter; + + return pEffect; +} + +bool EffectSequenceHelper::disposeShape( const Reference< XShape >& xShape ) +{ + bool bChanges = false; + + EffectSequence::iterator aIter( maEffects.begin() ); + while( aIter != maEffects.end() ) + { + if( (*aIter)->getTargetShape() == xShape ) + { + (*aIter)->setEffectSequence( nullptr ); + bChanges = true; + aIter = maEffects.erase( aIter ); + } + else + { + ++aIter; + } + } + + return bChanges; +} + +bool EffectSequenceHelper::hasEffect( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + return std::any_of(maEffects.begin(), maEffects.end(), + [&xShape](const CustomAnimationEffectPtr& rxEffect) { return rxEffect->getTargetShape() == xShape; }); +} + +bool EffectSequenceHelper::getParagraphNumberingLevels( const Reference< XShape >& xShape, std::vector< sal_Int32 >& rParagraphNumberingLevel ) +{ + rParagraphNumberingLevel.clear(); + + if( !hasEffect( xShape ) ) + return false; + + Reference< XText > xText( xShape, UNO_QUERY ); + if( xText.is() ) + { + Reference< XEnumerationAccess > xEA( xText, UNO_QUERY ); + if( xEA.is() ) + { + Reference< XEnumeration > xEnumeration = xEA->createEnumeration(); + + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XPropertySet > xParaSet; + xEnumeration->nextElement() >>= xParaSet; + + sal_Int32 nParaDepth = 0; + if( xParaSet.is() ) + { + xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth; + } + + rParagraphNumberingLevel.push_back( nParaDepth ); + } + } + } + } + + return true; +} + +void EffectSequenceHelper::insertTextRange( const css::uno::Any& aTarget ) +{ + ParagraphTarget aParaTarget; + if( !(aTarget >>= aParaTarget ) ) + return; + + // get map [paragraph index] -> [NumberingLevel] + // for following reusage inside all animation effects + std::vector< sal_Int32 > paragraphNumberingLevel; + std::vector< sal_Int32 >* paragraphNumberingLevelParam = nullptr; + if ( getParagraphNumberingLevels( aParaTarget.Shape, paragraphNumberingLevel ) ) + paragraphNumberingLevelParam = ¶graphNumberingLevel; + + // update internal flags for each animation effect + const bool bChanges = std::accumulate(maEffects.begin(), maEffects.end(), false, + [&aParaTarget, ¶graphNumberingLevelParam](const bool bCheck, const CustomAnimationEffectPtr& rxEffect) { + bool bRes = bCheck; + if (rxEffect->getTargetShape() == aParaTarget.Shape) + bRes |= rxEffect->checkForText( paragraphNumberingLevelParam ); + return bRes; + }); + + if( bChanges ) + rebuild(); +} + +static bool isParagraphTargetTextEmpty( ParagraphTarget aParaTarget ) +{ + // get paragraph + Reference< XText > xText ( aParaTarget.Shape, UNO_QUERY ); + if( xText.is() ) + { + Reference< XEnumerationAccess > xEA( xText, UNO_QUERY ); + if( xEA.is() ) + { + Reference< XEnumeration > xEnumeration = xEA->createEnumeration(); + if( xEnumeration.is() ) + { + // advance to the Nth paragraph + sal_Int32 nPara = aParaTarget.Paragraph; + while( xEnumeration->hasMoreElements() && nPara-- ) + xEnumeration->nextElement(); + + // get Nth paragraph's text and check if it's empty + if( xEnumeration->hasMoreElements() ) + { + Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY ); + if( xRange.is() ) + { + OUString text = xRange->getString(); + return text.isEmpty(); + } + } + } + } + } + return false; +} + +void EffectSequenceHelper::disposeTextRange( const css::uno::Any& aTarget ) +{ + ParagraphTarget aParaTarget; + if( !(aTarget >>= aParaTarget ) ) + return; + + bool bChanges = false; + + // building list of effects for target shape; process effects not on target shape + EffectSequence aTargetParagraphEffects; + for( const auto &pEffect : maEffects ) + { + Any aIterTarget( pEffect->getTarget() ); + if( aIterTarget.getValueType() == ::cppu::UnoType::get() ) + { + ParagraphTarget aIterParaTarget; + if( (aIterTarget >>= aIterParaTarget) && (aIterParaTarget.Shape == aParaTarget.Shape) ) + { + aTargetParagraphEffects.push_back(pEffect); + } + } + else if( pEffect->getTargetShape() == aParaTarget.Shape ) + { + bChanges |= pEffect->checkForText(); + } + } + + // select effect to delete: + // if paragraph before target is blank, then delete its animation effect (if any) instead + ParagraphTarget aPreviousParagraph = aParaTarget; + --aPreviousParagraph.Paragraph; + bool bIsPreviousParagraphEmpty = isParagraphTargetTextEmpty( aPreviousParagraph ); + sal_Int16 anParaNumToDelete = bIsPreviousParagraphEmpty ? aPreviousParagraph.Paragraph : aParaTarget.Paragraph; + + // update effects + for( const auto &pEffect : aTargetParagraphEffects ) + { + Any aIterTarget( pEffect->getTarget() ); + + ParagraphTarget aIterParaTarget; + aIterTarget >>= aIterParaTarget; + + // delete effect for target paragraph (may have effects in more than one text group) + if( aIterParaTarget.Paragraph == anParaNumToDelete ) + { + auto aItr = find( pEffect ); + DBG_ASSERT( aItr != maEffects.end(), "sd::EffectSequenceHelper::disposeTextRange(), Expected effect missing."); + if( aItr != maEffects.end() ) + { + (*aItr)->setEffectSequence( nullptr ); + maEffects.erase(aItr); + bChanges = true; + } + } + + // shift all paragraphs after disposed paragraph + if( aIterParaTarget.Paragraph > anParaNumToDelete ) + { + --aIterParaTarget.Paragraph; + pEffect->setTarget( Any( aIterParaTarget ) ); + bChanges = true; + } + } + + if( bChanges ) + { + rebuild(); + } +} + +CustomAnimationTextGroup::CustomAnimationTextGroup( const Reference< XShape >& rTarget, sal_Int32 nGroupId ) +: maTarget( rTarget ), + mnGroupId( nGroupId ) +{ + reset(); +} + +void CustomAnimationTextGroup::reset() +{ + mnTextGrouping = -1; + mbAnimateForm = false; + mbTextReverse = false; + mfGroupingAuto = -1.0; + mnLastPara = -1; // used to check for TextReverse + + for (sal_Int8 & rn : mnDepthFlags) + { + rn = 0; + } + + maEffects.clear(); +} + +void CustomAnimationTextGroup::addEffect( CustomAnimationEffectPtr const & pEffect ) +{ + maEffects.push_back( pEffect ); + + Any aTarget( pEffect->getTarget() ); + if( aTarget.getValueType() == ::cppu::UnoType::get() ) + { + // now look at the paragraph + ParagraphTarget aParaTarget; + aTarget >>= aParaTarget; + + if( mnLastPara != -1 ) + mbTextReverse = mnLastPara > aParaTarget.Paragraph; + + mnLastPara = aParaTarget.Paragraph; + + const sal_Int32 nParaDepth = pEffect->getParaDepth(); + + // only look at the first PARA_LEVELS levels + if( nParaDepth < PARA_LEVELS ) + { + // our first paragraph with this level? + if( mnDepthFlags[nParaDepth] == 0 ) + { + // so set it to the first found + mnDepthFlags[nParaDepth] = static_cast(pEffect->getNodeType()); + } + else if( mnDepthFlags[nParaDepth] != pEffect->getNodeType() ) + { + mnDepthFlags[nParaDepth] = -1; + } + + if( pEffect->getNodeType() == EffectNodeType::AFTER_PREVIOUS ) + mfGroupingAuto = pEffect->getBegin(); + + mnTextGrouping = PARA_LEVELS; + while( (mnTextGrouping > 0) + && (mnDepthFlags[mnTextGrouping - 1] <= 0) ) + --mnTextGrouping; + } + } + else + { + // if we have an effect with the shape as a target, we animate the background + mbAnimateForm = pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_TEXT; + } +} + +CustomAnimationTextGroupPtr EffectSequenceHelper::findGroup( sal_Int32 nGroupId ) +{ + CustomAnimationTextGroupPtr aPtr; + + CustomAnimationTextGroupMap::iterator aIter( maGroupMap.find( nGroupId ) ); + if( aIter != maGroupMap.end() ) + aPtr = (*aIter).second; + + return aPtr; +} + +void EffectSequenceHelper::updateTextGroups() +{ + maGroupMap.clear(); + + // first create all the groups + for( const CustomAnimationEffectPtr& pEffect : maEffects ) + { + const sal_Int32 nGroupId = pEffect->getGroupId(); + + if( nGroupId == -1 ) + continue; // trivial case, no group + + CustomAnimationTextGroupPtr pGroup = findGroup( nGroupId ); + if( !pGroup ) + { + pGroup = std::make_shared( pEffect->getTargetShape(), nGroupId ); + maGroupMap[nGroupId] = pGroup; + } + + pGroup->addEffect( pEffect ); + } + + // Now that all the text groups have been cleared up and rebuilt, we need to update its + // text grouping. addEffect() already make mnTextGrouping the last possible level, + // so just continue to find the last level that is not EffectNodeType::WITH_PREVIOUS. + for(const auto &rGroupMapItem: maGroupMap) + { + const CustomAnimationTextGroupPtr &pGroup = rGroupMapItem.second; + while(pGroup->mnTextGrouping > 0 && pGroup->mnDepthFlags[pGroup->mnTextGrouping - 1] == EffectNodeType::WITH_PREVIOUS) + --pGroup->mnTextGrouping; + } +} + +CustomAnimationTextGroupPtr +EffectSequenceHelper::createTextGroup(const CustomAnimationEffectPtr& pEffect, + sal_Int32 nTextGrouping, double fTextGroupingAuto, + bool bAnimateForm, bool bTextReverse) +{ + // first find a free group-id + sal_Int32 nGroupId = 0; + + CustomAnimationTextGroupMap::iterator aIter( maGroupMap.begin() ); + const CustomAnimationTextGroupMap::iterator aEnd( maGroupMap.end() ); + while( aIter != aEnd ) + { + if( (*aIter).first == nGroupId ) + { + nGroupId++; + aIter = maGroupMap.begin(); + } + else + { + ++aIter; + } + } + + Reference< XShape > xTarget( pEffect->getTargetShape() ); + + CustomAnimationTextGroupPtr pTextGroup = std::make_shared( xTarget, nGroupId ); + maGroupMap[nGroupId] = pTextGroup; + + bool bUsed = false; + + // do we need to target the shape? + if( (nTextGrouping == 0) || bAnimateForm ) + { + sal_Int16 nSubItem; + if( nTextGrouping == 0) + nSubItem = bAnimateForm ? ShapeAnimationSubType::AS_WHOLE : ShapeAnimationSubType::ONLY_TEXT; + else + nSubItem = ShapeAnimationSubType::ONLY_BACKGROUND; + + pEffect->setTarget( Any( xTarget ) ); + pEffect->setTargetSubItem( nSubItem ); + pEffect->setEffectSequence( this ); + pEffect->setGroupId( nGroupId ); + + pTextGroup->addEffect( pEffect ); + bUsed = true; + } + + pTextGroup->mnTextGrouping = nTextGrouping; + pTextGroup->mfGroupingAuto = fTextGroupingAuto; + pTextGroup->mbTextReverse = bTextReverse; + + // now add an effect for each paragraph + createTextGroupParagraphEffects( pTextGroup, pEffect, bUsed ); + + notify_listeners(); + + return pTextGroup; +} + +void EffectSequenceHelper::createTextGroupParagraphEffects( const CustomAnimationTextGroupPtr& pTextGroup, const CustomAnimationEffectPtr& pEffect, bool bUsed ) +{ + Reference< XShape > xTarget( pTextGroup->maTarget ); + + sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping; + double fTextGroupingAuto = pTextGroup->mfGroupingAuto; + bool bTextReverse = pTextGroup->mbTextReverse; + + // now add an effect for each paragraph + if( nTextGrouping < 0 ) + return; + + try + { + EffectSequence::iterator aInsertIter( find( pEffect ) ); + + Reference< XEnumerationAccess > xText( xTarget, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_SET_THROW ); + + std::deque< sal_Int16 > aParaList; + sal_Int16 nPara; + + // fill the list with all valid paragraphs + for( nPara = 0; xEnumeration->hasMoreElements(); nPara++ ) + { + Reference< XTextRange > xRange( xEnumeration->nextElement(), UNO_QUERY ); + if( xRange.is() && !xRange->getString().isEmpty() ) + { + if( bTextReverse ) // sort them + aParaList.push_front( nPara ); + else + aParaList.push_back( nPara ); + } + } + + ParagraphTarget aTarget; + aTarget.Shape = xTarget; + + for( const auto i : aParaList ) + { + aTarget.Paragraph = i; + + CustomAnimationEffectPtr pNewEffect; + if( bUsed ) + { + // clone a new effect from first effect + pNewEffect = pEffect->clone(); + ++aInsertIter; + aInsertIter = maEffects.insert( aInsertIter, pNewEffect ); + } + else + { + // reuse first effect if it's not yet used + pNewEffect = pEffect; + bUsed = true; + aInsertIter = find( pNewEffect ); + } + + // set target and group-id + pNewEffect->setTarget( Any( aTarget ) ); + pNewEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT ); + pNewEffect->setGroupId( pTextGroup->mnGroupId ); + pNewEffect->setEffectSequence( this ); + + // set correct node type + if( pNewEffect->getParaDepth() < nTextGrouping ) + { + if( fTextGroupingAuto == -1.0 ) + { + pNewEffect->setNodeType( EffectNodeType::ON_CLICK ); + pNewEffect->setBegin( 0.0 ); + } + else + { + pNewEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + pNewEffect->setBegin( fTextGroupingAuto ); + } + } + else + { + pNewEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); + pNewEffect->setBegin( 0.0 ); + } + + pTextGroup->addEffect( pNewEffect ); + } + notify_listeners(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createTextGroup()" ); + } +} + +void EffectSequenceHelper::setTextGrouping( const CustomAnimationTextGroupPtr& pTextGroup, sal_Int32 nTextGrouping ) +{ + if( pTextGroup->mnTextGrouping == nTextGrouping ) + { + // first case, trivial case, do nothing + } + else if( (pTextGroup->mnTextGrouping == -1) && (nTextGrouping >= 0) ) + { + // second case, we need to add new effects for each paragraph + + CustomAnimationEffectPtr pEffect( pTextGroup->maEffects.front() ); + + pTextGroup->mnTextGrouping = nTextGrouping; + createTextGroupParagraphEffects( pTextGroup, pEffect, true ); + notify_listeners(); + } + else if( (pTextGroup->mnTextGrouping >= 0) && (nTextGrouping == -1 ) ) + { + // third case, we need to remove effects for each paragraph + + EffectSequence aEffects( pTextGroup->maEffects ); + pTextGroup->reset(); + + for( const CustomAnimationEffectPtr& pEffect : aEffects ) + { + if( pEffect->getTarget().getValueType() == ::cppu::UnoType::get() ) + remove( pEffect ); + else + pTextGroup->addEffect( pEffect ); + } + notify_listeners(); + } + else + { + // fourth case, we need to change the node types for the text nodes + double fTextGroupingAuto = pTextGroup->mfGroupingAuto; + + EffectSequence aEffects( pTextGroup->maEffects ); + pTextGroup->reset(); + + for( CustomAnimationEffectPtr& pEffect : aEffects ) + { + if( pEffect->getTarget().getValueType() == ::cppu::UnoType::get() ) + { + // set correct node type + if( pEffect->getParaDepth() < nTextGrouping ) + { + if( fTextGroupingAuto == -1.0 ) + { + pEffect->setNodeType( EffectNodeType::ON_CLICK ); + pEffect->setBegin( 0.0 ); + } + else + { + pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + pEffect->setBegin( fTextGroupingAuto ); + } + } + else + { + pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); + pEffect->setBegin( 0.0 ); + } + } + + pTextGroup->addEffect( pEffect ); + + } + notify_listeners(); + } +} + +void EffectSequenceHelper::setAnimateForm( const CustomAnimationTextGroupPtr& pTextGroup, bool bAnimateForm ) +{ + if( pTextGroup->mbAnimateForm == bAnimateForm ) + { + // trivial case, do nothing + } + else + { + EffectSequence aEffects( pTextGroup->maEffects ); + pTextGroup->reset(); + + SAL_WARN_IF(aEffects.empty(), "sd", "EffectSequenceHelper::setAnimateForm effects empty" ); + + if (aEffects.empty()) + return; + + EffectSequence::iterator aIter( aEffects.begin() ); + const EffectSequence::iterator aEnd( aEffects.end() ); + + // first insert if we have to + if( bAnimateForm ) + { + EffectSequence::iterator aInsertIter( find( *aIter ) ); + + CustomAnimationEffectPtr pEffect; + if( (aEffects.size() == 1) && ((*aIter)->getTarget().getValueType() != ::cppu::UnoType::get() ) ) + { + // special case, only one effect and that targets whole text, + // convert this to target whole shape + pEffect = *aIter++; + pEffect->setTargetSubItem( ShapeAnimationSubType::AS_WHOLE ); + } + else + { + pEffect = (*aIter)->clone(); + pEffect->setTarget( Any( (*aIter)->getTargetShape() ) ); + pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_BACKGROUND ); + maEffects.insert( aInsertIter, pEffect ); + } + + pTextGroup->addEffect( pEffect ); + } + + if( !bAnimateForm && (aEffects.size() == 1) ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + pEffect->setTarget( Any( (*aIter)->getTargetShape() ) ); + pEffect->setTargetSubItem( ShapeAnimationSubType::ONLY_TEXT ); + pTextGroup->addEffect( pEffect ); + } + else + { + // read the rest to the group again + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect( *aIter++ ); + + if( pEffect->getTarget().getValueType() == ::cppu::UnoType::get() ) + { + pTextGroup->addEffect( pEffect ); + } + else + { + DBG_ASSERT( !bAnimateForm, "sd::EffectSequenceHelper::setAnimateForm(), something is wrong here!" ); + remove( pEffect ); + } + } + } + notify_listeners(); + } +} + +void EffectSequenceHelper::setTextGroupingAuto( const CustomAnimationTextGroupPtr& pTextGroup, double fTextGroupingAuto ) +{ + sal_Int32 nTextGrouping = pTextGroup->mnTextGrouping; + + EffectSequence aEffects( pTextGroup->maEffects ); + pTextGroup->reset(); + + for( CustomAnimationEffectPtr& pEffect : aEffects ) + { + if( pEffect->getTarget().getValueType() == ::cppu::UnoType::get() ) + { + // set correct node type + if( pEffect->getParaDepth() < nTextGrouping ) + { + if( fTextGroupingAuto == -1.0 ) + { + pEffect->setNodeType( EffectNodeType::ON_CLICK ); + pEffect->setBegin( 0.0 ); + } + else + { + pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + pEffect->setBegin( fTextGroupingAuto ); + } + } + else + { + pEffect->setNodeType( EffectNodeType::WITH_PREVIOUS ); + pEffect->setBegin( 0.0 ); + } + } + + pTextGroup->addEffect( pEffect ); + + } + notify_listeners(); +} + +namespace { + +struct ImplStlTextGroupSortHelper +{ + explicit ImplStlTextGroupSortHelper( bool bReverse ) : mbReverse( bReverse ) {}; + bool operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 ); + bool mbReverse; + sal_Int32 getTargetParagraph( const CustomAnimationEffectPtr& p1 ); +}; + +} + +sal_Int32 ImplStlTextGroupSortHelper::getTargetParagraph( const CustomAnimationEffectPtr& p1 ) +{ + const Any aTarget(p1->getTarget()); + if( aTarget.hasValue() && aTarget.getValueType() == ::cppu::UnoType::get() ) + { + ParagraphTarget aParaTarget; + aTarget >>= aParaTarget; + return aParaTarget.Paragraph; + } + else + { + return mbReverse ? 0x7fffffff : -1; + } +} + +bool ImplStlTextGroupSortHelper::operator()( const CustomAnimationEffectPtr& p1, const CustomAnimationEffectPtr& p2 ) +{ + if( mbReverse ) + { + return getTargetParagraph( p2 ) < getTargetParagraph( p1 ); + } + else + { + return getTargetParagraph( p1 ) < getTargetParagraph( p2 ); + } +} + +void EffectSequenceHelper::setTextReverse( const CustomAnimationTextGroupPtr& pTextGroup, bool bTextReverse ) +{ + if( pTextGroup->mbTextReverse == bTextReverse ) + { + // do nothing + } + else + { + std::vector< CustomAnimationEffectPtr > aSortedVector( pTextGroup->maEffects.begin(), pTextGroup->maEffects.end() ); + ImplStlTextGroupSortHelper aSortHelper( bTextReverse ); + std::sort( aSortedVector.begin(), aSortedVector.end(), aSortHelper ); + + pTextGroup->reset(); + + std::vector< CustomAnimationEffectPtr >::iterator aIter( aSortedVector.begin() ); + const std::vector< CustomAnimationEffectPtr >::iterator aEnd( aSortedVector.end() ); + + if( aIter != aEnd ) + { + pTextGroup->addEffect( *aIter ); + EffectSequence::iterator aInsertIter( find( *aIter++ ) ); + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect( *aIter++ ); + maEffects.erase( find( pEffect ) ); + aInsertIter = maEffects.insert( ++aInsertIter, pEffect ); + pTextGroup->addEffect( pEffect ); + } + } + notify_listeners(); + } +} + +void EffectSequenceHelper::addListener( ISequenceListener* pListener ) +{ + if( std::find( maListeners.begin(), maListeners.end(), pListener ) == maListeners.end() ) + maListeners.push_back( pListener ); +} + +void EffectSequenceHelper::removeListener( ISequenceListener* pListener ) +{ + maListeners.remove( pListener ); +} + +namespace { + +struct stl_notify_listeners_func +{ + stl_notify_listeners_func() {} + void operator()(ISequenceListener* pListener) { pListener->notify_change(); } +}; + +} + +void EffectSequenceHelper::notify_listeners() +{ + stl_notify_listeners_func aFunc; + std::for_each( maListeners.begin(), maListeners.end(), aFunc ); +} + +void EffectSequenceHelper::create( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +{ + DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::create(), illegal argument" ); + + if( !xNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + createEffectsequence( xChildNode ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::create()" ); + } +} + +void EffectSequenceHelper::createEffectsequence( const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffectsequence(), illegal argument" ); + + if( !xNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + createEffects( xChildNode ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createEffectsequence()" ); + } +} + +void EffectSequenceHelper::createEffects( const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( xNode.is(), "sd::EffectSequenceHelper::createEffects(), illegal argument" ); + + if( !xNode.is() ) + return; + + try + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + switch( xChildNode->getType() ) + { + // found an effect + case AnimationNodeType::PAR: + case AnimationNodeType::ITERATE: + { + CustomAnimationEffectPtr pEffect = std::make_shared( xChildNode ); + + if( pEffect->mnNodeType != -1 ) + { + pEffect->setEffectSequence( this ); + maEffects.push_back(pEffect); + } + } + break; + + // found an after effect + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATECOLOR: + { + processAfterEffect( xChildNode ); + } + break; + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::createEffects()" ); + } +} + +void EffectSequenceHelper::processAfterEffect( const Reference< XAnimationNode >& xNode ) +{ + try + { + Reference< XAnimationNode > xMaster; + + const Sequence< NamedValue > aUserData( xNode->getUserData() ); + const NamedValue* pProp = std::find_if(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "master-element"; }); + + if (pProp != aUserData.end()) + pProp->Value >>= xMaster; + + // only process if this is a valid after effect + if( xMaster.is() ) + { + CustomAnimationEffectPtr pMasterEffect; + + // find the master effect + stl_CustomAnimationEffect_search_node_predict aSearchPredict( xMaster ); + EffectSequence::iterator aIter( std::find_if( maEffects.begin(), maEffects.end(), aSearchPredict ) ); + if( aIter != maEffects.end() ) + pMasterEffect = *aIter; + + if( pMasterEffect ) + { + pMasterEffect->setHasAfterEffect( true ); + + // find out what kind of after effect this is + if( xNode->getType() == AnimationNodeType::ANIMATECOLOR ) + { + // it's a dim + Reference< XAnimate > xAnimate( xNode, UNO_QUERY_THROW ); + pMasterEffect->setDimColor( xAnimate->getTo() ); + pMasterEffect->setAfterEffectOnNext( true ); + } + else + { + // it's a hide + pMasterEffect->setAfterEffectOnNext( xNode->getParent() != xMaster->getParent() ); + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::EffectSequenceHelper::processAfterEffect()" ); + } +} + +namespace { + +class AnimationChangeListener : public cppu::WeakImplHelper< XChangesListener > +{ +public: + explicit AnimationChangeListener( MainSequence* pMainSequence ) : mpMainSequence( pMainSequence ) {} + + virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& Event ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; +private: + MainSequence* mpMainSequence; +}; + +} + +void SAL_CALL AnimationChangeListener::changesOccurred( const css::util::ChangesEvent& ) +{ + if( mpMainSequence ) + mpMainSequence->startRecreateTimer(); +} + +void SAL_CALL AnimationChangeListener::disposing( const css::lang::EventObject& ) +{ +} + +MainSequence::MainSequence() + : mxTimingRootNode(SequenceTimeContainer::create(::comphelper::getProcessComponentContext())) + , maTimer("sd MainSequence maTimer") + , mbTimerMode(false) + , mbRebuilding( false ) + , mnRebuildLockGuard( 0 ) + , mbPendingRebuildRequest( false ) + , mbIgnoreChanges( 0 ) +{ + if( mxTimingRootNode.is() ) + { + Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::MAIN_SEQUENCE) } }; + mxTimingRootNode->setUserData( aUserData ); + } + init(); +} + +MainSequence::MainSequence( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) + : mxTimingRootNode( xNode, UNO_QUERY ) + , maTimer("sd MainSequence maTimer") + , mbTimerMode( false ) + , mbRebuilding( false ) + , mnRebuildLockGuard( 0 ) + , mbPendingRebuildRequest( false ) + , mbIgnoreChanges( 0 ) +{ + init(); +} + +MainSequence::~MainSequence() +{ + reset(); +} + +void MainSequence::init() +{ + mnSequenceType = EffectNodeType::MAIN_SEQUENCE; + + maTimer.SetInvokeHandler( LINK(this, MainSequence, onTimerHdl) ); + maTimer.SetTimeout(50); + + mxChangesListener.set( new AnimationChangeListener( this ) ); + + createMainSequence(); +} + +void MainSequence::reset( const css::uno::Reference< css::animations::XAnimationNode >& xTimingRootNode ) +{ + reset(); + + mxTimingRootNode.set( xTimingRootNode, UNO_QUERY ); + + createMainSequence(); +} + +Reference< css::animations::XAnimationNode > MainSequence::getRootNode() +{ + DBG_ASSERT( mnRebuildLockGuard == 0, "MainSequence::getRootNode(), rebuild is locked, is this really what you want?" ); + + if( maTimer.IsActive() && mbTimerMode ) + { + // force a rebuild NOW if one is pending + maTimer.Stop(); + implRebuild(); + } + + return EffectSequenceHelper::getRootNode(); +} + +void MainSequence::createMainSequence() +{ + if( mxTimingRootNode.is() ) try + { + Reference< XEnumerationAccess > xEnumerationAccess( mxTimingRootNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + sal_Int32 nNodeType = CustomAnimationEffect::get_node_type( xChildNode ); + if( nNodeType == EffectNodeType::MAIN_SEQUENCE ) + { + mxSequenceRoot.set( xChildNode, UNO_QUERY ); + EffectSequenceHelper::create( xChildNode ); + } + else if( nNodeType == EffectNodeType::INTERACTIVE_SEQUENCE ) + { + Reference< XTimeContainer > xInteractiveRoot( xChildNode, UNO_QUERY_THROW ); + InteractiveSequencePtr pIS = std::make_shared( xInteractiveRoot, this ); + pIS->addListener( this ); + maInteractiveSequenceVector.push_back( pIS ); + } + } + + // see if we have a mainsequence at all. if not, create one... + if( !mxSequenceRoot.is() ) + { + mxSequenceRoot = SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() ); + + uno::Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::MAIN_SEQUENCE) } }; + mxSequenceRoot->setUserData( aUserData ); + + // empty sequence until now, set duration to 0.0 + // explicitly (otherwise, this sequence will never + // end) + mxSequenceRoot->setDuration( Any(0.0) ); + + Reference< XAnimationNode > xMainSequenceNode( mxSequenceRoot, UNO_QUERY_THROW ); + mxTimingRootNode->appendChild( xMainSequenceNode ); + } + + updateTextGroups(); + + notify_listeners(); + + Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY ); + if( xNotifier.is() ) + xNotifier->addChangesListener( mxChangesListener ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::MainSequence::create()" ); + return; + } + + DBG_ASSERT( mxSequenceRoot.is(), "sd::MainSequence::create(), found no main sequence!" ); +} + +void MainSequence::reset() +{ + EffectSequenceHelper::reset(); + + for (auto const& interactiveSequence : maInteractiveSequenceVector) + interactiveSequence->reset(); + maInteractiveSequenceVector.clear(); + + try + { + Reference< XChangesNotifier > xNotifier( mxTimingRootNode, UNO_QUERY ); + if( xNotifier.is() ) + xNotifier->removeChangesListener( mxChangesListener ); + } + catch( Exception& ) + { + + } +} + +InteractiveSequencePtr MainSequence::createInteractiveSequence( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + InteractiveSequencePtr pIS; + + // create a new interactive sequence container + Reference< XTimeContainer > xISRoot = SequenceTimeContainer::create( ::comphelper::getProcessComponentContext() ); + + uno::Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE) } }; + xISRoot->setUserData( aUserData ); + xISRoot->setRestart( css::animations::AnimationRestart::WHEN_NOT_ACTIVE ); + + Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW ); + Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW ); + xParent->appendChild( xISRoot ); + + pIS = std::make_shared( xISRoot, this); + pIS->setTriggerShape( xShape ); + pIS->addListener( this ); + maInteractiveSequenceVector.push_back( pIS ); + return pIS; +} + +CustomAnimationEffectPtr MainSequence::findEffect( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const +{ + CustomAnimationEffectPtr pEffect = EffectSequenceHelper::findEffect( xNode ); + + if( !pEffect ) + { + for (auto const& interactiveSequence : maInteractiveSequenceVector) + { + pEffect = interactiveSequence->findEffect( xNode ); + if (pEffect) + break; + } + } + return pEffect; +} + +sal_Int32 MainSequence::getOffsetFromEffect( const CustomAnimationEffectPtr& pEffect ) const +{ + sal_Int32 nOffset = EffectSequenceHelper::getOffsetFromEffect( pEffect ); + + if( nOffset != -1 ) + return nOffset; + + nOffset = EffectSequenceHelper::getCount(); + + for (auto const& interactiveSequence : maInteractiveSequenceVector) + { + sal_Int32 nTemp = interactiveSequence->getOffsetFromEffect( pEffect ); + if( nTemp != -1 ) + return nOffset + nTemp; + + nOffset += interactiveSequence->getCount(); + } + + return -1; +} + +CustomAnimationEffectPtr MainSequence::getEffectFromOffset( sal_Int32 nOffset ) const +{ + if( nOffset >= 0 ) + { + if( nOffset < getCount() ) + return EffectSequenceHelper::getEffectFromOffset( nOffset ); + + nOffset -= getCount(); + + auto aIter( maInteractiveSequenceVector.begin() ); + + while( (aIter != maInteractiveSequenceVector.end()) && (nOffset > (*aIter)->getCount()) ) + nOffset -= (*aIter++)->getCount(); + + if( (aIter != maInteractiveSequenceVector.end()) && (nOffset >= 0) ) + return (*aIter)->getEffectFromOffset( nOffset ); + } + + CustomAnimationEffectPtr pEffect; + return pEffect; +} + +bool MainSequence::disposeShape( const Reference< XShape >& xShape ) +{ + bool bChanges = EffectSequenceHelper::disposeShape( xShape ); + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + bChanges |= iterativeSequence->disposeShape( xShape ); + } + + if( bChanges ) + startRebuildTimer(); + + return bChanges; +} + +bool MainSequence::hasEffect( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + if( EffectSequenceHelper::hasEffect( xShape ) ) + return true; + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + if( iterativeSequence->getTriggerShape() == xShape ) + return true; + + if( iterativeSequence->hasEffect( xShape ) ) + return true; + } + + return false; +} + +void MainSequence::insertTextRange( const css::uno::Any& aTarget ) +{ + EffectSequenceHelper::insertTextRange( aTarget ); + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + iterativeSequence->insertTextRange( aTarget ); + } +} + +void MainSequence::disposeTextRange( const css::uno::Any& aTarget ) +{ + EffectSequenceHelper::disposeTextRange( aTarget ); + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + iterativeSequence->disposeTextRange( aTarget ); + } +} + +/** callback from the sd::View when an object just left text edit mode */ +void MainSequence::onTextChanged( const Reference< XShape >& xShape ) +{ + EffectSequenceHelper::onTextChanged( xShape ); + + for (auto const& iterativeSequence : maInteractiveSequenceVector) + { + iterativeSequence->onTextChanged( xShape ); + } +} + +void EffectSequenceHelper::onTextChanged( const Reference< XShape >& xShape ) +{ + // get map [paragraph index] -> [NumberingLevel] + // for following reusage inside all animation effects + std::vector< sal_Int32 > paragraphNumberingLevel; + std::vector< sal_Int32 >* paragraphNumberingLevelParam = nullptr; + if ( getParagraphNumberingLevels( xShape, paragraphNumberingLevel ) ) + paragraphNumberingLevelParam = ¶graphNumberingLevel; + + // update internal flags for each animation effect + const bool bChanges = std::accumulate(maEffects.begin(), maEffects.end(), false, + [&xShape, ¶graphNumberingLevelParam](const bool bCheck, const CustomAnimationEffectPtr& rxEffect) { + bool bRes = bCheck; + if (rxEffect->getTargetShape() == xShape) + bRes |= rxEffect->checkForText( paragraphNumberingLevelParam ); + return bRes; + }); + + if( bChanges ) + rebuild(); +} + +void MainSequence::rebuild() +{ + startRebuildTimer(); +} + +void MainSequence::lockRebuilds() +{ + mnRebuildLockGuard++; +} + +void MainSequence::unlockRebuilds() +{ + DBG_ASSERT( mnRebuildLockGuard, "sd::MainSequence::unlockRebuilds(), no corresponding lockRebuilds() call!" ); + if( mnRebuildLockGuard ) + mnRebuildLockGuard--; + + if( (mnRebuildLockGuard == 0) && mbPendingRebuildRequest ) + { + mbPendingRebuildRequest = false; + startRebuildTimer(); + } +} + +void MainSequence::implRebuild() +{ + if( mnRebuildLockGuard ) + { + mbPendingRebuildRequest = true; + return; + } + + mbRebuilding = true; + + EffectSequenceHelper::implRebuild(); + + auto aIter( maInteractiveSequenceVector.begin() ); + while( aIter != maInteractiveSequenceVector.end() ) + { + InteractiveSequencePtr pIS( *aIter ); + if( pIS->maEffects.empty() ) + { + // remove empty interactive sequences + aIter = maInteractiveSequenceVector.erase( aIter ); + + Reference< XChild > xChild( mxSequenceRoot, UNO_QUERY_THROW ); + Reference< XTimeContainer > xParent( xChild->getParent(), UNO_QUERY_THROW ); + Reference< XAnimationNode > xISNode( pIS->mxSequenceRoot, UNO_QUERY_THROW ); + xParent->removeChild( xISNode ); + } + else + { + pIS->implRebuild(); + ++aIter; + } + } + + notify_listeners(); + mbRebuilding = false; +} + +void MainSequence::notify_change() +{ + notify_listeners(); +} + +bool MainSequence::setTrigger( const CustomAnimationEffectPtr& pEffect, const css::uno::Reference< css::drawing::XShape >& xTriggerShape ) +{ + EffectSequenceHelper* pOldSequence = pEffect->getEffectSequence(); + + EffectSequenceHelper* pNewSequence = nullptr; + if( xTriggerShape.is() ) + { + for (InteractiveSequencePtr const& pIS : maInteractiveSequenceVector) + { + if( pIS->getTriggerShape() == xTriggerShape ) + { + pNewSequence = pIS.get(); + break; + } + } + + if( !pNewSequence ) + pNewSequence = createInteractiveSequence( xTriggerShape ).get(); + } + else + { + pNewSequence = this; + } + + if( pOldSequence != pNewSequence ) + { + if( pOldSequence ) + pOldSequence->maEffects.remove( pEffect ); + if( pNewSequence ) + pNewSequence->maEffects.push_back( pEffect ); + pEffect->setEffectSequence( pNewSequence ); + return true; + } + else + { + return false; + } + +} + +IMPL_LINK_NOARG(MainSequence, onTimerHdl, Timer *, void) +{ + if( mbTimerMode ) + { + implRebuild(); + } + else + { + reset(); + createMainSequence(); + } +} + +/** starts a timer that recreates the internal structure from the API core */ +void MainSequence::startRecreateTimer() +{ + if( !mbRebuilding && (mbIgnoreChanges == 0) ) + { + mbTimerMode = false; + maTimer.Start(); + } +} + +/** + * starts a timer that rebuilds the API core from the internal structure + * This is used to reduce the number of screen redraws due to animation changes. +*/ +void MainSequence::startRebuildTimer() +{ + mbTimerMode = true; + maTimer.Start(); +} + +InteractiveSequence::InteractiveSequence( const Reference< XTimeContainer >& xSequenceRoot, MainSequence* pMainSequence ) +: EffectSequenceHelper( xSequenceRoot ), mpMainSequence( pMainSequence ) +{ + mnSequenceType = EffectNodeType::INTERACTIVE_SEQUENCE; + + try + { + if( mxSequenceRoot.is() ) + { + Reference< XEnumerationAccess > xEnumerationAccess( mxSequenceRoot, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), UNO_SET_THROW ); + while( !mxEventSource.is() && xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + Event aEvent; + if( (xChildNode->getBegin() >>= aEvent) && (aEvent.Trigger == EventTrigger::ON_CLICK) ) + aEvent.Source >>= mxEventSource; + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::InteractiveSequence::InteractiveSequence()" ); + return; + } +} + +void InteractiveSequence::rebuild() +{ + mpMainSequence->rebuild(); +} + +void InteractiveSequence::implRebuild() +{ + EffectSequenceHelper::implRebuild(); +} + +MainSequenceRebuildGuard::MainSequenceRebuildGuard( const MainSequencePtr& pMainSequence ) +: mpMainSequence( pMainSequence ) +{ + if( mpMainSequence ) + mpMainSequence->lockRebuilds(); +} + +MainSequenceRebuildGuard::~MainSequenceRebuildGuard() +{ + if( mpMainSequence ) + mpMainSequence->unlockRebuilds(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/CustomAnimationPreset.cxx b/sd/source/core/CustomAnimationPreset.cxx new file mode 100644 index 000000000..d7c19401d --- /dev/null +++ b/sd/source/core/CustomAnimationPreset.cxx @@ -0,0 +1,514 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::io::XInputStream; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::util::XCloneable; +using ::com::sun::star::beans::NamedValue; + +namespace sd { + +static Reference< XNameAccess > getNodeAccess( const Reference< XMultiServiceFactory >& xConfigProvider, const OUString& rNodePath ) +{ + Reference< XNameAccess > xConfigAccess; + + try + { + Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(rNodePath)} + })); + + xConfigAccess.set( + xConfigProvider->createInstanceWithArguments( "com.sun.star.configuration.ConfigurationAccess", aArgs ), + UNO_QUERY); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::getNodeAccess()" ); + } + + return xConfigAccess; +} + +void implImportLabels( const Reference< XMultiServiceFactory >& xConfigProvider, const OUString& rNodePath, UStringMap& rStringMap ) +{ + try + { + Reference< XNameAccess > xConfigAccess( getNodeAccess( xConfigProvider, rNodePath ) ); + if( xConfigAccess.is() ) + { + Reference< XNameAccess > xNameAccess; + const Sequence< OUString > aNames( xConfigAccess->getElementNames() ); + for(const OUString& rName : aNames) + { + xConfigAccess->getByName( rName ) >>= xNameAccess; + if( xNameAccess.is() ) + { + OUString aUIName; + xNameAccess->getByName( "Label" ) >>= aUIName; + if( !aUIName.isEmpty() ) + { + rStringMap[ rName ] = aUIName; + } + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::implImportLabels()" ); + } +} + +CustomAnimationPreset::CustomAnimationPreset( const CustomAnimationEffectPtr& pEffect ) +{ + maPresetId = pEffect->getPresetId(); + maProperty = pEffect->getProperty(); + + add( pEffect ); + + mfDuration = pEffect->getDuration(); + maDefaultSubTyp = pEffect->getPresetSubType(); + + const Sequence< NamedValue > aUserData( pEffect->getNode()->getUserData() ); + + mbIsTextOnly = std::any_of(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "text-only"; }); +} + +void CustomAnimationPreset::add( const CustomAnimationEffectPtr& pEffect ) +{ + maSubTypes[ pEffect->getPresetSubType() ] = pEffect; +} + +std::vector CustomAnimationPreset::getSubTypes() +{ + std::vector aSubTypes; + + if( maSubTypes.size() > 1 ) + { + std::transform(maSubTypes.begin(), maSubTypes.end(), std::back_inserter(aSubTypes), + [](EffectsSubTypeMap::value_type& rEntry) -> OUString { return rEntry.first; }); + } + + return aSubTypes; +} + +Reference< XAnimationNode > CustomAnimationPreset::create( const OUString& rstrSubType ) +{ + try + { + OUString strSubType( rstrSubType ); + if( strSubType.isEmpty() ) + strSubType = maDefaultSubTyp; + + CustomAnimationEffectPtr pEffect = maSubTypes[strSubType]; + if( pEffect ) + { + Reference< XCloneable > xCloneable( pEffect->getNode(), UNO_QUERY_THROW ); + Reference< XAnimationNode > xNode( xCloneable->createClone(), UNO_QUERY_THROW ); + return xNode; + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPresets::create()" ); + } + + Reference< XAnimationNode > xNode; + return xNode; +} + +std::vector CustomAnimationPreset::getProperties() const +{ + std::vector aPropertyList; + if (!maProperty.isEmpty()) + { + sal_Int32 nPos = 0; + do + { + aPropertyList.push_back(maProperty.getToken(0, ';', nPos)); + } + while (nPos >= 0); + } + return aPropertyList; +} + +bool CustomAnimationPreset::hasProperty( std::u16string_view rProperty )const +{ + if (maProperty.isEmpty()) + return false; + + sal_Int32 nPos = 0; + do + { + if (o3tl::getToken(maProperty, 0, ';', nPos) == rProperty) + return true; + } + while (nPos >= 0); + + return false; +} + +CustomAnimationPresets::CustomAnimationPresets() +{ +} + +CustomAnimationPresets::~CustomAnimationPresets() +{ +} + +Reference< XAnimationNode > implImportEffects( const Reference< XMultiServiceFactory >& xServiceFactory, const OUString& rPath ) +{ + Reference< XAnimationNode > xRootNode; + + try + { + // create stream + std::unique_ptr pIStm = ::utl::UcbStreamHelper::CreateStream( rPath, StreamMode::READ ); + Reference xInputStream( new utl::OInputStreamWrapper( std::move(pIStm) ) ); + + // prepare ParserInputSource + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = rPath; + aParserInput.aInputStream = xInputStream; + + // get filter + Reference< xml::sax::XFastParser > xFilter( xServiceFactory->createInstance("com.sun.star.comp.Xmloff.AnimationsImport" ), UNO_QUERY_THROW ); + + xFilter->parseStream( aParserInput ); + + Reference< XAnimationNodeSupplier > xAnimationNodeSupplier( xFilter, UNO_QUERY_THROW ); + xRootNode = xAnimationNodeSupplier->getAnimationNode(); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd", ""); + } + + return xRootNode; +} + +void CustomAnimationPresets::importEffects() +{ + try + { + uno::Reference< uno::XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< XMultiServiceFactory > xServiceFactory( + xContext->getServiceManager(), UNO_QUERY_THROW ); + + Reference< XMultiServiceFactory > xConfigProvider = + configuration::theDefaultProvider::get( xContext ); + + // read path to transition effects files from config + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(OUString("/org.openoffice.Office.Impress/Misc"))} + })); + Reference xNameAccess( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArgs ), UNO_QUERY_THROW ); + uno::Sequence< OUString > aFiles; + xNameAccess->getByName( "EffectFiles" ) >>= aFiles; + + for( const auto& rFile : std::as_const(aFiles) ) + { + OUString aURL = comphelper::getExpandedUri(xContext, rFile); + + mxRootNode = implImportEffects( xServiceFactory, aURL ); + + if( mxRootNode.is() ) + { + Reference< XTimeContainer > xRootContainer( mxRootNode, UNO_QUERY_THROW ); + EffectSequenceHelper aSequence( xRootContainer ); + + EffectSequence::iterator aIter( aSequence.getBegin() ); + const EffectSequence::iterator aEnd( aSequence.getEnd() ); + + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect = *aIter; + + const OUString aPresetId( pEffect->getPresetId() ); + CustomAnimationPresetPtr pDescriptor = getEffectDescriptor( aPresetId ); + if( pDescriptor ) + pDescriptor->add( pEffect ); + else + { + pDescriptor = std::make_shared( pEffect ); + pDescriptor->maLabel = getUINameForPresetId( pEffect->getPresetId() ); + maEffectDescriptorMap[aPresetId] = pDescriptor; + } + + ++aIter; + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPresets::importEffects()" ); + } +} + +void CustomAnimationPresets::importResources() +{ + try + { + // Get service factory + Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() ); + + Reference< XMultiServiceFactory > xConfigProvider = + configuration::theDefaultProvider::get( xContext ); + + implImportLabels( xConfigProvider, "/org.openoffice.Office.UI.Effects/UserInterface/Properties", maPropertyNameMap ); + + implImportLabels( xConfigProvider, "/org.openoffice.Office.UI.Effects/UserInterface/Effects", maEffectNameMap ); + + importEffects(); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/Entrance", maEntrancePresets ); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/Emphasis", maEmphasisPresets ); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/Exit", maExitPresets ); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/MotionPaths", maMotionPathsPresets ); + + importPresets( xConfigProvider, "/org.openoffice.Office.UI.Effects/Presets/Misc", maMiscPresets ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPresets::importResources()" ); + } +} + +void CustomAnimationPresets::importPresets( const Reference< XMultiServiceFactory >& xConfigProvider, const OUString& rNodePath, PresetCategoryList& rPresetMap ) +{ +#ifdef DEBUG + OUString aMissedPresetIds; +#endif + + try + { + Reference< XNameAccess > xTypeAccess( getNodeAccess( xConfigProvider, rNodePath ) ); + if( xTypeAccess.is() ) + { + Reference< XNameAccess > xCategoryAccess; + + const Sequence< OUString > aNames( xTypeAccess->getElementNames() ); + for(const OUString& rName : aNames) + { + xTypeAccess->getByName( rName ) >>= xCategoryAccess; + + if( xCategoryAccess.is() && xCategoryAccess->hasByName( "Label" ) && xCategoryAccess->hasByName( "Effects" ) ) + { + OUString aLabel; + xCategoryAccess->getByName( "Label" ) >>= aLabel; + + Sequence< OUString > aEffects; + xCategoryAccess->getByName( "Effects" ) >>= aEffects; + + EffectDescriptorList aEffectsList; + + for( const OUString& rEffectName : std::as_const(aEffects) ) + { + CustomAnimationPresetPtr pEffect = getEffectDescriptor( rEffectName ); + if( pEffect ) + { + aEffectsList.push_back( pEffect ); + } +#ifdef DEBUG + else + { + aMissedPresetIds += OUString(rEffectName); + aMissedPresetIds += "\n"; + } +#endif + } + rPresetMap.push_back( std::make_shared( aLabel, std::move(aEffectsList) ) ); + } + } + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPresets::importPresets()" ); + } + +#ifdef DEBUG + SAL_WARN_IF(!aMissedPresetIds.isEmpty(), "sd", "sd::CustomAnimationPresets::importPresets(), invalid preset id: " + << aMissedPresetIds); +#endif +} + +CustomAnimationPresetPtr CustomAnimationPresets::getEffectDescriptor( const OUString& rPresetId ) const +{ + EffectDescriptorMap::const_iterator aIter( maEffectDescriptorMap.find( rPresetId ) ); + + if( aIter != maEffectDescriptorMap.end() ) + { + return (*aIter).second; + } + else + { + return CustomAnimationPresetPtr(nullptr); + } +} + +const OUString& CustomAnimationPresets::getUINameForPresetId( const OUString& rPresetId ) const +{ + return translateName( rPresetId, maEffectNameMap ); +} + +const OUString& CustomAnimationPresets::getUINameForProperty( const OUString& rPresetId ) const +{ + return translateName( rPresetId, maPropertyNameMap ); +} + +const OUString& CustomAnimationPresets::translateName( const OUString& rId, const UStringMap& rNameMap ) +{ + UStringMap::const_iterator aIter( rNameMap.find( rId ) ); + + if( aIter != rNameMap.end() ) + { + return (*aIter).second; + } + else + { + return rId; + } +} +void CustomAnimationPresets::changePresetSubType( const CustomAnimationEffectPtr& pEffect, const OUString& rPresetSubType ) const +{ + if( pEffect && pEffect->getPresetSubType() != rPresetSubType ) + { + CustomAnimationPresetPtr pDescriptor( getEffectDescriptor( pEffect->getPresetId() ) ); + + if( pDescriptor ) + { + Reference< XAnimationNode > xNewNode( pDescriptor->create( rPresetSubType ) ); + if( xNewNode.is() ) + pEffect->replaceNode( xNewNode ); + } + } +} + +std::map CustomAnimationPresets::mPresetsMap; + +const CustomAnimationPresets& CustomAnimationPresets::getCustomAnimationPresets() +{ + // Support localization per-view. Currently not useful for Desktop + // but very much critical for LOK. The cache now is per-language. + const OUString aLang = comphelper::LibreOfficeKit::isActive() + ? comphelper::LibreOfficeKit::getLanguageTag().getBcp47() + : SvtSysLocaleOptions().GetLanguageTag().getBcp47(); + + SolarMutexGuard aGuard; + const auto it = mPresetsMap.find(aLang); + if (it != mPresetsMap.end()) + return it->second; + + CustomAnimationPresets& rPresets = mPresetsMap[aLang]; + rPresets.importResources(); + return rPresets; +} + +Reference< XAnimationNode > CustomAnimationPresets::getRandomPreset( sal_Int16 nPresetClass ) const +{ + Reference< XAnimationNode > xNode; + + const PresetCategoryList* pCategoryList = nullptr; + switch( nPresetClass ) + { + case EffectPresetClass::ENTRANCE: pCategoryList = &maEntrancePresets; break; + case EffectPresetClass::EXIT: pCategoryList = &maExitPresets; break; + case EffectPresetClass::EMPHASIS: pCategoryList = &maEmphasisPresets; break; + case EffectPresetClass::MOTIONPATH: pCategoryList = &maMotionPathsPresets; break; + default: + pCategoryList = nullptr; + } + + if( pCategoryList && !pCategoryList->empty() ) + { + sal_Int32 nCategory = comphelper::rng::uniform_size_distribution(0, pCategoryList->size()-1); + + PresetCategoryPtr pCategory = (*pCategoryList)[nCategory]; + if( pCategory && !pCategory->maEffects.empty() ) + { + sal_Int32 nDescriptor = comphelper::rng::uniform_size_distribution(0, pCategory->maEffects.size()-1); + CustomAnimationPresetPtr pPreset = pCategory->maEffects[nDescriptor]; + if( pPreset ) + { + std::vector aSubTypes = pPreset->getSubTypes(); + + OUString aSubType; + if( !aSubTypes.empty() ) + { + size_t nSubType = comphelper::rng::uniform_size_distribution(0, aSubTypes.size()-1); + aSubType = aSubTypes[nSubType]; + } + xNode = pPreset->create( aSubType ); + } + } + } + + return xNode; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/EffectMigration.cxx b/sd/source/core/EffectMigration.cxx new file mode 100644 index 000000000..8dd9d0905 --- /dev/null +++ b/sd/source/core/EffectMigration.cxx @@ -0,0 +1,1439 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::sd; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::beans::NamedValue; + +namespace { + +struct deprecated_FadeEffect_conversion_table_entry +{ + FadeEffect meFadeEffect; + const char* mpPresetId; +}; + +} + +deprecated_FadeEffect_conversion_table_entry const deprecated_FadeEffect_conversion_table[] = +{ +// OOo 1.x transitions + { FadeEffect_FADE_FROM_LEFT, "wipe-right" }, + { FadeEffect_FADE_FROM_TOP, "wipe-down" }, + { FadeEffect_FADE_FROM_RIGHT, "wipe-left" }, + { FadeEffect_FADE_FROM_BOTTOM, "wipe-up" }, + + { FadeEffect_CLOCKWISE, "wheel-clockwise-1-spoke" }, + + { FadeEffect_UNCOVER_TO_LEFT, "uncover-left" }, + { FadeEffect_UNCOVER_TO_UPPERLEFT, "uncover-left-up" }, + { FadeEffect_UNCOVER_TO_TOP, "uncover-up" }, + { FadeEffect_UNCOVER_TO_UPPERRIGHT, "uncover-right-up" }, + { FadeEffect_UNCOVER_TO_RIGHT, "uncover-right" }, + { FadeEffect_UNCOVER_TO_LOWERRIGHT, "uncover-right-down" }, + { FadeEffect_UNCOVER_TO_BOTTOM, "uncover-down" }, + { FadeEffect_UNCOVER_TO_LOWERLEFT, "uncover-left-down" }, + + { FadeEffect_VERTICAL_LINES, "random-bars-vertical" }, + { FadeEffect_HORIZONTAL_LINES, "random-bars-horizontal" }, + + { FadeEffect_VERTICAL_CHECKERBOARD, "checkerboard-down" }, + { FadeEffect_HORIZONTAL_CHECKERBOARD, "checkerboard-across" }, + + { FadeEffect_FADE_TO_CENTER, "box-in" }, + { FadeEffect_FADE_FROM_CENTER, "box-out" }, + + { FadeEffect_VERTICAL_STRIPES, "venetian-blinds-vertical" }, + { FadeEffect_HORIZONTAL_STRIPES, "venetian-blinds-horizontal" }, + + { FadeEffect_MOVE_FROM_LEFT, "cover-right" }, + { FadeEffect_MOVE_FROM_TOP, "cover-down" }, + { FadeEffect_MOVE_FROM_RIGHT, "cover-left" }, + { FadeEffect_MOVE_FROM_BOTTOM, "cover-up" }, + { FadeEffect_MOVE_FROM_UPPERLEFT, "cover-right-down" }, + { FadeEffect_MOVE_FROM_UPPERRIGHT, "cover-left-down" }, + { FadeEffect_MOVE_FROM_LOWERRIGHT, "cover-left-up" }, + { FadeEffect_MOVE_FROM_LOWERLEFT, "cover-right-up" }, + + { FadeEffect_DISSOLVE, "dissolve" }, + + { FadeEffect_RANDOM, "random-transition" }, + + { FadeEffect_ROLL_FROM_LEFT, "push-right" }, + { FadeEffect_ROLL_FROM_TOP, "push-down" }, + { FadeEffect_ROLL_FROM_RIGHT, "push-left" }, + { FadeEffect_ROLL_FROM_BOTTOM, "push-up" }, + + { FadeEffect_CLOSE_VERTICAL, "split-horizontal-in" }, + { FadeEffect_CLOSE_HORIZONTAL, "split-vertical-in" }, + { FadeEffect_OPEN_VERTICAL, "split-horizontal-out" }, + { FadeEffect_OPEN_HORIZONTAL, "split-vertical-out" }, + + { FadeEffect_FADE_FROM_UPPERLEFT, "diagonal-squares-right-down" }, + { FadeEffect_FADE_FROM_UPPERRIGHT, "diagonal-squares-left-down" }, + { FadeEffect_FADE_FROM_LOWERLEFT, "diagonal-squares-right-up" }, + { FadeEffect_FADE_FROM_LOWERRIGHT, "diagonal-squares-left-up" }, + +// OOo 1.x transitions not in OOo 2.x + { FadeEffect_CLOCKWISE, "clock-wipe-twelve" }, + { FadeEffect_COUNTERCLOCKWISE, "reverse-clock-wipe-twelve" }, + { FadeEffect_SPIRALIN_LEFT, "spiral-wipe-top-left-clockwise" }, + { FadeEffect_SPIRALIN_RIGHT, "spiral-wipe-top-right-counter-clockwise" }, + { FadeEffect_SPIRALOUT_LEFT, "spiral-wipe-out-to-bottom-right-clockwise" }, + { FadeEffect_SPIRALOUT_RIGHT, "spiral-wipe-out-to-bottom-left-counter-clockwise" }, + { FadeEffect_WAVYLINE_FROM_LEFT, "snake-wipe-top-left-vertical" }, + { FadeEffect_WAVYLINE_FROM_TOP, "snake-wipe-top-left-horizontal" }, + { FadeEffect_WAVYLINE_FROM_RIGHT, "snake-wipe-bottom-right-vertical" }, + { FadeEffect_WAVYLINE_FROM_BOTTOM, "snake-wipe-bottom-right-horizontal" }, + { FadeEffect_STRETCH_FROM_LEFT, "wipe-right" }, // todo + { FadeEffect_STRETCH_FROM_TOP, "wipe-down" }, // todo + { FadeEffect_STRETCH_FROM_RIGHT, "wipe-left" }, // todo + { FadeEffect_STRETCH_FROM_BOTTOM, "wipe-up" }, // todo + +// OOo 1.x not available transitions + + { FadeEffect_CLOCKWISE, "wheel-clockwise-2-spokes" }, + { FadeEffect_CLOCKWISE, "wheel-clockwise-3-spokes" }, + { FadeEffect_CLOCKWISE, "wheel-clockwise-4-spokes" }, + { FadeEffect_CLOCKWISE, "wheel-clockwise-8-spokes" }, + + { FadeEffect_FADE_FROM_CENTER, "shape-circle" }, + { FadeEffect_FADE_FROM_CENTER, "shape-diamond" }, + { FadeEffect_FADE_FROM_CENTER, "shape-plus" }, + + { FadeEffect_CLOCKWISE, "wedge" }, + + { FadeEffect_DISSOLVE, "fade-through-black" }, + + { FadeEffect_CLOCKWISE, "zoom-rotate-in" }, + + { FadeEffect_HORIZONTAL_LINES, "comb-horizontal" }, + { FadeEffect_VERTICAL_LINES, "comb-vertical" }, + + { FadeEffect_DISSOLVE, "fade-smoothly" }, + + { FadeEffect_NONE, nullptr } +}; + +/* todo +cut cut (same as NONE?) +cut-through-black cut toBlack +wedge wedge +*/ + +void EffectMigration::SetFadeEffect( SdPage* pPage, css::presentation::FadeEffect eNewEffect) +{ + deprecated_FadeEffect_conversion_table_entry const * pEntry = deprecated_FadeEffect_conversion_table; + while( (pEntry->meFadeEffect != FadeEffect_NONE) && (pEntry->meFadeEffect != eNewEffect) ) + pEntry++; + + if( pEntry->mpPresetId ) + { + const OUString aPresetId( OUString::createFromAscii( pEntry->mpPresetId ) ); + + const TransitionPresetList& rPresetList = TransitionPreset::getTransitionPresetList(); + + auto aIt = std::find_if(rPresetList.begin(), rPresetList.end(), + [&aPresetId](const TransitionPresetPtr& rxPreset) { return rxPreset->getPresetId() == aPresetId; }); + if (aIt != rPresetList.end()) + { + pPage->setTransitionType( (*aIt)->getTransition() ); + pPage->setTransitionSubtype( (*aIt)->getSubtype() ); + pPage->setTransitionDirection( (*aIt)->getDirection() ); + pPage->setTransitionFadeColor( (*aIt)->getFadeColor() ); + } + } + else + { + pPage->setTransitionType( 0 ); + pPage->setTransitionSubtype( 0 ); + pPage->setTransitionDirection( false ); + pPage->setTransitionFadeColor( 0 ); + } +} + +FadeEffect EffectMigration::GetFadeEffect( const SdPage* pPage ) +{ + const TransitionPresetList & rPresetList = TransitionPreset::getTransitionPresetList(); + auto aIt = std::find_if(rPresetList.begin(), rPresetList.end(), + [&pPage](const TransitionPresetPtr& rxPreset) { + return (rxPreset->getTransition() == pPage->getTransitionType()) + && (rxPreset->getSubtype() == pPage->getTransitionSubtype()) + && (rxPreset->getDirection() == pPage->getTransitionDirection()) + && (rxPreset->getFadeColor() == pPage->getTransitionFadeColor()); + }); + if (aIt != rPresetList.end()) + { + const OUString& aPresetId = (*aIt)->getPresetId(); + + deprecated_FadeEffect_conversion_table_entry const * pEntry = deprecated_FadeEffect_conversion_table; + while( (pEntry->meFadeEffect != FadeEffect_NONE) && (!aPresetId.equalsAscii( pEntry->mpPresetId ) ) ) + pEntry++; + + return pEntry->meFadeEffect; + } + return FadeEffect_NONE; +} + +namespace { + +struct deprecated_AnimationEffect_conversion_table_entry +{ + AnimationEffect meEffect; + const char* mpPresetId; + const char* mpPresetSubType; +}; + +} + +deprecated_AnimationEffect_conversion_table_entry const deprecated_AnimationEffect_conversion_table[] = +{ +// OOo 1.x entrance effects + { AnimationEffect_APPEAR, "ooo-entrance-appear",nullptr }, + + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-box","in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-box","out" }, + + { AnimationEffect_VERTICAL_CHECKERBOARD, "ooo-entrance-checkerboard","downward" }, + { AnimationEffect_HORIZONTAL_CHECKERBOARD, "ooo-entrance-checkerboard","across" }, + + { AnimationEffect_FADE_FROM_UPPERLEFT, "ooo-entrance-diagonal-squares","right-to-bottom" }, + { AnimationEffect_FADE_FROM_UPPERRIGHT, "ooo-entrance-diagonal-squares","left-to-bottom" }, + { AnimationEffect_FADE_FROM_LOWERLEFT, "ooo-entrance-diagonal-squares","right-to-top" }, + { AnimationEffect_FADE_FROM_LOWERRIGHT, "ooo-entrance-diagonal-squares","left-to-top" }, + + { AnimationEffect_DISSOLVE, "ooo-entrance-dissolve-in",nullptr }, + + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-fly-in","from-left" }, + { AnimationEffect_MOVE_FROM_TOP, "ooo-entrance-fly-in","from-top" }, + { AnimationEffect_MOVE_FROM_RIGHT, "ooo-entrance-fly-in","from-right" }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-fly-in","from-bottom" }, + { AnimationEffect_MOVE_FROM_UPPERLEFT, "ooo-entrance-fly-in","from-top-left" }, + { AnimationEffect_MOVE_FROM_UPPERRIGHT, "ooo-entrance-fly-in","from-top-right" }, + { AnimationEffect_MOVE_FROM_LOWERRIGHT, "ooo-entrance-fly-in","from-bottom-right" }, + { AnimationEffect_MOVE_FROM_LOWERLEFT, "ooo-entrance-fly-in","from-bottom-left" }, + + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-fly-in-slow", "from-bottom" }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-fly-in-slow", "from-left" }, + { AnimationEffect_MOVE_FROM_RIGHT, "ooo-entrance-fly-in-slow", "from-right" }, + { AnimationEffect_MOVE_FROM_TOP, "ooo-entrance-fly-in-slow", "from-top" }, + + { AnimationEffect_MOVE_SHORT_FROM_LEFT, "ooo-entrance-peek-in","from-left" }, + { AnimationEffect_MOVE_SHORT_FROM_TOP, "ooo-entrance-peek-in","from-top" }, + { AnimationEffect_MOVE_SHORT_FROM_RIGHT, "ooo-entrance-peek-in","from-right" }, + { AnimationEffect_MOVE_SHORT_FROM_BOTTOM, "ooo-entrance-peek-in","from-bottom" }, + + { AnimationEffect_VERTICAL_LINES, "ooo-entrance-random-bars","horizontal" }, + { AnimationEffect_HORIZONTAL_LINES, "ooo-entrance-random-bars","vertical" }, + + { AnimationEffect_RANDOM, "ooo-entrance-random",nullptr }, + + { AnimationEffect_CLOSE_VERTICAL, "ooo-entrance-split","horizontal-in" }, + { AnimationEffect_CLOSE_HORIZONTAL, "ooo-entrance-split","vertical-in" }, + { AnimationEffect_OPEN_VERTICAL, "ooo-entrance-split","horizontal-out" }, + { AnimationEffect_OPEN_HORIZONTAL, "ooo-entrance-split","vertical-out" }, + + { AnimationEffect_VERTICAL_STRIPES, "ooo-entrance-venetian-blinds","horizontal" }, + { AnimationEffect_HORIZONTAL_STRIPES, "ooo-entrance-venetian-blinds","vertical" }, + + { AnimationEffect_FADE_FROM_LEFT, "ooo-entrance-wipe","from-left" }, + { AnimationEffect_FADE_FROM_TOP, "ooo-entrance-wipe","from-bottom" }, + { AnimationEffect_FADE_FROM_RIGHT, "ooo-entrance-wipe","from-right" }, + { AnimationEffect_FADE_FROM_BOTTOM, "ooo-entrance-wipe","from-top" }, + + { AnimationEffect_HORIZONTAL_ROTATE, "ooo-entrance-swivel","vertical" }, + { AnimationEffect_VERTICAL_ROTATE, "ooo-entrance-swivel","horizontal" }, + + { AnimationEffect_STRETCH_FROM_LEFT, "ooo-entrance-stretchy","from-left" }, + { AnimationEffect_STRETCH_FROM_UPPERLEFT, "ooo-entrance-stretchy","from-top-left" }, + { AnimationEffect_STRETCH_FROM_TOP, "ooo-entrance-stretchy","from-top" }, + { AnimationEffect_STRETCH_FROM_UPPERRIGHT, "ooo-entrance-stretchy","from-top-right" }, + { AnimationEffect_STRETCH_FROM_RIGHT, "ooo-entrance-stretchy","from-right" }, + { AnimationEffect_STRETCH_FROM_LOWERRIGHT, "ooo-entrance-stretchy","from-bottom-right" }, + { AnimationEffect_STRETCH_FROM_BOTTOM, "ooo-entrance-stretchy","from-bottom" }, + { AnimationEffect_STRETCH_FROM_LOWERLEFT, "ooo-entrance-stretchy","from-bottom-left" }, + + { AnimationEffect_HORIZONTAL_STRETCH, "ooo-entrance-expand", nullptr }, + + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel","1" }, + { AnimationEffect_COUNTERCLOCKWISE, "ooo-entrance-clock-wipe","counter-clockwise" }, + + { AnimationEffect_SPIRALIN_LEFT, "ooo-entrance-spiral-wipe", "from-top-left-clockwise" }, + { AnimationEffect_SPIRALIN_RIGHT, "ooo-entrance-spiral-wipe", "from-top-right-counter-clockwise" }, + { AnimationEffect_SPIRALOUT_LEFT, "ooo-entrance-spiral-wipe", "from-center-clockwise" }, + { AnimationEffect_SPIRALOUT_RIGHT, "ooo-entrance-spiral-wipe", "from-center-counter-clockwise" }, + + { AnimationEffect_WAVYLINE_FROM_LEFT, "ooo-entrance-snake-wipe","from-top-left-vertical" }, + { AnimationEffect_WAVYLINE_FROM_TOP, "ooo-entrance-snake-wipe","from-top-left-horizontal" }, + { AnimationEffect_WAVYLINE_FROM_RIGHT, "ooo-entrance-snake-wipe","from-bottom-right-vertical" }, + { AnimationEffect_WAVYLINE_FROM_BOTTOM, "ooo-entrance-snake-wipe","from-bottom-right-horizontal" }, + +// ooo 1.x exit effects + { AnimationEffect_HIDE, "ooo-exit-disappear",nullptr }, + { AnimationEffect_MOVE_TO_LEFT, "ooo-exit-fly-out", "from-right" }, + { AnimationEffect_MOVE_TO_TOP, "ooo-exit-fly-out", "from-bottom" }, + { AnimationEffect_MOVE_TO_RIGHT, "ooo-exit-fly-out", "from-left" }, + { AnimationEffect_MOVE_TO_BOTTOM, "ooo-exit-fly-out", "from-top" }, + { AnimationEffect_MOVE_TO_UPPERLEFT, "ooo-exit-fly-out", "from-top-right" }, + { AnimationEffect_MOVE_TO_UPPERRIGHT, "ooo-exit-fly-out", "from-top-left" }, + { AnimationEffect_MOVE_TO_LOWERRIGHT, "ooo-exit-fly-out", "from-bottom-left" }, + { AnimationEffect_MOVE_TO_LOWERLEFT, "ooo-exit-fly-out", "from-bottom-right" }, + { AnimationEffect_MOVE_SHORT_TO_LEFT, "ooo-exit-peek-out", "from-right" }, + { AnimationEffect_MOVE_SHORT_TO_UPPERLEFT, "ooo-exit-peek-out", "from-right" }, + { AnimationEffect_MOVE_SHORT_TO_TOP, "ooo-exit-peek-out", "from-bottom" }, + { AnimationEffect_MOVE_SHORT_TO_UPPERRIGHT, "ooo-exit-peek-out", "from-bottom" }, + { AnimationEffect_MOVE_SHORT_TO_RIGHT, "ooo-exit-peek-out", "from-left" }, + { AnimationEffect_MOVE_SHORT_TO_LOWERRIGHT, "ooo-exit-peek-out","from-left" }, + { AnimationEffect_MOVE_SHORT_TO_BOTTOM, "ooo-exit-peek-out", "from-top" }, + { AnimationEffect_MOVE_SHORT_TO_LOWERLEFT, "ooo-exit-peek-out", "from-top" }, + +// no matching in OOo 2.x + { AnimationEffect_MOVE_SHORT_FROM_UPPERLEFT, "ooo-entrance-peek-in","from-left" }, + { AnimationEffect_MOVE_SHORT_FROM_UPPERRIGHT, "ooo-entrance-peek-in","from-top" }, + { AnimationEffect_MOVE_SHORT_FROM_LOWERRIGHT, "ooo-entrance-peek-in","from-right" }, + { AnimationEffect_MOVE_SHORT_FROM_LOWERLEFT, "ooo-entrance-peek-in","from-bottom" }, + { AnimationEffect_LASER_FROM_LEFT, "ooo-entrance-fly-in","from-left" }, + { AnimationEffect_LASER_FROM_TOP, "ooo-entrance-fly-in","from-top" }, + { AnimationEffect_LASER_FROM_RIGHT, "ooo-entrance-fly-in","from-right" }, + { AnimationEffect_LASER_FROM_BOTTOM, "ooo-entrance-fly-in","from-bottom" }, + { AnimationEffect_LASER_FROM_UPPERLEFT, "ooo-entrance-fly-in","from-top-left" }, + { AnimationEffect_LASER_FROM_UPPERRIGHT, "ooo-entrance-fly-in","from-top-right" }, + { AnimationEffect_LASER_FROM_LOWERLEFT, "ooo-entrance-fly-in","from-bottom-left" }, + { AnimationEffect_LASER_FROM_LOWERRIGHT, "ooo-entrance-fly-in","from-bottom-right" }, + +// no matching in OOo 1.x + + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-circle", "in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-circle", "out" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-diamond", "in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-diamond", "out" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-plus", "in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-plus", "out" }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wedge", nullptr }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel", "2" }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel", "3" }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel", "4" }, + { AnimationEffect_CLOCKWISE, "ooo-entrance-wheel", "8" }, + + { AnimationEffect_MOVE_FROM_RIGHT, "ooo-entrance-boomerang", nullptr }, + { AnimationEffect_MOVE_FROM_UPPERRIGHT, "ooo-entrance-bounce", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-curve-up", nullptr }, + { AnimationEffect_MOVE_FROM_TOP, "ooo-entrance-float", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-glide", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-magnify", nullptr }, + { AnimationEffect_HORIZONTAL_ROTATE, "ooo-entrance-pinwheel", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-breaks", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-sling", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-spiral-in", nullptr }, + { AnimationEffect_MOVE_FROM_LEFT, "ooo-entrance-thread", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-ascend", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-center-revolve", nullptr }, + { AnimationEffect_APPEAR, "ooo-entrance-compress", nullptr }, + { AnimationEffect_MOVE_SHORT_FROM_TOP, "ooo-entrance-descend", nullptr }, + { AnimationEffect_MOVE_SHORT_FROM_LEFT, "ooo-entrance-ease-in", nullptr }, + { AnimationEffect_MOVE_FROM_BOTTOM, "ooo-entrance-rise-up", nullptr }, + { AnimationEffect_HORIZONTAL_ROTATE, "ooo-entrance-spin-in", nullptr }, + { AnimationEffect_STRETCH_FROM_LEFT, "ooo-entrance-stretchy", "across" }, + { AnimationEffect_STRETCH_FROM_TOP, "ooo-entrance-stretchy", "downward" }, + + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-zoom","in" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-zoom","in-slightly" }, + { AnimationEffect_FADE_FROM_CENTER, "ooo-entrance-zoom","in-from-screen-center" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-zoom","out" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-zoom","out-slightly" }, + { AnimationEffect_FADE_TO_CENTER, "ooo-entrance-zoom","out-from-screen-center" }, + + { AnimationEffect_DISSOLVE, "ooo-entrance-fade-in", nullptr }, + { AnimationEffect_DISSOLVE, "ooo-entrance-fade-in-and-zoom", nullptr }, + { AnimationEffect_DISSOLVE, "ooo-entrance-fade-in-and-swivel", nullptr }, + + // still open (no matching effect: AnimationEffect_ZOOM_IN_FROM_*, + // AnimationEffect_ZOOM_OUT_FROM_*, AnimationEffect_PATH + + { AnimationEffect_NONE, nullptr, nullptr } +}; + +static EffectSequence::iterator ImplFindEffect( MainSequencePtr const & pMainSequence, const Reference< XShape >& rShape, sal_Int16 nSubItem ) +{ + return std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&rShape, &nSubItem](const CustomAnimationEffectPtr& pEffect) { + return (pEffect->getTargetShape() == rShape) + && (pEffect->getTargetSubItem() == nSubItem); + }); +} + +static bool implIsInsideGroup( SdrObject const * pObj ) +{ + // TTTT for current state of transition, SdrObject has a parent* + // to a SdrObjList. That may be a SdrPage or a SdrObjGroup, both + // are already derived from SdrObjList. To finally check, use + // the method 'getSdrObjectFromSdrObjList' - if it's not a SdrPage, + // it will return SdrObjGroup or E3dScene -> SdrObject. + // For future states, test for SdrObject. Trying to get the SdrPage + // will in the future depend on the Object(this) to be inserted to a + // SdrPage, regardless of e.g. being a group member. + if(nullptr == pObj) + { + return false; + } + + SdrObjList* pSdrObjList(pObj->getParentSdrObjListFromSdrObject()); + + if(nullptr == pSdrObjList) + { + return false; + } + + return (nullptr != pSdrObjList->getSdrObjectFromSdrObjList()); +} + +void EffectMigration::SetAnimationEffect( SvxShape* pShape, AnimationEffect eEffect ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + OUString aPresetId; + OUString aPresetSubType; + + if( !ConvertAnimationEffect( eEffect, aPresetId, aPresetSubType ) ) + { + OSL_FAIL( "sd::EffectMigration::SetAnimationEffect(), no mapping for given AnimationEffect value" ); + return; + } + + const CustomAnimationPresets& rPresets = CustomAnimationPresets::getCustomAnimationPresets(); + + CustomAnimationPresetPtr pPreset( rPresets.getEffectDescriptor( aPresetId ) ); + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + if( !(pPreset && pMainSequence) ) + return; + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIterOnlyBackground( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_BACKGROUND ) ); + EffectSequence::iterator aIterAsWhole( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::AS_WHOLE ) ); + const EffectSequence::iterator aEnd( pMainSequence->getEnd() ); + + if( (aIterOnlyBackground == aEnd) && (aIterAsWhole == aEnd) ) + { + bool bEffectCreated = false; + + // check if there is already a text effect for this shape + EffectSequence::iterator aIterOnlyText( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_TEXT ) ); + if( aIterOnlyText != aEnd ) + { + // check if this is an animation text group + sal_Int32 nGroupId = (*aIterOnlyText)->getGroupId(); + if( nGroupId >= 0 ) + { + CustomAnimationTextGroupPtr pGroup = pMainSequence->findGroup( nGroupId ); + if( pGroup ) + { + // add an effect to animate the shape + pMainSequence->setAnimateForm( pGroup, true ); + + // find this effect + EffectSequence::iterator aIter( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_BACKGROUND ) ); + + if( aIter != aEnd ) + { + if( ((*aIter)->getPresetId() != aPresetId) || + ((*aIter)->getPresetSubType() != aPresetSubType) ) + { + (*aIter)->replaceNode( pPreset->create( aPresetSubType ) ); + pMainSequence->rebuild(); + bEffectCreated = true; + } + } + } + } + } + + if( !bEffectCreated ) + { + // if there is not yet an effect that target this shape, we generate one + // we insert the shape effect before it + Reference< XAnimationNode > xNode( pPreset->create( aPresetSubType ) ); + DBG_ASSERT( xNode.is(), "EffectMigration::SetAnimationEffect(), could not create preset!" ); + if( xNode.is() ) + { + CustomAnimationEffectPtr pEffect = std::make_shared( xNode ); + pEffect->setTarget( Any( xShape ) ); + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + const bool bManual = (pPage == nullptr) || (pPage->GetPresChange() == PresChange::Manual); + if( !bManual ) + pEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + + pMainSequence->append( pEffect ); + + if( ( pObj->GetObjInventor() == SdrInventor::Default ) && ( pObj->GetObjIdentifier() == SdrObjKind::OutlineText ) ) + { + // special case for outline text, effects are always mapped to text group effect + pMainSequence-> + createTextGroup( pEffect, 10, bManual ? -1 : 0.0, false, false ); + } + } + } + } + else + { + // if there is already an effect targeting this shape + // just replace it + CustomAnimationEffectPtr pEffect; + if( aIterAsWhole != aEnd ) + { + pEffect = *aIterAsWhole; + } + else + { + pEffect = *aIterOnlyBackground; + } + + if( pEffect ) + { + if( (pEffect->getPresetId() != aPresetId) || + (pEffect->getPresetSubType() != aPresetSubType) ) + { + pMainSequence->replace( pEffect, pPreset, aPresetSubType, -1.0 ); + } + } + } +} + +AnimationEffect EffectMigration::GetAnimationEffect( SvxShape* pShape ) +{ + OUString aPresetId; + OUString aPresetSubType; + + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + if( pMainSequence ) + { + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { + return (pEffect->getTargetShape() == xShape) + && ((pEffect->getTargetSubItem() == ShapeAnimationSubType::ONLY_BACKGROUND) + || (pEffect->getTargetSubItem() == ShapeAnimationSubType::AS_WHOLE)) + && (pEffect->getDuration() != 0.1); // ignore appear effects created from old text effect import + }); + + if (aIter != pMainSequence->getEnd()) + { + aPresetId = (*aIter)->getPresetId(); + aPresetSubType = (*aIter)->getPresetSubType(); + } + } + + // now find old effect + AnimationEffect eEffect = AnimationEffect_NONE; + + if( !ConvertPreset( aPresetId, &aPresetSubType, eEffect ) ) + ConvertPreset( aPresetId, nullptr, eEffect ); + + return eEffect; +} + +void EffectMigration::SetTextAnimationEffect( SvxShape* pShape, AnimationEffect eEffect ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + // first map the deprecated AnimationEffect to a preset and subtype + OUString aPresetId; + OUString aPresetSubType; + + if( !ConvertAnimationEffect( eEffect, aPresetId, aPresetSubType ) ) + { + OSL_FAIL( "sd::EffectMigration::SetAnimationEffect(), no mapping for given AnimationEffect value" ); + return; + } + + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + + // ignore old text effects on shape without text + if( (pTextObj == nullptr) || (!pTextObj->HasText()) ) + return; + + const CustomAnimationPresets& rPresets = CustomAnimationPresets::getCustomAnimationPresets(); + + // create an effect from this preset + CustomAnimationPresetPtr pPreset( rPresets.getEffectDescriptor( aPresetId ) ); + + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + if( !(pPreset && pMainSequence) ) + return; + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIterOnlyText( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_TEXT ) ); + const EffectSequence::iterator aEnd( pMainSequence->getEnd() ); + + CustomAnimationTextGroupPtr pGroup; + + // is there already an animation text group for this shape? + if( aIterOnlyText != aEnd ) + { + const sal_Int32 nGroupId = (*aIterOnlyText)->getGroupId(); + if( nGroupId >= 0 ) + pGroup = pMainSequence->findGroup( nGroupId ); + } + + // if there is not yet a group, create it + if( !pGroup ) + { + CustomAnimationEffectPtr pShapeEffect; + + EffectSequence::iterator aIterOnlyBackground( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_BACKGROUND ) ); + if( aIterOnlyBackground != aEnd ) + { + pShapeEffect = *aIterOnlyBackground; + } + else + { + EffectSequence::iterator aIterAsWhole( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::AS_WHOLE ) ); + if( aIterAsWhole != aEnd ) + { + pShapeEffect = *aIterAsWhole; + } + else + { + Reference< XAnimationNode > xNode( pPreset->create( "" ) ); + DBG_ASSERT( xNode.is(), "EffectMigration::SetTextAnimationEffect(), could not create preset!" ); + if( xNode.is() ) + { + pShapeEffect = std::make_shared( xNode ); + pShapeEffect->setTarget( Any( xShape ) ); + pShapeEffect->setDuration( 0.1 ); + pMainSequence->append( pShapeEffect ); + + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + if( pPage && pPage->GetPresChange() != PresChange::Manual ) + pShapeEffect->setNodeType( EffectNodeType::AFTER_PREVIOUS ); + } + } + } + + if( pShapeEffect ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + const bool bManual = (pPage == nullptr) || (pPage->GetPresChange() == PresChange::Manual); + + // now create effects for each paragraph + pGroup = + pMainSequence-> + createTextGroup( pShapeEffect, 10, bManual ? -1 : 0.0, true, false ); + } + } + + if( pGroup ) + { + const bool bLaserEffect = (eEffect >= AnimationEffect_LASER_FROM_LEFT) && (eEffect <= AnimationEffect_LASER_FROM_LOWERRIGHT); + + // now we have a group, so check if all effects are same as we like to have them + const EffectSequence& rEffects = pGroup->getEffects(); + + for( auto& rxEffect : rEffects ) + { + // only work on paragraph targets + if( rxEffect->getTarget().getValueType() == ::cppu::UnoType::get() ) + { + if( (rxEffect->getPresetId() != aPresetId) || + (rxEffect->getPresetSubType() != aPresetSubType) ) + { + rxEffect->replaceNode( pPreset->create( aPresetSubType ) ); + } + + if( bLaserEffect ) + { + rxEffect->setIterateType( TextAnimationType::BY_LETTER ); + rxEffect->setIterateInterval( 0.5 );// TODO: + // Determine + // interval + // according + // to + // total + // effect + // duration + } + } + } + } + pMainSequence->rebuild(); +} + +AnimationEffect EffectMigration::GetTextAnimationEffect( SvxShape* pShape ) +{ + OUString aPresetId; + OUString aPresetSubType; + + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj ) + { + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + if( pMainSequence ) + { + const Reference< XShape > xShape( pShape ); + EffectSequence::iterator aIter( ImplFindEffect( pMainSequence, xShape, ShapeAnimationSubType::ONLY_TEXT ) ); + if( aIter != pMainSequence->getEnd() ) + { + aPresetId = (*aIter)->getPresetId(); + aPresetSubType = (*aIter)->getPresetSubType(); + } + } + } + + // now find old effect + AnimationEffect eEffect = AnimationEffect_NONE; + + if( !ConvertPreset( aPresetId, &aPresetSubType, eEffect ) ) + ConvertPreset( aPresetId, nullptr, eEffect ); + + return eEffect; +} + +bool EffectMigration::ConvertPreset( std::u16string_view rPresetId, const OUString* pPresetSubType, AnimationEffect& rEffect ) +{ + rEffect = AnimationEffect_NONE; + if( !rPresetId.empty() ) + { + // first try a match for preset id and subtype + deprecated_AnimationEffect_conversion_table_entry const * p = deprecated_AnimationEffect_conversion_table; + while( p->mpPresetId ) + { + if( o3tl::equalsAscii( rPresetId, p->mpPresetId ) && + (( p->mpPresetSubType == nullptr ) || + ( pPresetSubType == nullptr) || + ( pPresetSubType->equalsAscii( p->mpPresetSubType )) ) ) + { + rEffect = p->meEffect; + return true; + } + p++; + } + return false; + } + else + { + // empty preset id means AnimationEffect_NONE + return true; + } +} + +bool EffectMigration::ConvertAnimationEffect( const AnimationEffect& rEffect, OUString& rPresetId, OUString& rPresetSubType ) +{ + deprecated_AnimationEffect_conversion_table_entry const * p = deprecated_AnimationEffect_conversion_table; + while( p->mpPresetId ) + { + if( p->meEffect == rEffect ) + { + rPresetId = OUString::createFromAscii( p->mpPresetId ); + rPresetSubType = OUString::createFromAscii( p->mpPresetSubType ); + return true; + } + p++; + } + + return false; +} + +double EffectMigration::ConvertAnimationSpeed( AnimationSpeed eSpeed ) +{ + double fDuration; + switch( eSpeed ) + { + case AnimationSpeed_SLOW: fDuration = 2.0; break; + case AnimationSpeed_FAST: fDuration = 0.5; break; + default: + fDuration = 1.0; break; + } + return fDuration; +} + +void EffectMigration::SetAnimationSpeed( SvxShape* pShape, AnimationSpeed eSpeed ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + double fDuration = ConvertAnimationSpeed( eSpeed ); + + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + if( pEffect->getDuration() != 0.1 ) + pEffect->setDuration( fDuration ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +AnimationSpeed EffectMigration::GetAnimationSpeed( SvxShape* pShape ) +{ + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + double fDuration = 1.0; + + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { + return (pEffect->getTargetShape() == xShape) + && (pEffect->getDuration() != 0.1); + }); + if (aIter != pMainSequence->getEnd()) + { + CustomAnimationEffectPtr pEffect( *aIter ); + fDuration = pEffect->getDuration(); + } + + return ConvertDuration( fDuration ); +} + +AnimationSpeed EffectMigration::ConvertDuration( double fDuration ) +{ + AnimationSpeed eSpeed; + + if( fDuration < 1.0 ) + eSpeed = AnimationSpeed_FAST; + else if( fDuration > 1.5 ) + eSpeed = AnimationSpeed_SLOW; + else + eSpeed = AnimationSpeed_MEDIUM; + + return eSpeed; +} + +void EffectMigration::SetDimColor( SvxShape* pShape, sal_Int32 nColor ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + pEffect->setHasAfterEffect( true ); + pEffect->setDimColor( Any( nColor ) ); + pEffect->setAfterEffectOnNext( true ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +sal_Int32 EffectMigration::GetDimColor( SvxShape* pShape ) +{ + sal_Int32 nColor = 0; + if( pShape ) + { + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { + return (pEffect->getTargetShape() == xShape) + && pEffect->getDimColor().hasValue() + && pEffect->hasAfterEffect(); + }); + if (aIter != pMainSequence->getEnd()) + { + CustomAnimationEffectPtr pEffect( *aIter ); + pEffect->getDimColor() >>= nColor; + } + } + } + + return nColor; +} + +void EffectMigration::SetDimHide( SvxShape* pShape, bool bDimHide ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + pEffect->setHasAfterEffect( bDimHide ); + if( bDimHide ) { + Any aEmpty; + pEffect->setDimColor( aEmpty ); + } + pEffect->setAfterEffectOnNext( false ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +bool EffectMigration::GetDimHide( SvxShape* pShape ) +{ + bool bRet = false; + if( pShape ) + { + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { return pEffect->getTargetShape() == xShape; }); + if (aIter != pMainSequence->getEnd()) + { + CustomAnimationEffectPtr pEffect( *aIter ); + bRet = pEffect->hasAfterEffect() && + !pEffect->getDimColor().hasValue() && + (!pEffect->IsAfterEffectOnNext()); + } + } + } + + return bRet; +} + +void EffectMigration::SetDimPrevious( SvxShape* pShape, bool bDimPrevious ) +{ + DBG_ASSERT( pShape && pShape->GetSdrObject() && pShape->GetSdrObject()->getSdrPageFromSdrObject(), + "sd::EffectMigration::SetAnimationEffect(), invalid argument!" ); + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + if( implIsInsideGroup( pObj ) ) + return; + + Any aColor; + + if( bDimPrevious ) + aColor <<= COL_LIGHTGRAY; + + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + pEffect->setHasAfterEffect( bDimPrevious ); + if( !bDimPrevious || !pEffect->getDimColor().hasValue() ) + pEffect->setDimColor( aColor ); + pEffect->setAfterEffectOnNext( true ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +bool EffectMigration::GetDimPrevious( SvxShape* pShape ) +{ + bool bRet = false; + if( pShape ) + { + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter = std::find_if(pMainSequence->getBegin(), pMainSequence->getEnd(), + [&xShape](const CustomAnimationEffectPtr& pEffect) { return pEffect->getTargetShape() == xShape; }); + if (aIter != pMainSequence->getEnd()) + { + CustomAnimationEffectPtr pEffect( *aIter ); + bRet = pEffect->hasAfterEffect() && + pEffect->getDimColor().hasValue() && + pEffect->IsAfterEffectOnNext(); + } + } + } + + return bRet; +} + +void EffectMigration::SetPresentationOrder( SvxShape* pShape, sal_Int32 nNewPos ) +{ + if( !pShape || !pShape->GetSdrObject() || !pShape->GetSdrObject()->getSdrPageFromSdrObject() ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + EffectSequence& rSequence = pMainSequence->getSequence(); + sal_Int32 nPos; + sal_Int32 nCurrentPos = -1; + std::vector< std::vector< EffectSequence::iterator > > aEffectVector(1); + + if( !rSequence.empty() ) + { + Reference< XShape > xThis( pShape ); + Reference< XShape > xCurrent; + + EffectSequence::iterator aIter( rSequence.begin() ); + EffectSequence::iterator aEnd( rSequence.end() ); + for( nPos = 0; aIter != aEnd; ++aIter ) + { + CustomAnimationEffectPtr pEffect = *aIter; + + if( !xCurrent.is() ) + { + xCurrent = pEffect->getTargetShape(); + } + else if( pEffect->getTargetShape() != xCurrent ) + { + nPos++; + xCurrent = pEffect->getTargetShape(); + aEffectVector.resize( nPos+1 ); + } + + // is this the first effect for xThis shape? + if(( nCurrentPos == -1 ) && ( xCurrent == xThis ) ) + { + nCurrentPos = nPos; + } + + aEffectVector[nPos].push_back( aIter ); + } + } + + // check if there is at least one effect for xThis + if( nCurrentPos == -1 ) + { + OSL_FAIL("sd::EffectMigration::SetPresentationOrder() failed cause this shape has no effect" ); + return; + } + + // check trivial case + if( nCurrentPos == nNewPos ) + return; + + std::vector< CustomAnimationEffectPtr > aEffects; + + for( const auto& rIter : aEffectVector[nCurrentPos] ) + { + aEffects.push_back( *rIter ); + rSequence.erase( rIter ); + } + + if( nNewPos > nCurrentPos ) + nNewPos++; + + if( nNewPos == static_cast(aEffectVector.size()) ) + { + rSequence.insert( rSequence.end(), aEffects.begin(), aEffects.end() ); + } + else + { + EffectSequence::iterator aPos( aEffectVector[nNewPos][0] ); + for( const auto& rEffect : aEffects ) + { + rSequence.insert( aPos, rEffect ); + } + } +} + +/** Returns the position of the given SdrObject in the Presentation order. + * This function returns -1 if the SdrObject is not in the Presentation order + * or if it's the path-object. + */ +sal_Int32 EffectMigration::GetPresentationOrder( SvxShape* pShape ) +{ + sal_Int32 nPos = -1, nFound = -1; + + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + EffectSequence& rSequence = pMainSequence->getSequence(); + + Reference< XShape > xThis( pShape ); + Reference< XShape > xCurrent; + + for( const CustomAnimationEffectPtr& pEffect : rSequence ) + { + if( !xCurrent.is() || pEffect->getTargetShape() != xCurrent ) + { + nPos++; + xCurrent = pEffect->getTargetShape(); + + // is this the first effect for xThis shape? + if( xCurrent == xThis ) + { + nFound = nPos; + break; + } + } + } + + return nFound; +} + +void EffectMigration::UpdateSoundEffect( SvxShape* pShape, SdAnimationInfo const * pInfo ) +{ + if( !pInfo ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + bool bNeedRebuild = false; + + OUString aSoundFile; + if( pInfo->mbSoundOn ) + aSoundFile = pInfo->maSoundFile; + + for( aIter = pMainSequence->getBegin(); aIter != pMainSequence->getEnd(); ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + if( !aSoundFile.isEmpty() ) + { + pEffect->createAudio( Any( aSoundFile ) ); + } + else + { + pEffect->removeAudio(); + } + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + pMainSequence->rebuild(); +} + +OUString EffectMigration::GetSoundFile( SvxShape* pShape ) +{ + OUString aSoundFile; + + if( pShape ) + { + SdrObject* pObj = pShape->GetSdrObject(); + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + + const Reference< XShape > xShape( pShape ); + + EffectSequence::iterator aIter; + + for( aIter = pMainSequence->getBegin(); + (aSoundFile.isEmpty()) && (aIter != pMainSequence->getEnd()); + ++aIter ) + { + CustomAnimationEffectPtr pEffect( *aIter ); + if( pEffect->getTargetShape() == xShape ) + { + if( pEffect->getAudio().is() ) + pEffect->getAudio()->getSource() >>= aSoundFile; + } + } + } + } + return aSoundFile; +} + +bool EffectMigration::GetSoundOn( SvxShape* pShape ) +{ + return !GetSoundFile( pShape ).isEmpty(); +} + +void EffectMigration::SetAnimationPath( SvxShape* pShape, SdrPathObj const * pPathObj ) +{ + if( !(pShape && pPathObj) ) + return; + + SdrObject* pObj = pShape->GetSdrObject(); + + if( pObj ) + { + const Reference< XShape > xShape( pShape ); + SdPage* pPage = dynamic_cast< SdPage* >(pPathObj->getSdrPageFromSdrObject()); + if( pPage ) + { + std::shared_ptr< sd::MainSequence > pMainSequence( pPage->getMainSequence() ); + if( pMainSequence ) + pMainSequence->append( *pPathObj, Any( xShape ), -1.0, "" ); + } + } +} + +// #i42894# helper which creates the needed XAnimate for changing visibility and all the (currently) needed embeddings +static void createVisibilityOnOffNode(Reference< XTimeContainer > const & rxParentContainer, SdrObject& rCandidate, bool bVisible, bool bOnClick, double fDuration) +{ + Reference< XMultiServiceFactory > xMsf(::comphelper::getProcessServiceFactory()); + + // create par container node + Reference< XAnimationNode > xOuterSeqTimeContainer(xMsf->createInstance("com.sun.star.animations.ParallelTimeContainer"), UNO_QUERY_THROW); + + // set begin + xOuterSeqTimeContainer->setBegin(Any(0.0)); + + // set fill + xOuterSeqTimeContainer->setFill(AnimationFill::HOLD); + + // set named values + Sequence< NamedValue > aUserDataSequence{ + { /* Name */ "node-type", + /* Value */ Any(bOnClick ? EffectNodeType::ON_CLICK : EffectNodeType::AFTER_PREVIOUS) } + }; + + xOuterSeqTimeContainer->setUserData(aUserDataSequence); + + // create animate set to change visibility for rCandidate + Reference< XAnimationNode > xAnimateSetForLast(xMsf->createInstance("com.sun.star.animations.AnimateSet"), UNO_QUERY_THROW); + + // set begin + xAnimateSetForLast->setBegin(Any(0.0)); + + // set duration + xAnimateSetForLast->setDuration(Any(fDuration)); + + // set fill + xAnimateSetForLast->setFill(AnimationFill::HOLD); + + // set target + Reference< XAnimate > xAnimate(xAnimateSetForLast, UNO_QUERY); + Reference< XShape > xTargetShape(rCandidate.getUnoShape(), UNO_QUERY); + xAnimate->setTarget(Any(xTargetShape)); + + // set AttributeName + xAnimate->setAttributeName("Visibility"); + + // set attribute value + xAnimate->setTo(Any(bVisible)); + + // ad set node to par node + Reference< XTimeContainer > xParentContainer(xOuterSeqTimeContainer, UNO_QUERY_THROW); + xParentContainer->appendChild(xAnimateSetForLast); + + // add node + rxParentContainer->appendChild(xOuterSeqTimeContainer); +} + +// #i42894# older native formats supported animated group objects, that means all members of the group +// were shown animated by showing one after the other. This is no longer supported, but the following +// fallback will create the needed SMIL animation stuff. Unfortunately the members of the group +// have to be moved directly to the page, else the (explained to be generic, thus I expected this to +// work) animations will not work in slideshow +void EffectMigration::CreateAnimatedGroup(SdrObjGroup const & rGroupObj, SdPage& rPage) +{ + // aw080 will give a vector immediately + SdrObjListIter aIter(rGroupObj); + + if(!aIter.Count()) + return; + + std::shared_ptr< sd::MainSequence > pMainSequence(rPage.getMainSequence()); + + if(!pMainSequence) + return; + + std::vector< SdrObject* > aObjects; + aObjects.reserve(aIter.Count()); + + while(aIter.IsMore()) + { + // do move to page rough with old/current stuff, will be different in aw080 anyways + SdrObject* pCandidate = aIter.Next(); + rGroupObj.GetSubList()->NbcRemoveObject(pCandidate->GetOrdNum()); + rPage.NbcInsertObject(pCandidate); + aObjects.push_back(pCandidate); + } + + // create main node + Reference< XMultiServiceFactory > xMsf(::comphelper::getProcessServiceFactory()); + Reference< XAnimationNode > xOuterSeqTimeContainer(xMsf->createInstance("com.sun.star.animations.ParallelTimeContainer"), UNO_QUERY_THROW); + + // set begin + xOuterSeqTimeContainer->setBegin(Any(0.0)); + + // prepare parent container + Reference< XTimeContainer > xParentContainer(xOuterSeqTimeContainer, UNO_QUERY_THROW); + + // prepare loop over objects + SdrObject* pNext = nullptr; + const double fDurationShow(0.2); + const double fDurationHide(0.001); + + for(size_t a(0); a < aObjects.size(); a++) + { + SdrObject* pLast = pNext; + pNext = aObjects[a]; + + // create node + if(pLast) + { + createVisibilityOnOffNode(xParentContainer, *pLast, false, false, fDurationHide); + } + + if(pNext) + { + createVisibilityOnOffNode(xParentContainer, *pNext, true, !a, fDurationShow); + } + } + + // create end node + if(pNext) + { + createVisibilityOnOffNode(xParentContainer, *pNext, false, false, fDurationHide); + } + + // add to main sequence and rebuild + pMainSequence->createEffects(xOuterSeqTimeContainer); + pMainSequence->rebuild(); +} + +void EffectMigration::DocumentLoaded(SdDrawDocument & rDoc) +{ + if (DocumentType::Draw == rDoc.GetDocumentType()) + return; // no animations in Draw + for (sal_uInt16 n = 0; n < rDoc.GetSdPageCount(PageKind::Standard); ++n) + { + SdPage *const pPage = rDoc.GetSdPage(n, PageKind::Standard); + if (pPage->hasAnimationNode()) + { + // this will force the equivalent of the MainSequence::onTimerHdl + // so that the animations are present in export-able representation + // *before* the import is finished + pPage->getMainSequence()->getRootNode(); + } + } + for (sal_uInt16 n = 0; n < rDoc.GetMasterSdPageCount(PageKind::Standard); ++n) + { + SdPage *const pPage = rDoc.GetMasterSdPage(n, PageKind::Standard); + if (pPage->hasAnimationNode()) + { + pPage->getMainSequence()->getRootNode(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/PageListWatcher.cxx b/sd/source/core/PageListWatcher.cxx new file mode 100644 index 000000000..c3d8846fc --- /dev/null +++ b/sd/source/core/PageListWatcher.cxx @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PageListWatcher.hxx" + +#include +#include +#include +#include + +void ImpPageListWatcher::ImpRecreateSortedPageListOnDemand() +{ + // clear vectors + maPageVectorStandard.clear(); + maPageVectorNotes.clear(); + mpHandoutPage = nullptr; + + // build up vectors again + const sal_uInt32 nPageCount(ImpGetPageCount()); + + for(sal_uInt32 a(0); a < nPageCount; a++) + { + SdPage* pCandidate = ImpGetPage(a); + DBG_ASSERT(pCandidate, "ImpPageListWatcher::ImpRecreateSortedPageListOnDemand: Invalid PageList in Model (!)"); + + switch(pCandidate->GetPageKind()) + { + case PageKind::Standard: + { + maPageVectorStandard.push_back(pCandidate); + break; + } + case PageKind::Notes: + { + maPageVectorNotes.push_back(pCandidate); + break; + } + case PageKind::Handout: + { + DBG_ASSERT(!mpHandoutPage, "ImpPageListWatcher::ImpRecreateSortedPageListOnDemand: Two Handout pages in PageList of Model (!)"); + mpHandoutPage = pCandidate; + break; + } + } + } + + // set to valid + mbPageListValid = true; +} + +ImpPageListWatcher::ImpPageListWatcher(const SdrModel& rModel) + : mrModel(rModel) + , mpHandoutPage(nullptr) + , mbPageListValid(false) +{ +} + +ImpPageListWatcher::~ImpPageListWatcher() +{ +} + +SdPage* ImpPageListWatcher::GetSdPage(PageKind ePgKind, sal_uInt32 nPgNum) +{ + SdPage* pRetval(nullptr); + + if(!mbPageListValid) + { + ImpRecreateSortedPageListOnDemand(); + } + + switch(ePgKind) + { + case PageKind::Standard: + { + if( nPgNum < static_cast(maPageVectorStandard.size()) ) + pRetval = maPageVectorStandard[nPgNum]; + else + { + SAL_INFO( "sd.core", + "ImpPageListWatcher::GetSdPage(PageKind::Standard): page number " << nPgNum << " >= " << maPageVectorStandard.size() ); + } + break; + } + case PageKind::Notes: + { + if( nPgNum < static_cast(maPageVectorNotes.size()) ) + pRetval = maPageVectorNotes[nPgNum]; + else + { + SAL_INFO( "sd.core", + "ImpPageListWatcher::GetSdPage(PageKind::Notes): page number " << nPgNum << " >= " << maPageVectorNotes.size() ); + } + break; + } + case PageKind::Handout: + { + // #11420# for models used to transfer drawing shapes via clipboard it's ok to not have a handout page + DBG_ASSERT(nPgNum == 0, "ImpPageListWatcher::GetSdPage: access to non existing handout page (!)"); + if (nPgNum == 0) + pRetval = mpHandoutPage; + else + { + DBG_ASSERT(nPgNum == 0, + "ImpPageListWatcher::GetSdPage: access to non existing handout page (!)"); + } + break; + } + } + + return pRetval; +} + +sal_uInt32 ImpPageListWatcher::GetSdPageCount(PageKind ePgKind) +{ + sal_uInt32 nRetval(0); + + if(!mbPageListValid) + { + ImpRecreateSortedPageListOnDemand(); + } + + switch(ePgKind) + { + case PageKind::Standard: + { + nRetval = maPageVectorStandard.size(); + break; + } + case PageKind::Notes: + { + nRetval = maPageVectorNotes.size(); + break; + } + case PageKind::Handout: + { + if(mpHandoutPage) + { + nRetval = 1; + } + + break; + } + } + + return nRetval; +} + +sal_uInt32 ImpPageListWatcher::GetVisibleSdPageCount() const +{ + sal_uInt32 nVisiblePageCount = 0; + + // build up vectors again + const sal_uInt32 nPageCount(ImpGetPageCount()); + + for(sal_uInt32 a(0); a < nPageCount; a++) + { + SdPage* pCandidate = ImpGetPage(a); + if ((pCandidate->GetPageKind() == PageKind::Standard)&&(!pCandidate->IsExcluded())) nVisiblePageCount++; + } + return nVisiblePageCount; +} + +sal_uInt32 ImpDrawPageListWatcher::ImpGetPageCount() const +{ + return static_cast(mrModel.GetPageCount()); +} + +SdPage* ImpDrawPageListWatcher::ImpGetPage(sal_uInt32 nIndex) const +{ + return const_cast(static_cast(mrModel.GetPage(static_cast(nIndex)))); +} + +ImpDrawPageListWatcher::ImpDrawPageListWatcher(const SdrModel& rModel) +: ImpPageListWatcher(rModel) +{ +} + +ImpDrawPageListWatcher::~ImpDrawPageListWatcher() +{ +} + +sal_uInt32 ImpMasterPageListWatcher::ImpGetPageCount() const +{ + return static_cast(mrModel.GetMasterPageCount()); +} + +SdPage* ImpMasterPageListWatcher::ImpGetPage(sal_uInt32 nIndex) const +{ + return const_cast(static_cast(mrModel.GetMasterPage(static_cast(nIndex)))); +} + +ImpMasterPageListWatcher::ImpMasterPageListWatcher(const SdrModel& rModel) +: ImpPageListWatcher(rModel) +{ +} + +ImpMasterPageListWatcher::~ImpMasterPageListWatcher() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/PageListWatcher.hxx b/sd/source/core/PageListWatcher.hxx new file mode 100644 index 000000000..252d18615 --- /dev/null +++ b/sd/source/core/PageListWatcher.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class SdPage; +class SdrModel; + +/** Maintain a map of page indices to page objects for faster access that + remains valid during deletions and insertions of pages (#109538#). +*/ +class ImpPageListWatcher +{ +protected: + // typedefs for a vector of SdPages + typedef ::std::vector< SdPage* > SdPageVector; + + const SdrModel& mrModel; + + SdPageVector maPageVectorStandard; + SdPageVector maPageVectorNotes; + SdPage* mpHandoutPage; + + bool mbPageListValid; + + void ImpRecreateSortedPageListOnDemand(); + virtual sal_uInt32 ImpGetPageCount() const = 0; + + /** Return the page with the given index. + @param nIndex + When given an invalid index then NULL is returned. + */ + virtual SdPage* ImpGetPage (sal_uInt32 nIndex) const = 0; + +public: + explicit ImpPageListWatcher(const SdrModel& rModel); + virtual ~ImpPageListWatcher(); + + void Invalidate() { mbPageListValid = false; } + SdPage* GetSdPage(PageKind ePgKind, sal_uInt32 nPgNum); + sal_uInt32 GetSdPageCount(PageKind ePgKind); + sal_uInt32 GetVisibleSdPageCount() const; +}; + +class ImpDrawPageListWatcher : public ImpPageListWatcher +{ +protected: + virtual sal_uInt32 ImpGetPageCount() const override; + virtual SdPage* ImpGetPage(sal_uInt32 nIndex) const override; + +public: + explicit ImpDrawPageListWatcher(const SdrModel& rModel); + virtual ~ImpDrawPageListWatcher() override; +}; + +class ImpMasterPageListWatcher : public ImpPageListWatcher +{ +protected: + virtual sal_uInt32 ImpGetPageCount() const override; + virtual SdPage* ImpGetPage(sal_uInt32 nIndex) const override; + +public: + explicit ImpMasterPageListWatcher(const SdrModel& rModel); + virtual ~ImpMasterPageListWatcher() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/TransitionPreset.cxx b/sd/source/core/TransitionPreset.cxx new file mode 100644 index 000000000..8d3a9d1d1 --- /dev/null +++ b/sd/source/core/TransitionPreset.cxx @@ -0,0 +1,385 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; + +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::lang::XMultiServiceFactory; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::beans::NamedValue; + +namespace sd { + +TransitionPreset::TransitionPreset( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +{ + // first locate preset id + const Sequence< NamedValue > aUserData( xNode->getUserData() ); + const NamedValue* pProp = std::find_if(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "preset-id"; }); + if (pProp != aUserData.end()) + pProp->Value >>= maPresetId; + + // second, locate transition filter element + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW ); + Reference< XTransitionFilter > xTransition( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + mnTransition = xTransition->getTransition(); + mnSubtype = xTransition->getSubtype(); + mbDirection = xTransition->getDirection(); + mnFadeColor = xTransition->getFadeColor(); +} + +bool TransitionPreset::importTransitionsFile( TransitionPresetList& rList, + Reference< XMultiServiceFactory > const & xServiceFactory, + const OUString& aURL ) +{ + SAL_INFO("sd.transitions", "Importing " << aURL); + + Reference< container::XNameAccess > xTransitionSets( officecfg::Office::UI::Effects::UserInterface::TransitionSets::get() ); + Reference< container::XNameAccess > xTransitionGroups( officecfg::Office::UI::Effects::UserInterface::TransitionGroups::get() ); + Reference< container::XNameAccess > xTransitionVariants( officecfg::Office::UI::Effects::UserInterface::TransitionVariants::get() ); + Reference< container::XNameAccess > xTransitions( officecfg::Office::UI::Effects::UserInterface::Transitions::get() ); + + // import transition presets + Reference< XAnimationNode > xAnimationNode; + + const std::set LOKSupportedTransitionTypes = { + TransitionType::BARWIPE, + TransitionType::BOXWIPE, + TransitionType::FOURBOXWIPE, + TransitionType::ELLIPSEWIPE, + TransitionType::CLOCKWIPE, + TransitionType::PINWHEELWIPE, + TransitionType::PUSHWIPE, + TransitionType::SLIDEWIPE, + TransitionType::FADE, + TransitionType::RANDOMBARWIPE, + TransitionType::CHECKERBOARDWIPE, + TransitionType::DISSOLVE, + TransitionType::SNAKEWIPE, + TransitionType::PARALLELSNAKESWIPE, + TransitionType::IRISWIPE, + TransitionType::BARNDOORWIPE, + TransitionType::VEEWIPE, + TransitionType::ZIGZAGWIPE, + TransitionType::BARNZIGZAGWIPE, + TransitionType::FANWIPE, + TransitionType::SINGLESWEEPWIPE, + TransitionType::WATERFALLWIPE, + TransitionType::SPIRALWIPE, + TransitionType::MISCDIAGONALWIPE, + TransitionType::BOXSNAKESWIPE + }; + + const std::set LOKSupportedTransitionSubTypes = { + TransitionSubType::DEFAULT, + TransitionSubType::LEFTTORIGHT, + TransitionSubType::TOPTOBOTTOM, + TransitionSubType::CORNERSIN, + TransitionSubType::CORNERSOUT, + TransitionSubType::VERTICAL, + TransitionSubType::HORIZONTAL, + TransitionSubType::DOWN, + TransitionSubType::CIRCLE, + TransitionSubType::CLOCKWISETWELVE, + TransitionSubType::CLOCKWISETHREE, + TransitionSubType::CLOCKWISESIX, + TransitionSubType::CLOCKWISENINE, + TransitionSubType::TWOBLADEVERTICAL, + TransitionSubType::TWOBLADEHORIZONTAL, + TransitionSubType::FOURBLADE, + TransitionSubType::FROMLEFT, + TransitionSubType::FROMTOP, + TransitionSubType::FROMRIGHT, + TransitionSubType::FROMBOTTOM, + TransitionSubType::CROSSFADE, + TransitionSubType::FADETOCOLOR, + TransitionSubType::FADEFROMCOLOR, + TransitionSubType::FADEOVERCOLOR, + TransitionSubType::THREEBLADE, + TransitionSubType::EIGHTBLADE, + TransitionSubType::ONEBLADE, + TransitionSubType::ACROSS, + TransitionSubType::TOPLEFTVERTICAL, + TransitionSubType::TOPLEFTHORIZONTAL, + TransitionSubType::TOPLEFTDIAGONAL, + TransitionSubType::TOPRIGHTDIAGONAL, + TransitionSubType::BOTTOMRIGHTDIAGONAL, + TransitionSubType::BOTTOMLEFTDIAGONAL, + TransitionSubType::RECTANGLE, + TransitionSubType::DIAMOND, + TransitionSubType::TOPLEFT, + TransitionSubType::TOPRIGHT, + TransitionSubType::BOTTOMRIGHT, + TransitionSubType::BOTTOMLEFT, + TransitionSubType::TOPCENTER, + TransitionSubType::RIGHTCENTER, + TransitionSubType::BOTTOMCENTER, + TransitionSubType::LEFTCENTER, + TransitionSubType::LEFT, + TransitionSubType::UP, + TransitionSubType::RIGHT, + TransitionSubType::DIAGONALBOTTOMLEFT, + TransitionSubType::DIAGONALTOPLEFT, + TransitionSubType::CENTERTOP, + TransitionSubType::CENTERRIGHT, + TransitionSubType::TOP, + TransitionSubType::BOTTOM, + TransitionSubType::CLOCKWISETOP, + TransitionSubType::CLOCKWISERIGHT, + TransitionSubType::CLOCKWISEBOTTOM, + TransitionSubType::CLOCKWISELEFT, + TransitionSubType::CLOCKWISETOPLEFT, + TransitionSubType::COUNTERCLOCKWISEBOTTOMLEFT, + TransitionSubType::CLOCKWISEBOTTOMRIGHT, + TransitionSubType::COUNTERCLOCKWISETOPRIGHT, + TransitionSubType::VERTICALLEFT, + TransitionSubType::VERTICALRIGHT, + TransitionSubType::HORIZONTALLEFT, + TransitionSubType::HORIZONTALRIGHT, + TransitionSubType::TOPLEFTCLOCKWISE, + TransitionSubType::TOPRIGHTCLOCKWISE, + TransitionSubType::BOTTOMRIGHTCLOCKWISE, + TransitionSubType::BOTTOMLEFTCLOCKWISE, + TransitionSubType::TOPLEFTCOUNTERCLOCKWISE, + TransitionSubType::TOPRIGHTCOUNTERCLOCKWISE, + TransitionSubType::BOTTOMRIGHTCOUNTERCLOCKWISE, + TransitionSubType::BOTTOMLEFTCOUNTERCLOCKWISE, + TransitionSubType::DOUBLEBARNDOOR, + TransitionSubType::DOUBLEDIAMOND, + TransitionSubType::VERTICALTOPSAME, + TransitionSubType::VERTICALBOTTOMSAME, + TransitionSubType::VERTICALTOPLEFTOPPOSITE, + TransitionSubType::VERTICALBOTTOMLEFTOPPOSITE, + TransitionSubType::HORIZONTALLEFTSAME, + TransitionSubType::HORIZONTALRIGHTSAME, + TransitionSubType::HORIZONTALTOPLEFTOPPOSITE, + TransitionSubType::HORIZONTALTOPRIGHTOPPOSITE, + TransitionSubType::DIAGONALBOTTOMLEFTOPPOSITE, + TransitionSubType::DIAGONALTOPLEFTOPPOSITE, + TransitionSubType::TWOBOXTOP, + TransitionSubType::TWOBOXBOTTOM, + TransitionSubType::TWOBOXLEFT, + TransitionSubType::TWOBOXRIGHT, + TransitionSubType::FOURBOXVERTICAL, + TransitionSubType::FOURBOXHORIZONTAL + }; + + try { + xAnimationNode = implImportEffects( xServiceFactory, aURL ); + Reference< XEnumerationAccess > xEnumerationAccess( xAnimationNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW ); + + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + if( xChildNode->getType() == AnimationNodeType::PAR ) + { + TransitionPresetPtr pPreset( new TransitionPreset( xChildNode ) ); + + if( comphelper::LibreOfficeKit::isActive() ) + { + sal_Int16 eTransitionType = pPreset->getTransition(); + sal_Int16 eTransitionSubType = pPreset->getSubtype(); + if( LOKSupportedTransitionTypes.find(eTransitionType) == LOKSupportedTransitionTypes.end() + || LOKSupportedTransitionSubTypes.find(eTransitionSubType) == LOKSupportedTransitionSubTypes.end() ) + { + continue; + } + } + + OUString aPresetId( pPreset->getPresetId() ); + + if( !aPresetId.isEmpty() ) + { + Reference< container::XNameAccess > xTransitionNode; + + if (xTransitions->hasByName( aPresetId ) && + (xTransitions->getByName( aPresetId ) >>= xTransitionNode) && + xTransitionNode.is() ) + { + OUString sSet; + OUString sVariant; + + xTransitionNode->getByName( "Set" ) >>= sSet; + xTransitionNode->getByName( "Variant" ) >>= sVariant; + + Reference< container::XNameAccess > xSetNode; + + xTransitionSets->getByName( sSet ) >>= xSetNode; + if( xSetNode.is() ) + { + pPreset->maSetId = sSet; + xSetNode->getByName( "Label" ) >>= sSet; + pPreset->maSetLabel = sSet; + + OUString sGroup; + + xSetNode->getByName( "Group" ) >>= sGroup; + + Reference< container::XNameAccess > xGroupNode; + xTransitionGroups->getByName( sGroup ) >>= xGroupNode; + + if( xGroupNode.is() ) + { + xGroupNode->getByName( "Label" ) >>= sGroup; + if( !sVariant.isEmpty() ) + { + Reference< container::XNameAccess > xVariantNode; + xTransitionVariants->getByName( sVariant ) >>= xVariantNode; + if( xVariantNode.is() ) + { + xVariantNode->getByName( "Label" ) >>= sVariant; + pPreset->maVariantLabel = sVariant; + } + } + + pPreset->maSetLabel = sSet; + SAL_INFO("sd.transitions", aPresetId << ": " << sGroup << "/" << sSet << (sVariant.isEmpty() ? OUString() : OUString("/" + sVariant))); + + rList.push_back( pPreset ); + } + else + SAL_WARN("sd.transitions", "group node " << sGroup << " not found"); + } + else + SAL_WARN("sd.transitions", "set node " << sSet << " not found"); + } + else + SAL_WARN("sd.transitions", "transition node " << aPresetId << " not found"); + } + } + else + { + SAL_WARN("sd.transitions", " malformed xml configuration file " << aURL ); + break; + } + } + } catch( Exception& ) { + return false; + } + + return true; +} + +bool TransitionPreset::importTransitionPresetList( TransitionPresetList& rList ) +{ + if (utl::ConfigManager::IsFuzzing()) + return false; + + bool bRet = false; + + try + { + uno::Reference< uno::XComponentContext > xContext( + comphelper::getProcessComponentContext() ); + Reference< XMultiServiceFactory > xServiceFactory( + xContext->getServiceManager(), UNO_QUERY_THROW ); + + // import ui strings + Reference< XMultiServiceFactory > xConfigProvider = + configuration::theDefaultProvider::get( xContext ); + + // read path to transition effects files from config + uno::Sequence aArgs(comphelper::InitAnyPropertySequence( + { + {"nodepath", uno::Any(OUString("/org.openoffice.Office.Impress/Misc"))} + })); + Reference xNameAccess( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArgs), + UNO_QUERY_THROW ); + uno::Sequence< OUString > aFiles; + xNameAccess->getByName("TransitionFiles") >>= aFiles; + + for( const auto& rFile : std::as_const(aFiles) ) + { + OUString aURL = comphelper::getExpandedUri(xContext, rFile); + + bRet |= importTransitionsFile( rList, + xServiceFactory, + aURL ); + } + + return bRet; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TransitionPreset::importResources()" ); + } + + return bRet; +} + +std::map sd::TransitionPreset::mPresetsMap; + +const TransitionPresetList& TransitionPreset::getTransitionPresetList() +{ + // Support localization per-view. Currently not useful for Desktop + // but very much critical for LOK. The cache now is per-language. + const OUString aLang = comphelper::LibreOfficeKit::isActive() + ? comphelper::LibreOfficeKit::getLanguageTag().getBcp47() + : SvtSysLocaleOptions().GetLanguageTag().getBcp47(); + + SolarMutexGuard aGuard; + const auto it = mPresetsMap.find(aLang); + if (it != mPresetsMap.end()) + return it->second; + + TransitionPresetList& rList = mPresetsMap[aLang]; + sd::TransitionPreset::importTransitionPresetList(rList); + return rList; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/anminfo.cxx b/sd/source/core/anminfo.cxx new file mode 100644 index 000000000..5f763708c --- /dev/null +++ b/sd/source/core/anminfo.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; + +SdAnimationInfo::SdAnimationInfo(SdrObject& rObject) + : SdrObjUserData(SdrInventor::StarDrawUserData, SD_ANIMATIONINFO_ID), + mePresObjKind (PresObjKind::NONE), + meEffect (presentation::AnimationEffect_NONE), + meTextEffect (presentation::AnimationEffect_NONE), + meSpeed (presentation::AnimationSpeed_SLOW), + mbActive (true), + mbDimPrevious (false), + mbIsMovie (false), + mbDimHide (false), + mbSoundOn (false), + mbPlayFull (false), + meClickAction (presentation::ClickAction_NONE), + meSecondEffect (presentation::AnimationEffect_NONE), + meSecondSpeed (presentation::AnimationSpeed_SLOW), + mbSecondSoundOn (false), + mbSecondPlayFull (false), + mnVerb (0), + mrObject (rObject) +{ + maBlueScreen = COL_LIGHTMAGENTA; + maDimColor = COL_LIGHTGRAY; +} + +SdAnimationInfo::SdAnimationInfo(const SdAnimationInfo& rAnmInfo, SdrObject& rObject) + : SdrObjUserData (rAnmInfo), + mePresObjKind (PresObjKind::NONE), + meEffect (rAnmInfo.meEffect), + meTextEffect (rAnmInfo.meTextEffect), + meSpeed (rAnmInfo.meSpeed), + mbActive (rAnmInfo.mbActive), + mbDimPrevious (rAnmInfo.mbDimPrevious), + mbIsMovie (rAnmInfo.mbIsMovie), + mbDimHide (rAnmInfo.mbDimHide), + maBlueScreen (rAnmInfo.maBlueScreen), + maDimColor (rAnmInfo.maDimColor), + maSoundFile (rAnmInfo.maSoundFile), + mbSoundOn (rAnmInfo.mbSoundOn), + mbPlayFull (rAnmInfo.mbPlayFull), + meClickAction (rAnmInfo.meClickAction), + meSecondEffect (rAnmInfo.meSecondEffect), + meSecondSpeed (rAnmInfo.meSecondSpeed), + maSecondSoundFile (rAnmInfo.maSecondSoundFile), + mbSecondSoundOn (rAnmInfo.mbSecondSoundOn), + mbSecondPlayFull (rAnmInfo.mbSecondPlayFull), + mnVerb (rAnmInfo.mnVerb), + mrObject (rObject) +{ + // can not be copied + if(meEffect == presentation::AnimationEffect_PATH) + meEffect = presentation::AnimationEffect_NONE; +} + +SdAnimationInfo::~SdAnimationInfo() +{ +} + +std::unique_ptr SdAnimationInfo::Clone(SdrObject* pObject) const +{ + DBG_ASSERT( pObject, "SdAnimationInfo::Clone(), pObject must not be null!" ); + if( pObject == nullptr ) + pObject = &mrObject; + + return std::unique_ptr(new SdAnimationInfo(*this, *pObject )); +} + +void SdAnimationInfo::SetBookmark( const OUString& rBookmark ) +{ + if( meClickAction == css::presentation::ClickAction_BOOKMARK ) + { + OUString sURL = "#" + rBookmark; + SvxFieldItem aURLItem( SvxURLField( sURL, sURL ), EE_FEATURE_FIELD ); + mrObject.SetMergedItem( aURLItem ); + } + else + { + SvxFieldItem aURLItem( SvxURLField( rBookmark, rBookmark ), EE_FEATURE_FIELD ); + mrObject.SetMergedItem( aURLItem ); + } +} + +OUString SdAnimationInfo::GetBookmark() const +{ + OUString sBookmark; + + const SvxFieldItem* pFldItem = &mrObject.GetMergedItem( EE_FEATURE_FIELD ); + if( pFldItem ) + { + SvxURLField* pURLField = const_cast< SvxURLField* >( dynamic_cast( pFldItem->GetField() ) ); + if( pURLField ) + sBookmark = pURLField->GetURL(); + } + + if( (meClickAction == css::presentation::ClickAction_BOOKMARK) && sBookmark.startsWith("#") ) + sBookmark = sBookmark.copy( 1 ); + + return sBookmark; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/annotations/Annotation.cxx b/sd/source/core/annotations/Annotation.cxx new file mode 100644 index 000000000..991412f06 --- /dev/null +++ b/sd/source/core/annotations/Annotation.cxx @@ -0,0 +1,480 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include + +#include +#include + +#include + +#include +#include + +#include + +#include + +#include + +using namespace css; + +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd { + +namespace { + +class UndoInsertOrRemoveAnnotation : public SdrUndoAction +{ +public: + UndoInsertOrRemoveAnnotation( Annotation& rAnnotation, bool bInsert ); + + virtual void Undo() override; + virtual void Redo() override; + +protected: + rtl::Reference< Annotation > mxAnnotation; + bool mbInsert; + int mnIndex; +}; + +struct AnnotationData +{ + geometry::RealPoint2D m_Position; + geometry::RealSize2D m_Size; + OUString m_Author; + OUString m_Initials; + util::DateTime m_DateTime; + OUString m_Text; + + void get( const rtl::Reference< Annotation >& xAnnotation ) + { + m_Position = xAnnotation->getPosition(); + m_Size = xAnnotation->getSize(); + m_Author = xAnnotation->getAuthor(); + m_Initials = xAnnotation->getInitials(); + m_DateTime = xAnnotation->getDateTime(); + uno::Reference xText(xAnnotation->getTextRange()); + m_Text = xText->getString(); + } + + void set( const rtl::Reference< Annotation >& xAnnotation ) + { + xAnnotation->setPosition(m_Position); + xAnnotation->setSize(m_Size); + xAnnotation->setAuthor(m_Author); + xAnnotation->setInitials(m_Initials); + xAnnotation->setDateTime(m_DateTime); + uno::Reference xText(xAnnotation->getTextRange()); + xText->setString(m_Text); + } +}; + +class UndoAnnotation : public SdrUndoAction +{ +public: + explicit UndoAnnotation( Annotation& rAnnotation ); + + virtual void Undo() override; + virtual void Redo() override; + +protected: + rtl::Reference< Annotation > mxAnnotation; + AnnotationData maUndoData; + AnnotationData maRedoData; +}; + +} + +void createAnnotation(uno::Reference& xAnnotation, SdPage* pPage ) +{ + xAnnotation.set( + new Annotation(comphelper::getProcessComponentContext(), pPage)); + pPage->addAnnotation(xAnnotation, -1); +} + +sal_uInt32 Annotation::m_nLastId = 1; + +Annotation::Annotation( const uno::Reference& context, SdPage* pPage ) +: ::cppu::WeakComponentImplHelper(m_aMutex) +, ::cppu::PropertySetMixin(context, IMPLEMENTS_PROPERTY_SET, uno::Sequence()) +, m_nId( m_nLastId++ ) +, mpPage( pPage ) +{ +} + +// override WeakComponentImplHelperBase::disposing() +// This function is called upon disposing the component, +// if your component needs special work when it becomes +// disposed, do it here. +void SAL_CALL Annotation::disposing() +{ + mpPage = nullptr; + if( m_TextRange.is() ) + { + m_TextRange->dispose(); + m_TextRange.clear(); + } +} + +uno::Any Annotation::queryInterface(css::uno::Type const & type) +{ + return ::cppu::WeakComponentImplHelper::queryInterface(type); +} + +// com.sun.star.beans.XPropertySet: +uno::Reference SAL_CALL Annotation::getPropertySetInfo() +{ + return ::cppu::PropertySetMixin::getPropertySetInfo(); +} + +void SAL_CALL Annotation::setPropertyValue(const OUString & aPropertyName, const uno::Any & aValue) +{ + ::cppu::PropertySetMixin::setPropertyValue(aPropertyName, aValue); +} + +uno::Any SAL_CALL Annotation::getPropertyValue(const OUString & aPropertyName) +{ + return ::cppu::PropertySetMixin::getPropertyValue(aPropertyName); +} + +void SAL_CALL Annotation::addPropertyChangeListener(const OUString & aPropertyName, const uno::Reference & xListener) +{ + ::cppu::PropertySetMixin::addPropertyChangeListener(aPropertyName, xListener); +} + +void SAL_CALL Annotation::removePropertyChangeListener(const OUString & aPropertyName, const uno::Reference & xListener) +{ + ::cppu::PropertySetMixin::removePropertyChangeListener(aPropertyName, xListener); +} + +void SAL_CALL Annotation::addVetoableChangeListener(const OUString & aPropertyName, const uno::Reference & xListener) +{ + ::cppu::PropertySetMixin::addVetoableChangeListener(aPropertyName, xListener); +} + +void SAL_CALL Annotation::removeVetoableChangeListener(const OUString & aPropertyName, const uno::Reference & xListener) +{ + ::cppu::PropertySetMixin::removeVetoableChangeListener(aPropertyName, xListener); +} + +uno::Any SAL_CALL Annotation::getAnchor() +{ + osl::MutexGuard g(m_aMutex); + uno::Any aRet; + if( mpPage ) + { + uno::Reference xPage( mpPage->getUnoPage(), uno::UNO_QUERY ); + aRet <<= xPage; + } + return aRet; +} + +// css::office::XAnnotation: +geometry::RealPoint2D SAL_CALL Annotation::getPosition() +{ + osl::MutexGuard g(m_aMutex); + return m_Position; +} + +void SAL_CALL Annotation::setPosition(const geometry::RealPoint2D & the_value) +{ + prepareSet("Position", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_Position = the_value; + } +} + +// css::office::XAnnotation: +geometry::RealSize2D SAL_CALL Annotation::getSize() +{ + osl::MutexGuard g(m_aMutex); + return m_Size; +} + +void SAL_CALL Annotation::setSize(const geometry::RealSize2D & the_value) +{ + prepareSet("Size", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_Size = the_value; + } +} + +OUString SAL_CALL Annotation::getAuthor() +{ + osl::MutexGuard g(m_aMutex); + return m_Author; +} + +void SAL_CALL Annotation::setAuthor(const OUString & the_value) +{ + prepareSet("Author", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_Author = the_value; + } +} + +OUString SAL_CALL Annotation::getInitials() +{ + osl::MutexGuard g(m_aMutex); + return m_Initials; +} + +void SAL_CALL Annotation::setInitials(const OUString & the_value) +{ + prepareSet("Initials", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_Initials = the_value; + } +} + +util::DateTime SAL_CALL Annotation::getDateTime() +{ + osl::MutexGuard g(m_aMutex); + return m_DateTime; +} + +void SAL_CALL Annotation::setDateTime(const util::DateTime & the_value) +{ + prepareSet("DateTime", uno::Any(), uno::Any(), nullptr); + { + osl::MutexGuard g(m_aMutex); + createChangeUndo(); + m_DateTime = the_value; + } +} + +void Annotation::createChangeUndo() +{ + SdrModel* pModel = GetModel(); // TTTT should use reference + if( pModel && pModel->IsUndoEnabled() ) + pModel->AddUndo( std::make_unique( *this ) ); + + if( pModel ) + { + pModel->SetChanged(); + uno::Reference< XInterface > xSource( static_cast( this ) ); + NotifyDocumentEvent( + static_cast< SdDrawDocument& >( *pModel ), + "OnAnnotationChanged" , + xSource ); + } +} + +uno::Reference SAL_CALL Annotation::getTextRange() +{ + osl::MutexGuard g(m_aMutex); + if( !m_TextRange.is() && (mpPage != nullptr) ) + { + m_TextRange = TextApiObject::create( static_cast< SdDrawDocument* >( &mpPage->getSdrModelFromSdrPage() ) ); + } + return m_TextRange; +} + +std::unique_ptr CreateUndoInsertOrRemoveAnnotation( const uno::Reference& xAnnotation, bool bInsert ) +{ + Annotation* pAnnotation = dynamic_cast< Annotation* >( xAnnotation.get() ); + if( pAnnotation ) + { + return std::make_unique< UndoInsertOrRemoveAnnotation >( *pAnnotation, bInsert ); + } + else + { + return nullptr; + } +} + +void CreateChangeUndo(const uno::Reference& xAnnotation) +{ + Annotation* pAnnotation = dynamic_cast(xAnnotation.get()); + if (pAnnotation) + pAnnotation->createChangeUndo(); +} + +sal_uInt32 getAnnotationId(const uno::Reference& xAnnotation) +{ + Annotation* pAnnotation = dynamic_cast(xAnnotation.get()); + sal_uInt32 nId = 0; + if (pAnnotation) + nId = pAnnotation->GetId(); + return nId; +} + +const SdPage* getAnnotationPage(const uno::Reference& xAnnotation) +{ + Annotation* pAnnotation = dynamic_cast(xAnnotation.get()); + if (pAnnotation) + return pAnnotation->GetPage(); + return nullptr; +} + +namespace +{ +std::string lcl_LOKGetCommentPayload(CommentNotificationType nType, uno::Reference const & rxAnnotation) +{ + ::tools::JsonWriter aJsonWriter; + { + auto aCommentNode = aJsonWriter.startNode("comment"); + + aJsonWriter.put("action", (nType == CommentNotificationType::Add ? "Add" : + (nType == CommentNotificationType::Remove ? "Remove" : + (nType == CommentNotificationType::Modify ? "Modify" : "???")))); + aJsonWriter.put("id", sd::getAnnotationId(rxAnnotation)); + + if (nType != CommentNotificationType::Remove && rxAnnotation.is()) + { + aJsonWriter.put("id", sd::getAnnotationId(rxAnnotation)); + aJsonWriter.put("author", rxAnnotation->getAuthor()); + aJsonWriter.put("dateTime", utl::toISO8601(rxAnnotation->getDateTime())); + uno::Reference xText(rxAnnotation->getTextRange()); + aJsonWriter.put("text", xText->getString()); + const SdPage* pPage = sd::getAnnotationPage(rxAnnotation); + aJsonWriter.put("parthash", pPage ? OString::number(pPage->GetHashCode()) : OString()); + geometry::RealPoint2D const & rPoint = rxAnnotation->getPosition(); + geometry::RealSize2D const & rSize = rxAnnotation->getSize(); + ::tools::Rectangle aRectangle(Point(rPoint.X * 100.0, rPoint.Y * 100.0), Size(rSize.Width * 100.0, rSize.Height * 100.0)); + aRectangle = OutputDevice::LogicToLogic(aRectangle, MapMode(MapUnit::Map100thMM), MapMode(MapUnit::MapTwip)); + OString sRectangle = aRectangle.toString(); + aJsonWriter.put("rectangle", sRectangle.getStr()); + } + } + return aJsonWriter.extractData(); +} +} // anonymous ns + +void LOKCommentNotify(CommentNotificationType nType, const SfxViewShell* pViewShell, uno::Reference const & rxAnnotation) +{ + // callbacks only if tiled annotations are explicitly turned off by LOK client + if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations()) + return ; + + std::string aPayload = lcl_LOKGetCommentPayload(nType, rxAnnotation); + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, aPayload.c_str()); +} + +void LOKCommentNotifyAll(CommentNotificationType nType, uno::Reference const & rxAnnotation) +{ + // callbacks only if tiled annotations are explicitly turned off by LOK client + if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations()) + return ; + + std::string aPayload = lcl_LOKGetCommentPayload(nType, rxAnnotation); + + const SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while (pViewShell) + { + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_COMMENT, aPayload.c_str()); + pViewShell = SfxViewShell::GetNext(*pViewShell); + } +} + +UndoInsertOrRemoveAnnotation::UndoInsertOrRemoveAnnotation( Annotation& rAnnotation, bool bInsert ) +: SdrUndoAction( *rAnnotation.GetModel() ) +, mxAnnotation( &rAnnotation ) +, mbInsert( bInsert ) +, mnIndex( 0 ) +{ + SdPage* pPage = rAnnotation.GetPage(); + if( pPage ) + { + uno::Reference xAnnotation( &rAnnotation ); + + const AnnotationVector& rVec = pPage->getAnnotations(); + auto iter = std::find(rVec.begin(), rVec.end(), xAnnotation); + mnIndex += std::distance(rVec.begin(), iter); + } +} + +void UndoInsertOrRemoveAnnotation::Undo() +{ + SdPage* pPage = mxAnnotation->GetPage(); + SdrModel* pModel = mxAnnotation->GetModel(); + if( !(pPage && pModel) ) + return; + + uno::Reference xAnnotation( mxAnnotation ); + if( mbInsert ) + { + pPage->removeAnnotation( xAnnotation ); + } + else + { + pPage->addAnnotation( xAnnotation, mnIndex ); + LOKCommentNotifyAll( CommentNotificationType::Add, xAnnotation ); + } +} + +void UndoInsertOrRemoveAnnotation::Redo() +{ + SdPage* pPage = mxAnnotation->GetPage(); + SdrModel* pModel = mxAnnotation->GetModel(); + if( !(pPage && pModel) ) + return; + + uno::Reference xAnnotation( mxAnnotation ); + + if( mbInsert ) + { + pPage->addAnnotation( xAnnotation, mnIndex ); + LOKCommentNotifyAll( CommentNotificationType::Add, xAnnotation ); + } + else + { + pPage->removeAnnotation( xAnnotation ); + } +} + +UndoAnnotation::UndoAnnotation( Annotation& rAnnotation ) +: SdrUndoAction( *rAnnotation.GetModel() ) +, mxAnnotation( &rAnnotation ) +{ + maUndoData.get( mxAnnotation ); +} + +void UndoAnnotation::Undo() +{ + maRedoData.get( mxAnnotation ); + maUndoData.set( mxAnnotation ); + LOKCommentNotifyAll( CommentNotificationType::Modify, mxAnnotation ); +} + +void UndoAnnotation::Redo() +{ + maUndoData.get( mxAnnotation ); + maRedoData.set( mxAnnotation ); + LOKCommentNotifyAll( CommentNotificationType::Modify, mxAnnotation ); +} + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/annotations/AnnotationEnumeration.cxx b/sd/source/core/annotations/AnnotationEnumeration.cxx new file mode 100644 index 000000000..5fae2422b --- /dev/null +++ b/sd/source/core/annotations/AnnotationEnumeration.cxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +namespace sd { + +namespace { + +class AnnotationEnumeration: public ::cppu::WeakImplHelper< css::office::XAnnotationEnumeration > +{ +public: + explicit AnnotationEnumeration( AnnotationVector&& rAnnotations ); + AnnotationEnumeration(const AnnotationEnumeration&) = delete; + AnnotationEnumeration& operator=(const AnnotationEnumeration&) = delete; + + // css::office::XAnnotationEnumeration: + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual css::uno::Reference< css::office::XAnnotation > SAL_CALL nextElement() override; + +private: + // destructor is private and will be called indirectly by the release call virtual ~AnnotationEnumeration() {} + + AnnotationVector maAnnotations; + AnnotationVector::iterator maIter; +}; + +} + +Reference< XAnnotationEnumeration > createAnnotationEnumeration( sd::AnnotationVector&& rAnnotations ) +{ + return new AnnotationEnumeration( std::move(rAnnotations) ); +} + +AnnotationEnumeration::AnnotationEnumeration( AnnotationVector&& rAnnotations ) +: maAnnotations(std::move(rAnnotations)) +{ + maIter = maAnnotations.begin(); +} + +// css::office::XAnnotationEnumeration: +sal_Bool SAL_CALL AnnotationEnumeration::hasMoreElements() +{ + return maIter != maAnnotations.end(); +} + +css::uno::Reference< css::office::XAnnotation > SAL_CALL AnnotationEnumeration::nextElement() +{ + if( maIter == maAnnotations.end() ) + throw css::container::NoSuchElementException(); + + return (*maIter++); +} + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/cusshow.cxx b/sd/source/core/cusshow.cxx new file mode 100644 index 000000000..8b51a613d --- /dev/null +++ b/sd/source/core/cusshow.cxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +using namespace ::com::sun::star; + +/************************************************************************* +|* +|* Ctor +|* +\************************************************************************/ +SdCustomShow::SdCustomShow() +{ +} + +/************************************************************************* +|* +|* Copy-Ctor +|* +\************************************************************************/ +SdCustomShow::SdCustomShow( const SdCustomShow& rShow ) + : maPages(rShow.maPages) +{ + aName = rShow.GetName(); +} + +SdCustomShow::SdCustomShow(css::uno::Reference< css::uno::XInterface > const & xShow ) + : mxUnoCustomShow( xShow ) +{ +} + +/************************************************************************* +|* +|* Dtor +|* +\************************************************************************/ +SdCustomShow::~SdCustomShow() +{ + uno::Reference< uno::XInterface > xShow( mxUnoCustomShow ); + uno::Reference< lang::XComponent > xComponent( xShow, uno::UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); +} + +uno::Reference< uno::XInterface > SdCustomShow::getUnoCustomShow() +{ + // try weak reference first + uno::Reference< uno::XInterface > xShow( mxUnoCustomShow ); + + if( !xShow.is() ) + { + xShow = createUnoCustomShow( this ); + } + + return xShow; +} + +void SdCustomShow::ReplacePage( const SdPage* pOldPage, const SdPage* pNewPage ) +{ + if( !pNewPage ) + { + maPages.erase(::std::remove(maPages.begin(), maPages.end(), pOldPage), maPages.end()); + } + else + { + ::std::replace(maPages.begin(), maPages.end(), pOldPage, pNewPage); + } +} + +void SdCustomShow::SetName(const OUString& rName) +{ + aName = rName; +} + +void SdCustomShowList::erase(std::vector>::iterator it) +{ + mShows.erase(it); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc.cxx b/sd/source/core/drawdoc.cxx new file mode 100644 index 000000000..182ffe7f2 --- /dev/null +++ b/sd/source/core/drawdoc.cxx @@ -0,0 +1,1206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include "PageListWatcher.hxx" +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace com::sun::star::linguistic2 { class XHyphenator; } +namespace com::sun::star::linguistic2 { class XSpellChecker1; } + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +using namespace com::sun::star::xml::dom; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::lang::XMultiServiceFactory; + + +SdDrawDocument* SdDrawDocument::s_pDocLockedInsertingLinks = nullptr; + +PresentationSettings::PresentationSettings() +: mbAll( true ), + mbEndless( false ), + mbCustomShow(false), + mbManual( false ), + mbMouseVisible( false ), + mbMouseAsPen( false ), + mbLockedPages( false ), + mbAlwaysOnTop( false ), + mbFullScreen( true ), + mbAnimationAllowed( true ), + mnPauseTimeout( 0 ), + mbShowPauseLogo( false ), + mbStartCustomShow( false ) +{ +} + +SdDrawDocument::SdDrawDocument(DocumentType eType, SfxObjectShell* pDrDocSh) +: FmFormModel( + nullptr, + pDrDocSh) +, mpDocSh(static_cast< ::sd::DrawDocShell*>(pDrDocSh)) +, mpCreatingTransferable( nullptr ) +, mbHasOnlineSpellErrors(false) +, mbInitialOnlineSpellingEnabled(true) +, mbNewOrLoadCompleted(false) +, mbOnlineSpell(false) +, mbStartWithPresentation( false ) +, mbExitAfterPresenting( false ) +, meLanguage( LANGUAGE_SYSTEM ) +, meLanguageCJK( LANGUAGE_SYSTEM ) +, meLanguageCTL( LANGUAGE_SYSTEM ) +, mePageNumType(SVX_NUM_ARABIC) +, mbAllocDocSh(false) +, meDocType(eType) +, mbEmbedFonts(false) +, mbEmbedUsedFontsOnly(false) +, mbEmbedFontScriptLatin(true) +, mbEmbedFontScriptAsian(true) +, mbEmbedFontScriptComplex(true) +, mnImagePreferredDPI(0) +{ + mpDrawPageListWatcher.reset(new ImpDrawPageListWatcher(*this)); + mpMasterPageListWatcher.reset(new ImpMasterPageListWatcher(*this)); + + InitLayoutVector(); + InitObjectVector(); + SetObjectShell(pDrDocSh); // for VCDrawModel + + if (mpDocSh) + { + SetSwapGraphics(); + } + + // Set measuring unit (of the application) and scale (of SdMod) + sal_Int32 nX, nY; + SdOptions* pOptions = SD_MOD()->GetSdOptions(meDocType); + pOptions->GetScale( nX, nY ); + + // Allow UI scale only for draw documents. + if( eType == DocumentType::Draw ) + SetUIUnit( static_cast(pOptions->GetMetric()), Fraction( nX, nY ) ); // user-defined + else + SetUIUnit( static_cast(pOptions->GetMetric()), Fraction( 1, 1 ) ); // default + + SetScaleUnit(MapUnit::Map100thMM); + SetScaleFraction(Fraction(1, 1)); + SetDefaultFontHeight(o3tl::convert(24, o3tl::Length::pt, o3tl::Length::mm100)); + + m_pItemPool->SetDefaultMetric(MapUnit::Map100thMM); + m_pItemPool->FreezeIdRanges(); + SetTextDefaults(); + + // DrawingEngine has to know where it is... + FmFormModel::SetStyleSheetPool( new SdStyleSheetPool( GetPool(), this ) ); + + // Set StyleSheetPool for DrawOutliner, so text objects can be read correctly. + // The link to the StyleRequest handler of the document is set later, in + // NewOrLoadCompleted, because only then do all the templates exist. + SdrOutliner& rOutliner = GetDrawOutliner(); + rOutliner.SetStyleSheetPool(static_cast(GetStyleSheetPool())); + SetCalcFieldValueHdl( &rOutliner ); + + // set linguistic options + if (!utl::ConfigManager::IsFuzzing()) + { + const SvtLinguConfig aLinguConfig; + SvtLinguOptions aOptions; + aLinguConfig.GetOptions( aOptions ); + + SetLanguage( MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage, + css::i18n::ScriptType::LATIN), EE_CHAR_LANGUAGE ); + SetLanguage( MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CJK, + css::i18n::ScriptType::ASIAN), EE_CHAR_LANGUAGE_CJK ); + SetLanguage( MsLangId::resolveSystemLanguageByScriptType(aOptions.nDefaultLanguage_CTL, + css::i18n::ScriptType::COMPLEX), EE_CHAR_LANGUAGE_CTL ); + + mbOnlineSpell = aOptions.bIsSpellAuto; + } + + LanguageType eRealLanguage = MsLangId::getRealLanguage( meLanguage ); + mpCharClass.reset(new CharClass( LanguageTag( eRealLanguage) )); + + // If the current application language is a language that uses right-to-left text... + LanguageType eRealCTLLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); + + // for korean and japanese languages we have a different default for apply spacing between asian, latin and ctl text + if (MsLangId::isKorean(eRealCTLLanguage) || (LANGUAGE_JAPANESE == eRealCTLLanguage)) + { + GetPool().GetSecondaryPool()->SetPoolDefaultItem( SvxScriptSpaceItem( false, EE_PARA_ASIANCJKSPACING ) ); + } + + // Set DefTab and SpellOptions for the SD module + sal_uInt16 nDefTab = pOptions->GetDefTab(); + SetDefaultTabulator( nDefTab ); + + try + { + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + rOutliner.SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + rOutliner.SetHyphenator( xHyphenator ); + + SetForbiddenCharsTable(SvxForbiddenCharactersTable::makeForbiddenCharactersTable(::comphelper::getProcessComponentContext())); + } + catch(...) + { + OSL_FAIL("Can't get SpellChecker"); + } + + rOutliner.SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + + if (mpDocSh) + { + SetLinkManager( new sfx2::LinkManager(mpDocSh) ); + } + + EEControlBits nCntrl = rOutliner.GetControlWord(); + nCntrl |= EEControlBits::ALLOWBIGOBJS; + + if (mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + nCntrl &= ~ EEControlBits::ULSPACESUMMATION; + if ( meDocType != DocumentType::Impress ) + SetSummationOfParagraphs( false ); + else + { + SetSummationOfParagraphs( pOptions->IsSummationOfParagraphs() ); + if ( pOptions->IsSummationOfParagraphs() ) + nCntrl |= EEControlBits::ULSPACESUMMATION; + } + rOutliner.SetControlWord(nCntrl); + + // Initialize the printer independent layout mode + SetPrinterIndependentLayout (pOptions->GetPrinterIndependentLayout()); + + // Set the StyleSheetPool for HitTestOutliner. + // The link to the StyleRequest handler of the document is set later, in + // NewOrLoadCompleted, because only then do all the templates exist. + m_pHitTestOutliner->SetStyleSheetPool( static_cast(GetStyleSheetPool()) ); + + SetCalcFieldValueHdl( m_pHitTestOutliner.get() ); + + try + { + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + m_pHitTestOutliner->SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + m_pHitTestOutliner->SetHyphenator( xHyphenator ); + } + catch(...) + { + OSL_FAIL("Can't get SpellChecker"); + } + + m_pHitTestOutliner->SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + + EEControlBits nCntrl2 = m_pHitTestOutliner->GetControlWord(); + nCntrl2 |= EEControlBits::ALLOWBIGOBJS; + nCntrl2 &= ~EEControlBits::ONLINESPELLING; + + nCntrl2 &= ~ EEControlBits::ULSPACESUMMATION; + if ( pOptions->IsSummationOfParagraphs() ) + nCntrl2 |= EEControlBits::ULSPACESUMMATION; + + m_pHitTestOutliner->SetControlWord( nCntrl2 ); + + /** Create layers + * + * We create the following default layers on all pages and master pages: + * + * sUNO_LayerName_layout; "layout": default layer for drawing objects of normal pages + * localized by SdResId(STR_LAYER_LAYOUT) + * + * sUNO_LayerName_background; "background": background of the master page + * localized by SdResId(STR_LAYER_BCKGRND) + * (currently unused within normal pages and not visible to users) + * + * sUNO_LayerName_background_objects; "backgroundobjects": objects on the background of master pages + * localized by SdResId(STR_LAYER_BCKGRNDOBJ) + * (currently unused within normal pages) + * + * sUNO_LayerName_controls; "controls": default layer for controls + * localized by SdResId(STR_LAYER_CONTROLS) + * (currently special handling in regard to z-order) + * + * sUNO_LayerName_measurelines; "measurelines" : default layer for measure lines + * localized by SdResId(STR_LAYER_MEASURELINES) + */ + + { + SdrLayerAdmin& rLayerAdmin = GetLayerAdmin(); + rLayerAdmin.NewLayer( sUNO_LayerName_layout ); + rLayerAdmin.NewLayer( sUNO_LayerName_background ); + rLayerAdmin.NewLayer( sUNO_LayerName_background_objects ); + rLayerAdmin.NewLayer( sUNO_LayerName_controls); + rLayerAdmin.NewLayer( sUNO_LayerName_measurelines ); + + rLayerAdmin.SetControlLayerName(sUNO_LayerName_controls); + } + +} + +// Destructor +SdDrawDocument::~SdDrawDocument() +{ + Broadcast(SdrHint(SdrHintKind::ModelCleared)); + + if (mpWorkStartupTimer) + { + if ( mpWorkStartupTimer->IsActive() ) + mpWorkStartupTimer->Stop(); + + mpWorkStartupTimer.reset(); + } + + StopOnlineSpelling(); + mpOnlineSearchItem.reset(); + + CloseBookmarkDoc(); + SetAllocDocSh(false); + + ClearModel(true); + + if (m_pLinkManager) + { + // Release BaseLinks + if ( !m_pLinkManager->GetLinks().empty() ) + { + m_pLinkManager->Remove( 0, m_pLinkManager->GetLinks().size() ); + } + + delete m_pLinkManager; + m_pLinkManager = nullptr; + } + + maFrameViewList.clear(); + mpCustomShowList.reset(); + mpOutliner.reset(); + mpInternalOutliner.reset(); + mpCharClass.reset(); +} + +void SdDrawDocument::adaptSizeAndBorderForAllPages( + const Size& rNewSize, + ::tools::Long nLeft, + ::tools::Long nRight, + ::tools::Long nUpper, + ::tools::Long nLower) +{ + const sal_uInt16 nMasterPageCnt(GetMasterSdPageCount(PageKind::Standard)); + const sal_uInt16 nPageCnt(GetSdPageCount(PageKind::Standard)); + + if(0 == nMasterPageCnt && 0 == nPageCnt) + { + return; + } + + SdPage* pPage(0 != nPageCnt ? GetSdPage(0, PageKind::Standard) : GetMasterSdPage(0, PageKind::Standard)); + + // call fully implemented local version, including getting + // some more information from one of the Pages (1st one) + AdaptPageSizeForAllPages( + rNewSize, + PageKind::Standard, + nullptr, + nLeft, + nRight, + nUpper, + nLower, + true, + pPage->GetOrientation(), + pPage->GetPaperBin(), + pPage->IsBackgroundFullSize()); + + // adjust handout page to new format of the standard page + if(0 != nPageCnt) + { + GetSdPage(0, PageKind::Handout)->CreateTitleAndLayout(true); + } +} + +void SdDrawDocument::AdaptPageSizeForAllPages( + const Size& rNewSize, + PageKind ePageKind, + SdUndoGroup* pUndoGroup, + ::tools::Long nLeft, + ::tools::Long nRight, + ::tools::Long nUpper, + ::tools::Long nLower, + bool bScaleAll, + Orientation eOrientation, + sal_uInt16 nPaperBin, + bool bBackgroundFullSize) +{ + sal_uInt16 i; + const sal_uInt16 nMasterPageCnt(GetMasterSdPageCount(ePageKind)); + const sal_uInt16 nPageCnt(GetSdPageCount(ePageKind)); + + if(0 == nMasterPageCnt && 0 == nPageCnt) + { + return; + } + + for (i = 0; i < nMasterPageCnt; i++) + { + // first, handle all master pages + SdPage* pPage(GetMasterSdPage(i, ePageKind)); + + if(pUndoGroup) + { + SdUndoAction* pUndo( + new SdPageFormatUndoAction( + this, + pPage, + pPage->GetSize(), + pPage->GetLeftBorder(), pPage->GetRightBorder(), + pPage->GetUpperBorder(), pPage->GetLowerBorder(), + pPage->GetOrientation(), + pPage->GetPaperBin(), + pPage->IsBackgroundFullSize(), + rNewSize, + nLeft, nRight, + nUpper, nLower, + bScaleAll, + eOrientation, + nPaperBin, + bBackgroundFullSize)); + pUndoGroup->AddAction(pUndo); + } + + if (rNewSize.Width() > 0 || nLeft >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0) + { + ::tools::Rectangle aNewBorderRect(nLeft, nUpper, nRight, nLower); + pPage->ScaleObjects(rNewSize, aNewBorderRect, bScaleAll); + + if (rNewSize.Width() > 0) + { + pPage->SetSize(rNewSize); + } + } + + if( nLeft >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0 ) + { + pPage->SetBorder(nLeft, nUpper, nRight, nLower); + } + + pPage->SetOrientation(eOrientation); + pPage->SetPaperBin( nPaperBin ); + pPage->SetBackgroundFullSize( bBackgroundFullSize ); + + if ( ePageKind == PageKind::Standard ) + { + GetMasterSdPage(i, PageKind::Notes)->CreateTitleAndLayout(); + } + + pPage->CreateTitleAndLayout(); + } + + for (i = 0; i < nPageCnt; i++) + { + // then, handle all pages + SdPage* pPage(GetSdPage(i, ePageKind)); + + if(pUndoGroup) + { + SdUndoAction* pUndo( + new SdPageFormatUndoAction( + this, + pPage, + pPage->GetSize(), + pPage->GetLeftBorder(), pPage->GetRightBorder(), + pPage->GetUpperBorder(), pPage->GetLowerBorder(), + pPage->GetOrientation(), + pPage->GetPaperBin(), + pPage->IsBackgroundFullSize(), + rNewSize, + nLeft, nRight, + nUpper, nLower, + bScaleAll, + eOrientation, + nPaperBin, + bBackgroundFullSize)); + pUndoGroup->AddAction(pUndo); + } + + if (rNewSize.Width() > 0 || nLeft >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0) + { + ::tools::Rectangle aNewBorderRect(nLeft, nUpper, nRight, nLower); + pPage->ScaleObjects(rNewSize, aNewBorderRect, bScaleAll); + + if (rNewSize.Width() > 0) + { + pPage->SetSize(rNewSize); + } + } + + if( nLeft >= 0 || nRight >= 0 || nUpper >= 0 || nLower >= 0 ) + { + pPage->SetBorder(nLeft, nUpper, nRight, nLower); + } + + pPage->SetOrientation(eOrientation); + pPage->SetPaperBin( nPaperBin ); + pPage->SetBackgroundFullSize( bBackgroundFullSize ); + + if ( ePageKind == PageKind::Standard ) + { + SdPage* pNotesPage = GetSdPage(i, PageKind::Notes); + pNotesPage->SetAutoLayout( pNotesPage->GetAutoLayout() ); + } + + pPage->SetAutoLayout( pPage->GetAutoLayout() ); + } +} + +SdrModel* SdDrawDocument::AllocModel() const +{ + return AllocSdDrawDocument(); +} + +namespace +{ + +/// Copies all user-defined properties from pSource to pDestination. +void lcl_copyUserDefinedProperties(const SfxObjectShell* pSource, const SfxObjectShell* pDestination) +{ + if (!pSource || !pDestination) + return; + + uno::Reference xSource = pSource->getDocProperties(); + uno::Reference xDestination = pDestination->getDocProperties(); + uno::Reference xSourcePropertyContainer = xSource->getUserDefinedProperties(); + uno::Reference xDestinationPropertyContainer = xDestination->getUserDefinedProperties(); + uno::Reference xSourcePropertySet(xSourcePropertyContainer, uno::UNO_QUERY); + const uno::Sequence aProperties = xSourcePropertySet->getPropertySetInfo()->getProperties(); + + for (const beans::Property& rProperty : aProperties) + { + const OUString& rKey = rProperty.Name; + uno::Any aValue = xSourcePropertySet->getPropertyValue(rKey); + // We know that pDestination was just created, so has no properties: addProperty() will never throw. + xDestinationPropertyContainer->addProperty(rKey, beans::PropertyAttribute::REMOVABLE, aValue); + } +} + +} + +// This method creates a new document (SdDrawDocument) and returns a pointer to +// said document. The drawing engine uses this method to put the document (or +// parts of it) into the clipboard/DragServer. +SdDrawDocument* SdDrawDocument::AllocSdDrawDocument() const +{ + SdDrawDocument* pNewModel = nullptr; + + if( mpCreatingTransferable ) + { + // Document is created for drag & drop/clipboard. To be able to + // do this, the document has to know a DocShell (SvPersist). + SfxObjectShell* pObj = nullptr; + ::sd::DrawDocShell* pNewDocSh = nullptr; + + if( meDocType == DocumentType::Impress ) + mpCreatingTransferable->SetDocShell( new ::sd::DrawDocShell( + SfxObjectCreateMode::EMBEDDED, true, meDocType ) ); + else + mpCreatingTransferable->SetDocShell( new ::sd::GraphicDocShell( + SfxObjectCreateMode::EMBEDDED ) ); + + pObj = mpCreatingTransferable->GetDocShell().get(); + pNewDocSh = static_cast< ::sd::DrawDocShell*>( pObj ); + pNewDocSh->DoInitNew(); + pNewModel = pNewDocSh->GetDoc(); + + // Only necessary for clipboard - + // for drag & drop this is handled by DragServer + SdStyleSheetPool* pOldStylePool = static_cast( GetStyleSheetPool() ); + SdStyleSheetPool* pNewStylePool = static_cast( pNewModel->GetStyleSheetPool() ); + + pNewStylePool->CopyGraphicSheets(*pOldStylePool); + pNewStylePool->CopyCellSheets(*pOldStylePool); + pNewStylePool->CopyTableStyles(*pOldStylePool); + + for (sal_uInt16 i = 0; i < GetMasterSdPageCount(PageKind::Standard); i++) + { + // Move with all of the master page's layouts + OUString aOldLayoutName(const_cast(this)->GetMasterSdPage(i, PageKind::Standard)->GetLayoutName()); + aOldLayoutName = aOldLayoutName.copy( 0, aOldLayoutName.indexOf( SD_LT_SEPARATOR ) ); + StyleSheetCopyResultVector aCreatedSheets; + pNewStylePool->CopyLayoutSheets(aOldLayoutName, *pOldStylePool, aCreatedSheets ); + } + + lcl_copyUserDefinedProperties(GetDocSh(), pNewDocSh); + + pNewModel->NewOrLoadCompleted( DocCreationMode::Loaded ); // loaded from source document + } + else if( mbAllocDocSh ) + { + // Create a DocShell which is then returned with GetAllocedDocSh() + SdDrawDocument* pDoc = const_cast(this); + pDoc->SetAllocDocSh(false); + pDoc->mxAllocedDocShRef = new ::sd::DrawDocShell( + SfxObjectCreateMode::EMBEDDED, true, meDocType); + pDoc->mxAllocedDocShRef->DoInitNew(); + pNewModel = pDoc->mxAllocedDocShRef->GetDoc(); + } + else + { + pNewModel = new SdDrawDocument(meDocType, nullptr); + } + + return pNewModel; +} + +rtl::Reference SdDrawDocument::AllocSdPage(bool bMasterPage) +{ + return new SdPage(*this, bMasterPage); +} + +// This method creates a new page (SdPage) and returns a pointer to said page. +// The drawing engine uses this method to create pages (whose types it does +// not know, as they are _derivatives_ of SdrPage) when loading. +rtl::Reference SdDrawDocument::AllocPage(bool bMasterPage) +{ + return AllocSdPage(bMasterPage); +} + +// When the model has changed +void SdDrawDocument::SetChanged(bool bFlag) +{ + if (mpDocSh) + { + if (mbNewOrLoadCompleted && mpDocSh->IsEnableSetModified()) + { + // Pass on to base class + FmFormModel::SetChanged(bFlag); + + // Forward to ObjectShell + mpDocSh->SetModified(bFlag); + } + } + else + { + // Pass on to base class + FmFormModel::SetChanged(bFlag); + } +} + +// The model changed, don't call anything else +void SdDrawDocument::NbcSetChanged(bool bFlag) +{ + // forward to baseclass + FmFormModel::SetChanged(bFlag); +} + +// NewOrLoadCompleted is called when the document is loaded, or when it is clear +// it won't load any more. +void SdDrawDocument::NewOrLoadCompleted(DocCreationMode eMode) +{ + if (eMode == DocCreationMode::New) + { + // New document: + // create slideshow and default templates, + // create pool for virtual controls + CreateLayoutTemplates(); + CreateDefaultCellStyles(); + + static_cast< SdStyleSheetPool* >( mxStyleSheetPool.get() )->CreatePseudosIfNecessary(); + } + else if (eMode == DocCreationMode::Loaded) + { + // Document has finished loading + + CheckMasterPages(); + + if ( GetMasterSdPageCount(PageKind::Standard) > 1 ) + RemoveUnnecessaryMasterPages( nullptr, true, false ); + + for ( sal_uInt16 i = 0; i < GetPageCount(); i++ ) + { + // Check for correct layout names + SdPage* pPage = static_cast( GetPage( i ) ); + + if(pPage->TRG_HasMasterPage()) + { + SdPage& rMaster = static_cast(pPage->TRG_GetMasterPage() ); + + if(rMaster.GetLayoutName() != pPage->GetLayoutName()) + { + pPage->SetLayoutName(rMaster.GetLayoutName()); + } + } + } + + for ( sal_uInt16 nPage = 0; nPage < GetMasterPageCount(); nPage++) + { + // LayoutName and PageName must be the same + SdPage* pPage = static_cast( GetMasterPage( nPage ) ); + + OUString aName( pPage->GetLayoutName() ); + aName = aName.copy( 0, aName.indexOf( SD_LT_SEPARATOR ) ); + + if( aName != pPage->GetName() ) + pPage->SetName( aName ); + } + + // Create names of the styles in the user's language + static_cast(mxStyleSheetPool.get())->UpdateStdNames(); + + // Create any missing styles - eg. formerly, there was no Subtitle style + static_cast(mxStyleSheetPool.get())->CreatePseudosIfNecessary(); + } + + // Set default style of Drawing Engine + OUString aName( SdResId(STR_STANDARD_STYLESHEET_NAME)); + SetDefaultStyleSheet(static_cast(mxStyleSheetPool->Find(aName, SfxStyleFamily::Para))); + + // #i119287# Set default StyleSheet for SdrGrafObj and SdrOle2Obj + SetDefaultStyleSheetForSdrGrafObjAndSdrOle2Obj(static_cast(mxStyleSheetPool->Find(SdResId(STR_POOLSHEET_OBJNOLINENOFILL), SfxStyleFamily::Para))); + + // Initialize DrawOutliner and DocumentOutliner, but don't initialize the + // global outliner, as it is not document specific like StyleSheetPool and + // StyleRequestHandler are. + ::Outliner& rDrawOutliner = GetDrawOutliner(); + rDrawOutliner.SetStyleSheetPool(static_cast(GetStyleSheetPool())); + EEControlBits nCntrl = rDrawOutliner.GetControlWord(); + if (mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + rDrawOutliner.SetControlWord(nCntrl); + + // Initialize HitTestOutliner and DocumentOutliner, but don't initialize the + // global outliner, as it is not document specific like StyleSheetPool and + // StyleRequestHandler are. + m_pHitTestOutliner->SetStyleSheetPool(static_cast(GetStyleSheetPool())); + + if(mpOutliner) + { + mpOutliner->SetStyleSheetPool(static_cast(GetStyleSheetPool())); + } + if(mpInternalOutliner) + { + mpInternalOutliner->SetStyleSheetPool(static_cast(GetStyleSheetPool())); + } + + if ( eMode == DocCreationMode::Loaded ) + { + // Make presentation objects listeners of the appropriate styles + SdStyleSheetPool* pSPool = static_cast( GetStyleSheetPool() ); + sal_uInt16 nPage, nPageCount; + + // create missing layout style sheets for broken documents + // that were created with the 5.2 + nPageCount = GetMasterSdPageCount( PageKind::Standard ); + for (nPage = 0; nPage < nPageCount; nPage++) + { + SdPage* pPage = GetMasterSdPage(nPage, PageKind::Standard); + pSPool->CreateLayoutStyleSheets( pPage->GetName(), true ); + } + + // Default and notes pages: + for (nPage = 0; nPage < GetPageCount(); nPage++) + { + SdPage* pPage = static_cast(GetPage(nPage)); + NewOrLoadCompleted( pPage, pSPool ); + } + + // Master pages: + for (nPage = 0; nPage < GetMasterPageCount(); nPage++) + { + SdPage* pPage = static_cast(GetMasterPage(nPage)); + + NewOrLoadCompleted( pPage, pSPool ); + } + } + + mbNewOrLoadCompleted = true; + UpdateAllLinks(); + SetChanged( false ); +} + +/** updates all links, only links in this document should by resolved */ +void SdDrawDocument::UpdateAllLinks() +{ + if (s_pDocLockedInsertingLinks || !m_pLinkManager || m_pLinkManager->GetLinks().empty()) + return; + + s_pDocLockedInsertingLinks = this; // lock inserting links. only links in this document should by resolved + + if (mpDocSh) + { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = mpDocSh->getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(true); + } + + m_pLinkManager->UpdateAllLinks(true, false, nullptr); // query box: update all links? + + if (s_pDocLockedInsertingLinks == this) + s_pDocLockedInsertingLinks = nullptr; // unlock inserting links +} + +/** this loops over the presentation objects of a page and repairs some new settings + from old binary files and resets all default strings for empty presentation objects. +*/ +void SdDrawDocument::NewOrLoadCompleted( SdPage* pPage, SdStyleSheetPool* pSPool ) +{ + sd::ShapeList& rPresentationShapes( pPage->GetPresentationShapeList() ); + if(rPresentationShapes.isEmpty()) + return; + + // Create lists of title and outline styles + OUString aName = pPage->GetLayoutName(); + aName = aName.copy( 0, aName.indexOf( SD_LT_SEPARATOR ) ); + + std::vector aOutlineList; + pSPool->CreateOutlineSheetList(aName,aOutlineList); + + SfxStyleSheet* pTitleSheet = static_cast(pSPool->GetTitleSheet(aName)); + + SdrObject* pObj = nullptr; + rPresentationShapes.seekShape(0); + + // Now look for title and outline text objects, then make those objects + // listeners. + while( (pObj = rPresentationShapes.getNextShape()) ) + { + if (pObj->GetObjInventor() == SdrInventor::Default) + { + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + SdrObjKind nId = pObj->GetObjIdentifier(); + + if (nId == SdrObjKind::TitleText) + { + if( pOPO && pOPO->GetOutlinerMode() == OutlinerMode::DontKnow ) + pOPO->SetOutlinerMode( OutlinerMode::TitleObject ); + + // sal_True: don't delete "hard" attributes when doing this. + if (pTitleSheet) + pObj->SetStyleSheet(pTitleSheet, true); + } + else if (nId == SdrObjKind::OutlineText) + { + if( pOPO && pOPO->GetOutlinerMode() == OutlinerMode::DontKnow ) + pOPO->SetOutlinerMode( OutlinerMode::OutlineObject ); + + std::vector::iterator iter; + for (iter = aOutlineList.begin(); iter != aOutlineList.end(); ++iter) + { + SfxStyleSheet* pSheet = static_cast(*iter); + + if (pSheet) + { + pObj->StartListening(*pSheet); + + if( iter == aOutlineList.begin()) + // text frame listens to stylesheet of layer 1 + pObj->NbcSetStyleSheet(pSheet, true); + } + } + } + + if( auto pTextObj = dynamic_cast( pObj ) ) + if (pTextObj->IsEmptyPresObj()) + { + PresObjKind ePresObjKind = pPage->GetPresObjKind(pObj); + OUString aString( pPage->GetPresObjText(ePresObjKind) ); + + if (!aString.isEmpty()) + { + SdOutliner* pInternalOutl = GetInternalOutliner(); + pPage->SetObjText( pTextObj, pInternalOutl, ePresObjKind, aString ); + pObj->NbcSetStyleSheet( pPage->GetStyleSheetForPresObj( ePresObjKind ), true ); + pInternalOutl->Clear(); + } + } + } + } +} + +// Local outliner that is used for outline mode. In this outliner, OutlinerViews +// may be inserted. +SdOutliner* SdDrawDocument::GetOutliner(bool bCreateOutliner) +{ + if (!mpOutliner && bCreateOutliner) + { + mpOutliner.reset(new SdOutliner( this, OutlinerMode::TextObject )); + + if (mpDocSh) + mpOutliner->SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + mpOutliner->SetDefTab( m_nDefaultTabulator ); + mpOutliner->SetStyleSheetPool(static_cast(GetStyleSheetPool())); + } + + return mpOutliner.get(); +} + +// Internal outliner that is used to create text objects. We don't insert any +// OutlinerViews into this outliner! +SdOutliner* SdDrawDocument::GetInternalOutliner(bool bCreateOutliner) +{ + if ( !mpInternalOutliner && bCreateOutliner ) + { + mpInternalOutliner.reset( new SdOutliner( this, OutlinerMode::TextObject ) ); + + // This outliner is only used to create special text objects. As no + // information about portions is saved in this outliner, the update mode + // can/should always remain sal_False. + mpInternalOutliner->SetUpdateLayout( false ); + mpInternalOutliner->EnableUndo( false ); + + if (mpDocSh) + mpInternalOutliner->SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + mpInternalOutliner->SetDefTab( m_nDefaultTabulator ); + mpInternalOutliner->SetStyleSheetPool(static_cast(GetStyleSheetPool())); + } + + DBG_ASSERT( !mpInternalOutliner || ( ! mpInternalOutliner->IsUpdateLayout() ) , "InternalOutliner: UpdateMode = sal_True !" ); + DBG_ASSERT( !mpInternalOutliner || ( ! mpInternalOutliner->IsUndoEnabled() ), "InternalOutliner: Undo = sal_True !" ); + + // If you add stuff here, always clear it out. + // Advantages: + // a) no unnecessary Clear calls + // b) no wasted memory + DBG_ASSERT( !mpInternalOutliner || ( ( mpInternalOutliner->GetParagraphCount() == 1 ) && ( mpInternalOutliner->GetText( mpInternalOutliner->GetParagraph( 0 ) ).isEmpty() ) ), "InternalOutliner: not empty!" ); + + return mpInternalOutliner.get(); +} + +// OnlineSpelling on/off +void SdDrawDocument::SetOnlineSpell(bool bIn) +{ + mbOnlineSpell = bIn; + EEControlBits nCntrl; + + if(mpOutliner) + { + nCntrl = mpOutliner->GetControlWord(); + + if(mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + mpOutliner->SetControlWord(nCntrl); + } + + if (mpInternalOutliner) + { + nCntrl = mpInternalOutliner->GetControlWord(); + + if (mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + mpInternalOutliner->SetControlWord(nCntrl); + } + + ::Outliner& rOutliner = GetDrawOutliner(); + + nCntrl = rOutliner.GetControlWord(); + + if (mbOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + rOutliner.SetControlWord(nCntrl); + + if (mbOnlineSpell) + { + StartOnlineSpelling(); + } + else + { + StopOnlineSpelling(); + } +} + +// OnlineSpelling: highlighting on/off +uno::Reference< uno::XInterface > SdDrawDocument::createUnoModel() +{ + uno::Reference< uno::XInterface > xModel; + + try + { + if ( mpDocSh ) + xModel = mpDocSh->GetModel(); + } + catch( uno::RuntimeException& ) + { + } + + return xModel; +} + +SvxNumType SdDrawDocument::GetPageNumType() const +{ + return mePageNumType; +} + +void SdDrawDocument::SetPrinterIndependentLayout (sal_Int32 nMode) +{ + switch (nMode) + { + case css::document::PrinterIndependentLayout::DISABLED: + case css::document::PrinterIndependentLayout::ENABLED: + // Just store supported modes and inform the doc shell + mnPrinterIndependentLayout = nMode; + + // Since it is possible that a SdDrawDocument is constructed without a + // SdDrawDocShell the pointer member mpDocSh needs to be tested + // before the call is executed. This is e. g. used for copy/paste. + if(mpDocSh) + { + mpDocSh->UpdateRefDevice (); + } + + break; + + default: + // Ignore unknown values + break; + } +} + +void SdDrawDocument::SetStartWithPresentation( bool bStartWithPresentation ) +{ + mbStartWithPresentation = bStartWithPresentation; +} + +void SdDrawDocument::SetExitAfterPresenting( bool bExitAfterPresenting ) +{ + mbExitAfterPresenting = bExitAfterPresenting; +} + +void SdDrawDocument::PageListChanged() +{ + mpDrawPageListWatcher->Invalidate(); +} + +void SdDrawDocument::MasterPageListChanged() +{ + mpMasterPageListWatcher->Invalidate(); +} + +void SdDrawDocument::SetCalcFieldValueHdl(::Outliner* pOutliner) +{ + pOutliner->SetCalcFieldValueHdl(LINK(SD_MOD(), SdModule, CalcFieldValueHdl)); +} + +sal_uInt16 SdDrawDocument::GetAnnotationAuthorIndex( const OUString& rAuthor ) +{ + // force current user to have first color + if( maAnnotationAuthors.empty() ) + { + SvtUserOptions aUserOptions; + maAnnotationAuthors.push_back( aUserOptions.GetFullName() ); + } + + auto iter = std::find(maAnnotationAuthors.begin(), maAnnotationAuthors.end(), rAuthor); + sal_uInt16 idx = static_cast(std::distance(maAnnotationAuthors.begin(), iter)); + + if( idx == maAnnotationAuthors.size() ) + { + maAnnotationAuthors.push_back( rAuthor ); + } + + return idx; +} + +void SdDrawDocument::InitLayoutVector() +{ + if (utl::ConfigManager::IsFuzzing()) + return; + + const Reference xContext( + ::comphelper::getProcessComponentContext() ); + + // get file list from configuration + const Sequence< OUString > aFiles( + officecfg::Office::Impress::Misc::LayoutListFiles::get() ); + + if (aFiles.getLength() == 0) + return; + const Reference xDocBuilder = DocumentBuilder::create( xContext ); + + for( const auto& rFile : aFiles ) + { + OUString sFilename = comphelper::getExpandedUri(xContext, rFile); + + // load layout file into DOM + + try + { + // loop over every layout entry in current file + const Reference xDoc = xDocBuilder->parseURI( sFilename ); + const Reference layoutlist = xDoc->getElementsByTagName("layout"); + const int nElements = layoutlist->getLength(); + for(int index=0; index < nElements; index++) + maLayoutInfo.push_back( layoutlist->item(index) ); + } + catch (const uno::Exception &) + { + // skip missing config. files + } + } +} + +void SdDrawDocument::InitObjectVector() +{ + if (utl::ConfigManager::IsFuzzing()) + return; + + const Reference xContext( + ::comphelper::getProcessComponentContext() ); + + // get file list from configuration + const Sequence< OUString > aFiles( + officecfg::Office::Impress::Misc::PresObjListFiles::get() ); + + if (aFiles.getLength() == 0) + return; + const Reference xDocBuilder = DocumentBuilder::create( xContext ); + for( const auto& rFile : aFiles ) + { + OUString sFilename = comphelper::getExpandedUri(xContext, rFile); + + // load presentation object file into DOM + + try + { + // loop over every object entry in current file + const Reference xDoc = xDocBuilder->parseURI( sFilename ); + const Reference objectlist = xDoc->getElementsByTagName("object"); + const int nElements = objectlist->getLength(); + for(int index=0; index < nElements; index++) + maPresObjectInfo.push_back( objectlist->item(index) ); + } + catch (const uno::Exception &) + { + // skip missing config. files + } + } +} + +void SdDrawDocument::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + bool bOwns = false; + if (!pWriter) + { + pWriter = xmlNewTextWriterFilename("model.xml", 0); + xmlTextWriterSetIndent(pWriter,1); + (void)xmlTextWriterSetIndentString(pWriter, BAD_CAST(" ")); + (void)xmlTextWriterStartDocument(pWriter, nullptr, nullptr, nullptr); + bOwns = true; + } + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdDrawDocument")); + (void)xmlTextWriterWriteFormatAttribute(pWriter, BAD_CAST("ptr"), "%p", this); + + if (mpOutliner) + mpOutliner->dumpAsXml(pWriter); + FmFormModel::dumpAsXml(pWriter); + if (GetUndoManager()) + GetUndoManager()->dumpAsXml(pWriter); + + (void)xmlTextWriterEndElement(pWriter); + if (bOwns) + { + (void)xmlTextWriterEndDocument(pWriter); + xmlFreeTextWriter(pWriter); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc2.cxx b/sd/source/core/drawdoc2.cxx new file mode 100644 index 000000000..d0187bab0 --- /dev/null +++ b/sd/source/core/drawdoc2.cxx @@ -0,0 +1,1382 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "PageListWatcher.hxx" +#include + +using namespace ::sd; + +const ::tools::Long PRINT_OFFSET = 30; // see /svx/source/dialog/page.cxx + +using namespace com::sun::star; + +// Looks up an object by name +SdrObject* SdDrawDocument::GetObj(std::u16string_view rObjName) const +{ + SdrObject* pObj = nullptr; + SdrObject* pObjFound = nullptr; + const SdPage* pPage = nullptr; + + // First search in all pages + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = GetPageCount(); + + while (nPage < nMaxPages && !pObjFound) + { + pPage = static_cast( GetPage(nPage) ); + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups); + + while (aIter.IsMore() && !pObjFound) + { + pObj = aIter.Next(); + + if( ( pObj->GetName() == rObjName ) || + ( SdrInventor::Default == pObj->GetObjInventor() && + SdrObjKind::OLE2 == pObj->GetObjIdentifier() && + rObjName == static_cast< SdrOle2Obj* >( pObj )->GetPersistName() ) ) + { + pObjFound = pObj; + } + } + + nPage++; + } + + // If it couldn't be found, look through all master pages + nPage = 0; + const sal_uInt16 nMaxMasterPages = GetMasterPageCount(); + + while (nPage < nMaxMasterPages && !pObjFound) + { + pPage = static_cast( GetMasterPage(nPage) ); + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups); + + while (aIter.IsMore() && !pObjFound) + { + pObj = aIter.Next(); + + if( ( pObj->GetName() == rObjName ) || + ( SdrInventor::Default == pObj->GetObjInventor() && + SdrObjKind::OLE2 == pObj->GetObjIdentifier() && + rObjName == static_cast< SdrOle2Obj* >( pObj )->GetPersistName() ) ) + { + pObjFound = pObj; + } + } + + nPage++; + } + + return pObjFound; +} + +// Find SdPage by name +sal_uInt16 SdDrawDocument::GetPageByName(std::u16string_view rPgName, bool& rbIsMasterPage) const +{ + SdPage* pPage = nullptr; + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = GetPageCount(); + sal_uInt16 nPageNum = SDRPAGE_NOTFOUND; + + rbIsMasterPage = false; + + // Search all regular pages and all notes pages (handout pages are + // ignored) + while (nPage < nMaxPages && nPageNum == SDRPAGE_NOTFOUND) + { + pPage = const_cast(static_cast( + GetPage(nPage))); + + if (pPage != nullptr + && pPage->GetPageKind() != PageKind::Handout + && pPage->GetName() == rPgName) + { + nPageNum = nPage; + } + + nPage++; + } + + // Search all master pages when not found among non-master pages + const sal_uInt16 nMaxMasterPages = GetMasterPageCount(); + nPage = 0; + + while (nPage < nMaxMasterPages && nPageNum == SDRPAGE_NOTFOUND) + { + pPage = const_cast(static_cast( + GetMasterPage(nPage))); + + if (pPage && pPage->GetName() == rPgName) + { + nPageNum = nPage; + rbIsMasterPage = true; + } + + nPage++; + } + + return nPageNum; +} + +bool SdDrawDocument::IsPageNameUnique( std::u16string_view rPgName ) const +{ + sal_uInt16 nCount = 0; + SdPage* pPage = nullptr; + + // Search all regular pages and all notes pages (handout pages are ignored) + sal_uInt16 nPage = 0; + sal_uInt16 nMaxPages = GetPageCount(); + while (nPage < nMaxPages) + { + pPage = const_cast(static_cast(GetPage(nPage))); + + if (pPage && pPage->GetName() == rPgName && pPage->GetPageKind() != PageKind::Handout) + nCount++; + + nPage++; + } + + // Search all master pages + nPage = 0; + nMaxPages = GetMasterPageCount(); + while (nPage < nMaxPages) + { + pPage = const_cast(static_cast(GetMasterPage(nPage))); + + if (pPage && pPage->GetName() == rPgName) + nCount++; + + nPage++; + } + + return nCount == 1; +} + +SdPage* SdDrawDocument::GetSdPage(sal_uInt16 nPgNum, PageKind ePgKind) const +{ + return mpDrawPageListWatcher->GetSdPage(ePgKind, sal_uInt32(nPgNum)); +} + +sal_uInt16 SdDrawDocument::GetSdPageCount(PageKind ePgKind) const +{ + return static_cast(mpDrawPageListWatcher->GetSdPageCount(ePgKind)); +} + +SdPage* SdDrawDocument::GetMasterSdPage(sal_uInt16 nPgNum, PageKind ePgKind) +{ + return mpMasterPageListWatcher->GetSdPage(ePgKind, sal_uInt32(nPgNum)); +} + +sal_uInt16 SdDrawDocument::GetMasterSdPageCount(PageKind ePgKind) const +{ + return static_cast(mpMasterPageListWatcher->GetSdPageCount(ePgKind)); +} + +sal_uInt16 SdDrawDocument::GetActiveSdPageCount() const +{ + return static_cast(mpDrawPageListWatcher->GetVisibleSdPageCount()); +} + +// Adapt the page numbers that are registered in the page objects of the notes +// pages +void SdDrawDocument::UpdatePageObjectsInNotes(sal_uInt16 nStartPos) +{ + sal_uInt16 nPageCount = GetPageCount(); + SdPage* pPage = nullptr; + + for (sal_uInt16 nPage = nStartPos; nPage < nPageCount; nPage++) + { + pPage = static_cast( GetPage(nPage) ); + + // If this is a notes page, find its page object and correct the page + // number + if (pPage && pPage->GetPageKind() == PageKind::Notes) + { + const size_t nObjCount = pPage->GetObjCount(); + for (size_t nObj = 0; nObj < nObjCount; ++nObj) + { + SdrObject* pObj = pPage->GetObj(nObj); + if (pObj->GetObjIdentifier() == SdrObjKind::Page && + pObj->GetObjInventor() == SdrInventor::Default) + { + // The page object is the preceding page (drawing page) + SAL_WARN_IF(!nStartPos, "sd", "Position of notes page must not be 0."); + + SAL_WARN_IF(nPage <= 1, "sd", "Page object must not be a handout."); + + if (nStartPos > 0 && nPage > 1) + static_cast(pObj)->SetReferencedPage(GetPage(nPage - 1)); + } + } + } + } +} + +void SdDrawDocument::UpdatePageRelativeURLs(const OUString& rOldName, std::u16string_view rNewName) +{ + if (rNewName.empty()) + return; + + SfxItemPool& rPool(GetPool()); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(EE_FEATURE_FIELD)) + { + const SvxFieldItem* pFldItem = dynamic_cast< const SvxFieldItem * > (pItem); + + if(pFldItem) + { + SvxURLField* pURLField = const_cast< SvxURLField* >( dynamic_cast( pFldItem->GetField() ) ); + + if(pURLField) + { + OUString aURL = pURLField->GetURL(); + + if (!aURL.isEmpty() && (aURL[0] == 35) && (aURL.indexOf(rOldName, 1) == 1)) + { + if (aURL.getLength() == rOldName.getLength() + 1) // standard page name + { + aURL = aURL.replaceAt(1, aURL.getLength() - 1, u"") + + rNewName; + pURLField->SetURL(aURL); + } + else + { + const OUString sNotes(SdResId(STR_NOTES)); + if (aURL.getLength() == rOldName.getLength() + 2 + sNotes.getLength() + && aURL.indexOf(sNotes, rOldName.getLength() + 2) == rOldName.getLength() + 2) + { + aURL = aURL.replaceAt(1, aURL.getLength() - 1, u"") + + rNewName + " " + sNotes; + pURLField->SetURL(aURL); + } + } + } + } + } + } +} + +void SdDrawDocument::UpdatePageRelativeURLs(SdPage const * pPage, sal_uInt16 nPos, sal_Int32 nIncrement) +{ + bool bNotes = (pPage->GetPageKind() == PageKind::Notes); + + SfxItemPool& rPool(GetPool()); + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(EE_FEATURE_FIELD)) + { + const SvxFieldItem* pFldItem; + + if ((pFldItem = dynamic_cast< const SvxFieldItem * > (pItem)) != nullptr) + { + SvxURLField* pURLField = const_cast< SvxURLField* >( dynamic_cast( pFldItem->GetField() ) ); + + if(pURLField) + { + OUString aURL = pURLField->GetURL(); + + if (!aURL.isEmpty() && (aURL[0] == 35)) + { + OUString aHashSlide = "#" + SdResId(STR_PAGE); + + if (aURL.startsWith(aHashSlide)) + { + OUString aURLCopy = aURL; + const OUString sNotes(SdResId(STR_NOTES)); + + aURLCopy = aURLCopy.replaceAt(0, aHashSlide.getLength(), u""); + + bool bNotesLink = ( aURLCopy.getLength() >= sNotes.getLength() + 3 + && aURLCopy.endsWith(sNotes) ); + + if (bNotesLink != bNotes) + continue; // no compatible link and page + + if (bNotes) + aURLCopy = aURLCopy.replaceAt(aURLCopy.getLength() - sNotes.getLength(), sNotes.getLength(), u""); + + sal_Int32 number = aURLCopy.toInt32(); + sal_uInt16 realPageNumber = (nPos + 1)/ 2; + + if ( number >= realPageNumber ) + { + // update link page number + number += nIncrement; + aURL = aURL.replaceAt(aHashSlide.getLength() + 1, aURL.getLength() - aHashSlide.getLength() - 1, u"") + + OUString::number(number); + if (bNotes) + { + aURL += " " + sNotes; + } + pURLField->SetURL(aURL); + } + } + } + } + } + } +} + +// Move page +void SdDrawDocument::MovePage(sal_uInt16 nPgNum, sal_uInt16 nNewPos) +{ + FmFormModel::MovePage(nPgNum, nNewPos); + + sal_uInt16 nMin = std::min(nPgNum, nNewPos); + + UpdatePageObjectsInNotes(nMin); +} + +// Insert page +void SdDrawDocument::InsertPage(SdrPage* pPage, sal_uInt16 nPos) +{ + bool bLast = (nPos == GetPageCount()); + + FmFormModel::InsertPage(pPage, nPos); + + static_cast(pPage)->ConnectLink(); + + UpdatePageObjectsInNotes(nPos); + + if (!bLast) + UpdatePageRelativeURLs(static_cast( pPage ), nPos, 1); + + if (comphelper::LibreOfficeKit::isActive() && static_cast(pPage)->GetPageKind() == PageKind::Standard) + { + SdXImpressDocument* pDoc = comphelper::getFromUnoTunnel(this->getUnoModel()); + SfxLokHelper::notifyDocumentSizeChangedAllViews(pDoc); + } +} + +// Delete page +void SdDrawDocument::DeletePage(sal_uInt16 nPgNum) +{ + FmFormModel::DeletePage(nPgNum); + + UpdatePageObjectsInNotes(nPgNum); +} + +// Remove page +rtl::Reference SdDrawDocument::RemovePage(sal_uInt16 nPgNum) +{ + rtl::Reference pPage = FmFormModel::RemovePage(nPgNum); + + bool bLast = ((nPgNum+1)/2 == (GetPageCount()+1)/2); + + auto pSdPage = static_cast(pPage.get()); + pSdPage->DisconnectLink(); + ReplacePageInCustomShows( pSdPage, nullptr ); + UpdatePageObjectsInNotes(nPgNum); + + if (!bLast) + UpdatePageRelativeURLs(pSdPage, nPgNum, -1); + + if (comphelper::LibreOfficeKit::isActive() && pSdPage->GetPageKind() == PageKind::Standard) + { + SdXImpressDocument* pDoc = comphelper::getFromUnoTunnel(this->getUnoModel()); + SfxLokHelper::notifyDocumentSizeChangedAllViews(pDoc); + } + + return pPage; +} + +// Warning: This is not called for new master pages created from SdrModel::Merge, +// you also have to modify code in SdDrawDocument::Merge! +void SdDrawDocument::InsertMasterPage(SdrPage* pPage, sal_uInt16 nPos ) +{ + FmFormModel::InsertMasterPage( pPage, nPos ); + if( pPage->IsMasterPage() && (static_cast(pPage)->GetPageKind() == PageKind::Standard) ) + { + // new master page created, add its style family + SdStyleSheetPool* pStylePool = static_cast( GetStyleSheetPool() ); + if( pStylePool ) + pStylePool->AddStyleFamily( static_cast(pPage) ); + } +} + +rtl::Reference SdDrawDocument::RemoveMasterPage(sal_uInt16 nPgNum) +{ + SdPage* pPage = static_cast(GetMasterPage(nPgNum )); + if( pPage && pPage->IsMasterPage() && (pPage->GetPageKind() == PageKind::Standard) ) + { + // master page removed, remove its style family + SdStyleSheetPool* pStylePool = static_cast( GetStyleSheetPool() ); + if( pStylePool ) + pStylePool->RemoveStyleFamily( pPage ); + } + + return FmFormModel::RemoveMasterPage(nPgNum); +} + +//Select pages +void SdDrawDocument::SetSelected(SdPage* pPage, bool bSelect) +{ + PageKind ePageKind = pPage->GetPageKind(); + + if (ePageKind == PageKind::Standard) + { + pPage->SetSelected(bSelect); + + const sal_uInt16 nDestPageNum(pPage->GetPageNum() + 1); + SdPage* pNotesPage = nullptr; + + if(nDestPageNum < GetPageCount()) + { + pNotesPage = static_cast(GetPage(nDestPageNum)); + } + + if (pNotesPage && pNotesPage->GetPageKind() == PageKind::Notes) + { + pNotesPage->SetSelected(bSelect); + } + } + else if (ePageKind == PageKind::Notes) + { + pPage->SetSelected(bSelect); + SdPage* pStandardPage = static_cast( GetPage( pPage->GetPageNum() - 1 ) ); + + if (pStandardPage && pStandardPage->GetPageKind() == PageKind::Standard) + pStandardPage->SetSelected(bSelect); + } +} + +// If no pages exist yet, create them now +void SdDrawDocument::CreateFirstPages( SdDrawDocument const * pRefDocument /* = 0 */ ) +{ + // If no page exists yet in the model, (File -> New), insert a page + sal_uInt16 nPageCount = GetPageCount(); + + if (nPageCount > 1) + return; + + // #i57181# Paper size depends on Language, like in Writer + Size aDefSize = SvxPaperInfo::GetDefaultPaperSize( MapUnit::Map100thMM ); + + // Insert handout page + rtl::Reference pHandoutPage = AllocSdPage(false); + + SdPage* pRefPage = nullptr; + + if( pRefDocument ) + pRefPage = pRefDocument->GetSdPage( 0, PageKind::Handout ); + + if( pRefPage ) + { + pHandoutPage->SetSize(pRefPage->GetSize()); + pHandoutPage->SetBorder( pRefPage->GetLeftBorder(), pRefPage->GetUpperBorder(), pRefPage->GetRightBorder(), pRefPage->GetLowerBorder() ); + } + else + { + pHandoutPage->SetSize(aDefSize); + pHandoutPage->SetBorder(0, 0, 0, 0); + } + + pHandoutPage->SetPageKind(PageKind::Handout); + pHandoutPage->SetName( SdResId(STR_HANDOUT) ); + InsertPage(pHandoutPage.get(), 0); + + // Insert master page and register this with the handout page + rtl::Reference pHandoutMPage = AllocSdPage(true); + pHandoutMPage->SetSize( pHandoutPage->GetSize() ); + pHandoutMPage->SetPageKind(PageKind::Handout); + pHandoutMPage->SetBorder( pHandoutPage->GetLeftBorder(), + pHandoutPage->GetUpperBorder(), + pHandoutPage->GetRightBorder(), + pHandoutPage->GetLowerBorder() ); + InsertMasterPage(pHandoutMPage.get(), 0); + pHandoutPage->TRG_SetMasterPage( *pHandoutMPage ); + + // Insert page + // If nPageCount==1 is, the model for the clipboard was created, thus a + // default page must already exist + rtl::Reference pPage; + bool bClipboard = false; + + if( pRefDocument ) + pRefPage = pRefDocument->GetSdPage( 0, PageKind::Standard ); + + if (nPageCount == 0) + { + pPage = AllocSdPage(false); + + if( pRefPage ) + { + pPage->SetSize( pRefPage->GetSize() ); + pPage->SetBorder( pRefPage->GetLeftBorder(), pRefPage->GetUpperBorder(), pRefPage->GetRightBorder(), pRefPage->GetLowerBorder() ); + } + else if (meDocType == DocumentType::Draw) + { + // Draw: always use default size with margins + pPage->SetSize(aDefSize); + + SfxPrinter* pPrinter = mpDocSh->GetPrinter(false); + if (pPrinter && pPrinter->IsValid()) + { + Size aOutSize(pPrinter->GetOutputSize()); + Point aPageOffset(pPrinter->GetPageOffset()); + aPageOffset -= pPrinter->PixelToLogic( Point() ); + ::tools::Long nOffset = !aPageOffset.X() && !aPageOffset.Y() ? 0 : PRINT_OFFSET; + + sal_uLong nTop = aPageOffset.Y(); + sal_uLong nLeft = aPageOffset.X(); + sal_uLong nBottom = std::max(::tools::Long(aDefSize.Height() - aOutSize.Height() - nTop + nOffset), ::tools::Long(0)); + sal_uLong nRight = std::max(::tools::Long(aDefSize.Width() - aOutSize.Width() - nLeft + nOffset), ::tools::Long(0)); + + pPage->SetBorder(nLeft, nTop, nRight, nBottom); + } + else + { + // The printer is not available. Use a border of 10mm + // on each side instead. + // This has to be kept synchronized with the border + // width set in the + // SvxPageDescPage::PaperSizeSelect_Impl callback. + pPage->SetBorder(1000, 1000, 1000, 1000); + } + } + else + { + // Impress: always use screen format, landscape. + Size aSz( SvxPaperInfo::GetPaperSize(PAPER_SCREEN_16_9, MapUnit::Map100thMM) ); + pPage->SetSize( Size( aSz.Height(), aSz.Width() ) ); + pPage->SetBorder(0, 0, 0, 0); + } + + InsertPage(pPage.get(), 1); + } + else + { + bClipboard = true; + pPage = static_cast( GetPage(1) ); + } + + // Insert master page, then register this with the page + rtl::Reference pMPage = AllocSdPage(true); + pMPage->SetSize( pPage->GetSize() ); + pMPage->SetBorder( pPage->GetLeftBorder(), + pPage->GetUpperBorder(), + pPage->GetRightBorder(), + pPage->GetLowerBorder() ); + InsertMasterPage(pMPage.get(), 1); + pPage->TRG_SetMasterPage( *pMPage ); + if( bClipboard ) + pMPage->SetLayoutName( pPage->GetLayoutName() ); + + // Insert notes page + rtl::Reference pNotesPage = AllocSdPage(false); + + if( pRefDocument ) + pRefPage = pRefDocument->GetSdPage( 0, PageKind::Notes ); + + if( pRefPage ) + { + pNotesPage->SetSize( pRefPage->GetSize() ); + pNotesPage->SetBorder( pRefPage->GetLeftBorder(), pRefPage->GetUpperBorder(), pRefPage->GetRightBorder(), pRefPage->GetLowerBorder() ); + } + else + { + // Always use portrait format + if (aDefSize.Height() >= aDefSize.Width()) + { + pNotesPage->SetSize(aDefSize); + } + else + { + pNotesPage->SetSize( Size(aDefSize.Height(), aDefSize.Width()) ); + } + + pNotesPage->SetBorder(0, 0, 0, 0); + } + pNotesPage->SetPageKind(PageKind::Notes); + InsertPage(pNotesPage.get(), 2); + if( bClipboard ) + pNotesPage->SetLayoutName( pPage->GetLayoutName() ); + + // Insert master page, then register this with the notes page + rtl::Reference pNotesMPage = AllocSdPage(true); + pNotesMPage->SetSize( pNotesPage->GetSize() ); + pNotesMPage->SetPageKind(PageKind::Notes); + pNotesMPage->SetBorder( pNotesPage->GetLeftBorder(), + pNotesPage->GetUpperBorder(), + pNotesPage->GetRightBorder(), + pNotesPage->GetLowerBorder() ); + InsertMasterPage(pNotesMPage.get(), 2); + pNotesPage->TRG_SetMasterPage( *pNotesMPage ); + if( bClipboard ) + pNotesMPage->SetLayoutName( pPage->GetLayoutName() ); + + if( !pRefPage && (meDocType != DocumentType::Draw) ) + pPage->SetAutoLayout( AUTOLAYOUT_TITLE, true, true ); + + mpWorkStartupTimer.reset( new Timer("DrawWorkStartupTimer") ); + mpWorkStartupTimer->SetInvokeHandler( LINK(this, SdDrawDocument, WorkStartupHdl) ); + mpWorkStartupTimer->SetTimeout(2000); + mpWorkStartupTimer->Start(); + + SetChanged(false); +} + +// Creates missing notes and handout pages (after PowerPoint import). +// We assume that at least one default page and one default master page exist. + +bool SdDrawDocument::CreateMissingNotesAndHandoutPages() +{ + bool bOK = false; + sal_uInt16 nPageCount = GetPageCount(); + + if (nPageCount != 0) + { + // Set PageKind + SdPage* pHandoutMPage = static_cast( GetMasterPage(0) ); + pHandoutMPage->SetPageKind(PageKind::Handout); + + SdPage* pHandoutPage = static_cast( GetPage(0) ); + pHandoutPage->SetPageKind(PageKind::Handout); + pHandoutPage->TRG_SetMasterPage( *pHandoutMPage ); + + for (sal_uInt16 i = 1; i < nPageCount; i = i + 2) + { + SdPage* pPage = static_cast( GetPage(i) ); + + if(!pPage->TRG_HasMasterPage()) + { + // No master page set -> use first default master page + // (If there was no default page in the PPT) + pPage->TRG_SetMasterPage(*GetMasterPage(1)); + } + + SdPage* pNotesPage = static_cast( GetPage(i+1) ); + pNotesPage->SetPageKind(PageKind::Notes); + + // Set notes master page + sal_uInt16 nMasterPageAfterPagesMasterPage = pPage->TRG_GetMasterPage().GetPageNum() + 1; + pNotesPage->TRG_SetMasterPage(*GetMasterPage(nMasterPageAfterPagesMasterPage)); + } + + bOK = true; + StopWorkStartupDelay(); + SetChanged(false); + } + + return bOK; +} + +void SdDrawDocument::UnselectAllPages() +{ + sal_uInt16 nNoOfPages = GetSdPageCount(PageKind::Standard); + for (sal_uInt16 nPage = 0; nPage < nNoOfPages; ++nPage) + { + SdPage* pPage = GetSdPage(nPage, PageKind::Standard); + pPage->SetSelected(false); + } +} + +// + Move selected pages after said page +// (nTargetPage = (sal_uInt16)-1 --> move before first page) +// + Returns sal_True when the page has been moved +bool SdDrawDocument::MovePages(sal_uInt16 nTargetPage) +{ + SdPage* pPage = nullptr; + sal_uInt16 nPage; + sal_uInt16 nNoOfPages = GetSdPageCount(PageKind::Standard); + bool bSomethingHappened = false; + + const bool bUndo = IsUndoEnabled(); + + if( bUndo ) + BegUndo(SdResId(STR_UNDO_MOVEPAGES)); + + // List of selected pages + std::vector aPageList; + for (nPage = 0; nPage < nNoOfPages; nPage++) + { + pPage = GetSdPage(nPage, PageKind::Standard); + + if (pPage->IsSelected()) { + aPageList.push_back(pPage); + } + } + + // If necessary, look backwards, until we find a page that wasn't selected + nPage = nTargetPage; + + if (nPage != sal_uInt16(-1)) + { + pPage = GetSdPage(nPage, PageKind::Standard); + while (nPage > 0 && pPage->IsSelected()) + { + nPage--; + pPage = GetSdPage(nPage, PageKind::Standard); + } + + if (pPage->IsSelected()) + { + nPage = sal_uInt16(-1); + } + } + + // Insert before the first page + if (nPage == sal_uInt16(-1)) + { + std::vector::reverse_iterator iter; + for (iter = aPageList.rbegin(); iter != aPageList.rend(); ++iter) + { + nPage = (*iter)->GetPageNum(); + if (nPage != 0) + { + SdrPage* pPg = GetPage(nPage); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage, 1)); + MovePage(nPage, 1); + pPg = GetPage(nPage+1); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage+1, 2)); + MovePage(nPage+1, 2); + bSomethingHappened = true; + } + } + } + // Insert after + else + { + nTargetPage = 2 * nPage + 1; // PageKind::Standard --> absolute + + for (const auto& rpPage : aPageList) + { + nPage = rpPage->GetPageNum(); + if (nPage > nTargetPage) + { + nTargetPage += 2; // Insert _after_ the page + + if (nPage != nTargetPage) + { + SdrPage* pPg = GetPage(nPage); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage, nTargetPage)); + MovePage(nPage, nTargetPage); + pPg = GetPage(nPage+1); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage+1, nTargetPage+1)); + MovePage(nPage+1, nTargetPage+1); + bSomethingHappened = true; + } + } + else + { + if (nPage != nTargetPage) + { + SdrPage* pPg = GetPage(nPage+1); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage+1, nTargetPage+1)); + MovePage(nPage+1, nTargetPage+1); + pPg = GetPage(nPage); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoSetPageNum(*pPg, nPage, nTargetPage)); + MovePage(nPage, nTargetPage); + bSomethingHappened = true; + } + } + nTargetPage = rpPage->GetPageNum(); + } + } + + if( bUndo ) + EndUndo(); + + return bSomethingHappened; +} + +// Return number of links in sfx2::LinkManager +sal_uLong SdDrawDocument::GetLinkCount() const +{ + return m_pLinkManager->GetLinks().size(); +} + +// Set Language +void SdDrawDocument::SetLanguage( const LanguageType eLang, const sal_uInt16 nId ) +{ + bool bChanged = false; + + if( nId == EE_CHAR_LANGUAGE && meLanguage != eLang ) + { + meLanguage = eLang; + bChanged = true; + } + else if( nId == EE_CHAR_LANGUAGE_CJK && meLanguageCJK != eLang ) + { + meLanguageCJK = eLang; + bChanged = true; + } + else if( nId == EE_CHAR_LANGUAGE_CTL && meLanguageCTL != eLang ) + { + meLanguageCTL = eLang; + bChanged = true; + } + + if( bChanged ) + { + GetDrawOutliner().SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + m_pHitTestOutliner->SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + m_pItemPool->SetPoolDefaultItem( SvxLanguageItem( eLang, nId ) ); + SetChanged( bChanged ); + } +} + +// Return language +LanguageType SdDrawDocument::GetLanguage( const sal_uInt16 nId ) const +{ + LanguageType eLangType = meLanguage; + + if( nId == EE_CHAR_LANGUAGE_CJK ) + eLangType = meLanguageCJK; + else if( nId == EE_CHAR_LANGUAGE_CTL ) + eLangType = meLanguageCTL; + + return eLangType; +} + +// Initiate WorkStartup +IMPL_LINK_NOARG(SdDrawDocument, WorkStartupHdl, Timer *, void) +{ + if (IsTransportContainer()) + return; + + if( mpDocSh ) + mpDocSh->SetWaitCursor( true ); + + bool bChanged = IsChanged(); // remember this + + // Initialize Autolayouts + SdPage* pHandoutMPage = GetMasterSdPage(0, PageKind::Handout); + + if (pHandoutMPage->GetAutoLayout() == AUTOLAYOUT_NONE) + { + // No AutoLayout yet -> initialize + pHandoutMPage->SetAutoLayout(AUTOLAYOUT_HANDOUT6, true, true); + } + + SdPage* pPage = GetSdPage(0, PageKind::Standard); + + if (pPage->GetAutoLayout() == AUTOLAYOUT_NONE) + { + // No AutoLayout yet -> initialize + pPage->SetAutoLayout(AUTOLAYOUT_NONE, true, true); + } + + SdPage* pNotesPage = GetSdPage(0, PageKind::Notes); + + if (pNotesPage->GetAutoLayout() == AUTOLAYOUT_NONE) + { + // No AutoLayout yet -> initialize + pNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true, true); + } + + SetChanged(bChanged); + + if( mpDocSh ) + mpDocSh->SetWaitCursor( false ); +} + +// When the WorkStartupTimer has been created (this only happens in +// SdDrawViewShell::Construct() ), the timer may be stopped and the WorkStartup +// may be initiated. +void SdDrawDocument::StopWorkStartupDelay() +{ + if (mpWorkStartupTimer) + { + if ( mpWorkStartupTimer->IsActive() ) + { + // Timer not yet expired -> initiate WorkStartup + mpWorkStartupTimer->Stop(); + WorkStartupHdl(nullptr); + } + + mpWorkStartupTimer.reset(); + } +} + +// When the WorkStartupTimer has been created (this only happens in +// SdDrawViewShell::Construct() ), the timer may be stopped and the WorkStartup +// may be initiated. +SdAnimationInfo* SdDrawDocument::GetAnimationInfo(SdrObject* pObject) +{ + DBG_ASSERT(pObject, "sd::SdDrawDocument::GetAnimationInfo(), invalid argument!"); + if( pObject ) + return GetShapeUserData( *pObject ); + else + return nullptr; +} + +SdAnimationInfo* SdDrawDocument::GetShapeUserData(SdrObject& rObject, bool bCreate /* = false */ ) +{ + sal_uInt16 nUD = 0; + sal_uInt16 nUDCount = rObject.GetUserDataCount(); + SdAnimationInfo* pRet = nullptr; + + // Can we find animation information within the user data? + for (nUD = 0; nUD < nUDCount; nUD++) + { + SdrObjUserData* pUD = rObject.GetUserData(nUD); + if((pUD->GetInventor() == SdrInventor::StarDrawUserData) && (pUD->GetId() == SD_ANIMATIONINFO_ID)) + { + pRet = dynamic_cast(pUD); + break; + } + } + + if( (pRet == nullptr) && bCreate ) + { + pRet = new SdAnimationInfo( rObject ); + rObject.AppendUserData( std::unique_ptr(pRet) ); + } + + return pRet; +} + +/** this method enforces that the masterpages are in the correct order, + that is at position 1 is a PageKind::Standard masterpage followed by a + PageKind::Notes masterpage and so on. # +*/ +void SdDrawDocument::CheckMasterPages() +{ + sal_uInt16 nMaxPages = GetMasterPageCount(); + + // we need at least a handout master and one master page + if( nMaxPages < 2 ) + { + return; + } + + SdPage* pPage = nullptr; + + sal_uInt16 nPage; + + // first see if the page order is correct + for( nPage = 1; nPage < nMaxPages; nPage++ ) + { + pPage = static_cast (GetMasterPage( nPage )); + // if an odd page is not a standard page or an even page is not a notes page + if( ((1 == (nPage & 1)) && (pPage->GetPageKind() != PageKind::Standard) ) || + ((0 == (nPage & 1)) && (pPage->GetPageKind() != PageKind::Notes) ) ) + break; // then we have a fatal error + } + + if( nPage >= nMaxPages ) + return; + + SdPage* pNotesPage = nullptr; + + // there is a fatal error in the master page order, + // we need to repair the document + bool bChanged = false; + + nPage = 1; + while( nPage < nMaxPages ) + { + pPage = static_cast (GetMasterPage( nPage )); + if( pPage->GetPageKind() != PageKind::Standard ) + { + bChanged = true; + sal_uInt16 nFound = nPage + 1; + while( nFound < nMaxPages ) + { + pPage = static_cast(GetMasterPage( nFound )); + if( PageKind::Standard == pPage->GetPageKind() ) + { + MoveMasterPage( nFound, nPage ); + pPage->SetInserted(); + break; + + } + + nFound++; + } + + // if we don't have any more standard pages, were done + if( nMaxPages == nFound ) + break; + } + + nPage++; + + if( nPage < nMaxPages ) + pNotesPage = static_cast(GetMasterPage( nPage )); + else + pNotesPage = nullptr; + + if( (nullptr == pNotesPage) || (pNotesPage->GetPageKind() != PageKind::Notes) || ( pPage->GetLayoutName() != pNotesPage->GetLayoutName() ) ) + { + bChanged = true; + + sal_uInt16 nFound = nPage + 1; + while( nFound < nMaxPages ) + { + pNotesPage = static_cast(GetMasterPage( nFound )); + if( (PageKind::Notes == pNotesPage->GetPageKind()) && ( pPage->GetLayoutName() == pNotesPage->GetLayoutName() ) ) + { + MoveMasterPage( nFound, nPage ); + pNotesPage->SetInserted(); + break; + } + + nFound++; + } + + // looks like we lost a notes page + if( nMaxPages == nFound ) + { + // so create one + + // first find a reference notes page for size + SdPage* pRefNotesPage = nullptr; + nFound = 0; + while( nFound < nMaxPages ) + { + pRefNotesPage = static_cast(GetMasterPage( nFound )); + if( PageKind::Notes == pRefNotesPage->GetPageKind() ) + break; + nFound++; + } + if( nFound == nMaxPages ) + pRefNotesPage = nullptr; + + rtl::Reference pNewNotesPage = AllocSdPage(true); + pNewNotesPage->SetPageKind(PageKind::Notes); + if( pRefNotesPage ) + { + pNewNotesPage->SetSize( pRefNotesPage->GetSize() ); + pNewNotesPage->SetBorder( pRefNotesPage->GetLeftBorder(), + pRefNotesPage->GetUpperBorder(), + pRefNotesPage->GetRightBorder(), + pRefNotesPage->GetLowerBorder() ); + } + InsertMasterPage(pNewNotesPage.get(), nPage ); + pNewNotesPage->SetLayoutName( pPage->GetLayoutName() ); + pNewNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true, true ); + nMaxPages++; + } + } + + nPage++; + } + + // now remove all remaining and unused non PageKind::Standard slides + while( nPage < nMaxPages ) + { + bChanged = true; + + RemoveMasterPage( nPage ); + nMaxPages--; + } + + if( bChanged ) + { + OSL_FAIL( "master pages where in a wrong order" ); + RecalcPageNums( true); + } +} + +sal_uInt16 SdDrawDocument::CreatePage ( + SdPage* pActualPage, + PageKind ePageKind, + const OUString& sStandardPageName, + const OUString& sNotesPageName, + AutoLayout eStandardLayout, + AutoLayout eNotesLayout, + bool bIsPageBack, + bool bIsPageObj, + const sal_Int32 nInsertPosition) +{ + SdPage* pPreviousStandardPage; + SdPage* pPreviousNotesPage; + rtl::Reference pStandardPage; + rtl::Reference pNotesPage; + + // From the given page determine the standard page and notes page of which + // to take the layout and the position where to insert the new pages. + if (ePageKind == PageKind::Notes) + { + pPreviousNotesPage = pActualPage; + sal_uInt16 nNotesPageNum = pPreviousNotesPage->GetPageNum() + 2; + pPreviousStandardPage = static_cast( GetPage(nNotesPageNum - 3) ); + eStandardLayout = pPreviousStandardPage->GetAutoLayout(); + } + else + { + pPreviousStandardPage = pActualPage; + sal_uInt16 nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2; + pPreviousNotesPage = static_cast( GetPage(nStandardPageNum - 1) ); + eNotesLayout = pPreviousNotesPage->GetAutoLayout(); + } + + // Create new standard page and set it up + pStandardPage = AllocSdPage(false); + + // Set the size here since else the presobj autolayout + // will be wrong. + pStandardPage->SetSize( pPreviousStandardPage->GetSize() ); + pStandardPage->SetBorder( pPreviousStandardPage->GetLeftBorder(), + pPreviousStandardPage->GetUpperBorder(), + pPreviousStandardPage->GetRightBorder(), + pPreviousStandardPage->GetLowerBorder() ); + + // Use master page of current page. + pStandardPage->TRG_SetMasterPage(pPreviousStandardPage->TRG_GetMasterPage()); + + // User layout of current standard page + pStandardPage->SetLayoutName( pPreviousStandardPage->GetLayoutName() ); + pStandardPage->SetAutoLayout(eStandardLayout, true); + pStandardPage->setHeaderFooterSettings( pPreviousStandardPage->getHeaderFooterSettings() ); + + // transition settings of current page + pStandardPage->setTransitionType( pPreviousStandardPage->getTransitionType() ); + pStandardPage->setTransitionSubtype( pPreviousStandardPage->getTransitionSubtype() ); + pStandardPage->setTransitionDirection( pPreviousStandardPage->getTransitionDirection() ); + pStandardPage->setTransitionFadeColor( pPreviousStandardPage->getTransitionFadeColor() ); + pStandardPage->setTransitionDuration( pPreviousStandardPage->getTransitionDuration() ); + + // apply previous animation timing + pStandardPage->SetPresChange( pPreviousStandardPage->GetPresChange() ); + pStandardPage->SetTime( pPreviousStandardPage->GetTime() ); + + // Create new notes page and set it up + pNotesPage = AllocSdPage(false); + pNotesPage->SetPageKind(PageKind::Notes); + + // Use master page of current page + pNotesPage->TRG_SetMasterPage(pPreviousNotesPage->TRG_GetMasterPage()); + + // Use layout of current notes page + pNotesPage->SetLayoutName( pPreviousNotesPage->GetLayoutName() ); + pNotesPage->SetAutoLayout(eNotesLayout, true); + pNotesPage->setHeaderFooterSettings( pPreviousNotesPage->getHeaderFooterSettings() ); + + return InsertPageSet ( + pActualPage, + ePageKind, + sStandardPageName, + sNotesPageName, + bIsPageBack, + bIsPageObj, + pStandardPage.get(), + pNotesPage.get(), + nInsertPosition); +} + +sal_uInt16 SdDrawDocument::DuplicatePage (sal_uInt16 nPageNum) +{ + PageKind ePageKind = PageKind::Standard; + + // Get current page + SdPage* pActualPage = GetSdPage(nPageNum, ePageKind); + + // Get background flags + SdrLayerAdmin& rLayerAdmin = GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = pActualPage->TRG_GetMasterPageVisibleLayers(); + + return DuplicatePage ( + pActualPage, ePageKind, + // No names for the new slides + OUString(), OUString(), + aVisibleLayers.IsSet(aBckgrnd), + aVisibleLayers.IsSet(aBckgrndObj), -1); +} + +sal_uInt16 SdDrawDocument::DuplicatePage ( + SdPage* pActualPage, + PageKind ePageKind, + const OUString& sStandardPageName, + const OUString& sNotesPageName, + bool bIsPageBack, + bool bIsPageObj, + const sal_Int32 nInsertPosition) +{ + SdPage* pPreviousStandardPage; + SdPage* pPreviousNotesPage; + rtl::Reference pStandardPage; + rtl::Reference pNotesPage; + + // From the given page determine the standard page and the notes page + // of which to make copies. + if (ePageKind == PageKind::Notes) + { + pPreviousNotesPage = pActualPage; + sal_uInt16 nNotesPageNum = pPreviousNotesPage->GetPageNum() + 2; + pPreviousStandardPage = static_cast( GetPage(nNotesPageNum - 3) ); + } + else + { + pPreviousStandardPage = pActualPage; + sal_uInt16 nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2; + pPreviousNotesPage = static_cast( GetPage(nStandardPageNum - 1) ); + } + + // Create duplicates of a standard page and the associated notes page + pStandardPage = static_cast( pPreviousStandardPage->CloneSdrPage(*this).get() ); + pNotesPage = static_cast( pPreviousNotesPage->CloneSdrPage(*this).get() ); + + return InsertPageSet ( + pActualPage, + ePageKind, + sStandardPageName, + sNotesPageName, + bIsPageBack, + bIsPageObj, + pStandardPage.get(), + pNotesPage.get(), + nInsertPosition); +} + +sal_uInt16 SdDrawDocument::InsertPageSet ( + SdPage* pActualPage, + PageKind ePageKind, + const OUString& sStandardPageName, + const OUString& sNotesPageName, + bool bIsPageBack, + bool bIsPageObj, + SdPage* pStandardPage, + SdPage* pNotesPage, + sal_Int32 nInsertPosition) +{ + SdPage* pPreviousStandardPage; + SdPage* pPreviousNotesPage; + sal_uInt16 nStandardPageNum; + sal_uInt16 nNotesPageNum; + OUString aNotesPageName(sNotesPageName); + + // Gather some information about the standard page and the notes page + // that are to be inserted. This makes sure that there is always one + // standard page followed by one notes page. + if (ePageKind == PageKind::Notes) + { + pPreviousNotesPage = pActualPage; + nNotesPageNum = pPreviousNotesPage->GetPageNum() + 2; + pPreviousStandardPage = static_cast( GetPage(nNotesPageNum - 3) ); + nStandardPageNum = nNotesPageNum - 1; + } + else + { + pPreviousStandardPage = pActualPage; + nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2; + pPreviousNotesPage = static_cast( GetPage(nStandardPageNum - 1) ); + nNotesPageNum = nStandardPageNum + 1; + aNotesPageName = sStandardPageName; + } + + OSL_ASSERT(nNotesPageNum==nStandardPageNum+1); + if (nInsertPosition < 0) + nInsertPosition = nStandardPageNum; + + // Set up and insert the standard page + SetupNewPage ( + pPreviousStandardPage, + pStandardPage, + sStandardPageName, + nInsertPosition, + bIsPageBack, + bIsPageObj); + + // Set up and insert the notes page + pNotesPage->SetPageKind(PageKind::Notes); + SetupNewPage ( + pPreviousNotesPage, + pNotesPage, + aNotesPageName, + nInsertPosition+1, + bIsPageBack, + bIsPageObj); + + // Return an index that allows the caller to access the newly inserted + // pages by using GetSdPage() + return pStandardPage->GetPageNum() / 2; +} + +void SdDrawDocument::SetupNewPage ( + SdPage const * pPreviousPage, + SdPage* pPage, + const OUString& sPageName, + sal_uInt16 nInsertionPoint, + bool bIsPageBack, + bool bIsPageObj) +{ + if (pPreviousPage != nullptr) + { + pPage->SetSize( pPreviousPage->GetSize() ); + pPage->SetBorder( pPreviousPage->GetLeftBorder(), + pPreviousPage->GetUpperBorder(), + pPreviousPage->GetRightBorder(), + pPreviousPage->GetLowerBorder() ); + } + pPage->SetName(sPageName); + + InsertPage(pPage, nInsertionPoint); + + if (pPreviousPage != nullptr) + { + SdrLayerAdmin& rLayerAdmin = GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = pPreviousPage->TRG_GetMasterPageVisibleLayers(); + aVisibleLayers.Set(aBckgrnd, bIsPageBack); + aVisibleLayers.Set(aBckgrndObj, bIsPageObj); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } +} + +sd::UndoManager* SdDrawDocument::GetUndoManager() const +{ + return mpDocSh ? dynamic_cast< sd::UndoManager* >(mpDocSh->GetUndoManager()) : nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc3.cxx b/sd/source/core/drawdoc3.cxx new file mode 100644 index 000000000..e25e8199d --- /dev/null +++ b/sd/source/core/drawdoc3.cxx @@ -0,0 +1,1873 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +/** Concrete incarnations get called by lcl_IterateBookmarkPages, for + every page in the bookmark document/list + */ + +namespace { + +class InsertBookmarkAsPage_FindDuplicateLayouts +{ +public: + explicit InsertBookmarkAsPage_FindDuplicateLayouts( std::vector &rLayoutsToTransfer ) + : mrLayoutsToTransfer(rLayoutsToTransfer) {} + void operator()( SdDrawDocument&, SdPage const *, bool, SdDrawDocument* ); +private: + std::vector &mrLayoutsToTransfer; +}; + +} + +void InsertBookmarkAsPage_FindDuplicateLayouts::operator()( SdDrawDocument& rDoc, SdPage const * pBMMPage, bool bRenameDuplicates, SdDrawDocument* pBookmarkDoc ) +{ + // now check for duplicate masterpage and layout names + + OUString aLayout( pBMMPage->GetLayoutName() ); + sal_Int32 nIndex = aLayout.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aLayout = aLayout.copy(0, nIndex); + + std::vector::const_iterator pIter = + find(mrLayoutsToTransfer.begin(),mrLayoutsToTransfer.end(),aLayout); + + bool bFound = pIter != mrLayoutsToTransfer.end(); + + const sal_uInt16 nMPageCount = rDoc.GetMasterPageCount(); + for (sal_uInt16 nMPage = 0; nMPage < nMPageCount && !bFound; nMPage++) + { + // Do the layouts already exist within the document? + SdPage* pTestPage = static_cast( rDoc.GetMasterPage(nMPage) ); + OUString aTest(pTestPage->GetLayoutName()); + sal_Int32 nIndex2 = aTest.indexOf( SD_LT_SEPARATOR ); + if( nIndex2 != -1 ) + aTest = aTest.copy(0, nIndex2); + + if (aTest == aLayout && pBMMPage->GetPageKind() == pTestPage->GetPageKind()) + { + // Ignore Layouts with "Default" these seem to be special - in the sense that there are lot of assumption all over Impress + // about this + if( bRenameDuplicates && aTest != SdResId( STR_LAYOUT_DEFAULT_NAME ) && !(pTestPage->Equals(*pBMMPage)) ) + { + pBookmarkDoc->RenameLayoutTemplate( + pBMMPage->GetLayoutName(), pBMMPage->GetName() + "_"); + aLayout = pBMMPage->GetName(); + + break; + } + else + bFound = true; + } + } + + if (!bFound) + mrLayoutsToTransfer.push_back(aLayout); +} + +// Inserts a bookmark as a page +static void lcl_IterateBookmarkPages( SdDrawDocument &rDoc, SdDrawDocument* pBookmarkDoc, + const std::vector &rBookmarkList, sal_uInt16 nBMSdPageCount, + InsertBookmarkAsPage_FindDuplicateLayouts& rPageIterator, bool bRenameDuplicates ) +{ + + // Refactored copy'n'pasted layout name collection from InsertBookmarkAsPage + + int nPos, nEndPos; + + if( rBookmarkList.empty() ) + { + // no list? whole source document + nEndPos = nBMSdPageCount; + } + else + { + // bookmark list? number of entries + nEndPos = rBookmarkList.size(); + } + + SdPage* pBMPage; + + // iterate over number of pages to insert + for (nPos = 0; nPos < nEndPos; ++nPos) + { + // the master page associated to the nPos'th page to insert + SdPage* pBMMPage = nullptr; + + if( rBookmarkList.empty() ) + { + // simply take master page of nPos'th page in source document + pBMMPage = static_cast(&(pBookmarkDoc->GetSdPage(static_cast(nPos), PageKind::Standard)->TRG_GetMasterPage())); + } + else + { + // fetch nPos'th entry from bookmark list, and determine master page + OUString aBMPgName(rBookmarkList[nPos]); + bool bIsMasterPage; + + sal_uInt16 nBMPage = pBookmarkDoc->GetPageByName( aBMPgName, bIsMasterPage ); + + if (nBMPage != SDRPAGE_NOTFOUND) + { + pBMPage = static_cast( pBookmarkDoc->GetPage(nBMPage) ); + } + else + { + pBMPage = nullptr; + } + + // enforce that bookmarked page is a standard page and not already a master page + if (pBMPage && pBMPage->GetPageKind()==PageKind::Standard && !pBMPage->IsMasterPage()) + { + const sal_uInt16 nBMSdPage = (nBMPage - 1) / 2; + pBMMPage = static_cast (&(pBookmarkDoc->GetSdPage(nBMSdPage, PageKind::Standard)->TRG_GetMasterPage())); + } + } + + // successfully determined valid (bookmarked) page? + if( pBMMPage ) + { + // yes, call functor + rPageIterator( rDoc, pBMMPage, bRenameDuplicates, pBookmarkDoc ); + } + } +} + +// Opens a bookmark document +SdDrawDocument* SdDrawDocument::OpenBookmarkDoc(SfxMedium* pMedium) +{ + bool bOK = true; + SdDrawDocument* pBookmarkDoc = nullptr; + OUString aBookmarkName = pMedium->GetName(); + std::shared_ptr pFilter = pMedium->GetFilter(); + if ( !pFilter ) + { + pMedium->UseInteractionHandler( true ); + SfxGetpApp()->GetFilterMatcher().GuessFilter(*pMedium, pFilter); + } + + if ( !pFilter ) + { + bOK = false; + } + else if ( !aBookmarkName.isEmpty() && maBookmarkFile != aBookmarkName ) + { + bool bCreateGraphicShell = pFilter->GetServiceName() == "com.sun.star.drawing.DrawingDocument"; + bool bCreateImpressShell = pFilter->GetServiceName() == "com.sun.star.presentation.PresentationDocument"; + if ( bCreateGraphicShell || bCreateImpressShell ) + { + CloseBookmarkDoc(); + + // Create a DocShell, as OLE objects might be contained in the + // document. (Persist) + // If that wasn't the case, we could load the model directly. + if ( bCreateGraphicShell ) + // Draw + mxBookmarkDocShRef = new ::sd::GraphicDocShell(SfxObjectCreateMode::STANDARD); + else + // Impress + mxBookmarkDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::STANDARD, true, DocumentType::Impress); + + bOK = mxBookmarkDocShRef->DoLoad(pMedium); + if( bOK ) + { + maBookmarkFile = aBookmarkName; + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + } + } + + DBG_ASSERT(!aBookmarkName.isEmpty(), "Empty document name!"); + + if (!bOK) + { + std::unique_ptr xErrorBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + + CloseBookmarkDoc(); + pBookmarkDoc = nullptr; + } + else if (mxBookmarkDocShRef.is()) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + + return pBookmarkDoc; +} + +// Opens a bookmark document +SdDrawDocument* SdDrawDocument::OpenBookmarkDoc(const OUString& rBookmarkFile) +{ + SdDrawDocument* pBookmarkDoc = nullptr; + + if (!rBookmarkFile.isEmpty() && maBookmarkFile != rBookmarkFile) + { + std::unique_ptr xMedium(new SfxMedium(rBookmarkFile, StreamMode::READ)); + pBookmarkDoc = OpenBookmarkDoc(xMedium.release()); + } + else if (mxBookmarkDocShRef.is()) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + + return pBookmarkDoc; +} + +// Inserts a bookmark (page or object) +void SdDrawDocument::InsertBookmark( + const std::vector &rBookmarkList, // List of names of the bookmarks to be inserted + std::vector &rExchangeList, // List of the names to be used + bool bLink, // Insert bookmarks as links? + sal_uInt16 nInsertPos, // Insertion position of pages + ::sd::DrawDocShell* pBookmarkDocSh, // If set, this is the source document + Point const * pObjPos) // Insertion position of objects +{ + bool bOK = true; + bool bInsertPages = false; + + if (rBookmarkList.empty()) + { + // Insert all pages + bInsertPages = true; + } + else + { + SdDrawDocument* pBookmarkDoc = nullptr; + + if (pBookmarkDocSh) + { + pBookmarkDoc = pBookmarkDocSh->GetDoc(); + } + else if ( mxBookmarkDocShRef.is() ) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + else + bOK = false; + + bInsertPages = bOK && std::any_of(rBookmarkList.begin(), rBookmarkList.end(), + [&pBookmarkDoc](const OUString& rBookmark) { + // Is there a page name in the bookmark list? + bool bIsMasterPage; + return pBookmarkDoc->GetPageByName(rBookmark, bIsMasterPage) != SDRPAGE_NOTFOUND; + }); + } + + bool bCalcObjCount = !rExchangeList.empty(); + + if ( bOK && bInsertPages ) + { + // Insert all page bookmarks + bOK = InsertBookmarkAsPage(rBookmarkList, &rExchangeList, bLink, false/*bReplace*/, + nInsertPos, false/*bNoDialogs*/, pBookmarkDocSh, true/*bCopy*/, true, false); + } + + if ( bOK && !rBookmarkList.empty() ) + { + // Insert all object bookmarks + InsertBookmarkAsObject(rBookmarkList, rExchangeList, + pBookmarkDocSh, pObjPos, bCalcObjCount); + } +} + +namespace +{ + +void +lcl_removeUnusedStyles(SfxStyleSheetBasePool* const pStyleSheetPool, StyleSheetCopyResultVector& rStyles) +{ + StyleSheetCopyResultVector aUsedStyles; + aUsedStyles.reserve(rStyles.size()); + for (const auto& a : rStyles) + { + if (a.m_xStyleSheet->IsUsed()) + aUsedStyles.push_back(a); + else + pStyleSheetPool->Remove(a.m_xStyleSheet.get()); + } + rStyles = aUsedStyles; +} + +SfxStyleSheet *lcl_findStyle(StyleSheetCopyResultVector& rStyles, std::u16string_view aStyleName) +{ + for (const auto& a : rStyles) + { + if (a.m_xStyleSheet->GetName().startsWith(aStyleName)) + return a.m_xStyleSheet.get(); + } + return nullptr; +} + +} + +bool SdDrawDocument::InsertBookmarkAsPage( + const std::vector &rBookmarkList, + std::vector *pExchangeList, // List of names to be used + bool bLink, + bool bReplace, + sal_uInt16 nInsertPos, + bool bNoDialogs, + ::sd::DrawDocShell* pBookmarkDocSh, + bool bCopy, + bool bMergeMasterPages, + bool bPreservePageNames) +{ + bool bContinue = true; + bool bScaleObjects = false; + sal_uInt16 nReplacedStandardPages = 0; + + SdDrawDocument* pBookmarkDoc = nullptr; + OUString aBookmarkName; + + if (pBookmarkDocSh) + { + pBookmarkDoc = pBookmarkDocSh->GetDoc(); + + if (pBookmarkDocSh->GetMedium()) + { + aBookmarkName = pBookmarkDocSh->GetMedium()->GetName(); + } + } + else if ( mxBookmarkDocShRef.is() ) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + aBookmarkName = maBookmarkFile; + } + else + { + return false; + } + + const sal_uInt16 nSdPageCount = GetSdPageCount(PageKind::Standard); + const sal_uInt16 nBMSdPageCount = pBookmarkDoc->GetSdPageCount(PageKind::Standard); + const sal_uInt16 nMPageCount = GetMasterPageCount(); + + if (nSdPageCount==0 || nBMSdPageCount==0 || nMPageCount==0) + { + return false; + } + + // Store the size and some other properties of the first page and notes + // page so that inserted pages can be properly scaled even when inserted + // before the first page. + // Note that the pointers are used later on as general page pointers. + SdPage* pRefPage = GetSdPage(0, PageKind::Standard); + Size aSize(pRefPage->GetSize()); + sal_Int32 nLeft = pRefPage->GetLeftBorder(); + sal_Int32 nRight = pRefPage->GetRightBorder(); + sal_Int32 nUpper = pRefPage->GetUpperBorder(); + sal_Int32 nLower = pRefPage->GetLowerBorder(); + Orientation eOrient = pRefPage->GetOrientation(); + + SdPage* pNPage = GetSdPage(0, PageKind::Notes); + Size aNSize(pNPage->GetSize()); + sal_Int32 nNLeft = pNPage->GetLeftBorder(); + sal_Int32 nNRight = pNPage->GetRightBorder(); + sal_Int32 nNUpper = pNPage->GetUpperBorder(); + sal_Int32 nNLower = pNPage->GetLowerBorder(); + Orientation eNOrient = pNPage->GetOrientation(); + + // Adapt page size and margins to those of the later pages? + pRefPage = GetSdPage(nSdPageCount - 1, PageKind::Standard); + + if( bNoDialogs ) + { + // If this is clipboard, then no need to scale objects: + // this will make copied masters to differ from the originals, + // and thus InsertBookmarkAsPage_FindDuplicateLayouts will + // duplicate masters on insert to same document + m_bTransportContainer = (SD_MOD()->pTransferClip && + SD_MOD()->pTransferClip->GetWorkDocument() == this); + if (!m_bTransportContainer) + { + if (rBookmarkList.empty()) + bScaleObjects = pRefPage->IsScaleObjects(); + else + bScaleObjects = true; + } + } + else + { + SdPage* pBMPage = pBookmarkDoc->GetSdPage(0,PageKind::Standard); + + if (pBMPage->GetSize() != pRefPage->GetSize() || + pBMPage->GetLeftBorder() != pRefPage->GetLeftBorder() || + pBMPage->GetRightBorder() != pRefPage->GetRightBorder() || + pBMPage->GetUpperBorder() != pRefPage->GetUpperBorder() || + pBMPage->GetLowerBorder() != pRefPage->GetLowerBorder()) + { + OUString aStr(SdResId(STR_SCALE_OBJECTS)); + std::unique_ptr xQueryBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Question, VclButtonsType::YesNo, + aStr)); + xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + sal_uInt16 nBut = xQueryBox->run(); + + bScaleObjects = nBut == RET_YES; + bContinue = nBut != RET_CANCEL; + + if (!bContinue) + { + return bContinue; + } + } + } + + // Get the necessary presentation stylesheets and transfer them before + // the pages, else, the text objects won't reference their styles anymore. + SfxUndoManager* pUndoMgr = nullptr; + if( mpDocSh ) + { + pUndoMgr = mpDocSh->GetUndoManager(); + ViewShellId nViewShellId(-1); + if (sd::ViewShell* pViewShell = mpDocSh->GetViewShell()) + nViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); + pUndoMgr->EnterListAction(SdResId(STR_UNDO_INSERTPAGES), "", 0, nViewShellId); + } + + // Refactored copy'n'pasted layout name collection into IterateBookmarkPages + + std::vector aLayoutsToTransfer; + InsertBookmarkAsPage_FindDuplicateLayouts aSearchFunctor( aLayoutsToTransfer ); + lcl_IterateBookmarkPages( *this, pBookmarkDoc, rBookmarkList, nBMSdPageCount, aSearchFunctor, ( rBookmarkList.empty() && pBookmarkDoc != this ) ); + + // Copy the style that we actually need. + SdStyleSheetPool& rBookmarkStyleSheetPool = dynamic_cast(*pBookmarkDoc->GetStyleSheetPool()); + SdStyleSheetPool& rStyleSheetPool = dynamic_cast(*GetStyleSheetPool()); + + // When copying styles, also copy the master pages! + if( !aLayoutsToTransfer.empty() ) + bMergeMasterPages = true; + + for ( const OUString& layoutName : aLayoutsToTransfer ) + { + StyleSheetCopyResultVector aCreatedStyles; + + rStyleSheetPool.CopyLayoutSheets(layoutName, rBookmarkStyleSheetPool,aCreatedStyles); + + if(!aCreatedStyles.empty()) + { + if( pUndoMgr ) + { + pUndoMgr->AddUndoAction(std::make_unique(this, aCreatedStyles, true)); + } + } + } + + // Copy styles. This unconditionally copies all styles, even those + // that are not used in any of the inserted pages. The unused styles + // are then removed at the end of the function, where we also create + // undo records for the inserted styles. + StyleSheetCopyResultVector aNewGraphicStyles; + OUString aRenameStr; + if(!bReplace && !bNoDialogs) + aRenameStr = "_"; + rStyleSheetPool.RenameAndCopyGraphicSheets(rBookmarkStyleSheetPool, aNewGraphicStyles, aRenameStr); + StyleSheetCopyResultVector aNewCellStyles; + rStyleSheetPool.CopyCellSheets(rBookmarkStyleSheetPool, aNewCellStyles); + + // TODO handle undo of table styles too + rStyleSheetPool.CopyTableStyles(rBookmarkStyleSheetPool); + + // Insert document + + const bool bUndo = IsUndoEnabled(); + + if( bUndo ) + BegUndo(SdResId(STR_UNDO_INSERTPAGES)); + + if (rBookmarkList.empty()) + { + if (nInsertPos >= GetPageCount()) + { + // Add pages to the end + nInsertPos = GetPageCount(); + } + + sal_uInt16 nActualInsertPos = nInsertPos; + + sal_uInt16 nBMSdPage; + std::set aRenameSet; + std::map aNameMap; + + for (nBMSdPage=0; nBMSdPage < nBMSdPageCount; nBMSdPage++) + { + SdPage* pBMPage = pBookmarkDoc->GetSdPage(nBMSdPage, PageKind::Standard); + OUString sName(pBMPage->GetName()); + bool bIsMasterPage; + + if (bLink) + { + // Remember the names of all pages + aNameMap.insert(std::make_pair(nBMSdPage,sName)); + } + + // Have to check for duplicate names here, too + // don't change name if source and dest model are the same! + if( pBookmarkDoc != this && + GetPageByName(sName, bIsMasterPage ) != SDRPAGE_NOTFOUND ) + { + // delay renaming *after* pages are copied (might destroy source otherwise) + aRenameSet.insert(nBMSdPage); + } + } + + Merge(*pBookmarkDoc, + 1, // Not the handout page + 0xFFFF, // But all others + nActualInsertPos, // Insert at position ... + bMergeMasterPages, // Move master pages? + false, // But only the master pages used + true, // Create an undo action + bCopy); // Copy (or merge) pages? + + for (nBMSdPage=0; nBMSdPage < nBMSdPageCount; nBMSdPage++) + { + SdPage* pPage = static_cast( GetPage(nActualInsertPos) ); + SdPage* pNotesPage = static_cast( GetPage(nActualInsertPos+1) ); + + // delay renaming *after* pages are copied (might destroy source otherwise) + if( aRenameSet.find(nBMSdPage) != aRenameSet.end() ) + { + // Page name already in use -> Use default name for default and + // notes page + pPage->SetName(OUString()); + pNotesPage->SetName(OUString()); + } + + if (bLink) + { + OUString aName(aNameMap[nBMSdPage]); + + // Assemble all link names + pPage->SetFileName(aBookmarkName); + pPage->SetBookmarkName(aName); + } + + nActualInsertPos += 2; + } + } + else + { + // Insert selected pages + SdPage* pBMPage; + + if (nInsertPos >= GetPageCount()) + { + // Add pages to the end + bReplace = false; + nInsertPos = GetPageCount(); + } + + sal_uInt16 nActualInsertPos = nInsertPos; + + // Collect the bookmarked pages + ::std::vector aBookmarkedPages (rBookmarkList.size(), nullptr); + for ( size_t nPos = 0, n = rBookmarkList.size(); nPos < n; ++nPos) + { + OUString aPgName(rBookmarkList[nPos]); + bool bIsMasterPage; + sal_uInt16 nBMPage = pBookmarkDoc->GetPageByName( aPgName, bIsMasterPage ); + + if (nBMPage != SDRPAGE_NOTFOUND) + { + aBookmarkedPages[nPos] = dynamic_cast(pBookmarkDoc->GetPage(nBMPage)); + } + } + + for ( size_t nPos = 0, n = rBookmarkList.size(); nPos < n; ++nPos) + { + pBMPage = aBookmarkedPages[nPos]; + sal_uInt16 nBMPage = pBMPage!=nullptr ? pBMPage->GetPageNum() : SDRPAGE_NOTFOUND; + + if (pBMPage && pBMPage->GetPageKind()==PageKind::Standard && !pBMPage->IsMasterPage()) + { + // It has to be a default page + bool bMustRename = false; + + // delay renaming *after* pages are copied (might destroy source otherwise) + // don't change name if source and dest model are the same! + // avoid renaming if replacing the same page + OUString aPgName(rBookmarkList[nPos]); + bool bIsMasterPage; + sal_uInt16 nPageSameName = GetPageByName(aPgName, bIsMasterPage); + if( pBookmarkDoc != this && + nPageSameName != SDRPAGE_NOTFOUND && + ( !bReplace || + nPageSameName != nActualInsertPos ) ) + { + bMustRename = true; + } + + SdPage* pBookmarkPage = pBMPage; + if (bReplace ) + { + ReplacePageInCustomShows( dynamic_cast< SdPage* >( GetPage( nActualInsertPos ) ), pBookmarkPage ); + } + + Merge(*pBookmarkDoc, + nBMPage, // From page (default page) + nBMPage+1, // To page (notes page) + nActualInsertPos, // Insert at position + bMergeMasterPages, // Move master pages? + false, // But only the master pages used + true, // Create undo action + bCopy); // Copy (or merge) pages? + + if( bReplace ) + { + if( GetPage( nActualInsertPos ) != pBookmarkPage ) + { + // bookmark page was not moved but cloned, so update custom shows again + ReplacePageInCustomShows( pBookmarkPage, dynamic_cast< SdPage* >( GetPage( nActualInsertPos ) ) ); + } + } + + if( bMustRename ) + { + // Page name already in use -> use default name for default and + // notes page + SdPage* pPage = static_cast( GetPage(nActualInsertPos) ); + pPage->SetName(OUString()); + SdPage* pNotesPage = static_cast( GetPage(nActualInsertPos+1) ); + pNotesPage->SetName(OUString()); + } + + if (bLink) + { + SdPage* pPage = static_cast( GetPage(nActualInsertPos) ); + pPage->SetFileName(aBookmarkName); + pPage->SetBookmarkName(aPgName); + } + + if (bReplace) + { + // Remove page and notes page. + const sal_uInt16 nDestPageNum(nActualInsertPos + 2); + SdPage* pStandardPage = nullptr; + + if(nDestPageNum < GetPageCount()) + { + pStandardPage = static_cast(GetPage(nDestPageNum)); + } + + if (pStandardPage) + { + if( bPreservePageNames ) + { + // Take old slide names for inserted pages + SdPage* pPage = static_cast( GetPage(nActualInsertPos) ); + pPage->SetName( pStandardPage->GetRealName() ); + } + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pStandardPage)); + + RemovePage(nDestPageNum); + } + + SdPage* pNotesPage = nullptr; + + if(nDestPageNum < GetPageCount()) + { + pNotesPage = static_cast(GetPage(nDestPageNum)); + } + + if (pNotesPage) + { + if( bPreservePageNames ) + { + // Take old slide names for inserted pages + SdPage* pNewNotesPage = static_cast( GetPage(nActualInsertPos+1)); + if( pNewNotesPage ) + pNewNotesPage->SetName( pStandardPage->GetRealName() ); + } + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage)); + + RemovePage(nDestPageNum); + } + + nReplacedStandardPages++; + } + + nActualInsertPos += 2; + } + } + } + + // We might have duplicate master pages now, as the drawing engine does not + // recognize duplicates. Remove these now. + sal_uInt16 nNewMPageCount = GetMasterPageCount(); + + // Go backwards, so the numbers don't become messed up + for (sal_uInt16 nPage = nNewMPageCount - 1; nPage >= nMPageCount; nPage--) + { + pRefPage = static_cast( GetMasterPage(nPage) ); + OUString aMPLayout(pRefPage->GetLayoutName()); + PageKind eKind = pRefPage->GetPageKind(); + + // Does this already exist? + for (sal_uInt16 nTest = 0; nTest < nMPageCount; nTest++) + { + SdPage* pTest = static_cast( GetMasterPage(nTest) ); + OUString aTest(pTest->GetLayoutName()); + + // nInsertPos > 2 is always true when inserting into non-empty models + if ( nInsertPos > 2 && + aTest == aMPLayout && + eKind == pTest->GetPageKind() ) + { + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pRefPage)); + + RemoveMasterPage(nPage); + + nNewMPageCount--; + break; + } + } + } + + // nInsertPos > 2 is always true when inserting into non-empty models + if (nInsertPos > 0) + { + sal_uInt16 nSdPageStart = (nInsertPos - 1) / 2; + sal_uInt16 nSdPageEnd = bReplace + ? nSdPageStart + nReplacedStandardPages - 1 + : GetSdPageCount(PageKind::Standard) - nSdPageCount + nSdPageStart - 1; + const bool bRemoveEmptyPresObj = + (pBookmarkDoc->GetDocumentType() == DocumentType::Impress) && + (GetDocumentType() == DocumentType::Draw); + + std::vector::iterator pExchangeIter; + + if (pExchangeList) + pExchangeIter = pExchangeList->begin(); + + for (sal_uInt16 nSdPage = nSdPageStart; nSdPage <= nSdPageEnd; nSdPage++) + { + pRefPage = GetSdPage(nSdPage, PageKind::Standard); + + if (pExchangeList && pExchangeIter != pExchangeList->end()) + { + // Get the name to use from Exchange list + OUString aExchangeName(*pExchangeIter); + pRefPage->SetName(aExchangeName); + Broadcast(SdrHint(SdrHintKind::PageOrderChange, pRefPage)); + + SdPage* pNewNotesPage = GetSdPage(nSdPage, PageKind::Notes); + pNewNotesPage->SetName(aExchangeName); + Broadcast(SdrHint(SdrHintKind::PageOrderChange, pNewNotesPage)); + + ++pExchangeIter; + } + + OUString aLayout(pRefPage->GetLayoutName()); + sal_Int32 nIndex = aLayout.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aLayout = aLayout.copy(0, nIndex); + + // update layout and referred master page + pRefPage->SetPresentationLayout(aLayout); + if( bUndo ) + AddUndo( GetSdrUndoFactory().CreateUndoPageChangeMasterPage( *pRefPage ) ); + + if (bScaleObjects) + { + ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower); + pRefPage->ScaleObjects(aSize, aBorderRect, true); + } + pRefPage->SetSize(aSize); + pRefPage->SetBorder(nLeft, nUpper, nRight, nLower); + pRefPage->SetOrientation( eOrient ); + + if( bRemoveEmptyPresObj ) + pRefPage->RemoveEmptyPresentationObjects(); + + pRefPage = GetSdPage(nSdPage, PageKind::Notes); + + // update layout and referred master page + pRefPage->SetPresentationLayout(aLayout); + if( bUndo ) + AddUndo( GetSdrUndoFactory().CreateUndoPageChangeMasterPage( *pRefPage ) ); + + if (bScaleObjects) + { + ::tools::Rectangle aBorderRect(nNLeft, nNUpper, nNRight, nNLower); + pRefPage->ScaleObjects(aNSize, aBorderRect, true); + } + + pRefPage->SetSize(aNSize); + pRefPage->SetBorder(nNLeft, nNUpper, nNRight, nNLower); + pRefPage->SetOrientation( eNOrient ); + + if( bRemoveEmptyPresObj ) + pRefPage->RemoveEmptyPresentationObjects(); + } + + ///Remove processed elements, to avoid doing hacks in InsertBookmarkAsObject + if ( pExchangeList ) + pExchangeList->erase(pExchangeList->begin(),pExchangeIter); + + for (sal_uInt16 nPage = nMPageCount; nPage < nNewMPageCount; nPage++) + { + pRefPage = static_cast( GetMasterPage(nPage) ); + if (pRefPage->GetPageKind() == PageKind::Standard) + { + if (bScaleObjects) + { + ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower); + pRefPage->ScaleObjects(aSize, aBorderRect, true); + } + pRefPage->SetSize(aSize); + pRefPage->SetBorder(nLeft, nUpper, nRight, nLower); + pRefPage->SetOrientation( eOrient ); + } + else // Can only be notes + { + if (bScaleObjects) + { + ::tools::Rectangle aBorderRect(nNLeft, nNUpper, nNRight, nNLower); + pRefPage->ScaleObjects(aNSize, aBorderRect, true); + } + pRefPage->SetSize(aNSize); + pRefPage->SetBorder(nNLeft, nNUpper, nNRight, nNLower); + pRefPage->SetOrientation( eNOrient ); + } + + if( bRemoveEmptyPresObj ) + pRefPage->RemoveEmptyPresentationObjects(); + } + } + + // Make absolutely sure no double masterpages are there + RemoveUnnecessaryMasterPages(nullptr, true); + + // Rename object styles if necessary + if(!aRenameStr.isEmpty()) + { + try + { + for(sal_uInt32 p = nInsertPos; p < sal_uInt32(nInsertPos) + sal_uInt32(nBMSdPageCount); p++) + { + SdPage *pPg = static_cast( GetPage(p) ); + for(size_t i = 0; pPg && (i < pPg->GetObjCount()); ++i) + { + if(pPg->GetObj(i)->GetStyleSheet()) + { + OUString aStyleName = pPg->GetObj(i)->GetStyleSheet()->GetName(); + SfxStyleSheet *pSheet = lcl_findStyle(aNewGraphicStyles, OUStringConcatenation(aStyleName + aRenameStr)); + if(pSheet != nullptr) + pPg->GetObj(i)->SetStyleSheet(pSheet, true); + } + } + } + } + catch(...) + { + TOOLS_WARN_EXCEPTION( "sd", "Exception while renaming styles @ SdDrawDocument::InsertBookmarkAsPage"); + } + } + // remove copied styles not used on any inserted page and create + // undo records + // WARNING: SdMoveStyleSheetsUndoAction clears the passed list of + // styles, so it cannot be used after this point + lcl_removeUnusedStyles(GetStyleSheetPool(), aNewGraphicStyles); + if (!aNewGraphicStyles.empty() && pUndoMgr) + pUndoMgr->AddUndoAction(std::make_unique(this, aNewGraphicStyles, true)); + lcl_removeUnusedStyles(GetStyleSheetPool(), aNewCellStyles); + if (!aNewCellStyles.empty() && pUndoMgr) + pUndoMgr->AddUndoAction(std::make_unique(this, aNewCellStyles, true)); + + if( bUndo ) + EndUndo(); + + if (pUndoMgr) + pUndoMgr->LeaveListAction(); + + return bContinue; +} + +// Inserts a bookmark as an object +bool SdDrawDocument::InsertBookmarkAsObject( + const std::vector &rBookmarkList, + const std::vector &rExchangeList, // List of names to use + ::sd::DrawDocShell* pBookmarkDocSh, + Point const * pObjPos, + bool bCalcObjCount) +{ + bool bOK = true; + bool bOLEObjFound = false; + std::unique_ptr<::sd::View> pBMView; + + SdDrawDocument* pBookmarkDoc = nullptr; + + if (pBookmarkDocSh) + { + pBookmarkDoc = pBookmarkDocSh->GetDoc(); + } + else if ( mxBookmarkDocShRef.is() ) + { + pBookmarkDoc = mxBookmarkDocShRef->GetDoc(); + } + else + { + return false; + } + + if (rBookmarkList.empty()) + { + pBMView.reset(new ::sd::View(*pBookmarkDoc, nullptr)); + pBMView->EndListening(*pBookmarkDoc); + pBMView->MarkAll(); + } + else + { + SdrPage* pPage; + SdrPageView* pPV; + + for ( const auto& rBookmark : rBookmarkList ) + { + // Get names of bookmarks from the list + SdrObject* pObj = pBookmarkDoc->GetObj(rBookmark); + + if (pObj) + { + // Found an object + if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::OLE2) + { + bOLEObjFound = true; + } + + if (!pBMView) + { + // Create View for the first time + pBMView.reset(new ::sd::View(*pBookmarkDoc, nullptr)); + pBMView->EndListening(*pBookmarkDoc); + } + + pPage = pObj->getSdrPageFromSdrObject(); + + if (pPage->IsMasterPage()) + { + pPV = pBMView->ShowSdrPage(pBMView->GetModel()->GetMasterPage(pPage->GetPageNum())); + } + else + { + pPV = pBMView->GetSdrPageView(); + if( !pPV || (pPV->GetPage() != pPage)) + pPV = pBMView->ShowSdrPage(pPage); + } + + pBMView->MarkObj(pObj, pPV); + } + } + } + + if (pBMView) + { + // Insert selected objects + std::optional<::sd::View> pView(std::in_place, *this, nullptr); + pView->EndListening(*this); + + // Look for the page into which the objects are supposed to be inserted + SdrPage* pPage = GetSdPage(0, PageKind::Standard); + + if (mpDocSh) + { + ::sd::ViewShell* pViewSh = mpDocSh->GetViewShell(); + + if (pViewSh) + { + // Which page is currently in view? + SdrPageView* pPV = pViewSh->GetView()->GetSdrPageView(); + + if (pPV) + { + pPage = pPV->GetPage(); + } + else if (pViewSh->GetActualPage()) + { + pPage = pViewSh->GetActualPage(); + } + } + } + + Point aObjPos; + + if (pObjPos) + { + aObjPos = *pObjPos; + } + else + { + aObjPos = ::tools::Rectangle(Point(), pPage->GetSize()).Center(); + } + + size_t nCountBefore = 0; + + if (!rExchangeList.empty() || bCalcObjCount) + { + // Sort OrdNums and get the number of objects before inserting + pPage->RecalcObjOrdNums(); + nCountBefore = pPage->GetObjCount(); + } + + if (bOLEObjFound) + pBMView->GetDoc().SetAllocDocSh(true); + + SdDrawDocument* pTmpDoc = static_cast( pBMView->CreateMarkedObjModel().release() ); + bOK = pView->Paste(*pTmpDoc, aObjPos, pPage, SdrInsertFlags::NONE); + + if (bOLEObjFound) + pBMView->GetDoc().SetAllocDocSh(false); + + if (!bOLEObjFound) + delete pTmpDoc; // Would otherwise be destroyed by DocShell + + pView.reset(); + + // Get number of objects after inserting. + const size_t nCount = pPage->GetObjCount(); + if (nCountBefore < nCount) + { + size_t nObj = nCountBefore; + for (const auto& rExchange : rExchangeList) + { + // Get the name to use from the Exchange list + if (pPage->GetObj(nObj)) + { + pPage->GetObj(nObj)->SetName(rExchange); + } + + ++nObj; + if (nObj >= nCount) + break; + } + } + } + + return bOK; +} + +// Stops the bookmark insertion +void SdDrawDocument::CloseBookmarkDoc() +{ + if (mxBookmarkDocShRef.is()) + { + mxBookmarkDocShRef->DoClose(); + } + + mxBookmarkDocShRef.clear(); + maBookmarkFile.clear(); +} + +// Is this document read-only? +bool SdDrawDocument::IsReadOnly() const +{ + return false; +} + +// In the subsequent AllocModel() a DocShell (xAllocedDocShRef) is created. +// Any pre-existing DocShell is deleted +void SdDrawDocument::SetAllocDocSh(bool bAlloc) +{ + mbAllocDocSh = bAlloc; + + if(mxAllocedDocShRef.is()) + { + mxAllocedDocShRef->DoClose(); + } + + mxAllocedDocShRef.clear(); +} + +// Return list of CustomShows (create it, too, if necessary) +SdCustomShowList* SdDrawDocument::GetCustomShowList(bool bCreate) +{ + if (!mpCustomShowList && bCreate) + { + mpCustomShowList.reset(new SdCustomShowList); + } + + return mpCustomShowList.get(); +} + +// Remove unused master pages and layouts +void SdDrawDocument::RemoveUnnecessaryMasterPages(SdPage* pMasterPage, bool bOnlyDuplicatePages, bool bUndo) +{ + ::sd::View* pView = nullptr; + SfxUndoManager* pUndoMgr = nullptr; + + if( bUndo && !IsUndoEnabled() ) + bUndo = false; + + if (mpDocSh) + { + pUndoMgr = mpDocSh->GetUndoManager(); + + if (mpDocSh->GetViewShell()) + pView = mpDocSh->GetViewShell()->GetView(); + } + + // Check all master pages + sal_uInt16 nSdMasterPageCount = GetMasterSdPageCount( PageKind::Standard ); + for (sal_Int32 nMPage = nSdMasterPageCount - 1; nMPage >= 0; nMPage--) + { + SdPage* pMaster = pMasterPage; + SdPage* pNotesMaster = nullptr; + + if (!pMaster) + { + pMaster = GetMasterSdPage( static_cast(nMPage), PageKind::Standard ); + pNotesMaster = GetMasterSdPage( static_cast(nMPage), PageKind::Notes ); + } + else + { + for ( sal_uInt16 nMPg = 0; nMPg < GetMasterPageCount(); nMPg++ ) + { + if ( pMaster == GetMasterPage( nMPg ) ) + { + pNotesMaster = static_cast( GetMasterPage( ++nMPg ) ); + break; + } + } + } + + DBG_ASSERT( pMaster->GetPageKind() == PageKind::Standard, "wrong page kind" ); + + if ( pMaster->GetPageKind() == PageKind::Standard && + GetMasterPageUserCount( pMaster ) == 0 && + pNotesMaster ) + { + // Do not delete master pages that have their precious flag set + bool bDeleteMaster = !pMaster->IsPrecious(); + OUString aLayoutName = pMaster->GetLayoutName(); + + if(bOnlyDuplicatePages ) + { + // remove only duplicate pages + bDeleteMaster = false; + for (sal_uInt16 i = 0; i < GetMasterSdPageCount( PageKind::Standard ); i++) + { + SdPage* pMPg = GetMasterSdPage( i, PageKind::Standard ); + if( pMPg != pMaster && + pMPg->GetLayoutName() == aLayoutName ) + { + // duplicate page found -> remove it + bDeleteMaster = true; + } + } + } + + if( bDeleteMaster ) + { + if (pView) + { + // if MasterPage is visible hide on pageview + SdrPageView* pPgView = pView->GetSdrPageView(); + if (pPgView) + { + SdrPage* pShownPage = pPgView->GetPage(); + if( (pShownPage == pMaster) || (pShownPage == pNotesMaster) ) + { + pView->HideSdrPage(); + pView->ShowSdrPage( GetSdPage( 0, PageKind::Standard ) ); + } + } + } + + if( bUndo ) + { + BegUndo(); + AddUndo( GetSdrUndoFactory().CreateUndoDeletePage( *pNotesMaster ) ); + } + + RemoveMasterPage( pNotesMaster->GetPageNum() ); + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoDeletePage(*pMaster)); + + RemoveMasterPage( pMaster->GetPageNum() ); + + if( bUndo ) + EndUndo(); // do this here already, so Joe's actions happen _between_ our own + + // Delete old, unused layout stylesheets + bool bDeleteOldStyleSheets = true; + for ( sal_uInt16 nMPg = 0; + nMPg < GetMasterPageCount() && bDeleteOldStyleSheets; + nMPg++ ) + { + SdPage* pMPg = static_cast( GetMasterPage(nMPg) ); + if (pMPg->GetLayoutName() == aLayoutName) + { + bDeleteOldStyleSheets = false; + } + } + + if (bDeleteOldStyleSheets) + { + SdStyleSheetVector aRemove; + static_cast( mxStyleSheetPool.get())->CreateLayoutSheetList( aLayoutName, aRemove ); + + if( bUndo ) + { + StyleSheetCopyResultVector aUndoRemove; + aUndoRemove.reserve(aRemove.size()); + for (const auto& a : aRemove) + aUndoRemove.emplace_back(a.get(), true); + + if (pUndoMgr) + pUndoMgr->AddUndoAction(std::make_unique(this, aUndoRemove, false)); + } + + for( const auto& a : aRemove ) + static_cast( mxStyleSheetPool.get())->Remove(a.get()); + } + } + } + + if (pMasterPage) + break; // Just this one master page! + } +} + +/** Exchange master page + * + * Either the nSdPageNum gets a new, own master page or the master page is + * exchanged completely (which then applies to all pages). + * + * nSdPageNum : page number that the new master page should get. + * rLayoutName : LayoutName of the new master page + * pSourceDoc : document (template) to get the master page from + * bMaster : exchange the master page of nSdPageNum + * bCheckMasters: remove unused master pages + * + * If pSourceDoc == NULL, an empty master page is applied. + * If rLayoutName is empty, the first master page is used. + */ +// #i121863# factored out functionality +static bool isMasterPageLayoutNameUnique(const SdDrawDocument& rDoc, std::u16string_view rCandidate) +{ + if (rCandidate.empty()) + { + return false; + } + + const sal_uInt16 nPageCount(rDoc.GetMasterPageCount()); + + for(sal_uInt16 a(0); a < nPageCount; a++) + { + const SdrPage* pCandidate = rDoc.GetMasterPage(a); + OUString aPageLayoutName(pCandidate->GetLayoutName()); + sal_Int32 nIndex = aPageLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + aPageLayoutName = aPageLayoutName.copy(0, nIndex); + + if(aPageLayoutName == rCandidate) + { + return false; + } + } + + return true; +} + +// #i121863# factored out functionality +static OUString createNewMasterPageLayoutName(const SdDrawDocument& rDoc) +{ + const OUString aBaseName(SdResId(STR_LAYOUT_DEFAULT_NAME)); + sal_uInt16 nCount(0); + for (;;) + { + OUString aRetval = aBaseName; + if(nCount) + { + aRetval += OUString::number(nCount); + } + if (isMasterPageLayoutNameUnique(rDoc, aRetval)) + return aRetval; + nCount++; + } +} + +void SdDrawDocument::SetMasterPage(sal_uInt16 nSdPageNum, + std::u16string_view rLayoutName, + SdDrawDocument* pSourceDoc, + bool bMaster, + bool bCheckMasters) +{ + SfxUndoManager* pUndoMgr = nullptr; + + if( mpDocSh ) + { + mpDocSh->SetWaitCursor( true ); + pUndoMgr = mpDocSh->GetUndoManager(); + } + + const bool bUndo = pUndoMgr && IsUndoEnabled(); + + if (bUndo) + { + ViewShellId nViewShellId(-1); + if (sd::ViewShell* pViewShell = mpDocSh->GetViewShell()) + nViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); + pUndoMgr->EnterListAction(SdResId(STR_UNDO_SET_PRESLAYOUT), OUString(), 0, nViewShellId); + } + + SdPage* pSelectedPage = GetSdPage(nSdPageNum, PageKind::Standard); + SdPage* pNotes = static_cast( GetPage(pSelectedPage->GetPageNum()+1) ); + SdPage& rOldMaster = static_cast(pSelectedPage->TRG_GetMasterPage()); + SdPage& rOldNotesMaster = static_cast(pNotes->TRG_GetMasterPage()); + rtl::Reference pMaster; + rtl::Reference pNotesMaster; + OUString aOldPageLayoutName(pSelectedPage->GetLayoutName()); + OUString aOldLayoutName(aOldPageLayoutName); + sal_Int32 nIndex = aOldLayoutName.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aOldLayoutName = aOldLayoutName.copy(0, nIndex); + + if (pSourceDoc) + { + std::vector aReplList; // List of replaced stylesheets + bool bLayoutReloaded = false; // Was ex. layout reloaded? + + // LayoutName, Page and Notes page + if (rLayoutName.empty()) + { + // No LayoutName: take first MasterPage + pMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Standard); + pNotesMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Notes); + } + else + { + OUString aSearchFor + = OUString::Concat(rLayoutName) + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + for (sal_uInt16 nMP = 0; nMP < pSourceDoc->GetMasterPageCount(); ++nMP) + { + SdPage* pMP = static_cast( pSourceDoc->GetMasterPage(nMP) ); + + if (pMP->GetLayoutName() == aSearchFor) + { + if (pMP->GetPageKind() == PageKind::Standard) + pMaster = pMP; + if (pMP->GetPageKind() == PageKind::Notes) + pNotesMaster = pMP; + } + if (pMaster && pNotesMaster) + break; + } + DBG_ASSERT(pMaster, "MasterPage (Standard page) not found"); + DBG_ASSERT(pNotesMaster, "MasterPage (Notes page) not found"); + + // this should not happen, but looking at crash reports, it does + if( (pMaster == nullptr) || (pNotesMaster == nullptr) ) + { + // so take the first MasterPage + pMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Standard); + pNotesMaster = pSourceDoc->GetMasterSdPage(0, PageKind::Notes); + } + } + + // we should never reach this, but one never knows... + if( (pMaster == nullptr) || (pNotesMaster == nullptr) ) + { + if (bUndo) + pUndoMgr->LeaveListAction(); + + if( mpDocSh ) + mpDocSh->SetWaitCursor( false ); + + OSL_FAIL( "SdDrawDocument::SetMasterPage() failed!" ); + + return; + } + + const OUString aOriginalNewLayoutName( pMaster->GetName() ); + OUString aTargetNewLayoutName(aOriginalNewLayoutName); + + if (pSourceDoc != this) + { + // #i121863# clone masterpages, they are from another model (!) + rtl::Reference pNewNotesMaster(dynamic_cast< SdPage* >(pNotesMaster->CloneSdrPage(*this).get())); + rtl::Reference pNewMaster(dynamic_cast< SdPage* >(pMaster->CloneSdrPage(*this).get())); + + if(!pNewNotesMaster || !pNewMaster) + { + OSL_FAIL("SdDrawDocument::SetMasterPage() cloning of MasterPage/NoteAmsterPage failed!" ); + return; + } + + pNotesMaster = pNewNotesMaster; + pMaster = pNewMaster; + + // layout name needs to be unique + aTargetNewLayoutName = pMaster->GetLayoutName(); + sal_Int32 nIndex2 = aTargetNewLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex2 != -1 ) + aTargetNewLayoutName = aTargetNewLayoutName.copy(0, nIndex2); + + if(!isMasterPageLayoutNameUnique(*this, aTargetNewLayoutName)) + { + aTargetNewLayoutName = createNewMasterPageLayoutName(*this); + + OUString aTemp = aTargetNewLayoutName + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + pMaster->SetName(aTargetNewLayoutName); + pMaster->SetLayoutName(aTemp); + + pNotesMaster->SetName(aTargetNewLayoutName); + pNotesMaster->SetLayoutName(aTemp); + } + } + + if (pSourceDoc != this) + { + const sal_uInt16 nMasterPageCount = GetMasterPageCount(); + for ( sal_uInt16 nMPage = 0; nMPage < nMasterPageCount; nMPage++ ) + { + SdPage* pCheckMaster = static_cast(GetMasterPage(nMPage)); + if( pCheckMaster->GetName() == aTargetNewLayoutName ) + { + bLayoutReloaded = true; + break; + } + } + + // Correct or create presentation templates -- + // only worry about presentation templates + OUString aName; + SdStyleSheetPool* pSourceStyleSheetPool = static_cast( pSourceDoc->GetStyleSheetPool() ); + + StyleSheetCopyResultVector aCreatedStyles; // List of created stylesheets + SfxStyleSheetBase* pHisSheet = pSourceStyleSheetPool->First(SfxStyleFamily::Page); + + while (pHisSheet) + { + aName = pHisSheet->GetName(); + + // #i121863# search in source styles with original style name from source of + // evtl. cloned master (not-cloned, renamed for uniqueness) + if( aName.startsWith( aOriginalNewLayoutName ) ) + { + // #i121863# build name of evtl. cloned master style to search for + if(aOriginalNewLayoutName != aTargetNewLayoutName) + { + const sal_Int32 nPos(aName.indexOf(SD_LT_SEPARATOR)); + aName = aTargetNewLayoutName + aName.subView(nPos); + } + + SfxStyleSheet* pMySheet = static_cast( mxStyleSheetPool->Find(aName, SfxStyleFamily::Page) ); + + if (pMySheet) + { + // A stylesheet of the same name already exists -> overwrite contents + bool bTest = pMySheet->SetName(pHisSheet->GetName()); + DBG_ASSERT(bTest, "Renaming StyleSheet failed."); + pMySheet->GetItemSet().ClearItem(); // Delete all + + if (bUndo) + { + pUndoMgr->AddUndoAction(std::make_unique(this, + pMySheet, &pHisSheet->GetItemSet())); + } + pMySheet->GetItemSet().Put(pHisSheet->GetItemSet()); + pMySheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + else + { + // create new style + OUString aHelpFile; + pMySheet = static_cast( &mxStyleSheetPool->Make(aName, SfxStyleFamily::Page, pHisSheet->GetMask()) ); + pMySheet->SetHelpId( aHelpFile, pHisSheet->GetHelpId(aHelpFile) ); + pMySheet->GetItemSet().ClearItem(); // Delete all + pMySheet->GetItemSet().Put(pHisSheet->GetItemSet()); + + aCreatedStyles.emplace_back(static_cast(pMySheet), true); + } + + StyleReplaceData aReplData; + aReplData.nNewFamily = pMySheet->GetFamily(); + aReplData.nFamily = pMySheet->GetFamily(); + aReplData.aNewName = pMySheet->GetName(); + + // #i121863# re-create original name of style used at page where to replace with + // this new style + OUString aTemp(pMySheet->GetName()); + const sal_Int32 nPos(aTemp.indexOf(SD_LT_SEPARATOR)); + aTemp = aOldLayoutName + aTemp.subView(nPos); + aReplData.aName = aTemp; + aReplList.push_back(aReplData); + } + + pHisSheet = pSourceStyleSheetPool->Next(); + } + + // If new styles were created: re-create parent chaining of the item + // sets in the styles. + if(!aCreatedStyles.empty()) + { + for ( const auto& rRData : aReplList ) + { + SfxStyleSheetBase* pSOld = mxStyleSheetPool->Find(rRData.aName, SfxStyleFamily::Page); + SfxStyleSheetBase* pSNew = mxStyleSheetPool->Find(rRData.aNewName, SfxStyleFamily::Page); + + if (pSOld && pSNew) + { + const OUString& rParentOfOld = pSOld->GetParent(); + const OUString& rParentOfNew = pSNew->GetParent(); + + if (!rParentOfOld.isEmpty() && rParentOfNew.isEmpty()) + { + std::vector::iterator pRDIter = std::find_if(aReplList.begin(), aReplList.end(), + [&rParentOfOld](const StyleReplaceData& rRD) { return (rRD.aName == rParentOfOld) && (rRD.aName != rRD.aNewName); }); + if (pRDIter != aReplList.end()) + { + OUString aParentOfNew(pRDIter->aNewName); + pSNew->SetParent(aParentOfNew); + } + } + } + } + } + + if (bUndo && !aCreatedStyles.empty()) + { + // Add UndoAction for creating and inserting the stylesheets to + // the top of the UndoManager + pUndoMgr->AddUndoAction(std::make_unique( this, aCreatedStyles, true)); + } + } + + // Create layout name based upon the name of the page layout of the + // master page + OUString aPageLayoutName(pMaster->GetLayoutName()); + OUString aLayoutName = aPageLayoutName; + sal_Int32 nIndex2 = aLayoutName.indexOf( SD_LT_SEPARATOR ); + if( nIndex2 != -1 ) + aLayoutName = aLayoutName.copy( 0, nIndex2); + + // #i121863# Do *not* remove from original document any longer, it is potentially used there + // and would lead to crashes. Rely on the automatic process of removing unused masterpages + // (see RemoveUnnecessaryMasterPages) + //if (pSourceDoc != this) + //{ + // // Remove from the source document + // pSourceDoc->RemoveMasterPage(pNotesMaster->GetPageNum()); + // pSourceDoc->RemoveMasterPage(pMaster->GetPageNum()); + //} + + // Register the new master pages with the document and then use + // the new presentation layout for the default and notes pages + if (pSourceDoc != this) + { + // Insert the master pages: + // Insert master pages from new layouts at the end. + // If a layout is being replaced, however, insert them before the + // position of the old master page, so from now on the new master + // page will be found when searching (e.g. + // SdPage::SetPresentationLayout). + sal_uInt16 nInsertPos = rOldMaster.GetPageNum(); + BegUndo(); + + if (!bLayoutReloaded) + nInsertPos = 0xFFFF; + InsertMasterPage(pMaster.get(), nInsertPos); + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pMaster)); + + nInsertPos++; + if (!bLayoutReloaded) + nInsertPos = 0xFFFF; + InsertMasterPage(pNotesMaster.get(), nInsertPos); + if( bUndo ) + { + AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pNotesMaster)); + + EndUndo(); // do this here already, so Joe's actions happen _between_ our own. + } + } + + // Fill list with pages + std::vector> aPageList; + +// #98456, this has to be removed according to CL (KA 07/08/2002) +// #109884# but we need them again to restore the styles of the presentation objects while undo + aPageList.push_back(pMaster); + aPageList.push_back(pNotesMaster); + + if (bMaster || bLayoutReloaded) + { + for (sal_uInt16 nPage = 1; nPage < GetPageCount(); nPage++) + { + SdPage* pPage = static_cast( GetPage(nPage) ); + OUString aTest = pPage->GetLayoutName(); + if (aTest == aOldPageLayoutName) + { + aPageList.push_back(pPage); + } + } + + } + else + { + aPageList.push_back(pSelectedPage); + aPageList.push_back(pNotes); + } + + for (rtl::Reference& pPage : aPageList) + { + AutoLayout eAutoLayout = pPage->GetAutoLayout(); + + if( bUndo ) + { + pUndoMgr->AddUndoAction(std::make_unique + (this, + pPage->IsMasterPage() ? aLayoutName : aOldLayoutName, + aLayoutName, + eAutoLayout, eAutoLayout, false, pPage.get())); + } + pPage->SetPresentationLayout(aLayoutName); + pPage->SetAutoLayout(eAutoLayout); + } + + // Adapt new master pages + if (pSourceDoc != this) + { + Size aSize(rOldMaster.GetSize()); + ::tools::Rectangle aBorderRect(rOldMaster.GetLeftBorder(), + rOldMaster.GetUpperBorder(), + rOldMaster.GetRightBorder(), + rOldMaster.GetLowerBorder()); + pMaster->ScaleObjects(aSize, aBorderRect, true); + pMaster->SetSize(aSize); + pMaster->SetBorder(rOldMaster.GetLeftBorder(), + rOldMaster.GetUpperBorder(), + rOldMaster.GetRightBorder(), + rOldMaster.GetLowerBorder()); + pMaster->SetOrientation( rOldMaster.GetOrientation() ); + pMaster->SetAutoLayout(pMaster->GetAutoLayout()); + + aSize = rOldNotesMaster.GetSize(); + ::tools::Rectangle aNotesBorderRect(rOldNotesMaster.GetLeftBorder(), + rOldNotesMaster.GetUpperBorder(), + rOldNotesMaster.GetRightBorder(), + rOldNotesMaster.GetLowerBorder()); + pNotesMaster->ScaleObjects(aSize, aNotesBorderRect, true); + pNotesMaster->SetSize(aSize); + pNotesMaster->SetBorder(rOldNotesMaster.GetLeftBorder(), + rOldNotesMaster.GetUpperBorder(), + rOldNotesMaster.GetRightBorder(), + rOldNotesMaster.GetLowerBorder()); + pNotesMaster->SetOrientation( rOldNotesMaster.GetOrientation() ); + pNotesMaster->SetAutoLayout(pNotesMaster->GetAutoLayout()); + + if( (pSourceDoc->GetDocumentType() == DocumentType::Impress) && + (GetDocumentType() == DocumentType::Draw) ) + { + pMaster->RemoveEmptyPresentationObjects(); + pNotesMaster->RemoveEmptyPresentationObjects(); + } + } + } + else + { + // Find a new name for the layout + OUString aName(createNewMasterPageLayoutName(*this)); + OUString aPageLayoutName(aName + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE); + + // Generate new stylesheets + static_cast( mxStyleSheetPool.get())->CreateLayoutStyleSheets(aName); + SdStyleSheetVector aCreatedStyles; + static_cast( mxStyleSheetPool.get())->CreateLayoutSheetList(aName, aCreatedStyles); + + if( bUndo ) + { + StyleSheetCopyResultVector aUndoInsert; + aUndoInsert.reserve(aCreatedStyles.size()); + for (const auto& a : aCreatedStyles) + aUndoInsert.emplace_back(a.get(), true); + pUndoMgr->AddUndoAction(std::make_unique(this, aUndoInsert, true)); + // Generate new master pages and register them with the document + BegUndo(); + } + + pMaster = AllocSdPage(true); + pMaster->SetSize(pSelectedPage->GetSize()); + pMaster->SetBorder(pSelectedPage->GetLeftBorder(), + pSelectedPage->GetUpperBorder(), + pSelectedPage->GetRightBorder(), + pSelectedPage->GetLowerBorder() ); + pMaster->SetName(aName); + pMaster->SetLayoutName(aPageLayoutName); + InsertMasterPage(pMaster.get()); + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pMaster)); + + pMaster->SetAutoLayout(AUTOLAYOUT_NONE, true, true); + + pNotesMaster = AllocSdPage(true); + pNotesMaster->SetPageKind(PageKind::Notes); + pNotesMaster->SetSize(pNotes->GetSize()); + pNotesMaster->SetBorder(pNotes->GetLeftBorder(), + pNotes->GetUpperBorder(), + pNotes->GetRightBorder(), + pNotes->GetLowerBorder() ); + pNotesMaster->SetName(aName); + pNotesMaster->SetLayoutName(aPageLayoutName); + InsertMasterPage(pNotesMaster.get()); + + if( bUndo ) + AddUndo(GetSdrUndoFactory().CreateUndoNewPage(*pNotesMaster)); + + pNotesMaster->SetAutoLayout(AUTOLAYOUT_NOTES, true, true); + + if( bUndo ) + EndUndo(); + + // Create a list of affected default and notes pages + std::vector aPageList; + if (bMaster) + { + for (sal_uInt16 nPage = 1; nPage < GetPageCount(); nPage++) + { + SdPage* pPage = static_cast( GetPage(nPage) ); + if (pPage->GetLayoutName() == aOldPageLayoutName) + { + aPageList.push_back(pPage); + } + } + } + else + { + aPageList.push_back(pSelectedPage); + aPageList.push_back(pNotes); + } + + // Set presentation layout and AutoLayout for the affected pages + for ( auto& rpPage : aPageList ) + { + AutoLayout eOldAutoLayout = rpPage->GetAutoLayout(); + AutoLayout eNewAutoLayout = + rpPage->GetPageKind() == PageKind::Standard ? AUTOLAYOUT_NONE : AUTOLAYOUT_NOTES; + + if( bUndo ) + { + pUndoMgr->AddUndoAction(std::make_unique + (this, aOldLayoutName, aName, + eOldAutoLayout, eNewAutoLayout, true, + rpPage)); + } + + rpPage->SetPresentationLayout(aName); + rpPage->SetAutoLayout(eNewAutoLayout); + } + } + + // If the old master pages aren't used anymore, they and their styles have + // to be removed. + if (bCheckMasters) + { + // Check all + RemoveUnnecessaryMasterPages(); + } + else + { + // Check only the master page that was replaced + RemoveUnnecessaryMasterPages(&rOldMaster); + } + + if( bUndo ) + pUndoMgr->LeaveListAction(); + + if( mpDocSh ) + mpDocSh->SetWaitCursor( false ); +} + +void SdDrawDocument::Merge(SdrModel& rSourceModel, + sal_uInt16 nFirstPageNum, sal_uInt16 nLastPageNum, + sal_uInt16 nDestPos, + bool bMergeMasterPages, bool bAllMasterPages, + bool bUndo, bool bTreadSourceAsConst) +{ + sal_uInt16 nMasterPageCount = GetMasterPageCount(); + SdrModel::Merge( rSourceModel, nFirstPageNum, nLastPageNum, nDestPos, bMergeMasterPages, bAllMasterPages, bUndo, bTreadSourceAsConst ); + + // add style family for each new master page + for( sal_uInt16 nMaster = nMasterPageCount; nMaster < GetMasterPageCount(); nMaster++ ) + { + SdPage* pPage = static_cast< SdPage* >( GetMasterPage( nMaster ) ); + if( pPage && pPage->IsMasterPage() && (pPage->GetPageKind() == PageKind::Standard) ) + { + // new master page created, add its style family + SdStyleSheetPool* pStylePool = static_cast( GetStyleSheetPool() ); + if( pStylePool ) + pStylePool->AddStyleFamily( pPage ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc4.cxx b/sd/source/core/drawdoc4.cxx new file mode 100644 index 000000000..590450568 --- /dev/null +++ b/sd/source/core/drawdoc4.cxx @@ -0,0 +1,1399 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace com::sun::star::linguistic2 { class XHyphenator; } + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::linguistic2; +using namespace ::sd; + +// CreateLayoutTemplates +// At the moment (31.03.1995), the StyleSheetPool only saves styleheets that +// have an ItemSet. To save all stylesheets, we force the creation of an ItemSet +// with a GetItemSet call. +// We can remove this behavior once the pool saves styleheets even without an ItemSet +void SdDrawDocument::CreateLayoutTemplates() +{ + SdStyleSheetPool* pSSPool = static_cast(GetStyleSheetPool()); + SfxStyleSheetBase* pSheet = nullptr; + const OUString aHelpFile; + OUString aStdName(SdResId(STR_STANDARD_STYLESHEET_NAME)); + + // Default style + + SfxStyleSearchBits nMask = SfxStyleSearchBits::Auto; + + OUString aName(aStdName); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_STANDARD_STYLESHEET_NAME ); + SfxItemSet& rISet = pSheet->GetItemSet(); + + ::basegfx::B2DPolyPolygon aNullPolyPolygon; + Color aNullCol(COL_DEFAULT_SHAPE_STROKE); + + XDash aNullDash; + XGradient aNullGrad(aNullCol,COL_WHITE); + aNullGrad.SetStartIntens( 100 ); + aNullGrad.SetEndIntens( 100 ); + XHatch aNullHatch(aNullCol); + + // Line attributes (Extended OutputDevice) + rISet.Put(XLineStyleItem(drawing::LineStyle_SOLID)); + rISet.Put(XLineColorItem(OUString(), COL_DEFAULT_SHAPE_STROKE)); + rISet.Put(XLineWidthItem(0)); + rISet.Put(XLineDashItem(aNullDash)); + rISet.Put(XLineStartItem(aNullPolyPolygon)); + rISet.Put(XLineEndItem(aNullPolyPolygon)); + rISet.Put(XLineStartWidthItem(200)); + rISet.Put(XLineEndWidthItem(200)); + rISet.Put(XLineStartCenterItem()); + rISet.Put(XLineEndCenterItem()); + rISet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); + + // Fill attributes (Extended OutputDevice) + rISet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + rISet.Put(XFillColorItem(OUString(), COL_DEFAULT_SHAPE_FILLING)); + + rISet.Put( XFillGradientItem( aNullGrad) ); + rISet.Put(XFillHatchItem(aNullHatch)); + Size aNullSize( 32, 32 ); + Bitmap aNullBmp(aNullSize, vcl::PixelFormat::N8_BPP); + aNullBmp.Erase( COL_WHITE ); + rISet.Put(XFillBitmapItem(Graphic(BitmapEx(aNullBmp)))); + + // Shadow attributes (Drawing Engine) + rISet.Put(makeSdrShadowItem(false)); + rISet.Put(makeSdrShadowColorItem(COL_GRAY)); + rISet.Put(makeSdrShadowXDistItem(200)); // 3 mm Shadow distance + rISet.Put(makeSdrShadowYDistItem(200)); + + vcl::Font aLatinFont, aCJKFont, aCTLFont; + + getDefaultFonts( aLatinFont, aCJKFont, aCTLFont ); + + SvxFontItem aSvxFontItem( aLatinFont.GetFamilyType(), aLatinFont.GetFamilyName(), aLatinFont.GetStyleName(), aLatinFont.GetPitch(), + aLatinFont.GetCharSet(), EE_CHAR_FONTINFO ); + + SvxFontItem aSvxFontItemCJK( aCJKFont.GetFamilyType(), aCJKFont.GetFamilyName(), aCJKFont.GetStyleName(), aCJKFont.GetPitch(), + aCJKFont.GetCharSet(), EE_CHAR_FONTINFO_CJK ); + + SvxFontItem aSvxFontItemCTL( aCTLFont.GetFamilyType(), aCTLFont.GetFamilyName(), aCTLFont.GetStyleName(), aCTLFont.GetPitch(), + aCTLFont.GetCharSet(), EE_CHAR_FONTINFO_CTL ); + + rISet.Put( aSvxFontItem ); + rISet.Put( aSvxFontItemCJK ); + rISet.Put( aSvxFontItemCTL ); + + rISet.Put( SvxFontHeightItem( 635, 100, EE_CHAR_FONTHEIGHT ) ); // sj: (i33745) changed default from 24 to 18 pt + rISet.Put( SvxFontHeightItem( 635, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 18 pt + rISet.Put( SvxFontHeightItem( convertFontHeightToCTL( 635 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 18 pt + + rISet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rISet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rISet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + + rISet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rISet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rISet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + + rISet.Put(SvxContourItem(false, EE_CHAR_OUTLINE )); + rISet.Put(SvxShadowedItem(false, EE_CHAR_SHADOW )); + rISet.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE)); + rISet.Put(SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE)); + rISet.Put(SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT )); + rISet.Put(SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP )); + rISet.Put(SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK)); + rISet.Put(SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF)); + rISet.Put(SvxColorItem(COL_AUTO, EE_CHAR_COLOR )); + + // Paragraph attributes (Edit Engine) + rISet.Put(SvxLRSpaceItem(EE_PARA_LRSPACE)); + rISet.Put(SvxULSpaceItem(EE_PARA_ULSPACE)); + + rISet.Put( makeSdrTextLeftDistItem( 250 ) ); // sj: (i33745) using text frame distances seems to be a better default + rISet.Put( makeSdrTextRightDistItem( 250 ) ); + rISet.Put( makeSdrTextUpperDistItem( 125 ) ); + rISet.Put( makeSdrTextLowerDistItem( 125 ) ); + + // Set Word-wrap to true by default + rISet.Put( makeSdrTextWordWrapItem(true) ); + + rISet.Put( SvxLineSpacingItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ) ); + + // #i16874# enable kerning by default but only for new documents + rISet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + + // Bullet + // BulletItem and BulletFont for title and outline + SvxBulletItem aBulletItem(EE_PARA_BULLET); + // Identical in all layers + aBulletItem.SetStyle(SvxBulletStyle::BULLET); + aBulletItem.SetStart(1); + aBulletItem.SetScale(45); // In percent + + vcl::Font aBulletFont( SdStyleSheetPool::GetBulletFont() ); + + aBulletFont.SetFontSize(Size(0,635)); // sj: (i33745) changed default from 24 to 18 pt + + aBulletItem.SetFont(aBulletFont); + aBulletItem.SetSymbol( 0x25CF ); // In points + rISet.Put(aBulletItem); + + // New BulletItem + SdStyleSheetPool::PutNumBulletItem( pSheet, aBulletFont ); + + SfxItemSet* pISet = nullptr; + + // Default > Object without filling + { + aName = SdResId(STR_POOLSHEET_OBJWITHOUTFILL); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aStdName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OBJWITHOUTFILL ); + pISet = &pSheet->GetItemSet(); + + pISet->Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + // Default > Object no fill no line + { + aName = SdResId(STR_POOLSHEET_OBJNOLINENOFILL); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aStdName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OBJNOLINENOFILL ); + pISet = &pSheet->GetItemSet(); + + pISet->Put(XFillStyleItem(drawing::FillStyle_NONE)); + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); + } + + // tdf#94369 + + // Text + OUString aTextName; + { + aTextName = SdResId(STR_POOLSHEET_TEXT); + pSheet = &(pSSPool->Make(aTextName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_TEXT ); + pISet = &pSheet->GetItemSet(); + aSvxFontItem.SetFamilyName("Noto Sans"); + pISet->Put( aSvxFontItem ); // Noto Sans + pISet->Put(XFillStyleItem(drawing::FillStyle_SOLID)); // solid fill + pISet->Put(XFillColorItem(OUString(), Color(0xeeeeee))); // light gray 5 + pISet->Put(XLineStyleItem(drawing::LineStyle_SOLID)); // solid fill + pISet->Put(XLineColorItem(OUString(), Color(0xcccccc))); // light gray 3 + } + // Text > A4 + OUString aA4Name; + { + aA4Name = SdResId(STR_POOLSHEET_A4); + pSheet = &(pSSPool->Make(aA4Name, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aTextName ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A4 ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(635, 100, EE_CHAR_FONTHEIGHT )); // 18 pt + pISet->Put(XFillStyleItem(drawing::FillStyle_NONE)); // no filling + } + // Text > A4 > Title + { + + aName = SdResId(STR_POOLSHEET_A4_TITLE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aA4Name ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A4_TITLE ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(1551, 100, EE_CHAR_FONTHEIGHT )); // 44 pt + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A4 > Headline + { + aName = SdResId(STR_POOLSHEET_A4_HEADLINE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aA4Name ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A4_HEADLINE ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(847, 100, EE_CHAR_FONTHEIGHT )); // 24 pt + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A4 > Text + { + aName = SdResId(STR_POOLSHEET_A4_TEXT); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aA4Name); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A4_TEXT ); + pISet = &pSheet->GetItemSet(); + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A0 + OUString aA0Name; + { + aA0Name = SdResId(STR_POOLSHEET_A0); + pSheet = &(pSSPool->Make(aA0Name, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aTextName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A0 ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(1692, 100, EE_CHAR_FONTHEIGHT )); // 48 pt + pISet->Put(XFillStyleItem(drawing::FillStyle_NONE)); // no filling + } + // Text > A0 > Title + { + aName = SdResId(STR_POOLSHEET_A0_TITLE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aA0Name); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A0_TITLE ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(3385, 100, EE_CHAR_FONTHEIGHT )); // 96 pt + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A0 > Headline + { + aName = SdResId(STR_POOLSHEET_A0_HEADLINE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aA0Name); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A0_HEADLINE ); + pISet = &pSheet->GetItemSet(); + pISet->Put(SvxFontHeightItem(2538, 100, EE_CHAR_FONTHEIGHT )); // 72 pt + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + // Text > A0 > Text + { + aName = SdResId(STR_POOLSHEET_A0_TEXT); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aA0Name); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_A0_TEXT ); + pISet = &pSheet->GetItemSet(); + pISet->Put(XLineStyleItem(drawing::LineStyle_NONE)); // no border + } + + // Graphic + OUString aGraphicName; + XFillGradientItem aFillGradient; + XGradient aGradient; + + { + aGraphicName = SdResId(STR_POOLSHEET_GRAPHIC); + pSheet = &(pSSPool->Make(aGraphicName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_GRAPHIC ); + pISet = &pSheet->GetItemSet(); + aSvxFontItem.SetFamilyName("Liberation Sans"); // Liberation Sans + pISet->Put( aSvxFontItem ); + pISet->Put( SvxFontHeightItem(635, 100, EE_CHAR_FONTHEIGHT) ); // 18 pt + pISet->Put( XFillStyleItem(drawing::FillStyle_SOLID) ); // solid fill + pISet->Put( XFillColorItem(OUString(), COL_WHITE) ); // filled white + + } + // Graphic > Shapes + OUString aShapesName; + { + aShapesName = SdResId(STR_POOLSHEET_SHAPES); + pSheet = &(pSSPool->Make(aShapesName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aGraphicName ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_SHAPES); + pISet = &pSheet->GetItemSet(); + pISet->Put(XFillStyleItem(drawing::FillStyle_GRADIENT)); // fill with gradient + aGradient.SetGradientStyle( ::awt::GradientStyle_RECT); // square type + aGradient.SetAngle( 0_deg10 ); // 0° angle + aGradient.SetStartColor( Color(0xcccccc) ); // white + aGradient.SetEndColor( COL_WHITE ); // light gray 3 + aFillGradient.SetName( aShapesName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( XLineStyleItem(drawing::LineStyle_NONE) ); // no border + pISet->Put( SvxFontHeightItem(494, 100, EE_CHAR_FONTHEIGHT) ); // 14 pt + pISet->Put( SvxWeightItem(WEIGHT_BOLD, EE_CHAR_WEIGHT) ); // bold + } + // Graphic > Shapes > Filled + OUString aFilledName(SdResId(STR_POOLSHEET_FILLED)); + { + aName = aFilledName; + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED ); + pSheet->SetParent( aShapesName ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetGradientStyle( ::awt::GradientStyle_LINEAR ); + aGradient.SetAngle( 300_deg10 ); + aGradient.SetStartColor( COL_WHITE ); // white + aGradient.SetEndColor( Color(0xcccccc) ); // light gray 3 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( XFillStyleItem(drawing::FillStyle_GRADIENT) ); + pISet->Put( aFillGradient ); + } + // Graphic > Shapes > Filled > Blue + { + aName =SdResId(STR_POOLSHEET_FILLED_BLUE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aFilledName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED_BLUE ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetStartColor( Color(0x00729fcf) ); // light blue 2 + aGradient.SetEndColor( Color(0x00355269) ); // dark blue 2 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( SvxColorItem(COL_WHITE, EE_CHAR_COLOR )); // font white + } + // Graphic > Shapes > Filled > Green + { + aName =SdResId(STR_POOLSHEET_FILLED_GREEN); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aFilledName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED_GREEN ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetStartColor( Color(0x0077bc65) ); // light green 2 + aGradient.SetEndColor( Color(0x00127622) ); // dark green 2 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( aSvxFontItem ); // font name + pISet->Put( SvxColorItem(COL_WHITE, EE_CHAR_COLOR )); // font white + } + // Graphic > Shapes > Filled > Red + { + aName =SdResId(STR_POOLSHEET_FILLED_RED); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aFilledName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED_RED ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetStartColor( Color(0x00ff6d6d) ); // light red 2 + aGradient.SetEndColor( Color(0x00c9211e) ); // dark red 2 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( SvxColorItem(COL_WHITE, EE_CHAR_COLOR )); // font white + } + // Graphic > Shapes > Filled > Yellow + { + aName =SdResId(STR_POOLSHEET_FILLED_YELLOW); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aFilledName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_FILLED_YELLOW ); + pISet = &pSheet->GetItemSet(); + + aGradient.SetStartColor( Color(0x00ffde59) ); // light gold 2 + aGradient.SetEndColor( Color(0x00b47804) ); // dark gold 2 + aFillGradient.SetName( aName ); + aFillGradient.SetGradientValue(aGradient); + pISet->Put( aFillGradient ); + pISet->Put( SvxColorItem(COL_WHITE, EE_CHAR_COLOR )); // font white + } + // Graphic > Shapes > Outlines + OUString aOutlineName(SdResId(STR_POOLSHEET_OUTLINE)); + { + aName = aOutlineName; + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE ); + pSheet->SetParent( aShapesName ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XFillStyleItem(drawing::FillStyle_NONE) ); // clear + pISet->Put( XLineStyleItem(drawing::LineStyle_SOLID) ); // solide line + pISet->Put( XLineWidthItem(81) ); // 2.3 pt + pISet->Put( XLineColorItem(OUString(), COL_BLACK) ); // b/w + } + // Graphic > Shapes > Outlines > Blue + { + aName =SdResId(STR_POOLSHEET_OUTLINE_BLUE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aOutlineName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE_BLUE ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineColorItem(OUString(), Color(0x00355269)) ); // dark blue 2 + pISet->Put( SvxColorItem(Color(0x00355269), EE_CHAR_COLOR )); // font color + } + // Graphic > Shapes > Outlines > Green + { + aName =SdResId(STR_POOLSHEET_OUTLINE_GREEN); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aOutlineName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE_GREEN ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineColorItem(OUString(), Color(0x00127622)) ); // dark green 2 + pISet->Put( SvxColorItem(Color(0x00127622), EE_CHAR_COLOR )); // font color + } + // Graphic > Shapes > Outlines > Red + { + aName =SdResId(STR_POOLSHEET_OUTLINE_RED); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aOutlineName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE_RED ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineColorItem(OUString(), Color(0x00c9211e)) ); // dark red 2 + pISet->Put( SvxColorItem(Color(0x00c9211e), EE_CHAR_COLOR )); // font color + } + // Graphic > Shapes > Outlines > Yellow + { + aName =SdResId(STR_POOLSHEET_OUTLINE_YELLOW); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aOutlineName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_OUTLINE_YELLOW ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineStyleItem(drawing::LineStyle_SOLID)); + pISet->Put( XLineColorItem(OUString(), Color(0x00b47804)) ); // dark gold 2 + pISet->Put( SvxColorItem(Color(0x00b47804), EE_CHAR_COLOR )); // font color + } + // Graphic > Lines + OUString aLinesName; + { + aLinesName = SdResId(STR_POOLSHEET_LINES); + pSheet = &(pSSPool->Make(aLinesName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent( aGraphicName ); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_LINES); + pISet = &pSheet->GetItemSet(); + pISet->Put( XFillStyleItem(drawing::FillStyle_NONE) ); // clear + pISet->Put( XLineStyleItem(drawing::LineStyle_SOLID) ); // solide line + pISet->Put( XLineColorItem(OUString(), COL_BLACK) ); // b/w + } + // Graphic > Lines > Measurements + { + aName = SdResId(STR_POOLSHEET_MEASURE); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aLinesName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_MEASURE ); + pISet = &pSheet->GetItemSet(); + + ::basegfx::B2DPolygon aArrow; // arrows + aArrow.append(::basegfx::B2DPoint(10.0, 0.0)); + aArrow.append(::basegfx::B2DPoint(0.0, 30.0)); + aArrow.append(::basegfx::B2DPoint(20.0, 30.0)); + aArrow.setClosed(true); + + pISet->Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW),::basegfx::B2DPolyPolygon(aArrow))); + pISet->Put(XLineStartWidthItem(200)); + pISet->Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW),::basegfx::B2DPolyPolygon(aArrow))); + pISet->Put(XLineEndWidthItem(200)); + pISet->Put(SdrYesNoItem(SDRATTR_MEASURESHOWUNIT, true)); + } + // Graphic > Lines > Dashed + { + aName = SdResId(STR_POOLSHEET_LINES_DASHED); + pSheet = &(pSSPool->Make(aName, SfxStyleFamily::Para, nMask)); + pSheet->SetParent(aLinesName); + pSheet->SetHelpId( aHelpFile, HID_POOLSHEET_LINES_DASHED ); + pISet = &pSheet->GetItemSet(); + pISet->Put( XLineStyleItem(drawing::LineStyle_DASH) ); // dashed line + } + + // Generate presentation templates for default layout. + OUString aPrefix = SdResId(STR_LAYOUT_DEFAULT_NAME); + pSSPool->CreateLayoutStyleSheets(aPrefix); +} + +static Any implMakeSolidCellStyle( SdStyleSheetPool* pSSPool, const OUString& rName, const OUString& rParent, const Color& rColor ) +{ + SfxStyleSheetBase* pSheet = &(pSSPool->Make(rName, SfxStyleFamily::Frame, SfxStyleSearchBits::Auto)); + pSheet->SetParent(rParent); + SfxItemSet* pISet = &pSheet->GetItemSet(); + pISet->Put(XFillStyleItem(drawing::FillStyle_SOLID)); + pISet->Put(XFillColorItem(OUString(), rColor)); + + return Any( Reference< XStyle >( static_cast< XWeak* >( pSheet ), UNO_QUERY ) ); +} + +static void implCreateTableTemplate( const Reference< XNameContainer >& xTableFamily, const OUString& rName, const Any& rBody, const Any& rHeading, const Any& rBanding ) +{ + if( !xTableFamily.is() ) + return; + + try + { + if( !xTableFamily->hasByName( rName ) ) + { + Reference< XSingleServiceFactory > xFactory( xTableFamily, UNO_QUERY_THROW ); + Reference< XNameReplace > xDefaultTableStyle( xFactory->createInstance(), UNO_QUERY_THROW ); + xTableFamily->insertByName( rName, Any( xDefaultTableStyle ) ); + + xDefaultTableStyle->replaceByName( "body", rBody ); + xDefaultTableStyle->replaceByName( "odd-rows" , rBanding ); + xDefaultTableStyle->replaceByName( "odd-columns" , rBanding ); + xDefaultTableStyle->replaceByName( "first-row" , rHeading ); + xDefaultTableStyle->replaceByName( "first-column" , rHeading ); + xDefaultTableStyle->replaceByName( "last-row" , rHeading ); + xDefaultTableStyle->replaceByName( "last-column" , rHeading ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::implCreateTableTemplate()"); + } +} + +void SdDrawDocument::CreateDefaultCellStyles() +{ + SdStyleSheetPool* pSSPool = static_cast< SdStyleSheetPool* >(GetStyleSheetPool()); + SfxStyleSheetBase* pSheet = nullptr; + + Reference< XNameContainer > xTableFamily( pSSPool->getByName( "table" ), UNO_QUERY ); + + // ---- Default ----------------------------------------------- + + OUString aDefaultCellStyleName( "default" ); + + pSheet = &(pSSPool->Make(aDefaultCellStyleName, SfxStyleFamily::Frame, SfxStyleSearchBits::Auto)); + pSheet->SetHelpId( OUString(), HID_SD_CELL_STYLE_DEFAULT ); + SfxItemSet& rISet = pSheet->GetItemSet(); + + rISet.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + rISet.Put(XFillColorItem(OUString(), Color(0x00ccccff))); + + vcl::Font aLatinFont, aCJKFont, aCTLFont; + + getDefaultFonts( aLatinFont, aCJKFont, aCTLFont ); + + SvxFontItem aSvxFontItem( aLatinFont.GetFamilyType(), aLatinFont.GetFamilyName(), aLatinFont.GetStyleName(), aLatinFont.GetPitch(), + aLatinFont.GetCharSet(), EE_CHAR_FONTINFO ); + + SvxFontItem aSvxFontItemCJK( aCJKFont.GetFamilyType(), aCJKFont.GetFamilyName(), aCJKFont.GetStyleName(), aCJKFont.GetPitch(), + aCJKFont.GetCharSet(), EE_CHAR_FONTINFO_CJK ); + + SvxFontItem aSvxFontItemCTL( aCTLFont.GetFamilyType(), aCTLFont.GetFamilyName(), aCTLFont.GetStyleName(), aCTLFont.GetPitch(), + aCTLFont.GetCharSet(), EE_CHAR_FONTINFO_CTL ); + + rISet.Put( aSvxFontItem ); + rISet.Put( aSvxFontItemCJK ); + rISet.Put( aSvxFontItemCTL ); + + rISet.Put( SvxFontHeightItem( 635, 100, EE_CHAR_FONTHEIGHT ) ); // sj: (i33745) changed default from 24 to 18 pt + rISet.Put( SvxFontHeightItem( 635, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 18 pt + rISet.Put( SvxFontHeightItem( convertFontHeightToCTL( 635 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 18 pt + + rISet.Put(SvxColorItem(COL_AUTO, EE_CHAR_COLOR )); + + // Paragraph attributes (Edit Engine) + rISet.Put(SvxLRSpaceItem(EE_PARA_LRSPACE)); + rISet.Put(SvxULSpaceItem(EE_PARA_ULSPACE)); + + rISet.Put( makeSdrTextLeftDistItem( 250 ) ); + rISet.Put( makeSdrTextRightDistItem( 250 ) ); + rISet.Put( makeSdrTextUpperDistItem( 130 ) ); + rISet.Put( makeSdrTextLowerDistItem( 130 ) ); + + rISet.Put( SvxLineSpacingItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ) ); + rISet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + rISet.Put( SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP) ); + rISet.Put( SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT) ); + + Color aWhite( COL_WHITE ); + ::editeng::SvxBorderLine aBorderLine( + &aWhite, 1, SvxBorderLineStyle::SOLID); + + SvxBoxItem aBoxItem( SDRATTR_TABLE_BORDER ); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::TOP ); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::BOTTOM ); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::LEFT ); + aBoxItem.SetLine( &aBorderLine, SvxBoxItemLine::RIGHT ); + + rISet.Put( aBoxItem ); + + // ---- default -------------------------------------------------- + + Any aGray1( implMakeSolidCellStyle( pSSPool, "gray1" , aDefaultCellStyleName, Color(230,230,230))); + Any aGray2( implMakeSolidCellStyle( pSSPool, "gray2" , aDefaultCellStyleName, Color(204,204,204))); + Any aGray3( implMakeSolidCellStyle( pSSPool, "gray3" , aDefaultCellStyleName, Color(179,179,179))); + + implCreateTableTemplate( xTableFamily, "default" , aGray1, aGray3, aGray2 ); + + // ---- BW ------------------------------------------------ + + Any aBW1( implMakeSolidCellStyle( pSSPool, "bw1" , aDefaultCellStyleName, Color(255,255,255))); + Any aBW2( implMakeSolidCellStyle( pSSPool, "bw2" , aDefaultCellStyleName, Color(230,230,230))); + Any aBW3( implMakeSolidCellStyle( pSSPool, "bw3" , aDefaultCellStyleName, Color(0,0,0))); + + implCreateTableTemplate( xTableFamily, "bw" , aBW1, aBW3, aBW2 ); + + // ---- Orange -------------------------------------------------- + + Any aOrange1( implMakeSolidCellStyle( pSSPool, "orange1" , aDefaultCellStyleName, Color(255,204,153))); + Any aOrange2( implMakeSolidCellStyle( pSSPool, "orange2" , aDefaultCellStyleName, Color(255,153,102))); + Any aOrange3( implMakeSolidCellStyle( pSSPool, "orange3" , aDefaultCellStyleName, Color(255,102,51))); + + implCreateTableTemplate( xTableFamily, "orange" , aOrange1, aOrange3, aOrange2 ); + + // ---- Turquoise -------------------------------------------------- + + Any aTurquoise1( implMakeSolidCellStyle( pSSPool, "turquoise1" , aDefaultCellStyleName, Color(71,184,184))); + Any aTurquoise2( implMakeSolidCellStyle( pSSPool, "turquoise2" , aDefaultCellStyleName, Color(51,163,163))); + Any aTurquoise3( implMakeSolidCellStyle( pSSPool, "turquoise3" , aDefaultCellStyleName, Color(25,138,138))); + + implCreateTableTemplate( xTableFamily, "turquoise" , aTurquoise1, aTurquoise3, aTurquoise2 ); + + // ---- Gray ------------------------------------------------ + + Any aBlue1( implMakeSolidCellStyle( pSSPool, "blue1" , aDefaultCellStyleName, Color(153,204,255))); + Any aBlue2( implMakeSolidCellStyle( pSSPool, "blue2" , aDefaultCellStyleName, Color(0,153,255))); + Any aBlue3( implMakeSolidCellStyle( pSSPool, "blue3" , aDefaultCellStyleName, Color(0,102,204))); + + implCreateTableTemplate( xTableFamily, "blue" , aBlue1, aBlue3, aBlue2 ); + + // ---- Sun ------------------------------------------------ + + Any aSun1( implMakeSolidCellStyle( pSSPool, "sun1" , aDefaultCellStyleName, Color(230,230,255))); + Any aSun2( implMakeSolidCellStyle( pSSPool, "sun2" , aDefaultCellStyleName, Color(204,204,255))); + Any aSun3( implMakeSolidCellStyle( pSSPool, "sun3" , aDefaultCellStyleName, Color(153,153,255))); + + implCreateTableTemplate( xTableFamily, "sun" , aSun1, aSun3, aSun2 ); + + // ---- Earth ---------------------------------------------- + + Any aEarth1( implMakeSolidCellStyle( pSSPool, "earth1" , aDefaultCellStyleName, Color(255,255,204))); + Any aEarth2( implMakeSolidCellStyle( pSSPool, "earth2" , aDefaultCellStyleName, Color(255,204,153))); + Any aEarth3( implMakeSolidCellStyle( pSSPool, "earth3" , aDefaultCellStyleName, Color(204,102,51))); + + implCreateTableTemplate( xTableFamily, "earth" , aEarth1, aEarth3, aEarth2 ); + + // ---- Green ---------------------------------------------- + + Any aGreen1( implMakeSolidCellStyle( pSSPool, "green1" , aDefaultCellStyleName, Color(255,255,204))); + Any aGreen2( implMakeSolidCellStyle( pSSPool, "green2" , aDefaultCellStyleName, Color(148,189,94))); + Any aGreen3( implMakeSolidCellStyle( pSSPool, "green3" , aDefaultCellStyleName, Color(92,133,38))); + + implCreateTableTemplate( xTableFamily, "green" , aGreen1, aGreen3, aGreen2 ); + + // ---- Seaweed ---------------------------------------------- + + Any aSeetang1( implMakeSolidCellStyle( pSSPool, "seetang1" , aDefaultCellStyleName, Color(204,255,255))); + Any aSeetang2( implMakeSolidCellStyle( pSSPool, "seetang2" , aDefaultCellStyleName, Color(71,184,184))); + Any aSeetang3( implMakeSolidCellStyle( pSSPool, "seetang3" , aDefaultCellStyleName, Color(51,163,163))); + + implCreateTableTemplate( xTableFamily, "seetang" , aSeetang1, aSeetang3, aSeetang2 ); + + // ---- LightBlue ---------------------------------------------- + + Any aLightBlue1( implMakeSolidCellStyle( pSSPool, "lightblue1" , aDefaultCellStyleName, Color(255,255,255))); + Any aLightBlue2( implMakeSolidCellStyle( pSSPool, "lightblue2" , aDefaultCellStyleName, Color(230,230,255))); + Any aLightBlue3( implMakeSolidCellStyle( pSSPool, "lightblue3" , aDefaultCellStyleName, Color(153,153,204))); + + implCreateTableTemplate( xTableFamily, "lightblue" , aLightBlue1, aLightBlue3, aLightBlue2 ); + + // ---- Yellow ---------------------------------------------- + + Any aYellow1( implMakeSolidCellStyle( pSSPool, "yellow1" , aDefaultCellStyleName, Color(255,255,204))); + Any aYellow2( implMakeSolidCellStyle( pSSPool, "yellow2" , aDefaultCellStyleName, Color(255,255,153))); + Any aYellow3( implMakeSolidCellStyle( pSSPool, "yellow3" , aDefaultCellStyleName, Color(255,204,153))); + + implCreateTableTemplate( xTableFamily, "yellow" , aYellow1, aYellow3, aYellow2 ); +} + +// Number of pages that reference a master page +sal_uInt16 SdDrawDocument::GetMasterPageUserCount(SdrPage const * pMaster) const +{ + sal_uInt16 nResult = 0; + sal_uInt16 nPage; + sal_uInt16 nPageCount = GetPageCount(); + + for (nPage = 0; nPage < nPageCount; nPage++) + { + const SdrPage* pPage = GetPage(nPage); + + if(pPage->TRG_HasMasterPage()) + { + if(&(pPage->TRG_GetMasterPage()) == pMaster) + { + nResult++; + } + } + } + return nResult; +} + +// Finish OnlineSpelling in the background + +void SdDrawDocument::StopOnlineSpelling() +{ + if (mpOnlineSpellingIdle && mpOnlineSpellingIdle->IsActive()) + { + mpOnlineSpellingIdle->Stop(); + } + + mpOnlineSpellingIdle.reset(); + mpOnlineSpellingList.reset(); +} + +// Start OnlineSpelling in the background +void SdDrawDocument::StartOnlineSpelling(bool bForceSpelling) +{ + if ( !mbOnlineSpell || !(bForceSpelling || mbInitialOnlineSpellingEnabled) || + !mpDocSh || mpDocSh->IsReadOnly() ) + return; + + StopOnlineSpelling(); + + SdOutliner* pOutl = GetInternalOutliner(); + + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + pOutl->SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + pOutl->SetHyphenator( xHyphenator ); + + pOutl->SetDefaultLanguage( meLanguage ); + + mpOnlineSpellingList.reset(new ShapeList); + sal_uInt16 nPage; + + for ( nPage = 0; nPage < GetPageCount(); nPage++ ) + { + // Search in all pages + FillOnlineSpellingList(static_cast(GetPage(nPage))); + } + + for (nPage = 0; nPage < GetMasterPageCount(); nPage++) + { + // Search all master pages + FillOnlineSpellingList(static_cast( GetMasterPage(nPage) )); + } + + mpOnlineSpellingList->seekShape(0); + mpOnlineSpellingIdle.reset(new Idle("OnlineSpelling")); + mpOnlineSpellingIdle->SetInvokeHandler( LINK(this, SdDrawDocument, OnlineSpellingHdl) ); + mpOnlineSpellingIdle->SetPriority(TaskPriority::LOWEST); + mpOnlineSpellingIdle->Start(); +} + +// Fill OnlineSpelling list +void SdDrawDocument::FillOnlineSpellingList(SdPage const * pPage) +{ + SdrObjListIter aIter(pPage, SdrIterMode::Flat); + + while (aIter.IsMore()) + { + SdrObject* pObj = aIter.Next(); + + if( !pObj ) + continue; + + if (pObj->GetOutlinerParaObject()) + { + // Found a text object + mpOnlineSpellingList->addShape(*pObj); + } + else if (pObj->GetObjIdentifier() == SdrObjKind::Group) + { + // Found a group object + SdrObjListIter aGroupIter(static_cast< SdrObjGroup* >(pObj)->GetSubList(), SdrIterMode::DeepNoGroups); + + bool bSubTextObjFound = false; + + while (aGroupIter.IsMore() && !bSubTextObjFound) + { + if (aGroupIter.Next()->GetOutlinerParaObject()) + { + // Found a text object in a group object + bSubTextObjFound = true; + } + } + + if (bSubTextObjFound) + { + mpOnlineSpellingList->addShape(*pObj); + } + } + } +} + +// OnlineSpelling in the background +IMPL_LINK_NOARG(SdDrawDocument, OnlineSpellingHdl, Timer *, void) +{ + if (mpOnlineSpellingList!=nullptr + && ( !mbOnlineSpell || mpOnlineSpellingList->hasMore())) + { + // Spell next object + SdrObject* pObj = mpOnlineSpellingList->getNextShape(); + + if (pObj) + { + if (pObj->GetOutlinerParaObject() && dynamic_cast< const SdrTextObj *>( pObj ) != nullptr) + { + // Spell text object + SpellObject(static_cast(pObj)); + } + else if (pObj->GetObjIdentifier() == SdrObjKind::Group) + { + // Found a group object + SdrObjListIter aGroupIter(static_cast< SdrObjGroup* >(pObj)->GetSubList(), SdrIterMode::DeepNoGroups); + + + while (aGroupIter.IsMore()) + { + SdrObject* pSubObj = aGroupIter.Next(); + + if (pSubObj->GetOutlinerParaObject()) + if (auto pTextObj = dynamic_cast< SdrTextObj *>( pSubObj )) + // Found a text object in a group object + SpellObject(pTextObj); + } + } + } + + // Continue search + mpOnlineSpellingIdle->Start(); + } + else + { + // Initial spelling has finished + mbInitialOnlineSpellingEnabled = false; + + // Stop search + StopOnlineSpelling(); + + mpOnlineSearchItem.reset(); + } +} + +// Spell object (for OnlineSpelling) +void SdDrawDocument::SpellObject(SdrTextObj* pObj) +{ + if (!(pObj && pObj->GetOutlinerParaObject()) /* && pObj != pView->GetTextEditObject() */) + return; + + mbHasOnlineSpellErrors = false; + SdOutliner* pOutl = GetInternalOutliner(); + pOutl->SetUpdateLayout(true); + Link aEvtHdl = pOutl->GetStatusEventHdl(); + pOutl->SetStatusEventHdl(LINK(this, SdDrawDocument, OnlineSpellEventHdl)); + + OutlinerMode nOldOutlMode = pOutl->GetOutlinerMode(); + OutlinerMode nOutlMode = OutlinerMode::TextObject; + if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::OutlineText) + { + nOutlMode = OutlinerMode::OutlineObject; + } + pOutl->Init( nOutlMode ); + + // Put text into the outliner + pOutl->SetText(*pObj->GetOutlinerParaObject()); + + if (!mpOnlineSearchItem || pOutl->HasText(*mpOnlineSearchItem)) + { + // Spelling + pOutl->CompleteOnlineSpelling(); + + if (mbHasOnlineSpellErrors) + { + std::optional pOPO = pOutl->CreateParaObject(); + if (pOPO) + { + if ( *pOPO != *pObj->GetOutlinerParaObject() || + !pObj->GetOutlinerParaObject()->isWrongListEqual( *pOPO )) + { + sd::ModifyGuard aGuard( this ); + + // taking text from the outliner + // use non-broadcasting version to avoid O(n^2) + pObj->NbcSetOutlinerParaObject( std::move(pOPO) ); + } + } + } + } + + pOutl->SetStatusEventHdl(aEvtHdl); + pOutl->SetUpdateLayout(false); + pOutl->Init( nOldOutlMode ); + mbHasOnlineSpellErrors = false; +} + +// Object was inserted into model +void SdDrawDocument::InsertObject(SdrObject* pObj) +{ + if(mpOnlineSpellingList && pObj) + { + if (pObj->GetOutlinerParaObject() || (pObj->GetObjIdentifier() == SdrObjKind::Group)) + { + // Add object to OnlineSpelling list + mpOnlineSpellingList->addShape(*pObj); + } + } +} + +// Object removed from model +void SdDrawDocument::RemoveObject(SdrObject* pObj) +{ + if(mpOnlineSpellingList && pObj) + { + if (pObj->GetOutlinerParaObject() || (pObj->GetObjIdentifier() == SdrObjKind::Group)) + { + // Replace object in OnlineSpelling list by 0 pointer + mpOnlineSpellingList->removeShape(*pObj); + } + } +} + +// Callback for ExecuteSpellPopup() +IMPL_LINK(SdDrawDocument, OnlineSpellEventHdl, EditStatus&, rEditStat, void) +{ + EditStatusFlags nStat = rEditStat.GetStatusWord(); + mbHasOnlineSpellErrors = bool(nStat & EditStatusFlags::WRONGWORDCHANGED); +} + +// Callback for ExecuteSpellPopup() + +// removed link and replaced with Imp method +void SdDrawDocument::ImpOnlineSpellCallback(SpellCallbackInfo const * pInfo, SdrObject* pObj, SdrOutliner const * pOutl) +{ + mpOnlineSearchItem.reset(); + + SpellCallbackCommand nCommand = pInfo->nCommand; + + if (nCommand == SpellCallbackCommand::IGNOREWORD + // restart when add to dictionary takes place, too. + || nCommand == SpellCallbackCommand::ADDTODICTIONARY) + { + if(pOutl) + if (auto pTextObj = dynamic_cast( pObj )) + { + bool bModified(IsChanged()); + pTextObj->SetOutlinerParaObject(pOutl->CreateParaObject()); + SetChanged(bModified); + pObj->BroadcastObjectChange(); + } + + mpOnlineSearchItem.reset(new SvxSearchItem( SID_SEARCH_ITEM ) ); + mpOnlineSearchItem->SetSearchString(pInfo->aWord); + StartOnlineSpelling(); + } + else if (nCommand == SpellCallbackCommand::STARTSPELLDLG) + { + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + pViewFrame->GetDispatcher()->Execute( SID_SPELL_DIALOG, SfxCallMode::ASYNCHRON ); + } + else if (nCommand == SpellCallbackCommand::AUTOCORRECT_OPTIONS) + { + if (SfxViewFrame* pViewFrame = SfxViewFrame::Current()) + pViewFrame->GetDispatcher()->Execute( SID_AUTO_CORRECT_DLG, SfxCallMode::ASYNCHRON ); + } +} + +// Return formatted page number (1, I, i, a, etc.) +OUString SdDrawDocument::CreatePageNumValue(sal_uInt16 nNum) const +{ + OUString aPageNumValue; + bool bUpper = false; + + switch (mePageNumType) + { + case css::style::NumberingType::CHARS_UPPER_LETTER: + aPageNumValue += OUStringChar( sal_Unicode((nNum - 1) % 26 + 'A') ); + break; + case css::style::NumberingType::CHARS_LOWER_LETTER: + aPageNumValue += OUStringChar( sal_Unicode((nNum - 1) % 26 + 'a') ); + break; + case css::style::NumberingType::ROMAN_UPPER: + bUpper = true; + [[fallthrough]]; + case css::style::NumberingType::ROMAN_LOWER: + aPageNumValue += SvxNumberFormat::CreateRomanString(nNum, bUpper); + break; + case css::style::NumberingType::NUMBER_NONE: + aPageNumValue = " "; + break; + default: + aPageNumValue += OUString::number(nNum); + } + + return aPageNumValue; +} + +// Rename layout template +// Keep in mind that rOldLayoutName contains the _complete_ name of the layout +// (including ~LT~). This is unlike rNewName. +void SdDrawDocument::RenameLayoutTemplate(const OUString& rOldLayoutName, const OUString& rNewName) +{ + OUString aSep(SD_LT_SEPARATOR); + OUString aOldName(rOldLayoutName); + sal_Int32 nPos = aOldName.indexOf( aSep ); + + // erase everything after '~LT~' + if (nPos != -1) + aOldName = aOldName.copy(0, nPos + aSep.getLength()); + + std::vector aReplList; + SfxStyleSheetIterator aIter(mxStyleSheetPool.get(), SfxStyleFamily::Page); + SfxStyleSheetBase* pSheet = aIter.First(); + + while (pSheet) + { + OUString aSheetName = pSheet->GetName(); + + // if the sheetname starts with aOldName + "~LT~" + if (aSheetName.startsWith(aOldName)) + { + aSheetName = aSheetName.replaceAt(0, aOldName.getLength() - aSep.getLength(), rNewName); + + StyleReplaceData aReplData; + aReplData.nFamily = pSheet->GetFamily(); + aReplData.nNewFamily = pSheet->GetFamily(); + aReplData.aName = pSheet->GetName(); + aReplData.aNewName = aSheetName; + aReplList.push_back(aReplData); + + pSheet->SetName(aSheetName); + } + + pSheet = aIter.Next(); + } + + // Now set the layout name of the drawing and the notes page, as well as + // their master pages. + OUString aPageLayoutName = rNewName + aSep + STR_LAYOUT_OUTLINE; + + // Inform all text objects on pages that use the renamed layout and set the + // new name. + sal_uInt16 nPage; + for (nPage = 0; nPage < GetPageCount(); nPage++) + { + SdPage* pPage = static_cast(GetPage(nPage)); + OUString aTemp(pPage->GetLayoutName()); + + if (aTemp == rOldLayoutName) + { + pPage->SetLayoutName(aPageLayoutName); + + for (size_t nObj = 0; nObj < pPage->GetObjCount(); ++nObj) + { + SdrObject* pObj = pPage->GetObj(nObj); + + if (pObj->GetObjInventor() == SdrInventor::Default) + { + switch( pObj->GetObjIdentifier() ) + { + case SdrObjKind::Text: + case SdrObjKind::OutlineText: + case SdrObjKind::TitleText: + { + OutlinerParaObject* pOPO = static_cast(pObj)->GetOutlinerParaObject(); + + if (pOPO) + { + for (const auto& rRepl : aReplList) + pOPO->ChangeStyleSheets( rRepl.aName, rRepl.nFamily, rRepl.aNewName, rRepl.nNewFamily ); + } + } + break; + + default: + break; + } + } + } + } + } + + // Now do this again for all master pages. + // The affected master pages get the name of the layout as their page name. + for (nPage = 0; nPage < GetMasterPageCount(); nPage++) + { + SdPage* pPage = static_cast( GetMasterPage(nPage) ); + OUString aTemp(pPage->GetLayoutName()); + + if (aTemp == rOldLayoutName) + { + pPage->SetLayoutName(aPageLayoutName); + pPage->SetName(rNewName); + + for (size_t nObj = 0; nObj < pPage->GetObjCount(); ++nObj) + { + SdrObject* pObj = pPage->GetObj(nObj); + + if (pObj->GetObjInventor() == SdrInventor::Default) + { + switch(pObj->GetObjIdentifier()) + { + case SdrObjKind::Text: + case SdrObjKind::OutlineText: + case SdrObjKind::TitleText: + { + OutlinerParaObject* pOPO = static_cast(pObj)->GetOutlinerParaObject(); + + if (pOPO) + { + for (const auto& rRepl : aReplList) + pOPO->ChangeStyleSheets( rRepl.aName, rRepl.nFamily, rRepl.aNewName, rRepl.nNewFamily ); + } + } + break; + + default: + break; + } + } + } + } + } +} + +// Set outliner defaults (pool defaults) +void SdDrawDocument::SetTextDefaults() const +{ + // BulletItem and BulletFont for Title and Outline + SvxBulletItem aBulletItem(EE_PARA_BULLET); + vcl::Font aBulletFont( SdStyleSheetPool::GetBulletFont() ); + aBulletFont.SetFontSize(Size(0,846)); // 24 pt + aBulletItem.SetFont(aBulletFont); + aBulletItem.SetStyle(SvxBulletStyle::BULLET); + aBulletItem.SetStart(1); + aBulletItem.SetScale(45); // In percent + aBulletItem.SetSymbol( 0x25CF ); // In points + m_pItemPool->SetPoolDefaultItem( aBulletItem ); + + // New BulletItem + SvxNumberFormat aNumberFormat(SVX_NUM_CHAR_SPECIAL); + aNumberFormat.SetBulletFont(&aBulletFont); + aNumberFormat.SetBulletChar( 0x25CF ); // StarBats: 0xF000 + 34 + aNumberFormat.SetBulletRelSize(45); + aNumberFormat.SetBulletColor(COL_AUTO); + aNumberFormat.SetStart(1); + aNumberFormat.SetNumAdjust(SvxAdjust::Left); + + SvxNumRule aNumRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::BULLET_COLOR, SVX_MAX_NUM, false); + + //aNumberFormat.SetAbsLSpace( 0 ); + //aNumberFormat.SetFirstLineOffset( 0 ); + //aNumRule.SetLevel( 0, aNumberFormat ); + + for( sal_uInt16 i = 0; i < aNumRule.GetLevelCount(); i++ ) + { + const auto nLSpace = (i + 1) * 600; + aNumberFormat.SetAbsLSpace(nLSpace); + aNumberFormat.SetFirstLineOffset(-600); + aNumRule.SetLevel( i, aNumberFormat ); + } + + SvxNumBulletItem aNumBulletItem( std::move(aNumRule), EE_PARA_NUMBULLET ); + m_pItemPool->SetPoolDefaultItem( aNumBulletItem ); +} + +css::text::WritingMode SdDrawDocument::GetDefaultWritingMode() const +{ + const SfxPoolItem* pItem = ( m_pItemPool ? m_pItemPool->GetPoolDefaultItem( EE_PARA_WRITINGDIR ) : nullptr ); + css::text::WritingMode eRet = css::text::WritingMode_LR_TB; + + if( pItem ) + { + switch( static_cast( *pItem ).GetValue() ) + { + case SvxFrameDirection::Horizontal_LR_TB: eRet = css::text::WritingMode_LR_TB; break; + case SvxFrameDirection::Horizontal_RL_TB: eRet = css::text::WritingMode_RL_TB; break; + case SvxFrameDirection::Vertical_RL_TB: eRet = css::text::WritingMode_TB_RL; break; + + default: + OSL_FAIL( "Frame direction not supported yet" ); + break; + } + } + + return eRet; +} + +void SdDrawDocument::SetDefaultWritingMode(css::text::WritingMode eMode ) +{ + if( !m_pItemPool ) + return; + + SvxFrameDirection nVal; + switch( eMode ) + { + case css::text::WritingMode_LR_TB: nVal = SvxFrameDirection::Horizontal_LR_TB; break; + case css::text::WritingMode_RL_TB: nVal = SvxFrameDirection::Horizontal_RL_TB; break; + case css::text::WritingMode_TB_RL: nVal = SvxFrameDirection::Vertical_RL_TB; break; + default: + OSL_FAIL( "Frame direction not supported yet" ); + return; + } + + SvxFrameDirectionItem aModeItem( nVal, EE_PARA_WRITINGDIR ); + m_pItemPool->SetPoolDefaultItem( aModeItem ); + + SvxAdjustItem aAdjust( SvxAdjust::Left, EE_PARA_JUST ); + + if( eMode == css::text::WritingMode_RL_TB ) + aAdjust.SetAdjust( SvxAdjust::Right ); + + m_pItemPool->SetPoolDefaultItem( aAdjust ); +} + +void SdDrawDocument::getDefaultFonts( vcl::Font& rLatinFont, vcl::Font& rCJKFont, vcl::Font& rCTLFont ) +{ + LanguageType eLatin = GetLanguage( EE_CHAR_LANGUAGE ); + + // If the UI language is Korean, the default Latin font has to + // be queried for Korean, too (the Latin language from the document can't be Korean). + // This is the same logic as in SwDocShell::InitNew. + LanguageType eUiLanguage = Application::GetSettings().GetUILanguageTag().getLanguageType(); + if (MsLangId::isKorean(eUiLanguage)) + eLatin = eUiLanguage; + + rLatinFont = OutputDevice::GetDefaultFont( DefaultFontType::LATIN_PRESENTATION, eLatin, GetDefaultFontFlags::OnlyOne ); + rCJKFont = OutputDevice::GetDefaultFont( DefaultFontType::CJK_PRESENTATION, GetLanguage( EE_CHAR_LANGUAGE_CJK ), GetDefaultFontFlags::OnlyOne ); + rCTLFont = OutputDevice::GetDefaultFont( DefaultFontType::CTL_PRESENTATION, GetLanguage( EE_CHAR_LANGUAGE_CTL ), GetDefaultFontFlags::OnlyOne ) ; +} + +/* converts the given western font height to a corresponding ctl font height, depending on the system language */ +sal_uInt32 SdDrawDocument::convertFontHeightToCTL( sal_uInt32 nWesternFontHeight ) +{ + LanguageType eRealCTLLanguage = Application::GetSettings().GetLanguageTag().getLanguageType(); + if( LANGUAGE_THAI == eRealCTLLanguage ) + { + // http://specs.openoffice.org/g11n/font_sizes/42775_42725_Individual_configurable_font_size_for_default_fonts.odt + double fTemp = double(nWesternFontHeight) * 1.333; + nWesternFontHeight = static_cast(fTemp); + // make some nice values for UI that displays PT instead of 1/100th mm + nWesternFontHeight = convertPointToMm100(convertMm100ToPoint(nWesternFontHeight)); + } + return nWesternFontHeight; +} + +SdStyleSheetPool* SdDrawDocument::GetSdStyleSheetPool() const +{ + return dynamic_cast< SdStyleSheetPool* >( GetStyleSheetPool() ); +} + +ModifyGuard::ModifyGuard( SdDrawDocument* pDoc ) +: mpDocShell( nullptr ), mpDoc( pDoc ) +{ + init(); +} + +void ModifyGuard::init() +{ + if( mpDocShell ) + { + mpDoc = mpDocShell->GetDoc(); + } + else if( mpDoc ) + { + mpDocShell = mpDoc->GetDocSh(); + } + + mbIsEnableSetModified = mpDocShell && mpDocShell->IsEnableSetModified(); + mbIsDocumentChanged = mpDoc && mpDoc->IsChanged(); + + if( mbIsEnableSetModified ) + mpDocShell->EnableSetModified( false ); +} + +ModifyGuard::~ModifyGuard() +{ + if( mbIsEnableSetModified ) + mpDocShell->EnableSetModified(); + + if( mpDoc && (mpDoc->IsChanged() != mbIsDocumentChanged) ) + mpDoc->SetChanged(mbIsDocumentChanged); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/drawdoc_animations.cxx b/sd/source/core/drawdoc_animations.cxx new file mode 100644 index 000000000..b7f1bd557 --- /dev/null +++ b/sd/source/core/drawdoc_animations.cxx @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; + +/** replaces a slide from all custom shows with a new one or removes a slide from + all custom shows if pNewPage is 0. +*/ +void SdDrawDocument::ReplacePageInCustomShows(const SdPage* pOldPage, const SdPage* pNewPage) +{ + if (mpCustomShowList) + { + for (size_t i = 0; i < mpCustomShowList->size(); i++) + { + SdCustomShow* pCustomShow = (*mpCustomShowList)[i].get(); + pCustomShow->ReplacePage(pOldPage, pNewPage); + } + } +} + +const Reference& SdDrawDocument::getPresentation() const +{ + if (!mxPresentation.is()) + { + const_cast(this)->mxPresentation = CreatePresentation(*this); + } + return mxPresentation; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/pglink.cxx b/sd/source/core/pglink.cxx new file mode 100644 index 000000000..358012df9 --- /dev/null +++ b/sd/source/core/pglink.cxx @@ -0,0 +1,128 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +/************************************************************************* +|* +|* Ctor +|* +\************************************************************************/ + +SdPageLink::SdPageLink(SdPage* pPg, const OUString& rFileName, + const OUString& rBookmarkName) : + ::sfx2::SvBaseLink( ::SfxLinkUpdateMode::ONCALL, SotClipboardFormatId::SIMPLE_FILE), + pPage(pPg) +{ + pPage->SetFileName(rFileName); + pPage->SetBookmarkName(rBookmarkName); +} + +/************************************************************************* +|* +|* Dtor +|* +\************************************************************************/ + +SdPageLink::~SdPageLink() +{ +} + +/************************************************************************* +|* +|* Date have changed +|* +\************************************************************************/ + +::sfx2::SvBaseLink::UpdateResult SdPageLink::DataChanged( + const OUString&, const css::uno::Any& ) +{ + SdDrawDocument* pDoc = static_cast( &pPage->getSdrModelFromSdrPage() ); + sfx2::LinkManager* pLinkManager = pDoc!=nullptr ? pDoc->GetLinkManager() : nullptr; + + if (pLinkManager) + { + /********************************************************************** + * Only standard pages are allowed to be linked + * The corresponding note pages are updated automatically + **********************************************************************/ + OUString aFileName; + OUString aBookmarkName; + OUString aFilterName; + sfx2::LinkManager::GetDisplayNames( this,nullptr, &aFileName, &aBookmarkName, + &aFilterName); + pPage->SetFileName(aFileName); + pPage->SetBookmarkName(aBookmarkName); + + SdDrawDocument* pBookmarkDoc = pDoc->OpenBookmarkDoc(aFileName); + + if (pBookmarkDoc) + { + /****************************************************************** + * the linked page is replaced in the model + ******************************************************************/ + if (aBookmarkName.isEmpty()) + { + // no page name specified: we assume it is the first page + aBookmarkName = pBookmarkDoc->GetSdPage(0, PageKind::Standard)->GetName(); + pPage->SetBookmarkName(aBookmarkName); + } + + std::vector aBookmarkList { aBookmarkName }; + sal_uInt16 nInsertPos = pPage->GetPageNum(); + bool bNoDialogs = false; + bool bCopy = false; + + if (SdDrawDocument::s_pDocLockedInsertingLinks) + { + // resolving links while loading pDoc + bNoDialogs = true; + bCopy = true; + } + + pDoc->InsertBookmarkAsPage(aBookmarkList, nullptr, true/*bLink*/, true/*bReplace*/, + nInsertPos, bNoDialogs, nullptr, bCopy, true, true); + + if (!SdDrawDocument::s_pDocLockedInsertingLinks) + pDoc->CloseBookmarkDoc(); + } + } + return SUCCESS; +} + +/************************************************************************* +|* +|* Connect or disconnect link +|* +\************************************************************************/ + +void SdPageLink::Closed() +{ + // the connection is closed + pPage->SetFileName(OUString()); + pPage->SetBookmarkName(OUString()); + + SvBaseLink::Closed(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/sdiocmpt.cxx b/sd/source/core/sdiocmpt.cxx new file mode 100644 index 000000000..67de6a64d --- /dev/null +++ b/sd/source/core/sdiocmpt.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +old_SdrDownCompat::old_SdrDownCompat(SvStream& rNewStream, StreamMode nNewMode) +: rStream(rNewStream), + nSubRecSiz(0), + nSubRecPos(0), + nMode(nNewMode), + bOpen(false) +{ + OpenSubRecord(); +} + +old_SdrDownCompat::~old_SdrDownCompat() +{ + if(bOpen) + CloseSubRecord(); +} + +void old_SdrDownCompat::Write() +{ + rStream.WriteUInt32( nSubRecSiz ); +} + +void old_SdrDownCompat::OpenSubRecord() +{ + if(rStream.GetError()) + return; + + nSubRecPos = rStream.Tell(); + + if(nMode == StreamMode::READ) + { + rStream.ReadUInt32( nSubRecSiz ); + } + else if(nMode == StreamMode::WRITE) + { + Write(); + } + + bOpen = true; +} + +void old_SdrDownCompat::CloseSubRecord() +{ + if(rStream.GetError()) + return; + + sal_uInt32 nCurrentPos(rStream.Tell()); + + if(nMode == StreamMode::READ) + { + sal_uInt32 nReadCnt(nCurrentPos - nSubRecPos); + if(nReadCnt != nSubRecSiz) + { + rStream.Seek(nSubRecPos + nSubRecSiz); + } + } + else if(nMode == StreamMode::WRITE) + { + nSubRecSiz = nCurrentPos - nSubRecPos; + rStream.Seek(nSubRecPos); + Write(); + rStream.Seek(nCurrentPos); + } + + bOpen = false; +} + +/************************************************************************* +|* +|* Constructor, writes and reads version number +|* +\************************************************************************/ + +SdIOCompat::SdIOCompat(SvStream& rNewStream, StreamMode nNewMode, sal_uInt16 nVersion) +: old_SdrDownCompat(rNewStream, nNewMode) +{ + if (nNewMode == StreamMode::WRITE) + { + DBG_ASSERT(nVersion != SDIOCOMPAT_VERSIONDONTKNOW, + "can't write unknown version"); + rNewStream.WriteUInt16( nVersion ); + } + else if (nNewMode == StreamMode::READ) + { + DBG_ASSERT(nVersion == SDIOCOMPAT_VERSIONDONTKNOW, + "referring to the version while reading is silly!"); + rNewStream.ReadUInt16( nVersion ); + } +} + +SdIOCompat::~SdIOCompat() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/sdpage.cxx b/sd/source/core/sdpage.cxx new file mode 100644 index 000000000..38318f294 --- /dev/null +++ b/sd/source/core/sdpage.cxx @@ -0,0 +1,3157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace com::sun::star::xml::dom; +using ::com::sun::star::uno::Reference; + + +sal_uInt16 SdPage::mnLastPageId = 1; + +/************************************************************************* +|* +|* Ctor +|* +\************************************************************************/ + +SdPage::SdPage(SdDrawDocument& rNewDoc, bool bMasterPage) +: FmFormPage(rNewDoc, bMasterPage) +, SdrObjUserCall() +, mePageKind(PageKind::Standard) +, meAutoLayout(AUTOLAYOUT_NONE) +, mbSelected(false) +, mePresChange(PresChange::Manual) +, mfTime(1.0) +, mbSoundOn(false) +, mbExcluded(false) +, mbLoopSound(false) +, mbStopSound(false) +, mbScaleObjects(true) +, meCharSet(osl_getThreadTextEncoding()) +, mnPaperBin(PAPERBIN_PRINTER_SETTINGS) +, mpPageLink(nullptr) +, mnTransitionType(0) +, mnTransitionSubtype(0) +, mbTransitionDirection(true) +, mnTransitionFadeColor(0) +, mfTransitionDuration(2.0) +, mbIsPrecious(true) +, mnPageId(mnLastPageId++) +{ + // The name of the layout of the page is used by SVDRAW to determine the + // presentation template of the outline objects. Therefore, it already + // contains the designator for the outline (STR_LAYOUT_OUTLINE). + maLayoutName = SdResId(STR_LAYOUT_DEFAULT_NAME)+ SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + // Stuff that former SetModel did also: + ConnectLink(); +} + +namespace +{ + void clearChildNodes(css::uno::Reference const & rAnimationNode) + { + css::uno::Reference xEnumerationAccess(rAnimationNode, UNO_QUERY); + if (!xEnumerationAccess.is()) + return; + css::uno::Reference xEnumeration = xEnumerationAccess->createEnumeration(); + if (!xEnumeration.is()) + return; + while (xEnumeration->hasMoreElements()) + { + css::uno::Reference xChildNode(xEnumeration->nextElement(), UNO_QUERY); + if (!xChildNode.is()) + continue; + clearChildNodes(xChildNode); + css::uno::Reference xAnimationNode(rAnimationNode, UNO_QUERY); + if (!xAnimationNode.is()) + { + SAL_WARN("sd.core", "can't remove node child, possible leak"); + continue; + } + xAnimationNode->removeChild(xChildNode); + } + } +} + +/************************************************************************* +|* +|* Dtor +|* +\************************************************************************/ + +SdPage::~SdPage() +{ + DisconnectLink(); + + EndListenOutlineText(); + + clearChildNodes(mxAnimationNode); + + // disconnect the UserCall link + SdrObjListIter aIter( this, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pChild = aIter.Next(); + if( pChild->GetUserCall() == this ) + pChild->SetUserCall(nullptr); + } + + // clear SdrObjects with broadcasting + ClearSdrObjList(); +} + +namespace { + +struct OrdNumSorter +{ + bool operator()( SdrObject const * p1, SdrObject const * p2 ) + { + return p1->GetOrdNum() < p2->GetOrdNum(); + } +}; + +} + +/** returns the nIndex'th object from the given PresObjKind, index starts with 1 */ +SdrObject* SdPage::GetPresObj(PresObjKind eObjKind, int nIndex, bool bFuzzySearch /* = false */ ) +{ + // first sort all matching shapes with z-order + std::vector< SdrObject* > aMatches; + + SdrObject* pObj = nullptr; + maPresentationShapeList.seekShape(0); + + while( (pObj = maPresentationShapeList.getNextShape()) ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj); + if( pInfo ) + { + bool bFound = false; + if( pInfo->mePresObjKind == eObjKind ) + { + bFound = true; + } + else if( bFuzzySearch && (eObjKind == PresObjKind::Outline) ) + { + switch( pInfo->mePresObjKind ) + { + case PresObjKind::Graphic: + case PresObjKind::Object: + case PresObjKind::Chart: + case PresObjKind::OrgChart: + case PresObjKind::Table: + case PresObjKind::Calc: + case PresObjKind::Media: + bFound = true; + break; + default: + break; + } + } + if( bFound ) + { + aMatches.push_back( pObj ); + } + } + } + + if( nIndex > 0 ) + nIndex--; + + if( (nIndex >= 0) && ( aMatches.size() > o3tl::make_unsigned(nIndex)) ) + { + if( aMatches.size() > 1 ) + std::nth_element( aMatches.begin(), aMatches.begin() + nIndex, aMatches.end(), + OrdNumSorter() ); + return aMatches[nIndex]; + } + + return nullptr; +} + +/** create background properties */ +void SdPage::EnsureMasterPageDefaultBackground() +{ + if(!mbMaster) + return; + + // no hard attributes on MasterPage attributes + getSdrPageProperties().ClearItem(); + SfxStyleSheet* pSheetForPresObj = GetStyleSheetForMasterPageBackground(); + + if(pSheetForPresObj) + { + // set StyleSheet for background fill attributes + getSdrPageProperties().SetStyleSheet(pSheetForPresObj); + } + else + { + // no style found, assert and set at least drawing::FillStyle_NONE + OSL_FAIL("No Style for MasterPageBackground fill found (!)"); + getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + } +} + +/** creates a presentation object with the given PresObjKind on this page. A user call will be set +*/ +SdrObject* SdPage::CreatePresObj(PresObjKind eObjKind, bool bVertical, const ::tools::Rectangle& rRect ) +{ + SfxUndoManager* pUndoManager(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetUndoManager()); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + SdrObject* pSdrObj = nullptr; + + bool bForceText = false; // forces the shape text to be set even if it's empty + bool bEmptyPresObj = true; + + switch( eObjKind ) + { + case PresObjKind::Title: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::TitleText); + + if (mbMaster) + { + pSdrObj->SetNotVisibleAsMaster(true); + } + } + break; + + case PresObjKind::Outline: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::OutlineText); + + if (mbMaster) + { + pSdrObj->SetNotVisibleAsMaster(true); + } + } + break; + + case PresObjKind::Notes: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::Text); + + if (mbMaster) + { + pSdrObj->SetNotVisibleAsMaster(true); + } + } + break; + + case PresObjKind::Text: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::Text); + } + break; + + case PresObjKind::Graphic: + { + BitmapEx aBmpEx(BMP_PRESOBJ_GRAPHIC); + Graphic aGraphic( aBmpEx ); + OutputDevice &aOutDev = *Application::GetDefaultDevice(); + aOutDev.Push(); + + aOutDev.SetMapMode( aGraphic.GetPrefMapMode() ); + Size aSizePix = aOutDev.LogicToPixel( aGraphic.GetPrefSize() ); + aOutDev.SetMapMode(MapMode(MapUnit::Map100thMM)); + + Size aSize = aOutDev.PixelToLogic(aSizePix); + Point aPnt (0, 0); + ::tools::Rectangle aRect (aPnt, aSize); + pSdrObj = new SdrGrafObj(getSdrModelFromSdrPage(), aGraphic, aRect); + aOutDev.Pop(); + } + break; + + case PresObjKind::Media: + case PresObjKind::Object: + { + pSdrObj = new SdrOle2Obj(getSdrModelFromSdrPage()); + BitmapEx aBmpEx(BMP_PRESOBJ_OBJECT); + Graphic aGraphic( aBmpEx ); + static_cast(pSdrObj)->SetGraphic(aGraphic); + } + break; + + case PresObjKind::Chart: + { + pSdrObj = new SdrOle2Obj(getSdrModelFromSdrPage()); + static_cast(pSdrObj)->SetProgName( "StarChart" ); + BitmapEx aBmpEx(BMP_PRESOBJ_CHART); + Graphic aGraphic( aBmpEx ); + static_cast(pSdrObj)->SetGraphic(aGraphic); + } + break; + + case PresObjKind::OrgChart: + { + pSdrObj = new SdrOle2Obj(getSdrModelFromSdrPage()); + static_cast(pSdrObj)->SetProgName( "StarOrg" ); + BitmapEx aBmpEx(BMP_PRESOBJ_ORGCHART); + Graphic aGraphic( aBmpEx ); + static_cast(pSdrObj)->SetGraphic(aGraphic); + } + break; + + case PresObjKind::Table: + case PresObjKind::Calc: + { + pSdrObj = new SdrOle2Obj(getSdrModelFromSdrPage()); + static_cast(pSdrObj)->SetProgName( "StarCalc" ); + BitmapEx aBmpEx(BMP_PRESOBJ_TABLE); + Graphic aGraphic( aBmpEx ); + static_cast(pSdrObj)->SetGraphic(aGraphic); + } + break; + + case PresObjKind::Handout: + { + // Save the first standard page at SdrPageObj + // #i105146# We want no content to be displayed for PageKind::Handout, + // so just never set a page as content + pSdrObj = new SdrPageObj(getSdrModelFromSdrPage(), nullptr); + } + break; + + case PresObjKind::Page: + { + // Save note pages at SdrPageObj + sal_uInt16 nDestPageNum(GetPageNum()); + + if(nDestPageNum) + { + // decrement only when != 0, else we get a 0xffff + nDestPageNum -= 1; + } + + if (nDestPageNum < getSdrModelFromSdrPage().GetPageCount()) + { + pSdrObj = new SdrPageObj(getSdrModelFromSdrPage(), getSdrModelFromSdrPage().GetPage(nDestPageNum)); + } + else + { + pSdrObj = new SdrPageObj(getSdrModelFromSdrPage()); + } + + pSdrObj->SetResizeProtect(true); + } + break; + + case PresObjKind::Header: + case PresObjKind::Footer: + case PresObjKind::DateTime: + case PresObjKind::SlideNumber: + { + pSdrObj = new SdrRectObj(getSdrModelFromSdrPage(), SdrObjKind::Text); + bEmptyPresObj = false; + bForceText = true; + } + break; + default: + break; + } + + if (pSdrObj) + { + pSdrObj->SetEmptyPresObj(bEmptyPresObj); + pSdrObj->SetLogicRect(rRect); + + InsertObject(pSdrObj); + + if ( auto pTextObj = dynamic_cast( pSdrObj ) ) + { + // Tell the object EARLY that it is vertical to have the + // defaults for AutoGrowWidth/Height reversed + if(bVertical) + pTextObj->SetVerticalWriting(true); + + SfxItemSet aTempAttr(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool()); + if( bVertical ) + aTempAttr.Put( makeSdrTextMinFrameWidthItem( rRect.GetSize().Width() ) ); + else + aTempAttr.Put( makeSdrTextMinFrameHeightItem( rRect.GetSize().Height() ) ); + + if (mbMaster) + { + // The size of presentation objects on the master page have to + // be freely selectable by the user. + + // potential problem: This action was still NOT + // adapted for vertical text. This sure needs to be done. + if(bVertical) + aTempAttr.Put(makeSdrTextAutoGrowWidthItem(false)); + else + aTempAttr.Put(makeSdrTextAutoGrowHeightItem(false)); + } + + // check if we need another vertical adjustment than the default + SdrTextVertAdjust eV = SDRTEXTVERTADJUST_TOP; + + if( (eObjKind == PresObjKind::Footer) && (mePageKind != PageKind::Standard) ) + { + eV = SDRTEXTVERTADJUST_BOTTOM; + } + else if( (eObjKind == PresObjKind::SlideNumber) && (mePageKind != PageKind::Standard) ) + { + eV = SDRTEXTVERTADJUST_BOTTOM; + } + + if( eV != SDRTEXTVERTADJUST_TOP ) + aTempAttr.Put(SdrTextVertAdjustItem(eV)); + + pSdrObj->SetMergedItemSet(aTempAttr); + + pSdrObj->SetLogicRect(rRect); + } + + OUString aString = GetPresObjText(eObjKind); + if(!aString.isEmpty() || bForceText) + if (auto pTextObj = dynamic_cast( pSdrObj ) ) + { + SdrOutliner* pOutliner = static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetInternalOutliner(); + + OutlinerMode nOutlMode = pOutliner->GetOutlinerMode(); + pOutliner->Init( OutlinerMode::TextObject ); + pOutliner->SetStyleSheet( 0, nullptr ); + pOutliner->SetVertical( bVertical ); + + SetObjText( pTextObj, pOutliner, eObjKind, aString ); + + pOutliner->Init( nOutlMode ); + pOutliner->SetStyleSheet( 0, nullptr ); + } + + if( (eObjKind == PresObjKind::Header) || (eObjKind == PresObjKind::Footer) || (eObjKind == PresObjKind::SlideNumber) || (eObjKind == PresObjKind::DateTime) ) + { + SfxItemSet aTempAttr(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool()); + aTempAttr.Put( SvxFontHeightItem( 493, 100, EE_CHAR_FONTHEIGHT ) ); + aTempAttr.Put( SvxFontHeightItem( 493, 100, EE_CHAR_FONTHEIGHT_CTL ) ); + aTempAttr.Put( SvxFontHeightItem( 493, 100, EE_CHAR_FONTHEIGHT_CJK ) ); + + SvxAdjust eH = SvxAdjust::Left; + + if( (eObjKind == PresObjKind::DateTime) && (mePageKind != PageKind::Standard ) ) + { + eH = SvxAdjust::Right; + } + else if( (eObjKind == PresObjKind::Footer) && (mePageKind == PageKind::Standard ) ) + { + eH = SvxAdjust::Center; + } + else if( eObjKind == PresObjKind::SlideNumber ) + { + eH = SvxAdjust::Right; + } + + if( eH != SvxAdjust::Left ) + aTempAttr.Put(SvxAdjustItem(eH, EE_PARA_JUST )); + + pSdrObj->SetMergedItemSet(aTempAttr); + } + + if (mbMaster) + { + SdrLayerAdmin& rLayerAdmin(getSdrModelFromSdrPage().GetLayerAdmin()); + + // background objects of the master page + pSdrObj->SetLayer( rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects) ); + } + + // Subscribe object at the style sheet + // Set style only when one was found (as in 5.2) + if( mePageKind != PageKind::Handout ) + { + SfxStyleSheet* pSheetForPresObj = GetStyleSheetForPresObj(eObjKind); + if(pSheetForPresObj) + pSdrObj->SetStyleSheet(pSheetForPresObj, false); + } + + if (eObjKind == PresObjKind::Outline) + { + for (sal_uInt16 nLevel = 1; nLevel < 10; nLevel++) + { + OUString aName( maLayoutName + " " + OUString::number( nLevel ) ); + SfxStyleSheet* pSheet = static_cast(getSdrModelFromSdrPage().GetStyleSheetPool()->Find(aName, SfxStyleFamily::Page)); + DBG_ASSERT(pSheet, "StyleSheet for outline object not found"); + if (pSheet) + pSdrObj->StartListening(*pSheet, DuplicateHandling::Allow); + } + } + + if ( eObjKind == PresObjKind::Object || + eObjKind == PresObjKind::Chart || + eObjKind == PresObjKind::OrgChart || + eObjKind == PresObjKind::Calc || + eObjKind == PresObjKind::Graphic ) + { + SfxItemSet aSet( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + aSet.Put( makeSdrTextContourFrameItem( true ) ); + aSet.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + + pSdrObj->SetMergedItemSet(aSet); + } + + if( bUndo ) + { + pUndoManager->AddUndoAction(getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoNewObject(*pSdrObj)); + + pUndoManager->AddUndoAction( std::make_unique( *pSdrObj ) ); + pUndoManager->AddUndoAction( std::make_unique(*pSdrObj) ); + } + + InsertPresObj(pSdrObj, eObjKind); + pSdrObj->SetUserCall(this); + + pSdrObj->RecalcBoundRect(); + } + + return pSdrObj; +} + +/************************************************************************* +|* +|* Creates presentation objects on the master page. +|* All presentation objects get a UserCall to the page. +|* +\************************************************************************/ + +SfxStyleSheet* SdPage::GetStyleSheetForMasterPageBackground() const +{ + OUString aName(GetLayoutName()); + OUString aSep( SD_LT_SEPARATOR ); + sal_Int32 nPos = aName.indexOf(aSep); + + if (nPos != -1) + { + nPos = nPos + aSep.getLength(); + aName = aName.copy(0, nPos); + } + + aName += STR_LAYOUT_BACKGROUND; + + SfxStyleSheetBasePool* pStShPool = getSdrModelFromSdrPage().GetStyleSheetPool(); + SfxStyleSheetBase* pResult = pStShPool->Find(aName, SfxStyleFamily::Page); + return static_cast(pResult); +} + +SfxStyleSheet* SdPage::GetStyleSheetForPresObj(PresObjKind eObjKind) const +{ + OUString aName(GetLayoutName()); + OUString aSep( SD_LT_SEPARATOR ); + sal_Int32 nPos = aName.indexOf(aSep); + if (nPos != -1) + { + nPos = nPos + aSep.getLength(); + aName = aName.copy(0, nPos); + } + + switch (eObjKind) + { + case PresObjKind::Outline: + { + aName = GetLayoutName() + " " + OUString::number( 1 ); + } + break; + + case PresObjKind::Title: + aName += STR_LAYOUT_TITLE; + break; + + case PresObjKind::Notes: + aName += STR_LAYOUT_NOTES; + break; + + case PresObjKind::Text: + aName += STR_LAYOUT_SUBTITLE; + break; + + case PresObjKind::Header: + case PresObjKind::Footer: + case PresObjKind::DateTime: + case PresObjKind::SlideNumber: + aName += STR_LAYOUT_BACKGROUNDOBJECTS; + break; + + default: + break; + } + + SfxStyleSheetBasePool* pStShPool = getSdrModelFromSdrPage().GetStyleSheetPool(); + SfxStyleSheetBase* pResult = pStShPool->Find(aName, SfxStyleFamily::Page); + return static_cast(pResult); +} + +/** returns the presentation style with the given helpid from this masterpage or this + slides masterpage */ +SdStyleSheet* SdPage::getPresentationStyle( sal_uInt32 nHelpId ) const +{ + OUString aStyleName( GetLayoutName() ); + const OUString aSep( SD_LT_SEPARATOR ); + sal_Int32 nIndex = aStyleName.indexOf(aSep); + if( nIndex != -1 ) + aStyleName = aStyleName.copy(0, nIndex + aSep.getLength()); + + OUString pNameId; + bool bOutline = false; + switch( nHelpId ) + { + case HID_PSEUDOSHEET_TITLE: pNameId = STR_LAYOUT_TITLE; break; + case HID_PSEUDOSHEET_SUBTITLE: pNameId = STR_LAYOUT_SUBTITLE; break; + case HID_PSEUDOSHEET_OUTLINE1: + case HID_PSEUDOSHEET_OUTLINE2: + case HID_PSEUDOSHEET_OUTLINE3: + case HID_PSEUDOSHEET_OUTLINE4: + case HID_PSEUDOSHEET_OUTLINE5: + case HID_PSEUDOSHEET_OUTLINE6: + case HID_PSEUDOSHEET_OUTLINE7: + case HID_PSEUDOSHEET_OUTLINE8: + case HID_PSEUDOSHEET_OUTLINE9: pNameId = STR_LAYOUT_OUTLINE; bOutline = true; break; + case HID_PSEUDOSHEET_BACKGROUNDOBJECTS: pNameId = STR_LAYOUT_BACKGROUNDOBJECTS; break; + case HID_PSEUDOSHEET_BACKGROUND: pNameId = STR_LAYOUT_BACKGROUND; break; + case HID_PSEUDOSHEET_NOTES: pNameId = STR_LAYOUT_NOTES; break; + + default: + OSL_FAIL( "SdPage::getPresentationStyle(), illegal argument!" ); + return nullptr; + } + aStyleName += pNameId; + if (bOutline) + { + aStyleName += " " + + OUString::number( sal_Int32( nHelpId - HID_PSEUDOSHEET_OUTLINE )); + } + + SfxStyleSheetBasePool* pStShPool = getSdrModelFromSdrPage().GetStyleSheetPool(); + SfxStyleSheetBase* pResult = pStShPool->Find(aStyleName, SfxStyleFamily::Page); + return dynamic_cast(pResult); +} + +/************************************************************************* +|* +|* The presentation object rObj has changed and is no longer referenced by the +|* presentation object of the master page. +|* The UserCall is deleted. +|* +\************************************************************************/ + +void SdPage::Changed(const SdrObject& rObj, SdrUserCallType eType, const ::tools::Rectangle& ) +{ + if (maLockAutoLayoutArrangement.isLocked()) + return; + + switch (eType) + { + case SdrUserCallType::MoveOnly: + case SdrUserCallType::Resize: + { + if ( getSdrModelFromSdrPage().isLocked()) + break; + + if (!mbMaster) + { + if (rObj.GetUserCall()) + { + SdrObject& _rObj = const_cast(rObj); + SfxUndoManager* pUndoManager + = static_cast(getSdrModelFromSdrPage()) + .GetUndoManager(); + const bool bUndo + = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + if (bUndo) + pUndoManager->AddUndoAction( + std::make_unique(_rObj)); + + // Object was resized by user and does not listen to its slide anymore + _rObj.SetUserCall(nullptr); + } + } + else + { + // Object of the master page changed, therefore adjust + // object on all pages + sal_uInt16 nPageCount = static_cast(getSdrModelFromSdrPage()) + .GetSdPageCount(mePageKind); + + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + SdPage* pLoopPage = static_cast(getSdrModelFromSdrPage()) + .GetSdPage(i, mePageKind); + + if (pLoopPage && this == &(pLoopPage->TRG_GetMasterPage())) + { + // Page listens to this master page, therefore + // adjust AutoLayout + pLoopPage->SetAutoLayout(pLoopPage->GetAutoLayout()); + } + } + } + } + break; + + case SdrUserCallType::Delete: + case SdrUserCallType::Removed: + default: + break; + } +} + +/************************************************************************* +|* +|* Creates on a master page: background, title- and layout area +|* +\************************************************************************/ + +void SdPage::CreateTitleAndLayout(bool bInit, bool bCreate ) +{ + SfxUndoManager* pUndoManager(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetUndoManager()); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + SdPage* pMasterPage = this; + + if (!mbMaster) + { + pMasterPage = static_cast(&(TRG_GetMasterPage())); + } + + if (!pMasterPage) + { + return; + } + + /************************************************************************** + * create background, title- and layout area + **************************************************************************/ + if( mePageKind == PageKind::Standard ) + { + pMasterPage->EnsureMasterPageDefaultBackground(); + } + + if (static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetDocumentType() != DocumentType::Impress) + return; + + if( mePageKind == PageKind::Handout && bInit ) + { + // handout template + + // delete all available handout presentation objects + SdrObject *pObj=nullptr; + while( (pObj = pMasterPage->GetPresObj(PresObjKind::Handout)) != nullptr ) + { + pMasterPage->RemoveObject(pObj->GetOrdNum()); + + if( bUndo ) + { + pUndoManager->AddUndoAction(getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj)); + } + else + { + SdrObject::Free( pObj ); + } + } + + std::vector< ::tools::Rectangle > aAreas; + CalculateHandoutAreas( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()), pMasterPage->GetAutoLayout(), false, aAreas ); + + const bool bSkip = pMasterPage->GetAutoLayout() == AUTOLAYOUT_HANDOUT3; + std::vector< ::tools::Rectangle >::iterator iter( aAreas.begin() ); + + while( iter != aAreas.end() ) + { + SdrPageObj* pPageObj = static_cast(pMasterPage->CreatePresObj(PresObjKind::Handout, false, (*iter++)) ); + // #i105146# We want no content to be displayed for PageKind::Handout, + // so just never set a page as content + pPageObj->SetReferencedPage(nullptr); + + if( bSkip && iter != aAreas.end() ) + ++iter; + } + } + + if( mePageKind != PageKind::Handout ) + { + SdrObject* pMasterTitle = pMasterPage->GetPresObj( PresObjKind::Title ); + if( pMasterTitle == nullptr ) + pMasterPage->CreateDefaultPresObj(PresObjKind::Title); + + SdrObject* pMasterOutline = pMasterPage->GetPresObj( mePageKind==PageKind::Notes ? PresObjKind::Notes : PresObjKind::Outline ); + if( pMasterOutline == nullptr ) + pMasterPage->CreateDefaultPresObj( mePageKind == PageKind::Standard ? PresObjKind::Outline : PresObjKind::Notes ); + } + + // create header&footer objects + + if( !bCreate ) + return; + + if( mePageKind != PageKind::Standard ) + { + SdrObject* pHeader = pMasterPage->GetPresObj( PresObjKind::Header ); + if( pHeader == nullptr ) + pMasterPage->CreateDefaultPresObj( PresObjKind::Header ); + } + + SdrObject* pDate = pMasterPage->GetPresObj( PresObjKind::DateTime ); + if( pDate == nullptr ) + pMasterPage->CreateDefaultPresObj( PresObjKind::DateTime ); + + SdrObject* pFooter = pMasterPage->GetPresObj( PresObjKind::Footer ); + if( pFooter == nullptr ) + pMasterPage->CreateDefaultPresObj( PresObjKind::Footer ); + + SdrObject* pNumber = pMasterPage->GetPresObj( PresObjKind::SlideNumber ); + if( pNumber == nullptr ) + pMasterPage->CreateDefaultPresObj( PresObjKind::SlideNumber ); +} + +namespace { + +const o3tl::enumarray PageKindVector = { + "PageKind::Standard", "PageKind::Notes", "PageKind::Handout" +}; + +const o3tl::enumarray PresObjKindVector = { + "PRESOBJ_NONE", "PRESOBJ_TITLE", "PRESOBJ_OUTLINE", + "PRESOBJ_TEXT" ,"PRESOBJ_GRAPHIC" , "PRESOBJ_OBJECT", + "PRESOBJ_CHART", "PRESOBJ_ORGCHART", "PRESOBJ_TABLE", + "PRESOBJ_PAGE", "PRESOBJ_HANDOUT", + "PRESOBJ_NOTES","PRESOBJ_HEADER", "PRESOBJ_FOOTER", + "PRESOBJ_DATETIME", "PRESOBJ_SLIDENUMBER", "PRESOBJ_CALC", + "PRESOBJ_MEDIA" +}; + +void getPresObjProp( const SdPage& rPage, const char* sObjKind, const char* sPageKind, double presObjPropValue[] ) +{ + bool bNoObjectFound = true; //used to break from outer loop + + const std::vector< Reference >& objectInfo = static_cast< const SdDrawDocument& >(rPage.getSdrModelFromSdrPage()).GetObjectVector(); + for( const Reference& objectNode : objectInfo ) + { + if(bNoObjectFound) + { + Reference objectattrlist = objectNode->getAttributes(); + Reference objectattr = objectattrlist->getNamedItem("type"); + OUString sObjType = objectattr->getNodeValue(); + + if (sObjType.equalsAscii(sObjKind)) + { + Reference objectChildren = objectNode->getChildNodes(); + const int objSize = objectChildren->getLength(); + + for( int j=0; j< objSize; j++) + { + Reference obj = objectChildren->item(j); + OUString nodename = obj->getNodeName(); + + //check whether child is blank 'text-node' or 'object-prop' node + if(nodename == "object-prop") + { + Reference ObjAttributes = obj->getAttributes(); + Reference ObjPageKind = ObjAttributes->getNamedItem("pagekind"); + OUString sObjPageKind = ObjPageKind->getNodeValue(); + + if (sObjPageKind.equalsAscii(sPageKind)) + { + Reference ObjSizeHeight = ObjAttributes->getNamedItem("relative-height"); + OUString sValue = ObjSizeHeight->getNodeValue(); + presObjPropValue[0] = sValue.toDouble(); + + Reference ObjSizeWidth = ObjAttributes->getNamedItem("relative-width"); + sValue = ObjSizeWidth->getNodeValue(); + presObjPropValue[1] = sValue.toDouble(); + + Reference ObjPosX = ObjAttributes->getNamedItem("relative-posX"); + sValue = ObjPosX->getNodeValue(); + presObjPropValue[2] = sValue.toDouble(); + + Reference ObjPosY = ObjAttributes->getNamedItem("relative-posY"); + sValue = ObjPosY->getNodeValue(); + presObjPropValue[3] = sValue.toDouble(); + + bNoObjectFound = false; + break; + } + } + } + } + } + else + break; + } +} + +} + +SdrObject* SdPage::CreateDefaultPresObj(PresObjKind eObjKind) +{ + if( eObjKind == PresObjKind::Title ) + { + ::tools::Rectangle aTitleRect( GetTitleRect() ); + return CreatePresObj(PresObjKind::Title, false, aTitleRect); + } + else if( eObjKind == PresObjKind::Outline ) + { + ::tools::Rectangle aLayoutRect( GetLayoutRect() ); + return CreatePresObj( PresObjKind::Outline, false, aLayoutRect); + } + else if( eObjKind == PresObjKind::Notes ) + { + ::tools::Rectangle aLayoutRect( GetLayoutRect() ); + return CreatePresObj( PresObjKind::Notes, false, aLayoutRect); + } + else if( (eObjKind == PresObjKind::Footer) || (eObjKind == PresObjKind::DateTime) || (eObjKind == PresObjKind::SlideNumber) || (eObjKind == PresObjKind::Header ) ) + { + double propvalue[] = {0,0,0,0}; + const char* sObjKind = PresObjKindVector[eObjKind]; + const char* sPageKind = PageKindVector[mePageKind]; + // create footer objects for standard master page + if( mePageKind == PageKind::Standard ) + { + const ::tools::Long nLftBorder = GetLeftBorder(); + const ::tools::Long nUppBorder = GetUpperBorder(); + + Point aPos ( nLftBorder, nUppBorder ); + Size aSize ( GetSize() ); + + aSize.AdjustWidth( -(nLftBorder + GetRightBorder()) ); + aSize.AdjustHeight( -(nUppBorder + GetLowerBorder()) ); + + getPresObjProp( *this, sObjKind, sPageKind, propvalue); + aPos.AdjustX(::tools::Long( aSize.Width() * propvalue[2] ) ); + aPos.AdjustY(::tools::Long( aSize.Height() * propvalue[3] ) ); + aSize.setWidth( ::tools::Long( aSize.Width() * propvalue[1] ) ); + aSize.setHeight( ::tools::Long( aSize.Height() * propvalue[0] ) ); + + if(eObjKind == PresObjKind::Header ) + { + OSL_FAIL( "SdPage::CreateDefaultPresObj() - can't create a header placeholder for a master slide" ); + return nullptr; + } + else + { + ::tools::Rectangle aRect( aPos, aSize ); + return CreatePresObj( eObjKind, false, aRect ); + } + } + else + { + // create header&footer objects for handout and notes master + Size aPageSize ( GetSize() ); + aPageSize.AdjustWidth( -(GetLeftBorder() + GetRightBorder()) ); + aPageSize.AdjustHeight( -(GetUpperBorder() + GetLowerBorder()) ); + + Point aPosition ( GetLeftBorder(), GetUpperBorder() ); + + getPresObjProp( *this, sObjKind, sPageKind, propvalue); + int NOTES_HEADER_FOOTER_WIDTH = ::tools::Long(aPageSize.Width() * propvalue[1]); + int NOTES_HEADER_FOOTER_HEIGHT = ::tools::Long(aPageSize.Height() * propvalue[0]); + Size aSize( NOTES_HEADER_FOOTER_WIDTH, NOTES_HEADER_FOOTER_HEIGHT ); + Point aPos ( 0 ,0 ); + if( propvalue[2] == 0 ) + aPos.setX( aPosition.X() ); + else + aPos.setX( aPosition.X() + ::tools::Long( aPageSize.Width() - NOTES_HEADER_FOOTER_WIDTH ) ); + if( propvalue[3] == 0 ) + aPos.setY( aPosition.Y() ); + else + aPos.setY( aPosition.Y() + ::tools::Long( aPageSize.Height() - NOTES_HEADER_FOOTER_HEIGHT ) ); + + ::tools::Rectangle aRect( aPos, aSize ); + return CreatePresObj( eObjKind, false, aRect ); + } + } + else + { + OSL_FAIL("SdPage::CreateDefaultPresObj() - unknown PRESOBJ kind" ); + return nullptr; + } +} + +void SdPage::DestroyDefaultPresObj(PresObjKind eObjKind) +{ + SdrObject* pObject = GetPresObj( eObjKind ); + + if( pObject ) + { + SdDrawDocument* pDoc(static_cast< SdDrawDocument* >(&getSdrModelFromSdrPage())); + const bool bUndo = pDoc->IsUndoEnabled(); + if( bUndo ) + pDoc->AddUndo(pDoc->GetSdrUndoFactory().CreateUndoDeleteObject(*pObject)); + SdrObjList* pOL = pObject->getParentSdrObjListFromSdrObject(); + pOL->RemoveObject(pObject->GetOrdNumDirect()); + + if( !bUndo ) + SdrObject::Free(pObject); + } +} + +/************************************************************************* +|* +|* return title area +|* +\************************************************************************/ + +::tools::Rectangle SdPage::GetTitleRect() const +{ + ::tools::Rectangle aTitleRect; + + if (mePageKind != PageKind::Handout) + { + double propvalue[] = {0,0,0,0}; + + /****************************************************************** + * standard- or note page: title area + ******************************************************************/ + Point aTitlePos ( GetLeftBorder(), GetUpperBorder() ); + Size aTitleSize ( GetSize() ); + aTitleSize.AdjustWidth( -(GetLeftBorder() + GetRightBorder()) ); + aTitleSize.AdjustHeight( -(GetUpperBorder() + GetLowerBorder()) ); + const char* sPageKind = PageKindVector[mePageKind]; + + if (mePageKind == PageKind::Standard) + { + getPresObjProp( *this , "PRESOBJ_TITLE" ,sPageKind, propvalue); + aTitlePos.AdjustX(::tools::Long( aTitleSize.Width() * propvalue[2] ) ); + aTitlePos.AdjustY(::tools::Long( aTitleSize.Height() * propvalue[3] ) ); + aTitleSize.setWidth( ::tools::Long( aTitleSize.Width() * propvalue[1] ) ); + aTitleSize.setHeight( ::tools::Long( aTitleSize.Height() * propvalue[0] ) ); + } + else if (mePageKind == PageKind::Notes) + { + Point aPos = aTitlePos; + getPresObjProp( *this, "PRESOBJ_TITLE" ,sPageKind, propvalue); + aPos.AdjustX(::tools::Long( aTitleSize.Width() * propvalue[2] ) ); + aPos.AdjustY(::tools::Long( aTitleSize.Height() * propvalue[3] ) ); + + // limit height + aTitleSize.setHeight( ::tools::Long( aTitleSize.Height() * propvalue[0] ) ); + aTitleSize.setWidth( ::tools::Long( aTitleSize.Width() * propvalue[1] ) ); + + Size aPartArea = aTitleSize; + Size aSize; + sal_uInt16 nDestPageNum(GetPageNum()); + SdrPage* pRefPage = nullptr; + + if(nDestPageNum) + { + // only decrement if != 0, else we get 0xffff + nDestPageNum -= 1; + } + + if(nDestPageNum < getSdrModelFromSdrPage().GetPageCount()) + { + pRefPage = getSdrModelFromSdrPage().GetPage(nDestPageNum); + } + + if ( pRefPage ) + { + // scale actually page size into handout rectangle + double fH = pRefPage->GetWidth() == 0 + ? 0 : static_cast(aPartArea.Width()) / pRefPage->GetWidth(); + double fV = pRefPage->GetHeight() == 0 + ? 0 : static_cast(aPartArea.Height()) / pRefPage->GetHeight(); + + if ( fH > fV ) + fH = fV; + aSize.setWidth( static_cast<::tools::Long>(fH * pRefPage->GetWidth()) ); + aSize.setHeight( static_cast<::tools::Long>(fH * pRefPage->GetHeight()) ); + + aPos.AdjustX((aPartArea.Width() - aSize.Width()) / 2 ); + aPos.AdjustY((aPartArea.Height()- aSize.Height())/ 2 ); + } + + aTitlePos = aPos; + aTitleSize = aSize; + } + + aTitleRect.SetPos(aTitlePos); + aTitleRect.SetSize(aTitleSize); + } + + return aTitleRect; +} + +/************************************************************************* +|* +|* return outline area +|* +\************************************************************************/ + +::tools::Rectangle SdPage::GetLayoutRect() const +{ + ::tools::Rectangle aLayoutRect; + + if (mePageKind != PageKind::Handout) + { + double propvalue[] = {0,0,0,0}; + + Point aLayoutPos ( GetLeftBorder(), GetUpperBorder() ); + Size aLayoutSize ( GetSize() ); + aLayoutSize.AdjustWidth( -(GetLeftBorder() + GetRightBorder()) ); + aLayoutSize.AdjustHeight( -(GetUpperBorder() + GetLowerBorder()) ); + const char* sPageKind = PageKindVector[mePageKind]; + + if (mePageKind == PageKind::Standard) + { + getPresObjProp( *this ,"PRESOBJ_OUTLINE", sPageKind, propvalue); + aLayoutPos.AdjustX(::tools::Long( aLayoutSize.Width() * propvalue[2] ) ); + aLayoutPos.AdjustY(::tools::Long( aLayoutSize.Height() * propvalue[3] ) ); + aLayoutSize.setWidth( ::tools::Long( aLayoutSize.Width() * propvalue[1] ) ); + aLayoutSize.setHeight( ::tools::Long( aLayoutSize.Height() * propvalue[0] ) ); + aLayoutRect.SetPos(aLayoutPos); + aLayoutRect.SetSize(aLayoutSize); + } + else if (mePageKind == PageKind::Notes) + { + getPresObjProp( *this, "PRESOBJ_NOTES", sPageKind, propvalue); + aLayoutPos.AdjustX(::tools::Long( aLayoutSize.Width() * propvalue[2] ) ); + aLayoutPos.AdjustY(::tools::Long( aLayoutSize.Height() * propvalue[3] ) ); + aLayoutSize.setWidth( ::tools::Long( aLayoutSize.Width() * propvalue[1] ) ); + aLayoutSize.setHeight( ::tools::Long( aLayoutSize.Height() * propvalue[0] ) ); + aLayoutRect.SetPos(aLayoutPos); + aLayoutRect.SetSize(aLayoutSize); + } + } + + return aLayoutRect; +} + +/************************************************************************** +|* +|* assign an AutoLayout +|* +\*************************************************************************/ + +const int MAX_PRESOBJS = 7; // maximum number of presentation objects per layout +const int VERTICAL = 0x8000; + +static constexpr PresObjKind operator|(PresObjKind e, int x) +{ + return static_cast(static_cast(e) | x); +} + +namespace { + +struct LayoutDescriptor +{ + PresObjKind meKind[MAX_PRESOBJS]; + bool mbVertical[MAX_PRESOBJS]; + + LayoutDescriptor( PresObjKind k0 = PresObjKind::NONE, PresObjKind k1 = PresObjKind::NONE, PresObjKind k2 = PresObjKind::NONE, PresObjKind k3 = PresObjKind::NONE, PresObjKind k4 = PresObjKind::NONE, PresObjKind k5 = PresObjKind::NONE, PresObjKind k6 = PresObjKind::NONE ); +}; + +} + +LayoutDescriptor::LayoutDescriptor( PresObjKind k0, PresObjKind k1, PresObjKind k2, PresObjKind k3, PresObjKind k4, PresObjKind k5, PresObjKind k6 ) +{ + auto removeVertical = [] (PresObjKind k) { return static_cast(static_cast(k) & ~VERTICAL); }; + auto isVertical = [] (PresObjKind k) { return bool(static_cast(k) & VERTICAL); }; + meKind[0] = removeVertical(k0); mbVertical[0] = isVertical(k0); + meKind[1] = removeVertical(k1); mbVertical[1] = isVertical(k1); + meKind[2] = removeVertical(k2); mbVertical[2] = isVertical(k2); + meKind[3] = removeVertical(k3); mbVertical[3] = isVertical(k3); + meKind[4] = removeVertical(k4); mbVertical[4] = isVertical(k4); + meKind[5] = removeVertical(k5); mbVertical[5] = isVertical(k5); + meKind[6] = removeVertical(k6); mbVertical[6] = isVertical(k6); +} + +static const LayoutDescriptor& GetLayoutDescriptor( AutoLayout eLayout ) +{ + static const LayoutDescriptor aLayouts[AUTOLAYOUT_END-AUTOLAYOUT_START] = + { + LayoutDescriptor( PresObjKind::Title, PresObjKind::Text ), // AUTOLAYOUT_TITLE + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline ), // AUTOLAYOUT_CHART + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_2CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTCHART + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline ), // AUTOLAYOUT_ORG + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTCLbIP + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_CHARTTEXT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline ), // AUTOLAYOUT_TAB + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_CLIPTEXT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTOBJ + LayoutDescriptor( PresObjKind::Title, PresObjKind::Object ), // AUTOLAYOUT_OBJ + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_CONTENT_2CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTOBJ + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_2CONTENT_CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline ), // AUTOLAYOUT_TEXTOVEROBJ + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, // AUTOLAYOUT_TITLE_4CONTENT + PresObjKind::Outline, PresObjKind::Outline ), + LayoutDescriptor( PresObjKind::Title, PresObjKind::NONE ), // AUTOLAYOUT_TITLE_ONLY + LayoutDescriptor( PresObjKind::NONE ), // AUTOLAYOUT_NONE + LayoutDescriptor( PresObjKind::Page, PresObjKind::Notes ), // AUTOLAYOUT_NOTES + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT1 + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT2 + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT3 + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT4 + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT6 + LayoutDescriptor( PresObjKind::Title|VERTICAL, PresObjKind::Outline|VERTICAL, PresObjKind::Outline ),// AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT + LayoutDescriptor( PresObjKind::Title|VERTICAL, PresObjKind::Outline|VERTICAL ), // AUTOLAYOUT_VTITLE_VCONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline|VERTICAL ), // AUTOLAYOUT_TITLE_VCONTENT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline|VERTICAL, PresObjKind::Outline|VERTICAL ), // AUTOLAYOUT_TITLE_2VTEXT + LayoutDescriptor( ), // AUTOLAYOUT_HANDOUT9 + LayoutDescriptor( PresObjKind::Text, PresObjKind::NONE ), // AUTOLAYOUT_ONLY_TEXT + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, // AUTOLAYOUT_4CLIPART + PresObjKind::Graphic, PresObjKind::Graphic ), + LayoutDescriptor( PresObjKind::Title, PresObjKind::Outline, PresObjKind::Outline, // AUTOLAYOUT_TITLE_6CONTENT + PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline, PresObjKind::Outline ) + }; + + if( (eLayout < AUTOLAYOUT_START) || (eLayout >= AUTOLAYOUT_END) ) + eLayout = AUTOLAYOUT_NONE; + + return aLayouts[ eLayout - AUTOLAYOUT_START ]; +} + +static OUString enumtoString(AutoLayout aut) +{ + OUString retstr; + switch (aut) + { + case AUTOLAYOUT_TITLE_CONTENT: + retstr="AUTOLAYOUT_TITLE_CONTENT"; + break; + case AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT: + retstr="AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT"; + break; + case AUTOLAYOUT_TITLE_CONTENT_2CONTENT: + retstr="AUTOLAYOUT_TITLE_CONTENT_2CONTENT"; + break; + case AUTOLAYOUT_TITLE_4CONTENT: + retstr="AUTOLAYOUT_TITLE_4CONTENT"; + break; + case AUTOLAYOUT_ONLY_TEXT: + retstr="AUTOLAYOUT_ONLY_TEXT"; + break; + case AUTOLAYOUT_TITLE_ONLY: + retstr="AUTOLAYOUT_TITLE_ONLY"; + break; + case AUTOLAYOUT_TITLE_6CONTENT: + retstr="AUTOLAYOUT_TITLE_6CONTENT"; + break; + case AUTOLAYOUT_START: + retstr="AUTOLAYOUT_START"; + break; + case AUTOLAYOUT_TITLE_2CONTENT_CONTENT: + retstr="AUTOLAYOUT_TITLE_2CONTENT_CONTENT"; + break; + case AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT: + retstr="AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT"; + break; + case AUTOLAYOUT_TITLE_2CONTENT: + retstr="AUTOLAYOUT_TITLE_2CONTENT"; + break; + case AUTOLAYOUT_VTITLE_VCONTENT: + retstr="AUTOLAYOUT_VTITLE_VCONTENT"; + break; + case AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT: + retstr="AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT"; + break; + case AUTOLAYOUT_TITLE_VCONTENT: + retstr="AUTOLAYOUT_TITLE_VCONTENT"; + break; + case AUTOLAYOUT_TITLE_2VTEXT: + retstr="AUTOLAYOUT_TITLE_2VTEXT"; + break; + default: + retstr="unknown"; + break; + // case AUTOLAYOUT_TITLE_4SCONTENT: return "AUTOLAYOUT_TITLE_4SCONTENT"; + } + return retstr; +} + +static void CalcAutoLayoutRectangles( SdPage const & rPage,::tools::Rectangle* rRectangle ,const OUString& sLayoutType ) +{ + ::tools::Rectangle aTitleRect; + ::tools::Rectangle aLayoutRect; + + if( rPage.GetPageKind() != PageKind::Handout ) + { + SdPage& rMasterPage = static_cast(rPage.TRG_GetMasterPage()); + SdrObject* pMasterTitle = rMasterPage.GetPresObj( PresObjKind::Title ); + SdrObject* pMasterSubTitle = rMasterPage.GetPresObj( PresObjKind::Text ); + SdrObject* pMasterOutline = rMasterPage.GetPresObj( rPage.GetPageKind()==PageKind::Notes ? PresObjKind::Notes : PresObjKind::Outline ); + + if( pMasterTitle ) + aTitleRect = pMasterTitle->GetLogicRect(); + + if (aTitleRect.IsEmpty() ) + aTitleRect = rPage.GetTitleRect(); + if( pMasterSubTitle ) + aLayoutRect = pMasterSubTitle->GetLogicRect(); + else if( pMasterOutline ) + aLayoutRect = pMasterOutline->GetLogicRect(); + + if (aLayoutRect.IsEmpty() ) + aLayoutRect = rPage.GetLayoutRect(); + } + + rRectangle[0] = aTitleRect; + for( int i = 1; i < MAX_PRESOBJS; i++ ) + rRectangle[i] = aLayoutRect; + + const Point aTitlePos( aTitleRect.TopLeft() ); + const Size aLayoutSize( aLayoutRect.GetSize() ); + const Point aLayoutPos( aLayoutRect.TopLeft() ); + double propvalue[] = {0,0,0,0}; + + const std::vector< Reference >& layoutInfo = static_cast< const SdDrawDocument& >(rPage.getSdrModelFromSdrPage()).GetLayoutVector(); + auto aIter = std::find_if(layoutInfo.begin(), layoutInfo.end(), + [&sLayoutType](const Reference& layoutNode) { + Reference layoutAttrList = layoutNode->getAttributes(); + + // get the attribute value of layout (i.e it's type) + OUString sLayoutAttName = layoutAttrList->getNamedItem("type")->getNodeValue(); + return sLayoutAttName == sLayoutType; + }); + if (aIter == layoutInfo.end()) + return; + + int count=0; + Reference layoutNode = *aIter; + Reference layoutChildren = layoutNode->getChildNodes(); + const int presobjsize = layoutChildren->getLength(); + for( int j=0; j< presobjsize ; j++) + { + OUString nodename; + Reference presobj = layoutChildren->item(j); + nodename=presobj->getNodeName(); + + //check whether child is blank 'text-node' or 'presobj' node + if(nodename == "presobj") + { + // TODO: rework sd to permit arbitrary number of presentation objects + assert(count < MAX_PRESOBJS); + + Reference presObjAttributes = presobj->getAttributes(); + + Reference presObjSizeHeight = presObjAttributes->getNamedItem("relative-height"); + OUString sValue = presObjSizeHeight->getNodeValue(); + propvalue[0] = sValue.toDouble(); + + Reference presObjSizeWidth = presObjAttributes->getNamedItem("relative-width"); + sValue = presObjSizeWidth->getNodeValue(); + propvalue[1] = sValue.toDouble(); + + Reference presObjPosX = presObjAttributes->getNamedItem("relative-posX"); + sValue = presObjPosX->getNodeValue(); + propvalue[2] = sValue.toDouble(); + + Reference presObjPosY = presObjAttributes->getNamedItem("relative-posY"); + sValue = presObjPosY->getNodeValue(); + propvalue[3] = sValue.toDouble(); + + if(count == 0) + { + Size aSize ( aTitleRect.GetSize() ); + aSize.setHeight( basegfx::fround(aSize.Height() * propvalue[0]) ); + aSize.setWidth( basegfx::fround(aSize.Width() * propvalue[1]) ); + Point aPos( basegfx::fround(aTitlePos.X() +(aSize.Width() * propvalue[2])), + basegfx::fround(aTitlePos.Y() + (aSize.Height() * propvalue[3])) ); + rRectangle[count] = ::tools::Rectangle(aPos, aSize); + count = count+1; + } + else + { + Size aSize( basegfx::fround(aLayoutSize.Width() * propvalue[1]), + basegfx::fround(aLayoutSize.Height() * propvalue[0]) ); + Point aPos( basegfx::fround(aLayoutPos.X() +(aSize.Width() * propvalue[2])), + basegfx::fround(aLayoutPos.Y() + (aSize.Height() * propvalue[3])) ); + rRectangle[count] = ::tools::Rectangle (aPos, aSize); + count = count+1; + } + } + } +} + +static void findAutoLayoutShapesImpl( SdPage& rPage, const LayoutDescriptor& rDescriptor, std::array& rShapes, bool bInit, bool bSwitchLayout ) +{ + // init list of indexes for each presentation shape kind + // this is used to find subsequent shapes with the same presentation shape kind + o3tl::enumarray PresObjIndex; + PresObjIndex.fill(1); + + bool bMissing = false; + + // for each entry in the layoutdescriptor, arrange a presentation shape + for (int i = 0; (i < MAX_PRESOBJS) && (rDescriptor.meKind[i] != PresObjKind::NONE); i++) + { + PresObjKind eKind = rDescriptor.meKind[i]; + SdrObject* pObj = nullptr; + while( (pObj = rPage.GetPresObj( eKind, PresObjIndex[eKind], true )) != nullptr ) + { + PresObjIndex[eKind]++; // on next search for eKind, find next shape with same eKind + + if( !bSwitchLayout || !pObj->IsEmptyPresObj() ) + { + rShapes[i] = pObj; + break; + } + } + + if( !pObj ) + bMissing = true; + } + + if( !(bMissing && bInit) ) + return; + + // for each entry in the layoutdescriptor, look for an alternative shape + for (int i = 0; (i < MAX_PRESOBJS) && (rDescriptor.meKind[i] != PresObjKind::NONE); i++) + { + if( rShapes[i] ) + continue; + + PresObjKind eKind = rDescriptor.meKind[i]; + + SdrObject* pObj = nullptr; + bool bFound = false; + + const size_t nShapeCount = rPage.GetObjCount(); + for(size_t nShapeIndex = 0; nShapeIndex < nShapeCount && !bFound; ++nShapeIndex ) + { + pObj = rPage.GetObj(nShapeIndex); + + if( pObj->IsEmptyPresObj() ) + continue; + + if( pObj->GetObjInventor() != SdrInventor::Default ) + continue; + + // do not reuse shapes that are already part of the layout + if( std::find( rShapes.begin(), rShapes.end(), pObj ) != rShapes.end() ) + continue; + + bool bPresStyle = pObj->GetStyleSheet() && (pObj->GetStyleSheet()->GetFamily() == SfxStyleFamily::Page); + SdrObjKind eSdrObjKind = pObj->GetObjIdentifier(); + + switch( eKind ) + { + case PresObjKind::Title: + bFound = eSdrObjKind == SdrObjKind::TitleText; + break; + case PresObjKind::Table: + bFound = eSdrObjKind == SdrObjKind::Table; + break; + case PresObjKind::Media: + bFound = eSdrObjKind == SdrObjKind::Media; + break; + case PresObjKind::Outline: + bFound = (eSdrObjKind == SdrObjKind::OutlineText) || + ((eSdrObjKind == SdrObjKind::Text) && bPresStyle) || + (eSdrObjKind == SdrObjKind::Table) || (eSdrObjKind == SdrObjKind::Media) || (eSdrObjKind == SdrObjKind::Graphic) || (eSdrObjKind == SdrObjKind::OLE2); + break; + case PresObjKind::Graphic: + bFound = eSdrObjKind == SdrObjKind::Graphic; + break; + case PresObjKind::Object: + if( eSdrObjKind == SdrObjKind::OLE2 ) + { + SdrOle2Obj* pOle2 = dynamic_cast< SdrOle2Obj* >( pObj ); + if( pOle2 ) + { + if( pOle2->IsEmpty() ) + bFound = true; + else + { + ::comphelper::IEmbeddedHelper* pPersist(rPage.getSdrModelFromSdrPage().GetPersist()); + + if( pPersist ) + { + uno::Reference < embed::XEmbeddedObject > xObject = pPersist->getEmbeddedObjectContainer(). + GetEmbeddedObject( pOle2->GetPersistName() ); + + // TODO CL->KA: Why is this not working anymore? + if( xObject.is() ) + { + SvGlobalName aClassId( xObject->getClassID() ); + + const SvGlobalName aAppletClassId( SO3_APPLET_CLASSID ); + const SvGlobalName aPluginClassId( SO3_PLUGIN_CLASSID ); + const SvGlobalName aIFrameClassId( SO3_IFRAME_CLASSID ); + + if( aPluginClassId != aClassId && aAppletClassId != aClassId && aIFrameClassId != aClassId ) + { + bFound = true; + } + } + } + } + } + } + break; + case PresObjKind::Chart: + case PresObjKind::Calc: + if( eSdrObjKind == SdrObjKind::OLE2 ) + { + SdrOle2Obj* pOle2 = dynamic_cast< SdrOle2Obj* >( pObj ); + if( pOle2 ) + { + if( + ((eKind == PresObjKind::Chart) && + ( pOle2->GetProgName() == "StarChart" || pOle2->IsChart() ) ) + || + ((eKind == PresObjKind::Calc) && + ( pOle2->GetProgName() == "StarCalc" || pOle2->IsCalc() ) ) ) + { + bFound = true; + } + } + break; + } + else if( eSdrObjKind == SdrObjKind::Table ) + { + bFound = true; + } + break; + case PresObjKind::Page: + case PresObjKind::Handout: + bFound = eSdrObjKind == SdrObjKind::Page; + break; + case PresObjKind::Notes: + case PresObjKind::Text: + bFound = (bPresStyle && (eSdrObjKind == SdrObjKind::Text)) || (eSdrObjKind == SdrObjKind::OutlineText); + break; + default: + break; + } + } + + if( bFound ) + rShapes[i] = pObj; + } +} + +void SdPage::SetAutoLayout(AutoLayout eLayout, bool bInit, bool bCreate ) +{ + sd::ScopeLockGuard aGuard( maLockAutoLayoutArrangement ); + + const bool bSwitchLayout = eLayout != GetAutoLayout(); + + SfxUndoManager* pUndoManager(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetUndoManager()); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + meAutoLayout = eLayout; + + // if needed, creates and initialises the presentation shapes on this slides master page + CreateTitleAndLayout(bInit, bCreate); + + if((meAutoLayout == AUTOLAYOUT_NONE && maPresentationShapeList.isEmpty()) || mbMaster) + { + // MasterPage or no layout and no presentation shapes available, nothing to do + return; + } + + ::tools::Rectangle aRectangle[MAX_PRESOBJS]; + const LayoutDescriptor& aDescriptor = GetLayoutDescriptor( meAutoLayout ); + OUString sLayoutName( enumtoString(meAutoLayout) ); + CalcAutoLayoutRectangles( *this, aRectangle, sLayoutName); + + o3tl::sorted_vector< SdrObject* > aUsedPresentationObjects; + + std::array aLayoutShapes; + aLayoutShapes.fill(nullptr); + findAutoLayoutShapesImpl( *this, aDescriptor, aLayoutShapes, bInit, bSwitchLayout ); + + // for each entry in the layoutdescriptor, arrange a presentation shape + for (int i = 0; (i < MAX_PRESOBJS) && (aDescriptor.meKind[i] != PresObjKind::NONE); i++) + { + PresObjKind eKind = aDescriptor.meKind[i]; + SdrObject* pObj = InsertAutoLayoutShape( aLayoutShapes[i], eKind, aDescriptor.mbVertical[i], aRectangle[i], bInit ); + if( pObj ) + aUsedPresentationObjects.insert(pObj); // remember that we used this empty shape + } + + // now delete all empty presentation objects that are no longer used by the new layout + if( !bInit ) + return; + + SdrObject* pObj = nullptr; + maPresentationShapeList.seekShape(0); + + while( (pObj = maPresentationShapeList.getNextShape()) ) + { + if( aUsedPresentationObjects.count(pObj) == 0 ) + { + + if( pObj->IsEmptyPresObj() ) + { + if( bUndo ) + pUndoManager->AddUndoAction(getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoDeleteObject(*pObj)); + + RemoveObject( pObj->GetOrdNum() ); + + if( !bUndo ) + SdrObject::Free( pObj ); + } +/* #i108541# keep non empty pres obj as pres obj even if they are not part of the current layout */ + } + } +} + +/************************************************************************* +|* +|* insert object +|* +\************************************************************************/ + +void SdPage::NbcInsertObject(SdrObject* pObj, size_t nPos) +{ + FmFormPage::NbcInsertObject(pObj, nPos); + + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).InsertObject(pObj); + + SdrLayerID nId = pObj->GetLayer(); + if( mbMaster ) + { + if( nId == SdrLayerID(0) ) + pObj->NbcSetLayer( SdrLayerID(2) ); // wrong layer. corrected to BackgroundObj layer + } + else + { + if( nId == SdrLayerID(2) ) + pObj->NbcSetLayer( SdrLayerID(0) ); // wrong layer. corrected to layout layer + } +} + +/************************************************************************* +|* +|* remove object +|* +\************************************************************************/ + +SdrObject* SdPage::RemoveObject(size_t nObjNum) +{ + onRemoveObject(GetObj( nObjNum )); + return FmFormPage::RemoveObject(nObjNum); +} + +/************************************************************************* +|* +|* remove object without broadcast +|* +\************************************************************************/ + +SdrObject* SdPage::NbcRemoveObject(size_t nObjNum) +{ + onRemoveObject(GetObj( nObjNum )); + return FmFormPage::NbcRemoveObject(nObjNum); +} + +// Also override ReplaceObject methods to realize when +// objects are removed with this mechanism instead of RemoveObject +SdrObject* SdPage::ReplaceObject(SdrObject* pNewObj, size_t nObjNum) +{ + onRemoveObject(GetObj( nObjNum )); + return FmFormPage::ReplaceObject(pNewObj, nObjNum); +} + +// called after a shape is removed or replaced from this slide + +void SdPage::onRemoveObject( SdrObject* pObject ) +{ + if( pObject ) + { + RemovePresObj(pObject); + + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).RemoveObject(pObject); + + removeAnimations( pObject ); + } +} + +void SdPage::SetSize(const Size& aSize) +{ + Size aOldSize = GetSize(); + + if (aSize != aOldSize) + { + FmFormPage::SetSize(aSize); + } +} + +void SdPage::SetBorder(sal_Int32 nLft, sal_Int32 nUpp, sal_Int32 nRgt, sal_Int32 nLwr) +{ + if (nLft != GetLeftBorder() || nUpp != GetUpperBorder() || + nRgt != GetRightBorder() || nLwr != GetLowerBorder() ) + { + FmFormPage::SetBorder(nLft, nUpp, nRgt, nLwr); + } +} + +void SdPage::SetLeftBorder(sal_Int32 nBorder) +{ + if (nBorder != GetLeftBorder() ) + { + FmFormPage::SetLeftBorder(nBorder); + } +} + +void SdPage::SetRightBorder(sal_Int32 nBorder) +{ + if (nBorder != GetRightBorder() ) + { + FmFormPage::SetRightBorder(nBorder); + } +} + +void SdPage::SetUpperBorder(sal_Int32 nBorder) +{ + if (nBorder != GetUpperBorder() ) + { + FmFormPage::SetUpperBorder(nBorder); + } +} + +void SdPage::SetLowerBorder(sal_Int32 nBorder) +{ + if (nBorder != GetLowerBorder() ) + { + FmFormPage::SetLowerBorder(nBorder); + } +} + +/************************************************************************* +|* +|* Adjust all objects to new page size. +|* +|* bScaleAllObj: all objects are scaled into the new area within the page +|* margins. We scale the position and size. For presentation objects on the +|* master page, we also scale the font height of the presentation template. +|* +\************************************************************************/ + +void SdPage::ScaleObjects(const Size& rNewPageSize, const ::tools::Rectangle& rNewBorderRect, bool bScaleAllObj) +{ + sd::ScopeLockGuard aGuard( maLockAutoLayoutArrangement ); + + mbScaleObjects = bScaleAllObj; + SdrObject* pObj = nullptr; + Point aRefPnt(0, 0); + Size aNewPageSize(rNewPageSize); + sal_Int32 nLeft = rNewBorderRect.Left(); + sal_Int32 nRight = rNewBorderRect.Right(); + sal_Int32 nUpper = rNewBorderRect.Top(); + sal_Int32 nLower = rNewBorderRect.Bottom(); + + // negative values are fixed values + // -> use up to date values + if (aNewPageSize.Width() < 0) + { + aNewPageSize.setWidth( GetWidth() ); + } + if (aNewPageSize.Height() < 0) + { + aNewPageSize.setHeight( GetHeight() ); + } + if (nLeft < 0) + { + nLeft = GetLeftBorder(); + } + if (nRight < 0) + { + nRight = GetRightBorder(); + } + if (nUpper < 0) + { + nUpper = GetUpperBorder(); + } + if (nLower < 0) + { + nLower = GetLowerBorder(); + } + + Size aBackgroundSize(aNewPageSize); + + if (mbScaleObjects) + { + aBackgroundSize.AdjustWidth( -(nLeft + nRight) ); + aBackgroundSize.AdjustHeight( -(nUpper + nLower) ); + aNewPageSize = aBackgroundSize; + } + + ::tools::Long nOldWidth = GetWidth() - GetLeftBorder() - GetRightBorder(); + ::tools::Long nOldHeight = GetHeight() - GetUpperBorder() - GetLowerBorder(); + + Fraction aFractX(aNewPageSize.Width(), nOldWidth); + Fraction aFractY(aNewPageSize.Height(), nOldHeight); + + const size_t nObjCnt = (mbScaleObjects ? GetObjCount() : 0); + + for (size_t nObj = 0; nObj < nObjCnt; ++nObj) + { + bool bIsPresObjOnMaster = false; + + // all Objects + pObj = GetObj(nObj); + + if (mbMaster && IsPresObj(pObj)) + { + // There is a presentation object on the master page + bIsPresObjOnMaster = true; + } + + if (pObj) + { + // remember aTopLeft as original TopLeft + Point aTopLeft(pObj->GetCurrentBoundRect().TopLeft()); + + if (!pObj->IsEdgeObj()) + { + /************************************************************** + * Scale objects + **************************************************************/ + if (mbScaleObjects) + { + // use aTopLeft as original TopLeft + aRefPnt = aTopLeft; + } + + pObj->Resize(aRefPnt, aFractX, aFractY); + + if (mbScaleObjects) + { + SdrObjKind eObjKind = pObj->GetObjIdentifier(); + + if (bIsPresObjOnMaster) + { + /********************************************************** + * presentation template: adjust test height + **********************************************************/ + + if (pObj == GetPresObj(PresObjKind::Title, 0)) + { + SfxStyleSheet* pTitleSheet = GetStyleSheetForPresObj(PresObjKind::Title); + + if (pTitleSheet) + { + SfxItemSet& rSet = pTitleSheet->GetItemSet(); + + const SvxFontHeightItem& rOldHgt = rSet.Get(EE_CHAR_FONTHEIGHT); + sal_uLong nFontHeight = rOldHgt.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast(aFractY)); + rSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT)); + + if( SfxItemState::DEFAULT == rSet.GetItemState( EE_CHAR_FONTHEIGHT_CJK ) ) + { + const SvxFontHeightItem& rOldHgt2 = rSet.Get(EE_CHAR_FONTHEIGHT_CJK); + nFontHeight = rOldHgt2.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast(aFractY)); + rSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CJK)); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( EE_CHAR_FONTHEIGHT_CTL ) ) + { + const SvxFontHeightItem& rOldHgt2 = rSet.Get(EE_CHAR_FONTHEIGHT_CTL); + nFontHeight = rOldHgt2.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast(aFractY)); + rSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CTL)); + } + + pTitleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } + else if (pObj == GetPresObj(PresObjKind::Outline, 0)) + { + OUString aName(GetLayoutName() + " "); + + for (sal_Int32 i=1; i<=9; i++) + { + OUString sLayoutName( aName + OUString::number( i ) ); + SfxStyleSheet* pOutlineSheet = static_cast(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetStyleSheetPool()->Find(sLayoutName, SfxStyleFamily::Page)); + + if (pOutlineSheet) + { + // Calculate new font height + SfxItemSet aTempSet(pOutlineSheet->GetItemSet()); + + const SvxFontHeightItem& rOldHgt = aTempSet.Get(EE_CHAR_FONTHEIGHT); + sal_uLong nFontHeight = rOldHgt.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast(aFractY)); + aTempSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT)); + + if( SfxItemState::DEFAULT == aTempSet.GetItemState( EE_CHAR_FONTHEIGHT_CJK ) ) + { + const SvxFontHeightItem& rOldHgt2 = aTempSet.Get(EE_CHAR_FONTHEIGHT_CJK); + nFontHeight = rOldHgt2.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast(aFractY)); + aTempSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CJK)); + } + + if( SfxItemState::DEFAULT == aTempSet.GetItemState( EE_CHAR_FONTHEIGHT_CTL ) ) + { + const SvxFontHeightItem& rOldHgt2 = aTempSet.Get(EE_CHAR_FONTHEIGHT_CTL); + nFontHeight = rOldHgt2.GetHeight(); + nFontHeight = ::tools::Long(nFontHeight * static_cast(aFractY)); + aTempSet.Put(SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CTL)); + } + + // adjust bullet + static_cast(pOutlineSheet)->AdjustToFontHeight(aTempSet, false); + + // Special treatment: reset the INVALIDS to + // NULL pointer (otherwise we have INVALID's + // or pointer to the DefaultItems in the + // template; both would suppress the + // attribute inheritance) + aTempSet.ClearInvalidItems(); + + // Special treatment: only the valid parts + // of the BulletItems + if (aTempSet.GetItemState(EE_PARA_BULLET) == SfxItemState::DEFAULT) + { + SvxBulletItem aOldBulItem( pOutlineSheet->GetItemSet().Get(EE_PARA_BULLET) ); + const SvxBulletItem& rNewBulItem = aTempSet.Get(EE_PARA_BULLET); + aOldBulItem.CopyValidProperties(rNewBulItem); + aTempSet.Put(aOldBulItem); + } + + pOutlineSheet->GetItemSet().Put(aTempSet); + pOutlineSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } + } + else if (pObj == GetPresObj(PresObjKind::Notes, 0)) + { + SfxStyleSheet* pNotesSheet = GetStyleSheetForPresObj(PresObjKind::Notes); + + if (pNotesSheet) + { + sal_uLong nHeight = pObj->GetLogicRect().GetSize().Height(); + sal_uLong nFontHeight = static_cast(nHeight * 0.0741); + SfxItemSet& rSet = pNotesSheet->GetItemSet(); + rSet.Put( SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT )); + rSet.Put( SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CJK )); + rSet.Put( SvxFontHeightItem(nFontHeight, 100, EE_CHAR_FONTHEIGHT_CTL )); + pNotesSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } + } + else if ( eObjKind != SdrObjKind::TitleText && + eObjKind != SdrObjKind::OutlineText && + dynamic_cast< const SdrTextObj *>( pObj ) != nullptr && + pObj->GetOutlinerParaObject() ) + { + /****************************************************** + * normal text object: adjust text height + ******************************************************/ + SvtScriptType nScriptType = pObj->GetOutlinerParaObject()->GetTextObject().GetScriptType(); + sal_uInt16 nWhich = EE_CHAR_FONTHEIGHT; + if ( nScriptType == SvtScriptType::ASIAN ) + nWhich = EE_CHAR_FONTHEIGHT_CJK; + else if ( nScriptType == SvtScriptType::COMPLEX ) + nWhich = EE_CHAR_FONTHEIGHT_CTL; + + // use more modern method to scale the text height + sal_uInt32 nFontHeight = static_cast(pObj->GetMergedItem(nWhich)).GetHeight(); + sal_uInt32 nNewFontHeight = sal_uInt32(static_cast(nFontHeight) * static_cast(aFractY)); + + pObj->SetMergedItem(SvxFontHeightItem(nNewFontHeight, 100, nWhich)); + } + } + } + + if (mbScaleObjects && !pObj->IsEdgeObj()) + { + /************************************************************** + * scale object position + **************************************************************/ + Point aNewPos; + + // corrected scaling; only distances may be scaled + // use aTopLeft as original TopLeft + aNewPos.setX( ::tools::Long((aTopLeft.X() - GetLeftBorder()) * static_cast(aFractX)) + nLeft ); + aNewPos.setY( ::tools::Long((aTopLeft.Y() - GetUpperBorder()) * static_cast(aFractY)) + nUpper ); + + Size aVec(aNewPos.X() - aTopLeft.X(), aNewPos.Y() - aTopLeft.Y()); + + if (aVec.Height() != 0 || aVec.Width() != 0) + { + pObj->NbcMove(aVec); + } + + pObj->SetChanged(); + pObj->BroadcastObjectChange(); + } + } + } +} + +static SdrObject* convertPresentationObjectImpl(SdPage& rPage, SdrObject* pSourceObj, PresObjKind& eObjKind, bool bVertical, const ::tools::Rectangle& rRect) +{ + SdDrawDocument& rModel(static_cast< SdDrawDocument& >(rPage.getSdrModelFromSdrPage())); + if( !pSourceObj ) + return pSourceObj; + + SfxUndoManager* pUndoManager = rModel.GetUndoManager(); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && rPage.IsInserted(); + + SdrObject* pNewObj = pSourceObj; + if((eObjKind == PresObjKind::Outline) && (pSourceObj->GetObjIdentifier() == SdrObjKind::Text) ) + { + pNewObj = rPage.CreatePresObj(PresObjKind::Outline, bVertical, rRect); + + // Set text of the subtitle into PRESOBJ_OUTLINE + OutlinerParaObject* pOutlParaObj = pSourceObj->GetOutlinerParaObject(); + + if(pOutlParaObj) + { + // assign text + SdOutliner* pOutl = rModel.GetInternalOutliner(); + pOutl->Clear(); + pOutl->SetText( *pOutlParaObj ); + pNewObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + pOutlParaObj = pNewObj->GetOutlinerParaObject(); + pOutl->Clear(); + pNewObj->SetEmptyPresObj(false); + + for (sal_uInt16 nLevel = 1; nLevel < 10; nLevel++) + { + // assign new template + OUString aName( rPage.GetLayoutName() + " " + OUString::number( nLevel ) ); + SfxStyleSheet* pSheet = static_cast( rModel.GetStyleSheetPool()->Find(aName, SfxStyleFamily::Page) ); + + if (pSheet && nLevel == 1) + { + SfxStyleSheet* pSubtitleSheet = rPage.GetStyleSheetForPresObj(PresObjKind::Text); + + if (pSubtitleSheet) + pOutlParaObj->ChangeStyleSheetName(SfxStyleFamily::Page, pSubtitleSheet->GetName(), pSheet->GetName()); + } + } + + // Remove LRSpace item + SfxItemSetFixed aSet(rModel.GetPool()); + + aSet.Put(pNewObj->GetMergedItemSet()); + + aSet.ClearItem(EE_PARA_LRSPACE); + + pNewObj->SetMergedItemSet(aSet); + + if( bUndo ) + pUndoManager->AddUndoAction( rModel.GetSdrUndoFactory().CreateUndoDeleteObject(*pSourceObj) ); + + // Remove outline shape from page + rPage.RemoveObject( pSourceObj->GetOrdNum() ); + + if( !bUndo ) + SdrObject::Free( pSourceObj ); + } + } + else if((eObjKind == PresObjKind::Text) && (pSourceObj->GetObjIdentifier() == SdrObjKind::OutlineText) ) + { + // is there an outline shape we can use to replace empty subtitle shape? + pNewObj = rPage.CreatePresObj(PresObjKind::Text, bVertical, rRect); + + // Set text of the outline object into PRESOBJ_TITLE + OutlinerParaObject* pOutlParaObj = pSourceObj->GetOutlinerParaObject(); + + if(pOutlParaObj) + { + // assign text + SdOutliner* pOutl = rModel.GetInternalOutliner(); + pOutl->Clear(); + pOutl->SetText( *pOutlParaObj ); + pNewObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + pOutl->Clear(); + pNewObj->SetEmptyPresObj(false); + + // reset left indent + SfxItemSetFixed aSet(rModel.GetPool()); + + aSet.Put(pNewObj->GetMergedItemSet()); + + const SvxLRSpaceItem& rLRItem = aSet.Get(EE_PARA_LRSPACE); + SvxLRSpaceItem aNewLRItem(rLRItem); + aNewLRItem.SetTextLeft(0); + aSet.Put(aNewLRItem); + + pNewObj->SetMergedItemSet(aSet); + + SfxStyleSheet* pSheet = rPage.GetStyleSheetForPresObj(PresObjKind::Text); + if (pSheet) + pNewObj->SetStyleSheet(pSheet, true); + + // Remove subtitle shape from page + if( bUndo ) + pUndoManager->AddUndoAction(rModel.GetSdrUndoFactory().CreateUndoDeleteObject(*pSourceObj)); + + rPage.RemoveObject( pSourceObj->GetOrdNum() ); + + if( !bUndo ) + SdrObject::Free( pSourceObj ); + } + } + else if((eObjKind == PresObjKind::Outline) && (pSourceObj->GetObjIdentifier() != SdrObjKind::OutlineText) ) + { + switch( pSourceObj->GetObjIdentifier() ) + { + case SdrObjKind::Table: eObjKind = PresObjKind::Table; break; + case SdrObjKind::Media: eObjKind = PresObjKind::Media; break; + case SdrObjKind::Graphic: eObjKind = PresObjKind::Graphic; break; + case SdrObjKind::OLE2: eObjKind = PresObjKind::Object; break; + default: break; + } + } + + return pNewObj; +} + +/** reuses or creates a presentation shape for an auto layout that fits the given parameter + + @param eObjKind + The kind of presentation shape we like to have + @param nIndex + If > 1 we skip the first nIndex-1 shapes with the presentation shape kind eObjKind while + looking for an existing presentation shape + @param bVertical + If true, the shape is created vertical if bInit is true + @param rRect + The rectangle that should be used to transform the shape + @param bInit + If true the shape is created if not found + @returns + A presentation shape that was either found or created with the given parameters +*/ +SdrObject* SdPage::InsertAutoLayoutShape(SdrObject* pObj, PresObjKind eObjKind, bool bVertical, const ::tools::Rectangle& rRect, bool bInit) +{ + SfxUndoManager* pUndoManager(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetUndoManager()); + const bool bUndo = pUndoManager && pUndoManager->IsInListAction() && IsInserted(); + + if (!pObj && bInit) + { + pObj = CreatePresObj(eObjKind, bVertical, rRect); + } + else if ( pObj && (pObj->GetUserCall() || bInit) ) + { + // convert object if shape type does not match kind (f.e. converting outline text to subtitle text) + if( bInit ) + pObj = convertPresentationObjectImpl(*this, pObj, eObjKind, bVertical, rRect); + + if( bUndo ) + { + pUndoManager->AddUndoAction( getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoGeoObject( *pObj ) ); + pUndoManager->AddUndoAction( getSdrModelFromSdrPage().GetSdrUndoFactory().CreateUndoAttrObject( *pObj, true, true ) ); + pUndoManager->AddUndoAction( std::make_unique( *pObj ) ); + } + + pObj->AdjustToMaxRect(rRect); + + pObj->SetUserCall(this); + + SdrTextObj* pTextObject = dynamic_cast< SdrTextObj* >(pObj); + if( pTextObject ) + { + if( pTextObject->IsVerticalWriting() != bVertical ) + { + pTextObject->SetVerticalWriting( bVertical ); + + // here make sure the correct anchoring is used when the object + // is re-used but orientation is changed + if(PresObjKind::Outline == eObjKind) + pTextObject->SetMergedItem(SdrTextHorzAdjustItem( bVertical ? SDRTEXTHORZADJUST_RIGHT : SDRTEXTHORZADJUST_BLOCK )); + } + + if( !mbMaster && (pTextObject->GetObjIdentifier() != SdrObjKind::Table) ) + { + if ( pTextObject->IsAutoGrowHeight() ) + { + // switch off AutoGrowHeight, set new MinHeight + SfxItemSet aTempAttr( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + SdrMetricItem aMinHeight( makeSdrTextMinFrameHeightItem(rRect.GetSize().Height()) ); + aTempAttr.Put( aMinHeight ); + aTempAttr.Put( makeSdrTextAutoGrowHeightItem(false) ); + pTextObject->SetMergedItemSet(aTempAttr); + pTextObject->SetLogicRect(rRect); + + // switch on AutoGrowHeight + SfxItemSet aAttr( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + aAttr.Put( makeSdrTextAutoGrowHeightItem(true) ); + + pTextObject->SetMergedItemSet(aAttr); + } + + if ( pTextObject->IsAutoGrowWidth() ) + { + // switch off AutoGrowWidth , set new MinWidth + SfxItemSet aTempAttr( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + SdrMetricItem aMinWidth( makeSdrTextMinFrameWidthItem(rRect.GetSize().Width()) ); + aTempAttr.Put( aMinWidth ); + aTempAttr.Put( makeSdrTextAutoGrowWidthItem(false) ); + pTextObject->SetMergedItemSet(aTempAttr); + pTextObject->SetLogicRect(rRect); + + // switch on AutoGrowWidth + SfxItemSet aAttr( static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetPool() ); + aAttr.Put( makeSdrTextAutoGrowWidthItem(true) ); + pTextObject->SetMergedItemSet(aAttr); + } + } + } + } + + if(pObj && bInit ) + { + if( !IsPresObj( pObj ) ) + { + if( bUndo ) + pUndoManager->AddUndoAction( std::make_unique( *pObj ) ); + + InsertPresObj( pObj, eObjKind ); + } + + // make adjustments for vertical title and outline shapes + if( bVertical && (( eObjKind == PresObjKind::Title) || (eObjKind == PresObjKind::Outline))) + { + SfxItemSet aNewSet(pObj->GetMergedItemSet()); + aNewSet.Put( makeSdrTextAutoGrowWidthItem(true) ); + aNewSet.Put( makeSdrTextAutoGrowHeightItem(false) ); + if( eObjKind == PresObjKind::Outline ) + { + aNewSet.Put( SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP) ); + aNewSet.Put( SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT) ); + } + pObj->SetMergedItemSet(aNewSet); + } + } + + if ( pObj && (pObj->GetUserCall() || bInit) && ( pObj->IsEmptyPresObj() || dynamic_cast< const SdrGrafObj *>( pObj ) == nullptr ) ) + pObj->AdjustToMaxRect(rRect); + + return pObj; +} + +/************************************************************************* +|* +|* Returns the PresObjKind of an object +|* +\************************************************************************/ + +PresObjKind SdPage::GetPresObjKind(SdrObject* pObj) const +{ + PresObjKind eKind = PresObjKind::NONE; + if( (pObj != nullptr) && (maPresentationShapeList.hasShape(*pObj)) ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj); + if( pInfo ) + eKind = pInfo->mePresObjKind; + } + + return eKind; +} + +bool SdPage::IsPresObj(const SdrObject* pObj) +{ + return pObj && maPresentationShapeList.hasShape( const_cast(*pObj) ); +} + +void SdPage::RemovePresObj(const SdrObject* pObj) +{ + if( pObj && maPresentationShapeList.hasShape(const_cast(*pObj)) ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(const_cast(*pObj)); + if( pInfo ) + pInfo->mePresObjKind = PresObjKind::NONE; + maPresentationShapeList.removeShape(const_cast(*pObj)); + } +} + +void SdPage::InsertPresObj(SdrObject* pObj, PresObjKind eKind ) +{ + DBG_ASSERT( pObj, "sd::SdPage::InsertPresObj(), invalid presentation object inserted!" ); + DBG_ASSERT( !IsPresObj(pObj), "sd::SdPage::InsertPresObj(), presentation object inserted twice!" ); + if( pObj ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj, true); + if( pInfo ) + pInfo->mePresObjKind = eKind; + maPresentationShapeList.addShape(*pObj); + } +} + +/************************************************************************* +|* +|* Set the text of an object +|* +\************************************************************************/ + +void SdPage::SetObjText(SdrTextObj* pObj, SdrOutliner* pOutliner, PresObjKind eObjKind, std::u16string_view rString ) +{ + if ( !pObj ) + return; + + ::Outliner* pOutl = pOutliner; + + if (!pOutliner) + { + SfxItemPool* pPool(static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetDrawOutliner().GetEmptyItemSet().GetPool()); + pOutl = new ::Outliner( pPool, OutlinerMode::OutlineObject ); + pOutl->SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + pOutl->SetEditTextObjectPool(pPool); + pOutl->SetStyleSheetPool(static_cast(getSdrModelFromSdrPage().GetStyleSheetPool())); + pOutl->EnableUndo(false); + pOutl->SetUpdateLayout( false ); + } + + OutlinerMode nOutlMode = pOutl->GetOutlinerMode(); + Size aPaperSize = pOutl->GetPaperSize(); + bool bUpdateMode = pOutl->SetUpdateLayout(false); + pOutl->SetParaAttribs( 0, pOutl->GetEmptyItemSet() ); + + // Always set the object's StyleSheet at the Outliner to + // use the current objects StyleSheet. Thus it's the same as in + // SetText(...). + // Moved this implementation from where SetObjText(...) was called + // to inside this method to work even when outliner is fetched here. + pOutl->SetStyleSheet(0, pObj->GetStyleSheet()); + + OUString aString; + + switch( eObjKind ) + { + case PresObjKind::Outline: + { + pOutl->Init( OutlinerMode::OutlineObject ); + + aString += OUString::Concat("\t") + rString; + + if (mbMaster) + { + pOutl->SetStyleSheet( 0, GetStyleSheetForPresObj(eObjKind) ); + aString += "\n\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER2) + + "\n\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER3) + + "\n\t\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER4) + + "\n\t\t\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER5) + + "\n\t\t\t\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER6) + + "\n\t\t\t\t\t\t\t" + + SdResId(STR_PRESOBJ_MPOUTLLAYER7); + + } + } + break; + + case PresObjKind::Title: + { + pOutl->Init( OutlinerMode::TitleObject ); + aString += rString; + } + break; + + default: + { + pOutl->Init( OutlinerMode::TextObject ); + aString += rString; + + // check if we need to add a text field + std::unique_ptr pData; + + switch( eObjKind ) + { + case PresObjKind::Header: + pData.reset(new SvxHeaderField()); + break; + case PresObjKind::Footer: + pData .reset(new SvxFooterField()); + break; + case PresObjKind::SlideNumber: + pData.reset(new SvxPageField()); + break; + case PresObjKind::DateTime: + pData.reset(new SvxDateTimeField()); + break; + default: + break; + } + + if( pData ) + { + ESelection e; + SvxFieldItem aField( *pData, EE_FEATURE_FIELD ); + pOutl->QuickInsertField(aField,e); + } + } + break; + } + + pOutl->SetPaperSize( pObj->GetLogicRect().GetSize() ); + + if( !aString.isEmpty() ) + pOutl->SetText( aString, pOutl->GetParagraph( 0 ) ); + + pObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + + if (!pOutliner) + { + delete pOutl; + pOutl = nullptr; + } + else + { + // restore the outliner + pOutl->Init( nOutlMode ); + pOutl->SetParaAttribs( 0, pOutl->GetEmptyItemSet() ); + pOutl->SetUpdateLayout( bUpdateMode ); + pOutl->SetPaperSize( aPaperSize ); + } +} + +/************************************************************************* +|* +|* Set the name of the layout +|* +\************************************************************************/ +void SdPage::SetLayoutName(const OUString& aName) +{ + maLayoutName = aName; + + if( mbMaster ) + { + sal_Int32 nPos = maLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + FmFormPage::SetName(maLayoutName.copy(0, nPos)); + } +} + +/************************************************************************* +|* +|* Return the page name and generates it if necessary +|* +\************************************************************************/ + +const OUString& SdPage::GetName() const +{ + OUString aCreatedPageName( maCreatedPageName ); + if (GetRealName().isEmpty()) + { + if ((mePageKind == PageKind::Standard || mePageKind == PageKind::Notes) && !mbMaster) + { + // default name for handout pages + sal_uInt16 nNum = (GetPageNum() + 1) / 2; + + aCreatedPageName = SdResId(STR_PAGE) + " "; + if (static_cast(getSdrModelFromSdrPage()).GetDocumentType() == DocumentType::Draw ) + aCreatedPageName = SdResId(STR_PAGE_NAME) + " "; + + if( getSdrModelFromSdrPage().GetPageNumType() == css::style::NumberingType::NUMBER_NONE ) + { + // if the document has number none as a formatting + // for page numbers we still default to arabic numbering + // to keep the default page names unique + aCreatedPageName += OUString::number( static_cast(nNum) ); + } + else + { + aCreatedPageName += static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).CreatePageNumValue(nNum); + } + } + else + { + /****************************************************************** + * default name for note pages + ******************************************************************/ + aCreatedPageName = SdResId(STR_LAYOUT_DEFAULT_NAME); + } + } + else + { + aCreatedPageName = GetRealName(); + } + + if (mePageKind == PageKind::Notes) + { + aCreatedPageName += " " + SdResId(STR_NOTES); + } + else if (mePageKind == PageKind::Handout && mbMaster) + { + aCreatedPageName += " (" + SdResId(STR_HANDOUT) + ")"; + } + + const_cast< SdPage* >(this)->maCreatedPageName = aCreatedPageName; + return maCreatedPageName; +} + +void SdPage::SetOrientation( Orientation /*eOrient*/) +{ + // Do nothing +} + +Orientation SdPage::GetOrientation() const +{ + Size aSize = GetSize(); + if ( aSize.getWidth() > aSize.getHeight() ) + { + return Orientation::Landscape; + } + else + { + return Orientation::Portrait; + } +} + +/************************************************************************* +|* +|* returns the default text of a PresObjektes +|* +\************************************************************************/ + +OUString SdPage::GetPresObjText(PresObjKind eObjKind) const +{ + OUString aString; + +#if defined(IOS) || defined(ANDROID) + bool isMobileDevice = true; +#else + bool isMobileDevice = false; + if (const SfxViewShell* pCurrentViewShell = SfxViewShell::Current()) + isMobileDevice = pCurrentViewShell->isLOKMobilePhone() || pCurrentViewShell->isLOKTablet(); +#endif + + if (eObjKind == PresObjKind::Title) + { + if (mbMaster) + { + if (mePageKind != PageKind::Notes) + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_MPTITLE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_MPTITLE); + } + else + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_MPNOTESTITLE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_MPNOTESTITLE); + } + } + else if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_TITLE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_TITLE); + } + else if (eObjKind == PresObjKind::Outline) + { + if (mbMaster) + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_MPOUTLINE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_MPOUTLINE); + } + else if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_OUTLINE_MOBILE); + else + aString = SdResId(STR_PRESOBJ_OUTLINE); + } + else if (eObjKind == PresObjKind::Notes) + { + if (mbMaster) + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_MPNOTESTEXT_MOBILE); + else + aString = SdResId(STR_PRESOBJ_MPNOTESTEXT); + } + else if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_NOTESTEXT_MOBILE); + else + aString = SdResId(STR_PRESOBJ_NOTESTEXT); + } + else if (eObjKind == PresObjKind::Text) + { + if (isMobileDevice) + aString = SdResId(STR_PRESOBJ_TEXT_MOBILE); + else + aString = SdResId(STR_PRESOBJ_TEXT); + } + else if (eObjKind == PresObjKind::Graphic) + { + aString = SdResId( STR_PRESOBJ_GRAPHIC ); + } + else if (eObjKind == PresObjKind::Object) + { + aString = SdResId( STR_PRESOBJ_OBJECT ); + } + else if (eObjKind == PresObjKind::Chart) + { + aString = SdResId( STR_PRESOBJ_CHART ); + } + else if (eObjKind == PresObjKind::OrgChart) + { + aString = SdResId( STR_PRESOBJ_ORGCHART ); + } + else if (eObjKind == PresObjKind::Calc) + { + aString = SdResId( STR_PRESOBJ_TABLE ); + } + + return aString; +} + +uno::Reference< uno::XInterface > SdPage::createUnoPage() +{ + return createUnoPageImpl( this ); +} + +/** returns the SdPage implementation for the given XDrawPage or 0 if not available */ +SdPage* SdPage::getImplementation( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) +{ + try + { + auto pUnoPage = comphelper::getFromUnoTunnel(xPage); + if( pUnoPage ) + return static_cast< SdPage* >( pUnoPage->GetSdrPage() ); + } + catch( css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdPage::getImplementation()" ); + } + + return nullptr; +} + +sal_Int64 SdPage::GetHashCode() const +{ + return sal::static_int_cast(reinterpret_cast(this)); +} + +void SdPage::SetName (const OUString& rName) +{ + OUString aOldName( GetName() ); + FmFormPage::SetName (rName); + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).UpdatePageRelativeURLs(aOldName, rName); + ActionChanged(); +} + +const HeaderFooterSettings& SdPage::getHeaderFooterSettings() const +{ + if( mePageKind == PageKind::Handout && !mbMaster ) + { + return static_cast(TRG_GetMasterPage()).maHeaderFooterSettings; + } + else + { + return maHeaderFooterSettings; + } +} + +void SdPage::setHeaderFooterSettings( const sd::HeaderFooterSettings& rNewSettings ) +{ + if( mePageKind == PageKind::Handout && !mbMaster ) + { + static_cast(TRG_GetMasterPage()).maHeaderFooterSettings = rNewSettings; + } + else + { + maHeaderFooterSettings = rNewSettings; + } + + SetChanged(); + + if(!TRG_HasMasterPage()) + return; + + TRG_GetMasterPageDescriptorViewContact().ActionChanged(); + + // #i119056# For HeaderFooterSettings SdrObjects are used, but the properties + // used are not part of their model data, but kept in SD. This data is applied + // using a 'backdoor' on primitive creation. Thus, the normal mechanism to detect + // object changes does not work here. It is necessary to trigger updates here + // directly. BroadcastObjectChange used for PagePreview invalidations, + // flushViewObjectContacts used to invalidate and flush all visualizations in + // edit views. + SdPage* pMasterPage = dynamic_cast< SdPage* >(&TRG_GetMasterPage()); + + if(!pMasterPage) + return; + + SdrObject* pCandidate = pMasterPage->GetPresObj( PresObjKind::Header ); + + if(pCandidate) + { + pCandidate->BroadcastObjectChange(); + pCandidate->GetViewContact().flushViewObjectContacts(); + } + + pCandidate = pMasterPage->GetPresObj( PresObjKind::DateTime ); + + if(pCandidate) + { + pCandidate->BroadcastObjectChange(); + pCandidate->GetViewContact().flushViewObjectContacts(); + } + + pCandidate = pMasterPage->GetPresObj( PresObjKind::Footer ); + + if(pCandidate) + { + pCandidate->BroadcastObjectChange(); + pCandidate->GetViewContact().flushViewObjectContacts(); + } + + pCandidate = pMasterPage->GetPresObj( PresObjKind::SlideNumber ); + + if(pCandidate) + { + pCandidate->BroadcastObjectChange(); + pCandidate->GetViewContact().flushViewObjectContacts(); + } +} + +bool SdPage::checkVisibility( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + bool bEdit ) +{ + if( !FmFormPage::checkVisibility( rOriginal, rDisplayInfo, bEdit ) ) + return false; + + SdrObject* pObj = rOriginal.GetViewContact().TryToGetSdrObject(); + if( pObj == nullptr ) + return false; + + const SdrPage* pVisualizedPage = GetSdrPageFromXDrawPage(rOriginal.GetObjectContact().getViewInformation2D().getVisualizedPage()); + const bool bIsPrinting(rOriginal.GetObjectContact().isOutputToPrinter() || rOriginal.GetObjectContact().isOutputToPDFFile()); + const SdrPageView* pPageView = rOriginal.GetObjectContact().TryToGetSdrPageView(); + const bool bIsInsidePageObj(pPageView && pPageView->GetPage() != pVisualizedPage); + + // empty presentation objects only visible during edit mode + if( (bIsPrinting || !bEdit || bIsInsidePageObj ) && pObj->IsEmptyPresObj() ) + { + if( (pObj->GetObjInventor() != SdrInventor::Default) || ( (pObj->GetObjIdentifier() != SdrObjKind::Rectangle) && (pObj->GetObjIdentifier() != SdrObjKind::Page) ) ) + return false; + } + + if( ( pObj->GetObjInventor() == SdrInventor::Default ) && ( pObj->GetObjIdentifier() == SdrObjKind::Text ) ) + { + const SdPage* pCheckPage = dynamic_cast< const SdPage* >(pObj->getSdrPageFromSdrObject()); + + if( pCheckPage ) + { + PresObjKind eKind = pCheckPage->GetPresObjKind(pObj); + + if((eKind == PresObjKind::Footer) || (eKind == PresObjKind::Header) || (eKind == PresObjKind::DateTime) || (eKind == PresObjKind::SlideNumber) ) + { + const bool bSubContentProcessing(rDisplayInfo.GetSubContentActive()); + + if( bSubContentProcessing || ( pCheckPage->GetPageKind() == PageKind::Handout && bIsPrinting ) ) + { + // use the page that is currently processed + const SdPage* pVisualizedSdPage = dynamic_cast< const SdPage* >(pVisualizedPage); + + if( pVisualizedSdPage ) + { + // if we are not on a masterpage, see if we have to draw this header&footer object at all + const sd::HeaderFooterSettings& rSettings = pVisualizedSdPage->getHeaderFooterSettings(); + + switch( eKind ) + { + case PresObjKind::Footer: + return rSettings.mbFooterVisible; + case PresObjKind::Header: + return rSettings.mbHeaderVisible; + case PresObjKind::DateTime: + return rSettings.mbDateTimeVisible; + case PresObjKind::SlideNumber: + return rSettings.mbSlideNumberVisible; + default: + break; + } + } + } + } // check for placeholders on master + else if( (eKind != PresObjKind::NONE) && pCheckPage->IsMasterPage() && ( pVisualizedPage != pCheckPage ) ) + { + // presentation objects on master slide are always invisible if slide is shown. + return false; + } + } + } + + // i63977, do not print SdrpageObjs from master pages + if( ( pObj->GetObjInventor() == SdrInventor::Default ) && ( pObj->GetObjIdentifier() == SdrObjKind::Page ) ) + { + if( pObj->getSdrPageFromSdrObject() && pObj->getSdrPageFromSdrObject()->IsMasterPage() ) + return false; + } + + return true; +} + +bool SdPage::RestoreDefaultText( SdrObject* pObj ) +{ + bool bRet = false; + + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + + if( pTextObj ) + { + PresObjKind ePresObjKind = GetPresObjKind(pTextObj); + + if (ePresObjKind == PresObjKind::Title || + ePresObjKind == PresObjKind::Outline || + ePresObjKind == PresObjKind::Notes || + ePresObjKind == PresObjKind::Text) + { + OUString aString( GetPresObjText(ePresObjKind) ); + + if (!aString.isEmpty()) + { + bool bVertical = false; + OutlinerParaObject* pOldPara = pTextObj->GetOutlinerParaObject(); + if( pOldPara ) + bVertical = pOldPara->IsEffectivelyVertical(); // is old para object vertical? + + SetObjText( pTextObj, nullptr, ePresObjKind, aString ); + + if( pOldPara ) + { + // Here, only the vertical flag for the + // OutlinerParaObjects needs to be changed. The + // AutoGrowWidth/Height items still exist in the + // not changed object. + if(pTextObj->GetOutlinerParaObject() + && pTextObj->GetOutlinerParaObject()->IsEffectivelyVertical() != bVertical) + { + ::tools::Rectangle aObjectRect = pTextObj->GetSnapRect(); + pTextObj->GetOutlinerParaObject()->SetVertical(bVertical); + pTextObj->SetSnapRect(aObjectRect); + } + } + + pTextObj->SetTextEditOutliner( nullptr ); // to make stylesheet settings work + pTextObj->NbcSetStyleSheet( GetStyleSheetForPresObj(ePresObjKind), true ); + pTextObj->SetEmptyPresObj(true); + bRet = true; + } + } + } + return bRet; +} + +void SdPage::CalculateHandoutAreas( SdDrawDocument& rModel, AutoLayout eLayout, bool bHorizontal, std::vector< ::tools::Rectangle >& rAreas ) +{ + SdPage& rHandoutMaster = *rModel.GetMasterSdPage( 0, PageKind::Handout ); + + static const sal_uInt16 aOffsets[5][9] = + { + { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, // AUTOLAYOUT_HANDOUT9, Portrait, Horizontal order + { 0, 2, 4, 1, 3, 5, 0, 0, 0 }, // AUTOLAYOUT_HANDOUT3, Landscape, Vertical + { 0, 2, 1, 3, 0, 0, 0, 0, 0 }, // AUTOLAYOUT_HANDOUT4, Landscape, Vertical + { 0, 3, 1, 4, 2, 5, 0, 0, 0 }, // AUTOLAYOUT_HANDOUT4, Portrait, Vertical + { 0, 3, 6, 1, 4, 7, 2, 5, 8 }, // AUTOLAYOUT_HANDOUT9, Landscape, Vertical + }; + + const sal_uInt16* pOffsets = aOffsets[0]; + + Size aArea = rHandoutMaster.GetSize(); + const bool bLandscape = aArea.Width() > aArea.Height(); + + if( eLayout == AUTOLAYOUT_NONE ) + { + // use layout from handout master + SdrObjListIter aShapeIter(&rHandoutMaster); + + std::vector< ::tools::Rectangle > vSlidesAreas; + while ( aShapeIter.IsMore() ) + { + SdrPageObj* pPageObj = dynamic_cast( aShapeIter.Next() ); + // get slide rectangles + if (pPageObj) + vSlidesAreas.push_back( pPageObj->GetCurrentBoundRect() ); + } + + if ( !bHorizontal || vSlidesAreas.size() < 4 ) + { // top to bottom, then right + rAreas.swap( vSlidesAreas ); + } + else + { // left to right, then down + switch ( vSlidesAreas.size() ) + { + case 4: + pOffsets = aOffsets[2]; + break; + + default: + [[fallthrough]]; + case 6: + pOffsets = aOffsets[ bLandscape ? 3 : 1 ]; + break; + + case 9: + pOffsets = aOffsets[4]; + break; + } + + rAreas.resize( static_cast(vSlidesAreas.size()) ); + + for( const ::tools::Rectangle& rRect : vSlidesAreas ) + { + rAreas[*pOffsets++] = rRect; + } + } + } + else + { + const ::tools::Long nGapW = 1000; // gap is 1cm + const ::tools::Long nGapH = 1000; + + ::tools::Long nLeftBorder = rHandoutMaster.GetLeftBorder(); + ::tools::Long nRightBorder = rHandoutMaster.GetRightBorder(); + ::tools::Long nTopBorder = rHandoutMaster.GetUpperBorder(); + ::tools::Long nBottomBorder = rHandoutMaster.GetLowerBorder(); + + const ::tools::Long nHeaderFooterHeight = static_cast< ::tools::Long >( (aArea.Height() - nTopBorder - nLeftBorder) * 0.05 ); + + nTopBorder += nHeaderFooterHeight; + nBottomBorder += nHeaderFooterHeight; + + ::tools::Long nX = nGapW + nLeftBorder; + ::tools::Long nY = nGapH + nTopBorder; + + aArea.AdjustWidth( -(nGapW * 2 + nLeftBorder + nRightBorder) ); + aArea.AdjustHeight( -(nGapH * 2 + nTopBorder + nBottomBorder) ); + + sal_uInt16 nColCnt = 0, nRowCnt = 0; + switch ( eLayout ) + { + case AUTOLAYOUT_HANDOUT1: + nColCnt = 1; nRowCnt = 1; + break; + + case AUTOLAYOUT_HANDOUT2: + if( bLandscape ) + { + nColCnt = 2; nRowCnt = 1; + } + else + { + nColCnt = 1; nRowCnt = 2; + } + break; + + case AUTOLAYOUT_HANDOUT3: + if( bLandscape ) + { + nColCnt = 3; nRowCnt = 2; + } + else + { + nColCnt = 2; nRowCnt = 3; + } + pOffsets = aOffsets[ bLandscape ? 1 : 0 ]; + break; + + case AUTOLAYOUT_HANDOUT4: + nColCnt = 2; nRowCnt = 2; + pOffsets = aOffsets[ bHorizontal ? 0 : 2 ]; + break; + + case AUTOLAYOUT_HANDOUT6: + if( bLandscape ) + { + nColCnt = 3; nRowCnt = 2; + } + else + { + nColCnt = 2; nRowCnt = 3; + } + if( !bHorizontal ) + pOffsets = aOffsets[ bLandscape ? 1 : 3 ]; + break; + + default: + case AUTOLAYOUT_HANDOUT9: + nColCnt = 3; nRowCnt = 3; + + if( !bHorizontal ) + pOffsets = aOffsets[4]; + break; + } + + rAreas.resize(static_cast(nColCnt) * nRowCnt); + + Size aPartArea, aSize; + aPartArea.setWidth( (aArea.Width() - ((nColCnt-1) * nGapW) ) / nColCnt ); + aPartArea.setHeight( (aArea.Height() - ((nRowCnt-1) * nGapH) ) / nRowCnt ); + + SdrPage* pFirstPage = rModel.GetMasterSdPage(0, PageKind::Standard); + if (pFirstPage && pFirstPage->GetWidth() && pFirstPage->GetHeight()) + { + // scale actual size into handout rect + double fScale = static_cast(aPartArea.Width()) / static_cast(pFirstPage->GetWidth()); + + aSize.setHeight( static_cast<::tools::Long>(fScale * pFirstPage->GetHeight() ) ); + if( aSize.Height() > aPartArea.Height() ) + { + fScale = static_cast(aPartArea.Height()) / static_cast(pFirstPage->GetHeight()); + aSize.setHeight( aPartArea.Height() ); + aSize.setWidth( static_cast<::tools::Long>(fScale * pFirstPage->GetWidth()) ); + } + else + { + aSize.setWidth( aPartArea.Width() ); + } + + nX += (aPartArea.Width() - aSize.Width()) / 2; + nY += (aPartArea.Height()- aSize.Height())/ 2; + } + else + { + aSize = aPartArea; + } + + Point aPos( nX, nY ); + + const bool bRTL = rModel.GetDefaultWritingMode() == css::text::WritingMode_RL_TB; + + const ::tools::Long nOffsetX = (aPartArea.Width() + nGapW) * (bRTL ? -1 : 1); + const ::tools::Long nOffsetY = aPartArea.Height() + nGapH; + const ::tools::Long nStartX = bRTL ? nOffsetX*(1 - nColCnt) + nX : nX; + + for(sal_uInt16 nRow = 0; nRow < nRowCnt; nRow++) + { + aPos.setX( nStartX ); + for(sal_uInt16 nCol = 0; nCol < nColCnt; nCol++) + { + rAreas[*pOffsets++] = ::tools::Rectangle(aPos, aSize); + aPos.AdjustX(nOffsetX ); + } + + aPos.AdjustY(nOffsetY ); + } + } +} + +void SdPage::SetPrecious (const bool bIsPrecious) +{ + mbIsPrecious = bIsPrecious; +} + +HeaderFooterSettings::HeaderFooterSettings() +{ + mbHeaderVisible = true; + mbFooterVisible = true; + mbSlideNumberVisible = false; + mbDateTimeVisible = true; + mbDateTimeIsFixed = true; + meDateFormat = SvxDateFormat::A; + meTimeFormat = SvxTimeFormat::AppDefault; +} + +bool HeaderFooterSettings::operator==( const HeaderFooterSettings& rSettings ) const +{ + return (mbHeaderVisible == rSettings.mbHeaderVisible) && + (maHeaderText == rSettings.maHeaderText) && + (mbFooterVisible == rSettings.mbFooterVisible) && + (maFooterText == rSettings.maFooterText) && + (mbSlideNumberVisible == rSettings.mbSlideNumberVisible) && + (mbDateTimeVisible == rSettings.mbDateTimeVisible) && + (mbDateTimeIsFixed == rSettings.mbDateTimeIsFixed) && + (meDateFormat == rSettings.meDateFormat) && + (meTimeFormat == rSettings.meTimeFormat) && + (maDateTimeText == rSettings.maDateTimeText); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/sdpage2.cxx b/sd/source/core/sdpage2.cxx new file mode 100644 index 000000000..a5c8d1d5f --- /dev/null +++ b/sd/source/core/sdpage2.cxx @@ -0,0 +1,651 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::office; + +/************************************************************************* +|* +|* Sets: names of layout, master page links and templates for presentation +|* objects +|* +|* Preconditions: - The page has to know the correct model! +|* - The corresponding master page has to be in the model. +|* - The corresponding style sheets have to be in the style sheet +|* pool. +|* +|* bReplaceStyleSheets = sal_True : Named style sheets are replaced +|* sal_False: All style sheets are reassigned +|* +|* bSetMasterPage = sal_True : search and assign master page +|* +|* bReverseOrder = sal_False: search master page from head to tail +|* sal_True : search master page from tail to head +|* (for undo operations) +|* +\************************************************************************/ + +void SdPage::SetPresentationLayout(std::u16string_view rLayoutName, + bool bReplaceStyleSheets, + bool bSetMasterPage, + bool bReverseOrder) +{ + /********************************************************************* + |* Name of the layout of the page + \********************************************************************/ + OUString aOldLayoutName(maLayoutName); // memorize + maLayoutName = OUString::Concat(rLayoutName) + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + /********************************************************************* + |* search and replace master page if necessary + \********************************************************************/ + if (bSetMasterPage && !IsMasterPage()) + { + SdPage* pMaster; + SdPage* pFoundMaster = nullptr; + sal_uInt16 nMaster = 0; + sal_uInt16 nMasterCount = getSdrModelFromSdrPage().GetMasterPageCount(); + + if( !bReverseOrder ) + { + for ( nMaster = 0; nMaster < nMasterCount; nMaster++ ) + { + pMaster = static_cast(getSdrModelFromSdrPage().GetMasterPage(nMaster)); + if (pMaster->GetPageKind() == mePageKind && pMaster->GetLayoutName() == maLayoutName) + { + pFoundMaster = pMaster; + break; + } + } + } + else + { + for ( nMaster = nMasterCount; nMaster > 0; nMaster-- ) + { + pMaster = static_cast(getSdrModelFromSdrPage().GetMasterPage(nMaster - 1)); + if (pMaster->GetPageKind() == mePageKind && pMaster->GetLayoutName() == maLayoutName) + { + pFoundMaster = pMaster; + break; + } + } + } + + DBG_ASSERT(pFoundMaster, "Masterpage for presentation layout not found!"); + + // this should never happen, but we play failsafe here + if( pFoundMaster == nullptr ) + pFoundMaster = static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetSdPage( 0, mePageKind ); + + if( pFoundMaster ) + TRG_SetMasterPage(*pFoundMaster); + } + + /********************************************************************* + |* templates for presentation objects + \********************************************************************/ + // list with: + // - pointer to templates for outline text object (old and new templates) + // - replace-data for OutlinerParaObject + std::vector aOutlineStyles; + std::vector aOldOutlineStyles; + std::vector aReplList; + bool bListsFilled = false; + + const size_t nObjCount = GetObjCount(); + + for (size_t nObj = 0; nObj < nObjCount; ++nObj) + { + auto pObj = GetObj(nObj); + + if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::OutlineText) + { + if (!bListsFilled || !bReplaceStyleSheets) + { + OUString aFullName; + OUString aOldFullName; + SfxStyleSheetBase* pSheet = nullptr; + SfxStyleSheetBasePool* pStShPool = getSdrModelFromSdrPage().GetStyleSheetPool(); + + for (sal_Int16 i = -1; i < 9; i++) + { + aOldFullName = aOldLayoutName + " " + + OUString::number( (i <= 0 ) ? 1 : i + 1 ); + aFullName = maLayoutName + " " + + OUString::number( (i <= 0 ) ? 1 : i + 1); + pSheet = pStShPool->Find(aOldFullName, SfxStyleFamily::Page); + DBG_ASSERT(pSheet, "Old outline style sheet not found"); + aOldOutlineStyles.push_back(pSheet); + + pSheet = pStShPool->Find(aFullName, SfxStyleFamily::Page); + DBG_ASSERT(pSheet, "New outline style sheet not found"); + aOutlineStyles.push_back(pSheet); + + if (bReplaceStyleSheets && pSheet) + { + // Replace instead Set + StyleReplaceData aReplData; + aReplData.nNewFamily = pSheet->GetFamily(); + aReplData.nFamily = pSheet->GetFamily(); + aReplData.aNewName = aFullName; + aReplData.aName = aOldFullName; + aReplList.push_back(aReplData); + } + else + { + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + + if( pOPO ) + pOPO->SetStyleSheets( i, aFullName, SfxStyleFamily::Page ); + } + } + + bListsFilled = true; + } + + + std::vector::iterator iterOldOut = aOldOutlineStyles.begin(); + + for (const auto& rpOut : aOutlineStyles) + { + SfxStyleSheet* pSheet = static_cast(rpOut); + SfxStyleSheet* pOldSheet = static_cast(*iterOldOut); + + if (pSheet != pOldSheet) + { + if (pOldSheet) + pObj->EndListening(*pOldSheet); + + if (pSheet && !pObj->IsListening(*pSheet)) + pObj->StartListening(*pSheet); + } + + ++iterOldOut; + } + + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + if ( bReplaceStyleSheets && pOPO ) + { + for (const auto& rRepl : aReplList) + { + pOPO->ChangeStyleSheets( rRepl.aName, rRepl.nFamily, rRepl.aNewName, rRepl.nNewFamily ); + } + } + } + else if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::TitleText) + { + // We do not get PresObjKind via GetPresObjKind() since there are + // only PresObjListe considered. But we want to consider all "Title + // objects" here (paste from clipboard etc.) + SfxStyleSheet* pSheet = GetStyleSheetForPresObj(PresObjKind::Title); + + if (pSheet) + pObj->SetStyleSheet(pSheet, true); + } + else + { + SfxStyleSheet* pSheet = GetStyleSheetForPresObj(GetPresObjKind(pObj)); + + if (pSheet) + pObj->SetStyleSheet(pSheet, true); + } + } +} + +/************************************************************************* +|* +|* disconnect outline text object from templates for outline levels +|* +\************************************************************************/ + +void SdPage::EndListenOutlineText() +{ + SdrObject* pOutlineTextObj = GetPresObj(PresObjKind::Outline); + + if (!pOutlineTextObj) + return; + + SdStyleSheetPool* pSPool = static_cast(getSdrModelFromSdrPage().GetStyleSheetPool()); + DBG_ASSERT(pSPool, "StyleSheetPool missing"); + OUString aTrueLayoutName(maLayoutName); + sal_Int32 nIndex = aTrueLayoutName.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aTrueLayoutName = aTrueLayoutName.copy(0, nIndex); + + std::vector aOutlineStyles; + pSPool->CreateOutlineSheetList(aTrueLayoutName,aOutlineStyles); + + for (const auto& rpStyle : aOutlineStyles) + { + SfxStyleSheet *pSheet = static_cast(rpStyle); + pOutlineTextObj->EndListening(*pSheet); + } +} + +/************************************************************************* +|* +|* Is this page read-only? +|* +\************************************************************************/ + +bool SdPage::IsReadOnly() const +{ + return false; +} + +/************************************************************************* +|* +|* Connect to sfx2::LinkManager +|* +\************************************************************************/ + +void SdPage::ConnectLink() +{ + sfx2::LinkManager* pLinkManager(getSdrModelFromSdrPage().GetLinkManager()); + + if (!(pLinkManager && !mpPageLink && !maFileName.isEmpty() && !maBookmarkName.isEmpty() && + mePageKind==PageKind::Standard && !IsMasterPage() && + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).IsNewOrLoadCompleted())) + return; + + /********************************************************************** + * Connect + * Only standard pages are allowed to be linked + **********************************************************************/ + ::sd::DrawDocShell* pDocSh = static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()).GetDocSh(); + + if (!pDocSh || pDocSh->GetMedium()->GetOrigURL() != maFileName) + { + // No links to document owned pages! + mpPageLink = new SdPageLink(this, maFileName, maBookmarkName); + OUString aFilterName(SdResId(STR_IMPRESS)); + pLinkManager->InsertFileLink(*mpPageLink, sfx2::SvBaseLinkObjectType::ClientFile, + maFileName, &aFilterName, &maBookmarkName); + mpPageLink->Connect(); + } +} + +/************************************************************************* +|* +|* Disconnect from sfx2::LinkManager +|* +\************************************************************************/ + +void SdPage::DisconnectLink() +{ + sfx2::LinkManager* pLinkManager(getSdrModelFromSdrPage().GetLinkManager()); + + if (pLinkManager && mpPageLink) + { + /********************************************************************** + * Disconnect + * (remove deletes *pGraphicLink implicit) + **********************************************************************/ + pLinkManager->Remove(mpPageLink); + mpPageLink=nullptr; + } +} + +void SdPage::lateInit(const SdPage& rSrcPage) +{ + // call parent + FmFormPage::lateInit(rSrcPage); + + // copy local variables (former stuff from copy constructor) + mePageKind = rSrcPage.mePageKind; + meAutoLayout = rSrcPage.meAutoLayout; + mbSelected = false; + mnTransitionType = rSrcPage.mnTransitionType; + mnTransitionSubtype = rSrcPage.mnTransitionSubtype; + mbTransitionDirection = rSrcPage.mbTransitionDirection; + mnTransitionFadeColor = rSrcPage.mnTransitionFadeColor; + mfTransitionDuration = rSrcPage.mfTransitionDuration; + mePresChange = rSrcPage.mePresChange; + mfTime = rSrcPage.mfTime; + mbSoundOn = rSrcPage.mbSoundOn; + mbExcluded = rSrcPage.mbExcluded; + maLayoutName = rSrcPage.maLayoutName; + maSoundFile = rSrcPage.maSoundFile; + mbLoopSound = rSrcPage.mbLoopSound; + mbStopSound = rSrcPage.mbStopSound; + maCreatedPageName.clear(); + maFileName = rSrcPage.maFileName; + maBookmarkName = rSrcPage.maBookmarkName; + mbScaleObjects = rSrcPage.mbScaleObjects; + meCharSet = rSrcPage.meCharSet; + mnPaperBin = rSrcPage.mnPaperBin; + mpPageLink = nullptr; // is set when inserting via ConnectLink() + mbIsPrecious = false; + + // use shape list directly to preserve constness of rSrcPage + const std::list< SdrObject* >& rShapeList = rSrcPage.maPresentationShapeList.getList(); + const size_t nObjCount = GetObjCount(); + for( SdrObject* pObj : rShapeList ) + { + size_t nOrdNum = pObj->GetOrdNum(); + InsertPresObj(nOrdNum < nObjCount ? GetObj(nOrdNum) : nullptr, rSrcPage.GetPresObjKind(pObj)); + } + + // header footer + setHeaderFooterSettings( rSrcPage.getHeaderFooterSettings() ); + + // animations + rSrcPage.cloneAnimations(*this); + + // annotations + for(const Reference< XAnnotation >& srcAnnotation : rSrcPage.maAnnotations) + { + Reference< XAnnotation > ref; + createAnnotation(ref); + ref->setPosition(srcAnnotation->getPosition()); + ref->setSize(srcAnnotation->getSize()); + ref->setAuthor(srcAnnotation->getAuthor()); + ref->setInitials(srcAnnotation->getInitials()); + ref->setDateTime(srcAnnotation->getDateTime()); + Reference< ::css::text::XTextCopy > srcRange ( srcAnnotation->getTextRange(), uno::UNO_QUERY); + Reference< ::css::text::XTextCopy > range ( ref->getTextRange(), uno::UNO_QUERY); + if(srcRange.is() && range.is()) + range->copyText( srcRange ); + } + + // fix user calls for duplicated slide + SdrObjListIter aSourceIter( &rSrcPage, SdrIterMode::DeepWithGroups ); + SdrObjListIter aTargetIter( this, SdrIterMode::DeepWithGroups ); + + while( aSourceIter.IsMore() && aTargetIter.IsMore() ) + { + SdrObject* pSource = aSourceIter.Next(); + SdrObject* pTarget = aTargetIter.Next(); + + if( pSource->GetUserCall() ) + pTarget->SetUserCall(this); + } +} + +/************************************************************************* +|* +|* Clone +|* +\************************************************************************/ + +rtl::Reference SdPage::CloneSdrPage(SdrModel& rTargetModel) const +{ + SdDrawDocument& rSdDrawDocument(static_cast< SdDrawDocument& >(rTargetModel)); + rtl::Reference pClonedSdPage( + new SdPage( + rSdDrawDocument, + IsMasterPage())); + pClonedSdPage->lateInit(*this); + return pClonedSdPage; +} + +/************************************************************************* +|* +|* GetTextStyleSheetForObject +|* +\************************************************************************/ + +SfxStyleSheet* SdPage::GetTextStyleSheetForObject( SdrObject* pObj ) const +{ + const PresObjKind eKind = GetPresObjKind(pObj); + if( eKind != PresObjKind::NONE ) + { + return GetStyleSheetForPresObj(eKind); + } + + return FmFormPage::GetTextStyleSheetForObject( pObj ); +} + +SfxItemSet* SdPage::getOrCreateItems() +{ + if( mpItems == nullptr ) + mpItems = std::make_unique>( getSdrModelFromSdrPage().GetItemPool()); + + return mpItems.get(); +} + +bool SdPage::setAlienAttributes( const css::uno::Any& rAttributes ) +{ + SfxItemSet* pSet = getOrCreateItems(); + + SvXMLAttrContainerItem aAlienAttributes( SDRATTR_XMLATTRIBUTES ); + if( aAlienAttributes.PutValue( rAttributes, 0 ) ) + { + pSet->Put( aAlienAttributes ); + return true; + } + + return false; +} + +void SdPage::getAlienAttributes( css::uno::Any& rAttributes ) +{ + const SvXMLAttrContainerItem* pItem; + + if( (mpItems == nullptr) || !( pItem = mpItems->GetItemIfSet( SDRATTR_XMLATTRIBUTES, false ) ) ) + { + SvXMLAttrContainerItem aAlienAttributes; + aAlienAttributes.QueryValue( rAttributes ); + } + else + { + pItem->QueryValue( rAttributes ); + } +} + +void SdPage::RemoveEmptyPresentationObjects() +{ + SdrObjListIter aShapeIter( this, SdrIterMode::DeepWithGroups ); + + for (SdrObject* pShape = aShapeIter.Next(); pShape; pShape = aShapeIter.Next()) + { + if (pShape->IsEmptyPresObj()) + { + RemoveObject( pShape->GetOrdNum() ); + SdrObject::Free( pShape ); + } + } +} + +void SdPage::setTransitionType( sal_Int16 nTransitionType ) +{ + mnTransitionType = nTransitionType; + ActionChanged(); +} + +void SdPage::setTransitionSubtype ( sal_Int16 nTransitionSubtype ) +{ + mnTransitionSubtype = nTransitionSubtype; + ActionChanged(); +} + +void SdPage::setTransitionDirection ( bool bTransitionbDirection ) +{ + mbTransitionDirection = bTransitionbDirection; + ActionChanged(); +} + +void SdPage::setTransitionFadeColor ( sal_Int32 nTransitionFadeColor ) +{ + mnTransitionFadeColor = nTransitionFadeColor; + ActionChanged(); +} + +void SdPage::setTransitionDuration ( double fTransitionDuration ) +{ + mfTransitionDuration = fTransitionDuration; + ActionChanged(); +} + +bool SdPage::Equals(const SdPage& rOtherPage) const +{ + if (GetObjCount() != rOtherPage.GetObjCount() || + mePageKind != rOtherPage.mePageKind || + meAutoLayout != rOtherPage.meAutoLayout || + mePresChange != rOtherPage.mePresChange || + !rtl::math::approxEqual(mfTime, rOtherPage.mfTime) || + mbSoundOn != rOtherPage.mbSoundOn || + mbExcluded != rOtherPage.mbExcluded || + maLayoutName != rOtherPage.maLayoutName || + maSoundFile != rOtherPage.maSoundFile || + mbLoopSound != rOtherPage.mbLoopSound || + mbStopSound != rOtherPage.mbStopSound || + maBookmarkName != rOtherPage.maBookmarkName || + mbScaleObjects != rOtherPage.mbScaleObjects || + IsBackgroundFullSize() != rOtherPage.IsBackgroundFullSize() || // ??? + meCharSet != rOtherPage.meCharSet || + mnPaperBin != rOtherPage.mnPaperBin || + mnTransitionType != rOtherPage.mnTransitionType || + mnTransitionSubtype != rOtherPage.mnTransitionSubtype || + mbTransitionDirection != rOtherPage.mbTransitionDirection || + mnTransitionFadeColor != rOtherPage.mnTransitionFadeColor || + !rtl::math::approxEqual(mfTransitionDuration, rOtherPage.mfTransitionDuration)) + return false; + + for(size_t i = 0; i < GetObjCount(); ++i) + if (!GetObj(i)->Equals(*(rOtherPage.GetObj(i)))) + return false; + + return true; + } + +void SdPage::createAnnotation( css::uno::Reference< css::office::XAnnotation >& xAnnotation ) +{ + sd::createAnnotation( xAnnotation, this ); +} + +void SdPage::addAnnotation( const Reference< XAnnotation >& xAnnotation, int nIndex ) +{ + if( (nIndex == -1) || (nIndex > static_cast(maAnnotations.size())) ) + { + maAnnotations.push_back( xAnnotation ); + } + else + { + maAnnotations.insert( maAnnotations.begin() + nIndex, xAnnotation ); + } + + if( getSdrModelFromSdrPage().IsUndoEnabled() ) + { + std::unique_ptr pAction = CreateUndoInsertOrRemoveAnnotation( xAnnotation, true ); + if( pAction ) + getSdrModelFromSdrPage().AddUndo( std::move(pAction) ); + } + + SetChanged(); + getSdrModelFromSdrPage().SetChanged(); + NotifyDocumentEvent( + static_cast< SdDrawDocument& >(getSdrModelFromSdrPage()), + "OnAnnotationInserted", + Reference(xAnnotation, UNO_QUERY)); +} + +void SdPage::removeAnnotation( const Reference< XAnnotation >& xAnnotation ) +{ + if( getSdrModelFromSdrPage().IsUndoEnabled() ) + { + std::unique_ptr pAction = CreateUndoInsertOrRemoveAnnotation( xAnnotation, false ); + if( pAction ) + getSdrModelFromSdrPage().AddUndo( std::move(pAction) ); + } + + AnnotationVector::iterator iter = std::find( maAnnotations.begin(), maAnnotations.end(), xAnnotation ); + if( iter != maAnnotations.end() ) + maAnnotations.erase( iter ); + + getSdrModelFromSdrPage().SetChanged(); + NotifyDocumentEvent( + static_cast< SdDrawDocument& >( getSdrModelFromSdrPage() ), + "OnAnnotationRemoved", + Reference( xAnnotation, UNO_QUERY ) ); +} + +void SdPage::getGraphicsForPrefetch(std::vector& graphics) const +{ + for( size_t i = 0; i < GetObjCount(); ++i) + { + SdrObject* obj = GetObj(i); + if( SdrGrafObj* grafObj = dynamic_cast(obj)) + if(!grafObj->GetGraphic().isAvailable()) + graphics.push_back( const_cast(&grafObj->GetGraphic())); + if( const Graphic* fillGraphic = obj->getFillGraphic()) + if(!fillGraphic->isAvailable()) + graphics.push_back( const_cast(fillGraphic)); + } +} + +void SdPage::dumpAsXml(xmlTextWriterPtr pWriter) const +{ + (void)xmlTextWriterStartElement(pWriter, BAD_CAST("SdPage")); + + const char* pPageKind = nullptr; + switch (mePageKind) + { + case PageKind::Standard: + pPageKind = "PageKind::Standard"; + break; + case PageKind::Notes: + pPageKind = "PageKind::Notes"; + break; + case PageKind::Handout: + pPageKind = "PageKind::Handout"; + break; + } + if (pPageKind) + (void)xmlTextWriterWriteAttribute(pWriter, BAD_CAST("mePageKind"), BAD_CAST(pPageKind)); + + + FmFormPage::dumpAsXml(pWriter); + (void)xmlTextWriterEndElement(pWriter); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/sdpage_animations.cxx b/sd/source/core/sdpage_animations.cxx new file mode 100644 index 000000000..c52938fd8 --- /dev/null +++ b/sd/source/core/sdpage_animations.cxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::sd; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::drawing::XShape; + +/** returns a helper class to manipulate effects inside the main sequence */ +std::shared_ptr< sd::MainSequence > const & SdPage::getMainSequence() +{ + if (nullptr == mpMainSequence) + mpMainSequence = std::make_shared( getAnimationNode() ); + + return mpMainSequence; +} + +/** returns the main animation node */ +Reference< XAnimationNode > const & SdPage::getAnimationNode() +{ + if( !mxAnimationNode.is() ) + { + mxAnimationNode.set( ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ), UNO_QUERY_THROW ); + Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::TIMING_ROOT) } }; + mxAnimationNode->setUserData( aUserData ); + } + + return mxAnimationNode; +} + +void SdPage::setAnimationNode( Reference< XAnimationNode > const & xNode ) +{ + mxAnimationNode = xNode; + if( mpMainSequence ) + mpMainSequence->reset( xNode ); +} + +/** removes all custom animations for the given shape */ +void SdPage::removeAnimations( const SdrObject* pObj ) +{ + if( mxAnimationNode.is() ) + { + getMainSequence(); + + Reference< XShape > xShape( const_cast(pObj)->getUnoShape(), UNO_QUERY ); + + if( mpMainSequence->hasEffect( xShape ) ) + mpMainSequence->disposeShape( xShape ); + } +} + +/** Notify that the object has been renamed and the animation effect has to update. */ +void SdPage::notifyObjectRenamed(const SdrObject* pObj) +{ + if (pObj && hasAnimationNode()) + { + Reference xShape(const_cast(pObj)->getUnoShape(), UNO_QUERY); + + if (xShape.is() && getMainSequence()->hasEffect(xShape)) + getMainSequence()->notify_change(); + } +} + +bool SdPage::hasAnimationNode() const +{ + return mxAnimationNode.is(); +} + +void SdPage::SetFadeEffect(css::presentation::FadeEffect eNewEffect) +{ + EffectMigration::SetFadeEffect( this, eNewEffect ); +} + +FadeEffect SdPage::GetFadeEffect() const +{ + return EffectMigration::GetFadeEffect( this ); +} + +/** callback from the sd::View when a new paragraph for one object on this page is created */ +void SdPage::onParagraphInserted( const ::Outliner* pOutliner, Paragraph const * pPara, SdrObject* pObj ) +{ + if( mxAnimationNode.is() ) + { + ParagraphTarget aTarget; + aTarget.Shape.set( pObj->getUnoShape(), UNO_QUERY ); + /* FIXME: Paragraph should be sal_Int32, though more than 64k + * paragraphs at a shape are unlikely... */ + aTarget.Paragraph = static_cast(pOutliner->GetAbsPos( pPara )); + + getMainSequence()->insertTextRange( Any( aTarget ) ); + } +} + +/** callback from the sd::View when a paragraph from one object on this page is removed */ +void SdPage::onParagraphRemoving( const ::Outliner* pOutliner, Paragraph const * pPara, SdrObject* pObj ) +{ + if( mxAnimationNode.is() ) + { + ParagraphTarget aTarget; + aTarget.Shape.set( pObj->getUnoShape(), UNO_QUERY ); + /* FIXME: Paragraph should be sal_Int32, though more than 64k + * paragraphs at a shape are unlikely... */ + aTarget.Paragraph = static_cast(pOutliner->GetAbsPos( pPara )); + + getMainSequence()->disposeTextRange( Any( aTarget ) ); + } +} + +/** callback from the sd::View when an object just left text edit mode */ +void SdPage::onEndTextEdit( SdrObject* pObj ) +{ + if( pObj && mxAnimationNode.is() ) + { + Reference< XShape > xObj( pObj->getUnoShape(), UNO_QUERY ); + getMainSequence()->onTextChanged( xObj ); + } +} + +void SdPage::cloneAnimations( SdPage& rTargetPage ) const +{ + if( mxAnimationNode.is() ) + { + Reference< XAnimationNode > xClonedNode( + ::sd::Clone( mxAnimationNode, this, &rTargetPage ) ); + + if( xClonedNode.is() ) + rTargetPage.setAnimationNode( xClonedNode ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/shapelist.cxx b/sd/source/core/shapelist.cxx new file mode 100644 index 000000000..613286c9b --- /dev/null +++ b/sd/source/core/shapelist.cxx @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include + +using namespace sd; + +ShapeList::ShapeList() +{ + maIter = maShapeList.end(); +} + +ShapeList::~ShapeList() +{ + clear(); +} + +/** adds the given shape to this list */ +void ShapeList::addShape( SdrObject& rObject ) +{ + ListImpl::iterator aIter( std::find( maShapeList.begin(), maShapeList.end(), &rObject ) ); + if( aIter == maShapeList.end() ) + { + maShapeList.push_back(&rObject); + rObject.AddObjectUser( *this ); + } + else + { + OSL_FAIL("sd::ShapeList::addShape(), given shape already part of list!"); + } +} + +/** removes the given shape from this list */ +void ShapeList::removeShape( SdrObject& rObject ) +{ + ListImpl::iterator aIter( std::find( maShapeList.begin(), maShapeList.end(), &rObject ) ); + if( aIter != maShapeList.end() ) + { + bool bIterErased = aIter == maIter; + + (*aIter)->RemoveObjectUser(*this); + aIter = maShapeList.erase( aIter ); + + if( bIterErased ) + maIter = aIter; + } + else + { + OSL_FAIL("sd::ShapeList::removeShape(), given shape not part of list!"); + } +} + +/** removes all shapes from this list + NOTE: iterators will become invalid */ +void ShapeList::clear() +{ + ListImpl aShapeList; + aShapeList.swap( maShapeList ); + + for( auto& rpShape : aShapeList ) + rpShape->RemoveObjectUser(*this); + + maIter = maShapeList.end(); +} + +/** returns true if this list is empty */ +bool ShapeList::isEmpty() const +{ + return maShapeList.empty(); +} + +/** returns true if given shape is part of this list */ +bool ShapeList::hasShape( SdrObject& rObject ) const +{ + return std::find( maShapeList.begin(), maShapeList.end(), &rObject ) != maShapeList.end(); +} + +void ShapeList::ObjectInDestruction(const SdrObject& rObject) +{ + ListImpl::iterator aIter( std::find( maShapeList.begin(), maShapeList.end(), &rObject ) ); + if( aIter != maShapeList.end() ) + { + bool bIterErased = aIter == maIter; + + aIter = maShapeList.erase( aIter ); + + if( bIterErased ) + maIter = aIter; + } + else + { + OSL_FAIL("sd::ShapeList::ObjectInDestruction(), got a call from an unknown friend!"); + } +} + +SdrObject* ShapeList::getNextShape() +{ + if( maIter != maShapeList.end() ) + { + return (*maIter++); + } + else + { + return nullptr; + } +} + +void ShapeList::seekShape( sal_uInt32 nIndex ) +{ + maIter = maShapeList.begin(); + nIndex = std::min(nIndex, static_cast(maShapeList.size())); + std::advance(maIter, nIndex); +} + +bool ShapeList::hasMore() const +{ + return maIter != maShapeList.end(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/stlfamily.cxx b/sd/source/core/stlfamily.cxx new file mode 100644 index 000000000..a396bd170 --- /dev/null +++ b/sd/source/core/stlfamily.cxx @@ -0,0 +1,513 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::beans; + +typedef std::map< OUString, rtl::Reference< SdStyleSheet > > PresStyleMap; + +struct SdStyleFamilyImpl +{ + unotools::WeakReference mxMasterPage; + OUString maLayoutName; + + PresStyleMap& getStyleSheets(); + rtl::Reference< SfxStyleSheetPool > mxPool; + +private: + PresStyleMap maStyleSheets; +}; + +PresStyleMap& SdStyleFamilyImpl::getStyleSheets() +{ + auto pMasterPage = mxMasterPage.get(); + if (!pMasterPage) + return maStyleSheets; + + if (pMasterPage->GetLayoutName() != maLayoutName ) + { + maLayoutName = pMasterPage->GetLayoutName(); + + OUString aLayoutName( maLayoutName ); + const sal_Int32 nLen = aLayoutName.indexOf(SD_LT_SEPARATOR ) + 4; + aLayoutName = aLayoutName.copy(0, nLen ); + + if( (maStyleSheets.empty()) || !(*maStyleSheets.begin()).second->GetName().startsWith( aLayoutName) ) + { + maStyleSheets.clear(); + + // The iterator will return only style sheets of family master page + std::shared_ptr aSSSIterator = std::make_shared(mxPool.get(), SfxStyleFamily::Page); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + if (pSdStyle->GetName().startsWith(aLayoutName)) + { + maStyleSheets[ pSdStyle->GetApiName() ].set( pSdStyle ); + } + } + } + } + + return maStyleSheets; +} + +SdStyleFamily::SdStyleFamily( const rtl::Reference< SfxStyleSheetPool >& xPool, SfxStyleFamily nFamily ) +: mnFamily( nFamily ) +, mxPool( xPool ) +{ +} + +SdStyleFamily::SdStyleFamily( const rtl::Reference< SfxStyleSheetPool >& xPool, const SdPage* pMasterPage ) +: mnFamily( SfxStyleFamily::Page ) +, mxPool( xPool ) +, mpImpl( new SdStyleFamilyImpl ) +{ + mpImpl->mxMasterPage = const_cast< SdPage* >( pMasterPage ); + mpImpl->mxPool = xPool; +} + +SdStyleFamily::~SdStyleFamily() +{ + DBG_ASSERT( !mxPool.is(), "SdStyleFamily::~SdStyleFamily(), dispose me first!" ); +} + +void SdStyleFamily::throwIfDisposed() const +{ + if( !mxPool.is() ) + throw DisposedException(); +} + +SdStyleSheet* SdStyleFamily::GetValidNewSheet( const Any& rElement ) +{ + Reference< XStyle > xStyle( rElement, UNO_QUERY ); + SdStyleSheet* pStyle = static_cast< SdStyleSheet* >( xStyle.get() ); + + if( pStyle == nullptr || (pStyle->GetFamily() != mnFamily) || (pStyle->GetPool() != mxPool.get()) || (mxPool->Find( pStyle->GetName(), mnFamily) != nullptr) ) + throw IllegalArgumentException(); + + return pStyle; +} + +SdStyleSheet* SdStyleFamily::GetSheetByName( const OUString& rName ) +{ + SdStyleSheet* pRet = nullptr; + if( !rName.isEmpty() ) + { + if( mnFamily == SfxStyleFamily::Page ) + { + PresStyleMap& rStyleMap = mpImpl->getStyleSheets(); + PresStyleMap::iterator iter( rStyleMap.find(rName) ); + if( iter != rStyleMap.end() ) + pRet = (*iter).second.get(); + } + else + { + std::shared_ptr aSSSIterator = std::make_shared(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + if (pSdStyle->GetApiName() == rName) + { + pRet = pSdStyle; + break; + } + } + } + } + if( pRet ) + return pRet; + + throw NoSuchElementException(); +} + +// XServiceInfo +OUString SAL_CALL SdStyleFamily::getImplementationName() +{ + return "SdStyleFamily"; +} + +sal_Bool SAL_CALL SdStyleFamily::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdStyleFamily::getSupportedServiceNames() +{ + return { "com.sun.star.style.StyleFamily" }; +} + +// XNamed +OUString SAL_CALL SdStyleFamily::getName() +{ + if( mnFamily == SfxStyleFamily::Page ) + { + rtl::Reference pPage = mpImpl->mxMasterPage.get(); + if( pPage == nullptr ) + throw DisposedException(); + + OUString aLayoutName( pPage->GetLayoutName() ); + sal_Int32 nIndex = aLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + aLayoutName = aLayoutName.copy(0, nIndex); + + return aLayoutName; + } + else + { + return SdStyleSheet::GetFamilyString( mnFamily ); + } +} + +void SAL_CALL SdStyleFamily::setName( const OUString& ) +{ +} + +// XNameAccess + +Any SAL_CALL SdStyleFamily::getByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + return Any( Reference< XStyle >( static_cast(GetSheetByName( rName )) ) ); +} + +Sequence< OUString > SAL_CALL SdStyleFamily::getElementNames() +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + if( mnFamily == SfxStyleFamily::Page ) + { + PresStyleMap& rStyleMap = mpImpl->getStyleSheets(); + Sequence< OUString > aNames( rStyleMap.size() ); + + OUString* pNames = aNames.getArray(); + for( const auto& rEntry : rStyleMap ) + { + rtl::Reference< SdStyleSheet > xStyle( rEntry.second ); + if( xStyle.is() ) + { + *pNames++ = xStyle->GetApiName(); + } + } + + return aNames; + } + else + { + std::vector< OUString > aNames; + std::shared_ptr aSSSIterator = std::make_shared(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + aNames.push_back(pSdStyle->GetApiName()); + } + return Sequence< OUString >( &(*aNames.begin()), aNames.size() ); + } +} + +sal_Bool SAL_CALL SdStyleFamily::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( !aName.isEmpty() ) + { + if( mnFamily == SfxStyleFamily::Page ) + { + PresStyleMap& rStyleSheets = mpImpl->getStyleSheets(); + PresStyleMap::iterator iter( rStyleSheets.find(aName) ); + return iter != rStyleSheets.end(); + } + else + { + std::shared_ptr aSSSIterator = std::make_shared(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + if (pSdStyle->GetApiName() == aName) + { + return true; + } + } + } + } + + return false; +} + +// XElementAccess + +Type SAL_CALL SdStyleFamily::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdStyleFamily::hasElements() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( mnFamily == SfxStyleFamily::Page ) + { + return true; + } + else + { + std::shared_ptr aSSSIterator = std::make_shared(mxPool.get(), mnFamily); + if (aSSSIterator->First()) + { + return true; + } + } + + return false; +} + +// XIndexAccess + +sal_Int32 SAL_CALL SdStyleFamily::getCount() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + sal_Int32 nCount = 0; + if( mnFamily == SfxStyleFamily::Page ) + { + return mpImpl->getStyleSheets().size(); + } + else + { + std::shared_ptr aSSSIterator = std::make_shared(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + nCount++; + } + } + + return nCount; +} + +Any SAL_CALL SdStyleFamily::getByIndex( sal_Int32 Index ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( Index >= 0 ) + { + if( mnFamily == SfxStyleFamily::Page ) + { + PresStyleMap& rStyleSheets = mpImpl->getStyleSheets(); + if( Index < static_cast(rStyleSheets.size()) ) + { + PresStyleMap::iterator iter( rStyleSheets.begin() ); + std::advance(iter, Index); + return Any( Reference< XStyle >( (*iter).second ) ); + } + } + else + { + std::shared_ptr aSSSIterator = std::make_shared(mxPool.get(), mnFamily); + for ( SfxStyleSheetBase* pStyle = aSSSIterator->First(); pStyle; + pStyle = aSSSIterator->Next() ) + { + // we assume that we have only SdStyleSheets + SdStyleSheet* pSdStyle = static_cast< SdStyleSheet* >( pStyle ); + if( Index-- == 0 ) + { + return Any( Reference< XStyle >( pSdStyle ) ); + } + } + } + } + + throw IndexOutOfBoundsException(); +} + +// XNameContainer + +void SAL_CALL SdStyleFamily::insertByName( const OUString& rName, const Any& rElement ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if(rName.isEmpty()) + throw IllegalArgumentException(); + + SdStyleSheet* pStyle = GetValidNewSheet( rElement ); + if( !pStyle->SetName( rName ) ) + throw ElementExistException(); + + pStyle->SetApiName( rName ); + mxPool->Insert( pStyle ); +} + +void SAL_CALL SdStyleFamily::removeByName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + SdStyleSheet* pStyle = GetSheetByName( rName ); + + if( !pStyle->IsUserDefined() ) + throw WrappedTargetException(); + + mxPool->Remove( pStyle ); +} + +// XNameReplace + +void SAL_CALL SdStyleFamily::replaceByName( const OUString& rName, const Any& aElement ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + SdStyleSheet* pOldStyle = GetSheetByName( rName ); + SdStyleSheet* pNewStyle = GetValidNewSheet( aElement ); + + mxPool->Remove( pOldStyle ); + mxPool->Insert( pNewStyle ); +} + +// XSingleServiceFactory + +Reference< XInterface > SAL_CALL SdStyleFamily::createInstance() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( mnFamily == SfxStyleFamily::Page ) + { + throw IllegalAccessException(); + } + return Reference( + static_cast(SdStyleSheet::CreateEmptyUserStyle(*mxPool, mnFamily).get())); +} + +Reference< XInterface > SAL_CALL SdStyleFamily::createInstanceWithArguments( const Sequence< Any >& ) +{ + return createInstance(); +} + +// XComponent + +void SAL_CALL SdStyleFamily::dispose( ) +{ + if( mxPool.is() ) + mxPool.clear(); + + mpImpl.reset(); +} + +void SAL_CALL SdStyleFamily::addEventListener( const Reference< XEventListener >& ) +{ +} + +void SAL_CALL SdStyleFamily::removeEventListener( const Reference< XEventListener >& ) +{ +} + +// XPropertySet + +Reference SdStyleFamily::getPropertySetInfo() +{ + OSL_FAIL( "###unexpected!" ); + return Reference(); +} + +void SdStyleFamily::setPropertyValue( const OUString& , const Any& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +Any SdStyleFamily::getPropertyValue( const OUString& PropertyName ) +{ + if ( PropertyName != "DisplayName" ) + { + throw UnknownPropertyException( "unknown property: " + PropertyName, static_cast(this) ); + } + + SolarMutexGuard aGuard; + OUString sDisplayName; + switch( mnFamily ) + { + case SfxStyleFamily::Page: sDisplayName = getName(); break; + case SfxStyleFamily::Frame: sDisplayName = SdResId(STR_CELL_STYLE_FAMILY); break; + default: sDisplayName = SdResId(STR_GRAPHICS_STYLE_FAMILY); break; + } + return Any( sDisplayName ); +} + +void SdStyleFamily::addPropertyChangeListener( const OUString& , const Reference& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SdStyleFamily::removePropertyChangeListener( const OUString& , const Reference& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SdStyleFamily::addVetoableChangeListener( const OUString& , const Reference& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +void SdStyleFamily::removeVetoableChangeListener( const OUString& , const Reference& ) +{ + OSL_FAIL( "###unexpected!" ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/stlpool.cxx b/sd/source/core/stlpool.cxx new file mode 100644 index 000000000..12891698a --- /dev/null +++ b/sd/source/core/stlpool.cxx @@ -0,0 +1,1395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::container; + +namespace +{ + +OUString lcl_findRenamedStyleName(std::vector< std::pair< OUString, OUString > > &rRenamedList, OUString const & aOriginalName ) +{ + auto aIter = std::find_if(rRenamedList.begin(), rRenamedList.end(), + [&aOriginalName](const std::pair& rItem) { return rItem.first == aOriginalName; }); + if (aIter != rRenamedList.end()) + return (*aIter).second; + return OUString(); +} + +SfxStyleSheet *lcl_findStyle(StyleSheetCopyResultVector& rStyles, std::u16string_view aStyleName) +{ + if( aStyleName.empty() ) + return nullptr; + for (const auto& a : rStyles) + { + if (a.m_xStyleSheet->GetName() == aStyleName) + return a.m_xStyleSheet.get(); + } + return nullptr; +} + +} + +SdStyleSheetPool::SdStyleSheetPool(SfxItemPool const& _rPool, SdDrawDocument* pDocument) +: SdStyleSheetPoolBase( _rPool ) +, mpActualStyleSheet(nullptr) +, mpDoc(pDocument) +{ + if( !mpDoc ) + return; + + rtl::Reference< SfxStyleSheetPool > xPool( this ); + + // create graphics family + mxGraphicFamily = new SdStyleFamily( xPool, SfxStyleFamily::Para ); + mxCellFamily = new SdStyleFamily( xPool, SfxStyleFamily::Frame ); + + mxTableFamily = sdr::table::CreateTableDesignFamily(); + Reference< XNamed > xNamed( mxTableFamily, UNO_QUERY ); + if( xNamed.is() ) + msTableFamilyName = xNamed->getName(); + + // create presentation families, one for each master page + const sal_uInt16 nCount = mpDoc->GetMasterSdPageCount(PageKind::Standard); + for( sal_uInt16 nPage = 0; nPage < nCount; ++nPage ) + AddStyleFamily( mpDoc->GetMasterSdPage(nPage,PageKind::Standard) ); +} + +SdStyleSheetPool::~SdStyleSheetPool() +{ + DBG_ASSERT( mpDoc == nullptr, "sd::SdStyleSheetPool::~SdStyleSheetPool(), dispose me first!" ); +} + +rtl::Reference SdStyleSheetPool::Create(const OUString& rName, SfxStyleFamily eFamily, SfxStyleSearchBits _nMask ) +{ + return new SdStyleSheet(rName, *this, eFamily, _nMask); +} + +SfxStyleSheetBase* SdStyleSheetPool::GetTitleSheet(std::u16string_view rLayoutName) +{ + OUString aName = OUString::Concat(rLayoutName) + SD_LT_SEPARATOR + STR_LAYOUT_TITLE; + SfxStyleSheetBase* pResult = Find(aName, SfxStyleFamily::Page); + return pResult; +} + +/************************************************************************* +|* +|* Create a list of outline text templates for a presentation layout. +|* The caller has to delete the list. +|* +\************************************************************************/ + +void SdStyleSheetPool::CreateOutlineSheetList (std::u16string_view rLayoutName, std::vector &rOutlineStyles) +{ + OUString aName = OUString::Concat(rLayoutName) + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + for (sal_Int32 nSheet = 1; nSheet < 10; nSheet++) + { + OUString aFullName(aName + " " + OUString::number( nSheet ) ); + SfxStyleSheetBase* pSheet = Find(aFullName, SfxStyleFamily::Page); + + if (pSheet) + rOutlineStyles.push_back(pSheet); + } +} + +/************************************************************************* +|* +|* Create style sheets with default values for the named presentation layout +|* +\************************************************************************/ + +void SdStyleSheetPool::CreateLayoutStyleSheets(std::u16string_view rLayoutName, bool bCheck /*= sal_False*/ ) +{ + const SfxStyleSearchBits nUsedMask = SfxStyleSearchBits::All & ~SfxStyleSearchBits::UserDefined; + + bool bCreated = false; + + SfxStyleSheetBase* pSheet = nullptr; + + OUString aPrefix(OUString::Concat(rLayoutName) + SD_LT_SEPARATOR); + + vcl::Font aLatinFont, aCJKFont, aCTLFont; + + mpDoc->getDefaultFonts( aLatinFont, aCJKFont, aCTLFont ); + + // Font for title and outline + SvxFontItem aSvxFontItem( aLatinFont.GetFamilyType(), aLatinFont.GetFamilyName(), aLatinFont.GetStyleName(), aLatinFont.GetPitch(), + aLatinFont.GetCharSet(), EE_CHAR_FONTINFO ); + + SvxFontItem aSvxFontItemCJK( aCJKFont.GetFamilyType(), aCJKFont.GetFamilyName(), aCJKFont.GetStyleName(), aCJKFont.GetPitch(), + aCJKFont.GetCharSet(), EE_CHAR_FONTINFO_CJK ); + + SvxFontItem aSvxFontItemCTL( aCTLFont.GetFamilyType(), aCTLFont.GetFamilyName(), aCTLFont.GetStyleName(), aCTLFont.GetPitch(), + aCTLFont.GetCharSet(), EE_CHAR_FONTINFO_CTL ); + + vcl::Font aBulletFont( GetBulletFont() ); + + /************************************************************************** + * outline levels + **************************************************************************/ + OUString aName(STR_LAYOUT_OUTLINE); + const OUString aHelpFile; + + SvxLRSpaceItem aSvxLRSpaceItem( EE_PARA_LRSPACE ); + SvxULSpaceItem aSvxULSpaceItem( EE_PARA_ULSPACE ); + + for( sal_Int32 nLevel = 1; nLevel < 10; nLevel++) + { + OUString aLevelName( aPrefix + aName + " " + OUString::number( nLevel ) ) ; + + if (!Find(aLevelName, SfxStyleFamily::Page)) + { + bCreated = true; + pSheet = &Make(aLevelName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_OUTLINE + nLevel ); + + pSheet->SetParent( OUString() ); + + // attributing for level 1, the others levels inherit + if (nLevel == 1) + { + SfxItemSet& rSet = pSheet->GetItemSet(); + + rSet.Put(aSvxFontItem); + rSet.Put(aSvxFontItemCJK); + rSet.Put(aSvxFontItemCTL); + rSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + rSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + rSet.Put( SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE ) ); + rSet.Put( SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE ) ); + rSet.Put( SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT ) ); + rSet.Put( SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP ) ); + rSet.Put( SvxShadowedItem(false, EE_CHAR_SHADOW ) ); + rSet.Put( SvxContourItem(false, EE_CHAR_OUTLINE ) ); + rSet.Put( SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK ) ); + rSet.Put( SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF) ); + rSet.Put( SvxColorItem( COL_AUTO, EE_CHAR_COLOR) ); + rSet.Put( SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR ) ); + rSet.Put( XLineStyleItem(css::drawing::LineStyle_NONE) ); + rSet.Put( XFillStyleItem(drawing::FillStyle_NONE) ); + rSet.Put( SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_AUTOFIT) ); + rSet.Put( makeSdrTextAutoGrowHeightItem(false) ); + // #i16874# enable kerning by default but only for new documents + rSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + + vcl::Font f( GetBulletFont() ); + PutNumBulletItem( pSheet, f ); + } + + sal_uLong nFontSize = 20; + sal_uInt16 nUpper = 100; + + switch (nLevel) + { + case 1: + { + nFontSize = 32; + nUpper = 500; + } + break; + + case 2: + { + nFontSize = 28; + nUpper = 400; + } + break; + + case 3: + { + nFontSize = 24; + nUpper = 300; + } + break; + + case 4: + { + nUpper = 200; + } + break; + } + + // FontSize + nFontSize = static_cast(convertPointToMm100(nFontSize)); + SfxItemSet& rOutlineSet = pSheet->GetItemSet(); + rOutlineSet.Put( SvxFontHeightItem( nFontSize, 100, EE_CHAR_FONTHEIGHT ) ); + rOutlineSet.Put( SvxFontHeightItem( nFontSize, 100, EE_CHAR_FONTHEIGHT_CJK ) ); + rOutlineSet.Put( SvxFontHeightItem( SdDrawDocument::convertFontHeightToCTL( nFontSize ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); + + // Line distance (upwards). Stuff around here cleaned up in i35937 + aSvxULSpaceItem.SetUpper(nUpper); + pSheet->GetItemSet().Put(aSvxULSpaceItem); + } + } + + // if we created outline styles, we need to chain them + if( bCreated ) + { + SfxStyleSheetBase* pParent = nullptr; + for (sal_Int32 nLevel = 1; nLevel < 10; nLevel++) + { + OUString aLevelName( aPrefix + aName + " " + OUString::number( nLevel ) ); + + pSheet = Find(aLevelName, SfxStyleFamily::Page); + + DBG_ASSERT( pSheet, "missing layout style!"); + + if( pSheet ) + { + if (pParent) + pSheet->SetParent(pParent->GetName()); + pParent = pSheet; + } + } + } + + /************************************************************************** + * Title + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_TITLE; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_TITLE ); + pSheet->SetParent( OUString() ); + SfxItemSet& rTitleSet = pSheet->GetItemSet(); + rTitleSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + rTitleSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + rTitleSet.Put(aSvxFontItem); + rTitleSet.Put(aSvxFontItemCJK); + rTitleSet.Put(aSvxFontItemCTL); + rTitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rTitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rTitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + rTitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rTitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rTitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + rTitleSet.Put(SvxFontHeightItem( 1552, 100, EE_CHAR_FONTHEIGHT ) ); // 44 pt + rTitleSet.Put(SvxFontHeightItem( 1552, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 44 pt + rTitleSet.Put(SvxFontHeightItem( SdDrawDocument::convertFontHeightToCTL( 1552 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 44 pt + rTitleSet.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE )); + rTitleSet.Put(SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE )); + rTitleSet.Put(SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT )); + rTitleSet.Put(SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP )); + rTitleSet.Put(SvxShadowedItem(false, EE_CHAR_SHADOW )); + rTitleSet.Put(SvxContourItem(false, EE_CHAR_OUTLINE )); + rTitleSet.Put( SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK ) ); + rTitleSet.Put( SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF ) ); + rTitleSet.Put(SvxColorItem( COL_AUTO, EE_CHAR_COLOR )); + rTitleSet.Put(SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR )); + rTitleSet.Put(SvxAdjustItem(SvxAdjust::Center, EE_PARA_JUST )); + rTitleSet.Put( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + // #i16874# enable kerning by default but only for new documents + rTitleSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + + aBulletFont.SetFontSize(Size(0,1552)); // 44 pt + PutNumBulletItem( pSheet, aBulletFont ); + } + + /************************************************************************** + * Subtitle + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_SUBTITLE; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_SUBTITLE ); + pSheet->SetParent( OUString() ); + SfxItemSet& rSubtitleSet = pSheet->GetItemSet(); + rSubtitleSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + rSubtitleSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + rSubtitleSet.Put(aSvxFontItem); + rSubtitleSet.Put(aSvxFontItemCJK); + rSubtitleSet.Put(aSvxFontItemCTL); + rSubtitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rSubtitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rSubtitleSet.Put(SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + rSubtitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rSubtitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rSubtitleSet.Put(SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + rSubtitleSet.Put( SvxFontHeightItem( 1129, 100, EE_CHAR_FONTHEIGHT ) ); // 32 pt + rSubtitleSet.Put( SvxFontHeightItem( 1129, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 32 pt + rSubtitleSet.Put( SvxFontHeightItem( SdDrawDocument::convertFontHeightToCTL( 1129 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 32 pt + rSubtitleSet.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE )); + rSubtitleSet.Put(SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE )); + rSubtitleSet.Put(SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT )); + rSubtitleSet.Put(SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP )); + rSubtitleSet.Put(SvxShadowedItem(false, EE_CHAR_SHADOW )); + rSubtitleSet.Put(SvxContourItem(false, EE_CHAR_OUTLINE )); + rSubtitleSet.Put( SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK ) ); + rSubtitleSet.Put( SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF ) ); + rSubtitleSet.Put(SvxColorItem( COL_AUTO, EE_CHAR_COLOR )); + rSubtitleSet.Put(SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR )); + rSubtitleSet.Put(SvxAdjustItem(SvxAdjust::Center, EE_PARA_JUST )); + rSubtitleSet.Put( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + // #i16874# enable kerning by default but only for new documents + rSubtitleSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + aSvxLRSpaceItem.SetTextLeft(0); + rSubtitleSet.Put(aSvxLRSpaceItem); + + vcl::Font aTmpFont( GetBulletFont() ); + aTmpFont.SetFontSize(Size(0, 1129)); // 32 pt + PutNumBulletItem( pSheet, aTmpFont ); + } + + /************************************************************************** + * Notes + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_NOTES; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_NOTES ); + pSheet->SetParent( OUString() ); + SfxItemSet& rNotesSet = pSheet->GetItemSet(); + rNotesSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + rNotesSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + rNotesSet.Put(aSvxFontItem); + rNotesSet.Put(aSvxFontItemCJK); + rNotesSet.Put(aSvxFontItemCTL); + rNotesSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC ) ); + rNotesSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rNotesSet.Put( SvxPostureItem( ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + rNotesSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rNotesSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rNotesSet.Put( SvxWeightItem( WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + rNotesSet.Put( SvxFontHeightItem( 705, 100, EE_CHAR_FONTHEIGHT ) ); // 20 pt + rNotesSet.Put( SvxFontHeightItem( 705, 100, EE_CHAR_FONTHEIGHT_CJK ) ); // 20 pt + rNotesSet.Put( SvxFontHeightItem( SdDrawDocument::convertFontHeightToCTL( 705 ), 100, EE_CHAR_FONTHEIGHT_CTL ) ); // 20 pt + rNotesSet.Put( SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE ) ); + rNotesSet.Put( SvxOverlineItem(LINESTYLE_NONE, EE_CHAR_OVERLINE ) ); + rNotesSet.Put( SvxCrossedOutItem(STRIKEOUT_NONE, EE_CHAR_STRIKEOUT ) ); + rNotesSet.Put( SvxCaseMapItem(SvxCaseMap::NotMapped, EE_CHAR_CASEMAP ) ); + rNotesSet.Put( SvxShadowedItem(false, EE_CHAR_SHADOW ) ); + rNotesSet.Put( SvxContourItem(false, EE_CHAR_OUTLINE ) ); + rNotesSet.Put( SvxEmphasisMarkItem(FontEmphasisMark::NONE, EE_CHAR_EMPHASISMARK ) ); + rNotesSet.Put( SvxCharReliefItem(FontRelief::NONE, EE_CHAR_RELIEF) ); + rNotesSet.Put( SvxColorItem( COL_AUTO, EE_CHAR_COLOR ) ); + rNotesSet.Put( SvxColorItem( COL_AUTO, EE_CHAR_BKGCOLOR ) ); + rNotesSet.Put( SvxLRSpaceItem( 0, 0, 600, -600, EE_PARA_LRSPACE ) ); + // #i16874# enable kerning by default but only for new documents + rNotesSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + +/* #i35937# */ + + } + + /************************************************************************** + * Background objects + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_BACKGROUNDOBJECTS; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_BACKGROUNDOBJECTS ); + pSheet->SetParent( OUString() ); + SfxItemSet& rBackgroundObjectsSet = pSheet->GetItemSet(); + rBackgroundObjectsSet.Put(makeSdrShadowItem(false)); + rBackgroundObjectsSet.Put(makeSdrShadowColorItem(COL_GRAY)); + rBackgroundObjectsSet.Put(makeSdrShadowXDistItem(200)); // 3 mm shadow distance + rBackgroundObjectsSet.Put(makeSdrShadowYDistItem(200)); + // #i16874# enable kerning by default but only for new documents + rBackgroundObjectsSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + rBackgroundObjectsSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_BLOCK)); + } + + /************************************************************************** + * Background + **************************************************************************/ + aName = aPrefix + STR_LAYOUT_BACKGROUND; + + if (!Find(aName, SfxStyleFamily::Page)) + { + bCreated = true; + + pSheet = &Make(aName, SfxStyleFamily::Page,nUsedMask); + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_BACKGROUND ); + pSheet->SetParent( OUString() ); + SfxItemSet& rBackgroundSet = pSheet->GetItemSet(); + rBackgroundSet.Put(XLineStyleItem(drawing::LineStyle_NONE)); + rBackgroundSet.Put(XFillStyleItem(drawing::FillStyle_NONE)); + // #i16874# enable kerning by default but only for new documents + rBackgroundSet.Put( SvxAutoKernItem( true, EE_CHAR_PAIRKERNING ) ); + } + + DBG_ASSERT( !bCheck || !bCreated, "missing layout style sheets detected!" ); +} + +/************************************************************************* +|* +|* Copy graphic style sheets from source pool into this pool +|* +|* (rSourcePool can not be const since SfxStyleSheetPoolBase::Find isn't const) +|* +\************************************************************************/ + +void SdStyleSheetPool::CopyGraphicSheets(SdStyleSheetPool& rSourcePool) +{ + CopySheets( rSourcePool, SfxStyleFamily::Para ); +} + +void SdStyleSheetPool::CopyCellSheets(SdStyleSheetPool& rSourcePool) +{ + CopySheets( rSourcePool, SfxStyleFamily::Frame ); +} + +void SdStyleSheetPool::CopyTableStyles(SdStyleSheetPool const & rSourcePool) +{ + Reference< XIndexAccess > xSource( rSourcePool.mxTableFamily, UNO_QUERY ); + Reference< XNameContainer > xTarget( mxTableFamily, UNO_QUERY ); + Reference< XSingleServiceFactory > xFactory( mxTableFamily, UNO_QUERY ); + + if( !(xSource.is() && xFactory.is() && mxTableFamily.is()) ) + return; + + for( sal_Int32 nIndex = 0; nIndex < xSource->getCount(); nIndex++ ) try + { + Reference< XStyle > xSourceTableStyle( xSource->getByIndex( nIndex ), UNO_QUERY ); + if( xSourceTableStyle.is() ) + { + Reference< XStyle > xNewTableStyle( xFactory->createInstance(), UNO_QUERY ); + if( xNewTableStyle.is() ) + { + Reference< XNameAccess> xSourceNames( xSourceTableStyle, UNO_QUERY_THROW ); + + const Sequence< OUString > aStyleNames( xSourceNames->getElementNames() ); + + Reference< XNameReplace > xTargetNames( xNewTableStyle, UNO_QUERY ); + + for( const OUString& aName : aStyleNames ) + { + Reference< XStyle > xSourceStyle( xSourceNames->getByName( aName ), UNO_QUERY ); + Reference< XStyle > xTargetStyle; + if( xSourceStyle.is() ) try + { + mxCellFamily->getByName( xSourceStyle->getName() ) >>= xTargetStyle; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdStyleSheetPool::CopyTableStyles()" ); + } + + if( xTargetStyle.is() ) + xTargetNames->replaceByName( aName, Any( xTargetStyle ) ); + } + } + + OUString sName( xSourceTableStyle->getName() ); + if( xTarget->hasByName( sName ) ) + xTarget->replaceByName( sName, Any( xNewTableStyle ) ); + else + xTarget->insertByName( sName, Any( xNewTableStyle ) ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdStyleSheetPool::CopyTableStyles()"); + } +} + +void SdStyleSheetPool::CopyCellSheets(SdStyleSheetPool& rSourcePool, StyleSheetCopyResultVector& rCreatedSheets) +{ + CopySheets( rSourcePool, SfxStyleFamily::Frame, rCreatedSheets ); +} + +void SdStyleSheetPool::RenameAndCopyGraphicSheets(SdStyleSheetPool& rSourcePool, StyleSheetCopyResultVector& rCreatedSheets, std::u16string_view rRenameSuffix) +{ + CopySheets( rSourcePool, SfxStyleFamily::Para, rCreatedSheets, rRenameSuffix ); +} + +void SdStyleSheetPool::CopySheets(SdStyleSheetPool& rSourcePool, SfxStyleFamily eFamily ) +{ + StyleSheetCopyResultVector aTmpSheets; + CopySheets(rSourcePool, eFamily, aTmpSheets); +} + +void SdStyleSheetPool::CopySheets(SdStyleSheetPool& rSourcePool, SfxStyleFamily eFamily, StyleSheetCopyResultVector& rCreatedSheets) +{ + CopySheets(rSourcePool, eFamily, rCreatedSheets, u""); +} + +namespace +{ + +struct HasFamilyPredicate : svl::StyleSheetPredicate +{ + explicit HasFamilyPredicate(SfxStyleFamily eFamily) + : meFamily(eFamily) {} + + bool Check(const SfxStyleSheetBase& sheet) override + { + return sheet.GetFamily() == meFamily; + } + SfxStyleFamily meFamily; +}; + +} + +void SdStyleSheetPool::CopySheets(SdStyleSheetPool& rSourcePool, SfxStyleFamily eFamily, StyleSheetCopyResultVector& rCreatedSheets, std::u16string_view rRenameSuffix) +{ + std::vector< std::pair< rtl::Reference< SfxStyleSheetBase >, OUString > > aNewStyles; + std::vector< std::pair< OUString, OUString > > aRenamedList; + + // find all style sheets of the source pool which have the same family + HasFamilyPredicate aHasFamilyPredicate(eFamily); + std::vector aSheetsWithFamily = rSourcePool.GetIndexedStyleSheets().FindPositionsByPredicate(aHasFamilyPredicate); + + for (const auto& rPos : aSheetsWithFamily) + { + SfxStyleSheetBase* pSheet = rSourcePool.GetStyleSheetByPositionInIndex( rPos ); + if( !pSheet ) + continue; + OUString aName( pSheet->GetName() ); + + // now check whether we already have a sheet with the same name + std::vector aSheetsWithName = GetIndexedStyleSheets().FindPositionsByName(aName); + bool bAddToList = false; + SfxStyleSheetBase * pExistingSheet = nullptr; + if (!aSheetsWithName.empty()) + { + // if we have a rename suffix, try to find a new name + pExistingSheet = + GetStyleSheetByPositionInIndex(aSheetsWithName.front()); + if (!rRenameSuffix.empty() && + !pExistingSheet->GetItemSet().Equals(pSheet->GetItemSet(), false)) + { + // we have found a sheet with the same name, but different contents. Try to find a new name. + // If we already have a sheet with the new name, and it is equal to the one in the source pool, + // do nothing. + OUString aTmpName = aName + rRenameSuffix; + sal_Int32 nSuffix = 1; + do + { + aTmpName = aName + rRenameSuffix + OUString::number(nSuffix); + pExistingSheet = Find(aTmpName, eFamily); + nSuffix++; + } while (pExistingSheet && + !pExistingSheet->GetItemSet().Equals(pSheet->GetItemSet(), false)); + aName = aTmpName; + bAddToList = true; + } + } + // we do not already have a sheet with the same name and contents. Create a new one. + if (!pExistingSheet) + { + assert(!Find(aName, eFamily)); + rtl::Reference< SfxStyleSheetBase > xNewSheet( &Make( aName, eFamily ) ); + + xNewSheet->SetMask( pSheet->GetMask() ); + + // Also set parent relation for copied style sheets + OUString aParent( pSheet->GetParent() ); + if( !aParent.isEmpty() ) + aNewStyles.emplace_back( xNewSheet, aParent ); + + if( !bAddToList ) + { + OUString aHelpFile; + xNewSheet->SetHelpId( aHelpFile, pSheet->GetHelpId( aHelpFile ) ); + } + xNewSheet->GetItemSet().Put( pSheet->GetItemSet() ); + + rCreatedSheets.emplace_back(static_cast(xNewSheet.get()), true); + aRenamedList.emplace_back( pSheet->GetName(), aName ); + } + else if (bAddToList) + { + // Add to list - used for renaming + rCreatedSheets.emplace_back(static_cast(pExistingSheet), false); + aRenamedList.emplace_back( pSheet->GetName(), aName ); + } + } + + // set parents on newly added stylesheets + for( auto& rStyle : aNewStyles ) + { + if( !rRenameSuffix.empty() ) + { + SfxStyleSheet *pParent = lcl_findStyle(rCreatedSheets, lcl_findRenamedStyleName(aRenamedList, rStyle.second)); + if( pParent ) + { + rStyle.first->SetParent( pParent->GetName() ); + continue; + } + } + DBG_ASSERT( rSourcePool.Find( rStyle.second, eFamily ), "StyleSheet has invalid parent: Family mismatch" ); + rStyle.first->SetParent( rStyle.second ); + } + // we have changed names of style sheets. Trigger reindexing. + Reindex(); +} + +/************************************************************************* +|* +|* Copy style sheets of the named presentation layout from the source pool into +|* this pool. Copies only the style sheets which aren't yet in this pool. +|* If not NULL, pCreatedSheets is filled with pointers to the created style +|* sheets. +|* +|* (rSourcePool can not be const since SfxStyleSheetPoolBase::Find isn't const) +|* +\************************************************************************/ + +void SdStyleSheetPool::CopyLayoutSheets(std::u16string_view rLayoutName, SdStyleSheetPool& rSourcePool, StyleSheetCopyResultVector& rCreatedSheets) +{ + SfxStyleSheetBase* pSheet = nullptr; + + std::vector aNameList; + CreateLayoutSheetNames(rLayoutName,aNameList); + + for (const auto& rName : aNameList) + { + pSheet = Find(rName, SfxStyleFamily::Page); + if (!pSheet) + { + SfxStyleSheetBase* pSourceSheet = rSourcePool.Find(rName, SfxStyleFamily::Page); + DBG_ASSERT(pSourceSheet, "CopyLayoutSheets: Style sheet missing"); + if (pSourceSheet) + { + // In the case one comes with Methusalem-Docs. + SfxStyleSheetBase& rNewSheet = Make(rName, SfxStyleFamily::Page); + OUString file; + rNewSheet.SetHelpId( file, pSourceSheet->GetHelpId( file ) ); + rNewSheet.GetItemSet().Put(pSourceSheet->GetItemSet()); + rCreatedSheets.emplace_back(static_cast(&rNewSheet), true); + } + } + } + + // Special treatment for outline templates: create parent relation + std::vector aOutlineSheets; + CreateOutlineSheetList(rLayoutName,aOutlineSheets); + + if( aOutlineSheets.empty() ) + return; + + std::vector::iterator it = aOutlineSheets.begin(); + SfxStyleSheetBase* pParent = *it; + ++it; + + while (it != aOutlineSheets.end()) + { + pSheet = *it; + + if (!pSheet) + break; + + if (pSheet->GetParent().isEmpty()) + pSheet->SetParent(pParent->GetName()); + + pParent = pSheet; + + ++it; + } +} + +/************************************************************************* +|* +|* Create list with names of the presentation templates of a layout. +|* The list and the containing strings are owned by the caller! +|* +\************************************************************************/ + +void SdStyleSheetPool::CreateLayoutSheetNames(std::u16string_view rLayoutName, std::vector &aNameList) +{ + OUString aPrefix(OUString::Concat(rLayoutName) + SD_LT_SEPARATOR); + + for (sal_Int32 nLevel = 1; nLevel < 10; nLevel++) + aNameList.emplace_back(aPrefix + STR_LAYOUT_OUTLINE + " " + OUString::number( nLevel ) ); + + aNameList.emplace_back(aPrefix + STR_LAYOUT_TITLE); + aNameList.emplace_back(aPrefix + STR_LAYOUT_SUBTITLE); + aNameList.emplace_back(aPrefix + STR_LAYOUT_NOTES); + aNameList.emplace_back(aPrefix + STR_LAYOUT_BACKGROUNDOBJECTS); + aNameList.emplace_back(aPrefix + STR_LAYOUT_BACKGROUND); +} + +/************************************************************************* +|* +|* Create a list with pointer to presentation templates of a layout. +|* The list is owned by the caller! +|* +\************************************************************************/ + +void SdStyleSheetPool::CreateLayoutSheetList(std::u16string_view rLayoutName, SdStyleSheetVector& rLayoutSheets ) +{ + OUString aLayoutNameWithSep(OUString::Concat(rLayoutName) + SD_LT_SEPARATOR); + + SfxStyleSheetIterator aIter(this, SfxStyleFamily::Page); + SfxStyleSheetBase* pSheet = aIter.First(); + + while (pSheet) + { + if (pSheet->GetName().startsWith(aLayoutNameWithSep)) + rLayoutSheets.emplace_back( static_cast< SdStyleSheet* >( pSheet ) ); + pSheet = aIter.Next(); + } +} + +/************************************************************************* +|* +|* Create pseudo style sheets if necessary +|* +\************************************************************************/ + +void SdStyleSheetPool::CreatePseudosIfNecessary() +{ + OUString aName; + const OUString aHelpFile; + SfxStyleSheetBase* pSheet = nullptr; + SfxStyleSheetBase* pParent = nullptr; + + SfxStyleSearchBits nUsedMask = SfxStyleSearchBits::Used; + + aName = SdResId(STR_PSEUDOSHEET_TITLE); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_TITLE ); + + aName = SdResId(STR_PSEUDOSHEET_SUBTITLE); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_SUBTITLE ); + + aName = SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_BACKGROUNDOBJECTS ); + + aName = SdResId(STR_PSEUDOSHEET_BACKGROUND); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_BACKGROUND ); + + aName = SdResId(STR_PSEUDOSHEET_NOTES); + if( (pSheet = Find(aName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aName, SfxStyleFamily::Pseudo, nUsedMask); + pSheet->SetParent( OUString() ); + static_cast(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_NOTES ); + + pParent = nullptr; + aName = SdResId(STR_PSEUDOSHEET_OUTLINE); + for (sal_Int32 nLevel = 1; nLevel < 10; nLevel++) + { + OUString aLevelName( aName + " " + OUString::number( nLevel ) ); + + if( (pSheet = Find(aLevelName, SfxStyleFamily::Pseudo)) == nullptr ) + { + pSheet = &Make(aLevelName, SfxStyleFamily::Pseudo, nUsedMask); + + if (pParent) + pSheet->SetParent(pParent->GetName()); + pParent = pSheet; + static_cast(pSheet)->StartListening(*this); + } + pSheet->SetHelpId( aHelpFile, HID_PSEUDOSHEET_OUTLINE + nLevel ); + } +} + +/************************************************************************* +|* +|* Set the correct name in the program language to the standard styles +|* +\************************************************************************/ + +namespace +{ +struct StyleSheetIsUserDefinedPredicate : svl::StyleSheetPredicate +{ + StyleSheetIsUserDefinedPredicate() + {} + + bool Check(const SfxStyleSheetBase& sheet) override + { + return sheet.IsUserDefined(); + } +}; +} + +void SdStyleSheetPool::UpdateStdNames() +{ + OUString aHelpFile; + StyleSheetIsUserDefinedPredicate aPredicate; + std::vector aEraseList; + std::vector aUserDefinedStyles = GetIndexedStyleSheets().FindPositionsByPredicate(aPredicate); + for (const auto& rStyle : aUserDefinedStyles) + { + SfxStyleSheetBase* pStyle = GetStyleSheetByPositionInIndex(rStyle); + + if( !pStyle->IsUserDefined() ) + { + OUString aOldName = pStyle->GetName(); + sal_uLong nHelpId = pStyle->GetHelpId( aHelpFile ); + SfxStyleFamily eFam = pStyle->GetFamily(); + + bool bHelpKnown = true; + TranslateId pNameId; + switch( nHelpId ) + { + case HID_STANDARD_STYLESHEET_NAME: pNameId = STR_STANDARD_STYLESHEET_NAME; break; + case HID_POOLSHEET_OBJWITHOUTFILL: pNameId = STR_POOLSHEET_OBJWITHOUTFILL; break; + case HID_POOLSHEET_OBJNOLINENOFILL: pNameId = STR_POOLSHEET_OBJNOLINENOFILL;break; + case HID_POOLSHEET_TEXT: pNameId = STR_POOLSHEET_TEXT; break; + case HID_POOLSHEET_A4: pNameId = STR_POOLSHEET_A4; break; + case HID_POOLSHEET_A4_TITLE: pNameId = STR_POOLSHEET_A4_TITLE; break; + case HID_POOLSHEET_A4_HEADLINE: pNameId = STR_POOLSHEET_A4_HEADLINE; break; + case HID_POOLSHEET_A4_TEXT: pNameId = STR_POOLSHEET_A4_TEXT; break; + case HID_POOLSHEET_A0: pNameId = STR_POOLSHEET_A0; break; + case HID_POOLSHEET_A0_TITLE: pNameId = STR_POOLSHEET_A0_TITLE; break; + case HID_POOLSHEET_A0_HEADLINE: pNameId = STR_POOLSHEET_A0_HEADLINE; break; + case HID_POOLSHEET_A0_TEXT: pNameId = STR_POOLSHEET_A0_TEXT; break; + case HID_POOLSHEET_GRAPHIC: pNameId = STR_POOLSHEET_GRAPHIC; break; + case HID_POOLSHEET_SHAPES: pNameId = STR_POOLSHEET_SHAPES; break; + case HID_POOLSHEET_FILLED: pNameId = STR_POOLSHEET_FILLED; break; + case HID_POOLSHEET_FILLED_BLUE: pNameId = STR_POOLSHEET_FILLED_BLUE; break; + case HID_POOLSHEET_FILLED_GREEN: pNameId = STR_POOLSHEET_FILLED_GREEN; break; + case HID_POOLSHEET_FILLED_RED: pNameId = STR_POOLSHEET_FILLED_RED; break; + case HID_POOLSHEET_FILLED_YELLOW: pNameId = STR_POOLSHEET_FILLED_YELLOW; break; + case HID_POOLSHEET_OUTLINE: pNameId = STR_POOLSHEET_OUTLINE; break; + case HID_POOLSHEET_OUTLINE_BLUE: pNameId = STR_POOLSHEET_OUTLINE_BLUE; break; + case HID_POOLSHEET_OUTLINE_GREEN: pNameId = STR_POOLSHEET_OUTLINE_GREEN; break; + case HID_POOLSHEET_OUTLINE_RED: pNameId = STR_POOLSHEET_OUTLINE_RED; break; + case HID_POOLSHEET_OUTLINE_YELLOW: pNameId = STR_POOLSHEET_OUTLINE_YELLOW; break; + case HID_POOLSHEET_LINES: pNameId = STR_POOLSHEET_LINES; break; + case HID_POOLSHEET_MEASURE: pNameId = STR_POOLSHEET_MEASURE; break; + case HID_POOLSHEET_LINES_DASHED: pNameId = STR_POOLSHEET_LINES_DASHED; break; + + case HID_PSEUDOSHEET_OUTLINE1: + case HID_PSEUDOSHEET_OUTLINE2: + case HID_PSEUDOSHEET_OUTLINE3: + case HID_PSEUDOSHEET_OUTLINE4: + case HID_PSEUDOSHEET_OUTLINE5: + case HID_PSEUDOSHEET_OUTLINE6: + case HID_PSEUDOSHEET_OUTLINE7: + case HID_PSEUDOSHEET_OUTLINE8: + case HID_PSEUDOSHEET_OUTLINE9: pNameId = STR_PSEUDOSHEET_OUTLINE; break; + case HID_PSEUDOSHEET_BACKGROUNDOBJECTS: pNameId = STR_PSEUDOSHEET_BACKGROUNDOBJECTS; break; + case HID_PSEUDOSHEET_BACKGROUND: pNameId = STR_PSEUDOSHEET_BACKGROUND; break; + case HID_PSEUDOSHEET_NOTES: pNameId = STR_PSEUDOSHEET_NOTES; break; + + case HID_SD_CELL_STYLE_DEFAULT: pNameId = STR_STANDARD_STYLESHEET_NAME; break; + case HID_SD_CELL_STYLE_BANDED: pNameId = STR_POOLSHEET_BANDED_CELL; break; + case HID_SD_CELL_STYLE_HEADER: pNameId = STR_POOLSHEET_HEADER; break; + case HID_SD_CELL_STYLE_TOTAL: pNameId = STR_POOLSHEET_TOTAL; break; + case HID_SD_CELL_STYLE_FIRST_COLUMN: pNameId = STR_POOLSHEET_FIRST_COLUMN; break; + case HID_SD_CELL_STYLE_LAST_COLUMN: pNameId = STR_POOLSHEET_LAST_COLUMN; break; + + default: + // 0 or wrong (old) HelpId + bHelpKnown = false; + } + if( bHelpKnown ) + { + OUString aNewName; + if (pNameId) + { + if (pNameId == STR_PSEUDOSHEET_OUTLINE) + { + aNewName += " " + OUString::number( sal_Int32( nHelpId - HID_PSEUDOSHEET_OUTLINE ) ); + } + } + + if( !aNewName.isEmpty() && aNewName != aOldName ) + { + SfxStyleSheetBase* pSheetFound = Find( aNewName, eFam ); + + if ( !pSheetFound ) + { + // Sheet does not yet exist: rename old sheet + pStyle->SetName( aNewName ); // transform also parents + } + else + { + // Sheet does exist: old sheet has to be removed + aEraseList.push_back( pStyle ); + } + } + } + } + } + + if (!aEraseList.empty()) + { + // styles that could not be renamed, must be removed + for (SfxStyleSheetBase* p : aEraseList) + Remove( p ); + Reindex(); + } +} + +void SdStyleSheetPool::setDefaultOutlineNumberFormatBulletAndIndent(sal_uInt16 i, SvxNumberFormat &rNumberFormat) +{ + rNumberFormat.SetBulletChar( 0x25CF ); // StarBats: 0xF000 + 34 + rNumberFormat.SetBulletRelSize(45); + const auto nLSpace = (i + 1) * 1200; + rNumberFormat.SetAbsLSpace(nLSpace); + sal_Int32 nFirstLineOffset = -600; + + switch(i) + { + case 0: + { + nFirstLineOffset = -900; + } + break; + + case 1: + { + rNumberFormat.SetBulletChar( 0x2013 ); // StarBats: 0xF000 + 150 + rNumberFormat.SetBulletRelSize(75); + nFirstLineOffset = -900; + } + break; + + case 2: + { + nFirstLineOffset = -800; + } + break; + + case 3: + { + rNumberFormat.SetBulletChar( 0x2013 ); // StarBats: 0xF000 + 150 + rNumberFormat.SetBulletRelSize(75); + } + break; + } + + rNumberFormat.SetFirstLineOffset(nFirstLineOffset); +} + +// Set new SvxNumBulletItem for the respective style sheet +void SdStyleSheetPool::PutNumBulletItem( SfxStyleSheetBase* pSheet, + vcl::Font& rBulletFont ) +{ + OUString aHelpFile; + sal_uLong nHelpId = pSheet->GetHelpId( aHelpFile ); + SfxItemSet& rSet = pSheet->GetItemSet(); + + switch ( nHelpId ) + { + case HID_STANDARD_STYLESHEET_NAME : + { + // Standard template + SvxNumberFormat aNumberFormat(SVX_NUM_CHAR_SPECIAL); + aNumberFormat.SetBulletFont(&rBulletFont); + aNumberFormat.SetBulletChar( 0x25CF ); // StarBats: 0xF000 + 34 + aNumberFormat.SetBulletRelSize(45); + aNumberFormat.SetBulletColor(COL_AUTO); + aNumberFormat.SetStart(1); + aNumberFormat.SetNumAdjust(SvxAdjust::Left); + + SvxNumRule aNumRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::BULLET_COLOR, SVX_MAX_NUM, false); + + for( sal_uInt16 i = 0; i < aNumRule.GetLevelCount(); i++ ) + { + const auto nLSpace = (i + 1) * 600; + aNumberFormat.SetAbsLSpace(nLSpace); + aNumberFormat.SetFirstLineOffset(-600); + aNumRule.SetLevel( i, aNumberFormat ); + } + + rSet.Put( SvxNumBulletItem( std::move(aNumRule), EE_PARA_NUMBULLET ) ); + static_cast(pSheet)->Broadcast(SfxHint( SfxHintId::DataChanged ) ); + } + break; + + case HID_PSEUDOSHEET_TITLE: + /* title gets same bullet as subtitle and not that page symbol anymore */ + case HID_PSEUDOSHEET_SUBTITLE : + { + // Subtitle template + SvxNumBulletItem const*const pItem( + rSet.GetPool()->GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET)); + const SvxNumRule *const pDefaultRule = pItem ? &pItem->GetNumRule() : nullptr; + DBG_ASSERT( pDefaultRule, "Where is my default template? [CL]" ); + + if(pDefaultRule) + { + SvxNumRule aNumRule(pDefaultRule->GetFeatureFlags(), 10, false); + for(sal_uInt16 i=0; i < aNumRule.GetLevelCount(); i++) + { + SvxNumberFormat aFrmt( pDefaultRule->GetLevel(i) ); + aFrmt.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + // #i93908# clear suffix for bullet lists + aFrmt.SetPrefix(OUString()); + aFrmt.SetSuffix(OUString()); + aFrmt.SetStart(1); + aFrmt.SetBulletRelSize(45); + aFrmt.SetBulletChar( 0x25CF ); // StarBats: 0xF000 + 34 + aFrmt.SetBulletFont(&rBulletFont); + aNumRule.SetLevel(i, aFrmt); + } + + rSet.Put( SvxNumBulletItem( std::move(aNumRule), EE_PARA_NUMBULLET ) ); + static_cast(pSheet)->Broadcast(SfxHint( SfxHintId::DataChanged ) ); + } + } + break; + + case HID_PSEUDOSHEET_OUTLINE + 1 : + { + // Outline template + SvxNumberFormat aNumberFormat(SVX_NUM_CHAR_SPECIAL); + aNumberFormat.SetBulletColor(COL_AUTO); + aNumberFormat.SetStart(1); + aNumberFormat.SetNumAdjust(SvxAdjust::Left); + + SvxNumRule aNumRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::BULLET_COLOR, + SVX_MAX_NUM, false ); + for( sal_uInt16 i = 0; i < aNumRule.GetLevelCount(); i++ ) + { + setDefaultOutlineNumberFormatBulletAndIndent(i, aNumberFormat); + rBulletFont.SetFontSize(Size(0,846)); // 24 pt + aNumberFormat.SetBulletFont(&rBulletFont); + aNumRule.SetLevel( i, aNumberFormat ); + } + + rSet.Put( SvxNumBulletItem( std::move(aNumRule), EE_PARA_NUMBULLET ) ); + static_cast(pSheet)->Broadcast(SfxHint( SfxHintId::DataChanged ) ); + } + break; + } +} + +/************************************************************************* +|* +|* Create standard bullet font (without size) +|* +\************************************************************************/ + +vcl::Font SdStyleSheetPool::GetBulletFont() +{ + vcl::Font aBulletFont( "StarSymbol", Size(0, 1000) ); + aBulletFont.SetCharSet(RTL_TEXTENCODING_UNICODE); + aBulletFont.SetWeight(WEIGHT_NORMAL); + aBulletFont.SetUnderline(LINESTYLE_NONE); + aBulletFont.SetOverline(LINESTYLE_NONE); + aBulletFont.SetStrikeout(STRIKEOUT_NONE); + aBulletFont.SetItalic(ITALIC_NONE); + aBulletFont.SetOutline(false); + aBulletFont.SetShadow(false); + aBulletFont.SetColor(COL_AUTO); + aBulletFont.SetTransparent(true); + + return aBulletFont; +} + +void SdStyleSheetPool::AddStyleFamily( const SdPage* pPage ) +{ + rtl::Reference< SfxStyleSheetPool > xPool( this ); + maStyleFamilyMap[pPage] = new SdStyleFamily( xPool, pPage ); +} + +void SdStyleSheetPool::RemoveStyleFamily( const SdPage* pPage ) +{ + SdStyleFamilyMap::iterator iter( maStyleFamilyMap.find( pPage ) ); + if( iter == maStyleFamilyMap.end() ) + return; + + SdStyleFamilyRef xStyle( (*iter).second ); + maStyleFamilyMap.erase( iter ); + + if( xStyle.is() ) try + { + xStyle->dispose(); + } + catch( Exception& ) + { + } +} + +void SdStyleSheetPool::throwIfDisposed() +{ + if( mpDoc == nullptr ) + throw DisposedException(); +} + +// XServiceInfo +OUString SAL_CALL SdStyleSheetPool::getImplementationName() +{ + return "SdStyleSheetPool"; +} + +sal_Bool SAL_CALL SdStyleSheetPool::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL SdStyleSheetPool::getSupportedServiceNames() +{ + return { "com.sun.star.style.StyleFamilies" }; +} + +// XNameAccess +Any SAL_CALL SdStyleSheetPool::getByName( const OUString& aName ) +{ + throwIfDisposed(); + + if( mxGraphicFamily->getName() == aName ) + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( mxGraphicFamily.get() ) ) ); + + if( mxCellFamily->getName() == aName ) + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( mxCellFamily.get() ) ) ); + + if( msTableFamilyName == aName ) + return Any( mxTableFamily ); + + auto iter = std::find_if(maStyleFamilyMap.begin(), maStyleFamilyMap.end(), + [&aName](const SdStyleFamilyMap::value_type& rEntry) { return rEntry.second->getName() == aName; }); + if (iter != maStyleFamilyMap.end()) + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( (*iter).second.get() ) ) ); + + throw NoSuchElementException(); +} + +Sequence< OUString > SAL_CALL SdStyleSheetPool::getElementNames() +{ + throwIfDisposed(); + + Sequence< OUString > aNames( maStyleFamilyMap.size() + 3 ); + OUString* pNames = aNames.getArray(); + + *pNames++ = mxGraphicFamily->getName(); + *pNames++ = mxCellFamily->getName(); + *pNames++ = msTableFamilyName; + + for( const auto& rEntry : maStyleFamilyMap ) + { + *pNames++ = rEntry.second->getName(); + } + + return aNames; +} + +sal_Bool SAL_CALL SdStyleSheetPool::hasByName( const OUString& aName ) +{ + throwIfDisposed(); + + if( mxGraphicFamily->getName() == aName ) + return true; + + if( mxCellFamily->getName() == aName ) + return true; + + if( msTableFamilyName == aName ) + return true; + + return std::any_of(maStyleFamilyMap.begin(), maStyleFamilyMap.end(), + [&aName](const SdStyleFamilyMap::value_type& rEntry) { return rEntry.second->getName() == aName; }); +} + +// XElementAccess + +Type SAL_CALL SdStyleSheetPool::getElementType() +{ + throwIfDisposed(); + + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdStyleSheetPool::hasElements() +{ + return true; +} + +// XIndexAccess + +sal_Int32 SAL_CALL SdStyleSheetPool::getCount() +{ + throwIfDisposed(); + + return maStyleFamilyMap.size() + 3; +} + +Any SAL_CALL SdStyleSheetPool::getByIndex( sal_Int32 Index ) +{ + switch( Index ) + { + case 0: + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( mxGraphicFamily.get() ) ) ); + + case 1: + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( mxCellFamily.get() ) ) ); + + case 2: + return Any( mxTableFamily ); + + default: + { + Index -= 3; + if( (Index < 0) || (Index >= sal::static_int_cast(maStyleFamilyMap.size())) ) + throw IndexOutOfBoundsException(); + SdStyleFamilyMap::iterator iter( maStyleFamilyMap.begin() ); + std::advance(iter, Index); + + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >( (*iter).second.get() ) ) ); + } + } +} + +// XComponent + +void SAL_CALL SdStyleSheetPool::dispose() +{ + if( !mpDoc ) + return; + + mxGraphicFamily->dispose(); + mxGraphicFamily.clear(); + mxCellFamily->dispose(); + mxCellFamily.clear(); + + Reference< XComponent > xComp( mxTableFamily, UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + mxTableFamily = nullptr; + + SdStyleFamilyMap aTempMap; + aTempMap.swap( maStyleFamilyMap ); + + for( auto& rEntry : aTempMap ) try + { + rEntry.second->dispose(); + } + catch( Exception& ) + { + } + + mpDoc = nullptr; + + Clear(); +} + +void SAL_CALL SdStyleSheetPool::addEventListener( const Reference< XEventListener >& /*xListener*/ ) +{ +} + +void SAL_CALL SdStyleSheetPool::removeEventListener( const Reference< XEventListener >& /*aListener*/ ) +{ +} + +SdStyleSheetVector SdStyleSheetPool::CreateChildList( SdStyleSheet const * pSheet ) +{ + SdStyleSheetVector aResult; + + const size_t nListenerCount = pSheet->GetSizeOfVector(); + for (size_t n = 0; n < nListenerCount; ++n) + { + SdStyleSheet* pChild = dynamic_cast< SdStyleSheet* >( pSheet->GetListener(n) ); + if(pChild && pChild->GetParent() == pSheet->GetName()) + { + aResult.emplace_back( pChild ); + } + } + + return aResult; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/stlsheet.cxx b/sd/source/core/stlsheet.cxx new file mode 100644 index 000000000..db061922b --- /dev/null +++ b/sd/source/core/stlsheet.cxx @@ -0,0 +1,1459 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using ::osl::MutexGuard; +using ::osl::ClearableMutexGuard; +using ::com::sun::star::table::BorderLine; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; + +#define WID_STYLE_HIDDEN 7997 +#define WID_STYLE_DISPNAME 7998 +#define WID_STYLE_FAMILY 7999 + +static SvxItemPropertySet& GetStylePropertySet() +{ + static const SfxItemPropertyMapEntry aFullPropertyMap_Impl[] = + { + { u"Family", WID_STYLE_FAMILY, ::cppu::UnoType::get(), PropertyAttribute::READONLY, 0}, + { u"UserDefinedAttributes", SDRATTR_XMLATTRIBUTES, cppu::UnoType::get(), 0, 0}, + { u"DisplayName", WID_STYLE_DISPNAME, ::cppu::UnoType::get(), PropertyAttribute::READONLY, 0}, + { u"Hidden", WID_STYLE_HIDDEN, cppu::UnoType::get(), 0, 0}, + + SVX_UNOEDIT_NUMBERING_PROPERTY, + SHADOW_PROPERTIES + LINE_PROPERTIES + LINE_PROPERTIES_START_END + FILL_PROPERTIES + EDGERADIUS_PROPERTIES + TEXT_PROPERTIES_DEFAULTS + CONNECTOR_PROPERTIES + SPECIAL_DIMENSIONING_PROPERTIES_DEFAULTS + { u"TopBorder", SDRATTR_TABLE_BORDER, ::cppu::UnoType::get(), 0, TOP_BORDER }, + { u"BottomBorder", SDRATTR_TABLE_BORDER, ::cppu::UnoType::get(), 0, BOTTOM_BORDER }, + { u"LeftBorder", SDRATTR_TABLE_BORDER, ::cppu::UnoType::get(), 0, LEFT_BORDER }, + { u"RightBorder", SDRATTR_TABLE_BORDER, ::cppu::UnoType::get(), 0, RIGHT_BORDER }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + static SvxItemPropertySet aPropSet( aFullPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return aPropSet; +} + +class ModifyListenerForwarder : public SfxListener +{ +public: + explicit ModifyListenerForwarder( SdStyleSheet* pStyleSheet ); + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + +private: + SdStyleSheet* mpStyleSheet; +}; + +ModifyListenerForwarder::ModifyListenerForwarder( SdStyleSheet* pStyleSheet ) +: mpStyleSheet( pStyleSheet ) +{ + if( pStyleSheet ) + { + SfxBroadcaster& rBC = static_cast< SfxBroadcaster& >( *pStyleSheet ); + StartListening( rBC ); + } +} + +void ModifyListenerForwarder::Notify(SfxBroadcaster& /*rBC*/, const SfxHint& /*rHint*/) +{ + if( mpStyleSheet ) + mpStyleSheet->notifyModifyListener(); +} + +SdStyleSheet::SdStyleSheet(const OUString& rDisplayName, SfxStyleSheetBasePool& _rPool, SfxStyleFamily eFamily, SfxStyleSearchBits _nMask) +: SdStyleSheetBase( rDisplayName, _rPool, eFamily, _nMask) +, ::cppu::BaseMutex() +, msApiName( rDisplayName ) +, mxPool( &_rPool ) +, mrBHelper( m_aMutex ) +{ +} + +SdStyleSheet::~SdStyleSheet() +{ + delete pSet; + pSet = nullptr; // that following destructors also get a change +} + +void SdStyleSheet::SetApiName( const OUString& rApiName ) +{ + msApiName = rApiName; +} + +OUString const & SdStyleSheet::GetApiName() const +{ + if( !msApiName.isEmpty() ) + return msApiName; + else + return GetName(); +} + +bool SdStyleSheet::SetParent(const OUString& rParentName) +{ + bool bResult = false; + + if (SfxStyleSheet::SetParent(rParentName)) + { + // PseudoStyleSheets do not have their own ItemSets + if (nFamily != SfxStyleFamily::Pseudo) + { + if( !rParentName.isEmpty() ) + { + SfxStyleSheetBase* pStyle = m_pPool->Find(rParentName, nFamily); + if (pStyle) + { + bResult = true; + SfxItemSet& rParentSet = pStyle->GetItemSet(); + GetItemSet().SetParent(&rParentSet); + Broadcast( SfxHint( SfxHintId::DataChanged ) ); + } + } + else + { + bResult = true; + GetItemSet().SetParent(nullptr); + Broadcast( SfxHint( SfxHintId::DataChanged ) ); + } + } + else + { + bResult = true; + } + } + return bResult; +} + +/** + * create if necessary and return ItemSets + */ +SfxItemSet& SdStyleSheet::GetItemSet() +{ + if (nFamily == SfxStyleFamily::Para || nFamily == SfxStyleFamily::Page) + { + // we create the ItemSet 'on demand' if necessary + if (!pSet) + { + pSet = new SfxItemSetFixed< + XATTR_LINE_FIRST, XATTR_LINE_LAST, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, + SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_WORDWRAP, + SDRATTR_EDGE_FIRST, SDRATTR_MEASURE_LAST, + SDRATTR_3D_FIRST, SDRATTR_3D_LAST, + EE_PARA_START, EE_CHAR_END>(GetPool()->GetPool()); + bMySet = true; + } + + return *pSet; + } + + else if( nFamily == SfxStyleFamily::Frame ) + { + if (!pSet) + { + pSet = new SfxItemSetFixed< + XATTR_LINE_FIRST, XATTR_LINE_LAST, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, + SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_XMLATTRIBUTES, + SDRATTR_TEXT_WORDWRAP, SDRATTR_TEXT_WORDWRAP, + SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST, + EE_PARA_START, EE_CHAR_END>(GetPool()->GetPool()); + + bMySet = true; + } + + return *pSet; + } + + // this is a dummy template for the internal template of the + // current presentation layout; return the ItemSet of that template + else + { + + SdStyleSheet* pSdSheet = GetRealStyleSheet(); + + if (pSdSheet) + { + return pSdSheet->GetItemSet(); + } + else + { + if (!pSet) + { + pSet = new SfxItemSetFixed< + XATTR_LINE_FIRST, XATTR_LINE_LAST, + XATTR_FILL_FIRST, XATTR_FILL_LAST, + SDRATTR_SHADOW_FIRST, SDRATTR_SHADOW_LAST, + SDRATTR_TEXT_MINFRAMEHEIGHT, SDRATTR_TEXT_WORDWRAP, + SDRATTR_EDGE_FIRST, SDRATTR_MEASURE_LAST, + SDRATTR_3D_FIRST, SDRATTR_3D_LAST, + EE_PARA_START, EE_CHAR_END>(GetPool()->GetPool()); + bMySet = true; + } + + return(*pSet); + } + } +} + +/** + * A template is used when it is referenced by inserted object or by a used + * template. + */ +bool SdStyleSheet::IsUsed() const +{ + bool bResult = false; + + const size_t nListenerCount = GetSizeOfVector(); + for (size_t n = 0; n < nListenerCount; ++n) + { + SfxListener* pListener = GetListener(n); + if( pListener == this ) + continue; + + const svl::StyleSheetUser* const pUser(dynamic_cast(pListener)); + if (pUser) + bResult = pUser->isUsedByModel(); + if (bResult) + break; + } + + if( !bResult ) + { + MutexGuard aGuard( mrBHelper.rMutex ); + + cppu::OInterfaceContainerHelper * pContainer = mrBHelper.getContainer( cppu::UnoType::get() ); + if( pContainer ) + { + const Sequence< Reference< XInterface > > aModifyListeners( pContainer->getElements() ); + bResult = std::any_of(aModifyListeners.begin(), aModifyListeners.end(), + [](const Reference& rListener) { + Reference< XStyle > xStyle( rListener, UNO_QUERY ); + return xStyle.is() && xStyle->isInUse(); + }); + } + } + return bResult; +} + +/** + * Determine the style sheet for which this dummy is for. + */ +SdStyleSheet* SdStyleSheet::GetRealStyleSheet() const +{ + OUString aRealStyle; + OUString aSep( SD_LT_SEPARATOR ); + SdStyleSheet* pRealStyle = nullptr; + SdDrawDocument* pDoc = static_cast(m_pPool)->GetDoc(); + + ::sd::DrawViewShell* pDrawViewShell = nullptr; + + ::sd::ViewShellBase* pBase = dynamic_cast< ::sd::ViewShellBase* >( SfxViewShell::Current() ); + if( pBase ) + pDrawViewShell = dynamic_cast< ::sd::DrawViewShell* >( pBase->GetMainViewShell().get() ); + + if (pDrawViewShell && pDrawViewShell->GetDoc() == pDoc) + { + SdPage* pPage = pDrawViewShell->getCurrentPage(); + if( pPage ) + { + aRealStyle = pPage->GetLayoutName(); + // cut after separator string + + if( aRealStyle.indexOf(aSep) >= 0) + { + aRealStyle = aRealStyle.copy(0,(aRealStyle.indexOf(aSep) + aSep.getLength())); + } + } + } + if (aRealStyle.isEmpty()) + { + SdPage* pPage = pDoc->GetSdPage(0, PageKind::Standard); + + if (pPage) + { + aRealStyle = pDoc->GetSdPage(0, PageKind::Standard)->GetLayoutName(); + } + else + { + /* no page available yet. This can happen when actualizing the + document templates. */ + SfxStyleSheetIterator aIter(m_pPool, SfxStyleFamily::Page); + SfxStyleSheetBase* pSheet = aIter.First(); + if( pSheet ) + aRealStyle = pSheet->GetName(); + } + + if( aRealStyle.indexOf(aSep) >= 0) + { + aRealStyle = aRealStyle.copy(0,(aRealStyle.indexOf(aSep) + aSep.getLength())); + } + } + + /* now map from the name (specified for country language) to the internal + name (independent of the country language) */ + OUString aInternalName; + OUString aStyleName(aName); + + if (aStyleName == SdResId(STR_PSEUDOSHEET_TITLE)) + { + aInternalName = STR_LAYOUT_TITLE; + } + else if (aStyleName == SdResId(STR_PSEUDOSHEET_SUBTITLE)) + { + aInternalName = STR_LAYOUT_SUBTITLE; + } + else if (aStyleName == SdResId(STR_PSEUDOSHEET_BACKGROUND)) + { + aInternalName = STR_LAYOUT_BACKGROUND; + } + else if (aStyleName == SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS)) + { + aInternalName = STR_LAYOUT_BACKGROUNDOBJECTS; + } + else if (aStyleName == SdResId(STR_PSEUDOSHEET_NOTES)) + { + aInternalName = STR_LAYOUT_NOTES; + } + else + { + OUString aOutlineStr(SdResId(STR_PSEUDOSHEET_OUTLINE)); + sal_Int32 nPos = aStyleName.indexOf(aOutlineStr); + if (nPos >= 0) + { + std::u16string_view aNumStr(aStyleName.subView(aOutlineStr.getLength())); + aInternalName = STR_LAYOUT_OUTLINE + aNumStr; + } + } + + aRealStyle += aInternalName; + pRealStyle = static_cast< SdStyleSheet* >( m_pPool->Find(aRealStyle, SfxStyleFamily::Page) ); + +#ifdef DBG_UTIL + if( !pRealStyle ) + { + SfxStyleSheetIterator aIter(m_pPool, SfxStyleFamily::Page); + if( aIter.Count() > 0 ) + // StyleSheet not found, but pool already loaded + DBG_ASSERT(pRealStyle, "Internal StyleSheet not found"); + } +#endif + + return pRealStyle; +} + +/** + * Determine pseudo style sheet which stands for this style sheet. + */ +SdStyleSheet* SdStyleSheet::GetPseudoStyleSheet() const +{ + SdStyleSheet* pPseudoStyle = nullptr; + OUString aSep( SD_LT_SEPARATOR ); + OUString aStyleName(aName); + // without layout name and separator + + if( aStyleName.indexOf(aSep) >=0 ) + { + aStyleName = aStyleName.copy (aStyleName.indexOf(aSep) + aSep.getLength()); + } + + if (aStyleName == STR_LAYOUT_TITLE) + { + aStyleName = SdResId(STR_PSEUDOSHEET_TITLE); + } + else if (aStyleName == STR_LAYOUT_SUBTITLE) + { + aStyleName = SdResId(STR_PSEUDOSHEET_SUBTITLE); + } + else if (aStyleName == STR_LAYOUT_BACKGROUND) + { + aStyleName = SdResId(STR_PSEUDOSHEET_BACKGROUND); + } + else if (aStyleName == STR_LAYOUT_BACKGROUNDOBJECTS) + { + aStyleName = SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS); + } + else if (aStyleName == STR_LAYOUT_NOTES) + { + aStyleName = SdResId(STR_PSEUDOSHEET_NOTES); + } + else + { + OUString aOutlineStr(STR_LAYOUT_OUTLINE); + sal_Int32 nPos = aStyleName.indexOf(aOutlineStr); + if (nPos != -1) + { + std::u16string_view aNumStr(aStyleName.subView(aOutlineStr.getLength())); + aStyleName = SdResId(STR_PSEUDOSHEET_OUTLINE) + aNumStr; + } + } + + pPseudoStyle = static_cast(m_pPool->Find(aStyleName, SfxStyleFamily::Pseudo)); + DBG_ASSERT(pPseudoStyle, "PseudoStyleSheet missing"); + + return pPseudoStyle; +} + +void SdStyleSheet::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) +{ + // first, base class functionality + SfxStyleSheet::Notify(rBC, rHint); + + if (nFamily != SfxStyleFamily::Pseudo) + return; + + /* if the dummy gets a notify about a changed attribute, he takes care that + the actual meant style sheet sends broadcasts. */ + if (rHint.GetId() == SfxHintId::DataChanged) + { + SdStyleSheet* pRealStyle = GetRealStyleSheet(); + if (pRealStyle) + pRealStyle->Broadcast(rHint); + } +} + +/** + * Adjust the bullet width and the left text indent of the provided ItemSets to + * their font height. The new values are calculated that the ratio to the font + * height is as in the style sheet. + * + * @param bOnlyMissingItems If sal_True, only not set items are completed. With + * sal_False, are items are overwritten. + */ +void SdStyleSheet::AdjustToFontHeight(SfxItemSet& rSet, bool bOnlyMissingItems) +{ + /* If not explicit set, adjust bullet width and text indent to new font + height. */ + SfxStyleFamily eFamily = nFamily; + OUString aStyleName(aName); + if (eFamily == SfxStyleFamily::Pseudo) + { + SfxStyleSheet* pRealStyle = GetRealStyleSheet(); + eFamily = pRealStyle->GetFamily(); + aStyleName = pRealStyle->GetName(); + } + + if (!(eFamily == SfxStyleFamily::Page && + aStyleName.indexOf(STR_LAYOUT_OUTLINE) != -1 && + rSet.GetItemState(EE_CHAR_FONTHEIGHT) == SfxItemState::SET)) + return; + + const SfxItemSet* pCurSet = &GetItemSet(); + sal_uInt32 nNewHeight = rSet.Get(EE_CHAR_FONTHEIGHT).GetHeight(); + sal_uInt32 nOldHeight = pCurSet->Get(EE_CHAR_FONTHEIGHT).GetHeight(); + + if (rSet.GetItemState(EE_PARA_BULLET) != SfxItemState::SET || !bOnlyMissingItems) + { + const SvxBulletItem& rBItem = pCurSet->Get(EE_PARA_BULLET); + double fBulletFraction = double(rBItem.GetWidth()) / nOldHeight; + SvxBulletItem aNewBItem(rBItem); + aNewBItem.SetWidth(static_cast(fBulletFraction * nNewHeight)); + rSet.Put(aNewBItem); + } + + if (rSet.GetItemState(EE_PARA_LRSPACE) != SfxItemState::SET || !bOnlyMissingItems) + { + const SvxLRSpaceItem& rLRItem = pCurSet->Get(EE_PARA_LRSPACE); + double fIndentFraction = double(rLRItem.GetTextLeft()) / nOldHeight; + SvxLRSpaceItem aNewLRItem(rLRItem); + aNewLRItem.SetTextLeft(fIndentFraction * nNewHeight); + double fFirstIndentFraction = double(rLRItem.GetTextFirstLineOffset()) / nOldHeight; + aNewLRItem.SetTextFirstLineOffset(static_cast(fFirstIndentFraction * nNewHeight)); + rSet.Put(aNewLRItem); + } + + if (rSet.GetItemState(EE_PARA_ULSPACE) != SfxItemState::SET || !bOnlyMissingItems) + { + const SvxULSpaceItem& rULItem = pCurSet->Get(EE_PARA_ULSPACE); + SvxULSpaceItem aNewULItem(rULItem); + double fLowerFraction = double(rULItem.GetLower()) / nOldHeight; + aNewULItem.SetLower(static_cast(fLowerFraction * nNewHeight)); + double fUpperFraction = double(rULItem.GetUpper()) / nOldHeight; + aNewULItem.SetUpper(static_cast(fUpperFraction * nNewHeight)); + rSet.Put(aNewULItem); + } +} + +bool SdStyleSheet::HasFollowSupport() const +{ + return false; +} + +bool SdStyleSheet::HasParentSupport() const +{ + return true; +} + +bool SdStyleSheet::HasClearParentSupport() const +{ + return true; +} + +namespace +{ +struct ApiNameMap +{ + std::u16string_view mpApiName; + sal_uInt32 mnHelpId; +} const pApiNameMap[] + = { { std::u16string_view(u"title"), HID_PSEUDOSHEET_TITLE }, + { std::u16string_view(u"subtitle"), HID_PSEUDOSHEET_SUBTITLE }, + { std::u16string_view(u"background"), HID_PSEUDOSHEET_BACKGROUND }, + { std::u16string_view(u"backgroundobjects"), HID_PSEUDOSHEET_BACKGROUNDOBJECTS }, + { std::u16string_view(u"notes"), HID_PSEUDOSHEET_NOTES }, + { std::u16string_view(u"standard"), HID_STANDARD_STYLESHEET_NAME }, + { std::u16string_view(u"objectwithoutfill"), HID_POOLSHEET_OBJWITHOUTFILL }, + + { std::u16string_view(u"Text"), HID_POOLSHEET_TEXT }, + { std::u16string_view(u"A4"), HID_POOLSHEET_A4 }, + { std::u16string_view(u"Title A4"), HID_POOLSHEET_A4_TITLE }, + { std::u16string_view(u"Heading A4"), HID_POOLSHEET_A4_HEADLINE }, + { std::u16string_view(u"Text A4"), HID_POOLSHEET_A4_TEXT }, + { std::u16string_view(u"A0"), HID_POOLSHEET_A0 }, + { std::u16string_view(u"Title A0"), HID_POOLSHEET_A0_TITLE }, + { std::u16string_view(u"Heading A0"), HID_POOLSHEET_A0_HEADLINE }, + { std::u16string_view(u"Text A0"), HID_POOLSHEET_A0_TEXT }, + + { std::u16string_view(u"Graphic"), HID_POOLSHEET_GRAPHIC }, + { std::u16string_view(u"Shapes"), HID_POOLSHEET_SHAPES }, + { std::u16string_view(u"Filled"), HID_POOLSHEET_FILLED }, + { std::u16string_view(u"Filled Blue"), HID_POOLSHEET_FILLED_BLUE }, + { std::u16string_view(u"Filled Green"), HID_POOLSHEET_FILLED_GREEN }, + { std::u16string_view(u"Filled Red"), HID_POOLSHEET_FILLED_RED }, + { std::u16string_view(u"Filled Yellow"), HID_POOLSHEET_FILLED_YELLOW }, + { std::u16string_view(u"Outlined"), HID_POOLSHEET_OUTLINE }, + { std::u16string_view(u"Outlined Blue"), HID_POOLSHEET_OUTLINE_BLUE }, + { std::u16string_view(u"Outlined Green"), HID_POOLSHEET_OUTLINE_GREEN }, + { std::u16string_view(u"Outlined Red"), HID_POOLSHEET_OUTLINE_RED }, + { std::u16string_view(u"Outlined Yellow"), HID_POOLSHEET_OUTLINE_YELLOW }, + { std::u16string_view(u"Lines"), HID_POOLSHEET_LINES }, + { std::u16string_view(u"Arrow Line"), HID_POOLSHEET_MEASURE }, + { std::u16string_view(u"Arrow Dashed"), HID_POOLSHEET_LINES_DASHED } + }; + +OUString GetApiNameForHelpId(sal_uLong nId) +{ + if ((nId >= HID_PSEUDOSHEET_OUTLINE1) && (nId <= HID_PSEUDOSHEET_OUTLINE9)) + return "outline" + OUStringChar(sal_Unicode('1' + (nId - HID_PSEUDOSHEET_OUTLINE1))); + + for (const auto& i : pApiNameMap) + if (nId == i.mnHelpId) + return OUString(i.mpApiName); + + return OUString(); +} + +sal_uInt32 GetHelpIdForApiName(std::u16string_view sName) +{ + std::u16string_view sRest; + if (o3tl::starts_with(sName, u"outline", &sRest)) + { + if (sRest.length() == 1) + { + sal_Unicode ch = sRest.front(); + if ('1' <= ch && ch <= '9') + return HID_PSEUDOSHEET_OUTLINE1 + ch - '1'; + } + // No other pre-defined names start with "outline" + return 0; + } + + for (const auto& i : pApiNameMap) + if (sName == i.mpApiName) + return i.mnHelpId; + + return 0; +} +} + +void SdStyleSheet::SetHelpId( const OUString& r, sal_uLong nId ) +{ + SfxStyleSheet::SetHelpId( r, nId ); + + const OUString sNewApiName = GetApiNameForHelpId(nId); + if (!sNewApiName.isEmpty()) + msApiName = sNewApiName; +} + +OUString SdStyleSheet::GetFamilyString( SfxStyleFamily eFamily ) +{ + switch( eFamily ) + { + case SfxStyleFamily::Frame: + return "cell"; + default: + OSL_FAIL( "SdStyleSheet::GetFamilyString(), illegal family!" ); + [[fallthrough]]; + case SfxStyleFamily::Para: + return "graphics"; + } +} + +void SdStyleSheet::throwIfDisposed() +{ + if( !mxPool.is() ) + throw DisposedException(); +} + +rtl::Reference SdStyleSheet::CreateEmptyUserStyle( SfxStyleSheetBasePool& rPool, SfxStyleFamily eFamily ) +{ + OUString aName; + sal_Int32 nIndex = 1; + do + { + aName = "user" + OUString::number( nIndex++ ); + } + while( rPool.Find( aName, eFamily ) != nullptr ); + + return new SdStyleSheet(aName, rPool, eFamily, SfxStyleSearchBits::UserDefined); +} + +// XInterface + +void SAL_CALL SdStyleSheet::release( ) noexcept +{ + if (osl_atomic_decrement( &m_refCount ) != 0) + return; + + // restore reference count: + osl_atomic_increment( &m_refCount ); + if (! mrBHelper.bDisposed) try + { + dispose(); + } + catch (RuntimeException const&) + { + // don't break throw () + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + OSL_ASSERT( mrBHelper.bDisposed ); + SdStyleSheetBase::release(); +} + +// XComponent + +void SAL_CALL SdStyleSheet::dispose( ) +{ + { + MutexGuard aGuard(mrBHelper.rMutex); + if (mrBHelper.bDisposed || mrBHelper.bInDispose) + return; + + mrBHelper.bInDispose = true; + } + try + { + // side effect: keeping a reference to this + EventObject aEvt( static_cast< OWeakObject * >( this ) ); + try + { + mrBHelper.aLC.disposeAndClear( aEvt ); + disposing(); + } + catch (...) + { + MutexGuard aGuard2( mrBHelper.rMutex ); + // bDisposed and bInDispose must be set in this order: + mrBHelper.bDisposed = true; + mrBHelper.bInDispose = false; + throw; + } + MutexGuard aGuard2( mrBHelper.rMutex ); + // bDisposed and bInDispose must be set in this order: + mrBHelper.bDisposed = true; + mrBHelper.bInDispose = false; + } + catch (RuntimeException &) + { + throw; + } + catch (const Exception & exc) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "unexpected UNO exception caught: " + exc.Message , + nullptr, anyEx ); + } +} + +void SdStyleSheet::disposing() +{ + SolarMutexGuard aGuard; + if (bMySet) + { + delete pSet; + } + pSet = nullptr; + m_pPool = nullptr; + mxPool.clear(); +} + +void SAL_CALL SdStyleSheet::addEventListener( const Reference< XEventListener >& xListener ) +{ + ClearableMutexGuard aGuard( mrBHelper.rMutex ); + if (mrBHelper.bDisposed || mrBHelper.bInDispose) + { + aGuard.clear(); + EventObject aEvt( static_cast< OWeakObject * >( this ) ); + xListener->disposing( aEvt ); + } + else + { + mrBHelper.addListener( cppu::UnoType::get(), xListener ); + } +} + +void SAL_CALL SdStyleSheet::removeEventListener( const Reference< XEventListener >& xListener ) +{ + mrBHelper.removeListener( cppu::UnoType::get(), xListener ); +} + +// XModifyBroadcaster + +void SAL_CALL SdStyleSheet::addModifyListener( const Reference< XModifyListener >& xListener ) +{ + ClearableMutexGuard aGuard( mrBHelper.rMutex ); + if (mrBHelper.bDisposed || mrBHelper.bInDispose) + { + aGuard.clear(); + EventObject aEvt( static_cast< OWeakObject * >( this ) ); + xListener->disposing( aEvt ); + } + else + { + if (!mpModifyListenerForwarder) + mpModifyListenerForwarder.reset( new ModifyListenerForwarder( this ) ); + mrBHelper.addListener( cppu::UnoType::get(), xListener ); + } +} + +void SAL_CALL SdStyleSheet::removeModifyListener( const Reference< XModifyListener >& xListener ) +{ + mrBHelper.removeListener( cppu::UnoType::get(), xListener ); +} + +void SdStyleSheet::notifyModifyListener() +{ + MutexGuard aGuard( mrBHelper.rMutex ); + + cppu::OInterfaceContainerHelper * pContainer = mrBHelper.getContainer( cppu::UnoType::get() ); + if( pContainer ) + { + EventObject aEvt( static_cast< OWeakObject * >( this ) ); + pContainer->forEach( + [&] (Reference const& xListener) { + return xListener->modified(aEvt); + } ); + } +} + +// XServiceInfo +OUString SAL_CALL SdStyleSheet::getImplementationName() +{ + return "SdStyleSheet"; +} + +sal_Bool SAL_CALL SdStyleSheet::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdStyleSheet::getSupportedServiceNames() +{ + return { "com.sun.star.style.Style", + "com.sun.star.drawing.FillProperties", + "com.sun.star.drawing.LineProperties", + "com.sun.star.drawing.ShadowProperties", + "com.sun.star.drawing.ConnectorProperties", + "com.sun.star.drawing.MeasureProperties", + "com.sun.star.style.ParagraphProperties", + "com.sun.star.style.CharacterProperties", + "com.sun.star.drawing.TextProperties", + "com.sun.star.drawing.Text" }; +} + +bool SdStyleSheet::SetName(const OUString& rNewName, bool bReindexNow) +{ + const bool bResult = SfxUnoStyleSheet::SetName(rNewName, bReindexNow); + // Don't overwrite predefined API names + if (bResult && GetHelpIdForApiName(msApiName) == 0) + { + msApiName = rNewName; + Broadcast(SfxHint(SfxHintId::DataChanged)); + } + return bResult; +} + +// XNamed +OUString SAL_CALL SdStyleSheet::getName() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + return GetApiName(); +} + +void SAL_CALL SdStyleSheet::setName( const OUString& rName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + SetName(rName); +} + +// XStyle + +sal_Bool SAL_CALL SdStyleSheet::isUserDefined() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + return IsUserDefined(); +} + +sal_Bool SAL_CALL SdStyleSheet::isInUse() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + return IsUsed(); +} + +OUString SAL_CALL SdStyleSheet::getParentStyle() +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( !GetParent().isEmpty() ) + { + SdStyleSheet* pParentStyle = static_cast< SdStyleSheet* >( mxPool->Find( GetParent(), nFamily ) ); + if( pParentStyle ) + return pParentStyle->GetApiName(); + } + return OUString(); +} + +void SAL_CALL SdStyleSheet::setParentStyle( const OUString& rParentName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + if( !rParentName.isEmpty() ) + { + OUString const name(GetName()); + sal_Int32 const sep(name.indexOf(SD_LT_SEPARATOR)); + OUString const master((sep == -1) ? OUString() : name.copy(0, sep)); + std::shared_ptr aSSSI = std::make_shared(mxPool.get(), nFamily); + for (SfxStyleSheetBase *pStyle = aSSSI->First(); pStyle; pStyle = aSSSI->Next()) + { + // we hope that we have only sd style sheets + SdStyleSheet* pSdStyleSheet = static_cast(pStyle); + OUString const curName(pStyle->GetName()); + sal_Int32 const curSep(curName.indexOf(SD_LT_SEPARATOR)); + OUString const curMaster((curSep == -1) + ? OUString() : curName.copy(0, curSep)); + // check that the master matches, as msApiName exists once per + // master page + if (pSdStyleSheet->msApiName == rParentName && master == curMaster) + { + if( pStyle != this ) + { + SetParent(curName); + } + return; + } + } + throw NoSuchElementException(); + } + else + { + SetParent( rParentName ); + } +} + +// XPropertySet/XMultiPropertySet utility functions + +// Does not broadcast +// Must be guarded by solar mutex; must not be disposed +void SdStyleSheet::setPropertyValue_Impl(const OUString& aPropertyName, const css::uno::Any& aValue) +{ + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( aPropertyName ); + if( pEntry == nullptr ) + { + throw UnknownPropertyException( aPropertyName, static_cast(this)); + } + + if( pEntry->nWID == WID_STYLE_HIDDEN ) + { + bool bValue = false; + if ( aValue >>= bValue ) + SetHidden( bValue ); + return; + } + if( pEntry->nWID == SDRATTR_TEXTDIRECTION ) + return; // not yet implemented for styles + + if( pEntry->nWID == WID_STYLE_FAMILY ) + throw PropertyVetoException(); + + if( (pEntry->nWID == EE_PARA_NUMBULLET) && (GetFamily() == SfxStyleFamily::Page) ) + { + OUString aStr; + const sal_uInt32 nTempHelpId = GetHelpId( aStr ); + + if( (nTempHelpId >= HID_PSEUDOSHEET_OUTLINE2) && (nTempHelpId <= HID_PSEUDOSHEET_OUTLINE9) ) + return; + } + + SfxItemSet &rStyleSet = GetItemSet(); + + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + BitmapMode eMode; + if( aValue >>= eMode ) + { + rStyleSet.Put( XFillBmpStretchItem( eMode == BitmapMode_STRETCH ) ); + rStyleSet.Put( XFillBmpTileItem( eMode == BitmapMode_REPEAT ) ); + return; + } + throw IllegalArgumentException(); + } + + if (pEntry->nWID == OWN_ATTR_TEXTCOLUMNS) + { + if (css::uno::Reference xColumns; aValue >>= xColumns) + { + rStyleSet.Put(SfxInt16Item(SDRATTR_TEXTCOLUMNS_NUMBER, xColumns->getColumnCount())); + if (css::uno::Reference xPropSet{ xColumns, + css::uno::UNO_QUERY }) + { + auto aVal = xPropSet->getPropertyValue("AutomaticDistance"); + if (sal_Int32 nSpacing; aVal >>= nSpacing) + rStyleSet.Put(SdrMetricItem(SDRATTR_TEXTCOLUMNS_SPACING, nSpacing)); + } + return; + } + throw IllegalArgumentException(); + } + + SfxItemSet aSet( GetPool()->GetPool(), pEntry->nWID, pEntry->nWID); + aSet.Put( rStyleSet ); + + if( !aSet.Count() ) + { + if( EE_PARA_NUMBULLET == pEntry->nWID ) + { + vcl::Font aBulletFont; + SdStyleSheetPool::PutNumBulletItem( this, aBulletFont ); + aSet.Put( rStyleSet ); + } + else + { + aSet.Put( GetPool()->GetPool().GetDefaultItem( pEntry->nWID ) ); + } + } + + if( pEntry->nMemberId == MID_NAME && + ( pEntry->nWID == XATTR_FILLBITMAP || pEntry->nWID == XATTR_FILLGRADIENT || + pEntry->nWID == XATTR_FILLHATCH || pEntry->nWID == XATTR_FILLFLOATTRANSPARENCE || + pEntry->nWID == XATTR_LINESTART || pEntry->nWID == XATTR_LINEEND || pEntry->nWID == XATTR_LINEDASH) ) + { + OUString aTempName; + if(!(aValue >>= aTempName )) + throw IllegalArgumentException(); + + SvxShape::SetFillAttribute( pEntry->nWID, aTempName, aSet ); + } + else if(!SvxUnoTextRangeBase::SetPropertyValueHelper( pEntry, aValue, aSet )) + { + SvxItemPropertySet_setPropertyValue( pEntry, aValue, aSet ); + } + + rStyleSet.Put( aSet ); +} + +// Must be guarded by solar mutex; must not be disposed +css::uno::Any SdStyleSheet::getPropertyValue_Impl(const OUString& PropertyName) +{ + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( PropertyName ); + if( pEntry == nullptr ) + { + throw UnknownPropertyException( PropertyName, static_cast(this)); + } + + Any aAny; + + if( pEntry->nWID == WID_STYLE_FAMILY ) + { + if( nFamily == SfxStyleFamily::Page ) + { + const OUString aLayoutName( GetName() ); + aAny <<= aLayoutName.copy( 0, aLayoutName.indexOf( SD_LT_SEPARATOR) ); + } + else + { + aAny <<= GetFamilyString(nFamily); + } + } + else if( pEntry->nWID == WID_STYLE_DISPNAME ) + { + OUString aDisplayName; + if ( nFamily == SfxStyleFamily::Page ) + { + const SdStyleSheet* pStyleSheet = GetPseudoStyleSheet(); + if (pStyleSheet != nullptr) + aDisplayName = pStyleSheet->GetName(); + } + + if (aDisplayName.isEmpty()) + aDisplayName = GetName(); + + aAny <<= aDisplayName; + } + else if( pEntry->nWID == SDRATTR_TEXTDIRECTION ) + { + aAny <<= false; + } + else if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + SfxItemSet &rStyleSet = GetItemSet(); + + const XFillBmpStretchItem* pStretchItem = rStyleSet.GetItem(XATTR_FILLBMP_STRETCH); + const XFillBmpTileItem* pTileItem = rStyleSet.GetItem(XATTR_FILLBMP_TILE); + + if( pStretchItem && pTileItem ) + { + if( pTileItem->GetValue() ) + aAny <<= BitmapMode_REPEAT; + else if( pStretchItem->GetValue() ) + aAny <<= BitmapMode_STRETCH; + else + aAny <<= BitmapMode_NO_REPEAT; + } + } + else if( pEntry->nWID == WID_STYLE_HIDDEN ) + { + aAny <<= IsHidden( ); + } + else + { + SfxItemSet aSet( GetPool()->GetPool(), pEntry->nWID, pEntry->nWID); + + const SfxPoolItem* pItem; + SfxItemSet& rStyleSet = GetItemSet(); + + if( rStyleSet.GetItemState( pEntry->nWID, true, &pItem ) == SfxItemState::SET ) + aSet.Put( *pItem ); + + if( !aSet.Count() ) + aSet.Put( GetPool()->GetPool().GetDefaultItem( pEntry->nWID ) ); + + if(SvxUnoTextRangeBase::GetPropertyValueHelper( aSet, pEntry, aAny )) + return aAny; + + // Get value of ItemSet + aAny = SvxItemPropertySet_getPropertyValue( pEntry, aSet ); + } + + if( pEntry->aType != aAny.getValueType() ) + { + // since the sfx uint16 item now exports a sal_Int32, we may have to fix this here + if( ( pEntry->aType == ::cppu::UnoType::get()) && aAny.getValueType() == ::cppu::UnoType::get() ) + { + sal_Int32 nValue = 0; + aAny >>= nValue; + aAny <<= static_cast(nValue); + } + else + { + OSL_FAIL("SvxShape::GetAnyForItem() Returnvalue has wrong Type!" ); + } + } + + return aAny; +} + +// XPropertySet + +Reference< XPropertySetInfo > SdStyleSheet::getPropertySetInfo() +{ + throwIfDisposed(); + static Reference< XPropertySetInfo > xInfo = GetStylePropertySet().getPropertySetInfo(); + return xInfo; +} + +void SAL_CALL SdStyleSheet::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + setPropertyValue_Impl(aPropertyName, aValue); + Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +Any SAL_CALL SdStyleSheet::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + return getPropertyValue_Impl(PropertyName); +} + +void SAL_CALL SdStyleSheet::addPropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) {} +void SAL_CALL SdStyleSheet::removePropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) {} +void SAL_CALL SdStyleSheet::addVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) {} +void SAL_CALL SdStyleSheet::removeVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) {} + +// XMultiPropertySet + +void SAL_CALL SdStyleSheet::setPropertyValues(const css::uno::Sequence& aPropertyNames, + const css::uno::Sequence& aValues) +{ + const sal_Int32 nCount = aPropertyNames.getLength(); + + if (nCount != aValues.getLength()) + throw css::lang::IllegalArgumentException(); + + if (!nCount) + return; + + SolarMutexGuard aGuard; + throwIfDisposed(); + + for (sal_Int32 i = 0; i < nCount; ++i) + { + try + { + setPropertyValue_Impl(aPropertyNames[i], aValues[i]); + } + catch (const css::beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } + + Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +css::uno::Sequence +SAL_CALL SdStyleSheet::getPropertyValues(const css::uno::Sequence& aPropertyNames) +{ + SolarMutexGuard aGuard; + throwIfDisposed(); + + const sal_Int32 nCount = aPropertyNames.getLength(); + css::uno::Sequence aValues(nCount); + Any* pAny = aValues.getArray(); + + for (sal_Int32 i = 0; i < nCount; ++i) + pAny[i] = getPropertyValue_Impl(aPropertyNames[i]); + + return aValues; +} + +void SAL_CALL SdStyleSheet::addPropertiesChangeListener(const css::uno::Sequence&, const css::uno::Reference&) {} +void SAL_CALL SdStyleSheet::removePropertiesChangeListener(const css::uno::Reference&) {} +void SAL_CALL SdStyleSheet::firePropertiesChangeEvent(const css::uno::Sequence&, const css::uno::Reference&) {} + +// XPropertyState + +PropertyState SAL_CALL SdStyleSheet::getPropertyState( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( PropertyName ); + + if( pEntry == nullptr ) + throw UnknownPropertyException( PropertyName, static_cast(this)); + + if( pEntry->nWID == WID_STYLE_FAMILY ) + { + return PropertyState_DIRECT_VALUE; + } + else if( pEntry->nWID == SDRATTR_TEXTDIRECTION ) + { + return PropertyState_DEFAULT_VALUE; + } + else if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + const SfxItemSet& rSet = GetItemSet(); + + if( rSet.GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET || + rSet.GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET ) + { + return PropertyState_DIRECT_VALUE; + } + else + { + return PropertyState_AMBIGUOUS_VALUE; + } + } + else if (pEntry->nWID == OWN_ATTR_TEXTCOLUMNS) + { + const SfxItemSet& rSet = GetItemSet(); + + const auto eState1 = rSet.GetItemState(SDRATTR_TEXTCOLUMNS_NUMBER, false); + const auto eState2 = rSet.GetItemState(SDRATTR_TEXTCOLUMNS_SPACING, false); + if (eState1 == SfxItemState::SET || eState2 == SfxItemState::SET) + return PropertyState_DIRECT_VALUE; + else if (eState1 == SfxItemState::DEFAULT && eState2 == SfxItemState::DEFAULT) + return PropertyState_DEFAULT_VALUE; + else + return PropertyState_AMBIGUOUS_VALUE; + } + else + { + SfxItemSet &rStyleSet = GetItemSet(); + + PropertyState eState; + + switch( rStyleSet.GetItemState( pEntry->nWID, false ) ) + { + case SfxItemState::SET: + eState = PropertyState_DIRECT_VALUE; + break; + case SfxItemState::DEFAULT: + eState = PropertyState_DEFAULT_VALUE; + break; + default: + eState = PropertyState_AMBIGUOUS_VALUE; + break; + } + + // if an item is set, this doesn't mean we want it :) + if( PropertyState_DIRECT_VALUE == eState ) + { + switch( pEntry->nWID ) + { + case XATTR_FILLBITMAP: + case XATTR_FILLGRADIENT: + case XATTR_FILLHATCH: + case XATTR_FILLFLOATTRANSPARENCE: + case XATTR_LINEEND: + case XATTR_LINESTART: + case XATTR_LINEDASH: + { + const NameOrIndex* pItem = rStyleSet.GetItem(pEntry->nWID); + if( ( pItem == nullptr ) || pItem->GetName().isEmpty() ) + eState = PropertyState_DEFAULT_VALUE; + } + break; + case XATTR_FILLCOLOR: + if (pEntry->nMemberId == MID_COLOR_THEME_INDEX) + { + const XFillColorItem* pColor = rStyleSet.GetItem(pEntry->nWID); + if (pColor->GetThemeColor().GetThemeIndex() == -1) + { + eState = PropertyState_DEFAULT_VALUE; + } + } + else if (pEntry->nMemberId == MID_COLOR_LUM_MOD) + { + const XFillColorItem* pColor = rStyleSet.GetItem(pEntry->nWID); + if (pColor->GetThemeColor().GetLumMod() == 10000) + { + eState = PropertyState_DEFAULT_VALUE; + } + } + else if (pEntry->nMemberId == MID_COLOR_LUM_OFF) + { + const XFillColorItem* pColor = rStyleSet.GetItem(pEntry->nWID); + if (pColor->GetThemeColor().GetLumOff() == 0) + { + eState = PropertyState_DEFAULT_VALUE; + } + } + break; + } + } + + return eState; + } +} + +Sequence< PropertyState > SAL_CALL SdStyleSheet::getPropertyStates( const Sequence< OUString >& aPropertyName ) +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + sal_Int32 nCount = aPropertyName.getLength(); + + Sequence< PropertyState > aPropertyStateSequence( nCount ); + + std::transform(aPropertyName.begin(), aPropertyName.end(), aPropertyStateSequence.getArray(), + [this](const OUString& rName) -> PropertyState { return getPropertyState(rName); }); + + return aPropertyStateSequence; +} + +void SAL_CALL SdStyleSheet::setPropertyToDefault( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( PropertyName ); + if( pEntry == nullptr ) + throw UnknownPropertyException( PropertyName, static_cast(this)); + + SfxItemSet &rStyleSet = GetItemSet(); + + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + rStyleSet.ClearItem( XATTR_FILLBMP_STRETCH ); + rStyleSet.ClearItem( XATTR_FILLBMP_TILE ); + } + else + { + rStyleSet.ClearItem( pEntry->nWID ); + } + Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +Any SAL_CALL SdStyleSheet::getPropertyDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + throwIfDisposed(); + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( aPropertyName ); + if( pEntry == nullptr ) + throw UnknownPropertyException( aPropertyName, static_cast(this)); + Any aRet; + if( pEntry->nWID == WID_STYLE_FAMILY ) + { + aRet <<= GetFamilyString(nFamily); + } + else if( pEntry->nWID == SDRATTR_TEXTDIRECTION ) + { + aRet <<= false; + } + else if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + aRet <<= BitmapMode_REPEAT; + } + else + { + SfxItemPool& rMyPool = GetPool()->GetPool(); + SfxItemSet aSet( rMyPool, pEntry->nWID, pEntry->nWID); + aSet.Put( rMyPool.GetDefaultItem( pEntry->nWID ) ); + aRet = SvxItemPropertySet_getPropertyValue( pEntry, aSet ); + } + return aRet; +} + +/** this is used because our property map is not sorted yet */ +const SfxItemPropertyMapEntry* SdStyleSheet::getPropertyMapEntry( std::u16string_view rPropertyName ) +{ + return GetStylePropertySet().getPropertyMapEntry(rPropertyName); +} + +//Broadcast that a SdStyleSheet has changed, taking into account outline sublevels +//which need to be explicitly broadcast as changing if their parent style was +//the one that changed +void SdStyleSheet::BroadcastSdStyleSheetChange(SfxStyleSheetBase const * pStyleSheet, + PresentationObjects ePO, SfxStyleSheetBasePool* pSSPool) +{ + SdStyleSheet* pRealSheet = static_cast(pStyleSheet)->GetRealStyleSheet(); + pRealSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + + if( (ePO < PresentationObjects::Outline_1) || (ePO > PresentationObjects::Outline_8) ) + return; + + OUString sStyleName(SdResId(STR_PSEUDOSHEET_OUTLINE) + " "); + + for( sal_uInt16 n = static_cast(ePO) - static_cast(PresentationObjects::Outline_1) + 2; n < 10; n++ ) + { + OUString aName( sStyleName + OUString::number(n) ); + + SfxStyleSheetBase* pSheet = pSSPool->Find( aName, SfxStyleFamily::Pseudo); + + if(pSheet) + { + SdStyleSheet* pRealStyleSheet = static_cast(pSheet)->GetRealStyleSheet(); + pRealStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/text/textapi.cxx b/sd/source/core/text/textapi.cxx new file mode 100644 index 000000000..2499588f8 --- /dev/null +++ b/sd/source/core/text/textapi.cxx @@ -0,0 +1,278 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace com::sun::star::container { class XNameContainer; } + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +namespace sd { + +namespace { + +class UndoTextAPIChanged : public SdrUndoAction +{ +public: + UndoTextAPIChanged( SdrModel& rModel, TextApiObject* pTextObj ); + + virtual void Undo() override; + virtual void Redo() override; + +protected: + std::optional mpOldText; + std::optional mpNewText; + rtl::Reference< TextApiObject > mxTextObj; +}; + +} + +UndoTextAPIChanged::UndoTextAPIChanged(SdrModel& rModel, TextApiObject* pTextObj ) +: SdrUndoAction( rModel ) +, mpOldText( pTextObj->CreateText() ) +, mxTextObj( pTextObj ) +{ +} + +void UndoTextAPIChanged::Undo() +{ + if( !mpNewText ) + mpNewText = mxTextObj->CreateText(); + + mxTextObj->SetText( *mpOldText ); +} + +void UndoTextAPIChanged::Redo() +{ + if( mpNewText ) + { + mxTextObj->SetText( *mpNewText ); + } +} + +namespace { + +struct TextAPIEditSource_Impl +{ + SdDrawDocument* mpDoc; + Outliner* mpOutliner; + SvxOutlinerForwarder* mpTextForwarder; +}; + +} + +class TextAPIEditSource : public SvxEditSource +{ + // refcounted + std::shared_ptr m_xImpl; + + virtual std::unique_ptr Clone() const override; + virtual SvxTextForwarder* GetTextForwarder() override; + virtual void UpdateData() override; + explicit TextAPIEditSource( const TextAPIEditSource& rSource ); + +public: + explicit TextAPIEditSource(SdDrawDocument* pDoc); + + void Dispose(); + void SetText( OutlinerParaObject const & rText ); + std::optional CreateText(); + OUString GetText() const; + SdDrawDocument* GetDoc() { return m_xImpl->mpDoc; } +}; + +static const SvxItemPropertySet* ImplGetSdTextPortionPropertyMap() +{ + static const SfxItemPropertyMapEntry aSdTextPortionPropertyEntries[] = + { + SVX_UNOEDIT_CHAR_PROPERTIES, + SVX_UNOEDIT_FONT_PROPERTIES, + SVX_UNOEDIT_OUTLINER_PROPERTIES, + SVX_UNOEDIT_PARA_PROPERTIES, + {u"TextField", EE_FEATURE_FIELD, cppu::UnoType::get(), PropertyAttribute::READONLY, 0 }, + {u"TextPortionType", WID_PORTIONTYPE, ::cppu::UnoType::get(), PropertyAttribute::READONLY, 0 }, + {u"TextUserDefinedAttributes", EE_CHAR_XMLATTRIBS, cppu::UnoType::get(), 0, 0}, + {u"ParaUserDefinedAttributes", EE_PARA_XMLATTRIBS, cppu::UnoType::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + static SvxItemPropertySet aSdTextPortionPropertyMap( aSdTextPortionPropertyEntries, SdrObject::GetGlobalDrawObjectItemPool() ); + + return &aSdTextPortionPropertyMap; +} + +TextApiObject::TextApiObject( std::unique_ptr pEditSource ) +: SvxUnoText( pEditSource.get(), ImplGetSdTextPortionPropertyMap(), Reference < XText >() ) +, mpSource(std::move(pEditSource)) +{ +} + +TextApiObject::~TextApiObject() noexcept +{ + dispose(); +} + +rtl::Reference< TextApiObject > TextApiObject::create( SdDrawDocument* pDoc ) +{ + rtl::Reference< TextApiObject > xRet( new TextApiObject( std::make_unique( pDoc ) ) ); + return xRet; +} + +void TextApiObject::dispose() +{ + if( mpSource ) + { + mpSource->Dispose(); + mpSource.reset(); + } + +} + +std::optional TextApiObject::CreateText() +{ + return mpSource->CreateText(); +} + +void TextApiObject::SetText( OutlinerParaObject const & rText ) +{ + SdrModel* pModel = mpSource->GetDoc(); + if( pModel && pModel->IsUndoEnabled() ) + pModel->AddUndo( std::make_unique( *pModel, this ) ); + + mpSource->SetText( rText ); + maSelection.nStartPara = EE_PARA_MAX_COUNT; +} + +OUString TextApiObject::GetText() const +{ + return mpSource->GetText(); +} + +TextApiObject* TextApiObject::getImplementation( const css::uno::Reference< css::text::XText >& xText ) +{ + TextApiObject* pImpl = dynamic_cast< TextApiObject* >( xText.get() ); + + if( !pImpl ) + pImpl = dynamic_cast< TextApiObject* >( comphelper::getFromUnoTunnel( xText ) ); + + return pImpl; +} + +TextAPIEditSource::TextAPIEditSource(const TextAPIEditSource& rSource) + : SvxEditSource(*this) + , m_xImpl(rSource.m_xImpl) // shallow copy; uses internal refcounting +{ +} + +std::unique_ptr TextAPIEditSource::Clone() const +{ + return std::unique_ptr(new TextAPIEditSource( *this )); +} + +void TextAPIEditSource::UpdateData() +{ + // data is kept in outliner all the time +} + +TextAPIEditSource::TextAPIEditSource(SdDrawDocument* pDoc) +: m_xImpl(std::make_shared()) +{ + m_xImpl->mpDoc = pDoc; + m_xImpl->mpOutliner = nullptr; + m_xImpl->mpTextForwarder = nullptr; +} + +void TextAPIEditSource::Dispose() +{ + m_xImpl->mpDoc=nullptr; + delete m_xImpl->mpTextForwarder; + m_xImpl->mpTextForwarder = nullptr; + + delete m_xImpl->mpOutliner; + m_xImpl->mpOutliner = nullptr; +} + +SvxTextForwarder* TextAPIEditSource::GetTextForwarder() +{ + if(!m_xImpl->mpDoc) + return nullptr; // mpDoc == 0 can be used to flag this as disposed + + if (!m_xImpl->mpOutliner) + { + //init draw model first + m_xImpl->mpOutliner = new SdOutliner(m_xImpl->mpDoc, OutlinerMode::TextObject); + SdDrawDocument::SetCalcFieldValueHdl(m_xImpl->mpOutliner); + } + + if (!m_xImpl->mpTextForwarder) + m_xImpl->mpTextForwarder = new SvxOutlinerForwarder(*m_xImpl->mpOutliner, false); + + return m_xImpl->mpTextForwarder; +} + +void TextAPIEditSource::SetText( OutlinerParaObject const & rText ) +{ + if (m_xImpl->mpDoc) + { + if (!m_xImpl->mpOutliner) + { + //init draw model first + m_xImpl->mpOutliner = new SdOutliner(m_xImpl->mpDoc, OutlinerMode::TextObject); + SdDrawDocument::SetCalcFieldValueHdl(m_xImpl->mpOutliner); + } + + m_xImpl->mpOutliner->SetText( rText ); + } +} + +std::optional TextAPIEditSource::CreateText() +{ + if (m_xImpl->mpDoc && m_xImpl->mpOutliner) + return m_xImpl->mpOutliner->CreateParaObject(); + else + return std::nullopt; +} + +OUString TextAPIEditSource::GetText() const +{ + if (m_xImpl->mpDoc && m_xImpl->mpOutliner) + return m_xImpl->mpOutliner->GetEditEngine().GetText(); + else + return OUString(); +} + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/typemap.cxx b/sd/source/core/typemap.cxx new file mode 100644 index 000000000..4378ad2d2 --- /dev/null +++ b/sd/source/core/typemap.cxx @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #UndoRedo# +#include + +#include + +#define avmedia_MediaItem ::avmedia::MediaItem +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef DISABLE_DYNLOADING +/* Avoid clash with the ones from svx/source/form/typemap.cxx */ +#define aSfxBoolItem_Impl sd_source_core_typemap_aSfxBoolItem_Impl +#define aSfxInt32Item_Impl sd_source_core_typemap_aSfxInt32Item_Impl +#define aSfxStringItem_Impl sd_source_core_typemap_aSfxStringItem_Impl +#define aSfxUInt16Item_Impl sd_source_core_typemap_aSfxUInt16Item_Impl +#define aSfxUInt32Item_Impl sd_source_core_typemap_aSfxUInt32Item_Impl +#define aSfxVoidItem_Impl sd_source_core_typemap_aSfxVoidItem_Impl +#define aSvxClipboardFormatItem_Impl sd_source_core_typemap_aSvxClipboardFormatItem_Impl +#define aSvxColorItem_Impl sd_source_core_typemap_aSvxColorItem_Impl +#define aSvxContourItem_Impl sd_source_core_typemap_aSvxContourItem_Impl +#define aSvxCrossedOutItem_Impl sd_source_core_typemap_aSvxCrossedOutItem_Impl +#define aSvxFontHeightItem_Impl sd_source_core_typemap_aSvxFontHeightItem_Impl +#define aSvxFontItem_Impl sd_source_core_typemap_aSvxFontItem_Impl +#define aSvxLanguageItem_Impl sd_source_core_typemap_aSvxLanguageItem_Impl +#define aSvxPostureItem_Impl sd_source_core_typemap_aSvxPostureItem_Impl +#define aSvxShadowedItem_Impl sd_source_core_typemap_aSvxShadowedItem_Impl +#define aSvxUnderlineItem_Impl sd_source_core_typemap_aSvxUnderlineItem_Impl +#define aSvxOverlineItem_Impl sd_source_core_typemap_aSvxOverlineItem_Impl +#define aSvxWeightItem_Impl sd_source_core_typemap_aSvxWeightItem_Impl +#endif + +#define SFX_TYPEMAP +#include + +#ifdef DISABLE_DYNLOADING +#undef aSfxBoolItem_Impl +#undef aSfxInt32Item_Impl +#undef aSfxStringItem_Impl +#undef aSfxUInt16Item_Impl +#undef aSfxUInt32Item_Impl +#undef aSfxVoidItem_Impl +#undef aSvxClipboardFormatItem_Impl +#undef aSvxColorItem_Impl +#undef aSvxContourItem_Impl +#undef aSvxCrossedOutItem_Impl +#undef aSvxFontHeightItem_Impl +#undef aSvxFontItem_Impl +#undef aSvxLanguageItem_Impl +#undef aSvxPostureItem_Impl +#undef aSvxShadowedItem_Impl +#undef aSvxTextLineItem_Impl +#undef aSvxWeightItem_Impl +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/undo/undofactory.cxx b/sd/source/core/undo/undofactory.cxx new file mode 100644 index 000000000..c87433b81 --- /dev/null +++ b/sd/source/core/undo/undofactory.cxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +using namespace sd; + +std::unique_ptr UndoFactory::CreateUndoRemoveObject(SdrObject& rObject) +{ + return std::make_unique(rObject); +} + +std::unique_ptr UndoFactory::CreateUndoDeleteObject( SdrObject& rObject, bool bOrdNumDirect ) +{ + return std::make_unique( rObject, bOrdNumDirect ); +} + +std::unique_ptr UndoFactory::CreateUndoObjectSetText( SdrObject& rNewObj, sal_Int32 nText ) +{ + return std::make_unique( rNewObj, nText ); +} + +std::unique_ptr UndoFactory::CreateUndoReplaceObject( SdrObject& rOldObject, SdrObject& rNewObject ) +{ + return std::make_unique( rOldObject, rNewObject ); +} + +std::unique_ptr UndoFactory::CreateUndoGeoObject( SdrObject& rObject ) +{ + return std::make_unique( rObject ); +} + +std::unique_ptr UndoFactory::CreateUndoAttrObject( SdrObject& rObject, bool bStyleSheet1, bool bSaveText ) +{ + return std::make_unique( rObject, bStyleSheet1, bSaveText ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/undo/undomanager.cxx b/sd/source/core/undo/undomanager.cxx new file mode 100644 index 000000000..672fe00e1 --- /dev/null +++ b/sd/source/core/undo/undomanager.cxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +using namespace sd; + +UndoManager::UndoManager() + : mpLinkedUndoManager(nullptr) +{ +} + +void UndoManager::EnterListAction(const OUString &rComment, const OUString& rRepeatComment, sal_uInt16 nId, ViewShellId nViewShellId) +{ + if( !IsDoing() ) + { + ClearLinkedRedoActions(); + SdrUndoManager::EnterListAction( rComment, rRepeatComment, nId, nViewShellId ); + } +} + +void UndoManager::AddUndoAction( std::unique_ptr pAction, bool bTryMerg /* = sal_False */ ) +{ + if( !IsDoing() ) + { + ClearLinkedRedoActions(); + SdrUndoManager::AddUndoAction( std::move(pAction), bTryMerg ); + } +} + +void UndoManager::SetLinkedUndoManager (SfxUndoManager* pLinkedUndoManager) +{ + mpLinkedUndoManager = pLinkedUndoManager; +} + +void UndoManager::ClearLinkedRedoActions() +{ + if (mpLinkedUndoManager != nullptr) + mpLinkedUndoManager->ClearRedo(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/undo/undoobjects.cxx b/sd/source/core/undo/undoobjects.cxx new file mode 100644 index 000000000..f586dac21 --- /dev/null +++ b/sd/source/core/undo/undoobjects.cxx @@ -0,0 +1,392 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace sd; + +SdUndoAction::SdUndoAction(SdDrawDocument* pSdDrawDocument) + : mpDoc(pSdDrawDocument), + mnViewShellId(-1) +{ + sd::DrawDocShell* pDocShell = pSdDrawDocument ? pSdDrawDocument->GetDocSh() : nullptr; + sd::ViewShell* pViewShell = pDocShell ? pDocShell->GetViewShell() : nullptr; + if (pViewShell) + mnViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); +} + +ViewShellId SdUndoAction::GetViewShellId() const +{ + return mnViewShellId; +} + +UndoRemovePresObjectImpl::UndoRemovePresObjectImpl( SdrObject& rObject ) +{ + SdPage* pPage = dynamic_cast< SdPage* >( rObject.getSdrPageFromSdrObject() ); + if( !pPage ) + return; + + if( pPage->IsPresObj(&rObject) ) + mpUndoPresObj.reset( new UndoObjectPresentationKind( rObject ) ); + if( rObject.GetUserCall() ) + mpUndoUsercall.reset( new UndoObjectUserCall(rObject) ); + + if( pPage->hasAnimationNode() ) + { + css::uno::Reference< css::drawing::XShape > xShape( rObject.getUnoShape(), css::uno::UNO_QUERY ); + if( pPage->getMainSequence()->hasEffect( xShape ) ) + { + mpUndoAnimation.reset( + new UndoAnimation( // TTTT may use ref? Or just *SdrPage? + static_cast< SdDrawDocument* >(&pPage->getSdrModelFromSdrPage()), + pPage)); + } + } +} + +UndoRemovePresObjectImpl::~UndoRemovePresObjectImpl() +{ +} + +void UndoRemovePresObjectImpl::Undo() +{ + if( mpUndoUsercall ) + mpUndoUsercall->Undo(); + if( mpUndoPresObj ) + mpUndoPresObj->Undo(); + if( mpUndoAnimation ) + mpUndoAnimation->Undo(); +} + +void UndoRemovePresObjectImpl::Redo() +{ + if( mpUndoAnimation ) + mpUndoAnimation->Redo(); + if( mpUndoPresObj ) + mpUndoPresObj->Redo(); + if( mpUndoUsercall ) + mpUndoUsercall->Redo(); +} + +UndoRemoveObject::UndoRemoveObject( SdrObject& rObject ) +: SdrUndoRemoveObj( rObject ), UndoRemovePresObjectImpl( rObject ) +, mxSdrObject(&rObject) +{ +} + +void UndoRemoveObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoRemoveObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + SdrUndoRemoveObj::Undo(); + UndoRemovePresObjectImpl::Undo(); + } +} + +void UndoRemoveObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoRemoveObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + UndoRemovePresObjectImpl::Redo(); + SdrUndoRemoveObj::Redo(); + } +} + +UndoDeleteObject::UndoDeleteObject( SdrObject& rObject, bool bOrdNumDirect ) +: SdrUndoDelObj( rObject, bOrdNumDirect ) +, UndoRemovePresObjectImpl( rObject ) +, mxSdrObject(&rObject) +{ +} + +void UndoDeleteObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoDeleteObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + SdrUndoDelObj::Undo(); + UndoRemovePresObjectImpl::Undo(); + } +} + +void UndoDeleteObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoDeleteObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + UndoRemovePresObjectImpl::Redo(); + SdrUndoDelObj::Redo(); + } +} + +UndoReplaceObject::UndoReplaceObject( SdrObject& rOldObject, SdrObject& rNewObject ) +: SdrUndoReplaceObj( rOldObject, rNewObject ) +, UndoRemovePresObjectImpl( rOldObject ) +, mxSdrObject( &rOldObject ) +{ +} + +void UndoReplaceObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoReplaceObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + SdrUndoReplaceObj::Undo(); + UndoRemovePresObjectImpl::Undo(); + } +} + +void UndoReplaceObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoReplaceObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + UndoRemovePresObjectImpl::Redo(); + SdrUndoReplaceObj::Redo(); + } +} + +UndoObjectSetText::UndoObjectSetText( SdrObject& rObject, sal_Int32 nText ) +: SdrUndoObjSetText( rObject, nText ) +, mbNewEmptyPresObj(false) +, mxSdrObject( &rObject ) +{ + SdPage* pPage = dynamic_cast< SdPage* >( rObject.getSdrPageFromSdrObject() ); + if( pPage && pPage->hasAnimationNode() ) + { + css::uno::Reference< css::drawing::XShape > xShape( rObject.getUnoShape(), css::uno::UNO_QUERY ); + if( pPage->getMainSequence()->hasEffect( xShape ) ) + { + mpUndoAnimation.reset( + new UndoAnimation( + static_cast< SdDrawDocument* >(&pPage->getSdrModelFromSdrPage()), + pPage)); + } + } +} + +UndoObjectSetText::~UndoObjectSetText() +{ +} + +void UndoObjectSetText::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoObjectSetText::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + mbNewEmptyPresObj = mxSdrObject->IsEmptyPresObj(); + SdrUndoObjSetText::Undo(); + if( mpUndoAnimation ) + mpUndoAnimation->Undo(); + } +} + +void UndoObjectSetText::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoObjectSetText::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( mpUndoAnimation ) + mpUndoAnimation->Redo(); + SdrUndoObjSetText::Redo(); + mxSdrObject->SetEmptyPresObj(mbNewEmptyPresObj); + } +} + +// Undo for SdrObject::SetUserCall() + +UndoObjectUserCall::UndoObjectUserCall(SdrObject& rObject) +: SdrUndoObj(rObject) +, mpOldUserCall(static_cast(rObject.GetUserCall())) +, mpNewUserCall(nullptr) +, mxSdrObject( &rObject ) +{ +} + +void UndoObjectUserCall::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoObjectUserCall::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + mpNewUserCall = mxSdrObject->GetUserCall(); + mxSdrObject->SetUserCall(mpOldUserCall); + } +} + +void UndoObjectUserCall::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoObjectUserCall::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + mxSdrObject->SetUserCall(mpNewUserCall); + } +} + +// Undo for SdPage::InsertPresObj() and SdPage::RemovePresObj() + +UndoObjectPresentationKind::UndoObjectPresentationKind(SdrObject& rObject) +: SdrUndoObj(rObject) +, meOldKind(PresObjKind::NONE) +, meNewKind(PresObjKind::NONE) +, mxPage( static_cast(rObject.getSdrPageFromSdrObject()) ) +, mxSdrObject( &rObject ) +{ + DBG_ASSERT( mxPage.get(), "sd::UndoObjectPresentationKind::UndoObjectPresentationKind(), does not work for shapes without a slide!" ); + + if( auto pPage = mxPage.get() ) + meOldKind = pPage->GetPresObjKind( &rObject ); +} + +void UndoObjectPresentationKind::Undo() +{ + if( !mxSdrObject.is() ) + return; + if( rtl::Reference pPage = mxPage.get() ) + { + meNewKind = pPage->GetPresObjKind( mxSdrObject.get() ); + if( meNewKind != PresObjKind::NONE ) + pPage->RemovePresObj( mxSdrObject.get() ); + if( meOldKind != PresObjKind::NONE ) + pPage->InsertPresObj( mxSdrObject.get(), meOldKind ); + } +} + +void UndoObjectPresentationKind::Redo() +{ + if( !mxSdrObject.is() ) + return; + if( rtl::Reference pPage = mxPage.get() ) + { + if( meOldKind != PresObjKind::NONE ) + pPage->RemovePresObj( mxSdrObject.get() ); + if( meNewKind != PresObjKind::NONE ) + pPage->InsertPresObj( mxSdrObject.get(), meNewKind ); + } +} + +UndoAutoLayoutPosAndSize::UndoAutoLayoutPosAndSize( SdPage& rPage ) +: mxPage( &rPage ) +{ +} + +void UndoAutoLayoutPosAndSize::Undo() +{ + // do nothing +} + +void UndoAutoLayoutPosAndSize::Redo() +{ + rtl::Reference pPage = mxPage.get(); + if( pPage ) + pPage->SetAutoLayout( pPage->GetAutoLayout() ); +} + +UndoGeoObject::UndoGeoObject( SdrObject& rNewObj ) +: SdrUndoGeoObj( rNewObj ) +, mxPage( static_cast(rNewObj.getSdrPageFromSdrObject()) ) +, mxSdrObject( &rNewObj ) +{ +} + +void UndoGeoObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoGeoObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( auto pPage = mxPage.get() ) + { + ScopeLockGuard aGuard( pPage->maLockAutoLayoutArrangement ); + SdrUndoGeoObj::Undo(); + } + else + { + SdrUndoGeoObj::Undo(); + } + } +} + +void UndoGeoObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoGeoObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( auto pPage = mxPage.get() ) + { + ScopeLockGuard aGuard( pPage->maLockAutoLayoutArrangement ); + SdrUndoGeoObj::Redo(); + } + else + { + SdrUndoGeoObj::Redo(); + } + } +} + +UndoAttrObject::UndoAttrObject( SdrObject& rObject, bool bStyleSheet1, bool bSaveText ) +: SdrUndoAttrObj( rObject, bStyleSheet1, bSaveText ) +, mxPage( static_cast(rObject.getSdrPageFromSdrObject()) ) +, mxSdrObject( &rObject ) +{ +} + +void UndoAttrObject::Undo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoAttrObject::Undo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( auto pPage = mxPage.get() ) + { + ScopeLockGuard aGuard( pPage->maLockAutoLayoutArrangement ); + SdrUndoAttrObj::Undo(); + } + else + { + SdrUndoAttrObj::Undo(); + } + } +} + +void UndoAttrObject::Redo() +{ + DBG_ASSERT( mxSdrObject.is(), "sd::UndoAttrObject::Redo(), object already dead!" ); + if( mxSdrObject.is() ) + { + if( auto pPage = mxPage.get() ) + { + ScopeLockGuard aGuard( pPage->maLockAutoLayoutArrangement ); + SdrUndoAttrObj::Redo(); + } + else + { + SdrUndoAttrObj::Redo(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/core/undoanim.cxx b/sd/source/core/undoanim.cxx new file mode 100644 index 000000000..64b233322 --- /dev/null +++ b/sd/source/core/undoanim.cxx @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace com::sun::star::animations { class XAnimationNode; } + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using namespace ::com::sun::star::animations; + +namespace sd +{ + +struct UndoAnimationImpl +{ + SdPage* mpPage; + Reference< XAnimationNode > mxOldNode; + Reference< XAnimationNode > mxNewNode; + bool mbNewNodeSet; +}; + +UndoAnimation::UndoAnimation( SdDrawDocument* pDoc, SdPage* pThePage ) +: SdrUndoAction( *pDoc ), mpImpl( new UndoAnimationImpl ) +{ + mpImpl->mpPage = pThePage; + mpImpl->mbNewNodeSet = false; + + try + { + if( pThePage->mxAnimationNode.is() ) + mpImpl->mxOldNode = ::sd::Clone( pThePage->getAnimationNode() ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::UndoAnimation::UndoAnimation()"); + } +} + +UndoAnimation::~UndoAnimation() +{ +} + +void UndoAnimation::Undo() +{ + try + { + if( !mpImpl->mbNewNodeSet ) + { + if( mpImpl->mpPage->mxAnimationNode.is() ) + mpImpl->mxNewNode.set( ::sd::Clone( mpImpl->mpPage->mxAnimationNode ) ); + mpImpl->mbNewNodeSet = true; + } + + Reference< XAnimationNode > xOldNode; + if( mpImpl->mxOldNode.is() ) + xOldNode = ::sd::Clone( mpImpl->mxOldNode ); + + mpImpl->mpPage->setAnimationNode( xOldNode ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::UndoAnimation::Undo()"); + } +} + +void UndoAnimation::Redo() +{ + try + { + Reference< XAnimationNode > xNewNode; + if( mpImpl->mxNewNode.is() ) + xNewNode = ::sd::Clone( mpImpl->mxNewNode ); + mpImpl->mpPage->setAnimationNode( xNewNode ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::UndoAnimation::Redo()"); + } +} + +OUString UndoAnimation::GetComment() const +{ + return SdResId(STR_UNDO_ANIMATION); +} + +struct UndoAnimationPathImpl +{ + SdPage* mpPage; + sal_Int32 mnEffectOffset; + OUString msUndoPath; + OUString msRedoPath; + + UndoAnimationPathImpl( SdPage* pThePage, const css::uno::Reference< css::animations::XAnimationNode >& xNode ) + : mpPage( pThePage ) + , mnEffectOffset( -1 ) + { + if( !(mpPage && xNode.is()) ) + return; + + std::shared_ptr< sd::MainSequence > pMainSequence( mpPage->getMainSequence() ); + if( pMainSequence ) + { + CustomAnimationEffectPtr pEffect( pMainSequence->findEffect( xNode ) ); + if( pEffect ) + { + mnEffectOffset = pMainSequence->getOffsetFromEffect( pEffect ); + msUndoPath = pEffect->getPath(); + } + } + } + UndoAnimationPathImpl(const UndoAnimationPathImpl&) = delete; + UndoAnimationPathImpl& operator=(const UndoAnimationPathImpl&) = delete; + + CustomAnimationEffectPtr getEffect() const + { + CustomAnimationEffectPtr pEffect; + if( mpPage && (mnEffectOffset >= 0) ) + { + std::shared_ptr< sd::MainSequence > pMainSequence( mpPage->getMainSequence() ); + if( pMainSequence ) + pEffect = pMainSequence->getEffectFromOffset( mnEffectOffset ); + } + return pEffect; + } +}; + +UndoAnimationPath::UndoAnimationPath( SdDrawDocument* pDoc, SdPage* pThePage, const css::uno::Reference< css::animations::XAnimationNode >& xNode ) +: SdrUndoAction( *pDoc ) +, mpImpl( new UndoAnimationPathImpl( pThePage, xNode ) ) +{ +} + +UndoAnimationPath::~UndoAnimationPath() +{ +} + +void UndoAnimationPath::Undo() +{ + CustomAnimationEffectPtr pEffect = mpImpl->getEffect(); + if( pEffect ) + { + mpImpl->msRedoPath = pEffect->getPath(); + pEffect->setPath( mpImpl->msUndoPath ); + } +} + +void UndoAnimationPath::Redo() +{ + CustomAnimationEffectPtr pEffect = mpImpl->getEffect(); + if( pEffect ) + { + pEffect->setPath( mpImpl->msRedoPath ); + } +} + +OUString UndoAnimationPath::GetComment() const +{ + return SdResId(STR_UNDO_ANIMATION); +} + +struct UndoTransitionImpl +{ + SdPage* mpPage; + + sal_Int16 mnNewTransitionType; + sal_Int16 mnNewTransitionSubtype; + bool mbNewTransitionDirection; + sal_Int32 mnNewTransitionFadeColor; + double mfNewTransitionDuration; + OUString maNewSoundFile; + bool mbNewSoundOn; + bool mbNewLoopSound; + bool mbNewStopSound; + + sal_Int16 mnOldTransitionType; + sal_Int16 mnOldTransitionSubtype; + bool mbOldTransitionDirection; + sal_Int32 mnOldTransitionFadeColor; + double mfOldTransitionDuration; + OUString maOldSoundFile; + bool mbOldSoundOn; + bool mbOldLoopSound; + bool mbOldStopSound; +}; + +UndoTransition::UndoTransition( SdDrawDocument* _pDoc, SdPage* pThePage ) +: SdUndoAction( _pDoc ), mpImpl( new UndoTransitionImpl ) +{ + mpImpl->mpPage = pThePage; + + mpImpl->mnNewTransitionType = -1; + mpImpl->mnOldTransitionType = pThePage->mnTransitionType; + mpImpl->mnOldTransitionSubtype = pThePage->mnTransitionSubtype; + mpImpl->mbOldTransitionDirection = pThePage->mbTransitionDirection; + mpImpl->mnOldTransitionFadeColor = pThePage->mnTransitionFadeColor; + mpImpl->mfOldTransitionDuration = pThePage->mfTransitionDuration; + mpImpl->maOldSoundFile = pThePage->maSoundFile; + mpImpl->mbOldSoundOn = pThePage->mbSoundOn; + mpImpl->mbOldLoopSound = pThePage->mbLoopSound; + mpImpl->mbOldStopSound = pThePage->mbStopSound; +} + +UndoTransition::~UndoTransition() +{ +} + +void UndoTransition::Undo() +{ + if( mpImpl->mnNewTransitionType == -1 ) + { + mpImpl->mnNewTransitionType = mpImpl->mpPage->mnTransitionType; + mpImpl->mnNewTransitionSubtype = mpImpl->mpPage->mnTransitionSubtype; + mpImpl->mbNewTransitionDirection = mpImpl->mpPage->mbTransitionDirection; + mpImpl->mnNewTransitionFadeColor = mpImpl->mpPage->mnTransitionFadeColor; + mpImpl->mfNewTransitionDuration = mpImpl->mpPage->mfTransitionDuration; + mpImpl->maNewSoundFile = mpImpl->mpPage->maSoundFile; + mpImpl->mbNewSoundOn = mpImpl->mpPage->mbSoundOn; + mpImpl->mbNewLoopSound = mpImpl->mpPage->mbLoopSound; + mpImpl->mbNewStopSound = mpImpl->mpPage->mbStopSound; + } + + mpImpl->mpPage->mnTransitionType = mpImpl->mnOldTransitionType; + mpImpl->mpPage->mnTransitionSubtype = mpImpl->mnOldTransitionSubtype; + mpImpl->mpPage->mbTransitionDirection = mpImpl->mbOldTransitionDirection; + mpImpl->mpPage->mnTransitionFadeColor = mpImpl->mnOldTransitionFadeColor; + mpImpl->mpPage->mfTransitionDuration = mpImpl->mfOldTransitionDuration; + mpImpl->mpPage->maSoundFile = mpImpl->maOldSoundFile; + mpImpl->mpPage->mbSoundOn = mpImpl->mbOldSoundOn; + mpImpl->mpPage->mbLoopSound = mpImpl->mbOldLoopSound; + mpImpl->mpPage->mbStopSound = mpImpl->mbOldStopSound; +} + +void UndoTransition::Redo() +{ + mpImpl->mpPage->mnTransitionType = mpImpl->mnNewTransitionType; + mpImpl->mpPage->mnTransitionSubtype = mpImpl->mnNewTransitionSubtype; + mpImpl->mpPage->mbTransitionDirection = mpImpl->mbNewTransitionDirection; + mpImpl->mpPage->mnTransitionFadeColor = mpImpl->mnNewTransitionFadeColor; + mpImpl->mpPage->mfTransitionDuration = mpImpl->mfNewTransitionDuration; + mpImpl->mpPage->maSoundFile = mpImpl->maNewSoundFile; + mpImpl->mpPage->mbSoundOn = mpImpl->mbNewSoundOn; + mpImpl->mpPage->mbLoopSound = mpImpl->mbNewLoopSound; + mpImpl->mpPage->mbStopSound = mpImpl->mbNewStopSound; +} + +OUString UndoTransition::GetComment() const +{ + return SdResId(STR_UNDO_SLIDE_PARAMS); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/cgm/sdcgmfilter.cxx b/sd/source/filter/cgm/sdcgmfilter.cxx new file mode 100644 index 000000000..80b11519d --- /dev/null +++ b/sd/source/filter/cgm/sdcgmfilter.cxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::frame; + +typedef sal_uInt32 ( *ImportCGMPointer )(SvStream&, Reference< XModel > const &, Reference< XStatusIndicator > const &); + +#ifdef DISABLE_DYNLOADING + +extern "C" sal_uInt32 ImportCGM(SvStream&, Reference< XModel > const &, Reference< XStatusIndicator > const &); + +#endif + +SdCGMFilter::SdCGMFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell ) : + SdFilter( rMedium, rDocShell ) +{ +} + +SdCGMFilter::~SdCGMFilter() +{ +} + +namespace +{ + class CGMPointer + { + ImportCGMPointer m_pPointer; + public: + CGMPointer() + { +#ifdef DISABLE_DYNLOADING + m_pPointer = ImportCGM; +#else + m_pPointer = reinterpret_cast( + SdFilter::GetLibrarySymbol("icg", "ImportCGM")); +#endif + } + ImportCGMPointer get() { return m_pPointer; } + }; +} + +bool SdCGMFilter::Import() +{ + bool bRet = false; + + CGMPointer aPointer; + ImportCGMPointer FncImportCGM = aPointer.get(); + if (FncImportCGM && mxModel.is()) + { + OUString aFileURL( mrMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + sal_uInt32 nRetValue; + + if( mrDocument.GetPageCount() == 0 ) + mrDocument.CreateFirstPages(); + + CreateStatusIndicator(); + std::unique_ptr xIn(::utl::UcbStreamHelper::CreateStream(aFileURL, StreamMode::READ)); + nRetValue = xIn ? FncImportCGM(*xIn, mxModel, mxStatusIndicator) : 0; + + if( nRetValue ) + { + bRet = true; + + if( ( nRetValue &~0xff000000 ) != 0xffffff ) // maybe the backgroundcolor is already white + { // so we must not set a master page + mrDocument.StopWorkStartupDelay(); + SdPage* pSdPage = mrDocument.GetMasterSdPage(0, PageKind::Standard); + + if(pSdPage) + { + // set PageFill to given color + const Color aColor(static_cast(nRetValue >> 16), static_cast(nRetValue >> 8), static_cast(nRetValue >> 16)); + pSdPage->getSdrPageProperties().PutItem(XFillColorItem(OUString(), aColor)); + pSdPage->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_SOLID)); + } + } + } + } + return bRet; +} + +bool SdCGMFilter::Export() +{ + // No ExportCGM function exists(!) + return false; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportCGM(SvStream &rStream) +{ + SdDLL::Init(); + + ::sd::DrawDocShellRef xDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::EMBEDDED, false, DocumentType::Impress); + + CGMPointer aPointer; + + xDocShRef->GetDoc()->EnableUndo(false); + bool bRet = aPointer.get()(rStream, xDocShRef->GetModel(), css::uno::Reference()) == 0; + + xDocShRef->DoClose(); + + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/eppt.cxx b/sd/source/filter/eppt/eppt.cxx new file mode 100644 index 000000000..6f58d919e --- /dev/null +++ b/sd/source/filter/eppt/eppt.cxx @@ -0,0 +1,1464 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "eppt.hxx" +#include "epptdef.hxx" +#include "pptexanimations.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SfxObjectShell; + // complete SfxObjectShell for SaveVBA under -fsanitize=function + +using namespace com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::beans::XPropertySet; + +//============================ PPTWriter ================================== + +PPTWriter::PPTWriter( tools::SvRef const & rSvStorage, + css::uno::Reference< css::frame::XModel > const & rXModel, + css::uno::Reference< css::task::XStatusIndicator > const & rXStatInd, + SvMemoryStream* pVBA, sal_uInt32 nCnvrtFlags ) : + PPTWriterBase ( rXModel, rXStatInd ), + mnCnvrtFlags ( nCnvrtFlags ), + mbStatus ( false ), + mnStatMaxValue ( 0 ), + mnLatestStatValue ( 0 ), + mnTextStyle( 0 ), + mbFontIndependentLineSpacing( false ), + mnTextSize( 0 ), + mrStg ( rSvStorage ), + mnVBAOleOfs ( 0 ), + mpVBA ( pVBA ), + mnExEmbed ( 0 ), + mpExEmbed ( new SvMemoryStream ), + mnPagesWritten ( 0 ), + mnTxId ( 0x7a2f64 ), + mnDiaMode ( 0 ), + mnShapeMasterTitle ( 0 ), + mnShapeMasterBody ( 0 ) +{ +} + +void PPTWriter::exportPPTPre( const std::vector< css::beans::PropertyValue >& rMediaData ) +{ + if ( !mrStg.is() ) + return; + + if ( mXStatusIndicator.is() ) + { + mbStatusIndicator = true; + mnStatMaxValue = ( mnPages + mnMasterPages ) * 5; + mXStatusIndicator->start( "PowerPoint Export", mnStatMaxValue + ( mnStatMaxValue >> 3 ) ); + } + + SvGlobalName aGName(MSO_PPT8_CLASSID); + mrStg->SetClass( aGName, SotClipboardFormatId::NONE, "MS PowerPoint 97" ); + + if ( !ImplCreateCurrentUserStream() ) + return; + + mpStrm = mrStg->OpenSotStream( "PowerPoint Document" ); + if ( !mpStrm ) + return; + + if ( !mpPicStrm ) + mpPicStrm = mrStg->OpenSotStream( "Pictures" ); + + auto aIter = std::find_if(rMediaData.begin(), rMediaData.end(), + [](const css::beans::PropertyValue& rProp) { return rProp.Name == "BaseURI"; }); + if (aIter != rMediaData.end()) + (*aIter).Value >>= maBaseURI; + mpPptEscherEx.reset( new PptEscherEx( *mpStrm, maBaseURI ) ); +} + +void PPTWriter::exportPPTPost( ) +{ + if ( !ImplCloseDocument() ) + return; + + if ( mbStatusIndicator ) + { + mXStatusIndicator->setText( "PowerPoint Export" ); + sal_uInt32 nValue = mnStatMaxValue + ( mnStatMaxValue >> 3 ); + if ( nValue > mnLatestStatValue ) + { + mXStatusIndicator->setValue( nValue ); + mnLatestStatValue = nValue; + } + } + + ImplWriteOLE(); + + ImplWriteVBA(); + + ImplWriteAtomEnding(); + + ImplCreateDocumentSummaryInformation(); + + mbStatus = true; +}; + +static void ImplExportComments( const uno::Reference< drawing::XDrawPage >& xPage, SvMemoryStream& rBinaryTagData10Atom ); + +void PPTWriter::ImplWriteSlide( sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 nMode, + bool bHasBackground, Reference< XPropertySet > const & aXBackgroundPropSet ) +{ + Any aAny; + + const PHLayout& rLayout = GetLayout( mXPagePropSet ); + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_Slide | nPageNum, mpStrm->Tell() ); + mpPptEscherEx->OpenContainer( EPP_Slide ); + mpPptEscherEx->AddAtom( 24, EPP_SlideAtom, 2 ); + mpStrm->WriteInt32( static_cast(rLayout.nLayout) ); + mpStrm->WriteBytes(rLayout.nPlaceHolder, 8); // placeholderIDs (8 parts) + mpStrm->WriteUInt32( nMasterNum | 0x80000000 ) // master ID (equals 0x80000000 on a master page) + .WriteUInt32( nPageNum + 0x100 ) // notes ID (equals null if no notes are present) + .WriteUInt16( nMode ) + .WriteUInt16( 0 ); // padword + + mnDiaMode = 0; + bool bVisible = true; + css::presentation::FadeEffect eFe = css::presentation::FadeEffect_NONE; + + if ( GetPropertyValue( aAny, mXPagePropSet, "Visible" ) ) + aAny >>= bVisible; + if ( GetPropertyValue( aAny, mXPagePropSet, "Change" ) ) + { + switch ( *o3tl::doAccess(aAny) ) + { + case 1 : // automatic + mnDiaMode++; + [[fallthrough]]; + case 2 : // semi-automatic + mnDiaMode++; + break; + default : + case 0 : // manual + break; + } + } + if ( GetPropertyValue( aAny, mXPagePropSet, "Effect" ) ) + aAny >>= eFe; + + sal_uInt32 nSoundRef = 0; + bool bIsSound = false; + bool bStopSound = false; + bool bLoopSound = false; + + if ( GetPropertyValue( aAny, mXPagePropSet, "Sound" ) ) + { + OUString aSoundURL; + if ( aAny >>= aSoundURL ) + { + nSoundRef = maSoundCollection.GetId( aSoundURL ); + bIsSound = true; + } + else + aAny >>= bStopSound; + } + if ( GetPropertyValue( aAny, mXPagePropSet, "LoopSound" ) ) + aAny >>= bLoopSound; + + bool bNeedsSSSlideInfoAtom = !bVisible + || ( mnDiaMode == 2 ) + || bIsSound + || bStopSound + || ( eFe != css::presentation::FadeEffect_NONE ); + if ( bNeedsSSSlideInfoAtom ) + { + sal_uInt8 nDirection = 0; + sal_uInt8 nTransitionType = 0; + sal_uInt16 nBuildFlags = 1; // advance by mouseclick + sal_Int32 nSlideTime = 0; // still has to !!! + sal_uInt8 nSpeed = 1; + + if ( GetPropertyValue( aAny, mXPagePropSet, "TransitionDuration" ) ) + { + css::presentation::AnimationSpeed aAs; + double fTransitionDuration = -1.0; + aAny >>= fTransitionDuration; + + if (fTransitionDuration >= 0) + { + if (fTransitionDuration <= 0.5) + { + aAs = css::presentation::AnimationSpeed::AnimationSpeed_FAST; + } + else if (fTransitionDuration >= 1.0) + { + aAs = css::presentation::AnimationSpeed::AnimationSpeed_SLOW; + } + else + { + aAs = css::presentation::AnimationSpeed::AnimationSpeed_MEDIUM; + } + } + else + aAs = css::presentation::AnimationSpeed::AnimationSpeed_MEDIUM; + + nSpeed = static_cast(aAs); + } + sal_Int16 nTT = 0; + if ( GetPropertyValue( aAny, mXPagePropSet, "TransitionType" ) + && ( aAny >>= nTT ) ) + { + sal_Int16 nTST = 0; + if ( GetPropertyValue( aAny, mXPagePropSet, "TransitionSubtype" ) + && ( aAny >>= nTST ) ) + nTransitionType = GetTransition( nTT, nTST, eFe, 0, nDirection ); + + } + if ( !nTransitionType ) + nTransitionType = GetTransition( eFe, nDirection ); + if ( mnDiaMode == 2 ) // automatic ? + nBuildFlags |= 0x400; + if ( !bVisible ) + nBuildFlags |= 4; + if ( bIsSound ) + nBuildFlags |= 16; + if ( bLoopSound ) + nBuildFlags |= 64; + if ( bStopSound ) + nBuildFlags |= 256; + + if ( GetPropertyValue( aAny, mXPagePropSet, "Duration" ) )// duration of this slide + nSlideTime = *o3tl::doAccess(aAny) << 10; // in ticks + + mpPptEscherEx->AddAtom( 16, EPP_SSSlideInfoAtom ); + mpStrm->WriteInt32( nSlideTime ) // standtime in ticks + .WriteUInt32( nSoundRef ) + .WriteUChar( nDirection ) + .WriteUChar( nTransitionType ) + .WriteUInt16( nBuildFlags ) + .WriteUChar( nSpeed ) + .WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ); + } + + ImplCreateHeaderFooters( mXPagePropSet ); + + EscherSolverContainer aSolverContainer; + mpPptEscherEx->OpenContainer( EPP_PPDrawing ); + mpPptEscherEx->OpenContainer( ESCHER_DgContainer ); + mpPptEscherEx->EnterGroup(nullptr,nullptr); + ImplWritePage( rLayout, aSolverContainer, NORMAL, false, nPageNum ); // the shapes of the pages are created in the PPT document + mpPptEscherEx->LeaveGroup(); + + if ( bHasBackground ) + ImplWriteBackground( aXBackgroundPropSet ); + else + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::Background | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_fillRectRight, PPTtoEMU( maDestPageSize.Width ) ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectBottom, PPTtoEMU( maDestPageSize.Width ) ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x120012 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + aPropOpt.AddOpt( ESCHER_Prop_bWMode, ESCHER_wDontShow ); + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); // if true, this is the background shape + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + + aSolverContainer.WriteSolver( *mpStrm ); + + mpPptEscherEx->CloseContainer(); // ESCHER_DgContainer + mpPptEscherEx->CloseContainer(); // EPP_Drawing + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 1 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + + SvMemoryStream aBinaryTagData10Atom; + ImplExportComments( mXDrawPage, aBinaryTagData10Atom ); + SvMemoryStream amsofbtAnimGroup; + ppt::AnimationExporter aExporter( aSolverContainer, maSoundCollection ); + aExporter.doexport( mXDrawPage, amsofbtAnimGroup ); + sal_uInt32 nmsofbtAnimGroupSize = amsofbtAnimGroup.Tell(); + if ( nmsofbtAnimGroupSize ) + { + { + EscherExAtom aMagic2( aBinaryTagData10Atom, 0x2eeb ); + aBinaryTagData10Atom.WriteUInt32( 0x01c45df9 ) + .WriteUInt32( 0xe1471b30 ); + } + { + EscherExAtom aMagic( aBinaryTagData10Atom, 0x2b00 ); + aBinaryTagData10Atom.WriteUInt32( 0 ); + } + aBinaryTagData10Atom.WriteBytes(amsofbtAnimGroup.GetData(), amsofbtAnimGroup.Tell()); + { + EscherExContainer aMagic2( aBinaryTagData10Atom, 0x2b02 ); + } + } + if ( aBinaryTagData10Atom.Tell() ) + { + EscherExContainer aProgTags ( *mpStrm, EPP_ProgTags ); + EscherExContainer aProgBinaryTag( *mpStrm, EPP_ProgBinaryTag ); + { + EscherExAtom aCString( *mpStrm, EPP_CString ); + mpStrm->WriteUInt32( 0x5f005f ) + .WriteUInt32( 0x50005f ) + .WriteUInt32( 0x540050 ) + .WriteUInt16( 0x31 ) + .WriteUInt16( 0x30 ); + } + { + EscherExAtom aBinaryTagData( *mpStrm, EPP_BinaryTagData ); + mpStrm->WriteBytes(aBinaryTagData10Atom.GetData(), aBinaryTagData10Atom.Tell()); + } + } + mpPptEscherEx->CloseContainer(); // EPP_Slide +} + +void PPTWriter::ImplWriteSlideMaster( sal_uInt32 nPageNum, Reference< XPropertySet > const & aXBackgroundPropSet ) +{ + if (!aXBackgroundPropSet) + return; + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_MainMaster | nPageNum, mpStrm->Tell() ); + mpPptEscherEx->OpenContainer( EPP_MainMaster ); + mpPptEscherEx->AddAtom( 24, EPP_SlideAtom, 2 ); + mpStrm->WriteInt32( static_cast(EppLayout::TITLEANDBODYSLIDE) ) // slide layout -> title and body slide + .WriteUChar( 1 ).WriteUChar( 2 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ) // placeholderID + .WriteUInt32( 0 ) // master ID (equals null at a master page) + .WriteUInt32( 0 ) // notes ID (equals null if no notes are present) + .WriteUInt16( 0 ) // Bit 1: Follow master objects, Bit 2: Follow master scheme, Bit 3: Follow master background + .WriteUInt16( 0 ); // padword + + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xff0000 ).WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x00ffff ).WriteUInt32( 0x0099ff ).WriteUInt32( 0xffff00 ).WriteUInt32( 0x0000ff ).WriteUInt32( 0x969696 ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xccffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x336666 ).WriteUInt32( 0x008080 ).WriteUInt32( 0x339933 ).WriteUInt32( 0x000080 ).WriteUInt32( 0xcc3300 ).WriteUInt32( 0x66ccff ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x333333 ).WriteUInt32( 0x000000 ).WriteUInt32( 0xdddddd ).WriteUInt32( 0x808080 ).WriteUInt32( 0x4d4d4d ).WriteUInt32( 0xeaeaea ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x66ccff ).WriteUInt32( 0xff0000 ).WriteUInt32( 0xcc00cc ).WriteUInt32( 0xc0c0c0 ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0xc0c0c0 ).WriteUInt32( 0xff6600 ).WriteUInt32( 0x0000ff ).WriteUInt32( 0x009900 ); + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 6 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0xff9933 ).WriteUInt32( 0xccff99 ).WriteUInt32( 0xcc00cc ).WriteUInt32( 0xb2b2b2 ); + + for ( int nInstance = EPP_TEXTTYPE_Title; nInstance <= EPP_TEXTTYPE_QuarterBody; nInstance++ ) + { + if ( nInstance == EPP_TEXTTYPE_notUsed ) + continue; + + // the auto color is dependent to the page background,so we have to set a page that is in the right context + if ( nInstance == EPP_TEXTTYPE_Notes ) + (void)GetPageByIndex(0, NOTICE); + else + (void)GetPageByIndex(0, MASTER); + + mpPptEscherEx->BeginAtom(); + + bool bSimpleText = false; + + mpStrm->WriteUInt16( 5 ); // paragraph count + + for ( sal_uInt16 nLev = 0; nLev < 5; nLev++ ) + { + if ( nInstance >= EPP_TEXTTYPE_CenterBody ) + { + bSimpleText = true; + mpStrm->WriteUInt16( nLev ); + } + mpStyleSheet->mpParaSheet[ nInstance ]->Write( *mpStrm, nLev, bSimpleText, mXPagePropSet ); + mpStyleSheet->mpCharSheet[ nInstance ]->Write( *mpStrm, nLev, bSimpleText, mXPagePropSet ); + } + mpPptEscherEx->EndAtom( EPP_TxMasterStyleAtom, 0, nInstance ); + } + GetPageByIndex( nPageNum, MASTER ); + + EscherSolverContainer aSolverContainer; + + mpPptEscherEx->OpenContainer( EPP_PPDrawing ); + mpPptEscherEx->OpenContainer( ESCHER_DgContainer ); + + mpPptEscherEx->EnterGroup(nullptr,nullptr); + ImplWritePage( GetLayout( 0 ), aSolverContainer, MASTER, true ); // the shapes of the pages are created in the PPT document + mpPptEscherEx->LeaveGroup(); + + ImplWriteBackground( aXBackgroundPropSet ); + + aSolverContainer.WriteSolver( *mpStrm ); + + mpPptEscherEx->CloseContainer(); // ESCHER_DgContainer + mpPptEscherEx->CloseContainer(); // EPP_Drawing + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 1 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + + if ( aBuExMasterStream.Tell() ) + { + ImplProgTagContainer( mpStrm.get(), &aBuExMasterStream ); + } + mpPptEscherEx->CloseContainer(); // EPP_MainMaster +}; + +PPTWriter::~PPTWriter() +{ + mpExEmbed.reset(); + mpPptEscherEx.reset(); + mpCurUserStrm.clear(); + mpPicStrm.clear(); + mpStrm.clear(); + maStyleSheetList.clear(); + maExOleObj.clear(); + if ( mbStatusIndicator ) + mXStatusIndicator->end(); +} + +bool PPTWriter::ImplCreateCurrentUserStream() +{ + mpCurUserStrm = mrStg->OpenSotStream( "Current User" ); + if ( !mpCurUserStrm ) + return false; + char pUserName[] = "Current User"; + sal_uInt32 nLenOfUserName = strlen( pUserName ); + sal_uInt32 nSizeOfRecord = 0x14 + ( ( nLenOfUserName + 4 ) & ~ 3 ); + + mpCurUserStrm->WriteUInt16( 0 ).WriteUInt16( EPP_CurrentUserAtom ).WriteUInt32( nSizeOfRecord ); + mpCurUserStrm->WriteUInt32( 0x14 ) // Len + .WriteUInt32( 0xe391c05f ); // Magic + + sal_uInt32 nEditPos = mpCurUserStrm->Tell(); + mpCurUserStrm->WriteUInt32( 0x0 ) // OffsetToCurrentEdit; + .WriteUInt16( nLenOfUserName ) + .WriteUInt16( 0x3f4 ) // DocFileVersion + .WriteUChar( 3 ) // MajorVersion + .WriteUChar( 0 ) // MinorVersion + .WriteUInt16( 0 ); // Pad Word + pUserName[ nLenOfUserName ] = 8; + mpCurUserStrm->WriteBytes(pUserName, nLenOfUserName + 1); + for ( sal_uInt32 i = 0x15 + nLenOfUserName; i < nSizeOfRecord; i++ ) + { + mpCurUserStrm->WriteUChar( 0 ); // pad bytes + } + mpCurUserStrm->Seek( nEditPos ); + return true; +}; + +void PPTWriter::ImplCreateDocumentSummaryInformation() +{ + uno::Reference xDPS( + mXModel, uno::UNO_QUERY_THROW); + uno::Reference xDocProps( + xDPS->getDocumentProperties()); + + if (!xDocProps.is()) + return; + + // no idea what this is... + static const sal_Int8 aGuid[ 0x52 ] = + { + 0x4e, 0x00, 0x00, 0x00, + '{',0,'D',0,'B',0,'1',0,'A',0,'C',0,'9',0,'6',0,'4',0,'-',0, + 'E',0,'3',0,'9',0,'C',0,'-',0,'1',0,'1',0,'D',0,'2',0,'-',0, + 'A',0,'1',0,'E',0,'F',0,'-',0,'0',0,'0',0,'6',0,'0',0,'9',0, + '7',0,'D',0,'A',0,'5',0,'6',0,'8',0,'9',0,'}',0 + }; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + uno::Sequence aGuidSeq(aGuid, 0x52); + + SvMemoryStream aHyperBlob; + ImplCreateHyperBlob( aHyperBlob ); + + auto nHyperLength = static_cast(aHyperBlob.Tell()); + const sal_Int8* pBlob( + static_cast(aHyperBlob.GetData())); + auto aHyperSeq = comphelper::arrayToSequence(pBlob, nHyperLength); + + if ( mnCnvrtFlags & 0x8000 ) + { + uno::Sequence aThumbSeq; + if ( GetPageByIndex( 0, NORMAL ) && ImplGetPropertyValue( mXPagePropSet, "PreviewBitmap" ) ) + { + aThumbSeq = *o3tl::doAccess>(mAny); + } + sfx2::SaveOlePropertySet( xDocProps, mrStg.get(), + &aThumbSeq, &aGuidSeq, &aHyperSeq); + } + else + { + sfx2::SaveOlePropertySet( xDocProps, mrStg.get(), + nullptr, &aGuidSeq, &aHyperSeq ); + } +} + +void PPTWriter::ImplWriteExtParaHeader( SvMemoryStream& rSt, sal_uInt32 nRef, sal_uInt32 nInstance, sal_uInt32 nSlideId ) +{ + if ( rSt.Tell() ) + { + aBuExOutlineStream.WriteUInt32( ( EPP_PST_ExtendedParagraphHeaderAtom << 16 ) + | ( nRef << 4 ) ) + .WriteUInt32( 8 ) + .WriteUInt32( nSlideId ) + .WriteUInt32( nInstance ); + aBuExOutlineStream.WriteBytes(rSt.GetData(), rSt.Tell()); + } +} + +void PPTWriter::ImplCreateHeaderFooterStrings( SvStream& rStrm, css::uno::Reference< css::beans::XPropertySet > const & rXPagePropSet ) +{ + if ( !rXPagePropSet.is() ) + return; + + OUString aString; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "HeaderText", true ) ) + { + if ( aAny >>= aString ) + PPTWriter::WriteCString( rStrm, aString, 1 ); + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "FooterText", true ) ) + { + if ( aAny >>= aString ) + PPTWriter::WriteCString( rStrm, aString, 2 ); + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "DateTimeText", true ) ) + { + if ( aAny >>= aString ) + PPTWriter::WriteCString( rStrm, aString ); + } +} + +void PPTWriter::ImplCreateHeaderFooters( css::uno::Reference< css::beans::XPropertySet > const & rXPagePropSet ) +{ + if ( !rXPagePropSet.is() ) + return; + + bool bVal = false; + sal_uInt32 nVal = 0; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsHeaderVisible", true ) ) + { + if ( ( aAny >>= bVal ) && bVal ) + nVal |= 0x100000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsFooterVisible", true ) ) + { + if ( ( aAny >>= bVal ) && bVal ) + nVal |= 0x200000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsDateTimeVisible", true ) ) + { + if ( ( aAny >>= bVal ) && bVal ) + nVal |= 0x010000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsPageNumberVisible", true ) ) + { + if ( ( aAny >>= bVal ) && bVal ) + nVal |= 0x080000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "IsDateTimeFixed", true ) ) + { + if ( ( aAny >>= bVal ) && !bVal ) + nVal |= 0x20000; + else + nVal |= 0x40000; + } + if ( PropValue::GetPropertyValue( aAny, rXPagePropSet, "DateTimeFormat", true ) ) + { + sal_Int32 nFormat = *o3tl::doAccess(aAny); + SvxDateFormat eDateFormat = static_cast( nFormat & 0xf ); + SvxTimeFormat eTimeFormat = static_cast( ( nFormat >> 4 ) & 0xf ); + switch( eDateFormat ) + { + case SvxDateFormat::F : + nFormat = 1; + break; + case SvxDateFormat::D : + nFormat = 2; + break; + case SvxDateFormat::C : + nFormat = 4; + break; + default: + case SvxDateFormat::A : + nFormat = 0; + } + switch( eTimeFormat ) + { + case SvxTimeFormat::HH24_MM : + nFormat = 9; + break; + case SvxTimeFormat::HH12_MM : + nFormat = 11; + break; + case SvxTimeFormat::HH24_MM_SS : + nFormat = 10; + break; + case SvxTimeFormat::HH12_MM_SS : + nFormat = 12; + break; + default: + break; + } + nVal |= nFormat; + } + + mpPptEscherEx->OpenContainer( EPP_HeadersFooters ); + mpPptEscherEx->AddAtom( 4, EPP_HeadersFootersAtom ); + mpStrm->WriteUInt32( nVal ); + ImplCreateHeaderFooterStrings( *mpStrm, rXPagePropSet ); + mpPptEscherEx->CloseContainer(); +} + +bool PPTWriter::ImplCreateDocument() +{ + sal_uInt32 i; + sal_uInt16 nSlideType = EPP_SLIDESIZE_TYPECUSTOM; + + sal_uInt32 nWidth = maDestPageSize.Width; + sal_uInt32 nHeight = maDestPageSize.Height; + + if ( ( nWidth == 0x1680 ) && ( nHeight == 0x10e0 ) ) + nSlideType = EPP_SLIDESIZE_TYPEONSCREEN; + else if ( ( nWidth == 0x1200 ) && ( nHeight == 0x240 ) ) + nSlideType = EPP_SLIDESIZE_TYPEBANNER; + else if ( ( nWidth == 0x1950 ) && ( nHeight == 0x10e0 ) ) + nSlideType = EPP_SLIDESIZE_TYPE35MM; + else if ( ( nWidth == 0x1860 ) && ( nHeight == 0x10e0 ) ) + nSlideType = EPP_SLIDESIZE_TYPEA4PAPER; + + mpPptEscherEx->OpenContainer( EPP_Document ); + // CREATE DOCUMENT ATOM + mpPptEscherEx->AddAtom( 40, EPP_DocumentAtom, 1 ); + mpStrm->WriteUInt32( nWidth ) // Slide Size in Master coordinates X + .WriteUInt32( nHeight ) // " " " " " Y + .WriteInt32( maNotesPageSize.Width ) // Notes Page Size X + .WriteInt32( maNotesPageSize.Height ) // " " " Y + .WriteInt32( 1 ).WriteInt32( 2 ); // the scale used when the Powerpoint document is embedded. the default is 1:2 + mpPptEscherEx->InsertPersistOffset( EPP_MAINNOTESMASTER_PERSIST_KEY, mpStrm->Tell() ); + mpStrm->WriteUInt32( 0 ) // Reference to NotesMaster ( 0 if none ); + .WriteUInt32( 0 ) // Reference to HandoutMaster ( 0 if none ); + .WriteInt16( 1 ) // Number of the first slide; + .WriteUInt16( nSlideType ) // Size of the document slides ( default: EPP_SLIDESIZETYPEONSCREEN ) + .WriteUChar( 0 ) // bool1 indicates if document was saved with embedded true type fonts + .WriteUChar( 0 ) // bool1 indicates if the placeholders on the title slide are omitted + .WriteUChar( 0 ) // bool1 right to left ( flag for Bidi version ) + .WriteUChar( 1 ); // bool1 visibility of comments shapes + + mpPptEscherEx->PtInsert( EPP_Persist_Document, mpStrm->Tell() ); + + mpPptEscherEx->OpenContainer( EPP_HeadersFooters, 3 ); //Master footer (default) + mpPptEscherEx->AddAtom( 4, EPP_HeadersFootersAtom ); + mpStrm->WriteUInt32( 0x25000d ); + if ( GetPageByIndex( 0, MASTER ) ) + ImplCreateHeaderFooterStrings( *mpStrm, mXPagePropSet ); + mpPptEscherEx->CloseContainer(); + mpPptEscherEx->OpenContainer( EPP_HeadersFooters, 4 ); //NotesMaster footer (default) + mpPptEscherEx->AddAtom( 4, EPP_HeadersFootersAtom ); + mpStrm->WriteUInt32( 0x3d000d ); + if ( GetPageByIndex( 0, NOTICE ) ) + ImplCreateHeaderFooterStrings( *mpStrm, mXPagePropSet ); + mpPptEscherEx->CloseContainer(); + + mpPptEscherEx->OpenContainer( EPP_SlideListWithText ); // animation information for the slides + + for ( i = 0; i < mnPages; i++ ) + { + mpPptEscherEx->AddAtom( 20, EPP_SlidePersistAtom ); + mpPptEscherEx->InsertPersistOffset( EPP_MAINSLIDE_PERSIST_KEY | i, mpStrm->Tell() ); + mpStrm->WriteUInt32( 0 ) // psrReference - logical reference to the slide persist object ( EPP_MAINSLIDE_PERSIST_KEY ) + .WriteUInt32( 4 ) // flags - only bit 3 used, if set then slide contains shapes other than placeholders + .WriteInt32( 0 ) // numberTexts - number of placeholder texts stored with the persist object. Allows to display outline view without loading the slide persist objects + .WriteInt32( i + 0x100 ) // slideId - Unique slide identifier, used for OLE link monikers for example + .WriteUInt32( 0 ); // reserved, usually 0 + + if ( !GetPageByIndex( i, NORMAL ) ) // very exciting: once again through all pages + return false; + SetCurrentStyleSheet( GetMasterIndex( NORMAL ) ); + + css::uno::Reference< css::container::XNamed > + aXName( mXDrawPage, css::uno::UNO_QUERY ); + + if ( aXName.is() ) + maSlideNameList.push_back( aXName->getName() ); + else + maSlideNameList.emplace_back( ); + } + mpPptEscherEx->CloseContainer(); // EPP_SlideListWithText + + mpPptEscherEx->OpenContainer( EPP_SlideListWithText, 2 ); // animation information for the notes + for( i = 0; i < mnPages; i++ ) + { + mpPptEscherEx->AddAtom( 20, EPP_SlidePersistAtom ); + mpPptEscherEx->InsertPersistOffset( EPP_MAINNOTES_PERSIST_KEY | i, mpStrm->Tell() ); + mpStrm->WriteUInt32( 0 ) + .WriteUInt32( 4 ) + .WriteInt32( 0 ) + .WriteInt32( i + 0x100 ) + .WriteUInt32( 0 ); + } + mpPptEscherEx->CloseContainer(); // EPP_SlideListWithText + + css::uno::Reference< css::presentation::XPresentationSupplier > + aXPresSupplier( mXModel, css::uno::UNO_QUERY ); + if ( aXPresSupplier.is() ) + { + css::uno::Reference< css::presentation::XPresentation > aXPresentation( aXPresSupplier->getPresentation() ); + if ( aXPresentation.is() ) + { + mXPropSet.set( aXPresentation, css::uno::UNO_QUERY ); + if ( mXPropSet.is() ) + { + OUString aCustomShow; + sal_uInt32 const nPenColor = 0x1000000; + sal_Int32 const nRestartTime = 0x7fffffff; + sal_Int16 nStartSlide = 0; + sal_Int16 nEndSlide = 0; + sal_uInt32 nFlags = 0; // Bit 0: Auto advance + // Bit 1 Skip builds ( do not allow slide effects ) + // Bit 2 Use slide range + // Bit 3 Use named show + // Bit 4 Browse mode on + // Bit 5 Kiosk mode on + // Bit 6 Skip narration + // Bit 7 loop continuously + // Bit 8 show scrollbar + + if ( ImplGetPropertyValue( "CustomShow" ) ) + { + aCustomShow = *o3tl::doAccess(mAny); + if ( !aCustomShow.isEmpty() ) + { + nFlags |= 8; + } + } + if ( ( nFlags & 8 ) == 0 ) + { + if ( ImplGetPropertyValue( "FirstPage" ) ) + { + auto aSlideName = o3tl::doAccess(mAny); + + std::vector::const_iterator pIter = std::find( + maSlideNameList.begin(),maSlideNameList.end(), *aSlideName); + + if (pIter != maSlideNameList.end()) + { + nStartSlide = pIter - maSlideNameList.begin() + 1; + nFlags |= 4; + nEndSlide = static_cast(mnPages); + } + } + } + + if ( ImplGetPropertyValue( "IsAutomatic" ) ) + { + bool bBool = false; + mAny >>= bBool; + if ( !bBool ) + nFlags |= 1; + } + + if ( ImplGetPropertyValue( "IsEndless" ) ) + { + bool bBool = false; + mAny >>= bBool; + if ( bBool ) + nFlags |= 0x80; + } + if ( ImplGetPropertyValue( "IsFullScreen" ) ) + { + bool bBool = false; + mAny >>= bBool; + if ( !bBool ) + nFlags |= 0x11; + } + + mpPptEscherEx->AddAtom( 80, EPP_SSDocInfoAtom, 1 ); + mpStrm->WriteUInt32( nPenColor ).WriteInt32( nRestartTime ).WriteInt16( nStartSlide ).WriteInt16( nEndSlide ); + + sal_uInt32 nCustomShowNameLen = aCustomShow.getLength(); + if ( nCustomShowNameLen > 31 ) + nCustomShowNameLen = 31; + if ( nCustomShowNameLen ) // named show identifier + { + const sal_Unicode* pCustomShow = aCustomShow.getStr(); + for ( i = 0; i < nCustomShowNameLen; i++ ) + { + mpStrm->WriteUInt16( pCustomShow[ i ] ); + } + } + for ( i = nCustomShowNameLen; i < 32; i++, mpStrm->WriteUInt16( 0 ) ) ; + + mpStrm->WriteUInt32( nFlags ); + css::uno::Reference< css::presentation::XCustomPresentationSupplier > aXCPSup( mXModel, css::uno::UNO_QUERY ); + if ( aXCPSup.is() ) + { + css::uno::Reference< css::container::XNameContainer > aXCont( aXCPSup->getCustomPresentations() ); + if ( aXCont.is() ) + { + const css::uno::Sequence< OUString> aNameSeq( aXCont->getElementNames() ); + if ( aNameSeq.hasElements() ) + { + mpPptEscherEx->OpenContainer( EPP_NamedShows ); + sal_uInt32 nCustomShowIndex = 0; + for( OUString const & customShowName : aNameSeq ) + { + if ( !customShowName.isEmpty() ) + { + mpPptEscherEx->OpenContainer( EPP_NamedShow, nCustomShowIndex++ ); + + sal_uInt32 nNamedShowLen = customShowName.getLength(); + if ( nNamedShowLen > 31 ) + nNamedShowLen = 31; + mpPptEscherEx->AddAtom( nNamedShowLen << 1, EPP_CString ); + const sal_Unicode* pCustomShowName = customShowName.getStr(); + for ( sal_uInt32 k = 0; k < nNamedShowLen; ++k ) + mpStrm->WriteUInt16( pCustomShowName[ k ] ); + mAny = aXCont->getByName( customShowName ); + css::uno::Reference< css::container::XIndexContainer > aXIC; + if ( mAny >>= aXIC ) + { + mpPptEscherEx->BeginAtom(); + + sal_Int32 nSlideCount = aXIC->getCount(); + for ( sal_Int32 j = 0; j < nSlideCount; j++ ) // number of slides + { + mAny = aXIC->getByIndex( j ); + css::uno::Reference< css::drawing::XDrawPage > aXDrawPage; + if ( mAny >>= aXDrawPage ) + { + css::uno::Reference< css::container::XNamed > aXName( aXDrawPage, css::uno::UNO_QUERY ); + if ( aXName.is() ) + { + OUString aSlideName( aXName->getName() ); + std::vector::const_iterator pIter = std::find( + maSlideNameList.begin(),maSlideNameList.end(),aSlideName); + + if (pIter != maSlideNameList.end()) + { + sal_uInt32 nPageNumber = pIter - maSlideNameList.begin(); + mpStrm->WriteUInt32( nPageNumber + 0x100 ); // unique slide id + } + } + } + } + mpPptEscherEx->EndAtom( EPP_NamedShowSlides ); + } + mpPptEscherEx->CloseContainer(); // EPP_NamedShow + } + } + mpPptEscherEx->CloseContainer(); // EPP_NamedShows + } + } + } + } + } + } + mpPptEscherEx->AddAtom( 0, EPP_EndDocument ); + mpPptEscherEx->CloseContainer(); // EPP_Document + return true; +}; + +void PPTWriter::ImplCreateHyperBlob( SvMemoryStream& rStrm ) +{ + sal_uInt32 nCurrentOfs, nParaOfs, nParaCount = 0; + + nParaOfs = rStrm.Tell(); + rStrm.WriteUInt32( 0 ); // property size + rStrm.WriteUInt32( 0 ); // property count + + for ( const auto& rHyperlink : maHyperlink ) + { + nParaCount += 6; + rStrm .WriteUInt32( 3 ) // Type VT_I4 + .WriteUInt32( 7 ) // (VTI4 - Private1) + .WriteUInt32( 3 ) // Type VT_I4 + .WriteUInt32( 6 ) // (VTI4 - Private2) + .WriteUInt32( 3 ) // Type VT_I4 + .WriteUInt32( 0 ); // (VTI4 - Private3) + + // INFO + // HIWORD: = 0 : do not change anything + // = 1 : replace the hyperlink with the target and subaddress in the following two VTLPWSTR + // = 2 : delete the hyperlink + // LOWORD: = 0 : graphic shown as background (link) + // = 1 : graphic shown as shape (link) + // = 2 : graphic is used to fill a shape + // = 3 : graphic used to fill a shape outline (future use) + // = 4 : hyperlink attached to a shape + // = 5 : " " " " (Word) field + // = 6 : " " " " (Excel) range + // = 7 : " " " " (PPT) text range + // = 8 : " " " " (Project) task + + sal_Int32 nUrlLen = rHyperlink.aURL.getLength(); + const OUString& rUrl = rHyperlink.aURL; + + sal_uInt32 const nInfo = 7; + + rStrm .WriteUInt32( 3 ) // Type VT_I4 + .WriteUInt32( nInfo ); // Info + + switch( rHyperlink.nType & 0xff ) + { + case 1 : // click action to slidenumber + { + rStrm.WriteUInt32( 0x1f ).WriteUInt32( 1 ).WriteUInt32( 0 ); // path + rStrm.WriteUInt32( 0x1f ).WriteUInt32( nUrlLen + 1 ); + for ( sal_Int32 i = 0; i < nUrlLen; i++ ) + { + rStrm.WriteUInt16( rUrl[ i ] ); + } + rStrm.WriteUInt16( 0 ); + } + break; + case 2 : + { + sal_Int32 i; + + rStrm .WriteUInt32( 0x1f ) + .WriteUInt32( nUrlLen + 1 ); + for ( i = 0; i < nUrlLen; i++ ) + { + rStrm.WriteUInt16( rUrl[ i ] ); + } + if ( ! ( i & 1 ) ) + rStrm.WriteUInt16( 0 ); + rStrm .WriteUInt16( 0 ) + .WriteUInt32( 0x1f ) + .WriteUInt32( 1 ) + .WriteUInt32( 0 ); + } + break; + } + } + nCurrentOfs = rStrm.Tell(); + rStrm.Seek( nParaOfs ); + rStrm.WriteUInt32( nCurrentOfs - ( nParaOfs + 4 ) ); + rStrm.WriteUInt32( nParaCount ); + rStrm.Seek( nCurrentOfs ); +} + +bool PPTWriter::ImplCreateMainNotes() +{ + EscherSolverContainer aSolverContainer; + + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_MainNotes, mpStrm->Tell() ); + mpPptEscherEx->OpenContainer( EPP_Notes ); + mpPptEscherEx->AddAtom( 8, EPP_NotesAtom, 1 ); + mpStrm->WriteUInt32( 0x80000001 ) // Number that identifies this slide + .WriteUInt32( 0 ); // follow nothing + mpPptEscherEx->OpenContainer( EPP_PPDrawing ); + mpPptEscherEx->OpenContainer( ESCHER_DgContainer ); + mpPptEscherEx->EnterGroup(nullptr,nullptr); + + ImplWritePage( GetLayout( 20 ), aSolverContainer, NOTICE, true ); + + mpPptEscherEx->LeaveGroup(); + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::Background | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0xffffff ); // stock valued fill color + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectRight, 0x68bdde ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectBottom, 0x8b9f8e ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x120012 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_bWMode, ESCHER_wDontShow ); + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); // if true, this is the background shape + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + + aSolverContainer.WriteSolver( *mpStrm ); + + mpPptEscherEx->CloseContainer(); // ESCHER_DgContainer + mpPptEscherEx->CloseContainer(); // EPP_Drawing + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 1 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + mpPptEscherEx->CloseContainer(); // EPP_Notes + return true; +} + +static OUString getInitials( const OUString& rName ) +{ + OUStringBuffer sInitials; + + const sal_Unicode * pStr = rName.getStr(); + sal_Int32 nLength = rName.getLength(); + + while( nLength ) + { + // skip whitespace + while( nLength && (*pStr <= ' ') ) + { + nLength--; pStr++; + } + + // take letter + if( nLength ) + { + sInitials.append( *pStr ); + nLength--; pStr++; + } + + // skip letters until whitespace + while( nLength && (*pStr > ' ') ) + { + nLength--; pStr++; + } + } + + return sInitials.makeStringAndClear(); +} + +void ImplExportComments( const uno::Reference< drawing::XDrawPage >& xPage, SvMemoryStream& rBinaryTagData10Atom ) +{ + try + { + uno::Reference< office::XAnnotationAccess > xAnnotationAccess( xPage, uno::UNO_QUERY_THROW ); + uno::Reference< office::XAnnotationEnumeration > xAnnotationEnumeration( xAnnotationAccess->createAnnotationEnumeration() ); + + sal_Int32 nIndex = 1; + + while( xAnnotationEnumeration->hasMoreElements() ) + { + EscherExContainer aComment10( rBinaryTagData10Atom, EPP_Comment10 ); + { + uno::Reference< office::XAnnotation > xAnnotation( xAnnotationEnumeration->nextElement() ); + + geometry::RealPoint2D aRealPoint2D( xAnnotation->getPosition() ); + Point aPoint(o3tl::convert(aRealPoint2D.X, o3tl::Length::mm, o3tl::Length::master), + o3tl::convert(aRealPoint2D.Y, o3tl::Length::mm, o3tl::Length::master)); + + OUString sAuthor( xAnnotation->getAuthor() ); + uno::Reference< text::XText > xText( xAnnotation->getTextRange() ); + OUString sText( xText->getString() ); + OUString sInitials( getInitials( sAuthor ) ); + util::DateTime aDateTime( xAnnotation->getDateTime() ); + if ( !sAuthor.isEmpty() ) + PPTWriter::WriteCString( rBinaryTagData10Atom, sAuthor ); + if ( !sText.isEmpty() ) + PPTWriter::WriteCString( rBinaryTagData10Atom, sText, 1 ); + if ( !sInitials.isEmpty() ) + PPTWriter::WriteCString( rBinaryTagData10Atom, sInitials, 2 ); + + sal_Int16 nMilliSeconds = static_cast(::rtl::math::round(static_cast(aDateTime.NanoSeconds) / 1000000000.0)); + EscherExAtom aCommentAtom10( rBinaryTagData10Atom, EPP_CommentAtom10 ); + rBinaryTagData10Atom.WriteInt32( nIndex++ ) + .WriteInt16( aDateTime.Year ) + .WriteUInt16( aDateTime.Month ) + .WriteUInt16( aDateTime.Day ) // todo: day of week + .WriteUInt16( aDateTime.Day ) + .WriteUInt16( aDateTime.Hours ) + .WriteUInt16( aDateTime.Minutes ) + .WriteUInt16( aDateTime.Seconds ) + .WriteInt16( nMilliSeconds ) + .WriteInt32( aPoint.X() ) + .WriteInt32( aPoint.Y() ); + } + } + } + catch ( uno::Exception& ) + { + } +} + +void PPTWriter::ImplWriteNotes( sal_uInt32 nPageNum ) +{ + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_Notes | nPageNum, mpStrm->Tell() ); + mpPptEscherEx->OpenContainer( EPP_Notes ); + mpPptEscherEx->AddAtom( 8, EPP_NotesAtom, 1 ); + mpStrm->WriteUInt32( nPageNum + 0x100 ) + .WriteUInt16( 3 ) // follow master... + .WriteUInt16( 0 ); + + ImplCreateHeaderFooters( mXPagePropSet ); + + EscherSolverContainer aSolverContainer; + + mpPptEscherEx->OpenContainer( EPP_PPDrawing ); + mpPptEscherEx->OpenContainer( ESCHER_DgContainer ); + mpPptEscherEx->EnterGroup(nullptr,nullptr); + + ImplWritePage( GetLayout( 20 ), aSolverContainer, NOTICE, false ); // the shapes of the pages are created in the PPT document + + mpPptEscherEx->LeaveGroup(); + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::Background | ShapeFlag::HaveShapeProperty ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0xffffff ); // stock valued fill color + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectRight, 0x8b9f8e ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectBottom, 0x68bdde ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x120012 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + aPropOpt.AddOpt( ESCHER_Prop_bWMode, ESCHER_wDontShow ); + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + + aSolverContainer.WriteSolver( *mpStrm ); + + mpPptEscherEx->CloseContainer(); // ESCHER_DgContainer + mpPptEscherEx->CloseContainer(); // EPP_Drawing + mpPptEscherEx->AddAtom( 32, EPP_ColorSchemeAtom, 0, 1 ); + mpStrm->WriteUInt32( 0xffffff ).WriteUInt32( 0x000000 ).WriteUInt32( 0x808080 ).WriteUInt32( 0x000000 ).WriteUInt32( 0x99cc00 ).WriteUInt32( 0xcc3333 ).WriteUInt32( 0xffcccc ).WriteUInt32( 0xb2b2b2 ); + mpPptEscherEx->CloseContainer(); // EPP_Notes +}; + +void PPTWriter::ImplWriteBackground( css::uno::Reference< css::beans::XPropertySet > const & rXPropSet ) +{ + //************************ ****** + //** DEFAULT BACKGROUND SHAPE ** + + sal_uInt32 nFillColor = 0xffffff; + sal_uInt32 nFillBackColor = 0; + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::Background | ShapeFlag::HaveShapeProperty ); + + // #i121183# Use real PageSize in 100th mm + ::tools::Rectangle aRect(Point(0, 0), Size(maPageSize.Width, maPageSize.Height)); + + EscherPropertyContainer aPropOpt( mpPptEscherEx->GetGraphicProvider(), mpPicStrm.get(), aRect ); + aPropOpt.AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid ); + css::drawing::FillStyle aFS( css::drawing::FillStyle_NONE ); + if ( ImplGetPropertyValue( rXPropSet, "FillStyle" ) ) + mAny >>= aFS; + + switch( aFS ) + { + case css::drawing::FillStyle_GRADIENT : + { + aPropOpt.CreateGradientProperties( rXPropSet ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x1f001e ); + aPropOpt.GetOpt( ESCHER_Prop_fillColor, nFillColor ); + aPropOpt.GetOpt( ESCHER_Prop_fillBackColor, nFillBackColor ); + } + break; + + case css::drawing::FillStyle_BITMAP : + aPropOpt.CreateGraphicProperties( rXPropSet, "FillBitmap", true ); + break; + + case css::drawing::FillStyle_HATCH : + aPropOpt.CreateGraphicProperties( rXPropSet, "FillHatch", true ); + break; + + case css::drawing::FillStyle_SOLID : + { + if ( ImplGetPropertyValue( rXPropSet, "FillColor" ) ) + { + nFillColor = EscherEx::GetColor( *o3tl::doAccess(mAny) ); + nFillBackColor = nFillColor ^ 0xffffff; + } + [[fallthrough]]; + } + case css::drawing::FillStyle_NONE : + default: + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x120012 ); + break; + } + aPropOpt.AddOpt( ESCHER_Prop_fillColor, nFillColor ); + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, nFillBackColor ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectRight, PPTtoEMU( maDestPageSize.Width ) ); + aPropOpt.AddOpt( ESCHER_Prop_fillRectBottom, PPTtoEMU( maDestPageSize.Height ) ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + aPropOpt.AddOpt( ESCHER_Prop_bWMode, ESCHER_bwWhite ); + aPropOpt.AddOpt( ESCHER_Prop_fBackground, 0x10001 ); + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer +} + +void PPTWriter::ImplWriteVBA() +{ + if ( mpVBA ) + { + sal_uInt32 nLen = mpVBA->TellEnd(); + if ( nLen > 8 ) + { + nLen -= 8; + mnVBAOleOfs = mpStrm->Tell(); + mpPptEscherEx->BeginAtom(); + mpStrm->WriteBytes(static_cast(mpVBA->GetData()) + 8, nLen); + mpPptEscherEx->EndAtom( EPP_ExOleObjStg, 0, 1 ); + } + } +} + +void PPTWriter::ImplWriteOLE( ) +{ + + SvxMSExportOLEObjects aOleExport( mnCnvrtFlags ); + + for ( const auto& rxExOleObjEntry : maExOleObj ) + { + PPTExOleObjEntry* pPtr = rxExOleObjEntry.get(); + std::unique_ptr pStrm; + pPtr->nOfsB = mpStrm->Tell(); + switch ( pPtr->eType ) + { + case NORMAL_OLE_OBJECT : + { + SdrObject* pSdrObj = SdrObject::getSdrObjectFromXShape(pPtr->xShape); + if ( auto pSdrOle2Obj = dynamic_cast< SdrOle2Obj* >(pSdrObj) ) + { + const ::uno::Reference < embed::XEmbeddedObject >& xObj( pSdrOle2Obj->GetObjRef() ); + if( xObj.is() ) + { + tools::SvRef xTempStorage( new SotStorage( new SvMemoryStream(), true ) ); + aOleExport.ExportOLEObject( xObj, *xTempStorage ); + + //TODO/MBA: testing + SvMemoryStream aStream; + tools::SvRef xCleanStorage( new SotStorage( false, aStream ) ); + xTempStorage->CopyTo( xCleanStorage.get() ); + // create a dummy content stream, the dummy content is necessary for ppt, but not for + // doc files, so we can't share code. + tools::SvRef xStm = xCleanStorage->OpenSotStream( SVEXT_PERSIST_STREAM ); + xStm->WriteUInt32( 0 ) // no ClipboardId + .WriteUInt32( 4 ) // no target device + .WriteUInt32( 1 ) // aspect ratio + .WriteInt32( -1 ) // L-Index + .WriteUInt32( 0 ) // Advanced Flags + .WriteUInt32( 0 ) // compression + .WriteUInt32( 0 ) // Size + .WriteUInt32( 0 ) // " + .WriteUInt32( 0 ); + pStrm = xCleanStorage->CreateMemoryStream(); + } + } + } + break; + + case OCX_CONTROL : + { + if ( pPtr->xControlModel.is() ) + { + OUString aName; + //Initialize the graphic size which will be used on export + css::awt::Size aSize( pPtr->xShape->getSize() ); + tools::SvRef xDest( new SotStorage( new SvMemoryStream(), true ) ); + bool bOk = oox::ole::MSConvertOCXControls::WriteOCXStream( mXModel, xDest, pPtr->xControlModel, aSize, aName ); + if ( bOk ) + pStrm = xDest->CreateMemoryStream(); + } + } + } + if ( pStrm ) + { + mpPptEscherEx->BeginAtom(); + pStrm->Seek( STREAM_SEEK_TO_END ); + sal_uInt32 npStrmSize = pStrm->Tell(); + mpStrm->WriteUInt32( npStrmSize ); // uncompressed size + + pStrm->Seek( 0 ); + ZCodec aZCodec( 0x8000, 0x8000 ); + aZCodec.BeginCompression(); + aZCodec.Compress( *pStrm, *mpStrm ); + aZCodec.EndCompression(); + pStrm.reset(); + mpPptEscherEx->EndAtom( EPP_ExOleObjStg, 0, 1 ); + } + } +} + +// write PersistantTable and UserEditAtom + +void PPTWriter::ImplWriteAtomEnding() +{ + +#define EPP_LastViewTypeSlideView 1 + + sal_uInt32 i, nPos, nOfs, nPersistOfs = mpStrm->Tell(); + sal_uInt32 nPersistEntrys = 0; + mpStrm->WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ); // skip record header and first entry + + // write document persist + nPersistEntrys++; + mpStrm->WriteUInt32( 0 ); + // write MasterPages persists + for ( i = 0; i < mnMasterPages; i++ ) + { + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_MainMaster | i ); + if ( nOfs ) + { + mpStrm->WriteUInt32( nOfs ); + mpPptEscherEx->InsertAtPersistOffset( EPP_MAINMASTER_PERSIST_KEY | i, ++nPersistEntrys ); + } + } + // write MainNotesMaster persist + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_MainNotes ); + if ( nOfs ) + { + mpStrm->WriteUInt32( nOfs ); + mpPptEscherEx->InsertAtPersistOffset( EPP_MAINNOTESMASTER_PERSIST_KEY, ++nPersistEntrys ); + } + // write slide persists -> we have to write a valid value into EPP_SlidePersistAtome too + for ( i = 0; i < mnPages; i++ ) + { + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_Slide | i ); + if ( nOfs ) + { + mpStrm->WriteUInt32( nOfs ); + mpPptEscherEx->InsertAtPersistOffset( EPP_MAINSLIDE_PERSIST_KEY | i, ++nPersistEntrys ); + } + } + // write Notes persists + for ( i = 0; i < mnPages; i++ ) + { + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_Notes | i ); + if ( nOfs ) + { + mpStrm->WriteUInt32( nOfs ); + mpPptEscherEx->InsertAtPersistOffset( EPP_MAINNOTES_PERSIST_KEY | i, ++nPersistEntrys ); + } + } + // Ole persists + for ( const auto& rxExOleObjEntry : maExOleObj ) + { + PPTExOleObjEntry* pPtr = rxExOleObjEntry.get(); + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_ExObj ); + if ( nOfs ) + { + nPersistEntrys++; + mpStrm->WriteUInt32( pPtr->nOfsB ); + sal_uInt32 nOldPos, nPersOfs = nOfs + pPtr->nOfsA + 16 + 8; // 8 bytes atom header, +16 to the persist entry + nOldPos = mpStrm->Tell(); + mpStrm->Seek( nPersOfs ); + mpStrm->WriteUInt32( nPersistEntrys ); + mpStrm->Seek( nOldPos ); + } + } + // VB persist + if ( mnVBAOleOfs && mpVBA ) + { + nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_VBAInfoAtom ); + if ( nOfs ) + { + nPersistEntrys++; + sal_uInt32 n1, n2; + + mpVBA->Seek( 0 ); + mpVBA->ReadUInt32( n1 ) + .ReadUInt32( n2 ); + + mpStrm->WriteUInt32( mnVBAOleOfs ); + sal_uInt32 nOldPos = mpStrm->Tell(); + mpStrm->Seek( nOfs ); // Fill the VBAInfoAtom with the correct index to the persisttable + mpStrm->WriteUInt32( nPersistEntrys ) + .WriteUInt32( n1 ) + .WriteInt32( 2 ); + mpStrm->Seek( nOldPos ); + + } + } + nPos = mpStrm->Tell(); + mpStrm->Seek( nPersistOfs ); + mpPptEscherEx->AddAtom( ( nPersistEntrys + 1 ) << 2, EPP_PersistPtrIncrementalBlock ); // insert Record Header + mpStrm->WriteUInt32( ( nPersistEntrys << 20 ) | 1 ); + mpStrm->Seek( nPos ); + + mpCurUserStrm->WriteUInt32( nPos ); // set offset to current edit + mpPptEscherEx->AddAtom( 28, EPP_UserEditAtom ); + mpStrm->WriteInt32( 0x100 ) // last slide ID + .WriteUInt32( 0x03000dbc ) // minor and major app version that did the save + .WriteUInt32( 0 ) // offset last save, 0 after a full save + .WriteUInt32( nPersistOfs ) // File offset to persist pointers for this save operation + .WriteUInt32( 1 ) // Persist reference to the document persist object + .WriteUInt32( nPersistEntrys ) // max persists written, Seed value for persist object id management + .WriteInt16( EPP_LastViewTypeSlideView ) // last view type + .WriteInt16( 0x12 ); // padword +} + +// - exported function - + +extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool ExportPPT( const std::vector< css::beans::PropertyValue >& rMediaData, + tools::SvRef const & rSvStorage, + css::uno::Reference< css::frame::XModel > const & rXModel, + css::uno::Reference< css::task::XStatusIndicator > const & rXStatInd, + SvMemoryStream* pVBA, + sal_uInt32 nCnvrtFlags ) +{ + PPTWriter aPPTWriter( rSvStorage, rXModel, rXStatInd, pVBA, nCnvrtFlags ); + aPPTWriter.exportPPT(rMediaData); + bool bStatus = aPPTWriter.IsValid(); + return bStatus; +} + +extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool SaveVBA( SfxObjectShell& rDocShell, SvMemoryStream*& pBas ) +{ + tools::SvRef xDest( new SotStorage( new SvMemoryStream(), true ) ); + SvxImportMSVBasic aMSVBas( rDocShell, *xDest ); + aMSVBas.SaveOrDelMSVBAStorage( true, "_MS_VBA_Overhead" ); + + tools::SvRef xOverhead = xDest->OpenSotStorage( "_MS_VBA_Overhead" ); + if ( xOverhead.is() && ( xOverhead->GetError() == ERRCODE_NONE ) ) + { + tools::SvRef xOverhead2 = xOverhead->OpenSotStorage( "_MS_VBA_Overhead" ); + if ( xOverhead2.is() && ( xOverhead2->GetError() == ERRCODE_NONE ) ) + { + tools::SvRef xTemp = xOverhead2->OpenSotStream( "_MS_VBA_Overhead2" ); + if ( xTemp.is() && ( xTemp->GetError() == ERRCODE_NONE ) ) + { + sal_uInt32 nLen = xTemp->GetSize(); + if ( nLen ) + { + char* pTemp = new char[ nLen ]; + xTemp->Seek( STREAM_SEEK_TO_BEGIN ); + xTemp->ReadBytes(pTemp, nLen); + pBas = new SvMemoryStream( pTemp, nLen, StreamMode::READ ); + pBas->ObjectOwnsMemory( true ); + return true; + } + } + } + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/eppt.hxx b/sd/source/filter/eppt/eppt.hxx new file mode 100644 index 000000000..eec839290 --- /dev/null +++ b/sd/source/filter/eppt/eppt.hxx @@ -0,0 +1,232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once +#include +#include +#include "escherex.hxx" +#include +#include +#include "pptexsoundcollection.hxx" + +#include "text.hxx" + +#include +#include + +#include "epptbase.hxx" + +namespace com::sun::star::awt { class XControlModel; } +namespace com::sun::star::beans { class XPropertySet; } +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::drawing { class XShape; } +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::task { class XStatusIndicator; } +namespace com::sun::star::text { class XSimpleText; } + +class SotStorage; + +#define EPP_MAINMASTER_PERSIST_KEY 0x80010000 +#define EPP_MAINNOTESMASTER_PERSIST_KEY 0x80020000 +#define EPP_MAINSLIDE_PERSIST_KEY 0x80030000 +#define EPP_MAINNOTES_PERSIST_KEY 0x80040000 + +#define EPP_Persist_Document 0x80080000 +#define EPP_Persist_MainMaster 0x80100000 +#define EPP_Persist_MainNotes 0x80200000 +#define EPP_Persist_Slide 0x80400000 +#define EPP_Persist_Notes 0x80800000 +#define EPP_Persist_CurrentPos 0x81000000 +#define EPP_Persist_VBAInfoAtom 0x84000000 +#define EPP_Persist_ExObj 0x88000000 + +#define EPP_TEXTSTYLE_NORMAL 0x00000001 +#define EPP_TEXTSTYLE_TITLE 0x00000010 +#define EPP_TEXTSTYLE_BODY 0x00000100 +#define EPP_TEXTSTYLE_TEXT 0x00001000 + +struct EPPTHyperlink +{ + OUString aURL; + sal_uInt32 nType; // bit 0-7 : type ( 1: click action to a slide ) + // ( 2: hyperlink url ) + // bit 8-23: index + // bit 31 : hyperlink is attached to a shape + + EPPTHyperlink( const OUString& rURL, sal_uInt32 nT ) : + aURL ( rURL ), + nType ( nT ){}; +}; + +enum PPTExOleObjEntryType +{ + NORMAL_OLE_OBJECT, OCX_CONTROL +}; + +struct PPTExOleObjEntry +{ + PPTExOleObjEntryType eType; + sal_uInt32 nOfsA; ///< offset to the EPP_ExOleObjAtom in mpExEmbed (set at creation) + sal_uInt32 nOfsB; ///< offset to the EPP_ExOleObjStg + + css::uno::Reference< css::awt::XControlModel > xControlModel; + css::uno::Reference< css::drawing::XShape > xShape; + + PPTExOleObjEntry(PPTExOleObjEntryType eT, sal_uInt32 nOfs) + : eType(eT) + , nOfsA(nOfs) + , nOfsB(0) + {} +}; + +struct TextRuleEntry +{ + std::unique_ptr pOut; +}; + +class TextObjBinary : public TextObj +{ +public: + TextObjBinary( css::uno::Reference< css::text::XSimpleText > const & rXText, + int nInstance, FontCollection& rFontCollection, PPTExBulletProvider& rBuProv ) : TextObj( rXText, nInstance, rFontCollection, rBuProv ) {} + void Write( SvStream* pStrm ); + void WriteTextSpecInfo( SvStream* pStrm ); +}; + +struct CellBorder; +class PPTWriter final : public PPTWriterBase, public PPTExBulletProvider +{ + sal_uInt32 mnCnvrtFlags; + bool mbStatus; + sal_uInt32 mnStatMaxValue; + sal_uInt32 mnLatestStatValue; + + std::vector maSlideNameList; + OUString maBaseURI; + + css::uno::Reference< css::text::XSimpleText > mXText; // TextRef of the global text + sal_uInt32 mnTextStyle; + + bool mbFontIndependentLineSpacing; + sal_uInt32 mnTextSize; + + tools::SvRef mrStg; + tools::SvRef mpCurUserStrm; + tools::SvRef mpStrm; + tools::SvRef mpPicStrm; + std::unique_ptr mpPptEscherEx; + + std::vector> maExOleObj; + sal_uInt32 mnVBAOleOfs; + SvMemoryStream* mpVBA; + sal_uInt32 mnExEmbed; + std::unique_ptr mpExEmbed; + + sal_uInt32 mnPagesWritten; + sal_uInt32 mnTxId; // Identifier determined by the HOST (PP) ???? + sal_uInt32 mnDiaMode; // 0 -> manual + // 1 -> semi-automatic + // 2 -> automatic + + sal_uInt32 mnShapeMasterTitle; + sal_uInt32 mnShapeMasterBody; + + std::vector maHyperlink; + + ppt::ExSoundCollection maSoundCollection; + + void ImplWriteExtParaHeader( SvMemoryStream& rSt, sal_uInt32 nRef, sal_uInt32 nInstance, sal_uInt32 nSlideId ); + + sal_uInt32 ImplProgBinaryTag( SvStream* pOutStrm ); + sal_uInt32 ImplProgBinaryTagContainer( SvStream* pOutStrm, SvMemoryStream* pBinTag ); + sal_uInt32 ImplProgTagContainer( SvStream* pOutStrm, SvMemoryStream* pBinTag = nullptr ); + static sal_uInt32 ImplOutlineViewInfoContainer( SvStream* pOutStrm ); + static sal_uInt32 ImplSlideViewInfoContainer( sal_uInt32 nInstance, SvStream* pOutStrm ); + sal_uInt32 ImplVBAInfoContainer( SvStream* pOutStrm ); + sal_uInt32 ImplDocumentListContainer( SvStream* pOutStrm ); + sal_uInt32 ImplMasterSlideListContainer( SvStream* pOutStrm ); + + public: + static void WriteCString( SvStream&, const OUString&, sal_uInt32 nInstance = 0 ); + + private: + + void ImplCreateDocumentSummaryInformation(); + bool ImplCreateCurrentUserStream(); + static void ImplCreateHeaderFooterStrings( SvStream& rOut, + css::uno::Reference< css::beans::XPropertySet > const & rXPagePropSet ); + void ImplCreateHeaderFooters( css::uno::Reference< css::beans::XPropertySet > const & rXPagePropSet ); + virtual bool ImplCreateDocument() override; + void ImplCreateHyperBlob( SvMemoryStream& rStream ); + sal_uInt32 ImplInsertBookmarkURL( const OUString& rBookmark, const sal_uInt32 nType, + const OUString& rStringVer0, const OUString& rStringVer1, const OUString& rStringVer2, const OUString& rStringVer3 ); + virtual bool ImplCreateMainNotes() override; + void ImplWriteBackground( css::uno::Reference< css::beans::XPropertySet > const & rXBackgroundPropSet ); + void ImplWriteVBA(); + void ImplWriteOLE(); + void ImplWriteAtomEnding(); + + void ImplFlipBoundingBox( EscherPropertyContainer& rPropOpt ); + bool ImplGetText(); + bool ImplCreatePresentationPlaceholder( const bool bMaster, + const sal_uInt32 StyleInstance, const sal_uInt8 PlaceHolderId ); + static bool ImplGetEffect( const css::uno::Reference< css::beans::XPropertySet > &, + css::presentation::AnimationEffect& eEffect, + css::presentation::AnimationEffect& eTextEffect, + bool& bHasSound ); + void ImplWriteClickAction( SvStream& rSt, css::presentation::ClickAction eAction, bool bMediaClickAction ); + void ImplWriteParagraphs( SvStream& rOutStrm, TextObj& rTextObj ); + void ImplWritePortions( SvStream& rOutStrm, TextObj& rTextObj ); + void ImplWriteTextStyleAtom( SvStream& rOut, int nTextInstance, sal_uInt32 nAtomInstance, + TextRuleEntry* pTextRule, SvStream& rExtBu, EscherPropertyContainer* ); + void ImplAdjustFirstLineLineSpacing( TextObj& rTextObj, EscherPropertyContainer& rPropOpt ); + void ImplCreateShape( sal_uInt32 nType, ShapeFlag nFlags, EscherSolverContainer& ); + void ImplCreateTextShape( EscherPropertyContainer&, EscherSolverContainer&, bool bFill ); + + void ImplWritePage( const PHLayout& rLayout, + EscherSolverContainer& rSolver, + PageType ePageType, + bool bMaster, + int nPageNumber = 0 ); + bool ImplCreateCellBorder( const CellBorder* pCellBorder, sal_Int32 nX1, sal_Int32 nY1, sal_Int32 nX2, sal_Int32 nY2 ); + void ImplCreateTable( css::uno::Reference< css::drawing::XShape > const & rXShape, EscherSolverContainer& aSolverContainer, + EscherPropertyContainer& aPropOpt ); + + bool ImplCloseDocument(); // we write the font, hyper and sound list + + virtual void ImplWriteSlide( sal_uInt32 nPageNum, sal_uInt32 nMasterID, sal_uInt16 nMode, + bool bHasBackground, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override; + virtual void ImplWriteNotes( sal_uInt32 nPageNum ) override; + virtual void ImplWriteSlideMaster( sal_uInt32 nPageNum, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override; + + public: + PPTWriter( tools::SvRef const & rSvStorage, + css::uno::Reference< css::frame::XModel > const & rModel, + css::uno::Reference< css::task::XStatusIndicator > const & rStatInd, + SvMemoryStream* pVBA, sal_uInt32 nCnvrtFlags ); + + virtual ~PPTWriter() override; + + bool IsValid() const { return mbStatus; }; + + virtual void exportPPTPre( const std::vector< css::beans::PropertyValue >& ) override; + virtual void exportPPTPost( ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/epptbase.hxx b/sd/source/filter/eppt/epptbase.hxx new file mode 100644 index 000000000..e8ac992e3 --- /dev/null +++ b/sd/source/filter/eppt/epptbase.hxx @@ -0,0 +1,412 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "grouptable.hxx" + +namespace com::sun::star::task { class XStatusIndicator; } +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::awt { struct Rectangle; } +namespace com::sun::star::drawing { class XMasterPagesSupplier; } +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XDrawPages; } +namespace com::sun::star::drawing { class XDrawPagesSupplier; } +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::beans { class XPropertySet; } +namespace com::sun::star::drawing { class XShape; } +namespace com::sun::star::drawing { class XShapes; } + +class VirtualDevice; + +// PLACEMENT_ID +enum class EppLayout +{ + TITLESLIDE = 0, /* The slide is a title slide */ + TITLEANDBODYSLIDE = 1, /* Title and body slide */ + TITLEMASTERSLIDE = 2, /* Title master slide */ + MASTERSLIDE = 3, /* Master slide layout */ + MASTERNOTES = 4, /* Master notes layout */ + NOTESTITLEBODY = 5, /* Notes title/body layout */ + HANDOUTLAYOUT = 6, /* Handout layout, therefore it doesn't have placeholders except header, footer, and date */ + ONLYTITLE = 7, /* Only title placeholder */ + TWOCOLUMNSANDTITLE = 8, /* Body of the slide has 2 columns and a title */ + TWOROWSANDTITLE = 9, /* Slide's body has 2 rows and a title */ + RIGHTCOLUMN2ROWS = 10, /* Body contains 2 columns, right column has 2 rows */ + LEFTCOLUMN2ROWS = 11, /* Body contains 2 columns, left column has 2 rows */ + BOTTOMROW2COLUMNS = 12, /* Body contains 2 rows, bottom row has 2 columns */ + TOPROW2COLUMN = 13, /* Body contains 2 rows, top row has 2 columns */ + FOUROBJECTS = 14, /* 4 objects */ + BIGOBJECT = 15, /* Big object */ + BLANKSLIDE = 16, /* Blank slide */ + TITLERIGHTBODYLEFT = 17, /* Vertical title on the right, body on the left */ + TITLERIGHT2BODIESLEFT = 18 /* Vertical title on the right, body on the left split into 2 rows */ +}; + +#define EPP_LAYOUT_SIZE 25 + +struct PHLayout +{ + EppLayout nLayout; + sal_uInt8 nPlaceHolder[ 8 ]; + + sal_uInt8 nUsedObjectPlaceHolder; + sal_uInt8 nTypeOfTitle; + sal_uInt8 nTypeOfOutliner; + + bool bTitlePossible; + bool bOutlinerPossible; + bool bSecOutlinerPossible; +}; + +enum PageType { NORMAL = 0, MASTER = 1, NOTICE = 2, UNDEFINED = 3, LAYOUT = 4 }; + +class PropValue +{ + protected: + + css::uno::Any mAny; + css::uno::Reference< css::beans::XPropertySet > mXPropSet; + + bool ImplGetPropertyValue( const OUString& rString ); + bool ImplGetPropertyValue( const css::uno::Reference + < css::beans::XPropertySet > &, const OUString& ); + + public: + + PropValue() {} + + static bool GetPropertyValue( + css::uno::Any& rAny, + const css::uno::Reference< css::beans::XPropertySet > &, + const OUString& rPropertyName, + bool bTestPropertyAvailability = false ); + + static css::beans::PropertyState GetPropertyState( + const css::uno::Reference < css::beans::XPropertySet > &, + const OUString& rPropertyName ); +}; + +class EscherGraphicProvider; +class PPTExBulletProvider +{ + friend struct PPTExParaSheet; + + protected: + + SvMemoryStream aBuExPictureStream; + SvMemoryStream aBuExOutlineStream; + SvMemoryStream aBuExMasterStream; + + std::unique_ptr + pGraphicProv; + + public: + + sal_uInt16 GetId(Graphic const & rGraphic, Size& rGraphicSize); + + PPTExBulletProvider(); + ~PPTExBulletProvider(); +}; + +struct FontCollectionEntry +{ + OUString Name; + double Scaling; + sal_Int16 Family; + sal_Int16 Pitch; + sal_Int16 CharSet; + + OUString Original; + + FontCollectionEntry( const OUString& rName, sal_Int16 nFamily, sal_Int16 nPitch, sal_Int16 nCharSet ) : + Scaling ( 1.0 ), + Family ( nFamily ), + Pitch ( nPitch ), + CharSet ( nCharSet ), + Original( rName ) + { + ImplInit( rName ); + }; + + explicit FontCollectionEntry( const OUString& rName ) : + Scaling ( 1.0 ), + Family ( 0 ), + Pitch ( 0 ), + CharSet ( 0 ), + Original( rName ) + { + ImplInit( rName ); + }; + + private: + + void ImplInit( const OUString& rName ); +}; + +class FontCollection +{ +public: + + FontCollection(); + + ~FontCollection(); + + static short GetScriptDirection( std::u16string_view rText ); + + sal_uInt32 GetId( FontCollectionEntry& rFontDescriptor ); + + sal_uInt32 GetCount() const { return maFonts.size(); }; + + const FontCollectionEntry* GetById( sal_uInt32 nId ); + + FontCollectionEntry& GetLast() { return *(maFonts.rbegin()); }; + +private: + + VclPtr pVDev; + std::vector maFonts; +}; + +#define PPTEX_STYLESHEETENTRIES 9 + +enum PPTExTextAttr +{ + ParaAttr_BulletOn, + ParaAttr_BuHardFont, + ParaAttr_BuHardColor, + ParaAttr_BuHardHeight, + ParaAttr_BulletChar, + ParaAttr_BulletFont, + ParaAttr_BulletHeight, + ParaAttr_BulletColor, + ParaAttr_Adjust, + ParaAttr_LineFeed, + ParaAttr_UpperDist, + ParaAttr_LowerDist, + ParaAttr_TextOfs, + ParaAttr_BulletOfs, + ParaAttr_DefaultTab, + ParaAttr_BiDi, + CharAttr_Bold, + CharAttr_Italic, + CharAttr_Underline, + CharAttr_Shadow, + CharAttr_Strikeout, + CharAttr_Embossed, + CharAttr_Font, + CharAttr_AsianOrComplexFont, + CharAttr_Symbol, + CharAttr_FontHeight, + CharAttr_FontColor, + CharAttr_Escapement +}; + +struct PPTExCharLevel +{ + sal_uInt16 mnFlags; + sal_uInt16 mnFont; + sal_uInt16 mnAsianOrComplexFont; + sal_uInt16 mnFontHeight; + sal_uInt16 mnEscapement; + Color mnFontColor; +}; + +struct PPTExCharSheet +{ + PPTExCharLevel maCharLevel[ 5 ]; + + explicit PPTExCharSheet( int nInstance ); + + void SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > &, + FontCollection& rFontCollection, int nLevel ); + void Write( SvStream& rSt, sal_uInt16 nLev, bool bSimpleText, + const css::uno::Reference< css::beans::XPropertySet > & rPagePropSet ); + +}; + +struct PPTExParaLevel +{ + bool mbIsBullet; + sal_uInt16 mnBulletChar; + sal_uInt16 mnBulletFont; + sal_uInt16 mnBulletHeight; + sal_uInt32 mnBulletColor; + + sal_uInt16 mnAdjust; + sal_uInt16 mnLineFeed; + sal_uInt16 mnUpperDist; + sal_uInt16 mnLowerDist; + sal_uInt16 mnTextOfs; + sal_uInt16 mnBulletOfs; + sal_uInt16 mnDefaultTab; + + bool mbExtendedBulletsUsed; + sal_uInt16 mnBulletId; + sal_uInt16 mnBulletStart; + sal_uInt32 mnMappedNumType; + sal_uInt32 mnNumberingType; + sal_uInt16 mnAsianSettings; + sal_uInt16 mnBiDi; +}; + +struct PPTExParaSheet +{ + PPTExBulletProvider* pBuProv; + + sal_uInt32 mnInstance; + + PPTExParaLevel maParaLevel[ 5 ]; + PPTExParaSheet( int nInstance, sal_uInt16 nDefaultTab, PPTExBulletProvider* pProv ); + + void SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > &, + FontCollection& rFontCollection, int nLevel, const PPTExCharLevel& rCharLevel ); + void Write( SvStream& rSt, sal_uInt16 nLev, bool bSimpleText, + const css::uno::Reference< css::beans::XPropertySet > & rPagePropSet ); +}; + +class PPTExStyleSheet +{ + + public: + + std::unique_ptr mpCharSheet[ PPTEX_STYLESHEETENTRIES ]; + std::unique_ptr mpParaSheet[ PPTEX_STYLESHEETENTRIES ]; + + PPTExStyleSheet( sal_uInt16 nDefaultTab, PPTExBulletProvider* pBuProv ); + ~PPTExStyleSheet(); + + PPTExParaSheet& GetParaSheet( int nInstance ) { return *mpParaSheet[ nInstance ]; }; + + void SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > &, + FontCollection& rFontCollection, int nInstance, int nLevel ); + bool IsHardAttribute( sal_uInt32 nInstance, sal_uInt32 nLevel, PPTExTextAttr eAttr, sal_uInt32 nValue ); + + static sal_uInt32 SizeOfTxCFStyleAtom() { return 24; } + void WriteTxCFStyleAtom( SvStream& rSt ); +}; + +class PPTWriterBase : public PropValue, public GroupTable +{ +protected: + css::uno::Reference< css::frame::XModel > mXModel; + css::uno::Reference< css::task::XStatusIndicator > mXStatusIndicator; + + bool mbStatusIndicator; + + css::uno::Reference< css::drawing::XDrawPagesSupplier > mXDrawPagesSupplier; + css::uno::Reference< css::drawing::XMasterPagesSupplier > mXMasterPagesSupplier; + css::uno::Reference< css::drawing::XDrawPages > mXDrawPages; + css::uno::Reference< css::drawing::XDrawPage > mXDrawPage; + css::uno::Reference< css::beans::XPropertySet > mXPagePropSet; + css::uno::Reference< css::beans::XPropertySet > mXBackgroundPropSet; + css::uno::Reference< css::drawing::XShapes > mXShapes; + css::uno::Reference< css::drawing::XShape > mXShape; + css::awt::Size maSize; + css::awt::Point maPosition; + ::tools::Rectangle maRect; + OString mType; + bool mbPresObj; + bool mbEmptyPresObj; + bool mbIsBackgroundDark; + sal_Int32 mnAngle; + + sal_uInt32 mnPages; ///< number of Slides ( w/o master pages & notes & handout ) + sal_uInt32 mnMasterPages; + + Fraction maFraction; + MapMode maMapModeSrc; + MapMode maMapModeDest; + css::awt::Size maDestPageSize; + css::awt::Size maPageSize; // #i121183# Keep size in logic coordinates (100th mm) + css::awt::Size maNotesPageSize; + + PageType meLatestPageType; + std::vector< std::unique_ptr > maStyleSheetList; + PPTExStyleSheet* mpStyleSheet; + + FontCollection maFontCollection; + + virtual void ImplWriteSlide( sal_uInt32 /* nPageNum */, sal_uInt32 /* nMasterNum */, sal_uInt16 /* nMode */, + bool /* bHasBackground */, css::uno::Reference< css::beans::XPropertySet > const & /* aXBackgroundPropSet */ ) {} + virtual void ImplWriteNotes( sal_uInt32 nPageNum ) = 0; + virtual void ImplWriteSlideMaster( sal_uInt32 /* nPageNum */, css::uno::Reference< css::beans::XPropertySet > const & /* aXBackgroundPropSet */ ) {} + + virtual void exportPPTPre( const std::vector< css::beans::PropertyValue >& ) {} + virtual void exportPPTPost() {} + + virtual bool ImplCreateDocument()=0; + virtual bool ImplCreateMainNotes()=0; + + bool GetStyleSheets(); + bool GetShapeByIndex( sal_uInt32 nIndex, bool bGroup ); + + bool CreateMainNotes(); + + css::awt::Size MapSize( const css::awt::Size& ); + css::awt::Point MapPoint( const css::awt::Point& ); + ::tools::Rectangle MapRectangle( const css::awt::Rectangle& ); + + bool ContainsOtherShapeThanPlaceholders(); + +public: + PPTWriterBase(); + PPTWriterBase( const css::uno::Reference< css::frame::XModel > & rModel, + const css::uno::Reference< css::task::XStatusIndicator > & rStatInd ); + + virtual ~PPTWriterBase(); + + void exportPPT(const std::vector< css::beans::PropertyValue >&); + + bool InitSOIface(); + bool GetPageByIndex( sal_uInt32 nIndex, PageType ); + sal_uInt32 GetMasterIndex( PageType ePageType ); + void SetCurrentStyleSheet( sal_uInt32 nPageNum ); + + bool GetPresObj() const { return mbPresObj; } + + static PHLayout const & GetLayout( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); + static PHLayout const & GetLayout( sal_Int32 nOffset ); + static sal_Int32 GetLayoutOffset( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); + static sal_Int32 GetLayoutOffsetFixed( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ); + + bool CreateSlide( sal_uInt32 nPageNum ); + bool CreateSlideMaster( sal_uInt32 nPageNum ); + bool CreateNotes( sal_uInt32 nPageNum ); + + static sal_Int8 GetTransition( sal_Int16 nTransitionType, sal_Int16 nTransitionSubtype, css::presentation::FadeEffect eEffect, + sal_Int32 nTransitionFadeColor, sal_uInt8& nDirection ); + static sal_Int8 GetTransition( css::presentation::FadeEffect eEffect, sal_uInt8& nDirection ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/epptdef.hxx b/sd/source/filter/eppt/epptdef.hxx new file mode 100644 index 000000000..f5059681c --- /dev/null +++ b/sd/source/filter/eppt/epptdef.hxx @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#define EPP_Document 1000 +#define EPP_DocumentAtom 1001 +#define EPP_EndDocument 1002 +#define EPP_Slide 1006 +#define EPP_SlideAtom 1007 +#define EPP_Notes 1008 +#define EPP_NotesAtom 1009 +#define EPP_Environment 1010 +#define EPP_SlidePersistAtom 1011 //0x03F3 +#define EPP_MainMaster 1016 +#define EPP_SSSlideInfoAtom 1017 +#define EPP_SlideViewInfo 1018 +#define EPP_GuideAtom 1019 +#define EPP_ViewInfoAtom 1021 +#define EPP_SlideViewInfoAtom 1022 +#define EPP_VBAInfo 1023 +#define EPP_VBAInfoAtom 1024 +#define EPP_SSDocInfoAtom 1025 +#define EPP_OutlineViewInfo 1031 +#define EPP_ExObjList 1033 +#define EPP_ExObjListAtom 1034 +#define EPP_PPDrawingGroup 1035 +#define EPP_PPDrawing 1036 +#define EPP_NamedShows 1040 +#define EPP_NamedShow 1041 +#define EPP_NamedShowSlides 1042 +#define EPP_List 2000 +#define EPP_FontCollection 2005 +#define EPP_SoundCollection 2020 +#define EPP_SoundCollAtom 2021 +#define EPP_Sound 2022 +#define EPP_SoundData 2023 +#define EPP_ColorSchemeAtom 2032 + +// these atoms first was seen in ppt2000 in a private Tag atom +#define EPP_PST_ExtendedBuGraContainer 2040 // consist of 4041 + +#define EPP_ExObjRefAtom 3009 +#define EPP_OEPlaceholderAtom 3011 +#define EPP_TextHeaderAtom 3999 +#define EPP_TextCharsAtom 4000 +#define EPP_StyleTextPropAtom 4001 +#define EPP_BaseTextPropAtom 4002 +#define EPP_TxMasterStyleAtom 4003 +#define EPP_TxCFStyleAtom 4004 +#define EPP_TextRulerAtom 4006 +#define EPP_TxSIStyleAtom 4009 +#define EPP_TextSpecInfoAtom 4010 + +// these atoms first was seen in ppt2000 in a private Tag atom +#define EPP_PST_ExtendedParagraphAtom 4012 +#define EPP_PST_ExtendedParagraphMasterAtom 4013 +#define EPP_PST_ExtendedPresRuleContainer 4014 // consist of 4012, 4015, +#define EPP_PST_ExtendedParagraphHeaderAtom 4015 // the instance of this atom indices the current presobj + // the first sal_uInt32 in this atom indices the current slideId + +#define EPP_FontEnityAtom 4023 +#define EPP_CString 4026 +#define EPP_ExOleObjAtom 4035 +#define EPP_SrKinsoku 4040 +#define EPP_ExEmbed 4044 +#define EPP_ExEmbedAtom 4045 +#define EPP_SrKinsokuAtom 4050 +#define EPP_ExHyperlinkAtom 4051 +#define EPP_ExHyperlink 4055 +#define EPP_SlideNumberMCAtom 4056 +#define EPP_HeadersFooters 4057 +#define EPP_HeadersFootersAtom 4058 +#define EPP_TxInteractiveInfoAtom 4063 +#define EPP_ExControl 4078 +#define EPP_ExControlAtom 4091 +#define EPP_SlideListWithText 4080 // 0x0FF0 +#define EPP_AnimationInfoAtom 4081 +#define EPP_InteractiveInfo 4082 +#define EPP_InteractiveInfoAtom 4083 +#define EPP_UserEditAtom 4085 +#define EPP_CurrentUserAtom 4086 +#define EPP_DateTimeMCAtom 4087 +#define EPP_GenericDateMCAtom 4088 +#define EPP_HeaderMCAtom 4089 +#define EPP_FooterMCAtom 4090 +#define EPP_ExMediaAtom 4100 +#define EPP_ExVideo 4101 +#define EPP_ExMCIMovie 4103 +#define EPP_ExOleObjStg 4113 +#define EPP_AnimationInfo 4116 +#define EPP_ProgTags 5000 +#define EPP_ProgBinaryTag 5002 +#define EPP_BinaryTagData 5003 +#define EPP_PersistPtrIncrementalBlock 6002 +#define EPP_Comment10 12000 +#define EPP_CommentAtom10 12001 + +#define EPP_PLACEHOLDER_NONE 0 // 0 None +#define EPP_PLACEHOLDER_MASTERTITLE 1 // 1 Master title +#define EPP_PLACEHOLDER_MASTERBODY 2 // 2 Master body +#define EPP_PLACEHOLDER_MASTERSUBTITLE 4 // 10 Master subtitle +#define EPP_PLACEHOLDER_MASTERNOTESSLIDEIMAGE 5 // 4 Master notes slide image +#define EPP_PLACEHOLDER_MASTERNOTESBODYIMAGE 6 // 5 Master notes body image +#define EPP_PLACEHOLDER_MASTERDATE 7 // 6 Master date +#define EPP_PLACEHOLDER_MASTERSLIDENUMBER 8 // 7 Master slide number +#define EPP_PLACEHOLDER_MASTERFOOTER 9 // 8 Master footer +#define EPP_PLACEHOLDER_MASTERHEADER 10 // 9 Master header +#define EPP_PLACEHOLDER_GENERICTEXTOBJECT // 11 Generic text object +#define EPP_PLACEHOLDER_NOTESBODY 12 // 14 Notes body +#define EPP_PLACEHOLDER_NOTESSLIDEIMAGE 11 // 19 Notes slide image + +#define EPP_TEXTTYPE_Title 0 +#define EPP_TEXTTYPE_Body 1 +#define EPP_TEXTTYPE_Notes 2 +#define EPP_TEXTTYPE_notUsed 3 +#define EPP_TEXTTYPE_Other 4 // ( Text in a shape ) +#define EPP_TEXTTYPE_CenterBody 5 // ( subtitle in title slide ) +#define EPP_TEXTTYPE_CenterTitle 6 // ( title in title slide ) +#define EPP_TEXTTYPE_HalfBody 7 // ( body in two-column slide ) +#define EPP_TEXTTYPE_QuarterBody 8 // ( body in four-body slide ) + +#define EPP_SLIDESIZE_TYPEONSCREEN 0 +#define EPP_SLIDESIZE_TYPEA4PAPER 2 +#define EPP_SLIDESIZE_TYPE35MM 3 +#define EPP_SLIDESIZE_TYPEBANNER 5 +#define EPP_SLIDESIZE_TYPECUSTOM 6 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/epptooxml.hxx b/sd/source/filter/eppt/epptooxml.hxx new file mode 100644 index 000000000..5ee3248ec --- /dev/null +++ b/sd/source/filter/eppt/epptooxml.hxx @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include "epptbase.hxx" + +using ::sax_fastparser::FSHelperPtr; + +namespace svx +{ +class Theme; +} + +namespace oox::core { + +struct LayoutInfo +{ + std::vector< sal_Int32 > mnFileIdArray; +}; + +enum PlaceholderType +{ + None, + SlideImage, + Notes, + Header, + Footer, + SlideNumber, + DateAndTime, + Outliner, + Title, + Subtitle +}; + +class PowerPointShapeExport; + +class PowerPointExport final : public XmlFilterBase, public PPTWriterBase +{ + friend class PowerPointShapeExport; +public: + + PowerPointExport(const css::uno::Reference & rContext, const css::uno::Sequence& rArguments); + + virtual ~PowerPointExport() override; + + // from FilterBase + virtual bool importDocument() noexcept override; + virtual bool exportDocument() override; + + // only needed for import, leave them empty, refactor later XmlFilterBase to export and import base? + virtual oox::vml::Drawing* getVmlDrawing() override { return nullptr; } + virtual const oox::drawingml::Theme* getCurrentTheme() const override { return nullptr; } + virtual oox::drawingml::table::TableStyleListPtr getTableStyles() override { return oox::drawingml::table::TableStyleListPtr(); } + virtual oox::drawingml::chart::ChartConverter* getChartConverter() override { return nullptr; } + + static const char* GetSideDirection( sal_uInt8 nDirection ); + static const char* GetCornerDirection( sal_uInt8 nDirection ); + static const char* Get8Direction( sal_uInt8 nDirection ); + static int GetPPTXLayoutId( int nOffset ); + + sal_Int32 GetShapeID(const css::uno::Reference& rXShape); + sal_Int32 GetNextAnimationNodeID(); + + void embedEffectAudio(const FSHelperPtr& pFS, const OUString& sUrl, OUString& sRelId, OUString& sName); + +private: + + virtual void ImplWriteSlide( sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 nMode, + bool bHasBackground, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override; + virtual void ImplWriteNotes( sal_uInt32 nPageNum ) override; + virtual void ImplWriteSlideMaster( sal_uInt32 nPageNum, css::uno::Reference< css::beans::XPropertySet > const & aXBackgroundPropSet ) override; + void ImplWritePPTXLayout( sal_Int32 nOffset, sal_uInt32 nMasterNum ); + + /// Export the color set part of a theme. + static bool WriteColorSets(const FSHelperPtr& pFS, svx::Theme* pTheme); + + /// Same as WriteColorSets(), but works from a grab-bag. + bool WriteColorSchemes(const FSHelperPtr& pFS, const OUString& rThemePath); + + static void WriteDefaultColorSchemes(const FSHelperPtr& pFS); + void WriteTheme( sal_Int32 nThemeNum, svx::Theme* pTheme ); + + virtual bool ImplCreateDocument() override; + virtual bool ImplCreateMainNotes() override; + virtual ::oox::ole::VbaProject* implCreateVbaProject() const override; + void WriteNotesMaster(); + + bool WriteComments( sal_uInt32 nPageNum ); + void ImplWriteBackground( const ::sax_fastparser::FSHelperPtr& pFS, const css::uno::Reference< css::beans::XPropertySet >& aXBackgroundPropSet ); + void WriteTransition( const ::sax_fastparser::FSHelperPtr& pFS ); + + sal_Int32 GetLayoutFileId( sal_Int32 nOffset, sal_uInt32 nMasterNum ); + + // shapes + void WriteShapeTree( const ::sax_fastparser::FSHelperPtr& pFS, PageType ePageType, bool bMaster ); + + sal_uInt32 GetNewSlideId() { return mnSlideIdMax ++; } + sal_uInt32 GetNewSlideMasterId() { return mnSlideMasterIdMax ++; } + sal_Int32 GetAuthorIdAndLastIndex( const OUString& sAuthor, sal_Int32& nLastIndex ); + + // Write docProps/core.xml and docprops/custom.xml and docprops/app.xml + void writeDocumentProperties(); + + void WriteCustomSlideShow(); + + void AddLayoutIdAndRelation( const ::sax_fastparser::FSHelperPtr& pFS, sal_Int32 nLayoutFileId ); + + virtual OUString SAL_CALL getImplementationName() override; + + static void WriteDiagram(const FSHelperPtr& pFS, PowerPointShapeExport& rDML, const css::uno::Reference& rXShape, int nDiagramId); + + /** Create a new placeholder index for a master placeholder shape + + @param rXShape Master placeholder shape + @returns Placeholder index + */ + sal_Int32 CreateNewPlaceholderIndex(const css::uno::Reference& rXShape); + css::uno::Reference GetReferencedPlaceholderXShape(const PlaceholderType eType, PageType ePageType) const; + void WritePlaceholderReferenceShapes(PowerPointShapeExport& rDML, PageType ePageType); + + /// Should we export as .pptm, ie. do we contain macros? + bool mbPptm; + + // Export as a template + bool mbExportTemplate; + + ::sax_fastparser::FSHelperPtr mPresentationFS; + + LayoutInfo mLayoutInfo[EPP_LAYOUT_SIZE]; + std::vector< ::sax_fastparser::FSHelperPtr > mpSlidesFSArray; + sal_Int32 mnLayoutFileIdMax; + + sal_uInt32 mnSlideIdMax; + sal_uInt32 mnSlideMasterIdMax; + sal_uInt32 mnAnimationNodeIdMax; + + sal_uInt32 mnDiagramId; + + std::vector maRelId; + + bool mbCreateNotes; + + ::oox::drawingml::ShapeExport::ShapeHashMap maShapeMap; + + sal_Int32 mnPlaceholderIndexMax; ///< Last used placeholder index + /// Map of placeholder indexes for Master placeholders + std::unordered_map< css::uno::Reference, sal_Int32 > maPlaceholderShapeToIndexMap; + + struct AuthorComments { + sal_Int32 nId; + sal_Int32 nLastIndex; + }; + typedef std::unordered_map< OUString, struct AuthorComments > AuthorsMap; + AuthorsMap maAuthors; + + void WriteAuthors(); + + void WritePresentationProps(); + + /// If this is PPTM, output the VBA stream. + void WriteVBA(); + + void WriteModifyVerifier(); +}; + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/epptso.cxx b/sd/source/filter/eppt/epptso.cxx new file mode 100644 index 000000000..41126aedf --- /dev/null +++ b/sd/source/filter/eppt/epptso.cxx @@ -0,0 +1,3361 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include "eppt.hxx" +#include "text.hxx" +#include "epptdef.hxx" +#include "escherex.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +#define ANSI_CHARSET 0 +#define SYMBOL_CHARSET 2 + +/* Font Families */ +#define FF_ROMAN 0x10 +#define FF_SWISS 0x20 +#define FF_MODERN 0x30 +#define FF_SCRIPT 0x40 +#define FF_DECORATIVE 0x50 + +#define DEFAULT_PITCH 0x00 +#define FIXED_PITCH 0x01 + +PPTExBulletProvider::PPTExBulletProvider() + : pGraphicProv( new EscherGraphicProvider( EscherGraphicProviderFlags::UseInstances ) ) +{ +} + +PPTExBulletProvider::~PPTExBulletProvider() +{ +} + +sal_uInt16 PPTExBulletProvider::GetId(Graphic const & rGraphic, Size& rGraphicSize ) +{ + sal_uInt16 nRetValue = 0xffff; + + if (!rGraphic.IsNone()) + { + Graphic aMappedGraphic, aGraphic(rGraphic); + GraphicObject aGraphicObject(aGraphic); + Size aPrefSize( aGraphic.GetPrefSize() ); + BitmapEx aBmpEx( aGraphic.GetBitmapEx() ); + + if ( rGraphicSize.Width() && rGraphicSize.Height() ) + { + if (aPrefSize.IsEmpty()) + { + aBmpEx.Scale(aPrefSize); + } + else + { + double fQ1 = static_cast(aPrefSize.Width()) / static_cast(aPrefSize.Height()); + double fQ2 = static_cast(rGraphicSize.Width()) / static_cast(rGraphicSize.Height()); + double fXScale = 1; + double fYScale = 1; + + if ( fQ1 > fQ2 ) + fYScale = fQ1 / fQ2; + else if ( fQ1 < fQ2 ) + fXScale = fQ2 / fQ1; + + if ( ( fXScale != 1.0 ) || ( fYScale != 1.0 ) ) + { + aBmpEx.Scale( fXScale, fYScale ); + rGraphicSize = Size( static_cast(static_cast(rGraphicSize.Width()) / fXScale + 0.5 ), + static_cast(static_cast(rGraphicSize.Height()) / fYScale + 0.5 ) ); + + aMappedGraphic = Graphic( aBmpEx ); + aGraphicObject.SetGraphic(aMappedGraphic); + } + } + } + sal_uInt32 nId = pGraphicProv->GetBlibID(aBuExPictureStream, aGraphicObject); + + if ( nId && ( nId < 0x10000 ) ) + nRetValue = static_cast(nId) - 1; + } + return nRetValue; +} + +sal_uInt32 PPTWriter::ImplVBAInfoContainer( SvStream* pStrm ) +{ + sal_uInt32 nSize = 28; + if ( pStrm ) + { + pStrm->WriteUInt32( 0x1f | ( EPP_VBAInfo << 16 ) ) + .WriteUInt32( nSize - 8 ) + .WriteUInt32( 2 | ( EPP_VBAInfoAtom << 16 ) ) + .WriteUInt32( 12 ); + mpPptEscherEx->InsertPersistOffset( EPP_Persist_VBAInfoAtom, pStrm->Tell() ); + pStrm->WriteUInt32( 0 ) + .WriteUInt32( 0 ) + .WriteUInt32( 1 ); + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplSlideViewInfoContainer( sal_uInt32 nInstance, SvStream* pStrm ) +{ + sal_uInt32 nSize = 111; + if ( pStrm ) + { + sal_uInt8 bShowGuides = 0; + sal_uInt8 const bSnapToGrid = 1; + sal_uInt8 const bSnapToShape = 0; + + sal_Int32 nScaling = 85; + sal_Int32 nMasterCoordinate = 0xdda; + sal_Int32 nXOrigin = -780; + sal_Int32 nYOrigin = -84; + + sal_Int32 nPosition1 = 0x870; + sal_Int32 nPosition2 = 0xb40; + + if ( nInstance ) + { + bShowGuides = 1; + nScaling = 0x3b; + nMasterCoordinate = 0xf0c; + nXOrigin = -1752; + nYOrigin = -72; + nPosition1 = 0xb40; + nPosition2 = 0x870; + } + pStrm->WriteUInt32( 0xf | ( EPP_SlideViewInfo << 16 ) | ( nInstance << 4 ) ) + .WriteUInt32( nSize - 8 ) + .WriteUInt32( EPP_SlideViewInfoAtom << 16 ).WriteUInt32( 3 ) + .WriteUChar( bShowGuides ).WriteUChar( bSnapToGrid ).WriteUChar( bSnapToShape ) + .WriteUInt32( EPP_ViewInfoAtom << 16 ).WriteUInt32( 52 ) + .WriteInt32( nScaling ).WriteInt32( 100 ).WriteInt32( nScaling ).WriteInt32( 100 ) // scaling atom - Keeps the current scale + .WriteInt32( nScaling ).WriteInt32( 100 ).WriteInt32( nScaling ).WriteInt32( 100 ) // scaling atom - Keeps the previous scale + .WriteInt32( 0x17ac ).WriteInt32( nMasterCoordinate )// Origin - Keeps the origin in master coordinates + .WriteInt32( nXOrigin ).WriteInt32( nYOrigin ) // Origin + .WriteUChar( 1 ) // Bool1 varScale - Set if zoom to fit is set + .WriteUChar( 0 ) // bool1 draftMode - Not used + .WriteUInt16( 0 ) // padword + .WriteUInt32( ( 7 << 4 ) | ( EPP_GuideAtom << 16 ) ).WriteUInt32( 8 ) + .WriteUInt32( 0 ) // Type of the guide. If the guide is horizontal this value is zero. If it's vertical, it's one. + .WriteInt32( nPosition1 ) // Position of the guide in master coordinates. X coordinate if it's vertical, and Y coordinate if it's horizontal. + .WriteUInt32( ( 7 << 4 ) | ( EPP_GuideAtom << 16 ) ).WriteUInt32( 8 ) + .WriteInt32( 1 ) // Type of the guide. If the guide is horizontal this value is zero. If it's vertical, it's one. + .WriteInt32( nPosition2 ); // Position of the guide in master coordinates. X coordinate if it's vertical, and Y coordinate if it's horizontal. + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplOutlineViewInfoContainer( SvStream* pStrm ) +{ + sal_uInt32 nSize = 68; + if ( pStrm ) + { + pStrm->WriteUInt32( 0xf | ( EPP_OutlineViewInfo << 16 ) ).WriteUInt32( nSize - 8 ) + .WriteUInt32( EPP_ViewInfoAtom << 16 ).WriteUInt32( 52 ) + .WriteInt32( 170 ).WriteInt32( 200 ).WriteInt32( 170 ).WriteInt32( 200 ) // scaling atom - Keeps the current scale + .WriteInt32( 170 ).WriteInt32( 200 ).WriteInt32( 170 ).WriteInt32( 200 ) // scaling atom - Keeps the previous scale + .WriteInt32( 0x17ac ).WriteInt32( 0xdda ) // Origin - Keeps the origin in master coordinates + .WriteInt32( -780 ).WriteInt32( -84 ) // Origin + .WriteUChar( 1 ) // bool1 varScale - Set if zoom to fit is set + .WriteUChar( 0 ) // bool1 draftMode - Not used + .WriteUInt16( 0 ); // padword + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplProgBinaryTag( SvStream* pStrm ) +{ + sal_uInt32 nPictureStreamSize, nOutlineStreamSize, nSize = 8; + + nPictureStreamSize = aBuExPictureStream.Tell(); + if ( nPictureStreamSize ) + nSize += nPictureStreamSize + 8; + + nOutlineStreamSize = aBuExOutlineStream.Tell(); + if ( nOutlineStreamSize ) + nSize += nOutlineStreamSize + 8; + + if ( pStrm ) + { + pStrm->WriteUInt32( EPP_BinaryTagData << 16 ).WriteUInt32( nSize - 8 ); + if ( nPictureStreamSize ) + { + pStrm->WriteUInt32( 0xf | ( EPP_PST_ExtendedBuGraContainer << 16 ) ).WriteUInt32( nPictureStreamSize ); + pStrm->WriteBytes(aBuExPictureStream.GetData(), nPictureStreamSize); + } + if ( nOutlineStreamSize ) + { + pStrm->WriteUInt32( 0xf | ( EPP_PST_ExtendedPresRuleContainer << 16 ) ).WriteUInt32( nOutlineStreamSize ); + pStrm->WriteBytes(aBuExOutlineStream.GetData(), nOutlineStreamSize); + } + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplProgBinaryTagContainer( SvStream* pStrm, SvMemoryStream* pBinTagStrm ) +{ + sal_uInt32 nSize = 8 + 8 + 14; + if ( pStrm ) + { + pStrm->WriteUInt32( 0xf | ( EPP_ProgBinaryTag << 16 ) ).WriteUInt32( 0 ) + .WriteUInt32( EPP_CString << 16 ).WriteUInt32( 14 ) + .WriteUInt32( 0x5f005f ).WriteUInt32( 0x50005f ) + .WriteUInt32( 0x540050 ).WriteUInt16( 0x39 ); + } + if ( pStrm && pBinTagStrm ) + { + sal_uInt32 nLen = pBinTagStrm->Tell(); + nSize += nLen + 8; + pStrm->WriteUInt32( EPP_BinaryTagData << 16 ).WriteUInt32( nLen ); + pStrm->WriteBytes(pBinTagStrm->GetData(), nLen); + } + else + nSize += ImplProgBinaryTag( pStrm ); + + if ( pStrm ) + { + pStrm->SeekRel( - ( static_cast(nSize) - 4 ) ); + pStrm->WriteUInt32( nSize - 8 ); + pStrm->SeekRel( nSize - 8 ); + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplProgTagContainer( SvStream* pStrm, SvMemoryStream* pBinTagStrm ) +{ + sal_uInt32 nSize = 0; + if ( aBuExPictureStream.Tell() || aBuExOutlineStream.Tell() || pBinTagStrm ) + { + nSize = 8; + if ( pStrm ) + { + pStrm->WriteUInt32( 0xf | ( EPP_ProgTags << 16 ) ).WriteUInt32( 0 ); + } + nSize += ImplProgBinaryTagContainer( pStrm, pBinTagStrm ); + if ( pStrm ) + { + pStrm->SeekRel( - ( static_cast(nSize) - 4 ) ); + pStrm->WriteUInt32( nSize - 8 ); + pStrm->SeekRel( nSize - 8 ); + } + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplDocumentListContainer( SvStream* pStrm ) +{ + sal_uInt32 nSize = 8; + if ( pStrm ) + { + pStrm->WriteUInt32( ( EPP_List << 16 ) | 0xf ).WriteUInt32( 0 ); + } + + nSize += ImplVBAInfoContainer( pStrm ); + nSize += ImplSlideViewInfoContainer( 0, pStrm ); + nSize += ImplOutlineViewInfoContainer( pStrm ); + nSize += ImplSlideViewInfoContainer( 1, pStrm ); + nSize += ImplProgTagContainer( pStrm ); + + if ( pStrm ) + { + pStrm->SeekRel( - ( static_cast(nSize) - 4 ) ); + pStrm->WriteUInt32( nSize - 8 ); + pStrm->SeekRel( nSize - 8 ); + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplMasterSlideListContainer( SvStream* pStrm ) +{ + sal_uInt32 i, nSize = 28 * mnMasterPages + 8; + if ( pStrm ) + { + pStrm->WriteUInt32( 0x1f | ( EPP_SlideListWithText << 16 ) ).WriteUInt32( nSize - 8 ); + + for ( i = 0; i < mnMasterPages; i++ ) + { + pStrm->WriteUInt32( EPP_SlidePersistAtom << 16 ).WriteUInt32( 20 ); + mpPptEscherEx->InsertPersistOffset( EPP_MAINMASTER_PERSIST_KEY | i, pStrm->Tell() ); + pStrm->WriteUInt32( 0 ) // psrReference - logical reference to the slide persist object ( EPP_MAINMASTER_PERSIST_KEY ) + .WriteUInt32( 0 ) // flags - only bit 3 used, if set then slide contains shapes other than placeholders + .WriteInt32( 0 ) // numberTexts - number of placeholder texts stored with the persist object. Allows to display outline view without loading the slide persist objects + .WriteInt32( 0x80000000 | i ) // slideId - Unique slide identifier, used for OLE link monikers for example + .WriteUInt32( 0 ); // reserved, usually 0 + } + } + return nSize; +} + +sal_uInt32 PPTWriter::ImplInsertBookmarkURL( const OUString& rBookmarkURL, const sal_uInt32 nType, + const OUString& rStringVer0, const OUString& rStringVer1, const OUString& rStringVer2, const OUString& rStringVer3 ) +{ + sal_uInt32 nHyperId = ++mnExEmbed; + + OUString sBookmarkURL( rBookmarkURL ); + INetURLObject aBaseURI( maBaseURI ); + INetURLObject aBookmarkURI( rBookmarkURL ); + if( aBaseURI.GetProtocol() == aBookmarkURI.GetProtocol() ) + { + OUString aRelUrl( INetURLObject::GetRelURL( maBaseURI, rBookmarkURL ) ); + if ( !aRelUrl.isEmpty() ) + sBookmarkURL = aRelUrl; + } + maHyperlink.emplace_back( sBookmarkURL, nType ); + + mpExEmbed->WriteUInt16( 0xf ) + .WriteUInt16( EPP_ExHyperlink ) + .WriteUInt32( 0 ); + sal_uInt32 nHyperSize, nHyperStart = mpExEmbed->Tell(); + mpExEmbed->WriteUInt16( 0 ) + .WriteUInt16( EPP_ExHyperlinkAtom ) + .WriteUInt32( 4 ) + .WriteUInt32( nHyperId ); + + PPTWriter::WriteCString( *mpExEmbed, rStringVer0 ); + PPTWriter::WriteCString( *mpExEmbed, rStringVer1, 1 ); + PPTWriter::WriteCString( *mpExEmbed, rStringVer2, 2 ); + PPTWriter::WriteCString( *mpExEmbed, rStringVer3, 3 ); + + nHyperSize = mpExEmbed->Tell() - nHyperStart; + mpExEmbed->SeekRel( - ( static_cast(nHyperSize) + 4 ) ); + mpExEmbed->WriteUInt32( nHyperSize ); + mpExEmbed->SeekRel( nHyperSize ); + return nHyperId; +} + +bool PPTWriter::ImplCloseDocument() +{ + sal_uInt32 nOfs = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_Document ); + if ( nOfs ) + { + mpPptEscherEx->PtReplaceOrInsert( EPP_Persist_CurrentPos, mpStrm->Tell() ); + mpStrm->Seek( nOfs ); + + // creating the TxMasterStyleAtom + SvMemoryStream aTxMasterStyleAtomStrm( 0x200, 0x200 ); + { + EscherExAtom aTxMasterStyleAtom( aTxMasterStyleAtomStrm, EPP_TxMasterStyleAtom, EPP_TEXTTYPE_Other ); + aTxMasterStyleAtomStrm.WriteUInt16( 5 ); // paragraph count + sal_uInt16 nLev; + for ( nLev = 0; nLev < 5; nLev++ ) + { + mpStyleSheet->mpParaSheet[ EPP_TEXTTYPE_Other ]->Write( aTxMasterStyleAtomStrm, nLev, false, mXPagePropSet ); + mpStyleSheet->mpCharSheet[ EPP_TEXTTYPE_Other ]->Write( aTxMasterStyleAtomStrm, nLev, false, mXPagePropSet ); + } + } + + sal_uInt32 nExEmbedSize = mpExEmbed->TellEnd(); + + // nEnvironment : whole size of the environment container + sal_uInt32 nEnvironment = maFontCollection.GetCount() * 76 // 68 bytes per Fontenityatom and 8 Bytes per header + + 8 // 1 FontCollection container + + 20 // SrKinsoku container + + 18 // 1 TxSiStyleAtom + + aTxMasterStyleAtomStrm.Tell() // 1 TxMasterStyleAtom; + + PPTExStyleSheet::SizeOfTxCFStyleAtom(); + + sal_uInt32 nBytesToInsert = nEnvironment + 8; + + if ( nExEmbedSize ) + nBytesToInsert += nExEmbedSize + 8 + 12; + + nBytesToInsert += maSoundCollection.GetSize(); + nBytesToInsert += mpPptEscherEx->DrawingGroupContainerSize(); + nBytesToInsert += ImplMasterSlideListContainer(nullptr); + nBytesToInsert += ImplDocumentListContainer(nullptr); + + // insert nBytes into stream and adjust depending container + mpPptEscherEx->InsertAtCurrentPos( nBytesToInsert ); + + // CREATE HYPERLINK CONTAINER + if ( nExEmbedSize ) + { + mpStrm->WriteUInt16( 0xf ) + .WriteUInt16( EPP_ExObjList ) + .WriteUInt32( nExEmbedSize + 12 ) + .WriteUInt16( 0 ) + .WriteUInt16( EPP_ExObjListAtom ) + .WriteUInt32( 4 ) + .WriteUInt32( mnExEmbed ); + mpPptEscherEx->InsertPersistOffset( EPP_Persist_ExObj, mpStrm->Tell() ); + mpStrm->WriteBytes(mpExEmbed->GetData(), nExEmbedSize); + } + + // CREATE ENVIRONMENT + mpStrm->WriteUInt16( 0xf ).WriteUInt16( EPP_Environment ).WriteUInt32( nEnvironment ); + + // Open Container ( EPP_SrKinsoku ) + mpStrm->WriteUInt16( 0x2f ).WriteUInt16( EPP_SrKinsoku ).WriteUInt32( 12 ); + mpPptEscherEx->AddAtom( 4, EPP_SrKinsokuAtom, 0, 3 ); + mpStrm->WriteInt32( 0 ); // SrKinsoku Level 0 + + // Open Container ( EPP_FontCollection ) + mpStrm->WriteUInt16( 0xf ).WriteUInt16( EPP_FontCollection ).WriteUInt32( maFontCollection.GetCount() * 76 ); + + for ( sal_uInt32 i = 0; i < maFontCollection.GetCount(); i++ ) + { + mpPptEscherEx->AddAtom( 68, EPP_FontEnityAtom, 0, i ); + const FontCollectionEntry* pDesc = maFontCollection.GetById( i ); + sal_Int32 nFontLen = pDesc->Name.getLength(); + if ( nFontLen > 31 ) + nFontLen = 31; + for ( sal_Int32 n = 0; n < 32; n++ ) + { + sal_Unicode nUniCode = 0; + if ( n < nFontLen ) + nUniCode = pDesc->Name[n]; + mpStrm->WriteUInt16( nUniCode ); + } + sal_uInt8 lfCharSet = ANSI_CHARSET; + sal_uInt8 const lfClipPrecision = 0; + sal_uInt8 const lfQuality = 6; + sal_uInt8 lfPitchAndFamily = 0; + + if ( pDesc->CharSet == RTL_TEXTENCODING_SYMBOL ) + lfCharSet = SYMBOL_CHARSET; + + switch( pDesc->Family ) + { + case css::awt::FontFamily::ROMAN : + lfPitchAndFamily |= FF_ROMAN; + break; + + case css::awt::FontFamily::SWISS : + lfPitchAndFamily |= FF_SWISS; + break; + + case css::awt::FontFamily::MODERN : + lfPitchAndFamily |= FF_MODERN; + break; + + case css::awt::FontFamily::SCRIPT: + lfPitchAndFamily |= FF_SCRIPT; + break; + + case css::awt::FontFamily::DECORATIVE: + lfPitchAndFamily |= FF_DECORATIVE; + break; + + default: + lfPitchAndFamily |= FAMILY_DONTKNOW; + break; + } + switch( pDesc->Pitch ) + { + case css::awt::FontPitch::FIXED: + lfPitchAndFamily |= FIXED_PITCH; + break; + + default: + lfPitchAndFamily |= DEFAULT_PITCH; + break; + } + mpStrm->WriteUChar( lfCharSet ) + .WriteUChar( lfClipPrecision ) + .WriteUChar( lfQuality ) + .WriteUChar( lfPitchAndFamily ); + } + mpStyleSheet->WriteTxCFStyleAtom( *mpStrm ); // create style that is used for new standard objects + mpPptEscherEx->AddAtom( 10, EPP_TxSIStyleAtom ); + mpStrm->WriteUInt32( 7 ) // ? + .WriteInt16( 2 ) // ? + .WriteUChar( 9 ) // ? + .WriteUChar( 8 ) // ? + .WriteInt16( 0 ); // ? + + mpStrm->WriteBytes(aTxMasterStyleAtomStrm.GetData(), aTxMasterStyleAtomStrm.Tell()); + maSoundCollection.Write( *mpStrm ); + mpPptEscherEx->WriteDrawingGroupContainer( *mpStrm ); + ImplMasterSlideListContainer( mpStrm.get() ); + ImplDocumentListContainer( mpStrm.get() ); + + sal_uInt32 nOldPos = mpPptEscherEx->PtGetOffsetByID( EPP_Persist_CurrentPos ); + if ( nOldPos ) + { + mpStrm->Seek( nOldPos ); + return true; + } + } + return false; +} + +bool PropValue::GetPropertyValue( + css::uno::Any& rAny, + const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + const OUString& rString, + bool bTestPropertyAvailability ) +{ + bool bRetValue = true; + if ( bTestPropertyAvailability ) + { + bRetValue = false; + try + { + css::uno::Reference< css::beans::XPropertySetInfo > aXPropSetInfo( rXPropSet->getPropertySetInfo() ); + if ( aXPropSetInfo.is() ) + bRetValue = aXPropSetInfo->hasPropertyByName( rString ); + } + catch( css::uno::Exception& ) + { + bRetValue = false; + } + } + if ( bRetValue ) + { + try + { + rAny = rXPropSet->getPropertyValue( rString ); + if ( !rAny.hasValue() ) + bRetValue = false; + } + catch( css::uno::Exception& ) + { + bRetValue = false; + } + } + return bRetValue; +} + +css::beans::PropertyState PropValue::GetPropertyState( + const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + const OUString& rPropertyName ) +{ + css::beans::PropertyState eRetValue = css::beans::PropertyState_AMBIGUOUS_VALUE; + try + { + css::uno::Reference< css::beans::XPropertyState > aXPropState( rXPropSet, css::uno::UNO_QUERY ); + if ( aXPropState.is() ) + eRetValue = aXPropState->getPropertyState( rPropertyName ); + } + catch( css::uno::Exception& ) + { + + } + return eRetValue; +} + +bool PropValue::ImplGetPropertyValue( const OUString& rString ) +{ + return GetPropertyValue( mAny, mXPropSet, rString ); +} + +bool PropValue::ImplGetPropertyValue( const css::uno::Reference< css::beans::XPropertySet > & aXPropSet, const OUString& rString ) +{ + return GetPropertyValue( mAny, aXPropSet, rString ); +} + +bool PropStateValue::ImplGetPropertyValue( const OUString& rString, bool bGetPropertyState ) +{ + ePropState = css::beans::PropertyState_AMBIGUOUS_VALUE; + bool bRetValue = true; +#ifdef UNX + css::uno::Reference< css::beans::XPropertySetInfo > + aXPropSetInfo( mXPropSet->getPropertySetInfo() ); + if ( !aXPropSetInfo.is() ) + return false; +#endif + try + { + mAny = mXPropSet->getPropertyValue( rString ); + if ( !mAny.hasValue() ) + bRetValue = false; + else if ( bGetPropertyState ) + ePropState = mXPropState->getPropertyState( rString ); + else + ePropState = css::beans::PropertyState_DIRECT_VALUE; + } + catch( css::uno::Exception& ) + { + bRetValue = false; + } + return bRetValue; +} + +void PPTWriter::ImplWriteParagraphs( SvStream& rOut, TextObj& rTextObj ) +{ + bool bFirstParagraph = true; + sal_uInt32 nCharCount; + sal_uInt32 nPropertyFlags = 0; + sal_Int16 nLineSpacing; + int nInstance = rTextObj.GetInstance(); + + for ( sal_uInt32 i = 0; i < rTextObj.ParagraphCount(); ++i, bFirstParagraph = false ) + { + ParagraphObj* pPara = rTextObj.GetParagraph(i); + const PortionObj& rPortion = pPara->front(); + nCharCount = pPara->CharacterCount(); + + if ( ( pPara->meTextAdjust == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_Adjust, pPara->mnTextAdjust ) ) ) + nPropertyFlags |= 0x00000800; + nLineSpacing = pPara->mnLineSpacing; + + const FontCollectionEntry* pDesc = maFontCollection.GetById( rPortion.mnFont ); + sal_Int16 nNormalSpacing = 100; + if ( !mbFontIndependentLineSpacing && pDesc ) + { + double fN = 100.0; + fN *= pDesc->Scaling; + nNormalSpacing = static_cast( fN + 0.5 ); + } + if ( !mbFontIndependentLineSpacing && bFirstParagraph && ( nLineSpacing > nNormalSpacing ) ) // sj: i28747, no replacement for fixed linespacing + { + nLineSpacing = nNormalSpacing; + nPropertyFlags |= 0x00001000; + } + else + { + if ( nLineSpacing > 0 ) + { + if ( !mbFontIndependentLineSpacing && pDesc ) + nLineSpacing = static_cast( static_cast(nLineSpacing) * pDesc->Scaling + 0.5 ); + } + else + { + if ( !pPara->mbFixedLineSpacing && rPortion.mnCharHeight > static_cast( static_cast(-nLineSpacing) * 0.001 * 72.0 / 2.54 ) ) // 1/100mm to point + nLineSpacing = nNormalSpacing; + else + nLineSpacing = static_cast( convertMm100ToMasterUnit(nLineSpacing) ); + } + if ( ( pPara->meLineSpacing == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_LineFeed, nLineSpacing ) ) ) + nPropertyFlags |= 0x00001000; + } + if ( ( pPara->meLineSpacingTop == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_UpperDist, pPara->mnLineSpacingTop ) ) ) + nPropertyFlags |= 0x00002000; + if ( ( pPara->meLineSpacingBottom == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_LowerDist, pPara->mnLineSpacingBottom ) ) ) + nPropertyFlags |= 0x00004000; + if ( ( pPara->meForbiddenRules == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_UpperDist, pPara->mbForbiddenRules ? 1 : 0 ) ) ) + nPropertyFlags |= 0x00020000; + if ( ( pPara->meParagraphPunctation == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_UpperDist, pPara->mbParagraphPunctation ? 1 : 0 ) ) ) + nPropertyFlags |= 0x00080000; + if ( ( pPara->meBiDi == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_BiDi, pPara->mnBiDi ) ) ) + nPropertyFlags |= 0x00200000; + + sal_Int32 nBuRealSize = pPara->nBulletRealSize; + sal_Int16 nBulletFlags = pPara->nBulletFlags; + + if ( pPara->bExtendedParameters ) + nPropertyFlags |= pPara->nParaFlags; + else + { + nPropertyFlags |= 1; // turn off bullet explicit + nBulletFlags = 0; + } + + // Write nTextOfs and nBullets + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_TextOfs, pPara->nTextOfs ) ) + nPropertyFlags |= 0x100; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, ParaAttr_BulletOfs, pPara->nBulletOfs )) + nPropertyFlags |= 0x400; + + FontCollectionEntry aFontDescEntry( pPara->aFontDesc.Name, pPara->aFontDesc.Family, pPara->aFontDesc.Pitch, pPara->aFontDesc.CharSet ); + sal_uInt16 nFontId = static_cast(maFontCollection.GetId( aFontDescEntry )); + + rOut.WriteUInt32( nCharCount ) + .WriteUInt16( pPara->nDepth ) // Level + .WriteUInt32( nPropertyFlags ); // Paragraph Attribute Set + + if ( nPropertyFlags & 0xf ) + rOut.WriteInt16( nBulletFlags ); + if ( nPropertyFlags & 0x80 ) + rOut.WriteUInt16( pPara->cBulletId ); + if ( nPropertyFlags & 0x10 ) + rOut.WriteUInt16( nFontId ); + if ( nPropertyFlags & 0x40 ) + rOut.WriteInt16( nBuRealSize ); + if ( nPropertyFlags & 0x20 ) + { + sal_uInt32 nBulletColor = pPara->nBulletColor; + if ( nBulletColor == sal_uInt32(COL_AUTO) ) + { + bool bIsDark = false; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, mXPagePropSet, "IsBackgroundDark", true ) ) + aAny >>= bIsDark; + nBulletColor = bIsDark ? 0xffffff : 0x000000; + } + nBulletColor &= 0xffffff; + nBulletColor |= 0xfe000000; + rOut.WriteUInt32( nBulletColor ); + } + if ( nPropertyFlags & 0x00000800 ) + rOut.WriteUInt16( pPara->mnTextAdjust ); + if ( nPropertyFlags & 0x00001000 ) + rOut.WriteUInt16( nLineSpacing ); + if ( nPropertyFlags & 0x00002000 ) + rOut.WriteUInt16( pPara->mnLineSpacingTop ); + if ( nPropertyFlags & 0x00004000 ) + rOut.WriteUInt16( pPara->mnLineSpacingBottom ); + if ( nPropertyFlags & 0x100 ) + rOut.WriteUInt16( pPara->nTextOfs ); + if ( nPropertyFlags & 0x400 ) + rOut.WriteUInt16( pPara->nBulletOfs ); + if ( nPropertyFlags & 0x000e0000 ) + { + sal_uInt16 nAsianSettings = 0; + if ( pPara->mbForbiddenRules ) + nAsianSettings |= 1; + if ( pPara->mbParagraphPunctation ) + nAsianSettings |= 4; + rOut.WriteUInt16( nAsianSettings ); + } + if ( nPropertyFlags & 0x200000 ) + rOut.WriteUInt16( pPara->mnBiDi ); + } +} + +void PPTWriter::ImplWritePortions( SvStream& rOut, TextObj& rTextObj ) +{ + sal_uInt32 nPropertyFlags; + int nInstance = rTextObj.GetInstance(); + + for ( sal_uInt32 i = 0; i < rTextObj.ParagraphCount(); ++i ) + { + ParagraphObj* pPara = rTextObj.GetParagraph(i); + for ( std::vector >::const_iterator it = pPara->begin(); it != pPara->end(); ++it ) + { + const PortionObj& rPortion = **it; + nPropertyFlags = 0; + sal_uInt32 nCharAttr = rPortion.mnCharAttr; + sal_uInt32 nCharColor = rPortion.mnCharColor; + + if ( nCharColor == sal_uInt32(COL_AUTO) ) // nCharColor depends to the background color + { + bool bIsDark = false; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, mXPagePropSet, "IsBackgroundDark", true ) ) + aAny >>= bIsDark; + nCharColor = bIsDark ? 0xffffff : 0x000000; + } + + nCharColor &= 0xffffff; + + /* the portion is using the embossed or engraved attribute, which we want to map to the relief feature of PPT. + Because the relief feature of PPT is dependent to the background color, such a mapping can not always be used. */ + if ( nCharAttr & 0x200 ) + { + sal_uInt32 nBackgroundColor = 0xffffff; + + if ( !nCharColor ) // special treatment for + nCharColor = 0xffffff; // black fontcolor + + css::uno::Any aAny; + css::drawing::FillStyle aFS( css::drawing::FillStyle_NONE ); + if ( PropValue::GetPropertyValue( aAny, mXPropSet, "FillStyle" ) ) + aAny >>= aFS; + switch( aFS ) + { + case css::drawing::FillStyle_GRADIENT : + { + ::tools::Rectangle aRect( Point(), Size( 28000, 21000 ) ); + EscherPropertyContainer aPropOpt( mpPptEscherEx->GetGraphicProvider(), mpPicStrm.get(), aRect ); + aPropOpt.CreateGradientProperties( mXPropSet ); + aPropOpt.GetOpt( ESCHER_Prop_fillColor, nBackgroundColor ); + } + break; + case css::drawing::FillStyle_SOLID : + { + if ( PropValue::GetPropertyValue( aAny, mXPropSet, "FillColor" ) ) + nBackgroundColor = EscherEx::GetColor( *o3tl::doAccess(aAny) ); + } + break; + case css::drawing::FillStyle_NONE : + { + css::uno::Any aBackAny; + css::drawing::FillStyle aBackFS( css::drawing::FillStyle_NONE ); + if ( PropValue::GetPropertyValue( aBackAny, mXBackgroundPropSet, "FillStyle" ) ) + aBackAny >>= aBackFS; + switch( aBackFS ) + { + case css::drawing::FillStyle_GRADIENT : + { + ::tools::Rectangle aRect( Point(), Size( 28000, 21000 ) ); + EscherPropertyContainer aPropOpt( mpPptEscherEx->GetGraphicProvider(), mpPicStrm.get(), aRect ); + aPropOpt.CreateGradientProperties( mXBackgroundPropSet ); + aPropOpt.GetOpt( ESCHER_Prop_fillColor, nBackgroundColor ); + } + break; + case css::drawing::FillStyle_SOLID : + { + if ( PropValue::GetPropertyValue( aAny, mXBackgroundPropSet, "FillColor" ) ) + nBackgroundColor = EscherEx::GetColor( *o3tl::doAccess(aAny) ); + } + break; + default: + break; + } + } + break; + default: + break; + } + + sal_Int32 nB = nBackgroundColor & 0xff; + nB += static_cast( nBackgroundColor >> 8 ); + nB += static_cast( nBackgroundColor >> 16 ); + // if the background color is nearly black, relief can't been used, because the text would not be visible + if ( nB < 0x60 || ( nBackgroundColor != nCharColor ) ) + { + nCharAttr &=~ 0x200; + + // now check if the text is part of a group, and if the previous object has the same color than the fontcolor + // ( and if fillcolor is not available the background color ), it is sometimes + // not possible to export the 'embossed' flag + if ( ( GetCurrentGroupLevel() > 0 ) && ( GetCurrentGroupIndex() >= 1 ) ) + { + css::uno::Reference< css::drawing::XShape > aGroupedShape( GetCurrentGroupAccess()->getByIndex( GetCurrentGroupIndex() - 1 ), uno::UNO_QUERY ); + if( aGroupedShape.is() ) + { + css::uno::Reference< css::beans::XPropertySet > aPropSetOfNextShape + ( aGroupedShape, css::uno::UNO_QUERY ); + if ( aPropSetOfNextShape.is() ) + { + if ( PropValue::GetPropertyValue( aAny, aPropSetOfNextShape, + "FillColor", true ) ) + { + if ( nCharColor == EscherEx::GetColor( *o3tl::doAccess(aAny) ) ) + { + nCharAttr |= 0x200; + } + } + } + } + } + } + } + nCharColor |= 0xfe000000; + if ( nInstance == 4 ) // special handling for normal textobjects: + nPropertyFlags |= nCharAttr & 0x217; // not all attributes are inherited + else + { + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Bold, nCharAttr ) ) + nPropertyFlags |= 1; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Italic, nCharAttr ) ) + nPropertyFlags |= 2; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Underline, nCharAttr ) ) + nPropertyFlags |= 4; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Shadow, nCharAttr ) ) + nPropertyFlags |= 0x10; + if ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Embossed, nCharAttr ) ) + nPropertyFlags |= 512; + } + if ( rTextObj.HasExtendedBullets() ) + { + nPropertyFlags |= ( i & 0x3f ) << 10 ; + nCharAttr |= ( i & 0x3f ) << 10; + } + if ( ( rPortion.meFontName == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Font, rPortion.mnFont ) ) ) + nPropertyFlags |= 0x00010000; + if ( ( rPortion.meAsianOrComplexFont == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_AsianOrComplexFont, rPortion.mnAsianOrComplexFont ) ) ) + nPropertyFlags |= 0x00200000; + if ( ( rPortion.meCharHeight == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_FontHeight, rPortion.mnCharHeight ) ) ) + nPropertyFlags |= 0x00020000; + if ( ( rPortion.meCharColor == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_FontColor, nCharColor & 0xffffff ) ) ) + nPropertyFlags |= 0x00040000; + if ( ( rPortion.meCharEscapement == css::beans::PropertyState_DIRECT_VALUE ) || + ( mpStyleSheet->IsHardAttribute( nInstance, pPara->nDepth, CharAttr_Escapement, rPortion.mnCharEscapement ) ) ) + nPropertyFlags |= 0x00080000; + + sal_uInt32 nCharCount = rPortion.Count(); + + rOut.WriteUInt32( nCharCount ) + .WriteUInt32( nPropertyFlags ); //PropertyFlags + + if ( nPropertyFlags & 0xffff ) + rOut.WriteUInt16( nCharAttr ); + if ( nPropertyFlags & 0x00010000 ) + rOut.WriteUInt16( rPortion.mnFont ); + if ( nPropertyFlags & 0x00200000 ) + rOut.WriteUInt16( rPortion.mnAsianOrComplexFont ); + if ( nPropertyFlags & 0x00020000 ) + rOut.WriteUInt16( rPortion.mnCharHeight ); + if ( nPropertyFlags & 0x00040000 ) + rOut.WriteUInt32( nCharColor ); + if ( nPropertyFlags & 0x00080000 ) + rOut.WriteInt16( rPortion.mnCharEscapement ); + } + } +} + +/** + * Loads and converts text from shape, value is stored in mnTextSize. + */ +bool PPTWriter::ImplGetText() +{ + mnTextSize = 0; + mbFontIndependentLineSpacing = false; + mXText.set( mXShape, css::uno::UNO_QUERY ); + + if ( mXText.is() ) + { + mnTextSize = mXText->getString().getLength(); + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "FontIndependentLineSpacing", true ) ) + aAny >>= mbFontIndependentLineSpacing; + } + return ( mnTextSize != 0 ); +} + +void PPTWriter::ImplFlipBoundingBox( EscherPropertyContainer& rPropOpt ) +{ + if ( mnAngle < 0 ) + mnAngle = ( 36000 + mnAngle ) % 36000; + else + mnAngle = ( 36000 - ( mnAngle % 36000 ) ); + + double fCos = cos( basegfx::deg2rad<100>(mnAngle) ); + double fSin = sin( basegfx::deg2rad<100>(mnAngle) ); + + double fWidthHalf = maRect.GetWidth() / 2.0; + double fHeightHalf = maRect.GetHeight() / 2.0; + + double fXDiff = fCos * fWidthHalf + fSin * (-fHeightHalf); + double fYDiff = - ( fSin * fWidthHalf - fCos * ( -fHeightHalf ) ); + + maRect.Move( static_cast( -( fWidthHalf - fXDiff ) ), static_cast( - ( fHeightHalf + fYDiff ) ) ); + mnAngle *= 655; + mnAngle += 0x8000; + mnAngle &=~0xffff; // round nAngle to full grads + rPropOpt.AddOpt( ESCHER_Prop_Rotation, mnAngle ); + + if ( ( mnAngle >= ( 45 << 16 ) && mnAngle < ( 135 << 16 ) ) || + ( mnAngle >= ( 225 << 16 ) && mnAngle < ( 315 << 16 ) ) ) + { + // Maddeningly, in those two areas of PPT is the BoundingBox already + // vertical. Therefore, we need to put down it BEFORE THE ROTATION. + css::awt::Point aTopLeft( static_cast( maRect.Left() + fWidthHalf - fHeightHalf ), static_cast( maRect.Top() + fHeightHalf - fWidthHalf ) ); + const tools::Long nRotatedWidth(maRect.GetHeight()); + const tools::Long nRotatedHeight(maRect.GetWidth()); + const Size aNewSize(nRotatedWidth, nRotatedHeight); + maRect = ::tools::Rectangle( Point( aTopLeft.X, aTopLeft.Y ), aNewSize ); + } +} + +void PPTWriter::ImplAdjustFirstLineLineSpacing( TextObj& rTextObj, EscherPropertyContainer& rPropOpt ) +{ + if ( mbFontIndependentLineSpacing ) + return; + + if ( !rTextObj.ParagraphCount() ) + return; + + ParagraphObj* pPara = rTextObj.GetParagraph(0); + if ( pPara->empty() ) + return; + + const PortionObj& rPortion = pPara->front(); + sal_Int16 nLineSpacing = pPara->mnLineSpacing; + const FontCollectionEntry* pDesc = maFontCollection.GetById( rPortion.mnFont ); + if ( pDesc ) + nLineSpacing = static_cast( static_cast(nLineSpacing) * pDesc->Scaling + 0.5 ); + + if ( ( nLineSpacing > 0 ) && ( nLineSpacing < 100 ) ) + { + double fCharHeight = convertPointToMm100(rPortion.mnCharHeight); + fCharHeight *= 100 - nLineSpacing; + fCharHeight /= 100; + + sal_uInt32 nUpperDistance = 0; + rPropOpt.GetOpt( ESCHER_Prop_dyTextTop, nUpperDistance ); + nUpperDistance += static_cast< sal_uInt32 >( fCharHeight * 360.0 ); + rPropOpt.AddOpt( ESCHER_Prop_dyTextTop, nUpperDistance ); + } +} + +void PPTWriter::ImplWriteTextStyleAtom( SvStream& rOut, int nTextInstance, sal_uInt32 nAtomInstance, + TextRuleEntry* pTextRule, SvStream& rExtBuStr, EscherPropertyContainer* pPropOpt ) +{ + PPTExParaSheet& rParaSheet = mpStyleSheet->GetParaSheet( nTextInstance ); + + rOut.WriteUInt32( ( EPP_TextHeaderAtom << 16 ) | ( nAtomInstance << 4 ) ).WriteUInt32( 4 ) + .WriteInt32( nTextInstance ); + + if ( mbEmptyPresObj ) + mnTextSize = 0; + if ( mbEmptyPresObj ) + return; + + ParagraphObj* pPara; + TextObjBinary aTextObj( mXText, nTextInstance, maFontCollection, static_cast(*this) ); + + // leaving out EPP_TextCharsAtom w/o text - still write out + // attribute info though + if ( mnTextSize ) + aTextObj.Write( &rOut ); + + if ( pPropOpt && mType != "drawing.Table" ) + ImplAdjustFirstLineLineSpacing( aTextObj, *pPropOpt ); + + sal_uInt32 nSize, nPos = rOut.Tell(); + + rOut.WriteUInt32( EPP_StyleTextPropAtom << 16 ).WriteUInt32( 0 ); + ImplWriteParagraphs( rOut, aTextObj ); + ImplWritePortions( rOut, aTextObj ); + nSize = rOut.Tell() - nPos; + rOut.SeekRel( - ( static_cast(nSize) - 4 ) ); + rOut.WriteUInt32( nSize - 8 ); + rOut.SeekRel( nSize - 8 ); + + for ( sal_uInt32 i = 0; i < aTextObj.ParagraphCount(); ++i ) + { + pPara = aTextObj.GetParagraph(i); + for ( std::vector >::const_iterator it = pPara->begin(); it != pPara->end(); ++it ) + { + const PortionObj& rPortion = **it; + if ( rPortion.mpFieldEntry ) + { + const FieldEntry* pFieldEntry = rPortion.mpFieldEntry.get(); + + switch ( pFieldEntry->nFieldType >> 28 ) + { + case 1 : + case 2 : + { + rOut.WriteUInt32( EPP_DateTimeMCAtom << 16 ).WriteUInt32( 8 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ) // TxtOffset to TxtField; + .WriteUChar( pFieldEntry->nFieldType & 0xff ) // Type + .WriteUChar( 0 ).WriteUInt16( 0 ); // PadBytes + } + break; + case 3 : + { + rOut.WriteUInt32( EPP_SlideNumberMCAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ); + } + break; + case 4 : + { + sal_uInt32 nPageIndex = 0; + OUString aPageUrl; + OUString aFile( pFieldEntry->aFieldUrl ); + OUString aTarget( pFieldEntry->aFieldUrl ); + INetURLObject aUrl( pFieldEntry->aFieldUrl ); + if ( INetProtocol::File == aUrl.GetProtocol() ) + aFile = aUrl.PathToFileName(); + else if ( INetProtocol::Smb == aUrl.GetProtocol() ) + { + // Convert smb notation to '\\' and skip the 'smb:' part + aFile = aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE).copy(4); + aFile = aFile.replaceAll( "/", "\\" ); + aTarget = aFile; + } + else if ( pFieldEntry->aFieldUrl.startsWith("#") ) + { + OUString aPage( INetURLObject::decode( pFieldEntry->aFieldUrl, INetURLObject::DecodeMechanism::WithCharset ) ); + aPage = aPage.copy( 1 ); + + std::vector::const_iterator pIter = std::find( + maSlideNameList.begin(),maSlideNameList.end(),aPage); + + if ( pIter != maSlideNameList.end() ) + { + nPageIndex = pIter - maSlideNameList.begin(); + aPageUrl = OUString::number(256 + nPageIndex) + + "," + + OUString::number(nPageIndex + 1) + + ",Slide " + + OUString::number(nPageIndex + 1); + } + } + sal_uInt32 nHyperId(0); + if ( !aPageUrl.isEmpty() ) + nHyperId = ImplInsertBookmarkURL( aPageUrl, 1 | ( nPageIndex << 8 ) | ( 1U << 31 ), pFieldEntry->aRepresentation, "", "", aPageUrl ); + else + nHyperId = ImplInsertBookmarkURL( pFieldEntry->aFieldUrl, 2 | ( nHyperId << 8 ), aFile, aTarget, "", "" ); + + rOut.WriteUInt32( ( EPP_InteractiveInfo << 16 ) | 0xf ).WriteUInt32( 24 ) + .WriteUInt32( EPP_InteractiveInfoAtom << 16 ).WriteUInt32( 16 ) + .WriteUInt32( 0 ) // soundref + .WriteUInt32( nHyperId ) // hyperlink id + .WriteUChar( 4 ) // hyperlink action + .WriteUChar( 0 ) // ole verb + .WriteUChar( 0 ) // jump + .WriteUChar( 0 ) // flags + .WriteUChar( 8 ) // hyperlink type ? + .WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ) + .WriteUInt32( EPP_TxInteractiveInfoAtom << 16 ).WriteUInt32( 8 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ) + .WriteUInt32( pFieldEntry->nFieldEndPos ); + } + break; + case 5 : + { + rOut.WriteUInt32( EPP_GenericDateMCAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ); + } + break; + case 6 : + { + rOut.WriteUInt32( EPP_HeaderMCAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ); + } + break; + case 7 : + { + rOut.WriteUInt32( EPP_FooterMCAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( pFieldEntry->nFieldStartPos ); + } + break; + default: + break; + } + } + } + } + + aTextObj.WriteTextSpecInfo( &rOut ); + + // write Star Office Default TabSizes (if necessary) + if ( aTextObj.ParagraphCount() ) + { + pPara = aTextObj.GetParagraph(0); + sal_uInt32 nParaFlags = 0x1f; + sal_Int16 nMask, nNumberingRule[ 10 ]; + const sal_uInt32 nTabs = pPara->maTabStop.getLength(); + const auto& rTabStops = pPara->maTabStop; + + for ( sal_uInt32 i = 0; i < aTextObj.ParagraphCount(); ++i ) + { + pPara = aTextObj.GetParagraph(i); + if ( pPara->bExtendedParameters ) + { + nMask = 1 << pPara->nDepth; + if ( nParaFlags & nMask ) + { + nParaFlags &=~ nMask; + if ( ( rParaSheet.maParaLevel[ pPara->nDepth ].mnTextOfs != pPara->nTextOfs ) || + ( rParaSheet.maParaLevel[ pPara->nDepth ].mnBulletOfs != pPara->nBulletOfs ) ) + { + nParaFlags |= nMask << 16; + nNumberingRule[ pPara->nDepth << 1 ] = pPara->nTextOfs; + nNumberingRule[ ( pPara->nDepth << 1 ) + 1 ] = static_cast(pPara->nBulletOfs); + } + } + } + } + nParaFlags >>= 16; + + sal_Int32 nDefaultTabSizeSrc = 2011; // I've no idea where this number came from, honestly + const uno::Reference< beans::XPropertySet > xPropSet( mXModel, uno::UNO_QUERY ); + if ( xPropSet.is() ) + { + if(ImplGetPropertyValue( xPropSet, "TabStop" )) + { + sal_Int32 nTabStop( 0 ); + if ( mAny >>= nTabStop ) + nDefaultTabSizeSrc = nTabStop; + } + } + const sal_uInt32 nDefaultTabSize = MapSize( awt::Size( nDefaultTabSizeSrc, 1 ) ).Width; + sal_uInt32 nDefaultTabs = std::abs( maRect.GetWidth() ) / nDefaultTabSize; + if ( nTabs ) + nDefaultTabs -= static_cast( convertMm100ToMasterUnit(rTabStops[ nTabs - 1 ].Position) / nDefaultTabSize ); + if ( static_cast(nDefaultTabs) < 0 ) + nDefaultTabs = 0; + + sal_uInt32 nTabCount = nTabs + nDefaultTabs; + sal_uInt32 i, nTextRulerAtomFlags = 0; + + if ( nTabCount ) + nTextRulerAtomFlags |= 4; + if ( nParaFlags ) + nTextRulerAtomFlags |= ( ( nParaFlags << 3 ) | ( nParaFlags << 8 ) ); + + if ( nTextRulerAtomFlags ) + { + SvStream* pRuleOut = &rOut; + if ( pTextRule ) + { + pTextRule->pOut.reset( new SvMemoryStream( 0x100, 0x100 ) ); + pRuleOut = pTextRule->pOut.get(); + } + + sal_uInt32 nRulePos = pRuleOut->Tell(); + pRuleOut->WriteUInt32( EPP_TextRulerAtom << 16 ).WriteUInt32( 0 ); + pRuleOut->WriteUInt32( nTextRulerAtomFlags ); + if ( nTextRulerAtomFlags & 4 ) + { + pRuleOut->WriteUInt16( nTabCount ); + for ( const css::style::TabStop& rTabStop : rTabStops ) + { + sal_uInt16 nPosition = static_cast( convertMm100ToMasterUnit(rTabStop.Position) ); + sal_uInt16 nType; + switch ( rTabStop.Alignment ) + { + case css::style::TabAlign_DECIMAL : nType = 3; break; + case css::style::TabAlign_RIGHT : nType = 2; break; + case css::style::TabAlign_CENTER : nType = 1; break; + + case css::style::TabAlign_LEFT : + default: nType = 0; + } + pRuleOut->WriteUInt16( nPosition ) + .WriteUInt16( nType ); + } + + sal_uInt32 nWidth = 1; + if ( nTabs ) + nWidth += static_cast( convertMm100ToMasterUnit(rTabStops[ nTabs - 1 ].Position) / nDefaultTabSize ); + nWidth *= nDefaultTabSize; + for ( i = 0; i < nDefaultTabs; i++, nWidth += nDefaultTabSize ) + pRuleOut->WriteUInt32( nWidth ); + } + for ( i = 0; i < 5; i++ ) + { + if ( nTextRulerAtomFlags & ( 8 << i ) ) + pRuleOut->WriteInt16( nNumberingRule[ i << 1 ] ); + if ( nTextRulerAtomFlags & ( 256 << i ) ) + pRuleOut->WriteInt16( nNumberingRule[ ( i << 1 ) + 1 ] ); + } + sal_uInt32 nBufSize = pRuleOut->Tell() - nRulePos; + pRuleOut->SeekRel( - ( static_cast(nBufSize) - 4 ) ); + pRuleOut->WriteUInt32( nBufSize - 8 ); + pRuleOut->SeekRel( nBufSize - 8 ); + } + } + if ( !aTextObj.HasExtendedBullets() ) + return; + + if ( !aTextObj.ParagraphCount() ) + return; + + sal_uInt32 nNumberingType = 0, nPos2 = rExtBuStr.Tell(); + + rExtBuStr.WriteUInt32( EPP_PST_ExtendedParagraphAtom << 16 ).WriteUInt32( 0 ); + + for ( sal_uInt32 i = 0; i < aTextObj.ParagraphCount(); ++i ) + { + ParagraphObj* pBulletPara = aTextObj.GetParagraph(i); + sal_uInt32 nBulletFlags = 0; + sal_uInt16 nBulletId = pBulletPara->nBulletId; + + if ( pBulletPara->bExtendedBulletsUsed ) + { + nBulletFlags = 0x800000; + if ( pBulletPara->nNumberingType != SVX_NUM_BITMAP ) + nBulletFlags = 0x3000000; + } + rExtBuStr.WriteUInt32( nBulletFlags ); + + if ( nBulletFlags & 0x800000 ) + rExtBuStr.WriteUInt16( nBulletId ); + if ( nBulletFlags & 0x1000000 ) + { + switch( pBulletPara->nNumberingType ) + { + case SVX_NUM_NUMBER_NONE : + case SVX_NUM_CHAR_SPECIAL : + nNumberingType = 0; + break; + case SVX_NUM_CHARS_UPPER_LETTER : + case SVX_NUM_CHARS_UPPER_LETTER_N : + case SVX_NUM_CHARS_LOWER_LETTER : + case SVX_NUM_CHARS_LOWER_LETTER_N : + case SVX_NUM_ROMAN_UPPER : + case SVX_NUM_ROMAN_LOWER : + case SVX_NUM_ARABIC : + case SVX_NUM_NUMBER_UPPER_ZH: + case SVX_NUM_CIRCLE_NUMBER: + case SVX_NUM_NUMBER_UPPER_ZH_TW: + case SVX_NUM_NUMBER_LOWER_ZH: + case SVX_NUM_FULL_WIDTH_ARABIC: + nNumberingType = pBulletPara->nMappedNumType; + break; + + case SVX_NUM_BITMAP : + nNumberingType = 0; + break; + default: break; + } + rExtBuStr.WriteUInt32( nNumberingType ); + } + if ( nBulletFlags & 0x2000000 ) + rExtBuStr.WriteUInt16( pBulletPara->nStartWith ); + rExtBuStr.WriteUInt32( 0 ).WriteUInt32( 0 ); + } + sal_uInt32 nBulletSize = ( rExtBuStr.Tell() - nPos2 ) - 8; + rExtBuStr.SeekRel( - ( static_cast(nBulletSize) + 4 ) ); + rExtBuStr.WriteUInt32( nBulletSize ); + rExtBuStr.SeekRel( nBulletSize ); +} + +void PPTWriter::ImplWriteClickAction( SvStream& rSt, css::presentation::ClickAction eCa, bool bMediaClickAction ) +{ + sal_uInt32 nSoundRef = 0; // a reference to a sound in the sound collection, or NULL. + sal_uInt32 nHyperLinkID = 0;// a persistent unique identifier to an external hyperlink object (only valid when action == HyperlinkAction). + sal_uInt8 nAction = 0; // Action See Action Table + sal_uInt8 const nOleVerb = 0; // OleVerb Only valid when action == OLEAction. OLE verb to use, 0 = first verb, 1 = second verb, etc. + sal_uInt8 nJump = 0; // Jump See Jump Table + sal_uInt8 const nFlags = 0; // Bit 1: Animated. If 1, then button is animated + // Bit 2: Stop sound. If 1, then stop current sound when button is pressed. + // Bit 3: CustomShowReturn. If 1, and this is a jump to custom show, then return to this slide after custom show. + sal_uInt8 nHyperLinkType = 0;// HyperlinkType a value from the LinkTo enum, such as LT_URL (only valid when action == HyperlinkAction). + + OUString aFile; + + /* + Action Table: Action Value + NoAction 0 + MacroAction 1 + RunProgramAction 2 + JumpAction 3 + HyperlinkAction 4 + OLEAction 5 + MediaAction 6 + CustomShowAction 7 + + Jump Table: Jump Value + NoJump 0 + NextSlide, 1 + PreviousSlide, 2 + FirstSlide, 3 + LastSlide, 4 + LastSlideViewed 5 + EndShow 6 + */ + + if ( bMediaClickAction ) + nAction = 6; + else switch( eCa ) + { + case css::presentation::ClickAction_STOPPRESENTATION : + nJump += 2; + [[fallthrough]]; + case css::presentation::ClickAction_LASTPAGE : + nJump++; + [[fallthrough]]; + case css::presentation::ClickAction_FIRSTPAGE : + nJump++; + [[fallthrough]]; + case css::presentation::ClickAction_PREVPAGE : + nJump++; + [[fallthrough]]; + case css::presentation::ClickAction_NEXTPAGE : + { + nJump++; + nAction = 3; + } + break; + case css::presentation::ClickAction_SOUND : + { + if ( ImplGetPropertyValue( "Bookmark" ) ) + nSoundRef = maSoundCollection.GetId( *o3tl::doAccess(mAny) ); + } + break; + case css::presentation::ClickAction_PROGRAM : + { + if ( ImplGetPropertyValue( "Bookmark" ) ) + { + INetURLObject aUrl( *o3tl::doAccess(mAny) ); + if ( INetProtocol::File == aUrl.GetProtocol() ) + { + aFile = aUrl.PathToFileName(); + nAction = 2; + } + } + } + break; + + case css::presentation::ClickAction_BOOKMARK : + { + if ( ImplGetPropertyValue( "Bookmark" ) ) + { + OUString aBookmark( *o3tl::doAccess(mAny) ); + sal_uInt32 nIndex = 0; + for ( const auto& rSlideName : maSlideNameList ) + { + if ( rSlideName == aBookmark ) + { + // Bookmark is a link to a document page + nAction = 4; + nHyperLinkType = 7; + + OUString aHyperString = OUString::number(256 + nIndex) + + "," + + OUString::number(nIndex + 1) + + ",Slide " + + OUString::number(nIndex + 1); + nHyperLinkID = ImplInsertBookmarkURL( aHyperString, 1 | ( nIndex << 8 ) | ( 1U << 31 ), aBookmark, "", "", aHyperString ); + } + nIndex++; + } + } + } + break; + + case css::presentation::ClickAction_DOCUMENT : + { + if ( ImplGetPropertyValue( "Bookmark" ) ) + { + OUString aBookmark( *o3tl::doAccess(mAny) ); + if ( !aBookmark.isEmpty() ) + { + nAction = 4; + nHyperLinkType = 8; + + OUString aBookmarkFile( aBookmark ); + INetURLObject aUrl( aBookmark ); + if ( INetProtocol::File == aUrl.GetProtocol() ) + aBookmarkFile = aUrl.PathToFileName(); + nHyperLinkID = ImplInsertBookmarkURL( aBookmark, sal_uInt32(2 | ( 1U << 31 )), aBookmarkFile, aBookmark, "", "" ); + } + } + } + break; + + case css::presentation::ClickAction_INVISIBLE : + case css::presentation::ClickAction_VERB : + case css::presentation::ClickAction_VANISH : + case css::presentation::ClickAction_MACRO : + default : + break; + } + + sal_uInt32 nContainerSize = 24; + if ( nAction == 2 ) + nContainerSize += ( aFile.getLength() * 2 ) + 8; + rSt.WriteUInt32( ( EPP_InteractiveInfo << 16 ) | 0xf ).WriteUInt32( nContainerSize ) + .WriteUInt32( EPP_InteractiveInfoAtom << 16 ).WriteUInt32( 16 ) + .WriteUInt32( nSoundRef ) + .WriteUInt32( nHyperLinkID ) + .WriteUChar( nAction ) + .WriteUChar( nOleVerb ) + .WriteUChar( nJump ) + .WriteUChar( nFlags ) + .WriteUInt32( nHyperLinkType ); + + if ( nAction == 2 ) // run program Action + { + sal_Int32 nLen = aFile.getLength(); + rSt.WriteUInt32( ( EPP_CString << 16 ) | 0x20 ).WriteUInt32( nLen * 2 ); + for ( sal_Int32 i = 0; i < nLen; i++ ) + rSt.WriteUInt16( aFile[i] ); + } + + rSt.WriteUInt32( ( EPP_InteractiveInfo << 16 ) | 0x1f ).WriteUInt32( 24 ) // Mouse Over Action + .WriteUInt32( EPP_InteractiveInfo << 16 ).WriteUInt32( 16 ); + for ( int i = 0; i < 4; i++, rSt.WriteUInt32( 0 ) ) ; +} + +bool PPTWriter::ImplGetEffect( const css::uno::Reference< css::beans::XPropertySet > & rPropSet, + css::presentation::AnimationEffect& eEffect, + css::presentation::AnimationEffect& eTextEffect, + bool& bIsSound ) +{ + css::uno::Any aAny; + if ( GetPropertyValue( aAny, rPropSet, "Effect" ) ) + aAny >>= eEffect; + else + eEffect = css::presentation::AnimationEffect_NONE; + + if ( GetPropertyValue( aAny, rPropSet, "TextEffect" ) ) + aAny >>= eTextEffect; + else + eTextEffect = css::presentation::AnimationEffect_NONE; + if ( GetPropertyValue( aAny, rPropSet, "SoundOn" ) ) + aAny >>= bIsSound; + else + bIsSound = false; + + bool bHasEffect = ( ( eEffect != css::presentation::AnimationEffect_NONE ) + || ( eTextEffect != css::presentation::AnimationEffect_NONE ) + || bIsSound ); + return bHasEffect; +}; + +bool PPTWriter::ImplCreatePresentationPlaceholder( const bool bMasterPage, + const sal_uInt32 nStyleInstance, const sal_uInt8 nPlaceHolderId ) +{ + bool bRet = ImplGetText(); + if ( bRet && bMasterPage ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + sal_uInt32 nPresShapeID = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, nPresShapeID ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x50001 ); + mnTxId += 0x60; + aPropOpt.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropOpt.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110001 ); + aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x8000001 ); + aPropOpt.AddOpt( ESCHER_Prop_shadowColor, 0x8000002 ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + sal_uInt32 nLineFlags = 0x90001; + if ( aPropOpt.GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ) ) + nLineFlags |= 0x10001; // draw dashed line if no line + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ); + + SvMemoryStream aExtBu( 0x200, 0x200 ); + SvMemoryStream aClientTextBox( 0x200, 0x200 ); + ImplWriteTextStyleAtom( aClientTextBox, nStyleInstance, 0, nullptr, aExtBu, &aPropOpt ); + + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId ); + aPropOpt.CreateShapeProperties( mXShape ); + aPropOpt.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 8, ESCHER_ClientAnchor ); + mpStrm->WriteInt16( maRect.Top() ).WriteInt16( maRect.Left() ).WriteInt16( maRect.Right() ).WriteInt16( maRect.Bottom() ); // top, left, right, bottom ???? + mpPptEscherEx->OpenContainer( ESCHER_ClientData ); + mpPptEscherEx->AddAtom( 8, EPP_OEPlaceholderAtom ); + mpStrm->WriteUInt32( 0 ) // PlacementID + .WriteUChar( nPlaceHolderId ) // PlaceHolderID + .WriteUChar( 0 ) // Size of PlaceHolder ( 0 = FULL, 1 = HALF, 2 = QUARTER ) + .WriteUInt16( 0 ); // padword + mpPptEscherEx->CloseContainer(); // ESCHER_ClientData + + if ( aClientTextBox.Tell() ) + { + mpStrm->WriteUInt32( ( ESCHER_ClientTextbox << 16 ) | 0xf ) + .WriteUInt32( aClientTextBox.Tell() ); + + mpStrm->WriteBytes(aClientTextBox.GetData(), aClientTextBox.Tell()); + } + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + else + bRet = false; + return bRet; +} + +void PPTWriter::ImplCreateShape( sal_uInt32 nType, ShapeFlag nFlags, EscherSolverContainer& rSolver ) +{ + sal_uInt32 nId = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( nType, nFlags, nId ); + rSolver.AddShape( mXShape, nId ); +} + +void PPTWriter::ImplCreateTextShape( EscherPropertyContainer& rPropOpt, EscherSolverContainer& rSolver, bool bFill ) +{ + mnTextStyle = EPP_TEXTSTYLE_TEXT; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, rSolver ); + if ( bFill ) + rPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + if ( ImplGetText() ) + { + mnTxId += 0x60; + rPropOpt.CreateTextProperties( mXPropSet, mnTxId ); + } +} + +void PPTWriter::ImplWritePage( const PHLayout& rLayout, EscherSolverContainer& aSolverContainer, PageType ePageType, bool bMasterPage, int nPageNumber ) +{ + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // sal_uInt32 nGroupLevel = 0; + + sal_uInt32 nGroups, nShapes, nShapeCount, nPer, nLastPer, nIndices, nOlePictureId; + css::awt::Point aTextRefPoint; + + nShapes = mXShapes->getCount(); + ResetGroupTable( nShapes ); + + nIndices = nLastPer = nShapeCount = 0; + + bool bIsTitlePossible = true; // powerpoint is not able to handle more than one title + + sal_uInt32 nOutlinerCount = 0; // the outline objects have to conform to the layout, + sal_uInt32 nPrevTextStyle = 0; // there are no more than two allowed + + nOlePictureId = 0; + + bool bAdditionalText = false; + + bool bSecOutl = false; + sal_uInt32 nPObjects = 0; + + std::unique_ptr pClientTextBox; + std::unique_ptr pClientData; + + while( GetNextGroupEntry() ) + { + nShapeCount++; + + nPer = ( 5 * nShapeCount ) / nShapes; + if ( nPer != nLastPer ) + { + nLastPer = nPer; + sal_uInt32 nValue = mnPagesWritten * 5 + nPer; + if ( nValue > mnStatMaxValue ) + nValue = mnStatMaxValue; + if ( mbStatusIndicator && ( nValue > mnLatestStatValue ) ) + { + mXStatusIndicator->setValue( nValue ); + mnLatestStatValue = nValue; + } + } + nGroups = GetGroupsClosed(); + for ( sal_uInt32 i = 0; i < nGroups; i++, mpPptEscherEx->LeaveGroup() ) ; + + if ( GetShapeByIndex( GetCurrentGroupIndex(), true ) ) + { + bool bIsSound; + bool bMediaClickAction = false; + css::presentation::AnimationEffect eAe; + css::presentation::AnimationEffect eTe; + + bool bEffect = ImplGetEffect( mXPropSet, eAe, eTe, bIsSound ); + css::presentation::ClickAction eCa = css::presentation::ClickAction_NONE; + if ( ImplGetPropertyValue( "OnClick" ) ) + mAny >>= eCa; + + bool bGroup = mType == "drawing.Group"; + bool bOpenBezier = mType == "drawing.OpenBezier"; + bool bClosedBezier = mType == "drawing.ClosedBezier"; + bool bPolyPolygon = mType == "drawing.PolyPolygon"; + bool bPolyLine = mType == "drawing.PolyLine"; + OUString aGraphicPropertyName("Graphic"); + + const css::awt::Size aSize100thmm( mXShape->getSize() ); + const css::awt::Point aPoint100thmm( mXShape->getPosition() ); + ::tools::Rectangle aRect100thmm( Point( aPoint100thmm.X, aPoint100thmm.Y ), Size( aSize100thmm.Width, aSize100thmm.Height ) ); + EscherPropertyContainer aPropOpt( mpPptEscherEx->GetGraphicProvider(), mpPicStrm.get(), aRect100thmm ); + + if ( bGroup ) + { + css::uno::Reference< css::container::XIndexAccess > + aXIndexAccess( mXShape, css::uno::UNO_QUERY ); + if ( EnterGroup( aXIndexAccess ) ) + { + std::unique_ptr pTmp; + if ( eCa != css::presentation::ClickAction_NONE ) + { + pTmp.reset(new SvMemoryStream(0x200, 0x200)); + ImplWriteClickAction( *pTmp, eCa, bMediaClickAction ); + } + sal_uInt32 nShapeId = mpPptEscherEx->EnterGroup(&maRect, pTmp.get()); + aSolverContainer.AddShape( mXShape, nShapeId ); + } + } + else + { + bool bIsFontwork = false; + bool bIsHatching = false; + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "IsFontwork", true ) ) + aAny >>= bIsFontwork; + if ( GetPropertyValue( aAny, mXPropSet, "FillStyle", true ) ) + { + css::drawing::FillStyle eFS; + aAny >>= eFS; + bIsHatching = eFS == css::drawing::FillStyle_HATCH; + if (mType == "drawing.Custom" && eFS == drawing::FillStyle_BITMAP) + { + ShapeFlag nMirrorFlags; + OUString sCustomShapeType; + MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( + mXShape, nMirrorFlags, sCustomShapeType); + if (eShapeType == mso_sptMax) + { + // We can't map this custom shape to a PPT preset and it has a bitmap + // fill. Make sure that at least the bitmap fill is not lost. + mType = "drawing.GraphicObject"; + aGraphicPropertyName = "Bitmap"; + } + } + } + if ( bIsHatching || bIsFontwork || ( mType == "drawing.Measure" ) || ( mType == "drawing.Caption" ) ) + { + if ( ImplGetPropertyValue( "BoundRect" ) ) + { + auto aRect = o3tl::doAccess(mAny); + maPosition = MapPoint( css::awt::Point( aRect->X, aRect->Y ) ); + maSize = MapSize( css::awt::Size( aRect->Width, aRect->Height ) ); + maRect = ::tools::Rectangle( Point( maPosition.X, maPosition.Y ), Size( maSize.Width, maSize.Height ) ); + } + mType = "drawing.dontknow"; + } + } + sal_uInt8 nPlaceHolderAtom = EPP_PLACEHOLDER_NONE; + + mnTextSize = 0; + mnTextStyle = EPP_TEXTSTYLE_NORMAL; + + if ( mType == "drawing.Custom" ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ShapeFlag nMirrorFlags; + OUString sCustomShapeType; + MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( mXShape, nMirrorFlags, sCustomShapeType ); + if ( sCustomShapeType == "col-502ad400" || sCustomShapeType == "col-60da8460" ) + { // sj: creating metafile for customshapes that can't be saved to ms format properly + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "MetaFile", false ) ) + { + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mXShape); + if ( pObj ) + { + ::tools::Rectangle aBound = pObj->GetCurrentBoundRect(); + maPosition = MapPoint( css::awt::Point( aBound.Left(), aBound.Top() ) ); + maSize = MapSize( css::awt::Size ( aBound.GetWidth(), aBound.GetHeight() ) ); + maRect = ::tools::Rectangle( Point( maPosition.X, maPosition.Y ), Size( maSize.Width, maSize.Height ) ); + mnAngle = 0; + } + } + } + else + { + ImplCreateShape( eShapeType, + nMirrorFlags | ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + aPropOpt.CreateCustomShapeProperties( eShapeType, mXShape ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape); + if ( ImplGetText() ) + { + if ( !aPropOpt.IsFontWork() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, true ); + } + } + } + } + else if ( mType == "drawing.Rectangle" ) + { + sal_Int32 nRadius = 0; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + if ( ImplGetPropertyValue( "CornerRadius" ) ) + { + mAny >>= nRadius; + nRadius = MapSize( css::awt::Size( nRadius, 0 ) ).Width; + } + if ( nRadius ) + { + ImplCreateShape( ESCHER_ShpInst_RoundRectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + sal_Int32 nLength = maRect.GetWidth(); + if ( nLength > maRect.GetHeight() ) + nLength = maRect.GetHeight(); + nLength >>= 1; + if ( nRadius >= nLength ) + nRadius = 0x2a30; // 0x2a30 is PPTs maximum radius + else + { + if (nLength != 0) + nRadius = ( 0x2a30 * nRadius ) / nLength; + else + nRadius = 0x2a30; // 0x2a30 is PPTs maximum radius + } + aPropOpt.AddOpt( ESCHER_Prop_adjustValue, nRadius ); + } + else + { + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + } + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + if ( ImplGetText() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, false, false ); + } + } + else if ( mType == "drawing.Ellipse" ) + { + css::drawing::CircleKind eCircleKind( css::drawing::CircleKind_FULL ); + PolyStyle ePolyKind = PolyStyle::Chord; + if ( ImplGetPropertyValue( "CircleKind" ) ) + { + mAny >>= eCircleKind; + switch ( eCircleKind ) + { + case css::drawing::CircleKind_SECTION : + { + ePolyKind = PolyStyle::Pie; + } + break; + case css::drawing::CircleKind_ARC : + { + ePolyKind = PolyStyle::Arc; + } + break; + + case css::drawing::CircleKind_CUT : + { + ePolyKind = PolyStyle::Chord; + } + break; + + default: + eCircleKind = css::drawing::CircleKind_FULL; + } + } + if ( eCircleKind == css::drawing::CircleKind_FULL ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_Ellipse, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + if ( ImplGetText() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, false, false ); + } + } + else + { + sal_Int32 nStartAngle, nEndAngle; + if ( !ImplGetPropertyValue( "CircleStartAngle" ) ) + continue; + nStartAngle = *o3tl::doAccess(mAny); + if( !ImplGetPropertyValue( "CircleEndAngle" ) ) + continue; + nEndAngle = *o3tl::doAccess(mAny); + css::awt::Point aPoint( mXShape->getPosition() ); + css::awt::Size aSize( mXShape->getSize() ); + css::awt::Point aStart, aEnd, aCenter; + ::tools::Rectangle aRect( Point( aPoint.X, aPoint.Y ), Size( aSize.Width, aSize.Height ) ); + aStart.X = static_cast( cos( basegfx::deg2rad<100>(nStartAngle) ) * 100.0 ); + aStart.Y = - static_cast( sin( basegfx::deg2rad<100>(nStartAngle) ) * 100.0 ); + aEnd.X = static_cast( cos( basegfx::deg2rad<100>(nEndAngle) ) * 100.0 ); + aEnd.Y = - static_cast( sin( basegfx::deg2rad<100>(nEndAngle) ) * 100.0 ); + aCenter.X = aPoint.X + ( aSize.Width / 2 ); + aCenter.Y = aPoint.Y + ( aSize.Height / 2 ); + aStart.X += aCenter.X; + aStart.Y += aCenter.Y; + aEnd.X += aCenter.X; + aEnd.Y += aCenter.Y; + tools::Polygon aPolygon( aRect, Point( aStart.X, aStart.Y ), Point( aEnd.X, aEnd.Y ), ePolyKind ); + bool bNeedText = true; + if ( mnAngle ) + { + aPolygon.Rotate( aRect.TopLeft(), Degree10(static_cast( mnAngle / 10 )) ); + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bNeedText = false; + bAdditionalText = true; + mnTextSize = 0; + } + mnAngle = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + switch ( ePolyKind ) + { + case PolyStyle::Pie : + case PolyStyle::Chord : + { + if ( aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect, &aPolygon ) ) + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + } + break; + + case PolyStyle::Arc : + { + if ( aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect, &aPolygon ) ) + aPropOpt.CreateLineProperties( mXPropSet, false ); + } + break; + } + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + if ( bNeedText && ImplGetText() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, false, false ); + } + } + } + else if ( mType == "drawing.Control" ) + { + css::uno::Reference< css::drawing::XControlShape > aXControlShape( mXShape, css::uno::UNO_QUERY ); + if ( !aXControlShape.is() ) + continue; + css::uno::Reference< css::awt::XControlModel > aXControlModel( aXControlShape->getControl() ); + if ( !aXControlModel.is() ) + continue; + + sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT; + try + { + // try to get the aspect when available + css::uno::Reference< css::beans::XPropertySet > xShapeProps( mXShape, css::uno::UNO_QUERY_THROW ); + xShapeProps->getPropertyValue("Aspect") >>= nAspect; + } + catch( css::uno::Exception& ) + {} + + mpExEmbed->WriteUInt32( 0xf | ( EPP_ExControl << 16 ) ) + .WriteUInt32( 0 ); // Size of this container + + sal_uInt32 nSize, nOldPos = mpExEmbed->Tell(); + + sal_uInt32 nPageId = nPageNumber; + if ( ePageType == MASTER ) + nPageId |= 0x80000000; + else + nPageId += 0x100; + mpExEmbed->WriteUInt32( EPP_ExControlAtom << 16 ) + .WriteUInt32( 4 ) + .WriteUInt32( nPageId ); + std::unique_ptr pEntry( new PPTExOleObjEntry( OCX_CONTROL, mpExEmbed->Tell() ) ); + pEntry->xControlModel = aXControlModel; + pEntry->xShape = mXShape; + maExOleObj.push_back( std::move(pEntry) ); + + mnExEmbed++; + + mpExEmbed->WriteUInt32( 1 | ( EPP_ExOleObjAtom << 16 ) ) + .WriteUInt32( 24 ) + .WriteUInt32( nAspect ) + .WriteUInt32( 2 ) + .WriteUInt32( mnExEmbed ) + .WriteUInt32( 0 ) + .WriteUInt32( 4 ) // index to the persist table + .WriteUInt32( 0x0012de00 ); + + css::awt::Size aSize; + OUString aControlName; + tools::SvRef xTemp( new SotStorage( new SvMemoryStream(), true ) ); + if ( oox::ole::MSConvertOCXControls::WriteOCXStream( mXModel, xTemp, aXControlModel, aSize, aControlName ) ) + { + OUString aUserName( xTemp->GetUserName() ); + OUString aOleIdentifier; + if ( !aUserName.isEmpty() ) + { + tools::SvRef xCompObj = xTemp->OpenSotStream( + "\1CompObj", + StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYALL ); + sal_uInt32 const nStreamLen = xCompObj->remainingSize(); + sal_Int16 nVersion, nByteOrder; + sal_Int32 nWinVersion, nVal, nStringLen; + xCompObj->ReadInt16( nVersion ) + .ReadInt16( nByteOrder ) + .ReadInt32( nWinVersion ) + .ReadInt32( nVal ); + xCompObj->SeekRel( 16 ); // skipping clsid + xCompObj->ReadInt32( nStringLen ); + if ( ( xCompObj->Tell() + nStringLen ) < nStreamLen ) + { + xCompObj->SeekRel( nStringLen ); // now skipping the UserName; + xCompObj->ReadInt32( nStringLen ); + if ( ( xCompObj->Tell() + nStringLen ) < nStreamLen ) + { + xCompObj->SeekRel( nStringLen ); // now skipping the clipboard formatname + xCompObj->ReadInt32( nStringLen ); + if ( ( nStringLen > 1 ) && ( ( xCompObj->Tell() + nStringLen ) < nStreamLen ) ) + { // i think that the OleIdentifier will follow + OString aTemp = read_uInt8s_ToOString(*xCompObj, nStringLen - 1); + aOleIdentifier = OStringToOUString(aTemp, RTL_TEXTENCODING_MS_1252); + } + } + } + } + + PPTWriter::WriteCString( *mpExEmbed, aControlName, 1 ); + PPTWriter::WriteCString( *mpExEmbed, aOleIdentifier, 2 ); + PPTWriter::WriteCString( *mpExEmbed, aUserName, 3 ); + } + nSize = mpExEmbed->Tell() - nOldPos; + mpExEmbed->Seek( nOldPos - 4 ); + mpExEmbed->WriteUInt32( nSize ); + mpExEmbed->Seek( STREAM_SEEK_TO_END ); + nOlePictureId = mnExEmbed; + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ShapeFlag const nSpFlags = ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | ShapeFlag::OLEShape; + ImplCreateShape( ESCHER_ShpInst_HostControl, nSpFlags, aSolverContainer ); + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "MetaFile", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + //export form control graphic + else if ( aPropOpt.CreateBlipPropertiesforOLEControl(mXPropSet,mXShape)) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + aPropOpt.AddOpt( ESCHER_Prop_pictureId, mnExEmbed ); + aPropOpt.AddOpt( ESCHER_Prop_pictureActive, 0x10000 ); + + if ( !aControlName.isEmpty() ) + { + aPropOpt.AddOpt(ESCHER_Prop_wzName, aControlName); + } + } + else if ( mType == "drawing.Connector" ) + { + sal_uInt16 nSpType; + ShapeFlag nSpFlags; + css::awt::Rectangle aNewRect; + if ( !aPropOpt.CreateConnectorProperties( mXShape, aSolverContainer, aNewRect, nSpType, nSpFlags ) ) + continue; + + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( nSpType, nSpFlags, aSolverContainer ); + + // #119459# for connector shape, the start point and end point is fixed, and should not be rotated. + mnAngle = 0; + } + else if ( mType == "drawing.Measure" ) + { + continue; + } + else if ( mType == "drawing.Line" ) + { + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_LINE, false, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + if ( ImplGetText() ) + { + aTextRefPoint = css::awt::Point( maRect.Left(), maRect.Top() ); + mnTextSize = 0; + bAdditionalText = true; + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( &maRect,0 ); + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ShapeFlag nFlags = ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty; + + if ( maRect.Top() > maRect.Bottom() ) + nFlags |= ShapeFlag::FlipV; + if ( maRect.Left() > maRect.Right() ) + nFlags |= ShapeFlag::FlipH; + + ImplCreateShape( ESCHER_ShpInst_Line, nFlags, aSolverContainer ); + aPropOpt.AddOpt( ESCHER_Prop_shapePath, ESCHER_ShapeComplex ); + aPropOpt.CreateLineProperties( mXPropSet, false ); + mnAngle = 0; + } + else if ( bPolyPolygon ) + { + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bAdditionalText = true; + mnTextSize = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + mnAngle = 0; + } + else if ( bPolyLine ) + { + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bAdditionalText = true; + mnTextSize = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + aPropOpt.CreateLineProperties( mXPropSet, false ); + mnAngle = 0; + } + else if ( bOpenBezier ) + { + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bAdditionalText = true; + mnTextSize = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, true, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + aPropOpt.CreateLineProperties( mXPropSet, false ); + mnAngle = 0; + } + else if ( bClosedBezier ) + { + if ( ImplGetText() ) + { + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->EnterGroup( 0,0 ); + // nGroupLevel = mpPptEscherEx->GetGroupLevel(); + bAdditionalText = true; + mnTextSize = 0; + } + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_NotPrimitive, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, true, aNewRect ); + maRect = MapRectangle( aNewRect ); + maPosition = css::awt::Point( maRect.Left(), maRect.Top() ); + maSize = css::awt::Size( maRect.GetWidth(), maRect.GetHeight() ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + mnAngle = 0; + } + else if ( ( mType == "drawing.GraphicObject" ) || ( mType == "presentation.GraphicObject" ) ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + + // a GraphicObject can also be a ClickMe element + if ( mbEmptyPresObj && ( ePageType == NORMAL ) ) + { + nPlaceHolderAtom = rLayout.nUsedObjectPlaceHolder; + ImplCreateShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveAnchor | ShapeFlag::HaveMaster, aSolverContainer ); + mnTxId += 0x60; + aPropOpt.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, mnShapeMasterBody ); + } + else + { + mXText.set( mXShape, css::uno::UNO_QUERY ); + + if ( mXText.is() ) + mnTextSize = mXText->getString().getLength(); + + if ( mnTextSize ) // graphic object or area fill + { + /* SJ #i34951#: because M. documents are not allowing GraphicObjects containing text, we + have to create a simple Rectangle with fill bitmap instead (while not allowing BitmapMode_Repeat). + */ + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "Graphic", true, true, false ) ) + { + aPropOpt.AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapNone ); + aPropOpt.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 ); + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x8000000 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + if ( ImplGetText() ) + { + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId, false, false ); + } + } + } + else + { + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + + if (aPropOpt.CreateGraphicProperties(mXPropSet, aGraphicPropertyName, false, + true)) + { + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + } + } + } + else if ( ( mType == "drawing.Text" ) || ( mType == "presentation.Notes" ) ) + { + if ( ( ePageType == NOTICE ) && mbPresObj ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Notes, EPP_PLACEHOLDER_MASTERNOTESBODYIMAGE ) ) + continue; + else + nPlaceHolderAtom = EPP_PLACEHOLDER_NOTESBODY; + } + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + else if ( mType == "presentation.TitleText" ) + { + if ( mbPresObj ) + { + if ( ( ePageType == NOTICE ) && mbEmptyPresObj ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + nPlaceHolderAtom = EPP_PLACEHOLDER_MASTERNOTESBODYIMAGE; + ImplCreateShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveAnchor, aSolverContainer ); + aPropOpt.CreateLineProperties( mXPropSet, false ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 ); + } + else if ( rLayout.bTitlePossible && bIsTitlePossible ) + { + bIsTitlePossible = false; + + ImplGetText(); + TextObjBinary aTextObj( mXText, EPP_TEXTTYPE_Title, maFontCollection, static_cast(*this) ); + if ( ePageType == MASTER ) + { + if ( mnTextSize ) + { + OUString aUString( mXText->getString() ); + sal_uInt16 nChar; + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mnShapeMasterTitle = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + mnShapeMasterTitle ); + EscherPropertyContainer aPropertyOptions; + aPropertyOptions.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x50001 ); + mnTxId += 0x60; + aPropertyOptions.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropertyOptions.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle ); + aPropertyOptions.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110001 ); + aPropertyOptions.AddOpt( ESCHER_Prop_lineColor, 0x8000001 ); + aPropertyOptions.AddOpt( ESCHER_Prop_shadowColor, 0x8000002 ); + aPropertyOptions.CreateFillProperties( mXPropSet, true, mXShape ); + sal_uInt32 nLineFlags = 0x90001; + if ( aPropertyOptions.GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ) ) + nLineFlags |= 0x10001; // draw dashed line if no line + aPropertyOptions.AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ); + mnTxId += 0x60; + aPropertyOptions.CreateTextProperties( mXPropSet, mnTxId ); + ImplAdjustFirstLineLineSpacing( aTextObj, aPropOpt ); + aPropertyOptions.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 8, ESCHER_ClientAnchor ); + mpStrm->WriteInt16( maRect.Top() ).WriteInt16( maRect.Left() ).WriteInt16( maRect.Right() ).WriteInt16( maRect.Bottom() ); // top, left, right, bottom ???? + mpPptEscherEx->OpenContainer( ESCHER_ClientData ); + mpPptEscherEx->AddAtom( 8, EPP_OEPlaceholderAtom ); + mpStrm->WriteUInt32( 0 ) // PlacementID + .WriteUChar( EPP_PLACEHOLDER_MASTERTITLE ) // PlaceHolderID + .WriteUChar( 0 ) // Size of PlaceHolder ( 0 = FULL, 1 = HALF, 2 = QUARTER ) + .WriteUInt16( 0 ); // padword + mpPptEscherEx->CloseContainer(); // ESCHER_ClientData + mpPptEscherEx->OpenContainer( ESCHER_ClientTextbox ); + mpPptEscherEx->AddAtom( 4, EPP_TextHeaderAtom ); + mpStrm->WriteUInt32( EPP_TEXTTYPE_Title ); + mpPptEscherEx->AddAtom( mnTextSize << 1, EPP_TextCharsAtom ); + const sal_Unicode* pString = aUString.getStr(); + for ( sal_uInt32 i = 0; i < mnTextSize; i++ ) + { + nChar = pString[ i ]; // 0xa -> 0xb soft newline + if ( nChar == 0xa ) + nChar++; // 0xd -> 0xd hard newline + mpStrm->WriteUInt16( nChar ); + } + mpPptEscherEx->AddAtom( 6, EPP_BaseTextPropAtom ); + mpStrm->WriteUInt32( mnTextSize + 1 ).WriteUInt16( 0 ); + mpPptEscherEx->AddAtom( 10, EPP_TextSpecInfoAtom ); + mpStrm->WriteUInt32( mnTextSize + 1 ).WriteUInt32( 1 ).WriteUInt16( 0 ); + mpPptEscherEx->CloseContainer(); // ESCHER_ClientTextBox + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + continue; + } + else + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mnTextStyle = EPP_TEXTSTYLE_TITLE; + nPlaceHolderAtom = rLayout.nTypeOfTitle; + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveMaster, + aSolverContainer ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, mnShapeMasterTitle ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId ); + ImplAdjustFirstLineLineSpacing( aTextObj, aPropOpt ); + if ( mbEmptyPresObj ) + { + sal_uInt32 nNoLineDrawDash = 0; + aPropOpt.GetOpt( ESCHER_Prop_fNoLineDrawDash, nNoLineDrawDash ); + nNoLineDrawDash |= 0x10001; + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, nNoLineDrawDash ); + } + } + } + else + mbPresObj = false; + } + if ( !mbPresObj ) + { + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( ( mType == "presentation.Outliner" ) || ( mType == "presentation.Subtitle" ) ) + { + if ( mbPresObj ) + { + nOutlinerCount++; + if ( (rLayout.bOutlinerPossible && ( nOutlinerCount == 1 )) || + (( rLayout.bSecOutlinerPossible && ( nOutlinerCount == 2 ) ) && ( nPrevTextStyle == EPP_TEXTSTYLE_BODY )) + ) + { + ImplGetText(); + TextObjBinary aTextObj( mXText, EPP_TEXTTYPE_Body, maFontCollection, static_cast(*this) ); + if ( ePageType == MASTER ) + { + nPrevTextStyle = EPP_TEXTSTYLE_TITLE; + if ( mnTextSize ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + mnShapeMasterBody = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + mnShapeMasterBody ); + EscherPropertyContainer aPropOpt2; + aPropOpt2.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x50001 ); + mnTxId += 0x60; + aPropOpt2.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropOpt2.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110001 ); + aPropOpt2.AddOpt( ESCHER_Prop_lineColor, 0x8000001 ); + aPropOpt2.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90001 ); + aPropOpt2.AddOpt( ESCHER_Prop_shadowColor, 0x8000002 ); + aPropOpt2.CreateFillProperties( mXPropSet, true, mXShape ); + sal_uInt32 nLineFlags = 0x90001; + if ( aPropOpt2.GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ) ) + nLineFlags |= 0x10001; // draw dashed line if no line + aPropOpt2.AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ); + mnTxId += 0x60; + aPropOpt2.CreateTextProperties( mXPropSet, mnTxId ); + ImplAdjustFirstLineLineSpacing( aTextObj, aPropOpt2 ); + aPropOpt2.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 8, ESCHER_ClientAnchor ); + mpStrm->WriteInt16( maRect.Top() ).WriteInt16( maRect.Left() ).WriteInt16( maRect.Right() ).WriteInt16( maRect.Bottom() ); // top, left, right, bottom ???? + mpPptEscherEx->OpenContainer( ESCHER_ClientData ); + mpPptEscherEx->AddAtom( 8, EPP_OEPlaceholderAtom ); + sal_uInt8 PlaceHolderID = ( mType == "presentation.Subtitle") ? EPP_PLACEHOLDER_MASTERSUBTITLE:EPP_PLACEHOLDER_MASTERBODY; + mpStrm->WriteUInt32( 1 ) // PlacementID + .WriteUChar( PlaceHolderID )/*(sal_uInt8)EPP_PLACEHOLDER_MASTERBODY */ // PlaceHolderID + .WriteUChar( 0 ) // Size of PlaceHolder ( 0 = FULL, 1 = HALF, 2 = QUARTER ) + .WriteUInt16( 0 ); // padword + mpPptEscherEx->CloseContainer(); // ESCHER_ClientData + mpPptEscherEx->OpenContainer( ESCHER_ClientTextbox ); // printf + mpPptEscherEx->AddAtom( 4, EPP_TextHeaderAtom ); + if ( mType == "presentation.Subtitle") + mpStrm->WriteUInt32( EPP_TEXTTYPE_CenterBody ); + else + mpStrm->WriteUInt32( EPP_TEXTTYPE_Body ); + mnTextSize = aTextObj.Count(); + aTextObj.Write( mpStrm.get() ); + mpPptEscherEx->BeginAtom(); + for ( sal_uInt32 i = 0; i < aTextObj.ParagraphCount() ; ++i ) + { + ParagraphObj* pPara = aTextObj.GetParagraph(i); + mpStrm->WriteUInt32( pPara->CharacterCount() ) + .WriteUInt16( pPara->nDepth ); + } + mpPptEscherEx->EndAtom( EPP_BaseTextPropAtom ); + mpPptEscherEx->AddAtom( 10, EPP_TextSpecInfoAtom ); + mpStrm->WriteUInt32( mnTextSize ).WriteUInt32( 1 ).WriteUInt16( 0 ); + + mpPptEscherEx->CloseContainer(); // ESCHER_ClientTextBox + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + continue; + } + else + { + mnTextStyle = EPP_TEXTSTYLE_BODY; + nPlaceHolderAtom = rLayout.nTypeOfOutliner; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveMaster, + aSolverContainer ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, mnShapeMasterBody ); + aPropOpt.CreateFillProperties( mXPropSet, true, mXShape ); + mnTxId += 0x60; + aPropOpt.CreateTextProperties( mXPropSet, mnTxId ); + ImplAdjustFirstLineLineSpacing( aTextObj, aPropOpt ); + if ( mbEmptyPresObj ) + { + sal_uInt32 nNoLineDrawDash = 0; + aPropOpt.GetOpt( ESCHER_Prop_fNoLineDrawDash, nNoLineDrawDash ); + nNoLineDrawDash |= 0x10001; + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, nNoLineDrawDash ); + } + } + } + else + mbPresObj = false; + } + if ( !mbPresObj ) + { + if (ePageType == MASTER ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(mXShape); + if (pObj && pObj->IsNotVisibleAsMaster()) + continue; + } + + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( ( mType == "drawing.Page" ) || ( mType == "presentation.Page" ) ) + { + if ( ( ePageType == NOTICE ) && mbPresObj ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Notes, EPP_PLACEHOLDER_MASTERNOTESSLIDEIMAGE ) ) + continue; + else + nPlaceHolderAtom = EPP_PLACEHOLDER_NOTESSLIDEIMAGE; + } + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + else if ( mType == "drawing.Frame" ) + { + continue; + } + else if ( ( mType == "drawing.OLE2" ) || ( mType == "presentation.OLE2" ) + || ( mType == "presentation.Chart" ) || ( mType == "presentation.Calc" ) + || ( mType == "presentation.OrgChart" ) ) + { + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + if ( mbEmptyPresObj && ( ePageType == NORMAL ) ) + { + nPlaceHolderAtom = rLayout.nUsedObjectPlaceHolder; + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveMaster, + aSolverContainer ); + mnTxId += 0x60; + aPropOpt.AddOpt( ESCHER_Prop_lTxid, mnTxId ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, mnShapeMasterBody ); + } + else + { + mpExEmbed->WriteUInt32( 0xf | ( EPP_ExEmbed << 16 ) ) + .WriteUInt32( 0 ); // Size of this container + + sal_uInt32 nSize, nOldPos = mpExEmbed->Tell(); + + mpExEmbed->WriteUInt32( EPP_ExEmbedAtom << 16 ) + .WriteUInt32( 8 ) + .WriteUInt32( 0 ) // follow colorscheme : 0->do not follow + // 1->follow colorscheme + // 2->follow text and background scheme + .WriteUChar( 1 ) // (bool)set if embedded server can not be locked + .WriteUChar( 0 ) // (bool)do not need to send dimension + .WriteUChar( 0 ) // (bool)is object a world table + .WriteUChar( 0 ); // pad byte + + std::unique_ptr pE( new PPTExOleObjEntry( NORMAL_OLE_OBJECT, mpExEmbed->Tell() ) ); + pE->xShape = mXShape; + maExOleObj.push_back( std::move(pE) ); + + mnExEmbed++; + + sal_Int64 nAspect = css::embed::Aspects::MSOLE_CONTENT; + try + { + // try to get the aspect when available + css::uno::Reference< css::beans::XPropertySet > xShapeProps( mXShape, css::uno::UNO_QUERY_THROW ); + xShapeProps->getPropertyValue("Aspect") >>= nAspect; + } + catch( css::uno::Exception& ) + {} + + mpExEmbed->WriteUInt32( 1 | ( EPP_ExOleObjAtom << 16 ) ) + .WriteUInt32( 24 ) + .WriteUInt32( nAspect ) // Aspect + .WriteUInt32( 0 ) + .WriteUInt32( mnExEmbed ) // index to the persist table + .WriteUInt32( 0 ) // subtype + .WriteUInt32( 0 ) + .WriteUInt32( 0x0012b600 ); + + nSize = mpExEmbed->Tell() - nOldPos; + mpExEmbed->Seek( nOldPos - 4 ); + mpExEmbed->WriteUInt32( nSize ); + mpExEmbed->Seek( STREAM_SEEK_TO_END ); + nOlePictureId = mnExEmbed; + + ShapeFlag nSpFlags = ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty; + if ( nOlePictureId ) + nSpFlags |= ShapeFlag::OLEShape; + ImplCreateShape( ESCHER_ShpInst_PictureFrame, nSpFlags, aSolverContainer ); + if ( aPropOpt.CreateOLEGraphicProperties( mXShape ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + if ( nOlePictureId ) + aPropOpt.AddOpt( ESCHER_Prop_pictureId, nOlePictureId ); + } + } + else if ( mType == "presentation.Header" ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Other, EPP_PLACEHOLDER_MASTERHEADER ) ) + continue; + else + { + mbPresObj = false; + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( mType == "presentation.Footer" ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Other, EPP_PLACEHOLDER_MASTERFOOTER ) ) + continue; + else + { + mbPresObj = false; + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( mType == "presentation.DateTime" ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Other, EPP_PLACEHOLDER_MASTERDATE ) ) + continue; + else + { + mbPresObj = false; + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( mType == "presentation.SlideNumber" ) + { + if ( ImplCreatePresentationPlaceholder( bMasterPage, EPP_TEXTTYPE_Other, EPP_PLACEHOLDER_MASTERSLIDENUMBER ) ) + continue; + else + { + mbPresObj = false; + mType = "drawing.Text"; + ImplCreateTextShape( aPropOpt, aSolverContainer, true ); + } + } + else if ( (mType.getLength() > 9) && (mType[8] == '3') && (mType[9] == 'D') ) // drawing.3D + { + // SceneObject, CubeObject, SphereObject, LatheObject, ExtrudeObject, PolygonObject + if ( !ImplGetPropertyValue( "Bitmap" ) ) + continue; + + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "Bitmap", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + else if ( mType == "drawing.Media" ) + { + mnAngle = 0; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + if ( aPropOpt.CreateMediaGraphicProperties( mXShape ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, mXPropSet, "MediaURL", true ) ) + { + OUString aMediaURL; + if ( (aAny >>= aMediaURL ) && !aMediaURL.isEmpty() ) + { + // SJ: creating the Media RefObj + sal_uInt32 nRefId = ++mnExEmbed; + + mpExEmbed->WriteUInt16( 0xf ) + .WriteUInt16( EPP_ExMCIMovie ) // PPT_PST_ExAviMovie + .WriteUInt32( 0 ); + sal_uInt32 nSize, nStart = mpExEmbed->Tell(); + mpExEmbed->WriteUInt16( 0 ) + .WriteUInt16( EPP_ExObjRefAtom ) + .WriteUInt32( 4 ) + .WriteUInt32( nRefId ); + mpExEmbed->WriteUInt16( 0xf ) + .WriteUInt16( EPP_ExVideo ) + .WriteUInt32( 0 ); + + mpExEmbed->WriteUInt16( 0 ) + .WriteUInt16( EPP_ExMediaAtom ) + .WriteUInt32( 8 ) + .WriteUInt32( nRefId ) + .WriteUInt16( 0 ) + .WriteUInt16( 0x435 ); + + sal_uInt16 i, nStringLen = static_cast(aMediaURL.getLength()); + mpExEmbed->WriteUInt32( EPP_CString << 16 ).WriteUInt32( nStringLen * 2 ); + for ( i = 0; i < nStringLen; i++ ) + { + sal_Unicode nChar = aMediaURL[ i ]; + mpExEmbed->WriteUInt16( nChar ); + } + nSize = mpExEmbed->Tell() - nStart; + mpExEmbed->SeekRel( - ( static_cast(nSize) + 4 ) ); + mpExEmbed->WriteUInt32( nSize ); // size of PPT_PST_ExMCIMovie + mpExEmbed->SeekRel( 0x10 ); + nSize -= 20; + mpExEmbed->WriteUInt32( nSize ); // PPT_PST_ExMediaAtom + mpExEmbed->SeekRel( nSize ); + + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + pClientData->WriteUInt16( 0 ) + .WriteUInt16( EPP_ExObjRefAtom ) + .WriteUInt32( 4 ) + .WriteUInt32( nRefId ); + // write EPP_InteractiveInfo container for no_action + pClientData->WriteUInt32( ( EPP_InteractiveInfo << 16 ) | 0xf ).WriteUInt32( 24 ); + pClientData->WriteUInt16( 0 ) + .WriteUInt16( EPP_InteractiveInfoAtom ) + .WriteUInt32( 16 ) + .WriteUInt32( 0 ) + .WriteUInt32( 0 ) + .WriteUChar( 6 ) + .WriteUChar( 0 ) + .WriteUChar( 0 ) + .WriteUChar( 0 ) + .WriteUInt32( 0 ); + } + } + } + else if ( (mType == "drawing.Table") || (mType == "presentation.Table") ) + { + if ( eCa != css::presentation::ClickAction_NONE ) + { + SvMemoryStream aTmp(0x200, 0x200); + ImplWriteClickAction( aTmp, eCa, bMediaClickAction ); + } + ImplCreateTable( mXShape, aSolverContainer, aPropOpt ); + continue; + } + else if ( mType == "drawing.dontknow" ) + { + mnAngle = 0; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + ImplCreateShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty, + aSolverContainer ); + if ( aPropOpt.CreateGraphicProperties( mXPropSet, "MetaFile", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + else + { + continue; + } + + bool bClientData = ( bEffect || ( eCa != css::presentation::ClickAction_NONE ) || + nPlaceHolderAtom || nOlePictureId ); + if ( bClientData ) + { + if ( nPlaceHolderAtom ) + { + sal_Int32 nPlacementID = -1; + if ( ( mnTextStyle == EPP_TEXTSTYLE_TITLE ) || ( mnTextStyle == EPP_TEXTSTYLE_BODY ) ) + nPlacementID = nIndices++; + else + { + switch ( nPlaceHolderAtom ) + { + default : + { + if ( nPlaceHolderAtom < 19 ) + break; + [[fallthrough]]; + } + case EPP_PLACEHOLDER_NOTESBODY : + case EPP_PLACEHOLDER_MASTERDATE : + case EPP_PLACEHOLDER_NOTESSLIDEIMAGE : + case EPP_PLACEHOLDER_MASTERNOTESBODYIMAGE : + nPlacementID = nIndices++; + } + } + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + + pClientData->WriteUInt32( EPP_OEPlaceholderAtom << 16 ).WriteUInt32( 8 ) + .WriteInt32( nPlacementID ) // PlacementID + .WriteUChar( nPlaceHolderAtom ) // PlaceHolderID + .WriteUChar( 0 ) // Size of PlaceHolder ( 0 = FULL, 1 = HALF, 2 = QUARTER ) + .WriteUInt16( 0 ); // padword + } + if ( nOlePictureId ) + { + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + + pClientData->WriteUInt32( EPP_ExObjRefAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( nOlePictureId ); + nOlePictureId = 0; + } + if ( bEffect && !pClientData ) + { + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + } + + if ( eCa != css::presentation::ClickAction_NONE ) + { + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + ImplWriteClickAction( *pClientData, eCa, bMediaClickAction ); + } + } + if ( ( mnTextStyle == EPP_TEXTSTYLE_TITLE ) || ( mnTextStyle == EPP_TEXTSTYLE_BODY ) ) + { + if ( !pClientTextBox ) + pClientTextBox.reset(new SvMemoryStream( 0x200, 0x200 )); + + if ( !mbEmptyPresObj ) + { + if ( ( ePageType == NORMAL ) && !bMasterPage ) + { + sal_uInt32 nTextType = EPP_TEXTTYPE_Body; + if ( mnTextStyle == EPP_TEXTSTYLE_BODY ) + { + if ( bSecOutl ) + nTextType = EPP_TEXTTYPE_HalfBody; + else if ( mType == "presentation.Subtitle" ) + nTextType = EPP_TEXTTYPE_CenterBody; + bSecOutl = true; + } + else + nTextType = EPP_TEXTTYPE_Title; + + TextRuleEntry aTextRule; + SvMemoryStream aExtBu( 0x200, 0x200 ); + ImplGetText(); + ImplWriteTextStyleAtom( *pClientTextBox, nTextType, nPObjects, &aTextRule, aExtBu, nullptr ); + ImplWriteExtParaHeader( aExtBu, nPObjects++, nTextType, nPageNumber + 0x100 ); + SvMemoryStream* pOut = aTextRule.pOut.get(); + if ( pOut ) + { + pClientTextBox->WriteBytes(pOut->GetData(), pOut->Tell()); + aTextRule.pOut.reset(); + } + if ( aExtBu.Tell() ) + { + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + ImplProgTagContainer( pClientData.get(), &aExtBu ); + } + } + } + } + else + { + if ( !aPropOpt.IsFontWork() ) + { + if ( mnTextSize || ( nPlaceHolderAtom == EPP_PLACEHOLDER_MASTERDATE ) || ( nPlaceHolderAtom == EPP_PLACEHOLDER_NOTESBODY ) ) + { + int nInstance2; + if ( ( nPlaceHolderAtom == EPP_PLACEHOLDER_MASTERDATE ) || ( nPlaceHolderAtom == EPP_PLACEHOLDER_NOTESBODY ) ) + nInstance2 = 2; + else + nInstance2 = EPP_TEXTTYPE_Other; // Text in a Shape + + if ( !pClientTextBox ) + pClientTextBox.reset(new SvMemoryStream( 0x200, 0x200 )); + + SvMemoryStream aExtBu( 0x200, 0x200 ); + ImplWriteTextStyleAtom( *pClientTextBox, nInstance2, 0, nullptr, aExtBu, &aPropOpt ); + if ( aExtBu.Tell() ) + { + if ( !pClientData ) + pClientData.reset(new SvMemoryStream( 0x200, 0x200 )); + ImplProgTagContainer( pClientData.get(), &aExtBu ); + } + } + else if ( nPlaceHolderAtom >= 19 ) + { + if ( !pClientTextBox ) + pClientTextBox.reset(new SvMemoryStream( 12 )); + + pClientTextBox->WriteUInt32( EPP_TextHeaderAtom << 16 ).WriteUInt32( 4 ) + .WriteUInt32( 7 ); + } + } + } + + aPropOpt.CreateShadowProperties( mXPropSet ); + maRect.Justify(); + if ( mnAngle ) + ImplFlipBoundingBox( aPropOpt ); + aPropOpt.CreateShapeProperties( mXShape ); + aPropOpt.Commit( *mpStrm ); + if ( GetCurrentGroupLevel() > 0 ) + mpPptEscherEx->AddChildAnchor( maRect ); + else + mpPptEscherEx->AddClientAnchor( maRect ); + + if ( pClientData ) + { + mpStrm->WriteUInt32( ( ESCHER_ClientData << 16 ) | 0xf ) + .WriteUInt32( pClientData->Tell() ); + + mpStrm->WriteBytes(pClientData->GetData(), pClientData->Tell()); + pClientData.reset(); + } + if ( pClientTextBox ) + { + mpStrm->WriteUInt32( ( ESCHER_ClientTextbox << 16 ) | 0xf ) + .WriteUInt32( pClientTextBox->Tell() ); + + mpStrm->WriteBytes(pClientTextBox->GetData(), pClientTextBox->Tell()); + pClientTextBox.reset(); + } + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + } + nPrevTextStyle = mnTextStyle; + + if ( bAdditionalText ) + { + bAdditionalText = false; + + css::uno::Any aAny; + EscherPropertyContainer aPropOpt; + mnAngle = ( PropValue::GetPropertyValue( aAny, + mXPropSet, "RotateAngle", true ) ) + ? *o3tl::doAccess(aAny) + : 0; + + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 ); + if ( mType == "drawing.Line" ) + { + double fDist = hypot( maRect.GetWidth(), maRect.GetHeight() ); + maRect = ::tools::Rectangle( Point( aTextRefPoint.X, aTextRefPoint.Y ), + Point( static_cast( aTextRefPoint.X + fDist ), aTextRefPoint.Y - 1 ) ); + ImplCreateTextShape( aPropOpt, aSolverContainer, false ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x60006 ); // Size Shape To Fit Text + if ( mnAngle < 0 ) + mnAngle = ( 36000 + mnAngle ) % 36000; + if ( mnAngle ) + ImplFlipBoundingBox( aPropOpt ); + } + else + { + ImplCreateTextShape( aPropOpt, aSolverContainer, false ); + if ( mnAngle < 0 ) + mnAngle = ( 36000 + mnAngle ) % 36000; + else + mnAngle = ( 36000 - ( mnAngle % 36000 ) ); + + mnAngle *= 655; + mnAngle += 0x8000; + mnAngle &=~0xffff; // round nAngle to full grad + aPropOpt.AddOpt( ESCHER_Prop_Rotation, mnAngle ); + + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->SetGroupSnapRect( nGroupLevel, maRect ); + // mpPptEscherEx->SetGroupLogicRect( nGroupLevel, maRect ); + } + if ( !pClientTextBox ) + pClientTextBox.reset(new SvMemoryStream( 0x200, 0x200 )); + + SvMemoryStream aExtBu( 0x200, 0x200 ); + ImplWriteTextStyleAtom( *pClientTextBox, EPP_TEXTTYPE_Other, 0, nullptr, aExtBu, &aPropOpt ); + + aPropOpt.CreateShapeProperties( mXShape ); + aPropOpt.Commit( *mpStrm ); + if ( GetCurrentGroupLevel() > 0 ) + mpPptEscherEx->AddChildAnchor( maRect ); + else + mpPptEscherEx->AddClientAnchor( maRect ); + + mpStrm->WriteUInt32( ( ESCHER_ClientTextbox << 16 ) | 0xf ) + .WriteUInt32( pClientTextBox->Tell() ); + + mpStrm->WriteBytes(pClientTextBox->GetData(), pClientTextBox->Tell()); + pClientTextBox.reset(); + + mpPptEscherEx->CloseContainer(); // ESCHER_SpContainer + + // #i119551# PPT does not support groups of polygons and text (MS patch KB2289187) + // mpPptEscherEx->LeaveGroup(); + } + } + ClearGroupTable(); // storing groups if any are still open, which should not be the case + nGroups = GetGroupsClosed(); + for ( sal_uInt32 i = 0; i < nGroups; i++, mpPptEscherEx->LeaveGroup() ) ; + mnPagesWritten++; +} + +struct CellBorder +{ + sal_Int32 mnPos; // specifies the distance to the top/left position of the table + table::BorderLine maCellBorder; + + CellBorder() : mnPos ( 0 ) {}; +}; + +bool PPTWriter::ImplCreateCellBorder( const CellBorder* pCellBorder, sal_Int32 nX1, sal_Int32 nY1, sal_Int32 nX2, sal_Int32 nY2) +{ + sal_Int32 nLineWidth = pCellBorder->maCellBorder.OuterLineWidth + pCellBorder->maCellBorder.InnerLineWidth; + if ( nLineWidth ) + { + nLineWidth *= 2; + mnAngle = 0; + mpPptEscherEx->OpenContainer( ESCHER_SpContainer ); + EscherPropertyContainer aPropOptSp; + + sal_uInt32 nId = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Line, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::Child, + nId ); + aPropOptSp.AddOpt( ESCHER_Prop_shapePath, ESCHER_ShapeComplex ); + aPropOptSp.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0xa0008 ); + aPropOptSp.AddOpt( ESCHER_Prop_fshadowObscured, 0x20000 ); + + sal_uInt32 nBorderColor = pCellBorder->maCellBorder.Color & 0xff00; // green + nBorderColor |= static_cast< sal_uInt8 >( pCellBorder->maCellBorder.Color ) << 16; // red + nBorderColor |= static_cast< sal_uInt8 >( pCellBorder->maCellBorder.Color >> 16 ); // blue + aPropOptSp.AddOpt( ESCHER_Prop_lineColor, nBorderColor ); + + aPropOptSp.AddOpt( ESCHER_Prop_lineWidth, nLineWidth * 360 ); + aPropOptSp.AddOpt( ESCHER_Prop_fc3DLightFace, 0x80000 ); + aPropOptSp.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 16, ESCHER_ChildAnchor ); + mpStrm ->WriteInt32( nX1 ) + .WriteInt32( nY1 ) + .WriteInt32( nX2 ) + .WriteInt32( nY2 ); + mpPptEscherEx->CloseContainer(); + return true; + } + return false; +} + +//get merged cell's width +static sal_Int32 GetCellRight( sal_Int32 nColumn, + ::tools::Rectangle const & rect, + std::vector< std::pair< sal_Int32, sal_Int32 > >& aColumns, + uno::Reference< table::XMergeableCell > const & xCell ) +{ + sal_Int32 nRight = aColumns[ nColumn ].first + aColumns[ nColumn ].second; + for ( sal_Int32 nColumnSpan = 1; nColumnSpan < xCell->getColumnSpan(); nColumnSpan++ ) + { + sal_uInt32 nC = nColumnSpan + nColumn; + if ( nC < aColumns.size() ) + nRight += aColumns[ nC ].second; + else + nRight = rect.Right(); + } + return nRight; +} +//get merged cell's height +static sal_Int32 GetCellBottom( sal_Int32 nRow, + ::tools::Rectangle const & rect, + std::vector< std::pair< sal_Int32, sal_Int32 > >& aRows, + uno::Reference< table::XMergeableCell > const & xCell ) +{ + sal_Int32 nBottom = aRows[nRow].first + aRows[nRow].second; + for ( sal_Int32 nRowSpan = 1; nRowSpan < xCell->getRowSpan(); nRowSpan++ ) + { + sal_uInt32 nR = nRowSpan + nRow; + if ( nR < aRows.size() ) + nBottom += aRows[ nR ].second; + else + nBottom = rect.Bottom(); + } + return nBottom; +} + +void PPTWriter::WriteCString( SvStream& rSt, const OUString& rString, sal_uInt32 nInstance ) +{ + sal_Int32 nLen = rString.getLength(); + if ( nLen ) + { + rSt.WriteUInt32( ( nInstance << 4 ) | ( EPP_CString << 16 ) ) + .WriteUInt32( nLen << 1 ); + for ( sal_Int32 i = 0; i < nLen; i++ ) + rSt.WriteUInt16( rString[i] ); + } +} + +namespace { + +class ContainerGuard +{ +private: + PptEscherEx* m_pPptEscherEx; +public: + ContainerGuard(PptEscherEx* pPptEscherEx, sal_uInt16 nRecord) + : m_pPptEscherEx(pPptEscherEx) + { + m_pPptEscherEx->OpenContainer(nRecord); + } + ~ContainerGuard() + { + m_pPptEscherEx->CloseContainer(); + } +}; + +} + +void PPTWriter::ImplCreateTable( uno::Reference< drawing::XShape > const & rXShape, EscherSolverContainer& aSolverContainer, + EscherPropertyContainer& aPropOpt ) +{ + try + { + uno::Reference< table::XTable > xTable; + if ( mXPropSet->getPropertyValue( "Model" ) >>= xTable ) + { + uno::Reference< table::XColumnRowRange > xColumnRowRange( xTable, uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xColumns( xColumnRowRange->getColumns(), uno::UNO_QUERY_THROW ); + uno::Reference< container::XIndexAccess > xRows( xColumnRowRange->getRows(), uno::UNO_QUERY_THROW ); + sal_uInt16 nRowCount = static_cast< sal_uInt16 >( xRows->getCount() ); + sal_uInt16 nColumnCount = static_cast< sal_uInt16 >( xColumns->getCount() ); + + std::vector< std::pair< sal_Int32, sal_Int32 > > aColumns; + std::vector< std::pair< sal_Int32, sal_Int32 > > aRows; + + awt::Point aPosition( MapPoint( rXShape->getPosition() ) ); + sal_Int32 nPosition = aPosition.X; + for ( sal_Int32 x = 0; x < nColumnCount; x++ ) + { + uno::Reference< beans::XPropertySet > xPropSet( xColumns->getByIndex( x ), uno::UNO_QUERY_THROW ); + awt::Size aS( 0, 0 ); + xPropSet->getPropertyValue( "Width" ) >>= aS.Width; + awt::Size aM( MapSize( aS ) ); + aColumns.emplace_back( nPosition, aM.Width ); + nPosition += aM.Width; + if ( x == nColumnCount - 1 && nPosition != maRect.Right() ) + maRect.SetRight( nPosition ); + } + + nPosition = aPosition.Y; + for ( sal_Int32 y = 0; y < nRowCount; y++ ) + { + uno::Reference< beans::XPropertySet > xPropSet( xRows->getByIndex( y ), uno::UNO_QUERY_THROW ); + awt::Size aS( 0, 0 ); + xPropSet->getPropertyValue( "Height" ) >>= aS.Height; + awt::Size aM( MapSize( aS ) ); + aRows.emplace_back( nPosition, aM.Height ); + nPosition += aM.Height; + if ( y == nRowCount - 1 && nPosition != maRect.Bottom()) + maRect.SetBottom( nPosition ); + } + std::optional xSpgrContainer(std::in_place, mpPptEscherEx.get(), ESCHER_SpgrContainer); + std::optional xSpContainer(std::in_place, mpPptEscherEx.get(), ESCHER_SpContainer); + mpPptEscherEx->AddAtom( 16, ESCHER_Spgr, 1 ); + mpStrm ->WriteInt32( maRect.Left() ) // Bounding box for the grouped shapes to which they are attached + .WriteInt32( maRect.Top() ) + .WriteInt32( maRect.Right() ) + .WriteInt32( maRect.Bottom() ); + + sal_uInt32 nShapeId = mpPptEscherEx->GenerateShapeId(); + mpPptEscherEx->AddShape( ESCHER_ShpInst_Min, ShapeFlag::HaveAnchor | ShapeFlag::Group, nShapeId ); + // TODO: check flags, comment does not match code // Flags: Group | Patriarch + aSolverContainer.AddShape( rXShape, nShapeId ); + EscherPropertyContainer aPropOpt2; + + SvMemoryStream aMemStrm; + aMemStrm.WriteUInt16( nRowCount ) + .WriteUInt16( nRowCount ) + .WriteUInt16( 4 ); + + for( const auto& rRow : aRows ) + aMemStrm.WriteInt32( rRow.second ); + + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x1000100 ); + aPropOpt2.AddOpt( ESCHER_Prop_tableProperties, 1 ); + aPropOpt2.AddOpt(ESCHER_Prop_tableRowProperties, true, 0, aMemStrm); + aPropOpt.CreateShapeProperties( rXShape ); + aPropOpt.Commit( *mpStrm ); + aPropOpt2.Commit( *mpStrm, 3, ESCHER_UDefProp ); + if ( GetCurrentGroupLevel() > 0 ) + mpPptEscherEx->AddChildAnchor( maRect ); + else + mpPptEscherEx->AddClientAnchor( maRect ); + xSpContainer.reset(); //ESCHER_SpContainer + + uno::Reference< table::XCellRange > xCellRange( xTable, uno::UNO_QUERY_THROW ); + for( sal_Int32 nRow = 0; nRow < xRows->getCount(); nRow++ ) + { + for( sal_Int32 nColumn = 0; nColumn < xColumns->getCount(); nColumn++ ) + { + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nColumn, nRow ), uno::UNO_QUERY_THROW ); + if ( !xCell->isMerged() ) + { + sal_Int32 nLeft = aColumns[ nColumn ].first; + sal_Int32 nTop = aRows[ nRow ].first; + sal_Int32 nRight = GetCellRight( nColumn, maRect,aColumns,xCell ); + sal_Int32 nBottom = GetCellBottom( nRow, maRect,aRows,xCell ); + + mbFontIndependentLineSpacing = false; + mXPropSet.set( xCell, uno::UNO_QUERY_THROW ); + mXText.set( xCell, uno::UNO_QUERY_THROW ); + mnTextSize = mXText->getString().getLength(); + + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "FontIndependentLineSpacing", true ) ) + aAny >>= mbFontIndependentLineSpacing; + + EscherPropertyContainer aPropOptSp; + std::optional xCellContainer(std::in_place, mpPptEscherEx.get(), ESCHER_SpContainer); + ImplCreateShape( ESCHER_ShpInst_Rectangle, + ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::Child, + aSolverContainer ); + aPropOptSp.CreateFillProperties( mXPropSet, true ); + aPropOptSp.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); + mnTxId += 0x60; + aPropOptSp.CreateTextProperties( mXPropSet, mnTxId ); + aPropOptSp.AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapSquare ); + + SvMemoryStream aClientTextBox( 0x200, 0x200 ); + SvMemoryStream aExtBu( 0x200, 0x200 ); + + ImplWriteTextStyleAtom( aClientTextBox, EPP_TEXTTYPE_Other, 0, nullptr, aExtBu, &aPropOptSp ); + + // need write client data for extend bullet + if ( aExtBu.Tell() ) + { + SvMemoryStream aClientData( 0x200, 0x200 ); + ImplProgTagContainer( &aClientData, &aExtBu ); + mpStrm->WriteUInt32( ( ESCHER_ClientData << 16 ) | 0xf ) + .WriteUInt32( aClientData.Tell() ); + + mpStrm->WriteBytes(aClientData.GetData(), aClientData.Tell()); + } + + aPropOptSp.Commit( *mpStrm ); + mpPptEscherEx->AddAtom( 16, ESCHER_ChildAnchor ); + mpStrm ->WriteInt32( nLeft ) + .WriteInt32( nTop ) + .WriteInt32( nRight ) + .WriteInt32( nBottom ); + + mpStrm->WriteUInt32( ( ESCHER_ClientTextbox << 16 ) | 0xf ) + .WriteUInt32( aClientTextBox.Tell() ); + + mpStrm->WriteBytes(aClientTextBox.GetData(), aClientTextBox.Tell()); + xCellContainer.reset(); + } + } + } + + // creating horz lines + for( sal_Int32 nLine = 0; nLine < ( xRows->getCount() + 1 ); nLine++ ) + { + for( sal_Int32 nColumn = 0; nColumn < xColumns->getCount(); nColumn++ ) + { + CellBorder aCellBorder; + aCellBorder.mnPos = aColumns[ nColumn ].first; + bool bTop = false; + //write nLine*nColumn cell's top border + if ( nLine < xRows->getCount() ) + { // top border + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nColumn, nLine ), uno::UNO_QUERY_THROW ); + if ( !xCell->isMerged() ) + { + uno::Reference< beans::XPropertySet > xPropSet2( xCell, uno::UNO_QUERY_THROW ); + table::BorderLine aBorderLine; + if ( xPropSet2->getPropertyValue( "TopBorder" ) >>= aBorderLine ) + aCellBorder.maCellBorder = aBorderLine; + sal_Int32 nRight = GetCellRight( nColumn, maRect,aColumns,xCell ); + bTop = ImplCreateCellBorder( &aCellBorder, aCellBorder.mnPos, + aRows[ nLine ].first, nRight, aRows[ nLine ].first ); + } + } + + //if nLine*nColumn cell's top border is empty, check (nLine-1)*nColumn cell's bottom border + //and write the last row's bottom border + if (( nLine && !bTop ) || (nLine == xRows->getCount())) + { // bottom border + sal_Int32 nRow = nLine; + + while( nRow ) + { //find last no merged cell + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nColumn, nRow - 1 ), uno::UNO_QUERY_THROW ); + if ( !xCell->isMerged() ) + { + sal_Int32 nRight = GetCellRight( nColumn, maRect,aColumns,xCell ); + sal_Int32 nBottom = GetCellBottom( nRow - 1, maRect,aRows,xCell ); + if ( nBottom == ( aRows[ nLine-1 ].first + aRows[ nLine-1 ].second ) ) + { + uno::Reference< table::XMergeableCell > xCellOwn( xCellRange->getCellByPosition( nColumn, nRow - 1 ), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xPropSet2( xCellOwn, uno::UNO_QUERY_THROW ); + table::BorderLine aBorderLine; + if ( xPropSet2->getPropertyValue( "BottomBorder" ) >>= aBorderLine ) + aCellBorder.maCellBorder = aBorderLine; + ImplCreateCellBorder( &aCellBorder, aCellBorder.mnPos, + nBottom, nRight, nBottom); + } + nRow=0; + } + else + nRow--; + } + } + } + } + + // creating vertical lines + for( sal_Int32 nLine = 0; nLine < ( xColumns->getCount() + 1 ); nLine++ ) + { + for( sal_Int32 nRow = 0; nRow < xRows->getCount(); nRow++ ) + { + + CellBorder aCellBorder; + aCellBorder.mnPos = aRows[ nRow].first; + bool bLeft = false; + if ( nLine < xColumns->getCount() ) + { // left border + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nLine, nRow ), uno::UNO_QUERY_THROW ); + if (!xCell->isMerged() ) + { + uno::Reference< beans::XPropertySet > xCellSet( xCell, uno::UNO_QUERY_THROW ); + table::BorderLine aBorderLine; + if ( xCellSet->getPropertyValue( "LeftBorder" ) >>= aBorderLine ) + aCellBorder.maCellBorder = aBorderLine; + sal_Int32 nBottom = GetCellBottom( nRow, maRect, aRows,xCell ); + bLeft = ImplCreateCellBorder( &aCellBorder, aColumns[nLine].first, aCellBorder.mnPos, + aColumns[nLine].first, nBottom ); + } + } + if ( ( nLine && !bLeft )||(nLine == xColumns->getCount())) + { // right border + sal_Int32 nColumn = nLine; + while ( nColumn ) + { + uno::Reference< table::XMergeableCell > xCell( xCellRange->getCellByPosition( nColumn - 1, nRow ), uno::UNO_QUERY_THROW ); + if (!xCell->isMerged() ) + { + sal_Int32 nRight = GetCellRight( nColumn-1, maRect, aColumns,xCell ); + sal_Int32 nBottom = GetCellBottom( nRow, maRect, aRows, xCell ); + if ( nRight == (aColumns[nLine-1].first + aColumns[nLine-1].second) ) + { + uno::Reference< table::XMergeableCell > xCellOwn( xCellRange->getCellByPosition( nColumn - 1, nRow ), uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySet > xCellSet( xCellOwn, uno::UNO_QUERY_THROW ); + table::BorderLine aBorderLine; + if ( xCellSet->getPropertyValue( "RightBorder" ) >>= aBorderLine ) + aCellBorder.maCellBorder = aBorderLine; + ImplCreateCellBorder( &aCellBorder, nRight, aCellBorder.mnPos, + nRight, nBottom ); + } + nColumn = 0; + } + else + nColumn --; + } + } + } + } + + xSpgrContainer.reset(); //ESCHER_SpgrContainer + } + } + catch( uno::Exception& ) + { + } +} + +void TextObjBinary::Write( SvStream* pStrm ) +{ + sal_uInt32 nSize, nPos = pStrm->Tell(); + pStrm->WriteUInt32( EPP_TextCharsAtom << 16 ).WriteUInt32( 0 ); + for ( sal_uInt32 i = 0; i < ParagraphCount(); ++i ) + GetParagraph(i)->Write( pStrm ); + nSize = pStrm->Tell() - nPos; + pStrm->SeekRel( - ( static_cast(nSize) - 4 ) ); + pStrm->WriteUInt32( nSize - 8 ); + pStrm->SeekRel( nSize - 8 ); +} + +void TextObjBinary::WriteTextSpecInfo( SvStream* pStrm ) +{ + sal_uInt32 nCharactersLeft( Count() ); + if ( nCharactersLeft < 1 ) + return; + + EscherExAtom aAnimationInfoAtom( *pStrm, EPP_TextSpecInfoAtom, 0, 0 ); + for ( sal_uInt32 i = 0; nCharactersLeft && i < ParagraphCount(); ++i ) + { + ParagraphObj* pPtr = GetParagraph(i); + for ( std::vector >::const_iterator it = pPtr->begin(); nCharactersLeft && it != pPtr->end(); ++it ) + { + const PortionObj& rPortion = **it; + sal_Int32 nPortionSize = rPortion.mnTextSize >= nCharactersLeft ? nCharactersLeft : rPortion.mnTextSize; + sal_Int32 const nFlags = 7; + nCharactersLeft -= nPortionSize; + pStrm ->WriteUInt32( nPortionSize ) + .WriteInt32( nFlags ) + .WriteInt16( 1 ) // spellinfo -> needs rechecking + .WriteInt16( static_cast(LanguageTag( rPortion.meCharLocale ).makeFallback().getLanguageType()) ) + .WriteInt16( 0 ); // alt language + } + } + if ( nCharactersLeft ) + pStrm->WriteUInt32( nCharactersLeft ).WriteInt32( 1 ).WriteInt16( 1 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/escherex.cxx b/sd/source/filter/eppt/escherex.cxx new file mode 100644 index 000000000..5032c2721 --- /dev/null +++ b/sd/source/filter/eppt/escherex.cxx @@ -0,0 +1,266 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "escherex.hxx" + +PptEscherEx::PptEscherEx( SvStream& rOutStrm, const OUString& rBaseURI ) : + EscherEx( std::make_shared( ), &rOutStrm ) +{ + mxGlobal->SetBaseURI( rBaseURI ); + mnCurrentDg = 0; +} + +sal_uInt32 PptEscherEx::DrawingGroupContainerSize() +{ + return ImplDggContainerSize() + 8; +} + +void PptEscherEx::WriteDrawingGroupContainer( SvStream& rSt ) +{ + sal_uInt32 nSize = DrawingGroupContainerSize(); + rSt.WriteUInt32( 0xf | ( 1035 << 16 ) ) // EPP_PPDrawingGroup + .WriteUInt32( nSize - 8 ); + + ImplWriteDggContainer( rSt ); +} + +sal_uInt32 PptEscherEx::ImplDggContainerSize() +{ + sal_uInt32 nSize; + + nSize = mxGlobal->GetDggAtomSize(); + nSize += mxGlobal->GetBlibStoreContainerSize(); + nSize += ImplOptAtomSize(); + nSize += ImplSplitMenuColorsAtomSize(); + + return nSize + 8; +} + +void PptEscherEx::ImplWriteDggContainer( SvStream& rSt ) +{ + sal_uInt32 nSize = ImplDggContainerSize(); + if ( nSize ) + { + rSt.WriteUInt32( 0xf | ( ESCHER_DggContainer << 16 ) ) + .WriteUInt32( nSize - 8 ); + + mxGlobal->SetDggContainer(); + mxGlobal->WriteDggAtom( rSt ); + mxGlobal->WriteBlibStoreContainer( rSt ); + ImplWriteOptAtom( rSt ); + ImplWriteSplitMenuColorsAtom( rSt ); + } +} + +#define ESCHER_OPT_COUNT 6 + +sal_uInt32 PptEscherEx::ImplOptAtomSize() +{ + sal_uInt32 nSize = 0; + if ( ESCHER_OPT_COUNT != 0 ) + nSize = ( ESCHER_OPT_COUNT * 6 ) + 8; + return nSize; +} + +void PptEscherEx::ImplWriteOptAtom( SvStream& rSt ) +{ + sal_uInt32 nSize = ImplOptAtomSize(); + if ( nSize ) + { + rSt.WriteUInt32( ( ESCHER_OPT << 16 ) | ( ESCHER_OPT_COUNT << 4 ) | 0x3 ) + .WriteUInt32( nSize - 8 ) + .WriteUInt16( ESCHER_Prop_fillColor ) .WriteUInt32( 0xffb800 ) + .WriteUInt16( ESCHER_Prop_fillBackColor ) .WriteUInt32( 0 ) + .WriteUInt16( ESCHER_Prop_fNoFillHitTest ) .WriteUInt32( 0x00100010 ) + .WriteUInt16( ESCHER_Prop_lineColor ) .WriteUInt32( 0x8000001 ) + .WriteUInt16( ESCHER_Prop_fNoLineDrawDash ) .WriteUInt32( 0x00080008 ) + .WriteUInt16( ESCHER_Prop_shadowColor ) .WriteUInt32( 0x8000002 ); + } +} + +#define ESCHER_SPLIT_MENU_COLORS_COUNT 4 + +sal_uInt32 PptEscherEx::ImplSplitMenuColorsAtomSize() +{ + sal_uInt32 nSize = 0; + if ( ESCHER_SPLIT_MENU_COLORS_COUNT != 0 ) + nSize = ( ESCHER_SPLIT_MENU_COLORS_COUNT << 2 ) + 8; + return nSize; +} + +void PptEscherEx::ImplWriteSplitMenuColorsAtom( SvStream& rSt ) +{ + sal_uInt32 nSize = ImplSplitMenuColorsAtomSize(); + if ( nSize ) + { + rSt.WriteUInt32( ( ESCHER_SplitMenuColors << 16 ) | ( ESCHER_SPLIT_MENU_COLORS_COUNT << 4 ) ) + .WriteUInt32( nSize - 8 ) + .WriteUInt32( 0x08000004 ) + .WriteUInt32( 0x08000001 ) + .WriteUInt32( 0x08000002 ) + .WriteUInt32( 0x100000f7 ); + } + +} + +PptEscherEx::~PptEscherEx() +{ +} + +void PptEscherEx::OpenContainer( sal_uInt16 n_EscherContainer, int nRecInstance ) +{ + mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | 0xf ).WriteUInt16( n_EscherContainer ).WriteUInt32( 0 ); + mOffsets.push_back( mpOutStrm->Tell() - 4 ); + mRecTypes.push_back( n_EscherContainer ); + + switch( n_EscherContainer ) + { + case ESCHER_DgContainer : + { + if ( !mbEscherDg ) + { + mbEscherDg = true; + mnCurrentDg = mxGlobal->GenerateDrawingId(); + AddAtom( 8, ESCHER_Dg, 0, mnCurrentDg ); + PtReplaceOrInsert( ESCHER_Persist_Dg | mnCurrentDg, mpOutStrm->Tell() ); + mpOutStrm->WriteUInt32( 0 ) // The number of shapes in this drawing + .WriteUInt32( 0 ); // The last MSOSPID given to an SP in this DG + } + } + break; + + case ESCHER_SpgrContainer : + { + if ( mbEscherDg ) + { + mbEscherSpgr = true; + } + } + break; + + default: + break; + } +} + +void PptEscherEx::CloseContainer() +{ + /* SJ: #Issue 26747# + not creating group objects with a depth higher than 16, because then + PPT is having a big performance problem when starting a slide show + */ + if ( ( mRecTypes.back() == ESCHER_SpgrContainer ) && ( mnGroupLevel >= 12 ) ) + return; + + sal_uInt32 nSize, nPos = mpOutStrm->Tell(); + nSize = ( nPos - mOffsets.back() ) - 4; + mpOutStrm->Seek( mOffsets.back() ); + mpOutStrm->WriteUInt32( nSize ); + + switch( mRecTypes.back() ) + { + case ESCHER_DgContainer : + { + if ( mbEscherDg ) + { + mbEscherDg = false; + if ( DoSeek( ESCHER_Persist_Dg | mnCurrentDg ) ) + mpOutStrm->WriteUInt32( mxGlobal->GetDrawingShapeCount( mnCurrentDg ) ).WriteUInt32( mxGlobal->GetLastShapeId( mnCurrentDg ) ); + } + } + break; + + case ESCHER_SpgrContainer : + { + if ( mbEscherSpgr ) + { + mbEscherSpgr = false; + } + } + break; + + default: + break; + } + mOffsets.pop_back(); + mRecTypes.pop_back(); + mpOutStrm->Seek( nPos ); +} + +sal_uInt32 PptEscherEx::EnterGroup( ::tools::Rectangle const * pBoundRect, SvMemoryStream* pClientData ) +{ + sal_uInt32 nShapeId = 0; + /* SJ: #Issue 26747# + not creating group objects with a depth higher than 16, because then + PPT is having a big performance problem when starting a slide show + */ + if ( mnGroupLevel < 12 ) + { + ::tools::Rectangle aRect; + if ( pBoundRect ) + aRect = *pBoundRect; + + OpenContainer( ESCHER_SpgrContainer ); + OpenContainer( ESCHER_SpContainer ); + AddAtom( 16, ESCHER_Spgr, 1 ); + PtReplaceOrInsert( ESCHER_Persist_Grouping_Snap | mnGroupLevel, mpOutStrm->Tell() ); + mpOutStrm ->WriteInt32( aRect.Left() ) // bounding box for the grouped shapes to which they are attached + .WriteInt32( aRect.Top() ) + .WriteInt32( aRect.Right() ) + .WriteInt32( aRect.Bottom() ); + + nShapeId = GenerateShapeId(); + if ( !mnGroupLevel ) + AddShape( ESCHER_ShpInst_Min, ShapeFlag::Group | ShapeFlag::Patriarch, nShapeId ); + else + { + AddShape( ESCHER_ShpInst_Min, ShapeFlag::HaveAnchor | ShapeFlag::Group, nShapeId ); + if ( mnGroupLevel == 1 ) + { + AddAtom( 8, ESCHER_ClientAnchor ); + PtReplaceOrInsert( ESCHER_Persist_Grouping_Logic | mnGroupLevel, mpOutStrm->Tell() ); + mpOutStrm->WriteInt16( aRect.Top() ).WriteInt16( aRect.Left() ).WriteInt16( aRect.Right() ).WriteInt16( aRect.Bottom() ); + } + else + { + AddAtom( 16, ESCHER_ChildAnchor ); + PtReplaceOrInsert( ESCHER_Persist_Grouping_Snap | mnGroupLevel, mpOutStrm->Tell() ); + mpOutStrm ->WriteInt32( aRect.Left() ) + .WriteInt32( aRect.Top() ) + .WriteInt32( aRect.Right() ) + .WriteInt32( aRect.Bottom() ); + } + } + if ( pClientData ) + { + sal_uInt32 nSize = pClientData->TellEnd(); + if ( nSize ) + { + mpOutStrm->WriteUInt32( ( ESCHER_ClientData << 16 ) | 0xf ) + .WriteUInt32( nSize ); + mpOutStrm->WriteBytes(pClientData->GetData(), nSize); + } + } + CloseContainer(); // ESCHER_SpContainer + } + mnGroupLevel++; + return nShapeId; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/escherex.hxx b/sd/source/filter/eppt/escherex.hxx new file mode 100644 index 000000000..8f64419f4 --- /dev/null +++ b/sd/source/filter/eppt/escherex.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once +#include + +/// Values for the sal_uLong in PPT_PST_TextHeaderAtom. +enum PPT_TextHeader +{ + PPTTH_TITLE, + PPTTH_BODY, + PPTTH_NOTES, + PPTTH_NOTUSED, + PPTTH_OTHER, ///< Text in a Shape + PPTTH_CENTERBODY, ///< Subtitle in Title-Slide + PPTTH_CENTERTITLE, ///< Title in Title-Slide + PPTTH_HALFBODY, ///< Body in two-column slide + PPTTH_QUARTERBODY ///< Body in four-body slide +}; + +class PptEscherEx : public EscherEx +{ + sal_uInt32 ImplDggContainerSize(); + void ImplWriteDggContainer( SvStream& rSt ); + + static sal_uInt32 ImplOptAtomSize(); + static void ImplWriteOptAtom( SvStream& rSt ); + + static sal_uInt32 ImplSplitMenuColorsAtomSize(); + static void ImplWriteSplitMenuColorsAtom( SvStream& rSt ); + + public: + + PptEscherEx( SvStream& rOut, const OUString& ); + virtual ~PptEscherEx() override; + + void OpenContainer( sal_uInt16 n_EscherContainer, int nRecInstance = 0 ) override; + void CloseContainer() override; + + sal_uInt32 EnterGroup( ::tools::Rectangle const * pBoundRect, SvMemoryStream* pClientData ); + + sal_uInt32 DrawingGroupContainerSize(); + void WriteDrawingGroupContainer( SvStream& rSt ); + + using EscherEx::EnterGroup; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/grouptable.hxx b/sd/source/filter/eppt/grouptable.hxx new file mode 100644 index 000000000..885f95741 --- /dev/null +++ b/sd/source/filter/eppt/grouptable.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +struct GroupEntry +{ + sal_uInt32 mnCurrentPos; + sal_uInt32 mnCount; + css::uno::Reference< css::container::XIndexAccess > mXIndexAccess; + + explicit GroupEntry( css::uno::Reference< css::container::XIndexAccess > const & rIndex ) + : mnCurrentPos(0), + mnCount(rIndex->getCount()), + mXIndexAccess(rIndex) + { + }; + + explicit GroupEntry( sal_uInt32 nCount ) + : mnCurrentPos(0), + mnCount(nCount) + { + }; +}; + +class GroupTable +{ + protected: + + sal_uInt32 mnIndex; + sal_uInt32 mnGroupsClosed; + std::vector mvGroupEntry; + + public: + + sal_uInt32 GetCurrentGroupIndex() const { return mnIndex; }; + sal_Int32 GetCurrentGroupLevel() const { return mvGroupEntry.size() - 1; }; + const css::uno::Reference< css::container::XIndexAccess > & + GetCurrentGroupAccess() const { return mvGroupEntry.back().mXIndexAccess; }; + sal_uInt32 GetGroupsClosed(); + void ResetGroupTable( sal_uInt32 nCount ); + void ClearGroupTable(); + bool EnterGroup( css::uno::Reference< css::container::XIndexAccess > const & rIndex ); + bool GetNextGroupEntry(); + GroupTable(); + ~GroupTable(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptexanimations.cxx b/sd/source/filter/eppt/pptexanimations.cxx new file mode 100644 index 000000000..99fe0b443 --- /dev/null +++ b/sd/source/filter/eppt/pptexanimations.cxx @@ -0,0 +1,2150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "pptexanimations.hxx" +#include "pptexsoundcollection.hxx" +#include "../ppt/pptanimations.hxx" +#include +#include +#include +#include +#include + +#include + +using ::com::sun::star::uno::Any; +using ::com::sun::star::util::XCloneable; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; + +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +namespace ppt +{ + +static void ImplTranslateAttribute( OUString& rString, const TranslateMode eTranslateMode ) +{ + if ( eTranslateMode == TRANSLATE_NONE ) + return; + + if ( ( eTranslateMode & TRANSLATE_VALUE ) || ( eTranslateMode & TRANSLATE_ATTRIBUTE ) ) + { + const oox::ppt::ImplAttributeNameConversion* p = oox::ppt::getAttributeConversionList(); + while( p->mpAPIName ) + { + if( rString.equalsAscii( p->mpAPIName ) ) + break; + p++; + } + if( p->mpMSName ) + { + if ( eTranslateMode & TRANSLATE_VALUE ) + { + rString = "#"; + rString += OUString::createFromAscii( p->mpMSName ); + } + else + rString = OUString::createFromAscii( p->mpMSName ); + } + } + else if ( eTranslateMode & TRANSLATE_MEASURE ) + { + const char* pDest[] = { "#ppt_x", "#ppt_y", "#ppt_w", "#ppt_h", nullptr }; + const char* pSource[] = { "x", "y", "width", "height", nullptr }; + sal_Int32 nIndex = 0; + + const char** ps = pSource; + const char** pd = pDest; + + while( *ps ) + { + const OUString aSearch( OUString::createFromAscii( *ps ) ); + while( (nIndex = rString.indexOf( aSearch, nIndex )) != -1 ) + { + sal_Int32 nLength = aSearch.getLength(); + if( nIndex && ( rString[nIndex-1] == '#' ) ) + { + nIndex--; + nLength++; + } + + const OUString aNew( OUString::createFromAscii( *pd ) ); + rString = rString.replaceAt( nIndex, nLength, aNew ); + nIndex += aNew.getLength(); + } + ps++; + pd++; + } + } +} + +sal_uInt32 AnimationExporter::TranslatePresetSubType( const sal_uInt32 nPresetClass, const sal_uInt32 nPresetId, std::u16string_view rPresetSubType ) +{ + sal_uInt32 nPresetSubType = 0; + bool bTranslated = false; + + if ( ( nPresetClass == sal_uInt32(EffectPresetClass::ENTRANCE) ) || ( nPresetClass == sal_uInt32(EffectPresetClass::EXIT) ) ) + { + if ( nPresetId != 21 ) + { + switch( nPresetId ) + { + case 5 : + { + if ( rPresetSubType == u"downward" ) + { + nPresetSubType = 5; + bTranslated = true; + } + else if ( rPresetSubType == u"across" ) + { + nPresetSubType = 10; + bTranslated = true; + } + } + break; + case 17 : + { + if ( rPresetSubType == u"across" ) + { + nPresetSubType = 10; + bTranslated = true; + } + } + break; + case 18 : + { + if ( rPresetSubType == u"right-to-top" ) + { + nPresetSubType = 3; + bTranslated = true; + } + else if ( rPresetSubType == u"right-to-bottom" ) + { + nPresetSubType = 6; + bTranslated = true; + } + else if ( rPresetSubType == u"left-to-top" ) + { + nPresetSubType = 9; + bTranslated = true; + } + else if ( rPresetSubType == u"left-to-bottom" ) + { + nPresetSubType = 12; + bTranslated = true; + } + } + break; + } + } + if ( !bTranslated ) + { + const oox::ppt::convert_subtype* p = oox::ppt::convert_subtype::getList(); + while( p->mpStrSubType ) + { + if ( o3tl::equalsAscii( rPresetSubType, p->mpStrSubType ) ) + { + nPresetSubType = p->mnID; + bTranslated = true; + break; + } + p++; + } + } + } + if ( !bTranslated ) + nPresetSubType = o3tl::toUInt32(rPresetSubType); + return nPresetSubType; +} + +const char* AnimationExporter::FindTransitionName( const sal_Int16 nType, const sal_Int16 nSubType, const bool bDirection ) +{ + const char* pRet = nullptr; + int nFit = 0; + + const oox::ppt::transition* p = oox::ppt::transition::getList(); + while( p->mpName ) + { + int nF = 0; + if ( nType == p->mnType ) + nF += 4; + if ( nSubType == p->mnSubType ) + nF += 2; + if ( bDirection == p->mbDirection ) + nF += 1; + if ( nF > nFit ) + { + pRet = p->mpName; + nFit = nF; + } + if ( nFit == 7 ) // maximum + break; + p++; + } + return pRet; +} + +SvStream& WriteAnimationNode(SvStream& rOut, AnimationNode const & rNode ) +{ + rOut.WriteInt32( rNode.mnU1 ); + rOut.WriteInt32( rNode.mnRestart ); + rOut.WriteInt32( rNode.mnGroupType ); + rOut.WriteInt32( rNode.mnFill ); + rOut.WriteInt32( rNode.mnU3 ); + rOut.WriteInt32( rNode.mnU4 ); + rOut.WriteInt32( rNode.mnDuration ); + rOut.WriteInt32( rNode.mnNodeType ); + + return rOut; +} + +AnimationExporter::AnimationExporter( const EscherSolverContainer& rSolverContainer, ppt::ExSoundCollection& rExSoundCollection ) : + mrSolverContainer ( rSolverContainer ), + mrExSoundCollection ( rExSoundCollection ), + mnCurrentGroup(0) +{ +} + +sal_Int16 AnimationExporter::GetFillMode( const Reference< XAnimationNode >& xNode, const sal_Int16 nFillDefault ) +{ + sal_Int16 nFill = xNode->getFill(); + //#i119699 The animation effect "Emphasis->FlashBulb" play incorrectly in Aoo saves a .ppt to another .ppt and plays the saved one. + //#i119740 The animation effect "Entrance->Flash Once" fails to play in Aoo while Aoo saves a .ppt to another .ppt and plays the saved one. + if ((xNode->getType() == AnimationNodeType::ANIMATE) + ||(xNode->getType() == AnimationNodeType::SET) + ||(xNode->getType() == AnimationNodeType::TRANSITIONFILTER)) + { + if ( nFill == AnimationFill::DEFAULT ) + return nFill; + } + + if ( nFill == AnimationFill::DEFAULT ) + { + nFill = nFillDefault; + } + if( nFill == AnimationFill::AUTO ) + { + nFill = AnimationFill::REMOVE; + bool bIsIndefiniteTiming = true; + Any aAny = xNode->getDuration(); + if( aAny.hasValue() ) + { + Timing eTiming; + if( aAny >>= eTiming ) + bIsIndefiniteTiming = eTiming == Timing_INDEFINITE; + } + if ( bIsIndefiniteTiming ) + { + aAny = xNode->getEnd(); + if( aAny.hasValue() ) + { + Timing eTiming; + if( aAny >>= eTiming ) + bIsIndefiniteTiming = eTiming == Timing_INDEFINITE; + } + if ( bIsIndefiniteTiming ) + { + if ( !xNode->getRepeatCount().hasValue() ) + { + aAny = xNode->getRepeatDuration(); + if( aAny.hasValue() ) + { + Timing eTiming; + if( aAny >>= eTiming ) + bIsIndefiniteTiming = eTiming == Timing_INDEFINITE; + } + if ( bIsIndefiniteTiming ) + nFill = AnimationFill::FREEZE; + } + } + } + } + return nFill; +} + +void AnimationExporter::doexport( const Reference< XDrawPage >& xPage, SvStream& rStrm ) +{ + Reference< XAnimationNodeSupplier > xNodeSupplier( xPage, UNO_QUERY ); + if( xNodeSupplier.is() ) + { + const Reference< XAnimationNode > xRootNode( xNodeSupplier->getAnimationNode() ); + if( xRootNode.is() ) + { + processAfterEffectNodes( xRootNode ); + exportNode( rStrm, xRootNode, DFF_msofbtAnimGroup, 1, 0, false, AnimationFill::AUTO ); + } + } +} + +void AnimationExporter::processAfterEffectNodes( const Reference< XAnimationNode >& xRootNode ) +{ + try + { + Reference< XEnumerationAccess > xEnumerationAccess( xRootNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xEnumerationAccess->createEnumeration(), css::uno::UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xNode( xEnumeration->nextElement(), UNO_QUERY_THROW ); + + Reference< XEnumerationAccess > xEnumerationAccess2( xNode, UNO_QUERY ); + if ( xEnumerationAccess2.is() ) + { + Reference< XEnumeration > xEnumeration2( xEnumerationAccess2->createEnumeration(), css::uno::UNO_SET_THROW ); + while( xEnumeration2->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration2->nextElement(), UNO_QUERY_THROW ); + + Reference< XEnumerationAccess > xEnumerationAccess3( xChildNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration3( xEnumerationAccess3->createEnumeration(), css::uno::UNO_SET_THROW ); + while( xEnumeration3->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode2( xEnumeration3->nextElement(), UNO_QUERY_THROW ); + + Reference< XEnumerationAccess > xEnumerationAccess4( xChildNode2, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration4( xEnumerationAccess4->createEnumeration(), css::uno::UNO_SET_THROW ); + while( xEnumeration4->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode3( xEnumeration4->nextElement(), UNO_QUERY_THROW ); + + switch( xChildNode3->getType() ) + { + // found an after effect + case AnimationNodeType::SET: + case AnimationNodeType::ANIMATECOLOR: + { + Reference< XAnimationNode > xMaster; + + const Sequence< NamedValue > aUserData( xChildNode3->getUserData() ); + const NamedValue* p = std::find_if(aUserData.begin(), aUserData.end(), + [](const NamedValue& rProp) { return rProp.Name == "master-element"; }); + + if (p != aUserData.end()) + p->Value >>= xMaster; + + AfterEffectNodePtr pAfterEffectNode = std::make_shared( xChildNode3, xMaster ); + maAfterEffectNodes.push_back( pAfterEffectNode ); + } + break; + } + } + } + } + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "(@CL)AnimationExporter::processAfterEffectNodes()" ); + } +} + +bool AnimationExporter::isAfterEffectNode( const Reference< XAnimationNode >& xNode ) const +{ + return std::any_of(maAfterEffectNodes.begin(), maAfterEffectNodes.end(), + [&xNode](const AfterEffectNodePtr& rxNode) { return rxNode->mxNode == xNode; }); +} + +bool AnimationExporter::hasAfterEffectNode( const Reference< XAnimationNode >& xNode, Reference< XAnimationNode >& xAfterEffectNode ) const +{ + auto aIter = std::find_if(maAfterEffectNodes.begin(), maAfterEffectNodes.end(), + [&xNode](const AfterEffectNodePtr& rxNode) { return rxNode->mxMaster == xNode; }); + if (aIter != maAfterEffectNodes.end()) + { + xAfterEffectNode = (*aIter)->mxNode; + return true; + } + + return false; +} + +// check if this group only contain empty groups. this may happen when +// after effect nodes are not exported at their original position +bool AnimationExporter::isEmptyNode( const Reference< XAnimationNode >& xNode ) const +{ + if( xNode.is() ) switch( xNode->getType() ) + { + case AnimationNodeType::PAR : + case AnimationNodeType::SEQ : + case AnimationNodeType::ITERATE : + { + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xChildNode.is() && !isEmptyNode( xChildNode ) ) + return false; + } + } + } + } + break; + + case AnimationNodeType::SET : + case AnimationNodeType::ANIMATECOLOR : + return isAfterEffectNode( xNode ); + default: + return false; + } + + return true; +} + +void AnimationExporter::exportNode( SvStream& rStrm, Reference< XAnimationNode > const & xNode_in, const sal_uInt16 nContainerRecType, + const sal_uInt16 nInstance, const sal_Int32 nGroupLevel, const bool bTakeBackInteractiveSequenceTiming, const sal_Int16 nFDef ) +{ + auto xNode = xNode_in; + + if( (nGroupLevel == 4) && isEmptyNode( xNode ) ) + return; + + if ( ( nContainerRecType == DFF_msofbtAnimGroup ) && ( nGroupLevel == 2 ) && isEmptyNode( xNode ) ) + return; + + if( nContainerRecType == DFF_msofbtAnimGroup ) + mnCurrentGroup++; + + bool bTakeBackInteractiveSequenceTimingForChild = false; + sal_Int16 nFillDefault = GetFillMode( xNode, nFDef ); + + Reference< XAnimationNode > xAudioNode; + static sal_uInt32 nAudioGroup; + + { + bool bSkipChildren = false; + EscherExContainer aContainer( rStrm, nContainerRecType, nInstance ); + switch( xNode->getType() ) + { + case AnimationNodeType::CUSTOM : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::PAR : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + sal_Int32 nFlags = nGroupLevel == 2 ? 0x10 : 0; + if ( bTakeBackInteractiveSequenceTiming ) + nFlags |= 0x40; + exportAnimEvent( rStrm, xNode, nFlags ); + exportAnimValue( rStrm, xNode, nGroupLevel == 4 ); + } + break; + + case AnimationNodeType::SEQ : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + sal_Int16 nNodeType = exportAnimPropertySet( rStrm, xNode ); + sal_Int32 nFlags = 12; + if ( ( nGroupLevel == 1 ) && ( nNodeType == css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE ) ) + { + nFlags |= 0x20; + bTakeBackInteractiveSequenceTimingForChild = true; + } + exportAnimAction( rStrm, xNode ); + exportAnimEvent( rStrm, xNode, nFlags ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::ITERATE : + { + { + EscherExAtom aAnimNodeExAtom( rStrm, DFF_msofbtAnimNode ); + AnimationNode aAnim; + aAnim.mnGroupType = mso_Anim_GroupType_PAR; + aAnim.mnNodeType = 1; + // attribute Restart + switch( xNode->getRestart() ) + { + default: + case AnimationRestart::DEFAULT : aAnim.mnRestart = 0; break; + case AnimationRestart::ALWAYS : aAnim.mnRestart = 1; break; + case AnimationRestart::WHEN_NOT_ACTIVE : aAnim.mnRestart = 2; break; + case AnimationRestart::NEVER : aAnim.mnRestart = 3; break; + } + // attribute Fill + switch( xNode->getFill() ) + { + default: + case AnimationFill::DEFAULT : aAnim.mnFill = 0; break; + case AnimationFill::REMOVE : aAnim.mnFill = 1; break; + case AnimationFill::FREEZE : aAnim.mnFill = 2; break; + case AnimationFill::HOLD : aAnim.mnFill = 3; break; + case AnimationFill::TRANSITION : aAnim.mnFill = 4; break; + } + WriteAnimationNode( rStrm, aAnim ); + } + exportIterate( rStrm, xNode ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::ANIMATE : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + exportAnimate( rStrm, xNode ); + } + break; + + case AnimationNodeType::SET : + { + bool bIsAfterEffectNode( isAfterEffectNode( xNode ) ); + if( (nGroupLevel != 4) || !bIsAfterEffectNode ) + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimateSet( rStrm, xNode, bIsAfterEffectNode ? AFTEREFFECT_SET : AFTEREFFECT_NONE ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + else + { + bSkipChildren = true; + } + } + break; + + case AnimationNodeType::ANIMATEMOTION : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimateMotion( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::ANIMATECOLOR : + { + bool bIsAfterEffectNode( isAfterEffectNode( xNode ) ); + if( (nGroupLevel != 4) || !bIsAfterEffectNode ) + { + if( bIsAfterEffectNode ) + xNode = createAfterEffectNodeClone( xNode ); + + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimateColor( rStrm, xNode, bIsAfterEffectNode ? AFTEREFFECT_COLOR : AFTEREFFECT_NONE ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + else + { + bSkipChildren = true; + } + } + break; + + case AnimationNodeType::ANIMATETRANSFORM : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimateTransform( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + } + break; + + case AnimationNodeType::TRANSITIONFILTER : + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + exportAnimEvent( rStrm, xNode ); + exportAnimValue( rStrm, xNode, false ); + exportTransitionFilter( rStrm, xNode ); + } + break; + + case AnimationNodeType::AUDIO : // #i58428# + { + exportAnimNode( rStrm, xNode, nFillDefault ); + exportAnimPropertySet( rStrm, xNode ); + + Reference< XAudio > xAudio( xNode, UNO_QUERY ); + if( xAudio.is() ) + { + Any aAny( xAudio->getSource() ); + OUString aURL; + + if ( ( aAny >>= aURL) && !aURL.isEmpty() ) + { + sal_Int32 nU1 = 2; + sal_Int32 nTrigger = 3; + sal_Int32 nU3 = nAudioGroup; + sal_Int32 nBegin = 0; + { + EscherExContainer aAnimEvent( rStrm, DFF_msofbtAnimEvent, 1 ); + { + EscherExAtom aAnimTrigger( rStrm, DFF_msofbtAnimTrigger ); + rStrm.WriteInt32( nU1 ).WriteInt32( nTrigger ).WriteInt32( nU3 ).WriteInt32( nBegin ); + } + } + nU1 = 1; + nTrigger = 0xb; + nU3 = 0; + { + EscherExContainer aAnimEvent( rStrm, DFF_msofbtAnimEvent, 2 ); + { + EscherExAtom aAnimTrigger( rStrm, DFF_msofbtAnimTrigger ); + rStrm.WriteInt32( nU1 ).WriteInt32( nTrigger ).WriteInt32( nU3 ).WriteInt32( nBegin ); + } + } + EscherExContainer aAnimateTargetElement( rStrm, DFF_msofbtAnimateTargetElement ); + { + sal_uInt32 const nRefMode = 3; + sal_uInt32 const nRefType = 2; + sal_uInt32 nRefId = mrExSoundCollection.GetId( aURL ); + sal_Int32 const begin = -1; + sal_Int32 const end = -1; + + EscherExAtom aAnimReference( rStrm, DFF_msofbtAnimReference ); + rStrm.WriteUInt32( nRefMode ).WriteUInt32( nRefType ).WriteUInt32( nRefId ).WriteInt32( begin ).WriteInt32( end ); + } + } + } + exportAnimValue( rStrm, xNode, false ); + } + break; + } + if( !bSkipChildren ) + { + // export after effect node if one exists for this node + Reference< XAnimationNode > xAfterEffectNode; + if( hasAfterEffectNode( xNode, xAfterEffectNode ) ) + { + exportNode( rStrm, xAfterEffectNode, DFF_msofbtAnimSubGoup, 1, nGroupLevel + 1, bTakeBackInteractiveSequenceTimingForChild, nFillDefault ); + } + + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimationNode > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xChildNode.is() ) + { + if ( xChildNode->getType() == AnimationNodeType::AUDIO ) + { + xAudioNode = xChildNode; + nAudioGroup = mnCurrentGroup; + } + else + exportNode( rStrm, xChildNode, DFF_msofbtAnimGroup, 1, nGroupLevel + 1, bTakeBackInteractiveSequenceTimingForChild, nFillDefault ); + } + } + } + } + } + } + if ( xAudioNode.is() ) + exportNode( rStrm, xAudioNode, DFF_msofbtAnimGroup, 1, nGroupLevel, bTakeBackInteractiveSequenceTimingForChild, nFillDefault ); + + if( xNode->getType() == AnimationNodeType::ITERATE ) + aTarget = Any(); +} + +Reference< XAnimationNode > AnimationExporter::createAfterEffectNodeClone( const Reference< XAnimationNode >& xNode ) +{ + try + { + Reference< css::util::XCloneable > xClonable( xNode, UNO_QUERY_THROW ); + Reference< XAnimationNode > xCloneNode( xClonable->createClone(), UNO_QUERY_THROW ); + + Any aEmpty; + xCloneNode->setBegin( aEmpty ); + + return xCloneNode; + } + catch( Exception& ) + { + OSL_FAIL("(@CL)sd::ppt::AnimationExporter::createAfterEffectNodeClone(), could not create clone!" ); + } + return xNode; +} + +bool AnimationExporter::GetNodeType( const Reference< XAnimationNode >& xNode, sal_Int16& nType ) +{ + // trying to get the nodetype + const Sequence< NamedValue > aUserData = xNode->getUserData(); + for( const NamedValue& rProp : aUserData ) + { + if ( rProp.Name == "node-type" ) + { + if ( rProp.Value >>= nType ) + return true; + } + } + + return false; +} + +void AnimationExporter::exportAnimNode( SvStream& rStrm, const Reference< XAnimationNode >& xNode, + const sal_Int16 nFillDefault ) +{ + EscherExAtom aAnimNodeExAtom( rStrm, DFF_msofbtAnimNode ); + AnimationNode aAnim; + + // attribute Restart + switch( xNode->getRestart() ) + { + default: + case AnimationRestart::DEFAULT : aAnim.mnRestart = 0; break; + case AnimationRestart::ALWAYS : aAnim.mnRestart = 1; break; + case AnimationRestart::WHEN_NOT_ACTIVE : aAnim.mnRestart = 2; break; + case AnimationRestart::NEVER : aAnim.mnRestart = 3; break; + } + + switch( nFillDefault ) + { + default: + case AnimationFill::DEFAULT : aAnim.mnFill = 0; break; + case AnimationFill::REMOVE : aAnim.mnFill = 1; break; + case AnimationFill::FREEZE : + case AnimationFill::HOLD : aAnim.mnFill = 3; break; + case AnimationFill::TRANSITION : aAnim.mnFill = 4; break; + } + // attribute Duration + double fDuration = 0.0; + css::animations::Timing eTiming; + if ( xNode->getDuration() >>= eTiming ) + { + if ( eTiming == Timing_INDEFINITE ) + aAnim.mnDuration = -1; + } + else if ( xNode->getDuration() >>= fDuration ) + { + aAnim.mnDuration = static_cast( fDuration * 1000.0 ); + } + else + aAnim.mnDuration = -1; + + // NodeType, NodeGroup + aAnim.mnNodeType = 1; + aAnim.mnGroupType = mso_Anim_GroupType_SEQ; + switch( xNode->getType() ) + { + case AnimationNodeType::PAR : + aAnim.mnGroupType = mso_Anim_GroupType_PAR; + [[fallthrough]]; + case AnimationNodeType::SEQ : + { + sal_Int16 nType = 0; + if( GetNodeType( xNode, nType ) ) + switch( nType ) + { + case css::presentation::EffectNodeType::TIMING_ROOT : aAnim.mnNodeType = 0x12; break; + case css::presentation::EffectNodeType::MAIN_SEQUENCE : aAnim.mnNodeType = 0x18; break; + } + } + break; + + case AnimationNodeType::ANIMATE : + case AnimationNodeType::SET : + + case AnimationNodeType::CUSTOM : + case AnimationNodeType::ITERATE : + case AnimationNodeType::ANIMATEMOTION : + case AnimationNodeType::ANIMATECOLOR : + case AnimationNodeType::ANIMATETRANSFORM : + { + aAnim.mnGroupType = mso_Anim_GroupType_NODE; + aAnim.mnNodeType = mso_Anim_Behaviour_ANIMATION; + } + break; + + case AnimationNodeType::AUDIO : + { + aAnim.mnGroupType = mso_Anim_GroupType_MEDIA; + aAnim.mnNodeType = mso_Anim_Behaviour_ANIMATION; + } + break; + + case AnimationNodeType::TRANSITIONFILTER : + { + aAnim.mnGroupType = mso_Anim_GroupType_NODE; + aAnim.mnNodeType = mso_Anim_Behaviour_FILTER; + } + break; + } + + WriteAnimationNode( rStrm, aAnim ); +} + +void AnimationExporter::GetUserData( const Sequence< NamedValue >& rUserData, const Any ** pAny, std::size_t nLen ) +{ + // storing user data into pAny, to allow direct access later + memset( pAny, 0, nLen ); + if ( !rUserData.hasElements() ) + return; + + for( const NamedValue& rProp : rUserData ) + { + if ( rProp.Name == "node-type" ) + { + pAny[ DFF_ANIM_NODE_TYPE ] = &(rProp.Value); + } + else if ( rProp.Name == "preset-class" ) + { + pAny[ DFF_ANIM_PRESET_CLASS ] = &(rProp.Value); + } + else if ( rProp.Name == "preset-id" ) + { + pAny[ DFF_ANIM_PRESET_ID ] = &(rProp.Value); + } + else if ( rProp.Name == "preset-sub-type" ) + { + pAny[ DFF_ANIM_PRESET_SUB_TYPE ] = &(rProp.Value); + } + else if ( rProp.Name == "master-element" ) + { + pAny[ DFF_ANIM_AFTEREFFECT ] = &(rProp.Value); + } + } +} + +sal_uInt32 AnimationExporter::GetPresetID( const OUString& rPreset, sal_uInt32 nAPIPresetClass, bool& bPresetId ) +{ + sal_uInt32 nPresetId = 0; + bPresetId = false; + + if ( rPreset.match("ppt_", 0) ) + { + sal_Int32 nLast = rPreset.lastIndexOf( '_' ); + if ( ( nLast != -1 ) && ( ( nLast + 1 ) < rPreset.getLength() ) ) + { + std::u16string_view aNumber( rPreset.subView( nLast + 1 ) ); + nPresetId = o3tl::toUInt32(aNumber); + bPresetId = true; + } + } + else + { + const oox::ppt::preset_mapping* p = oox::ppt::preset_mapping::getList(); + while( p->mpStrPresetId && ((p->mnPresetClass != static_cast(nAPIPresetClass)) || !rPreset.equalsAscii( p->mpStrPresetId )) ) + p++; + + if( p->mpStrPresetId ) + { + nPresetId = p->mnPresetId; + bPresetId = true; + } + } + + return nPresetId; +} + +sal_Int16 AnimationExporter::exportAnimPropertySet( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + sal_Int16 nNodeType = css::presentation::EffectNodeType::DEFAULT; + + EscherExContainer aAnimPropertySet( rStrm, DFF_msofbtAnimPropertySet ); + + Reference< XAnimationNode > xMaster; + + Any aMasterRel, aOverride, aRunTimeContext; + + // storing user data into pAny, to allow direct access later + const Sequence< NamedValue > aUserData = xNode->getUserData(); + const css::uno::Any* pAny[ DFF_ANIM_PROPERTY_ID_COUNT ]; + GetUserData( aUserData, pAny, sizeof( pAny ) ); + + if( pAny[ DFF_ANIM_AFTEREFFECT ] ) + ( *pAny[ DFF_ANIM_AFTEREFFECT ] ) >>= xMaster; + + // calculate master-rel + if( xMaster.is() ) + { + sal_Int32 nMasterRel = 2; + if( xNode.is() && xMaster.is() && (xNode->getParent() == xMaster->getParent() ) ) + nMasterRel = 0; + + aMasterRel <<= nMasterRel; + + pAny[ DFF_ANIM_MASTERREL ] = &aMasterRel; + + aOverride <<= sal_Int32(1); + pAny[ DFF_ANIM_OVERRIDE ] = &aOverride; + + aRunTimeContext <<= sal_Int32(1); + pAny[ DFF_ANIM_RUNTIMECONTEXT ] = &aRunTimeContext; + } + + // the order is important + if ( pAny[ DFF_ANIM_NODE_TYPE ] ) + { + if ( *pAny[ DFF_ANIM_NODE_TYPE ] >>= nNodeType ) + { + sal_uInt32 nPPTNodeType = DFF_ANIM_NODE_TYPE_ON_CLICK; + switch( nNodeType ) + { + case css::presentation::EffectNodeType::ON_CLICK : nPPTNodeType = DFF_ANIM_NODE_TYPE_ON_CLICK; break; + case css::presentation::EffectNodeType::WITH_PREVIOUS : nPPTNodeType = DFF_ANIM_NODE_TYPE_WITH_PREVIOUS; break; + case css::presentation::EffectNodeType::AFTER_PREVIOUS : nPPTNodeType = DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS; break; + case css::presentation::EffectNodeType::MAIN_SEQUENCE : nPPTNodeType = DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE; break; + case css::presentation::EffectNodeType::TIMING_ROOT : nPPTNodeType = DFF_ANIM_NODE_TYPE_TIMING_ROOT; break; + case css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE: nPPTNodeType = DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ; break; + } + exportAnimPropertyuInt32( rStrm, DFF_ANIM_NODE_TYPE, nPPTNodeType ); + } + } + sal_uInt32 nPresetId = 0; + sal_uInt32 nPresetSubType = 0; + sal_uInt32 nAPIPresetClass = EffectPresetClass::CUSTOM; + sal_uInt32 nPresetClass = DFF_ANIM_PRESS_CLASS_USER_DEFINED; + bool bPresetClass, bPresetId, bPresetSubType; + bPresetId = bPresetClass = bPresetSubType = false; + + if ( pAny[ DFF_ANIM_PRESET_CLASS ] ) + { + if ( *pAny[ DFF_ANIM_PRESET_CLASS ] >>= nAPIPresetClass ) + { + sal_uInt8 nPPTPresetClass; + switch( nAPIPresetClass ) + { + case EffectPresetClass::ENTRANCE : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_ENTRANCE; break; + case EffectPresetClass::EXIT : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_EXIT; break; + case EffectPresetClass::EMPHASIS : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_EMPHASIS; break; + case EffectPresetClass::MOTIONPATH : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_MOTIONPATH; break; + case EffectPresetClass::OLEACTION : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_OLE_ACTION; break; + case EffectPresetClass::MEDIACALL : nPPTPresetClass = DFF_ANIM_PRESS_CLASS_MEDIACALL; break; + default : + nPPTPresetClass = DFF_ANIM_PRESS_CLASS_USER_DEFINED; + } + nPresetClass = nPPTPresetClass; + bPresetClass = true; + } + } + if ( pAny[ DFF_ANIM_PRESET_ID ] ) + { + OUString sPreset; + if ( *pAny[ DFF_ANIM_PRESET_ID ] >>= sPreset ) + nPresetId = GetPresetID( sPreset, nAPIPresetClass, bPresetId ); + } + + if ( pAny[ DFF_ANIM_PRESET_SUB_TYPE ] ) + { + OUString sPresetSubType; + if ( *pAny[ DFF_ANIM_PRESET_SUB_TYPE ] >>= sPresetSubType ) + { + nPresetSubType = TranslatePresetSubType( nPresetClass, nPresetId, sPresetSubType ); + bPresetSubType = true; + } + } + if ( bPresetId ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_PRESET_ID, nPresetId ); + if ( bPresetSubType ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_PRESET_SUB_TYPE, nPresetSubType ); + if ( bPresetClass ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_PRESET_CLASS, nPresetClass ); + + if ( pAny[ DFF_ANIM_ID ] ) + { + // TODO DFF_ANIM_ID + } + + if ( pAny[ DFF_ANIM_AFTEREFFECT ] ) + { + bool bAfterEffect = false; + if ( *pAny[ DFF_ANIM_AFTEREFFECT ] >>= bAfterEffect ) + exportAnimPropertyByte( rStrm, DFF_ANIM_AFTEREFFECT, int(bAfterEffect) ); + } + + if ( pAny[ DFF_ANIM_RUNTIMECONTEXT ] ) + { + sal_Int32 nRunTimeContext = 0; + if ( *pAny[ DFF_ANIM_RUNTIMECONTEXT ] >>= nRunTimeContext ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_RUNTIMECONTEXT, nRunTimeContext ); + } + if ( pAny[ DFF_ANIM_PATH_EDIT_MODE ] ) + { + // TODO DFF_ANIM_ID + } + + if( !xMaster.is() ) + { + Reference< XAnimateColor > xColor( xNode, UNO_QUERY ); + if( xColor.is() ) + { + + bool bDirection = !xColor->getDirection(); + exportAnimPropertyuInt32( rStrm, DFF_ANIM_DIRECTION, bDirection ? 1 : 0 ); + } + } + + if ( pAny[ DFF_ANIM_OVERRIDE ] ) + { + sal_Int32 nOverride = 0; + if ( *pAny[ DFF_ANIM_OVERRIDE ] >>= nOverride ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_OVERRIDE, nOverride ); + } + + if ( pAny[ DFF_ANIM_MASTERREL ] ) + { + sal_Int32 nMasterRel = 0; + if ( *pAny[ DFF_ANIM_MASTERREL ] >>= nMasterRel ) + exportAnimPropertyuInt32( rStrm, DFF_ANIM_MASTERREL, nMasterRel ); + } + +/* todo + Reference< XAudio > xAudio( xNode, UNO_QUERY ); + if( xAudio.is() ) + { + sal_Int16 nEndAfterSlide = 0; + nEndAfterSlide = xAudio->getEndAfterSlide(); + exportAnimPropertyuInt32( rStrm, DFF_ANIM_ENDAFTERSLIDE, nEndAfterSlide, TRANSLATE_NONE ); + } +*/ + Reference< XAnimate > xAnim( xNode, UNO_QUERY ); + if( xAnim.is() ) + { + // TODO: DFF_ANIM_TIMEFILTER + } + if ( pAny[ DFF_ANIM_EVENT_FILTER ] ) + { + // TODO DFF_ANIM_EVENT_FILTER + } + if ( pAny[ DFF_ANIM_VOLUME ] ) + { + // TODO DFF_ANIM_VOLUME + } + return nNodeType; +} + +bool AnimationExporter::exportAnimProperty( SvStream& rStrm, const sal_uInt16 nPropertyId, const css::uno::Any& rAny, const TranslateMode eTranslateMode ) +{ + bool bRet = false; + if ( rAny.hasValue() ) + { + switch( rAny.getValueType().getTypeClass() ) + { + case css::uno::TypeClass_UNSIGNED_SHORT : + case css::uno::TypeClass_SHORT : + case css::uno::TypeClass_UNSIGNED_LONG : + case css::uno::TypeClass_LONG : + { + sal_Int32 nVal = 0; + if ( rAny >>= nVal ) + { + exportAnimPropertyuInt32( rStrm, nPropertyId, nVal ); + bRet = true; + } + } + break; + + case css::uno::TypeClass_DOUBLE : + { + double fVal = 0.0; + if ( rAny >>= fVal ) + { + exportAnimPropertyFloat( rStrm, nPropertyId, fVal ); + bRet = true; + } + } + break; + case css::uno::TypeClass_FLOAT : + { + float fVal = 0.0; + if ( rAny >>= fVal ) + { + if ( eTranslateMode & TRANSLATE_NUMBER_TO_STRING ) + { + OUString aNumber( OUString::number( fVal ) ); + exportAnimPropertyString( rStrm, nPropertyId, aNumber, eTranslateMode ); + } + else + { + exportAnimPropertyFloat( rStrm, nPropertyId, fVal ); + bRet = true; + } + } + } + break; + case css::uno::TypeClass_STRING : + { + OUString aStr; + if ( rAny >>= aStr ) + { + exportAnimPropertyString( rStrm, nPropertyId, aStr, eTranslateMode ); + bRet = true; + } + } + break; + default: + break; + } + } + return bRet; +} +void AnimationExporter::exportAnimPropertyString( SvStream& rStrm, const sal_uInt16 nPropertyId, const OUString& rVal, const TranslateMode eTranslateMode ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAttributeValue, nPropertyId ); + rStrm.WriteUChar( DFF_ANIM_PROP_TYPE_UNISTRING ); + OUString aStr( rVal ); + if ( eTranslateMode != TRANSLATE_NONE ) + ImplTranslateAttribute( aStr, eTranslateMode ); + writeZString( rStrm, aStr ); +} + +void AnimationExporter::exportAnimPropertyFloat( SvStream& rStrm, const sal_uInt16 nPropertyId, const double& rVal ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAttributeValue, nPropertyId ); + float fFloat = static_cast(rVal); + rStrm.WriteUChar( DFF_ANIM_PROP_TYPE_FLOAT ) + .WriteFloat( fFloat ); +} + +void AnimationExporter::exportAnimPropertyuInt32( SvStream& rStrm, const sal_uInt16 nPropertyId, const sal_uInt32 nVal ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAttributeValue, nPropertyId ); + rStrm.WriteUChar( DFF_ANIM_PROP_TYPE_INT32 ) + .WriteUInt32( nVal ); +} + +void AnimationExporter::exportAnimPropertyByte( SvStream& rStrm, const sal_uInt16 nPropertyId, const sal_uInt8 nVal ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAttributeValue, nPropertyId ); + rStrm.WriteUChar( DFF_ANIM_PROP_TYPE_BYTE ) + .WriteUChar( nVal ); +} + +void AnimationExporter::writeZString( SvStream& rStrm, const OUString& rVal ) +{ + sal_Int32 i; + for ( i = 0; i < rVal.getLength(); i++ ) + rStrm.WriteUInt16( rVal[ i ] ); + rStrm.WriteUInt16( 0 ); +} + +void AnimationExporter::exportAnimAction( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimAction ); + + sal_Int32 const nConcurrent = 1; + sal_Int32 const nNextAction = 1; + sal_Int32 nEndSync = 0; + sal_Int32 const nU4 = 0; + sal_Int32 const nU5 = 3; + + sal_Int16 nAnimationEndSync = 0; + if ( xNode->getEndSync() >>= nAnimationEndSync ) + { + if ( nAnimationEndSync == AnimationEndSync::ALL ) + nEndSync = 1; + } + rStrm.WriteInt32( nConcurrent ) + .WriteInt32( nNextAction ) + .WriteInt32( nEndSync ) + .WriteInt32( nU4 ) + .WriteInt32( nU5 ); + +} + +// nFlags Bit 6 = fixInteractiveSequenceTiming (for child) +// nFlags Bit 5 = fixInteractiveSequenceTiming (for root) +// nFlags Bit 4 = first node of main sequence -> begin event next has to be replaced to indefinite +void AnimationExporter::exportAnimEvent( SvStream& rStrm, const Reference< XAnimationNode >& xNode, const sal_Int32 nFlags ) +{ + sal_uInt16 i; + for ( i = 0; i < 4; i++ ) + { + sal_Int32 nU1 = 0; + sal_Int32 nTrigger = 0; + sal_Int32 nU3 = 0; + sal_Int32 nBegin = 0; + + bool bCreateEvent = false; + Any aSource; + + switch( i ) + { + case 0 : + case 1 : + { + Any aAny; + Event aEvent; + css::animations::Timing eTiming; + if ( i == 0 ) + { + if ( nFlags & 0x20 ) + { + // taking the first child + Reference< XEnumerationAccess > xEA( xNode, UNO_QUERY_THROW ); + Reference< XEnumeration > xE( xEA->createEnumeration(), css::uno::UNO_SET_THROW ); + if ( xE->hasMoreElements() ) + { + Reference< XAnimationNode > xClickNode( xE->nextElement(), UNO_QUERY ); + aAny = xClickNode->getBegin(); + } + } + else if ( nFlags & 0x40 ) + { + // begin has to be replaced with void, so don't do anything + } + else + { + aAny = xNode->getBegin(); + if ( nFlags & 0x10 ) // replace ON_NEXT with INDEFINITE + { + if ( ( aAny >>= aEvent ) && ( aEvent.Trigger == EventTrigger::ON_NEXT ) ) + { + eTiming = Timing_INDEFINITE; + aAny <<= eTiming; + } + } + } + } + else + aAny = xNode->getEnd(); + + double fTiming = 0.0; + if ( aAny >>= aEvent ) + { + bCreateEvent = true; + switch( aEvent.Trigger ) + { + case EventTrigger::NONE : nTrigger = 0; break; + case EventTrigger::ON_BEGIN : nTrigger = 1; break; + case EventTrigger::ON_END : nTrigger = 2; break; + case EventTrigger::BEGIN_EVENT : nTrigger = 3; break; + case EventTrigger::END_EVENT : nTrigger = 4; nU1 = 2; nU3 = mnCurrentGroup; break; + case EventTrigger::ON_CLICK : nTrigger = 5; break; + case EventTrigger::ON_DBL_CLICK : nTrigger = 6; break; + case EventTrigger::ON_MOUSE_ENTER : nTrigger = 7; break; + case EventTrigger::ON_MOUSE_LEAVE : nTrigger = 8; break; + case EventTrigger::ON_NEXT : nTrigger = 9; break; + case EventTrigger::ON_PREV : nTrigger = 10; break; + case EventTrigger::ON_STOP_AUDIO : nTrigger = 11; break; + } + if ( aEvent.Offset.hasValue() ) + { + if ( aEvent.Offset >>= eTiming ) + { + if ( eTiming == Timing_INDEFINITE ) + nBegin = -1; + } + else if ( aEvent.Offset >>= fTiming ) + nBegin = static_cast( fTiming * 1000.0 ); + } + aSource = aEvent.Source; + } + else if ( aAny >>= eTiming ) + { + bCreateEvent = true; + if ( eTiming == Timing_INDEFINITE ) + nBegin = -1; + } + else if ( aAny >>= fTiming ) + { + bCreateEvent = true; + nBegin = static_cast( fTiming * 1000.0 ); + } + } + break; + + case 2 : + { + if ( nFlags & ( 1 << i ) ) + { + bCreateEvent = true; + nU1 = 1; + nTrigger = 9; + } + } + break; + case 3 : + { + if ( nFlags & ( 1 << i ) ) + { + bCreateEvent = true; + nU1 = 1; + nTrigger = 10; + } + } + break; + } + if ( bCreateEvent ) + { + EscherExContainer aAnimEvent( rStrm, DFF_msofbtAnimEvent, i + 1 ); + { + EscherExAtom aAnimTrigger( rStrm, DFF_msofbtAnimTrigger ); + rStrm.WriteInt32( nU1 ) + .WriteInt32( nTrigger ) + .WriteInt32( nU3 ) + .WriteInt32( nBegin ); + } + exportAnimateTargetElement( rStrm, aSource, ( nFlags & ( 1 << i ) ) != 0 ); + } + } +} + +Any AnimationExporter::convertAnimateValue( const Any& rSourceValue, std::u16string_view rAttributeName ) +{ + OUString aDest; + if ( rAttributeName == u"X" + || rAttributeName == u"Y" + || rAttributeName == u"Width" + || rAttributeName == u"Height" + ) + { + OUString aStr; + if ( rSourceValue >>= aStr ) + { + ImplTranslateAttribute( aStr, TRANSLATE_MEASURE ); + aDest += aStr; + } + } + else if ( rAttributeName == u"Rotate" // "r" or "style.rotation" ? + || rAttributeName == u"Opacity" + || rAttributeName == u"CharHeight" + || rAttributeName == u"SkewX" + ) + { + double fNumber = 0.0; + if ( rSourceValue >>= fNumber ) + aDest += OUString::number( fNumber ); + } + else if ( rAttributeName == u"Color" + || rAttributeName == u"FillColor" // "Fillcolor" or "FillColor" ? + || rAttributeName == u"LineColor" + || rAttributeName == u"CharColor" + ) + { + sal_Int32 nColor = 0; + Sequence< double > aHSL( 3 ); + OUString aP( "," ); + if ( rSourceValue >>= aHSL ) + { + aDest += "hsl(" + + OUString::number( static_cast( aHSL[ 0 ] / ( 360.0 / 255 ) ) ) + + aP + + OUString::number( static_cast( aHSL[ 1 ] * 255.0 ) ) + + aP + + OUString::number( static_cast( aHSL[ 2 ] * 255.0 ) ) + + ")"; + } + else if ( rSourceValue >>= nColor ) + { + aDest += "rgb(" + + OUString::number( static_cast(nColor) ) + + aP + + OUString::number( static_cast( nColor >> 8 ) ) + + aP + + OUString::number( static_cast( nColor >> 16 ) ) + + ")"; + } + } + else if ( rAttributeName == u"FillStyle" ) + { + css::drawing::FillStyle eFillStyle; + if ( rSourceValue >>= eFillStyle ) + { + if ( eFillStyle == css::drawing::FillStyle_NONE ) + aDest += "none"; // ? + else + aDest += "solid"; + } + } + else if (rAttributeName == u"FillOn") + { + bool bFillOn; + if ( rSourceValue >>= bFillOn ) + { + if ( bFillOn ) + aDest += "true"; + else + aDest += "false"; + } + } + else if ( rAttributeName == u"LineStyle" ) + { + css::drawing::LineStyle eLineStyle; + if ( rSourceValue >>= eLineStyle ) + { + if ( eLineStyle == css::drawing::LineStyle_NONE ) + aDest += "false"; + else + aDest += "true"; + } + } + else if ( rAttributeName == u"CharWeight" ) + { + float fFontWeight = 0.0; + if ( rSourceValue >>= fFontWeight ) + { + if ( fFontWeight == css::awt::FontWeight::BOLD ) + aDest += "bold"; + else + aDest += "normal"; + } + } + else if ( rAttributeName == u"CharUnderline" ) + { + sal_Int16 nFontUnderline = 0; + if ( rSourceValue >>= nFontUnderline ) + { + if ( nFontUnderline == css::awt::FontUnderline::NONE ) + aDest += "false"; + else + aDest += "true"; + } + } + else if ( rAttributeName == u"CharPosture" ) + { + css::awt::FontSlant eFontSlant; + if ( rSourceValue >>= eFontSlant ) + { + if ( eFontSlant == css::awt::FontSlant_ITALIC ) + aDest += "italic"; + else + aDest += "normal"; // ? + } + } + else if ( rAttributeName == u"Visibility" ) + { + bool bVisible = true; + if ( rSourceValue >>= bVisible ) + { + if ( bVisible ) + aDest += "visible"; + else + aDest += "hidden"; + } + } + Any aRet; + if ( !aDest.isEmpty() ) + aRet <<= aDest; + else + aRet = rSourceValue; + return aRet; +} + +void AnimationExporter::exportAnimateSet( SvStream& rStrm, const Reference< XAnimationNode >& xNode, int nAfterEffectType ) +{ + Reference< XAnimateSet > xSet( xNode, UNO_QUERY ); + if( !xSet.is() ) + return; + + EscherExContainer aAnimateSet( rStrm, DFF_msofbtAnimateSet, 0 ); + { + EscherExAtom aAnimateSetData( rStrm, DFF_msofbtAnimateSetData ); + sal_uInt32 const nId1 = 1; // ?? + sal_uInt32 const nId2 = 1; // ?? + rStrm.WriteUInt32( nId1 ).WriteUInt32( nId2 ); + } + Any aConvertedValue( convertAnimateValue( xSet->getTo(), xSet->getAttributeName() ) ); + if ( aConvertedValue.hasValue() ) + exportAnimProperty( rStrm, 1, aConvertedValue, TRANSLATE_NONE ); + exportAnimateTarget( rStrm, xNode, 0, nAfterEffectType ); +} + +sal_uInt32 AnimationExporter::GetValueTypeForAttributeName( const OUString& rAttributeName ) +{ + sal_uInt32 nValueType = 0; + + struct Entry + { + const char* pName; + sal_uInt8 nType; + }; + static const Entry lcl_attributeMap[] = + { + { "charcolor", 2 }, + { "charfontname", 0 }, + { "charheight", 1 }, + { "charposture", 0 }, + // TODO(Q1): This should prolly be changed in PPT import + // { "charrotation", ATTRIBUTE_CHAR_ROTATION }, + { "charrotation", 1 }, + { "charunderline", 0 }, + { "charweight", 0 }, + { "color", 2 }, + { "dimcolor", 2 }, + { "fillcolor", 2 }, + { "fillstyle", 0 }, + { "height", 1 }, + { "linecolor", 2 }, + { "linestyle", 0 }, + { "opacity", 0 }, + { "rotate", 1 }, + { "skewx", 1 }, + { "skewy", 1 }, + { "visibility", 1 }, + { "width", 1 }, + { "x", 1 }, + { "y", 1 }, + { nullptr, 0 } + }; + const Entry* pPtr = &lcl_attributeMap[ 0 ]; + while( pPtr->pName ) + { + if ( rAttributeName.equalsIgnoreAsciiCaseAscii( pPtr->pName ) ) + { + nValueType = pPtr->nType; + break; + } + pPtr++; + } + DBG_ASSERT( pPtr->pName, "GetValueTypeForAttributeName, unknown property value!" ); + return nValueType; +} + +void AnimationExporter::exportAnimate( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimate > xAnimate( xNode, UNO_QUERY ); + if ( !xAnimate.is() ) + return; + + Any aBy ( xAnimate->getBy() ); + Any aFrom( xAnimate->getFrom() ); + Any aTo ( xAnimate->getTo() ); + + EscherExContainer aContainer( rStrm, DFF_msofbtAnimate, 0 ); + { + EscherExAtom aAnimateData( rStrm, DFF_msofbtAnimateData ); + sal_uInt32 nBits = 0x38; + sal_Int16 nTmp = xAnimate->getCalcMode(); + sal_uInt32 nCalcMode = /* (nTmp == AnimationCalcMode::FORMULA) ? 2 : */ (nTmp == AnimationCalcMode::LINEAR) ? 1 : 0; + sal_uInt32 nValueType = GetValueTypeForAttributeName( xAnimate->getAttributeName() ); + + if ( aBy.hasValue() ) + nBits |= 1; + if ( aFrom.hasValue() ) + nBits |= 2; + if ( aTo.hasValue() ) + nBits |= 4; + + rStrm.WriteUInt32( nCalcMode ) + .WriteUInt32( nBits ) + .WriteUInt32( nValueType ); + } + if ( aBy.hasValue() ) + exportAnimProperty( rStrm, 1, aBy, TRANSLATE_NUMBER_TO_STRING | TRANSLATE_MEASURE ); + if ( aFrom.hasValue() ) + exportAnimProperty( rStrm, 2, aFrom, TRANSLATE_NUMBER_TO_STRING | TRANSLATE_MEASURE ); + if ( aTo.hasValue() ) + exportAnimProperty( rStrm, 3, aTo, TRANSLATE_NUMBER_TO_STRING | TRANSLATE_MEASURE ); + + exportAnimateKeyPoints( rStrm, xAnimate ); + exportAnimateTarget( rStrm, xNode ); +} + +void AnimationExporter::exportAnimateTarget( SvStream& rStrm, const Reference< XAnimationNode >& xNode, const sal_uInt32 nForceAttributeNames, int nAfterEffectType ) +{ + EscherExContainer aAnimateTarget( rStrm, DFF_msofbtAnimateTarget, 0 ); + Reference< XAnimate > xAnimate( xNode, UNO_QUERY ); + if ( !xAnimate.is() ) + return; + + { + EscherExAtom aAnimateTargetSettings( rStrm, DFF_msofbtAnimateTargetSettings, 0 ); + // nBits %0001: additive, %0010: accumulate, %0100: attributeName, %1000: transformtype + // nAdditive 0 = base, 1 = sum, 2 = replace, 3 = multiply, 4 = none + // nAccumulate 0 = none, 1 = always + // nTransformType 0: "property" else "image" + sal_uInt32 nBits = 0; + sal_uInt32 nAdditive = 0; + sal_uInt32 nAccumulate = 0; + sal_uInt32 const nTransformType = 0; + if ( xAnimate.is() ) + { + if ( !xAnimate->getAttributeName().isEmpty() ) + nBits |= 4; // what is attributeName ?, maybe this is set if a DFF_msofbtAnimateAttributeNames is written + sal_Int16 nAdditiveMode = xAnimate->getAdditive(); + if ( nAdditiveMode != AnimationAdditiveMode::BASE ) + { + nBits |= 1; + switch( nAdditiveMode ) + { + case AnimationAdditiveMode::SUM : nAdditive = 1; break; + case AnimationAdditiveMode::REPLACE : nAdditive = 2; break; + case AnimationAdditiveMode::MULTIPLY : nAdditive = 3; break; + case AnimationAdditiveMode::NONE : nAdditive = 4; break; + } + } + if ( xAnimate->getAccumulate() ) + { + nBits |= 2; + nAccumulate = 1; + } + } + rStrm.WriteUInt32( nBits ) + .WriteUInt32( nAdditive ) + .WriteUInt32( nAccumulate ) + .WriteUInt32( nTransformType ); + } + if ( !xAnimate->getAttributeName().isEmpty() || nForceAttributeNames ) + { + EscherExContainer aAnimateAttributeNames( rStrm, DFF_msofbtAnimateAttributeNames, 1 ); + OUString aAttributeName( xAnimate->getAttributeName() ); + if ( nForceAttributeNames ) + { + if( nForceAttributeNames == 1 ) + { + aAttributeName = "r"; + } + } + sal_Int32 nIndex = 0; + do + { + OUString aToken( aAttributeName.getToken( 0, ';', nIndex ) ); + exportAnimPropertyString( rStrm, 0, aToken, TRANSLATE_ATTRIBUTE ); + } + while ( nIndex >= 0 ); + } + + if( nAfterEffectType != AFTEREFFECT_NONE ) + { + EscherExContainer aAnimPropertySet( rStrm, DFF_msofbtAnimPropertySet ); + exportAnimPropertyuInt32( rStrm, 6, 1 ); + if( nAfterEffectType == AFTEREFFECT_COLOR ) + { + exportAnimPropertyuInt32( rStrm, 4, 0 ); + exportAnimPropertyuInt32( rStrm, 5, 0 ); + } + } + exportAnimateTargetElement( rStrm, aTarget.hasValue() ? aTarget : xAnimate->getTarget(), false ); +} + +Reference< XShape > AnimationExporter::getTargetElementShape( const Any& rAny, sal_Int32& rBegin, sal_Int32& rEnd, bool& rParagraphTarget ) +{ + Reference< XShape > xShape; + rAny >>= xShape; + + rParagraphTarget = false; + + if( xShape.is() ) + return xShape; + + ParagraphTarget aParaTarget; + if( rAny >>= aParaTarget ) + xShape = aParaTarget.Shape; + if ( !xShape.is() ) + return xShape; + + // now calculating the character range for the paragraph + sal_Int16 nParagraph = aParaTarget.Paragraph; + Reference< XSimpleText > xText( xShape, UNO_QUERY ); + if ( !xText.is() ) + return xShape; + + rParagraphTarget = true; + Reference< XEnumerationAccess > xTextParagraphEnumerationAccess( xText, UNO_QUERY ); + if ( !xTextParagraphEnumerationAccess.is() ) + return xShape; + + Reference< XEnumeration > xTextParagraphEnumeration( xTextParagraphEnumerationAccess->createEnumeration() ); + if ( !xTextParagraphEnumeration.is() ) + return xShape; + + sal_Int16 nCurrentParagraph; + rBegin = rEnd = nCurrentParagraph = 0; + while ( xTextParagraphEnumeration->hasMoreElements() ) + { + Reference< XTextRange > xTextRange( xTextParagraphEnumeration->nextElement(), UNO_QUERY ); + if ( xTextRange.is() ) + { + OUString aParaText( xTextRange->getString() ); + sal_Int32 nLength = aParaText.getLength() + 1; + rEnd += nLength; + if ( nCurrentParagraph == nParagraph ) + break; + nCurrentParagraph++; + rBegin += nLength; + } + } + + return xShape; +} + +void AnimationExporter::exportAnimateTargetElement( SvStream& rStrm, const Any& rAny, const bool bCreate2b01Atom ) +{ + sal_uInt32 nRefMode = 0; // nRefMode == 2 -> Paragraph + sal_Int32 begin = -1; + sal_Int32 end = -1; + bool bParagraphTarget; + + Reference< XShape > xShape = getTargetElementShape(rAny, begin, end, bParagraphTarget); + + if( bParagraphTarget ) + nRefMode = 2; + + if ( !(xShape.is() || bCreate2b01Atom) ) + return; + + EscherExContainer aAnimateTargetElement( rStrm, DFF_msofbtAnimateTargetElement ); + if ( xShape.is() ) + { + EscherExAtom aAnimReference( rStrm, DFF_msofbtAnimReference ); + + sal_uInt32 const nRefType = 1; // TODO: nRefType == 2 -> Sound; + sal_uInt32 nRefId = mrSolverContainer.GetShapeId( xShape ); + + rStrm.WriteUInt32( nRefMode ) + .WriteUInt32( nRefType ) + .WriteUInt32( nRefId ) + .WriteInt32( begin ) + .WriteInt32( end ); + } + if ( bCreate2b01Atom ) + { + EscherExAtom a2b01Atom( rStrm, 0x2b01 ); + rStrm.WriteUInt32( 1 ); // ? + } +} + +void AnimationExporter::exportAnimateKeyPoints( SvStream& rStrm, const Reference< XAnimate >& xAnimate ) +{ + Sequence< double > aKeyTimes( xAnimate->getKeyTimes() ); + Sequence< Any > aValues( xAnimate->getValues() ); + OUString aFormula( xAnimate->getFormula() ); + if ( !aKeyTimes.hasElements() ) + return; + + EscherExContainer aAnimKeyPoints( rStrm, DFF_msofbtAnimKeyPoints ); + sal_Int32 i; + for ( i = 0; i < aKeyTimes.getLength(); i++ ) + { + { + EscherExAtom aAnimKeyTime( rStrm, DFF_msofbtAnimKeyTime ); + sal_Int32 nKeyTime = static_cast( aKeyTimes[ i ] * 1000.0 ); + rStrm.WriteInt32( nKeyTime ); + } + Any aAny[ 2 ]; + if ( aValues[ i ].hasValue() ) + { + ValuePair aPair; + if ( aValues[ i ] >>= aPair ) + { + aAny[ 0 ] = convertAnimateValue( aPair.First, xAnimate->getAttributeName() ); + aAny[ 1 ] = convertAnimateValue( aPair.Second, xAnimate->getAttributeName() ); + } + else + { + aAny[ 0 ] = convertAnimateValue( aValues[ i ], xAnimate->getAttributeName() ); + } + if ( !i && !aFormula.isEmpty() ) + { + ImplTranslateAttribute( aFormula, TRANSLATE_MEASURE ); + aAny[ 1 ] <<= aFormula; + } + exportAnimProperty( rStrm, 0, aAny[ 0 ], TRANSLATE_NONE ); + exportAnimProperty( rStrm, 1, aAny[ 1 ], TRANSLATE_NONE ); + } + } +} + +void AnimationExporter::exportAnimValue( SvStream& rStrm, const Reference< XAnimationNode >& xNode, const bool bExportAlways ) +{ + Any aAny; + // repeat count (0) + double fRepeat = 0.0; + float fRepeatCount = 0.0; + css::animations::Timing eTiming; + aAny = xNode->getRepeatCount(); + if ( aAny >>= eTiming ) + { + if ( eTiming == Timing_INDEFINITE ) + fRepeatCount = (float(3.40282346638528860e+38)); + } + else if ( aAny >>= fRepeat ) + fRepeatCount = static_cast(fRepeat); + if ( fRepeatCount != 0.0 ) + { + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimValue ); + sal_uInt32 const nType = 0; + rStrm.WriteUInt32( nType ) + .WriteFloat( fRepeatCount ); + } + // accelerate (3) + float fAccelerate = static_cast(xNode->getAcceleration()); + if ( bExportAlways || ( fAccelerate != 0.0 ) ) + { + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimValue ); + sal_uInt32 const nType = 3; + rStrm.WriteUInt32( nType ) + .WriteFloat( fAccelerate ); + } + + // decelerate (4) + float fDecelerate = static_cast(xNode->getDecelerate()); + if ( bExportAlways || ( fDecelerate != 0.0 ) ) + { + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimValue ); + sal_uInt32 const nType = 4; + rStrm.WriteUInt32( nType ) + .WriteFloat( fDecelerate ); + } + + // autoreverse (5) + bool bAutoReverse = xNode->getAutoReverse(); + if ( bExportAlways || bAutoReverse ) + { + EscherExAtom aExAtom( rStrm, DFF_msofbtAnimValue ); + sal_uInt32 const nType = 5; + sal_uInt32 nVal = bAutoReverse ? 1 : 0; + rStrm.WriteUInt32( nType ) + .WriteUInt32( nVal ); + } +} + +void AnimationExporter::exportTransitionFilter( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XTransitionFilter > xFilter( xNode, UNO_QUERY ); + if ( !xFilter.is() ) + return; + + EscherExContainer aAnimateFilter( rStrm, DFF_msofbtAnimateFilter ); + { + EscherExAtom aAnimateFilterData( rStrm, DFF_msofbtAnimateFilterData ); + sal_uInt32 const nBits = 3; // bit 0 -> use AnimAttributeValue + // bit 1 -> use nTransition + + sal_uInt32 nTransition = xFilter->getMode() ? 0 : 1; + rStrm.WriteUInt32( nBits ) + .WriteUInt32( nTransition ); + } + const char* pFilter = FindTransitionName( xFilter->getTransition(), xFilter->getSubtype(), xFilter->getDirection() ); + if ( pFilter ) + { + const OUString aStr( OUString::createFromAscii( pFilter ) ); + exportAnimPropertyString( rStrm, 1, aStr, TRANSLATE_NONE ); + } + exportAnimateTarget( rStrm, xNode ); +} + +void AnimationExporter::exportAnimateMotion( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateMotion > xMotion( xNode, UNO_QUERY ); + if ( !xMotion.is() ) + return; + + EscherExContainer aAnimateMotion( rStrm, DFF_msofbtAnimateMotion ); + { + { //SJ: Ignored from import filter + EscherExAtom aAnimateMotionData( rStrm, DFF_msofbtAnimateMotionData ); + sal_uInt32 const nBits = 0x98; + sal_uInt32 const nOrigin = 0x2; + float const fByX = 100.0; // nBits&1 + float const fByY = 100.0; // nBits&1 + float const fFromX = 0.0; // nBits&2 + float const fFromY = 0.0; // nBits&2 + float const fToX = 100.0; // nBits&4 + float const fToY = 100.0; // nBits&4 + rStrm.WriteUInt32( nBits ).WriteFloat( fByX ).WriteFloat( fByY ).WriteFloat( fFromX ).WriteFloat( fFromY ).WriteFloat( fToX ).WriteFloat( fToY ).WriteUInt32( nOrigin ); + } + + OUString aStr; + if ( xMotion->getPath() >>= aStr ) + { + if ( !aStr.isEmpty() ) + exportAnimPropertyString( rStrm, 1, aStr, TRANSLATE_NONE ); + } + exportAnimateTarget( rStrm, xNode ); + } +} + +void AnimationExporter::exportAnimateTransform( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateTransform > xTransform( xNode, UNO_QUERY ); + if ( !xTransform.is() ) + return; + + if ( xTransform->getTransformType() == AnimationTransformType::SCALE ) + { + EscherExContainer aAnimateScale( rStrm, DFF_msofbtAnimateScale ); + { + EscherExAtom aAnimateScaleData( rStrm, DFF_msofbtAnimateScaleData ); + sal_uInt32 nBits = 0; + sal_uInt32 const nZoomContents = 1; + float fByX = 100.0; + float fByY = 100.0; + float fFromX = 0.0; + float fFromY = 0.0; + float fToX = 100.0; + float fToY = 100.0; + + double fX = 0.0, fY = 0.0; + ValuePair aPair; + if ( xTransform->getBy() >>= aPair ) + { + if ( ( aPair.First >>= fX ) && ( aPair.Second >>= fY ) ) + { + nBits |= 1; + fByX = static_cast( fX * 100 ); + fByY = static_cast( fY * 100 ); + } + } + if ( xTransform->getFrom() >>= aPair ) + { + if ( ( aPair.First >>= fX ) && ( aPair.Second >>= fY ) ) + { + nBits |= 2; + fFromX = static_cast( fX * 100 ); + fFromY = static_cast( fY * 100 ); + } + } + if( xTransform->getTo() >>= aPair ) + { + if ( ( aPair.First >>= fX ) && ( aPair.Second >>= fY ) ) + { + nBits |= 4; + fToX = static_cast( fX * 100 ); + fToY = static_cast( fY * 100 ); + } + } + + // TODO: ZoomContents: + //if( nBits & 8 ) + //( fprintf( mpFile, " zoomContents=\"%s\"", nZoomContents ? "true" : "false" ); + + rStrm.WriteUInt32( nBits ).WriteFloat( fByX ).WriteFloat( fByY ).WriteFloat( fFromX ).WriteFloat( fFromY ).WriteFloat( fToX ).WriteFloat( fToY ).WriteUInt32( nZoomContents ); + } + exportAnimateTarget( rStrm, xNode ); + } + else if ( xTransform->getTransformType() == AnimationTransformType::ROTATE ) + { + EscherExContainer aAnimateRotation( rStrm, DFF_msofbtAnimateRotation ); + { + EscherExAtom aAnimateRotationData( rStrm, DFF_msofbtAnimateRotationData ); + sal_uInt32 nBits = 0; + sal_uInt32 const nU1 = 0; + float fBy = 360.0; + float fFrom = 0.0; + float fTo = 360.0; + + double fVal = 0.0; + if ( xTransform->getBy() >>= fVal ) + { + nBits |= 1; + fBy = static_cast(fVal); + } + if ( xTransform->getFrom() >>= fVal ) + { + nBits |= 2; + fFrom = static_cast(fVal); + } + if ( xTransform->getTo() >>= fVal ) + { + nBits |= 4; + fTo = static_cast(fVal); + } + rStrm.WriteUInt32( nBits ).WriteFloat( fBy ).WriteFloat( fFrom ).WriteFloat( fTo ).WriteUInt32( nU1 ); + } + exportAnimateTarget( rStrm, xNode, 1 ); + } +} + +bool AnimationExporter::getColorAny( const Any& rAny, const sal_Int16 nColorSpace, sal_Int32& rMode, sal_Int32& rA, sal_Int32& rB, sal_Int32& rC ) +{ + bool bIsColor = true; + + rMode = 0; + if ( nColorSpace == AnimationColorSpace::HSL ) + rMode = 1; + + sal_Int32 nColor = 0; + Sequence< double > aHSL( 3 ); + if ( rAny >>= nColor ) // RGB color + { + rA = static_cast( nColor >> 16 ); + rB = static_cast( nColor >> 8 ); + rC = static_cast(nColor); + } + else if ( rAny >>= aHSL ) // HSL + { + rA = static_cast( aHSL[ 0 ] * 255.0 / 360.0 ); + rB = static_cast( aHSL[ 1 ] * 255.0 ); + rC = static_cast( aHSL[ 2 ] * 255.0 ); + } + else + bIsColor = false; + return bIsColor; +} + +void AnimationExporter::exportAnimateColor( SvStream& rStrm, const Reference< XAnimationNode >& xNode, int nAfterEffectType ) +{ + Reference< XAnimateColor > xColor( xNode, UNO_QUERY ); + if ( !xColor.is() ) + return; + + EscherExContainer aAnimateColor( rStrm, DFF_msofbtAnimateColor ); + { + EscherExAtom aAnimateColorData( rStrm, DFF_msofbtAnimateColorData ); + sal_uInt32 nBits = 8; + + sal_Int32 nByMode, nByA, nByB, nByC; + nByMode = nByA = nByB = nByC = 0; + + sal_Int32 nFromMode, nFromA, nFromB, nFromC; + nFromMode = nFromA = nFromB = nFromC = 0; + + sal_Int32 nToMode, nToA, nToB, nToC; + nToMode = nToA = nToB = nToC = 0; + + sal_Int16 nColorSpace = xColor->getColorInterpolation(); + + Any aAny( xColor->getBy() ); + if ( aAny.hasValue() ) + { + if ( getColorAny( aAny, nColorSpace, nByMode, nByA, nByB, nByC ) ) + nBits |= 0x11; + } + aAny = xColor->getFrom(); + if ( aAny.hasValue() ) + { + if ( getColorAny( aAny, nColorSpace, nFromMode, nFromA, nFromB, nFromC ) ) + nBits |= 0x12; + } + aAny = xColor->getTo(); + if ( aAny.hasValue() ) + { + if ( getColorAny( aAny, nColorSpace, nToMode, nToA, nToB, nToC ) ) + nBits |= 0x14; + } + rStrm .WriteUInt32( nBits ) + .WriteInt32( nByMode ).WriteInt32( nByA ).WriteInt32( nByB ).WriteInt32( nByC ) + .WriteInt32( nFromMode ).WriteInt32( nFromA ).WriteInt32( nFromB ).WriteInt32( nFromC ) + .WriteInt32( nToMode ).WriteInt32( nToA ).WriteInt32( nToB ).WriteInt32( nToC ); + } + exportAnimateTarget( rStrm, xNode, 0, nAfterEffectType ); +} + +void AnimationExporter::exportIterate( SvStream& rStrm, const Reference< XAnimationNode >& xNode ) +{ + Reference< XIterateContainer > xIterate( xNode, UNO_QUERY ); + if ( !xIterate.is() ) + return; + + EscherExAtom aAnimIteration( rStrm, DFF_msofbtAnimIteration ); + + float fInterval = 10.0; + sal_Int32 nTextUnitEffect = 0; + sal_Int32 const nU1 = 1; + sal_Int32 const nU2 = 1; + sal_Int32 const nU3 = 0xe; + + sal_Int16 nIterateType = xIterate->getIterateType(); + switch( nIterateType ) + { + case TextAnimationType::BY_WORD : nTextUnitEffect = 1; break; + case TextAnimationType::BY_LETTER : nTextUnitEffect = 2; break; + } + + fInterval = static_cast(xIterate->getIterateInterval()); + + // convert interval from absolute to percentage + double fDuration = 0.0; + + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xChildNode.is() ) + { + double fChildBegin = 0.0; + double fChildDuration = 0.0; + xChildNode->getBegin() >>= fChildBegin; + xChildNode->getDuration() >>= fChildDuration; + + fChildDuration += fChildBegin; + if( fChildDuration > fDuration ) + fDuration = fChildDuration; + } + } + } + } + + if( fDuration ) + fInterval = static_cast(100.0 * fInterval / fDuration); + + rStrm.WriteFloat( fInterval ).WriteInt32( nTextUnitEffect ).WriteInt32( nU1 ).WriteInt32( nU2 ).WriteInt32( nU3 ); + aTarget = xIterate->getTarget(); +} + +} // namespace ppt; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptexanimations.hxx b/sd/source/filter/eppt/pptexanimations.hxx new file mode 100644 index 000000000..daa54d85b --- /dev/null +++ b/sd/source/filter/eppt/pptexanimations.hxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#ifdef DBG_ANIM_LOG +#include +#endif + +#include +#include +#include + +#include +#include + +namespace com::sun::star::animations { class XAnimate; } +namespace com::sun::star::animations { class XAnimationNode; } +namespace com::sun::star::beans { struct NamedValue; } +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XShape; } +namespace ppt { class ExSoundCollection; } + +class SvStream; +class EscherSolverContainer; + +namespace ppt +{ + + struct AfterEffectNode + { + css::uno::Reference< css::animations::XAnimationNode > mxNode; + css::uno::Reference< css::animations::XAnimationNode > mxMaster; + + AfterEffectNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode, + const css::uno::Reference< css::animations::XAnimationNode >& xMaster ) + : mxNode( xNode ), mxMaster( xMaster ) {} + }; + + typedef std::shared_ptr< AfterEffectNode > AfterEffectNodePtr; + +typedef sal_uInt32 TranslateMode; +#define TRANSLATE_NONE 0 +#define TRANSLATE_VALUE 1 +#define TRANSLATE_ATTRIBUTE 2 +#define TRANSLATE_MEASURE 4 +#define TRANSLATE_NUMBER_TO_STRING 8 + +const int AFTEREFFECT_NONE = 0; +const int AFTEREFFECT_COLOR = 1; +const int AFTEREFFECT_SET = 2; + +class AnimationExporter +{ + css::uno::Any aTarget; + const EscherSolverContainer& mrSolverContainer; + ppt::ExSoundCollection& mrExSoundCollection; + std::vector< AfterEffectNodePtr > maAfterEffectNodes; + sal_Int32 mnCurrentGroup; + + static void writeZString( SvStream& rStrm, const OUString& rVal ); + static bool getColorAny( const css::uno::Any& rAny, const sal_Int16 nColorSpace, sal_Int32& rMode, sal_Int32& rA, sal_Int32& rB, sal_Int32& rC ); + static bool exportAnimProperty( SvStream& rStrm, const sal_uInt16 nPropertyId, const css::uno::Any& rAny, const TranslateMode eTranslateMode ); + static void exportAnimPropertyString( SvStream& rStrm, const sal_uInt16 nPropertyId, const OUString& rVal, const TranslateMode eTranslateMode ); + static void exportAnimPropertyFloat( SvStream& rStrm, const sal_uInt16 nPropertyId, const double& rVal ); + static void exportAnimPropertyuInt32( SvStream& rStrm, const sal_uInt16 nPropertyId, const sal_uInt32 nVal ); + static void exportAnimPropertyByte( SvStream& rStrm, const sal_uInt16 nPropertyId, const sal_uInt8 nVal ); + + /** if available exportAnimPropertySet + @return the css::presentation::EffectNodeType*/ + static sal_Int16 exportAnimPropertySet( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + static void exportAnimNode( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, + const sal_Int16 nFillDefault ); + void exportAnimate( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimateTarget( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, const sal_uInt32 nForceAttributeName = 0, int nAfterEffectType = AFTEREFFECT_NONE ); + void exportAnimateSet( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, int nAfterEffectType ); + static void exportAnimAction( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimEvent( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, const sal_Int32 nFlags = 0 ); + void exportNode( SvStream& rStrm, css::uno::Reference< css::animations::XAnimationNode > const & xNode, + const sal_uInt16 nContainerRecType, const sal_uInt16 nInstance, const sal_Int32 nGroupLevel, const bool bTakeBackInteractiveSequenceTiming, + const sal_Int16 nFillDefault ); + void exportAnimateTargetElement( SvStream& rStrm, const css::uno::Any& rAny, const bool bCreate2b01Atom ); + static void exportAnimateKeyPoints( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimate >& xAnimate ); + static void exportAnimValue( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, const bool bExportAlways ); + void exportTransitionFilter( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimateMotion( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimateTransform( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void exportAnimateColor( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode, int nAfterEffectType ); + void exportIterate( SvStream& rStrm, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + void processAfterEffectNodes( const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + bool isAfterEffectNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const; + bool hasAfterEffectNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode, css::uno::Reference< css::animations::XAnimationNode >& xAfterEffectNode ) const; + bool isEmptyNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode ) const; + + static css::uno::Reference< css::animations::XAnimationNode > createAfterEffectNodeClone( const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + +public: + AnimationExporter( const EscherSolverContainer& rSolverContainer, ppt::ExSoundCollection& rExSoundCollection ); + + void doexport( const css::uno::Reference< css::drawing::XDrawPage >& xPage, SvStream& rStrm ); + + // helper methods also used in ooxml export + static css::uno::Any convertAnimateValue( const css::uno::Any& rSource, std::u16string_view rAttributeName ); + static bool GetNodeType( const css::uno::Reference< css::animations::XAnimationNode >& xNode, sal_Int16& nType ); + static sal_Int16 GetFillMode( const css::uno::Reference< css::animations::XAnimationNode >& xNode, const sal_Int16 nFillDefault ); + static void GetUserData( const css::uno::Sequence< css::beans::NamedValue >& rUserData, const css::uno::Any ** pAny, std::size_t nLen ); + static sal_uInt32 TranslatePresetSubType( const sal_uInt32 nPresetClass, const sal_uInt32 nPresetId, std::u16string_view rPresetSubType ); + static sal_uInt32 GetPresetID( const OUString& rPreset, sal_uInt32 nAPIPresetClass, bool& bPresetId ); + static sal_uInt32 GetValueTypeForAttributeName( const OUString& rAttributeName ); + + static const char* FindTransitionName( const sal_Int16 nType, const sal_Int16 nSubType, const bool bDirection ); + static css::uno::Reference< css::drawing::XShape > getTargetElementShape( const css::uno::Any& rAny, sal_Int32& rBegin, sal_Int32& rEnd, bool& rParagraphTarget ); +}; +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptexsoundcollection.cxx b/sd/source/filter/eppt/pptexsoundcollection.cxx new file mode 100644 index 000000000..c4770e644 --- /dev/null +++ b/sd/source/filter/eppt/pptexsoundcollection.cxx @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "pptexsoundcollection.hxx" +#include "epptdef.hxx" +#include +#include +#include +#include +#include + +namespace ppt +{ + +ExSoundEntry::ExSoundEntry(const OUString& rString) + : nFileSize(0) + , aSoundURL(rString) +{ + try + { + ::ucbhelper::Content aCnt( aSoundURL, + css::uno::Reference< css::ucb::XCommandEnvironment >(), + comphelper::getProcessComponentContext() ); + sal_Int64 nVal = 0; + aCnt.getPropertyValue("Size") >>= nVal; + nFileSize = static_cast(nVal); + } + catch( css::uno::Exception& ) + { + + } +}; + +OUString ExSoundEntry::ImplGetName() const +{ + INetURLObject aTmp( aSoundURL ); + return aTmp.GetLastName(); +} + +OUString ExSoundEntry::ImplGetExtension() const +{ + INetURLObject aTmp( aSoundURL ); + OUString aExtension(aTmp.GetFileExtension()); + if ( !aExtension.isEmpty() ) + { + aExtension = "." + aExtension; + } + return aExtension; +} + +bool ExSoundEntry::IsSameURL(std::u16string_view rURL) const +{ + return ( rURL == aSoundURL ); +} + +sal_uInt32 ExSoundEntry::GetSize( sal_uInt32 nId ) const +{ + OUString aName( ImplGetName() ); + OUString aExtension( ImplGetExtension() ); + + sal_uInt32 nSize = 8; // SoundContainer Header + if ( !aName.isEmpty() ) // String Atom ( instance 0 - name of sound ) + nSize += aName.getLength() * 2 + 8; + if ( !aExtension.isEmpty() ) // String Atom ( instance 1 - extension of sound ) + nSize += aExtension.getLength() * 2 + 8; + + OUString aId( OUString::number(nId) ); // String Atom ( instance 2 - reference id ) + nSize += 2 * aId.getLength() + 8; + + nSize += nFileSize + 8; // SoundData Atom + + return nSize; +} + +void ExSoundEntry::Write( SvStream& rSt, sal_uInt32 nId ) const +{ + try + { + ::ucbhelper::Content aLoadContentIfExists( aSoundURL, + css::uno::Reference< css::ucb::XCommandEnvironment >(), + comphelper::getProcessComponentContext() ); + + // create SoundContainer + rSt.WriteUInt32( ( EPP_Sound << 16 ) | 0xf ).WriteUInt32( GetSize( nId ) - 8 ); + + OUString aSoundName( ImplGetName() ); + sal_Int32 i, nSoundNameLen = aSoundName.getLength(); + if ( nSoundNameLen ) + { + // name of sound ( instance 0 ) + rSt.WriteUInt32( EPP_CString << 16 ).WriteUInt32( nSoundNameLen * 2 ); + for ( i = 0; i < nSoundNameLen; ++i ) + rSt.WriteUInt16( aSoundName[i] ); + } + OUString aExtension( ImplGetExtension() ); + sal_Int32 nExtensionLen = aExtension.getLength(); + if ( nExtensionLen ) + { + // extension of sound ( instance 1 ) + rSt.WriteUInt32( ( EPP_CString << 16 ) | 16 ).WriteUInt32( nExtensionLen * 2 ); + for ( i = 0; i < nExtensionLen; ++i ) + rSt.WriteUInt16( aExtension[i] ); + } + // id of sound ( instance 2 ) + OUString aId( OUString::number(nId ) ); + sal_Int32 nIdLen = aId.getLength(); + rSt.WriteUInt32( ( EPP_CString << 16 ) | 32 ).WriteUInt32( nIdLen * 2 ); + for ( i = 0; i < nIdLen; ++i ) + rSt.WriteUInt16( aId[i] ); + + rSt.WriteUInt32( EPP_SoundData << 16 ).WriteUInt32( nFileSize ); + sal_uInt32 nBytesLeft = nFileSize; + std::unique_ptr pSourceFile = ::utl::UcbStreamHelper::CreateStream( aSoundURL, StreamMode::READ ); + if ( pSourceFile ) + { + std::unique_ptr pBuf( new sal_uInt8[ 0x10000 ] ); // 64 kB Buffer + while ( nBytesLeft ) + { + sal_uInt32 nToDo = std::min( nBytesLeft, 0x10000 ); + pSourceFile->ReadBytes(pBuf.get(), nToDo); + rSt.WriteBytes(pBuf.get(), nToDo); + nBytesLeft -= nToDo; + } + } + } + catch( css::uno::Exception& ) + { + + } +} + +sal_uInt32 ExSoundCollection::GetId(const OUString& rString) +{ + sal_uInt32 nSoundId = 0; + if (!rString.isEmpty()) + { + const sal_uInt32 nSoundCount = maEntries.size(); + + auto iter = std::find_if(maEntries.begin(), maEntries.end(), + [&rString](const ExSoundEntry& rEntry) { return rEntry.IsSameURL(rString); }); + nSoundId = static_cast(std::distance(maEntries.begin(), iter)); + + if ( nSoundId++ == nSoundCount ) + { + ExSoundEntry aEntry( rString ); + if ( aEntry.GetFileSize() ) + maEntries.push_back(aEntry); + else + { + nSoundId = 0; // only insert sounds that are accessible + } + } + } + return nSoundId; +} + +sal_uInt32 ExSoundCollection::GetSize() const +{ + sal_uInt32 nSize = 0; + if (!maEntries.empty()) + { + nSize += 8 + 12; // size of SoundCollectionContainerHeader + SoundCollAtom + sal_uInt32 i = 1; + for ( const auto& rEntry : maEntries ) + { + nSize += rEntry.GetSize(i); + ++i; + } + } + return nSize; +} + +void ExSoundCollection::Write( SvStream& rSt ) const +{ + if (maEntries.empty()) + return; + + sal_uInt32 i = 1; + sal_uInt32 nSoundCount = maEntries.size(); + + // create SoundCollection Container + rSt.WriteUInt16( 0xf ).WriteUInt16( EPP_SoundCollection ).WriteUInt32( GetSize() - 8 ); + + // create SoundCollAtom ( reference to the next free SoundId ); + rSt.WriteUInt32( EPP_SoundCollAtom << 16 ).WriteUInt32( 4 ).WriteUInt32( nSoundCount ); + + for ( const auto& rEntry : maEntries ) + { + rEntry.Write(rSt,i); + ++i; + } +} + +} // namespace ppt; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptexsoundcollection.hxx b/sd/source/filter/eppt/pptexsoundcollection.hxx new file mode 100644 index 000000000..d81bb8118 --- /dev/null +++ b/sd/source/filter/eppt/pptexsoundcollection.hxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#ifdef DBG_ANIM_LOG +#include +#endif +#include + +class SvStream; + +namespace ppt +{ + +class ExSoundEntry +{ + sal_uInt32 nFileSize; + OUString aSoundURL; + + OUString ImplGetName() const; + OUString ImplGetExtension() const; + + public: + + bool IsSameURL(std::u16string_view rURL) const; + sal_uInt32 GetFileSize( ) const { return nFileSize; }; + + ExSoundEntry(const OUString& rSoundURL); + + /// @return size of a complete SoundContainer. + sal_uInt32 GetSize( sal_uInt32 nId ) const; + void Write( SvStream& rSt, sal_uInt32 nId ) const; +}; + +class ExSoundCollection +{ + public: + + sal_uInt32 GetId(const OUString&); + + /// @return size of a complete SoundCollectionContainer. + sal_uInt32 GetSize() const; + void Write( SvStream& rSt ) const; + +private: + + std::vector maEntries; +}; + +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-animations.cxx b/sd/source/filter/eppt/pptx-animations.cxx new file mode 100644 index 000000000..1c901573c --- /dev/null +++ b/sd/source/filter/eppt/pptx-animations.cxx @@ -0,0 +1,1539 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include "epptooxml.hxx" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pptexanimations.hxx" +#include "pptx-animations.hxx" +#include "../ppt/pptanimations.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::uno; +using namespace ::ppt; +using namespace oox::drawingml; +using namespace oox::core; +using namespace oox; + +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::text::XSimpleText; +using ::sax_fastparser::FSHelperPtr; + +namespace +{ +void WriteAnimationProperty(const FSHelperPtr& pFS, const Any& rAny, sal_Int32 nToken = 0) +{ + if (!rAny.hasValue()) + return; + + ValuePair aPair; + + if (rAny >>= aPair) + { + double x, y; + if ((aPair.First >>= x) && (aPair.Second >>= y)) + { + if (nToken == XML_by) + { + // MS needs ending values but we have offset values. + x += 1.0; + y += 1.0; + } + pFS->singleElementNS(XML_p, nToken, XML_x, OString::number(x * 100000), XML_y, + OString::number(y * 100000)); + } + return; + } + + sal_Int32 nRgb = {}; // spurious -Werror=maybe-uninitialized + double fDouble = {}; // spurious -Werror=maybe-uninitialized + + TypeClass aClass = rAny.getValueType().getTypeClass(); + bool bWriteToken + = nToken + && (aClass == TypeClass_LONG || aClass == TypeClass_DOUBLE || aClass == TypeClass_STRING); + + if (bWriteToken) + pFS->startElementNS(XML_p, nToken); + + switch (rAny.getValueType().getTypeClass()) + { + case TypeClass_LONG: + if (!(rAny >>= nRgb)) + { + assert(false); + } + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nRgb)); + break; + case TypeClass_DOUBLE: + if (!(rAny >>= fDouble)) + { + assert(false); + } + pFS->singleElementNS(XML_p, XML_fltVal, XML_val, OString::number(fDouble)); + break; + case TypeClass_STRING: + pFS->singleElementNS(XML_p, XML_strVal, XML_val, *o3tl::doAccess(rAny)); + break; + default: + break; + } + + if (bWriteToken) + pFS->endElementNS(XML_p, nToken); +} + +void WriteAnimateColorColor(const FSHelperPtr& pFS, const Any& rAny, sal_Int32 nToken) +{ + if (!rAny.hasValue()) + return; + + sal_Int32 nColor = 0; + if (rAny >>= nColor) + { + pFS->startElementNS(XML_p, nToken); + + if (nToken == XML_by) + { + // CT_TLByRgbColorTransform + SAL_WARN("sd.eppt", "Export p:rgb in p:by of animClr isn't implemented yet."); + } + else + { + // CT_Color + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor)); + } + + pFS->endElementNS(XML_p, nToken); + } + + Sequence aHSL(3); + if (!(rAny >>= aHSL)) + return; + + pFS->startElementNS(XML_p, nToken); + + if (nToken == XML_by) + { + // CT_TLByHslColorTransform + pFS->singleElementNS(XML_p, XML_hsl, XML_h, OString::number(aHSL[0] * 60000), // ST_Angel + XML_s, OString::number(aHSL[1] * 100000), XML_l, + OString::number(aHSL[2] * 100000)); + } + else + { + // CT_Color + SAL_WARN("sd.eppt", "Export p:hsl in p:from or p:to of animClr isn't implemented yet."); + } + + pFS->endElementNS(XML_p, nToken); +} + +void WriteAnimateTo(const FSHelperPtr& pFS, const Any& rValue, const OUString& rAttributeName) +{ + if (!rValue.hasValue()) + return; + + SAL_INFO("sd.eppt", "to attribute name: " << rAttributeName.toUtf8()); + + WriteAnimationProperty(pFS, AnimationExporter::convertAnimateValue(rValue, rAttributeName), + XML_to); +} + +void WriteAnimateValues(const FSHelperPtr& pFS, const Reference& rXAnimate) +{ + const Sequence aKeyTimes = rXAnimate->getKeyTimes(); + if (!aKeyTimes.hasElements()) + return; + const Sequence aValues = rXAnimate->getValues(); + const OUString& sFormula = rXAnimate->getFormula(); + const OUString& rAttributeName = rXAnimate->getAttributeName(); + + SAL_INFO("sd.eppt", "animate values, formula: " << sFormula.toUtf8()); + + assert(aValues.getLength() == aKeyTimes.getLength()); + + pFS->startElementNS(XML_p, XML_tavLst); + + for (int i = 0; i < aKeyTimes.getLength(); i++) + { + SAL_INFO("sd.eppt", "animate value " << i << ": " << aKeyTimes[i]); + if (aValues[i].hasValue()) + { + pFS->startElementNS(XML_p, XML_tav, XML_fmla, + sax_fastparser::UseIf(sFormula, !sFormula.isEmpty()), XML_tm, + OString::number(static_cast(aKeyTimes[i] * 100000.0))); + pFS->startElementNS(XML_p, XML_val); + ValuePair aPair; + if (aValues[i] >>= aPair) + { + WriteAnimationProperty( + pFS, AnimationExporter::convertAnimateValue(aPair.First, rAttributeName)); + WriteAnimationProperty( + pFS, AnimationExporter::convertAnimateValue(aPair.Second, rAttributeName)); + } + else + WriteAnimationProperty( + pFS, AnimationExporter::convertAnimateValue(aValues[i], rAttributeName)); + + pFS->endElementNS(XML_p, XML_val); + pFS->endElementNS(XML_p, XML_tav); + } + } + + pFS->endElementNS(XML_p, XML_tavLst); +} + +// Write condition list ( either prevCondlst or nextCondlst ) of Seq. +void WriteAnimationCondListForSeq(const FSHelperPtr& pFS, sal_Int32 nToken) +{ + const char* pEvent = (nToken == XML_prevCondLst) ? "onPrev" : "onNext"; + + pFS->startElementNS(XML_p, nToken); + pFS->startElementNS(XML_p, XML_cond, XML_evt, pEvent); + pFS->startElementNS(XML_p, XML_tgtEl); + pFS->singleElementNS(XML_p, XML_sldTgt); + pFS->endElementNS(XML_p, XML_tgtEl); + pFS->endElementNS(XML_p, XML_cond); + pFS->endElementNS(XML_p, nToken); +} + +const char* convertEventTrigger(sal_Int16 nTrigger) +{ + const char* pEvent = nullptr; + switch (nTrigger) + { + case EventTrigger::ON_NEXT: + pEvent = "onNext"; + break; + case EventTrigger::ON_PREV: + pEvent = "onPrev"; + break; + case EventTrigger::BEGIN_EVENT: + pEvent = "begin"; + break; + case EventTrigger::END_EVENT: + pEvent = "end"; + break; + case EventTrigger::ON_BEGIN: + pEvent = "onBegin"; + break; + case EventTrigger::ON_END: + pEvent = "onEnd"; + break; + case EventTrigger::ON_CLICK: + pEvent = "onClick"; + break; + case EventTrigger::ON_DBL_CLICK: + pEvent = "onDblClick"; + break; + case EventTrigger::ON_STOP_AUDIO: + pEvent = "onStopAudio"; + break; + case EventTrigger::ON_MOUSE_ENTER: + pEvent = "onMouseOver"; // not exact? + break; + case EventTrigger::ON_MOUSE_LEAVE: + pEvent = "onMouseOut"; + break; + } + return pEvent; +} + +void WriteAnimationAttributeName(const FSHelperPtr& pFS, const OUString& rAttributeName) +{ + if (rAttributeName.isEmpty()) + return; + + pFS->startElementNS(XML_p, XML_attrNameLst); + + SAL_INFO("sd.eppt", "write attribute name: " << rAttributeName.toUtf8()); + + if (rAttributeName == "X;Y") + { + pFS->startElementNS(XML_p, XML_attrName); + pFS->writeEscaped("ppt_x"); + pFS->endElementNS(XML_p, XML_attrName); + + pFS->startElementNS(XML_p, XML_attrName); + pFS->writeEscaped("ppt_y"); + pFS->endElementNS(XML_p, XML_attrName); + } + else + { + const oox::ppt::ImplAttributeNameConversion* attrConv + = oox::ppt::getAttributeConversionList(); + const char* pAttribute = nullptr; + + while (attrConv->mpAPIName != nullptr) + { + if (rAttributeName.equalsAscii(attrConv->mpAPIName)) + { + pAttribute = attrConv->mpMSName; + break; + } + attrConv++; + } + + if (pAttribute) + { + pFS->startElementNS(XML_p, XML_attrName); + pFS->writeEscaped(pAttribute); + pFS->endElementNS(XML_p, XML_attrName); + } + else + { + SAL_WARN("sd.eppt", "unhandled animation attribute name: " << rAttributeName); + } + } + + pFS->endElementNS(XML_p, XML_attrNameLst); +} + +bool isValidTarget(const Any& rTarget) +{ + Reference xShape; + + if ((rTarget >>= xShape) && xShape.is()) + return true; + + ParagraphTarget aParagraphTarget; + + return (rTarget >>= aParagraphTarget) && aParagraphTarget.Shape.is(); +} + +/// extract ooxml node type from a XAnimationNode. +sal_Int32 extractNodeType(const Reference& rXNode) +{ + sal_Int16 nType = rXNode->getType(); + sal_Int32 xmlNodeType = -1; + switch (nType) + { + case AnimationNodeType::ITERATE: + case AnimationNodeType::PAR: + xmlNodeType = XML_par; + break; + case AnimationNodeType::SEQ: + xmlNodeType = XML_seq; + break; + case AnimationNodeType::ANIMATE: + xmlNodeType = XML_anim; + break; + case AnimationNodeType::ANIMATEMOTION: + xmlNodeType = XML_animMotion; + break; + case AnimationNodeType::ANIMATETRANSFORM: + { + Reference xTransform(rXNode, UNO_QUERY); + if (xTransform.is()) + { + if (xTransform->getTransformType() == AnimationTransformType::SCALE) + xmlNodeType = XML_animScale; + else if (xTransform->getTransformType() == AnimationTransformType::ROTATE) + xmlNodeType = XML_animRot; + } + break; + } + case AnimationNodeType::ANIMATECOLOR: + xmlNodeType = XML_animClr; + break; + case AnimationNodeType::SET: + xmlNodeType = XML_set; + break; + case AnimationNodeType::TRANSITIONFILTER: + xmlNodeType = XML_animEffect; + break; + case AnimationNodeType::COMMAND: + xmlNodeType = XML_cmd; + break; + case AnimationNodeType::AUDIO: + xmlNodeType = XML_audio; + break; + default: + SAL_WARN("sd.eppt", "unhandled animation node: " << nType); + break; + } + return xmlNodeType; +} + +/// Convert AnimationRestart to ST_TLTimeNodeRestartType value. +const char* convertAnimationRestart(sal_Int16 nRestart) +{ + const char* pRestart = nullptr; + switch (nRestart) + { + case AnimationRestart::ALWAYS: + pRestart = "always"; + break; + case AnimationRestart::WHEN_NOT_ACTIVE: + pRestart = "whenNotActive"; + break; + case AnimationRestart::NEVER: + pRestart = "never"; + break; + } + return pRestart; +} + +/// Convert EffectNodeType to ST_TLTimeNodeType +const char* convertEffectNodeType(sal_Int16 nType) +{ + const char* pNodeType = nullptr; + switch (nType) + { + case EffectNodeType::TIMING_ROOT: + pNodeType = "tmRoot"; + break; + case EffectNodeType::MAIN_SEQUENCE: + pNodeType = "mainSeq"; + break; + case EffectNodeType::ON_CLICK: + pNodeType = "clickEffect"; + break; + case EffectNodeType::AFTER_PREVIOUS: + pNodeType = "afterEffect"; + break; + case EffectNodeType::WITH_PREVIOUS: + pNodeType = "withEffect"; + break; + case EffectNodeType::INTERACTIVE_SEQUENCE: + pNodeType = "interactiveSeq"; + break; + } + return pNodeType; +} + +/// Convert EffectPresetClass to ST_TLTimeNodePresetClassType +const char* convertEffectPresetClass(sal_Int16 nPresetClass) +{ + const char* pPresetClass = nullptr; + switch (nPresetClass) + { + case EffectPresetClass::ENTRANCE: + pPresetClass = "entr"; + break; + case EffectPresetClass::EXIT: + pPresetClass = "exit"; + break; + case EffectPresetClass::EMPHASIS: + pPresetClass = "emph"; + break; + case EffectPresetClass::MOTIONPATH: + pPresetClass = "path"; + break; + case EffectPresetClass::OLEACTION: + pPresetClass = "verb"; // ? + break; + case EffectPresetClass::MEDIACALL: + pPresetClass = "mediacall"; + break; + } + return pPresetClass; +} + +/// convert AnimationFill to ST_TLTimeNodeFillType. +const char* convertAnimationFill(sal_Int16 nFill) +{ + const char* pFill = nullptr; + switch (nFill) + { + case AnimationFill::FREEZE: + pFill = "hold"; + break; + case AnimationFill::HOLD: + pFill = "hold"; + break; + case AnimationFill::REMOVE: + pFill = "remove"; + break; + case AnimationFill::TRANSITION: + pFill = "transition"; + break; + } + return pFill; +} + +/// Convert TextAnimationType to ST_IterateType. +const char* convertTextAnimationType(sal_Int16 nType) +{ + const char* sType = nullptr; + switch (nType) + { + case TextAnimationType::BY_PARAGRAPH: + sType = "el"; + break; + case TextAnimationType::BY_LETTER: + sType = "lt"; + break; + case TextAnimationType::BY_WORD: + default: + sType = "wd"; + break; + } + return sType; +} + +class NodeContext; + +typedef std::unique_ptr NodeContextPtr; + +class NodeContext +{ + const Reference mxNode; + const bool mbMainSeqChild; + + std::vector maChildNodes; + // if the node has valid target or contains at least one valid target. + bool mbValid; + + // Attributes initialized from mxNode->getUserData(). + sal_Int16 mnEffectNodeType; + sal_Int16 mnEffectPresetClass; + OUString msEffectPresetId; + OUString msEffectPresetSubType; + + /// constructor helper for initializing user data. + void initUserData(); + + /// constructor helper to initialize maChildNodes. + /// return true if at least one childnode is valid. + bool initChildNodes(); + + /// constructor helper to initialize mbValid + void initValid(bool bHasValidChild, bool bIsIterateChild); + +public: + NodeContext(const Reference& xNode, bool bMainSeqChild, bool bIsIterateChild); + const Reference& getNode() const { return mxNode; } + bool isMainSeqChild() const { return mbMainSeqChild; } + sal_Int16 getEffectNodeType() const { return mnEffectNodeType; } + sal_Int16 getEffectPresetClass() const { return mnEffectPresetClass; } + const OUString& getEffectPresetId() const { return msEffectPresetId; } + const OUString& getEffectPresetSubType() const { return msEffectPresetSubType; } + bool isValid() const { return mbValid; } + const std::vector& getChildNodes() const { return maChildNodes; }; + Any getCondition(bool bBegin) const; +}; + +struct Cond +{ + OString msDelay; + const char* mpEvent; + Reference mxShape; + Reference mxNode; + + Cond(const Any& rAny, bool bIsMainSeqChild); + + bool isValid() const { return msDelay.getLength() || mpEvent; } + const char* getDelay() const { return msDelay.getLength() ? msDelay.getStr() : nullptr; } +}; + +Cond::Cond(const Any& rAny, bool bIsMainSeqChild) + : mpEvent(nullptr) +{ + bool bHasFDelay = false; + double fDelay = 0; + Timing eTiming; + Event aEvent; + + if (rAny >>= eTiming) + { + if (eTiming == Timing_INDEFINITE) + msDelay = "indefinite"; + } + else if (rAny >>= aEvent) + { + if (aEvent.Trigger == EventTrigger::ON_NEXT && bIsMainSeqChild) + msDelay = "indefinite"; + else + { + mpEvent = convertEventTrigger(aEvent.Trigger); + if (!(aEvent.Source >>= mxShape)) + aEvent.Source >>= mxNode; + + if (aEvent.Offset >>= fDelay) + bHasFDelay = true; + } + } + else if (rAny >>= fDelay) + bHasFDelay = true; + + if (bHasFDelay) + { + sal_Int32 nDelay = static_cast(fDelay * 1000.0); + msDelay = OString::number(nDelay); + } +} + +class PPTXAnimationExport +{ + void WriteAnimationNode(const NodeContextPtr& pContext); + void WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType); + void WriteAnimationNodeAnimateInside(bool bSimple, bool bWriteTo = true); + void WriteAnimationNodeSeq(); + void WriteAnimationNodeEffect(); + void WriteAnimationNodeCommand(); + void WriteAnimationNodeAudio(); + void WriteAnimationNodeCommonPropsStart(); + void WriteAnimationTarget(const Any& rTarget); + void WriteAnimationCondList(const Any& rAny, sal_Int32 nToken); + void WriteAnimationCond(const Cond& rCond); + bool isMainSeqChild() const; + const Reference& getCurrentNode() const; + + PowerPointExport& mrPowerPointExport; + const FSHelperPtr& mpFS; + const NodeContext* mpContext; + + std::map, sal_Int32> maAnimationNodeIdMap; + sal_Int32 GetNextAnimationNodeId(const Reference& rNode); + sal_Int32 GetAnimationNodeId(const Reference& rNode); + +public: + PPTXAnimationExport(PowerPointExport& rExport, const FSHelperPtr& pFS); + void WriteAnimations(const Reference& rXDrawPage); +}; + +/// Returns if rURL has an extension which is an audio format. +bool IsAudioURL(const OUString& rURL) +{ + return rURL.endsWithIgnoreAsciiCase(".wav") || rURL.endsWithIgnoreAsciiCase(".m4a"); +} + +/// Returns if rURL has an extension which is a video format. +bool IsVideoURL(const OUString& rURL) { return rURL.endsWithIgnoreAsciiCase(".mp4"); } +} + +namespace oox::core +{ +void WriteAnimations(const FSHelperPtr& pFS, const Reference& rXDrawPage, + PowerPointExport& rExport) +{ + PPTXAnimationExport aAnimationExport(rExport, pFS); + aAnimationExport.WriteAnimations(rXDrawPage); +} +} + +PPTXAnimationExport::PPTXAnimationExport(PowerPointExport& rExport, const FSHelperPtr& pFS) + : mrPowerPointExport(rExport) + , mpFS(pFS) + , mpContext(nullptr) +{ +} + +bool PPTXAnimationExport::isMainSeqChild() const +{ + assert(mpContext); + return mpContext->isMainSeqChild(); +} + +const Reference& PPTXAnimationExport::getCurrentNode() const +{ + assert(mpContext); + return mpContext->getNode(); +} + +void PPTXAnimationExport::WriteAnimationTarget(const Any& rTarget) +{ + sal_Int32 nParagraph = -1; + bool bParagraphTarget = false; + + Reference rXShape; + rTarget >>= rXShape; + + if (!rXShape.is()) + { + ParagraphTarget aParagraphTarget; + if (rTarget >>= aParagraphTarget) + rXShape = aParagraphTarget.Shape; + if (rXShape.is()) + { + nParagraph = static_cast(aParagraphTarget.Paragraph); + Reference xText(rXShape, UNO_QUERY); + if (xText.is()) + { + bParagraphTarget = true; + } + } + } + + if (!rXShape.is()) + return; + + sal_Int32 nShapeID = mrPowerPointExport.GetShapeID(rXShape); + + mpFS->startElementNS(XML_p, XML_tgtEl); + mpFS->startElementNS(XML_p, XML_spTgt, XML_spid, OString::number(nShapeID)); + if (bParagraphTarget) + { + mpFS->startElementNS(XML_p, XML_txEl); + mpFS->singleElementNS(XML_p, XML_pRg, XML_st, OString::number(nParagraph), XML_end, + OString::number(nParagraph)); + mpFS->endElementNS(XML_p, XML_txEl); + } + mpFS->endElementNS(XML_p, XML_spTgt); + mpFS->endElementNS(XML_p, XML_tgtEl); +} + +void PPTXAnimationExport::WriteAnimationCondList(const Any& rAny, sal_Int32 nToken) +{ + if (!rAny.hasValue()) + return; + + std::vector aList; + + bool bIsMainSeqChild = isMainSeqChild(); + + Sequence aCondSeq; + if (rAny >>= aCondSeq) + { + for (const auto& rCond : std::as_const(aCondSeq)) + { + Cond aCond(rCond, bIsMainSeqChild); + if (aCond.isValid()) + aList.push_back(aCond); + } + } + else + { + Cond aCond(rAny, bIsMainSeqChild); + if (aCond.isValid()) + aList.push_back(aCond); + } + + if (aList.size() > 0) + { + mpFS->startElementNS(XML_p, nToken); + + for (const Cond& rCond : aList) + WriteAnimationCond(rCond); + + mpFS->endElementNS(XML_p, nToken); + } +} + +void PPTXAnimationExport::WriteAnimationCond(const Cond& rCond) +{ + if (rCond.mpEvent) + { + sal_Int32 nId = -1; + if (rCond.mxShape.is()) + { + mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt, + rCond.mpEvent); + WriteAnimationTarget(Any(rCond.mxShape)); + mpFS->endElementNS(XML_p, XML_cond); + } + else if (rCond.mxNode.is() && (nId = GetAnimationNodeId(rCond.mxNode)) != -1) + { + mpFS->startElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt, + rCond.mpEvent); + mpFS->singleElementNS(XML_p, XML_tn, XML_val, OString::number(nId)); + mpFS->endElementNS(XML_p, XML_cond); + } + else + { + mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay(), XML_evt, + rCond.mpEvent); + } + } + else + mpFS->singleElementNS(XML_p, XML_cond, XML_delay, rCond.getDelay()); +} + +void PPTXAnimationExport::WriteAnimationNodeAnimate(sal_Int32 nXmlNodeType) +{ + const Reference& rXNode = getCurrentNode(); + Reference rXAnimate(rXNode, UNO_QUERY); + if (!rXAnimate.is()) + return; + + const char* pCalcMode = nullptr; + const char* pValueType = nullptr; + bool bSimple = (nXmlNodeType != XML_anim); + bool bTo = true; + + if (!bSimple) + { + switch (rXAnimate->getCalcMode()) + { + case AnimationCalcMode::DISCRETE: + pCalcMode = "discrete"; + break; + case AnimationCalcMode::LINEAR: + pCalcMode = "lin"; + break; + } + + switch (AnimationExporter::GetValueTypeForAttributeName(rXAnimate->getAttributeName())) + { + case AnimationValueType::STRING: + pValueType = "str"; + break; + case AnimationValueType::NUMBER: + pValueType = "num"; + break; + case AnimationValueType::COLOR: + pValueType = "clr"; + break; + } + } + + if (nXmlNodeType == XML_animMotion) + { + OUString aPath; + Reference xMotion(rXNode, UNO_QUERY); + if (xMotion.is()) + { + xMotion->getPath() >>= aPath; + ::basegfx::B2DPolyPolygon aPolyPoly; + if (::basegfx::utils::importFromSvgD(aPolyPoly, aPath, true, nullptr)) + aPath = ::basegfx::utils::exportToSvgD(aPolyPoly, false, false, true, true); + } + + mpFS->startElementNS(XML_p, nXmlNodeType, XML_origin, "layout", XML_path, aPath); + } + else if (nXmlNodeType == XML_animRot) + { + // when const char* is nullptr, the attribute is completely omitted in the output + const char* pBy = nullptr; + const char* pFrom = nullptr; + const char* pTo = nullptr; + OString aBy, aFrom, aTo; + + Reference xTransform(rXNode, UNO_QUERY); + if (xTransform.is()) + { + double value; + if (xTransform->getBy() >>= value) + { + aBy = OString::number(static_cast(value * PER_DEGREE)); + pBy = aBy.getStr(); + } + + if (xTransform->getFrom() >>= value) + { + aFrom = OString::number(static_cast(value * PER_DEGREE)); + pFrom = aFrom.getStr(); + } + + if (xTransform->getTo() >>= value) + { + aTo = OString::number(static_cast(value * PER_DEGREE)); + pTo = aTo.getStr(); + } + } + + mpFS->startElementNS(XML_p, nXmlNodeType, XML_by, pBy, XML_from, pFrom, XML_to, pTo); + } + else if (nXmlNodeType == XML_animClr) + { + Reference xColor(rXNode, UNO_QUERY); + const char* pColorSpace = "rgb"; + const char* pDirection = nullptr; + if (xColor.is() && xColor->getColorInterpolation() == AnimationColorSpace::HSL) + { + // Note: from, to, by can still be specified in any supported format. + pColorSpace = "hsl"; + pDirection = xColor->getDirection() ? "cw" : "ccw"; + } + mpFS->startElementNS(XML_p, nXmlNodeType, XML_clrSpc, pColorSpace, XML_dir, pDirection, + XML_calcmode, pCalcMode, XML_valueType, pValueType); + } + else + { + OUString sFrom, sTo, sBy; + if (rXAnimate.is() && nXmlNodeType == XML_anim) + { + OUString sAttributeName = rXAnimate->getAttributeName(); + Any aFrom + = AnimationExporter::convertAnimateValue(rXAnimate->getFrom(), sAttributeName); + aFrom >>= sFrom; + Any aTo = AnimationExporter::convertAnimateValue(rXAnimate->getTo(), sAttributeName); + aTo >>= sTo; + Any aBy = AnimationExporter::convertAnimateValue(rXAnimate->getBy(), sAttributeName); + aBy >>= sBy; + } + + mpFS->startElementNS(XML_p, nXmlNodeType, XML_calcmode, pCalcMode, XML_valueType, + pValueType, XML_from, sax_fastparser::UseIf(sFrom, !sFrom.isEmpty()), + XML_to, sax_fastparser::UseIf(sTo, !sTo.isEmpty()), XML_by, + sax_fastparser::UseIf(sBy, !sBy.isEmpty())); + bTo = sTo.isEmpty() && sFrom.isEmpty() && sBy.isEmpty(); + } + + WriteAnimationNodeAnimateInside(bSimple, bTo); + mpFS->endElementNS(XML_p, nXmlNodeType); +} + +void PPTXAnimationExport::WriteAnimationNodeAnimateInside(bool bSimple, bool bWriteTo) +{ + const Reference& rXNode = getCurrentNode(); + Reference rXAnimate(rXNode, UNO_QUERY); + if (!rXAnimate.is()) + return; + + const char* pAdditive = nullptr; + + if (!bSimple) + { + switch (rXAnimate->getAdditive()) + { + case AnimationAdditiveMode::BASE: + pAdditive = "base"; + break; + case AnimationAdditiveMode::SUM: + pAdditive = "sum"; + break; + case AnimationAdditiveMode::REPLACE: + pAdditive = "repl"; + break; + case AnimationAdditiveMode::MULTIPLY: + pAdditive = "mult"; + break; + case AnimationAdditiveMode::NONE: + pAdditive = "none"; + break; + } + } + + mpFS->startElementNS(XML_p, XML_cBhvr, XML_additive, pAdditive); + WriteAnimationNodeCommonPropsStart(); + + Reference xIterate(rXNode->getParent(), UNO_QUERY); + WriteAnimationTarget(xIterate.is() ? xIterate->getTarget() : rXAnimate->getTarget()); + + Reference xTransform(rXNode, UNO_QUERY); + + // The attribute name of AnimateTransform is "Transform", we have to fix it. + OUString sNewAttr; + if (xTransform.is() && xTransform->getTransformType() == AnimationTransformType::ROTATE) + sNewAttr = "Rotate"; + + WriteAnimationAttributeName(mpFS, xTransform.is() ? sNewAttr : rXAnimate->getAttributeName()); + + mpFS->endElementNS(XML_p, XML_cBhvr); + WriteAnimateValues(mpFS, rXAnimate); + + Reference xColor(rXNode, UNO_QUERY); + + if (xColor.is()) + { + WriteAnimateColorColor(mpFS, xColor->getBy(), XML_by); + WriteAnimateColorColor(mpFS, xColor->getFrom(), XML_from); + WriteAnimateColorColor(mpFS, xColor->getTo(), XML_to); + } + else if (xTransform.is() && xTransform->getTransformType() == AnimationTransformType::SCALE) + { + WriteAnimationProperty(mpFS, rXAnimate->getBy(), XML_by); + WriteAnimationProperty(mpFS, rXAnimate->getFrom(), XML_from); + WriteAnimationProperty(mpFS, rXAnimate->getTo(), XML_to); + } + else if (bWriteTo) + WriteAnimateTo(mpFS, rXAnimate->getTo(), rXAnimate->getAttributeName()); +} + +void PPTXAnimationExport::WriteAnimationNodeCommonPropsStart() +{ + const Reference& rXNode = getCurrentNode(); + std::optional sDuration; + std::optional sRepeatCount; + const char* pRestart = nullptr; + const char* pNodeType = nullptr; + const char* pPresetClass = nullptr; + const char* pFill = nullptr; + double fDuration = 0; + double fRepeatCount = 0; + Any aAny; + assert(mpContext); + + aAny = rXNode->getDuration(); + if (aAny.hasValue()) + { + Timing eTiming; + + if (aAny >>= eTiming) + { + if (eTiming == Timing_INDEFINITE) + sDuration = "indefinite"; + } + else + aAny >>= fDuration; + } + + pRestart = convertAnimationRestart(rXNode->getRestart()); + + sal_Int16 nType = mpContext->getEffectNodeType(); + if (nType != -1) + { + pNodeType = convertEffectNodeType(nType); + if (nType == EffectNodeType::TIMING_ROOT) + { + if (!sDuration) + sDuration = "indefinite"; + if (!pRestart) + pRestart = "never"; + } + else if (nType == EffectNodeType::MAIN_SEQUENCE) + { + sDuration = "indefinite"; + } + } + + if (fDuration != 0) + sDuration = OString::number(static_cast(fDuration * 1000.0)); + + sal_uInt32 nPresetClass = mpContext->getEffectPresetClass(); + if (nPresetClass != DFF_ANIM_PRESS_CLASS_USER_DEFINED) + pPresetClass = convertEffectPresetClass(nPresetClass); + + sal_uInt32 nPresetId = 0; + bool bPresetId = false; + const OUString& rPresetId = mpContext->getEffectPresetId(); + if (rPresetId.getLength() > 0) + { + nPresetId = AnimationExporter::GetPresetID(rPresetId, nPresetClass, bPresetId); + bPresetId = true; + } + + sal_uInt32 nPresetSubType = 0; + bool bPresetSubType = false; + const OUString& sPresetSubType = mpContext->getEffectPresetSubType(); + if (sPresetSubType.getLength() > 0) + { + nPresetSubType + = AnimationExporter::TranslatePresetSubType(nPresetClass, nPresetId, sPresetSubType); + bPresetSubType = true; + } + + if (nType != EffectNodeType::TIMING_ROOT && nType != EffectNodeType::MAIN_SEQUENCE) + { + // it doesn't seem to work right on root and mainseq nodes + sal_Int16 nFill = AnimationExporter::GetFillMode(rXNode, AnimationFill::AUTO); + pFill = convertAnimationFill(nFill); + } + + bool bAutoReverse = rXNode->getAutoReverse(); + + aAny = rXNode->getRepeatCount(); + if (aAny.hasValue()) + { + Timing eTiming; + + if (aAny >>= eTiming) + { + if (eTiming == Timing_INDEFINITE) + sRepeatCount = "indefinite"; + } + else + aAny >>= fRepeatCount; + } + + if (fRepeatCount != 0) + sRepeatCount = OString::number(static_cast(fRepeatCount * 1000.0)); + + mpFS->startElementNS( + XML_p, XML_cTn, XML_id, OString::number(GetNextAnimationNodeId(rXNode)), XML_dur, sDuration, + XML_autoRev, sax_fastparser::UseIf("1", bAutoReverse), XML_restart, pRestart, XML_nodeType, + pNodeType, XML_fill, pFill, XML_presetClass, pPresetClass, XML_presetID, + sax_fastparser::UseIf(OString::number(nPresetId), bPresetId), XML_presetSubtype, + sax_fastparser::UseIf(OString::number(nPresetSubType), bPresetSubType), XML_repeatCount, + sRepeatCount); + + WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst); + WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst); + + if (rXNode->getType() == AnimationNodeType::ITERATE) + { + Reference xIterate(rXNode, UNO_QUERY); + if (xIterate.is()) + { + const char* sType = convertTextAnimationType(xIterate->getIterateType()); + + mpFS->startElementNS(XML_p, XML_iterate, XML_type, sType); + mpFS->singleElementNS(XML_p, XML_tmAbs, XML_val, + OString::number(xIterate->getIterateInterval() * 1000)); + mpFS->endElementNS(XML_p, XML_iterate); + } + } + + const std::vector& aChildNodes = mpContext->getChildNodes(); + if (!aChildNodes.empty()) + { + mpFS->startElementNS(XML_p, XML_childTnLst); + for (const NodeContextPtr& pChildContext : aChildNodes) + { + if (pChildContext->isValid()) + WriteAnimationNode(pChildContext); + } + mpFS->endElementNS(XML_p, XML_childTnLst); + } + mpFS->endElementNS(XML_p, XML_cTn); +} + +void PPTXAnimationExport::WriteAnimationNodeSeq() +{ + SAL_INFO("sd.eppt", "write animation node SEQ"); + + mpFS->startElementNS(XML_p, XML_seq); + + WriteAnimationNodeCommonPropsStart(); + + WriteAnimationCondListForSeq(mpFS, XML_prevCondLst); + WriteAnimationCondListForSeq(mpFS, XML_nextCondLst); + + mpFS->endElementNS(XML_p, XML_seq); +} + +void PPTXAnimationExport::WriteAnimationNodeEffect() +{ + SAL_INFO("sd.eppt", "write animation node FILTER"); + Reference xFilter(getCurrentNode(), UNO_QUERY); + if (xFilter.is()) + { + const char* pFilter = ::ppt::AnimationExporter::FindTransitionName( + xFilter->getTransition(), xFilter->getSubtype(), xFilter->getDirection()); + const char* pMode = xFilter->getMode() ? "in" : "out"; + mpFS->startElementNS(XML_p, XML_animEffect, XML_filter, pFilter, XML_transition, pMode); + + WriteAnimationNodeAnimateInside(false); + + mpFS->endElementNS(XML_p, XML_animEffect); + } +} + +void PPTXAnimationExport::WriteAnimationNodeCommand() +{ + SAL_INFO("sd.eppt", "write animation node COMMAND"); + Reference xCommand(getCurrentNode(), UNO_QUERY); + if (!xCommand.is()) + return; + + const char* pType = "call"; + OString aCommand; + switch (xCommand->getCommand()) + { + case EffectCommands::VERB: + pType = "verb"; + aCommand = "1"; /* FIXME hardcoded viewing */ + break; + case EffectCommands::PLAY: + { + aCommand = "play"; + uno::Sequence aParamSeq; + xCommand->getParameter() >>= aParamSeq; + comphelper::SequenceAsHashMap aMap(aParamSeq); + auto it = aMap.find("MediaTime"); + if (it != aMap.end()) + { + double fMediaTime = 0; + it->second >>= fMediaTime; + // PowerPoint represents 0 as 0.0, so just use a single decimal. + OString aMediaTime + = rtl::math::doubleToString(fMediaTime, rtl_math_StringFormat_F, 1, '.'); + aCommand += "From(" + aMediaTime + ")"; + } + break; + } + case EffectCommands::TOGGLEPAUSE: + aCommand = "togglePause"; + break; + case EffectCommands::STOP: + aCommand = "stop"; + break; + default: + SAL_WARN("sd.eppt", "unknown command: " << xCommand->getCommand()); + break; + } + + mpFS->startElementNS(XML_p, XML_cmd, XML_type, pType, XML_cmd, aCommand.getStr()); + + WriteAnimationNodeAnimateInside(false); + mpFS->startElementNS(XML_p, XML_cBhvr); + WriteAnimationNodeCommonPropsStart(); + WriteAnimationTarget(xCommand->getTarget()); + mpFS->endElementNS(XML_p, XML_cBhvr); + + mpFS->endElementNS(XML_p, XML_cmd); +} + +void PPTXAnimationExport::WriteAnimationNodeAudio() +{ + SAL_INFO("sd.eppt", "write animation node audio"); + Reference xAudio(getCurrentNode(), UNO_QUERY); + + OUString sUrl; + uno::Reference xShape; + OUString sRelId; + OUString sName; + + if (!xAudio.is()) + { + return; + } + + bool bValid = false; + if ((xAudio->getSource() >>= sUrl) && !sUrl.isEmpty() && IsAudioURL(sUrl)) + { + bValid = true; + } + + bool bVideo = false; + if (!bValid) + { + if (xAudio->getSource() >>= xShape) + { + uno::Reference xShapeProps(xShape, uno::UNO_QUERY); + bool bHasMediaURL = xShapeProps->getPropertySetInfo()->hasPropertyByName("MediaURL"); + if (bHasMediaURL && (xShapeProps->getPropertyValue("MediaURL") >>= sUrl)) + { + bVideo = IsVideoURL(sUrl); + bValid = IsAudioURL(sUrl) || bVideo; + } + } + } + + if (!bValid) + return; + + if (!xShape.is()) + { + mrPowerPointExport.embedEffectAudio(mpFS, sUrl, sRelId, sName); + } + + if (bVideo) + { + mpFS->startElementNS(XML_p, XML_video); + mpFS->startElementNS(XML_p, XML_cMediaNode); + } + else + { + bool bNarration = xAudio->getNarration(); + mpFS->startElementNS(XML_p, XML_audio, XML_isNarration, bNarration ? "1" : "0"); + bool bHideDuringShow = xAudio->getHideDuringShow(); + mpFS->startElementNS(XML_p, XML_cMediaNode, XML_showWhenStopped, + bHideDuringShow ? "0" : "1"); + } + + animations::Timing eTiming{}; + bool bLooping + = (xAudio->getRepeatCount() >>= eTiming) && eTiming == animations::Timing_INDEFINITE; + if (bVideo && bLooping) + { + mpFS->startElementNS(XML_p, XML_cTn, XML_repeatCount, "indefinite"); + } + else + { + mpFS->startElementNS(XML_p, XML_cTn); + } + WriteAnimationCondList(mpContext->getCondition(true), XML_stCondLst); + WriteAnimationCondList(mpContext->getCondition(false), XML_endCondLst); + mpFS->endElementNS(XML_p, XML_cTn); + + mpFS->startElementNS(XML_p, XML_tgtEl); + if (xShape.is()) + { + sal_Int32 nShapeID = mrPowerPointExport.GetShapeID(xShape); + mpFS->singleElementNS(XML_p, XML_spTgt, XML_spid, OString::number(nShapeID)); + } + else + { + mpFS->singleElementNS(XML_p, XML_sndTgt, FSNS(XML_r, XML_embed), + sax_fastparser::UseIf(sRelId, !sRelId.isEmpty()), XML_name, + sax_fastparser::UseIf(sName, !sUrl.isEmpty())); + } + mpFS->endElementNS(XML_p, XML_tgtEl); + + mpFS->endElementNS(XML_p, XML_cMediaNode); + if (bVideo) + { + mpFS->endElementNS(XML_p, XML_video); + } + else + { + mpFS->endElementNS(XML_p, XML_audio); + } +} + +void PPTXAnimationExport::WriteAnimationNode(const NodeContextPtr& pContext) +{ + const NodeContext* pSavedContext = mpContext; + mpContext = pContext.get(); + + const Reference& rXNode = getCurrentNode(); + + SAL_INFO("sd.eppt", "export node type: " << rXNode->getType()); + sal_Int32 xmlNodeType = extractNodeType(rXNode); + + switch (xmlNodeType) + { + case XML_par: + mpFS->startElementNS(XML_p, xmlNodeType); + WriteAnimationNodeCommonPropsStart(); + mpFS->endElementNS(XML_p, xmlNodeType); + break; + case XML_seq: + WriteAnimationNodeSeq(); + break; + case XML_animScale: + case XML_animRot: + case XML_anim: + case XML_animMotion: + case XML_animClr: + case XML_set: + WriteAnimationNodeAnimate(xmlNodeType); + break; + case XML_animEffect: + WriteAnimationNodeEffect(); + break; + case XML_cmd: + WriteAnimationNodeCommand(); + break; + case XML_audio: + WriteAnimationNodeAudio(); + break; + default: + SAL_WARN("sd.eppt", "export ooxml node type: " << xmlNodeType); + break; + } + + mpContext = pSavedContext; +} + +void PPTXAnimationExport::WriteAnimations(const Reference& rXDrawPage) +{ + Reference xNodeSupplier(rXDrawPage, UNO_QUERY); + if (!xNodeSupplier.is()) + return; + + const Reference xNode(xNodeSupplier->getAnimationNode()); + if (!xNode.is()) + return; + + Reference xEnumerationAccess(xNode, UNO_QUERY); + if (!xEnumerationAccess.is()) + return; + + Reference xEnumeration = xEnumerationAccess->createEnumeration(); + if (!(xEnumeration.is() && xEnumeration->hasMoreElements())) + return; + + auto pNodeContext = std::make_unique(xNode, false, false); + if (pNodeContext->isValid()) + { + mpFS->startElementNS(XML_p, XML_timing); + mpFS->startElementNS(XML_p, XML_tnLst); + + WriteAnimationNode(pNodeContext); + + mpFS->endElementNS(XML_p, XML_tnLst); + mpFS->endElementNS(XML_p, XML_timing); + } +} + +sal_Int32 PPTXAnimationExport::GetNextAnimationNodeId(const Reference& xNode) +{ + sal_Int32 nId = mrPowerPointExport.GetNextAnimationNodeID(); + maAnimationNodeIdMap[xNode] = nId; + return nId; +} + +sal_Int32 PPTXAnimationExport::GetAnimationNodeId(const Reference& xNode) +{ + sal_Int32 nId = -1; + const auto& aIter = maAnimationNodeIdMap.find(xNode); + if (aIter != maAnimationNodeIdMap.end()) + { + nId = aIter->second; + } + return nId; +} + +NodeContext::NodeContext(const Reference& xNode, bool bMainSeqChild, + bool bIsIterateChild) + : mxNode(xNode) + , mbMainSeqChild(bMainSeqChild) + , mbValid(true) + , mnEffectNodeType(-1) + , mnEffectPresetClass(DFF_ANIM_PRESS_CLASS_USER_DEFINED) +{ + assert(xNode.is()); + + initUserData(); + + initValid(initChildNodes(), bIsIterateChild); +} + +void NodeContext::initUserData() +{ + assert(mxNode.is()); + + Sequence aUserData = mxNode->getUserData(); + const Any* aIndexedData[DFF_ANIM_PROPERTY_ID_COUNT]; + AnimationExporter::GetUserData(aUserData, aIndexedData, sizeof(aIndexedData)); + + const Any* pAny = aIndexedData[DFF_ANIM_NODE_TYPE]; + if (pAny) + *pAny >>= mnEffectNodeType; + + pAny = aIndexedData[DFF_ANIM_PRESET_CLASS]; + if (pAny) + *pAny >>= mnEffectPresetClass; + + pAny = aIndexedData[DFF_ANIM_PRESET_ID]; + if (pAny) + *pAny >>= msEffectPresetId; + + pAny = aIndexedData[DFF_ANIM_PRESET_SUB_TYPE]; + if (pAny) + *pAny >>= msEffectPresetSubType; +} + +void NodeContext::initValid(bool bHasValidChild, bool bIsIterateChild) +{ + sal_Int16 nType = mxNode->getType(); + + if (nType == AnimationNodeType::ITERATE) + { + Reference xIterate(mxNode, UNO_QUERY); + mbValid = xIterate.is() && (bIsIterateChild || isValidTarget(xIterate->getTarget())) + && !maChildNodes.empty(); + } + else if (nType == AnimationNodeType::COMMAND) + { + Reference xCommand(mxNode, UNO_QUERY); + mbValid = xCommand.is() && (bIsIterateChild || isValidTarget(xCommand->getTarget())); + } + else if (nType == AnimationNodeType::PAR || nType == AnimationNodeType::SEQ) + { + mbValid = bHasValidChild; + } + else if (nType == AnimationNodeType::AUDIO) + { + Reference xAudio(mxNode, UNO_QUERY); + OUString sURL; + uno::Reference xShape; + mbValid = false; + if (xAudio.is()) + { + if (xAudio->getSource() >>= sURL) + { + mbValid = IsAudioURL(sURL); + } + else if (xAudio->getSource() >>= xShape) + { + uno::Reference xShapeProps(xShape, uno::UNO_QUERY); + bool bHasMediaURL + = xShapeProps->getPropertySetInfo()->hasPropertyByName("MediaURL"); + if (bHasMediaURL && (xShapeProps->getPropertyValue("MediaURL") >>= sURL)) + { + mbValid = IsAudioURL(sURL) || IsVideoURL(sURL); + } + } + } + } + else + { + Reference xAnimate(mxNode, UNO_QUERY); + mbValid = xAnimate.is() && (bIsIterateChild || isValidTarget(xAnimate->getTarget())); + } +} + +bool NodeContext::initChildNodes() +{ + bool bValid = false; + Reference xEnumerationAccess(mxNode, UNO_QUERY); + if (xEnumerationAccess.is()) + { + Reference xEnumeration = xEnumerationAccess->createEnumeration(); + bool bIsMainSeq = mnEffectNodeType == EffectNodeType::MAIN_SEQUENCE; + bool bIsIterateChild = mxNode->getType() == AnimationNodeType::ITERATE; + if (xEnumeration.is()) + { + while (xEnumeration->hasMoreElements()) + { + Reference xChildNode(xEnumeration->nextElement(), UNO_QUERY); + if (xChildNode.is()) + { + auto pChildContext + = std::make_unique(xChildNode, bIsMainSeq, bIsIterateChild); + if (pChildContext->isValid()) + bValid = true; + maChildNodes.push_back(std::move(pChildContext)); + } + } + } + } + return bValid; +} + +Any NodeContext::getCondition(bool bBegin) const +{ + const bool bParent + = (mnEffectNodeType != EffectNodeType::INTERACTIVE_SEQUENCE || maChildNodes.empty()); + const Reference& rNode = bParent ? mxNode : maChildNodes[0]->getNode(); + + return bBegin ? rNode->getBegin() : rNode->getEnd(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-animations.hxx b/sd/source/filter/eppt/pptx-animations.hxx new file mode 100644 index 000000000..24654abb8 --- /dev/null +++ b/sd/source/filter/eppt/pptx-animations.hxx @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include + +#include +#include + +#include "epptooxml.hxx" + +namespace oox::core +{ +void WriteAnimations(const ::sax_fastparser::FSHelperPtr& pFS, + const css::uno::Reference& rXDrawPage, + PowerPointExport& rExport); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-epptbase.cxx b/sd/source/filter/eppt/pptx-epptbase.cxx new file mode 100644 index 000000000..690738996 --- /dev/null +++ b/sd/source/filter/eppt/pptx-epptbase.cxx @@ -0,0 +1,1000 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "epptbase.hxx" +#include "epptdef.hxx" +#include "../ppt/pptanimations.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace com::sun::star; + +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::awt::FontFamily; +using namespace ::com::sun::star::awt::FontPitch; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::container::XNameAccess; +using ::com::sun::star::container::XNamed; +using ::com::sun::star::drawing::XMasterPageTarget; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::frame::XModel; +using ::com::sun::star::style::XStyleFamiliesSupplier; +using ::com::sun::star::style::XStyle; +using ::com::sun::star::task::XStatusIndicator; +using ::com::sun::star::text::XSimpleText; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; + +PHLayout const pPHLayout[] = +{ + { EppLayout::TITLESLIDE, { 0x0d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x10, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x14, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x0e, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, true, true, true }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x0e, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x14, 0x0d, 0x0e, true, true, false }, + { EppLayout::BLANKSLIDE, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, false, false, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x0e, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x16, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x14, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x14, 0x0d, 0x0e, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x15, 0x0d, 0x0e, true, false, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x16, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x16, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x0e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, false, false }, + { EppLayout::RIGHTCOLUMN2ROWS, { 0x0d, 0x0e, 0x13, 0x13, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x13, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOROWSANDTITLE, { 0x0d, 0x13, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::LEFTCOLUMN2ROWS, { 0x0d, 0x13, 0x13, 0x0e, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TOPROW2COLUMN, { 0x0d, 0x13, 0x13, 0x0e, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::TWOROWSANDTITLE, { 0x0d, 0x0e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, true, false }, + { EppLayout::FOUROBJECTS, { 0x0d, 0x13, 0x13, 0x13, 0x13, 0x00, 0x00, 0x00 }, 0x13, 0x0d, 0x0e, true, false, false }, + { EppLayout::ONLYTITLE, { 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, true, false, false }, + { EppLayout::BLANKSLIDE, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x0e, false, false, false }, + { EppLayout::TITLERIGHT2BODIESLEFT, { 0x11, 0x12, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x14, 0x11, 0x12, true, true, false }, + { EppLayout::TITLERIGHTBODYLEFT, { 0x11, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x11, 0x12, true, true, false }, + { EppLayout::TITLEANDBODYSLIDE, { 0x0d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x00, 0x0d, 0x12, true, true, false }, + { EppLayout::TWOCOLUMNSANDTITLE, { 0x0d, 0x16, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00 }, 0x16, 0x0d, 0x12, true, true, false } +}; + +PPTWriterBase::PPTWriterBase() + : mbStatusIndicator(false) + , mbPresObj(false) + , mbEmptyPresObj(false) + , mbIsBackgroundDark(false) + , mnAngle(0) + , mnPages(0) + , mnMasterPages(0) + , maFraction(1, 576) + , maMapModeSrc(MapUnit::Map100thMM) + , maMapModeDest(MapUnit::MapInch, Point(), maFraction, maFraction) + , meLatestPageType(NORMAL) + , mpStyleSheet(nullptr) +{ + SAL_INFO("sd.eppt", "PPTWriterBase::PPTWriterBase()"); +} + +PPTWriterBase::PPTWriterBase( const Reference< XModel > & rXModel, + const Reference< XStatusIndicator > & rXStatInd ) + : mXModel(rXModel) + , mXStatusIndicator(rXStatInd) + , mbStatusIndicator(false) + , mbPresObj(false) + , mbEmptyPresObj(false) + , mbIsBackgroundDark(false) + , mnAngle(0) + , mnPages(0) + , mnMasterPages(0) + , maFraction(1, 576) + , maMapModeSrc(MapUnit::Map100thMM) + , maMapModeDest(MapUnit::MapInch, Point(), maFraction, maFraction) + , meLatestPageType (NORMAL) + , mpStyleSheet(nullptr) +{ +} + +PPTWriterBase::~PPTWriterBase() +{ + // Possibly unnecessary sanity check for mXStatusIndicator.is(). + // In 3.3 we had a bug report of a crash where it was null, + // https://bugzilla.novell.com/show_bug.cgi?id=694119 (non-public, + // bug report, sorry). + if ( mbStatusIndicator && mXStatusIndicator.is() ) + mXStatusIndicator->end(); +} + +void PPTWriterBase::exportPPT( const std::vector< css::beans::PropertyValue >& rMediaData ) +{ + if ( !InitSOIface() ) + return; + + FontCollectionEntry aDefaultFontDesc( "Times New Roman", + ROMAN, + awt::FontPitch::VARIABLE, + RTL_TEXTENCODING_MS_1252 ); + maFontCollection.GetId( aDefaultFontDesc ); // default is always times new roman + + if ( !GetPageByIndex( 0, NOTICE ) ) + return; + + sal_Int32 nWidth = 21000; + if ( ImplGetPropertyValue( mXPagePropSet, "Width" ) ) + mAny >>= nWidth; + sal_Int32 nHeight = 29700; + if ( ImplGetPropertyValue( mXPagePropSet, "Height" ) ) + mAny >>= nHeight; + + maNotesPageSize = MapSize( awt::Size( nWidth, nHeight ) ); + + if ( !GetPageByIndex( 0, MASTER ) ) + return; + + nWidth = 28000; + if ( ImplGetPropertyValue( mXPagePropSet, "Width" ) ) + mAny >>= nWidth; + nHeight = 21000; + if ( ImplGetPropertyValue( mXPagePropSet, "Height" ) ) + mAny >>= nHeight; + maDestPageSize = MapSize( awt::Size( nWidth, nHeight ) ); + maPageSize = awt::Size(nWidth, nHeight); + + SAL_INFO("sd.eppt", "call exportDocumentPre()"); + exportPPTPre(rMediaData); + + if ( !GetStyleSheets() ) + return; + + if ( !ImplCreateDocument() ) + return; + + sal_uInt32 i; + + for ( i = 0; i < mnMasterPages; i++ ) + { + if ( !CreateSlideMaster( i ) ) + return; + } + if ( !CreateMainNotes() ) + return; + + for ( i = 0; i < mnPages; i++ ) + { + SAL_INFO("sd.eppt", "call ImplCreateSlide( " << i << " )"); + if ( !CreateSlide( i ) ) + return; + } + + for ( i = 0; i < mnPages; i++ ) + { + if ( !CreateNotes( i ) ) + return; + } + + SAL_INFO("sd.eppt", "call exportDocumentPost()"); + exportPPTPost(); +} + +bool PPTWriterBase::InitSOIface() +{ + while( true ) + { + mXDrawPagesSupplier.set( mXModel, UNO_QUERY ); + if ( !mXDrawPagesSupplier.is() ) + break; + + mXMasterPagesSupplier.set( mXModel, UNO_QUERY ); + if ( !mXMasterPagesSupplier.is() ) + break; + mXDrawPages = mXMasterPagesSupplier->getMasterPages(); + if ( !mXDrawPages.is() ) + break; + mnMasterPages = mXDrawPages->getCount(); + mXDrawPages = mXDrawPagesSupplier->getDrawPages(); + if( !mXDrawPages.is() ) + break; + mnPages = mXDrawPages->getCount(); + if ( !GetPageByIndex( 0, NORMAL ) ) + break; + + return true; + } + return false; +} + +bool PPTWriterBase::GetPageByIndex( sal_uInt32 nIndex, PageType ePageType ) +{ + while( true ) + { + if ( ePageType != meLatestPageType ) + { + switch( ePageType ) + { + case NORMAL : + case NOTICE : + { + mXDrawPages = mXDrawPagesSupplier->getDrawPages(); + if( !mXDrawPages.is() ) + return false; + } + break; + + case MASTER : + { + mXDrawPages = mXMasterPagesSupplier->getMasterPages(); + if( !mXDrawPages.is() ) + return false; + } + break; + default: + break; + } + meLatestPageType = ePageType; + } + Any aAny( mXDrawPages->getByIndex( nIndex ) ); + aAny >>= mXDrawPage; + if ( !mXDrawPage.is() ) + break; + if ( ePageType == NOTICE ) + { + Reference< XPresentationPage > aXPresentationPage( mXDrawPage, UNO_QUERY ); + if ( !aXPresentationPage.is() ) + break; + mXDrawPage = aXPresentationPage->getNotesPage(); + if ( !mXDrawPage.is() ) + break; + } + mXPagePropSet.set( mXDrawPage, UNO_QUERY ); + if ( !mXPagePropSet.is() ) + break; + + if (GetPropertyValue( aAny, mXPagePropSet, "IsBackgroundDark" ) ) + aAny >>= mbIsBackgroundDark; + + mXShapes = mXDrawPage; + if ( !mXShapes.is() ) + break; + + /* try to get the "real" background PropertySet. If the normal page is not supporting this property, it is + taken the property from the master */ + bool bHasBackground = GetPropertyValue( aAny, mXPagePropSet, "Background", true ); + if ( bHasBackground ) + bHasBackground = ( aAny >>= mXBackgroundPropSet ); + if ( !bHasBackground ) + { + Reference< XMasterPageTarget > aXMasterPageTarget( mXDrawPage, UNO_QUERY ); + if ( aXMasterPageTarget.is() ) + { + Reference< XDrawPage > aXMasterDrawPage = aXMasterPageTarget->getMasterPage(); + if ( aXMasterDrawPage.is() ) + { + Reference< XPropertySet > aXMasterPagePropSet; + aXMasterPagePropSet.set( aXMasterDrawPage, UNO_QUERY ); + if ( aXMasterPagePropSet.is() ) + { + bool bBackground = GetPropertyValue( aAny, aXMasterPagePropSet, "Background" ); + if ( bBackground ) + { + aAny >>= mXBackgroundPropSet; + } + } + } + } + } + return true; + } + return false; +} + +bool PPTWriterBase::CreateSlide( sal_uInt32 nPageNum ) +{ + Any aAny; + + if ( !GetPageByIndex( nPageNum, NORMAL ) ) + return false; + + sal_uInt32 nMasterNum = GetMasterIndex( NORMAL ); + SetCurrentStyleSheet( nMasterNum ); + + Reference< XPropertySet > aXBackgroundPropSet; + bool bHasBackground = GetPropertyValue( aAny, mXPagePropSet, "Background" ); + if ( bHasBackground ) + bHasBackground = ( aAny >>= aXBackgroundPropSet ); + + sal_uInt16 nMode = 7; // Bit 1: Follow master objects, Bit 2: Follow master scheme, Bit 3: Follow master background + if ( bHasBackground ) + nMode &=~4; + +/* sj: Don't know what's IsBackgroundVisible for, have to ask cl + if ( GetPropertyValue( aAny, mXPagePropSet, OUString( "IsBackgroundVisible" ) ) ) + { + bool bBackgroundVisible; + if ( aAny >>= bBackgroundVisible ) + { + if ( bBackgroundVisible ) + nMode &= ~4; + } + } +*/ + if ( GetPropertyValue( aAny, mXPagePropSet, "IsBackgroundObjectsVisible" ) ) + { + bool bBackgroundObjectsVisible = false; + if ( aAny >>= bBackgroundObjectsVisible ) + { + if ( !bBackgroundObjectsVisible ) + nMode &= ~1; + } + } + + ImplWriteSlide( nPageNum, nMasterNum, nMode, bHasBackground, aXBackgroundPropSet ); + + return true; +}; + +bool PPTWriterBase::CreateNotes( sal_uInt32 nPageNum ) +{ + if ( !GetPageByIndex( nPageNum, NOTICE ) ) + return false; + SetCurrentStyleSheet( GetMasterIndex( NORMAL ) ); + + ImplWriteNotes( nPageNum ); + + return true; +}; + +bool PPTWriterBase::CreateSlideMaster( sal_uInt32 nPageNum ) +{ + if ( !GetPageByIndex( nPageNum, MASTER ) ) + return false; + SetCurrentStyleSheet( nPageNum ); + + css::uno::Reference< css::beans::XPropertySet > aXBackgroundPropSet; + if (ImplGetPropertyValue(mXPagePropSet, "Background")) // load background shape + mAny >>= aXBackgroundPropSet; + + ImplWriteSlideMaster( nPageNum, aXBackgroundPropSet ); + + return true; +}; + +sal_Int32 PPTWriterBase::GetLayoutOffset( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ) +{ + css::uno::Any aAny; + sal_Int32 nLayout = 20; + if ( GetPropertyValue( aAny, rXPropSet, "Layout", true ) ) + aAny >>= nLayout; + + SAL_INFO("sd.eppt", "GetLayoutOffset " << nLayout); + + return nLayout; +} + +sal_Int32 PPTWriterBase::GetLayoutOffsetFixed( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ) +{ + sal_Int32 nLayout = GetLayoutOffset( rXPropSet ); + + if ( ( nLayout >= 21 ) && ( nLayout <= 26 ) ) // NOTES _> HANDOUT6 + nLayout = 20; + if ( ( nLayout >= 27 ) && ( nLayout <= 30 ) ) // VERTICAL LAYOUT + nLayout -= 6; + else if ( nLayout > 30 ) + nLayout = 20; + + return nLayout; +} + +PHLayout const & PPTWriterBase::GetLayout( const css::uno::Reference< css::beans::XPropertySet >& rXPropSet ) +{ + return pPHLayout[ GetLayoutOffsetFixed( rXPropSet ) ]; +} + +PHLayout const & PPTWriterBase::GetLayout( sal_Int32 nOffset ) +{ + if( nOffset >= 0 && nOffset < EPP_LAYOUT_SIZE ) + return pPHLayout[ nOffset ]; + + SAL_INFO("sd.eppt", "asked " << nOffset << " for layout outside of 0, " << EPP_LAYOUT_SIZE << " array scope"); + + return pPHLayout[ 0 ]; +} + +sal_uInt32 PPTWriterBase::GetMasterIndex( PageType ePageType ) +{ + sal_uInt32 nRetValue = 0; + css::uno::Reference< css::drawing::XMasterPageTarget >aXMasterPageTarget( mXDrawPage, css::uno::UNO_QUERY ); + + if ( aXMasterPageTarget.is() ) + { + css::uno::Reference< css::drawing::XDrawPage >aXDrawPage = aXMasterPageTarget->getMasterPage(); + if ( aXDrawPage.is() ) + { + css::uno::Reference< css::beans::XPropertySet > aXPropertySet( aXDrawPage, css::uno::UNO_QUERY ); + if ( aXPropertySet.is() ) + { + if ( ImplGetPropertyValue( aXPropertySet, "Number" ) ) + nRetValue |= *o3tl::doAccess(mAny); + if ( nRetValue & 0xffff ) // avoid overflow + nRetValue--; + } + } + } + if ( ePageType == NOTICE ) + nRetValue += mnMasterPages; + return nRetValue; +} + +void PPTWriterBase::SetCurrentStyleSheet( sal_uInt32 nPageNum ) +{ + if ( nPageNum >= maStyleSheetList.size() ) + nPageNum = 0; + mpStyleSheet = maStyleSheetList[ nPageNum ].get(); +} + +bool PPTWriterBase::GetStyleSheets() +{ + int nInstance, nLevel; + bool bRetValue = false; + sal_uInt32 nPageNum; + + for ( nPageNum = 0; nPageNum < mnMasterPages; nPageNum++ ) + { + Reference< XNamed > + aXNamed; + + Reference< XNameAccess > + aXNameAccess; + + Reference< XStyleFamiliesSupplier > + aXStyleFamiliesSupplier( mXModel, UNO_QUERY ); + + Reference< XPropertySet > + aXPropSet( mXModel, UNO_QUERY ); + + sal_uInt16 nDefaultTab = ( aXPropSet.is() && ImplGetPropertyValue( aXPropSet, "TabStop" ) ) + ? static_cast( convertMm100ToMasterUnit(*o3tl::doAccess(mAny)) ) + : 1250; + + maStyleSheetList.emplace_back( new PPTExStyleSheet( nDefaultTab, dynamic_cast(this) ) ); + SetCurrentStyleSheet( nPageNum ); + if ( GetPageByIndex( nPageNum, MASTER ) ) + aXNamed.set( mXDrawPage, UNO_QUERY ); + + if ( aXStyleFamiliesSupplier.is() ) + aXNameAccess = aXStyleFamiliesSupplier->getStyleFamilies(); + + bRetValue = aXNamed.is() && aXNameAccess.is() && aXStyleFamiliesSupplier.is(); + if ( bRetValue ) + { + for ( nInstance = EPP_TEXTTYPE_Title; nInstance <= EPP_TEXTTYPE_CenterTitle; nInstance++ ) + { + OUString aStyle; + OUString aFamily; + switch ( nInstance ) + { + case EPP_TEXTTYPE_CenterTitle : + case EPP_TEXTTYPE_Title : + { + aStyle = "title"; + aFamily = aXNamed->getName(); + } + break; + case EPP_TEXTTYPE_Body : + { + aStyle = "outline1"; // SD_LT_SEPARATOR + aFamily = aXNamed->getName(); + } + break; + case EPP_TEXTTYPE_Other : + { + aStyle = "standard"; + aFamily = "graphics"; + } + break; + case EPP_TEXTTYPE_CenterBody : + { + aStyle = "subtitle"; + aFamily = aXNamed->getName(); + } + break; + } + if ( !aStyle.isEmpty() && !aFamily.isEmpty() ) + { + try + { + Reference< XNameAccess >xNameAccess; + if ( aXNameAccess->hasByName( aFamily ) ) + { + Any aAny( aXNameAccess->getByName( aFamily ) ); + xNameAccess.set(aAny, css::uno::UNO_QUERY); + if( xNameAccess.is() ) + { + Reference< XNameAccess > aXFamily; + if ( aAny >>= aXFamily ) + { + if ( aXFamily->hasByName( aStyle ) ) + { + aAny = aXFamily->getByName( aStyle ); + Reference< XStyle > xStyle( + aAny, css::uno::UNO_QUERY); + if( xStyle.is() ) + { + Reference< XStyle > aXStyle; + aAny >>= aXStyle; + Reference< XPropertySet > + xPropSet( aXStyle, UNO_QUERY ); + if( xPropSet.is() ) + mpStyleSheet->SetStyleSheet( xPropSet, maFontCollection, nInstance, 0 ); + for ( nLevel = 1; nLevel < 5; nLevel++ ) + { + if ( nInstance == EPP_TEXTTYPE_Body ) + { + sal_Unicode cTemp = aStyle[aStyle.getLength() - 1]; + aStyle = aStyle.subView(0, aStyle.getLength() - 1) + OUStringChar(++cTemp); + if ( aXFamily->hasByName( aStyle ) ) + { + aXFamily->getByName( aStyle ) >>= xStyle; + if( xStyle.is() ) + { + Reference< XPropertySet > + xPropertySet( xStyle, UNO_QUERY ); + if ( xPropertySet.is() ) + mpStyleSheet->SetStyleSheet( xPropertySet, maFontCollection, nInstance, nLevel ); + } + } + } + else + mpStyleSheet->SetStyleSheet( xPropSet, maFontCollection, nInstance, nLevel ); + } + } + } + } + } + } + } + catch( Exception& ) + { + + } + } + } + for ( ; nInstance <= EPP_TEXTTYPE_QuarterBody; nInstance++ ) + { + + } + } + } + return bRetValue; +} + +bool PPTWriterBase::CreateMainNotes() +{ + if ( !GetPageByIndex( 0, NOTICE ) ) + return false; + SetCurrentStyleSheet( 0 ); + + css::uno::Reference< css::drawing::XMasterPageTarget > aXMasterPageTarget( mXDrawPage, css::uno::UNO_QUERY ); + + if ( !aXMasterPageTarget.is() ) + return false; + + mXDrawPage = aXMasterPageTarget->getMasterPage(); + if ( !mXDrawPage.is() ) + return false; + + mXPropSet.set( mXDrawPage, css::uno::UNO_QUERY ); + if ( !mXPropSet.is() ) + return false; + + mXShapes = mXDrawPage; + if ( !mXShapes.is() ) + return false; + + return ImplCreateMainNotes(); +} + +awt::Size PPTWriterBase::MapSize( const awt::Size& rSize ) +{ + Size aRetSize( OutputDevice::LogicToLogic( Size( rSize.Width, rSize.Height ), maMapModeSrc, maMapModeDest ) ); + + if ( !aRetSize.Width() ) + aRetSize.AdjustWidth( 1 ); + if ( !aRetSize.Height() ) + aRetSize.AdjustHeight( 1 ); + return awt::Size( aRetSize.Width(), aRetSize.Height() ); +} + +awt::Point PPTWriterBase::MapPoint( const awt::Point& rPoint ) +{ + Point aRet( OutputDevice::LogicToLogic( Point( rPoint.X, rPoint.Y ), maMapModeSrc, maMapModeDest ) ); + return awt::Point( aRet.X(), aRet.Y() ); +} + +::tools::Rectangle PPTWriterBase::MapRectangle( const awt::Rectangle& rRect ) +{ + css::awt::Point aPoint( rRect.X, rRect.Y ); + css::awt::Size aSize( rRect.Width, rRect.Height ); + css::awt::Point aP( MapPoint( aPoint ) ); + css::awt::Size aS( MapSize( aSize ) ); + return ::tools::Rectangle( Point( aP.X, aP.Y ), Size( aS.Width, aS.Height ) ); +} + +bool PPTWriterBase::GetShapeByIndex( sal_uInt32 nIndex, bool bGroup ) +{ + while(true) + { + if ( !bGroup || ( GetCurrentGroupLevel() == 0 ) ) + { + Any aAny( mXShapes->getByIndex( nIndex ) ); + aAny >>= mXShape; + } + else + { + Any aAny( GetCurrentGroupAccess()->getByIndex( GetCurrentGroupIndex() ) ); + aAny >>= mXShape; + } + if ( !mXShape.is() ) + break; + + Any aAny( mXShape->queryInterface( cppu::UnoType::get())); + aAny >>= mXPropSet; + + if ( !mXPropSet.is() ) + break; + maPosition = MapPoint( mXShape->getPosition() ); + maSize = MapSize( mXShape->getSize() ); + maRect = ::tools::Rectangle( Point( maPosition.X, maPosition.Y ), Size( maSize.Width, maSize.Height ) ); + + OStringBuffer aTypeBuffer(OUStringToOString( + mXShape->getShapeType(), RTL_TEXTENCODING_UTF8)); + // remove "com.sun.star." + aTypeBuffer.remove(0, RTL_CONSTASCII_LENGTH("com.sun.star.")); + + sal_Int32 nPos = aTypeBuffer.toString().indexOf("Shape"); + aTypeBuffer.remove(nPos, RTL_CONSTASCII_LENGTH("Shape")); + mType = aTypeBuffer.makeStringAndClear(); + + mbPresObj = mbEmptyPresObj = false; + if ( ImplGetPropertyValue( "IsPresentationObject" ) ) + mAny >>= mbPresObj; + + if ( mbPresObj && ImplGetPropertyValue( "IsEmptyPresentationObject" ) ) + mAny >>= mbEmptyPresObj; + + mnAngle = ( PropValue::GetPropertyValue( aAny, + mXPropSet, "RotateAngle", true ) ) + ? *o3tl::doAccess(aAny) + : 0; + + return true; + } + return false; +} + +sal_Int8 PPTWriterBase::GetTransition( sal_Int16 nTransitionType, sal_Int16 nTransitionSubtype, FadeEffect eEffect, + sal_Int32 nTransitionFadeColor, sal_uInt8& nDirection ) +{ + sal_Int8 nPPTTransitionType = 0; + nDirection = 0; + + switch( nTransitionType ) + { + case TransitionType::FADE : + { + if ( nTransitionSubtype == TransitionSubType::CROSSFADE ) + nPPTTransitionType = PPT_TRANSITION_TYPE_SMOOTHFADE; + else if ( nTransitionSubtype == TransitionSubType::FADEOVERCOLOR ) + { + if( nTransitionFadeColor == static_cast(COL_WHITE) ) + nPPTTransitionType = PPT_TRANSITION_TYPE_FLASH; + else + nPPTTransitionType = PPT_TRANSITION_TYPE_FADE; + } + } + break; + case TransitionType::PUSHWIPE : + { + if (nTransitionSubtype == TransitionSubType::COMBVERTICAL || + nTransitionSubtype == TransitionSubType::COMBHORIZONTAL) + { + nPPTTransitionType = PPT_TRANSITION_TYPE_COMB; + } + else + { + nPPTTransitionType = PPT_TRANSITION_TYPE_PUSH; + } + switch (nTransitionSubtype) + { + case TransitionSubType::FROMRIGHT: nDirection = 0; break; + case TransitionSubType::FROMBOTTOM: nDirection = 1; break; + case TransitionSubType::FROMLEFT: nDirection = 2; break; + case TransitionSubType::FROMTOP: nDirection = 3; break; + case TransitionSubType::COMBHORIZONTAL: nDirection = 0; break; + case TransitionSubType::COMBVERTICAL: nDirection = 1; break; + } + } + break; + case TransitionType::PINWHEELWIPE : + { + nPPTTransitionType = PPT_TRANSITION_TYPE_WHEEL; + switch( nTransitionSubtype ) + { + case TransitionSubType::ONEBLADE: nDirection = 1; break; + case TransitionSubType::TWOBLADEVERTICAL : nDirection = 2; break; + case TransitionSubType::THREEBLADE : nDirection = 3; break; + case TransitionSubType::FOURBLADE: nDirection = 4; break; + case TransitionSubType::EIGHTBLADE: nDirection = 8; break; + } + } + break; + case TransitionType::FANWIPE : + { + nPPTTransitionType = PPT_TRANSITION_TYPE_WEDGE; + } + break; + case TransitionType::ELLIPSEWIPE : + { + switch( nTransitionSubtype ) { + case TransitionSubType::VERTICAL: + case TransitionSubType::HORIZONTAL: + // no ellipse or oval in PPT or OOXML, fallback to circle + default: + nPPTTransitionType = PPT_TRANSITION_TYPE_CIRCLE; + } + } + break; + case TransitionType::FOURBOXWIPE : + { + nPPTTransitionType = PPT_TRANSITION_TYPE_PLUS; + } + break; + case TransitionType::IRISWIPE : + { + switch( nTransitionSubtype ) { + case TransitionSubType::RECTANGLE: + nPPTTransitionType = PPT_TRANSITION_TYPE_ZOOM; + nDirection = (eEffect == FadeEffect_FADE_FROM_CENTER) ? 0 : 1; + break; + default: + nPPTTransitionType = PPT_TRANSITION_TYPE_DIAMOND; + break; + } + } + break; + case TransitionType::ZOOM: + { + switch(nTransitionSubtype) + { + case TransitionSubType::ROTATEIN: + nPPTTransitionType = PPT_TRANSITION_TYPE_NEWSFLASH; + break; + default: + break; + } + } + break; + } + + return nPPTTransitionType; +} + +sal_Int8 PPTWriterBase::GetTransition( FadeEffect eEffect, sal_uInt8& nDirection ) +{ + sal_Int8 nPPTTransitionType = 0; + + switch ( eEffect ) + { + default : + case FadeEffect_RANDOM : + nPPTTransitionType = PPT_TRANSITION_TYPE_RANDOM; + break; + + case FadeEffect_HORIZONTAL_STRIPES : + nDirection++; + [[fallthrough]]; + case FadeEffect_VERTICAL_STRIPES : + nPPTTransitionType = PPT_TRANSITION_TYPE_BLINDS; + break; + + case FadeEffect_VERTICAL_CHECKERBOARD : + nDirection++; + [[fallthrough]]; + case FadeEffect_HORIZONTAL_CHECKERBOARD : + nPPTTransitionType = PPT_TRANSITION_TYPE_CHECKER; + break; + + case FadeEffect_MOVE_FROM_UPPERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_UPPERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_LOWERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_LOWERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_TOP : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_LEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_BOTTOM : + nDirection++; + [[fallthrough]]; + case FadeEffect_MOVE_FROM_RIGHT : + nPPTTransitionType = PPT_TRANSITION_TYPE_COVER; + break; + + case FadeEffect_DISSOLVE : + nPPTTransitionType = PPT_TRANSITION_TYPE_DISSOLVE; + break; + + case FadeEffect_VERTICAL_LINES : + nDirection++; + [[fallthrough]]; + case FadeEffect_HORIZONTAL_LINES : + nPPTTransitionType = PPT_TRANSITION_TYPE_RANDOM_BARS; + break; + + case FadeEffect_CLOSE_HORIZONTAL : + nDirection++; + [[fallthrough]]; + case FadeEffect_OPEN_HORIZONTAL : + nDirection++; + [[fallthrough]]; + case FadeEffect_CLOSE_VERTICAL : + nDirection++; + [[fallthrough]]; + case FadeEffect_OPEN_VERTICAL : + nPPTTransitionType = PPT_TRANSITION_TYPE_SPLIT; + break; + + case FadeEffect_FADE_FROM_UPPERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_UPPERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_LOWERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_LOWERRIGHT : + nDirection += 4; + nPPTTransitionType = PPT_TRANSITION_TYPE_STRIPS; + break; + + case FadeEffect_UNCOVER_TO_LOWERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_LOWERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_UPPERRIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_UPPERLEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_BOTTOM : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_RIGHT : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_TOP : + nDirection++; + [[fallthrough]]; + case FadeEffect_UNCOVER_TO_LEFT : + nPPTTransitionType = PPT_TRANSITION_TYPE_PULL; + break; + + case FadeEffect_FADE_FROM_TOP : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_LEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_BOTTOM : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_RIGHT : + nPPTTransitionType = PPT_TRANSITION_TYPE_WIPE; + break; + + case FadeEffect_ROLL_FROM_TOP : + nDirection++; + [[fallthrough]]; + case FadeEffect_ROLL_FROM_LEFT : + nDirection++; + [[fallthrough]]; + case FadeEffect_ROLL_FROM_BOTTOM : + nDirection++; + [[fallthrough]]; + case FadeEffect_ROLL_FROM_RIGHT : + nPPTTransitionType = PPT_TRANSITION_TYPE_WIPE; + break; + + case FadeEffect_FADE_TO_CENTER : + nDirection++; + [[fallthrough]]; + case FadeEffect_FADE_FROM_CENTER : + nPPTTransitionType = PPT_TRANSITION_TYPE_ZOOM; + break; + + case FadeEffect_NONE : + nDirection = 2; + break; + } + + return nPPTTransitionType; +} + +bool PPTWriterBase::ContainsOtherShapeThanPlaceholders() +{ + sal_uInt32 nShapes = mXShapes->getCount(); + bool bOtherThanPlaceHolders = false; + + if ( nShapes ) + for ( sal_uInt32 nIndex = 0; ( nIndex < nShapes ) && !bOtherThanPlaceHolders; nIndex++ ) + { + if ( GetShapeByIndex( nIndex, false ) && mType != "drawing.Page" ) + { + if( mType == "presentation.Page" || mType == "presentation.Notes" ) + { + Reference< XSimpleText > rXText( mXShape, UNO_QUERY ); + + if( rXText.is() && !rXText->getString().isEmpty() ) + bOtherThanPlaceHolders = true; + } + else + bOtherThanPlaceHolders = true; + } + SAL_INFO("sd.eppt", "mType == " << mType); + } + + return bOtherThanPlaceHolders; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-epptooxml.cxx b/sd/source/filter/eppt/pptx-epptooxml.cxx new file mode 100644 index 000000000..6a338a6a6 --- /dev/null +++ b/sd/source/filter/eppt/pptx-epptooxml.cxx @@ -0,0 +1,2594 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include "epptooxml.hxx" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pptx-animations.hxx" +#include "../ppt/pptanimations.hxx" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#if OSL_DEBUG_LEVEL > 1 +#include +#endif + +// presentation namespaces +#define PNMSS FSNS(XML_xmlns, XML_a), OUStringToOString(this->getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p), OUStringToOString(this->getNamespaceURL(OOX_NS(ppt)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_r), OUStringToOString(this->getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p14), OUStringToOString(this->getNamespaceURL(OOX_NS(p14)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p15), OUStringToOString(this->getNamespaceURL(OOX_NS(p15)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_mc), OUStringToOString(this->getNamespaceURL(OOX_NS(mce)), RTL_TEXTENCODING_UTF8).getStr() + +// presentationPr namespace +#define PPRNMSS FSNS(XML_xmlns, XML_a), OUStringToOString(this->getNamespaceURL(OOX_NS(dml)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_r), OUStringToOString(this->getNamespaceURL(OOX_NS(officeRel)), RTL_TEXTENCODING_UTF8).getStr(), \ + FSNS(XML_xmlns, XML_p), OUStringToOString(this->getNamespaceURL(OOX_NS(ppt)), RTL_TEXTENCODING_UTF8).getStr() + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::geometry; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::ppt; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::beans::XPropertySetInfo; +using ::sax_fastparser::FSHelperPtr; +using namespace oox::drawingml; +using namespace oox::core; + +#if OSL_DEBUG_LEVEL > 1 +void dump_pset(Reference< XPropertySet > const& rXPropSet); +#endif + +namespace oox::core +{ + +class PowerPointShapeExport : public ShapeExport +{ + PowerPointExport& mrExport; + PageType mePageType; + bool mbMaster; +public: + PowerPointShapeExport(FSHelperPtr pFS, ShapeHashMap* pShapeMap, PowerPointExport* pFB); + void SetMaster(bool bMaster); + void SetPageType(PageType ePageType); + ShapeExport& WriteNonVisualProperties(const Reference< XShape >& xShape) override; + ShapeExport& WriteTextShape(const Reference< XShape >& xShape) override; + ShapeExport& WriteUnknownShape(const Reference< XShape >& xShape) override; + ShapeExport& WritePlaceholderShape(const Reference< XShape >& xShape, PlaceholderType ePlaceholder); + /** Writes a placeholder shape that references the placeholder on the master slide */ + ShapeExport& WritePlaceholderReferenceShape(PlaceholderType ePlaceholder, sal_Int32 nReferencedPlaceholderIdx, PageType ePageType, const Reference& rXPagePropSet); + ShapeExport& WritePageShape(const Reference< XShape >& xShape, PageType ePageType, bool bPresObj); + /** Writes textbody of a placeholder that references the placeholder on the master slide */ + ShapeExport& WritePlaceholderReferenceTextBody(PlaceholderType ePlaceholder, PageType ePageType, const Reference xPagePropSet); + + // helper parts + bool WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster); +}; + + +namespace +{ +void WriteSndAc(const FSHelperPtr& pFS, const OUString& sSoundRelId, const OUString& sSoundName) +{ + pFS->startElementNS(XML_p, XML_sndAc); + pFS->startElementNS(XML_p, XML_stSnd); + pFS->singleElementNS(XML_p, XML_snd, FSNS(XML_r, XML_embed), + sax_fastparser::UseIf(sSoundRelId, !sSoundRelId.isEmpty()), XML_name, + sax_fastparser::UseIf(sSoundName, !sSoundName.isEmpty())); + pFS->endElement(FSNS(XML_p, XML_stSnd)); + pFS->endElement(FSNS(XML_p, XML_sndAc)); +} + +const char* getPlaceholderTypeName(PlaceholderType ePlaceholder) +{ + switch (ePlaceholder) + { + case SlideImage: + return "sldImg"; + case Notes: + return "body"; + case Header: + return "hdr"; + case Footer: + return "ftr"; + case SlideNumber: + return "sldNum"; + case DateAndTime: + return "dt"; + case Outliner: + return "body"; + case Title: + return "title"; + case Subtitle: + return "subTitle"; + default: + SAL_INFO("sd.eppt", "warning: unhandled placeholder type: " << ePlaceholder); + return ""; + } +} +} +} + +namespace { + +enum PPTXLayout +{ + LAYOUT_BLANK, + LAYOUT_TITLE_SLIDE, + LAYOUT_TITLE_CONTENT, + LAYOUT_TITLE_2CONTENT, + LAYOUT_TITLE, + LAYOUT_CENTERED_TEXT, + LAYOUT_TITLE_2CONTENT_CONTENT, + LAYOUT_TITLE_CONTENT_2CONTENT, + LAYOUT_TITLE_2CONTENT_OVER_CONTENT, + LAYOUT_TITLE_CONTENT_OVER_CONTENT, + LAYOUT_TITLE_4CONTENT, + LAYOUT_TITLE_6CONTENT, + LAYOUT_SIZE +}; + +struct PPTXLayoutInfo +{ + int nType; + const char* sName; + const char* sType; +}; + +} + +const PPTXLayoutInfo aLayoutInfo[LAYOUT_SIZE] = +{ + { 20, "Blank Slide", "blank" }, + { 0, "Title Slide", "tx" }, + { 1, "Title, Content", "obj" }, + { 3, "Title, 2 Content", "twoObj" }, + { 19, "Title Only", "titleOnly" }, + { 32, "Centered Text", "objOnly" }, // not exactly, but close + { 15, "Title, 2 Content and Content", "twoObjAndObj" }, + { 12, "Title Content and 2 Content", "objAndTwoObj" }, + { 16, "Title, 2 Content over Content", "twoObjOverTx" }, // not exactly, but close + { 14, "Title, Content over Content", "objOverTx" }, // not exactly, but close + { 18, "Title, 4 Content", "fourObj" }, + { 34, "Title, 6 Content", "blank" } // not defined => blank +}; + +int PowerPointExport::GetPPTXLayoutId(int nOffset) +{ + int nId = LAYOUT_BLANK; + + SAL_INFO("sd.eppt", "GetPPTXLayoutId " << nOffset); + + switch (nOffset) + { + case 0: + nId = LAYOUT_TITLE_SLIDE; + break; + case 1: + nId = LAYOUT_TITLE_CONTENT; + break; + case 3: + nId = LAYOUT_TITLE_2CONTENT; + break; + case 19: + nId = LAYOUT_TITLE; + break; + case 15: + nId = LAYOUT_TITLE_2CONTENT_CONTENT; + break; + case 12: + nId = LAYOUT_TITLE_CONTENT_2CONTENT; + break; + case 16: + nId = LAYOUT_TITLE_2CONTENT_OVER_CONTENT; + break; + case 14: + nId = LAYOUT_TITLE_CONTENT_OVER_CONTENT; + break; + case 18: + nId = LAYOUT_TITLE_4CONTENT; + break; + case 32: + nId = LAYOUT_CENTERED_TEXT; + break; + case 34: + nId = LAYOUT_TITLE_6CONTENT; + break; + case 20: + default: + nId = LAYOUT_BLANK; + break; + } + + return nId; +} + +PowerPointShapeExport::PowerPointShapeExport(FSHelperPtr pFS, ShapeHashMap* pShapeMap, + PowerPointExport* pFB) + : ShapeExport(XML_p, std::move(pFS), pShapeMap, pFB) + , mrExport(*pFB) + , mePageType(UNDEFINED) + , mbMaster(false) +{ +} + +void PowerPointShapeExport::SetMaster(bool bMaster) +{ + mbMaster = bMaster; +} + +void PowerPointShapeExport::SetPageType(PageType ePageType) +{ + mePageType = ePageType; +} + +ShapeExport& PowerPointShapeExport::WriteNonVisualProperties(const Reference< XShape >&) +{ + GetFS()->singleElementNS(XML_p, XML_nvPr); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WriteTextShape(const Reference< XShape >& xShape) +{ + OUString sShapeType = xShape->getShapeType(); + + SAL_INFO("sd.eppt", "shape(text) : " << sShapeType.toUtf8()); + + if (sShapeType == "com.sun.star.drawing.TextShape" || sShapeType == "com.sun.star.drawing.GraphicObjectShape") + { + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.DateTimeShape") + { + if (!WritePlaceholder(xShape, DateAndTime, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.FooterShape") + { + if (!WritePlaceholder(xShape, Footer, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.HeaderShape") + { + if (!WritePlaceholder(xShape, Header, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.NotesShape") + { + if (mePageType == NOTICE && mrExport.GetPresObj()) + WritePlaceholderShape(xShape, Notes); + else + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.OutlinerShape") + { + if (!WritePlaceholder(xShape, Outliner, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.SlideNumberShape") + { + if (!WritePlaceholder(xShape, SlideNumber, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else if (sShapeType == "com.sun.star.presentation.TitleTextShape") + { + if (!WritePlaceholder(xShape, Title, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + else + SAL_WARN("sd.eppt", "PowerPointShapeExport::WriteTextShape: shape of type '" << sShapeType << "' is ignored"); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WriteUnknownShape(const Reference< XShape >& xShape) +{ + OUString sShapeType = xShape->getShapeType(); + + SAL_INFO("sd.eppt", "shape(unknown): " << sShapeType.toUtf8()); + + if (sShapeType == "com.sun.star.presentation.PageShape") + { + WritePageShape(xShape, mePageType, mrExport.GetPresObj()); + } + else if (sShapeType == "com.sun.star.presentation.SubtitleShape") + { + if(mePageType != MASTER) + { + if (!WritePlaceholder(xShape, Subtitle, mbMaster)) + ShapeExport::WriteTextShape(xShape); + } + } + else + SAL_WARN("sd.eppt", "unknown shape not handled: " << sShapeType.toUtf8()); + + return *this; +} + +PowerPointExport::PowerPointExport(const Reference< XComponentContext >& rContext, const uno::Sequence& rArguments) + : XmlFilterBase(rContext) + , mnLayoutFileIdMax(1) + , mnSlideIdMax(1 << 8) + , mnSlideMasterIdMax(1U << 31) + , mnAnimationNodeIdMax(1) + , mnDiagramId(1) + , mbCreateNotes(false) + , mnPlaceholderIndexMax(1) +{ + comphelper::SequenceAsHashMap aArgumentsMap(rArguments); + mbPptm = aArgumentsMap.getUnpackedValueOrDefault("IsPPTM", false); + mbExportTemplate = aArgumentsMap.getUnpackedValueOrDefault("IsTemplate", false); +} + +PowerPointExport::~PowerPointExport() +{ +} + +void PowerPointExport::writeDocumentProperties() +{ + uno::Reference xDPS(mXModel, uno::UNO_QUERY); + uno::Reference xDocProps = xDPS->getDocumentProperties(); + + if (xDocProps.is()) + { + bool bSecurityOptOpenReadOnly = false; + uno::Reference< lang::XMultiServiceFactory > xFactory(mXModel, uno::UNO_QUERY); + uno::Reference< beans::XPropertySet > xSettings(xFactory->createInstance("com.sun.star.document.Settings"), uno::UNO_QUERY); + try + { + xSettings->getPropertyValue("LoadReadonly") >>= bSecurityOptOpenReadOnly; + } + catch( Exception& ) + { + } + exportDocumentProperties(xDocProps, bSecurityOptOpenReadOnly); + } + + exportCustomFragments(); +} + +bool PowerPointExport::importDocument() noexcept +{ + return false; +} + +bool PowerPointExport::exportDocument() +{ + DrawingML::PushExportGraphics(); + maShapeMap.clear(); + + mXModel = getModel(); + + //write document properties + writeDocumentProperties(); + + addRelation(oox::getRelationship(Relationship::OFFICEDOCUMENT), u"ppt/presentation.xml"); + + OUString aMediaType; + if (mbPptm) + { + if (mbExportTemplate) + { + aMediaType = "application/vnd.ms-powerpoint.template.macroEnabled.main+xml"; + } + else + { + aMediaType = "application/vnd.ms-powerpoint.presentation.macroEnabled.main+xml"; + } + } + else + { + if (mbExportTemplate) + { + aMediaType = "application/vnd.openxmlformats-officedocument.presentationml.template.main+xml"; + } + else + { + aMediaType = "application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml"; + } + } + + mPresentationFS = openFragmentStreamWithSerializer("ppt/presentation.xml", aMediaType); + + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + u"theme/theme1.xml"); + + mPresentationFS->startElementNS(XML_p, XML_presentation, PNMSS); + + mXStatusIndicator = getStatusIndicator(); + + std::vector< PropertyValue > aProperties; + PropertyValue aProperty; + aProperty.Name = "BaseURI"; + aProperty.Value <<= getFileUrl(); + aProperties.push_back(aProperty); + + exportPPT(aProperties); + + mPresentationFS->singleElementNS(XML_p, XML_sldSz, + XML_cx, OString::number(PPTtoEMU(maDestPageSize.Width)), + XML_cy, OString::number(PPTtoEMU(maDestPageSize.Height))); + // for some reason if added before slides list it will not load the slides (alas with error reports) in mso + mPresentationFS->singleElementNS(XML_p, XML_notesSz, + XML_cx, OString::number(PPTtoEMU(maNotesPageSize.Width)), + XML_cy, OString::number(PPTtoEMU(maNotesPageSize.Height))); + + WriteCustomSlideShow(); + + WritePresentationProps(); + + WriteAuthors(); + + WriteVBA(); + + WriteModifyVerifier(); + + mPresentationFS->endElementNS(XML_p, XML_presentation); + mPresentationFS.reset(); + // Free all FSHelperPtr, to flush data before committing storage + mpSlidesFSArray.clear(); + + commitStorage(); + + DrawingML::PopExportGraphics(); + maShapeMap.clear(); + maAuthors.clear(); + maRelId.clear(); + + return true; +} + +::oox::ole::VbaProject* PowerPointExport::implCreateVbaProject() const +{ + return new ::oox::ole::VbaProject(getComponentContext(), getModel(), u"Impress"); +} + +void PowerPointExport::WriteCustomSlideShow() +{ + Reference aXCPSup(mXModel, css::uno::UNO_QUERY); + if (!aXCPSup.is() || !aXCPSup->getCustomPresentations()->hasElements()) + return; + + mPresentationFS->startElementNS(XML_p, XML_custShowLst); + + Reference xDPS(getModel(), uno::UNO_QUERY_THROW); + Reference xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW); + Reference aXNameCont(aXCPSup->getCustomPresentations()); + const Sequence aNameSeq(aXNameCont->getElementNames()); + + OUString sRelId; + sal_uInt32 nCustomShowIndex = 0; + sal_Int32 nSlideCount = xDrawPages->getCount(); + + for (OUString const& customShowName : aNameSeq) + { + mPresentationFS->startElementNS(XML_p, XML_custShow, XML_name, customShowName, XML_id, + OUString::number(nCustomShowIndex++)); + + mAny = aXNameCont->getByName(customShowName); + Reference aXIContainer; + if (mAny >>= aXIContainer) + { + mPresentationFS->startElementNS(XML_p, XML_sldLst); + + sal_Int32 nCustomShowSlideCount = aXIContainer->getCount(); + for (sal_Int32 i = 0; i < nCustomShowSlideCount; ++i) + { + Reference aXCustomShowDrawPage; + aXIContainer->getByIndex(i) >>= aXCustomShowDrawPage; + Reference aXName(aXCustomShowDrawPage, UNO_QUERY_THROW); + OUString sCustomShowSlideName = aXName->getName(); + + for (sal_Int32 j = 0; j < nSlideCount; ++j) + { + Reference xDrawPage; + xDrawPages->getByIndex(j) >>= xDrawPage; + Reference xNamed(xDrawPage, UNO_QUERY_THROW); + OUString sSlideName = xNamed->getName(); + + if (sCustomShowSlideName == sSlideName) + { + sRelId = maRelId[j]; + break; + } + } + mPresentationFS->singleElementNS(XML_p, XML_sld, FSNS(XML_r, XML_id), sRelId); + } + mPresentationFS->endElementNS(XML_p, XML_sldLst); + } + mPresentationFS->endElementNS(XML_p, XML_custShow); + } + mPresentationFS->endElementNS(XML_p, XML_custShowLst); +} + +void PowerPointExport::ImplWriteBackground(const FSHelperPtr& pFS, const Reference< XPropertySet >& rXPropSet) +{ + FillStyle aFillStyle(FillStyle_NONE); + if (ImplGetPropertyValue(rXPropSet, "FillStyle")) + mAny >>= aFillStyle; + + if (aFillStyle == FillStyle_NONE || + aFillStyle == FillStyle_HATCH) + return; + + pFS->startElementNS(XML_p, XML_bg); + pFS->startElementNS(XML_p, XML_bgPr); + + PowerPointShapeExport aDML(pFS, &maShapeMap, this); + aDML.SetBackgroundDark(mbIsBackgroundDark); + aDML.WriteFill(rXPropSet); + + pFS->endElementNS(XML_p, XML_bgPr); + pFS->endElementNS(XML_p, XML_bg); +} + +#define MAIN_GROUP \ + "\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " + +const char* PowerPointExport::GetSideDirection(sal_uInt8 nDirection) +{ + const char* pDirection = nullptr; + + switch (nDirection) + { + case 0: + pDirection = "l"; + break; + case 1: + pDirection = "u"; + break; + case 2: + pDirection = "r"; + break; + case 3: + pDirection = "d"; + break; + } + + return pDirection; +} + +const char* PowerPointExport::GetCornerDirection(sal_uInt8 nDirection) +{ + const char* pDirection = nullptr; + + switch (nDirection) + { + case 4: + pDirection = "lu"; + break; + case 5: + pDirection = "ru"; + break; + case 6: + pDirection = "ld"; + break; + case 7: + pDirection = "rd"; + break; + } + + return pDirection; +} + +const char* PowerPointExport::Get8Direction(sal_uInt8 nDirection) +{ + const char* pDirection = GetSideDirection(nDirection); + + if (!pDirection) + pDirection = GetCornerDirection(nDirection); + + return pDirection; +} + +void PowerPointExport::WriteTransition(const FSHelperPtr& pFS) +{ + FadeEffect eFadeEffect = FadeEffect_NONE; + if (ImplGetPropertyValue(mXPagePropSet, "Effect")) + mAny >>= eFadeEffect; + + sal_Int16 nTransitionType = 0, nTransitionSubtype = 0; + sal_Int8 nPPTTransitionType = 0; + sal_uInt8 nDirection = 0; + + OUString sSoundUrl; + OUString sSoundRelId; + OUString sSoundName; + + if (ImplGetPropertyValue(mXPagePropSet, "TransitionType") && (mAny >>= nTransitionType) && + ImplGetPropertyValue(mXPagePropSet, "TransitionSubtype") && (mAny >>= nTransitionSubtype)) + { + // FADEOVERCOLOR with black -> fade, with white -> flash + sal_Int32 nTransitionFadeColor = 0; + if( ImplGetPropertyValue(mXPagePropSet, "TransitionFadeColor")) + mAny >>= nTransitionFadeColor; + nPPTTransitionType = GetTransition(nTransitionType, nTransitionSubtype, eFadeEffect, nTransitionFadeColor, nDirection); + } + + if (!nPPTTransitionType && eFadeEffect != FadeEffect_NONE) + nPPTTransitionType = GetTransition(eFadeEffect, nDirection); + + if (ImplGetPropertyValue(mXPagePropSet, "Sound") && (mAny >>= sSoundUrl)) + embedEffectAudio(pFS, sSoundUrl, sSoundRelId, sSoundName); + + bool bOOXmlSpecificTransition = false; + + sal_Int32 nTransition = 0; + const char* pDirection = nullptr; + const char* pOrientation = nullptr; + const char* pThruBlk = nullptr; + const char* pSpokes = nullptr; + + char pSpokesTmp[2] = "0"; + + // p14 + sal_Int32 nTransition14 = 0; + const char* pDirection14 = nullptr; + const char* pInverted = nullptr; + const char* pPattern = nullptr; // diamond or hexagon + + //p15 + const char* pPresetTransition = nullptr; + + if (!nPPTTransitionType) + { + switch (nTransitionType) + { + case animations::TransitionType::BARWIPE: + { + if (nTransitionSubtype == animations::TransitionSubType::FADEOVERCOLOR) + { + nTransition = XML_cut; + pThruBlk = "true"; + bOOXmlSpecificTransition = true; + } + break; + } + case animations::TransitionType::MISCSHAPEWIPE: + { + switch (nTransitionSubtype) + { + case animations::TransitionSubType::TOPTOBOTTOM: // Turn around + nTransition = XML_fade; + nTransition14 = XML_flip; + pDirection14 = "l"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::BOTTOMRIGHT: // Rochade + nTransition = XML_fade; + nTransition14 = XML_switch; + pDirection14 = "r"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::VERTICAL: // Vortex + nTransition = XML_fade; + nTransition14 = XML_vortex; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::HORIZONTAL: // Ripple + nTransition = XML_fade; + nTransition14 = XML_ripple; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::LEFTTORIGHT: // Fall + nTransition = XML_fade; + pPresetTransition = "fallOver"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::CORNERSIN: // Inside turning cube + pInverted = "true"; + [[fallthrough]]; + case animations::TransitionSubType::CORNERSOUT: // Outside turning cube + nTransition = XML_fade; + nTransition14 = XML_prism; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::DIAMOND: // Glitter + nTransition = XML_fade; + nTransition14 = XML_glitter; + pDirection14 = "l"; + pPattern = "hexagon"; + bOOXmlSpecificTransition = true; + break; + case animations::TransitionSubType::HEART: // Honeycomb + nTransition = XML_fade; + nTransition14 = XML_honeycomb; + bOOXmlSpecificTransition = true; + break; + } + break; + } + } + } + + AnimationSpeed animationSpeed = AnimationSpeed_MEDIUM; + const char* speed = nullptr; + sal_Int32 advanceTiming = -1; + sal_Int32 changeType = 0; + + sal_Int32 nTransitionDuration = -1; + bool isTransitionDurationSet = false; + + // try to use TransitionDuration instead of old Speed property + if (ImplGetPropertyValue(mXPagePropSet, "TransitionDuration")) + { + double fTransitionDuration = -1.0; + mAny >>= fTransitionDuration; + if (fTransitionDuration >= 0) + { + nTransitionDuration = fTransitionDuration * 1000.0; + + // override values because in MS formats meaning of fast/medium/slow is different + if (nTransitionDuration <= 500) + { + // fast is default + speed = nullptr; + } + else if (nTransitionDuration >= 1000) + { + speed = "slow"; + } + else + { + speed = "med"; + } + + bool isStandardValue = nTransitionDuration == 500 + || nTransitionDuration == 750 + || nTransitionDuration == 1000; + + if(!isStandardValue) + isTransitionDurationSet = true; + } + } + else if (ImplGetPropertyValue(mXPagePropSet, "Speed")) + { + mAny >>= animationSpeed; + + switch (animationSpeed) + { + default: + case AnimationSpeed_MEDIUM: + speed = "med"; + break; + case AnimationSpeed_SLOW: + speed = "slow"; + break; + case AnimationSpeed_FAST: + break; + } + } + + // check if we resolved what transition to export or time is set + if (!nPPTTransitionType && !bOOXmlSpecificTransition && !isTransitionDurationSet) + return; + + if (ImplGetPropertyValue(mXPagePropSet, "Change")) + mAny >>= changeType; + + // 1 means automatic, 2 half automatic - not sure what it means - at least I don't see it in UI + if (changeType == 1 && ImplGetPropertyValue(mXPagePropSet, "Duration")) + mAny >>= advanceTiming; + + if (!bOOXmlSpecificTransition) + { + switch (nPPTTransitionType) + { + case PPT_TRANSITION_TYPE_BLINDS: + nTransition = XML_blinds; + pDirection = (nDirection == 0) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_CHECKER: + nTransition = XML_checker; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_CIRCLE: + nTransition = XML_circle; + break; + case PPT_TRANSITION_TYPE_COMB: + nTransition = XML_comb; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_COVER: + nTransition = XML_cover; + pDirection = Get8Direction(nDirection); + break; + case PPT_TRANSITION_TYPE_DIAMOND: + nTransition = XML_diamond; + break; + case PPT_TRANSITION_TYPE_DISSOLVE: + nTransition = XML_dissolve; + break; + case PPT_TRANSITION_TYPE_FADE: + nTransition = XML_fade; + pThruBlk = "true"; + break; + case PPT_TRANSITION_TYPE_SMOOTHFADE: + nTransition = XML_fade; + break; + case PPT_TRANSITION_TYPE_NEWSFLASH: + nTransition = XML_newsflash; + break; + case PPT_TRANSITION_TYPE_PLUS: + nTransition = XML_plus; + break; + case PPT_TRANSITION_TYPE_PULL: + nTransition = XML_pull; + pDirection = Get8Direction(nDirection); + break; + case PPT_TRANSITION_TYPE_PUSH: + nTransition = XML_push; + pDirection = GetSideDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_RANDOM: + nTransition = XML_random; + break; + case PPT_TRANSITION_TYPE_RANDOM_BARS: + nTransition = XML_randomBar; + pDirection = (nDirection == 1) ? "vert" : "horz"; + break; + case PPT_TRANSITION_TYPE_SPLIT: + nTransition = XML_split; + pDirection = (nDirection & 1) ? "in" : "out"; + pOrientation = (nDirection < 2) ? "horz" : "vert"; + break; + case PPT_TRANSITION_TYPE_STRIPS: + nTransition = XML_strips; + pDirection = GetCornerDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_WEDGE: + nTransition = XML_wedge; + break; + case PPT_TRANSITION_TYPE_WHEEL: + nTransition = XML_wheel; + if (nDirection != 4 && nDirection <= 9) + { + pSpokesTmp[0] = '0' + nDirection; + pSpokes = pSpokesTmp; + } + break; + case PPT_TRANSITION_TYPE_WIPE: + nTransition = XML_wipe; + pDirection = GetSideDirection(nDirection); + break; + case PPT_TRANSITION_TYPE_ZOOM: + nTransition = XML_zoom; + pDirection = (nDirection == 1) ? "in" : "out"; + break; + case PPT_TRANSITION_TYPE_FLASH: + nTransition14 = XML_flash; + nTransition = XML_fade; + bOOXmlSpecificTransition = true; + break; + // coverity[dead_error_line] - following conditions exist to avoid compiler warning + case PPT_TRANSITION_TYPE_NONE: + default: + nTransition = 0; + break; + } + } + + bool isAdvanceTimingSet = advanceTiming != -1; + if (nTransition14 || pPresetTransition || isTransitionDurationSet) + { + const char* pRequiresNS = (nTransition14 || isTransitionDurationSet) ? "p14" : "p15"; + + pFS->startElement(FSNS(XML_mc, XML_AlternateContent)); + pFS->startElement(FSNS(XML_mc, XML_Choice), XML_Requires, pRequiresNS); + + if(isTransitionDurationSet && isAdvanceTimingSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, OString::number(advanceTiming * 1000), + FSNS(XML_p14, XML_dur), OString::number(nTransitionDuration)); + } + else if(isTransitionDurationSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + FSNS(XML_p14, XML_dur), OString::number(nTransitionDuration)); + } + else if(isAdvanceTimingSet) + { + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, OString::number(advanceTiming * 1000)); + } + else + { + pFS->startElementNS(XML_p, XML_transition, XML_spd, speed); + } + + if (nTransition14) + { + pFS->singleElementNS(XML_p14, nTransition14, + XML_isInverted, pInverted, + XML_dir, pDirection14, + XML_pattern, pPattern); + } + else if (pPresetTransition) + { + pFS->singleElementNS(XML_p15, XML_prstTrans, + XML_prst, pPresetTransition); + } + else if (isTransitionDurationSet && nTransition) + { + pFS->singleElementNS(XML_p, nTransition, + XML_dir, pDirection, + XML_orient, pOrientation, + XML_spokes, pSpokes, + XML_thruBlk, pThruBlk); + } + + if (!sSoundRelId.isEmpty()) + WriteSndAc(pFS, sSoundRelId, sSoundName); + + pFS->endElement(FSNS(XML_p, XML_transition)); + + pFS->endElement(FSNS(XML_mc, XML_Choice)); + pFS->startElement(FSNS(XML_mc, XML_Fallback)); + } + + pFS->startElementNS(XML_p, XML_transition, + XML_spd, speed, + XML_advTm, sax_fastparser::UseIf(OString::number(advanceTiming * 1000), isAdvanceTimingSet)); + + if (nTransition) + { + pFS->singleElementNS(XML_p, nTransition, + XML_dir, pDirection, + XML_orient, pOrientation, + XML_spokes, pSpokes, + XML_thruBlk, pThruBlk); + } + + if (!sSoundRelId.isEmpty()) + WriteSndAc(pFS, sSoundRelId, sSoundName); + + pFS->endElementNS(XML_p, XML_transition); + + if (nTransition14 || pPresetTransition || isTransitionDurationSet) + { + pFS->endElement(FSNS(XML_mc, XML_Fallback)); + pFS->endElement(FSNS(XML_mc, XML_AlternateContent)); + } +} + +static OUString lcl_GetInitials(const OUString& sName) +{ + OUStringBuffer sRet; + + if (!sName.isEmpty()) + { + sRet.append(sName[0]); + sal_Int32 nStart = 0, nOffset; + + while ((nOffset = sName.indexOf(' ', nStart)) != -1) + { + if (nOffset + 1 < sName.getLength()) + sRet.append(sName[ nOffset + 1 ]); + nStart = nOffset + 1; + } + } + + return sRet.makeStringAndClear(); +} + +void PowerPointExport::WriteAuthors() +{ + if (maAuthors.empty()) + return; + + FSHelperPtr pFS = openFragmentStreamWithSerializer("ppt/commentAuthors.xml", + "application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml"); + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::COMMENTAUTHORS), + u"commentAuthors.xml"); + + pFS->startElementNS(XML_p, XML_cmAuthorLst, + FSNS(XML_xmlns, XML_p), getNamespaceURL(OOX_NS(ppt))); + + for (const AuthorsMap::value_type& i : maAuthors) + { + pFS->singleElementNS(XML_p, XML_cmAuthor, + XML_id, OString::number(i.second.nId), + XML_name, i.first, + XML_initials, lcl_GetInitials(i.first), + XML_lastIdx, OString::number(i.second.nLastIndex), + XML_clrIdx, OString::number(i.second.nId)); + } + + pFS->endElementNS(XML_p, XML_cmAuthorLst); +} + +sal_Int32 PowerPointExport::GetAuthorIdAndLastIndex(const OUString& sAuthor, sal_Int32& nLastIndex) +{ + if (maAuthors.count(sAuthor) <= 0) + { + struct AuthorComments aAuthorComments; + + aAuthorComments.nId = maAuthors.size(); + aAuthorComments.nLastIndex = 0; + + maAuthors[ sAuthor ] = aAuthorComments; + } + + nLastIndex = ++maAuthors[ sAuthor ].nLastIndex; + + return maAuthors[ sAuthor ].nId; +} + +void PowerPointExport::WritePresentationProps() +{ + Reference xPresentationSupplier(mXModel, uno::UNO_QUERY); + if (!xPresentationSupplier.is()) + return; + + Reference xPresentationProps(xPresentationSupplier->getPresentation(), + uno::UNO_QUERY); + bool bEndlessVal = xPresentationProps->getPropertyValue("IsEndless").get(); + bool bChangeManually = xPresentationProps->getPropertyValue("IsAutomatic").get(); + OUString sFirstPage = xPresentationProps->getPropertyValue("FirstPage").get(); + OUString sCustomShow = xPresentationProps->getPropertyValue("CustomShow").get(); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/presProps.xml", + "application/vnd.openxmlformats-officedocument.presentationml.presProps+xml"); + + addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::PRESPROPS), u"presProps.xml"); + + pFS->startElementNS(XML_p, XML_presentationPr, PPRNMSS); + + pFS->startElementNS(XML_p, XML_showPr, XML_loop, sax_fastparser::UseIf("1", bEndlessVal), + XML_useTimings, sax_fastparser::UseIf("0", bChangeManually), + XML_showNarration, "1"); + + Reference xDPS(mXModel, uno::UNO_QUERY_THROW); + Reference xDrawPages(xDPS->getDrawPages(), uno::UNO_SET_THROW); + if (!sFirstPage.isEmpty()) + { + sal_Int32 nStartSlide = 1; + sal_Int32 nEndSlide = xDrawPages->getCount(); + for (sal_Int32 i = 0; i < nEndSlide; i++) + { + Reference xDrawPage; + xDrawPages->getByIndex(i) >>= xDrawPage; + Reference xNamed(xDrawPage, uno::UNO_QUERY_THROW); + if (xNamed->getName() == sFirstPage) + { + nStartSlide = i + 1; + break; + } + } + + pFS->singleElementNS(XML_p, XML_sldRg, XML_st, OUString::number(nStartSlide), XML_end, + OUString::number(nEndSlide)); + } + + if (!sCustomShow.isEmpty()) + { + css::uno::Reference + XCustPresentationSupplier(mXModel, css::uno::UNO_QUERY_THROW); + css::uno::Reference mxCustShows; + mxCustShows = XCustPresentationSupplier->getCustomPresentations(); + const css::uno::Sequence aNameSeq(mxCustShows->getElementNames()); + + sal_Int32 nCustShowIndex = 0; + for (sal_Int32 i = 0; i < aNameSeq.getLength(); i++) + { + if (aNameSeq[i] == sCustomShow) + { + nCustShowIndex = i; + break; + } + } + + pFS->singleElementNS(XML_p, XML_custShow, XML_id, OUString::number(nCustShowIndex)); + } + + pFS->endElementNS(XML_p, XML_showPr); + + pFS->endElementNS(XML_p, XML_presentationPr); +} + +bool PowerPointExport::WriteComments(sal_uInt32 nPageNum) +{ + Reference< XAnnotationAccess > xAnnotationAccess(mXDrawPage, uno::UNO_QUERY); + if (xAnnotationAccess.is()) + { + Reference< XAnnotationEnumeration > xAnnotationEnumeration(xAnnotationAccess->createAnnotationEnumeration()); + + if (xAnnotationEnumeration->hasMoreElements()) + { + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/comments/comment" + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.comments+xml"); + + pFS->startElementNS(XML_p, XML_cmLst, + FSNS(XML_xmlns, XML_p), this->getNamespaceURL(OOX_NS(ppt))); + + do + { + Reference< XAnnotation > xAnnotation(xAnnotationEnumeration->nextElement()); + util::DateTime aDateTime(xAnnotation->getDateTime()); + RealPoint2D aRealPoint2D(xAnnotation->getPosition()); + Reference< XText > xText(xAnnotation->getTextRange()); + sal_Int32 nLastIndex; + sal_Int32 nId = GetAuthorIdAndLastIndex(xAnnotation->getAuthor(), nLastIndex); + char cDateTime[sizeof("-32768-65535-65535T65535:65535:65535.4294967295")]; + // reserve enough space for hypothetical max length + + snprintf(cDateTime, sizeof cDateTime, "%02" SAL_PRIdINT32 "-%02" SAL_PRIuUINT32 "-%02" SAL_PRIuUINT32 "T%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ":%02" SAL_PRIuUINT32 ".%09" SAL_PRIuUINT32, sal_Int32(aDateTime.Year), sal_uInt32(aDateTime.Month), sal_uInt32(aDateTime.Day), sal_uInt32(aDateTime.Hours), sal_uInt32(aDateTime.Minutes), sal_uInt32(aDateTime.Seconds), aDateTime.NanoSeconds); + + pFS->startElementNS(XML_p, XML_cm, + XML_authorId, OString::number(nId), + XML_dt, cDateTime, + XML_idx, OString::number(nLastIndex)); + + pFS->singleElementNS(XML_p, XML_pos, + XML_x, OString::number(std::round(convertMm100ToMasterUnit(aRealPoint2D.X * 100))), + XML_y, OString::number(std::round(convertMm100ToMasterUnit(aRealPoint2D.Y * 100)))); + + pFS->startElementNS(XML_p, XML_text); + pFS->write(xText->getString()); + pFS->endElementNS(XML_p, XML_text); + + pFS->endElementNS(XML_p, XML_cm); + + } + while (xAnnotationEnumeration->hasMoreElements()); + + pFS->endElementNS(XML_p, XML_cmLst); + + return true; + } + } + + return false; +} + +void PowerPointExport::WriteVBA() +{ + if (!mbPptm) + return; + + uno::Reference xStorageBasedDocument(getModel(), uno::UNO_QUERY); + if (!xStorageBasedDocument.is()) + return; + + uno::Reference xDocumentStorage = xStorageBasedDocument->getDocumentStorage(); + OUString aMacrosName("_MS_VBA_Macros"); + if (!xDocumentStorage.is() || !xDocumentStorage->hasByName(aMacrosName)) + return; + + const sal_Int32 nOpenMode = embed::ElementModes::READ; + uno::Reference xMacrosStream(xDocumentStorage->openStreamElement(aMacrosName, nOpenMode), uno::UNO_QUERY); + if (!xMacrosStream.is()) + return; + + uno::Reference xOutputStream = openFragmentStream("ppt/vbaProject.bin", "application/vnd.ms-office.vbaProject"); + comphelper::OStorageHelper::CopyInputToOutput(xMacrosStream, xOutputStream); + + // Write the relationship. + addRelation(mPresentationFS->getOutputStream(), oox::getRelationship(Relationship::VBAPROJECT), u"vbaProject.bin"); +} + +void PowerPointExport::WriteModifyVerifier() +{ + Sequence aInfo; + + try + { + Reference xFactory(mXModel, UNO_QUERY); + Reference xDocSettings( + xFactory->createInstance("com.sun.star.document.Settings"), UNO_QUERY); + xDocSettings->getPropertyValue("ModifyPasswordInfo") >>= aInfo; + } + catch (const Exception&) + { + } + + if (aInfo.hasElements()) + { + OUString sAlgorithm, sSalt, sHash; + sal_Int32 nCount = 0; + for (auto& prop : aInfo) + { + if (prop.Name == "algorithm-name") + prop.Value >>= sAlgorithm; + else if (prop.Name == "salt") + prop.Value >>= sSalt; + else if (prop.Name == "iteration-count") + prop.Value >>= nCount; + else if (prop.Name == "hash") + prop.Value >>= sHash; + } + if (!sAlgorithm.isEmpty() && !sSalt.isEmpty() && !sHash.isEmpty()) + { + sal_Int32 nAlgorithmSid = 0; + if (sAlgorithm == "MD2") + nAlgorithmSid = 1; + else if (sAlgorithm == "MD4") + nAlgorithmSid = 2; + else if (sAlgorithm == "MD5") + nAlgorithmSid = 3; + else if (sAlgorithm == "SHA-1") + nAlgorithmSid = 4; + else if (sAlgorithm == "MAC") + nAlgorithmSid = 5; + else if (sAlgorithm == "RIPEMD") + nAlgorithmSid = 6; + else if (sAlgorithm == "RIPEMD-160") + nAlgorithmSid = 7; + else if (sAlgorithm == "HMAC") + nAlgorithmSid = 9; + else if (sAlgorithm == "SHA-256") + nAlgorithmSid = 12; + else if (sAlgorithm == "SHA-384") + nAlgorithmSid = 13; + else if (sAlgorithm == "SHA-512") + nAlgorithmSid = 14; + + if (nAlgorithmSid != 0) + mPresentationFS->singleElementNS(XML_p, XML_modifyVerifier, + XML_cryptProviderType, "rsaAES", + XML_cryptAlgorithmClass, "hash", + XML_cryptAlgorithmType, "typeAny", + XML_cryptAlgorithmSid, OString::number(nAlgorithmSid).getStr(), + XML_spinCount, OString::number(nCount).getStr(), + XML_saltData, sSalt.toUtf8().getStr(), + XML_hashData, sHash.toUtf8().getStr()); + } + } +} + +void PowerPointExport::ImplWriteSlide(sal_uInt32 nPageNum, sal_uInt32 nMasterNum, sal_uInt16 /* nMode */, + bool bHasBackground, Reference< XPropertySet > const& aXBackgroundPropSet) +{ + SAL_INFO("sd.eppt", "write slide: " << nPageNum << "\n----------------"); + + // slides list + if (nPageNum == 0) + mPresentationFS->startElementNS(XML_p, XML_sldIdLst); + + // add explicit relation of presentation to this slide + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDE), + OUStringConcatenation("slides/slide" + OUString::number(nPageNum + 1) +".xml")); + + mPresentationFS->singleElementNS(XML_p, XML_sldId, + XML_id, OString::number(GetNewSlideId()), + FSNS(XML_r, XML_id), sRelId); + + maRelId.push_back(sRelId); + + if (nPageNum == mnPages - 1) + mPresentationFS->endElementNS(XML_p, XML_sldIdLst); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/slides/slide" + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slide+xml"); + + if (mpSlidesFSArray.size() < mnPages) + mpSlidesFSArray.resize(mnPages); + mpSlidesFSArray[ nPageNum ] = pFS; + + const char* pShow = nullptr; + const char* pShowMasterShape = nullptr; + + if (ImplGetPropertyValue(mXPagePropSet, "Visible")) + { + bool bShow(false); + if ((mAny >>= bShow) && !bShow) + pShow = "0"; + } + + if (ImplGetPropertyValue(mXPagePropSet, "IsBackgroundObjectsVisible")) + { + bool bShowMasterShape(false); + if ((mAny >>= bShowMasterShape) && !bShowMasterShape) + pShowMasterShape = "0"; + } + + pFS->startElementNS(XML_p, XML_sld, PNMSS, XML_show, pShow, XML_showMasterSp, pShowMasterShape); + + pFS->startElementNS(XML_p, XML_cSld); + + // background + if (bHasBackground) + { + ImplWriteBackground(pFS, aXBackgroundPropSet); + } + + WriteShapeTree(pFS, NORMAL, false); + + pFS->endElementNS(XML_p, XML_cSld); + + WriteTransition(pFS); + WriteAnimations(pFS, mXDrawPage, *this); + + pFS->endElementNS(XML_p, XML_sld); + + // add implicit relation to slide layout + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDELAYOUT), + OUStringConcatenation("../slideLayouts/slideLayout" + + OUString::number(GetLayoutFileId(GetPPTXLayoutId(GetLayoutOffset(mXPagePropSet)), nMasterNum)) + + ".xml")); + + if (WriteComments(nPageNum)) + // add implicit relation to slide comments + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::COMMENTS), + OUStringConcatenation("../comments/comment" + OUString::number(nPageNum + 1) + ".xml")); + + SAL_INFO("sd.eppt", "----------------"); +} + +void PowerPointExport::ImplWriteNotes(sal_uInt32 nPageNum) +{ + if (!mbCreateNotes || !ContainsOtherShapeThanPlaceholders()) + return; + + SAL_INFO("sd.eppt", "write Notes " << nPageNum << "\n----------------"); + + FSHelperPtr pFS = openFragmentStreamWithSerializer( + "ppt/notesSlides/notesSlide" + + OUString::number(nPageNum + 1) + + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml"); + + pFS->startElementNS(XML_p, XML_notes, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + WriteShapeTree(pFS, NOTICE, false); + + pFS->endElementNS(XML_p, XML_cSld); + + pFS->endElementNS(XML_p, XML_notes); + + // add implicit relation to slide + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDE), + OUStringConcatenation("../slides/slide" + OUString::number(nPageNum + 1) + ".xml")); + + // add slide implicit relation to notes + if (nPageNum < mpSlidesFSArray.size()) + addRelation(mpSlidesFSArray[ nPageNum ]->getOutputStream(), + oox::getRelationship(Relationship::NOTESSLIDE), + OUStringConcatenation("../notesSlides/notesSlide" + OUString::number(nPageNum + 1) + ".xml")); + + // add implicit relation to notes master + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::NOTESMASTER), + u"../notesMasters/notesMaster1.xml"); + + SAL_INFO("sd.eppt", "-----------------"); +} + +void PowerPointExport::AddLayoutIdAndRelation(const FSHelperPtr& pFS, sal_Int32 nLayoutFileId) +{ + // add implicit relation of slide master to slide layout + OUString sRelId = addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDELAYOUT), + OUStringConcatenation("../slideLayouts/slideLayout" + OUString::number(nLayoutFileId) + ".xml")); + + pFS->singleElementNS(XML_p, XML_sldLayoutId, + XML_id, OString::number(GetNewSlideMasterId()), + FSNS(XML_r, XML_id), sRelId); +} + +void PowerPointExport::ImplWriteSlideMaster(sal_uInt32 nPageNum, Reference< XPropertySet > const& aXBackgroundPropSet) +{ + SAL_INFO("sd.eppt", "write master slide: " << nPageNum << "\n--------------"); + + // slides list + if (nPageNum == 0) + mPresentationFS->startElementNS(XML_p, XML_sldMasterIdLst); + + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDEMASTER), + OUStringConcatenation("slideMasters/slideMaster" + OUString::number(nPageNum + 1) + ".xml")); + + mPresentationFS->singleElementNS(XML_p, XML_sldMasterId, + XML_id, OString::number(GetNewSlideMasterId()), + FSNS(XML_r, XML_id), sRelId); + + if (nPageNum == mnMasterPages - 1) + mPresentationFS->endElementNS(XML_p, XML_sldMasterIdLst); + + FSHelperPtr pFS = + openFragmentStreamWithSerializer("ppt/slideMasters/slideMaster" + + OUString::number(nPageNum + 1) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml"); + + SdrPage* pMasterPage = SdPage::getImplementation(mXDrawPage); + svx::Theme* pTheme = nullptr; + if (pMasterPage) + { + pTheme = pMasterPage->getSdrPageProperties().GetTheme(); + } + + // write theme per master + WriteTheme(nPageNum, pTheme); + + // add implicit relation to the presentation theme + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + OUStringConcatenation("../theme/theme" + OUString::number(nPageNum + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_sldMaster, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + if (aXBackgroundPropSet) + ImplWriteBackground(pFS, aXBackgroundPropSet); + WriteShapeTree(pFS, MASTER, true); + + pFS->endElementNS(XML_p, XML_cSld); + + // color map - now it uses colors from hardcoded theme, once we eventually generate theme, this might need update + pFS->singleElementNS(XML_p, XML_clrMap, + XML_bg1, "lt1", + XML_bg2, "lt2", + XML_tx1, "dk1", + XML_tx2, "dk2", + XML_accent1, "accent1", + XML_accent2, "accent2", + XML_accent3, "accent3", + XML_accent4, "accent4", + XML_accent5, "accent5", + XML_accent6, "accent6", + XML_hlink, "hlink", + XML_folHlink, "folHlink"); + + // use master's id type as they have same range, mso does that as well + pFS->startElementNS(XML_p, XML_sldLayoutIdLst); + + for (int i = 0; i < LAYOUT_SIZE; i++) + { + sal_Int32 nLayoutFileId = GetLayoutFileId(i, nPageNum); + if (nLayoutFileId > 0) + { + AddLayoutIdAndRelation(pFS, nLayoutFileId); + } + else + { + ImplWritePPTXLayout(i, nPageNum); + AddLayoutIdAndRelation(pFS, GetLayoutFileId(i, nPageNum)); + } + } + + pFS->endElementNS(XML_p, XML_sldLayoutIdLst); + + pFS->endElementNS(XML_p, XML_sldMaster); + + SAL_INFO("sd.eppt", "----------------"); +} + +sal_Int32 PowerPointExport::GetLayoutFileId(sal_Int32 nOffset, sal_uInt32 nMasterNum) +{ + SAL_INFO("sd.eppt", "GetLayoutFileId offset: " << nOffset << " master: " << nMasterNum); + if (mLayoutInfo[ nOffset ].mnFileIdArray.size() <= nMasterNum) + return 0; + + return mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ]; +} + +void PowerPointExport::ImplWritePPTXLayout(sal_Int32 nOffset, sal_uInt32 nMasterNum) +{ + SAL_INFO("sd.eppt", "write layout: " << nOffset); + + Reference< drawing::XDrawPagesSupplier > xDPS(getModel(), uno::UNO_QUERY); + Reference< drawing::XDrawPages > xDrawPages = xDPS->getDrawPages(); + Reference< drawing::XDrawPage > xSlide = xDrawPages->insertNewByIndex(xDrawPages->getCount()); + +#ifdef DEBUG + if (xSlide.is()) + printf("new page created\n"); +#endif + + Reference< beans::XPropertySet > xPropSet(xSlide, uno::UNO_QUERY); + xPropSet->setPropertyValue("Layout", Any(short(aLayoutInfo[ nOffset ].nType))); +#if OSL_DEBUG_LEVEL > 1 + dump_pset(xPropSet); +#endif + mXPagePropSet.set(xSlide, UNO_QUERY); + mXShapes = xSlide; + + if (mLayoutInfo[ nOffset ].mnFileIdArray.size() < mnMasterPages) + { + mLayoutInfo[ nOffset ].mnFileIdArray.resize(mnMasterPages); + } + + if (mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ] != 0) + return; + + FSHelperPtr pFS + = openFragmentStreamWithSerializer("ppt/slideLayouts/slideLayout" + + OUString::number(mnLayoutFileIdMax) + ".xml", + "application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml"); + + // add implicit relation of slide layout to slide master + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::SLIDEMASTER), + OUStringConcatenation("../slideMasters/slideMaster" + OUString::number(nMasterNum + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_sldLayout, + PNMSS, + XML_type, aLayoutInfo[ nOffset ].sType, + XML_preserve, "1"); + + pFS->startElementNS(XML_p, XML_cSld, + XML_name, aLayoutInfo[ nOffset ].sName); + //pFS->write( MINIMAL_SPTREE ); // TODO: write actual shape tree + WriteShapeTree(pFS, LAYOUT, true); + + pFS->endElementNS(XML_p, XML_cSld); + + pFS->endElementNS(XML_p, XML_sldLayout); + + mLayoutInfo[ nOffset ].mnFileIdArray[ nMasterNum ] = mnLayoutFileIdMax; + + mnLayoutFileIdMax ++; + + xDrawPages->remove(xSlide); +} + +void PowerPointExport::WriteShapeTree(const FSHelperPtr& pFS, PageType ePageType, bool bMaster) +{ + PowerPointShapeExport aDML(pFS, &maShapeMap, this); + aDML.SetMaster(bMaster); + aDML.SetPageType(ePageType); + aDML.SetBackgroundDark(mbIsBackgroundDark); + + pFS->startElementNS(XML_p, XML_spTree); + pFS->write(MAIN_GROUP); + + ResetGroupTable(mXShapes->getCount()); + + while (GetNextGroupEntry()) + { + + sal_uInt32 nGroups = GetGroupsClosed(); + for (sal_uInt32 i = 0; i < nGroups; i++) + { + SAL_INFO("sd.eppt", "leave group"); + } + + if (GetShapeByIndex(GetCurrentGroupIndex(), true)) + { + SAL_INFO("sd.eppt", "mType: " << mType); + const SdrObjGroup* pDiagramCandidate(dynamic_cast(SdrObject::getSdrObjectFromXShape(mXShape))); + const bool bIsDiagram(nullptr != pDiagramCandidate && pDiagramCandidate->isDiagram()); + + if (bIsDiagram) + WriteDiagram(pFS, aDML, mXShape, mnDiagramId++); + else + aDML.WriteShape(mXShape); + } + } + + if ( ePageType == NORMAL || ePageType == LAYOUT ) + WritePlaceholderReferenceShapes(aDML, ePageType); + pFS->endElementNS(XML_p, XML_spTree); +} + +ShapeExport& PowerPointShapeExport::WritePageShape(const Reference< XShape >& xShape, PageType ePageType, bool bPresObj) +{ + if ((ePageType == NOTICE && bPresObj) || ePageType == LAYOUT || ePageType == MASTER) + return WritePlaceholderShape(xShape, SlideImage); + + return WriteTextShape(xShape); +} + +bool PowerPointShapeExport::WritePlaceholder(const Reference< XShape >& xShape, PlaceholderType ePlaceholder, bool bMaster) +{ + SAL_INFO("sd.eppt", "WritePlaceholder " << bMaster << " " << ShapeExport::NonEmptyText(xShape)); + if (!xShape) + return false; + try + { + Reference xShapeProps(xShape, UNO_QUERY); + if (xShapeProps->getPropertyValue("IsPresentationObject").get()) + { + WritePlaceholderShape(xShape, ePlaceholder); + + return true; + } + } + catch (Exception&) + { + return false; + } + return false; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderShape(const Reference< XShape >& xShape, PlaceholderType ePlaceholder) +{ + Reference xProps(xShape, UNO_QUERY); + bool bUseBackground(false); + if (xProps.is() && xProps->getPropertySetInfo()->hasPropertyByName("FillUseSlideBackground")) + xProps->getPropertyValue("FillUseSlideBackground") >>= bUseBackground; + + if (bUseBackground) + mpFS->startElementNS(XML_p, XML_sp, XML_useBgFill, "1"); + else + mpFS->startElementNS(XML_p, XML_sp); + + // non visual shape properties + mpFS->startElementNS(XML_p, XML_nvSpPr); + const OString aPlaceholderID("PlaceHolder " + OString::number(mnShapeIdMax++)); + WriteNonVisualDrawingProperties(xShape, aPlaceholderID.getStr()); + mpFS->startElementNS(XML_p, XML_cNvSpPr); + mpFS->singleElementNS(XML_a, XML_spLocks, XML_noGrp, "1"); + mpFS->endElementNS(XML_p, XML_cNvSpPr); + mpFS->startElementNS(XML_p, XML_nvPr); + + bool bUsePlaceholderIndex + = ePlaceholder == Footer || ePlaceholder == DateAndTime || ePlaceholder == SlideNumber; + const char* pType = getPlaceholderTypeName(ePlaceholder); + + SAL_INFO("sd.eppt", "write placeholder " << pType); + if (bUsePlaceholderIndex) + { + mpFS->singleElementNS( + XML_p, XML_ph, XML_type, pType, XML_idx, + OString::number( + static_cast(GetFB())->CreateNewPlaceholderIndex(xShape))); + } + else + { + if ((mePageType == PageType::LAYOUT || mePageType == PageType::NORMAL) + && ePlaceholder == Outliner) + mpFS->singleElementNS(XML_p, XML_ph); + else + mpFS->singleElementNS(XML_p, XML_ph, XML_type, pType); + } + mpFS->endElementNS(XML_p, XML_nvPr); + mpFS->endElementNS(XML_p, XML_nvSpPr); + + // visual shape properties + mpFS->startElementNS(XML_p, XML_spPr); + WriteShapeTransformation(xShape, XML_a); + WritePresetShape("rect"); + if (xProps.is()) + { + WriteBlipFill(xProps, "Graphic"); + // Do not forget to export the visible properties. + WriteFill( xProps ); + WriteOutline( xProps ); + WriteShapeEffects( xProps ); + + bool bHas3DEffectinShape = false; + uno::Sequence grabBag; + if (xProps->getPropertySetInfo()->hasPropertyByName("InteropGrabBag")) + xProps->getPropertyValue("InteropGrabBag") >>= grabBag; + + for (auto const& it : std::as_const(grabBag)) + if (it.Name == "3DEffectProperties") + bHas3DEffectinShape = true; + + if( bHas3DEffectinShape) + Write3DEffects( xProps, /*bIsText=*/false ); + } + mpFS->endElementNS(XML_p, XML_spPr); + + WriteTextBox(xShape, XML_p, /*bWritePropertiesAsLstStyles=*/bUsePlaceholderIndex); + + mpFS->endElementNS(XML_p, XML_sp); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceShape( + PlaceholderType ePlaceholder, sal_Int32 nReferencedPlaceholderIdx, PageType ePageType, + const Reference& rXPagePropSet) +{ + mpFS->startElementNS(XML_p, XML_sp); + + // non visual shape properties + mpFS->startElementNS(XML_p, XML_nvSpPr); + const OString aPlaceholderID("PlaceHolder " + OString::number(mnShapeIdMax++)); + GetFS()->singleElementNS(XML_p, XML_cNvPr, XML_id, OString::number(mnShapeIdMax), XML_name, + aPlaceholderID.getStr()); + + mpFS->startElementNS(XML_p, XML_cNvSpPr); + mpFS->singleElementNS(XML_a, XML_spLocks, XML_noGrp, "1"); + mpFS->endElementNS(XML_p, XML_cNvSpPr); + mpFS->startElementNS(XML_p, XML_nvPr); + + const char* pType = getPlaceholderTypeName(ePlaceholder); + mpFS->singleElementNS(XML_p, XML_ph, XML_type, pType, XML_idx, + OString::number(nReferencedPlaceholderIdx)); + mpFS->endElementNS(XML_p, XML_nvPr); + mpFS->endElementNS(XML_p, XML_nvSpPr); + + // visual shape properties + mpFS->startElementNS(XML_p, XML_spPr); + mpFS->endElementNS(XML_p, XML_spPr); + + WritePlaceholderReferenceTextBody(ePlaceholder, ePageType, rXPagePropSet); + + mpFS->endElementNS(XML_p, XML_sp); + + return *this; +} + +ShapeExport& PowerPointShapeExport::WritePlaceholderReferenceTextBody( + PlaceholderType ePlaceholder, PageType ePageType, const Reference xPagePropSet) +{ + mpFS->startElementNS(XML_p, XML_txBody); + mpFS->singleElementNS(XML_a, XML_bodyPr); + mpFS->startElementNS(XML_a, XML_p); + + switch (ePlaceholder) + { + case Header: + break; + case Footer: + { + OUString aFooterText; + if (ePageType == LAYOUT) + { + aFooterText = "Footer"; + } + else + { + xPagePropSet->getPropertyValue("FooterText") >>= aFooterText; + } + mpFS->startElementNS(XML_a, XML_r); + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aFooterText); + mpFS->endElementNS(XML_a, XML_t); + mpFS->endElementNS(XML_a, XML_r); + break; + } + case SlideNumber: + { + OUString aSlideNum; + sal_Int32 nSlideNum = 0; + if (ePageType == LAYOUT) + { + aSlideNum = "<#>"; + } + else + { + xPagePropSet->getPropertyValue("Number") >>= nSlideNum; + aSlideNum = OUString::number(nSlideNum); + } + OString aUUID(comphelper::xml::generateGUIDString()); + mpFS->startElementNS(XML_a, XML_fld, XML_id, aUUID.getStr(), XML_type, "slidenum"); + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aSlideNum); + mpFS->endElementNS(XML_a, XML_t); + mpFS->endElementNS(XML_a, XML_fld); + break; + } + case DateAndTime: + { + OUString aDateTimeType = "datetime1"; + bool bIsDateTimeFixed = false; + xPagePropSet->getPropertyValue("IsDateTimeFixed") >>= bIsDateTimeFixed; + + OUString aDateTimeText = "Date"; + const LanguageTag& rLanguageTag = Application::GetSettings().GetLanguageTag(); + + if(ePageType != LAYOUT && !bIsDateTimeFixed) + { + sal_Int32 nDateTimeFormat = 0; + xPagePropSet->getPropertyValue("DateTimeFormat") >>= nDateTimeFormat; + + // 4 LSBs represent the date + SvxDateFormat eDate = static_cast(nDateTimeFormat & 0x0f); + // the 4 bits after the date bits represent the time + SvxTimeFormat eTime = static_cast(nDateTimeFormat >> 4); + aDateTimeType = GetDatetimeTypeFromDateTime(eDate, eTime); + + if (aDateTimeType == "datetime") + aDateTimeType = "datetime1"; + + ::DateTime aDateTime( ::DateTime::SYSTEM ); + + aDateTimeText = SvxDateTimeField::GetFormatted( + aDateTime, aDateTime, eDate, + eTime, *(SD_MOD()->GetNumberFormatter()), + rLanguageTag.getLanguageType()); + } + + if(!bIsDateTimeFixed) + { + OString aUUID(comphelper::xml::generateGUIDString()); + mpFS->startElementNS(XML_a, XML_fld, XML_id, aUUID.getStr(), XML_type, aDateTimeType); + } + else + { + xPagePropSet->getPropertyValue("DateTimeText") >>= aDateTimeText; + mpFS->startElementNS(XML_a, XML_r); + } + + mpFS->startElementNS(XML_a, XML_rPr, XML_lang, rLanguageTag.getBcp47MS()); + mpFS->endElementNS(XML_a, XML_rPr); + + mpFS->startElementNS(XML_a, XML_t); + mpFS->writeEscaped(aDateTimeText); + mpFS->endElementNS(XML_a, XML_t); + + mpFS->endElementNS(XML_a, bIsDateTimeFixed ? XML_r : XML_fld); + break; + } + default: + SAL_INFO("sd.eppt", "warning: no defined textbody for referenced placeholder type: " + << ePlaceholder); + } + mpFS->endElementNS(XML_a, XML_p); + mpFS->endElementNS(XML_p, XML_txBody); + + return *this; +} + +#define SYS_COLOR_SCHEMES " \ + \ + \ + \ + \ + " + +#define MINIMAL_THEME " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " + +void PowerPointExport::WriteDefaultColorSchemes(const FSHelperPtr& pFS) +{ + for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++) + { + OUString sName = PredefinedClrNames[static_cast(nId)]; + sal_Int32 nColor = 0; + + switch (nId) + { + case PredefinedClrSchemeId::dk2: + nColor = 0x1F497D; + break; + case PredefinedClrSchemeId::lt2: + nColor = 0xEEECE1; + break; + case PredefinedClrSchemeId::accent1: + nColor = 0x4F81BD; + break; + case PredefinedClrSchemeId::accent2: + nColor = 0xC0504D; + break; + case PredefinedClrSchemeId::accent3: + nColor = 0x9BBB59; + break; + case PredefinedClrSchemeId::accent4: + nColor = 0x8064A2; + break; + case PredefinedClrSchemeId::accent5: + nColor = 0x4BACC6; + break; + case PredefinedClrSchemeId::accent6: + nColor = 0xF79646; + break; + case PredefinedClrSchemeId::hlink: + nColor = 0x0000FF; + break; + case PredefinedClrSchemeId::folHlink: + nColor = 0x800080; + break; + } + + OUString sOpenColorScheme = ""; + pFS->write(sOpenColorScheme); + + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor)); + + OUString sCloseColorScheme = ""; + pFS->write(sCloseColorScheme); + } +} + +bool PowerPointExport::WriteColorSets(const FSHelperPtr& pFS, svx::Theme* pTheme) +{ + static std::map aPredefinedClrTokens = + { + { dk1, XML_dk1 }, + { lt1, XML_lt1 }, + { dk2, XML_dk2 }, + { lt2, XML_lt2 }, + { accent1, XML_accent1 }, + { accent2, XML_accent2 }, + { accent3, XML_accent3 }, + { accent4, XML_accent4 }, + { accent5, XML_accent5 }, + { accent6, XML_accent6 }, + { hlink, XML_hlink }, + { folHlink, XML_folHlink } + }; + + if (!pTheme) + { + return false; + } + + svx::ColorSet* pColorSet = pTheme->GetColorSet(); + if (!pColorSet) + { + return false; + } + + for (int nId = PredefinedClrSchemeId::dk1; nId < PredefinedClrSchemeId::Count; nId++) + { + sal_Int32 nToken = aPredefinedClrTokens[static_cast(nId)]; + pFS->startElementNS(XML_a, nToken); + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(static_cast(pColorSet->getColor(nId)))); + pFS->endElementNS(XML_a, nToken); + } + + return true; +} + +bool PowerPointExport::WriteColorSchemes(const FSHelperPtr& pFS, const OUString& rThemePath) +{ + try + { + uno::Reference xDocProps(getModel(), uno::UNO_QUERY); + if (xDocProps.is()) + { + uno::Reference xPropsInfo = xDocProps->getPropertySetInfo(); + + static const OUStringLiteral aGrabBagPropName = u"InteropGrabBag"; + if (xPropsInfo.is() && xPropsInfo->hasPropertyByName(aGrabBagPropName)) + { + comphelper::SequenceAsHashMap aGrabBag(xDocProps->getPropertyValue(aGrabBagPropName)); + uno::Sequence aCurrentTheme; + + aGrabBag.getValue(rThemePath) >>= aCurrentTheme; + + if (!aCurrentTheme.hasElements()) + return false; + + // Order is important + for (int nId = PredefinedClrSchemeId::dk2; nId != PredefinedClrSchemeId::Count; nId++) + { + OUString sName = PredefinedClrNames[static_cast(nId)]; + sal_Int32 nColor = 0; + + for (auto aIt = std::cbegin(aCurrentTheme); aIt != std::cend(aCurrentTheme); aIt++) + { + if (aIt->Name == sName) + { + aIt->Value >>= nColor; + break; + } + } + + OUString sOpenColorScheme =""; + pFS->write(sOpenColorScheme); + + pFS->singleElementNS(XML_a, XML_srgbClr, XML_val, I32SHEX(nColor)); + + OUString sCloseColorScheme = ""; + pFS->write(sCloseColorScheme); + } + + // TODO: write complete color schemes & only if successful, protection against partial export + return true; + } + } + } + catch (const uno::Exception&) + { + SAL_WARN("writerfilter", "Failed to save documents grab bag"); + } + + return false; +} + +void PowerPointExport::WriteTheme(sal_Int32 nThemeNum, svx::Theme* pTheme) +{ + OUString sThemePath = "ppt/theme/theme" + OUString::number(nThemeNum + 1) + ".xml"; + + FSHelperPtr pFS = openFragmentStreamWithSerializer(sThemePath, + "application/vnd.openxmlformats-officedocument.theme+xml"); + + OUString aThemeName("Office Theme"); + if (pTheme) + { + aThemeName = pTheme->GetName(); + } + pFS->startElementNS(XML_a, XML_theme, + FSNS(XML_xmlns, XML_a), this->getNamespaceURL(OOX_NS(dml)), + XML_name, aThemeName); + + pFS->startElementNS(XML_a, XML_themeElements); + OUString aColorSchemeName("Office"); + if (pTheme) + { + svx::ColorSet* pColorSet = pTheme->GetColorSet(); + if (pColorSet) + { + aColorSchemeName = pColorSet->getName(); + } + } + pFS->startElementNS(XML_a, XML_clrScheme, XML_name, aColorSchemeName); + + if (!WriteColorSets(pFS, pTheme)) + { + pFS->write(SYS_COLOR_SCHEMES); + if (!WriteColorSchemes(pFS, sThemePath)) + { + // if style is not defined, try to use first one + if (!WriteColorSchemes(pFS, "ppt/theme/theme1.xml")) + { + // color schemes are required - use default values + WriteDefaultColorSchemes(pFS); + } + } + } + + pFS->endElementNS(XML_a, XML_clrScheme); + + // export remaining part + pFS->write(MINIMAL_THEME); + + pFS->endElementNS(XML_a, XML_themeElements); + pFS->endElementNS(XML_a, XML_theme); +} + +bool PowerPointExport::ImplCreateDocument() +{ + mbCreateNotes = false; + + for (sal_uInt32 i = 0; i < mnPages; i++) + { + if (!GetPageByIndex(i, NOTICE)) + return false; + + if (ContainsOtherShapeThanPlaceholders()) + { + mbCreateNotes = true; + break; + } + } + + return true; +} + +void PowerPointExport::WriteNotesMaster() +{ + SAL_INFO("sd.eppt", "write Notes master\n---------------"); + + mPresentationFS->startElementNS(XML_p, XML_notesMasterIdLst); + + OUString sRelId = addRelation(mPresentationFS->getOutputStream(), + oox::getRelationship(Relationship::NOTESMASTER), + u"notesMasters/notesMaster1.xml"); + + mPresentationFS->singleElementNS(XML_p, XML_notesMasterId, + FSNS(XML_r, XML_id), sRelId); + + mPresentationFS->endElementNS(XML_p, XML_notesMasterIdLst); + + FSHelperPtr pFS = + openFragmentStreamWithSerializer("ppt/notesMasters/notesMaster1.xml", + "application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml"); + // write theme per master + WriteTheme(mnMasterPages, nullptr); + + // add implicit relation to the presentation theme + addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::THEME), + OUStringConcatenation("../theme/theme" + OUString::number(mnMasterPages + 1) + ".xml")); + + pFS->startElementNS(XML_p, XML_notesMaster, PNMSS); + + pFS->startElementNS(XML_p, XML_cSld); + + Reference< XPropertySet > aXBackgroundPropSet; + if (ImplGetPropertyValue(mXPagePropSet, "Background") && + (mAny >>= aXBackgroundPropSet)) + ImplWriteBackground(pFS, aXBackgroundPropSet); + + WriteShapeTree(pFS, NOTICE, true); + + pFS->endElementNS(XML_p, XML_cSld); + + // color map - now it uses colors from hardcoded theme, once we eventually generate theme, this might need update + pFS->singleElementNS(XML_p, XML_clrMap, + XML_bg1, "lt1", + XML_bg2, "lt2", + XML_tx1, "dk1", + XML_tx2, "dk2", + XML_accent1, "accent1", + XML_accent2, "accent2", + XML_accent3, "accent3", + XML_accent4, "accent4", + XML_accent5, "accent5", + XML_accent6, "accent6", + XML_hlink, "hlink", + XML_folHlink, "folHlink"); + + pFS->endElementNS(XML_p, XML_notesMaster); + + SAL_INFO("sd.eppt", "----------------"); +} + +void PowerPointExport::embedEffectAudio(const FSHelperPtr& pFS, const OUString& sUrl, OUString& sRelId, OUString& sName) +{ + comphelper::LifecycleProxy aProxy; + + if (!sUrl.endsWithIgnoreAsciiCase(".wav")) + return; + + uno::Reference xAudioStream; + try + { + if (sUrl.startsWith("vnd.sun.star.Package:")) + { + uno::Reference xStorageBasedDocument(getModel(), uno::UNO_QUERY); + if (!xStorageBasedDocument.is()) + return; + + uno::Reference xDocumentStorage = xStorageBasedDocument->getDocumentStorage(); + if (!xDocumentStorage.is()) + return; + + uno::Reference xStream = comphelper::OStorageHelper::GetStreamAtPackageURL(xDocumentStorage, sUrl, + css::embed::ElementModes::READ, aProxy); + + if (xStream.is()) + xAudioStream = xStream->getInputStream(); + } + else + xAudioStream = comphelper::OStorageHelper::GetInputStreamFromURL(sUrl, getComponentContext()); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd", "PowerPointExport::embedEffectAudio"); + } + + if (!xAudioStream.is()) + return; + + int nLastSlash = sUrl.lastIndexOf('/'); + sName = sUrl.copy(nLastSlash >= 0 ? nLastSlash + 1 : 0); + + OUString sPath = "../media/" + sName; + sRelId = addRelation(pFS->getOutputStream(), + oox::getRelationship(Relationship::AUDIO), sPath); + + uno::Reference xOutputStream = openFragmentStream(sPath.replaceAt(0, 2, u"/ppt"), + "audio/x-wav"); + + comphelper::OStorageHelper::CopyInputToOutput(xAudioStream, xOutputStream); +} + +sal_Int32 PowerPointExport::GetShapeID(const Reference& rXShape) +{ + return ShapeExport::GetShapeID(rXShape, &maShapeMap); +} + +sal_Int32 PowerPointExport::GetNextAnimationNodeID() +{ + return mnAnimationNodeIdMax++; +} + +bool PowerPointExport::ImplCreateMainNotes() +{ + if (mbCreateNotes) + WriteNotesMaster(); + + return true; +} + +OUString PowerPointExport::getImplementationName() +{ + return "com.sun.star.comp.Impress.oox.PowerPointExport"; +} + +void PowerPointExport::WriteDiagram(const FSHelperPtr& pFS, PowerPointShapeExport& rDML, const css::uno::Reference& rXShape, int nDiagramId) +{ + SAL_INFO("sd.eppt", "writing Diagram " + OUString::number(nDiagramId)); + pFS->startElementNS(XML_p, XML_graphicFrame); + rDML.WriteDiagram(rXShape, nDiagramId); + pFS->endElementNS(XML_p, XML_graphicFrame); +} + +void PowerPointExport::WritePlaceholderReferenceShapes(PowerPointShapeExport& rDML, PageType ePageType) +{ + bool bCheckProps = ePageType == NORMAL; + Reference xShape; + Any aAny; + OUString aText; + if (ePageType == LAYOUT + || (bCheckProps && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsFooterVisible", true) + && aAny == true && GetPropertyValue(aAny, mXPagePropSet, "FooterText", true) + && (aAny >>= aText) && !aText.isEmpty())) + { + if ((xShape = GetReferencedPlaceholderXShape(Footer, ePageType))) + rDML.WritePlaceholderReferenceShape(Footer, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } + + if (ePageType == LAYOUT + || (bCheckProps + && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsPageNumberVisible", true) + && aAny == true)) + { + if ((xShape = GetReferencedPlaceholderXShape(SlideNumber, ePageType))) + rDML.WritePlaceholderReferenceShape(SlideNumber, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } + + if (ePageType == LAYOUT + || (bCheckProps + && PropValue::GetPropertyValue(aAny, mXPagePropSet, "IsDateTimeVisible", true) + && aAny == true + && ((GetPropertyValue(aAny, mXPagePropSet, "DateTimeText", true) && (aAny >>= aText) + && !aText.isEmpty()) + || mXPagePropSet->getPropertyValue("IsDateTimeFixed") == false))) + { + if ((xShape = GetReferencedPlaceholderXShape(DateAndTime, ePageType))) + rDML.WritePlaceholderReferenceShape(DateAndTime, + maPlaceholderShapeToIndexMap.find(xShape)->second, + ePageType, mXPagePropSet); + } +} + +sal_Int32 PowerPointExport::CreateNewPlaceholderIndex(const css::uno::Reference &rXShape) +{ + maPlaceholderShapeToIndexMap.insert({rXShape, mnPlaceholderIndexMax}); + return mnPlaceholderIndexMax++; +} + +Reference PowerPointExport::GetReferencedPlaceholderXShape(const PlaceholderType eType, + PageType ePageType) const +{ + PresObjKind ePresObjKind = PresObjKind::NONE; + switch (eType) + { + case oox::core::None: + break; + case oox::core::SlideImage: + break; + case oox::core::Notes: + break; + case oox::core::Header: + ePresObjKind = PresObjKind::Header; + break; + case oox::core::Footer: + ePresObjKind = PresObjKind::Footer; + break; + case oox::core::SlideNumber: + ePresObjKind = PresObjKind::SlideNumber; + break; + case oox::core::DateAndTime: + ePresObjKind = PresObjKind::DateTime; + break; + case oox::core::Outliner: + break; + case oox::core::Title: + ePresObjKind = PresObjKind::Title; + break; + case oox::core::Subtitle: + break; + } + if (ePresObjKind != PresObjKind::NONE) + { + SdPage* pMasterPage; + if (ePageType == LAYOUT) + { + // since Layout pages do not have drawpages themselves - mXDrawPage is still the master they reference to.. + pMasterPage = SdPage::getImplementation(mXDrawPage); + } + else + { + pMasterPage + = &static_cast(SdPage::getImplementation(mXDrawPage)->TRG_GetMasterPage()); + } + if (SdrObject* pMasterFooter = pMasterPage->GetPresObj(ePresObjKind)) + return GetXShapeForSdrObject(pMasterFooter); + } + return nullptr; +} + +// UNO component +extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface* +css_comp_Impress_oox_PowerPointExport(uno::XComponentContext* rxCtxt, + uno::Sequence const& rArguments) +{ + return cppu::acquire(new PowerPointExport(rxCtxt, rArguments)); +} + +#if OSL_DEBUG_LEVEL > 1 +void dump_pset(Reference< XPropertySet > const& rXPropSet) +{ + Reference< XPropertySetInfo > info = rXPropSet->getPropertySetInfo(); + Sequence< beans::Property > props = info->getProperties(); + + for (int i=0; i < props.getLength(); i++) + { + OString name = OUStringToOString(props [i].Name, RTL_TEXTENCODING_UTF8); + + Any value = rXPropSet->getPropertyValue(props [i].Name); + + OUString strValue; + sal_Int32 intValue; + bool boolValue; + RectanglePoint pointValue; + + if (value >>= strValue) + SAL_INFO("sd.eppt", name << " = \"" << strValue << "\""); + else if (value >>= intValue) + SAL_INFO("sd.eppt", name << " = " << intValue << "(hex : " << std::hex << intValue << ")"); + else if (value >>= boolValue) + SAL_INFO("sd.eppt", name << " = " << boolValue << " (bool)"); + else if (value >>= pointValue) + SAL_INFO("sd.eppt", name << " = " << static_cast(pointValue) << " (RectanglePoint)"); + else + SAL_INFO("sd.eppt", "??? "); + } +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-grouptable.cxx b/sd/source/filter/eppt/pptx-grouptable.cxx new file mode 100644 index 000000000..bf91f2fb6 --- /dev/null +++ b/sd/source/filter/eppt/pptx-grouptable.cxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "grouptable.hxx" + +using ::com::sun::star::container::XIndexAccess; + +GroupTable::GroupTable() + : mnIndex(0) + , mnGroupsClosed(0) +{ + mvGroupEntry.reserve(32); +} + +GroupTable::~GroupTable() +{ +} + +bool GroupTable::EnterGroup( css::uno::Reference< css::container::XIndexAccess > const & rXIndexAccessRef ) +{ + bool bRet = false; + if ( rXIndexAccessRef.is() ) + { + GroupEntry aNewGroup( rXIndexAccessRef ); + if ( aNewGroup.mnCount ) + { + mvGroupEntry.push_back( std::move(aNewGroup) ); + bRet = true; + } + } + return bRet; +} + +sal_uInt32 GroupTable::GetGroupsClosed() +{ + sal_uInt32 nRet = mnGroupsClosed; + mnGroupsClosed = 0; + return nRet; +} + +void GroupTable::ClearGroupTable() +{ + mvGroupEntry.clear(); +} + +void GroupTable::ResetGroupTable( sal_uInt32 nCount ) +{ + ClearGroupTable(); + mvGroupEntry.push_back( GroupEntry( nCount ) ); +} + +bool GroupTable::GetNextGroupEntry() +{ + while ( !mvGroupEntry.empty() ) + { + mnIndex = mvGroupEntry.back().mnCurrentPos++; + + if ( mvGroupEntry.back().mnCount > mnIndex ) + return true; + + mvGroupEntry.pop_back(); + + if ( !mvGroupEntry.empty() ) + mnGroupsClosed++; + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-stylesheet.cxx b/sd/source/filter/eppt/pptx-stylesheet.cxx new file mode 100644 index 000000000..459020278 --- /dev/null +++ b/sd/source/filter/eppt/pptx-stylesheet.cxx @@ -0,0 +1,489 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "epptbase.hxx" +#include "epptdef.hxx" +#include "text.hxx" +#include +#include +#include +#include + +using namespace ::com::sun::star; + +PPTExCharSheet::PPTExCharSheet( int nInstance ) +{ + sal_uInt16 nFontHeight = 24; + + for ( int nDepth = 0; nDepth < 5; nDepth++ ) + { + PPTExCharLevel& rLev = maCharLevel[ nDepth ]; + switch ( nInstance ) + { + case EPP_TEXTTYPE_Title : + case EPP_TEXTTYPE_CenterTitle : + nFontHeight = 44; + break; + case EPP_TEXTTYPE_Body : + case EPP_TEXTTYPE_CenterBody : + case EPP_TEXTTYPE_HalfBody : + case EPP_TEXTTYPE_QuarterBody : + { + switch ( nDepth ) + { + case 0 : nFontHeight = 32; break; + case 1 : nFontHeight = 28; break; + case 2 : nFontHeight = 24; break; + default :nFontHeight = 20; break; + } + } + break; + case EPP_TEXTTYPE_Notes : + nFontHeight = 12; + break; + case EPP_TEXTTYPE_notUsed : + case EPP_TEXTTYPE_Other : + nFontHeight = 24; + break; + } + rLev.mnFlags = 0; + rLev.mnFont = 0; + rLev.mnAsianOrComplexFont = 0xffff; + rLev.mnFontHeight = nFontHeight; + rLev.mnFontColor = 0; + rLev.mnEscapement = 0; + } +} + +void PPTExCharSheet::SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + FontCollection& rFontCollection, int nLevel ) +{ + PortionObj aPortionObj( rXPropSet, rFontCollection ); + + PPTExCharLevel& rLev = maCharLevel[ nLevel ]; + + if ( aPortionObj.meCharColor == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnFontColor = Color(ColorTransparency, aPortionObj.mnCharColor); + if ( aPortionObj.meCharEscapement == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnEscapement = aPortionObj.mnCharEscapement; + if ( aPortionObj.meCharHeight == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnFontHeight = aPortionObj.mnCharHeight; + if ( aPortionObj.meFontName == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnFont = aPortionObj.mnFont; + if ( aPortionObj.meAsianOrComplexFont == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnAsianOrComplexFont = aPortionObj.mnAsianOrComplexFont; + rLev.mnFlags = aPortionObj.mnCharAttr; +} + +void PPTExCharSheet::Write( SvStream& rSt, sal_uInt16 nLev, bool bSimpleText, + const css::uno::Reference< css::beans::XPropertySet > & rPagePropSet ) +{ + const PPTExCharLevel& rLev = maCharLevel[ nLev ]; + + sal_uInt32 nCharFlags = 0xefffff; + if ( bSimpleText ) + nCharFlags = 0x7ffff; + + rSt.WriteUInt32( nCharFlags ) + .WriteUInt16( rLev.mnFlags ) + .WriteUInt16( rLev.mnFont ); + + Color nFontColor = rLev.mnFontColor; + if ( nFontColor == COL_AUTO ) + { + bool bIsDark = false; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, rPagePropSet, "IsBackgroundDark", true ) ) + aAny >>= bIsDark; + nFontColor = Color(ColorTransparency, bIsDark ? 0xffffff : 0x000000); + } + nFontColor.SetAlpha(1); + if ( bSimpleText ) + { + rSt.WriteUInt16( rLev.mnFontHeight ) + .WriteUInt32( sal_uInt32(nFontColor) ); + } + else + { + rSt.WriteUInt16( rLev.mnAsianOrComplexFont ) + .WriteUInt16( 0xffff ) // unknown + .WriteUInt16( 0xffff ) // unknown + .WriteUInt16( rLev.mnFontHeight ) + .WriteUInt32( sal_uInt32(nFontColor) ) + .WriteUInt16( rLev.mnEscapement ); + } +} + +PPTExParaSheet::PPTExParaSheet( int nInstance, sal_uInt16 nDefaultTab, PPTExBulletProvider* pProv ) : + pBuProv ( pProv ), + mnInstance ( nInstance ) +{ + bool bHasBullet = false; + + sal_uInt16 nUpperDist = 0; + sal_uInt16 nBulletChar = 0x2022; + sal_uInt16 nBulletOfs = 0; + sal_uInt16 nTextOfs = 0; + + for ( int nDepth = 0; nDepth < 5; nDepth++ ) + { + PPTExParaLevel& rLev = maParaLevel[ nDepth ]; + switch ( nInstance ) + { + case EPP_TEXTTYPE_Title : + case EPP_TEXTTYPE_CenterTitle : + break; + case EPP_TEXTTYPE_Body : + case EPP_TEXTTYPE_CenterBody : + case EPP_TEXTTYPE_HalfBody : + case EPP_TEXTTYPE_QuarterBody : + { + bHasBullet = true; + nUpperDist = 0x14; + } + break; + case EPP_TEXTTYPE_Notes : + nUpperDist = 0x1e; + break; + + } + switch ( nDepth ) + { + case 0 : + { + nBulletChar = 0x2022; + nBulletOfs = 0; + nTextOfs = bHasBullet ? 0xd8 : 0; + } + break; + case 1 : + { + nBulletChar = 0x2013; + nBulletOfs = 0x120; + nTextOfs = 0x1d4; + } + break; + case 2 : + { + nBulletChar = 0x2022; + nBulletOfs = 0x240; + nTextOfs = 0x2d0; + } + break; + case 3 : + { + nBulletChar = 0x2013; + nBulletOfs = 0x360; + nTextOfs = 0x3f0; + } + break; + case 4 : + { + nBulletChar = 0xbb; + nBulletOfs = 0x480; + nTextOfs = 0x510; + } + break; + } + rLev.mbIsBullet = bHasBullet; + rLev.mnBulletChar = nBulletChar; + rLev.mnBulletFont = 0; + rLev.mnBulletHeight = 100; + rLev.mnBulletColor = 0; + rLev.mnAdjust = 0; + rLev.mnLineFeed = 100; + rLev.mnLowerDist = 0; + rLev.mnUpperDist = nUpperDist; + rLev.mnTextOfs = nTextOfs; + rLev.mnBulletOfs = nBulletOfs; + rLev.mnDefaultTab = nDefaultTab; + rLev.mnAsianSettings = 2; + rLev.mnBiDi = 0; + + rLev.mbExtendedBulletsUsed = false; + rLev.mnBulletId = 0xffff; + rLev.mnBulletStart = 0; + rLev.mnMappedNumType = 0; + rLev.mnNumberingType = 0; + } +} + +void PPTExParaSheet::SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + FontCollection& rFontCollection, int nLevel, const PPTExCharLevel& rCharLevel ) +{ + ParagraphObj aParagraphObj( rXPropSet, pBuProv ); + aParagraphObj.CalculateGraphicBulletSize( rCharLevel.mnFontHeight ); + PPTExParaLevel& rLev = maParaLevel[ nLevel ]; + + if ( aParagraphObj.meTextAdjust == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnAdjust = aParagraphObj.mnTextAdjust; + if ( aParagraphObj.meLineSpacing == css::beans::PropertyState_DIRECT_VALUE ) + { + sal_Int16 nLineSpacing = aParagraphObj.mnLineSpacing; + if ( nLineSpacing > 0 ) // if nLinespacing is < 0 the linespacing is an absolute spacing + { + bool bFixedLineSpacing = false; + uno::Any aAny = rXPropSet->getPropertyValue("FontIndependentLineSpacing"); + if( !(aAny >>= bFixedLineSpacing) || !bFixedLineSpacing ) + { + const FontCollectionEntry* pDesc = rFontCollection.GetById( rCharLevel.mnFont ); + if ( pDesc ) + nLineSpacing = static_cast( static_cast(nLineSpacing) * pDesc->Scaling + 0.5 ); + } + } + else + { + if ( rCharLevel.mnFontHeight > static_cast( static_cast(-nLineSpacing) * 0.001 * 72.0 / 2.54 ) ) // 1/100mm to point + { + const FontCollectionEntry* pDesc = rFontCollection.GetById( rCharLevel.mnFont ); + if ( pDesc ) + nLineSpacing = static_cast( 100.0 * pDesc->Scaling + 0.5 ); + else + nLineSpacing = 100; + } + else + nLineSpacing = static_cast(convertMm100ToMasterUnit(nLineSpacing)); + } + rLev.mnLineFeed = nLineSpacing; + } + if ( aParagraphObj.meLineSpacingBottom == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnLowerDist = aParagraphObj.mnLineSpacingBottom; + if ( aParagraphObj.meLineSpacingTop == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnUpperDist = aParagraphObj.mnLineSpacingTop; + if ( aParagraphObj.meForbiddenRules == css::beans::PropertyState_DIRECT_VALUE ) + { + rLev.mnAsianSettings &=~1; + if ( aParagraphObj.mbForbiddenRules ) + rLev.mnAsianSettings |= 1; + } + if ( aParagraphObj.meParagraphPunctation == css::beans::PropertyState_DIRECT_VALUE ) + { + rLev.mnAsianSettings &=~4; + if ( aParagraphObj.mbParagraphPunctation ) + rLev.mnAsianSettings |= 4; + } + + if ( aParagraphObj.meBiDi == css::beans::PropertyState_DIRECT_VALUE ) + rLev.mnBiDi = aParagraphObj.mnBiDi; + + rLev.mbIsBullet = aParagraphObj.mbIsBullet; //( ( aParagraphObj.nBulletFlags & 1 ) != 0 ); + + if ( nLevel ) + return; + + if (!(aParagraphObj.bExtendedParameters && + aParagraphObj.meBullet == css::beans::PropertyState_DIRECT_VALUE)) + return; + + for ( sal_Int16 i = 0; i < 5; i++ ) + { + PPTExParaLevel& rLevel = maParaLevel[ i ]; + if ( i ) + aParagraphObj.ImplGetNumberingLevel( pBuProv, i, false, false ); + rLevel.mnTextOfs = aParagraphObj.nTextOfs; + rLevel.mnBulletOfs = static_cast(aParagraphObj.nBulletOfs); + rLevel.mnBulletChar = aParagraphObj.cBulletId; + FontCollectionEntry aFontDescEntry( aParagraphObj.aFontDesc.Name, aParagraphObj.aFontDesc.Family, + aParagraphObj.aFontDesc.Pitch, aParagraphObj.aFontDesc.CharSet ); + rLevel.mnBulletFont = static_cast(rFontCollection.GetId( aFontDescEntry )); + rLevel.mnBulletHeight = aParagraphObj.nBulletRealSize; + rLevel.mnBulletColor = aParagraphObj.nBulletColor; + + rLevel.mbExtendedBulletsUsed = aParagraphObj.bExtendedBulletsUsed; + rLevel.mnBulletId = aParagraphObj.nBulletId; + rLevel.mnNumberingType = aParagraphObj.nNumberingType; + rLevel.mnBulletStart = aParagraphObj.nStartWith; + rLevel.mnMappedNumType = aParagraphObj.nMappedNumType; + } +} + +void PPTExParaSheet::Write( SvStream& rSt, sal_uInt16 nLev, bool bSimpleText, + const css::uno::Reference< css::beans::XPropertySet > & rPagePropSet ) +{ + const PPTExParaLevel& rLev = maParaLevel[ nLev ]; + + if ( maParaLevel[ 0 ].mbExtendedBulletsUsed || maParaLevel[ 1 ].mbExtendedBulletsUsed || + maParaLevel[ 2 ].mbExtendedBulletsUsed || maParaLevel[ 3 ].mbExtendedBulletsUsed || + maParaLevel[ 4 ].mbExtendedBulletsUsed ) + { + SvStream& rOut = pBuProv->aBuExMasterStream; + if ( !nLev ) + { + rOut.WriteUInt32( ( EPP_PST_ExtendedParagraphMasterAtom << 16 ) | ( mnInstance << 4 ) ) + .WriteUInt32( 5 * 16 + 2 ) + .WriteUInt16( 5 ); // depth + } + sal_uInt16 nBulletId = rLev.mnBulletId; + if ( rLev.mnNumberingType != SVX_NUM_BITMAP ) + nBulletId = 0xffff; + rOut.WriteUInt32( 0x03800000 ) + .WriteUInt16( nBulletId ) + .WriteUInt32( rLev.mnMappedNumType ) + .WriteUInt16( rLev.mnBulletStart ) + .WriteUInt32( 0 ); + } + + sal_uInt32 nParaFlags = 0x3ffdff; + sal_uInt16 nBulletFlags = ( rLev.mbIsBullet ) ? 0xf : 0xe; + + if ( nLev ) + nParaFlags &= 0x207fff; + if ( bSimpleText ) + nParaFlags &= 0x7fff; + sal_uInt32 nBulletColor = rLev.mnBulletColor; + if ( nBulletColor == sal_uInt32(COL_AUTO) ) + { + bool bIsDark = false; + css::uno::Any aAny; + if ( PropValue::GetPropertyValue( aAny, rPagePropSet, "IsBackgroundDark", true ) ) + aAny >>= bIsDark; + nBulletColor = bIsDark ? 0xffffff : 0x000000; + } + nBulletColor &= 0xffffff; + nBulletColor |= 0xfe000000; + rSt.WriteUInt32( nParaFlags ) + .WriteUInt16( nBulletFlags ) + .WriteUInt16( rLev.mnBulletChar ) + .WriteUInt16( rLev.mnBulletFont ) + .WriteUInt16( rLev.mnBulletHeight ) + .WriteUInt32( nBulletColor ) + .WriteUInt16( rLev.mnAdjust ) + .WriteUInt16( rLev.mnLineFeed ) + .WriteUInt16( rLev.mnUpperDist ) + .WriteUInt16( rLev.mnLowerDist ) + .WriteUInt16( rLev.mnTextOfs ) + .WriteUInt16( rLev.mnBulletOfs ); + + if ( bSimpleText || nLev ) + { + if ( nParaFlags & 0x200000 ) + rSt.WriteUInt16( rLev.mnBiDi ); + } + else + { + rSt.WriteUInt16( rLev.mnDefaultTab ) + .WriteUInt16( 0 ) + .WriteUInt16( 0 ) + .WriteUInt16( rLev.mnAsianSettings ) + .WriteUInt16( rLev.mnBiDi ); + } +} + +PPTExStyleSheet::PPTExStyleSheet( sal_uInt16 nDefaultTab, PPTExBulletProvider* pBuProv ) +{ + for ( int nInstance = EPP_TEXTTYPE_Title; nInstance <= EPP_TEXTTYPE_QuarterBody; nInstance++ ) + { + if (nInstance != EPP_TEXTTYPE_notUsed) + { + mpParaSheet[ nInstance ].reset(new PPTExParaSheet( nInstance, nDefaultTab, pBuProv )); + mpCharSheet[ nInstance ].reset(new PPTExCharSheet( nInstance )); + } + } +} + +PPTExStyleSheet::~PPTExStyleSheet() +{ +} + +void PPTExStyleSheet::SetStyleSheet( const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + FontCollection& rFontCollection, int nInstance, int nLevel ) +{ + if ( nInstance == EPP_TEXTTYPE_notUsed ) + return; + mpCharSheet[ nInstance ]->SetStyleSheet( rXPropSet, rFontCollection, nLevel ); + mpParaSheet[ nInstance ]->SetStyleSheet( rXPropSet, rFontCollection, nLevel, mpCharSheet[ nInstance ]->maCharLevel[ nLevel ] ); +} + +bool PPTExStyleSheet::IsHardAttribute( sal_uInt32 nInstance, sal_uInt32 nLevel, PPTExTextAttr eAttr, sal_uInt32 nValue ) +{ + assert(nInstance < PPTEX_STYLESHEETENTRIES && nLevel < 5); + + const PPTExParaLevel& rPara = mpParaSheet[ nInstance ]->maParaLevel[ nLevel ]; + const PPTExCharLevel& rChar = mpCharSheet[ nInstance ]->maCharLevel[ nLevel ]; + + sal_uInt32 nFlag = 0; + + switch ( eAttr ) + { + case ParaAttr_BulletOn : return ( rPara.mbIsBullet ) ? nValue == 0 : nValue != 0; + case ParaAttr_BuHardFont : + case ParaAttr_BulletFont : return ( rPara.mnBulletFont != nValue ); + case ParaAttr_BuHardColor : + case ParaAttr_BulletColor : return ( rPara.mnBulletColor != nValue ); + case ParaAttr_BuHardHeight : + case ParaAttr_BulletHeight : return ( rPara.mnBulletHeight != nValue ); + case ParaAttr_BulletChar : return ( rPara.mnBulletChar != nValue ); + case ParaAttr_Adjust : return ( rPara.mnAdjust != nValue ); + case ParaAttr_LineFeed : return ( rPara.mnLineFeed != nValue ); + case ParaAttr_UpperDist : return ( rPara.mnUpperDist != nValue ); + case ParaAttr_LowerDist : return ( rPara.mnLowerDist != nValue ); + case ParaAttr_TextOfs : return ( rPara.mnTextOfs != nValue ); + case ParaAttr_BulletOfs : return ( rPara.mnBulletOfs != nValue ); + case ParaAttr_DefaultTab : return ( rPara.mnDefaultTab != nValue ); + case ParaAttr_BiDi : return ( rPara.mnBiDi != nValue ); + case CharAttr_Bold : nFlag = 1; break; + case CharAttr_Italic : nFlag = 2; break; + case CharAttr_Underline : nFlag = 4; break; + case CharAttr_Shadow : nFlag = 16; break; + case CharAttr_Strikeout : nFlag = 256; break; + case CharAttr_Embossed : nFlag = 512; break; + case CharAttr_Font : return ( rChar.mnFont != nValue ); + case CharAttr_AsianOrComplexFont : return ( rChar.mnAsianOrComplexFont != nValue ); + case CharAttr_Symbol : return true; + case CharAttr_FontHeight : return ( rChar.mnFontHeight != nValue ); + case CharAttr_FontColor : return ( rChar.mnFontColor != Color(ColorTransparency, nValue) ); + case CharAttr_Escapement : return ( rChar.mnEscapement != nValue ); + default: + break; + } + if ( nFlag ) + { + if ( rChar.mnFlags & nFlag ) + return ( ( nValue & nFlag ) == 0 ); + else + return ( ( nValue & nFlag ) != 0 ); + } + return true; +} + +// the TxCFStyleAtom stores the text properties that are used +// when creating new objects in PowerPoint. + +void PPTExStyleSheet::WriteTxCFStyleAtom( SvStream& rSt ) +{ + const PPTExCharLevel& rCharStyle = mpCharSheet[ EPP_TEXTTYPE_Other ]->maCharLevel[ 0 ]; + + sal_uInt16 const nFlags = 0x60 // ?? + | 0x02 // fontsize; + | 0x04; // fontcolor + + sal_uInt32 nCharFlags = rCharStyle.mnFlags; + nCharFlags &= CharAttr_Italic | CharAttr_Bold | CharAttr_Underline | CharAttr_Shadow; + + rSt.WriteUInt32( EPP_TxCFStyleAtom << 16 ) // recordheader + .WriteUInt32( SizeOfTxCFStyleAtom() - 8 ) + .WriteUInt16( 0x80 | nCharFlags ) + .WriteUInt16( nFlags ) + .WriteUInt16( nCharFlags ) + .WriteInt32( -1 ) // ? + .WriteUInt16( rCharStyle.mnFontHeight ) + .WriteUInt32( sal_uInt32(rCharStyle.mnFontColor) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/pptx-text.cxx b/sd/source/filter/eppt/pptx-text.cxx new file mode 100644 index 000000000..85c37f77d --- /dev/null +++ b/sd/source/filter/eppt/pptx-text.cxx @@ -0,0 +1,1400 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "text.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace css; + +static css::uno::Reference< css::i18n::XBreakIterator > xPPTBreakIter; + +PortionObj::PortionObj(const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + FontCollection& rFontCollection) + : meCharColor(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meCharHeight(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meFontName(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meAsianOrComplexFont(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meCharEscapement(css::beans::PropertyState_AMBIGUOUS_VALUE) + , mnCharAttrHard(0) + , mnCharColor(0) + , mnCharAttr(0) + , mnFont(0) + , mnAsianOrComplexFont(0xffff) + , mnTextSize(0) + , mbLastPortion(true) +{ + mXPropSet = rXPropSet; + + ImplGetPortionValues( rFontCollection, false ); +} + +PortionObj::PortionObj(css::uno::Reference< css::text::XTextRange > & rXTextRange, + bool bLast, FontCollection& rFontCollection) + : meCharColor(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meCharHeight(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meFontName(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meAsianOrComplexFont(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meCharEscapement(css::beans::PropertyState_AMBIGUOUS_VALUE) + , mnCharAttrHard(0) + , mnCharColor(0) + , mnCharAttr(0) + , mnCharHeight(0) + , mnFont(0) + , mnAsianOrComplexFont(0xffff) + , mnCharEscapement(0) + , mbLastPortion(bLast) +{ + OUString aString( rXTextRange->getString() ); + OUString aURL; + + mnTextSize = aString.getLength(); + if ( bLast ) + mnTextSize++; + + if ( !mnTextSize ) + return; + + bool bRTL_endingParen = false; + mpFieldEntry = nullptr; + sal_uInt32 nFieldType = 0; + + mXPropSet.set( rXTextRange, css::uno::UNO_QUERY ); + mXPropState.set( rXTextRange, css::uno::UNO_QUERY ); + + bool bPropSetsValid = ( mXPropSet.is() && mXPropState.is() ); + if ( bPropSetsValid ) + nFieldType = ImplGetTextField( rXTextRange, mXPropSet, aURL ); + if ( nFieldType ) + { + mpFieldEntry.reset( new FieldEntry( nFieldType, 0, mnTextSize ) ); + if ( nFieldType >> 28 == 4 ) + { + mpFieldEntry->aRepresentation = aString; + mpFieldEntry->aFieldUrl = aURL; + } + } + bool bSymbol = false; + + if ( bPropSetsValid && ImplGetPropertyValue( "CharFontCharSet", false ) ) + { + sal_Int16 nCharset = 0; + mAny >>= nCharset; + if ( nCharset == css::awt::CharSet::SYMBOL ) + bSymbol = true; + } + if ( mpFieldEntry && ( nFieldType & 0x800000 ) ) // placeholder ? + { + mnTextSize = 1; + if ( bLast ) + mnTextSize++; + mpText.reset( new sal_uInt16[ mnTextSize ] ); + mpText[ 0 ] = 0x2a; + } + else + { + // For i39516 - a closing parenthesis that ends an RTL string is displayed backwards by PPT + // Solution: add a Unicode Right-to-Left Mark, following the method described in i18024 + if (bLast && !aString.isEmpty() + && aString[aString.getLength() - 1] == ')' + && FontCollection::GetScriptDirection(aString) == css::i18n::ScriptDirection::RIGHT_TO_LEFT) + { + mnTextSize++; + bRTL_endingParen = true; + } + mpText.reset( new sal_uInt16[ mnTextSize ] ); + sal_uInt16 nChar; + for ( sal_Int32 i = 0; i < aString.getLength(); i++ ) + { + nChar = static_cast(aString[ i ]); + if ( nChar == 0xa ) + nChar++; + else if ( !bSymbol ) + { + switch ( nChar ) + { + // Currency + case 128: nChar = 0x20AC; break; + // Punctuation and other + case 130: nChar = 0x201A; break;// SINGLE LOW-9 QUOTATION MARK + case 131: nChar = 0x0192; break;// LATIN SMALL LETTER F WITH HOOK + case 132: nChar = 0x201E; break;// DOUBLE LOW-9 QUOTATION MARK + // LOW DOUBLE PRIME QUOTATION MARK + case 133: nChar = 0x2026; break;// HORIZONTAL ELLIPSES + case 134: nChar = 0x2020; break;// DAGGER + case 135: nChar = 0x2021; break;// DOUBLE DAGGER + case 136: nChar = 0x02C6; break;// MODIFIER LETTER CIRCUMFLEX ACCENT + case 137: nChar = 0x2030; break;// PER MILLE SIGN + case 138: nChar = 0x0160; break;// LATIN CAPITAL LETTER S WITH CARON + case 139: nChar = 0x2039; break;// SINGLE LEFT-POINTING ANGLE QUOTATION MARK + case 140: nChar = 0x0152; break;// LATIN CAPITAL LIGATURE OE + case 142: nChar = 0x017D; break;// LATIN CAPITAL LETTER Z WITH CARON + case 145: nChar = 0x2018; break;// LEFT SINGLE QUOTATION MARK + // MODIFIER LETTER TURNED COMMA + case 146: nChar = 0x2019; break;// RIGHT SINGLE QUOTATION MARK + // MODIFIER LETTER APOSTROPHE + case 147: nChar = 0x201C; break;// LEFT DOUBLE QUOTATION MARK + // REVERSED DOUBLE PRIME QUOTATION MARK + case 148: nChar = 0x201D; break;// RIGHT DOUBLE QUOTATION MARK + // REVERSED DOUBLE PRIME QUOTATION MARK + case 149: nChar = 0x2022; break;// BULLET + case 150: nChar = 0x2013; break;// EN DASH + case 151: nChar = 0x2014; break;// EM DASH + case 152: nChar = 0x02DC; break;// SMALL TILDE + case 153: nChar = 0x2122; break;// TRADE MARK SIGN + case 154: nChar = 0x0161; break;// LATIN SMALL LETTER S WITH CARON + case 155: nChar = 0x203A; break;// SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + case 156: nChar = 0x0153; break;// LATIN SMALL LIGATURE OE + case 158: nChar = 0x017E; break;// LATIN SMALL LETTER Z WITH CARON + case 159: nChar = 0x0178; break;// LATIN CAPITAL LETTER Y WITH DIAERESIS + } + } + mpText[ i ] = nChar; + } + } + if ( bRTL_endingParen ) + mpText[ mnTextSize - 2 ] = 0x200F; // Unicode Right-to-Left mark + + if ( bLast ) + mpText[ mnTextSize - 1 ] = 0xd; + + if ( bPropSetsValid ) + ImplGetPortionValues( rFontCollection, true ); +} + +PortionObj::PortionObj( const PortionObj& rPortionObj ) +: PropStateValue( rPortionObj ) +{ + ImplConstruct( rPortionObj ); +} + +PortionObj::~PortionObj() +{ + ImplClear(); +} + +void PortionObj::Write( SvStream* pStrm, bool bLast ) +{ + sal_uInt32 nCount = mnTextSize; + if ( bLast && mbLastPortion ) + nCount--; + for ( sal_uInt32 i = 0; i < nCount; i++ ) + pStrm->WriteUInt16( mpText[ i ] ); +} + +void PortionObj::ImplGetPortionValues( FontCollection& rFontCollection, bool bGetPropStateValue ) +{ + + bool bOk = ImplGetPropertyValue( "CharFontName", bGetPropStateValue ); + meFontName = ePropState; + if ( bOk ) + { + FontCollectionEntry aFontDesc( *o3tl::doAccess(mAny) ); + sal_uInt32 nCount = rFontCollection.GetCount(); + mnFont = static_cast(rFontCollection.GetId( aFontDesc )); + if ( mnFont == nCount ) + { + FontCollectionEntry& rFontDesc = rFontCollection.GetLast(); + if ( ImplGetPropertyValue( "CharFontCharSet", false ) ) + mAny >>= rFontDesc.CharSet; + if ( ImplGetPropertyValue( "CharFontFamily", false ) ) + mAny >>= rFontDesc.Family; + if ( ImplGetPropertyValue( "CharFontPitch", false ) ) + mAny >>= rFontDesc.Pitch; + } + } + + sal_Int16 nScriptType = SvtLanguageOptions::FromSvtScriptTypeToI18N( SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ) ); + if ( mpText && mnTextSize && xPPTBreakIter.is() ) + { + OUString sT( reinterpret_cast(mpText.get()), mnTextSize ); + nScriptType = xPPTBreakIter->getScriptType( sT, 0 ); + } + if ( nScriptType != css::i18n::ScriptType::COMPLEX ) + { + bOk = ImplGetPropertyValue( "CharFontNameAsian", bGetPropStateValue ); + meAsianOrComplexFont = ePropState; + if ( bOk ) + { + FontCollectionEntry aFontDesc( *o3tl::doAccess(mAny) ); + sal_uInt32 nCount = rFontCollection.GetCount(); + mnAsianOrComplexFont = static_cast(rFontCollection.GetId( aFontDesc )); + if ( mnAsianOrComplexFont == nCount ) + { + FontCollectionEntry& rFontDesc = rFontCollection.GetLast(); + if ( ImplGetPropertyValue( "CharFontCharSetAsian", false ) ) + mAny >>= rFontDesc.CharSet; + if ( ImplGetPropertyValue( "CharFontFamilyAsian", false ) ) + mAny >>= rFontDesc.Family; + if ( ImplGetPropertyValue( "CharFontPitchAsian", false ) ) + mAny >>= rFontDesc.Pitch; + } + } + } + else + { + bOk = ImplGetPropertyValue( "CharFontNameComplex", bGetPropStateValue ); + meAsianOrComplexFont = ePropState; + if ( bOk ) + { + FontCollectionEntry aFontDesc( *o3tl::doAccess(mAny) ); + sal_uInt32 nCount = rFontCollection.GetCount(); + mnAsianOrComplexFont = static_cast(rFontCollection.GetId( aFontDesc )); + if ( mnAsianOrComplexFont == nCount ) + { + FontCollectionEntry& rFontDesc = rFontCollection.GetLast(); + if ( ImplGetPropertyValue( "CharFontCharSetComplex", false ) ) + mAny >>= rFontDesc.CharSet; + if ( ImplGetPropertyValue( "CharFontFamilyComplex", false ) ) + mAny >>= rFontDesc.Family; + if ( ImplGetPropertyValue( "CharFontPitchComplex", false ) ) + mAny >>= rFontDesc.Pitch; + } + } + } + + OUString aCharHeightName, aCharWeightName, aCharLocaleName, aCharPostureName; + switch( nScriptType ) + { + case css::i18n::ScriptType::ASIAN : + { + aCharHeightName = "CharHeightAsian"; + aCharWeightName = "CharWeightAsian"; + aCharLocaleName = "CharLocaleAsian"; + aCharPostureName = "CharPostureAsian"; + break; + } + case css::i18n::ScriptType::COMPLEX : + { + aCharHeightName = "CharHeightComplex"; + aCharWeightName = "CharWeightComplex"; + aCharLocaleName = "CharLocaleComplex"; + aCharPostureName = "CharPostureComplex"; + break; + } + default: + { + aCharHeightName = "CharHeight"; + aCharWeightName = "CharWeight"; + aCharLocaleName = "CharLocale"; + aCharPostureName = "CharPosture"; + break; + } + } + + mnCharHeight = 24; + if ( GetPropertyValue( mAny, mXPropSet, aCharHeightName ) ) + { + float fVal(0.0); + if ( mAny >>= fVal ) + { + mnCharHeight = static_cast( fVal + 0.5 ); + meCharHeight = GetPropertyState( mXPropSet, aCharHeightName ); + } + } + if ( GetPropertyValue( mAny, mXPropSet, aCharWeightName ) ) + { + float fFloat(0.0); + if ( mAny >>= fFloat ) + { + if ( fFloat >= css::awt::FontWeight::SEMIBOLD ) + mnCharAttr |= 1; + if ( GetPropertyState( mXPropSet, aCharWeightName ) == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 1; + } + } + if ( GetPropertyValue( mAny, mXPropSet, aCharLocaleName ) ) + { + css::lang::Locale eLocale; + if ( mAny >>= eLocale ) + meCharLocale = eLocale; + } + if ( GetPropertyValue( mAny, mXPropSet, aCharPostureName ) ) + { + css::awt::FontSlant aFS; + if ( mAny >>= aFS ) + { + switch( aFS ) + { + case css::awt::FontSlant_OBLIQUE : + case css::awt::FontSlant_ITALIC : + mnCharAttr |= 2; + break; + default: + break; + } + if ( GetPropertyState( mXPropSet, aCharPostureName ) == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 2; + } + } + + if ( ImplGetPropertyValue( "CharUnderline", bGetPropStateValue ) ) + { + sal_Int16 nVal(0); + mAny >>= nVal; + switch ( nVal ) + { + case css::awt::FontUnderline::SINGLE : + case css::awt::FontUnderline::DOUBLE : + case css::awt::FontUnderline::DOTTED : + mnCharAttr |= 4; + } + } + if ( ePropState == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 4; + + if ( ImplGetPropertyValue( "CharShadowed", bGetPropStateValue ) ) + { + bool bBool(false); + mAny >>= bBool; + if ( bBool ) + mnCharAttr |= 0x10; + } + if ( ePropState == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 16; + + if ( ImplGetPropertyValue( "CharRelief", bGetPropStateValue ) ) + { + sal_Int16 nVal(0); + mAny >>= nVal; + if ( nVal != css::text::FontRelief::NONE ) + mnCharAttr |= 512; + } + if ( ePropState == css::beans::PropertyState_DIRECT_VALUE ) + mnCharAttrHard |= 512; + + if ( ImplGetPropertyValue( "CharColor", bGetPropStateValue ) ) + { + sal_uInt32 nSOColor = *( o3tl::doAccess(mAny) ); + mnCharColor = nSOColor & 0xff00ff00; // green and hibyte + mnCharColor |= static_cast(nSOColor) << 16; // red and blue is switched + mnCharColor |= static_cast( nSOColor >> 16 ); + } + meCharColor = ePropState; + + mnCharEscapement = 0; + if ( ImplGetPropertyValue( "CharEscapement", bGetPropStateValue ) ) + { + mAny >>= mnCharEscapement; + if ( mnCharEscapement > 100 ) + mnCharEscapement = 33; + else if ( mnCharEscapement < -100 ) + mnCharEscapement = -33; + } + meCharEscapement = ePropState; +} + +void PortionObj::ImplClear() +{ + mpFieldEntry.reset(); + mpText.reset(); +} + +void PortionObj::ImplConstruct( const PortionObj& rPortionObj ) +{ + meCharColor = rPortionObj.meCharColor; + meCharHeight = rPortionObj.meCharHeight; + meFontName = rPortionObj.meFontName; + meAsianOrComplexFont = rPortionObj.meAsianOrComplexFont; + meCharEscapement = rPortionObj.meCharEscapement; + meCharLocale = rPortionObj.meCharLocale; + mnCharAttrHard = rPortionObj.mnCharAttrHard; + + mbLastPortion = rPortionObj.mbLastPortion; + mnTextSize = rPortionObj.mnTextSize; + mnCharColor = rPortionObj.mnCharColor; + mnCharEscapement = rPortionObj.mnCharEscapement; + mnCharAttr = rPortionObj.mnCharAttr; + mnCharHeight = rPortionObj.mnCharHeight; + mnFont = rPortionObj.mnFont; + mnAsianOrComplexFont = rPortionObj.mnAsianOrComplexFont; + + if ( rPortionObj.mpText ) + { + mpText.reset( new sal_uInt16[ mnTextSize ] ); + memcpy( mpText.get(), rPortionObj.mpText.get(), mnTextSize << 1 ); + } + + if ( rPortionObj.mpFieldEntry ) + mpFieldEntry.reset( new FieldEntry( *( rPortionObj.mpFieldEntry ) ) ); +} + +sal_uInt32 PortionObj::ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition ) +{ + if ( mpFieldEntry && ( !mpFieldEntry->nFieldStartPos ) ) + { + mpFieldEntry->nFieldStartPos += nCurrentTextPosition; + mpFieldEntry->nFieldEndPos += nCurrentTextPosition; + } + return mnTextSize; +} + +// Return: 0 = no TextField +// bit28->31 text field type : +// 1 = Date +// 2 = Time +// 3 = SlideNumber +// 4 = Url +// 5 = DateTime +// 6 = header +// 7 = footer +// bit24->27 text field sub type (optional) +// 23-> PPT Textfield needs a placeholder + +sal_uInt32 PortionObj::ImplGetTextField( css::uno::Reference< css::text::XTextRange > & , + const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, OUString& rURL ) +{ + sal_uInt32 nRetValue = 0; + sal_Int32 nFormat; + css::uno::Any aAny; + if ( GetPropertyValue( aAny, rXPropSet, "TextPortionType", true ) ) + { + auto aTextFieldType = o3tl::doAccess(aAny); + if ( *aTextFieldType == "TextField" ) + { + if ( GetPropertyValue( aAny, rXPropSet, *aTextFieldType, true ) ) + { + css::uno::Reference< css::text::XTextField > aXTextField; + if ( aAny >>= aXTextField ) + { + if ( aXTextField.is() ) + { + css::uno::Reference< css::beans::XPropertySet > xFieldPropSet( aXTextField, css::uno::UNO_QUERY ); + if ( xFieldPropSet.is() ) + { + OUString aFieldKind( aXTextField->getPresentation( true ) ); + if ( aFieldKind == "Date" ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) ) + { + bool bBool = false; + aAny >>= bBool; + if ( !bBool ) // Fixed DateFields does not exist in PPT + { + if ( GetPropertyValue( aAny, xFieldPropSet, "Format", true ) ) + { + nFormat = *o3tl::doAccess(aAny); + switch ( nFormat ) + { + default: + case 5 : + case 4 : + case 2 : nFormat = 0; break; + case 8 : + case 9 : + case 3 : nFormat = 1; break; + case 7 : + case 6 : nFormat = 2; break; + } + nRetValue |= ( ( ( 1 << 4 ) | nFormat ) << 24 ) | 0x800000; + } + } + } + } + else if ( aFieldKind == "URL" ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "URL", true ) ) + rURL = *o3tl::doAccess(aAny); + nRetValue = 4 << 28; + } + else if ( aFieldKind == "Page" ) + { + nRetValue = 3 << 28 | 0x800000; + } + else if ( aFieldKind == "Pages" ) + { + + } + else if ( aFieldKind == "Time" ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) ) + { + bool bBool = false; + aAny >>= bBool; + if ( !bBool ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) ) + { + nFormat = *o3tl::doAccess(aAny); + nRetValue |= ( ( ( 2 << 4 ) | nFormat ) << 24 ) | 0x800000; + } + } + } + } + else if ( aFieldKind == "File" ) + { + + } + else if ( aFieldKind == "Table" ) + { + + } + else if ( aFieldKind == "ExtTime" ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "IsFix", true ) ) + { + bool bBool = false; + aAny >>= bBool; + if ( !bBool ) + { + if ( GetPropertyValue( aAny, xFieldPropSet, "Format", true ) ) + { + nFormat = *o3tl::doAccess(aAny); + switch ( nFormat ) + { + default: + case 6 : + case 7 : + case 8 : + case 2 : nFormat = 12; break; + case 3 : nFormat = 9; break; + case 5 : + case 4 : nFormat = 10; break; + + } + nRetValue |= ( ( ( 2 << 4 ) | nFormat ) << 24 ) | 0x800000; + } + } + } + } + else if ( aFieldKind == "ExtFile" ) + { + + } + else if ( aFieldKind == "Author" ) + { + + } + else if ( aFieldKind == "DateTime" ) + { + nRetValue = 5 << 28 | 0x800000; + } + else if ( aFieldKind == "Header" ) + { + nRetValue = 6 << 28 | 0x800000; + } + else if ( aFieldKind == "Footer" ) + { + nRetValue = 7 << 28 | 0x800000; + } + } + } + } + } + } + } + return nRetValue; +} + +PortionObj& PortionObj::operator=( const PortionObj& rPortionObj ) +{ + if ( this != &rPortionObj ) + { + ImplClear(); + ImplConstruct( rPortionObj ); + } + return *this; +} + +ParagraphObj::ParagraphObj(const css::uno::Reference< css::beans::XPropertySet > & rXPropSet, + PPTExBulletProvider* pProv) + : mnTextSize(0) + , mbFirstParagraph(false) + , mbLastParagraph(false) + , meBullet(css::beans::PropertyState_AMBIGUOUS_VALUE) + , mnTextAdjust(0) + , mnLineSpacing(0) + , mbFixedLineSpacing(false) + , mnLineSpacingTop(0) + , mnLineSpacingBottom(0) + , mbForbiddenRules(false) + , mbParagraphPunctation(false) + , mnBiDi(0) +{ + mXPropSet = rXPropSet; + + bExtendedParameters = false; + + nDepth = 0; + nBulletFlags = 0; + nParaFlags = 0; + + ImplGetParagraphValues( pProv, false ); +} + +ParagraphObj::ParagraphObj(css::uno::Reference< css::text::XTextContent > const & rXTextContent, + ParaFlags aParaFlags, FontCollection& rFontCollection, PPTExBulletProvider& rProv ) + : mnTextSize(0) + , mbIsBullet(false) + , mbFirstParagraph( aParaFlags.bFirstParagraph ) + , mbLastParagraph( aParaFlags.bLastParagraph ) + , meBullet(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meTextAdjust(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meLineSpacing(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meLineSpacingTop(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meLineSpacingBottom(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meForbiddenRules(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meParagraphPunctation(css::beans::PropertyState_AMBIGUOUS_VALUE) + , meBiDi(css::beans::PropertyState_AMBIGUOUS_VALUE) + , mnTextAdjust(0) + , mnLineSpacing(0) + , mbFixedLineSpacing(false) + , mnLineSpacingTop(0) + , mnLineSpacingBottom(0) + , mbForbiddenRules(false) + , mbParagraphPunctation(false) + , mnBiDi(0) +{ + bExtendedParameters = false; + + nDepth = 0; + nBulletFlags = 0; + nParaFlags = 0; + + mXPropSet.set( rXTextContent, css::uno::UNO_QUERY ); + + mXPropState.set( rXTextContent, css::uno::UNO_QUERY ); + + if ( !(mXPropSet.is() && mXPropState.is()) ) + return; + + css::uno::Reference< css::container::XEnumerationAccess > aXTextPortionEA( rXTextContent, css::uno::UNO_QUERY ); + if ( aXTextPortionEA.is() ) + { + css::uno::Reference< css::container::XEnumeration > aXTextPortionE( aXTextPortionEA->createEnumeration() ); + if ( aXTextPortionE.is() ) + { + while ( aXTextPortionE->hasMoreElements() ) + { + css::uno::Reference< css::text::XTextRange > aXCursorText; + css::uno::Any aAny( aXTextPortionE->nextElement() ); + if ( aAny >>= aXCursorText ) + { + std::unique_ptr pPortionObj(new PortionObj( aXCursorText, !aXTextPortionE->hasMoreElements(), rFontCollection )); + if ( pPortionObj->Count() ) + mvPortions.push_back( std::move(pPortionObj) ); + } + } + } + } + ImplGetParagraphValues( &rProv, true ); +} + +ParagraphObj::~ParagraphObj() +{ + ImplClear(); +} + +void ParagraphObj::Write( SvStream* pStrm ) +{ + for ( std::vector >::iterator it = mvPortions.begin(); it != mvPortions.end(); ++it ) + (*it)->Write( pStrm, mbLastParagraph ); +} + +void ParagraphObj::ImplClear() +{ + mvPortions.clear(); +} + +void ParagraphObj::CalculateGraphicBulletSize( sal_uInt16 nFontHeight ) +{ + if ( ( nNumberingType != SVX_NUM_BITMAP ) || ( nBulletId == 0xffff ) ) + return; + + // calculate the bullet real size for this graphic + if ( aBuGraSize.Width() && aBuGraSize.Height() ) + { + double fCharHeight = nFontHeight; + double fLen = aBuGraSize.Height(); + fCharHeight = fCharHeight * 0.2540; + double fQuo = fLen / fCharHeight; + nBulletRealSize = static_cast( fQuo + 0.5 ); + if ( static_cast(nBulletRealSize) > 400 ) + nBulletRealSize = 400; + } +} + +void ParagraphObj::ImplGetNumberingLevel( PPTExBulletProvider* pBuProv, sal_Int16 nNumberingDepth, bool bIsBullet, bool bGetPropStateValue ) +{ + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "ParaLeftMargin" ) ) + { + sal_Int32 nVal(0); + if ( aAny >>= nVal ) + nTextOfs = convertMm100ToMasterUnit(nVal); + } + if ( GetPropertyValue( aAny, mXPropSet, "ParaFirstLineIndent" ) ) + { + if ( aAny >>= nBulletOfs ) + nBulletOfs = convertMm100ToMasterUnit(nBulletOfs); + } + if ( GetPropertyValue( aAny, mXPropSet, "NumberingIsNumber" ) ) + aAny >>= bNumberingIsNumber; + + css::uno::Reference< css::container::XIndexReplace > aXIndexReplace; + + if ( bIsBullet && ImplGetPropertyValue( "NumberingRules", bGetPropStateValue ) ) + { + if ( ( mAny >>= aXIndexReplace ) && nNumberingDepth < aXIndexReplace->getCount() ) + { + mAny = aXIndexReplace->getByIndex( nNumberingDepth ); + auto aPropertySequence = o3tl::doAccess>(mAny); + + if ( aPropertySequence->hasElements() ) + { + bExtendedParameters = true; + nBulletRealSize = 100; + nMappedNumType = 0; + + uno::Reference xGraphic; + for ( const css::beans::PropertyValue& rPropValue : *aPropertySequence ) + { + OUString aPropName( rPropValue.Name ); + if ( aPropName == "NumberingType" ) + nNumberingType = static_cast(*o3tl::doAccess(rPropValue.Value)); + else if ( aPropName == "Adjust" ) + nHorzAdjust = *o3tl::doAccess(rPropValue.Value); + else if ( aPropName == "BulletChar" ) + { + OUString aString( *o3tl::doAccess(rPropValue.Value) ); + if ( !aString.isEmpty() ) + cBulletId = aString[ 0 ]; + } + else if ( aPropName == "BulletFont" ) + { + aFontDesc = *o3tl::doAccess(rPropValue.Value); + + // Our numbullet dialog has set the wrong textencoding for our "StarSymbol" font, + // instead of a Unicode encoding the encoding RTL_TEXTENCODING_SYMBOL was used. + // Because there might exist a lot of damaged documents I added this two lines + // which fixes the bullet problem for the export. + if ( aFontDesc.Name.equalsIgnoreAsciiCase("StarSymbol") ) + aFontDesc.CharSet = RTL_TEXTENCODING_MS_1252; + + } + else if ( aPropName == "GraphicBitmap" ) + { + auto xBitmap = rPropValue.Value.get>(); + xGraphic.set(xBitmap, uno::UNO_QUERY); + } + else if ( aPropName == "GraphicSize" ) + { + if (auto aSize = o3tl::tryAccess(rPropValue.Value)) + { + // don't cast awt::Size to Size as on 64-bits they are not the same. + aBuGraSize.setWidth( aSize->Width ); + aBuGraSize.setHeight( aSize->Height ); + } + } + else if ( aPropName == "StartWith" ) + nStartWith = *o3tl::doAccess(rPropValue.Value); + else if ( aPropName == "LeftMargin" ) + nTextOfs += convertMm100ToMasterUnit(*o3tl::doAccess(rPropValue.Value)); + else if ( aPropName == "FirstLineOffset" ) + nBulletOfs += convertMm100ToMasterUnit(*o3tl::doAccess(rPropValue.Value)); + else if ( aPropName == "BulletColor" ) + { + sal_uInt32 nSOColor = *o3tl::doAccess(rPropValue.Value); + nBulletColor = nSOColor & 0xff00ff00; // green and hibyte + nBulletColor |= static_cast(nSOColor) << 16; // red + nBulletColor |= static_cast( nSOColor >> 16 ) | 0xfe000000; // blue + } + else if ( aPropName == "BulletRelSize" ) + { + nBulletRealSize = *o3tl::doAccess(rPropValue.Value); + nParaFlags |= 0x40; + nBulletFlags |= 8; + } + else if ( aPropName == "Prefix" ) + sPrefix = *o3tl::doAccess(rPropValue.Value); + else if ( aPropName == "Suffix" ) + sSuffix = *o3tl::doAccess(rPropValue.Value); +#ifdef DBG_UTIL + else if ( aPropName != "SymbolTextDistance" && aPropName != "GraphicBitmap" ) + { + OSL_FAIL( "Unknown Property" ); + } +#endif + } + + if (xGraphic.is()) + { + if ( aBuGraSize.Width() && aBuGraSize.Height() ) + { + nBulletId = pBuProv->GetId(xGraphic, aBuGraSize ); + if ( nBulletId != 0xffff ) + bExtendedBulletsUsed = true; + } + else + { + nNumberingType = SVX_NUM_NUMBER_NONE; + } + } + + CalculateGraphicBulletSize( ( mvPortions.empty() ) ? 24 : mvPortions.front()->mnCharHeight ); + + switch( nNumberingType ) + { + case SVX_NUM_NUMBER_NONE : nParaFlags |= 0xf; break; + + case SVX_NUM_CHAR_SPECIAL : // Bullet + { + if ( IsStarSymbol(aFontDesc.Name) ) + { + rtl_TextEncoding eChrSet = aFontDesc.CharSet; + cBulletId = msfilter::util::bestFitOpenSymbolToMSFont(cBulletId, eChrSet, aFontDesc.Name); + aFontDesc.CharSet = eChrSet; + } + + if ( !aFontDesc.Name.isEmpty() ) + { + nParaFlags |= 0x90; // we define the font and charset + } + + [[fallthrough]]; + } + case SVX_NUM_CHARS_UPPER_LETTER : // count from a-z, aa - az, ba - bz, ... + case SVX_NUM_CHARS_LOWER_LETTER : + case SVX_NUM_ROMAN_UPPER : + case SVX_NUM_ROMAN_LOWER : + case SVX_NUM_ARABIC : + case SVX_NUM_PAGEDESC : // numbering from the page template + case SVX_NUM_BITMAP : + case SVX_NUM_CHARS_UPPER_LETTER_N : // count from a-z, aa-zz, aaa-zzz + case SVX_NUM_CHARS_LOWER_LETTER_N : + case SVX_NUM_NUMBER_UPPER_ZH: + case SVX_NUM_CIRCLE_NUMBER: + case SVX_NUM_NUMBER_UPPER_ZH_TW: + case SVX_NUM_NUMBER_LOWER_ZH: + case SVX_NUM_FULL_WIDTH_ARABIC: + { + if ( nNumberingType != SVX_NUM_CHAR_SPECIAL ) + { + bExtendedBulletsUsed = true; + if ( nNumberingDepth & 1 ) + cBulletId = 0x2013; // defaulting bullet characters for ppt97 + else if ( nNumberingDepth == 4 ) + cBulletId = 0xbb; + else + cBulletId = 0x2022; + + switch( nNumberingType ) + { + case SVX_NUM_CHARS_UPPER_LETTER : + case SVX_NUM_CHARS_UPPER_LETTER_N : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0xa0001; // (A) + else + nMappedNumType = 0xb0001; // A) + } + else + nMappedNumType = 0x10001; // A. + } + break; + case SVX_NUM_CHARS_LOWER_LETTER : + case SVX_NUM_CHARS_LOWER_LETTER_N : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0x80001; // (a) + else + nMappedNumType = 0x90001; // a) + } + else + nMappedNumType = 0x00001; // a. + } + break; + case SVX_NUM_ROMAN_UPPER : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0xe0001; // (I) + else + nMappedNumType = 0xf0001; // I) + } + else + nMappedNumType = 0x70001; // I. + } + break; + case SVX_NUM_ROMAN_LOWER : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0x40001; // (i) + else + nMappedNumType = 0x50001; // i) + } + else + nMappedNumType = 0x60001; // i. + } + break; + case SVX_NUM_ARABIC : + { + if ( sSuffix == ")" ) + { + if ( sPrefix == "(" ) + nMappedNumType = 0xc0001; // (1) + else + nMappedNumType = 0x20001; // 1) + } + else + { + if ( sSuffix.isEmpty() && sPrefix.isEmpty() ) + nMappedNumType = 0xd0001; // 1 + else + nMappedNumType = 0x30001; // 1. + } + } + break; + case SVX_NUM_NUMBER_UPPER_ZH : + { + if ( !sSuffix.isEmpty() ) + nMappedNumType = 0x110001; // Simplified Chinese with single-byte period. + else + nMappedNumType = 0x100001; // Simplified Chinese. + } + break; + case SVX_NUM_CIRCLE_NUMBER : + { + nMappedNumType = 0x120001; // Double byte circle numbers. + } + break; + case SVX_NUM_NUMBER_UPPER_ZH_TW : + { + if ( !sSuffix.isEmpty() ) + nMappedNumType = 0x160001; // Traditional Chinese with single-byte period. + else + nMappedNumType = 0x150001; // Traditional Chinese. + } + break; + case SVX_NUM_NUMBER_LOWER_ZH : + { + if ( sSuffix == u"\uff0e" ) + nMappedNumType = 0x260001; // Japanese with double-byte period. + else if ( !sSuffix.isEmpty() ) + nMappedNumType = 0x1B0001; // Japanese/Korean with single-byte period. + else + nMappedNumType = 0x1A0001; // Japanese/Korean. + } + break; + case SVX_NUM_FULL_WIDTH_ARABIC : + { + if ( !sSuffix.isEmpty() ) + nMappedNumType = 0x1D0001; // Double-byte Arabic numbers with double-byte period. + else + nMappedNumType = 0x1C0001; // Double-byte Arabic numbers. + } + break; + default: + break; + } + } + nParaFlags |= 0x2f; + nBulletFlags |= 6; + if ( mbIsBullet && bNumberingIsNumber ) + nBulletFlags |= 1; + break; + } + default: + break; + } + } + } + } + nBulletOfs = nTextOfs + nBulletOfs; + if ( nBulletOfs < 0 ) + nBulletOfs = 0; +} + +void ParagraphObj::ImplGetParagraphValues( PPTExBulletProvider* pBuProv, bool bGetPropStateValue ) +{ + css::uno::Any aAny; + if ( GetPropertyValue( aAny, mXPropSet, "NumberingLevel", true ) ) + { + if ( bGetPropStateValue ) + meBullet = GetPropertyState( mXPropSet, "NumberingLevel" ); + nDepth = *o3tl::doAccess(aAny); + + if ( nDepth < 0 ) + { + mbIsBullet = false; + nDepth = 0; + } + else + { + if ( nDepth > 4 ) + nDepth = 4; + mbIsBullet = true; + } + } + else + { + nDepth = 0; + mbIsBullet = false; + } + ImplGetNumberingLevel( pBuProv, nDepth, mbIsBullet, bGetPropStateValue ); + + if ( ImplGetPropertyValue( "ParaTabStops", bGetPropStateValue ) ) + maTabStop = *o3tl::doAccess>(mAny); + sal_Int16 eTextAdjust = sal_Int16(css::style::ParagraphAdjust_LEFT); + if ( GetPropertyValue( aAny, mXPropSet, "ParaAdjust", bGetPropStateValue ) ) + aAny >>= eTextAdjust; + switch ( static_cast(eTextAdjust) ) + { + case css::style::ParagraphAdjust_CENTER : + mnTextAdjust = 1; + break; + case css::style::ParagraphAdjust_RIGHT : + mnTextAdjust = 2; + break; + case css::style::ParagraphAdjust_BLOCK : + mnTextAdjust = 3; + break; + default : + case css::style::ParagraphAdjust_LEFT : + mnTextAdjust = 0; + break; + } + meTextAdjust = ePropState; + + if ( ImplGetPropertyValue( "ParaLineSpacing", bGetPropStateValue ) ) + { + css::style::LineSpacing aLineSpacing + = *o3tl::doAccess(mAny); + switch ( aLineSpacing.Mode ) + { + case css::style::LineSpacingMode::FIX : + mnLineSpacing = static_cast(-( aLineSpacing.Height ) ); + mbFixedLineSpacing = true; + break; + case css::style::LineSpacingMode::MINIMUM : + case css::style::LineSpacingMode::LEADING : + mnLineSpacing = static_cast(-( aLineSpacing.Height ) ); + mbFixedLineSpacing = false; + break; + + case css::style::LineSpacingMode::PROP : + default: + mnLineSpacing = aLineSpacing.Height; + break; + } + } + meLineSpacing = ePropState; + + if ( ImplGetPropertyValue( "ParaBottomMargin", bGetPropStateValue ) ) + { + double fSpacing = *o3tl::doAccess(mAny) + convertMasterUnitToMm100(1.0) - 1; + mnLineSpacingBottom = std::round(-convertMm100ToMasterUnit(fSpacing)); + } + meLineSpacingBottom = ePropState; + + if ( ImplGetPropertyValue( "ParaTopMargin", bGetPropStateValue ) ) + { + double fSpacing = *o3tl::doAccess(mAny) + convertMasterUnitToMm100(1.0) - 1; + mnLineSpacingTop = std::round(-convertMm100ToMasterUnit(fSpacing)); + } + meLineSpacingTop = ePropState; + + if ( ImplGetPropertyValue( "ParaIsForbiddenRules", bGetPropStateValue ) ) + mAny >>= mbForbiddenRules; + meForbiddenRules = ePropState; + + if ( ImplGetPropertyValue( "ParaIsHangingPunctuation", bGetPropStateValue ) ) + mAny >>= mbParagraphPunctation; + meParagraphPunctation = ePropState; + + mnBiDi = 0; + if ( ImplGetPropertyValue( "WritingMode", bGetPropStateValue ) ) + { + sal_Int16 nWritingMode = 0; + mAny >>= nWritingMode; + + SvxFrameDirection eWritingMode = static_cast(nWritingMode); + if ( ( eWritingMode == SvxFrameDirection::Horizontal_RL_TB ) + || ( eWritingMode == SvxFrameDirection::Vertical_RL_TB ) ) + { + mnBiDi = 1; + } + } + meBiDi = ePropState; +} + +void ParagraphObj::ImplConstruct( const ParagraphObj& rParagraphObj ) +{ + mbIsBullet = rParagraphObj.mbIsBullet; + meBullet = rParagraphObj.meBullet; + meTextAdjust = rParagraphObj.meTextAdjust; + meLineSpacing = rParagraphObj.meLineSpacing; + meLineSpacingTop = rParagraphObj.meLineSpacingTop; + meLineSpacingBottom = rParagraphObj.meLineSpacingBottom; + meForbiddenRules = rParagraphObj.meForbiddenRules; + meParagraphPunctation = rParagraphObj.meParagraphPunctation; + meBiDi =rParagraphObj.meBiDi; + mbFixedLineSpacing = rParagraphObj.mbFixedLineSpacing; + mnTextSize = rParagraphObj.mnTextSize; + mnTextAdjust = rParagraphObj.mnTextAdjust; + mnLineSpacing = rParagraphObj.mnLineSpacing; + mnLineSpacingTop = rParagraphObj.mnLineSpacingTop; + mnLineSpacingBottom = rParagraphObj.mnLineSpacingBottom; + mbFirstParagraph = rParagraphObj.mbFirstParagraph; + mbLastParagraph = rParagraphObj.mbLastParagraph; + mbParagraphPunctation = rParagraphObj.mbParagraphPunctation; + mbForbiddenRules = rParagraphObj.mbForbiddenRules; + mnBiDi = rParagraphObj.mnBiDi; + + for ( std::vector >::const_iterator it = rParagraphObj.begin(); it != rParagraphObj.end(); ++it ) + mvPortions.push_back( std::make_unique( **it ) ); + + maTabStop = rParagraphObj.maTabStop; + bExtendedParameters = rParagraphObj.bExtendedParameters; + nParaFlags = rParagraphObj.nParaFlags; + nBulletFlags = rParagraphObj.nBulletFlags; + sPrefix = rParagraphObj.sPrefix; + sSuffix = rParagraphObj.sSuffix; + sGraphicUrl = rParagraphObj.sGraphicUrl; // String to a graphic + aBuGraSize = rParagraphObj.aBuGraSize; + nNumberingType = rParagraphObj.nNumberingType; // this is actually a SvxEnum + nHorzAdjust = rParagraphObj.nHorzAdjust; + nBulletColor = rParagraphObj.nBulletColor; + nBulletOfs = rParagraphObj.nBulletOfs; + nStartWith = rParagraphObj.nStartWith; // start of numbering + nTextOfs = rParagraphObj.nTextOfs; + nBulletRealSize = rParagraphObj.nBulletRealSize; // scale in percent + nDepth = rParagraphObj.nDepth; // actual depth + cBulletId = rParagraphObj.cBulletId; // if Numbering Type == CharSpecial + aFontDesc = rParagraphObj.aFontDesc; + + bExtendedBulletsUsed = rParagraphObj.bExtendedBulletsUsed; + nBulletId = rParagraphObj.nBulletId; +} + +sal_uInt32 ParagraphObj::ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition ) +{ + mnTextSize = 0; + for ( std::vector >::iterator it = mvPortions.begin(); it != mvPortions.end(); ++it ) + mnTextSize += (*it)->ImplCalculateTextPositions( nCurrentTextPosition + mnTextSize ); + return mnTextSize; +} + +ParagraphObj& ParagraphObj::operator=( const ParagraphObj& rParagraphObj ) +{ + if ( this != &rParagraphObj ) + { + ImplClear(); + ImplConstruct( rParagraphObj ); + } + return *this; +} + +struct ImplTextObj +{ + sal_uInt32 mnTextSize; + int mnInstance; + std::vector> maList; + bool mbHasExtendedBullets; + + explicit ImplTextObj( int nInstance ); +}; + +ImplTextObj::ImplTextObj( int nInstance ) + : mnTextSize(0), + mnInstance(nInstance), + mbHasExtendedBullets(false) +{ +} + +TextObj::TextObj( css::uno::Reference< css::text::XSimpleText > const & rXTextRef, + int nInstance, FontCollection& rFontCollection, PPTExBulletProvider& rProv ): + mpImplTextObj(std::make_shared(nInstance)) +{ + css::uno::Reference< css::container::XEnumerationAccess > aXTextParagraphEA( rXTextRef, css::uno::UNO_QUERY ); + + if ( aXTextParagraphEA.is() ) + { + css::uno::Reference< css::container::XEnumeration > aXTextParagraphE( aXTextParagraphEA->createEnumeration() ); + if ( aXTextParagraphE.is() ) + { + ParaFlags aParaFlags; + while ( aXTextParagraphE->hasMoreElements() ) + { + css::uno::Reference< css::text::XTextContent > aXParagraph; + css::uno::Any aAny( aXTextParagraphE->nextElement() ); + if ( aAny >>= aXParagraph ) + { + if ( !aXTextParagraphE->hasMoreElements() ) + aParaFlags.bLastParagraph = true; + std::unique_ptr pPara(new ParagraphObj( aXParagraph, aParaFlags, rFontCollection, rProv )); + mpImplTextObj->mbHasExtendedBullets |= pPara->bExtendedBulletsUsed; + mpImplTextObj->maList.push_back( std::move(pPara) ); + aParaFlags.bFirstParagraph = false; + } + } + } + } + ImplCalculateTextPositions(); +} + +void TextObj::ImplCalculateTextPositions() +{ + mpImplTextObj->mnTextSize = 0; + for ( sal_uInt32 i = 0; i < ParagraphCount(); ++i ) + mpImplTextObj->mnTextSize += GetParagraph(i)->ImplCalculateTextPositions( mpImplTextObj->mnTextSize ); +} + +ParagraphObj* TextObj::GetParagraph(int idx) +{ + return mpImplTextObj->maList[idx].get(); +} + +sal_uInt32 TextObj::ParagraphCount() const +{ + return mpImplTextObj->maList.size(); +} + +sal_uInt32 TextObj::Count() const +{ + return mpImplTextObj->mnTextSize; +} + +int TextObj::GetInstance() const +{ + return mpImplTextObj->mnInstance; +} + +bool TextObj::HasExtendedBullets() const +{ + return mpImplTextObj->mbHasExtendedBullets; +} + +void FontCollectionEntry::ImplInit( const OUString& rName ) +{ + OUString aSubstName( GetSubsFontName( rName, SubsFontFlags::ONLYONE | SubsFontFlags::MS ) ); + if ( !aSubstName.isEmpty() ) + { + Name = aSubstName; + } + else + { + Name = rName; + } +} + +FontCollection::~FontCollection() +{ + pVDev.disposeAndClear(); + xPPTBreakIter = nullptr; +} + +FontCollection::FontCollection() : + pVDev ( nullptr ) +{ + xPPTBreakIter = css::i18n::BreakIterator::create( ::comphelper::getProcessComponentContext() ); +} + +short FontCollection::GetScriptDirection( std::u16string_view rString ) +{ + short nRet = ScriptTypeDetector::getScriptDirection( rString, 0, css::i18n::ScriptDirection::NEUTRAL ); + return nRet; +} + +sal_uInt32 FontCollection::GetId( FontCollectionEntry& rEntry ) +{ + if( !rEntry.Name.isEmpty() ) + { + const sal_uInt32 nFonts = maFonts.size(); + + for( sal_uInt32 i = 0; i < nFonts; i++ ) + { + const FontCollectionEntry* pEntry = GetById( i ); + if( pEntry->Name == rEntry.Name ) + return i; + } + vcl::Font aFont; + aFont.SetCharSet( rEntry.CharSet ); + aFont.SetFamilyName( rEntry.Original ); + aFont.SetFontHeight( 100 ); + + if ( !pVDev ) + pVDev = VclPtr::Create(); + + pVDev->SetFont( aFont ); + FontMetric aMetric( pVDev->GetFontMetric() ); + + sal_uInt16 nTxtHeight = static_cast(aMetric.GetAscent()) + static_cast(aMetric.GetDescent()); + + if ( nTxtHeight ) + { + double fScaling = static_cast(nTxtHeight) / 120.0; + if ( ( fScaling > 0.50 ) && ( fScaling < 1.5 ) ) + rEntry.Scaling = fScaling; + } + + maFonts.push_back(rEntry); + return nFonts; + } + return 0; +} + +const FontCollectionEntry* FontCollection::GetById( sal_uInt32 nId ) +{ + return nId < maFonts.size() ? &maFonts[nId] : nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/eppt/text.hxx b/sd/source/filter/eppt/text.hxx new file mode 100644 index 000000000..ee2fc537c --- /dev/null +++ b/sd/source/filter/eppt/text.hxx @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "epptbase.hxx" + +#include +#include +#include +#include +#include + +namespace com::sun::star { + namespace awt { struct FontDescriptor; } + namespace beans { class XPropertyState; } + namespace text { class XTextRange; class XTextContent; class XSimpleText; } + namespace style { struct TabStop; } +} + +struct SOParagraph +{ + bool bExtendedParameters; + sal_uInt32 nParaFlags; + sal_Int16 nBulletFlags; + OUString sPrefix; + OUString sSuffix; + OUString sGraphicUrl; // String to a graphic + Size aBuGraSize; + SvxNumType nNumberingType; + sal_uInt32 nHorzAdjust; + sal_uInt32 nBulletColor; + sal_Int32 nBulletOfs; + sal_Int16 nStartWith; // start of numbering + sal_Int16 nTextOfs; + sal_Int16 nBulletRealSize; // scale in percent + sal_Int16 nDepth; // actual depth + sal_Unicode cBulletId; // if Numbering Type == CharSpecial + css::awt::FontDescriptor aFontDesc; + + bool bExtendedBulletsUsed; + sal_uInt16 nBulletId; + sal_uInt32 nMappedNumType; + bool bNumberingIsNumber; + + SOParagraph() + : bExtendedParameters(false) + , nParaFlags(0) + , nBulletFlags(0) + , nNumberingType(SVX_NUM_NUMBER_NONE) + , nHorzAdjust(0) + , nBulletColor(0) + , nBulletOfs(0) + , nStartWith(0) + , nTextOfs(0) + , nBulletRealSize(0) + , nDepth(0) + , cBulletId(0) + , bExtendedBulletsUsed(false) + , nBulletId(0xffff) + , nMappedNumType(0) + , bNumberingIsNumber(true) + { + } +}; + +class PropStateValue : public PropValue +{ +public: + PropStateValue() + : PropValue() + , ePropState(css::beans::PropertyState_AMBIGUOUS_VALUE) + { + } +protected: + css::beans::PropertyState ePropState; + css::uno::Reference < css::beans::XPropertyState > mXPropState; + + bool ImplGetPropertyValue( const OUString& rString, bool bGetPropertyState ); +}; + +struct FieldEntry +{ + sal_uInt32 nFieldType; + sal_uInt32 nFieldStartPos; + sal_uInt32 nFieldEndPos; + OUString aRepresentation; + OUString aFieldUrl; + + FieldEntry( sal_uInt32 nType, sal_uInt32 nStart, sal_uInt32 nEnd ) + : nFieldType(nType), + nFieldStartPos(nStart), + nFieldEndPos(nEnd) + { + } +}; + +class PortionObj final : public PropStateValue +{ + + friend class ParagraphObj; + + void ImplClear(); + void ImplConstruct( const PortionObj& rPortionObj ); + static sal_uInt32 ImplGetTextField( css::uno::Reference< css::text::XTextRange > & rXTextRangeRef, + const css::uno::Reference< css::beans::XPropertySet > & rXPropSetRef, OUString& rURL ); + sal_uInt32 ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition ); + void ImplGetPortionValues( FontCollection& rFontCollection, bool bGetPropStateValue ); + + public: + + css::beans::PropertyState meCharColor; + css::beans::PropertyState meCharHeight; + css::beans::PropertyState meFontName; + css::beans::PropertyState meAsianOrComplexFont; + css::beans::PropertyState meCharEscapement; + css::lang::Locale meCharLocale; + sal_uInt16 mnCharAttrHard; + + sal_uInt32 mnCharColor; + sal_uInt16 mnCharAttr; + sal_uInt16 mnCharHeight; + sal_uInt16 mnFont; + sal_uInt16 mnAsianOrComplexFont; + sal_Int16 mnCharEscapement; + + sal_uInt32 mnTextSize; + bool mbLastPortion; + + std::unique_ptr mpText; + std::unique_ptr mpFieldEntry; + + PortionObj( css::uno::Reference< css::text::XTextRange > & rXTextRangeRef, + bool bLast, FontCollection& rFontCollection ); + PortionObj( const css::uno::Reference< css::beans::XPropertySet > & rXPropSetRef, + FontCollection& rFontCollection ); + PortionObj( const PortionObj& rPortionObj ); + ~PortionObj(); + + void Write( SvStream* pStrm, bool bLast ); + sal_uInt32 Count() const { return mnTextSize; }; + + PortionObj& operator=( const PortionObj& rPortionObj ); +}; + +struct ParaFlags +{ + bool bFirstParagraph : 1; + bool bLastParagraph : 1; + + ParaFlags() { bFirstParagraph = true; bLastParagraph = false; }; +}; + +class ParagraphObj : public PropStateValue, public SOParagraph +{ + friend class TextObj; + friend struct PPTExParaSheet; + + std::vector > mvPortions; + + protected: + + void ImplConstruct( const ParagraphObj& rParagraphObj ); + void ImplClear(); + sal_uInt32 ImplCalculateTextPositions( sal_uInt32 nCurrentTextPosition ); + void ImplGetParagraphValues( PPTExBulletProvider* pBuProv, bool bGetPropStateValue ); + void ImplGetNumberingLevel( PPTExBulletProvider* pBuProv, sal_Int16 nDepth, bool bIsBullet, bool bGetPropStateValue ); + + public: + + css::uno::Sequence< css::style::TabStop > maTabStop; + + sal_uInt32 mnTextSize; + + bool mbIsBullet; + bool mbFirstParagraph; + bool mbLastParagraph; + + css::beans::PropertyState meBullet; + css::beans::PropertyState meTextAdjust; + css::beans::PropertyState meLineSpacing; + css::beans::PropertyState meLineSpacingTop; + css::beans::PropertyState meLineSpacingBottom; + css::beans::PropertyState meForbiddenRules; + css::beans::PropertyState meParagraphPunctation; + css::beans::PropertyState meBiDi; + + sal_uInt16 mnTextAdjust; + sal_Int16 mnLineSpacing; + bool mbFixedLineSpacing; + sal_Int16 mnLineSpacingTop; + sal_Int16 mnLineSpacingBottom; + bool mbForbiddenRules; + bool mbParagraphPunctation; + sal_uInt16 mnBiDi; + + ParagraphObj( css::uno::Reference< css::text::XTextContent > const & rXTextContentRef, + ParaFlags, FontCollection& rFontCollection, + PPTExBulletProvider& rBuProv ); + ParagraphObj( const ParagraphObj& rParargraphObj ) = delete; + ParagraphObj( const css::uno::Reference< css::beans::XPropertySet > & rXPropSetRef, + PPTExBulletProvider* pBuProv ); + + bool empty() const { return mvPortions.empty(); } + + const PortionObj& front() const { return *mvPortions.front(); } + + std::vector >::const_iterator begin() const { return mvPortions.begin(); } + std::vector >::const_iterator end() const { return mvPortions.end(); } + + void CalculateGraphicBulletSize( sal_uInt16 nFontHeight ); + ~ParagraphObj(); + + void Write( SvStream* pStrm ); + sal_uInt32 CharacterCount() const { return mnTextSize; }; + + ParagraphObj& operator=( const ParagraphObj& rParagraphObj ); +}; + +struct ImplTextObj; + +class TextObj +{ + std::shared_ptr mpImplTextObj; + void ImplCalculateTextPositions(); + +public: + TextObj( css::uno::Reference< css::text::XSimpleText > const & + rXText, int nInstance, FontCollection& rFontCollection, PPTExBulletProvider& rBuProv ); + + ParagraphObj* GetParagraph(int idx); + sal_uInt32 ParagraphCount() const; + sal_uInt32 Count() const; + int GetInstance() const; + bool HasExtendedBullets() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/grf/sdgrffilter.cxx b/sd/source/filter/grf/sdgrffilter.cxx new file mode 100644 index 000000000..46ed24f3b --- /dev/null +++ b/sd/source/filter/grf/sdgrffilter.cxx @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::graphic; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::ucb; +using namespace com::sun::star::ui::dialogs; +using namespace ::sfx2; + +namespace { + +class SdGRFFilter_ImplInteractionHdl : public ::cppu::WeakImplHelper< css::task::XInteractionHandler > +{ + css::uno::Reference< css::task::XInteractionHandler > m_xInter; + ErrCode nFilterError; + + public: + + explicit SdGRFFilter_ImplInteractionHdl( css::uno::Reference< css::task::XInteractionHandler > const & xInteraction ) : + m_xInter( xInteraction ), + nFilterError( ERRCODE_NONE ) + {} + + ErrCode const & GetErrorCode() const { return nFilterError; }; + + virtual void SAL_CALL handle( const css::uno::Reference< css::task::XInteractionRequest >& ) override; +}; + +} + +void SdGRFFilter_ImplInteractionHdl::handle( const css::uno::Reference< css::task::XInteractionRequest >& xRequest ) +{ + if( !m_xInter.is() ) + return; + + css::drawing::GraphicFilterRequest aErr; + if ( xRequest->getRequest() >>= aErr ) + nFilterError = ErrCode(aErr.ErrCode); + else + m_xInter->handle( xRequest ); +} + + +SdGRFFilter::SdGRFFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell ) : + SdFilter( rMedium, rDocShell ) +{ +} + +SdGRFFilter::~SdGRFFilter() +{ +} + +void SdGRFFilter::HandleGraphicFilterError( ErrCode nFilterError, ErrCode nStreamError ) +{ + if (ERRCODE_NONE != nStreamError) + { + ErrorHandler::HandleError(nStreamError); + return; + } + + TranslateId pId; + + if( nFilterError == ERRCODE_GRFILTER_OPENERROR ) + pId = STR_IMPORT_GRFILTER_OPENERROR; + else if( nFilterError == ERRCODE_GRFILTER_IOERROR ) + pId = STR_IMPORT_GRFILTER_IOERROR; + else if( nFilterError == ERRCODE_GRFILTER_FORMATERROR ) + pId = STR_IMPORT_GRFILTER_FORMATERROR; + else if( nFilterError == ERRCODE_GRFILTER_VERSIONERROR ) + pId = STR_IMPORT_GRFILTER_VERSIONERROR; + else if( nFilterError == ERRCODE_GRFILTER_TOOBIG ) + pId = STR_IMPORT_GRFILTER_TOOBIG; + else if( nFilterError == ERRCODE_NONE ) + ; + else + pId = STR_IMPORT_GRFILTER_FILTERERROR; + + if (pId && pId == STR_IMPORT_GRFILTER_IOERROR) + ErrorHandler::HandleError( ERRCODE_IO_GENERAL ); + else + { + std::unique_ptr xErrorBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::Ok, pId ? SdResId(pId) : OUString())); + xErrorBox->run(); + } +} + +bool SdGRFFilter::Import() +{ + Graphic aGraphic; + const OUString aFileName( mrMedium.GetURLObject().GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + const sal_uInt16 nFilter = rGraphicFilter.GetImportFormatNumberForTypeName( mrMedium.GetFilter()->GetTypeName() ); + bool bRet = false; + + SvStream* pIStm = mrMedium.GetInStream(); + ErrCode nReturn = pIStm ? rGraphicFilter.ImportGraphic( aGraphic, aFileName, *pIStm, nFilter ) : ErrCode(1); + + if( nReturn ) + HandleGraphicFilterError( nReturn, rGraphicFilter.GetLastError() ); + else + { + if( mrDocument.GetPageCount() == 0 ) + mrDocument.CreateFirstPages(); + + SdPage* pPage = mrDocument.GetSdPage( 0, PageKind::Standard ); + Point aPos; + Size aPagSize( pPage->GetSize() ); + Size aGrfSize( OutputDevice::LogicToLogic( aGraphic.GetPrefSize(), + aGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM))); + + aPagSize.AdjustWidth( -(pPage->GetLeftBorder() + pPage->GetRightBorder()) ); + aPagSize.AdjustHeight( -(pPage->GetUpperBorder() + pPage->GetLowerBorder()) ); + + // scale to fit page + if ( ( ( aGrfSize.Height() > aPagSize.Height() ) || ( aGrfSize.Width() > aPagSize.Width() ) ) && + aGrfSize.Height() && aPagSize.Height() ) + { + double fGrfWH = static_cast(aGrfSize.Width()) / aGrfSize.Height(); + double fWinWH = static_cast(aPagSize.Width()) / aPagSize.Height(); + + // adjust graphic to page size (scales) + if( fGrfWH < fWinWH ) + { + aGrfSize.setWidth( static_cast( aPagSize.Height() * fGrfWH ) ); + aGrfSize.setHeight( aPagSize.Height() ); + } + else if( fGrfWH > 0.F ) + { + aGrfSize.setWidth( aPagSize.Width() ); + aGrfSize.setHeight( static_cast( aPagSize.Width() / fGrfWH ) ); + } + } + + // set output rectangle for graphic + aPos.setX( ( ( aPagSize.Width() - aGrfSize.Width() ) >> 1 ) + pPage->GetLeftBorder() ); + aPos.setY( ( ( aPagSize.Height() - aGrfSize.Height() ) >> 1 ) + pPage->GetUpperBorder() ); + + pPage->InsertObject( + new SdrGrafObj( + pPage->getSdrModelFromSdrPage(), + aGraphic, + ::tools::Rectangle(aPos, aGrfSize))); + bRet = true; + } + + return bRet; +} + +bool SdGRFFilter::Export() +{ + // SJ: todo: error handling, the GraphicExportFilter does not support proper errorhandling + bool bRet = false; + + uno::Reference< uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + uno::Reference< drawing::XGraphicExportFilter > xExporter = drawing::GraphicExportFilter::create( xContext ); + + SdPage* pPage = nullptr; + sd::DrawViewShell* pDrawViewShell = dynamic_cast<::sd::DrawViewShell* >(mrDocShell.GetViewShell() ); + + PageKind ePageKind = PageKind::Standard; + if( pDrawViewShell ) + { + ePageKind = pDrawViewShell->GetPageKind(); + if( PageKind::Handout == ePageKind ) + pPage = mrDocument.GetSdPage( 0, PageKind::Handout ); + else + pPage = pDrawViewShell->GetActualPage(); + } + else + pPage = mrDocument.GetSdPage( 0, PageKind::Standard ); + + if ( pPage ) + { + // taking the 'correct' page number, seems that there might exist a better method to archive this + pPage = mrDocument.GetSdPage( pPage->GetPageNum() ? ( pPage->GetPageNum() - 1 ) >> 1 : 0, ePageKind ); + if ( pPage ) + { + uno::Reference< lang::XComponent > xSource( pPage->getUnoPage(), uno::UNO_QUERY ); + SfxItemSet* pSet = mrMedium.GetItemSet(); + if ( pSet && xSource.is() ) + { + const OUString aTypeName( mrMedium.GetFilter()->GetTypeName() ); + GraphicFilter &rGraphicFilter = GraphicFilter::GetGraphicFilter(); + const sal_uInt16 nFilter = rGraphicFilter.GetExportFormatNumberForTypeName( aTypeName ); + if ( nFilter != GRFILTER_FORMAT_NOTFOUND ) + { + uno::Reference< task::XInteractionHandler > xInteractionHandler; + + beans::PropertyValues aArgs; + TransformItems( SID_SAVEASDOC, *pSet, aArgs ); + + static const OUStringLiteral sFilterName( u"FilterName" ); + OUString sShortName( rGraphicFilter.GetExportFormatShortName( nFilter ) ); + + bool bFilterNameFound = false; + for ( auto& rArg : asNonConstRange(aArgs) ) + { + OUString& rStr = rArg.Name; + if ( rStr == sFilterName ) + { + bFilterNameFound = true; + rArg.Value <<= sShortName; + } + else if ( rStr == "InteractionHandler" ) + { + uno::Reference< task::XInteractionHandler > xHdl; + if ( rArg.Value >>= xHdl ) + { + xInteractionHandler = new SdGRFFilter_ImplInteractionHdl( xHdl ); + rArg.Value <<= xInteractionHandler; + } + } + } + if ( !bFilterNameFound ) + { + sal_Int32 nCount = aArgs.getLength(); + aArgs.realloc( nCount + 1 ); + auto pArgs = aArgs.getArray(); + pArgs[ nCount ].Name = sFilterName; + pArgs[ nCount ].Value <<= sShortName; + } + + // take selection if needed + if( ( SfxItemState::SET == pSet->GetItemState( SID_SELECTION ) ) + && pSet->Get( SID_SELECTION ).GetValue() + && pDrawViewShell ) + { + uno::Reference< view::XSelectionSupplier > xSelectionSupplier( + pDrawViewShell->GetViewShellBase().GetController(), uno::UNO_QUERY ); + if ( xSelectionSupplier.is() ) + { + uno::Any aSelection( xSelectionSupplier->getSelection() ); + uno::Reference< lang::XComponent > xSelection; + if ( aSelection >>= xSelection ) + xSource = xSelection; + } + } + xExporter->setSourceDocument( xSource ); + bRet = xExporter->filter( aArgs ); + if ( !bRet && xInteractionHandler.is() ) + SdGRFFilter::HandleGraphicFilterError( + static_cast< SdGRFFilter_ImplInteractionHdl* >( xInteractionHandler.get() )->GetErrorCode(), + rGraphicFilter.GetLastError() ); + } + } + } + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/HtmlOptionsDialog.cxx b/sd/source/filter/html/HtmlOptionsDialog.cxx new file mode 100644 index 000000000..78939dc4d --- /dev/null +++ b/sd/source/filter/html/HtmlOptionsDialog.cxx @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::document; +using namespace com::sun::star::beans; +using namespace com::sun::star::ui::dialogs; + +#include +#include + +namespace { + +class SdHtmlOptionsDialog : public cppu::WeakImplHelper +< + XExporter, + XExecutableDialog, + XPropertyAccess, + XInitialization, + XServiceInfo +> +{ + Sequence< PropertyValue > maMediaDescriptor; + Sequence< PropertyValue > maFilterDataSequence; + DocumentType meDocType; + +public: + + SdHtmlOptionsDialog(); + + // XInterface + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any > & aArguments ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XPropertyAccess + virtual Sequence< PropertyValue > SAL_CALL getPropertyValues() override; + virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< css::beans::PropertyValue > & aProps ) override; + + // XExecuteDialog + virtual sal_Int16 SAL_CALL execute() override; + virtual void SAL_CALL setTitle( const OUString& aTitle ) override; + + // XExporter + virtual void SAL_CALL setSourceDocument( const css::uno::Reference< css::lang::XComponent >& xDoc ) override; + +}; + +} + +SdHtmlOptionsDialog::SdHtmlOptionsDialog() : + meDocType ( DocumentType::Draw ) +{ +} + +void SAL_CALL SdHtmlOptionsDialog::acquire() noexcept +{ + OWeakObject::acquire(); +} + +void SAL_CALL SdHtmlOptionsDialog::release() noexcept +{ + OWeakObject::release(); +} + +// XInitialization +void SAL_CALL SdHtmlOptionsDialog::initialize( const Sequence< Any > & ) +{ +} + +// XServiceInfo +OUString SAL_CALL SdHtmlOptionsDialog::getImplementationName() +{ + return "com.sun.star.comp.draw.SdHtmlOptionsDialog"; +} + +sal_Bool SAL_CALL SdHtmlOptionsDialog::supportsService( const OUString& rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > SAL_CALL SdHtmlOptionsDialog::getSupportedServiceNames() +{ + return { "com.sun.star.ui.dialog.FilterOptionsDialog" }; +} + +// XPropertyAccess +Sequence< PropertyValue > SdHtmlOptionsDialog::getPropertyValues() +{ + auto pProp = std::find_if(std::cbegin(maMediaDescriptor), std::cend(maMediaDescriptor), + [](const PropertyValue& rProp) { return rProp.Name == "FilterData"; }); + auto i = static_cast(std::distance(std::cbegin(maMediaDescriptor), pProp)); + sal_Int32 nCount = maMediaDescriptor.getLength(); + if ( i == nCount ) + maMediaDescriptor.realloc( ++nCount ); + + // the "FilterData" Property is an Any that will contain our PropertySequence of Values + auto& el = maMediaDescriptor.getArray()[ i ]; + el.Name = "FilterData"; + el.Value <<= maFilterDataSequence; + return maMediaDescriptor; +} + +void SdHtmlOptionsDialog::setPropertyValues( const Sequence< PropertyValue > & aProps ) +{ + maMediaDescriptor = aProps; + + auto pProp = std::find_if(std::cbegin(maMediaDescriptor), std::cend(maMediaDescriptor), + [](const PropertyValue& rProp) { return rProp.Name == "FilterData"; }); + if (pProp != std::cend(maMediaDescriptor)) + pProp->Value >>= maFilterDataSequence; +} + +// XExecutableDialog +void SdHtmlOptionsDialog::setTitle( const OUString& ) +{ +} + +sal_Int16 SdHtmlOptionsDialog::execute() +{ + sal_Int16 nRet = ExecutableDialogResults::CANCEL; + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSdPublishingDlg(nullptr /*TODO*/, meDocType)); + if( pDlg->Execute() ) + { + pDlg->GetParameterSequence( maFilterDataSequence ); + nRet = ExecutableDialogResults::OK; + } + else + { + nRet = ExecutableDialogResults::CANCEL; + } + return nRet; +} + +// XEmporter +void SdHtmlOptionsDialog::setSourceDocument( const Reference< XComponent >& xDoc ) +{ + // try to set the corresponding metric unit + Reference< XServiceInfo > xServiceInfo(xDoc, UNO_QUERY); + if ( xServiceInfo.is() ) + { + if ( xServiceInfo->supportsService( "com.sun.star.presentation.PresentationDocument" ) ) + { + meDocType = DocumentType::Impress; + return; + } + else if ( xServiceInfo->supportsService( "com.sun.star.drawing.DrawingDocument" ) ) + { + meDocType = DocumentType::Draw; + return; + } + } + throw IllegalArgumentException(); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_draw_SdHtmlOptionsDialog_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new SdHtmlOptionsDialog()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/buttonset.cxx b/sd/source/filter/html/buttonset.cxx new file mode 100644 index 000000000..3929f7422 --- /dev/null +++ b/sd/source/filter/html/buttonset.cxx @@ -0,0 +1,290 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "buttonset.hxx" + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::graphic; +using namespace ::com::sun::star::embed; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; + +namespace { + +class ButtonsImpl +{ +public: + explicit ButtonsImpl( const OUString& rURL ); + + Reference< XInputStream > getInputStream( const OUString& rName ); + + bool getGraphic( const Reference< XGraphicProvider >& xGraphicProvider, const OUString& rName, Graphic& rGraphic ); + + bool copyGraphic( const OUString& rName, const OUString& rPath ); + +private: + Reference< XStorage > mxStorage; +}; + +} + +ButtonsImpl::ButtonsImpl( const OUString& rURL ) +{ + try + { + mxStorage = comphelper::OStorageHelper::GetStorageOfFormatFromURL( ZIP_STORAGE_FORMAT_STRING, rURL, ElementModes::READ ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::ButtonsImpl::ButtonsImpl()" ); + } +} + +Reference< XInputStream > ButtonsImpl::getInputStream( const OUString& rName ) +{ + Reference< XInputStream > xInputStream; + if( mxStorage.is() ) try + { + Reference< XStream > xStream( mxStorage->openStreamElement( rName, ElementModes::READ ) ); + if( xStream.is() ) + xInputStream = xStream->getInputStream(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::ButtonsImpl::getInputStream()" ); + } + return xInputStream; +} + +bool ButtonsImpl::getGraphic( const Reference< XGraphicProvider >& xGraphicProvider, const OUString& rName, Graphic& rGraphic ) +{ + Reference< XInputStream > xInputStream( getInputStream( rName ) ); + if( xInputStream.is() && xGraphicProvider.is() ) try + { + Sequence< PropertyValue > aMediaProperties{ comphelper::makePropertyValue( + "InputStream", xInputStream) }; + Reference< XGraphic > xGraphic( xGraphicProvider->queryGraphic( aMediaProperties ) ); + + if( xGraphic.is() ) + { + rGraphic = Graphic( xGraphic ); + return true; + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::ButtonsImpl::getGraphic()" ); + } + return false; +} + +bool ButtonsImpl::copyGraphic( const OUString& rName, const OUString& rPath ) +{ + Reference< XInputStream > xInput( getInputStream( rName ) ); + if( xInput.is() ) try + { + osl::File::remove( rPath ); + osl::File aOutputFile( rPath ); + if( aOutputFile.open( osl_File_OpenFlag_Write|osl_File_OpenFlag_Create ) == osl::FileBase::E_None ) + { + Reference< XOutputStream > xOutput( new comphelper::OSLOutputStreamWrapper( aOutputFile ) ); + comphelper::OStorageHelper::CopyInputToOutput( xInput, xOutput ); + return true; + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::ButtonsImpl::copyGraphic()" ); + } + + return false; +} + +class ButtonSetImpl +{ +public: + ButtonSetImpl(); + + int getCount() const; + + bool getPreview( int nSet, const std::vector< OUString >& rButtons, Image& rImage ); + bool exportButton( int nSet, const OUString& rPath, const OUString& rName ); + + void scanForButtonSets( const OUString& rPath ); + + Reference< XGraphicProvider > const & getGraphicProvider(); + + std::vector< std::shared_ptr< ButtonsImpl > > maButtons; + Reference< XGraphicProvider > mxGraphicProvider; +}; + +ButtonSetImpl::ButtonSetImpl() +{ + static const char sSubPath[] = "/wizard/web/buttons" ; + + OUString sSharePath = SvtPathOptions().GetConfigPath() + + sSubPath; + scanForButtonSets( sSharePath ); + + OUString sUserPath = SvtPathOptions().GetUserConfigPath() + + sSubPath; + scanForButtonSets( sUserPath ); +} + +void ButtonSetImpl::scanForButtonSets( const OUString& rPath ) +{ + osl::Directory aDirectory( rPath ); + if( aDirectory.open() != osl::FileBase::E_None ) + return; + + osl::DirectoryItem aItem; + while( aDirectory.getNextItem( aItem, 2211 ) == osl::FileBase::E_None ) + { + osl::FileStatus aStatus( osl_FileStatus_Mask_FileName|osl_FileStatus_Mask_FileURL ); + if( aItem.getFileStatus( aStatus ) == osl::FileBase::E_None ) + { + OUString sFileName( aStatus.getFileName() ); + if( sFileName.endsWithIgnoreAsciiCase( ".zip" ) ) + maButtons.push_back( std::make_shared< ButtonsImpl >( aStatus.getFileURL() ) ); + } + } +} + +int ButtonSetImpl::getCount() const +{ + return maButtons.size(); +} + +bool ButtonSetImpl::getPreview( int nSet, const std::vector< OUString >& rButtons, Image& rImage ) +{ + if( (nSet >= 0) && (o3tl::make_unsigned(nSet) < maButtons.size())) + { + ButtonsImpl& rSet = *maButtons[nSet]; + + std::vector< Graphic > aGraphics; + + ScopedVclPtrInstance< VirtualDevice > pDev; + pDev->SetMapMode(MapMode(MapUnit::MapPixel)); + + Size aSize; + std::vector< OUString >::const_iterator aIter( rButtons.begin() ); + while( aIter != rButtons.end() ) + { + Graphic aGraphic; + if( !rSet.getGraphic( getGraphicProvider(), (*aIter++), aGraphic ) ) + return false; + + aGraphics.push_back(aGraphic); + + Size aGraphicSize( aGraphic.GetSizePixel( pDev ) ); + aSize.AdjustWidth(aGraphicSize.Width() ); + + if( aSize.Height() < aGraphicSize.Height() ) + aSize.setHeight( aGraphicSize.Height() ); + + if( aIter != rButtons.end() ) + aSize.AdjustWidth(3 ); + } + + pDev->SetOutputSizePixel( aSize ); + + Point aPos; + + for( const Graphic& aGraphic : aGraphics ) + { + aGraphic.Draw(*pDev, aPos); + + aPos.AdjustX(aGraphic.GetSizePixel().Width() + 3 ); + } + + rImage = Image( pDev->GetBitmapEx( Point(), aSize ) ); + return true; + } + return false; +} + +bool ButtonSetImpl::exportButton( int nSet, const OUString& rPath, const OUString& rName ) +{ + if( (nSet >= 0) && (o3tl::make_unsigned(nSet) < maButtons.size())) + { + ButtonsImpl& rSet = *maButtons[nSet]; + + return rSet.copyGraphic( rName, rPath ); + } + return false; +} + +Reference< XGraphicProvider > const & ButtonSetImpl::getGraphicProvider() +{ + if( !mxGraphicProvider.is() ) + { + Reference< XComponentContext > xComponentContext = ::comphelper::getProcessComponentContext(); + mxGraphicProvider = GraphicProvider::create(xComponentContext); + } + return mxGraphicProvider; +} + +ButtonSet::ButtonSet() +: mpImpl( new ButtonSetImpl() ) +{ +} + +ButtonSet::~ButtonSet() +{ +} + +int ButtonSet::getCount() const +{ + return mpImpl->getCount(); +} + +bool ButtonSet::getPreview( int nSet, const std::vector< OUString >& rButtons, Image& rImage ) +{ + return mpImpl->getPreview( nSet, rButtons, rImage ); +} + +bool ButtonSet::exportButton( int nSet, const OUString& rPath, const OUString& rName ) +{ + return mpImpl->exportButton( nSet, rPath, rName ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/buttonset.hxx b/sd/source/filter/html/buttonset.hxx new file mode 100644 index 000000000..4289c10e9 --- /dev/null +++ b/sd/source/filter/html/buttonset.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include +#include + +class Image; +class ButtonSetImpl; + +class SD_DLLPUBLIC ButtonSet +{ +public: + ButtonSet(); + ~ButtonSet(); + + int getCount() const; + + bool getPreview(int nSet, const std::vector& rButtons, Image& rImage); + bool exportButton(int nSet, const OUString& rPath, const OUString& rName); + +private: + std::unique_ptr mpImpl; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlattr.cxx b/sd/source/filter/html/htmlattr.cxx new file mode 100644 index 000000000..b89ac9b6b --- /dev/null +++ b/sd/source/filter/html/htmlattr.cxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "htmlattr.hxx" +#include +#include +#include + +SdHtmlAttrPreview::SdHtmlAttrPreview() +{ +} + +SdHtmlAttrPreview::~SdHtmlAttrPreview() +{ +} + +void SdHtmlAttrPreview::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) +{ + ::tools::Rectangle aTextRect; + aTextRect.SetSize(GetOutputSizePixel()); + + rRenderContext.SetLineColor(m_aBackColor); + rRenderContext.SetFillColor(m_aBackColor); + rRenderContext.DrawRect(rRect); + rRenderContext.SetFillColor(); + + int nHeight = (aTextRect.Bottom() - aTextRect.Top()) >> 2; + aTextRect.SetBottom( nHeight + aTextRect.Top() ); + + rRenderContext.SetTextColor(m_aTextColor); + rRenderContext.DrawText(aTextRect, SdResId(STR_HTMLATTR_TEXT), DrawTextFlags::Center | DrawTextFlags::VCenter); + + aTextRect.Move(0,nHeight); + rRenderContext.SetTextColor(m_aLinkColor); + rRenderContext.DrawText(aTextRect, SdResId(STR_HTMLATTR_LINK), DrawTextFlags::Center | DrawTextFlags::VCenter); + + aTextRect.Move(0,nHeight); + rRenderContext.SetTextColor(m_aALinkColor); + rRenderContext.DrawText(aTextRect, SdResId(STR_HTMLATTR_ALINK), DrawTextFlags::Center | DrawTextFlags::VCenter); + + aTextRect.Move(0,nHeight); + rRenderContext.SetTextColor(m_aVLinkColor); + rRenderContext.DrawText(aTextRect, SdResId(STR_HTMLATTR_VLINK), DrawTextFlags::Center | DrawTextFlags::VCenter); +} + +void SdHtmlAttrPreview::SetColors(Color const & aBack, Color const & aText, Color const & aLink, + Color const & aVLink, Color const & aALink) +{ + m_aBackColor = aBack; + m_aTextColor = aText; + m_aLinkColor = aLink; + m_aVLinkColor = aVLink; + m_aALinkColor = aALink; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlattr.hxx b/sd/source/filter/html/htmlattr.hxx new file mode 100644 index 000000000..bf80b9e4b --- /dev/null +++ b/sd/source/filter/html/htmlattr.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SdHtmlAttrPreview final : public weld::CustomWidgetController +{ + Color m_aBackColor, m_aTextColor, m_aLinkColor; + Color m_aVLinkColor, m_aALinkColor; + +public: + SdHtmlAttrPreview(); + virtual ~SdHtmlAttrPreview() override; + + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + + void SetColors( Color const & aBack, Color const & aText, Color const & aLink, + Color const & aVLink, Color const & aALink ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlex.cxx b/sd/source/filter/html/htmlex.cxx new file mode 100644 index 000000000..072ac3c27 --- /dev/null +++ b/sd/source/filter/html/htmlex.cxx @@ -0,0 +1,3186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "htmlex.hxx" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "htmlpublishmode.hxx" +#include +#include +#include +#include +#include +#include +#include "buttonset.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::document; + +using namespace sdr::table; + +// get parameter from Itemset +#define RESTOHTML( res ) StringToHTMLString(SdResId(res)) + +const char * const pButtonNames[] = +{ + "first-inactive.png", + "first.png", + "left-inactive.png", + "left.png", + "right-inactive.png", + "right.png", + "last-inactive.png", + "last.png", + "home.png", + "text.png", + "expand.png", + "collapse.png", +}; + +#define BTN_FIRST_0 0 +#define BTN_FIRST_1 1 +#define BTN_PREV_0 2 +#define BTN_PREV_1 3 +#define BTN_NEXT_0 4 +#define BTN_NEXT_1 5 +#define BTN_LAST_0 6 +#define BTN_LAST_1 7 +#define BTN_INDEX 8 +#define BTN_TEXT 9 +#define BTN_MORE 10 +#define BTN_LESS 11 + +namespace { + +// Helper class for the simple creation of files local/remote +class EasyFile +{ +private: + std::unique_ptr pOStm; + bool bOpen; + +public: + + EasyFile(); + ~EasyFile(); + + ErrCode createStream( const OUString& rUrl, SvStream*& rpStr ); + void createFileName( const OUString& rUrl, OUString& rFileName ); + void close(); +}; + +} + +// Helper class for the embedding of text attributes into the html output +class HtmlState +{ +private: + bool mbColor; + bool mbWeight; + bool mbItalic; + bool mbUnderline; + bool mbStrike; + bool mbLink; + Color maColor; + Color maDefColor; + OUString maLink; + OUString maTarget; + +public: + explicit HtmlState( Color aDefColor ); + + OUString SetWeight( bool bWeight ); + OUString SetItalic( bool bItalic ); + OUString SetUnderline( bool bUnderline ); + OUString SetColor( Color aColor ); + OUString SetStrikeout( bool bStrike ); + OUString SetLink( const OUString& aLink, const OUString& aTarget ); + OUString Flush(); +}; + +// close all still open tags +OUString HtmlState::Flush() +{ + OUString aStr = SetWeight(false) + + SetItalic(false) + + SetUnderline(false) + + SetStrikeout(false) + + SetColor(maDefColor) + + SetLink("",""); + + return aStr; +} + +// c'tor with default color for the page +HtmlState::HtmlState( Color aDefColor ) + : mbColor(false), + mbWeight(false), + mbItalic(false), + mbUnderline(false), + mbStrike(false), + mbLink(false), + maDefColor(aDefColor) +{ +} + +// enables/disables bold print +OUString HtmlState::SetWeight( bool bWeight ) +{ + OUString aStr; + + if(bWeight && !mbWeight) + aStr = ""; + else if(!bWeight && mbWeight) + aStr = ""; + + mbWeight = bWeight; + return aStr; +} + +// enables/disables italic + +OUString HtmlState::SetItalic( bool bItalic ) +{ + OUString aStr; + + if(bItalic && !mbItalic) + aStr = ""; + else if(!bItalic && mbItalic) + aStr = ""; + + mbItalic = bItalic; + return aStr; +} + +// enables/disables underlines + +OUString HtmlState::SetUnderline( bool bUnderline ) +{ + OUString aStr; + + if(bUnderline && !mbUnderline) + aStr = ""; + else if(!bUnderline && mbUnderline) + aStr = ""; + + mbUnderline = bUnderline; + return aStr; +} + +// enables/disables strike through +OUString HtmlState::SetStrikeout( bool bStrike ) +{ + OUString aStr; + + if(bStrike && !mbStrike) + aStr = ""; + else if(!bStrike && mbStrike) + aStr = ""; + + mbStrike = bStrike; + return aStr; +} + +// Sets the specified text color +OUString HtmlState::SetColor( Color aColor ) +{ + OUString aStr; + + if(mbColor && aColor == maColor) + return aStr; + + if(mbColor) + { + aStr = ""; + mbColor = false; + } + + if(aColor != maDefColor) + { + maColor = aColor; + aStr += ""; + mbColor = true; + } + + return aStr; +} + +// enables/disables a hyperlink +OUString HtmlState::SetLink( const OUString& aLink, const OUString& aTarget ) +{ + OUString aStr; + + if(mbLink&&maLink == aLink&&maTarget==aTarget) + return aStr; + + if(mbLink) + { + aStr = ""; + mbLink = false; + } + + if (!aLink.isEmpty()) + { + aStr += ""; + mbLink = true; + maLink = aLink; + maTarget = aTarget; + } + + return aStr; +} +namespace +{ + +OUString getParagraphStyle( const SdrOutliner* pOutliner, sal_Int32 nPara ) +{ + SfxItemSet aParaSet( pOutliner->GetParaAttribs( nPara ) ); + + OUString sStyle; + + if( aParaSet.GetItem( EE_PARA_WRITINGDIR )->GetValue() == SvxFrameDirection::Horizontal_RL_TB ) + { + + sStyle = "direction: rtl;"; + } + else + { + // This is the default so don't write it out + // sStyle += "direction: ltr;"; + } + return sStyle; +} + +void lclAppendStyle(OUStringBuffer& aBuffer, std::u16string_view aTag, std::u16string_view aStyle) +{ + if (aStyle.empty()) + aBuffer.append(OUString::Concat("<") + aTag + ">"); + else + aBuffer.append(OUString::Concat("<") + aTag + " style=\"" + aStyle + "\">"); +} + +} // anonymous namespace + +constexpr OUStringLiteral gaHTMLHeader( + u"\r\n" + "\r\n\r\n" ); + +constexpr OUStringLiteral gaHTMLExtension = u"" STR_HTMLEXP_DEFAULT_EXTENSION; + +// constructor for the html export helper classes +HtmlExport::HtmlExport( + const OUString& aPath, + const Sequence< PropertyValue >& rParams, + SdDrawDocument* pExpDoc, + sd::DrawDocShell* pDocShell ) + : maPath( aPath ), + mpDoc(pExpDoc), + mpDocSh( pDocShell ), + meMode( PUBLISH_SINGLE_DOCUMENT ), + mbContentsPage(false), + mnButtonThema(-1), + mnWidthPixel( PUB_MEDRES_WIDTH ), + meFormat( FORMAT_JPG ), + mbNotes(false), + mnCompression( -1 ), + mbDownload( false ), + mbSlideSound(true), + mbHiddenSlides(true), + mbUserAttr(false), + maTextColor(COL_BLACK), + maBackColor(COL_WHITE), + mbDocColors(false), + maIndexUrl("index"), + meScript( SCRIPT_ASP ), + mpButtonSet( new ButtonSet() ) +{ + bool bChange = mpDoc->IsChanged(); + + maIndexUrl += gaHTMLExtension; + + InitExportParameters( rParams ); + + switch( meMode ) + { + case PUBLISH_HTML: + case PUBLISH_FRAMES: + ExportHtml(); + break; + case PUBLISH_WEBCAST: + ExportWebCast(); + break; + case PUBLISH_KIOSK: + ExportKiosk(); + break; + case PUBLISH_SINGLE_DOCUMENT: + ExportSingleDocument(); + break; + } + + mpDoc->SetChanged(bChange); +} + +HtmlExport::~HtmlExport() +{ +} + +// get common export parameters from item set +void HtmlExport::InitExportParameters( const Sequence< PropertyValue >& rParams ) +{ + mbImpress = mpDoc->GetDocumentType() == DocumentType::Impress; + + OUString aStr; + for( const PropertyValue& rParam : rParams ) + { + if ( rParam.Name == "PublishMode" ) + { + sal_Int32 temp = 0; + rParam.Value >>= temp; + meMode = static_cast(temp); + } + else if ( rParam.Name == "IndexURL" ) + { + rParam.Value >>= aStr; + maIndexUrl = aStr; + } + else if ( rParam.Name == "Format" ) + { + sal_Int32 temp = 0; + rParam.Value >>= temp; + meFormat = static_cast(temp); + } + else if ( rParam.Name == "Compression" ) + { + rParam.Value >>= aStr; + OUString aTmp( aStr ); + if(!aTmp.isEmpty()) + { + aTmp = aTmp.replaceFirst("%", ""); + mnCompression = static_cast(aTmp.toInt32()); + } + } + else if ( rParam.Name == "Width" ) + { + sal_Int32 temp = 0; + rParam.Value >>= temp; + mnWidthPixel = static_cast(temp); + } + else if ( rParam.Name == "UseButtonSet" ) + { + sal_Int32 temp = 0; + rParam.Value >>= temp; + mnButtonThema = static_cast(temp); + } + else if ( rParam.Name == "IsExportNotes" ) + { + if( mbImpress ) + { + bool temp = false; + rParam.Value >>= temp; + mbNotes = temp; + } + } + else if ( rParam.Name == "IsExportContentsPage" ) + { + bool temp = false; + rParam.Value >>= temp; + mbContentsPage = temp; + } + else if ( rParam.Name == "Author" ) + { + rParam.Value >>= aStr; + maAuthor = aStr; + } + else if ( rParam.Name == "EMail" ) + { + rParam.Value >>= aStr; + maEMail = aStr; + } + else if ( rParam.Name == "HomepageURL" ) + { + rParam.Value >>= aStr; + maHomePage = aStr; + } + else if ( rParam.Name == "UserText" ) + { + rParam.Value >>= aStr; + maInfo = aStr; + } + else if ( rParam.Name == "EnableDownload" ) + { + bool temp = false; + rParam.Value >>= temp; + mbDownload = temp; + } + else if ( rParam.Name == "SlideSound" ) + { + bool temp = true; + rParam.Value >>= temp; + mbSlideSound = temp; + } + else if ( rParam.Name == "HiddenSlides" ) + { + bool temp = true; + rParam.Value >>= temp; + mbHiddenSlides = temp; + } + else if ( rParam.Name == "BackColor" ) + { + Color temp; + rParam.Value >>= temp; + maBackColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "TextColor" ) + { + Color temp; + rParam.Value >>= temp; + maTextColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "LinkColor" ) + { + Color temp ; + rParam.Value >>= temp; + maLinkColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "VLinkColor" ) + { + Color temp; + rParam.Value >>= temp; + maVLinkColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "ALinkColor" ) + { + Color temp; + rParam.Value >>= temp; + maALinkColor = temp; + mbUserAttr = true; + } + else if ( rParam.Name == "IsUseDocumentColors" ) + { + bool temp = false; + rParam.Value >>= temp; + mbDocColors = temp; + } + else if ( rParam.Name == "KioskSlideDuration" ) + { + double temp = 0.0; + rParam.Value >>= temp; + mfSlideDuration = temp; + mbAutoSlide = true; + } + else if ( rParam.Name == "KioskEndless" ) + { + bool temp = false; + rParam.Value >>= temp; + mbEndless = temp; + } + else if ( rParam.Name == "WebCastCGIURL" ) + { + rParam.Value >>= aStr; + maCGIPath = aStr; + } + else if ( rParam.Name == "WebCastTargetURL" ) + { + rParam.Value >>= aStr; + maURLPath = aStr; + } + else if ( rParam.Name == "WebCastScriptLanguage" ) + { + rParam.Value >>= aStr; + if ( aStr == "asp" ) + { + meScript = SCRIPT_ASP; + } + else + { + meScript = SCRIPT_PERL; + } + } + else + { + OSL_FAIL("Unknown property for html export detected!"); + } + } + + if( meMode == PUBLISH_KIOSK ) + { + mbContentsPage = false; + mbNotes = false; + + } + + // calculate image sizes + SdPage* pPage = mpDoc->GetSdPage(0, PageKind::Standard); + Size aTmpSize( pPage->GetSize() ); + double dRatio=static_cast(aTmpSize.Width())/aTmpSize.Height(); + + mnHeightPixel = static_cast(mnWidthPixel/dRatio); + + // we come up with a destination... + + INetURLObject aINetURLObj( maPath ); + DBG_ASSERT( aINetURLObj.GetProtocol() != INetProtocol::NotValid, "invalid URL" ); + + maExportPath = aINetURLObj.GetPartBeforeLastName(); // with trailing '/' + maIndex = aINetURLObj.GetLastName(); + + mnSdPageCount = mpDoc->GetSdPageCount( PageKind::Standard ); + for( sal_uInt16 nPage = 0; nPage < mnSdPageCount; nPage++ ) + { + pPage = mpDoc->GetSdPage( nPage, PageKind::Standard ); + + if( mbHiddenSlides || !pPage->IsExcluded() ) + { + maPages.push_back( pPage ); + maNotesPages.push_back( mpDoc->GetSdPage( nPage, PageKind::Notes ) ); + } + } + mnSdPageCount = maPages.size(); + + mbFrames = meMode == PUBLISH_FRAMES; + + maDocFileName = maIndex; +} + +void HtmlExport::ExportSingleDocument() +{ + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + + maPageNames.resize(mnSdPageCount); + + mnPagesWritten = 0; + InitProgress(mnSdPageCount); + + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(DocumentMetadata()); + aStr.append("\r\n"); + aStr.append("\r\n"); + aStr.append(CreateBodyTag()); + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; ++nSdPage) + { + SdPage* pPage = maPages[nSdPage]; + maPageNames[nSdPage] = pPage->GetName(); + + if( mbDocColors ) + { + SetDocColors( pPage ); + } + + // page title + OUString sTitleText(CreateTextForTitle(pOutliner, pPage, pPage->GetPageBackgroundColor())); + OUString sStyle; + + if (nSdPage != 0) // First page - no need for a page break here + sStyle += "page-break-before:always; "; + sStyle += getParagraphStyle(pOutliner, 0); + + lclAppendStyle(aStr, u"h1", sStyle); + + aStr.append(sTitleText); + aStr.append("\r\n"); + + // write outline text + aStr.append(CreateTextForPage( pOutliner, pPage, true, pPage->GetPageBackgroundColor() )); + + // notes + if(mbNotes) + { + SdPage* pNotesPage = maNotesPages[ nSdPage ]; + OUString aNotesStr( CreateTextForNotesPage( pOutliner, pNotesPage, maBackColor) ); + + if (!aNotesStr.isEmpty()) + { + aStr.append("
\r\n

"); + aStr.append(RESTOHTML(STR_HTMLEXP_NOTES)); + aStr.append(":

\r\n"); + + aStr.append(aNotesStr); + } + } + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + } + + // close page + aStr.append("\r\n"); + + WriteHtml(maDocFileName, false, aStr.makeStringAndClear()); + + pOutliner->Clear(); + ResetProgress(); +} + +// exports the (in the c'tor specified impress document) to html +void HtmlExport::ExportHtml() +{ + if(mbUserAttr) + { + if( maTextColor == COL_AUTO ) + { + if( !maBackColor.IsDark() ) + maTextColor = COL_BLACK; + } + } + else if( mbDocColors ) + { + // default colors for the color schema 'From Document' + SetDocColors(); + maFirstPageColor = maBackColor; + } + + // get name for downloadable presentation if needed + if( mbDownload ) + { + // fade out separator search and extension + sal_Int32 nSepPos = maDocFileName.indexOf('.'); + if (nSepPos != -1) + maDocFileName = maDocFileName.copy(0, nSepPos); + + maDocFileName += ".odp"; + } + + sal_uInt16 nProgrCount = mnSdPageCount; + nProgrCount += mbImpress?mnSdPageCount:0; + nProgrCount += mbContentsPage?1:0; + nProgrCount += (mbFrames && mbNotes)?mnSdPageCount:0; + nProgrCount += mbFrames ? 8 : 0; + InitProgress( nProgrCount ); + + mpDocSh->SetWaitCursor( true ); + + // Exceptions are cool... + + CreateFileNames(); + + // this is not a true while + while( true ) + { + if( checkForExistingFiles() ) + break; + + if( !CreateImagesForPresPages() ) + break; + + if( mbContentsPage && + !CreateImagesForPresPages( true ) ) + break; + + if( !CreateHtmlForPresPages() ) + break; + + if( mbImpress ) + if( !CreateHtmlTextForPresPages() ) + break; + + if( mbFrames ) + { + if( !CreateFrames() ) + break; + + if( !CreateOutlinePages() ) + break; + + if( !CreateNavBarFrames() ) + break; + + if( mbNotes && mbImpress ) + if( !CreateNotesPages() ) + break; + + } + + if( mbContentsPage ) + if( !CreateContentPage() ) + break; + + CreateBitmaps(); + + mpDocSh->SetWaitCursor( false ); + ResetProgress(); + + if( mbDownload ) + SavePresentation(); + + return; + } + + // if we get to this point the export was + // canceled by the user after an error + mpDocSh->SetWaitCursor( false ); + ResetProgress(); +} + +void HtmlExport::SetDocColors( SdPage* pPage ) +{ + if( pPage == nullptr ) + pPage = mpDoc->GetSdPage(0, PageKind::Standard); + + svtools::ColorConfig aConfig; + maVLinkColor = aConfig.GetColorValue(svtools::LINKSVISITED).nColor; + maALinkColor = aConfig.GetColorValue(svtools::LINKS).nColor; + maLinkColor = aConfig.GetColorValue(svtools::LINKS).nColor; + maTextColor = COL_BLACK; + + SfxStyleSheet* pSheet = nullptr; + + if( mpDoc->GetDocumentType() == DocumentType::Impress ) + { + // default text color from the outline template of the first page + pSheet = pPage->GetStyleSheetForPresObj(PresObjKind::Outline); + if(pSheet == nullptr) + pSheet = pPage->GetStyleSheetForPresObj(PresObjKind::Text); + if(pSheet == nullptr) + pSheet = pPage->GetStyleSheetForPresObj(PresObjKind::Title); + } + + if(pSheet == nullptr) + pSheet = mpDoc->GetDefaultStyleSheet(); + + if(pSheet) + { + SfxItemSet& rSet = pSheet->GetItemSet(); + if(rSet.GetItemState(EE_CHAR_COLOR) == SfxItemState::SET) + maTextColor = rSet.GetItem(EE_CHAR_COLOR)->GetValue(); + } + + // default background from the background of the master page of the first page + maBackColor = pPage->GetPageBackgroundColor(); + + if( maTextColor == COL_AUTO ) + { + if( !maBackColor.IsDark() ) + maTextColor = COL_BLACK; + } +} + +void HtmlExport::InitProgress( sal_uInt16 nProgrCount ) +{ + mpProgress.reset(new SfxProgress( mpDocSh, SdResId(STR_CREATE_PAGES), nProgrCount )); +} + +void HtmlExport::ResetProgress() +{ + mpProgress.reset(); +} + +void HtmlExport::ExportKiosk() +{ + mnPagesWritten = 0; + InitProgress( 2*mnSdPageCount ); + + CreateFileNames(); + if( !checkForExistingFiles() ) + { + if( CreateImagesForPresPages() ) + CreateHtmlForPresPages(); + } + + ResetProgress(); +} + +// Export Document with WebCast (TM) Technology +void HtmlExport::ExportWebCast() +{ + mnPagesWritten = 0; + InitProgress( mnSdPageCount + 9 ); + + mpDocSh->SetWaitCursor( true ); + + CreateFileNames(); + + if (maCGIPath.isEmpty()) + maCGIPath = "."; + + if (!maCGIPath.endsWith("/")) + maCGIPath += "/"; + + if( meScript == SCRIPT_ASP ) + { + maURLPath = "./"; + } + else + { + if (maURLPath.isEmpty()) + maURLPath = "."; + + if (!maURLPath.endsWith("/")) + maURLPath += "/"; + } + + // this is not a true while + while(true) + { + if( checkForExistingFiles() ) + break; + + if(!CreateImagesForPresPages()) + break; + + if( meScript == SCRIPT_ASP ) + { + if(!CreateASPScripts()) + break; + } + else + { + if(!CreatePERLScripts()) + break; + } + + if(!CreateImageFileList()) + break; + + if(!CreateImageNumberFile()) + break; + + break; + } + + mpDocSh->SetWaitCursor( false ); + ResetProgress(); +} + +// Save the presentation as a downloadable file in the dest directory +bool HtmlExport::SavePresentation() +{ + meEC.SetContext( STR_HTMLEXP_ERROR_CREATE_FILE, maDocFileName ); + + OUString aURL(maExportPath + maDocFileName); + + mpDocSh->EnableSetModified(); + + try + { + uno::Reference< frame::XStorable > xStorable( mpDoc->getUnoModel(), uno::UNO_QUERY ); + if( xStorable.is() ) + { + uno::Sequence< beans::PropertyValue > aProperties{ + comphelper::makePropertyValue("Overwrite", true), + comphelper::makePropertyValue("FilterName", OUString("impress8")) + }; + xStorable->storeToURL( aURL, aProperties ); + + mpDocSh->EnableSetModified( false ); + + return true; + } + } + catch( Exception& ) + { + } + + mpDocSh->EnableSetModified( false ); + + return false; +} + +// create image files +bool HtmlExport::CreateImagesForPresPages( bool bThumbnail) +{ + try + { + Reference < XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + + Reference< drawing::XGraphicExportFilter > xGraphicExporter = drawing::GraphicExportFilter::create( xContext ); + + Sequence< PropertyValue > aFilterData(((meFormat==FORMAT_JPG)&&(mnCompression != -1))? 3 : 2); + auto pFilterData = aFilterData.getArray(); + pFilterData[0].Name = "PixelWidth"; + pFilterData[0].Value <<= static_cast(bThumbnail ? PUB_THUMBNAIL_WIDTH : mnWidthPixel ); + pFilterData[1].Name = "PixelHeight"; + pFilterData[1].Value <<= static_cast(bThumbnail ? PUB_THUMBNAIL_HEIGHT : mnHeightPixel); + if((meFormat==FORMAT_JPG)&&(mnCompression != -1)) + { + pFilterData[2].Name = "Quality"; + pFilterData[2].Value <<= static_cast(mnCompression); + } + + OUString sFormat; + if( meFormat == FORMAT_PNG ) + sFormat = "PNG"; + else if( meFormat == FORMAT_GIF ) + sFormat = "GIF"; + else + sFormat = "JPG"; + + Sequence< PropertyValue > aDescriptor{ + comphelper::makePropertyValue("URL", Any()), + comphelper::makePropertyValue("FilterName", sFormat), + comphelper::makePropertyValue("FilterData", aFilterData) + }; + auto pDescriptor = aDescriptor.getArray(); + + for (sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + SdPage* pPage = maPages[ nSdPage ]; + + OUString aFull(maExportPath); + if (bThumbnail) + aFull += maThumbnailFiles[nSdPage]; + else + aFull += maImageFiles[nSdPage]; + + pDescriptor[0].Value <<= aFull; + + Reference< XComponent > xPage( pPage->getUnoPage(), UNO_QUERY ); + xGraphicExporter->setSourceDocument( xPage ); + xGraphicExporter->filter( aDescriptor ); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + } + catch( Exception& ) + { + return false; + } + + return true; +} + +// get SdrTextObject with layout text of this page +SdrTextObj* HtmlExport::GetLayoutTextObject(SdrPage const * pPage) +{ + const size_t nObjectCount = pPage->GetObjCount(); + SdrTextObj* pResult = nullptr; + + for (size_t nObject = 0; nObject < nObjectCount; ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject->GetObjInventor() == SdrInventor::Default && + pObject->GetObjIdentifier() == SdrObjKind::OutlineText) + { + pResult = static_cast(pObject); + break; + } + } + return pResult; +} + +// create HTML text version of impress pages +OUString HtmlExport::CreateMetaCharset() +{ + OUString aStr; + const char *pCharSet = rtl_getBestMimeCharsetFromTextEncoding( RTL_TEXTENCODING_UTF8 ); + if ( pCharSet ) + { + aStr = " \r\n"; + } + return aStr; +} + +OUString HtmlExport::DocumentMetadata() const +{ + SvMemoryStream aStream; + + uno::Reference xDocProps; + if (mpDocSh) + { + uno::Reference xDPS( + mpDocSh->GetModel(), uno::UNO_QUERY_THROW); + xDocProps.set(xDPS->getDocumentProperties()); + } + + SfxFrameHTMLWriter::Out_DocInfo(aStream, maDocFileName, xDocProps, + " "); + + const sal_uInt64 nLen = aStream.GetSize(); + OSL_ENSURE(nLen < o3tl::make_unsigned(SAL_MAX_INT32), "Stream can't fit in OString"); + OString aData(static_cast(aStream.GetData()), static_cast(nLen)); + + return OStringToOUString(aData, RTL_TEXTENCODING_UTF8); +} + +bool HtmlExport::CreateHtmlTextForPresPages() +{ + bool bOk = true; + + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount && bOk; nSdPage++) + { + SdPage* pPage = maPages[ nSdPage ]; + + if( mbDocColors ) + { + SetDocColors( pPage ); + } + + // HTML head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" "); + aStr.append(StringToHTMLString(maPageNames[nSdPage])); + aStr.append("\r\n"); + aStr.append("\r\n"); + aStr.append(CreateBodyTag()); + + // navigation bar + aStr.append(CreateNavBar(nSdPage, true)); + + // page title + OUString sTitleText( CreateTextForTitle(pOutliner,pPage, pPage->GetPageBackgroundColor()) ); + lclAppendStyle(aStr, u"h1", getParagraphStyle(pOutliner, 0)); + aStr.append(sTitleText); + aStr.append("\r\n"); + + // write outline text + aStr.append(CreateTextForPage( pOutliner, pPage, true, pPage->GetPageBackgroundColor() )); + + // notes + if(mbNotes) + { + SdPage* pNotesPage = maNotesPages[ nSdPage ]; + OUString aNotesStr( CreateTextForNotesPage( pOutliner, pNotesPage, maBackColor) ); + + if (!aNotesStr.isEmpty()) + { + aStr.append("
\r\n

"); + aStr.append(RESTOHTML(STR_HTMLEXP_NOTES)); + aStr.append(":

\r\n"); + + aStr.append(aNotesStr); + } + } + + // close page + aStr.append("\r\n"); + + bOk = WriteHtml(maTextFiles[nSdPage], false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + } + + pOutliner->Clear(); + + return bOk; +} + +/** exports the given html data into a non unicode file in the current export path with + the given filename */ +bool HtmlExport::WriteHtml( const OUString& rFileName, bool bAddExtension, std::u16string_view rHtmlData ) +{ + ErrCode nErr = ERRCODE_NONE; + + OUString aFileName( rFileName ); + if( bAddExtension ) + aFileName += gaHTMLExtension; + + meEC.SetContext( STR_HTMLEXP_ERROR_CREATE_FILE, rFileName ); + EasyFile aFile; + SvStream* pStr; + OUString aFull(maExportPath + aFileName); + nErr = aFile.createStream(aFull , pStr); + if(nErr == ERRCODE_NONE) + { + OString aStr(OUStringToOString(rHtmlData, RTL_TEXTENCODING_UTF8)); + pStr->WriteOString( aStr ); + aFile.close(); + } + + if( nErr != ERRCODE_NONE ) + ErrorHandler::HandleError(nErr); + + return nErr == ERRCODE_NONE; +} + +/** creates an outliner text for the title objects of a page + */ +OUString HtmlExport::CreateTextForTitle( SdrOutliner* pOutliner, SdPage* pPage, const Color& rBackgroundColor ) +{ + SdrTextObj* pTO = static_cast(pPage->GetPresObj(PresObjKind::Title)); + if(!pTO) + pTO = GetLayoutTextObject(pPage); + + if (pTO && !pTO->IsEmptyPresObj()) + { + OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject(); + if(pOPO && pOutliner->GetParagraphCount() != 0) + { + pOutliner->Clear(); + pOutliner->SetText(*pOPO); + return ParagraphToHTMLString(pOutliner,0, rBackgroundColor); + } + } + + return OUString(); +} + +// creates an outliner text for a page +OUString HtmlExport::CreateTextForPage(SdrOutliner* pOutliner, SdPage const * pPage, + bool bHeadLine, const Color& rBackgroundColor) +{ + OUStringBuffer aStr; + + for (size_t i = 0; i GetObjCount(); ++i ) + { + SdrObject* pObject = pPage->GetObj(i); + PresObjKind eKind = pPage->GetPresObjKind(pObject); + + switch (eKind) + { + case PresObjKind::NONE: + { + if (pObject->GetObjIdentifier() == SdrObjKind::Group) + { + SdrObjGroup* pObjectGroup = static_cast(pObject); + WriteObjectGroup(aStr, pObjectGroup, pOutliner, rBackgroundColor, false); + } + else if (pObject->GetObjIdentifier() == SdrObjKind::Table) + { + SdrTableObj* pTableObject = static_cast(pObject); + WriteTable(aStr, pTableObject, pOutliner, rBackgroundColor); + } + else + { + if (pObject->GetOutlinerParaObject()) + { + WriteOutlinerParagraph(aStr, pOutliner, pObject->GetOutlinerParaObject(), rBackgroundColor, false); + } + } + } + break; + + case PresObjKind::Table: + { + SdrTableObj* pTableObject = static_cast(pObject); + WriteTable(aStr, pTableObject, pOutliner, rBackgroundColor); + } + break; + + case PresObjKind::Text: + case PresObjKind::Outline: + { + SdrTextObj* pTextObject = static_cast(pObject); + if (pTextObject->IsEmptyPresObj()) + continue; + WriteOutlinerParagraph(aStr, pOutliner, pTextObject->GetOutlinerParaObject(), rBackgroundColor, bHeadLine); + } + break; + + default: + break; + } + } + return aStr.makeStringAndClear(); +} + +void HtmlExport::WriteTable(OUStringBuffer& aStr, SdrTableObj const * pTableObject, SdrOutliner* pOutliner, const Color& rBackgroundColor) +{ + CellPos aStart, aEnd; + + aStart = SdrTableObj::getFirstCell(); + aEnd = pTableObject->getLastCell(); + + sal_Int32 nColCount = pTableObject->getColumnCount(); + aStr.append("\r\n"); + for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++) + { + aStr.append(" \r\n"); + for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++) + { + aStr.append(" \r\n"); + } + aStr.append(" \r\n"); + } + aStr.append("
\r\n"); + sal_Int32 nCellIndex = nRow * nColCount + nCol; + SdrText* pText = pTableObject->getText(nCellIndex); + + if (pText == nullptr) + continue; + WriteOutlinerParagraph(aStr, pOutliner, pText->GetOutlinerParaObject(), rBackgroundColor, false); + aStr.append("
\r\n"); +} + +void HtmlExport::WriteObjectGroup(OUStringBuffer& aStr, SdrObjGroup const * pObjectGroup, SdrOutliner* pOutliner, + const Color& rBackgroundColor, bool bHeadLine) +{ + SdrObjListIter aGroupIterator(pObjectGroup->GetSubList(), SdrIterMode::DeepNoGroups); + while (aGroupIterator.IsMore()) + { + SdrObject* pCurrentObject = aGroupIterator.Next(); + if (pCurrentObject->GetObjIdentifier() == SdrObjKind::Group) + { + SdrObjGroup* pCurrentGroupObject = static_cast(pCurrentObject); + WriteObjectGroup(aStr, pCurrentGroupObject, pOutliner, rBackgroundColor, bHeadLine); + } + else + { + OutlinerParaObject* pOutlinerParagraphObject = pCurrentObject->GetOutlinerParaObject(); + if (pOutlinerParagraphObject != nullptr) + { + WriteOutlinerParagraph(aStr, pOutliner, pOutlinerParagraphObject, rBackgroundColor, bHeadLine); + } + } + } +} + +void HtmlExport::WriteOutlinerParagraph(OUStringBuffer& aStr, SdrOutliner* pOutliner, + OutlinerParaObject const * pOutlinerParagraphObject, + const Color& rBackgroundColor, bool bHeadLine) +{ + if (pOutlinerParagraphObject == nullptr) + return; + + pOutliner->SetText(*pOutlinerParagraphObject); + + sal_Int32 nCount = pOutliner->GetParagraphCount(); + + + sal_Int16 nCurrentDepth = -1; + + for (sal_Int32 nIndex = 0; nIndex < nCount; nIndex++) + { + Paragraph* pParagraph = pOutliner->GetParagraph(nIndex); + if(pParagraph == nullptr) + continue; + + const sal_Int16 nDepth = static_cast(pOutliner->GetDepth(nIndex)); + OUString aParaText = ParagraphToHTMLString(pOutliner, nIndex, rBackgroundColor); + + if (aParaText.isEmpty()) + continue; + + if (nDepth < 0) + { + OUString aTag = bHeadLine ? OUString("h2") : OUString("p"); + lclAppendStyle(aStr, aTag, getParagraphStyle(pOutliner, nIndex)); + + aStr.append(aParaText); + aStr.append("\r\n"); + } + else + { + while(nCurrentDepth < nDepth) + { + aStr.append("
    \r\n"); + nCurrentDepth++; + } + while(nCurrentDepth > nDepth) + { + aStr.append("
\r\n"); + nCurrentDepth--; + } + lclAppendStyle(aStr, u"li", getParagraphStyle(pOutliner, nIndex)); + aStr.append(aParaText); + aStr.append("\r\n"); + } + } + while(nCurrentDepth >= 0) + { + aStr.append("\r\n"); + nCurrentDepth--; + } + pOutliner->Clear(); +} + +// creates an outliner text for a note page +OUString HtmlExport::CreateTextForNotesPage( SdrOutliner* pOutliner, + SdPage* pPage, + const Color& rBackgroundColor ) +{ + OUStringBuffer aStr; + + SdrTextObj* pTO = static_cast(pPage->GetPresObj(PresObjKind::Notes)); + + if (pTO && !pTO->IsEmptyPresObj()) + { + OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject(); + if (pOPO) + { + pOutliner->Clear(); + pOutliner->SetText( *pOPO ); + + sal_Int32 nCount = pOutliner->GetParagraphCount(); + for (sal_Int32 nPara = 0; nPara < nCount; nPara++) + { + lclAppendStyle(aStr, u"p", getParagraphStyle(pOutliner, nPara)); + aStr.append(ParagraphToHTMLString(pOutliner, nPara, rBackgroundColor)); + aStr.append("

\r\n"); + } + } + } + + return aStr.makeStringAndClear(); +} + +// converts a paragraph of the outliner to html +OUString HtmlExport::ParagraphToHTMLString( SdrOutliner const * pOutliner, sal_Int32 nPara, const Color& rBackgroundColor ) +{ + OUStringBuffer aStr; + + if(nullptr == pOutliner) + return OUString(); + + // TODO: MALTE!!! + EditEngine& rEditEngine = *const_cast(&pOutliner->GetEditEngine()); + bool bOldUpdateMode = rEditEngine.SetUpdateLayout(true); + + Paragraph* pPara = pOutliner->GetParagraph(nPara); + if(nullptr == pPara) + return OUString(); + + HtmlState aState( (mbUserAttr || mbDocColors) ? maTextColor : COL_BLACK ); + std::vector aPortionList; + rEditEngine.GetPortions( nPara, aPortionList ); + + sal_Int32 nPos1 = 0; + for( sal_Int32 nPos2 : aPortionList ) + { + ESelection aSelection( nPara, nPos1, nPara, nPos2); + + SfxItemSet aSet( rEditEngine.GetAttribs( aSelection ) ); + + OUString aPortion(StringToHTMLString(rEditEngine.GetText( aSelection ))); + + aStr.append(TextAttribToHTMLString( &aSet, &aState, rBackgroundColor )); + aStr.append(aPortion); + + nPos1 = nPos2; + } + aStr.append(aState.Flush()); + rEditEngine.SetUpdateLayout(bOldUpdateMode); + + return aStr.makeStringAndClear(); +} + +// Depending on the attributes of the specified set and the specified +// HtmlState, it creates the needed html tags in order to get the +// attributes +OUString HtmlExport::TextAttribToHTMLString( SfxItemSet const * pSet, HtmlState* pState, const Color& rBackgroundColor ) +{ + OUStringBuffer aStr; + + if(nullptr == pSet) + return OUString(); + + OUString aLink, aTarget; + if ( pSet->GetItemState( EE_FEATURE_FIELD ) == SfxItemState::SET ) + { + const SvxFieldItem* pItem = pSet->GetItem( EE_FEATURE_FIELD ); + if(pItem) + { + const SvxURLField* pURL = dynamic_cast( pItem->GetField() ); + if(pURL) + { + aLink = pURL->GetURL(); + aTarget = pURL->GetTargetFrame(); + } + } + } + + bool bTemp; + OUString aTemp; + + if ( pSet->GetItemState( EE_CHAR_WEIGHT ) == SfxItemState::SET ) + { + bTemp = pSet->Get( EE_CHAR_WEIGHT ).GetWeight() == WEIGHT_BOLD; + aTemp = pState->SetWeight( bTemp ); + if( bTemp ) + aStr.insert(0, aTemp); + else + aStr.append(aTemp); + } + + if ( pSet->GetItemState( EE_CHAR_UNDERLINE ) == SfxItemState::SET ) + { + bTemp = pSet->Get( EE_CHAR_UNDERLINE ).GetLineStyle() != LINESTYLE_NONE; + aTemp = pState->SetUnderline( bTemp ); + if( bTemp ) + aStr.insert(0, aTemp); + else + aStr.append(aTemp); + } + + if ( pSet->GetItemState( EE_CHAR_STRIKEOUT ) == SfxItemState::SET ) + { + bTemp = pSet->Get( EE_CHAR_STRIKEOUT ).GetStrikeout() != STRIKEOUT_NONE; + aTemp = pState->SetStrikeout( bTemp ); + if( bTemp ) + aStr.insert(0, aTemp); + else + aStr.append(aTemp); + } + + if ( pSet->GetItemState( EE_CHAR_ITALIC ) == SfxItemState::SET ) + { + bTemp = pSet->Get( EE_CHAR_ITALIC ).GetPosture() != ITALIC_NONE; + aTemp = pState->SetItalic( bTemp ); + if( bTemp ) + aStr.insert(0, aTemp); + else + aStr.append(aTemp); + } + + if(mbDocColors) + { + if ( pSet->GetItemState( EE_CHAR_COLOR ) == SfxItemState::SET ) + { + Color aTextColor = pSet->Get( EE_CHAR_COLOR ).GetValue(); + if( aTextColor == COL_AUTO ) + { + if( !rBackgroundColor.IsDark() ) + aTextColor = COL_BLACK; + } + aStr.append(pState->SetColor( aTextColor )); + } + } + + if (!aLink.isEmpty()) + aStr.insert(0, pState->SetLink(aLink, aTarget)); + else + aStr.append(pState->SetLink(aLink, aTarget)); + + return aStr.makeStringAndClear(); +} + +// create HTML wrapper for picture files +bool HtmlExport::CreateHtmlForPresPages() +{ + bool bOk = true; + + std::vector aClickableObjects; + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount && bOk; nSdPage++) + { + // find clickable objects (also on the master page) and put it in the + // list. This in reverse order character order since in html the first + // area is taken in the case they overlap. + SdPage* pPage = maPages[ nSdPage ]; + + if( mbDocColors ) + { + SetDocColors( pPage ); + } + + bool bMasterDone = false; + + while (!bMasterDone) + { + // sal_True = backwards + SdrObjListIter aIter(pPage, SdrIterMode::DeepWithGroups, true); + + SdrObject* pObject = aIter.Next(); + while (pObject) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetAnimationInfo(pObject); + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObject); + + if ((pInfo && + (pInfo->meClickAction == presentation::ClickAction_BOOKMARK || + pInfo->meClickAction == presentation::ClickAction_DOCUMENT || + pInfo->meClickAction == presentation::ClickAction_PREVPAGE || + pInfo->meClickAction == presentation::ClickAction_NEXTPAGE || + pInfo->meClickAction == presentation::ClickAction_FIRSTPAGE || + pInfo->meClickAction == presentation::ClickAction_LASTPAGE)) || + pIMapInfo) + { + aClickableObjects.push_back(pObject); + } + + pObject = aIter.Next(); + } + // now to the master page or finishing + if (!pPage->IsMasterPage()) + pPage = static_cast(&(pPage->TRG_GetMasterPage())); + else + bMasterDone = true; + } + + // HTML Head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" " + StringToHTMLString(maPageNames[nSdPage]) + "\r\n"); + + // insert timing information + pPage = maPages[ nSdPage ]; + if( meMode == PUBLISH_KIOSK ) + { + double fSecs = 0; + bool bEndless = false; + if( !mbAutoSlide ) + { + if( pPage->GetPresChange() != PresChange::Manual ) + { + fSecs = pPage->GetTime(); + bEndless = mpDoc->getPresentationSettings().mbEndless; + } + } + else + { + fSecs = mfSlideDuration; + bEndless = mbEndless; + } + + if( fSecs != 0 ) + { + if( nSdPage < (mnSdPageCount-1) || bEndless ) + { + aStr.append("\r\n"); + } + } + } + + aStr.append("\r\n"); + + // HTML Body + aStr.append(CreateBodyTag()); + + if( mbSlideSound && pPage->IsSoundOn() ) + aStr.append(InsertSound(pPage->GetSoundFile())); + + // navigation bar + if(!mbFrames ) + aStr.append(CreateNavBar(nSdPage, false)); + // Image + aStr.append("
"); + aStr.append("\"\"");
\r\n"); + + // notes + if(mbNotes && !mbFrames) + { + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + SdPage* pNotesPage = maNotesPages[ nSdPage ]; + OUString aNotesStr( CreateTextForNotesPage( pOutliner, pNotesPage, maBackColor) ); + pOutliner->Clear(); + + if (!aNotesStr.isEmpty()) + { + aStr.append("

"); + aStr.append(RESTOHTML(STR_HTMLEXP_NOTES)); + aStr.append(":


\r\n\r\n

"); + + aStr.append(aNotesStr); + aStr.append("\r\n

\r\n"); + } + } + + // create Imagemap if necessary + if (!aClickableObjects.empty()) + { + aStr.append("\r\n"); + + for (SdrObject* pObject : aClickableObjects) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetAnimationInfo(pObject); + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObject); + + ::tools::Rectangle aRect(pObject->GetCurrentBoundRect()); + Point aLogPos(aRect.TopLeft()); + bool bIsSquare = aRect.GetWidth() == aRect.GetHeight(); + + sal_uLong nPageWidth = pPage->GetSize().Width() - pPage->GetLeftBorder() - + pPage->GetRightBorder(); + + // BoundRect is relative to the physical page origin, not the + // origin of ordinates + aRect.Move(-pPage->GetLeftBorder(), -pPage->GetUpperBorder()); + + double fLogicToPixel = static_cast(mnWidthPixel) / nPageWidth; + aRect.SetLeft( static_cast(aRect.Left() * fLogicToPixel) ); + aRect.SetTop( static_cast(aRect.Top() * fLogicToPixel) ); + aRect.SetRight( static_cast(aRect.Right() * fLogicToPixel) ); + aRect.SetBottom( static_cast(aRect.Bottom() * fLogicToPixel) ); + tools::Long nRadius = aRect.GetWidth() / 2; + + /** + insert areas into Imagemap of the object, if the object has + such an Imagemap + */ + if (pIMapInfo) + { + const ImageMap& rIMap = pIMapInfo->GetImageMap(); + sal_uInt16 nAreaCount = rIMap.GetIMapObjectCount(); + for (sal_uInt16 nArea = 0; nArea < nAreaCount; nArea++) + { + IMapObject* pArea = rIMap.GetIMapObject(nArea); + IMapObjectType nType = pArea->GetType(); + OUString aURL( pArea->GetURL() ); + + // if necessary, convert page and object names into the + // corresponding names of the html file + bool bIsMasterPage; + sal_uInt16 nPgNum = mpDoc->GetPageByName( aURL, bIsMasterPage ); + + if (nPgNum == SDRPAGE_NOTFOUND) + { + // is the bookmark an object? + SdrObject* pObj = mpDoc->GetObj( aURL ); + if (pObj) + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + } + if (nPgNum != SDRPAGE_NOTFOUND) + { + nPgNum = (nPgNum - 1) / 2; // SdrPageNum --> SdPageNum + aURL = CreatePageURL(nPgNum); + } + + switch(nType) + { + case IMapObjectType::Rectangle: + { + ::tools::Rectangle aArea(static_cast(pArea)-> + GetRectangle(false)); + + // conversion into pixel coordinates + aArea.Move(aLogPos.X() - pPage->GetLeftBorder(), + aLogPos.Y() - pPage->GetUpperBorder()); + aArea.SetLeft( static_cast(aArea.Left() * fLogicToPixel) ); + aArea.SetTop( static_cast(aArea.Top() * fLogicToPixel) ); + aArea.SetRight( static_cast(aArea.Right() * fLogicToPixel) ); + aArea.SetBottom( static_cast(aArea.Bottom() * fLogicToPixel) ); + + aStr.append(CreateHTMLRectArea(aArea, aURL)); + } + break; + + case IMapObjectType::Circle: + { + Point aCenter(static_cast(pArea)-> + GetCenter(false)); + aCenter += Point(aLogPos.X() - pPage->GetLeftBorder(), + aLogPos.Y() - pPage->GetUpperBorder()); + aCenter.setX( static_cast(aCenter.X() * fLogicToPixel) ); + aCenter.setY( static_cast(aCenter.Y() * fLogicToPixel) ); + + sal_uLong nCircleRadius = static_cast(pArea)-> + GetRadius(false); + nCircleRadius = static_cast(nCircleRadius * fLogicToPixel); + aStr.append(CreateHTMLCircleArea(nCircleRadius, + aCenter.X(), aCenter.Y(), + aURL)); + } + break; + + case IMapObjectType::Polygon: + { + tools::Polygon aArea(static_cast(pArea)->GetPolygon(false)); + aStr.append(CreateHTMLPolygonArea(::basegfx::B2DPolyPolygon(aArea.getB2DPolygon()), + Size(aLogPos.X() - pPage->GetLeftBorder(), + aLogPos.Y() - pPage->GetUpperBorder()), + fLogicToPixel, aURL)); + } + break; + + default: + { + SAL_INFO("sd", "unknown IMapObjectType"); + } + break; + } + } + } + + /** + if there is a presentation::ClickAction, determine bookmark + and create area for the whole object + */ + if( pInfo ) + { + OUString aHRef; + presentation::ClickAction eClickAction = pInfo->meClickAction; + + switch( eClickAction ) + { + case presentation::ClickAction_BOOKMARK: + { + bool bIsMasterPage; + sal_uInt16 nPgNum = mpDoc->GetPageByName( pInfo->GetBookmark(), bIsMasterPage ); + + if( nPgNum == SDRPAGE_NOTFOUND ) + { + // is the bookmark an object? + SdrObject* pObj = mpDoc->GetObj(pInfo->GetBookmark()); + if (pObj) + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + } + + if( SDRPAGE_NOTFOUND != nPgNum ) + aHRef = CreatePageURL(( nPgNum - 1 ) / 2 ); + } + break; + + case presentation::ClickAction_DOCUMENT: + aHRef = pInfo->GetBookmark(); + break; + + case presentation::ClickAction_PREVPAGE: + { + sal_uLong nPage; + + if (nSdPage == 0) + nPage = 0; + else + nPage = nSdPage - 1; + + aHRef = CreatePageURL( static_cast(nPage)); + } + break; + + case presentation::ClickAction_NEXTPAGE: + { + sal_uLong nPage; + if (nSdPage == mnSdPageCount - 1) + nPage = mnSdPageCount - 1; + else + nPage = nSdPage + 1; + + aHRef = CreatePageURL( static_cast(nPage)); + } + break; + + case presentation::ClickAction_FIRSTPAGE: + aHRef = CreatePageURL(0); + break; + + case presentation::ClickAction_LASTPAGE: + aHRef = CreatePageURL(mnSdPageCount - 1); + break; + + default: + break; + } + + // and now the areas + if (!aHRef.isEmpty()) + { + // a circle? + if (pObject->GetObjInventor() == SdrInventor::Default && + pObject->GetObjIdentifier() == SdrObjKind::CircleOrEllipse && + bIsSquare ) + { + aStr.append(CreateHTMLCircleArea(aRect.GetWidth() / 2, + aRect.Left() + nRadius, + aRect.Top() + nRadius, + aHRef)); + } + // a polygon? + else if (pObject->GetObjInventor() == SdrInventor::Default && + (pObject->GetObjIdentifier() == SdrObjKind::PathLine || + pObject->GetObjIdentifier() == SdrObjKind::PolyLine || + pObject->GetObjIdentifier() == SdrObjKind::Polygon)) + { + aStr.append(CreateHTMLPolygonArea(static_cast(pObject)->GetPathPoly(), Size(-pPage->GetLeftBorder(), -pPage->GetUpperBorder()), fLogicToPixel, aHRef)); + } + // something completely different: use the BoundRect + else + { + aStr.append(CreateHTMLRectArea(aRect, aHRef)); + } + + } + } + } + + aStr.append("\r\n"); + } + aClickableObjects.clear(); + + aStr.append("\r\n"); + + bOk = WriteHtml(maHTMLFiles[nSdPage], false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + return bOk; +} + +// create overview pages +bool HtmlExport::CreateContentPage() +{ + if( mbDocColors ) + SetDocColors(); + + // html head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" "); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("\r\n\r\n"); + aStr.append(CreateBodyTag()); + + // page head + aStr.append("
\r\n"); + + if(mbHeader) + { + aStr.append("

"); + aStr.append(getDocumentTitle()); + aStr.append("


\r\n"); + } + + aStr.append("

"); + + // Solaris compiler bug workaround + if( mbFrames ) + aStr.append(CreateLink(maFramePage, + RESTOHTML(STR_HTMLEXP_CLICKSTART))); + else + aStr.append(CreateLink(StringToHTMLString(maHTMLFiles[0]), + RESTOHTML(STR_HTMLEXP_CLICKSTART))); + + aStr.append("

\r\n
\r\n"); + + aStr.append("
\r\n"); + + // table of content + aStr.append("\r\n"); + + // document information + aStr.append("
\r\n"); + aStr.append("

"); + aStr.append(RESTOHTML(STR_HTMLEXP_CONTENTS)); + aStr.append("

"); + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + OUString aPageName = maPageNames[nSdPage]; + aStr.append("
"); + if(mbFrames) + aStr.append(StringToHTMLString(aPageName)); + else + aStr.append(CreateLink(maHTMLFiles[nSdPage], aPageName)); + aStr.append("
\r\n"); + } + aStr.append("
\r\n"); + + if (!maAuthor.isEmpty()) + { + aStr.append("

"); + aStr.append(RESTOHTML(STR_HTMLEXP_AUTHOR)); + aStr.append(": "); + aStr.append(StringToHTMLString(maAuthor)); + aStr.append("

\r\n"); + } + + if (!maEMail.isEmpty()) + { + aStr.append("

"); + aStr.append(RESTOHTML(STR_HTMLEXP_EMAIL)); + aStr.append(": "); + aStr.append(StringToHTMLString(maEMail)); + aStr.append("

\r\n"); + } + + if (!maHomePage.isEmpty()) + { + aStr.append("

"); + aStr.append(RESTOHTML(STR_HTMLEXP_HOMEPAGE)); + aStr.append(": "); + aStr.append(StringToHTMLString(maHomePage)); + aStr.append("

\r\n"); + } + + if (!maInfo.isEmpty()) + { + aStr.append("

"); + aStr.append(RESTOHTML(STR_HTMLEXP_INFO)); + aStr.append(":
\r\n"); + aStr.append(StringToHTMLString(maInfo)); + aStr.append("

\r\n"); + } + + if(mbDownload) + { + aStr.append("

"); + aStr.append(RESTOHTML(STR_HTMLEXP_DOWNLOAD)); + aStr.append("

\r\n"); + } + + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + OUString aText( + "\"""); + + aStr.append(CreateLink(maHTMLFiles[nSdPage], aText)); + aStr.append("\r\n"); + } + + aStr.append("
\r\n"); + + aStr.append("\r\n"); + + bool bOk = WriteHtml(maIndex, false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + return bOk; +} + +// create note pages (for frames) + +bool HtmlExport::CreateNotesPages() +{ + bool bOk = true; + + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + for( sal_uInt16 nSdPage = 0; bOk && nSdPage < mnSdPageCount; nSdPage++ ) + { + SdPage* pPage = maNotesPages[nSdPage]; + if( mbDocColors ) + SetDocColors( pPage ); + + // Html head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" "); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("\r\n\r\n"); + aStr.append(CreateBodyTag()); + + if(pPage) + aStr.append(CreateTextForNotesPage( pOutliner, pPage, maBackColor )); + + aStr.append("\r\n"); + + OUString aFileName("note" + OUString::number(nSdPage)); + bOk = WriteHtml(aFileName, true, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + pOutliner->Clear(); + + return bOk; +} + +// create outline pages (for frames) + +bool HtmlExport::CreateOutlinePages() +{ + bool bOk = true; + + if( mbDocColors ) + { + SetDocColors(); + } + + // page 0 will be the closed outline, page 1 the opened + for (sal_Int32 nPage = 0; nPage < (mbImpress?2:1) && bOk; ++nPage) + { + // Html head + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" "); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("\r\n\r\n"); + aStr.append(CreateBodyTag()); + + SdrOutliner* pOutliner = mpDoc->GetInternalOutliner(); + for(sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + SdPage* pPage = maPages[ nSdPage ]; + + aStr.append("
"); + OUString aLink("JavaScript:parent.NavigateAbs(" + + OUString::number(nSdPage) + ")"); + + OUString aTitle = CreateTextForTitle(pOutliner, pPage, maBackColor); + if (aTitle.isEmpty()) + aTitle = maPageNames[nSdPage]; + + lclAppendStyle(aStr, u"p", getParagraphStyle(pOutliner, 0)); + aStr.append(CreateLink(aLink, aTitle)); + aStr.append("

"); + + if(nPage==1) + { + aStr.append(CreateTextForPage( pOutliner, pPage, false, maBackColor )); + } + aStr.append("
\r\n"); + } + pOutliner->Clear(); + + aStr.append("\r\n"); + + OUString aFileName("outline" + OUString::number(nPage)); + bOk = WriteHtml(aFileName, true, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + return bOk; +} + +// set file name +void HtmlExport::CreateFileNames() +{ + // create lists with new file names + maHTMLFiles.resize(mnSdPageCount); + maImageFiles.resize(mnSdPageCount); + maThumbnailFiles.resize(mnSdPageCount); + maPageNames.resize(mnSdPageCount); + maTextFiles.resize(mnSdPageCount); + + mbHeader = false; // headline on overview page? + + for (sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + OUString aHTMLFileName; + if(nSdPage == 0 && !mbContentsPage && !mbFrames ) + aHTMLFileName = maIndex; + else + { + aHTMLFileName = "img" + OUString::number(nSdPage) + gaHTMLExtension; + } + + maHTMLFiles[nSdPage] = aHTMLFileName; + + OUString aImageFileName = "img" + OUString::number(nSdPage); + if( meFormat==FORMAT_GIF ) + aImageFileName += ".gif"; + else if( meFormat==FORMAT_JPG ) + aImageFileName += ".jpg"; + else + aImageFileName += ".png"; + + maImageFiles[nSdPage] = aImageFileName; + + OUString aThumbnailFileName = "thumb" + OUString::number(nSdPage); + if( meFormat!=FORMAT_JPG ) + aThumbnailFileName += ".png"; + else + aThumbnailFileName += ".jpg"; + + maThumbnailFiles[nSdPage] = aThumbnailFileName; + + maTextFiles[nSdPage] = "text" + OUString::number(nSdPage) + gaHTMLExtension; + + SdPage* pSdPage = maPages[ nSdPage ]; + + // get slide title from page name + maPageNames[nSdPage] = pSdPage->GetName(); + } + + if(!mbContentsPage && mbFrames) + maFramePage = maIndex; + else + { + maFramePage = "siframes" + gaHTMLExtension; + } +} + +OUString const & HtmlExport::getDocumentTitle() +{ + // check for a title object in this page, if it's the first + // title it becomes this documents title for the content + // page + if( !mbHeader ) + { + if(mbImpress) + { + // if there is a non-empty title object, use their first passage + // as page title + SdPage* pSdPage = mpDoc->GetSdPage(0, PageKind::Standard); + SdrObject* pTitleObj = pSdPage->GetPresObj(PresObjKind::Title); + if (pTitleObj && !pTitleObj->IsEmptyPresObj()) + { + OutlinerParaObject* pParaObject = pTitleObj->GetOutlinerParaObject(); + if (pParaObject) + { + const EditTextObject& rEditTextObject = + pParaObject->GetTextObject(); + OUString aTest(rEditTextObject.GetText(0)); + if (!aTest.isEmpty()) + mDocTitle = aTest; + } + } + + mDocTitle = mDocTitle.replace(0xff, ' '); + } + + if (mDocTitle.isEmpty()) + { + mDocTitle = maDocFileName; + sal_Int32 nDot = mDocTitle.indexOf('.'); + if (nDot > 0) + mDocTitle = mDocTitle.copy(0, nDot); + } + mbHeader = true; + } + + return mDocTitle; +} + +constexpr OUStringLiteral JS_NavigateAbs = + u"function NavigateAbs( nPage )\r\n" + "{\r\n" + " frames[\"show\"].location.href = \"img\" + nPage + \".$EXT\";\r\n" + " //frames[\"notes\"].location.href = \"note\" + nPage + \".$EXT\";\r\n" + " nCurrentPage = nPage;\r\n" + " if(nCurrentPage==0)\r\n" + " {\r\n" + " frames[\"navbar1\"].location.href = \"navbar0.$EXT\";\r\n" + " }\r\n" + " else if(nCurrentPage==nPageCount-1)\r\n" + " {\r\n" + " frames[\"navbar1\"].location.href = \"navbar2.$EXT\";\r\n" + " }\r\n" + " else\r\n" + " {\r\n" + " frames[\"navbar1\"].location.href = \"navbar1.$EXT\";\r\n" + " }\r\n" + "}\r\n\r\n"; + +constexpr OUStringLiteral JS_NavigateRel = + u"function NavigateRel( nDelta )\r\n" + "{\r\n" + " var nPage = parseInt(nCurrentPage) + parseInt(nDelta);\r\n" + " if( (nPage >= 0) && (nPage < nPageCount) )\r\n" + " {\r\n" + " NavigateAbs( nPage );\r\n" + " }\r\n" + "}\r\n\r\n"; + +constexpr OUStringLiteral JS_ExpandOutline = + u"function ExpandOutline()\r\n" + "{\r\n" + " frames[\"navbar2\"].location.href = \"navbar4.$EXT\";\r\n" + " frames[\"outline\"].location.href = \"outline1.$EXT\";\r\n" + "}\r\n\r\n"; + +constexpr OUStringLiteral JS_CollapseOutline = + u"function CollapseOutline()\r\n" + "{\r\n" + " frames[\"navbar2\"].location.href = \"navbar3.$EXT\";\r\n" + " frames[\"outline\"].location.href = \"outline0.$EXT\";\r\n" + "}\r\n\r\n"; + +// create page with the frames + +bool HtmlExport::CreateFrames() +{ + OUString aTmp; + OUStringBuffer aStr( + "\r\n" + "\r\n\r\n"); + + aStr.append(CreateMetaCharset()); + aStr.append(" "); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("\r\n"); + + aStr.append("\r\n"); + + aStr.append("\r\n"); + + aStr.append("(mnWidthPixel + 16)); + aStr.append("\">\r\n"); + if(mbImpress) + { + aStr.append(" \r\n"); + aStr.append(" \r\n"); + } + aStr.append(" \r\n"); + if(mbImpress) + aStr.append(" \r\n"); + + if(mbNotes) + { + aStr.append(" (static_cast(mnWidthPixel) * 0.75) + 16); + aStr.append(",*\">\r\n"); + } + else + aStr.append(" \r\n"); + + aStr.append(" \r\n"); + + aStr.append(" \r\n"); + + if(mbNotes) + { + aStr.append(" \r\n"); + } + aStr.append(" \r\n"); + + aStr.append("\r\n"); + aStr.append(CreateBodyTag()); + aStr.append(RESTOHTML(STR_HTMLEXP_NOFRAMES)); + aStr.append("\r\n\r\n\r\n"); + + bool bOk = WriteHtml(maFramePage, false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + return bOk; +} + +// create button bar for standard +// we create the following html files +// navbar0.htm navigation bar graphic for the first page +// navbar1.htm navigation bar graphic for the second until second last page +// navbar2.htm navigation bar graphic for the last page +// navbar3.htm navigation outline closed +// navbar4.htm navigation outline open +bool HtmlExport::CreateNavBarFrames() +{ + bool bOk = true; + OUString aButton; + + if( mbDocColors ) + { + SetDocColors(); + maBackColor = maFirstPageColor; + } + + for( int nFile = 0; nFile < 3 && bOk; nFile++ ) + { + OUStringBuffer aStr(gaHTMLHeader); + aStr.append(CreateMetaCharset()); + aStr.append(" "); + aStr.append(StringToHTMLString(maPageNames[0])); + aStr.append("\r\n\r\n"); + aStr.append(CreateBodyTag()); + aStr.append("
\r\n"); + + // first page + aButton = SdResId(STR_HTMLEXP_FIRSTPAGE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(nFile == 0 || mnSdPageCount == 1 ? BTN_FIRST_0 : BTN_FIRST_1), + aButton); + + if(nFile != 0 && mnSdPageCount > 1) + aButton = CreateLink(u"JavaScript:parent.NavigateAbs(0)", aButton); + + aStr.append(aButton); + aStr.append("\r\n"); + + // to the previous page + aButton = SdResId(STR_PUBLISH_BACK); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(nFile == 0 || mnSdPageCount == 1? + BTN_PREV_0:BTN_PREV_1), + aButton); + + if(nFile != 0 && mnSdPageCount > 1) + aButton = CreateLink(u"JavaScript:parent.NavigateRel(-1)", aButton); + + aStr.append(aButton); + aStr.append("\r\n"); + + // to the next page + aButton = SdResId(STR_PUBLISH_NEXT); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(nFile ==2 || mnSdPageCount == 1? + BTN_NEXT_0:BTN_NEXT_1), + aButton); + + if(nFile != 2 && mnSdPageCount > 1) + aButton = CreateLink(u"JavaScript:parent.NavigateRel(1)", aButton); + + aStr.append(aButton); + aStr.append("\r\n"); + + // to the last page + aButton = SdResId(STR_HTMLEXP_LASTPAGE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(nFile ==2 || mnSdPageCount == 1? + BTN_LAST_0:BTN_LAST_1), + aButton); + + if(nFile != 2 && mnSdPageCount > 1) + { + OUString aLink("JavaScript:parent.NavigateAbs(" + + OUString::number(mnSdPageCount-1) + ")"); + + aButton = CreateLink(aLink, aButton); + } + + aStr.append(aButton); + aStr.append("\r\n"); + + // content + if (mbContentsPage) + { + aButton = SdResId(STR_PUBLISH_OUTLINE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(BTN_INDEX), aButton); + + // to the overview + aStr.append(CreateLink(maIndex, aButton, u"_top")); + aStr.append("\r\n"); + } + + // text mode + if(mbImpress) + { + aButton = SdResId(STR_HTMLEXP_SETTEXT); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(BTN_TEXT), aButton); + + OUString aText0("text0" + gaHTMLExtension); + aStr.append(CreateLink(aText0, aButton, u"_top")); + aStr.append("\r\n"); + } + + // and finished... + aStr.append("
\r\n"); + aStr.append("\r\n"); + + OUString aFileName("navbar" + OUString::number(nFile)); + + bOk = WriteHtml(aFileName, true, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + // the navigation bar outliner closed... + if(bOk) + { + aButton = SdResId(STR_HTMLEXP_OUTLINE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(BTN_MORE), aButton); + + bOk = WriteHtml( + "navbar3", true, + OUStringConcatenation( + gaHTMLHeader + CreateMetaCharset() + " " + + StringToHTMLString(maPageNames[0]) + "\r\n\r\n" + CreateBodyTag() + + CreateLink(u"JavaScript:parent.ExpandOutline()", aButton) + + "\r\n")); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + } + + // ... and the outliner open + if( bOk ) + { + aButton = SdResId(STR_HTMLEXP_NOOUTLINE); + if(mnButtonThema != -1) + aButton = CreateImage(GetButtonName(BTN_LESS), aButton); + + bOk = WriteHtml( + "navbar4", true, + OUStringConcatenation( + gaHTMLHeader + CreateMetaCharset() + " " + + StringToHTMLString(maPageNames[0]) + "\r\n\r\n" + CreateBodyTag() + + CreateLink(u"JavaScript:parent.CollapseOutline()", aButton) + + "\r\n")); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + } + + return bOk; +} + +// create button bar for standard +OUString HtmlExport::CreateNavBar( sal_uInt16 nSdPage, bool bIsText ) const +{ + // prepare button bar + OUString aStrNavFirst(SdResId(STR_HTMLEXP_FIRSTPAGE)); + OUString aStrNavPrev(SdResId(STR_PUBLISH_BACK)); + OUString aStrNavNext(SdResId(STR_PUBLISH_NEXT)); + OUString aStrNavLast(SdResId(STR_HTMLEXP_LASTPAGE)); + OUString aStrNavContent(SdResId(STR_PUBLISH_OUTLINE)); + OUString aStrNavText; + if( bIsText ) + { + aStrNavText = SdResId(STR_HTMLEXP_SETGRAPHIC); + } + else + { + aStrNavText = SdResId(STR_HTMLEXP_SETTEXT); + } + + if(!bIsText && mnButtonThema != -1) + { + if(nSdPage<1 || mnSdPageCount == 1) + { + aStrNavFirst = CreateImage(GetButtonName(BTN_FIRST_0), aStrNavFirst); + aStrNavPrev = CreateImage(GetButtonName(BTN_PREV_0), aStrNavPrev); + } + else + { + aStrNavFirst = CreateImage(GetButtonName(BTN_FIRST_1), aStrNavFirst); + aStrNavPrev = CreateImage(GetButtonName(BTN_PREV_1), aStrNavPrev); + } + + if(nSdPage == mnSdPageCount-1 || mnSdPageCount == 1) + { + aStrNavNext = CreateImage(GetButtonName(BTN_NEXT_0), aStrNavNext); + aStrNavLast = CreateImage(GetButtonName(BTN_LAST_0), aStrNavLast); + } + else + { + aStrNavNext = CreateImage(GetButtonName(BTN_NEXT_1), aStrNavNext); + aStrNavLast = CreateImage(GetButtonName(BTN_LAST_1), aStrNavLast); + } + + aStrNavContent = CreateImage(GetButtonName(BTN_INDEX), aStrNavContent); + aStrNavText = CreateImage(GetButtonName(BTN_TEXT), aStrNavText); + } + + OUStringBuffer aStr("
\r\n"); //\r\n"); + + // first page + if(nSdPage > 0) + aStr.append(CreateLink( bIsText ? maTextFiles[0] : maHTMLFiles[0],aStrNavFirst)); + else + aStr.append(aStrNavFirst); + aStr.append(' '); + + // to Previous page + if(nSdPage > 0) + aStr.append(CreateLink( bIsText ? maTextFiles[nSdPage-1] + : maHTMLFiles[nSdPage-1], aStrNavPrev)); + else + aStr.append(aStrNavPrev); + aStr.append(' '); + + // to Next page + if(nSdPage < mnSdPageCount-1) + aStr.append(CreateLink( bIsText ? maTextFiles[nSdPage+1] + : maHTMLFiles[nSdPage+1], aStrNavNext)); + else + aStr.append(aStrNavNext); + aStr.append(' '); + + // to Last page + if(nSdPage < mnSdPageCount-1) + aStr.append(CreateLink( bIsText ? maTextFiles[mnSdPageCount-1] + : maHTMLFiles[mnSdPageCount-1], + aStrNavLast)); + else + aStr.append(aStrNavLast); + aStr.append(' '); + + // to Index page + if (mbContentsPage) + { + aStr.append(CreateLink(maIndex, aStrNavContent)); + aStr.append(' '); + } + + // Text/Graphics + if(mbImpress) + { + aStr.append(CreateLink( bIsText ? (mbFrames ? maFramePage : maHTMLFiles[nSdPage]) + : maTextFiles[nSdPage], aStrNavText)); + + } + + aStr.append("
\r\n"); + + return aStr.makeStringAndClear(); +} + +// export navigation graphics from button set +void HtmlExport::CreateBitmaps() +{ + if(mnButtonThema == -1 || !mpButtonSet) + return; + + for( int nButton = 0; nButton != SAL_N_ELEMENTS(pButtonNames); nButton++ ) + { + if(!mbFrames && (nButton == BTN_MORE || nButton == BTN_LESS)) + continue; + + if(!mbImpress && (nButton == BTN_TEXT || nButton == BTN_MORE || nButton == BTN_LESS )) + continue; + + OUString aFull = maExportPath + GetButtonName(nButton); + mpButtonSet->exportButton( mnButtonThema, aFull, GetButtonName(nButton) ); + } +} + +// creates the tag, including the specified color attributes +OUString HtmlExport::CreateBodyTag() const +{ + OUStringBuffer aStr( "\r\n"); + + return aStr.makeStringAndClear(); +} + +// creates a hyperlink +OUString HtmlExport::CreateLink( std::u16string_view aLink, + std::u16string_view aText, + std::u16string_view aTarget ) +{ + OUStringBuffer aStr( ""); + aStr.append(aText); + aStr.append(""); + + return aStr.makeStringAndClear(); +} + +// creates an image tag +OUString HtmlExport::CreateImage( std::u16string_view aImage, std::u16string_view aAltText ) +{ + OUStringBuffer aStr( "\"");'); + + return aStr.makeStringAndClear(); +} + +// create area for a circle; we expect pixel coordinates +OUString HtmlExport::ColorToHTMLString( Color aColor ) +{ + static const char hex[] = "0123456789ABCDEF"; + OUStringBuffer aStr( "#xxxxxx" ); + aStr[1] = hex[(aColor.GetRed() >> 4) & 0xf]; + aStr[2] = hex[aColor.GetRed() & 0xf]; + aStr[3] = hex[(aColor.GetGreen() >> 4) & 0xf]; + aStr[4] = hex[aColor.GetGreen() & 0xf]; + aStr[5] = hex[(aColor.GetBlue() >> 4) & 0xf]; + aStr[6] = hex[aColor.GetBlue() & 0xf]; + + return aStr.makeStringAndClear(); +} + +// create area for a circle; we expect pixel coordinates +OUString HtmlExport::CreateHTMLCircleArea( sal_uLong nRadius, + sal_uLong nCenterX, + sal_uLong nCenterY, + std::u16string_view rHRef ) +{ + OUString aStr( + "\"\"\n"); + + return aStr; +} + +// create area for a polygon; we expect pixel coordinates +OUString HtmlExport::CreateHTMLPolygonArea( const ::basegfx::B2DPolyPolygon& rPolyPolygon, + Size aShift, double fFactor, std::u16string_view rHRef ) +{ + OUStringBuffer aStr; + const sal_uInt32 nNoOfPolygons(rPolyPolygon.count()); + + for ( sal_uInt32 nXPoly = 0; nXPoly < nNoOfPolygons; nXPoly++ ) + { + const ::basegfx::B2DPolygon& aPolygon = rPolyPolygon.getB2DPolygon(nXPoly); + const sal_uInt32 nNoOfPoints(aPolygon.count()); + + aStr.append("\"\"(aPnt.X() * fFactor) ); + aPnt.setY( static_cast(aPnt.Y() * fFactor) ); + aStr.append( OUString::number(aPnt.X()) + "," + OUString::number(aPnt.Y()) ); + + if (nPoint < nNoOfPoints - 1) + aStr.append(','); + } + aStr.append(OUString::Concat("\" href=\"") + rHRef + "\">\n"); + } + + return aStr.makeStringAndClear(); +} + +// create area for a rectangle; we expect pixel coordinates +OUString HtmlExport::CreateHTMLRectArea( const ::tools::Rectangle& rRect, + std::u16string_view rHRef ) +{ + OUString aStr( + "\"\"\n"); + + return aStr; +} + +// escapes a string for html +OUString HtmlExport::StringToHTMLString( const OUString& rString ) +{ + SvMemoryStream aMemStm; + HTMLOutFuncs::Out_String( aMemStm, rString ); + aMemStm.WriteChar( char(0) ); + sal_Int32 nLength = strlen(static_cast(aMemStm.GetData())); + return OUString( static_cast(aMemStm.GetData()), nLength, RTL_TEXTENCODING_UTF8 ); +} + +// creates a URL for a specific page +OUString HtmlExport::CreatePageURL( sal_uInt16 nPgNum ) +{ + if(mbFrames) + { + return OUString("JavaScript:parent.NavigateAbs(" + + OUString::number(nPgNum) + ")"); + } + else + return maHTMLFiles[nPgNum]; +} + +bool HtmlExport::CopyScript( std::u16string_view rPath, const OUString& rSource, const OUString& rDest, bool bUnix /* = false */ ) +{ + INetURLObject aURL( SvtPathOptions().GetConfigPath() ); + OUStringBuffer aScriptBuf; + + aURL.Append( u"webcast" ); + aURL.Append( rSource ); + + meEC.SetContext( STR_HTMLEXP_ERROR_OPEN_FILE, rSource ); + + ErrCode nErr = ERRCODE_NONE; + std::unique_ptr pIStm = ::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ); + + if( pIStm ) + { + OStringBuffer aLine; + + while( pIStm->ReadLine( aLine ) ) + { + aScriptBuf.appendAscii( aLine.getStr(), aLine.getLength() ); + if( bUnix ) + { + aScriptBuf.append("\n"); + } + else + { + aScriptBuf.append("\r\n"); + } + } + + nErr = pIStm->GetError(); + pIStm.reset(); + } + + if( nErr != ERRCODE_NONE ) + { + ErrorHandler::HandleError( nErr ); + return static_cast(nErr); + } + + OUString aScript(aScriptBuf.makeStringAndClear()); + aScript = aScript.replaceAll("$$1", getDocumentTitle()); + aScript = aScript.replaceAll("$$2", RESTOHTML(STR_WEBVIEW_SAVE)); + aScript = aScript.replaceAll("$$3", maCGIPath); + aScript = aScript.replaceAll("$$4", OUString::number(mnWidthPixel)); + aScript = aScript.replaceAll("$$5", OUString::number(mnHeightPixel)); + + OUString aDest(rPath + rDest); + + meEC.SetContext( STR_HTMLEXP_ERROR_CREATE_FILE, rDest ); + // write script file + { + EasyFile aFile; + SvStream* pStr; + nErr = aFile.createStream(aDest, pStr); + if(nErr == ERRCODE_NONE) + { + OString aStr(OUStringToOString(aScript, RTL_TEXTENCODING_UTF8)); + pStr->WriteOString( aStr ); + aFile.close(); + } + } + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + if( nErr != ERRCODE_NONE ) + ErrorHandler::HandleError( nErr ); + + return nErr == ERRCODE_NONE; +} + +static const char * ASP_Scripts[] = { "common.inc", "webcast.asp", "show.asp", "savepic.asp", "poll.asp", "editpic.asp" }; + +/** creates and saves the ASP scripts for WebShow */ +bool HtmlExport::CreateASPScripts() +{ + for(const char * p : ASP_Scripts) + { + OUString aScript = OUString::createFromAscii(p); + + if(!CopyScript(maExportPath, aScript, aScript)) + return false; + } + + return CopyScript(maExportPath, "edit.asp", maIndex); +} + +static const char *PERL_Scripts[] = { "webcast.pl", "common.pl", "editpic.pl", "poll.pl", "savepic.pl", "show.pl" }; + +// creates and saves the PERL scripts for WebShow +bool HtmlExport::CreatePERLScripts() +{ + for(const char * p : PERL_Scripts) + { + OUString aScript = OUString::createFromAscii(p); + + if(!CopyScript(maExportPath, aScript, aScript, true)) + return false; + } + + if (!CopyScript(maExportPath, "edit.pl", maIndex, true)) + return false; + + if (!CopyScript(maExportPath, "index.pl", maIndexUrl, true)) + return false; + + return true; +} + +// creates a list with names of the saved images +bool HtmlExport::CreateImageFileList() +{ + OUStringBuffer aStr; + for( sal_uInt16 nSdPage = 0; nSdPage < mnSdPageCount; nSdPage++) + { + aStr.append(static_cast(nSdPage + 1)); + aStr.append(';'); + aStr.append(maURLPath); + aStr.append(maImageFiles[nSdPage]); + aStr.append("\r\n"); + } + + bool bOk = WriteHtml("picture.txt", false, aStr.makeStringAndClear()); + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + return bOk; +} + +// creates a file with the actual page number +bool HtmlExport::CreateImageNumberFile() +{ + OUString aFileName("currpic.txt"); + OUString aFull(maExportPath + aFileName); + + meEC.SetContext( STR_HTMLEXP_ERROR_CREATE_FILE, aFileName ); + EasyFile aFile; + SvStream* pStr; + ErrCode nErr = aFile.createStream(aFull, pStr); + if(nErr == ERRCODE_NONE) + { + pStr->WriteCharPtr( "1" ); + aFile.close(); + } + + if (mpProgress) + mpProgress->SetState(++mnPagesWritten); + + if( nErr != ERRCODE_NONE ) + ErrorHandler::HandleError( nErr ); + + return nErr == ERRCODE_NONE; +} + +OUString HtmlExport::InsertSound( const OUString& rSoundFile ) +{ + if (rSoundFile.isEmpty()) + return rSoundFile; + + INetURLObject aURL( rSoundFile ); + OUString aSoundFileName = aURL.getName(); + + DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" ); + + OUString aStr(""); + + CopyFile(rSoundFile, maExportPath + aSoundFileName); + + return aStr; +} + +bool HtmlExport::CopyFile( const OUString& rSourceFile, const OUString& rDestFile ) +{ + meEC.SetContext( STR_HTMLEXP_ERROR_COPY_FILE, rSourceFile, rDestFile ); + osl::FileBase::RC Error = osl::File::copy( rSourceFile, rDestFile ); + + if( Error != osl::FileBase::E_None ) + { + ErrorHandler::HandleError(ErrCode(Error)); + return false; + } + else + { + return true; + } +} + +bool HtmlExport::checkFileExists( Reference< css::ucb::XSimpleFileAccess3 > const & xFileAccess, std::u16string_view aFileName ) +{ + try + { + OUString url = maExportPath + aFileName; + return xFileAccess->exists( url ); + } + catch( css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::HtmlExport::checkFileExists()" ); + } + + return false; +} + +bool HtmlExport::checkForExistingFiles() +{ + bool bFound = false; + + try + { + Reference< XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + uno::Reference xFA(ucb::SimpleFileAccess::create(xContext)); + + sal_uInt16 nSdPage; + for( nSdPage = 0; !bFound && (nSdPage < mnSdPageCount); nSdPage++) + { + if( checkFileExists( xFA, maImageFiles[nSdPage] ) || + checkFileExists( xFA, maHTMLFiles[nSdPage] ) || + checkFileExists( xFA, maThumbnailFiles[nSdPage] ) || + checkFileExists( xFA, maPageNames[nSdPage] ) || + checkFileExists( xFA, maTextFiles[nSdPage] ) ) + { + bFound = true; + } + } + + if( !bFound && mbDownload ) + bFound = checkFileExists( xFA, maDocFileName ); + + if( !bFound && mbFrames ) + bFound = checkFileExists( xFA, maFramePage ); + + if( bFound ) + { + OUString aSystemPath; + osl::FileBase::getSystemPathFromFileURL( maExportPath, aSystemPath ); + OUString aMsg(SdResId(STR_OVERWRITE_WARNING)); + aMsg = aMsg.replaceFirst( "%FILENAME", aSystemPath ); + + std::unique_ptr xWarn(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::YesNo, + aMsg)); + xWarn->set_default_response(RET_YES); + bFound = (RET_NO == xWarn->run()); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::HtmlExport::checkForExistingFiles()" ); + bFound = false; + } + + return bFound; +} + +OUString HtmlExport::GetButtonName( int nButton ) +{ + return OUString::createFromAscii(pButtonNames[nButton]); +} + +EasyFile::EasyFile() : bOpen(false) +{ +} + +EasyFile::~EasyFile() +{ + if( bOpen ) + close(); +} + +ErrCode EasyFile::createStream( const OUString& rUrl, SvStream* &rpStr ) +{ + if(bOpen) + close(); + + OUString aFileName; + createFileName( rUrl, aFileName ); + + ErrCode nErr = ERRCODE_NONE; + pOStm = ::utl::UcbStreamHelper::CreateStream( aFileName, StreamMode::WRITE | StreamMode::TRUNC ); + if( pOStm ) + { + bOpen = true; + nErr = pOStm->GetError(); + } + else + { + nErr = ERRCODE_SFX_CANTCREATECONTENT; + } + + if( nErr != ERRCODE_NONE ) + { + bOpen = false; + pOStm.reset(); + } + + rpStr = pOStm.get(); + + return nErr; +} + +void EasyFile::createFileName( const OUString& rURL, OUString& rFileName ) +{ + if( bOpen ) + close(); + + INetURLObject aURL( rURL ); + + if( aURL.GetProtocol() == INetProtocol::NotValid ) + { + OUString aURLStr; + osl::FileBase::getFileURLFromSystemPath( rURL, aURLStr ); + aURL = INetURLObject( aURLStr ); + } + DBG_ASSERT( aURL.GetProtocol() != INetProtocol::NotValid, "invalid URL" ); + rFileName = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); +} + +void EasyFile::close() +{ + pOStm.reset(); + bOpen = false; +} + +// This class helps reporting errors during file i/o +HtmlErrorContext::HtmlErrorContext() + : ErrorContext(nullptr) +{ +} + +bool HtmlErrorContext::GetString( ErrCode, OUString& rCtxStr ) +{ + DBG_ASSERT(mpResId, "No error context set"); + if (!mpResId) + return false; + + rCtxStr = SdResId(mpResId); + + rCtxStr = rCtxStr.replaceAll( "$(URL1)", maURL1 ); + rCtxStr = rCtxStr.replaceAll( "$(URL2)", maURL2 ); + + return true; +} + +void HtmlErrorContext::SetContext(TranslateId pResId, const OUString& rURL) +{ + mpResId = pResId; + maURL1 = rURL; + maURL2.clear(); +} + +void HtmlErrorContext::SetContext(TranslateId pResId, const OUString& rURL1, const OUString& rURL2 ) +{ + mpResId = pResId; + maURL1 = rURL1; + maURL2 = rURL2; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlex.hxx b/sd/source/filter/html/htmlex.hxx new file mode 100644 index 000000000..5f6f06e49 --- /dev/null +++ b/sd/source/filter/html/htmlex.hxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "htmlpublishmode.hxx" + +#include +#include +#include + +namespace basegfx { class B2DPolyPolygon; } +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::ucb { class XSimpleFileAccess3; } +namespace sd { class DrawDocShell; } +namespace tools { class Rectangle; } + +#define PUB_LOWRES_WIDTH 640 +#define PUB_MEDRES_WIDTH 800 +#define PUB_HIGHRES_WIDTH 1024 +#define PUB_FHDRES_WIDTH 1920 + +#define PUB_THUMBNAIL_WIDTH 256 +#define PUB_THUMBNAIL_HEIGHT 192 + +class ErrCode; +class OutlinerParaObject; +class SfxItemSet; +class Size; +class SfxProgress; +class SdrOutliner; +class SdPage; +class HtmlState; +class SdrTextObj; +class SdrObjGroup; +namespace sdr::table { class SdrTableObj; } +class SdrPage; +class SdDrawDocument; +class ButtonSet; + +class HtmlErrorContext : public ErrorContext +{ +private: + TranslateId mpResId; + OUString maURL1; + OUString maURL2; + +public: + explicit HtmlErrorContext(); + + virtual bool GetString( ErrCode nErrId, OUString& rCtxStr ) override; + + void SetContext(TranslateId pResId, const OUString& rURL); + void SetContext(TranslateId pResId, const OUString& rURL1, const OUString& rURL2); +}; + +/// this class exports an Impress Document as a HTML Presentation. +class HtmlExport final +{ + std::vector< SdPage* > maPages; + std::vector< SdPage* > maNotesPages; + + OUString maPath; + + SdDrawDocument* mpDoc; + ::sd::DrawDocShell* mpDocSh; + + HtmlErrorContext meEC; + + HtmlPublishMode meMode; + std::unique_ptr mpProgress; + bool mbImpress; + sal_uInt16 mnSdPageCount; + sal_uInt16 mnPagesWritten; + bool mbContentsPage; + sal_Int16 mnButtonThema; + sal_uInt16 mnWidthPixel; + sal_uInt16 mnHeightPixel; + PublishingFormat meFormat; + bool mbHeader; + bool mbNotes; + bool mbFrames; + OUString maIndex; + OUString maEMail; + OUString maAuthor; + OUString maHomePage; + OUString maInfo; + sal_Int16 mnCompression; + OUString maDocFileName; + OUString maFramePage; + OUString mDocTitle; + bool mbDownload; + + bool mbAutoSlide; + double mfSlideDuration; + bool mbSlideSound; + bool mbHiddenSlides; + bool mbEndless; + + bool mbUserAttr; + Color maTextColor; ///< The following colors are used for the tag if mbUserAttr is true. + Color maBackColor; + Color maLinkColor; + Color maVLinkColor; + Color maALinkColor; + Color maFirstPageColor; + bool mbDocColors; + + std::vector maHTMLFiles; + std::vector maImageFiles; + std::vector maThumbnailFiles; + std::vector maPageNames; + std::vector maTextFiles; + + OUString maExportPath; ///< output directory or URL. + OUString maIndexUrl; + OUString maURLPath; + OUString maCGIPath; + PublishingScript meScript; + + std::unique_ptr< ButtonSet > mpButtonSet; + + static SdrTextObj* GetLayoutTextObject(SdrPage const * pPage); + + void SetDocColors( SdPage* pPage = nullptr ); + + bool CreateImagesForPresPages( bool bThumbnails = false ); + bool CreateHtmlTextForPresPages(); + bool CreateHtmlForPresPages(); + bool CreateContentPage(); + void CreateFileNames(); + void CreateBitmaps(); + bool CreateOutlinePages(); + bool CreateFrames(); + bool CreateNotesPages(); + bool CreateNavBarFrames(); + + bool CreateASPScripts(); + bool CreatePERLScripts(); + bool CreateImageFileList(); + bool CreateImageNumberFile(); + + bool checkForExistingFiles(); + bool checkFileExists( css::uno::Reference< css::ucb::XSimpleFileAccess3 > const & xFileAccess, std::u16string_view aFileName ); + + OUString const & getDocumentTitle(); + bool SavePresentation(); + + static OUString CreateLink( std::u16string_view aLink, std::u16string_view aText, + std::u16string_view aTarget = std::u16string_view()); + static OUString CreateImage( std::u16string_view aImage, std::u16string_view aAltText ); + OUString CreateNavBar( sal_uInt16 nSdPage, bool bIsText ) const; + OUString CreateBodyTag() const; + + OUString ParagraphToHTMLString( SdrOutliner const * pOutliner, sal_Int32 nPara, const Color& rBackgroundColor ); + OUString TextAttribToHTMLString( SfxItemSet const * pSet, HtmlState* pState, const Color& rBackgroundColor ); + + OUString CreateTextForTitle( SdrOutliner* pOutliner, SdPage* pPage, const Color& rBackgroundColor ); + OUString CreateTextForPage( SdrOutliner* pOutliner, SdPage const * pPage, bool bHeadLine, const Color& rBackgroundColor ); + OUString CreateTextForNotesPage( SdrOutliner* pOutliner, SdPage* pPage, const Color& rBackgroundColor ); + + static OUString CreateHTMLCircleArea( sal_uLong nRadius, sal_uLong nCenterX, + sal_uLong nCenterY, std::u16string_view rHRef ); + static OUString CreateHTMLPolygonArea( const ::basegfx::B2DPolyPolygon& rPolyPoly, Size aShift, double fFactor, std::u16string_view rHRef ); + static OUString CreateHTMLRectArea( const ::tools::Rectangle& rRect, + std::u16string_view rHRef ); + + OUString CreatePageURL( sal_uInt16 nPgNum ); + + OUString InsertSound( const OUString& rSoundFile ); + bool CopyFile( const OUString& rSourceFile, const OUString& rDestFile ); + bool CopyScript( std::u16string_view rPath, const OUString& rSource, const OUString& rDest, bool bUnix = false ); + + void InitProgress( sal_uInt16 nProgrCount ); + void ResetProgress(); + + /// Output only the charset metadata, title etc. will be handled separately. + static OUString CreateMetaCharset(); + + /// Output document metadata. + OUString DocumentMetadata() const; + + void InitExportParameters( const css::uno::Sequence< css::beans::PropertyValue >& rParams); + void ExportHtml(); + void ExportKiosk(); + void ExportWebCast(); + void ExportSingleDocument(); + + bool WriteHtml( const OUString& rFileName, bool bAddExtension, std::u16string_view rHtmlData ); + static OUString GetButtonName( int nButton ); + + void WriteOutlinerParagraph(OUStringBuffer& aStr, SdrOutliner* pOutliner, + OutlinerParaObject const * pOutlinerParagraphObject, + const Color& rBackgroundColor, bool bHeadLine); + + void WriteObjectGroup(OUStringBuffer& aStr, SdrObjGroup const * pObjectGroup, + SdrOutliner* pOutliner, const Color& rBackgroundColor, bool bHeadLine); + + void WriteTable(OUStringBuffer& aStr, sdr::table::SdrTableObj const * pTableObject, + SdrOutliner* pOutliner, const Color& rBackgroundColor); + + public: + HtmlExport(const OUString& aPath, + const css::uno::Sequence& rParams, + SdDrawDocument* pExpDoc, + sd::DrawDocShell* pDocShell); + + ~HtmlExport(); + + static OUString ColorToHTMLString( Color aColor ); + static OUString StringToHTMLString( const OUString& rString ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/htmlpublishmode.hxx b/sd/source/filter/html/htmlpublishmode.hxx new file mode 100644 index 000000000..3ba7eeb80 --- /dev/null +++ b/sd/source/filter/html/htmlpublishmode.hxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +enum HtmlPublishMode +{ + PUBLISH_HTML, + PUBLISH_FRAMES, + PUBLISH_WEBCAST, + PUBLISH_KIOSK, + PUBLISH_SINGLE_DOCUMENT +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/pubdlg.cxx b/sd/source/filter/html/pubdlg.cxx new file mode 100644 index 000000000..257021d96 --- /dev/null +++ b/sd/source/filter/html/pubdlg.cxx @@ -0,0 +1,1539 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "htmlattr.hxx" +#include "htmlex.hxx" +#include "htmlpublishmode.hxx" +#include +#include "buttonset.hxx" +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::beans; + +#define NOOFPAGES 6 + +//ID for the config-data with the HTML-settings +const sal_uInt16 nMagic = sal_uInt16(0x1977); + +// Key for the soffice.ini +constexpr OUStringLiteral KEY_QUALITY = u"JPG-EXPORT-QUALITY"; + +// The Help-IDs of the pages +const char* const aPageHelpIds[NOOFPAGES] = +{ + HID_SD_HTMLEXPORT_PAGE1, + HID_SD_HTMLEXPORT_PAGE2, + HID_SD_HTMLEXPORT_PAGE3, + HID_SD_HTMLEXPORT_PAGE4, + HID_SD_HTMLEXPORT_PAGE5, + HID_SD_HTMLEXPORT_PAGE6 +}; + +static SvStream& operator >> (SvStream& rIn, SdPublishingDesign& rDesign); + +static SvStream& WriteSdPublishingDesign(SvStream& rOut, const SdPublishingDesign& rDesign); + +// This class has all the settings for the HTML-export autopilot +class SdPublishingDesign +{ +public: + OUString m_aDesignName; + + HtmlPublishMode m_eMode; + + // special WebCast options + PublishingScript m_eScript; + OUString m_aCGI; + OUString m_aURL; + + // special Kiosk options + bool m_bAutoSlide; + sal_uInt32 m_nSlideDuration; + bool m_bEndless; + + // special HTML options + bool m_bContentPage; + bool m_bNotes; + + // misc options + sal_uInt16 m_nResolution; + OUString m_aCompression; + PublishingFormat m_eFormat; + bool m_bSlideSound; + bool m_bHiddenSlides; + + // title page information + OUString m_aAuthor; + OUString m_aEMail; + OUString m_aWWW; + OUString m_aMisc; + bool m_bDownload; + bool m_bCreated; // not used + + // buttons and colorscheme + sal_Int16 m_nButtonThema; + bool m_bUserAttr; + Color m_aBackColor; + Color m_aTextColor; + Color m_aLinkColor; + Color m_aVLinkColor; + Color m_aALinkColor; + bool m_bUseAttribs; + bool m_bUseColor; + + SdPublishingDesign(); + + bool operator ==(const SdPublishingDesign & rDesign) const; + friend SvStream& operator >> (SvStream& rIn, SdPublishingDesign& rDesign); + friend SvStream& WriteSdPublishingDesign(SvStream& rOut, const SdPublishingDesign& rDesign); +}; + +// load Default-settings +SdPublishingDesign::SdPublishingDesign() + : m_eMode(PUBLISH_HTML) + , m_eScript(SCRIPT_ASP) + , m_bAutoSlide(true) + , m_nSlideDuration(15) + , m_bEndless(true) + , m_bContentPage(true) + , m_bNotes(true) + , m_nResolution(PUB_LOWRES_WIDTH) + , m_eFormat(FORMAT_PNG) + , m_bSlideSound(true) + , m_bHiddenSlides(false) + , m_bDownload(false) + , m_bCreated(false) + , m_nButtonThema(-1) + , m_bUserAttr(false) + , m_aBackColor(COL_WHITE) + , m_aTextColor(COL_BLACK) + , m_aLinkColor(COL_BLUE) + , m_aVLinkColor(COL_LIGHTGRAY) + , m_aALinkColor(COL_GRAY) + , m_bUseAttribs(true) + , m_bUseColor(true) +{ + FilterConfigItem aFilterConfigItem(u"Office.Common/Filter/Graphic/Export/JPG"); + sal_Int32 nCompression = aFilterConfigItem.ReadInt32( KEY_QUALITY, 75 ); + m_aCompression = OUString::number(nCompression) + "%"; + + SvtUserOptions aUserOptions; + m_aAuthor = aUserOptions.GetFirstName(); + if (!m_aAuthor.isEmpty() && !aUserOptions.GetLastName().isEmpty()) + m_aAuthor += " "; + m_aAuthor += aUserOptions.GetLastName(); + m_aEMail = aUserOptions.GetEmail(); +} + +// Compares the values without paying attention to the name +bool SdPublishingDesign::operator ==(const SdPublishingDesign & rDesign) const +{ + return + ( + m_eMode == rDesign.m_eMode && + m_nResolution == rDesign.m_nResolution && + m_aCompression == rDesign.m_aCompression && + m_eFormat == rDesign.m_eFormat && + m_bHiddenSlides == rDesign.m_bHiddenSlides && + ( // compare html options + (m_eMode != PUBLISH_HTML && m_eMode != PUBLISH_FRAMES) || + ( + m_bContentPage == rDesign.m_bContentPage && + m_bNotes == rDesign.m_bNotes && + m_aAuthor == rDesign.m_aAuthor && + m_aEMail == rDesign.m_aEMail && + m_aWWW == rDesign.m_aWWW && + m_aMisc == rDesign.m_aMisc && + m_bDownload == rDesign.m_bDownload && + m_nButtonThema == rDesign.m_nButtonThema && + m_bUserAttr == rDesign.m_bUserAttr && + m_aBackColor == rDesign.m_aBackColor && + m_aTextColor == rDesign.m_aTextColor && + m_aLinkColor == rDesign.m_aLinkColor && + m_aVLinkColor == rDesign.m_aVLinkColor && + m_aALinkColor == rDesign.m_aALinkColor && + m_bUseAttribs == rDesign.m_bUseAttribs && + m_bSlideSound == rDesign.m_bSlideSound && + m_bUseColor == rDesign.m_bUseColor + ) + ) && + ( // compare kiosk options + (m_eMode != PUBLISH_KIOSK) || + ( + m_bAutoSlide == rDesign.m_bAutoSlide && + m_bSlideSound == rDesign.m_bSlideSound && + ( + !m_bAutoSlide || + ( + m_nSlideDuration == rDesign.m_nSlideDuration && + m_bEndless == rDesign.m_bEndless + ) + ) + ) + ) && + ( // compare WebCast options + (m_eMode != PUBLISH_WEBCAST) || + ( + m_eScript == rDesign.m_eScript && + ( + m_eScript != SCRIPT_PERL || + ( + m_aURL == rDesign.m_aURL && + m_aCGI == rDesign.m_aCGI + ) + ) + ) + ) + ); +} + +// Load the design from the stream +SvStream& operator >> (SvStream& rIn, SdPublishingDesign& rDesign) +{ + SdIOCompat aIO(rIn, StreamMode::READ); + + sal_uInt16 nTemp16; + tools::GenericTypeSerializer aSerializer(rIn); + + rDesign.m_aDesignName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rIn.ReadUInt16( nTemp16 ); + rDesign.m_eMode = static_cast(nTemp16); + rIn.ReadCharAsBool( rDesign.m_bContentPage ); + rIn.ReadCharAsBool( rDesign.m_bNotes ); + rIn.ReadUInt16( rDesign.m_nResolution ); + rDesign.m_aCompression = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rIn.ReadUInt16( nTemp16 ); + rDesign.m_eFormat = static_cast(nTemp16); + rDesign.m_aAuthor = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rDesign.m_aEMail = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rDesign.m_aWWW = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rDesign.m_aMisc = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rIn.ReadCharAsBool( rDesign.m_bDownload ); + rIn.ReadCharAsBool( rDesign.m_bCreated ); // not used + rIn.ReadInt16( rDesign.m_nButtonThema ); + rIn.ReadCharAsBool( rDesign.m_bUserAttr ); + aSerializer.readColor(rDesign.m_aBackColor); + aSerializer.readColor(rDesign.m_aTextColor); + aSerializer.readColor(rDesign.m_aLinkColor); + aSerializer.readColor(rDesign.m_aVLinkColor); + aSerializer.readColor(rDesign.m_aALinkColor); + rIn.ReadCharAsBool( rDesign.m_bUseAttribs ); + rIn.ReadCharAsBool( rDesign.m_bUseColor ); + + rIn.ReadUInt16( nTemp16 ); + rDesign.m_eScript = static_cast(nTemp16); + rDesign.m_aURL = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + rDesign.m_aCGI = read_uInt16_lenPrefixed_uInt8s_ToOUString(rIn, + RTL_TEXTENCODING_UTF8); + + rIn.ReadCharAsBool( rDesign.m_bAutoSlide ); + rIn.ReadUInt32( rDesign.m_nSlideDuration ); + rIn.ReadCharAsBool( rDesign.m_bEndless ); + rIn.ReadCharAsBool( rDesign.m_bSlideSound ); + rIn.ReadCharAsBool( rDesign.m_bHiddenSlides ); + + return rIn; +} + +// Set the design to the stream +SvStream& WriteSdPublishingDesign(SvStream& rOut, const SdPublishingDesign& rDesign) +{ + // The last parameter is the versionnumber of the code + SdIOCompat aIO(rOut, StreamMode::WRITE, 0); + + tools::GenericTypeSerializer aSerializer(rOut); + + // Name + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aDesignName, + RTL_TEXTENCODING_UTF8); + + rOut.WriteUInt16( rDesign.m_eMode ); + rOut.WriteBool( rDesign.m_bContentPage ); + rOut.WriteBool( rDesign.m_bNotes ); + rOut.WriteUInt16( rDesign.m_nResolution ); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aCompression, + RTL_TEXTENCODING_UTF8); + rOut.WriteUInt16( rDesign.m_eFormat ); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aAuthor, + RTL_TEXTENCODING_UTF8); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aEMail, + RTL_TEXTENCODING_UTF8); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aWWW, + RTL_TEXTENCODING_UTF8); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aMisc, + RTL_TEXTENCODING_UTF8); + rOut.WriteBool( rDesign.m_bDownload ); + rOut.WriteBool( rDesign.m_bCreated ); // not used + rOut.WriteInt16( rDesign.m_nButtonThema ); + rOut.WriteBool( rDesign.m_bUserAttr ); + aSerializer.writeColor(rDesign.m_aBackColor); + aSerializer.writeColor(rDesign.m_aTextColor); + aSerializer.writeColor(rDesign.m_aLinkColor); + aSerializer.writeColor(rDesign.m_aVLinkColor); + aSerializer.writeColor(rDesign.m_aALinkColor); + rOut.WriteBool( rDesign.m_bUseAttribs ); + rOut.WriteBool( rDesign.m_bUseColor ); + + rOut.WriteUInt16( rDesign.m_eScript ); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aURL, + RTL_TEXTENCODING_UTF8); + write_uInt16_lenPrefixed_uInt8s_FromOUString(rOut, rDesign.m_aCGI, + RTL_TEXTENCODING_UTF8); + + rOut.WriteBool( rDesign.m_bAutoSlide ); + rOut.WriteUInt32( rDesign.m_nSlideDuration ); + rOut.WriteBool( rDesign.m_bEndless ); + rOut.WriteBool( rDesign.m_bSlideSound ); + rOut.WriteBool( rDesign.m_bHiddenSlides ); + + return rOut; +} + +namespace { + +// Dialog for the entry of the name of the design +class SdDesignNameDlg : public weld::GenericDialogController +{ +private: + std::unique_ptr m_xEdit; + std::unique_ptr m_xBtnOK; + +public: + SdDesignNameDlg(weld::Window* pWindow, const OUString& aName ); + OUString GetDesignName() const; + DECL_LINK(ModifyHdl, weld::Entry&, void); +}; + +} + +// SdPublishingDlg Methods + +SdPublishingDlg::SdPublishingDlg(weld::Window* pWindow, DocumentType eDocType) + : GenericDialogController(pWindow, "modules/simpress/ui/publishingdialog.ui", "PublishingDialog") + , m_xPage1_Designs(m_xBuilder->weld_tree_view("designsTreeview")) + , m_xPage2_Standard_FB(m_xBuilder->weld_image("standardFBImage")) + , m_xPage2_Frames_FB(m_xBuilder->weld_image("framesFBImage")) + , m_xPage2_Kiosk_FB(m_xBuilder->weld_image("kioskFBImage")) + , m_xPage2_WebCast_FB(m_xBuilder->weld_image("webCastFBImage")) + , m_xPage4_Misc(m_xBuilder->weld_text_view("miscTextview")) + , m_xButtonSet(new ButtonSet()) + , m_xLastPageButton(m_xBuilder->weld_button("lastPageButton")) + , m_xNextPageButton(m_xBuilder->weld_button("nextPageButton")) + , m_xFinishButton(m_xBuilder->weld_button("finishButton")) + , aAssistentFunc(NOOFPAGES) + , m_bButtonsDirty(true) + , m_bDesignListDirty(false) + , m_pDesign(nullptr) +{ + m_bImpress = eDocType == DocumentType::Impress; + + Size aSize(m_xPage2_Standard_FB->get_approximate_digit_width() * 12, + m_xPage2_Standard_FB->get_text_height() * 6); + m_xPage2_Standard_FB->set_size_request(aSize.Width(), aSize.Height()); + m_xPage2_Frames_FB->set_size_request(aSize.Width(), aSize.Height()); + m_xPage2_Kiosk_FB->set_size_request(aSize.Width(), aSize.Height()); + m_xPage2_WebCast_FB->set_size_request(aSize.Width(), aSize.Height()); + + m_xPage4_Misc->set_size_request(m_xPage4_Misc->get_approximate_digit_width() * 40, + m_xPage4_Misc->get_height_rows(8)); + + m_xPage1_Designs->set_size_request(m_xPage4_Misc->get_approximate_digit_width() * 40, + m_xPage4_Misc->get_height_rows(8)); + + //Lock down the preferred size based on the + //initial max-size configuration + aSize = m_xDialog->get_preferred_size(); + m_xDialog->set_size_request(aSize.Width(), aSize.Height()); + + CreatePages(); + Load(); + + // sets the output page + aAssistentFunc.GotoPage(1); + m_xLastPageButton->set_sensitive(false); + + // button assignment + m_xFinishButton->connect_clicked(LINK(this,SdPublishingDlg,FinishHdl)); + m_xLastPageButton->connect_clicked(LINK(this,SdPublishingDlg,LastPageHdl)); + m_xNextPageButton->connect_clicked(LINK(this,SdPublishingDlg,NextPageHdl)); + + m_xPage1_NewDesign->connect_toggled(LINK(this,SdPublishingDlg,DesignHdl)); + m_xPage1_OldDesign->connect_toggled(LINK(this,SdPublishingDlg,DesignHdl)); + m_xPage1_Designs->connect_changed(LINK(this,SdPublishingDlg,DesignSelectHdl)); + m_xPage1_DelDesign->connect_clicked(LINK(this,SdPublishingDlg,DesignDeleteHdl)); + + m_xPage2_Standard->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + m_xPage2_Frames->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + m_xPage2_SingleDocument->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + m_xPage2_Kiosk->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + m_xPage2_WebCast->connect_toggled(LINK(this,SdPublishingDlg,BaseHdl)); + + m_xPage2_Content->connect_toggled(LINK(this,SdPublishingDlg,ContentHdl)); + + m_xPage2_ASP->connect_toggled(LINK(this,SdPublishingDlg,WebServerHdl)); + m_xPage2_PERL->connect_toggled(LINK(this,SdPublishingDlg,WebServerHdl)); + m_xPage2_Index->set_text("index" STR_HTMLEXP_DEFAULT_EXTENSION); + m_xPage2_CGI->set_text("/cgi-bin/"); + + m_xPage3_Png->connect_toggled(LINK(this,SdPublishingDlg, GfxFormatHdl)); + m_xPage3_Gif->connect_toggled(LINK(this,SdPublishingDlg, GfxFormatHdl)); + m_xPage3_Jpg->connect_toggled(LINK(this,SdPublishingDlg, GfxFormatHdl)); + m_xPage3_Quality->set_sensitive(false); + + m_xPage3_Resolution_1->connect_toggled(LINK(this,SdPublishingDlg, ResolutionHdl )); + m_xPage3_Resolution_2->connect_toggled(LINK(this,SdPublishingDlg, ResolutionHdl )); + m_xPage3_Resolution_3->connect_toggled(LINK(this,SdPublishingDlg, ResolutionHdl )); + m_xPage3_Resolution_4->connect_toggled(LINK(this, SdPublishingDlg, ResolutionHdl)); + + m_xPage2_ChgDefault->connect_toggled(LINK(this,SdPublishingDlg, SlideChgHdl)); + m_xPage2_ChgAuto->connect_toggled(LINK(this,SdPublishingDlg, SlideChgHdl)); + + m_xPage5_Buttons->SetSelectHdl(LINK(this,SdPublishingDlg, ButtonsHdl )); + m_xPage5_Buttons->SetStyle( m_xPage5_Buttons->GetStyle() | WB_VSCROLL ); + + m_xPage6_Back->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + m_xPage6_Text->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + m_xPage6_Link->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + m_xPage6_VLink->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + m_xPage6_ALink->connect_clicked(LINK(this,SdPublishingDlg, ColorHdl )); + + m_xPage6_DocColors->set_active(true); + + m_xPage3_Quality->append_text( "25%" ); + m_xPage3_Quality->append_text( "50%" ); + m_xPage3_Quality->append_text( "75%" ); + m_xPage3_Quality->append_text( "100%" ); + + m_xPage5_Buttons->SetColCount(); + m_xPage5_Buttons->SetLineCount( 4 ); + m_xPage5_Buttons->SetExtraSpacing( 1 ); + + for( const auto& rDesign : m_aDesignList ) + m_xPage1_Designs->append_text(rDesign.m_aDesignName); + + SetDefaults(); + + m_xDialog->set_help_id(aPageHelpIds[0]); + + m_xNextPageButton->grab_focus(); +} + +SdPublishingDlg::~SdPublishingDlg() +{ +} + +// Generate dialog controls and embed them in the pages +void SdPublishingDlg::CreatePages() +{ + // Page 1 + m_xPage1 = m_xBuilder->weld_container("page1"); + m_xPage1_Title = m_xBuilder->weld_label("assignLabel"); + m_xPage1_NewDesign = m_xBuilder->weld_radio_button("newDesignRadiobutton"); + m_xPage1_OldDesign = m_xBuilder->weld_radio_button("oldDesignRadiobutton"); + m_xPage1_DelDesign = m_xBuilder->weld_button("delDesingButton"); + m_xPage1_Desc = m_xBuilder->weld_label("descLabel"); + aAssistentFunc.InsertControl(1, m_xPage1.get()); + aAssistentFunc.InsertControl(1, m_xPage1_Title.get()); + aAssistentFunc.InsertControl(1, m_xPage1_NewDesign.get()); + aAssistentFunc.InsertControl(1, m_xPage1_OldDesign.get()); + aAssistentFunc.InsertControl(1, m_xPage1_Designs.get()); + aAssistentFunc.InsertControl(1, m_xPage1_DelDesign.get()); + aAssistentFunc.InsertControl(1, m_xPage1_Desc.get()); + + // Page 2 + m_xPage2 = m_xBuilder->weld_container("page2"); + m_xPage2Frame2 = m_xBuilder->weld_container("page2.2"); + m_xPage2Frame3 = m_xBuilder->weld_container("page2.3"); + m_xPage2Frame4 = m_xBuilder->weld_container("page2.4"); + m_xPage2_Title = m_xBuilder->weld_label("publicationLabel"); + m_xPage2_Standard = m_xBuilder->weld_radio_button("standardRadiobutton"); + m_xPage2_Frames = m_xBuilder->weld_radio_button("framesRadiobutton"); + m_xPage2_SingleDocument = m_xBuilder->weld_radio_button("singleDocumentRadiobutton"); + m_xPage2_Kiosk = m_xBuilder->weld_radio_button("kioskRadiobutton"); + m_xPage2_WebCast = m_xBuilder->weld_radio_button("webCastRadiobutton"); + aAssistentFunc.InsertControl(2, m_xPage2.get()); + aAssistentFunc.InsertControl(2, m_xPage2Frame2.get()); + aAssistentFunc.InsertControl(2, m_xPage2Frame3.get()); + aAssistentFunc.InsertControl(2, m_xPage2Frame4.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Title.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Standard.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Frames.get()); + aAssistentFunc.InsertControl(2, m_xPage2_SingleDocument.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Kiosk.get()); + aAssistentFunc.InsertControl(2, m_xPage2_WebCast.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Standard_FB.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Frames_FB.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Kiosk_FB.get()); + aAssistentFunc.InsertControl(2, m_xPage2_WebCast_FB.get()); + + m_xPage2_Title_Html = m_xBuilder->weld_label( "htmlOptionsLabel"); + m_xPage2_Content = m_xBuilder->weld_check_button("contentCheckbutton"); + m_xPage2_Notes = m_xBuilder->weld_check_button("notesCheckbutton"); + aAssistentFunc.InsertControl(2, m_xPage2_Title_Html.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Content.get()); + if (m_bImpress) + aAssistentFunc.InsertControl(2, m_xPage2_Notes.get()); + + m_xPage2_Title_WebCast = m_xBuilder->weld_label("webCastLabel"); + m_xPage2_ASP = m_xBuilder->weld_radio_button("ASPRadiobutton"); + m_xPage2_PERL = m_xBuilder->weld_radio_button("perlRadiobutton"); + m_xPage2_URL_txt = m_xBuilder->weld_label("URLTxtLabel"); + m_xPage2_URL = m_xBuilder->weld_entry("URLEntry"); + m_xPage2_CGI_txt = m_xBuilder->weld_label("CGITxtLabel"); + m_xPage2_CGI = m_xBuilder->weld_entry("CGIEntry"); + m_xPage2_Index_txt = m_xBuilder->weld_label("indexTxtLabel"); + m_xPage2_Index = m_xBuilder->weld_entry("indexEntry"); + m_xPage2_Title_Kiosk = m_xBuilder->weld_label("kioskLabel"); + m_xPage2_ChgDefault = m_xBuilder->weld_radio_button("chgDefaultRadiobutton"); + m_xPage2_ChgAuto = m_xBuilder->weld_radio_button("chgAutoRadiobutton"); + m_xPage2_Duration_txt = m_xBuilder->weld_label("durationTxtLabel"); + m_xPage2_Duration = m_xBuilder->weld_formatted_spin_button("durationSpinbutton"); + m_xFormatter.reset(new weld::TimeFormatter(*m_xPage2_Duration)); + m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration); + m_xPage2_Endless = m_xBuilder->weld_check_button("endlessCheckbutton"); + aAssistentFunc.InsertControl(2, m_xPage2_Title_WebCast.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Index_txt.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Index.get()); + aAssistentFunc.InsertControl(2, m_xPage2_ASP.get()); + aAssistentFunc.InsertControl(2, m_xPage2_PERL.get()); + aAssistentFunc.InsertControl(2, m_xPage2_URL_txt.get()); + aAssistentFunc.InsertControl(2, m_xPage2_URL.get()); + aAssistentFunc.InsertControl(2, m_xPage2_CGI_txt.get()); + aAssistentFunc.InsertControl(2, m_xPage2_CGI.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Title_Kiosk.get()); + aAssistentFunc.InsertControl(2, m_xPage2_ChgDefault.get()); + aAssistentFunc.InsertControl(2, m_xPage2_ChgAuto.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Duration_txt.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Duration.get()); + aAssistentFunc.InsertControl(2, m_xPage2_Endless.get()); + + // Page 3 + m_xPage3 = m_xBuilder->weld_container("page3"); + m_xPage3_Title1 = m_xBuilder->weld_label("saveImgAsLabel"); + m_xPage3_Png = m_xBuilder->weld_radio_button("pngRadiobutton"); + m_xPage3_Gif = m_xBuilder->weld_radio_button("gifRadiobutton"); + m_xPage3_Jpg = m_xBuilder->weld_radio_button("jpgRadiobutton"); + m_xPage3_Quality_txt = m_xBuilder->weld_label("qualityTxtLabel"); + m_xPage3_Quality= m_xBuilder->weld_combo_box("qualityCombobox"); + m_xPage3_Title2 = m_xBuilder->weld_label("monitorResolutionLabel"); + m_xPage3_Resolution_1 = m_xBuilder->weld_radio_button("resolution1Radiobutton"); + m_xPage3_Resolution_2 = m_xBuilder->weld_radio_button("resolution2Radiobutton"); + m_xPage3_Resolution_3 = m_xBuilder->weld_radio_button("resolution3Radiobutton"); + m_xPage3_Resolution_4 = m_xBuilder->weld_radio_button("resolution4Radiobutton"); + m_xPage3_Title3 = m_xBuilder->weld_label("effectsLabel"); + m_xPage3_SldSound = m_xBuilder->weld_check_button("sldSoundCheckbutton"); + m_xPage3_HiddenSlides = m_xBuilder->weld_check_button("hiddenSlidesCheckbutton"); + aAssistentFunc.InsertControl(3, m_xPage3.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Title1.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Png.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Gif.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Jpg.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Quality_txt.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Quality.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Title2.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Resolution_1.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Resolution_2.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Resolution_3.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Resolution_4.get()); + aAssistentFunc.InsertControl(3, m_xPage3_Title3.get()); + aAssistentFunc.InsertControl(3, m_xPage3_SldSound.get()); + aAssistentFunc.InsertControl(3, m_xPage3_HiddenSlides.get()); + + // Page 4 + m_xPage4 = m_xBuilder->weld_container("page4"); + m_xPage4_Title1 = m_xBuilder->weld_label("infTitlePageLabel"); + m_xPage4_Author_txt = m_xBuilder->weld_label("authorTxtLabel"); + m_xPage4_Author = m_xBuilder->weld_entry("authorEntry"); + m_xPage4_Email_txt = m_xBuilder->weld_label("emailTxtLabel"); + m_xPage4_Email = m_xBuilder->weld_entry("emailEntry"); + m_xPage4_WWW_txt = m_xBuilder->weld_label("wwwTxtLabel"); + m_xPage4_WWW = m_xBuilder->weld_entry("wwwEntry"); + m_xPage4_Title2 = m_xBuilder->weld_label("addInformLabel"); + m_xPage4_Download = m_xBuilder->weld_check_button("downloadCheckbutton"); + aAssistentFunc.InsertControl(4, m_xPage4.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Title1.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Author_txt.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Author.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Email_txt.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Email.get()); + aAssistentFunc.InsertControl(4, m_xPage4_WWW_txt.get()); + aAssistentFunc.InsertControl(4, m_xPage4_WWW.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Title2.get()); + aAssistentFunc.InsertControl(4, m_xPage4_Misc.get()); + if(m_bImpress) + aAssistentFunc.InsertControl(4, m_xPage4_Download.get()); + + // Page 5 + m_xPage5 = m_xBuilder->weld_container("page5"); + m_xPage5_Title = m_xBuilder->weld_label("buttonStyleLabel"); + m_xPage5_TextOnly = m_xBuilder->weld_check_button("textOnlyCheckbutton"); + m_xPage5_Buttons.reset(new ValueSet(m_xBuilder->weld_scrolled_window("buttonsDrawingareawin", true))); + m_xPage5_ButtonsWnd.reset(new weld::CustomWeld(*m_xBuilder, "buttonsDrawingarea", *m_xPage5_Buttons)); + aAssistentFunc.InsertControl(5, m_xPage5.get()); + aAssistentFunc.InsertControl(5, m_xPage5_Title.get()); + aAssistentFunc.InsertControl(5, m_xPage5_TextOnly.get()); + aAssistentFunc.InsertControl(5, m_xPage5_Buttons->GetDrawingArea()); + + // Page 6 + m_xPage6 = m_xBuilder->weld_container("page6"); + m_xPage6_Title = m_xBuilder->weld_label("selectColorLabel"); + m_xPage6_Default = m_xBuilder->weld_radio_button("defaultRadiobutton"); + m_xPage6_User = m_xBuilder->weld_radio_button("userRadiobutton"); + m_xPage6_Back = m_xBuilder->weld_button("backButton"); + m_xPage6_Text = m_xBuilder->weld_button("textButton"); + m_xPage6_Link = m_xBuilder->weld_button("linkButton"); + m_xPage6_VLink = m_xBuilder->weld_button("vLinkButton"); + m_xPage6_ALink = m_xBuilder->weld_button("aLinkButton"); + m_xPage6_DocColors = m_xBuilder->weld_radio_button("docColorsRadiobutton"); + m_xPage6_Preview.reset(new SdHtmlAttrPreview); + m_xPage6_PreviewWnd.reset(new weld::CustomWeld(*m_xBuilder, "previewDrawingarea", *m_xPage6_Preview)); + aAssistentFunc.InsertControl(6, m_xPage6.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Title.get()); + aAssistentFunc.InsertControl(6, m_xPage6_DocColors.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Default.get()); + aAssistentFunc.InsertControl(6, m_xPage6_User.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Text.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Link.get()); + aAssistentFunc.InsertControl(6, m_xPage6_ALink.get()); + aAssistentFunc.InsertControl(6, m_xPage6_VLink.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Back.get()); + aAssistentFunc.InsertControl(6, m_xPage6_Preview->GetDrawingArea()); +} + +// Initialize dialog with default-values +void SdPublishingDlg::SetDefaults() +{ + SdPublishingDesign aDefault; + SetDesign(&aDefault); + + m_xPage1_NewDesign->set_active(true); + m_xPage1_OldDesign->set_active(false); + UpdatePage(); +} + +// Feed the SfxItemSet with the settings of the dialog +void SdPublishingDlg::GetParameterSequence( Sequence< PropertyValue >& rParams ) +{ + std::vector< PropertyValue > aProps; + + PropertyValue aValue; + + // Page 2 + aValue.Name = "PublishMode"; + + HtmlPublishMode ePublishMode; + if (m_xPage2_Frames->get_active()) + ePublishMode = PUBLISH_FRAMES; + else if (m_xPage2_SingleDocument->get_active()) + ePublishMode = PUBLISH_SINGLE_DOCUMENT; + else if (m_xPage2_Kiosk->get_active()) + ePublishMode = PUBLISH_KIOSK; + else if (m_xPage2_WebCast->get_active()) + ePublishMode = PUBLISH_WEBCAST; + else + ePublishMode = PUBLISH_HTML; + + aValue.Value <<= static_cast(ePublishMode); + aProps.push_back( aValue ); + + aValue.Name = "IsExportContentsPage"; + aValue.Value <<= m_xPage2_Content->get_active(); + aProps.push_back( aValue ); + + if(m_bImpress) + { + aValue.Name = "IsExportNotes"; + aValue.Value <<= m_xPage2_Notes->get_active(); + aProps.push_back( aValue ); + } + + if( m_xPage2_WebCast->get_active() ) + { + aValue.Name = "WebCastScriptLanguage"; + if( m_xPage2_ASP->get_active() ) + aValue.Value <<= OUString( "asp" ); + else + aValue.Value <<= OUString( "perl" ); + aProps.push_back( aValue ); + + aValue.Name = "WebCastCGIURL"; + aValue.Value <<= m_xPage2_CGI->get_text(); + aProps.push_back( aValue ); + + aValue.Name = "WebCastTargetURL"; + aValue.Value <<= m_xPage2_URL->get_text(); + aProps.push_back( aValue ); + } + aValue.Name = "IndexURL"; + aValue.Value <<= m_xPage2_Index->get_text(); + aProps.push_back( aValue ); + + if( m_xPage2_Kiosk->get_active() && m_xPage2_ChgAuto->get_active() ) + { + aValue.Name = "KioskSlideDuration"; + aValue.Value <<= static_cast(m_xFormatter->GetTime().GetMSFromTime()) / 1000; + aProps.push_back( aValue ); + + aValue.Name = "KioskEndless"; + aValue.Value <<= m_xPage2_Endless->get_active(); + aProps.push_back( aValue ); + } + + // Page 3 + + aValue.Name = "Width"; + sal_Int32 nTmpWidth = PUB_LOWRES_WIDTH; + if( m_xPage3_Resolution_2->get_active() ) + nTmpWidth = PUB_MEDRES_WIDTH; + else if( m_xPage3_Resolution_3->get_active() ) + nTmpWidth = PUB_HIGHRES_WIDTH; + else if (m_xPage3_Resolution_4->get_active()) + nTmpWidth = PUB_FHDRES_WIDTH; + + aValue.Value <<= nTmpWidth; + aProps.push_back( aValue ); + + aValue.Name = "Compression"; + aValue.Value <<= m_xPage3_Quality->get_active_text(); + aProps.push_back( aValue ); + + aValue.Name = "Format"; + sal_Int32 nFormat; + if( m_xPage3_Png->get_active() ) + nFormat = static_cast(FORMAT_PNG); + else if( m_xPage3_Gif->get_active() ) + nFormat = static_cast(FORMAT_GIF); + else + nFormat = static_cast(FORMAT_JPG); + aValue.Value <<= nFormat; + aProps.push_back( aValue ); + + aValue.Name = "SlideSound"; + aValue.Value <<= m_xPage3_SldSound->get_active(); + aProps.push_back( aValue ); + + aValue.Name = "HiddenSlides"; + aValue.Value <<= m_xPage3_HiddenSlides->get_active(); + aProps.push_back( aValue ); + + // Page 4 + aValue.Name = "Author"; + aValue.Value <<= m_xPage4_Author->get_text(); + aProps.push_back( aValue ); + + aValue.Name = "EMail"; + aValue.Value <<= m_xPage4_Email->get_text(); + aProps.push_back( aValue ); + + // try to guess protocol for user's homepage + INetURLObject aHomeURL( m_xPage4_WWW->get_text(), + INetProtocol::Http, // default proto is HTTP + INetURLObject::EncodeMechanism::All ); + + aValue.Name = "HomepageURL"; + aValue.Value <<= aHomeURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + aProps.push_back( aValue ); + + aValue.Name = "UserText"; + aValue.Value <<= m_xPage4_Misc->get_text(); + aProps.push_back( aValue ); + + if( m_bImpress ) + { + aValue.Name = "EnableDownload"; + aValue.Value <<= m_xPage4_Download->get_active(); + aProps.push_back( aValue ); + } + + // Page 5 + if( !m_xPage5_TextOnly->get_active() ) + { + aValue.Name = "UseButtonSet"; + aValue.Value <<= static_cast(m_xPage5_Buttons->GetSelectedItemId() - 1); + aProps.push_back( aValue ); + } + + // Page 6 + if( m_xPage6_User->get_active() ) + { + aValue.Name = "BackColor"; + aValue.Value <<= m_aBackColor; + aProps.push_back( aValue ); + + aValue.Name = "TextColor"; + aValue.Value <<= m_aTextColor; + aProps.push_back( aValue ); + + aValue.Name = "LinkColor"; + aValue.Value <<= m_aLinkColor; + aProps.push_back( aValue ); + + aValue.Name = "VLinkColor"; + aValue.Value <<= m_aVLinkColor; + aProps.push_back( aValue ); + + aValue.Name = "ALinkColor"; + aValue.Value <<= m_aALinkColor; + aProps.push_back( aValue ); + } + + if( m_xPage6_DocColors->get_active() ) + { + aValue.Name = "IsUseDocumentColors"; + aValue.Value <<= true; + aProps.push_back( aValue ); + } + + rParams = comphelper::containerToSequence(aProps); +} + +// Clickhandler for the radiobuttons of the design-selection +IMPL_LINK( SdPublishingDlg, DesignHdl, weld::Toggleable&, rButton, void ) +{ + if (!rButton.get_active()) + return; + + if (m_xPage1_NewDesign->get_active()) + { + m_xPage1_NewDesign->set_active(true); // because of DesignDeleteHdl + m_xPage1_OldDesign->set_active(false); + m_xPage1_Designs->set_sensitive(false); + m_xPage1_DelDesign->set_sensitive(false); + m_pDesign = nullptr; + + SdPublishingDesign aDefault; + SetDesign(&aDefault); + } + else + { + m_xPage1_NewDesign->set_active(false); + m_xPage1_Designs->set_sensitive(true); + m_xPage1_DelDesign->set_sensitive(true); + + if (m_xPage1_Designs->get_selected_index() == -1) + m_xPage1_Designs->select(0); + + const sal_Int32 nPos = m_xPage1_Designs->get_selected_index(); + m_pDesign = &m_aDesignList[nPos]; + DBG_ASSERT(m_pDesign, "No Design? That's not allowed (CL)"); + + if(m_pDesign) + SetDesign(m_pDesign); + } +} + +// Clickhandler for the choice of one design +IMPL_LINK_NOARG(SdPublishingDlg, DesignSelectHdl, weld::TreeView&, void) +{ + const sal_Int32 nPos = m_xPage1_Designs->get_selected_index(); + m_pDesign = &m_aDesignList[nPos]; + DBG_ASSERT(m_pDesign, "No Design? That's not allowed (CL)"); + + if(m_pDesign) + SetDesign(m_pDesign); + + UpdatePage(); +} + +// Clickhandler for the delete of one design +IMPL_LINK_NOARG(SdPublishingDlg, DesignDeleteHdl, weld::Button&, void) +{ + const sal_Int32 nPos = m_xPage1_Designs->get_selected_index(); + + std::vector::iterator iter = m_aDesignList.begin()+nPos; + + DBG_ASSERT(iter != m_aDesignList.end(), "No Design? That's not allowed (CL)"); + + m_xPage1_Designs->remove(nPos); + + if(m_pDesign == &(*iter)) + DesignHdl(*m_xPage1_NewDesign); + + m_aDesignList.erase(iter); + + m_bDesignListDirty = true; + + UpdatePage(); +} + +// Clickhandler for the other servertypes +IMPL_LINK(SdPublishingDlg, WebServerHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + bool bASP = m_xPage2_ASP->get_active(); + m_xPage2_ASP->set_sensitive( bASP ); + m_xPage2_PERL->set_sensitive( !bASP ); + UpdatePage(); +} + +// Clickhandler for the Radiobuttons of the graphicformat choice +IMPL_LINK(SdPublishingDlg, GfxFormatHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + m_xPage3_Png->set_sensitive(m_xPage3_Png->get_active()); + m_xPage3_Gif->set_sensitive(m_xPage3_Gif->get_active()); + m_xPage3_Jpg->set_sensitive(m_xPage3_Jpg->get_active()); + m_xPage3_Quality->set_sensitive(m_xPage3_Jpg->get_active()); +} + +// Clickhandler for the Radiobuttons Standard/Frames +IMPL_LINK(SdPublishingDlg, BaseHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + + UpdatePage(); +} + +// Clickhandler for the Checkbox of the Title page +IMPL_LINK_NOARG(SdPublishingDlg, ContentHdl, weld::Toggleable&, void) +{ + if(m_xPage2_Content->get_active()) + { + if(!aAssistentFunc.IsEnabled(4)) + { + aAssistentFunc.EnablePage(4); + UpdatePage(); + } + } + else + { + if(aAssistentFunc.IsEnabled(4)) + { + aAssistentFunc.DisablePage(4); + UpdatePage(); + } + } +} + +// Clickhandler for the Resolution Radiobuttons +IMPL_LINK( SdPublishingDlg, ResolutionHdl, weld::Toggleable&, rButton, void ) +{ + if (!rButton.get_active()) + return; + m_xPage3_Resolution_1->set_sensitive(m_xPage3_Resolution_1->get_active()); + m_xPage3_Resolution_2->set_sensitive(m_xPage3_Resolution_2->get_active()); + m_xPage3_Resolution_3->set_sensitive(m_xPage3_Resolution_3->get_active()); + m_xPage3_Resolution_4->set_sensitive(m_xPage3_Resolution_4->get_active()); +} + +// Clickhandler for the ValueSet with the bitmap-buttons +IMPL_LINK_NOARG(SdPublishingDlg, ButtonsHdl, ValueSet*, void) +{ + // if one bitmap-button is chosen, then disable TextOnly + m_xPage5_TextOnly->set_active(false); +} + +// Fill the SfxItemSet with the settings of the dialog +IMPL_LINK( SdPublishingDlg, ColorHdl, weld::Button&, rButton, void) +{ + SvColorDialog aDlg; + + if (&rButton == m_xPage6_Back.get()) + { + aDlg.SetColor( m_aBackColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aBackColor = aDlg.GetColor(); + } + else if (&rButton == m_xPage6_Text.get()) + { + aDlg.SetColor( m_aTextColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aTextColor = aDlg.GetColor(); + } + else if (&rButton == m_xPage6_Link.get()) + { + aDlg.SetColor( m_aLinkColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aLinkColor = aDlg.GetColor(); + } + else if (&rButton == m_xPage6_VLink.get()) + { + aDlg.SetColor( m_aVLinkColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aVLinkColor = aDlg.GetColor(); + } + else if (&rButton == m_xPage6_ALink.get()) + { + aDlg.SetColor( m_aALinkColor ); + if(aDlg.Execute(m_xDialog.get()) == RET_OK ) + m_aALinkColor = aDlg.GetColor(); + } + + m_xPage6_User->set_active(true); + m_xPage6_Preview->SetColors( m_aBackColor, m_aTextColor, m_aLinkColor, + m_aVLinkColor, m_aALinkColor ); + m_xPage6_Preview->Invalidate(); +} + +IMPL_LINK(SdPublishingDlg, SlideChgHdl, weld::Toggleable&, rButton, void) +{ + if (!rButton.get_active()) + return; + UpdatePage(); +} + +// Clickhandler for the Ok Button +IMPL_LINK_NOARG(SdPublishingDlg, FinishHdl, weld::Button&, void) +{ + //End + SdPublishingDesign aDesign; + GetDesign(&aDesign); + + bool bSave = false; + + if(m_xPage1_OldDesign->get_active() && m_pDesign) + { + // are there changes? + if(!(aDesign == *m_pDesign)) + bSave = true; + } + else + { + SdPublishingDesign aDefaultDesign; + if(!(aDefaultDesign == aDesign)) + bSave = true; + } + + if(bSave) + { + OUString aName; + if(m_pDesign) + aName = m_pDesign->m_aDesignName; + + bool bRetry; + do + { + bRetry = false; + + SdDesignNameDlg aDlg(m_xDialog.get(), aName); + + if (aDlg.run() == RET_OK) + { + aDesign.m_aDesignName = aDlg.GetDesignName(); + + auto iter = std::find_if(m_aDesignList.begin(), m_aDesignList.end(), + [&aDesign](const SdPublishingDesign& rDesign) { return rDesign.m_aDesignName == aDesign.m_aDesignName; }); + + if (iter != m_aDesignList.end()) + { + std::unique_ptr xErrorBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Error, VclButtonsType::YesNo, + SdResId(STR_PUBDLG_SAMENAME))); + bRetry = xErrorBox->run() == RET_NO; + + if(!bRetry) + m_aDesignList.erase(iter); + } + + if(!bRetry) + { + m_aDesignList.push_back(aDesign); + m_bDesignListDirty = true; + } + } + } + while(bRetry); + } + + if(m_bDesignListDirty) + Save(); + + m_xDialog->response(RET_OK); +} + +// Refresh the dialogs when changing from pages +void SdPublishingDlg::ChangePage() +{ + int nPage = aAssistentFunc.GetCurrentPage(); + m_xDialog->set_help_id(aPageHelpIds[nPage-1]); + + UpdatePage(); + + if (m_xNextPageButton->get_sensitive()) + m_xNextPageButton->grab_focus(); + else + m_xFinishButton->grab_focus(); +} + +void SdPublishingDlg::UpdatePage() +{ + m_xNextPageButton->set_sensitive(!aAssistentFunc.IsLastPage()); + m_xLastPageButton->set_sensitive(!aAssistentFunc.IsFirstPage()); + + int nPage = aAssistentFunc.GetCurrentPage(); + + switch( nPage ) + { + case 1: + if(m_xPage1_NewDesign->get_active()) + { + m_xPage1_Designs->set_sensitive(false); + m_xPage1_DelDesign->set_sensitive(false); + } + + if(m_aDesignList.empty()) + m_xPage1_OldDesign->set_sensitive(false); + break; + case 2: + m_xPage2_Frames_FB->set_visible(m_xPage2_Frames->get_active()); + m_xPage2_Standard_FB->set_visible(m_xPage2_Standard->get_active()); + m_xPage2_Kiosk_FB->set_visible(m_xPage2_Kiosk->get_active()); + m_xPage2_WebCast_FB->set_visible(m_xPage2_WebCast->get_active()); + + if( m_xPage2_WebCast->get_active() ) + { + m_xPage2Frame4->show(); + m_xPage2_Title_WebCast->show(); + m_xPage2_ASP->show(); + m_xPage2_PERL->show(); + m_xPage2_URL_txt->show(); + m_xPage2_URL->show(); + m_xPage2_CGI_txt->show(); + m_xPage2_CGI->show(); + m_xPage2_Index_txt->show(); + m_xPage2_Index->show(); + + bool bPerl = m_xPage2_PERL->get_active(); + m_xPage2_Index->set_sensitive(bPerl); + m_xPage2_Index_txt->set_sensitive(bPerl); + m_xPage2_URL_txt->set_sensitive(bPerl); + m_xPage2_URL->set_sensitive(bPerl); + m_xPage2_CGI_txt->set_sensitive(bPerl); + m_xPage2_CGI->set_sensitive(bPerl); + } + else + { + m_xPage2Frame4->hide(); + m_xPage2_Title_WebCast->hide(); + m_xPage2_ASP->hide(); + m_xPage2_PERL->hide(); + m_xPage2_URL_txt->hide(); + m_xPage2_URL->hide(); + m_xPage2_CGI_txt->hide(); + m_xPage2_CGI->hide(); + m_xPage2_Index->hide(); + m_xPage2_Index_txt->hide(); + } + + if( m_xPage2_Kiosk->get_active() ) + { + m_xPage2Frame3->show(); + m_xPage2_Title_Kiosk->show(); + m_xPage2_ChgDefault->show(); + m_xPage2_ChgAuto->show(); + m_xPage2_Duration_txt->show(); + m_xPage2_Duration->show(); + m_xPage2_Endless->show(); + bool bAuto = m_xPage2_ChgAuto->get_active(); + m_xPage2_Duration->set_sensitive(bAuto); + m_xPage2_Endless->set_sensitive(bAuto); + } + else + { + m_xPage2Frame3->hide(); + m_xPage2_Title_Kiosk->hide(); + m_xPage2_ChgDefault->hide(); + m_xPage2_ChgAuto->hide(); + m_xPage2_Duration->hide(); + m_xPage2_Duration_txt->hide(); + m_xPage2_Endless->hide(); + } + + if( m_xPage2_Standard->get_active() || m_xPage2_Frames->get_active() ) + { + m_xPage2Frame2->show(); + m_xPage2_Title_Html->show(); + m_xPage2_Content->show(); + if(m_bImpress) + m_xPage2_Notes->show(); + } + else + { + m_xPage2Frame2->hide(); + m_xPage2_Title_Html->hide(); + m_xPage2_Content->hide(); + if(m_bImpress) + m_xPage2_Notes->hide(); + } + break; + case 3: + if( m_xPage2_Kiosk->get_active() || m_xPage2_WebCast->get_active() ) + m_xNextPageButton->set_sensitive(false); + + if( m_xPage2_WebCast->get_active() ) + m_xPage3_SldSound->set_sensitive(false); + + m_xPage3_Quality->set_sensitive(m_xPage3_Jpg->get_active()); + + break; + case 5: + if( m_bButtonsDirty ) + LoadPreviewButtons(); + break; + } +} + +/** loads the html buttons from the button sets, creates a preview and fills the + itemset for page 5 + */ +void SdPublishingDlg::LoadPreviewButtons() +{ + if (!m_xButtonSet) + return; + + const int nButtonCount = 8; + static const char *pButtonNames[nButtonCount] = + { + "first.png", + "left.png", + "right.png", + "last.png", + "home.png", + "text.png", + "expand.png", + "collapse.png", + }; + + std::vector< OUString > aButtonNames; + for(const char * p : pButtonNames) + aButtonNames.push_back( OUString::createFromAscii( p ) ); + + int nSetCount = m_xButtonSet->getCount(); + + int nHeight = 32; + Image aImage; + for( int nSet = 0; nSet < nSetCount; ++nSet ) + { + if( m_xButtonSet->getPreview( nSet, aButtonNames, aImage ) ) + { + m_xPage5_Buttons->InsertItem( static_cast(nSet)+1, aImage ); + if( nHeight < aImage.GetSizePixel().Height() ) + nHeight = aImage.GetSizePixel().Height(); + } + } + + m_xPage5_Buttons->SetItemHeight( nHeight ); + m_bButtonsDirty = false; +} + +// Clickhandler for the Forward Button +IMPL_LINK_NOARG(SdPublishingDlg, NextPageHdl, weld::Button&, void) +{ + aAssistentFunc.NextPage(); + ChangePage(); +} + +// Sets the Controls in the dialog to the settings in the design +void SdPublishingDlg::SetDesign( SdPublishingDesign const * pDesign ) +{ + if(!pDesign) + return; + + m_xPage2_Standard->set_sensitive(pDesign->m_eMode == PUBLISH_HTML); + m_xPage2_Frames->set_sensitive(pDesign->m_eMode == PUBLISH_FRAMES); + m_xPage2_Kiosk->set_sensitive(pDesign->m_eMode == PUBLISH_KIOSK ); + m_xPage2_WebCast->set_sensitive(pDesign->m_eMode == PUBLISH_WEBCAST ); + + m_xPage2_Content->set_sensitive(pDesign->m_bContentPage); + if(pDesign->m_bContentPage) + aAssistentFunc.EnablePage(4); + else + aAssistentFunc.DisablePage(4); + + if(m_bImpress) + m_xPage2_Notes->set_sensitive(pDesign->m_bNotes); + + m_xPage2_ASP->set_sensitive(pDesign->m_eScript == SCRIPT_ASP); + m_xPage2_PERL->set_sensitive(pDesign->m_eScript == SCRIPT_PERL); + m_xPage2_CGI->set_text(pDesign->m_aCGI); + m_xPage2_URL->set_text(pDesign->m_aURL); + + m_xPage2_ChgDefault->set_sensitive( !pDesign->m_bAutoSlide ); + m_xPage2_ChgAuto->set_sensitive( pDesign->m_bAutoSlide ); + + tools::Time aTime( tools::Time::EMPTY ); + aTime.MakeTimeFromMS( pDesign->m_nSlideDuration * 1000 ); + m_xFormatter->SetTime(aTime); + + m_xPage2_Endless->set_sensitive( pDesign->m_bEndless ); + + m_xPage3_Png->set_sensitive(pDesign->m_eFormat == FORMAT_PNG); + m_xPage3_Gif->set_sensitive(pDesign->m_eFormat == FORMAT_GIF); + m_xPage3_Jpg->set_sensitive(pDesign->m_eFormat == FORMAT_JPG); + m_xPage3_Quality->set_sensitive(pDesign->m_eFormat == FORMAT_JPG); + + m_xPage3_Quality->set_entry_text(pDesign->m_aCompression); + m_xPage3_Resolution_1->set_sensitive(pDesign->m_nResolution == PUB_LOWRES_WIDTH); + m_xPage3_Resolution_2->set_sensitive(pDesign->m_nResolution == PUB_MEDRES_WIDTH); + m_xPage3_Resolution_3->set_sensitive(pDesign->m_nResolution == PUB_HIGHRES_WIDTH); + m_xPage3_Resolution_4->set_sensitive(pDesign->m_nResolution == PUB_FHDRES_WIDTH); + + m_xPage3_SldSound->set_sensitive( pDesign->m_bSlideSound ); + m_xPage3_HiddenSlides->set_sensitive( pDesign->m_bHiddenSlides ); + + m_xPage4_Author->set_text(pDesign->m_aAuthor); + m_xPage4_Email->set_text(pDesign->m_aEMail); + m_xPage4_WWW->set_text(pDesign->m_aWWW); + m_xPage4_Misc->set_text(pDesign->m_aMisc); + if(m_bImpress) + m_xPage4_Download->set_sensitive(pDesign->m_bDownload); + + m_xPage5_TextOnly->set_sensitive(pDesign->m_nButtonThema == -1); + if(pDesign->m_nButtonThema != -1) + { + if(m_bButtonsDirty) + LoadPreviewButtons(); + m_xPage5_Buttons->SelectItem(pDesign->m_nButtonThema + 1); + } + else + m_xPage5_Buttons->SetNoSelection(); + + m_xPage6_User->set_sensitive(pDesign->m_bUserAttr); + m_aBackColor = pDesign->m_aBackColor; + m_aTextColor = pDesign->m_aTextColor; + m_aLinkColor = pDesign->m_aLinkColor; + m_aVLinkColor = pDesign->m_aVLinkColor; + m_aALinkColor = pDesign->m_aALinkColor; + + m_xPage6_DocColors->set_sensitive(pDesign->m_bUseColor); + + m_xPage6_Preview->SetColors( m_aBackColor, m_aTextColor, m_aLinkColor, + m_aVLinkColor, m_aALinkColor ); + m_xPage6_Preview->Invalidate(); + + UpdatePage(); +} + +// Transfer the status of the Design Dialog Controls +void SdPublishingDlg::GetDesign( SdPublishingDesign* pDesign ) +{ + if(!pDesign) + return; + + pDesign->m_eMode = m_xPage2_Standard->get_active()?PUBLISH_HTML: + m_xPage2_Frames->get_active()?PUBLISH_FRAMES: + m_xPage2_Kiosk->get_active()?PUBLISH_KIOSK: + PUBLISH_WEBCAST; + + pDesign->m_bContentPage = m_xPage2_Content->get_active(); + if(m_bImpress) + pDesign->m_bNotes = m_xPage2_Notes->get_active(); + + if( m_xPage3_Gif->get_active() ) + pDesign->m_eFormat = FORMAT_GIF; + else if( m_xPage3_Jpg->get_active() ) + pDesign->m_eFormat = FORMAT_JPG; + else + pDesign->m_eFormat = FORMAT_PNG; + + pDesign->m_aCompression = m_xPage3_Quality->get_active_text(); + + if (m_xPage3_Resolution_1->get_active()) + pDesign->m_nResolution = PUB_LOWRES_WIDTH; + else if (m_xPage3_Resolution_2->get_active()) + pDesign->m_nResolution = PUB_MEDRES_WIDTH; + else if (m_xPage3_Resolution_3->get_active()) + pDesign->m_nResolution = PUB_HIGHRES_WIDTH; + else + pDesign->m_nResolution = PUB_FHDRES_WIDTH; + + pDesign->m_bSlideSound = m_xPage3_SldSound->get_active(); + pDesign->m_bHiddenSlides = m_xPage3_HiddenSlides->get_active(); + + pDesign->m_aAuthor = m_xPage4_Author->get_text(); + pDesign->m_aEMail = m_xPage4_Email->get_text(); + pDesign->m_aWWW = m_xPage4_WWW->get_text(); + pDesign->m_aMisc = m_xPage4_Misc->get_text(); + pDesign->m_bDownload = m_bImpress && m_xPage4_Download->get_active(); + + if(m_xPage5_TextOnly->get_active()) + pDesign->m_nButtonThema = -1; + else + pDesign->m_nButtonThema = m_xPage5_Buttons->GetSelectedItemId() - 1; + + pDesign->m_bUserAttr = m_xPage6_User->get_active(); + pDesign->m_aBackColor = m_aBackColor; + pDesign->m_aTextColor = m_aTextColor; + pDesign->m_aLinkColor = m_aLinkColor; + pDesign->m_aVLinkColor = m_aVLinkColor; + pDesign->m_aALinkColor = m_aALinkColor; + pDesign->m_bUseColor = m_xPage6_DocColors->get_active(); + + pDesign->m_eScript = m_xPage2_ASP->get_active()?SCRIPT_ASP:SCRIPT_PERL; + pDesign->m_aCGI = m_xPage2_CGI->get_text(); + pDesign->m_aURL = m_xPage2_URL->get_text(); + + pDesign->m_bAutoSlide = m_xPage2_ChgAuto->get_active(); + pDesign->m_nSlideDuration = static_cast(m_xFormatter->GetTime().GetMSFromTime()) / 1000; + pDesign->m_bEndless = m_xPage2_Endless->get_active(); +} + +// Clickhandler for the back Button +IMPL_LINK_NOARG(SdPublishingDlg, LastPageHdl, weld::Button&, void) +{ + aAssistentFunc.PreviousPage(); + ChangePage(); +} + +// Load Designs +void SdPublishingDlg::Load() +{ + m_bDesignListDirty = false; + + INetURLObject aURL( SvtPathOptions().GetUserConfigPath() ); + aURL.Append( u"designs.sod" ); + + // check if file exists, SfxMedium shows an errorbox else + { + std::unique_ptr pIStm = ::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ); + + bool bOk = pIStm && ( pIStm->GetError() == ERRCODE_NONE); + + if( !bOk ) + return; + } + + SfxMedium aMedium( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ | StreamMode::NOCREATE ); + + SvStream* pStream = aMedium.GetInStream(); + + if( !pStream ) + return; + + sal_uInt16 aCheck; + pStream->ReadUInt16( aCheck ); + + if(aCheck != nMagic) + return; + + SdIOCompat aIO(*pStream, StreamMode::READ); + + sal_uInt16 nDesigns(0); + pStream->ReadUInt16(nDesigns); + + // there has to at least be a sal_uInt16 header in each design + const size_t nMaxRecords = pStream->remainingSize() / sizeof(sal_uInt16); + if (nDesigns > nMaxRecords) + { + SAL_WARN("sd", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nDesigns << " claimed, truncating"); + nDesigns = nMaxRecords; + } + + for( sal_uInt16 nIndex = 0; + pStream->GetError() == ERRCODE_NONE && nIndex < nDesigns; + nIndex++ ) + { + SdPublishingDesign aDesign; + *pStream >> aDesign; + + m_aDesignList.push_back(aDesign); + } +} + +// Save Designs +bool SdPublishingDlg::Save() +{ + INetURLObject aURL( SvtPathOptions().GetUserConfigPath() ); + aURL.Append( u"designs.sod" ); + SfxMedium aMedium( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::TRUNC ); + + SvStream* pStream = aMedium.GetOutStream(); + + if( !pStream ) + return false; + + pStream->WriteUInt16( nMagic ); + + // Destroys the SdIOCompat before the Stream is being distributed + { + SdIOCompat aIO(*pStream, StreamMode::WRITE, 0); + + sal_uInt16 nDesigns = static_cast(m_aDesignList.size()); + pStream->WriteUInt16( nDesigns ); + + for( sal_uInt16 nIndex = 0; + pStream->GetError() == ERRCODE_NONE && nIndex < nDesigns; + nIndex++ ) + WriteSdPublishingDesign( *pStream, m_aDesignList[nIndex] ); + } + + aMedium.Close(); + aMedium.Commit(); + + return( aMedium.GetError() == ERRCODE_NONE ); +} + +// SdDesignNameDlg Methods +SdDesignNameDlg::SdDesignNameDlg(weld::Window* pWindow, const OUString& rName) + : GenericDialogController(pWindow, "modules/sdraw/ui/namedesign.ui", "NameDesignDialog") + , m_xEdit(m_xBuilder->weld_entry("entry")) + , m_xBtnOK(m_xBuilder->weld_button("ok")) +{ + m_xEdit->connect_changed(LINK(this, SdDesignNameDlg, ModifyHdl )); + m_xEdit->set_text(rName); + m_xBtnOK->set_sensitive(!rName.isEmpty()); +} + +OUString SdDesignNameDlg::GetDesignName() const +{ + return m_xEdit->get_text(); +} + +IMPL_LINK_NOARG(SdDesignNameDlg, ModifyHdl, weld::Entry&, void) +{ + m_xBtnOK->set_sensitive(!m_xEdit->get_text().isEmpty()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/html/sdhtmlfilter.cxx b/sd/source/filter/html/sdhtmlfilter.cxx new file mode 100644 index 000000000..f7a3bc10f --- /dev/null +++ b/sd/source/filter/html/sdhtmlfilter.cxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include "htmlex.hxx" +#include + +SdHTMLFilter::SdHTMLFilter(SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell) + : SdFilter(rMedium, rDocShell) +{ +} + +SdHTMLFilter::~SdHTMLFilter() {} + +bool SdHTMLFilter::Export() +{ + mrMedium.Close(); + mrMedium.Commit(); + + SfxItemSet* pSet = mrMedium.GetItemSet(); + + css::uno::Sequence aParams; + + if (const SfxUnoAnyItem* pItem = pSet->GetItemIfSet(SID_FILTER_DATA, false)) + pItem->GetValue() >>= aParams; + + HtmlExport aExport(mrMedium.GetName(), aParams, &mrDocument, &mrDocShell); + + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/pdf/sdpdffilter.cxx b/sd/source/filter/pdf/sdpdffilter.cxx new file mode 100644 index 000000000..002c1c5db --- /dev/null +++ b/sd/source/filter/pdf/sdpdffilter.cxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#include +#include + +#include + +using namespace css; + +SdPdfFilter::SdPdfFilter(SfxMedium& rMedium, sd::DrawDocShell& rDocShell) + : SdFilter(rMedium, rDocShell) +{ +} + +SdPdfFilter::~SdPdfFilter() {} + +bool SdPdfFilter::Import() +{ + const OUString aFileName( + mrMedium.GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::NONE)); + + std::vector aGraphics; + if (vcl::ImportPDFUnloaded(aFileName, aGraphics) == 0) + return false; + + bool bWasLocked = mrDocument.isLocked(); + mrDocument.setLock(true); + const bool bSavedUndoEnabled = mrDocument.IsUndoEnabled(); + mrDocument.EnableUndo(false); + + // Add as many pages as we need up-front. + mrDocument.CreateFirstPages(); + for (size_t i = 0; i < aGraphics.size() - 1; ++i) + { + mrDocument.DuplicatePage(0); + } + + for (vcl::PDFGraphicResult const& rPDFGraphicResult : aGraphics) + { + const Graphic& rGraphic = rPDFGraphicResult.GetGraphic(); + const Size& aSizeHMM = rPDFGraphicResult.GetSize(); + + const sal_Int32 nPageNumber = rGraphic.getPageNumber(); + assert(nPageNumber >= 0 && o3tl::make_unsigned(nPageNumber) < aGraphics.size()); + + // Create the page and insert the Graphic. + SdPage* pPage = mrDocument.GetSdPage(nPageNumber, PageKind::Standard); + if (!pPage) // failed to duplicate page, out of memory? + return false; + + // Make the page size match the rendered image. + pPage->SetSize(aSizeHMM); + + SdrGrafObj* pSdrGrafObj = new SdrGrafObj(pPage->getSdrModelFromSdrPage(), rGraphic, + tools::Rectangle(Point(), aSizeHMM)); + + pSdrGrafObj->SetResizeProtect(true); + pSdrGrafObj->SetMoveProtect(true); + + pPage->InsertObject(pSdrGrafObj); + + for (auto const& rPDFAnnotation : rPDFGraphicResult.GetAnnotations()) + { + uno::Reference xAnnotation; + pPage->createAnnotation(xAnnotation); + + xAnnotation->setAuthor(rPDFAnnotation.maAuthor); + + uno::Reference xText(xAnnotation->getTextRange()); + xText->setString(rPDFAnnotation.maText); + // position is in mm not 100thmm + geometry::RealPoint2D aUnoPosition(rPDFAnnotation.maRectangle.getMinX() / 100.0, + rPDFAnnotation.maRectangle.getMinY() / 100.00); + geometry::RealSize2D aUnoSize(rPDFAnnotation.maRectangle.getWidth() / 100.0, + rPDFAnnotation.maRectangle.getHeight() / 100.00); + xAnnotation->setPosition(aUnoPosition); + xAnnotation->setSize(aUnoSize); + xAnnotation->setDateTime(rPDFAnnotation.maDateTime); + + if (rPDFAnnotation.mpMarker) + { + auto* pAnnotation = static_cast(xAnnotation.get()); + pAnnotation->createCustomAnnotationMarker(); + sd::CustomAnnotationMarker& rCustomAnnotationMarker + = pAnnotation->getCustomAnnotationMarker(); + + rCustomAnnotationMarker.maLineColor = rPDFAnnotation.maColor; + + if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Polygon) + { + auto* pMarker = static_cast( + rPDFAnnotation.mpMarker.get()); + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = pMarker->maFillColor; + rCustomAnnotationMarker.maPolygons.push_back(pMarker->maPolygon); + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Square) + { + auto* pMarker = static_cast( + rPDFAnnotation.mpMarker.get()); + basegfx::B2DPolygon aPoly + = basegfx::utils::createPolygonFromRect(rPDFAnnotation.maRectangle); + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = pMarker->maFillColor; + rCustomAnnotationMarker.maPolygons.push_back(aPoly); + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Circle) + { + auto* pMarker = static_cast( + rPDFAnnotation.mpMarker.get()); + + basegfx::B2DPoint rCenter = rPDFAnnotation.maRectangle.getCenter(); + double fRadiusX = rPDFAnnotation.maRectangle.getWidth() / 2; + double fRadiusY = rPDFAnnotation.maRectangle.getHeight() / 2; + + basegfx::B2DPolygon aPoly + = basegfx::utils::createPolygonFromEllipse(rCenter, fRadiusX, fRadiusY); + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = pMarker->maFillColor; + rCustomAnnotationMarker.maPolygons.push_back(aPoly); + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Ink) + { + auto* pMarker = static_cast( + rPDFAnnotation.mpMarker.get()); + rCustomAnnotationMarker.maPolygons.insert( + rCustomAnnotationMarker.maPolygons.end(), pMarker->maStrokes.begin(), + pMarker->maStrokes.end()); + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = pMarker->maFillColor; + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Highlight) + { + if (!rCustomAnnotationMarker.maLineColor.IsTransparent()) + rCustomAnnotationMarker.maLineColor.SetAlpha(255 - 0x90); + auto* pMarker = static_cast( + rPDFAnnotation.mpMarker.get()); + rCustomAnnotationMarker.maPolygons.insert( + rCustomAnnotationMarker.maPolygons.end(), pMarker->maQuads.begin(), + pMarker->maQuads.end()); + rCustomAnnotationMarker.mnLineWidth = 1; + rCustomAnnotationMarker.maFillColor = rPDFAnnotation.maColor; + if (!rCustomAnnotationMarker.maFillColor.IsTransparent()) + rCustomAnnotationMarker.maFillColor.SetAlpha(255 - 0x90); + } + else if (rPDFAnnotation.meSubType == vcl::pdf::PDFAnnotationSubType::Line) + { + auto* pMarker = static_cast( + rPDFAnnotation.mpMarker.get()); + + basegfx::B2DPolygon aPoly; + aPoly.append(pMarker->maLineStart); + aPoly.append(pMarker->maLineEnd); + rCustomAnnotationMarker.maPolygons.push_back(aPoly); + + rCustomAnnotationMarker.mnLineWidth = pMarker->mnWidth; + rCustomAnnotationMarker.maFillColor = COL_TRANSPARENT; + } + } + } + } + mrDocument.setLock(bWasLocked); + mrDocument.EnableUndo(bSavedUndoEnabled); + return true; +} + +bool SdPdfFilter::Export() { return false; } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/ppt97animations.cxx b/sd/source/filter/ppt/ppt97animations.cxx new file mode 100644 index 000000000..db3a960a7 --- /dev/null +++ b/sd/source/filter/ppt/ppt97animations.cxx @@ -0,0 +1,682 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ppt97animations.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +void Ppt97AnimationInfoAtom::ReadStream( SvStream& rIn ) +{ + sal_uInt32 nTmp; + rIn.ReadUInt32( nTmp ); + nDimColor = Color(ColorTransparency, nTmp); + rIn.ReadUInt32( nFlags ); + rIn.ReadUInt32( nSoundRef ); + rIn.ReadInt32( nDelayTime ); + rIn.ReadUInt16( nOrderID ); + rIn.ReadUInt16( nSlideCount ); + rIn.ReadUChar( nBuildType ); + rIn.ReadUChar( nFlyMethod ); + rIn.ReadUChar( nFlyDirection ); + rIn.ReadUChar( nAfterEffect ); + rIn.ReadUChar( nSubEffect ); + rIn.ReadUChar( nOLEVerb ); + rIn.ReadUChar( nUnknown1 ); + rIn.ReadUChar( nUnknown2 ); +} + +Ppt97Animation::Ppt97Animation( SvStream& rInputStream ) + : m_aAtom() + , m_bDirtyCache(true) + , m_bHasSpecialDuration(false) + , m_fDurationInSeconds(0.001) +{ + m_aAtom.ReadStream( rInputStream ); +} + +bool Ppt97Animation::operator < ( const Ppt97Animation& rAnimation ) const +{ + return m_aAtom.nOrderID < rAnimation.m_aAtom.nOrderID; +} +bool Ppt97Animation::operator > ( const Ppt97Animation& rAnimation ) const +{ + return m_aAtom.nOrderID > rAnimation.m_aAtom.nOrderID; +} +bool Ppt97Animation::HasEffect() const +{ + return m_aAtom.nBuildType != 0; +} +bool Ppt97Animation::HasParagraphEffect() const +{ + return m_aAtom.nBuildType > 1; +} +sal_Int32 Ppt97Animation::GetParagraphLevel() const +{ + sal_Int32 nParagraphLevel = 0; + if(m_aAtom.nBuildType>1) + nParagraphLevel = m_aAtom.nBuildType-1; + return nParagraphLevel; +} +bool Ppt97Animation::HasSoundEffect() const +{ + return m_aAtom.nSoundRef && m_aAtom.nFlags & 0x0010; +} +bool Ppt97Animation::HasStopPreviousSound() const +{ + return m_aAtom.nFlags & 0x0040; +} +bool Ppt97Animation::HasReverseOrder() const +{ + return m_aAtom.nFlags & 0x001; +} +bool Ppt97Animation::HasAnimateAssociatedShape() const +{ + return m_aAtom.nFlags & 0x004000; +} +bool Ppt97Animation::HasAfterEffect() const +{ + return m_aAtom.nAfterEffect != 0; +} +bool Ppt97Animation::HasAfterEffect_ChangeColor() const +{ + return m_aAtom.nAfterEffect == 1; +} +bool Ppt97Animation::HasAfterEffect_DimAtNextEffect() const +{ + return m_aAtom.nAfterEffect == 2; +} +#ifdef FUTURE +bool Ppt97Animation::HasAfterEffect_DimAfterEffect() const +{ + return m_aAtom.nAfterEffect == 3; +} +#endif +void Ppt97Animation::SetSoundFileUrl( const OUString& rSoundFileUrl ) +{ + m_aSoundFileUrl = rSoundFileUrl; +} + +double Ppt97Animation::GetDelayTimeInSeconds() const +{ + return m_aAtom.nDelayTime != 0X7FFFFFFF ? m_aAtom.nDelayTime/1000.0 : 0.0; +} + +bool Ppt97Animation::GetSpecialDuration( double& rfDurationInSeconds ) const +{ + UpdateCacheData(); + if( m_bHasSpecialDuration ) + rfDurationInSeconds = m_fDurationInSeconds; + return m_bHasSpecialDuration; +} + +bool Ppt97Animation::GetSpecialTextIterationDelay( double& rfTextIterationDelay ) const +{ + bool bRet = false; + switch(GetTextAnimationType()) + { + case presentation::TextAnimationType::BY_LETTER: + rfTextIterationDelay = 0.075; + bRet = true; + break; + case presentation::TextAnimationType::BY_WORD: + rfTextIterationDelay = 0.3; + bRet = true; + break; + default: + break; + } + return bRet; +} + +void Ppt97Animation::SetDimColor( Color nDimColor ) +{ + m_aAtom.nDimColor = nDimColor; +} +void Ppt97Animation::SetAnimateAssociatedShape( bool bAnimate ) +{ + if( !bAnimate ) + { + //the appear effect cannot be animated without text + if( GetPresetId() == "ooo-entrance-appear" ) + return; + //the random effect may be the appear effect and then has the same problem + if( GetPresetId() == "ooo-entrance-random" ) + { + //this case is not 100% correct -> feel free to complete + //i consider this case as seldom and not that problematic and a simple correct fix is not in sight + SAL_INFO("sd", "you tried to deselect the animation of the form for random animation-> this has been refused"); + return; + } + + } + + if(bAnimate) + m_aAtom.nFlags = m_aAtom.nFlags | 0x004000; + else if( HasAnimateAssociatedShape() ) + { + m_aAtom.nFlags = m_aAtom.nFlags ^ 0x004000; + } +} + +sal_Int16 Ppt97Animation::GetEffectNodeType() const //see css::presentation::EffectNodeType +{ + sal_Int16 nRet = presentation::EffectNodeType::ON_CLICK; + if( m_aAtom.nFlags & 0x04 ) + { + nRet = presentation::EffectNodeType::AFTER_PREVIOUS; + } + return nRet; +} + +sal_Int16 Ppt97Animation::GetTextAnimationType() const +{ + sal_Int16 nRet = presentation::TextAnimationType::BY_PARAGRAPH; + switch( m_aAtom.nSubEffect ) + { + case 0: + break; + case 2: + nRet = presentation::TextAnimationType::BY_LETTER; + break; + default: + nRet = presentation::TextAnimationType::BY_WORD; + break; + } + return nRet; +} +OUString const & Ppt97Animation::GetPresetId() const +{ + UpdateCacheData(); + return m_aPresetId; +} +OUString const & Ppt97Animation::GetPresetSubType() const +{ + UpdateCacheData(); + return m_aSubType; +} + +void Ppt97Animation::ClearCacheData() const +{ + m_aPresetId.clear(); + m_aSubType.clear(); + m_bHasSpecialDuration = false; + m_fDurationInSeconds = 0.001; +} +void Ppt97Animation::UpdateCacheData() const +{ + if( !m_bDirtyCache ) + return; + + ClearCacheData(); + + if( !HasEffect() ) + { + m_bDirtyCache = false; + return; + } + + switch( m_aAtom.nFlyMethod ) + { + case 0x0: + m_aPresetId = "ooo-entrance-appear"; // --- appear --- + break; + case 0x01: + m_aPresetId = "ooo-entrance-random"; // --- random --- + break; + case 0x02: // --- blinds effect --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-venetian-blinds"; + m_aSubType = "horizontal"; // horizontal + break; + case 0x1: + m_aPresetId = "ooo-entrance-venetian-blinds"; + m_aSubType = "vertical"; // vertical + break; + } + } + break; + case 0x03: // --- (hor/ver) shifted appear --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-checkerboard"; + m_aSubType = "across"; // vertical ??? + break; + case 0x1: + m_aPresetId = "ooo-entrance-checkerboard"; + m_aSubType = "downward"; // horizontal ??? + break; + } + } + break; + case 0x05: + m_aPresetId = "ooo-entrance-dissolve-in"; + break; + case 0x08: // --- (hor/ver) lines --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-random-bars"; + m_aSubType = "vertical"; // horizontal ??? + break; + case 0x1: + m_aPresetId = "ooo-entrance-random-bars"; + m_aSubType = "horizontal"; // vertical ??? + break; + } + } + break; + case 0x09: // --- diagonal --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x4: + m_aPresetId = "ooo-entrance-diagonal-squares"; + m_aSubType = "left-to-top"; // to left top + break; + case 0x5: + m_aPresetId = "ooo-entrance-diagonal-squares"; + m_aSubType = "right-to-top"; // to right top + break; + case 0x6: + m_aPresetId = "ooo-entrance-diagonal-squares"; + m_aSubType = "left-to-bottom"; // to left bottom + break; + case 0x7: + m_aPresetId = "ooo-entrance-diagonal-squares"; + m_aSubType = "right-to-bottom"; // to right bottom + break; + } + } + break; + case 0x0a: // --- roll/wipe --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-wipe"; + m_aSubType = "from-right"; // from right + break; + case 0x1: + m_aPresetId = "ooo-entrance-wipe"; + m_aSubType = "from-bottom"; // from bottom + break; + case 0x2: + m_aPresetId = "ooo-entrance-wipe"; + m_aSubType = "from-left"; // from left + break; + case 0x3: + m_aPresetId = "ooo-entrance-wipe"; + m_aSubType = "from-top"; // from top + break; + } + } + break; + case 0x0b: //--- fade in --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-box"; + m_aSubType = "out"; // from center + break; + case 0x1: + m_aPresetId = "ooo-entrance-box"; + m_aSubType = "in"; // to center + break; + } + } + break; + case 0x0c: // --- text effects --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-left"; + + break; + case 0x1: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-top"; + break; + case 0x2: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-right"; + break; + case 0x3: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-bottom"; + break; + case 0x4: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-top-left"; + break; + case 0x5: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-top-right"; + break; + case 0x6: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-bottom-left"; + break; + case 0x7: + m_aPresetId = "ooo-entrance-fly-in"; + m_aSubType = "from-bottom-right"; + break; + case 0x8: // -- short text effects -- + m_aPresetId = "ooo-entrance-peek-in"; + m_aSubType = "from-left"; + break; + case 0x9: + m_aPresetId = "ooo-entrance-peek-in"; + m_aSubType = "from-bottom"; + break; + case 0xa: + m_aPresetId = "ooo-entrance-peek-in"; + m_aSubType = "from-right"; + break; + case 0xb: + m_aPresetId = "ooo-entrance-peek-in"; + m_aSubType = "from-top"; + break; + case 0xc: // -- slow text effects -- + { + m_aPresetId = "ooo-entrance-fly-in-slow"; + m_aSubType = "from-left"; + } + break; + case 0xd: + { + m_aPresetId = "ooo-entrance-fly-in-slow"; + m_aSubType = "from-top"; + } + break; + case 0xe: + { + m_aPresetId = "ooo-entrance-fly-in-slow"; + m_aSubType = "from-right"; + } + break; + case 0xf: + { + m_aPresetId = "ooo-entrance-fly-in-slow"; + m_aSubType = "from-bottom"; + } + break; + case 0x10: // --- zoom --- + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "in"; + break; + case 0x11: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "in-slightly"; + break; + case 0x12: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "out"; + break; + case 0x13: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "out-slightly"; + break; + case 0x14: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "in-from-screen-center"; + break; + case 0x15: + m_aPresetId = "ooo-entrance-zoom"; + m_aSubType = "out-from-screen-center"; + break; + case 0x16: // --- stretch --- + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "across"; + break; + case 0x17: + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "from-left"; + break; + case 0x18: + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "from-top"; + break; + case 0x19: + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "from-right"; + break; + case 0x1a: + m_aPresetId = "ooo-entrance-stretchy"; + m_aSubType = "from-bottom"; + break; + case 0x1b: // --- rotate --- + m_aPresetId = "ooo-entrance-swivel"; + m_aSubType = "vertical"; + break; + case 0x1c: // --- spirale --- + m_aPresetId = "ooo-entrance-spiral-in"; + break; + } + } + break; + case 0x0d: // --- open/close --- + { + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: + m_aPresetId = "ooo-entrance-split"; + m_aSubType = "horizontal-out"; //horizontal open + break; + case 0x1: + m_aPresetId = "ooo-entrance-split"; + m_aSubType = "horizontal-in"; //horizontal close + break; + case 0x2: + m_aPresetId = "ooo-entrance-split"; + m_aSubType = "vertical-out"; // vertical open + break; + case 0x3: + m_aPresetId = "ooo-entrance-split"; + m_aSubType = "vertical-in"; // vertical close + break; + } + } + break; + case 0x0e: // --- blink --- + { + m_aPresetId = "ooo-entrance-flash-once"; + switch ( m_aAtom.nFlyDirection ) + { + case 0x0: //fast + m_fDurationInSeconds = 0.075; + m_bHasSpecialDuration = true; + break; + case 0x1: //medium + m_fDurationInSeconds = 0.5; + m_bHasSpecialDuration = true; + break; + case 0x2: //slow + m_fDurationInSeconds = 1.0; + m_bHasSpecialDuration = true; + break; + } + } + break; + default: + { + m_aPresetId = "ooo-entrance-appear"; + OSL_FAIL("no effect mapped"); + } + break; + } + m_bDirtyCache = false; +} + +void Ppt97Animation::createAndSetCustomAnimationEffect( SdrObject* pObj ) +{ + + if( !HasEffect() ) + return; + if( !pObj || !pObj->getSdrPageFromSdrObject() ) + { + OSL_FAIL("no valid SdrObject or page found for ppt import"); + return; + } + + uno::Reference< drawing::XShape > xShape = GetXShapeForSdrObject( pObj ); + if( !xShape.is() ) + { + OSL_FAIL("no XShape interface found for ppt import"); + return; + } + ::sd::MainSequencePtr pMainSequence = static_cast(pObj->getSdrPageFromSdrObject())->getMainSequence(); + if( !pMainSequence ) + { + OSL_FAIL("no MainSequence found for ppt import"); + return; + } + + const ::sd::CustomAnimationPresets& rPresets( ::sd::CustomAnimationPresets::getCustomAnimationPresets() ); + ::sd::CustomAnimationPresetPtr pPreset( rPresets.getEffectDescriptor( GetPresetId() ) ); + if( !pPreset ) + { + OSL_FAIL("no suitable preset found for ppt import"); + return; + } + + //--------------start doing something + + //1. ------ create an effect from the presets ------ + ::sd::CustomAnimationEffectPtr pEffect = std::make_shared<::sd::CustomAnimationEffect>( pPreset->create( GetPresetSubType() ) ); + + //2. ------ adapt the created effect ------ + + // set the shape targeted by this effect + pEffect->setTarget( css::uno::Any( xShape ) ); + + pEffect->setBegin( GetDelayTimeInSeconds() ); + + // some effects need a different duration than that of the mapped preset effect + double fDurationInSeconds = 1.0; //in seconds + if( GetSpecialDuration( fDurationInSeconds ) ) + pEffect->setDuration( fDurationInSeconds ); + + // set after effect + if( HasAfterEffect() ) + { + pEffect->setHasAfterEffect( true ); + if( HasAfterEffect_ChangeColor() ) + pEffect->setDimColor( uno::Any( GetDimColor() ) ); + else + pEffect->setAfterEffectOnNext( HasAfterEffect_DimAtNextEffect() ); + } + + // set sound effect + if( HasSoundEffect() ) + pEffect->createAudio( uno::Any( m_aSoundFileUrl ) ); + + // text iteration + pEffect->setIterateType( GetTextAnimationType() ); + + // some effects need a different delay between text iteration than that of the mapped preset effect + double fTextIterationDelay = 1.0; + if( GetSpecialTextIterationDelay( fTextIterationDelay ) ) + pEffect->setIterateInterval( fTextIterationDelay ); + + // is the effect started on click or after the last effect (Another possible value is EffectNodeType::WITH_PREVIOUS ) + pEffect->setNodeType( GetEffectNodeType() ); + + //set stop sound effect + if( HasStopPreviousSound() ) + pEffect->setStopAudio(); + + // append the effect to the main sequence + if( !HasParagraphEffect() ) + { + // TODO: !HasAnimateAssociatedShape() can possibly have this set to ONLY_TEXT - see i#42737 + pEffect->setTargetSubItem( presentation::ShapeAnimationSubType::AS_WHOLE ); + } + + //3. ------ put the created effect to the model and do some last changes fro paragraph effects ------ + pMainSequence->append( pEffect ); + if( HasParagraphEffect() ) + { + sal_Int32 nParagraphLevel = GetParagraphLevel(); + double fDelaySeconds = GetDelayTimeInSeconds(); + bool bAnimateAssociatedShape = HasAnimateAssociatedShape();//or only text + bool bTextReverse = HasReverseOrder(); + + // now create effects for each paragraph + ::sd::CustomAnimationTextGroupPtr pGroup = pMainSequence-> + createTextGroup( pEffect, nParagraphLevel, fDelaySeconds, bAnimateAssociatedShape, bTextReverse ); + + if( pGroup ) + { + const ::sd::EffectSequence& rEffects = pGroup->getEffects(); + + ::sd::CustomAnimationEffectPtr pLastEffect; + sal_Int32 nIndex = 0; + for( const auto& rxEffect : rEffects ) + { + ::sd::CustomAnimationEffectPtr pGroupEffect(rxEffect); + + ////todo? if( nIndex > 1 && pLastEffect && HasSoundEffect() ) + //// pLastEffect->setStopAudio(); + if( nIndex < 2 ) + { + pGroupEffect->setNodeType( GetEffectNodeType() ); + } + else if( nIndex > 0 ) + { + bool bAtParagraphBegin = false; + if(!bTextReverse) + bAtParagraphBegin = pGroupEffect->getParaDepth() < nParagraphLevel; + else + bAtParagraphBegin = !pLastEffect || pLastEffect->getParaDepth() < nParagraphLevel; + if( bAtParagraphBegin ) + pGroupEffect->setNodeType( GetEffectNodeType() ); + else if( GetTextAnimationType() == presentation::TextAnimationType::BY_PARAGRAPH ) + pGroupEffect->setNodeType( presentation::EffectNodeType::WITH_PREVIOUS ); + else + pGroupEffect->setNodeType( presentation::EffectNodeType::AFTER_PREVIOUS ); + } + pLastEffect = pGroupEffect; + nIndex++; + } + } + } + pMainSequence->rebuild(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/ppt97animations.hxx b/sd/source/filter/ppt/ppt97animations.hxx new file mode 100644 index 000000000..1811d93a3 --- /dev/null +++ b/sd/source/filter/ppt/ppt97animations.hxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class SdrObject; +class SvStream; + +/// helper class for reading PPT AnimationInfoAtom +class Ppt97AnimationInfoAtom +{ + friend class Ppt97Animation; + +//-- member + Color nDimColor; + sal_uInt32 nFlags; ///< 0x0004: time instead of click + sal_uInt32 nSoundRef; + sal_Int32 nDelayTime; ///< 1/1000 sec + sal_uInt16 nOrderID; + sal_uInt16 nSlideCount; + sal_uInt8 nBuildType; + sal_uInt8 nFlyMethod; + sal_uInt8 nFlyDirection; + sal_uInt8 nAfterEffect; ///< nAfterEffect: 0: none; 1: change color; 2: dim on next effect; 3: dim after effect; + sal_uInt8 nSubEffect; + sal_uInt8 nOLEVerb; + + // unknown, because whole size needs to be 28 + sal_uInt8 nUnknown1; + sal_uInt8 nUnknown2; + +//-- methods + void ReadStream( SvStream& rIn ); +/* + nFlags: + decimal / hexadecimal / binary + 1040 0x00000410 10000010000 mouseclick + 17428 0x00004414 100010000010100 after previous 0 sec (animate form) + 17412 0x00004404 100010000000100 after previous 0 sec + 1088 0x00000440 10001000000 stop previous sound and mouseclick + 1044 0x00000414 10000010100 play sound automatic + 1041 0x00000411 10000010001 + | | | | | | + | | | | | reverse order + | | | | after previous + | | | sound + | | stop previous sound + | ? + animate form + + nAfterEffect: + 1: color + 0: nothing + 3: hide after animation + 2: hide at next mouse click +*/ +}; + +/** this is a helping class for import of PPT 97 animations + 1. use the constructor Ppt97Animation( SvStream& rIn ) to import information from the stream + 2. use the set methods to modify and complete the data + 3. use the method createAndSetCustomAnimationEffect( ) to create an effect in sd model + */ +class Ppt97Animation +{ + +public: //public methods + explicit Ppt97Animation( SvStream& rIn ); + + bool operator < ( const Ppt97Animation& rAnimation ) const;//later is greater + bool operator > ( const Ppt97Animation& rAnimation ) const;//later is greater + + //get methods + bool HasEffect() const; + bool HasParagraphEffect() const; + bool HasSoundEffect() const; + sal_Int32 GetDimColor() const { return static_cast(m_aAtom.nDimColor);} + sal_uInt32 GetSoundRef() const { return m_aAtom.nSoundRef;} + /// @return true if the shape should be animated in addition to the text + bool HasAnimateAssociatedShape() const; + + //set methods + void SetDimColor( Color nDimColor ); + void SetSoundFileUrl( const OUString& rSoundFileUrl ); + void SetAnimateAssociatedShape( bool bAnimate ); //true if the shape should be animated in addition to the text + + //action methods + /** this method creates a CustomAnimationEffect for the given SdrObject + from internal data and stores the created effect at the draw model + */ + void createAndSetCustomAnimationEffect( SdrObject* pObj ); + +private: //private methods + + //read methods + OUString const & GetPresetId() const; + OUString const & GetPresetSubType() const; + bool HasAfterEffect() const; + bool HasAfterEffect_ChangeColor() const; + bool HasAfterEffect_DimAtNextEffect() const; + bool HasStopPreviousSound() const; + + /// @return true if the text paragraphs should be animated in reverse order + bool HasReverseOrder() const; + + ///paragraph level that is animated ( that paragraph and higher levels ) + sal_Int32 GetParagraphLevel() const; + + ///@see css::presentation::TextAnimationType + sal_Int16 GetTextAnimationType() const; + + ///@see css::presentation::EffectNodeType + sal_Int16 GetEffectNodeType() const; + + /// @return -1 for start on mouseclick or >= 0 for a delay in seconds for automatic start + double GetDelayTimeInSeconds() const; + bool GetSpecialDuration( double& rfDurationInSeconds ) const; + bool GetSpecialTextIterationDelay( double& rfTextIterationDelay ) const; + + void UpdateCacheData() const; + void ClearCacheData() const; + +private: //private member + //input information: + Ppt97AnimationInfoAtom m_aAtom; ///< pure input from stream + OUString m_aSoundFileUrl; ///< this needs to be set in addition from outside as this class has not the knowledge to translate the sound bits to a file url + + //cached generated output information: + mutable bool m_bDirtyCache; + mutable OUString m_aPresetId; // m_aPresetId and m_aSubType match to the values in sd/xml/effects.xml + mutable OUString m_aSubType; + mutable bool m_bHasSpecialDuration; + mutable double m_fDurationInSeconds; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptanimations.hxx b/sd/source/filter/ppt/pptanimations.hxx new file mode 100644 index 000000000..7fda1bb68 --- /dev/null +++ b/sd/source/filter/ppt/pptanimations.hxx @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + +class SvStream; + +namespace ppt +{ + +// old transition types +#define PPT_TRANSITION_TYPE_NONE 0 +#define PPT_TRANSITION_TYPE_RANDOM 1 +#define PPT_TRANSITION_TYPE_BLINDS 2 +#define PPT_TRANSITION_TYPE_CHECKER 3 +#define PPT_TRANSITION_TYPE_COVER 4 +#define PPT_TRANSITION_TYPE_DISSOLVE 5 +#define PPT_TRANSITION_TYPE_FADE 6 +#define PPT_TRANSITION_TYPE_PULL 7 // Uncover in MS-PPT Specs +#define PPT_TRANSITION_TYPE_RANDOM_BARS 8 +#define PPT_TRANSITION_TYPE_STRIPS 9 +#define PPT_TRANSITION_TYPE_WIPE 10 +#define PPT_TRANSITION_TYPE_ZOOM 11 // Box In/Out in MS-PPT Specs +#define PPT_TRANSITION_TYPE_SPLIT 13 + +// effects, new in xp +#define PPT_TRANSITION_TYPE_DIAMOND 17 +#define PPT_TRANSITION_TYPE_PLUS 18 +#define PPT_TRANSITION_TYPE_WEDGE 19 +#define PPT_TRANSITION_TYPE_PUSH 20 +#define PPT_TRANSITION_TYPE_COMB 21 +#define PPT_TRANSITION_TYPE_NEWSFLASH 22 +#define PPT_TRANSITION_TYPE_SMOOTHFADE 23 // Alpha Fade in MS-PPT Specs +#define PPT_TRANSITION_TYPE_WHEEL 26 +#define PPT_TRANSITION_TYPE_CIRCLE 27 + +// undocumented(?) +#define PPT_TRANSITION_TYPE_FLASH 30 + +// atoms +#define DFF_msofbtAnimEvent 0xf125 +#define DFF_msofbtAnimNode 0xf127 +#define DFF_msofbtAnimTrigger 0xf128 +#define DFF_msofbtAnimValue 0xf129 +#define DFF_msofbtAnimateTarget 0xf12a +#define DFF_msofbtAnimate 0xf12b +#define DFF_msofbtAnimateColor 0xf12c +#define DFF_msofbtAnimateFilter 0xf12d +#define DFF_msofbtAnimateMotion 0xf12e +#define DFF_msofbtAnimateRotation 0xf12f +#define DFF_msofbtAnimateScale 0xf130 +#define DFF_msofbtAnimateSet 0xf131 +#define DFF_msofbtAnimCommand 0xf132 +#define DFF_msofbtAnimateTargetSettings 0xf133 +#define DFF_msofbtAnimateData 0xf134 +#define DFF_msofbtAnimateColorData 0xf135 +#define DFF_msofbtAnimateFilterData 0xf136 +#define DFF_msofbtAnimateMotionData 0xf137 +#define DFF_msofbtAnimateScaleData 0xf139 +#define DFF_msofbtAnimateSetData 0xf13a +#define DFF_msofbtCommandData 0xf13b +#define DFF_msofbtAnimateTargetElement 0xf13c +#define DFF_msofbtAnimPropertySet 0xf13d +#define DFF_msofbtAnimateAttributeNames 0xf13e +#define DFF_msofbtAnimKeyPoints 0xf13f +#define DFF_msofbtAnimIteration 0xf140 +#define DFF_msofbtAnimAction 0xf141 // correct name?? +#define DFF_msofbtAnimAttributeValue 0xf142 +#define DFF_msofbtAnimKeyTime 0xf143 +#define DFF_msofbtAnimGroup 0xf144 +#define DFF_msofbtAnimSubGoup 0xf145 +#define DFF_msofbtAnimateRotationData 0xf138 +#define DFF_msofbtAnimReference 0x2afb + +// property ids +#define DFF_ANIM_ID 1 +#define DFF_ANIM_RUNTIMECONTEXT 2 +#define DFF_ANIM_PATH_EDIT_MODE 3 +#define DFF_ANIM_COLORSPACE 4 +#define DFF_ANIM_DIRECTION 5 // TODO: Conflict? +#define DFF_ANIM_MASTERREL 5 // TODO: Conflict? +#define DFF_ANIM_OVERRIDE 6 +#define DFF_ANIM_PRESET_ID 9 +#define DFF_ANIM_PRESET_SUB_TYPE 10 +#define DFF_ANIM_PRESET_CLASS 11 +#define DFF_ANIM_AFTEREFFECT 13 +#define DFF_ANIM_ENDAFTERSLIDE 15 +#define DFF_ANIM_TIMEFILTER 16 +#define DFF_ANIM_EVENT_FILTER 17 +#define DFF_ANIM_GROUP_ID 19 +#define DFF_ANIM_NODE_TYPE 20 +#define DFF_ANIM_VOLUME 22 +#define DFF_ANIM_PROPERTY_ID_COUNT (DFF_ANIM_VOLUME + 1) + +// property types +#define DFF_ANIM_PROP_TYPE_BYTE 0 +#define DFF_ANIM_PROP_TYPE_INT32 1 +#define DFF_ANIM_PROP_TYPE_FLOAT 2 +#define DFF_ANIM_PROP_TYPE_UNISTRING 3 + +#define DFF_ANIM_PRESS_CLASS_USER_DEFINED 0 +#define DFF_ANIM_PRESS_CLASS_ENTRANCE 1 +#define DFF_ANIM_PRESS_CLASS_EXIT 2 +#define DFF_ANIM_PRESS_CLASS_EMPHASIS 3 +#define DFF_ANIM_PRESS_CLASS_MOTIONPATH 4 +#define DFF_ANIM_PRESS_CLASS_OLE_ACTION 5 +#define DFF_ANIM_PRESS_CLASS_MEDIACALL 6 + +// Effect node type. +#define DFF_ANIM_NODE_TYPE_ON_CLICK 1 +#define DFF_ANIM_NODE_TYPE_WITH_PREVIOUS 2 +#define DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS 3 +#define DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE 4 +#define DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ 5 +#define DFF_ANIM_NODE_TYPE_CLICK_PARALLEL 6 +#define DFF_ANIM_NODE_TYPE_WITH_GROUP 7 +#define DFF_ANIM_NODE_TYPE_AFTER_GROUP 8 +#define DFF_ANIM_NODE_TYPE_TIMING_ROOT 9 + +/* constants for fill entry in AnimationNode */ +const sal_Int32 mso_Anim_GroupType_PAR = 0; +const sal_Int32 mso_Anim_GroupType_SEQ = 1; +const sal_Int32 mso_Anim_GroupType_NODE = 3; +const sal_Int32 mso_Anim_GroupType_MEDIA = 4; + +/* constants for fill entry in AnimationNode */ +const sal_Int32 mso_Anim_Fill_ALWAYS = 1; +const sal_Int32 mso_Anim_Fill_WHENOFF = 2; +const sal_Int32 mso_Anim_Fill_NEVER = 3; + +/* constants for fill entry in AnimationNode */ +const sal_Int32 mso_Anim_Fill_REMOVE = 1; +const sal_Int32 mso_Anim_Fill_FREEZE = 2; +const sal_Int32 mso_Anim_Fill_HOLD = 3; + +/* constants for behaviour entry in PPTAnimationNode */ +const sal_Int32 mso_Anim_Behaviour_FILTER = 24; +const sal_Int32 mso_Anim_Behaviour_ANIMATION= 25; + +typedef ::std::map< sal_Int32, css::uno::Any > PropertySetMap_t; + +class PropertySet +{ +public: + PropertySetMap_t maProperties; + + bool hasProperty( sal_Int32 nProperty ) const; + css::uno::Any getProperty( sal_Int32 nProperty ) const; +}; + +/** this atom is the first entry in each animation group */ +struct AnimationNode +{ +public: + /** see mso_Anim_GroupType_? */ + sal_Int32 mnGroupType; + + /** see mso_Anim_Restart_? */ + sal_Int32 mnRestart; + + /** see mso_Anim_Fill_? */ + sal_Int32 mnFill; + + /** see mso_Anim_Behaviour_? */ + sal_Int32 mnNodeType; + + /** duration of this group in 1000th seconds */ + sal_Int32 mnDuration; + + sal_Int32 mnU1, mnU3, mnU4; + + AnimationNode() + : mnGroupType(0) + , mnRestart(0) + , mnFill(0) + , mnNodeType(0) + , mnDuration(0) + , mnU1(0), mnU3(0), mnU4(0) + { + } +public: + + friend SvStream& WriteAnimationNode(SvStream& rOut, AnimationNode const & rAtom); +}; + +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptatom.cxx b/sd/source/filter/ppt/pptatom.cxx new file mode 100644 index 000000000..24d87f040 --- /dev/null +++ b/sd/source/filter/ppt/pptatom.cxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "pptatom.hxx" + +using namespace ppt; + +Atom::Atom( const DffRecordHeader& rRecordHeader, SvStream& rStream ) +: mrStream( rStream ) +, maRecordHeader( rRecordHeader ) +, mpFirstChild( nullptr ) +, mpNextAtom( nullptr ) +{ + if( isContainer() ) + { + if( seekToContent() ) + { + DffRecordHeader aChildHeader; + + Atom* pLastAtom = nullptr; + + // retrieve file size (to allow sanity checks) + sal_uInt64 const nStreamSize = mrStream.TellEnd(); + + while( mrStream.good() + && ( mrStream.Tell() < nStreamSize ) + && ( mrStream.Tell() < maRecordHeader.GetRecEndFilePos() ) ) + { + if (ReadDffRecordHeader(mrStream, aChildHeader)) + { + Atom* pAtom = new Atom( aChildHeader, mrStream ); + + if( pLastAtom ) + pLastAtom->mpNextAtom = pAtom; + if( mpFirstChild == nullptr ) + mpFirstChild = pAtom; + + pLastAtom = pAtom; + } + } + } + } + + if (!maRecordHeader.SeekToEndOfRecord(mrStream)) + mrStream.SetError(SVSTREAM_FILEFORMAT_ERROR); +} + +Atom::~Atom() +{ + Atom* pChild = mpFirstChild; + while( pChild ) + { + Atom* pNextChild = pChild->mpNextAtom; + delete pChild; + pChild = pNextChild; + } +} + +/** imports this atom and its child atoms */ +Atom* Atom::import( const DffRecordHeader& rRootRecordHeader, SvStream& rStCtrl ) +{ + Atom* pRootAtom = new Atom( rRootRecordHeader, rStCtrl ); + + if( rStCtrl.GetError() == ERRCODE_NONE ) + { + return pRootAtom; + } + else + { + delete pRootAtom; + return nullptr; + } +} + +/** returns the next child atom after pLast with nRecType or NULL */ +const Atom* Atom::findNextChildAtom( sal_uInt16 nRecType, const Atom* pLast ) const +{ + Atom* pChild = pLast != nullptr ? pLast->mpNextAtom : mpFirstChild; + while( pChild && pChild->maRecordHeader.nRecType != nRecType ) + { + pChild = pChild->mpNextAtom; + } + + return pChild; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptatom.hxx b/sd/source/filter/ppt/pptatom.hxx new file mode 100644 index 000000000..55ee7f687 --- /dev/null +++ b/sd/source/filter/ppt/pptatom.hxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace ppt +{ +class Atom +{ +public: + ~Atom(); + + /** imports this atom and its child atoms */ + static Atom* import(const DffRecordHeader& rRootRecordHeader, SvStream& rStCtrl); + + /** @return true if at least one atom with the given nRecType is found */ + inline bool hasChildAtom(sal_uInt16 nRecType) const; + + /** @return the first child atom with nRecType or NULL */ + inline const Atom* findFirstChildAtom(sal_uInt16 nRecType) const; + + /** @return the next child atom after pLast with nRecType or NULL */ + const Atom* findNextChildAtom(sal_uInt16 nRecType, const Atom* pLast) const; + + /** @return the first child atom or NULL */ + inline const Atom* findFirstChildAtom() const; + + /** @return the next child atom after pLast or NULL */ + static inline const Atom* findNextChildAtom(const Atom* pLast); + + /** @return true if this atom is a container */ + inline bool isContainer() const; + + /** seeks to the contents of this atom */ + inline bool seekToContent() const; + + /** @return the record type */ + inline sal_uInt16 getType() const; + + /** @return the record instance */ + inline sal_uInt16 getInstance() const; + + /** @return the record length */ + inline sal_uInt32 getLength() const; + +private: + Atom(const DffRecordHeader& rRecordHeader, SvStream& rStCtrl); + + SvStream& mrStream; + DffRecordHeader maRecordHeader; + Atom* mpFirstChild; + Atom* mpNextAtom; +}; + +inline bool Atom::hasChildAtom(sal_uInt16 nRecType) const +{ + return findFirstChildAtom(nRecType) != nullptr; +} + +inline const Atom* Atom::findFirstChildAtom(sal_uInt16 nRecType) const +{ + return findNextChildAtom(nRecType, nullptr); +} + +inline const Atom* Atom::findFirstChildAtom() const { return mpFirstChild; } + +inline const Atom* Atom::findNextChildAtom(const Atom* pLast) +{ + return pLast ? pLast->mpNextAtom : pLast; +} + +inline bool Atom::isContainer() const { return maRecordHeader.IsContainer(); } + +inline bool Atom::seekToContent() const +{ + maRecordHeader.SeekToContent(mrStream); + return mrStream.GetError() == ERRCODE_NONE; +} + +inline sal_uInt16 Atom::getType() const { return maRecordHeader.nRecType; } + +inline sal_uInt16 Atom::getInstance() const { return maRecordHeader.nRecInstance; } + +inline sal_uInt32 Atom::getLength() const { return maRecordHeader.nRecLen; } + +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptin.cxx b/sd/source/filter/ppt/pptin.cxx new file mode 100644 index 000000000..305e1813a --- /dev/null +++ b/sd/source/filter/ppt/pptin.cxx @@ -0,0 +1,2821 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include "pptin.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "propread.hxx" +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#define MAX_USER_MOVE 2 + +#include "pptanimations.hxx" +#include "pptinanimations.hxx" +#include "ppt97animations.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace ::com::sun::star; + +SdPPTImport::SdPPTImport( SdDrawDocument* pDocument, SvStream& rDocStream, SotStorage& rStorage, SfxMedium& rMedium ) + : maParam(rDocStream) +{ +#ifdef DBG_UTIL + std::unique_ptr pSummaryInformation(new PropRead( rStorage, "\005SummaryInformation" )); + if ( pSummaryInformation->IsValid() ) + { + pSummaryInformation->Read(); + sal_uInt8 const aPropSetGUID[ 16 ] + { + 0xe0, 0x85, 0x9f, 0xf2, 0xf9, 0x4f, 0x68, 0x10, 0xab, 0x91, 0x08, 0x00, 0x2b, 0x27, 0xb3, 0xd9 + }; + Section* pSection = const_cast(pSummaryInformation->GetSection( aPropSetGUID )); + if ( pSection ) + { + PropItem aPropItem; + if ( pSection->GetProperty( PID_COMMENTS, aPropItem ) ) + { + OUString aComment; + aPropItem.Read( aComment ); + if ( aComment.indexOf( "Applixware" ) >= 0 ) + { + maParam.nImportFlags |= PPT_IMPORTFLAGS_NO_TEXT_ASSERT; + } + } + } + } + pSummaryInformation.reset(); +#endif + + tools::SvRef pCurrentUserStream(rStorage.OpenSotStream("Current User", StreamMode::STD_READ)); + if (pCurrentUserStream) + { + ReadPptCurrentUserAtom(*pCurrentUserStream, maParam.aCurrentUserAtom); + } + + if( pDocument ) + { + // iterate over all styles + SdStyleSheetPool* pStyleSheetPool = pDocument->GetSdStyleSheetPool(); + std::shared_ptr aIter = + std::make_shared(pStyleSheetPool, SfxStyleFamily::All); + + for (SfxStyleSheetBase *pSheet = aIter->First(); pSheet; pSheet = aIter->Next()) + { + SfxItemSet& rSet = pSheet->GetItemSet(); + // if autokerning is set in style, override it, ppt has no autokerning + if( rSet.GetItemState( EE_CHAR_PAIRKERNING, false ) == SfxItemState::SET ) + rSet.ClearItem( EE_CHAR_PAIRKERNING ); + } + } + + pFilter.reset(new ImplSdPPTImport(pDocument, rStorage, rMedium, maParam)); +} + +bool SdPPTImport::Import() +{ + return pFilter->Import(); +} + +SdPPTImport::~SdPPTImport() +{ +} + +ImplSdPPTImport::ImplSdPPTImport( SdDrawDocument* pDocument, SotStorage& rStorage_, SfxMedium& rMedium, PowerPointImportParam& rParam ) + : SdrPowerPointImport(rParam, rMedium.GetBaseURL()) + , mrMed(rMedium) + , mrStorage(rStorage_) + , mbDocumentFound(false) + , mnFilterOptions(0) + , mpDoc(pDocument) + , mePresChange(PresChange::Manual) + , mnBackgroundObjectsLayerID(0) +{ + if ( !m_bOk ) + return; + + mbDocumentFound = SeekToDocument( &maDocHd ); // maDocHd = the latest DocumentHeader + while ( SeekToRec( rStCtrl, PPT_PST_Document, nStreamLen, &maDocHd ) ) + mbDocumentFound = true; + + sal_uInt32 nDggContainerOfs = 0; + + if ( mbDocumentFound ) + { + sal_uInt64 nOldPos = rStCtrl.Tell(); + + mxPicturesStream = rStorage_.OpenSotStream( "Pictures", StreamMode::STD_READ ); + pStData = mxPicturesStream.get(); + + rStCtrl.Seek( maDocHd.GetRecBegFilePos() + 8 ); + sal_uLong nDocLen = maDocHd.GetRecEndFilePos(); + DffRecordHeader aPPDGHd; + if ( SeekToRec( rStCtrl, PPT_PST_PPDrawingGroup, nDocLen, &aPPDGHd ) ) + { + sal_uLong nPPDGLen = aPPDGHd.GetRecEndFilePos(); + if ( SeekToRec( rStCtrl, DFF_msofbtDggContainer, nPPDGLen ) ) + nDggContainerOfs = rStCtrl.Tell(); + } + rStCtrl.Seek( nOldPos ); + } + sal_uInt32 nSvxMSDffOLEConvFlags2 = 0; + + const SvtFilterOptions& rBasOpt = SvtFilterOptions::Get(); + if ( rBasOpt.IsLoadPPointBasicCode() ) + mnFilterOptions |= 1; + if ( rBasOpt.IsMathType2Math() ) + nSvxMSDffOLEConvFlags2 |= OLE_MATHTYPE_2_STARMATH; + if ( rBasOpt.IsWinWord2Writer() ) + nSvxMSDffOLEConvFlags2 |= OLE_WINWORD_2_STARWRITER; + if ( rBasOpt.IsExcel2Calc() ) + nSvxMSDffOLEConvFlags2 |= OLE_EXCEL_2_STARCALC; + if ( rBasOpt.IsPowerPoint2Impress() ) + nSvxMSDffOLEConvFlags2 |= OLE_POWERPOINT_2_STARIMPRESS; + + InitSvxMSDffManager( nDggContainerOfs, pStData, nSvxMSDffOLEConvFlags2 ); + SetSvxMSDffSettings( SVXMSDFF_SETTINGS_CROP_BITMAPS + | SVXMSDFF_SETTINGS_IMPORT_PPT ); + SetModel( mpDoc, 576 ); +} + +// Dtor +ImplSdPPTImport::~ImplSdPPTImport() +{ + pStData = nullptr; + mxPicturesStream.clear(); +} + +// Import +bool ImplSdPPTImport::Import() +{ + if ( !m_bOk ) + return false; + + bool bWasLocked = pSdrModel->isLocked(); + pSdrModel->setLock(true); + const bool bSavedUndoEnabled = pSdrModel->IsUndoEnabled(); + pSdrModel->EnableUndo(false); + + SdrOutliner& rOutl = mpDoc->GetDrawOutliner(); + EEControlBits nControlWord = rOutl.GetEditEngine().GetControlWord(); + nControlWord |= EEControlBits::ULSPACESUMMATION; + const_cast(rOutl.GetEditEngine()).SetControlWord( nControlWord ); + + SdrLayerAdmin& rAdmin = mpDoc->GetLayerAdmin(); + mnBackgroundObjectsLayerID = rAdmin.GetLayerID( sUNO_LayerName_background_objects ); + + ::sd::DrawDocShell* pDocShell = mpDoc->GetDocSh(); + if ( pDocShell ) + SeekOle( pDocShell, mnFilterOptions ); + + // hyperlinks + std::unique_ptr pDInfoSec2(new PropRead( mrStorage, "\005DocumentSummaryInformation" )); + if ( pDInfoSec2->IsValid() ) + { + PropItem aPropItem; + + sal_uInt32 nType(0), nPropCount(0); + + pDInfoSec2->Read(); + + sal_uInt8 const aPropSetGUID[ 16 ] + { + 0x02, 0xd5, 0xcd, 0xd5, 0x9c, 0x2e, 0x1b, 0x10, 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae + }; + Section* pSection = const_cast(pDInfoSec2->GetSection( aPropSetGUID )); + if ( pSection ) + { + if ( pSection->GetProperty( PID_SLIDECOUNT, aPropItem ) ) + { + aPropItem.ReadUInt32( nType ); + if ( ( nType == VT_I4 ) || ( nType == VT_UI4 ) ) + { + // examine PID_HEADINGPAIR to get the correct entry for PID_DOCPARTS + sal_uInt32 nSlideCount(0), nVecCount(0); + aPropItem.ReadUInt32( nSlideCount ); + if ( nSlideCount && pSection->GetProperty( PID_HEADINGPAIR, aPropItem ) ) + { + sal_uInt32 nSlideTitleIndex = 0, nSlideTitleCount = 0; + + OUString aUString; + + aPropItem.ReadUInt32( nType ) + .ReadUInt32( nVecCount ); + + if ( ( nType == ( VT_VARIANT | VT_VECTOR ) ) && ( nVecCount ^ 1 ) ) + { + nVecCount >>= 1; + sal_uInt32 nEntryCount = 0; + for (sal_uInt32 i = 0; i < nVecCount; ++i) + { + if ( !aPropItem.Read( aUString, VT_EMPTY, false ) ) + break; + aPropItem.ReadUInt32( nType ); + if ( ( nType != VT_I4 ) && ( nType != VT_UI4 ) ) + break; + sal_uInt32 nTemp(0); + aPropItem.ReadUInt32( nTemp ); + if ( aUString == "Slide Titles" || aUString == "Folientitel" ) + { + nSlideTitleCount = nTemp; + nSlideTitleIndex = nEntryCount; + } + nEntryCount += nTemp; + } + } + if ( ( nSlideCount == nSlideTitleCount ) && pSection->GetProperty( PID_DOCPARTS, aPropItem ) ) + { + aPropItem.ReadUInt32( nType ) + .ReadUInt32( nVecCount ); + + bool bVecOk = ( ( nVecCount >= (nSlideTitleIndex + nSlideTitleCount) ) + && ( nType == ( VT_LPSTR | VT_VECTOR ) ) ); + + if (bVecOk) + { + for (sal_uInt32 i = 0; i != nSlideTitleIndex; ++i) + { + sal_uInt32 nTemp(0); + aPropItem.ReadUInt32(nTemp); + if (!aPropItem.good()) + { + bVecOk = false; + break; + } + auto nPos = aPropItem.Tell() + nTemp; + if (!checkSeek(aPropItem, nPos)) + { + bVecOk = false; + break; + } + } + } + if (bVecOk) + { + for (sal_uInt32 i = 0; i < nSlideTitleCount; ++i) + { + if (!aPropItem.Read(aUString, nType, false)) + break; + + OUString aString( aUString ); + if ( aString == "No Slide Title" ) + aString.clear(); + else + { + std::vector::const_iterator pIter = + std::find(maSlideNameList.begin(),maSlideNameList.end(),aString); + + if (pIter != maSlideNameList.end()) + aString.clear(); + } + maSlideNameList.push_back( aString ); + } + } + } + } + } + } + + sal_uInt8 const aUserPropSetGUID[ 16 ] + { + 0x05, 0xd5, 0xcd, 0xd5, 0x9c, 0x2e, 0x1b, 0x10, 0x93, 0x97, 0x08, 0x00, 0x2b, 0x2c, 0xf9, 0xae + }; + pSection = const_cast(pDInfoSec2->GetSection( aUserPropSetGUID )); + if ( pSection ) + { + PropDictionary aDict; + pSection->GetDictionary(aDict); + if (!aDict.empty()) + { + auto iter = aDict.find( OUString("_PID_HLINKS") ); + + if ( iter != aDict.end() ) + { + if ( pSection->GetProperty( iter->second, aPropItem ) ) + { + aPropItem.Seek( STREAM_SEEK_TO_BEGIN ); + aPropItem.ReadUInt32( nType ); + if ( nType == VT_BLOB ) + { + sal_uInt32 nPropSize; + aPropItem.ReadUInt32( nPropSize ) + .ReadUInt32( nPropCount ); + + if ( ! ( nPropCount % 6 ) ) + { + sal_uInt32 i; + + nPropCount /= 6; // 6 properties per hyperlink + + for ( i = 0; i < nPropCount; i++ ) + { + SdHyperlinkEntry aHyperlink; + aHyperlink.nIndex = 0; + aPropItem.ReadUInt32( nType ); + if ( nType != VT_I4 ) + break; + aPropItem.ReadInt32( aHyperlink.nPrivate1 ) + .ReadUInt32( nType ); + if ( nType != VT_I4 ) + break; + aPropItem.ReadInt32( aHyperlink.nPrivate2 ) + .ReadUInt32( nType ); + if ( nType != VT_I4 ) + break; + aPropItem.ReadInt32( aHyperlink.nPrivate3 ) + .ReadUInt32( nType ); + if ( nType != VT_I4 ) + break; + aPropItem.ReadInt32( aHyperlink.nInfo ); + if ( !aPropItem.Read( aHyperlink.aTarget ) ) + break; + + // Convert '\\' notation to 'smb://' + INetURLObject aUrl( aHyperlink.aTarget, INetProtocol::File ); + aHyperlink.aTarget = aUrl.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if ( !aPropItem.Read( aHyperlink.aSubAddress ) ) + break; + + if ( !aHyperlink.aSubAddress.isEmpty() ) // get the converted subaddress + { + sal_uInt32 nPageNumber = 0; + OUString aString( aHyperlink.aSubAddress ); + OString aStringAry[ 3 ]; + size_t nTokenCount = 0; + sal_Int32 nPos = 0; + do + { + aStringAry[nTokenCount] = + OUStringToOString(o3tl::getToken(aString, 0, ',', nPos ), RTL_TEXTENCODING_UTF8); + } + while ( ++nTokenCount < SAL_N_ELEMENTS(aStringAry) && nPos >= 0 ); + + bool bDocInternalSubAddress = false; + + // first pass, searching for a SlideId + for( size_t nToken = 0; nToken < nTokenCount; ++nToken ) + { + if (comphelper::string::isdigitAsciiString(aStringAry[nToken])) + { + sal_Int32 nNumber = aStringAry[ nToken ].toInt32(); + if ( nNumber & ~0xff ) + { + PptSlidePersistList* pPageList = GetPageList( PPT_SLIDEPAGE ); + if ( pPageList ) + { + sal_uInt16 nPage = pPageList->FindPage( nNumber ); + if ( nPage != PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + { + nPageNumber = nPage; + bDocInternalSubAddress = true; + break; + } + } + } + } + } + if ( !bDocInternalSubAddress ) + { // second pass, searching for a SlideName + for ( size_t nToken = 0; nToken < nTokenCount; ++nToken ) + { + OUString aToken(OStringToOUString(aStringAry[nToken], RTL_TEXTENCODING_UTF8)); + std::vector::const_iterator pIter = + std::find(maSlideNameList.begin(),maSlideNameList.end(),aToken); + + if (pIter != maSlideNameList.end()) + { + nPageNumber = pIter - maSlideNameList.begin(); + bDocInternalSubAddress = true; + } + } + } + if ( !bDocInternalSubAddress ) + { // third pass, searching for a slide number + for ( size_t nToken = 0; nToken < nTokenCount; ++nToken ) + { + if (comphelper::string::isdigitAsciiString(aStringAry[nToken])) + { + sal_Int32 nNumber = aStringAry[ nToken ].toInt32(); + if ( ( nNumber & ~0xff ) == 0 ) + { + nPageNumber = static_cast(nNumber) - 1; + bDocInternalSubAddress = true; + break; + } + } + } + } + // if a document internal sub address + if ( bDocInternalSubAddress ) + { + if ( nPageNumber < maSlideNameList.size() ) + aHyperlink.aConvSubString = maSlideNameList[ nPageNumber ]; + if ( aHyperlink.aConvSubString.isEmpty() ) + { + aHyperlink.aConvSubString = SdResId( STR_PAGE ) + " " + mpDoc->CreatePageNumValue( static_cast(nPageNumber) + 1 ); + } + } else { + // if sub address is given but not internal, use it as it is + if ( aHyperlink.aConvSubString.isEmpty() ) + { + aHyperlink.aConvSubString = aString; + } + } + } + m_aHyperList.push_back( aHyperlink ); + } + } + } + } + } + } + } + } + } + pDInfoSec2.reset(); + + if ( mbDocumentFound ) + { + rStCtrl.Seek( maDocHd.GetRecBegFilePos() + 8 ); + // read hyperlist / set indices of the entries + DffRecordHeader aHyperHd; + if ( SeekToRec( rStCtrl, PPT_PST_ExObjList, maDocHd.GetRecEndFilePos(), &aHyperHd ) ) + { + sal_uInt32 nExObjHyperListLen = aHyperHd.GetRecEndFilePos(); + for (SdHyperlinkEntry & entry : m_aHyperList) + { + DffRecordHeader aHyperE; + if ( !SeekToRec( rStCtrl, PPT_PST_ExHyperlink, nExObjHyperListLen, &aHyperE ) ) + break; + if ( !SeekToRec( rStCtrl, PPT_PST_ExHyperlinkAtom, nExObjHyperListLen ) ) + break; + rStCtrl.SeekRel( 8 ); + rStCtrl.ReadUInt32( entry.nIndex ); + if (!aHyperE.SeekToEndOfRecord(rStCtrl)) + break; + } + + if (m_aHyperList.size() == 0) + { + while(true) + { + + DffRecordHeader aHyperE; + if (!SeekToRec(rStCtrl, PPT_PST_ExHyperlink, nExObjHyperListLen, &aHyperE)) + break; + if (!SeekToRec(rStCtrl, PPT_PST_ExHyperlinkAtom, nExObjHyperListLen)) + continue; + + SdHyperlinkEntry aHyperlink; + + OUString aURLText; + OUString aURLLink; + rStCtrl.SeekRel(8); + rStCtrl.ReadUInt32(aHyperlink.nIndex); + + ReadString(aURLText); + ReadString(aURLLink); + aHyperlink.aTarget = aURLLink; + m_aHyperList.push_back(aHyperlink); + } + } + } + } + + if (pDocShell) + { + Size aVisAreaSize; + switch ( m_aUserEditAtom.eLastViewType ) + { + case PptViewTypeEnum::Notes: + case PptViewTypeEnum::NotesMaster: + aVisAreaSize = aDocAtom.GetNotesPageSize(); + break; + default : + aVisAreaSize = aDocAtom.GetSlidesPageSize(); + } + Scale( aVisAreaSize ); + pDocShell->SetVisArea( ::tools::Rectangle( Point(), aVisAreaSize ) ); + } + + // create master pages: + + std::unique_ptr xStbMgr; + if (!utl::ConfigManager::IsFuzzing()) + { + xStbMgr.reset(new SfxProgress(pDocShell, + SdResId( STR_POWERPOINT_IMPORT), + m_pMasterPages->size() + + m_pSlidePages->size() + m_pNotePages->size())); + } + + sal_uInt32 nImportedPages = 0; + { + sal_uInt16 nMasterCnt = GetPageCount( PPT_MASTERPAGE ); + + for ( sal_uInt16 nMasterNum = 0; nMasterNum < nMasterCnt; nMasterNum++ ) + { + SetPageNum( nMasterNum, PPT_MASTERPAGE ); + rtl::Reference pPage = static_cast(MakeBlankPage( true ).get()); + if ( pPage ) + { + bool bNotesMaster = (*GetPageList( m_eCurrentPageKind ) )[ m_nCurrentPageNum ].bNotesMaster; + bool bStarDrawFiller = (*GetPageList( m_eCurrentPageKind ) )[ m_nCurrentPageNum ].bStarDrawFiller; + + PageKind ePgKind = bNotesMaster ? PageKind::Notes : PageKind::Standard; + bool bHandout = (*GetPageList( m_eCurrentPageKind ) )[ m_nCurrentPageNum ].bHandoutMaster; + if ( bHandout ) + ePgKind = PageKind::Handout; + + pPage->SetPageKind( ePgKind ); + pSdrModel->InsertMasterPage( pPage.get() ); + if ( bNotesMaster && bStarDrawFiller ) + pPage->SetAutoLayout( AUTOLAYOUT_NOTES, true ); + if ( nMasterNum ) + { + std::optional< sal_Int16 > oStartNumbering; + SfxStyleSheet* pSheet; + if ( nMasterNum == 1 ) + { + // standardsheet + pSheet = static_cast(mpDoc->GetStyleSheetPool()->Find(SdResId(STR_STANDARD_STYLESHEET_NAME), SfxStyleFamily::Para )); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, TSS_Type::TextInShape, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, TSS_Type::TextInShape, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast(*this), TSS_Type::Unknown ); + } + } + + // PSEUDO + pSheet = static_cast(mpDoc->GetStyleSheetPool()->Find(SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS), SfxStyleFamily::Pseudo )); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, TSS_Type::TextInShape, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, TSS_Type::TextInShape, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast(*this), TSS_Type::Unknown ); + } + + // create layoutstylesheets, set layoutname and stylesheet + // (only on standard and not pages) + + OUString aLayoutName( SdResId( STR_LAYOUT_DEFAULT_NAME ) ); + if ( nMasterNum > 2 ) + { + if ( ePgKind == PageKind::Standard ) + { // standard page: create new presentation layout + aLayoutName = SdResId( STR_LAYOUT_DEFAULT_TITLE_NAME ) + + OUString::number( static_cast( ( nMasterNum + 1 ) / 2 - 1 ) ); + static_cast( mpDoc->GetStyleSheetPool() )->CreateLayoutStyleSheets( aLayoutName ); + } + else // note page: use presentation layout of standard page + aLayoutName = static_cast( mpDoc->GetMasterPage( nMasterNum - 1 ) )->GetName(); + } + pPage->SetName( aLayoutName ); + aLayoutName += SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + pPage->SetLayoutName( aLayoutName ); + + // set stylesheets + if ( pPage->GetPageKind() == PageKind::Standard ) + { + TSS_Type nTitleInstance = TSS_Type::PageTitle; + TSS_Type nOutlinerInstance = TSS_Type::Body; + const PptSlideLayoutAtom* pSlideLayout = GetSlideLayoutAtom(); + bool bSwapStyleSheet = pSlideLayout->eLayout == PptSlideLayout::TITLEMASTERSLIDE; + if ( bSwapStyleSheet ) + { + nTitleInstance = TSS_Type::Title; + nOutlinerInstance = TSS_Type::Subtitle; + } + + // titlestylesheet + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, nTitleInstance, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, nTitleInstance, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast(*this), TSS_Type::Unknown ); + } + + // outlinerstylesheet + sal_uInt16 nLevel; + PPTParagraphObj* pParagraphs[ 9 ]; + + for ( nLevel = 0; nLevel < 9; nLevel++ ) + { + OUString aName = pPage->GetLayoutName() + + " " + OUString::number( nLevel + 1 ); + SfxStyleSheet* pOutlineSheet = static_cast( mpDoc->GetStyleSheetPool()->Find( aName, SfxStyleFamily::Page ) ); + DBG_ASSERT( pOutlineSheet, "Template for outline object not found" ); + if ( pOutlineSheet ) + { + pParagraphs[ nLevel ] = new PPTParagraphObj( *m_pPPTStyleSheet, nOutlinerInstance, nLevel ); + SfxItemSet& rItemSet = pOutlineSheet->GetItemSet(); + PPTPortionObj aPortion( *m_pPPTStyleSheet, nOutlinerInstance, nLevel ); + pParagraphs[ nLevel ]->AppendPortion( aPortion ); + pParagraphs[ nLevel ]->ApplyTo( rItemSet, oStartNumbering, static_cast(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast(*this), TSS_Type::Unknown ); + } + else + pParagraphs[ nLevel ] = nullptr; + } + for ( nLevel = 0; nLevel < 9; delete pParagraphs[ nLevel++ ] ) ; + + // subtitle stylesheet + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Text ); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, TSS_Type::Subtitle, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, TSS_Type::Subtitle, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast(*this), TSS_Type::Unknown ); + } + } + else if ( ePgKind == PageKind::Notes ) + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Notes ); + if ( pSheet ) + { + SfxItemSet& rItemSet = pSheet->GetItemSet(); + PPTParagraphObj aParagraph( *m_pPPTStyleSheet, TSS_Type::Notes, 0 ); + PPTPortionObj aPortion( *m_pPPTStyleSheet, TSS_Type::Notes, 0 ); + aParagraph.AppendPortion( aPortion ); + aParagraph.ApplyTo( rItemSet, oStartNumbering, static_cast(*this), TSS_Type::Unknown ); + aPortion.ApplyTo( rItemSet, static_cast(*this), TSS_Type::Unknown ); + } + } + } + } + } + } + for (sal_uInt16 i = 0; i < mpDoc->GetMasterPageCount(); ++i) + { + SdPage *const pMPage(static_cast(mpDoc->GetMasterPage(i))); + if (pMPage == nullptr) + break; + SetPageNum( i, PPT_MASTERPAGE ); + + // importing master page objects + PptSlidePersistList* pList = GetPageList( m_eCurrentPageKind ); + PptSlidePersistEntry* pPersist = ( pList && ( m_nCurrentPageNum < pList->size() ) ) + ? &(*pList)[ m_nCurrentPageNum ] : nullptr; + if ( pPersist ) + { + if ( pPersist->bStarDrawFiller && pPersist->bNotesMaster && ( m_nCurrentPageNum > 2 ) && ( ( m_nCurrentPageNum & 1 ) == 0 ) ) + { + pSdrModel->DeleteMasterPage( m_nCurrentPageNum ); + SdPage* pMasterPage2 = static_cast(pSdrModel->GetMasterPage( 2 )); + rtl::Reference pNotesClone = static_cast(pMasterPage2->CloneSdrPage(*pSdrModel).get()); + pSdrModel->InsertMasterPage( pNotesClone.get(), m_nCurrentPageNum ); + if ( pNotesClone ) + { + OUString aLayoutName( static_cast(pSdrModel->GetMasterPage( m_nCurrentPageNum - 1 ))->GetLayoutName() ); + pNotesClone->SetPresentationLayout( aLayoutName, false, false ); + pNotesClone->SetLayoutName( aLayoutName ); + } + } + else if ( !pPersist->bStarDrawFiller ) + { + PptSlidePersistEntry* pE = pPersist; + while( ( pE->aSlideAtom.nFlags & 4 ) && pE->aSlideAtom.nMasterId ) + { + auto nOrigMasterId = pE->aSlideAtom.nMasterId; + sal_uInt16 nNextMaster = m_pMasterPages->FindPage(nOrigMasterId); + if ( nNextMaster == PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + break; + else + pE = &(*pList)[ nNextMaster ]; + if (pE->aSlideAtom.nMasterId == nOrigMasterId) + { + SAL_WARN("filter.ms", "loop in atom chain"); + break; + } + } + SdrObject* pObj = ImportPageBackgroundObject( *pMPage, pE->nBackgroundOffset ); // import background + if ( pObj ) + pMPage->NbcInsertObject( pObj ); + + bool bNewAnimationsUsed = false; + ProcessData aProcessData( (*pList)[ m_nCurrentPageNum ], SdPageCapsule(pMPage) ); + sal_uInt32 nOldFPos = rStCtrl.Tell(); + DffRecordHeader aPageHd; + if ( SeekToCurrentPage( &aPageHd ) ) + { + auto nEndRecPos = SanitizeEndPos(rStCtrl, aPageHd.GetRecEndFilePos()); + while( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nEndRecPos ) ) + { + DffRecordHeader aHd; + if (!ReadDffRecordHeader( rStCtrl, aHd )) + break; + switch( aHd.nRecType ) + { + case PPT_PST_PPDrawing : + { + aHd.SeekToBegOfRecord( rStCtrl ); + DffRecordHeader aPPDrawHd; + if ( SeekToRec( rStCtrl, PPT_PST_PPDrawing, aHd.GetRecEndFilePos(), &aPPDrawHd ) ) + { + sal_uInt32 nPPDrawEnd = aPPDrawHd.GetRecEndFilePos(); + DffRecordHeader aEscherF002Hd; + if ( SeekToRec( rStCtrl, DFF_msofbtDgContainer, nPPDrawEnd, &aEscherF002Hd ) ) + { + sal_uInt32 nEscherF002End = aEscherF002Hd.GetRecEndFilePos(); + DffRecordHeader aEscherObjListHd; + if ( SeekToRec( rStCtrl, DFF_msofbtSpgrContainer, nEscherF002End, &aEscherObjListHd ) ) + { + sal_uInt32 nObjCount = 0; + auto nListEndRecPos = SanitizeEndPos(rStCtrl, aEscherObjListHd.GetRecEndFilePos()); + while( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nListEndRecPos ) ) + { + DffRecordHeader aHd2; + ReadDffRecordHeader( rStCtrl, aHd2 ); + if ( ( aHd2.nRecType == DFF_msofbtSpContainer ) || ( aHd2.nRecType == DFF_msofbtSpgrContainer ) ) + { + if ( nObjCount++ ) // skipping the first object + { + ::tools::Rectangle aEmpty; + if (!aHd2.SeekToBegOfRecord(rStCtrl)) + break; + SdrObject* pImpObj = ImportObj( rStCtrl, aProcessData, aEmpty, aEmpty, /*nCalledByGroup*/0, /*pShapeId*/ nullptr ); + if ( pImpObj ) + { + pImpObj->SetLayer( mnBackgroundObjectsLayerID ); + pMPage->NbcInsertObject( pImpObj ); + } + } + } + if (!aHd2.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + } + } + break; + + case PPT_PST_ProgTags : + { + DffRecordHeader aProgTagHd; + if ( SeekToContentOfProgTag( 10, rStCtrl, aPageHd, aProgTagHd ) ) + { + auto nTagEndRecPos = SanitizeEndPos(rStCtrl, aProgTagHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nTagEndRecPos ) ) + { + DffRecordHeader aProgTagContentHd; + ReadDffRecordHeader( rStCtrl, aProgTagContentHd ); + switch( aProgTagContentHd.nRecType ) + { + case DFF_msofbtAnimGroup : + { + css::uno::Reference< css::drawing::XDrawPage > xPage( pMPage->getUnoPage(), css::uno::UNO_QUERY ); + ppt::AnimationImporter aImporter( this, rStCtrl ); + bNewAnimationsUsed = aImporter.import( xPage, aProgTagContentHd ) > 0; + } + break; + } + if (!aProgTagContentHd.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + break; + } + bool bSuccess = aHd.SeekToEndOfRecord(rStCtrl); + if (!bSuccess) + { + SAL_WARN("filter.ms", "Could not seek to end of record"); + break; + } + } + } + rStCtrl.Seek( nOldFPos ); + ImportPageEffect( pMPage, bNewAnimationsUsed ); + + // background object + pObj = pMPage->GetObj( 0 ); + if ( pObj && pObj->GetObjIdentifier() == SdrObjKind::Rectangle ) + { + if ( pMPage->GetPageKind() == PageKind::Standard ) + { + // transform data from imported background object to new form + // and delete the object. It was used as container to transport + // the attributes of the MasterPage background fill + SfxStyleSheet* pSheet = pMPage->GetStyleSheetForMasterPageBackground(); + + if(pSheet) + { + // if we have a StyleSheet (for Masterpages), set attributes there and use it + pSheet->GetItemSet().ClearItem(); + pSheet->GetItemSet().Put(pObj->GetMergedItemSet()); + pMPage->getSdrPageProperties().ClearItem(); + pMPage->getSdrPageProperties().SetStyleSheet(pSheet); + } + else + { + // without StyleSheet, set attributes directly. This + // should not be done at all and is an error (will be asserted by SdrPage) + pMPage->getSdrPageProperties().ClearItem(); + pMPage->getSdrPageProperties().PutItemSet(pObj->GetMergedItemSet()); + } + + pMPage->RemoveObject(pObj->GetOrdNum()); + SdrObject::Free(pObj); + } + } + } + } + if (xStbMgr) + xStbMgr->SetState( nImportedPages++ ); + } + + // importing slide pages + { + sal_uInt32 nOldFPos = rStCtrl.Tell(); + PptPageKind ePageKind = m_eCurrentPageKind; + sal_uInt16 nPageNum = m_nCurrentPageNum; + + rtl::Reference pHandoutPage = static_cast(MakeBlankPage( false ).get()); + pHandoutPage->SetPageKind( PageKind::Handout ); + pSdrModel->InsertPage( pHandoutPage.get() ); + + sal_uInt16 nPageCnt = GetPageCount(); + if ( nPageCnt ) + { + for ( sal_uInt16 nPage = 0; nPage < nPageCnt; nPage++ ) + { + mePresChange = PresChange::SemiAuto; + SetPageNum( nPage ); + rtl::Reference pPage = static_cast(MakeBlankPage( false ).get()); + PptSlidePersistEntry* pMasterPersist = nullptr; + if ( HasMasterPage( nPage ) ) // try to get the LayoutName from the masterpage + { + sal_uInt16 nMasterNum = GetMasterPageIndex( m_nCurrentPageNum, m_eCurrentPageKind ); + pPage->TRG_SetMasterPage(*pSdrModel->GetMasterPage(nMasterNum)); + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + if ( pPageList && nMasterNum < pPageList->size() ) + pMasterPersist = &(*pPageList)[ nMasterNum ]; + pPage->SetLayoutName(static_cast(pPage->TRG_GetMasterPage()).GetLayoutName()); + } + pPage->SetPageKind( PageKind::Standard ); + pSdrModel->InsertPage( pPage.get() ); // SJ: #i29625# because of form controls, the + ImportPage( pPage.get(), pMasterPersist ); // page must be inserted before importing + SetHeaderFooterPageSettings( pPage.get(), pMasterPersist ); + // CWS preseng01: pPage->SetPageKind( PageKind::Standard ); + + DffRecordHeader aPageHd; + if ( SeekToCurrentPage( &aPageHd ) ) + { + bool bNewAnimationsUsed = false; + + aPageHd.SeekToContent( rStCtrl ); + auto nEndRecPos = SanitizeEndPos(rStCtrl, aPageHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nEndRecPos ) ) + { + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + switch ( aHd.nRecType ) + { + case PPT_PST_ProgTags : + { + DffRecordHeader aProgTagHd; + if ( SeekToContentOfProgTag( 10, rStCtrl, aPageHd, aProgTagHd ) ) + { + auto nHdEndRecPos = SanitizeEndPos(rStCtrl, aProgTagHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nHdEndRecPos ) ) + { + DffRecordHeader aProgTagContentHd; + ReadDffRecordHeader( rStCtrl, aProgTagContentHd ); + switch( aProgTagContentHd.nRecType ) + { + case DFF_msofbtAnimGroup : + { + css::uno::Reference< css::drawing::XDrawPage > xPage( pPage->getUnoPage(), css::uno::UNO_QUERY ); + ppt::AnimationImporter aImporter( this, rStCtrl ); + bNewAnimationsUsed = aImporter.import( xPage, aProgTagContentHd ) > 0; + } + break; + + case PPT_PST_HashCodeAtom : // ??? + break; + + case PPT_PST_SlideTime10Atom : // ??? don't know, this atom is always 8 bytes big + break; // and is appearing in nearly every l10 progtag + } + if (!aProgTagContentHd.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + break; + + case PPT_PST_HeadersFooters : + case PPT_PST_PPDrawing : + default: + break; + } + + if (!aHd.SeekToEndOfRecord(rStCtrl)) + break; + } + ImportPageEffect( pPage.get(), bNewAnimationsUsed ); + } + + // creating the corresponding note page + m_eCurrentPageKind = PPT_NOTEPAGE; + rtl::Reference pNotesPage = static_cast(MakeBlankPage( false ).get()); + sal_uInt16 nNotesMasterNum = GetMasterPageIndex( nPage ) + 1; + sal_uInt32 nNotesPageId = GetNotesPageId( nPage ); + if ( nNotesPageId ) + { + nImportedPages++; + sal_uInt16 nNotesPageIndex = m_pNotePages->FindPage( nNotesPageId ); + if ( nNotesPageIndex == PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + nNotesPageIndex = 0; + SetPageNum( nNotesPageIndex, PPT_NOTEPAGE ); + PptSlidePersistEntry* pMasterPersist2 = nullptr; + if ( HasMasterPage( nNotesPageIndex, PPT_NOTEPAGE ) ) // try to get the LayoutName from the masterpage + { + pNotesPage->TRG_SetMasterPage(*pSdrModel->GetMasterPage(nNotesMasterNum)); + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + if ( pPageList && nNotesMasterNum < pPageList->size() ) + pMasterPersist2 = &(*pPageList)[ nNotesMasterNum ]; + pNotesPage->SetLayoutName( static_cast(pNotesPage->TRG_GetMasterPage()).GetLayoutName() ); + } + pNotesPage->SetPageKind( PageKind::Notes ); + pNotesPage->TRG_SetMasterPage(*pSdrModel->GetMasterPage(nNotesMasterNum)); + pSdrModel->InsertPage( pNotesPage.get() ); // SJ: #i29625# because of form controls, the + ImportPage( pNotesPage.get(), pMasterPersist2 ); // page must be inserted before importing + SetHeaderFooterPageSettings( pNotesPage.get(), pMasterPersist2 ); + pNotesPage->SetAutoLayout( AUTOLAYOUT_NOTES ); + } + else + { + pNotesPage->SetPageKind( PageKind::Notes ); + pNotesPage->TRG_SetMasterPage(*pSdrModel->GetMasterPage(nNotesMasterNum)); + pNotesPage->SetAutoLayout( AUTOLAYOUT_NOTES, true ); + pSdrModel->InsertPage( pNotesPage.get() ); + SdrObject* pPageObj = pNotesPage->GetPresObj( PresObjKind::Page ); + if ( pPageObj ) + static_cast(pPageObj)->SetReferencedPage(pSdrModel->GetPage(( nPage << 1 ) + 1)); + } + + if (xStbMgr) + xStbMgr->SetState( nImportedPages++ ); + } + } + else + { + // that can happen by document templates + m_eCurrentPageKind = PPT_SLIDEPAGE; + rtl::Reference pPage = static_cast(MakeBlankPage( false ).get()); + pSdrModel->InsertPage( pPage.get() ); + + // #i37397#, trying to set the title master for the first page + sal_uInt16 nMaster, nMasterCount = pSdrModel->GetMasterPageCount(); + SdPage* pFoundMaster = nullptr; + for ( nMaster = 1; nMaster < nMasterCount; nMaster++ ) + { + SdPage* pMaster = static_cast( pSdrModel->GetMasterPage( nMaster ) ); + if ( pMaster->GetPageKind() == PageKind::Standard ) + { + SetPageNum( nMaster, PPT_MASTERPAGE ); + if ( !pFoundMaster ) + pFoundMaster = pMaster; + else if ( GetSlideLayoutAtom()->eLayout == PptSlideLayout::TITLEMASTERSLIDE ) + pFoundMaster = pMaster; + if ( GetSlideLayoutAtom()->eLayout == PptSlideLayout::TITLEMASTERSLIDE ) + break; + } + } + if ( pFoundMaster ) + { + pPage->TRG_SetMasterPage( *pFoundMaster ); + pPage->SetLayoutName( pFoundMaster->GetLayoutName() ); + } + pPage->SetAutoLayout( AUTOLAYOUT_TITLE, true, true ); + + m_eCurrentPageKind = PPT_NOTEPAGE; + rtl::Reference pNPage = MakeBlankPage( false ); + pSdrModel->InsertPage( pNPage.get() ); + } + SetPageNum( nPageNum, ePageKind ); + rStCtrl.Seek( nOldFPos ); + } + + // create handout and note pages + m_bOk = mpDoc->CreateMissingNotesAndHandoutPages(); + if ( m_bOk ) + { + for ( sal_uInt16 i = 0; i < mpDoc->GetSdPageCount( PageKind::Standard ); i++ ) + { + + // set AutoLayout + SetPageNum( i ); + SdPage* pPage = mpDoc->GetSdPage( i, PageKind::Standard ); + AutoLayout eAutoLayout = AUTOLAYOUT_NONE; + const PptSlideLayoutAtom* pSlideLayout = GetSlideLayoutAtom(); + if ( pSlideLayout ) + { + switch ( pSlideLayout->eLayout ) // presentation layout for standard pages + { + case PptSlideLayout::TITLEANDBODYSLIDE : + { + eAutoLayout = AUTOLAYOUT_TITLE_CONTENT; + PptPlaceholder nID1 = pSlideLayout->aPlaceholderId[ 1 ]; + switch ( nID1 ) + { + case PptPlaceholder::BODY : + eAutoLayout = AUTOLAYOUT_TITLE_CONTENT; + break; + case PptPlaceholder::TABLE : + eAutoLayout = AUTOLAYOUT_TAB; + break; + case PptPlaceholder::ORGANISZATIONCHART : + eAutoLayout = AUTOLAYOUT_ORG; + break; + case PptPlaceholder::GRAPH : + eAutoLayout = AUTOLAYOUT_CHART; + break; + case PptPlaceholder::OBJECT : + eAutoLayout = AUTOLAYOUT_OBJ; + break; + case PptPlaceholder::VERTICALTEXTBODY : + eAutoLayout = AUTOLAYOUT_TITLE_VCONTENT; + break; + default: break; + } + } + break; + + case PptSlideLayout::TWOCOLUMNSANDTITLE : + { + eAutoLayout = AUTOLAYOUT_TITLE_2CONTENT; + PptPlaceholder nID1 = pSlideLayout->aPlaceholderId[ 1 ]; + PptPlaceholder nID2 = pSlideLayout->aPlaceholderId[ 2 ]; + if ( nID1 == PptPlaceholder::BODY && nID2 == PptPlaceholder::GRAPH ) + eAutoLayout = AUTOLAYOUT_TEXTCHART; + else if ( nID1 == PptPlaceholder::GRAPH && nID2 == PptPlaceholder::BODY ) + eAutoLayout = AUTOLAYOUT_CHARTTEXT; + else if ( nID1 == PptPlaceholder::BODY && nID2 == PptPlaceholder::CLIPART ) + eAutoLayout = AUTOLAYOUT_TEXTCLIP; + else if ( nID1 == PptPlaceholder::CLIPART && nID2 == PptPlaceholder::BODY ) + eAutoLayout = AUTOLAYOUT_CLIPTEXT; + else if ( nID1 == PptPlaceholder::CLIPART && nID2 == PptPlaceholder::VERTICALTEXTBODY ) + eAutoLayout = AUTOLAYOUT_TITLE_2VTEXT; + else if ( ( nID1 == PptPlaceholder::BODY ) + && ( ( nID2 == PptPlaceholder::OBJECT ) || ( nID2 == PptPlaceholder::MEDIACLIP ) ) ) + eAutoLayout = AUTOLAYOUT_TEXTOBJ; + else if ( ( nID2 == PptPlaceholder::BODY ) + && ( ( nID1 == PptPlaceholder::OBJECT ) || ( nID1 == PptPlaceholder::MEDIACLIP ) ) ) + eAutoLayout = AUTOLAYOUT_OBJTEXT; + else if ( ( nID1 == PptPlaceholder::OBJECT ) && ( nID2 == PptPlaceholder::OBJECT ) ) + eAutoLayout = AUTOLAYOUT_OBJ; + } + break; + + case PptSlideLayout::TWOROWSANDTITLE : + { + eAutoLayout = AUTOLAYOUT_TITLE_2CONTENT; + PptPlaceholder nID1 = pSlideLayout->aPlaceholderId[ 1 ]; + PptPlaceholder nID2 = pSlideLayout->aPlaceholderId[ 2 ]; + if ( nID1 == PptPlaceholder::BODY && nID2 == PptPlaceholder::OBJECT ) + eAutoLayout = AUTOLAYOUT_TEXTOVEROBJ; + else if ( nID1 == PptPlaceholder::OBJECT && nID2 == PptPlaceholder::BODY ) + eAutoLayout = AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT; + } + break; + + case PptSlideLayout::TITLESLIDE : + eAutoLayout = AUTOLAYOUT_TITLE; + break; + case PptSlideLayout::ONLYTITLE : + eAutoLayout = AUTOLAYOUT_TITLE_ONLY; + break; + case PptSlideLayout::RIGHTCOLUMN2ROWS : + eAutoLayout = AUTOLAYOUT_TITLE_CONTENT_2CONTENT; + break; + case PptSlideLayout::LEFTCOLUMN2ROWS : + eAutoLayout = AUTOLAYOUT_TITLE_2CONTENT_CONTENT; + break; + case PptSlideLayout::TOPROW2COLUMN : + eAutoLayout = AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT; + break; + case PptSlideLayout::FOUROBJECTS : + eAutoLayout = AUTOLAYOUT_TITLE_4CONTENT; + break; + case PptSlideLayout::BIGOBJECT : + eAutoLayout = AUTOLAYOUT_OBJ; + break; + case PptSlideLayout::TITLERIGHTBODYLEFT : + eAutoLayout = AUTOLAYOUT_VTITLE_VCONTENT; + break; + case PptSlideLayout::TITLERIGHT2BODIESLEFT : + eAutoLayout = AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT; + break; + + case PptSlideLayout::BOTTOMROW2COLUMNS : + case PptSlideLayout::BLANKSLIDE : + case PptSlideLayout::MASTERSLIDE : // layout of the standard and title master page + case PptSlideLayout::TITLEMASTERSLIDE : + case PptSlideLayout::MASTERNOTES : // layout of the note master page + case PptSlideLayout::NOTESTITLEBODY : // presentation layout for note pages + case PptSlideLayout::HANDOUTLAYOUT : // presentation layout for handout + eAutoLayout = AUTOLAYOUT_NONE; + break; + } + if ( eAutoLayout != AUTOLAYOUT_NONE ) + pPage->SetAutoLayout( eAutoLayout ); + } + } + + // handout master page: auto layout + SdPage* pHandoutMPage = mpDoc->GetMasterSdPage( 0, PageKind::Handout ); + pHandoutMPage->SetAutoLayout( AUTOLAYOUT_HANDOUT6, true, true ); + } + + sal_uInt32 nSlideCount = GetPageCount(); + for ( sal_uInt32 i = 0; ( i < nSlideCount) && ( i < maSlideNameList.size() ); i++ ) + { + SdPage* pPage = mpDoc->GetSdPage( i, PageKind::Standard ); + OUString &aName = maSlideNameList[ i ]; + if ( pPage ) + { + if ( !aName.isEmpty() ) + pPage->SetName( aName ); + else + aName = pPage->GetName(); + } + } + if ( mbDocumentFound ) + { + mpDoc->SetSummationOfParagraphs(); + if ( pDocShell ) + { + ::sd::FrameView* pFrameView = mpDoc->GetFrameView( 0 ); + if ( !pFrameView ) + { + std::vector> &rViews = mpDoc->GetFrameViewList(); + pFrameView = new ::sd::FrameView( mpDoc ); + rViews.push_back( std::unique_ptr(pFrameView) ); + } + sal_uInt16 nSelectedPage = 0; + PageKind ePageKind = PageKind::Standard; + EditMode eEditMode = EditMode::Page; + + switch ( m_aUserEditAtom.eLastViewType ) + { + case PptViewTypeEnum::Outline: + { + SfxItemSet* pSet = mrMed.GetItemSet(); + if ( pSet ) + pSet->Put( SfxUInt16Item( SID_VIEW_ID, 3 ) ); + } + break; + case PptViewTypeEnum::SlideSorter: + { + SfxItemSet* pSet = mrMed.GetItemSet(); + if ( pSet ) + pSet->Put( SfxUInt16Item( SID_VIEW_ID, 2 ) ); + } + break; + case PptViewTypeEnum::TitleMaster: + nSelectedPage = 1; + [[fallthrough]]; + case PptViewTypeEnum::SlideMaster: + { + ePageKind = PageKind::Standard; + eEditMode = EditMode::MasterPage; + } + break; + case PptViewTypeEnum::NotesMaster: + eEditMode = EditMode::MasterPage; + [[fallthrough]]; + case PptViewTypeEnum::Notes: + ePageKind = PageKind::Notes; + break; + case PptViewTypeEnum::Handout: + ePageKind = PageKind::Handout; + break; + default : + case PptViewTypeEnum::Slide: + break; + } + pFrameView->SetPageKind( ePageKind ); + pFrameView->SetSelectedPage( nSelectedPage ); + pFrameView->SetViewShEditMode( eEditMode ); + } + DffRecordHeader aCustomShowHeader; + // read and set custom show + rStCtrl.Seek( maDocHd.GetRecBegFilePos() + 8 ); + if ( SeekToRec( rStCtrl, PPT_PST_NamedShows, maDocHd.GetRecEndFilePos(), &aCustomShowHeader ) ) + { + DffRecordHeader aCuHeader; + while( SeekToRec( rStCtrl, PPT_PST_NamedShow, aCustomShowHeader.GetRecEndFilePos(), &aCuHeader ) ) + { + DffRecordHeader aContent; + if ( SeekToRec( rStCtrl, PPT_PST_CString, aCuHeader.GetRecEndFilePos(), &aContent ) ) + { + OUString aCuShow; + aContent.SeekToBegOfRecord( rStCtrl ); + if ( ReadString( aCuShow ) ) + { + if ( SeekToRec( rStCtrl, PPT_PST_NamedShowSlides, aCuHeader.GetRecEndFilePos(), &aContent ) ) + { + PptSlidePersistList* pPageList = GetPageList( PPT_SLIDEPAGE ); + const auto nRemainingSize = rStCtrl.remainingSize(); + sal_uInt32 nBCount = aContent.nRecLen; + if (nBCount > nRemainingSize) + { + SAL_WARN("filter.ms", "page number data len longer than remaining stream size"); + nBCount = nRemainingSize; + } + sal_uInt32 nSCount = nBCount >> 2; + + if ( pPageList && nSCount ) + { + SdCustomShowList* pList = mpDoc->GetCustomShowList( true ); + if ( pList ) + { + std::unique_ptr pSdCustomShow(new SdCustomShow); + pSdCustomShow->SetName( aCuShow ); + sal_uInt32 nFound = 0; + for ( sal_uInt32 nS = 0; nS < nSCount; nS++ ) + { + sal_uInt32 nPageNumber; + rStCtrl.ReadUInt32( nPageNumber ); + sal_uInt16 nPage = pPageList->FindPage( nPageNumber ); + if ( nPage != PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + { + SdPage* pPage = mpDoc->GetSdPage( nPage, PageKind::Standard ); + if ( pPage ) + { + pSdCustomShow->PagesVector().push_back( pPage ); + nFound++; + } + } + } + if ( nFound ) + pList->push_back( std::move(pSdCustomShow) ); + } + } + } + } + } + } + } + // this is defaulted, maybe there is no SSDocInfoAtom + OUStringBuffer aCustomShow; + sal_uInt32 nFlags = 1; // Bit 0: Auto advance + sal_uInt16 nStartSlide = 0; + + // read the pres. configuration + rStCtrl.Seek( maDocHd.GetRecBegFilePos() + 8 ); + if ( SeekToRec( rStCtrl, PPT_PST_SSDocInfoAtom, maDocHd.GetRecEndFilePos(), &aCustomShowHeader ) ) + { + sal_uInt32 nPenColor = 0x1000000; + sal_Int32 nRestartTime = 0x7fffffff; + sal_Int16 nEndSlide = 0; + rStCtrl.ReadUInt32( nPenColor ) + .ReadInt32( nRestartTime ) + .ReadUInt16( nStartSlide ) + .ReadInt16( nEndSlide ); + + sal_Unicode nChar; + for ( sal_uInt32 i2 = 0; i2 < 32; i2++ ) + { + rStCtrl.ReadUtf16( nChar ); + if ( nChar ) + aCustomShow.append( nChar ); + else + { + rStCtrl.SeekRel( ( 31 - i2 ) << 1 ); + break; + } + } + rStCtrl.ReadUInt32( nFlags ); + } + // set the current custom show + if ( !aCustomShow.isEmpty() ) + { + SdCustomShowList* pList = mpDoc->GetCustomShowList(); + if ( pList ) + { + SdCustomShow* pPtr = nullptr; + OUString aCustomShowStr = aCustomShow.makeStringAndClear(); + for( pPtr = pList->First(); pPtr; pPtr = pList->Next() ) + { + if ( pPtr->GetName() == aCustomShowStr ) + break; + } + if ( !pPtr ) + pList->First(); + } + } + sd::PresentationSettings& rPresSettings = mpDoc->getPresentationSettings(); + + rPresSettings.mbManual = ( nFlags & 1 ) == 0; + rPresSettings.mbAnimationAllowed = ( nFlags & 2 ) == 0; + rPresSettings.mbAll = ( nFlags & 4 ) == 0; + rPresSettings.mbCustomShow = ( nFlags & 8 ) != 0; + rPresSettings.mbEndless = ( nFlags & 0x80 ) != 0; + rPresSettings.mbFullScreen = ( nFlags & 0x10 ) == 0; + + if ( nStartSlide && ( nStartSlide <= GetPageCount() ) ) + { + SdPage* pPage = mpDoc->GetSdPage( nStartSlide - 1, PageKind::Standard ); + if ( pPage ) + rPresSettings.maPresPage = pPage->GetName(); + } + } + + xStbMgr.reset(); + + // read DocumentProperties + uno::Reference xDPS( + mpDoc->GetObjectShell()->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference xDocProps + = xDPS->getDocumentProperties(); + sfx2::LoadOlePropertySet(xDocProps, &mrStorage); + xDocProps->setTemplateName(OUString()); + + pSdrModel->setLock(bWasLocked); + pSdrModel->EnableUndo(bSavedUndoEnabled); + return m_bOk; +} + +void ImplSdPPTImport::SetHeaderFooterPageSettings( SdPage* pPage, const PptSlidePersistEntry* pMasterPersist ) +{ + sal_uInt32 i; + PptSlidePersistList* pList = GetPageList( m_eCurrentPageKind ); + if ( ( !pList ) || ( pList->size() <= m_nCurrentPageNum ) ) + return; + PptSlidePersistEntry& rSlidePersist = (*pList)[ m_nCurrentPageNum ]; + HeaderFooterEntry* pHFE = rSlidePersist.xHeaderFooterEntry.get(); + if (!pHFE) + return; + + for ( i = 0; i < 4; i++ ) + { + bool bVisible = pHFE->IsToDisplay( i ); + if ( ( m_eCurrentPageKind == PPT_SLIDEPAGE ) + && ( rSlidePersist.aSlideAtom.aLayout.eLayout == PptSlideLayout::TITLESLIDE ) + && ( aDocAtom.bTitlePlaceholdersOmitted ) ) + { + bVisible = false; + } + if ( bVisible && pMasterPersist ) + { + sal_uInt32 nPosition = pHFE->NeedToImportInstance( i, rSlidePersist ); + if ( nPosition ) + { + ::tools::Rectangle aEmpty; + bVisible = false; + rStCtrl.Seek( nPosition ); + ProcessData aProcessData( rSlidePersist, SdPageCapsule(pPage) ); + SdrObject* pObj = ImportObj( rStCtrl, aProcessData, aEmpty, aEmpty, /*nCalledByGroup*/0, /*pShapeId*/nullptr ); + if ( pObj ) + pPage->NbcInsertObject( pObj, 0 ); + } + } + OUString aPlaceHolderString = pHFE->pPlaceholder[ i ]; + + sd::HeaderFooterSettings rHeaderFooterSettings( pPage->getHeaderFooterSettings() ); + switch( i ) + { + case 0 : + { + rHeaderFooterSettings.mbDateTimeVisible = bVisible; + rHeaderFooterSettings.mbDateTimeIsFixed = ( pHFE->nAtom & 0x20000 ) == 0; + rHeaderFooterSettings.maDateTimeText = aPlaceHolderString; + SvxDateFormat eDateFormat; + SvxTimeFormat eTimeFormat; + PPTFieldEntry::GetDateTime( pHFE->nAtom & 0xff, eDateFormat, eTimeFormat ); + rHeaderFooterSettings.meDateFormat = eDateFormat; + rHeaderFooterSettings.meTimeFormat = eTimeFormat; + } + break; + case 1 : + { + rHeaderFooterSettings.mbHeaderVisible = bVisible; + rHeaderFooterSettings.maHeaderText = aPlaceHolderString; + } + break; + case 2 : + { + rHeaderFooterSettings.mbFooterVisible = bVisible; + rHeaderFooterSettings.maFooterText = aPlaceHolderString; + } + break; + case 3 : + { + rHeaderFooterSettings.mbSlideNumberVisible = bVisible; + } + break; + } + pPage->setHeaderFooterSettings( rHeaderFooterSettings ); + } +} + +namespace { + +// Import of pages +struct Ppt97AnimationStlSortHelper +{ + bool operator()( const std::pair< SdrObject*, Ppt97AnimationPtr >& p1, const std::pair< SdrObject*, Ppt97AnimationPtr >& p2 ); +}; + +} + +bool Ppt97AnimationStlSortHelper::operator()( const std::pair< SdrObject*, Ppt97AnimationPtr >& p1, const std::pair< SdrObject*, Ppt97AnimationPtr >& p2 ) +{ + if( !p1.second || !p2.second ) + return p1.second.get() < p2.second.get(); + if( *p1.second < *p2.second ) + return true; + if( *p1.second > *p2.second ) + return false; + return p1.first->GetOrdNum() < p2.first->GetOrdNum(); +} + +void ImplSdPPTImport::ImportPageEffect( SdPage* pPage, const bool bNewAnimationsUsed ) +{ + sal_uInt64 nOldFilePos = rStCtrl.Tell(); + + // set PageKind at page (up to now only PageKind::Standard or PageKind::Notes) + if ( pPage->GetPageKind() == PageKind::Standard ) + { + PptSlidePersistList* pPersistList = GetPageList( m_eCurrentPageKind ); + PptSlidePersistEntry* pActualSlidePersist = ( pPersistList && ( m_nCurrentPageNum < pPersistList->size() ) ) + ? &(*pPersistList)[ m_nCurrentPageNum ] : nullptr; + + if ( pActualSlidePersist && ( m_eCurrentPageKind == PPT_SLIDEPAGE ) ) + { + if ( ! ( pActualSlidePersist->aSlideAtom.nFlags & 1 ) ) // do not follow master objects ? + { + if(pPage->TRG_HasMasterPage()) + { + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aVisibleLayers.Set(mnBackgroundObjectsLayerID, false); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + } + } + DffRecordHeader aPageRecHd; + if ( SeekToCurrentPage( &aPageRecHd ) ) + { + sal_uLong nPageRecEnd = SanitizeEndPos(rStCtrl, aPageRecHd.GetRecEndFilePos()); + + bool bTryTwice = ( m_eCurrentPageKind == PPT_SLIDEPAGE ); + bool bSSSlideInfoAtom = false; + while ( true ) + { + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nPageRecEnd ) ) + { + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + switch ( aHd.nRecType ) + { + case PPT_PST_SSSlideInfoAtom: + { + bSSSlideInfoAtom = true; + if ( m_eCurrentPageKind == PPT_MASTERPAGE ) + { + if ( pActualSlidePersist ) + pActualSlidePersist->aPersistAtom.nReserved = aHd.GetRecBegFilePos(); + } + else + { + sal_Int8 nDirection, nTransitionType, nByteDummy, nSpeed; + sal_Int16 nBuildFlags; + sal_Int32 nSlideTime, nSoundRef; + rStCtrl.ReadInt32( nSlideTime ) // time to show (in Ticks) + .ReadInt32( nSoundRef ) // Index of SoundCollection + .ReadSChar( nDirection ) // direction of fade effect + .ReadSChar( nTransitionType ) // fade effect + .ReadInt16( nBuildFlags ) // Buildflags (s.u.) + .ReadSChar( nSpeed ) // speed (slow, medium, fast) + .ReadSChar( nByteDummy ).ReadSChar( nByteDummy ).ReadSChar( nByteDummy ); + + switch ( nTransitionType ) + { + case PPT_TRANSITION_TYPE_BLINDS : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_VERTICAL_STRIPES ); // fade vertical + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_HORIZONTAL_STRIPES ); // fade horizontal + } + break; + case PPT_TRANSITION_TYPE_CHECKER : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_HORIZONTAL_CHECKERBOARD ); // fade vertical with offset ?? + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_VERTICAL_CHECKERBOARD ); // fade horizontal with offset ?? + } + break; + case PPT_TRANSITION_TYPE_COVER : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_RIGHT ); // overlay from right + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_BOTTOM ); // overlay from bottom + else if ( nDirection == 2 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_LEFT ); // overlay from left + else if ( nDirection == 3 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_TOP ); // overlay from top + else if ( nDirection == 4 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_LOWERRIGHT ); // overlay from bottom right ?? + else if ( nDirection == 5 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_LOWERLEFT ); // overlay from bottom left ?? + else if ( nDirection == 6 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_UPPERRIGHT ); // overlay from top right + else if ( nDirection == 7 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_MOVE_FROM_UPPERLEFT ); // overlay from top left ?? + } + break; + case PPT_TRANSITION_TYPE_NONE : + { + if ( nBuildFlags ) + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_NONE ); // direct + else if ( nDirection == 1 ) + { + pPage->setTransitionType( animations::TransitionType::BARWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::FADEOVERCOLOR ); + pPage->setTransitionFadeColor( 0 ); + } + } + else + pPage->setTransitionType( 0 ); + } + break; + case PPT_TRANSITION_TYPE_DISSOLVE : + pPage->SetFadeEffect(css::presentation::FadeEffect_DISSOLVE); // dissolve + break; + case PPT_TRANSITION_TYPE_RANDOM_BARS : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_HORIZONTAL_LINES ); // horizontal lines + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_VERTICAL_LINES ); // vertical lines + } + break; + case PPT_TRANSITION_TYPE_SPLIT : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_OPEN_VERTICAL ); // open horizontal ?? + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_CLOSE_VERTICAL ); // close horizontal ?? + else if ( nDirection == 2 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_OPEN_HORIZONTAL ); // open vertical ?? + else if ( nDirection == 3 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_CLOSE_HORIZONTAL ); // close vertical ?? + } + break; + case PPT_TRANSITION_TYPE_STRIPS : + { + if ( nDirection == 4 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_LOWERRIGHT ); // diagonal to top left + else if ( nDirection == 5 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_LOWERLEFT ); // diagonal to top right + else if ( nDirection == 6 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_UPPERRIGHT ); // diagonal to bottom left + else if ( nDirection == 7 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_UPPERLEFT ); // diagonal to bottom right + } + break; + case PPT_TRANSITION_TYPE_PULL : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_LEFT ); // uncover to left + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_TOP ); // uncover to top + else if ( nDirection == 2 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_RIGHT ); // uncover to right + else if ( nDirection == 3 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_BOTTOM ); // uncover to bottom + else if ( nDirection == 4 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_UPPERLEFT ); // uncover to top left + else if ( nDirection == 5 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_UPPERRIGHT ); // uncover to top right + else if ( nDirection == 6 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_LOWERLEFT ); // uncover to bottom left + else if ( nDirection == 7 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_UNCOVER_TO_LOWERRIGHT ); // uncover to bottom right + } + break; + case PPT_TRANSITION_TYPE_WIPE : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_RIGHT ); // roll from right + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_BOTTOM ); // roll from bottom + else if ( nDirection == 2 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_LEFT ); // roll from left + else if ( nDirection == 3 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_TOP ); // roll from top + } + break; + case PPT_TRANSITION_TYPE_RANDOM : + pPage->SetFadeEffect( css::presentation::FadeEffect_RANDOM ); // automatic + break; + case PPT_TRANSITION_TYPE_FADE : + { + pPage->setTransitionType( animations::TransitionType::FADE ); + pPage->setTransitionSubtype( animations::TransitionSubType::FADEOVERCOLOR ); + pPage->setTransitionFadeColor( 0 ); + } + break; + case PPT_TRANSITION_TYPE_ZOOM : + { + if ( nDirection == 0 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_FROM_CENTER ); // fade from center + else if ( nDirection == 1 ) + pPage->SetFadeEffect( css::presentation::FadeEffect_FADE_TO_CENTER ); // fade from the outside + } + break; + case PPT_TRANSITION_TYPE_DIAMOND : + { + pPage->setTransitionType( animations::TransitionType::IRISWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::DIAMOND ); + } + break; + case PPT_TRANSITION_TYPE_PLUS : + { + pPage->setTransitionType( animations::TransitionType::FOURBOXWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::CORNERSOUT ); + } + break; + case PPT_TRANSITION_TYPE_CIRCLE : + { + pPage->setTransitionType( animations::TransitionType::ELLIPSEWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::CIRCLE ); + } + break; + case PPT_TRANSITION_TYPE_WEDGE : + { + pPage->setTransitionType( animations::TransitionType::FANWIPE ); + pPage->setTransitionSubtype( animations::TransitionSubType::CENTERTOP ); + } + break; + case PPT_TRANSITION_TYPE_WHEEL : + { + pPage->setTransitionType( animations::TransitionType::PINWHEELWIPE ); + sal_Int16 nSubType; + switch( nDirection ) + { + default: + case 1 : nSubType = animations::TransitionSubType::ONEBLADE; break; + case 2 : nSubType = animations::TransitionSubType::TWOBLADEVERTICAL; break; + case 3 : nSubType = animations::TransitionSubType::THREEBLADE; break; + case 4 : nSubType = animations::TransitionSubType::FOURBLADE; break; + case 8 : nSubType = animations::TransitionSubType::EIGHTBLADE; break; + } + pPage->setTransitionSubtype( nSubType ); + } + break; + case PPT_TRANSITION_TYPE_PUSH : + { + pPage->setTransitionType( animations::TransitionType::PUSHWIPE ); + sal_Int16 nSubType; + switch( nDirection ) + { + default: + case 0 : nSubType = animations::TransitionSubType::FROMRIGHT; break; + case 1 : nSubType = animations::TransitionSubType::FROMBOTTOM; break; + case 2 : nSubType = animations::TransitionSubType::FROMLEFT; break; + case 3 : nSubType = animations::TransitionSubType::FROMTOP; break; + } + pPage->setTransitionSubtype( nSubType ); + } + break; + case PPT_TRANSITION_TYPE_COMB : + { + pPage->setTransitionType( animations::TransitionType::PUSHWIPE ); + pPage->setTransitionSubtype( nDirection ? animations::TransitionSubType::COMBVERTICAL : animations::TransitionSubType::COMBHORIZONTAL ); + } + break; + case PPT_TRANSITION_TYPE_NEWSFLASH : + { + pPage->setTransitionType( animations::TransitionType::ZOOM ); + pPage->setTransitionSubtype( animations::TransitionSubType::ROTATEIN ); + } + break; + case PPT_TRANSITION_TYPE_SMOOTHFADE : + { + pPage->setTransitionType( animations::TransitionType::FADE ); + pPage->setTransitionSubtype( animations::TransitionSubType::CROSSFADE ); + } + break; + } + + if ( nSpeed == 0 ) + pPage->setTransitionDuration( 1.0 ); // slow + else if ( nSpeed == 1 ) + pPage->setTransitionDuration( 0.75 ); // medium + else if ( nSpeed == 2 ) + pPage->setTransitionDuration( 0.5 ); // fast + + if ( nBuildFlags & 0x400 ) // slidechange by time + { // time to show (in Ticks) + pPage->SetPresChange( PresChange::Auto ); + pPage->SetTime( nSlideTime / 1000.0 ); + } + else + pPage->SetPresChange( mePresChange ); + + if ( nBuildFlags & 4 ) + pPage->SetExcluded( true ); // don't show slide + if ( nBuildFlags & 16 ) + { // slide with sound effect + pPage->SetSound( true ); + OUString aSoundFile( ReadSound( nSoundRef ) ); + pPage->SetSoundFile( aSoundFile ); + } + if ( nBuildFlags & ( 1 << 6 ) ) // Loop until next sound + pPage->SetLoopSound( true ); + if ( nBuildFlags & ( 1 << 8 ) ) // Stop the previous sound + pPage->SetStopSound( true ); + break; + } + } + } + if (!aHd.SeekToEndOfRecord(rStCtrl)) + break; + } + if ( bTryTwice && !bSSSlideInfoAtom ) + { + bTryTwice = false; + if ( HasMasterPage( m_nCurrentPageNum, m_eCurrentPageKind ) ) + { + sal_uInt16 nMasterNum = GetMasterPageIndex( m_nCurrentPageNum, m_eCurrentPageKind ); + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + if ( pPageList && ( nMasterNum < pPageList->size() ) ) + { + assert( !pPageList->is_null( nMasterNum ) ); + const PptSlidePersistEntry& rE = (*pPageList)[ nMasterNum ]; + sal_uInt32 nOfs = rE.aPersistAtom.nReserved; + if ( nOfs ) + { + rStCtrl.Seek( nOfs ); + nPageRecEnd = nOfs + 16; + continue; + } + } + + } + } + break; + } + } + } + + if ( !bNewAnimationsUsed ) + { + std::vector< std::pair< SdrObject*, Ppt97AnimationPtr > > aAnimationsOnThisPage; + + // add effects from page in correct order + SdrObjListIter aSdrIter( pPage, SdrIterMode::Flat ); + while ( aSdrIter.IsMore() ) + { + SdrObject* pObj = aSdrIter.Next(); + tAnimationMap::iterator aFound = maAnimations.find( pObj ); + if( aFound != maAnimations.end() ) + { + std::pair< SdrObject*, Ppt97AnimationPtr > aPair( (*aFound).first, (*aFound).second ); + aAnimationsOnThisPage.push_back( aPair ); + } + } + + std::sort( aAnimationsOnThisPage.begin(), aAnimationsOnThisPage.end(), Ppt97AnimationStlSortHelper() ); + + for( auto& rEntry : aAnimationsOnThisPage ) + { + Ppt97AnimationPtr pPpt97Animation = rEntry.second; + if( pPpt97Animation ) + pPpt97Animation->createAndSetCustomAnimationEffect( rEntry.first ); + } + } + rStCtrl.Seek( nOldFilePos ); +} + +// import of sounds + +// Not only the sounds are imported as string, they are also inserted to +// the gallery if they are not already there. +OUString ImplSdPPTImport::ReadSound(sal_uInt32 nSoundRef) const +{ + OUString aRetval; + sal_uInt32 nOldPos = rStCtrl.Tell(); + DffRecordHeader aDocHd; + if ( SeekToDocument( &aDocHd ) ) + { + sal_uInt32 nSoundLen = aDocHd.GetRecEndFilePos(); + DffRecordHeader aSoundBlockRecHd; + if( SeekToRec( rStCtrl, PPT_PST_SoundCollection, nSoundLen, &aSoundBlockRecHd ) ) + { + sal_uInt32 nDataLen = aSoundBlockRecHd.GetRecEndFilePos(); + DffRecordHeader aSoundRecHd; + bool bRefStrValid = false; + bool bDone = false; + + while( !bDone && SeekToRec( rStCtrl, PPT_PST_Sound, nDataLen, &aSoundRecHd ) ) + { + sal_uInt32 nStrLen = aSoundRecHd.GetRecEndFilePos(); + OUString aRefStr; + sal_uInt32 nOldPos2 = rStCtrl.Tell(); + if ( SeekToRec( rStCtrl, PPT_PST_CString, nStrLen, nullptr, 2 ) ) + { + if ( ReadString( aRefStr ) ) + bRefStrValid = true; + } + if ( bRefStrValid ) + { + if ( std::u16string_view(OUString::number(nSoundRef)) == aRefStr ) + { + rStCtrl.Seek( nOldPos2 ); + if ( SeekToRec( rStCtrl, PPT_PST_CString, nStrLen ) ) + { + ReadString( aRetval ); + bDone = true; + } + } + } + if ( bDone ) + { + // Check if this sound file already exists. + // If not, it is exported to our local sound directory. + bool bSoundExists = false; + ::std::vector< OUString > aSoundList; + + GalleryExplorer::FillObjList( GALLERY_THEME_SOUNDS, aSoundList ); + GalleryExplorer::FillObjList( GALLERY_THEME_USERSOUNDS, aSoundList ); + + for( size_t n = 0; ( n < aSoundList.size() ) && !bSoundExists; ++n ) + { + INetURLObject aURL( aSoundList[ n ] ); + + if (aURL.GetLastName() == aRetval) + { + aRetval = aSoundList[ n ]; + bSoundExists = true; + } + } + + aSoundList.clear(); + + if ( !bSoundExists ) + { + rStCtrl.Seek( nOldPos2 ); + DffRecordHeader aSoundDataRecHd; + if ( SeekToRec( rStCtrl, PPT_PST_SoundData, nStrLen, &aSoundDataRecHd ) ) + { + OUString aGalleryDir; + if (utl::ConfigManager::IsFuzzing()) + osl_getTempDirURL(&aGalleryDir.pData); + else + aGalleryDir = SvtPathOptions().GetGalleryPath(); + // Use last token delimited by ';'. copy(lastIndexOf+1) works whether + // string is empty or not and whether ';' is there or not. + INetURLObject aGalleryUserSound( aGalleryDir.subView(aGalleryDir.lastIndexOf(';')+1) ); + + aGalleryUserSound.Append( aRetval ); + const auto nRemainingSize = rStCtrl.remainingSize(); + sal_uInt32 nSoundDataLen = aSoundDataRecHd.nRecLen; + if (nSoundDataLen > nRemainingSize) + { + SAL_WARN("filter.ms", "sound data len longer than remaining stream size"); + nSoundDataLen = nRemainingSize; + } + std::vector aBuf(nSoundDataLen); + + rStCtrl.ReadBytes(aBuf.data(), nSoundDataLen); + std::unique_ptr pOStm = ::utl::UcbStreamHelper::CreateStream( aGalleryUserSound.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::WRITE | StreamMode::TRUNC ); + + if( pOStm ) + { + pOStm->WriteBytes(aBuf.data(), nSoundDataLen); + + if( pOStm->GetError() == ERRCODE_NONE ) + { + GalleryExplorer::InsertURL( GALLERY_THEME_USERSOUNDS, aGalleryUserSound.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + aRetval = aGalleryUserSound.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + } + } + } + } + } + if ( !bDone ) + { + if (!aSoundRecHd.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + } + rStCtrl.Seek( nOldPos ); + return aRetval; +} + +// media object import, the return value is the url to the media object +OUString ImplSdPPTImport::ReadMedia( sal_uInt32 nMediaRef ) const +{ + OUString aRetVal; + DffRecordHeader* pHd( const_cast(this)->aDocRecManager.GetRecordHeader( PPT_PST_ExObjList ) ); + if ( pHd ) + { + pHd->SeekToContent( rStCtrl ); + auto nEndRecPos = SanitizeEndPos(rStCtrl, pHd->GetRecEndFilePos()); + while ( ( rStCtrl.Tell() < nEndRecPos ) && aRetVal.isEmpty() ) + { + DffRecordHeader aHdMovie; + ReadDffRecordHeader( rStCtrl, aHdMovie ); + switch( aHdMovie.nRecType ) + { + case PPT_PST_ExAviMovie : + case PPT_PST_ExMCIMovie : + { + DffRecordHeader aExVideoHd; + if ( SeekToRec( rStCtrl, PPT_PST_ExVideo, aHdMovie.GetRecEndFilePos(), &aExVideoHd ) ) + { + DffRecordHeader aExMediaAtomHd; + if ( SeekToRec( rStCtrl, PPT_PST_ExMediaAtom, aExVideoHd.GetRecEndFilePos(), &aExMediaAtomHd ) ) + { + sal_uInt32 nRef; + rStCtrl.ReadUInt32( nRef ); + if ( nRef == nMediaRef ) + { + aExVideoHd.SeekToContent( rStCtrl ); + auto nHdEndRecPos = SanitizeEndPos(rStCtrl, aExVideoHd.GetRecEndFilePos()); + while (rStCtrl.Tell() < nHdEndRecPos) + { + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + switch( aHd.nRecType ) + { + case PPT_PST_CString : + { + aHd.SeekToBegOfRecord( rStCtrl ); + OUString aStr; + if ( ReadString( aStr ) ) + { + if( osl::FileBase::getFileURLFromSystemPath( aStr, aRetVal ) + == osl::FileBase::E_None ) + { + aRetVal = INetURLObject( aRetVal ).GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ); + }else{ + aRetVal = aStr; + } + } + } + break; + } + if (!aHd.SeekToEndOfRecord(rStCtrl)) + break; + } + break; + } + } + } + } + break; + } + if (!aHdMovie.SeekToEndOfRecord(rStCtrl)) + break; + } + } + return aRetVal; +} + +// import of objects +void ImplSdPPTImport::FillSdAnimationInfo( SdAnimationInfo* pInfo, PptInteractiveInfoAtom const * pIAtom, const OUString& aMacroName ) +{ + // set local information into pInfo + if( pIAtom->nSoundRef ) + { + pInfo->SetBookmark( ReadSound( pIAtom->nSoundRef ) ); // path to sound file in MS DOS notation + pInfo->meClickAction = css::presentation::ClickAction_SOUND; // RunProgramAction + } + + switch ( pIAtom->nAction ) + { + + case 0x02 : // RunProgramAction + { + pInfo->meClickAction = css::presentation::ClickAction_PROGRAM; + pInfo->SetBookmark( aMacroName ); // program name in aBookmark + } + break; + case 0x03 : // JumpAction + { + switch( pIAtom->nJump ) + { + case 0x01 : + pInfo->meClickAction = css::presentation::ClickAction_NEXTPAGE; // Next slide + break; + case 0x02 : + pInfo->meClickAction = css::presentation::ClickAction_PREVPAGE; // Previous slide + break; + case 0x03 : + pInfo->meClickAction = css::presentation::ClickAction_FIRSTPAGE; // First slide + break; + case 0x04 : + pInfo->meClickAction = css::presentation::ClickAction_LASTPAGE; // last Slide + break; + case 0x05 : + pInfo->meClickAction = css::presentation::ClickAction_PREVPAGE; // Last slide viewed + break; + case 0x06 : + pInfo->meClickAction = css::presentation::ClickAction_STOPPRESENTATION; // End show + break; + default : + pInfo->meClickAction = css::presentation::ClickAction_NONE; // 0x00: no action, else unknown + break; + } + } + break; + case 0x04 : + { + SdHyperlinkEntry* pPtr = nullptr; + for (SdHyperlinkEntry & entry : m_aHyperList) { + if ( entry.nIndex == pIAtom->nExHyperlinkId ) { + pPtr = &entry; + break; + } + } + if ( pPtr ) + { + switch( pIAtom->nHyperlinkType ) + { + case 9: + case 8: // hyperlink : URL + { + if ( !pPtr->aTarget.isEmpty() ) + { + ::sd::DrawDocShell* pDocShell = mpDoc->GetDocSh(); + SfxMedium* pMedium = pDocShell ? pDocShell->GetMedium() : nullptr; + if (pMedium) + { + OUString aBaseURL = pMedium->GetBaseURL(); + OUString aBookmarkURL( pInfo->GetBookmark() ); + INetURLObject aURL( pPtr->aTarget ); + if( INetProtocol::NotValid == aURL.GetProtocol() + && (osl::FileBase::getFileURLFromSystemPath( + pPtr->aTarget, aBookmarkURL) + != osl::FileBase::E_None) ) + aBookmarkURL.clear(); + if( aBookmarkURL.isEmpty() ) + aBookmarkURL = URIHelper::SmartRel2Abs( INetURLObject(aBaseURL), pPtr->aTarget, URIHelper::GetMaybeFileHdl() ); + pInfo->SetBookmark( aBookmarkURL ); + pInfo->meClickAction = css::presentation::ClickAction_PROGRAM; + } + } + } + break; + + case 10: + break; + + case 7: // hyperlink to a page + { + if ( !pPtr->aConvSubString.isEmpty() ) + { + pInfo->meClickAction = css::presentation::ClickAction_BOOKMARK; + pInfo->SetBookmark( pPtr->aConvSubString ); + } + } + break; + } + } + } + break; + case 0x05 : // OLEAction ( OLEVerb to use, 0==first, 1==second, .. ) + case 0x06 : // MediaAction + case 0x07 : // CustomShowAction + default : // 0x00: no action, else unknown action + break; + } +} + +SdrObject* ImplSdPPTImport::ApplyTextObj( PPTTextObj* pTextObj, SdrTextObj* pObj, SdPageCapsule pPageCapsule, + SfxStyleSheet* pSheet, SfxStyleSheet** ppStyleSheetAry ) const +{ + SdPage * pPage = static_cast(pPageCapsule.page); + SfxStyleSheet* pStyleSheetAry[ 9 ]; + SdrTextObj* pText = pObj; + SdrObject* pRet = pText; + + ppStyleSheetAry = nullptr; + + PresObjKind ePresKind = PresObjKind::NONE; + PptOEPlaceholderAtom* pPlaceHolder = pTextObj->GetOEPlaceHolderAtom(); + OUString aPresentationText; + if ( pPlaceHolder ) + { + switch( pPlaceHolder->nPlaceholderId ) + { + case PptPlaceholder::MASTERNOTESSLIDEIMAGE : + case PptPlaceholder::MASTERCENTEREDTITLE : + case PptPlaceholder::MASTERTITLE : + { + ePresKind = PresObjKind::Title; + aPresentationText = pPage->GetPresObjText( ePresKind ); + } + break; + case PptPlaceholder::MASTERBODY : + { + ePresKind = PresObjKind::Outline; + aPresentationText = pPage->GetPresObjText( ePresKind ); + } + break; + case PptPlaceholder::MASTERSUBTITLE : + { + ePresKind = PresObjKind::Text; + aPresentationText = pPage->GetPresObjText( ePresKind ); + } + break; + case PptPlaceholder::MASTERNOTESBODYIMAGE : + { + ePresKind = PresObjKind::Notes; + aPresentationText = pPage->GetPresObjText( ePresKind ); + } + break; + case PptPlaceholder::MASTERDATE : ePresKind = PresObjKind::DateTime; break; + case PptPlaceholder::MASTERSLIDENUMBER : ePresKind = PresObjKind::SlideNumber;break; + case PptPlaceholder::MASTERFOOTER : ePresKind = PresObjKind::Footer; break; + case PptPlaceholder::MASTERHEADER : ePresKind = PresObjKind::Header; break; + default: break; + } + } + switch ( pTextObj->GetDestinationInstance() ) + { + case TSS_Type::PageTitle : + case TSS_Type::Title : + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + if ( pSheet ) + static_cast(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for titleobject (SJ)" ); + } + break; + case TSS_Type::Subtitle : + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Text ); + if ( pSheet ) + static_cast(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for subtitleobject (SJ)" ); + } + break; + case TSS_Type::Body : + case TSS_Type::HalfBody : + case TSS_Type::QuarterBody : + { + for ( sal_uInt16 nLevel = 9; nLevel; nLevel-- ) + { + OUString aName = pPage->GetLayoutName() + " " + OUString::number( nLevel ); + pSheet = static_cast(mpDoc->GetStyleSheetPool()->Find( aName, SfxStyleFamily::Page )); + if ( pSheet ) + pText->StartListening( *pSheet ); + pStyleSheetAry[ nLevel - 1 ] = pSheet; + } + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for outlinerobject (SJ)" ); + if ( pSheet ) + static_cast(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + ppStyleSheetAry = &pStyleSheetAry[ 0 ]; + } + break; + case TSS_Type::Notes : + { + if ( pPlaceHolder && ( ( pPlaceHolder->nPlaceholderId == PptPlaceholder::NOTESSLIDEIMAGE ) + || ( pPlaceHolder->nPlaceholderId == PptPlaceholder::MASTERNOTESSLIDEIMAGE ) ) ) + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + if ( pSheet ) + static_cast(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for titleobject (SJ)" ); + } + else + { + pSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Notes ); + DBG_ASSERT( pSheet, "ImplSdPPTImport::ApplyTextObj -> could not get stylesheet for notesobj (SJ)" ); + if ( pSheet ) + static_cast(pText)->SdrAttrObj::NbcSetStyleSheet( pSheet, true ); + } + } + break; + case TSS_Type::Unused : + case TSS_Type::TextInShape : + { + switch( ePresKind ) + { + case PresObjKind::DateTime : + case PresObjKind::SlideNumber : + case PresObjKind::Footer : + case PresObjKind::Header : + pSheet = static_cast(mpDoc->GetStyleSheetPool()->Find(SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS), SfxStyleFamily::Pseudo )); + break; + default : + pSheet = static_cast(mpDoc->GetStyleSheetPool()->Find(SdResId(STR_STANDARD_STYLESHEET_NAME), SfxStyleFamily::Para )); + } + } + break; + default: break; + } + + pText = static_cast(SdrPowerPointImport::ApplyTextObj( pTextObj, pText, pPageCapsule, pSheet, ppStyleSheetAry )); + + if ( pPlaceHolder && pPlaceHolder->nPlaceholderId != PptPlaceholder::NONE ) + { + if ( m_eCurrentPageKind == PPT_MASTERPAGE ) + { + bool bCreatePlaceHolder = ( pTextObj->GetInstance() != TSS_Type::Unused ); + bool bIsHeaderFooter = ( ePresKind == PresObjKind::Header) || (ePresKind == PresObjKind::Footer) + || (ePresKind == PresObjKind::DateTime) || (ePresKind == PresObjKind::SlideNumber); + if ( bCreatePlaceHolder && ( pTextObj->GetInstance() == TSS_Type::TextInShape ) ) + bCreatePlaceHolder = bIsHeaderFooter; + if ( bCreatePlaceHolder ) + { + if ( !bIsHeaderFooter ) + { + pText->SetNotVisibleAsMaster( true ); + pText->SetEmptyPresObj( true ); + } + pText->SetUserCall( pPage ); + pPage->InsertPresObj( pText, ePresKind ); + SdrOutliner* pOutl = nullptr; + if ( pTextObj->GetInstance() == TSS_Type::Notes ) + pOutl = GetDrawOutliner( pText ); + if ( !aPresentationText.isEmpty() ) + pPage->SetObjText( pText, pOutl, ePresKind, aPresentationText ); + + if ( pPage->GetPageKind() != PageKind::Notes && pPage->GetPageKind() != PageKind::Handout) + { + SfxStyleSheet* pSheet2( pPage->GetStyleSheetForPresObj( ePresKind ) ); + if ( pSheet2 ) + { + SfxItemSet& rItemSet = pSheet2->GetItemSet(); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_LEFTDIST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_RIGHTDIST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_UPPERDIST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_LOWERDIST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_VERTADJUST ) ); + rItemSet.Put( pText->GetMergedItem( SDRATTR_TEXT_HORZADJUST ) ); + if ( pTextObj->GetInstance() == TSS_Type::Title + || pTextObj->GetInstance() == TSS_Type::Subtitle) + { + rItemSet.Put( pText->GetMergedItemSet() ); + } + } + } + + SfxItemSet aTempAttr( mpDoc->GetPool() ); + SdrMetricItem aMinHeight( makeSdrTextMinFrameHeightItem(pText->GetLogicRect().GetSize().Height()) ); + aTempAttr.Put( aMinHeight ); + SdrOnOffItem aAutoGrowHeight( makeSdrTextAutoGrowHeightItem(false) ); + aTempAttr.Put( aAutoGrowHeight ); + pText->SetMergedItemSet(aTempAttr); + } + else + { + pRet = nullptr; + } + } + else + { + const PptSlideLayoutAtom* pSlideLayout = GetSlideLayoutAtom(); + if ( pSlideLayout || ( m_eCurrentPageKind == PPT_NOTEPAGE ) ) + { + sal_uInt32 nPlacementId = pPlaceHolder->nPlacementId; + PptPlaceholder nPlaceholderId = pPlaceHolder->nPlaceholderId; + PresObjKind ePresObjKind = PresObjKind::NONE; + bool bEmptyPresObj = true; + bool bVertical = false; + if ( ( pTextObj->GetShapeType() == mso_sptRectangle ) || ( pTextObj->GetShapeType() == mso_sptTextBox ) ) + { + //if a placeholder with some custom attribute,the pTextObj will keep those attr,whose text size is zero, + //so sdPage should renew a PresObj to process placeholder. + bEmptyPresObj = ( pTextObj->Count() == 0 ) || ( pTextObj->Count() == 1 && pTextObj->First()->GetTextSize() == 0 ); + switch ( nPlaceholderId ) + { + case PptPlaceholder::NOTESBODY : ePresObjKind = PresObjKind::Notes; break; + case PptPlaceholder::VERTICALTEXTTITLE : + bVertical = true; + [[fallthrough]]; + case PptPlaceholder::TITLE : ePresObjKind = PresObjKind::Title; break; + case PptPlaceholder::VERTICALTEXTBODY : + bVertical = true; + [[fallthrough]]; + case PptPlaceholder::BODY : ePresObjKind = PresObjKind::Outline; break; + case PptPlaceholder::CENTEREDTITLE : ePresObjKind = PresObjKind::Title; break; + case PptPlaceholder::SUBTITLE : ePresObjKind = PresObjKind::Text; break; // PresObjKind::Outline + + default : + { + if ( pTextObj->Count() == 0 ) + { + switch ( nPlaceholderId ) + { + case PptPlaceholder::MEDIACLIP : + case PptPlaceholder::OBJECT : ePresObjKind = PresObjKind::Object; break; + case PptPlaceholder::GRAPH : ePresObjKind = PresObjKind::Chart; break; + case PptPlaceholder::TABLE : ePresObjKind = PresObjKind::Table; break; + case PptPlaceholder::CLIPART : ePresObjKind = PresObjKind::Graphic; break; + case PptPlaceholder::ORGANISZATIONCHART : ePresObjKind = PresObjKind::OrgChart; break; + default: break; + } + } + }; + } + } + else if ( pTextObj->GetShapeType() == mso_sptPictureFrame ) + { + if ( !pTextObj->Count() && dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr ) + { + bEmptyPresObj = false; + switch ( nPlaceholderId ) + { + case PptPlaceholder::MEDIACLIP : + case PptPlaceholder::OBJECT : ePresObjKind = PresObjKind::Object; break; + case PptPlaceholder::GRAPH : ePresObjKind = PresObjKind::Chart; break; + case PptPlaceholder::TABLE : ePresObjKind = PresObjKind::Calc; break; + case PptPlaceholder::CLIPART : ePresObjKind = PresObjKind::Graphic; break; + case PptPlaceholder::ORGANISZATIONCHART : ePresObjKind = PresObjKind::OrgChart; break; + default: break; + } + } + } + if ( ePresObjKind != PresObjKind::NONE ) + { + if ( !bEmptyPresObj ) + { + pPage->InsertPresObj( pRet, ePresObjKind ); + } + else + { + SdrObject* pPresObj = pPage->CreatePresObj( ePresObjKind, bVertical, pText->GetLogicRect() ); + pPresObj->SetUserCall( pPage ); + + SfxItemSet aSet( pSdrModel->GetItemPool() ); + ApplyAttributes( rStCtrl, aSet ); + pPresObj->SetLogicRect(pText->GetLogicRect()); + ApplyTextAnchorAttributes( *pTextObj, aSet ); + //set custom font attribute of the placeholder + if ( pTextObj->Count() == 1 ) + { + PPTParagraphObj* pPara = pTextObj->First(); + if ( pPara && pPara->GetTextSize() == 0 ) + { + if ( PPTPortionObj * pPor = pPara->First() ) + { + pPor->ApplyTo(aSet, const_cast(static_cast(*this)), pTextObj->GetDestinationInstance()); + } + } + } + pPresObj->SetMergedItemSet(aSet); + + if ((m_eCurrentPageKind != PPT_NOTEPAGE) && (nPlacementId != 0xffffffff) && pPage->TRG_HasMasterPage()) + { + SdrObject* pTitleObj = static_cast(pPage->TRG_GetMasterPage()).GetPresObj( PresObjKind::Title ); + SdrObject* pOutlineObj = static_cast(pPage->TRG_GetMasterPage()).GetPresObj( PresObjKind::Outline ); + + ::tools::Rectangle aTitleRect; + ::tools::Rectangle aOutlineRect; + Size aOutlineSize; + + if ( pTitleObj ) + aTitleRect = pTitleObj->GetLogicRect(); + if ( pOutlineObj ) + { + aOutlineRect = pOutlineObj->GetLogicRect(); + aOutlineSize = aOutlineRect.GetSize(); + } + ::tools::Rectangle aLogicRect( pPresObj->GetLogicRect() ); + Size aLogicSize( aLogicRect.GetSize() ); + + switch ( nPlacementId ) + { + case 0 : // position in title area + { + if ( aLogicRect != aTitleRect ) + pPresObj->SetUserCall( nullptr ); + } + break; + + case 1: + { + if ( pSlideLayout->eLayout == PptSlideLayout::TITLEANDBODYSLIDE ) + { // position in outline area + if ( aLogicRect != aOutlineRect ) + pPresObj->SetUserCall( nullptr ); + } + else if ( pSlideLayout->eLayout == PptSlideLayout::TWOCOLUMNSANDTITLE ) + { // position in outline area left + if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE || + aOutlineSize.Width() == 0 || + static_cast(aLogicSize.Width()) / aOutlineSize.Width() < 0.48 || + static_cast(aLogicSize.Width()) / aOutlineSize.Width() > 0.5) + { + pPresObj->SetUserCall(nullptr); + } + } + else if ( pSlideLayout->eLayout == PptSlideLayout::TWOROWSANDTITLE ) + { // position in outline area top + if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE || + std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE) + { + pPresObj->SetUserCall( nullptr ); + } + } + else if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE) + { // position in outline area top left + pPresObj->SetUserCall( nullptr ); + } + } + break; + + case 2: + { + if ( pSlideLayout->eLayout == PptSlideLayout::TWOCOLUMNSANDTITLE ) + { // position in outline area right + if (std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE || + aOutlineSize.Width() == 0 || + static_cast(aLogicSize.Width()) / aOutlineSize.Width() < 0.48 || + static_cast(aLogicSize.Width()) / aOutlineSize.Width() > 0.5) + { + pPresObj->SetUserCall( nullptr ); + } + } + else if ( pSlideLayout->eLayout == PptSlideLayout::TWOROWSANDTITLE ) + { // position in outline area bottom + if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE || + std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE) + { + pPresObj->SetUserCall( nullptr ); + } + } + else if (std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE || + std::abs(aLogicRect.Top() - aOutlineRect.Top()) > MAX_USER_MOVE) + { // position in outline area top right + pPresObj->SetUserCall(nullptr); + } + } + break; + + case 3: + { // position in outline area bottom left + if (std::abs(aLogicRect.Left() - aOutlineRect.Left()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE) + { + pPresObj->SetUserCall( nullptr ); + } + } + break; + + case 4: + { // position in outline area bottom right + if (std::abs(aLogicRect.Right() - aOutlineRect.Right()) > MAX_USER_MOVE || + std::abs(aLogicRect.Bottom() - aOutlineRect.Bottom()) > MAX_USER_MOVE) + { + pObj->SetUserCall( nullptr ); + } + } + break; + } + } + pRet = nullptr; // return zero cause this obj was already inserted by CreatePresObj + } + } + else if ( !pTextObj->Count() ) + pRet = nullptr; + } + } + } + if ( pRet != pText ) + { + SdrObject* pFree( pText ); + SdrObject::Free( pFree ); + } + return pRet; +} + +SdrObject* ImplSdPPTImport::ProcessObj( SvStream& rSt, DffObjData& rData, SvxMSDffClientData& rClientData, ::tools::Rectangle& rTextRect, SdrObject* pRet ) +{ + SdrObject* pObj = SdrPowerPointImport::ProcessObj( rSt, rData, rClientData, rTextRect, pRet ); + + // read animation effect of object + if ( pObj ) + { + // further setup placeholder objects + if (dynamic_cast(pObj)) + { + const ProcessData& rProcessData=static_cast(rClientData); + if(rProcessData.pPage.page) + static_cast(rProcessData.pPage.page)->InsertPresObj( + pObj, PresObjKind::Page ); + } + + DffRecordHeader aMasterShapeHd; + + if ( maShapeRecords.SeekToContent( rSt, DFF_msofbtClientData, SEEK_FROM_CURRENT_AND_RESTART ) ) + { + bool bInhabitanceChecked = false; + bool bAnimationInfoFound = false; + + DffRecordHeader& rHdClientData = *maShapeRecords.Current(); + while( true ) + { + sal_uInt32 nClientDataLen = SanitizeEndPos(rSt, rHdClientData.GetRecEndFilePos()); + DffRecordHeader aHd; + do + { + ReadDffRecordHeader( rSt, aHd ); + sal_uInt32 nHdRecEnd = aHd.GetRecEndFilePos(); + switch ( aHd.nRecType ) + { + case PPT_PST_AnimationInfo : + { + DffRecordHeader aHdAnimInfoAtom; + if ( SeekToRec( rSt, PPT_PST_AnimationInfoAtom, nHdRecEnd, &aHdAnimInfoAtom ) ) + { + // read data from stream + Ppt97AnimationPtr pAnimation = std::make_shared( rSt ); + // store animation information + if( pAnimation->HasEffect() ) + { + // translate color to RGB + pAnimation->SetDimColor( MSO_CLR_ToColor(pAnimation->GetDimColor()) ); + // translate sound bits to file url + if( pAnimation->HasSoundEffect() ) + pAnimation->SetSoundFileUrl( ReadSound( pAnimation->GetSoundRef() ) ); + + bool bDontAnimateInvisibleShape = false; + { + SdrTextObj* pTextObj = dynamic_cast(pObj); + + if( pTextObj && pTextObj->HasText() && + dynamic_cast< SdrObjGroup *>( pObj ) == nullptr && + pAnimation->HasAnimateAssociatedShape() ) + { + const SfxItemSet& rObjItemSet = pObj->GetMergedItemSet(); + + drawing::FillStyle eFillStyle = rObjItemSet.Get(XATTR_FILLSTYLE).GetValue(); + drawing::LineStyle eLineStyle = rObjItemSet.Get(XATTR_LINESTYLE).GetValue(); + + if ( ( eFillStyle == drawing::FillStyle_NONE ) && ( eLineStyle == drawing::LineStyle_NONE ) ) + bDontAnimateInvisibleShape = true; + } + } + if( bDontAnimateInvisibleShape ) + pAnimation->SetAnimateAssociatedShape(false); + + //maybe some actions necessary to ensure that animations on master pages are played before animations on normal pages + //maybe todo in future: bool bIsEffectOnMasterPage = !bInhabitanceChecked;? + + maAnimations[pObj] = pAnimation; + + bAnimationInfoFound = true; + } + } + } + break; + case PPT_PST_InteractiveInfo: + { + sal_uInt32 nOldFilePos2 = rSt.Tell(); + OUString aMacroName; + + if(SeekToRec( rSt, PPT_PST_CString, nHdRecEnd ) ) + ReadString(aMacroName); + + rSt.Seek( nOldFilePos2 ); + DffRecordHeader aHdInteractiveInfoAtom; + if ( SeekToRec( rSt, PPT_PST_InteractiveInfoAtom, nHdRecEnd, &aHdInteractiveInfoAtom ) ) + { + PptInteractiveInfoAtom aInteractiveInfoAtom; + ReadPptInteractiveInfoAtom( rSt, aInteractiveInfoAtom ); + + // interactive object + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj, true); + + FillSdAnimationInfo( pInfo, &aInteractiveInfoAtom, aMacroName ); + if ( aInteractiveInfoAtom.nAction == 6 ) // Sj -> media action + { + rHdClientData.SeekToContent( rStCtrl ); + DffRecordHeader aObjRefAtomHd; + if ( SeekToRec( rSt, PPT_PST_ExObjRefAtom, nHdRecEnd, &aObjRefAtomHd ) ) + { + sal_uInt32 nRef; + rSt.ReadUInt32( nRef ); + OUString aMediaURL( ReadMedia( nRef ) ); + if ( aMediaURL.isEmpty() ) + aMediaURL = ReadSound( nRef ); + if ( !aMediaURL.isEmpty() ) + { + SdrMediaObj* pMediaObj = new SdrMediaObj( + pObj->getSdrModelFromSdrObject(), + pObj->GetSnapRect()); + pMediaObj->SetMergedItemSet( pObj->GetMergedItemSet() ); + + //--remove object from maAnimations list and add the new object instead + Ppt97AnimationPtr pAnimation; + { + tAnimationMap::iterator aFound = maAnimations.find( pObj ); + if( aFound != maAnimations.end() ) + { + pAnimation = (*aFound).second; + maAnimations.erase(aFound); + } + maAnimations[pMediaObj] = pAnimation; + } + + SdrObject::Free( pObj ); + pObj = pMediaObj; // SJ: hoping that pObj is not inserted in any list + pMediaObj->setURL( aMediaURL, ""/*TODO?*/ ); + } + } + } + } + } + break; + } + if (!aHd.SeekToEndOfRecord(rSt)) + break; + } + while( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < nClientDataLen ) ); + + if ( bInhabitanceChecked || bAnimationInfoFound ) + break; + bInhabitanceChecked = true; + if ( ! ( IsProperty( DFF_Prop_hspMaster ) && SeekToShape( rSt, &rClientData, GetPropertyValue( DFF_Prop_hspMaster, 0 ) ) ) ) + break; + ReadDffRecordHeader( rSt, aMasterShapeHd ); + if ( !SeekToRec( rSt, DFF_msofbtClientData, aMasterShapeHd.GetRecEndFilePos(), &aMasterShapeHd ) ) + break; + aMasterShapeHd.SeekToContent( rSt ); + rHdClientData = aMasterShapeHd; + } + } + } + return pObj; +} + +bool +ImplSdPPTImport::ReadFormControl( tools::SvRef& rSrc1, css::uno::Reference< css::form::XFormComponent > & rFormComp ) const +{ + uno::Reference< frame::XModel > xModel; + if ( mpDoc->GetDocSh() ) + { + xModel = mpDoc->GetDocSh()->GetModel(); + oox::ole::MSConvertOCXControls aCtrlImporter( xModel ); + return aCtrlImporter.ReadOCXStorage( rSrc1, rFormComp ); + } + return false; +} + +// exported function +extern "C" SAL_DLLPUBLIC_EXPORT sal_Bool ImportPPT( + SdDrawDocument* pDocument, SvStream& rDocStream, SotStorage& rStorage, SfxMedium& rMedium ) +{ + std::unique_ptr pImport( new SdPPTImport( pDocument, rDocStream, rStorage, rMedium )); + return pImport->Import(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportPPT(SvStream &rStream) +{ + bool bRet = false; + try + { + tools::SvRef xStorage(new SotStorage(rStream)); + if (xStorage->GetError()) + return false; + + tools::SvRef xDocStream(xStorage->OpenSotStream( "PowerPoint Document", StreamMode::STD_READ)); + if ( !xDocStream.is() ) + return false; + + SdDLL::Init(); + + SfxMedium aSrcMed("", StreamMode::STD_READ); + + xDocStream->SetVersion(xStorage->GetVersion()); + xDocStream->SetCryptMaskKey(xStorage->GetKey()); + + ::sd::DrawDocShellRef xDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::EMBEDDED, false, DocumentType::Impress); + SdDrawDocument *pDoc = xDocShRef->GetDoc(); + + try + { + bRet = ImportPPT(pDoc, *xDocStream, *xStorage, aSrcMed); + } + catch (...) + { + } + + xDocShRef->DoClose(); + } + catch (...) + { + } + return bRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptin.hxx b/sd/source/filter/ppt/pptin.hxx new file mode 100644 index 000000000..39eff2890 --- /dev/null +++ b/sd/source/filter/ppt/pptin.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +class SdDrawDocument; +class SfxMedium; + +/************************************************************************* +|* +|* local import +|* +\************************************************************************/ + +class SdPage; +class SdAnimationInfo; +class Ppt97Animation; + +typedef std::shared_ptr< Ppt97Animation > Ppt97AnimationPtr; +typedef ::std::map < SdrObject*, Ppt97AnimationPtr > tAnimationMap; + +class ImplSdPPTImport : public SdrPowerPointImport +{ + tools::SvRef mxPicturesStream; + SfxMedium& mrMed; + SotStorage& mrStorage; + DffRecordHeader maDocHd; + std::vector maSlideNameList; + bool mbDocumentFound; + sal_uInt32 mnFilterOptions; + SdDrawDocument* mpDoc; + PresChange mePresChange; + SdrLayerID mnBackgroundObjectsLayerID; + + tAnimationMap maAnimations; + void SetHeaderFooterPageSettings( SdPage* pPage, const PptSlidePersistEntry* pMasterPersist ); + void ImportPageEffect( SdPage* pPage, const bool bNewAnimationsUsed ); + + void FillSdAnimationInfo( SdAnimationInfo* pInfo, PptInteractiveInfoAtom const * pIAtom, const OUString& aMacroName ); + + virtual SdrObject* ProcessObj( SvStream& rSt, DffObjData& rData, SvxMSDffClientData& rClientData, ::tools::Rectangle& rTextRect, SdrObject* pObj ) override; + virtual SdrObject* ApplyTextObj( PPTTextObj* pTextObj, SdrTextObj* pText, SdPageCapsule pPage, + SfxStyleSheet*, SfxStyleSheet** ) const override; + +public: + + OUString ReadSound( sal_uInt32 nSoundRef ) const; + OUString ReadMedia( sal_uInt32 nMediaRef ) const; + + ImplSdPPTImport( SdDrawDocument* pDoc, SotStorage& rStorage, SfxMedium& rMed, PowerPointImportParam& ); + virtual ~ImplSdPPTImport() override; + + bool Import(); + virtual bool ReadFormControl( tools::SvRef& rSrc1, css::uno::Reference< css::form::XFormComponent > & rFormComp ) const override; +}; + +class SdPPTImport +{ + PowerPointImportParam maParam; + std::unique_ptr pFilter; + +public: + + SdPPTImport( SdDrawDocument* pDoc, SvStream& rDocStream, SotStorage& rStorage, SfxMedium& rMed ); + ~SdPPTImport(); + + bool Import(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptinanimations.cxx b/sd/source/filter/ppt/pptinanimations.cxx new file mode 100644 index 000000000..ff49054dd --- /dev/null +++ b/sd/source/filter/ppt/pptinanimations.cxx @@ -0,0 +1,3294 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pptanimations.hxx" +#include "pptinanimations.hxx" +#include "pptatom.hxx" +#include "pptin.hxx" +#include + +#include +#include + +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +namespace ppt +{ + +static SvStream& operator>>(SvStream& rIn, AnimationNode& rNode ) +{ + rIn.ReadInt32( rNode.mnU1 ); + rIn.ReadInt32( rNode.mnRestart ); + rIn.ReadInt32( rNode.mnGroupType ); + rIn.ReadInt32( rNode.mnFill ); + rIn.ReadInt32( rNode.mnU3 ); + rIn.ReadInt32( rNode.mnU4 ); + rIn.ReadInt32( rNode.mnDuration ); + rIn.ReadInt32( rNode.mnNodeType ); + + return rIn; +} + +bool PropertySet::hasProperty( sal_Int32 nProperty ) const +{ + return maProperties.find( nProperty ) != maProperties.end(); +} + +Any PropertySet::getProperty( sal_Int32 nProperty ) const +{ + PropertySetMap_t::const_iterator aIter( maProperties.find( nProperty ) ); + if( aIter != maProperties.end() ) + return (*aIter).second; + else + return Any(); +} + +AnimationImporter::AnimationImporter( ImplSdPPTImport* pPPTImport, SvStream& rStCtrl ) +: mpPPTImport( pPPTImport ), mrStCtrl( rStCtrl ) +{ +} + +int AnimationImporter::import( const Reference< XDrawPage >& xPage, const DffRecordHeader& rProgTagContentHd ) +{ + int nNodes = 0; + +#ifdef DBG_ANIM_LOG + static int ppt_anim_debug_stream_number = 1; + OUString ppt_anim_debug_filename("ppt-animation-import-debug-output-"); + ppt_anim_debug_filename += OUString::number(ppt_anim_debug_stream_number++); + ppt_anim_debug_filename += ".xml"; + mpFile = fopen( OUStringToOString( ppt_anim_debug_filename, RTL_TEXTENCODING_UTF8).getStr() , "w+" ); +#endif + dump("\n"); + + Reference< XAnimationNodeSupplier > xNodeSupplier( xPage, UNO_QUERY ); + if( xNodeSupplier.is() ) + { + mxRootNode = xNodeSupplier->getAnimationNode(); + if( mxRootNode.is() ) + { + Reference< XAnimationNode > xParent; + + std::unique_ptr pAtom(Atom::import( rProgTagContentHd, mrStCtrl )); + if( pAtom ) + { + nNodes = importAnimationContainer( pAtom.get(), xParent ); + } + + std::for_each( maAfterEffectNodes.begin(), maAfterEffectNodes.end(), + sd::stl_process_after_effect_node_func ); + } + } + +#ifdef DBG_ANIM_LOG + fclose( mpFile ); +#endif + + return nNodes; +} + +Reference< XAnimationNode > AnimationImporter::createNode( const Atom* pAtom, const AnimationNode& rNode ) +{ + const char* pServiceName = nullptr; + + switch( rNode.mnGroupType ) + { + case mso_Anim_GroupType_PAR: + if( pAtom->hasChildAtom( DFF_msofbtAnimIteration ) ) + pServiceName = "com.sun.star.animations.IterateContainer"; + else + pServiceName = "com.sun.star.animations.ParallelTimeContainer"; + break; + case mso_Anim_GroupType_SEQ: + pServiceName = "com.sun.star.animations.SequenceTimeContainer"; + break; + case mso_Anim_GroupType_NODE: + { + switch( rNode.mnNodeType ) + { + case mso_Anim_Behaviour_FILTER: + case mso_Anim_Behaviour_ANIMATION: + if( pAtom->hasChildAtom( DFF_msofbtAnimateSet ) ) + pServiceName = "com.sun.star.animations.AnimateSet"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateColor ) ) + pServiceName = "com.sun.star.animations.AnimateColor"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateScale ) ) + pServiceName = "com.sun.star.animations.AnimateTransform"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateRotation ) ) + pServiceName = "com.sun.star.animations.AnimateTransform"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateMotion ) ) + pServiceName = "com.sun.star.animations.AnimateMotion"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimateFilter ) ) + pServiceName = "com.sun.star.animations.TransitionFilter"; + else if( pAtom->hasChildAtom( DFF_msofbtAnimCommand ) ) + pServiceName = "com.sun.star.animations.Command"; + else + pServiceName = "com.sun.star.animations.Animate"; + break; + } + break; + } + case mso_Anim_GroupType_MEDIA: + pServiceName = "com.sun.star.animations.Audio"; + break; + + default: + pServiceName = "com.sun.star.animations.Animate"; + break; + } + + Reference< XAnimationNode > xNode; + if( pServiceName ) + { + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + const OUString aServiceName( OUString::createFromAscii(pServiceName) ); + Reference< XInterface > xFac( xContext->getServiceManager()->createInstanceWithContext(aServiceName, xContext) ); + xNode.set(xFac , UNO_QUERY ); + } + + DBG_ASSERT( xNode.is(), "sd::AnimationImporter::createNode(), node creation failed!" ); + return xNode; +} + +static bool is_random( const AnimationNode& rNode, const PropertySet& rSet, sal_Int32& rPresetClass ) +{ + if( rNode.mnGroupType != mso_Anim_GroupType_PAR ) + return false; + + if( !rSet.hasProperty( DFF_ANIM_PRESET_ID ) || !rSet.hasProperty( DFF_ANIM_PRESET_CLASS ) ) + return false; + + sal_Int32 nPresetId = 0; + if( !(rSet.getProperty( DFF_ANIM_PRESET_ID ) >>= nPresetId) || (nPresetId != 24) ) + return false; + + sal_Int32 nPresetClass = 0; + if( !(rSet.getProperty( DFF_ANIM_PRESET_CLASS ) >>= nPresetClass) ) + return false; + + switch( nPresetClass ) + { + case DFF_ANIM_PRESS_CLASS_ENTRANCE: rPresetClass = EffectPresetClass::ENTRANCE; return true; + case DFF_ANIM_PRESS_CLASS_EXIT: rPresetClass = EffectPresetClass::EXIT; return true; + } + return false; +} + +int AnimationImporter::importAnimationContainer( const Atom* pAtom, const Reference< XAnimationNode >& xParent ) +{ + int nNodes = 0; + if( pAtom->seekToContent() ) + { + AnimationNode aNode; + const Atom* pAnimationNodeAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimNode ); + if( pAnimationNodeAtom && pAnimationNodeAtom->seekToContent() ) + mrStCtrl >> aNode; + + PropertySet aSet; + const Atom* pAnimationPropertySetAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimPropertySet ); + if( pAnimationPropertySetAtom ) + importPropertySetContainer( pAnimationPropertySetAtom, aSet ); + + Reference< XAnimationNode > xNode; + + if( xParent.is() ) + { + sal_Int32 nPresetClass; + if( is_random( aNode, aSet, nPresetClass ) ) + { + // create a random animation node with the given preset class + xNode.set( sd::RandomAnimationNode_createInstance( static_cast(nPresetClass) ), UNO_QUERY ); + } + + if( !xNode.is() ) + { + // create a node for the given atom + xNode = createNode( pAtom, aNode ); + } + } + else + { + // if we have no parent we fill the root node + xNode = mxRootNode; + } + + // import if we have a node and it's not random + if( xNode.is() ) + { + fillNode( xNode, aNode, aSet ); + + switch( aNode.mnGroupType ) + { + case mso_Anim_GroupType_PAR: + { + dump( "\n" ); + + // for iteration containers, map target from children to iteration + Reference< XIterateContainer > xIter( xNode, UNO_QUERY ); + if( xIter.is() ) + { + double fDuration = 0.0; + Any aTarget, aEmpty; + Reference< XEnumerationAccess > xEnumerationAccess( xNode, UNO_QUERY ); + if( xEnumerationAccess.is() ) + { + Reference< XEnumeration > xEnumeration = xEnumerationAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xChildNode( xEnumeration->nextElement(), UNO_QUERY ); + if( xChildNode.is() ) + { + double fChildBegin = 0.0; + double fChildDuration = 0.0; + xChildNode->getBegin() >>= fChildBegin; + xChildNode->getDuration() >>= fChildDuration; + + fChildDuration += fChildBegin; + if( fChildDuration > fDuration ) + fDuration = fChildDuration; + + if( !aTarget.hasValue() ) + aTarget = xChildNode->getTarget(); + + xChildNode->setTarget( aEmpty ); + } + } + } + } + + xIter->setTarget( aTarget ); + + double fIterateInterval = xIter->getIterateInterval() * fDuration / 100; + xIter->setIterateInterval( fIterateInterval ); + } + } + break; + + case mso_Anim_GroupType_SEQ: + { + dump( "\n" ); + + if( aSet.hasProperty( DFF_ANIM_NODE_TYPE ) ) + { + sal_Int32 nPPTNodeType = 0; + if( aSet.getProperty( DFF_ANIM_NODE_TYPE ) >>= nPPTNodeType ) + { + switch(nPPTNodeType) + { + case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE: + oox::ppt::fixMainSequenceTiming( xNode ); + break; + case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ: + oox::ppt::fixInteractiveSequenceTiming( xNode ); + break; + } + } + } + } + break; + + case mso_Anim_GroupType_NODE: + { +#ifdef DBG_ANIM_LOG + if( pAtom->hasChildAtom( DFF_msofbtAnimateSet ) ) + { + dump( "hasChildAtom( DFF_msofbtAnimateColor ) ) + { + dump( "hasChildAtom( DFF_msofbtAnimateScale ) ) + { + dump( "hasChildAtom( DFF_msofbtAnimateRotation ) ) + { + dump( "hasChildAtom( DFF_msofbtAnimateMotion ) ) + { + dump( "hasChildAtom( DFF_msofbtAnimate ) ) + { + dump( "hasChildAtom( DFF_msofbtAnimateFilter ) ) + { + dump( "hasChildAtom( DFF_msofbtAnimCommand ) ) + { + dump( "\n"); + + } + break; + + case mso_Anim_GroupType_MEDIA: + { + dump( "\n" ); + } + break; + + default: + OSL_FAIL( "unknown group atom!" ); + + dump_atom_header( pAtom, true, false ); + dump_atom( pAtom ); + dump_atom_header( pAtom, false, false ); + break; + + } + } + + if( xParent.is() && xNode.is() ) + { + Reference< XTimeContainer > xParentContainer( xParent, UNO_QUERY ); + DBG_ASSERT( xParentContainer.is(), "parent is no container, then why do I have a child here?" ); + if( xParentContainer.is() ) + { + xParentContainer->appendChild( xNode ); + } + } + } + + return nNodes; +} + +bool AnimationImporter::convertAnimationNode( const Reference< XAnimationNode >& xNode, const Reference< XAnimationNode >& xParent ) +{ + Reference< XAnimate > xAnimate( xNode, UNO_QUERY ); + if( !xAnimate.is() ) + return true; + + if( !xAnimate->getTarget().hasValue() ) + return false; + + const sal_Int16 nNodeType = xNode->getType(); + + if( nNodeType == AnimationNodeType::TRANSITIONFILTER ) + return true; + + OUString aAttributeName( xAnimate->getAttributeName() ); + + if( (nNodeType == AnimationNodeType::SET) && aAttributeName == "fill.on" ) + return false; + + const oox::ppt::ImplAttributeNameConversion* p = oox::ppt::getAttributeConversionList(); + + oox::ppt::AnimationAttributeEnum eAttribute = oox::ppt::AnimationAttributeEnum::UNKNOWN; + + if( (nNodeType == AnimationNodeType::ANIMATEMOTION) || + (nNodeType == AnimationNodeType::ANIMATETRANSFORM) ) + { + aAttributeName.clear(); + } + else + { + while( p->mpMSName ) + { + if( aAttributeName.equalsAscii( p->mpMSName ) ) + break; + + p++; + } + + DBG_ASSERT( p->mpMSName || aAttributeName.isEmpty(), "sd::AnimationImporter::convertAnimationNode(), unknown attribute!" ); +#ifdef DBG_ANIM_LOG + if( p->mpMSName == 0 ) dump( "\n" ); +#endif + + eAttribute = p->meAttribute; + + if( p->mpAPIName ) + aAttributeName = OUString::createFromAscii( p->mpAPIName ); + } + + xAnimate->setAttributeName( aAttributeName ); + + if(eAttribute != oox::ppt::AnimationAttributeEnum::UNKNOWN) + { + Any aAny( xAnimate->getFrom() ); + if( aAny.hasValue() ) + { + if(oox::ppt::convertAnimationValue(eAttribute, aAny)) + xAnimate->setFrom( aAny ); + } + + aAny = xAnimate->getBy(); + if( aAny.hasValue() ) + { + if(oox::ppt::convertAnimationValue(eAttribute, aAny)) + xAnimate->setBy( aAny ); + } + + aAny = xAnimate->getTo(); + if( aAny.hasValue() ) + { + if(oox::ppt::convertAnimationValue(eAttribute, aAny)) + xAnimate->setTo( aAny ); + } + + Sequence< Any > aValues( xAnimate->getValues() ); + if( aValues.hasElements() ) + { + for( Any& rValue : asNonConstRange(aValues) ) + oox::ppt::convertAnimationValue(eAttribute, rValue); + + xAnimate->setValues( aValues ); + } + + OUString aFormula( xAnimate->getFormula() ); + if( !aFormula.isEmpty() ) + { + if(oox::ppt::convertMeasure(aFormula)) + xAnimate->setFormula( aFormula ); + } + } + + // check for after-effect + Sequence< NamedValue > aUserData( xNode->getUserData() ); + NamedValue* pLastValue = aUserData.getArray(); + sal_Int32 nRemoved = 0; + + bool bAfterEffect = false; + sal_Int32 nMasterRel = 0; + for( const NamedValue& rValue : std::as_const(aUserData) ) + { + if ( rValue.Name == "after-effect" ) + { + rValue.Value >>= bAfterEffect; + nRemoved++; + } + else if ( rValue.Name == "master-rel" ) + { + rValue.Value >>= nMasterRel; + nRemoved++; + } + else + { + if( nRemoved ) + *pLastValue = rValue; + pLastValue++; + } + } + + if( nRemoved ) + { + aUserData.realloc( aUserData.getLength() - nRemoved ); + xNode->setUserData( aUserData ); + } + + // if it's an after effect node, add it to the list for + // later processing + // after effect nodes are not inserted at their import + // position, so return false in this case + if( bAfterEffect ) + { + if( nMasterRel != 2 ) + { + Event aEvent; + + aEvent.Source <<= xParent; + aEvent.Trigger = EventTrigger::END_EVENT; + aEvent.Repeat = 0; + + xNode->setBegin( Any( aEvent ) ); + } + + // add to after effect nodes for later processing + sd::AfterEffectNode aNode( xNode, xParent, nMasterRel == 2 ); + maAfterEffectNodes.push_back( aNode ); + return false; + } + + return true; +} + +void AnimationImporter::fillNode( Reference< XAnimationNode > const & xNode, const AnimationNode& rNode, const PropertySet& rSet ) +{ + bool bAfterEffect = false; + + // attribute Restart + if( rNode.mnRestart ) + { + sal_Int16 nRestart = AnimationRestart::DEFAULT; + switch( rNode.mnRestart ) + { + case 1: nRestart = AnimationRestart::ALWAYS; break; + case 2: nRestart = AnimationRestart::WHEN_NOT_ACTIVE; break; + case 3: nRestart = AnimationRestart::NEVER; break; + } + xNode->setRestart( nRestart ); + } + + // attribute Fill + if( rNode.mnFill ) + { + sal_Int16 nFill = AnimationFill::DEFAULT; + switch( rNode.mnFill ) + { + case 1: nFill = AnimationFill::REMOVE; break; + case 2: nFill = AnimationFill::FREEZE; break; + case 3: nFill = AnimationFill::HOLD; break; + case 4: nFill = AnimationFill::TRANSITION; break; + } + xNode->setFill( nFill ); + } + + // attribute Duration + if( rNode.mnDuration ) + { + Any aDuration; + if( rNode.mnDuration > 0 ) + { + aDuration <<= rNode.mnDuration / 1000.0; + } + else if( rNode.mnDuration < 0 ) + { + aDuration <<= Timing_INDEFINITE; + } + xNode->setDuration( aDuration ); + } + + // TODO: DFF_ANIM_PATH_EDIT_MODE + + // set user data + Sequence< NamedValue > aUserData; + + // attribute Type + if( rSet.hasProperty( DFF_ANIM_NODE_TYPE ) ) + { + sal_Int32 nPPTNodeType = 0; + if( rSet.getProperty( DFF_ANIM_NODE_TYPE ) >>= nPPTNodeType ) + { + sal_Int16 nNodeType = css::presentation::EffectNodeType::DEFAULT; + switch( nPPTNodeType ) + { + case DFF_ANIM_NODE_TYPE_CLICK_PARALLEL: [[fallthrough]]; + case DFF_ANIM_NODE_TYPE_ON_CLICK: nNodeType = css::presentation::EffectNodeType::ON_CLICK; break; + case DFF_ANIM_NODE_TYPE_WITH_GROUP: [[fallthrough]]; + case DFF_ANIM_NODE_TYPE_WITH_PREVIOUS: nNodeType = css::presentation::EffectNodeType::WITH_PREVIOUS; break; + case DFF_ANIM_NODE_TYPE_AFTER_GROUP: [[fallthrough]]; + case DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS: nNodeType = css::presentation::EffectNodeType::AFTER_PREVIOUS; break; + case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE: nNodeType = css::presentation::EffectNodeType::MAIN_SEQUENCE; break; + case DFF_ANIM_NODE_TYPE_TIMING_ROOT: nNodeType = css::presentation::EffectNodeType::TIMING_ROOT; break; + case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ:nNodeType = css::presentation::EffectNodeType::INTERACTIVE_SEQUENCE; break; + } + + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "node-type"; + pUserData[nSize].Value <<= nNodeType; + } + } + + if( rSet.hasProperty( DFF_ANIM_GROUP_ID ) ) + { + sal_Int32 nGroupId; + if( rSet.getProperty( DFF_ANIM_GROUP_ID ) >>= nGroupId ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "group-id"; + pUserData[nSize].Value <<= nGroupId; + } + } + + sal_Int16 nEffectPresetClass = EffectPresetClass::CUSTOM; + sal_Int32 nPresetId = 0; + + if( rSet.hasProperty( DFF_ANIM_PRESET_CLASS ) ) + { + sal_Int32 nPresetClass = 0; + if ( rSet.getProperty( DFF_ANIM_PRESET_CLASS ) >>= nPresetClass ) + { + switch( nPresetClass ) + { + case DFF_ANIM_PRESS_CLASS_ENTRANCE: nEffectPresetClass = EffectPresetClass::ENTRANCE; break; + case DFF_ANIM_PRESS_CLASS_EXIT: nEffectPresetClass = EffectPresetClass::EXIT; break; + case DFF_ANIM_PRESS_CLASS_EMPHASIS: nEffectPresetClass = EffectPresetClass::EMPHASIS; break; + case DFF_ANIM_PRESS_CLASS_MOTIONPATH: nEffectPresetClass = EffectPresetClass::MOTIONPATH; break; + case DFF_ANIM_PRESS_CLASS_OLE_ACTION: nEffectPresetClass = EffectPresetClass::OLEACTION; break; + case DFF_ANIM_PRESS_CLASS_MEDIACALL: nEffectPresetClass = EffectPresetClass::MEDIACALL; break; + } + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "preset-class"; + pUserData[nSize].Value <<= nEffectPresetClass; + } + } + + if( rSet.hasProperty( DFF_ANIM_PRESET_ID ) ) + { + if( rSet.getProperty( DFF_ANIM_PRESET_ID ) >>= nPresetId ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "preset-id"; + + const oox::ppt::preset_mapping* p = oox::ppt::preset_mapping::getList(); + while( p->mpStrPresetId && ((p->mnPresetClass != nEffectPresetClass) || (p->mnPresetId != nPresetId )) ) + p++; + + if( p->mpStrPresetId ) + { + pUserData[nSize].Value <<= OUString::createFromAscii( p->mpStrPresetId ); + } + else + { + OUStringBuffer sBuffer; + sBuffer.append( "ppt_" ); + switch( nEffectPresetClass ) + { + case EffectPresetClass::ENTRANCE: sBuffer.append( "entrance_" ); break; + case EffectPresetClass::EXIT: sBuffer.append( "exit_" ); break; + case EffectPresetClass::EMPHASIS: sBuffer.append( "emphasis_" ); break; + case EffectPresetClass::MOTIONPATH: sBuffer.append( "motionpath_" ); break; + case EffectPresetClass::OLEACTION: sBuffer.append( "oleaction_" ); break; + case EffectPresetClass::MEDIACALL: sBuffer.append( "mediacall_" ); break; + } + sBuffer.append( nPresetId ); + + pUserData[nSize].Value <<= sBuffer.makeStringAndClear(); + } + } + } + + if( rSet.hasProperty( DFF_ANIM_PRESET_SUB_TYPE ) ) + { + sal_Int32 nPresetSubType = 0; + if( rSet.getProperty( DFF_ANIM_PRESET_SUB_TYPE ) >>= nPresetSubType ) + { + if( nPresetSubType ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "preset-sub-type"; + pUserData[nSize].Value <<= oox::ppt::getConvertedSubType( nEffectPresetClass, nPresetId, nPresetSubType ); + } + } + } + + if( rSet.hasProperty( DFF_ANIM_AFTEREFFECT ) ) + { + if( rSet.getProperty( DFF_ANIM_AFTEREFFECT ) >>= bAfterEffect ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "after-effect"; + pUserData[nSize].Value <<= bAfterEffect; + } + } + + if( bAfterEffect && rSet.hasProperty( DFF_ANIM_MASTERREL ) ) + { + sal_Int32 nMasterRel = 2; + if( rSet.getProperty( DFF_ANIM_MASTERREL ) >>= nMasterRel ) + { + sal_Int32 nSize = aUserData.getLength(); + aUserData.realloc(nSize+1); + auto pUserData = aUserData.getArray(); + pUserData[nSize].Name = "master-rel"; + pUserData[nSize].Value <<= nMasterRel; + } + } + + xNode->setUserData( aUserData ); + + // TODO: DFF_ANIM_ID + if( rSet.hasProperty( DFF_ANIM_ID ) ) + { + OUString aString; + rSet.getProperty( DFF_ANIM_ID ) >>= aString; + //if( !aString.isEmpty() ) + //{ + //} + } + + // TODO: DFF_ANIM_EVENT_FILTER + if( rSet.hasProperty( DFF_ANIM_EVENT_FILTER ) ) + { + OUString aString; + rSet.getProperty( DFF_ANIM_EVENT_FILTER ) >>= aString; + //if( !aString.isEmpty() ) + //{ + //} + } + + // DFF_ANIM_TIMEFILTER + if( rSet.hasProperty( DFF_ANIM_TIMEFILTER ) ) + { + Reference< XAnimate > xAnim( xNode, UNO_QUERY ); + if( xAnim.is() ) + { + OUString aString; + rSet.getProperty( DFF_ANIM_TIMEFILTER ) >>= aString; + if( !aString.isEmpty() ) + { + sal_Int32 nElements = 1; // a non empty string has at least one value + + sal_Int32 fromIndex = 0; + while(true) + { + fromIndex = aString.indexOf( ';', fromIndex ); + if( fromIndex == -1 ) + break; + + fromIndex++; + nElements++; + } + + Sequence< TimeFilterPair > aTimeFilter( nElements ); + + TimeFilterPair* pValues = aTimeFilter.getArray(); + sal_Int32 nIndex = 0; + while( (nElements--) && (nIndex >= 0) ) + { + const std::u16string_view aToken( o3tl::getToken(aString, 0, ';', nIndex ) ); + + size_t nPos = aToken.find( ',' ); + if( nPos != std::u16string_view::npos ) + { + pValues->Time = o3tl::toDouble(aToken.substr( 0, nPos )); + pValues->Progress = o3tl::toDouble(aToken.substr( nPos+1 )); + } + pValues++; + } + + xAnim->setTimeFilter( aTimeFilter ); + } + } + } + +// TODO: DFF_ANIM_ENDAFTERSLIDE / DFF_ANIM_VOLUME handling. git history has sample code + Reference< XAnimateColor > xColor( xNode, UNO_QUERY ); + if( !xColor.is() ) + return; + + if( rSet.hasProperty( DFF_ANIM_DIRECTION ) ) + { + bool bDirection = false; + if( rSet.getProperty( DFF_ANIM_DIRECTION ) >>= bDirection ) + xColor->setDirection( !bDirection ); + } + + if( rSet.hasProperty( DFF_ANIM_COLORSPACE ) ) + { + sal_Int32 nColorSpace = 0; + rSet.getProperty( DFF_ANIM_COLORSPACE ) >>= nColorSpace; + xColor->setColorInterpolation( (nColorSpace == 0) ? AnimationColorSpace::RGB : AnimationColorSpace::HSL ); + } +} + +int AnimationImporter::importTimeContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + int nNodes = 0; + + DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importTimeContainer()!"); + if( pAtom && xNode.is() ) + { + importAnimationEvents( pAtom, xNode ); + importAnimationValues( pAtom, xNode ); + importAnimationActions( pAtom, xNode ); + + dump(">\n"); + + // import sub containers + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimNode: + case DFF_msofbtAnimEvent: + case DFF_msofbtAnimValue: + case DFF_msofbtAnimAction: + case DFF_msofbtAnimPropertySet: + break; + + case DFF_msofbtAnimSubGoup : + { + if( pChildAtom->hasChildAtom( DFF_msofbtAnimCommand ) ) + { + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference< XAnimationNode > xChildNode( Command::create(xContext), UNO_QUERY_THROW ); + nNodes += importAnimationNodeContainer( pChildAtom, xChildNode ); + Reference< XTimeContainer > xParentContainer( xNode, UNO_QUERY ); + if( xParentContainer.is() && xChildNode.is() ) + xParentContainer->appendChild( xChildNode ); + } + else + { + nNodes += importAnimationContainer( pChildAtom, xNode ); + } + } + break; + case DFF_msofbtAnimGroup : + { + nNodes += importAnimationContainer( pChildAtom, xNode ); + } + break; + case DFF_msofbtAnimIteration: + { + if( pChildAtom->seekToContent() ) + { + float fInterval(0.0); + sal_Int32 nTextUnitEffect(0), nU1(0), nU2(0), nU3(0); + + mrStCtrl.ReadFloat( fInterval ).ReadInt32( nTextUnitEffect ).ReadInt32( nU1 ).ReadInt32( nU2 ).ReadInt32( nU3 ); + + Reference< XIterateContainer > xIter( xNode, UNO_QUERY ); + if( xIter.is() ) + { + sal_Int16 nIterateType = TextAnimationType::BY_PARAGRAPH; + switch( nTextUnitEffect ) + { + case 1: nIterateType = TextAnimationType::BY_WORD; break; + case 2: nIterateType = TextAnimationType::BY_LETTER; break; + } + xIter->setIterateType( nIterateType ); + xIter->setIterateInterval( static_cast(fInterval) ); + } + + nNodes++; + + dump( "\n", nU3 ); + } + } + break; + + case 0xf136: + { +#ifdef DBG_ANIM_LOG + sal_uInt32 nU1, nU2; + mrStCtrl.ReadUInt32(nU1).ReadUInt32(nU2); + + fprintf( mpFile, "\n", nU1, nU2 ); +#endif + } + break; + + default: + { + dump_atom_header( pChildAtom, true, false ); + dump_atom( pChildAtom ); + dump_atom_header( pChildAtom, false, false ); + } + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + } + + return nNodes; +} + +int AnimationImporter::importAnimationNodeContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + int nNodes = 0; + + DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importAnimationNodeContainer()!"); + if( pAtom && xNode.is() ) + { + importAnimationEvents( pAtom, xNode ); + importAnimationValues( pAtom, xNode ); + importAnimationActions( pAtom, xNode ); + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + nNodes ++; + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimNode: + case DFF_msofbtAnimEvent: + case DFF_msofbtAnimValue: + case DFF_msofbtAnimAction: + case DFF_msofbtAnimPropertySet: + break; + + case DFF_msofbtAnimateFilter: + importAnimateFilterContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateSet: + importAnimateSetContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimate: + importAnimateContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateScale: + importAnimateScaleContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateColor: + importAnimateColorContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateRotation: + importAnimateRotationContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimateMotion: + importAnimateMotionContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimCommand: + importCommandContainer( pChildAtom, xNode ); + break; + + default: + { + nNodes --; + dump_atom_header( pChildAtom, true, false ); + dump_atom( pChildAtom ); + dump_atom_header( pChildAtom, false, false ); + } + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + } + + return nNodes; +} + +void AnimationImporter::importAnimateFilterContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XTransitionFilter > xFilter( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateFilter && xFilter.is(), "invalid call to ppt::AnimationImporter::importAnimateFilterContainer()!"); + if( !(pAtom && xFilter.is()) ) + return; + + sal_uInt32 nBits = 0; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateFilterData: + { + sal_uInt32 transition(0); + mrStCtrl.ReadUInt32( nBits ); + mrStCtrl.ReadUInt32( transition ); + + if( nBits & 1 ) + xFilter->setMode( transition == 0 ); + + dump( " transition=\"%s\"", (transition == 0) ? "in" : "out" ); + } + break; + + case DFF_msofbtAnimAttributeValue: + { + if( (nBits & 2 ) && ( pChildAtom->getInstance() == 1 ) ) + { + Any aAny; + if ( importAttributeValue( pChildAtom, aAny ) ) + { + OUString filter; + aAny >>= filter; + + dump( " filter=\"%s\"", filter ); + + const oox::ppt::transition* pTransition = oox::ppt::transition::find( filter ); + if( pTransition ) + { + xFilter->setTransition( pTransition->mnType ); + xFilter->setSubtype( pTransition->mnSubType ); + xFilter->setDirection( pTransition->mbDirection ); + } + else + { + OSL_FAIL( "unknown transition!" ); + } + } + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateAttributeTargetContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateTarget, "invalid call to ppt::AnimationImporter::importAnimateAttributeTargetContainer()!"); + + Any aTarget; + + Reference< XAnimate > xAnimate( xNode, UNO_QUERY ); + + bool bWrongContext = false; + + if( pAtom ) + { + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimPropertySet: + { + PropertySet aSet; + importPropertySetContainer( pChildAtom, aSet ); + if( aSet.hasProperty( DFF_ANIM_RUNTIMECONTEXT ) ) + { + OUString aContext; + if( aSet.getProperty( DFF_ANIM_RUNTIMECONTEXT ) >>= aContext ) + { + if( aContext != "PPT" ) + bWrongContext = true; + } + } + + dump( aSet ); + } + break; + + case DFF_msofbtAnimateTargetSettings: + { + if( xAnimate.is() ) + { + sal_uInt32 nBits(0); + sal_uInt32 nAdditive(0); + sal_uInt32 nAccumulate(0); + sal_uInt32 nTransformType(0); + + mrStCtrl.ReadUInt32( nBits ).ReadUInt32( nAdditive ).ReadUInt32( nAccumulate ).ReadUInt32( nTransformType ); + + // nBits %0001: additive, %0010: accumulate, %0100: attributeName, %1000: transformtype + // nAdditive 0 = base, 1 = sum, 2 = replace, 3 = multiply, 4 = none + // nAccumulate 0 = none, 1 = always + // nTransformType 0: "property" else "image" + + if( nBits & 3 && xAnimate.is() ) + { + if( nBits & 1 ) + { + sal_Int16 nTemp = AnimationAdditiveMode::BASE; + switch( nAdditive ) + { + case 1: nTemp = AnimationAdditiveMode::SUM; break; + case 2: nTemp = AnimationAdditiveMode::REPLACE; break; + case 3: nTemp = AnimationAdditiveMode::MULTIPLY; break; + case 4: nTemp = AnimationAdditiveMode::NONE; break; + } + xAnimate->setAdditive( nTemp ); + } + + if( nBits & 2 ) + { + xAnimate->setAccumulate( nAccumulate == 0 ); + } + } +#ifdef DBG_ANIM_LOG + if( nBits & 1 ) + fprintf( mpFile, " additive=\"%s\"", (nAdditive == 0) ? "base" : (nAdditive == 2) ? "replace" : (nAdditive == 1) ? "sum" : (nAdditive == 3 ) ? "multiply" : (nAdditive == 4) ? "none" : "unknown" ); + + if( nBits & 2 ) + fprintf( mpFile, " accumulate=\"%s\"", (nAccumulate == 0) ? "none" : "always" ); + + if( nBits & 8 ) + fprintf( mpFile, " transformType=\"%s\"", (nTransformType == 0) ? "property" : "image" ); +#endif + } + } + break; + + case DFF_msofbtAnimateAttributeNames: + { + if( xAnimate.is() ) + { + OUString aAttributeName; + importAttributeNamesContainer( pChildAtom, aAttributeName ); + if( xAnimate.is() ) + xAnimate->setAttributeName( aAttributeName ); + dump( " attributeName=\"%s\"", aAttributeName ); + } + } + break; + + case DFF_msofbtAnimateTargetElement: + { + sal_Int16 nSubType; + importTargetElementContainer( pChildAtom, aTarget, nSubType ); + if( xAnimate.is() ) + xAnimate->setSubItem( nSubType ); + + dump( " target=\"" ); + dump_target( aTarget ); + dump( "\"" ); + } + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + } + + if( bWrongContext ) + aTarget.clear(); + + if( xAnimate.is() ) + xAnimate->setTarget( aTarget ); + else + { + Reference< XCommand > xCommand( xNode, UNO_QUERY ); + if( xCommand.is() ) + xCommand->setTarget( aTarget ); + } +} + +sal_Int16 AnimationImporter::implGetColorSpace( sal_Int32 nMode, sal_Int32 /*nA*/, sal_Int32 /*nB*/, sal_Int32 /*nC*/ ) +{ + switch( nMode ) + { + case 2: // index + default: + case 0: // rgb + return AnimationColorSpace::RGB; + + case 1: // hsl + return AnimationColorSpace::HSL; + } +} + +Any AnimationImporter::implGetColorAny( sal_Int32 nMode, sal_Int32 nA, sal_Int32 nB, sal_Int32 nC ) +{ + switch( nMode ) + { + case 0: // rgb + { + dump( "rgb(%ld", nA ); + dump( ",%ld", nB ); + dump( ",%ld)", nC ); + Color aColor( static_cast(nA), static_cast(nB), static_cast(nC) ); + return Any( static_cast(aColor.GetRGBColor()) ); + } + case 1: // hsl + { + dump( "hsl(%ld", nA ); + dump( ",%ld", nB ); + dump( ",%ld)", nC ); + Sequence< double > aHSL{ nA * 360.0/255.0, + nB / 255.0, + nC / 255.0 }; + return Any( aHSL ); + } + + case 2: // index + { + Color aColor; + mpPPTImport->GetColorFromPalette(static_cast(nA), aColor ); + dump( "index(%ld", nA ); + dump( " [%ld", static_cast(aColor.GetRed()) ); + dump( ",%ld", static_cast(aColor.GetGreen()) ); + dump( ",%ld])", static_cast(aColor.GetBlue()) ); + return Any( static_cast(aColor.GetRGBColor()) ); + } + + default: + { + dump( "unknown_%ld(", nMode ); + dump( "%ld", nA ); + dump( ",%ld", nB ); + dump( ",%ld)", nC ); + OSL_FAIL( "ppt::implGetColorAny(), unhandled color type" ); + + Any aAny; + return aAny; + } + } +} + +void AnimationImporter::importAnimateColorContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateColor > xColor( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateColor && xColor.is(), "invalid call to ppt::AnimationImporter::importAnimateColorContainer()!"); + if( !(pAtom && xColor.is()) ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateColorData: + { + sal_uInt32 nBits; + sal_Int32 nByMode, nByA, nByB, nByC; + sal_Int32 nFromMode, nFromA, nFromB, nFromC; + sal_Int32 nToMode, nToA, nToB, nToC; + mrStCtrl.ReadUInt32( nBits ); + mrStCtrl.ReadInt32( nByMode ).ReadInt32( nByA ).ReadInt32( nByB ).ReadInt32( nByC ); + mrStCtrl.ReadInt32( nFromMode ).ReadInt32( nFromA ).ReadInt32( nFromB ).ReadInt32( nFromC ); + mrStCtrl.ReadInt32( nToMode ).ReadInt32( nToA ).ReadInt32( nToB ).ReadInt32( nToC ); + + if (!mrStCtrl.good()) + { + SAL_WARN("filter.ms", "DFF_msofbtAnimateColorData: short read"); + break; + } + + if( nBits & 1 ) + { + dump( " by=\"" ); + xColor->setBy( implGetColorAny( nByMode, nByA, nByB, nByC ) ); + xColor->setColorInterpolation( implGetColorSpace( nByMode, nByA, nByB, nByC ) ); + dump( "\""); + } + + if( nBits & 2 ) + { + dump( " from=\"" ); + xColor->setFrom( implGetColorAny( nFromMode, nFromA, nFromB, nFromC ) ); + xColor->setColorInterpolation( implGetColorSpace( nFromMode, nFromA, nFromB, nFromC ) ); + dump( "\""); + } + + if( nBits & 4 ) + { + dump( " to=\"" ); + xColor->setTo( implGetColorAny( nToMode, nToA, nToB, nToC ) ); + xColor->setColorInterpolation( implGetColorSpace( nToMode, nToA, nToB, nToC ) ); + dump( "\""); + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateSetContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateSet > xSet( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateSet && xSet.is(), "invalid call to ppt::AnimationImporter::importAnimateSetContainer()!"); + if( !(pAtom && xSet.is()) ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateSetData: + { + sal_Int32 nU1, nU2; + mrStCtrl.ReadInt32( nU1 ).ReadInt32( nU2 ); + + dump( " set_1=\"%ld\"", nU1 ); + dump( " set_2=\"%ld\"", nU2 ); + } + break; + + case DFF_msofbtAnimAttributeValue: + { + Any aTo; + if ( importAttributeValue( pChildAtom, aTo ) ) + { + xSet->setTo( aTo ); + + dump( " value=\"" ); + dump( aTo ); + dump( "\"" ); + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimate > xAnim( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimate && xAnim.is(), "invalid call to ppt::AnimationImporter::importAnimateContainer()!"); + if( !(pAtom && xAnim.is()) ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateData: + { + sal_uInt32 nCalcmode(0), nBits(0), nValueType(0); + mrStCtrl.ReadUInt32( nCalcmode ).ReadUInt32( nBits ).ReadUInt32( nValueType ); + + if( nBits & 0x08 ) + { + sal_Int16 n = (nCalcmode == 1) ? AnimationCalcMode::LINEAR : /* (nCalcmode == 2) ? AnimationCalcMode::FORMULA : */ AnimationCalcMode::DISCRETE; + xAnim->setCalcMode( n ); + dump( " calcmode=\"%s\"", (nCalcmode == 0) ? "discrete" : (nCalcmode == 1) ? "linear" : (nCalcmode == 2) ? "formula" : "unknown" ); + } + + if( nBits & 0x30 ) + { + sal_Int16 n = (nValueType == 1) ? AnimationValueType::NUMBER : (nValueType == 2 ) ? AnimationValueType::COLOR : AnimationValueType::STRING; + xAnim->setValueType( n ); + dump( " valueType=\"%s\"", (nValueType == 0) ? "string" : (nValueType == 1) ? "number" : (nValueType == 2) ? "color" : "unknown" ); + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimKeyPoints: + importAnimateKeyPoints( pChildAtom, xNode ); + break; + + case DFF_msofbtAnimAttributeValue: + { + Any a; + if ( importAttributeValue( pChildAtom, a ) ) + { + switch( pChildAtom->getInstance() ) + { + case 1: xAnim->setBy( a ); dump( " by=\"" ); break; + case 2: xAnim->setFrom( a ); dump( " from=\"" ); break; + case 3: xAnim->setTo( a ); dump( " to=\"" ); break; + default: + dump( " unknown_value=\"" ); + } + + dump( a ); + dump( "\"" ); + } + } + break; + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateMotionContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateMotion > xMotion( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateMotion && xMotion.is(), "invalid call to ppt::AnimationImporter::importAnimateMotionContainer()!"); + if( !(pAtom && xMotion.is()) ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateMotionData: + { + sal_uInt32 nBits, nOrigin; + float fByX, fByY, fFromX, fFromY, fToX, fToY; + + mrStCtrl.ReadUInt32( nBits ).ReadFloat( fByX ).ReadFloat( fByY ).ReadFloat( fFromX ).ReadFloat( fFromY ).ReadFloat( fToX ).ReadFloat( fToY ).ReadUInt32( nOrigin ); + +#ifdef DBG_ANIM_LOG + if( nBits & 1 ) + fprintf( mpFile, " by=\"%g,%g\"", (double)fByX, (double)fByY ); + + if( nBits & 2 ) + fprintf( mpFile, " from=\"%g,%g\"", (double)fFromX, (double)fFromY ); + + if( nBits & 4 ) + fprintf( mpFile, " to=\"%g,%g\"", (double)fToX, (double)fToY ); + + if( nBits & 8 ) + fprintf( mpFile, " origin=\"%s\"", (nOrigin == 1) ? "parent" : (nOrigin == 2) ? "layout" : "unknown" ); + +#endif + } + break; + + case DFF_msofbtAnimAttributeValue: + { + Any aPath; + if ( importAttributeValue( pChildAtom, aPath ) ) + { + OUString aStr; + if ( aPath >>= aStr ) + { + // E can appear inside a number, so we only check for its presence at the end + aStr = aStr.trim(); + if (aStr.endsWith("E")) + aStr = aStr.copy(0, aStr.getLength() - 1); + aStr = aStr.trim(); + aPath <<= aStr; + xMotion->setPath( aPath ); + dump( " path=\"" ); + dump( aPath ); + dump( "\"" ); + } + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importCommandContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XCommand > xCommand( xNode, UNO_QUERY ); + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimCommand && xCommand.is(), "invalid call to ppt::AnimationImporter::importCommandContainer()!"); + if( !(pAtom && xCommand.is()) ) + return; + + sal_Int32 nBits = 0; + Any aValue; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtCommandData: + { + sal_Int32 nCommandType; + // looks like U1 is a bitset, bit 1 enables the type and bit 2 enables + // a propertyvalue that follows + mrStCtrl.ReadInt32( nBits ); + mrStCtrl.ReadInt32( nCommandType ); + + if( nBits & 1 ) + { + dump( " type=\"%s\"", (nCommandType == 0) ? "event" : ( nCommandType == 1) ? "call" : "verb" ); + } + } + break; + + case DFF_msofbtAnimAttributeValue: + { + if ( importAttributeValue( pChildAtom, aValue ) ) + { + if( nBits & 2 ) + { + dump( " cmd=\"" ); + dump( aValue ); + dump( "\"" ); + } + } + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + + if( !(nBits & 3) ) + return; + + OUString aParam; + aValue >>= aParam; + + sal_Int16 nCommand = EffectCommands::CUSTOM; + + NamedValue aParamValue; + + if ( aParam == "onstopaudio" ) + { + nCommand = EffectCommands::STOPAUDIO; + } + else if ( aParam == "play" ) + { + nCommand = EffectCommands::PLAY; + } + else if( aParam.startsWith( "playFrom" ) ) + { + const std::u16string_view aMediaTime( aParam.subView( 9, aParam.getLength() - 10 ) ); + rtl_math_ConversionStatus eStatus; + double fMediaTime = ::rtl::math::stringToDouble( aMediaTime, u'.', u',', &eStatus ); + if( eStatus == rtl_math_ConversionStatus_Ok ) + { + aParamValue.Name = "MediaTime"; + aParamValue.Value <<= fMediaTime; + } + nCommand = EffectCommands::PLAY; + } + else if ( aParam == "togglePause" ) + { + nCommand = EffectCommands::TOGGLEPAUSE; + } + else if ( aParam == "stop" ) + { + nCommand = EffectCommands::STOP; + } + + xCommand->setCommand( nCommand ); + if( nCommand == EffectCommands::CUSTOM ) + { + OSL_FAIL("sd::AnimationImporter::importCommandContainer(), unknown command!"); + aParamValue.Name = "UserDefined"; + aParamValue.Value <<= aParam; + } + + if( aParamValue.Value.hasValue() ) + { + Sequence< NamedValue > aParamSeq( &aParamValue, 1 ); + xCommand->setParameter( Any( aParamSeq ) ); + } +} + +int AnimationImporter::importAudioContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + int nNodes = 0; + + Reference< XAudio > xAudio( xNode, UNO_QUERY ); + DBG_ASSERT( pAtom && xAudio.is() && + ( (pAtom->getType() == DFF_msofbtAnimGroup) || + (pAtom->getType() == DFF_msofbtAnimSubGoup) ), "invalid call to ppt::AnimationImporter::importAudioContainer()!"); + if( pAtom && xAudio.is() ) + { + importAnimationEvents( pAtom, xNode ); + importAnimationValues( pAtom, xNode ); + importAnimationActions( pAtom, xNode ); + + dump(">\n"); + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimNode: + case DFF_msofbtAnimEvent: + case DFF_msofbtAnimValue: + case DFF_msofbtAnimAction: + case DFF_msofbtAnimPropertySet: + break; + + case DFF_msofbtAnimAttributeValue: + { + Any aValue; + if ( importAttributeValue( pChildAtom, aValue ) ) + { + nNodes ++; + dump( " value=\"" ); + dump( aValue ); + dump( "\"" ); + } + } + break; + + case DFF_msofbtAnimateTargetElement: + { + sal_Int16 nSubType; + Any aSource; + importTargetElementContainer( pChildAtom, aSource, nSubType ); + if( xAudio.is() ) { + xAudio->setSource( aSource ); + nNodes ++; + } + } + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + + // TODO: What to do with them? + Any aEmpty; + xAudio->setBegin( aEmpty ); + xAudio->setEnd( aEmpty ); + } + + return nNodes; +} + +void AnimationImporter::importAnimateScaleContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateTransform > xTransform( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateScale && xTransform.is(), "invalid call to ppt::AnimationImporter::importAnimateScaleContainer()!"); + if( !(pAtom && xTransform.is()) ) + return; + + xTransform->setTransformType( AnimationTransformType::SCALE ); + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateScaleData: + { + sal_uInt32 nBits(0), nZoomContents(0); + float fByX(0.0), fByY(0.0), fFromX(0.0), fFromY(0.0), fToX(0.0), fToY(0.0); + + // nBits %001: by, %010: from, %100: to, %1000: zoomContents(bool) + mrStCtrl.ReadUInt32( nBits ).ReadFloat( fByX ).ReadFloat( fByY ).ReadFloat( fFromX ).ReadFloat( fFromY ).ReadFloat( fToX ).ReadFloat( fToY ).ReadUInt32( nZoomContents ); + + ValuePair aPair; + // 'from' value + if( nBits & 2 ) + { + aPair.First <<= static_cast(fFromX) / 100.0; + aPair.Second <<= static_cast(fFromY) / 100.0; + xTransform->setFrom( Any( aPair ) ); + } + + // 'to' value + if( nBits & 4 ) + { + aPair.First <<= static_cast(fToX) / 100.0; + aPair.Second <<= static_cast(fToY) / 100.0; + xTransform->setTo( Any( aPair ) ); + } + + // 'by' value + if( nBits & 1 ) + { + aPair.First <<= static_cast(fByX) / 100.0; + aPair.Second <<= static_cast(fByY) / 100.0; + + if( nBits & 2 ) + { + // 'from' value given, import normally + xTransform->setBy( Any( aPair ) ); + } + else + { + // mapping 'by' to 'to', if no 'from' is + // given. This is due to a non-conformity in + // PPT, which exports animateScale effects + // with a sole 'by' value, but with the + // semantics of a sole 'to' animation + xTransform->setTo( Any( aPair ) ); + } + } + +#ifdef DBG_ANIM_LOG + if( nBits & 1 ) + fprintf( mpFile, " by=\"%g,%g\"", (double)fByX, (double)fByY ); + + if( nBits & 2 ) + fprintf( mpFile, " from=\"%g,%g\"", (double)fFromX, (double)fFromY ); + + if( nBits & 4 ) + fprintf( mpFile, " to=\"%g,%g\"", (double)fToX, (double)fToY ); + + if( nBits & 8 ) + fprintf( mpFile, " zoomContents=\"%s\"", nZoomContents ? "true" : "false" ); +#endif + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAnimateRotationContainer( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimateTransform > xTransform( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimateRotation && xTransform.is(), "invalid call to ppt::AnimationImporter::importAnimateRotationContainer()!"); + if( !(pAtom && xTransform.is()) ) + return; + + xTransform->setTransformType( AnimationTransformType::ROTATE ); + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + + while( pChildAtom ) + { + if( !pChildAtom->isContainer() ) + { + if( !pChildAtom->seekToContent() ) + break; + } + + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimateRotationData: + { + sal_uInt32 nBits(0), nU1(0); + float fBy(0.0), fFrom(0.0), fTo(0.0); + + // nBits %001: by, %010: from, %100: to, %1000: zoomContents(bool) + mrStCtrl.ReadUInt32( nBits ).ReadFloat( fBy ).ReadFloat( fFrom ).ReadFloat( fTo ).ReadUInt32( nU1 ); + + if( nBits & 1 ) + xTransform->setBy( Any( static_cast(fBy) ) ); + + if( nBits & 2 ) + xTransform->setFrom( Any( static_cast(fFrom) ) ); + + if( nBits & 4 ) + xTransform->setTo( Any( static_cast(fTo) ) ); + +#ifdef DBG_ANIM_LOG + if( nBits & 1 ) + fprintf( mpFile, " by=\"%g\"", (double)fBy ); + + if( nBits & 2 ) + fprintf( mpFile, " from=\"%g\"", (double)fFrom ); + + if( nBits & 4 ) + fprintf( mpFile, " to=\"%g\"", (double)fTo ); + + if( nU1 ) + fprintf( mpFile, " rotation_1=\"%" SAL_PRIdINT32 "\"", nU1 ); +#endif + } + break; + + case DFF_msofbtAnimateTarget: + importAnimateAttributeTargetContainer( pChildAtom, xNode ); + break; + + default: + dump( " unknown_atom=\"%ld\"", static_cast(pChildAtom->getType()) ); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +void AnimationImporter::importAttributeNamesContainer( const Atom* pAtom, OUString& rAttributeNames ) +{ + OUStringBuffer aNames; + + DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimateAttributeNames), "invalid call to ppt::AnimationImporter::importAttributeName()!" ); + if( pAtom ) + { + const Atom* pAttributeValueAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimAttributeValue ); + + while( pAttributeValueAtom ) + { + Any aAny; + if ( importAttributeValue( pAttributeValueAtom, aAny ) ) + { + OUString aName; + if( aAny >>= aName ) + { + if( !aNames.isEmpty() ) + aNames.append( ';' ); + + aNames.append( aName ); + } + } + else + { + OSL_FAIL( "error during ppt::AnimationImporter::importAttributeName()!" ); + } + + pAttributeValueAtom = pAtom->findNextChildAtom( DFF_msofbtAnimAttributeValue, pAttributeValueAtom ); + } + } + + rAttributeNames = aNames.makeStringAndClear(); +} + +void AnimationImporter::importAnimationValues( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( pAtom, "invalid call to ppt::AnimationImporter::importAnimationValues()!" ); + + if( !pAtom ) + return; + + const Atom* pValueAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimValue ); + + while( pValueAtom && pValueAtom->seekToContent() ) + { + sal_uInt32 nType(0); + mrStCtrl.ReadUInt32( nType ); + switch( nType ) + { + case 0: + { + float fRepeat(0.0); + mrStCtrl.ReadFloat( fRepeat ); + xNode->setRepeatCount( (fRepeat < (float(3.40282346638528860e+38))) ? Any( static_cast(fRepeat) ) : Any( Timing_INDEFINITE ) ); + +#ifdef DBG_ANIM_LOG + if( (fRepeat < ((float)3.40282346638528860e+38)) ) + { + dump( " repeat=\"%g\"", (double)fRepeat ); + } + else + { + dump( " repeat=\"indefinite\"" ); + } +#endif + } + break; + + case 3: + { + float faccelerate(0.0); + mrStCtrl.ReadFloat( faccelerate ); + xNode->setAcceleration( faccelerate ); + dump( " accelerate=\"%g\"", static_cast(faccelerate) ); + } + break; + + case 4: + { + float fdecelerate(0.0); + mrStCtrl.ReadFloat( fdecelerate ); + xNode->setDecelerate( fdecelerate ); + dump( " decelerate=\"%g\"", static_cast(fdecelerate) ); + } + break; + + case 5: + { + sal_Int32 nAutoreverse(0); + mrStCtrl.ReadInt32( nAutoreverse ); + xNode->setAutoReverse( nAutoreverse != 0 ); + dump( " autoreverse=\"%#lx\"", nAutoreverse ); + } + break; + + default: + { + sal_uInt32 nUnknown; + mrStCtrl.ReadUInt32( nUnknown ); +#ifdef DBG_ANIM_LOG + fprintf(mpFile, " attribute_%d=\"%#lx\"", nType, nUnknown ); +#endif + } + break; + } + + pValueAtom = pAtom->findNextChildAtom( DFF_msofbtAnimValue, pValueAtom ); + } +} + +void AnimationImporter::importAnimateKeyPoints( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + Reference< XAnimate > xAnim( xNode, UNO_QUERY ); + + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimKeyPoints && xAnim.is(), "invalid call to ppt::AnimationImporter::importAnimateKeyPoints()!" ); + + if( !(pAtom && xAnim.is()) ) + return; + + // first count keytimes + const Atom* pIter = nullptr; + int nKeyTimes = 0; + + while( (pIter = pAtom->findNextChildAtom( DFF_msofbtAnimKeyTime, pIter )) != nullptr ) + nKeyTimes++; + + Sequence< double > aKeyTimes( nKeyTimes ); + auto aKeyTimesRange = asNonConstRange(aKeyTimes); + Sequence< Any > aValues( nKeyTimes ); + auto aValuesRange = asNonConstRange(aValues); + OUString aFormula; + + pIter = pAtom->findFirstChildAtom(DFF_msofbtAnimKeyTime); + bool bToNormalize = false; + for( int nKeyTime = 0; (nKeyTime < nKeyTimes) && pIter; nKeyTime++ ) + { + if( pIter->seekToContent() ) + { + sal_Int32 nTemp(0); + mrStCtrl.ReadInt32(nTemp); + double fTemp = static_cast(nTemp) / 1000.0; + aKeyTimesRange[nKeyTime] = fTemp; + if( fTemp == -1 ) + bToNormalize = true; + + const Atom* pValue = Atom::findNextChildAtom(pIter); + if( pValue && pValue->getType() == DFF_msofbtAnimAttributeValue ) + { + Any aValue1, aValue2; + if( importAttributeValue( pValue, aValue1 ) ) + { + pValue = Atom::findNextChildAtom(pValue); + if( pValue && pValue->getType() == DFF_msofbtAnimAttributeValue ) + { + // Any occurrence of the formula becomes the formula of the whole list. + if (importAttributeValue(pValue, aValue2) && aFormula.isEmpty()) + aValue2 >>= aFormula; + } + aValuesRange[nKeyTime] = aValue1; + } + } + } + pIter = pAtom->findNextChildAtom(DFF_msofbtAnimKeyTime, pIter); + } + +#ifdef DBG_ANIM_LOG + dump( " keyTimes=\"" ); + for( int i=0; i>= aStr ) + dump( "%s", + OUStringToOString( aStr, + RTL_TEXTENCODING_ASCII_US ).getStr() ); + else if( aValues[i] >>= nVal ) + dump( "%f", nVal ); + else + { + ValuePair aValuePair; + + if( aValues[i] >>= aValuePair ) + { + if( aValuePair.First >>= aStr ) + dump( "%s", + OUStringToOString( aStr, + RTL_TEXTENCODING_ASCII_US ).getStr() ); + else if( aValuePair.First >>= nVal ) + dump( "%f", nVal ); + else + dump( "%X", (sal_Int64)&aValuePair.First ); + + if( aValuePair.Second >>= aStr ) + dump( ",%s", + OUStringToOString( aStr, + RTL_TEXTENCODING_ASCII_US ).getStr() ); + else if( aValuePair.Second >>= nVal ) + dump( ",%f", nVal ); + else + dump( ",%X", (sal_Int64)&aValuePair.Second ); + } + } + } + dump( "\"" ); +#endif + if( bToNormalize && nKeyTimes >= 2 ) + { + // if TimeAnimationValueList contains time -1000, key points must be evenly distributed between 0 and 1 ([MS-PPT] 2.8.31) + for( int nKeyTime = 0; nKeyTime < nKeyTimes; ++nKeyTime ) + { + aKeyTimesRange[nKeyTime] = static_cast(nKeyTime) / static_cast(nKeyTimes - 1); + } + } + + if (aValues.getLength() != aKeyTimes.getLength()) + throw css::io::WrongFormatException(); + + xAnim->setKeyTimes( aKeyTimes ); + xAnim->setValues( aValues ); + xAnim->setFormula( aFormula ); +} + +bool AnimationImporter::importAttributeValue( const Atom* pAtom, Any& rAny ) +{ + DBG_ASSERT( pAtom && pAtom->getType() == DFF_msofbtAnimAttributeValue, "invalid call to ppt::AnimationImporter::importAttributeValue()!" ); + + bool bOk = false; + + if( pAtom && pAtom->seekToContent() ) + { + sal_uInt32 nRecLen = pAtom->getLength(); + if ( nRecLen >= 1 ) + { + sal_Int8 nType(0); + mrStCtrl.ReadSChar( nType ); + switch( nType ) + { + case DFF_ANIM_PROP_TYPE_BYTE : + { + if ( nRecLen == 2 ) + { + sal_uInt8 nByte(0); + mrStCtrl.ReadUChar( nByte ); + rAny <<= nByte; + + bOk = true; + } + } + break; + + case DFF_ANIM_PROP_TYPE_INT32 : + { + if ( nRecLen == 5 ) + { + sal_uInt32 nInt32(0); + mrStCtrl.ReadUInt32( nInt32 ); + rAny <<= nInt32; + + bOk = true; + } + } + break; + + case DFF_ANIM_PROP_TYPE_FLOAT: + { + if( nRecLen == 5 ) + { + float fFloat(0.0); + mrStCtrl.ReadFloat( fFloat ); + rAny <<= static_cast(fFloat); + + bOk = true; + } + } + break; + + case DFF_ANIM_PROP_TYPE_UNISTRING : + { + if ( ( nRecLen & 1 ) && ( nRecLen > 1 ) ) + { + OUString aOUString = SvxMSDffManager::MSDFFReadZString( mrStCtrl, nRecLen - 1, true ); + rAny <<= aOUString; + + bOk = true; + } + } + break; + } + } + } + + DBG_ASSERT( bOk, "invalid value inside ppt::AnimationImporter::importAttributeValue()!" ); + return bOk; +} + +void AnimationImporter::importAnimationEvents( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( xNode.is() && pAtom, "invalid call to ppt::AnimationImporter::importAnimationEvents()!" ); + + Any aBegin, aEnd, aNext, aPrev; + + const Atom* pEventAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimEvent ); + while( pEventAtom ) + { + Any* pEvents = nullptr; + + switch( pEventAtom->getInstance() ) + { + case 1: pEvents = &aBegin; break; + case 2: pEvents = &aEnd; break; + case 3: pEvents = &aNext; break; + case 4: pEvents = &aPrev; break; + } + + if( pEvents ) + { + Event aEvent; + aEvent.Trigger = EventTrigger::NONE; + aEvent.Repeat = 0; + + const Atom* pChildAtom = pEventAtom->findFirstChildAtom(); + + while( pChildAtom && pChildAtom->seekToContent() ) + { + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimTrigger: + { + sal_Int32 nU1(0), nTrigger(0), nU3(0), nBegin(0); + mrStCtrl.ReadInt32( nU1 ); + mrStCtrl.ReadInt32( nTrigger ); + mrStCtrl.ReadInt32( nU3 ); + mrStCtrl.ReadInt32( nBegin ); + + switch( nTrigger ) + { + case 0: aEvent.Trigger = EventTrigger::NONE; break; + case 1: aEvent.Trigger = EventTrigger::ON_BEGIN; break; + case 2: aEvent.Trigger = EventTrigger::ON_END; break; + case 3: aEvent.Trigger = EventTrigger::BEGIN_EVENT; break; + case 4: aEvent.Trigger = EventTrigger::END_EVENT; break; + case 5: aEvent.Trigger = EventTrigger::ON_CLICK; break; + case 6: aEvent.Trigger = EventTrigger::ON_DBL_CLICK; break; + case 7: aEvent.Trigger = EventTrigger::ON_MOUSE_ENTER; break; + case 8: aEvent.Trigger = EventTrigger::ON_MOUSE_LEAVE; break; + case 9: aEvent.Trigger = EventTrigger::ON_NEXT; break; + case 10: aEvent.Trigger = EventTrigger::ON_PREV; break; + case 11: aEvent.Trigger = EventTrigger::ON_STOP_AUDIO; break; + } + + if( (nBegin != 0) || (aEvent.Trigger == EventTrigger::NONE) ) + aEvent.Offset = (nBegin == -1) ? Any( Timing_INDEFINITE ) : Any( nBegin / 1000.0 ); + } + break; + case DFF_msofbtAnimateTargetElement: + { + sal_Int16 nSubType; + importTargetElementContainer( pChildAtom, aEvent.Source, nSubType ); + } + break; + default: + { + OSL_FAIL("unknown atom inside ppt::AnimationImporter::importAnimationEvents()!"); + } + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } + + *pEvents = oox::addToSequence( *pEvents, (aEvent.Trigger == EventTrigger::NONE) ? aEvent.Offset : Any( aEvent ) ); + } + + pEventAtom = pAtom->findNextChildAtom( DFF_msofbtAnimEvent, pEventAtom ); + } + + xNode->setBegin( aBegin ); + xNode->setEnd( aEnd ); + // TODO: xNode->setNext( aNext ); + // TODO: xNode->setPrev( aNext ); + +#ifdef DBG_ANIM_LOG + if( aBegin.hasValue() ) + { + dump( " begin=\"" ); + dump( aBegin ); + dump( "\"" ); + } + + if( aEnd.hasValue() ) + { + dump( " end=\"" ); + dump( aEnd ); + dump( "\"" ); + } + + if( aNext.hasValue() ) + { + dump( " next=\"" ); + dump( aNext ); + dump( "\"" ); + } + + if( aPrev.hasValue() ) + { + dump( " prev=\"" ); + dump( aPrev ); + dump( "\"" ); + } +#endif +} + +void AnimationImporter::importAnimationActions( const Atom* pAtom, const Reference< XAnimationNode >& xNode ) +{ + DBG_ASSERT( pAtom && xNode.is(), "invalid call to ppt::AnimationImporter::importAnimationActions()!"); + + if( !pAtom ) + return; + + const Atom* pActionAtom = pAtom->findFirstChildAtom( DFF_msofbtAnimAction ); + + if( !(pActionAtom && pActionAtom->seekToContent()) ) + return; + + sal_Int32 nConcurrent(0), nNextAction(0), nEndSync(0), nU4(0), nU5(0); + mrStCtrl.ReadInt32( nConcurrent ); + mrStCtrl.ReadInt32( nNextAction ); + mrStCtrl.ReadInt32( nEndSync ); + mrStCtrl.ReadInt32( nU4 ); + mrStCtrl.ReadInt32( nU5 ); + + if( nEndSync == 1 ) + xNode->setEndSync( Any( AnimationEndSync::ALL ) ); + +#ifdef DBG_ANIM_LOG + dump( " concurrent=\"%s\"", nConcurrent == 0 ? "disabled" : (nConcurrent == 1 ? "enabled" : "unknown") ); + + dump( " nextAction=\"%s\"", nNextAction == 0 ? "none" : (nNextAction == 1 ? "seek" : "unknown") ); + + if( nEndSync != 0 ) + { + dump( " endSync=\"%s\"", nEndSync == 1 ? "all" : "unknown" ); + } + + dump( " action_4=\"%#lx\"", nU4 ); + dump( " action_5=\"%#lx\"", nU5 ); +#endif +} + +void AnimationImporter::importTargetElementContainer( const Atom* pAtom, Any& rTarget, sal_Int16& rSubType ) +{ + rSubType = ShapeAnimationSubType::AS_WHOLE; + sal_Int32 nRefMode = -1; + + DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimateTargetElement), "invalid call to ppt::AnimationImporter::importTargetElementContainer()!" ); + if( !pAtom ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + while( pChildAtom && pChildAtom->seekToContent() ) + { + switch( pChildAtom->getType() ) + { + case DFF_msofbtAnimReference: + { + sal_Int32 nRefType(0), nRefId(0); + sal_Int32 begin(0), end(0); + mrStCtrl.ReadInt32( nRefMode ); + mrStCtrl.ReadInt32( nRefType ); + mrStCtrl.ReadInt32( nRefId ); + mrStCtrl.ReadInt32( begin ); + mrStCtrl.ReadInt32( end ); + + switch( nRefType ) + { + case 1: // shape + { + SdrObject* pSdrObject = mpPPTImport->getShapeForId( nRefId ); + if( pSdrObject == nullptr ) + break; + + rTarget <<= pSdrObject->getUnoShape(); + + switch( nRefMode ) + { + case 6: rSubType = ShapeAnimationSubType::ONLY_BACKGROUND; break; + case 8: rSubType = ShapeAnimationSubType::ONLY_TEXT; break; + case 2: // one paragraph + { + if((begin == -1) && (end == -1)) + break; + + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pSdrObject ); + if(!pTextObj) + break; + + const OutlinerParaObject* pOPO = pTextObj->GetOutlinerParaObject(); + if( pOPO == nullptr ) + break; + + const EditTextObject& rEditTextObject = pOPO->GetTextObject(); + + const sal_Int32 nParaCount = rEditTextObject.GetParagraphCount(); + + sal_Int32 nPara = 0; + + while( (nPara < nParaCount) && (begin > 0) ) + { + sal_Int32 nParaLength = rEditTextObject.GetText( nPara ).getLength() + 1; + begin -= nParaLength; + end -= nParaLength; + nPara++; + } + + if( nPara < nParaCount ) + { + ParagraphTarget aParaTarget; + rTarget >>= aParaTarget.Shape; + /* FIXME: Paragraph should be sal_Int32 as well */ + aParaTarget.Paragraph = static_cast(nPara); + rTarget <<= aParaTarget; + + rSubType = ShapeAnimationSubType::ONLY_TEXT; + dump( " paragraph %d,", nPara); + dump( " %d characters", end ); + } + } + } + } + break; + + case 2: // sound + { + OUString aSoundURL( mpPPTImport->ReadSound( nRefId ) ); + rTarget <<= aSoundURL; + dump( " srcRef=\"%s\"", aSoundURL ); + } + break; + case 3: // audio object + case 4: // video object + { + SdrObject* pSdrObject = mpPPTImport->getShapeForId( nRefId ); + if( pSdrObject == nullptr ) + break; + + rTarget <<= pSdrObject->getUnoShape(); + } + break; + default: + OSL_FAIL("unknown reference type"); + } + + } + break; + case 0x2b01: + { + sal_Int32 nU1; + mrStCtrl.ReadInt32( nU1 ); + } + break; + default: + OSL_FAIL("unknown atom inside ppt::AnimationImporter::importTargetElementContainer()!"); + break; + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + + } +} + +void AnimationImporter::importPropertySetContainer( const Atom* pAtom, PropertySet& rSet ) +{ + DBG_ASSERT( pAtom && (pAtom->getType() == DFF_msofbtAnimPropertySet), "invalid call to ppt::AnimationImporter::importPropertySetContainer()!" ); + + if( !pAtom ) + return; + + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + while( pChildAtom ) + { + if( pChildAtom->getType() == DFF_msofbtAnimAttributeValue ) + { + Any aAny; + (void)importAttributeValue( pChildAtom, aAny ); + rSet.maProperties[ pChildAtom->getInstance() ] = aAny; + } + else + { + OSL_FAIL("unknown atom inside ppt::AnimationImporter::importPropertySetContainer()!"); + } + + pChildAtom = Atom::findNextChildAtom( pChildAtom ); + } +} + +#ifdef DBG_ANIM_LOG +void AnimationImporter::dump_atom_header( const Atom* pAtom, bool bOpen, bool bAppend ) +{ + if( pAtom ) + { + const char* pTitle; + + switch( pAtom->getType() ) + { + case DFF_msofbtAnimEvent: pTitle = "AnimEvent"; break; + case DFF_msofbtAnimTrigger: pTitle = "AnimTrigger"; break; + case DFF_msofbtAnimateMotion: pTitle = "AnimateMotion"; break; + case DFF_msofbtAnimPropertySet: pTitle = "AnimPropertySet"; break; + case DFF_msofbtAnimateAttributeNames: pTitle = "AnimAttributeName"; break; + case DFF_msofbtAnimAttributeValue: pTitle = "AnimAttributeValue"; break; + case DFF_msofbtAnimGroup: pTitle = "AnimGroup"; break; + case DFF_msofbtAnimNode: pTitle = "AnimNode"; break; + case DFF_msofbtAnimValue: pTitle = "AnimValue"; break; + case DFF_msofbtAnimateFilter: pTitle = "animateFilter"; break; + case DFF_msofbtAnimate: pTitle = "animate"; break; + case DFF_msofbtAnimateSet: pTitle = "set"; break; + case DFF_msofbtAnimKeyTime: pTitle = "AnimKeyTime"; break; + case DFF_msofbtAnimKeyPoints: pTitle = "AnimKeyPoints"; break; + case DFF_msofbtAnimReference: pTitle = "AnimReference"; break; + case DFF_msofbtAnimateTargetElement: pTitle = "AnimTargetElementContainer"; break; + case DFF_msofbtAnimAction: pTitle = "AnimAction"; break; + case DFF_msofbtAnimCommand: pTitle = "AnimCommand"; break; + case DFF_msofbtAnimateTarget: pTitle = "TransformationTarget"; break; + case DFF_msofbtAnimateTargetSettings: pTitle = "TransformationTargetSettings"; break; + case DFF_msofbtAnimIteration: pTitle = "iterate"; break; + case DFF_msofbtAnimateColorData: pTitle = "colorData"; break; + case DFF_msofbtAnimateScaleData: pTitle = "scaleData"; break; + case DFF_msofbtAnimateSetData: pTitle = "setData"; break; + + default: + { + static char buffer[128]; + sprintf( buffer, "unknown_%#x", pAtom->getType() ); + pTitle = buffer; + } + } + + if( bOpen ) + { + fprintf(mpFile, "<%s", pTitle ); + + fprintf(mpFile, " instance=\"%hu\"%s", + pAtom->getInstance(), + bAppend ? "" : ">\n"); + } + else + { + if( bAppend ) + fprintf(mpFile,"/>\n"); + else + fprintf(mpFile, "\n", pTitle ); + } + } +} + +void AnimationImporter::dump( sal_uInt32 nLen, bool bNewLine ) +{ + char * faul = "0123456789abcdef"; + + sal_uInt32 i = 0; + int b = 0; + char nData; + + for( i = 0; i < nLen; i++ ) + { + mrStCtrl.ReadChar(nData); + + fprintf( mpFile, "%c%c ", faul[ (nData >> 4) & 0x0f ], faul[ nData & 0x0f ] ); + + b++; + if( bNewLine && (b == 32) ) + { + fprintf(mpFile,"\n"); + b = 0; + } + } + if( (b != 0) && bNewLine ) + fprintf(mpFile,"\n"); +} + +void AnimationImporter::dump_atom( const Atom* pAtom, bool bNewLine ) +{ + if( pAtom ) + { + if( pAtom->isContainer() ) + { + const Atom* pChildAtom = pAtom->findFirstChildAtom(); + while( pChildAtom ) + { + if( pChildAtom->getType() == DFF_msofbtAnimAttributeValue ) + { + fprintf(mpFile, "getInstance() ); + + Any aValue; + if( importAttributeValue( pChildAtom, aValue ) ) + { + sal_Int32 nInt; + OUString aString; + double fDouble; + + if( aValue >>= nInt ) + { + fprintf(mpFile, " value=\"%" SAL_PRIdINT32 "\"", nInt ); + } + else if( aValue >>= aString ) + { + fprintf(mpFile, " value=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr()); + } + else if( aValue >>= fDouble ) + { + fprintf(mpFile, " value=\"%g\"", fDouble ); + } + } + else + { + if( pChildAtom->seekToContent() ) + { + fprintf(mpFile, " value=\"" ); + dump_atom( pChildAtom, false ); + fprintf(mpFile, "\""); + } + } + + fprintf(mpFile, "/>\n" ); + } + else + { + dump_atom_header( pChildAtom, true, pChildAtom->getType() == DFF_msofbtAnimAttributeValue ); + dump_atom( pChildAtom ); + dump_atom_header( pChildAtom, false, pChildAtom->getType() == DFF_msofbtAnimAttributeValue ); + } + + pChildAtom = Atom::findNextChildAtom(pChildAtom); + } + } + else if( pAtom->seekToContent() ) + { + dump( pAtom->getLength(), bNewLine ); + } + } +} + +void AnimationImporter::dump_anim_group( const Atom* pAtom, const AnimationNode& rNode, const PropertySet& rSet, bool bOpen ) +{ + fprintf( mpFile, bOpen ? "<" : "hasChildAtom( DFF_msofbtAnimateSet ) ) + fprintf( mpFile, "set" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimateColor ) ) + fprintf( mpFile, "animateColor" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimateScale ) ) + fprintf( mpFile, "animateScale" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimateRotation ) ) + fprintf( mpFile, "animateRotation" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimateMotion ) ) + fprintf( mpFile, "animateMotion" ); + else if( pAtom->hasChildAtom( DFF_msofbtAnimCommand ) ) + fprintf( mpFile, "command" ); + else + fprintf( mpFile, "animation" ); + break; + default: + { + fprintf( mpFile, "unknown_node_%#lx", rNode.mnNodeType ); + } + break; + } + break; + case mso_Anim_GroupType_MEDIA: + fprintf( mpFile, "media" ); + break; + default: + fprintf( mpFile, "unknown_group_%#lx", rNode.mnGroupType ); + break; + } + + if( bOpen ) + { + dump( rNode ); + dump( rSet ); + } + + fprintf(mpFile,">\n"); +} + +void AnimationImporter::dump( const AnimationNode& rNode ) +{ + // dump animation node + if( rNode.mnRestart != 0 ) + { + fprintf(mpFile," restart=\"%s\"", + rNode.mnRestart == 1 ? "always" : (rNode.mnRestart == 2 ? "whenOff" : (rNode.mnRestart == 3 ? "never" : "unknown")) ); + } + + if( rNode.mnFill ) + { + fprintf(mpFile," fill=\"%s\"", + rNode.mnFill == 1 ? "remove" : (rNode.mnFill == 3 ? "hold" : (rNode.mnFill == 2 ? "freeze" : "unknown")) ); + } + + if( rNode.mnDuration > 0 ) + { + double fSeconds = rNode.mnDuration; + fSeconds /= 1000.0; + fprintf(mpFile, " dur=\"%g\"", fSeconds); + } + else if( rNode.mnDuration < 0 ) + { + fprintf(mpFile, " dur=\"indefinite\"" ); + } + + if( rNode.mnU1 ) fprintf(mpFile," u1=\"%#lx\"", rNode.mnU1); + if( rNode.mnU3 ) fprintf(mpFile," u3=\"%#lx\"", rNode.mnU3); + if( rNode.mnU4 ) fprintf(mpFile," u4=\"%#lx\"", rNode.mnU4); +} + +void AnimationImporter::dump( Any& rAny ) +{ + Sequence< Any > aSeq; + sal_Int32 nInt; + double fDouble; + OUString aString; + sal_Bool bBool; + Event aEvent; + Timing aTiming; + + if( rAny >>= aSeq ) + { + const sal_Int32 nSize = aSeq.getLength(); + sal_Int32 nIndex = 0; + while( nIndex < nSize ) + { + dump( aSeq[nIndex++] ); + if(nIndex < nSize) + fprintf( mpFile, "," ); + } + } + else if( rAny >>= aString ) + { + fprintf( mpFile, "%s", OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + } + else if( rAny >>= nInt ) + { + fprintf( mpFile, "%" SAL_PRIdINT32, nInt ); + } + else if( rAny >>= bBool ) + { + fprintf( mpFile, "%s", bBool ? "true" : "false" ); + } + else if( rAny >>= fDouble ) + { + fprintf( mpFile, "%g", fDouble ); + } + else if( rAny >>= aTiming ) + { + fprintf( mpFile, "%s", aTiming == (Timing_INDEFINITE) ? "indefinite" : "media" ); + } + else if( rAny >>= aEvent ) + { + if( aEvent.Trigger != EventTrigger::NONE ) + { + static const char* triggers[] = + { + "none","onbegin","onend","begin", + "end","onclick","ondoubleclick","onmouseenter", + "onmouseleave","onpptnext","onpptprev","onstopaudio" + }; + + if( aEvent.Source.hasValue() ) + { + dump_target( aEvent.Source ); + dump( "." ); + } + + dump( triggers[ aEvent.Trigger ] ); + } + + if( aEvent.Offset.hasValue() ) + { + double fOffset; + if( aEvent.Offset >>= fOffset ) + fprintf( mpFile, "%g", fOffset ); + else + dump( "indefinite" ); + } + } +} + +void AnimationImporter::dump( const PropertySet& rSet ) +{ + // dump property set + + for( const auto& rProp : rSet.maProperties ) + { + bool bKnown = false; + + const sal_Int32 nInstance = rProp.first; + Any aAny( rProp.second ); + + switch ( nInstance ) + { + case DFF_ANIM_COLORSPACE: + { + sal_Int32 nColorSpace; + if( aAny >>= nColorSpace ) + { + fprintf( mpFile, " colorSpace=\"%s\"", (nColorSpace == 0) ? "rgb" : (nColorSpace == 1) ? "hsl" : "unknown" ); + bKnown = true; + } + } + break; + + case DFF_ANIM_DIRECTION: + { + sal_Bool bDirection; + if( aAny >>= bDirection ) + { + fprintf( mpFile, " direction=\"%s\"", bDirection ? "cclockwise" : "clockwise" ); + bKnown = true; + } + else + { + sal_Int32 nMasterRel; + if( aAny >>= nMasterRel ) + { + fprintf( mpFile, " direction=\"%s\"", nMasterRel == 0 ? "sameClick" : ( nMasterRel == 2 ? "nextClick" : "lastClick" ) ); + bKnown = true; + } + } + } + break; + + case DFF_ANIM_OVERRIDE: // TODO + { + sal_Int32 nOverride; + if( aAny >>= nOverride ) + { + fprintf( mpFile, " override=\"%s\"", (nOverride == 1) ? "childStyle" : (nOverride == 0) ? "normal" : "unknown" ); + bKnown = true; + } + } + break; + + case DFF_ANIM_PATH_EDIT_MODE: + { + sal_Bool bPathEditMode; + if( aAny >>= bPathEditMode ) + { + fprintf( mpFile, " pptPathEditMode=\"%s\"", bPathEditMode ? "relative" : "fixed" ); + bKnown = true; + } + } + break; + + case DFF_ANIM_PRESET_ID : + { + sal_Int32 nPresetId ; + if( aAny >>= nPresetId ) + { + fprintf(mpFile, " presetid=\"%" SAL_PRIdINT32 "\"", nPresetId ); + bKnown = true; + } + } + break; + + case DFF_ANIM_PRESET_SUB_TYPE : + { + sal_Int32 nPointsType ; + if( aAny >>= nPointsType ) + { + fprintf(mpFile, " presetSubType=\"%" SAL_PRIdINT32 "\"", nPointsType ); + bKnown = true; + } + } + break; + + case DFF_ANIM_PRESET_CLASS : + { + sal_Int32 nPresetClass; + if ( aAny >>= nPresetClass ) + { + const char* pMode; + switch( nPresetClass ) + { + case DFF_ANIM_PRESS_CLASS_USER_DEFINED: pMode = "userdefined"; break; + case DFF_ANIM_PRESS_CLASS_ENTRANCE: pMode = "entrance"; break; + case DFF_ANIM_PRESS_CLASS_EXIT: pMode = "exit"; break; + case DFF_ANIM_PRESS_CLASS_EMPHASIS: pMode = "emphasis"; break; + case DFF_ANIM_PRESS_CLASS_MOTIONPATH: pMode = "motionpath"; break; + case DFF_ANIM_PRESS_CLASS_OLE_ACTION: pMode = "oleaction"; break; + case DFF_ANIM_PRESS_CLASS_MEDIACALL: pMode = "mediacall"; break; + default: + pMode = nullptr; + break; + } + + if (pMode) + fprintf(mpFile, " class=\"%s\"", pMode); + else + fprintf(mpFile, " class =\"%" SAL_PRIdINT32 "\"", nPresetClass); + bKnown = true; + } + } + break; + + case DFF_ANIM_NODE_TYPE : + { + sal_Int32 nNodeType; + if ( aAny >>= nNodeType ) + { + const char* pNode; + switch( nNodeType ) + { + case DFF_ANIM_NODE_TYPE_ON_CLICK: pNode = "onclick"; break; + case DFF_ANIM_NODE_TYPE_WITH_PREVIOUS: pNode = "withprevious"; break; + case DFF_ANIM_NODE_TYPE_AFTER_PREVIOUS: pNode = "afterprevious"; break; + case DFF_ANIM_NODE_TYPE_MAIN_SEQUENCE: pNode = "mainsequence"; break; + case DFF_ANIM_NODE_TYPE_TIMING_ROOT: pNode = "timingroot"; break; + case DFF_ANIM_NODE_TYPE_INTERACTIVE_SEQ:pNode = "interactivesequence"; break; + default : + { + static char buffer[128]; + sprintf( buffer, "%" SAL_PRIdINT32, nNodeType ); + pNode = buffer; + } + break; + } + + fprintf(mpFile, " nodeType=\"%s\"", pNode); + bKnown = true; + } + } + break; + + case DFF_ANIM_GROUP_ID: + { + sal_Int32 nGroupId; + if ( aAny >>= nGroupId ) + { + fprintf( mpFile, " groupId=\"%" SAL_PRIdINT32 "\"", nGroupId ); + bKnown = true; + } + } + break; + + case DFF_ANIM_ID: + { + OUString aString; + if( aAny >>= aString ) + { + fprintf( mpFile, " id=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + bKnown = true; + } + } + break; + + case DFF_ANIM_EVENT_FILTER: + { + OUString aString; + if( aAny >>= aString ) + { + fprintf( mpFile, " eventFilter=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + bKnown = true; + } + } + break; + + case DFF_ANIM_ENDAFTERSLIDE: + { + sal_Int32 nEndAfterSlide; + if( aAny >>= nEndAfterSlide ) + { + fprintf(mpFile, " endAfterSlide=\"%" SAL_PRIdINT32 "\"", nEndAfterSlide ); + bKnown = true; + } + } + + case DFF_ANIM_TIMEFILTER: + { + OUString aString; + if( aAny >>= aString ) + { + fprintf( mpFile, " timeFilter=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + bKnown = true; + } + } + break; + + case DFF_ANIM_RUNTIMECONTEXT: + { + OUString aString; + if( aAny >>= aString ) + { + fprintf( mpFile, " runtimeContext=\"%s\"", + OUStringToOString(aString, + RTL_TEXTENCODING_UTF8).getStr() ); + bKnown = true; + } + } + break; + + case DFF_ANIM_VOLUME: + { + double fVolume(0.0); + if( aAny >>= fVolume ) + { + fprintf( mpFile, " volume=\"%g%%\"", (double)(fVolume * 100.0) ); + bKnown = true; + } + } + break; + + case DFF_ANIM_AFTEREFFECT: + { + sal_Bool bAfterEffect; + if( aAny >>= bAfterEffect ) + { + fprintf( mpFile, "afterEffect=\"%s\"", bAfterEffect ? "true" : "false" ); + bKnown = true; + } + } + break; + + } + + if( !bKnown ) + { + fprintf( mpFile, " unknown_%" SAL_PRIdINT32 "=\"", nInstance ); + dump( aAny ); + fprintf( mpFile, "\"" ); + } + } +} + +void AnimationImporter::dump_target( Any& rAny ) +{ + Any aSource, aSourceData; + Sequence< Any > aSeq; + if( rAny >>= aSeq ) + { + if( aSeq.getLength() >= 1 ) aSource = aSeq[0]; + if( aSeq.getLength() >= 2 ) aSourceData = aSeq[1]; + } + else + { + aSource = rAny; + } + + Reference< XShape > xShape; + aSource >>= xShape; + if( xShape.is() ) + { + OUString aStr( xShape->getShapeType() ); + dump( aStr ); + + if( aSourceData.hasValue() ) + { + dump( "(" ); + dump( aSourceData ); + dump( ")" ); + } + } +} + +void AnimationImporter::dump( const char * pText ) +{ + fprintf( mpFile, "%s", pText ); +} + +void AnimationImporter::dump( const OUString& rString ) +{ + fprintf( mpFile, OUStringToOString(rString, + RTL_TEXTENCODING_UTF8).getStr() ); +} + +void AnimationImporter::dump( const char * pText, sal_Int64 nInt ) +{ + fprintf( mpFile, pText, nInt ); +} + +void AnimationImporter::dump( const char * pText, sal_Int32 nInt ) +{ + fprintf( mpFile, pText, nInt ); +} + +void AnimationImporter::dump( const char * pText, double fDouble ) +{ + fprintf( mpFile, pText, fDouble ); +} + +void AnimationImporter::dump( const char * pText, const char * pText2 ) +{ + fprintf( mpFile, pText, pText2 ); +} + +void AnimationImporter::dump( const char * pText, const OUString& rString ) +{ + fprintf( mpFile, pText, OUStringToOString(rString, + RTL_TEXTENCODING_UTF8).getStr() ); +} + +#else + +void AnimationImporter::dump_atom_header( const Atom* , bool , bool ) +{ +} + +void AnimationImporter::dump_atom( const Atom* , bool ) +{ +} + +void AnimationImporter::dump_target( css::uno::Any& ) +{ +} + +void AnimationImporter::dump( css::uno::Any& ) +{ +} + +void AnimationImporter::dump( const PropertySet& ) +{ +} + +void AnimationImporter::dump( const AnimationNode& ) +{ +} + +void AnimationImporter::dump( const char * ) +{ +} + +void AnimationImporter::dump( const char * , sal_Int32 ) +{ +} + +void AnimationImporter::dump( const char * , double ) +{ +} + +void AnimationImporter::dump( const char * , const char * ) +{ +} + +void AnimationImporter::dump( const char * , std::u16string_view ) +{ +} + +#endif + +} // namespace ppt; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/pptinanimations.hxx b/sd/source/filter/ppt/pptinanimations.hxx new file mode 100644 index 000000000..ed79144b9 --- /dev/null +++ b/sd/source/filter/ppt/pptinanimations.hxx @@ -0,0 +1,115 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + +#ifdef DBG_ANIM_LOG +#include +#endif + +namespace com::sun::star::animations { class XAnimationNode; } +namespace com::sun::star::drawing { class XDrawPage; } +namespace ppt { struct AnimationNode; } + +class DffRecordHeader; +class SvStream; +class ImplSdPPTImport; + +namespace ppt +{ +class PropertySet; +class Atom; + +class AnimationImporter +{ +public: + AnimationImporter( ImplSdPPTImport* pPPTImport, SvStream& rStCtrl ); + + int import( const css::uno::Reference< css::drawing::XDrawPage >& xPage, const DffRecordHeader& rProgTagContentHd ); + +private: + int importAnimationContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xParent ); + int importTimeContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + int importAnimationNodeContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + void importAnimateSetContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateFilterContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateScaleContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateColorContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateRotationContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateMotionContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importCommandContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + int importAudioContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + void importAnimationEvents( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimationValues( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimationActions( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importAnimateAttributeTargetContainer( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + + void importAnimateKeyPoints( const Atom* pAtom, const css::uno::Reference< css::animations::XAnimationNode >& xNode ); + void importPropertySetContainer( const Atom* pAtom,PropertySet& rSet ); + bool importAttributeValue( const Atom* pAtom, css::uno::Any& rAny ); + void importAttributeNamesContainer( const Atom* pAtom, OUString& rAttributeNames ); + void importTargetElementContainer( const Atom* pAtom, css::uno::Any& rTarget, sal_Int16& nSubType ); + + static void fillNode( css::uno::Reference< css::animations::XAnimationNode > const & xTiming, const AnimationNode& rNode, const PropertySet& rSet ); + static css::uno::Reference< css::animations::XAnimationNode > createNode( const Atom* pAtom, const AnimationNode& rNode ); + + bool convertAnimationNode( const css::uno::Reference< css::animations::XAnimationNode >& xNode, const css::uno::Reference< css::animations::XAnimationNode >& xParent ); + css::uno::Any implGetColorAny( sal_Int32 nMode, sal_Int32 nA, sal_Int32 nB, sal_Int32 nC ); + static sal_Int16 implGetColorSpace( sal_Int32 nMode, sal_Int32 nA, sal_Int32 nB, sal_Int32 nC ); + +private: + css::uno::Reference< css::animations::XAnimationNode > mxRootNode; + + ImplSdPPTImport* mpPPTImport; + SvStream& mrStCtrl; + + std::vector< sd::AfterEffectNode > maAfterEffectNodes; + +#ifdef DBG_ANIM_LOG + FILE * mpFile; + void dump_anim_group( const Atom* pAtom, const AnimationNode& rNode, const PropertySet& rSet, bool bOpen ); + void dump( const OUString& rString ); + void dump( sal_uInt32 nLen, bool bNewLine = true ); +#endif + + static void dump_atom_header( const Atom* pAtom, bool bOpen, bool bAppend ); + static void dump_atom( const Atom* pAtom, bool bNewLine = true ); + static void dump_target( css::uno::Any& rAny ); + static void dump( css::uno::Any& rAny ); + static void dump( const PropertySet& rSet ); + static void dump( const AnimationNode& rNode ); + static void dump( const char * pText ); + static void dump( const char * pText, sal_Int32 nInt ); + void dump( const char * pText, sal_Int64 nInt ); + static void dump( const char * pText, double fDouble ); + static void dump( const char * pText, const char * pText2 ); + static void dump( const char * pText, std::u16string_view rString ); +}; + +} // namespace ppt + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/propread.cxx b/sd/source/filter/ppt/propread.cxx new file mode 100644 index 000000000..c82c0d791 --- /dev/null +++ b/sd/source/filter/ppt/propread.cxx @@ -0,0 +1,615 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "propread.hxx" +#include +#include +#include +#include +#include +#include + +PropEntry::PropEntry( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ) : + mnId ( nId ), + mnSize ( nBufSize ), + mpBuf ( new sal_uInt8[ nBufSize ] ) +{ + memcpy( mpBuf.get(), pBuf, nBufSize ); +}; + +PropEntry::PropEntry( const PropEntry& rProp ) : + mnId ( rProp.mnId ), + mnSize ( rProp.mnSize ), + mpBuf ( new sal_uInt8[ mnSize ] ) +{ + memcpy( mpBuf.get(), rProp.mpBuf.get(), mnSize ); +}; + +PropEntry& PropEntry::operator=(const PropEntry& rPropEntry) +{ + if ( this != &rPropEntry ) + { + mnId = rPropEntry.mnId; + mnSize = rPropEntry.mnSize; + mpBuf.reset( new sal_uInt8[ mnSize ] ); + memcpy( mpBuf.get(), rPropEntry.mpBuf.get(), mnSize ); + } + return *this; +} + +void PropItem::Clear() +{ + Seek( STREAM_SEEK_TO_BEGIN ); + delete[] static_cast(SwitchBuffer()); +} + +static sal_Int32 lcl_getMaxSafeStrLen(sal_uInt32 nSize) +{ + nSize -= 1; //Drop NULL terminator + + //If it won't fit in a string, clip it to the max size that does + if (nSize > SAL_MAX_INT32) + nSize = SAL_MAX_INT32; + + return static_cast< sal_Int32 >( nSize ); +} + +bool PropItem::Read( OUString& rString, sal_uInt32 nStringType, bool bAlign ) +{ + sal_uInt32 nType, nItemPos; + bool bRetValue = false; + + nItemPos = Tell(); + + if ( nStringType == VT_EMPTY ) + { + nType = VT_NULL; // Initialize in case stream fails. + ReadUInt32( nType ); + } + else + nType = nStringType & VT_TYPEMASK; + + sal_uInt32 nItemSize(0); // Initialize in case stream fails. + ReadUInt32(nItemSize); + + switch( nType ) + { + case VT_LPSTR : + { + if (nItemSize) + { + auto nMaxSizePossible = remainingSize(); + if (nItemSize > nMaxSizePossible) + { + SAL_WARN("sd.filter", "String of Len " << nItemSize << " claimed, only " << nMaxSizePossible << " possible"); + nItemSize = nMaxSizePossible; + } + } + + if (nItemSize) + { + try + { + std::unique_ptr pString( new char[ nItemSize ] ); + if ( mnTextEnc == RTL_TEXTENCODING_UCS2 ) + { + nItemSize >>= 1; + if ( nItemSize > 1 ) + { + sal_Unicode* pWString = reinterpret_cast(pString.get()); + for (sal_uInt32 i = 0; i < nItemSize; ++i) + ReadUtf16( pWString[ i ] ); + rString = OUString(pWString, lcl_getMaxSafeStrLen(nItemSize)); + } + else + rString.clear(); + bRetValue = true; + } + else + { + SvMemoryStream::ReadBytes(pString.get(), nItemSize); + if ( pString[ nItemSize - 1 ] == 0 ) + { + if ( nItemSize > 1 ) + rString = OUString(pString.get(), rtl_str_getLength(pString.get()), mnTextEnc); + else + rString.clear(); + bRetValue = true; + } + } + } + catch( const std::bad_alloc& ) + { + OSL_FAIL( "sd PropItem::Read bad alloc" ); + } + } + if ( bAlign ) + SeekRel( ( 4 - ( nItemSize & 3 ) ) & 3 ); // dword align + } + break; + + case VT_LPWSTR : + { + if (nItemSize) + { + auto nMaxSizePossible = remainingSize() / sizeof(sal_Unicode); + if (nItemSize > nMaxSizePossible) + { + SAL_WARN("sd.filter", "String of Len " << nItemSize << " claimed, only " << nMaxSizePossible << " possible"); + nItemSize = nMaxSizePossible; + } + } + + if (nItemSize) + { + try + { + std::unique_ptr pString( new sal_Unicode[ nItemSize ] ); + for (sal_uInt32 i = 0; i < nItemSize; ++i) + ReadUtf16( pString[ i ] ); + if ( pString[ nItemSize - 1 ] == 0 ) + { + if ( static_cast(nItemSize) > 1 ) + rString = OUString(pString.get(), lcl_getMaxSafeStrLen(nItemSize)); + else + rString.clear(); + bRetValue = true; + } + } + catch( const std::bad_alloc& ) + { + OSL_FAIL( "sd PropItem::Read bad alloc" ); + } + } + if ( bAlign && ( nItemSize & 1 ) ) + SeekRel( 2 ); // dword align + } + break; + } + if ( !bRetValue ) + Seek( nItemPos ); + return bRetValue; +} + +PropItem& PropItem::operator=( PropItem& rPropItem ) +{ + if ( this != &rPropItem ) + { + Seek( STREAM_SEEK_TO_BEGIN ); + delete[] static_cast(SwitchBuffer()); + + mnTextEnc = rPropItem.mnTextEnc; + SvMemoryStream::WriteBytes(rPropItem.GetData(), rPropItem.TellEnd()); + } + return *this; +} + +Section::Section( const Section& rSection ) + : mnTextEnc(rSection.mnTextEnc) +{ + for ( int i = 0; i < 16; i++ ) + aFMTID[ i ] = rSection.aFMTID[ i ]; + for(const std::unique_ptr& rEntry : rSection.maEntries) + maEntries.push_back(std::make_unique(*rEntry)); +} + +Section::Section( const sal_uInt8* pFMTID ) : mnTextEnc(RTL_TEXTENCODING_MS_1252) +{ + for ( int i = 0; i < 16; i++ ) + aFMTID[ i ] = pFMTID[ i ]; +} + +bool Section::GetProperty( sal_uInt32 nId, PropItem& rPropItem ) +{ + if ( nId ) + { + auto iter = std::find_if(maEntries.begin(), maEntries.end(), + [nId](const std::unique_ptr& rxEntry) { return rxEntry->mnId == nId; }); + + if (iter != maEntries.end()) + { + rPropItem.Clear(); + rPropItem.SetTextEncoding( mnTextEnc ); + rPropItem.WriteBytes( (*iter)->mpBuf.get(), (*iter)->mnSize ); + rPropItem.Seek( STREAM_SEEK_TO_BEGIN ); + return true; + } + } + return false; +} + +void Section::AddProperty( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ) +{ + // just a simple id check + + if ( !nId ) + return; + if ( nId == 0xffffffff ) + nId = 0; + + // do not allow same PropId's, sort + auto iter = std::find_if(maEntries.begin(), maEntries.end(), + [nId](const std::unique_ptr& rxEntry) { return rxEntry->mnId >= nId; }); + if (iter != maEntries.end()) + { + if ( (*iter)->mnId == nId ) + (*iter).reset(new PropEntry( nId, pBuf, nBufSize )); + else + maEntries.insert( iter, std::make_unique( nId, pBuf, nBufSize )); + } + else + { + maEntries.push_back( std::make_unique( nId, pBuf, nBufSize ) ); + } +} + +void Section::GetDictionary(PropDictionary& rDict) +{ + auto iter = std::find_if(maEntries.begin(), maEntries.end(), + [](const std::unique_ptr& rxEntry) { return rxEntry->mnId == 0; }); + + if (iter == maEntries.end()) + return; + + SvMemoryStream aStream( (*iter)->mpBuf.get(), (*iter)->mnSize, StreamMode::READ ); + aStream.Seek( STREAM_SEEK_TO_BEGIN ); + sal_uInt32 nDictCount(0); + aStream.ReadUInt32( nDictCount ); + for (sal_uInt32 i = 0; i < nDictCount; ++i) + { + sal_uInt32 nId(0), nSize(0); + aStream.ReadUInt32(nId).ReadUInt32(nSize); + if (!aStream.good() || nSize > aStream.remainingSize()) + break; + if (mnTextEnc == RTL_TEXTENCODING_UCS2) + nSize >>= 1; + if (!nSize) + continue; + OUString aString; + try + { + if ( mnTextEnc == RTL_TEXTENCODING_UCS2 ) + { + std::unique_ptr pWString( new sal_Unicode[nSize] ); + for (sal_uInt32 j = 0; j < nSize; ++j) + aStream.ReadUtf16(pWString[j]); + aString = OUString(pWString.get(), lcl_getMaxSafeStrLen(nSize)); + } + else + { + std::unique_ptr pString( new char[nSize] ); + aStream.ReadBytes(pString.get(), nSize); + aString = OUString(pString.get(), lcl_getMaxSafeStrLen(nSize), mnTextEnc); + } + } + catch( const std::bad_alloc& ) + { + OSL_FAIL( "sd Section::GetDictionary bad alloc" ); + } + if (aString.isEmpty()) + break; + rDict.insert( std::make_pair(aString,nId) ); + } +} + +void Section::Read( SotStorageStream *pStrm ) +{ + sal_uInt32 nSecOfs = pStrm->Tell(); + sal_uInt32 nStrmSize = pStrm->remainingSize(); + + mnTextEnc = RTL_TEXTENCODING_MS_1252; + sal_uInt32 nSecSize(0), nPropCount(0); + pStrm->ReadUInt32(nSecSize).ReadUInt32(nPropCount); + if (nSecSize > nStrmSize) + { + SAL_WARN("sd.filter", "Section Len " << nSecSize << " claimed, only " << nStrmSize << " possible"); + nSecSize = nStrmSize; + } + + while (nPropCount--) + { + sal_uInt32 nPropId(0), nPropOfs(0); + pStrm->ReadUInt32(nPropId).ReadUInt32(nPropOfs); + if (!pStrm->good()) + break; + auto nCurrent = pStrm->Tell(); + sal_uInt64 nOffset = nPropOfs + nSecOfs; + if (!checkSeek(*pStrm, nOffset)) + break; + if ( nPropId ) // do not read dictionary + { + sal_uInt32 nPropType(0), nVectorCount(0); + pStrm->ReadUInt32(nPropType); + + sal_uInt32 nPropSize = 4; + if ( nPropType & VT_VECTOR ) + { + pStrm->ReadUInt32( nVectorCount ); + nPropType &=~VT_VECTOR; + nPropSize += 4; + } + else + nVectorCount = 1; + + bool bVariant = ( nPropType == VT_VARIANT ); + + o3tl::sorted_vector aVisitedOffsets; + + for (sal_uInt32 i = 0; nPropSize && i < nVectorCount && pStrm->good(); ++i) + { + if ( bVariant ) + { + pStrm->ReadUInt32( nPropType ); + nPropSize += 4; + } + sal_uInt32 nTemp(0); + switch( nPropType ) + { + case VT_UI1 : + nPropSize++; + break; + + case VT_I2 : + case VT_UI2 : + case VT_BOOL : + nPropSize += 2; + break; + + case VT_I4 : + case VT_R4 : + case VT_UI4 : + case VT_ERROR : + nPropSize += 4; + break; + + case VT_I8 : + case VT_R8 : + case VT_CY : + case VT_UI8 : + case VT_DATE : + case VT_FILETIME : + nPropSize += 8; + break; + + case VT_BSTR : + pStrm->ReadUInt32( nTemp ); + nPropSize += ( nTemp + 4 ); + break; + + case VT_LPSTR : + pStrm->ReadUInt32( nTemp ); + nPropSize += ( nTemp + 4 ); + break; + + case VT_LPWSTR : + { + pStrm->ReadUInt32( nTemp ); + // looks like these are aligned to 4 bytes + sal_uInt32 nLength = nPropOfs + nSecOfs + nPropSize + ( nTemp << 1 ) + 4; + nPropSize += ( nTemp << 1 ) + 4 + (nLength % 4); + } + break; + + case VT_BLOB_OBJECT : + case VT_BLOB : + case VT_CF : + pStrm->ReadUInt32( nTemp ); + nPropSize += ( nTemp + 4 ); + break; + + case VT_CLSID : + case VT_STREAM : + case VT_STORAGE : + case VT_STREAMED_OBJECT : + case VT_STORED_OBJECT : + case VT_VARIANT : + case VT_VECTOR : + default : + nPropSize = 0; + } + if ( nPropSize ) + { + if ( ( nVectorCount - i ) > 1 ) + { + nOffset = nPropOfs + nSecOfs + nPropSize; + if (!checkSeek(*pStrm, nOffset)) + break; + // inserts returns false if an equivalent element already existed + if (!aVisitedOffsets.insert(nOffset).second) + { + SAL_WARN("sd.filter", "loop in Section::Read property list"); + break; + } + } + } + else + break; + } + if ( nPropSize ) + { + if ( nPropSize > nStrmSize ) + { + break; + } + pStrm->Seek( nPropOfs + nSecOfs ); + // make sure we don't overflow the section size + if( nPropSize > nSecSize - nSecOfs ) + nPropSize = nSecSize - nSecOfs; + std::unique_ptr pBuf( new sal_uInt8[ nPropSize ] ); + nPropSize = pStrm->ReadBytes(pBuf.get(), nPropSize); + AddProperty( nPropId, pBuf.get(), nPropSize ); + } + if ( nPropId == 1 ) + { + PropItem aPropItem; + if ( GetProperty( 1, aPropItem ) ) + { + aPropItem.ReadUInt32( nPropType ); + if ( nPropType == VT_I2 ) + { + sal_uInt16 nCodePage(0); + aPropItem.ReadUInt16(nCodePage); + + if ( nCodePage == 1200 ) + { + mnTextEnc = RTL_TEXTENCODING_UCS2; + } + else + { + mnTextEnc = rtl_getTextEncodingFromWindowsCodePage( nCodePage ); + if ( mnTextEnc == RTL_TEXTENCODING_DONTKNOW ) + mnTextEnc = RTL_TEXTENCODING_MS_1252; + } + } + else + { + mnTextEnc = RTL_TEXTENCODING_MS_1252; + } + } + } + } + else + { + sal_uInt32 nDictCount(0); + pStrm->ReadUInt32(nDictCount); + auto nMaxRecordsPossible = pStrm->remainingSize() / (sizeof(sal_uInt32)*2); + if (nDictCount > nMaxRecordsPossible) + { + SAL_WARN("sd.filter", "Dictionary count of " << nDictCount << " claimed, only " << nMaxRecordsPossible << " possible"); + nDictCount = nMaxRecordsPossible; + } + for (sal_uInt32 i = 0; i < nDictCount; ++i) + { + sal_uInt32 nSize(0); + pStrm->ReadUInt32( nSize ).ReadUInt32( nSize ); + if (!pStrm->good()) + break; + sal_uInt64 nPos = pStrm->Tell() + nSize; + if (!checkSeek(*pStrm, nPos)) + break; + } + sal_uInt32 nSize = pStrm->Tell(); + pStrm->Seek( nPropOfs + nSecOfs ); + nSize -= pStrm->Tell(); + if ( nSize > nStrmSize ) + { + break; + } + std::unique_ptr pBuf( new sal_uInt8[ nSize ] ); + nSize = pStrm->ReadBytes(pBuf.get(), nSize); + AddProperty( 0xffffffff, pBuf.get(), nSize ); + } + pStrm->Seek(nCurrent); + } + pStrm->Seek(nSecOfs + nSecSize); +} + +Section& Section::operator=( const Section& rSection ) +{ + if ( this != &rSection ) + { + memcpy( static_cast(aFMTID), static_cast(rSection.aFMTID), 16 ); + + for(const std::unique_ptr& rEntry : rSection.maEntries) + maEntries.push_back(std::make_unique(*rEntry)); + } + return *this; +} + +PropRead::PropRead( SotStorage& rStorage, const OUString& rName ) : + mbStatus ( false ), + mnByteOrder ( 0xfffe ) +{ + if ( rStorage.IsStream( rName ) ) + { + mpSvStream = rStorage.OpenSotStream( rName, StreamMode::STD_READ ); + if ( mpSvStream.is() ) + { + mpSvStream->SetEndian( SvStreamEndian::LITTLE ); + memset( mApplicationCLSID, 0, 16 ); + mbStatus = true; + } + } +} + +const Section* PropRead::GetSection( const sal_uInt8* pFMTID ) +{ + auto it = std::find_if(maSections.begin(), maSections.end(), + [&pFMTID](const std::unique_ptr
& rxSection) { return memcmp( rxSection->GetFMTID(), pFMTID, 16 ) == 0; }); + if (it != maSections.end()) + return it->get(); + return nullptr; +} + +void PropRead::Read() +{ + maSections.clear(); + + if ( !mbStatus ) + return; + + sal_uInt16 mnVersionLo; + sal_uInt16 mnVersionHi; + sal_uInt16 mnFormat; + mpSvStream->ReadUInt16( mnByteOrder ).ReadUInt16( mnFormat ).ReadUInt16( mnVersionLo ).ReadUInt16( mnVersionHi ); + if ( mnByteOrder != 0xfffe ) + return; + + std::vector aSectCLSID(16); + mpSvStream->ReadBytes(mApplicationCLSID, 16); + sal_uInt32 nSections(0); + mpSvStream->ReadUInt32(nSections); + if ( nSections > 2 ) // sj: PowerPoint documents are containing max 2 sections + { + mbStatus = false; + } + else + for ( sal_uInt32 i = 0; i < nSections; i++ ) + { + mpSvStream->ReadBytes(aSectCLSID.data(), aSectCLSID.size()); + sal_uInt32 nSectionOfs(0); + mpSvStream->ReadUInt32( nSectionOfs ); + sal_uInt32 nCurrent = mpSvStream->Tell(); + if (checkSeek(*mpSvStream, nSectionOfs)) + { + Section aSection(aSectCLSID.data()); + aSection.Read(mpSvStream.get()); + maSections.push_back(std::make_unique
(aSection)); + } + mpSvStream->Seek( nCurrent ); + } +} + +PropRead& PropRead::operator=( const PropRead& rPropRead ) +{ + if ( this != &rPropRead ) + { + mbStatus = rPropRead.mbStatus; + mpSvStream = rPropRead.mpSvStream; + + mnByteOrder = rPropRead.mnByteOrder; + memcpy( mApplicationCLSID, rPropRead.mApplicationCLSID, 16 ); + + for(const std::unique_ptr
& rSection : rPropRead.maSections) + maSections.push_back(std::make_unique
(*rSection)); + } + return *this; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/ppt/propread.hxx b/sd/source/filter/ppt/propread.hxx new file mode 100644 index 000000000..402a04624 --- /dev/null +++ b/sd/source/filter/ppt/propread.hxx @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +// SummaryInformation +#define PID_TITLE 0x02 +#define PID_SUBJECT 0x03 +#define PID_AUTHOR 0x04 +#define PID_KEYWORDS 0x05 +#define PID_COMMENTS 0x06 +#define PID_TEMPLATE 0x07 +#define PID_LASTAUTHOR 0x08 +#define PID_REVNUMBER 0x09 +#define PID_CREATE_DTM 0x0c + +// DocumentSummaryInformation +#define PID_SLIDECOUNT 0x07 +#define PID_HEADINGPAIR 0x0c +#define PID_DOCPARTS 0x0d + +#define VT_EMPTY 0 +#define VT_NULL 1 +#define VT_I2 2 +#define VT_I4 3 +#define VT_R4 4 +#define VT_R8 5 +#define VT_CY 6 +#define VT_DATE 7 +#define VT_BSTR 8 +#define VT_UI4 9 +#define VT_ERROR 10 +#define VT_BOOL 11 +#define VT_VARIANT 12 +#define VT_DECIMAL 14 +#define VT_I1 16 +#define VT_UI1 17 +#define VT_UI2 18 +#define VT_I8 20 +#define VT_UI8 21 +#define VT_INT 22 +#define VT_UINT 23 +#define VT_LPSTR 30 +#define VT_LPWSTR 31 +#define VT_FILETIME 64 +#define VT_BLOB 65 +#define VT_STREAM 66 +#define VT_STORAGE 67 +#define VT_STREAMED_OBJECT 68 +#define VT_STORED_OBJECT 69 +#define VT_BLOB_OBJECT 70 +#define VT_CF 71 +#define VT_CLSID 72 +#define VT_VECTOR 0x1000 +#define VT_ARRAY 0x2000 +#define VT_BYREF 0x4000 +#define VT_TYPEMASK 0xFFF + +typedef std::map PropDictionary; + +struct PropEntry +{ + sal_uInt32 mnId; + sal_uInt32 mnSize; + std::unique_ptr mpBuf; + + PropEntry( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ); + PropEntry( const PropEntry& rProp ); + + PropEntry& operator=(const PropEntry& rPropEntry); +}; + +class PropItem : public SvMemoryStream +{ + sal_uInt16 mnTextEnc; + +public: + PropItem() + : mnTextEnc(RTL_TEXTENCODING_DONTKNOW) + { + } + void Clear(); + + void SetTextEncoding( sal_uInt16 nTextEnc ){ mnTextEnc = nTextEnc; }; + bool Read( OUString& rString, sal_uInt32 nType = VT_EMPTY, bool bDwordAlign = true ); + PropItem& operator=( PropItem& rPropItem ); +}; + +class Section final +{ + sal_uInt16 mnTextEnc; + std::vector > maEntries; + + sal_uInt8 aFMTID[ 16 ]; + + void AddProperty( sal_uInt32 nId, const sal_uInt8* pBuf, sal_uInt32 nBufSize ); + + public: + explicit Section( const sal_uInt8* pFMTID ); + Section( const Section& rSection ); + + Section& operator=( const Section& rSection ); + bool GetProperty( sal_uInt32 nId, PropItem& rPropItem ); + void GetDictionary( PropDictionary& rDict ); + const sal_uInt8* GetFMTID() const { return aFMTID; }; + void Read( SotStorageStream* pStrm ); +}; + +class PropRead +{ + bool mbStatus; + tools::SvRef mpSvStream; + + sal_uInt16 mnByteOrder; + sal_uInt8 mApplicationCLSID[ 16 ]; + std::vector > maSections; + + public: + PropRead( SotStorage& rSvStorage, const OUString& rName ); + + PropRead& operator=( const PropRead& rPropRead ); + const Section* GetSection( const sal_uInt8* pFMTID ); + bool IsValid() const { return mbStatus; }; + void Read(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/sdfilter.cxx b/sd/source/filter/sdfilter.cxx new file mode 100644 index 000000000..11ad11d76 --- /dev/null +++ b/sd/source/filter/sdfilter.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::task; + + +SdFilter::SdFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell ) +: mxModel( rDocShell.GetModel() ) +, mrMedium( rMedium ) +, mrDocShell( rDocShell ) +, mrDocument( *rDocShell.GetDoc() ) +, mbIsDraw( rDocShell.GetDocumentType() == DocumentType::Draw ) +{ +} + +SdFilter::~SdFilter() +{ +} + +OUString SdFilter::ImplGetFullLibraryName( std::u16string_view rLibraryName ) +{ + return OUString(SVLIBRARY("?")).replaceFirst( "?", rLibraryName ); +} + +#ifndef DISABLE_DYNLOADING + +static std::map> g_SdModuleMap; + +extern "C" { static void thisModule() {} } + +oslGenericFunction SdFilter::GetLibrarySymbol( const OUString& rLibraryName, const OUString &rFnSymbol ) +{ + osl::Module *pMod = nullptr; + auto it = g_SdModuleMap.find(rLibraryName); + if (it != g_SdModuleMap.end()) + pMod = it->second.get(); + + if (!pMod) + { + pMod = new osl::Module; + if (pMod->loadRelative(&thisModule, ImplGetFullLibraryName(rLibraryName), + SAL_LOADMODULE_GLOBAL | SAL_LOADMODULE_LAZY)) + g_SdModuleMap[rLibraryName] = std::unique_ptr(pMod); + else + { + delete pMod; + pMod = nullptr; + } + } + if (!pMod) + return nullptr; + else + return pMod->getFunctionSymbol(rFnSymbol); +} + +void SdFilter::Preload() +{ + (void)GetLibrarySymbol("sdfilt", "ImportPPT"); + (void)GetLibrarySymbol("icg", "ImportCGM"); +} + +#endif + +void SdFilter::CreateStatusIndicator() +{ + // The status indicator must be retrieved from the provided medium arguments + const SfxUnoAnyItem* pStatusBarItem = + mrMedium.GetItemSet()->GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + + if ( pStatusBarItem ) + pStatusBarItem->GetValue() >>= mxStatusIndicator; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/sdpptwrp.cxx b/sd/source/filter/sdpptwrp.cxx new file mode 100644 index 000000000..59829f854 --- /dev/null +++ b/sd/source/filter/sdpptwrp.cxx @@ -0,0 +1,377 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::frame; + +typedef sal_Bool ( *ExportPPTPointer )( const std::vector< css::beans::PropertyValue >&, tools::SvRef const&, + Reference< XModel > const &, + Reference< XStatusIndicator > const &, + SvMemoryStream*, sal_uInt32 nCnvrtFlags ); + +typedef sal_Bool ( *ImportPPTPointer )( SdDrawDocument*, SvStream&, SotStorage&, SfxMedium& ); + +typedef sal_Bool ( *SaveVBAPointer )( SfxObjectShell&, SvMemoryStream*& ); + +#ifdef DISABLE_DYNLOADING + +extern "C" sal_Bool ExportPPT( const std::vector< css::beans::PropertyValue >&, tools::SvRef const&, + Reference< XModel > const &, + Reference< XStatusIndicator > const &, + SvMemoryStream*, sal_uInt32 nCnvrtFlags ); + +extern "C" sal_Bool ImportPPT( SdDrawDocument*, SvStream&, SotStorage&, SfxMedium& ); + +extern "C" sal_Bool SaveVBA( SfxObjectShell&, SvMemoryStream*& ); + +#endif + + +SdPPTFilter::SdPPTFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell ) : + SdFilter( rMedium, rDocShell ), + pBas ( nullptr ) +{ +} + +SdPPTFilter::~SdPPTFilter() +{ + delete pBas; // deleting the compressed basic storage +} + +static void lcl_getListOfStreams(SotStorage * pStorage, comphelper::SequenceAsHashMap& aStreamsData, const OUString& sPrefix) +{ + SvStorageInfoList aElements; + pStorage->FillInfoList(&aElements); + for (const auto & aElement : aElements) + { + OUString sStreamFullName = sPrefix.getLength() ? sPrefix + "/" + aElement.GetName() : aElement.GetName(); + if (aElement.IsStorage()) + { + tools::SvRef xSubStorage = pStorage->OpenSotStorage(aElement.GetName(), StreamMode::STD_READ | StreamMode::SHARE_DENYALL); + lcl_getListOfStreams(xSubStorage.get(), aStreamsData, sStreamFullName); + } + else + { + // Read stream + tools::SvRef rStream = pStorage->OpenSotStream(aElement.GetName(), StreamMode::READ | StreamMode::SHARE_DENYALL); + if (rStream.is()) + { + sal_Int32 nStreamSize = rStream->GetSize(); + Sequence< sal_Int8 > oData; + oData.realloc(nStreamSize); + sal_Int32 nReadBytes = rStream->ReadBytes(oData.getArray(), nStreamSize); + if (nStreamSize == nReadBytes) + aStreamsData[sStreamFullName] <<= oData; + } + } + } +} + +static tools::SvRef lcl_DRMDecrypt(const SfxMedium& rMedium, const tools::SvRef& rStorage, std::shared_ptr& rNewStorageStrm) +{ + tools::SvRef aNewStorage; + + // We have DRM encrypted storage. We should try to decrypt it first, if we can + Sequence< Any > aArguments; + Reference xComponentContext(comphelper::getProcessComponentContext()); + Reference< css::packages::XPackageEncryption > xPackageEncryption( + xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.oox.crypto.DRMDataSpace", aArguments, xComponentContext), UNO_QUERY); + + if (!xPackageEncryption.is()) + { + // We do not know how to decrypt this + return aNewStorage; + } + + comphelper::SequenceAsHashMap aStreamsData; + lcl_getListOfStreams(rStorage.get(), aStreamsData, ""); + + try { + Sequence aStreams = aStreamsData.getAsConstNamedValueList(); + if (!xPackageEncryption->readEncryptionInfo(aStreams)) + { + // We failed with decryption + return aNewStorage; + } + + tools::SvRef rContentStream = rStorage->OpenSotStream("\011DRMContent", StreamMode::READ | StreamMode::SHARE_DENYALL); + if (!rContentStream.is()) + { + return aNewStorage; + } + + rNewStorageStrm = std::make_shared(); + + Reference xInputStream(new utl::OSeekableInputStreamWrapper(rContentStream.get(), false)); + Reference xDecryptedStream(new utl::OSeekableOutputStreamWrapper(*rNewStorageStrm)); + + if (!xPackageEncryption->decrypt(xInputStream, xDecryptedStream)) + { + // We failed with decryption + return aNewStorage; + } + + rNewStorageStrm->Seek(0); + + // Further reading is done from new document + aNewStorage = new SotStorage(*rNewStorageStrm); + + // Set the media descriptor data + Sequence aEncryptionData = xPackageEncryption->createEncryptionData(""); + rMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, Any(aEncryptionData))); + } + catch (const std::exception&) + { + return aNewStorage; + } + + return aNewStorage; +} + +bool SdPPTFilter::Import() +{ + bool bRet = false; + std::shared_ptr aDecryptedStorageStrm; + tools::SvRef pStorage = new SotStorage( mrMedium.GetInStream(), false ); + if( !pStorage->GetError() ) + { + /* check if there is a dualstorage, then the + document is probably a PPT95 containing PPT97 */ + tools::SvRef xDualStorage; + OUString sDualStorage( "PP97_DUALSTORAGE" ); + if ( pStorage->IsContained( sDualStorage ) ) + { + xDualStorage = pStorage->OpenSotStorage( sDualStorage, StreamMode::STD_READ ); + pStorage = xDualStorage; + } + if (pStorage->IsContained("\011DRMContent")) + { + // Document is DRM encrypted + pStorage = lcl_DRMDecrypt(mrMedium, pStorage, aDecryptedStorageStrm); + } + tools::SvRef pDocStream(pStorage->OpenSotStream( "PowerPoint Document" , StreamMode::STD_READ )); + if( pDocStream ) + { + pDocStream->SetVersion( pStorage->GetVersion() ); + pDocStream->SetCryptMaskKey(pStorage->GetKey()); + + if ( pStorage->IsStream( "EncryptedSummary" ) ) + mrMedium.SetError(ERRCODE_SVX_READ_FILTER_PPOINT); + else + { +#ifdef DISABLE_DYNLOADING + ImportPPTPointer pPPTImport = ImportPPT; +#else + ImportPPTPointer pPPTImport = reinterpret_cast< ImportPPTPointer >( + SdFilter::GetLibrarySymbol(mrMedium.GetFilter()->GetUserData(), "ImportPPT")); +#endif + + if ( pPPTImport ) + bRet = pPPTImport( &mrDocument, *pDocStream, *pStorage, mrMedium ); + + if ( !bRet ) + mrMedium.SetError(SVSTREAM_WRONGVERSION); + } + } + } + + return bRet; +} + +bool SdPPTFilter::Export() +{ + bool bRet = false; + + if( mxModel.is() ) + { +#ifdef DISABLE_DYNLOADING + ExportPPTPointer PPTExport = ExportPPT; +#else + ExportPPTPointer PPTExport = reinterpret_cast< ExportPPTPointer >( + SdFilter::GetLibrarySymbol(mrMedium.GetFilter()->GetUserData(), "ExportPPT")); +#endif + + if( PPTExport) + { + sal_uInt32 nCnvrtFlags = 0; + const SvtFilterOptions& rFilterOptions = SvtFilterOptions::Get(); + if ( rFilterOptions.IsMath2MathType() ) + nCnvrtFlags |= OLE_STARMATH_2_MATHTYPE; + if ( rFilterOptions.IsWriter2WinWord() ) + nCnvrtFlags |= OLE_STARWRITER_2_WINWORD; + if ( rFilterOptions.IsCalc2Excel() ) + nCnvrtFlags |= OLE_STARCALC_2_EXCEL; + if ( rFilterOptions.IsImpress2PowerPoint() ) + nCnvrtFlags |= OLE_STARIMPRESS_2_POWERPOINT; + if ( rFilterOptions.IsEnablePPTPreview() ) + nCnvrtFlags |= 0x8000; + + CreateStatusIndicator(); + + //OUString sBaseURI( "BaseURI"); + std::vector< PropertyValue > aProperties; + PropertyValue aProperty; + aProperty.Name = "BaseURI"; + aProperty.Value <<= mrMedium.GetBaseURL( true ); + aProperties.push_back( aProperty ); + + SvStream * pOutputStrm = mrMedium.GetOutStream(); + + Sequence< NamedValue > aEncryptionData; + Reference< css::packages::XPackageEncryption > xPackageEncryption; + const SfxUnoAnyItem* pEncryptionDataItem = SfxItemSet::GetItem(mrMedium.GetItemSet(), SID_ENCRYPTIONDATA, false); + std::shared_ptr pMediaStrm; + if (pEncryptionDataItem && (pEncryptionDataItem->GetValue() >>= aEncryptionData)) + { + ::comphelper::SequenceAsHashMap aHashData(aEncryptionData); + OUString sCryptoType = aHashData.getUnpackedValueOrDefault("CryptoType", OUString()); + + if (sCryptoType.getLength()) + { + Reference xComponentContext(comphelper::getProcessComponentContext()); + Sequence aArguments{ + Any(NamedValue("Binary", Any(true))) }; + xPackageEncryption.set( + xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.comp.oox.crypto." + sCryptoType, aArguments, xComponentContext), UNO_QUERY); + + if (xPackageEncryption.is()) + { + // We have an encryptor. Export document into memory stream and encrypt it later + pMediaStrm = std::make_shared(); + pOutputStrm = pMediaStrm.get(); + + // Temp removal of EncryptionData to avoid password protection triggering + mrMedium.GetItemSet()->ClearItem(SID_ENCRYPTIONDATA); + } + } + } + + tools::SvRef xStorRef = new SotStorage(pOutputStrm, false); + + if (xStorRef.is()) + { + bRet = PPTExport(aProperties, xStorRef, mxModel, mxStatusIndicator, pBas, nCnvrtFlags); + xStorRef->Commit(); + + if (xPackageEncryption.is()) + { + // Perform DRM encryption + pOutputStrm->Seek(0); + + xPackageEncryption->setupEncryption(aEncryptionData); + + Reference xInputStream(new utl::OSeekableInputStreamWrapper(pOutputStrm, false)); + Sequence aStreams = xPackageEncryption->encrypt(xInputStream); + + tools::SvRef xEncryptedRootStrg = new SotStorage(mrMedium.GetOutStream(), false); + for (const NamedValue & aStreamData : std::as_const(aStreams)) + { + // To avoid long paths split and open substorages recursively + // Splitting paths manually, since comphelper::string::split is trimming special characters like \0x01, \0x09 + tools::SvRef pStorage = xEncryptedRootStrg.get(); + OUString sFileName; + sal_Int32 idx = 0; + do + { + OUString sPathElem = aStreamData.Name.getToken(0, L'/', idx); + if (!sPathElem.isEmpty()) + { + if (idx < 0) + { + sFileName = sPathElem; + } + else + { + pStorage = pStorage->OpenSotStorage(sPathElem); + } + } + } while (pStorage && idx >= 0); + + if (!pStorage) + { + bRet = false; + break; + } + + tools::SvRef pStream = pStorage->OpenSotStream(sFileName); + if (!pStream) + { + bRet = false; + break; + } + Sequence aStreamContent; + aStreamData.Value >>= aStreamContent; + size_t nBytesWritten = pStream->WriteBytes(aStreamContent.getConstArray(), aStreamContent.getLength()); + if (nBytesWritten != static_cast(aStreamContent.getLength())) + { + bRet = false; + break; + } + } + xEncryptedRootStrg->Commit(); + + // Restore encryption data + mrMedium.GetItemSet()->Put(SfxUnoAnyItem(SID_ENCRYPTIONDATA, Any(aEncryptionData))); + } + } + } + } + + return bRet; +} + +void SdPPTFilter::PreSaveBasic() +{ + const SvtFilterOptions& rFilterOptions = SvtFilterOptions::Get(); + if( rFilterOptions.IsLoadPPointBasicStorage() ) + { +#ifdef DISABLE_DYNLOADING + SaveVBAPointer pSaveVBA= SaveVBA; +#else + SaveVBAPointer pSaveVBA = reinterpret_cast< SaveVBAPointer >( + SdFilter::GetLibrarySymbol(mrMedium.GetFilter()->GetUserData(), "SaveVBA")); +#endif + if( pSaveVBA ) + pSaveVBA( static_cast(mrDocShell), pBas ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/xml/sdtransform.cxx b/sd/source/filter/xml/sdtransform.cxx new file mode 100644 index 000000000..4e296eaf6 --- /dev/null +++ b/sd/source/filter/xml/sdtransform.cxx @@ -0,0 +1,368 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "sdtransform.hxx" + +using namespace ::com::sun::star::style; + +namespace { + +class SdTransformOOo2xDocument +{ +public: + explicit SdTransformOOo2xDocument( SdDrawDocument& rDocument ); + + void transform(); + + void transformMasterPages(); + void transformDrawPages(); + + void transformStyles(); + void transformStyles( SfxStyleFamily eFam ); + void transformStyle( SfxStyleSheetBase& rSheet ); + + void transformShapes( SdrObjList const & rShapes ); + void transformShape( SdrObject& rObj ); + + void transformTextShape( SdrTextObj& rTextShape ); + + bool getBulletState( const SfxItemSet& rSet, SfxStyleSheetBase* pSheet, bool& rState ); + static bool getBulletState( const SfxItemSet& rSet, sal_uInt16 nWhich, bool& rState ); + + static bool transformItemSet( SfxItemSet& rSet, bool bNumbering ); + + static bool removeAlienAttributes( SfxItemSet& rSet ); + static bool removeAlienAttributes( SfxItemSet& rSet, sal_uInt16 nWhich ); + + SdDrawDocument& mrDocument; + SdrOutliner& mrOutliner; +}; + +} + +/** transforms the given model from OOo 2.x to OOo 3.x. This maps + the deprecated EE_PARA_BULLETSTATE and clears the EE_PARA_LRSPACE + if used together with a EE_PARA_NUMBULLET */ +void TransformOOo2xDocument( SdDrawDocument* pDocument ) +{ + if( pDocument ) + { + SdTransformOOo2xDocument aTransformer( *pDocument ); + aTransformer.transform(); + } +} + +constexpr OUStringLiteral gsEnableNumbering( u"enable-numbering" ); +constexpr OUStringLiteral gsTextNamespace( u"urn:oasis:names:tc:opendocument:xmlns:text:1.0" ); +constexpr OUStringLiteral gsTrue( u"true" ); + +SdTransformOOo2xDocument::SdTransformOOo2xDocument( SdDrawDocument& rDocument ) +: mrDocument( rDocument ) +, mrOutliner( rDocument.GetDrawOutliner() ) +{ +} + +void SdTransformOOo2xDocument::transform() +{ + transformMasterPages(); + transformDrawPages(); + transformStyles(); +} + +void SdTransformOOo2xDocument::transformMasterPages() +{ + sal_uInt16 nMasterPageCount = mrDocument.GetMasterPageCount(); + for( sal_uInt16 nMasterPage = 0; nMasterPage < nMasterPageCount; nMasterPage++ ) + { + SdrObjList* pPage = mrDocument.GetMasterPage( nMasterPage ); + if( pPage ) + transformShapes( *pPage ); + } +} + +void SdTransformOOo2xDocument::transformDrawPages() +{ + sal_uInt16 nPageCount = mrDocument.GetPageCount(); + for( sal_uInt16 nPage = 0; nPage < nPageCount; nPage++ ) + { + SdrObjList* pPage = mrDocument.GetPage( nPage ); + if( pPage ) + transformShapes( *pPage ); + } +} + +void SdTransformOOo2xDocument::transformStyles() +{ + transformStyles( SfxStyleFamily::Para ); + transformStyles( SfxStyleFamily::Page ); +} + +void SdTransformOOo2xDocument::transformStyles( SfxStyleFamily eFam ) +{ + + rtl::Reference< SfxStyleSheetBasePool > xStyleSheetPool( mrDocument.GetStyleSheetPool() ); + + SfxStyleSheetIterator aIter( xStyleSheetPool.get(), eFam ); + + SfxStyleSheetBase* pSheet = aIter.First(); + while( pSheet ) + { + transformStyle( *pSheet ); + pSheet = aIter.Next(); + } +} + +void SdTransformOOo2xDocument::transformStyle( SfxStyleSheetBase& rSheet ) +{ + SfxItemSet& rSet = rSheet.GetItemSet(); + + bool bState = false; + getBulletState( rSheet.GetItemSet(), rSheet.GetPool()->Find( rSheet.GetParent(), rSheet.GetFamily() ), bState ); + + transformItemSet( rSet, bState ); + removeAlienAttributes( rSet ); +} + +void SdTransformOOo2xDocument::transformShapes( SdrObjList const & rShapes ) +{ + const size_t nShapeCount = rShapes.GetObjCount(); + for( size_t nShape = 0; nShape < nShapeCount; ++nShape ) + { + SdrObject* pObj = rShapes.GetObj( nShape ); + if( pObj ) + transformShape( *pObj ); + } +} + +void SdTransformOOo2xDocument::transformShape( SdrObject& rObj ) +{ + SdrTextObj* pTextShape = dynamic_cast< SdrTextObj* >( &rObj ); + if( pTextShape ) + { + transformTextShape( *pTextShape ); + return; + } + + SdrObjGroup* pGroupShape = dynamic_cast< SdrObjGroup* >( &rObj ); + if( pGroupShape ) + { + SdrObjList* pObjList = pGroupShape->GetSubList(); + if( pObjList ) + transformShapes( *pObjList ); + return; + } +} + +void SdTransformOOo2xDocument::transformTextShape( SdrTextObj& rTextShape ) +{ + + if(rTextShape.IsEmptyPresObj()) + return; + + OutlinerParaObject* pOPO = rTextShape.GetOutlinerParaObject(); + if (!pOPO) + return; + + mrOutliner.SetText( *pOPO ); + + sal_Int32 nCount = mrOutliner.GetParagraphCount(); + + bool bChange = false; + + for(sal_Int32 nPara = 0; nPara < nCount; nPara++) + { + SfxItemSet aParaSet( mrOutliner.GetParaAttribs( nPara ) ); + + bool bItemChange = false; + + bool bState = false; + const sal_Int16 nDepth = mrOutliner.GetDepth( nPara ); + if( (nDepth != -1) && (!getBulletState( aParaSet, mrOutliner.GetStyleSheet( nPara ), bState ) || !bState) ) + { + // disable bullet if text::enable-bullet="false" is found + if( (nDepth > 0 ) && (rTextShape.GetObjInventor() == SdrInventor::Default) && (rTextShape.GetObjIdentifier() == SdrObjKind::OutlineText) ) + { + // for outline object and level > 0 burn in the style sheet because it will be changed to "outline 1" + SfxStyleSheet* pStyleSheet = mrOutliner.GetStyleSheet( nPara ); + + if( pStyleSheet ) + { + // optimize me: only put items hard into paragraph that are not equal to "outline 1" style! + SfxItemSet& rStyleSet = pStyleSheet->GetItemSet(); + + SfxWhichIter aIter(aParaSet); + sal_uInt16 nWhich(aIter.FirstWhich()); + + // now set all none hard attributes from the style + while(nWhich) + { + if(SfxItemState::SET != aIter.GetItemState()) + { + aParaSet.Put(rStyleSet.Get(nWhich)); + bItemChange = true; + } + + nWhich = aIter.NextWhich(); + } + } + } + + mrOutliner.SetDepth( mrOutliner.GetParagraph( nPara ), -1 ); + + bChange = true; + } + + bItemChange |= transformItemSet( aParaSet, bState ); + + bItemChange |= removeAlienAttributes( aParaSet ); + + if( bItemChange ) + { + mrOutliner.SetParaAttribs( nPara, aParaSet ); + bChange = true; + } + } + + if( bChange ) + rTextShape.SetOutlinerParaObject(mrOutliner.CreateParaObject()); + + mrOutliner.Clear(); +} + +bool SdTransformOOo2xDocument::getBulletState( const SfxItemSet& rSet, SfxStyleSheetBase* pSheet, bool& rState ) +{ + if( getBulletState( rSet, EE_PARA_XMLATTRIBS, rState ) ) + return true; + + if( getBulletState( rSet, SDRATTR_XMLATTRIBUTES, rState ) ) + return true; + + if( pSheet && getBulletState( pSheet->GetItemSet(), pSheet->GetPool()->Find( pSheet->GetParent(), pSheet->GetFamily() ), rState ) ) + return true; + + return false; +} + +bool SdTransformOOo2xDocument::getBulletState( const SfxItemSet& rSet, sal_uInt16 nWhich, bool& rState ) +{ + if( rSet.GetItemState( nWhich ) == SfxItemState::SET ) + { + const SvXMLAttrContainerItem& rAttr = *rSet.GetItem( nWhich ); + + const sal_uInt16 nCount = rAttr.GetAttrCount(); + for( sal_uInt16 nItem = 0; nItem < nCount; nItem++ ) + { + if( ( rAttr.GetAttrLName( nItem ) == gsEnableNumbering ) && ( rAttr.GetAttrNamespace( nItem ) == gsTextNamespace ) ) + { + const OUString& sValue( rAttr.GetAttrValue( nItem ) ); + rState = sValue == gsTrue; + return true; + } + } + } + + return false; +} + +bool SdTransformOOo2xDocument::transformItemSet( SfxItemSet& rSet, bool bNumbering ) +{ + bool bRet = false; + const SvxLRSpaceItem* pItem = bNumbering ? rSet.GetItem(EE_PARA_LRSPACE) : nullptr; + if (pItem) + { + SvxLRSpaceItem aItem(*pItem); + if( (aItem.GetLeft() != 0) || (aItem.GetTextFirstLineOffset() != 0) ) + { + aItem.SetLeftValue( 0 ); + aItem.SetTextFirstLineOffset( 0 ); + rSet.Put( aItem ); + bRet = true; + } + } + + return bRet; +} + +bool SdTransformOOo2xDocument::removeAlienAttributes( SfxItemSet& rSet ) +{ + bool b = removeAlienAttributes( rSet, EE_PARA_XMLATTRIBS ); + b |= removeAlienAttributes( rSet, SDRATTR_XMLATTRIBUTES ); + return b; +} + +bool SdTransformOOo2xDocument::removeAlienAttributes( SfxItemSet& rSet, sal_uInt16 nWhich ) +{ + if( rSet.GetItemState( nWhich ) == SfxItemState::SET ) + { + const SvXMLAttrContainerItem& rAttr = *rSet.GetItem( nWhich ); + + const sal_uInt16 nCount = rAttr.GetAttrCount(); + for( sal_uInt16 nItem = 0; nItem < nCount; nItem++ ) + { + if( ( rAttr.GetAttrLName( nItem ) == gsEnableNumbering ) && ( rAttr.GetAttrNamespace( nItem ) == gsTextNamespace ) ) + { + if( nCount == 1 ) + { + rSet.ClearItem( nWhich ); + } + else + { + SvXMLAttrContainerItem aNewItem( nWhich ); + + const sal_uInt16 nFound = nItem; + for( nItem = 0; nItem < nCount; nItem++ ) + { + if( nItem != nFound ) + { + OUString const& rNamespace(rAttr.GetAttrNamespace(nItem)); + OUString const& rPrefix(rAttr.GetAttrPrefix(nItem)); + if (rPrefix.isEmpty()) + { + aNewItem.AddAttr(rAttr.GetAttrLName(nItem), rAttr.GetAttrValue(nItem)); + } + else + { + aNewItem.AddAttr(rPrefix, rNamespace, rAttr.GetAttrLName(nItem), rAttr.GetAttrValue(nItem)); + } + } + } + + rSet.Put( aNewItem ); + } + return true; + } + } + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/xml/sdtransform.hxx b/sd/source/filter/xml/sdtransform.hxx new file mode 100644 index 000000000..64bb1c0a1 --- /dev/null +++ b/sd/source/filter/xml/sdtransform.hxx @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +void TransformOOo2xDocument(SdDrawDocument* pDocument); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/filter/xml/sdxmlwrp.cxx b/sd/source/filter/xml/sdxmlwrp.cxx new file mode 100644 index 000000000..90ef68e35 --- /dev/null +++ b/sd/source/filter/xml/sdxmlwrp.cxx @@ -0,0 +1,1056 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// include necessary for XML progress bar at load time +#include +#include +#include + +#include +#include +#include +#include "sdtransform.hxx" +#include + +#include +#include +#include + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; +using namespace com::sun::star::document; +using namespace comphelper; + +#define SD_XML_READERROR ErrCode(1234) + +char const sXML_export_impress_meta_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisMetaExporter"; +char const sXML_export_impress_styles_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisStylesExporter"; +char const sXML_export_impress_content_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisContentExporter"; +char const sXML_export_impress_settings_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisSettingsExporter"; + +char const sXML_export_draw_meta_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisMetaExporter"; +char const sXML_export_draw_styles_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisStylesExporter"; +char const sXML_export_draw_content_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisContentExporter"; +char const sXML_export_draw_settings_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisSettingsExporter"; + +char const sXML_import_impress_meta_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisMetaImporter"; +char const sXML_import_impress_styles_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisStylesImporter"; +char const sXML_import_impress_content_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisContentImporter"; +char const sXML_import_impress_settings_oasis_service[] = "com.sun.star.comp.Impress.XMLOasisSettingsImporter"; + +char const sXML_import_draw_meta_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisMetaImporter"; +char const sXML_import_draw_styles_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisStylesImporter"; +char const sXML_import_draw_content_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisContentImporter"; +char const sXML_import_draw_settings_oasis_service[] = "com.sun.star.comp.Draw.XMLOasisSettingsImporter"; + +// OOo +char const sXML_import_impress_meta_ooo_service[] = "com.sun.star.comp.Impress.XMLMetaImporter"; +char const sXML_import_impress_styles_ooo_service[] = "com.sun.star.comp.Impress.XMLStylesImporter"; +char const sXML_import_impress_content_ooo_service[] = "com.sun.star.comp.Impress.XMLContentImporter"; +char const sXML_import_impress_settings_ooo_service[] = "com.sun.star.comp.Impress.XMLSettingsImporter"; + +char const sXML_import_draw_meta_ooo_service[] = "com.sun.star.comp.Draw.XMLMetaImporter"; +char const sXML_import_draw_styles_ooo_service[] = "com.sun.star.comp.Draw.XMLStylesImporter"; +char const sXML_import_draw_content_ooo_service[] = "com.sun.star.comp.Draw.XMLContentImporter"; +char const sXML_import_draw_settings_ooo_service[] = "com.sun.star.comp.Draw.XMLSettingsImporter"; + +namespace { + +struct XML_SERVICEMAP +{ + const char* mpService; + const char* mpStream; +}; + +struct XML_SERVICES +{ + const char* mpMeta; + const char* mpStyles; + const char* mpContent; + const char* mpSettings; +}; + +} + +static XML_SERVICES const * getServices( bool bImport, bool bDraw, sal_uLong nStoreVer ) +{ + // Expect that export always sets nStoreVer to SOFFICE_FILEFORMAT_8. + assert(bImport || nStoreVer != SOFFICE_FILEFORMAT_60); + + static XML_SERVICES const gServices[] = + { + { sXML_import_impress_meta_oasis_service, sXML_import_impress_styles_oasis_service, sXML_import_impress_content_oasis_service, sXML_import_impress_settings_oasis_service }, + { sXML_import_draw_meta_oasis_service, sXML_import_draw_styles_oasis_service, sXML_import_draw_content_oasis_service, sXML_import_draw_settings_oasis_service }, + { sXML_export_impress_meta_oasis_service, sXML_export_impress_styles_oasis_service, sXML_export_impress_content_oasis_service, sXML_export_impress_settings_oasis_service }, + { sXML_export_draw_meta_oasis_service, sXML_export_draw_styles_oasis_service, sXML_export_draw_content_oasis_service, sXML_export_draw_settings_oasis_service }, + + { sXML_import_impress_meta_ooo_service, sXML_import_impress_styles_ooo_service, sXML_import_impress_content_ooo_service, sXML_import_impress_settings_ooo_service }, + { sXML_import_draw_meta_ooo_service, sXML_import_draw_styles_ooo_service, sXML_import_draw_content_ooo_service, sXML_import_draw_settings_ooo_service }, + }; + + return &gServices[ (bImport ? 0 : 2) + ((nStoreVer == SOFFICE_FILEFORMAT_60) ? 4 : 0) + (bDraw ? 1 : 0 ) ]; +} + + +SdXMLFilter::SdXMLFilter( SfxMedium& rMedium, ::sd::DrawDocShell& rDocShell, SdXMLFilterMode eFilterMode, sal_uLong nStoreVer ) : + SdFilter( rMedium, rDocShell ), meFilterMode( eFilterMode ), mnStoreVer( nStoreVer ) +{ +} + +SdXMLFilter::~SdXMLFilter() +{ +} + +namespace +{ + +ErrCode ReadThroughComponent( + const Reference& xInputStream, + const Reference& xModelComponent, + const OUString& rStreamName, + Reference const & rxContext, + const char* pFilterName, + const Sequence& rFilterArguments, + const OUString& rName, + bool bMustBeSuccessful, + bool bEncrypted ) +{ + DBG_ASSERT(xInputStream.is(), "input stream missing"); + DBG_ASSERT(xModelComponent.is(), "document missing"); + DBG_ASSERT(rxContext.is(), "factory missing"); + DBG_ASSERT(nullptr != pFilterName,"I need a service name for the component!"); + + SAL_INFO( "sd.filter", "ReadThroughComponent" ); + + // prepare ParserInputSource + xml::sax::InputSource aParserInput; + aParserInput.sSystemId = rName; + aParserInput.aInputStream = xInputStream; + + // get filter + OUString aFilterName(OUString::createFromAscii(pFilterName)); + // the underlying SvXMLImport implements XFastParser, XImporter, XFastDocumentHandler + Reference< XInterface > xFilter( + rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(aFilterName, rFilterArguments, rxContext), + UNO_QUERY ); + SAL_WARN_IF(!xFilter.is(), "sd.filter", "Can't instantiate filter component: " << aFilterName); + if( !xFilter.is() ) + return SD_XML_READERROR; + Reference< xml::sax::XFastParser > xFastParser(xFilter, UNO_QUERY); + Reference< xml::sax::XDocumentHandler > xDocumentHandler; + if (!xFastParser) + xDocumentHandler.set(xFilter, UNO_QUERY); + if (!xFastParser && !xDocumentHandler) + { + SAL_WARN("sd", "service does not implement XFastParser or XDocumentHandler"); + assert(false); + return SD_XML_READERROR; + } + SAL_INFO( "sd.filter", "" << pFilterName << " created" ); + + // connect model and filter + Reference < XImporter > xImporter( xFilter, UNO_QUERY ); + xImporter->setTargetDocument( xModelComponent ); + + // finally, parser the stream + SAL_INFO( "sd.filter", "parsing stream" ); + try + { + if (xFastParser) + xFastParser->parseStream( aParserInput ); + else + { + Reference< xml::sax::XParser > xParser = xml::sax::Parser::create(rxContext); + // connect parser and filter + xParser->setDocumentHandler( xDocumentHandler ); + xParser->parseStream( aParserInput ); + } + } + catch (const xml::sax::SAXParseException& r) + { + css::uno::Any ex( cppu::getCaughtException() ); + // sax parser sends wrapped exceptions, + // try to find the original one + xml::sax::SAXException aSaxEx = *static_cast(&r); + bool bTryChild = true; + + while( bTryChild ) + { + xml::sax::SAXException aTmp; + if ( aSaxEx.WrappedException >>= aTmp ) + aSaxEx = aTmp; + else + bTryChild = false; + } + + packages::zip::ZipIOException aBrokenPackage; + if ( aSaxEx.WrappedException >>= aBrokenPackage ) + return ERRCODE_IO_BROKENPACKAGE; + + if( bEncrypted ) + return ERRCODE_SFX_WRONGPASSWORD; + + SAL_WARN( "sd.filter", "SAX parse exception caught while importing: " << exceptionToString(ex)); + + OUString sErr = OUString::number( r.LineNumber ) + + "," + OUString::number( r.ColumnNumber ); + + if (!rStreamName.isEmpty()) + { + return *new TwoStringErrorInfo( + (bMustBeSuccessful ? ERR_FORMAT_FILE_ROWCOL + : WARN_FORMAT_FILE_ROWCOL), + rStreamName, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + else + { + DBG_ASSERT( bMustBeSuccessful, "Warnings are not supported" ); + return *new StringErrorInfo( ERR_FORMAT_ROWCOL, sErr, + DialogMask::ButtonsOk | DialogMask::MessageError ); + } + } + catch (const xml::sax::SAXException& r) + { + css::uno::Any ex( cppu::getCaughtException() ); + packages::zip::ZipIOException aBrokenPackage; + if ( r.WrappedException >>= aBrokenPackage ) + return ERRCODE_IO_BROKENPACKAGE; + + if( bEncrypted ) + return ERRCODE_SFX_WRONGPASSWORD; + + SAL_WARN( "sd.filter", "SAX exception caught while importing: " << exceptionToString(ex)); + return SD_XML_READERROR; + } + catch (const packages::zip::ZipIOException&) + { + TOOLS_WARN_EXCEPTION( "sd.filter", "Zip exception caught while importing"); + return ERRCODE_IO_BROKENPACKAGE; + } + catch (const io::IOException&) + { + TOOLS_WARN_EXCEPTION( "sd.filter", "IO exception caught while importing"); + return SD_XML_READERROR; + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd.filter", "uno exception caught while importing"); + return SD_XML_READERROR; + } + + // success! + return ERRCODE_NONE; +} + +ErrCode ReadThroughComponent( + const uno::Reference < embed::XStorage >& xStorage, + const Reference& xModelComponent, + const char* pStreamName, + Reference const & rxContext, + const char* pFilterName, + const Sequence& rFilterArguments, + const OUString& rName, + bool bMustBeSuccessful ) +{ + DBG_ASSERT(xStorage.is(), "Need storage!"); + DBG_ASSERT(nullptr != pStreamName, "Please, please, give me a name!"); + + // open stream (and set parser input) + OUString sStreamName = OUString::createFromAscii(pStreamName); + bool bContainsStream = false; + try + { + bContainsStream = xStorage->isStreamElement(sStreamName); + } + catch (const container::NoSuchElementException&) + { + } + + if (!bContainsStream ) + { + // stream name not found! return immediately with OK signal + return ERRCODE_NONE; + } + + // set Base URL + uno::Reference< beans::XPropertySet > xInfoSet; + if( rFilterArguments.hasElements() ) + rFilterArguments.getConstArray()[0] >>= xInfoSet; + DBG_ASSERT( xInfoSet.is(), "missing property set" ); + if( xInfoSet.is() ) + { + xInfoSet->setPropertyValue( "StreamName", Any( sStreamName ) ); + } + + try + { + // get input stream + Reference xStream = + xStorage->openStreamElement( sStreamName, embed::ElementModes::READ ); + Reference xProps( xStream, uno::UNO_QUERY ); + if ( !xStream.is() || ! xProps.is() ) + return SD_XML_READERROR; + + Any aAny = xProps->getPropertyValue( "Encrypted" ); + + bool bEncrypted = false; + aAny >>= bEncrypted; + + Reference xInputStream = xStream->getInputStream(); + + // read from the stream + return ReadThroughComponent( + xInputStream, xModelComponent, sStreamName, rxContext, + pFilterName, rFilterArguments, + rName, bMustBeSuccessful, bEncrypted ); + } + catch (const packages::WrongPasswordException&) + { + return ERRCODE_SFX_WRONGPASSWORD; + } + catch (const packages::zip::ZipIOException&) + { + return ERRCODE_IO_BROKENPACKAGE; + } + catch (const uno::Exception&) + {} + + return SD_XML_READERROR; +} + +} + +//PresObjKind::Outlines in master pages are the preview of the outline styles +//numbering format. Since fdo#78151 toggling bullets on and off changes +//the style they are a preview of, previously toggling bullets on and off +//would only affect the preview paragraph itself without an effect on the +//style. i.e. previews of numbering which don't match the real numbering +//they are supposed to be a preview of. +// +//But there exist documents which were saved previous to that modification +//so here we detect such cases and fix them up to ensure the previews +//numbering level matches that of the outline level it previews +static void fixupOutlinePlaceholderNumberingDepths(SdDrawDocument* pDoc) +{ + for (sal_uInt16 i = 0; i < pDoc->GetMasterSdPageCount(PageKind::Standard); ++i) + { + SdPage *pMasterPage = pDoc->GetMasterSdPage(i, PageKind::Standard); + SdrObject* pMasterOutline = pMasterPage->GetPresObj(PresObjKind::Outline); + if (!pMasterOutline) + continue; + OutlinerParaObject* pOutlParaObj = pMasterOutline->GetOutlinerParaObject(); + if (!pOutlParaObj) + continue; + SdOutliner* pOutliner = pDoc->GetInternalOutliner(); + pOutliner->Clear(); + pOutliner->SetText(*pOutlParaObj); + bool bInconsistent = false; + const sal_Int32 nParaCount = pOutliner->GetParagraphCount(); + for (sal_Int32 j = 0; j < nParaCount; ++j) + { + //Make sure the depth of the paragraph matches that of the outline style it previews + const sal_Int16 nExpectedDepth = j; + if (nExpectedDepth != pOutliner->GetDepth(j)) + { + Paragraph* p = pOutliner->GetParagraph(j); + pOutliner->SetDepth(p, nExpectedDepth); + bInconsistent = true; + } + + //If the preview has hard-coded bullets/numbering then they must + //be stripped to reveal the true underlying styles attributes + SfxItemSet aAttrs(pOutliner->GetParaAttribs(j)); + if (aAttrs.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET) + { + aAttrs.ClearItem(EE_PARA_NUMBULLET); + pOutliner->SetParaAttribs(j, aAttrs); + bInconsistent = true; + } + + } + if (bInconsistent) + { + SAL_WARN("sd.filter", "Fixing inconsistent outline numbering placeholder preview"); + pMasterOutline->SetOutlinerParaObject(pOutliner->CreateParaObject(0, nParaCount)); + } + pOutliner->Clear(); + } +} + +bool SdXMLFilter::Import( ErrCode& nError ) +{ + ErrCode nRet = ERRCODE_NONE; + + // Get service factory + Reference< uno::XComponentContext > rxContext = + comphelper::getProcessComponentContext(); + + SdDrawDocument* pDoc = mrDocShell.GetDoc(); + bool const bWasUndo(pDoc->IsUndoEnabled()); + pDoc->EnableUndo(false); + pDoc->NewOrLoadCompleted( DocCreationMode::New ); + pDoc->CreateFirstPages(); + pDoc->StopWorkStartupDelay(); + + mxModel->lockControllers(); + + /** property map for import info set */ + static PropertyMapEntry const aImportInfoMap[] = + { + // necessary properties for XML progress bar at load time + { OUString("ProgressRange"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressMax"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressCurrent"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("Preview"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("PageLayouts"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("PrivateData"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("BaseURI"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamRelPath"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("BuildId"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("OrganizerMode"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("SourceStorage"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + }; + + uno::Reference< beans::XPropertySet > xInfoSet( GenericPropertySet_CreateInstance( new PropertySetInfo( aImportInfoMap ) ) ); + xInfoSet->setPropertyValue( "Preview" , uno::Any( mrDocShell.GetDoc()->IsStarDrawPreviewMode() ) ); + + // ---- get BuildId from parent container if available + + uno::Reference< container::XChild > xChild( mxModel, uno::UNO_QUERY ); + if( xChild.is() ) + { + uno::Reference< beans::XPropertySet > xParentSet( xChild->getParent(), uno::UNO_QUERY ); + if( xParentSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropSetInfo( xParentSet->getPropertySetInfo() ); + OUString sPropName( "BuildId" ); + if( xPropSetInfo.is() && xPropSetInfo->hasPropertyByName(sPropName) ) + { + xInfoSet->setPropertyValue( sPropName, xParentSet->getPropertyValue(sPropName) ); + } + } + } + + uno::Reference xGraphicStorageHandler; + rtl::Reference xGraphicHelper; + Reference< document::XEmbeddedObjectResolver > xObjectResolver; + rtl::Reference xObjectHelper; + + Reference< lang::XComponent > xModelComp = mxModel; + + // try to get an XStatusIndicator from the Medium + { + SfxItemSet* pSet = mrMedium.GetItemSet(); + if(pSet) + { + const SfxUnoAnyItem* pItem = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL); + if (pItem) + { + pItem->GetValue() >>= mxStatusIndicator; + } + } + + if(mxStatusIndicator.is()) + { + sal_Int32 nProgressRange(1000000); + OUString aMsg(SvxResId(RID_SVXSTR_DOC_LOAD)); + mxStatusIndicator->start(aMsg, nProgressRange); + + // set ProgressRange + uno::Any aProgRange; + aProgRange <<= nProgressRange; + xInfoSet->setPropertyValue( "ProgressRange" , aProgRange); + + // set ProgressCurrent + uno::Any aProgCurrent; + aProgCurrent <<= sal_Int32(0); + xInfoSet->setPropertyValue( "ProgressCurrent" , aProgCurrent); + } + } + + // get the input stream (storage or stream) + + uno::Reference < embed::XStorage > xStorage = mrMedium.GetStorage(); + + xInfoSet->setPropertyValue( "SourceStorage", Any( xStorage ) ); + + if( !xStorage.is() ) + nRet = SD_XML_READERROR; + + if( ERRCODE_NONE == nRet ) + { + xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, + SvXMLGraphicHelperMode::Read ); + xGraphicStorageHandler = xGraphicHelper.get(); + xObjectHelper = SvXMLEmbeddedObjectHelper::Create( + xStorage, *pDoc->GetPersist(), + SvXMLEmbeddedObjectHelperMode::Read ); + xObjectResolver = xObjectHelper.get(); + } + + // Set base URI + OUString const baseURI(mrMedium.GetBaseURL()); + // needed for relative URLs, but in clipboard copy/paste there may be none + SAL_INFO_IF(baseURI.isEmpty(), "sd.filter", "SdXMLFilter: no base URL"); + xInfoSet->setPropertyValue("BaseURI", Any(baseURI)); + + if( ERRCODE_NONE == nRet && SfxObjectCreateMode::EMBEDDED == mrDocShell.GetCreateMode() ) + { + OUString aName; + if ( mrMedium.GetItemSet() ) + { + const SfxStringItem* pDocHierarchItem = + mrMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME); + if ( pDocHierarchItem ) + aName = pDocHierarchItem->GetValue(); + } + else + aName = "dummyObjectName" ; + + if( !aName.isEmpty() ) + xInfoSet->setPropertyValue( "StreamRelPath", Any( aName ) ); + } + + if (SdXMLFilterMode::Organizer == meFilterMode) + xInfoSet->setPropertyValue("OrganizerMode", uno::Any(true)); + + if( ERRCODE_NONE == nRet ) + { + + // prepare filter arguments + Sequence aFilterArgs( 4 ); + Any *pArgs = aFilterArgs.getArray(); + *pArgs++ <<= xInfoSet; + *pArgs++ <<= xGraphicStorageHandler; + *pArgs++ <<= xObjectResolver; + *pArgs++ <<= mxStatusIndicator; + + Sequence aEmptyArgs( 2 ); + pArgs = aEmptyArgs.getArray(); + *pArgs++ <<= xInfoSet; + *pArgs++ <<= mxStatusIndicator; + + const OUString aName( mrMedium.GetName() ); + + XML_SERVICES const * pServices = getServices( true, IsDraw(), mnStoreVer ); + + ErrCode nWarn = ERRCODE_NONE; + ErrCode nWarn2 = ERRCODE_NONE; + // read storage streams + // #i103539#: always read meta.xml for generator + nWarn = ReadThroughComponent( + xStorage, xModelComp, "meta.xml", rxContext, + pServices->mpMeta, + aEmptyArgs, aName, false ); + + if( meFilterMode != SdXMLFilterMode::Organizer ) + { + nWarn2 = ReadThroughComponent( + xStorage, xModelComp, "settings.xml", rxContext, + pServices->mpSettings, + aFilterArgs, aName, false ); + } + + nRet = ReadThroughComponent( + xStorage, xModelComp, "styles.xml", rxContext, + pServices->mpStyles, + aFilterArgs, aName, true ); + + if( !nRet && (meFilterMode != SdXMLFilterMode::Organizer) ) + nRet = ReadThroughComponent( + xStorage, xModelComp, "content.xml", rxContext, + pServices->mpContent, + aFilterArgs, aName, true ); + + if( !nRet ) + { + if( nWarn ) + nRet = nWarn; + else if( nWarn2 ) + nRet = nWarn2; + } + } + + if( xGraphicHelper ) + xGraphicHelper->dispose(); + xGraphicHelper.clear(); + xGraphicStorageHandler = nullptr; + if( xObjectHelper.is() ) + xObjectHelper->dispose(); + xObjectHelper.clear(); + xObjectResolver = nullptr; + + if( mxStatusIndicator.is() ) + mxStatusIndicator->end(); + + if( mxModel.is() ) + mxModel->unlockControllers(); + + if( nRet == ERRCODE_NONE ) + pDoc->UpdateAllLinks(); + + if( nRet.anyOf( ERRCODE_NONE, SD_XML_READERROR ) ) + ; + else if( nRet == ERRCODE_IO_BROKENPACKAGE && xStorage.is() ) + nError = ERRCODE_IO_BROKENPACKAGE; + else + { + // TODO/LATER: this is completely wrong! Filter code should never call ErrorHandler directly! + ErrorHandler::HandleError( nRet ); + if( nRet.IsWarning() ) + nRet = ERRCODE_NONE; + } + + // clear unused named items from item pool + + ::svx::DropUnusedNamedItems(mxModel); + + // set BuildId on XModel for later OLE object loading + if( xInfoSet.is() ) + { + uno::Reference< beans::XPropertySet > xModelSet( mxModel, uno::UNO_QUERY ); + if( xModelSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xModelSetInfo( xModelSet->getPropertySetInfo() ); + static const OUStringLiteral sPropName( u"BuildId" ); + + OUString sBuildId; + xInfoSet->getPropertyValue(sPropName) >>= sBuildId; + + if( xModelSetInfo.is() && xModelSetInfo->hasPropertyByName(sPropName) ) + { + xModelSet->setPropertyValue( sPropName, Any( sBuildId ) ); + } + + bool bTransform = false; + + if( nRet == ERRCODE_NONE ) + { + if( !sBuildId.isEmpty() ) + { + sal_Int32 nIndex = sBuildId.indexOf('$'); + if( nIndex != -1 ) + { + sal_Int32 nUPD = o3tl::toInt32(sBuildId.subView( 0, nIndex )); + + if( nUPD == 300 ) + { + sal_Int32 nBuildId = o3tl::toInt32(sBuildId.subView( nIndex+1 )); + if( (nBuildId > 0) && (nBuildId < 9316) ) + bTransform = true; // treat OOo 3.0 beta1 as OOo 2.x + } + else if( (nUPD == 680) || ( nUPD >= 640 && nUPD <= 645 ) ) + bTransform = true; + } + } + else + { + // check for binary formats + std::shared_ptr pFilter = mrMedium.GetFilter(); + if( pFilter ) + { + OUString typeName(pFilter->GetRealTypeName()); + if( typeName.startsWith( "impress_StarImpress" ) || + typeName.startsWith( "draw_StarDraw" ) ) + { + bTransform = true; + } + } + } + } + + if( bTransform ) + TransformOOo2xDocument( pDoc ); + } + } + + fixupOutlinePlaceholderNumberingDepths(pDoc); + + pDoc->EnableUndo(bWasUndo); + mrDocShell.ClearUndoBuffer(); + return nRet == ERRCODE_NONE; +} + +bool SdXMLFilter::Export() +{ + rtl::Reference xObjectHelper; + rtl::Reference xGraphicHelper; + bool bDocRet = false; + + if( !mxModel.is() ) + { + SAL_WARN( "sd.filter","Got NO Model in XMLExport"); + return false; + } + + bool bLocked = mxModel->hasControllersLocked(); + + try + { + mxModel->lockControllers(); + + uno::Reference< lang::XServiceInfo > xServiceInfo( mxModel, uno::UNO_QUERY ); + + if( !xServiceInfo.is() || !xServiceInfo->supportsService( "com.sun.star.drawing.GenericDrawingDocument" ) ) + { + SAL_WARN( "sd.filter", "Model is no DrawingDocument in XMLExport" ); + return false; + } + + uno::Reference xContext( ::comphelper::getProcessComponentContext() ); + + uno::Reference< xml::sax::XWriter > xWriter = xml::sax::Writer::create( xContext ); + + /** property map for export info set */ + static PropertyMapEntry const aExportInfoMap[] = + { + { OUString("ProgressRange"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressMax"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("ProgressCurrent"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("UsePrettyPrinting"),0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("PageLayoutNames"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0}, + { OUString("BaseURI"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamRelPath"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StreamName"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleNames"), 0, cppu::UnoType>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("StyleFamilies"), 0, cppu::UnoType>::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + { OUString("TargetStorage"), 0, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0 }, + }; + + uno::Reference< beans::XPropertySet > xInfoSet( GenericPropertySet_CreateInstance( new PropertySetInfo( aExportInfoMap ) ) ); + + bool bUsePrettyPrinting = officecfg::Office::Common::Save::Document::PrettyPrinting::get(); + xInfoSet->setPropertyValue( "UsePrettyPrinting", Any( bUsePrettyPrinting ) ); + + const uno::Reference < embed::XStorage >& xStorage = mrMedium.GetOutputStorage(); + + // Set base URI + OUString sPropName( "BaseURI" ); + xInfoSet->setPropertyValue( sPropName, Any( mrMedium.GetBaseURL( true ) ) ); + + xInfoSet->setPropertyValue( "TargetStorage", Any( xStorage ) ); + + if( SfxObjectCreateMode::EMBEDDED == mrDocShell.GetCreateMode() ) + { + OUString aName; + if ( mrMedium.GetItemSet() ) + { + const SfxStringItem* pDocHierarchItem = + mrMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME); + if ( pDocHierarchItem ) + aName = pDocHierarchItem->GetValue(); + } + + if( !aName.isEmpty() ) + { + sPropName = "StreamRelPath"; + xInfoSet->setPropertyValue( sPropName, Any( aName ) ); + } + } + + // initialize descriptor + uno::Sequence< beans::PropertyValue > aDescriptor( 1 ); + beans::PropertyValue* pProps = aDescriptor.getArray(); + + pProps[0].Name = "FileName"; + pProps[0].Value <<= mrMedium.GetName(); + + { + uno::Reference< document::XEmbeddedObjectResolver > xObjectResolver; + uno::Reference xGraphicStorageHandler; + + // create helper for graphic and ole export if we have a storage + if( xStorage.is() ) + { + xObjectHelper = SvXMLEmbeddedObjectHelper::Create( xStorage, *mrDocShell.GetDoc()->GetPersist(), SvXMLEmbeddedObjectHelperMode::Write ); + xObjectResolver = xObjectHelper.get(); + + xGraphicHelper = SvXMLGraphicHelper::Create( xStorage, SvXMLGraphicHelperMode::Write ); + xGraphicStorageHandler = xGraphicHelper.get(); + } + + CreateStatusIndicator(); + if(mxStatusIndicator.is()) + { + sal_Int32 nProgressRange(1000000); + OUString aMsg(SdResId(STR_SAVE_DOC)); + mxStatusIndicator->start(aMsg, nProgressRange); + + // set ProgressRange + uno::Any aProgRange; + aProgRange <<= nProgressRange; + xInfoSet->setPropertyValue( "ProgressRange" , aProgRange); + + // set ProgressCurrent + uno::Any aProgCurrent; + aProgCurrent <<= sal_Int32(0); + xInfoSet->setPropertyValue( "ProgressCurrent" , aProgCurrent); + } + + XML_SERVICES const * pServiceNames = getServices( false, IsDraw(), mnStoreVer ); + + XML_SERVICEMAP aServices[5]; sal_uInt16 i = 0; + aServices[i ].mpService = pServiceNames->mpStyles; + aServices[i++].mpStream = "styles.xml"; + + aServices[i ].mpService = pServiceNames->mpContent; + aServices[i++].mpStream = "content.xml"; + + aServices[i ].mpService = pServiceNames->mpSettings; + aServices[i++].mpStream = "settings.xml"; + + if( mrDocShell.GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + aServices[i ].mpService = pServiceNames->mpMeta; + aServices[i++].mpStream = "meta.xml"; + }; + + aServices[i].mpService = nullptr; + aServices[i].mpStream = nullptr; + + XML_SERVICEMAP* pServices = aServices; + + // doc export + do + { + SAL_INFO( "sd.filter", "exporting substream " << pServices->mpStream ); + + uno::Reference xDocOut; + if( xStorage.is() ) + { + const OUString sDocName( OUString::createFromAscii( pServices->mpStream ) ); + uno::Reference xStream = + xStorage->openStreamElement( sDocName, + embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE ); + + DBG_ASSERT(xStream.is(), "Can't create output stream in package!"); + if( !xStream.is() ) + return false; + + xDocOut = xStream->getOutputStream(); + Reference xProps( xStream, uno::UNO_QUERY ); + if( !xDocOut.is() || !xProps.is() ) + return false; + + xProps->setPropertyValue( "MediaType", Any(OUString( "text/xml"))); + + // encrypt all streams + xProps->setPropertyValue( "UseCommonStoragePasswordEncryption", + uno::Any( true ) ); + + xInfoSet->setPropertyValue( "StreamName", Any( sDocName ) ); + } + + xWriter->setOutputStream( xDocOut ); + + uno::Sequence< uno::Any > aArgs( 2 + ( mxStatusIndicator.is() ? 1 : 0 ) + ( xGraphicStorageHandler.is() ? 1 : 0 ) + ( xObjectResolver.is() ? 1 : 0 ) ); + uno::Any* pArgs = aArgs.getArray(); + *pArgs++ <<= xInfoSet; + if (xGraphicStorageHandler.is()) + *pArgs++ <<= xGraphicStorageHandler; + if (xObjectResolver.is()) + *pArgs++ <<= xObjectResolver; + if (mxStatusIndicator.is()) + *pArgs++ <<= mxStatusIndicator; + + *pArgs <<= xWriter; + + uno::Reference< document::XFilter > xFilter( xContext->getServiceManager()->createInstanceWithArgumentsAndContext( OUString::createFromAscii( pServices->mpService ), aArgs, xContext ), uno::UNO_QUERY ); + if( xFilter.is() ) + { + uno::Reference< document::XExporter > xExporter( xFilter, uno::UNO_QUERY ); + if( xExporter.is() ) + { + xExporter->setSourceDocument( mxModel ); + // outputstream will be closed by SAX parser + bDocRet = xFilter->filter( aDescriptor ); + } + } + + pServices++; + } + while( bDocRet && pServices->mpService ); + + if(mxStatusIndicator.is()) + mxStatusIndicator->end(); + } + } + catch (const uno::Exception &) + { + TOOLS_WARN_EXCEPTION( "sd.filter", "uno Exception caught while exporting"); + bDocRet = false; + } + if ( !bLocked ) + mxModel->unlockControllers(); + + if( xGraphicHelper ) + xGraphicHelper->dispose(); + xGraphicHelper.clear(); + + if( xObjectHelper ) + xObjectHelper->dispose(); + xObjectHelper.clear(); + + return bDocRet; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportFODP(SvStream &rStream) +{ + SdDLL::Init(); + + sd::DrawDocShellRef xDocSh(new sd::DrawDocShell(SfxObjectCreateMode::EMBEDDED, false, DocumentType::Impress)); + xDocSh->DoInitNew(); + uno::Reference xModel(xDocSh->GetModel()); + + uno::Reference xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference xStream(new ::utl::OSeekableInputStreamWrapper(rStream)); + uno::Reference xInterface(xMultiServiceFactory->createInstance("com.sun.star.comp.Writer.XmlFilterAdaptor"), uno::UNO_SET_THROW); + + css::uno::Sequence aUserData + { + "com.sun.star.comp.filter.OdfFlatXml", + "", + "com.sun.star.comp.Impress.XMLOasisImporter", + "com.sun.star.comp.Impress.XMLOasisExporter", + "", + "", + "true" + }; + uno::Sequence aAdaptorArgs(comphelper::InitPropertySequence( + { + { "UserData", uno::Any(aUserData) }, + })); + css::uno::Sequence aOuterArgs{ uno::Any(aAdaptorArgs) }; + + uno::Reference xInit(xInterface, uno::UNO_QUERY_THROW); + xInit->initialize(aOuterArgs); + + uno::Reference xImporter(xInterface, uno::UNO_QUERY_THROW); + uno::Sequence aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "URL", uno::Any(OUString("private:stream")) }, + })); + xImporter->setTargetDocument(xModel); + + uno::Reference xFilter(xInterface, uno::UNO_QUERY_THROW); + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + xDocSh->SetLoading(SfxLoadedFlags::NONE); + bool ret = xFilter->filter(aArgs); + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return ret; +} + +extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportPPTX(SvStream &rStream) +{ + SdDLL::Init(); + + sd::DrawDocShellRef xDocSh(new sd::DrawDocShell(SfxObjectCreateMode::EMBEDDED, false, DocumentType::Impress)); + xDocSh->DoInitNew(); + uno::Reference xModel(xDocSh->GetModel()); + + uno::Reference xMultiServiceFactory(comphelper::getProcessServiceFactory()); + uno::Reference xStream(new utl::OSeekableInputStreamWrapper(rStream)); + + uno::Reference xFilter(xMultiServiceFactory->createInstance("com.sun.star.comp.oox.ppt.PowerPointImport"), uno::UNO_QUERY_THROW); + + uno::Reference xImporter(xFilter, uno::UNO_QUERY_THROW); + uno::Sequence aArgs(comphelper::InitPropertySequence( + { + { "InputStream", uno::Any(xStream) }, + { "InputMode", uno::Any(true) }, + })); + xImporter->setTargetDocument(xModel); + + //SetLoading hack because the document properties will be re-initted + //by the xml filter and during the init, while it's considered uninitialized, + //setting a property will inform the document it's modified, which attempts + //to update the properties, which throws cause the properties are uninitialized + xDocSh->SetLoading(SfxLoadedFlags::NONE); + bool ret = false; + try + { + ret = xFilter->filter(aArgs); + } + catch (...) + { + } + xDocSh->SetLoading(SfxLoadedFlags::ALL); + + xDocSh->DoClose(); + + return ret; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/helper/simplereferencecomponent.cxx b/sd/source/helper/simplereferencecomponent.cxx new file mode 100644 index 000000000..740c2629b --- /dev/null +++ b/sd/source/helper/simplereferencecomponent.cxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +using com::sun::star::uno::RuntimeException; +using sd::SimpleReferenceComponent; + +SimpleReferenceComponent::SimpleReferenceComponent() + : m_nCount(0) + , mbDisposed(false) +{ +} + +SimpleReferenceComponent::~SimpleReferenceComponent() +{ + OSL_ASSERT(m_nCount == 0); + OSL_ASSERT(mbDisposed); +} + +void SimpleReferenceComponent::acquire() { osl_atomic_increment(&m_nCount); } + +void SimpleReferenceComponent::release() +{ + if ((1 == m_nCount) && !mbDisposed) + { + try + { + Dispose(); + } + catch (RuntimeException const&) // don't break throw () + { + TOOLS_WARN_EXCEPTION("sd", ""); + } + } + + if (osl_atomic_decrement(&m_nCount) == 0) + delete this; +} + +void SimpleReferenceComponent::Dispose() +{ + if (!mbDisposed) + { + mbDisposed = true; + disposing(); + } +} + +void SimpleReferenceComponent::disposing() {} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx b/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx new file mode 100644 index 000000000..c297184fa --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleDocumentViewBase.cxx @@ -0,0 +1,773 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + +namespace accessibility { + +//===== internal ============================================================ +AccessibleDocumentViewBase::AccessibleDocumentViewBase ( + ::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const uno::Reference& rxController, + const uno::Reference& rxParent) + : AccessibleContextBase (rxParent, + pViewShell->GetDoc()->GetDocumentType() == DocumentType::Impress ? + AccessibleRole::DOCUMENT_PRESENTATION : + AccessibleRole::DOCUMENT), + mxController (rxController), + maViewForwarder ( + static_cast(pViewShell->GetView()), + *pSdWindow->GetOutDev()) +{ + if (mxController.is()) + mxModel = mxController->getModel(); + + // Fill the shape tree info. + maShapeTreeInfo.SetModelBroadcaster ( + uno::Reference( + mxModel, uno::UNO_QUERY_THROW)); + maShapeTreeInfo.SetController (mxController); + maShapeTreeInfo.SetSdrView (pViewShell->GetView()); + maShapeTreeInfo.SetWindow (pSdWindow); + maShapeTreeInfo.SetViewForwarder (&maViewForwarder); + + mxWindow = ::VCLUnoHelper::GetInterface (pSdWindow); + mpViewShell = pViewShell; +} + +AccessibleDocumentViewBase::~AccessibleDocumentViewBase() +{ + // At this place we should be disposed. You may want to add a + // corresponding assertion into the destructor of a derived class. +} + +void AccessibleDocumentViewBase::Init() +{ + // Finish the initialization of the shape tree info container. + maShapeTreeInfo.SetDocumentWindow (this); + + // Register as window listener to stay up to date with its size and + // position. + mxWindow->addWindowListener (this); + // Register as focus listener to + mxWindow->addFocusListener (this); + + // Determine the list of shapes on the current page. + uno::Reference xShapeList; + uno::Reference xView (mxController, uno::UNO_QUERY); + if (xView.is()) + xShapeList = xView->getCurrentPage(); + + // Register this object as dispose event listener at the model. + if (mxModel.is()) + mxModel->addEventListener ( + static_cast(this)); + + // Register as property change listener at the controller. + uno::Reference xSet (mxController, uno::UNO_QUERY); + if (xSet.is()) + xSet->addPropertyChangeListener ( + "", + static_cast(this)); + + // Register this object as dispose event listener at the controller. + if (mxController.is()) + mxController->addEventListener ( + static_cast(this)); + + // Register at VCL Window to be informed of activated and deactivated + // OLE objects. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + if (pWindow != nullptr) + { + maWindowLink = LINK( + this, AccessibleDocumentViewBase, WindowChildEventListener); + + pWindow->AddChildEventListener (maWindowLink); + + sal_uInt16 nCount = pWindow->GetChildCount(); + for (sal_uInt16 i=0; iGetChild (i); + if (pChildWindow && + (AccessibleRole::EMBEDDED_OBJECT + ==pChildWindow->GetAccessibleRole())) + { + SetAccessibleOLEObject (pChildWindow->GetAccessible()); + } + } + } + SfxObjectShell* pObjShell = mpViewShell->GetViewFrame()->GetObjectShell(); + if(!pObjShell->IsReadOnly()) + SetState(AccessibleStateType::EDITABLE); +} + +IMPL_LINK(AccessibleDocumentViewBase, WindowChildEventListener, + VclWindowEvent&, rEvent, void) +{ + // DBG_ASSERT( pVclEvent->GetWindow(), "Window???" ); + switch (rEvent.GetId()) + { + case VclEventId::ObjectDying: + { + // Window is dying. Unregister from VCL Window. + // This is also attempted in the disposing() method. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + vcl::Window* pDyingWindow = rEvent.GetWindow(); + if (pWindow==pDyingWindow && pWindow!=nullptr && maWindowLink.IsSet()) + { + pWindow->RemoveChildEventListener (maWindowLink); + maWindowLink = Link(); + } + } + break; + + case VclEventId::WindowShow: + { + // A new window has been created. Is it an OLE object? + vcl::Window* pChildWindow = static_cast( + rEvent.GetData()); + if (pChildWindow!=nullptr + && (pChildWindow->GetAccessibleRole() + == AccessibleRole::EMBEDDED_OBJECT)) + { + SetAccessibleOLEObject (pChildWindow->GetAccessible()); + } + } + break; + + case VclEventId::WindowHide: + { + // A window has been destroyed. Has that been an OLE + // object? + vcl::Window* pChildWindow = static_cast( + rEvent.GetData()); + if (pChildWindow!=nullptr + && (pChildWindow->GetAccessibleRole() + == AccessibleRole::EMBEDDED_OBJECT)) + { + SetAccessibleOLEObject (nullptr); + } + } + break; + + default: break; + } +} + +//===== IAccessibleViewForwarderListener ==================================== + +void AccessibleDocumentViewBase::ViewForwarderChanged() +{ + // Empty +} + +//===== XAccessibleContext ================================================== + +Reference SAL_CALL + AccessibleDocumentViewBase::getAccessibleParent() +{ + ThrowIfDisposed (); + + return AccessibleContextBase::getAccessibleParent(); +} + +sal_Int32 SAL_CALL + AccessibleDocumentViewBase::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + if (mxAccessibleOLEObject.is()) + return 1; + else + return 0; +} + +Reference SAL_CALL + AccessibleDocumentViewBase::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + + ::osl::MutexGuard aGuard (m_aMutex); + if (mxAccessibleOLEObject.is()) + if (nIndex == 0) + return mxAccessibleOLEObject; + + throw lang::IndexOutOfBoundsException ( "no child with index " + OUString::number(nIndex) ); +} + +//===== XAccessibleComponent ================================================ + +/** Iterate over all children and test whether the specified point lies + within one of their bounding boxes. Return the first child for which + this is true. +*/ +uno::Reference SAL_CALL + AccessibleDocumentViewBase::getAccessibleAtPoint ( + const awt::Point& aPoint) +{ + ThrowIfDisposed (); + + ::osl::MutexGuard aGuard (m_aMutex); + uno::Reference xChildAtPosition; + + sal_Int32 nChildCount = getAccessibleChildCount (); + for (sal_Int32 i=nChildCount-1; i>=0; --i) + { + Reference xChild (getAccessibleChild (i)); + if (xChild.is()) + { + Reference xChildComponent ( + xChild->getAccessibleContext(), uno::UNO_QUERY); + if (xChildComponent.is()) + { + awt::Rectangle aBBox (xChildComponent->getBounds()); + if ( (aPoint.X >= aBBox.X) + && (aPoint.Y >= aBBox.Y) + && (aPoint.X < aBBox.X+aBBox.Width) + && (aPoint.Y < aBBox.Y+aBBox.Height) ) + { + xChildAtPosition = xChild; + break; + } + } + } + } + + // Have not found a child under the given point. Returning empty + // reference to indicate this. + return xChildAtPosition; +} + +awt::Rectangle SAL_CALL + AccessibleDocumentViewBase::getBounds() +{ + ThrowIfDisposed (); + + // Transform visible area into screen coordinates. + ::tools::Rectangle aVisibleArea ( + maShapeTreeInfo.GetViewForwarder()->GetVisibleArea()); + ::Point aPixelTopLeft ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.TopLeft())); + ::Point aPixelSize ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.BottomRight()) + - aPixelTopLeft); + + // Prepare to subtract the parent position to transform into relative + // coordinates. + awt::Point aParentPosition; + Reference xParent = getAccessibleParent (); + if (xParent.is()) + { + Reference xParentComponent ( + xParent->getAccessibleContext(), uno::UNO_QUERY); + if (xParentComponent.is()) + aParentPosition = xParentComponent->getLocationOnScreen(); + } + + return awt::Rectangle ( + aPixelTopLeft.X() - aParentPosition.X, + aPixelTopLeft.Y() - aParentPosition.Y, + aPixelSize.X(), + aPixelSize.Y()); +} + +awt::Point SAL_CALL + AccessibleDocumentViewBase::getLocation() +{ + ThrowIfDisposed (); + awt::Rectangle aBoundingBox (getBounds()); + return awt::Point (aBoundingBox.X, aBoundingBox.Y); +} + +awt::Point SAL_CALL + AccessibleDocumentViewBase::getLocationOnScreen() +{ + ThrowIfDisposed (); + ::Point aLogicalPoint (maShapeTreeInfo.GetViewForwarder()->GetVisibleArea().TopLeft()); + ::Point aPixelPoint (maShapeTreeInfo.GetViewForwarder()->LogicToPixel (aLogicalPoint)); + return awt::Point (aPixelPoint.X(), aPixelPoint.Y()); +} + +awt::Size SAL_CALL + AccessibleDocumentViewBase::getSize() +{ + ThrowIfDisposed (); + + // Transform visible area into screen coordinates. + ::tools::Rectangle aVisibleArea ( + maShapeTreeInfo.GetViewForwarder()->GetVisibleArea()); + ::Point aPixelTopLeft ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.TopLeft())); + ::Point aPixelSize ( + maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + aVisibleArea.BottomRight()) + - aPixelTopLeft); + + return awt::Size (aPixelSize.X(), aPixelSize.Y()); +} + +//===== XInterface ========================================================== + +uno::Any SAL_CALL + AccessibleDocumentViewBase::queryInterface (const uno::Type & rType) +{ + uno::Any aReturn = AccessibleContextBase::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast(this), + static_cast(this), + static_cast( + static_cast(this)), + static_cast(this), + static_cast(this), + static_cast(this) + ,static_cast(this) + ); + return aReturn; +} + +void SAL_CALL + AccessibleDocumentViewBase::acquire() + noexcept +{ + AccessibleContextBase::acquire (); +} + +void SAL_CALL + AccessibleDocumentViewBase::release() + noexcept +{ + AccessibleContextBase::release (); +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleDocumentViewBase::getImplementationName() +{ + return "AccessibleDocumentViewBase"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessibleDocumentViewBase::getSupportedServiceNames() +{ + ThrowIfDisposed (); + return AccessibleContextBase::getSupportedServiceNames (); +} + +//===== XTypeProvider ======================================================= + +css::uno::Sequence< css::uno::Type> SAL_CALL + AccessibleDocumentViewBase::getTypes() +{ + ThrowIfDisposed (); + + return comphelper::concatSequences( + // Get list of types from the context base implementation, ... + AccessibleContextBase::getTypes(), + // ... get list of types from component base implementation, ... + AccessibleComponentBase::getTypes(), + // ...and add the additional type for the component, ... + css::uno::Sequence { + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get() }); +} + +void AccessibleDocumentViewBase::impl_dispose() +{ + // Unregister from VCL Window. + vcl::Window* pWindow = maShapeTreeInfo.GetWindow(); + if (maWindowLink.IsSet()) + { + if (pWindow) + pWindow->RemoveChildEventListener (maWindowLink); + maWindowLink = Link(); + } + else + { + DBG_ASSERT (pWindow, "AccessibleDocumentViewBase::disposing"); + } + + // Unregister from window. + if (mxWindow.is()) + { + mxWindow->removeWindowListener (this); + mxWindow->removeFocusListener (this); + mxWindow = nullptr; + } + + // Unregister from the model. + if (mxModel.is()) + mxModel->removeEventListener ( + static_cast(this)); + + // Unregister from the controller. + if (mxController.is()) + { + uno::Reference xSet (mxController, uno::UNO_QUERY); + if (xSet.is()) + xSet->removePropertyChangeListener ("", static_cast(this)); + + mxController->removeEventListener ( + static_cast(this)); + } + + // Propagate change of controller down the shape tree. + maShapeTreeInfo.SetModelBroadcaster (nullptr); + + // Reset the model reference. + mxModel = nullptr; + // Reset the model reference. + mxController = nullptr; + + maShapeTreeInfo.SetDocumentWindow (nullptr); +} + +//===== XEventListener ====================================================== + +void SAL_CALL + AccessibleDocumentViewBase::disposing (const lang::EventObject& rEventObject) +{ + ThrowIfDisposed (); + + // Register this object as dispose event and document::XEventListener + // listener at the model. + + if ( ! rEventObject.Source.is()) + { + // Paranoia. Can this really happen? + } + else if (rEventObject.Source == mxModel || rEventObject.Source == mxController) + { + impl_dispose(); + } +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL AccessibleDocumentViewBase::propertyChange (const beans::PropertyChangeEvent& ) +{ + // Empty +} + +//===== XWindowListener ===================================================== + +void SAL_CALL + AccessibleDocumentViewBase::windowResized (const css::awt::WindowEvent& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowMoved (const css::awt::WindowEvent& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowShown (const css::lang::EventObject& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +void SAL_CALL + AccessibleDocumentViewBase::windowHidden (const css::lang::EventObject& ) +{ + if( IsDisposed() ) + return; + + ViewForwarderChanged(); +} + +//===== XFocusListener ================================================== + +void AccessibleDocumentViewBase::focusGained (const css::awt::FocusEvent& e) +{ + ThrowIfDisposed (); + if (e.Source == mxWindow) + Activated (); +} + +void AccessibleDocumentViewBase::focusLost (const css::awt::FocusEvent& e) +{ + ThrowIfDisposed (); + if (e.Source == mxWindow) + Deactivated (); +} + +//===== protected internal ================================================== + +// This method is called from the component helper base class while disposing. +void SAL_CALL AccessibleDocumentViewBase::disposing() +{ + impl_dispose(); + + AccessibleContextBase::disposing (); +} + +/// Create a name for this view. +OUString + AccessibleDocumentViewBase::CreateAccessibleName() +{ + return "AccessibleDocumentViewBase"; +} + +void AccessibleDocumentViewBase::Activated() +{ + // Empty. Overwrite to do something useful. +} + +void AccessibleDocumentViewBase::Deactivated() +{ + // Empty. Overwrite to do something useful. +} + +void AccessibleDocumentViewBase::SetAccessibleOLEObject ( + const Reference & xOLEObject) +{ + // Send child event about removed accessible OLE object if necessary. + if (mxAccessibleOLEObject != xOLEObject) + if (mxAccessibleOLEObject.is()) + CommitChange ( + AccessibleEventId::CHILD, + uno::Any(), + uno::Any (mxAccessibleOLEObject)); + + // Assume that the accessible OLE Object disposes itself correctly. + + { + ::osl::MutexGuard aGuard (m_aMutex); + mxAccessibleOLEObject = xOLEObject; + } + + // Send child event about new accessible OLE object if necessary. + if (mxAccessibleOLEObject.is()) + CommitChange ( + AccessibleEventId::CHILD, + uno::Any (mxAccessibleOLEObject), + uno::Any()); +} + +//===== methods from AccessibleSelectionBase ================================================== + +// return the member maMutex; +::osl::Mutex& + AccessibleDocumentViewBase::implGetMutex() +{ + return m_aMutex; +} + +// return ourself as context in default case +uno::Reference< XAccessibleContext > + AccessibleDocumentViewBase::implGetAccessibleContext() +{ + return this; +} + +// return sal_False in default case +bool + AccessibleDocumentViewBase::implIsSelected( sal_Int32 ) +{ + return false; +} + +// return nothing in default case +void + AccessibleDocumentViewBase::implSelect( sal_Int32, bool ) +{ +} + +uno::Any SAL_CALL AccessibleDocumentViewBase::getExtendedAttributes() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + uno::Any anyAttribute; + OUStringBuffer sValue; + if (auto pDrViewSh = dynamic_cast<::sd::DrawViewShell* > (mpViewShell)) + { + OUString sDisplay; + OUString sName = "page-name:"; + // MT IA2: Not used... + // SdPage* pCurrPge = pDrViewSh->getCurrentPage(); + SdDrawDocument* pDoc = pDrViewSh->GetDoc(); + sDisplay = pDrViewSh->getCurrentPage()->GetName(); + sDisplay = sDisplay.replaceFirst( "\\", "\\\\" ); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue = sName + sDisplay ; + sValue.append(";page-number:"); + sValue.append(static_cast(static_cast((pDrViewSh->getCurrentPage()->GetPageNum()-1)>>1) + 1)); + sValue.append(";total-pages:"); + sValue.append(static_cast(pDrViewSh->GetPageTabControl().GetPageCount())); + sValue.append(";"); + if(pDrViewSh->IsLayerModeActive() && pDrViewSh->GetLayerTabControl()) // #i87182# + { + sName = "page-name:"; + sValue = sName; + OUString sLayerName(pDrViewSh->GetLayerTabControl()->GetLayerName(pDrViewSh->GetLayerTabControl()->GetCurPageId()) ); + sDisplay = pDrViewSh->GetLayerTabControl()->GetPageText(pDrViewSh->GetLayerTabControl()->GetCurPageId()); + if( pDoc ) + { + SdrLayerAdmin& rLayerAdmin = pDoc->GetLayerAdmin(); + SdrLayer* aSdrLayer = rLayerAdmin.GetLayer(sLayerName); + if( aSdrLayer ) + { + const OUString& layerAltText = aSdrLayer->GetTitle(); + if (!layerAltText.isEmpty()) + { + sName = " "; + sDisplay += sName + layerAltText; + } + } + } + sDisplay = sDisplay.replaceFirst( "\\", "\\\\" ); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue.append(sDisplay); + sValue.append(";page-number:"); + sValue.append(static_cast(pDrViewSh->GetActiveTabLayerIndex()+1)); + sValue.append(";total-pages:"); + sValue.append(static_cast(pDrViewSh->GetLayerTabControl()->GetPageCount())); + sValue.append(";"); + } + } + if (auto pPresViewSh = dynamic_cast<::sd::PresentationViewShell* >(mpViewShell)) + { + SdPage* pCurrPge = pPresViewSh->getCurrentPage(); + SdDrawDocument* pDoc = pPresViewSh->GetDoc(); + SdPage* pNotesPge = pDoc->GetSdPage((pCurrPge->GetPageNum()-1)>>1, PageKind::Notes); + if (pNotesPge) + { + SdrObject* pNotesObj = pNotesPge->GetPresObj(PresObjKind::Notes); + if (pNotesObj) + { + OutlinerParaObject* pPara = pNotesObj->GetOutlinerParaObject(); + if (pPara) + { + sValue.append("note:"); + const EditTextObject& rEdit = pPara->GetTextObject(); + for (sal_Int32 i=0;i(mpViewShell ) != nullptr ) + { + SdPage* pCurrPge = mpViewShell->GetActualPage(); + SdDrawDocument* pDoc = mpViewShell->GetDoc(); + if(pCurrPge && pDoc) + { + OUString sDisplay; + sDisplay = pCurrPge->GetName(); + sDisplay = sDisplay.replaceFirst( "=", "\\=" ); + sDisplay = sDisplay.replaceFirst( ";", "\\;" ); + sDisplay = sDisplay.replaceFirst( ",", "\\," ); + sDisplay = sDisplay.replaceFirst( ":", "\\:" ); + sValue = "page-name:" + sDisplay; + sValue.append(";page-number:"); + sValue.append(static_cast(static_cast((pCurrPge->GetPageNum()-1)>>1) + 1)); + sValue.append(";total-pages:"); + sValue.append(static_cast(pDoc->GetSdPageCount(PageKind::Standard))); + sValue.append(";"); + } + } + if (sValue.getLength()) + anyAttribute <<= sValue.makeStringAndClear(); + return anyAttribute; +} + +sal_Int32 SAL_CALL AccessibleDocumentViewBase::getForeground( ) +{ + return sal_Int32(COL_BLACK); +} + +sal_Int32 SAL_CALL AccessibleDocumentViewBase::getBackground( ) +{ + ThrowIfDisposed (); + ::osl::MutexGuard aGuard (m_aMutex); + return sal_Int32(mpViewShell->GetView()->getColorConfig().GetColorValue( ::svtools::DOCCOLOR ).nColor); +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx b/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx new file mode 100644 index 000000000..f6111962a --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleDrawDocumentView.cxx @@ -0,0 +1,777 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +namespace { + +struct XShapePosCompareHelper +{ + bool operator() ( const uno::Reference& xshape1, + const uno::Reference& xshape2 ) const + { + // modify the compare method to return the Z-Order, not layout order + SdrObject* pObj1 = SdrObject::getSdrObjectFromXShape(xshape1); + SdrObject* pObj2 = SdrObject::getSdrObjectFromXShape(xshape2); + if(pObj1 && pObj2) + return pObj1->GetOrdNum() < pObj2->GetOrdNum(); + else + return false; + } +}; + +} + +//===== internal ============================================================ + +AccessibleDrawDocumentView::AccessibleDrawDocumentView ( + ::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const uno::Reference& rxController, + const uno::Reference& rxParent) + : AccessibleDocumentViewBase (pSdWindow, pViewShell, rxController, rxParent), + mpSdViewSh( pViewShell ) +{ + UpdateAccessibleName(); +} + +AccessibleDrawDocumentView::~AccessibleDrawDocumentView() +{ + DBG_ASSERT (rBHelper.bDisposed || rBHelper.bInDispose, + "~AccessibleDrawDocumentView: object has not been disposed"); +} + +void AccessibleDrawDocumentView::Init() +{ + AccessibleDocumentViewBase::Init (); + + // Determine the list of shapes on the current page. + uno::Reference xShapeList; + uno::Reference xView (mxController, uno::UNO_QUERY); + if (xView.is()) + xShapeList = xView->getCurrentPage(); + + // Create the children manager. + mpChildrenManager.reset(new ChildrenManager(this, xShapeList, maShapeTreeInfo, *this)); + + rtl::Reference xPage(CreateDrawPageShape()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (); + } + + mpChildrenManager->UpdateSelection (); +} + +void AccessibleDrawDocumentView::ViewForwarderChanged() +{ + AccessibleDocumentViewBase::ViewForwarderChanged(); + if (mpChildrenManager != nullptr) + mpChildrenManager->ViewForwarderChanged(); +} + +/** The page shape is created on every call at the moment (provided that + everything goes well). +*/ +rtl::Reference AccessibleDrawDocumentView::CreateDrawPageShape() +{ + rtl::Reference xShape; + + // Create a shape that represents the actual draw page. + uno::Reference xView (mxController, uno::UNO_QUERY); + if (xView.is()) + { + uno::Reference xSet ( + uno::Reference (xView->getCurrentPage(), uno::UNO_QUERY)); + if (xSet.is()) + { + // Create a rectangle shape that will represent the draw page. + uno::Reference xFactory (mxModel, uno::UNO_QUERY); + uno::Reference xRectangle; + if (xFactory.is()) + xRectangle.set(xFactory->createInstance ("com.sun.star.drawing.RectangleShape"), + uno::UNO_QUERY); + + // Set the shape's size and position. + if (xRectangle.is()) + { + uno::Any aValue; + awt::Point aPosition; + awt::Size aSize; + + // Set size and position of the shape to those of the draw + // page. + aValue = xSet->getPropertyValue ("BorderLeft"); + aValue >>= aPosition.X; + aValue = xSet->getPropertyValue ("BorderTop"); + aValue >>= aPosition.Y; + xRectangle->setPosition (aPosition); + + aValue = xSet->getPropertyValue ("Width"); + aValue >>= aSize.Width; + aValue = xSet->getPropertyValue ("Height"); + aValue >>= aSize.Height; + xRectangle->setSize (aSize); + + // Create the accessible object for the shape and + // initialize it. + xShape = new AccessiblePageShape ( + xView->getCurrentPage(), this, maShapeTreeInfo); + } + } + } + return xShape; +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessibleDrawDocumentView::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + tools::Long nChildCount = AccessibleDocumentViewBase::getAccessibleChildCount(); + + // Forward request to children manager. + if (mpChildrenManager != nullptr) + nChildCount += mpChildrenManager->GetChildCount(); + + return nChildCount; +} + +uno::Reference SAL_CALL + AccessibleDrawDocumentView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + + ::osl::ClearableMutexGuard aGuard (m_aMutex); + + // Take care of children of the base class. + sal_Int32 nCount = AccessibleDocumentViewBase::getAccessibleChildCount(); + if (nCount > 0) + { + if (nIndex < nCount) + return AccessibleDocumentViewBase::getAccessibleChild(nIndex); + else + nIndex -= nCount; + } + + // Create a copy of the pointer to the children manager and release the + // mutex before calling any of its methods. + ChildrenManager* pChildrenManager = mpChildrenManager.get(); + aGuard.clear(); + + // Forward request to children manager. + if (pChildrenManager == nullptr) + throw lang::IndexOutOfBoundsException ( + "no accessible child with index " + OUString::number(nIndex), + static_cast(this)); + + return pChildrenManager->GetChild (nIndex); +} + +OUString SAL_CALL + AccessibleDrawDocumentView::getAccessibleName() +{ + SolarMutexGuard g; + + OUString sName = SdResId(SID_SD_A11Y_D_PRESENTATION); + ::sd::View* pSdView = static_cast< ::sd::View* >( maShapeTreeInfo.GetSdrView() ); + if ( pSdView ) + { + SdDrawDocument& rDoc = pSdView->GetDoc(); + OUString sFileName = rDoc.getDocAccTitle(); + if ( !sFileName.getLength() ) + { + ::sd::DrawDocShell* pDocSh = pSdView->GetDocSh(); + if ( pDocSh ) + { + sFileName = pDocSh->GetTitle( SFX_TITLE_APINAME ); + } + } + + OUString sReadOnly; + if(rDoc.getDocReadOnly()) + { + sReadOnly = SdResId(SID_SD_A11Y_D_PRESENTATION_READONLY); + } + + if ( sFileName.getLength() ) + { + sName = sFileName + sReadOnly + " - " + sName; + } + } + + return sName; +} + +//===== XEventListener ====================================================== + +void SAL_CALL + AccessibleDrawDocumentView::disposing (const lang::EventObject& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::disposing (rEventObject); + if (rEventObject.Source == mxModel) + { + ::osl::Guard< ::osl::Mutex> aGuard (::osl::Mutex::getGlobalMutex()); + // maShapeTreeInfo has been modified in base class. + if (mpChildrenManager != nullptr) + mpChildrenManager->SetInfo (maShapeTreeInfo); + } +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL + AccessibleDrawDocumentView::propertyChange (const beans::PropertyChangeEvent& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::propertyChange (rEventObject); + + // add page switch event for slide show mode + if (rEventObject.PropertyName == "CurrentPage" || + rEventObject.PropertyName == "PageChange") + { + // Update the accessible name to reflect the current slide. + UpdateAccessibleName(); + + // The current page changed. Update the children manager accordingly. + uno::Reference xView (mxController, uno::UNO_QUERY); + if (xView.is() && mpChildrenManager!=nullptr) + { + // Inform the children manager to forget all children and give + // him the new ones. + mpChildrenManager->ClearAccessibleShapeList (); + mpChildrenManager->SetShapeList (xView->getCurrentPage()); + + rtl::Reference xPage(CreateDrawPageShape ()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (false); + } + } + else + SAL_WARN("sd", "View invalid"); + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue,rEventObject.OldValue); + } + else if ( rEventObject.PropertyName == "VisibleArea" ) + { + if (mpChildrenManager != nullptr) + mpChildrenManager->ViewForwarderChanged(); + } + else if (rEventObject.PropertyName == "ActiveLayer") + { + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue,rEventObject.OldValue); + } + else if (rEventObject.PropertyName == "UpdateAcc") + { + // The current page changed. Update the children manager accordingly. + uno::Reference xView (mxController, uno::UNO_QUERY); + if (xView.is() && mpChildrenManager!=nullptr) + { + // Inform the children manager to forget all children and give + // him the new ones. + mpChildrenManager->ClearAccessibleShapeList (); + // update the slide show page's accessible info + //mpChildrenManager->SetShapeList (uno::Reference ( + // xView->getCurrentPage(), uno::UNO_QUERY)); + rtl::Reference< sd::SlideShow > xSlideshow( sd::SlideShow::GetSlideShow( mpSdViewSh->GetViewShellBase() ) ); + if( xSlideshow.is() && xSlideshow->isRunning() && xSlideshow->isFullScreen() ) + { + css::uno::Reference< drawing::XDrawPage > xSlide; + // MT IA2: Not used... + // sal_Int32 currentPageIndex = xSlideshow->getCurrentPageIndex(); + css::uno::Reference< css::presentation::XSlideShowController > xSlideController = xSlideshow->getController(); + if( xSlideController.is() ) + { + xSlide = xSlideController->getCurrentSlide(); + if (xSlide.is()) + { + mpChildrenManager->SetShapeList (xSlide); + } + } + } + rtl::Reference xPage(CreateDrawPageShape ()); + if (xPage.is()) + { + xPage->Init(); + mpChildrenManager->AddAccessibleShape (xPage); + mpChildrenManager->Update (false); + } + } + } + else + { + SAL_INFO("sd", "unhandled"); + } +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleDrawDocumentView::getImplementationName() +{ + return "AccessibleDrawDocumentView"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessibleDrawDocumentView::getSupportedServiceNames() +{ + ThrowIfDisposed(); + const css::uno::Sequence vals { "com.sun.star.drawing.AccessibleDrawDocumentView" }; + uno::Sequence aServiceNames = + AccessibleDocumentViewBase::getSupportedServiceNames(); + + return comphelper::concatSequences(aServiceNames, vals); +} + +//===== XInterface ========================================================== + +uno::Any SAL_CALL + AccessibleDrawDocumentView::queryInterface (const uno::Type & rType) +{ + uno::Any aReturn = AccessibleDocumentViewBase::queryInterface (rType); + if ( ! aReturn.hasValue()) + aReturn = ::cppu::queryInterface (rType, + static_cast(this) + ); + return aReturn; +} + +void SAL_CALL + AccessibleDrawDocumentView::acquire() + noexcept +{ + AccessibleDocumentViewBase::acquire (); +} +void SAL_CALL + AccessibleDrawDocumentView::release() + noexcept +{ + AccessibleDocumentViewBase::release (); +} +//===== XAccessibleGroupPosition ========================================= +uno::Sequence< sal_Int32 > SAL_CALL + AccessibleDrawDocumentView::getGroupPosition( const uno::Any& rAny ) +{ + SolarMutexGuard g; + + // we will return the: + // [0] group level(always be 0 now) + // [1] similar items counts in the group + // [2] the position of the object in the group + uno::Sequence< sal_Int32 > aRet( 3 ); + //get the xShape of the current selected drawing object + uno::Reference xAccContent; + rAny >>= xAccContent; + if ( !xAccContent.is() ) + { + return aRet; + } + AccessibleShape* pAcc = comphelper::getFromUnoTunnel( xAccContent ); + if ( !pAcc ) + { + return aRet; + } + uno::Reference< drawing::XShape > xCurShape = pAcc->GetXShape(); + if ( !xCurShape.is() ) + { + return aRet; + } + //find all the child in the page, insert them into a vector and sort + if ( mpChildrenManager == nullptr ) + { + return aRet; + } + std::vector< uno::Reference > vXShapes; + sal_Int32 nCount = mpChildrenManager->GetChildCount(); + //get pointer of SdView & SdrPageView for further use. + SdrPageView* pPV = nullptr; + ::sd::View* pSdView = nullptr; + if ( mpSdViewSh ) + { + pSdView = mpSdViewSh->GetView(); + pPV = pSdView->GetSdrPageView(); + } + for ( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xShape = mpChildrenManager->GetChildShape(i); + if ( xShape.is() ) + { + //if the object is visible in the page, we add it into the group list. + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + if ( pObj && pPV && pSdView && pSdView->IsObjMarkable( pObj, pPV ) ) + { + vXShapes.push_back( xShape ); + } + } + } + std::sort( vXShapes.begin(), vXShapes.end(), XShapePosCompareHelper() ); + //get the index of the selected object in the group + auto aIter = std::find_if(vXShapes.begin(), vXShapes.end(), + [&xCurShape](const uno::Reference& rxShape) { return rxShape.get() == xCurShape.get(); }); + if (aIter != vXShapes.end()) + { + sal_Int32* pArray = aRet.getArray(); + pArray[0] = 1; //it should be 1 based, not 0 based. + pArray[1] = vXShapes.size(); + pArray[2] = static_cast(std::distance(vXShapes.begin(), aIter)) + 1; //we start counting position from 1 + } + return aRet; +} + +OUString AccessibleDrawDocumentView::getObjectLink( const uno::Any& rAny ) +{ + SolarMutexGuard g; + + OUString aRet; + //get the xShape of the current selected drawing object + uno::Reference xAccContent; + rAny >>= xAccContent; + if ( !xAccContent.is() ) + { + return aRet; + } + AccessibleShape* pAcc = comphelper::getFromUnoTunnel( xAccContent ); + if ( !pAcc ) + { + return aRet; + } + uno::Reference< drawing::XShape > xCurShape = pAcc->GetXShape(); + if ( !xCurShape.is() ) + { + return aRet; + } + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xCurShape); + if (pObj) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObj); + if( pInfo && (pInfo->meClickAction == presentation::ClickAction_DOCUMENT) ) + aRet = pInfo->GetBookmark(); + } + return aRet; +} + +/// Create a name for this view. +OUString AccessibleDrawDocumentView::CreateAccessibleName() +{ + OUString sName; + + uno::Reference xInfo (mxController, uno::UNO_QUERY); + if (xInfo.is()) + { + uno::Sequence< OUString > aServices( xInfo->getSupportedServiceNames() ); + OUString sFirstService = aServices[0]; + if ( sFirstService == "com.sun.star.drawing.DrawingDocumentDrawView" ) + { + if( aServices.getLength() >= 2 && aServices[1] == "com.sun.star.presentation.PresentationView") + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_DRAWVIEW_N); + } + else + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_D_DRAWVIEW_N); + } + } + else if ( sFirstService == "com.sun.star.presentation.NotesView" ) + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_NOTESVIEW_N); + } + else if ( sFirstService == "com.sun.star.presentation.HandoutView" ) + { + SolarMutexGuard aGuard; + + sName = SdResId(SID_SD_A11Y_I_HANDOUTVIEW_N); + } + else + { + sName = sFirstService; + } + } + else + { + sName = "AccessibleDrawDocumentView"; + } + return sName; +} + +/** Return selection state of specified child +*/ +bool + AccessibleDrawDocumentView::implIsSelected( sal_Int32 nAccessibleChildIndex ) +{ + const SolarMutexGuard aSolarGuard; + uno::Reference< view::XSelectionSupplier > xSel( mxController, uno::UNO_QUERY ); + bool bRet = false; + + OSL_ENSURE( 0 <= nAccessibleChildIndex, "AccessibleDrawDocumentView::implIsSelected: invalid index!" ); + + if( xSel.is() && ( 0 <= nAccessibleChildIndex ) ) + { + uno::Any aAny( xSel->getSelection() ); + uno::Reference< drawing::XShapes > xShapes; + + aAny >>= xShapes; + + if( xShapes.is() ) + { + AccessibleShape* pAcc = comphelper::getFromUnoTunnel( getAccessibleChild( nAccessibleChildIndex ) ); + + if( pAcc ) + { + uno::Reference< drawing::XShape > xShape( pAcc->GetXShape() ); + + if( xShape.is() ) + { + for( sal_Int32 i = 0, nCount = xShapes->getCount(); ( i < nCount ) && !bRet; ++i ) + if( xShapes->getByIndex( i ) == xShape ) + bRet = true; + } + } + } + } + + return bRet; +} + +/** Select or deselect the specified shapes. The corresponding accessible + shapes are notified over the selection change listeners registered with + the XSelectionSupplier of the controller. +*/ +void + AccessibleDrawDocumentView::implSelect( sal_Int32 nAccessibleChildIndex, bool bSelect ) +{ + const SolarMutexGuard aSolarGuard; + uno::Reference< view::XSelectionSupplier > xSel( mxController, uno::UNO_QUERY ); + + if( !xSel.is() ) + return; + + uno::Any aAny; + + if( ACCESSIBLE_SELECTION_CHILD_ALL == nAccessibleChildIndex ) + { + // Select or deselect all children. + + if( !bSelect ) + xSel->select( aAny ); + else + { + uno::Reference< drawing::XShapes > xShapes = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + + for(sal_Int32 i = 0, nCount = getAccessibleChildCount(); i < nCount; ++i ) + { + AccessibleShape* pAcc = comphelper::getFromUnoTunnel( getAccessibleChild( i ) ); + + if( pAcc && pAcc->GetXShape().is() ) + xShapes->add( pAcc->GetXShape() ); + } + + if( xShapes->getCount() ) + { + xSel->select( Any(xShapes) ); + } + } + } + else if( nAccessibleChildIndex >= 0 ) + { + // Select or deselect only the child with index + // nAccessibleChildIndex. + + AccessibleShape* pAcc = comphelper::getFromUnoTunnel( + getAccessibleChild( nAccessibleChildIndex )); + + // Add or remove the shape that is made accessible from the + // selection of the controller. + if( pAcc ) + { + uno::Reference< drawing::XShape > xShape( pAcc->GetXShape() ); + + if( xShape.is() ) + { + uno::Reference< drawing::XShapes > xShapes; + bool bFound = false; + + aAny = xSel->getSelection(); + aAny >>= xShapes; + + // Search shape to be selected in current selection. + if (xShapes.is()) + { + sal_Int32 nCount = xShapes->getCount(); + for (sal_Int32 i=0; ( i < nCount ) && !bFound; ++i ) + if( xShapes->getByIndex( i ) == xShape ) + bFound = true; + } + else + // Create an empty selection to add the shape to. + xShapes = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + + // Update the selection. + if( !bFound && bSelect ) + xShapes->add( xShape ); + else if( bFound && !bSelect ) + xShapes->remove( xShape ); + + xSel->select( Any(xShapes) ); + } + } + } +} + +void AccessibleDrawDocumentView::Activated() +{ + if (mpChildrenManager == nullptr) + return; + + bool bChange = false; + // When none of the children has the focus then claim it for the + // view. + if ( ! mpChildrenManager->HasFocus()) + { + SetState (AccessibleStateType::FOCUSED); + bChange = true; + } + else + ResetState (AccessibleStateType::FOCUSED); + mpChildrenManager->UpdateSelection(); + // if the child gets focus in UpdateSelection(), needs to reset the focus on document. + if (mpChildrenManager->HasFocus() && bChange) + ResetState (AccessibleStateType::FOCUSED); +} + +void AccessibleDrawDocumentView::Deactivated() +{ + if (mpChildrenManager != nullptr) + mpChildrenManager->RemoveFocus(); + ResetState (AccessibleStateType::FOCUSED); +} + +void AccessibleDrawDocumentView::impl_dispose() +{ + mpChildrenManager.reset(); + AccessibleDocumentViewBase::impl_dispose(); +} + +/** This method is called from the component helper base class while + disposing. +*/ +void SAL_CALL AccessibleDrawDocumentView::disposing() +{ + // Release resources. + mpChildrenManager.reset(); + + // Forward call to base classes. + AccessibleDocumentViewBase::disposing (); +} + +void AccessibleDrawDocumentView::UpdateAccessibleName() +{ + OUString sNewName (CreateAccessibleName() + ": "); + + // Add the number of the current slide. + uno::Reference xView (mxController, uno::UNO_QUERY); + if (xView.is()) + { + uno::Reference xProperties (xView->getCurrentPage(), UNO_QUERY); + if (xProperties.is()) + try + { + sal_Int16 nPageNumber (0); + if (xProperties->getPropertyValue("Number") >>= nPageNumber) + { + sNewName += OUString::number(nPageNumber); + } + } + catch (const beans::UnknownPropertyException&) + { + } + } + + // Add the number of pages/slides. + Reference xPagesSupplier (mxModel, UNO_QUERY); + if (xPagesSupplier.is()) + { + Reference xPages = xPagesSupplier->getDrawPages(); + if (xPages.is()) + { + sNewName += " / " + OUString::number(xPages->getCount()); + } + } + + SetAccessibleName (sNewName, AutomaticallyCreated); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx b/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx new file mode 100644 index 000000000..a1a79a678 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleOutlineEditSource.cxx @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +namespace accessibility +{ + + AccessibleOutlineEditSource::AccessibleOutlineEditSource( + SdrOutliner& rOutliner, + SdrView& rView, + OutlinerView& rOutlView, + const vcl::Window& rViewWindow ) + : mrView( rView ), + mrWindow( rViewWindow ), + mpOutliner( &rOutliner ), + mpOutlinerView( &rOutlView ), + mTextForwarder( rOutliner, false ), + mViewForwarder( rOutlView ) + { + // register as listener - need to broadcast state change messages + // Moved to ::GetTextForwarder() + //rOutliner.SetNotifyHdl( LINK(this, AccessibleOutlineEditSource, NotifyHdl) ); + StartListening(rOutliner); + } + + AccessibleOutlineEditSource::~AccessibleOutlineEditSource() + { + if( mpOutliner ) + mpOutliner->SetNotifyHdl( Link() ); + Broadcast( TextHint( SfxHintId::Dying ) ); + } + + std::unique_ptr AccessibleOutlineEditSource::Clone() const + { + return std::unique_ptr(new AccessibleOutlineEditSource(*mpOutliner, mrView, *mpOutlinerView, mrWindow)); + } + + SvxTextForwarder* AccessibleOutlineEditSource::GetTextForwarder() + { + // TODO: maybe suboptimal + if( IsValid() ) + { + // Moved here to make sure that + // the NotifyHandler was set on the current object. + mpOutliner->SetNotifyHdl( LINK(this, AccessibleOutlineEditSource, NotifyHdl) ); + return &mTextForwarder; + } + else + return nullptr; + } + + SvxViewForwarder* AccessibleOutlineEditSource::GetViewForwarder() + { + // TODO: maybe suboptimal + if( IsValid() ) + return this; + else + return nullptr; + } + + SvxEditViewForwarder* AccessibleOutlineEditSource::GetEditViewForwarder( bool ) + { + // TODO: maybe suboptimal + if( IsValid() ) + { + // ignore parameter, we're always in edit mode here + return &mViewForwarder; + } + else + return nullptr; + } + + void AccessibleOutlineEditSource::UpdateData() + { + // NOOP, since we're always working on the 'real' outliner, + // i.e. changes are immediately reflected on the screen + } + + SfxBroadcaster& AccessibleOutlineEditSource::GetBroadcaster() const + { + return * const_cast< AccessibleOutlineEditSource* > (this); + } + + bool AccessibleOutlineEditSource::IsValid() const + { + if( mpOutliner && mpOutlinerView ) + { + // Our view still on outliner? + sal_uLong nCurrView, nViews; + + for( nCurrView=0, nViews=mpOutliner->GetViewCount(); nCurrViewGetView(nCurrView) == mpOutlinerView ) + return true; + } + } + + return false; + } + + Point AccessibleOutlineEditSource::LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const + { + if( IsValid() && mrView.GetModel() ) + { + Point aPoint( OutputDevice::LogicToLogic( rPoint, rMapMode, + MapMode(mrView.GetModel()->GetScaleUnit()) ) ); + MapMode aMapMode(mrWindow.GetMapMode()); + aMapMode.SetOrigin(Point()); + return mrWindow.LogicToPixel( aPoint, aMapMode ); + } + + return Point(); + } + + Point AccessibleOutlineEditSource::PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const + { + if( IsValid() && mrView.GetModel() ) + { + MapMode aMapMode(mrWindow.GetMapMode()); + aMapMode.SetOrigin(Point()); + Point aPoint( mrWindow.PixelToLogic( rPoint, aMapMode ) ); + return OutputDevice::LogicToLogic( aPoint, + MapMode(mrView.GetModel()->GetScaleUnit()), + rMapMode ); + } + + return Point(); + } + + void AccessibleOutlineEditSource::Notify( SfxBroadcaster& rBroadcaster, const SfxHint& rHint ) + { + bool bDispose = false; + + if( &rBroadcaster == mpOutliner ) + { + if( rHint.GetId() == SfxHintId::Dying ) + { + bDispose = true; + mpOutliner = nullptr; + } + } + else + { + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint ); + if( pSdrHint->GetKind() == SdrHintKind::ModelCleared ) + { + // model is dying under us, going defunc + bDispose = true; + } + } + } + + if( bDispose ) + { + if( mpOutliner ) + mpOutliner->SetNotifyHdl( Link() ); + mpOutliner = nullptr; + mpOutlinerView = nullptr; + Broadcast( TextHint( SfxHintId::Dying ) ); + } + } + + IMPL_LINK(AccessibleOutlineEditSource, NotifyHdl, EENotify&, rNotify, void) + { + ::std::unique_ptr< SfxHint > aHint( SvxEditSourceHelper::EENotification2Hint( &rNotify) ); + + if (aHint) + { + Broadcast(*aHint); + } + } + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleOutlineView.cxx b/sd/source/ui/accessibility/AccessibleOutlineView.cxx new file mode 100644 index 000000000..4e020efef --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleOutlineView.cxx @@ -0,0 +1,238 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +//===== internal ============================================================ + +AccessibleOutlineView::AccessibleOutlineView ( + ::sd::Window* pSdWindow, + ::sd::OutlineViewShell* pViewShell, + const uno::Reference& rxController, + const uno::Reference& rxParent) + : AccessibleDocumentViewBase (pSdWindow, pViewShell, rxController, rxParent), + maTextHelper( ::std::unique_ptr< SvxEditSource >() ) +{ + SolarMutexGuard aGuard; + + // Beware! Here we leave the paths of the UNO API and descend into the + // depths of the core. Necessary for making the edit engine accessible. + if (!pSdWindow) + return; + + ::sd::View* pView = pViewShell->GetView(); + + auto pShellView = dynamic_cast<::sd::OutlineView* >( pView ); + if(!pShellView) + return; + + OutlinerView* pOutlineView = pShellView->GetViewByWindow( pSdWindow ); + SdrOutliner& rOutliner = pShellView->GetOutliner(); + + if( pOutlineView ) + { + maTextHelper.SetEditSource( ::std::unique_ptr< SvxEditSource >( new AccessibleOutlineEditSource( + rOutliner, *pView, *pOutlineView, *pSdWindow ) ) ); + } +} + +AccessibleOutlineView::~AccessibleOutlineView() +{ +} + +void AccessibleOutlineView::Init() +{ + // Set event source _before_ starting to listen + maTextHelper.SetEventSource(this); + + AccessibleDocumentViewBase::Init (); +} + +void AccessibleOutlineView::ViewForwarderChanged() +{ + AccessibleDocumentViewBase::ViewForwarderChanged(); + + UpdateChildren(); +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessibleOutlineView::getAccessibleChildCount() +{ + ThrowIfDisposed (); + + // forward + return maTextHelper.GetChildCount(); +} + +uno::Reference SAL_CALL + AccessibleOutlineView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed (); + // Forward request to children manager. + return maTextHelper.GetChild(nIndex); +} + +OUString SAL_CALL + AccessibleOutlineView::getAccessibleName() +{ + SolarMutexGuard g; + + OUString sName = SdResId(SID_SD_A11Y_D_PRESENTATION); + ::sd::View* pSdView = static_cast< ::sd::View* >( maShapeTreeInfo.GetSdrView() ); + if ( pSdView ) + { + SdDrawDocument& rDoc = pSdView->GetDoc(); + OUString sFileName = rDoc.getDocAccTitle(); + if (sFileName.isEmpty()) + { + ::sd::DrawDocShell* pDocSh = pSdView->GetDocSh(); + if ( pDocSh ) + { + sFileName = pDocSh->GetTitle( SFX_TITLE_APINAME ); + } + } + if (!sFileName.isEmpty()) + { + sName = sFileName + " - " + sName; + } + } + return sName; +} + +//===== XAccessibleEventBroadcaster ======================================== + +void SAL_CALL AccessibleOutlineView::addAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) +{ + // delegate listener handling to children manager. + if ( ! IsDisposed()) + maTextHelper.AddEventListener(xListener); + AccessibleContextBase::addEventListener(xListener); +} + +void SAL_CALL AccessibleOutlineView::removeAccessibleEventListener( const uno::Reference< XAccessibleEventListener >& xListener ) +{ + // forward + if ( ! IsDisposed()) + maTextHelper.RemoveEventListener(xListener); + AccessibleContextBase::removeEventListener(xListener); +} + +// XServiceInfo + +OUString SAL_CALL + AccessibleOutlineView::getImplementationName() +{ + return "AccessibleOutlineView"; +} + +//===== XEventListener ====================================================== + +//===== protected internal ================================================== + +void AccessibleOutlineView::Activated() +{ + SolarMutexGuard aGuard; + + // delegate listener handling to children manager. + maTextHelper.SetFocus(); +} + +void AccessibleOutlineView::Deactivated() +{ + SolarMutexGuard aGuard; + + // delegate listener handling to children manager. + maTextHelper.SetFocus(false); +} + +void SAL_CALL AccessibleOutlineView::disposing() +{ + // dispose children + maTextHelper.Dispose(); + + AccessibleDocumentViewBase::disposing (); +} + +//===== XPropertyChangeListener ============================================= + +void SAL_CALL + AccessibleOutlineView::propertyChange (const beans::PropertyChangeEvent& rEventObject) +{ + ThrowIfDisposed (); + + AccessibleDocumentViewBase::propertyChange (rEventObject); + + //add page switch event for slide show mode + if (rEventObject.PropertyName == "CurrentPage" || + rEventObject.PropertyName == "PageChange") + { + // The current page changed. Update the children accordingly. + UpdateChildren(); + CommitChange(AccessibleEventId::PAGE_CHANGED,rEventObject.NewValue, rEventObject.OldValue); + } + else if ( rEventObject.PropertyName == "VisibleArea" ) + { + // The visible area changed. Update the children accordingly. + UpdateChildren(); + } + else + { + SAL_INFO("sd", "unhandled"); + } +} + +/// Create a name for this view. +OUString AccessibleOutlineView::CreateAccessibleName() +{ + return SdResId(SID_SD_A11Y_I_OUTLINEVIEW_N); +} + +void AccessibleOutlineView::UpdateChildren() +{ + SolarMutexGuard aGuard; + + // Update visible children + maTextHelper.UpdateChildren(); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePageShape.cxx b/sd/source/ui/accessibility/AccessiblePageShape.cxx new file mode 100644 index 000000000..2900019ae --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePageShape.cxx @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; +using ::com::sun::star::uno::Reference; + +namespace accessibility { + +//===== internal ============================================================ + +AccessiblePageShape::AccessiblePageShape ( + const uno::Reference& rxPage, + const uno::Reference& rxParent, + const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape (AccessibleShapeInfo (nullptr, rxParent), rShapeTreeInfo), + mxPage (rxPage) +{ + // The main part of the initialization is done in the init method which + // has to be called from this constructor's caller. +} + +AccessiblePageShape::~AccessiblePageShape() +{ +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL + AccessiblePageShape::getAccessibleChildCount() +{ + return 0; +} + +/** Forward the request to the shape. Return the requested shape or throw + an exception for a wrong index. +*/ +uno::Reference SAL_CALL + AccessiblePageShape::getAccessibleChild( sal_Int32 ) +{ + throw lang::IndexOutOfBoundsException ("page shape has no children", + static_cast(this)); +} + +//===== XAccessibleComponent ================================================ + +awt::Rectangle SAL_CALL AccessiblePageShape::getBounds() +{ + ThrowIfDisposed (); + + awt::Rectangle aBoundingBox; + + if (maShapeTreeInfo.GetViewForwarder() != nullptr) + { + uno::Reference xSet (mxPage, uno::UNO_QUERY); + if (xSet.is()) + { + uno::Any aValue; + + aValue = xSet->getPropertyValue ("BorderLeft"); + aValue >>= aBoundingBox.X; + aValue = xSet->getPropertyValue ("BorderTop"); + aValue >>= aBoundingBox.Y; + + aValue = xSet->getPropertyValue ("Width"); + aValue >>= aBoundingBox.Width; + aValue = xSet->getPropertyValue ("Height"); + aValue >>= aBoundingBox.Height; + } + + // Transform coordinates from internal to pixel. + ::Size aPixelSize = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Size (aBoundingBox.Width, aBoundingBox.Height)); + ::Point aPixelPosition = maShapeTreeInfo.GetViewForwarder()->LogicToPixel ( + ::Point (aBoundingBox.X, aBoundingBox.Y)); + + // Clip the shape's bounding box with the bounding box of its parent. + Reference xParentComponent ( + getAccessibleParent(), uno::UNO_QUERY); + if (xParentComponent.is()) + { + // Make the coordinates relative to the parent. + awt::Point aParentLocation (xParentComponent->getLocationOnScreen()); + int x = aPixelPosition.getX() - aParentLocation.X; + int y = aPixelPosition.getY() - aParentLocation.Y; + + // Clip with parent (with coordinates relative to itself). + ::tools::Rectangle aBBox ( + x, y, x + aPixelSize.getWidth(), y + aPixelSize.getHeight()); + awt::Size aParentSize (xParentComponent->getSize()); + ::tools::Rectangle aParentBBox (0,0, aParentSize.Width, aParentSize.Height); + aBBox = aBBox.GetIntersection (aParentBBox); + aBoundingBox = awt::Rectangle ( + aBBox.Left(), + aBBox.Top(), + aBBox.getWidth(), + aBBox.getHeight()); + } + else + aBoundingBox = awt::Rectangle ( + aPixelPosition.getX(), aPixelPosition.getY(), + aPixelSize.getWidth(), aPixelSize.getHeight()); + } + + return aBoundingBox; +} + +sal_Int32 SAL_CALL AccessiblePageShape::getForeground() +{ + ThrowIfDisposed (); + sal_Int32 nColor (0x0ffffffL); + + try + { + uno::Reference aSet (mxPage, uno::UNO_QUERY); + if (aSet.is()) + { + uno::Any aColor = aSet->getPropertyValue ("LineColor"); + aColor >>= nColor; + } + } + catch (const css::beans::UnknownPropertyException&) + { + // Ignore exception and return default color. + } + return nColor; +} + +/** Extract the background color from the Background property of the + draw page or its master page. +*/ +sal_Int32 SAL_CALL AccessiblePageShape::getBackground() +{ + ThrowIfDisposed (); + sal_Int32 nColor (0x01020ffL); + + try + { + uno::Reference xSet (mxPage, uno::UNO_QUERY); + if (xSet.is()) + { + uno::Any aBGSet = xSet->getPropertyValue ("Background"); + Reference xBGSet (aBGSet, uno::UNO_QUERY); + if ( ! xBGSet.is()) + { + // Draw page has no Background property. Try the master + // page instead. + Reference xTarget (mxPage, uno::UNO_QUERY); + if (xTarget.is()) + { + xSet.set(xTarget->getMasterPage(), uno::UNO_QUERY); + aBGSet = xSet->getPropertyValue ("Background"); + xBGSet.set(aBGSet, uno::UNO_QUERY); + } + } + // Fetch the fill color. Has to be extended to cope with + // gradients, hashes, and bitmaps. + if (xBGSet.is()) + { + uno::Any aColor = xBGSet->getPropertyValue ("FillColor"); + aColor >>= nColor; + } + else + SAL_WARN("sd", "no Background property in page"); + } + } + catch (const css::beans::UnknownPropertyException&) + { + TOOLS_WARN_EXCEPTION("sd", "caught exception due to unknown property"); + // Ignore exception and return default color. + } + return nColor; +} + +// XServiceInfo + +OUString SAL_CALL + AccessiblePageShape::getImplementationName() +{ + ThrowIfDisposed (); + return "AccessiblePageShape"; +} + +css::uno::Sequence< OUString> SAL_CALL + AccessiblePageShape::getSupportedServiceNames() +{ + ThrowIfDisposed (); + return AccessibleShape::getSupportedServiceNames(); +} + +//===== XComponent ========================================================== + +void AccessiblePageShape::dispose() +{ + // Cleanup. + mxShape = nullptr; + + // Call base classes. + AccessibleContextBase::dispose (); +} + +//===== protected internal ================================================== + +OUString + AccessiblePageShape::CreateAccessibleBaseName() +{ + return "PageShape"; +} + +OUString + AccessiblePageShape::CreateAccessibleName() +{ + Reference xPageProperties (mxPage, UNO_QUERY); + + // Get name of the current slide. + OUString sCurrentSlideName; + try + { + if (xPageProperties.is()) + { + xPageProperties->getPropertyValue( "LinkDisplayName" ) >>= sCurrentSlideName; + } + } + catch (const beans::UnknownPropertyException&) + { + } + + return CreateAccessibleBaseName()+": "+sCurrentSlideName; +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx new file mode 100644 index 000000000..a8c37fa89 --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationGraphicShape.cxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationGraphicShape::AccessiblePresentationGraphicShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleGraphicShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationGraphicShape::~AccessiblePresentationGraphicShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationGraphicShape::getImplementationName() +{ + return "AccessiblePresentationGraphicShape"; +} + +/// Set this object's name if is different to the current name. +OUString AccessiblePresentationGraphicShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_GRAPHIC_OBJECT: + sName = "ImpressGraphicObject"; + break; + default: + sName = "UnknownAccessibleImpressShape"; + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +sal_Int16 SAL_CALL AccessiblePresentationGraphicShape::getAccessibleRole() +{ + return AccessibleRole::GRAPHIC; +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx new file mode 100644 index 000000000..411d04af1 --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationOLEShape.cxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationOLEShape::AccessiblePresentationOLEShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleOLEShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationOLEShape::~AccessiblePresentationOLEShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationOLEShape::getImplementationName() +{ + return "AccessiblePresentationOLEShape"; +} + +/// Set this object's name if it is different to the current name. +OUString AccessiblePresentationOLEShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_OLE: + sName = "ImpressOLE"; + break; + case PRESENTATION_CHART: + sName = "ImpressChart"; + break; + case PRESENTATION_TABLE: + sName = "ImpressTable"; + break; + default: + sName = "UnknownAccessibleImpressOLEShape"; + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +// Return this object's role. +sal_Int16 SAL_CALL AccessiblePresentationOLEShape::getAccessibleRole() +{ + return AccessibleRole::EMBEDDED_OBJECT; +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessiblePresentationShape.cxx b/sd/source/ui/accessibility/AccessiblePresentationShape.cxx new file mode 100644 index 000000000..e4afe7e9a --- /dev/null +++ b/sd/source/ui/accessibility/AccessiblePresentationShape.cxx @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::accessibility; + +namespace accessibility +{ +//===== internal ============================================================ + +AccessiblePresentationShape::AccessiblePresentationShape( + const AccessibleShapeInfo& rShapeInfo, const AccessibleShapeTreeInfo& rShapeTreeInfo) + : AccessibleShape(rShapeInfo, rShapeTreeInfo) +{ +} + +AccessiblePresentationShape::~AccessiblePresentationShape() {} + +// XServiceInfo + +OUString SAL_CALL AccessiblePresentationShape::getImplementationName() +{ + return "AccessiblePresentationShape"; +} + +/// Set this object's name if is different to the current name. +OUString AccessiblePresentationShape::CreateAccessibleBaseName() +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_TITLE: + sName = SdResId(SID_SD_A11Y_P_TITLE_N); + break; + case PRESENTATION_OUTLINER: + sName = SdResId(SID_SD_A11Y_P_OUTLINER_N); + break; + case PRESENTATION_SUBTITLE: + sName = SdResId(SID_SD_A11Y_P_SUBTITLE_N); + break; + case PRESENTATION_PAGE: + sName = SdResId(SID_SD_A11Y_P_PAGE_N); + break; + case PRESENTATION_NOTES: + sName = SdResId(SID_SD_A11Y_P_NOTES_N); + break; + case PRESENTATION_HANDOUT: + sName = SdResId(SID_SD_A11Y_P_HANDOUT_N); + break; + case PRESENTATION_HEADER: + sName = SdResId(SID_SD_A11Y_P_HEADER_N); + break; + case PRESENTATION_FOOTER: + sName = SdResId(SID_SD_A11Y_P_FOOTER_N); + break; + case PRESENTATION_DATETIME: + sName = SdResId(SID_SD_A11Y_P_DATE_N); + break; + case PRESENTATION_PAGENUMBER: + sName = SdResId(SID_SD_A11Y_P_NUMBER_N); + break; + default: + sName = SdResId(SID_SD_A11Y_P_UNKNOWN_N); + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} + +OUString AccessiblePresentationShape::GetStyle() const +{ + OUString sName; + + ShapeTypeId nShapeType = ShapeTypeHandler::Instance().GetTypeId(mxShape); + switch (nShapeType) + { + case PRESENTATION_TITLE: + sName = SdResId(SID_SD_A11Y_P_TITLE_N_STYLE); + break; + case PRESENTATION_OUTLINER: + sName = SdResId(SID_SD_A11Y_P_OUTLINER_N_STYLE); + break; + case PRESENTATION_SUBTITLE: + sName = SdResId(SID_SD_A11Y_P_SUBTITLE_N_STYLE); + break; + case PRESENTATION_PAGE: + sName = SdResId(SID_SD_A11Y_P_PAGE_N_STYLE); + break; + case PRESENTATION_NOTES: + sName = SdResId(SID_SD_A11Y_P_NOTES_N_STYLE); + break; + case PRESENTATION_HANDOUT: + sName = SdResId(SID_SD_A11Y_P_HANDOUT_N_STYLE); + break; + case PRESENTATION_FOOTER: + sName = SdResId(SID_SD_A11Y_P_FOOTER_N_STYLE); + break; + case PRESENTATION_HEADER: + sName = SdResId(SID_SD_A11Y_P_HEADER_N_STYLE); + break; + case PRESENTATION_DATETIME: + sName = SdResId(SID_SD_A11Y_P_DATE_N_STYLE); + break; + case PRESENTATION_PAGENUMBER: + sName = SdResId(SID_SD_A11Y_P_NUMBER_N_STYLE); + break; + default: + sName = SdResId(SID_SD_A11Y_P_UNKNOWN_N_STYLE); + if (mxShape.is()) + sName += ": " + mxShape->getShapeType(); + } + + return sName; +} +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx b/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx new file mode 100644 index 000000000..13fc60db0 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleSlideSorterObject.cxx @@ -0,0 +1,429 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +AccessibleSlideSorterObject::AccessibleSlideSorterObject( + const Reference& rxParent, + ::sd::slidesorter::SlideSorter& rSlideSorter, + sal_uInt16 nPageNumber) + : mxParent(rxParent), + mnPageNumber(nPageNumber), + mrSlideSorter(rSlideSorter), + mnClientId(0) +{ +} + +AccessibleSlideSorterObject::~AccessibleSlideSorterObject() +{ + if ( ! IsDisposed()) + dispose(); +} + +void AccessibleSlideSorterObject::FireAccessibleEvent ( + short nEventId, + const uno::Any& rOldValue, + const uno::Any& rNewValue) +{ + if (mnClientId != 0) + { + AccessibleEventObject aEventObject; + + aEventObject.Source = Reference(this); + aEventObject.EventId = nEventId; + aEventObject.NewValue = rNewValue; + aEventObject.OldValue = rOldValue; + + comphelper::AccessibleEventNotifier::addEvent(mnClientId, aEventObject); + } +} + +void AccessibleSlideSorterObject::disposing(std::unique_lock&) +{ + // Send a disposing to all listeners. + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing(mnClientId, *this); + mnClientId = 0; + } +} + +//===== XAccessible =========================================================== + +Reference SAL_CALL + AccessibleSlideSorterObject::getAccessibleContext() +{ + ThrowIfDisposed(); + return this; +} + +//===== XAccessibleContext ==================================================== + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getAccessibleChildCount() +{ + ThrowIfDisposed(); + return 0; +} + +Reference SAL_CALL AccessibleSlideSorterObject::getAccessibleChild (sal_Int32 ) +{ + ThrowIfDisposed(); + throw lang::IndexOutOfBoundsException(); +} + +Reference SAL_CALL AccessibleSlideSorterObject::getAccessibleParent() +{ + ThrowIfDisposed(); + return mxParent; +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getAccessibleIndexInParent() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + sal_Int32 nIndexInParent(-1); + + if (mxParent.is()) + { + Reference xParentContext (mxParent->getAccessibleContext()); + if (xParentContext.is()) + { + sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); + for (sal_Int32 i=0; igetAccessibleChild(i).get() + == static_cast(this)) + { + nIndexInParent = i; + break; + } + } + } + + return nIndexInParent; +} + +sal_Int16 SAL_CALL AccessibleSlideSorterObject::getAccessibleRole() +{ + ThrowIfDisposed(); + return AccessibleRole::SHAPE; +} + +OUString SAL_CALL AccessibleSlideSorterObject::getAccessibleDescription() +{ + ThrowIfDisposed(); + return SdResId(STR_PAGE); +} + +OUString SAL_CALL AccessibleSlideSorterObject::getAccessibleName() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + SdPage* pPage = GetPage(); + if (pPage != nullptr) + return pPage->GetName(); + else + return OUString(); +} + +Reference SAL_CALL + AccessibleSlideSorterObject::getAccessibleRelationSet() +{ + ThrowIfDisposed(); + return Reference(); +} + +Reference SAL_CALL + AccessibleSlideSorterObject::getAccessibleStateSet() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper(); + + if (mxParent.is()) + { + // Unconditional states. + pStateSet->AddState(AccessibleStateType::SELECTABLE); + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::VISIBLE); + pStateSet->AddState(AccessibleStateType::SHOWING); + pStateSet->AddState(AccessibleStateType::ACTIVE); + pStateSet->AddState(AccessibleStateType::SENSITIVE); + + // Conditional states. + if (mrSlideSorter.GetController().GetPageSelector().IsPageSelected(mnPageNumber)) + pStateSet->AddState(AccessibleStateType::SELECTED); + if (mrSlideSorter.GetController().GetFocusManager().GetFocusedPageIndex() == mnPageNumber) + if (mrSlideSorter.GetController().GetFocusManager().IsFocusShowing()) + pStateSet->AddState(AccessibleStateType::FOCUSED); + } + + return pStateSet; +} + +lang::Locale SAL_CALL AccessibleSlideSorterObject::getLocale() +{ + ThrowIfDisposed(); + // Delegate request to parent. + if (mxParent.is()) + { + Reference xParentContext (mxParent->getAccessibleContext()); + if (xParentContext.is()) + return xParentContext->getLocale (); + } + + // No locale and no parent. Therefore throw exception to indicate this + // cluelessness. + throw IllegalAccessibleComponentStateException(); +} + +//===== XAccessibleEventBroadcaster =========================================== + +void SAL_CALL AccessibleSlideSorterObject::addAccessibleEventListener( + const Reference& rxListener) +{ + if (!rxListener.is()) + return; + + const std::unique_lock aGuard(m_aMutex); + + if (IsDisposed()) + { + uno::Reference x (static_cast(this), uno::UNO_QUERY); + rxListener->disposing (lang::EventObject (x)); + } + else + { + if (mnClientId == 0) + mnClientId = comphelper::AccessibleEventNotifier::registerClient(); + comphelper::AccessibleEventNotifier::addEventListener(mnClientId, rxListener); + } +} + +void SAL_CALL AccessibleSlideSorterObject::removeAccessibleEventListener( + const Reference& rxListener) +{ + ThrowIfDisposed(); + if (!(rxListener.is() && mnClientId)) + return; + + const std::unique_lock aGuard(m_aMutex); + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( mnClientId, rxListener ); + if ( !nListenerCount ) + { + // no listeners anymore + // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case somebody calls + // NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); + mnClientId = 0; + } +} + +//===== XAccessibleComponent ================================================== + +sal_Bool SAL_CALL AccessibleSlideSorterObject::containsPoint(const awt::Point& aPoint) +{ + ThrowIfDisposed(); + const awt::Size aSize (getSize()); + return (aPoint.X >= 0) + && (aPoint.X < aSize.Width) + && (aPoint.Y >= 0) + && (aPoint.Y < aSize.Height); +} + +Reference SAL_CALL + AccessibleSlideSorterObject::getAccessibleAtPoint(const awt::Point& ) +{ + return nullptr; +} + +awt::Rectangle SAL_CALL AccessibleSlideSorterObject::getBounds() +{ + ThrowIfDisposed (); + + const SolarMutexGuard aSolarGuard; + + ::tools::Rectangle aBBox ( + mrSlideSorter.GetView().GetLayouter().GetPageObjectLayouter()->GetBoundingBox( + mrSlideSorter.GetModel().GetPageDescriptor(mnPageNumber), + ::sd::slidesorter::view::PageObjectLayouter::Part::PageObject, + ::sd::slidesorter::view::PageObjectLayouter::WindowCoordinateSystem)); + + if (mxParent.is()) + { + Reference xParentComponent(mxParent->getAccessibleContext(), UNO_QUERY); + if (xParentComponent.is()) + { + awt::Rectangle aParentBBox (xParentComponent->getBounds()); + aBBox.Intersection(::tools::Rectangle( + aParentBBox.X, + aParentBBox.Y, + aParentBBox.Width, + aParentBBox.Height)); + } + } + + return awt::Rectangle( + aBBox.Left(), + aBBox.Top(), + aBBox.GetWidth(), + aBBox.GetHeight()); +} + +awt::Point SAL_CALL AccessibleSlideSorterObject::getLocation () +{ + ThrowIfDisposed (); + const awt::Rectangle aBBox (getBounds()); + return awt::Point(aBBox.X, aBBox.Y); +} + +awt::Point SAL_CALL AccessibleSlideSorterObject::getLocationOnScreen() +{ + ThrowIfDisposed (); + + const SolarMutexGuard aSolarGuard; + + awt::Point aLocation (getLocation()); + + if (mxParent.is()) + { + Reference xParentComponent(mxParent->getAccessibleContext(),UNO_QUERY); + if (xParentComponent.is()) + { + const awt::Point aParentLocationOnScreen(xParentComponent->getLocationOnScreen()); + aLocation.X += aParentLocationOnScreen.X; + aLocation.Y += aParentLocationOnScreen.Y; + } + } + + return aLocation; +} + +awt::Size SAL_CALL AccessibleSlideSorterObject::getSize() +{ + ThrowIfDisposed (); + const awt::Rectangle aBBox (getBounds()); + return awt::Size(aBBox.Width,aBBox.Height); +} + +void SAL_CALL AccessibleSlideSorterObject::grabFocus() +{ + // nothing to do +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getForeground() +{ + ThrowIfDisposed (); + svtools::ColorConfig aColorConfig; + Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast(nColor); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterObject::getBackground() +{ + ThrowIfDisposed (); + Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + return sal_Int32(nColor); +} + +// XServiceInfo +OUString SAL_CALL + AccessibleSlideSorterObject::getImplementationName() +{ + return "AccessibleSlideSorterObject"; +} + +sal_Bool SAL_CALL AccessibleSlideSorterObject::supportsService (const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString> SAL_CALL + AccessibleSlideSorterObject::getSupportedServiceNames() +{ + ThrowIfDisposed (); + + return uno::Sequence { + OUString("com.sun.star.accessibility.Accessible"), + OUString("com.sun.star.accessibility.AccessibleContext") + }; +} + +void AccessibleSlideSorterObject::ThrowIfDisposed() +{ + if (m_bDisposed) + { + SAL_WARN("sd", "Calling disposed object. Throwing exception:"); + throw lang::DisposedException ("object has been already disposed", + static_cast(this)); + } +} + +bool AccessibleSlideSorterObject::IsDisposed() const +{ + return m_bDisposed; +} + +SdPage* AccessibleSlideSorterObject::GetPage() const +{ + ::sd::slidesorter::model::SharedPageDescriptor pDescriptor( + mrSlideSorter.GetModel().GetPageDescriptor(mnPageNumber)); + if (pDescriptor) + return pDescriptor->GetPage(); + else + return nullptr; +} + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx b/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx new file mode 100644 index 000000000..87eea89d2 --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleSlideSorterView.cxx @@ -0,0 +1,950 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace accessibility { + +/** Inner implementation class of the AccessibleSlideSorterView. + + Note that some event broadcasting is done asynchronously because + otherwise it could lead to deadlocks on (at least) some Solaris + machines. Probably (but unverified) this can happen on all GTK based + systems. The asynchronous broadcasting is just a workaround for a + poorly understood problem. +*/ +class AccessibleSlideSorterView::Implementation + : public SfxListener +{ +public: + Implementation ( + AccessibleSlideSorterView& rAccessibleSlideSorter, + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pWindow); + virtual ~Implementation() override; + + void RequestUpdateChildren(); + void Clear(); + sal_Int32 GetVisibleChildCount() const; + AccessibleSlideSorterObject* GetAccessibleChild (sal_Int32 nIndex); + AccessibleSlideSorterObject* GetVisibleChild (sal_Int32 nIndex); + + void ConnectListeners(); + void ReleaseListeners(); + void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; + DECL_LINK(WindowEventListener, VclWindowEvent&, void); + DECL_LINK(SelectionChangeListener, LinkParamNone*, void); + DECL_LINK(BroadcastSelectionChange, void*, void); + DECL_LINK(FocusChangeListener, LinkParamNone*, void); + DECL_LINK(VisibilityChangeListener, LinkParamNone*, void); + DECL_LINK(UpdateChildrenCallback, void*, void); + + void Activated(); +private: + AccessibleSlideSorterView& mrAccessibleSlideSorter; + ::sd::slidesorter::SlideSorter& mrSlideSorter; + typedef ::std::vector > PageObjectList; + PageObjectList maPageObjects; + sal_Int32 mnFirstVisibleChild; + sal_Int32 mnLastVisibleChild; + bool mbListeningToDocument; + VclPtr mpWindow; + sal_Int32 mnFocusedIndex; + bool mbModelChangeLocked; + ImplSVEvent * mnUpdateChildrenUserEventId; + ImplSVEvent * mnSelectionChangeUserEventId; + + void UpdateChildren(); +}; + +//===== AccessibleSlideSorterView ============================================= + +AccessibleSlideSorterView::AccessibleSlideSorterView( + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pContentWindow) + : AccessibleSlideSorterViewBase(m_aMutex), + mrSlideSorter(rSlideSorter), + mnClientId(0), + mpContentWindow(pContentWindow) +{ +} + +void AccessibleSlideSorterView::Init() +{ + mpImpl.reset(new Implementation(*this,mrSlideSorter,mpContentWindow)); +} + +AccessibleSlideSorterView::~AccessibleSlideSorterView() +{ + Destroyed (); +} + +void AccessibleSlideSorterView::FireAccessibleEvent ( + short nEventId, + const uno::Any& rOldValue, + const uno::Any& rNewValue ) +{ + if (mnClientId != 0) + { + AccessibleEventObject aEventObject; + + aEventObject.Source = Reference(this); + aEventObject.EventId = nEventId; + aEventObject.NewValue = rNewValue; + aEventObject.OldValue = rOldValue; + + comphelper::AccessibleEventNotifier::addEvent (mnClientId, aEventObject); + } +} + +void SAL_CALL AccessibleSlideSorterView::disposing() +{ + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } + mpImpl.reset(); +} + +AccessibleSlideSorterObject* AccessibleSlideSorterView::GetAccessibleChildImplementation ( + sal_Int32 nIndex) +{ + AccessibleSlideSorterObject* pResult = nullptr; + ::osl::MutexGuard aGuard (m_aMutex); + + if (nIndex>=0 && nIndexGetVisibleChildCount()) + pResult = mpImpl->GetVisibleChild(nIndex); + + return pResult; +} + +void AccessibleSlideSorterView::Destroyed() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + // Send a disposing to all listeners. + if (mnClientId != 0) + { + comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( mnClientId, *this ); + mnClientId = 0; + } +} + +//===== XAccessible ========================================================= + +Reference SAL_CALL + AccessibleSlideSorterView::getAccessibleContext() +{ + ThrowIfDisposed (); + return this; +} + +//===== XAccessibleContext ================================================== + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getAccessibleChildCount() +{ + ThrowIfDisposed(); + ::osl::MutexGuard aGuard (m_aMutex); + return mpImpl->GetVisibleChildCount(); +} + +Reference SAL_CALL + AccessibleSlideSorterView::getAccessibleChild (sal_Int32 nIndex) +{ + ThrowIfDisposed(); + ::osl::MutexGuard aGuard (m_aMutex); + + if (nIndex<0 || nIndex>=mpImpl->GetVisibleChildCount()) + throw lang::IndexOutOfBoundsException(); + + return mpImpl->GetVisibleChild(nIndex); +} + +Reference SAL_CALL AccessibleSlideSorterView::getAccessibleParent() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + Reference xParent; + + if (mpContentWindow != nullptr) + { + vcl::Window* pParent = mpContentWindow->GetAccessibleParentWindow(); + if (pParent != nullptr) + xParent = pParent->GetAccessible(); + } + + return xParent; +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getAccessibleIndexInParent() +{ + OSL_ASSERT(getAccessibleParent().is()); + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + sal_Int32 nIndexInParent(-1); + + Reference xParentContext (getAccessibleParent()->getAccessibleContext()); + if (xParentContext.is()) + { + sal_Int32 nChildCount (xParentContext->getAccessibleChildCount()); + for (sal_Int32 i=0; igetAccessibleChild(i).get() + == static_cast(this)) + { + nIndexInParent = i; + break; + } + } + + return nIndexInParent; +} + +sal_Int16 SAL_CALL AccessibleSlideSorterView::getAccessibleRole() +{ + ThrowIfDisposed(); + return AccessibleRole::DOCUMENT; +} + +OUString SAL_CALL AccessibleSlideSorterView::getAccessibleDescription() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + return SdResId(SID_SD_A11Y_I_SLIDEVIEW_D); +} + +OUString SAL_CALL AccessibleSlideSorterView::getAccessibleName() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + return SdResId(SID_SD_A11Y_I_SLIDEVIEW_N); +} + +Reference SAL_CALL + AccessibleSlideSorterView::getAccessibleRelationSet() +{ + return Reference(); +} + +Reference SAL_CALL + AccessibleSlideSorterView::getAccessibleStateSet() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet = new ::utl::AccessibleStateSetHelper(); + + pStateSet->AddState(AccessibleStateType::FOCUSABLE); + pStateSet->AddState(AccessibleStateType::SELECTABLE); + pStateSet->AddState(AccessibleStateType::ENABLED); + pStateSet->AddState(AccessibleStateType::ACTIVE); + pStateSet->AddState(AccessibleStateType::MULTI_SELECTABLE); + pStateSet->AddState(AccessibleStateType::OPAQUE); + if (mpContentWindow!=nullptr) + { + if (mpContentWindow->IsVisible()) + pStateSet->AddState(AccessibleStateType::VISIBLE); + if (mpContentWindow->IsReallyVisible()) + pStateSet->AddState(AccessibleStateType::SHOWING); + } + + return pStateSet; +} + +lang::Locale SAL_CALL AccessibleSlideSorterView::getLocale() +{ + ThrowIfDisposed (); + Reference xParentContext; + Reference xParent (getAccessibleParent()); + if (xParent.is()) + xParentContext = xParent->getAccessibleContext(); + + if (xParentContext.is()) + return xParentContext->getLocale(); + else + // Strange, no parent! Anyway, return the default locale. + return Application::GetSettings().GetLanguageTag().getLocale(); +} + +void SAL_CALL AccessibleSlideSorterView::addAccessibleEventListener( + const Reference& rxListener) +{ + if (!rxListener.is()) + return; + + const osl::MutexGuard aGuard(m_aMutex); + + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + uno::Reference x (static_cast(this), uno::UNO_QUERY); + rxListener->disposing (lang::EventObject (x)); + } + else + { + if ( ! mnClientId) + mnClientId = comphelper::AccessibleEventNotifier::registerClient(); + comphelper::AccessibleEventNotifier::addEventListener(mnClientId, rxListener); + } +} + +void SAL_CALL AccessibleSlideSorterView::removeAccessibleEventListener( + const Reference& rxListener) +{ + ThrowIfDisposed(); + if (!rxListener.is()) + return; + + const osl::MutexGuard aGuard(m_aMutex); + + if (mnClientId == 0) + return; + + sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( + mnClientId, rxListener ); + if ( !nListenerCount ) + { + // no listeners anymore -> revoke ourself. This may lead to + // the notifier thread dying (if we were the last client), + // and at least to us not firing any events anymore, in case + // somebody calls NotifyAccessibleEvent, again + comphelper::AccessibleEventNotifier::revokeClient( mnClientId ); + mnClientId = 0; + } +} + +//===== XAccessibleComponent ================================================== + +sal_Bool SAL_CALL AccessibleSlideSorterView::containsPoint (const awt::Point& aPoint) +{ + ThrowIfDisposed(); + const awt::Rectangle aBBox (getBounds()); + return (aPoint.X >= 0) + && (aPoint.X < aBBox.Width) + && (aPoint.Y >= 0) + && (aPoint.Y < aBBox.Height); +} + +Reference SAL_CALL + AccessibleSlideSorterView::getAccessibleAtPoint (const awt::Point& aPoint) +{ + ThrowIfDisposed(); + Reference xAccessible; + const SolarMutexGuard aSolarGuard; + + const Point aTestPoint (aPoint.X, aPoint.Y); + ::sd::slidesorter::model::SharedPageDescriptor pHitDescriptor ( + mrSlideSorter.GetController().GetPageAt(aTestPoint)); + if (pHitDescriptor) + xAccessible = mpImpl->GetAccessibleChild( + (pHitDescriptor->GetPage()->GetPageNum()-1)/2); + + return xAccessible; +} + +awt::Rectangle SAL_CALL AccessibleSlideSorterView::getBounds() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + awt::Rectangle aBBox; + + if (mpContentWindow != nullptr) + { + const Point aPosition (mpContentWindow->GetPosPixel()); + const Size aSize (mpContentWindow->GetOutputSizePixel()); + + aBBox.X = aPosition.X(); + aBBox.Y = aPosition.Y(); + aBBox.Width = aSize.Width(); + aBBox.Height = aSize.Height(); + } + + return aBBox; +} + +awt::Point SAL_CALL AccessibleSlideSorterView::getLocation() +{ + ThrowIfDisposed(); + awt::Point aLocation; + + if (mpContentWindow != nullptr) + { + const Point aPosition (mpContentWindow->GetPosPixel()); + aLocation.X = aPosition.X(); + aLocation.Y = aPosition.Y(); + } + + return aLocation; +} + +/** Calculate the location on screen from the parent's location on screen + and our own relative location. +*/ +awt::Point SAL_CALL AccessibleSlideSorterView::getLocationOnScreen() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + awt::Point aParentLocationOnScreen; + + Reference xParent (getAccessibleParent()); + if (xParent.is()) + { + Reference xParentComponent ( + xParent->getAccessibleContext(), uno::UNO_QUERY); + if (xParentComponent.is()) + aParentLocationOnScreen = xParentComponent->getLocationOnScreen(); + } + + awt::Point aLocationOnScreen (getLocation()); + aLocationOnScreen.X += aParentLocationOnScreen.X; + aLocationOnScreen.Y += aParentLocationOnScreen.Y; + + return aLocationOnScreen; +} + +awt::Size SAL_CALL AccessibleSlideSorterView::getSize() +{ + ThrowIfDisposed(); + awt::Size aSize; + + if (mpContentWindow != nullptr) + { + const Size aOutputSize (mpContentWindow->GetOutputSizePixel()); + aSize.Width = aOutputSize.Width(); + aSize.Height = aOutputSize.Height(); + } + + return aSize; +} + +void SAL_CALL AccessibleSlideSorterView::grabFocus() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + if (mpContentWindow) + mpContentWindow->GrabFocus(); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getForeground() +{ + ThrowIfDisposed(); + svtools::ColorConfig aColorConfig; + Color nColor = aColorConfig.GetColorValue( svtools::FONTCOLOR ).nColor; + return static_cast(nColor); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getBackground() +{ + ThrowIfDisposed(); + Color nColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + return sal_Int32(nColor); +} + +//===== XAccessibleSelection ================================================== + +void SAL_CALL AccessibleSlideSorterView::selectAccessibleChild (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + mrSlideSorter.GetController().GetPageSelector().SelectPage(pChild->GetPageNumber()); +} + +sal_Bool SAL_CALL AccessibleSlideSorterView::isAccessibleChildSelected (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + bool bIsSelected = false; + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + bIsSelected = mrSlideSorter.GetController().GetPageSelector().IsPageSelected( + pChild->GetPageNumber()); + + return bIsSelected; +} + +void SAL_CALL AccessibleSlideSorterView::clearAccessibleSelection() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); +} + +void SAL_CALL AccessibleSlideSorterView::selectAllAccessibleChildren() +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + mrSlideSorter.GetController().GetPageSelector().SelectAllPages(); +} + +sal_Int32 SAL_CALL AccessibleSlideSorterView::getSelectedAccessibleChildCount() +{ + ThrowIfDisposed (); + const SolarMutexGuard aSolarGuard; + return mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount(); +} + +Reference SAL_CALL + AccessibleSlideSorterView::getSelectedAccessibleChild (sal_Int32 nSelectedChildIndex ) +{ + ThrowIfDisposed (); + const SolarMutexGuard aSolarGuard; + Reference xChild; + + ::sd::slidesorter::controller::PageSelector& rSelector ( + mrSlideSorter.GetController().GetPageSelector()); + sal_Int32 nPageCount(rSelector.GetPageCount()); + sal_Int32 nSelectedCount = 0; + for (sal_Int32 i=0; iGetAccessibleChild(i); + break; + } + ++nSelectedCount; + } + + if ( ! xChild.is() ) + throw lang::IndexOutOfBoundsException(); + + return xChild; +} + +void SAL_CALL AccessibleSlideSorterView::deselectAccessibleChild (sal_Int32 nChildIndex) +{ + ThrowIfDisposed(); + const SolarMutexGuard aSolarGuard; + + AccessibleSlideSorterObject* pChild = mpImpl->GetAccessibleChild(nChildIndex); + if (pChild == nullptr) + throw lang::IndexOutOfBoundsException(); + + mrSlideSorter.GetController().GetPageSelector().DeselectPage(pChild->GetPageNumber()); +} + +// XServiceInfo +OUString SAL_CALL + AccessibleSlideSorterView::getImplementationName() +{ + return "AccessibleSlideSorterView"; +} + +sal_Bool SAL_CALL AccessibleSlideSorterView::supportsService (const OUString& sServiceName) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString> SAL_CALL + AccessibleSlideSorterView::getSupportedServiceNames() +{ + ThrowIfDisposed (); + + return uno::Sequence { + OUString("com.sun.star.accessibility.Accessible"), + OUString("com.sun.star.accessibility.AccessibleContext"), + OUString("com.sun.star.drawing.AccessibleSlideSorterView") + }; +} + +void AccessibleSlideSorterView::ThrowIfDisposed() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + SAL_WARN("sd", "Calling disposed object. Throwing exception:"); + throw lang::DisposedException ("object has been already disposed", + static_cast(this)); + } +} + +//===== AccessibleSlideSorterView::Implementation ============================= + +AccessibleSlideSorterView::Implementation::Implementation ( + AccessibleSlideSorterView& rAccessibleSlideSorter, + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pWindow) + : mrAccessibleSlideSorter(rAccessibleSlideSorter), + mrSlideSorter(rSlideSorter), + mnFirstVisibleChild(0), + mnLastVisibleChild(-1), + mbListeningToDocument(false), + mpWindow(pWindow), + mnFocusedIndex(-1), + mbModelChangeLocked(false), + mnUpdateChildrenUserEventId(nullptr), + mnSelectionChangeUserEventId(nullptr) +{ + ConnectListeners(); + UpdateChildren(); +} + +AccessibleSlideSorterView::Implementation::~Implementation() +{ + if (mnUpdateChildrenUserEventId != nullptr) + Application::RemoveUserEvent(mnUpdateChildrenUserEventId); + if (mnSelectionChangeUserEventId != nullptr) + Application::RemoveUserEvent(mnSelectionChangeUserEventId); + ReleaseListeners(); + Clear(); +} + +void AccessibleSlideSorterView::Implementation::RequestUpdateChildren() +{ + if (mnUpdateChildrenUserEventId == nullptr) + mnUpdateChildrenUserEventId = Application::PostUserEvent( + LINK(this, AccessibleSlideSorterView::Implementation, + UpdateChildrenCallback)); +} + +void AccessibleSlideSorterView::Implementation::UpdateChildren() +{ + //By default, all children should be accessible. So here workaround is to make all children visible. + // MT: This was in UpdateVisibility, which has some similarity, and hg merge automatically has put it here. Correct?! + // In the IA2 CWS, also setting mnFirst/LastVisibleChild was commented out! + mnLastVisibleChild = maPageObjects.size(); + + if (mbModelChangeLocked) + { + // Do nothing right now. When the flag is reset, this method is + // called again. + return; + } + + const Range aRange (mrSlideSorter.GetView().GetVisiblePageRange()); + mnFirstVisibleChild = aRange.Min(); + mnLastVisibleChild = aRange.Max(); + + // Release all children. + Clear(); + + // Create new children for the modified visible range. + maPageObjects.resize(mrSlideSorter.GetModel().GetPageCount()); + + // No Visible children + if (mnFirstVisibleChild == -1 && mnLastVisibleChild == -1) + return; + + for (sal_Int32 nIndex(mnFirstVisibleChild); nIndex<=mnLastVisibleChild; ++nIndex) + GetAccessibleChild(nIndex); +} + +void AccessibleSlideSorterView::Implementation::Clear() +{ + for (auto& rxPageObject : maPageObjects) + if (rxPageObject != nullptr) + { + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::CHILD, + Any(Reference(rxPageObject)), + Any()); + + Reference xComponent (Reference(rxPageObject), UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + rxPageObject = nullptr; + } + maPageObjects.clear(); +} + +sal_Int32 AccessibleSlideSorterView::Implementation::GetVisibleChildCount() const +{ + if (mnFirstVisibleChild<=mnLastVisibleChild && mnFirstVisibleChild>=0) + return mnLastVisibleChild - mnFirstVisibleChild + 1; + else + return 0; +} + +AccessibleSlideSorterObject* AccessibleSlideSorterView::Implementation::GetVisibleChild ( + sal_Int32 nIndex) +{ + assert(nIndex>=0 && nIndex=0 && o3tl::make_unsigned(nIndex)GetPage()->GetPageNum()-1)/2); + + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::CHILD, + Any(), + Any(Reference(maPageObjects[nIndex]))); + } + + } + + pChild = maPageObjects[nIndex].get(); + } + else + { + OSL_ASSERT(nIndex>=0 && o3tl::make_unsigned(nIndex)AddEventListener( + LINK(this,AccessibleSlideSorterView::Implementation,WindowEventListener)); + + mrSlideSorter.GetController().GetSelectionManager()->AddSelectionChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,SelectionChangeListener)); + mrSlideSorter.GetController().GetFocusManager().AddFocusChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,FocusChangeListener)); + mrSlideSorter.GetView().AddVisibilityChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,VisibilityChangeListener)); +} + +void AccessibleSlideSorterView::Implementation::ReleaseListeners() +{ + mrSlideSorter.GetController().GetFocusManager().RemoveFocusChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,FocusChangeListener)); + mrSlideSorter.GetController().GetSelectionManager()->RemoveSelectionChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,SelectionChangeListener)); + mrSlideSorter.GetView().RemoveVisibilityChangeListener( + LINK(this,AccessibleSlideSorterView::Implementation,VisibilityChangeListener)); + + if (mpWindow != nullptr) + mpWindow->RemoveEventListener( + LINK(this,AccessibleSlideSorterView::Implementation,WindowEventListener)); + + if (mbListeningToDocument) + { + if (mrSlideSorter.GetViewShell() != nullptr && !IsListening(*mrSlideSorter.GetViewShell())) + { // ??? is it even possible that ConnectListeners is called with no + // view shell and this one with a view shell? + StartListening(*mrSlideSorter.GetViewShell()); + } + EndListening (*mrSlideSorter.GetModel().GetDocument()); + mbListeningToDocument = false; + } +} + +void AccessibleSlideSorterView::Implementation::Notify ( + SfxBroadcaster&, + const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast(&rHint); + switch (pSdrHint->GetKind()) + { + case SdrHintKind::PageOrderChange: + RequestUpdateChildren(); + break; + default: + break; + } + } + else if (auto pViewShellHint = dynamic_cast(&rHint)) + { + switch (pViewShellHint->GetHintId()) + { + case sd::ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START: + mbModelChangeLocked = true; + break; + + case sd::ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END: + mbModelChangeLocked = false; + RequestUpdateChildren(); + break; + default: + break; + } + } +} + +void AccessibleSlideSorterView::SwitchViewActivated() +{ + // Firstly, set focus to view + FireAccessibleEvent(AccessibleEventId::STATE_CHANGED, + Any(), + Any(AccessibleStateType::FOCUSED)); + + mpImpl->Activated(); +} + +void AccessibleSlideSorterView::Implementation::Activated() +{ + mrSlideSorter.GetController().GetFocusManager().ShowFocus(); + +} + +IMPL_LINK(AccessibleSlideSorterView::Implementation, WindowEventListener, VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowMove: + case VclEventId::WindowResize: + RequestUpdateChildren(); + break; + + case VclEventId::WindowGetFocus: + case VclEventId::WindowLoseFocus: + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::SELECTION_CHANGED, + Any(), + Any()); + break; + default: + break; + } +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, SelectionChangeListener, LinkParamNone*, void) +{ + if (mnSelectionChangeUserEventId == nullptr) + mnSelectionChangeUserEventId = Application::PostUserEvent( + LINK(this, AccessibleSlideSorterView::Implementation, BroadcastSelectionChange)); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, BroadcastSelectionChange, void*, void) +{ + mnSelectionChangeUserEventId = nullptr; + mrAccessibleSlideSorter.FireAccessibleEvent( + AccessibleEventId::SELECTION_CHANGED, + Any(), + Any()); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, FocusChangeListener, LinkParamNone*, void) +{ + sal_Int32 nNewFocusedIndex ( + mrSlideSorter.GetController().GetFocusManager().GetFocusedPageIndex()); + + bool bHasFocus = mrSlideSorter.GetController().GetFocusManager().IsFocusShowing(); + if (!bHasFocus) + nNewFocusedIndex = -1; + + // add a checker whether the focus event is sent out. Only after sent, the mnFocusedIndex should be updated. + bool bSentFocus = false; + if (nNewFocusedIndex == mnFocusedIndex) + return; + + if (mnFocusedIndex >= 0) + { + AccessibleSlideSorterObject* pObject = GetAccessibleChild(mnFocusedIndex); + if (pObject != nullptr) + { + pObject->FireAccessibleEvent( + AccessibleEventId::STATE_CHANGED, + Any(AccessibleStateType::FOCUSED), + Any()); + bSentFocus = true; + } + } + if (nNewFocusedIndex >= 0) + { + AccessibleSlideSorterObject* pObject = GetAccessibleChild(nNewFocusedIndex); + if (pObject != nullptr) + { + pObject->FireAccessibleEvent( + AccessibleEventId::STATE_CHANGED, + Any(), + Any(AccessibleStateType::FOCUSED)); + bSentFocus = true; + } + } + if (bSentFocus) + mnFocusedIndex = nNewFocusedIndex; +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, UpdateChildrenCallback, void*, void) +{ + mnUpdateChildrenUserEventId = nullptr; + UpdateChildren(); +} + +IMPL_LINK_NOARG(AccessibleSlideSorterView::Implementation, VisibilityChangeListener, LinkParamNone*, void) +{ + UpdateChildren(); +} + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/AccessibleViewForwarder.cxx b/sd/source/ui/accessibility/AccessibleViewForwarder.cxx new file mode 100644 index 000000000..09225e27f --- /dev/null +++ b/sd/source/ui/accessibility/AccessibleViewForwarder.cxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +namespace accessibility +{ +/** For the time being, the implementation of this class will not use the + member mrDevice. Instead the device is retrieved from the view + every time it is used. This is necessary because the device has to stay + up-to-date with the current view and the class has to stay compatible. + May change in the future. +*/ + +AccessibleViewForwarder::AccessibleViewForwarder(SdrPaintView* pView, const OutputDevice& rDevice) + : mpView(pView) + , mnWindowId(0) +{ + // Search the output device to determine its id. + for (sal_uInt32 a(0); a < mpView->PaintWindowCount(); a++) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(a); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + + if (&rOutDev == &rDevice) + { + mnWindowId = static_cast(a); + break; + } + } +} + +AccessibleViewForwarder::~AccessibleViewForwarder() +{ + // empty +} + +::tools::Rectangle AccessibleViewForwarder::GetVisibleArea() const +{ + ::tools::Rectangle aVisibleArea; + + if (static_cast(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast(mnWindowId)); + aVisibleArea = pPaintWindow->GetVisibleArea(); + } + + return aVisibleArea; +} + +/** Transform the given point into pixel coordinates. After the pixel + coordinates of the window origin are added to make the point coordinates + absolute. +*/ +Point AccessibleViewForwarder::LogicToPixel(const Point& rPoint) const +{ + OSL_ASSERT(mpView != nullptr); + if (static_cast(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast(mnWindowId)); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + ::tools::Rectangle aBBox(rOutDev.GetOwnerWindow()->GetWindowExtentsRelative(nullptr)); + return rOutDev.LogicToPixel(rPoint) + aBBox.TopLeft(); + } + else + return Point(); +} + +Size AccessibleViewForwarder::LogicToPixel(const Size& rSize) const +{ + OSL_ASSERT(mpView != nullptr); + if (static_cast(mnWindowId) < mpView->PaintWindowCount()) + { + SdrPaintWindow* pPaintWindow = mpView->GetPaintWindow(static_cast(mnWindowId)); + OutputDevice& rOutDev = pPaintWindow->GetOutputDevice(); + return rOutDev.LogicToPixel(rSize); + } + else + return Size(); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/accessibility/SdShapeTypes.cxx b/sd/source/ui/accessibility/SdShapeTypes.cxx new file mode 100644 index 000000000..7fab0961e --- /dev/null +++ b/sd/source/ui/accessibility/SdShapeTypes.cxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +namespace accessibility { + +static rtl::Reference + CreateSdAccessibleShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo, + ShapeTypeId nId) +{ + switch (nId) + { + case PRESENTATION_TITLE: + case PRESENTATION_OUTLINER: + case PRESENTATION_SUBTITLE: + case PRESENTATION_PAGE: + case PRESENTATION_NOTES: + case PRESENTATION_HANDOUT: + case PRESENTATION_HEADER: + case PRESENTATION_FOOTER: + case PRESENTATION_DATETIME: + case PRESENTATION_PAGENUMBER: + return new AccessiblePresentationShape (rShapeInfo, rShapeTreeInfo); + + case PRESENTATION_GRAPHIC_OBJECT: + return new AccessiblePresentationGraphicShape (rShapeInfo, rShapeTreeInfo); + + case PRESENTATION_OLE: + case PRESENTATION_CHART: + case PRESENTATION_TABLE: + return new AccessiblePresentationOLEShape (rShapeInfo, rShapeTreeInfo); + + default: + return new AccessibleShape (rShapeInfo, rShapeTreeInfo); + } +} + +void RegisterImpressShapeTypes() +{ + /** List of shape type descriptors corresponding to the + SdShapeTypes enum. + */ + ShapeTypeDescriptor aSdShapeTypeList[] = { + ShapeTypeDescriptor ( + PRESENTATION_OUTLINER, + "com.sun.star.presentation.OutlinerShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_SUBTITLE, + "com.sun.star.presentation.SubtitleShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_GRAPHIC_OBJECT, + "com.sun.star.presentation.GraphicObjectShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_PAGE, + "com.sun.star.presentation.PageShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_OLE, + "com.sun.star.presentation.OLE2Shape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_CHART, + "com.sun.star.presentation.ChartShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_TABLE, + "com.sun.star.presentation.TableShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_NOTES, + "com.sun.star.presentation.NotesShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_TITLE, + "com.sun.star.presentation.TitleTextShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_HANDOUT, + "com.sun.star.presentation.HandoutShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_HEADER, + "com.sun.star.presentation.HeaderShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_FOOTER, + "com.sun.star.presentation.FooterShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_DATETIME, + "com.sun.star.presentation.DateTimeShape", + CreateSdAccessibleShape ), + ShapeTypeDescriptor ( + PRESENTATION_PAGENUMBER, + "com.sun.star.presentation.SlideNumberShape", + CreateSdAccessibleShape ) + }; + + ShapeTypeHandler::Instance().AddShapeTypeList ( + PRESENTATION_PAGENUMBER - PRESENTATION_OUTLINER + 1, + aSdShapeTypeList); +} + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/CustomAnimationDialog.cxx b/sd/source/ui/animations/CustomAnimationDialog.cxx new file mode 100644 index 000000000..7490a62c5 --- /dev/null +++ b/sd/source/ui/animations/CustomAnimationDialog.cxx @@ -0,0 +1,2090 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "CustomAnimationDialog.hxx" +#include +#include "STLPropertySet.hxx" +#include + +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::beans::XPropertySet; + +namespace sd { + +SdPropertySubControl::SdPropertySubControl(weld::Container* pParent) + : mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/customanimationfragment.ui", + false, reinterpret_cast(SfxViewShell::Current()))) + , mxContainer(mxBuilder->weld_container("EffectFragment")) + , mpParent(pParent) +{ +} + +SdPropertySubControl::~SdPropertySubControl() +{ + mpParent->move(mxContainer.get(), nullptr); +} + +namespace { + +class SdPresetPropertyBox : public SdPropertySubControl +{ +public: + SdPresetPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const OUString& aPresetId, const Link& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& rPresetId ) override; + +private: + std::vector maPropertyValues; + Link maModifyLink; + std::unique_ptr mxControl; + + DECL_LINK(OnSelect, weld::ComboBox&, void); +}; + +} + +SdPresetPropertyBox::SdPresetPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const OUString& aPresetId, const Link& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyLink(rModifyHdl) + , mxControl(mxBuilder->weld_combo_box("combo")) +{ + mxControl->connect_changed(LINK(this, SdPresetPropertyBox, OnSelect)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_PRESETPROPERTYBOX); + mxControl->show(); + pLabel->set_mnemonic_widget(mxControl.get()); + setValue(rValue, aPresetId); +} + +IMPL_LINK_NOARG(SdPresetPropertyBox, OnSelect, weld::ComboBox&, void) +{ + maModifyLink.Call(nullptr); +} + +void SdPresetPropertyBox::setValue( const Any& rValue, const OUString& rPresetId ) +{ + if (!mxControl) + return; + + mxControl->freeze(); + mxControl->clear(); + maPropertyValues.clear(); + int nPos = -1; + + const CustomAnimationPresets& rPresets = CustomAnimationPresets::getCustomAnimationPresets(); + CustomAnimationPresetPtr pDescriptor = rPresets.getEffectDescriptor( rPresetId ); + if( pDescriptor ) + { + + OUString aPropertyValue; + rValue >>= aPropertyValue; + + std::vector aSubTypes( pDescriptor->getSubTypes() ); + + mxControl->set_sensitive(!aSubTypes.empty()); + + for( const auto& aSubType : aSubTypes ) + { + mxControl->append_text(rPresets.getUINameForProperty(aSubType)); + maPropertyValues.push_back(aSubType); + if (aSubType == aPropertyValue) + nPos = maPropertyValues.size() - 1; + } + } + else + { + mxControl->set_sensitive(false); + } + mxControl->thaw(); + if (nPos != -1) + mxControl->set_active(nPos); +} + +Any SdPresetPropertyBox::getValue() +{ + const int nIndex = mxControl->get_active(); + if (nIndex == -1) + return Any(); + return Any(maPropertyValues[nIndex]); +} + +namespace { + +class SdColorPropertyBox : public SdPropertySubControl +{ +public: + SdColorPropertyBox(weld::Label* pLabel, weld::Container* pParent, weld::Window* pTopLevel, const Any& rValue, const Link& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& rPresetId ) override; + +private: + Link maModifyLink; + std::unique_ptr mxControl; + + DECL_LINK(OnSelect, ColorListBox&, void); +}; + +} + +SdColorPropertyBox::SdColorPropertyBox(weld::Label* pLabel, weld::Container* pParent, weld::Window* pTopLevel, const Any& rValue, const Link& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyLink(rModifyHdl) + , mxControl(new ColorListBox(mxBuilder->weld_menu_button("color"), [pTopLevel]{ return pTopLevel; })) +{ + mxControl->SetSelectHdl(LINK(this, SdColorPropertyBox, OnSelect)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_COLORPROPERTYBOX); + pLabel->set_mnemonic_widget(&mxControl->get_widget()); + mxControl->show(); + + Color nColor; + rValue >>= nColor; + mxControl->SelectEntry(nColor); +} + +IMPL_LINK_NOARG(SdColorPropertyBox, OnSelect, ColorListBox&, void) +{ + maModifyLink.Call(nullptr); +} + +void SdColorPropertyBox::setValue( const Any& rValue, const OUString& ) +{ + if (mxControl) + { + Color nColor; + rValue >>= nColor; + + mxControl->SetNoSelection(); + mxControl->SelectEntry(nColor); + } +} + +Any SdColorPropertyBox::getValue() +{ + return Any(sal_Int32(mxControl->GetSelectEntryColor().GetRGBColor())); +} + +namespace { + +class SdFontPropertyBox : public SdPropertySubControl +{ +public: + SdFontPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue(const Any& rValue, const OUString& rPresetId) override; + +private: + Link maModifyHdl; + std::unique_ptr mxControl; + + DECL_LINK(ControlSelectHdl, weld::ComboBox&, void); +}; + +} + +SdFontPropertyBox::SdFontPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl(rModifyHdl) + , mxControl(mxBuilder->weld_combo_box("fontname")) +{ + mxControl->connect_changed(LINK(this, SdFontPropertyBox, ControlSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_FONTPROPERTYBOX); + mxControl->show(); + pLabel->set_mnemonic_widget(mxControl.get()); + + const FontList* pFontList = nullptr; + bool bMustDelete = false; + + if (SfxObjectShell* pDocSh = SfxObjectShell::Current()) + { + auto pItem = pDocSh->GetItem( SID_ATTR_CHAR_FONTLIST ); + if (pItem) + pFontList = static_cast(pItem)->GetFontList(); + } + + if (!pFontList) + { + pFontList = new FontList(Application::GetDefaultDevice(), nullptr); + bMustDelete = true; + } + + mxControl->freeze(); + + sal_uInt16 nFontCount = pFontList->GetFontNameCount(); + for (sal_uInt16 i = 0; i < nFontCount; ++i) + { + const FontMetric& rFontMetric = pFontList->GetFontName(i); + mxControl->append_text(rFontMetric.GetFamilyName()); + } + + mxControl->thaw(); + + if( bMustDelete ) + delete pFontList; + + setValue( rValue, OUString() ); +} + +IMPL_LINK_NOARG(SdFontPropertyBox, ControlSelectHdl, weld::ComboBox&, void) +{ + maModifyHdl.Call(nullptr); +} + +void SdFontPropertyBox::setValue( const Any& rValue, const OUString& ) +{ + if (mxControl) + { + OUString aFontName; + rValue >>= aFontName; + mxControl->set_entry_text(aFontName); + } +} + +Any SdFontPropertyBox::getValue() +{ + OUString aFontName(mxControl->get_active_text()); + return Any(aFontName); +} + +namespace { + +class SdCharHeightPropertyBox : public SdPropertySubControl +{ +public: + SdCharHeightPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& ) override; + + DECL_LINK(implMenuSelectHdl, const OString& rIdent, void); + +private: + Link maModifyHdl; + std::unique_ptr mxMetric; + std::unique_ptr mxControl; + + DECL_LINK(EditModifyHdl, weld::MetricSpinButton&, void); +}; + +} + +SdCharHeightPropertyBox::SdCharHeightPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl(rModifyHdl) + , mxMetric(mxBuilder->weld_metric_spin_button("fontsize", FieldUnit::PERCENT)) + , mxControl(mxBuilder->weld_menu_button("fontsizemenu")) +{ + mxMetric->connect_value_changed(LINK(this, SdCharHeightPropertyBox, EditModifyHdl)); + mxMetric->set_help_id(HID_SD_CUSTOMANIMATIONPANE_CHARHEIGHTPROPERTYBOX); + mxMetric->show(); + pLabel->set_mnemonic_widget(&mxMetric->get_widget()); + + mxControl->connect_selected(LINK(this, SdCharHeightPropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_CHARHEIGHTPROPERTYBOX); + mxControl->show(); + + setValue(rValue, OUString()); +} + +IMPL_LINK_NOARG(SdCharHeightPropertyBox, EditModifyHdl, weld::MetricSpinButton&, void) +{ + maModifyHdl.Call(nullptr); +} + +IMPL_LINK(SdCharHeightPropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + sal_Int32 nValue = rIdent.toInt32(); + mxMetric->set_value(nValue, FieldUnit::PERCENT); + EditModifyHdl(*mxMetric); +} + +void SdCharHeightPropertyBox::setValue( const Any& rValue, const OUString& ) +{ + if (mxMetric) + { + double fValue = 0.0; + rValue >>= fValue; + mxMetric->set_value(static_cast<::tools::Long>(fValue * 100.0), FieldUnit::PERCENT); + } +} + +Any SdCharHeightPropertyBox::getValue() +{ + return Any(static_cast(mxMetric->get_value(FieldUnit::PERCENT)) / 100.0); +} + +namespace { + +class SdTransparencyPropertyBox : public SdPropertySubControl +{ +public: + SdTransparencyPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& rPresetId ) override; + + DECL_LINK(implMenuSelectHdl, const OString&, void); + DECL_LINK(implModifyHdl, weld::MetricSpinButton&, void); + + void updateMenu(); + +private: + Link maModifyHdl; + + std::unique_ptr mxMetric; + std::unique_ptr mxControl; +}; + +} + +SdTransparencyPropertyBox::SdTransparencyPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl(rModifyHdl) + , mxMetric(mxBuilder->weld_metric_spin_button("transparent", FieldUnit::PERCENT)) + , mxControl(mxBuilder->weld_menu_button("transparentmenu")) +{ + for (sal_Int32 i = 25; i < 101; i += 25) + { + OUString aStr(unicode::formatPercent(i, + Application::GetSettings().GetUILanguageTag())); + mxControl->append_item_check(OUString::number(i), aStr); + } + + mxControl->connect_selected(LINK(this, SdTransparencyPropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_TRANSPARENCYPROPERTYBOX); + mxControl->show(); + + mxMetric->connect_value_changed(LINK(this, SdTransparencyPropertyBox, implModifyHdl)); + mxMetric->set_help_id(HID_SD_CUSTOMANIMATIONPANE_TRANSPARENCYPROPERTYBOX); + mxMetric->show(); + pLabel->set_mnemonic_widget(&mxMetric->get_widget()); + + setValue(rValue, OUString()); +} + +void SdTransparencyPropertyBox::updateMenu() +{ + sal_Int64 nValue = mxMetric->get_value(FieldUnit::PERCENT); + for (sal_uInt16 i = 25; i < 101; i += 25) + mxControl->set_item_active(OString::number(i), nValue == i); +} + +IMPL_LINK_NOARG(SdTransparencyPropertyBox, implModifyHdl, weld::MetricSpinButton&, void) +{ + updateMenu(); + maModifyHdl.Call(nullptr); +} + +IMPL_LINK(SdTransparencyPropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + auto nValue = rIdent.toInt32(); + if (nValue != mxMetric->get_value(FieldUnit::PERCENT)) + { + mxMetric->set_value(nValue, FieldUnit::PERCENT); + implModifyHdl(*mxMetric); + } +} + +void SdTransparencyPropertyBox::setValue(const Any& rValue, const OUString&) +{ + if (mxMetric) + { + double fValue = 0.0; + rValue >>= fValue; + ::tools::Long nValue = static_cast<::tools::Long>(fValue * 100); + mxMetric->set_value(nValue, FieldUnit::PERCENT); + updateMenu(); + } +} + +Any SdTransparencyPropertyBox::getValue() +{ + return Any(static_cast(mxMetric->get_value(FieldUnit::PERCENT)) / 100.0); +} + +namespace { + +class SdRotationPropertyBox : public SdPropertySubControl +{ +public: + SdRotationPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& ) override; + + DECL_LINK(implMenuSelectHdl, const OString&, void); + DECL_LINK(implModifyHdl, weld::MetricSpinButton&, void); + + void updateMenu(); + +private: + Link maModifyHdl; + + std::unique_ptr mxMetric; + std::unique_ptr mxControl; +}; + +} + +SdRotationPropertyBox::SdRotationPropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl(rModifyHdl) + , mxMetric(mxBuilder->weld_metric_spin_button("rotate", FieldUnit::DEGREE)) + , mxControl(mxBuilder->weld_menu_button("rotatemenu")) +{ + mxMetric->connect_value_changed(LINK( this, SdRotationPropertyBox, implModifyHdl)); + mxMetric->set_help_id(HID_SD_CUSTOMANIMATIONPANE_ROTATIONPROPERTYBOX); + mxMetric->show(); + pLabel->set_mnemonic_widget(&mxMetric->get_widget()); + + mxControl->connect_selected(LINK(this, SdRotationPropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_ROTATIONPROPERTYBOX); + mxControl->show(); + + setValue(rValue, OUString()); +} + +void SdRotationPropertyBox::updateMenu() +{ + sal_Int64 nValue = mxMetric->get_value(FieldUnit::DEGREE); + bool bDirection = nValue >= 0; + nValue = (nValue < 0 ? -nValue : nValue); + + mxControl->set_item_active("90", nValue == 90); + mxControl->set_item_active("180", nValue == 180); + mxControl->set_item_active("360", nValue == 360); + mxControl->set_item_active("720", nValue == 720); + + mxControl->set_item_active("closewise", bDirection); + mxControl->set_item_active("counterclock", !bDirection); +} + +IMPL_LINK_NOARG(SdRotationPropertyBox, implModifyHdl, weld::MetricSpinButton&, void) +{ + updateMenu(); + maModifyHdl.Call(nullptr); +} + +IMPL_LINK(SdRotationPropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + auto nValue = mxMetric->get_value(FieldUnit::DEGREE); + bool bDirection = nValue >= 0; + nValue = (nValue < 0 ? -nValue : nValue); + + if (rIdent == "clockwise") + bDirection = true; + else if (rIdent == "counterclock") + bDirection = false; + else + nValue = rIdent.toInt32(); + + if( !bDirection ) + nValue = -nValue; + + if (nValue != mxMetric->get_value(FieldUnit::DEGREE)) + { + mxMetric->set_value(nValue, FieldUnit::DEGREE); + implModifyHdl(*mxMetric); + } +} + +void SdRotationPropertyBox::setValue( const Any& rValue, const OUString& ) +{ + if (mxMetric) + { + double fValue = 0.0; + rValue >>= fValue; + ::tools::Long nValue = static_cast<::tools::Long>(fValue); + mxMetric->set_value(nValue, FieldUnit::DEGREE); + updateMenu(); + } +} + +Any SdRotationPropertyBox::getValue() +{ + return Any(static_cast(mxMetric->get_value(FieldUnit::DEGREE))); +} + +namespace { + +class SdScalePropertyBox : public SdPropertySubControl +{ +public: + SdScalePropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& ) override; + + DECL_LINK(implMenuSelectHdl, const OString&, void); + DECL_LINK(implModifyHdl, weld::MetricSpinButton&, void); + + void updateMenu(); + +private: + Link maModifyHdl; + int mnDirection; + + std::unique_ptr mxMetric; + std::unique_ptr mxControl; +}; + +} + +SdScalePropertyBox::SdScalePropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl) + : SdPropertySubControl(pParent) + , maModifyHdl( rModifyHdl ) + , mxMetric(mxBuilder->weld_metric_spin_button("scale", FieldUnit::PERCENT)) + , mxControl(mxBuilder->weld_menu_button("scalemenu")) +{ + mxControl->connect_selected(LINK(this, SdScalePropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_SCALEPROPERTYBOX); + mxControl->show(); + + mxMetric->connect_value_changed(LINK(this, SdScalePropertyBox, implModifyHdl)); + mxMetric->set_help_id(HID_SD_CUSTOMANIMATIONPANE_SCALEPROPERTYBOX); + mxMetric->show(); + pLabel->set_mnemonic_widget(&mxMetric->get_widget()); + + setValue(rValue, OUString()); +} + +void SdScalePropertyBox::updateMenu() +{ + auto nValue = mxMetric->get_value(FieldUnit::PERCENT); + + mxControl->set_item_active("25scale", nValue == 25); + mxControl->set_item_active("50scale", nValue == 50); + mxControl->set_item_active("150scale", nValue == 150); + mxControl->set_item_active("400scale", nValue == 400); + + mxControl->set_item_active("hori", mnDirection == 1); + mxControl->set_item_active("vert", mnDirection == 2); + mxControl->set_item_active("both", mnDirection == 3); +} + +IMPL_LINK_NOARG(SdScalePropertyBox, implModifyHdl, weld::MetricSpinButton&, void) +{ + updateMenu(); + maModifyHdl.Call(nullptr); +} + +IMPL_LINK(SdScalePropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + auto nValue = mxMetric->get_value(FieldUnit::PERCENT); + + int nDirection = mnDirection; + + if (rIdent == "hori") + nDirection = 1; + else if (rIdent == "vert") + nDirection = 2; + else if (rIdent == "both") + nDirection = 3; + else + nValue = rIdent.toInt32(); // Getting here indicates a UI bug and should be handled better + + bool bModified = false; + + if( nDirection != mnDirection ) + { + mnDirection = nDirection; + bModified = true; + } + + if (nValue != mxMetric->get_value(FieldUnit::PERCENT)) + { + mxMetric->set_value(nValue, FieldUnit::PERCENT); + bModified = true; + } + + if(bModified) + { + implModifyHdl(*mxMetric); + updateMenu(); + } +} + +void SdScalePropertyBox::setValue(const Any& rValue, const OUString&) +{ + if (!mxMetric) + return; + + ValuePair aValues; + rValue >>= aValues; + + double fValue1 = 0.0; + double fValue2 = 0.0; + + aValues.First >>= fValue1; + aValues.Second >>= fValue2; + + // 'Size' drop down menu set by mnDirection when loading Grow and Shrink Animation + // Shouldn't compare a float directly to zero... should be fixed with delta epsilon compare + // Might be better to just have a flag in the content.xml for this + if( (fValue1 == 0.0) && (fValue2 == 0.0) ) + mnDirection = 3; // assume 'Both' scaling option when both are zero + else if( (fValue1 != 0.0) && (fValue2 == 0.0) ) + mnDirection = 1; + else if( (fValue1 == 0.0) && (fValue2 != 0.0) ) + mnDirection = 2; + else + mnDirection = 3; + + // Grow and Shrink Animation is a relative change with value stored in content.xml under tag + // smil:by=*,* + // An offset of 1 must be added to properly translate from content.xml to UI value displayed + // e.g. if in content.xml smil:by=0.5,0.5 then 1 + (0.5,0.5) = (1.5,1.5) => grow by 150% of the + // size horizontal and vertical + // e.g. if in content.xml smil:by=-0.5,-0.5 then 1 + (-0.5,-0.5) = (0.5,0.5) => shrink by 50% + // of the size horizontal and vertical + fValue1 += 1; + fValue2 += 1; + + // Determine value from file for UI 'Size' field based on determined mnDirection + ::tools::Long nValue; + if( mnDirection == 1 ) + nValue = static_cast<::tools::Long>(fValue1 * 100.0); + else if( mnDirection == 2 ) + nValue = static_cast<::tools::Long>(fValue2 * 100.0); + else if( mnDirection == 3 ){ + if (fValue1 >= fValue2) + nValue = static_cast<::tools::Long>(fValue1 * 100.0); + else + nValue = static_cast<::tools::Long>(fValue2 * 100.0); + } + else + nValue = static_cast<::tools::Long>(100.0); // default to 100% in UI if something goes wrong + + mxMetric->set_value(nValue, FieldUnit::PERCENT); + updateMenu(); +} + +Any SdScalePropertyBox::getValue() +{ + double fValue1 = static_cast(mxMetric->get_value(FieldUnit::PERCENT)) / 100.0; + + // Grow and Shrink Animation is a relative change with value stored in content.xml under tag + // smil:by=*,* + // An offset of 1 must be subtracted to properly translate UI value displayed and save to + // content.xml + // e.g. if UI value is 150% then 1.5 - 1 = 0.5 and is set to smil:by=0.5,0.5 in content.xml + // e.g. if UI value is 50% then 0.5 - 1 = -0.5 and is set to smil:by=-0.5,-0.5 in content.xml + fValue1 -= 1; + + double fValue2 = fValue1; + + // mnDirection set by 'Size' drop down menu and used to zero out either horizontal or vertical + // scaling depending on what option is selected + if( mnDirection == 1 ) + fValue2 = 0.0; + else if( mnDirection == 2 ) + fValue1 = 0.0; + + ValuePair aValues; + aValues.First <<= fValue1; + aValues.Second <<= fValue2; + + return Any( aValues ); +} + +namespace { + +class SdFontStylePropertyBox : public SdPropertySubControl +{ +public: + SdFontStylePropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl); + + virtual Any getValue() override; + virtual void setValue( const Any& rValue, const OUString& ) override; + + DECL_LINK(implMenuSelectHdl, const OString&, void); + + void update(); + +private: + float mfFontWeight; + awt::FontSlant meFontSlant; + sal_Int16 mnFontUnderline; + Link maModifyHdl; + + std::unique_ptr mxEdit; + std::unique_ptr mxControl; +}; + +} + +SdFontStylePropertyBox::SdFontStylePropertyBox(weld::Label* pLabel, weld::Container* pParent, const Any& rValue, const Link& rModifyHdl ) + : SdPropertySubControl(pParent) + , maModifyHdl( rModifyHdl ) + , mxEdit(mxBuilder->weld_entry("entry")) + , mxControl(mxBuilder->weld_menu_button("entrymenu")) +{ + mxEdit->set_text(SdResId(STR_CUSTOMANIMATION_SAMPLE)); + mxEdit->set_help_id(HID_SD_CUSTOMANIMATIONPANE_FONTSTYLEPROPERTYBOX); + pLabel->set_mnemonic_widget(mxEdit.get()); + mxEdit->show(); + + mxControl->connect_selected(LINK(this, SdFontStylePropertyBox, implMenuSelectHdl)); + mxControl->set_help_id(HID_SD_CUSTOMANIMATIONPANE_FONTSTYLEPROPERTYBOX); + mxControl->show(); + + setValue(rValue, OUString()); +} + +void SdFontStylePropertyBox::update() +{ + // update menu + mxControl->set_item_active("bold", mfFontWeight == awt::FontWeight::BOLD); + mxControl->set_item_active("italic", meFontSlant == awt::FontSlant_ITALIC); + mxControl->set_item_active("underline", mnFontUnderline != awt::FontUnderline::NONE ); + + // update sample edit + vcl::Font aFont(mxEdit->get_font()); + aFont.SetWeight(mfFontWeight == awt::FontWeight::BOLD ? WEIGHT_BOLD : WEIGHT_NORMAL); + aFont.SetItalic(meFontSlant == awt::FontSlant_ITALIC ? ITALIC_NORMAL : ITALIC_NONE); + aFont.SetUnderline(mnFontUnderline == awt::FontUnderline::NONE ? LINESTYLE_NONE : LINESTYLE_SINGLE); + mxEdit->set_font(aFont); +} + +IMPL_LINK(SdFontStylePropertyBox, implMenuSelectHdl, const OString&, rIdent, void) +{ + if (rIdent == "bold") + { + if( mfFontWeight == awt::FontWeight::BOLD ) + mfFontWeight = awt::FontWeight::NORMAL; + else + mfFontWeight = awt::FontWeight::BOLD; + } + else if (rIdent == "italic") + { + if( meFontSlant == awt::FontSlant_ITALIC ) + meFontSlant = awt::FontSlant_NONE; + else + meFontSlant = awt::FontSlant_ITALIC; + } + else if (rIdent == "underline") + { + if( mnFontUnderline == awt::FontUnderline::SINGLE ) + mnFontUnderline = awt::FontUnderline::NONE; + else + mnFontUnderline = awt::FontUnderline::SINGLE; + } + + update(); + maModifyHdl.Call(nullptr); +} + +void SdFontStylePropertyBox::setValue( const Any& rValue, const OUString& ) +{ + Sequence aValues; + rValue >>= aValues; + + aValues[0] >>= mfFontWeight; + aValues[1] >>= meFontSlant; + aValues[2] >>= mnFontUnderline; + + update(); +} + +Any SdFontStylePropertyBox::getValue() +{ + Sequence aValues{ Any(mfFontWeight), Any(meFontSlant), Any(mnFontUnderline) }; + return Any( aValues ); +} + +class CustomAnimationEffectTabPage +{ +public: + CustomAnimationEffectTabPage(weld::Container* pParent, weld::Window* pDialog, const STLPropertySet* pSet); + + void update( STLPropertySet* pSet ); + DECL_LINK(implSelectHdl, weld::ComboBox&, void); + DECL_LINK(implClickHdl, weld::Button&, void); + void implHdl(const weld::Widget*); + +private: + void updateControlStates(); + void fillSoundListBox(); + void clearSoundListBox(); + sal_Int32 getSoundObject( std::u16string_view rStr ); + void openSoundFileDialog(); + void onSoundPreview(); + weld::Window* GetFrameWeld() const { return mpDialog; } + +private: + ::std::vector< OUString > maSoundList; + bool mbHasText; + const STLPropertySet* mpSet; + css::uno::Reference mxPlayer; + + weld::Window* mpDialog; + std::unique_ptr mxBuilder; + std::unique_ptr mxContainer; + std::unique_ptr mxSettings; + std::unique_ptr mxFTProperty1; + std::unique_ptr mxPlaceholderBox; + std::unique_ptr mxCBSmoothStart; + std::unique_ptr mxCBSmoothEnd; + std::unique_ptr mxFTSound; + std::unique_ptr mxLBSound; + std::unique_ptr mxPBSoundPreview; + std::unique_ptr mxFTAfterEffect; + std::unique_ptr mxLBAfterEffect; + std::unique_ptr mxFTDimColor; + std::unique_ptr mxCLBDimColor; + std::unique_ptr mxFTTextAnim; + std::unique_ptr mxLBTextAnim; + std::unique_ptr mxMFTextDelay; + std::unique_ptr mxFTTextDelay; + std::unique_ptr mxLBSubControl; +}; + +CustomAnimationEffectTabPage::CustomAnimationEffectTabPage(weld::Container* pParent, weld::Window* pDialog, const STLPropertySet* pSet) + : mbHasText(false) + , mpSet(pSet) + , mpDialog(pDialog) + , mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/customanimationeffecttab.ui")) + , mxContainer(mxBuilder->weld_container("EffectTab")) + , mxSettings(mxBuilder->weld_widget("settings")) + , mxFTProperty1(mxBuilder->weld_label("prop_label1")) + , mxPlaceholderBox(mxBuilder->weld_container("placeholder")) + , mxCBSmoothStart(mxBuilder->weld_check_button("smooth_start")) + , mxCBSmoothEnd(mxBuilder->weld_check_button("smooth_end")) + , mxFTSound(mxBuilder->weld_label("sound_label")) + , mxLBSound(mxBuilder->weld_combo_box("sound_list")) + , mxPBSoundPreview(mxBuilder->weld_button("sound_preview")) + , mxFTAfterEffect(mxBuilder->weld_label("aeffect_label")) + , mxLBAfterEffect(mxBuilder->weld_combo_box("aeffect_list")) + , mxFTDimColor(mxBuilder->weld_label("dim_color_label")) + , mxCLBDimColor(new ColorListBox(mxBuilder->weld_menu_button("dim_color_list"), [pDialog]{ return pDialog; })) + , mxFTTextAnim(mxBuilder->weld_label("text_animation_label")) + , mxLBTextAnim(mxBuilder->weld_combo_box("text_animation_list")) + , mxMFTextDelay(mxBuilder->weld_metric_spin_button("text_delay", FieldUnit::PERCENT)) + , mxFTTextDelay(mxBuilder->weld_label("text_delay_label")) +{ + mxCLBDimColor->SelectEntry(COL_BLACK); + + // fill the soundbox + fillSoundListBox(); + + mxLBSound->connect_changed(LINK(this, CustomAnimationEffectTabPage, implSelectHdl)); + mxPBSoundPreview->connect_clicked(LINK(this, CustomAnimationEffectTabPage, implClickHdl)); + + // only show settings if all selected effects have the same preset-id + if( pSet->getPropertyState( nHandlePresetId ) != STLPropertyState::Ambiguous ) + { + OUString aPresetId; + pSet->getPropertyValue( nHandlePresetId ) >>= aPresetId; + + // property 1 + + if( pSet->getPropertyState( nHandleProperty1Type ) != STLPropertyState::Ambiguous ) + { + sal_Int32 nType = 0; + pSet->getPropertyValue( nHandleProperty1Type ) >>= nType; + + if( nType != nPropertyTypeNone ) + { + // set ui name for property at fixed text + OUString aPropertyName( getPropertyName( nType ) ); + + if( !aPropertyName.isEmpty() ) + { + mxSettings->show(); + mxFTProperty1->set_label(aPropertyName); + } + + // get property value + const Any aValue( pSet->getPropertyValue( nHandleProperty1Value ) ); + + // create property sub control + mxLBSubControl = SdPropertySubControl::create(nType, mxFTProperty1.get(), mxPlaceholderBox.get(), mpDialog, aValue, aPresetId, Link()); + } + } + + mxFTProperty1->set_sensitive(mxPlaceholderBox->get_sensitive()); + + // accelerate & decelerate + + if( pSet->getPropertyState( nHandleAccelerate ) == STLPropertyState::Direct ) + { + mxCBSmoothStart->show(); + mxCBSmoothEnd->show(); + + double fTemp = 0.0; + pSet->getPropertyValue( nHandleAccelerate ) >>= fTemp; + mxCBSmoothStart->set_active( fTemp > 0.0 ); + + pSet->getPropertyValue( nHandleDecelerate ) >>= fTemp; + mxCBSmoothEnd->set_active( fTemp > 0.0 ); + } + } + + // init after effect controls + + mxLBAfterEffect->connect_changed(LINK(this, CustomAnimationEffectTabPage, implSelectHdl)); + mxLBTextAnim->connect_changed(LINK(this, CustomAnimationEffectTabPage, implSelectHdl)); + + if( (pSet->getPropertyState( nHandleHasAfterEffect ) != STLPropertyState::Ambiguous) && + (pSet->getPropertyState( nHandleAfterEffectOnNextEffect ) != STLPropertyState::Ambiguous) && + (pSet->getPropertyState( nHandleDimColor ) != STLPropertyState::Ambiguous)) + { + bool bHasAfterEffect = false; + pSet->getPropertyValue( nHandleHasAfterEffect ) >>= bHasAfterEffect; + + sal_Int32 nPos = 0; + if( bHasAfterEffect ) + { + nPos++; + + bool bAfterEffectOnNextClick = false; + pSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bAfterEffectOnNextClick; + Any aDimColor( pSet->getPropertyValue( nHandleDimColor ) ); + + if( aDimColor.hasValue() ) + { + Color aColor; + aDimColor >>= aColor; + mxCLBDimColor->SelectEntry(aColor); + } + else + { + nPos++; + if( bAfterEffectOnNextClick ) + nPos++; + } + } + + mxLBAfterEffect->set_active(nPos); + } + + if( pSet->getPropertyState( nHandleHasText ) != STLPropertyState::Ambiguous ) + pSet->getPropertyValue( nHandleHasText ) >>= mbHasText; + + if( mbHasText ) + { + if( pSet->getPropertyState( nHandleIterateType ) != STLPropertyState::Ambiguous) + { + int nPos = -1; + + sal_Int32 nIterateType = 0; + pSet->getPropertyValue( nHandleIterateType ) >>= nIterateType; + switch( nIterateType ) + { + case TextAnimationType::BY_PARAGRAPH: nPos = 0; break; + case TextAnimationType::BY_WORD: nPos = 1; break; + case TextAnimationType::BY_LETTER: nPos = 2; break; + } + + mxLBTextAnim->set_active(nPos); + } + + if( pSet->getPropertyState( nHandleIterateInterval ) != STLPropertyState::Default ) + { + double fIterateInterval = 0.0; + pSet->getPropertyValue( nHandleIterateInterval ) >>= fIterateInterval; + mxMFTextDelay->set_value(static_cast<::tools::Long>(fIterateInterval*10), FieldUnit::NONE); + } + } + else + { + mxFTTextAnim->set_sensitive(false); + mxLBTextAnim->set_sensitive(false); + mxMFTextDelay->set_sensitive(false); + mxFTTextDelay->set_sensitive(false); + + } + + if( pSet->getPropertyState( nHandleSoundURL ) != STLPropertyState::Ambiguous ) + { + sal_Int32 nPos = 0; + + const Any aValue( pSet->getPropertyValue( nHandleSoundURL ) ); + + if( aValue.getValueType() == ::cppu::UnoType::get() ) + { + nPos = 1; + } + else + { + OUString aSoundURL; + aValue >>= aSoundURL; + + if( !aSoundURL.isEmpty() ) + { + sal_uLong i; + for( i = 0; i < maSoundList.size(); i++ ) + { + OUString aString = maSoundList[ i ]; + if( aString == aSoundURL ) + { + nPos = static_cast(i)+2; + break; + } + } + + if( nPos == 0 ) + { + nPos = static_cast(maSoundList.size())+2; + maSoundList.push_back( aSoundURL ); + INetURLObject aURL( aSoundURL ); + mxLBSound->insert_text(nPos, aURL.GetBase()); + } + } + } + + if( nPos != -1) + mxLBSound->set_active(nPos); + } + + updateControlStates(); + +} + +void CustomAnimationEffectTabPage::updateControlStates() +{ + auto nPos = mxLBAfterEffect->get_active(); + mxCLBDimColor->set_sensitive( nPos == 1 ); + mxFTDimColor->set_sensitive( nPos == 1 ); + + if( mbHasText ) + { + nPos = mxLBTextAnim->get_active(); + mxMFTextDelay->set_sensitive( nPos != 0 ); + mxFTTextDelay->set_sensitive( nPos != 0 ); + } + + if (comphelper::LibreOfficeKit::isActive()) + { + mxFTSound->hide(); + mxLBSound->hide(); + mxPBSoundPreview->hide(); + } + else + { + nPos = mxLBSound->get_active(); + mxPBSoundPreview->set_sensitive( nPos >= 2 ); + } + +} + +IMPL_LINK(CustomAnimationEffectTabPage, implClickHdl, weld::Button&, rBtn, void) +{ + implHdl(&rBtn); +} + +IMPL_LINK(CustomAnimationEffectTabPage, implSelectHdl, weld::ComboBox&, rListBox, void) +{ + implHdl(&rListBox); +} + +void CustomAnimationEffectTabPage::implHdl(const weld::Widget* pControl) +{ + if (pControl == mxLBTextAnim.get()) + { + if (mxMFTextDelay->get_value(FieldUnit::NONE) == 0) + mxMFTextDelay->set_value(100, FieldUnit::NONE); + } + else if (pControl == mxLBSound.get()) + { + auto nPos = mxLBSound->get_active(); + if (nPos == (mxLBSound->get_count() - 1)) + { + openSoundFileDialog(); + } + } + else if (pControl == mxPBSoundPreview.get()) + { + onSoundPreview(); + } + + updateControlStates(); +} + +void CustomAnimationEffectTabPage::update( STLPropertySet* pSet ) +{ + if (mxLBSubControl) + { + Any aNewValue(mxLBSubControl->getValue()); + Any aOldValue; + if( mpSet->getPropertyState( nHandleProperty1Value ) != STLPropertyState::Ambiguous) + aOldValue = mpSet->getPropertyValue( nHandleProperty1Value ); + + if( aOldValue != aNewValue ) + pSet->setPropertyValue( nHandleProperty1Value, aNewValue ); + } + + if (mxCBSmoothStart->get_visible()) + { + // set selected value for accelerate if different than in original set + + double fTemp = mxCBSmoothStart->get_active() ? 0.5 : 0.0; + + double fOldTemp = 0.0; + if(mpSet->getPropertyState( nHandleAccelerate ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleAccelerate ) >>= fOldTemp; + else + fOldTemp = -2.0; + + if( fOldTemp != fTemp ) + pSet->setPropertyValue( nHandleAccelerate, Any( fTemp ) ); + + // set selected value for decelerate if different than in original set + fTemp = mxCBSmoothEnd->get_active() ? 0.5 : 0.0; + + if(mpSet->getPropertyState( nHandleDecelerate ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleDecelerate ) >>= fOldTemp; + else + fOldTemp = -2.0; + + if( fOldTemp != fTemp ) + pSet->setPropertyValue( nHandleDecelerate, Any( fTemp ) ); + } + + auto nPos = mxLBAfterEffect->get_active(); + if (nPos != -1) + { + bool bAfterEffect = nPos != 0; + + bool bOldAfterEffect = false; + + if(mpSet->getPropertyState( nHandleHasAfterEffect ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleHasAfterEffect ) >>= bOldAfterEffect; + else + bOldAfterEffect = !bAfterEffect; + + if( bOldAfterEffect != bAfterEffect ) + pSet->setPropertyValue( nHandleHasAfterEffect, Any( bAfterEffect ) ); + + Any aDimColor; + if( nPos == 1 ) + { + Color aSelectedColor = mxCLBDimColor->GetSelectEntryColor(); + aDimColor <<= aSelectedColor.GetRGBColor(); + } + + if( (mpSet->getPropertyState( nHandleDimColor ) == STLPropertyState::Ambiguous) || + (mpSet->getPropertyValue( nHandleDimColor ) != aDimColor) ) + pSet->setPropertyValue( nHandleDimColor, aDimColor ); + + bool bAfterEffectOnNextEffect = nPos != 2; + bool bOldAfterEffectOnNextEffect = !bAfterEffectOnNextEffect; + + if( mpSet->getPropertyState( nHandleAfterEffectOnNextEffect ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bOldAfterEffectOnNextEffect; + + if( bAfterEffectOnNextEffect != bOldAfterEffectOnNextEffect ) + pSet->setPropertyValue( nHandleAfterEffectOnNextEffect, Any( bAfterEffectOnNextEffect ) ); + } + + nPos = mxLBTextAnim->get_active(); + if (nPos != -1) + { + sal_Int16 nIterateType; + + switch( nPos ) + { + case 1: nIterateType = TextAnimationType::BY_WORD; break; + case 2: nIterateType = TextAnimationType::BY_LETTER; break; + default: + nIterateType = TextAnimationType::BY_PARAGRAPH; + } + + sal_Int16 nOldIterateType = nIterateType-1; + + if(mpSet->getPropertyState( nHandleIterateType ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleIterateType ) >>= nOldIterateType; + + if( nIterateType != nOldIterateType ) + pSet->setPropertyValue( nHandleIterateType, Any( nIterateType ) ); + } + + { + double fIterateInterval = static_cast(mxMFTextDelay->get_value(FieldUnit::NONE)) / 10; + double fOldIterateInterval = -1.0; + + if( mpSet->getPropertyState( nHandleIterateInterval ) != STLPropertyState::Ambiguous ) + mpSet->getPropertyValue( nHandleIterateInterval ) >>= fOldIterateInterval; + + if( fIterateInterval != fOldIterateInterval ) + pSet->setPropertyValue( nHandleIterateInterval, Any( fIterateInterval ) ); + } + + nPos = mxLBSound->get_active(); + if (nPos == -1) + return; + + Any aNewSoundURL, aOldSoundURL( Any( sal_Int32(0) ) ); + + if( nPos == 0 ) + { + // 0 means no sound, so leave any empty + } + else if( nPos == 1 ) + { + // this means stop sound + aNewSoundURL <<= true; + } + else + { + OUString aSoundURL( maSoundList[ nPos-2 ] ); + aNewSoundURL <<= aSoundURL; + } + + if( mpSet->getPropertyState( nHandleSoundURL ) != STLPropertyState::Ambiguous ) + aOldSoundURL = mpSet->getPropertyValue( nHandleSoundURL ); + + if( aNewSoundURL != aOldSoundURL ) + pSet->setPropertyValue( nHandleSoundURL, aNewSoundURL ); +} + +void CustomAnimationEffectTabPage::fillSoundListBox() +{ + GalleryExplorer::FillObjList( GALLERY_THEME_SOUNDS, maSoundList ); + GalleryExplorer::FillObjList( GALLERY_THEME_USERSOUNDS, maSoundList ); + + mxLBSound->append_text( SdResId(STR_CUSTOMANIMATION_NO_SOUND) ); + mxLBSound->append_text( SdResId(STR_CUSTOMANIMATION_STOP_PREVIOUS_SOUND) ); + for(const OUString & rString : maSoundList) + { + INetURLObject aURL( rString ); + mxLBSound->append_text( aURL.GetBase() ); + } + mxLBSound->append_text( SdResId(STR_CUSTOMANIMATION_BROWSE_SOUND) ); +} + +void CustomAnimationEffectTabPage::clearSoundListBox() +{ + maSoundList.clear(); + mxLBSound->clear(); +} + +sal_Int32 CustomAnimationEffectTabPage::getSoundObject( std::u16string_view rStr ) +{ + size_t i; + const size_t nCount = maSoundList.size(); + for( i = 0; i < nCount; i++ ) + { + if( maSoundList[ i ].equalsIgnoreAsciiCase(rStr) ) + return i+2; + } + + return -1; +} + +void CustomAnimationEffectTabPage::openSoundFileDialog() +{ + SdOpenSoundFileDialog aFileDialog(GetFrameWeld()); + + bool bValidSoundFile = false; + bool bQuitLoop = false; + ::tools::Long nPos = 0; + + while( !bQuitLoop && (aFileDialog.Execute() == ERRCODE_NONE) ) + { + OUString aFile = aFileDialog.GetPath(); + nPos = getSoundObject( aFile ); + + if( nPos < 0 ) // not in Soundliste + { + // try to insert in Gallery + if( GalleryExplorer::InsertURL( GALLERY_THEME_USERSOUNDS, aFile ) ) + { + clearSoundListBox(); + fillSoundListBox(); + + nPos = getSoundObject( aFile ); + DBG_ASSERT( nPos >= 0, "sd::CustomAnimationEffectTabPage::openSoundFileDialog(), Recently inserted sound not in list!" ); + + bValidSoundFile=true; + bQuitLoop=true; + } + else + { + OUString aStrWarning(SdResId(STR_WARNING_NOSOUNDFILE)); + aStrWarning = aStrWarning.replaceFirst("%", aFile); + std::unique_ptr xWarn(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::NONE, + aStrWarning)); + xWarn->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY); + xWarn->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + bQuitLoop = xWarn->run() != RET_RETRY; + + bValidSoundFile=false; + } + } + else + { + bValidSoundFile=true; + bQuitLoop=true; + } + } + + if( !bValidSoundFile ) + nPos = 0; + + mxLBSound->set_active(nPos); +} + +void CustomAnimationEffectTabPage::onSoundPreview() +{ +#if HAVE_FEATURE_AVMEDIA + const auto nPos = mxLBSound->get_active(); + + if( nPos >= 2 ) try + { + const OUString aSoundURL( maSoundList[ nPos-2 ] ); + mxPlayer.set( avmedia::MediaWindow::createPlayer( aSoundURL, "" ), uno::UNO_SET_THROW ); + mxPlayer->start(); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "CustomAnimationEffectTabPage::onSoundPreview()" ); + } +#endif +} + +class CustomAnimationDurationTabPage +{ +public: + CustomAnimationDurationTabPage(weld::Container* pParent, const STLPropertySet* pSet); + + void update( STLPropertySet* pSet ); + + DECL_LINK(implControlHdl, weld::ComboBox&, void); + DECL_LINK(DurationModifiedHdl, weld::MetricSpinButton&, void); + +private: + const STLPropertySet* mpSet; + + std::unique_ptr mxBuilder; + std::unique_ptr mxContainer; + std::unique_ptr mxFTStart; + std::unique_ptr mxLBStart; + std::unique_ptr mxFTStartDelay; + std::unique_ptr mxMFStartDelay; + std::unique_ptr mxFTDuration; + std::unique_ptr mxCBXDuration; + std::unique_ptr mxFTRepeat; + std::unique_ptr mxCBRepeat; + std::unique_ptr mxCBXRewind; + std::unique_ptr mxRBClickSequence; + std::unique_ptr mxRBInteractive; + std::unique_ptr mxLBTrigger; +}; + +CustomAnimationDurationTabPage::CustomAnimationDurationTabPage(weld::Container* pParent, const STLPropertySet* pSet) + : mpSet(pSet) + , mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/customanimationtimingtab.ui")) + , mxContainer(mxBuilder->weld_container("TimingTab")) + , mxFTStart(mxBuilder->weld_label("start_label")) + , mxLBStart(mxBuilder->weld_combo_box("start_list")) + , mxFTStartDelay(mxBuilder->weld_label("delay_label")) + , mxMFStartDelay(mxBuilder->weld_metric_spin_button("delay_value", FieldUnit::SECOND)) + , mxFTDuration(mxBuilder->weld_label("duration_label")) + , mxCBXDuration(mxBuilder->weld_metric_spin_button("anim_duration", FieldUnit::SECOND)) + , mxFTRepeat(mxBuilder->weld_label("repeat_label")) + , mxCBRepeat(mxBuilder->weld_combo_box("repeat_list")) + , mxCBXRewind(mxBuilder->weld_check_button("rewind")) + , mxRBClickSequence(mxBuilder->weld_radio_button("rb_click_sequence")) + , mxRBInteractive(mxBuilder->weld_radio_button("rb_interactive")) + , mxLBTrigger(mxBuilder->weld_combo_box("trigger_list")) +{ + mxLBTrigger->set_size_request(mxLBTrigger->get_approximate_digit_width() * 20, -1); + + fillRepeatComboBox(*mxCBRepeat); + + mxLBTrigger->connect_changed(LINK(this, CustomAnimationDurationTabPage, implControlHdl)); + mxCBXDuration->connect_value_changed(LINK( this, CustomAnimationDurationTabPage, DurationModifiedHdl)); + + if( pSet->getPropertyState( nHandleStart ) != STLPropertyState::Ambiguous ) + { + sal_Int16 nStart = 0; + pSet->getPropertyValue( nHandleStart ) >>= nStart; + sal_Int32 nPos = 0; + switch( nStart ) + { + case EffectNodeType::WITH_PREVIOUS: nPos = 1; break; + case EffectNodeType::AFTER_PREVIOUS: nPos = 2; break; + } + mxLBStart->set_active(nPos); + } + + if( pSet->getPropertyState( nHandleBegin ) != STLPropertyState::Ambiguous ) + { + double fBegin = 0.0; + pSet->getPropertyValue( nHandleBegin ) >>= fBegin; + mxMFStartDelay->set_value(static_cast<::tools::Long>(fBegin*10), FieldUnit::NONE); + } + + if( pSet->getPropertyState( nHandleDuration ) != STLPropertyState::Ambiguous ) + { + double fDuration = 0.0; + pSet->getPropertyValue( nHandleDuration ) >>= fDuration; + + if( fDuration == 0.001 ) + { + mxFTDuration->set_sensitive(false); + mxCBXDuration->set_sensitive(false); + mxFTRepeat->set_sensitive(false); + mxCBRepeat->set_sensitive(false); + mxCBXRewind->set_sensitive(false); + } + else + { + mxCBXDuration->set_value(fDuration * 100.0, FieldUnit::NONE); + } + } + + if( pSet->getPropertyState( nHandleRepeat ) != STLPropertyState::Ambiguous ) + { + Any aRepeatCount( pSet->getPropertyValue( nHandleRepeat ) ); + if( (aRepeatCount.getValueType() == ::cppu::UnoType::get()) || !aRepeatCount.hasValue() ) + { + double fRepeat = 0.0; + if( aRepeatCount.hasValue() ) + aRepeatCount >>= fRepeat; + + auto nPos = -1; + + if( fRepeat == 0 ) + nPos = 0; + else if( fRepeat == 2.0 ) + nPos = 1; + else if( fRepeat == 3.0 ) + nPos = 2; + else if( fRepeat == 4.0 ) + nPos = 3; + else if( fRepeat == 5.0 ) + nPos = 4; + else if( fRepeat == 10.0 ) + nPos = 5; + + if (nPos != -1) + mxCBRepeat->set_active(nPos); + else + mxCBRepeat->set_entry_text(OUString::number(fRepeat)); + } + else if( aRepeatCount.getValueType() == ::cppu::UnoType::get() ) + { + Any aEnd; + if( pSet->getPropertyState( nHandleEnd ) != STLPropertyState::Ambiguous ) + aEnd = pSet->getPropertyValue( nHandleEnd ); + + mxCBRepeat->set_active(aEnd.hasValue() ? 6 : 7); + } + } + + if( pSet->getPropertyState( nHandleRewind ) != STLPropertyState::Ambiguous ) + { + sal_Int16 nFill = 0; + if( pSet->getPropertyValue( nHandleRewind ) >>= nFill ) + { + mxCBXRewind->set_active(nFill == AnimationFill::REMOVE); + } + else + { + mxCBXRewind->set_state(TRISTATE_INDET); + } + } + + Reference< XShape > xTrigger; + + if( pSet->getPropertyState( nHandleTrigger ) != STLPropertyState::Ambiguous ) + { + pSet->getPropertyValue( nHandleTrigger ) >>= xTrigger; + + mxRBInteractive->set_active(xTrigger.is()); + mxRBClickSequence->set_active(!xTrigger.is()); + } + + Reference< XDrawPage > xCurrentPage; + pSet->getPropertyValue( nHandleCurrentPage ) >>= xCurrentPage; + if( !xCurrentPage.is() ) + return; + + static const OUStringLiteral aStrIsEmptyPresObj( u"IsEmptyPresentationObject" ); + + sal_Int32 nShape, nCount = xCurrentPage->getCount(); + for( nShape = 0; nShape < nCount; nShape++ ) + { + Reference< XShape > xShape( xCurrentPage->getByIndex( nShape ), UNO_QUERY ); + + if( !xShape.is() ) + continue; + + Reference< XPropertySet > xSet( xShape, UNO_QUERY ); + if( xSet.is() && xSet->getPropertySetInfo()->hasPropertyByName( aStrIsEmptyPresObj ) ) + { + bool bIsEmpty = false; + xSet->getPropertyValue( aStrIsEmptyPresObj ) >>= bIsEmpty; + if( bIsEmpty ) + continue; + } + + OUString aDescription( getShapeDescription( xShape, true ) ); + mxLBTrigger->append(OUString::number(nShape), aDescription); + auto nPos = mxLBTrigger->get_count() - 1; + if (xShape == xTrigger) + mxLBTrigger->set_active(nPos); + } +} + +IMPL_LINK_NOARG(CustomAnimationDurationTabPage, implControlHdl, weld::ComboBox&, void) +{ + mxRBInteractive->set_active(true); + assert(!mxRBClickSequence->get_active()); +} + +IMPL_LINK_NOARG(CustomAnimationDurationTabPage, DurationModifiedHdl, weld::MetricSpinButton&, void) +{ + if (!mxCBXDuration->get_text().isEmpty()) + { + double duration_value = static_cast(mxCBXDuration->get_value(FieldUnit::NONE)); + if(duration_value <= 0.0) + mxCBXDuration->set_value(1, FieldUnit::NONE); + else + mxCBXDuration->set_value(duration_value, FieldUnit::NONE); + } +} + +void CustomAnimationDurationTabPage::update( STLPropertySet* pSet ) +{ + auto nPos = mxLBStart->get_active(); + if (nPos != -1) + { + sal_Int16 nStart; + sal_Int16 nOldStart = -1; + + switch( nPos ) + { + case 1: nStart = EffectNodeType::WITH_PREVIOUS; break; + case 2: nStart = EffectNodeType::AFTER_PREVIOUS; break; + default: + nStart = EffectNodeType::ON_CLICK; break; + } + + if(mpSet->getPropertyState( nHandleStart ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleStart ) >>= nOldStart; + + if( nStart != nOldStart ) + pSet->setPropertyValue( nHandleStart, Any( nStart ) ); + } + + { + double fBegin = static_cast(mxMFStartDelay->get_value(FieldUnit::NONE)) / 10.0; + double fOldBegin = -1.0; + + if( mpSet->getPropertyState( nHandleBegin ) != STLPropertyState::Ambiguous ) + mpSet->getPropertyValue( nHandleBegin ) >>= fOldBegin; + + if( fBegin != fOldBegin ) + pSet->setPropertyValue( nHandleBegin, Any( fBegin ) ); + } + + nPos = mxCBRepeat->get_active(); + if (nPos != -1 || !mxCBRepeat->get_active_text().isEmpty()) + { + Any aRepeatCount; + Any aEnd; + + switch( nPos ) + { + case 0: + break; + + case 6: + { + Event aEvent; + aEvent.Trigger = EventTrigger::ON_NEXT; + aEvent.Repeat = 0; + aEnd <<= aEvent; + } + [[fallthrough]]; + case 7: + aRepeatCount <<= Timing_INDEFINITE; + break; + default: + { + OUString aText(mxCBRepeat->get_text(nPos)); + if( !aText.isEmpty() ) + aRepeatCount <<= aText.toDouble(); + } + } + + Any aOldRepeatCount( aRepeatCount ); + if( mpSet->getPropertyState( nHandleRepeat ) != STLPropertyState::Ambiguous ) + aOldRepeatCount = mpSet->getPropertyValue( nHandleRepeat ); + + if( aRepeatCount != aOldRepeatCount ) + pSet->setPropertyValue( nHandleRepeat, aRepeatCount ); + + Any aOldEnd( aEnd ); + if( mpSet->getPropertyState( nHandleEnd ) != STLPropertyState::Ambiguous ) + aOldEnd = mpSet->getPropertyValue( nHandleEnd ); + + if( aEnd != aOldEnd ) + pSet->setPropertyValue( nHandleEnd, aEnd ); + } + + double fDuration = -1.0; + + if (!mxCBXDuration->get_text().isEmpty()) + { + double duration_value = static_cast(mxCBXDuration->get_value(FieldUnit::NONE)); + + if(duration_value > 0) + fDuration = duration_value/100.0; + } + + if( fDuration != -1.0 ) + { + double fOldDuration = -1; + + if( mpSet->getPropertyState( nHandleDuration ) != STLPropertyState::Ambiguous ) + mpSet->getPropertyValue( nHandleDuration ) >>= fOldDuration; + + if( fDuration != fOldDuration ) + pSet->setPropertyValue( nHandleDuration, Any( fDuration ) ); + } + + if (mxCBXRewind->get_state() != TRISTATE_INDET) + { + sal_Int16 nFill = mxCBXRewind->get_active() ? AnimationFill::REMOVE : AnimationFill::HOLD; + + bool bSet = true; + + if( mpSet->getPropertyState( nHandleRewind ) != STLPropertyState::Ambiguous ) + { + sal_Int16 nOldFill = 0; + mpSet->getPropertyValue( nHandleRewind ) >>= nOldFill; + bSet = nFill != nOldFill; + } + + if( bSet ) + pSet->setPropertyValue( nHandleRewind, Any( nFill ) ); + } + + Reference< XShape > xTrigger; + + if (mxRBInteractive->get_active()) + { + nPos = mxLBTrigger->get_active(); + if (nPos != -1) + { + sal_Int32 nShape = mxLBTrigger->get_id(nPos).toInt32(); + + Reference< XDrawPage > xCurrentPage; + mpSet->getPropertyValue( nHandleCurrentPage ) >>= xCurrentPage; + + if( xCurrentPage.is() && (nShape >= 0) && (nShape < xCurrentPage->getCount()) ) + xCurrentPage->getByIndex( nShape ) >>= xTrigger; + } + } + + if (xTrigger.is() || mxRBClickSequence->get_active()) + { + Any aNewValue( xTrigger ); + Any aOldValue; + + if( mpSet->getPropertyState( nHandleTrigger ) != STLPropertyState::Ambiguous ) + aOldValue = mpSet->getPropertyValue( nHandleTrigger ); + + if( aNewValue != aOldValue ) + pSet->setPropertyValue( nHandleTrigger, aNewValue ); + } +} + +class CustomAnimationTextAnimTabPage +{ +public: + CustomAnimationTextAnimTabPage(weld::Container* pParent, const STLPropertySet* pSet); + + void update( STLPropertySet* pSet ); + + void updateControlStates(); + DECL_LINK(implSelectHdl, weld::ComboBox&, void); + +private: + const STLPropertySet* mpSet; + bool mbHasVisibleShapes; + + std::unique_ptr mxBuilder; + std::unique_ptr mxContainer; + std::unique_ptr mxFTGroupText; + std::unique_ptr mxLBGroupText; + std::unique_ptr mxCBXGroupAuto; + std::unique_ptr mxMFGroupAuto; + std::unique_ptr mxCBXAnimateForm; + std::unique_ptr mxCBXReverse; +}; + +CustomAnimationTextAnimTabPage::CustomAnimationTextAnimTabPage(weld::Container* pParent, const STLPropertySet* pSet) + : mpSet(pSet) + , mbHasVisibleShapes(true) + , mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/customanimationtexttab.ui")) + , mxContainer(mxBuilder->weld_container("TextAnimationTab")) + , mxFTGroupText(mxBuilder->weld_label("group_text_label")) + , mxLBGroupText(mxBuilder->weld_combo_box("group_text_list")) + , mxCBXGroupAuto(mxBuilder->weld_check_button("auto_after")) + , mxMFGroupAuto(mxBuilder->weld_metric_spin_button("auto_after_value",FieldUnit::SECOND)) + , mxCBXAnimateForm(mxBuilder->weld_check_button("animate_shape")) + , mxCBXReverse(mxBuilder->weld_check_button("reverse_order")) +{ + mxLBGroupText->connect_changed(LINK(this, CustomAnimationTextAnimTabPage, implSelectHdl)); + + if( pSet->getPropertyState( nHandleTextGrouping ) != STLPropertyState::Ambiguous ) + { + sal_Int32 nTextGrouping = 0; + if( pSet->getPropertyValue( nHandleTextGrouping ) >>= nTextGrouping ) + mxLBGroupText->set_active(nTextGrouping + 1); + } + + if( pSet->getPropertyState( nHandleHasVisibleShape ) != STLPropertyState::Ambiguous ) + pSet->getPropertyValue( nHandleHasVisibleShape ) >>= mbHasVisibleShapes; + + if( pSet->getPropertyState( nHandleTextGroupingAuto ) != STLPropertyState::Ambiguous ) + { + double fTextGroupingAuto = 0.0; + if( pSet->getPropertyValue( nHandleTextGroupingAuto ) >>= fTextGroupingAuto ) + { + mxCBXGroupAuto->set_active(fTextGroupingAuto >= 0.0); + if( fTextGroupingAuto >= 0.0 ) + mxMFGroupAuto->set_value(static_cast<::tools::Long>(fTextGroupingAuto*10), FieldUnit::NONE); + } + } + else + { + mxCBXGroupAuto->set_state( TRISTATE_INDET ); + } + + mxCBXAnimateForm->set_state( TRISTATE_INDET ); + if( pSet->getPropertyState( nHandleAnimateForm ) != STLPropertyState::Ambiguous ) + { + bool bAnimateForm = false; + if( pSet->getPropertyValue( nHandleAnimateForm ) >>= bAnimateForm ) + { + mxCBXAnimateForm->set_active( bAnimateForm ); + } + } + else + { + mxCBXAnimateForm->set_sensitive(false); + } + + mxCBXReverse->set_state(TRISTATE_INDET); + if( pSet->getPropertyState( nHandleTextReverse ) != STLPropertyState::Ambiguous ) + { + bool bTextReverse = false; + if( pSet->getPropertyValue( nHandleTextReverse ) >>= bTextReverse ) + { + mxCBXReverse->set_active( bTextReverse ); + } + } + + if( pSet->getPropertyState( nHandleMaxParaDepth ) == STLPropertyState::Direct ) + { + sal_Int32 nMaxParaDepth = 0; + pSet->getPropertyValue( nHandleMaxParaDepth ) >>= nMaxParaDepth; + nMaxParaDepth += 1; + + sal_Int32 nPos = 6; + while( (nPos > 2) && (nPos > nMaxParaDepth) ) + { + mxLBGroupText->remove(nPos); + nPos--; + } + } + + updateControlStates(); +} + +void CustomAnimationTextAnimTabPage::update( STLPropertySet* pSet ) +{ + auto nPos = mxLBGroupText->get_active(); + if (nPos != -1) + { + sal_Int32 nTextGrouping = nPos - 1; + sal_Int32 nOldGrouping = -2; + + if(mpSet->getPropertyState( nHandleTextGrouping ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleTextGrouping ) >>= nOldGrouping; + + if( nTextGrouping != nOldGrouping ) + pSet->setPropertyValue( nHandleTextGrouping, Any( nTextGrouping ) ); + } + + if (nPos != 0) + { + bool bTextReverse = mxCBXReverse->get_active(); + bool bOldTextReverse = !bTextReverse; + + if(mpSet->getPropertyState( nHandleTextReverse ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleTextReverse ) >>= bOldTextReverse; + + if( bTextReverse != bOldTextReverse ) + pSet->setPropertyValue( nHandleTextReverse, Any( bTextReverse ) ); + + if( nPos > 1 ) + { + double fTextGroupingAuto = mxCBXGroupAuto->get_active() ? mxMFGroupAuto->get_value(FieldUnit::NONE) / 10.0 : -1.0; + double fOldTextGroupingAuto = -2.0; + + if(mpSet->getPropertyState( nHandleTextGroupingAuto ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleTextGroupingAuto ) >>= fOldTextGroupingAuto; + + if( fTextGroupingAuto != fOldTextGroupingAuto ) + pSet->setPropertyValue( nHandleTextGroupingAuto, Any( fTextGroupingAuto ) ); + } + } + //#i120049# impress crashes when modifying the "Random effects" animation + //effect's trigger condition to "Start effect on click of". + //If this control is disabled, we should ignore its value + if (mxCBXAnimateForm->get_sensitive()) + { + bool bAnimateForm = mxCBXAnimateForm->get_active(); + bool bOldAnimateForm = !bAnimateForm; + + if(mpSet->getPropertyState( nHandleAnimateForm ) != STLPropertyState::Ambiguous) + mpSet->getPropertyValue( nHandleAnimateForm ) >>= bOldAnimateForm; + + if( bAnimateForm != bOldAnimateForm ) + pSet->setPropertyValue( nHandleAnimateForm, Any( bAnimateForm ) ); + } +} + +void CustomAnimationTextAnimTabPage::updateControlStates() +{ + auto nPos = mxLBGroupText->get_active(); + + mxCBXGroupAuto->set_sensitive( nPos > 1 ); + mxMFGroupAuto->set_sensitive( nPos > 1 ); + mxCBXReverse->set_sensitive( nPos > 0 ); + + if( !mbHasVisibleShapes && nPos > 0 ) + { + mxCBXAnimateForm->set_active(false); + mxCBXAnimateForm->set_sensitive(false); + } + else + { + mxCBXAnimateForm->set_sensitive(true); + } +} + +IMPL_LINK_NOARG(CustomAnimationTextAnimTabPage, implSelectHdl, weld::ComboBox&, void) +{ + updateControlStates(); +} + +CustomAnimationDialog::CustomAnimationDialog(weld::Window* pParent, std::unique_ptr pSet, const OString& rPage) + : GenericDialogController(pParent, "modules/simpress/ui/customanimationproperties.ui", "CustomAnimationProperties") + , mxSet(std::move(pSet)) + , mxTabControl(m_xBuilder->weld_notebook("tabcontrol")) + , mxDurationTabPage(new CustomAnimationDurationTabPage(mxTabControl->get_page("timing"), mxSet.get())) + , mxEffectTabPage(new CustomAnimationEffectTabPage(mxTabControl->get_page("effect"), m_xDialog.get(), mxSet.get())) +{ + bool bHasText = false; + if( mxSet->getPropertyState( nHandleHasText ) != STLPropertyState::Ambiguous ) + mxSet->getPropertyValue( nHandleHasText ) >>= bHasText; + + if( bHasText ) + { + mxTextAnimTabPage.reset(new CustomAnimationTextAnimTabPage(mxTabControl->get_page("textanim"), mxSet.get())); + } + else + { + mxTabControl->remove_page("textanim"); + } + + if (!rPage.isEmpty()) + mxTabControl->set_current_page(rPage); +} + +CustomAnimationDialog::~CustomAnimationDialog() +{ +} + +STLPropertySet* CustomAnimationDialog::getResultSet() +{ + mxResultSet = createDefaultSet(); + + mxEffectTabPage->update( mxResultSet.get() ); + mxDurationTabPage->update( mxResultSet.get() ); + if (mxTextAnimTabPage) + mxTextAnimTabPage->update( mxResultSet.get() ); + + return mxResultSet.get(); +} + +std::unique_ptr CustomAnimationDialog::createDefaultSet() +{ + Any aEmpty; + + std::unique_ptr pSet(new STLPropertySet()); + pSet->setPropertyDefaultValue( nHandleMaxParaDepth, Any( sal_Int32(-1) ) ); + + pSet->setPropertyDefaultValue( nHandleHasAfterEffect, Any( false ) ); + pSet->setPropertyDefaultValue( nHandleAfterEffectOnNextEffect, Any( false ) ); + pSet->setPropertyDefaultValue( nHandleDimColor, aEmpty ); + pSet->setPropertyDefaultValue( nHandleIterateType, Any( sal_Int16(0) ) ); + pSet->setPropertyDefaultValue( nHandleIterateInterval, Any( 0.0 ) ); + + pSet->setPropertyDefaultValue( nHandleStart, Any( sal_Int16(EffectNodeType::ON_CLICK) ) ); + pSet->setPropertyDefaultValue( nHandleBegin, Any( 0.0 ) ); + pSet->setPropertyDefaultValue( nHandleDuration, Any( 2.0 ) ); + pSet->setPropertyDefaultValue( nHandleRepeat, aEmpty ); + pSet->setPropertyDefaultValue( nHandleRewind, Any( AnimationFill::HOLD ) ); + + pSet->setPropertyDefaultValue( nHandleEnd, aEmpty ); + + pSet->setPropertyDefaultValue( nHandlePresetId, aEmpty ); + pSet->setPropertyDefaultValue( nHandleProperty1Type, Any( nPropertyTypeNone ) ); + pSet->setPropertyDefaultValue( nHandleProperty1Value, aEmpty ); + pSet->setPropertyDefaultValue( nHandleProperty2Type, Any( nPropertyTypeNone ) ); + pSet->setPropertyDefaultValue( nHandleProperty2Value, aEmpty ); + pSet->setPropertyDefaultValue( nHandleAccelerate, aEmpty ); + pSet->setPropertyDefaultValue( nHandleDecelerate, aEmpty ); + pSet->setPropertyDefaultValue( nHandleAutoReverse, aEmpty ); + pSet->setPropertyDefaultValue( nHandleTrigger, aEmpty ); + + pSet->setPropertyDefaultValue( nHandleHasText, Any( false ) ); + pSet->setPropertyDefaultValue( nHandleHasVisibleShape, Any( false ) ); + pSet->setPropertyDefaultValue( nHandleTextGrouping, Any( sal_Int32(-1) ) ); + pSet->setPropertyDefaultValue( nHandleAnimateForm, Any( true ) ); + pSet->setPropertyDefaultValue( nHandleTextGroupingAuto, Any( -1.0 ) ); + pSet->setPropertyDefaultValue( nHandleTextReverse, Any( false ) ); + + pSet->setPropertyDefaultValue( nHandleCurrentPage, aEmpty ); + + pSet->setPropertyDefaultValue( nHandleSoundURL, aEmpty ); + pSet->setPropertyDefaultValue( nHandleSoundVolume, Any( 1.0) ); + pSet->setPropertyDefaultValue( nHandleSoundEndAfterSlide, Any( sal_Int32(0) ) ); + + pSet->setPropertyDefaultValue( nHandleCommand, Any( sal_Int16(0) ) ); + return pSet; +} + +std::unique_ptr SdPropertySubControl::create(sal_Int32 nType, weld::Label* pLabel, weld::Container* pParent, weld::Window* pTopLevel, const Any& rValue, const OUString& rPresetId, const Link& rModifyHdl) +{ + std::unique_ptr pSubControl; + switch( nType ) + { + case nPropertyTypeDirection: + case nPropertyTypeSpokes: + case nPropertyTypeZoom: + pSubControl.reset( new SdPresetPropertyBox( pLabel, pParent, rValue, rPresetId, rModifyHdl ) ); + break; + + case nPropertyTypeColor: + case nPropertyTypeFillColor: + case nPropertyTypeFirstColor: + case nPropertyTypeCharColor: + case nPropertyTypeLineColor: + pSubControl.reset( new SdColorPropertyBox( pLabel, pParent, pTopLevel, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeFont: + pSubControl.reset( new SdFontPropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeCharHeight: + pSubControl.reset( new SdCharHeightPropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeRotate: + pSubControl.reset( new SdRotationPropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeTransparency: + pSubControl.reset( new SdTransparencyPropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeScale: + pSubControl.reset( new SdScalePropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + + case nPropertyTypeCharDecoration: + pSubControl.reset( new SdFontStylePropertyBox( pLabel, pParent, rValue, rModifyHdl ) ); + break; + } + + return pSubControl; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/CustomAnimationDialog.hxx b/sd/source/ui/animations/CustomAnimationDialog.hxx new file mode 100644 index 000000000..b8a8abcff --- /dev/null +++ b/sd/source/ui/animations/CustomAnimationDialog.hxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd { + +// property handles +const sal_Int32 nHandleSound = 0; +const sal_Int32 nHandleHasAfterEffect = 1; +const sal_Int32 nHandleIterateType = 2; +const sal_Int32 nHandleIterateInterval = 3; +const sal_Int32 nHandleStart = 4; +const sal_Int32 nHandleBegin = 5; +const sal_Int32 nHandleDuration = 6; +const sal_Int32 nHandleRepeat = 7; +const sal_Int32 nHandleRewind = 8; +const sal_Int32 nHandleEnd = 9; +const sal_Int32 nHandleAfterEffectOnNextEffect = 10; +const sal_Int32 nHandleDimColor = 11; +const sal_Int32 nHandleMaxParaDepth = 12; +const sal_Int32 nHandlePresetId = 13; +const sal_Int32 nHandleProperty1Type = 14; +const sal_Int32 nHandleProperty1Value = 15; +const sal_Int32 nHandleProperty2Type = 16; +const sal_Int32 nHandleProperty2Value = 17; + +const sal_Int32 nHandleAccelerate = 18; +const sal_Int32 nHandleDecelerate = 19; +const sal_Int32 nHandleAutoReverse = 20; +const sal_Int32 nHandleTrigger = 21; + +const sal_Int32 nHandleHasText = 22; +const sal_Int32 nHandleTextGrouping = 23; +const sal_Int32 nHandleAnimateForm = 24; +const sal_Int32 nHandleTextGroupingAuto = 25; +const sal_Int32 nHandleTextReverse = 26; + +const sal_Int32 nHandleCurrentPage = 27; +const sal_Int32 nHandleSoundURL = 28; +const sal_Int32 nHandleSoundVolume = 29; +const sal_Int32 nHandleSoundEndAfterSlide = 30; + +const sal_Int32 nHandleCommand = 31; + +const sal_Int32 nHandleHasVisibleShape = 32; + +const sal_Int32 nPropertyTypeNone = 0; +const sal_Int32 nPropertyTypeDirection = 1; +const sal_Int32 nPropertyTypeSpokes = 2; +const sal_Int32 nPropertyTypeFirstColor = 3; +const sal_Int32 nPropertyTypeSecondColor = 4; +const sal_Int32 nPropertyTypeZoom = 5; +const sal_Int32 nPropertyTypeFillColor = 6; +const sal_Int32 nPropertyTypeColorStyle = 7; +const sal_Int32 nPropertyTypeFont = 8; +const sal_Int32 nPropertyTypeCharHeight = 9; +const sal_Int32 nPropertyTypeCharColor = 10; +const sal_Int32 nPropertyTypeCharHeightStyle = 11; +const sal_Int32 nPropertyTypeCharDecoration = 12; +const sal_Int32 nPropertyTypeLineColor = 13; +const sal_Int32 nPropertyTypeRotate = 14; +const sal_Int32 nPropertyTypeColor = 15; +const sal_Int32 nPropertyTypeAccelerate = 16; +const sal_Int32 nPropertyTypeDecelerate = 17; +const sal_Int32 nPropertyTypeAutoReverse = 18; +const sal_Int32 nPropertyTypeTransparency = 19; +const sal_Int32 nPropertyTypeFontStyle = 20; +const sal_Int32 nPropertyTypeScale = 21; + +class SdPropertySubControl +{ +public: + explicit SdPropertySubControl(weld::Container* pParent); + virtual ~SdPropertySubControl(); + + virtual css::uno::Any getValue() = 0; + virtual void setValue( const css::uno::Any& rValue, const OUString& rPresetId ) = 0; + + static std::unique_ptr + create( sal_Int32 nType, + weld::Label* pLabel, + weld::Container* pParent, + weld::Window* pTopLevel, + const css::uno::Any& rValue, + const OUString& rPresetId, + const Link& rModifyHdl ); + +protected: + std::unique_ptr mxBuilder; + std::unique_ptr mxContainer; + weld::Container* mpParent; +}; + +class CustomAnimationDurationTabPage; +class CustomAnimationEffectTabPage; +class CustomAnimationTextAnimTabPage; +class STLPropertySet; + +class CustomAnimationDialog : public weld::GenericDialogController +{ +public: + CustomAnimationDialog(weld::Window* pParent, std::unique_ptr pSet, const OString& Page); + virtual ~CustomAnimationDialog() override; + + STLPropertySet* getResultSet(); + STLPropertySet* getPropertySet() const { return mxSet.get(); } + + static std::unique_ptr createDefaultSet(); + +private: + std::unique_ptr mxSet; + std::unique_ptr mxResultSet; + + std::unique_ptr mxTabControl; + std::unique_ptr mxDurationTabPage; + std::unique_ptr mxEffectTabPage; + std::unique_ptr mxTextAnimTabPage; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/CustomAnimationList.cxx b/sd/source/ui/animations/CustomAnimationList.cxx new file mode 100644 index 000000000..cc85ed74f --- /dev/null +++ b/sd/source/ui/animations/CustomAnimationList.cxx @@ -0,0 +1,1231 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::presentation; + +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::text::XTextRange; +using ::com::sun::star::drawing::XShape; +using ::com::sun::star::drawing::XShapes; +using ::com::sun::star::drawing::XDrawPage; +using ::com::sun::star::container::XChild; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::beans::XPropertySetInfo; + +namespace sd { + +// go recursively through all shapes in the given XShapes collection and return true as soon as the +// given shape is found. nIndex is incremented for each shape with the same shape type as the given +// shape is found until the given shape is found. +static bool getShapeIndex( const Reference< XShapes >& xShapes, const Reference< XShape >& xShape, sal_Int32& nIndex ) +{ + const sal_Int32 nCount = xShapes->getCount(); + sal_Int32 n; + for( n = 0; n < nCount; n++ ) + { + Reference< XShape > xChild; + xShapes->getByIndex( n ) >>= xChild; + if( xChild == xShape ) + return true; + + if( xChild->getShapeType() == xShape->getShapeType() ) + nIndex++; + + Reference< XShapes > xChildContainer( xChild, UNO_QUERY ); + if( xChildContainer.is() ) + { + if( getShapeIndex( xChildContainer, xShape, nIndex ) ) + return true; + } + } + + return false; +} + +// returns the index of the shape type from the given shape +static sal_Int32 getShapeIndex( const Reference< XShape >& xShape ) +{ + Reference< XChild > xChild( xShape, UNO_QUERY ); + Reference< XShapes > xPage; + + while( xChild.is() && !xPage.is() ) + { + Reference< XInterface > x( xChild->getParent() ); + xChild.set( x, UNO_QUERY ); + Reference< XDrawPage > xTestPage( x, UNO_QUERY ); + if( xTestPage.is() ) + xPage.set( x, UNO_QUERY ); + } + + sal_Int32 nIndex = 1; + + if( xPage.is() && getShapeIndex( xPage, xShape, nIndex ) ) + return nIndex; + else + return -1; +} + +OUString getShapeDescription( const Reference< XShape >& xShape, bool bWithText ) +{ + OUString aDescription; + Reference< XPropertySet > xSet( xShape, UNO_QUERY ); + bool bAppendIndex = true; + + if(xSet.is()) try + { + Reference xInfo(xSet->getPropertySetInfo()); + if (xInfo.is()) + { + static const OUStringLiteral aPropName1(u"Name"); + if(xInfo->hasPropertyByName(aPropName1)) + xSet->getPropertyValue(aPropName1) >>= aDescription; + + bAppendIndex = aDescription.isEmpty(); + + static const OUStringLiteral aPropName2(u"UINameSingular"); + if(xInfo->hasPropertyByName(aPropName2)) + xSet->getPropertyValue(aPropName2) >>= aDescription; + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::getShapeDescription()" ); + } + + if (bAppendIndex) + { + aDescription += " " + OUString::number(getShapeIndex(xShape)); + } + + if( bWithText ) + { + Reference< XTextRange > xText( xShape, UNO_QUERY ); + if( xText.is() ) + { + OUString aText( xText->getString() ); + if( !aText.isEmpty() ) + { + aDescription += ": "; + + aText = aText.replace( '\n', ' ' ); + aText = aText.replace( '\r', ' ' ); + + aDescription += aText; + } + } + } + return aDescription; +} + +static OUString getDescription( const Any& rTarget, bool bWithText ) +{ + OUString aDescription; + + if( rTarget.getValueType() == ::cppu::UnoType::get() ) + { + ParagraphTarget aParaTarget; + rTarget >>= aParaTarget; + + css::uno::Reference xLockable(aParaTarget.Shape, css::uno::UNO_QUERY); + if (xLockable.is()) + xLockable->addActionLock(); + comphelper::ScopeGuard aGuard([&xLockable]() + { + if (xLockable.is()) + xLockable->removeActionLock(); + }); + + Reference< XEnumerationAccess > xText( aParaTarget.Shape, UNO_QUERY_THROW ); + Reference< XEnumeration > xEnumeration( xText->createEnumeration(), css::uno::UNO_SET_THROW ); + sal_Int32 nPara = aParaTarget.Paragraph; + + while( xEnumeration->hasMoreElements() && nPara ) + { + xEnumeration->nextElement(); + nPara--; + } + + DBG_ASSERT( xEnumeration->hasMoreElements(), "sd::CustomAnimationEffect::prepareText(), paragraph out of range!" ); + + if( xEnumeration->hasMoreElements() ) + { + Reference< XTextRange > xParagraph; + xEnumeration->nextElement() >>= xParagraph; + + if( xParagraph.is() ) + aDescription = xParagraph->getString(); + } + } + else + { + Reference< XShape > xShape; + rTarget >>= xShape; + if( xShape.is() ) + aDescription = getShapeDescription( xShape, bWithText ); + } + + return aDescription; +} + +class CustomAnimationListEntryItem +{ +public: + CustomAnimationListEntryItem(const OUString& aDescription, + const CustomAnimationEffectPtr& pEffect); + const CustomAnimationEffectPtr& getEffect() const { return mpEffect; } + + Size GetSize(const vcl::RenderContext& rRenderContext); + void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected); + void PaintEffect(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected); + void PaintTrigger(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect); + +private: + OUString msDescription; + OUString msEffectName; + CustomAnimationEffectPtr mpEffect; + +public: + static const ::tools::Long nIconWidth = 19; + static const ::tools::Long nItemMinHeight = 38; +}; + +CustomAnimationListEntryItem::CustomAnimationListEntryItem(const OUString& aDescription, const CustomAnimationEffectPtr& pEffect) + : msDescription(aDescription) + , mpEffect(pEffect) +{ + if (!mpEffect) + return; + switch (mpEffect->getPresetClass()) + { + case EffectPresetClass::ENTRANCE: + msEffectName = SdResId(STR_CUSTOMANIMATION_ENTRANCE); break; + case EffectPresetClass::EXIT: + msEffectName = SdResId(STR_CUSTOMANIMATION_EXIT); break; + case EffectPresetClass::EMPHASIS: + msEffectName = SdResId(STR_CUSTOMANIMATION_EMPHASIS); break; + case EffectPresetClass::MOTIONPATH: + msEffectName = SdResId(STR_CUSTOMANIMATION_MOTION_PATHS); break; + default: + msEffectName = SdResId(STR_CUSTOMANIMATION_MISC); break; + } + msEffectName = msEffectName.replaceFirst( "%1" , CustomAnimationPresets::getCustomAnimationPresets().getUINameForPresetId(mpEffect->getPresetId())); +} + +IMPL_STATIC_LINK(CustomAnimationList, CustomRenderHdl, weld::TreeView::render_args, aPayload, void) +{ + vcl::RenderContext& rRenderContext = std::get<0>(aPayload); + const ::tools::Rectangle& rRect = std::get<1>(aPayload); + bool bSelected = std::get<2>(aPayload); + const OUString& rId = std::get<3>(aPayload); + + CustomAnimationListEntryItem* pItem = weld::fromId(rId); + + pItem->Paint(rRenderContext, rRect, bSelected); +} + +IMPL_STATIC_LINK(CustomAnimationList, CustomGetSizeHdl, weld::TreeView::get_size_args, aPayload, Size) +{ + vcl::RenderContext& rRenderContext = aPayload.first; + const OUString& rId = aPayload.second; + + CustomAnimationListEntryItem* pItem = weld::fromId(rId); + if (!pItem) + return Size(CustomAnimationListEntryItem::nIconWidth, CustomAnimationListEntryItem::nItemMinHeight); + return pItem->GetSize(rRenderContext); +} + +Size CustomAnimationListEntryItem::GetSize(const vcl::RenderContext& rRenderContext) +{ + auto width = rRenderContext.GetTextWidth( msDescription ) + nIconWidth; + if (width < (rRenderContext.GetTextWidth( msEffectName ) + 2*nIconWidth)) + width = rRenderContext.GetTextWidth( msEffectName ) + 2*nIconWidth; + + Size aSize(width, rRenderContext.GetTextHeight()); + if (aSize.Height() < nItemMinHeight) + aSize.setHeight(nItemMinHeight); + return aSize; +} + +void CustomAnimationListEntryItem::PaintTrigger(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) +{ + Size aSize(rRect.GetSize()); + + ::tools::Rectangle aOutRect(rRect); + + // fill the background + Color aColor(rRenderContext.GetSettings().GetStyleSettings().GetDialogColor()); + + rRenderContext.Push(); + rRenderContext.SetFillColor(aColor); + rRenderContext.SetLineColor(); + rRenderContext.DrawRect(aOutRect); + + // Erase the four corner pixels to make the rectangle appear rounded. + rRenderContext.SetLineColor(rRenderContext.GetSettings().GetStyleSettings().GetWindowColor()); + rRenderContext.DrawPixel(aOutRect.TopLeft()); + rRenderContext.DrawPixel(Point(aOutRect.Right(), aOutRect.Top())); + rRenderContext.DrawPixel(Point(aOutRect.Left(), aOutRect.Bottom())); + rRenderContext.DrawPixel(Point(aOutRect.Right(), aOutRect.Bottom())); + + // draw the category title + + int nVertBorder = ((aSize.Height() - rRenderContext.GetTextHeight()) >> 1); + int nHorzBorder = rRenderContext.LogicToPixel(Size(3, 3), MapMode(MapUnit::MapAppFont)).Width(); + + aOutRect.AdjustLeft(nHorzBorder ); + aOutRect.AdjustRight( -nHorzBorder ); + aOutRect.AdjustTop( nVertBorder ); + aOutRect.AdjustBottom( -nVertBorder ); + + rRenderContext.DrawText(aOutRect, rRenderContext.GetEllipsisString(msDescription, aOutRect.GetWidth())); + rRenderContext.Pop(); +} + +void CustomAnimationListEntryItem::PaintEffect(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected) +{ + rRenderContext.Push(vcl::PushFlags::TEXTCOLOR); + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + if (bSelected) + rRenderContext.SetTextColor(rStyleSettings.GetHighlightTextColor()); + else + rRenderContext.SetTextColor(rStyleSettings.GetDialogTextColor()); + + Point aPos(rRect.TopLeft()); + int nItemHeight = rRect.GetHeight(); + + sal_Int16 nNodeType = mpEffect->getNodeType(); + if (nNodeType == EffectNodeType::ON_CLICK ) + { + rRenderContext.DrawImage(aPos, Image(StockImage::Yes, BMP_CUSTOMANIMATION_ON_CLICK)); + } + else if (nNodeType == EffectNodeType::AFTER_PREVIOUS) + { + rRenderContext.DrawImage(aPos, Image(StockImage::Yes, BMP_CUSTOMANIMATION_AFTER_PREVIOUS)); + } + else if (nNodeType == EffectNodeType::WITH_PREVIOUS) + { + //FIXME With previous image not defined in CustomAnimation.src + } + + aPos.AdjustX(nIconWidth); + + //TODO, full width of widget ? + rRenderContext.DrawText(aPos, rRenderContext.GetEllipsisString(msDescription, rRect.GetWidth())); + + aPos.AdjustY(nIconWidth); + + OUString sImage; + switch (mpEffect->getPresetClass()) + { + case EffectPresetClass::ENTRANCE: + sImage = BMP_CUSTOMANIMATION_ENTRANCE_EFFECT; break; + case EffectPresetClass::EXIT: + sImage = BMP_CUSTOMANIMATION_EXIT_EFFECT; break; + case EffectPresetClass::EMPHASIS: + sImage = BMP_CUSTOMANIMATION_EMPHASIS_EFFECT; break; + case EffectPresetClass::MOTIONPATH: + sImage = BMP_CUSTOMANIMATION_MOTION_PATH; break; + case EffectPresetClass::OLEACTION: + sImage = BMP_CUSTOMANIMATION_OLE; break; + case EffectPresetClass::MEDIACALL: + switch (mpEffect->getCommand()) + { + case EffectCommands::TOGGLEPAUSE: + sImage = BMP_CUSTOMANIMATION_MEDIA_PAUSE; break; + case EffectCommands::STOP: + sImage = BMP_CUSTOMANIMATION_MEDIA_STOP; break; + case EffectCommands::PLAY: + default: + sImage = BMP_CUSTOMANIMATION_MEDIA_PLAY; break; + } + break; + default: + break; + } + + if (!sImage.isEmpty()) + { + Image aImage(StockImage::Yes, sImage); + Point aImagePos(aPos); + aImagePos.AdjustY((nItemHeight/2 - aImage.GetSizePixel().Height()) >> 1 ); + rRenderContext.DrawImage(aImagePos, aImage); + } + + aPos.AdjustX(nIconWidth ); + aPos.AdjustY((nItemHeight/2 - rRenderContext.GetTextHeight()) >> 1 ); + + rRenderContext.DrawText(aPos, rRenderContext.GetEllipsisString(msEffectName, rRect.GetWidth())); + rRenderContext.Pop(); +} + +void CustomAnimationListEntryItem::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect, bool bSelected) +{ + if (mpEffect) + PaintEffect(rRenderContext, rRect, bSelected); + else + PaintTrigger(rRenderContext, rRect); +} + +CustomAnimationList::CustomAnimationList(std::unique_ptr xTreeView, + std::unique_ptr xLabel, + std::unique_ptr xScrolledWindow) + : mxTreeView(std::move(xTreeView)) + , maDropTargetHelper(*this) + , mxEmptyLabel(std::move(xLabel)) + , mxEmptyLabelParent(std::move(xScrolledWindow)) + , mbIgnorePaint(false) + , mpController(nullptr) + , mnLastGroupId(0) + , mnPostExpandEvent(nullptr) + , mnPostCollapseEvent(nullptr) +{ + mxEmptyLabel->set_stack_background(); + + mxTreeView->set_selection_mode(SelectionMode::Multiple); + mxTreeView->connect_changed(LINK(this, CustomAnimationList, SelectHdl)); + mxTreeView->connect_key_press(LINK(this, CustomAnimationList, KeyInputHdl)); + mxTreeView->connect_popup_menu(LINK(this, CustomAnimationList, CommandHdl)); + mxTreeView->connect_row_activated(LINK(this, CustomAnimationList, DoubleClickHdl)); + mxTreeView->connect_expanding(LINK(this, CustomAnimationList, ExpandHdl)); + mxTreeView->connect_collapsing(LINK(this, CustomAnimationList, CollapseHdl)); + mxTreeView->connect_drag_begin(LINK(this, CustomAnimationList, DragBeginHdl)); + mxTreeView->connect_custom_get_size(LINK(this, CustomAnimationList, CustomGetSizeHdl)); + mxTreeView->connect_custom_render(LINK(this, CustomAnimationList, CustomRenderHdl)); + mxTreeView->set_column_custom_renderer(0, true); +} + +CustomAnimationListDropTarget::CustomAnimationListDropTarget(CustomAnimationList& rTreeView) + : DropTargetHelper(rTreeView.get_widget().get_drop_target()) + , m_rTreeView(rTreeView) +{ +} + +sal_Int8 CustomAnimationListDropTarget::AcceptDrop(const AcceptDropEvent& rEvt) +{ + sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt); + + if (nAccept != DND_ACTION_NONE) + { + // to enable the autoscroll when we're close to the edges + weld::TreeView& rWidget = m_rTreeView.get_widget(); + rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true); + } + + return nAccept; +} + +sal_Int8 CustomAnimationListDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + return m_rTreeView.ExecuteDrop(rEvt); +} + +// D'n'D #1: Record selected effects for drag'n'drop. +IMPL_LINK(CustomAnimationList, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = false; + + // Record which effects are selected: + // Since NextSelected(..) iterates through the selected items in the order they + // were selected, create a sorted list for simpler drag'n'drop algorithms. + mDndEffectsSelected.clear(); + mxTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + mDndEffectsSelected.emplace_back(mxTreeView->make_iterator(&rEntry)); + return false; + }); + + // Note: pEntry is the effect with focus (if multiple effects are selected) + mxDndEffectDragging = mxTreeView->make_iterator(); + if (!mxTreeView->get_cursor(mxDndEffectDragging.get())) + mxDndEffectDragging.reset(); + + // Allow normal processing. + return false; +} + +// D'n'D #3: Called each time mouse moves during drag +sal_Int8 CustomAnimationList::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 ret = DND_ACTION_NONE; + + const bool bIsMove = DND_ACTION_MOVE == rEvt.mnAction; + if (mxDndEffectDragging && !rEvt.mbLeaving && bIsMove) + ret = DND_ACTION_MOVE; + return ret; +} + +// D'n'D #5: Tell model to update effect order. +sal_Int8 CustomAnimationList::ExecuteDrop(const ExecuteDropEvent& rEvt) +{ + std::unique_ptr xDndEffectInsertBefore(mxTreeView->make_iterator()); + if (!mxTreeView->get_dest_row_at_pos(rEvt.maPosPixel, xDndEffectInsertBefore.get(), true)) + xDndEffectInsertBefore.reset(); + + const bool bMovingEffect = ( mxDndEffectDragging != nullptr ); + const bool bMoveNotSelf = !xDndEffectInsertBefore || (mxDndEffectDragging && mxTreeView->iter_compare(*xDndEffectInsertBefore, *mxDndEffectDragging) != 0); + const bool bHaveSequence(mpMainSequence); + + if( bMovingEffect && bMoveNotSelf && bHaveSequence ) + { + CustomAnimationListEntryItem* pTarget = xDndEffectInsertBefore ? + weld::fromId(mxTreeView->get_id(*xDndEffectInsertBefore)) : + nullptr; + + // Build list of effects + std::vector< CustomAnimationEffectPtr > aEffects; + for( const auto &pEntry : mDndEffectsSelected ) + { + CustomAnimationListEntryItem* pCustomAnimationEffect = weld::fromId(mxTreeView->get_id(*pEntry)); + aEffects.push_back(pCustomAnimationEffect->getEffect()); + } + + // Callback to observer to have it update the model. + // If pTarget is null, pass nullptr to indicate end of list. + mpController->onDragNDropComplete( + std::move(aEffects), + pTarget ? pTarget->getEffect() : nullptr ); + + // Reset selection + mxTreeView->select(*mxDndEffectDragging); + Select(); + } + + // NOTE: Don't call default handler because all required + // move operations have been completed here to update the model. + return DND_ACTION_NONE; +} + +CustomAnimationList::~CustomAnimationList() +{ + if (mnPostExpandEvent) + { + Application::RemoveUserEvent(mnPostExpandEvent); + mnPostExpandEvent = nullptr; + } + + if (mnPostCollapseEvent) + { + Application::RemoveUserEvent(mnPostCollapseEvent); + mnPostCollapseEvent = nullptr; + } + + if( mpMainSequence ) + mpMainSequence->removeListener( this ); + + clear(); +} + +IMPL_LINK(CustomAnimationList, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch (nKeyCode) + { + case KEY_DELETE: + mpController->onContextMenu("remove"); + return true; + case KEY_INSERT: + mpController->onContextMenu("create"); + return true; + case KEY_SPACE: + { + std::unique_ptr xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_cursor(xEntry.get())) + { + auto aRect = mxTreeView->get_row_area(*xEntry); + const Point aPos(aRect.getWidth() / 2, aRect.getHeight() / 2); + const CommandEvent aCEvt(aPos, CommandEventId::ContextMenu); + CommandHdl(aCEvt); + return true; + } + } + } + return false; +} + +/** selects or deselects the given effect. + Selections of other effects are not changed */ +void CustomAnimationList::select( const CustomAnimationEffectPtr& pEffect ) +{ + CustomAnimationListEntryItem* pEntry = nullptr; + + std::unique_ptr xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pTestEntry = weld::fromId(mxTreeView->get_id(*xEntry)); + if (pTestEntry->getEffect() == pEffect) + { + mxTreeView->select(*xEntry); + mxTreeView->scroll_to_row(*xEntry); + pEntry = pTestEntry; + break; + } + } while (mxTreeView->iter_next(*xEntry)); + } + + if( !pEntry ) + { + append( pEffect ); + select( pEffect ); + } +} + +void CustomAnimationList::clear() +{ + mxEntries.clear(); + mxTreeView->clear(); + + mxEmptyLabelParent->show(); + mxTreeView->hide(); + + mxLastParentEntry.reset(); + mxLastTargetShape = nullptr; +} + +void CustomAnimationList::update( const MainSequencePtr& pMainSequence ) +{ + if( mpMainSequence ) + mpMainSequence->removeListener( this ); + + mpMainSequence = pMainSequence; + update(); + + if( mpMainSequence ) + mpMainSequence->addListener( this ); +} + +struct stl_append_effect_func +{ + explicit stl_append_effect_func( CustomAnimationList& rList ) : mrList( rList ) {} + void operator()(const CustomAnimationEffectPtr& pEffect); + CustomAnimationList& mrList; +}; + +void stl_append_effect_func::operator()(const CustomAnimationEffectPtr& pEffect) +{ + mrList.append( pEffect ); +} + +void CustomAnimationList::update() +{ + mbIgnorePaint = true; + + std::vector< CustomAnimationEffectPtr > aVisible; + std::vector< CustomAnimationEffectPtr > aSelected; + CustomAnimationEffectPtr aCurrent; + + CustomAnimationEffectPtr pFirstSelEffect; + CustomAnimationEffectPtr pLastSelEffect; + ::tools::Long nFirstVis = -1; + ::tools::Long nLastVis = -1; + ::tools::Long nFirstSelOld = -1; + ::tools::Long nLastSelOld = -1; + + std::unique_ptr xEntry = mxTreeView->make_iterator(); + + if( mpMainSequence ) + { + std::unique_ptr xLastSelectedEntry; + std::unique_ptr xLastVisibleEntry; + + // save selection, current, and expand (visible) states + mxTreeView->all_foreach([this, &aVisible, &nFirstVis, &xLastVisibleEntry, + &aSelected, &nFirstSelOld, &pFirstSelEffect, &xLastSelectedEntry](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = weld::fromId(mxTreeView->get_id(rEntry)); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect) + { + if (weld::IsEntryVisible(*mxTreeView, rEntry)) + { + aVisible.push_back(pEffect); + // save scroll position + if (nFirstVis == -1) + nFirstVis = weld::GetAbsPos(*mxTreeView, rEntry); + if (!xLastVisibleEntry) + xLastVisibleEntry = mxTreeView->make_iterator(&rEntry); + else + mxTreeView->copy_iterator(rEntry, *xLastVisibleEntry); + } + + if (mxTreeView->is_selected(rEntry)) + { + aSelected.push_back(pEffect); + if (nFirstSelOld == -1) + { + pFirstSelEffect = pEffect; + nFirstSelOld = weld::GetAbsPos(*mxTreeView, rEntry); + } + if (!xLastSelectedEntry) + xLastSelectedEntry = mxTreeView->make_iterator(&rEntry); + else + mxTreeView->copy_iterator(rEntry, *xLastSelectedEntry); + } + } + + return false; + }); + + if (xLastSelectedEntry) + { + CustomAnimationListEntryItem* pEntry = weld::fromId(mxTreeView->get_id(*xLastSelectedEntry)); + pLastSelEffect = pEntry->getEffect(); + nLastSelOld = weld::GetAbsPos(*mxTreeView, *xLastSelectedEntry); + } + + if (xLastVisibleEntry) + nLastVis = weld::GetAbsPos(*mxTreeView, *xLastVisibleEntry); + + if (mxTreeView->get_cursor(xEntry.get())) + { + CustomAnimationListEntryItem* pEntry = weld::fromId(mxTreeView->get_id(*xEntry)); + aCurrent = pEntry->getEffect(); + } + } + + // rebuild list + + mxTreeView->freeze(); + + clear(); + + if (mpMainSequence) + { + std::for_each( mpMainSequence->getBegin(), mpMainSequence->getEnd(), stl_append_effect_func( *this ) ); + mxLastParentEntry.reset(); + + auto rInteractiveSequenceVector = mpMainSequence->getInteractiveSequenceVector(); + + for (InteractiveSequencePtr const& pIS : rInteractiveSequenceVector) + { + Reference< XShape > xShape( pIS->getTriggerShape() ); + if( xShape.is() ) + { + OUString aDescription = SdResId(STR_CUSTOMANIMATION_TRIGGER) + ": " + + getShapeDescription( xShape, false ); + + mxEntries.emplace_back(std::make_unique(aDescription, nullptr)); + + OUString sId(weld::toId(mxEntries.back().get())); + mxTreeView->insert(nullptr, -1, &aDescription, &sId, nullptr, nullptr, false, nullptr); + std::for_each( pIS->getBegin(), pIS->getEnd(), stl_append_effect_func( *this ) ); + mxLastParentEntry.reset(); + } + } + } + + mxTreeView->thaw(); + + if (mxTreeView->n_children()) + { + mxEmptyLabelParent->hide(); + mxTreeView->show(); + } + + if (mpMainSequence) + { + ::tools::Long nFirstSelNew = -1; + ::tools::Long nLastSelNew = -1; + + std::vector> aNewSelection; + + // restore selection state, expand state, and current-entry (under cursor) + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pEntry = weld::fromId(mxTreeView->get_id(*xEntry)); + + CustomAnimationEffectPtr pEffect( pEntry->getEffect() ); + if (pEffect) + { + // Any effects that were visible should still be visible, so expand their parents. + // (a previously expanded parent may have moved leaving a child to now be the new parent to expand) + if( std::find( aVisible.begin(), aVisible.end(), pEffect ) != aVisible.end() ) + { + if (mxTreeView->get_iter_depth(*xEntry)) + { + std::unique_ptr xParentEntry = mxTreeView->make_iterator(xEntry.get()); + mxTreeView->iter_parent(*xParentEntry); + mxTreeView->expand_row(*xParentEntry); + } + } + + if( std::find( aSelected.begin(), aSelected.end(), pEffect ) != aSelected.end() ) + aNewSelection.emplace_back(mxTreeView->make_iterator(xEntry.get())); + + // Restore the cursor, as it may deselect other effects wait until + // after the loop to reset the selection + if( pEffect == aCurrent ) + mxTreeView->set_cursor(*xEntry); + + if (pEffect == pFirstSelEffect) + nFirstSelNew = weld::GetAbsPos(*mxTreeView, *xEntry); + + if (pEffect == pLastSelEffect) + nLastSelNew = weld::GetAbsPos(*mxTreeView, *xEntry); + } + } while (mxTreeView->iter_next(*xEntry)); + } + + // tdf#147032 unselect what previous set_cursor may have caused to get selected as a side-effect + mxTreeView->unselect_all(); + for (const auto& rEntry : aNewSelection) + mxTreeView->select(*rEntry); + + // Scroll to a selected entry, depending on where the selection moved. + const bool bMoved = nFirstSelNew != nFirstSelOld; + const bool bMovedUp = nFirstSelNew < nFirstSelOld; + const bool bMovedDown = nFirstSelNew > nFirstSelOld; + + if( bMoved && nLastSelOld < nFirstVis && nLastSelNew < nFirstVis ) + { + // The selection is above the visible area. + // Scroll up to show the last few selected entries. + if( nLastSelNew - (nLastVis - nFirstVis) > nFirstSelNew) + { + // The entries in the selection range can't fit in view. + // Scroll so the last selected entry is last in view. + mxTreeView->vadjustment_set_value(nLastSelNew - (nLastVis - nFirstVis)); + } + else + mxTreeView->vadjustment_set_value(nFirstSelNew); + } + else if( bMoved && nFirstSelOld > nLastVis && nFirstSelNew > nLastVis ) + { + // The selection is below the visible area. + // Scroll down to the first few selected entries. + mxTreeView->vadjustment_set_value(nFirstSelNew); + } + else if( bMovedUp && nFirstSelOld <= nFirstVis ) + { + // A visible entry has moved up out of view; scroll up one. + mxTreeView->vadjustment_set_value(nFirstVis - 1); + } + else if( bMovedDown && nLastSelOld >= nLastVis ) + { + // An entry has moved down out of view; scroll down one. + mxTreeView->vadjustment_set_value(nFirstVis + 1); + } + else if ( nFirstVis != -1 ) + { + // The selection is still in view, or it hasn't moved. + mxTreeView->vadjustment_set_value(nFirstVis); + } + } + + mbIgnorePaint = false; + + Select(); +} + +void CustomAnimationList::append( CustomAnimationEffectPtr pEffect ) +{ + Any aTarget( pEffect->getTarget() ); + if( !aTarget.hasValue() ) + return; + + try + { + // create a ui description + OUString aDescription = getDescription(aTarget, pEffect->getTargetSubItem() != ShapeAnimationSubType::ONLY_BACKGROUND); + + std::unique_ptr xParentEntry; + + Reference< XShape > xTargetShape( pEffect->getTargetShape() ); + sal_Int32 nGroupId = pEffect->getGroupId(); + + // if this effect has the same target and group-id as the last root effect, + // the last root effect is also this effects parent + if (mxLastParentEntry && nGroupId != -1 && mxLastTargetShape == xTargetShape && mnLastGroupId == nGroupId) + xParentEntry = mxTreeView->make_iterator(mxLastParentEntry.get()); + + // create an entry for the effect + std::unique_ptr xEntry = mxTreeView->make_iterator(); + + mxEntries.emplace_back(std::make_unique(aDescription, pEffect)); + + OUString sId(weld::toId(mxEntries.back().get())); + + if (xParentEntry) + { + // add a subentry + mxTreeView->insert(xParentEntry.get(), -1, &aDescription, &sId, nullptr, nullptr, false, xEntry.get()); + } + else + { + // add a root entry + mxTreeView->insert(nullptr, -1, &aDescription, &sId, nullptr, nullptr, false, xEntry.get()); + + // and the new root entry becomes the possible next group header + mxLastTargetShape = xTargetShape; + mnLastGroupId = nGroupId; + mxLastParentEntry = std::move(xEntry); + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationList::append()" ); + } +} + +static void selectShape(weld::TreeView* pTreeList, const Reference< XShape >& xShape ) +{ + std::unique_ptr xEntry = pTreeList->make_iterator(); + if (!pTreeList->get_iter_first(*xEntry)) + return; + + bool bFirstEntry = true; + + do + { + CustomAnimationListEntryItem* pEntry = weld::fromId(pTreeList->get_id(*xEntry)); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect) + { + if (pEffect->getTarget() == xShape) + { + pTreeList->select(*xEntry); + if (bFirstEntry) + { + pTreeList->scroll_to_row(*xEntry); + bFirstEntry = false; + } + } + } + } while (pTreeList->iter_next(*xEntry)); +} + +void CustomAnimationList::onSelectionChanged(const Any& rSelection) +{ + try + { + mxTreeView->unselect_all(); + + if (rSelection.hasValue()) + { + Reference< XIndexAccess > xShapes(rSelection, UNO_QUERY); + if( xShapes.is() ) + { + sal_Int32 nCount = xShapes->getCount(); + sal_Int32 nIndex; + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + Reference< XShape > xShape( xShapes->getByIndex( nIndex ), UNO_QUERY ); + if( xShape.is() ) + selectShape(mxTreeView.get(), xShape); + } + } + else + { + Reference< XShape > xShape(rSelection, UNO_QUERY); + if( xShape.is() ) + selectShape(mxTreeView.get(), xShape); + } + } + + Select(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationList::onSelectionChanged()" ); + } +} + +IMPL_LINK_NOARG(CustomAnimationList, SelectHdl, weld::TreeView&, void) +{ + Select(); +} + +// Notify controller to refresh UI when we are notified of selection change from base class +void CustomAnimationList::Select() +{ + if( mbIgnorePaint ) + return; + mpController->onSelect(); +} + +IMPL_LINK_NOARG(CustomAnimationList, PostExpandHdl, void*, void) +{ + std::unique_ptr xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_selected(xEntry.get())) + { + for (bool bChild = mxTreeView->iter_children(*xEntry); bChild; bChild = mxTreeView->iter_next_sibling(*xEntry)) + { + if (!mxTreeView->is_selected(*xEntry)) + mxTreeView->select(*xEntry); + } + } + + // Notify controller that selection has changed (it should update the UI) + mpController->onSelect(); + + mnPostExpandEvent = nullptr; +} + +IMPL_LINK(CustomAnimationList, ExpandHdl, const weld::TreeIter&, rParent, bool) +{ + // If expanded entry is selected, then select its children too afterwards. + if (mxTreeView->is_selected(rParent) && !mnPostExpandEvent) { + mnPostExpandEvent = Application::PostUserEvent(LINK(this, CustomAnimationList, PostExpandHdl)); + } + + return true; +} + +IMPL_LINK_NOARG(CustomAnimationList, PostCollapseHdl, void*, void) +{ + // Deselect all entries as SvTreeListBox::Collapse selects the last + // entry to have focus (or its parent), which is not desired + mxTreeView->unselect_all(); + + // Restore selection state for entries which are still visible + for (const auto &pEntry : lastSelectedEntries) + { + if (weld::IsEntryVisible(*mxTreeView, *pEntry)) + mxTreeView->select(*pEntry); + } + + lastSelectedEntries.clear(); + + // Notify controller that selection has changed (it should update the UI) + mpController->onSelect(); + + mnPostCollapseEvent = nullptr; +} + +IMPL_LINK_NOARG(CustomAnimationList, CollapseHdl, const weld::TreeIter&, bool) +{ + if (!mnPostCollapseEvent) + { + // weld::TreeView::collapse() discards multi-selection state + // of list entries, so first save current selection state + mxTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + lastSelectedEntries.emplace_back(mxTreeView->make_iterator(&rEntry)); + return false; + }); + + mnPostCollapseEvent = Application::PostUserEvent(LINK(this, CustomAnimationList, PostCollapseHdl)); + } + + // Execute collapse on base class + return true; +} + +bool CustomAnimationList::isExpanded( const CustomAnimationEffectPtr& pEffect ) const +{ + bool bExpanded = true; // we assume expanded by default + + std::unique_ptr xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pEntry = + weld::fromId(mxTreeView->get_id(*xEntry)); + if (pEntry->getEffect() == pEffect) + { + if (mxTreeView->get_iter_depth(*xEntry)) // no parent, keep expanded default of true + { + std::unique_ptr xParentEntry = mxTreeView->make_iterator(xEntry.get()); + if (mxTreeView->iter_parent(*xParentEntry)) + bExpanded = mxTreeView->get_row_expanded(*xParentEntry); + } + break; + } + } while (mxTreeView->iter_next(*xEntry)); + } + + return bExpanded; +} + +bool CustomAnimationList::isVisible(const CustomAnimationEffectPtr& pEffect) const +{ + std::unique_ptr xEntry = mxTreeView->make_iterator(); + if (mxTreeView->get_iter_first(*xEntry)) + { + do + { + CustomAnimationListEntryItem* pTestEntry = weld::fromId(mxTreeView->get_id(*xEntry)); + if (pTestEntry->getEffect() == pEffect) + return weld::IsEntryVisible(*mxTreeView, *xEntry); + } while (mxTreeView->iter_next(*xEntry)); + } + return true; +} + +EffectSequence CustomAnimationList::getSelection() const +{ + EffectSequence aSelection; + + mxTreeView->selected_foreach([this, &aSelection](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = weld::fromId(mxTreeView->get_id(rEntry)); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + if (pEffect) + aSelection.push_back(pEffect); + + // if the selected effect is not expanded and has children + // we say that the children are automatically selected + if (!mxTreeView->get_row_expanded(rEntry) && mxTreeView->iter_has_child(rEntry)) + { + std::unique_ptr xChild = mxTreeView->make_iterator(&rEntry); + (void)mxTreeView->iter_children(*xChild); + + do + { + if (!mxTreeView->is_selected(*xChild)) + { + CustomAnimationListEntryItem* pChild = weld::fromId(mxTreeView->get_id(*xChild)); + const CustomAnimationEffectPtr& pChildEffect( pChild->getEffect() ); + if( pChildEffect ) + aSelection.push_back( pChildEffect ); + } + } while (mxTreeView->iter_next_sibling(*xChild)); + } + + return false; + }); + + return aSelection; +} + +IMPL_LINK_NOARG(CustomAnimationList, DoubleClickHdl, weld::TreeView&, bool) +{ + mpController->onDoubleClick(); + return false; +} + +IMPL_LINK(CustomAnimationList, CommandHdl, const CommandEvent&, rCEvt, bool) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + + if (rCEvt.IsMouseEvent()) + { + ::Point aPos = rCEvt.GetMousePosPixel(); + std::unique_ptr xIter(mxTreeView->make_iterator()); + if (mxTreeView->get_dest_row_at_pos(aPos, xIter.get(), false) && !mxTreeView->is_selected(*xIter)) + { + mxTreeView->unselect_all(); + mxTreeView->set_cursor(*xIter); + mxTreeView->select(*xIter); + SelectHdl(*mxTreeView); + } + } + + if (!mxTreeView->get_selected(nullptr)) + return false; + + std::unique_ptr xBuilder(Application::CreateBuilder(mxTreeView.get(), "modules/simpress/ui/effectmenu.ui")); + std::unique_ptr xMenu = xBuilder->weld_menu("menu"); + + sal_Int16 nNodeType = -1; + sal_Int16 nEntries = 0; + + mxTreeView->selected_foreach([this, &nNodeType, &nEntries](weld::TreeIter& rEntry){ + CustomAnimationListEntryItem* pEntry = weld::fromId(mxTreeView->get_id(rEntry)); + CustomAnimationEffectPtr pEffect(pEntry->getEffect()); + + nEntries++; + if (pEffect) + { + if( nNodeType == -1 ) + { + nNodeType = pEffect->getNodeType(); + } + else + { + if( nNodeType != pEffect->getNodeType() ) + { + nNodeType = -1; + return true; + } + } + } + + return false; + }); + + xMenu->set_active("onclick", nNodeType == EffectNodeType::ON_CLICK); + xMenu->set_active("withprev", nNodeType == EffectNodeType::WITH_PREVIOUS); + xMenu->set_active("afterprev", nNodeType == EffectNodeType::AFTER_PREVIOUS); + xMenu->set_sensitive("options", nEntries == 1); + xMenu->set_sensitive("timing", nEntries == 1); + + OString sCommand = xMenu->popup_at_rect(mxTreeView.get(), ::tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))); + if (!sCommand.isEmpty()) + ExecuteContextMenuAction(sCommand); + + return true; +} + +void CustomAnimationList::ExecuteContextMenuAction(const OString& rIdent) +{ + mpController->onContextMenu(rIdent); +} + +void CustomAnimationList::notify_change() +{ + update(); + mpController->onSelect(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/CustomAnimationPane.cxx b/sd/source/ui/animations/CustomAnimationPane.cxx new file mode 100644 index 000000000..0910ba96e --- /dev/null +++ b/sd/source/ui/animations/CustomAnimationPane.cxx @@ -0,0 +1,2578 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "STLPropertySet.hxx" +#include +#include "CustomAnimationDialog.hxx" +#include +#include "motionpathtag.hxx" +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::text; + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using ::com::sun::star::view::XSelectionSupplier; +using ::com::sun::star::beans::XPropertySet; +using ::com::sun::star::container::XIndexAccess; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::text::XText; +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Exception; + +namespace sd { + +void fillRepeatComboBox(weld::ComboBox& rBox) +{ + OUString aNone( SdResId( STR_CUSTOMANIMATION_REPEAT_NONE ) ); + rBox.append_text(aNone); + rBox.append_text(OUString::number(2)); + rBox.append_text(OUString::number(3)); + rBox.append_text(OUString::number(4)); + rBox.append_text(OUString::number(5)); + rBox.append_text(OUString::number(10)); + + OUString aUntilClick( SdResId( STR_CUSTOMANIMATION_REPEAT_UNTIL_NEXT_CLICK ) ); + rBox.append_text(aUntilClick); + + OUString aEndOfSlide( SdResId( STR_CUSTOMANIMATION_REPEAT_UNTIL_END_OF_SLIDE ) ); + rBox.append_text(aEndOfSlide); +} + +CustomAnimationPane::CustomAnimationPane( weld::Widget* pParent, ViewShellBase& rBase ) + : PanelLayout(pParent, "CustomAnimationsPanel", "modules/simpress/ui/customanimationspanel.ui") + , mrBase(rBase) + // load resources + , mxFTAnimation(m_xBuilder->weld_label("effectlabel")) + , mxCustomAnimationList(new CustomAnimationList(m_xBuilder->weld_tree_view("custom_animation_list"), + m_xBuilder->weld_label("custom_animation_label"), + m_xBuilder->weld_widget("custom_animation_label_parent"))) + , mxPBAddEffect(m_xBuilder->weld_button("add_effect")) + , mxPBRemoveEffect(m_xBuilder->weld_button("remove_effect")) + , mxPBMoveUp(m_xBuilder->weld_button("move_up")) + , mxPBMoveDown(m_xBuilder->weld_button("move_down")) + , mxFTCategory(m_xBuilder->weld_label("categorylabel")) + , mxLBCategory(m_xBuilder->weld_combo_box("categorylb")) + , mxFTEffect(m_xBuilder->weld_label("effect_label")) + , mxLBAnimation(m_xBuilder->weld_tree_view("effect_list")) + , mxFTStart(m_xBuilder->weld_label("start_effect")) + , mxLBStart(m_xBuilder->weld_combo_box("start_effect_list")) + , mxFTProperty(m_xBuilder->weld_label("effect_property")) + , mxPlaceholderBox(m_xBuilder->weld_container("placeholder")) + , mxPBPropertyMore(m_xBuilder->weld_button("more_properties")) + , mxFTDuration(m_xBuilder->weld_label("effect_duration")) + , mxCBXDuration(m_xBuilder->weld_metric_spin_button("anim_duration", FieldUnit::SECOND)) + , mxFTStartDelay(m_xBuilder->weld_label("delay_label")) + , mxMFStartDelay(m_xBuilder->weld_metric_spin_button("delay_value", FieldUnit::SECOND)) + , mxCBAutoPreview(m_xBuilder->weld_check_button("auto_preview")) + , mxPBPlay(m_xBuilder->weld_button("play")) + , maIdle("sd idle treeview select") + , mnLastSelectedAnimation(-1) + , mnPropertyType(nPropertyTypeNone) + , mnCurvePathPos(-1) + , mnPolygonPathPos(-1) + , mnFreeformPathPos(-1) + , maLateInitTimer("sd CustomAnimationPane maLateInitTimer") +{ + initialize(); +} + +css::ui::LayoutSize CustomAnimationPane::GetHeightForWidth(const sal_Int32 /*nWidth*/) +{ + sal_Int32 nMinimumHeight = get_preferred_size().Height(); + return css::ui::LayoutSize(nMinimumHeight, -1, nMinimumHeight); +} + +void CustomAnimationPane::initialize() +{ + mxLBAnimation->connect_changed(LINK(this, CustomAnimationPane, AnimationSelectHdl)); + mxCustomAnimationList->setController( static_cast ( this ) ); + mxCustomAnimationList->set_size_request(mxCustomAnimationList->get_approximate_digit_width() * 15, + mxCustomAnimationList->get_height_rows(4)); + + mxLBAnimation->set_size_request(mxLBAnimation->get_approximate_digit_width() * 15, + mxLBAnimation->get_height_rows(4)); + + maStrProperty = mxFTProperty->get_label(); + + mxPBAddEffect->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxPBRemoveEffect->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxLBStart->connect_changed( LINK( this, CustomAnimationPane, implControlListBoxHdl ) ); + mxCBXDuration->connect_value_changed(LINK( this, CustomAnimationPane, DurationModifiedHdl)); + mxPBPropertyMore->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxPBMoveUp->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxPBMoveDown->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxPBPlay->connect_clicked( LINK( this, CustomAnimationPane, implClickHdl ) ); + mxCBAutoPreview->connect_toggled( LINK( this, CustomAnimationPane, implToggleHdl ) ); + mxLBCategory->connect_changed( LINK(this, CustomAnimationPane, UpdateAnimationLB) ); + mxMFStartDelay->connect_value_changed( LINK(this, CustomAnimationPane, DelayModifiedHdl) ); + mxMFStartDelay->connect_focus_out(LINK( this, CustomAnimationPane, DelayLoseFocusHdl)); + + maIdle.SetPriority(TaskPriority::DEFAULT); + maIdle.SetInvokeHandler(LINK(this, CustomAnimationPane, SelectionHandler)); + + maStrModify = mxFTEffect->get_label(); + + // get current controller and initialize listeners + try + { + mxView.set(mrBase.GetController(), UNO_QUERY); + addListener(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::CustomAnimationPane()" ); + } + + // tdf#137637 keep user selection during initialization + ScopeLockGuard aGuard(maSelectionLock); + // get current page and update custom animation list + onChangeCurrentPage(); + + // Wait a short time before the presets list is created. This gives the + // system time to paint the control. + maLateInitTimer.SetTimeout(100); + maLateInitTimer.SetInvokeHandler(LINK(this, CustomAnimationPane, lateInitCallback)); + maLateInitTimer.Start(); +} + +CustomAnimationPane::~CustomAnimationPane() +{ + maLateInitTimer.Stop(); + + removeListener(); + + MotionPathTagVector aTags; + aTags.swap( maMotionPathTags ); + for (auto const& tag : aTags) + tag->Dispose(); + + mxPBAddEffect.reset(); + mxPBRemoveEffect.reset(); + mxFTEffect.reset(); + mxFTStart.reset(); + mxLBStart.reset(); + mxLBSubControl.reset(); + mxFTProperty.reset(); + mxPlaceholderBox.reset(); + mxPBPropertyMore.reset(); + mxFTDuration.reset(); + mxCBXDuration.reset(); + mxFTStartDelay.reset(); + mxMFStartDelay.reset(); + mxCustomAnimationList.reset(); + mxPBMoveUp.reset(); + mxPBMoveDown.reset(); + mxPBPlay.reset(); + mxCBAutoPreview.reset(); + mxFTCategory.reset(); + mxLBCategory.reset(); + mxFTAnimation.reset(); + mxLBAnimation.reset(); +} + +void CustomAnimationPane::addUndo() +{ + SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager(); + if( pManager ) + { + SdPage* pPage = SdPage::getImplementation( mxCurrentPage ); + if( pPage ) + pManager->AddUndoAction( std::make_unique( mrBase.GetDocShell()->GetDoc(), pPage ) ); + } +} + +void CustomAnimationPane::addListener() +{ + Link aLink( LINK(this,CustomAnimationPane,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener(aLink); +} + +void CustomAnimationPane::removeListener() +{ + Link aLink( LINK(this,CustomAnimationPane,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(CustomAnimationPane,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::EditViewSelection: + onSelectionChanged(); + break; + + case EventMultiplexerEventId::CurrentPageChanged: + onChangeCurrentPage(); + break; + + case EventMultiplexerEventId::MainViewAdded: + // At this moment the controller may not yet been set at model + // or ViewShellBase. Take it from the view shell passed with + // the event. + if (mrBase.GetMainViewShell() != nullptr) + { + if( mrBase.GetMainViewShell()->GetShellType() == ViewShell::ST_IMPRESS ) + { + mxView.set(mrBase.GetDrawController(), UNO_QUERY); + onSelectionChanged(); + onChangeCurrentPage(); + break; + } + } + [[fallthrough]]; + case EventMultiplexerEventId::MainViewRemoved: + mxView = nullptr; + mxCurrentPage = nullptr; + updateControls(); + break; + + case EventMultiplexerEventId::Disposing: + mxView.clear(); + onSelectionChanged(); + onChangeCurrentPage(); + break; + case EventMultiplexerEventId::EndTextEdit: + if (mpMainSequence && rEvent.mpUserData) + mxCustomAnimationList->update( mpMainSequence ); + break; + default: break; + } +} + +static sal_Int32 getPropertyType( std::u16string_view rProperty ) +{ + if ( rProperty == u"Direction" ) + return nPropertyTypeDirection; + + if ( rProperty == u"Spokes" ) + return nPropertyTypeSpokes; + + if ( rProperty == u"Zoom" ) + return nPropertyTypeZoom; + + if ( rProperty == u"Accelerate" ) + return nPropertyTypeAccelerate; + + if ( rProperty == u"Decelerate" ) + return nPropertyTypeDecelerate; + + if ( rProperty == u"Color1" ) + return nPropertyTypeFirstColor; + + if ( rProperty == u"Color2" ) + return nPropertyTypeSecondColor; + + if ( rProperty == u"FillColor" ) + return nPropertyTypeFillColor; + + if ( rProperty == u"ColorStyle" ) + return nPropertyTypeColorStyle; + + if ( rProperty == u"AutoReverse" ) + return nPropertyTypeAutoReverse; + + if ( rProperty == u"FontStyle" ) + return nPropertyTypeFont; + + if ( rProperty == u"CharColor" ) + return nPropertyTypeCharColor; + + if ( rProperty == u"CharHeight" ) + return nPropertyTypeCharHeight; + + if ( rProperty == u"CharDecoration" ) + return nPropertyTypeCharDecoration; + + if ( rProperty == u"LineColor" ) + return nPropertyTypeLineColor; + + if ( rProperty == u"Rotate" ) + return nPropertyTypeRotate; + + if ( rProperty == u"Transparency" ) + return nPropertyTypeTransparency; + + if ( rProperty == u"Color" ) + return nPropertyTypeColor; + + if ( rProperty == u"Scale" ) + return nPropertyTypeScale; + + return nPropertyTypeNone; +} + +OUString getPropertyName( sal_Int32 nPropertyType ) +{ + switch( nPropertyType ) + { + case nPropertyTypeDirection: + return SdResId(STR_CUSTOMANIMATION_DIRECTION_PROPERTY); + + case nPropertyTypeSpokes: + return SdResId(STR_CUSTOMANIMATION_SPOKES_PROPERTY); + + case nPropertyTypeFirstColor: + return SdResId(STR_CUSTOMANIMATION_FIRST_COLOR_PROPERTY); + + case nPropertyTypeSecondColor: + return SdResId(STR_CUSTOMANIMATION_SECOND_COLOR_PROPERTY); + + case nPropertyTypeZoom: + return SdResId(STR_CUSTOMANIMATION_ZOOM_PROPERTY); + + case nPropertyTypeFillColor: + return SdResId(STR_CUSTOMANIMATION_FILL_COLOR_PROPERTY); + + case nPropertyTypeColorStyle: + return SdResId(STR_CUSTOMANIMATION_STYLE_PROPERTY); + + case nPropertyTypeFont: + return SdResId(STR_CUSTOMANIMATION_FONT_PROPERTY); + + case nPropertyTypeCharHeight: + return SdResId(STR_CUSTOMANIMATION_SIZE_PROPERTY); + + case nPropertyTypeCharColor: + return SdResId(STR_CUSTOMANIMATION_FONT_COLOR_PROPERTY); + + case nPropertyTypeCharHeightStyle: + return SdResId(STR_CUSTOMANIMATION_FONT_SIZE_STYLE_PROPERTY); + + case nPropertyTypeCharDecoration: + return SdResId(STR_CUSTOMANIMATION_FONT_STYLE_PROPERTY); + + case nPropertyTypeLineColor: + return SdResId(STR_CUSTOMANIMATION_LINE_COLOR_PROPERTY); + + case nPropertyTypeRotate: + return SdResId(STR_CUSTOMANIMATION_AMOUNT_PROPERTY); + + case nPropertyTypeColor: + return SdResId(STR_CUSTOMANIMATION_COLOR_PROPERTY); + + case nPropertyTypeTransparency: + return SdResId(STR_CUSTOMANIMATION_AMOUNT_PROPERTY); + + case nPropertyTypeScale: + return SdResId(STR_CUSTOMANIMATION_SCALE_PROPERTY); + } + + return OUString(); +} + +void CustomAnimationPane::updateControls() +{ + mxFTDuration->set_sensitive(mxView.is()); + mxCBXDuration->set_sensitive(mxView.is()); + mxCustomAnimationList->set_sensitive(mxView.is()); + if (comphelper::LibreOfficeKit::isActive()) + { + mxPBPlay->hide(); + mxCBAutoPreview->set_active(false); + mxCBAutoPreview->hide(); + } + else + { + mxPBPlay->set_sensitive(mxView.is()); + mxCBAutoPreview->set_sensitive(mxView.is()); + } + + if (!mxView.is()) + { + mxPBAddEffect->set_sensitive(false); + mxPBRemoveEffect->set_sensitive(false); + mxFTStart->set_sensitive(false); + mxLBStart->set_sensitive(false); + mxPBPropertyMore->set_sensitive(false); + mxPlaceholderBox->set_sensitive(false); + mxFTProperty->set_sensitive(false); + mxFTCategory->set_sensitive(false); + mxLBCategory->set_sensitive(false); + mxFTAnimation->set_sensitive(false); + mxLBAnimation->set_sensitive(false); + mxFTStartDelay->set_sensitive(false); + mxMFStartDelay->set_sensitive(false); + mxLBAnimation->clear(); + mnLastSelectedAnimation = -1; + mxCustomAnimationList->clear(); + return; + } + + const int nSelectionCount = maListSelection.size(); + + mxPBAddEffect->set_sensitive( maViewSelection.hasValue() ); + mxPBRemoveEffect->set_sensitive(nSelectionCount != 0); + bool bIsSelected = (nSelectionCount > 0); + + if(bIsSelected) + { + mxFTAnimation->set_sensitive(true); + mxLBAnimation->set_sensitive(true); + } + else + { + mxFTAnimation->set_sensitive(false); + mxLBAnimation->set_sensitive(false); + mxLBAnimation->clear(); + mnLastSelectedAnimation = -1; + } + + mxLBCategory->set_sensitive(bIsSelected); + mxFTCategory->set_sensitive(bIsSelected); + + mxFTStart->set_sensitive(nSelectionCount > 0); + mxLBStart->set_sensitive(nSelectionCount > 0); + mxPlaceholderBox->set_sensitive(nSelectionCount > 0); + mxPBPropertyMore->set_sensitive(nSelectionCount > 0); + mxFTStartDelay->set_sensitive(nSelectionCount > 0); + mxMFStartDelay->set_sensitive(nSelectionCount > 0); + + mxFTProperty->set_label(maStrProperty); + + sal_Int32 nOldPropertyType = mnPropertyType; + + mnPropertyType = nPropertyTypeNone; + + if(bIsSelected) + { + CustomAnimationEffectPtr pEffect = maListSelection.front(); + + OUString aUIName( CustomAnimationPresets::getCustomAnimationPresets().getUINameForPresetId( pEffect->getPresetId() ) ); + + OUString aTemp( maStrModify ); + + if( !aUIName.isEmpty() ) + { + aTemp += " " + aUIName; + mxFTEffect->set_label( aTemp ); + } + + Any aValue; + CustomAnimationPresetPtr pDescriptor = CustomAnimationPresets::getCustomAnimationPresets().getEffectDescriptor( pEffect->getPresetId() ); + if (pDescriptor) + { + std::vector aProperties( pDescriptor->getProperties() ); + if( !aProperties.empty() ) + { + mnPropertyType = getPropertyType( aProperties.front() ); + + mxFTProperty->set_label( getPropertyName( mnPropertyType ) ); + + aValue = getProperty1Value( mnPropertyType, pEffect ); + } + } + + sal_Int32 nNewPropertyType = mnPropertyType; + // if there is no value, then the control will be disabled, just show a disabled Direction box in that + // case to have something to fill the space + if (!aValue.hasValue()) + nNewPropertyType = nPropertyTypeDirection; + + if (!mxLBSubControl || nOldPropertyType != nNewPropertyType) + { + // for LOK destroy old widgets first + mxLBSubControl.reset(nullptr); + // then create new control, to keep correct pointers for actions + mxLBSubControl = SdPropertySubControl::create(nNewPropertyType, mxFTProperty.get(), mxPlaceholderBox.get(), GetFrameWeld(), aValue, pEffect->getPresetId(), LINK(this, CustomAnimationPane, implPropertyHdl)); + } + else + { + mxLBSubControl->setValue(aValue, pEffect->getPresetId()); + } + + bool bEnable = aValue.hasValue(); + mxPlaceholderBox->set_sensitive( bEnable ); + mxFTProperty->set_sensitive( bEnable ); + + if (!pDescriptor) + { + mxPBPropertyMore->set_sensitive( false ); + mxFTStartDelay->set_sensitive( false ); + mxMFStartDelay->set_sensitive( false ); + } + sal_Int32 nCategoryPos = -1; + switch(pEffect->getPresetClass()) + { + case EffectPresetClass::ENTRANCE: nCategoryPos = 0; break; + case EffectPresetClass::EMPHASIS: nCategoryPos = 1; break; + case EffectPresetClass::EXIT: nCategoryPos = 2; break; + case EffectPresetClass::MOTIONPATH: nCategoryPos = 3; break; + default: + break; + } + switch(pEffect->getCommand()) + { + case EffectCommands::TOGGLEPAUSE: + case EffectCommands::STOP: + case EffectCommands::PLAY: + nCategoryPos = 4; break; + default: + break; + } + mxLBCategory->set_active(nCategoryPos); + + fillAnimationLB( pEffect->hasText() ); + + OUString rsPresetId = pEffect->getPresetId(); + sal_Int32 nAnimationPos = mxLBAnimation->n_children(); + while( nAnimationPos-- ) + { + auto pEntryData = weld::fromId(mxLBAnimation->get_id(nAnimationPos)); + if (pEntryData) + { + CustomAnimationPresetPtr& pPtr = *pEntryData; + if( pPtr && pPtr->getPresetId() == rsPresetId ) + { + mxLBAnimation->select( nAnimationPos ); + mnLastSelectedAnimation = nAnimationPos; + break; + } + } + } + + // If preset id is missing and category is motion path. + if (nAnimationPos < 0 && nCategoryPos == 3) + { + if (rsPresetId == "libo-motionpath-curve") + { + mxLBAnimation->select(mnCurvePathPos); + mnLastSelectedAnimation = mnCurvePathPos; + } + else if (rsPresetId == "libo-motionpath-polygon") + { + mxLBAnimation->select(mnPolygonPathPos); + mnLastSelectedAnimation = mnPolygonPathPos; + } + else if (rsPresetId == "libo-motionpath-freeform-line") + { + mxLBAnimation->select(mnFreeformPathPos); + mnLastSelectedAnimation = mnFreeformPathPos; + } + } + + sal_uInt16 nPos = 0xffff; + + sal_Int16 nNodeType = pEffect->getNodeType(); + switch( nNodeType ) + { + case EffectNodeType::ON_CLICK: nPos = 0; break; + case EffectNodeType::WITH_PREVIOUS: nPos = 1; break; + case EffectNodeType::AFTER_PREVIOUS: nPos = 2; break; + } + + mxLBStart->set_active( nPos ); + + double fDuration = pEffect->getDuration(); + const bool bHasSpeed = fDuration > 0.001; + + mxFTDuration->set_sensitive(bHasSpeed); + mxCBXDuration->set_sensitive(bHasSpeed); + + if( bHasSpeed ) + { + mxCBXDuration->set_value(fDuration*100.0, FieldUnit::NONE); + } + + mxPBPropertyMore->set_sensitive(true); + + mxFTStartDelay->set_sensitive(true); + mxMFStartDelay->set_sensitive(true); + double fBegin = pEffect->getBegin(); + mxMFStartDelay->set_value(fBegin*10.0, FieldUnit::NONE); + } + else + { + // use an empty direction box to fill the space + if (!mxLBSubControl || (nOldPropertyType != nPropertyTypeDirection && nOldPropertyType != nPropertyTypeNone)) + { + // for LOK destroy old widgets first + mxLBSubControl.reset(nullptr); + // then create new control, to keep correct pointers for actions + mxLBSubControl = SdPropertySubControl::create(nPropertyTypeDirection, mxFTProperty.get(), mxPlaceholderBox.get(), GetFrameWeld(), uno::Any(), OUString(), LINK(this, CustomAnimationPane, implPropertyHdl)); + } + else + mxLBSubControl->setValue(uno::Any(), OUString()); + + mxPlaceholderBox->set_sensitive(false); + mxFTProperty->set_sensitive(false); + mxFTStartDelay->set_sensitive(false); + mxMFStartDelay->set_sensitive(false); + mxPBPropertyMore->set_sensitive(false); + mxFTDuration->set_sensitive(false); + mxCBXDuration->set_sensitive(false); + mxCBXDuration->set_text(OUString()); + mxFTEffect->set_label(maStrModify); + } + + bool bEnableUp = true; + bool bEnableDown = true; + if( nSelectionCount == 0 ) + { + bEnableUp = false; + bEnableDown = false; + } + else + { + if( mpMainSequence->find( maListSelection.front() ) == mpMainSequence->getBegin() ) + bEnableUp = false; + + EffectSequence::iterator aIter( mpMainSequence->find( maListSelection.back() ) ); + if( aIter == mpMainSequence->getEnd() ) + { + bEnableDown = false; + } + else + { + do + { + ++aIter; + } + while( (aIter != mpMainSequence->getEnd()) && !(mxCustomAnimationList->isExpanded(*aIter) ) ); + + if( aIter == mpMainSequence->getEnd() ) + bEnableDown = false; + } + + if( bEnableUp || bEnableDown ) + { + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + EffectSequenceHelper* pSequence = nullptr; + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + if( pEffect ) + { + if( pSequence == nullptr ) + { + pSequence = pEffect->getEffectSequence(); + } + else + { + if( pSequence != pEffect->getEffectSequence() ) + { + bEnableUp = false; + bEnableDown = false; + break; + } + } + } + } + } + } + + mxPBMoveUp->set_sensitive(mxView.is() && bEnableUp); + mxPBMoveDown->set_sensitive(mxView.is() && bEnableDown); + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + mxCBAutoPreview->set_active(pOptions->IsPreviewChangedEffects()); + + updateMotionPathTags(); +} + +static bool updateMotionPathImpl( CustomAnimationPane& rPane, ::sd::View& rView, EffectSequence::iterator aIter, const EffectSequence::iterator& aEnd, MotionPathTagVector& rOldTags, MotionPathTagVector& rNewTags ) +{ + bool bChanges = false; + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect( *aIter++ ); + if( pEffect && pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH ) + { + rtl::Reference< MotionPathTag > xMotionPathTag; + // first try to find if there is already a tag for this + auto aMIter = std::find_if(rOldTags.begin(), rOldTags.end(), + [&pEffect](const rtl::Reference& xTag) { return xTag->getEffect() == pEffect; }); + if (aMIter != rOldTags.end()) + { + rtl::Reference< MotionPathTag > xTag( *aMIter ); + if( !xTag->isDisposed() ) + { + xMotionPathTag = xTag; + rOldTags.erase( aMIter ); + } + } + + // if not found, create new one + if( !xMotionPathTag.is() ) + { + xMotionPathTag.set( new MotionPathTag( rPane, rView, pEffect ) ); + bChanges = true; + } + + if( xMotionPathTag.is() ) + rNewTags.push_back( xMotionPathTag ); + } + } + + return bChanges; +} + +void CustomAnimationPane::updateMotionPathTags() +{ + bool bChanges = false; + + MotionPathTagVector aTags; + aTags.swap( maMotionPathTags ); + + ::sd::View* pView = nullptr; + + if( mxView.is() ) + { + std::shared_ptr xViewShell( mrBase.GetMainViewShell() ); + if( xViewShell ) + pView = xViewShell->GetView(); + } + + if (mpMainSequence && pView) + { + bChanges = updateMotionPathImpl( *this, *pView, mpMainSequence->getBegin(), mpMainSequence->getEnd(), aTags, maMotionPathTags ); + + auto rInteractiveSequenceVector = mpMainSequence->getInteractiveSequenceVector(); + for (InteractiveSequencePtr const& pIS : rInteractiveSequenceVector) + { + bChanges |= updateMotionPathImpl( *this, *pView, pIS->getBegin(), pIS->getEnd(), aTags, maMotionPathTags ); + } + } + + if( !aTags.empty() ) + { + bChanges = true; + for( rtl::Reference< MotionPathTag >& xTag : aTags ) + { + xTag->Dispose(); + } + } + + if( bChanges && pView ) + pView->updateHandles(); +} + +void CustomAnimationPane::onSelectionChanged() +{ + if( maSelectionLock.isLocked() ) + return; + + ScopeLockGuard aGuard( maSelectionLock ); + + if( mxView.is() ) try + { + Reference< XSelectionSupplier > xSel( mxView, UNO_QUERY_THROW ); + maViewSelection = xSel->getSelection(); + mxCustomAnimationList->onSelectionChanged( maViewSelection ); + updateControls(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::onSelectionChanged()" ); + } +} + +void CustomAnimationPane::onDoubleClick() +{ + showOptions(); +} + +void CustomAnimationPane::onContextMenu(const OString &rIdent) +{ + if (rIdent == "onclick") + onChangeStart( EffectNodeType::ON_CLICK ); + else if (rIdent == "withprev") + onChangeStart( EffectNodeType::WITH_PREVIOUS ); + else if (rIdent == "afterprev") + onChangeStart( EffectNodeType::AFTER_PREVIOUS ); + else if (rIdent == "options") + showOptions(); + else if (rIdent == "timing") + showOptions("timing"); + else if (rIdent == "remove") + onRemove(); + else if (rIdent == "create" && maViewSelection.hasValue()) + onAdd(); + updateControls(); +} + +static void addValue( const std::unique_ptr& pSet, sal_Int32 nHandle, const Any& rValue ) +{ + switch( pSet->getPropertyState( nHandle ) ) + { + case STLPropertyState::Ambiguous: + // value is already ambiguous, do nothing + break; + case STLPropertyState::Direct: + // set to ambiguous if existing value is different + if( rValue != pSet->getPropertyValue( nHandle ) ) + pSet->setPropertyState( nHandle, STLPropertyState::Ambiguous ); + break; + case STLPropertyState::Default: + // just set new value + pSet->setPropertyValue( nHandle, rValue ); + break; + } +} + +static sal_Int32 calcMaxParaDepth( const Reference< XShape >& xTargetShape ) +{ + sal_Int32 nMaxParaDepth = -1; + + if( xTargetShape.is() ) + { + Reference< XEnumerationAccess > xText( xTargetShape, UNO_QUERY ); + if( xText.is() ) + { + Reference< XPropertySet > xParaSet; + + Reference< XEnumeration > xEnumeration( xText->createEnumeration(), UNO_SET_THROW ); + while( xEnumeration->hasMoreElements() ) + { + xEnumeration->nextElement() >>= xParaSet; + if( xParaSet.is() ) + { + sal_Int32 nParaDepth = 0; + xParaSet->getPropertyValue( "NumberingLevel" ) >>= nParaDepth; + + if( nParaDepth > nMaxParaDepth ) + nMaxParaDepth = nParaDepth; + } + } + } + } + + return nMaxParaDepth + 1; +} + +Any CustomAnimationPane::getProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect ) +{ + switch( nType ) + { + case nPropertyTypeDirection: + case nPropertyTypeSpokes: + case nPropertyTypeZoom: + return Any( pEffect->getPresetSubType() ); + + case nPropertyTypeColor: + case nPropertyTypeFillColor: + case nPropertyTypeFirstColor: + case nPropertyTypeSecondColor: + case nPropertyTypeCharColor: + case nPropertyTypeLineColor: + { + const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1; + return pEffect->getColor( nIndex ); + } + + case nPropertyTypeFont: + return pEffect->getProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To ); + + case nPropertyTypeCharHeight: + { + static const OUStringLiteral aAttributeName( u"CharHeight" ); + Any aValue( pEffect->getProperty( AnimationNodeType::SET, aAttributeName, EValue::To ) ); + if( !aValue.hasValue() ) + aValue = pEffect->getProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To ); + return aValue; + } + + case nPropertyTypeRotate: + return pEffect->getTransformationProperty( AnimationTransformType::ROTATE, EValue::By); + + case nPropertyTypeTransparency: + return pEffect->getProperty( AnimationNodeType::SET, u"Opacity" , EValue::To ); + + case nPropertyTypeScale: + return pEffect->getTransformationProperty( AnimationTransformType::SCALE, EValue::By ); + + case nPropertyTypeCharDecoration: + { + Sequence< Any > aValues{ + pEffect->getProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To ), + pEffect->getProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To ), + pEffect->getProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To ) + }; + return Any( aValues ); + } + } + + Any aAny; + return aAny; +} + +bool CustomAnimationPane::setProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect, const Any& rValue ) +{ + bool bEffectChanged = false; + switch( nType ) + { + case nPropertyTypeDirection: + case nPropertyTypeSpokes: + case nPropertyTypeZoom: + { + OUString aPresetSubType; + rValue >>= aPresetSubType; + if( aPresetSubType != pEffect->getPresetSubType() ) + { + CustomAnimationPresets::getCustomAnimationPresets().changePresetSubType( pEffect, aPresetSubType ); + bEffectChanged = true; + } + } + break; + + case nPropertyTypeFillColor: + case nPropertyTypeColor: + case nPropertyTypeFirstColor: + case nPropertyTypeSecondColor: + case nPropertyTypeCharColor: + case nPropertyTypeLineColor: + { + const sal_Int32 nIndex = (nPropertyTypeFirstColor == nType) ? 0 : 1; + Any aOldColor( pEffect->getColor( nIndex ) ); + if( aOldColor != rValue ) + { + pEffect->setColor( nIndex, rValue ); + bEffectChanged = true; + } + } + break; + + case nPropertyTypeFont: + bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharFontName" , EValue::To, rValue ); + break; + + case nPropertyTypeCharHeight: + { + static const OUStringLiteral aAttributeName( u"CharHeight" ); + bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, aAttributeName, EValue::To, rValue ); + if( !bEffectChanged ) + bEffectChanged = pEffect->setProperty( AnimationNodeType::ANIMATE, aAttributeName, EValue::To, rValue ); + } + break; + case nPropertyTypeRotate: + bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::ROTATE, EValue::By , rValue ); + break; + + case nPropertyTypeTransparency: + bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"Opacity" , EValue::To, rValue ); + break; + + case nPropertyTypeScale: + bEffectChanged = pEffect->setTransformationProperty( AnimationTransformType::SCALE, EValue::By, rValue ); + break; + + case nPropertyTypeCharDecoration: + { + Sequence< Any > aValues(3); + rValue >>= aValues; + bEffectChanged = pEffect->setProperty( AnimationNodeType::SET, u"CharWeight" , EValue::To, aValues[0] ); + bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharPosture" , EValue::To, aValues[1] ); + bEffectChanged |= pEffect->setProperty( AnimationNodeType::SET, u"CharUnderline" , EValue::To, aValues[2] ); + } + break; + + } + + return bEffectChanged; +} + +static bool hasVisibleShape( const Reference< XShape >& xShape ) +{ + try + { + const OUString sShapeType( xShape->getShapeType() ); + + if( sShapeType == "com.sun.star.presentation.TitleTextShape" || sShapeType == "com.sun.star.presentation.OutlinerShape" || + sShapeType == "com.sun.star.presentation.SubtitleShape" || sShapeType == "com.sun.star.drawing.TextShape" ) + { + Reference< XPropertySet > xSet( xShape, UNO_QUERY_THROW ); + + FillStyle eFillStyle; + xSet->getPropertyValue( "FillStyle" ) >>= eFillStyle; + + css::drawing::LineStyle eLineStyle; + xSet->getPropertyValue( "LineStyle" ) >>= eLineStyle; + + return eFillStyle != FillStyle_NONE || eLineStyle != css::drawing::LineStyle_NONE; + } + } + catch( Exception& ) + { + } + return true; +} + +std::unique_ptr CustomAnimationPane::createSelectionSet() +{ + std::unique_ptr pSet = CustomAnimationDialog::createDefaultSet(); + + pSet->setPropertyValue( nHandleCurrentPage, Any( mxCurrentPage ) ); + + sal_Int32 nMaxParaDepth = 0; + + // get options from selected effects + const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets()); + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence(); + if( !pEffectSequence ) + pEffectSequence = mpMainSequence.get(); + + if( pEffect->hasText() ) + { + sal_Int32 n = calcMaxParaDepth(pEffect->getTargetShape()); + if( n > nMaxParaDepth ) + nMaxParaDepth = n; + } + + addValue( pSet, nHandleHasAfterEffect, Any( pEffect->hasAfterEffect() ) ); + addValue( pSet, nHandleAfterEffectOnNextEffect, Any( pEffect->IsAfterEffectOnNext() ) ); + addValue( pSet, nHandleDimColor, pEffect->getDimColor() ); + addValue( pSet, nHandleIterateType, Any( pEffect->getIterateType() ) ); + + // convert absolute time to percentage value + // This calculation is done in float to avoid some rounding artifacts. + float fIterateInterval = static_cast(pEffect->getIterateInterval()); + if( pEffect->getDuration() ) + fIterateInterval = static_cast(fIterateInterval / pEffect->getDuration() ); + fIterateInterval *= 100.0; + addValue( pSet, nHandleIterateInterval, Any( static_cast(fIterateInterval) ) ); + + addValue( pSet, nHandleBegin, Any( pEffect->getBegin() ) ); + addValue( pSet, nHandleDuration, Any( pEffect->getDuration() ) ); + addValue( pSet, nHandleStart, Any( pEffect->getNodeType() ) ); + addValue( pSet, nHandleRepeat, pEffect->getRepeatCount() ); + addValue( pSet, nHandleEnd, pEffect->getEnd() ); + addValue( pSet, nHandleRewind, Any( pEffect->getFill() ) ); + + addValue( pSet, nHandlePresetId, Any( pEffect->getPresetId() ) ); + + addValue( pSet, nHandleHasText, Any( pEffect->hasText() ) ); + + addValue( pSet, nHandleHasVisibleShape, Any( hasVisibleShape( pEffect->getTargetShape() ) ) ); + + Any aSoundSource; + if( pEffect->getAudio().is() ) + { + aSoundSource = pEffect->getAudio()->getSource(); + addValue( pSet, nHandleSoundVolume, Any( pEffect->getAudio()->getVolume() ) ); +// todo addValue( pSet, nHandleSoundEndAfterSlide, makeAny( pEffect->getAudio()->getEndAfterSlide() ) ); +// this is now stored at the XCommand parameter sequence + } + else if( pEffect->getCommand() == EffectCommands::STOPAUDIO ) + { + aSoundSource <<= true; + } + addValue( pSet, nHandleSoundURL, aSoundSource ); + + sal_Int32 nGroupId = pEffect->getGroupId(); + CustomAnimationTextGroupPtr pTextGroup; + if( nGroupId != -1 ) + pTextGroup = pEffectSequence->findGroup( nGroupId ); + + addValue( pSet, nHandleTextGrouping, Any( pTextGroup ? pTextGroup->getTextGrouping() : sal_Int32(-1) ) ); + addValue( pSet, nHandleAnimateForm, Any( !pTextGroup || pTextGroup->getAnimateForm() ) ); + addValue( pSet, nHandleTextGroupingAuto, Any( pTextGroup ? pTextGroup->getTextGroupingAuto() : -1.0 ) ); + addValue( pSet, nHandleTextReverse, Any( pTextGroup && pTextGroup->getTextReverse() ) ); + + if( pEffectSequence->getSequenceType() == EffectNodeType::INTERACTIVE_SEQUENCE ) + { + InteractiveSequence* pIS = static_cast< InteractiveSequence* >( pEffectSequence ); + addValue( pSet, nHandleTrigger, Any( pIS->getTriggerShape() ) ); + } + + CustomAnimationPresetPtr pDescriptor = rPresets.getEffectDescriptor( pEffect->getPresetId() ); + if( pDescriptor ) + { + sal_Int32 nType = nPropertyTypeNone; + + std::vector aProperties( pDescriptor->getProperties() ); + if( !aProperties.empty() ) + nType = getPropertyType( aProperties.front() ); + + if( nType != nPropertyTypeNone ) + { + addValue( pSet, nHandleProperty1Type, Any( nType ) ); + addValue( pSet, nHandleProperty1Value, getProperty1Value( nType, pEffect ) ); + } + + if( pDescriptor->hasProperty( u"Accelerate" ) ) + { + addValue( pSet, nHandleAccelerate, Any( pEffect->getAcceleration() ) ); + } + + if( pDescriptor->hasProperty( u"Decelerate" ) ) + { + addValue( pSet, nHandleDecelerate, Any( pEffect->getDecelerate() ) ); + } + + if( pDescriptor->hasProperty( u"AutoReverse" ) ) + { + addValue( pSet, nHandleAutoReverse, Any( pEffect->getAutoReverse() ) ); + } + } + } + + addValue( pSet, nHandleMaxParaDepth, Any( nMaxParaDepth ) ); + + return pSet; +} + +void CustomAnimationPane::changeSelection( STLPropertySet const * pResultSet, STLPropertySet const * pOldSet ) +{ + // change selected effect + bool bChanged = false; + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + DBG_ASSERT( pEffect->getEffectSequence(), "sd::CustomAnimationPane::changeSelection(), dead effect in selection!" ); + if( !pEffect->getEffectSequence() ) + continue; + + double fDuration = 0.0; // we might need this for iterate-interval + if( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct ) + { + pResultSet->getPropertyValue( nHandleDuration ) >>= fDuration; + } + else + { + fDuration = pEffect->getDuration(); + } + + if( pResultSet->getPropertyState( nHandleIterateType ) == STLPropertyState::Direct ) + { + sal_Int16 nIterateType = 0; + pResultSet->getPropertyValue( nHandleIterateType ) >>= nIterateType; + if( pEffect->getIterateType() != nIterateType ) + { + pEffect->setIterateType( nIterateType ); + bChanged = true; + } + } + + if( pEffect->getIterateType() ) + { + if( pResultSet->getPropertyState( nHandleIterateInterval ) == STLPropertyState::Direct ) + { + double fIterateInterval = 0.0; + pResultSet->getPropertyValue( nHandleIterateInterval ) >>= fIterateInterval; + if( pEffect->getIterateInterval() != fIterateInterval ) + { + const double f = fIterateInterval * pEffect->getDuration() / 100; + pEffect->setIterateInterval( f ); + bChanged = true; + } + } + } + + double fBegin = 0.0; + + if( pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct ) + pResultSet->getPropertyValue( nHandleBegin ) >>= fBegin; + else + fBegin = pEffect->getBegin(); + + if( pEffect->getBegin() != fBegin && pResultSet->getPropertyState( nHandleBegin ) == STLPropertyState::Direct) + { + pEffect->setBegin( fBegin ); + bChanged = true; + } + + if( pResultSet->getPropertyState( nHandleDuration ) == STLPropertyState::Direct ) + { + if( pEffect->getDuration() != fDuration ) + { + pEffect->setDuration( fDuration ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleStart ) == STLPropertyState::Direct ) + { + sal_Int16 nNodeType = 0; + pResultSet->getPropertyValue( nHandleStart ) >>= nNodeType; + if( pEffect->getNodeType() != nNodeType ) + { + pEffect->setNodeType( nNodeType ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleRepeat ) == STLPropertyState::Direct ) + { + Any aRepeatCount( pResultSet->getPropertyValue( nHandleRepeat ) ); + if( aRepeatCount != pEffect->getRepeatCount() ) + { + pEffect->setRepeatCount( aRepeatCount ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleEnd ) == STLPropertyState::Direct ) + { + Any aEndValue( pResultSet->getPropertyValue( nHandleEnd ) ); + if( pEffect->getEnd() != aEndValue ) + { + pEffect->setEnd( aEndValue ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleRewind ) == STLPropertyState::Direct ) + { + sal_Int16 nFill = 0; + pResultSet->getPropertyValue( nHandleRewind ) >>= nFill; + if( pEffect->getFill() != nFill ) + { + pEffect->setFill( nFill ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleHasAfterEffect ) == STLPropertyState::Direct ) + { + bool bHasAfterEffect = false; + if( pResultSet->getPropertyValue( nHandleHasAfterEffect ) >>= bHasAfterEffect ) + { + if( pEffect->hasAfterEffect() != bHasAfterEffect ) + { + pEffect->setHasAfterEffect( bHasAfterEffect ); + bChanged = true; + } + } + } + + if( pResultSet->getPropertyState( nHandleAfterEffectOnNextEffect ) == STLPropertyState::Direct ) + { + bool bAfterEffectOnNextEffect = false; + if( (pResultSet->getPropertyValue( nHandleAfterEffectOnNextEffect ) >>= bAfterEffectOnNextEffect) + && (pEffect->IsAfterEffectOnNext() != bAfterEffectOnNextEffect) ) + { + pEffect->setAfterEffectOnNext( bAfterEffectOnNextEffect ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleDimColor ) == STLPropertyState::Direct ) + { + Any aDimColor( pResultSet->getPropertyValue( nHandleDimColor ) ); + if( pEffect->getDimColor() != aDimColor ) + { + pEffect->setDimColor( aDimColor ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleAccelerate ) == STLPropertyState::Direct ) + { + double fAccelerate = 0.0; + pResultSet->getPropertyValue( nHandleAccelerate ) >>= fAccelerate; + if( pEffect->getAcceleration() != fAccelerate ) + { + pEffect->setAcceleration( fAccelerate ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleDecelerate ) == STLPropertyState::Direct ) + { + double fDecelerate = 0.0; + pResultSet->getPropertyValue( nHandleDecelerate ) >>= fDecelerate; + if( pEffect->getDecelerate() != fDecelerate ) + { + pEffect->setDecelerate( fDecelerate ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleAutoReverse ) == STLPropertyState::Direct ) + { + bool bAutoReverse = false; + pResultSet->getPropertyValue( nHandleAutoReverse ) >>= bAutoReverse; + if( pEffect->getAutoReverse() != bAutoReverse ) + { + pEffect->setAutoReverse( bAutoReverse ); + bChanged = true; + } + } + + if( pResultSet->getPropertyState( nHandleProperty1Value ) == STLPropertyState::Direct ) + { + sal_Int32 nType = 0; + pOldSet->getPropertyValue( nHandleProperty1Type ) >>= nType; + + bChanged |= setProperty1Value( nType, pEffect, pResultSet->getPropertyValue( nHandleProperty1Value ) ); + } + + if( pResultSet->getPropertyState( nHandleSoundURL ) == STLPropertyState::Direct ) + { + const Any aSoundSource( pResultSet->getPropertyValue( nHandleSoundURL ) ); + + if( aSoundSource.getValueType() == ::cppu::UnoType::get() ) + { + pEffect->setStopAudio(); + bChanged = true; + } + else + { + OUString aSoundURL; + aSoundSource >>= aSoundURL; + + if( !aSoundURL.isEmpty() ) + { + if( !pEffect->getAudio().is() ) + { + pEffect->createAudio( aSoundSource ); + bChanged = true; + } + else + { + if( pEffect->getAudio()->getSource() != aSoundSource ) + { + pEffect->getAudio()->setSource( aSoundSource ); + bChanged = true; + } + } + } + else + { + if( pEffect->getAudio().is() || pEffect->getStopAudio() ) + { + pEffect->removeAudio(); + bChanged = true; + } + } + } + } + + if( pResultSet->getPropertyState( nHandleTrigger ) == STLPropertyState::Direct ) + { + Reference< XShape > xTriggerShape; + pResultSet->getPropertyValue( nHandleTrigger ) >>= xTriggerShape; + bChanged |= mpMainSequence->setTrigger( pEffect, xTriggerShape ); + } + } + + const bool bHasTextGrouping = pResultSet->getPropertyState( nHandleTextGrouping ) == STLPropertyState::Direct; + const bool bHasAnimateForm = pResultSet->getPropertyState( nHandleAnimateForm ) == STLPropertyState::Direct; + const bool bHasTextGroupingAuto = pResultSet->getPropertyState( nHandleTextGroupingAuto ) == STLPropertyState::Direct; + const bool bHasTextReverse = pResultSet->getPropertyState( nHandleTextReverse ) == STLPropertyState::Direct; + + if( bHasTextGrouping || bHasAnimateForm || bHasTextGroupingAuto || bHasTextReverse ) + { + // we need to do a second pass for text grouping options + // since changing them can cause effects to be removed + // or replaced, we do this after we applied all other options + // above + + sal_Int32 nTextGrouping = 0; + bool bAnimateForm = true, bTextReverse = false; + double fTextGroupingAuto = -1.0; + + if( bHasTextGrouping ) + pResultSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping; + else + pOldSet->getPropertyValue(nHandleTextGrouping) >>= nTextGrouping; + + if( bHasAnimateForm ) + pResultSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm; + else + pOldSet->getPropertyValue(nHandleAnimateForm) >>= bAnimateForm; + + if( bHasTextGroupingAuto ) + pResultSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto; + else + pOldSet->getPropertyValue(nHandleTextGroupingAuto) >>= fTextGroupingAuto; + + if( bHasTextReverse ) + pResultSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse; + else + pOldSet->getPropertyValue(nHandleTextReverse) >>= bTextReverse; + + EffectSequence const aSelectedEffects( maListSelection ); + for( CustomAnimationEffectPtr const& pEffect : aSelectedEffects ) + { + EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence(); + if( !pEffectSequence ) + pEffectSequence = mpMainSequence.get(); + + sal_Int32 nGroupId = pEffect->getGroupId(); + CustomAnimationTextGroupPtr pTextGroup; + if( nGroupId != -1 ) + { + // use existing group + pTextGroup = pEffectSequence->findGroup( nGroupId ); + } + else + { + // somethings changed so we need a group now + pTextGroup = pEffectSequence->createTextGroup( pEffect, nTextGrouping, fTextGroupingAuto, bAnimateForm, bTextReverse ); + bChanged = true; + } + + //#i119988# + /************************************************************************/ + /* + Note, the setAnimateForm means set the animation from TextGroup to Object's Shape + And on the UI in means "Animate attached shape" in "Effect Option" dialog + The setTextGrouping means set animation to Object's Text, + the nTextGrouping is Text Animation Type + nTextGrouping = -1 is "As one Object", means no text animation. + + The previous call order first do the setTextGrouping and then do the setAnimateForm, + that will cause such defect: in the setTextGrouping, the effect has been removed, + but in setAnimateForm still need this effect, then a NULL pointer of that effect will + be gotten, and cause crash. + + []bHasAnimateForm means the UI has changed, bAnimateForm is it value + + So if create a new textgroup animation, the following animation will never be run! + Since the \A1\B0Animate attached shape\A1\B1 is default checked. + And the bHasAnimateForm default is false, and if user uncheck it the value bAnimateForm will be false, + it same as the TextGroup\A1\AFs default value, also could not be run setAnimateForm. + if( bHasAnimateForm ) + { + if( pTextGroup->getAnimateForm() != bAnimateForm ) + { + pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm ); + bChanged = true; + } + } + + In setTextGrouping, there are three case: + 1. Create new text effects for empty TextGroup + 2. Remove all text effects of TextGroup (nTextGrouping == -1) + 3. Change all the text effects\A1\AF start type + + So here is the right logic: + If set the animation from text to shape and remove text animation, + should do setAnimateForm first, then do setTextGrouping. + Other case,do setTextGrouping first, then do setAnimateForm. + + */ + /************************************************************************/ + + bool bDoSetAnimateFormFirst = false; + bool bNeedDoSetAnimateForm = false; + + if( bHasAnimateForm ) + { + if( pTextGroup && pTextGroup->getAnimateForm() != bAnimateForm ) + { + if( (pTextGroup->getTextGrouping() >= 0) && (nTextGrouping == -1 ) ) + { + bDoSetAnimateFormFirst = true; + } + bNeedDoSetAnimateForm = true; + } + } + + if (bDoSetAnimateFormFirst) + { + pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm ); + bChanged = true; + } + + if( bHasTextGrouping ) + { + if( pTextGroup && pTextGroup->getTextGrouping() != nTextGrouping ) + { + pEffectSequence->setTextGrouping( pTextGroup, nTextGrouping ); + + // All the effects of the outline object is removed so we need to + // put it back. OTOH, the shape object that still has effects + // in the text group is fine. + if (nTextGrouping == -1 && pTextGroup->getEffects().empty()) + { + pEffect->setTarget(Any(pEffect->getTargetShape())); + pEffect->setGroupId(-1); + mpMainSequence->append(pEffect); + } + + bChanged = true; + } + } + + if (!bDoSetAnimateFormFirst && bNeedDoSetAnimateForm) + { + if( pTextGroup ) + { + pEffectSequence->setAnimateForm( pTextGroup, bAnimateForm ); + bChanged = true; + } + } + + if( bHasTextGroupingAuto ) + { + if( pTextGroup && pTextGroup->getTextGroupingAuto() != fTextGroupingAuto ) + { + pEffectSequence->setTextGroupingAuto( pTextGroup, fTextGroupingAuto ); + bChanged = true; + } + } + + if( bHasTextReverse ) + { + if( pTextGroup && pTextGroup->getTextReverse() != bTextReverse ) + { + pEffectSequence->setTextReverse( pTextGroup, bTextReverse ); + bChanged = true; + } + } + } + } + + if( bChanged ) + { + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } +} + +void CustomAnimationPane::showOptions(const OString& rPage) +{ + std::unique_ptr xSet = createSelectionSet(); + + auto xDlg = std::make_shared(GetFrameWeld(), std::move(xSet), rPage); + + weld::DialogController::runAsync(xDlg, [xDlg, this](sal_Int32 nResult){ + if (nResult ) + { + addUndo(); + changeSelection(xDlg->getResultSet(), xDlg->getPropertySet()); + updateControls(); + } + }); +} + +void CustomAnimationPane::onChangeCurrentPage() +{ + if( !mxView.is() ) + return; + + try + { + Reference< XDrawPage > xNewPage( mxView->getCurrentPage() ); + if( xNewPage != mxCurrentPage ) + { + mxCurrentPage = xNewPage; + SdPage* pPage = SdPage::getImplementation( mxCurrentPage ); + if( pPage ) + { + mpMainSequence = pPage->getMainSequence(); + mxCustomAnimationList->update( mpMainSequence ); + } + updateControls(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::onChangeCurrentPage()" ); + } +} + +static bool getTextSelection( const Any& rSelection, Reference< XShape >& xShape, std::vector< sal_Int16 >& rParaList ) +{ + Reference< XTextRange > xSelectedText; + rSelection >>= xSelectedText; + if( xSelectedText.is() ) try + { + xShape.set( xSelectedText->getText(), UNO_QUERY_THROW ); + + css::uno::Reference xLockable(xShape, css::uno::UNO_QUERY); + if (xLockable.is()) + xLockable->addActionLock(); + comphelper::ScopeGuard aGuard([&xLockable]() + { + if (xLockable.is()) + xLockable->removeActionLock(); + }); + + Reference< XTextRangeCompare > xTextRangeCompare( xShape, UNO_QUERY_THROW ); + Reference< XEnumerationAccess > xParaEnumAccess( xShape, UNO_QUERY_THROW ); + Reference< XEnumeration > xParaEnum( xParaEnumAccess->createEnumeration(), UNO_SET_THROW ); + Reference< XTextRange > xRange; + Reference< XTextRange > xStart( xSelectedText->getStart() ); + Reference< XTextRange > xEnd( xSelectedText->getEnd() ); + + if( xTextRangeCompare->compareRegionEnds( xStart, xEnd ) < 0 ) + { + Reference< XTextRange > xTemp( xStart ); + xStart = xEnd; + xEnd = xTemp; + } + + sal_Int16 nPara = 0; + while( xParaEnum->hasMoreElements() ) + { + xParaEnum->nextElement() >>= xRange; + + // break if start of selection is prior to end of current paragraph + if( xRange.is() && (xTextRangeCompare->compareRegionEnds( xStart, xRange ) >= 0 ) ) + break; + + nPara++; + } + + while( xRange.is() ) + { + if( xRange.is() && !xRange->getString().isEmpty() ) + rParaList.push_back( nPara ); + + // break if end of selection is before or at end of current paragraph + if( xRange.is() && xTextRangeCompare->compareRegionEnds( xEnd, xRange ) >= 0 ) + break; + + nPara++; + + if( xParaEnum->hasMoreElements() ) + xParaEnum->nextElement() >>= xRange; + else + xRange.clear(); + } + + return true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::getTextSelection()" ); + } + + return false; +} + +namespace +{ + Reference getTargetShape(const Any& rTarget) + { + Reference xShape; + rTarget >>= xShape; + if( !xShape.is() ) + { + ParagraphTarget aParaTarget; + if (rTarget >>= aParaTarget) + xShape = aParaTarget.Shape; + } + return xShape; + } +} + +void CustomAnimationPane::onAdd() +{ + bool bHasText = true; + + // first create vector of targets for dialog preview + std::vector< Any > aTargets; + + // gather shapes from the selection + Reference< XSelectionSupplier > xSel( mxView, UNO_QUERY_THROW ); + maViewSelection = xSel->getSelection(); + + if( maViewSelection.getValueType() == cppu::UnoType::get()) + { + Reference< XIndexAccess > xShapes; + maViewSelection >>= xShapes; + + sal_Int32 nCount = xShapes->getCount(); + aTargets.reserve( nCount ); + for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + Any aTarget( xShapes->getByIndex( nIndex ) ); + aTargets.push_back( aTarget ); + if( bHasText ) + { + Reference< XText > xText; + aTarget >>= xText; + if( !xText.is() || xText->getString().isEmpty() ) + bHasText = false; + } + } + } + else if ( maViewSelection.getValueType() == cppu::UnoType::get()) + { + aTargets.push_back( maViewSelection ); + Reference< XText > xText; + maViewSelection >>= xText; + if( !xText.is() || xText->getString().isEmpty() ) + bHasText = false; + } + else if ( maViewSelection.getValueType() == cppu::UnoType::get()) + { + Reference< XShape > xShape; + std::vector< sal_Int16 > aParaList; + if( getTextSelection( maViewSelection, xShape, aParaList ) ) + { + ParagraphTarget aParaTarget; + aParaTarget.Shape = xShape; + + for( const auto& rPara : aParaList ) + { + aParaTarget.Paragraph = rPara; + aTargets.push_back( Any( aParaTarget ) ); + } + } + } + else + { + OSL_FAIL("sd::CustomAnimationPane::onAdd(), unknown view selection!" ); + return; + } + + CustomAnimationPresetPtr pDescriptor; + mxFTCategory->set_sensitive(true); + mxFTAnimation->set_sensitive(true); + + bool bCategoryReset = false; + + if (!mxLBCategory->get_sensitive() || mxLBCategory->get_active() == -1) + { + mxLBCategory->set_sensitive(true); + mxLBCategory->set_active(0); + bCategoryReset = true; + } + + if (bCategoryReset || !mxLBAnimation->get_sensitive() || + mxLBAnimation->get_selected_index() == -1) + { + mxLBAnimation->set_sensitive(true); + + sal_Int32 nFirstEffect = fillAnimationLB(bHasText); + if (nFirstEffect == -1) + return; + + mxLBAnimation->select(nFirstEffect); + mnLastSelectedAnimation = nFirstEffect; + } + + auto pEntryData = weld::fromId(mxLBAnimation->get_selected_id()); + if (pEntryData) + pDescriptor = *pEntryData; + + if( pDescriptor ) + { + const double fDuration = pDescriptor->getDuration(); + mxCBXDuration->set_value(fDuration*100.0, FieldUnit::NONE); + bool bHasSpeed = pDescriptor->getDuration() > 0.001; + mxCBXDuration->set_sensitive( bHasSpeed ); + mxFTDuration->set_sensitive( bHasSpeed ); + + mxCustomAnimationList->unselect_all(); + + // gather shapes from the selection + bool bFirst = true; + for( const auto& rTarget : aTargets ) + { + css::uno::Reference xLockable(getTargetShape(rTarget), css::uno::UNO_QUERY); + if (xLockable.is()) + xLockable->addActionLock(); + comphelper::ScopeGuard aGuard([&xLockable]() + { + if (xLockable.is()) + xLockable->removeActionLock(); + }); + + CustomAnimationEffectPtr pCreated = mpMainSequence->append( pDescriptor, rTarget, fDuration ); + + // if only one shape with text and no fill or outline is selected, animate only by first level paragraphs + if( bHasText && (aTargets.size() == 1) ) + { + Reference< XShape > xShape( rTarget, UNO_QUERY ); + if( xShape.is() && !hasVisibleShape( xShape ) ) + { + mpMainSequence->createTextGroup( pCreated, 1, -1.0, false, false ); + } + } + + if( bFirst ) + bFirst = false; + else + pCreated->setNodeType( EffectNodeType::WITH_PREVIOUS ); + + if( pCreated ) + mxCustomAnimationList->select( pCreated ); + } + } + + PathKind ePathKind = getCreatePathKind(); + + if (ePathKind != PathKind::NONE) + { + createPath( ePathKind, aTargets, 0.0 ); + updateMotionPathTags(); + } + + addUndo(); + mrBase.GetDocShell()->SetModified(); + + updateControls(); + + SlideShow::Stop( mrBase ); +} + +void CustomAnimationPane::onRemove() +{ + if( maListSelection.empty() ) + return; + + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + EffectSequence aList( maListSelection ); + + for( CustomAnimationEffectPtr& pEffect : aList ) + { + if( pEffect->getEffectSequence() ) + pEffect->getEffectSequence()->remove( pEffect ); + } + + maListSelection.clear(); + mrBase.GetDocShell()->SetModified(); +} + +void CustomAnimationPane::remove( CustomAnimationEffectPtr const & pEffect ) +{ + if( pEffect->getEffectSequence() ) + { + addUndo(); + pEffect->getEffectSequence()->remove( pEffect ); + mrBase.GetDocShell()->SetModified(); + } +} + +void CustomAnimationPane::onChangeStart() +{ + sal_Int16 nNodeType; + switch( mxLBStart->get_active() ) + { + case 0: nNodeType = EffectNodeType::ON_CLICK; break; + case 1: nNodeType = EffectNodeType::WITH_PREVIOUS; break; + case 2: nNodeType = EffectNodeType::AFTER_PREVIOUS; break; + default: + return; + } + + onChangeStart( nNodeType ); +} + +void CustomAnimationPane::onChangeStart( sal_Int16 nNodeType ) +{ + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + bool bNeedRebuild = false; + + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + if( pEffect->getNodeType() != nNodeType ) + { + pEffect->setNodeType( nNodeType ); + bNeedRebuild = true; + } + } + + if( bNeedRebuild ) + { + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } +} + +void CustomAnimationPane::onChangeSpeed() +{ + double fDuration = getDuration(); + + if(fDuration < 0) + return; + else + { + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + // change selected effect + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + pEffect->setDuration( fDuration ); + } + + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } +} + +double CustomAnimationPane::getDuration() const +{ + double fDuration = 0; + + if (!mxCBXDuration->get_text().isEmpty()) + fDuration = mxCBXDuration->get_value(FieldUnit::NONE) / 100.0; + + return fDuration; +} + +PathKind CustomAnimationPane::getCreatePathKind() const +{ + PathKind eKind = PathKind::NONE; + + if (mxLBAnimation->count_selected_rows() == 1 && + mxLBCategory->get_active() == gnMotionPathPos) + { + const sal_Int32 nPos = mxLBAnimation->get_selected_index(); + if( nPos == mnCurvePathPos ) + { + eKind = PathKind::CURVE; + } + else if( nPos == mnPolygonPathPos ) + { + eKind = PathKind::POLYGON; + } + else if( nPos == mnFreeformPathPos ) + { + eKind = PathKind::FREEFORM; + } + } + + return eKind; +} + +void CustomAnimationPane::createPath( PathKind eKind, std::vector< Any >& rTargets, double fDuration) +{ + sal_uInt16 nSID = 0; + + switch( eKind ) + { + case PathKind::CURVE: nSID = SID_DRAW_BEZIER_NOFILL; break; + case PathKind::POLYGON: nSID = SID_DRAW_POLYGON_NOFILL; break; + case PathKind::FREEFORM: nSID = SID_DRAW_FREELINE_NOFILL; break; + default: break; + } + + if( !nSID ) + return; + + DrawViewShell* pViewShell = dynamic_cast< DrawViewShell* >( + FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get()); + + if( pViewShell ) + { + DrawView* pView = pViewShell->GetDrawView(); + if( pView ) + pView->UnmarkAllObj(); + + std::vector< Any > aTargets( 1, Any( fDuration ) ); + aTargets.insert( aTargets.end(), rTargets.begin(), rTargets.end() ); + Sequence< Any > aTargetSequence( comphelper::containerToSequence( aTargets ) ); + const SfxUnoAnyItem aItem( SID_ADD_MOTION_PATH, Any( aTargetSequence ) ); + pViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( nSID, SfxCallMode::ASYNCHRON, {&aItem} ); + } +} + + +/// this link is called when the property box is modified by the user +IMPL_LINK_NOARG(CustomAnimationPane, implPropertyHdl, LinkParamNone*, void) +{ + if (!mxLBSubControl) + return; + + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + const Any aValue(mxLBSubControl->getValue()); + + bool bNeedUpdate = false; + + // change selected effect + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + if( setProperty1Value( mnPropertyType, pEffect, aValue ) ) + bNeedUpdate = true; + } + + if( bNeedUpdate ) + { + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } + + onPreview( false ); +} + +IMPL_LINK_NOARG(CustomAnimationPane, DelayModifiedHdl, weld::MetricSpinButton&, void) +{ + addUndo(); +} + +IMPL_LINK_NOARG(CustomAnimationPane, DelayLoseFocusHdl, weld::Widget&, void) +{ + double fBegin = mxMFStartDelay->get_value(FieldUnit::NONE); + + //sequence rebuild only when the control loses focus + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + // change selected effect + for( CustomAnimationEffectPtr& pEffect : maListSelection ) + { + pEffect->setBegin( fBegin/10.0 ); + } + + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); +} + +IMPL_LINK_NOARG(CustomAnimationPane, AnimationSelectHdl, weld::TreeView&, void) +{ + maIdle.Start(); +} + +IMPL_LINK_NOARG(CustomAnimationPane, SelectionHandler, Timer*, void) +{ + if (mxLBAnimation->has_grab()) // tdf#136474 try again later + { + maIdle.Start(); + return; + } + + int nSelected = mxLBAnimation->get_selected_index(); + if (nSelected == -1) + return; + + // tdf#99137, the selected entry may also be a subcategory title, so not an effect + // just skip it and move to the next one in this case + if (mxLBAnimation->get_text_emphasis(nSelected, 0)) + { + if (nSelected == 0 || nSelected > mnLastSelectedAnimation) + mxLBAnimation->select(++nSelected); + else + mxLBAnimation->select(--nSelected); + } + + mnLastSelectedAnimation = nSelected; + + CustomAnimationPresetPtr* pPreset = weld::fromId(mxLBAnimation->get_id(nSelected)); + PathKind ePathKind = getCreatePathKind(); + + if ( ePathKind != PathKind::NONE ) + { + std::vector< Any > aTargets; + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + aTargets.push_back( pEffect->getTarget() ); + + EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence(); + if( !pEffectSequence ) + pEffectSequence = mpMainSequence.get(); + + // delete the old animation, new one will be appended + // by createPath and SID_ADD_MOTION_PATH therein + pEffectSequence->remove( pEffect ); + } + + createPath( ePathKind, aTargets, 0.0 ); + updateMotionPathTags(); + return; + } + + CustomAnimationPresetPtr pDescriptor(*pPreset); + const double fDuration = (*pPreset)->getDuration(); + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + // get selected effect + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + // Dispose the deprecated motion path tag. It will be rebuilt later. + if (pEffect->getPresetClass() == css::presentation::EffectPresetClass::MOTIONPATH) + { + for (auto const& xTag: maMotionPathTags) + { + if(xTag->getEffect() == pEffect && !xTag->isDisposed()) + xTag->Dispose(); + } + } + + EffectSequenceHelper* pEffectSequence = pEffect->getEffectSequence(); + if( !pEffectSequence ) + pEffectSequence = mpMainSequence.get(); + + pEffectSequence->replace( pEffect, pDescriptor, fDuration ); + } + + addUndo(); + onPreview(false); +} + +IMPL_LINK_NOARG(CustomAnimationPane, UpdateAnimationLB, weld::ComboBox&, void) +{ + //FIXME: first effect only? what if there is more? + CustomAnimationEffectPtr pEffect = maListSelection.front(); + fillAnimationLB( pEffect->hasText() ); +} + +IMPL_LINK_NOARG(CustomAnimationPane, DurationModifiedHdl, weld::MetricSpinButton&, void) +{ + if (!mxCBXDuration->get_text().isEmpty()) + { + double duration_value = static_cast(mxCBXDuration->get_value(FieldUnit::NONE)); + if(duration_value <= 0.0) + { + mxCBXDuration->set_value(1, FieldUnit::NONE); + } + onChangeSpeed(); + } +} + +namespace +{ + void InsertCategory(weld::TreeView& rLBAnimation, const OUString& rMotionPathLabel) + { + int nRow = rLBAnimation.n_children(); + rLBAnimation.append_text(rMotionPathLabel); + rLBAnimation.set_text_emphasis(nRow, true, 0); + rLBAnimation.set_text_align(nRow, 0.5, 0); + } +} + +sal_Int32 CustomAnimationPane::fillAnimationLB( bool bHasText ) +{ + PresetCategoryList rCategoryList; + sal_uInt16 nPosition = mxLBCategory->get_active(); + const CustomAnimationPresets& rPresets (CustomAnimationPresets::getCustomAnimationPresets()); + switch(nPosition) + { + case 0:rCategoryList = rPresets.getEntrancePresets();break; + case 1:rCategoryList = rPresets.getEmphasisPresets();break; + case 2:rCategoryList = rPresets.getExitPresets();break; + case 3:rCategoryList = rPresets.getMotionPathsPresets();break; + case 4:rCategoryList = rPresets.getMiscPresets();break; + } + + sal_Int32 nFirstEffect = -1; + + int nOldEntryCount = mxLBAnimation->n_children(); + int nOldScrollPos = mxLBAnimation->vadjustment_get_value(); + + mxLBAnimation->freeze(); + mxLBAnimation->clear(); + mnLastSelectedAnimation = -1; + + if (nPosition == gnMotionPathPos) + { + OUString sMotionPathLabel( SdResId( STR_CUSTOMANIMATION_USERPATH ) ); + InsertCategory(*mxLBAnimation, sMotionPathLabel); + mnCurvePathPos = mxLBAnimation->n_children(); + mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulCOMBLINE) ); + mxLBAnimation->set_text_emphasis(mnCurvePathPos, false, 0); + mnPolygonPathPos = mnCurvePathPos + 1; + mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulPOLY) ); + mxLBAnimation->set_text_emphasis(mnPolygonPathPos, false, 0); + mnFreeformPathPos = mnPolygonPathPos + 1; + mxLBAnimation->append_text( SvxResId(STR_ObjNameSingulFREELINE) ); + mxLBAnimation->set_text_emphasis(mnFreeformPathPos, false, 0); + } + + for (const PresetCategoryPtr& pCategory : rCategoryList) + { + if( pCategory ) + { + InsertCategory(*mxLBAnimation, pCategory->maLabel); + + int nPos = mxLBAnimation->n_children(); + + std::vector< CustomAnimationPresetPtr > aSortedVector = + pCategory->maEffects; + + for( const CustomAnimationPresetPtr& pDescriptor : aSortedVector ) + { + // ( !isTextOnly || ( isTextOnly && bHasText ) ) <=> !isTextOnly || bHasText + if( pDescriptor && ( !pDescriptor->isTextOnly() || bHasText ) ) + { + auto pCustomPtr = new CustomAnimationPresetPtr(pDescriptor); + OUString sId = weld::toId(pCustomPtr); + mxLBAnimation->append(sId, pDescriptor->getLabel()); + mxLBAnimation->set_text_emphasis(nPos, false, 0); + + if (nFirstEffect == -1) + nFirstEffect = nPos; + + ++nPos; + } + } + } + } + + mxLBAnimation->thaw(); + + if (mxLBAnimation->n_children() == nOldEntryCount) + mxLBAnimation->vadjustment_set_value(nOldScrollPos); + + return nFirstEffect; +} + +IMPL_LINK(CustomAnimationPane, implToggleHdl, weld::Toggleable&, rBtn, void) +{ + implControlHdl(&rBtn); +} + +IMPL_LINK(CustomAnimationPane, implClickHdl, weld::Button&, rBtn, void) +{ + implControlHdl(&rBtn); +} + +IMPL_LINK( CustomAnimationPane, implControlListBoxHdl, weld::ComboBox&, rListBox, void ) +{ + implControlHdl(&rListBox); +} + +/// this link is called when one of the controls is modified +void CustomAnimationPane::implControlHdl(const weld::Widget* pControl) +{ + if (pControl == mxPBAddEffect.get()) + onAdd(); + else if (pControl == mxPBRemoveEffect.get()) + onRemove(); + else if (pControl == mxLBStart.get()) + onChangeStart(); + else if (pControl == mxPBPropertyMore.get()) + showOptions(); + else if (pControl == mxPBMoveUp.get()) + moveSelection( true ); + else if (pControl == mxPBMoveDown.get()) + moveSelection( false ); + else if (pControl == mxPBPlay.get()) + onPreview( true ); + else if (pControl == mxCBAutoPreview.get()) + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + pOptions->SetPreviewChangedEffects(mxCBAutoPreview->get_active()); + } +} + +IMPL_LINK_NOARG(CustomAnimationPane, lateInitCallback, Timer *, void) +{ + // Call getPresets() to initiate the (expensive) construction of the + // presets list. + CustomAnimationPresets::getCustomAnimationPresets(); + + // update selection and control states + onSelectionChanged(); +} + +void CustomAnimationPane::moveSelection( bool bUp ) +{ + if( maListSelection.empty() ) + return; + + EffectSequenceHelper* pSequence = maListSelection.front()->getEffectSequence(); + if( pSequence == nullptr ) + return; + + addUndo(); + + bool bChanged = false; + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + EffectSequence& rEffectSequence = pSequence->getSequence(); + + if( bUp ) + { + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + EffectSequence::iterator aUpEffectPos( pSequence->find( pEffect ) ); + // coverity[copy_paste_error : FALSE] - this is correct, checking if it exists + if( aUpEffectPos != rEffectSequence.end() ) + { + EffectSequence::iterator aInsertPos( rEffectSequence.erase( aUpEffectPos ) ); + + if( aInsertPos != rEffectSequence.begin() ) + { + --aInsertPos; + while( (aInsertPos != rEffectSequence.begin()) && !mxCustomAnimationList->isExpanded(*aInsertPos)) + --aInsertPos; + rEffectSequence.insert( aInsertPos, pEffect ); + } + else + { + rEffectSequence.push_front( pEffect ); + } + bChanged = true; + } + } + } + else + { + EffectSequence::reverse_iterator aIter( maListSelection.rbegin() ); + const EffectSequence::reverse_iterator aEnd( maListSelection.rend() ); + + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect = *aIter++; + + EffectSequence::iterator aDownEffectPos( pSequence->find( pEffect ) ); + // coverity[copy_paste_error : FALSE] - this is correct, checking if it exists + if( aDownEffectPos != rEffectSequence.end() ) + { + EffectSequence::iterator aInsertPos( rEffectSequence.erase( aDownEffectPos ) ); + + if( aInsertPos != rEffectSequence.end() ) + { + ++aInsertPos; + // Advance over rolled-up (un-expanded) items, unless we just moved it there. + while( (aInsertPos != rEffectSequence.end()) + && !mxCustomAnimationList->isExpanded(*aInsertPos) + && (std::find(maListSelection.begin(), maListSelection.end(), *aInsertPos) + == maListSelection.end()) + ) + ++aInsertPos; + rEffectSequence.insert( aInsertPos, pEffect ); + } + else + { + rEffectSequence.push_back( pEffect ); + } + bChanged = true; + } + } + } + + if( bChanged ) + { + mpMainSequence->rebuild(); + updateControls(); + mrBase.GetDocShell()->SetModified(); + } +} + +void CustomAnimationPane::onPreview( bool bForcePreview ) +{ + if (!bForcePreview && !mxCBAutoPreview->get_active()) + return; + + // No preview in LOK. + if (comphelper::LibreOfficeKit::isActive()) + return; + + if( maListSelection.empty() ) + { + rtl::Reference< MotionPathTag > xMotionPathTag; + auto aIter = std::find_if(maMotionPathTags.begin(), maMotionPathTags.end(), + [](const MotionPathTagVector::value_type& rxMotionPathTag) { return rxMotionPathTag->isSelected(); }); + if (aIter != maMotionPathTags.end()) + xMotionPathTag = *aIter; + + if( xMotionPathTag.is() ) + { + MainSequencePtr pSequence = std::make_shared(); + pSequence->append( xMotionPathTag->getEffect()->clone() ); + preview( pSequence->getRootNode() ); + } + else + { + Reference< XAnimationNodeSupplier > xNodeSupplier( mxCurrentPage, UNO_QUERY ); + if( !xNodeSupplier.is() ) + return; + + preview( xNodeSupplier->getAnimationNode() ); + } + } + else + { + MainSequencePtr pSequence = std::make_shared(); + + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + pSequence->append( pEffect->clone() ); + } + + preview( pSequence->getRootNode() ); + } +} + +void CustomAnimationPane::preview( const Reference< XAnimationNode >& xAnimationNode ) +{ + Reference< XParallelTimeContainer > xRoot = ParallelTimeContainer::create( ::comphelper::getProcessComponentContext() ); + Sequence< css::beans::NamedValue > aUserData + { { "node-type", css::uno::Any(css::presentation::EffectNodeType::TIMING_ROOT) } }; + xRoot->setUserData( aUserData ); + xRoot->appendChild( xAnimationNode ); + + SlideShow::StartPreview( mrBase, mxCurrentPage, xRoot ); +} + +// ICustomAnimationListController +void CustomAnimationPane::onSelect() +{ + maListSelection = mxCustomAnimationList->getSelection(); + updateControls(); + + // mark shapes from selected effects + if( maSelectionLock.isLocked() ) + return; + + // tdf#145030 if nothing is selected in the effects list, leave the selection of + // objects in the slide untouched + if (maListSelection.empty()) + return; + + ScopeLockGuard aGuard( maSelectionLock ); + DrawViewShell* pViewShell = dynamic_cast< DrawViewShell* >( + FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get()); + DrawView* pView = pViewShell ? pViewShell->GetDrawView() : nullptr; + + if( pView ) + { + pView->UnmarkAllObj(); + for( const CustomAnimationEffectPtr& pEffect : maListSelection ) + { + Reference< XShape > xShape( pEffect->getTargetShape() ); + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + if( pObj ) + pView->MarkObj(pObj, pView->GetSdrPageView()); + } + } +} + +// ICustomAnimationListController +// pEffectInsertBefore may be null if moving to end of list. +void CustomAnimationPane::onDragNDropComplete(std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore) +{ + if ( !mpMainSequence ) + return; + + addUndo(); + + MainSequenceRebuildGuard aGuard( mpMainSequence ); + + // Move all selected effects + for( auto const& pEffectDragged : pEffectsDragged ) + { + // Move this dragged effect and any hidden sub-effects + EffectSequence::iterator aIter = mpMainSequence->find( pEffectDragged ); + const EffectSequence::iterator aEnd( mpMainSequence->getEnd() ); + + while( aIter != aEnd ) + { + CustomAnimationEffectPtr pEffect = *aIter++; + + // Update model with new location (function triggers a rebuild) + // target may be null, which will insert at the end. + mpMainSequence->moveToBeforeEffect( pEffect, pEffectInsertBefore ); + // Done moving effect and its hidden sub-effects when *next* effect is visible. + if (aIter != aEnd && mxCustomAnimationList->isVisible(*aIter)) + break; + } + } + + updateControls(); + mrBase.GetDocShell()->SetModified(); +} + +void CustomAnimationPane::updatePathFromMotionPathTag( const rtl::Reference< MotionPathTag >& xTag ) +{ + MainSequenceRebuildGuard aGuard( mpMainSequence ); + if( !xTag.is() ) + return; + + SdrPathObj* pPathObj = xTag->getPathObj(); + CustomAnimationEffectPtr pEffect = xTag->getEffect(); + if( (pPathObj != nullptr) && pEffect ) + { + SfxUndoManager* pManager = mrBase.GetDocShell()->GetUndoManager(); + if( pManager ) + { + SdPage* pPage = SdPage::getImplementation( mxCurrentPage ); + if( pPage ) + pManager->AddUndoAction( std::make_unique( mrBase.GetDocShell()->GetDoc(), pPage, pEffect->getNode() ) ); + } + + pEffect->updatePathFromSdrPathObj( *pPathObj ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/STLPropertySet.cxx b/sd/source/ui/animations/STLPropertySet.cxx new file mode 100644 index 000000000..592d7639c --- /dev/null +++ b/sd/source/ui/animations/STLPropertySet.cxx @@ -0,0 +1,113 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "STLPropertySet.hxx" +#include + +using com::sun::star::uno::Any; + +namespace sd +{ + +STLPropertySet::STLPropertySet() +{ +} + +STLPropertySet::~STLPropertySet() +{ +} + +void STLPropertySet::setPropertyDefaultValue( sal_Int32 nHandle, const Any& rValue ) +{ + STLPropertyMapEntry aEntry( rValue ); + maPropertyMap[ nHandle ] = aEntry; +} + +void STLPropertySet::setPropertyValue( sal_Int32 nHandle, const Any& rValue ) +{ + PropertyMapIter aIter; + if( findProperty( nHandle, aIter ) ) + { + (*aIter).second.mnState = STLPropertyState::Direct; + (*aIter).second.maValue = rValue; + } + else + { + SAL_WARN("sd", "sd::STLPropertySet::setPropertyValue(), unknown property!"); + } +} + +Any STLPropertySet::getPropertyValue( sal_Int32 nHandle ) const +{ + PropertyMapConstIter aIter; + if( findProperty( nHandle, aIter ) ) + { + return (*aIter).second.maValue; + } + else + { + SAL_WARN("sd", "sd::STLPropertySet::getPropertyValue(), unknown property!"); + + Any aAny; + return aAny; + } +} + +STLPropertyState STLPropertySet::getPropertyState( sal_Int32 nHandle ) const +{ + PropertyMapConstIter aIter; + if( findProperty( nHandle, aIter ) ) + { + return (*aIter).second.mnState; + } + else + { + SAL_WARN("sd", "sd::STLPropertySet::getPropertyState(), unknown property!"); + return STLPropertyState::Ambiguous; + } +} + +void STLPropertySet::setPropertyState( sal_Int32 nHandle, STLPropertyState nState ) +{ + PropertyMapIter aIter; + if( findProperty( nHandle, aIter ) ) + { + (*aIter).second.mnState = nState; + } + else + { + SAL_WARN("sd","sd::STLPropertySet::setPropertyState(), unknown property!"); + } +} + +bool STLPropertySet::findProperty( sal_Int32 nHandle, PropertyMapIter& rIter ) +{ + rIter = maPropertyMap.find(nHandle); + return( rIter != maPropertyMap.end() ); +} + +bool STLPropertySet::findProperty( sal_Int32 nHandle, PropertyMapConstIter& rIter ) const +{ + rIter = maPropertyMap.find(nHandle); + return( rIter != maPropertyMap.end() ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/STLPropertySet.hxx b/sd/source/ui/animations/STLPropertySet.hxx new file mode 100644 index 000000000..3096e7c78 --- /dev/null +++ b/sd/source/ui/animations/STLPropertySet.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd +{ + +enum class STLPropertyState { + Default = 0, + Direct = 1, + Ambiguous = 3 +}; + +struct STLPropertyMapEntry +{ + css::uno::Any maValue; + STLPropertyState mnState; + + STLPropertyMapEntry() + : mnState( STLPropertyState::Ambiguous ) {} + explicit STLPropertyMapEntry(css::uno::Any aValue) + : maValue( aValue ), mnState( STLPropertyState::Default ) {} + +}; + +typedef std::map PropertyMap; +typedef PropertyMap::iterator PropertyMapIter; +typedef PropertyMap::const_iterator PropertyMapConstIter; + +class STLPropertySet +{ +public: + STLPropertySet(); + ~STLPropertySet(); + + void setPropertyDefaultValue( sal_Int32 nHandle, const css::uno::Any& rValue ); + void setPropertyValue( sal_Int32 nHandle, const css::uno::Any& rValue ); + css::uno::Any getPropertyValue( sal_Int32 nHandle ) const; + + STLPropertyState getPropertyState( sal_Int32 nHandle ) const; + void setPropertyState( sal_Int32 nHandle, STLPropertyState nState ); + +private: + bool findProperty( sal_Int32 nHandle, PropertyMapIter& rIter ); + bool findProperty( sal_Int32 nHandle, PropertyMapConstIter& rIter ) const; + +private: + PropertyMap maPropertyMap; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/SlideTransitionPane.cxx b/sd/source/ui/animations/SlideTransitionPane.cxx new file mode 100644 index 000000000..846f21c34 --- /dev/null +++ b/sd/source/ui/animations/SlideTransitionPane.cxx @@ -0,0 +1,1155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; + + +namespace sd::impl +{ +struct TransitionEffect +{ + TransitionEffect() : + mnType( 0 ), + mnSubType( 0 ), + mbDirection( true ), + mnFadeColor( 0 ) + { + init(); + } + explicit TransitionEffect( const ::sd::TransitionPreset & rPreset ) : + mnType( rPreset.getTransition()), + mnSubType( rPreset.getSubtype()), + mbDirection( rPreset.getDirection()), + mnFadeColor( rPreset.getFadeColor()) + { + init(); + } + explicit TransitionEffect( const SdPage & rPage ) : + mnType( rPage.getTransitionType() ), + mnSubType( rPage.getTransitionSubtype() ), + mbDirection( rPage.getTransitionDirection() ), + mnFadeColor( rPage.getTransitionFadeColor() ) + { + init(); + + mfDuration = rPage.getTransitionDuration(); + mfTime = rPage.GetTime(); + mePresChange = rPage.GetPresChange(); + mbSoundOn = rPage.IsSoundOn(); + maSound = rPage.GetSoundFile(); + mbLoopSound = rPage.IsLoopSound(); + mbStopSound = rPage.IsStopSound(); + } + + void init() + { + mfDuration = 2.0; + mfTime = 0.0; + mePresChange = PresChange::Manual; + mbSoundOn = false; + mbLoopSound = false; + mbStopSound = false; + + mbEffectAmbiguous = false; + mbDurationAmbiguous = false; + mbTimeAmbiguous = false; + mbPresChangeAmbiguous = false; + mbSoundAmbiguous = false; + mbLoopSoundAmbiguous = false; + } + + void setAllAmbiguous() + { + mbEffectAmbiguous = true; + mbDurationAmbiguous = true; + mbTimeAmbiguous = true; + mbPresChangeAmbiguous = true; + mbSoundAmbiguous = true; + mbLoopSoundAmbiguous = true; + } + + bool operator == ( const ::sd::TransitionPreset & rPreset ) const + { + return + (mnType == rPreset.getTransition()) && + (mnSubType == rPreset.getSubtype()) && + (mbDirection == rPreset.getDirection()) && + (mnFadeColor == rPreset.getFadeColor()); + } + + void applyTo( SdPage & rOutPage ) const + { + if( ! mbEffectAmbiguous ) + { + rOutPage.setTransitionType( mnType ); + rOutPage.setTransitionSubtype( mnSubType ); + rOutPage.setTransitionDirection( mbDirection ); + rOutPage.setTransitionFadeColor( mnFadeColor ); + } + + if( ! mbDurationAmbiguous ) + rOutPage.setTransitionDuration( mfDuration ); + if( ! mbTimeAmbiguous ) + rOutPage.SetTime( mfTime ); + if( ! mbPresChangeAmbiguous ) + rOutPage.SetPresChange( mePresChange ); + if( ! mbSoundAmbiguous ) + { + if( mbStopSound ) + { + rOutPage.SetStopSound( true ); + rOutPage.SetSound( false ); + } + else + { + rOutPage.SetStopSound( false ); + rOutPage.SetSound( mbSoundOn ); + rOutPage.SetSoundFile( maSound ); + } + } + if( ! mbLoopSoundAmbiguous ) + rOutPage.SetLoopSound( mbLoopSound ); + } + + void compareWith( const SdPage & rPage ) + { + TransitionEffect aOtherEffect( rPage ); + mbEffectAmbiguous = mbEffectAmbiguous || aOtherEffect.mbEffectAmbiguous + || (mnType != aOtherEffect.mnType) + || (mnSubType != aOtherEffect.mnSubType) + || (mbDirection != aOtherEffect.mbDirection) + || (mnFadeColor != aOtherEffect.mnFadeColor); + + mbDurationAmbiguous = mbDurationAmbiguous || aOtherEffect.mbDurationAmbiguous || mfDuration != aOtherEffect.mfDuration; + mbTimeAmbiguous = mbTimeAmbiguous || aOtherEffect.mbTimeAmbiguous || mfTime != aOtherEffect.mfTime; + mbPresChangeAmbiguous = mbPresChangeAmbiguous || aOtherEffect.mbPresChangeAmbiguous || mePresChange != aOtherEffect.mePresChange; + mbSoundAmbiguous = mbSoundAmbiguous || aOtherEffect.mbSoundAmbiguous || mbSoundOn != aOtherEffect.mbSoundOn; +#if 0 + // Weird leftover isolated expression with no effect, introduced in 2007 in + // CWS impress122. Ifdeffed out to avoid compiler warning, kept here in case + // somebody who understands this code notices and understands what the + // "right" thing to do might be. + (!mbStopSound && !aOtherEffect.mbStopSound && maSound != aOtherEffect.maSound) || (mbStopSound != aOtherEffect.mbStopSound); +#endif + mbLoopSoundAmbiguous = mbLoopSoundAmbiguous || aOtherEffect.mbLoopSoundAmbiguous || mbLoopSound != aOtherEffect.mbLoopSound; + } + + // effect + sal_Int16 mnType; + sal_Int16 mnSubType; + bool mbDirection; + sal_Int32 mnFadeColor; + + // other settings + double mfDuration; + double mfTime; + PresChange mePresChange; + bool mbSoundOn; + OUString maSound; + bool mbLoopSound; + bool mbStopSound; + + bool mbEffectAmbiguous; + bool mbDurationAmbiguous; + bool mbTimeAmbiguous; + bool mbPresChangeAmbiguous; + bool mbSoundAmbiguous; + bool mbLoopSoundAmbiguous; +}; + +} // namespace sd::impl + +// Local Helper Functions +namespace +{ + +void lcl_ApplyToPages( + const ::sd::slidesorter::SharedPageSelection& rpPages, + const ::sd::impl::TransitionEffect & rEffect ) +{ + for( const auto& rpPage : *rpPages ) + { + rEffect.applyTo( *rpPage ); + } +} + +void lcl_CreateUndoForPages( + const ::sd::slidesorter::SharedPageSelection& rpPages, + ::sd::ViewShellBase const & rBase ) +{ + ::sd::DrawDocShell* pDocSh = rBase.GetDocShell(); + if (!pDocSh) + return; + SfxUndoManager* pManager = pDocSh->GetUndoManager(); + if (!pManager) + return; + SdDrawDocument* pDoc = pDocSh->GetDoc(); + if (!pDoc) + return; + + OUString aComment( SdResId(STR_UNDO_SLIDE_PARAMS) ); + pManager->EnterListAction(aComment, aComment, 0, rBase.GetViewShellId()); + std::unique_ptr pUndoGroup(new SdUndoGroup( pDoc )); + pUndoGroup->SetComment( aComment ); + + for( const auto& rpPage : *rpPages ) + { + pUndoGroup->AddAction( new sd::UndoTransition( pDoc, rpPage ) ); + } + + pManager->AddUndoAction( std::move(pUndoGroup) ); + pManager->LeaveListAction(); +} + +struct lcl_EqualsSoundFileName +{ + explicit lcl_EqualsSoundFileName( const OUString & rStr ) : + maStr( rStr ) + {} + + bool operator() ( const OUString & rStr ) const + { + // note: formerly this was a case insensitive search for all + // platforms. It seems more sensible to do this platform-dependent + INetURLObject aURL(rStr); +#if defined(_WIN32) + return maStr.equalsIgnoreAsciiCase( aURL.GetBase() ); +#else + return maStr == aURL.GetBase(); +#endif + } + +private: + OUString maStr; +}; + +// returns -1 if no object was found +bool lcl_findSoundInList( const ::std::vector< OUString > & rSoundList, + std::u16string_view rFileName, + ::std::vector< OUString >::size_type & rOutPosition ) +{ + INetURLObject aURL(rFileName); + ::std::vector< OUString >::const_iterator aIt = + ::std::find_if( rSoundList.begin(), rSoundList.end(), + lcl_EqualsSoundFileName( aURL.GetBase())); + if( aIt != rSoundList.end()) + { + rOutPosition = ::std::distance( rSoundList.begin(), aIt ); + return true; + } + + return false; +} + +OUString lcl_getSoundFileURL( + const ::std::vector< OUString > & rSoundList, + const weld::ComboBox& rListBox ) +{ + sal_Int32 nPos = rListBox.get_active(); + // the first three entries are no actual sounds + if( nPos >= 3 ) + { + DBG_ASSERT( static_cast(rListBox.get_count() - 3) == rSoundList.size(), + "Sound list-box is not synchronized to sound list" ); + nPos -= 3; + if( rSoundList.size() > o3tl::make_unsigned(nPos) ) + return rSoundList[ nPos ]; + } + + return OUString(); +} + +struct lcl_AppendSoundToListBox +{ + explicit lcl_AppendSoundToListBox(weld::ComboBox& rListBox) + : mrListBox( rListBox ) + {} + + void operator() ( std::u16string_view rString ) const + { + INetURLObject aURL( rString ); + mrListBox.append_text( aURL.GetBase() ); + } + +private: + weld::ComboBox& mrListBox; +}; + +void lcl_FillSoundListBox( + const ::std::vector< OUString > & rSoundList, + weld::ComboBox& rOutListBox ) +{ + sal_Int32 nCount = rOutListBox.get_count(); + + // keep first three entries + for( sal_Int32 i=nCount - 1; i>=3; --i ) + rOutListBox.remove( i ); + + ::std::for_each( rSoundList.begin(), rSoundList.end(), + lcl_AppendSoundToListBox( rOutListBox )); +} + +/// Returns an offset into the list of transition presets +size_t getPresetOffset( const sd::impl::TransitionEffect &rEffect ) +{ + const sd::TransitionPresetList& rPresetList = + sd::TransitionPreset::getTransitionPresetList(); + + size_t nIdx = 0; + for( const auto& aIt: rPresetList ) + { + if( rEffect.operator==( *aIt )) + break; + nIdx++; + } + return nIdx; +} + +} // anonymous namespace + +namespace sd +{ + +class TransitionPane : public ValueSet +{ +public: + explicit TransitionPane(std::unique_ptr pScrolledWindow) + : ValueSet(std::move(pScrolledWindow)) + { + } + + void Recalculate() + { + GetScrollBar()->set_vpolicy(VclPolicyType::AUTOMATIC); + RecalculateItemSizes(); + } + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override + { + Size aSize = pDrawingArea->get_ref_device().LogicToPixel(Size(70, 88), MapMode(MapUnit::MapAppFont)); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + ValueSet::SetDrawingArea(pDrawingArea); + SetOutputSizePixel(aSize); + + SetStyle(GetStyle() | WB_ITEMBORDER | WB_FLATVALUESET | WB_VSCROLL); + EnableFullItemMode( false ); + SetColCount(3); + } +}; + +// SlideTransitionPane +SlideTransitionPane::SlideTransitionPane( + weld::Widget* pParent, + ViewShellBase & rBase) : + PanelLayout( pParent, "SlideTransitionsPanel", "modules/simpress/ui/slidetransitionspanel.ui" ), + mrBase( rBase ), + mpDrawDoc( rBase.GetDocShell() ? rBase.GetDocShell()->GetDoc() : nullptr ), + mbHasSelection( false ), + mbUpdatingControls( false ), + mbIsMainViewChangePending( false ), + maLateInitTimer("sd SlideTransitionPane maLateInitTimer") +{ + Initialize(mpDrawDoc); +} + +css::ui::LayoutSize SlideTransitionPane::GetHeightForWidth(const sal_Int32 /*nWidth*/) +{ + sal_Int32 nMinimumHeight = get_preferred_size().Height(); + return css::ui::LayoutSize(nMinimumHeight, -1, nMinimumHeight); +} + +constexpr sal_uInt16 nNoneId = std::numeric_limits::max(); + +void SlideTransitionPane::Initialize(SdDrawDocument* pDoc) +{ + mxFT_VARIANT = m_xBuilder->weld_label("variant_label"); + mxLB_VARIANT = m_xBuilder->weld_combo_box("variant_list"); + mxFT_duration = m_xBuilder->weld_label("duration_label"); + mxCBX_duration = m_xBuilder->weld_metric_spin_button("transition_duration", FieldUnit::SECOND); + mxFT_SOUND = m_xBuilder->weld_label("sound_label"); + mxLB_SOUND = m_xBuilder->weld_combo_box("sound_list"); + mxCB_LOOP_SOUND = m_xBuilder->weld_check_button("loop_sound"); + mxRB_ADVANCE_ON_MOUSE = m_xBuilder->weld_radio_button("rb_mouse_click"); + mxRB_ADVANCE_AUTO = m_xBuilder->weld_radio_button("rb_auto_after"); + mxMF_ADVANCE_AUTO_AFTER = m_xBuilder->weld_metric_spin_button("auto_after_value", FieldUnit::SECOND); + mxPB_APPLY_TO_ALL = m_xBuilder->weld_button("apply_to_all"); + mxPB_PLAY = m_xBuilder->weld_button("play"); + mxCB_AUTO_PREVIEW = m_xBuilder->weld_check_button("auto_preview"); + + auto nMax = mxMF_ADVANCE_AUTO_AFTER->get_max(FieldUnit::SECOND); + mxMF_ADVANCE_AUTO_AFTER->set_max(99, FieldUnit::SECOND); + int nWidthChars = mxMF_ADVANCE_AUTO_AFTER->get_width_chars(); + mxMF_ADVANCE_AUTO_AFTER->set_max(nMax, FieldUnit::SECOND); + mxMF_ADVANCE_AUTO_AFTER->set_width_chars(nWidthChars); + mxCBX_duration->set_width_chars(nWidthChars); + + mxVS_TRANSITION_ICONS.reset(new TransitionPane(m_xBuilder->weld_scrolled_window("transitions_iconswin", true))); + mxVS_TRANSITION_ICONSWin.reset(new weld::CustomWeld(*m_xBuilder, "transitions_icons", *mxVS_TRANSITION_ICONS)); + + if( pDoc ) + mxModel.set( pDoc->getUnoModel(), uno::UNO_QUERY ); + // TODO: get correct view + if( mxModel.is()) + mxView.set( mxModel->getCurrentController(), uno::UNO_QUERY ); + + // dummy list box of slide transitions for startup. + mxVS_TRANSITION_ICONS->InsertItem( + nNoneId, Image( StockImage::Yes, "sd/cmd/transition-none.png" ), + SdResId( STR_SLIDETRANSITION_NONE ), + VALUESET_APPEND, /* show legend */ true ); + mxVS_TRANSITION_ICONS->Recalculate(); + + // set defaults + mxCB_AUTO_PREVIEW->set_active(true); // automatic preview on + + // update control states before adding handlers + updateControls(); + + // set handlers + mxPB_APPLY_TO_ALL->connect_clicked( LINK( this, SlideTransitionPane, ApplyToAllButtonClicked )); + mxPB_PLAY->connect_clicked( LINK( this, SlideTransitionPane, PlayButtonClicked )); + + mxVS_TRANSITION_ICONS->SetSelectHdl( LINK( this, SlideTransitionPane, TransitionSelected )); + + mxLB_VARIANT->connect_changed( LINK( this, SlideTransitionPane, VariantListBoxSelected )); + mxCBX_duration->connect_value_changed(LINK( this, SlideTransitionPane, DurationModifiedHdl)); + mxCBX_duration->connect_focus_out(LINK( this, SlideTransitionPane, DurationLoseFocusHdl)); + mxLB_SOUND->connect_changed( LINK( this, SlideTransitionPane, SoundListBoxSelected )); + mxCB_LOOP_SOUND->connect_toggled( LINK( this, SlideTransitionPane, LoopSoundBoxChecked )); + + mxRB_ADVANCE_ON_MOUSE->connect_toggled( LINK( this, SlideTransitionPane, AdvanceSlideRadioButtonToggled )); + mxRB_ADVANCE_AUTO->connect_toggled( LINK( this, SlideTransitionPane, AdvanceSlideRadioButtonToggled )); + mxMF_ADVANCE_AUTO_AFTER->connect_value_changed( LINK( this, SlideTransitionPane, AdvanceTimeModified )); + mxCB_AUTO_PREVIEW->connect_toggled( LINK( this, SlideTransitionPane, AutoPreviewClicked )); + addListener(); + + maLateInitTimer.SetTimeout(200); + maLateInitTimer.SetInvokeHandler(LINK(this, SlideTransitionPane, LateInitCallback)); + maLateInitTimer.Start(); +} + +SlideTransitionPane::~SlideTransitionPane() +{ + maLateInitTimer.Stop(); + removeListener(); + mxVS_TRANSITION_ICONSWin.reset(); + mxVS_TRANSITION_ICONS.reset(); + mxFT_VARIANT.reset(); + mxLB_VARIANT.reset(); + mxFT_duration.reset(); + mxCBX_duration.reset(); + mxFT_SOUND.reset(); + mxLB_SOUND.reset(); + mxCB_LOOP_SOUND.reset(); + mxRB_ADVANCE_ON_MOUSE.reset(); + mxRB_ADVANCE_AUTO.reset(); + mxMF_ADVANCE_AUTO_AFTER.reset(); + mxPB_APPLY_TO_ALL.reset(); + mxPB_PLAY.reset(); + mxCB_AUTO_PREVIEW.reset(); +} + +void SlideTransitionPane::onSelectionChanged() +{ + updateControls(); +} + +void SlideTransitionPane::onChangeCurrentPage() +{ + updateControls(); +} + +::sd::slidesorter::SharedPageSelection SlideTransitionPane::getSelectedPages() const +{ + ::sd::slidesorter::SlideSorterViewShell * pSlideSorterViewShell + = ::sd::slidesorter::SlideSorterViewShell::GetSlideSorter(mrBase); + std::shared_ptr pSelection; + + if( pSlideSorterViewShell ) + { + pSelection = pSlideSorterViewShell->GetPageSelection(); + } + else + { + pSelection = std::make_shared(); + if( mxView.is() ) + { + SdPage* pPage = SdPage::getImplementation( mxView->getCurrentPage() ); + if( pPage ) + pSelection->push_back(pPage); + } + } + + return pSelection; +} + +void SlideTransitionPane::updateControls() +{ + ::sd::slidesorter::SharedPageSelection pSelectedPages(getSelectedPages()); + if( pSelectedPages->empty()) + { + mbHasSelection = false; + return; + } + mbHasSelection = true; + + DBG_ASSERT( ! mbUpdatingControls, "Multiple Control Updates" ); + mbUpdatingControls = true; + + // get model data for first page + SdPage * pFirstPage = pSelectedPages->front(); + DBG_ASSERT( pFirstPage, "Invalid Page" ); + + impl::TransitionEffect aEffect( *pFirstPage ); + + // merge with other pages + + // start with second page (note aIt != aEndIt, because ! aSelectedPages.empty()) + for( const auto& rpPage : *pSelectedPages ) + { + if( rpPage ) + aEffect.compareWith( *rpPage ); + } + + // detect current slide effect + if( aEffect.mbEffectAmbiguous ) + { + SAL_WARN( "sd.transitions", "Unusual, ambiguous transition effect" ); + mxVS_TRANSITION_ICONS->SelectItem(nNoneId); + } + else + { + // ToDo: That 0 is "no transition" is documented nowhere except in the + // CTOR of sdpage + if( aEffect.mnType == 0 ) + mxVS_TRANSITION_ICONS->SelectItem(nNoneId); + else + updateVariants( getPresetOffset( aEffect ) ); + } + + if( aEffect.mbDurationAmbiguous ) + { + mxCBX_duration->set_text(""); +//TODO mxCBX_duration->SetNoSelection(); + } + else + { + mxCBX_duration->set_value( (aEffect.mfDuration)*100.0, FieldUnit::SECOND ); + } + + if( aEffect.mbSoundAmbiguous ) + { + mxLB_SOUND->set_active(-1); + maCurrentSoundFile.clear(); + } + else + { + maCurrentSoundFile.clear(); + if( aEffect.mbStopSound ) + { + mxLB_SOUND->set_active( 1 ); + } + else if( aEffect.mbSoundOn && !aEffect.maSound.isEmpty() ) + { + std::vector::size_type nPos = 0; + if( lcl_findSoundInList( maSoundList, aEffect.maSound, nPos )) + { + mxLB_SOUND->set_active( nPos + 3 ); + maCurrentSoundFile = aEffect.maSound; + } + } + else + { + mxLB_SOUND->set_active( 0 ); + } + } + + if( aEffect.mbLoopSoundAmbiguous ) + { + mxCB_LOOP_SOUND->set_state(TRISTATE_INDET); + } + else + { + mxCB_LOOP_SOUND->set_active(aEffect.mbLoopSound); + } + + if( aEffect.mbPresChangeAmbiguous ) + { + mxRB_ADVANCE_ON_MOUSE->set_active( false ); + mxRB_ADVANCE_AUTO->set_active( false ); + } + else + { + mxRB_ADVANCE_ON_MOUSE->set_active( aEffect.mePresChange == PresChange::Manual ); + mxRB_ADVANCE_AUTO->set_active( aEffect.mePresChange == PresChange::Auto ); + mxMF_ADVANCE_AUTO_AFTER->set_value(aEffect.mfTime * 100.0, FieldUnit::SECOND); + } + + if (comphelper::LibreOfficeKit::isActive()) + { + mxPB_PLAY->hide(); + mxCB_AUTO_PREVIEW->set_active(false); + mxCB_AUTO_PREVIEW->hide(); + mxFT_SOUND->hide(); + mxLB_SOUND->hide(); + mxCB_LOOP_SOUND->hide(); + } + else + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + mxCB_AUTO_PREVIEW->set_active( pOptions->IsPreviewTransitions() ); + } + + mbUpdatingControls = false; + + updateControlState(); +} + +void SlideTransitionPane::updateControlState() +{ + mxVS_TRANSITION_ICONSWin->set_sensitive( mbHasSelection ); + mxLB_VARIANT->set_sensitive( mbHasSelection && mxLB_VARIANT->get_count() > 0 ); + mxCBX_duration->set_sensitive( mbHasSelection ); + mxLB_SOUND->set_sensitive( mbHasSelection ); + mxCB_LOOP_SOUND->set_sensitive( mbHasSelection && (mxLB_SOUND->get_active() > 2)); + mxRB_ADVANCE_ON_MOUSE->set_sensitive( mbHasSelection ); + mxRB_ADVANCE_AUTO->set_sensitive( mbHasSelection ); + mxMF_ADVANCE_AUTO_AFTER->set_sensitive( mbHasSelection && mxRB_ADVANCE_AUTO->get_active()); + + mxPB_APPLY_TO_ALL->set_sensitive( mbHasSelection ); + mxPB_PLAY->set_sensitive( mbHasSelection ); + mxCB_AUTO_PREVIEW->set_sensitive( mbHasSelection ); +} + +void SlideTransitionPane::updateSoundList() +{ + maSoundList.clear(); + + GalleryExplorer::FillObjList( GALLERY_THEME_SOUNDS, maSoundList ); + GalleryExplorer::FillObjList( GALLERY_THEME_USERSOUNDS, maSoundList ); + + lcl_FillSoundListBox( maSoundList, *mxLB_SOUND ); +} + +void SlideTransitionPane::openSoundFileDialog() +{ + if( ! mxLB_SOUND->get_sensitive()) + return; + + SdOpenSoundFileDialog aFileDialog(GetFrameWeld()); + + DBG_ASSERT( mxLB_SOUND->get_active() == 2, + "Dialog should only open when \"Other sound\" is selected" ); + + bool bValidSoundFile( false ); + bool bQuitLoop( false ); + + while( ! bQuitLoop && + aFileDialog.Execute() == ERRCODE_NONE ) + { + OUString aFile = aFileDialog.GetPath(); + std::vector::size_type nPos = 0; + bValidSoundFile = lcl_findSoundInList( maSoundList, aFile, nPos ); + + if( bValidSoundFile ) + { + bQuitLoop = true; + } + else // not in sound list + { + // try to insert into gallery + if( GalleryExplorer::InsertURL( GALLERY_THEME_USERSOUNDS, aFile ) ) + { + updateSoundList(); + bValidSoundFile = lcl_findSoundInList( maSoundList, aFile, nPos ); + DBG_ASSERT( bValidSoundFile, "Adding sound to gallery failed" ); + + bQuitLoop = true; + } + else + { + OUString aStrWarning(SdResId(STR_WARNING_NOSOUNDFILE)); + aStrWarning = aStrWarning.replaceFirst("%", aFile); + std::unique_ptr xWarn(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::NONE, + aStrWarning)); + xWarn->add_button(GetStandardText(StandardButtonType::Retry), RET_RETRY); + xWarn->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + bQuitLoop = (xWarn->run() != RET_RETRY); + + bValidSoundFile = false; + } + } + + if( bValidSoundFile ) + // skip first three entries in list + mxLB_SOUND->set_active( nPos + 3 ); + } + + if( bValidSoundFile ) + return; + + if( !maCurrentSoundFile.isEmpty() ) + { + std::vector::size_type nPos = 0; + if( lcl_findSoundInList( maSoundList, maCurrentSoundFile, nPos )) + mxLB_SOUND->set_active( nPos + 3 ); + else + mxLB_SOUND->set_active( 0 ); // NONE + } + else + mxLB_SOUND->set_active( 0 ); // NONE +} + +impl::TransitionEffect SlideTransitionPane::getTransitionEffectFromControls() const +{ + impl::TransitionEffect aResult; + aResult.setAllAmbiguous(); + + bool bNoneSelected = mxVS_TRANSITION_ICONS->IsNoSelection() || mxVS_TRANSITION_ICONS->GetSelectedItemId() == nNoneId; + + // check first (aResult might be overwritten) + if( mxVS_TRANSITION_ICONSWin->get_sensitive() && + !bNoneSelected && + mxVS_TRANSITION_ICONS->GetSelectedItemId() > 0 ) + { + const sd::TransitionPresetList& rPresetList = sd::TransitionPreset::getTransitionPresetList(); + auto aSelected = rPresetList.begin(); + std::advance( aSelected, mxVS_TRANSITION_ICONS->GetSelectedItemId() - 1); + + if (mxLB_VARIANT->get_active() == -1) + { + // Transition with just one effect. + aResult = impl::TransitionEffect( **aSelected ); + aResult.setAllAmbiguous(); + } + else + { + int nVariant = 0; + bool bFound = false; + for( const auto& aIter: rPresetList ) + { + if( aIter->getSetId() == (*aSelected)->getSetId() ) + { + if( mxLB_VARIANT->get_active() == nVariant) + { + aResult = impl::TransitionEffect( *aIter ); + aResult.setAllAmbiguous(); + bFound = true; + break; + } + else + { + nVariant++; + } + } + } + if( !bFound ) + { + aResult.mnType = 0; + } + } + aResult.mbEffectAmbiguous = false; + } + else if (bNoneSelected) + { + aResult.mbEffectAmbiguous = false; + } + + //duration + + if( mxCBX_duration->get_sensitive() && (!(mxCBX_duration->get_text()).isEmpty()) ) + { + aResult.mfDuration = static_cast(mxCBX_duration->get_value(FieldUnit::SECOND))/100.0; + aResult.mbDurationAmbiguous = false; + } + + // slide-advance mode + if( mxRB_ADVANCE_ON_MOUSE->get_sensitive() && mxRB_ADVANCE_AUTO->get_sensitive() && + (mxRB_ADVANCE_ON_MOUSE->get_active() || mxRB_ADVANCE_AUTO->get_active())) + { + if( mxRB_ADVANCE_ON_MOUSE->get_active()) + aResult.mePresChange = PresChange::Manual; + else + { + aResult.mePresChange = PresChange::Auto; + if( mxMF_ADVANCE_AUTO_AFTER->get_sensitive()) + { + aResult.mfTime = static_cast(mxMF_ADVANCE_AUTO_AFTER->get_value(FieldUnit::SECOND) ) / 100.0 ; + aResult.mbTimeAmbiguous = false; + } + } + + aResult.mbPresChangeAmbiguous = false; + } + + // sound + if( mxLB_SOUND->get_sensitive()) + { + maCurrentSoundFile.clear(); + sal_Int32 nPos = mxLB_SOUND->get_active(); + if (nPos != -1) + { + aResult.mbStopSound = nPos == 1; + aResult.mbSoundOn = nPos > 1; + if( aResult.mbStopSound ) + { + aResult.maSound.clear(); + aResult.mbSoundAmbiguous = false; + } + else + { + aResult.maSound = lcl_getSoundFileURL(maSoundList, *mxLB_SOUND); + aResult.mbSoundAmbiguous = false; + maCurrentSoundFile = aResult.maSound; + } + } + } + + // sound loop + if( mxCB_LOOP_SOUND->get_sensitive() ) + { + aResult.mbLoopSound = mxCB_LOOP_SOUND->get_active(); + aResult.mbLoopSoundAmbiguous = false; + } + + return aResult; +} + +void SlideTransitionPane::applyToSelectedPages(bool bPreview = true) +{ + if( mbUpdatingControls ) + return; + + vcl::Window *pFocusWindow = Application::GetFocusWindow(); + + ::sd::slidesorter::SharedPageSelection pSelectedPages( getSelectedPages()); + impl::TransitionEffect aEffect = getTransitionEffectFromControls(); + if( ! pSelectedPages->empty()) + { + lcl_CreateUndoForPages( pSelectedPages, mrBase ); + lcl_ApplyToPages( pSelectedPages, aEffect ); + mrBase.GetDocShell()->SetModified(); + } + if( mxCB_AUTO_PREVIEW->get_sensitive() && + mxCB_AUTO_PREVIEW->get_active() && bPreview) + { + if (aEffect.mnType) // mnType = 0 denotes no transition + playCurrentEffect(); + else if( mxView.is() ) + SlideShow::Stop( mrBase ); + } + + if (pFocusWindow) + pFocusWindow->GrabFocus(); +} + +void SlideTransitionPane::playCurrentEffect() +{ + if( mxView.is() ) + { + + Reference< css::animations::XAnimationNode > xNode; + SlideShow::StartPreview( mrBase, mxView->getCurrentPage(), xNode ); + } +} + +void SlideTransitionPane::addListener() +{ + Link aLink( LINK(this,SlideTransitionPane,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener( aLink ); +} + +void SlideTransitionPane::removeListener() +{ + Link aLink( LINK(this,SlideTransitionPane,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(SlideTransitionPane,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::EditViewSelection: + onSelectionChanged(); + break; + + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::SlideSortedSelection: + onChangeCurrentPage(); + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxView.clear(); + onSelectionChanged(); + onChangeCurrentPage(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending) + { + mbIsMainViewChangePending = false; + + // At this moment the controller may not yet been set at + // model or ViewShellBase. Take it from the view shell + // passed with the event. + if (mrBase.GetMainViewShell() != nullptr) + { + mxView.set(mrBase.GetController(), css::uno::UNO_QUERY); + onSelectionChanged(); + onChangeCurrentPage(); + } + } + break; + + default: + if (rEvent.meEventId != EventMultiplexerEventId::Disposing) + { + onSelectionChanged(); + onChangeCurrentPage(); + } + break; + } +} + +IMPL_LINK_NOARG(SlideTransitionPane, ApplyToAllButtonClicked, weld::Button&, void) +{ + DBG_ASSERT( mpDrawDoc, "Invalid Draw Document!" ); + if( !mpDrawDoc ) + return; + + ::sd::slidesorter::SharedPageSelection pPages = + std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>(); + + sal_uInt16 nPageCount = mpDrawDoc->GetSdPageCount( PageKind::Standard ); + pPages->reserve( nPageCount ); + for( sal_uInt16 i=0; iGetSdPage( i, PageKind::Standard ); + if( pPage ) + pPages->push_back( pPage ); + } + + if( ! pPages->empty()) + { + lcl_CreateUndoForPages( pPages, mrBase ); + lcl_ApplyToPages( pPages, getTransitionEffectFromControls() ); + } +} + +IMPL_LINK_NOARG(SlideTransitionPane, PlayButtonClicked, weld::Button&, void) +{ + playCurrentEffect(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, TransitionSelected, ValueSet*, void) +{ + updateVariants( mxVS_TRANSITION_ICONS->GetSelectedItemId() - 1 ); + applyToSelectedPages(); +} + +/// we use an integer offset into the list of transition presets +void SlideTransitionPane::updateVariants( size_t nPresetOffset ) +{ + const sd::TransitionPresetList& rPresetList = sd::TransitionPreset::getTransitionPresetList(); + mxLB_VARIANT->clear(); + mxVS_TRANSITION_ICONS->SelectItem(nNoneId); + + if( nPresetOffset >= rPresetList.size() ) + { + mxLB_VARIANT->set_sensitive( false ); + } + else + { + auto pFound = rPresetList.begin(); + std::advance( pFound, nPresetOffset ); + + // Fill in the variant listbox + size_t nFirstItem = 0, nItem = 1; + for( const auto& aIt: rPresetList ) + { + if( aIt->getSetId() == (*pFound)->getSetId() ) + { + if (!nFirstItem) + nFirstItem = nItem; + if( !aIt->getVariantLabel().isEmpty() ) + { + mxLB_VARIANT->append_text( aIt->getVariantLabel() ); + if( *pFound == aIt ) + mxLB_VARIANT->set_active( mxLB_VARIANT->get_count()-1 ); + } + } + nItem++; + } + + if( mxLB_VARIANT->get_count() == 0 ) + mxLB_VARIANT->set_sensitive( false ); + else + mxLB_VARIANT->set_sensitive(true); + + // item has the id of the first transition from this set. + mxVS_TRANSITION_ICONS->SelectItem( nFirstItem ); + } +} + +IMPL_LINK_NOARG(SlideTransitionPane, AdvanceSlideRadioButtonToggled, weld::Toggleable&, void) +{ + updateControlState(); + applyToSelectedPages(false); +} + +IMPL_LINK_NOARG(SlideTransitionPane, AdvanceTimeModified, weld::MetricSpinButton&, void) +{ + applyToSelectedPages(false); +} + +IMPL_LINK_NOARG(SlideTransitionPane, VariantListBoxSelected, weld::ComboBox&, void) +{ + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, DurationModifiedHdl, weld::MetricSpinButton&, void) +{ + double duration_value = static_cast(mxCBX_duration->get_value(FieldUnit::SECOND)); + if (duration_value <= 0.0) + mxCBX_duration->set_value(0, FieldUnit::SECOND); + else + mxCBX_duration->set_value(duration_value, FieldUnit::SECOND); + + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, DurationLoseFocusHdl, weld::Widget&, void) +{ + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, SoundListBoxSelected, weld::ComboBox&, void) +{ + sal_Int32 nPos = mxLB_SOUND->get_active(); + if( nPos == 2 ) + { + // other sound... + openSoundFileDialog(); + } + updateControlState(); + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, LoopSoundBoxChecked, weld::Toggleable&, void) +{ + applyToSelectedPages(); +} + +IMPL_LINK_NOARG(SlideTransitionPane, AutoPreviewClicked, weld::Toggleable&, void) +{ + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + pOptions->SetPreviewTransitions( mxCB_AUTO_PREVIEW->get_active() ); +} + +IMPL_LINK_NOARG(SlideTransitionPane, LateInitCallback, Timer *, void) +{ + const TransitionPresetList& rPresetList = TransitionPreset::getTransitionPresetList(); + + size_t nPresetOffset = 0; + for( const TransitionPresetPtr& pPreset: rPresetList ) + { + const OUString sLabel( pPreset->getSetLabel() ); + if( !sLabel.isEmpty() ) + { + if( m_aNumVariants.find( pPreset->getSetId() ) == m_aNumVariants.end() ) + { + OUString sImageName("sd/cmd/transition-" + pPreset->getSetId() + ".png"); + BitmapEx aIcon( sImageName ); + if ( aIcon.IsEmpty() ) // need a fallback + sImageName = "sd/cmd/transition-none.png"; + + mxVS_TRANSITION_ICONS->InsertItem( + nPresetOffset + 1, Image(StockImage::Yes, sImageName), sLabel, + VALUESET_APPEND, /* show legend */ true ); + + m_aNumVariants[ pPreset->getSetId() ] = 1; + } + else + { + m_aNumVariants[ pPreset->getSetId() ]++; + } + } + nPresetOffset++; + } + mxVS_TRANSITION_ICONS->Recalculate(); + + SAL_INFO( "sd.transitions", "Item transition offsets in ValueSet:"); + for( size_t i = 0; i < mxVS_TRANSITION_ICONS->GetItemCount(); ++i ) + SAL_INFO( "sd.transitions", i << ":" << mxVS_TRANSITION_ICONS->GetItemId( i ) ); + + nPresetOffset = 0; + SAL_INFO( "sd.transitions", "Transition presets by offsets:"); + for( const auto& aIter: rPresetList ) + { + SAL_INFO( "sd.transitions", nPresetOffset++ << " " << + aIter->getPresetId() << ": " << aIter->getSetId() ); + } + + updateSoundList(); + updateControls(); +} + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/motionpathtag.cxx b/sd/source/ui/animations/motionpathtag.cxx new file mode 100644 index 000000000..ced685395 --- /dev/null +++ b/sd/source/ui/animations/motionpathtag.cxx @@ -0,0 +1,1200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "motionpathtag.hxx" +#include +#include + +#include +#include + +using sdr::PolyPolygonEditor; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::drawing; + +namespace sd +{ + +const sal_uInt32 SMART_TAG_HDL_NUM = SAL_MAX_UINT32; +const int DRGPIX = 2; // Drag MinMove in Pixel + +namespace { + +class PathDragMove : public SdrDragMove +{ +private: + basegfx::B2DPolyPolygon maPathPolyPolygon; + +protected: + virtual void createSdrDragEntries() override; + +public: + PathDragMove(SdrDragView& rNewView, + const rtl::Reference & xTag, + const basegfx::B2DPolyPolygon& rPathPolyPolygon) + : SdrDragMove(rNewView), + maPathPolyPolygon(rPathPolyPolygon), + mxTag( xTag ) + {} + + PathDragMove(SdrDragView& rNewView, + const rtl::Reference & xTag) + : SdrDragMove(rNewView), + mxTag( xTag ) + {} + + virtual bool BeginSdrDrag() override; + virtual bool EndSdrDrag(bool bCopy) override; + + rtl::Reference mxTag; +}; + +} + +void PathDragMove::createSdrDragEntries() +{ + // call parent + SdrDragMove::createSdrDragEntries(); + + if(maPathPolyPolygon.count()) + { + addSdrDragEntry(std::unique_ptr(new SdrDragEntryPolyPolygon(maPathPolyPolygon))); + } +} + +bool PathDragMove::BeginSdrDrag() +{ + if( mxTag.is() ) + { + SdrPathObj* pPathObj = mxTag->getPathObj(); + if( pPathObj ) + { + DragStat().SetActionRect(pPathObj->GetCurrentBoundRect()); + } + } + Show(); + return true; +} + +bool PathDragMove::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + if( mxTag.is() ) + mxTag->MovePath( DragStat().GetDX(), DragStat().GetDY() ); + return true; +} + +namespace { + +class PathDragResize : public SdrDragResize +{ +private: + basegfx::B2DPolyPolygon maPathPolyPolygon; + +protected: + virtual void createSdrDragEntries() override; + +public: + PathDragResize(SdrDragView& rNewView, + const rtl::Reference & xTag, + const basegfx::B2DPolyPolygon& rPathPolyPolygon) + : SdrDragResize(rNewView), + maPathPolyPolygon(rPathPolyPolygon), + mxTag( xTag ) + {} + + PathDragResize(SdrDragView& rNewView, + const rtl::Reference & xTag) + : SdrDragResize(rNewView), + mxTag( xTag ) + {} + + virtual bool EndSdrDrag(bool bCopy) override; + rtl::Reference mxTag; +}; + +} + +void PathDragResize::createSdrDragEntries() +{ + // call parent + SdrDragResize::createSdrDragEntries(); + + if(maPathPolyPolygon.count()) + { + addSdrDragEntry(std::unique_ptr(new SdrDragEntryPolyPolygon(maPathPolyPolygon))); + } +} + +bool PathDragResize::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + if( mxTag.is() ) + { + SdrPathObj* pPathObj = mxTag->getPathObj(); + if( pPathObj ) + { + const Point aRef( DragStat().GetRef1() ); + basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-aRef.X(), -aRef.Y())); + aTrans.scale(double(aXFact), double(aYFact)); + aTrans.translate(aRef.X(), aRef.Y()); + basegfx::B2DPolyPolygon aDragPoly(pPathObj->GetPathPoly()); + aDragPoly.transform(aTrans); + pPathObj->SetPathPoly( aDragPoly ); + } + } + return true; +} + +namespace { + +class PathDragObjOwn : public SdrDragObjOwn +{ +private: + basegfx::B2DPolyPolygon maPathPolyPolygon; + +protected: + virtual void createSdrDragEntries() override; + +public: + PathDragObjOwn(SdrDragView& rNewView, + const basegfx::B2DPolyPolygon& rPathPolyPolygon) + : SdrDragObjOwn(rNewView), + maPathPolyPolygon(rPathPolyPolygon) + {} + + explicit PathDragObjOwn(SdrDragView& rNewView) + : SdrDragObjOwn(rNewView) + {} + + virtual bool EndSdrDrag(bool bCopy) override; +}; + +} + +void PathDragObjOwn::createSdrDragEntries() +{ + // call parent + SdrDragObjOwn::createSdrDragEntries(); + + if(maPathPolyPolygon.count()) + { + addSdrDragEntry(std::unique_ptr(new SdrDragEntryPolyPolygon(maPathPolyPolygon))); + } +} + +bool PathDragObjOwn::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + + SdrObject* pObj = GetDragObj(); + + if(pObj && pObj->applySpecialDrag(DragStat())) + { + pObj->SetChanged(); + pObj->BroadcastObjectChange(); + return true; + } + else + { + return false; + } +} + +namespace { + +class SdPathHdl : public SmartHdl +{ +public: + SdPathHdl( const SmartTagReference& xTag, SdrPathObj* mpPathObj ); + + virtual void CreateB2dIAObject() override; + virtual bool IsFocusHdl() const override; + +private: + SdrPathObj* mpPathObj; +}; + +} + +SdPathHdl::SdPathHdl( const SmartTagReference& xTag, SdrPathObj* pPathObj ) +: SmartHdl( xTag, pPathObj->GetCurrentBoundRect().TopLeft(), SdrHdlKind::SmartTag ) +, mpPathObj( pPathObj ) +{ +} + +void SdPathHdl::CreateB2dIAObject() +{ + // first throw away old one + GetRidOfIAObject(); + + if(!pHdlList) + return; + + SdrMarkView* pView = pHdlList->GetView(); + + if(!pView || pView->areMarkHandlesHidden()) + return; + + SdrPageView* pPageView = pView->GetSdrPageView(); + + if(!pPageView) + return; + + for(sal_uInt32 b(0); b < pPageView->PageWindowCount(); b++) + { + const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); + + if(rPageWindow.GetPaintWindow().OutputToWindow()) + { + const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); + if (xManager.is() && mpPathObj) + { + const sdr::contact::ViewContact& rVC = mpPathObj->GetViewContact(); + drawinglayer::primitive2d::Primitive2DContainer aSequence; + rVC.getViewIndependentPrimitive2DContainer(aSequence); + std::unique_ptr pNew(new sdr::overlay::OverlayPrimitive2DSequenceObject(std::move(aSequence))); + + // OVERLAYMANAGER + insertNewlyCreatedOverlayObjectForSdrHdl( + std::move(pNew), + rPageWindow.GetObjectContact(), + *xManager); + } + } + } +} + +bool SdPathHdl::IsFocusHdl() const +{ + return false; +} + +MotionPathTag::MotionPathTag( CustomAnimationPane& rPane, ::sd::View& rView, const CustomAnimationEffectPtr& pEffect ) +: SmartTag( rView ) +, mrPane( rPane ) +, mpEffect( pEffect ) +, mxOrigin( pEffect->getTargetShape() ) +, msLastPath( pEffect->getPath() ) +, mbInUpdatePath( false ) +{ + mpPathObj = mpEffect->createSdrPathObjFromPath(rView.getSdrModelFromSdrView()); + mxPolyPoly = mpPathObj->GetPathPoly(); + if (mxOrigin.is()) + maOriginPos = mxOrigin->getPosition(); + + XDash aDash( css::drawing::DashStyle_RECT, 1, 80, 1, 80, 80); + OUString aEmpty( "?" ); + mpPathObj->SetMergedItem( XLineDashItem( aEmpty, aDash ) ); + mpPathObj->SetMergedItem( XLineStyleItem( drawing::LineStyle_DASH ) ); + mpPathObj->SetMergedItem( XLineColorItem(aEmpty, COL_GRAY) ); + mpPathObj->SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) ); + + ::basegfx::B2DPolygon aStartArrow; + aStartArrow.append(::basegfx::B2DPoint(20.0, 0.0)); + aStartArrow.append(::basegfx::B2DPoint(0.0, 0.0)); + aStartArrow.append(::basegfx::B2DPoint(10.0, 30.0)); + aStartArrow.setClosed(true); + mpPathObj->SetMergedItem(XLineStartItem(aEmpty,::basegfx::B2DPolyPolygon(aStartArrow))); + mpPathObj->SetMergedItem(XLineStartWidthItem(400)); + mpPathObj->SetMergedItem(XLineStartCenterItem(true)); + + updatePathAttributes(); + + mpPathObj->SetMergedItem(XLineTransparenceItem(50)); + + mpMark.reset(new SdrMark( mpPathObj, mrView.GetSdrPageView() )); + + mpPathObj->AddListener( *this ); + + Reference< XChangesNotifier > xNotifier( mpEffect->getNode(), UNO_QUERY ); + if( xNotifier.is() ) + { + xNotifier->addChangesListener( this ); + } +} + +MotionPathTag::~MotionPathTag() +{ + DBG_ASSERT( mpPathObj == nullptr, "sd::MotionPathTag::~MotionPathTag(), dispose me first!" ); + Dispose(); +} + +void MotionPathTag::updatePathAttributes() +{ + ::basegfx::B2DPolygon aCandidate; + if( mxPolyPoly.count() ) + { + aCandidate = mxPolyPoly.getB2DPolygon(0); + ::basegfx::utils::checkClosed( aCandidate ); + } + + if( !aCandidate.isClosed() ) + { + ::basegfx::B2DPolygon aEndArrow; + aEndArrow.append(::basegfx::B2DPoint(10.0, 0.0)); + aEndArrow.append(::basegfx::B2DPoint(0.0, 30.0)); + aEndArrow.append(::basegfx::B2DPoint(20.0, 30.0)); + aEndArrow.setClosed(true); + mpPathObj->SetMergedItem(XLineEndItem("?",::basegfx::B2DPolyPolygon(aEndArrow))); + mpPathObj->SetMergedItem(XLineEndWidthItem(400)); + mpPathObj->SetMergedItem(XLineEndCenterItem(true)); + } + else + { + mpPathObj->SetMergedItem(XLineEndItem()); + } +} + +void MotionPathTag::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint ) +{ + if( !(mpPathObj && !mbInUpdatePath && rHint.GetId() == SfxHintId::ThisIsAnSdrHint && mpEffect) ) + return; + + if( mxPolyPoly != mpPathObj->GetPathPoly() ) + { + mbInUpdatePath = true; + mxPolyPoly = mpPathObj->GetPathPoly(); + rtl::Reference< MotionPathTag > xTag( this ); + mrPane.updatePathFromMotionPathTag( xTag ); + msLastPath = mpEffect->getPath(); + updatePathAttributes(); + mbInUpdatePath = false; + } +} + +void MotionPathTag::MovePath( int nDX, int nDY ) +{ + if( mpPathObj ) + { + mpPathObj->Move( Size( nDX, nDY ) ); + mrView.updateHandles(); + } +} + +/** returns true if the MotionPathTag handled the event. */ +bool MotionPathTag::MouseButtonDown( const MouseEvent& rMEvt, SmartHdl& rHdl ) +{ + if( !mpPathObj ) + return false; + + if( !isSelected() ) + { + SmartTagReference xTag( this ); + mrView.getSmartTags().select( xTag ); + selectionChanged(); + return true; + } + else + { + if( rMEvt.IsLeft() && (rMEvt.GetClicks() == 2) ) + { + mrView.GetViewShell()->GetViewFrame()->GetDispatcher()->Execute(SID_BEZIER_EDIT, SfxCallMode::ASYNCHRON); + return true; + } + else if( rMEvt.IsLeft() ) + { + OutputDevice* pOut = mrView.GetViewShell()->GetActiveWindow()->GetOutDev(); + Point aMDPos( pOut->PixelToLogic( rMEvt.GetPosPixel() ) ); + + if( !mrView.IsFrameDragSingles() && mrView.IsInsObjPointMode() && (rHdl.GetObjHdlNum() == SMART_TAG_HDL_NUM) ) + { + // insert a point in edit mode + const bool bNewObj = rMEvt.IsMod1(); + + mrView.BrkAction(); + + Point aPt(aMDPos); // - pMarkedPV->GetOffset()); + + if(bNewObj) + aPt = mrView.GetSnapPos(aPt,mrView.GetSdrPageView()); + + bool bClosed0(mpPathObj->IsClosedObj()); + + sal_uInt32 nInsPointNum = mpPathObj->NbcInsPointOld(aPt, bNewObj); + + if(bClosed0 != mpPathObj->IsClosedObj()) + { + // Obj was closed implicit + // object changed + mpPathObj->SetChanged(); + mpPathObj->BroadcastObjectChange(); + } + + if(0xffffffff != nInsPointNum) + { + mrView.UnmarkAllPoints(); + mrView.updateHandles(); + + bool bRet = mrView.BegDragObj(aMDPos, pOut, mrView.GetHdl(nInsPointNum+1), 0, new PathDragObjOwn( mrView ) ); + + if (bRet) + { + const_cast< SdrDragStat* >( &mrView.GetDragStat() )->SetMinMoved(); + mrView.MovDragObj(aMDPos); + } + } + return true; + } + else + { + SmartHdl* pHdl = &rHdl; + if (!mrView.IsPointMarked(*pHdl) || rMEvt.IsShift()) + { + if (!rMEvt.IsShift()) + { + mrView.UnmarkAllPoints(); + pHdl = dynamic_cast< SmartHdl* >( mrView.PickHandle(aMDPos) ); + } + else + { + if (mrView.IsPointMarked(*pHdl) ) + { + mrView.UnmarkPoint(*pHdl); + pHdl = nullptr; + } + else + { + pHdl = dynamic_cast< SmartHdl* >( mrView.PickHandle(aMDPos) ); + } + } + + if (pHdl) + mrView.MarkPoint(*pHdl); + } + + if( pHdl && !rMEvt.IsRight() ) + { + mrView.BrkAction(); + const sal_uInt16 nDrgLog = static_cast(pOut->PixelToLogic(Size(DRGPIX,0)).Width()); + + rtl::Reference< MotionPathTag > xTag( this ); + SdrDragMethod* pDragMethod; + + // #i95646# add DragPoly as geometry to each local SdrDragMethod to be able + // to create the needed local SdrDragEntry for it in createSdrDragEntries() + const basegfx::B2DPolyPolygon aDragPoly(mpPathObj->GetPathPoly()); + + if( (pHdl->GetKind() == SdrHdlKind::Move) || (pHdl->GetKind() == SdrHdlKind::SmartTag) ) + { + pDragMethod = new PathDragMove( mrView, xTag, aDragPoly ); + pHdl->SetPos( aMDPos ); + } + else if( pHdl->GetKind() == SdrHdlKind::Poly ) + { + pDragMethod = new PathDragObjOwn( mrView, aDragPoly ); + } + else + { + pDragMethod = new PathDragResize( mrView, xTag, aDragPoly ); + } + + mrView.BegDragObj(aMDPos, nullptr, pHdl, nDrgLog, pDragMethod ); + } + return true; + } + } + } + + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool MotionPathTag::KeyInput( const KeyEvent& rKEvt ) +{ + if( !mpPathObj ) + return false; + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch( nCode ) + { + case KEY_DELETE: + return OnDelete(); + + case KEY_DOWN: + case KEY_UP: + case KEY_LEFT: + case KEY_RIGHT: + return OnMove( rKEvt ); + + case KEY_ESCAPE: + { + SmartTagReference xThis( this ); + mrView.getSmartTags().deselect(); + return true; + } + + case KEY_TAB: + return OnTabHandles( rKEvt ); + + case KEY_SPACE: + return OnMarkHandle( rKEvt ); + + default: + break; + } + return false; +} + +bool MotionPathTag::OnDelete() +{ + mrPane.remove( mpEffect ); + return true; +} + +bool MotionPathTag::OnTabHandles( const KeyEvent& rKEvt ) +{ + if(rKEvt.GetKeyCode().IsMod1() || rKEvt.GetKeyCode().IsMod2()) + { + const SdrHdlList& rHdlList = mrView.GetHdlList(); + bool bForward(!rKEvt.GetKeyCode().IsShift()); + + const_cast(rHdlList).TravelFocusHdl(bForward); + + // guarantee visibility of focused handle + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); + if( pWindow ) + { + Point aHdlPosition(pHdl->GetPos()); + ::tools::Rectangle aVisRect(aHdlPosition - Point(100, 100), Size(200, 200)); + mrView.MakeVisible(aVisRect, *pWindow); + } + } + + return true; + } + + return false; +} + +bool MotionPathTag::OnMarkHandle( const KeyEvent& rKEvt ) +{ + const SdrHdlList& rHdlList = mrView.GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl && pHdl->GetKind() == SdrHdlKind::Poly ) + { + // rescue ID of point with focus + sal_uInt32 nPol(pHdl->GetPolyNum()); + sal_uInt32 nPnt(pHdl->GetPointNum()); + + if(mrView.IsPointMarked(*pHdl)) + { + if(rKEvt.GetKeyCode().IsShift()) + { + mrView.UnmarkPoint(*pHdl); + } + } + else + { + if(!rKEvt.GetKeyCode().IsShift()) + { + mrView.UnmarkAllPoints(); + } + mrView.MarkPoint(*pHdl); + } + + if(nullptr == rHdlList.GetFocusHdl()) + { + // restore point with focus + SdrHdl* pNewOne = nullptr; + + for(size_t a = 0; !pNewOne && a < rHdlList.GetHdlCount(); ++a) + { + SdrHdl* pAct = rHdlList.GetHdl(a); + + if(pAct && pAct->GetKind() == SdrHdlKind::Poly && pAct->GetPolyNum() == nPol && pAct->GetPointNum() == nPnt) + pNewOne = pAct; + } + + if(pNewOne) + const_cast(rHdlList).SetFocusHdl(pNewOne); + } + } + + return true; +} + +bool MotionPathTag::OnMove( const KeyEvent& rKEvt ) +{ + ::tools::Long nX = 0; + ::tools::Long nY = 0; + + switch( rKEvt.GetKeyCode().GetCode() ) + { + case KEY_UP: nY = -1; break; + case KEY_DOWN: nY = 1; break; + case KEY_LEFT: nX = -1; break; + case KEY_RIGHT: nX = 1; break; + default: break; + } + + if(rKEvt.GetKeyCode().IsMod2()) + { + OutputDevice* pOut = mrView.GetViewShell()->GetActiveWindow()->GetOutDev(); + Size aLogicSizeOnePixel = pOut ? pOut->PixelToLogic(Size(1,1)) : Size(100, 100); + nX *= aLogicSizeOnePixel.Width(); + nY *= aLogicSizeOnePixel.Height(); + } + else + { + // old, fixed move distance + nX *= 100; + nY *= 100; + } + + if( nX || nY ) + { + // in point edit mode move the handle with the focus + const SdrHdlList& rHdlList = mrView.GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + // now move the Handle (nX, nY) + Point aStartPoint(pHdl->GetPos()); + Point aEndPoint(pHdl->GetPos() + Point(nX, nY)); + + // start dragging + rtl::Reference< MotionPathTag > xTag( this ); + SdrDragMethod* pDragMethod = nullptr; + if( (pHdl->GetKind() == SdrHdlKind::Move) || (pHdl->GetKind() == SdrHdlKind::SmartTag) ) + { + pDragMethod = new PathDragMove( mrView, xTag ); + } + else if( pHdl->GetKind() == SdrHdlKind::Poly ) + { + pDragMethod = new PathDragObjOwn( mrView ); + } + else if( pHdl->GetKind() != SdrHdlKind::BezierWeight ) + { + pDragMethod = new PathDragResize( mrView, xTag ); + } + mrView.BegDragObj(aStartPoint, nullptr, pHdl, 0, pDragMethod); + + if(mrView.IsDragObj()) + { + bool bWasNoSnap = mrView.GetDragStat().IsNoSnap(); + bool bWasSnapEnabled = mrView.IsSnapEnabled(); + + // switch snapping off + if(!bWasNoSnap) + const_cast(mrView.GetDragStat()).SetNoSnap(); + if(bWasSnapEnabled) + mrView.SetSnapEnabled(false); + + mrView.MovAction(aEndPoint); + mrView.EndDragObj(); + + // restore snap + if(!bWasNoSnap) + const_cast(mrView.GetDragStat()).SetNoSnap(bWasNoSnap); + if(bWasSnapEnabled) + mrView.SetSnapEnabled(bWasSnapEnabled); + } + } + else + { + // move the path + MovePath( nX, nY ); + } + } + + return true; +} + +sal_Int32 MotionPathTag::GetMarkablePointCount() const +{ + if( mpPathObj && isSelected() ) + { + return mpPathObj->GetPointCount(); + } + else + { + return 0; + } +} + +sal_Int32 MotionPathTag::GetMarkedPointCount() const +{ + if( mpMark ) + { + const SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + return rPts.size(); + } + else + { + return 0; + } +} + +bool MotionPathTag::MarkPoint(SdrHdl& rHdl, bool bUnmark ) +{ + bool bRet=false; + if( mpPathObj && mrView.IsPointMarkable( rHdl ) && (rHdl.GetKind() != SdrHdlKind::SmartTag) ) + { + SmartHdl* pSmartHdl = dynamic_cast< SmartHdl* >( &rHdl ); + if( pSmartHdl && pSmartHdl->getTag().get() == this ) + { + if (mrView.MarkPointHelper(&rHdl,mpMark.get(),bUnmark)) + { + mrView.MarkListHasChanged(); + bRet=true; + } + } + } + return bRet; +} + +bool MotionPathTag::MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark ) +{ + bool bChgd=false; + + if( mpPathObj && isSelected() ) + { + size_t nHdlNum = mrView.GetHdlList().GetHdlCount(); + if ( nHdlNum <= 1 ) + return false; + + while( --nHdlNum > 0 ) + { + SmartHdl* pHdl = dynamic_cast< SmartHdl* >( mrView.GetHdl( nHdlNum ) ); + + if( pHdl && (pHdl->getTag().get() == this) && mrView.IsPointMarkable(*pHdl) && pHdl->IsSelected() == bUnmark) + { + Point aPos(pHdl->GetPos()); + if( pRect==nullptr || pRect->Contains(aPos)) + { + if( mrView.MarkPointHelper(pHdl,mpMark.get(),bUnmark) ) + bChgd=true; + } + } + } + + if(bChgd) + mrView.MarkListHasChanged(); + } + + return bChgd; +} + +bool MotionPathTag::getContext( SdrViewContext& rContext ) +{ + if( mpPathObj && isSelected() && !mrView.IsFrameDragSingles() ) + { + rContext = SdrViewContext::PointEdit; + return true; + } + else + { + return false; + } +} + +void MotionPathTag::CheckPossibilities() +{ + if( !(mpPathObj && isSelected()) ) + return; + + mrView.SetMoveAllowed( true ); + mrView.SetMoveProtected( false ); + mrView.SetResizeFreeAllowed( true ); + mrView.SetResizePropAllowed( true ); + mrView.SetResizeProtected( false ); + + if( !mrView.IsFrameDragSingles() ) + { + bool b1stSmooth(true); + bool b1stSegm(true); + bool bCurve(false); + bool bSmoothFuz(false); + bool bSegmFuz(false); + basegfx::B2VectorContinuity eSmooth = basegfx::B2VectorContinuity::NONE; + + mrView.CheckPolyPossibilitiesHelper( mpMark.get(), b1stSmooth, b1stSegm, bCurve, bSmoothFuz, bSegmFuz, eSmooth ); + } +} + +void MotionPathTag::addCustomHandles( SdrHdlList& rHandlerList ) +{ + if( !mpPathObj ) + return; + + css::awt::Point aPos; + if (mxOrigin.is()) + aPos = mxOrigin->getPosition(); + if( (aPos.X != maOriginPos.X) || (aPos.Y != maOriginPos.Y) ) + { + const basegfx::B2DHomMatrix aTransform(basegfx::utils::createTranslateB2DHomMatrix( + aPos.X - maOriginPos.X, aPos.Y - maOriginPos.Y)); + mxPolyPoly.transform( aTransform ); + mpPathObj->SetPathPoly( mxPolyPoly ); + maOriginPos = aPos; + } + + SmartTagReference xThis( this ); + std::unique_ptr pHdl(new SdPathHdl( xThis, mpPathObj )); + pHdl->SetObjHdlNum( SMART_TAG_HDL_NUM ); + pHdl->SetPageView( mrView.GetSdrPageView() ); + pHdl->SetObj(mpPathObj); + rHandlerList.AddHdl( std::move(pHdl) ); + + if( !isSelected() ) + return; + + mrView.GetSdrPageView()->SetHasMarkedObj(true); + + if( !mrView.IsFrameDragSingles() ) + { + SdrHdlList aTemp( rHandlerList.GetView() ); + mpPathObj->AddToHdlList( aTemp ); + const SdrUShortCont& rMrkPnts = mpMark->GetMarkedPoints(); + + for( size_t nHandle = 0; nHandle < aTemp.GetHdlCount(); ++nHandle ) + { + SdrHdl* pTempHdl = aTemp.GetHdl( nHandle ); + + SmartHdl* pSmartHdl = new SmartHdl( xThis, mpPathObj, pTempHdl->GetPos(), pTempHdl->GetKind() ); + pSmartHdl->SetObjHdlNum( static_cast(nHandle) ); + pSmartHdl->SetPolyNum( pTempHdl->GetPolyNum() ); + pSmartHdl->SetPointNum( pTempHdl->GetPointNum() ); + pSmartHdl->SetPlusHdl( pTempHdl->IsPlusHdl() ); + pSmartHdl->SetSourceHdlNum( pTempHdl->GetSourceHdlNum() ); + pSmartHdl->SetPageView( mrView.GetSdrPageView() ); + + rHandlerList.AddHdl( std::unique_ptr(pSmartHdl) ); + + const bool bSelected = rMrkPnts.find( sal_uInt16(nHandle) ) != rMrkPnts.end(); + pSmartHdl->SetSelected(bSelected); + + if( mrView.IsPlusHandlesAlwaysVisible() || bSelected ) + { + SdrHdlList plusList(nullptr); + mpPathObj->AddToPlusHdlList(plusList, *pSmartHdl); + sal_uInt32 nPlusHdlCnt=plusList.GetHdlCount(); + for (sal_uInt32 nPlusNum=0; nPlusNumSetObj(mpPathObj); + pPlusHdl->SetPageView(mrView.GetSdrPageView()); + pPlusHdl->SetPlusHdl(true); + } + plusList.MoveTo(rHandlerList); + } + } + } + else + { + ::tools::Rectangle aRect(mpPathObj->GetCurrentBoundRect()); + + if(!aRect.IsEmpty()) + { + size_t nCount = rHandlerList.GetHdlCount(); + + bool bWdt0=aRect.Left()==aRect.Right(); + bool bHgt0=aRect.Top()==aRect.Bottom(); + if (bWdt0 && bHgt0) + { + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.TopLeft(),SdrHdlKind::UpperLeft)); + } + else if (bWdt0 || bHgt0) + { + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.TopLeft() ,SdrHdlKind::UpperLeft)); + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.BottomRight(),SdrHdlKind::LowerRight)); + } + else // !bWdt0 && !bHgt0 + { + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.TopLeft() ,SdrHdlKind::UpperLeft)); + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.TopCenter() ,SdrHdlKind::Upper)); + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.TopRight() ,SdrHdlKind::UpperRight)); + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.LeftCenter() ,SdrHdlKind::Left )); + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.RightCenter() ,SdrHdlKind::Right)); + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.BottomLeft() ,SdrHdlKind::LowerLeft)); + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.BottomCenter(),SdrHdlKind::Lower)); + rHandlerList.AddHdl(std::make_unique( xThis, mpPathObj, aRect.BottomRight() ,SdrHdlKind::LowerRight)); + } + + while( nCount < rHandlerList.GetHdlCount() ) + { + rHandlerList.GetHdl(nCount++)->SetPageView( mrView.GetSdrPageView() ); + } + } + } +} + +void MotionPathTag::disposing() +{ + Reference< XChangesNotifier > xNotifier( mpEffect->getNode(), UNO_QUERY ); + if( xNotifier.is() ) + { + xNotifier->removeChangesListener( this ); + } + + if( mpPathObj ) + { + SdrObject* pTemp(mpPathObj); + mpPathObj = nullptr; + mrView.updateHandles(); + + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject::Free(pTemp); + } + + mpMark.reset(); + + SmartTag::disposing(); +} + +void MotionPathTag::deselect() +{ + SmartTag::deselect(); + + if( mpMark ) + { + SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + rPts.clear(); + } + + selectionChanged(); +} + +void MotionPathTag::selectionChanged() +{ + if( mrView.GetViewShell() && mrView.GetViewShell()->GetViewFrame() ) + { + SfxBindings& rBindings = mrView.GetViewShell()->GetViewFrame()->GetBindings(); + rBindings.InvalidateAll(true); + } +} + +// IPolyPolygonEditorController + +void MotionPathTag::DeleteMarkedPoints() +{ + if( !(mpPathObj && IsDeleteMarkedPointsPossible()) ) + return; + + mrView.BrkAction(); + + SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + PolyPolygonEditor aEditor( mpPathObj->GetPathPoly()); + if (aEditor.DeletePoints(rPts)) + { + if( aEditor.GetPolyPolygon().count() ) + { + mpPathObj->SetPathPoly( aEditor.GetPolyPolygon() ); + } + + mrView.UnmarkAllPoints(); + mrView.MarkListHasChanged(); + mrView.updateHandles(); + } +} + +bool MotionPathTag::IsDeleteMarkedPointsPossible() const +{ + return mpPathObj && isSelected() && (GetMarkedPointCount() != 0); +} + +void MotionPathTag::RipUpAtMarkedPoints() +{ + // not supported for motion path +} + +bool MotionPathTag::IsRipUpAtMarkedPointsPossible() const +{ + // not supported for motion path + return false; +} + +bool MotionPathTag::IsSetMarkedSegmentsKindPossible() const +{ + if( mpPathObj ) + return mrView.IsSetMarkedSegmentsKindPossible(); + else + return false; +} + +SdrPathSegmentKind MotionPathTag::GetMarkedSegmentsKind() const +{ + if( mpPathObj ) + return mrView.GetMarkedSegmentsKind(); + else + return SdrPathSegmentKind::Line; +} + +void MotionPathTag::SetMarkedSegmentsKind(SdrPathSegmentKind eKind) +{ + if(mpPathObj && isSelected() && (GetMarkedPointCount() != 0)) + { + SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + PolyPolygonEditor aEditor( mpPathObj->GetPathPoly() ); + if (aEditor.SetSegmentsKind(eKind, rPts)) + { + mpPathObj->SetPathPoly(aEditor.GetPolyPolygon()); + mrView.MarkListHasChanged(); + mrView.updateHandles(); + } + } +} + +bool MotionPathTag::IsSetMarkedPointsSmoothPossible() const +{ + if( mpPathObj ) + return mrView.IsSetMarkedPointsSmoothPossible(); + else + return false; +} + +SdrPathSmoothKind MotionPathTag::GetMarkedPointsSmooth() const +{ + if( mpPathObj ) + return mrView.GetMarkedPointsSmooth(); + else + return SdrPathSmoothKind::Angular; +} + +void MotionPathTag::SetMarkedPointsSmooth(SdrPathSmoothKind eKind) +{ + basegfx::B2VectorContinuity eFlags; + + if(SdrPathSmoothKind::Angular == eKind) + { + eFlags = basegfx::B2VectorContinuity::NONE; + } + else if(SdrPathSmoothKind::Asymmetric == eKind) + { + eFlags = basegfx::B2VectorContinuity::C1; + } + else if(SdrPathSmoothKind::Symmetric == eKind) + { + eFlags = basegfx::B2VectorContinuity::C2; + } + else + { + return; + } + + if(mpPathObj && mpMark && isSelected() && (GetMarkedPointCount() != 0)) + { + SdrUShortCont& rPts = mpMark->GetMarkedPoints(); + PolyPolygonEditor aEditor( mpPathObj->GetPathPoly()); + if (aEditor.SetPointsSmooth(eFlags, rPts)) + { + mpPathObj->SetPathPoly(aEditor.GetPolyPolygon()); + mrView.MarkListHasChanged(); + mrView.updateHandles(); + } + } +} + +bool MotionPathTag::IsOpenCloseMarkedObjectsPossible() const +{ + // not supported for motion path + return false; +} + +SdrObjClosedKind MotionPathTag::GetMarkedObjectsClosedState() const +{ + // not supported for motion path + return SdrObjClosedKind::Open; +} + +// XChangesListener +void SAL_CALL MotionPathTag::changesOccurred( const ChangesEvent& /*Event*/ ) +{ + if( mpPathObj && !mbInUpdatePath && (mpEffect->getPath() != msLastPath) ) + { + mbInUpdatePath =true; + msLastPath = mpEffect->getPath(); + mpEffect->updateSdrPathObjFromPath( *mpPathObj ); + mbInUpdatePath = false; + updatePathAttributes(); + mrView.updateHandles(); + } +} + +void SAL_CALL MotionPathTag::disposing( const EventObject& /*Source*/ ) +{ + if( mpPathObj ) + Dispose(); +} + +Any SAL_CALL MotionPathTag::queryInterface( const css::uno::Type& aType ) +{ + if( aType == cppu::UnoType::get() ) + return Any( Reference< XChangesListener >( this ) ); + if( aType == cppu::UnoType::get() ) + return Any( Reference< XEventListener >( this ) ); + if( aType == cppu::UnoType::get() ) + return Any( Reference< XInterface >( this ) ); + + return Any(); +} + +void SAL_CALL MotionPathTag::acquire() noexcept +{ + SimpleReferenceComponent::acquire(); +} + +void SAL_CALL MotionPathTag::release( ) noexcept +{ + SimpleReferenceComponent::release(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/animations/motionpathtag.hxx b/sd/source/ui/animations/motionpathtag.hxx new file mode 100644 index 000000000..715ce4268 --- /dev/null +++ b/sd/source/ui/animations/motionpathtag.hxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace com::sun::star::drawing { class XShape; } +class SdrPathObj; + +namespace sd { + +class View; +class CustomAnimationPane; + +/// Base class for all functions. +class MotionPathTag final : public SmartTag, public IPolyPolygonEditorController, public SfxListener, public css::util::XChangesListener +{ +public: + MotionPathTag( CustomAnimationPane& rPane, ::sd::View& rView, const CustomAnimationEffectPtr& pEffect ); + virtual ~MotionPathTag() override; + + SdrPathObj* getPathObj() const { return mpPathObj; } + + /// @return true if the SmartTag handled the event. + virtual bool MouseButtonDown( const MouseEvent&, SmartHdl& ) override; + + /// @return true if the SmartTag consumes this event. + virtual bool KeyInput( const KeyEvent& rKEvt ) override; + + // callbacks from sdr view + virtual sal_Int32 GetMarkablePointCount() const override; + virtual sal_Int32 GetMarkedPointCount() const override; + virtual bool MarkPoint(SdrHdl& rHdl, bool bUnmark) override; + virtual void CheckPossibilities() override; + virtual bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) override; + + const CustomAnimationEffectPtr& getEffect() const { return mpEffect; } + + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + // IPolyPolygonEditorController + virtual void DeleteMarkedPoints() override; + virtual bool IsDeleteMarkedPointsPossible() const override; + + virtual void RipUpAtMarkedPoints() override; + virtual bool IsRipUpAtMarkedPointsPossible() const override; + + virtual bool IsSetMarkedSegmentsKindPossible() const override; + virtual SdrPathSegmentKind GetMarkedSegmentsKind() const override; + virtual void SetMarkedSegmentsKind(SdrPathSegmentKind eKind) override; + + virtual bool IsSetMarkedPointsSmoothPossible() const override; + virtual SdrPathSmoothKind GetMarkedPointsSmooth() const override; + virtual void SetMarkedPointsSmooth(SdrPathSmoothKind eKind) override; + + virtual bool IsOpenCloseMarkedObjectsPossible() const override; + virtual SdrObjClosedKind GetMarkedObjectsClosedState() const override; + + void MovePath( int nDX, int nDY ); + bool OnDelete(); + bool OnTabHandles( const KeyEvent& rKEvt ); + bool OnMarkHandle( const KeyEvent& rKEvt ); + bool OnMove( const KeyEvent& rKEvt ); + + // XChangesListener + virtual void SAL_CALL changesOccurred( const css::util::ChangesEvent& Event ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire( ) noexcept override; + virtual void SAL_CALL release( ) noexcept override; + +private: + virtual void addCustomHandles( SdrHdlList& rHandlerList ) override; + virtual bool getContext( SdrViewContext& rContext ) override; + virtual void disposing() override; + virtual void deselect() override; + + void updatePathAttributes(); + void selectionChanged(); + + CustomAnimationPane& mrPane; + CustomAnimationEffectPtr mpEffect; + ::basegfx::B2DPolyPolygon mxPolyPoly; + css::uno::Reference< css::drawing::XShape > mxOrigin; + SdrPathObj* mpPathObj; + css::awt::Point maOriginPos; + std::unique_ptr mpMark; + OUString msLastPath; + bool mbInUpdatePath; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationmanager.cxx b/sd/source/ui/annotations/annotationmanager.cxx new file mode 100644 index 000000000..ab9fe0c1a --- /dev/null +++ b/sd/source/ui/annotations/annotationmanager.cxx @@ -0,0 +1,1220 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include "annotationmanagerimpl.hxx" +#include "annotationwindow.hxx" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::geometry; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::office; + +namespace sd { + +SfxItemPool* GetAnnotationPool() +{ + static rtl::Reference s_pAnnotationPool; + if( !s_pAnnotationPool ) + { + s_pAnnotationPool = EditEngine::CreatePool(); + s_pAnnotationPool->SetPoolDefaultItem(SvxFontHeightItem(423,100,EE_CHAR_FONTHEIGHT)); + + vcl::Font aAppFont( Application::GetSettings().GetStyleSettings().GetAppFont() ); + s_pAnnotationPool->SetPoolDefaultItem(SvxFontItem(aAppFont.GetFamilyType(),aAppFont.GetFamilyName(),"",PITCH_DONTKNOW,RTL_TEXTENCODING_DONTKNOW,EE_CHAR_FONTINFO)); + } + + return s_pAnnotationPool.get(); +} + +static SfxBindings* getBindings( ViewShellBase const & rBase ) +{ + if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() ) + return &rBase.GetMainViewShell()->GetViewFrame()->GetBindings(); + + return nullptr; +} + +static SfxDispatcher* getDispatcher( ViewShellBase const & rBase ) +{ + if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() ) + return rBase.GetMainViewShell()->GetViewFrame()->GetDispatcher(); + + return nullptr; +} + +css::util::DateTime getCurrentDateTime() +{ + DateTime aCurrentDate( DateTime::SYSTEM ); + return css::util::DateTime( 0, aCurrentDate.GetSec(), + aCurrentDate.GetMin(), aCurrentDate.GetHour(), + aCurrentDate.GetDay(), aCurrentDate.GetMonth(), + aCurrentDate.GetYear(), false ); +} + +OUString getAnnotationDateTimeString( const Reference< XAnnotation >& xAnnotation ) +{ + OUString sRet; + if( xAnnotation.is() ) + { + const SvtSysLocale aSysLocale; + const LocaleDataWrapper& rLocalData = aSysLocale.GetLocaleData(); + + css::util::DateTime aDateTime( xAnnotation->getDateTime() ); + + Date aSysDate( Date::SYSTEM ); + Date aDate( aDateTime.Day, aDateTime.Month, aDateTime.Year ); + if (aDate==aSysDate) + sRet = SdResId(STR_ANNOTATION_TODAY); + else if (aDate == (aSysDate-1)) + sRet = SdResId(STR_ANNOTATION_YESTERDAY); + else if (aDate.IsValidAndGregorian() ) + sRet = rLocalData.getDate(aDate); + + ::tools::Time aTime( aDateTime ); + if(aTime.GetTime() != 0) + sRet += " " + rLocalData.getTime( aTime,false ); + } + return sRet; +} + +AnnotationManagerImpl::AnnotationManagerImpl( ViewShellBase& rViewShellBase ) +: mrBase( rViewShellBase ) +, mpDoc( rViewShellBase.GetDocument() ) +, mbShowAnnotations( true ) +, mnUpdateTagsEvent( nullptr ) +{ + SdOptions* pOptions = SD_MOD()->GetSdOptions(mpDoc->GetDocumentType()); + if( pOptions ) + mbShowAnnotations = pOptions->IsShowComments(); +} + +void AnnotationManagerImpl::init() +{ + // get current controller and initialize listeners + try + { + addListener(); + mxView.set(mrBase.GetController(), UNO_QUERY); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::AnnotationManagerImpl()" ); + } + + try + { + Reference xModel (mrBase.GetDocShell()->GetModel(), UNO_QUERY_THROW ); + Reference xListener( this ); + xModel->addEventListener( xListener ); + } + catch( Exception& ) + { + } +} + +// WeakComponentImplHelper +void AnnotationManagerImpl::disposing (std::unique_lock&) +{ + try + { + Reference xModel (mrBase.GetDocShell()->GetModel(), UNO_QUERY_THROW ); + Reference xListener( this ); + xModel->removeEventListener( xListener ); + } + catch( Exception& ) + { + } + + removeListener(); + DisposeTags(); + + if( mnUpdateTagsEvent ) + { + Application::RemoveUserEvent( mnUpdateTagsEvent ); + mnUpdateTagsEvent = nullptr; + } + + mxView.clear(); + mxCurrentPage.clear(); +} + +// XEventListener +void SAL_CALL AnnotationManagerImpl::notifyEvent( const css::document::EventObject& aEvent ) +{ + if( !(aEvent.EventName == "OnAnnotationInserted" || aEvent.EventName == "OnAnnotationRemoved" || aEvent.EventName == "OnAnnotationChanged") ) + return; + + // AnnotationInsertion and modification is not handled here because when + // a new annotation is inserted, it consists of OnAnnotationInserted + // followed by a chain of OnAnnotationChanged (called for setting each + // of the annotation attributes - author, text etc.). This is not what a + // LOK client wants. So only handle removal here as annotation removal + // consists of only one event - 'OnAnnotationRemoved' + if ( aEvent.EventName == "OnAnnotationRemoved" ) + { + Reference< XAnnotation > xAnnotation( aEvent.Source, uno::UNO_QUERY ); + if ( xAnnotation.is() ) + { + LOKCommentNotify(CommentNotificationType::Remove, &mrBase, xAnnotation); + } + } + + UpdateTags(); +} + +void SAL_CALL AnnotationManagerImpl::disposing( const css::lang::EventObject& /*Source*/ ) +{ +} + +Reference AnnotationManagerImpl::GetAnnotationById(sal_uInt32 nAnnotationId) +{ + SdPage* pPage = nullptr; + do + { + pPage = GetNextPage(pPage, true); + if( pPage && !pPage->getAnnotations().empty() ) + { + AnnotationVector aAnnotations(pPage->getAnnotations()); + auto iter = std::find_if(aAnnotations.begin(), aAnnotations.end(), + [nAnnotationId](const Reference& xAnnotation) { + return sd::getAnnotationId(xAnnotation) == nAnnotationId; + }); + if (iter != aAnnotations.end()) + return *iter; + } + } while( pPage ); + + Reference xAnnotationEmpty; + return xAnnotationEmpty; +} + +void AnnotationManagerImpl::ShowAnnotations( bool bShow ) +{ + // enforce show annotations if a new annotation is inserted + if( mbShowAnnotations != bShow ) + { + mbShowAnnotations = bShow; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(mpDoc->GetDocumentType()); + if( pOptions ) + pOptions->SetShowComments( mbShowAnnotations ); + + UpdateTags(); + } +} + +void AnnotationManagerImpl::ExecuteAnnotation(SfxRequest const & rReq ) +{ + switch( rReq.GetSlot() ) + { + case SID_INSERT_POSTIT: + ExecuteInsertAnnotation( rReq ); + break; + case SID_DELETE_POSTIT: + case SID_DELETEALL_POSTIT: + case SID_DELETEALLBYAUTHOR_POSTIT: + ExecuteDeleteAnnotation( rReq ); + break; + case SID_EDIT_POSTIT: + ExecuteEditAnnotation( rReq ); + break; + case SID_PREVIOUS_POSTIT: + case SID_NEXT_POSTIT: + SelectNextAnnotation( rReq.GetSlot() == SID_NEXT_POSTIT ); + break; + case SID_REPLYTO_POSTIT: + ExecuteReplyToAnnotation( rReq ); + break; + case SID_TOGGLE_NOTES: + ShowAnnotations( !mbShowAnnotations ); + break; + } +} + +void AnnotationManagerImpl::ExecuteInsertAnnotation(SfxRequest const & rReq) +{ + if (!comphelper::LibreOfficeKit::isActive() || comphelper::LibreOfficeKit::isTiledAnnotations()) + ShowAnnotations(true); + + const SfxItemSet* pArgs = rReq.GetArgs(); + OUString sText; + if (pArgs) + { + if (const SfxStringItem* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_TEXT)) + { + sText = pPoolItem->GetValue(); + } + } + + InsertAnnotation(sText); +} + +void AnnotationManagerImpl::ExecuteDeleteAnnotation(SfxRequest const & rReq) +{ + ShowAnnotations( true ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + switch( rReq.GetSlot() ) + { + case SID_DELETEALL_POSTIT: + DeleteAllAnnotations(); + break; + case SID_DELETEALLBYAUTHOR_POSTIT: + if( pArgs ) + { + const SfxPoolItem* pPoolItem = nullptr; + if( SfxItemState::SET == pArgs->GetItemState( SID_DELETEALLBYAUTHOR_POSTIT, true, &pPoolItem ) ) + { + OUString sAuthor( static_cast( pPoolItem )->GetValue() ); + DeleteAnnotationsByAuthor( sAuthor ); + } + } + break; + case SID_DELETE_POSTIT: + { + Reference< XAnnotation > xAnnotation; + sal_uInt32 nId = 0; + if( pArgs ) + { + const SfxPoolItem* pPoolItem = nullptr; + if( SfxItemState::SET == pArgs->GetItemState( SID_DELETE_POSTIT, true, &pPoolItem ) ) + static_cast(pPoolItem)->GetValue() >>= xAnnotation; + if( SfxItemState::SET == pArgs->GetItemState( SID_ATTR_POSTIT_ID, true, &pPoolItem ) ) + nId = static_cast(pPoolItem)->GetValue().toUInt32(); + } + + if (nId != 0) + xAnnotation = GetAnnotationById(nId); + else if( !xAnnotation.is() ) + GetSelectedAnnotation( xAnnotation ); + + DeleteAnnotation( xAnnotation ); + } + break; + } + + UpdateTags(); +} + +void AnnotationManagerImpl::ExecuteEditAnnotation(SfxRequest const & rReq) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + Reference< XAnnotation > xAnnotation; + OUString sText; + sal_Int32 nPositionX = -1; + sal_Int32 nPositionY = -1; + + if (!pArgs) + return; + + if (mpDoc->IsUndoEnabled()) + mpDoc->BegUndo(SdResId(STR_ANNOTATION_UNDO_EDIT)); + + if (const SvxPostItIdItem* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_ID)) + { + sal_uInt32 nId = pPoolItem->GetValue().toUInt32(); + xAnnotation = GetAnnotationById(nId); + } + if (const SfxStringItem* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_TEXT)) + sText = pPoolItem->GetValue(); + + if (const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_POSITION_X)) + nPositionX = pPoolItem->GetValue(); + + if (const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet(SID_ATTR_POSTIT_POSITION_Y)) + nPositionY = pPoolItem->GetValue(); + + if (xAnnotation.is()) + { + CreateChangeUndo(xAnnotation); + + if (nPositionX >= 0 && nPositionY >= 0) + { + double fX = convertTwipToMm100(nPositionX) / 100.0; + double fY = convertTwipToMm100(nPositionY) / 100.0; + xAnnotation->setPosition({fX, fY}); + } + + if (!sText.isEmpty()) + { + // TODO: Not allow other authors to change others' comments ? + Reference xText(xAnnotation->getTextRange()); + xText->setString(sText); + } + + LOKCommentNotifyAll(CommentNotificationType::Modify, xAnnotation); + } + + if (mpDoc->IsUndoEnabled()) + mpDoc->EndUndo(); + + UpdateTags(true); +} + +void AnnotationManagerImpl::InsertAnnotation(const OUString& rText) +{ + SdPage* pPage = GetCurrentPage(); + if( !pPage ) + return; + + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_INSERT ) ); + + // find free space for new annotation + int y = 0, x = 0; + + AnnotationVector aAnnotations( pPage->getAnnotations() ); + if( !aAnnotations.empty() ) + { + const int page_width = pPage->GetSize().Width(); + const int width = 1000; + const int height = 800; + ::tools::Rectangle aTagRect; + + while( true ) + { + ::tools::Rectangle aNewRect( x, y, x + width - 1, y + height - 1 ); + bool bFree = true; + + for( const auto& rxAnnotation : aAnnotations ) + { + RealPoint2D aPoint( rxAnnotation->getPosition() ); + aTagRect.SetLeft( sal::static_int_cast< ::tools::Long >( aPoint.X * 100.0 ) ); + aTagRect.SetTop( sal::static_int_cast< ::tools::Long >( aPoint.Y * 100.0 ) ); + aTagRect.SetRight( aTagRect.Left() + width - 1 ); + aTagRect.SetBottom( aTagRect.Top() + height - 1 ); + + if( aNewRect.Overlaps( aTagRect ) ) + { + bFree = false; + break; + } + } + + if( !bFree ) + { + x += width; + if( x > page_width ) + { + x = 0; + y += height; + } + } + else + { + break; + } + } + } + + Reference< XAnnotation > xAnnotation; + pPage->createAnnotation( xAnnotation ); + + OUString sAuthor; + if (comphelper::LibreOfficeKit::isActive()) + sAuthor = mrBase.GetMainViewShell()->GetView()->GetAuthor(); + else + { + SvtUserOptions aUserOptions; + sAuthor = aUserOptions.GetFullName(); + xAnnotation->setInitials( aUserOptions.GetID() ); + } + + if (!rText.isEmpty()) + { + Reference xText(xAnnotation->getTextRange()); + xText->setString(rText); + } + + // set current author to new annotation + xAnnotation->setAuthor( sAuthor ); + // set current time to new annotation + xAnnotation->setDateTime( getCurrentDateTime() ); + + // set position + RealPoint2D aPos( static_cast(x) / 100.0, static_cast(y) / 100.0 ); + xAnnotation->setPosition( aPos ); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); + + // Tell our LOK clients about new comment added + LOKCommentNotifyAll(CommentNotificationType::Add, xAnnotation); + + UpdateTags(true); + SelectAnnotation( xAnnotation, true ); +} + +void AnnotationManagerImpl::ExecuteReplyToAnnotation( SfxRequest const & rReq ) +{ + Reference< XAnnotation > xAnnotation; + const SfxItemSet* pArgs = rReq.GetArgs(); + OUString sReplyText; + if( pArgs ) + { + const SfxPoolItem* pPoolItem = nullptr; + if( SfxItemState::SET == pArgs->GetItemState( SID_ATTR_POSTIT_ID, true, &pPoolItem ) ) + { + sal_uInt32 nReplyId = 0; // Id of the comment to reply to + nReplyId = static_cast(pPoolItem)->GetValue().toUInt32(); + xAnnotation = GetAnnotationById(nReplyId); + } + else if( SfxItemState::SET == pArgs->GetItemState( rReq.GetSlot(), true, &pPoolItem ) ) + static_cast( pPoolItem )->GetValue() >>= xAnnotation; + + if( SfxItemState::SET == pArgs->GetItemState( SID_ATTR_POSTIT_TEXT, true, &pPoolItem ) ) + sReplyText = static_cast( pPoolItem )->GetValue(); + } + + TextApiObject* pTextApi = getTextApiObject( xAnnotation ); + if( !pTextApi ) + return; + + ::Outliner aOutliner( GetAnnotationPool(),OutlinerMode::TextObject ); + + SdDrawDocument::SetCalcFieldValueHdl( &aOutliner ); + aOutliner.SetUpdateLayout( true ); + + OUString aStr(SdResId(STR_ANNOTATION_REPLY)); + OUString sAuthor( xAnnotation->getAuthor() ); + if( sAuthor.isEmpty() ) + sAuthor = SdResId( STR_ANNOTATION_NOAUTHOR ); + + aStr = aStr.replaceFirst("%1", sAuthor) + + " (" + getAnnotationDateTimeString( xAnnotation ) + "): \""; + + OUString sQuote( pTextApi->GetText() ); + + if( sQuote.isEmpty() ) + sQuote = "..."; + aStr += sQuote + "\"\n"; + + for( sal_Int32 nIdx = 0; nIdx >= 0; ) + aOutliner.Insert( aStr.getToken( 0, '\n', nIdx ), EE_PARA_APPEND, -1 ); + + if( aOutliner.GetParagraphCount() > 1 ) + { + SfxItemSet aAnswerSet( aOutliner.GetEmptyItemSet() ); + aAnswerSet.Put(SvxPostureItem(ITALIC_NORMAL,EE_CHAR_ITALIC)); + + ESelection aSel; + aSel.nEndPara = aOutliner.GetParagraphCount()-2; + aSel.nEndPos = aOutliner.GetText( aOutliner.GetParagraph( aSel.nEndPara ) ).getLength(); + + aOutliner.QuickSetAttribs( aAnswerSet, aSel ); + } + + if (!sReplyText.isEmpty()) + aOutliner.Insert(sReplyText); + + std::optional< OutlinerParaObject > pOPO( aOutliner.CreateParaObject() ); + pTextApi->SetText(*pOPO); + + OUString sReplyAuthor; + if (comphelper::LibreOfficeKit::isActive()) + sReplyAuthor = mrBase.GetMainViewShell()->GetView()->GetAuthor(); + else + { + SvtUserOptions aUserOptions; + sReplyAuthor = aUserOptions.GetFullName(); + xAnnotation->setInitials( aUserOptions.GetID() ); + } + + xAnnotation->setAuthor( sReplyAuthor ); + // set current time to reply + xAnnotation->setDateTime( getCurrentDateTime() ); + + // Tell our LOK clients about this (comment modification) + LOKCommentNotifyAll(CommentNotificationType::Modify, xAnnotation); + + UpdateTags(true); + SelectAnnotation( xAnnotation, true ); +} + +void AnnotationManagerImpl::DeleteAnnotation( const Reference< XAnnotation >& xAnnotation ) +{ + SdPage* pPage = GetCurrentPage(); + + if( xAnnotation.is() && pPage ) + { + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE ) ); + + pPage->removeAnnotation( xAnnotation ); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); + + UpdateTags(); + } +} + +void AnnotationManagerImpl::DeleteAnnotationsByAuthor( std::u16string_view sAuthor ) +{ + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE ) ); + + SdPage* pPage = nullptr; + do + { + pPage = GetNextPage( pPage, true ); + + if( pPage && !pPage->getAnnotations().empty() ) + { + AnnotationVector aAnnotations( pPage->getAnnotations() ); + for( Reference< XAnnotation >& xAnnotation : aAnnotations ) + { + if( xAnnotation->getAuthor() == sAuthor ) + { + if( mxSelectedAnnotation == xAnnotation ) + mxSelectedAnnotation.clear(); + pPage->removeAnnotation( xAnnotation ); + } + } + } + } while( pPage ); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); +} + +void AnnotationManagerImpl::DeleteAllAnnotations() +{ + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_DELETE ) ); + + SdPage* pPage = nullptr; + do + { + pPage = GetNextPage( pPage, true ); + + if( pPage && !pPage->getAnnotations().empty() ) + { + + AnnotationVector aAnnotations( pPage->getAnnotations() ); + for( const auto& rxAnnotation : aAnnotations ) + { + pPage->removeAnnotation( rxAnnotation ); + } + } + } + while( pPage ); + + mxSelectedAnnotation.clear(); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); +} + +void AnnotationManagerImpl::GetAnnotationState(SfxItemSet& rSet) +{ + SdPage* pCurrentPage = GetCurrentPage(); + + const bool bReadOnly = mrBase.GetDocShell()->IsReadOnly(); + const bool bWrongPageKind = (pCurrentPage == nullptr) || (pCurrentPage->GetPageKind() != PageKind::Standard); + + const SvtSaveOptions::ODFSaneDefaultVersion nCurrentODFVersion( GetODFSaneDefaultVersion() ); + + if (bReadOnly || bWrongPageKind || (nCurrentODFVersion <= SvtSaveOptions::ODFSVER_012)) + rSet.DisableItem( SID_INSERT_POSTIT ); + + rSet.Put(SfxBoolItem(SID_TOGGLE_NOTES, mbShowAnnotations)); + + Reference< XAnnotation > xAnnotation; + GetSelectedAnnotation( xAnnotation ); + + // Don't disable these slot in case of LOK, as postit doesn't need to + // selected before doing an operation on it in LOK + if( (!xAnnotation.is() && !comphelper::LibreOfficeKit::isActive()) || bReadOnly ) + { + rSet.DisableItem( SID_DELETE_POSTIT ); + rSet.DisableItem( SID_EDIT_POSTIT ); + } + + SdPage* pPage = nullptr; + + bool bHasAnnotations = false; + do + { + pPage = GetNextPage( pPage, true ); + + if( pPage && !pPage->getAnnotations().empty() ) + bHasAnnotations = true; + } + while( pPage && !bHasAnnotations ); + + if( !bHasAnnotations || bReadOnly ) + { + rSet.DisableItem( SID_DELETEALL_POSTIT ); + } + + if( bWrongPageKind || !bHasAnnotations ) + { + rSet.DisableItem( SID_PREVIOUS_POSTIT ); + rSet.DisableItem( SID_NEXT_POSTIT ); + } +} + +void AnnotationManagerImpl::SelectNextAnnotation(bool bForward) +{ + ShowAnnotations( true ); + + Reference< XAnnotation > xCurrent; + GetSelectedAnnotation( xCurrent ); + SdPage* pPage = GetCurrentPage(); + if( !pPage ) + return; + + AnnotationVector aAnnotations( pPage->getAnnotations() ); + + if( bForward ) + { + if( xCurrent.is() ) + { + auto iter = std::find(aAnnotations.begin(), aAnnotations.end(), xCurrent); + if (iter != aAnnotations.end()) + { + ++iter; + if( iter != aAnnotations.end() ) + { + SelectAnnotation( *iter ); + return; + } + } + } + else if( !aAnnotations.empty() ) + { + SelectAnnotation( *(aAnnotations.begin()) ); + return; + } + } + else + { + if( xCurrent.is() ) + { + auto iter = std::find(aAnnotations.begin(), aAnnotations.end(), xCurrent); + if (iter != aAnnotations.end() && iter != aAnnotations.begin()) + { + --iter; + SelectAnnotation( *iter ); + return; + } + } + else if( !aAnnotations.empty() ) + { + AnnotationVector::iterator iter( aAnnotations.end() ); + SelectAnnotation( *(--iter) ); + return; + } + } + + mxSelectedAnnotation.clear(); + do + { + do + { + pPage = GetNextPage( pPage, bForward ); + + if( pPage && !pPage->getAnnotations().empty() ) + { + // switch to next/previous slide with annotations + std::shared_ptr pDrawViewShell(std::dynamic_pointer_cast(mrBase.GetMainViewShell())); + if (pDrawViewShell != nullptr) + { + pDrawViewShell->ChangeEditMode(pPage->IsMasterPage() ? EditMode::MasterPage : EditMode::Page, false); + pDrawViewShell->SwitchPage((pPage->GetPageNum() - 1) >> 1); + + SfxDispatcher* pDispatcher = getDispatcher( mrBase ); + if( pDispatcher ) + pDispatcher->Execute( bForward ? SID_NEXT_POSTIT : SID_PREVIOUS_POSTIT ); + + return; + } + } + } + while( pPage ); + + // The question text depends on the search direction. + bool bImpress = mpDoc->GetDocumentType() == DocumentType::Impress; + TranslateId pStringId; + if(bForward) + pStringId = bImpress ? STR_ANNOTATION_WRAP_FORWARD : STR_ANNOTATION_WRAP_FORWARD_DRAW; + else + pStringId = bImpress ? STR_ANNOTATION_WRAP_BACKWARD : STR_ANNOTATION_WRAP_BACKWARD_DRAW; + + // Pop up question box that asks the user whether to wrap around. + // The dialog is made modal with respect to the whole application. + std::unique_ptr xQueryBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Question, VclButtonsType::YesNo, + SdResId(pStringId))); + xQueryBox->set_default_response(RET_YES); + if (xQueryBox->run() != RET_YES) + break; + } + while( true ); +} + +void AnnotationManagerImpl::onTagSelected( AnnotationTag const & rTag ) +{ + mxSelectedAnnotation = rTag.GetAnnotation(); + invalidateSlots(); +} + +void AnnotationManagerImpl::onTagDeselected( AnnotationTag const & rTag ) +{ + if( rTag.GetAnnotation() == mxSelectedAnnotation ) + { + mxSelectedAnnotation.clear(); + invalidateSlots(); + } +} + +void AnnotationManagerImpl::SelectAnnotation( const css::uno::Reference< css::office::XAnnotation >& xAnnotation, bool bEdit /* = sal_False */ ) +{ + mxSelectedAnnotation = xAnnotation; + + auto iter = std::find_if(maTagVector.begin(), maTagVector.end(), + [&xAnnotation](const rtl::Reference& rxTag) { return rxTag->GetAnnotation() == xAnnotation; }); + if (iter != maTagVector.end()) + { + SmartTagReference xTag( *iter ); + mrBase.GetMainViewShell()->GetView()->getSmartTags().select( xTag ); + (*iter)->OpenPopup( bEdit ); + } +} + +void AnnotationManagerImpl::GetSelectedAnnotation( css::uno::Reference< css::office::XAnnotation >& xAnnotation ) +{ + xAnnotation = mxSelectedAnnotation; +} + +void AnnotationManagerImpl::invalidateSlots() +{ + SfxBindings* pBindings = getBindings( mrBase ); + if( pBindings ) + { + pBindings->Invalidate( SID_INSERT_POSTIT ); + pBindings->Invalidate( SID_DELETE_POSTIT ); + pBindings->Invalidate( SID_DELETEALL_POSTIT ); + pBindings->Invalidate( SID_PREVIOUS_POSTIT ); + pBindings->Invalidate( SID_NEXT_POSTIT ); + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); + } +} + +void AnnotationManagerImpl::onSelectionChanged() +{ + if( !(mxView.is() && mrBase.GetDrawView()) ) + return; + + try + { + Reference< XAnnotationAccess > xPage( mxView->getCurrentPage(), UNO_QUERY ); + + if( xPage != mxCurrentPage ) + { + mxCurrentPage = xPage; + + UpdateTags(true); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::onSelectionChanged()" ); + } +} + +void AnnotationManagerImpl::UpdateTags( bool bSynchron ) +{ + if( bSynchron ) + { + if( mnUpdateTagsEvent ) + Application::RemoveUserEvent( mnUpdateTagsEvent ); + + UpdateTagsHdl(nullptr); + } + else + { + if( !mnUpdateTagsEvent && mxView.is() ) + mnUpdateTagsEvent = Application::PostUserEvent( LINK( this, AnnotationManagerImpl, UpdateTagsHdl ) ); + } +} + +IMPL_LINK_NOARG(AnnotationManagerImpl, UpdateTagsHdl, void*, void) +{ + mnUpdateTagsEvent = nullptr; + DisposeTags(); + + if( mbShowAnnotations ) + CreateTags(); + + if( mrBase.GetDrawView() ) + static_cast< ::sd::View* >( mrBase.GetDrawView() )->updateHandles(); + + invalidateSlots(); +} + +void AnnotationManagerImpl::CreateTags() +{ + if( !(mxCurrentPage.is() && mpDoc) ) + return; + + auto xViewShell = mrBase.GetMainViewShell(); + if (!xViewShell) + return; + + try + { + int nIndex = 1; + maFont = Application::GetSettings().GetStyleSettings().GetAppFont(); + + rtl::Reference< AnnotationTag > xSelectedTag; + + Reference< XAnnotationEnumeration > xEnum( mxCurrentPage->createAnnotationEnumeration() ); + while( xEnum->hasMoreElements() ) + { + Reference< XAnnotation > xAnnotation( xEnum->nextElement() ); + Color aColor( GetColorLight( mpDoc->GetAnnotationAuthorIndex( xAnnotation->getAuthor() ) ) ); + rtl::Reference< AnnotationTag > xTag( new AnnotationTag( *this, *xViewShell->GetView(), xAnnotation, aColor, nIndex++, maFont ) ); + maTagVector.push_back(xTag); + + if( xAnnotation == mxSelectedAnnotation ) + { + xSelectedTag = xTag; + } + } + + if( xSelectedTag.is() ) + { + SmartTagReference xTag( xSelectedTag ); + mrBase.GetMainViewShell()->GetView()->getSmartTags().select( xTag ); + } + else + { + // no tag, no selection! + mxSelectedAnnotation.clear(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnnotationManagerImpl::onSelectionChanged()" ); + } +} + +void AnnotationManagerImpl::DisposeTags() +{ + for (auto& rxTag : maTagVector) + { + rxTag->Dispose(); + } + + maTagVector.clear(); +} + +void AnnotationManagerImpl::addListener() +{ + Link aLink( LINK(this,AnnotationManagerImpl,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener(aLink); +} + +void AnnotationManagerImpl::removeListener() +{ + Link aLink( LINK(this,AnnotationManagerImpl,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(AnnotationManagerImpl,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::EditViewSelection: + onSelectionChanged(); + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxView.clear(); + onSelectionChanged(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mxView.set( mrBase.GetController(), UNO_QUERY ); + onSelectionChanged(); + break; + + default: break; + } +} + +void AnnotationManagerImpl::ExecuteAnnotationTagContextMenu(const Reference& xAnnotation, weld::Widget* pParent, const ::tools::Rectangle& rContextRect) +{ + SfxDispatcher* pDispatcher( getDispatcher( mrBase ) ); + if( !pDispatcher ) + return; + + const bool bReadOnly = mrBase.GetDocShell()->IsReadOnly(); + + if (bReadOnly) + return; + + std::unique_ptr xBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/annotationtagmenu.ui")); + std::unique_ptr xMenu(xBuilder->weld_menu("menu")); + + SvtUserOptions aUserOptions; + OUString sCurrentAuthor( aUserOptions.GetFullName() ); + OUString sAuthor( xAnnotation->getAuthor() ); + + OUString aStr(xMenu->get_label(".uno:DeleteAllAnnotationByAuthor")); + OUString aReplace( sAuthor ); + if( aReplace.isEmpty() ) + aReplace = SdResId( STR_ANNOTATION_NOAUTHOR ); + aStr = aStr.replaceFirst("%1", aReplace); + xMenu->set_label(".uno:DeleteAllAnnotationByAuthor", aStr); + + bool bShowReply = sAuthor != sCurrentAuthor; + xMenu->set_visible(".uno:ReplyToAnnotation", bShowReply); + xMenu->set_visible("separator", bShowReply); + xMenu->set_visible(".uno:DeleteAnnotation", xAnnotation.is()); + + auto sId = xMenu->popup_at_rect(pParent, rContextRect); + + if (sId == ".uno:ReplyToAnnotation") + { + const SfxUnoAnyItem aItem( SID_REPLYTO_POSTIT, Any( xAnnotation ) ); + pDispatcher->ExecuteList(SID_REPLYTO_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (sId == ".uno:DeleteAnnotation") + { + const SfxUnoAnyItem aItem( SID_DELETE_POSTIT, Any( xAnnotation ) ); + pDispatcher->ExecuteList(SID_DELETE_POSTIT, SfxCallMode::ASYNCHRON, + { &aItem }); + } + else if (sId == ".uno:DeleteAllAnnotationByAuthor") + { + const SfxStringItem aItem( SID_DELETEALLBYAUTHOR_POSTIT, sAuthor ); + pDispatcher->ExecuteList( SID_DELETEALLBYAUTHOR_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (sId == ".uno:DeleteAllAnnotation") + pDispatcher->Execute( SID_DELETEALL_POSTIT ); +} + +Color AnnotationManagerImpl::GetColor(sal_uInt16 aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayNormal[] = { + COL_AUTHOR1_NORMAL, COL_AUTHOR2_NORMAL, COL_AUTHOR3_NORMAL, + COL_AUTHOR4_NORMAL, COL_AUTHOR5_NORMAL, COL_AUTHOR6_NORMAL, + COL_AUTHOR7_NORMAL, COL_AUTHOR8_NORMAL, COL_AUTHOR9_NORMAL }; + + return aArrayNormal[ aAuthorIndex % SAL_N_ELEMENTS( aArrayNormal ) ]; + } + + return COL_WHITE; +} + +Color AnnotationManagerImpl::GetColorLight(sal_uInt16 aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayLight[] = { + COL_AUTHOR1_LIGHT, COL_AUTHOR2_LIGHT, COL_AUTHOR3_LIGHT, + COL_AUTHOR4_LIGHT, COL_AUTHOR5_LIGHT, COL_AUTHOR6_LIGHT, + COL_AUTHOR7_LIGHT, COL_AUTHOR8_LIGHT, COL_AUTHOR9_LIGHT }; + + return aArrayLight[ aAuthorIndex % SAL_N_ELEMENTS( aArrayLight ) ]; + } + + return COL_WHITE; +} + +Color AnnotationManagerImpl::GetColorDark(sal_uInt16 aAuthorIndex) +{ + if (!Application::GetSettings().GetStyleSettings().GetHighContrastMode()) + { + static const Color aArrayAnkor[] = { + COL_AUTHOR1_DARK, COL_AUTHOR2_DARK, COL_AUTHOR3_DARK, + COL_AUTHOR4_DARK, COL_AUTHOR5_DARK, COL_AUTHOR6_DARK, + COL_AUTHOR7_DARK, COL_AUTHOR8_DARK, COL_AUTHOR9_DARK }; + + return aArrayAnkor[ aAuthorIndex % SAL_N_ELEMENTS( aArrayAnkor ) ]; + } + + return COL_WHITE; +} + +SdPage* AnnotationManagerImpl::GetNextPage( SdPage const * pPage, bool bForward ) +{ + if( pPage == nullptr ) + { + if (bForward) + return mpDoc->GetSdPage(0, PageKind::Standard ); // first page + else + return mpDoc->GetMasterSdPage( mpDoc->GetMasterSdPageCount(PageKind::Standard) - 1, PageKind::Standard ); // last page + } + + sal_uInt16 nPageNum = (pPage->GetPageNum() - 1) >> 1; + + // first all non master pages + if( !pPage->IsMasterPage() ) + { + if( bForward ) + { + if( nPageNum >= mpDoc->GetSdPageCount(PageKind::Standard)-1 ) + { + // we reached end of draw pages, start with master pages (skip handout master for draw) + return mpDoc->GetMasterSdPage( (mpDoc->GetDocumentType() == DocumentType::Impress) ? 0 : 1, PageKind::Standard ); + } + nPageNum++; + } + else + { + if( nPageNum == 0 ) + return nullptr; // we are already on the first draw page, finished + + nPageNum--; + } + return mpDoc->GetSdPage(nPageNum, PageKind::Standard); + } + else + { + if( bForward ) + { + if( nPageNum >= mpDoc->GetMasterSdPageCount(PageKind::Standard)-1 ) + { + return nullptr; // we reached the end, there is nothing more to see here + } + nPageNum++; + } + else + { + if( nPageNum == (mpDoc->GetDocumentType() == DocumentType::Impress ? 0 : 1) ) + { + // we reached beginning of master pages, start with end if pages + return mpDoc->GetSdPage( mpDoc->GetSdPageCount(PageKind::Standard)-1, PageKind::Standard ); + } + + nPageNum--; + } + return mpDoc->GetMasterSdPage(nPageNum,PageKind::Standard); + } +} + +SdPage* AnnotationManagerImpl::GetCurrentPage() +{ + if (mrBase.GetMainViewShell()) + return mrBase.GetMainViewShell()->getCurrentPage(); + return nullptr; +} + +AnnotationManager::AnnotationManager( ViewShellBase& rViewShellBase ) +: mxImpl( new AnnotationManagerImpl( rViewShellBase ) ) +{ + mxImpl->init(); +} + +AnnotationManager::~AnnotationManager() +{ + mxImpl->dispose(); +} + +void AnnotationManager::ExecuteAnnotation(SfxRequest const & rRequest) +{ + mxImpl->ExecuteAnnotation( rRequest ); +} + +void AnnotationManager::GetAnnotationState(SfxItemSet& rItemSet) +{ + mxImpl->GetAnnotationState(rItemSet); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationmanagerimpl.hxx b/sd/source/ui/annotations/annotationmanagerimpl.hxx new file mode 100644 index 000000000..c5871d90c --- /dev/null +++ b/sd/source/ui/annotations/annotationmanagerimpl.hxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include + +#include "annotationtag.hxx" + +namespace com::sun::star::drawing { class XDrawView; } +namespace com::sun::star::office { class XAnnotationAccess; } +namespace com::sun::star::office { class XAnnotation; } + +class SfxRequest; +class SdPage; +class SdDrawDocument; +struct ImplSVEvent; + +namespace sd +{ + +class ViewShellBase; + +namespace tools { +class EventMultiplexerEvent; +} + +typedef comphelper::WeakComponentImplHelper < + css::document::XEventListener + > AnnotationManagerImplBase; + +class AnnotationManagerImpl : public AnnotationManagerImplBase +{ +public: + explicit AnnotationManagerImpl( ViewShellBase& rViewShellBase ); + + void init(); + + // WeakComponentImplHelper + virtual void disposing (std::unique_lock&) override; + + // XEventListener + virtual void SAL_CALL notifyEvent( const css::document::EventObject& Event ) override; + virtual void SAL_CALL disposing( const css::lang::EventObject& Source ) override; + + void ExecuteAnnotation (SfxRequest const & rRequest); + void GetAnnotationState (SfxItemSet& rItemSet); + + void ExecuteInsertAnnotation(SfxRequest const & rReq); + void ExecuteDeleteAnnotation(SfxRequest const & rReq); + void ExecuteEditAnnotation(SfxRequest const & rReq); + void ExecuteReplyToAnnotation(SfxRequest const & rReq); + + void SelectNextAnnotation(bool bForward); + + void SelectAnnotation( const css::uno::Reference< css::office::XAnnotation >& xAnnotation, bool bEdit = false ); + void GetSelectedAnnotation( css::uno::Reference< css::office::XAnnotation >& xAnnotation ); + + void InsertAnnotation(const OUString& rText); + void DeleteAnnotation( const css::uno::Reference< css::office::XAnnotation >& xAnnotation ); + void DeleteAnnotationsByAuthor( std::u16string_view sAuthor ); + void DeleteAllAnnotations(); + + void ExecuteAnnotationTagContextMenu(const css::uno::Reference& xAnnotation, weld::Widget* pParent, const ::tools::Rectangle& rContextRect); + + static Color GetColorDark(sal_uInt16 aAuthorIndex); + static Color GetColorLight(sal_uInt16 aAuthorIndex); + static Color GetColor(sal_uInt16 aAuthorIndex); + + // callbacks + void onTagSelected( AnnotationTag const & rTag ); + void onTagDeselected( AnnotationTag const & rTag ); + + void onSelectionChanged(); + + void addListener(); + void removeListener(); + + void invalidateSlots(); + + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void); + DECL_LINK(UpdateTagsHdl, void *, void); + + void UpdateTags(bool bSynchron = false); + void CreateTags(); + void DisposeTags(); + + SdPage* GetNextPage( SdPage const * pPage, bool bForward ); + + SdPage* GetCurrentPage(); + + SdDrawDocument* GetDoc() { return mpDoc; } + + void ShowAnnotations(bool bShow); + +private: + ViewShellBase& mrBase; + SdDrawDocument* mpDoc; + + std::vector< rtl::Reference< AnnotationTag > > maTagVector; + + css::uno::Reference< css::drawing::XDrawView > mxView; + css::uno::Reference< css::office::XAnnotationAccess > mxCurrentPage; + css::uno::Reference< css::office::XAnnotation > mxSelectedAnnotation; + + bool mbShowAnnotations; + ImplSVEvent * mnUpdateTagsEvent; + vcl::Font maFont; + + css::uno::Reference GetAnnotationById(sal_uInt32 nAnnotationId); +}; + +OUString getAnnotationDateTimeString( const css::uno::Reference< css::office::XAnnotation >& xAnnotation ); + +SfxItemPool* GetAnnotationPool(); + +css::util::DateTime getCurrentDateTime(); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationtag.cxx b/sd/source/ui/annotations/annotationtag.cxx new file mode 100644 index 000000000..cfd632dcc --- /dev/null +++ b/sd/source/ui/annotations/annotationtag.cxx @@ -0,0 +1,662 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "annotationmanagerimpl.hxx" +#include "annotationwindow.hxx" +#include "annotationtag.hxx" +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::geometry; + +namespace sd +{ + +const sal_uInt32 SMART_TAG_HDL_NUM = SAL_MAX_UINT32; +const int DRGPIX = 2; // Drag MinMove in Pixel + +static OUString getInitials( const OUString& rName ) +{ + OUStringBuffer sInitials; + + const sal_Unicode * pStr = rName.getStr(); + sal_Int32 nLength = rName.getLength(); + + while( nLength ) + { + // skip whitespace + while( nLength && (*pStr <= ' ') ) + { + nLength--; pStr++; + } + + // take letter + if( nLength ) + { + sInitials.append(*pStr); + nLength--; pStr++; + } + + // skip letters until whitespace + while( nLength && (*pStr > ' ') ) + { + nLength--; pStr++; + } + } + + return sInitials.makeStringAndClear(); +} + +namespace { + +class AnnotationDragMove : public SdrDragMove +{ +public: + AnnotationDragMove(SdrDragView& rNewView, const rtl::Reference & xTag); + virtual bool BeginSdrDrag() override; + virtual bool EndSdrDrag(bool bCopy) override; + virtual void MoveSdrDrag(const Point& rNoSnapPnt) override; + virtual void CancelSdrDrag() override; + +private: + rtl::Reference mxTag; + Point maOrigin; +}; + +} + +AnnotationDragMove::AnnotationDragMove(SdrDragView& rNewView, const rtl::Reference & xTag) +: SdrDragMove(rNewView) +, mxTag( xTag ) +{ +} + +bool AnnotationDragMove::BeginSdrDrag() +{ + DragStat().SetRef1(GetDragHdl()->GetPos()); + DragStat().SetShown(!DragStat().IsShown()); + + maOrigin = GetDragHdl()->GetPos(); + DragStat().SetActionRect(::tools::Rectangle(maOrigin,maOrigin)); + + return true; +} + +void AnnotationDragMove::MoveSdrDrag(const Point& rNoSnapPnt) +{ + Point aPnt(rNoSnapPnt); + + if (DragStat().CheckMinMoved(rNoSnapPnt)) + { + if (aPnt!=DragStat().GetNow()) + { + Hide(); + DragStat().NextMove(aPnt); + GetDragHdl()->SetPos( maOrigin + Point( DragStat().GetDX(), DragStat().GetDY() ) ); + Show(); + DragStat().SetActionRect(::tools::Rectangle(aPnt,aPnt)); + } + } +} + +bool AnnotationDragMove::EndSdrDrag(bool /*bCopy*/) +{ + Hide(); + if( mxTag.is() ) + mxTag->Move( DragStat().GetDX(), DragStat().GetDY() ); + return true; +} + +void AnnotationDragMove::CancelSdrDrag() +{ + Hide(); +} + +namespace { + +class AnnotationHdl : public SmartHdl +{ +public: + AnnotationHdl( const SmartTagReference& xTag, const Reference< XAnnotation >& xAnnotation, const Point& rPnt ); + + virtual void CreateB2dIAObject() override; + virtual bool IsFocusHdl() const override; + +private: + Reference< XAnnotation > mxAnnotation; + rtl::Reference< AnnotationTag > mxTag; +}; + +} + +AnnotationHdl::AnnotationHdl( const SmartTagReference& xTag, const Reference< XAnnotation >& xAnnotation, const Point& rPnt ) +: SmartHdl( xTag, rPnt, SdrHdlKind::SmartTag ) +, mxAnnotation( xAnnotation ) +, mxTag( dynamic_cast< AnnotationTag* >( xTag.get() ) ) +{ +} + +void AnnotationHdl::CreateB2dIAObject() +{ + // first throw away old one + GetRidOfIAObject(); + + if (!mxAnnotation.is()) + return; + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + const Point aTagPos( GetPos() ); + basegfx::B2DPoint aPosition( aTagPos.X(), aTagPos.Y() ); + + const bool bFocused = IsFocusHdl() && pHdlList && (pHdlList->GetFocusHdl() == this); + + BitmapEx aBitmapEx( mxTag->CreateAnnotationBitmap(mxTag->isSelected()) ); + BitmapEx aBitmapEx2; + if( bFocused ) + aBitmapEx2 = mxTag->CreateAnnotationBitmap(!mxTag->isSelected() ); + + if(!pHdlList) + return; + + SdrMarkView* pView = pHdlList->GetView(); + + if(!pView || pView->areMarkHandlesHidden()) + return; + + SdrPageView* pPageView = pView->GetSdrPageView(); + + if(!pPageView) + return; + + for(sal_uInt32 b = 0; b < pPageView->PageWindowCount(); b++) + { + // const SdrPageViewWinRec& rPageViewWinRec = rPageViewWinList[b]; + const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); + + SdrPaintWindow& rPaintWindow = rPageWindow.GetPaintWindow(); + const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); + if(rPaintWindow.OutputToWindow() && xManager.is() ) + { + std::unique_ptr pOverlayObject; + + auto* pAnnotation = dynamic_cast(mxAnnotation.get()); + + if (pAnnotation && pAnnotation->hasCustomAnnotationMarker()) + { + CustomAnnotationMarker& rCustomAnnotationMarker = pAnnotation->getCustomAnnotationMarker(); + + auto& rPolygons = rCustomAnnotationMarker.maPolygons; + if (!rPolygons.empty()) + { + basegfx::B2DPolyPolygon aPolyPolygon; + for (auto const & rPolygon : rPolygons) + aPolyPolygon.append(rPolygon); + + pOverlayObject.reset(new sdr::overlay::OverlayPolyPolygon( + aPolyPolygon, + rCustomAnnotationMarker.maLineColor, + rCustomAnnotationMarker.mnLineWidth, + rCustomAnnotationMarker.maFillColor)); + } + } + else + { + // animate focused handles + if(bFocused) + { + const sal_uInt64 nBlinkTime = rStyleSettings.GetCursorBlinkTime(); + + pOverlayObject.reset(new sdr::overlay::OverlayAnimatedBitmapEx(aPosition, aBitmapEx, aBitmapEx2, nBlinkTime, 0, 0, 0, 0 )); + } + else + { + pOverlayObject.reset(new sdr::overlay::OverlayBitmapEx( aPosition, aBitmapEx, 0, 0 )); + } + } + + // OVERLAYMANAGER + insertNewlyCreatedOverlayObjectForSdrHdl( + std::move(pOverlayObject), + rPageWindow.GetObjectContact(), + *xManager); + } + } +} + +bool AnnotationHdl::IsFocusHdl() const +{ + return true; +} + +AnnotationTag::AnnotationTag( AnnotationManagerImpl& rManager, ::sd::View& rView, const Reference< XAnnotation >& xAnnotation, Color const & rColor, int nIndex, const vcl::Font& rFont ) +: SmartTag( rView ) +, mrManager( rManager ) +, mxAnnotation( xAnnotation ) +, maColor( rColor ) +, mnIndex( nIndex ) +, mrFont( rFont ) +, mpListenWindow( nullptr ) +{ +} + +AnnotationTag::~AnnotationTag() +{ + DBG_ASSERT( !mxAnnotation.is(), "sd::AnnotationTag::~AnnotationTag(), dispose me first!" ); + Dispose(); +} + +/** returns true if the AnnotationTag handled the event. */ +bool AnnotationTag::MouseButtonDown( const MouseEvent& rMEvt, SmartHdl& /*rHdl*/ ) +{ + if( !mxAnnotation.is() ) + return false; + + bool bRet = false; + if( !isSelected() ) + { + SmartTagReference xTag( this ); + mrView.getSmartTags().select( xTag ); + bRet = true; + } + + if( rMEvt.IsLeft() && !rMEvt.IsRight() ) + { + vcl::Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); + if( pWindow ) + { + maMouseDownPos = pWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + if( mpListenWindow ) + mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + + mpListenWindow = pWindow; + mpListenWindow->AddEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + } + + bRet = true; + } + + return bRet; +} + +/** returns true if the SmartTag consumes this event. */ +bool AnnotationTag::KeyInput( const KeyEvent& rKEvt ) +{ + if( !mxAnnotation.is() ) + return false; + + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch( nCode ) + { + case KEY_DELETE: + mrManager.DeleteAnnotation( mxAnnotation ); + return true; + + case KEY_DOWN: + case KEY_UP: + case KEY_LEFT: + case KEY_RIGHT: + return OnMove( rKEvt ); + + case KEY_ESCAPE: + { + SmartTagReference xThis( this ); + mrView.getSmartTags().deselect(); + return true; + } + + case KEY_TAB: + mrManager.SelectNextAnnotation(!rKEvt.GetKeyCode().IsShift()); + return true; + + case KEY_RETURN: + case KEY_SPACE: + OpenPopup( true ); + return true; + + default: + return false; + } +} + +/** returns true if the SmartTag consumes this event. */ +bool AnnotationTag::Command( const CommandEvent& rCEvt ) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu) + return false; + if (vcl::Window* pWindow = mrView.GetViewShell()->GetActiveWindow()) + { + ::tools::Rectangle aContextRect(rCEvt.GetMousePosPixel(),Size(1,1)); + weld::Window* pParent = weld::GetPopupParent(*pWindow, aContextRect); + mrManager.ExecuteAnnotationTagContextMenu(mxAnnotation, pParent, aContextRect); + return true; + } + return false; +} + +void AnnotationTag::Move( int nDX, int nDY ) +{ + if( !mxAnnotation.is() ) + return; + + if( mrManager.GetDoc()->IsUndoEnabled() ) + mrManager.GetDoc()->BegUndo( SdResId( STR_ANNOTATION_UNDO_MOVE ) ); + + RealPoint2D aPosition( mxAnnotation->getPosition() ); + aPosition.X += static_cast(nDX) / 100.0; + aPosition.Y += static_cast(nDY) / 100.0; + mxAnnotation->setPosition( aPosition ); + + if( mrManager.GetDoc()->IsUndoEnabled() ) + mrManager.GetDoc()->EndUndo(); + + mrView.updateHandles(); +} + +bool AnnotationTag::OnMove( const KeyEvent& rKEvt ) +{ + ::tools::Long nX = 0; + ::tools::Long nY = 0; + + switch( rKEvt.GetKeyCode().GetCode() ) + { + case KEY_UP: nY = -1; break; + case KEY_DOWN: nY = 1; break; + case KEY_LEFT: nX = -1; break; + case KEY_RIGHT: nX = 1; break; + default: break; + } + + if(rKEvt.GetKeyCode().IsMod2()) + { + OutputDevice* pOut = mrView.GetViewShell()->GetActiveWindow()->GetOutDev(); + Size aLogicSizeOnePixel = pOut ? pOut->PixelToLogic(Size(1,1)) : Size(100, 100); + nX *= aLogicSizeOnePixel.Width(); + nY *= aLogicSizeOnePixel.Height(); + } + else + { + // old, fixed move distance + nX *= 100; + nY *= 100; + } + + if( nX || nY ) + { + // move the annotation + Move( nX, nY ); + } + + return true; +} + +void AnnotationTag::CheckPossibilities() +{ +} + +sal_Int32 AnnotationTag::GetMarkablePointCount() const +{ + return 0; +} + +sal_Int32 AnnotationTag::GetMarkedPointCount() const +{ + return 0; +} + +bool AnnotationTag::MarkPoint(SdrHdl& /*rHdl*/, bool /*bUnmark*/ ) +{ + return false; +} + +bool AnnotationTag::MarkPoints(const ::tools::Rectangle* /*pRect*/, bool /*bUnmark*/ ) +{ + return false; +} + +bool AnnotationTag::getContext( SdrViewContext& /*rContext*/ ) +{ + return false; +} + +void AnnotationTag::addCustomHandles( SdrHdlList& rHandlerList ) +{ + if( !mxAnnotation.is() ) + return; + + SmartTagReference xThis( this ); + std::unique_ptr pHdl(new AnnotationHdl( xThis, mxAnnotation, Point() )); + pHdl->SetObjHdlNum( SMART_TAG_HDL_NUM ); + pHdl->SetPageView( mrView.GetSdrPageView() ); + + RealPoint2D aPosition( mxAnnotation->getPosition() ); + Point aBasePos( static_cast<::tools::Long>(aPosition.X * 100.0), static_cast<::tools::Long>(aPosition.Y * 100.0) ); + pHdl->SetPos( aBasePos ); + + rHandlerList.AddHdl( std::move(pHdl) ); +} + +void AnnotationTag::disposing() +{ + if( mpListenWindow ) + { + mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + } + + mxAnnotation.clear(); + ClosePopup(); + SmartTag::disposing(); +} + +void AnnotationTag::select() +{ + SmartTag::select(); + + mrManager.onTagSelected( *this ); + + vcl::Window* pWindow = mrView.GetViewShell()->GetActiveWindow(); + if( pWindow ) + { + RealPoint2D aPosition( mxAnnotation->getPosition() ); + Point aPos( static_cast<::tools::Long>(aPosition.X * 100.0), static_cast<::tools::Long>(aPosition.Y * 100.0) ); + + ::tools::Rectangle aVisRect( aPos, pWindow->PixelToLogic(maSize) ); + mrView.MakeVisible(aVisRect, *pWindow); + } +} + +void AnnotationTag::deselect() +{ + SmartTag::deselect(); + + ClosePopup(); + + mrManager.onTagDeselected( *this ); +} + +BitmapEx AnnotationTag::CreateAnnotationBitmap( bool bSelected ) +{ + ScopedVclPtrInstance< VirtualDevice > pVDev; + + OUString sInitials(mxAnnotation->getInitials()); + if (sInitials.isEmpty()) + sInitials = getInitials(mxAnnotation->getAuthor()); + + OUString sAuthor(sInitials + " " + OUString::number(mnIndex)); + + pVDev->SetFont( mrFont ); + + const int BORDER_X = 4; // pixels + const int BORDER_Y = 4; // pixels + + maSize = Size( pVDev->GetTextWidth( sAuthor ) + 2*BORDER_X, pVDev->GetTextHeight() + 2*BORDER_Y ); + pVDev->SetOutputSizePixel( maSize, false ); + + Color aBorderColor( maColor ); + + if( bSelected ) + { + aBorderColor.Invert(); + } + else + { + if( maColor.IsDark() ) + { + aBorderColor.IncreaseLuminance( 32 ); + } + else + { + aBorderColor.DecreaseLuminance( 32 ); + } + } + + Point aPos; + ::tools::Rectangle aBorderRect( aPos, maSize ); + pVDev->SetLineColor(aBorderColor); + pVDev->SetFillColor(maColor); + pVDev->DrawRect( aBorderRect ); + + pVDev->SetTextColor( maColor.IsDark() ? COL_WHITE : COL_BLACK ); + pVDev->DrawText( Point( BORDER_X, BORDER_Y ), sAuthor ); + + return pVDev->GetBitmapEx( aPos, maSize ); +} + +void AnnotationTag::OpenPopup( bool bEdit ) +{ + if( !mxAnnotation.is() ) + return; + + if( !mpAnnotationWindow ) + { + OutputDevice* pOut = getView().GetFirstOutputDevice(); + vcl::Window* pWindow = pOut ? pOut->GetOwnerWindow() : nullptr; + if( pWindow ) + { + RealPoint2D aPosition( mxAnnotation->getPosition() ); + Point aPos(pWindow->LogicToPixel( Point( static_cast<::tools::Long>(aPosition.X * 100.0), static_cast<::tools::Long>(aPosition.Y * 100.0) ) ) ); + + aPos.AdjustX(4 ); // magic! + aPos.AdjustY(1 ); + + ::tools::Rectangle aRect( aPos, maSize ); + + weld::Window* pParent = weld::GetPopupParent(*pWindow, aRect); + mpAnnotationWindow.reset(new AnnotationWindow(pParent, aRect, mrView.GetDocSh(), mxAnnotation)); + mpAnnotationWindow->connect_closed(LINK(this, AnnotationTag, PopupModeEndHdl)); + } + } + + if (bEdit && mpAnnotationWindow) + mpAnnotationWindow->StartEdit(); +} + +IMPL_LINK_NOARG(AnnotationTag, PopupModeEndHdl, weld::Popover&, void) +{ + ClosePopup(); +} + +void AnnotationTag::ClosePopup() +{ + if (mpAnnotationWindow) + { + mpAnnotationWindow->SaveToDocument(); + mpAnnotationWindow.reset(); + } +} + +IMPL_LINK(AnnotationTag, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + vcl::Window* pWindow = rEvent.GetWindow(); + + if( !pWindow ) + return; + + if( pWindow != mpListenWindow ) + return; + + switch( rEvent.GetId() ) + { + case VclEventId::WindowMouseButtonUp: + { + // if we stop pressing the button without a mouse move we open the popup + mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + mpListenWindow = nullptr; + if( !mpAnnotationWindow ) + OpenPopup(false); + } + break; + case VclEventId::WindowMouseMove: + { + // if we move the mouse after a button down we want to start dragging + mpListenWindow->RemoveEventListener( LINK(this, AnnotationTag, WindowEventHandler)); + mpListenWindow = nullptr; + + SdrHdl* pHdl = mrView.PickHandle(maMouseDownPos); + if( pHdl ) + { + mrView.BrkAction(); + const sal_uInt16 nDrgLog = static_cast(pWindow->PixelToLogic(Size(DRGPIX,0)).Width()); + + rtl::Reference< AnnotationTag > xTag( this ); + + SdrDragMethod* pDragMethod = new AnnotationDragMove( mrView, xTag ); + mrView.BegDragObj(maMouseDownPos, nullptr, pHdl, nDrgLog, pDragMethod ); + } + } + break; + case VclEventId::ObjectDying: + mpListenWindow = nullptr; + break; + default: break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationtag.hxx b/sd/source/ui/annotations/annotationtag.hxx new file mode 100644 index 000000000..23dcde13a --- /dev/null +++ b/sd/source/ui/annotations/annotationtag.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include "annotationwindow.hxx" + +namespace com::sun::star::office { class XAnnotation; } + +namespace sd { + +class View; +class AnnotationManagerImpl; + +class AnnotationTag final : public SmartTag +{ +public: + AnnotationTag( AnnotationManagerImpl& rManager, ::sd::View& rView, const css::uno::Reference< css::office::XAnnotation >& xAnnotation, Color const & rColor, int nIndex, const vcl::Font& rFont ); + virtual ~AnnotationTag() override; + + /// @return true if the SmartTag handled the event. + virtual bool MouseButtonDown( const MouseEvent&, SmartHdl& ) override; + + /// @return true if the SmartTag consumes this event. + virtual bool KeyInput( const KeyEvent& rKEvt ) override; + + /// @return true if the SmartTag consumes this event. + virtual bool Command( const CommandEvent& rCEvt ) override; + + // callbacks from sdr view + virtual sal_Int32 GetMarkablePointCount() const override; + virtual sal_Int32 GetMarkedPointCount() const override; + virtual bool MarkPoint(SdrHdl& rHdl, bool bUnmark) override; + virtual void CheckPossibilities() override; + virtual bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) override; + + void Move( int nDX, int nDY ); + bool OnMove( const KeyEvent& rKEvt ); + + BitmapEx CreateAnnotationBitmap(bool); + + const css::uno::Reference< css::office::XAnnotation >& GetAnnotation() const { return mxAnnotation; } + + void OpenPopup( bool bEdit ); + void ClosePopup(); + +private: + virtual void addCustomHandles( SdrHdlList& rHandlerList ) override; + virtual bool getContext( SdrViewContext& rContext ) override; + virtual void disposing() override; + virtual void select() override; + virtual void deselect() override; + + DECL_LINK( WindowEventHandler, VclWindowEvent&, void ); + DECL_LINK(PopupModeEndHdl, weld::Popover&, void); + + AnnotationManagerImpl& mrManager; + css::uno::Reference< css::office::XAnnotation > mxAnnotation; + std::unique_ptr mpAnnotationWindow; + Color maColor; + int mnIndex; + const vcl::Font& mrFont; + Size maSize; + VclPtr mpListenWindow; + Point maMouseDownPos; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationwindow.cxx b/sd/source/ui/annotations/annotationwindow.cxx new file mode 100644 index 000000000..6c1210575 --- /dev/null +++ b/sd/source/ui/annotations/annotationwindow.cxx @@ -0,0 +1,802 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "annotationwindow.hxx" +#include "annotationmanagerimpl.hxx" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::office; +using namespace ::com::sun::star::text; + +#define METABUTTON_WIDTH 16 +#define METABUTTON_HEIGHT 18 +#define POSTIT_META_HEIGHT sal_Int32(30) + +namespace sd { + +void AnnotationTextWindow::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) +{ + Size aSize = GetOutputSizePixel(); + + const bool bHighContrast = Application::GetSettings().GetStyleSettings().GetHighContrastMode(); + if (!bHighContrast) + { + rRenderContext.DrawGradient(::tools::Rectangle(Point(0,0), rRenderContext.PixelToLogic(aSize)), + Gradient(GradientStyle::Linear, mrContents.maColorLight, mrContents.maColor)); + } + + DoPaint(rRenderContext, rRect); +} + +void AnnotationTextWindow::EditViewScrollStateChange() +{ + mrContents.SetScrollbar(); +} + +bool AnnotationTextWindow::KeyInput(const KeyEvent& rKeyEvt) +{ + const vcl::KeyCode& rKeyCode = rKeyEvt.GetKeyCode(); + sal_uInt16 nKey = rKeyCode.GetCode(); + + bool bDone = false; + + if ((rKeyCode.IsMod1() && rKeyCode.IsMod2()) && ((nKey == KEY_PAGEUP) || (nKey == KEY_PAGEDOWN))) + { + SfxDispatcher* pDispatcher = mrContents.DocShell()->GetViewShell()->GetViewFrame()->GetDispatcher(); + if( pDispatcher ) + pDispatcher->Execute( nKey == KEY_PAGEDOWN ? SID_NEXT_POSTIT : SID_PREVIOUS_POSTIT ); + bDone = true; + } + else if (nKey == KEY_INSERT) + { + if (!rKeyCode.IsMod1() && !rKeyCode.IsMod2()) + mrContents.ToggleInsMode(); + bDone = true; + } + else + { + ::tools::Long aOldHeight = mrContents.GetPostItTextHeight(); + + /// HACK: need to switch off processing of Undo/Redo in Outliner + if ( !( (nKey == KEY_Z || nKey == KEY_Y) && rKeyCode.IsMod1()) ) + { + bool bIsProtected = mrContents.IsProtected(); + if (!bIsProtected || !EditEngine::DoesKeyChangeText(rKeyEvt) ) + { + if (EditView* pEditView = GetEditView()) + { + bDone = pEditView->PostKeyEvent(rKeyEvt); + if (!bDone && rKeyEvt.GetKeyCode().IsMod1() && !rKeyEvt.GetKeyCode().IsMod2()) + { + if (nKey == KEY_A) + { + EditEngine* pEditEngine = GetEditEngine(); + sal_Int32 nPar = pEditEngine->GetParagraphCount(); + if (nPar) + { + sal_Int32 nLen = pEditEngine->GetTextLen(nPar - 1); + pEditView->SetSelection(ESelection(0, 0, nPar - 1, nLen)); + } + bDone = true; + } + } + } + } + } + if (bDone) + { + mrContents.ResizeIfNecessary(aOldHeight, mrContents.GetPostItTextHeight()); + } + } + + return bDone; +} + +AnnotationTextWindow::AnnotationTextWindow(AnnotationWindow& rContents) + : mrContents(rContents) +{ +} + +EditView* AnnotationTextWindow::GetEditView() const +{ + OutlinerView* pOutlinerView = mrContents.GetOutlinerView(); + if (!pOutlinerView) + return nullptr; + return &pOutlinerView->GetEditView(); +} + +EditEngine* AnnotationTextWindow::GetEditEngine() const +{ + OutlinerView* pOutlinerView = mrContents.GetOutlinerView(); + if (!pOutlinerView) + return nullptr; + return pOutlinerView->GetEditView().GetEditEngine(); +} + +void AnnotationTextWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(0, 0); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + + SetOutputSizePixel(aSize); + + weld::CustomWidgetController::SetDrawingArea(pDrawingArea); + + EnableRTL(false); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + Color aBgColor = rStyleSettings.GetWindowColor(); + + OutputDevice& rDevice = pDrawingArea->get_ref_device(); + + rDevice.SetMapMode(MapMode(MapUnit::Map100thMM)); + rDevice.SetBackground(aBgColor); + + Size aOutputSize(rDevice.PixelToLogic(aSize)); + + EditView* pEditView = GetEditView(); + pEditView->setEditViewCallbacks(this); + + EditEngine* pEditEngine = GetEditEngine(); + pEditEngine->SetPaperSize(aOutputSize); + pEditEngine->SetRefDevice(&rDevice); + + pEditView->SetOutputArea(::tools::Rectangle(Point(0, 0), aOutputSize)); + pEditView->SetBackgroundColor(aBgColor); + + pDrawingArea->set_cursor(PointerStyle::Text); + + InitAccessible(); +} + +// see SwAnnotationWin in sw for something similar +AnnotationWindow::AnnotationWindow(weld::Window* pParent, const ::tools::Rectangle& rRect, + DrawDocShell* pDocShell, + const Reference& xAnnotation) + : mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/annotation.ui")) + , mxPopover(mxBuilder->weld_popover("Annotation")) + , mxContainer(mxBuilder->weld_widget("container")) + , mpDocShell(pDocShell) + , mpDoc(pDocShell->GetDoc()) + , mbReadonly(pDocShell->IsReadOnly()) + , mbProtected(false) +{ + mxContainer->set_size_request(320, 240); + mxPopover->popup_at_rect(pParent, rRect); + + InitControls(); + setAnnotation(xAnnotation); + FillMenuButton(); + + DoResize(); + + mxTextControl->GrabFocus(); +} + +AnnotationWindow::~AnnotationWindow() +{ +} + +void AnnotationWindow::InitControls() +{ + // window control for author and date + mxMeta = mxBuilder->weld_label("meta"); + mxMeta->set_direction(AllSettings::GetLayoutRTL()); + + maLabelFont = Application::GetSettings().GetStyleSettings().GetLabelFont(); + maLabelFont.SetFontHeight(8); + + // we should leave this setting alone, but for this we need a better layout algo + // with variable meta size height + mxMeta->set_font(maLabelFont); + + mpOutliner.reset( new ::Outliner(GetAnnotationPool(),OutlinerMode::TextObject) ); + SdDrawDocument::SetCalcFieldValueHdl( mpOutliner.get() ); + mpOutliner->SetUpdateLayout( true ); + + if (OutputDevice* pDev = mpDoc->GetRefDevice()) + mpOutliner->SetRefDevice( pDev ); + + mpOutlinerView.reset( new OutlinerView ( mpOutliner.get(), nullptr) ); + mpOutliner->InsertView(mpOutlinerView.get() ); + + //create Scrollbars + mxVScrollbar = mxBuilder->weld_scrolled_window("scrolledwindow", true); + + // actual window which holds the user text + mxTextControl.reset(new AnnotationTextWindow(*this)); + mxTextControlWin.reset(new weld::CustomWeld(*mxBuilder, "editview", *mxTextControl)); + mxTextControl->SetPointer(PointerStyle::Text); + + Rescale(); + OutputDevice& rDevice = mxTextControl->GetDrawingArea()->get_ref_device(); + + mxVScrollbar->set_direction(false); + mxVScrollbar->connect_vadjustment_changed(LINK(this, AnnotationWindow, ScrollHdl)); + + mpOutlinerView->SetBackgroundColor(COL_TRANSPARENT); + mpOutlinerView->SetOutputArea(rDevice.PixelToLogic(::tools::Rectangle(0, 0, 1, 1))); + + mxMenuButton = mxBuilder->weld_menu_button("menubutton"); + if (mbReadonly) + mxMenuButton->hide(); + else + { + mxMenuButton->set_size_request(METABUTTON_WIDTH, METABUTTON_HEIGHT); + mxMenuButton->connect_selected(LINK(this, AnnotationWindow, MenuItemSelectedHdl)); + } + + EEControlBits nCntrl = mpOutliner->GetControlWord(); + nCntrl |= EEControlBits::PASTESPECIAL | EEControlBits::AUTOCORRECT | EEControlBits::USECHARATTRIBS | EEControlBits::NOCOLORS; + mpOutliner->SetControlWord(nCntrl); + + mpOutliner->SetModifyHdl( Link() ); + mpOutliner->EnableUndo( false ); + + mpOutliner->ClearModifyFlag(); + mpOutliner->GetUndoManager().Clear(); + mpOutliner->EnableUndo( true ); + + SetLanguage(SvxLanguageItem(mpDoc->GetLanguage(EE_CHAR_LANGUAGE), SID_ATTR_LANGUAGE)); + + mxTextControl->GrabFocus(); +} + +IMPL_LINK(AnnotationWindow, MenuItemSelectedHdl, const OString&, rIdent, void) +{ + SfxDispatcher* pDispatcher = mpDocShell->GetViewShell()->GetViewFrame()->GetDispatcher(); + if (!pDispatcher) + return; + + if (rIdent == ".uno:ReplyToAnnotation") + { + const SfxUnoAnyItem aItem( SID_REPLYTO_POSTIT, Any( mxAnnotation ) ); + pDispatcher->ExecuteList(SID_REPLYTO_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (rIdent == ".uno:DeleteAnnotation") + { + const SfxUnoAnyItem aItem( SID_DELETE_POSTIT, Any( mxAnnotation ) ); + pDispatcher->ExecuteList(SID_DELETE_POSTIT, SfxCallMode::ASYNCHRON, + { &aItem }); + } + else if (rIdent == ".uno:DeleteAllAnnotationByAuthor") + { + const SfxStringItem aItem( SID_DELETEALLBYAUTHOR_POSTIT, mxAnnotation->getAuthor() ); + pDispatcher->ExecuteList( SID_DELETEALLBYAUTHOR_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (rIdent == ".uno:DeleteAllAnnotation") + pDispatcher->Execute( SID_DELETEALL_POSTIT ); +} + +void AnnotationWindow::FillMenuButton() +{ + SvtUserOptions aUserOptions; + OUString sCurrentAuthor( aUserOptions.GetFullName() ); + OUString sAuthor( mxAnnotation->getAuthor() ); + + OUString aStr(mxMenuButton->get_item_label(".uno:DeleteAllAnnotationByAuthor")); + OUString aReplace( sAuthor ); + if( aReplace.isEmpty() ) + aReplace = SdResId( STR_ANNOTATION_NOAUTHOR ); + aStr = aStr.replaceFirst("%1", aReplace); + mxMenuButton->set_item_label(".uno:DeleteAllAnnotationByAuthor", aStr); + + bool bShowReply = sAuthor != sCurrentAuthor && !mbReadonly; + mxMenuButton->set_item_visible(".uno:ReplyToAnnotation", bShowReply); + mxMenuButton->set_item_visible("separator", bShowReply); + mxMenuButton->set_item_visible(".uno:DeleteAnnotation", mxAnnotation.is() && !mbReadonly); + mxMenuButton->set_item_visible(".uno:DeleteAllAnnotationByAuthor", !mbReadonly); + mxMenuButton->set_item_visible(".uno:DeleteAllAnnotation", !mbReadonly); +} + +void AnnotationWindow::StartEdit() +{ + GetOutlinerView()->SetSelection(ESelection(EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT,EE_PARA_MAX_COUNT,EE_TEXTPOS_MAX_COUNT)); + GetOutlinerView()->ShowCursor(); +} + +void AnnotationWindow::SetMapMode(const MapMode& rNewMapMode) +{ + OutputDevice& rDevice = mxTextControl->GetDrawingArea()->get_ref_device(); + rDevice.SetMapMode(rNewMapMode); +} + +void AnnotationWindow::Rescale() +{ + MapMode aMode(MapUnit::Map100thMM); + aMode.SetOrigin( Point() ); + mpOutliner->SetRefMapMode( aMode ); + SetMapMode( aMode ); + + if (mxMeta) + { + vcl::Font aFont = maLabelFont; + sal_Int32 nHeight = ::tools::Long(aFont.GetFontHeight() * aMode.GetScaleY()); + aFont.SetFontHeight( nHeight ); + mxMeta->set_font(aFont); + } +} + +void AnnotationWindow::DoResize() +{ + OutputDevice& rDevice = mxTextControl->GetDrawingArea()->get_ref_device(); + + ::tools::Long aHeight = mxContainer->get_preferred_size().Height(); + ::tools::ULong aWidth = mxContainer->get_preferred_size().Width(); + + aHeight -= POSTIT_META_HEIGHT; + + mpOutliner->SetPaperSize( rDevice.PixelToLogic( Size(aWidth, aHeight) ) ) ; + ::tools::Long aTextHeight = rDevice.LogicToPixel(mpOutliner->CalcTextSize()).Height(); + + if( aTextHeight > aHeight ) + { + const int nThickness = mxVScrollbar->get_scroll_thickness(); + if (nThickness) + { + // we need vertical scrollbars and have to reduce the width + aWidth -= nThickness; + mpOutliner->SetPaperSize(rDevice.PixelToLogic(Size(aWidth, aHeight))); + } + mxVScrollbar->set_vpolicy(VclPolicyType::ALWAYS); + } + else + { + mxVScrollbar->set_vpolicy(VclPolicyType::NEVER); + } + + ::tools::Rectangle aOutputArea = rDevice.PixelToLogic(::tools::Rectangle(0, 0, aWidth, aHeight)); + if (mxVScrollbar->get_vpolicy() == VclPolicyType::NEVER) + { + // if we do not have a scrollbar anymore, we want to see the complete text + mpOutlinerView->SetVisArea(aOutputArea); + } + mpOutlinerView->SetOutputArea(aOutputArea); + mpOutlinerView->ShowCursor(true, true); + + int nUpper = mpOutliner->GetTextHeight(); + int nCurrentDocPos = mpOutlinerView->GetVisArea().Top(); + int nStepIncrement = mpOutliner->GetTextHeight() / 10; + int nPageIncrement = rDevice.PixelToLogic(Size(0,aHeight)).Height() * 8 / 10; + int nPageSize = rDevice.PixelToLogic(Size(0,aHeight)).Height(); + + /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has + effectively... + + lower = gtk_adjustment_get_lower + upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size + + and requires that upper > lower or the deceleration animation never ends + */ + nPageSize = std::min(nPageSize, nUpper); + + mxVScrollbar->vadjustment_configure(nCurrentDocPos, 0, nUpper, + nStepIncrement, nPageIncrement, nPageSize); +} + +void AnnotationWindow::SetScrollbar() +{ + mxVScrollbar->vadjustment_set_value(mpOutlinerView->GetVisArea().Top()); +} + +void AnnotationWindow::ResizeIfNecessary(::tools::Long aOldHeight, ::tools::Long aNewHeight) +{ + if (aOldHeight != aNewHeight) + DoResize(); + else + SetScrollbar(); +} + +void AnnotationWindow::SetLanguage(const SvxLanguageItem &aNewItem) +{ + mpOutliner->SetModifyHdl( Link() ); + ESelection aOld = GetOutlinerView()->GetSelection(); + + ESelection aNewSelection( 0, 0, mpOutliner->GetParagraphCount()-1, EE_TEXTPOS_ALL ); + GetOutlinerView()->SetSelection( aNewSelection ); + SfxItemSet aEditAttr(GetOutlinerView()->GetAttribs()); + aEditAttr.Put(aNewItem); + GetOutlinerView()->SetAttribs( aEditAttr ); + + GetOutlinerView()->SetSelection(aOld); + + mxTextControl->Invalidate(); +} + +void AnnotationWindow::ToggleInsMode() +{ + if( mpOutlinerView ) + { + SfxBindings &rBnd = mpDocShell->GetViewShell()->GetViewFrame()->GetBindings(); + rBnd.Invalidate(SID_ATTR_INSERT); + rBnd.Update(SID_ATTR_INSERT); + } +} + +::tools::Long AnnotationWindow::GetPostItTextHeight() +{ + OutputDevice& rDevice = mxTextControl->GetDrawingArea()->get_ref_device(); + return mpOutliner ? rDevice.LogicToPixel(mpOutliner->CalcTextSize()).Height() : 0; +} + +IMPL_LINK(AnnotationWindow, ScrollHdl, weld::ScrolledWindow&, rScrolledWindow, void) +{ + ::tools::Long nDiff = GetOutlinerView()->GetEditView().GetVisArea().Top() - rScrolledWindow.vadjustment_get_value(); + GetOutlinerView()->Scroll( 0, nDiff ); +} + +TextApiObject* getTextApiObject( const Reference< XAnnotation >& xAnnotation ) +{ + if( xAnnotation.is() ) + { + Reference< XText > xText( xAnnotation->getTextRange() ); + return TextApiObject::getImplementation( xText ); + } + return nullptr; +} + +void AnnotationWindow::setAnnotation( const Reference< XAnnotation >& xAnnotation ) +{ + if( (xAnnotation == mxAnnotation) || !xAnnotation.is() ) + return; + + mxAnnotation = xAnnotation; + + SetColor(); + + SvtUserOptions aUserOptions; + mbProtected = aUserOptions.GetFullName() != xAnnotation->getAuthor(); + + mpOutliner->Clear(); + TextApiObject* pTextApi = getTextApiObject( mxAnnotation ); + + if( pTextApi ) + { + std::optional< OutlinerParaObject > pOPO( pTextApi->CreateText() ); + mpOutliner->SetText(*pOPO); + } + + mpOutliner->ClearModifyFlag(); + mpOutliner->GetUndoManager().Clear(); + +//TODO Invalidate(); + + OUString sMeta( xAnnotation->getAuthor() ); + OUString sDateTime( getAnnotationDateTimeString(xAnnotation) ); + + if( !sDateTime.isEmpty() ) + { + if( !sMeta.isEmpty() ) + sMeta += "\n"; + + sMeta += sDateTime; + } + mxMeta->set_label(sMeta); +} + +void AnnotationWindow::SetColor() +{ + sal_uInt16 nAuthorIdx = mpDoc->GetAnnotationAuthorIndex( mxAnnotation->getAuthor() ); + + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + const bool bHighContrast = rStyleSettings.GetHighContrastMode(); + if( bHighContrast ) + { + maColor = rStyleSettings.GetWindowColor(); + maColorDark = maColor; + maColorLight = rStyleSettings.GetWindowTextColor(); + } + else + { + maColor = AnnotationManagerImpl::GetColor( nAuthorIdx ); + maColorDark = AnnotationManagerImpl::GetColorDark( nAuthorIdx ); + maColorLight = AnnotationManagerImpl::GetColorLight( nAuthorIdx ); + } + + { + SvtAccessibilityOptions aOptions; + mpOutliner->ForceAutoColor( bHighContrast || aOptions.GetIsAutomaticFontColor() ); + } + + mxPopover->set_background(maColor); + mxMenuButton->set_background(maColor); + + mxMeta->set_font_color(bHighContrast ? maColorLight : maColorDark); + + mxVScrollbar->customize_scrollbars(maColorLight, + maColorDark, + maColor); + mxVScrollbar->set_scroll_thickness(GetPrefScrollbarWidth()); +} + +void AnnotationWindow::SaveToDocument() +{ + Reference< XAnnotation > xAnnotation( mxAnnotation ); + + // write changed text back to annotation + if (mpOutliner->IsModified()) + { + TextApiObject* pTextApi = getTextApiObject( xAnnotation ); + + if( pTextApi ) + { + std::optional pOPO = mpOutliner->CreateParaObject(); + if( pOPO ) + { + if( mpDoc->IsUndoEnabled() ) + mpDoc->BegUndo( SdResId( STR_ANNOTATION_UNDO_EDIT ) ); + + pTextApi->SetText( *pOPO ); + pOPO.reset(); + + // set current time to changed annotation + xAnnotation->setDateTime( getCurrentDateTime() ); + + if( mpDoc->IsUndoEnabled() ) + mpDoc->EndUndo(); + + mpDocShell->SetModified(); + } + + } + } + mpOutliner->ClearModifyFlag(); + + mpOutliner->GetUndoManager().Clear(); +} + +bool AnnotationTextWindow::Command(const CommandEvent& rCEvt) +{ + if (rCEvt.GetCommand() == CommandEventId::ContextMenu) + { + const bool bReadOnly = mrContents.DocShell()->IsReadOnly(); + if (bReadOnly) + return true; + + SfxDispatcher* pDispatcher = mrContents.DocShell()->GetViewShell()->GetViewFrame()->GetDispatcher(); + if( !pDispatcher ) + return true; + + if (IsMouseCaptured()) + { + // so the menu can capture it and the EditView doesn't get the button release and change its + // selection on a successful button click + ReleaseMouse(); + } + + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1)); + weld::Widget* pPopupParent = GetDrawingArea(); + std::unique_ptr xBuilder(Application::CreateBuilder(pPopupParent, "modules/simpress/ui/annotationtagmenu.ui")); + std::unique_ptr xMenu(xBuilder->weld_menu("menu")); + + auto xAnnotation = mrContents.getAnnotation(); + + SvtUserOptions aUserOptions; + OUString sCurrentAuthor( aUserOptions.GetFullName() ); + OUString sAuthor( xAnnotation->getAuthor() ); + + OUString aStr(xMenu->get_label(".uno:DeleteAllAnnotationByAuthor")); + OUString aReplace( sAuthor ); + if( aReplace.isEmpty() ) + aReplace = SdResId( STR_ANNOTATION_NOAUTHOR ); + aStr = aStr.replaceFirst("%1", aReplace); + xMenu->set_label(".uno:DeleteAllAnnotationByAuthor", aStr); + + bool bShowReply = sAuthor != sCurrentAuthor && !bReadOnly; + xMenu->set_visible(".uno:ReplyToAnnotation", bShowReply); + xMenu->set_visible("separator", bShowReply); + xMenu->set_visible(".uno:DeleteAnnotation", xAnnotation.is() && !bReadOnly); + xMenu->set_visible(".uno:DeleteAllAnnotationByAuthor", !bReadOnly); + xMenu->set_visible(".uno:DeleteAllAnnotation", !bReadOnly); + + int nInsertPos = 2; + + auto xFrame = mrContents.DocShell()->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface(); + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(xFrame)); + + bool bEditable = !mrContents.IsProtected() && !bReadOnly; + if (bEditable) + { + SfxItemSet aSet(mrContents.GetOutlinerView()->GetAttribs()); + + xMenu->insert(nInsertPos++, ".uno:Bold", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Bold", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Bold", xFrame), + TRISTATE_TRUE); + + if ( aSet.GetItemState( EE_CHAR_WEIGHT ) == SfxItemState::SET ) + { + if( aSet.Get( EE_CHAR_WEIGHT ).GetWeight() == WEIGHT_BOLD ) + xMenu->set_active(".uno:Bold", true); + } + + xMenu->insert(nInsertPos++, ".uno:Italic", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Italic", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Italic", xFrame), + TRISTATE_TRUE); + + if ( aSet.GetItemState( EE_CHAR_ITALIC ) == SfxItemState::SET ) + { + if( aSet.Get( EE_CHAR_ITALIC ).GetPosture() != ITALIC_NONE ) + xMenu->set_active(".uno:Italic", true); + + } + + xMenu->insert(nInsertPos++, ".uno:Underline", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Underline", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Underline", xFrame), + TRISTATE_TRUE); + + if ( aSet.GetItemState( EE_CHAR_UNDERLINE ) == SfxItemState::SET ) + { + if( aSet.Get( EE_CHAR_UNDERLINE ).GetLineStyle() != LINESTYLE_NONE ) + xMenu->set_active(".uno:Underline", true); + } + + xMenu->insert(nInsertPos++, ".uno:Strikeout", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Strikeout", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Strikeout", xFrame), + TRISTATE_TRUE); + + if ( aSet.GetItemState( EE_CHAR_STRIKEOUT ) == SfxItemState::SET ) + { + if( aSet.Get( EE_CHAR_STRIKEOUT ).GetStrikeout() != STRIKEOUT_NONE ) + xMenu->set_active(".uno:Strikeout", true); + } + + xMenu->insert_separator(nInsertPos++, "separator2"); + } + + xMenu->insert(nInsertPos++, ".uno:Copy", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Copy", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Copy", xFrame), + TRISTATE_INDET); + + xMenu->insert(nInsertPos++, ".uno:Paste", + vcl::CommandInfoProvider::GetMenuLabelForCommand( + vcl::CommandInfoProvider::GetCommandProperties(".uno:Paste", aModuleName)), + nullptr, nullptr, vcl::CommandInfoProvider::GetXGraphicForCommand(".uno:Paste", xFrame), + TRISTATE_INDET); + + bool bCanPaste = false; + if (bEditable) + { + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(GetClipboard())); + bCanPaste = aDataHelper.GetFormatCount() != 0; + } + + xMenu->insert_separator(nInsertPos++, "separator3"); + + xMenu->set_sensitive(".uno:Copy", mrContents.GetOutlinerView()->HasSelection()); + xMenu->set_sensitive(".uno:Paste", bCanPaste); + + auto sId = xMenu->popup_at_rect(pPopupParent, aRect); + + if (sId == ".uno:ReplyToAnnotation") + { + const SfxUnoAnyItem aItem( SID_REPLYTO_POSTIT, Any( xAnnotation ) ); + pDispatcher->ExecuteList(SID_REPLYTO_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (sId == ".uno:DeleteAnnotation") + { + const SfxUnoAnyItem aItem( SID_DELETE_POSTIT, Any( xAnnotation ) ); + pDispatcher->ExecuteList(SID_DELETE_POSTIT, SfxCallMode::ASYNCHRON, + { &aItem }); + } + else if (sId == ".uno:DeleteAllAnnotationByAuthor") + { + const SfxStringItem aItem( SID_DELETEALLBYAUTHOR_POSTIT, sAuthor ); + pDispatcher->ExecuteList( SID_DELETEALLBYAUTHOR_POSTIT, + SfxCallMode::ASYNCHRON, { &aItem }); + } + else if (sId == ".uno:DeleteAllAnnotation") + pDispatcher->Execute( SID_DELETEALL_POSTIT ); + else if (sId == ".uno:Copy") + { + mrContents.GetOutlinerView()->Copy(); + } + else if (sId == ".uno:Paste") + { + mrContents.GetOutlinerView()->PasteSpecial(); + mrContents.DoResize(); + } + else if (!sId.isEmpty()) + { + SfxItemSet aEditAttr(mrContents.GetOutlinerView()->GetAttribs()); + SfxItemSet aNewAttr(mrContents.GetOutliner()->GetEmptyItemSet()); + + if (sId == ".uno:Bold") + { + FontWeight eFW = aEditAttr.Get( EE_CHAR_WEIGHT ).GetWeight(); + aNewAttr.Put( SvxWeightItem( eFW == WEIGHT_NORMAL ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + } + else if (sId == ".uno:Italic") + { + FontItalic eFI = aEditAttr.Get( EE_CHAR_ITALIC ).GetPosture(); + aNewAttr.Put( SvxPostureItem( eFI == ITALIC_NORMAL ? ITALIC_NONE : ITALIC_NORMAL, EE_CHAR_ITALIC ) ); + } + else if (sId == ".uno:Underline") + { + FontLineStyle eFU = aEditAttr. Get( EE_CHAR_UNDERLINE ).GetLineStyle(); + aNewAttr.Put( SvxUnderlineItem( eFU == LINESTYLE_SINGLE ? LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_UNDERLINE ) ); + } + else if (sId == ".uno:Strikeout") + { + FontStrikeout eFSO = aEditAttr.Get( EE_CHAR_STRIKEOUT ).GetStrikeout(); + aNewAttr.Put( SvxCrossedOutItem( eFSO == STRIKEOUT_SINGLE ? STRIKEOUT_NONE : STRIKEOUT_SINGLE, EE_CHAR_STRIKEOUT ) ); + } + + mrContents.GetOutlinerView()->SetAttribs( aNewAttr ); + } + + return true; + } + return WeldEditView::Command(rCEvt); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/annotations/annotationwindow.hxx b/sd/source/ui/annotations/annotationwindow.hxx new file mode 100644 index 000000000..558cc6165 --- /dev/null +++ b/sd/source/ui/annotations/annotationwindow.hxx @@ -0,0 +1,143 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::office { class XAnnotation; } + +class OutlinerView; +class Outliner; +class SvxLanguageItem; +class SdDrawDocument; + +namespace sd { + +class AnnotationManagerImpl; +class DrawDocShell; +class TextApiObject; + +class AnnotationWindow; + +class AnnotationTextWindow : public WeldEditView +{ +private: + AnnotationWindow& mrContents; + +public: + AnnotationTextWindow(AnnotationWindow& rContents); + + virtual EditView* GetEditView() const override; + + virtual EditEngine* GetEditEngine() const override; + + virtual void EditViewScrollStateChange() override; + + void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + virtual bool KeyInput(const KeyEvent& rKeyEvt) override; + virtual bool Command(const CommandEvent& rCEvt) override; +}; + +class AnnotationWindow final +{ +private: + std::unique_ptr mxBuilder; + std::unique_ptr mxPopover; + std::unique_ptr mxContainer; + + DrawDocShell* mpDocShell; + SdDrawDocument* mpDoc; + + bool mbReadonly; + bool mbProtected; + + css::uno::Reference< css::office::XAnnotation > mxAnnotation; + +public: + Color maColor; + Color maColorDark; + Color maColorLight; + +private: + vcl::Font maLabelFont; + + std::unique_ptr mpOutlinerView; + std::unique_ptr<::Outliner> mpOutliner; + + std::unique_ptr mxVScrollbar; + std::unique_ptr mxTextControl; + std::unique_ptr mxTextControlWin; + std::unique_ptr mxMeta; + std::unique_ptr mxMenuButton; + + DECL_LINK(ScrollHdl, weld::ScrolledWindow&, void); + DECL_LINK(MenuItemSelectedHdl, const OString&, void); + + void FillMenuButton(); + void InitControls(); + + void SetMapMode(const MapMode& rNewMapMode); + void setAnnotation(const css::uno::Reference& xAnnotation); + + static sal_Int32 GetPrefScrollbarWidth() { return 16; } +public: + AnnotationWindow(weld::Window* pParent, const ::tools::Rectangle& rRect, DrawDocShell* pDocShell, + const css::uno::Reference& xAnnotation); + + void connect_closed(const Link& rLink) { mxPopover->connect_closed(rLink); } + + void DoResize(); + void ResizeIfNecessary(::tools::Long aOldHeight, ::tools::Long aNewHeight); + void SetScrollbar(); + void StartEdit(); + + const css::uno::Reference& getAnnotation() const { return mxAnnotation; } + + void SaveToDocument(); + + ::tools::Long GetPostItTextHeight(); + + DrawDocShell* DocShell() { return mpDocShell; } + + void SetLanguage(const SvxLanguageItem &aNewItem); + + void Rescale(); + + void ToggleInsMode(); + + bool IsProtected() const { return mbProtected; } + + OutlinerView* GetOutlinerView() { return mpOutlinerView.get();} + ::Outliner* GetOutliner() { return mpOutliner.get();} + ~AnnotationWindow(); + + void SetColor(); +}; + +TextApiObject* getTextApiObject( const css::uno::Reference< css::office::XAnnotation >& xAnnotation ); + + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/optsitem.cxx b/sd/source/ui/app/optsitem.cxx new file mode 100644 index 000000000..5baff32e2 --- /dev/null +++ b/sd/source/ui/app/optsitem.cxx @@ -0,0 +1,1407 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::utl; +using namespace ::com::sun::star::uno; + +template< class T > static T getSafeValue( const Any& rAny ) +{ + T value = T(); + bool bOk = (rAny >>= value); + + DBG_ASSERT( bOk, "SdOptionsItem, wrong type from configuration!" ); + + return value; +} + + +SdOptionsItem::SdOptionsItem( const SdOptionsGeneric& rParent, const OUString& rSubTree ) : + ConfigItem ( rSubTree ), + mrParent ( rParent ) +{ +} + +SdOptionsItem::~SdOptionsItem() +{ +} + +void SdOptionsItem::ImplCommit() +{ + if( IsModified() ) + mrParent.Commit( *this ); +}; + +void SdOptionsItem::Notify( const css::uno::Sequence& ) +{} + +Sequence< Any > SdOptionsItem::GetProperties( const Sequence< OUString >& rNames ) +{ + return ConfigItem::GetProperties( rNames ); +} + +bool SdOptionsItem::PutProperties( const Sequence< OUString >& rNames, const Sequence< Any>& rValues ) +{ + return ConfigItem::PutProperties( rNames, rValues ); +} + +SdOptionsGeneric::SdOptionsGeneric(bool bImpress, const OUString& rSubTree) + : maSubTree(rSubTree) + , mbImpress(bImpress) + , mbInit(rSubTree.isEmpty()) + , mbEnableModify(false) +{ +} + +SdOptionsGeneric::SdOptionsGeneric(SdOptionsGeneric const & rSource) +{ + operator=(rSource); +} + +SdOptionsGeneric& SdOptionsGeneric::operator=(SdOptionsGeneric const & rSource) +{ + if (this != &rSource) + { + maSubTree = rSource.maSubTree; + mpCfgItem.reset(rSource.mpCfgItem ? new SdOptionsItem(*rSource.mpCfgItem) : nullptr ); + mbImpress = rSource.mbImpress; + mbInit = rSource.mbInit; + mbEnableModify = rSource.mbEnableModify; + } + return *this; +} + +void SdOptionsGeneric::Init() const +{ + if( mbInit ) + return; + + SdOptionsGeneric* pThis = const_cast(this); + + if( !mpCfgItem ) + pThis->mpCfgItem.reset( new SdOptionsItem( *this, maSubTree ) ); + assert(mpCfgItem && "mpCfgItem is set by now"); + + const Sequence< OUString > aNames( GetPropertyNames() ); + const Sequence< Any > aValues = mpCfgItem->GetProperties( aNames ); + + if( aNames.hasElements() && ( aValues.getLength() == aNames.getLength() ) ) + { + const Any* pValues = aValues.getConstArray(); + + pThis->EnableModify( false ); + pThis->mbInit = pThis->ReadData( pValues ); + pThis->EnableModify( true ); + } + else + pThis->mbInit = true; +} + +SdOptionsGeneric::~SdOptionsGeneric() +{ +} + +void SdOptionsGeneric::Commit( SdOptionsItem& rCfgItem ) const +{ + const Sequence< OUString > aNames( GetPropertyNames() ); + Sequence< Any > aValues( aNames.getLength() ); + + if( aNames.hasElements() ) + { + if( WriteData( aValues.getArray() ) ) + rCfgItem.PutProperties( aNames, aValues ); + else + { + OSL_FAIL( "PutProperties failed" ); + } + } +} + +Sequence< OUString > SdOptionsGeneric::GetPropertyNames() const +{ + sal_uLong nCount; + const char** ppPropNames; + + GetPropNameArray( ppPropNames, nCount ); + + Sequence< OUString > aNames( nCount ); + OUString* pNames = aNames.getArray(); + + for( sal_uLong i = 0; i < nCount; i++ ) + pNames[ i ] = OUString::createFromAscii( ppPropNames[ i ] ); + + return aNames; +} + +void SdOptionsGeneric::Store() +{ + if( mpCfgItem ) + mpCfgItem->Commit(); +} + +bool SdOptionsGeneric::isMetricSystem() +{ + SvtSysLocale aSysLocale; + MeasurementSystem eSys = aSysLocale.GetLocaleData().getMeasurementSystemEnum(); + + return ( eSys == MeasurementSystem::Metric ); +} + +/************************************************************************* +|* +|* SdOptionsLayout +|* +\************************************************************************/ + +SdOptionsLayout::SdOptionsLayout(bool bImpress, bool bUseConfig) : + SdOptionsGeneric( bImpress, bUseConfig ? + ( bImpress ? + OUString( "Office.Impress/Layout" ) : + OUString( "Office.Draw/Layout" ) ) : + OUString() ), + bRuler( true ), + bMoveOutline( true ), + bDragStripes( false ), + bHandlesBezier( false ), + bHelplines( true ), + nMetric(static_cast(isMetricSystem() ? FieldUnit::CM : FieldUnit::INCH)), + nDefTab( 1250 ) +{ + EnableModify( true ); +} + +bool SdOptionsLayout::operator==( const SdOptionsLayout& rOpt ) const +{ + return( IsRulerVisible() == rOpt.IsRulerVisible() && + IsMoveOutline() == rOpt.IsMoveOutline() && + IsDragStripes() == rOpt.IsDragStripes() && + IsHandlesBezier() == rOpt.IsHandlesBezier() && + IsHelplines() == rOpt.IsHelplines() && + GetMetric() == rOpt.GetMetric() && + GetDefTab() == rOpt.GetDefTab() ); +} + +void SdOptionsLayout::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + if( isMetricSystem() ) + { + static const char* aPropNamesMetric[] = + { + "Display/Ruler", + "Display/Bezier", + "Display/Contour", + "Display/Guide", + "Display/Helpline", + "Other/MeasureUnit/Metric", + "Other/TabStop/Metric" + }; + ppNames = aPropNamesMetric; + rCount = SAL_N_ELEMENTS(aPropNamesMetric); + } + else + { + static const char* aPropNamesNonMetric[] = + { + "Display/Ruler", + "Display/Bezier", + "Display/Contour", + "Display/Guide", + "Display/Helpline", + "Other/MeasureUnit/NonMetric", + "Other/TabStop/NonMetric" + }; + ppNames = aPropNamesNonMetric; + rCount = SAL_N_ELEMENTS(aPropNamesNonMetric); + } +} + +bool SdOptionsLayout::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetRulerVisible( *o3tl::doAccess(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetHandlesBezier( *o3tl::doAccess(pValues[ 1 ]) ); + if( pValues[2].hasValue() ) SetMoveOutline( *o3tl::doAccess(pValues[ 2 ]) ); + if( pValues[3].hasValue() ) SetDragStripes( *o3tl::doAccess(pValues[ 3 ]) ); + if( pValues[4].hasValue() ) SetHelplines( *o3tl::doAccess(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetMetric( static_cast(*o3tl::doAccess(pValues[ 5 ])) ); + if( pValues[6].hasValue() ) SetDefTab( static_cast(*o3tl::doAccess(pValues[ 6 ])) ); + + return true; +} + +bool SdOptionsLayout::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= IsRulerVisible(); + pValues[ 1 ] <<= IsHandlesBezier(); + pValues[ 2 ] <<= IsMoveOutline(); + pValues[ 3 ] <<= IsDragStripes(); + pValues[ 4 ] <<= IsHelplines(); + pValues[ 5 ] <<= static_cast(GetMetric()); + pValues[ 6 ] <<= static_cast(GetDefTab()); + + return true; +} + +/************************************************************************* +|* +|* SdOptionsLayoutItem +|* +\************************************************************************/ + +SdOptionsLayoutItem::SdOptionsLayoutItem() +: SfxPoolItem ( ATTR_OPTIONS_LAYOUT ) +, maOptionsLayout ( false, false ) +{ +} + +SdOptionsLayoutItem::SdOptionsLayoutItem( SdOptions const * pOpts, ::sd::FrameView const * pView ) +: SfxPoolItem ( ATTR_OPTIONS_LAYOUT ) +, maOptionsLayout ( false, false ) +{ + if( pOpts ) + { + maOptionsLayout.SetMetric( pOpts->GetMetric() ); + maOptionsLayout.SetDefTab( pOpts->GetDefTab() ); + } + + if( pView ) + { + maOptionsLayout.SetRulerVisible( pView->HasRuler() ); + maOptionsLayout.SetMoveOutline( !pView->IsNoDragXorPolys() ); + maOptionsLayout.SetDragStripes( pView->IsDragStripes() ); + maOptionsLayout.SetHandlesBezier( pView->IsPlusHandlesAlwaysVisible() ); + maOptionsLayout.SetHelplines( pView->IsHlplVisible() ); + } + else if( pOpts ) + { + maOptionsLayout.SetRulerVisible( pOpts->IsRulerVisible() ); + maOptionsLayout.SetMoveOutline( pOpts->IsMoveOutline() ); + maOptionsLayout.SetDragStripes( pOpts->IsDragStripes() ); + maOptionsLayout.SetHandlesBezier( pOpts->IsHandlesBezier() ); + maOptionsLayout.SetHelplines( pOpts->IsHelplines() ); + } +} + +SdOptionsLayoutItem* SdOptionsLayoutItem::Clone( SfxItemPool* ) const +{ + return new SdOptionsLayoutItem( *this ); +} + +bool SdOptionsLayoutItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return maOptionsLayout == static_cast(rAttr).maOptionsLayout; +} + +void SdOptionsLayoutItem::SetOptions( SdOptions* pOpts ) const +{ + if( pOpts ) + { + pOpts->SetRulerVisible( maOptionsLayout.IsRulerVisible() ); + pOpts->SetMoveOutline( maOptionsLayout.IsMoveOutline() ); + pOpts->SetDragStripes( maOptionsLayout.IsDragStripes() ); + pOpts->SetHandlesBezier( maOptionsLayout.IsHandlesBezier() ); + pOpts->SetHelplines( maOptionsLayout.IsHelplines() ); + pOpts->SetMetric( maOptionsLayout.GetMetric() ); + pOpts->SetDefTab( maOptionsLayout.GetDefTab() ); + } +} + +/************************************************************************* +|* +|* SdOptionsContents +|* +\************************************************************************/ + +SdOptionsContents::SdOptionsContents(bool bImpress) : + SdOptionsGeneric( bImpress, bImpress ? + OUString( "Office.Impress/Content" ) : + OUString( "Office.Draw/Content" ) ) +{ + EnableModify( true ); +} + +bool SdOptionsContents::operator==(const SdOptionsContents&) const +{ + return true; +} + +void SdOptionsContents::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + static const char* aPropNames[] = + { + "Display/PicturePlaceholder", + "Display/ContourMode", + "Display/LineContour", + "Display/TextPlaceholder" + }; + + rCount = SAL_N_ELEMENTS(aPropNames); + ppNames = aPropNames; +} + +bool SdOptionsContents::ReadData(const Any*) +{ + return true; +} + +bool SdOptionsContents::WriteData( Any* pValues ) const +{ + //#i80528# no draft anymore + pValues[ 0 ] <<= false; + pValues[ 1 ] <<= false; + pValues[ 2 ] <<= false; + pValues[ 3 ] <<= false; + + return true; +} +/************************************************************************* +|* +|* SdOptionsMisc +|* +\************************************************************************/ + +SdOptionsMisc::SdOptionsMisc( bool bImpress, bool bUseConfig ) : + SdOptionsGeneric( bImpress, bUseConfig ? + ( bImpress ? + OUString( "Office.Impress/Misc" ) : + OUString( "Office.Draw/Misc" ) ) : + OUString() ), + nDefaultObjectSizeWidth(8000), + nDefaultObjectSizeHeight(5000), + bStartWithTemplate( false ), + bMarkedHitMovesAlways( true ), + bMoveOnlyDragging( false ), + bCrookNoContortion( false ), + bQuickEdit( IsImpress() ), + bMasterPageCache( true ), + bDragWithCopy( false ), + bPickThrough( true ), + bDoubleClickTextEdit( true ), + bClickChangeRotation( false ), + bEnableSdremote( false ), + bEnablePresenterScreen( true), + bSolidDragging( true ), + bSummationOfParagraphs( false ), + bTabBarVisible( true ), + bShowUndoDeleteWarning( true ), + bSlideshowRespectZOrder( true ), + bShowComments( true ), + bPreviewNewEffects( true ), + bPreviewChangedEffects( false ), + bPreviewTransitions( true ), + mnDisplay( 0 ), + mnPenColor( 0xff0000 ), + mnPenWidth( 150.0 ), + + // The default for 6.1-and-above documents is to use printer-independent + // formatting. + mnPrinterIndependentLayout (1) +{ + EnableModify( true ); +} + +bool SdOptionsMisc::operator==( const SdOptionsMisc& rOpt ) const +{ + return( IsStartWithTemplate() == rOpt.IsStartWithTemplate() && + IsMarkedHitMovesAlways() == rOpt.IsMarkedHitMovesAlways() && + IsMoveOnlyDragging() == rOpt.IsMoveOnlyDragging() && + IsCrookNoContortion() == rOpt.IsCrookNoContortion() && + IsQuickEdit() == rOpt.IsQuickEdit() && + IsMasterPagePaintCaching() == rOpt.IsMasterPagePaintCaching() && + IsDragWithCopy() == rOpt.IsDragWithCopy() && + IsPickThrough() == rOpt.IsPickThrough() && + IsDoubleClickTextEdit() == rOpt.IsDoubleClickTextEdit() && + IsClickChangeRotation() == rOpt.IsClickChangeRotation() && + IsEnableSdremote() == rOpt.IsEnableSdremote() && + IsEnablePresenterScreen() == rOpt.IsEnablePresenterScreen()&& + IsSummationOfParagraphs() == rOpt.IsSummationOfParagraphs() && + IsTabBarVisible() == rOpt.IsTabBarVisible() && + IsSolidDragging() == rOpt.IsSolidDragging() && + IsShowUndoDeleteWarning() == rOpt.IsShowUndoDeleteWarning() && + IsSlideshowRespectZOrder() == rOpt.IsSlideshowRespectZOrder() && + GetPrinterIndependentLayout() == rOpt.GetPrinterIndependentLayout() && + GetDefaultObjectSizeWidth() == rOpt.GetDefaultObjectSizeWidth() && + GetDefaultObjectSizeHeight() == rOpt.GetDefaultObjectSizeHeight() && + + IsPreviewNewEffects() == rOpt.IsPreviewNewEffects() && + IsPreviewChangedEffects() == rOpt.IsPreviewChangedEffects() && + IsPreviewTransitions() == rOpt.IsPreviewTransitions() && + GetDisplay() == rOpt.GetDisplay() && + IsShowComments() == rOpt.IsShowComments() && + GetPresentationPenColor() == rOpt.GetPresentationPenColor() && + GetPresentationPenWidth() == rOpt.GetPresentationPenWidth() + ); +} + +void SdOptionsMisc::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + static const char* aPropNames[] = + { + "ObjectMoveable", + "NoDistort", + "TextObject/QuickEditing", + "BackgroundCache", + "CopyWhileMoving", + "TextObject/Selectable", + "DclickTextedit", + "RotateClick", + "Preview", + "ModifyWithAttributes", + "DefaultObjectSize/Width", + "DefaultObjectSize/Height", + + "Compatibility/PrinterIndependentLayout", + + "ShowComments", + + // just for impress + "NewDoc/AutoPilot", + "Compatibility/AddBetween", + "ShowUndoDeleteWarning", + "SlideshowRespectZOrder", + + "PreviewNewEffects", + "PreviewChangedEffects", + "PreviewTransitions", + + "Display", + + "PenColor", + "PenWidth", + "Start/EnableSdremote", + "Start/EnablePresenterScreen", + "TabBarVisible" + }; + + rCount = ( IsImpress() ? SAL_N_ELEMENTS(aPropNames) : 14 ); + ppNames = aPropNames; +} + +bool SdOptionsMisc::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetMarkedHitMovesAlways( *o3tl::doAccess(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetCrookNoContortion( *o3tl::doAccess(pValues[ 1 ]) ); + if( pValues[2].hasValue() ) SetQuickEdit( *o3tl::doAccess(pValues[ 2 ]) ); + if( pValues[3].hasValue() ) SetMasterPagePaintCaching( *o3tl::doAccess(pValues[ 3 ]) ); + if( pValues[4].hasValue() ) SetDragWithCopy( *o3tl::doAccess(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetPickThrough( *o3tl::doAccess(pValues[ 5 ]) ); + if( pValues[6].hasValue() ) SetDoubleClickTextEdit( *o3tl::doAccess(pValues[ 6 ]) ); + if( pValues[7].hasValue() ) SetClickChangeRotation( *o3tl::doAccess(pValues[ 7 ]) ); + if( pValues[9].hasValue() ) SetSolidDragging( *o3tl::doAccess(pValues[ 9 ]) ); + if( pValues[10].hasValue() ) SetDefaultObjectSizeWidth( *o3tl::doAccess(pValues[ 10 ]) ); + if( pValues[11].hasValue() ) SetDefaultObjectSizeHeight( *o3tl::doAccess(pValues[ 11 ]) ); + if( pValues[12].hasValue() ) SetPrinterIndependentLayout( *o3tl::doAccess(pValues[ 12 ]) ); + + if( pValues[13].hasValue() ) + SetShowComments( *o3tl::doAccess(pValues[ 13 ]) ); + + // just for Impress + if (IsImpress()) + { + if( pValues[14].hasValue() ) + SetStartWithTemplate( *o3tl::doAccess(pValues[ 14 ]) ); + if( pValues[15].hasValue() ) + SetSummationOfParagraphs( *o3tl::doAccess(pValues[ 15 ]) ); + if( pValues[16].hasValue() ) + SetShowUndoDeleteWarning( *o3tl::doAccess(pValues[ 16 ]) ); + + if( pValues[17].hasValue() ) + SetSlideshowRespectZOrder(*o3tl::doAccess(pValues[ 17 ])); + + if( pValues[18].hasValue() ) + SetPreviewNewEffects(*o3tl::doAccess(pValues[ 18 ])); + + if( pValues[19].hasValue() ) + SetPreviewChangedEffects(*o3tl::doAccess(pValues[ 19 ])); + + if( pValues[20].hasValue() ) + SetPreviewTransitions(*o3tl::doAccess(pValues[ 20 ])); + + if( pValues[21].hasValue() ) + SetDisplay(*o3tl::doAccess(pValues[ 21 ])); + + if( pValues[22].hasValue() ) + SetPresentationPenColor( getSafeValue< sal_Int32 >( pValues[ 22 ] ) ); + + if( pValues[23].hasValue() ) + SetPresentationPenWidth( getSafeValue< double >( pValues[ 23 ] ) ); + + if( pValues[24].hasValue() ) + SetEnableSdremote( *o3tl::doAccess(pValues[ 24 ]) ); + + if( pValues[25].hasValue() ) + SetEnablePresenterScreen( *o3tl::doAccess(pValues[ 25 ]) ); + + if( pValues[26].hasValue() ) { + SetTabBarVisible( *o3tl::doAccess(pValues[ 26 ]) ); + } + } + + return true; +} + +bool SdOptionsMisc::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= IsMarkedHitMovesAlways(); + pValues[ 1 ] <<= IsCrookNoContortion(); + pValues[ 2 ] <<= IsQuickEdit(); + pValues[ 3 ] <<= IsMasterPagePaintCaching(); + pValues[ 4 ] <<= IsDragWithCopy(); + pValues[ 5 ] <<= IsPickThrough(); + pValues[ 6 ] <<= IsDoubleClickTextEdit(); + pValues[ 7 ] <<= IsClickChangeRotation(); + // The preview is not supported anymore. Use a dummy value. + pValues[ 8 ] <<= double(0);// GetPreviewQuality(); + pValues[ 9 ] <<= IsSolidDragging(); + pValues[ 10 ] <<= GetDefaultObjectSizeWidth(); + pValues[ 11 ] <<= GetDefaultObjectSizeHeight(); + pValues[ 12 ] <<= GetPrinterIndependentLayout(); + pValues[ 13 ] <<= IsShowComments(); + + // just for Impress + if (IsImpress()) + { + pValues[ 14 ] <<= IsStartWithTemplate(); + pValues[ 15 ] <<= IsSummationOfParagraphs(); + pValues[ 16 ] <<= IsShowUndoDeleteWarning(); + pValues[ 17 ] <<= IsSlideshowRespectZOrder(); + + pValues[ 18 ] <<= IsPreviewNewEffects(); + pValues[ 19 ] <<= IsPreviewChangedEffects(); + pValues[ 20 ] <<= IsPreviewTransitions(); + + pValues[ 21 ] <<= GetDisplay(); + + pValues[ 22 ] <<= GetPresentationPenColor(); + pValues[ 23 ] <<= GetPresentationPenWidth(); + pValues[ 24 ] <<= IsEnableSdremote(); + pValues[ 25 ] <<= IsEnablePresenterScreen(); + pValues[ 26 ] <<= IsTabBarVisible(); + } + + return true; +} + +/************************************************************************* +|* +|* SdOptionsMiscItem +|* +\************************************************************************/ + +SdOptionsMiscItem::SdOptionsMiscItem() +: SfxPoolItem ( ATTR_OPTIONS_MISC ) +, maOptionsMisc ( false, false ) +{ +} + +SdOptionsMiscItem::SdOptionsMiscItem( SdOptions const * pOpts, ::sd::FrameView const * pView ) +: SfxPoolItem ( ATTR_OPTIONS_MISC ) +, maOptionsMisc ( false, false ) +{ + if( pOpts ) + { + maOptionsMisc.SetStartWithTemplate( pOpts->IsStartWithTemplate() ); + maOptionsMisc.SetEnableSdremote( pOpts->IsEnableSdremote() ); + maOptionsMisc.SetEnablePresenterScreen( pOpts->IsEnablePresenterScreen() ); + maOptionsMisc.SetSummationOfParagraphs( pOpts->IsSummationOfParagraphs() ); + maOptionsMisc.SetTabBarVisible( pOpts->IsTabBarVisible() ); + maOptionsMisc.SetShowUndoDeleteWarning( pOpts->IsShowUndoDeleteWarning() ); + maOptionsMisc.SetPrinterIndependentLayout( pOpts->GetPrinterIndependentLayout() ); + maOptionsMisc.SetDefaultObjectSizeWidth( pOpts->GetDefaultObjectSizeWidth() ); + maOptionsMisc.SetDefaultObjectSizeHeight( pOpts->GetDefaultObjectSizeHeight() ); + + maOptionsMisc.SetPreviewNewEffects(pOpts->IsPreviewNewEffects()); + maOptionsMisc.SetPreviewChangedEffects(pOpts->IsPreviewChangedEffects()); + maOptionsMisc.SetPreviewTransitions(pOpts->IsPreviewTransitions()); + + maOptionsMisc.SetDisplay(pOpts->GetDisplay()); + maOptionsMisc.SetShowComments( pOpts->IsShowComments() ); + + maOptionsMisc.SetPresentationPenColor(pOpts->GetPresentationPenColor() ); + maOptionsMisc.SetPresentationPenWidth(pOpts->GetPresentationPenWidth() ); + } + + if( pView ) + { + maOptionsMisc.SetMarkedHitMovesAlways( pView->IsMarkedHitMovesAlways() ); + maOptionsMisc.SetMoveOnlyDragging( pView->IsMoveOnlyDragging() ); + maOptionsMisc.SetCrookNoContortion( pView->IsCrookNoContortion() ); + maOptionsMisc.SetQuickEdit( pView->IsQuickEdit() ); + + // #i26631# + maOptionsMisc.SetMasterPagePaintCaching( pView->IsMasterPagePaintCaching() ); + + maOptionsMisc.SetDragWithCopy( pView->IsDragWithCopy() ); + maOptionsMisc.SetPickThrough( pView->GetModel()->IsPickThroughTransparentTextFrames() ); + maOptionsMisc.SetDoubleClickTextEdit( pView->IsDoubleClickTextEdit() ); + maOptionsMisc.SetClickChangeRotation( pView->IsClickChangeRotation() ); + maOptionsMisc.SetSolidDragging( pView->IsSolidDragging() ); + } + else if( pOpts ) + { + maOptionsMisc.SetMarkedHitMovesAlways( pOpts->IsMarkedHitMovesAlways() ); + maOptionsMisc.SetMoveOnlyDragging( pOpts->IsMoveOnlyDragging() ); + maOptionsMisc.SetCrookNoContortion( pOpts->IsCrookNoContortion() ); + maOptionsMisc.SetQuickEdit( pOpts->IsQuickEdit() ); + maOptionsMisc.SetMasterPagePaintCaching( pOpts->IsMasterPagePaintCaching() ); + maOptionsMisc.SetDragWithCopy( pOpts->IsDragWithCopy() ); + maOptionsMisc.SetPickThrough( pOpts->IsPickThrough() ); + maOptionsMisc.SetDoubleClickTextEdit( pOpts->IsDoubleClickTextEdit() ); + maOptionsMisc.SetClickChangeRotation( pOpts->IsClickChangeRotation() ); + maOptionsMisc.SetSolidDragging( pOpts->IsSolidDragging() ); + } +} + +SdOptionsMiscItem* SdOptionsMiscItem::Clone( SfxItemPool* ) const +{ + return new SdOptionsMiscItem( *this ); +} + +bool SdOptionsMiscItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return maOptionsMisc == static_cast(rAttr).maOptionsMisc; +} + +void SdOptionsMiscItem::SetOptions( SdOptions* pOpts ) const +{ + if( !pOpts ) + return; + + pOpts->SetStartWithTemplate( maOptionsMisc.IsStartWithTemplate() ); + pOpts->SetMarkedHitMovesAlways( maOptionsMisc.IsMarkedHitMovesAlways() ); + pOpts->SetMoveOnlyDragging( maOptionsMisc.IsMoveOnlyDragging() ); + pOpts->SetCrookNoContortion( maOptionsMisc.IsCrookNoContortion() ); + pOpts->SetQuickEdit( maOptionsMisc.IsQuickEdit() ); + pOpts->SetMasterPagePaintCaching( maOptionsMisc.IsMasterPagePaintCaching() ); + pOpts->SetDragWithCopy( maOptionsMisc.IsDragWithCopy() ); + pOpts->SetPickThrough( maOptionsMisc.IsPickThrough() ); + pOpts->SetDoubleClickTextEdit( maOptionsMisc.IsDoubleClickTextEdit() ); + pOpts->SetClickChangeRotation( maOptionsMisc.IsClickChangeRotation() ); + pOpts->SetEnableSdremote( maOptionsMisc.IsEnableSdremote() ); + pOpts->SetEnablePresenterScreen( maOptionsMisc.IsEnablePresenterScreen() ); + pOpts->SetSummationOfParagraphs( maOptionsMisc.IsSummationOfParagraphs() ); + pOpts->SetTabBarVisible( maOptionsMisc.IsTabBarVisible() ); + + pOpts->SetSolidDragging( maOptionsMisc.IsSolidDragging() ); + pOpts->SetShowUndoDeleteWarning( maOptionsMisc.IsShowUndoDeleteWarning() ); + pOpts->SetPrinterIndependentLayout( maOptionsMisc.GetPrinterIndependentLayout() ); + pOpts->SetShowComments( maOptionsMisc.IsShowComments() ); + pOpts->SetDefaultObjectSizeWidth( maOptionsMisc.GetDefaultObjectSizeWidth() ); + pOpts->SetDefaultObjectSizeHeight( maOptionsMisc.GetDefaultObjectSizeHeight() ); + + pOpts->SetPreviewNewEffects( maOptionsMisc.IsPreviewNewEffects() ); + pOpts->SetPreviewChangedEffects( maOptionsMisc.IsPreviewChangedEffects() ); + pOpts->SetPreviewTransitions( maOptionsMisc.IsPreviewTransitions() ); + + pOpts->SetDisplay( maOptionsMisc.GetDisplay() ); + + pOpts->SetPresentationPenColor( maOptionsMisc.GetPresentationPenColor() ); + pOpts->SetPresentationPenWidth( maOptionsMisc.GetPresentationPenWidth() ); +} + +/************************************************************************* +|* +|* SdOptionsSnap +|* +\************************************************************************/ + +SdOptionsSnap::SdOptionsSnap( bool bImpress, bool bUseConfig ) : + SdOptionsGeneric( bImpress, bUseConfig ? + ( bImpress ? + OUString( "Office.Impress/Snap" ) : + OUString( "Office.Draw/Snap" ) ) : + OUString() ), + bSnapHelplines( true ), + bSnapBorder( true ), + bSnapFrame( false ), + bSnapPoints( false ), + bOrtho( false ), + bBigOrtho( true ), + bRotate( false ), + nSnapArea( 5 ), + nAngle( 1500 ), + nBezAngle( 1500 ) + +{ + EnableModify( true ); +} + +bool SdOptionsSnap::operator==( const SdOptionsSnap& rOpt ) const +{ + return( IsSnapHelplines() == rOpt.IsSnapHelplines() && + IsSnapBorder() == rOpt.IsSnapBorder() && + IsSnapFrame() == rOpt.IsSnapFrame() && + IsSnapPoints() == rOpt.IsSnapPoints() && + IsOrtho() == rOpt.IsOrtho() && + IsBigOrtho() == rOpt.IsBigOrtho() && + IsRotate() == rOpt.IsRotate() && + GetSnapArea() == rOpt.GetSnapArea() && + GetAngle() == rOpt.GetAngle() && + GetEliminatePolyPointLimitAngle() == rOpt.GetEliminatePolyPointLimitAngle() ); +} + +void SdOptionsSnap::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + static const char* aPropNames[] = + { + "Object/SnapLine", + "Object/PageMargin", + "Object/ObjectFrame", + "Object/ObjectPoint", + "Position/CreatingMoving", + "Position/ExtendEdges", + "Position/Rotating", + "Object/Range", + "Position/RotatingValue", + "Position/PointReduction" + }; + + rCount = SAL_N_ELEMENTS(aPropNames); + ppNames = aPropNames; +} + +bool SdOptionsSnap::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetSnapHelplines( *o3tl::doAccess(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetSnapBorder( *o3tl::doAccess(pValues[ 1 ]) ); + if( pValues[2].hasValue() ) SetSnapFrame( *o3tl::doAccess(pValues[ 2 ]) ); + if( pValues[3].hasValue() ) SetSnapPoints( *o3tl::doAccess(pValues[ 3 ]) ); + if( pValues[4].hasValue() ) SetOrtho( *o3tl::doAccess(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetBigOrtho( *o3tl::doAccess(pValues[ 5 ]) ); + if( pValues[6].hasValue() ) SetRotate( *o3tl::doAccess(pValues[ 6 ]) ); + if( pValues[7].hasValue() ) SetSnapArea( static_cast(*o3tl::doAccess(pValues[ 7 ])) ); + if( pValues[8].hasValue() ) SetAngle( Degree100(*o3tl::doAccess(pValues[ 8 ])) ); + if( pValues[9].hasValue() ) SetEliminatePolyPointLimitAngle( Degree100(*o3tl::doAccess(pValues[ 9 ])) ); + + return true; +} + +bool SdOptionsSnap::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= IsSnapHelplines(); + pValues[ 1 ] <<= IsSnapBorder(); + pValues[ 2 ] <<= IsSnapFrame(); + pValues[ 3 ] <<= IsSnapPoints(); + pValues[ 4 ] <<= IsOrtho(); + pValues[ 5 ] <<= IsBigOrtho(); + pValues[ 6 ] <<= IsRotate(); + pValues[ 7 ] <<= static_cast(GetSnapArea()); + pValues[ 8 ] <<= static_cast(GetAngle().get()); + pValues[ 9 ] <<= static_cast(GetEliminatePolyPointLimitAngle().get()); + + return true; +} + +/************************************************************************* +|* +|* SdOptionsSnapItem +|* +\************************************************************************/ + +SdOptionsSnapItem::SdOptionsSnapItem() +: SfxPoolItem ( ATTR_OPTIONS_SNAP ) +, maOptionsSnap ( false, false ) +{ +} + +SdOptionsSnapItem::SdOptionsSnapItem( SdOptions const * pOpts, ::sd::FrameView const * pView ) +: SfxPoolItem ( ATTR_OPTIONS_SNAP ) +, maOptionsSnap ( false, false ) +{ + if( pView ) + { + maOptionsSnap.SetSnapHelplines( pView->IsHlplSnap() ); + maOptionsSnap.SetSnapBorder( pView->IsBordSnap() ); + maOptionsSnap.SetSnapFrame( pView->IsOFrmSnap() ); + maOptionsSnap.SetSnapPoints( pView->IsOPntSnap() ); + maOptionsSnap.SetOrtho( pView->IsOrtho() ); + maOptionsSnap.SetBigOrtho( pView->IsBigOrtho() ); + maOptionsSnap.SetRotate( pView->IsAngleSnapEnabled() ); + maOptionsSnap.SetSnapArea( pView->GetSnapMagneticPixel() ); + maOptionsSnap.SetAngle( pView->GetSnapAngle() ); + maOptionsSnap.SetEliminatePolyPointLimitAngle( pView->GetEliminatePolyPointLimitAngle() ); + } + else if( pOpts ) + { + maOptionsSnap.SetSnapHelplines( pOpts->IsSnapHelplines() ); + maOptionsSnap.SetSnapBorder( pOpts->IsSnapBorder() ); + maOptionsSnap.SetSnapFrame( pOpts->IsSnapFrame() ); + maOptionsSnap.SetSnapPoints( pOpts->IsSnapPoints() ); + maOptionsSnap.SetOrtho( pOpts->IsOrtho() ); + maOptionsSnap.SetBigOrtho( pOpts->IsBigOrtho() ); + maOptionsSnap.SetRotate( pOpts->IsRotate() ); + maOptionsSnap.SetSnapArea( pOpts->GetSnapArea() ); + maOptionsSnap.SetAngle( pOpts->GetAngle() ); + maOptionsSnap.SetEliminatePolyPointLimitAngle( pOpts->GetEliminatePolyPointLimitAngle() ); + } +} + +SdOptionsSnapItem* SdOptionsSnapItem::Clone( SfxItemPool* ) const +{ + return new SdOptionsSnapItem( *this ); +} + +bool SdOptionsSnapItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return maOptionsSnap == static_cast(rAttr).maOptionsSnap; +} + +void SdOptionsSnapItem::SetOptions( SdOptions* pOpts ) const +{ + if( !pOpts ) + return; + + pOpts->SetSnapHelplines( maOptionsSnap.IsSnapHelplines() ); + pOpts->SetSnapBorder( maOptionsSnap.IsSnapBorder() ); + pOpts->SetSnapFrame( maOptionsSnap.IsSnapFrame() ); + pOpts->SetSnapPoints( maOptionsSnap.IsSnapPoints() ); + pOpts->SetOrtho( maOptionsSnap.IsOrtho() ); + pOpts->SetBigOrtho( maOptionsSnap.IsBigOrtho() ); + pOpts->SetRotate( maOptionsSnap.IsRotate() ); + pOpts->SetSnapArea( maOptionsSnap.GetSnapArea() ); + pOpts->SetAngle( maOptionsSnap.GetAngle() ); + pOpts->SetEliminatePolyPointLimitAngle( maOptionsSnap.GetEliminatePolyPointLimitAngle() ); +} + +/************************************************************************* +|* +|* SdOptionsZoom +|* +\************************************************************************/ + +SdOptionsZoom::SdOptionsZoom( bool bImpress ) : + SdOptionsGeneric( bImpress, bImpress ? + OUString() : + OUString("Office.Draw/Zoom") ), + nX( 1 ), + nY( 1 ) + +{ + EnableModify( true ); +} + +void SdOptionsZoom::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + static const char* aPropNames[] = + { + "ScaleX", + "ScaleY" + }; + + rCount = !IsImpress() ? SAL_N_ELEMENTS(aPropNames) : 0; + ppNames = aPropNames; +} + +bool SdOptionsZoom::ReadData( const Any* pValues ) +{ + sal_Int32 x = 1, y = 1; + + if( pValues[0].hasValue() ) x = *o3tl::doAccess(pValues[ 0 ]); + if( pValues[1].hasValue() ) y = *o3tl::doAccess(pValues[ 1 ]); + + SetScale( x, y ); + + return true; +} + +bool SdOptionsZoom::WriteData( Any* pValues ) const +{ + sal_Int32 x, y; + + GetScale( x, y ); + + pValues[ 0 ] <<= x; + pValues[ 1 ] <<= y; + + return true; +} + +/************************************************************************* +|* +|* SdOptionsGrid +|* +\************************************************************************/ + +SdOptionsGrid::SdOptionsGrid(bool bImpress) : + SdOptionsGeneric( bImpress, + bImpress ? + OUString( "Office.Impress/Grid" ) : + OUString( "Office.Draw/Grid" ) + ) +{ + EnableModify( false ); + SetDefaults(); + EnableModify( true ); +} + +SdOptionsGrid::~SdOptionsGrid() +{ +} + +void SdOptionsGrid::SetDefaults() +{ + const sal_uInt32 nVal = 1000; + + SetFieldDivisionX( nVal ); + SetFieldDivisionY( nVal ); + SetFieldDrawX( nVal ); + SetFieldDrawY( nVal ); + SetFieldSnapX( nVal ); + SetFieldSnapY( nVal ); + SetUseGridSnap( false ); + SetSynchronize( true ); + SetGridVisible( false ); + SetEqualGrid( true ); +} + +void SdOptionsGrid::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + if( isMetricSystem() ) + { + static const char* aPropNamesMetric[] = + { + "Resolution/XAxis/Metric", + "Resolution/YAxis/Metric", + "Subdivision/XAxis", + "Subdivision/YAxis", + "SnapGrid/XAxis/Metric", + "SnapGrid/YAxis/Metric", + "Option/SnapToGrid", + "Option/Synchronize", + "Option/VisibleGrid", + "SnapGrid/Size" + }; + ppNames = aPropNamesMetric; + rCount = SAL_N_ELEMENTS(aPropNamesMetric); + } + else + { + static const char* aPropNamesNonMetric[] = + { + "Resolution/XAxis/NonMetric", + "Resolution/YAxis/NonMetric", + "Subdivision/XAxis", + "Subdivision/YAxis", + "SnapGrid/XAxis/NonMetric", + "SnapGrid/YAxis/NonMetric", + "Option/SnapToGrid", + "Option/Synchronize", + "Option/VisibleGrid", + "SnapGrid/Size" + }; + ppNames = aPropNamesNonMetric; + rCount = SAL_N_ELEMENTS(aPropNamesNonMetric); + } +} + +bool SdOptionsGrid::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetFieldDrawX( *o3tl::doAccess(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetFieldDrawY( *o3tl::doAccess(pValues[ 1 ]) ); + + if( pValues[2].hasValue() ) + { + const sal_uInt32 nDivX = FRound( *o3tl::doAccess(pValues[ 2 ]) ); + SetFieldDivisionX( SvxOptionsGrid::GetFieldDrawX() / ( nDivX + 1 ) ); + } + + if( pValues[3].hasValue() ) + { + const sal_uInt32 nDivY = FRound( *o3tl::doAccess(pValues[ 3 ]) ); + SetFieldDivisionY( SvxOptionsGrid::GetFieldDrawY() / ( nDivY + 1 ) ); + } + + if( pValues[4].hasValue() ) SetFieldSnapX( *o3tl::doAccess(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetFieldSnapY( *o3tl::doAccess(pValues[ 5 ]) ); + if( pValues[6].hasValue() ) SetUseGridSnap( *o3tl::doAccess(pValues[ 6 ]) ); + if( pValues[7].hasValue() ) SetSynchronize( *o3tl::doAccess(pValues[ 7 ]) ); + if( pValues[8].hasValue() ) SetGridVisible( *o3tl::doAccess(pValues[ 8 ]) ); + if( pValues[9].hasValue() ) SetEqualGrid( *o3tl::doAccess(pValues[ 9 ]) ); + + return true; +} + +bool SdOptionsGrid::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= static_cast(GetFieldDrawX()); + pValues[ 1 ] <<= static_cast(GetFieldDrawY()); + pValues[ 2 ] <<= ( GetFieldDivisionX() ? ( static_cast(GetFieldDrawX()) / GetFieldDivisionX() - 1.0 ) : double(0) ); + pValues[ 3 ] <<= ( GetFieldDivisionY() ? ( static_cast(GetFieldDrawY()) / GetFieldDivisionY() - 1.0 ) : double(0) ); + pValues[ 4 ] <<= static_cast(GetFieldSnapX()); + pValues[ 5 ] <<= static_cast(GetFieldSnapY()); + pValues[ 6 ] <<= IsUseGridSnap(); + pValues[ 7 ] <<= IsSynchronize(); + pValues[ 8 ] <<= IsGridVisible(); + pValues[ 9 ] <<= IsEqualGrid(); + + return true; +} + +/************************************************************************* +|* +|* SdOptionsGridItem +|* +\************************************************************************/ + +SdOptionsGridItem::SdOptionsGridItem( SdOptions const * pOpts ) : + SvxGridItem( SID_ATTR_GRID_OPTIONS ) +{ + SetSynchronize( pOpts->IsSynchronize() ); + SetEqualGrid( pOpts->IsEqualGrid() ); + + SetFieldDrawX( pOpts->GetFieldDrawX() ); + SetFieldDrawY( pOpts->GetFieldDrawY() ); + SetFieldDivisionX( pOpts->GetFieldDivisionX() ? ( pOpts->GetFieldDrawX() / pOpts->GetFieldDivisionX() - 1 ) : 0 ); + SetFieldDivisionY( pOpts->GetFieldDivisionY() ? ( pOpts->GetFieldDrawY() / pOpts->GetFieldDivisionY() - 1 ) : 0 ); + SetFieldSnapX( pOpts->GetFieldSnapX() ); + SetFieldSnapY( pOpts->GetFieldSnapY() ); + SetUseGridSnap( pOpts->IsUseGridSnap() ); + SetGridVisible( pOpts->IsGridVisible() ); +} + +void SdOptionsGridItem::SetOptions( SdOptions* pOpts ) const +{ + pOpts->SetFieldDrawX( GetFieldDrawX() ); + pOpts->SetFieldDivisionX( GetFieldDrawX() / ( GetFieldDivisionX() + 1 ) ); + pOpts->SetFieldDrawY( GetFieldDrawY() ); + pOpts->SetFieldDivisionY( GetFieldDrawY() / ( GetFieldDivisionY() + 1 ) ); + pOpts->SetFieldSnapX( GetFieldSnapX() ); + pOpts->SetFieldSnapY( GetFieldSnapY() ); + pOpts->SetUseGridSnap( GetUseGridSnap() ); + pOpts->SetSynchronize( GetSynchronize() ); + pOpts->SetGridVisible( GetGridVisible() ); + pOpts->SetEqualGrid( GetEqualGrid() ); +} + +/************************************************************************* +|* +|* SdOptionsPrint +|* +\************************************************************************/ + +SdOptionsPrint::SdOptionsPrint( bool bImpress, bool bUseConfig ) : + SdOptionsGeneric( bImpress, bUseConfig ? + ( bImpress ? + OUString( "Office.Impress/Print" ) : + OUString( "Office.Draw/Print" ) ) : + OUString() ), + bDraw( true ), + bNotes( false ), + bHandout( false ), + bOutline( false ), + bDate( false ), + bTime( false ), + bPagename( false ), + bHiddenPages( true ), + bPagesize( false ), + bPagetile( false ), + bWarningPrinter( true ), + bWarningSize( false ), + bWarningOrientation( false ), + bBooklet( false ), + bFront( true ), + bBack( true ), + bCutPage( false ), + bPaperbin( false ), + mbHandoutHorizontal( true ), + mnHandoutPages( 6 ), + nQuality( 0 ) +{ + EnableModify( true ); +} + +bool SdOptionsPrint::operator==( const SdOptionsPrint& rOpt ) const +{ + return( IsDraw() == rOpt.IsDraw() && + IsNotes() == rOpt.IsNotes() && + IsHandout() == rOpt.IsHandout() && + IsOutline() == rOpt.IsOutline() && + IsDate() == rOpt.IsDate() && + IsTime() == rOpt.IsTime() && + IsPagename() == rOpt.IsPagename() && + IsHiddenPages() == rOpt.IsHiddenPages() && + IsPagesize() == rOpt.IsPagesize() && + IsPagetile() == rOpt.IsPagetile() && + IsWarningPrinter() == rOpt.IsWarningPrinter() && + IsWarningSize() == rOpt.IsWarningSize() && + IsWarningOrientation() == rOpt.IsWarningOrientation() && + IsBooklet() == rOpt.IsBooklet() && + IsFrontPage() == rOpt.IsFrontPage() && + IsBackPage() == rOpt.IsBackPage() && + IsCutPage() == rOpt.IsCutPage() && + IsPaperbin() == rOpt.IsPaperbin() && + GetOutputQuality() == rOpt.GetOutputQuality() && + IsHandoutHorizontal() == rOpt.IsHandoutHorizontal() && + GetHandoutPages() == rOpt.GetHandoutPages() ); +} + +void SdOptionsPrint::GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const +{ + if (IsImpress()) + { + static const char* aImpressPropNames[] = + { + "Other/Date", + "Other/Time", + "Other/PageName", + "Other/HiddenPage", + "Page/PageSize", + "Page/PageTile", + // bWarningPrinter + // bWarningSize + // bWarningOrientation + "Page/Booklet", + "Page/BookletFront", + "Page/BookletBack", + // bCutPage + "Other/FromPrinterSetup", + "Other/Quality", + "Content/Presentation", + "Content/Note", + "Content/Handout", + "Content/Outline", + "Other/HandoutHorizontal", + "Other/PagesPerHandout" + }; + rCount = SAL_N_ELEMENTS(aImpressPropNames); + ppNames = aImpressPropNames; + } + else + { + static const char* aDrawPropNames[] = + { + "Other/Date", + "Other/Time", + "Other/PageName", + "Other/HiddenPage", + "Page/PageSize", + "Page/PageTile", + // bWarningPrinter + // bWarningSize + // bWarningOrientation + "Page/Booklet", + "Page/BookletFront", + "Page/BookletBack", + // bCutPage + "Other/FromPrinterSetup", + "Other/Quality", + "Content/Drawing", + }; + rCount = SAL_N_ELEMENTS(aDrawPropNames); + ppNames = aDrawPropNames; + } +} + +bool SdOptionsPrint::ReadData( const Any* pValues ) +{ + if( pValues[0].hasValue() ) SetDate( *o3tl::doAccess(pValues[ 0 ]) ); + if( pValues[1].hasValue() ) SetTime( *o3tl::doAccess(pValues[ 1 ]) ); + if( pValues[2].hasValue() ) SetPagename( *o3tl::doAccess(pValues[ 2 ]) ); + if( pValues[3].hasValue() ) SetHiddenPages( *o3tl::doAccess(pValues[ 3 ]) ); + if( pValues[4].hasValue() ) SetPagesize( *o3tl::doAccess(pValues[ 4 ]) ); + if( pValues[5].hasValue() ) SetPagetile( *o3tl::doAccess(pValues[ 5 ]) ); + if( pValues[6].hasValue() ) SetBooklet( *o3tl::doAccess(pValues[ 6 ]) ); + if( pValues[7].hasValue() ) SetFrontPage( *o3tl::doAccess(pValues[ 7 ]) ); + if( pValues[8].hasValue() ) SetBackPage( *o3tl::doAccess(pValues[ 8 ]) ); + if( pValues[9].hasValue() ) SetPaperbin( *o3tl::doAccess(pValues[ 9 ]) ); + if( pValues[10].hasValue() ) SetOutputQuality( static_cast(*o3tl::doAccess(pValues[ 10 ])) ); + if( pValues[11].hasValue() ) SetDraw( *o3tl::doAccess(pValues[ 11 ]) ); + + // just for impress + if (IsImpress()) + { + if( pValues[12].hasValue() ) SetNotes( *o3tl::doAccess(pValues[ 12 ]) ); + if( pValues[13].hasValue() ) SetHandout( *o3tl::doAccess(pValues[ 13 ]) ); + if( pValues[14].hasValue() ) SetOutline( *o3tl::doAccess(pValues[ 14 ]) ); + if( pValues[15].hasValue() ) SetHandoutHorizontal( *o3tl::doAccess(pValues[15]) ); + if( pValues[16].hasValue() ) SetHandoutPages( static_cast(*o3tl::doAccess(pValues[16])) ); + } + + return true; +} + +bool SdOptionsPrint::WriteData( Any* pValues ) const +{ + pValues[ 0 ] <<= IsDate(); + pValues[ 1 ] <<= IsTime(); + pValues[ 2 ] <<= IsPagename(); + pValues[ 3 ] <<= IsHiddenPages(); + pValues[ 4 ] <<= IsPagesize(); + pValues[ 5 ] <<= IsPagetile(); + pValues[ 6 ] <<= IsBooklet(); + pValues[ 7 ] <<= IsFrontPage(); + pValues[ 8 ] <<= IsBackPage(); + pValues[ 9 ] <<= IsPaperbin(); + pValues[ 10 ] <<= static_cast(GetOutputQuality()); + pValues[ 11 ] <<= IsDraw(); + + // just for impress + if (IsImpress()) + { + pValues[ 12 ] <<= IsNotes(); + pValues[ 13 ] <<= IsHandout(); + pValues[ 14 ] <<= IsOutline(); + pValues[ 15 ] <<= IsHandoutHorizontal(); + pValues[ 16 ] <<= GetHandoutPages(); + } + + return true; +} + +/************************************************************************* +|* +|* SdOptionsPrintItem +|* +\************************************************************************/ + +SdOptionsPrintItem::SdOptionsPrintItem() +: SfxPoolItem ( ATTR_OPTIONS_PRINT ) +, maOptionsPrint ( false, false ) +{ +} + +SdOptionsPrintItem::SdOptionsPrintItem( SdOptions const * pOpts ) +: SfxPoolItem ( ATTR_OPTIONS_PRINT ) +, maOptionsPrint ( false, false ) +{ + if( !pOpts ) + return; + + maOptionsPrint.SetDraw( pOpts->IsDraw() ); + maOptionsPrint.SetNotes( pOpts->IsNotes() ); + maOptionsPrint.SetHandout( pOpts->IsHandout() ); + maOptionsPrint.SetOutline( pOpts->IsOutline() ); + maOptionsPrint.SetDate( pOpts->IsDate() ); + maOptionsPrint.SetTime( pOpts->IsTime() ); + maOptionsPrint.SetPagename( pOpts->IsPagename() ); + maOptionsPrint.SetHiddenPages( pOpts->IsHiddenPages() ); + maOptionsPrint.SetPagesize( pOpts->IsPagesize() ); + maOptionsPrint.SetPagetile( pOpts->IsPagetile() ); + maOptionsPrint.SetWarningPrinter( pOpts->IsWarningPrinter() ); + maOptionsPrint.SetWarningSize( pOpts->IsWarningSize() ); + maOptionsPrint.SetWarningOrientation( pOpts->IsWarningOrientation() ); + maOptionsPrint.SetBooklet( pOpts->IsBooklet() ); + maOptionsPrint.SetFrontPage( pOpts->IsFrontPage() ); + maOptionsPrint.SetBackPage( pOpts->IsBackPage() ); + maOptionsPrint.SetCutPage( pOpts->IsCutPage() ); + maOptionsPrint.SetPaperbin( pOpts->IsPaperbin() ); + maOptionsPrint.SetOutputQuality( pOpts->GetOutputQuality() ); +} + +SdOptionsPrintItem* SdOptionsPrintItem::Clone( SfxItemPool* ) const +{ + return new SdOptionsPrintItem( *this ); +} + +bool SdOptionsPrintItem::operator==( const SfxPoolItem& rAttr ) const +{ + assert(SfxPoolItem::operator==(rAttr)); + return maOptionsPrint == static_cast(rAttr).maOptionsPrint; +} + +void SdOptionsPrintItem::SetOptions( SdOptions* pOpts ) const +{ + if( !pOpts ) + return; + + pOpts->SetDraw( maOptionsPrint.IsDraw() ); + pOpts->SetNotes( maOptionsPrint.IsNotes() ); + pOpts->SetHandout( maOptionsPrint.IsHandout() ); + pOpts->SetOutline( maOptionsPrint.IsOutline() ); + pOpts->SetDate( maOptionsPrint.IsDate() ); + pOpts->SetTime( maOptionsPrint.IsTime() ); + pOpts->SetPagename( maOptionsPrint.IsPagename() ); + pOpts->SetHiddenPages( maOptionsPrint.IsHiddenPages() ); + pOpts->SetPagesize( maOptionsPrint.IsPagesize() ); + pOpts->SetPagetile( maOptionsPrint.IsPagetile() ); + pOpts->SetWarningPrinter( maOptionsPrint.IsWarningPrinter() ); + pOpts->SetWarningSize( maOptionsPrint.IsWarningSize() ); + pOpts->SetWarningOrientation( maOptionsPrint.IsWarningOrientation() ); + pOpts->SetBooklet( maOptionsPrint.IsBooklet() ); + pOpts->SetFrontPage( maOptionsPrint.IsFrontPage() ); + pOpts->SetBackPage( maOptionsPrint.IsBackPage() ); + pOpts->SetCutPage( maOptionsPrint.IsCutPage() ); + pOpts->SetPaperbin( maOptionsPrint.IsPaperbin() ); + pOpts->SetOutputQuality( maOptionsPrint.GetOutputQuality() ); +} + +/************************************************************************* +|* +|* SdOptions +|* +\************************************************************************/ + +SdOptions::SdOptions(bool bImpress) : + SdOptionsLayout( bImpress, true ), + SdOptionsContents( bImpress ), + SdOptionsMisc( bImpress, true ), + SdOptionsSnap( bImpress, true ), + SdOptionsZoom( bImpress ), + SdOptionsGrid( bImpress ), + SdOptionsPrint( bImpress, true ) +{ +} + +SdOptions::~SdOptions() +{ +} + +void SdOptions::StoreConfig() +{ + SdOptionsLayout::Store(); + SdOptionsContents::Store(); + SdOptionsMisc::Store(); + SdOptionsSnap::Store(); + SdOptionsZoom::Store(); + SdOptionsGrid::Store(); + SdOptionsPrint::Store(); +} + +sal_Int32 SdOptionsMisc::GetDisplay() const +{ + Init(); + return mnDisplay; +} + +void SdOptionsMisc::SetDisplay( sal_Int32 nDisplay ) +{ + if( mnDisplay != nDisplay ) + { + OptionsChanged(); + mnDisplay = nDisplay; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/scalectrl.cxx b/sd/source/ui/app/scalectrl.cxx new file mode 100644 index 000000000..0444163b5 --- /dev/null +++ b/sd/source/ui/app/scalectrl.cxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +SFX_IMPL_STATUSBAR_CONTROL(SdScaleControl, SfxStringItem); + +// class SdScaleControl ------------------------------------------ +SdScaleControl::SdScaleControl(sal_uInt16 _nSlotId, sal_uInt16 _nId, StatusBar& rStb) + : SfxStatusBarControl(_nSlotId, _nId, rStb) +{ + GetStatusBar().SetQuickHelpText(GetId(), SdResId(STR_SCALE_TOOLTIP)); +} + +SdScaleControl::~SdScaleControl() {} + +void SdScaleControl::StateChangedAtStatusBarControl(sal_uInt16 /*nSID*/, SfxItemState eState, + const SfxPoolItem* pState) +{ + if (eState != SfxItemState::DEFAULT || pState->IsVoidItem()) + return; + auto pStringItem = dynamic_cast(pState); + GetStatusBar().SetItemText(GetId(), pStringItem->GetValue()); +} + +void SdScaleControl::Command(const CommandEvent& rCEvt) +{ + if (rCEvt.GetCommand() != CommandEventId::ContextMenu + || GetStatusBar().GetItemText(GetId()).isEmpty()) + return; + + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + + sd::ViewShellBase* pViewShellBase = sd::ViewShellBase::GetViewShellBase(pViewFrame); + if (!pViewShellBase) + return; + + SdDrawDocument* pDoc = pViewShellBase->GetDocument(); + if (!pDoc) + return; + + std::unique_ptr xBuilder( + Application::CreateBuilder(nullptr, "modules/simpress/ui/masterpagemenu.ui")); + std::unique_ptr xPopup(xBuilder->weld_menu("menu")); + + sal_uInt16 aTable[12] = { 1, 2, 4, 5, 8, 10, 16, 20, 30, 40, 50, 100 }; + + for (sal_uInt16 i = 11; i > 0; i--) + xPopup->append(OUString::number(12 - i), OUString::number(aTable[i]) + ":1"); + for (sal_uInt16 i = 0; i < 12; i++) + xPopup->append(OUString::number(12 + i), "1:" + OUString::number(aTable[i])); + + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1)); + weld::Window* pParent = weld::GetPopupParent(GetStatusBar(), aRect); + OString sResult = xPopup->popup_at_rect(pParent, aRect); + if (sResult.isEmpty()) + return; + + sal_Int32 i = sResult.toUInt32(); + sal_Int32 nX; + sal_Int32 nY; + if (i > 11) + nX = 1; + else + nX = aTable[(12 - i) % 12]; + if (i > 11) + nY = aTable[i % 12]; + else + nY = 1; + pDoc->SetUIScale(Fraction(nX, nY)); + + SfxBindings& pBindings = pViewFrame->GetBindings(); + pBindings.Invalidate(SID_SCALE); //update statusbar + pBindings.Invalidate(SID_ATTR_METRIC); //update sidebar + pViewShellBase->UpdateBorder(true); // update ruler +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sddll.cxx b/sd/source/ui/app/sddll.cxx new file mode 100644 index 000000000..4e20d0997 --- /dev/null +++ b/sd/source/ui/app/sddll.cxx @@ -0,0 +1,269 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +// Register all Factories +void SdDLL::RegisterFactorys() +{ + if (utl::ConfigManager::IsFuzzing() || SvtModuleOptions().IsImpress()) + { + ::sd::ImpressViewShellBase::RegisterFactory ( + ::sd::IMPRESS_FACTORY_ID); + ::sd::SlideSorterViewShellBase::RegisterFactory ( + ::sd::SLIDE_SORTER_FACTORY_ID); + ::sd::OutlineViewShellBase::RegisterFactory ( + ::sd::OUTLINE_FACTORY_ID); + ::sd::PresentationViewShellBase::RegisterFactory ( + ::sd::PRESENTATION_FACTORY_ID); + } + if (!utl::ConfigManager::IsFuzzing() && SvtModuleOptions().IsDraw()) + { + ::sd::GraphicViewShellBase::RegisterFactory (::sd::DRAW_FACTORY_ID); + } +} + +// Register all Interfaces + +void SdDLL::RegisterInterfaces(const SdModule* pMod) +{ + // Module + SdModule::RegisterInterface(pMod); + + // View shell base. + ::sd::ViewShellBase::RegisterInterface(pMod); + + // DocShells + ::sd::DrawDocShell::RegisterInterface(pMod); + ::sd::GraphicDocShell::RegisterInterface(pMod); + + // Impress ViewShells + ::sd::DrawViewShell::RegisterInterface(pMod); + ::sd::OutlineViewShell::RegisterInterface(pMod); + ::sd::PresentationViewShell::RegisterInterface(pMod); + + // Draw ViewShell + ::sd::GraphicViewShell::RegisterInterface(pMod); + + // Impress ObjectShells + ::sd::BezierObjectBar::RegisterInterface(pMod); + ::sd::TextObjectBar::RegisterInterface(pMod); + ::sd::GraphicObjectBar::RegisterInterface(pMod); + + // Media ObjectShell + ::sd::MediaObjectBar::RegisterInterface(pMod); + + // Table ObjectShell + ::sd::ui::table::RegisterInterfaces(pMod); + + // View shells for the side panes. + ::sd::slidesorter::SlideSorterViewShell::RegisterInterface (pMod); +} + +// Register all Controllers + +void SdDLL::RegisterControllers(SdModule* pMod) +{ + SdTbxCtlDiaPages::RegisterControl( SID_PAGES_PER_ROW, pMod ); + SdTbxCtlGlueEscDir::RegisterControl( SID_GLUE_ESCDIR, pMod ); + + ::sd::AnimationChildWindow::RegisterChildWindow(false, pMod); + + Svx3DChildWindow::RegisterChildWindow(false, pMod); + SvxFontWorkChildWindow::RegisterChildWindow(false, pMod); + SvxColorChildWindow::RegisterChildWindow(false, pMod, SfxChildWindowFlags::TASK); + SvxSearchDialogWrapper::RegisterChildWindow(false, pMod); + SvxBmpMaskChildWindow::RegisterChildWindow(false, pMod); + SvxIMapDlgChildWindow::RegisterChildWindow(false, pMod); + SvxHlinkDlgWrapper::RegisterChildWindow(false, pMod); + ::sd::SpellDialogChildWindow::RegisterChildWindow( + false, pMod, comphelper::LibreOfficeKit::isActive() ? SfxChildWindowFlags::NEVERCLONE + : SfxChildWindowFlags::NONE); +#if HAVE_FEATURE_AVMEDIA + ::avmedia::MediaPlayer::RegisterChildWindow(false, pMod); +#endif + ::sd::LeftPaneImpressChildWindow::RegisterChildWindow(false, pMod); + ::sd::LeftPaneDrawChildWindow::RegisterChildWindow(false, pMod); + ::sfx2::sidebar::SidebarChildWindow::RegisterChildWindow(false, pMod); + DevelopmentToolChildWindow::RegisterChildWindow(false, pMod); + + ::sd::SdNavigatorWrapper::RegisterChildWindow(false, pMod, SfxChildWindowFlags::NEVERHIDE); + + SvxFillToolBoxControl::RegisterControl(0, pMod); + SvxLineWidthToolBoxControl::RegisterControl(0, pMod); + + SvxGrafModeToolBoxControl::RegisterControl( SID_ATTR_GRAF_MODE, pMod ); + SvxGrafRedToolBoxControl::RegisterControl( SID_ATTR_GRAF_RED, pMod ); + SvxGrafGreenToolBoxControl::RegisterControl( SID_ATTR_GRAF_GREEN, pMod ); + SvxGrafBlueToolBoxControl::RegisterControl( SID_ATTR_GRAF_BLUE, pMod ); + SvxGrafLuminanceToolBoxControl::RegisterControl( SID_ATTR_GRAF_LUMINANCE, pMod ); + SvxGrafContrastToolBoxControl::RegisterControl( SID_ATTR_GRAF_CONTRAST, pMod ); + SvxGrafGammaToolBoxControl::RegisterControl( SID_ATTR_GRAF_GAMMA, pMod ); + SvxGrafTransparenceToolBoxControl::RegisterControl( SID_ATTR_GRAF_TRANSPARENCE, pMod ); + + // register StatusBarControls + SvxZoomPageStatusBarControl::RegisterControl( SID_ZOOM_ENTIRE_PAGE, pMod ); + SvxZoomStatusBarControl::RegisterControl( SID_ATTR_ZOOM, pMod ); + SvxPosSizeStatusBarControl::RegisterControl( SID_ATTR_SIZE, pMod ); + SvxModifyControl::RegisterControl( SID_DOC_MODIFIED, pMod ); + SvxZoomSliderControl::RegisterControl( SID_ATTR_ZOOMSLIDER, pMod ); + + svx::FormatPaintBrushToolBoxControl::RegisterControl(SID_FORMATPAINTBRUSH, pMod ); + + SvxClipBoardControl::RegisterControl( SID_PASTE, pMod ); + SvxClipBoardControl::RegisterControl( SID_PASTE_UNFORMATTED, pMod ); + +#if HAVE_FEATURE_AVMEDIA + ::avmedia::MediaToolBoxControl::RegisterControl( SID_AVMEDIA_TOOLBOX, pMod ); +#endif + XmlSecStatusBarControl::RegisterControl( SID_SIGNATURE, pMod ); + SdTemplateControl::RegisterControl( SID_STATUS_LAYOUT, pMod ); + SdScaleControl::RegisterControl( SID_SCALE, pMod ); + SvxTbxCtlDraw::RegisterControl(SID_INSERT_DRAW, pMod ); +} + +void SdDLL::Init() +{ + if ( SfxApplication::GetModule(SfxToolsModule::Draw) ) // Module already active + return; + + SfxObjectFactory* pDrawFact = nullptr; + SfxObjectFactory* pImpressFact = nullptr; + + if (utl::ConfigManager::IsFuzzing() || SvtModuleOptions().IsImpress()) + pImpressFact = &::sd::DrawDocShell::Factory(); + + if (!utl::ConfigManager::IsFuzzing() && SvtModuleOptions().IsDraw()) + pDrawFact = &::sd::GraphicDocShell::Factory(); + + auto pUniqueModule = std::make_unique(pImpressFact, pDrawFact); + SdModule* pModule = pUniqueModule.get(); + SfxApplication::SetModule(SfxToolsModule::Draw, std::move(pUniqueModule)); + + if (!utl::ConfigManager::IsFuzzing() && SvtModuleOptions().IsImpress()) + { + // Register the Impress shape types in order to make the shapes accessible. + ::accessibility::RegisterImpressShapeTypes (); + ::sd::DrawDocShell::Factory().SetDocumentServiceName( "com.sun.star.presentation.PresentationDocument" ); + } + + if (!utl::ConfigManager::IsFuzzing() && SvtModuleOptions().IsDraw()) + { + ::sd::GraphicDocShell::Factory().SetDocumentServiceName( "com.sun.star.drawing.DrawingDocument" ); + } + + // register your view-factories here + RegisterFactorys(); + + // register your shell-interfaces here + RegisterInterfaces(pModule); + + // register your controllers here + RegisterControllers(pModule); + + // register 3D-object-factory + E3dObjFactory(); + + // register css::form::component::Form-Object-Factory + FmFormObjFactory(); + + // register your exotic remote controls here +#ifdef ENABLE_SDREMOTE + if (!utl::ConfigManager::IsFuzzing() && !Application::IsHeadlessModeEnabled()) + RegisterRemotes(); +#endif +} + +#ifndef DISABLE_DYNLOADING + +extern "C" SAL_DLLPUBLIC_EXPORT +void lok_preload_hook() +{ + SdFilter::Preload(); + SdAbstractDialogFactory::Create(); +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdmod.cxx b/sd/source/ui/app/sdmod.cxx new file mode 100644 index 000000000..c7d56831d --- /dev/null +++ b/sd/source/ui/app/sdmod.cxx @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define ShellClass_SdModule +#include + +SFX_IMPL_INTERFACE(SdModule, SfxModule) + +void SdModule::InitInterface_Impl() +{ + GetStaticInterface()->RegisterStatusBar(StatusBarId::DrawStatusBar); +} + +// Ctor +SdModule::SdModule(SfxObjectFactory* pFact1, SfxObjectFactory* pFact2 ) +: SfxModule("sd", {pFact1, pFact2}), + pTransferClip(nullptr), + pTransferDrag(nullptr), + pTransferSelection(nullptr), + pImpressOptions(nullptr), + pDrawOptions(nullptr), + bWaterCan(false), + mbEventListenerAdded(false), + mpColorConfig(new svtools::ColorConfig) +{ + SetName( "StarDraw" ); // Do not translate! + pSearchItem.reset( new SvxSearchItem(SID_SEARCH_ITEM) ); + pSearchItem->SetAppFlag(SvxSearchApp::DRAW); + StartListening( *SfxGetpApp() ); + SvxErrorHandler::ensure(); + mpErrorHdl.reset( new SfxErrorHandler(RID_SD_ERRHDL, ErrCodeArea::Sd, ErrCodeArea::Sd, GetResLocale()) ); + + // Create a new ref device and (by calling SetReferenceDevice()) + // set its resolution to 600 DPI. This leads to a visually better + // formatting of text in small sizes (6 point and below.) + mpVirtualRefDevice.reset(VclPtr::Create()); + mpVirtualRefDevice->SetMapMode(MapMode(MapUnit::Map100thMM)); + mpVirtualRefDevice->SetReferenceDevice ( VirtualDevice::RefDevMode::Dpi600 ); +} + +OUString SdResId(TranslateId aId) +{ + return Translate::get(aId, SD_MOD()->GetResLocale()); +} + +OUString SdResId(TranslateNId aContextSingularPlural, int nCardinality) +{ + return Translate::nget(aContextSingularPlural, nCardinality, SD_MOD()->GetResLocale()); +} + +// Dtor +SdModule::~SdModule() +{ + pSearchItem.reset(); + pNumberFormatter.reset(); + + if (mbEventListenerAdded) + { + Application::RemoveEventListener( LINK( this, SdModule, EventListenerHdl ) ); + } + + mpErrorHdl.reset(); + mpVirtualRefDevice.disposeAndClear(); +} + +void SdModule::SetSearchItem(std::unique_ptr pItem) +{ + pSearchItem = std::move(pItem); +} + +/// get notifications +void SdModule::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if( rHint.GetId() == SfxHintId::Deinitializing ) + { + delete pImpressOptions; + pImpressOptions = nullptr; + delete pDrawOptions; + pDrawOptions = nullptr; + } +} + +/// Return options +SdOptions* SdModule::GetSdOptions(DocumentType eDocType) +{ + SdOptions* pOptions = nullptr; + + if (eDocType == DocumentType::Draw) + { + if (!pDrawOptions) + pDrawOptions = new SdOptions(false); + + pOptions = pDrawOptions; + } + else if (eDocType == DocumentType::Impress) + { + if (!pImpressOptions) + pImpressOptions = new SdOptions(true); + + pOptions = pImpressOptions; + } + if( pOptions ) + { + sal_uInt16 nMetric = pOptions->GetMetric(); + + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = nullptr; + if (pDocSh) + pDoc = pDocSh->GetDoc(); + + if( nMetric != 0xffff && pDoc && eDocType == pDoc->GetDocumentType() ) + PutItem( SfxUInt16Item( SID_ATTR_METRIC, nMetric ) ); + } + + return pOptions; +} + +/** + * Open and return option stream for internal options; + * if the stream is opened for reading but does not exist, an 'empty' + * RefObject is returned + */ +tools::SvRef SdModule::GetOptionStream( std::u16string_view rOptionName, + SdOptionStreamMode eMode ) +{ + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + tools::SvRef xStm; + + if( pDocSh ) + { + DocumentType eType = pDocSh->GetDoc()->GetDocumentType(); + + if( !xOptionStorage.is() ) + { + INetURLObject aURL( SvtPathOptions().GetUserConfigPath() ); + + aURL.Append( u"drawing.cfg" ); + + std::unique_ptr pStm = ::utl::UcbStreamHelper::CreateStream( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READWRITE ); + + if( pStm ) + xOptionStorage = new SotStorage( pStm.release(), true ); + } + + OUString aStmName; + + if( DocumentType::Draw == eType ) + aStmName = "Draw_"; + else + aStmName = "Impress_"; + + aStmName += rOptionName; + + if( SdOptionStreamMode::Store == eMode || xOptionStorage->IsContained( aStmName ) ) + xStm = xOptionStorage->OpenSotStream( aStmName ); + } + + return xStm; +} + +SvNumberFormatter* SdModule::GetNumberFormatter() +{ + if( !pNumberFormatter ) + pNumberFormatter.reset( new SvNumberFormatter( ::comphelper::getProcessComponentContext(), LANGUAGE_SYSTEM ) ); + + return pNumberFormatter.get(); +} + +svtools::ColorConfig& SdModule::GetColorConfig() +{ + return *mpColorConfig; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdmod1.cxx b/sd/source/ui/app/sdmod1.cxx new file mode 100644 index 000000000..573ee8530 --- /dev/null +++ b/sd/source/ui/app/sdmod1.cxx @@ -0,0 +1,638 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::frame::XFrame; + +namespace { + +class OutlineToImpressFinalizer final +{ +public: + OutlineToImpressFinalizer ( + ::sd::ViewShellBase& rBase, + SdDrawDocument& rDocument, + SvLockBytes const & rBytes); + void operator() (bool bEventSeen); +private: + ::sd::ViewShellBase& mrBase; + SdDrawDocument& mrDocument; + std::shared_ptr mpStream; +}; + +} //end of anonymous namespace + +void SdModule::Execute(SfxRequest& rReq) +{ + const SfxItemSet* pSet = rReq.GetArgs(); + sal_uLong nSlotId = rReq.GetSlot(); + + switch ( nSlotId ) + { + case SID_NEWDOC: + { + SfxGetpApp()->ExecuteSlot(rReq, SfxGetpApp()->GetInterface()); + } + break; + + case SID_AUTOSPELL_CHECK: + { + // automatic spell checker + const SfxBoolItem* pItem; + if( pSet && (pItem = pSet->GetItemIfSet( SID_AUTOSPELL_CHECK, false ) ) ) + { + bool bOnlineSpelling = pItem->GetValue(); + // save at document: + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + { + SdDrawDocument* pDoc = pDocSh->GetDoc(); + pDoc->SetOnlineSpell( bOnlineSpelling ); + } + } + } + break; + + case SID_ATTR_METRIC: + { + const SfxUInt16Item* pItem; + if ( pSet && (pItem = pSet->GetItemIfSet( SID_ATTR_METRIC ) ) ) + { + FieldUnit eUnit = static_cast(pItem->GetValue()); + switch( eUnit ) + { + case FieldUnit::MM: // only the units which are also in the dialog + case FieldUnit::CM: + case FieldUnit::INCH: + case FieldUnit::PICA: + case FieldUnit::POINT: + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if(pDocSh) + { + DocumentType eDocType = pDocSh->GetDoc()->GetDocumentType(); + + PutItem( *pItem ); + SdOptions* pOptions = GetSdOptions( eDocType ); + if(pOptions) + pOptions->SetMetric( static_cast(eUnit) ); + rReq.Done(); + } + } + break; + default: + break; + } + } + + } + break; + + case SID_ATTR_LANGUAGE: + case SID_ATTR_CHAR_CJK_LANGUAGE: + case SID_ATTR_CHAR_CTL_LANGUAGE: + { + const SfxPoolItem* pItem; + if( pSet && + ( + SfxItemState::SET == pSet->GetItemState(SID_ATTR_LANGUAGE, false, &pItem ) || + SfxItemState::SET == pSet->GetItemState(SID_ATTR_CHAR_CJK_LANGUAGE, false, &pItem ) || + SfxItemState::SET == pSet->GetItemState(SID_ATTR_CHAR_CTL_LANGUAGE, false, &pItem ) + ) + ) + { + // save at the document: + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if ( pDocSh ) + { + LanguageType eLanguage = static_cast(pItem)->GetValue(); + SdDrawDocument* pDoc = pDocSh->GetDoc(); + + if( nSlotId == sal_uInt16(SID_ATTR_CHAR_CJK_LANGUAGE) ) + pDoc->SetLanguage( eLanguage, EE_CHAR_LANGUAGE_CJK ); + else if( nSlotId == sal_uInt16(SID_ATTR_CHAR_CTL_LANGUAGE) ) + pDoc->SetLanguage( eLanguage, EE_CHAR_LANGUAGE_CTL ); + else + pDoc->SetLanguage( eLanguage, EE_CHAR_LANGUAGE ); + + if( pDoc->GetOnlineSpell() ) + { + pDoc->StopOnlineSpelling(); + pDoc->StartOnlineSpelling(); + } + } + } + } + break; + + case SID_NEWSD: + { + SfxFrame* pFrame = ExecuteNewDocument( rReq ); + // if a frame was created, set it as return value + if(pFrame) + rReq.SetReturnValue(SfxFrameItem(0, pFrame)); + } + + break; + + case SID_OPENHYPERLINK: + case SID_OPENDOC: + { + bool bIntercept = false; + ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if (pDocShell) + { + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + if (pViewShell) + { + if( sd::SlideShow::IsRunning( pViewShell->GetViewShellBase() ) ) + { + // Prevent documents from opening while the slide + // show is running, except when this request comes + // from a shape interaction. + if (rReq.GetArgs() == nullptr) + { + bIntercept = true; + } + } + } + } + + if (!bIntercept) + { + SfxGetpApp()->ExecuteSlot(rReq, SfxGetpApp()->GetInterface()); + } + else + { + std::unique_ptr xErrorBox(Application::CreateMessageDialog(nullptr, + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_CANT_PERFORM_IN_LIVEMODE))); + + xErrorBox->run(); + + const SfxLinkItem* pLinkItem = rReq.GetArg(SID_DONELINK); + if( pLinkItem ) + pLinkItem->GetValue().Call( nullptr ); + } + } + break; + + case SID_OUTLINE_TO_IMPRESS: + OutlineToImpress (rReq); + break; + + default: + break; + } +} + +bool SdModule::OutlineToImpress(SfxRequest const & rRequest) +{ + const SfxItemSet* pSet = rRequest.GetArgs(); + + if (pSet) + { + SvLockBytes* pBytes = static_cast(pSet->Get(SID_OUTLINE_TO_IMPRESS)).GetValue(); + + if (pBytes) + { + SfxObjectShellLock xDocShell; + ::sd::DrawDocShell* pDocSh; + xDocShell = pDocSh = new ::sd::DrawDocShell( + SfxObjectCreateMode::STANDARD, false, DocumentType::Impress); + + pDocSh->DoInitNew(); + SdDrawDocument* pDoc = pDocSh->GetDoc(); + if(pDoc) + { + pDoc->CreateFirstPages(); + pDoc->StopWorkStartupDelay(); + } + + const SfxFrameItem* pFrmItem = rRequest.GetArg(SID_DOCFRAME); + SfxViewFrame::LoadDocumentIntoFrame( *pDocSh, pFrmItem, ::sd::OUTLINE_FACTORY_ID ); + + ::sd::ViewShell* pViewSh = pDocSh->GetViewShell(); + + if (pViewSh && pDoc) + { + // AutoLayouts have to be finished + pDoc->StopWorkStartupDelay(); + + SfxViewFrame* pViewFrame = pViewSh->GetViewFrame(); + + // When the view frame has not been just created we have + // to switch synchronously to the outline view. + // (Otherwise the request will be ignored anyway.) + ::sd::ViewShellBase* pBase + = dynamic_cast< ::sd::ViewShellBase*>(pViewFrame->GetViewShell()); + if (pBase != nullptr) + { + std::shared_ptr pHelper ( + FrameworkHelper::Instance(*pBase)); + pHelper->RequestView( + FrameworkHelper::msOutlineViewURL, + FrameworkHelper::msCenterPaneURL); + + pHelper->RunOnResourceActivation( + FrameworkHelper::CreateResourceId( + FrameworkHelper::msOutlineViewURL, + FrameworkHelper::msCenterPaneURL), + OutlineToImpressFinalizer(*pBase, *pDoc, *pBytes)); + } + } + } + } + + return rRequest.IsDone(); +} + +void SdModule::GetState(SfxItemSet& rItemSet) +{ + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_ATTR_METRIC ) ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if(pDocSh) + { + DocumentType eDocType = pDocSh->GetDoc()->GetDocumentType(); + + SdOptions* pOptions = GetSdOptions(eDocType); + rItemSet.Put( SfxUInt16Item( SID_ATTR_METRIC, pOptions->GetMetric() ) ); + } + } + + // state of SID_OPENDOC is determined by the base class + if (rItemSet.GetItemState(SID_OPENDOC) != SfxItemState::UNKNOWN) + { + const SfxPoolItem* pItem = SfxGetpApp()->GetSlotState(SID_OPENDOC, SfxGetpApp()->GetInterface()); + if (pItem) + rItemSet.Put(*pItem); + } + + // state of SID_OPENHYPERLINK is determined by the base class + if (rItemSet.GetItemState(SID_OPENHYPERLINK) != SfxItemState::UNKNOWN) + { + const SfxPoolItem* pItem = SfxGetpApp()->GetSlotState(SID_OPENHYPERLINK, SfxGetpApp()->GetInterface()); + if (pItem) + rItemSet.Put(*pItem); + } + + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_AUTOSPELL_CHECK ) ) + { + ::sd::DrawDocShell* pDocSh = + dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + { + SdDrawDocument* pDoc = pDocSh->GetDoc(); + rItemSet.Put( SfxBoolItem( SID_AUTOSPELL_CHECK, pDoc->GetOnlineSpell() ) ); + } + } + + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_ATTR_LANGUAGE ) ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + rItemSet.Put( SvxLanguageItem( pDocSh->GetDoc()->GetLanguage( EE_CHAR_LANGUAGE ), SID_ATTR_LANGUAGE ) ); + } + + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_ATTR_CHAR_CJK_LANGUAGE ) ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + rItemSet.Put( SvxLanguageItem( pDocSh->GetDoc()->GetLanguage( EE_CHAR_LANGUAGE_CJK ), SID_ATTR_CHAR_CJK_LANGUAGE ) ); + } + + if( SfxItemState::DEFAULT == rItemSet.GetItemState( SID_ATTR_CHAR_CTL_LANGUAGE ) ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocSh ) + rItemSet.Put( SvxLanguageItem( pDocSh->GetDoc()->GetLanguage( EE_CHAR_LANGUAGE_CTL ), SID_ATTR_CHAR_CTL_LANGUAGE ) ); + } + + if ( mbEventListenerAdded ) + return; + + ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocShell ) // Impress or Draw ? + { + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + + if( pViewShell && (pDocShell->GetDocumentType() == DocumentType::Impress) ) + { + // add our event listener as soon as possible + Application::AddEventListener( LINK( this, SdModule, EventListenerHdl ) ); + mbEventListenerAdded = true; + } + } +} + +IMPL_STATIC_LINK( SdModule, EventListenerHdl, VclSimpleEvent&, rSimpleEvent, void ) +{ + if( !((rSimpleEvent.GetId() == VclEventId::WindowCommand) && static_cast(&rSimpleEvent)->GetData()) ) + return; + + const CommandEvent& rEvent = *static_cast(static_cast(&rSimpleEvent)->GetData()); + + if( rEvent.GetCommand() != CommandEventId::Media ) + return; + + CommandMediaData* pMediaData = rEvent.GetMediaData(); + pMediaData->SetPassThroughToOS(false); + switch (pMediaData->GetMediaId()) + { + case MediaCommand::Play: + { + ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + if( pDocShell ) // Impress or Draw ? + { + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + + // #i97925# start the presentation if and only if an Impress document is focused + if( pViewShell && (pDocShell->GetDocumentType() == DocumentType::Impress) ) + pViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_PRESENTATION ); + } + break; + } + default: + pMediaData->SetPassThroughToOS(true); + break; + } +} + + +SfxFrame* SdModule::CreateFromTemplate(const OUString& rTemplatePath, const Reference& i_rFrame, + const bool bReplaceable) +{ + SfxFrame* pFrame = nullptr; + + SfxObjectShellLock xDocShell; + + std::unique_ptr pSet(new SfxAllItemSet( SfxGetpApp()->GetPool() )); + pSet->Put( SfxBoolItem( SID_TEMPLATE, true ) ); + + ErrCode lErr = SfxGetpApp()->LoadTemplate( xDocShell, rTemplatePath, std::move(pSet) ); + + SfxObjectShell* pDocShell = xDocShell; + + if( lErr ) + { + ErrorHandler::HandleError(lErr); + } + else if( pDocShell ) + { + if (pDocShell->GetMedium() && pDocShell->GetMedium()->GetItemSet()) + pDocShell->GetMedium()->GetItemSet()->Put(SfxBoolItem(SID_REPLACEABLE, bReplaceable)); + SfxViewFrame* pViewFrame = SfxViewFrame::LoadDocumentIntoFrame( *pDocShell, i_rFrame ); + OSL_ENSURE( pViewFrame, "SdModule::CreateFromTemplate: no view frame - was the document really loaded?" ); + pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr; + } + + return pFrame; + +} + +SfxFrame* SdModule::ExecuteNewDocument( SfxRequest const & rReq ) +{ + SfxFrame* pFrame = nullptr; + if ( SvtModuleOptions().IsImpress() ) + { + Reference< XFrame > xTargetFrame; + const SfxUnoFrameItem* pFrmItem = rReq.GetArg(SID_FILLFRAME); + if ( pFrmItem ) + xTargetFrame = pFrmItem->GetFrame(); + + SdOptions* pOpt = GetSdOptions(DocumentType::Impress); + bool bStartWithTemplate = pOpt->IsStartWithTemplate(); + + bool bNewDocDirect = rReq.GetSlot() == SID_NEWSD; + + if( bNewDocDirect ) + { + //we start without wizard + + //check whether we should load a template document + OUString aStandardTemplate( SfxObjectFactory::GetStandardTemplate( u"com.sun.star.presentation.PresentationDocument" ) ); + + if( !aStandardTemplate.isEmpty() ) + { + //load a template document + pFrame = CreateFromTemplate(aStandardTemplate, xTargetFrame, true); + } + else + { + //create an empty document + pFrame = CreateEmptyDocument( xTargetFrame ); + } + } + + if (bStartWithTemplate) + { + //Launch TemplateSelectionDialog + SfxTemplateSelectionDlg aTemplDlg(SfxGetpApp()->GetTopWindow()); + aTemplDlg.run(); + + //check to disable the dialog + pOpt->SetStartWithTemplate( aTemplDlg.IsStartWithTemplate() ); + + //pFrame is loaded with the desired template + if (!aTemplDlg.getTemplatePath().isEmpty()) + pFrame = CreateFromTemplate(aTemplDlg.getTemplatePath(), xTargetFrame, false); + + // show tip-of-the-day dialog if it was deferred because SfxTemplateSelectionDlg + // was open + if (pFrame && SfxApplication::IsTipOfTheDayDue() && !SfxApplication::IsHeadlessOrUITest()) + { + if (SfxDispatcher* pDispatcher = GetDispatcher()) + { + // tdf#127946 pass in argument for dialog parent + SfxUnoFrameItem aDocFrame(SID_FILLFRAME, pFrame->GetFrameInterface()); + pDispatcher->ExecuteList(SID_TIPOFTHEDAY, SfxCallMode::SLOT, {}, { &aDocFrame }); + } + } + } + } + + return pFrame; +} + +SfxFrame* SdModule::CreateEmptyDocument( const Reference< XFrame >& i_rFrame ) +{ + SfxFrame* pFrame = nullptr; + + SfxObjectShellLock xDocShell; + ::sd::DrawDocShell* pNewDocSh; + xDocShell = pNewDocSh = new ::sd::DrawDocShell(SfxObjectCreateMode::STANDARD,false,DocumentType::Impress); + pNewDocSh->DoInitNew(); + SdDrawDocument* pDoc = pNewDocSh->GetDoc(); + if (pDoc) + { + pDoc->CreateFirstPages(); + pDoc->StopWorkStartupDelay(); + } + if (pNewDocSh->GetMedium() && pNewDocSh->GetMedium()->GetItemSet()) + pNewDocSh->GetMedium()->GetItemSet()->Put(SfxBoolItem(SID_REPLACEABLE, true)); + + SfxViewFrame* pViewFrame = SfxViewFrame::LoadDocumentIntoFrame( *pNewDocSh, i_rFrame ); + OSL_ENSURE( pViewFrame, "SdModule::CreateEmptyDocument: no view frame - was the document really loaded?" ); + pFrame = pViewFrame ? &pViewFrame->GetFrame() : nullptr; + + return pFrame; +} + +//===== OutlineToImpressFinalize ============================================== + +namespace { + +OutlineToImpressFinalizer::OutlineToImpressFinalizer ( + ::sd::ViewShellBase& rBase, + SdDrawDocument& rDocument, + SvLockBytes const & rBytes) + : mrBase(rBase), + mrDocument(rDocument) +{ + // The given stream has a lifetime shorter than this new + // OutlineToImpressFinalizer object. Therefore a local copy of the + // stream is created. + const SvStream* pStream (rBytes.GetStream()); + if (pStream == nullptr) + return; + + // Create a memory stream and prepare to fill it with the content of + // the original stream. + mpStream = std::make_shared(); + static const std::size_t nBufferSize = 4096; + ::std::unique_ptr pBuffer (new sal_Int8[nBufferSize]); + + sal_uInt64 nReadPosition(0); + bool bLoop (true); + while (bLoop) + { + // Read the next part of the original stream. + std::size_t nReadByteCount (0); + const ErrCode nErrorCode ( + rBytes.ReadAt( + nReadPosition, + pBuffer.get(), + nBufferSize, + &nReadByteCount)); + + // Check the error code and stop copying the stream data when an + // error has occurred. + if (nErrorCode == ERRCODE_NONE) + { + if (nReadByteCount == 0) + bLoop = false; + } + else if (nErrorCode == ERRCODE_IO_PENDING) + ; + else + { + bLoop = false; + nReadByteCount = 0; + } + + // Append the read bytes to the end of the memory stream. + if (nReadByteCount > 0) + { + mpStream->WriteBytes(pBuffer.get(), nReadByteCount); + nReadPosition += nReadByteCount; + } + } + + // Rewind the memory stream so that in the operator() method its + // content is properly read. + mpStream->Seek(STREAM_SEEK_TO_BEGIN); +} + +void OutlineToImpressFinalizer::operator() (bool) +{ + // Fetch the new outline view shell. + ::sd::OutlineViewShell* pOutlineShell + = dynamic_cast(FrameworkHelper::Instance(mrBase)->GetViewShell(FrameworkHelper::msCenterPaneURL).get()); + + if (pOutlineShell != nullptr && mpStream != nullptr) + { + sd::OutlineView* pView = static_cast(pOutlineShell->GetView()); + // mba: the stream can't contain any relative URLs, because we don't + // have any information about a BaseURL! + pOutlineShell->ReadRtf(*mpStream); + + // Call UpdatePreview once for every slide to resync the + // document with the outliner of the OutlineViewShell. + sal_uInt16 nPageCount (mrDocument.GetSdPageCount(PageKind::Standard)); + for (sal_uInt16 nIndex=0; nIndexSetActualPage(pPage); + pOutlineShell->UpdatePreview(pPage); + } + // Select the first slide. + SdPage* pPage = mrDocument.GetSdPage(0, PageKind::Standard); + pView->SetActualPage(pPage); + pOutlineShell->UpdatePreview(pPage); + } + + // Undo-Stack needs to be cleared, else the user may remove the + // only drawpage and this is a state we cannot handle ATM. + ::sd::DrawDocShell* pDocShell = mrDocument.GetDocSh(); + if( pDocShell ) + pDocShell->ClearUndoBuffer(); +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdmod2.cxx b/sd/source/ui/app/sdmod2.cxx new file mode 100644 index 000000000..cccf42517 --- /dev/null +++ b/sd/source/ui/app/sdmod2.cxx @@ -0,0 +1,809 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** retrieves the page that is currently painted. This will only be the master page + if the current drawn view only shows the master page*/ +static SdPage* GetCurrentPage( sd::ViewShell const * pViewSh, EditFieldInfo const * pInfo, bool& bMasterView ) +{ + if( !pInfo ) + return nullptr; + + bMasterView = false; + SdPage* pPage = dynamic_cast< SdPage* >( pInfo->GetSdrPage() ); + SdrOutliner* pOutliner = dynamic_cast< SdrOutliner* >( pInfo->GetOutliner() ); + + // special case, someone already set the current page on the EditFieldInfo + // This is used from the svx::UnoGraphicsExporter f.e. + if( pPage ) + { + bMasterView = false; + return pPage; + } + + // first try to check if we are inside the outline view + sd::OutlineView* pSdView = nullptr; + if( auto pOutlineViewShell = dynamic_cast(pViewSh) ) + pSdView = static_cast(pOutlineViewShell->GetView()); + + if (pSdView != nullptr && (pOutliner == &pSdView->GetOutliner())) + { + // outline mode + int nPgNum = 0; + Outliner& rOutl = pSdView->GetOutliner(); + tools::Long nPos = pInfo->GetPara(); + sal_Int32 nParaPos = 0; + + for( Paragraph* pPara = rOutl.GetParagraph( 0 ); pPara && nPos >= 0; pPara = rOutl.GetParagraph( ++nParaPos ), nPos-- ) + { + if( Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) ) + nPgNum++; + } + + pPage = pViewSh->GetDoc()->GetSdPage( static_cast(nPgNum), PageKind::Standard ); + } + else + { + // draw mode, slide mode and preview. Get the processed page from the outliner + if(pOutliner) + { + pPage = dynamic_cast< SdPage* >(const_cast< SdrPage* >(pOutliner->getVisualizedPage())); + } + + // The path using GetPaintingPageView() and GetCurrentPaintingDisplayInfo() + // is no longer needed. I debugged and checked all usages of PageNumber decompositions + // which all use the new possibility of setting the visualized page at the SdrOutliner. + + // if all else failed, geht the current page from the object that is + // currently formatted from the document + if(!pPage) + { + const SdrTextObj* pTextObj = (pViewSh && pViewSh->GetDoc()) ? pViewSh->GetDoc()->GetFormattingTextObj() : nullptr; + + if( pTextObj ) + { + pPage = dynamic_cast< SdPage* >( pTextObj->getSdrPageFromSdrObject() ); + } + } + + if(pPage) + { + bMasterView = pPage->IsMasterPage(); + } + } + + return pPage; +} + +/** + * Link for CalcFieldValue of Outliners + */ +IMPL_LINK(SdModule, CalcFieldValueHdl, EditFieldInfo*, pInfo, void) +{ + if (!pInfo) + return; + + const SvxFieldData* pField = pInfo->GetField().GetField(); + ::sd::DrawDocShell* pDocShell = nullptr; + SdDrawDocument* pDoc = nullptr; + + SdrOutliner* pSdrOutliner = dynamic_cast< SdrOutliner* >( pInfo->GetOutliner() ); + if( pSdrOutliner ) + { + const SdrTextObj* pTextObj = pSdrOutliner->GetTextObj(); + + if( pTextObj ) + pDoc = dynamic_cast< SdDrawDocument* >( &pTextObj->getSdrModelFromSdrObject() ); + + if( pDoc ) + pDocShell = pDoc->GetDocSh(); + } + + if( !pDocShell ) + pDocShell = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + + const SvxDateField* pDateField = nullptr; + const SvxExtTimeField* pExtTimeField = nullptr; + const SvxExtFileField* pExtFileField = nullptr; + const SvxAuthorField* pAuthorField = nullptr; + const SvxURLField* pURLField = nullptr; + + const editeng::CustomPropertyField* pCustomPropertyField = nullptr; + + if( (pDateField = dynamic_cast< const SvxDateField* >(pField)) != nullptr ) + { + LanguageType eLang = pInfo->GetOutliner()->GetLanguage( pInfo->GetPara(), pInfo->GetPos() ); + pInfo->SetRepresentation( pDateField->GetFormatted( *GetNumberFormatter(), eLang ) ); + } + else if( (pExtTimeField = dynamic_cast< const SvxExtTimeField *>(pField)) != nullptr ) + { + LanguageType eLang = pInfo->GetOutliner()->GetLanguage( pInfo->GetPara(), pInfo->GetPos() ); + pInfo->SetRepresentation( pExtTimeField->GetFormatted( *GetNumberFormatter(), eLang ) ); + } + else if( (pExtFileField = dynamic_cast< const SvxExtFileField * >(pField)) != nullptr ) + { + if( pDocShell && (pExtFileField->GetType() != SvxFileType::Fix) ) + { + OUString aName; + if( pDocShell->HasName() ) + aName = pDocShell->GetMedium()->GetName(); + else + aName = pDocShell->GetName(); + + const_cast< SvxExtFileField* >(pExtFileField)->SetFile( aName ); + } + pInfo->SetRepresentation( pExtFileField->GetFormatted() ); + + } + else if( (pAuthorField = dynamic_cast< const SvxAuthorField* >( pField )) != nullptr ) + { + if( pAuthorField->GetType() != SvxAuthorType::Fix ) + { + SvtUserOptions aUserOptions; + SvxAuthorField aAuthorField( + aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID(), + pAuthorField->GetType(), pAuthorField->GetFormat() ); + + *const_cast< SvxAuthorField* >(pAuthorField) = aAuthorField; + } + pInfo->SetRepresentation( pAuthorField->GetFormatted() ); + + } + else if( dynamic_cast< const SvxPageField* >(pField) ) + { + OUString aRepresentation(" "); + + ::sd::ViewShell* pViewSh = pDocShell ? pDocShell->GetViewShell() : nullptr; + if(pViewSh == nullptr) + { + ::sd::ViewShellBase* pBase = dynamic_cast< ::sd::ViewShellBase *>( SfxViewShell::Current() ); + if(pBase) + pViewSh = pBase->GetMainViewShell().get(); + } + if( !pDoc && pViewSh ) + pDoc = pViewSh->GetDoc(); + + bool bMasterView; + SdPage* pPage = GetCurrentPage( pViewSh, pInfo, bMasterView ); + + if( pPage && pDoc && !bMasterView ) + { + int nPgNum; + + if( (pPage->GetPageKind() == PageKind::Handout) && pViewSh ) + { + nPgNum = pViewSh->GetPrintedHandoutPageNum(); + } + else + { + nPgNum = (pPage->GetPageNum() - 1) / 2 + 1; + } + aRepresentation = pDoc->CreatePageNumValue(static_cast(nPgNum)); + } + else + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_NUMBER); + + pInfo->SetRepresentation( aRepresentation ); + } + else if( dynamic_cast< const SvxPageTitleField* >(pField) ) + { + OUString aRepresentation(" "); + + ::sd::ViewShell* pViewSh = pDocShell ? pDocShell->GetViewShell() : nullptr; + if(pViewSh == nullptr) + { + ::sd::ViewShellBase* pBase = dynamic_cast< ::sd::ViewShellBase *>( SfxViewShell::Current() ); + if(pBase) + pViewSh = pBase->GetMainViewShell().get(); + } + if( !pDoc && pViewSh ) + pDoc = pViewSh->GetDoc(); + + bool bMasterView; + SdPage* pPage = GetCurrentPage( pViewSh, pInfo, bMasterView ); + + if( pPage && pDoc && !bMasterView ) + { + aRepresentation = pPage->GetName(); + } + else + { + DocumentType eDocType = pDoc ? pDoc->GetDocumentType() : DocumentType::Impress; + aRepresentation = ( ( eDocType == DocumentType::Impress ) + ? SdResId(STR_FIELD_PLACEHOLDER_SLIDENAME) + : SdResId(STR_FIELD_PLACEHOLDER_PAGENAME) ); + } + + pInfo->SetRepresentation( aRepresentation ); + } + else if( dynamic_cast< const SvxPagesField* >(pField) ) + { + OUString aRepresentation(" "); + + ::sd::ViewShell* pViewSh = pDocShell ? pDocShell->GetViewShell() : nullptr; + if(pViewSh == nullptr) + { + ::sd::ViewShellBase* pBase = dynamic_cast< ::sd::ViewShellBase *>( SfxViewShell::Current() ); + if(pBase) + pViewSh = pBase->GetMainViewShell().get(); + } + if( !pDoc && pViewSh ) + pDoc = pViewSh->GetDoc(); + + bool bMasterView; + SdPage* pPage = GetCurrentPage( pViewSh, pInfo, bMasterView ); + + sal_uInt16 nPageCount = 0; + + if( !bMasterView ) + { + if( pPage && (pPage->GetPageKind() == PageKind::Handout) && pViewSh ) + { + nPageCount = pViewSh->GetPrintedHandoutPageCount(); + } + else if( pDoc ) + { + nPageCount = pDoc->GetActiveSdPageCount(); + } + } + + if( nPageCount > 0 ) + aRepresentation = pDoc->CreatePageNumValue(nPageCount); + else + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_COUNT); + + pInfo->SetRepresentation( aRepresentation ); + } + else if( (pURLField = dynamic_cast< const SvxURLField* >(pField)) != nullptr ) + { + switch ( pURLField->GetFormat() ) + { + case SvxURLFormat::AppDefault: //!!! adjustable at App??? + case SvxURLFormat::Repr: + pInfo->SetRepresentation( pURLField->GetRepresentation() ); + break; + + case SvxURLFormat::Url: + pInfo->SetRepresentation( pURLField->GetURL() ); + break; + } + + const OUString& aURL = pURLField->GetURL(); + + svtools::ColorConfig aConfig; + svtools::ColorConfigEntry eEntry = + INetURLHistory::GetOrCreate()->QueryUrl( aURL ) ? svtools::LINKSVISITED : svtools::LINKS; + pInfo->SetTextColor( aConfig.GetColorValue(eEntry).nColor ); + } + else if ( dynamic_cast< const SdrMeasureField* >(pField)) + { + pInfo->SetFieldColor(std::optional()); // clear the field color + } + else if ((pCustomPropertyField = dynamic_cast(pField)) != nullptr) + { + try + { + SfxObjectShell* pObjSh = SfxObjectShell::Current(); + if (pObjSh && pObjSh->IsLoadingFinished()) + { + auto pNonConstCustomPropertyField = const_cast(pCustomPropertyField); + OUString sCurrent = pNonConstCustomPropertyField->GetFormatted(pObjSh->getDocProperties()); + pInfo->SetRepresentation(sCurrent); + } + else + pInfo->SetRepresentation(pCustomPropertyField->GetCurrentPresentation()); + } + catch (...) + { + pInfo->SetRepresentation(pCustomPropertyField->GetCurrentPresentation()); + } + } + else + { + OUString aRepresentation; + + bool bHeaderField = dynamic_cast< const SvxHeaderField* >( pField ) != nullptr; + bool bFooterField = !bHeaderField && (dynamic_cast< const SvxFooterField* >( pField ) != nullptr ); + bool bDateTimeField = !bHeaderField && !bFooterField && (dynamic_cast< const SvxDateTimeField* >( pField ) != nullptr); + + if( bHeaderField || bFooterField || bDateTimeField ) + { + sd::ViewShell* pViewSh = pDocShell ? pDocShell->GetViewShell() : nullptr; + bool bMasterView = false; + SdPage* pPage = GetCurrentPage( pViewSh, pInfo, bMasterView ); + + if( (pPage == nullptr) || bMasterView ) + { + if( bHeaderField ) + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_HEADER); + else if (bFooterField ) + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_FOOTER); + else if (bDateTimeField ) + aRepresentation = SdResId(STR_FIELD_PLACEHOLDER_DATETIME); + } + else + { + const sd::HeaderFooterSettings &rSettings = pPage->getHeaderFooterSettings(); + + if( bHeaderField ) + { + aRepresentation = rSettings.maHeaderText; + } + else if( bFooterField ) + { + aRepresentation = rSettings.maFooterText; + } + else if( bDateTimeField ) + { + if( rSettings.mbDateTimeIsFixed ) + { + aRepresentation = rSettings.maDateTimeText; + } + else + { + DateTime aDateTime( DateTime::SYSTEM ); + LanguageType eLang = pInfo->GetOutliner()->GetLanguage( pInfo->GetPara(), pInfo->GetPos() ); + aRepresentation = SvxDateTimeField::GetFormatted( aDateTime, aDateTime, + rSettings.meDateFormat, rSettings.meTimeFormat, *GetNumberFormatter(), eLang ); + } + } + } + } + else + { + OSL_FAIL("sd::SdModule::CalcFieldValueHdl(), unknown field type!"); + } + + if( aRepresentation.isEmpty() ) // TODO: Edit engine doesn't handle empty fields? + aRepresentation = " "; + pInfo->SetRepresentation( aRepresentation ); + } +} + +/** + * virtual methods for option dialog + */ +std::optional SdModule::CreateItemSet( sal_uInt16 nSlot ) +{ + ::sd::FrameView* pFrameView = nullptr; + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = nullptr; + + // Here we set the DocType of the option dialog (not document!) + DocumentType eDocType = DocumentType::Impress; + if( nSlot == SID_SD_GRAPHIC_OPTIONS ) + eDocType = DocumentType::Draw; + + if (pDocSh) + { + pDoc = pDocSh->GetDoc(); + + // If the option dialog is identical to the document type, + // we can pass the FrameView too: + if( pDoc && eDocType == pDoc->GetDocumentType() ) + pFrameView = pDocSh->GetFrameView(); + + ::sd::ViewShell* pViewShell = pDocSh->GetViewShell(); + if (pViewShell != nullptr) + pViewShell->WriteFrameViewData(); + } + + SdOptions* pOptions = GetSdOptions(eDocType); + + // Pool has by default MapUnit Twips (Awgh!) + SfxItemPool& rPool = GetPool(); + rPool.SetDefaultMetric( MapUnit::Map100thMM ); + + SfxItemSetFixed< + SID_ATTR_GRID_OPTIONS, SID_ATTR_GRID_OPTIONS, + SID_ATTR_METRIC, SID_ATTR_METRIC, + SID_ATTR_DEFTABSTOP, SID_ATTR_DEFTABSTOP, + ATTR_OPTIONS_LAYOUT, ATTR_OPTIONS_SCALE_END> aRet(rPool); + + // TP_OPTIONS_LAYOUT: + aRet.Put( SdOptionsLayoutItem( pOptions, pFrameView ) ); + + sal_uInt16 nDefTab = 0; + if( pFrameView) + nDefTab = pDoc->GetDefaultTabulator(); + else + nDefTab = pOptions->GetDefTab(); + aRet.Put( SfxUInt16Item( SID_ATTR_DEFTABSTOP, nDefTab ) ); + + FieldUnit nMetric = FieldUnit(0xffff); + if( pFrameView) + nMetric = pDoc->GetUIUnit(); + else + nMetric = static_cast(pOptions->GetMetric()); + + if( nMetric == FieldUnit(0xffff) ) + nMetric = GetFieldUnit(); + + aRet.Put( SfxUInt16Item( SID_ATTR_METRIC, static_cast(nMetric) ) ); + + // TP_OPTIONS_MISC: + SdOptionsMiscItem aSdOptionsMiscItem( pOptions, pFrameView ); + if ( pFrameView ) + { + aSdOptionsMiscItem.GetOptionsMisc().SetSummationOfParagraphs( pDoc->IsSummationOfParagraphs() ); + aSdOptionsMiscItem.GetOptionsMisc().SetPrinterIndependentLayout ( + static_cast(pDoc->GetPrinterIndependentLayout())); + } + aRet.Put( aSdOptionsMiscItem ); + + // TP_OPTIONS_SNAP: + aRet.Put( SdOptionsSnapItem( pOptions, pFrameView ) ); + + // TP_SCALE: + sal_uInt32 nW = 10; + sal_uInt32 nH = 10; + sal_Int32 nX; + sal_Int32 nY; + if( pDocSh ) + { + SdrPage* pPage = pDoc->GetSdPage(0, PageKind::Standard); + Size aSize(pPage->GetSize()); + nW = aSize.Width(); + nH = aSize.Height(); + } + + if(pFrameView) + { + const Fraction& rFraction = pDoc->GetUIScale(); + nX=rFraction.GetNumerator(); + nY=rFraction.GetDenominator(); + } + else + { + // Get options from configuration file + pOptions->GetScale( nX, nY ); + } + + aRet.Put( SfxInt32Item( ATTR_OPTIONS_SCALE_X, nX ) ); + aRet.Put( SfxInt32Item( ATTR_OPTIONS_SCALE_Y, nY ) ); + aRet.Put( SfxUInt32Item( ATTR_OPTIONS_SCALE_WIDTH, nW ) ); + aRet.Put( SfxUInt32Item( ATTR_OPTIONS_SCALE_HEIGHT, nH ) ); + + // TP_OPTIONS_PRINT: + aRet.Put( SdOptionsPrintItem( pOptions ) ); + + // RID_SVXPAGE_GRID: + aRet.Put( SdOptionsGridItem( pOptions ) ); + + return aRet; +} + +void SdModule::ApplyItemSet( sal_uInt16 nSlot, const SfxItemSet& rSet ) +{ + bool bNewDefTab = false; + bool bNewPrintOptions = false; + bool bMiscOptions = false; + + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = nullptr; + // Here we set the DocType of the option dialog (not document!) + DocumentType eDocType = DocumentType::Impress; + if( nSlot == SID_SD_GRAPHIC_OPTIONS ) + eDocType = DocumentType::Draw; + + ::sd::ViewShell* pViewShell = nullptr; + + if (pDocSh) + { + pDoc = pDocSh->GetDoc(); + + pViewShell = pDocSh->GetViewShell(); + if (pViewShell != nullptr) + pViewShell->WriteFrameViewData(); + } + SdOptions* pOptions = GetSdOptions(eDocType); + // Grid + if( const SdOptionsGridItem* pGridItem = static_cast(rSet.GetItemIfSet( SID_ATTR_GRID_OPTIONS, false )) ) + { + pGridItem->SetOptions( pOptions ); + } + + // Layout + if( const SdOptionsLayoutItem* pLayoutItem = rSet.GetItemIfSet( ATTR_OPTIONS_LAYOUT, false )) + { + pLayoutItem->SetOptions( pOptions ); + } + + // Metric + if( const SfxUInt16Item* pItem = rSet.GetItemIfSet( SID_ATTR_METRIC, false ) ) + { + if( pDoc && eDocType == pDoc->GetDocumentType() ) + PutItem( *pItem ); + pOptions->SetMetric( pItem->GetValue() ); + } + sal_uInt16 nDefTab = pOptions->GetDefTab(); + // Default-Tabulator + if( const SfxUInt16Item* pItem = rSet.GetItemIfSet( SID_ATTR_DEFTABSTOP, false ) ) + { + nDefTab = pItem->GetValue(); + pOptions->SetDefTab( nDefTab ); + + bNewDefTab = true; + } + + // Scale + if( const SfxInt32Item* pItem = rSet.GetItemIfSet( ATTR_OPTIONS_SCALE_X, false ) ) + { + sal_Int32 nX = pItem->GetValue(); + pItem = rSet.GetItemIfSet( ATTR_OPTIONS_SCALE_Y, false ); + if( pItem ) + { + sal_Int32 nY = pItem->GetValue(); + pOptions->SetScale( nX, nY ); + + // Apply to document only if doc type match + if( pDocSh && pDoc && eDocType == pDoc->GetDocumentType() ) + { + pDoc->SetUIScale( Fraction( nX, nY ) ); + if( pViewShell ) + pViewShell->SetRuler( pViewShell->HasRuler() ); + } + } + } + + // Misc + const SdOptionsMiscItem* pMiscItem = rSet.GetItemIfSet( ATTR_OPTIONS_MISC, false); + if( pMiscItem ) + { + pMiscItem->SetOptions( pOptions ); + bMiscOptions = true; + } + + // Snap + const SdOptionsSnapItem* pSnapItem = rSet.GetItemIfSet( ATTR_OPTIONS_SNAP, false ); + if( pSnapItem ) + { + pSnapItem->SetOptions( pOptions ); + } + + SfxItemSetFixed aPrintSet( GetPool() ); + + // Print + const SdOptionsPrintItem* pPrintItem = rSet.GetItemIfSet( ATTR_OPTIONS_PRINT, false); + if( pPrintItem ) + { + pPrintItem->SetOptions( pOptions ); + + // set PrintOptionsSet + SdOptionsPrintItem aPrintItem( pOptions ); + SfxFlagItem aFlagItem( SID_PRINTER_CHANGESTODOC ); + SfxPrinterChangeFlags nFlags = + (aPrintItem.GetOptionsPrint().IsWarningSize() ? SfxPrinterChangeFlags::CHG_SIZE : SfxPrinterChangeFlags::NONE) | + (aPrintItem.GetOptionsPrint().IsWarningOrientation() ? SfxPrinterChangeFlags::CHG_ORIENTATION : SfxPrinterChangeFlags::NONE); + aFlagItem.SetValue( static_cast(nFlags) ); + + aPrintSet.Put( aPrintItem ); + aPrintSet.Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, aPrintItem.GetOptionsPrint().IsWarningPrinter() ) ); + aPrintSet.Put( aFlagItem ); + + bNewPrintOptions = true; + } + + // Only if also the document type matches... + if( pDocSh && pDoc && eDocType == pDoc->GetDocumentType() ) + { + if( bNewPrintOptions ) + { + pDocSh->GetPrinter(true)->SetOptions( aPrintSet ); + } + + // set DefTab at Model + if( bNewDefTab ) + { + SdDrawDocument* pDocument = pDocSh->GetDoc(); + pDocument->SetDefaultTabulator( nDefTab ); + + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + pOutl->SetDefTab( nDefTab ); + + SdOutliner* pInternalOutl = pDocument->GetInternalOutliner( false ); + if( pInternalOutl ) + pInternalOutl->SetDefTab( nDefTab ); + } + if ( bMiscOptions ) + { + pDoc->SetSummationOfParagraphs( pMiscItem->GetOptionsMisc().IsSummationOfParagraphs() ); + EEControlBits nSum = pMiscItem->GetOptionsMisc().IsSummationOfParagraphs() ? EEControlBits::ULSPACESUMMATION : EEControlBits::NONE; + EEControlBits nCntrl; + + SdDrawDocument* pDocument = pDocSh->GetDoc(); + SdrOutliner& rOutl = pDocument->GetDrawOutliner(); + nCntrl = rOutl.GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + rOutl.SetControlWord( nCntrl | nSum ); + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + { + nCntrl = pOutl->GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + pOutl->SetControlWord( nCntrl | nSum ); + } + pOutl = pDocument->GetInternalOutliner( false ); + if( pOutl ) + { + nCntrl = pOutl->GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + pOutl->SetControlWord( nCntrl | nSum ); + } + + // Set printer independent layout mode. + if( pDoc->GetPrinterIndependentLayout() != pMiscItem->GetOptionsMisc().GetPrinterIndependentLayout() ) + pDoc->SetPrinterIndependentLayout (pMiscItem->GetOptionsMisc().GetPrinterIndependentLayout()); + } + } + + pOptions->StoreConfig(); + + // Only if also the document type matches... + if( pDocSh && pDoc && eDocType == pDoc->GetDocumentType() ) + { + FieldUnit eUIUnit = static_cast(pOptions->GetMetric()); + pDoc->SetUIUnit(eUIUnit); + + if (pViewShell) + { + // make sure no one is in text edit mode, cause there + // are some pointers remembered else (!) + if(pViewShell->GetView()) + pViewShell->GetView()->SdrEndTextEdit(); + + ::sd::FrameView* pFrame = pViewShell->GetFrameView(); + pFrame->Update(pOptions); + pViewShell->ReadFrameViewData(pFrame); + pViewShell->SetUIUnit(eUIUnit); + pViewShell->SetDefTabHRuler( nDefTab ); + } + } + + if( pViewShell && pViewShell->GetViewFrame() ) + pViewShell->GetViewFrame()->GetBindings().InvalidateAll( true ); +} + +std::unique_ptr SdModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet ) +{ + std::unique_ptr xRet; + SfxAllItemSet aSet(*(rSet.GetPool())); + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + + switch(nId) + { + case SID_SD_TP_CONTENTS: + case SID_SI_TP_CONTENTS: + { + ::CreateTabPage fnCreatePage = pFact->GetSdOptionsContentsTabPageCreatorFunc(); + if( fnCreatePage ) + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + } + break; + case SID_SD_TP_SNAP: + case SID_SI_TP_SNAP: + { + ::CreateTabPage fnCreatePage = pFact->GetSdOptionsSnapTabPageCreatorFunc(); + if( fnCreatePage ) + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + } + break; + case SID_SD_TP_PRINT: + case SID_SI_TP_PRINT: + { + ::CreateTabPage fnCreatePage = pFact->GetSdPrintOptionsTabPageCreatorFunc(); + if( fnCreatePage ) + { + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + if(SID_SD_TP_PRINT == nId) + aSet.Put (SfxUInt32Item(SID_SDMODE_FLAG,SD_DRAW_MODE)); + xRet->PageCreated(aSet); + } + } + break; + case SID_SI_TP_MISC: + case SID_SD_TP_MISC: + { + ::CreateTabPage fnCreatePage = pFact->GetSdOptionsMiscTabPageCreatorFunc(); + if( fnCreatePage ) + { + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + if(SID_SD_TP_MISC == nId) + aSet.Put (SfxUInt32Item(SID_SDMODE_FLAG,SD_DRAW_MODE)); + else + aSet.Put (SfxUInt32Item(SID_SDMODE_FLAG,SD_IMPRESS_MODE)); + xRet->PageCreated(aSet); + } + } + break; + case RID_SVXPAGE_TEXTANIMATION : + { + SfxAbstractDialogFactory* pSfxFact = SfxAbstractDialogFactory::Create(); + ::CreateTabPage fnCreatePage = pSfxFact->GetTabPageCreatorFunc( nId ); + if ( fnCreatePage ) + xRet = (*fnCreatePage)( pPage, pController, &rSet ); + } + break; + } + DBG_ASSERT( xRet, "SdModule::CreateTabPage(): no valid ID for TabPage!" ); + + return xRet; +} + +std::optional SdModule::CreateStyleFamilies() +{ + SfxStyleFamilies aStyleFamilies; + + aStyleFamilies.emplace_back(SfxStyleFamily::Para, + SdResId(STR_GRAPHICS_STYLE_FAMILY), + BMP_STYLES_FAMILY_GRAPHICS, + RID_GRAPHICSTYLEFAMILY, SD_MOD()->GetResLocale()); + + aStyleFamilies.emplace_back(SfxStyleFamily::Pseudo, + SdResId(STR_PRESENTATIONS_STYLE_FAMILY), + BMP_STYLES_FAMILY_PRESENTATIONS, + RID_PRESENTATIONSTYLEFAMILY, SD_MOD()->GetResLocale()); + + return aStyleFamilies; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdpopup.cxx b/sd/source/ui/app/sdpopup.cxx new file mode 100644 index 000000000..4aafd2848 --- /dev/null +++ b/sd/source/ui/app/sdpopup.cxx @@ -0,0 +1,318 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Popup menu for editing of field command + */ +SdFieldPopup::SdFieldPopup(const SvxFieldData* pInField, LanguageType eLanguage) + : m_xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/fieldmenu.ui")) + , m_xPopup(m_xBuilder->weld_menu("menu")) + , m_pField(pInField) +{ + Fill(eLanguage); +} + +SdFieldPopup::~SdFieldPopup() +{ +} + +void SdFieldPopup::Fill( LanguageType eLanguage ) +{ + sal_uInt16 nID = 1; + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FIX)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_VAR)); + m_xPopup->append_separator("separator1"); + + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + SvxDateField aDateField( *pDateField ); + + if (pDateField->GetType() == SvxDateType::Fix) + m_xPopup->set_active("1", true); + else + m_xPopup->set_active("2", true); + + //SvxDateFormat::AppDefault, // is not used + //SvxDateFormat::System, // is not used + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_STANDARD_SMALL)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_STANDARD_BIG)); + + SvNumberFormatter* pNumberFormatter = SD_MOD()->GetNumberFormatter(); + aDateField.SetFormat( SvxDateFormat::A ); // 13.02.96 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + aDateField.SetFormat( SvxDateFormat::B ); // 13.02.1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + aDateField.SetFormat( SvxDateFormat::C ); // 13.Feb 1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + + aDateField.SetFormat( SvxDateFormat::D ); // 13.Februar 1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + aDateField.SetFormat( SvxDateFormat::E ); // Die, 13.Februar 1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + aDateField.SetFormat( SvxDateFormat::F ); // Dienstag, 13.Februar 1996 + m_xPopup->append_radio(OUString::number(nID++), aDateField.GetFormatted(*pNumberFormatter, eLanguage)); + + m_xPopup->set_active(OString::number(static_cast( pDateField->GetFormat() ) + 1), true); // - 2 + 3 ! + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + SvxExtTimeField aTimeField( *pTimeField ); + + if( pTimeField->GetType() == SvxTimeType::Fix ) + m_xPopup->set_active("1", true); + else + m_xPopup->set_active("2", true); + + //SvxTimeFormat::AppDefault, // is not used + //SvxTimeFormat::System, // is not used + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_STANDARD_NORMAL)); + + SvNumberFormatter* pNumberFormatter = SD_MOD()->GetNumberFormatter(); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM ); // 13:49 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM_SS ); // 13:49:38 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM_SS_00 ); // 13:49:38.78 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + + aTimeField.SetFormat( SvxTimeFormat::HH12_MM ); // 01:49 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM_SS ); // 01:49:38 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM_SS_00 ); // 01:49:38.78 + m_xPopup->append_radio(OUString::number(nID++), aTimeField.GetFormatted(*pNumberFormatter, eLanguage)); + //SvxTimeFormat::HH12_MM_AMPM, // 01:49 PM + //SvxTimeFormat::HH12_MM_SS_AMPM, // 01:49:38 PM + //SvxTimeFormat::HH12_MM_SS_00_AMPM // 01:49:38.78 PM + + m_xPopup->set_active(OString::number(static_cast( pTimeField->GetFormat() ) + 1), true); // - 2 + 3 ! + } + else if( auto pFileField = dynamic_cast< const SvxExtFileField *>( m_pField ) ) + { + //SvxExtFileField aFileField( *pFileField ); + + if( pFileField->GetType() == SvxFileType::Fix ) + m_xPopup->set_active("1", true); + else + m_xPopup->set_active("2", true); + + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FILEFORMAT_NAME_EXT)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FILEFORMAT_FULLPATH)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FILEFORMAT_PATH)); + m_xPopup->append_radio(OUString::number(nID++), SdResId(STR_FILEFORMAT_NAME)); + + m_xPopup->set_active(OString::number(static_cast( pFileField->GetFormat() ) + 3), true); + } + else if( auto pAuthorField = dynamic_cast< const SvxAuthorField *>( m_pField ) ) + { + SvxAuthorField aAuthorField( *pAuthorField ); + + if( pAuthorField->GetType() == SvxAuthorType::Fix ) + m_xPopup->set_active("1", true); + else + m_xPopup->set_active("2", true); + + for( sal_uInt16 i = 0; i < 4; i++ ) + { + aAuthorField.SetFormat( static_cast(i) ); + m_xPopup->append_radio(OUString::number(nID++), aAuthorField.GetFormatted()); + } + m_xPopup->set_active(OString::number(static_cast( pAuthorField->GetFormat() ) + 3), true); + } +} + +void SdFieldPopup::Execute(weld::Window* pParent, const tools::Rectangle& rRect) +{ + OString sIdent = m_xPopup->popup_at_rect(pParent, rRect); + if (sIdent.isEmpty()) + return; + + if (sIdent == "1" || sIdent == "2") + { + m_xPopup->set_active("1", sIdent == "1"); + m_xPopup->set_active("2", sIdent == "2"); + } + else + { + int nCount = m_xPopup->n_children(); + for (int i = 3; i < nCount; i++) + m_xPopup->set_active( + OString::number(i), sIdent == std::string_view(OString::number(i))); + } +} + +/** + * Returns a new field, owned by caller. + * Returns NULL if nothing changed. + */ +SvxFieldData* SdFieldPopup::GetField() +{ + SvxFieldData* pNewField = nullptr; + + sal_uInt16 nCount = m_xPopup->n_children(); + + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + SvxDateType eType; + SvxDateFormat eFormat; + sal_uInt16 i; + + if (m_xPopup->get_active("1")) + eType = SvxDateType::Fix; + else + eType = SvxDateType::Var; + + for( i = 3; i < nCount; i++ ) + { + if (m_xPopup->get_active(OString::number(i))) + break; + } + eFormat = static_cast( i - 1 ); + + if( pDateField->GetFormat() != eFormat || + pDateField->GetType() != eType ) + { + pNewField = new SvxDateField( *pDateField ); + static_cast( pNewField )->SetType( eType ); + static_cast( pNewField )->SetFormat( eFormat ); + + if( (pDateField->GetType() == SvxDateType::Var) && (eType == SvxDateType::Fix) ) + { + Date aDate( Date::SYSTEM ); + static_cast( pNewField )->SetFixDate( aDate ); + } + } + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + SvxTimeType eType; + SvxTimeFormat eFormat; + sal_uInt16 i; + + if (m_xPopup->get_active("1")) + eType = SvxTimeType::Fix; + else + eType = SvxTimeType::Var; + + for( i = 3; i < nCount; i++ ) + { + if (m_xPopup->get_active(OString::number(i))) + break; + } + eFormat = static_cast( i - 1 ); + + if( pTimeField->GetFormat() != eFormat || + pTimeField->GetType() != eType ) + { + pNewField = new SvxExtTimeField( *pTimeField ); + static_cast( pNewField )->SetType( eType ); + static_cast( pNewField )->SetFormat( eFormat ); + + if( (pTimeField->GetType() == SvxTimeType::Var) && (eType == SvxTimeType::Fix) ) + { + tools::Time aTime( tools::Time::SYSTEM ); + static_cast( pNewField )->SetFixTime( aTime ); + } + + } + } + else if( auto pFileField = dynamic_cast< const SvxExtFileField *>( m_pField ) ) + { + SvxFileType eType; + SvxFileFormat eFormat; + sal_uInt16 i; + + if (m_xPopup->get_active("1")) + eType = SvxFileType::Fix; + else + eType = SvxFileType::Var; + + for( i = 3; i < nCount; i++ ) + { + if (m_xPopup->get_active(OString::number(i))) + break; + } + eFormat = static_cast( i - 3 ); + + if( pFileField->GetFormat() != eFormat || + pFileField->GetType() != eType ) + { + ::sd::DrawDocShell* pDocSh = dynamic_cast<::sd::DrawDocShell* >( SfxObjectShell::Current() ); + + if( pDocSh ) + { + OUString aName; + if( pDocSh->HasName() ) + aName = pDocSh->GetMedium()->GetName(); + + // Get current filename, not the one stored in the old field + pNewField = new SvxExtFileField( aName ); + static_cast( pNewField )->SetType( eType ); + static_cast( pNewField )->SetFormat( eFormat ); + } + } + } + else if( auto pAuthorField = dynamic_cast< const SvxAuthorField *>( m_pField ) ) + { + SvxAuthorType eType; + SvxAuthorFormat eFormat; + sal_uInt16 i; + + if (m_xPopup->get_active("1")) + eType = SvxAuthorType::Fix; + else + eType = SvxAuthorType::Var; + + for( i = 3; i < nCount; i++ ) + { + if (m_xPopup->get_active(OString::number(i))) + break; + } + eFormat = static_cast( i - 3 ); + + if( pAuthorField->GetFormat() != eFormat || + pAuthorField->GetType() != eType ) + { + // Get current state of address, not the old one + SvtUserOptions aUserOptions; + pNewField = new SvxAuthorField( aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID() ); + static_cast( pNewField )->SetType( eType ); + static_cast( pNewField )->SetFormat( eFormat ); + } + } + return pNewField; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/sdxfer.cxx b/sd/source/ui/app/sdxfer.cxx new file mode 100644 index 000000000..67016fd19 --- /dev/null +++ b/sd/source/ui/app/sdxfer.cxx @@ -0,0 +1,807 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::datatransfer::clipboard; + +constexpr sal_uInt32 SDTRANSFER_OBJECTTYPE_DRAWMODEL = 1; +constexpr sal_uInt32 SDTRANSFER_OBJECTTYPE_DRAWOLE = 2; + +SdTransferable::SdTransferable( SdDrawDocument* pSrcDoc, ::sd::View* pWorkView, bool bInitOnGetData ) +: mpPageDocShell( nullptr ) +, mpSdView( pWorkView ) +, mpSdViewIntern( pWorkView ) +, mpSdDrawDocument( nullptr ) +, mpSdDrawDocumentIntern( nullptr ) +, mpSourceDoc( pSrcDoc ) +, mpVDev( nullptr ) +, mbInternalMove( false ) +, mbOwnDocument( false ) +, mbOwnView( false ) +, mbLateInit( bInitOnGetData ) +, mbPageTransferable( false ) +, mbPageTransferablePersistent( false ) +{ + if( mpSourceDoc ) + StartListening( *mpSourceDoc ); + + if( pWorkView ) + StartListening( *pWorkView ); + + if( !mbLateInit ) + CreateData(); +} + +SdTransferable::~SdTransferable() +{ + SolarMutexGuard g; + + if( mpSourceDoc ) + EndListening( *mpSourceDoc ); + + if( mpSdView ) + EndListening( *const_cast< sd::View *>( mpSdView) ); + + ObjectReleased(); + + if( mbOwnView ) + delete mpSdViewIntern; + + mpOLEDataHelper.reset(); + + if( maDocShellRef.is() ) + { + SfxObjectShell* pObj = maDocShellRef.get(); + ::sd::DrawDocShell* pDocSh = static_cast< ::sd::DrawDocShell*>(pObj); + pDocSh->DoClose(); + } + + maDocShellRef.clear(); + + if( mbOwnDocument ) + delete mpSdDrawDocumentIntern; + + mpGraphic.reset(); + mpBookmark.reset(); + mpImageMap.reset(); + + mpVDev.disposeAndClear(); + mpObjDesc.reset(); + + //call explicitly at end of dtor to be covered by above SolarMutex + maUserData.clear(); +} + +void SdTransferable::CreateObjectReplacement( SdrObject* pObj ) +{ + if( !pObj ) + return; + + mpOLEDataHelper.reset(); + mpGraphic.reset(); + mpBookmark.reset(); + mpImageMap.reset(); + + if( auto pOleObj = dynamic_cast< SdrOle2Obj* >( pObj ) ) + { + try + { + uno::Reference < embed::XEmbeddedObject > xObj = pOleObj->GetObjRef(); + uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); + if( xObj.is() && xPersist.is() && xPersist->hasEntry() ) + { + mpOLEDataHelper.reset( new TransferableDataHelper( new SvEmbedTransferHelper( xObj, pOleObj->GetGraphic(), pOleObj->GetAspect() ) ) ); + + // TODO/LATER: the standalone handling of the graphic should not be used any more in future + // The EmbedDataHelper should bring the graphic in future + const Graphic* pObjGr = pOleObj->GetGraphic(); + if ( pObjGr ) + mpGraphic.reset( new Graphic( *pObjGr ) ); + } + } + catch( uno::Exception& ) + {} + } + else if( dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr && (mpSourceDoc && !SdDrawDocument::GetAnimationInfo( pObj )) ) + { + mpGraphic.reset( new Graphic( static_cast< SdrGrafObj* >( pObj )->GetTransformedGraphic() ) ); + } + else if( pObj->IsUnoObj() && SdrInventor::FmForm == pObj->GetObjInventor() && ( pObj->GetObjIdentifier() == SdrObjKind::FormButton ) ) + { + SdrUnoObj* pUnoCtrl = static_cast< SdrUnoObj* >( pObj ); + + if (SdrInventor::FmForm == pUnoCtrl->GetObjInventor()) + { + const Reference< css::awt::XControlModel >& xControlModel( pUnoCtrl->GetUnoControlModel() ); + + if( !xControlModel.is() ) + return; + + Reference< css::beans::XPropertySet > xPropSet( xControlModel, UNO_QUERY ); + + if( !xPropSet.is() ) + return; + + css::form::FormButtonType eButtonType; + Any aTmp( xPropSet->getPropertyValue( "ButtonType" ) ); + + if( aTmp >>= eButtonType ) + { + OUString aLabel, aURL; + + xPropSet->getPropertyValue( "Label" ) >>= aLabel; + xPropSet->getPropertyValue( "TargetURL" ) >>= aURL; + + mpBookmark.reset( new INetBookmark( aURL, aLabel ) ); + } + } + } + else if( auto pTextObj = dynamic_cast< SdrTextObj *>( pObj ) ) + { + const OutlinerParaObject* pPara; + + if( (pPara = pTextObj->GetOutlinerParaObject()) != nullptr ) + { + const SvxFieldItem* pField; + + if( (pField = pPara->GetTextObject().GetField()) != nullptr ) + { + const SvxFieldData* pData = pField->GetField(); + + if( auto pURL = dynamic_cast< const SvxURLField *>( pData ) ) + { + // #i63399# This special code identifies TextFrames which have just a URL + // as content and directly add this to the clipboard, probably to avoid adding + // an unnecessary DrawObject to the target where paste may take place. This is + // wanted only for SdrObjects with no fill and no line, else it is necessary to + // use the whole SdrObject. Test here for Line/FillStyle and take shortcut only + // when both are unused + if(!pObj->HasFillStyle() && !pObj->HasLineStyle()) + { + mpBookmark.reset( new INetBookmark( pURL->GetURL(), pURL->GetRepresentation() ) ); + } + } + } + } + } + + SvxIMapInfo* pInfo = SvxIMapInfo::GetIMapInfo( pObj ); + + if( pInfo ) + mpImageMap.reset( new ImageMap( pInfo->GetImageMap() ) ); +} + +void SdTransferable::CreateData() +{ + if( mpSdDrawDocument && !mpSdViewIntern ) + { + mbOwnView = true; + + SdPage* pPage = mpSdDrawDocument->GetSdPage(0, PageKind::Standard); + + if( pPage && 1 == pPage->GetObjCount() ) + CreateObjectReplacement( pPage->GetObj( 0 ) ); + + mpVDev = VclPtr::Create( *Application::GetDefaultDevice() ); + mpVDev->SetMapMode( MapMode( mpSdDrawDocumentIntern->GetScaleUnit(), Point(), mpSdDrawDocumentIntern->GetScaleFraction(), mpSdDrawDocumentIntern->GetScaleFraction() ) ); + mpSdViewIntern = new ::sd::View( *mpSdDrawDocumentIntern, mpVDev ); + mpSdViewIntern->EndListening(*mpSdDrawDocumentIntern ); + mpSdViewIntern->hideMarkHandles(); + SdrPageView* pPageView = mpSdViewIntern->ShowSdrPage(pPage); + mpSdViewIntern->MarkAllObj(pPageView); + } + else if( mpSdView && !mpSdDrawDocumentIntern ) + { + const SdrMarkList& rMarkList = mpSdView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() == 1 ) + CreateObjectReplacement( rMarkList.GetMark( 0 )->GetMarkedSdrObj() ); + + if( mpSourceDoc ) + mpSourceDoc->CreatingDataObj(this); + mpSdDrawDocumentIntern = static_cast( mpSdView->CreateMarkedObjModel().release() ); + if( mpSourceDoc ) + mpSourceDoc->CreatingDataObj(nullptr); + + if( !maDocShellRef.is() && mpSdDrawDocumentIntern->GetDocSh() ) + maDocShellRef = mpSdDrawDocumentIntern->GetDocSh(); + + if( !maDocShellRef.is() ) + { + OSL_FAIL( "SdTransferable::CreateData(), failed to create a model with persist, clipboard operation will fail for OLE objects!" ); + mbOwnDocument = true; + } + + // Use dimension of source page + SdrPageView* pPgView = mpSdView->GetSdrPageView(); + SdPage* pOldPage = static_cast( pPgView->GetPage() ); + SdrModel* pOldModel = mpSdView->GetModel(); + SdStyleSheetPool* pOldStylePool = static_cast( pOldModel->GetStyleSheetPool() ); + SdStyleSheetPool* pNewStylePool = static_cast( mpSdDrawDocumentIntern->GetStyleSheetPool() ); + SdPage* pPage = mpSdDrawDocumentIntern->GetSdPage( 0, PageKind::Standard ); + OUString aOldLayoutName( pOldPage->GetLayoutName() ); + + pPage->SetSize( pOldPage->GetSize() ); + pPage->SetLayoutName( aOldLayoutName ); + pNewStylePool->CopyGraphicSheets( *pOldStylePool ); + pNewStylePool->CopyCellSheets( *pOldStylePool ); + pNewStylePool->CopyTableStyles( *pOldStylePool ); + sal_Int32 nPos = aOldLayoutName.indexOf( SD_LT_SEPARATOR ); + if( nPos != -1 ) + aOldLayoutName = aOldLayoutName.copy( 0, nPos ); + StyleSheetCopyResultVector aCreatedSheets; + pNewStylePool->CopyLayoutSheets( aOldLayoutName, *pOldStylePool, aCreatedSheets ); + } + + // set VisArea and adjust objects if necessary + if( !(maVisArea.IsEmpty() && + mpSdDrawDocumentIntern && mpSdViewIntern && + mpSdDrawDocumentIntern->GetPageCount()) ) + return; + + SdPage* pPage = mpSdDrawDocumentIntern->GetSdPage( 0, PageKind::Standard ); + + if( 1 == mpSdDrawDocumentIntern->GetPageCount() ) + { + // #112978# need to use GetAllMarkedBoundRect instead of GetAllMarkedRect to get + // fat lines correctly + maVisArea = mpSdViewIntern->GetAllMarkedBoundRect(); + Point aOrigin( maVisArea.TopLeft() ); + Size aVector( -aOrigin.X(), -aOrigin.Y() ); + + for( size_t nObj = 0, nObjCount = pPage->GetObjCount(); nObj < nObjCount; ++nObj ) + { + SdrObject* pObj = pPage->GetObj( nObj ); + pObj->NbcMove( aVector ); + } + } + else + maVisArea.SetSize( pPage->GetSize() ); + + // output is at the zero point + maVisArea.SetPos( Point() ); +} + +static bool lcl_HasOnlyControls( SdrModel* pModel ) +{ + bool bOnlyControls = false; // default if there are no objects + + if ( pModel ) + { + SdrPage* pPage = pModel->GetPage(0); + if (pPage) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups ); + SdrObject* pObj = aIter.Next(); + if ( pObj ) + { + bOnlyControls = true; // only set if there are any objects at all + while ( pObj ) + { + if (dynamic_cast< const SdrUnoObj *>( pObj ) == nullptr) + { + bOnlyControls = false; + break; + } + pObj = aIter.Next(); + } + } + } + } + + return bOnlyControls; +} + +static bool lcl_HasOnlyOneTable( SdrModel* pModel ) +{ + if ( pModel ) + { + SdrPage* pPage = pModel->GetPage(0); + if (pPage && pPage->GetObjCount() == 1 ) + { + if( dynamic_cast< sdr::table::SdrTableObj* >( pPage->GetObj(0) ) != nullptr ) + return true; + } + } + return false; +} + +void SdTransferable::AddSupportedFormats() +{ + if( mbPageTransferable && !mbPageTransferablePersistent ) + return; + + if( !mbLateInit ) + CreateData(); + + if( mpObjDesc ) + AddFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ); + + if( mpOLEDataHelper ) + { + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + + DataFlavorExVector aVector( mpOLEDataHelper->GetDataFlavorExVector() ); + + for( const auto& rItem : aVector ) + AddFormat( rItem ); + } + else if( mpGraphic ) + { + // #i25616# + AddFormat( SotClipboardFormatId::DRAWING ); + + AddFormat( SotClipboardFormatId::SVXB ); + + if( mpGraphic->GetType() == GraphicType::Bitmap ) + { + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + } + else + { + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + } + } + else if( mpBookmark ) + { + AddFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + AddFormat( SotClipboardFormatId::STRING ); + } + else + { + AddFormat( SotClipboardFormatId::EMBED_SOURCE ); + AddFormat( SotClipboardFormatId::DRAWING ); + if( !mpSdDrawDocument || !lcl_HasOnlyControls( mpSdDrawDocument ) ) + { + AddFormat( SotClipboardFormatId::GDIMETAFILE ); + AddFormat( SotClipboardFormatId::PNG ); + AddFormat( SotClipboardFormatId::BITMAP ); + } + + if( lcl_HasOnlyOneTable( mpSdDrawDocument ) ) { + AddFormat( SotClipboardFormatId::RTF ); + AddFormat( SotClipboardFormatId::RICHTEXT ); + } + } + + if( mpImageMap ) + AddFormat( SotClipboardFormatId::SVIM ); +} + +bool SdTransferable::GetData( const DataFlavor& rFlavor, const OUString& rDestDoc ) +{ + if (SD_MOD()==nullptr) + return false; + + SotClipboardFormatId nFormat = SotExchange::GetFormat( rFlavor ); + bool bOK = false; + + CreateData(); + + if( nFormat == SotClipboardFormatId::RTF && lcl_HasOnlyOneTable( mpSdDrawDocument ) ) + { + bOK = SetTableRTF( mpSdDrawDocument ); + } + else if( mpOLEDataHelper && mpOLEDataHelper->HasFormat( rFlavor ) ) + { + // TODO/LATER: support all the graphical formats, the embedded object scenario should not have separated handling + if( nFormat == SotClipboardFormatId::GDIMETAFILE && mpGraphic ) + bOK = SetGDIMetaFile( mpGraphic->GetGDIMetaFile() ); + else + bOK = SetAny( mpOLEDataHelper->GetAny(rFlavor, rDestDoc) ); + } + else if( HasFormat( nFormat ) ) + { + if( ( nFormat == SotClipboardFormatId::LINKSRCDESCRIPTOR || nFormat == SotClipboardFormatId::OBJECTDESCRIPTOR ) && mpObjDesc ) + { + bOK = SetTransferableObjectDescriptor( *mpObjDesc ); + } + else if( nFormat == SotClipboardFormatId::DRAWING ) + { + SfxObjectShellRef aOldRef( maDocShellRef ); + + maDocShellRef.clear(); + + if( mpSdViewIntern ) + { + SdDrawDocument& rInternDoc = mpSdViewIntern->GetDoc(); + rInternDoc.CreatingDataObj(this); + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( mpSdViewIntern->CreateMarkedObjModel().release() ); + rInternDoc.CreatingDataObj(nullptr); + + bOK = SetObject( pDoc, SDTRANSFER_OBJECTTYPE_DRAWMODEL, rFlavor ); + + if( maDocShellRef.is() ) + { + maDocShellRef->DoClose(); + } + else + { + delete pDoc; + } + } + + maDocShellRef = aOldRef; + } + else if( nFormat == SotClipboardFormatId::GDIMETAFILE ) + { + if (mpSdViewIntern) + { + const bool bToggleOnlineSpell = mpSdDrawDocumentIntern && mpSdDrawDocumentIntern->GetOnlineSpell(); + if (bToggleOnlineSpell) + mpSdDrawDocumentIntern->SetOnlineSpell(false); + bOK = SetGDIMetaFile( mpSdViewIntern->GetMarkedObjMetaFile( true ) ); + if (bToggleOnlineSpell) + mpSdDrawDocumentIntern->SetOnlineSpell(true); + } + } + else if( SotClipboardFormatId::BITMAP == nFormat || SotClipboardFormatId::PNG == nFormat ) + { + if (mpSdViewIntern) + { + const bool bToggleOnlineSpell = mpSdDrawDocumentIntern && mpSdDrawDocumentIntern->GetOnlineSpell(); + if (bToggleOnlineSpell) + mpSdDrawDocumentIntern->SetOnlineSpell(false); + bOK = SetBitmapEx( mpSdViewIntern->GetMarkedObjBitmapEx(true), rFlavor ); + if (bToggleOnlineSpell) + mpSdDrawDocumentIntern->SetOnlineSpell(true); + } + } + else if( ( nFormat == SotClipboardFormatId::STRING ) && mpBookmark ) + { + bOK = SetString( mpBookmark->GetURL() ); + } + else if( ( nFormat == SotClipboardFormatId::SVXB ) && mpGraphic ) + { + bOK = SetGraphic( *mpGraphic ); + } + else if( ( nFormat == SotClipboardFormatId::SVIM ) && mpImageMap ) + { + bOK = SetImageMap( *mpImageMap ); + } + else if( mpBookmark ) + { + bOK = SetINetBookmark( *mpBookmark, rFlavor ); + } + else if( nFormat == SotClipboardFormatId::EMBED_SOURCE ) + { + if( mpSdDrawDocumentIntern ) + { + if( !maDocShellRef.is() ) + { + maDocShellRef = new ::sd::DrawDocShell( + mpSdDrawDocumentIntern, + SfxObjectCreateMode::EMBEDDED, + true, + mpSdDrawDocumentIntern->GetDocumentType()); + mbOwnDocument = false; + maDocShellRef->DoInitNew(); + } + + maDocShellRef->SetVisArea( maVisArea ); + bOK = SetObject( maDocShellRef.get(), SDTRANSFER_OBJECTTYPE_DRAWOLE, rFlavor ); + } + } + } + + return bOK; +} + +bool SdTransferable::WriteObject( tools::SvRef& rxOStm, void* pObject, sal_uInt32 nObjectType, const DataFlavor& ) +{ + bool bRet = false; + + switch( nObjectType ) + { + case SDTRANSFER_OBJECTTYPE_DRAWMODEL: + { + try + { + static const bool bDontBurnInStyleSheet = ( getenv( "AVOID_BURN_IN_FOR_GALLERY_THEME" ) != nullptr ); + SdDrawDocument* pDoc = static_cast(pObject); + if ( !bDontBurnInStyleSheet ) + pDoc->BurnInStyleSheetAttributes(); + rxOStm->SetBufferSize( 16348 ); + + Reference< XComponent > xComponent( new SdXImpressDocument( pDoc, true ) ); + pDoc->setUnoModel( Reference< XInterface >::query( xComponent ) ); + + { + css::uno::Reference xDocOut( new utl::OOutputStreamWrapper( *rxOStm ) ); + SvxDrawingLayerExport( pDoc, xDocOut, xComponent, (pDoc->GetDocumentType() == DocumentType::Impress) ? "com.sun.star.comp.Impress.XMLClipboardExporter" : "com.sun.star.comp.DrawingLayer.XMLExporter" ); + } + + xComponent->dispose(); + bRet = ( rxOStm->GetError() == ERRCODE_NONE ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdTransferable::WriteObject()" ); + bRet = false; + } + } + break; + + case SDTRANSFER_OBJECTTYPE_DRAWOLE: + { + SfxObjectShell* pEmbObj = static_cast(pObject); + ::utl::TempFile aTempFile; + aTempFile.EnableKillingFile(); + + try + { + uno::Reference< embed::XStorage > xWorkStore = + ::comphelper::OStorageHelper::GetStorageFromURL( aTempFile.GetURL(), embed::ElementModes::READWRITE ); + + // write document storage + pEmbObj->SetupStorage( xWorkStore, SOFFICE_FILEFORMAT_CURRENT, false ); + // mba: no relative URLs for clipboard! + SfxMedium aMedium( xWorkStore, OUString() ); + pEmbObj->DoSaveObjectAs( aMedium, false ); + pEmbObj->DoSaveCompleted(); + + uno::Reference< embed::XTransactedObject > xTransact( xWorkStore, uno::UNO_QUERY ); + if ( xTransact.is() ) + xTransact->commit(); + + std::unique_ptr pSrcStm = ::utl::UcbStreamHelper::CreateStream( aTempFile.GetURL(), StreamMode::READ ); + if( pSrcStm ) + { + rxOStm->SetBufferSize( 0xff00 ); + rxOStm->WriteStream( *pSrcStm ); + pSrcStm.reset(); + } + + bRet = true; + } + catch ( Exception& ) + {} + } + + break; + + default: + break; + } + + return bRet; +} + +void SdTransferable::DragFinished( sal_Int8 nDropAction ) +{ + if( mpSdView ) + const_cast< ::sd::View* >(mpSdView)->DragFinished( nDropAction ); +} + +void SdTransferable::ObjectReleased() +{ + SdModule *pModule = SD_MOD(); + if (!pModule) + return; + + if( this == pModule->pTransferClip ) + pModule->pTransferClip = nullptr; + + if( this == pModule->pTransferDrag ) + pModule->pTransferDrag = nullptr; + + if( this == pModule->pTransferSelection ) + pModule->pTransferSelection = nullptr; +} + +void SdTransferable::SetObjectDescriptor( std::unique_ptr pObjDesc ) +{ + mpObjDesc = std::move(pObjDesc); + PrepareOLE( *mpObjDesc ); +} + +void SdTransferable::SetPageBookmarks( std::vector && rPageBookmarks, bool bPersistent ) +{ + if( !mpSourceDoc ) + return; + + if( mpSdViewIntern ) + mpSdViewIntern->HideSdrPage(); + + mpSdDrawDocument->ClearModel(false); + + mpPageDocShell = nullptr; + + maPageBookmarks.clear(); + + if( bPersistent ) + { + mpSdDrawDocument->CreateFirstPages(mpSourceDoc); + mpSdDrawDocument->InsertBookmarkAsPage( rPageBookmarks, nullptr, false, true, 1, true, + mpSourceDoc->GetDocSh(), true, true, false ); + } + else + { + mpPageDocShell = mpSourceDoc->GetDocSh(); + maPageBookmarks = std::move(rPageBookmarks); + } + + if( mpSdViewIntern ) + { + SdPage* pPage = mpSdDrawDocument->GetSdPage( 0, PageKind::Standard ); + + if( pPage ) + { + mpSdViewIntern->MarkAllObj( mpSdViewIntern->ShowSdrPage( pPage ) ); + } + } + + // set flags for page transferable; if ( mbPageTransferablePersistent == sal_False ), + // don't offer any formats => it's just for internal purposes + mbPageTransferable = true; + mbPageTransferablePersistent = bPersistent; +} + +sal_Int64 SAL_CALL SdTransferable::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this); +} + +void SdTransferable::AddUserData (const std::shared_ptr& rpData) +{ + maUserData.push_back(rpData); +} + +sal_Int32 SdTransferable::GetUserDataCount() const +{ + return maUserData.size(); +} + +std::shared_ptr SdTransferable::GetUserData (const sal_Int32 nIndex) const +{ + if (nIndex>=0 && o3tl::make_unsigned(nIndex)(); +} + +const css::uno::Sequence< sal_Int8 >& SdTransferable::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theSdTransferableUnoTunnelId; + return theSdTransferableUnoTunnelId.getSeq(); +} + +SdTransferable* SdTransferable::getImplementation( const Reference< XInterface >& rxData ) noexcept +{ + try + { + return comphelper::getFromUnoTunnel(rxData); + } + catch( const css::uno::Exception& ) + { + } + return nullptr; +} + +void SdTransferable::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast< const SdrHint* >( &rHint ); + if( SdrHintKind::ModelCleared == pSdrHint->GetKind() ) + { + EndListening(*mpSourceDoc); + mpSourceDoc = nullptr; + } + } + else + { + if( rHint.GetId() == SfxHintId::Dying ) + { + if( &rBC == mpSourceDoc ) + mpSourceDoc = nullptr; + if( &rBC == mpSdViewIntern ) + mpSdViewIntern = nullptr; + if( &rBC == mpSdView ) + mpSdView = nullptr; + } + } +} + +void SdTransferable::SetView(const ::sd::View* pView) +{ + if (mpSdView) + EndListening(*const_cast(mpSdView)); + mpSdView = pView; + if (mpSdView) + StartListening(*const_cast(mpSdView)); +} + +bool SdTransferable::SetTableRTF( SdDrawDocument* pModel ) +{ + if ( pModel ) + { + SdrPage* pPage = pModel->GetPage(0); + if (pPage && pPage->GetObjCount() == 1 ) + { + sdr::table::SdrTableObj* pTableObj = dynamic_cast< sdr::table::SdrTableObj* >( pPage->GetObj(0) ); + if( pTableObj ) + { + SvMemoryStream aMemStm( 65535, 65535 ); + sdr::table::ExportAsRTF( aMemStm, *pTableObj ); + return SetAny( Any( Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aMemStm.GetData() ), aMemStm.TellEnd() ) ) ); + } + } + } + + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/app/tmplctrl.cxx b/sd/source/ui/app/tmplctrl.cxx new file mode 100644 index 000000000..1f645bf66 --- /dev/null +++ b/sd/source/ui/app/tmplctrl.cxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +SFX_IMPL_STATUSBAR_CONTROL( SdTemplateControl, SfxStringItem ); + +// class SdTemplateControl ------------------------------------------ +SdTemplateControl::SdTemplateControl( sal_uInt16 _nSlotId, + sal_uInt16 _nId, + StatusBar& rStb ) : + SfxStatusBarControl( _nSlotId, _nId, rStb ) +{ + GetStatusBar().SetQuickHelpText(GetId(), SdResId(STR_STATUSBAR_MASTERPAGE)); +} + +SdTemplateControl::~SdTemplateControl() +{ +} + +void SdTemplateControl::StateChangedAtStatusBarControl( + sal_uInt16 /*nSID*/, SfxItemState eState, const SfxPoolItem* pState ) +{ + if( eState != SfxItemState::DEFAULT || pState->IsVoidItem() ) + GetStatusBar().SetItemText( GetId(), OUString() ); + else if ( auto pStringItem = dynamic_cast< const SfxStringItem *>( pState ) ) + { + msTemplate = pStringItem->GetValue(); + GetStatusBar().SetItemText( GetId(), msTemplate ); + } +} + +void SdTemplateControl::Paint( const UserDrawEvent& ) +{ +} + +void SdTemplateControl::Command( const CommandEvent& rCEvt ) +{ + if ( rCEvt.GetCommand() != CommandEventId::ContextMenu || GetStatusBar().GetItemText( GetId() ).isEmpty() ) + return; + + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + + sd::ViewShellBase* pViewShellBase = sd::ViewShellBase::GetViewShellBase( pViewFrame ); + if( !pViewShellBase ) + return; + + SdDrawDocument* pDoc = pViewShellBase->GetDocument(); + if( !pDoc ) + return; + + std::unique_ptr xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/masterpagemenu.ui")); + std::unique_ptr xPopup(xBuilder->weld_menu("menu")); + + const sal_uInt16 nMasterCount = pDoc->GetMasterSdPageCount(PageKind::Standard); + + for (sal_uInt16 nPage = 0; nPage < nMasterCount; ++nPage) + { + SdPage* pMaster = pDoc->GetMasterSdPage(nPage, PageKind::Standard); + if (!pMaster) + continue; + xPopup->append(OUString::number(nPage), pMaster->GetName()); + } + + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(1, 1)); + weld::Window* pParent = weld::GetPopupParent(GetStatusBar(), aRect); + OString sResult = xPopup->popup_at_rect(pParent, aRect); + if (!sResult.isEmpty()) + { + sal_uInt16 nCurrId = sResult.toUInt32(); + SdPage* pMaster = pDoc->GetMasterSdPage(nCurrId, PageKind::Standard); + SfxStringItem aStyle( ATTR_PRESLAYOUT_NAME, pMaster->GetName() ); + pViewFrame->GetDispatcher()->ExecuteList( + SID_PRESENTATION_LAYOUT, SfxCallMode::SLOT, { &aStyle }); + pViewFrame->GetBindings().Invalidate(SID_PRESENTATION_LAYOUT); + pViewFrame->GetBindings().Invalidate(SID_STATUS_LAYOUT); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/controller/displaymodecontroller.cxx b/sd/source/ui/controller/displaymodecontroller.cxx new file mode 100644 index 000000000..81ad2d19e --- /dev/null +++ b/sd/source/ui/controller/displaymodecontroller.cxx @@ -0,0 +1,264 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include + +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::beans; + +namespace sd +{ + +// Component to select which display mode has to be used. +// Composed of a dropdown button in the toolbar and a +// popup menu to select the value + +namespace { + +class DisplayModeController : public svt::PopupWindowController +{ +public: + explicit DisplayModeController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + + virtual std::unique_ptr weldPopupWindow() override; + virtual VclPtr createVclPopupWindow( vcl::Window* pParent ) override; + + // XInitialization + virtual void SAL_CALL initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + void setToolboxItemImage(const OUString& rImage); +}; + +class DisplayModeToolbarMenu final : public WeldToolbarPopup +{ +public: + DisplayModeToolbarMenu(DisplayModeController* pControl, weld::Widget* pParent); + virtual void GrabFocus() override + { + mxDisplayModeSet1->GrabFocus(); + } + +private: + rtl::Reference mxControl; + std::unique_ptr mxFrame1; + std::unique_ptr mxDisplayModeSet1; + std::unique_ptr mxDisplayModeSetWin1; + std::unique_ptr mxFrame2; + std::unique_ptr mxDisplayModeSet2; + std::unique_ptr mxDisplayModeSetWin2; + + DECL_LINK(SelectValueSetHdl, ValueSet*, void); +}; + +struct snew_slide_value_info +{ + sal_uInt16 mnId; + OUString msBmpResId; + TranslateId mpStrResId; + const char* msUnoCommand; +}; + +} + +const snew_slide_value_info editmodes[] = +{ + {1, + BMP_DISPLAYMODE_SLIDE, + STR_NORMAL_MODE, + ".uno:NormalMultiPaneGUI" }, + {2, + BMP_DISPLAYMODE_OUTLINE, + STR_OUTLINE_MODE, + ".uno:OutlineMode" }, + {3, + BMP_DISPLAYMODE_NOTES, + STR_NOTES_MODE, + ".uno:NotesMode" }, + {4, + BMP_DISPLAYMODE_SLIDE_SORTER, + STR_SLIDE_SORTER_MODE, + ".uno:DiaMode" }, + {0, "", {}, "" } +}; + +const snew_slide_value_info mastermodes[] = +{ + {5, + BMP_DISPLAYMODE_SLIDE_MASTER, + STR_SLIDE_MASTER_MODE, + ".uno:SlideMasterPage" }, + {6, + BMP_DISPLAYMODE_NOTES_MASTER, + STR_NOTES_MASTER_MODE, + ".uno:NotesMasterPage" }, + {7, + BMP_DISPLAYMODE_HANDOUT_MASTER, + STR_HANDOUT_MASTER_MODE, + ".uno:HandoutMode" }, + {0, "", {}, "" } +}; + + +static void fillLayoutValueSet(ValueSet* pValue, const snew_slide_value_info* pInfo) +{ + Size aLayoutItemSize; + for( ; pInfo->mnId; pInfo++ ) + { + OUString aText(SdResId(pInfo->mpStrResId)); + BitmapEx aBmp(pInfo->msBmpResId); + + pValue->InsertItem(pInfo->mnId, Image(aBmp), aText); + + aLayoutItemSize.setWidth( std::max( aLayoutItemSize.Width(), aBmp.GetSizePixel().Width() ) ); + aLayoutItemSize.setHeight( std::max( aLayoutItemSize.Height(), aBmp.GetSizePixel().Height() ) ); + } + + aLayoutItemSize = pValue->CalcItemSizePixel( aLayoutItemSize ); + Size aSize(pValue->CalcWindowSizePixel(aLayoutItemSize)); + + const sal_Int32 LAYOUT_BORDER_PIX = 7; + aSize.AdjustWidth((pValue->GetColCount() + 1) * LAYOUT_BORDER_PIX ); + aSize.AdjustHeight((pValue->GetLineCount() +1) * LAYOUT_BORDER_PIX ); + + pValue->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height()); + pValue->SetOutputSizePixel(aSize); +} + +DisplayModeToolbarMenu::DisplayModeToolbarMenu(DisplayModeController* pControl, weld::Widget* pParent) + : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "modules/simpress/ui/displaywindow.ui", "DisplayWindow") + , mxControl(pControl) + , mxFrame1(m_xBuilder->weld_frame("editframe")) + , mxDisplayModeSet1(new ValueSet(nullptr)) + , mxDisplayModeSetWin1(new weld::CustomWeld(*m_xBuilder, "valueset1", *mxDisplayModeSet1)) + , mxFrame2(m_xBuilder->weld_frame("masterframe")) + , mxDisplayModeSet2(new ValueSet(nullptr)) + , mxDisplayModeSetWin2(new weld::CustomWeld(*m_xBuilder, "valueset2", *mxDisplayModeSet2)) +{ + mxDisplayModeSet1->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT); + mxDisplayModeSet1->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT); + + mxDisplayModeSet1->SetSelectHdl( LINK( this, DisplayModeToolbarMenu, SelectValueSetHdl ) ); + mxDisplayModeSet2->SetSelectHdl( LINK( this, DisplayModeToolbarMenu, SelectValueSetHdl ) ); + + sal_Int16 nColCount = 2; + + mxDisplayModeSet1->SetColCount( nColCount ); + fillLayoutValueSet( mxDisplayModeSet1.get(), &editmodes[0] ); + + mxDisplayModeSet2->SetColCount( nColCount ); + fillLayoutValueSet( mxDisplayModeSet2.get(), &mastermodes[0] ); +} + +IMPL_LINK( DisplayModeToolbarMenu, SelectValueSetHdl, ValueSet*, pControl, void ) +{ + OUString sCommandURL; + OUString sImage; + + if( pControl == mxDisplayModeSet1.get() ) { + sCommandURL = OUString::createFromAscii(editmodes[mxDisplayModeSet1->GetSelectedItemId() - 1 ].msUnoCommand); + sImage = editmodes[mxDisplayModeSet1->GetSelectedItemId() - 1 ].msBmpResId; + } + else if( pControl == mxDisplayModeSet2.get() ) { + sCommandURL = OUString::createFromAscii(mastermodes[mxDisplayModeSet2->GetSelectedItemId() - 5 ].msUnoCommand); + sImage = mastermodes[mxDisplayModeSet2->GetSelectedItemId() - 5 ].msBmpResId; + } + + if (!sCommandURL.isEmpty()) + mxControl->dispatchCommand( sCommandURL, Sequence< PropertyValue >() ); + + mxControl->setToolboxItemImage(sImage); + mxControl->EndPopupMode(); +} + +DisplayModeController::DisplayModeController( const css::uno::Reference< css::uno::XComponentContext >& rxContext ) +: svt::PopupWindowController( rxContext, Reference< frame::XFrame >(), OUString() ) +{ +} + +void SAL_CALL DisplayModeController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + svt::PopupWindowController::initialize( aArguments ); + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY ); + setToolboxItemImage(BMP_DISPLAYMODE_SLIDE); +} + +std::unique_ptr DisplayModeController::weldPopupWindow() +{ + return std::make_unique(this, m_pToolbar); +} + +VclPtr DisplayModeController::createVclPopupWindow( vcl::Window* pParent ) +{ + mxInterimPopover = VclPtr::Create(getFrameInterface(), pParent, + std::make_unique(this, pParent->GetFrameWeld())); + + mxInterimPopover->Show(); + + return mxInterimPopover; +} + +void DisplayModeController::setToolboxItemImage(const OUString& rImage) +{ + ToolBoxItemId nId; + ToolBox* pToolBox = nullptr; + if (!getToolboxId( nId, &pToolBox )) + return; + + BitmapEx aBmp(rImage); + int targetSize = (pToolBox->GetToolboxButtonSize() == ToolBoxButtonSize::Large) ? 32 : 16; + double scale = 1.0f; + Size size = aBmp.GetSizePixel(); + if (size.Width() > targetSize) + scale = static_cast(targetSize) / static_cast(size.Width()); + if (size.Height() > targetSize) + scale = ::std::min( scale, static_cast(targetSize) / static_cast(size.Height()) ); + aBmp.Scale( scale, scale ); + pToolBox->SetItemImage( nId, Image( aBmp ) ); +} + +// XServiceInfo + +OUString SAL_CALL DisplayModeController::getImplementationName() +{ + return "com.sun.star.comp.sd.DisplayModeController"; +} + +Sequence< OUString > SAL_CALL DisplayModeController::getSupportedServiceNames( ) +{ + css::uno::Sequence aRet { "com.sun.star.frame.ToolbarController" }; + return aRet; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT ::com::sun::star::uno::XInterface* +com_sun_star_comp_sd_DisplayModeController_get_implementation( css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::DisplayModeController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/controller/slidelayoutcontroller.cxx b/sd/source/ui/controller/slidelayoutcontroller.cxx new file mode 100644 index 000000000..251548a22 --- /dev/null +++ b/sd/source/ui/controller/slidelayoutcontroller.cxx @@ -0,0 +1,380 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +#include + +#include + +#include +#include +#include "slidelayoutcontroller.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::beans; + +namespace sd +{ + +namespace { + +class LayoutToolbarMenu : public WeldToolbarPopup +{ +public: + LayoutToolbarMenu(SlideLayoutController* pController, weld::Widget* pParent, const bool bInsertPage, const OUString& rCommand); + virtual void GrabFocus() override + { + mxLayoutSet1->GrabFocus(); + } + +protected: + DECL_LINK(SelectToolbarMenuHdl, weld::Button&, void); + DECL_LINK(SelectValueSetHdl, ValueSet*, void); + void SelectHdl(AutoLayout eLayout); +private: + rtl::Reference mxControl; + bool const mbInsertPage; + std::unique_ptr mxFrame1; + std::unique_ptr mxLayoutSet1; + std::unique_ptr mxLayoutSetWin1; + std::unique_ptr mxFrame2; + std::unique_ptr mxLayoutSet2; + std::unique_ptr mxLayoutSetWin2; + std::unique_ptr mxMoreButton; +}; + +struct snew_slide_value_info_layout +{ + rtl::OUStringConstExpr msBmpResId; + TranslateId mpStrResId; + AutoLayout maAutoLayout; +}; + +} + +constexpr OUStringLiteral EMPTY = u""; + +const snew_slide_value_info_layout notes[] = +{ + {BMP_SLIDEN_01, STR_AUTOLAYOUT_NOTES, AUTOLAYOUT_NOTES}, + {EMPTY, {}, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info_layout handout[] = +{ + {BMP_SLIDEH_01, STR_AUTOLAYOUT_HANDOUT1, AUTOLAYOUT_HANDOUT1}, + {BMP_SLIDEH_02, STR_AUTOLAYOUT_HANDOUT2, AUTOLAYOUT_HANDOUT2}, + {BMP_SLIDEH_03, STR_AUTOLAYOUT_HANDOUT3, AUTOLAYOUT_HANDOUT3}, + {BMP_SLIDEH_04, STR_AUTOLAYOUT_HANDOUT4, AUTOLAYOUT_HANDOUT4}, + {BMP_SLIDEH_06, STR_AUTOLAYOUT_HANDOUT6, AUTOLAYOUT_HANDOUT6}, + {BMP_SLIDEH_09, STR_AUTOLAYOUT_HANDOUT9, AUTOLAYOUT_HANDOUT9}, + {EMPTY, {}, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info_layout standard[] = +{ + {BMP_LAYOUT_EMPTY, STR_AUTOLAYOUT_NONE, AUTOLAYOUT_NONE }, + {BMP_LAYOUT_HEAD03, STR_AUTOLAYOUT_TITLE, AUTOLAYOUT_TITLE }, + {BMP_LAYOUT_HEAD02, STR_AUTOLAYOUT_CONTENT, AUTOLAYOUT_TITLE_CONTENT }, + {BMP_LAYOUT_HEAD02A, STR_AUTOLAYOUT_2CONTENT, AUTOLAYOUT_TITLE_2CONTENT }, + {BMP_LAYOUT_HEAD01, STR_AUTOLAYOUT_ONLY_TITLE, AUTOLAYOUT_TITLE_ONLY }, + {BMP_LAYOUT_TEXTONLY, STR_AUTOLAYOUT_ONLY_TEXT, AUTOLAYOUT_ONLY_TEXT }, + {BMP_LAYOUT_HEAD03B, STR_AUTOLAYOUT_2CONTENT_CONTENT, AUTOLAYOUT_TITLE_2CONTENT_CONTENT }, + {BMP_LAYOUT_HEAD03C, STR_AUTOLAYOUT_CONTENT_2CONTENT, AUTOLAYOUT_TITLE_CONTENT_2CONTENT }, + {BMP_LAYOUT_HEAD03A, STR_AUTOLAYOUT_2CONTENT_OVER_CONTENT,AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT }, + {BMP_LAYOUT_HEAD02B, STR_AUTOLAYOUT_CONTENT_OVER_CONTENT, AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT }, + {BMP_LAYOUT_HEAD04, STR_AUTOLAYOUT_4CONTENT, AUTOLAYOUT_TITLE_4CONTENT }, + {BMP_LAYOUT_HEAD06, STR_AUTOLAYOUT_6CONTENT, AUTOLAYOUT_TITLE_6CONTENT }, + {EMPTY, {}, AUTOLAYOUT_NONE} +}; + +const snew_slide_value_info_layout v_standard[] = +{ + // vertical + {BMP_LAYOUT_VERTICAL02, STR_AL_VERT_TITLE_TEXT_CHART, AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT }, + {BMP_LAYOUT_VERTICAL01, STR_AL_VERT_TITLE_VERT_OUTLINE, AUTOLAYOUT_VTITLE_VCONTENT }, + {BMP_LAYOUT_HEAD02, STR_AL_TITLE_VERT_OUTLINE, AUTOLAYOUT_TITLE_VCONTENT }, + {BMP_LAYOUT_HEAD02A, STR_AL_TITLE_VERT_OUTLINE_CLIPART, AUTOLAYOUT_TITLE_2VTEXT }, + {EMPTY, {}, AUTOLAYOUT_NONE} +}; + +static void fillLayoutValueSet( ValueSet* pValue, const snew_slide_value_info_layout* pInfo ) +{ + Size aLayoutItemSize; + for( ; pInfo->mpStrResId; pInfo++ ) + { + OUString aText(SdResId(pInfo->mpStrResId)); + Image aImg(StockImage::Yes, pInfo->msBmpResId); + pValue->InsertItem(static_cast(pInfo->maAutoLayout)+1, aImg, aText); + aLayoutItemSize.setWidth( std::max( aLayoutItemSize.Width(), aImg.GetSizePixel().Width() ) ); + aLayoutItemSize.setHeight( std::max( aLayoutItemSize.Height(), aImg.GetSizePixel().Height() ) ); + } + + aLayoutItemSize = pValue->CalcItemSizePixel( aLayoutItemSize ); + Size aSize(pValue->CalcWindowSizePixel(aLayoutItemSize)); + + const sal_Int32 LAYOUT_BORDER_PIX = 7; + + aSize.AdjustWidth((pValue->GetColCount() + 1) * LAYOUT_BORDER_PIX); + aSize.AdjustHeight((pValue->GetLineCount() +1) * LAYOUT_BORDER_PIX); + + pValue->GetDrawingArea()->set_size_request(aSize.Width(), aSize.Height()); + pValue->SetOutputSizePixel(aSize); +} + +LayoutToolbarMenu::LayoutToolbarMenu(SlideLayoutController* pControl, weld::Widget* pParent, const bool bInsertPage, const OUString& rCommand) + : WeldToolbarPopup(pControl->getFrameInterface(), pParent, "modules/simpress/ui/layoutwindow.ui", "LayoutWindow") + , mxControl(pControl) + , mbInsertPage(bInsertPage) + , mxFrame1(m_xBuilder->weld_frame("horiframe")) + , mxLayoutSet1(new ValueSet(nullptr)) + , mxLayoutSetWin1(new weld::CustomWeld(*m_xBuilder, "valueset1", *mxLayoutSet1)) + , mxFrame2(m_xBuilder->weld_frame("vertframe")) + , mxLayoutSet2(new ValueSet(nullptr)) + , mxLayoutSetWin2(new weld::CustomWeld(*m_xBuilder, "valueset2", *mxLayoutSet2)) + , mxMoreButton(m_xBuilder->weld_button("more")) +{ + mxLayoutSet1->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT); + mxLayoutSet2->SetStyle(WB_TABSTOP | WB_MENUSTYLEVALUESET | WB_FLATVALUESET | WB_NOBORDER | WB_NO_DIRECTSELECT); + + DrawViewMode eMode = DrawViewMode_DRAW; + + // find out which view is running + if( m_xFrame.is() ) try + { + Reference< XPropertySet > xControllerSet( m_xFrame->getController(), UNO_QUERY_THROW ); + xControllerSet->getPropertyValue( "DrawViewMode" ) >>= eMode; + } + catch( Exception& ) + { + OSL_ASSERT(false); + } + + const bool bVerticalEnabled = SvtCJKOptions::IsVerticalTextEnabled(); + + mxLayoutSet1->SetSelectHdl( LINK( this, LayoutToolbarMenu, SelectValueSetHdl ) ); + + const snew_slide_value_info_layout* pInfo = nullptr; + sal_Int16 nColCount = 4; + switch( eMode ) + { + case DrawViewMode_DRAW: pInfo = &standard[0]; break; + case DrawViewMode_HANDOUT: pInfo = &handout[0]; nColCount = 2; break; + case DrawViewMode_NOTES: pInfo = ¬es[0]; nColCount = 1; break; + default: assert(false); // can't happen, will crash later otherwise + } + + mxLayoutSet1->SetColCount( nColCount ); + + fillLayoutValueSet( mxLayoutSet1.get(), pInfo ); + + bool bUseUILabel = (bVerticalEnabled && eMode == DrawViewMode_DRAW); + if (!bUseUILabel) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(rCommand, mxControl->getModuleName()); + mxFrame1->set_label(vcl::CommandInfoProvider::GetLabelForCommand(aProperties)); + } + + if (bVerticalEnabled && eMode == DrawViewMode_DRAW) + { + mxLayoutSet2->SetSelectHdl( LINK( this, LayoutToolbarMenu, SelectValueSetHdl ) ); + mxLayoutSet2->SetColCount( 4 ); + mxLayoutSet2->EnableFullItemMode( false ); + + fillLayoutValueSet( mxLayoutSet2.get(), &v_standard[0] ); + + mxFrame2->show(); + } + + if( eMode != DrawViewMode_DRAW ) + return; + + if( !m_xFrame.is() ) + return; + + OUString sSlotStr; + + if( bInsertPage ) + sSlotStr = ".uno:DuplicatePage"; + else + sSlotStr = ".uno:Undo"; + + css::uno::Reference xSlotImage = vcl::CommandInfoProvider::GetXGraphicForCommand(sSlotStr, m_xFrame); + + OUString sSlotTitle; + if( bInsertPage ) + { + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(sSlotStr, mxControl->getModuleName()); + sSlotTitle = vcl::CommandInfoProvider::GetLabelForCommand(aProperties); + } + else + sSlotTitle = SdResId( STR_RESET_LAYOUT ); + + mxMoreButton->set_label(sSlotTitle); + mxMoreButton->set_image(xSlotImage); + mxMoreButton->connect_clicked(LINK(this, LayoutToolbarMenu, SelectToolbarMenuHdl)); + mxMoreButton->show(); +} + +IMPL_LINK(LayoutToolbarMenu, SelectValueSetHdl, ValueSet*, pLayoutSet, void) +{ + SelectHdl(static_cast(pLayoutSet->GetSelectedItemId()-1)); +} + +IMPL_LINK_NOARG(LayoutToolbarMenu, SelectToolbarMenuHdl, weld::Button&, void) +{ + SelectHdl(AUTOLAYOUT_END); +} + +void LayoutToolbarMenu::SelectHdl(AutoLayout eLayout) +{ + Sequence< PropertyValue > aArgs; + + OUString sCommandURL( mxControl->getCommandURL() ); + + if( eLayout != AUTOLAYOUT_END ) + { + aArgs = { comphelper::makePropertyValue("WhatLayout", static_cast(eLayout)) }; + } + else if( mbInsertPage ) + { + sCommandURL = ".uno:DuplicatePage"; + } + + mxControl->dispatchCommand( sCommandURL, aArgs ); + + mxControl->EndPopupMode(); +} + + +/// @throws css::uno::RuntimeException +static OUString SlideLayoutController_getImplementationName() +{ + return "com.sun.star.comp.sd.SlideLayoutController"; +} + +/// @throws RuntimeException +static Sequence< OUString > SlideLayoutController_getSupportedServiceNames() +{ + Sequence aSNS { "com.sun.star.frame.ToolbarController" }; + return aSNS; +} + +/// @throws css::uno::RuntimeException +static OUString InsertSlideController_getImplementationName() +{ + return "com.sun.star.comp.sd.InsertSlideController"; +} + +/// @throws RuntimeException +static Sequence< OUString > InsertSlideController_getSupportedServiceNames() +{ + Sequence aSNS { "com.sun.star.frame.ToolbarController" }; + return aSNS; +} + +SlideLayoutController::SlideLayoutController(const Reference< uno::XComponentContext >& rxContext, bool bInsertPage) + : svt::PopupWindowController(rxContext, nullptr, OUString()) + , mbInsertPage(bInsertPage) +{ +} + +void SAL_CALL SlideLayoutController::initialize( const css::uno::Sequence< css::uno::Any >& aArguments ) +{ + svt::PopupWindowController::initialize( aArguments ); + + ToolBox* pToolBox = nullptr; + ToolBoxItemId nId; + if ( getToolboxId( nId, &pToolBox ) ) + { + if ( mbInsertPage ) + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWN ); + else + pToolBox->SetItemBits( nId, pToolBox->GetItemBits( nId ) | ToolBoxItemBits::DROPDOWNONLY ); + } +} + +std::unique_ptr SlideLayoutController::weldPopupWindow() +{ + return std::make_unique(this, m_pToolbar, mbInsertPage, m_aCommandURL); +} + +VclPtr SlideLayoutController::createVclPopupWindow( vcl::Window* pParent ) +{ + mxInterimPopover = VclPtr::Create(getFrameInterface(), pParent, + std::make_unique(this, pParent->GetFrameWeld(), mbInsertPage, m_aCommandURL)); + + mxInterimPopover->Show(); + + return mxInterimPopover; +} + +// XServiceInfo + +OUString SAL_CALL SlideLayoutController::getImplementationName() +{ + if( mbInsertPage ) + return InsertSlideController_getImplementationName(); + else + return SlideLayoutController_getImplementationName(); +} + +Sequence< OUString > SAL_CALL SlideLayoutController::getSupportedServiceNames( ) +{ + if( mbInsertPage ) + return InsertSlideController_getSupportedServiceNames(); + else + return SlideLayoutController_getSupportedServiceNames(); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_sd_SlideLayoutController_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::SlideLayoutController(context, false)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_sd_InsertSlideController_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::SlideLayoutController(context, true)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/controller/slidelayoutcontroller.hxx b/sd/source/ui/controller/slidelayoutcontroller.hxx new file mode 100644 index 000000000..ae4a3a09f --- /dev/null +++ b/sd/source/ui/controller/slidelayoutcontroller.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd +{ +class SlideLayoutController : public svt::PopupWindowController +{ +public: + SlideLayoutController(const css::uno::Reference& rxContext, + bool bInsertPage); + + virtual std::unique_ptr weldPopupWindow() override; + virtual VclPtr createVclPopupWindow(vcl::Window* pParent) override; + + // XInitialization + virtual void SAL_CALL initialize(const css::uno::Sequence& aArguments) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + +private: + bool mbInsertPage; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/AnimationChildWindow.cxx b/sd/source/ui/dlg/AnimationChildWindow.cxx new file mode 100644 index 000000000..2f221fc9e --- /dev/null +++ b/sd/source/ui/dlg/AnimationChildWindow.cxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +namespace sd { + +SFX_IMPL_DOCKINGWINDOW_WITHID(AnimationChildWindow, SID_ANIMATION_OBJECTS) + +/** + * Derivative from SfxChildWindow as "container" for animator + */ +AnimationChildWindow::AnimationChildWindow( + vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo ) + : SfxChildWindow( _pParent, nId ) +{ + VclPtr pAnimWin = VclPtr::Create(pBindings, this, _pParent); + SetWindow(pAnimWin); + + pAnimWin->Initialize( pInfo ); + + SetHideNotDelete( true ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/BulletAndPositionDlg.cxx b/sd/source/ui/dlg/BulletAndPositionDlg.cxx new file mode 100644 index 000000000..384b477e4 --- /dev/null +++ b/sd/source/ui/dlg/BulletAndPositionDlg.cxx @@ -0,0 +1,1293 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SHOW_NUMBERING 0 +#define SHOW_BULLET 1 +#define SHOW_BITMAP 2 + +#define MAX_BMP_WIDTH 16 +#define MAX_BMP_HEIGHT 16 + +static bool bLastRelative = false; + +static const vcl::Font& lcl_GetDefaultBulletFont() +{ + static vcl::Font aDefBulletFont = []() { + vcl::Font tmp("OpenSymbol", "", Size(0, 14)); + tmp.SetCharSet(RTL_TEXTENCODING_SYMBOL); + tmp.SetFamily(FAMILY_DONTKNOW); + tmp.SetPitch(PITCH_DONTKNOW); + tmp.SetWeight(WEIGHT_DONTKNOW); + tmp.SetTransparent(true); + return tmp; + }(); + return aDefBulletFont; +} + +class SdDrawDocument; + +SvxBulletAndPositionDlg::SvxBulletAndPositionDlg(weld::Window* pWindow, const SfxItemSet& rSet, + const ::sd::View* pView) + : GenericDialogController(pWindow, "cui/ui/bulletandposition.ui", "BulletAndPosition") + , aInvalidateTimer("sd SvxBulletAndPositionDlg aInvalidateTimer") + , rFirstStateSet(rSet) + , bLastWidthModified(false) + , bModified(false) + , bInInitControl(false) + , bLabelAlignmentPosAndSpaceModeActive(false) + , bApplyToMaster(false) + , nBullet(0xff) + , nActNumLvl(1) + , p_Window(pWindow) + , nNumItemId(SID_ATTR_NUMBERING_RULE) + , m_xGrid(m_xBuilder->weld_widget("grid2")) + , m_xLevelLB(m_xBuilder->weld_tree_view("levellb")) + , m_xFmtLB(m_xBuilder->weld_combo_box("numfmtlb")) + , m_xPrefixFT(m_xBuilder->weld_label("prefixft")) + , m_xPrefixED(m_xBuilder->weld_entry("prefix")) + , m_xSuffixFT(m_xBuilder->weld_label("suffixft")) + , m_xSuffixED(m_xBuilder->weld_entry("suffix")) + , m_xBeforeAfter(m_xBuilder->weld_frame("beforeafter")) + , m_xBulColorFT(m_xBuilder->weld_label("colorft")) + , m_xBulColLB(new ColorListBox(m_xBuilder->weld_menu_button("color"), + [this] { return m_xDialog.get(); })) + , m_xBulRelSizeFT(m_xBuilder->weld_label("relsizeft")) + , m_xBulRelSizeMF(m_xBuilder->weld_metric_spin_button("relsize", FieldUnit::PERCENT)) + , m_xStartFT(m_xBuilder->weld_label("startatft")) + , m_xStartED(m_xBuilder->weld_spin_button("startat")) + , m_xBulletFT(m_xBuilder->weld_label("bulletft")) + , m_xBulletPB(m_xBuilder->weld_button("bullet")) + , m_xBitmapMB(m_xBuilder->weld_menu_button("bitmap")) + , m_xWidthFT(m_xBuilder->weld_label("widthft")) + , m_xWidthMF(m_xBuilder->weld_metric_spin_button("widthmf", FieldUnit::CM)) + , m_xHeightFT(m_xBuilder->weld_label("heightft")) + , m_xHeightMF(m_xBuilder->weld_metric_spin_button("heightmf", FieldUnit::CM)) + , m_xRatioCB(m_xBuilder->weld_check_button("keepratio")) + , m_xPreviewWIN(new weld::CustomWeld(*m_xBuilder, "preview", m_aPreviewWIN)) + , m_xDistBorderFT(m_xBuilder->weld_label("indent")) + , m_xDistBorderMF(m_xBuilder->weld_metric_spin_button("indentmf", FieldUnit::CM)) + , m_xRelativeCB(m_xBuilder->weld_check_button("relative")) + , m_xIndentFT(m_xBuilder->weld_label("numberingwidth")) + , m_xIndentMF(m_xBuilder->weld_metric_spin_button("numberingwidthmf", FieldUnit::CM)) + , m_xLeftTB(m_xBuilder->weld_toggle_button("left")) + , m_xCenterTB(m_xBuilder->weld_toggle_button("center")) + , m_xRightTB(m_xBuilder->weld_toggle_button("right")) + , m_xSlideRB(m_xBuilder->weld_radio_button("sliderb")) + , m_xSelectionRB(m_xBuilder->weld_radio_button("selectionrb")) + , m_xApplyToMaster(m_xBuilder->weld_toggle_button("applytomaster")) + , m_xReset(m_xBuilder->weld_button("reset")) +{ + m_xBulColLB->SetSlotId(SID_ATTR_CHAR_COLOR); + m_xBulRelSizeMF->set_min(SVX_NUM_REL_SIZE_MIN, FieldUnit::PERCENT); + m_xBulRelSizeMF->set_increments(5, 50, FieldUnit::PERCENT); + aActBulletFont = lcl_GetDefaultBulletFont(); + + m_xBulletPB->connect_clicked(LINK(this, SvxBulletAndPositionDlg, BulletHdl_Impl)); + m_xFmtLB->connect_changed(LINK(this, SvxBulletAndPositionDlg, NumberTypeSelectHdl_Impl)); + m_xBitmapMB->connect_selected(LINK(this, SvxBulletAndPositionDlg, GraphicHdl_Impl)); + m_xBitmapMB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, PopupActivateHdl_Impl)); + m_xLevelLB->set_selection_mode(SelectionMode::Multiple); + m_xLevelLB->connect_changed(LINK(this, SvxBulletAndPositionDlg, LevelHdl_Impl)); + m_xWidthMF->connect_value_changed(LINK(this, SvxBulletAndPositionDlg, SizeHdl_Impl)); + m_xHeightMF->connect_value_changed(LINK(this, SvxBulletAndPositionDlg, SizeHdl_Impl)); + m_xRatioCB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, RatioHdl_Impl)); + m_xStartED->connect_value_changed(LINK(this, SvxBulletAndPositionDlg, SpinModifyHdl_Impl)); + m_xPrefixED->connect_changed(LINK(this, SvxBulletAndPositionDlg, EditModifyHdl_Impl)); + m_xSuffixED->connect_changed(LINK(this, SvxBulletAndPositionDlg, EditModifyHdl_Impl)); + m_xBulRelSizeMF->connect_value_changed(LINK(this, SvxBulletAndPositionDlg, BulRelSizeHdl_Impl)); + m_xBulColLB->SetSelectHdl(LINK(this, SvxBulletAndPositionDlg, BulColorHdl_Impl)); + m_xLeftTB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, SelectLeftAlignmentHdl_Impl)); + m_xCenterTB->connect_toggled( + LINK(this, SvxBulletAndPositionDlg, SelectCenterAlignmentHdl_Impl)); + m_xRightTB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, SelectRightAlignmentHdl_Impl)); + m_xApplyToMaster->connect_toggled(LINK(this, SvxBulletAndPositionDlg, ApplyToMasterHdl_Impl)); + m_xReset->connect_clicked(LINK(this, SvxBulletAndPositionDlg, ResetHdl_Impl)); + + aInvalidateTimer.SetInvokeHandler( + LINK(this, SvxBulletAndPositionDlg, PreviewInvalidateHdl_Impl)); + aInvalidateTimer.SetTimeout(50); + + eCoreUnit = rSet.GetPool()->GetMetric(rSet.GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE)); + + // Fill ListBox with predefined / translated numbering types. + sal_uInt32 nCount = SvxNumberingTypeTable::Count(); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + m_xFmtLB->append(OUString::number(SvxNumberingTypeTable::GetValue(i)), + SvxNumberingTypeTable::GetString(i)); + } + + // Get advanced numbering types from the component. + // Watch out for the ugly + // 136 == 0x88 == SVX_NUM_BITMAP|0x80 == SVX_NUM_BITMAP|LINK_TOKEN + // to not remove that. + SvxNumOptionsTabPageHelper::GetI18nNumbering(*m_xFmtLB, (SVX_NUM_BITMAP | LINK_TOKEN)); + + m_xFmtLB->set_active(0); + m_xRelativeCB->set_active(true); + + Link aLk3 + = LINK(this, SvxBulletAndPositionDlg, DistanceHdl_Impl); + m_xDistBorderMF->connect_value_changed(aLk3); + m_xIndentMF->connect_value_changed(aLk3); + + m_xRelativeCB->connect_toggled(LINK(this, SvxBulletAndPositionDlg, RelativeHdl_Impl)); + m_xRelativeCB->set_active(bLastRelative); + + Size aSize(m_xGrid->get_preferred_size()); + m_xGrid->set_size_request(aSize.Width(), -1); + + // PageCreated + FieldUnit eMetric = pView->GetDoc().GetUIUnit(); + SfxAllItemSet aSet(*(rSet.GetPool())); + aSet.Put(SfxUInt16Item(SID_METRIC_ITEM, static_cast(eMetric))); + + const SfxStringItem* pNumCharFmt = aSet.GetItem(SID_NUM_CHAR_FMT, false); + const SfxUInt16Item* pMetricItem = aSet.GetItem(SID_METRIC_ITEM, false); + + if (pNumCharFmt) + SetCharFmt(pNumCharFmt->GetValue()); + + if (pMetricItem) + SetMetric(static_cast(pMetricItem->GetValue())); + + // tdf#130526: Hide "Apply To Master"-button in Draw and rename "Slide" to "Page" + DocumentType aDocumentType = pView->GetDoc().GetDocumentType(); + if (aDocumentType == DocumentType::Draw) + { + m_xApplyToMaster->hide(); + m_xSlideRB->set_label(SdResId(STR_PAGE_NAME)); + } + // tdf#137406: Crash when clicking "Apply to Master" in Slide Master mode on Bullets and Numbering dialog + EditMode aEditmode = static_cast<::sd::DrawViewShell*>(pView->GetViewShell())->GetEditMode(); + if (aDocumentType == DocumentType::Impress && aEditmode == EditMode::MasterPage) + m_xApplyToMaster->hide(); + + // End PageCreated + + Reset(&rSet); + + // ActivatePage part + + const SfxItemSet* pExampleSet = &rSet; + sal_uInt16 nTmpNumLvl = 1; + bool bPreset = false; + if (pExampleSet) + { + if (const SfxBoolItem* pItem = pExampleSet->GetItemIfSet(SID_PARAM_NUM_PRESET, false)) + bPreset = pItem->GetValue(); + if (const SfxUInt16Item* pItem = pExampleSet->GetItemIfSet(SID_PARAM_CUR_NUM_LEVEL, false)) + nTmpNumLvl = pItem->GetValue(); + } + if (const SvxNumBulletItem* pItem = rSet.GetItemIfSet(nNumItemId, false)) + { + pSaveNum.reset(new SvxNumRule(pItem->GetNumRule())); + } + + bModified = (!pActNum->Get(0) || bPreset); + if (*pActNum != *pSaveNum || nActNumLvl != nTmpNumLvl) + { + nActNumLvl = nTmpNumLvl; + sal_uInt16 nMask = 1; + if (nActNumLvl == SAL_MAX_UINT16) + m_xLevelLB->select(pActNum->GetLevelCount()); + if (nActNumLvl != SAL_MAX_UINT16) + { + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + m_xLevelLB->select(i); + nMask <<= 1; + } + } + *pActNum = *pSaveNum; + + m_xRelativeCB->set_sensitive(nActNumLvl != 1); + + InitPosAndSpaceMode(); + InitControls(); + } + + m_aPreviewWIN.SetLevel(nActNumLvl); + m_aPreviewWIN.Invalidate(); + + // End of the ActivatePage part +} + +SvxBulletAndPositionDlg::~SvxBulletAndPositionDlg() {} + +void SvxBulletAndPositionDlg::SetMetric(FieldUnit eMetric) +{ + if (eMetric == FieldUnit::MM) + { + m_xWidthMF->set_digits(1); + m_xHeightMF->set_digits(1); + m_xDistBorderMF->set_digits(1); + m_xIndentMF->set_digits(1); + } + m_xWidthMF->set_unit(eMetric); + m_xHeightMF->set_unit(eMetric); + m_xDistBorderMF->set_unit(eMetric); + m_xIndentMF->set_unit(eMetric); +} + +SfxItemSet* SvxBulletAndPositionDlg::GetOutputItemSet(SfxItemSet* pSet) +{ + pSet->Put(SfxUInt16Item(SID_PARAM_CUR_NUM_LEVEL, nActNumLvl)); + if (bModified && pActNum) + { + *pSaveNum = *pActNum; + pSet->Put(SvxNumBulletItem(*pSaveNum, nNumItemId)); + pSet->Put(SfxBoolItem(SID_PARAM_NUM_PRESET, false)); + } + return pSet; +}; + +bool SvxBulletAndPositionDlg::IsApplyToMaster() const { return bApplyToMaster; } +bool SvxBulletAndPositionDlg::IsSlideScope() const { return m_xSlideRB->get_active(); } + +void SvxBulletAndPositionDlg::Reset(const SfxItemSet* rSet) +{ + const SvxNumBulletItem* pItem = rSet->GetItemIfSet(SID_ATTR_NUMBERING_RULE, false); + // in Draw the item exists as WhichId, in Writer only as SlotId + if (!pItem) + { + nNumItemId = rSet->GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE); + pItem = rSet->GetItemIfSet(nNumItemId, false); + + if (!pItem) + { + pItem = &rSet->Get(nNumItemId); + } + } + DBG_ASSERT(pItem, "no item found!"); + pSaveNum.reset(new SvxNumRule(pItem->GetNumRule())); + + // insert levels + if (!m_xLevelLB->n_children()) + { + for (sal_uInt16 i = 1; i <= pSaveNum->GetLevelCount(); i++) + m_xLevelLB->append_text(OUString::number(i)); + if (pSaveNum->GetLevelCount() > 1) + { + OUString sEntry = "1 - " + OUString::number(pSaveNum->GetLevelCount()); + m_xLevelLB->append_text(sEntry); + m_xLevelLB->select_text(sEntry); + } + else + m_xLevelLB->select(0); + } + else + m_xLevelLB->select(m_xLevelLB->n_children() - 1); + + sal_uInt16 nMask = 1; + m_xLevelLB->unselect_all(); + if (nActNumLvl == SAL_MAX_UINT16) + { + m_xLevelLB->select(pSaveNum->GetLevelCount()); + } + else + { + for (sal_uInt16 i = 0; i < pSaveNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + m_xLevelLB->select(i); + nMask <<= 1; + } + } + + if (!pActNum) + pActNum.reset(new SvxNumRule(*pSaveNum)); + else if (*pSaveNum != *pActNum) + *pActNum = *pSaveNum; + m_aPreviewWIN.SetNumRule(pActNum.get()); + + bool bContinuous = pActNum->IsFeatureSupported(SvxNumRuleFlags::CONTINUOUS); + + // again misusage: in Draw there is numeration only until the bitmap + // without SVX_NUM_NUMBER_NONE + //remove types that are unsupported by Draw/Impress + if (!bContinuous) + { + sal_Int32 nFmtCount = m_xFmtLB->get_count(); + for (sal_Int32 i = nFmtCount; i; i--) + { + sal_uInt16 nEntryData = m_xFmtLB->get_id(i - 1).toUInt32(); + if (/*SVX_NUM_NUMBER_NONE == nEntryData ||*/ + (SVX_NUM_BITMAP | LINK_TOKEN) == nEntryData) + m_xFmtLB->remove(i - 1); + } + } + //one must be enabled + if (!pActNum->IsFeatureSupported(SvxNumRuleFlags::ENABLE_LINKED_BMP)) + { + auto nPos = m_xFmtLB->find_id(OUString::number(SVX_NUM_BITMAP | LINK_TOKEN)); + if (nPos != -1) + m_xFmtLB->remove(nPos); + } + else if (!pActNum->IsFeatureSupported(SvxNumRuleFlags::ENABLE_EMBEDDED_BMP)) + { + auto nPos = m_xFmtLB->find_id(OUString::number(SVX_NUM_BITMAP)); + if (nPos != -1) + m_xFmtLB->remove(nPos); + } + + // MegaHack: because of a not-fixable 'design mistake/error' in Impress + // delete all kinds of numeric enumerations + if (pActNum->IsFeatureSupported(SvxNumRuleFlags::NO_NUMBERS)) + { + sal_Int32 nFmtCount = m_xFmtLB->get_count(); + for (sal_Int32 i = nFmtCount; i; i--) + { + sal_uInt16 nEntryData = m_xFmtLB->get_id(i - 1).toUInt32(); + if (/*nEntryData >= SVX_NUM_CHARS_UPPER_LETTER &&*/ nEntryData <= SVX_NUM_NUMBER_NONE) + m_xFmtLB->remove(i - 1); + } + } + + InitPosAndSpaceMode(); + + InitControls(); + bModified = false; +} + +void SvxBulletAndPositionDlg::InitControls() +{ + bInInitControl = true; + + const bool bRelative = !bLabelAlignmentPosAndSpaceModeActive && m_xRelativeCB->get_sensitive() + && m_xRelativeCB->get_active(); + const bool bSingleSelection + = m_xLevelLB->count_selected_rows() == 1 && SAL_MAX_UINT16 != nActNumLvl; + + m_xDistBorderMF->set_sensitive(!bLabelAlignmentPosAndSpaceModeActive + && (bSingleSelection || bRelative)); + m_xDistBorderFT->set_sensitive(!bLabelAlignmentPosAndSpaceModeActive + && (bSingleSelection || bRelative)); + + bool bShowBullet = true; + bool bShowBitmap = true; + bool bSameType = true; + bool bSameStart = true; + bool bSamePrefix = true; + bool bSameSuffix = true; + bool bSameSize = true; + bool bSameBulColor = true; + bool bSameBulRelSize = true; + bool bSameDistBorderNum = !bLabelAlignmentPosAndSpaceModeActive; + bool bSetDistEmpty = false; + bool bSameIndent = !bLabelAlignmentPosAndSpaceModeActive; + + const SvxNumberFormat* aNumFmtArr[SVX_MAX_NUM]; + SvxAdjust eFirstAdjust = SvxAdjust::Left; + Size aFirstSize(0, 0); + sal_uInt16 nMask = 1; + sal_uInt16 nLvl = SAL_MAX_UINT16; + + bool bBullColor = pActNum->IsFeatureSupported(SvxNumRuleFlags::BULLET_COLOR); + bool bBullRelSize = pActNum->IsFeatureSupported(SvxNumRuleFlags::BULLET_REL_SIZE); + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + aNumFmtArr[i] = &pActNum->GetLevel(i); + + if (nActNumLvl & nMask) + { + bShowBullet &= aNumFmtArr[i]->GetNumberingType() == SVX_NUM_CHAR_SPECIAL; + bShowBitmap &= (aNumFmtArr[i]->GetNumberingType() & (~LINK_TOKEN)) == SVX_NUM_BITMAP; + eFirstAdjust = aNumFmtArr[i]->GetNumAdjust(); + if (SAL_MAX_UINT16 == nLvl) + { + nLvl = i; + if (bShowBitmap) + aFirstSize = aNumFmtArr[i]->GetGraphicSize(); + } + if (i > nLvl) + { + bSameType + &= aNumFmtArr[i]->GetNumberingType() == aNumFmtArr[nLvl]->GetNumberingType(); + bSameStart = aNumFmtArr[i]->GetStart() == aNumFmtArr[nLvl]->GetStart(); + + bSamePrefix = aNumFmtArr[i]->GetPrefix() == aNumFmtArr[nLvl]->GetPrefix(); + bSameSuffix = aNumFmtArr[i]->GetSuffix() == aNumFmtArr[nLvl]->GetSuffix(); + //bSameAdjust &= eFirstAdjust == aNumFmtArr[i]->GetNumAdjust(); + if (bShowBitmap && bSameSize) + bSameSize &= aNumFmtArr[i]->GetGraphicSize() == aFirstSize; + bSameBulColor + &= aNumFmtArr[i]->GetBulletColor() == aNumFmtArr[nLvl]->GetBulletColor(); + bSameBulRelSize + &= aNumFmtArr[i]->GetBulletRelSize() == aNumFmtArr[nLvl]->GetBulletRelSize(); + bSameIndent //? + &= aNumFmtArr[i]->GetFirstLineOffset() + == aNumFmtArr[nLvl]->GetFirstLineOffset(); + } + } + + nMask <<= 1; + } + SwitchNumberType(bShowBullet ? 1 : bShowBitmap ? 2 : 0); + + sal_uInt16 nNumberingType; + if (nLvl != SAL_MAX_UINT16) + nNumberingType = aNumFmtArr[nLvl]->GetNumberingType(); + else + { + nNumberingType = SVX_NUM_NUMBER_NONE; + bSameDistBorderNum = false; + bSameIndent = false; + bSameBulRelSize = false; + bSameBulColor = false; + bSameStart = false; + bSamePrefix = false; + bSameSuffix = false; + } + + CheckForStartValue_Impl(nNumberingType); + + if (bShowBitmap) + { + if (bSameSize) + { + SetMetricValue(*m_xHeightMF, aFirstSize.Height(), eCoreUnit); + SetMetricValue(*m_xWidthMF, aFirstSize.Width(), eCoreUnit); + } + else + { + m_xHeightMF->set_text(""); + m_xWidthMF->set_text(""); + } + } + + if (bSameType) + { + sal_uInt16 nLBData = nNumberingType; + m_xFmtLB->set_active_id(OUString::number(nLBData)); + } + else + m_xFmtLB->set_active(-1); + + if (bBullRelSize) + { + if (bSameBulRelSize) + m_xBulRelSizeMF->set_value(aNumFmtArr[nLvl]->GetBulletRelSize(), FieldUnit::PERCENT); + else + m_xBulRelSizeMF->set_text(""); + } + if (bBullColor) + { + if (bSameBulColor) + m_xBulColLB->SelectEntry(aNumFmtArr[nLvl]->GetBulletColor()); + else + m_xBulColLB->SetNoSelection(); + } + switch (nBullet) + { + case SHOW_NUMBERING: + if (bSameStart) + { + m_xStartED->set_value(aNumFmtArr[nLvl]->GetStart()); + } + else + m_xStartED->set_text(""); + break; + case SHOW_BULLET: + break; + case SHOW_BITMAP: + break; + } + + switch (eFirstAdjust) + { + case SvxAdjust::Left: + m_xLeftTB->set_active(true); + m_xCenterTB->set_active(false); + m_xRightTB->set_active(false); + break; + case SvxAdjust::Center: + m_xLeftTB->set_active(false); + m_xCenterTB->set_active(true); + m_xRightTB->set_active(false); + break; + case SvxAdjust::Right: + m_xLeftTB->set_active(false); + m_xCenterTB->set_active(false); + m_xRightTB->set_active(true); + break; + default: + break; + } + + if (bSamePrefix) + m_xPrefixED->set_text(aNumFmtArr[nLvl]->GetPrefix()); + else + m_xPrefixED->set_text(""); + if (bSameSuffix) + m_xSuffixED->set_text(aNumFmtArr[nLvl]->GetSuffix()); + else + m_xSuffixED->set_text(""); + + if (bSameDistBorderNum) + { + tools::Long nDistBorderNum; + if (bRelative) + { + nDistBorderNum = static_cast(aNumFmtArr[nLvl]->GetAbsLSpace()) + + aNumFmtArr[nLvl]->GetFirstLineOffset(); + if (nLvl) + nDistBorderNum -= static_cast(aNumFmtArr[nLvl - 1]->GetAbsLSpace()) + + aNumFmtArr[nLvl - 1]->GetFirstLineOffset(); + } + else + { + nDistBorderNum = static_cast(aNumFmtArr[nLvl]->GetAbsLSpace()) + + aNumFmtArr[nLvl]->GetFirstLineOffset(); + } + SetMetricValue(*m_xDistBorderMF, nDistBorderNum, eCoreUnit); + } + else + bSetDistEmpty = true; + + if (bSetDistEmpty) + m_xDistBorderMF->set_text(""); + + if (bSameIndent) + SetMetricValue(*m_xIndentMF, -aNumFmtArr[nLvl]->GetFirstLineOffset(), eCoreUnit); + else + m_xIndentMF->set_text(""); + + m_xSelectionRB->set_active(true); + + m_aPreviewWIN.SetLevel(nActNumLvl); + m_aPreviewWIN.Invalidate(); + bInInitControl = false; +} + +// 0 - Number; 1 - Bullet; 2 - Bitmap +void SvxBulletAndPositionDlg::SwitchNumberType(sal_uInt8 nType) +{ + if (nBullet == nType) + return; + nBullet = nType; + bool bBullet = (nType == SHOW_BULLET); + bool bBitmap = (nType == SHOW_BITMAP); + bool bEnableBitmap = (nType == SHOW_BITMAP); + bool bNumeric = !(bBitmap || bBullet); + m_xPrefixFT->set_visible(bNumeric); + m_xPrefixED->set_visible(bNumeric); + m_xSuffixFT->set_visible(bNumeric); + m_xSuffixED->set_visible(bNumeric); + m_xBeforeAfter->set_visible(bNumeric); + + m_xStartFT->set_visible(!(bBullet || bBitmap)); + m_xStartED->set_visible(!(bBullet || bBitmap)); + + m_xBulletFT->set_visible(bBullet); + m_xBulletPB->set_visible(bBullet); + bool bBullColor = pActNum->IsFeatureSupported(SvxNumRuleFlags::BULLET_COLOR); + m_xBulColorFT->set_visible(!bBitmap && bBullColor); + m_xBulColLB->set_visible(!bBitmap && bBullColor); + bool bBullResSize = pActNum->IsFeatureSupported(SvxNumRuleFlags::BULLET_REL_SIZE); + m_xBulRelSizeFT->set_visible(!bBitmap && bBullResSize); + m_xBulRelSizeMF->set_visible(!bBitmap && bBullResSize); + + m_xBitmapMB->set_visible(bBitmap); + + m_xWidthFT->set_visible(bBitmap); + m_xWidthMF->set_visible(bBitmap); + m_xHeightFT->set_visible(bBitmap); + m_xHeightMF->set_visible(bBitmap); + m_xRatioCB->set_visible(bBitmap); + + m_xWidthFT->set_sensitive(bEnableBitmap); + m_xWidthMF->set_sensitive(bEnableBitmap); + m_xHeightFT->set_sensitive(bEnableBitmap); + m_xHeightMF->set_sensitive(bEnableBitmap); + m_xRatioCB->set_sensitive(bEnableBitmap); +} + +void SvxBulletAndPositionDlg::CheckForStartValue_Impl(sal_uInt16 nNumberingType) +{ + bool bIsNull = m_xStartED->get_value() == 0; + bool bNoZeroAllowed = nNumberingType < SVX_NUM_ARABIC + || SVX_NUM_CHARS_UPPER_LETTER_N == nNumberingType + || SVX_NUM_CHARS_LOWER_LETTER_N == nNumberingType; + m_xStartED->set_min(bNoZeroAllowed ? 1 : 0); + if (bIsNull && bNoZeroAllowed) + SpinModifyHdl_Impl(*m_xStartED); +} + +IMPL_LINK(SvxBulletAndPositionDlg, LevelHdl_Impl, weld::TreeView&, rBox, void) +{ + sal_uInt16 nSaveNumLvl = nActNumLvl; + nActNumLvl = 0; + auto aSelectedRows = rBox.get_selected_rows(); + if (std::find(aSelectedRows.begin(), aSelectedRows.end(), pActNum->GetLevelCount()) + != aSelectedRows.end() + && (aSelectedRows.size() == 1 || nSaveNumLvl != 0xffff)) + { + nActNumLvl = 0xFFFF; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + rBox.unselect(i); + } + else if (!aSelectedRows.empty()) + { + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (std::find(aSelectedRows.begin(), aSelectedRows.end(), i) != aSelectedRows.end()) + nActNumLvl |= nMask; + nMask <<= 1; + } + rBox.unselect(pActNum->GetLevelCount()); + } + else + nActNumLvl = nSaveNumLvl; + + InitControls(); +} + +IMPL_LINK_NOARG(SvxBulletAndPositionDlg, PreviewInvalidateHdl_Impl, Timer*, void) +{ + m_aPreviewWIN.Invalidate(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, NumberTypeSelectHdl_Impl, weld::ComboBox&, rBox, void) +{ + bool bBmp = false; + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + // PAGEDESC does not exist + SvxNumType nNumType = static_cast(rBox.get_active_id().toUInt32()); + aNumFmt.SetNumberingType(nNumType); + sal_uInt16 nNumberingType = aNumFmt.GetNumberingType(); + if (SVX_NUM_BITMAP == (nNumberingType & (~LINK_TOKEN))) + { + bBmp |= nullptr != aNumFmt.GetBrush(); + aNumFmt.SetIncludeUpperLevels(0); + aNumFmt.SetListFormat("", "", i); + if (!bBmp) + aNumFmt.SetGraphic(""); + pActNum->SetLevel(i, aNumFmt); + SwitchNumberType(SHOW_BITMAP); + } + else if (SVX_NUM_CHAR_SPECIAL == nNumberingType) + { + aNumFmt.SetIncludeUpperLevels(0); + aNumFmt.SetListFormat("", "", i); + if (!aNumFmt.GetBulletFont()) + aNumFmt.SetBulletFont(&aActBulletFont); + if (!aNumFmt.GetBulletChar()) + aNumFmt.SetBulletChar(SVX_DEF_BULLET); + pActNum->SetLevel(i, aNumFmt); + SwitchNumberType(SHOW_BULLET); + // allocation of the drawing pattern is automatic + } + else + { + aNumFmt.SetListFormat(m_xPrefixED->get_text(), m_xSuffixED->get_text(), i); + SwitchNumberType(SHOW_NUMBERING); + pActNum->SetLevel(i, aNumFmt); + CheckForStartValue_Impl(nNumberingType); + + // allocation of the drawing pattern is automatic + } + } + nMask <<= 1; + } + + SetModified(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, BulColorHdl_Impl, ColorListBox&, rColorBox, void) +{ + Color nSetColor = rColorBox.GetSelectEntryColor(); + + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetBulletColor(nSetColor); + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, BulRelSizeHdl_Impl, weld::MetricSpinButton&, rField, void) +{ + sal_uInt16 nRelSize = rField.get_value(FieldUnit::PERCENT); + + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetBulletRelSize(nRelSize); + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + SetModified(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, GraphicHdl_Impl, const OString&, rIdent, void) +{ + OUString aGrfName; + Size aSize; + bool bSucc(false); + SvxOpenGraphicDialog aGrfDlg(SdResId(RID_SVXSTR_EDIT_GRAPHIC), p_Window); + + OString sNumber; + if (rIdent.startsWith("gallery", &sNumber)) + { + auto idx = sNumber.toUInt32(); + if (idx < aGrfNames.size()) + { + aGrfName = aGrfNames[idx]; + Graphic aGraphic; + if (GalleryExplorer::GetGraphicObj(GALLERY_THEME_BULLETS, idx, &aGraphic)) + { + aSize = SvxNumberFormat::GetGraphicSizeMM100(&aGraphic); + bSucc = true; + } + } + } + else if (rIdent == "fromfile") + { + aGrfDlg.EnableLink(false); + aGrfDlg.AsLink(false); + if (!aGrfDlg.Execute()) + { + // memorize selected filter + aGrfName = aGrfDlg.GetPath(); + + Graphic aGraphic; + if (!aGrfDlg.GetGraphic(aGraphic)) + { + aSize = SvxNumberFormat::GetGraphicSizeMM100(&aGraphic); + bSucc = true; + } + } + } + if (!bSucc) + return; + + aSize = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(eCoreUnit)); + + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetCharFormatName(m_sNumCharFmtName); + aNumFmt.SetGraphic(aGrfName); + + // set size for a later comparison + const SvxBrushItem* pBrushItem = aNumFmt.GetBrush(); + // initiate asynchronous loading + sal_Int16 eOrient = aNumFmt.GetVertOrient(); + aNumFmt.SetGraphicBrush(pBrushItem, &aSize, &eOrient); + aInitSize[i] = aNumFmt.GetGraphicSize(); + + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + m_xRatioCB->set_sensitive(true); + m_xWidthFT->set_sensitive(true); + m_xHeightFT->set_sensitive(true); + m_xWidthMF->set_sensitive(true); + m_xHeightMF->set_sensitive(true); + SetMetricValue(*m_xWidthMF, aSize.Width(), eCoreUnit); + SetMetricValue(*m_xHeightMF, aSize.Height(), eCoreUnit); + + SetModified(); + //needed due to asynchronous loading of graphics in the SvxBrushItem + aInvalidateTimer.Start(); +} + +IMPL_LINK_NOARG(SvxBulletAndPositionDlg, PopupActivateHdl_Impl, weld::Toggleable&, void) +{ + if (m_xGalleryMenu) + return; + + m_xGalleryMenu = m_xBuilder->weld_menu("gallerysubmenu"); + weld::WaitObject aWait(p_Window); + + if (!GalleryExplorer::FillObjList(GALLERY_THEME_BULLETS, aGrfNames)) + return; + + GalleryExplorer::BeginLocking(GALLERY_THEME_BULLETS); + + Graphic aGraphic; + OUString sGrfName; + ScopedVclPtrInstance pVD; + size_t i = 0; + for (const auto& grfName : aGrfNames) + { + sGrfName = grfName; + OUString sItemId = "gallery" + OUString::number(i); + INetURLObject aObj(sGrfName); + if (aObj.GetProtocol() == INetProtocol::File) + sGrfName = aObj.PathToFileName(); + if (GalleryExplorer::GetGraphicObj(GALLERY_THEME_BULLETS, i, &aGraphic)) + { + BitmapEx aBitmap(aGraphic.GetBitmapEx()); + Size aSize(aBitmap.GetSizePixel()); + if (aSize.Width() > MAX_BMP_WIDTH || aSize.Height() > MAX_BMP_HEIGHT) + { + bool bWidth = aSize.Width() > aSize.Height(); + double nScale = bWidth + ? double(MAX_BMP_WIDTH) / static_cast(aSize.Width()) + : double(MAX_BMP_HEIGHT) / static_cast(aSize.Height()); + aBitmap.Scale(nScale, nScale); + } + pVD->SetOutputSizePixel(aBitmap.GetSizePixel(), false); + pVD->DrawBitmapEx(Point(), aBitmap); + + // We want to show only icon names not full path. + aObj.removeExtension(); + OUString sIconName = aObj.GetLastName(INetURLObject::DecodeMechanism::WithCharset); + + m_xGalleryMenu->append(sItemId, sIconName, *pVD); + } + else + { + m_xGalleryMenu->append(sItemId, sGrfName); + } + ++i; + } + GalleryExplorer::EndLocking(GALLERY_THEME_BULLETS); +} + +IMPL_LINK_NOARG(SvxBulletAndPositionDlg, BulletHdl_Impl, weld::Button&, void) +{ + SvxCharacterMap aMap(p_Window, nullptr, nullptr); + + sal_uInt16 nMask = 1; + std::optional pFmtFont; + bool bSameBullet = true; + sal_UCS4 cBullet = 0; + bool bFirst = true; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + const SvxNumberFormat& rCurFmt = pActNum->GetLevel(i); + if (bFirst) + { + cBullet = rCurFmt.GetBulletChar(); + } + else if (rCurFmt.GetBulletChar() != cBullet) + { + bSameBullet = false; + break; + } + if (!pFmtFont) + pFmtFont = rCurFmt.GetBulletFont(); + bFirst = false; + } + nMask <<= 1; + } + + if (pFmtFont) + aMap.SetCharFont(*pFmtFont); + else + aMap.SetCharFont(aActBulletFont); + if (bSameBullet) + aMap.SetChar(cBullet); + if (aMap.run() != RET_OK) + return; + + // change Font Numrules + aActBulletFont = aMap.GetCharFont(); + + sal_uInt16 _nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & _nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetBulletFont(&aActBulletFont); + aNumFmt.SetBulletChar(aMap.GetChar()); + pActNum->SetLevel(i, aNumFmt); + } + _nMask <<= 1; + } + + SetModified(); +} + +IMPL_LINK(SvxBulletAndPositionDlg, SizeHdl_Impl, weld::MetricSpinButton&, rField, void) +{ + bool bWidth = &rField == m_xWidthMF.get(); + bLastWidthModified = bWidth; + bool bRatio = m_xRatioCB->get_active(); + tools::Long nWidthVal = static_cast( + m_xWidthMF->denormalize(m_xWidthMF->get_value(FieldUnit::MM_100TH))); + tools::Long nHeightVal = static_cast( + m_xHeightMF->denormalize(m_xHeightMF->get_value(FieldUnit::MM_100TH))); + nWidthVal = OutputDevice::LogicToLogic(nWidthVal, MapUnit::Map100thMM, eCoreUnit); + nHeightVal = OutputDevice::LogicToLogic(nHeightVal, MapUnit::Map100thMM, eCoreUnit); + double fSizeRatio; + + bool bRepaint = false; + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + if (SVX_NUM_BITMAP == (aNumFmt.GetNumberingType() & (~LINK_TOKEN))) + { + Size aSize(aNumFmt.GetGraphicSize()); + Size aSaveSize(aSize); + + if (aInitSize[i].Height()) + fSizeRatio = static_cast(aInitSize[i].Width()) + / static_cast(aInitSize[i].Height()); + else + fSizeRatio = double(1); + + if (bWidth) + { + tools::Long nDelta = nWidthVal - aInitSize[i].Width(); + aSize.setWidth(nWidthVal); + if (bRatio) + { + aSize.setHeight( + aInitSize[i].Height() + + static_cast(static_cast(nDelta) / fSizeRatio)); + m_xHeightMF->set_value(m_xHeightMF->normalize(OutputDevice::LogicToLogic( + aSize.Height(), eCoreUnit, MapUnit::Map100thMM)), + FieldUnit::MM_100TH); + } + } + else + { + tools::Long nDelta = nHeightVal - aInitSize[i].Height(); + aSize.setHeight(nHeightVal); + if (bRatio) + { + aSize.setWidth( + aInitSize[i].Width() + + static_cast(static_cast(nDelta) * fSizeRatio)); + m_xWidthMF->set_value(m_xWidthMF->normalize(OutputDevice::LogicToLogic( + aSize.Width(), eCoreUnit, MapUnit::Map100thMM)), + FieldUnit::MM_100TH); + } + } + const SvxBrushItem* pBrushItem = aNumFmt.GetBrush(); + sal_Int16 eOrient = aNumFmt.GetVertOrient(); + if (aSize != aSaveSize) + bRepaint = true; + aNumFmt.SetGraphicBrush(pBrushItem, &aSize, &eOrient); + pActNum->SetLevel(i, aNumFmt); + } + } + nMask <<= 1; + } + SetModified(bRepaint); +} + +IMPL_LINK(SvxBulletAndPositionDlg, RatioHdl_Impl, weld::Toggleable&, rBox, void) +{ + if (rBox.get_active()) + { + if (bLastWidthModified) + SizeHdl_Impl(*m_xWidthMF); + else + SizeHdl_Impl(*m_xHeightMF); + } +} + +IMPL_LINK(SvxBulletAndPositionDlg, SelectLeftAlignmentHdl_Impl, weld::Toggleable&, rButton, void) +{ + if (rButton.get_active()) + { + SetAlignmentHdl_Impl(SvxAdjust::Left); + + m_xCenterTB->set_active(false); + m_xRightTB->set_active(false); + + SetModified(); + } +} + +IMPL_LINK(SvxBulletAndPositionDlg, SelectCenterAlignmentHdl_Impl, weld::Toggleable&, rButton, void) +{ + if (rButton.get_active()) + { + SetAlignmentHdl_Impl(SvxAdjust::Center); + + m_xLeftTB->set_active(false); + m_xRightTB->set_active(false); + + SetModified(); + } +} + +IMPL_LINK(SvxBulletAndPositionDlg, SelectRightAlignmentHdl_Impl, weld::Toggleable&, rButton, void) +{ + if (rButton.get_active()) + { + SetAlignmentHdl_Impl(SvxAdjust::Right); + + m_xLeftTB->set_active(false); + m_xCenterTB->set_active(false); + + SetModified(); + } +} + +void SvxBulletAndPositionDlg::SetAlignmentHdl_Impl(SvxAdjust eAdjust) +{ + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + aNumFmt.SetNumAdjust(eAdjust); + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } +} + +IMPL_LINK(SvxBulletAndPositionDlg, ApplyToMasterHdl_Impl, weld::Toggleable&, rButton, void) +{ + bApplyToMaster = rButton.get_active(); +} + +IMPL_LINK_NOARG(SvxBulletAndPositionDlg, ResetHdl_Impl, weld::Button&, void) +{ + Reset(&rFirstStateSet); +} + +IMPL_LINK(SvxBulletAndPositionDlg, EditModifyHdl_Impl, weld::Entry&, rEdit, void) +{ + EditModifyHdl_Impl(&rEdit); +} + +IMPL_LINK(SvxBulletAndPositionDlg, SpinModifyHdl_Impl, weld::SpinButton&, rSpinButton, void) +{ + EditModifyHdl_Impl(&rSpinButton); +} + +IMPL_LINK(SvxBulletAndPositionDlg, DistanceHdl_Impl, weld::MetricSpinButton&, rFld, void) +{ + if (bInInitControl) + return; + tools::Long nValue = GetCoreValue(rFld, eCoreUnit); + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + if (&rFld == m_xDistBorderMF.get()) + { + if (m_xRelativeCB->get_active()) + { + if (0 == i) + { + auto const nTmp = aNumFmt.GetFirstLineOffset(); + aNumFmt.SetAbsLSpace(nValue - nTmp); + } + else + { + tools::Long nTmp = pActNum->GetLevel(i - 1).GetAbsLSpace() + + pActNum->GetLevel(i - 1).GetFirstLineOffset() + - pActNum->GetLevel(i).GetFirstLineOffset(); + + aNumFmt.SetAbsLSpace(nValue + nTmp); + } + } + else + { + aNumFmt.SetAbsLSpace(nValue - aNumFmt.GetFirstLineOffset()); + } + } + else if (&rFld == m_xIndentMF.get()) + { + // together with the FirstLineOffset the AbsLSpace must be changed, too + tools::Long nDiff = nValue + aNumFmt.GetFirstLineOffset(); + auto const nAbsLSpace = aNumFmt.GetAbsLSpace(); + aNumFmt.SetAbsLSpace(nAbsLSpace + nDiff); + aNumFmt.SetFirstLineOffset(-nValue); + } + + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + + SetModified(); + if (!m_xDistBorderMF->get_sensitive()) + { + m_xDistBorderMF->set_text(""); + } + + sal_Int32 aLastLevelLSpace + = pActNum->GetLevel(pActNum->GetLevelCount() - 1).GetAbsLSpace() / 40; + m_aPreviewWIN.set_size_request(aLastLevelLSpace, 300); +} + +IMPL_LINK(SvxBulletAndPositionDlg, RelativeHdl_Impl, weld::Toggleable&, rBox, void) +{ + bool bOn = rBox.get_active(); + bool bSingleSelection = m_xLevelLB->count_selected_rows() == 1 && SAL_MAX_UINT16 != nActNumLvl; + bool bSetValue = false; + tools::Long nValue = 0; + if (bOn || bSingleSelection) + { + sal_uInt16 nMask = 1; + bool bFirst = true; + bSetValue = true; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + const SvxNumberFormat& rNumFmt = pActNum->GetLevel(i); + if (bFirst) + { + nValue = rNumFmt.GetAbsLSpace() + rNumFmt.GetFirstLineOffset(); + if (bOn && i) + nValue -= (pActNum->GetLevel(i - 1).GetAbsLSpace() + + pActNum->GetLevel(i - 1).GetFirstLineOffset()); + } + else + bSetValue = nValue + == (rNumFmt.GetAbsLSpace() + rNumFmt.GetFirstLineOffset()) + - (pActNum->GetLevel(i - 1).GetAbsLSpace() + + pActNum->GetLevel(i - 1).GetFirstLineOffset()); + bFirst = false; + } + nMask <<= 1; + } + } + if (bSetValue) + SetMetricValue(*m_xDistBorderMF, nValue, eCoreUnit); + else + m_xDistBorderMF->set_text(""); + m_xDistBorderMF->set_sensitive(bOn || bSingleSelection); + m_xDistBorderFT->set_sensitive(bOn || bSingleSelection); + bLastRelative = bOn; +} + +void SvxBulletAndPositionDlg::EditModifyHdl_Impl(const weld::Entry* pEdit) +{ + bool bPrefixOrSuffix = (pEdit == m_xPrefixED.get()) || (pEdit == m_xSuffixED.get()); + bool bStart = pEdit == m_xStartED.get(); + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); i++) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + if (bPrefixOrSuffix) + aNumFmt.SetListFormat(m_xPrefixED->get_text(), m_xSuffixED->get_text(), i); + else if (bStart) + aNumFmt.SetStart(m_xStartED->get_value()); + pActNum->SetLevel(i, aNumFmt); + } + nMask <<= 1; + } + SetModified(); +} + +void SvxBulletAndPositionDlg::SetModified(bool bRepaint) +{ + bModified = true; + if (bRepaint) + { + m_aPreviewWIN.SetLevel(nActNumLvl); + m_aPreviewWIN.Invalidate(); + } +} + +void SvxBulletAndPositionDlg::InitPosAndSpaceMode() +{ + if (pActNum == nullptr) + { + SAL_WARN("cui.tabpages", " - misusage of " + "method -> has to be already set!"); + return; + } + + SvxNumberFormat::SvxNumPositionAndSpaceMode ePosAndSpaceMode = SvxNumberFormat::LABEL_ALIGNMENT; + sal_uInt16 nMask = 1; + for (sal_uInt16 i = 0; i < pActNum->GetLevelCount(); ++i) + { + if (nActNumLvl & nMask) + { + SvxNumberFormat aNumFmt(pActNum->GetLevel(i)); + ePosAndSpaceMode = aNumFmt.GetPositionAndSpaceMode(); + if (ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT) + { + break; + } + } + nMask <<= 1; + } + + bLabelAlignmentPosAndSpaceModeActive = ePosAndSpaceMode == SvxNumberFormat::LABEL_ALIGNMENT; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/LayerTabBar.cxx b/sd/source/ui/dlg/LayerTabBar.cxx new file mode 100644 index 000000000..41cc90ac7 --- /dev/null +++ b/sd/source/ui/dlg/LayerTabBar.cxx @@ -0,0 +1,437 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + +/** + * default constructor + */ +LayerTabBar::LayerTabBar(DrawViewShell* pViewSh, vcl::Window* pParent) + : TabBar( pParent, WinBits( WB_BORDER | WB_3DLOOK | WB_SCROLL ) ), + DropTargetHelper( this ), + pDrViewSh(pViewSh) +{ + EnableEditMode(); + SetSizePixel(Size(0, 0)); + SetMaxPageWidth( 150 ); + SetHelpId( HID_SD_TABBAR_LAYERS ); +} + +LayerTabBar::~LayerTabBar() +{ + disposeOnce(); +} + +void LayerTabBar::dispose() +{ + DropTargetHelper::dispose(); + TabBar::dispose(); +} + +OUString LayerTabBar::convertToLocalizedName(const OUString& rName) +{ + if ( rName == sUNO_LayerName_background ) + return SdResId( STR_LAYER_BCKGRND ); + + if ( rName == sUNO_LayerName_background_objects ) + return SdResId( STR_LAYER_BCKGRNDOBJ ); + + if ( rName == sUNO_LayerName_layout ) + return SdResId( STR_LAYER_LAYOUT ); + + if ( rName == sUNO_LayerName_controls ) + return SdResId( STR_LAYER_CONTROLS ); + + if ( rName == sUNO_LayerName_measurelines ) + return SdResId( STR_LAYER_MEASURELINES ); + + return rName; +} + +// Use a method name, that is specific to LayerTabBar to make code better readable +OUString LayerTabBar::GetLayerName(sal_uInt16 nPageId) const +{ + return GetAuxiliaryText(nPageId); +} + +void LayerTabBar::SetLayerName( sal_uInt16 nPageId, const OUString& rText ) +{ + SetAuxiliaryText(nPageId, rText); +} + +// Here "Page" is a tab in the LayerTabBar. +void LayerTabBar::InsertPage( sal_uInt16 nPageId, const OUString& rText, + TabBarPageBits nBits, sal_uInt16 nPos) +{ + OUString sLocalizedName(convertToLocalizedName(rText)); + TabBar::InsertPage(nPageId, sLocalizedName, nBits, nPos ); + SetLayerName(nPageId, rText); +} + +void LayerTabBar::SetPageText( sal_uInt16 nPageId, const OUString& rText ) +{ + OUString sLocalizedName(convertToLocalizedName(rText)); + SetLayerName(nPageId, rText); + TabBar::SetPageText(nPageId, sLocalizedName); +} + +bool LayerTabBar::IsLocalizedNameOfStandardLayer(std::u16string_view rName) +{ + return ( rName == SdResId(STR_LAYER_LAYOUT) + || rName == SdResId(STR_LAYER_CONTROLS) + || rName == SdResId(STR_LAYER_MEASURELINES) + || rName == SdResId(STR_LAYER_BCKGRND) + || rName == SdResId(STR_LAYER_BCKGRNDOBJ) ); +} + +bool LayerTabBar::IsRealNameOfStandardLayer(std::u16string_view rName) +{ + return ( rName == sUNO_LayerName_layout + || rName == sUNO_LayerName_controls + || rName == sUNO_LayerName_measurelines + || rName == sUNO_LayerName_background + || rName == sUNO_LayerName_background_objects ); +} + +void LayerTabBar::Select() +{ + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHLAYER, SfxCallMode::ASYNCHRON); +} + +void LayerTabBar::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bSetPageID=false; + + if (rMEvt.IsLeft()) + { + Point aPosPixel = rMEvt.GetPosPixel(); + sal_uInt16 aTabId = GetPageId( PixelToLogic(aPosPixel) ); + if (aTabId == 0) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_INSERTLAYER, SfxCallMode::SYNCHRON); + + bSetPageID=true; + } + else if (rMEvt.IsMod2()) + { + // direct editing of tab text + // make sure the clicked tab is the current tab otherwise Edit() acts on the wrong tab + if ( aTabId != GetCurPageId()) + { + MouseEvent aSyntheticEvent (rMEvt.GetPosPixel(), 1, MouseEventModifiers::SYNTHETIC, MOUSE_LEFT, 0); + TabBar::MouseButtonDown(aSyntheticEvent); + } + } + else if (rMEvt.IsMod1() || rMEvt.IsShift()) + { + // keyboard Shortcuts to change layer attributes + + OUString aName(GetLayerName(aTabId)); + SdrPageView* pPV = pDrViewSh->GetView()->GetSdrPageView(); + + // Save old state + + bool bOldPrintable = pPV->IsLayerPrintable(aName); + bool bOldVisible = pPV->IsLayerVisible(aName); + bool bOldLocked = pPV->IsLayerLocked(aName); + + bool bNewPrintable = bOldPrintable; + bool bNewVisible = bOldVisible; + bool bNewLocked = bOldLocked; + + if (rMEvt.IsMod1() && rMEvt.IsShift()) + { + // Shift+Ctrl: Toggle between layer printable / not printable + bNewPrintable = !bOldPrintable; + pPV->SetLayerPrintable(aName, bNewPrintable); + } + else if (rMEvt.IsShift()) + { + // Shift: Toggle between layer visible / hidden + bNewVisible = !bOldVisible; + pPV->SetLayerVisible(aName, bNewVisible); + } + else // if (rMEvt.IsMod1()) + { + // Ctrl: Toggle between layer locked / unlocked + bNewLocked = !bOldLocked; + pPV->SetLayerLocked(aName, bNewLocked); + } + + pDrViewSh->ResetActualLayer(); + + // Add Undo action + + ::sd::View* pView = pDrViewSh->GetView(); + DrawView* pDrView = dynamic_cast(pView); + + SdDrawDocument& rDoc = pView->GetDoc(); + SdrLayer* pLayer = rDoc.GetLayerAdmin().GetLayer(aName); + + if (pLayer) + { + assert (pDrView && "Change layer attribute undo action is only working with a SdDrawView"); + if(pDrView) + { + SfxUndoManager* pManager = rDoc.GetDocSh()->GetUndoManager(); + std::unique_ptr pAction(new SdLayerModifyUndoAction( + &rDoc, + pLayer, + aName, + pLayer->GetTitle(), + pLayer->GetDescription(), + bOldVisible, + bOldLocked, + bOldPrintable, + aName, + pLayer->GetTitle(), + pLayer->GetDescription(), + bNewVisible, + bNewLocked, + bNewPrintable + )); + pManager->AddUndoAction(std::move(pAction)); + } + } + + // Mark document changed + + pView->GetDoc().SetChanged(); + } + } + + // If you insert a new layer you must not call TabBar::MouseButtonDown(rMEvt); + // because you want to activate the new layer + if( !bSetPageID ) + TabBar::MouseButtonDown(rMEvt); +} + +void LayerTabBar::DoubleClick() +{ + if (GetCurPageId() != 0) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute( SID_MODIFYLAYER, SfxCallMode::SYNCHRON ); + } +} + +/** + * AcceptDrop-Event + */ + +sal_Int8 LayerTabBar::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if( rEvt.mbLeaving ) + EndSwitchPage(); + + if( !pDrViewSh->GetDocSh()->IsReadOnly() ) + { + Point aPos( PixelToLogic( rEvt.maPosPixel ) ); + OUString sLayerName( GetLayerName(GetPageId(aPos)) ); + SdrLayerID nLayerId = pDrViewSh->GetView()->GetDoc().GetLayerAdmin().GetLayerID(sLayerName); + + nRet = pDrViewSh->AcceptDrop( rEvt, *this, nullptr, SDRPAGE_NOTFOUND, nLayerId ); + + SwitchPage( aPos ); + } + + return nRet; +} + +/** + * ExecuteDrop-Event + */ +sal_Int8 LayerTabBar::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + Point aPos( PixelToLogic(rEvt.maPosPixel) ); + OUString sLayerName( GetLayerName(GetPageId(aPos)) ); + SdrLayerID nLayerId = pDrViewSh->GetView()->GetDoc().GetLayerAdmin().GetLayerID(sLayerName); + + sal_Int8 nRet = pDrViewSh->ExecuteDrop( rEvt, *this, nullptr, SDRPAGE_NOTFOUND, nLayerId ); + + EndSwitchPage(); + + return nRet; + +} + +void LayerTabBar::Command(const CommandEvent& rCEvt) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->ExecutePopup("layertab"); + } +} + +bool LayerTabBar::StartRenaming() +{ + bool bOK = true; + OUString aLayerName = GetLayerName( GetEditPageId() ); + + if ( IsRealNameOfStandardLayer(aLayerName)) + { + // It is not allowed to change these names + bOK = false; + } + else + { + ::sd::View* pView = pDrViewSh->GetView(); + + if ( pView->IsTextEdit() ) + { + pView->SdrEndTextEdit(); + } + } + + return bOK; +} + +TabBarAllowRenamingReturnCode LayerTabBar::AllowRenaming() +{ + bool bOK = true; + + // Check if names already exists + ::sd::View* pView = pDrViewSh->GetView(); + SdDrawDocument& rDoc = pView->GetDoc(); + OUString aLayerName = pView->GetActiveLayer(); + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + OUString aNewName( GetEditText() ); + + if (aNewName.isEmpty() || + (rLayerAdmin.GetLayer( aNewName ) && aLayerName != aNewName) ) + { + // Name already exists. + std::unique_ptr xWarn(Application::CreateMessageDialog(pDrViewSh->GetViewFrame()->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + bOK = false; + } + + if (bOK) + { + if ( IsLocalizedNameOfStandardLayer(aNewName) || IsRealNameOfStandardLayer(aNewName) ) + { + // Standard layer names may not be changed. + bOK = false; + } + } + + return bOK ? TABBAR_RENAMING_YES : TABBAR_RENAMING_NO; +} + +void LayerTabBar::EndRenaming() +{ + if( IsEditModeCanceled() ) + return; + + ::sd::View* pView = pDrViewSh->GetView(); + DrawView* pDrView = dynamic_cast( pView ); + + SdDrawDocument& rDoc = pView->GetDoc(); + OUString aLayerName = pView->GetActiveLayer(); + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayer* pLayer = rLayerAdmin.GetLayer(aLayerName); + + if (!pLayer) + return; + + OUString aNewName( GetEditText() ); + assert (pDrView && "Rename layer undo action is only working with a SdDrawView"); + if( pDrView ) + { + SfxUndoManager* pManager = rDoc.GetDocSh()->GetUndoManager(); + std::unique_ptr pAction(new SdLayerModifyUndoAction( + &rDoc, + pLayer, + aLayerName, + pLayer->GetTitle(), + pLayer->GetDescription(), + pDrView->IsLayerVisible(aLayerName), + pDrView->IsLayerLocked(aLayerName), + pDrView->IsLayerPrintable(aLayerName), + aNewName, + pLayer->GetTitle(), + pLayer->GetDescription(), + pDrView->IsLayerVisible(aLayerName), + pDrView->IsLayerLocked(aLayerName), + pDrView->IsLayerPrintable(aLayerName) + )); + pManager->AddUndoAction( std::move(pAction) ); + } + + // First notify View since SetName() calls ResetActualLayer() and + // the View then already has to know the Layer + pView->SetActiveLayer(aNewName); + pLayer->SetName(aNewName); + rDoc.SetChanged(); +} + +void LayerTabBar::ActivatePage() +{ + if (pDrViewSh!=nullptr) + { + + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHLAYER, SfxCallMode::ASYNCHRON); + } +} + +void LayerTabBar::SendActivatePageEvent() +{ + CallEventListeners (VclEventId::TabbarPageActivated, + reinterpret_cast(GetCurPageId())); +} + +void LayerTabBar::SendDeactivatePageEvent() +{ + CallEventListeners (VclEventId::TabbarPageDeactivated, + reinterpret_cast(GetCurPageId())); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/NavigatorChildWindow.cxx b/sd/source/ui/dlg/NavigatorChildWindow.cxx new file mode 100644 index 000000000..6055c238a --- /dev/null +++ b/sd/source/ui/dlg/NavigatorChildWindow.cxx @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + +static void RequestNavigatorUpdate (SfxBindings const * pBindings) +{ + if (pBindings != nullptr + && pBindings->GetDispatcher() != nullptr) + { + SfxBoolItem aItem (SID_NAVIGATOR_INIT, true); + pBindings->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_INIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + } +} + +SdNavigatorFloat::SdNavigatorFloat(SfxBindings* _pBindings, SfxChildWindow* _pMgr, + vcl::Window* _pParent, SfxChildWinInfo* pInfo) + : SfxNavigator(_pBindings, _pMgr, _pParent, pInfo) + , m_xNavWin(std::make_unique(m_xContainer.get(), _pBindings, this)) + , m_bSetInitialFocusOnActivate(true) +{ + m_xNavWin->SetUpdateRequestFunctor( + [_pBindings] () { return RequestNavigatorUpdate(_pBindings); }); + + SetMinOutputSizePixel(GetOptimalSize()); +} + +void SdNavigatorFloat::Activate() +{ + SfxNavigator::Activate(); + // tdf#141708 defer grabbing focus to preferred widget until the float is + // first activated + if (m_bSetInitialFocusOnActivate) + { + m_xNavWin->FirstFocus(); + m_bSetInitialFocusOnActivate = false; + } +} + +void SdNavigatorFloat::InitTreeLB(const SdDrawDocument* pDoc) +{ + m_xNavWin->InitTreeLB(pDoc); +} + +void SdNavigatorFloat::FreshTree(const SdDrawDocument* pDoc) +{ + m_xNavWin->FreshTree(pDoc); +} + +void SdNavigatorFloat::dispose() +{ + m_xNavWin.reset(); + SfxNavigator::dispose(); +} + +SdNavigatorFloat::~SdNavigatorFloat() +{ + disposeOnce(); +} + +SFX_IMPL_DOCKINGWINDOW(SdNavigatorWrapper, SID_NAVIGATOR); + +SdNavigatorWrapper::SdNavigatorWrapper(vcl::Window *_pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo* pInfo) + : SfxNavigatorWrapper(_pParent, nId) +{ + SetWindow(VclPtr::Create(pBindings, this, _pParent, pInfo)); + Initialize(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PaneChildWindows.cxx b/sd/source/ui/dlg/PaneChildWindows.cxx new file mode 100644 index 000000000..7f73e005b --- /dev/null +++ b/sd/source/ui/dlg/PaneChildWindows.cxx @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace sd { + + +SFX_IMPL_DOCKINGWINDOW_WITHID(LeftPaneImpressChildWindow, SID_LEFT_PANE_IMPRESS) +SFX_IMPL_DOCKINGWINDOW_WITHID(LeftPaneDrawChildWindow, SID_LEFT_PANE_DRAW) + +//===== PaneChildWindow ======================================================= +PaneChildWindow::PaneChildWindow ( + vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo, + TranslateId pTitleBarResId) + : SfxChildWindow (pParentWindow, nId) +{ + SetWindow( VclPtr::Create( + pBindings, + this, + pParentWindow, + SdResId(pTitleBarResId))); + SetAlignment(SfxChildAlignment::LEFT); + SfxDockingWindow* pDockingWindow = static_cast(GetWindow()); + pDockingWindow->EnableInput(); + pDockingWindow->Initialize(pInfo); + SetHideNotDelete(true); + + ViewShellBase* pBase = ViewShellBase::GetViewShellBase(pBindings->GetDispatcher()->GetFrame()); + if (pBase != nullptr) + { + framework::FrameworkHelper::Instance(*pBase)->UpdateConfiguration(); + } +} + +PaneChildWindow::~PaneChildWindow() +{ + ViewShellBase* pBase = nullptr; + PaneDockingWindow* pDockingWindow = dynamic_cast(GetWindow()); + if (pDockingWindow != nullptr) + pBase = ViewShellBase::GetViewShellBase( + pDockingWindow->GetBindings().GetDispatcher()->GetFrame()); + if (pBase != nullptr) + framework::FrameworkHelper::Instance(*pBase)->UpdateConfiguration(); +} + +//===== LeftPaneImpressChildWindow ============================================ +LeftPaneImpressChildWindow::LeftPaneImpressChildWindow ( + vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo) + : PaneChildWindow( + pParentWindow, + nId, + pBindings, + pInfo, + STR_LEFT_PANE_IMPRESS_TITLE) +{ +} + +//===== LeftPaneDrawChildWindow =============================================== +LeftPaneDrawChildWindow::LeftPaneDrawChildWindow ( + vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo) + : PaneChildWindow( + pParentWindow, + nId, + pBindings, + pInfo, + STR_LEFT_PANE_DRAW_TITLE) +{ +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PaneDockingWindow.cxx b/sd/source/ui/dlg/PaneDockingWindow.cxx new file mode 100644 index 000000000..6f7332ad9 --- /dev/null +++ b/sd/source/ui/dlg/PaneDockingWindow.cxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::TitledDockingWindow; + +namespace sd { + +PaneDockingWindow::PaneDockingWindow( + SfxBindings *_pBindings, SfxChildWindow *pChildWindow, vcl::Window* pParent, + const OUString& rsTitle ) + : TitledDockingWindow(_pBindings, pChildWindow, pParent) +{ + SetTitle(rsTitle); + SetSizePixel(LogicToPixel(Size(80,200), MapMode(MapUnit::MapAppFont))); +} + +PaneDockingWindow::~PaneDockingWindow() +{ +} + +void PaneDockingWindow::StateChanged( StateChangedType nType ) +{ + switch (nType) + { + case StateChangedType::InitShow: + Resize(); + GetContentWindow().SetStyle(GetContentWindow().GetStyle() | WB_DIALOGCONTROL); + break; + + case StateChangedType::Visible: + { + // The visibility of the docking window has changed. Tell the + // ConfigurationController so that it can activate or deactivate + // a/the view for the pane. + // Without this the side panes remain empty after closing an + // in-place slide show. + ViewShellBase* pBase = ViewShellBase::GetViewShellBase( + GetBindings().GetDispatcher()->GetFrame()); + if (pBase != nullptr) + { + framework::FrameworkHelper::Instance(*pBase)->UpdateConfiguration(); + } + } + break; + + default:; + } + SfxDockingWindow::StateChanged (nType); +} + +void PaneDockingWindow::MouseButtonDown (const MouseEvent& rEvent) +{ + if (rEvent.GetButtons() == MOUSE_LEFT) + { + // For some strange reason we have to set the WB_DIALOGCONTROL at + // the content window in order to have it pass focus to its content + // window. Without setting this flag here that works only on views + // that have not been taken from the cash and relocated to this pane + // docking window. + GetContentWindow().SetStyle(GetContentWindow().GetStyle() | WB_DIALOGCONTROL); + GetContentWindow().GrabFocus(); + } + SfxDockingWindow::MouseButtonDown(rEvent); +} + +void PaneDockingWindow::SetValidSizeRange (const Range& rValidSizeRange) +{ + SplitWindow* pSplitWindow = dynamic_cast(GetParent()); + if (pSplitWindow == nullptr) + return; + + const sal_uInt16 nId (pSplitWindow->GetItemId(static_cast< vcl::Window*>(this))); + const sal_uInt16 nSetId (pSplitWindow->GetSet(nId)); + // Because the PaneDockingWindow paints its own decoration, we have + // to compensate the valid size range for that. + const SvBorder aBorder (GetDecorationBorder()); + sal_Int32 nCompensation (pSplitWindow->IsHorizontal() + ? aBorder.Top() + aBorder.Bottom() + : aBorder.Left() + aBorder.Right()); + pSplitWindow->SetItemSizeRange( + nSetId, + Range( + rValidSizeRange.Min() + nCompensation, + rValidSizeRange.Max() + nCompensation)); +} + +PaneDockingWindow::Orientation PaneDockingWindow::GetOrientation() const +{ + SplitWindow* pSplitWindow = dynamic_cast(GetParent()); + if (pSplitWindow == nullptr) + return UnknownOrientation; + else if (pSplitWindow->IsHorizontal()) + return HorizontalOrientation; + else + return VerticalOrientation; +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PaneShells.cxx b/sd/source/ui/dlg/PaneShells.cxx new file mode 100644 index 000000000..8870d1186 --- /dev/null +++ b/sd/source/ui/dlg/PaneShells.cxx @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include + +namespace sd { + +//===== LeftImpressPaneShell ================================================== + +static SfxSlot aLeftImpressPaneShellSlots_Impl[] = +{ + { 0, SfxGroupId::NONE, SfxSlotMode::NONE, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, SfxDisableFlags::NONE, nullptr } +}; + +SFX_IMPL_INTERFACE(LeftImpressPaneShell, SfxShell) + +void LeftImpressPaneShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(::sd::LeftPaneImpressChildWindow::GetChildWindowId()); +} + + +LeftImpressPaneShell::LeftImpressPaneShell() +{ + SetName("LeftImpressPane"); +} + +LeftImpressPaneShell::~LeftImpressPaneShell() +{ +} + +//===== LeftDrawPaneShell ===================================================== + +static SfxSlot aLeftDrawPaneShellSlots_Impl[] = +{ + { 0, SfxGroupId::NONE, SfxSlotMode::NONE, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, 0, SfxDisableFlags::NONE, nullptr } +}; + +SFX_IMPL_INTERFACE(LeftDrawPaneShell, SfxShell) + +void LeftDrawPaneShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(::sd::LeftPaneDrawChildWindow::GetChildWindowId()); +} + + +LeftDrawPaneShell::LeftDrawPaneShell() +{ + SetName("LeftDrawPane"); +} + +LeftDrawPaneShell::~LeftDrawPaneShell() +{ +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PhotoAlbumDialog.cxx b/sd/source/ui/dlg/PhotoAlbumDialog.cxx new file mode 100644 index 000000000..f63afe7bb --- /dev/null +++ b/sd/source/ui/dlg/PhotoAlbumDialog.cxx @@ -0,0 +1,775 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* +* This file is part of the LibreOffice project. +* +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PhotoAlbumDialog.hxx" +#include +#include +#include +#include + +namespace sd +{ + +SdPhotoAlbumDialog::SdPhotoAlbumDialog(weld::Window* pWindow, SdDrawDocument* pActDoc) + : GenericDialogController(pWindow, "modules/simpress/ui/photoalbum.ui", "PhotoAlbumCreatorDialog") + , m_pDoc(pActDoc) + , m_aImg(m_xDialog.get()) + , m_xCancelBtn(m_xBuilder->weld_button("cancel")) + , m_xCreateBtn(m_xBuilder->weld_button("ok")) + , m_xAddBtn(m_xBuilder->weld_button("add_btn")) + , m_xUpBtn(m_xBuilder->weld_button("up_btn")) + , m_xDownBtn(m_xBuilder->weld_button("down_btn")) + , m_xRemoveBtn(m_xBuilder->weld_button("rem_btn")) + , m_xImagesLst(m_xBuilder->weld_tree_view("images_tree")) + , m_xImg(new weld::CustomWeld(*m_xBuilder, "preview_img", m_aImg)) + , m_xInsTypeCombo(m_xBuilder->weld_combo_box("opt_combo")) + , m_xASRCheck(m_xBuilder->weld_check_button("asr_check")) + , m_xASRCheckCrop(m_xBuilder->weld_check_button("asr_check_crop")) + , m_xCapCheck(m_xBuilder->weld_check_button("cap_check")) + , m_xInsertAsLinkCheck(m_xBuilder->weld_check_button("insert_as_link_check")) +{ + m_xCancelBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, CancelHdl)); + m_xCreateBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, CreateHdl)); + + m_xAddBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, FileHdl)); + m_xUpBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, UpHdl)); + m_xUpBtn->set_sensitive(false); + m_xDownBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, DownHdl)); + m_xDownBtn->set_sensitive(false); + m_xRemoveBtn->connect_clicked(LINK(this, SdPhotoAlbumDialog, RemoveHdl)); + m_xRemoveBtn->set_sensitive(false); + m_xImagesLst->connect_changed(LINK(this, SdPhotoAlbumDialog, SelectHdl)); + m_xInsTypeCombo->connect_changed(LINK(this, SdPhotoAlbumDialog, TypeSelectHdl)); + + m_pGraphicFilter = new GraphicFilter; + m_xAddBtn->grab_focus(); +} + +SdPhotoAlbumDialog::~SdPhotoAlbumDialog() +{ +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, CancelHdl, weld::Button&, void) +{ + m_xDialog->response(RET_CANCEL); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, CreateHdl, weld::Button&, void) +{ + if (m_xImagesLst->n_children() == 0) + { + std::unique_ptr xWarn(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_PHOTO_ALBUM_EMPTY_WARNING))); + xWarn->run(); + } + else + { + Reference< drawing::XDrawPagesSupplier > xDPS( m_pDoc->getUnoModel(), uno::UNO_QUERY ); + Reference< drawing::XDrawPages > xDrawPages = xDPS->getDrawPages(); + Reference< lang::XMultiServiceFactory > xShapeFactory( m_pDoc->getUnoModel(), uno::UNO_QUERY ); + + Reference< XComponentContext > xContext(::comphelper::getProcessComponentContext()); + Reference< graphic::XGraphicProvider> xProvider(graphic::GraphicProvider::create(xContext)); + + // determine if to use Captions (use TitleObject) and choose the correct AutoLayout + // from the beginning + const bool bCreateCaptions(m_xCapCheck->get_active()); + const bool bInsertAsLink(m_xInsertAsLinkCheck->get_active()); + const AutoLayout aAutoLayout(bCreateCaptions ? AUTOLAYOUT_TITLE_ONLY : AUTOLAYOUT_NONE); + + // get the option + const int nOpt = m_xInsTypeCombo->get_active(); + if (nOpt == ONE_IMAGE) + { + for( sal_Int32 i = 0; i < m_xImagesLst->n_children(); ++i ) + { + OUString sUrl = m_xImagesLst->get_id(i); + + Reference< drawing::XDrawPage > xSlide = appendNewSlide(aAutoLayout, xDrawPages); + Reference< beans::XPropertySet > xSlideProps( xSlide, uno::UNO_QUERY ); + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl); + + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Size aPageSize; + + xSlideProps->getPropertyValue( + "Width") >>= aPageSize.Width; + xSlideProps->getPropertyValue( + "Height") >>= aPageSize.Height; + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active() && !m_xASRCheckCrop->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, aPageSize); + } + else if (m_xASRCheckCrop->get_active()) + { + aPicSize = createASRSizeCrop(aPicSize, aPageSize); + } + + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width - aPicSize.Width)/2; + aPicPos.Y = (aPageSize.Height - aPicSize.Height)/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + if (bCreateCaptions) + createCaption( aPageSize ); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + } + else if( nOpt == TWO_IMAGES ) + { + for( sal_Int32 i = 0; i < m_xImagesLst->n_children(); i+=2 ) + { + // create the slide + Reference< drawing::XDrawPage > xSlide = appendNewSlide(aAutoLayout, xDrawPages); + Reference< beans::XPropertySet > xSlideProps( xSlide, uno::UNO_QUERY ); + //Slide dimensions + ::awt::Size aPageSize; + + xSlideProps->getPropertyValue( + "Width") >>= aPageSize.Width; + xSlideProps->getPropertyValue( + "Height") >>= aPageSize.Height; + + // grab the left one + OUString sUrl1 = m_xImagesLst->get_id(i); + // grab the right one + OUString sUrl2 = m_xImagesLst->get_id(i+1); + + if( !sUrl1.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl1, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl1); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2); + aPicPos.Y = aPageSize.Height/2 - aPicSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + + if( !sUrl2.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl2, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl2); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2) + aPageSize.Width/2; + aPicPos.Y = aPageSize.Height/2 - aPicSize.Height/2; + + xShape->setPosition(aPicPos); + + try + { + xSlide->add(xShape); + if(bCreateCaptions) + createCaption( aPageSize ); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + } + } + else if( nOpt == FOUR_IMAGES ) + { + for( sal_Int32 i = 0; i < m_xImagesLst->n_children(); i+=4 ) + { + // create the slide + Reference< drawing::XDrawPage > xSlide = appendNewSlide(aAutoLayout, xDrawPages); + Reference< beans::XPropertySet > xSlideProps( xSlide, uno::UNO_QUERY ); + //Slide dimensions + ::awt::Size aPageSize; + + xSlideProps->getPropertyValue( + "Width") >>= aPageSize.Width; + xSlideProps->getPropertyValue( + "Height") >>= aPageSize.Height; + + // grab the upper left one + OUString sUrl1 = m_xImagesLst->get_id(i); + + // grab the upper right one + OUString sUrl2 = m_xImagesLst->get_id(i+1); + + // grab the lower left one + OUString sUrl3 = m_xImagesLst->get_id(i+2); + + // grab the lower right one + OUString sUrl4 = m_xImagesLst->get_id(i+3); + + if( !sUrl1.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl1, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl1); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2); + aPicPos.Y = aPageSize.Height/4 - aPicSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + if( !sUrl2.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl2, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl2); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2) + aPageSize.Width/2; + aPicPos.Y = aPageSize.Height/4 - aPicSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + if( !sUrl3.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl3, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl3); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2); + aPicPos.Y = aPageSize.Height/4 - aPicSize.Height/2 + aPageSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + if( !sUrl4.isEmpty() ) + { + Reference< graphic::XGraphic > xGraphic = createXGraphicFromUrl(sUrl4, xProvider); + + Graphic aGraphic(xGraphic); + if (bInsertAsLink) + aGraphic.setOriginURL(sUrl4); + // Save the original size, multiplied with 100 + ::awt::Size aPicSize(aGraphic.GetSizePixel().Width()*100, aGraphic.GetSizePixel().Height()*100); + + Reference< drawing::XShape > xShape( + xShapeFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + + Reference< beans::XPropertySet > xProps( xShape, uno::UNO_QUERY ); + xProps->setPropertyValue("Graphic", ::uno::Any(xGraphic)); + + ::awt::Point aPicPos; + + if (m_xASRCheck->get_active()) + { + // Resize the image, with keeping ASR + aPicSize = createASRSize(aPicSize, ::awt::Size(aPageSize.Width/2 - 100, aPageSize.Height/2 - 100)); + } + else + { + aPicSize.Width = aPageSize.Width/2 - 100; + aPicSize.Height = aPageSize.Height/2 - 100; + } + xShape->setSize(aPicSize); + aPicPos.X = (aPageSize.Width/4 - aPicSize.Width/2) + aPageSize.Width/2; + aPicPos.Y = aPageSize.Height/4 - aPicSize.Height/2 + aPageSize.Height/2; + + xShape->setPosition(aPicPos); + try + { + xSlide->add(xShape); + if(bCreateCaptions) + createCaption( aPageSize ); + } + catch (const css::uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + } + } + else + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Info, VclButtonsType::Ok, + "Function is not implemented!")); + xInfoBox->run(); + } + m_xDialog->response(RET_OK); + } +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, FileHdl, weld::Button&, void) +{ + ::sfx2::FileDialogHelper aDlg( + css::ui::dialogs::TemplateDescription::FILEOPEN_PREVIEW, + FileDialogFlags::Graphic | FileDialogFlags::MultiSelection, m_xDialog.get()); + aDlg.SetContext(sfx2::FileDialogHelper::ImpressPhotoDialog); + + if ( aDlg.Execute() == ERRCODE_NONE ) + { + const Sequence< OUString > aFilesArr = aDlg.GetSelectedFiles(); + for ( const auto& rFile : aFilesArr ) + { + // Store full path, show filename only. Use INetURLObject to display spaces in filename correctly + INetURLObject aUrl(rFile); + m_xImagesLst->append(aUrl.GetMainURL(INetURLObject::DecodeMechanism::NONE), aUrl.GetLastName(INetURLObject::DecodeMechanism::WithCharset), ""); + } + } + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, UpHdl, weld::Button&, void) +{ + const int nActPos = m_xImagesLst->get_selected_index(); + if (nActPos != -1 && nActPos != 0) + { + OUString sActEntry(m_xImagesLst->get_text(nActPos)); + // actual data + OUString sAct(m_xImagesLst->get_id(nActPos)); + + OUString sUpperEntry(m_xImagesLst->get_text(nActPos - 1)); + // upper data + OUString sUpper(m_xImagesLst->get_id(nActPos - 1)); + + m_xImagesLst->remove_text(sActEntry); + m_xImagesLst->remove_text(sUpperEntry); + + m_xImagesLst->insert(nActPos - 1, sActEntry, &sAct, nullptr, nullptr); + m_xImagesLst->insert(nActPos, sUpperEntry, &sUpper, nullptr, nullptr); + + m_xImagesLst->select(nActPos - 1); + } + + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, DownHdl, weld::Button&, void) +{ + const int nActPos = m_xImagesLst->get_selected_index(); + if (!m_xImagesLst->get_text(nActPos + 1).isEmpty()) + { + OUString sActEntry(m_xImagesLst->get_selected_text()); + OUString sAct(m_xImagesLst->get_selected_id()); + + OUString sDownEntry(m_xImagesLst->get_text(nActPos + 1)); + OUString sDown(m_xImagesLst->get_id(nActPos + 1)); + + m_xImagesLst->remove_text(sActEntry); + m_xImagesLst->remove_text(sDownEntry); + + m_xImagesLst->insert(nActPos, sDownEntry, &sDown, nullptr, nullptr); + m_xImagesLst->insert(nActPos + 1, sActEntry, &sAct, nullptr, nullptr); + + m_xImagesLst->select(nActPos + 1); + } + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, RemoveHdl, weld::Button&, void) +{ + m_xImagesLst->remove(m_xImagesLst->get_selected_index()); + m_aImg.SetGraphic(Graphic()); + + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, SelectHdl, weld::TreeView&, void) +{ + OUString sImgUrl = m_xImagesLst->get_selected_id(); + + if (sImgUrl != SdResId(STR_PHOTO_ALBUM_TEXTBOX)) + { + Graphic aGraphic; + INetURLObject aURLObj( sImgUrl ); + + sal_uInt16 nFilter = GRFILTER_FORMAT_DONTKNOW; + + if ( aURLObj.HasError() || INetProtocol::NotValid == aURLObj.GetProtocol() ) + { + aURLObj.SetSmartProtocol( INetProtocol::File ); + aURLObj.SetSmartURL( sImgUrl ); + } + + GraphicFilterImportFlags nFilterImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg; + // remote? + if ( INetProtocol::File != aURLObj.GetProtocol() ) + { + std::unique_ptr pStream = ::utl::UcbStreamHelper::CreateStream( sImgUrl, StreamMode::READ ); + + if( pStream ) + m_pGraphicFilter->ImportGraphic( aGraphic, sImgUrl, *pStream, nFilter, nullptr, nFilterImportFlags ); + else + m_pGraphicFilter->ImportGraphic( aGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags ); + } + else + { + m_pGraphicFilter->ImportGraphic( aGraphic, aURLObj, nFilter, nullptr, nFilterImportFlags ); + } + + BitmapEx aBmp = aGraphic.GetBitmapEx(); + sal_Int32 nBmpWidth = aBmp.GetSizePixel().Width(); + sal_Int32 nBmpHeight = aBmp.GetSizePixel().Height(); + + double nXRatio = double(200) / nBmpWidth; + double nYRatio = double(150) / nBmpHeight; + if ( nXRatio < nYRatio ) + aBmp.Scale( nXRatio, nXRatio ); + else + aBmp.Scale( nYRatio, nYRatio ); + + aBmp.Convert( BmpConversion::N24Bit ); + m_aImg.SetGraphic(Graphic(aBmp)); + } + else + { + m_aImg.SetGraphic(Graphic()); + } + EnableDisableButtons(); +} + +IMPL_LINK_NOARG(SdPhotoAlbumDialog, TypeSelectHdl, weld::ComboBox&, void) +{ + // Enable "Fill Slide" only for one image + // If we want to have it for other images too, we need to implement the actual cropping. + bool const bEnable = m_xInsTypeCombo->get_active() == ONE_IMAGE; + m_xASRCheckCrop->set_sensitive(bEnable); + if (!bEnable) + m_xASRCheckCrop->set_active(false); +} + +Reference< drawing::XDrawPage > SdPhotoAlbumDialog::appendNewSlide(AutoLayout aLayout, + const Reference< drawing::XDrawPages >& xDrawPages +) +{ + // Create the slide + Reference< drawing::XDrawPage > xSlide = xDrawPages->insertNewByIndex( xDrawPages->getCount() ); + SdPage* pSlide = m_pDoc->GetSdPage( m_pDoc->GetSdPageCount(PageKind::Standard)-1, PageKind::Standard); + pSlide->SetAutoLayout(aLayout, true); // Set the layout here + return xSlide; +} + +awt::Size SdPhotoAlbumDialog::createASRSize(const awt::Size& aPicSize, const awt::Size& aMaxSize) +{ + double resizeWidth = aPicSize.Width; + double resizeHeight = aPicSize.Height; + double aspect = resizeWidth/resizeHeight; + + if( resizeWidth > aMaxSize.Width ) + { + resizeWidth = aMaxSize.Width; + resizeHeight = resizeWidth / aspect; + } + + if( resizeHeight > aMaxSize.Height ) + { + aspect = resizeWidth/resizeHeight; + resizeHeight = aMaxSize.Height; + resizeWidth = resizeHeight * aspect; + } + return awt::Size(resizeWidth, resizeHeight); +} + +awt::Size SdPhotoAlbumDialog::createASRSizeCrop(const awt::Size& aPicSize, const awt::Size& aMaxSize) +{ + double resizeWidth = aPicSize.Width; + double resizeHeight = aPicSize.Height; + double imgAspect = resizeWidth / resizeHeight; + double windowAspectRatio = static_cast(aMaxSize.Width) / aMaxSize.Height ; + + + //When both sides of an image are bigger than canvas size, image would be downscaled. + if( resizeWidth > aMaxSize.Width && resizeHeight > aMaxSize.Height ) + { + if( imgAspect > windowAspectRatio ) + { + resizeHeight = aMaxSize.Height; + resizeWidth = aMaxSize.Height * imgAspect; + } + else + { + resizeHeight = aMaxSize.Width / imgAspect; + resizeWidth = aMaxSize.Width; + } + + } + //In all other cases image is upscaled + else + { + if( imgAspect > windowAspectRatio ) + { + resizeHeight = aMaxSize.Height; + resizeWidth = aMaxSize.Height * imgAspect; + } + else + { + resizeWidth = aMaxSize.Width; + resizeHeight = aMaxSize.Width / imgAspect; + } + } + return awt::Size(resizeWidth, resizeHeight); +} + +void SdPhotoAlbumDialog::createCaption(const awt::Size& aPageSize ) +{ + Point CapPos; + Size CapSize; + + CapSize.setWidth( aPageSize.Width ); + CapSize.setHeight( aPageSize.Height/6 ); + CapPos.setX( 0 ); + CapPos.setY( aPageSize.Height - CapSize.Height() ); + SdPage* pSlide = m_pDoc->GetSdPage( m_pDoc->GetSdPageCount(PageKind::Standard)-1, PageKind::Standard ); + + // try to get existing PresObj + const ::tools::Rectangle rRect(CapPos,CapSize); + SdrObject* pSdrObj = pSlide->GetPresObj(PresObjKind::Title); + + if(!pSdrObj) + { + // if not exists, create. Beware: It is already inserted to the SdPage + pSdrObj = pSlide->CreatePresObj(PresObjKind::Title,false,rRect); + } + else + { + // if exists, bring to front and position it + const size_t nObjNum(pSlide->GetObjCount()); + + if(nObjNum) + { + pSlide->SetObjectOrdNum(pSdrObj->GetOrdNum(), nObjNum - 1); + } + + pSdrObj->SetSnapRect(rRect); + } + + if(pSdrObj) + { + // set color, style and some transparency + SfxItemSet aSet(m_pDoc->GetItemPool() ); + + aSet.Put( XFillStyleItem(drawing::FillStyle_SOLID) ); + aSet.Put( XFillColorItem( "", COL_BLACK ) ); + aSet.Put( XFillTransparenceItem( 20 ) ); + pSdrObj->SetMergedItemSetAndBroadcast(aSet); + } +} + +Reference< graphic::XGraphic> SdPhotoAlbumDialog::createXGraphicFromUrl(const OUString& sUrl, + const Reference< graphic::XGraphicProvider>& xProvider +) +{ + // The same as above, except this returns an XGraphic from the image URL + ::comphelper::NamedValueCollection aMediaProperties; + aMediaProperties.put( "URL", sUrl ); + Reference< graphic::XGraphic> xGraphic = + xProvider->queryGraphic( aMediaProperties.getPropertyValues() ); + return xGraphic; +} + +void SdPhotoAlbumDialog::EnableDisableButtons() +{ + m_xRemoveBtn->set_sensitive(m_xImagesLst->count_selected_rows() > 0); + m_xUpBtn->set_sensitive(m_xImagesLst->count_selected_rows() > 0 && + m_xImagesLst->get_selected_index() != 0); + m_xDownBtn->set_sensitive(m_xImagesLst->count_selected_rows() > 0 && + m_xImagesLst->get_selected_index() < m_xImagesLst->n_children() - 1); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/PhotoAlbumDialog.hxx b/sd/source/ui/dlg/PhotoAlbumDialog.hxx new file mode 100644 index 000000000..f84ff5cc3 --- /dev/null +++ b/sd/source/ui/dlg/PhotoAlbumDialog.hxx @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* +* This file is part of the LibreOffice project. +* +* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this +* file, You can obtain one at http://mozilla.org/MPL/2.0/. +*/ + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XDrawPages; } +namespace com::sun::star::graphic { class XGraphicProvider; } + +class SdDrawDocument; +class GraphicFilter; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd +{ + +class SdPhotoAlbumDialog : public weld::GenericDialogController +{ +public: + SdPhotoAlbumDialog(weld::Window* pWindow, SdDrawDocument* pActDoc); + virtual ~SdPhotoAlbumDialog() override; + +private: + SdDrawDocument* m_pDoc; + GraphicFilter* m_pGraphicFilter; + + GraphCtrl m_aImg; + + std::unique_ptr m_xCancelBtn; + std::unique_ptr m_xCreateBtn; + std::unique_ptr m_xAddBtn; + std::unique_ptr m_xUpBtn; + std::unique_ptr m_xDownBtn; + std::unique_ptr m_xRemoveBtn; + std::unique_ptr m_xImagesLst; + std::unique_ptr m_xImg; + std::unique_ptr m_xInsTypeCombo; + std::unique_ptr m_xASRCheck; + std::unique_ptr m_xASRCheckCrop; + std::unique_ptr m_xCapCheck; + std::unique_ptr m_xInsertAsLinkCheck; + + DECL_LINK(CancelHdl, weld::Button&, void); + DECL_LINK(CreateHdl, weld::Button&, void); + + DECL_LINK(FileHdl, weld::Button&, void); + DECL_LINK(UpHdl, weld::Button&, void); + DECL_LINK(DownHdl, weld::Button&, void); + DECL_LINK(RemoveHdl, weld::Button&, void); + + DECL_LINK(SelectHdl, weld::TreeView&, void); + DECL_LINK(TypeSelectHdl, weld::ComboBox&, void); + + Reference< drawing::XDrawPage > appendNewSlide(AutoLayout aLayout, + const Reference< drawing::XDrawPages >& xDrawPages); + + static awt::Size createASRSize(const awt::Size& aPicSize, const awt::Size& aMaxSize); + static awt::Size createASRSizeCrop(const awt::Size& aPicSize, const awt::Size& aMaxSize); + void createCaption(const awt::Size& aPageSize); + static Reference< graphic::XGraphic> createXGraphicFromUrl(const OUString& sUrl, + const Reference< graphic::XGraphicProvider>& xProvider); + + void EnableDisableButtons(); + + enum SlideImageLayout + { + ONE_IMAGE=0, + TWO_IMAGES, + FOUR_IMAGES + }; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/RemoteDialog.cxx b/sd/source/ui/dlg/RemoteDialog.cxx new file mode 100644 index 000000000..e28f57ecd --- /dev/null +++ b/sd/source/ui/dlg/RemoteDialog.cxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "RemoteDialog.hxx" +#include + +using namespace ::sd; +using namespace ::std; + +RemoteDialog::RemoteDialog(weld::Window* pWindow) + : GenericDialogController(pWindow, "modules/simpress/ui/remotedialog.ui", "RemoteDialog") + , m_xButtonConnect(m_xBuilder->weld_button("ok")) + , m_xClientBox(new sd::ClientBox(m_xBuilder->weld_scrolled_window("scroll"), + m_xBuilder->weld_container("tree"))) +{ + m_xButtonConnect->connect_clicked(LINK(this, RemoteDialog, HandleConnectButton)); +} + +RemoteDialog::~RemoteDialog() {} + +IMPL_LINK_NOARG(RemoteDialog, HandleConnectButton, weld::Button&, void) +{ + weld::WaitObject(m_xDialog.get()); +#if defined(ENABLE_SDREMOTE) + auto xEntry = m_xClientBox->GetActiveEntry(); + if (!xEntry) + return; + OUString aPin = xEntry->m_xPinBox->get_text(); + if (RemoteServer::connectClient(xEntry->m_xClientInfo, aPin)) + m_xDialog->response(RET_OK); +#endif +} + +short RemoteDialog::run() +{ + short nRet = weld::GenericDialogController::run(); +#ifdef ENABLE_SDREMOTE + RemoteServer::restoreDiscoverable(); +#endif + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/RemoteDialog.hxx b/sd/source/ui/dlg/RemoteDialog.hxx new file mode 100644 index 000000000..6c3f94ff8 --- /dev/null +++ b/sd/source/ui/dlg/RemoteDialog.hxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include + +#include "RemoteDialogClientBox.hxx" + +namespace sd +{ +class RemoteDialog : public weld::GenericDialogController +{ +private: + std::unique_ptr m_xButtonConnect; + std::unique_ptr m_xClientBox; + + DECL_LINK(HandleConnectButton, weld::Button&, void); + +public: + explicit RemoteDialog(weld::Window* pWindow); + virtual short run() override; + virtual ~RemoteDialog() override; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/RemoteDialogClientBox.cxx b/sd/source/ui/dlg/RemoteDialogClientBox.cxx new file mode 100644 index 000000000..2371c4fb8 --- /dev/null +++ b/sd/source/ui/dlg/RemoteDialogClientBox.cxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include "RemoteDialogClientBox.hxx" +#include + +#include + +namespace sd { + +// struct ClientBoxEntry + +ClientBoxEntry::ClientBoxEntry(ClientBox* pClientBox, + const std::shared_ptr& pClientInfo) + : m_xBuilder(Application::CreateBuilder(pClientBox->GetContainer(), "modules/simpress/ui/clientboxfragment.ui")) + , m_xContainer(m_xBuilder->weld_container("ClientboxFragment")) + , m_xDeviceName(m_xBuilder->weld_label("name")) + , m_xPinLabel(m_xBuilder->weld_label("pinlabel")) + , m_xPinBox(m_xBuilder->weld_entry("pin")) + , m_xDeauthoriseButton(m_xBuilder->weld_button("button")) + , m_xClientInfo(pClientInfo) + , m_pClientBox(pClientBox) +{ + m_xDeviceName->set_label(m_xClientInfo->mName); + m_xDeauthoriseButton->connect_clicked(LINK(this, ClientBoxEntry, DeauthoriseHdl)); + m_xDeauthoriseButton->set_visible(m_xClientInfo->mbIsAlreadyAuthorised); + m_xPinBox->set_visible(!m_xClientInfo->mbIsAlreadyAuthorised); + m_xPinLabel->set_visible(!m_xClientInfo->mbIsAlreadyAuthorised); + + m_xDeauthoriseButton->connect_focus_in(LINK(this, ClientBoxEntry, FocusHdl)); + m_xPinBox->connect_focus_in(LINK(this, ClientBoxEntry, FocusHdl)); +} + +ClientBoxEntry::~ClientBoxEntry() +{ + m_pClientBox->GetContainer()->move(m_xContainer.get(), nullptr); +} + +// ClientBox +ClientBox::ClientBox(std::unique_ptr xScroll, + std::unique_ptr xContents) + : m_xScroll(std::move(xScroll)) + , m_xContents(std::move(xContents)) + , m_pActive(nullptr) +{ + Size aSize(m_xScroll->get_approximate_digit_width() * 40, + m_xScroll->get_text_height() * 16); + m_xScroll->set_size_request(aSize.Width(), aSize.Height()); + + m_xContents->set_stack_background(); + + populateEntries(); +} + +ClientBox::~ClientBox() +{ +} + +ClientBoxEntry* ClientBox::GetActiveEntry() +{ + return m_pActive; +} + +void ClientBox::addEntry( const std::shared_ptr& pClientInfo ) +{ + TClientBoxEntry xEntry = std::make_shared(this, pClientInfo); + m_vEntries.push_back(xEntry); +} + +void ClientBox::setActive(ClientBoxEntry* pClientEntry) +{ + m_pActive = pClientEntry; +} + +void ClientBox::clearEntries() +{ + m_vEntries.clear(); + m_pActive = nullptr; +} + +void ClientBox::populateEntries() +{ + clearEntries(); + +#ifdef ENABLE_SDREMOTE + RemoteServer::ensureDiscoverable(); + + std::vector< std::shared_ptr< ClientInfo > > aClients( RemoteServer::getClients() ); + + for ( const auto& rxClient : aClients ) + { + addEntry( rxClient ); + } +#endif + +} + +IMPL_LINK_NOARG(ClientBoxEntry, DeauthoriseHdl, weld::Button&, void) +{ +#ifdef ENABLE_SDREMOTE + RemoteServer::deauthoriseClient(m_xClientInfo); +#endif + m_pClientBox->populateEntries(); +} + +IMPL_LINK_NOARG(ClientBoxEntry, FocusHdl, weld::Widget&, void) +{ + if (ClientBoxEntry* pOldEntry = m_pClientBox->GetActiveEntry()) + pOldEntry->m_xContainer->set_stack_background(); + m_pClientBox->setActive(this); + m_xContainer->set_highlight_background(); +} + +} //namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/RemoteDialogClientBox.hxx b/sd/source/ui/dlg/RemoteDialogClientBox.hxx new file mode 100644 index 000000000..c7a916dc9 --- /dev/null +++ b/sd/source/ui/dlg/RemoteDialogClientBox.hxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +namespace sd { + +#define SMALL_ICON_SIZE 16 +#define TOP_OFFSET 5 +#define ICON_HEIGHT 42 +#define ICON_WIDTH 47 +#define ICON_OFFSET 72 +#define RIGHT_ICON_OFFSET 5 +#define SPACE_BETWEEN 3 + +class ClientBox; +struct ClientBoxEntry; +struct ClientInfo; + +typedef std::shared_ptr TClientBoxEntry; + +struct ClientBoxEntry +{ + std::unique_ptr m_xBuilder; + std::unique_ptr m_xContainer; + std::unique_ptr m_xDeviceName; + std::unique_ptr m_xPinLabel; + std::unique_ptr m_xPinBox; + std::unique_ptr m_xDeauthoriseButton; + + std::shared_ptr m_xClientInfo; + ClientBox* m_pClientBox; + + DECL_LINK(DeauthoriseHdl, weld::Button&, void); + DECL_LINK(FocusHdl, weld::Widget&, void); + + ClientBoxEntry(ClientBox* pClientBox, const std::shared_ptr& pClientInfo); + ~ClientBoxEntry(); +}; + +class ClientBox +{ + std::unique_ptr m_xScroll; + std::unique_ptr m_xContents; + + std::vector< TClientBoxEntry > m_vEntries; + ClientBoxEntry* m_pActive; + +public: + ClientBox(std::unique_ptr xScroll, std::unique_ptr xContents); + weld::Container* GetContainer() { return m_xContents.get(); } + ~ClientBox(); + + ClientBoxEntry* GetActiveEntry(); + + void addEntry(const std::shared_ptr& pClientInfo); + void setActive(ClientBoxEntry* pClientData); + void clearEntries(); + + void populateEntries(); +}; + +} // end namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/SpellDialogChildWindow.cxx b/sd/source/ui/dlg/SpellDialogChildWindow.cxx new file mode 100644 index 000000000..c87919346 --- /dev/null +++ b/sd/source/ui/dlg/SpellDialogChildWindow.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace sd { + +SFX_IMPL_CHILDWINDOW_WITHID(SpellDialogChildWindow, SID_SPELL_DIALOG) + +SpellDialogChildWindow::SpellDialogChildWindow ( + vcl::Window* _pParent, + sal_uInt16 nId, + SfxBindings* pBindings, + SAL_UNUSED_PARAMETER SfxChildWinInfo* /*pInfo*/) + : svx::SpellDialogChildWindow (_pParent, nId, pBindings), + mpSdOutliner (nullptr), + mbOwnOutliner (false) +{ + ProvideOutliner(); +} + +SpellDialogChildWindow::~SpellDialogChildWindow() +{ + EndSpellingAndClearOutliner(); +} + +SfxChildWinInfo SpellDialogChildWindow::GetInfo() const +{ + return svx::SpellDialogChildWindow::GetInfo(); +} + +void SpellDialogChildWindow::InvalidateSpellDialog() +{ + svx::SpellDialogChildWindow::InvalidateSpellDialog(); +} + +svx::SpellPortions SpellDialogChildWindow::GetNextWrongSentence( bool /*bRecheck*/ ) +{ + svx::SpellPortions aResult; + + if (mpSdOutliner != nullptr) + { + ProvideOutliner(); + aResult = mpSdOutliner->GetNextSpellSentence(); + } + return aResult; +} + +void SpellDialogChildWindow::ApplyChangedSentence ( + const svx::SpellPortions& rChanged, bool bRecheck ) +{ + if (mpSdOutliner != nullptr) + { + OutlinerView* pOutlinerView = mpSdOutliner->GetView(0); + if (pOutlinerView != nullptr) + mpSdOutliner->ApplyChangedSentence ( + pOutlinerView->GetEditView(), + rChanged, bRecheck); + } +} + +void SpellDialogChildWindow::GetFocus() +{ + // In order to detect a cursor movement we could compare the + // currently selected text shape with the one that was selected + // when LoseFocus() was called the last time. + // For the time being we instead rely on the DetectChange() method + // in the SdOutliner class. +} + +void SpellDialogChildWindow::LoseFocus() +{ +} + +void SpellDialogChildWindow::EndSpellingAndClearOutliner() +{ + if (!mpSdOutliner) + return; + EndListening(*mpSdOutliner->GetDoc()); + mpSdOutliner->EndSpelling(); + if (mbOwnOutliner) + delete mpSdOutliner; + mpSdOutliner = nullptr; + mbOwnOutliner = false; +} + +void SpellDialogChildWindow::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast(&rHint); + if (SdrHintKind::ModelCleared == pSdrHint->GetKind()) + { + EndSpellingAndClearOutliner(); + } +} + +void SpellDialogChildWindow::ProvideOutliner() +{ + ViewShellBase* pViewShellBase = dynamic_cast( SfxViewShell::Current() ); + + if (pViewShellBase == nullptr) + return; + + ViewShell* pViewShell = pViewShellBase->GetMainViewShell().get(); + // If there already exists an outliner that has been created + // for another view shell then destroy it first. + if (mpSdOutliner != nullptr) + if(( dynamic_cast< const DrawViewShell *>( pViewShell ) != nullptr && ! mbOwnOutliner) + || (dynamic_cast< const OutlineViewShell *>( pViewShell ) != nullptr && mbOwnOutliner)) + { + EndSpellingAndClearOutliner(); + } + + // Now create/get an outliner if none is present. + if (mpSdOutliner != nullptr) + return; + + if( dynamic_cast< const DrawViewShell *>( pViewShell ) != nullptr) + { + // We need an outliner for the spell check so we have + // to create one. + mbOwnOutliner = true; + SdDrawDocument *pDoc = pViewShell->GetDoc(); + mpSdOutliner = new SdOutliner(pDoc, OutlinerMode::TextObject); + StartListening(*pDoc); + } + else if( dynamic_cast< const OutlineViewShell *>( pViewShell ) != nullptr) + { + // An outline view is already visible. The SdOutliner + // will use it instead of creating its own. + mbOwnOutliner = false; + SdDrawDocument *pDoc = pViewShell->GetDoc(); + mpSdOutliner = pDoc->GetOutliner(); + StartListening(*pDoc); + } + + // Initialize spelling. + if (mpSdOutliner != nullptr) + { + mpSdOutliner->PrepareSpelling(); + mpSdOutliner->StartSpelling(); + } +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/TemplateScanner.cxx b/sd/source/ui/dlg/TemplateScanner.cxx new file mode 100644 index 000000000..afd23ff85 --- /dev/null +++ b/sd/source/ui/dlg/TemplateScanner.cxx @@ -0,0 +1,342 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +constexpr OUStringLiteral TITLE = u"Title"; + +class FolderDescriptor +{ +public: + FolderDescriptor ( + int nPriority, + const OUString& rsContentIdentifier, + const Reference& rxFolderEnvironment) + : mnPriority(nPriority), + msContentIdentifier(rsContentIdentifier), + mxFolderEnvironment(rxFolderEnvironment) + { } + int mnPriority; + OUString msContentIdentifier; + // Reference mxFolderResultSet; + Reference mxFolderEnvironment; + + class Comparator + { + public: + bool operator() (const FolderDescriptor& r1, const FolderDescriptor& r2) const + { return r1.mnPriority < r2.mnPriority; } + }; +}; + +/** Use a heuristic based on the URL of a top-level template folder to + assign a priority that is used to sort the folders. +*/ +int Classify (std::u16string_view rsURL) +{ + int nPriority (0); + + if (rsURL.empty()) + nPriority = 100; + else if (rsURL.find(u"presnt") != std::u16string_view::npos) + { + nPriority = 30; + } + else if (rsURL.find(u"layout") != std::u16string_view::npos) + { + nPriority = 20; + } + else if (rsURL.find(u"educate") != std::u16string_view::npos) + { + nPriority = 40; + } + else if (rsURL.find(u"finance") != std::u16string_view::npos) + { + nPriority = 40; + } + else + { + // All other folders are taken for user supplied and have the + // highest priority. + nPriority = 10; + } + + return nPriority; +} + +} // end of anonymous namespace + +namespace sd +{ + +class TemplateScanner::FolderDescriptorList + : public ::std::multiset +{ +}; + +TemplateScanner::TemplateScanner() + : meState(INITIALIZE_SCANNING), + mpFolderDescriptors(new FolderDescriptorList) +{ + // empty; +} + +TemplateScanner::~TemplateScanner() +{ +} + +TemplateScanner::State TemplateScanner::GetTemplateRoot() +{ + Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext(); + Reference xTemplates = frame::DocumentTemplates::create(xContext); + mxTemplateRoot = xTemplates->getContent(); + + return INITIALIZE_FOLDER_SCANNING; +} + +TemplateScanner::State TemplateScanner::InitializeEntryScanning() +{ + State eNextState (SCAN_ENTRY); + + if (maFolderContent.isFolder()) + { + mxEntryEnvironment.clear(); + + // Create a cursor to iterate over the templates in this folders. + // We are interested only in three properties: the entry's name, + // its URL, and its content type. + mxEntryResultSet.set( maFolderContent.createCursor({ TITLE, "TargetURL", "TypeDescription" }, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY)); + } + else + eNextState = ERROR; + + return eNextState; +} + +TemplateScanner::State TemplateScanner::ScanEntry() +{ + State eNextState (ERROR); + + Reference xContentAccess (mxEntryResultSet, UNO_QUERY); + Reference xRow (mxEntryResultSet, UNO_QUERY); + + if (xContentAccess.is() && xRow.is() && mxEntryResultSet.is()) + { + if (mxEntryResultSet->next()) + { + OUString sTitle (xRow->getString (1)); + OUString sTargetURL (xRow->getString (2)); + OUString sContentType (xRow->getString (3)); + + OUString aId = xContentAccess->queryContentIdentifierString(); + ::ucbhelper::Content aContent(aId, mxEntryEnvironment, comphelper::getProcessComponentContext()); + if (aContent.isDocument ()) + { + // Check whether the entry is an impress template. If so + // add a new entry to the resulting list (which is created + // first if necessary). + // These strings are used to find impress templates in the tree of + // template files. Should probably be determined dynamically. + if ( (sContentType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_TEMPLATE_ASCII) + || (sContentType == MIMETYPE_OASIS_OPENDOCUMENT_PRESENTATION_ASCII) + || (sContentType == "application/vnd.stardivision.impress") + || (sContentType == MIMETYPE_VND_SUN_XML_IMPRESS_ASCII) + // The following id comes from the bugdoc in #i2764#. + || (sContentType == "Impress 2.0")) + { + OUString sLocalisedTitle = SfxDocumentTemplates::ConvertResourceString(sTitle); + mpTemplateEntries.push_back(std::make_unique(sLocalisedTitle, sTargetURL)); + } + } + + // Continue scanning entries. + eNextState = SCAN_ENTRY; + } + else + { + // Continue with scanning the next folder. + eNextState = SCAN_FOLDER; + } + } + + return eNextState; +} + +TemplateScanner::State TemplateScanner::InitializeFolderScanning() +{ + State eNextState (ERROR); + + mxFolderResultSet.clear(); + + try + { + // Create content for template folders. + mxFolderEnvironment.clear(); + ::ucbhelper::Content aTemplateDir (mxTemplateRoot, mxFolderEnvironment, comphelper::getProcessComponentContext()); + + // Create a cursor to iterate over the template folders. + mxFolderResultSet.set( aTemplateDir.createCursor({ TITLE, "TargetDirURL" }, ::ucbhelper::INCLUDE_FOLDERS_ONLY)); + if (mxFolderResultSet.is()) + eNextState = GATHER_FOLDER_LIST; + } + catch (css::uno::Exception&) + { + eNextState = ERROR; + } + + return eNextState; +} + +TemplateScanner::State TemplateScanner::GatherFolderList() +{ + State eNextState (ERROR); + + Reference xContentAccess (mxFolderResultSet, UNO_QUERY); + if (xContentAccess.is() && mxFolderResultSet.is()) + { + while (mxFolderResultSet->next()) + { + Reference xRow (mxFolderResultSet, UNO_QUERY); + if (xRow.is()) + { + OUString sTargetDir (xRow->getString (2)); + OUString aId = xContentAccess->queryContentIdentifierString(); + + mpFolderDescriptors->insert( + FolderDescriptor( + Classify(sTargetDir), + aId, + mxFolderEnvironment)); + } + } + + eNextState = SCAN_FOLDER; + } + + return eNextState; +} + +TemplateScanner::State TemplateScanner::ScanFolder() +{ + State eNextState (ERROR); + + if (!mpFolderDescriptors->empty()) + { + FolderDescriptor aDescriptor (*mpFolderDescriptors->begin()); + mpFolderDescriptors->erase(mpFolderDescriptors->begin()); + + OUString aId (aDescriptor.msContentIdentifier); + + maFolderContent = ::ucbhelper::Content (aId, aDescriptor.mxFolderEnvironment, comphelper::getProcessComponentContext()); + if (maFolderContent.isFolder()) + { + // Scan the folder and insert it into the list of template + // folders. + // Continue with scanning all entries in the folder. + mpTemplateEntries.clear(); + eNextState = INITIALIZE_ENTRY_SCAN; + } + } + else + { + eNextState = DONE; + } + + return eNextState; +} + +void TemplateScanner::RunNextStep() +{ + switch (meState) + { + case INITIALIZE_SCANNING: + meState = GetTemplateRoot(); + break; + + case INITIALIZE_FOLDER_SCANNING: + meState = InitializeFolderScanning(); + break; + + case SCAN_FOLDER: + meState = ScanFolder(); + break; + + case GATHER_FOLDER_LIST: + meState = GatherFolderList(); + break; + + case INITIALIZE_ENTRY_SCAN: + meState = InitializeEntryScanning(); + break; + + case SCAN_ENTRY: + meState = ScanEntry(); + break; + default: + break; + } + + switch (meState) + { + case DONE: + case ERROR: + mxTemplateRoot.clear(); + mxFolderEnvironment.clear(); + mxEntryEnvironment.clear(); + mxFolderResultSet.clear(); + mxEntryResultSet.clear(); + break; + default: + break; + } +} + +bool TemplateScanner::HasNextStep() +{ + switch (meState) + { + case DONE: + case ERROR: + return false; + + default: + return true; + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/animobjs.cxx b/sd/source/ui/dlg/animobjs.cxx new file mode 100644 index 000000000..b70848e23 --- /dev/null +++ b/sd/source/ui/dlg/animobjs.cxx @@ -0,0 +1,1134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +using namespace ::com::sun::star; + +namespace sd { + +/** + * SdDisplay - Control + */ +SdDisplay::SdDisplay() + : aScale(1, 1) +{ +} + +SdDisplay::~SdDisplay() +{ +} + +void SdDisplay::SetBitmapEx( BitmapEx const * pBmpEx ) +{ + if( pBmpEx ) + { + aBitmapEx = *pBmpEx; + } + else + { + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + const Color aFillColor = rStyles.GetFieldColor(); + aBitmapEx.Erase(aFillColor); + } +} + +void SdDisplay::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) +{ + rRenderContext.Push(vcl::PushFlags::MAPMODE); + + rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel)); + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + rRenderContext.SetBackground( Wallpaper( rStyles.GetFieldColor() ) ); + rRenderContext.Erase(); + + Point aPt; + Size aSize = GetOutputSizePixel(); + + Size aBmpSize = aBitmapEx.GetBitmap().GetSizePixel(); + aBmpSize.setWidth( static_cast<::tools::Long>( static_cast(aBmpSize.Width()) * static_cast(aScale) ) ); + aBmpSize.setHeight( static_cast<::tools::Long>( static_cast(aBmpSize.Height()) * static_cast(aScale) ) ); + + if( aBmpSize.Width() < aSize.Width() ) + aPt.setX( ( aSize.Width() - aBmpSize.Width() ) / 2 ); + if( aBmpSize.Height() < aSize.Height() ) + aPt.setY( ( aSize.Height() - aBmpSize.Height() ) / 2 ); + + aBitmapEx.Draw(&rRenderContext, aPt, aBmpSize); + + rRenderContext.Pop(); +} + +void SdDisplay::SetScale( const Fraction& rFrac ) +{ + aScale = rFrac; +} + +void SdDisplay::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + CustomWidgetController::SetDrawingArea(pDrawingArea); + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(147, 87), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + SetOutputSizePixel(aSize); +} + +const size_t AnimationWindow::EMPTY_FRAMELIST = std::numeric_limits::max(); + +/** + * AnimationWindow - FloatingWindow + */ +AnimationWindow::AnimationWindow(SfxBindings* pInBindings, SfxChildWindow *pCW, vcl::Window* pParent) + : SfxDockingWindow(pInBindings, pCW, pParent, + "DockingAnimation", "modules/simpress/ui/dockinganimation.ui") + , m_xCtlDisplay(new SdDisplay) + , m_xCtlDisplayWin(new weld::CustomWeld(*m_xBuilder, "preview", *m_xCtlDisplay)) + , m_xBtnFirst(m_xBuilder->weld_button("first")) + , m_xBtnReverse(m_xBuilder->weld_button("prev")) + , m_xBtnStop(m_xBuilder->weld_button("stop")) + , m_xBtnPlay(m_xBuilder->weld_button("next")) + , m_xBtnLast(m_xBuilder->weld_button("last")) + , m_xNumFldBitmap(m_xBuilder->weld_spin_button("numbitmap")) + , m_xTimeField(m_xBuilder->weld_formatted_spin_button("duration")) + , m_xFormatter(new weld::TimeFormatter(*m_xTimeField)) + , m_xLbLoopCount(m_xBuilder->weld_combo_box("loopcount")) + , m_xBtnGetOneObject(m_xBuilder->weld_button("getone")) + , m_xBtnGetAllObjects(m_xBuilder->weld_button("getall")) + , m_xBtnRemoveBitmap(m_xBuilder->weld_button("delone")) + , m_xBtnRemoveAll(m_xBuilder->weld_button("delall")) + , m_xFiCount(m_xBuilder->weld_label("count")) + , m_xRbtGroup(m_xBuilder->weld_radio_button("group")) + , m_xRbtBitmap(m_xBuilder->weld_radio_button("bitmap")) + , m_xFtAdjustment(m_xBuilder->weld_label("alignmentft")) + , m_xLbAdjustment(m_xBuilder->weld_combo_box("alignment")) + , m_xBtnCreateGroup(m_xBuilder->weld_button("create")) + , m_xBtnHelp(m_xBuilder->weld_button("help")) + , m_nCurrentFrame(EMPTY_FRAMELIST) + , bMovie(false) + , bAllObjects(false) +{ + SetText(SdResId(STR_ANIMATION_DIALOG_TITLE)); + + m_xFormatter->SetDuration(true); + m_xFormatter->SetTimeFormat(TimeFieldFormat::F_SEC_CS); + m_xFormatter->EnableEmptyField(false); + + // create new document with page + pMyDoc.reset( new SdDrawDocument(DocumentType::Impress, nullptr) ); + rtl::Reference pPage = pMyDoc->AllocSdPage(false); + pMyDoc->InsertPage(pPage.get()); + + pControllerItem.reset( new AnimationControllerItem( SID_ANIMATOR_STATE, this, pInBindings ) ); + + m_xBtnFirst->connect_clicked( LINK( this, AnimationWindow, ClickFirstHdl ) ); + m_xBtnReverse->connect_clicked( LINK( this, AnimationWindow, ClickPlayHdl ) ); + m_xBtnStop->connect_clicked( LINK( this, AnimationWindow, ClickStopHdl ) ); + m_xBtnPlay->connect_clicked( LINK( this, AnimationWindow, ClickPlayHdl ) ); + m_xBtnLast->connect_clicked( LINK( this, AnimationWindow, ClickLastHdl ) ); + + m_xBtnGetOneObject->connect_clicked( LINK( this, AnimationWindow, ClickGetObjectHdl ) ); + m_xBtnGetAllObjects->connect_clicked( LINK( this, AnimationWindow, ClickGetObjectHdl ) ); + m_xBtnRemoveBitmap->connect_clicked( LINK( this, AnimationWindow, ClickRemoveBitmapHdl ) ); + m_xBtnRemoveAll->connect_clicked( LINK( this, AnimationWindow, ClickRemoveBitmapHdl ) ); + + m_xRbtGroup->connect_toggled( LINK( this, AnimationWindow, ClickRbtHdl ) ); + m_xRbtBitmap->connect_toggled( LINK( this, AnimationWindow, ClickRbtHdl ) ); + m_xBtnCreateGroup->connect_clicked( LINK( this, AnimationWindow, ClickCreateGroupHdl ) ); + m_xBtnHelp->connect_clicked( LINK( this, AnimationWindow, ClickHelpHdl ) ); + m_xNumFldBitmap->connect_value_changed( LINK( this, AnimationWindow, ModifyBitmapHdl ) ); + m_xTimeField->connect_value_changed( LINK( this, AnimationWindow, ModifyTimeHdl ) ); + + SetMinOutputSizePixel(GetOptimalSize()); + + ResetAttrs(); + + // the animator is empty; no animation group can be created + m_xBtnCreateGroup->set_sensitive(false); +} + +AnimationWindow::~AnimationWindow() +{ + disposeOnce(); +} + +void AnimationWindow::dispose() +{ + pControllerItem.reset(); + + m_FrameList.clear(); + m_nCurrentFrame = EMPTY_FRAMELIST; + + // delete the clones + pMyDoc.reset(); + + m_xCtlDisplayWin.reset(); + m_xCtlDisplay.reset(); + m_xBtnFirst.reset(); + m_xBtnReverse.reset(); + m_xBtnStop.reset(); + m_xBtnPlay.reset(); + m_xBtnLast.reset(); + m_xNumFldBitmap.reset(); + m_xFormatter.reset(); + m_xTimeField.reset(); + m_xLbLoopCount.reset(); + m_xBtnGetOneObject.reset(); + m_xBtnGetAllObjects.reset(); + m_xBtnRemoveBitmap.reset(); + m_xBtnRemoveAll.reset(); + m_xFiCount.reset(); + m_xRbtGroup.reset(); + m_xRbtBitmap.reset(); + m_xFtAdjustment.reset(); + m_xLbAdjustment.reset(); + m_xBtnCreateGroup.reset(); + m_xBtnHelp.reset(); + SfxDockingWindow::dispose(); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickFirstHdl, weld::Button&, void) +{ + m_nCurrentFrame = (m_FrameList.empty()) ? EMPTY_FRAMELIST : 0; + UpdateControl(); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickStopHdl, weld::Button&, void) +{ + bMovie = false; +} + +IMPL_LINK( AnimationWindow, ClickPlayHdl, weld::Button&, rButton, void ) +{ + ScopeLockGuard aGuard( maPlayLock ); + + bMovie = true; + bool bDisableCtrls = false; + size_t const nCount = m_FrameList.size(); + bool bReverse = &rButton == m_xBtnReverse.get(); + + // it is difficult to find it later on + bool bRbtGroupEnabled = m_xRbtGroup->get_sensitive(); + bool bBtnGetAllObjectsEnabled = m_xBtnGetAllObjects->get_sensitive(); + bool bBtnGetOneObjectEnabled = m_xBtnGetOneObject->get_sensitive(); + + // calculate overall time + ::tools::Time aTime( 0 ); + ::tools::Long nFullTime; + if( m_xRbtBitmap->get_active() ) + { + for (size_t i = 0; i < nCount; ++i) + { + aTime += m_FrameList[i].second; + } + nFullTime = aTime.GetMSFromTime(); + } + else + { + nFullTime = nCount * 100; + aTime.MakeTimeFromMS( nFullTime ); + } + + // StatusBarManager from 1 second + std::unique_ptr pProgress; + if( nFullTime >= 1000 ) + { + bDisableCtrls = true; + m_xBtnStop->set_sensitive(true); + pProgress.reset(new SfxProgress( nullptr, "Animator:", nFullTime )); // "Animator:" here we should think about something smart + } + + sal_uLong nTmpTime = 0; + size_t i = 0; + bool bCount = i < nCount; + if( bReverse ) + { + i = nCount - 1; + } + while( bCount && bMovie ) + { + // make list and view consistent + assert(i < m_FrameList.size()); + m_nCurrentFrame = i; + + UpdateControl(bDisableCtrls); + + if( m_xRbtBitmap->get_active() ) + { + ::tools::Time const & rTime = m_FrameList[i].second; + + m_xFormatter->SetTime( rTime ); + sal_uLong nTime = rTime.GetMSFromTime(); + + WaitInEffect( nTime, nTmpTime, pProgress.get() ); + nTmpTime += nTime; + } + else + { + WaitInEffect( 100, nTmpTime, pProgress.get() ); + nTmpTime += 100; + } + if( bReverse ) + { + if (i == 0) + { + // Terminate loop. + bCount = false; + } + else + { + --i; + } + } + else + { + i++; + if (i >= nCount) + { + // Terminate loop. + bCount = false; + // Move i back into valid range. + i = nCount - 1; + } + } + } + + // to re-enable the controls + bMovie = false; + if (nCount > 0) + { + assert(i == m_nCurrentFrame); + UpdateControl(); + } + + if( pProgress ) + { + pProgress.reset(); + m_xBtnStop->set_sensitive(false); + } + + m_xRbtGroup->set_sensitive( bRbtGroupEnabled ); + m_xBtnGetAllObjects->set_sensitive( bBtnGetAllObjectsEnabled ); + m_xBtnGetOneObject->set_sensitive( bBtnGetOneObjectEnabled ); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickLastHdl, weld::Button&, void) +{ + m_nCurrentFrame = + (m_FrameList.empty()) ? EMPTY_FRAMELIST : m_FrameList.size() - 1 ; + UpdateControl(); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickRbtHdl, weld::Toggleable&, void) +{ + if (m_FrameList.empty() || m_xRbtGroup->get_active()) + { + m_xTimeField->set_text( OUString() ); + m_xTimeField->set_sensitive( false ); + m_xLbLoopCount->set_sensitive( false ); + } + else if (m_xRbtBitmap->get_active()) + { + sal_uLong n = m_xNumFldBitmap->get_value(); + if( n > 0 ) + { + ::tools::Time const & rTime = m_FrameList[n - 1].second; + m_xFormatter->SetTime( rTime ); + m_xFormatter->ReFormat(); + } + m_xTimeField->set_sensitive(true); + m_xLbLoopCount->set_sensitive(true); + } +} + +IMPL_LINK(AnimationWindow, ClickHelpHdl, weld::Button&, rButton, void) +{ + if (Help* pHelp = Application::GetHelp()) + pHelp->Start(OUString::fromUtf8(m_xContainer->get_help_id()), &rButton); +} + +IMPL_LINK( AnimationWindow, ClickGetObjectHdl, weld::Button&, rBtn, void ) +{ + bAllObjects = &rBtn == m_xBtnGetAllObjects.get(); + + // Code now in AddObj() + SfxBoolItem aItem( SID_ANIMATOR_ADD, true ); + + GetBindings().GetDispatcher()->ExecuteList( + SID_ANIMATOR_ADD, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); +} + +IMPL_LINK( AnimationWindow, ClickRemoveBitmapHdl, weld::Button&, rBtn, void ) +{ + SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); + SdrObject* pObject; + + // tdf#95298 check m_nCurrentFrame for EMPTY_FRAMELIST to avoid out-of-bound array access + if (&rBtn == m_xBtnRemoveBitmap.get() && EMPTY_FRAMELIST != m_nCurrentFrame) + { + m_FrameList.erase(m_FrameList.begin() + m_nCurrentFrame); + + pObject = pPage->GetObj(m_nCurrentFrame); + // Through acquisition of the AnimatedGIFs, objects does not need to + // exist. + if( pObject ) + { + pObject = pPage->RemoveObject(m_nCurrentFrame); + DBG_ASSERT(pObject, "Clone not found during deletion"); + SdrObject::Free( pObject ); + pPage->RecalcObjOrdNums(); + } + + if (m_nCurrentFrame >= m_FrameList.size()) + { + // tdf#95298 last frame was deleted, try to use the one before it or go on empty state + m_nCurrentFrame = m_FrameList.empty() ? EMPTY_FRAMELIST : m_FrameList.size() - 1; + } + } + else // delete everything + { + std::unique_ptr xWarn(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::YesNo, + SdResId(STR_ASK_DELETE_ALL_PICTURES))); + short nReturn = xWarn->run(); + + if( nReturn == RET_YES ) + { + // clear frame list + for (size_t i = m_FrameList.size(); i > 0; ) + { + --i; + pObject = pPage->GetObj( i ); + if( pObject ) + { + pObject = pPage->RemoveObject( i ); + DBG_ASSERT(pObject, "Clone not found during deletion"); + SdrObject::Free( pObject ); + //pPage->RecalcObjOrdNums(); + } + } + m_FrameList.clear(); + m_nCurrentFrame = EMPTY_FRAMELIST; + } + } + + // can we create an animation group + if (m_FrameList.empty()) + { + m_xBtnCreateGroup->set_sensitive(false); + // if previous disabled by acquisition of AnimatedGIFs: + //m_xRbtBitmap->set_sensitive(true); + m_xRbtGroup->set_sensitive(true); + } + + // calculate and set zoom for DisplayWin + Fraction aFrac(GetScale()); + m_xCtlDisplay->SetScale(aFrac); + + UpdateControl(); +} + +IMPL_LINK_NOARG(AnimationWindow, ClickCreateGroupHdl, weld::Button&, void) +{ + // Code now in CreatePresObj() + SfxBoolItem aItem( SID_ANIMATOR_CREATE, true ); + + GetBindings().GetDispatcher()->ExecuteList(SID_ANIMATOR_CREATE, + SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); +} + +IMPL_LINK_NOARG(AnimationWindow, ModifyBitmapHdl, weld::SpinButton&, void) +{ + sal_uLong nBmp = m_xNumFldBitmap->get_value(); + + if (nBmp > m_FrameList.size()) + { + nBmp = m_FrameList.size(); + } + + m_nCurrentFrame = nBmp - 1; + + UpdateControl(); +} + +IMPL_LINK_NOARG(AnimationWindow, ModifyTimeHdl, weld::FormattedSpinButton&, void) +{ + sal_uLong nPos = m_xNumFldBitmap->get_value() - 1; + + ::tools::Time & rTime = m_FrameList[nPos].second; + + rTime = m_xFormatter->GetTime(); +} + +void AnimationWindow::UpdateControl(bool const bDisableCtrls) +{ + // tdf#95298 check m_nCurrentFrame for EMPTY_FRAMELIST to avoid out-of-bound array access + if (!m_FrameList.empty() && EMPTY_FRAMELIST != m_nCurrentFrame) + { + BitmapEx & rBmp(m_FrameList[m_nCurrentFrame].first); + + SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); + SdrObject *const pObject = pPage->GetObj(m_nCurrentFrame); + if( pObject ) + { + ScopedVclPtrInstance< VirtualDevice > pVD; + ::tools::Rectangle aObjRect( pObject->GetCurrentBoundRect() ); + Size aObjSize( aObjRect.GetSize() ); + Point aOrigin( Point( -aObjRect.Left(), -aObjRect.Top() ) ); + MapMode aMap( pVD->GetMapMode() ); + aMap.SetMapUnit( MapUnit::Map100thMM ); + aMap.SetOrigin( aOrigin ); + pVD->SetMapMode( aMap ); + pVD->SetOutputSize( aObjSize ); + const StyleSettings& rStyles = Application::GetSettings().GetStyleSettings(); + pVD->SetBackground( Wallpaper( rStyles.GetFieldColor() ) ); + pVD->SetDrawMode( rStyles.GetHighContrastMode() + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR ); + pVD->Erase(); + pObject->SingleObjectPainter( *pVD ); + rBmp = pVD->GetBitmapEx( aObjRect.TopLeft(), aObjSize ); + } + + m_xCtlDisplay->SetBitmapEx(&rBmp); + } + else + { + m_xCtlDisplay->SetBitmapEx(nullptr); + } + + m_xCtlDisplay->Invalidate(); + + m_xFiCount->set_label(OUString::number( + m_FrameList.size())); + + if (!m_FrameList.empty() && !bMovie) + { + size_t nIndex = m_nCurrentFrame + 1; + m_xNumFldBitmap->set_value(nIndex); + + // if there is at least 1 object in the list + m_xBtnFirst->set_sensitive(true); + m_xBtnReverse->set_sensitive(true); + m_xBtnPlay->set_sensitive(true); + m_xBtnLast->set_sensitive(true); + m_xNumFldBitmap->set_sensitive(true); + m_xTimeField->set_sensitive(true); + m_xLbLoopCount->set_sensitive(true); + m_xBtnRemoveBitmap->set_sensitive(true); + m_xBtnRemoveAll->set_sensitive(true); + } + else + { + // if no object is in the list + m_xBtnFirst->set_sensitive( false ); + m_xBtnReverse->set_sensitive( false ); + m_xBtnPlay->set_sensitive( false ); + m_xBtnLast->set_sensitive( false ); + m_xNumFldBitmap->set_sensitive( false ); + m_xTimeField->set_sensitive( false ); + m_xLbLoopCount->set_sensitive( false ); + m_xBtnRemoveBitmap->set_sensitive( false ); + m_xBtnRemoveAll->set_sensitive( false ); + } + + if( bMovie && bDisableCtrls ) + { + m_xBtnGetOneObject->set_sensitive( false ); + m_xBtnGetAllObjects->set_sensitive( false ); + m_xRbtGroup->set_sensitive( false ); + m_xRbtBitmap->set_sensitive( false ); + m_xBtnCreateGroup->set_sensitive( false ); + m_xFtAdjustment->set_sensitive( false ); + m_xLbAdjustment->set_sensitive( false ); + } + else + { + // enable 'group object' only if it is not an Animated GIF + if (m_FrameList.empty()) + { + m_xRbtGroup->set_sensitive(true); + } + + m_xRbtBitmap->set_sensitive(true); + m_xBtnCreateGroup->set_sensitive(!m_FrameList.empty()); + m_xFtAdjustment->set_sensitive(true); + m_xLbAdjustment->set_sensitive(true); + } + + ClickRbtHdl(*m_xRbtGroup); +} + +void AnimationWindow::ResetAttrs() +{ + m_xRbtGroup->set_active(true); + m_xLbAdjustment->set_active( BA_CENTER ); + // LoopCount + m_xLbLoopCount->set_active( m_xLbLoopCount->get_count() - 1); + + UpdateControl(); +} + +void AnimationWindow::WaitInEffect( sal_uLong nMilliSeconds, sal_uLong nTime, + SfxProgress* pProgress ) const +{ + sal_uInt64 aEnd = ::tools::Time::GetSystemTicks() + nMilliSeconds; + sal_uInt64 aCurrent = ::tools::Time::GetSystemTicks(); + while (aCurrent < aEnd) + { + aCurrent = ::tools::Time::GetSystemTicks(); + + if( pProgress ) + pProgress->SetState( nTime + nMilliSeconds + aCurrent - aEnd ); + + Application::Reschedule(); + + if( !bMovie ) + return; + } +} + +Fraction AnimationWindow::GetScale() +{ + Fraction aFrac; + size_t const nCount = m_FrameList.size(); + if (nCount > 0) + { + Size aBmpSize(0, 0); + for (size_t i = 0; i < nCount; i++) + { + BitmapEx const & rBitmap = m_FrameList[i].first; + Size aTempSize( rBitmap.GetBitmap().GetSizePixel() ); + aBmpSize.setWidth( std::max( aBmpSize.Width(), aTempSize.Width() ) ); + aBmpSize.setHeight( std::max( aBmpSize.Height(), aTempSize.Height() ) ); + } + + aBmpSize.AdjustWidth(10 ); + aBmpSize.AdjustHeight(10 ); + + Size aDisplaySize(m_xCtlDisplay->GetOutputSizePixel()); + + aFrac = Fraction( std::min( static_cast(aDisplaySize.Width()) / static_cast(aBmpSize.Width()), + static_cast(aDisplaySize.Height()) / static_cast(aBmpSize.Height()) ) ); + } + return aFrac; +} + +void AnimationWindow::Resize() +{ + SfxDockingWindow::Resize(); + Fraction aFrac(GetScale()); + m_xCtlDisplay->SetScale(aFrac); +} + +bool AnimationWindow::Close() +{ + if( maPlayLock.isLocked() ) + { + return false; + } + else + { + SfxBoolItem aItem( SID_ANIMATION_OBJECTS, false ); + + GetBindings().GetDispatcher()->ExecuteList( + SID_ANIMATION_OBJECTS, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + + SfxDockingWindow::Close(); + + return true; + } +} + +void AnimationWindow::AddObj (::sd::View& rView ) +{ + // finish text entry mode to ensure that bitmap is identical with object + if( rView.IsTextEdit() ) + rView.SdrEndTextEdit(); + + // clone object(s) and insert the clone(s) into the list + const SdrMarkList& rMarkList = rView.GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); + const size_t nCloneCount = pPage->GetObjCount(); + + if (nMarkCount <= 0) + return; + + // If it is ONE animation object or one group object, which was + // 'individually taken', we insert the objects separately + bool bAnimObj = false; + if( nMarkCount == 1 ) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObject = pMark->GetMarkedSdrObj(); + SdAnimationInfo* pAnimInfo = SdDrawDocument::GetAnimationInfo( pObject ); + SdrInventor nInv = pObject->GetObjInventor(); + SdrObjKind nId = pObject->GetObjIdentifier(); + + // Animated Bitmap (GIF) + if( nInv == SdrInventor::Default && nId == SdrObjKind::Graphic && static_cast( pObject )->IsAnimated() ) + { + const SdrGrafObj* pGrafObj = static_cast(pObject); + Graphic aGraphic( pGrafObj->GetTransformedGraphic() ); + sal_uInt16 nCount = 0; + + if( aGraphic.IsAnimated() ) + nCount = aGraphic.GetAnimation().Count(); + + if( nCount > 0 ) + { + const Animation aAnimation( aGraphic.GetAnimation() ); + + for( sal_uInt16 i = 0; i < nCount; i++ ) + { + const AnimationBitmap& rAnimationBitmap = aAnimation.Get( i ); + + // LoopCount + if( i == 0 ) + { + sal_uInt32 nLoopCount = aAnimation.GetLoopCount(); + + if( !nLoopCount ) // endless + m_xLbLoopCount->set_active( m_xLbLoopCount->get_count() - 1); + else + m_xLbLoopCount->set_active_text(OUString::number( nLoopCount ) ); + } + + ::tools::Long nTime = rAnimationBitmap.mnWait; + ::tools::Time aTime( 0, 0, nTime / 100, nTime % 100 ); + size_t nIndex = m_nCurrentFrame + 1; + m_FrameList.insert( + m_FrameList.begin() + nIndex, + ::std::make_pair(rAnimationBitmap.maBitmapEx, aTime)); + + // increment => next one inserted after this one + ++m_nCurrentFrame; + } + // if an animated GIF is taken, only such one can be created + m_xRbtBitmap->set_active(true); + m_xRbtGroup->set_sensitive( false ); + bAnimObj = true; + } + } + else if( bAllObjects || ( pAnimInfo && pAnimInfo->mbIsMovie ) ) + { + // several objects + SdrObjList* pObjList = static_cast(pObject)->GetSubList(); + + for( size_t nObject = 0; nObject < pObjList->GetObjCount(); ++nObject ) + { + SdrObject* pSnapShot(pObjList->GetObj(nObject)); + BitmapEx aBitmapEx(SdrExchangeView::GetObjGraphic(*pSnapShot).GetBitmapEx()); + size_t nIndex = m_nCurrentFrame + 1; + m_FrameList.insert( + m_FrameList.begin() + nIndex, + ::std::make_pair(aBitmapEx, m_xFormatter->GetTime())); + + // increment => next one inserted after this one + ++m_nCurrentFrame; + + // Clone + pPage->InsertObject( + pSnapShot->CloneSdrObject(pPage->getSdrModelFromSdrPage()), + m_nCurrentFrame); + } + bAnimObj = true; + } + } + // also one single animated object + if( !bAnimObj && !( bAllObjects && nMarkCount > 1 ) ) + { + BitmapEx aBitmapEx(rView.GetAllMarkedGraphic().GetBitmapEx()); + + ::tools::Time aTime( m_xFormatter->GetTime() ); + + size_t nIndex = m_nCurrentFrame + 1; + m_FrameList.insert( + m_FrameList.begin() + nIndex, + ::std::make_pair(aBitmapEx, aTime)); + } + + // one single object + if( nMarkCount == 1 && !bAnimObj ) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObject = pMark->GetMarkedSdrObj(); + SdrObject* pClone(pObject->CloneSdrObject(pPage->getSdrModelFromSdrPage())); + size_t nIndex = m_nCurrentFrame + 1; + pPage->InsertObject(pClone, nIndex); + } + // several objects: group the clones + else if (nMarkCount > 1) + { + // take objects separately + if( bAllObjects ) + { + for( size_t nObject= 0; nObject < nMarkCount; ++nObject ) + { + // Clone + SdrObject* pObject(rMarkList.GetMark(nObject)->GetMarkedSdrObj()); + BitmapEx aBitmapEx(SdrExchangeView::GetObjGraphic(*pObject).GetBitmapEx()); + size_t nIndex = m_nCurrentFrame + 1; + m_FrameList.insert( + m_FrameList.begin() + nIndex, + ::std::make_pair(aBitmapEx, m_xFormatter->GetTime())); + + // increment => next one inserted after this one + ++m_nCurrentFrame; + + pPage->InsertObject( + pObject->CloneSdrObject(pPage->getSdrModelFromSdrPage()), + m_nCurrentFrame); + } + bAnimObj = true; // that we don't change again + } + else + { + SdrObjGroup* pCloneGroup = new SdrObjGroup(rView.getSdrModelFromSdrView()); + SdrObjList* pObjList = pCloneGroup->GetSubList(); + + for (size_t nObject= 0; nObject < nMarkCount; ++nObject) + { + pObjList->InsertObject( + rMarkList.GetMark(nObject)->GetMarkedSdrObj()->CloneSdrObject( + pPage->getSdrModelFromSdrPage())); + } + + size_t nIndex = m_nCurrentFrame + 1; + pPage->InsertObject(pCloneGroup, nIndex); + } + } + + if( !bAnimObj ) + { + ++m_nCurrentFrame; + } + + // if there was nothing in the animator before but now is something + // there, we can create an animation group + if (nCloneCount == 0 && !m_FrameList.empty()) + { + m_xBtnCreateGroup->set_sensitive(true); + } + + // calculate and set zoom for DisplayWin + Fraction aFrac( GetScale() ); + m_xCtlDisplay->SetScale(aFrac); + + UpdateControl(); +} + +void AnimationWindow::CreateAnimObj (::sd::View& rView ) +{ + vcl::Window* pOutWin = rView.GetFirstOutputDevice()->GetOwnerWindow(); // GetWin( 0 ); + DBG_ASSERT( pOutWin, "Window does not exist!" ); + + // find window center + const MapMode aMap100( MapUnit::Map100thMM ); + Size aMaxSizeLog; + Size aMaxSizePix; + Size aTemp( pOutWin->GetOutputSizePixel() ); + const Point aWindowCenter( pOutWin->PixelToLogic( Point( aTemp.Width() >> 1, aTemp.Height() >> 1 ) ) ); + const OutputDevice* pDefDev = Application::GetDefaultDevice(); + const size_t nCount = m_FrameList.size(); + BitmapAdjustment eBA = static_cast(m_xLbAdjustment->get_active()); + + // find biggest bitmap + for (size_t i = 0; i < nCount; ++i) + { + const BitmapEx& rBmpEx = m_FrameList[i].first; + const Graphic aGraphic( rBmpEx ); + Size aTmpSizeLog; + const Size aTmpSizePix( rBmpEx.GetSizePixel() ); + + if ( aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) + aTmpSizeLog = pDefDev->PixelToLogic( aGraphic.GetPrefSize(), aMap100 ); + else + aTmpSizeLog = OutputDevice::LogicToLogic( aGraphic.GetPrefSize(), aGraphic.GetPrefMapMode(), aMap100 ); + + aMaxSizeLog.setWidth( std::max( aMaxSizeLog.Width(), aTmpSizeLog.Width() ) ); + aMaxSizeLog.setHeight( std::max( aMaxSizeLog.Height(), aTmpSizeLog.Height() ) ); + + aMaxSizePix.setWidth( std::max( aMaxSizePix.Width(), aTmpSizePix.Width() ) ); + aMaxSizePix.setHeight( std::max( aMaxSizePix.Height(), aTmpSizePix.Height() ) ); + } + + SdrPageView* pPV = rView.GetSdrPageView(); + + if( m_xRbtBitmap->get_active() ) + { + // create bitmap group (Animated GIF) + Animation aAnimation; + Point aPt; + + for (size_t i = 0; i < nCount; ++i) + { + ::tools::Time const & rTime = m_FrameList[i].second; + ::tools::Long nTime = rTime.GetNanoSec(); + nTime += rTime.GetSec() * 100; + + BitmapEx const & rBitmapEx = m_FrameList[i].first; + + // calculate offset for the specified direction + const Size aBitmapSize( rBitmapEx.GetSizePixel() ); + + switch( eBA ) + { + case BA_LEFT_UP: + break; + + case BA_LEFT: + aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); + break; + + case BA_LEFT_DOWN: + aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); + break; + + case BA_UP: + aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); + break; + + case BA_CENTER: + aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); + aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); + break; + + case BA_DOWN: + aPt.setX( (aMaxSizePix.Width() - aBitmapSize.Width()) >> 1 ); + aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); + break; + + case BA_RIGHT_UP: + aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); + break; + + case BA_RIGHT: + aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); + aPt.setY( (aMaxSizePix.Height() - aBitmapSize.Height()) >> 1 ); + break; + + case BA_RIGHT_DOWN: + aPt.setX( aMaxSizePix.Width() - aBitmapSize.Width() ); + aPt.setY( aMaxSizePix.Height() - aBitmapSize.Height() ); + break; + + } + + // find LoopCount (number of passes) + AnimationBitmap aAnimationBitmap; + sal_uInt32 nLoopCount = 0; + sal_Int32 nPos = m_xLbLoopCount->get_active(); + + if( nPos != -1 && nPos != m_xLbLoopCount->get_count() - 1 ) // endless + nLoopCount = m_xLbLoopCount->get_active_text().toUInt32(); + + aAnimationBitmap.maBitmapEx = rBitmapEx; + aAnimationBitmap.maPositionPixel = aPt; + aAnimationBitmap.maSizePixel = aBitmapSize; + aAnimationBitmap.mnWait = nTime; + aAnimationBitmap.meDisposal = Disposal::Back; + aAnimationBitmap.mbUserInput = false; + + aAnimation.Insert( aAnimationBitmap ); + aAnimation.SetDisplaySizePixel( aMaxSizePix ); + aAnimation.SetLoopCount( nLoopCount ); + } + + SdrGrafObj* pGrafObj = new SdrGrafObj( + rView.getSdrModelFromSdrView(), + Graphic(aAnimation)); + const Point aOrg( aWindowCenter.X() - ( aMaxSizeLog.Width() >> 1 ), aWindowCenter.Y() - ( aMaxSizeLog.Height() >> 1 ) ); + + pGrafObj->SetLogicRect( ::tools::Rectangle( aOrg, aMaxSizeLog ) ); + rView.InsertObjectAtView( pGrafObj, *pPV, SdrInsertFlags::SETDEFLAYER); + } + else + { + // calculate offset for the specified direction + Size aOffset; + SdrObject * pClone = nullptr; + SdPage* pPage = pMyDoc->GetSdPage(0, PageKind::Standard); + + for (size_t i = 0; i < nCount; ++i) + { + pClone = pPage->GetObj(i); + ::tools::Rectangle aRect( pClone->GetSnapRect() ); + + switch( eBA ) + { + case BA_LEFT_UP: + break; + + case BA_LEFT: + aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); + break; + + case BA_LEFT_DOWN: + aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); + break; + + case BA_UP: + aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); + break; + + case BA_CENTER: + aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); + aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); + break; + + case BA_DOWN: + aOffset.setWidth( (aMaxSizeLog.Width() - aRect.GetWidth()) / 2 ); + aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); + break; + + case BA_RIGHT_UP: + aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); + break; + + case BA_RIGHT: + aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); + aOffset.setHeight( (aMaxSizeLog.Height() - aRect.GetHeight()) / 2 ); + break; + + case BA_RIGHT_DOWN: + aOffset.setWidth( aMaxSizeLog.Width() - aRect.GetWidth() ); + aOffset.setHeight( aMaxSizeLog.Height() - aRect.GetHeight() ); + break; + + } + // Unfortunately, SetSnapRect is not implemented for ellipses !!! + Point aMovePt( aWindowCenter + Point( aOffset.Width(), aOffset.Height() ) - aRect.TopLeft() ); + Size aMoveSize( aMovePt.X(), aMovePt.Y() ); + pClone->NbcMove( aMoveSize ); + } + + // #i42894# Caution(!) variable pPage looks right, but it is a page from the local + // document the dialog is using (!), so get the target page from the target view + SdPage* pTargetSdPage = dynamic_cast< SdPage* >(rView.GetSdrPageView() ? rView.GetSdrPageView()->GetPage() : nullptr); + + if(pTargetSdPage) + { + // create animation group + SdrObjGroup* pGroup = new SdrObjGroup(rView.getSdrModelFromSdrView()); + SdrObjList* pObjList = pGroup->GetSubList(); + + for (size_t i = 0; i < nCount; ++i) + { + // the clone remains in the animation; we insert a clone of the + // clone into the group + pClone = pPage->GetObj(i); + SdrObject* pCloneOfClone(pClone->CloneSdrObject(pTargetSdPage->getSdrModelFromSdrPage())); + //SdrObject* pCloneOfClone = pPage->GetObj(i)->Clone(); + pObjList->InsertObject(pCloneOfClone); + } + + // until now the top left corner of the group is in the window center; + // correct the position by half of the size of the group + aTemp = aMaxSizeLog; + aTemp.setHeight( - aTemp.Height() / 2 ); + aTemp.setWidth( - aTemp.Width() / 2 ); + pGroup->NbcMove(aTemp); + + // #i42894# create needed SMIL stuff and move child objects to page directly (see + // comments at EffectMigration::CreateAnimatedGroup why this has to be done). + EffectMigration::CreateAnimatedGroup(*pGroup, *pTargetSdPage); + + // #i42894# if that worked, delete the group again + if(!pGroup->GetSubList()->GetObjCount()) + { + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject* pTemp(pGroup); + SdrObject::Free(pTemp); + } + } + } + + ClickFirstHdl(*m_xBtnFirst); +} + +void AnimationWindow::DataChanged( const DataChangedEvent& rDCEvt ) +{ + SfxDockingWindow::DataChanged( rDCEvt ); + + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + UpdateControl(); + } +} + +/** + * ControllerItem for Animator + */ +AnimationControllerItem::AnimationControllerItem( + sal_uInt16 _nId, + AnimationWindow* pAnimWin, + SfxBindings* _pBindings) + : SfxControllerItem( _nId, *_pBindings ), + pAnimationWin( pAnimWin ) +{ +} + +void AnimationControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, + SfxItemState eState, const SfxPoolItem* pItem ) +{ + if( eState >= SfxItemState::DEFAULT && nSId == SID_ANIMATOR_STATE ) + { + const SfxUInt16Item* pStateItem = dynamic_cast< const SfxUInt16Item*>( pItem ); + assert(pStateItem); //SfxUInt16Item expected + if (pStateItem) + { + sal_uInt16 nState = pStateItem->GetValue(); + pAnimationWin->m_xBtnGetOneObject->set_sensitive( nState & 1 ); + pAnimationWin->m_xBtnGetAllObjects->set_sensitive( nState & 2 ); + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/assclass.cxx b/sd/source/ui/dlg/assclass.cxx new file mode 100644 index 000000000..4d48a327f --- /dev/null +++ b/sd/source/ui/dlg/assclass.cxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +Assistent::Assistent(int nNoOfPages) + : mnPages(nNoOfPages), mnCurrentPage(1) +{ + if(mnPages > MAX_PAGES) + mnPages = MAX_PAGES; + + mpPageStatus.reset(new bool[mnPages]); + + for(int i=0; i < mnPages; ++i) + mpPageStatus[i] = true; +} + +bool Assistent::InsertControl(int nDestPage, weld::Widget* pUsedControl) +{ + DBG_ASSERT( (nDestPage > 0) && (nDestPage <= mnPages), "Page not available!"); + + if((nDestPage>0)&&(nDestPage<=mnPages)) + { + maPages[nDestPage-1].emplace_back(pUsedControl); + pUsedControl->hide(); + pUsedControl->set_sensitive(false); + return true; + } + + return false; +} + +void Assistent::NextPage() +{ + if(mnCurrentPage1) + { + int nPage = mnCurrentPage-1; + while(nPage >= 0 && !mpPageStatus[nPage-1]) + nPage--; + + if(nPage >= 0) + GotoPage(nPage); + } +} + +bool Assistent::GotoPage(const int nPageToGo) +{ + DBG_ASSERT( (nPageToGo > 0) && (nPageToGo <= mnPages), "Page not available!"); + + if((nPageToGo>0)&&(nPageToGo<=mnPages)&&mpPageStatus[nPageToGo-1]) + { + int nIndex=mnCurrentPage-1; + + for(auto& rxPage : maPages[nIndex]) + { + rxPage->set_sensitive(false); + rxPage->hide(); + } + + mnCurrentPage=nPageToGo; + nIndex=mnCurrentPage-1; + + for(auto& rxPage : maPages[nIndex]) + { + rxPage->set_sensitive(true); + rxPage->show(); + } + + return true; + } + + return false; +} + +bool Assistent::IsLastPage() const +{ + if(mnCurrentPage == mnPages) + return true; + + int nPage = mnCurrentPage+1; + while(nPage <= mnPages && !mpPageStatus[nPage-1]) + nPage++; + + return nPage > mnPages; +} + +bool Assistent::IsFirstPage() const +{ + if(mnCurrentPage == 1) + return true; + + int nPage = mnCurrentPage-1; + while(nPage > 0 && !mpPageStatus[nPage-1]) + nPage--; + + return nPage == 0; +} + +bool Assistent::IsEnabled( int nPage ) const +{ + DBG_ASSERT( (nPage>0) && (nPage <= mnPages), "Page not available!" ); + + return (nPage>0) && (nPage <= mnPages && mpPageStatus[nPage-1]); +} + +void Assistent::EnablePage( int nPage ) +{ + DBG_ASSERT( (nPage>0) && (nPage <= mnPages), "Page not available!" ); + + if((nPage>0) && (nPage < mnPages && !mpPageStatus[nPage-1])) + { + mpPageStatus[nPage-1] = true; + } +} + +void Assistent::DisablePage( int nPage ) +{ + DBG_ASSERT( (nPage>0) && (nPage <= mnPages), "Page not available!" ); + + if((nPage>0) && (nPage <= mnPages && mpPageStatus[nPage-1])) + { + mpPageStatus[nPage-1] = false; + if(mnCurrentPage == nPage) + GotoPage(1); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/brkdlg.cxx b/sd/source/ui/dlg/brkdlg.cxx new file mode 100644 index 000000000..bc1d0f5cf --- /dev/null +++ b/sd/source/ui/dlg/brkdlg.cxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace sd { + +/** + * dialog to split metafiles + */ + +BreakDlg::BreakDlg(weld::Window* pWindow, DrawView* pDrView, DrawDocShell* pShell, + sal_uLong nSumActionCount, sal_uLong nObjCount) + : SfxDialogController(pWindow, "modules/sdraw/ui/breakdialog.ui", "BreakDialog") + , m_xFiObjInfo(m_xBuilder->weld_label("metafiles")) + , m_xFiActInfo(m_xBuilder->weld_label("metaobjects")) + , m_xFiInsInfo(m_xBuilder->weld_label("drawingobjects")) + , m_xBtnCancel(m_xBuilder->weld_button("cancel")) + , m_pDrView(pDrView) + , m_bCancel(false) + , m_aUpdateIdle( "sd::BreakDlg m_aUpdateIdle" ) +{ + m_aUpdateIdle.SetPriority( TaskPriority::REPAINT ); + m_aUpdateIdle.SetInvokeHandler( LINK( this, BreakDlg, InitialUpdate ) ); + + m_xBtnCancel->connect_clicked(LINK(this, BreakDlg, CancelButtonHdl)); + + m_xProgress.reset(new SfxProgress(pShell, SdResId(STR_BREAK_METAFILE), nSumActionCount*3)); + + m_xProgrInfo.reset(new SvdProgressInfo(LINK(this, BreakDlg, UpDate))); + // every action is edited 3 times in DoImport() + m_xProgrInfo->Init( nObjCount ); +} + +// Control-Handler for cancel button +IMPL_LINK_NOARG(BreakDlg, CancelButtonHdl, weld::Button&, void) +{ + m_bCancel = true; + m_xBtnCancel->set_sensitive(false); +} + +/** + * The working function has to call the UpDate method periodically. + * With the first call, the overall number of actions is provided. + * Every following call should contain the finished actions since the + * last call of UpDate. + */ +IMPL_LINK( BreakDlg, UpDate, void*, nInit, bool ) +{ + if (!m_xProgrInfo) + return true; + + // update status bar or show an error message? + if(nInit == reinterpret_cast(1)) + { + std::unique_ptr xErrBox(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_BREAK_FAIL))); + xErrBox->run(); + } + else + { + if (m_xProgress) + m_xProgress->SetState(m_xProgrInfo->GetSumCurAction()); + } + + // which object is shown at the moment? + OUString info = OUString::number(m_xProgrInfo->GetCurObj()) + + "/" + + OUString::number(m_xProgrInfo->GetObjCount()); + m_xFiObjInfo->set_label(info); + + // how many actions are started? + if (m_xProgrInfo->GetActionCount() == 0) + { + m_xFiActInfo->set_label( OUString() ); + } + else + { + info = OUString::number(m_xProgrInfo->GetCurAction()) + + "/" + + OUString::number(m_xProgrInfo->GetActionCount()); + m_xFiActInfo->set_label(info); + } + + // and inserted???? + if (m_xProgrInfo->GetInsertCount() == 0) + { + m_xFiInsInfo->set_label( OUString() ); + } + else + { + info = OUString::number(m_xProgrInfo->GetCurInsert()) + + "/" + + OUString::number(m_xProgrInfo->GetInsertCount()); + m_xFiInsInfo->set_label(info); + } + + // make sure dialog gets painted, it is intended to + // show the progress to the user. Also necessary to + // provide a clickable cancel button + Scheduler::ProcessEventsToIdle(); + + // return okay-value (-> !cancel) + return !m_bCancel; +} + +/** + * open a modal dialog and start a timer which calls the working function after + * the opening of the dialog + */ +short BreakDlg::run() +{ + m_aUpdateIdle.Start(); + return SfxDialogController::run(); +} + +/** + * link-method which starts the working function + */ +IMPL_LINK_NOARG(BreakDlg, InitialUpdate, Timer *, void) +{ + m_pDrView->DoImportMarkedMtf(m_xProgrInfo.get()); + m_xDialog->response(RET_OK); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/copydlg.cxx b/sd/source/ui/dlg/copydlg.cxx new file mode 100644 index 000000000..1fa8c2dab --- /dev/null +++ b/sd/source/ui/dlg/copydlg.cxx @@ -0,0 +1,263 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + + +namespace sd { + +constexpr char TOKEN = ';'; + +CopyDlg::CopyDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, ::sd::View* pInView) + : SfxDialogController(pWindow, "modules/sdraw/ui/copydlg.ui", "DuplicateDialog") + , mrOutAttrs(rInAttrs) + , maUIScale(pInView->GetDoc().GetUIScale()) + , mpView(pInView) + , m_xNumFldCopies(m_xBuilder->weld_spin_button("copies")) + , m_xBtnSetViewData(m_xBuilder->weld_button("viewdata")) + , m_xMtrFldMoveX(m_xBuilder->weld_metric_spin_button("x", FieldUnit::CM)) + , m_xMtrFldMoveY(m_xBuilder->weld_metric_spin_button("y", FieldUnit::CM)) + , m_xMtrFldAngle(m_xBuilder->weld_metric_spin_button("angle", FieldUnit::DEGREE)) + , m_xMtrFldWidth(m_xBuilder->weld_metric_spin_button("width", FieldUnit::CM)) + , m_xMtrFldHeight(m_xBuilder->weld_metric_spin_button("height", FieldUnit::CM)) + , m_xFtEndColor(m_xBuilder->weld_label("endlabel")) + , m_xBtnSetDefault(m_xBuilder->weld_button("default")) + , m_xLbStartColor(new ColorListBox(m_xBuilder->weld_menu_button("start"), [this]{ return m_xDialog.get(); } )) + , m_xLbEndColor(new ColorListBox(m_xBuilder->weld_menu_button("end"), [this]{ return m_xDialog.get(); } )) +{ + m_xLbStartColor->SetSelectHdl( LINK( this, CopyDlg, SelectColorHdl ) ); + m_xBtnSetViewData->connect_clicked( LINK( this, CopyDlg, SetViewData ) ); + m_xBtnSetDefault->connect_clicked( LINK( this, CopyDlg, SetDefault ) ); + + FieldUnit eFUnit( SfxModule::GetCurrentFieldUnit() ); + + SetFieldUnit( *m_xMtrFldMoveX, eFUnit, true ); + SetFieldUnit( *m_xMtrFldMoveY, eFUnit, true ); + SetFieldUnit( *m_xMtrFldWidth, eFUnit, true ); + SetFieldUnit( *m_xMtrFldHeight, eFUnit, true ); + + Reset(); +} + +CopyDlg::~CopyDlg() +{ + SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); + OUString sStr = + OUString::number(m_xNumFldCopies->get_value()) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldMoveX->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldMoveY->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldAngle->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldWidth->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(m_xMtrFldHeight->get_value(FieldUnit::NONE)) + OUStringChar(TOKEN) + + OUString::number(static_cast(m_xLbStartColor->GetSelectEntryColor())) + OUStringChar(TOKEN) + + OUString::number(static_cast(m_xLbEndColor->GetSelectEntryColor())); + aDlgOpt.SetUserItem("UserItem", css::uno::Any(sStr)); +} + +/** + * reads provided item set or evaluate ini string + */ +void CopyDlg::Reset() +{ + // Set Min/Max values + ::tools::Rectangle aRect = mpView->GetAllMarkedRect(); + Size aPageSize = mpView->GetSdrPageView()->GetPage()->GetSize(); + + // tdf#125011 draw/impress sizes are in mm_100th already, "normalize" to + // decimal shift by number of decimal places the widgets are using (2) then + // scale by the ui scaling factor + auto nPageWidth = tools::Long(m_xMtrFldMoveX->normalize(aPageSize.Width()) / maUIScale); + auto nPageHeight = tools::Long(m_xMtrFldMoveX->normalize(aPageSize.Height()) / maUIScale); + auto nRectWidth = tools::Long(m_xMtrFldMoveX->normalize(aRect.GetWidth()) / maUIScale); + auto nRectHeight = tools::Long(m_xMtrFldMoveX->normalize(aRect.GetHeight()) / maUIScale); + + m_xMtrFldMoveX->set_range(-nPageWidth, nPageWidth, FieldUnit::MM_100TH); + m_xMtrFldMoveY->set_range(-nPageHeight, nPageHeight, FieldUnit::MM_100TH); + m_xMtrFldWidth->set_range(-nRectWidth, nPageWidth, FieldUnit::MM_100TH); + m_xMtrFldHeight->set_range(-nRectHeight, nPageHeight, FieldUnit::MM_100TH); + + OUString aStr; + SvtViewOptions aDlgOpt(EViewType::Dialog, OStringToOUString(m_xDialog->get_help_id(), RTL_TEXTENCODING_UTF8)); + if (aDlgOpt.Exists()) + { + css::uno::Any aUserItem = aDlgOpt.GetUserItem("UserItem"); + aUserItem >>= aStr; + } + + if (aStr.isEmpty()) + { + if( const SfxUInt16Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_NUMBER ) ) + m_xNumFldCopies->set_value(pPoolItem->GetValue()); + else + m_xNumFldCopies->set_value(1); + + tools::Long nMoveX = 500; + if( const SfxInt32Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_MOVE_X ) ) + nMoveX = pPoolItem->GetValue(); + SetMetricValue( *m_xMtrFldMoveX, tools::Long(nMoveX / maUIScale), MapUnit::Map100thMM); + + tools::Long nMoveY = 500; + if( const SfxInt32Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_MOVE_Y ) ) + nMoveY = pPoolItem->GetValue(); + SetMetricValue( *m_xMtrFldMoveY, tools::Long(nMoveY / maUIScale), MapUnit::Map100thMM); + + if( const SdrAngleItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_ANGLE ) ) + m_xMtrFldAngle->set_value( pPoolItem->GetValue().get(), FieldUnit::NONE); + else + m_xMtrFldAngle->set_value(0, FieldUnit::NONE); + + tools::Long nWidth = 0; + if( const SfxInt32Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_WIDTH ) ) + nWidth = pPoolItem->GetValue(); + SetMetricValue( *m_xMtrFldWidth, tools::Long(nWidth / maUIScale), MapUnit::Map100thMM); + + tools::Long nHeight = 0; + if( const SfxInt32Item* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_HEIGHT ) ) + nHeight = pPoolItem->GetValue(); + SetMetricValue( *m_xMtrFldHeight, tools::Long(nHeight / maUIScale), MapUnit::Map100thMM); + + if( const XColorItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_START_COLOR ) ) + { + Color aColor = pPoolItem->GetColorValue(); + m_xLbStartColor->SelectEntry( aColor ); + m_xLbEndColor->SelectEntry( aColor ); + } + else + { + m_xLbStartColor->SetNoSelection(); + m_xLbEndColor->SetNoSelection(); + m_xLbEndColor->set_sensitive(false); + m_xFtEndColor->set_sensitive(false); + } + } + else + { + sal_Int32 nIdx {0}; + m_xNumFldCopies->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx))); + m_xMtrFldMoveX->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xMtrFldMoveY->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xMtrFldAngle->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xMtrFldWidth->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xMtrFldHeight->set_value(o3tl::toInt64(o3tl::getToken(aStr, 0, TOKEN, nIdx)), FieldUnit::NONE); + m_xLbStartColor->SelectEntry( Color( ColorTransparency, o3tl::toUInt32(o3tl::getToken(aStr, 0, TOKEN, nIdx)) ) ); + m_xLbEndColor->SelectEntry( Color( ColorTransparency, o3tl::toUInt32(o3tl::getToken(aStr, 0, TOKEN, nIdx)) ) ); + } + +} + +/** + * fills provided item set with dialog box attributes + */ +void CopyDlg::GetAttr( SfxItemSet& rOutAttrs ) +{ + tools::Long nMoveX = tools::Long( GetCoreValue( *m_xMtrFldMoveX, MapUnit::Map100thMM) * maUIScale); + tools::Long nMoveY = tools::Long( GetCoreValue( *m_xMtrFldMoveY, MapUnit::Map100thMM) * maUIScale); + tools::Long nHeight = tools::Long( GetCoreValue( *m_xMtrFldHeight, MapUnit::Map100thMM) * maUIScale); + tools::Long nWidth = tools::Long( GetCoreValue( *m_xMtrFldWidth, MapUnit::Map100thMM) * maUIScale); + + rOutAttrs.Put( SfxUInt16Item( ATTR_COPY_NUMBER, static_cast(m_xNumFldCopies->get_value()) ) ); + rOutAttrs.Put( SfxInt32Item( ATTR_COPY_MOVE_X, nMoveX ) ); + rOutAttrs.Put( SfxInt32Item( ATTR_COPY_MOVE_Y, nMoveY ) ); + rOutAttrs.Put( SdrAngleItem( ATTR_COPY_ANGLE, Degree100(static_cast(m_xMtrFldAngle->get_value(FieldUnit::DEGREE))) ) ); + rOutAttrs.Put( SfxInt32Item( ATTR_COPY_WIDTH, nWidth ) ); + rOutAttrs.Put( SfxInt32Item( ATTR_COPY_HEIGHT, nHeight ) ); + + NamedColor aColor = m_xLbStartColor->GetSelectedEntry(); + rOutAttrs.Put(XColorItem(ATTR_COPY_START_COLOR, aColor.second, aColor.first)); + aColor = m_xLbEndColor->GetSelectedEntry(); + rOutAttrs.Put(XColorItem(ATTR_COPY_END_COLOR, aColor.second, aColor.first)); +} + +/** + * enables and selects end color LB + */ +IMPL_LINK_NOARG(CopyDlg, SelectColorHdl, ColorListBox&, void) +{ + const Color aColor = m_xLbStartColor->GetSelectEntryColor(); + + if (!m_xLbEndColor->get_sensitive()) + { + m_xLbEndColor->SelectEntry(aColor); + m_xLbEndColor->set_sensitive(true); + m_xFtEndColor->set_sensitive(true); + } +} + +/** + * sets values of selection + */ +IMPL_LINK_NOARG(CopyDlg, SetViewData, weld::Button&, void) +{ + ::tools::Rectangle aRect = mpView->GetAllMarkedRect(); + + SetMetricValue( *m_xMtrFldMoveX, tools::Long( aRect.GetWidth() / + maUIScale ), MapUnit::Map100thMM); + SetMetricValue( *m_xMtrFldMoveY, tools::Long( aRect.GetHeight() / + maUIScale ), MapUnit::Map100thMM); + + // sets color attribute + if( const XColorItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_START_COLOR ) ) + { + Color aColor = pPoolItem->GetColorValue(); + m_xLbStartColor->SelectEntry( aColor ); + } +} + +/** + * resets values to default + */ +IMPL_LINK_NOARG(CopyDlg, SetDefault, weld::Button&, void) +{ + m_xNumFldCopies->set_value(1); + + tools::Long nValue = 500; + SetMetricValue( *m_xMtrFldMoveX, tools::Long(nValue / maUIScale), MapUnit::Map100thMM); + SetMetricValue( *m_xMtrFldMoveY, tools::Long(nValue / maUIScale), MapUnit::Map100thMM); + + nValue = 0; + m_xMtrFldAngle->set_value(nValue, FieldUnit::DEGREE); + SetMetricValue( *m_xMtrFldWidth, tools::Long(nValue / maUIScale), MapUnit::Map100thMM); + SetMetricValue( *m_xMtrFldHeight, tools::Long(nValue / maUIScale), MapUnit::Map100thMM); + + // set color attribute + if( const XColorItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_COPY_START_COLOR ) ) + { + Color aColor = pPoolItem->GetColorValue(); + m_xLbStartColor->SelectEntry( aColor ); + m_xLbEndColor->SelectEntry( aColor ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/custsdlg.cxx b/sd/source/ui/dlg/custsdlg.cxx new file mode 100644 index 000000000..bc421d7e4 --- /dev/null +++ b/sd/source/ui/dlg/custsdlg.cxx @@ -0,0 +1,478 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +// SdCustomShowDlg +SdCustomShowDlg::SdCustomShowDlg(weld::Window* pWindow, SdDrawDocument& rDrawDoc) + : GenericDialogController(pWindow, "modules/simpress/ui/customslideshows.ui", "CustomSlideShows") + , rDoc(rDrawDoc) + , pCustomShowList(nullptr) + , m_xLbCustomShows(m_xBuilder->weld_tree_view("customshowlist")) + , m_xBtnNew(m_xBuilder->weld_button("new")) + , m_xBtnEdit(m_xBuilder->weld_button("edit")) + , m_xBtnRemove(m_xBuilder->weld_button("delete")) + , m_xBtnCopy(m_xBuilder->weld_button("copy")) + , m_xBtnHelp(m_xBuilder->weld_button("help")) + , m_xBtnStartShow(m_xBuilder->weld_button("startshow")) + , m_xBtnOK(m_xBuilder->weld_button("ok")) +{ + m_xLbCustomShows->set_size_request(m_xLbCustomShows->get_approximate_digit_width() * 32, + m_xLbCustomShows->get_height_rows(8)); + + Link aLink( LINK( this, SdCustomShowDlg, ClickButtonHdl ) ); + m_xBtnNew->connect_clicked( aLink ); + m_xBtnEdit->connect_clicked( aLink ); + m_xBtnRemove->connect_clicked( aLink ); + m_xBtnCopy->connect_clicked( aLink ); + m_xLbCustomShows->connect_changed( LINK( this, SdCustomShowDlg, SelectListBoxHdl ) ); + + m_xBtnStartShow->connect_clicked( LINK( this, SdCustomShowDlg, StartShowHdl ) ); // for test + + // get CustomShow list of docs + pCustomShowList = rDoc.GetCustomShowList(); + if( pCustomShowList ) + { + tools::Long nPosToSelect = pCustomShowList->GetCurPos(); + // fill ListBox with CustomShows + for( SdCustomShow* pCustomShow = pCustomShowList->First(); + pCustomShow != nullptr; + pCustomShow = pCustomShowList->Next() ) + { + m_xLbCustomShows->append_text(pCustomShow->GetName()); + } + m_xLbCustomShows->select(nPosToSelect); + pCustomShowList->Seek( nPosToSelect ); + } + + CheckState(); +} + +SdCustomShowDlg::~SdCustomShowDlg() +{ +} + +void SdCustomShowDlg::CheckState() +{ + int nPos = m_xLbCustomShows->get_selected_index(); + + bool bEnable = nPos != -1; + m_xBtnEdit->set_sensitive( bEnable ); + m_xBtnRemove->set_sensitive( bEnable ); + m_xBtnCopy->set_sensitive( bEnable ); + m_xBtnStartShow->set_sensitive(bEnable); + + if (bEnable && pCustomShowList) + pCustomShowList->Seek( nPos ); +} + +IMPL_LINK( SdCustomShowDlg, ClickButtonHdl, weld::Button&, r, void ) +{ + SelectHdl(&r); +} + +IMPL_LINK( SdCustomShowDlg, SelectListBoxHdl, weld::TreeView&, rListBox, void ) +{ + SelectHdl(&rListBox); +} + +void SdCustomShowDlg::SelectHdl(void const *p) +{ + // new CustomShow + if (p == m_xBtnNew.get()) + { + std::unique_ptr pCustomShow; + SdDefineCustomShowDlg aDlg(m_xDialog.get(), rDoc, pCustomShow); + if (aDlg.run() == RET_OK) + { + if( pCustomShow ) + { + if( !pCustomShowList ) + pCustomShowList = rDoc.GetCustomShowList( true ); + + SdCustomShow* pCustomShowTmp = pCustomShow.get(); + pCustomShowList->push_back( std::move(pCustomShow) ); + pCustomShowList->Last(); + m_xLbCustomShows->append_text( pCustomShowTmp->GetName() ); + m_xLbCustomShows->select_text( pCustomShowTmp->GetName() ); + } + } + } + // edit CustomShow + else if( p == m_xBtnEdit.get() ) + { + int nPos = m_xLbCustomShows->get_selected_index(); + if (nPos != -1) + { + DBG_ASSERT( pCustomShowList, "pCustomShowList does not exist" ); + std::unique_ptr& pCustomShow = (*pCustomShowList)[ nPos ]; + SdDefineCustomShowDlg aDlg(m_xDialog.get(), rDoc, pCustomShow); + + if (aDlg.run() == RET_OK) + { + pCustomShowList->Seek(nPos); + m_xLbCustomShows->remove(nPos); + m_xLbCustomShows->insert_text(nPos, pCustomShow->GetName()); + m_xLbCustomShows->select(nPos); + } + } + } + // delete CustomShow + else if( p == m_xBtnRemove.get() ) + { + int nPos = m_xLbCustomShows->get_selected_index(); + if (nPos != -1) + { + pCustomShowList->erase( pCustomShowList->begin() + nPos ); + m_xLbCustomShows->remove(nPos); + m_xLbCustomShows->select(nPos == 0 ? nPos : nPos - 1); + } + } + // copy CustomShow + else if( p == m_xBtnCopy.get() ) + { + int nPos = m_xLbCustomShows->get_selected_index(); + if (nPos != -1) + { + std::unique_ptr pShow(new SdCustomShow( *(*pCustomShowList)[nPos] )); + OUString aStr( pShow->GetName() ); + OUString aStrCopy( SdResId( STR_COPY_CUSTOMSHOW ) ); + + sal_Int32 nStrPos = aStr.indexOf( aStrCopy ); + sal_Int32 nNum = 1; + if( nStrPos < 0 ) + { + aStr += " (" + aStrCopy + OUString::number( nNum ) + ")"; + nStrPos = aStr.indexOf( aStrCopy ); + } + nStrPos = nStrPos + aStrCopy.getLength(); + // that we do not access into the nirvana (--> endless loop) + if( nStrPos >= aStr.getLength() ) + { + aStr += " " + OUString::number( nNum ); + } + + // check name... + bool bDifferent = false; + //long nPosToSelect = pCustomShowList->GetCurPos(); + while( !bDifferent ) + { + bDifferent = true; + for( SdCustomShow* pCustomShow = pCustomShowList->First(); + pCustomShow != nullptr && bDifferent; + pCustomShow = pCustomShowList->Next() ) + { + if( aStr == pCustomShow->GetName() ) + bDifferent = false; + } + if( !bDifferent ) + { + // replace number by a number increased by 1 + + const CharClass* pCharClass = rDoc.GetCharClass(); + while( pCharClass->isDigit( aStr, nStrPos ) ) + aStr = aStr.replaceAt( nStrPos, 1, u"" ); + aStr = aStr.subView( 0, nStrPos) + OUString::number( ++nNum ) + aStr.subView( nStrPos); + } + + } + //pCustomShowList->Seek( nPosToSelect ); + pShow->SetName( aStr ); + + auto pShowTmp = pShow.get(); + pCustomShowList->push_back( std::move(pShow) ); + pCustomShowList->Last(); + m_xLbCustomShows->append_text(pShowTmp->GetName()); + m_xLbCustomShows->select_text(pShowTmp->GetName()); + } + } + else if( p == m_xLbCustomShows.get() ) + { + int nPos = m_xLbCustomShows->get_selected_index(); + if (nPos != -1) + pCustomShowList->Seek(nPos); + } + + CheckState(); +} + +// StartShow-Hdl +IMPL_LINK_NOARG(SdCustomShowDlg, StartShowHdl, weld::Button&, void) +{ + m_xDialog->response(RET_YES); +} + +// CheckState +bool SdCustomShowDlg::IsCustomShow() const +{ + if (!pCustomShowList->empty()) + return true; + else + return false; +} + +// SdDefineCustomShowDlg +SdDefineCustomShowDlg::SdDefineCustomShowDlg(weld::Window* pWindow, SdDrawDocument& rDrawDoc, std::unique_ptr& rpCS) + : GenericDialogController(pWindow, "modules/simpress/ui/definecustomslideshow.ui", "DefineCustomSlideShow") + , rDoc(rDrawDoc) + , rpCustomShow(rpCS) + , bModified(false) + , m_xEdtName(m_xBuilder->weld_entry("customname")) + , m_xLbPages(m_xBuilder->weld_tree_view("pages")) + , m_xBtnAdd(m_xBuilder->weld_button("add")) + , m_xBtnRemove(m_xBuilder->weld_button("remove")) + , m_xLbCustomPages(m_xBuilder->weld_tree_view("custompages")) + , m_xDropTargetHelper(new weld::ReorderingDropTarget(*m_xLbCustomPages)) + , m_xBtnOK(m_xBuilder->weld_button("ok")) + , m_xBtnCancel(m_xBuilder->weld_button("cancel")) + , m_xBtnHelp(m_xBuilder->weld_button("help")) +{ + Link aLink = LINK( this, SdDefineCustomShowDlg, ClickButtonHdl ); + m_xBtnAdd->connect_clicked( aLink ); + m_xBtnRemove->connect_clicked( aLink ); + m_xEdtName->connect_changed( LINK( this, SdDefineCustomShowDlg, ClickButtonEditHdl ) ); + m_xLbPages->connect_changed( LINK( this, SdDefineCustomShowDlg, ClickButtonHdl4 ) ); // because of status + m_xLbCustomPages->connect_changed( LINK( this, SdDefineCustomShowDlg, ClickButtonHdl3 ) ); // because of status + + m_xBtnOK->connect_clicked( LINK( this, SdDefineCustomShowDlg, OKHdl ) ); + + m_xLbPages->set_selection_mode(SelectionMode::Multiple); + + // shape 'em a bit + m_xLbPages->set_size_request(m_xLbPages->get_approximate_digit_width() * 24, m_xLbPages->get_height_rows(10)); + m_xLbCustomPages->set_size_request(m_xLbPages->get_approximate_digit_width() * 24, m_xLbCustomPages->get_height_rows(10)); + + // fill Listbox with page names of Docs + for( tools::Long nPage = 0; + nPage < rDoc.GetSdPageCount( PageKind::Standard ); + nPage++ ) + { + SdPage* pPage = rDoc.GetSdPage( static_cast(nPage), PageKind::Standard ); + m_xLbPages->append_text(pPage->GetName()); + } + // aLbPages.SelectEntryPos( 0 ); + + if( rpCustomShow ) + { + aOldName = rpCustomShow->GetName(); + m_xEdtName->set_text( aOldName ); + + // fill ListBox with CustomShow pages + for( const auto& rpPage : rpCustomShow->PagesVector() ) + { + m_xLbCustomPages->append(weld::toId(rpPage), rpPage->GetName(), ""); + } + } + else + { + rpCustomShow.reset(new SdCustomShow); + m_xEdtName->set_text( SdResId( STR_NEW_CUSTOMSHOW ) ); + m_xEdtName->select_region(0, -1); + rpCustomShow->SetName( m_xEdtName->get_text() ); + } + + m_xBtnOK->set_sensitive( false ); + CheckState(); +} + +SdDefineCustomShowDlg::~SdDefineCustomShowDlg() +{ +} + +// CheckState +void SdDefineCustomShowDlg::CheckState() +{ + bool bPages = m_xLbPages->count_selected_rows() > 0; + bool bCSPages = m_xLbCustomPages->get_selected_index() != -1; + bool bCount = m_xLbCustomPages->n_children() > 0; + + m_xBtnOK->set_sensitive( bCount ); + m_xBtnAdd->set_sensitive( bPages ); + m_xBtnRemove->set_sensitive( bCSPages ); +} + +IMPL_LINK( SdDefineCustomShowDlg, ClickButtonHdl, weld::Button&, rWidget, void ) +{ + ClickButtonHdl2(&rWidget); +} + +IMPL_LINK( SdDefineCustomShowDlg, ClickButtonHdl3, weld::TreeView&, rWidget, void ) +{ + ClickButtonHdl2(&rWidget); +} + +IMPL_LINK( SdDefineCustomShowDlg, ClickButtonHdl4, weld::TreeView&, rListBox, void ) +{ + ClickButtonHdl2(&rListBox); +} + +IMPL_LINK( SdDefineCustomShowDlg, ClickButtonEditHdl, weld::Entry&, rEdit, void ) +{ + ClickButtonHdl2(&rEdit); +} + +// ButtonHdl() +void SdDefineCustomShowDlg::ClickButtonHdl2(void const * p) +{ + if( p == m_xBtnAdd.get() ) + { + auto aRows = m_xLbPages->get_selected_rows(); + if (!aRows.empty()) + { + int nPosCP = m_xLbCustomPages->get_selected_index(); + if (nPosCP != -1) + ++nPosCP; + + for (auto i : aRows) + { + OUString aStr = m_xLbPages->get_text(i); + SdPage* pPage = rDoc.GetSdPage(i, PageKind::Standard); + OUString sId(weld::toId(pPage)); + m_xLbCustomPages->insert(nPosCP, aStr, &sId, nullptr, nullptr); + m_xLbCustomPages->select(nPosCP != -1 ? nPosCP : m_xLbCustomPages->n_children() - 1); + + if (nPosCP != -1) + ++nPosCP; + } + bModified = true; + } + } + else if (p == m_xBtnRemove.get()) + { + int nPos = m_xLbCustomPages->get_selected_index(); + if (nPos != -1) + { + m_xLbCustomPages->remove(nPos); + m_xLbCustomPages->select(nPos == 0 ? nPos : nPos - 1); + bModified = true; + } + } + else if( p == m_xEdtName.get() ) + { + bModified = true; + } + + CheckState(); +} + +/** + * Checks the page pointer of the Show since entries can be moved and copied + * by TreeLB. + */ +void SdDefineCustomShowDlg::CheckCustomShow() +{ + bool bDifferent = false; + + // compare count + size_t nCount = m_xLbCustomPages->n_children(); + if (rpCustomShow->PagesVector().size() != nCount) + { + rpCustomShow->PagesVector().clear(); + bDifferent = true; + } + + // compare page pointer + if( !bDifferent ) + { + size_t i = 0; + for (const auto& rpPage : rpCustomShow->PagesVector()) + { + SdPage* pPage = weld::fromId(m_xLbCustomPages->get_id(i)); + if (rpPage != pPage) + { + rpCustomShow->PagesVector().clear(); + bDifferent = true; + break; + } + + ++i; + } + } + + // set new page pointer + if( bDifferent ) + { + for (size_t i = 0; i < nCount; ++i) + { + SdPage* pPage = weld::fromId(m_xLbCustomPages->get_id(i)); + rpCustomShow->PagesVector().push_back(pPage); + } + bModified = true; + } + + // compare name and set name if necessary + OUString aStr( m_xEdtName->get_text() ); + if( rpCustomShow->GetName() != aStr ) + { + rpCustomShow->SetName( aStr ); + bModified = true; + } +} + +// OK-Hdl +IMPL_LINK_NOARG(SdDefineCustomShowDlg, OKHdl, weld::Button&, void) +{ + // check name... + bool bDifferent = true; + SdCustomShowList* pCustomShowList = rDoc.GetCustomShowList(); + if( pCustomShowList ) + { + OUString aName( m_xEdtName->get_text() ); + SdCustomShow* pCustomShow; + + tools::Long nPosToSelect = pCustomShowList->GetCurPos(); + for( pCustomShow = pCustomShowList->First(); + pCustomShow != nullptr; + pCustomShow = pCustomShowList->Next() ) + { + if( aName == pCustomShow->GetName() && aName != aOldName ) + bDifferent = false; + } + pCustomShowList->Seek( nPosToSelect ); + } + + if( bDifferent ) + { + CheckCustomShow(); + + m_xDialog->response(RET_OK); + } + else + { + std::unique_ptr xWarn(Application::CreateMessageDialog(m_xDialog.get(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + m_xEdtName->grab_focus(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/diactrl.cxx b/sd/source/ui/dlg/diactrl.cxx new file mode 100644 index 000000000..233550809 --- /dev/null +++ b/sd/source/ui/dlg/diactrl.cxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +#include +#include + +using namespace ::com::sun::star; + +SFX_IMPL_TOOLBOX_CONTROL( SdTbxCtlDiaPages, SfxUInt16Item ) + +namespace +{ + OUString format_number(int nSlides) + { + OUString aSlides(SdResId(STR_SLIDES, nSlides)); + return aSlides.replaceFirst("%1", OUString::number(nSlides)); + } +} + +// SdPagesField +SdPagesField::SdPagesField( vcl::Window* pParent, + const uno::Reference< frame::XFrame >& rFrame ) + : InterimItemWindow(pParent, "modules/simpress/ui/pagesfieldbox.ui", "PagesFieldBox") + , m_xWidget(m_xBuilder->weld_spin_button("pagesfield")) + , m_xFrame(rFrame) +{ + InitControlBase(m_xWidget.get()); + + // set parameter of MetricFields + m_xWidget->set_digits(0); + m_xWidget->set_range(1, 15); + m_xWidget->set_increments(1, 5); + m_xWidget->connect_value_changed(LINK(this, SdPagesField, ModifyHdl)); + m_xWidget->connect_output(LINK(this, SdPagesField, OutputHdl)); + m_xWidget->connect_input(LINK(this, SdPagesField, spin_button_input)); + m_xWidget->connect_key_press(LINK(this, SdPagesField, KeyInputHdl)); + + auto width = std::max(m_xWidget->get_pixel_size(format_number(1)).Width(), + m_xWidget->get_pixel_size(format_number(15)).Width()); + int chars = ceil(width / m_xWidget->get_approximate_digit_width()); + m_xWidget->set_width_chars(chars); + + SetSizePixel(m_xWidget->get_preferred_size()); +} + +IMPL_LINK(SdPagesField, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +void SdPagesField::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +SdPagesField::~SdPagesField() +{ + disposeOnce(); +} + +void SdPagesField::set_sensitive(bool bSensitive) +{ + Enable(bSensitive); + m_xWidget->set_sensitive(bSensitive); + if (!bSensitive) + m_xWidget->set_text(""); +} + +void SdPagesField::UpdatePagesField( const SfxUInt16Item* pItem ) +{ + if (pItem) + m_xWidget->set_value(pItem->GetValue()); + else + m_xWidget->set_text(OUString()); +} + +IMPL_STATIC_LINK(SdPagesField, OutputHdl, weld::SpinButton&, rSpinButton, void) +{ + rSpinButton.set_text(format_number(rSpinButton.get_value())); +} + +IMPL_LINK(SdPagesField, spin_button_input, int*, result, bool) +{ + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); + double fResult(0.0); + bool bRet = vcl::TextToValue(m_xWidget->get_text(), fResult, 0, m_xWidget->get_digits(), rLocaleData, FieldUnit::NONE); + if (bRet) + { + if (fResult > SAL_MAX_INT32) + fResult = SAL_MAX_INT32; + else if (fResult < SAL_MIN_INT32) + fResult = SAL_MIN_INT32; + *result = fResult; + } + return bRet; +} + +IMPL_LINK_NOARG(SdPagesField, ModifyHdl, weld::SpinButton&, void) +{ + SfxUInt16Item aItem(SID_PAGES_PER_ROW, m_xWidget->get_value()); + + uno::Any a; + aItem.QueryValue( a ); + uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue("PagesPerRow", a) }; + SfxToolBoxControl::Dispatch( ::uno::Reference< ::frame::XDispatchProvider >( m_xFrame->getController(), ::uno::UNO_QUERY ), + ".uno:PagesPerRow", + aArgs ); +} + +SdTbxCtlDiaPages::SdTbxCtlDiaPages( sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{ +} + +SdTbxCtlDiaPages::~SdTbxCtlDiaPages() +{ +} + +void SdTbxCtlDiaPages::StateChangedAtToolBoxControl( sal_uInt16, + SfxItemState eState, const SfxPoolItem* pState ) +{ + SdPagesField* pFld = static_cast( GetToolBox().GetItemWindow( GetId() ) ); + DBG_ASSERT( pFld, "Window not found" ); + + if ( eState == SfxItemState::DISABLED ) + { + pFld->set_sensitive(false); + } + else + { + pFld->set_sensitive(true); + + const SfxUInt16Item* pItem = nullptr; + if ( eState == SfxItemState::DEFAULT ) + { + pItem = dynamic_cast< const SfxUInt16Item* >( pState ); + DBG_ASSERT( pItem, "sd::SdTbxCtlDiaPages::StateChanged(), wrong item type!" ); + } + + pFld->UpdatePagesField( pItem ); + } +} + +VclPtr SdTbxCtlDiaPages::CreateItemWindow( vcl::Window* pParent ) +{ + VclPtr pWindow = VclPtr::Create(pParent, m_xFrame); + pWindow->Show(); + + return pWindow; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgchar.cxx b/sd/source/ui/dlg/dlgchar.cxx new file mode 100644 index 000000000..df1b24179 --- /dev/null +++ b/sd/source/ui/dlg/dlgchar.cxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include + +/** + * Constructor of tab dialog: append pages to dialog + */ +SdCharDlg::SdCharDlg(weld::Window* pParent, const SfxItemSet* pAttr, + const SfxObjectShell* pDocShell) + : SfxTabDialogController(pParent, "modules/sdraw/ui/drawchardialog.ui", + "DrawCharDialog", pAttr) + , rDocShell(*pDocShell) +{ + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + + AddTabPage("RID_SVXPAGE_CHAR_NAME", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_NAME), nullptr); + AddTabPage("RID_SVXPAGE_CHAR_EFFECTS", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_EFFECTS), nullptr); + AddTabPage("RID_SVXPAGE_CHAR_POSITION", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_CHAR_POSITION), nullptr); + AddTabPage("RID_SVXPAGE_BKG", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_BKG), nullptr); +} + +void SdCharDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "RID_SVXPAGE_CHAR_NAME") + { + SvxFontListItem aItem(* static_cast( rDocShell.GetItem( SID_ATTR_CHAR_FONTLIST) ) ); + + aSet.Put (SvxFontListItem( aItem.GetFontList(), SID_ATTR_CHAR_FONTLIST)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_CHAR_EFFECTS") + { + // Opt in for character transparency. + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE, SVX_ENABLE_CHAR_TRANSPARENCY)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_BKG") + { + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE,static_cast(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR))); + rPage.PageCreated(aSet); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgfield.cxx b/sd/source/ui/dlg/dlgfield.cxx new file mode 100644 index 000000000..75263a17c --- /dev/null +++ b/sd/source/ui/dlg/dlgfield.cxx @@ -0,0 +1,301 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/** + * dialog to edit field commands + */ +SdModifyFieldDlg::SdModifyFieldDlg(weld::Window* pWindow, const SvxFieldData* pInField, const SfxItemSet& rSet) + : GenericDialogController(pWindow, "modules/simpress/ui/dlgfield.ui", "EditFieldsDialog") + , m_aInputSet(rSet) + , m_pField(pInField) + , m_xRbtFix(m_xBuilder->weld_radio_button("fixedRB")) + , m_xRbtVar(m_xBuilder->weld_radio_button("varRB")) + , m_xLbLanguage(new SvxLanguageBox(m_xBuilder->weld_combo_box("languageLB"))) + , m_xLbFormat(m_xBuilder->weld_combo_box("formatLB")) +{ + m_xLbLanguage->SetLanguageList( SvxLanguageListFlags::ALL|SvxLanguageListFlags::ONLY_KNOWN, false ); + m_xLbLanguage->connect_changed(LINK(this, SdModifyFieldDlg, LanguageChangeHdl)); + FillControls(); +} + +SdModifyFieldDlg::~SdModifyFieldDlg() +{ +} + +/** + * Returns the new field, owned by caller. + * Returns NULL if nothing has changed. + */ +SvxFieldData* SdModifyFieldDlg::GetField() +{ + SvxFieldData* pNewField = nullptr; + + if( m_xRbtFix->get_state_changed_from_saved() || + m_xRbtVar->get_state_changed_from_saved() || + m_xLbFormat->get_value_changed_from_saved() ) + { + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + SvxDateType eType; + SvxDateFormat eFormat; + + if( m_xRbtFix->get_active() ) + eType = SvxDateType::Fix; + else + eType = SvxDateType::Var; + + eFormat = static_cast( m_xLbFormat->get_active() + 2 ); + + pNewField = new SvxDateField( *pDateField ); + static_cast( pNewField )->SetType( eType ); + static_cast( pNewField )->SetFormat( eFormat ); + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + SvxTimeType eType; + SvxTimeFormat eFormat; + + if( m_xRbtFix->get_active() ) + eType = SvxTimeType::Fix; + else + eType = SvxTimeType::Var; + + eFormat = static_cast( m_xLbFormat->get_active() + 2 ); + + pNewField = new SvxExtTimeField( *pTimeField ); + static_cast( pNewField )->SetType( eType ); + static_cast( pNewField )->SetFormat( eFormat ); + } + else if( dynamic_cast< const SvxExtFileField *>( m_pField ) != nullptr ) + { + SvxFileType eType; + SvxFileFormat eFormat; + + if( m_xRbtFix->get_active() ) + eType = SvxFileType::Fix; + else + eType = SvxFileType::Var; + + eFormat = static_cast( m_xLbFormat->get_active() ); + + ::sd::DrawDocShell* pDocSh = dynamic_cast< ::sd::DrawDocShell* >(SfxObjectShell::Current() ); + + if( pDocSh ) + { + OUString aName; + if( pDocSh->HasName() ) + aName = pDocSh->GetMedium()->GetName(); + + // Get current filename, not the one stored in the old field + pNewField = new SvxExtFileField( aName ); + static_cast( pNewField )->SetType( eType ); + static_cast( pNewField )->SetFormat( eFormat ); + } + } + else if( dynamic_cast< const SvxAuthorField *>( m_pField ) != nullptr ) + { + SvxAuthorType eType; + SvxAuthorFormat eFormat; + + if( m_xRbtFix->get_active() ) + eType = SvxAuthorType::Fix; + else + eType = SvxAuthorType::Var; + + eFormat = static_cast( m_xLbFormat->get_active() ); + + // Get current state of address, not the old one + SvtUserOptions aUserOptions; + pNewField = new SvxAuthorField( aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID() ); + static_cast( pNewField )->SetType( eType ); + static_cast( pNewField )->SetFormat( eFormat ); + } + } + + return pNewField; +} + +void SdModifyFieldDlg::FillFormatList() +{ + LanguageType eLangType = m_xLbLanguage->get_active_id(); + + m_xLbFormat->clear(); + + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + SvxDateField aDateField( *pDateField ); + + //SvxDateFormat::AppDefault, // not used + //SvxDateFormat::System, // not used + m_xLbFormat->append_text( SdResId( STR_STANDARD_SMALL ) ); + m_xLbFormat->append_text( SdResId( STR_STANDARD_BIG ) ); + + SvNumberFormatter* pNumberFormatter = SD_MOD()->GetNumberFormatter(); + aDateField.SetFormat( SvxDateFormat::A ); // 13.02.96 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::B ); // 13.02.1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::C ); // 13.Feb 1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::D ); // 13.Februar 1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::E ); // Die, 13.Februar 1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + aDateField.SetFormat( SvxDateFormat::F ); // Dienstag, 13.Februar 1996 + m_xLbFormat->append_text( aDateField.GetFormatted( *pNumberFormatter, eLangType ) ); + + m_xLbFormat->set_active( static_cast(pDateField->GetFormat()) - 2 ); + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + SvxExtTimeField aTimeField( *pTimeField ); + + //SvxTimeFormat::AppDefault, // not used + //SvxTimeFormat::System, // not used + m_xLbFormat->append_text( SdResId( STR_STANDARD_NORMAL ) ); + + SvNumberFormatter* pNumberFormatter = SD_MOD()->GetNumberFormatter(); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM ); // 13:49 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM_SS ); // 13:49:38 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH24_MM_SS_00 ); // 13:49:38.78 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM ); // 01:49 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM_SS ); // 01:49:38 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + aTimeField.SetFormat( SvxTimeFormat::HH12_MM_SS_00 ); // 01:49:38.78 + m_xLbFormat->append_text( aTimeField.GetFormatted( *pNumberFormatter, eLangType ) ); + //SvxTimeFormat::HH12_MM_AMPM, // 01:49 PM + //SvxTimeFormat::HH12_MM_SS_AMPM, // 01:49:38 PM + //SvxTimeFormat::HH12_MM_SS_00_AMPM // 01:49:38.78 PM + + m_xLbFormat->set_active( static_cast(pTimeField->GetFormat()) - 2 ); + } + else if( auto pFileField = dynamic_cast< const SvxExtFileField *>( m_pField ) ) + { + m_xLbFormat->append_text( SdResId( STR_FILEFORMAT_NAME_EXT ) ); + m_xLbFormat->append_text( SdResId( STR_FILEFORMAT_FULLPATH ) ); + m_xLbFormat->append_text( SdResId( STR_FILEFORMAT_PATH ) ); + m_xLbFormat->append_text( SdResId( STR_FILEFORMAT_NAME ) ); + + m_xLbFormat->set_active( static_cast( pFileField->GetFormat() ) ); + } + else if( auto pAuthorField = dynamic_cast< const SvxAuthorField *>( m_pField ) ) + { + SvxAuthorField aAuthorField( *pAuthorField ); + + for( sal_uInt16 i = 0; i < 4; i++ ) + { + aAuthorField.SetFormat( static_cast(i) ); + m_xLbFormat->append_text( aAuthorField.GetFormatted() ); + } + + m_xLbFormat->set_active( static_cast( pAuthorField->GetFormat() ) ); + + } + +} + +void SdModifyFieldDlg::FillControls() +{ + m_xLbFormat->clear(); + + if( auto pDateField = dynamic_cast< const SvxDateField *>( m_pField ) ) + { + if( pDateField->GetType() == SvxDateType::Fix ) + m_xRbtFix->set_active(true); + else + m_xRbtVar->set_active(true); + } + else if( auto pTimeField = dynamic_cast< const SvxExtTimeField *>( m_pField ) ) + { + if( pTimeField->GetType() == SvxTimeType::Fix ) + m_xRbtFix->set_active(true); + else + m_xRbtVar->set_active(true); + } + else if( auto pFileField = dynamic_cast< const SvxExtFileField *>( m_pField ) ) + { + if( pFileField->GetType() == SvxFileType::Fix ) + m_xRbtFix->set_active(true); + else + m_xRbtVar->set_active(true); + } + else if( auto pAuthorField = dynamic_cast< const SvxAuthorField *>( m_pField ) ) + { + if( pAuthorField->GetType() == SvxAuthorType::Fix ) + m_xRbtFix->set_active(true); + else + m_xRbtVar->set_active(true); + } + m_xRbtFix->save_state(); + m_xRbtVar->save_state(); + + if( const SvxLanguageItem* pItem = m_aInputSet.GetItemIfSet(EE_CHAR_LANGUAGE ) ) + m_xLbLanguage->set_active_id(pItem->GetLanguage()); + + m_xLbLanguage->save_active_id(); + + FillFormatList(); + m_xLbFormat->save_value(); +} + +IMPL_LINK_NOARG(SdModifyFieldDlg, LanguageChangeHdl, weld::ComboBox&, void) +{ + FillFormatList(); +} + +SfxItemSet SdModifyFieldDlg::GetItemSet() const +{ + SfxItemSet aOutput( *m_aInputSet.GetPool(), svl::Items ); + + if (m_xLbLanguage->get_active_id_changed_from_saved()) + { + LanguageType eLangType = m_xLbLanguage->get_active_id(); + SvxLanguageItem aItem( eLangType, EE_CHAR_LANGUAGE ); + aOutput.Put( aItem ); + + SvxLanguageItem aItemCJK( eLangType, EE_CHAR_LANGUAGE_CJK ); + aOutput.Put( aItemCJK ); + + SvxLanguageItem aItemCTL( eLangType, EE_CHAR_LANGUAGE_CTL ); + aOutput.Put( aItemCTL ); + } + + return aOutput; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgolbul.cxx b/sd/source/ui/dlg/dlgolbul.cxx new file mode 100644 index 000000000..41c00efa8 --- /dev/null +++ b/sd/source/ui/dlg/dlgolbul.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sd { + +/** + * Constructor of tab dialog: append pages to the dialog + */ +OutlineBulletDlg::OutlineBulletDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) + : SfxTabDialogController(pParent, "modules/sdraw/ui/bulletsandnumbering.ui", "BulletsAndNumberingDialog") + , m_aInputSet(*pAttr) + , m_bTitle(false) + , m_pSdView(pView) +{ + m_aInputSet.MergeRange(SID_PARAM_NUM_PRESET, SID_PARAM_CUR_NUM_LEVEL); + m_aInputSet.Put(*pAttr); + + m_xOutputSet.reset( new SfxItemSet( *pAttr ) ); + m_xOutputSet->ClearItem(); + + bool bOutliner = false; + + // special treatment if a title object is selected + if (pView) + { + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + for(size_t nNum = 0; nNum < nCount; ++nNum) + { + SdrObject* pObj = rMarkList.GetMark(nNum)->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + switch(pObj->GetObjIdentifier()) + { + case SdrObjKind::TitleText: + m_bTitle = true; + break; + case SdrObjKind::OutlineText: + bOutliner = true; + break; + default: + break; + } + } + } + } + + if( SfxItemState::SET != m_aInputSet.GetItemState(EE_PARA_NUMBULLET)) + { + const SvxNumBulletItem *pItem = nullptr; + if(bOutliner) + { + SfxStyleSheetBasePool* pSSPool = pView->GetDocSh()->GetStyleSheetPool(); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( STR_LAYOUT_OUTLINE + " 1", SfxStyleFamily::Pseudo); + if( pFirstStyleSheet ) + pItem = pFirstStyleSheet->GetItemSet().GetItemIfSet(EE_PARA_NUMBULLET, false); + } + + if( pItem == nullptr ) + pItem = m_aInputSet.GetPool()->GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET); + + DBG_ASSERT( pItem, "No EE_PARA_NUMBULLET in Pool! [CL]" ); + + m_aInputSet.Put(pItem->CloneSetWhich(EE_PARA_NUMBULLET)); + } + + if (m_bTitle && m_aInputSet.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET ) + { + const SvxNumBulletItem* pItem = m_aInputSet.GetItem(EE_PARA_NUMBULLET); + const SvxNumRule& rRule = pItem->GetNumRule(); + SvxNumRule aNewRule( rRule ); + aNewRule.SetFeatureFlag( SvxNumRuleFlags::NO_NUMBERS ); + + SvxNumBulletItem aNewItem( std::move(aNewRule), EE_PARA_NUMBULLET ); + m_aInputSet.Put(aNewItem); + } + + SetInputSet(&m_aInputSet); + + if (m_bTitle) + RemoveTabPage("singlenum"); + + AddTabPage("customize", RID_SVXPAGE_NUM_OPTIONS); + AddTabPage("position", RID_SVXPAGE_NUM_POSITION); +} + +OutlineBulletDlg::~OutlineBulletDlg() +{ +} + +void OutlineBulletDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + if (!m_pSdView) + return; + if (rId == "customize") + { + FieldUnit eMetric = m_pSdView->GetDoc().GetUIUnit(); + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put ( SfxUInt16Item(SID_METRIC_ITEM,static_cast(eMetric))); + rPage.PageCreated(aSet); + } + else if (rId == "position") + { + FieldUnit eMetric = m_pSdView->GetDoc().GetUIUnit(); + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + aSet.Put ( SfxUInt16Item(SID_METRIC_ITEM,static_cast(eMetric))); + rPage.PageCreated(aSet); + } +} + +const SfxItemSet* OutlineBulletDlg::GetBulletOutputItemSet() const +{ + SfxItemSet aSet(*GetOutputItemSet()); + m_xOutputSet->Put(aSet); + + const SfxPoolItem *pItem = nullptr; + if( SfxItemState::SET == m_xOutputSet->GetItemState(m_xOutputSet->GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE), false, &pItem )) + { + SdBulletMapper::MapFontsInNumRule(const_cast(static_cast(pItem)->GetNumRule()), *m_xOutputSet); + // #i35937 - removed EE_PARA_BULLETSTATE setting + } + + if (m_bTitle && m_xOutputSet->GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET) + { + const SvxNumBulletItem* pBulletItem = m_xOutputSet->GetItem(EE_PARA_NUMBULLET); + SvxNumRule& rRule = const_cast(pBulletItem->GetNumRule()); + rRule.SetFeatureFlag( SvxNumRuleFlags::NO_NUMBERS, false ); + } + + return m_xOutputSet.get(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgpage.cxx b/sd/source/ui/dlg/dlgpage.cxx new file mode 100644 index 000000000..e3bc5978b --- /dev/null +++ b/sd/source/ui/dlg/dlgpage.cxx @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/** + * Constructor of tab dialog: appends pages to the dialog + */ +SdPageDlg::SdPageDlg(SfxObjectShell const* pDocSh, weld::Window* pParent, const SfxItemSet* pAttr, + bool bAreaPage, bool bIsImpressDoc, bool bIsImpressMaster) + : SfxTabDialogController(pParent, "modules/sdraw/ui/drawpagedialog.ui", "DrawPageDialog", pAttr) + , mbIsImpressDoc(bIsImpressDoc) +{ + SvxColorListItem const* pColorListItem = pDocSh->GetItem(SID_COLOR_TABLE); + SvxGradientListItem const* pGradientListItem = pDocSh->GetItem(SID_GRADIENT_LIST); + SvxBitmapListItem const* pBitmapListItem = pDocSh->GetItem(SID_BITMAP_LIST); + SvxPatternListItem const* pPatternListItem = pDocSh->GetItem(SID_PATTERN_LIST); + SvxHatchListItem const* pHatchListItem = pDocSh->GetItem(SID_HATCH_LIST); + + mpColorList = pColorListItem->GetColorList(); + mpGradientList = pGradientListItem->GetGradientList(); + mpHatchingList = pHatchListItem->GetHatchList(); + mpBitmapList = pBitmapListItem->GetBitmapList(); + mpPatternList = pPatternListItem->GetPatternList(); + + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + + AddTabPage("RID_SVXPAGE_PAGE", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_PAGE), nullptr); + AddTabPage("RID_SVXPAGE_AREA", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_AREA), nullptr); + AddTabPage("RID_SVXPAGE_TRANSPARENCE", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_TRANSPARENCE), + nullptr); + AddTabPage("RID_SVXPAGE_THEME", pFact->GetTabPageCreatorFunc(RID_SVXPAGE_THEME), nullptr); + + if (!bAreaPage) // I have to add the page before I remove it ! + { + RemoveTabPage("RID_SVXPAGE_AREA"); + RemoveTabPage("RID_SVXPAGE_TRANSPARENCE"); + } + + if (!bIsImpressMaster) + { + // Only slide masters can have a theme. + RemoveTabPage("RID_SVXPAGE_THEME"); + } + + if (mbIsImpressDoc) + { + set_title(SdResId(STR_SLIDE_SETUP_TITLE)); + m_xTabCtrl->set_tab_label_text("RID_SVXPAGE_PAGE", SdResId(STR_SLIDE_NAME)); + } +} + +void SdPageDlg::PageCreated(const OString& rId, SfxTabPage& rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "RID_SVXPAGE_PAGE") + { + aSet.Put(SfxUInt16Item(sal_uInt16(SID_ENUM_PAGE_MODE), SVX_PAGE_MODE_PRESENTATION)); + aSet.Put(SfxUInt16Item(SID_PAPER_START, PAPER_A0)); + aSet.Put(SfxUInt16Item(SID_PAPER_END, PAPER_E)); + + if (mbIsImpressDoc) + aSet.Put(SfxBoolItem(SID_IMPRESS_DOC, true)); + + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_AREA") + { + aSet.Put(SvxColorListItem(mpColorList, SID_COLOR_TABLE)); + aSet.Put(SvxGradientListItem(mpGradientList, SID_GRADIENT_LIST)); + aSet.Put(SvxHatchListItem(mpHatchingList, SID_HATCH_LIST)); + aSet.Put(SvxBitmapListItem(mpBitmapList, SID_BITMAP_LIST)); + aSet.Put(SvxPatternListItem(mpPatternList, SID_PATTERN_LIST)); + aSet.Put(SfxUInt16Item(SID_PAGE_TYPE, 0)); + aSet.Put(SfxUInt16Item(SID_DLG_TYPE, 1)); + aSet.Put(SfxUInt16Item(SID_TABPAGE_POS, 0)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_TRANSPARENCE") + { + aSet.Put(SfxUInt16Item(SID_PAGE_TYPE, 0)); + aSet.Put(SfxUInt16Item(SID_DLG_TYPE, 1)); + rPage.PageCreated(aSet); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/dlgsnap.cxx b/sd/source/ui/dlg/dlgsnap.cxx new file mode 100644 index 000000000..9b1383089 --- /dev/null +++ b/sd/source/ui/dlg/dlgsnap.cxx @@ -0,0 +1,185 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/** + * dialog to adjust grid (scarcely ESO!) + */ +SdSnapLineDlg::SdSnapLineDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, ::sd::View const * pView) + : GenericDialogController(pWindow, "modules/sdraw/ui/dlgsnap.ui", "SnapObjectDialog") + , aUIScale(pView->GetDoc().GetUIScale()) + , m_xFtX(m_xBuilder->weld_label("xlabel")) + , m_xMtrFldX(m_xBuilder->weld_metric_spin_button("x", FieldUnit::CM)) + , m_xFtY(m_xBuilder->weld_label("ylabel")) + , m_xMtrFldY(m_xBuilder->weld_metric_spin_button("y", FieldUnit::CM)) + , m_xRadioGroup(m_xBuilder->weld_widget("radiogroup")) + , m_xRbPoint(m_xBuilder->weld_radio_button("point")) + , m_xRbVert(m_xBuilder->weld_radio_button("vert")) + , m_xRbHorz(m_xBuilder->weld_radio_button("horz")) + , m_xBtnDelete(m_xBuilder->weld_button("delete")) +{ + m_xRbHorz->connect_toggled(LINK(this, SdSnapLineDlg, ToggleHdl)); + m_xRbVert->connect_toggled(LINK(this, SdSnapLineDlg, ToggleHdl)); + m_xRbPoint->connect_toggled(LINK(this, SdSnapLineDlg, ToggleHdl)); + + m_xBtnDelete->connect_clicked(LINK(this, SdSnapLineDlg, ClickHdl)); + + FieldUnit eUIUnit = pView->GetDoc().GetUIUnit(); + SetFieldUnit(*m_xMtrFldX, eUIUnit, true); + SetFieldUnit(*m_xMtrFldY, eUIUnit, true); + + // get WorkArea + ::tools::Rectangle aWorkArea = pView->GetWorkArea(); + + // determine PoolUnit + SfxItemPool* pPool = rInAttrs.GetPool(); + DBG_ASSERT( pPool, "Where's the Pool?" ); + MapUnit ePoolUnit = pPool->GetMetric( SID_ATTR_FILL_HATCH ); + + // #i48497# Consider page origin + SdrPageView* pPV = pView->GetSdrPageView(); + Point aLeftTop(aWorkArea.Left()+1, aWorkArea.Top()+1); + pPV->LogicToPagePos(aLeftTop); + Point aRightBottom(aWorkArea.Right()-2, aWorkArea.Bottom()-2); + pPV->LogicToPagePos(aRightBottom); + + // determine max and min values depending on + // WorkArea, PoolUnit and FieldUnit: + auto const map = [ePoolUnit](std::unique_ptr const & msb, tools::Long value) { + auto const n1 = OutputDevice::LogicToLogic(value, ePoolUnit, MapUnit::Map100thMM); + auto const n2 = msb->normalize(n1); + auto const n3 = msb->convert_value_from(n2, FieldUnit::MM_100TH); + auto const n4 = msb->convert_value_to(n3, FieldUnit::NONE); + return n4; + }; + m_xMtrFldX->set_range(map(m_xMtrFldX, sal_Int32(aLeftTop.X() / aUIScale)), + map(m_xMtrFldX, sal_Int32(aRightBottom.X() / aUIScale)), + FieldUnit::NONE); + m_xMtrFldY->set_range(map(m_xMtrFldY, sal_Int32(aLeftTop.Y() / aUIScale)), + map(m_xMtrFldY, sal_Int32(aRightBottom.Y() / aUIScale)), + FieldUnit::NONE); + + // set values + nXValue = static_cast( rInAttrs.Get(ATTR_SNAPLINE_X)).GetValue(); + nYValue = static_cast( rInAttrs.Get(ATTR_SNAPLINE_Y)).GetValue(); + nXValue = sal_Int32(nXValue / aUIScale); + nYValue = sal_Int32(nYValue / aUIScale); + SetMetricValue(*m_xMtrFldX, nXValue, MapUnit::Map100thMM); + SetMetricValue(*m_xMtrFldY, nYValue, MapUnit::Map100thMM); + + m_xRbPoint->set_active(true); +} + +SdSnapLineDlg::~SdSnapLineDlg() +{ +} + +/** + * fills provided item sets with dialog box attributes + */ +IMPL_LINK(SdSnapLineDlg, ToggleHdl, weld::Toggleable&, rBtn, void) +{ + if (!rBtn.get_active()) + return; + if (m_xRbPoint->get_active()) + SetInputFields(true, true); + else if (m_xRbHorz->get_active()) + SetInputFields(false, true); + else if (m_xRbVert->get_active()) + SetInputFields(true, false); +} + +IMPL_LINK( SdSnapLineDlg, ClickHdl, weld::Button&, rBtn, void ) +{ + if (&rBtn == m_xBtnDelete.get()) + m_xDialog->response(RET_SNAP_DELETE); +} + +/** + * fills provided item sets with dialog box attributes + */ +void SdSnapLineDlg::GetAttr(SfxItemSet& rOutAttrs) +{ + SnapKind eKind; + + if (m_xRbHorz->get_active()) eKind = SnapKind::Horizontal; + else if (m_xRbVert->get_active()) eKind = SnapKind::Vertical; + else eKind = SnapKind::Point; + + nXValue = sal_Int32(GetCoreValue(*m_xMtrFldX, MapUnit::Map100thMM) * aUIScale); + nYValue = sal_Int32(GetCoreValue(*m_xMtrFldY, MapUnit::Map100thMM) * aUIScale); + + rOutAttrs.Put(SfxUInt16Item(ATTR_SNAPLINE_KIND, static_cast(eKind))); + rOutAttrs.Put(SfxInt32Item(ATTR_SNAPLINE_X, nXValue)); + rOutAttrs.Put(SfxInt32Item(ATTR_SNAPLINE_Y, nYValue)); +} + +void SdSnapLineDlg::HideRadioGroup() +{ + m_xRadioGroup->hide(); +} + +/** + * disable X or Y input fields + */ +void SdSnapLineDlg::SetInputFields(bool bEnableX, bool bEnableY) +{ + if ( bEnableX ) + { + if (!m_xMtrFldX->get_sensitive()) + m_xMtrFldX->set_value(nXValue, FieldUnit::NONE); + m_xMtrFldX->set_sensitive(true); + m_xFtX->set_sensitive(true); + } + else if (m_xMtrFldX->get_sensitive()) + { + nXValue = m_xMtrFldX->get_value(FieldUnit::NONE); + m_xMtrFldX->set_text(OUString()); + m_xMtrFldX->set_sensitive(false); + m_xFtX->set_sensitive(false); + } + if ( bEnableY ) + { + if (!m_xMtrFldY->get_sensitive()) + m_xMtrFldY->set_value(nYValue, FieldUnit::NONE); + m_xMtrFldY->set_sensitive(true); + m_xFtY->set_sensitive(true); + } + else if (m_xMtrFldY->get_sensitive()) + { + nYValue = m_xMtrFldY->get_value(FieldUnit::NONE); + m_xMtrFldY->set_text(OUString()); + m_xMtrFldY->set_sensitive(false); + m_xFtY->set_sensitive(false); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/filedlg.cxx b/sd/source/ui/dlg/filedlg.cxx new file mode 100644 index 000000000..b2087c408 --- /dev/null +++ b/sd/source/ui/dlg/filedlg.cxx @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// ----------- SdFileDialog_Imp --------------------------- + +class SdFileDialog_Imp : public sfx2::FileDialogHelper +{ +private: + friend class SdOpenSoundFileDialog; + + css::uno::Reference< css::ui::dialogs::XFilePickerControlAccess > mxControlAccess; + + css::uno::Reference< css::media::XPlayer > mxPlayer; + ImplSVEvent * mnPlaySoundEvent; + bool mbLabelPlaying; + Idle maUpdateIdle; + + DECL_LINK( PlayMusicHdl, void *, void ); + DECL_LINK( IsMusicStoppedHdl, Timer *, void ); + +public: + explicit SdFileDialog_Imp(weld::Window *pParent); + virtual ~SdFileDialog_Imp() override; + + // overwritten from FileDialogHelper, to receive user feedback + virtual void ControlStateChanged( const css::ui::dialogs::FilePickerEvent& aEvent ) override; +}; + +void SdFileDialog_Imp::ControlStateChanged( const css::ui::dialogs::FilePickerEvent& aEvent ) +{ + SolarMutexGuard aGuard; + + switch( aEvent.ElementId ) + { + case css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY: + if( mxControlAccess.is() ) + { + if( mnPlaySoundEvent ) + Application::RemoveUserEvent( mnPlaySoundEvent ); + + mnPlaySoundEvent = Application::PostUserEvent( LINK( this, SdFileDialog_Imp, PlayMusicHdl ) ); + } + break; + } +} + +IMPL_LINK_NOARG(SdFileDialog_Imp, PlayMusicHdl, void*, void) +{ + maUpdateIdle.Stop(); + mnPlaySoundEvent = nullptr; + + if (mxPlayer.is()) + { + if (mxPlayer->isPlaying()) + mxPlayer->stop(); + mxPlayer.clear(); + } + +#if HAVE_FEATURE_AVMEDIA + if( mbLabelPlaying ) + { + try + { + mxControlAccess->setLabel( css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, + SdResId( STR_PLAY ) ); + mbLabelPlaying = false; + } + catch(const css::lang::IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot access play button" ); +#endif + } + } + else + { + OUString aUrl( GetPath() ); + if ( !aUrl.isEmpty() ) + { + try + { + mxPlayer.set( avmedia::MediaWindow::createPlayer( aUrl, "" ), css::uno::UNO_SET_THROW ); + mxPlayer->start(); + maUpdateIdle.Start(); + } + catch (const css::uno::Exception&) + { + mxPlayer.clear(); + } + + if (mxPlayer.is()) + { + try + { + mxControlAccess->setLabel( css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, + SdResId( STR_STOP ) ); + mbLabelPlaying = true; + } + catch (const css::lang::IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot access play button" ); +#endif + } + } + } + } +#endif +} + +IMPL_LINK_NOARG(SdFileDialog_Imp, IsMusicStoppedHdl, Timer *, void) +{ + SolarMutexGuard aGuard; + + if (mxPlayer.is() && mxPlayer->isPlaying() && + mxPlayer->getMediaTime() < mxPlayer->getDuration()) + { + maUpdateIdle.Start(); + return; + } + + if( !mxControlAccess.is() ) + return; + + try + { + mxControlAccess->setLabel( css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, + SdResId( STR_PLAY ) ); + mbLabelPlaying = false; + } + catch (const css::lang::IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot access play button" ); +#endif + } +} + +SdFileDialog_Imp::SdFileDialog_Imp(weld::Window* pParent) + : FileDialogHelper(css::ui::dialogs::TemplateDescription::FILEOPEN_LINK_PLAY, FileDialogFlags::NONE, pParent) + , mnPlaySoundEvent(nullptr) + , mbLabelPlaying(false) + , maUpdateIdle( "SdFileDialog_Imp maUpdateIdle" ) +{ + maUpdateIdle.SetInvokeHandler(LINK(this, SdFileDialog_Imp, IsMusicStoppedHdl)); + + css::uno::Reference < css::ui::dialogs::XFilePicker3 > xFileDlg = GetFilePicker(); + + // get the control access + mxControlAccess.set( xFileDlg, css::uno::UNO_QUERY ); + + if( !mxControlAccess.is() ) + return; + + try + { + mxControlAccess->setLabel( css::ui::dialogs::ExtendedFilePickerElementIds::PUSHBUTTON_PLAY, + SdResId( STR_PLAY ) ); + } + catch (const css::lang::IllegalArgumentException&) + { +#ifdef DBG_UTIL + OSL_FAIL( "Cannot set play button label" ); +#endif + } +} + +SdFileDialog_Imp::~SdFileDialog_Imp() +{ + if( mnPlaySoundEvent ) + Application::RemoveUserEvent( mnPlaySoundEvent ); +} + +// ----------- SdOpenSoundFileDialog ----------------------- + +// these are simple forwarders +SdOpenSoundFileDialog::SdOpenSoundFileDialog(weld::Window *pParent) + : mpImpl(new SdFileDialog_Imp(pParent)) +{ + OUString aDescr = SdResId(STR_ALL_FILES); + mpImpl->AddFilter( aDescr, "*.*"); + mpImpl->SetContext(sfx2::FileDialogHelper::DrawImpressOpenSound); + + // setup filter +#if defined UNX + aDescr = SdResId(STR_AU_FILE); + mpImpl->AddFilter( aDescr, "*.au;*.snd"); + aDescr = SdResId(STR_VOC_FILE); + mpImpl->AddFilter( aDescr, "*.voc"); + aDescr = SdResId(STR_WAV_FILE); + mpImpl->AddFilter( aDescr, "*.wav"); + aDescr = SdResId(STR_AIFF_FILE); + mpImpl->AddFilter( aDescr, "*.aiff"); + aDescr = SdResId(STR_SVX_FILE); + mpImpl->AddFilter( aDescr, "*.svx"); +#else + aDescr = SdResId(STR_WAV_FILE); + mpImpl->AddFilter( aDescr, "*.wav;*.mp3;*.ogg" ); + aDescr = SdResId(STR_MIDI_FILE); + mpImpl->AddFilter( aDescr, "*.mid" ); +#endif +} + +SdOpenSoundFileDialog::~SdOpenSoundFileDialog() +{ +} + +ErrCode SdOpenSoundFileDialog::Execute() +{ + return mpImpl->Execute(); +} + +OUString SdOpenSoundFileDialog::GetPath() const +{ + return mpImpl->GetPath(); +} + +void SdOpenSoundFileDialog::SetPath( const OUString& rPath ) +{ + mpImpl->SetDisplayDirectory( rPath ); +} + +// WIP, please don't remove, dear Clang plugins +bool SdOpenSoundFileDialog::IsInsertAsLinkSelected() const +{ + bool bInsertAsLinkSelected = false; + css::uno::Reference const xFilePicker(mpImpl->GetFilePicker()); + css::uno::Reference const xControlAccess(xFilePicker, css::uno::UNO_QUERY_THROW); + xControlAccess->getValue(css::ui::dialogs::ExtendedFilePickerElementIds::CHECKBOX_LINK, 0) >>= bInsertAsLinkSelected; + return bInsertAsLinkSelected; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/gluectrl.cxx b/sd/source/ui/dlg/gluectrl.cxx new file mode 100644 index 000000000..a6baf3e92 --- /dev/null +++ b/sd/source/ui/dlg/gluectrl.cxx @@ -0,0 +1,200 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; + +// at the moment, Joe only supports the methods specified below +#define ESCDIR_COUNT 5 +const SdrEscapeDirection aEscDirArray[] = +{ + SdrEscapeDirection::SMART, + SdrEscapeDirection::LEFT, + SdrEscapeDirection::RIGHT, + SdrEscapeDirection::TOP, + SdrEscapeDirection::BOTTOM +}; + +SFX_IMPL_TOOLBOX_CONTROL( SdTbxCtlGlueEscDir, SfxUInt16Item ) + +/** + * Constructor for gluepoint escape direction Listbox + */ +GlueEscDirLB::GlueEscDirLB(vcl::Window* pParent, const Reference& rFrame) + : InterimItemWindow(pParent, "modules/simpress/ui/gluebox.ui", "GlueBox") + , m_xFrame(rFrame) + , m_xWidget(m_xBuilder->weld_combo_box("gluetype")) +{ + InitControlBase(m_xWidget.get()); + + Fill(); + + m_xWidget->connect_changed(LINK(this, GlueEscDirLB, SelectHdl)); + m_xWidget->connect_key_press(LINK(this, GlueEscDirLB, KeyInputHdl)); + + SetSizePixel(m_xWidget->get_preferred_size()); + + Show(); +} + +void GlueEscDirLB::dispose() +{ + m_xWidget.reset(); + InterimItemWindow::dispose(); +} + +GlueEscDirLB::~GlueEscDirLB() +{ + disposeOnce(); +} + +void GlueEscDirLB::set_sensitive(bool bSensitive) +{ + Enable(bSensitive); + m_xWidget->set_sensitive(bSensitive); +} + +IMPL_LINK(GlueEscDirLB, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + return ChildKeyInput(rKEvt); +} + +/** + * Determines the escape direction and sends the corresponding slot + */ +IMPL_LINK(GlueEscDirLB, SelectHdl, weld::ComboBox&, rBox, void) +{ + sal_Int32 nPos = rBox.get_active(); + SfxUInt16Item aItem( SID_GLUE_ESCDIR, static_cast(aEscDirArray[ nPos ]) ); + + if ( m_xFrame.is() ) + { + Any a; + aItem.QueryValue( a ); + Sequence< PropertyValue > aArgs{ comphelper::makePropertyValue("GlueEscapeDirection", a) }; + SfxToolBoxControl::Dispatch( Reference< XDispatchProvider >( m_xFrame->getController(), UNO_QUERY ), + ".uno:GlueEscapeDirection", + aArgs ); + } +} + +/** + * Fills the Listbox with strings + */ +void GlueEscDirLB::Fill() +{ + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_SMART ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_LEFT ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_RIGHT ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_TOP ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_BOTTOM ) ); + /* + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_LO ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_LU ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_RO ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_RU ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_HORZ ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_VERT ) ); + m_xWidget->append_text( SdResId( STR_GLUE_ESCDIR_ALL ) ); + */ +} + +/** + * Constructor for gluepoint escape direction toolbox control + */ +SdTbxCtlGlueEscDir::SdTbxCtlGlueEscDir( + sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx ) : + SfxToolBoxControl( nSlotId, nId, rTbx ) +{ +} + +/** + * Represents state in the listbox of the controller + */ +void SdTbxCtlGlueEscDir::StateChangedAtToolBoxControl( sal_uInt16 nSId, + SfxItemState eState, const SfxPoolItem* pState ) +{ + if( eState == SfxItemState::DEFAULT ) + { + GlueEscDirLB* pGlueEscDirLB = static_cast ( GetToolBox(). + GetItemWindow( GetId() ) ); + if( pGlueEscDirLB ) + { + if( pState ) + { + pGlueEscDirLB->set_sensitive(true); + if ( IsInvalidItem( pState ) ) + { + pGlueEscDirLB->set_active(-1); + } + else + { + SdrEscapeDirection nEscDir = static_cast(static_cast( pState )->GetValue()); + pGlueEscDirLB->set_active( GetEscDirPos( nEscDir ) ); + } + } + else + { + pGlueEscDirLB->set_sensitive(false); + pGlueEscDirLB->set_active(-1); + } + } + } + + SfxToolBoxControl::StateChangedAtToolBoxControl( nSId, eState, pState ); +} + +VclPtr SdTbxCtlGlueEscDir::CreateItemWindow( vcl::Window *pParent ) +{ + if( GetSlotId() == SID_GLUE_ESCDIR ) + return VclPtr::Create( pParent, m_xFrame ).get(); + + return VclPtr(); +} + +/** + * Returns position in the array for EscDir (Mapping for Listbox) + */ +sal_uInt16 SdTbxCtlGlueEscDir::GetEscDirPos( SdrEscapeDirection nEscDir ) +{ + for( sal_uInt16 i = 0; i < ESCDIR_COUNT; i++ ) + { + if( aEscDirArray[ i ] == nEscDir ) + return i; + } + return 99; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/headerfooterdlg.cxx b/sd/source/ui/dlg/headerfooterdlg.cxx new file mode 100644 index 000000000..703f2f598 --- /dev/null +++ b/sd/source/ui/dlg/headerfooterdlg.cxx @@ -0,0 +1,759 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +// preview control for presentation layout +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +namespace sd +{ + +namespace { + +class PresLayoutPreview : public weld::CustomWidgetController +{ +private: + SdPage* mpMaster; + HeaderFooterSettings maSettings; + Size maPageSize; + ::tools::Rectangle maOutRect; + +private: + void Paint(vcl::RenderContext& rRenderContext, SdrTextObj const * pObj, bool bVisible, bool bDotted = false); + +public: + explicit PresLayoutPreview(); + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + + void init(SdPage* pMaster); + void update(HeaderFooterSettings const & rSettings); +}; + +} + +} + +// tab page for slide & header'n'notes + +namespace sd +{ + +const int nDateTimeFormatsCount = 12; + +namespace { + +struct DateAndTimeFormat { + SvxDateFormat meDateFormat; + SvxTimeFormat meTimeFormat; +}; + +} + +DateAndTimeFormat const nDateTimeFormats[nDateTimeFormatsCount] = +{ + { SvxDateFormat::A, SvxTimeFormat::AppDefault }, + { SvxDateFormat::B, SvxTimeFormat::AppDefault }, + { SvxDateFormat::C, SvxTimeFormat::AppDefault }, + { SvxDateFormat::D, SvxTimeFormat::AppDefault }, + { SvxDateFormat::E, SvxTimeFormat::AppDefault }, + { SvxDateFormat::F, SvxTimeFormat::AppDefault }, + + { SvxDateFormat::A, SvxTimeFormat::HH24_MM }, + { SvxDateFormat::A, SvxTimeFormat::HH12_MM }, + + { SvxDateFormat::AppDefault, SvxTimeFormat::HH24_MM }, + { SvxDateFormat::AppDefault, SvxTimeFormat::HH24_MM_SS }, + + { SvxDateFormat::AppDefault, SvxTimeFormat::HH12_MM }, + { SvxDateFormat::AppDefault, SvxTimeFormat::HH12_MM_SS }, +}; + +class HeaderFooterTabPage +{ +private: + SdDrawDocument* mpDoc; + LanguageType meOldLanguage; + bool mbHandoutMode; + + std::unique_ptr mxBuilder; + std::unique_ptr mxContainer; + std::unique_ptr mxFTIncludeOn; + std::unique_ptr mxCBHeader; + std::unique_ptr mxHeaderBox; + std::unique_ptr mxTBHeader; + std::unique_ptr mxCBDateTime; + std::unique_ptr mxRBDateTimeFixed; + std::unique_ptr mxRBDateTimeAutomatic; + std::unique_ptr mxTBDateTimeFixed; + std::unique_ptr mxCBDateTimeFormat; + std::unique_ptr mxFTDateTimeLanguage; + std::unique_ptr mxCBDateTimeLanguage; + std::unique_ptr mxCBFooter; + std::unique_ptr mxFooterBox; + std::unique_ptr mxTBFooter; + std::unique_ptr mxCBSlideNumber; + std::unique_ptr mxCBNotOnTitle; + std::unique_ptr mxReplacementA; + std::unique_ptr mxReplacementB; + std::unique_ptr mxCTPreview; + std::unique_ptr mxCTPreviewWin; + + + DECL_LINK( UpdateOnToggleHdl, weld::Toggleable&, void ); + DECL_LINK( LanguageChangeHdl, weld::ComboBox&, void ); + + void FillFormatList(sal_Int32 nSelectedPos); + void GetOrSetDateTimeLanguage( LanguageType &rLanguage, bool bSet ); + void GetOrSetDateTimeLanguage( LanguageType &rLanguage, bool bSet, SdPage* pPage ); + +public: + HeaderFooterTabPage(weld::Container* pParent, SdDrawDocument* pDoc, SdPage* pActualPage, bool bHandoutMode ); + + void init( const HeaderFooterSettings& rSettings, bool bNotOnTitle ); + void getData( HeaderFooterSettings& rSettings, bool& rNotOnTitle ); + void update(); +}; + +} + +using namespace ::sd; + +HeaderFooterDialog::HeaderFooterDialog(ViewShell* pViewShell, weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage) + : GenericDialogController(pParent, "modules/simpress/ui/headerfooterdialog.ui", "HeaderFooterDialog") + , mpDoc( pDoc ) + , mpCurrentPage( pCurrentPage ) + , mpViewShell( pViewShell ) + , mxTabCtrl(m_xBuilder->weld_notebook("tabcontrol")) + , mxPBApplyToAll(m_xBuilder->weld_button("apply_all")) + , mxPBApply(m_xBuilder->weld_button("apply")) + , mxPBCancel(m_xBuilder->weld_button("cancel")) +{ + SdPage* pSlide; + SdPage* pNotes; + if( pCurrentPage->GetPageKind() == PageKind::Standard ) + { + pSlide = pCurrentPage; + pNotes = static_cast(pDoc->GetPage( pCurrentPage->GetPageNum() + 1 )); + } + else if( pCurrentPage->GetPageKind() == PageKind::Notes ) + { + pNotes = pCurrentPage; + pSlide = static_cast(pDoc->GetPage( pCurrentPage->GetPageNum() -1 )); + mpCurrentPage = pSlide; + } + else + { + // handout + pSlide = pDoc->GetSdPage( 0, PageKind::Standard ); + pNotes = pDoc->GetSdPage( 0, PageKind::Notes ); + mpCurrentPage = nullptr; + } + + mxSlideTabPage.reset(new HeaderFooterTabPage(mxTabCtrl->get_page("slides"), pDoc, pSlide, false)); + mxNotesHandoutsTabPage.reset(new HeaderFooterTabPage(mxTabCtrl->get_page("notes"), pDoc, pNotes, true)); + + pDoc->StopWorkStartupDelay(); + mxTabCtrl->show(); + + ActivatePageHdl(mxTabCtrl->get_current_page_ident()); + + mxTabCtrl->connect_enter_page( LINK( this, HeaderFooterDialog, ActivatePageHdl ) ); + + mxPBApplyToAll->connect_clicked( LINK( this, HeaderFooterDialog, ClickApplyToAllHdl ) ); + mxPBApply->connect_clicked( LINK( this, HeaderFooterDialog, ClickApplyHdl ) ); + mxPBCancel->connect_clicked( LINK( this, HeaderFooterDialog, ClickCancelHdl ) ); + + maSlideSettings = pSlide->getHeaderFooterSettings(); + + const HeaderFooterSettings& rTitleSettings = mpDoc->GetSdPage(0, PageKind::Standard)->getHeaderFooterSettings(); + bool bNotOnTitle = !rTitleSettings.mbFooterVisible && !rTitleSettings.mbSlideNumberVisible && !rTitleSettings.mbDateTimeVisible; + + mxSlideTabPage->init( maSlideSettings, bNotOnTitle ); + + maNotesHandoutSettings = pNotes->getHeaderFooterSettings(); + mxNotesHandoutsTabPage->init( maNotesHandoutSettings, false ); +} + +HeaderFooterDialog::~HeaderFooterDialog() +{ +} + +IMPL_LINK(HeaderFooterDialog, ActivatePageHdl, const OString&, rIdent, void) +{ + mxPBApply->set_visible(rIdent == "slides"); + mxPBApply->set_sensitive(mpCurrentPage != nullptr); +} + +IMPL_LINK_NOARG(HeaderFooterDialog, ClickApplyToAllHdl, weld::Button&, void) +{ + ApplyToAll(); +} + +IMPL_LINK_NOARG(HeaderFooterDialog, ClickApplyHdl, weld::Button&, void) +{ + Apply(); +} + +IMPL_LINK_NOARG(HeaderFooterDialog, ClickCancelHdl, weld::Button&, void) +{ + m_xDialog->response(RET_CANCEL); +} + +short HeaderFooterDialog::run() +{ + short nRet = GenericDialogController::run(); + if (nRet) + mpViewShell->GetDocSh()->SetModified(); + return nRet; +} + +void HeaderFooterDialog::ApplyToAll() +{ + OString tabId = mxTabCtrl->get_current_page_ident(); + apply(true, tabId == "slides"); + m_xDialog->response(RET_OK); +} + +void HeaderFooterDialog::Apply() +{ + OString tabId = mxTabCtrl->get_current_page_ident(); + apply(false, tabId == "slides"); + m_xDialog->response(RET_OK); +} + +void HeaderFooterDialog::apply( bool bToAll, bool bForceSlides ) +{ + std::unique_ptr pUndoGroup(new SdUndoGroup(mpDoc)); + OUString aComment( m_xDialog->get_title() ); + pUndoGroup->SetComment( aComment ); + + HeaderFooterSettings aNewSettings; + bool bNewNotOnTitle; + + // change slide settings first ... + + mxSlideTabPage->getData( aNewSettings, bNewNotOnTitle ); + + // only if we pressed apply or apply all on the slide tab page or if the slide settings + // have been changed + if( bForceSlides || !(aNewSettings == maSlideSettings) ) + { + // apply to all slides + if( bToAll ) + { + int nPageCount = mpDoc->GetSdPageCount( PageKind::Standard ); + int nPage; + for( nPage = 0; nPage < nPageCount; nPage++ ) + { + SdPage* pPage = mpDoc->GetSdPage( static_cast(nPage), PageKind::Standard ); + change( pUndoGroup.get(), pPage, aNewSettings ); + } + } + else + { + // apply only to the current slide + DBG_ASSERT( mpCurrentPage && mpCurrentPage->GetPageKind() == PageKind::Standard, "no current page to apply to!" ); + if( mpCurrentPage && (mpCurrentPage->GetPageKind() == PageKind::Standard) ) + { + change( pUndoGroup.get(), mpCurrentPage, aNewSettings ); + } + } + } + + // if we don't want to have header&footer on the first slide + if( bNewNotOnTitle ) + { + // just hide them, plain simple UI feature + HeaderFooterSettings aTempSettings = mpDoc->GetSdPage( 0, PageKind::Standard )->getHeaderFooterSettings(); + + aTempSettings.mbFooterVisible = false; + aTempSettings.mbSlideNumberVisible = false; + aTempSettings.mbDateTimeVisible = false; + + change( pUndoGroup.get(), mpDoc->GetSdPage( 0, PageKind::Standard ), aTempSettings ); + } + + // now notes settings + + mxNotesHandoutsTabPage->getData( aNewSettings, bNewNotOnTitle ); + + // only if we pressed apply or apply all on the notes tab page or if the notes settings + // have been changed + if( !bForceSlides || !(aNewSettings == maNotesHandoutSettings) ) + { + // first set to all notes pages + int nPageCount = mpDoc->GetSdPageCount( PageKind::Notes ); + int nPage; + for( nPage = 0; nPage < nPageCount; nPage++ ) + { + SdPage* pPage = mpDoc->GetSdPage( static_cast(nPage), PageKind::Notes ); + + change( pUndoGroup.get(), pPage, aNewSettings ); + } + + // and last but not least to the handout page + change( pUndoGroup.get(), mpDoc->GetMasterSdPage( 0, PageKind::Handout ), aNewSettings ); + } + + // give the undo group to the undo manager + mpViewShell->GetViewFrame()->GetObjectShell()->GetUndoManager()->AddUndoAction(std::move(pUndoGroup)); +} + +void HeaderFooterDialog::change( SdUndoGroup* pUndoGroup, SdPage* pPage, const HeaderFooterSettings& rNewSettings ) +{ + pUndoGroup->AddAction(new SdHeaderFooterUndoAction(mpDoc, pPage, rNewSettings )); + pPage->setHeaderFooterSettings( rNewSettings ); +} + +HeaderFooterTabPage::HeaderFooterTabPage(weld::Container* pParent, SdDrawDocument* pDoc, SdPage* pActualPage, bool bHandoutMode) + : mpDoc(pDoc) + , mbHandoutMode(bHandoutMode) + , mxBuilder(Application::CreateBuilder(pParent, "modules/simpress/ui/headerfootertab.ui")) + , mxContainer(mxBuilder->weld_container("HeaderFooterTab")) + , mxFTIncludeOn(mxBuilder->weld_label("include_label")) + , mxCBHeader(mxBuilder->weld_check_button("header_cb" )) + , mxHeaderBox(mxBuilder->weld_widget("header_box")) + , mxTBHeader(mxBuilder->weld_entry("header_text")) + , mxCBDateTime(mxBuilder->weld_check_button("datetime_cb")) + , mxRBDateTimeFixed(mxBuilder->weld_radio_button("rb_fixed")) + , mxRBDateTimeAutomatic(mxBuilder->weld_radio_button("rb_auto")) + , mxTBDateTimeFixed(mxBuilder->weld_entry("datetime_value")) + , mxCBDateTimeFormat(mxBuilder->weld_combo_box("datetime_format_list")) + , mxFTDateTimeLanguage(mxBuilder->weld_label("language_label")) + , mxCBDateTimeLanguage(new SvxLanguageBox(mxBuilder->weld_combo_box("language_list"))) + , mxCBFooter(mxBuilder->weld_check_button("footer_cb")) + , mxFooterBox(mxBuilder->weld_widget("footer_box" )) + , mxTBFooter(mxBuilder->weld_entry("footer_text")) + , mxCBSlideNumber(mxBuilder->weld_check_button("slide_number")) + , mxCBNotOnTitle(mxBuilder->weld_check_button("not_on_title")) + , mxReplacementA(mxBuilder->weld_label("replacement_a")) + , mxReplacementB(mxBuilder->weld_label("replacement_b")) + , mxCTPreview(new PresLayoutPreview) + , mxCTPreviewWin(new weld::CustomWeld(*mxBuilder, "preview", *mxCTPreview)) +{ + mxCTPreview->init( pActualPage ? + (pActualPage->IsMasterPage() ? pActualPage : static_cast(&(pActualPage->TRG_GetMasterPage()))) : + (pDoc->GetMasterSdPage( 0, bHandoutMode ? PageKind::Notes : PageKind::Standard )) ); + + if( mbHandoutMode ) + { + OUString sPageNo = mxReplacementA->get_label(); + mxCBSlideNumber->set_label( sPageNo ); + + OUString sFrameTitle = mxReplacementB->get_label(); + mxFTIncludeOn->set_label( sFrameTitle ); + } + + mxCBHeader->set_visible( mbHandoutMode ); + mxHeaderBox->set_visible( mbHandoutMode ); + mxCBNotOnTitle->set_visible( !mbHandoutMode ); + + mxCBDateTime->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxRBDateTimeFixed->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxRBDateTimeAutomatic->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxCBFooter->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxCBHeader->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + mxCBSlideNumber->connect_toggled( LINK( this, HeaderFooterTabPage, UpdateOnToggleHdl ) ); + + mxCBDateTimeLanguage->SetLanguageList( SvxLanguageListFlags::ALL|SvxLanguageListFlags::ONLY_KNOWN, false, false ); + mxCBDateTimeLanguage->connect_changed( LINK( this, HeaderFooterTabPage, LanguageChangeHdl ) ); + + GetOrSetDateTimeLanguage( meOldLanguage, false ); + meOldLanguage = MsLangId::getRealLanguage( meOldLanguage ); + mxCBDateTimeLanguage->set_active_id( meOldLanguage ); + + FillFormatList(0); +} + +IMPL_LINK_NOARG(HeaderFooterTabPage, LanguageChangeHdl, weld::ComboBox&, void) +{ + FillFormatList( mxCBDateTimeFormat->get_active() ); +} + +void HeaderFooterTabPage::FillFormatList( sal_Int32 nSelectedPos ) +{ + LanguageType eLanguage = mxCBDateTimeLanguage->get_active_id(); + + mxCBDateTimeFormat->clear(); + + DateTime aDateTime( DateTime::SYSTEM ); + + for (int nFormat = 0; nFormat < nDateTimeFormatsCount; ++nFormat) + { + OUString aStr( SvxDateTimeField::GetFormatted( + aDateTime, aDateTime, + nDateTimeFormats[nFormat].meDateFormat, nDateTimeFormats[nFormat].meTimeFormat, + *(SD_MOD()->GetNumberFormatter()), eLanguage ) ); + mxCBDateTimeFormat->append_text(aStr); + if (nFormat == nSelectedPos) + mxCBDateTimeFormat->set_active(nFormat); + } +} + +void HeaderFooterTabPage::init( const HeaderFooterSettings& rSettings, bool bNotOnTitle ) +{ + mxCBDateTime->set_active( rSettings.mbDateTimeVisible ); + mxRBDateTimeFixed->set_active( rSettings.mbDateTimeIsFixed ); + mxRBDateTimeAutomatic->set_active( !rSettings.mbDateTimeIsFixed ); + mxTBDateTimeFixed->set_text( rSettings.maDateTimeText ); + + mxCBHeader->set_active( rSettings.mbHeaderVisible ); + mxTBHeader->set_text( rSettings.maHeaderText ); + + mxCBFooter->set_active( rSettings.mbFooterVisible ); + mxTBFooter->set_text( rSettings.maFooterText ); + + mxCBSlideNumber->set_active( rSettings.mbSlideNumberVisible ); + + mxCBNotOnTitle->set_active( bNotOnTitle ); + + mxCBDateTimeLanguage->set_active_id( meOldLanguage ); + + for (sal_Int32 nPos = 0, nEntryCount = mxCBDateTimeFormat->get_count(); nPos < nEntryCount; ++nPos) + { + if( nDateTimeFormats[nPos].meDateFormat == rSettings.meDateFormat && nDateTimeFormats[nPos].meTimeFormat == rSettings.meTimeFormat ) + { + mxCBDateTimeFormat->set_active(nPos); + break; + } + } + + update(); +} + +void HeaderFooterTabPage::getData( HeaderFooterSettings& rSettings, bool& rNotOnTitle ) +{ + rSettings.mbDateTimeVisible = mxCBDateTime->get_active(); + rSettings.mbDateTimeIsFixed = mxRBDateTimeFixed->get_active(); + rSettings.maDateTimeText = mxTBDateTimeFixed->get_text(); + rSettings.mbFooterVisible = mxCBFooter->get_active(); + rSettings.maFooterText = mxTBFooter->get_text(); + rSettings.mbSlideNumberVisible = mxCBSlideNumber->get_active(); + rSettings.mbHeaderVisible = mxCBHeader->get_active(); + rSettings.maHeaderText = mxTBHeader->get_text(); + + int nPos = mxCBDateTimeFormat->get_active(); + if (nPos != -1) + { + rSettings.meDateFormat = nDateTimeFormats[nPos].meDateFormat; + rSettings.meTimeFormat = nDateTimeFormats[nPos].meTimeFormat; + } + + LanguageType eLanguage = mxCBDateTimeLanguage->get_active_id(); + if( eLanguage != meOldLanguage ) + GetOrSetDateTimeLanguage( eLanguage, true ); + + rNotOnTitle = mxCBNotOnTitle->get_active(); +} + +void HeaderFooterTabPage::update() +{ + mxRBDateTimeFixed->set_sensitive( mxCBDateTime->get_active() ); + mxTBDateTimeFixed->set_sensitive( mxRBDateTimeFixed->get_active() && mxCBDateTime->get_active() ); + mxRBDateTimeAutomatic->set_sensitive( mxCBDateTime->get_active() ); + mxCBDateTimeFormat->set_sensitive( mxCBDateTime->get_active() && mxRBDateTimeAutomatic->get_active() ); + mxFTDateTimeLanguage->set_sensitive( mxCBDateTime->get_active() && mxRBDateTimeAutomatic->get_active() ); + mxCBDateTimeLanguage->set_sensitive( mxCBDateTime->get_active() && mxRBDateTimeAutomatic->get_active() ); + mxFooterBox->set_sensitive( mxCBFooter->get_active() ); + mxHeaderBox->set_sensitive( mxCBHeader->get_active() ); + + HeaderFooterSettings aSettings; + bool bNotOnTitle; + getData( aSettings, bNotOnTitle ); + mxCTPreview->update( aSettings ); +} + +IMPL_LINK_NOARG(HeaderFooterTabPage, UpdateOnToggleHdl, weld::Toggleable&, void) +{ + update(); +} + +void HeaderFooterTabPage::GetOrSetDateTimeLanguage( LanguageType &rLanguage, bool bSet ) +{ + if( mbHandoutMode ) + { + // if set, set it on all notes master pages + if( bSet ) + { + sal_uInt16 nPageCount = mpDoc->GetMasterSdPageCount( PageKind::Notes ); + sal_uInt16 nPage; + for( nPage = 0; nPage < nPageCount; nPage++ ) + { + GetOrSetDateTimeLanguage( rLanguage, bSet, mpDoc->GetMasterSdPage( nPage, PageKind::Notes ) ); + } + } + + // #i119985# and set it, or just get it from the notes master page + GetOrSetDateTimeLanguage( rLanguage, bSet, mpDoc->GetMasterSdPage( 0, PageKind::Notes ) ); + } + else + { + // get the language from the first master page + // or set it to all master pages + sal_uInt16 nPageCount = bSet ? mpDoc->GetMasterSdPageCount( PageKind::Notes ) : 1; + sal_uInt16 nPage; + for( nPage = 0; nPage < nPageCount; nPage++ ) + { + GetOrSetDateTimeLanguage( rLanguage, bSet, mpDoc->GetMasterSdPage( nPage, PageKind::Standard ) ); + } + } +} + +void HeaderFooterTabPage::GetOrSetDateTimeLanguage( LanguageType &rLanguage, bool bSet, SdPage* pPage ) +{ + if( !pPage ) + return; + + SdrTextObj* pObj = static_cast(pPage->GetPresObj( PresObjKind::DateTime )); + if( !pObj ) + return; + + Outliner* pOutl = mpDoc->GetInternalOutliner(); + pOutl->Init( OutlinerMode::TextObject ); + OutlinerMode nOutlMode = pOutl->GetOutlinerMode(); + + EditEngine* pEdit = const_cast< EditEngine* >(&pOutl->GetEditEngine()); + + OutlinerParaObject* pOPO = pObj->GetOutlinerParaObject(); + if( pOPO ) + pOutl->SetText( *pOPO ); + + EPosition aDateFieldPosition; + bool bHasDateFieldItem = false; + + sal_Int32 nParaCount = pEdit->GetParagraphCount(); + for (sal_Int32 nPara = 0; (nPara < nParaCount) && !bHasDateFieldItem; ++nPara) + { + sal_uInt16 nFieldCount = pEdit->GetFieldCount(nPara); + for (sal_uInt16 nField = 0; (nField < nFieldCount); ++nField) + { + EFieldInfo aFieldInfo = pEdit->GetFieldInfo(nPara, nField); + if (aFieldInfo.pFieldItem) + { + const SvxFieldData* pFieldData = aFieldInfo.pFieldItem->GetField(); + if (dynamic_cast(pFieldData) != nullptr || + dynamic_cast(pFieldData) != nullptr) + { + bHasDateFieldItem = true; + aDateFieldPosition = aFieldInfo.aPosition; + break; + } + } + } + } + + if (bHasDateFieldItem) + { + if( bSet ) + { + SfxItemSet aSet(pEdit->GetAttribs(aDateFieldPosition.nPara, + aDateFieldPosition.nIndex, + aDateFieldPosition.nIndex+1, + GetAttribsFlags::CHARATTRIBS)); + + SvxLanguageItem aItem( rLanguage, EE_CHAR_LANGUAGE ); + aSet.Put( aItem ); + + SvxLanguageItem aItemCJK( rLanguage, EE_CHAR_LANGUAGE_CJK ); + aSet.Put( aItemCJK ); + + SvxLanguageItem aItemCTL( rLanguage, EE_CHAR_LANGUAGE_CTL ); + aSet.Put( aItemCTL ); + + ESelection aSel(aDateFieldPosition.nPara, aDateFieldPosition.nIndex, + aDateFieldPosition.nPara, aDateFieldPosition.nIndex+1 ); + pEdit->QuickSetAttribs( aSet, aSel ); + + pObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + pOutl->UpdateFields(); + } + else + { + rLanguage = pOutl->GetLanguage(aDateFieldPosition.nPara, + aDateFieldPosition.nIndex ); + } + } + + pOutl->Clear(); + pOutl->Init( nOutlMode ); +} + +PresLayoutPreview::PresLayoutPreview() + : mpMaster(nullptr) +{ +} + +void PresLayoutPreview::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(80, 80), MapMode(MapUnit::MapAppFont))); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + CustomWidgetController::SetDrawingArea(pDrawingArea); + SetOutputSizePixel(aSize); +} + +void PresLayoutPreview::init( SdPage *pMaster ) +{ + mpMaster = pMaster; + maPageSize = pMaster->GetSize(); +} + +void PresLayoutPreview::update( HeaderFooterSettings const & rSettings ) +{ + maSettings = rSettings; + Invalidate(); +} + +void PresLayoutPreview::Paint(vcl::RenderContext& rRenderContext, SdrTextObj const * pObj, bool bVisible, bool bDotted /* = false*/ ) +{ + // get object transformation + basegfx::B2DHomMatrix aObjectTransform; + basegfx::B2DPolyPolygon aObjectPolyPolygon; + pObj->TRGetBaseGeometry(aObjectTransform, aObjectPolyPolygon); + + // build complete transformation by adding view transformation from + // logic page coordinates to local pixel coordinates + const double fScaleX(static_cast(maOutRect.getWidth()) / static_cast(maPageSize.Width())); + const double fScaleY(static_cast(maOutRect.getHeight()) / static_cast(maPageSize.Height())); + aObjectTransform.scale(fScaleX, fScaleY); + aObjectTransform.translate(maOutRect.Left(), maOutRect.Top()); + + // create geometry using unit range and object transform + basegfx::B2DPolyPolygon aGeometry(basegfx::utils::createUnitPolygon()); + aGeometry.transform(aObjectTransform); + + // apply line pattern if wanted + if (bDotted) + { + static const double fFactor(1.0); + std::vector aPattern + { + 3.0 * fFactor, + 1.0 * fFactor + }; + + basegfx::B2DPolyPolygon aDashed; + basegfx::utils::applyLineDashing(aGeometry, aPattern, &aDashed); + aGeometry = aDashed; + } + + // determine line color + svtools::ColorConfig aColorConfig; + svtools::ColorConfigValue aColor( aColorConfig.GetColorValue( bVisible ? svtools::FONTCOLOR : svtools::OBJECTBOUNDARIES ) ); + + // paint at OutDev + rRenderContext.SetLineColor(aColor.nColor); + rRenderContext.SetFillColor(); + + for (sal_uInt32 a(0); a < aGeometry.count(); a++) + { + rRenderContext.DrawPolyLine(aGeometry.getB2DPolygon(a)); + } +} + +void PresLayoutPreview::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle&) +{ + rRenderContext.Push(); + + maOutRect = ::tools::Rectangle(Point(0,0), rRenderContext.GetOutputSize()); + + // calculate page size with correct aspect ratio + int nWidth, nHeight; + if( maPageSize.Width() > maPageSize.Height() ) + { + nWidth = maOutRect.GetWidth(); + nHeight = maPageSize.Width() == 0 ? 0 : tools::Long( static_cast(nWidth * maPageSize.Height()) / static_cast(maPageSize.Width()) ); + } + else + { + nHeight = maOutRect.GetHeight(); + nWidth = maPageSize.Height() == 0 ? 0 : tools::Long( static_cast(nHeight * maPageSize.Width()) / static_cast(maPageSize.Height()) ); + } + + maOutRect.AdjustLeft((maOutRect.GetWidth() - nWidth) >> 1 ); + maOutRect.SetRight( maOutRect.Left() + nWidth - 1 ); + maOutRect.AdjustTop((maOutRect.GetHeight() - nHeight) >> 1 ); + maOutRect.SetBottom( maOutRect.Top() + nHeight - 1 ); + + // draw decoration frame + DecorationView aDecoView(&rRenderContext); + maOutRect = aDecoView.DrawFrame(maOutRect, DrawFrameStyle::In); + + // draw page background + rRenderContext.SetFillColor(COL_WHITE); + rRenderContext.DrawRect(maOutRect); + + // paint presentation objects from masterpage + if (nullptr != mpMaster) + { + SdrTextObj* pMasterTitle = static_cast(mpMaster->GetPresObj(PresObjKind::Title)); + SdrTextObj* pMasterOutline = static_cast(mpMaster->GetPresObj(mpMaster->GetPageKind() == PageKind::Notes ? PresObjKind::Notes : PresObjKind::Outline)); + SdrTextObj* pHeader = static_cast(mpMaster->GetPresObj(PresObjKind::Header)); + SdrTextObj* pFooter = static_cast(mpMaster->GetPresObj(PresObjKind::Footer)); + SdrTextObj* pDate = static_cast(mpMaster->GetPresObj(PresObjKind::DateTime)); + SdrTextObj* pNumber = static_cast(mpMaster->GetPresObj(PresObjKind::SlideNumber)); + + if (pMasterTitle) + Paint(rRenderContext, pMasterTitle, true, true); + if (pMasterOutline) + Paint(rRenderContext, pMasterOutline, true, true); + if (pHeader) + Paint(rRenderContext, pHeader, maSettings.mbHeaderVisible); + if (pFooter) + Paint(rRenderContext, pFooter, maSettings.mbFooterVisible); + if (pDate) + Paint(rRenderContext, pDate, maSettings.mbDateTimeVisible); + if (pNumber) + Paint(rRenderContext, pNumber, maSettings.mbSlideNumberVisible); + } + + rRenderContext.Pop(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/ins_paste.cxx b/sd/source/ui/dlg/ins_paste.cxx new file mode 100644 index 000000000..f1020c0cb --- /dev/null +++ b/sd/source/ui/dlg/ins_paste.cxx @@ -0,0 +1,34 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +SdInsertPasteDlg::SdInsertPasteDlg(weld::Window* pWindow) + : GenericDialogController(pWindow, "modules/simpress/ui/insertslides.ui", "InsertSlidesDialog") + , m_xRbBefore(m_xBuilder->weld_radio_button("before")) + , m_xRbAfter(m_xBuilder->weld_radio_button("after")) +{ + m_xRbAfter->set_active(true); +} + +SdInsertPasteDlg::~SdInsertPasteDlg() {} + +bool SdInsertPasteDlg::IsInsertBefore() const { return m_xRbBefore->get_active(); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/inspagob.cxx b/sd/source/ui/dlg/inspagob.cxx new file mode 100644 index 000000000..5c984dd23 --- /dev/null +++ b/sd/source/ui/dlg/inspagob.cxx @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +SdInsertPagesObjsDlg::SdInsertPagesObjsDlg( + weld::Window* pWindow, const SdDrawDocument* pInDoc, + SfxMedium* pSfxMedium, const OUString& rFileName ) + : GenericDialogController(pWindow, "modules/sdraw/ui/insertslidesdialog.ui", "InsertSlidesDialog") + , m_pMedium(pSfxMedium) + , m_pDoc(pInDoc) + , m_rName(rFileName) + , m_xLbTree(new SdPageObjsTLV(m_xBuilder->weld_tree_view("tree"))) + , m_xCbxLink(m_xBuilder->weld_check_button("links")) + , m_xCbxMasters(m_xBuilder->weld_check_button("backgrounds")) +{ + m_xLbTree->set_size_request(m_xLbTree->get_approximate_digit_width() * 48, + m_xLbTree->get_height_rows(12)); + + m_xLbTree->SetViewFrame( pInDoc->GetDocSh()->GetViewShell()->GetViewFrame() ); + + m_xLbTree->connect_changed(LINK(this, SdInsertPagesObjsDlg, SelectObjectHdl)); + + // insert text + if (!m_pMedium) + m_xDialog->set_title(SdResId(STR_INSERT_TEXT)); + else if (m_pDoc && m_pDoc->GetDocumentType() == DocumentType::Draw) + m_xDialog->set_title(SdResId(STR_INSERT_PAGES)); + + Reset(); +} + +SdInsertPagesObjsDlg::~SdInsertPagesObjsDlg() +{ +} + +/** + * Fills the TreeLB dependent on the medium. Is not medium available, then + * it is a text and not a draw document. + */ +void SdInsertPagesObjsDlg::Reset() +{ + if( m_pMedium ) + { + m_xLbTree->set_selection_mode(SelectionMode::Multiple); + + // transfer ownership of Medium + m_xLbTree->Fill( m_pDoc, m_pMedium, m_rName ); + } + else + { + m_xLbTree->InsertEntry(m_rName, BMP_DOC_TEXT); + } + + m_xCbxMasters->set_active(true); +} + +std::vector SdInsertPagesObjsDlg::GetList( const sal_uInt16 nType ) +{ + // With Draw documents, we have to return NULL on selection of the document + if( m_pMedium ) + { + // to ensure that bookmarks are opened + // (when the whole document is selected) + m_xLbTree->GetBookmarkDoc(); + + // If the document is selected (too) or nothing is selected, + // the whole document is inserted (but not more!) + std::unique_ptr xIter(m_xLbTree->make_iterator()); + if (!m_xLbTree->get_iter_first(*xIter) || m_xLbTree->is_selected(*xIter)) + return std::vector(); + } + + return m_xLbTree->GetSelectEntryList( nType ); +} + +/** + * is link checked + */ +bool SdInsertPagesObjsDlg::IsLink() const +{ + return m_xCbxLink->get_active(); +} + +/** + * is link checked + */ +bool SdInsertPagesObjsDlg::IsRemoveUnnecessaryMasterPages() const +{ + return m_xCbxMasters->get_active(); +} + +/** + * Enabled and selects end-color-LB + */ +IMPL_LINK_NOARG(SdInsertPagesObjsDlg, SelectObjectHdl, weld::TreeView&, void) +{ + m_xCbxLink->set_sensitive(m_xLbTree->IsLinkableSelected()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/layeroptionsdlg.cxx b/sd/source/ui/dlg/layeroptionsdlg.cxx new file mode 100644 index 000000000..a70c71f47 --- /dev/null +++ b/sd/source/ui/dlg/layeroptionsdlg.cxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +SdInsertLayerDlg::SdInsertLayerDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, + bool bDeletable, const OUString& rStr) + : GenericDialogController(pWindow, "modules/sdraw/ui/insertlayer.ui", "InsertLayerDialog") + , m_xEdtName(m_xBuilder->weld_entry("name")) + , m_xEdtTitle(m_xBuilder->weld_entry("title")) + , m_xEdtDesc(m_xBuilder->weld_text_view("textview")) + , m_xCbxVisible(m_xBuilder->weld_check_button("visible")) + , m_xCbxPrintable(m_xBuilder->weld_check_button("printable")) + , m_xCbxLocked(m_xBuilder->weld_check_button("locked")) + , m_xNameFrame(m_xBuilder->weld_widget("nameframe")) +{ + m_xDialog->set_title(rStr); + + m_xEdtName->set_text( static_cast( rInAttrs.Get( ATTR_LAYER_NAME ) ).GetValue() ); + m_xEdtTitle->set_text( static_cast( rInAttrs.Get( ATTR_LAYER_TITLE ) ).GetValue() ); + m_xEdtDesc->set_text( static_cast( rInAttrs.Get( ATTR_LAYER_DESC ) ).GetValue() ); + m_xEdtDesc->set_size_request(-1, m_xEdtDesc->get_height_rows(4)); + m_xCbxVisible->set_active( static_cast( rInAttrs.Get( ATTR_LAYER_VISIBLE ) ).GetValue() ); + m_xCbxPrintable->set_active( static_cast( rInAttrs.Get( ATTR_LAYER_PRINTABLE ) ).GetValue() ); + m_xCbxLocked->set_active( static_cast( rInAttrs.Get( ATTR_LAYER_LOCKED ) ).GetValue() ); + m_xNameFrame->set_sensitive(bDeletable); +} + +SdInsertLayerDlg::~SdInsertLayerDlg() +{ +} + +void SdInsertLayerDlg::GetAttr( SfxItemSet& rAttrs ) +{ + rAttrs.Put( makeSdAttrLayerName( m_xEdtName->get_text() ) ); + rAttrs.Put( makeSdAttrLayerTitle( m_xEdtTitle->get_text() ) ); + rAttrs.Put( makeSdAttrLayerDesc( m_xEdtDesc->get_text() ) ); + rAttrs.Put( makeSdAttrLayerVisible( m_xCbxVisible->get_active() ) ); + rAttrs.Put( makeSdAttrLayerPrintable( m_xCbxPrintable->get_active() ) ); + rAttrs.Put( makeSdAttrLayerLocked( m_xCbxLocked->get_active() ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/masterlayoutdlg.cxx b/sd/source/ui/dlg/masterlayoutdlg.cxx new file mode 100644 index 000000000..ce4e069b0 --- /dev/null +++ b/sd/source/ui/dlg/masterlayoutdlg.cxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +using namespace ::sd; + +MasterLayoutDialog::MasterLayoutDialog(weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage) + : GenericDialogController(pParent, "modules/simpress/ui/masterlayoutdlg.ui", "MasterLayoutDialog") + , mpDoc(pDoc) + , mpCurrentPage(pCurrentPage) + , mxCBDate(m_xBuilder->weld_check_button("datetime")) + , mxCBPageNumber(m_xBuilder->weld_check_button("pagenumber")) + , mxCBSlideNumber(m_xBuilder->weld_check_button("slidenumber")) + , mxCBHeader(m_xBuilder->weld_check_button("header")) + , mxCBFooter(m_xBuilder->weld_check_button("footer")) +{ + if( mpCurrentPage && !mpCurrentPage->IsMasterPage() ) + { + mpCurrentPage = static_cast(&(mpCurrentPage->TRG_GetMasterPage())); + } + + if( mpCurrentPage == nullptr ) + { + mpCurrentPage = pDoc->GetMasterSdPage( 0, PageKind::Standard ); + OSL_FAIL( "MasterLayoutDialog::MasterLayoutDialog() - no current page?" ); + } + + switch( mpCurrentPage->GetPageKind() ) + { + case PageKind::Standard: + { + mxCBHeader->set_sensitive(false); + mxCBPageNumber->set_label(mxCBSlideNumber->get_label()); + break; + } + case PageKind::Notes: + break; + case PageKind::Handout: + break; + } + + mbOldHeader = mpCurrentPage->GetPresObj( PresObjKind::Header ) != nullptr; + mbOldDate = mpCurrentPage->GetPresObj( PresObjKind::DateTime ) != nullptr; + mbOldFooter = mpCurrentPage->GetPresObj( PresObjKind::Footer ) != nullptr; + mbOldPageNumber = mpCurrentPage->GetPresObj( PresObjKind::SlideNumber ) != nullptr; + + mxCBHeader->set_active( mbOldHeader ); + mxCBDate->set_active( mbOldDate ); + mxCBFooter->set_active( mbOldFooter ); + mxCBPageNumber->set_active( mbOldPageNumber ); +} + +MasterLayoutDialog::~MasterLayoutDialog() +{ +} + +short MasterLayoutDialog::run() +{ + if (GenericDialogController::run() == RET_OK) + applyChanges(); + return RET_OK; +} + +void MasterLayoutDialog::applyChanges() +{ + mpDoc->BegUndo(m_xDialog->get_title()); + + if( (mpCurrentPage->GetPageKind() != PageKind::Standard) && (mbOldHeader != mxCBHeader->get_active() ) ) + { + if( mbOldHeader ) + remove( PresObjKind::Header ); + else + create( PresObjKind::Header ); + } + + if( mbOldFooter != mxCBFooter->get_active() ) + { + if( mbOldFooter ) + remove( PresObjKind::Footer ); + else + create( PresObjKind::Footer ); + } + + if( mbOldDate != mxCBDate->get_active() ) + { + if( mbOldDate ) + remove( PresObjKind::DateTime ); + else + create( PresObjKind::DateTime ); + } + + if( mbOldPageNumber != mxCBPageNumber->get_active() ) + { + if( mbOldPageNumber ) + remove( PresObjKind::SlideNumber ); + else + create( PresObjKind::SlideNumber ); + } + + mpDoc->EndUndo(); +} + +void MasterLayoutDialog::create(PresObjKind eKind) +{ + mpCurrentPage->CreateDefaultPresObj(eKind); +} + +void MasterLayoutDialog::remove( PresObjKind eKind ) +{ + mpCurrentPage->DestroyDefaultPresObj(eKind); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/morphdlg.cxx b/sd/source/ui/dlg/morphdlg.cxx new file mode 100644 index 000000000..c0d7f4e5a --- /dev/null +++ b/sd/source/ui/dlg/morphdlg.cxx @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + +MorphDlg::MorphDlg(weld::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2 ) + : GenericDialogController(pParent, "modules/sdraw/ui/crossfadedialog.ui", "CrossFadeDialog") + , m_xMtfSteps(m_xBuilder->weld_spin_button("increments")) + , m_xCbxAttributes(m_xBuilder->weld_check_button("attributes")) + , m_xCbxOrientation(m_xBuilder->weld_check_button("orientation")) +{ + LoadSettings(); + + SfxItemPool & rPool = pObj1->GetObjectItemPool(); + SfxItemSet aSet1( rPool ); + SfxItemSet aSet2( rPool ); + + aSet1.Put(pObj1->GetMergedItemSet()); + aSet2.Put(pObj2->GetMergedItemSet()); + + const drawing::LineStyle eLineStyle1 = aSet1.Get( XATTR_LINESTYLE ).GetValue(); + const drawing::LineStyle eLineStyle2 = aSet2.Get( XATTR_LINESTYLE ).GetValue(); + const drawing::FillStyle eFillStyle1 = aSet1.Get( XATTR_FILLSTYLE ).GetValue(); + const drawing::FillStyle eFillStyle2 = aSet2.Get( XATTR_FILLSTYLE ).GetValue(); + + if ( ( ( eLineStyle1 == drawing::LineStyle_NONE ) || ( eLineStyle2 == drawing::LineStyle_NONE ) ) && + ( ( eFillStyle1 != drawing::FillStyle_SOLID ) || ( eFillStyle2 != drawing::FillStyle_SOLID ) ) ) + { + m_xCbxAttributes->set_sensitive(false); + } +} + +MorphDlg::~MorphDlg() +{ +} + +void MorphDlg::LoadSettings() +{ + tools::SvRef xIStm( SD_MOD()->GetOptionStream( SD_OPTION_MORPHING , + SdOptionStreamMode::Load ) ); + sal_uInt16 nSteps; + bool bOrient, bAttrib; + + if( xIStm.is() ) + { + SdIOCompat aCompat( *xIStm, StreamMode::READ ); + + xIStm->ReadUInt16( nSteps ).ReadCharAsBool( bOrient ).ReadCharAsBool( bAttrib ); + } + else + { + nSteps = 16; + bOrient = bAttrib = true; + } + + m_xMtfSteps->set_value(nSteps); + m_xCbxOrientation->set_active(bOrient); + m_xCbxAttributes->set_active(bAttrib); +} + +void MorphDlg::SaveSettings() const +{ + tools::SvRef xOStm( SD_MOD()->GetOptionStream( SD_OPTION_MORPHING , + SdOptionStreamMode::Store ) ); + + if( xOStm.is() ) + { + SdIOCompat aCompat( *xOStm, StreamMode::WRITE, 1 ); + + xOStm->WriteUInt16( m_xMtfSteps->get_value() ) + .WriteBool( m_xCbxOrientation->get_active() ) + .WriteBool( m_xCbxAttributes->get_active() ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/navigatr.cxx b/sd/source/ui/dlg/navigatr.cxx new file mode 100644 index 000000000..78525efe5 --- /dev/null +++ b/sd/source/ui/dlg/navigatr.cxx @@ -0,0 +1,735 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * SdNavigatorWin - FloatingWindow + */ +SdNavigatorWin::SdNavigatorWin(weld::Widget* pParent, SfxBindings* pInBindings, SfxNavigator* pNavigatorDlg) + : PanelLayout(pParent, "NavigatorPanel", "modules/simpress/ui/navigatorpanel.ui") + , mxToolbox(m_xBuilder->weld_toolbar("toolbox")) + , mxTlbObjects(new SdPageObjsTLV(m_xBuilder->weld_tree_view("tree"))) + , mxLbDocs(m_xBuilder->weld_combo_box("documents")) + , mxDragModeMenu(m_xBuilder->weld_menu("dragmodemenu")) + , mxShapeMenu(m_xBuilder->weld_menu("shapemenu")) + , mxNavigatorDlg(pNavigatorDlg) + , mbDocImported ( false ) + // On changes of the DragType: adjust SelectionMode of TLB! + , meDragType ( NAVIGATOR_DRAGTYPE_EMBEDDED ) + , mpBindings ( pInBindings ) +{ + mxTlbObjects->SetViewFrame( mpBindings->GetDispatcher()->GetFrame() ); + + mxTlbObjects->connect_row_activated(LINK(this, SdNavigatorWin, ClickObjectHdl)); + mxTlbObjects->set_selection_mode(SelectionMode::Single); + + mxToolbox->connect_clicked(LINK(this, SdNavigatorWin, SelectToolboxHdl)); + mxToolbox->connect_menu_toggled(LINK(this, SdNavigatorWin, DropdownClickToolBoxHdl)); + + mxToolbox->set_item_menu("dragmode", mxDragModeMenu.get()); + mxDragModeMenu->connect_activate(LINK(this, SdNavigatorWin, MenuSelectHdl)); + + // Shape filter drop down menu. + mxToolbox->set_item_menu("shapes", mxShapeMenu.get()); + mxShapeMenu->connect_activate(LINK(this, SdNavigatorWin, ShapeFilterCallback)); + + mxTlbObjects->SetSdNavigator(this); + + // DragTypeListBox + mxLbDocs->set_size_request(42, -1); // set a nominal width so it takes width of surroundings + mxLbDocs->connect_changed(LINK(this, SdNavigatorWin, SelectDocumentHdl)); + + SetDragImage(); + + mxToolbox->connect_key_press(LINK(this, SdNavigatorWin, KeyInputHdl)); + mxTlbObjects->connect_key_press(LINK(this, SdNavigatorWin, KeyInputHdl)); + mxLbDocs->connect_key_press(LINK(this, SdNavigatorWin, KeyInputHdl)); +} + +void SdNavigatorWin::FirstFocus() +{ + // set focus to listbox, otherwise it is in the toolbox which is only useful + // for keyboard navigation + mxTlbObjects->grab_focus(); +} + +weld::Window* SdNavigatorWin::GetFrameWeld() const +{ + if (mxNavigatorDlg) + return mxNavigatorDlg->GetFrameWeld(); + return PanelLayout::GetFrameWeld(); +} + +void SdNavigatorWin::SetUpdateRequestFunctor(const UpdateRequestFunctor& rUpdateRequest) +{ + mpNavigatorCtrlItem.reset( new SdNavigatorControllerItem(SID_NAVIGATOR_STATE, this, mpBindings, rUpdateRequest) ); + mpPageNameCtrlItem.reset( new SdPageNameControllerItem(SID_NAVIGATOR_PAGENAME, this, mpBindings) ); + + // InitTlb; is initiated over Slot + if (rUpdateRequest) + rUpdateRequest(); +} + +SdNavigatorWin::~SdNavigatorWin() +{ + mpNavigatorCtrlItem.reset(); + mpPageNameCtrlItem.reset(); + mxDragModeMenu.reset(); + mxShapeMenu.reset(); + mxToolbox.reset(); + mxTlbObjects.reset(); + mxLbDocs.reset(); +} + +//when object is marked , fresh the corresponding entry tree . +void SdNavigatorWin::FreshTree( const SdDrawDocument* pDoc ) +{ + SdDrawDocument* pNonConstDoc = const_cast(pDoc); // const as const can... + sd::DrawDocShell* pDocShell = pNonConstDoc->GetDocSh(); + const OUString& aDocShName( pDocShell->GetName() ); + OUString aDocName = pDocShell->GetMedium()->GetName(); + mxTlbObjects->Fill( pDoc, false, aDocName ); // Only normal pages + RefreshDocumentLB(); + mxLbDocs->set_active_text(aDocShName); +} + +void SdNavigatorWin::InitTreeLB( const SdDrawDocument* pDoc ) +{ + SdDrawDocument* pNonConstDoc = const_cast(pDoc); // const as const can... + ::sd::DrawDocShell* pDocShell = pNonConstDoc->GetDocSh(); + OUString aDocShName( pDocShell->GetName() ); + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + + // Restore the 'ShowAllShapes' flag from the last time (in this session) + // that the navigator was shown. + if (pViewShell != nullptr) + { + ::sd::FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + mxTlbObjects->SetShowAllShapes(pFrameView->IsNavigatorShowingAllShapes(), false); + } + + // Disable the shape filter drop down menu when there is a running slide + // show. + if (pViewShell!=nullptr && sd::SlideShow::IsRunning( pViewShell->GetViewShellBase() )) + mxToolbox->set_item_sensitive("shapes", false); + else + mxToolbox->set_item_sensitive("shapes", true); + + if( !mxTlbObjects->IsEqualToDoc( pDoc ) ) + { + OUString aDocName = pDocShell->GetMedium()->GetName(); + mxTlbObjects->clear(); + mxTlbObjects->Fill( pDoc, false, aDocName ); // only normal pages + + RefreshDocumentLB(); + mxLbDocs->set_active_text(aDocShName); + } + else + { + mxLbDocs->set_active(-1); + mxLbDocs->set_active_text(aDocShName); + +// commented in order to fix 30246 +// if( mxLbDocs->get_active() == -1 ) + { + RefreshDocumentLB(); + mxLbDocs->set_active_text(aDocShName); + } + } + + SfxViewFrame* pViewFrame = ( ( pViewShell && pViewShell->GetViewFrame() ) ? pViewShell->GetViewFrame() : SfxViewFrame::Current() ); + if( pViewFrame ) + pViewFrame->GetBindings().Invalidate(SID_NAVIGATOR_PAGENAME, true, true); +} + +/** + * DragType is set on dependence if a Drag is even possible. For example, + * under certain circumstances, it is not allowed to drag graphics (#31038#). + */ +NavigatorDragType SdNavigatorWin::GetNavigatorDragType() +{ + NavigatorDragType eDT = meDragType; + NavDocInfo* pInfo = GetDocInfo(); + + if( ( eDT == NAVIGATOR_DRAGTYPE_LINK ) && ( ( pInfo && !pInfo->HasName() ) || !mxTlbObjects->IsLinkableSelected() ) ) + eDT = NAVIGATOR_DRAGTYPE_NONE; + + return eDT; +} + +SdPageObjsTLV& SdNavigatorWin::GetObjects() +{ + return *mxTlbObjects; +} + +IMPL_LINK(SdNavigatorWin, SelectToolboxHdl, const OString&, rCommand, void) +{ + PageJump ePage = PAGE_NONE; + + if (rCommand == "first") + ePage = PAGE_FIRST; + else if (rCommand == "previous") + ePage = PAGE_PREVIOUS; + else if (rCommand == "next") + ePage = PAGE_NEXT; + else if (rCommand == "last") + ePage = PAGE_LAST; + else if (rCommand == "dragmode") + mxToolbox->set_menu_item_active("dragmode", !mxToolbox->get_menu_item_active("dragmode")); + else if (rCommand == "shapes") + mxToolbox->set_menu_item_active("shapes", !mxToolbox->get_menu_item_active("shapes")); + + if (ePage != PAGE_NONE) + { + SfxUInt16Item aItem( SID_NAVIGATOR_PAGE, static_cast(ePage) ); + mpBindings->GetDispatcher()->ExecuteList(SID_NAVIGATOR_PAGE, + SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } +} + +IMPL_LINK(SdNavigatorWin, DropdownClickToolBoxHdl, const OString&, rCommand, void) +{ + if (!mxToolbox->get_menu_item_active(rCommand)) + return; + + if (rCommand == "dragmode") + { + NavDocInfo* pInfo = GetDocInfo(); + if( ( pInfo && !pInfo->HasName() ) || !mxTlbObjects->IsLinkableSelected() ) + { + mxDragModeMenu->set_sensitive(OString::number(NAVIGATOR_DRAGTYPE_LINK), false); + mxDragModeMenu->set_sensitive(OString::number(NAVIGATOR_DRAGTYPE_URL), false); + meDragType = NAVIGATOR_DRAGTYPE_EMBEDDED; + } + + mxDragModeMenu->set_active(OString::number(meDragType), true); + } + else if (rCommand == "shapes") + { + bool bAll = mxTlbObjects->GetShowAllShapes(); + mxShapeMenu->set_active("named", !bAll); + mxShapeMenu->set_active("all", bAll); + } +} + +IMPL_LINK_NOARG(SdNavigatorWin, ClickObjectHdl, weld::TreeView&, bool) +{ + if( !mbDocImported || mxLbDocs->get_active() != 0 ) + { + NavDocInfo* pInfo = GetDocInfo(); + + // if it is the active window, we jump to the page + if( pInfo && pInfo->IsActive() ) + { + OUString aStr(mxTlbObjects->get_selected_text()); + + if( !aStr.isEmpty() ) + { + SfxStringItem aItem( SID_NAVIGATOR_OBJECT, aStr ); + mpBindings->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_OBJECT, + SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + + // moved here from SetGetFocusHdl. Reset the + // focus only if something has been selected in the + // document. + SfxViewShell* pCurSh = SfxViewShell::Current(); + + if ( pCurSh ) + { + vcl::Window* pShellWnd = pCurSh->GetWindow(); + if ( pShellWnd ) + pShellWnd->GrabFocus(); + } + + // We navigated to an object, but the current shell may be + // still the slide sorter. Explicitly try to grab the draw + // shell focus, so follow-up operations work with the object + // and not with the whole slide. + sd::DrawDocShell* pDocShell = pInfo->mpDocShell; + if (pDocShell) + { + sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + if (pViewShell) + { + vcl::Window* pWindow = pViewShell->GetActiveWindow(); + if (pWindow) + pWindow->GrabFocus(); + } + } + + if (!mxTlbObjects->IsNavigationGrabsFocus()) + // This is the case when keyboard navigation inside the + // navigator should continue to work. + mxTlbObjects->grab_focus(); + } + } + } + return false; +} + +IMPL_LINK_NOARG(SdNavigatorWin, SelectDocumentHdl, weld::ComboBox&, void) +{ + OUString aStrLb = mxLbDocs->get_active_text(); + tools::Long nPos = mxLbDocs->get_active(); + bool bFound = false; + ::sd::DrawDocShell* pDocShell = nullptr; + NavDocInfo* pInfo = GetDocInfo(); + + // is it a dragged object? + if( mbDocImported && nPos == 0 ) + { + // construct document in TLB + InsertFile( aStrLb ); + } + else if (pInfo) + { + pDocShell = pInfo->mpDocShell; + + bFound = true; + } + + if( bFound ) + { + SdDrawDocument* pDoc = pDocShell->GetDoc(); + if( !mxTlbObjects->IsEqualToDoc( pDoc ) ) + { + SdDrawDocument* pNonConstDoc = pDoc; // const as const can... + ::sd::DrawDocShell* pNCDocShell = pNonConstDoc->GetDocSh(); + OUString aDocName = pNCDocShell->GetMedium()->GetName(); + mxTlbObjects->clear(); + mxTlbObjects->Fill( pDoc, false, aDocName ); // only normal pages + } + } + + // check if link or url is possible + if( ( pInfo && !pInfo->HasName() ) || !mxTlbObjects->IsLinkableSelected() || ( meDragType != NAVIGATOR_DRAGTYPE_EMBEDDED ) ) + { + meDragType = NAVIGATOR_DRAGTYPE_EMBEDDED; + SetDragImage(); + } +} + +/** + * Set DrageType and set image accordingly to it. + */ +IMPL_LINK(SdNavigatorWin, MenuSelectHdl, const OString&, rIdent, void) +{ + sal_uInt32 nMenuId = rIdent.toUInt32(); + + NavigatorDragType eDT = static_cast(nMenuId); + if( meDragType == eDT ) + return; + + meDragType = eDT; + SetDragImage(); + + if( meDragType == NAVIGATOR_DRAGTYPE_URL ) + { + // patch, prevents endless loop + if (mxTlbObjects->count_selected_rows() > 1) + mxTlbObjects->unselect_all(); + + mxTlbObjects->set_selection_mode(SelectionMode::Single); + } + else + mxTlbObjects->set_selection_mode(SelectionMode::Multiple); +} + +IMPL_LINK( SdNavigatorWin, ShapeFilterCallback, const OString&, rIdent, void ) +{ + bool bShowAllShapes(mxTlbObjects->GetShowAllShapes()); + if (rIdent == "named") + bShowAllShapes = false; + else if (rIdent == "all") + bShowAllShapes = true; + else + OSL_FAIL("SdNavigatorWin::ShapeFilterCallback called for unknown menu entry"); + + mxTlbObjects->SetShowAllShapes(bShowAllShapes, true); + + // Remember the selection in the FrameView. + NavDocInfo* pInfo = GetDocInfo(); + if (pInfo == nullptr) + return; + + ::sd::DrawDocShell* pDocShell = pInfo->mpDocShell; + if (pDocShell != nullptr) + { + ::sd::ViewShell* pViewShell = pDocShell->GetViewShell(); + if (pViewShell != nullptr) + { + ::sd::FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + { + pFrameView->SetIsNavigatorShowingAllShapes(bShowAllShapes); + } + } + } +} + +bool SdNavigatorWin::InsertFile(const OUString& rFileName) +{ + INetURLObject aURL( rFileName ); + + if( aURL.GetProtocol() == INetProtocol::NotValid ) + { + OUString aURLStr; + osl::FileBase::getFileURLFromSystemPath( rFileName, aURLStr ); + aURL = INetURLObject( aURLStr ); + } + + // get adjusted FileName + OUString aFileName( aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + + if (aFileName.isEmpty()) + { + // show actual document again + maDropFileName = aFileName; + } + else + { + // show dragged-in document + std::shared_ptr pFilter; + ErrCode nErr = ERRCODE_NONE; + + if (aFileName != maDropFileName) + { + SfxMedium aMed(aFileName, (StreamMode::READ | StreamMode::SHARE_DENYNONE)); + SfxFilterMatcher aMatch( "simpress" ); + aMed.UseInteractionHandler( true ); + nErr = aMatch.GuessFilter(aMed, pFilter); + } + + if ((pFilter && !nErr) || aFileName == maDropFileName) + { + // The medium may be opened with READ/WRITE. Therefore, we first + // check if it contains a Storage. + std::unique_ptr xMedium(new SfxMedium(aFileName, + StreamMode::READ | StreamMode::NOCREATE)); + + if (xMedium->IsStorage()) + { + // Now depending on mode: + // mxTlbObjects->set_selection_mode(SelectionMode::Multiple); + // handover of ownership of xMedium; + SdDrawDocument* pDropDoc = mxTlbObjects->GetBookmarkDoc(xMedium.release()); + + if (pDropDoc) + { + mxTlbObjects->clear(); + maDropFileName = aFileName; + + if( !mxTlbObjects->IsEqualToDoc( pDropDoc ) ) + { + // only normal pages + mxTlbObjects->Fill(pDropDoc, false, maDropFileName); + RefreshDocumentLB( &maDropFileName ); + } + } + } + else + { + return false; + } + } + else + { + return false; + } + } + + return true; +} + +void SdNavigatorWin::RefreshDocumentLB( const OUString* pDocName ) +{ + sal_Int32 nPos = 0; + + if( pDocName ) + { + if( mbDocImported ) + mxLbDocs->remove(0); + + mxLbDocs->insert_text(0, *pDocName); + mbDocImported = true; + } + else + { + nPos = mxLbDocs->get_active(); + if (nPos == -1) + nPos = 0; + + OUString aStr; + if( mbDocImported ) + aStr = mxLbDocs->get_text(0); + + mxLbDocs->clear(); + + // delete list of DocInfos + maDocList.clear(); + + if( mbDocImported ) + mxLbDocs->insert_text(0, aStr); + + ::sd::DrawDocShell* pCurrentDocShell = + dynamic_cast< ::sd::DrawDocShell *>( SfxObjectShell::Current() ); + SfxObjectShell* pSfxDocShell = SfxObjectShell::GetFirst([](const SfxObjectShell*){return true;}, false); + while( pSfxDocShell ) + { + ::sd::DrawDocShell* pDocShell = dynamic_cast< ::sd::DrawDocShell *>( pSfxDocShell ); + if( pDocShell && !pDocShell->IsInDestruction() && ( pDocShell->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) ) + { + NavDocInfo aInfo ; + aInfo.mpDocShell = pDocShell; + + SfxMedium *pMedium = pDocShell->GetMedium(); + aStr = pMedium ? pMedium->GetName() : OUString(); + if( !aStr.isEmpty() ) + aInfo.SetName( true ); + else + aInfo.SetName( false ); + // at the moment, we use the name of the shell again (i.e. + // without path) since Koose thinks it is an error if the path + // is shown in url notation! + aStr = pDocShell->GetName(); + + mxLbDocs->append_text(aStr); + + if( pDocShell == pCurrentDocShell ) + aInfo.SetActive( true ); + else + aInfo.SetActive( false ); + + maDocList.push_back( aInfo ); + } + pSfxDocShell = SfxObjectShell::GetNext( *pSfxDocShell, [](const SfxObjectShell*){return true;}, false ); + } + } + mxLbDocs->set_active(nPos); +} + +OUString SdNavigatorWin::GetDragTypeSdBmpId(NavigatorDragType eDT) +{ + switch( eDT ) + { + case NAVIGATOR_DRAGTYPE_NONE: + return OUString(); + case NAVIGATOR_DRAGTYPE_URL: + return BMP_HYPERLINK; + case NAVIGATOR_DRAGTYPE_EMBEDDED: + return BMP_EMBEDDED; + case NAVIGATOR_DRAGTYPE_LINK: + return BMP_LINK; + default: OSL_FAIL( "No resource for DragType available!" ); + } + return OUString(); +} + +NavDocInfo* SdNavigatorWin::GetDocInfo() +{ + sal_uInt32 nPos = mxLbDocs->get_active(); + + if( mbDocImported ) + { + if( nPos == 0 ) + { + return nullptr; + } + nPos--; + } + + return nPos < maDocList.size() ? &(maDocList[ nPos ]) : nullptr; +} + +/** + * catch ESCAPE in order to end show + */ +IMPL_LINK(SdNavigatorWin, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bConsumed = false; + + if (KEY_ESCAPE == rKEvt.GetKeyCode().GetCode()) + { + // during drag'n'drop we just stop the drag but do not close the navigator + if (!SdPageObjsTLV::IsInDrag()) + { + ::sd::ViewShellBase* pBase = ::sd::ViewShellBase::GetViewShellBase( mpBindings->GetDispatcher()->GetFrame()); + if (pBase) + sd::SlideShow::Stop(*pBase); + bConsumed = true; + } + } + + return bConsumed; +} + +void SdNavigatorWin::SetDragImage() +{ + mxToolbox->set_item_icon_name("dragmode", GetDragTypeSdBmpId(meDragType)); +} + +/** + * ControllerItem for Navigator + */ +SdNavigatorControllerItem::SdNavigatorControllerItem( + sal_uInt16 _nId, + SdNavigatorWin* pNavWin, + SfxBindings* _pBindings, + const SdNavigatorWin::UpdateRequestFunctor& rUpdateRequest) + : SfxControllerItem( _nId, *_pBindings ), + pNavigatorWin( pNavWin ), + maUpdateRequest(rUpdateRequest) +{ +} + +void SdNavigatorControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, + SfxItemState eState, const SfxPoolItem* pItem ) +{ + if( eState < SfxItemState::DEFAULT || nSId != SID_NAVIGATOR_STATE ) + return; + + // only if doc in LB is the active + NavDocInfo* pInfo = pNavigatorWin->GetDocInfo(); + if( !(pInfo && pInfo->IsActive()) ) + return; + + if (::sd::DrawDocShell* pDrawDocShell = pInfo->GetDrawDocShell()) + { + const auto pDrawViewShell = + static_cast<::sd::DrawViewShell *>(pDrawDocShell->GetViewShell()); + if (pDrawViewShell) + { + bool bEditModePage(pDrawViewShell->GetEditMode() == EditMode::Page); + pNavigatorWin->mxToolbox->set_sensitive(bEditModePage); + pNavigatorWin->mxLbDocs->set_sensitive(bEditModePage); + pNavigatorWin->mxTlbObjects->set_sensitive(bEditModePage); + } + } + + const SfxUInt32Item& rStateItem = dynamic_cast(*pItem); + NavState nState = static_cast(rStateItem.GetValue()); + + // First + if (nState & NavState::BtnFirstEnabled && + !pNavigatorWin->mxToolbox->get_item_sensitive("first")) + pNavigatorWin->mxToolbox->set_item_sensitive("first", true); + if (nState & NavState::BtnFirstDisabled && + pNavigatorWin->mxToolbox->get_item_sensitive("first")) + pNavigatorWin->mxToolbox->set_item_sensitive("first", false); + + // Prev + if (nState & NavState::BtnPrevEnabled && + !pNavigatorWin->mxToolbox->get_item_sensitive("previous")) + pNavigatorWin->mxToolbox->set_item_sensitive("previous", true); + if (nState & NavState::BtnPrevDisabled && + pNavigatorWin->mxToolbox->get_item_sensitive("previous")) + pNavigatorWin->mxToolbox->set_item_sensitive("previous", false); + + // Last + if (nState & NavState::BtnLastEnabled && + !pNavigatorWin->mxToolbox->get_item_sensitive("last")) + pNavigatorWin->mxToolbox->set_item_sensitive("last", true); + if (nState & NavState::BtnLastDisabled && + pNavigatorWin->mxToolbox->get_item_sensitive("last")) + pNavigatorWin->mxToolbox->set_item_sensitive("last", false); + + // Next + if (nState & NavState::BtnNextEnabled && + !pNavigatorWin->mxToolbox->get_item_sensitive("next")) + pNavigatorWin->mxToolbox->set_item_sensitive("next", true); + if (nState & NavState::BtnNextDisabled && + pNavigatorWin->mxToolbox->get_item_sensitive("next")) + pNavigatorWin->mxToolbox->set_item_sensitive("next", false); + + if (nState & NavState::TableUpdate) + { + // InitTlb; is initiated by Slot + if (maUpdateRequest) + maUpdateRequest(); + } +} + +/** + * ControllerItem for Navigator to show page in TreeLB + */ +SdPageNameControllerItem::SdPageNameControllerItem( + sal_uInt16 _nId, + SdNavigatorWin* pNavWin, + SfxBindings* _pBindings) + : SfxControllerItem( _nId, *_pBindings ), + pNavigatorWin( pNavWin ) +{ +} + +void SdPageNameControllerItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, + SfxItemState eState, const SfxPoolItem* pItem ) +{ + if( eState < SfxItemState::DEFAULT || nSId != SID_NAVIGATOR_PAGENAME ) + return; + + // only if doc in LB is the active + NavDocInfo* pInfo = pNavigatorWin->GetDocInfo(); + if( !(pInfo && pInfo->IsActive()) ) + return; + + const SfxStringItem& rStateItem = dynamic_cast(*pItem); + const OUString& aPageName = rStateItem.GetValue(); + + if( !pNavigatorWin->mxTlbObjects->HasSelectedChildren( aPageName ) ) + { + if (pNavigatorWin->mxTlbObjects->get_selection_mode() == SelectionMode::Multiple) + { + // because otherwise it is always additional select + pNavigatorWin->mxTlbObjects->unselect_all(); + } + pNavigatorWin->mxTlbObjects->SelectEntry( aPageName ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/paragr.cxx b/sd/source/ui/dlg/paragr.cxx new file mode 100644 index 000000000..be9df558e --- /dev/null +++ b/sd/source/ui/dlg/paragr.cxx @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +namespace { + +class SdParagraphNumTabPage : public SfxTabPage +{ +public: + SdParagraphNumTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet); + static std::unique_ptr Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* rSet ); + + static WhichRangesContainer GetRanges(); + + virtual bool FillItemSet( SfxItemSet* rSet ) override; + virtual void Reset( const SfxItemSet* rSet ) override; + +private: + bool mbModified; + std::unique_ptr m_xNewStartCB; + std::unique_ptr m_xNewStartNumberCB; + std::unique_ptr m_xNewStartNF; + + DECL_LINK( ImplNewStartHdl, weld::Toggleable&, void ); +}; + +} + +SdParagraphNumTabPage::SdParagraphNumTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttr) + : SfxTabPage(pPage, pController, "modules/sdraw/ui/paranumberingtab.ui", "DrawParaNumbering", &rAttr) + , mbModified(false) + , m_xNewStartCB(m_xBuilder->weld_check_button("checkbuttonCB_NEW_START")) + , m_xNewStartNumberCB(m_xBuilder->weld_check_button("checkbuttonCB_NUMBER_NEW_START")) + , m_xNewStartNF(m_xBuilder->weld_spin_button("spinbuttonNF_NEW_START")) +{ + m_xNewStartCB->connect_toggled(LINK(this, SdParagraphNumTabPage, ImplNewStartHdl)); + m_xNewStartNumberCB->connect_toggled(LINK(this, SdParagraphNumTabPage, ImplNewStartHdl)); +} + +std::unique_ptr SdParagraphNumTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet * rAttrSet) +{ + return std::make_unique(pPage, pController, *rAttrSet); +} + +WhichRangesContainer SdParagraphNumTabPage::GetRanges() +{ + return WhichRangesContainer(svl::Items); +} + +bool SdParagraphNumTabPage::FillItemSet( SfxItemSet* rSet ) +{ + if (m_xNewStartCB->get_state_changed_from_saved() || + m_xNewStartNumberCB->get_state_changed_from_saved()|| + m_xNewStartNF->get_value_changed_from_saved()) + { + mbModified = true; + bool bNewStartChecked = TRISTATE_TRUE == m_xNewStartCB->get_state(); + bool bNumberNewStartChecked = TRISTATE_TRUE == m_xNewStartNumberCB->get_state(); + rSet->Put(SfxBoolItem(ATTR_NUMBER_NEWSTART, bNewStartChecked)); + + const sal_Int16 nStartAt = static_cast(m_xNewStartNF->get_value()); + rSet->Put(SfxInt16Item(ATTR_NUMBER_NEWSTART_AT, bNumberNewStartChecked && bNewStartChecked ? nStartAt : -1)); + } + + return mbModified; +} + +void SdParagraphNumTabPage::Reset( const SfxItemSet* rSet ) +{ + SfxItemState eItemState = rSet->GetItemState( ATTR_NUMBER_NEWSTART ); + if(eItemState > SfxItemState::DEFAULT ) + { + const SfxBoolItem& rStart = rSet->Get(ATTR_NUMBER_NEWSTART); + m_xNewStartCB->set_state( rStart.GetValue() ? TRISTATE_TRUE : TRISTATE_FALSE ); + } + else + { + m_xNewStartCB->set_state(TRISTATE_INDET); + m_xNewStartCB->set_sensitive(false); + } + m_xNewStartCB->save_state(); + + eItemState = rSet->GetItemState( ATTR_NUMBER_NEWSTART_AT); + if( eItemState > SfxItemState::DEFAULT ) + { + sal_Int16 nNewStart = rSet->Get(ATTR_NUMBER_NEWSTART_AT).GetValue(); + m_xNewStartNumberCB->set_active(-1 != nNewStart); + if(-1 == nNewStart) + nNewStart = 1; + + m_xNewStartNF->set_value(nNewStart); + } + else + { + m_xNewStartCB->set_state(TRISTATE_INDET); + } + ImplNewStartHdl(*m_xNewStartCB); + m_xNewStartNF->save_value(); + m_xNewStartNumberCB->save_state(); + mbModified = false; +} + +IMPL_LINK_NOARG(SdParagraphNumTabPage, ImplNewStartHdl, weld::Toggleable&, void) +{ + bool bEnable = m_xNewStartCB->get_active(); + m_xNewStartNumberCB->set_sensitive(bEnable); + m_xNewStartNF->set_sensitive(bEnable && m_xNewStartNumberCB->get_active()); +} + +SdParagraphDlg::SdParagraphDlg(weld::Window* pParent, const SfxItemSet* pAttr) + : SfxTabDialogController(pParent, "modules/sdraw/ui/drawparadialog.ui", + "DrawParagraphPropertiesDialog", pAttr) +{ + AddTabPage( "labelTP_PARA_STD", RID_SVXPAGE_STD_PARAGRAPH); + + if( SvtCJKOptions::IsAsianTypographyEnabled() ) + AddTabPage( "labelTP_PARA_ASIAN", RID_SVXPAGE_PARA_ASIAN); + else + RemoveTabPage( "labelTP_PARA_ASIAN" ); + + AddTabPage( "labelTP_PARA_ALIGN", RID_SVXPAGE_ALIGN_PARAGRAPH); + + static const bool bShowParaNumbering = ( getenv( "SD_SHOW_NUMBERING_PAGE" ) != nullptr ); + if( bShowParaNumbering ) + AddTabPage( "labelNUMBERING", SdParagraphNumTabPage::Create, SdParagraphNumTabPage::GetRanges ); + else + RemoveTabPage( "labelNUMBERING" ); + + AddTabPage("labelTP_TABULATOR", RID_SVXPAGE_TABULATOR); +} + +void SdParagraphDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "labelTP_PARA_STD") + { + aSet.Put(SfxUInt32Item(SID_SVXSTDPARAGRAPHTABPAGE_ABSLINEDIST, MM50/2)); + rPage.PageCreated(aSet); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/present.cxx b/sd/source/ui/dlg/present.cxx new file mode 100644 index 000000000..73932ba62 --- /dev/null +++ b/sd/source/ui/dlg/present.cxx @@ -0,0 +1,323 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +SdStartPresentationDlg::SdStartPresentationDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, + const std::vector &rPageNames, SdCustomShowList* pCSList) + : GenericDialogController(pWindow, "modules/simpress/ui/presentationdialog.ui", "PresentationDialog") + , pCustomShowList(pCSList) + , rOutAttrs(rInAttrs) + , mnMonitors(0) + , m_xRbtAll(m_xBuilder->weld_radio_button("allslides")) + , m_xRbtAtDia(m_xBuilder->weld_radio_button("from")) + , m_xRbtCustomshow(m_xBuilder->weld_radio_button("customslideshow")) + , m_xLbDias(m_xBuilder->weld_combo_box("from_cb")) + , m_xLbCustomshow(m_xBuilder->weld_combo_box("customslideshow_cb")) + , m_xRbtStandard(m_xBuilder->weld_radio_button("default")) + , m_xRbtWindow(m_xBuilder->weld_radio_button("window")) + , m_xRbtAuto(m_xBuilder->weld_radio_button("auto")) + , m_xTmfPause(m_xBuilder->weld_formatted_spin_button("pauseduration")) + , m_xFormatter(new weld::TimeFormatter(*m_xTmfPause)) + , m_xCbxAutoLogo(m_xBuilder->weld_check_button("showlogo")) + , m_xCbxManuel(m_xBuilder->weld_check_button("manualslides")) + , m_xCbxMousepointer(m_xBuilder->weld_check_button("pointervisible")) + , m_xCbxPen(m_xBuilder->weld_check_button("pointeraspen")) + , m_xCbxAnimationAllowed(m_xBuilder->weld_check_button("animationsallowed")) + , m_xCbxChangePage(m_xBuilder->weld_check_button("changeslidesbyclick")) + , m_xCbxAlwaysOnTop(m_xBuilder->weld_check_button("alwaysontop")) + , m_xFtMonitor(m_xBuilder->weld_label("presdisplay_label")) + , m_xLBMonitor(m_xBuilder->weld_combo_box("presdisplay_cb")) + , m_xMonitor(m_xBuilder->weld_label("monitor_str")) + , m_xAllMonitors(m_xBuilder->weld_label("allmonitors_str")) + , m_xMonitorExternal(m_xBuilder->weld_label("externalmonitor_str")) + , m_xExternal(m_xBuilder->weld_label("external_str")) +{ + m_xFormatter->SetExtFormat(ExtTimeFieldFormat::LongDuration); + m_xFormatter->EnableEmptyField(false); + + Link aLink( LINK( this, SdStartPresentationDlg, ChangeRangeHdl ) ); + + m_xRbtAll->connect_toggled( aLink ); + m_xRbtAtDia->connect_toggled( aLink ); + m_xRbtCustomshow->connect_toggled( aLink ); + + aLink = LINK( this, SdStartPresentationDlg, ClickWindowPresentationHdl ); + m_xRbtStandard->connect_toggled( aLink ); + m_xRbtWindow->connect_toggled( aLink ); + m_xRbtAuto->connect_toggled( aLink ); + + m_xTmfPause->connect_value_changed( LINK( this, SdStartPresentationDlg, ChangePauseHdl ) ); + + // fill Listbox with page names + for (const auto& rPageName : rPageNames) + m_xLbDias->append_text(rPageName); + + if( pCustomShowList ) + { + sal_uInt16 nPosToSelect = pCustomShowList->GetCurPos(); + SdCustomShow* pCustomShow; + // fill Listbox with CustomShows + for( pCustomShow = pCustomShowList->First(); + pCustomShow != nullptr; + pCustomShow = pCustomShowList->Next() ) + { + m_xLbCustomshow->append_text( pCustomShow->GetName() ); + } + m_xLbCustomshow->set_active( nPosToSelect ); + pCustomShowList->Seek( nPosToSelect ); + } + else + m_xRbtCustomshow->set_sensitive(false); + + if( static_cast( rOutAttrs.Get( ATTR_PRESENT_CUSTOMSHOW ) ).GetValue() && pCSList ) + m_xRbtCustomshow->set_active(true); + else if( static_cast( rOutAttrs.Get( ATTR_PRESENT_ALL ) ).GetValue() ) + m_xRbtAll->set_active(true); + else + m_xRbtAtDia->set_active(true); + + m_xLbDias->set_active_text( static_cast( rOutAttrs.Get( ATTR_PRESENT_DIANAME ) ).GetValue() ); + m_xCbxManuel->set_active( static_cast( rOutAttrs.Get( ATTR_PRESENT_MANUEL ) ).GetValue() ); + m_xCbxMousepointer->set_active( static_cast( rOutAttrs.Get( ATTR_PRESENT_MOUSE ) ).GetValue() ); + m_xCbxPen->set_active( static_cast( rOutAttrs.Get( ATTR_PRESENT_PEN ) ).GetValue() ); + m_xCbxAnimationAllowed->set_active( static_cast( rOutAttrs.Get( ATTR_PRESENT_ANIMATION_ALLOWED ) ).GetValue() ); + m_xCbxChangePage->set_active( static_cast( rOutAttrs.Get( ATTR_PRESENT_CHANGE_PAGE ) ).GetValue() ); + m_xCbxAlwaysOnTop->set_active( static_cast( rOutAttrs.Get( ATTR_PRESENT_ALWAYS_ON_TOP ) ).GetValue() ); + + const bool bEndless = static_cast( rOutAttrs.Get( ATTR_PRESENT_ENDLESS ) ).GetValue(); + const bool bWindow = !static_cast( rOutAttrs.Get( ATTR_PRESENT_FULLSCREEN ) ).GetValue(); + const tools::Long nPause = static_cast( rOutAttrs.Get( ATTR_PRESENT_PAUSE_TIMEOUT ) ).GetValue(); + + m_xFormatter->SetTime( tools::Time( 0, 0, nPause ) ); + // set cursor in timefield to end + m_xTmfPause->set_position(-1); + + m_xCbxAutoLogo->set_active( static_cast( rOutAttrs.Get( ATTR_PRESENT_SHOW_PAUSELOGO ) ).GetValue() ); + + if( bWindow ) + m_xRbtWindow->set_active(true); + else if( bEndless ) + m_xRbtAuto->set_active(true); + else + m_xRbtStandard->set_active(true); + + InitMonitorSettings(); + + ChangeRangeHdl(*m_xRbtCustomshow); + + ClickWindowPresentationHdl(*m_xRbtStandard); + ChangePause(); +} + +SdStartPresentationDlg::~SdStartPresentationDlg() +{ +} + +OUString SdStartPresentationDlg::GetDisplayName( sal_Int32 nDisplay, + DisplayType eType ) +{ + OUString aName; + + switch ( eType ) + { + case EXTERNAL_IS_NUMBER: + aName = m_xExternal->get_label(); + break; + case MONITOR_IS_EXTERNAL: + aName = m_xMonitorExternal->get_label(); + break; + default: + case MONITOR_NORMAL: + aName = m_xMonitor->get_label(); + break; + } + aName = aName.replaceFirst( "%1", OUString::number( nDisplay ) ); + + return aName; +} + +/// Store display index together with name in user data +sal_Int32 SdStartPresentationDlg::InsertDisplayEntry(const OUString &aName, + sal_Int32 nDisplay) +{ + m_xLBMonitor->append(OUString::number(nDisplay), aName); + return m_xLBMonitor->get_count() - 1; +} + +void SdStartPresentationDlg::InitMonitorSettings() +{ + try + { + m_xFtMonitor->show(); + m_xLBMonitor->show(); + + mnMonitors = Application::GetScreenCount(); + + if( mnMonitors <= 1 ) + { + m_xFtMonitor->set_sensitive( false ); + m_xLBMonitor->set_sensitive( false ); + } + else + { + bool bUnifiedDisplay = Application::IsUnifiedDisplay(); + sal_Int32 nExternalIndex = Application::GetDisplayExternalScreen(); + + sal_Int32 nSelectedIndex (-1); + sal_Int32 nDefaultExternalIndex (-1); + const sal_Int32 nDefaultSelectedDisplay ( + static_cast( rOutAttrs.Get( ATTR_PRESENT_DISPLAY ) ).GetValue()); + + // Un-conditionally add a version for '0' the default external display + sal_Int32 nInsertedEntry; + + // Initial entry - the auto-detected external monitor + OUString aName = GetDisplayName( nExternalIndex + 1, EXTERNAL_IS_NUMBER); + nInsertedEntry = InsertDisplayEntry( aName, 0 ); + if( nDefaultSelectedDisplay == 0) + nSelectedIndex = nInsertedEntry; + + // The user data contains the real setting + for( sal_Int32 nDisplay = 0; nDisplay < mnMonitors; nDisplay++ ) + { + aName = GetDisplayName( nDisplay + 1, + nDisplay == nExternalIndex ? + MONITOR_IS_EXTERNAL : MONITOR_NORMAL ); + nInsertedEntry = InsertDisplayEntry( aName, nDisplay + 1 ); + + // Remember the index of the default selection. + if( nDisplay + 1 == nDefaultSelectedDisplay ) + nSelectedIndex = nInsertedEntry; + + // Remember index of the default display. + if( nDisplay == nExternalIndex ) + nDefaultExternalIndex = nInsertedEntry; + } + + if( bUnifiedDisplay ) + { + nInsertedEntry = InsertDisplayEntry( m_xAllMonitors->get_label(), -1 ); + if( nDefaultSelectedDisplay == -1 ) + nSelectedIndex = nInsertedEntry; + } + + if (nSelectedIndex < 0) + { + if (nExternalIndex < 0) + nSelectedIndex = 0; + else + nSelectedIndex = nDefaultExternalIndex; + } + + m_xLBMonitor->set_active(nSelectedIndex); + } + } + catch( Exception& ) + { + } +} + +/** + * sets the selected attributes of the dialog + */ +void SdStartPresentationDlg::GetAttr( SfxItemSet& rAttr ) +{ + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_ALL, m_xRbtAll->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_CUSTOMSHOW, m_xRbtCustomshow->get_active() ) ); + rAttr.Put( SfxStringItem ( ATTR_PRESENT_DIANAME, m_xLbDias->get_active_text() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_MANUEL, m_xCbxManuel->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_MOUSE, m_xCbxMousepointer->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_PEN, m_xCbxPen->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_ANIMATION_ALLOWED, m_xCbxAnimationAllowed->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_CHANGE_PAGE, m_xCbxChangePage->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_ALWAYS_ON_TOP, m_xCbxAlwaysOnTop->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_FULLSCREEN, !m_xRbtWindow->get_active() ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_ENDLESS, m_xRbtAuto->get_active() ) ); + rAttr.Put( SfxUInt32Item ( ATTR_PRESENT_PAUSE_TIMEOUT, m_xFormatter->GetTime().GetMSFromTime() / 1000 ) ); + rAttr.Put( SfxBoolItem ( ATTR_PRESENT_SHOW_PAUSELOGO, m_xCbxAutoLogo->get_active() ) ); + + int nPos = m_xLBMonitor->get_active(); + if (nPos != -1) + rAttr.Put(SfxInt32Item(ATTR_PRESENT_DISPLAY, m_xLBMonitor->get_id(nPos).toInt32())); + + nPos = m_xLbCustomshow->get_active(); + if (nPos != -1) + pCustomShowList->Seek( nPos ); +} + +/** + * Handler: Enabled/Disabled Listbox "Dias" + */ +IMPL_LINK_NOARG(SdStartPresentationDlg, ChangeRangeHdl, weld::Toggleable&, void) +{ + m_xLbDias->set_sensitive( m_xRbtAtDia->get_active() ); + m_xLbCustomshow->set_sensitive( m_xRbtCustomshow->get_active() ); +} + +/** + * Handler: Enabled/Disabled Checkbox "AlwaysOnTop" + */ +IMPL_LINK_NOARG(SdStartPresentationDlg, ClickWindowPresentationHdl, weld::Toggleable&, void) +{ + const bool bAuto = m_xRbtAuto->get_active(); + const bool bWindow = m_xRbtWindow->get_active(); + + m_xTmfPause->set_sensitive( bAuto ); + m_xCbxAutoLogo->set_sensitive( bAuto && ( m_xFormatter->GetTime().GetMSFromTime() > 0 ) ); + + const bool bDisplay = !bWindow && ( mnMonitors > 1 ); + m_xFtMonitor->set_sensitive( bDisplay ); + m_xLBMonitor->set_sensitive( bDisplay ); + + if( bWindow ) + { + m_xCbxAlwaysOnTop->set_sensitive(false); + m_xCbxAlwaysOnTop->set_active(false); + } + else + m_xCbxAlwaysOnTop->set_sensitive(true); +} + +/** + * Handler: Enabled/Disabled Checkbox "AlwaysOnTop" + */ +IMPL_LINK_NOARG(SdStartPresentationDlg, ChangePauseHdl, weld::FormattedSpinButton&, void) +{ + ChangePause(); +} + +void SdStartPresentationDlg::ChangePause() +{ + m_xCbxAutoLogo->set_sensitive(m_xRbtAuto->get_active() && ( m_xFormatter->GetTime().GetMSFromTime() > 0 )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/prltempl.cxx b/sd/source/ui/dlg/prltempl.cxx new file mode 100644 index 000000000..869f13c83 --- /dev/null +++ b/sd/source/ui/dlg/prltempl.cxx @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define IS_OUTLINE(x) (x >= PresentationObjects::Outline_1 && x <= PresentationObjects::Outline_9) + +/** + * Constructor of Tab dialog: appends pages to the dialog + */ +SdPresLayoutTemplateDlg::SdPresLayoutTemplateDlg(SfxObjectShell const * pDocSh, + weld::Window* pParent, + bool bBackground, + SfxStyleSheetBase& rStyleBase, + PresentationObjects _ePO, + SfxStyleSheetBasePool* pSSPool) + : SfxTabDialogController(pParent, "modules/sdraw/ui/drawprtldialog.ui", "DrawPRTLDialog") + , mpDocShell(pDocSh) + , ePO(_ePO) + , aInputSet(*rStyleBase.GetItemSet().GetPool(), svl::Items) +{ + const SfxItemSet* pOrgSet(&rStyleBase.GetItemSet()); + + if( IS_OUTLINE(ePO)) + { + // Unfortunately, the Itemsets of our style sheets are not discrete... + const WhichRangesContainer& pPtr = pOrgSet->GetRanges(); + sal_uInt16 p1, p2; + for( sal_Int32 i = 0; i < pPtr.size(); ++i ) + { + p1 = pPtr[i].first; + p2 = pPtr[i].second; + + // first, we make it discrete + while(i < pPtr.size() - 1 && (pPtr[i+1].first - p2 == 1)) + { + p2 = pPtr[i+1].second; + ++i; + } + aInputSet.MergeRange( p1, p2 ); + } + + aInputSet.Put( rStyleBase.GetItemSet() ); + + // need parent-relationship + const SfxItemSet* pParentItemSet = rStyleBase.GetItemSet().GetParent(); + if( pParentItemSet ) + aInputSet.SetParent( pParentItemSet ); + + pOutSet.reset( new SfxItemSet( rStyleBase.GetItemSet() ) ); + pOutSet->ClearItem(); + + // If there is no bullet item in this stylesheet, we get it + // from 'Outline 1' style sheet. + const SfxPoolItem *pItem = nullptr; + if( SfxItemState::SET != aInputSet.GetItemState(EE_PARA_NUMBULLET, false, &pItem )) + { + OUString aStyleName(SdResId(STR_PSEUDOSHEET_OUTLINE) + " 1"); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( aStyleName, SfxStyleFamily::Pseudo); + + if(pFirstStyleSheet) + if( SfxItemState::SET == pFirstStyleSheet->GetItemSet().GetItemState(EE_PARA_NUMBULLET, false, &pItem) ) + aInputSet.Put( *pItem ); + } + + // preselect selected layer in dialog + aInputSet.Put( SfxUInt16Item( SID_PARAM_CUR_NUM_LEVEL, 1<GetItem( SID_COLOR_TABLE ); + SvxGradientListItem const *pGradientListItem = mpDocShell->GetItem( SID_GRADIENT_LIST ); + SvxBitmapListItem const *pBitmapListItem = mpDocShell->GetItem( SID_BITMAP_LIST ); + SvxPatternListItem const *pPatternListItem = mpDocShell->GetItem( SID_PATTERN_LIST ); + SvxHatchListItem const *pHatchListItem = mpDocShell->GetItem( SID_HATCH_LIST ); + SvxDashListItem const *pDashListItem = mpDocShell->GetItem( SID_DASH_LIST ); + SvxLineEndListItem const *pLineEndListItem = mpDocShell->GetItem( SID_LINEEND_LIST ); + + pColorTab = pColorListItem->GetColorList(); + pDashList = pDashListItem->GetDashList(); + pLineEndList = pLineEndListItem->GetLineEndList(); + pGradientList = pGradientListItem->GetGradientList(); + pHatchingList = pHatchListItem->GetHatchList(); + pBitmapList = pBitmapListItem->GetBitmapList(); + pPatternList = pPatternListItem->GetPatternList(); + + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + + AddTabPage( "RID_SVXPAGE_LINE", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_LINE ), nullptr ); + AddTabPage( "RID_SVXPAGE_AREA", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_AREA ), nullptr ); + AddTabPage( "RID_SVXPAGE_SHADOW", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_SHADOW ), nullptr ); + AddTabPage( "RID_SVXPAGE_TRANSPARENCE", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TRANSPARENCE ), nullptr ); + AddTabPage( "RID_SVXPAGE_CHAR_NAME", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_NAME ), nullptr ); + AddTabPage( "RID_SVXPAGE_CHAR_EFFECTS", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_EFFECTS ), nullptr ); + AddTabPage( "RID_SVXPAGE_STD_PARAGRAPH", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_STD_PARAGRAPH ), nullptr ); + AddTabPage( "RID_SVXPAGE_TEXTATTR", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TEXTATTR ), nullptr ); + AddTabPage( "RID_SVXPAGE_PICK_BULLET", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PICK_BULLET ), nullptr ); + AddTabPage( "RID_SVXPAGE_PICK_SINGLE_NUM", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PICK_SINGLE_NUM ), nullptr ); + AddTabPage( "RID_SVXPAGE_PICK_BMP", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PICK_BMP ), nullptr ); + AddTabPage( "RID_SVXPAGE_NUM_OPTIONS", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_NUM_OPTIONS ), nullptr ); + AddTabPage( "RID_SVXPAGE_TABULATOR", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_TABULATOR ), nullptr ); + AddTabPage( "RID_SVXPAGE_PARA_ASIAN", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PARA_ASIAN ), nullptr ); + AddTabPage( "RID_SVXPAGE_ALIGN_PARAGRAPH", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_ALIGN_PARAGRAPH ), nullptr ); + AddTabPage( "RID_SVXPAGE_BKG", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), nullptr); + + if( !SvtCJKOptions::IsAsianTypographyEnabled() ) + RemoveTabPage( "RID_SVXPAGE_PARA_ASIAN" ); + + if (bBackground) + { + RemoveTabPage( "RID_SVXPAGE_LINE"); + + RemoveTabPage( "RID_SVXPAGE_SHADOW"); + RemoveTabPage( "RID_SVXPAGE_TRANSPARENCE"); + RemoveTabPage( "RID_SVXPAGE_CHAR_NAME"); + RemoveTabPage( "RID_SVXPAGE_CHAR_EFFECTS"); + RemoveTabPage( "RID_SVXPAGE_STD_PARAGRAPH"); + RemoveTabPage( "RID_SVXPAGE_TEXTATTR"); + RemoveTabPage( "RID_SVXPAGE_PICK_BULLET"); + RemoveTabPage( "RID_SVXPAGE_PICK_SINGLE_NUM"); + RemoveTabPage( "RID_SVXPAGE_PICK_BMP"); + RemoveTabPage( "RID_SVXPAGE_NUM_OPTIONS"); + RemoveTabPage( "RID_SVXPAGE_TABULATOR"); + RemoveTabPage( "RID_SVXPAGE_ALIGN_PARAGRAPH"); + RemoveTabPage( "RID_SVXPAGE_PARA_ASIAN" ); + RemoveTabPage( "RID_SVXPAGE_BKG" ); + } + + // set title and add corresponding pages to dialog + OUString aTitle; + + switch( ePO ) + { + case PresentationObjects::Title: + aTitle = SdResId(STR_PSEUDOSHEET_TITLE); + break; + + case PresentationObjects::Subtitle: + aTitle = SdResId(STR_PSEUDOSHEET_SUBTITLE); + break; + + case PresentationObjects::Background: + aTitle = SdResId(STR_PSEUDOSHEET_BACKGROUND); + break; + + case PresentationObjects::BackgroundObjects: + aTitle = SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS); + break; + + case PresentationObjects::Outline_1: + case PresentationObjects::Outline_2: + case PresentationObjects::Outline_3: + case PresentationObjects::Outline_4: + case PresentationObjects::Outline_5: + case PresentationObjects::Outline_6: + case PresentationObjects::Outline_7: + case PresentationObjects::Outline_8: + case PresentationObjects::Outline_9: + aTitle = SdResId(STR_PSEUDOSHEET_OUTLINE) + " " + + OUString::number( static_cast(ePO) - static_cast(PresentationObjects::Outline_1) + 1 ); + break; + + case PresentationObjects::Notes: + aTitle = SdResId(STR_PSEUDOSHEET_NOTES); + break; + } + m_xDialog->set_title(aTitle); +} + +SdPresLayoutTemplateDlg::~SdPresLayoutTemplateDlg() +{ +} + +void SdPresLayoutTemplateDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(aInputSet.GetPool())); + + if (rId == "RID_SVXPAGE_LINE") + { + aSet.Put (SvxColorListItem(pColorTab,SID_COLOR_TABLE)); + aSet.Put (SvxDashListItem(pDashList,SID_DASH_LIST)); + aSet.Put (SvxLineEndListItem(pLineEndList,SID_LINEEND_LIST)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_AREA") + { + aSet.Put (SvxColorListItem(pColorTab,SID_COLOR_TABLE)); + aSet.Put (SvxGradientListItem(pGradientList,SID_GRADIENT_LIST)); + aSet.Put (SvxHatchListItem(pHatchingList,SID_HATCH_LIST)); + aSet.Put (SvxBitmapListItem(pBitmapList,SID_BITMAP_LIST)); + aSet.Put (SvxPatternListItem(pPatternList,SID_PATTERN_LIST)); + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + aSet.Put (SfxUInt16Item(SID_TABPAGE_POS,0)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_SHADOW") + { + aSet.Put (SvxColorListItem(pColorTab,SID_COLOR_TABLE)); + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_TRANSPARENCE") + { + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_CHAR_NAME") + { + SvxFontListItem aItem(*static_cast(mpDocShell->GetItem( SID_ATTR_CHAR_FONTLIST) ) ); + aSet.Put (SvxFontListItem( aItem.GetFontList(), SID_ATTR_CHAR_FONTLIST)); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_CHAR_EFFECTS") + { + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_TEXTATTR") + { + aSet.Put(CntUInt16Item(SID_SVXTEXTATTRPAGE_OBJKIND, static_cast(SdrObjKind::Text))); + rPage.PageCreated(aSet); + } + else if (rId == "RID_SVXPAGE_BKG") + { + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE,static_cast(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR))); + rPage.PageCreated(aSet); + } +} + +const SfxItemSet* SdPresLayoutTemplateDlg::GetOutputItemSet() const +{ + if (pOutSet) + { + pOutSet->Put(*SfxTabDialogController::GetOutputItemSet()); + + const SvxNumBulletItem *pSvxNumBulletItem = pOutSet->GetItemIfSet(EE_PARA_NUMBULLET, false); + if (pSvxNumBulletItem) + SdBulletMapper::MapFontsInNumRule( const_cast(pSvxNumBulletItem->GetNumRule()), *pOutSet ); + return pOutSet.get(); + } + else + return SfxTabDialogController::GetOutputItemSet(); +} + +sal_uInt16 SdPresLayoutTemplateDlg::GetOutlineLevel() const +{ + switch( ePO ) + { + case PresentationObjects::Outline_1: return 0; + case PresentationObjects::Outline_2: return 1; + case PresentationObjects::Outline_3: return 2; + case PresentationObjects::Outline_4: return 3; + case PresentationObjects::Outline_5: return 4; + case PresentationObjects::Outline_6: return 5; + case PresentationObjects::Outline_7: return 6; + case PresentationObjects::Outline_8: return 7; + case PresentationObjects::Outline_9: return 8; + default: + SAL_WARN( "sd", "Wrong Po! [CL]"); + } + return 0; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/prntopts.cxx b/sd/source/ui/dlg/prntopts.cxx new file mode 100644 index 000000000..4b50875bb --- /dev/null +++ b/sd/source/ui/dlg/prntopts.cxx @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +/** + * dialog to adjust print options + */ +SdPrintOptions::SdPrintOptions(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SfxTabPage(pPage, pController, "modules/simpress/ui/prntopts.ui", "prntopts", &rInAttrs) + , m_xFrmContent(m_xBuilder->weld_frame("contentframe")) + , m_xCbxDraw(m_xBuilder->weld_check_button("drawingcb")) + , m_xCbxNotes(m_xBuilder->weld_check_button("notecb")) + , m_xCbxHandout(m_xBuilder->weld_check_button("handoutcb")) + , m_xCbxOutline(m_xBuilder->weld_check_button("outlinecb")) + , m_xRbtColor(m_xBuilder->weld_radio_button("defaultrb")) + , m_xRbtGrayscale(m_xBuilder->weld_radio_button("grayscalerb")) + , m_xRbtBlackWhite(m_xBuilder->weld_radio_button("blackwhiterb")) + , m_xCbxPagename(m_xBuilder->weld_check_button("pagenmcb")) + , m_xCbxDate(m_xBuilder->weld_check_button("datecb")) + , m_xCbxTime(m_xBuilder->weld_check_button("timecb")) + , m_xCbxHiddenPages(m_xBuilder->weld_check_button("hiddenpgcb")) + , m_xRbtDefault(m_xBuilder->weld_radio_button("pagedefaultrb")) + , m_xRbtPagesize(m_xBuilder->weld_radio_button("fittopgrb")) + , m_xRbtPagetile(m_xBuilder->weld_radio_button("tilepgrb")) + , m_xRbtBooklet(m_xBuilder->weld_radio_button("brouchrb")) + , m_xCbxFront(m_xBuilder->weld_check_button("frontcb")) + , m_xCbxBack(m_xBuilder->weld_check_button("backcb")) + , m_xCbxPaperbin(m_xBuilder->weld_check_button("papertryfrmprntrcb")) +{ + Link aLink = LINK( this, SdPrintOptions, ClickBookletHdl ); + m_xRbtDefault->connect_toggled( aLink ); + m_xRbtPagesize->connect_toggled( aLink ); + m_xRbtPagetile->connect_toggled( aLink ); + m_xRbtBooklet->connect_toggled( aLink ); + + aLink = LINK( this, SdPrintOptions, ClickCheckboxHdl ); + m_xCbxDraw->connect_toggled( aLink ); + m_xCbxNotes->connect_toggled( aLink ); + m_xCbxHandout->connect_toggled( aLink ); + m_xCbxOutline->connect_toggled( aLink ); + +#ifndef MACOSX + SetDrawMode(); +#endif +} + +SdPrintOptions::~SdPrintOptions() +{ +} + +bool SdPrintOptions::FillItemSet( SfxItemSet* rAttrs ) +{ + if( m_xCbxDraw->get_state_changed_from_saved() || + m_xCbxNotes->get_state_changed_from_saved() || + m_xCbxHandout->get_state_changed_from_saved() || + m_xCbxOutline->get_state_changed_from_saved() || + m_xCbxDate->get_state_changed_from_saved() || + m_xCbxTime->get_state_changed_from_saved() || + m_xCbxPagename->get_state_changed_from_saved() || + m_xCbxHiddenPages->get_state_changed_from_saved() || + m_xRbtPagesize->get_state_changed_from_saved() || + m_xRbtPagetile->get_state_changed_from_saved() || + m_xRbtBooklet->get_state_changed_from_saved() || + m_xCbxFront->get_state_changed_from_saved() || + m_xCbxBack->get_state_changed_from_saved() || + m_xCbxPaperbin->get_state_changed_from_saved() || + m_xRbtColor->get_state_changed_from_saved() || + m_xRbtGrayscale->get_state_changed_from_saved()|| + m_xRbtBlackWhite->get_state_changed_from_saved()) + { + SdOptionsPrintItem aOptions; + + aOptions.GetOptionsPrint().SetDraw( m_xCbxDraw->get_active() ); + aOptions.GetOptionsPrint().SetNotes( m_xCbxNotes->get_active() ); + aOptions.GetOptionsPrint().SetHandout( m_xCbxHandout->get_active() ); + aOptions.GetOptionsPrint().SetOutline( m_xCbxOutline->get_active() ); + aOptions.GetOptionsPrint().SetDate( m_xCbxDate->get_active() ); + aOptions.GetOptionsPrint().SetTime( m_xCbxTime->get_active() ); + aOptions.GetOptionsPrint().SetPagename( m_xCbxPagename->get_active() ); + aOptions.GetOptionsPrint().SetHiddenPages( m_xCbxHiddenPages->get_active() ); + aOptions.GetOptionsPrint().SetPagesize( m_xRbtPagesize->get_active() ); + aOptions.GetOptionsPrint().SetPagetile( m_xRbtPagetile->get_active() ); + aOptions.GetOptionsPrint().SetBooklet( m_xRbtBooklet->get_active() ); + aOptions.GetOptionsPrint().SetFrontPage( m_xCbxFront->get_active() ); + aOptions.GetOptionsPrint().SetBackPage( m_xCbxBack->get_active() ); + aOptions.GetOptionsPrint().SetPaperbin( m_xCbxPaperbin->get_active() ); + + sal_uInt16 nQuality = 0; // Standard, also Color + if( m_xRbtGrayscale->get_active() ) + nQuality = 1; + if( m_xRbtBlackWhite->get_active() ) + nQuality = 2; + aOptions.GetOptionsPrint().SetOutputQuality( nQuality ); + + rAttrs->Put( aOptions ); + + return true; + } + return false; +} + +void SdPrintOptions::Reset( const SfxItemSet* rAttrs ) +{ + const SdOptionsPrintItem* pPrintOpts = rAttrs->GetItemIfSet( ATTR_OPTIONS_PRINT, false); + if( pPrintOpts ) + { + m_xCbxDraw->set_active( pPrintOpts->GetOptionsPrint().IsDraw() ); + m_xCbxNotes->set_active( pPrintOpts->GetOptionsPrint().IsNotes() ); + m_xCbxHandout->set_active( pPrintOpts->GetOptionsPrint().IsHandout() ); + m_xCbxOutline->set_active( pPrintOpts->GetOptionsPrint().IsOutline() ); + m_xCbxDate->set_active( pPrintOpts->GetOptionsPrint().IsDate() ); + m_xCbxTime->set_active( pPrintOpts->GetOptionsPrint().IsTime() ); + m_xCbxPagename->set_active( pPrintOpts->GetOptionsPrint().IsPagename() ); + m_xCbxHiddenPages->set_active( pPrintOpts->GetOptionsPrint().IsHiddenPages() ); + m_xRbtPagesize->set_active( pPrintOpts->GetOptionsPrint().IsPagesize() ); + m_xRbtPagetile->set_active( pPrintOpts->GetOptionsPrint().IsPagetile() ); + m_xRbtBooklet->set_active( pPrintOpts->GetOptionsPrint().IsBooklet() ); + m_xCbxFront->set_active( pPrintOpts->GetOptionsPrint().IsFrontPage() ); + m_xCbxBack->set_active( pPrintOpts->GetOptionsPrint().IsBackPage() ); + m_xCbxPaperbin->set_active( pPrintOpts->GetOptionsPrint().IsPaperbin() ); + + if( !m_xRbtPagesize->get_active() && + !m_xRbtPagetile->get_active() && + !m_xRbtBooklet->get_active() ) + { + m_xRbtDefault->set_active(true); + } + + sal_uInt16 nQuality = pPrintOpts->GetOptionsPrint().GetOutputQuality(); + if( nQuality == 0 ) + m_xRbtColor->set_active(true); + else if( nQuality == 1 ) + m_xRbtGrayscale->set_active(true); + else + m_xRbtBlackWhite->set_active(true); + } + m_xCbxDraw->save_state(); + m_xCbxNotes->save_state(); + m_xCbxHandout->save_state(); + m_xCbxOutline->save_state(); + m_xCbxDate->save_state(); + m_xCbxTime->save_state(); + m_xCbxPagename->save_state(); + m_xCbxHiddenPages->save_state(); + m_xRbtPagesize->save_state(); + m_xRbtPagetile->save_state(); + m_xRbtBooklet->save_state(); + m_xCbxPaperbin->save_state(); + m_xRbtColor->save_state(); + m_xRbtGrayscale->save_state(); + m_xRbtBlackWhite->save_state(); + + updateControls(); +} + +std::unique_ptr SdPrintOptions::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rOutAttrs ) +{ + return std::make_unique( pPage, pController, *rOutAttrs ); +} + +IMPL_LINK(SdPrintOptions, ClickCheckboxHdl, weld::Toggleable&, rCbx, void) +{ + // there must be at least one of them checked + if( !m_xCbxDraw->get_active() && !m_xCbxNotes->get_active() && !m_xCbxOutline->get_active() && !m_xCbxHandout->get_active() ) + rCbx.set_active(true); + + updateControls(); +} + +IMPL_LINK_NOARG(SdPrintOptions, ClickBookletHdl, weld::Toggleable&, void) +{ + updateControls(); +} + +void SdPrintOptions::updateControls() +{ + m_xCbxFront->set_sensitive(m_xRbtBooklet->get_active()); + m_xCbxBack->set_sensitive(m_xRbtBooklet->get_active()); + + m_xCbxDate->set_sensitive( !m_xRbtBooklet->get_active() ); + m_xCbxTime->set_sensitive( !m_xRbtBooklet->get_active() ); + + m_xCbxPagename->set_sensitive( !m_xRbtBooklet->get_active() && (m_xCbxDraw->get_active() || m_xCbxNotes->get_active() || m_xCbxOutline->get_active()) ); +} + +void SdPrintOptions::SetDrawMode() +{ + if (m_xCbxNotes->get_visible()) + { + m_xFrmContent->hide(); + } +} + +void SdPrintOptions::PageCreated (const SfxAllItemSet& +#ifdef MACOSX + aSet +#endif + ) +{ +#ifdef MACOSX + const SfxUInt32Item* pFlagItem = aSet.GetItem(SID_SDMODE_FLAG, false); + if (pFlagItem) + { + sal_uInt32 nFlags=pFlagItem->GetValue(); + if ( ( nFlags & SD_DRAW_MODE ) == SD_DRAW_MODE ) + SetDrawMode(); + } +#else + SetDrawMode(); +#endif +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sdabstdlg.cxx b/sd/source/ui/dlg/sdabstdlg.cxx new file mode 100644 index 000000000..2b686a3e8 --- /dev/null +++ b/sd/source/ui/dlg/sdabstdlg.cxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +typedef SdAbstractDialogFactory* (*SdFuncPtrCreateDialogFactory)(); + +#ifndef DISABLE_DYNLOADING + +extern "C" { +static void thisModule() {} +} + +#else + +extern "C" SdAbstractDialogFactory* SdCreateDialogFactory(); + +#endif + +SdAbstractDialogFactory* SdAbstractDialogFactory::Create() +{ + SdFuncPtrCreateDialogFactory fp = nullptr; +#ifndef DISABLE_DYNLOADING + static ::osl::Module aDialogLibrary; + static constexpr OUStringLiteral sLibName(u"" SDUI_DLL_NAME); + if (aDialogLibrary.is() || aDialogLibrary.loadRelative(&thisModule, sLibName)) + fp = reinterpret_cast( + aDialogLibrary.getFunctionSymbol("SdCreateDialogFactory")); +#else + fp = SdCreateDialogFactory; +#endif + if (fp) + return fp(); + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sddlgfact.cxx b/sd/source/ui/dlg/sddlgfact.cxx new file mode 100644 index 000000000..c526df75d --- /dev/null +++ b/sd/source/ui/dlg/sddlgfact.cxx @@ -0,0 +1,739 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "sddlgfact.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RemoteDialog.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "PhotoAlbumDialog.hxx" +#include + +short AbstractSvxBulletAndPositionDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short SdAbstractGenericDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +BitmapEx SdAbstractGenericDialog_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString SdAbstractGenericDialog_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +const SfxItemSet* AbstractSvxBulletAndPositionDlg_Impl::GetOutputItemSet( SfxItemSet* pSet ) const +{ + return m_xDlg->GetOutputItemSet( pSet ); +} + +bool AbstractSvxBulletAndPositionDlg_Impl::IsApplyToMaster() +{ + return m_xDlg->IsApplyToMaster(); +} + +bool AbstractSvxBulletAndPositionDlg_Impl::IsSlideScope() +{ + return m_xDlg->IsSlideScope(); +} + +short AbstractCopyDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdCustomShowDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short SdPresLayoutTemplateDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool SdPresLayoutTemplateDlg_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +short AbstractSdModifyFieldDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdSnapLineDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdInsertLayerDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdInsertPagesObjsDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractMorphDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdStartPresDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdPresLayoutDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short SdAbstractSfxDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdVectorizeDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractSdPublishingDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +short AbstractHeaderFooterDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool AbstractHeaderFooterDialog_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return weld::DialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +BitmapEx AbstractHeaderFooterDialog_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractHeaderFooterDialog_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +short AbstractBulletDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +bool AbstractBulletDialog_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +AbstractBreakDlg_Impl::AbstractBreakDlg_Impl(std::unique_ptr<::sd::BreakDlg> pDlg) + : m_xDlg(std::move(pDlg)) +{ +} + +short AbstractBreakDlg_Impl::Execute() +{ + return m_xDlg->run(); +} + +BitmapEx AbstractBreakDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractBreakDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +AbstractMasterLayoutDialog_Impl::AbstractMasterLayoutDialog_Impl(std::unique_ptr<::sd::MasterLayoutDialog> pDlg) + : m_xDlg(std::move(pDlg)) +{ +} + +short AbstractMasterLayoutDialog_Impl::Execute() +{ + return m_xDlg->run(); +} + +BitmapEx AbstractMasterLayoutDialog_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractMasterLayoutDialog_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractCopyDlg_Impl::GetAttr( SfxItemSet& rOutAttrs ) +{ + m_xDlg->GetAttr( rOutAttrs ); +} + +BitmapEx AbstractCopyDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractCopyDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +bool AbstractSdCustomShowDlg_Impl::IsCustomShow() const +{ + return m_xDlg->IsCustomShow(); +} + +BitmapEx AbstractSdCustomShowDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdCustomShowDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +short SdAbstractTabController_Impl::Execute() +{ + return m_xDlg->run(); +} + +void SdAbstractTabController_Impl::SetCurPageId( const OString &rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* SdAbstractTabController_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +WhichRangesContainer SdAbstractTabController_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges( pItem ); +} + +void SdAbstractTabController_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet( pInSet ); +} + +bool SdAbstractTabController_Impl::StartExecuteAsync(AsyncContext &rCtx) +{ + return SfxTabDialogController::runAsync(m_xDlg, rCtx.maEndDialogFn); +} + +//From class Window. +void SdAbstractTabController_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +BitmapEx SdAbstractTabController_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString SdAbstractTabController_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractBulletDialog_Impl::SetCurPageId( const OString& rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* AbstractBulletDialog_Impl::GetOutputItemSet() const +{ + return static_cast< ::sd::OutlineBulletDlg*>(m_xDlg.get())->GetBulletOutputItemSet(); +} + +WhichRangesContainer AbstractBulletDialog_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges(pItem); +} + +void AbstractBulletDialog_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet(pInSet); +} + +void AbstractBulletDialog_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +BitmapEx AbstractBulletDialog_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractBulletDialog_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void SdPresLayoutTemplateDlg_Impl::SetCurPageId( const OString& rName ) +{ + m_xDlg->SetCurPageId( rName ); +} + +const SfxItemSet* SdPresLayoutTemplateDlg_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +WhichRangesContainer SdPresLayoutTemplateDlg_Impl::GetInputRanges(const SfxItemPool& pItem ) +{ + return m_xDlg->GetInputRanges( pItem ); +} + +void SdPresLayoutTemplateDlg_Impl::SetInputSet( const SfxItemSet* pInSet ) +{ + m_xDlg->SetInputSet( pInSet ); +} + +void SdPresLayoutTemplateDlg_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +BitmapEx SdPresLayoutTemplateDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString SdPresLayoutTemplateDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +SvxFieldData* AbstractSdModifyFieldDlg_Impl::GetField() +{ + return m_xDlg->GetField(); +} + +SfxItemSet AbstractSdModifyFieldDlg_Impl::GetItemSet() +{ + return m_xDlg->GetItemSet(); +} + +BitmapEx AbstractSdModifyFieldDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdModifyFieldDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdSnapLineDlg_Impl::GetAttr(SfxItemSet& rOutAttrs) +{ + m_xDlg->GetAttr(rOutAttrs); +} + +void AbstractSdSnapLineDlg_Impl::HideRadioGroup() +{ + m_xDlg->HideRadioGroup(); +} + +void AbstractSdSnapLineDlg_Impl::HideDeleteBtn() +{ + m_xDlg->HideDeleteBtn(); +} + +void AbstractSdSnapLineDlg_Impl::SetInputFields(bool bEnableX, bool bEnableY) +{ + m_xDlg->SetInputFields(bEnableX, bEnableY); +} + +void AbstractSdSnapLineDlg_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +BitmapEx AbstractSdSnapLineDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdSnapLineDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdInsertLayerDlg_Impl::GetAttr( SfxItemSet& rOutAttrs ) +{ + m_xDlg->GetAttr(rOutAttrs); +} + +void AbstractSdInsertLayerDlg_Impl::SetHelpId( const OString& rHelpId ) +{ + m_xDlg->set_help_id(rHelpId); +} + +BitmapEx AbstractSdInsertLayerDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdInsertLayerDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +std::vector AbstractSdInsertPagesObjsDlg_Impl::GetList(const sal_uInt16 nType) +{ + return m_xDlg->GetList(nType); +} + +bool AbstractSdInsertPagesObjsDlg_Impl::IsLink() +{ + return m_xDlg->IsLink(); +} + +bool AbstractSdInsertPagesObjsDlg_Impl::IsRemoveUnnecessaryMasterPages() const +{ + return m_xDlg->IsRemoveUnnecessaryMasterPages(); +} + +BitmapEx AbstractSdInsertPagesObjsDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdInsertPagesObjsDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractMorphDlg_Impl::SaveSettings() const +{ + m_xDlg->SaveSettings(); +} + +sal_uInt16 AbstractMorphDlg_Impl::GetFadeSteps() const +{ + return m_xDlg->GetFadeSteps(); +} + +bool AbstractMorphDlg_Impl::IsAttributeFade() const +{ + return m_xDlg->IsAttributeFade(); +} + +bool AbstractMorphDlg_Impl::IsOrientationFade() const +{ + return m_xDlg->IsOrientationFade(); +} + +BitmapEx AbstractMorphDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractMorphDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdStartPresDlg_Impl::GetAttr( SfxItemSet& rOutAttrs ) +{ + m_xDlg->GetAttr(rOutAttrs); +} + +BitmapEx AbstractSdStartPresDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdStartPresDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdPresLayoutDlg_Impl::GetAttr( SfxItemSet& rOutAttrs ) +{ + m_xDlg->GetAttr(rOutAttrs); +} + +BitmapEx AbstractSdPresLayoutDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdPresLayoutDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +const SfxItemSet* SdAbstractSfxDialog_Impl::GetOutputItemSet() const +{ + return m_xDlg->GetOutputItemSet(); +} + +void SdAbstractSfxDialog_Impl::SetText( const OUString& rStr ) +{ + m_xDlg->set_title(rStr); +} + +const GDIMetaFile& AbstractSdVectorizeDlg_Impl::GetGDIMetaFile() const +{ + return m_xDlg->GetGDIMetaFile(); +} + +BitmapEx AbstractSdVectorizeDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdVectorizeDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +void AbstractSdPublishingDlg_Impl::GetParameterSequence( css::uno::Sequence< css::beans::PropertyValue >& rParams ) +{ + m_xDlg->GetParameterSequence( rParams ); +} + +BitmapEx AbstractSdPublishingDlg_Impl::createScreenshot() const +{ + VclPtr xDialogSurface(m_xDlg->getDialog()->screenshot()); + return xDialogSurface->GetBitmapEx(Point(), xDialogSurface->GetOutputSizePixel()); +} + +OString AbstractSdPublishingDlg_Impl::GetScreenshotId() const +{ + return m_xDlg->get_help_id(); +} + +//-------------- SdAbstractDialogFactory implementation-------------- + +VclPtr SdAbstractDialogFactory_Impl::CreateSvxBulletAndPositionDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) +{ + return VclPtr::Create(std::make_unique(pParent, *pAttr, pView)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateBreakDlg( + weld::Window* pParent, + ::sd::DrawView* pDrView, + ::sd::DrawDocShell* pShell, + sal_uLong nSumActionCount, + sal_uLong nObjCount ) +{ + return VclPtr::Create(std::make_unique<::sd::BreakDlg>(pParent, pDrView, pShell, nSumActionCount, nObjCount)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateCopyDlg(weld::Window* pParent, + const SfxItemSet& rInAttrs, + ::sd::View* pView ) +{ + return VclPtr::Create(std::make_unique<::sd::CopyDlg>(pParent, rInAttrs, pView)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdCustomShowDlg(weld::Window* pParent, SdDrawDocument& rDrawDoc ) +{ + return VclPtr::Create(std::make_unique(pParent, rDrawDoc)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdTabCharDialog(weld::Window* pParent, const SfxItemSet* pAttr, SfxObjectShell* pDocShell) +{ + return VclPtr::Create(std::make_shared(pParent, pAttr, pDocShell)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdTabPageDialog(weld::Window* pParent, const SfxItemSet* pAttr, SfxObjectShell* pDocShell, bool bAreaPage, bool bIsImpressDoc, bool bIsImpressMaster ) +{ + return VclPtr::Create(std::make_shared(pDocShell, pParent, pAttr, bAreaPage, bIsImpressDoc, bIsImpressMaster)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdModifyFieldDlg(weld::Window* pParent, const SvxFieldData* pInField, const SfxItemSet& rSet) +{ + return VclPtr::Create(std::make_unique(pParent, pInField, rSet)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdSnapLineDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, ::sd::View* pView) +{ + return VclPtr::Create(std::make_unique(pParent, rInAttrs, pView)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdInsertLayerDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, bool bDeletable, const OUString& aStr) +{ + return VclPtr::Create(std::make_unique(pParent, rInAttrs, bDeletable, aStr)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdInsertPagesObjsDlg(weld::Window* pParent, const SdDrawDocument* pDoc, SfxMedium* pSfxMedium, const OUString& rFileName) +{ + return VclPtr::Create(std::make_unique(pParent, pDoc, pSfxMedium, rFileName)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateMorphDlg(weld::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2) +{ + return VclPtr::Create(std::make_unique<::sd::MorphDlg>(pParent, pObj1, pObj2)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdOutlineBulletTabDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) +{ + return VclPtr::Create(std::make_shared<::sd::OutlineBulletDlg>(pParent, pAttr, pView)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdParagraphTabDlg(weld::Window* pParent, const SfxItemSet* pAttr ) +{ + return VclPtr::Create(std::make_shared(pParent, pAttr)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdStartPresentationDlg(weld::Window* pParent, + const SfxItemSet& rInAttrs, const std::vector &rPageNames, SdCustomShowList* pCSList) +{ + return VclPtr::Create(std::make_unique(pParent, rInAttrs, rPageNames, pCSList)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateRemoteDialog(weld::Window* pParent) +{ + return VclPtr::Create(std::make_unique<::sd::RemoteDialog>(pParent)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdPresLayoutTemplateDlg(SfxObjectShell* pDocSh, weld::Window* pParent, bool bBackgroundDlg, SfxStyleSheetBase& rStyleBase, PresentationObjects ePO, SfxStyleSheetBasePool* pSSPool) +{ + return VclPtr::Create(std::make_shared(pDocSh, pParent, bBackgroundDlg, rStyleBase, ePO, pSSPool)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdPresLayoutDlg(weld::Window* pParent, ::sd::DrawDocShell* pDocShell, const SfxItemSet& rInAttrs) +{ + return VclPtr::Create(std::make_unique(pDocShell, pParent, rInAttrs)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdTabTemplateDlg(weld::Window* pParent, const SfxObjectShell* pDocShell, SfxStyleSheetBase& rStyleBase, SdrModel* pModel, SdrView* pView) +{ + return VclPtr::Create(std::make_shared(pParent, pDocShell, rStyleBase, pModel, pView)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreatSdActionDialog(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView ) +{ + return VclPtr::Create(std::make_unique(pParent, pAttr, pView)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell) +{ + return VclPtr::Create(std::make_unique(pParent, rBmp, pDocShell)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdPublishingDlg(weld::Window* pParent, DocumentType eDocType) +{ + return VclPtr::Create(std::make_unique(pParent, eDocType)); +} + +// Factories for TabPages +CreateTabPage SdAbstractDialogFactory_Impl::GetSdOptionsContentsTabPageCreatorFunc() +{ + return SdTpOptionsContents::Create; +} + +CreateTabPage SdAbstractDialogFactory_Impl::GetSdPrintOptionsTabPageCreatorFunc() +{ + return SdPrintOptions::Create; +} + +CreateTabPage SdAbstractDialogFactory_Impl::GetSdOptionsMiscTabPageCreatorFunc() +{ + return SdTpOptionsMisc::Create; +} + +CreateTabPage SdAbstractDialogFactory_Impl::GetSdOptionsSnapTabPageCreatorFunc() +{ + return SdTpOptionsSnap::Create; +} + +VclPtr SdAbstractDialogFactory_Impl::CreateMasterLayoutDialog(weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage) +{ + return VclPtr::Create(std::make_unique<::sd::MasterLayoutDialog>(pParent, pDoc, pCurrentPage)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateHeaderFooterDialog(sd::ViewShell* pViewShell, + weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage) +{ + return VclPtr::Create(std::make_shared<::sd::HeaderFooterDialog>(pViewShell, pParent, pDoc, pCurrentPage)); +} + +VclPtr SdAbstractDialogFactory_Impl::CreateSdPhotoAlbumDialog(weld::Window* pParent, SdDrawDocument* pDoc) +{ + return VclPtr::Create(std::make_unique(pParent, pDoc)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sddlgfact.hxx b/sd/source/ui/dlg/sddlgfact.hxx new file mode 100644 index 000000000..8da5c74dd --- /dev/null +++ b/sd/source/ui/dlg/sddlgfact.hxx @@ -0,0 +1,448 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//namespace sd { +// class MorphDlg; +// class CopyDlg; +// class BreakDlg; +// class HeaderFooterDialog; +// class MasterLayoutDialog; +//} + +class SvxBulletAndPositionDlg; + +/// Provides managing and getting information from the numbering and position dialog. +class AbstractSvxBulletAndPositionDlg_Impl :public AbstractSvxBulletAndPositionDlg +{ + std::unique_ptr m_xDlg; +public: + explicit AbstractSvxBulletAndPositionDlg_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual const SfxItemSet* GetOutputItemSet( SfxItemSet* ) const override ; + virtual bool IsApplyToMaster() override; + virtual bool IsSlideScope() override; +}; + +class SdAbstractGenericDialog_Impl : public VclAbstractDialog +{ + std::unique_ptr m_xDlg; +public: + explicit SdAbstractGenericDialog_Impl(std::unique_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractMasterLayoutDialog_Impl : public VclAbstractDialog +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractMasterLayoutDialog_Impl(std::unique_ptr<::sd::MasterLayoutDialog> pDlg); + virtual short Execute() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractBreakDlg_Impl : public VclAbstractDialog +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractBreakDlg_Impl(std::unique_ptr<::sd::BreakDlg> pDlg); + virtual short Execute() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractCopyDlg_Impl : public AbstractCopyDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractCopyDlg_Impl(std::unique_ptr<::sd::CopyDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr( SfxItemSet& rOutAttrs ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdCustomShowDlg_Impl : public AbstractSdCustomShowDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdCustomShowDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual bool IsCustomShow() const override ; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class SdAbstractTabController_Impl : public SfxAbstractTabDialog +{ + std::shared_ptr m_xDlg; +public: + explicit SdAbstractTabController_Impl(std::shared_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString &rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual WhichRangesContainer GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractBulletDialog_Impl : public SfxAbstractTabDialog +{ + std::shared_ptr m_xDlg; +public: + explicit AbstractBulletDialog_Impl(std::shared_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString& rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual WhichRangesContainer GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class SdPresLayoutTemplateDlg_Impl : public SfxAbstractTabDialog +{ + std::shared_ptr m_xDlg; +public: + explicit SdPresLayoutTemplateDlg_Impl(std::shared_ptr p) + : m_xDlg(std::move(p)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + virtual void SetCurPageId( const OString& rName ) override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual WhichRangesContainer GetInputRanges( const SfxItemPool& pItem ) override; + virtual void SetInputSet( const SfxItemSet* pInSet ) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdModifyFieldDlg_Impl : public AbstractSdModifyFieldDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdModifyFieldDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual SvxFieldData* GetField() override; + virtual SfxItemSet GetItemSet() override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdSnapLineDlg_Impl : public AbstractSdSnapLineDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdSnapLineDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr(SfxItemSet& rOutAttrs) override; + virtual void HideRadioGroup() override; + virtual void HideDeleteBtn() override; + virtual void SetInputFields(bool bEnableX, bool bEnableY) override; + virtual void SetText( const OUString& rStr ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdInsertLayerDlg_Impl : public AbstractSdInsertLayerDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdInsertLayerDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr( SfxItemSet& rOutAttrs ) override ; + virtual void SetHelpId( const OString& rHelpId ) override ; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdInsertPagesObjsDlg_Impl : public AbstractSdInsertPagesObjsDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdInsertPagesObjsDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual std::vector GetList ( const sal_uInt16 nType ) override; + virtual bool IsLink() override; + virtual bool IsRemoveUnnecessaryMasterPages() const override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractMorphDlg_Impl : public AbstractMorphDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractMorphDlg_Impl(std::unique_ptr<::sd::MorphDlg> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void SaveSettings() const override; + virtual sal_uInt16 GetFadeSteps() const override; + virtual bool IsAttributeFade() const override ; + virtual bool IsOrientationFade() const override ; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdStartPresDlg_Impl : public AbstractSdStartPresDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdStartPresDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr( SfxItemSet& rOutAttrs ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdPresLayoutDlg_Impl : public AbstractSdPresLayoutDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdPresLayoutDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetAttr(SfxItemSet& rOutAttrs) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class SdAbstractSfxDialog_Impl : public SfxAbstractDialog +{ +private: + std::unique_ptr m_xDlg; +public: + SdAbstractSfxDialog_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual const SfxItemSet* GetOutputItemSet() const override; + virtual void SetText( const OUString& rStr ) override; +}; + +class AbstractSdVectorizeDlg_Impl :public AbstractSdVectorizeDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdVectorizeDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual const GDIMetaFile& GetGDIMetaFile() const override ; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractSdPublishingDlg_Impl :public AbstractSdPublishingDlg +{ +private: + std::unique_ptr m_xDlg; +public: + AbstractSdPublishingDlg_Impl(std::unique_ptr pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual void GetParameterSequence( css::uno::Sequence< css::beans::PropertyValue >& rParams ) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +class AbstractHeaderFooterDialog_Impl :public AbstractHeaderFooterDialog +{ +private: + std::shared_ptr<::sd::HeaderFooterDialog> m_xDlg; +public: + AbstractHeaderFooterDialog_Impl(std::shared_ptr<::sd::HeaderFooterDialog> pDlg) + : m_xDlg(std::move(pDlg)) + { + } + virtual short Execute() override; + virtual bool StartExecuteAsync(AsyncContext &rCtx) override; + + // screenshotting + virtual BitmapEx createScreenshot() const override; + virtual OString GetScreenshotId() const override; +}; + +//AbstractDialogFactory_Impl implementations +class SdAbstractDialogFactory_Impl : public SdAbstractDialogFactory +{ + +public: + virtual ~SdAbstractDialogFactory_Impl() {} + + virtual VclPtr CreateSvxBulletAndPositionDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) override; + virtual VclPtr CreateBreakDlg(weld::Window* pWindow, ::sd::DrawView* pDrView, ::sd::DrawDocShell* pShell, sal_uLong nSumActionCount, sal_uLong nObjCount) override; + virtual VclPtr CreateCopyDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, ::sd::View* pView) override; + virtual VclPtr CreateSdCustomShowDlg(weld::Window* pParent, SdDrawDocument& rDrawDoc) override; + virtual VclPtr CreateSdTabCharDialog(weld::Window* pWindow, const SfxItemSet* pAttr, SfxObjectShell* pDocShell) override; + virtual VclPtr CreateSdTabPageDialog(weld::Window* pWindow, const SfxItemSet* pAttr, SfxObjectShell* pDocShell, bool bAreaPage, bool bIsImpressDoc, bool bIsImpressMaster) override; + virtual VclPtr CreateSdModifyFieldDlg(weld::Window* pWindow, const SvxFieldData* pInField, const SfxItemSet& rSet) override; + virtual VclPtr CreateSdSnapLineDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, ::sd::View* pView) override; + virtual VclPtr CreateSdInsertLayerDlg(weld::Window* pParent, const SfxItemSet& rInAttrs, bool bDeletable, const OUString& aStr) override; + virtual VclPtr CreateSdInsertPagesObjsDlg(weld::Window* pParent, const SdDrawDocument* pDoc, SfxMedium* pSfxMedium, const OUString& rFileName ) override; + virtual VclPtr CreateMorphDlg(weld::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2) override; + virtual VclPtr CreateSdOutlineBulletTabDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) override; + virtual VclPtr CreateSdParagraphTabDlg(weld::Window* pParent, const SfxItemSet* pAttr) override; + virtual VclPtr CreateSdStartPresentationDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, + const std::vector &rPageNames, SdCustomShowList* pCSList ) override; + virtual VclPtr CreateRemoteDialog(weld::Window* pWindow) override; // add for RemoteDialog + virtual VclPtr CreateSdPresLayoutTemplateDlg(SfxObjectShell* pDocSh, weld::Window* pParent, bool bBackgroundDlg, SfxStyleSheetBase& rStyleBase, PresentationObjects ePO, SfxStyleSheetBasePool* pSSPool) override; + virtual VclPtr CreateSdPresLayoutDlg(weld::Window* pParent, ::sd::DrawDocShell* pDocShell, const SfxItemSet& rInAttrs) override; + virtual VclPtr CreateSdTabTemplateDlg(weld::Window* pParent, const SfxObjectShell* pDocShell, SfxStyleSheetBase& rStyleBase, SdrModel* pModel, SdrView* pView ) override; + virtual VclPtr CreatSdActionDialog(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView) override; + virtual VclPtr CreateSdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell) override; + virtual VclPtr CreateSdPublishingDlg(weld::Window* pWindow, DocumentType eDocType) override; + + virtual VclPtr CreateSdPhotoAlbumDialog(weld::Window* pWindow, SdDrawDocument* pDoc) override; + + virtual VclPtr CreateMasterLayoutDialog(weld::Window* pParent, SdDrawDocument* pDoc, SdPage*) override; + + virtual VclPtr CreateHeaderFooterDialog(sd::ViewShell* pViewShell, + weld::Window* pParent, + SdDrawDocument* pDoc, + SdPage* pCurrentPage) override; + + // For TabPage + virtual CreateTabPage GetSdOptionsContentsTabPageCreatorFunc() override; + virtual CreateTabPage GetSdPrintOptionsTabPageCreatorFunc() override; + virtual CreateTabPage GetSdOptionsMiscTabPageCreatorFunc() override; + virtual CreateTabPage GetSdOptionsSnapTabPageCreatorFunc() override; + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sdpreslt.cxx b/sd/source/ui/dlg/sdpreslt.cxx new file mode 100644 index 000000000..dd54611e2 --- /dev/null +++ b/sd/source/ui/dlg/sdpreslt.cxx @@ -0,0 +1,267 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +SdPresLayoutDlg::SdPresLayoutDlg(::sd::DrawDocShell* pDocShell, + weld::Window* pWindow, const SfxItemSet& rInAttrs) + : GenericDialogController(pWindow, "modules/simpress/ui/slidedesigndialog.ui", "SlideDesignDialog") + , mpDocSh(pDocShell) + , mrOutAttrs(rInAttrs) + , maStrNone(SdResId(STR_NULL)) + , m_xCbxMasterPage(m_xBuilder->weld_check_button("masterpage")) + , m_xCbxCheckMasters(m_xBuilder->weld_check_button("checkmasters")) + , m_xBtnLoad(m_xBuilder->weld_button("load")) + , m_xVS(new ValueSet(m_xBuilder->weld_scrolled_window("selectwin", true))) + , m_xVSWin(new weld::CustomWeld(*m_xBuilder, "select", *m_xVS)) +{ + m_xVSWin->set_size_request(m_xBtnLoad->get_approximate_digit_width() * 60, + m_xBtnLoad->get_text_height() * 20); + + m_xVS->SetDoubleClickHdl(LINK(this, SdPresLayoutDlg, ClickLayoutHdl)); + m_xBtnLoad->connect_clicked(LINK(this, SdPresLayoutDlg, ClickLoadHdl)); + + Reset(); +} + +SdPresLayoutDlg::~SdPresLayoutDlg() +{ +} + +/** + * Initialize + */ +void SdPresLayoutDlg::Reset() +{ + tools::Long nName; + + // replace master page + if( const SfxBoolItem* pPoolItem = mrOutAttrs.GetItemIfSet( ATTR_PRESLAYOUT_MASTER_PAGE, false ) ) + { + bool bMasterPage = pPoolItem->GetValue(); + m_xCbxMasterPage->set_sensitive( !bMasterPage ); + m_xCbxMasterPage->set_active( bMasterPage ); + } + + // remove not used master pages + m_xCbxCheckMasters->set_active(false); + + if( const SfxStringItem* pPoolItem = mrOutAttrs.GetItemIfSet(ATTR_PRESLAYOUT_NAME) ) + maName = pPoolItem->GetValue(); + else + maName.clear(); + + FillValueSet(); + + mnLayoutCount = maLayoutNames.size(); + for( nName = 0; nName < mnLayoutCount; nName++ ) + { + if (maLayoutNames[nName] == maName) + break; + } + DBG_ASSERT(nName < mnLayoutCount, "Layout not found"); + + m_xVS->SelectItem(static_cast(nName) + 1); // Indices of the ValueSets start at 1 + +} + +/** + * Fills the provided Item-Set with dialog box attributes + */ +void SdPresLayoutDlg::GetAttr(SfxItemSet& rOutAttrs) +{ + short nId = m_xVS->GetSelectedItemId(); + bool bLoad = nId > mnLayoutCount; + rOutAttrs.Put( SfxBoolItem( ATTR_PRESLAYOUT_LOAD, bLoad ) ); + + OUString aLayoutName; + + if( bLoad ) + { + aLayoutName = maName + "#" + maLayoutNames[ nId - 1 ]; + } + else if (nId) + { + aLayoutName = maLayoutNames[ nId - 1 ]; + if( aLayoutName == maStrNone ) + aLayoutName.clear(); // that way we encode "- nothing -" (see below) + } + + rOutAttrs.Put( SfxStringItem( ATTR_PRESLAYOUT_NAME, aLayoutName ) ); + rOutAttrs.Put( SfxBoolItem( ATTR_PRESLAYOUT_MASTER_PAGE, m_xCbxMasterPage->get_active() ) ); + rOutAttrs.Put( SfxBoolItem( ATTR_PRESLAYOUT_CHECK_MASTERS, m_xCbxCheckMasters->get_active() ) ); +} + +/** + * Fills ValueSet with bitmaps + */ +void SdPresLayoutDlg::FillValueSet() +{ + m_xVS->SetStyle(m_xVS->GetStyle() | WB_ITEMBORDER | WB_DOUBLEBORDER + | WB_VSCROLL | WB_NAMEFIELD); + + m_xVS->SetColCount(2); + m_xVS->SetLineCount(2); + m_xVS->SetExtraSpacing(2); + + SdDrawDocument* pDoc = mpDocSh->GetDoc(); + + sal_uInt16 nCount = pDoc->GetMasterPageCount(); + + for (sal_uInt16 nLayout = 0; nLayout < nCount; nLayout++) + { + SdPage* pMaster = static_cast(pDoc->GetMasterPage(nLayout)); + if (pMaster->GetPageKind() == PageKind::Standard) + { + OUString aLayoutName(pMaster->GetLayoutName()); + aLayoutName = aLayoutName.copy(0, aLayoutName.indexOf(SD_LT_SEPARATOR)); + maLayoutNames.push_back(aLayoutName); + + Image aBitmap(mpDocSh->GetPagePreviewBitmap(pMaster)); + m_xVS->InsertItem(static_cast(maLayoutNames.size()), aBitmap, aLayoutName); + } + } + + m_xVS->Show(); +} + +/** + * DoubleClick handler + */ +IMPL_LINK_NOARG(SdPresLayoutDlg, ClickLayoutHdl, ValueSet*, void) +{ + m_xDialog->response(RET_OK); +} + +/** + * Click handler for load button + */ +IMPL_LINK_NOARG(SdPresLayoutDlg, ClickLoadHdl, weld::Button&, void) +{ + SfxNewFileDialog aDlg(m_xDialog.get(), SfxNewFileDialogMode::Preview); + aDlg.set_title(SdResId(STR_LOAD_PRESENTATION_LAYOUT)); + sal_uInt16 nResult = aDlg.run(); + + bool bCancel = false; + + switch (nResult) + { + case RET_OK: + { + if (aDlg.IsTemplate()) + { + maName = aDlg.GetTemplateFileName(); + } + else + { + // that way we encode "- nothing -" + maName.clear(); + } + } + break; + + default: + bCancel = true; + } + + if( bCancel ) + return; + + // check if template already exists + OUString aCompareStr(maName); + if (aCompareStr.isEmpty()) + aCompareStr = maStrNone; + + auto it = std::find(maLayoutNames.begin(), maLayoutNames.end(), aCompareStr); + if (it != maLayoutNames.end()) + { + sal_uInt16 aPos = static_cast(std::distance(maLayoutNames.begin(), it)); + // select template + m_xVS->SelectItem( aPos + 1 ); + } + else + { + // load document in order to determine preview bitmap (if template is selected) + if (!maName.isEmpty()) + { + // determine document in order to call OpenBookmarkDoc + SdDrawDocument* pDoc = mpDocSh->GetDoc(); + SdDrawDocument* pTemplDoc = pDoc->OpenBookmarkDoc( maName ); + + if (pTemplDoc) + { + ::sd::DrawDocShell* pTemplDocSh= pTemplDoc->GetDocSh(); + + sal_uInt16 nCount = pTemplDoc->GetMasterPageCount(); + + for (sal_uInt16 nLayout = 0; nLayout < nCount; nLayout++) + { + SdPage* pMaster = static_cast( pTemplDoc->GetMasterPage(nLayout) ); + if (pMaster->GetPageKind() == PageKind::Standard) + { + OUString aLayoutName(pMaster->GetLayoutName()); + aLayoutName = aLayoutName.copy(0, aLayoutName.indexOf(SD_LT_SEPARATOR)); + maLayoutNames.push_back(aLayoutName); + + Image aBitmap(pTemplDocSh->GetPagePreviewBitmap(pMaster)); + m_xVS->InsertItem(static_cast(maLayoutNames.size()), aBitmap, aLayoutName); + } + } + } + else + { + bCancel = true; + } + + pDoc->CloseBookmarkDoc(); + } + else + { + // empty layout + maLayoutNames.push_back(maStrNone); + m_xVS->InsertItem( static_cast(maLayoutNames.size()), + Image(BMP_SLIDE_NONE), maStrNone ); + } + + if (!bCancel) + { + // select template + m_xVS->SelectItem( static_cast(maLayoutNames.size()) ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sdtreelb.cxx b/sd/source/ui/dlg/sdtreelb.cxx new file mode 100644 index 000000000..c15a2ea09 --- /dev/null +++ b/sd/source/ui/dlg/sdtreelb.cxx @@ -0,0 +1,1206 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +using namespace com::sun::star; + +bool SdPageObjsTLV::bIsInDrag = false; + +bool SdPageObjsTLV::IsInDrag() +{ + return bIsInDrag; +} + +SotClipboardFormatId SdPageObjsTLV::SdPageObjsTransferable::mnListBoxDropFormatId = static_cast(SAL_MAX_UINT32); + +SdPageObjsTLV::SdPageObjsTransferable::SdPageObjsTransferable( + const INetBookmark& rBookmark, + ::sd::DrawDocShell& rDocShell, + NavigatorDragType eDragType) + : SdTransferable(rDocShell.GetDoc(), nullptr, true), + maBookmark( rBookmark ), + mrDocShell( rDocShell ), + meDragType( eDragType ) +{ +} + +SdPageObjsTLV::SdPageObjsTransferable::~SdPageObjsTransferable() +{ +} + +void SdPageObjsTLV::SdPageObjsTransferable::AddSupportedFormats() +{ + AddFormat(SotClipboardFormatId::NETSCAPE_BOOKMARK); + AddFormat(SotClipboardFormatId::TREELISTBOX); + AddFormat(GetListBoxDropFormatId()); +} + +bool SdPageObjsTLV::SdPageObjsTransferable::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ ) +{ + SotClipboardFormatId nFormatId = SotExchange::GetFormat( rFlavor ); + switch (nFormatId) + { + case SotClipboardFormatId::NETSCAPE_BOOKMARK: + SetINetBookmark( maBookmark, rFlavor ); + return true; + + case SotClipboardFormatId::TREELISTBOX: + { + css::uno::Any aTreeListBoxData; // empty for now + SetAny(aTreeListBoxData); + return true; + } + + default: + return false; + } +} + +void SdPageObjsTLV::SdPageObjsTransferable::DragFinished( sal_Int8 nDropAction ) +{ + SdPageObjsTLV::OnDragFinished(); + SdTransferable::DragFinished(nDropAction); +} + +sal_Int64 SAL_CALL SdPageObjsTLV::SdPageObjsTransferable::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf{}); +} + +const css::uno::Sequence& SdPageObjsTLV::SdPageObjsTransferable::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theSdPageObjsTLBUnoTunnelId; + return theSdPageObjsTLBUnoTunnelId.getSeq(); +} + +SdPageObjsTLV::SdPageObjsTransferable* SdPageObjsTLV::SdPageObjsTransferable::getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData ) + noexcept +{ + try + { + return comphelper::getFromUnoTunnel(rxData); + } + catch( const css::uno::Exception& ) + { + } + return nullptr; +} + +SotClipboardFormatId SdPageObjsTLV::SdPageObjsTransferable::GetListBoxDropFormatId() +{ + if (mnListBoxDropFormatId == static_cast(SAL_MAX_UINT32)) + mnListBoxDropFormatId = SotExchange::RegisterFormatMimeType("application/x-openoffice-treelistbox-moveonly;windows_formatname=\"SV_LBOX_DD_FORMAT_MOVE\""); + return mnListBoxDropFormatId; +} + +/** + * @return true if children of the specified string are selected + */ +bool SdPageObjsTLV::HasSelectedChildren( std::u16string_view rName ) +{ + bool bChildren = false; + + if( !rName.empty() ) + { + std::unique_ptr xEntry(m_xTreeView->make_iterator()); + OUString aTmp; + + if (m_xTreeView->get_iter_first(*xEntry)) + { + do + { + aTmp = m_xTreeView->get_text(*xEntry); + if (aTmp == rName) + { + + // see if any of the selected nodes are subchildren of this node + m_xTreeView->selected_foreach([this, &bChildren, &xEntry](weld::TreeIter& rEntry){ + std::unique_ptr xParent(m_xTreeView->make_iterator(&rEntry)); + while (!bChildren && m_xTreeView->iter_parent(*xParent)) + bChildren = m_xTreeView->iter_compare(*xParent, *xEntry) == 0; + return bChildren; + }); + + break; + } + } + while (m_xTreeView->iter_next(*xEntry)); + } + } + + return bChildren; +} + +void SdPageObjsTLV::SetShowAllShapes ( + const bool bShowAllShapes, + const bool bFillList) +{ + m_bShowAllShapes = bShowAllShapes; + if (bFillList) + { + if (m_pMedium == nullptr) + Fill(m_pDoc, m_bShowAllPages, m_aDocName); + else + Fill(m_pDoc, m_pMedium, m_aDocName); + } +} + +bool SdPageObjsTLV::IsEqualToShapeList(std::unique_ptr& rEntry, const SdrObjList& rList, + std::u16string_view rListName) +{ + if (!rEntry) + return false; + OUString aName = m_xTreeView->get_text(*rEntry); + + if (rListName != aName) + return false; + + if (!m_xTreeView->iter_next(*rEntry)) + rEntry.reset(); + + SdrObjListIter aIter(&rList, + !rList.HasObjectNavigationOrder() /* use navigation order, if available */, + SdrIterMode::Flat); + + while (aIter.IsMore()) + { + SdrObject* pObj = aIter.Next(); + + const OUString aObjectName(GetObjectName(pObj)); + + if (!aObjectName.isEmpty()) + { + if (!rEntry) + return false; + + aName = m_xTreeView->get_text(*rEntry); + + if (aObjectName != aName) + return false; + + if (pObj->IsGroupObject()) + { + bool bRet = IsEqualToShapeList(rEntry, *pObj->GetSubList(), aObjectName); + if (!bRet) + return false; + } + else + { + if (!m_xTreeView->iter_next(*rEntry)) + rEntry.reset(); + } + } + } + + return true; +} + +/** + * Checks if the pages (PageKind::Standard) of a doc and the objects on the pages + * are identical to the TreeLB. + * If a doc is provided, this will be the used doc (important by more than + * one document). + */ +bool SdPageObjsTLV::IsEqualToDoc( const SdDrawDocument* pInDoc ) +{ + if( pInDoc ) + m_pDoc = pInDoc; + + if( !m_pDoc ) + return false; + + std::unique_ptr xEntry(m_xTreeView->make_iterator()); + if (!m_xTreeView->get_iter_first(*xEntry)) + xEntry.reset(); + + // compare all pages including the objects + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = m_pDoc->GetPageCount(); + + while( nPage < nMaxPages ) + { + const SdPage* pPage = static_cast( m_pDoc->GetPage( nPage ) ); + if( pPage->GetPageKind() == PageKind::Standard ) + { + bool bRet = IsEqualToShapeList(xEntry, *pPage, pPage->GetName()); + if (!bRet) + return false; + } + nPage++; + } + // If there are still entries in the listbox, + // then objects (with names) or pages were deleted + return !xEntry; +} + +IMPL_LINK(SdPageObjsTLV, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + const vcl::KeyCode& rKeyCode = rKEvt.GetKeyCode(); + if (m_xAccel->execute(rKeyCode)) + { + // the accelerator consumed the event + return true; + } + if (rKeyCode.GetCode() == KEY_RETURN) + { + std::unique_ptr xCursor(m_xTreeView->make_iterator()); + if (m_xTreeView->get_cursor(xCursor.get()) && m_xTreeView->iter_has_child(*xCursor)) + { + if (m_xTreeView->get_row_expanded(*xCursor)) + m_xTreeView->collapse_row(*xCursor); + else + m_xTreeView->expand_row(*xCursor); + } + m_aRowActivatedHdl.Call(*m_xTreeView); + return true; + } + return m_aKeyPressHdl.Call(rKEvt); +} + +IMPL_LINK(SdPageObjsTLV, MousePressHdl, const MouseEvent&, rMEvt, bool) +{ + m_bSelectionHandlerNavigates = rMEvt.GetClicks() == 1; + m_bNavigationGrabsFocus = rMEvt.GetClicks() != 1; + return false; +} + +IMPL_LINK_NOARG(SdPageObjsTLV, MouseReleaseHdl, const MouseEvent&, bool) +{ + m_bSelectionHandlerNavigates = false; + m_bNavigationGrabsFocus = true; + return false; +} + +IMPL_LINK(SdPageObjsTLV, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = false; + return StartDrag(); +} + +namespace +{ + bool CanDragSource(const weld::TreeView& rTreeView) + { + std::unique_ptr xSource(rTreeView.make_iterator()); + if (!rTreeView.get_selected(xSource.get())) + return false; + + std::unique_ptr xSourceParent(rTreeView.make_iterator(xSource.get())); + bool bSourceHasParent = rTreeView.iter_parent(*xSourceParent); + // level 1 objects only + if (!bSourceHasParent || rTreeView.get_iter_depth(*xSourceParent)) + return false; + + SdrObject* pSourceObject = weld::fromId(rTreeView.get_id(*xSource)); + if (pSourceObject == reinterpret_cast(1)) + pSourceObject = nullptr; + + if (!pSourceObject) + return false; + + SdrPage* pObjectList = pSourceObject->getSdrPageFromSdrObject(); + if (!pObjectList) + return false; + + return true; + } +} + +/** + * StartDrag-Request + */ +bool SdPageObjsTLV::StartDrag() +{ + return !CanDragSource(*m_xTreeView) || DoDrag(); +} + +/** + * Begin drag + */ +bool SdPageObjsTLV::DoDrag() +{ + if (!m_pNavigator) + return true; + + if (!m_xHelper) + return true; + + // Get the view. + ::sd::DrawDocShell* pDocShell = m_pDoc->GetDocSh(); + ::sd::ViewShell* pViewShell = GetViewShellForDocShell(*pDocShell); + if (pViewShell == nullptr) + { + OSL_ASSERT(pViewShell!=nullptr); + return true; + } + sd::View* pView = pViewShell->GetView(); + if (pView == nullptr) + { + OSL_ASSERT(pView!=nullptr); + return true; + } + + bIsInDrag = true; + + std::unique_ptr xEntry = m_xTreeView->make_iterator(); + bool bUserData = m_xTreeView->get_cursor(xEntry.get()); + + SdrObject* pObject = nullptr; + sal_Int64 nUserData = bUserData ? m_xTreeView->get_id(*xEntry).toInt64() : 0; + if (nUserData != 1) + pObject = reinterpret_cast(nUserData); + if (pObject != nullptr) + { + // For shapes without a user supplied name (the automatically + // created name does not count), a different drag and drop technique + // is used. + if (GetObjectName(pObject, false).isEmpty()) + { + AddShapeToTransferable(*m_xHelper, *pObject); + m_xHelper->SetView(pView); + SD_MOD()->pTransferDrag = m_xHelper.get(); + } + + // Unnamed shapes have to be selected to be recognized by the + // current drop implementation. In order to have a consistent + // behaviour for all shapes, every shape that is to be dragged is + // selected first. + SdrPageView* pPageView = pView->GetSdrPageView(); + pView->UnmarkAllObj(pPageView); + pView->MarkObj(pObject, pPageView); + } + else + { + m_xHelper->SetView(pView); + SD_MOD()->pTransferDrag = m_xHelper.get(); + } + + return false; +} + +void SdPageObjsTLV::OnDragFinished() +{ + bIsInDrag = false; +} + +SdPageObjsTLVDropTarget::SdPageObjsTLVDropTarget(weld::TreeView& rTreeView) + : DropTargetHelper(rTreeView.get_drop_target()) + , m_rTreeView(rTreeView) +{ +} + +/** + * AcceptDrop-Event + */ +sal_Int8 SdPageObjsTLVDropTarget::AcceptDrop(const AcceptDropEvent& rEvt) +{ + weld::TreeView* pSource = m_rTreeView.get_drag_source(); + // only dragging within the same widget allowed + if (!pSource || pSource != &m_rTreeView) + return DND_ACTION_NONE; + + std::unique_ptr xTarget(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true)) + return DND_ACTION_NONE; + + std::unique_ptr xSource(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_selected(xSource.get())) + return DND_ACTION_NONE; + + std::unique_ptr xTargetParent(m_rTreeView.make_iterator(xTarget.get())); + while (m_rTreeView.get_iter_depth(*xTargetParent)) + m_rTreeView.iter_parent(*xTargetParent); + + std::unique_ptr xSourceParent(m_rTreeView.make_iterator(xSource.get())); + while (m_rTreeView.get_iter_depth(*xSourceParent)) + m_rTreeView.iter_parent(*xSourceParent); + + // can only drop within the same page + if (m_rTreeView.iter_compare(*xTargetParent, *xSourceParent) != 0) + return DND_ACTION_NONE; + + return DND_ACTION_MOVE; +} + +/** + * ExecuteDrop-Event + */ +sal_Int8 SdPageObjsTLVDropTarget::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + weld::TreeView* pSource = m_rTreeView.get_drag_source(); + // only dragging within the same widget allowed + if (!pSource || pSource != &m_rTreeView) + return DND_ACTION_NONE; + + std::unique_ptr xSource(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_selected(xSource.get())) + return DND_ACTION_NONE; + + std::unique_ptr xTarget(m_rTreeView.make_iterator()); + if (!m_rTreeView.get_dest_row_at_pos(rEvt.maPosPixel, xTarget.get(), true)) + return DND_ACTION_NONE; + int nTargetPos = m_rTreeView.get_iter_index_in_parent(*xTarget) + 1; + + SdrObject* pTargetObject = weld::fromId(m_rTreeView.get_id(*xTarget)); + SdrObject* pSourceObject = weld::fromId(m_rTreeView.get_id(*xSource)); + if (pSourceObject == reinterpret_cast(1)) + pSourceObject = nullptr; + + if (pTargetObject != nullptr && pSourceObject != nullptr) + { + SdrPage* pObjectList = pSourceObject->getSdrPageFromSdrObject(); + if (pObjectList != nullptr) + { + sal_uInt32 nNewPosition; + if (pTargetObject == reinterpret_cast(1)) + { + nNewPosition = 0; + nTargetPos = 0; + } + else + nNewPosition = pTargetObject->GetNavigationPosition() + 1; + pObjectList->SetObjectNavigationPosition(*pSourceObject, nNewPosition); + } + + std::unique_ptr xSourceParent(m_rTreeView.make_iterator(xSource.get())); + m_rTreeView.iter_parent(*xSourceParent); + + m_rTreeView.move_subtree(*xSource, xSourceParent.get(), nTargetPos); + } + + return DND_ACTION_NONE; +} + +void SdPageObjsTLV::AddShapeToTransferable ( + SdTransferable& rTransferable, + const SdrObject& rObject) const +{ + std::unique_ptr pObjectDescriptor(new TransferableObjectDescriptor); + bool bIsDescriptorFillingPending (true); + + const SdrOle2Obj* pOleObject = dynamic_cast(&rObject); + if (pOleObject != nullptr && pOleObject->GetObjRef().is()) + { + // If object has no persistence it must be copied as part of the document + try + { + uno::Reference< embed::XEmbedPersist > xPersObj (pOleObject->GetObjRef(), uno::UNO_QUERY ); + if (xPersObj.is() && xPersObj->hasEntry()) + { + SvEmbedTransferHelper::FillTransferableObjectDescriptor( + *pObjectDescriptor, + pOleObject->GetObjRef(), + pOleObject->GetGraphic(), + pOleObject->GetAspect()); + bIsDescriptorFillingPending = false; + } + } + catch( uno::Exception& ) + { + } + } + + ::sd::DrawDocShell* pDocShell = m_pDoc->GetDocSh(); + if (bIsDescriptorFillingPending && pDocShell!=nullptr) + { + pDocShell->FillTransferableObjectDescriptor(*pObjectDescriptor); + } + + Point aDragPos (rObject.GetCurrentBoundRect().Center()); + pObjectDescriptor->maDragStartPos = aDragPos; + if (pDocShell != nullptr) + pObjectDescriptor->maDisplayName = pDocShell->GetMedium()->GetURLObject().GetURLNoPass(); + else + pObjectDescriptor->maDisplayName.clear(); + + rTransferable.SetStartPos(aDragPos); + rTransferable.SetObjectDescriptor( std::move(pObjectDescriptor) ); +} + +::sd::ViewShell* SdPageObjsTLV::GetViewShellForDocShell (::sd::DrawDocShell& rDocShell) +{ + { + ::sd::ViewShell* pViewShell = rDocShell.GetViewShell(); + if (pViewShell != nullptr) + return pViewShell; + } + + try + { + // Get a component enumeration from the desktop and search it for documents. + uno::Reference xContext( ::comphelper::getProcessComponentContext()); + + uno::Reference xDesktop = frame::Desktop::create(xContext); + + if ( ! xDesktop.is()) + return nullptr; + + uno::Reference xFrameAccess = xDesktop->getFrames(); + if ( ! xFrameAccess.is()) + return nullptr; + + for (sal_Int32 nIndex=0,nCount=xFrameAccess->getCount(); nIndex xFrame; + if ( ! (xFrameAccess->getByIndex(nIndex) >>= xFrame)) + continue; + + auto xController = xFrame->getController(); + ::sd::DrawController* pController = dynamic_cast(xController.get()); + if (pController == nullptr) + continue; + ::sd::ViewShellBase* pBase = pController->GetViewShellBase(); + if (pBase == nullptr) + continue; + if (pBase->GetDocShell() != &rDocShell) + continue; + + const std::shared_ptr pViewShell (pBase->GetMainViewShell()); + if (pViewShell) + return pViewShell.get(); + } + } + catch (uno::Exception &) + { + // When there is an exception then simply use the default value of + // bIsEnabled and disable the controls. + } + return nullptr; +} + +SdPageObjsTLV::SdPageObjsTLV(std::unique_ptr xTreeView) + : m_xTreeView(std::move(xTreeView)) + , m_xScratchIter(m_xTreeView->make_iterator()) + , m_xDropTargetHelper(new SdPageObjsTLVDropTarget(*m_xTreeView)) + , m_xAccel(::svt::AcceleratorExecute::createAcceleratorHelper()) + , m_pNavigator(nullptr) + , m_pDoc(nullptr) + , m_pBookmarkDoc(nullptr) + , m_pMedium(nullptr) + , m_pOwnMedium(nullptr) + , m_bLinkableSelected(false) + , m_bShowAllShapes(false) + , m_bShowAllPages(false) + , m_bSelectionHandlerNavigates(false) + , m_bNavigationGrabsFocus(true) + , m_eSelectionMode(SelectionMode::Single) + , m_nSelectEventId(nullptr) + , m_nRowActivateEventId(nullptr) +{ + m_xTreeView->connect_expanding(LINK(this, SdPageObjsTLV, RequestingChildrenHdl)); + m_xTreeView->connect_changed(LINK(this, SdPageObjsTLV, SelectHdl)); + m_xTreeView->connect_row_activated(LINK(this, SdPageObjsTLV, RowActivatedHdl)); + m_xTreeView->connect_drag_begin(LINK(this, SdPageObjsTLV, DragBeginHdl)); + m_xTreeView->connect_key_press(LINK(this, SdPageObjsTLV, KeyInputHdl)); + m_xTreeView->connect_mouse_press(LINK(this, SdPageObjsTLV, MousePressHdl)); + m_xTreeView->connect_mouse_release(LINK(this, SdPageObjsTLV, MouseReleaseHdl)); + + m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 28, + m_xTreeView->get_text_height() * 8); +} + +IMPL_LINK_NOARG(SdPageObjsTLV, SelectHdl, weld::TreeView&, void) +{ + if (m_nSelectEventId) + Application::RemoveUserEvent(m_nSelectEventId); + // post the event to process select event after mouse press event + m_nSelectEventId = Application::PostUserEvent(LINK(this, SdPageObjsTLV, AsyncSelectHdl)); +} + +IMPL_LINK_NOARG(SdPageObjsTLV, RowActivatedHdl, weld::TreeView&, bool) +{ + if (m_nRowActivateEventId) + Application::RemoveUserEvent(m_nRowActivateEventId); + // post the event to process row activate after mouse press event + m_nRowActivateEventId = Application::PostUserEvent(LINK(this, SdPageObjsTLV, AsyncRowActivatedHdl)); + return true; +} + +IMPL_LINK_NOARG(SdPageObjsTLV, AsyncSelectHdl, void*, void) +{ + Select(); +} + +void SdPageObjsTLV::Select() +{ + m_nSelectEventId = nullptr; + + m_bLinkableSelected = true; + + m_xTreeView->selected_foreach([this](weld::TreeIter& rEntry){ + if (m_xTreeView->get_id(rEntry).toInt64() == 0) + m_bLinkableSelected = false; + return false; + }); + + m_aChangeHdl.Call(*m_xTreeView); + + if (m_bSelectionHandlerNavigates) + { + // Page items in the tree are given user data value 1. + // Drawing object items are given user data value of the object pointer they represent. + sal_Int64 nUserData = m_xTreeView->get_selected_id().toInt64(); + if (nUserData != 1) + { + SdrObject* pObject = reinterpret_cast(nUserData); + if (pObject && pObject->GetName().isEmpty()) + { + const bool bUndo = pObject->getSdrModelFromSdrObject().IsUndoEnabled(); + pObject->getSdrModelFromSdrObject().EnableUndo(false); + pObject->SetName(m_xTreeView->get_selected_text(), false); + pObject->getSdrModelFromSdrObject().EnableUndo(bUndo); + m_aRowActivatedHdl.Call(*m_xTreeView); + pObject->getSdrModelFromSdrObject().EnableUndo(false); + pObject->SetName(OUString(), false); + pObject->getSdrModelFromSdrObject().EnableUndo(bUndo); + } + else + m_aRowActivatedHdl.Call(*m_xTreeView); + } + else + m_aRowActivatedHdl.Call(*m_xTreeView); + } + + if (!m_pNavigator) + { + m_xHelper.clear(); + return; + } + + ::sd::DrawDocShell* pDocShell = m_pDoc->GetDocSh(); + OUString aURL = INetURLObject(pDocShell->GetMedium()->GetPhysicalName(), INetProtocol::File).GetMainURL(INetURLObject::DecodeMechanism::NONE); + NavigatorDragType eDragType = m_pNavigator->GetNavigatorDragType(); + + OUString sSelectedEntry = m_xTreeView->get_selected_text(); + aURL += "#" + sSelectedEntry; + + INetBookmark aBookmark(aURL, sSelectedEntry); + sal_Int8 nDNDActions = DND_ACTION_COPYMOVE; + + if( eDragType == NAVIGATOR_DRAGTYPE_LINK ) + nDNDActions = DND_ACTION_LINK; // Either COPY *or* LINK, never both! + else if (m_pDoc->GetSdPageCount(PageKind::Standard) == 1) + { + // Can not move away the last slide in a document. + nDNDActions = DND_ACTION_COPY; + } + + // object is destroyed by internal reference mechanism + m_xHelper.set(new SdPageObjsTLV::SdPageObjsTransferable(aBookmark, *pDocShell, eDragType)); + rtl::Reference xHelper(m_xHelper); + m_xTreeView->enable_drag_source(xHelper, nDNDActions); +} + +IMPL_LINK_NOARG(SdPageObjsTLV, AsyncRowActivatedHdl, void*, void) +{ + m_nRowActivateEventId = nullptr; + m_aRowActivatedHdl.Call(*m_xTreeView); +} + +OUString SdPageObjsTLV::GetObjectName( + const SdrObject* pObject, + const bool bCreate) const +{ + OUString aRet; + + if ( pObject ) + { + aRet = pObject->GetName(); + + if (aRet.isEmpty()) + if (auto pOleObj = dynamic_cast(pObject)) + aRet = pOleObj->GetPersistName(); + } + + if (bCreate + && m_bShowAllShapes + && aRet.isEmpty() + && pObject!=nullptr) + { + aRet = SdResId(STR_NAVIGATOR_SHAPE_BASE_NAME) + " (" + pObject->TakeObjNameSingul() +")"; + aRet = aRet.replaceFirst("%1", OUString::number(pObject->GetOrdNum() + 1)); + } + + return aRet; +} + +std::vector SdPageObjsTLV::GetSelectEntryList(const int nDepth) const +{ + std::vector aEntries; + + m_xTreeView->selected_foreach([this, nDepth, &aEntries](weld::TreeIter& rEntry){ + int nListDepth = m_xTreeView->get_iter_depth(rEntry); + if (nListDepth == nDepth) + aEntries.push_back(m_xTreeView->get_text(rEntry)); + return false; + }); + + return aEntries; +} + +/** + * Checks if it is a draw file and opens the BookmarkDoc depending of + * the provided Docs + */ +SdDrawDocument* SdPageObjsTLV::GetBookmarkDoc(SfxMedium* pMed) +{ + if ( + !m_pBookmarkDoc || + (pMed && (!m_pOwnMedium || m_pOwnMedium->GetName() != pMed->GetName())) + ) + { + // create a new BookmarkDoc if now one exists or if a new Medium is provided + if (m_pOwnMedium != pMed) + { + CloseBookmarkDoc(); + } + + if (pMed) + { + // it looks that it is undefined if a Medium was set by Fill() already + DBG_ASSERT( !m_pMedium, "SfxMedium confusion!" ); + delete m_pMedium; + m_pMedium = nullptr; + + // take over this Medium (currently used only be Navigator) + m_pOwnMedium = pMed; + } + + DBG_ASSERT( m_pMedium || pMed, "No SfxMedium provided!" ); + + if( pMed ) + { + // in this mode the document is also owned and controlled by this instance + m_xBookmarkDocShRef = new ::sd::DrawDocShell(SfxObjectCreateMode::STANDARD, true, DocumentType::Impress); + if (m_xBookmarkDocShRef->DoLoad(pMed)) + m_pBookmarkDoc = m_xBookmarkDocShRef->GetDoc(); + else + m_pBookmarkDoc = nullptr; + } + else if ( m_pMedium ) + // in this mode the document is owned and controlled by the SdDrawDocument + // it can be released by calling the corresponding CloseBookmarkDoc method + // successful creation of a document makes this the owner of the medium + m_pBookmarkDoc = const_cast(m_pDoc)->OpenBookmarkDoc(m_pMedium); + + if ( !m_pBookmarkDoc ) + { + std::unique_ptr xErrorBox(Application::CreateMessageDialog(m_xTreeView.get(), + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + m_pMedium = nullptr; //On failure the SfxMedium is invalid + } + } + + return m_pBookmarkDoc; +} + +/** + * Entries are inserted only by request (double click) + */ +IMPL_LINK(SdPageObjsTLV, RequestingChildrenHdl, const weld::TreeIter&, rFileEntry, bool) +{ + if (!m_xTreeView->iter_has_child(rFileEntry)) + { + if (GetBookmarkDoc()) + { + SdrObject* pObj = nullptr; + + OUString sImgPage(BMP_PAGE); + OUString sImgPageObjs(BMP_PAGEOBJS); + OUString sImgObjects(BMP_OBJECTS); + OUString sImgOle(BMP_OLE); + OUString sImgGraphic(BMP_GRAPHIC); + + // document name already inserted + + // only insert all "normal" ? slides with objects + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = m_pBookmarkDoc->GetPageCount(); + + std::unique_ptr xPageEntry; + while (nPage < nMaxPages) + { + SdPage* pPage = static_cast(m_pBookmarkDoc->GetPage(nPage)); + if (pPage->GetPageKind() == PageKind::Standard) + { + OUString sId(OUString::number(1)); + m_xTreeView->insert(&rFileEntry, -1, &pPage->GetName(), &sId, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, sImgPage); + + if (!xPageEntry) + { + xPageEntry = m_xTreeView->make_iterator(&rFileEntry); + (void)m_xTreeView->iter_children(*xPageEntry); + } + else + (void)m_xTreeView->iter_next_sibling(*xPageEntry); + + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + + while( aIter.IsMore() ) + { + pObj = aIter.Next(); + OUString aStr( GetObjectName( pObj ) ); + if( !aStr.isEmpty() ) + { + if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::OLE2 ) + { + m_xTreeView->insert(xPageEntry.get(), -1, &aStr, nullptr, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, sImgOle); + } + else if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::Graphic ) + { + m_xTreeView->insert(xPageEntry.get(), -1, &aStr, nullptr, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, sImgGraphic); + } + else + { + m_xTreeView->insert(xPageEntry.get(), -1, &aStr, nullptr, + nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, sImgObjects); + } + } + } + if (m_xTreeView->iter_has_child(*xPageEntry)) + { + m_xTreeView->set_image(*xPageEntry, sImgPageObjs); + } + } + nPage++; + } + } + } + return true; +} + +void SdPageObjsTLV::SetSdNavigator(SdNavigatorWin* pNavigator) +{ + m_pNavigator = pNavigator; +} + +void SdPageObjsTLV::SetViewFrame(const SfxViewFrame* pViewFrame) +{ + sd::ViewShellBase* pBase = sd::ViewShellBase::GetViewShellBase(pViewFrame); + std::shared_ptr xViewShell = pBase->GetMainViewShell(); + SAL_WARN_IF(!xViewShell, "sd", "null pBaseViewFrame"); + const css::uno::Reference< css::frame::XFrame > xFrame = xViewShell ? xViewShell->GetViewFrame()->GetFrame().GetFrameInterface() : nullptr; + m_xAccel->init(::comphelper::getProcessComponentContext(), xFrame); +} + +/** + * Close and delete bookmark document + */ +void SdPageObjsTLV::CloseBookmarkDoc() +{ + if (m_xBookmarkDocShRef.is()) + { + m_xBookmarkDocShRef->DoClose(); + m_xBookmarkDocShRef.clear(); + + // Medium is owned by document, so it's destroyed already + m_pOwnMedium = nullptr; + } + else if (m_pBookmarkDoc) + { + DBG_ASSERT(!m_pOwnMedium, "SfxMedium confusion!"); + if (m_pDoc) + { + // The document owns the Medium, so the Medium will be invalid after closing the document + const_cast(m_pDoc)->CloseBookmarkDoc(); + m_pMedium = nullptr; + } + } + else + { + // perhaps mpOwnMedium provided, but no successful creation of BookmarkDoc + delete m_pOwnMedium; + m_pOwnMedium = nullptr; + } + + m_pBookmarkDoc = nullptr; +} + +bool SdPageObjsTLV::PageBelongsToCurrentShow(const SdPage* pPage) const +{ + // Return as default when there is no custom show or when none + // is used. The page does then belong to the standard show. + bool bBelongsToShow = true; + + if (m_pDoc->getPresentationSettings().mbCustomShow) + { + // Get the current custom show. + SdCustomShow* pCustomShow = nullptr; + SdCustomShowList* pShowList = const_cast(m_pDoc)->GetCustomShowList(); + if (pShowList != nullptr) + { + sal_uLong nCurrentShowIndex = pShowList->GetCurPos(); + pCustomShow = (*pShowList)[nCurrentShowIndex].get(); + } + + // Check whether the given page is part of that custom show. + if (pCustomShow != nullptr) + { + bBelongsToShow = false; + size_t nPageCount = pCustomShow->PagesVector().size(); + for (size_t i=0; iPagesVector()[i]) + bBelongsToShow = true; + } + } + + return bBelongsToShow; +} + +void SdPageObjsTLV::AddShapeList ( + const SdrObjList& rList, + const SdrObject* pShape, + const OUString& rsName, + const bool bIsExcluded, + const weld::TreeIter* pParentEntry) +{ + OUString aIcon(BMP_PAGE); + if (bIsExcluded) + aIcon = BMP_PAGE_EXCLUDED; + else if (pShape != nullptr) + aIcon = BMP_GROUP; + + OUString aUserData("1"); + if (pShape != nullptr) + aUserData = weld::toId(pShape); + + std::unique_ptr xEntry = m_xTreeView->make_iterator(); + InsertEntry(pParentEntry, aUserData, rsName, aIcon, xEntry.get()); + + SdrObjListIter aIter( + &rList, + !rList.HasObjectNavigationOrder() /* use navigation order, if available */, + SdrIterMode::Flat); + + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OSL_ASSERT(pObj!=nullptr); + + // Get the shape name. + OUString aStr (GetObjectName( pObj ) ); + OUString sId(weld::toId(pObj)); + + if( !aStr.isEmpty() ) + { + if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::OLE2 ) + { + InsertEntry(xEntry.get(), sId, aStr, BMP_OLE); + } + else if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::Graphic ) + { + InsertEntry(xEntry.get(), sId, aStr, BMP_GRAPHIC); + } + else if (pObj->IsGroupObject()) + { + AddShapeList( + *pObj->GetSubList(), + pObj, + aStr, + false, + xEntry.get()); + } + else + { + InsertEntry(xEntry.get(), sId, aStr, BMP_OBJECTS); + } + } + } + + if (!m_xTreeView->iter_has_child(*xEntry)) + return; + + if (bIsExcluded) + m_xTreeView->set_image(*xEntry, BMP_PAGEOBJS_EXCLUDED); + else + m_xTreeView->set_image(*xEntry, BMP_PAGEOBJS); + m_xTreeView->expand_row(*xEntry); +} + +/** + * Fill TreeLB with pages and objects + */ +void SdPageObjsTLV::Fill(const SdDrawDocument* pInDoc, bool bAllPages, const OUString& rDocName) +{ + OUString aSelection = m_xTreeView->get_selected_text(); + clear(); + + m_pDoc = pInDoc; + m_aDocName = rDocName; + m_bShowAllPages = bAllPages; + m_pMedium = nullptr; + + // first insert all pages including objects + sal_uInt16 nPage = 0; + const sal_uInt16 nMaxPages = m_pDoc->GetPageCount(); + + while( nPage < nMaxPages ) + { + const SdPage* pPage = static_cast( m_pDoc->GetPage( nPage ) ); + if( (m_bShowAllPages || pPage->GetPageKind() == PageKind::Standard) + && (pPage->GetPageKind() != PageKind::Handout) ) //#94954# never list the normal handout page ( handout-masterpage is used instead ) + { + bool bPageExcluded = pPage->IsExcluded(); + + bool bPageBelongsToShow = PageBelongsToCurrentShow (pPage); + bPageExcluded |= !bPageBelongsToShow; + + AddShapeList(*pPage, nullptr, pPage->GetName(), bPageExcluded, nullptr); + } + nPage++; + } + + // then insert all master pages including objects + if( m_bShowAllPages ) + { + nPage = 0; + const sal_uInt16 nMaxMasterPages = m_pDoc->GetMasterPageCount(); + + while( nPage < nMaxMasterPages ) + { + const SdPage* pPage = static_cast( m_pDoc->GetMasterPage( nPage ) ); + AddShapeList(*pPage, nullptr, pPage->GetName(), false, nullptr); + nPage++; + } + } + if (!aSelection.isEmpty()) + { + m_xTreeView->all_foreach([this, &aSelection](weld::TreeIter& rEntry){ + if (m_xTreeView->get_text(rEntry) == aSelection) + { + m_xTreeView->select(rEntry); + return true; + } + return false; + }); + } +} + +/** + * We insert only the first entry. Children are created on demand. + */ +void SdPageObjsTLV::Fill( const SdDrawDocument* pInDoc, SfxMedium* pInMedium, + const OUString& rDocName ) +{ + m_pDoc = pInDoc; + + // this object now owns the Medium + m_pMedium = pInMedium; + m_aDocName = rDocName; + + OUString sId(OUString::number(1)); + // insert document name + m_xTreeView->insert(nullptr, -1, &m_aDocName, &sId, nullptr, nullptr, true, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, BMP_DOC_OPEN); +} + +/** + * select an entry in TreeLB + */ +bool SdPageObjsTLV::SelectEntry( std::u16string_view rName ) +{ + bool bFound = false; + + if (!rName.empty()) + { + std::unique_ptr xEntry(m_xTreeView->make_iterator()); + OUString aTmp; + + if (m_xTreeView->get_iter_first(*xEntry)) + { + do + { + aTmp = m_xTreeView->get_text(*xEntry); + if (aTmp == rName) + { + m_xTreeView->set_cursor(*xEntry); + m_xTreeView->select(*xEntry); + bFound = true; + break; + } + } + while (m_xTreeView->iter_next(*xEntry)); + } + } + + return bFound; +} + +SdPageObjsTLV::~SdPageObjsTLV() +{ + if (m_nSelectEventId) + Application::RemoveUserEvent(m_nSelectEventId); + if (m_nRowActivateEventId) + Application::RemoveUserEvent(m_nRowActivateEventId); + + if (m_pBookmarkDoc) + CloseBookmarkDoc(); + else + { + // no document was created from m_pMedium, so this object is still the owner of it + delete m_pMedium; + } + m_xAccel.reset(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/sduiexp.cxx b/sd/source/ui/dlg/sduiexp.cxx new file mode 100644 index 000000000..62901c70d --- /dev/null +++ b/sd/source/ui/dlg/sduiexp.cxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "sddlgfact.hxx" +#include + +class SdAbstractDialogFactory; + +extern "C" { +SAL_DLLPUBLIC_EXPORT SdAbstractDialogFactory* SdCreateDialogFactory() +{ + static SdAbstractDialogFactory_Impl aFactory; + return &aFactory; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/tabtempl.cxx b/sd/source/ui/dlg/tabtempl.cxx new file mode 100644 index 000000000..e5db39e68 --- /dev/null +++ b/sd/source/ui/dlg/tabtempl.cxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/** + * Constructor of the Tab dialog: appends pages to the dialog + */ +SdTabTemplateDlg::SdTabTemplateDlg(weld::Window* pParent, + const SfxObjectShell* pDocShell, + SfxStyleSheetBase& rStyleBase, + SdrModel const * pModel, + SdrView* pView) + : SfxStyleDialogController(pParent, "modules/simpress/ui/templatedialog.ui", + "TemplateDialog", rStyleBase) + , rDocShell(*pDocShell) + , pSdrView(pView) + , pColorList(pModel->GetColorList()) + , pGradientList(pModel->GetGradientList()) + , pHatchingList(pModel->GetHatchList()) + , pBitmapList(pModel->GetBitmapList()) + , pPatternList(pModel->GetPatternList()) + , pDashList(pModel->GetDashList()) + , pLineEndList(pModel->GetLineEndList()) +{ + // fill Listbox and set Select-Handler + + AddTabPage("line", RID_SVXPAGE_LINE); + AddTabPage("area", RID_SVXPAGE_AREA); + AddTabPage("shadowing", RID_SVXPAGE_SHADOW); + AddTabPage("transparency", RID_SVXPAGE_TRANSPARENCE); + AddTabPage("font", RID_SVXPAGE_CHAR_NAME); + AddTabPage("fonteffect", RID_SVXPAGE_CHAR_EFFECTS); + AddTabPage("background", RID_SVXPAGE_BKG); + AddTabPage("indents", RID_SVXPAGE_STD_PARAGRAPH); + AddTabPage("text", RID_SVXPAGE_TEXTATTR); + AddTabPage("animation", RID_SVXPAGE_TEXTANIMATION); + AddTabPage("dimensioning", RID_SVXPAGE_MEASURE); + AddTabPage("connector", RID_SVXPAGE_CONNECTION); + AddTabPage("alignment", RID_SVXPAGE_ALIGN_PARAGRAPH); + AddTabPage("tabs", RID_SVXPAGE_TABULATOR); + if( SvtCJKOptions::IsAsianTypographyEnabled() ) + AddTabPage("asiantypo", RID_SVXPAGE_PARA_ASIAN); + else + RemoveTabPage("asiantypo"); +} + +void SdTabTemplateDlg::PageCreated(const OString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "line") + { + aSet.Put (SvxColorListItem(pColorList,SID_COLOR_TABLE)); + aSet.Put (SvxDashListItem(pDashList,SID_DASH_LIST)); + aSet.Put (SvxLineEndListItem(pLineEndList,SID_LINEEND_LIST)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "area") + { + aSet.Put (SvxColorListItem(pColorList,SID_COLOR_TABLE)); + aSet.Put (SvxGradientListItem(pGradientList,SID_GRADIENT_LIST)); + aSet.Put (SvxHatchListItem(pHatchingList,SID_HATCH_LIST)); + aSet.Put (SvxBitmapListItem(pBitmapList,SID_BITMAP_LIST)); + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + aSet.Put (SfxUInt16Item(SID_TABPAGE_POS,0)); + aSet.Put (SvxPatternListItem(pPatternList,SID_PATTERN_LIST)); + rPage.PageCreated(aSet); + } + else if (rId == "shadowing") + { + aSet.Put (SvxColorListItem(pColorList,SID_COLOR_TABLE)); + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "transparency") + { + aSet.Put (SfxUInt16Item(SID_PAGE_TYPE,0)); + aSet.Put (SfxUInt16Item(SID_DLG_TYPE,1)); + rPage.PageCreated(aSet); + } + else if (rId == "font") + { + SvxFontListItem aItem(*static_cast( + rDocShell.GetItem( SID_ATTR_CHAR_FONTLIST) ) ); + + aSet.Put (SvxFontListItem( aItem.GetFontList(), SID_ATTR_CHAR_FONTLIST)); + rPage.PageCreated(aSet); + } + else if (rId == "fonteffect") + { + rPage.PageCreated(aSet); + } + else if (rId == "background") + { + aSet.Put(SfxUInt32Item(SID_FLAG_TYPE,static_cast(SvxBackgroundTabFlags::SHOW_CHAR_BKGCOLOR))); + rPage.PageCreated(aSet); + } + else if (rId == "text") + { + rPage.PageCreated(aSet); + } + else if (rId == "dimensioning") + { + aSet.Put (OfaPtrItem(SID_OBJECT_LIST,pSdrView)); + rPage.PageCreated(aSet); + } + else if (rId == "connector") + { + aSet.Put (OfaPtrItem(SID_OBJECT_LIST,pSdrView)); + rPage.PageCreated(aSet); + } +} + +void SdTabTemplateDlg::RefreshInputSet() +{ + SfxItemSet* pInputSet = GetInputSetImpl(); + + if( pInputSet ) + { + pInputSet->ClearItem(); + pInputSet->SetParent( GetStyleSheet().GetItemSet().GetParent() ); + } + else + SetInputSet(&GetStyleSheet().GetItemSet()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/titledockwin.cxx b/sd/source/ui/dlg/titledockwin.cxx new file mode 100644 index 000000000..f2aa744c2 --- /dev/null +++ b/sd/source/ui/dlg/titledockwin.cxx @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace sd +{ + //= TitledDockingWindow + TitledDockingWindow::TitledDockingWindow( SfxBindings* i_pBindings, SfxChildWindow* i_pChildWindow, vcl::Window* i_pParent ) + :SfxDockingWindow( i_pBindings, i_pChildWindow, i_pParent, WB_MOVEABLE|WB_CLOSEABLE|WB_DOCKABLE|WB_HIDE|WB_3DLOOK ) + ,m_aToolbox( VclPtr::Create(this) ) + ,m_aContentWindow( VclPtr::Create(this, WB_DIALOGCONTROL) ) + ,m_aBorder( 3, 1, 3, 3 ) + ,m_nTitleBarHeight(0) + { + SetBackground( Wallpaper() ); + + m_aToolbox->SetSelectHdl( LINK( this, TitledDockingWindow, OnToolboxItemSelected ) ); + m_aToolbox->SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetDialogColor() ) ); + m_aToolbox->Show(); + impl_resetToolBox(); + + m_aContentWindow->Show(); + } + + TitledDockingWindow::~TitledDockingWindow() + { + disposeOnce(); + } + + void TitledDockingWindow::dispose() + { + m_aToolbox.disposeAndClear(); + m_aContentWindow.disposeAndClear(); + SfxDockingWindow::dispose(); + } + + void TitledDockingWindow::SetTitle( const OUString& i_rTitle ) + { + m_sTitle = i_rTitle; + Invalidate(); + } + + + void TitledDockingWindow::SetText( const OUString& i_rText ) + { + SfxDockingWindow::SetText( i_rText ); + if ( m_sTitle.isEmpty() ) + // our text is used as title, too => repaint + Invalidate(); + } + + + void TitledDockingWindow::Resize() + { + SfxDockingWindow::Resize(); + impl_layout(); + } + + + void TitledDockingWindow::impl_layout() + { + m_aToolbox->ShowItem( ToolBoxItemId(1), !IsFloatingMode() ); + + const Size aToolBoxSize( m_aToolbox->CalcWindowSizePixel() ); + Size aWindowSize( GetOutputSizePixel() ); + + // position the tool box + m_nTitleBarHeight = GetSettings().GetStyleSettings().GetTitleHeight(); + if ( aToolBoxSize.Height() > m_nTitleBarHeight ) + m_nTitleBarHeight = aToolBoxSize.Height(); + m_aToolbox->SetPosSizePixel( + Point( + aWindowSize.Width() - aToolBoxSize.Width(), + ( m_nTitleBarHeight - aToolBoxSize.Height() ) / 2 + ), + aToolBoxSize + ); + + // Place the content window. + if ( m_nTitleBarHeight < aToolBoxSize.Height() ) + m_nTitleBarHeight = aToolBoxSize.Height(); + aWindowSize.AdjustHeight( -m_nTitleBarHeight ); + m_aContentWindow->SetPosSizePixel( + Point( m_aBorder.Left(), m_nTitleBarHeight + m_aBorder.Top() ), + Size( + aWindowSize.Width() - m_aBorder.Left() - m_aBorder.Right(), + aWindowSize.Height() - m_aBorder.Top() - m_aBorder.Bottom() + ) + ); + } + + void TitledDockingWindow::ApplySettings(vcl::RenderContext& rRenderContext) + { + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + + // Font + ApplyControlFont(rRenderContext, rStyleSettings.GetAppFont()); + + // Color + ApplyControlForeground(rRenderContext, rStyleSettings.GetButtonTextColor()); + rRenderContext.SetTextFillColor(); + } + + void TitledDockingWindow::Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& i_rArea) + { + const StyleSettings& rStyleSettings = rRenderContext.GetSettings().GetStyleSettings(); + + SfxDockingWindow::Paint(rRenderContext, i_rArea); + + rRenderContext.Push(vcl::PushFlags::FONT | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::LINECOLOR); + + rRenderContext.SetFillColor(rStyleSettings.GetDialogColor()); + rRenderContext.SetLineColor(); + + // bold font + vcl::Font aFont(rRenderContext.GetFont()); + aFont.SetWeight(WEIGHT_BOLD); + rRenderContext.SetFont(aFont); + + // Set border values. + Size aWindowSize(GetOutputSizePixel()); + int nOuterLeft = 0; + int nInnerLeft = nOuterLeft + m_aBorder.Left() - 1; + int nOuterRight = aWindowSize.Width() - 1; + int nInnerRight = nOuterRight - m_aBorder.Right() + 1; + int nInnerTop = m_nTitleBarHeight + m_aBorder.Top() - 1; + int nOuterBottom = aWindowSize.Height() - 1; + int nInnerBottom = nOuterBottom - m_aBorder.Bottom() + 1; + + // Paint title bar background. + ::tools::Rectangle aTitleBarBox(::tools::Rectangle(nOuterLeft, 0, nOuterRight, nInnerTop - 1)); + rRenderContext.DrawRect(aTitleBarBox); + + if (nInnerLeft > nOuterLeft) + rRenderContext.DrawRect(::tools::Rectangle(nOuterLeft, nInnerTop, nInnerLeft, nInnerBottom)); + if (nOuterRight > nInnerRight) + rRenderContext.DrawRect(::tools::Rectangle(nInnerRight, nInnerTop, nOuterRight, nInnerBottom)); + if (nInnerBottom < nOuterBottom) + rRenderContext.DrawRect(::tools::Rectangle(nOuterLeft, nInnerBottom, nOuterRight, nOuterBottom)); + + // Paint bevel border. + rRenderContext.SetFillColor(); + rRenderContext.SetLineColor(rStyleSettings.GetShadowColor()); + if (m_aBorder.Top() > 0) + rRenderContext.DrawLine(Point(nInnerLeft, nInnerTop), Point(nInnerLeft, nInnerBottom)); + if (m_aBorder.Left() > 0) + rRenderContext.DrawLine(Point(nInnerLeft, nInnerTop), Point(nInnerRight, nInnerTop)); + + rRenderContext.SetLineColor(rStyleSettings.GetLightColor()); + if (m_aBorder.Bottom() > 0) + rRenderContext.DrawLine(Point(nInnerRight, nInnerBottom), Point(nInnerLeft, nInnerBottom)); + if (m_aBorder.Right() > 0) + rRenderContext.DrawLine(Point(nInnerRight, nInnerBottom), Point(nInnerRight, nInnerTop)); + + // Paint title bar text. + rRenderContext.SetLineColor(rStyleSettings.GetActiveTextColor()); + aTitleBarBox.AdjustLeft(3 ); + rRenderContext.DrawText(aTitleBarBox, + !m_sTitle.isEmpty() ? m_sTitle : GetText(), + DrawTextFlags::Left | DrawTextFlags::VCenter | DrawTextFlags::MultiLine | DrawTextFlags::WordBreak); + + // Restore original values of the output device. + rRenderContext.Pop(); + } + + + void TitledDockingWindow::impl_resetToolBox() + { + m_aToolbox->Clear(); + + // Get the closer bitmap and set it as right most button. + m_aToolbox->InsertItem(ToolBoxItemId(1), Image(StockImage::Yes, SFX_BMP_CLOSE_DOC)); + m_aToolbox->SetQuickHelpText(ToolBoxItemId(1), SdResId(STR_CLOSE_PANE)); + m_aToolbox->ShowItem( ToolBoxItemId(1) ); + } + + + IMPL_LINK( TitledDockingWindow, OnToolboxItemSelected, ToolBox*, pToolBox, void ) + { + const ToolBoxItemId nId = pToolBox->GetCurItemId(); + + if ( nId == ToolBoxItemId(1) ) + { + // the closer + EndTracking(); + const sal_uInt16 nChildWindowId( GetChildWindow_Impl()->GetType() ); + const SfxBoolItem aVisibility( nChildWindowId, false ); + GetBindings().GetDispatcher()->ExecuteList( + nChildWindowId, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aVisibility } + ); + } + } + + + void TitledDockingWindow::StateChanged( StateChangedType i_nType ) + { + switch ( i_nType ) + { + case StateChangedType::InitShow: + impl_layout(); + break; + default:; + } + SfxDockingWindow::StateChanged( i_nType ); + } + + void TitledDockingWindow::DataChanged( const DataChangedEvent& i_rDataChangedEvent ) + { + SfxDockingWindow::DataChanged( i_rDataChangedEvent ); + + switch ( i_rDataChangedEvent.GetType() ) + { + case DataChangedEventType::SETTINGS: + if ( !( i_rDataChangedEvent.GetFlags() & AllSettingsFlags::STYLE ) ) + break; + [[fallthrough]]; + case DataChangedEventType::FONTS: + case DataChangedEventType::FONTSUBSTITUTION: + { + impl_layout(); + Invalidate(); + } + break; + default: break; + } + } + + +} // namespace sfx2 + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/tpaction.cxx b/sd/source/ui/dlg/tpaction.cxx new file mode 100644 index 000000000..f89fa51f5 --- /dev/null +++ b/sd/source/ui/dlg/tpaction.cxx @@ -0,0 +1,801 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace ::com::sun::star; +using namespace com::sun::star::uno; +using namespace com::sun::star::lang; + +#define DOCUMENT_TOKEN '#' + +// XML content stream +constexpr OUStringLiteral pStarDrawXMLContent( u"content.xml" ); + +/** + * Constructor of the Tab dialog: appends the pages to the dialog + */ +SdActionDlg::SdActionDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View const * pView) + : SfxSingleTabDialogController(pParent, pAttr, "modules/simpress/ui/interactiondialog.ui", + "InteractionDialog") +{ + std::unique_ptr xNewPage = SdTPAction::Create(get_content_area(), this, *pAttr); + + // formerly in PageCreated + static_cast( xNewPage.get() )->SetView( pView ); + static_cast( xNewPage.get() )->Construct(); + + SetTabPage(std::move(xNewPage)); +} + +/** + * Action-TabPage + */ +SdTPAction::SdTPAction(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SfxTabPage(pPage, pController, "modules/simpress/ui/interactionpage.ui", "InteractionPage", &rInAttrs) + , mpView(nullptr) + , mpDoc(nullptr) + , bTreeUpdated(false) + , m_xLbAction(m_xBuilder->weld_combo_box("listbox")) + , m_xFtTree(m_xBuilder->weld_label("fttree")) + , m_xLbTree(new SdPageObjsTLV(m_xBuilder->weld_tree_view("tree"))) + , m_xLbTreeDocument(new SdPageObjsTLV(m_xBuilder->weld_tree_view("treedoc"))) + , m_xLbOLEAction(m_xBuilder->weld_tree_view("oleaction")) + , m_xFrame(m_xBuilder->weld_frame("frame")) + , m_xEdtSound(m_xBuilder->weld_entry("sound")) + , m_xEdtBookmark(m_xBuilder->weld_entry("bookmark")) + , m_xEdtDocument(m_xBuilder->weld_entry("document")) + , m_xEdtProgram(m_xBuilder->weld_entry("program")) + , m_xEdtMacro(m_xBuilder->weld_entry("macro")) + , m_xBtnSearch(m_xBuilder->weld_button("browse")) + , m_xBtnSeek(m_xBuilder->weld_button("find")) +{ + m_xLbOLEAction->set_size_request(m_xLbOLEAction->get_approximate_digit_width() * 48, + m_xLbOLEAction->get_height_rows(12)); + + m_xBtnSearch->connect_clicked( LINK( this, SdTPAction, ClickSearchHdl ) ); + m_xBtnSeek->connect_clicked( LINK( this, SdTPAction, ClickSearchHdl ) ); + + // this page needs ExchangeSupport + SetExchangeSupport(); + + m_xLbAction->connect_changed( LINK( this, SdTPAction, ClickActionHdl ) ); + m_xLbTree->connect_changed( LINK( this, SdTPAction, SelectTreeHdl ) ); + m_xEdtDocument->connect_focus_out( LINK( this, SdTPAction, CheckFileHdl ) ); + m_xEdtMacro->connect_focus_out( LINK( this, SdTPAction, CheckFileHdl ) ); + + //Lock to initial max size + Size aSize(m_xContainer->get_preferred_size()); + m_xContainer->set_size_request(aSize.Width(), aSize.Height()); + + ClickActionHdl( *m_xLbAction ); +} + +SdTPAction::~SdTPAction() +{ +} + +void SdTPAction::SetView( const ::sd::View* pSdView ) +{ + mpView = pSdView; + + // get ColorTable and fill ListBox + ::sd::DrawDocShell* pDocSh = mpView->GetDocSh(); + if( pDocSh && pDocSh->GetViewShell() ) + { + mpDoc = pDocSh->GetDoc(); + SfxViewFrame* pFrame = pDocSh->GetViewShell()->GetViewFrame(); + m_xLbTree->SetViewFrame( pFrame ); + m_xLbTreeDocument->SetViewFrame( pFrame ); + + pColList = pDocSh->GetItem( SID_COLOR_TABLE )->GetColorList(); + DBG_ASSERT( pColList.is(), "No color table available!" ); + } + else + { + OSL_FAIL("sd::SdTPAction::SetView(), no docshell or viewshell?"); + } +} + +void SdTPAction::Construct() +{ + // fill OLE-Actionlistbox + SdrOle2Obj* pOleObj = nullptr; + SdrGrafObj* pGrafObj = nullptr; + bool bOLEAction = false; + + if ( mpView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::OLE2) + { + pOleObj = static_cast(pObj); + } + else if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::Graphic) + { + pGrafObj = static_cast(pObj); + } + } + } + if( pGrafObj ) + { + bOLEAction = true; + + aVerbVector.push_back( 0 ); + m_xLbOLEAction->append_text( MnemonicGenerator::EraseAllMnemonicChars( SdResId( STR_EDIT_OBJ ) ) ); + } + else if( pOleObj ) + { + const uno::Reference < embed::XEmbeddedObject >& xObj = pOleObj->GetObjRef(); + if ( xObj.is() ) + { + bOLEAction = true; + uno::Sequence < embed::VerbDescriptor > aVerbs; + try + { + aVerbs = xObj->getSupportedVerbs(); + } + catch ( embed::NeedsRunningStateException& ) + { + xObj->changeState( embed::EmbedStates::RUNNING ); + aVerbs = xObj->getSupportedVerbs(); + } + + for( const embed::VerbDescriptor& aVerb : std::as_const(aVerbs) ) + { + if( aVerb.VerbAttributes & embed::VerbAttributes::MS_VERBATTR_ONCONTAINERMENU ) + { + OUString aTmp( aVerb.VerbName ); + aVerbVector.push_back( aVerb.VerbID ); + m_xLbOLEAction->append_text( MnemonicGenerator::EraseAllMnemonicChars( aTmp ) ); + } + } + } + } + + maCurrentActions.push_back( presentation::ClickAction_NONE ); + maCurrentActions.push_back( presentation::ClickAction_PREVPAGE ); + maCurrentActions.push_back( presentation::ClickAction_NEXTPAGE ); + maCurrentActions.push_back( presentation::ClickAction_FIRSTPAGE ); + maCurrentActions.push_back( presentation::ClickAction_LASTPAGE ); + maCurrentActions.push_back( presentation::ClickAction_BOOKMARK ); + maCurrentActions.push_back( presentation::ClickAction_DOCUMENT ); + maCurrentActions.push_back( presentation::ClickAction_SOUND ); + if( bOLEAction && m_xLbOLEAction->n_children() ) + maCurrentActions.push_back( presentation::ClickAction_VERB ); + maCurrentActions.push_back( presentation::ClickAction_PROGRAM ); + maCurrentActions.push_back( presentation::ClickAction_MACRO ); + maCurrentActions.push_back( presentation::ClickAction_STOPPRESENTATION ); + + // fill Action-Listbox + for (const presentation::ClickAction & rAction : maCurrentActions) + { + TranslateId pRId = GetClickActionSdResId(rAction); + m_xLbAction->append_text(SdResId(pRId)); + } + +} + +bool SdTPAction::FillItemSet( SfxItemSet* rAttrs ) +{ + bool bModified = false; + presentation::ClickAction eCA = presentation::ClickAction_NONE; + + if (m_xLbAction->get_active() != -1) + eCA = GetActualClickAction(); + + if( m_xLbAction->get_value_changed_from_saved() ) + { + rAttrs->Put( SfxUInt16Item( ATTR_ACTION, static_cast(eCA) ) ); + bModified = true; + } + else + rAttrs->InvalidateItem( ATTR_ACTION ); + + OUString aFileName = GetEditText( true ); + if( aFileName.isEmpty() ) + rAttrs->InvalidateItem( ATTR_ACTION_FILENAME ); + else + { + if( mpDoc && mpDoc->GetDocSh() && mpDoc->GetDocSh()->GetMedium() ) + { + OUString aBaseURL = mpDoc->GetDocSh()->GetMedium()->GetBaseURL(); + if( eCA == presentation::ClickAction_SOUND || + eCA == presentation::ClickAction_DOCUMENT || + eCA == presentation::ClickAction_PROGRAM ) + aFileName = ::URIHelper::SmartRel2Abs( INetURLObject(aBaseURL), aFileName, URIHelper::GetMaybeFileHdl(), true, false, + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ); + + rAttrs->Put( SfxStringItem( ATTR_ACTION_FILENAME, aFileName ) ); + bModified = true; + } + else + { + OSL_FAIL("sd::SdTPAction::FillItemSet(), I need a medium!"); + } + } + + return bModified; +} + +void SdTPAction::Reset( const SfxItemSet* rAttrs ) +{ + presentation::ClickAction eCA = presentation::ClickAction_NONE; + OUString aFileName; + + // m_xLbAction + if( rAttrs->GetItemState( ATTR_ACTION ) != SfxItemState::DONTCARE ) + { + eCA = static_cast(static_cast( rAttrs-> + Get( ATTR_ACTION ) ).GetValue()); + SetActualClickAction( eCA ); + } + else + m_xLbAction->set_active(-1); + + // m_xEdtSound + if( rAttrs->GetItemState( ATTR_ACTION_FILENAME ) != SfxItemState::DONTCARE ) + { + aFileName = static_cast( rAttrs->Get( ATTR_ACTION_FILENAME ) ).GetValue(); + SetEditText( aFileName ); + } + + switch( eCA ) + { + case presentation::ClickAction_BOOKMARK: + { + if (!m_xLbTree->SelectEntry(aFileName)) + m_xLbTree->unselect_all(); + } + break; + + case presentation::ClickAction_DOCUMENT: + { + if( comphelper::string::getTokenCount(aFileName, DOCUMENT_TOKEN) == 2 ) + m_xLbTreeDocument->SelectEntry( o3tl::getToken(aFileName, 1, DOCUMENT_TOKEN ) ); + } + break; + + default: + break; + } + ClickActionHdl( *m_xLbAction ); + + m_xLbAction->save_value(); + m_xEdtSound->save_value(); +} + +void SdTPAction::ActivatePage( const SfxItemSet& ) +{ +} + +DeactivateRC SdTPAction::DeactivatePage( SfxItemSet* pPageSet ) +{ + if( pPageSet ) + FillItemSet( pPageSet ); + + return DeactivateRC::LeavePage; +} + +std::unique_ptr SdTPAction::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rAttrs) +{ + return std::make_unique( pPage, pController, rAttrs ); +} + +void SdTPAction::UpdateTree() +{ + if( !bTreeUpdated && mpDoc && mpDoc->GetDocSh() && mpDoc->GetDocSh()->GetMedium() ) + { + m_xLbTree->Fill( mpDoc, true, mpDoc->GetDocSh()->GetMedium()->GetName() ); + bTreeUpdated = true; + } +} + +void SdTPAction::OpenFileDialog() +{ + // Soundpreview only for interaction with sound + presentation::ClickAction eCA = GetActualClickAction(); + bool bSound = ( eCA == presentation::ClickAction_SOUND ); + bool bPage = ( eCA == presentation::ClickAction_BOOKMARK ); + bool bDocument = ( eCA == presentation::ClickAction_DOCUMENT || + eCA == presentation::ClickAction_PROGRAM ); + bool bMacro = ( eCA == presentation::ClickAction_MACRO ); + + if( bPage ) + { + // search in the TreeLB for the specified object + m_xLbTree->SelectEntry(GetEditText()); + } + else + { + OUString aFile( GetEditText() ); + + if (bSound) + { + SdOpenSoundFileDialog aFileDialog(GetFrameWeld()); + + if( !aFile.isEmpty() ) + aFileDialog.SetPath( aFile ); + + if( aFileDialog.Execute() == ERRCODE_NONE ) + { + aFile = aFileDialog.GetPath(); + SetEditText( aFile ); + } + } + else if (bMacro) + { + // choose macro dialog + OUString aScriptURL = SfxApplication::ChooseScript(GetFrameWeld()); + + if ( !aScriptURL.isEmpty() ) + { + SetEditText( aScriptURL ); + } + } + else + { + sfx2::FileDialogHelper aFileDialog( + ui::dialogs::TemplateDescription::FILEOPEN_READONLY_VERSION, + FileDialogFlags::NONE, GetFrameWeld()); + aFileDialog.SetContext(sfx2::FileDialogHelper::ImpressClickAction); + + // The following is a workaround for #i4306#: + // The addition of the implicitly existing "all files" + // filter makes the (Windows system) open file dialog follow + // links on the desktop to directories. + aFileDialog.AddFilter ( + SfxResId(STR_SFX_FILTERNAME_ALL), + "*.*"); + + if( aFileDialog.Execute() == ERRCODE_NONE ) + { + aFile = aFileDialog.GetPath(); + SetEditText( aFile ); + } + if( bDocument ) + CheckFileHdl( *m_xEdtDocument ); + } + } +} + +IMPL_LINK_NOARG(SdTPAction, ClickSearchHdl, weld::Button&, void) +{ + OpenFileDialog(); +} + +IMPL_LINK_NOARG(SdTPAction, ClickActionHdl, weld::ComboBox&, void) +{ + presentation::ClickAction eCA = GetActualClickAction(); + + // hide controls we don't need + switch( eCA ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_STOPPRESENTATION: + default: + m_xFtTree->hide(); + m_xLbTree->hide(); + m_xLbTreeDocument->hide(); + m_xLbOLEAction->hide(); + + m_xFrame->hide(); + m_xEdtSound->hide(); + m_xEdtBookmark->hide(); + m_xEdtDocument->hide(); + m_xEdtProgram->hide(); + m_xEdtMacro->hide(); + m_xBtnSearch->hide(); + m_xBtnSeek->hide(); + break; + + case presentation::ClickAction_SOUND: + case presentation::ClickAction_PROGRAM: + case presentation::ClickAction_MACRO: + m_xFtTree->hide(); + m_xLbTree->hide(); + m_xLbTreeDocument->hide(); + m_xLbOLEAction->hide(); + + m_xEdtDocument->hide(); + + if( eCA == presentation::ClickAction_MACRO ) + { + m_xEdtSound->hide(); + m_xEdtProgram->hide(); + } + else if( eCA == presentation::ClickAction_PROGRAM ) + { + m_xEdtSound->hide(); + m_xEdtMacro->hide(); + } + else if( eCA == presentation::ClickAction_SOUND ) + { + m_xEdtProgram->hide(); + m_xEdtMacro->hide(); + } + + m_xBtnSeek->hide(); + break; + + case presentation::ClickAction_DOCUMENT: + m_xLbTree->hide(); + m_xLbOLEAction->hide(); + + m_xEdtSound->hide(); + m_xEdtProgram->hide(); + m_xEdtMacro->hide(); + m_xEdtBookmark->hide(); + m_xBtnSeek->hide(); + break; + + case presentation::ClickAction_BOOKMARK: + m_xLbTreeDocument->hide(); + m_xLbOLEAction->hide(); + m_xEdtSound->hide(); + m_xEdtDocument->hide(); + m_xEdtProgram->hide(); + m_xEdtMacro->hide(); + m_xBtnSearch->hide(); + break; + + case presentation::ClickAction_VERB: + m_xLbTree->hide(); + m_xEdtDocument->hide(); + m_xEdtProgram->hide(); + m_xEdtBookmark->hide(); + m_xEdtMacro->hide(); + m_xBtnSearch->hide(); + m_xFrame->hide(); + m_xEdtSound->hide(); + m_xBtnSeek->hide(); + break; + } + + // show controls we do need + switch( eCA ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_STOPPRESENTATION: + // none + break; + + case presentation::ClickAction_SOUND: + m_xFrame->show(); + m_xEdtSound->show(); + m_xEdtSound->set_sensitive(true); + m_xBtnSearch->show(); + m_xBtnSearch->set_sensitive(true); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_SOUND ) ); + break; + + case presentation::ClickAction_PROGRAM: + case presentation::ClickAction_MACRO: + m_xFrame->show(); + m_xBtnSearch->show(); + m_xBtnSearch->set_sensitive(true); + if( eCA == presentation::ClickAction_MACRO ) + { + m_xEdtMacro->show(); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_MACRO ) ); + } + else + { + m_xEdtProgram->show(); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_PROGRAM ) ); + } + break; + + case presentation::ClickAction_DOCUMENT: + m_xFtTree->show(); + m_xLbTreeDocument->show(); + + m_xFrame->show(); + m_xEdtDocument->show(); + m_xBtnSearch->show(); + m_xBtnSearch->set_sensitive(true); + + m_xFtTree->set_label( SdResId( STR_EFFECTDLG_JUMP ) ); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_DOCUMENT ) ); + + CheckFileHdl( *m_xEdtDocument ); + break; + + case presentation::ClickAction_VERB: + m_xFtTree->show(); + m_xLbOLEAction->show(); + + m_xFtTree->set_label( SdResId( STR_EFFECTDLG_ACTION ) ); + break; + + case presentation::ClickAction_BOOKMARK: + UpdateTree(); + + m_xFtTree->show(); + m_xLbTree->show(); + + m_xFrame->show(); + m_xEdtBookmark->show(); + m_xBtnSeek->show(); + + m_xFtTree->set_label( SdResId( STR_EFFECTDLG_JUMP ) ); + m_xFrame->set_label( SdResId( STR_EFFECTDLG_PAGE_OBJECT ) ); + break; + default: + break; + } +} + +IMPL_LINK_NOARG(SdTPAction, SelectTreeHdl, weld::TreeView&, void) +{ + m_xEdtBookmark->set_text( m_xLbTree->get_selected_text() ); +} + +IMPL_LINK_NOARG(SdTPAction, CheckFileHdl, weld::Widget&, void) +{ + OUString aFile( GetEditText() ); + + if( aFile == aLastFile ) + return; + + bool bHideTreeDocument = true; + + if (mpDoc) + { + // check if it is a valid draw file + SfxMedium aMedium( aFile, + StreamMode::READ | StreamMode::NOCREATE ); + + if( aMedium.IsStorage() ) + { + weld::WaitObject aWait(GetFrameWeld()); + + // is it a draw file? + // open with READ, otherwise the Storages might write into the file! + uno::Reference < embed::XStorage > xStorage = aMedium.GetStorage(); + DBG_ASSERT( xStorage.is(), "No storage!" ); + + if (xStorage.is()) + { + try + { + if (xStorage->hasByName(pStarDrawXMLContent)) + { + if (SdDrawDocument* pBookmarkDoc = mpDoc->OpenBookmarkDoc(aFile)) + { + aLastFile = aFile; + + m_xLbTreeDocument->clear(); + m_xLbTreeDocument->Fill(pBookmarkDoc, true, aFile); + mpDoc->CloseBookmarkDoc(); + m_xLbTreeDocument->show(); + bHideTreeDocument = false; + } + } + } + catch (...) + { + } + } + } + } + + if (bHideTreeDocument) + m_xLbTreeDocument->hide(); +} + +presentation::ClickAction SdTPAction::GetActualClickAction() +{ + presentation::ClickAction eCA = presentation::ClickAction_NONE; + int nPos = m_xLbAction->get_active(); + if (nPos != -1 && o3tl::make_unsigned(nPos) < maCurrentActions.size()) + eCA = maCurrentActions[ nPos ]; + return eCA; +} + +void SdTPAction::SetActualClickAction( presentation::ClickAction eCA ) +{ + std::vector::const_iterator pIter = + std::find(maCurrentActions.begin(),maCurrentActions.end(),eCA); + + if ( pIter != maCurrentActions.end() ) + m_xLbAction->set_active(pIter-maCurrentActions.begin()); +} + +void SdTPAction::SetEditText( OUString const & rStr ) +{ + presentation::ClickAction eCA = GetActualClickAction(); + OUString aText(rStr); + + // possibly convert URI back to system path + switch( eCA ) + { + case presentation::ClickAction_DOCUMENT: + if( comphelper::string::getTokenCount(rStr, DOCUMENT_TOKEN) == 2 ) + aText = rStr.getToken( 0, DOCUMENT_TOKEN ); + + [[fallthrough]]; + case presentation::ClickAction_SOUND: + case presentation::ClickAction_PROGRAM: + { + INetURLObject aURL( aText ); + + // try to convert to system path + OUString aTmpStr(aURL.getFSysPath(FSysStyle::Detect)); + + if( !aTmpStr.isEmpty() ) + aText = aTmpStr; // was a system path + } + break; + default: + break; + } + + // set the string to the corresponding control + switch( eCA ) + { + case presentation::ClickAction_SOUND: + m_xEdtSound->set_text(aText ); + break; + case presentation::ClickAction_VERB: + { + ::std::vector< tools::Long >::iterator aFound( ::std::find( aVerbVector.begin(), aVerbVector.end(), rStr.toInt32() ) ); + if( aFound != aVerbVector.end() ) + m_xLbOLEAction->select(aFound - aVerbVector.begin()); + } + break; + case presentation::ClickAction_PROGRAM: + m_xEdtProgram->set_text( aText ); + break; + case presentation::ClickAction_MACRO: + m_xEdtMacro->set_text( aText ); + break; + case presentation::ClickAction_DOCUMENT: + m_xEdtDocument->set_text( aText ); + break; + case presentation::ClickAction_BOOKMARK: + m_xEdtBookmark->set_text( aText ); + break; + default: + break; + } +} + +OUString SdTPAction::GetEditText( bool bFullDocDestination ) +{ + OUString aStr; + presentation::ClickAction eCA = GetActualClickAction(); + + switch( eCA ) + { + case presentation::ClickAction_SOUND: + aStr = m_xEdtSound->get_text(); + break; + case presentation::ClickAction_VERB: + { + const int nPos = m_xLbOLEAction->get_selected_index(); + if (nPos != -1 && o3tl::make_unsigned(nPos) < aVerbVector.size() ) + aStr = OUString::number( aVerbVector[ nPos ] ); + return aStr; + } + case presentation::ClickAction_DOCUMENT: + aStr = m_xEdtDocument->get_text(); + break; + + case presentation::ClickAction_PROGRAM: + aStr = m_xEdtProgram->get_text(); + break; + + case presentation::ClickAction_MACRO: + { + return m_xEdtMacro->get_text(); + } + + case presentation::ClickAction_BOOKMARK: + return m_xEdtBookmark->get_text(); + + default: + break; + } + + // validate file URI + INetURLObject aURL( aStr ); + OUString aBaseURL; + if( mpDoc && mpDoc->GetDocSh() && mpDoc->GetDocSh()->GetMedium() ) + aBaseURL = mpDoc->GetDocSh()->GetMedium()->GetBaseURL(); + + if( !aStr.isEmpty() && aURL.GetProtocol() == INetProtocol::NotValid ) + aURL = INetURLObject( ::URIHelper::SmartRel2Abs( INetURLObject(aBaseURL), aStr, URIHelper::GetMaybeFileHdl() ) ); + + // get adjusted file name + aStr = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + if( bFullDocDestination && + eCA == presentation::ClickAction_DOCUMENT && + m_xLbTreeDocument->get_visible() && + m_xLbTreeDocument->get_selected() ) + { + OUString aTmpStr( m_xLbTreeDocument->get_selected_text() ); + if( !aTmpStr.isEmpty() ) + { + aStr += OUStringChar(DOCUMENT_TOKEN) + aTmpStr; + } + } + + return aStr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/tpoption.cxx b/sd/source/ui/dlg/tpoption.cxx new file mode 100644 index 000000000..0c534682d --- /dev/null +++ b/sd/source/ui/dlg/tpoption.cxx @@ -0,0 +1,618 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +SdTpOptionsSnap::SdTpOptionsSnap(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SvxGridTabPage(pPage, pController, rInAttrs) +{ + m_xSnapFrames->show(); +} + +SdTpOptionsSnap::~SdTpOptionsSnap() +{ +} + +bool SdTpOptionsSnap::FillItemSet( SfxItemSet* rAttrs ) +{ + SvxGridTabPage::FillItemSet(rAttrs); + SdOptionsSnapItem aOptsItem; + + aOptsItem.GetOptionsSnap().SetSnapHelplines( m_xCbxSnapHelplines->get_active() ); + aOptsItem.GetOptionsSnap().SetSnapBorder( m_xCbxSnapBorder->get_active() ); + aOptsItem.GetOptionsSnap().SetSnapFrame( m_xCbxSnapFrame->get_active() ); + aOptsItem.GetOptionsSnap().SetSnapPoints( m_xCbxSnapPoints->get_active() ); + aOptsItem.GetOptionsSnap().SetOrtho( m_xCbxOrtho->get_active() ); + aOptsItem.GetOptionsSnap().SetBigOrtho( m_xCbxBigOrtho->get_active() ); + aOptsItem.GetOptionsSnap().SetRotate( m_xCbxRotate->get_active() ); + aOptsItem.GetOptionsSnap().SetSnapArea(static_cast(m_xMtrFldSnapArea->get_value(FieldUnit::PIXEL))); + aOptsItem.GetOptionsSnap().SetAngle(Degree100(m_xMtrFldAngle->get_value(FieldUnit::DEGREE))); + aOptsItem.GetOptionsSnap().SetEliminatePolyPointLimitAngle(Degree100(m_xMtrFldBezAngle->get_value(FieldUnit::DEGREE))); + + rAttrs->Put( aOptsItem ); + + // we get a possible existing GridItem, this ensures that we do not set + // some default values by accident + return true; +} + +void SdTpOptionsSnap::Reset( const SfxItemSet* rAttrs ) +{ + SvxGridTabPage::Reset(rAttrs); + + SdOptionsSnapItem aOptsItem( rAttrs->Get( ATTR_OPTIONS_SNAP ) ); + + m_xCbxSnapHelplines->set_active( aOptsItem.GetOptionsSnap().IsSnapHelplines() ); + m_xCbxSnapBorder->set_active( aOptsItem.GetOptionsSnap().IsSnapBorder() ); + m_xCbxSnapFrame->set_active( aOptsItem.GetOptionsSnap().IsSnapFrame() ); + m_xCbxSnapPoints->set_active( aOptsItem.GetOptionsSnap().IsSnapPoints() ); + m_xCbxOrtho->set_active( aOptsItem.GetOptionsSnap().IsOrtho() ); + m_xCbxBigOrtho->set_active( aOptsItem.GetOptionsSnap().IsBigOrtho() ); + m_xCbxRotate->set_active( aOptsItem.GetOptionsSnap().IsRotate() ); + m_xMtrFldSnapArea->set_value(aOptsItem.GetOptionsSnap().GetSnapArea(), FieldUnit::PIXEL); + m_xMtrFldAngle->set_value(aOptsItem.GetOptionsSnap().GetAngle().get(), FieldUnit::DEGREE); + m_xMtrFldBezAngle->set_value(aOptsItem.GetOptionsSnap().GetEliminatePolyPointLimitAngle().get(), FieldUnit::DEGREE); + + ClickRotateHdl_Impl(*m_xCbxRotate); +} + +std::unique_ptr SdTpOptionsSnap::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrs ) +{ + return std::make_unique(pPage, pController, *rAttrs); +} + +/************************************************************************* +|* +|* TabPage to adjust the content options +|* +\************************************************************************/ +SdTpOptionsContents::SdTpOptionsContents(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SfxTabPage(pPage, pController, "modules/simpress/ui/sdviewpage.ui", "SdViewPage", &rInAttrs) + , m_xCbxRuler(m_xBuilder->weld_check_button("ruler")) + , m_xCbxDragStripes(m_xBuilder->weld_check_button("dragstripes")) + , m_xCbxHandlesBezier(m_xBuilder->weld_check_button("handlesbezier")) + , m_xCbxMoveOutline(m_xBuilder->weld_check_button("moveoutline")) +{ +} + +SdTpOptionsContents::~SdTpOptionsContents() +{ +} + +bool SdTpOptionsContents::FillItemSet( SfxItemSet* rAttrs ) +{ + bool bModified = false; + + if( m_xCbxRuler->get_state_changed_from_saved() || + m_xCbxMoveOutline->get_state_changed_from_saved() || + m_xCbxDragStripes->get_state_changed_from_saved() || + m_xCbxHandlesBezier->get_state_changed_from_saved() ) + { + SdOptionsLayoutItem aOptsItem; + + aOptsItem.GetOptionsLayout().SetRulerVisible( m_xCbxRuler->get_active() ); + aOptsItem.GetOptionsLayout().SetMoveOutline( m_xCbxMoveOutline->get_active() ); + aOptsItem.GetOptionsLayout().SetDragStripes( m_xCbxDragStripes->get_active() ); + aOptsItem.GetOptionsLayout().SetHandlesBezier( m_xCbxHandlesBezier->get_active() ); + + rAttrs->Put( aOptsItem ); + bModified = true; + } + return bModified; +} + +void SdTpOptionsContents::Reset( const SfxItemSet* rAttrs ) +{ + SdOptionsLayoutItem aLayoutItem( rAttrs->Get( ATTR_OPTIONS_LAYOUT ) ); + + m_xCbxRuler->set_active( aLayoutItem.GetOptionsLayout().IsRulerVisible() ); + m_xCbxMoveOutline->set_active( aLayoutItem.GetOptionsLayout().IsMoveOutline() ); + m_xCbxDragStripes->set_active( aLayoutItem.GetOptionsLayout().IsDragStripes() ); + m_xCbxHandlesBezier->set_active( aLayoutItem.GetOptionsLayout().IsHandlesBezier() ); + + m_xCbxRuler->save_state(); + m_xCbxMoveOutline->save_state(); + m_xCbxDragStripes->save_state(); + m_xCbxHandlesBezier->save_state(); +} + +std::unique_ptr SdTpOptionsContents::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrs ) +{ + return std::make_unique(pPage, pController, *rAttrs); +} + +/************************************************************************* +|* +|* TabPage to adjust the misc options +|* +\************************************************************************/ +#define TABLE_COUNT 12 +#define TOKEN ':' + +SdTpOptionsMisc::SdTpOptionsMisc(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs) + : SfxTabPage(pPage, pController, "modules/simpress/ui/optimpressgeneralpage.ui", "OptSavePage", &rInAttrs) + , nWidth(0) + , nHeight(0) + , m_xCbxQuickEdit(m_xBuilder->weld_check_button("qickedit")) + , m_xCbxPickThrough(m_xBuilder->weld_check_button("textselected")) + , m_xNewDocumentFrame(m_xBuilder->weld_frame("newdocumentframe")) + , m_xCbxStartWithTemplate(m_xBuilder->weld_check_button("startwithwizard")) + , m_xCbxMasterPageCache(m_xBuilder->weld_check_button("backgroundback")) + , m_xCbxCopy(m_xBuilder->weld_check_button("copywhenmove")) + , m_xCbxMarkedHitMovesAlways(m_xBuilder->weld_check_button("objalwymov")) + , m_xPresentationFrame(m_xBuilder->weld_frame("presentationframe")) + , m_xLbMetric(m_xBuilder->weld_combo_box("units")) + , m_xMtrFldTabstop(m_xBuilder->weld_metric_spin_button("metricFields", FieldUnit::MM)) + , m_xCbxEnableSdremote(m_xBuilder->weld_check_button("enremotcont")) + , m_xCbxEnablePresenterScreen(m_xBuilder->weld_check_button("enprsntcons")) + , m_xCbxUsePrinterMetrics(m_xBuilder->weld_check_button("printermetrics")) + , m_xCbxCompatibility(m_xBuilder->weld_check_button("cbCompatibility")) + , m_xScaleFrame(m_xBuilder->weld_frame("scaleframe")) + , m_xCbScale(m_xBuilder->weld_combo_box("scaleBox")) + , m_xNewDocLb(m_xBuilder->weld_label("newdoclbl")) + , m_xFiInfo1(m_xBuilder->weld_label("info1")) + , m_xMtrFldOriginalWidth(m_xBuilder->weld_metric_spin_button("metricWidthFields", FieldUnit::MM)) + , m_xWidthLb(m_xBuilder->weld_label("widthlbl")) + , m_xHeightLb(m_xBuilder->weld_label("heightlbl")) + , m_xFiInfo2(m_xBuilder->weld_label("info2")) + , m_xMtrFldOriginalHeight(m_xBuilder->weld_metric_spin_button("metricHeightFields", FieldUnit::MM)) + , m_xCbxDistort(m_xBuilder->weld_check_button("distortcb")) + , m_xMtrFldInfo1(m_xBuilder->weld_metric_spin_button("metricInfo1Fields", FieldUnit::MM)) + , m_xMtrFldInfo2(m_xBuilder->weld_metric_spin_button("metricInfo2Fields", FieldUnit::MM)) +{ + SetExchangeSupport(); + + // set metric + FieldUnit eFUnit; + + sal_uInt16 nWhich = GetWhich( SID_ATTR_METRIC ); + if ( rInAttrs.GetItemState( nWhich ) >= SfxItemState::DEFAULT ) + { + const SfxUInt16Item& rItem = static_cast(rInAttrs.Get( nWhich )); + eFUnit = static_cast(rItem.GetValue()); + } + else + eFUnit = SfxModule::GetCurrentFieldUnit(); + + SetFieldUnit( *m_xMtrFldTabstop , eFUnit ); + // tdf#148292 - avoid right frame to change position depending on width of this control + m_xMtrFldTabstop->set_size_request(150, -1); + // Impress is default mode, let' hide the entire scale frame etc. + m_xCbxDistort->hide(); + m_xScaleFrame->hide(); + + // fill ListBox with metrics + for (sal_uInt32 i = 0; i < SvxFieldUnitTable::Count(); ++i) + { + OUString sMetric = SvxFieldUnitTable::GetString(i); + sal_uInt32 nFieldUnit = sal_uInt32(SvxFieldUnitTable::GetValue(i)); + m_xLbMetric->append(OUString::number(nFieldUnit), sMetric); + } + m_xLbMetric->connect_changed( LINK( this, SdTpOptionsMisc, SelectMetricHdl_Impl ) ); + + SetFieldUnit( *m_xMtrFldOriginalWidth, eFUnit ); + SetFieldUnit( *m_xMtrFldOriginalHeight, eFUnit ); + m_xMtrFldOriginalWidth->set_max(999999999, FieldUnit::NONE); + m_xMtrFldOriginalHeight->set_max(999999999, FieldUnit::NONE); + + // temporary fields for info texts (for formatting/calculation) + m_xMtrFldInfo1->set_unit( eFUnit ); + m_xMtrFldInfo1->set_max(999999999, FieldUnit::NONE); + m_xMtrFldInfo1->set_digits( 2 ); + m_xMtrFldInfo2->set_unit( eFUnit ); + m_xMtrFldInfo2->set_max(999999999, FieldUnit::NONE); + m_xMtrFldInfo2->set_digits( 2 ); + + // determine PoolUnit + SfxItemPool* pPool = rInAttrs.GetPool(); + DBG_ASSERT( pPool, "Where is the Pool?" ); + ePoolUnit = pPool->GetMetric( SID_ATTR_FILL_HATCH ); + + // Fill the CB + sal_uInt16 aTable[ TABLE_COUNT ] = + { 1, 2, 4, 5, 8, 10, 16, 20, 30, 40, 50, 100 }; + + for( sal_uInt16 i = TABLE_COUNT-1; i > 0 ; i-- ) + m_xCbScale->append_text( GetScale( 1, aTable[i] ) ); + for( sal_uInt16 i = 0; i < TABLE_COUNT; i++ ) + m_xCbScale->append_text( GetScale( aTable[i], 1 ) ); +} + +SdTpOptionsMisc::~SdTpOptionsMisc() +{ +} + +void SdTpOptionsMisc::ActivatePage( const SfxItemSet& rSet ) +{ + // We have to call save_state again since it can happen that the value + // has no effect on other TabPages + m_xLbMetric->save_value(); + // change metric if necessary (since TabPage is in the Dialog where + // the metric is set) + const SfxUInt16Item* pAttr = rSet.GetItemIfSet( SID_ATTR_METRIC , false ); + if( !pAttr ) + return; + + FieldUnit eFUnit = static_cast(static_cast(pAttr->GetValue())); + + if( eFUnit == m_xMtrFldOriginalWidth->get_unit() ) + return; + + // set metrics + sal_Int64 nVal = m_xMtrFldOriginalWidth->denormalize( m_xMtrFldOriginalWidth->get_value( FieldUnit::TWIP ) ); + SetFieldUnit( *m_xMtrFldOriginalWidth, eFUnit, true ); + m_xMtrFldOriginalWidth->set_value( m_xMtrFldOriginalWidth->normalize( nVal ), FieldUnit::TWIP ); + + nVal = m_xMtrFldOriginalHeight->denormalize( m_xMtrFldOriginalHeight->get_value( FieldUnit::TWIP ) ); + SetFieldUnit( *m_xMtrFldOriginalHeight, eFUnit, true ); + m_xMtrFldOriginalHeight->set_value( m_xMtrFldOriginalHeight->normalize( nVal ), FieldUnit::TWIP ); + + if( nWidth == 0 || nHeight == 0 ) + return; + + m_xMtrFldInfo1->set_unit( eFUnit ); + m_xMtrFldInfo2->set_unit( eFUnit ); + + SetMetricValue( *m_xMtrFldInfo1, nWidth, ePoolUnit ); + aInfo1 = m_xMtrFldInfo1->get_text(); + m_xFiInfo1->set_label( aInfo1 ); + + SetMetricValue( *m_xMtrFldInfo2, nHeight, ePoolUnit ); + aInfo2 = m_xMtrFldInfo2->get_text(); + m_xFiInfo2->set_label( aInfo2 ); +} + +DeactivateRC SdTpOptionsMisc::DeactivatePage( SfxItemSet* pActiveSet ) +{ + // check parser + sal_Int32 nX, nY; + if( SetScale( m_xCbScale->get_active_text(), nX, nY ) ) + { + if( pActiveSet ) + FillItemSet( pActiveSet ); + return DeactivateRC::LeavePage; + } + + std::unique_ptr xWarn(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::YesNo, + SdResId(STR_WARN_SCALE_FAIL))); + if (xWarn->run() == RET_YES) + return DeactivateRC::KeepPage; + + if( pActiveSet ) + FillItemSet( pActiveSet ); + + return DeactivateRC::LeavePage; +} + +bool SdTpOptionsMisc::FillItemSet( SfxItemSet* rAttrs ) +{ + bool bModified = false; + + if( m_xCbxStartWithTemplate->get_state_changed_from_saved() || + m_xCbxMarkedHitMovesAlways->get_state_changed_from_saved() || + m_xCbxQuickEdit->get_state_changed_from_saved() || + m_xCbxPickThrough->get_state_changed_from_saved() || + m_xCbxMasterPageCache->get_state_changed_from_saved() || + m_xCbxCopy->get_state_changed_from_saved() || + m_xCbxEnableSdremote->get_state_changed_from_saved() || + m_xCbxEnablePresenterScreen->get_state_changed_from_saved() || + m_xCbxCompatibility->get_state_changed_from_saved() || + m_xCbxUsePrinterMetrics->get_state_changed_from_saved() || + m_xCbxDistort->get_state_changed_from_saved()) + { + SdOptionsMiscItem aOptsItem; + + aOptsItem.GetOptionsMisc().SetStartWithTemplate( m_xCbxStartWithTemplate->get_active() ); + aOptsItem.GetOptionsMisc().SetMarkedHitMovesAlways( m_xCbxMarkedHitMovesAlways->get_active() ); + aOptsItem.GetOptionsMisc().SetQuickEdit( m_xCbxQuickEdit->get_active() ); + aOptsItem.GetOptionsMisc().SetPickThrough( m_xCbxPickThrough->get_active() ); + aOptsItem.GetOptionsMisc().SetMasterPagePaintCaching( m_xCbxMasterPageCache->get_active() ); + aOptsItem.GetOptionsMisc().SetDragWithCopy( m_xCbxCopy->get_active() ); + aOptsItem.GetOptionsMisc().SetEnableSdremote( m_xCbxEnableSdremote->get_active() ); + aOptsItem.GetOptionsMisc().SetEnablePresenterScreen( m_xCbxEnablePresenterScreen->get_active() ); + aOptsItem.GetOptionsMisc().SetSummationOfParagraphs( m_xCbxCompatibility->get_active() ); + aOptsItem.GetOptionsMisc().SetPrinterIndependentLayout ( + m_xCbxUsePrinterMetrics->get_active() + ? css::document::PrinterIndependentLayout::DISABLED + : css::document::PrinterIndependentLayout::ENABLED); + aOptsItem.GetOptionsMisc().SetCrookNoContortion( m_xCbxDistort->get_active() ); + rAttrs->Put( aOptsItem ); + + bModified = true; + } + + // metric + if (m_xLbMetric->get_value_changed_from_saved()) + { + const sal_Int32 nMPos = m_xLbMetric->get_active(); + sal_uInt16 nFieldUnit = m_xLbMetric->get_id(nMPos).toUInt32(); + rAttrs->Put( SfxUInt16Item( GetWhich( SID_ATTR_METRIC ), nFieldUnit ) ); + bModified = true; + } + + // tabulator space + if( m_xMtrFldTabstop->get_value_changed_from_saved() ) + { + MapUnit eUnit = rAttrs->GetPool()->GetMetric( SID_ATTR_DEFTABSTOP ); + SfxUInt16Item aDef( SID_ATTR_DEFTABSTOP, static_cast(GetCoreValue( *m_xMtrFldTabstop, eUnit )) ); + rAttrs->Put( aDef ); + bModified = true; + } + + sal_Int32 nX, nY; + if( SetScale( m_xCbScale->get_active_text(), nX, nY ) ) + { + rAttrs->Put( SfxInt32Item( ATTR_OPTIONS_SCALE_X, nX ) ); + rAttrs->Put( SfxInt32Item( ATTR_OPTIONS_SCALE_Y, nY ) ); + + bModified = true; + } + + return bModified; +} + +void SdTpOptionsMisc::Reset( const SfxItemSet* rAttrs ) +{ + SdOptionsMiscItem aOptsItem( rAttrs->Get( ATTR_OPTIONS_MISC ) ); + + m_xCbxStartWithTemplate->set_active( aOptsItem.GetOptionsMisc().IsStartWithTemplate() ); + m_xCbxMarkedHitMovesAlways->set_active( aOptsItem.GetOptionsMisc().IsMarkedHitMovesAlways() ); + m_xCbxQuickEdit->set_active( aOptsItem.GetOptionsMisc().IsQuickEdit() ); + m_xCbxPickThrough->set_active( aOptsItem.GetOptionsMisc().IsPickThrough() ); + m_xCbxMasterPageCache->set_active( aOptsItem.GetOptionsMisc().IsMasterPagePaintCaching() ); + m_xCbxCopy->set_active( aOptsItem.GetOptionsMisc().IsDragWithCopy() ); + m_xCbxEnableSdremote->set_active( aOptsItem.GetOptionsMisc().IsEnableSdremote() ); + m_xCbxEnablePresenterScreen->set_active( aOptsItem.GetOptionsMisc().IsEnablePresenterScreen() ); + m_xCbxCompatibility->set_active( aOptsItem.GetOptionsMisc().IsSummationOfParagraphs() ); + m_xCbxUsePrinterMetrics->set_active( aOptsItem.GetOptionsMisc().GetPrinterIndependentLayout()==1 ); + m_xCbxDistort->set_active( aOptsItem.GetOptionsMisc().IsCrookNoContortion() ); + m_xCbxStartWithTemplate->save_state(); + m_xCbxMarkedHitMovesAlways->save_state(); + m_xCbxQuickEdit->save_state(); + m_xCbxPickThrough->save_state(); + + m_xCbxMasterPageCache->save_state(); + m_xCbxCopy->save_state(); + m_xCbxEnableSdremote->save_state(); + m_xCbxEnablePresenterScreen->save_state(); + m_xCbxCompatibility->save_state(); + m_xCbxUsePrinterMetrics->save_state(); + m_xCbxDistort->save_state(); + + // metric + sal_uInt16 nWhich = GetWhich( SID_ATTR_METRIC ); + m_xLbMetric->set_active(-1); + + if ( rAttrs->GetItemState( nWhich ) >= SfxItemState::DEFAULT ) + { + const SfxUInt16Item& rItem = static_cast(rAttrs->Get( nWhich )); + sal_uInt32 nFieldUnit = static_cast(rItem.GetValue()); + + for (sal_Int32 i = 0, nEntryCount = m_xLbMetric->get_count(); i < nEntryCount; ++i) + { + if (m_xLbMetric->get_id(i).toUInt32() == nFieldUnit) + { + m_xLbMetric->set_active( i ); + break; + } + } + } + + // tabulator space + constexpr auto nWhich2 = SID_ATTR_DEFTABSTOP; + if( rAttrs->GetItemState( nWhich2 ) >= SfxItemState::DEFAULT ) + { + MapUnit eUnit = rAttrs->GetPool()->GetMetric( nWhich2 ); + const SfxUInt16Item& rItem = rAttrs->Get( nWhich2 ); + SetMetricValue( *m_xMtrFldTabstop, rItem.GetValue(), eUnit ); + } + m_xLbMetric->save_value(); + m_xMtrFldTabstop->save_value(); + //Scale + sal_Int32 nX = rAttrs->Get( ATTR_OPTIONS_SCALE_X ).GetValue(); + sal_Int32 nY = rAttrs->Get( ATTR_OPTIONS_SCALE_Y ).GetValue(); + nWidth = static_cast( rAttrs-> + Get( ATTR_OPTIONS_SCALE_WIDTH ) ).GetValue(); + nHeight = static_cast( rAttrs-> + Get( ATTR_OPTIONS_SCALE_HEIGHT ) ).GetValue(); + + m_xCbScale->set_entry_text( GetScale( nX, nY ) ); + + m_xMtrFldOriginalWidth->hide(); + m_xMtrFldOriginalWidth->set_text( aInfo1 ); // empty + m_xMtrFldOriginalHeight->hide(); + m_xMtrFldOriginalHeight->set_text( aInfo2 ); //empty + m_xFiInfo1->hide(); + m_xFiInfo2->hide(); + + UpdateCompatibilityControls (); +} + +std::unique_ptr SdTpOptionsMisc::Create( weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet* rAttrs ) +{ + return std::make_unique( pPage, pController, *rAttrs ); +} + +IMPL_LINK_NOARG(SdTpOptionsMisc, SelectMetricHdl_Impl, weld::ComboBox&, void) +{ + int nPos = m_xLbMetric->get_active(); + if (nPos != -1) + { + FieldUnit eUnit = static_cast(m_xLbMetric->get_id(nPos).toUInt32()); + sal_Int64 nVal = + m_xMtrFldTabstop->denormalize(m_xMtrFldTabstop->get_value(FieldUnit::TWIP)); + SetFieldUnit( *m_xMtrFldTabstop, eUnit ); + m_xMtrFldTabstop->set_value( m_xMtrFldTabstop->normalize( nVal ), FieldUnit::TWIP ); + } +} + +void SdTpOptionsMisc::SetImpressMode() +{ +#ifndef ENABLE_SDREMOTE + m_xCbxEnableSdremote->hide(); +#else + (void) this; // loplugin:staticmethods +#endif +} + +void SdTpOptionsMisc::SetDrawMode() +{ + m_xScaleFrame->show(); + m_xNewDocumentFrame->hide(); + m_xCbxEnableSdremote->hide(); + m_xCbxEnablePresenterScreen->hide(); + m_xCbxCompatibility->hide(); + m_xNewDocLb->hide(); + m_xCbScale->show(); + m_xPresentationFrame->hide(); + m_xMtrFldInfo1->hide(); + m_xMtrFldInfo2->hide(); + m_xWidthLb->hide(); + m_xHeightLb->hide(); + m_xFiInfo1->show(); + m_xMtrFldOriginalWidth->show(); + m_xFiInfo2->show(); + m_xMtrFldOriginalHeight->show(); + m_xCbxDistort->show(); + m_xCbxCompatibility->hide(); +} + +OUString SdTpOptionsMisc::GetScale( sal_Int32 nX, sal_Int32 nY ) +{ + return OUString::number(nX) + OUStringChar(TOKEN) + OUString::number(nY); +} + +bool SdTpOptionsMisc::SetScale( std::u16string_view aScale, sal_Int32& rX, sal_Int32& rY ) +{ + if (aScale.empty()) + return false; + + sal_Int32 nIdx {0}; + + std::u16string_view aTmp(o3tl::getToken(aScale, 0, TOKEN, nIdx)); + if (nIdx<0) + return false; // we expect another token! + + if (!comphelper::string::isdigitAsciiString(aTmp)) + return false; + + rX = static_cast(o3tl::toInt32(aTmp)); + if( rX == 0 ) + return false; + + aTmp = o3tl::getToken(aScale, 0, TOKEN, nIdx); + if (nIdx>=0) + return false; // we require just 2 tokens! + + if (!comphelper::string::isdigitAsciiString(aTmp)) + return false; + + rY = static_cast(o3tl::toInt32(aTmp)); + return rY != 0; +} + +void SdTpOptionsMisc::UpdateCompatibilityControls() +{ + // Disable the compatibility controls by default. Enable them only when + // there is at least one open document. + bool bIsEnabled = false; + + try + { + // Get a component enumeration from the desktop and search it for documents. + Reference xContext( ::comphelper::getProcessComponentContext()); + do + { + Reference xDesktop = frame::Desktop::create(xContext); + + Reference xComponents = + xDesktop->getComponents(); + if ( ! xComponents.is()) + break; + + Reference xEnumeration ( + xComponents->createEnumeration()); + if ( ! xEnumeration.is()) + break; + + while (xEnumeration->hasMoreElements()) + { + Reference xModel (xEnumeration->nextElement(), UNO_QUERY); + if (xModel.is()) + { + // There is at least one model/document: Enable the compatibility controls. + bIsEnabled = true; + break; + } + } + + } + while (false); // One 'loop'. + } + catch (const uno::Exception&) + { + // When there is an exception then simply use the default value of + // bIsEnabled and disable the controls. + } + + m_xCbxCompatibility->set_sensitive(bIsEnabled); + m_xCbxUsePrinterMetrics->set_sensitive(bIsEnabled); +} + +void SdTpOptionsMisc::PageCreated(const SfxAllItemSet& aSet) +{ + const SfxUInt32Item* pFlagItem = aSet.GetItem(SID_SDMODE_FLAG, false); + if (pFlagItem) + { + sal_uInt32 nFlags=pFlagItem->GetValue(); + if ( ( nFlags & SD_DRAW_MODE ) == SD_DRAW_MODE ) + SetDrawMode(); + if ( ( nFlags & SD_IMPRESS_MODE ) == SD_IMPRESS_MODE ) + SetImpressMode(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/unchss.cxx b/sd/source/ui/dlg/unchss.cxx new file mode 100644 index 000000000..7d963cddf --- /dev/null +++ b/sd/source/ui/dlg/unchss.cxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +StyleSheetUndoAction::StyleSheetUndoAction(SdDrawDocument* pTheDoc, + SfxStyleSheet* pTheStyleSheet, + const SfxItemSet* pTheNewItemSet) : + SdUndoAction(pTheDoc) +{ + DBG_ASSERT(pTheStyleSheet, "Undo without StyleSheet ???"); + mpStyleSheet = pTheStyleSheet; + + // Create ItemSets; Attention, it is possible that the new one is from a, + // different pool. Therefore we clone it with its items. + mpNewSet = std::make_unique(static_cast(SdrObject::GetGlobalDrawObjectItemPool()), pTheNewItemSet->GetRanges()); + SdrModel::MigrateItemSet( pTheNewItemSet, mpNewSet.get(), pTheDoc ); + + mpOldSet = std::make_unique(static_cast(SdrObject::GetGlobalDrawObjectItemPool()), mpStyleSheet->GetItemSet().GetRanges()); + SdrModel::MigrateItemSet( &mpStyleSheet->GetItemSet(), mpOldSet.get(), pTheDoc ); + + OUString aComment(SdResId(STR_UNDO_CHANGE_PRES_OBJECT)); + OUString aName(mpStyleSheet->GetName()); + + // delete layout name and separator + sal_Int32 nPos = aName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aName = aName.copy(nPos + SD_LT_SEPARATOR.getLength()); + + if (aName == STR_LAYOUT_TITLE) + { + aName = SdResId(STR_PSEUDOSHEET_TITLE); + } + else if (aName == STR_LAYOUT_SUBTITLE) + { + aName = SdResId(STR_PSEUDOSHEET_SUBTITLE); + } + else if (aName == STR_LAYOUT_BACKGROUND) + { + aName = SdResId(STR_PSEUDOSHEET_BACKGROUND); + } + else if (aName == STR_LAYOUT_BACKGROUNDOBJECTS) + { + aName = SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS); + } + else if (aName == STR_LAYOUT_NOTES) + { + aName = SdResId(STR_PSEUDOSHEET_NOTES); + } + else + { + OUString aOutlineStr(SdResId(STR_PSEUDOSHEET_OUTLINE)); + nPos = aName.indexOf(aOutlineStr); + if (nPos != -1) + { + std::u16string_view aNumStr(aName.subView(aOutlineStr.getLength())); + aName = STR_LAYOUT_OUTLINE + aNumStr; + } + } + + // replace placeholder with template name + SetComment(aComment.replaceFirst("$", aName)); +} + +void StyleSheetUndoAction::Undo() +{ + SfxItemSet aNewSet( mpDoc->GetItemPool(), mpOldSet->GetRanges() ); + SdrModel::MigrateItemSet( mpOldSet.get(), &aNewSet, mpDoc ); + + mpStyleSheet->GetItemSet().Set(aNewSet); + if( mpStyleSheet->GetFamily() == SfxStyleFamily::Pseudo ) + static_cast(mpStyleSheet)->GetRealStyleSheet()->Broadcast(SfxHint(SfxHintId::DataChanged)); + else + mpStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +void StyleSheetUndoAction::Redo() +{ + SfxItemSet aNewSet( mpDoc->GetItemPool(), mpOldSet->GetRanges() ); + SdrModel::MigrateItemSet( mpNewSet.get(), &aNewSet, mpDoc ); + + mpStyleSheet->GetItemSet().Set(aNewSet); + if( mpStyleSheet->GetFamily() == SfxStyleFamily::Pseudo ) + static_cast(mpStyleSheet)->GetRealStyleSheet()->Broadcast(SfxHint(SfxHintId::DataChanged)); + else + mpStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/dlg/vectdlg.cxx b/sd/source/ui/dlg/vectdlg.cxx new file mode 100644 index 000000000..2db041e7e --- /dev/null +++ b/sd/source/ui/dlg/vectdlg.cxx @@ -0,0 +1,336 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define VECTORIZE_MAX_EXTENT 512 + +SdVectorizeDlg::SdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell) + : GenericDialogController(pParent, "modules/sdraw/ui/vectorize.ui", "VectorizeDialog") + , m_pDocSh(pDocShell) + , aBmp(rBmp) + , m_aBmpWin(m_xDialog.get()) + , m_aMtfWin(m_xDialog.get()) + , m_xNmLayers(m_xBuilder->weld_spin_button("colors")) + , m_xMtReduce(m_xBuilder->weld_metric_spin_button("points", FieldUnit::PIXEL)) + , m_xFtFillHoles(m_xBuilder->weld_label("tilesft")) + , m_xMtFillHoles(m_xBuilder->weld_metric_spin_button("tiles", FieldUnit::PIXEL)) + , m_xCbFillHoles(m_xBuilder->weld_check_button("fillholes")) + , m_xBmpWin(new weld::CustomWeld(*m_xBuilder, "source", m_aBmpWin)) + , m_xMtfWin(new weld::CustomWeld(*m_xBuilder, "vectorized", m_aMtfWin)) + , m_xPrgs(m_xBuilder->weld_progress_bar("progressbar")) + , m_xBtnOK(m_xBuilder->weld_button("ok")) + , m_xBtnPreview(m_xBuilder->weld_button("preview")) +{ + const int nWidth = m_xFtFillHoles->get_approximate_digit_width() * 32; + const int nHeight = m_xFtFillHoles->get_text_height() * 16; + m_xBmpWin->set_size_request(nWidth, nHeight); + m_xMtfWin->set_size_request(nWidth, nHeight); + + m_xBtnPreview->connect_clicked( LINK( this, SdVectorizeDlg, ClickPreviewHdl ) ); + m_xBtnOK->connect_clicked( LINK( this, SdVectorizeDlg, ClickOKHdl ) ); + m_xNmLayers->connect_value_changed( LINK( this, SdVectorizeDlg, ModifyHdl ) ); + m_xMtReduce->connect_value_changed( LINK( this, SdVectorizeDlg, MetricModifyHdl ) ); + m_xMtFillHoles->connect_value_changed( LINK( this, SdVectorizeDlg, MetricModifyHdl ) ); + m_xCbFillHoles->connect_toggled( LINK( this, SdVectorizeDlg, ToggleHdl ) ); + + LoadSettings(); + InitPreviewBmp(); +} + +SdVectorizeDlg::~SdVectorizeDlg() +{ +} + +::tools::Rectangle SdVectorizeDlg::GetRect( const Size& rDispSize, const Size& rBmpSize ) +{ + ::tools::Rectangle aRect; + + if( rBmpSize.Width() && rBmpSize.Height() && rDispSize.Width() && rDispSize.Height() ) + { + Size aBmpSize( rBmpSize ); + const double fGrfWH = static_cast(aBmpSize.Width()) / aBmpSize.Height(); + const double fWinWH = static_cast(rDispSize.Width()) / rDispSize.Height(); + + if( fGrfWH < fWinWH ) + { + aBmpSize.setWidth( static_cast( rDispSize.Height() * fGrfWH ) ); + aBmpSize.setHeight( rDispSize.Height() ); + } + else + { + aBmpSize.setWidth( rDispSize.Width() ); + aBmpSize.setHeight( static_cast( rDispSize.Width() / fGrfWH) ); + } + + const Point aBmpPos( ( rDispSize.Width() - aBmpSize.Width() ) >> 1, + ( rDispSize.Height() - aBmpSize.Height() ) >> 1 ); + + aRect = ::tools::Rectangle( aBmpPos, aBmpSize ); + } + + return aRect; +} + +void SdVectorizeDlg::InitPreviewBmp() +{ + const ::tools::Rectangle aRect( GetRect( m_aBmpWin.GetOutputSizePixel(), aBmp.GetSizePixel() ) ); + + aPreviewBmp = aBmp; + aPreviewBmp.Scale( aRect.GetSize() ); + m_aBmpWin.SetGraphic(BitmapEx(aPreviewBmp)); +} + +Bitmap SdVectorizeDlg::GetPreparedBitmap( Bitmap const & rBmp, Fraction& rScale ) +{ + Bitmap aNew( rBmp ); + const Size aSizePix( aNew.GetSizePixel() ); + + if( aSizePix.Width() > VECTORIZE_MAX_EXTENT || aSizePix.Height() > VECTORIZE_MAX_EXTENT ) + { + const ::tools::Rectangle aRect( GetRect( Size( VECTORIZE_MAX_EXTENT, VECTORIZE_MAX_EXTENT ), aSizePix ) ); + rScale = Fraction( aSizePix.Width(), aRect.GetWidth() ); + aNew.Scale( aRect.GetSize() ); + } + else + rScale = Fraction( 1, 1 ); + + BitmapEx aNewBmpEx(aNew); + BitmapFilter::Filter(aNewBmpEx, BitmapSimpleColorQuantizationFilter(m_xNmLayers->get_value())); + aNew = aNewBmpEx.GetBitmap(); + + return aNew; +} + +void SdVectorizeDlg::Calculate( Bitmap const & rBmp, GDIMetaFile& rMtf ) +{ + m_pDocSh->SetWaitCursor( true ); + m_xPrgs->set_percentage(0); + + Fraction aScale; + Bitmap aTmp( GetPreparedBitmap( rBmp, aScale ) ); + + if( !aTmp.IsEmpty() ) + { + const Link<::tools::Long,void> aPrgsHdl( LINK( this, SdVectorizeDlg, ProgressHdl ) ); + aTmp.Vectorize( rMtf, static_cast(m_xMtReduce->get_value(FieldUnit::NONE)), &aPrgsHdl ); + + if (m_xCbFillHoles->get_active()) + { + GDIMetaFile aNewMtf; + Bitmap::ScopedReadAccess pRAcc(aTmp); + + if( pRAcc ) + { + const tools::Long nWidth = pRAcc->Width(); + const tools::Long nHeight = pRAcc->Height(); + const tools::Long nTileX = m_xMtFillHoles->get_value(FieldUnit::NONE); + const tools::Long nTileY = m_xMtFillHoles->get_value(FieldUnit::NONE); + assert(nTileX && "div-by-zero"); + const tools::Long nCountX = nWidth / nTileX; + assert(nTileY && "div-by-zero"); + const tools::Long nCountY = nHeight / nTileY; + const tools::Long nRestX = nWidth % nTileX; + const tools::Long nRestY = nHeight % nTileY; + + MapMode aMap( rMtf.GetPrefMapMode() ); + aNewMtf.SetPrefSize( rMtf.GetPrefSize() ); + aNewMtf.SetPrefMapMode( aMap ); + + for( tools::Long nTY = 0; nTY < nCountY; nTY++ ) + { + const tools::Long nY = nTY * nTileY; + + for( tools::Long nTX = 0; nTX < nCountX; nTX++ ) + AddTile( pRAcc.get(), aNewMtf, nTX * nTileX, nTY * nTileY, nTileX, nTileY ); + + if( nRestX ) + AddTile( pRAcc.get(), aNewMtf, nCountX * nTileX, nY, nRestX, nTileY ); + } + + if( nRestY ) + { + const tools::Long nY = nCountY * nTileY; + + for( tools::Long nTX = 0; nTX < nCountX; nTX++ ) + AddTile( pRAcc.get(), aNewMtf, nTX * nTileX, nY, nTileX, nRestY ); + + if( nRestX ) + AddTile( pRAcc.get(), aNewMtf, nCountX * nTileX, nCountY * nTileY, nRestX, nRestY ); + } + + pRAcc.reset(); + + for( size_t n = 0, nCount = rMtf.GetActionSize(); n < nCount; n++ ) + aNewMtf.AddAction( rMtf.GetAction( n )->Clone() ); + + aMap.SetScaleX( aMap.GetScaleX() * aScale ); + aMap.SetScaleY( aMap.GetScaleY() * aScale ); + aNewMtf.SetPrefMapMode( aMap ); + rMtf = aNewMtf; + } + } + } + + m_xPrgs->set_percentage(0); + m_pDocSh->SetWaitCursor( false ); +} + +void SdVectorizeDlg::AddTile( BitmapReadAccess const * pRAcc, GDIMetaFile& rMtf, + tools::Long nPosX, tools::Long nPosY, tools::Long nWidth, tools::Long nHeight ) +{ + sal_uLong nSumR = 0, nSumG = 0, nSumB = 0; + const tools::Long nRight = nPosX + nWidth - 1; + const tools::Long nBottom = nPosY + nHeight - 1; + const double fMult = 1.0 / ( nWidth * nHeight ); + + for( tools::Long nY = nPosY; nY <= nBottom; nY++ ) + { + for( tools::Long nX = nPosX; nX <= nRight; nX++ ) + { + const BitmapColor aPixel( pRAcc->GetColor( nY, nX ) ); + + nSumR += aPixel.GetRed(); + nSumG += aPixel.GetGreen(); + nSumB += aPixel.GetBlue(); + } + } + + const Color aColor( static_cast(FRound( nSumR * fMult )), + static_cast(FRound( nSumG * fMult )), + static_cast(FRound( nSumB * fMult )) ); + + ::tools::Rectangle aRect( Point( nPosX, nPosY ), Size( nWidth + 1, nHeight + 1 ) ); + const Size& rMaxSize = rMtf.GetPrefSize(); + + aRect = Application::GetDefaultDevice()->PixelToLogic(aRect, rMtf.GetPrefMapMode()); + + if( aRect.Right() > ( rMaxSize.Width() - 1 ) ) + aRect.SetRight( rMaxSize.Width() - 1 ); + + if( aRect.Bottom() > ( rMaxSize.Height() - 1 ) ) + aRect.SetBottom( rMaxSize.Height() - 1 ); + + rMtf.AddAction( new MetaLineColorAction( aColor, true ) ); + rMtf.AddAction( new MetaFillColorAction( aColor, true ) ); + rMtf.AddAction( new MetaRectAction( aRect ) ); +} + +IMPL_LINK( SdVectorizeDlg, ProgressHdl, tools::Long, nData, void ) +{ + m_xPrgs->set_percentage(nData); +} + +IMPL_LINK_NOARG(SdVectorizeDlg, ClickPreviewHdl, weld::Button&, void) +{ + Calculate( aBmp, aMtf ); + m_aMtfWin.SetGraphic( aMtf ); + m_xBtnPreview->set_sensitive(false); +} + +IMPL_LINK_NOARG(SdVectorizeDlg, ClickOKHdl, weld::Button&, void) +{ + if (m_xBtnPreview->get_sensitive()) + Calculate( aBmp, aMtf ); + + SaveSettings(); + m_xDialog->response(RET_OK); +} + +IMPL_LINK( SdVectorizeDlg, ToggleHdl, weld::Toggleable&, rCb, void ) +{ + if (rCb.get_active()) + { + m_xFtFillHoles->set_sensitive(true); + m_xMtFillHoles->set_sensitive(true); + } + else + { + m_xFtFillHoles->set_sensitive(false); + m_xMtFillHoles->set_sensitive(false); + } + + m_xBtnPreview->set_sensitive(true); +} + +IMPL_LINK_NOARG(SdVectorizeDlg, ModifyHdl, weld::SpinButton&, void) +{ + m_xBtnPreview->set_sensitive(true); +} + +IMPL_LINK_NOARG(SdVectorizeDlg, MetricModifyHdl, weld::MetricSpinButton&, void) +{ + m_xBtnPreview->set_sensitive(true); +} + +void SdVectorizeDlg::LoadSettings() +{ + tools::SvRef xIStm( SD_MOD()->GetOptionStream( + SD_OPTION_VECTORIZE , + SdOptionStreamMode::Load ) ); + sal_uInt16 nLayers; + sal_uInt16 nReduce; + sal_uInt16 nFillHoles; + bool bFillHoles; + + if( xIStm.is() ) + { + SdIOCompat aCompat( *xIStm, StreamMode::READ ); + xIStm->ReadUInt16( nLayers ).ReadUInt16( nReduce ).ReadUInt16( nFillHoles ).ReadCharAsBool( bFillHoles ); + } + else + { + nLayers = 8; + nReduce = 0; + nFillHoles = 32; + bFillHoles = false; + } + + m_xNmLayers->set_value(nLayers); + m_xMtReduce->set_value(nReduce, FieldUnit::NONE); + m_xMtFillHoles->set_value(nFillHoles, FieldUnit::NONE); + m_xCbFillHoles->set_active(bFillHoles); + + ToggleHdl(*m_xCbFillHoles); +} + +void SdVectorizeDlg::SaveSettings() const +{ + tools::SvRef xOStm( SD_MOD()->GetOptionStream( + SD_OPTION_VECTORIZE , + SdOptionStreamMode::Store ) ); + + if( xOStm.is() ) + { + SdIOCompat aCompat( *xOStm, StreamMode::WRITE, 1 ); + xOStm->WriteUInt16( m_xNmLayers->get_value() ).WriteUInt16(m_xMtReduce->get_value(FieldUnit::NONE)); + xOStm->WriteUInt16( m_xMtFillHoles->get_value(FieldUnit::NONE) ).WriteBool(m_xCbFillHoles->get_active()); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshel2.cxx b/sd/source/ui/docshell/docshel2.cxx new file mode 100644 index 000000000..160c64a66 --- /dev/null +++ b/sd/source/ui/docshell/docshel2.cxx @@ -0,0 +1,416 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace sd { + +/** + * Drawing of DocShell (with the helper class SdDrawViewShell) + */ +void DrawDocShell::Draw(OutputDevice* pOut, const JobSetup&, sal_uInt16 nAspect) +{ + if (nAspect == ASPECT_THUMBNAIL) + { + // THUMBNAIL: here we may can set the draft mode + } + + std::optional pView( std::in_place, this, pOut ); + + pView->SetHlplVisible(false); + pView->SetGridVisible(false); + pView->SetBordVisible(false); + pView->SetPageVisible(false); + pView->SetGlueVisible(false); + + SdPage* pSelectedPage = nullptr; + + const std::vector> &rViews = mpDoc->GetFrameViewList(); + if( !rViews.empty() ) + { + sd::FrameView* pFrameView = rViews[0].get(); + if( pFrameView->GetPageKind() == PageKind::Standard ) + { + sal_uInt16 nSelectedPage = pFrameView->GetSelectedPage(); + pSelectedPage = mpDoc->GetSdPage(nSelectedPage, PageKind::Standard); + } + } + + if( nullptr == pSelectedPage ) + { + SdPage* pPage = nullptr; + sal_uInt16 nPageCnt = mpDoc->GetSdPageCount(PageKind::Standard); + + for (sal_uInt16 i = 0; i < nPageCnt; i++) + { + pPage = mpDoc->GetSdPage(i, PageKind::Standard); + + if ( pPage->IsSelected() ) + pSelectedPage = pPage; + } + + if( nullptr == pSelectedPage ) + pSelectedPage = mpDoc->GetSdPage(0, PageKind::Standard); + } + + ::tools::Rectangle aVisArea = GetVisArea(nAspect); + pOut->IntersectClipRegion(aVisArea); + pView->ShowSdrPage(pSelectedPage); + + if (pOut->GetOutDevType() == OUTDEV_WINDOW) + return; + + MapMode aOldMapMode = pOut->GetMapMode(); + + if (pOut->GetOutDevType() == OUTDEV_PRINTER) + { + MapMode aMapMode = aOldMapMode; + Point aOrigin = aMapMode.GetOrigin(); + aOrigin.AdjustX(1 ); + aOrigin.AdjustY(1 ); + aMapMode.SetOrigin(aOrigin); + pOut->SetMapMode(aMapMode); + } + + vcl::Region aRegion(aVisArea); + pView->CompleteRedraw(pOut, aRegion); + + if (pOut->GetOutDevType() == OUTDEV_PRINTER) + { + pOut->SetMapMode(aOldMapMode); + } +} + +::tools::Rectangle DrawDocShell::GetVisArea(sal_uInt16 nAspect) const +{ + ::tools::Rectangle aVisArea; + + if( ( ASPECT_THUMBNAIL == nAspect ) || ( ASPECT_DOCPRINT == nAspect ) ) + { + // provide size of first page + aVisArea.SetSize(mpDoc->GetSdPage(0, PageKind::Standard)->GetSize()); + } + else + { + aVisArea = SfxObjectShell::GetVisArea(nAspect); + } + + if (aVisArea.IsEmpty() && mpViewShell) + { + vcl::Window* pWin = mpViewShell->GetActiveWindow(); + + if (pWin) + { + aVisArea = pWin->PixelToLogic(::tools::Rectangle(Point(0,0), pWin->GetOutputSizePixel())); + } + } + + return aVisArea; +} + +void DrawDocShell::Connect(ViewShell* pViewSh) +{ + mpViewShell = pViewSh; +} + +void DrawDocShell::Disconnect(ViewShell const * pViewSh) +{ + if (mpViewShell == pViewSh) + { + mpViewShell = nullptr; + } +} + +FrameView* DrawDocShell::GetFrameView() +{ + FrameView* pFrameView = nullptr; + + if (mpViewShell) + { + pFrameView = mpViewShell->GetFrameView(); + } + + return pFrameView; +} + +/** + * Creates a bitmap of an arbitrary page + */ +BitmapEx DrawDocShell::GetPagePreviewBitmap(SdPage* pPage) +{ + const sal_uInt16 nMaxEdgePixel = 90; + MapMode aMapMode( MapUnit::Map100thMM ); + const Size aSize( pPage->GetSize() ); + const Point aNullPt; + ScopedVclPtrInstance< VirtualDevice > pVDev( *Application::GetDefaultDevice() ); + + pVDev->SetMapMode( aMapMode ); + + const Size aPixSize( pVDev->LogicToPixel( aSize ) ); + const sal_uLong nMaxEdgePix = std::max( aPixSize.Width(), aPixSize.Height() ); + Fraction aFrac( nMaxEdgePixel, nMaxEdgePix ); + + aMapMode.SetScaleX( aFrac ); + aMapMode.SetScaleY( aFrac ); + pVDev->SetMapMode( aMapMode ); + pVDev->SetOutputSize( aSize ); + + // that we also get the dark lines at the right and bottom page margin + aFrac = Fraction( nMaxEdgePixel - 1, nMaxEdgePix ); + aMapMode.SetScaleX( aFrac ); + aMapMode.SetScaleY( aFrac ); + pVDev->SetMapMode( aMapMode ); + + std::optional pView( std::in_place, this, pVDev ); + FrameView* pFrameView = GetFrameView(); + pView->ShowSdrPage( pPage ); + + if ( GetFrameView() ) + { + // initialize the drawing-(screen) attributes + pView->SetGridCoarse( pFrameView->GetGridCoarse() ); + pView->SetGridFine( pFrameView->GetGridFine() ); + pView->SetSnapGridWidth(pFrameView->GetSnapGridWidthX(), pFrameView->GetSnapGridWidthY()); + pView->SetGridVisible( pFrameView->IsGridVisible() ); + pView->SetGridFront( pFrameView->IsGridFront() ); + pView->SetSnapAngle( pFrameView->GetSnapAngle() ); + pView->SetGridSnap( pFrameView->IsGridSnap() ); + pView->SetBordSnap( pFrameView->IsBordSnap() ); + pView->SetHlplSnap( pFrameView->IsHlplSnap() ); + pView->SetOFrmSnap( pFrameView->IsOFrmSnap() ); + pView->SetOPntSnap( pFrameView->IsOPntSnap() ); + pView->SetOConSnap( pFrameView->IsOConSnap() ); + pView->SetDragStripes( pFrameView->IsDragStripes() ); + pView->SetFrameDragSingles( pFrameView->IsFrameDragSingles() ); + pView->SetSnapMagneticPixel( pFrameView->GetSnapMagneticPixel() ); + pView->SetMarkedHitMovesAlways( pFrameView->IsMarkedHitMovesAlways() ); + pView->SetMoveOnlyDragging( pFrameView->IsMoveOnlyDragging() ); + pView->SetSlantButShear( pFrameView->IsSlantButShear() ); + pView->SetNoDragXorPolys( pFrameView->IsNoDragXorPolys() ); + pView->SetCrookNoContortion( pFrameView->IsCrookNoContortion() ); + pView->SetAngleSnapEnabled( pFrameView->IsAngleSnapEnabled() ); + pView->SetBigOrtho( pFrameView->IsBigOrtho() ); + pView->SetOrtho( pFrameView->IsOrtho() ); + + SdrPageView* pPageView = pView->GetSdrPageView(); + + if (pPageView) + { + if ( pPageView->GetVisibleLayers() != pFrameView->GetVisibleLayers() ) + pPageView->SetVisibleLayers( pFrameView->GetVisibleLayers() ); + + if ( pPageView->GetPrintableLayers() != pFrameView->GetPrintableLayers() ) + pPageView->SetPrintableLayers( pFrameView->GetPrintableLayers() ); + + if ( pPageView->GetLockedLayers() != pFrameView->GetLockedLayers() ) + pPageView->SetLockedLayers( pFrameView->GetLockedLayers() ); + + pPageView->SetHelpLines( pFrameView->GetStandardHelpLines() ); + } + + if ( pView->GetActiveLayer() != pFrameView->GetActiveLayer() ) + pView->SetActiveLayer( pFrameView->GetActiveLayer() ); + } + + pView->CompleteRedraw( pVDev, vcl::Region(::tools::Rectangle(aNullPt, aSize)) ); + + // IsRedrawReady() always gives sal_True while ( !pView->IsRedrawReady() ) {} + pView.reset(); + + pVDev->SetMapMode( MapMode() ); + + BitmapEx aPreview( pVDev->GetBitmapEx( aNullPt, pVDev->GetOutputSizePixel() ) ); + + DBG_ASSERT(!aPreview.IsEmpty(), "Preview-Bitmap could not be generated"); + + return aPreview; +} + +/** + * Checks if the page exists. If so, we force the user to enter a not yet used + * name. + * @return sal_False if the user cancels the action. + */ +bool DrawDocShell::CheckPageName(weld::Window* pWin, OUString& rName) +{ + const OUString aStrForDlg( rName ); + bool bIsNameValid = IsNewPageNameValid( rName, true ); + + if( ! bIsNameValid ) + { + OUString aDesc; + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + + if (GetDocumentType() == DocumentType::Draw) + aDesc = SdResId( STR_WARN_PAGE_EXISTS_DRAW ); + else + aDesc = SdResId( STR_WARN_PAGE_EXISTS ); + + ScopedVclPtr aNameDlg(pFact->CreateSvxNameDialog(pWin, aStrForDlg, aDesc)); + aNameDlg->SetEditHelpId( HID_SD_NAMEDIALOG_PAGE ); + + aNameDlg->SetCheckNameHdl( LINK( this, DrawDocShell, RenameSlideHdl ) ); + + rtl::Reference xFunc( mpViewShell->GetCurrentFunction() ); + if( xFunc.is() ) + xFunc->cancel(); + + if( aNameDlg->Execute() == RET_OK ) + { + aNameDlg->GetName( rName ); + bIsNameValid = IsNewPageNameValid( rName ); + } + } + + return bIsNameValid; +} + +bool DrawDocShell::IsNewPageNameValid( OUString & rInOutPageName, bool bResetStringIfStandardName /* = false */ ) +{ + bool bCanUseNewName = false; + + // check if name is something like 'Slide n' + OUString aStrPage(SdResId(STR_SD_PAGE) + " "); + + bool bIsStandardName = false; + + // prevent also _future_ slide names of the form "'STR_SD_PAGE' + ' ' + '[0-9]+|[a-z]|[A-Z]|[CDILMVX]+|[cdilmvx]+'" + // (arabic, lower- and upper case single letter, lower- and upper case roman numbers) + if (rInOutPageName.startsWith(aStrPage) && + rInOutPageName.getLength() > aStrPage.getLength()) + { + sal_Int32 nIdx{ aStrPage.getLength() }; + std::u16string_view sRemainder = o3tl::getToken(rInOutPageName, 0, ' ', nIdx); + if (!sRemainder.empty() && sRemainder[0] >= '0' && sRemainder[0] <= '9') + { + // check for arabic numbering + + size_t nIndex = 1; + // skip all following numbers + while (nIndex < sRemainder.size() && + sRemainder[nIndex] >= '0' && sRemainder[nIndex] <= '9') + { + nIndex++; + } + + // EOL? Reserved name! + if (nIndex >= sRemainder.size()) + { + bIsStandardName = true; + } + } + else if (sRemainder.size() == 1 && + rtl::isAsciiLowerCase(sRemainder[0])) + { + // lower case, single character: reserved + bIsStandardName = true; + } + else if (sRemainder.size() == 1 && + rtl::isAsciiUpperCase(sRemainder[0])) + { + // upper case, single character: reserved + bIsStandardName = true; + } + else + { + // check for upper/lower case roman numbering + OUString sReserved("cdilmvx"); + + // skip all following characters contained in one reserved class + if (sReserved.indexOf(sRemainder[0]) == -1) + sReserved = sReserved.toAsciiUpperCase(); + + size_t nIndex = 0; + while (nIndex < sRemainder.size() && + sReserved.indexOf(sRemainder[nIndex]) != -1) + { + nIndex++; + } + + // EOL? Reserved name! + if (nIndex >= sRemainder.size()) + { + bIsStandardName = true; + } + } + } + + if( bIsStandardName ) + { + if( bResetStringIfStandardName ) + { + // this is for insertion of slides from other files with standard + // name. They get a new standard name, if the string is set to an + // empty one. + rInOutPageName.clear(); + bCanUseNewName = true; + } + else + bCanUseNewName = false; + } + else + { + if (!rInOutPageName.isEmpty()) + { + bool bOutDummy; + sal_uInt16 nExistingPageNum = mpDoc->GetPageByName( rInOutPageName, bOutDummy ); + bCanUseNewName = ( nExistingPageNum == SDRPAGE_NOTFOUND ); + } + else + bCanUseNewName = false; + } + + return bCanUseNewName; +} + +bool DrawDocShell::IsPageNameUnique( std::u16string_view rPageName ) const +{ + return mpDoc->IsPageNameUnique(rPageName); +} + +IMPL_LINK( DrawDocShell, RenameSlideHdl, AbstractSvxNameDialog&, rDialog, bool ) +{ + OUString aNewName; + rDialog.GetName( aNewName ); + return IsNewPageNameValid( aNewName ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshel3.cxx b/sd/source/ui/docshell/docshel3.cxx new file mode 100644 index 000000000..be045818a --- /dev/null +++ b/sd/source/ui/docshell/docshel3.cxx @@ -0,0 +1,443 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; + +namespace sd { + +static void lcl_setLanguageForObj( SdrObject *pObj, LanguageType nLang, bool bLanguageNone ) +{ + const sal_uInt16 aLangWhichId_EE[3] = + { + EE_CHAR_LANGUAGE, + EE_CHAR_LANGUAGE_CJK, + EE_CHAR_LANGUAGE_CTL + }; + + if( bLanguageNone ) + nLang = LANGUAGE_NONE; + + if( nLang != LANGUAGE_DONTKNOW ) + { + if( nLang == LANGUAGE_NONE ) + { + for(sal_uInt16 n : aLangWhichId_EE) + pObj->SetMergedItem( SvxLanguageItem( nLang, n ) ); + } + else + { + sal_uInt16 nLangWhichId = 0; + SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nLang ); + switch (nScriptType) + { + case SvtScriptType::LATIN : nLangWhichId = EE_CHAR_LANGUAGE; break; + case SvtScriptType::ASIAN : nLangWhichId = EE_CHAR_LANGUAGE_CJK; break; + case SvtScriptType::COMPLEX : nLangWhichId = EE_CHAR_LANGUAGE_CTL; break; + default: + OSL_FAIL("unexpected case" ); + return; + } + pObj->SetMergedItem( SvxLanguageItem( nLang, nLangWhichId ) ); + + // Reset shape text language to default, so it inherits the shape language set above. + OutlinerParaObject* pOutliner = pObj->GetOutlinerParaObject(); + if (pOutliner) + { + EditTextObject& rEditTextObject + = const_cast(pOutliner->GetTextObject()); + for (sal_uInt16 n : aLangWhichId_EE) + { + rEditTextObject.RemoveCharAttribs(n); + } + } + } + } + else // Reset to default + { + for(sal_uInt16 n : aLangWhichId_EE) + pObj->ClearMergedItem( n ); + } +} + +static void lcl_setLanguage( const SdDrawDocument *pDoc, std::u16string_view rLanguage, bool bLanguageNone = false ) +{ + LanguageType nLang = SvtLanguageTable::GetLanguageType( rLanguage ); + + // Do it for SdDrawDocument->SetLanguage as well? + + sal_uInt16 nPageCount = pDoc->GetPageCount(); // Pick All Pages + for( sal_uInt16 nPage = 0; nPage < nPageCount; nPage++ ) + { + const SdrPage *pPage = pDoc->GetPage( nPage ); + const size_t nObjCount = pPage->GetObjCount(); + for( size_t nObj = 0; nObj < nObjCount; ++nObj ) + { + SdrObject *pObj = pPage->GetObj( nObj ); + if (pObj->GetObjIdentifier() != SdrObjKind::Page) + lcl_setLanguageForObj( pObj, nLang, bLanguageNone ); + } + } +} + +/** + * Handles SFX-Requests + */ +void DrawDocShell::Execute( SfxRequest& rReq ) +{ + if(mpViewShell && SlideShow::IsRunning( mpViewShell->GetViewShellBase() )) + { + // during a running presentation no slot will be executed + return; + } + + switch ( rReq.GetSlot() ) + { + case SID_SEARCH_ITEM: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if (pReqArgs) + { + const SvxSearchItem & rSearchItem = pReqArgs->Get(SID_SEARCH_ITEM); + + SD_MOD()->SetSearchItem(std::unique_ptr(rSearchItem.Clone())); + } + + rReq.Done(); + } + break; + + case FID_SEARCH_ON: + { + // no action needed + rReq.Done(); + } + break; + + case FID_SEARCH_OFF: + { + if (mpViewShell) + { + sd::View* pView = mpViewShell->GetView(); + if (pView) + { + auto& rFunctionContext = pView->getSearchContext(); + rtl::Reference& xFuSearch(rFunctionContext.getFunctionSearch()); + + if (xFuSearch.is()) + { + // End Search&Replace in all docshells + SfxObjectShell* pFirstShell = SfxObjectShell::GetFirst(); + SfxObjectShell* pShell = pFirstShell; + + while (pShell) + { + auto pDrawDocShell = dynamic_cast(pShell); + if (pDrawDocShell) + pDrawDocShell->CancelSearching(); + + pShell = SfxObjectShell::GetNext(*pShell); + + if (pShell == pFirstShell) + pShell = nullptr; + } + + rFunctionContext.resetSearchFunction(); + Invalidate(); + rReq.Done(); + } + } + } + } + break; + + case FID_SEARCH_NOW: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if (pReqArgs && mpViewShell) + { + sd::View* pView = mpViewShell->GetView(); + if (pView) + { + rtl::Reference & xFuSearch = pView->getSearchContext().getFunctionSearch(); + + if (!xFuSearch.is()) + { + xFuSearch = rtl::Reference( + FuSearch::createPtr(mpViewShell, + mpViewShell->GetActiveWindow(), + pView, mpDoc, rReq)); + + pView->getSearchContext().setSearchFunction(xFuSearch); + } + + if (xFuSearch.is()) + { + const SvxSearchItem& rSearchItem = pReqArgs->Get(SID_SEARCH_ITEM); + + SD_MOD()->SetSearchItem(std::unique_ptr(rSearchItem.Clone())); + xFuSearch->SearchAndReplace(&rSearchItem); + } + } + } + + rReq.Done(); + } + break; + + case SID_CLOSEDOC: + { + ExecuteSlot(rReq, SfxObjectShell::GetStaticInterface()); + } + break; + + case SID_GET_COLORLIST: + { + const SvxColorListItem* pColItem = GetItem( SID_COLOR_TABLE ); + const XColorListRef& pList = pColItem->GetColorList(); + rReq.SetReturnValue( OfaXColorListItem( SID_GET_COLORLIST, pList ) ); + } + break; + + case SID_VERSION: + { + ExecuteSlot( rReq, SfxObjectShell::GetStaticInterface() ); + } + break; + + case SID_HANGUL_HANJA_CONVERSION: + { + if( mpViewShell ) + { + rtl::Reference aFunc( FuHangulHanjaConversion::Create( mpViewShell, mpViewShell->GetActiveWindow(), mpViewShell->GetView(), mpDoc, rReq ) ); + static_cast< FuHangulHanjaConversion* >( aFunc.get() )->StartConversion( LANGUAGE_KOREAN, LANGUAGE_KOREAN, nullptr, i18n::TextConversionOption::CHARACTER_BY_CHARACTER, true ); + } + } + break; + + case SID_CHINESE_CONVERSION: + { + if( mpViewShell ) + { + rtl::Reference aFunc( FuHangulHanjaConversion::Create( mpViewShell, mpViewShell->GetActiveWindow(), mpViewShell->GetView(), mpDoc, rReq ) ); + static_cast< FuHangulHanjaConversion* >( aFunc.get() )->StartChineseConversion(); + } + } + break; + case SID_LANGUAGE_STATUS: + { + OUString aNewLangTxt; + const SfxStringItem* pItem = rReq.GetArg(SID_LANGUAGE_STATUS); + if (pItem) + aNewLangTxt = pItem->GetValue(); + + if (aNewLangTxt == "*" ) + { + // open the dialog "Tools/Options/Language Settings - Language" + if (mpViewShell) + { + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateVclDialog( mpViewShell->GetFrameWeld(), SID_LANGUAGE_OPTIONS )); + pDlg->Execute(); + } + } + else + { + if( mpViewShell ) + { + // setting the new language... + if (!aNewLangTxt.isEmpty()) + { + static const OUStringLiteral aSelectionLangPrefix(u"Current_"); + static const OUStringLiteral aParagraphLangPrefix(u"Paragraph_"); + static const OUStringLiteral aDocumentLangPrefix(u"Default_"); + + bool bSelection = false; + bool bParagraph = false; + + SdDrawDocument* pDoc = mpViewShell->GetDoc(); + sal_Int32 nPos = -1; + if (-1 != (nPos = aNewLangTxt.indexOf( aDocumentLangPrefix ))) + { + aNewLangTxt = aNewLangTxt.replaceAt( nPos, aDocumentLangPrefix.getLength(), u"" ); + + if (aNewLangTxt == "LANGUAGE_NONE") + lcl_setLanguage( pDoc, u"", true ); + else if (aNewLangTxt == "RESET_LANGUAGES") + lcl_setLanguage( pDoc, u"" ); + else + lcl_setLanguage( pDoc, aNewLangTxt ); + } + else if (-1 != (nPos = aNewLangTxt.indexOf( aSelectionLangPrefix ))) + { + bSelection = true; + aNewLangTxt = aNewLangTxt.replaceAt( nPos, aSelectionLangPrefix.getLength(), u"" ); + } + else if (-1 != (nPos = aNewLangTxt.indexOf( aParagraphLangPrefix ))) + { + bParagraph = true; + aNewLangTxt = aNewLangTxt.replaceAt( nPos, aParagraphLangPrefix.getLength(), u"" ); + } + + if (bSelection || bParagraph) + { + SdrView* pSdrView = mpViewShell->GetDrawView(); + if (!pSdrView) + return; + + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + const LanguageType nLangToUse = SvtLanguageTable::GetLanguageType( aNewLangTxt ); + SvtScriptType nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nLangToUse ); + + SfxItemSet aAttrs = rEditView.GetEditEngine()->GetEmptyItemSet(); + if (nScriptType == SvtScriptType::LATIN) + aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE ) ); + if (nScriptType == SvtScriptType::COMPLEX) + aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CTL ) ); + if (nScriptType == SvtScriptType::ASIAN) + aAttrs.Put( SvxLanguageItem( nLangToUse, EE_CHAR_LANGUAGE_CJK ) ); + ESelection aOldSel; + if (bParagraph) + { + ESelection aSel = rEditView.GetSelection(); + aOldSel = aSel; + aSel.nStartPos = 0; + aSel.nEndPos = EE_TEXTPOS_ALL; + rEditView.SetSelection( aSel ); + } + + rEditView.SetAttribs( aAttrs ); + if (bParagraph) + rEditView.SetSelection( aOldSel ); + } + + if ( pDoc->GetOnlineSpell() ) + { + pDoc->StartOnlineSpelling(); + } + } + } + } + Broadcast(SfxHint(SfxHintId::LanguageChanged)); + } + break; + case SID_SPELLCHECK_IGNORE_ALL: + { + if (!mpViewShell) + return; + SdrView* pSdrView = mpViewShell->GetDrawView(); + if (!pSdrView) + return; + + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + OUString sIgnoreText; + const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_1); + if (pItem2) + sIgnoreText = pItem2->GetValue(); + + if(sIgnoreText == "Spelling") + { + ESelection aOldSel = rEditView.GetSelection(); + rEditView.SpellIgnoreWord(); + rEditView.SetSelection( aOldSel ); + } + } + break; + case SID_SPELLCHECK_APPLY_SUGGESTION: + { + if (!mpViewShell) + return; + SdrView* pSdrView = mpViewShell->GetDrawView(); + if (!pSdrView) + return; + + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + OUString sApplyText; + const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_1); + if (pItem2) + sApplyText = pItem2->GetValue(); + + static const OUStringLiteral sSpellingRule(u"Spelling_"); + sal_Int32 nPos = 0; + if(-1 != (nPos = sApplyText.indexOf( sSpellingRule ))) + { + sApplyText = sApplyText.replaceAt(nPos, sSpellingRule.getLength(), u""); + rEditView.InsertText( sApplyText ); + } + } + break; + + case SID_NOTEBOOKBAR: + { + const SfxStringItem* pFile = rReq.GetArg( SID_NOTEBOOKBAR ); + + if ( mpViewShell ) + { + SfxBindings& rBindings( mpViewShell->GetFrame()->GetBindings() ); + + if ( sfx2::SfxNotebookBar::IsActive() ) + sfx2::SfxNotebookBar::ExecMethod( rBindings, pFile ? pFile->GetValue() : "" ); + else + sfx2::SfxNotebookBar::CloseMethod( rBindings ); + } + } + break; + + default: + break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshel4.cxx b/sd/source/ui/docshell/docshel4.cxx new file mode 100644 index 000000000..6fe599e44 --- /dev/null +++ b/sd/source/ui/docshell/docshel4.cxx @@ -0,0 +1,1002 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using ::sd::framework::FrameworkHelper; + +// PowerPoint-Filter +constexpr OUStringLiteral pFilterPowerPoint97( u"MS PowerPoint 97" ); +constexpr OUStringLiteral pFilterPowerPoint97Template( u"MS PowerPoint 97 Vorlage" ); +constexpr OUStringLiteral pFilterPowerPoint97AutoPlay( u"MS PowerPoint 97 AutoPlay" ); + +namespace sd { + +/** + * Creates (if necessary) and returns a SfxPrinter + */ +SfxPrinter* DrawDocShell::GetPrinter(bool bCreate) +{ + if (bCreate && !mpPrinter) + { + // create ItemSet with special pool area + auto pSet = std::make_unique>( GetPool() ); + // set PrintOptionsSet + SdOptionsPrintItem aPrintItem( SD_MOD()->GetSdOptions(mpDoc->GetDocumentType()) ); + SfxFlagItem aFlagItem( SID_PRINTER_CHANGESTODOC ); + SfxPrinterChangeFlags nFlags = + (aPrintItem.GetOptionsPrint().IsWarningSize() ? SfxPrinterChangeFlags::CHG_SIZE : SfxPrinterChangeFlags::NONE) | + (aPrintItem.GetOptionsPrint().IsWarningOrientation() ? SfxPrinterChangeFlags::CHG_ORIENTATION : SfxPrinterChangeFlags::NONE); + aFlagItem.SetValue( static_cast(nFlags) ); + + pSet->Put( aPrintItem ); + pSet->Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, aPrintItem.GetOptionsPrint().IsWarningPrinter() ) ); + pSet->Put( aFlagItem ); + + mpPrinter = VclPtr::Create(std::move(pSet)); + mbOwnPrinter = true; + + // set output quality + sal_uInt16 nQuality = aPrintItem.GetOptionsPrint().GetOutputQuality(); + + DrawModeFlags nMode = DrawModeFlags::Default; + // 1 == Grayscale, 2 == Black & White (with grayscale images) + if( nQuality == 1 ) + nMode = DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText | DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient; + else if( nQuality == 2 ) + nMode = DrawModeFlags::BlackLine | DrawModeFlags::WhiteFill | DrawModeFlags::BlackText | DrawModeFlags::GrayBitmap | DrawModeFlags::WhiteGradient; + + mpPrinter->SetDrawMode( nMode ); + + MapMode aMM (mpPrinter->GetMapMode()); + aMM.SetMapUnit(MapUnit::Map100thMM); + mpPrinter->SetMapMode(aMM); + UpdateRefDevice(); + } + return mpPrinter; +} + +/** + * Set new SfxPrinter (transfer of ownership) + */ +void DrawDocShell::SetPrinter(SfxPrinter *pNewPrinter) +{ + if ( mpViewShell ) + { + ::sd::View* pView = mpViewShell->GetView(); + if ( pView->IsTextEdit() ) + pView->SdrEndTextEdit(); + } + + if ( mpPrinter && mbOwnPrinter && (mpPrinter.get() != pNewPrinter) ) + mpPrinter.disposeAndClear(); + + mpPrinter = pNewPrinter; + mbOwnPrinter = true; + if ( mpDoc->GetPrinterIndependentLayout() == css::document::PrinterIndependentLayout::DISABLED ) + UpdateFontList(); + UpdateRefDevice(); +} + +void DrawDocShell::UpdateFontList() +{ + mpFontList.reset(); + OutputDevice* pRefDevice = nullptr; + if ( mpDoc->GetPrinterIndependentLayout() == css::document::PrinterIndependentLayout::DISABLED ) + pRefDevice = GetPrinter(true); + else + pRefDevice = SD_MOD()->GetVirtualRefDevice(); + mpFontList.reset( new FontList(pRefDevice, nullptr) ); + SvxFontListItem aFontListItem( mpFontList.get(), SID_ATTR_CHAR_FONTLIST ); + PutItem( aFontListItem ); +} + +Printer* DrawDocShell::GetDocumentPrinter() +{ + return GetPrinter(false); +} + +void DrawDocShell::OnDocumentPrinterChanged(Printer* pNewPrinter) +{ + // if we already have a printer, see if it's the same + if( mpPrinter ) + { + // easy case + if( mpPrinter == pNewPrinter ) + return; + + // compare if it's the same printer with the same job setup + if( (mpPrinter->GetName() == pNewPrinter->GetName()) && + (mpPrinter->GetJobSetup() == pNewPrinter->GetJobSetup())) + return; + } + + SfxPrinter* const pSfxPrinter = dynamic_cast(pNewPrinter); + if (pSfxPrinter) + { + SetPrinter(pSfxPrinter); + + // container owns printer + mbOwnPrinter = false; + } +} + +void DrawDocShell::UpdateRefDevice() +{ + if( !mpDoc ) + return; + + // Determine the device for which the output will be formatted. + VclPtr< OutputDevice > pRefDevice; + switch (mpDoc->GetPrinterIndependentLayout()) + { + case css::document::PrinterIndependentLayout::DISABLED: + pRefDevice = mpPrinter.get(); + break; + + case css::document::PrinterIndependentLayout::ENABLED: + pRefDevice = SD_MOD()->GetVirtualRefDevice(); + break; + + default: + // We are confronted with an invalid or un-implemented + // layout mode. Use the printer as formatting device + // as a fall-back. + SAL_WARN( "sd", "DrawDocShell::UpdateRefDevice(): Unexpected printer layout mode"); + + pRefDevice = mpPrinter.get(); + break; + } + mpDoc->SetRefDevice( pRefDevice.get() ); + + SdOutliner* pOutl = mpDoc->GetOutliner( false ); + + if( pOutl ) + pOutl->SetRefDevice( pRefDevice ); + + SdOutliner* pInternalOutl = mpDoc->GetInternalOutliner( false ); + + if( pInternalOutl ) + pInternalOutl->SetRefDevice( pRefDevice ); +} + +/** + * Creates new document, opens streams + */ +bool DrawDocShell::InitNew( const css::uno::Reference< css::embed::XStorage >& xStorage ) +{ + bool bRet = SfxObjectShell::InitNew( xStorage ); + + ::tools::Rectangle aVisArea( Point(0, 0), Size(14100, 10000) ); + SetVisArea(aVisArea); + + if (bRet) + { + if( !mbSdDataObj ) + mpDoc->NewOrLoadCompleted(DocCreationMode::New); // otherwise calling + // NewOrLoadCompleted(NEW_LOADED) in + // SdDrawDocument::AllocModel() + } + return bRet; +} + +/** + * loads pools and document + */ +bool DrawDocShell::Load( SfxMedium& rMedium ) +{ + // If this is an ODF file being loaded, then by default, use legacy processing + // for tdf#99729 (if required, it will be overridden in *::ReadUserDataSequence()) + if (IsOwnStorageFormat(rMedium)) + { + mpDoc->SetAnchoredTextOverflowLegacy(true); + } + + bool bRet = false; + bool bStartPresentation = false; + ErrCode nError = ERRCODE_NONE; + + SfxItemSet* pSet = rMedium.GetItemSet(); + + if( pSet ) + { + if( ( SfxItemState::SET == pSet->GetItemState(SID_PREVIEW ) ) && pSet->Get( SID_PREVIEW ).GetValue() ) + { + mpDoc->SetStarDrawPreviewMode( true ); + } + + if( SfxItemState::SET == pSet->GetItemState(SID_DOC_STARTPRESENTATION)&& + pSet->Get( SID_DOC_STARTPRESENTATION ).GetValue() ) + { + bStartPresentation = true; + mpDoc->SetStartWithPresentation( true ); + } + } + + bRet = SfxObjectShell::Load( rMedium ); + if (bRet) + { + comphelper::EmbeddedObjectContainer& rEmbeddedObjectContainer = getEmbeddedObjectContainer(); + rEmbeddedObjectContainer.setUserAllowsLinkUpdate(false); + bRet = SdXMLFilter( rMedium, *this, SdXMLFilterMode::Normal, SotStorage::GetVersion( rMedium.GetStorage() ) ).Import( nError ); + } + + if( bRet ) + { + // for legacy markup in OOoXML filter, convert the animations now + EffectMigration::DocumentLoaded(*GetDoc()); + UpdateTablePointers(); + + // If we're an embedded OLE object, use tight bounds + // for our visArea. No point in showing the user lots of empty + // space. Had to remove the check for empty VisArea below, + // since XML load always sets a VisArea before. + //TODO/LATER: looks a little bit strange! + if( ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) && SfxObjectShell::GetVisArea( ASPECT_CONTENT ).IsEmpty() ) + { + SdPage* pPage = mpDoc->GetSdPage( 0, PageKind::Standard ); + + if( pPage ) + SetVisArea( pPage->GetAllObjBoundRect() ); + } + + FinishedLoading(); + + const INetURLObject aUrl; + SfxObjectShell::SetAutoLoad( aUrl, 0, false ); + } + else + { + if( nError == ERRCODE_IO_BROKENPACKAGE ) + SetError(ERRCODE_IO_BROKENPACKAGE); + + // TODO/LATER: correct error handling?! + //pStore->SetError(SVSTREAM_WRONGVERSION); + else + SetError(ERRCODE_ABORT); + } + + // tell SFX to change viewshell when in preview mode + if( IsPreview() || bStartPresentation ) + { + SfxItemSet *pMediumSet = GetMedium()->GetItemSet(); + if( pMediumSet ) + pMediumSet->Put( SfxUInt16Item( SID_VIEW_ID, bStartPresentation ? 1 : 5 ) ); + } + + return bRet; +} + +/** + * loads content for organizer + */ +bool DrawDocShell::LoadFrom( SfxMedium& rMedium ) +{ + std::unique_ptr pWait; + if( mpViewShell ) + pWait.reset(new weld::WaitObject(mpViewShell->GetFrameWeld())); + + mpDoc->NewOrLoadCompleted( DocCreationMode::New ); + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + + // TODO/LATER: nobody is interested in the error code?! + ErrCode nError = ERRCODE_NONE; + bool bRet = SdXMLFilter( rMedium, *this, SdXMLFilterMode::Organizer, SotStorage::GetVersion( rMedium.GetStorage() ) ).Import( nError ); + + // tell SFX to change viewshell when in preview mode + if( IsPreview() ) + { + SfxItemSet *pSet = GetMedium()->GetItemSet(); + + if( pSet ) + pSet->Put( SfxUInt16Item( SID_VIEW_ID, 5 ) ); + } + + return bRet; +} + +/** + * load from 3rd party format + */ +bool DrawDocShell::ImportFrom(SfxMedium &rMedium, + uno::Reference const& xInsertPosition) +{ + const OUString aFilterName( rMedium.GetFilter()->GetFilterName() ); + if (aFilterName == "Impress MS PowerPoint 2007 XML" || + aFilterName == "Impress MS PowerPoint 2007 XML AutoPlay" || + aFilterName == "Impress MS PowerPoint 2007 XML VBA") + { + // As this is a MSFT format, we should use the "MS Compat" + // mode for spacing before and after paragraphs. + + // This is copied from what is done for .ppt import in + // ImplSdPPTImport::Import() in sd/source/filter/ppt/pptin.cxx + // in. We need to tell both the edit engine of the draw outliner, + // and the document, to do "summation of paragraphs". + SdrOutliner& rOutl = mpDoc->GetDrawOutliner(); + EEControlBits nControlWord = rOutl.GetEditEngine().GetControlWord(); + nControlWord |= EEControlBits::ULSPACESUMMATION; + const_cast(rOutl.GetEditEngine()).SetControlWord( nControlWord ); + + mpDoc->SetSummationOfParagraphs(); + } + + if (aFilterName == "Impress MS PowerPoint 2007 XML" || + aFilterName == "Impress MS PowerPoint 2007 XML AutoPlay" || + aFilterName == "Impress MS PowerPoint 2007 XML VBA" || + aFilterName == "Impress Office Open XML") + { + // We need to be able to set the default tab size for each text object. + // This is possible at the moment only for the whole document. See + // TextParagraphPropertiesContext constructor. So default tab width + // of the LibreOffice is 1270 but MSO is 2540 on general settings. + mpDoc->SetDefaultTabulator( 2540 ); + } + + const bool bRet = SfxObjectShell::ImportFrom(rMedium, xInsertPosition); + + SfxItemSet* pSet = rMedium.GetItemSet(); + if( pSet ) + { + if( SfxItemState::SET == pSet->GetItemState(SID_DOC_STARTPRESENTATION)&& + pSet->Get( SID_DOC_STARTPRESENTATION ).GetValue() ) + { + mpDoc->SetStartWithPresentation( true ); + + // tell SFX to change viewshell when in preview mode + if( IsPreview() ) + { + SfxItemSet *pMediumSet = GetMedium()->GetItemSet(); + if( pMediumSet ) + pMediumSet->Put( SfxUInt16Item( SID_VIEW_ID, 1 ) ); + } + } + } + + return bRet; +} + +/** + * load from a foreign format + */ +bool DrawDocShell::ConvertFrom( SfxMedium& rMedium ) +{ + const OUString aFilterName( rMedium.GetFilter()->GetFilterName() ); + bool bRet = false; + bool bStartPresentation = false; + + SetWaitCursor( true ); + + SfxItemSet* pSet = rMedium.GetItemSet(); + if( pSet ) + { + if( ( SfxItemState::SET == pSet->GetItemState(SID_PREVIEW ) ) && pSet->Get( SID_PREVIEW ).GetValue() ) + { + mpDoc->SetStarDrawPreviewMode( true ); + } + + if( SfxItemState::SET == pSet->GetItemState(SID_DOC_STARTPRESENTATION)&& + pSet->Get( SID_DOC_STARTPRESENTATION ).GetValue() ) + { + bStartPresentation = true; + mpDoc->SetStartWithPresentation( true ); + } + } + + if( aFilterName == pFilterPowerPoint97 + || aFilterName == pFilterPowerPoint97Template + || aFilterName == pFilterPowerPoint97AutoPlay) + { + mpDoc->StopWorkStartupDelay(); + bRet = SdPPTFilter( rMedium, *this ).Import(); + } + else if (aFilterName.indexOf("impress8") >= 0 || + aFilterName.indexOf("draw8") >= 0) + { + // TODO/LATER: nobody is interested in the error code?! + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + ErrCode nError = ERRCODE_NONE; + bRet = SdXMLFilter( rMedium, *this ).Import( nError ); + + } + else if (aFilterName.indexOf("StarOffice XML (Draw)") >= 0 || + aFilterName.indexOf("StarOffice XML (Impress)") >= 0) + { + // TODO/LATER: nobody is interested in the error code?! + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + ErrCode nError = ERRCODE_NONE; + bRet = SdXMLFilter( rMedium, *this, SdXMLFilterMode::Normal, SOFFICE_FILEFORMAT_60 ).Import( nError ); + } + else if (aFilterName == "CGM - Computer Graphics Metafile") + { + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + bRet = SdCGMFilter( rMedium, *this ).Import(); + } + else if (aFilterName == "draw_pdf_import") + { + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + bRet = SdPdfFilter(rMedium, *this).Import(); + } + else + { + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + bRet = SdGRFFilter( rMedium, *this ).Import(); + } + + FinishedLoading(); + + // tell SFX to change viewshell when in preview mode + if( IsPreview() ) + { + SfxItemSet *pMediumSet = GetMedium()->GetItemSet(); + + if( pMediumSet ) + pMediumSet->Put( SfxUInt16Item( SID_VIEW_ID, 5 ) ); + } + SetWaitCursor( false ); + + // tell SFX to change viewshell when in preview mode + if( IsPreview() || bStartPresentation ) + { + SfxItemSet *pMediumSet = GetMedium()->GetItemSet(); + if( pMediumSet ) + pMediumSet->Put( SfxUInt16Item( SID_VIEW_ID, bStartPresentation ? 1 : 5 ) ); + } + + return bRet; +} + +/** + * Writes pools and document to the open streams + */ +bool DrawDocShell::Save() +{ + mpDoc->StopWorkStartupDelay(); + + //TODO/LATER: why this?! + if( GetCreateMode() == SfxObjectCreateMode::STANDARD ) + SfxObjectShell::SetVisArea( ::tools::Rectangle() ); + + bool bRet = SfxObjectShell::Save(); + + if( bRet ) + bRet = SdXMLFilter( *GetMedium(), *this, SdXMLFilterMode::Normal, SotStorage::GetVersion( GetMedium()->GetStorage() ) ).Export(); + + return bRet; +} + +/** + * Writes pools and document to the provided storage + */ +bool DrawDocShell::SaveAs( SfxMedium& rMedium ) +{ + mpDoc->setDocAccTitle(OUString()); + if (SfxViewFrame* pFrame1 = SfxViewFrame::GetFirst(this)) + { + if (vcl::Window* pSysWin = pFrame1->GetWindow().GetSystemWindow()) + { + pSysWin->SetAccessibleName(OUString()); + } + } + mpDoc->StopWorkStartupDelay(); + + //With custom animation, if Outliner is modified, update text before saving + if( mpViewShell ) + { + SdPage* pPage = mpViewShell->getCurrentPage(); + if( pPage && pPage->getMainSequence()->getCount() ) + { + SdrObject* pObj = mpViewShell->GetView()->GetTextEditObject(); + SdrOutliner* pOutl = mpViewShell->GetView()->GetTextEditOutliner(); + if( pObj && pOutl && pOutl->IsModified() ) + { + std::optional pNewText = pOutl->CreateParaObject( 0, pOutl->GetParagraphCount() ); + pObj->SetOutlinerParaObject( std::move(pNewText) ); + pOutl->ClearModifyFlag(); + } + } + } + + //TODO/LATER: why this?! + if( GetCreateMode() == SfxObjectCreateMode::STANDARD ) + SfxObjectShell::SetVisArea( ::tools::Rectangle() ); + + bool bRet = SfxObjectShell::SaveAs( rMedium ); + + if( bRet ) + bRet = SdXMLFilter( rMedium, *this, SdXMLFilterMode::Normal, SotStorage::GetVersion( rMedium.GetStorage() ) ).Export(); + + if( GetError() == ERRCODE_NONE ) + SetError(ERRCODE_NONE); + + return bRet; +} + +/** + * save to foreign format + */ +bool DrawDocShell::ConvertTo( SfxMedium& rMedium ) +{ + bool bRet = false; + + if( mpDoc->GetPageCount() ) + { + std::shared_ptr pMediumFilter = rMedium.GetFilter(); + const OUString aTypeName( pMediumFilter->GetTypeName() ); + std::unique_ptr xFilter; + + if( aTypeName.indexOf( "graphic_HTML" ) >= 0 ) + { + xFilter = std::make_unique(rMedium, *this); + } + else if( aTypeName.indexOf( "MS_PowerPoint_97" ) >= 0 ) + { + xFilter = std::make_unique(rMedium, *this); + static_cast(xFilter.get())->PreSaveBasic(); + } + else if ( aTypeName.indexOf( "CGM_Computer_Graphics_Metafile" ) >= 0 ) + { + xFilter = std::make_unique(rMedium, *this); + } + else if( aTypeName.indexOf( "draw8" ) >= 0 || + aTypeName.indexOf( "impress8" ) >= 0 ) + { + xFilter = std::make_unique(rMedium, *this); + } + else if( aTypeName.indexOf( "StarOffice_XML_Impress" ) >= 0 || + aTypeName.indexOf( "StarOffice_XML_Draw" ) >= 0 ) + { + xFilter = std::make_unique(rMedium, *this, SdXMLFilterMode::Normal, SOFFICE_FILEFORMAT_60); + } + else + { + xFilter = std::make_unique(rMedium, *this); + } + + if (xFilter) + { + if ( mpViewShell ) + { + ::sd::View* pView = mpViewShell->GetView(); + if ( pView->IsTextEdit() ) + pView->SdrEndTextEdit(); + } + + bRet = xFilter->Export(); + } + } + + return bRet; +} + +/** + * Reopen own streams to ensure that nobody else can prevent use from opening + * them. + */ +bool DrawDocShell::SaveCompleted( const css::uno::Reference< css::embed::XStorage >& xStorage ) +{ + bool bRet = false; + + if( SfxObjectShell::SaveCompleted(xStorage) ) + { + mpDoc->NbcSetChanged( false ); + + if( mpViewShell ) + { + if( dynamic_cast< OutlineViewShell *>( mpViewShell ) != nullptr ) + static_cast(mpViewShell->GetView()) + ->GetOutliner().ClearModifyFlag(); + + SdrOutliner* pOutl = mpViewShell->GetView()->GetTextEditOutliner(); + if( pOutl ) + { + SdrObject* pObj = mpViewShell->GetView()->GetTextEditObject(); + if( pObj ) + pObj->NbcSetOutlinerParaObject( pOutl->CreateParaObject() ); + + pOutl->ClearModifyFlag(); + } + } + + bRet = true; + + SfxViewFrame* pFrame = ( mpViewShell && mpViewShell->GetViewFrame() ) ? + mpViewShell->GetViewFrame() : + SfxViewFrame::Current(); + + if( pFrame ) + pFrame->GetBindings().Invalidate( SID_NAVIGATOR_STATE, true ); + } + return bRet; +} + +SfxStyleSheetBasePool* DrawDocShell::GetStyleSheetPool() +{ + return mpDoc->GetStyleSheetPool(); +} + +void DrawDocShell::GotoBookmark(std::u16string_view rBookmark) +{ + auto pDrawViewShell = dynamic_cast( mpViewShell ); + if (!pDrawViewShell) + return; + + ViewShellBase& rBase (mpViewShell->GetViewShellBase()); + + bool bIsMasterPage = false; + sal_uInt16 nPageNumber = SDRPAGE_NOTFOUND; + SdrObject* pObj = nullptr; + + static constexpr std::u16string_view sInteraction( u"action?" ); + if ( o3tl::starts_with(rBookmark, sInteraction ) ) + { + static constexpr std::u16string_view sJump( u"jump=" ); + if ( o3tl::starts_with(rBookmark.substr( sInteraction.size() ), sJump ) ) + { + std::u16string_view aDestination( rBookmark.substr( sInteraction.size() + sJump.size() ) ); + if ( o3tl::starts_with(aDestination, u"firstslide" ) ) + { + nPageNumber = 1; + } + else if ( o3tl::starts_with(aDestination, u"lastslide" ) ) + { + nPageNumber = mpDoc->GetPageCount() - 2; + } + else if ( o3tl::starts_with(aDestination, u"previousslide" ) ) + { + SdPage* pPage = pDrawViewShell->GetActualPage(); + nPageNumber = pPage->GetPageNum(); + nPageNumber = nPageNumber > 2 ? nPageNumber - 2 : SDRPAGE_NOTFOUND; + } + else if ( o3tl::starts_with(aDestination, u"nextslide" ) ) + { + SdPage* pPage = pDrawViewShell->GetActualPage(); + nPageNumber = pPage->GetPageNum() + 2; + if ( nPageNumber >= mpDoc->GetPageCount() ) + nPageNumber = SDRPAGE_NOTFOUND; + } + } + } + else + { + // Is the bookmark a page? + nPageNumber = mpDoc->GetPageByName( rBookmark, bIsMasterPage ); + + if (nPageNumber == SDRPAGE_NOTFOUND) + { + // Is the bookmark an object? + pObj = mpDoc->GetObj(rBookmark); + + if (pObj) + { + nPageNumber = pObj->getSdrPageFromSdrObject()->GetPageNum(); + } + } + } + if (nPageNumber != SDRPAGE_NOTFOUND) + { + // Jump to the bookmarked page. This is done in three steps. + + SdPage* pPage; + if (bIsMasterPage) + pPage = static_cast( mpDoc->GetMasterPage(nPageNumber) ); + else + pPage = static_cast( mpDoc->GetPage(nPageNumber) ); + + // 1.) Change the view shell to the edit view, the notes view, + // or the handout view. + PageKind eNewPageKind = pPage->GetPageKind(); + + if( (eNewPageKind != PageKind::Standard) && (mpDoc->GetDocumentType() == DocumentType::Draw) ) + return; + + if (eNewPageKind != pDrawViewShell->GetPageKind()) + { + // change work area + GetFrameView()->SetPageKind(eNewPageKind); + OUString sViewURL; + switch (eNewPageKind) + { + case PageKind::Standard: + sViewURL = FrameworkHelper::msImpressViewURL; + break; + case PageKind::Notes: + sViewURL = FrameworkHelper::msNotesViewURL; + break; + case PageKind::Handout: + sViewURL = FrameworkHelper::msHandoutViewURL; + break; + default: + break; + } + if (!sViewURL.isEmpty()) + { + std::shared_ptr pHelper ( + FrameworkHelper::Instance(rBase)); + pHelper->RequestView( + sViewURL, + FrameworkHelper::msCenterPaneURL); + pHelper->WaitForUpdate(); + + // Get the new DrawViewShell. + mpViewShell = pHelper->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + pDrawViewShell = dynamic_cast(mpViewShell); + } + else + { + pDrawViewShell = nullptr; + } + } + + if (pDrawViewShell != nullptr) + { + setEditMode(pDrawViewShell, bIsMasterPage); + + // Make the bookmarked page the current page. This is done + // by using the API because this takes care of all the + // little things to be done. Especially writing the view + // data to the frame view. + sal_uInt16 nSdPgNum = (nPageNumber - 1) / 2; + Reference xController (rBase.GetController(), UNO_QUERY); + if (xController.is()) + { + Reference xDrawPage (pPage->getUnoPage(), UNO_QUERY); + xController->setCurrentPage (xDrawPage); + } + else + { + // As a fall back switch to the page via the core. + DBG_ASSERT (xController.is(), + "DrawDocShell::GotoBookmark: can't switch page via API"); + pDrawViewShell->SwitchPage(nSdPgNum); + } + + if (pDrawViewShell->GetDispatcher()) + { + // show page + SvxZoomItem aZoom; + aZoom.SetType( SvxZoomType::WHOLEPAGE ); + pDrawViewShell->GetDispatcher()->ExecuteList(SID_ATTR_ZOOM, SfxCallMode::ASYNCHRON, { &aZoom }); + } + + if (pObj != nullptr) + { + // select object + pDrawViewShell->GetView()->UnmarkAll(); + pDrawViewShell->GetView()->MarkObj( + pObj, + pDrawViewShell->GetView()->GetSdrPageView()); + } + } + } + + SfxBindings& rBindings = ((pDrawViewShell && pDrawViewShell->GetViewFrame()!=nullptr) + ? pDrawViewShell->GetViewFrame() + : SfxViewFrame::Current() )->GetBindings(); + + rBindings.Invalidate(SID_NAVIGATOR_STATE, true); + rBindings.Invalidate(SID_NAVIGATOR_PAGENAME); +} + +/** + * If it should become a document template. + */ +bool DrawDocShell::SaveAsOwnFormat( SfxMedium& rMedium ) +{ + + std::shared_ptr pFilter = rMedium.GetFilter(); + + if (pFilter->IsOwnTemplateFormat()) + { + /* now the StarDraw specialty: + we assign known layout names to the layout template of the first + page, we set the layout names of the affected masterpages and pages. + We inform all text objects of the affected standard, note and + masterpages about the name change. + */ + + OUString aLayoutName; + + SfxStringItem const * pLayoutItem = rMedium.GetItemSet()->GetItemIfSet(SID_TEMPLATE_NAME, false); + if( pLayoutItem ) + { + aLayoutName = pLayoutItem->GetValue(); + } + else + { + INetURLObject aURL( rMedium.GetName() ); + aURL.removeExtension(); + aLayoutName = aURL.getName(); + } + + if (aLayoutName.isEmpty()) + { + sal_uInt32 nCount = mpDoc->GetMasterSdPageCount(PageKind::Standard); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + OUString aOldPageLayoutName = mpDoc->GetMasterSdPage(i, PageKind::Standard)->GetLayoutName(); + OUString aNewLayoutName = aLayoutName; + // Don't add suffix for the first master page + if( i > 0 ) + aNewLayoutName += OUString::number(i); + + mpDoc->RenameLayoutTemplate(aOldPageLayoutName, aNewLayoutName); + } + } + } + + return SfxObjectShell::SaveAsOwnFormat(rMedium); +} + +void DrawDocShell::FillClass(SvGlobalName* pClassName, + SotClipboardFormatId* pFormat, + OUString* pFullTypeName, + sal_Int32 nFileFormat, + bool bTemplate /* = false */) const +{ + if (nFileFormat == SOFFICE_FILEFORMAT_60) + { + if ( meDocType == DocumentType::Draw ) + { + *pClassName = SvGlobalName(SO3_SDRAW_CLASSID_60); + *pFormat = SotClipboardFormatId::STARDRAW_60; + *pFullTypeName = SdResId(STR_GRAPHIC_DOCUMENT_FULLTYPE_60); + } + else + { + *pClassName = SvGlobalName(SO3_SIMPRESS_CLASSID_60); + *pFormat = SotClipboardFormatId::STARIMPRESS_60; + *pFullTypeName = SdResId(STR_IMPRESS_DOCUMENT_FULLTYPE_60); + } + } + else if (nFileFormat == SOFFICE_FILEFORMAT_8) + { + if ( meDocType == DocumentType::Draw ) + { + *pClassName = SvGlobalName(SO3_SDRAW_CLASSID_60); + *pFormat = bTemplate ? SotClipboardFormatId::STARDRAW_8_TEMPLATE : SotClipboardFormatId::STARDRAW_8; + *pFullTypeName = SdResId(STR_GRAPHIC_DOCUMENT_FULLTYPE_80); // HACK: method will be removed with new storage API + } + else + { + *pClassName = SvGlobalName(SO3_SIMPRESS_CLASSID_60); + *pFormat = bTemplate ? SotClipboardFormatId::STARIMPRESS_8_TEMPLATE : SotClipboardFormatId::STARIMPRESS_8; + *pFullTypeName = SdResId(STR_IMPRESS_DOCUMENT_FULLTYPE_80); // HACK: method will be removed with new storage API + } + } +} + +OutputDevice* DrawDocShell::GetDocumentRefDev() +{ + OutputDevice* pReferenceDevice = SfxObjectShell::GetDocumentRefDev (); + // Only when our parent does not have a reference device then we return + // our own. + if (pReferenceDevice == nullptr && mpDoc != nullptr) + pReferenceDevice = mpDoc->GetRefDevice (); + return pReferenceDevice; +} + +/** executes the SID_OPENDOC slot to let the framework open a document + with the given URL and this document as a referer */ +void DrawDocShell::OpenBookmark( const OUString& rBookmarkURL ) +{ + SfxStringItem aStrItem( SID_FILE_NAME, rBookmarkURL ); + SfxStringItem aReferer( SID_REFERER, GetMedium()->GetName() ); + const SfxPoolItem* ppArgs[] = { &aStrItem, &aReferer, nullptr }; + ( mpViewShell ? mpViewShell->GetViewFrame() : SfxViewFrame::Current() )->GetBindings().Execute( SID_OPENHYPERLINK, ppArgs ); +} + +std::shared_ptr DrawDocShell::CreateDocumentInfoDialog(weld::Window* pParent, const SfxItemSet &rSet) +{ + std::shared_ptr xDlg = std::make_shared(pParent, rSet); + DrawDocShell* pDocSh = dynamic_cast(SfxObjectShell::Current()); + if( pDocSh == this ) + { + xDlg->AddFontTabPage(); + } + return xDlg; +} + +void DrawDocShell::setEditMode(DrawViewShell* pDrawViewShell, bool isMasterPage) +{ + // Set the edit mode to either the normal edit mode or the + // master page mode. + EditMode eNewEditMode = EditMode::Page; + if (isMasterPage) + { + eNewEditMode = EditMode::MasterPage; + } + + if (eNewEditMode != pDrawViewShell->GetEditMode()) + { + // Set EditMode + pDrawViewShell->ChangeEditMode(eNewEditMode, false); + } +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/docshell.cxx b/sd/source/ui/docshell/docshell.cxx new file mode 100644 index 000000000..78279687a --- /dev/null +++ b/sd/source/ui/docshell/docshell.cxx @@ -0,0 +1,515 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace sd; +#define ShellClass_DrawDocShell +#include + +SFX_IMPL_SUPERCLASS_INTERFACE(DrawDocShell, SfxObjectShell); + +void DrawDocShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SvxSearchDialogWrapper::GetChildWindowId()); +} + +namespace sd { + +/** + * slotmaps and definitions of SFX + */ + +SFX_IMPL_OBJECTFACTORY( + DrawDocShell, + SvGlobalName(SO3_SIMPRESS_CLASSID), + "simpress" ) + +void DrawDocShell::Construct( bool bClipboard ) +{ + mbInDestruction = false; + SetSlotFilter(); // resets the filter + + mbOwnDocument = mpDoc == nullptr; + if( mbOwnDocument ) + mpDoc = new SdDrawDocument(meDocType, this); + + // The document has been created so we can call UpdateRefDevice() to set + // the document's ref device. + UpdateRefDevice(); + + SetBaseModel( new SdXImpressDocument( this, bClipboard ) ); + SetPool( &mpDoc->GetItemPool() ); + std::unique_ptr pUndoManager(new sd::UndoManager); + pUndoManager->SetDocShell(this); + mpUndoManager = std::move(pUndoManager); + + if (!utl::ConfigManager::IsFuzzing() + && officecfg::Office::Common::Undo::Steps::get() < 1) + { + mpUndoManager->EnableUndo(false); // tdf#108863 disable if 0 steps + } + mpDoc->SetSdrUndoManager( mpUndoManager.get() ); + mpDoc->SetSdrUndoFactory( new sd::UndoFactory ); + UpdateTablePointers(); + SetStyleFamily(SfxStyleFamily::Pseudo); +} + +DrawDocShell::DrawDocShell(SfxObjectCreateMode eMode, + bool bDataObject, + DocumentType eDocumentType) : + SfxObjectShell( eMode == SfxObjectCreateMode::INTERNAL ? SfxObjectCreateMode::EMBEDDED : eMode), + mpDoc(nullptr), + mpPrinter(nullptr), + mpViewShell(nullptr), + meDocType(eDocumentType), + mbSdDataObj(bDataObject), + mbOwnPrinter(false) +{ + Construct( eMode == SfxObjectCreateMode::INTERNAL ); +} + +DrawDocShell::DrawDocShell( SfxModelFlags nModelCreationFlags, bool bDataObject, DocumentType eDocumentType ) : + SfxObjectShell( nModelCreationFlags ), + mpDoc(nullptr), + mpPrinter(nullptr), + mpViewShell(nullptr), + meDocType(eDocumentType), + mbSdDataObj(bDataObject), + mbOwnPrinter(false) +{ + Construct( false ); +} + +DrawDocShell::DrawDocShell(SdDrawDocument* pDoc, SfxObjectCreateMode eMode, + bool bDataObject, + DocumentType eDocumentType) : + SfxObjectShell(eMode == SfxObjectCreateMode::INTERNAL ? SfxObjectCreateMode::EMBEDDED : eMode), + mpDoc(pDoc), + mpPrinter(nullptr), + mpViewShell(nullptr), + meDocType(eDocumentType), + mbSdDataObj(bDataObject), + mbOwnPrinter(false) +{ + Construct( eMode == SfxObjectCreateMode::INTERNAL ); +} + +DrawDocShell::~DrawDocShell() +{ + // Tell all listeners that the doc shell is about to be + // destroyed. This has been introduced for the PreviewRenderer to + // free its view (that uses the item poll of the doc shell) but + // may be useful in other places as well. + Broadcast(SfxHint(SfxHintId::Dying)); + + mbInDestruction = true; + + if (mpViewShell) + { + auto* pView = mpViewShell->GetView(); + if (pView) + { + auto & pSearchContext = pView->getSearchContext(); + pSearchContext.resetSearchFunction(); + } + } + + mpFontList.reset(); + + if( mpDoc ) + mpDoc->SetSdrUndoManager( nullptr ); + mpUndoManager.reset(); + + if (mbOwnPrinter) + mpPrinter.disposeAndClear(); + + if( mbOwnDocument ) + delete mpDoc; + + // that the navigator get informed about the disappearance of the document + SfxBoolItem aItem(SID_NAVIGATOR_INIT, true); + SfxViewFrame* pFrame = mpViewShell ? mpViewShell->GetFrame() : GetFrame(); + + if( !pFrame ) + pFrame = SfxViewFrame::GetFirst( this ); + + if( pFrame ) + { + pFrame->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_INIT, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + } +} + +void DrawDocShell::GetState(SfxItemSet &rSet) +{ + + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + + switch ( nSlotId ) + { + case SID_ATTR_CHAR_FONTLIST: + rSet.Put( SvxFontListItem( mpFontList.get(), nSlotId ) ); + break; + + case SID_SEARCH_ITEM: + { + rSet.Put( *SD_MOD()->GetSearchItem() ); + } + break; + + case SID_CLOSEDOC: + GetSlotState(SID_CLOSEDOC, SfxObjectShell::GetInterface(), &rSet); + break; + + case SID_SEARCH_OPTIONS: + { + SearchOptionFlags nOpt = SearchOptionFlags::SEARCH | + SearchOptionFlags::WHOLE_WORDS | + SearchOptionFlags::BACKWARDS | + SearchOptionFlags::REG_EXP | + SearchOptionFlags::EXACT | + SearchOptionFlags::SIMILARITY | + SearchOptionFlags::SELECTION; + + if (!IsReadOnly()) + { + nOpt |= SearchOptionFlags::REPLACE; + nOpt |= SearchOptionFlags::REPLACE_ALL; + } + + rSet.Put(SfxUInt16Item(nWhich, static_cast(nOpt))); + } + break; + + case SID_VERSION: + { + GetSlotState( SID_VERSION, SfxObjectShell::GetInterface(), &rSet ); + } + break; + + case SID_CHINESE_CONVERSION: + case SID_HANGUL_HANJA_CONVERSION: + { + rSet.Put(SfxVisibilityItem(nWhich, SvtCJKOptions::IsAnyEnabled())); + } + break; + case SID_LANGUAGE_STATUS: + { + SdrObject* pObj = nullptr; + bool bLanguageFound = false; + OutlinerParaObject* pParaObj = nullptr; + LanguageType eLanguage( LANGUAGE_DONTKNOW ); + sal_uInt16 nCount = mpDoc->GetPageCount(); + for ( sal_uInt16 itPage = 0; itPage < nCount && !bLanguageFound; itPage++ ) + { + SdrObjListIter aListIter(mpDoc->GetPage(itPage), SdrIterMode::DeepWithGroups); + while ( aListIter.IsMore() && !bLanguageFound ) + { + pObj = aListIter.Next(); + if ( pObj ) + { + pParaObj = pObj->GetOutlinerParaObject(); + if ( pParaObj ) + { + SdrOutliner aOutliner(&mpDoc->GetPool(), OutlinerMode::TextObject); + aOutliner.SetText(*pParaObj); + eLanguage = aOutliner.GetLanguage(0, 0); + bLanguageFound = eLanguage != LANGUAGE_DONTKNOW; + } + } + } + } + + if ( eLanguage == LANGUAGE_DONTKNOW ) + { + eLanguage = mpDoc->GetLanguage( EE_CHAR_LANGUAGE ); + } + + OUString aLanguage = SvtLanguageTable::GetLanguageString(eLanguage); + if (comphelper::LibreOfficeKit::isActive()) + { + if (eLanguage == LANGUAGE_DONTKNOW) + { + aLanguage += ";-"; + } + else + { + aLanguage += ";" + LanguageTag(eLanguage).getBcp47(false); + } + } + rSet.Put(SfxStringItem(nWhich, aLanguage)); + } + break; + + case SID_NOTEBOOKBAR: + { + if (mpViewShell) + { + bool bImpress = mpDoc->GetDocumentType() == DocumentType::Impress; + bool bVisible = false; + if(bImpress) + { + bVisible = sfx2::SfxNotebookBar::StateMethod(mpViewShell->GetFrame()->GetBindings(), + u"modules/simpress/ui/"); + } + else + { + bVisible = sfx2::SfxNotebookBar::StateMethod(mpViewShell->GetFrame()->GetBindings(), + u"modules/sdraw/ui/"); + } + rSet.Put( SfxBoolItem( SID_NOTEBOOKBAR, bVisible ) ); + } + } + break; + + default: + break; + } + nWhich = aIter.NextWhich(); + } + + SfxViewFrame* pFrame = SfxViewFrame::Current(); + + if (pFrame) + { + if (rSet.GetItemState(SID_RELOAD) != SfxItemState::UNKNOWN) + { + pFrame->GetSlotState(SID_RELOAD, + pFrame->GetInterface(), &rSet); + } + } +} + +void DrawDocShell::Activate( bool bMDI) +{ + if (bMDI) + { + ApplySlotFilter(); + mpDoc->StartOnlineSpelling(); + } +} + +void DrawDocShell::Deactivate( bool ) +{ +} + +SfxUndoManager* DrawDocShell::GetUndoManager() +{ + return mpUndoManager.get(); +} + +void DrawDocShell::UpdateTablePointers() +{ + PutItem( SvxColorListItem( mpDoc->GetColorList(), SID_COLOR_TABLE ) ); + PutItem( SvxGradientListItem( mpDoc->GetGradientList(), SID_GRADIENT_LIST ) ); + PutItem( SvxHatchListItem( mpDoc->GetHatchList(), SID_HATCH_LIST ) ); + PutItem( SvxBitmapListItem( mpDoc->GetBitmapList(), SID_BITMAP_LIST ) ); + PutItem( SvxPatternListItem( mpDoc->GetPatternList(), SID_PATTERN_LIST ) ); + PutItem( SvxDashListItem( mpDoc->GetDashList(), SID_DASH_LIST ) ); + PutItem( SvxLineEndListItem( mpDoc->GetLineEndList(), SID_LINEEND_LIST ) ); + + UpdateFontList(); +} + +void DrawDocShell::CancelSearching() +{ + if (mpViewShell) + { + auto* pView = mpViewShell->GetView(); + if (pView) + { + auto & pSearchContext = pView->getSearchContext(); + pSearchContext.resetSearchFunction(); + } + } +} + +/** + * apply configured slot filters + */ +void DrawDocShell::ApplySlotFilter() const +{ + SfxViewShell* pTestViewShell = SfxViewShell::GetFirst(); + + while( pTestViewShell ) + { + if( pTestViewShell->GetObjectShell() + == this + && pTestViewShell->GetViewFrame() + && pTestViewShell->GetViewFrame()->GetDispatcher() ) + { + SfxDispatcher* pDispatcher = pTestViewShell->GetViewFrame()->GetDispatcher(); + + if( !mpFilterSIDs.empty() ) + pDispatcher->SetSlotFilter( mbFilterEnable ? SfxSlotFilterState::ENABLED : SfxSlotFilterState::DISABLED, mpFilterSIDs ); + else + pDispatcher->SetSlotFilter(); + + if( pDispatcher->GetBindings() ) + pDispatcher->GetBindings()->InvalidateAll( true ); + } + + pTestViewShell = SfxViewShell::GetNext( *pTestViewShell ); + } +} + +void DrawDocShell::SetModified( bool bSet /* = true */ ) +{ + SfxObjectShell::SetModified( bSet ); + + // change model state, too + // only set the changed state if modification is enabled + if( IsEnableSetModified() ) + { + if ( mpDoc ) + mpDoc->NbcSetChanged( bSet ); + + Broadcast( SfxHint( SfxHintId::DocChanged ) ); + } +} + +/** + * Callback for ExecuteSpellPopup() + */ +// ExecuteSpellPopup now handled by DrawDocShell. This is necessary +// to get hands on the outliner and the text object. +IMPL_LINK(DrawDocShell, OnlineSpellCallback, SpellCallbackInfo&, rInfo, void) +{ + SdrObject* pObj = nullptr; + SdrOutliner* pOutl = nullptr; + + if(GetViewShell()) + { + pOutl = GetViewShell()->GetView()->GetTextEditOutliner(); + pObj = GetViewShell()->GetView()->GetTextEditObject(); + } + + mpDoc->ImpOnlineSpellCallback(&rInfo, pObj, pOutl); +} + +void DrawDocShell::ClearUndoBuffer() +{ + // clear possible undo buffers of outliners + SfxViewFrame* pSfxViewFrame = SfxViewFrame::GetFirst(this, false); + while(pSfxViewFrame) + { + ViewShellBase* pViewShellBase = dynamic_cast< ViewShellBase* >( pSfxViewFrame->GetViewShell() ); + if( pViewShellBase ) + { + std::shared_ptr pViewSh( pViewShellBase->GetMainViewShell() ); + if( pViewSh ) + { + ::sd::View* pView = pViewSh->GetView(); + if( pView ) + { + pView->SdrEndTextEdit(); + sd::OutlineView* pOutlView = dynamic_cast< sd::OutlineView* >( pView ); + if( pOutlView ) + { + pOutlView->GetOutliner().GetUndoManager().Clear(); + } + } + } + } + pSfxViewFrame = SfxViewFrame::GetNext(*pSfxViewFrame, this, false); + } + + SfxUndoManager* pUndoManager = GetUndoManager(); + if(pUndoManager && pUndoManager->GetUndoActionCount()) + pUndoManager->Clear(); +} + +std::vector DrawDocShell::GetThemeColors() +{ + auto pViewShell = dynamic_cast(GetViewShell()); + if (!pViewShell) + { + return {}; + } + + SdPage* pPage = pViewShell->getCurrentPage(); + svx::Theme* pTheme = pPage->getSdrPageProperties().GetTheme(); + if (!pPage->IsMasterPage()) + { + pTheme = pPage->TRG_GetMasterPage().getSdrPageProperties().GetTheme(); + } + + if (!pTheme) + { + return {}; + } + + return pTheme->GetColors(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/grdocsh.cxx b/sd/source/ui/docshell/grdocsh.cxx new file mode 100644 index 000000000..f0f5af956 --- /dev/null +++ b/sd/source/ui/docshell/grdocsh.cxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include + +#include +#include + +using namespace sd; +#define ShellClass_GraphicDocShell +#include + +namespace sd +{ +SFX_IMPL_SUPERCLASS_INTERFACE(GraphicDocShell, SfxObjectShell) + +void GraphicDocShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); +} + +SFX_IMPL_OBJECTFACTORY(GraphicDocShell, SvGlobalName(SO3_SDRAW_CLASSID_60), "sdraw") + +GraphicDocShell::GraphicDocShell(SfxObjectCreateMode eMode) + : DrawDocShell(eMode, /*bDataObject*/ true, DocumentType::Draw) +{ + SetStyleFamily(SfxStyleFamily::Para); +} + +GraphicDocShell::GraphicDocShell(SfxModelFlags nModelCreationFlags) + : DrawDocShell(nModelCreationFlags, /*bDataObject*/ false, DocumentType::Draw) +{ + SetStyleFamily(SfxStyleFamily::Para); +} + +GraphicDocShell::~GraphicDocShell() {} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/docshell/sdclient.cxx b/sd/source/ui/docshell/sdclient.cxx new file mode 100644 index 000000000..02521c257 --- /dev/null +++ b/sd/source/ui/docshell/sdclient.cxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + +Client::Client(SdrOle2Obj* pObj, ViewShell* pViewShell, vcl::Window* pWindow) : + SfxInPlaceClient(pViewShell->GetViewShell(), pWindow, pObj->GetAspect() ), + mpViewShell(pViewShell), + pSdrOle2Obj(pObj) +{ + SetObject( pObj->GetObjRef() ); + DBG_ASSERT( GetObject().is(), "No object connected!" ); +} + +Client::~Client() +{ +} + +/** + * If IP active, then we get this request to increase the visible section of the + * object. + */ +void Client::RequestNewObjectArea( ::tools::Rectangle& aObjRect ) +{ + ::sd::View* pView = mpViewShell->GetView(); + + bool bSizeProtect = false; + bool bPosProtect = false; + + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + // no need to check for changes, this method is called only if the area really changed + bSizeProtect = pObj->IsResizeProtect(); + bPosProtect = pObj->IsMoveProtect(); + } + + ::tools::Rectangle aOldRect = GetObjArea(); + if ( bPosProtect ) + aObjRect.SetPos( aOldRect.TopLeft() ); + + if ( bSizeProtect ) + aObjRect.SetSize( aOldRect.GetSize() ); + + ::tools::Rectangle aWorkArea( pView->GetWorkArea() ); + if ( aWorkArea.Contains(aObjRect) || bPosProtect || aObjRect == aOldRect ) + return; + + // correct position + Point aPos = aObjRect.TopLeft(); + Size aSize = aObjRect.GetSize(); + Point aWorkAreaTL = aWorkArea.TopLeft(); + Point aWorkAreaBR = aWorkArea.BottomRight(); + + aPos.setX( std::max(aPos.X(), aWorkAreaTL.X()) ); + aPos.setX( std::min(aPos.X(), aWorkAreaBR.X()-aSize.Width()) ); + aPos.setY( std::max(aPos.Y(), aWorkAreaTL.Y()) ); + aPos.setY( std::min(aPos.Y(), aWorkAreaBR.Y()-aSize.Height()) ); + + aObjRect.SetPos(aPos); +} + +void Client::ObjectAreaChanged() +{ + ::sd::View* pView = mpViewShell->GetView(); + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() != 1) + return; + + SdrMark* pMark = rMarkList.GetMark(0); + SdrOle2Obj* pObj = dynamic_cast< SdrOle2Obj* >(pMark->GetMarkedSdrObj()); + + if(!pObj) + return; + + // no need to check for changes, this method is called only if the area really changed + ::tools::Rectangle aNewRectangle(GetScaledObjArea()); + + // #i118524# if sheared/rotated, center to non-rotated LogicRect + pObj->setSuppressSetVisAreaSize(true); + + if(pObj->GetGeoStat().nRotationAngle || pObj->GetGeoStat().nShearAngle) + { + pObj->SetLogicRect( aNewRectangle ); + + const ::tools::Rectangle& rBoundRect = pObj->GetCurrentBoundRect(); + const Point aDelta(aNewRectangle.Center() - rBoundRect.Center()); + + aNewRectangle.Move(aDelta.X(), aDelta.Y()); + } + + pObj->SetLogicRect( aNewRectangle ); + pObj->setSuppressSetVisAreaSize(false); +} + +void Client::ViewChanged() +{ + if ( GetAspect() == embed::Aspects::MSOLE_ICON ) + { + // the iconified object seems not to need such a scaling handling + // since the replacement image and the size a completely controlled by the container + // TODO/LATER: when the icon exchange is implemented the scaling handling might be required again here + + pSdrOle2Obj->ActionChanged(); // draw needs it to remove lines in slide preview + return; + } + + //TODO/LATER: should we try to avoid the recalculation of the visareasize + //if we know that it didn't change? + if (!mpViewShell->GetActiveWindow()) + return; + + ::sd::View* pView = mpViewShell->GetView(); + if (!pView) + return; + + ::tools::Rectangle aLogicRect( pSdrOle2Obj->GetLogicRect() ); + Size aLogicSize( aLogicRect.GetWidth(), aLogicRect.GetHeight() ); + + if( pSdrOle2Obj->IsChart() ) + { + //charts never should be stretched see #i84323# for example + pSdrOle2Obj->SetLogicRect( ::tools::Rectangle( aLogicRect.TopLeft(), aLogicSize ) ); + pSdrOle2Obj->BroadcastObjectChange(); + return; + } + + // TODO/LEAN: maybe we can do this without requesting the VisualArea? + // working with the visual area might need running state, so the object may switch itself to this state + MapMode aMap100( MapUnit::Map100thMM ); + ::tools::Rectangle aVisArea; + Size aSize = pSdrOle2Obj->GetOrigObjSize( &aMap100 ); + + aVisArea.SetSize( aSize ); + Size aScaledSize( static_cast< ::tools::Long >( GetScaleWidth() * Fraction( aVisArea.GetWidth() ) ), + static_cast< ::tools::Long >( GetScaleHeight() * Fraction( aVisArea.GetHeight() ) ) ); + + // react to the change if the difference is bigger than one pixel + Size aPixelDiff = + Application::GetDefaultDevice()->LogicToPixel( + Size( aLogicRect.GetWidth() - aScaledSize.Width(), + aLogicRect.GetHeight() - aScaledSize.Height() ), + aMap100 ); + if( aPixelDiff.Width() || aPixelDiff.Height() ) + { + pSdrOle2Obj->SetLogicRect( ::tools::Rectangle( aLogicRect.TopLeft(), aScaledSize ) ); + pSdrOle2Obj->BroadcastObjectChange(); + } + else + pSdrOle2Obj->ActionChanged(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx b/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx new file mode 100644 index 000000000..0168c162b --- /dev/null +++ b/sd/source/ui/framework/configuration/ChangeRequestQueue.cxx @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ChangeRequestQueue.hxx" + +namespace sd::framework +{ +ChangeRequestQueue::ChangeRequestQueue() {} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx b/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx new file mode 100644 index 000000000..e60b5b527 --- /dev/null +++ b/sd/source/ui/framework/configuration/ChangeRequestQueue.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +namespace com::sun::star::drawing::framework +{ +class XConfigurationChangeRequest; +} + +namespace sd::framework +{ +/** The ChangeRequestQueue stores the pending requests for changes to the + requested configuration. It is the task of the + ChangeRequestQueueProcessor to process these requests. +*/ +class ChangeRequestQueue + : public ::std::queue> +{ +public: + /** Create an empty queue. + */ + ChangeRequestQueue(); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx new file mode 100644 index 000000000..da633a540 --- /dev/null +++ b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.cxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "debugtrace.hxx" +#include "ChangeRequestQueueProcessor.hxx" +#include "ConfigurationTracer.hxx" + +#include "ConfigurationUpdater.hxx" + +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace { + +#if DEBUG_SD_CONFIGURATION_TRACE + +void TraceRequest (const Reference& rxRequest) +{ + Reference xNamed (rxRequest, UNO_QUERY); + if (xNamed.is()) + SAL_INFO("sd.fwk", __func__ << ": " << xNamed->getName()); +} + +#endif + +} // end of anonymous namespace + +namespace sd::framework { + +ChangeRequestQueueProcessor::ChangeRequestQueueProcessor ( + const std::shared_ptr& rpConfigurationUpdater) + : mnUserEventId(nullptr), + mpConfigurationUpdater(rpConfigurationUpdater) +{ +} + +ChangeRequestQueueProcessor::~ChangeRequestQueueProcessor() +{ + if (mnUserEventId != nullptr) + Application::RemoveUserEvent(mnUserEventId); +} + +void ChangeRequestQueueProcessor::SetConfiguration ( + const Reference& rxConfiguration) +{ + ::osl::MutexGuard aGuard (maMutex); + + mxConfiguration = rxConfiguration; + StartProcessing(); +} + +void ChangeRequestQueueProcessor::AddRequest ( + const Reference& rxRequest) +{ + ::osl::MutexGuard aGuard (maMutex); + +#if DEBUG_SD_CONFIGURATION_TRACE + if (maQueue.empty()) + { + SAL_INFO("sd.fwk", __func__ << ": Adding requests to empty queue"); + ConfigurationTracer::TraceConfiguration( + mxConfiguration, "current configuration of queue processor"); + } + SAL_INFO("sd.fwk", __func__ << ": Adding request"); + TraceRequest(rxRequest); +#endif + + maQueue.push(rxRequest); + StartProcessing(); +} + +void ChangeRequestQueueProcessor::StartProcessing() +{ + ::osl::MutexGuard aGuard (maMutex); + + if (mnUserEventId == nullptr + && mxConfiguration.is() + && ! maQueue.empty()) + { + SAL_INFO("sd.fwk", __func__ << ": ChangeRequestQueueProcessor scheduling processing"); + mnUserEventId = Application::PostUserEvent( + LINK(this,ChangeRequestQueueProcessor,ProcessEvent)); + } +} + +IMPL_LINK_NOARG(ChangeRequestQueueProcessor, ProcessEvent, void*, void) +{ + ::osl::MutexGuard aGuard (maMutex); + + mnUserEventId = nullptr; + + ProcessOneEvent(); + + if ( ! maQueue.empty()) + { + // Schedule the processing of the next event. + StartProcessing(); + } +} + +void ChangeRequestQueueProcessor::ProcessOneEvent() +{ + ::osl::MutexGuard aGuard (maMutex); + + SAL_INFO("sd.fwk", __func__ << ": ProcessOneEvent"); + + if (!mxConfiguration.is() || maQueue.empty()) + return; + + // Get and remove the first entry from the queue. + Reference xRequest (maQueue.front()); + maQueue.pop(); + + // Execute the change request. + if (xRequest.is()) + { +#if DEBUG_SD_CONFIGURATION_TRACE + TraceRequest(xRequest); +#endif + xRequest->execute(mxConfiguration); + } + + if (!maQueue.empty()) + return; + + SAL_INFO("sd.fwk", __func__ << ": All requests are processed"); + // The queue is empty so tell the ConfigurationManager to update + // its state. + if (mpConfigurationUpdater != nullptr) + { +#if DEBUG_SD_CONFIGURATION_TRACE + ConfigurationTracer::TraceConfiguration ( + mxConfiguration, "updating to configuration"); +#endif + mpConfigurationUpdater->RequestUpdate(mxConfiguration); + } +} + +bool ChangeRequestQueueProcessor::IsEmpty() const +{ + return maQueue.empty(); +} + +void ChangeRequestQueueProcessor::ProcessUntilEmpty() +{ + while ( ! IsEmpty()) + ProcessOneEvent(); +} + +void ChangeRequestQueueProcessor::Clear() +{ + ::osl::MutexGuard aGuard (maMutex); + ChangeRequestQueue().swap(maQueue); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx new file mode 100644 index 000000000..4afd5af76 --- /dev/null +++ b/sd/source/ui/framework/configuration/ChangeRequestQueueProcessor.hxx @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ChangeRequestQueue.hxx" +#include + +#include + +#include + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XConfigurationChangeRequest; +} + +struct ImplSVEvent; + +namespace sd::framework +{ +class ConfigurationUpdater; + +/** The ChangeRequestQueueProcessor owns the ChangeRequestQueue and + processes the configuration change requests. + + When after processing one entry the queue is empty then the + XConfigurationController::update() method is called so that the changes + made to the local XConfiguration reference are reflected by the UI. + + Queue entries are processed asynchronously by calling PostUserEvent(). +*/ +class ChangeRequestQueueProcessor +{ +public: + /** The queue processor is created with a reference to an + ConfigurationController so that its UpdateConfiguration() method can + be called when the queue becomes empty. + */ + explicit ChangeRequestQueueProcessor(const std::shared_ptr& rpUpdater); + ~ChangeRequestQueueProcessor(); + + /** Sets the configuration who will be changed by subsequent change + requests. This method should be called only by the configuration + controller who owns the configuration. + */ + void SetConfiguration( + const css::uno::Reference& rxConfiguration); + + /** The given request is appended to the end of the queue and will + eventually be processed when all other entries in front of it have + been processed. + */ + void AddRequest( + const css::uno::Reference& rxRequest); + + /** Returns when the queue is empty. + */ + bool IsEmpty() const; + + /** Process all events in the queue synchronously. + +

This method is typically called when the framework is shut down + to establish an empty configuration.

+ */ + void ProcessUntilEmpty(); + + /** Process the first event in queue. + */ + void ProcessOneEvent(); + + /** Remove all events from the queue. + +

This method is typically called when the framework is shut down + to avoid the processing of still pending activation requests.

+ */ + void Clear(); + +private: + mutable ::osl::Mutex maMutex; + + ChangeRequestQueue maQueue; + + /** The id returned by the last PostUserEvent() call. This id is stored + so that a pending user event can be removed when the queue processor + is destroyed. + */ + ImplSVEvent* mnUserEventId; + + css::uno::Reference mxConfiguration; + + std::shared_ptr mpConfigurationUpdater; + + /** Initiate the processing of the entries in the queue. The actual + processing starts asynchronously. + */ + void StartProcessing(); + + /** Callback function for the PostUserEvent() call. + */ + DECL_LINK(ProcessEvent, void*, void); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/Configuration.cxx b/sd/source/ui/framework/configuration/Configuration.cxx new file mode 100644 index 000000000..7b813a42b --- /dev/null +++ b/sd/source/ui/framework/configuration/Configuration.cxx @@ -0,0 +1,311 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; + +namespace { +/** Use the XResourceId::compareTo() method to implement a compare operator + for STL containers. +*/ +class XResourceIdLess +{ +public: + bool operator () (const Reference& rId1, const Reference& rId2) const + { + return rId1->compareTo(rId2) == -1; + } +}; + +} // end of anonymous namespace + +namespace sd::framework { + +class Configuration::ResourceContainer + : public ::std::set, XResourceIdLess> +{ +public: + ResourceContainer() {} +}; + +//===== Configuration ========================================================= + +Configuration::Configuration ( + const Reference& rxBroadcaster, + bool bBroadcastRequestEvents) + : mpResourceContainer(new ResourceContainer()), + mxBroadcaster(rxBroadcaster), + mbBroadcastRequestEvents(bBroadcastRequestEvents) +{ +} + +Configuration::Configuration ( + const Reference& rxBroadcaster, + bool bBroadcastRequestEvents, + const ResourceContainer& rResourceContainer) + : mpResourceContainer(new ResourceContainer(rResourceContainer)), + mxBroadcaster(rxBroadcaster), + mbBroadcastRequestEvents(bBroadcastRequestEvents) +{ +} + +Configuration::~Configuration() +{ +} + +void Configuration::disposing(std::unique_lock&) +{ + mpResourceContainer->clear(); + mxBroadcaster = nullptr; +} + +//----- XConfiguration -------------------------------------------------------- + +void SAL_CALL Configuration::addResource (const Reference& rxResourceId) +{ + ThrowIfDisposed(); + + if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty()) + throw css::lang::IllegalArgumentException(); + + if (mpResourceContainer->insert(rxResourceId).second) + { + SAL_INFO("sd.fwk", __func__ << ": Configuration::addResource() " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + PostEvent(rxResourceId, true); + } +} + +void SAL_CALL Configuration::removeResource (const Reference& rxResourceId) +{ + ThrowIfDisposed(); + + if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty()) + throw css::lang::IllegalArgumentException(); + + ResourceContainer::iterator iResource (mpResourceContainer->find(rxResourceId)); + if (iResource != mpResourceContainer->end()) + { + SAL_INFO("sd.fwk", __func__ << ": Configuration::removeResource() " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + PostEvent(rxResourceId,false); + mpResourceContainer->erase(iResource); + } +} + +Sequence > SAL_CALL Configuration::getResources ( + const Reference& rxAnchorId, + const OUString& rsResourceURLPrefix, + AnchorBindingMode eMode) +{ + std::unique_lock aGuard (m_aMutex); + ThrowIfDisposed(); + + const bool bFilterResources (!rsResourceURLPrefix.isEmpty()); + + // Collect the matching resources in a vector. + ::std::vector > aResources; + for (const auto& rxResource : *mpResourceContainer) + { + if ( ! rxResource->isBoundTo(rxAnchorId,eMode)) + continue; + + if (bFilterResources) + { + // Apply the given resource prefix as filter. + + // Make sure that the resource is bound directly to the anchor. + if (eMode != AnchorBindingMode_DIRECT + && ! rxResource->isBoundTo(rxAnchorId, AnchorBindingMode_DIRECT)) + { + continue; + } + + // Make sure that the resource URL matches the given prefix. + if ( ! rxResource->getResourceURL().match(rsResourceURLPrefix)) + { + continue; + } + } + + aResources.push_back(rxResource); + } + + return comphelper::containerToSequence(aResources); +} + +sal_Bool SAL_CALL Configuration::hasResource (const Reference& rxResourceId) +{ + std::unique_lock aGuard (m_aMutex); + ThrowIfDisposed(); + + return rxResourceId.is() + && mpResourceContainer->find(rxResourceId) != mpResourceContainer->end(); +} + +//----- XCloneable ------------------------------------------------------------ + +Reference SAL_CALL Configuration::createClone() +{ + std::unique_lock aGuard (m_aMutex); + ThrowIfDisposed(); + + return new Configuration( + mxBroadcaster, + mbBroadcastRequestEvents, + *mpResourceContainer); +} + +//----- XNamed ---------------------------------------------------------------- + +OUString SAL_CALL Configuration::getName() +{ + std::unique_lock aGuard (m_aMutex); + OUStringBuffer aString; + + if (m_bDisposed) + aString.append("DISPOSED "); + aString.append("Configuration["); + + ResourceContainer::const_iterator iResource; + for (iResource=mpResourceContainer->begin(); + iResource!=mpResourceContainer->end(); + ++iResource) + { + if (iResource != mpResourceContainer->begin()) + aString.append(", "); + aString.append(FrameworkHelper::ResourceIdToString(*iResource)); + } + aString.append("]"); + + return aString.makeStringAndClear(); +} + +void SAL_CALL Configuration::setName (const OUString&) +{ + // ignored. +} + +OUString Configuration::getImplementationName() +{ + return + "com.sun.star.comp.Draw.framework.configuration.Configuration"; +} + +sal_Bool Configuration::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence Configuration::getSupportedServiceNames() +{ + return css::uno::Sequence{ + "com.sun.star.drawing.framework.Configuration"}; +} + +void Configuration::PostEvent ( + const Reference& rxResourceId, + const bool bActivation) +{ + OSL_ASSERT(rxResourceId.is()); + + if (!mxBroadcaster.is()) + return; + + ConfigurationChangeEvent aEvent; + aEvent.ResourceId = rxResourceId; + if (bActivation) + if (mbBroadcastRequestEvents) + aEvent.Type = FrameworkHelper::msResourceActivationRequestEvent; + else + aEvent.Type = FrameworkHelper::msResourceActivationEvent; + else + if (mbBroadcastRequestEvents) + aEvent.Type = FrameworkHelper::msResourceDeactivationRequestEvent; + else + aEvent.Type = FrameworkHelper::msResourceDeactivationEvent; + aEvent.Configuration = this; + + mxBroadcaster->notifyEvent(aEvent); +} + +void Configuration::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("Configuration object has already been disposed", + const_cast(static_cast(this))); + } +} + +bool AreConfigurationsEquivalent ( + const Reference& rxConfiguration1, + const Reference& rxConfiguration2) +{ + if (rxConfiguration1.is() != rxConfiguration2.is()) + return false; + if ( ! rxConfiguration1.is() && ! rxConfiguration2.is()) + return true; + + // Get the lists of resources from the two given configurations. + const Sequence > aResources1( + rxConfiguration1->getResources( + nullptr, OUString(), AnchorBindingMode_INDIRECT)); + const Sequence > aResources2( + rxConfiguration2->getResources( + nullptr, OUString(), AnchorBindingMode_INDIRECT)); + + // When the number of resources differ then the configurations can not + // be equivalent. + // Comparison of the two lists of resource ids relies on their + // ordering. + return std::equal(aResources1.begin(), aResources1.end(), aResources2.begin(), aResources2.end(), + [](const Reference& a, const Reference& b) { + if (a.is() && b.is()) + return a->compareTo(b) == 0; + return a.is() == b.is(); + }); +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_configuration_Configuration_get_implementation( + css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::framework::Configuration(nullptr, false)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx b/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx new file mode 100644 index 000000000..99fc1297d --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationClassifier.cxx @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ConfigurationClassifier.hxx" + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +ConfigurationClassifier::ConfigurationClassifier ( + const Reference& rxConfiguration1, + const Reference& rxConfiguration2) + : mxConfiguration1(rxConfiguration1), + mxConfiguration2(rxConfiguration2) +{ +} + +bool ConfigurationClassifier::Partition() +{ + maC1minusC2.clear(); + maC2minusC1.clear(); + + PartitionResources( + mxConfiguration1->getResources(nullptr, OUString(), AnchorBindingMode_DIRECT), + mxConfiguration2->getResources(nullptr, OUString(), AnchorBindingMode_DIRECT)); + + return !maC1minusC2.empty() || !maC2minusC1.empty(); +} + +void ConfigurationClassifier::PartitionResources ( + const css::uno::Sequence >& rS1, + const css::uno::Sequence >& rS2) +{ + ResourceIdVector aC1minusC2; + ResourceIdVector aC2minusC1; + ResourceIdVector aC1andC2; + + // Classify the resources in the configurations that are not bound to + // other resources. + ClassifyResources( + rS1, + rS2, + aC1minusC2, + aC2minusC1, + aC1andC2); + + SAL_INFO("sd.fwk", __func__ << ": copying resource ids to C1-C2"); + CopyResources(aC1minusC2, mxConfiguration1, maC1minusC2); + SAL_INFO("sd.fwk", __func__ << ": copying resource ids to C2-C1"); + CopyResources(aC2minusC1, mxConfiguration2, maC2minusC1); + + // Process the unique resources that belong to both configurations. + for (const auto& rxResource : aC1andC2) + { + PartitionResources( + mxConfiguration1->getResources(rxResource, OUString(), AnchorBindingMode_DIRECT), + mxConfiguration2->getResources(rxResource, OUString(), AnchorBindingMode_DIRECT)); + } +} + +void ConfigurationClassifier::ClassifyResources ( + const css::uno::Sequence >& rS1, + const css::uno::Sequence >& rS2, + ResourceIdVector& rS1minusS2, + ResourceIdVector& rS2minusS1, + ResourceIdVector& rS1andS2) +{ + // Find all elements in rS1 and place them in rS1minusS2 or rS1andS2 + // depending on whether they are in rS2 or not. + for (const Reference& rA1 : rS1) + { + bool bFound = std::any_of(rS2.begin(), rS2.end(), + [&rA1](const Reference& rA2) { + return rA1->getResourceURL() == rA2->getResourceURL(); }); + + if (bFound) + rS1andS2.push_back(rA1); + else + rS1minusS2.push_back(rA1); + } + + // Find all elements in rS2 that are not in rS1. The elements that are + // in both rS1 and rS2 have been handled above and are therefore ignored + // here. + for (const Reference& rA2 : rS2) + { + bool bFound = std::any_of(rS1.begin(), rS1.end(), + [&rA2](const Reference& rA1) { + return rA2->getResourceURL() == rA1->getResourceURL(); }); + + if ( ! bFound) + rS2minusS1.push_back(rA2); + } +} + +void ConfigurationClassifier::CopyResources ( + const ResourceIdVector& rSource, + const Reference& rxConfiguration, + ResourceIdVector& rTarget) +{ + // Copy all resources bound to the ones in aC1minusC2Unique to rC1minusC2. + for (const auto& rxResource : rSource) + { + const Sequence > aBoundResources ( + rxConfiguration->getResources( + rxResource, + OUString(), + AnchorBindingMode_INDIRECT)); + const sal_Int32 nL (aBoundResources.getLength()); + + rTarget.reserve(rTarget.size() + 1 + nL); + rTarget.push_back(rxResource); + + SAL_INFO("sd.fwk", __func__ << ": copying " << + FrameworkHelper::ResourceIdToString(rxResource)); + + for (const Reference& rBoundResource : aBoundResources) + { + rTarget.push_back(rBoundResource); + SAL_INFO("sd.fwk", __func__ << ": copying " << + FrameworkHelper::ResourceIdToString(rBoundResource)); + } + } +} + +#if DEBUG_SD_CONFIGURATION_TRACE + +void ConfigurationClassifier::TraceResourceIdVector ( + const char* pMessage, + const ResourceIdVector& rResources) +{ + + SAL_INFO("sd.fwk", __func__ << ": " << pMessage); + for (const auto& rxResource : rResources) + { + OUString sResource (FrameworkHelper::ResourceIdToString(rxResource)); + SAL_INFO("sd.fwk", __func__ << ": " << sResource); + } +} + +#endif + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx b/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx new file mode 100644 index 000000000..e9384713b --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationClassifier.hxx @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "debugtrace.hxx" +#include + +#include + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} + +namespace sd::framework +{ +/** A ConfigurationClassifier object compares two configurations of + resources and gives access to the differences. It is used mainly when + changes to the current configuration have been requested and the various + resource controllers have to be supplied with the set of resources that + are to be activated or deactivated. +*/ +class ConfigurationClassifier +{ +public: + /** Create a new ConfigurationClassifier object that will compare the + two given configurations. + */ + ConfigurationClassifier( + const css::uno::Reference& rxConfiguration1, + const css::uno::Reference& rxConfiguration2); + + /** Calculate three lists of resource ids. These contain the resources + that belong to one configuration but not the other, or that belong + to both configurations. + @return + When the two configurations differ then return . When + they are equivalent then return . + */ + bool Partition(); + + typedef ::std::vector> + ResourceIdVector; + + /** Return the resources that belong to the configuration given as + rxConfiguration1 to the constructor but that do not belong to + rxConfiguration2. + @return + A reference to the, possibly empty, list of resources is + returned. This reference remains valid as long as the called + ConfigurationClassifier object stays alive. + */ + const ResourceIdVector& GetC1minusC2() const { return maC1minusC2; } + + /** Return the resources that belong to the configuration given as + rxConfiguration2 to the constructor but that do not belong to + rxConfiguration1. + @return + A reference to the, possibly empty, list of resources is + returned. This reference remains valid as long as the called + ConfigurationClassifier object stays alive. + */ + const ResourceIdVector& GetC2minusC1() const { return maC2minusC1; } + +#if DEBUG_SD_CONFIGURATION_TRACE + + /** Return the resources that belong to both the configurations that + where given to the constructor. + @return + A reference to the, possibly empty, list of resources is + returned. This reference remains valid as long as the called + ConfigurationClassifier object stays alive. + */ + const ResourceIdVector& GetC1andC2() const { return maC1andC2; } + + static void TraceResourceIdVector(const char* pMessage, const ResourceIdVector& rResources); + +#endif + +private: + css::uno::Reference mxConfiguration1; + css::uno::Reference mxConfiguration2; + + /** After the call to Classify() this vector holds all elements from + mxConfiguration1 that are not in mxConfiguration2. + */ + ResourceIdVector maC1minusC2; + + /** After the call to Classify() this vector holds all elements from + mxConfiguration2 that are not in mxConfiguration1. + */ + ResourceIdVector maC2minusC1; + + /** Put all the elements in the two given sequences of resource ids and + copy them into one of the resource id result vectors maC1minusC2, + maC2minusC1, and maC1andC2. This is done by using only the resource + URLs for classification. Therefore this method calls itself + recursively. + @param rS1 + One sequence of XResourceId objects. + @param rS2 + Another sequence of XResourceId objects. + */ + void PartitionResources( + const css::uno::Sequence>& rS1, + const css::uno::Sequence>& rS2); + + /** Compare the given sequences of resource ids and put their elements + in one of three vectors depending on whether an element belongs to + both sequences or to one but not the other. Note that only the + resource URLs of the XResourceId objects are used for the + classification. + @param rS1 + One sequence of XResourceId objects. + @param rS2 + Another sequence of XResourceId objects. + */ + static void ClassifyResources( + const css::uno::Sequence>& rS1, + const css::uno::Sequence>& rS2, + ResourceIdVector& rS1minusS2, ResourceIdVector& rS2minusS1, ResourceIdVector& rS1andS2); + + /** Copy the resources given in rSource to the list of resources + specified by rTarget. Resources bound to the ones in rSource, + either directly or indirectly, are copied as well. + @param rSource + All resources and the ones bound to them, either directly or + indirectly, are copied. + @param rxConfiguration + This configuration is used to determine the resources bound to + the ones in rSource. + @param rTarget + This list is filled with resources from rSource and the ones + bound to them. + */ + static void CopyResources( + const ResourceIdVector& rSource, + const css::uno::Reference& rxConfiguration, + ResourceIdVector& rTarget); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationController.cxx b/sd/source/ui/framework/configuration/ConfigurationController.cxx new file mode 100644 index 000000000..3fc95adb9 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationController.cxx @@ -0,0 +1,541 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include "ConfigurationUpdater.hxx" +#include "ConfigurationControllerBroadcaster.hxx" +#include "ConfigurationTracer.hxx" +#include "GenericConfigurationChangeRequest.hxx" +#include "ConfigurationControllerResourceManager.hxx" +#include "ResourceFactoryManager.hxx" +#include "UpdateRequest.hxx" +#include "ChangeRequestQueueProcessor.hxx" +#include "ConfigurationClassifier.hxx" +#include +#include + +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//----- ConfigurationController::Implementation ------------------------------- + +class ConfigurationController::Implementation +{ +public: + Implementation ( + ConfigurationController& rController, + const Reference& rxController); + + Reference mxControllerManager; + + /** The Broadcaster class implements storing and calling of listeners. + */ + std::shared_ptr mpBroadcaster; + + /** The requested configuration which is modified (asynchronously) by + calls to requestResourceActivation() and + requestResourceDeactivation(). The mpConfigurationUpdater makes the + current configuration reflect the content of this one. + */ + css::uno::Reference mxRequestedConfiguration; + + std::shared_ptr mpResourceFactoryContainer; + + std::shared_ptr mpResourceManager; + + std::shared_ptr mpConfigurationUpdater; + + /** The queue processor owns the queue of configuration change request + objects and processes the objects. + */ + std::unique_ptr mpQueueProcessor; + + std::shared_ptr mpConfigurationUpdaterLock; + + sal_Int32 mnLockCount; +}; + +//===== ConfigurationController::Lock ========================================= + +ConfigurationController::Lock::Lock (const Reference& rxController) + : mxController(rxController) +{ + OSL_ASSERT(mxController.is()); + + if (mxController.is()) + mxController->lock(); +} + +ConfigurationController::Lock::~Lock() +{ + if (mxController.is()) + mxController->unlock(); +} + +//===== ConfigurationController =============================================== + +ConfigurationController::ConfigurationController() noexcept + : ConfigurationControllerInterfaceBase(m_aMutex) + , mbIsDisposed(false) +{ +} + +ConfigurationController::~ConfigurationController() noexcept +{ +} + +void SAL_CALL ConfigurationController::disposing() +{ + if (mpImplementation == nullptr) + return; + + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::disposing"); + SAL_INFO("sd.fwk", __func__ << ": requesting empty configuration"); + // To destroy all resources an empty configuration is requested and then, + // synchronously, all resulting requests are processed. + mpImplementation->mpQueueProcessor->Clear(); + restoreConfiguration(new Configuration(this,false)); + mpImplementation->mpQueueProcessor->ProcessUntilEmpty(); + SAL_INFO("sd.fwk", __func__ << ": all requests processed"); + + // Now that all resources have been deactivated, mark the controller as + // disposed. + mbIsDisposed = true; + + // Release the listeners. + lang::EventObject aEvent; + aEvent.Source = uno::Reference(static_cast(this)); + + { + const SolarMutexGuard aSolarGuard; + mpImplementation->mpBroadcaster->DisposeAndClear(); + } + + mpImplementation->mpQueueProcessor.reset(); + mpImplementation->mxRequestedConfiguration = nullptr; + mpImplementation.reset(); +} + +void ConfigurationController::ProcessEvent() +{ + if (mpImplementation != nullptr) + { + OSL_ASSERT(mpImplementation->mpQueueProcessor != nullptr); + + mpImplementation->mpQueueProcessor->ProcessOneEvent(); + } +} + +void ConfigurationController::RequestSynchronousUpdate() +{ + if (mpImplementation == nullptr) + return; + if (mpImplementation->mpQueueProcessor == nullptr) + return; + mpImplementation->mpQueueProcessor->ProcessUntilEmpty(); +} + +//----- XConfigurationControllerBroadcaster ----------------------------------- + +void SAL_CALL ConfigurationController::addConfigurationChangeListener ( + const Reference& rxListener, + const OUString& rsEventType, + const Any& rUserData) +{ + ::osl::MutexGuard aGuard (m_aMutex); + + ThrowIfDisposed(); + OSL_ASSERT(mpImplementation != nullptr); + mpImplementation->mpBroadcaster->AddListener(rxListener, rsEventType, rUserData); +} + +void SAL_CALL ConfigurationController::removeConfigurationChangeListener ( + const Reference& rxListener) +{ + ::osl::MutexGuard aGuard (m_aMutex); + + ThrowIfDisposed(); + mpImplementation->mpBroadcaster->RemoveListener(rxListener); +} + +void SAL_CALL ConfigurationController::notifyEvent ( + const ConfigurationChangeEvent& rEvent) +{ + ThrowIfDisposed(); + mpImplementation->mpBroadcaster->NotifyListeners(rEvent); +} + +//----- XConfigurationController ---------------------------------------------- + +void SAL_CALL ConfigurationController::lock() +{ + OSL_ASSERT(mpImplementation != nullptr); + OSL_ASSERT(mpImplementation->mpConfigurationUpdater != nullptr); + + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + ++mpImplementation->mnLockCount; + if (mpImplementation->mpConfigurationUpdaterLock == nullptr) + mpImplementation->mpConfigurationUpdaterLock + = mpImplementation->mpConfigurationUpdater->GetLock(); +} + +void SAL_CALL ConfigurationController::unlock() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + // Allow unlocking while the ConfigurationController is being disposed + // (but not when that is done and the controller is disposed.) + if (rBHelper.bDisposed) + ThrowIfDisposed(); + + OSL_ASSERT(mpImplementation->mnLockCount>0); + --mpImplementation->mnLockCount; + if (mpImplementation->mnLockCount == 0) + mpImplementation->mpConfigurationUpdaterLock.reset(); +} + +void SAL_CALL ConfigurationController::requestResourceActivation ( + const Reference& rxResourceId, + ResourceActivationMode eMode) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + // Check whether we are being disposed. This is handled differently + // then being completely disposed because the first thing disposing() + // does is to deactivate all remaining resources. This is done via + // regular methods which must not throw DisposedExceptions. Therefore + // we just return silently during that stage. + if (rBHelper.bInDispose) + { + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceActivation(): ignoring " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + return; + } + + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceActivation() " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + + if (!rxResourceId.is()) + return; + + if (eMode == ResourceActivationMode_REPLACE) + { + // Get a list of the matching resources and create deactivation + // requests for them. + const Sequence > aResourceList ( + mpImplementation->mxRequestedConfiguration->getResources( + rxResourceId->getAnchor(), + rxResourceId->getResourceTypePrefix(), + AnchorBindingMode_DIRECT)); + + for (const auto& rResource : aResourceList) + { + // Do not request the deactivation of the resource for which + // this method was called. Doing it would not change the + // outcome but would result in unnecessary work. + if (rxResourceId->compareTo(rResource) == 0) + continue; + + // Request the deactivation of a resource and all resources + // linked to it. + requestResourceDeactivation(rResource); + } + } + + Reference xRequest( + new GenericConfigurationChangeRequest( + rxResourceId, + GenericConfigurationChangeRequest::Activation)); + postChangeRequest(xRequest); +} + +void SAL_CALL ConfigurationController::requestResourceDeactivation ( + const Reference& rxResourceId) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::requestResourceDeactivation() " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + + if (!rxResourceId.is()) + return; + + // Request deactivation of all resources linked to the specified one + // as well. + const Sequence > aLinkedResources ( + mpImplementation->mxRequestedConfiguration->getResources( + rxResourceId, + OUString(), + AnchorBindingMode_DIRECT)); + for (const auto& rLinkedResource : aLinkedResources) + { + // We do not add deactivation requests directly but call this + // method recursively, so that when one time there are resources + // linked to linked resources, these are handled correctly, too. + requestResourceDeactivation(rLinkedResource); + } + + // Add a deactivation request for the specified resource. + Reference xRequest( + new GenericConfigurationChangeRequest( + rxResourceId, + GenericConfigurationChangeRequest::Deactivation)); + postChangeRequest(xRequest); +} + +Reference SAL_CALL ConfigurationController::getResource ( + const Reference& rxResourceId) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + ConfigurationControllerResourceManager::ResourceDescriptor aDescriptor ( + mpImplementation->mpResourceManager->GetResource(rxResourceId)); + return aDescriptor.mxResource; +} + +void SAL_CALL ConfigurationController::update() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + if (mpImplementation->mpQueueProcessor->IsEmpty()) + { + // The queue is empty. Add another request that does nothing but + // asynchronously trigger a request for an update. + mpImplementation->mpQueueProcessor->AddRequest(new UpdateRequest()); + } + else + { + // The queue is not empty, so we rely on the queue processor to + // request an update automatically when the queue becomes empty. + } +} + +sal_Bool SAL_CALL ConfigurationController::hasPendingRequests() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + return ! mpImplementation->mpQueueProcessor->IsEmpty(); +} + +void SAL_CALL ConfigurationController::postChangeRequest ( + const Reference& rxRequest) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + mpImplementation->mpQueueProcessor->AddRequest(rxRequest); +} + +Reference SAL_CALL ConfigurationController::getRequestedConfiguration() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + if (mpImplementation->mxRequestedConfiguration.is()) + return Reference( + mpImplementation->mxRequestedConfiguration->createClone(), UNO_QUERY); + else + return Reference(); +} + +Reference SAL_CALL ConfigurationController::getCurrentConfiguration() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + Reference xCurrentConfiguration( + mpImplementation->mpConfigurationUpdater->GetCurrentConfiguration()); + if (xCurrentConfiguration.is()) + return Reference(xCurrentConfiguration->createClone(), UNO_QUERY); + else + return Reference(); +} + +/** The given configuration is restored by generating the appropriate set of + activation and deactivation requests. +*/ +void SAL_CALL ConfigurationController::restoreConfiguration ( + const Reference& rxNewConfiguration) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + // We will probably be making a couple of activation and deactivation + // requests so lock the configuration controller and let it later update + // all changes at once. + std::shared_ptr pLock ( + mpImplementation->mpConfigurationUpdater->GetLock()); + + // Get lists of resources that are to be activated or deactivated. + Reference xCurrentConfiguration (mpImplementation->mxRequestedConfiguration); +#if OSL_DEBUG_LEVEL >=1 + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::restoreConfiguration("); + ConfigurationTracer::TraceConfiguration(rxNewConfiguration, "requested configuration"); + ConfigurationTracer::TraceConfiguration(xCurrentConfiguration, "current configuration"); +#endif + ConfigurationClassifier aClassifier (rxNewConfiguration, xCurrentConfiguration); + aClassifier.Partition(); +#if DEBUG_SD_CONFIGURATION_TRACE + aClassifier.TraceResourceIdVector( + "requested but not current resources:\n", aClassifier.GetC1minusC2()); + aClassifier.TraceResourceIdVector( + "current but not requested resources:\n", aClassifier.GetC2minusC1()); + aClassifier.TraceResourceIdVector( + "requested and current resources:\n", aClassifier.GetC1andC2()); +#endif + + // Request the deactivation of resources that are not requested in the + // new configuration. + const ConfigurationClassifier::ResourceIdVector& rResourcesToDeactivate ( + aClassifier.GetC2minusC1()); + for (const auto& rxResource : rResourcesToDeactivate) + { + requestResourceDeactivation(rxResource); + } + + // Request the activation of resources that are requested in the + // new configuration but are not part of the current configuration. + const ConfigurationClassifier::ResourceIdVector& rResourcesToActivate ( + aClassifier.GetC1minusC2()); + for (const auto& rxResource : rResourcesToActivate) + { + requestResourceActivation(rxResource, ResourceActivationMode_ADD); + } + + pLock.reset(); +} + +//----- XResourceFactoryManager ----------------------------------------------- + +void SAL_CALL ConfigurationController::addResourceFactory( + const OUString& sResourceURL, + const Reference& rxResourceFactory) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + mpImplementation->mpResourceFactoryContainer->AddFactory(sResourceURL, rxResourceFactory); +} + +void SAL_CALL ConfigurationController::removeResourceFactoryForURL( + const OUString& sResourceURL) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + mpImplementation->mpResourceFactoryContainer->RemoveFactoryForURL(sResourceURL); +} + +void SAL_CALL ConfigurationController::removeResourceFactoryForReference( + const Reference& rxResourceFactory) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + mpImplementation->mpResourceFactoryContainer->RemoveFactoryForReference(rxResourceFactory); +} + +Reference SAL_CALL ConfigurationController::getResourceFactory ( + const OUString& sResourceURL) +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + return mpImplementation->mpResourceFactoryContainer->GetFactory(sResourceURL); +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL ConfigurationController::initialize (const Sequence& aArguments) +{ + ::osl::MutexGuard aGuard (m_aMutex); + + if (aArguments.getLength() == 1) + { + const SolarMutexGuard aSolarGuard; + + mpImplementation.reset(new Implementation( + *this, + Reference(aArguments[0], UNO_QUERY_THROW))); + } +} + +void ConfigurationController::ThrowIfDisposed () const +{ + if (mbIsDisposed) + { + throw lang::DisposedException ("ConfigurationController object has already been disposed", + const_cast(static_cast(this))); + } + + if (mpImplementation == nullptr) + { + OSL_ASSERT(mpImplementation != nullptr); + throw RuntimeException("ConfigurationController not initialized", + const_cast(static_cast(this))); + } +} + +//===== ConfigurationController::Implementation =============================== + +ConfigurationController::Implementation::Implementation ( + ConfigurationController& rController, + const Reference& rxController) + : mxControllerManager(rxController, UNO_QUERY_THROW), + mpBroadcaster(std::make_shared(&rController)), + mxRequestedConfiguration(new Configuration(&rController, true)), + mpResourceFactoryContainer(std::make_shared(mxControllerManager)), + mpResourceManager( + std::make_shared(mpResourceFactoryContainer,mpBroadcaster)), + mpConfigurationUpdater( + std::make_shared(mpBroadcaster, mpResourceManager,mxControllerManager)), + mpQueueProcessor(new ChangeRequestQueueProcessor(mpConfigurationUpdater)), + mnLockCount(0) +{ + mpQueueProcessor->SetConfiguration(mxRequestedConfiguration); +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_configuration_ConfigurationController_get_implementation( + css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::framework::ConfigurationController()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx new file mode 100644 index 000000000..5d9f22255 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.cxx @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ConfigurationControllerBroadcaster.hxx" +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +ConfigurationControllerBroadcaster::ConfigurationControllerBroadcaster ( + const Reference& rxController) + : mxConfigurationController(rxController) +{ +} + +void ConfigurationControllerBroadcaster::AddListener( + const Reference& rxListener, + const OUString& rsEventType, + const Any& rUserData) +{ + if ( ! rxListener.is()) + throw lang::IllegalArgumentException("invalid listener", + mxConfigurationController, + 0); + + maListenerMap.try_emplace(rsEventType); + + ListenerDescriptor aDescriptor; + aDescriptor.mxListener = rxListener; + aDescriptor.maUserData = rUserData; + maListenerMap[rsEventType].push_back(aDescriptor); +} + +void ConfigurationControllerBroadcaster::RemoveListener( + const Reference& rxListener) +{ + if ( ! rxListener.is()) + throw lang::IllegalArgumentException("invalid listener", + mxConfigurationController, + 0); + + ListenerList::iterator iList; + for (auto& rMap : maListenerMap) + { + iList = std::find_if(rMap.second.begin(), rMap.second.end(), + [&rxListener](const ListenerDescriptor& rList) { return rList.mxListener == rxListener; }); + if (iList != rMap.second.end()) + rMap.second.erase(iList); + } +} + +void ConfigurationControllerBroadcaster::NotifyListeners ( + const ListenerList& rList, + const ConfigurationChangeEvent& rEvent) +{ + // Create a local copy of the event in which the user data is modified + // for every listener. + ConfigurationChangeEvent aEvent (rEvent); + + for (const auto& rListener : rList) + { + try + { + aEvent.UserData = rListener.maUserData; + rListener.mxListener->notifyConfigurationChange(aEvent); + } + catch (const lang::DisposedException& rException) + { + // When the exception comes from the listener itself then + // unregister it. + if (rException.Context == rListener.mxListener) + RemoveListener(rListener.mxListener); + } + catch (const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } +} + +void ConfigurationControllerBroadcaster::NotifyListeners (const ConfigurationChangeEvent& rEvent) +{ + // Notify the specialized listeners. + ListenerMap::const_iterator iMap (maListenerMap.find(rEvent.Type)); + if (iMap != maListenerMap.end()) + { + // Create a local list of the listeners to avoid problems with + // concurrent changes and to be able to remove disposed listeners. + ListenerList aList (iMap->second.begin(), iMap->second.end()); + NotifyListeners(aList,rEvent); + } + + // Notify the universal listeners. + iMap = maListenerMap.find(OUString()); + if (iMap != maListenerMap.end()) + { + // Create a local list of the listeners to avoid problems with + // concurrent changes and to be able to remove disposed listeners. + ListenerList aList (iMap->second.begin(), iMap->second.end()); + NotifyListeners(aList,rEvent); + } +} + +void ConfigurationControllerBroadcaster::NotifyListeners ( + const OUString& rsEventType, + const Reference& rxResourceId, + const Reference& rxResourceObject) +{ + ConfigurationChangeEvent aEvent; + aEvent.Type = rsEventType; + aEvent.ResourceId = rxResourceId; + aEvent.ResourceObject = rxResourceObject; + try + { + NotifyListeners(aEvent); + } + catch (const lang::DisposedException&) + { + } +} + +void ConfigurationControllerBroadcaster::DisposeAndClear() +{ + lang::EventObject aEvent; + aEvent.Source = mxConfigurationController; + while (!maListenerMap.empty()) + { + ListenerMap::iterator iMap (maListenerMap.begin()); + if (iMap == maListenerMap.end()) + break; + + // When the first vector is empty then remove it from the map. + if (iMap->second.empty()) + { + maListenerMap.erase(iMap); + continue; + } + else + { + Reference xListener ( + iMap->second.front().mxListener ); + if (xListener.is()) + { + // Tell the listener that the configuration controller is + // being disposed and remove the listener (for all event + // types). + try + { + RemoveListener(xListener); + xListener->disposing(aEvent); + } + catch (const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } + else + { + // Remove just this reference to the listener. + iMap->second.erase(iMap->second.begin()); + } + } + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx new file mode 100644 index 000000000..5dfd6843d --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationControllerBroadcaster.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + +namespace com::sun::star::drawing::framework { class XConfigurationChangeListener; } +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XResource; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; } + +namespace sd::framework { + +/** This class manages the set of XConfigurationChangeListeners and + calls them when the ConfigurationController wants to broadcast an + event. + + For every registered combination of listener and event type a user data + object is stored. This user data object is then given to the listener + whenever it is called for an event. With this the listener can use + a switch statement to handle different event types. +*/ +class ConfigurationControllerBroadcaster +{ +public: + /** The given controller is used as origin of thrown exceptions. + */ + explicit ConfigurationControllerBroadcaster ( + const css::uno::Reference< + css::drawing::framework::XConfigurationController>& rxController); + + /** Add a listener for one type of event. When one listener is + interested in more than one event type this method has to be called + once for every event type. Alternatively it can register as + universal listener that will be called for all event types. + @param rxListener + A valid reference to a listener. + @param rsEventType + The type of event that the listener will be called for. The + empty string is a special value in that the listener will be + called for all event types. + @param rUserData + This object is passed to the listener whenever it is called for + the specified event type. For different event types different + user data objects can be provided. + @throws IllegalArgumentException + when an empty listener reference is given. + */ + void AddListener( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeListener>& rxListener, + const OUString& rsEventType, + const css::uno::Any& rUserData); + + /** Remove all references to the given listener. When one listener has + been registered for more than one type of event then it is removed + for all of them. + @param rxListener + A valid reference to a listener. + @throws IllegalArgumentException + when an empty listener reference is given. + */ + void RemoveListener( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeListener>& rxListener); + + /** Broadcast the given event to all listeners that have been registered + for its type of event as well as all universal listeners. + + When calling a listener results in a DisposedException being thrown + the listener is unregistered automatically. + */ + void NotifyListeners ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent); + + /** This convenience variant of NotifyListeners create the event from + the given arguments. + */ + void NotifyListeners ( + const OUString& rsEventType, + const css::uno::Reference& rxResourceId, + const css::uno::Reference& rxResourceObject); + + /** Call all listeners and inform them that the + ConfigurationController is being disposed. When this method returns + the list of registered listeners is empty. Further calls to + RemoveListener() are not necessary but do not result in an error. + */ + void DisposeAndClear(); + +private: + css::uno::Reference mxConfigurationController; + class ListenerDescriptor + { + public: + css::uno::Reference mxListener; + css::uno::Any maUserData; + }; + typedef std::vector ListenerList; + typedef std::unordered_map + ListenerMap; + ListenerMap maListenerMap; + + /** Broadcast the given event to all listeners in the given list. + + When calling a listener results in a DisposedException being thrown + the listener is unregistered automatically. + */ + void NotifyListeners ( + const ListenerList& rList, + const css::drawing::framework::ConfigurationChangeEvent& rEvent); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx new file mode 100644 index 000000000..904011d7d --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.cxx @@ -0,0 +1,303 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ConfigurationControllerResourceManager.hxx" +#include "ConfigurationControllerBroadcaster.hxx" +#include "ResourceFactoryManager.hxx" +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +//===== ConfigurationControllerResourceManager ================================ + +ConfigurationControllerResourceManager::ConfigurationControllerResourceManager ( + const std::shared_ptr& rpResourceFactoryContainer, + const std::shared_ptr& rpBroadcaster) + : maResourceMap(ResourceComparator()), + mpResourceFactoryContainer(rpResourceFactoryContainer), + mpBroadcaster(rpBroadcaster) +{ +} + +ConfigurationControllerResourceManager::~ConfigurationControllerResourceManager() +{ +} + +ConfigurationControllerResourceManager::ResourceDescriptor + ConfigurationControllerResourceManager::GetResource ( + const Reference& rxResourceId) +{ + ::osl::MutexGuard aGuard (maMutex); + ResourceMap::const_iterator iResource (maResourceMap.find(rxResourceId)); + if (iResource != maResourceMap.end()) + return iResource->second; + else + return ResourceDescriptor(); +} + +void ConfigurationControllerResourceManager::ActivateResources ( + const ::std::vector >& rResources, + const Reference& rxConfiguration) +{ + ::osl::MutexGuard aGuard (maMutex); + // Iterate in normal order over the resources that are to be + // activated so that resources on which others depend are activated + // before the depending resources are activated. + for (const Reference& xResource : rResources) + ActivateResource(xResource, rxConfiguration); +} + +void ConfigurationControllerResourceManager::DeactivateResources ( + const ::std::vector >& rResources, + const Reference& rxConfiguration) +{ + ::osl::MutexGuard aGuard (maMutex); + // Iterate in reverse order over the resources that are to be + // deactivated so that resources on which others depend are deactivated + // only when the depending resources have already been deactivated. + ::std::for_each( + rResources.rbegin(), + rResources.rend(), + [&] (Reference const& xResource) { + return DeactivateResource(xResource, rxConfiguration); + } ); +} + +/* In this method we do following steps. + 1. Get the factory with which the resource will be created. + 2. Create the resource. + 3. Add the resource to the URL->Object map of the configuration + controller. + 4. Add the resource id to the current configuration. + 5. Notify listeners. +*/ +void ConfigurationControllerResourceManager::ActivateResource ( + const Reference& rxResourceId, + const Reference& rxConfiguration) +{ + if ( ! rxResourceId.is()) + { + OSL_ASSERT(rxResourceId.is()); + return; + } + + SAL_INFO("sd.fwk", __func__ << ": activating resource " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + + // 1. Get the factory. + const OUString sResourceURL (rxResourceId->getResourceURL()); + Reference xFactory (mpResourceFactoryContainer->GetFactory(sResourceURL)); + if ( ! xFactory.is()) + { + SAL_INFO("sd.fwk", __func__ << ": no factory found for " << sResourceURL); + return; + } + + try + { + // 2. Create the resource. + Reference xResource; + try + { + xResource = xFactory->createResource(rxResourceId); + } + catch (lang::DisposedException&) + { + // The factory is disposed and can be removed from the list + // of registered factories. + mpResourceFactoryContainer->RemoveFactoryForReference(xFactory); + } + catch (Exception&) {} + + if (xResource.is()) + { + SAL_INFO("sd.fwk", __func__ << ": successfully created"); + // 3. Add resource to URL->Object map. + AddResource(xResource, xFactory); + + // 4. Add resource id to current configuration. + rxConfiguration->addResource(rxResourceId); + + // 5. Notify the new resource to listeners of the ConfigurationController. + mpBroadcaster->NotifyListeners( + FrameworkHelper::msResourceActivationEvent, + rxResourceId, + xResource); + } + else + { + SAL_INFO("sd.fwk", __func__ << ": resource creation failed"); + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +/* In this method we do following steps. + 1. Remove the resource from the URL->Object map of the configuration + controller. + 2. Notify listeners that deactivation has started. + 3. Remove the resource id from the current configuration. + 4. Release the resource. + 5. Notify listeners about that deactivation is completed. +*/ +void ConfigurationControllerResourceManager::DeactivateResource ( + const Reference& rxResourceId, + const Reference& rxConfiguration) +{ + if ( ! rxResourceId.is()) + return; + +#if OSL_DEBUG_LEVEL >= 1 + bool bSuccess (false); +#endif + try + { + // 1. Remove resource from URL->Object map. + ResourceDescriptor aDescriptor (RemoveResource(rxResourceId)); + + if (aDescriptor.mxResource.is() && aDescriptor.mxResourceFactory.is()) + { + // 2. Notify listeners that the resource is being deactivated. + mpBroadcaster->NotifyListeners( + FrameworkHelper::msResourceDeactivationEvent, + rxResourceId, + aDescriptor.mxResource); + + // 3. Remove resource id from current configuration. + rxConfiguration->removeResource(rxResourceId); + + // 4. Release the resource. + try + { + aDescriptor.mxResourceFactory->releaseResource(aDescriptor.mxResource); + } + catch (const lang::DisposedException& rException) + { + if ( ! rException.Context.is() + || rException.Context == aDescriptor.mxResourceFactory) + { + // The factory is disposed and can be removed from the + // list of registered factories. + mpResourceFactoryContainer->RemoveFactoryForReference( + aDescriptor.mxResourceFactory); + } + } + +#if OSL_DEBUG_LEVEL >= 1 + bSuccess = true; +#endif + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + + // 5. Notify listeners that the resource is being deactivated. + mpBroadcaster->NotifyListeners( + FrameworkHelper::msResourceDeactivationEndEvent, + rxResourceId, + nullptr); + +#if OSL_DEBUG_LEVEL >= 1 + if (bSuccess) + SAL_INFO("sd.fwk", __func__ << ": successfully deactivated " << + FrameworkHelper::ResourceIdToString(rxResourceId)); + else + SAL_INFO("sd.fwk", __func__ << ": activating resource " << + FrameworkHelper::ResourceIdToString(rxResourceId) + << " failed"); +#endif +} + +void ConfigurationControllerResourceManager::AddResource ( + const Reference& rxResource, + const Reference& rxFactory) +{ + if ( ! rxResource.is()) + { + OSL_ASSERT(rxResource.is()); + return; + } + + // Add the resource to the resource container. + ResourceDescriptor aDescriptor; + aDescriptor.mxResource = rxResource; + aDescriptor.mxResourceFactory = rxFactory; + maResourceMap[rxResource->getResourceId()] = aDescriptor; + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.fwk", __func__ << ": ConfigurationControllerResourceManager::AddResource(): added " << + FrameworkHelper::ResourceIdToString(rxResource->getResourceId()) << + " -> " << rxResource.get()); +#endif +} + +ConfigurationControllerResourceManager::ResourceDescriptor + ConfigurationControllerResourceManager::RemoveResource ( + const Reference& rxResourceId) +{ + ResourceDescriptor aDescriptor; + + ResourceMap::iterator iResource (maResourceMap.find(rxResourceId)); + if (iResource != maResourceMap.end()) + { +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.fwk", __func__ << ": ConfigurationControllerResourceManager::RemoveResource(): removing " << + FrameworkHelper::ResourceIdToString(rxResourceId) << + " -> " << &iResource); +#endif + + aDescriptor = iResource->second; + maResourceMap.erase(rxResourceId); + } + + return aDescriptor; +} + +//===== ConfigurationControllerResourceManager::ResourceComparator ============ + +bool ConfigurationControllerResourceManager::ResourceComparator::operator() ( + const Reference& rxId1, + const Reference& rxId2) const +{ + if (rxId1.is() && rxId2.is()) + return rxId1->compareTo(rxId2)<0; + else if (rxId1.is()) + return true; + else + return false; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx new file mode 100644 index 000000000..f3a3d6d76 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationControllerResourceManager.hxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace com::sun::star::drawing::framework { class XConfiguration; } +namespace com::sun::star::drawing::framework { class XResourceFactory; } +namespace com::sun::star::drawing::framework { class XResource; } +namespace com::sun::star::drawing::framework { class XResourceId; } + +namespace sd::framework { + +class ConfigurationControllerBroadcaster; +class ResourceFactoryManager; + +/** Manage the set of active resources. Activate and deactivate resources. +*/ +class ConfigurationControllerResourceManager +{ +public: + /** For every active resource both the resource itself as well as its + creating factory are remembered, so that on deactivation, the + resource can be deactivated by this factory. + */ + class ResourceDescriptor + { + public: + css::uno::Reference mxResource; + css::uno::Reference mxResourceFactory; + }; + + /** A new ResourceManager object is created with the resource factory + container for creating resources and the event broadcaster for + notifying ConfigurationChangeListeners of activated or deactivated + resources. + */ + ConfigurationControllerResourceManager ( + const std::shared_ptr& rpResourceFactoryContainer, + const std::shared_ptr& rpBroadcaster); + + ~ConfigurationControllerResourceManager(); + + /// Forbid copy construction and copy assignment + ConfigurationControllerResourceManager(const ConfigurationControllerResourceManager&) = delete; + ConfigurationControllerResourceManager& operator=(const ConfigurationControllerResourceManager&) = delete; + + /** Activate all the resources that are specified by resource ids in + rResources. The resource ids of activated resources are added to + the given configuration. Activated resources are notified to all + interested ConfigurationChangeListeners. + */ + void ActivateResources ( + const ::std::vector< + css::uno::Reference >& rResources, + const css::uno::Reference& rxConfiguration); + + /** Deactivate all the resources that are specified by resource ids in + rResources. The resource ids of deactivated resources are removed + from the given configuration. Activated resources are notified to all + interested ConfigurationChangeListeners. + */ + void DeactivateResources ( + const ::std::vector< + css::uno::Reference >& rResources, + const css::uno::Reference& rxConfiguration); + + /** Return the descriptor for the specified resource. + @return + When there is no active resource for the given resource id then + an empty descriptor is returned. + */ + ResourceDescriptor GetResource ( + const css::uno::Reference& rxResourceId); + +private: + osl::Mutex maMutex; + + class ResourceComparator + { + public: + bool operator() ( + const css::uno::Reference& rxId1, + const css::uno::Reference& rxId2) const; + }; + + typedef ::std::map< + css::uno::Reference, + ResourceDescriptor, + ResourceComparator> ResourceMap; + ResourceMap maResourceMap; + + std::shared_ptr mpResourceFactoryContainer; + + /** This broadcaster is used to notify the activation and deactivation + of resources. + */ + std::shared_ptr mpBroadcaster; + + void ActivateResource ( + const css::uno::Reference& rxResourceId, + const css::uno::Reference& rxConfiguration); + + void DeactivateResource ( + const css::uno::Reference& rxResourceId, + const css::uno::Reference& rxConfiguration); + + void AddResource ( + const css::uno::Reference& rxResource, + const css::uno::Reference& rxFactory); + + ResourceDescriptor RemoveResource ( + const css::uno::Reference& rxResourceId); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationTracer.cxx b/sd/source/ui/framework/configuration/ConfigurationTracer.cxx new file mode 100644 index 000000000..00ddd5ff1 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationTracer.cxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ConfigurationTracer.hxx" + +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +void ConfigurationTracer::TraceConfiguration ( + const Reference& rxConfiguration, + const char* pMessage) +{ +#if OSL_DEBUG_LEVEL >=1 + SAL_INFO("sd.ui","" << pMessage << " at " << rxConfiguration.get() << " {"); + if (rxConfiguration.is()) + { + TraceBoundResources(rxConfiguration, nullptr, 0); + } + else + { + SAL_INFO("sd.ui"," empty"); + } + SAL_INFO("sd.ui","}"); +#else + (void)rxConfiguration; + (void)pMessage; +#endif +} + +#if OSL_DEBUG_LEVEL >=1 +void ConfigurationTracer::TraceBoundResources ( + const Reference& rxConfiguration, + const Reference& rxResourceId, + const int nIndentation) +{ + const Sequence > aResourceList ( + rxConfiguration->getResources(rxResourceId, OUString(), AnchorBindingMode_DIRECT)); + static const OUStringLiteral sIndentation (u" "); + for (Reference const & resourceId : aResourceList) + { + OUString sLine (resourceId->getResourceURL()); + for (int i=0; i + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} +namespace com::sun::star::uno +{ +template class Reference; +} + +namespace sd::framework +{ +/** Print debug information about configurations to the standard error + output channel. +*/ +class ConfigurationTracer +{ +public: + static void TraceConfiguration( + const css::uno::Reference& rxConfiguration, + const char* pMessage); +#if OSL_DEBUG_LEVEL >= 1 + static void TraceBoundResources( + const css::uno::Reference& rxConfiguration, + const css::uno::Reference& rxResourceId, + const int nIndentation); +#endif +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx b/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx new file mode 100644 index 000000000..96ac74186 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationUpdater.cxx @@ -0,0 +1,376 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ConfigurationUpdater.hxx" +#include "ConfigurationTracer.hxx" +#include "ConfigurationClassifier.hxx" +#include "ConfigurationControllerBroadcaster.hxx" +#include "ConfigurationControllerResourceManager.hxx" +#include +#include + +#include +#include +#include +#include + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; +using ::std::vector; + +namespace { +const sal_Int32 snShortTimeout (100); +const sal_Int32 snNormalTimeout (1000); +const sal_Int32 snLongTimeout (10000); +const sal_Int32 snShortTimeoutCountThreshold (1); +const sal_Int32 snNormalTimeoutCountThreshold (5); +} + +namespace sd::framework { + +//===== ConfigurationUpdaterLock ============================================== + +class ConfigurationUpdaterLock +{ +public: + explicit ConfigurationUpdaterLock (ConfigurationUpdater& rUpdater) + : mrUpdater(rUpdater) { mrUpdater.LockUpdates(); } + ~ConfigurationUpdaterLock() { mrUpdater.UnlockUpdates(); } +private: + ConfigurationUpdater& mrUpdater; +}; + +//===== ConfigurationUpdater ================================================== + +ConfigurationUpdater::ConfigurationUpdater ( + const std::shared_ptr& rpBroadcaster, + const std::shared_ptr& rpResourceManager, + const Reference& rxControllerManager) + : mpBroadcaster(rpBroadcaster), + mxCurrentConfiguration(Reference(new Configuration(nullptr, false))), + mbUpdatePending(false), + mbUpdateBeingProcessed(false), + mnLockCount(0), + maUpdateTimer("sd::ConfigurationUpdater maUpdateTimer"), + mnFailedUpdateCount(0), + mpResourceManager(rpResourceManager) +{ + // Prepare the timer that is started when after an update the current + // and the requested configuration differ. With the timer we try + // updates until the two configurations are the same. + maUpdateTimer.SetTimeout(snNormalTimeout); + maUpdateTimer.SetInvokeHandler(LINK(this,ConfigurationUpdater,TimeoutHandler)); + mxControllerManager = rxControllerManager; +} + +ConfigurationUpdater::~ConfigurationUpdater() +{ + maUpdateTimer.Stop(); +} + +void ConfigurationUpdater::RequestUpdate ( + const Reference& rxRequestedConfiguration) +{ + mxRequestedConfiguration = rxRequestedConfiguration; + + // Find out whether we really can update the configuration. + if (IsUpdatePossible()) + { + SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration start"); + + // Call UpdateConfiguration while that is possible and while someone + // set mbUpdatePending to true in the middle of it. + do + { + UpdateConfiguration(); + } + while (mbUpdatePending && IsUpdatePossible()); + } + else + { + mbUpdatePending = true; + SAL_INFO("sd.fwk", __func__ << ": scheduling update for later"); + } +} + +bool ConfigurationUpdater::IsUpdatePossible() const +{ + return ! mbUpdateBeingProcessed + && mxControllerManager.is() + && mnLockCount==0 + && mxRequestedConfiguration.is() + && mxCurrentConfiguration.is(); +} + +void ConfigurationUpdater::UpdateConfiguration() +{ + SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration update"); + SetUpdateBeingProcessed(true); + comphelper::ScopeGuard aScopeGuard ( + [this] () { return this->SetUpdateBeingProcessed(false); }); + + try + { + mbUpdatePending = false; + + CleanRequestedConfiguration(); + ConfigurationClassifier aClassifier(mxRequestedConfiguration, mxCurrentConfiguration); + if (aClassifier.Partition()) + { +#if DEBUG_SD_CONFIGURATION_TRACE + SAL_INFO("sd.fwk", __func__ << ": ConfigurationUpdater::UpdateConfiguration("); + ConfigurationTracer::TraceConfiguration( + mxRequestedConfiguration, "requested configuration"); + ConfigurationTracer::TraceConfiguration( + mxCurrentConfiguration, "current configuration"); +#endif + // Notify the beginning of the update. + ConfigurationChangeEvent aEvent; + aEvent.Type = FrameworkHelper::msConfigurationUpdateStartEvent; + aEvent.Configuration = mxRequestedConfiguration; + mpBroadcaster->NotifyListeners(aEvent); + + // Do the actual update. All exceptions are caught and ignored, + // so that the end of the update is notified always. + try + { + if (mnLockCount == 0) + UpdateCore(aClassifier); + } + catch(const RuntimeException&) + { + } + + // Notify the end of the update. + aEvent.Type = FrameworkHelper::msConfigurationUpdateEndEvent; + mpBroadcaster->NotifyListeners(aEvent); + + CheckUpdateSuccess(); + } + else + { + SAL_INFO("sd.fwk", __func__ << ": nothing to do"); +#if DEBUG_SD_CONFIGURATION_TRACE + ConfigurationTracer::TraceConfiguration( + mxRequestedConfiguration, "requested configuration"); + ConfigurationTracer::TraceConfiguration( + mxCurrentConfiguration, "current configuration"); +#endif + } + } + catch(const RuntimeException &) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + + SAL_INFO("sd.fwk", __func__ << ": ConfigurationUpdater::UpdateConfiguration)"); + SAL_INFO("sd.fwk", __func__ << ": UpdateConfiguration end"); +} + +void ConfigurationUpdater::CleanRequestedConfiguration() +{ + if (!mxControllerManager.is()) + return; + + // Request the deactivation of pure anchors that have no child. + vector > aResourcesToDeactivate; + CheckPureAnchors(mxRequestedConfiguration, aResourcesToDeactivate); + if (!aResourcesToDeactivate.empty()) + { + Reference xCC ( + mxControllerManager->getConfigurationController()); + for (const auto& rxId : aResourcesToDeactivate) + if (rxId.is()) + xCC->requestResourceDeactivation(rxId); + } +} + +void ConfigurationUpdater::CheckUpdateSuccess() +{ + // When the two configurations differ then start the timer to call + // another update later. + if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration)) + { + if (mnFailedUpdateCount <= snShortTimeoutCountThreshold) + maUpdateTimer.SetTimeout(snShortTimeout); + else if (mnFailedUpdateCount < snNormalTimeoutCountThreshold) + maUpdateTimer.SetTimeout(snNormalTimeout); + else + maUpdateTimer.SetTimeout(snLongTimeout); + ++mnFailedUpdateCount; + maUpdateTimer.Start(); + } + else + { + // Update was successful. Reset the failed update count. + mnFailedUpdateCount = 0; + } +} + +void ConfigurationUpdater::UpdateCore (const ConfigurationClassifier& rClassifier) +{ + try + { +#if DEBUG_SD_CONFIGURATION_TRACE + rClassifier.TraceResourceIdVector( + "requested but not current resources:", rClassifier.GetC1minusC2()); + rClassifier.TraceResourceIdVector( + "current but not requested resources:", rClassifier.GetC2minusC1()); + rClassifier.TraceResourceIdVector( + "requested and current resources:", rClassifier.GetC1andC2()); +#endif + + // Updating of the sub controllers is done in two steps. In the + // first the sub controllers typically shut down resources that are + // not requested anymore. In the second the sub controllers + // typically set up resources that have been newly requested. + mpResourceManager->DeactivateResources(rClassifier.GetC2minusC1(), mxCurrentConfiguration); + mpResourceManager->ActivateResources(rClassifier.GetC1minusC2(), mxCurrentConfiguration); + +#if DEBUG_SD_CONFIGURATION_TRACE + SAL_INFO("sd.fwk", __func__ << ": ConfigurationController::UpdateConfiguration)"); + ConfigurationTracer::TraceConfiguration( + mxRequestedConfiguration, "requested configuration"); + ConfigurationTracer::TraceConfiguration( + mxCurrentConfiguration, "current configuration"); +#endif + + // Deactivate pure anchors that have no child. + vector > aResourcesToDeactivate; + CheckPureAnchors(mxCurrentConfiguration, aResourcesToDeactivate); + if (!aResourcesToDeactivate.empty()) + mpResourceManager->DeactivateResources(aResourcesToDeactivate, mxCurrentConfiguration); + } + catch(const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void ConfigurationUpdater::CheckPureAnchors ( + const Reference& rxConfiguration, + vector >& rResourcesToDeactivate) +{ + if ( ! rxConfiguration.is()) + return; + + // Get a list of all resources in the configuration. + Sequence > aResources( + rxConfiguration->getResources( + nullptr, OUString(), AnchorBindingMode_INDIRECT)); + auto aResourcesRange = asNonConstRange(aResources); + sal_Int32 nCount (aResources.getLength()); + + // Prepare the list of pure anchors that have to be deactivated. + rResourcesToDeactivate.clear(); + + // Iterate over the list in reverse order because when there is a chain + // of pure anchors with only the last one having no child then the whole + // list has to be deactivated. + sal_Int32 nIndex (nCount-1); + while (nIndex >= 0) + { + const Reference xResourceId (aResources[nIndex]); + const Reference xResource ( + mpResourceManager->GetResource(xResourceId).mxResource); + bool bDeactiveCurrentResource (false); + + // Skip all resources that are no pure anchors. + if (xResource.is() && xResource->isAnchorOnly()) + { + // When xResource is not an anchor of the next resource in + // the list then it is the anchor of no resource at all. + if (nIndex == nCount-1) + { + // No following anchors, deactivate this one, then remove it + // from the list. + bDeactiveCurrentResource = true; + } + else + { + const Reference xPrevResourceId (aResources[nIndex+1]); + if ( ! xPrevResourceId.is() + || ! xPrevResourceId->isBoundTo(xResourceId, AnchorBindingMode_DIRECT)) + { + // The previous resource (id) does not exist or is not bound to + // the current anchor. + bDeactiveCurrentResource = true; + } + } + } + + if (bDeactiveCurrentResource) + { + SAL_INFO("sd.fwk", __func__ << ": deactivating pure anchor " << + FrameworkHelper::ResourceIdToString(xResourceId) << + "because it has no children"); + // Erase element from current configuration. + for (sal_Int32 nI=nIndex; nI ConfigurationUpdater::GetLock() +{ + return std::make_shared(*this); +} + +void ConfigurationUpdater::SetUpdateBeingProcessed (bool bValue) +{ + mbUpdateBeingProcessed = bValue; +} + +IMPL_LINK_NOARG(ConfigurationUpdater, TimeoutHandler, Timer *, void) +{ + if ( ! mbUpdateBeingProcessed + && mxCurrentConfiguration.is() + && mxRequestedConfiguration.is()) + { + if ( ! AreConfigurationsEquivalent(mxCurrentConfiguration, mxRequestedConfiguration)) + { + RequestUpdate(mxRequestedConfiguration); + } + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx b/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx new file mode 100644 index 000000000..9fba364b1 --- /dev/null +++ b/sd/source/ui/framework/configuration/ConfigurationUpdater.hxx @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XControllerManager; +} +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} + +namespace sd::framework +{ +class ConfigurationClassifier; +class ConfigurationUpdaterLock; +class ConfigurationControllerResourceManager; +class ConfigurationControllerBroadcaster; + +/** This is a helper class for the ConfigurationController. It handles the + update of the current configuration so that it looks like a requested + configuration. An update is made by activating or deactivating drawing + framework resources. + + When an update is not successful, i.e. after the update the current + configuration is not equivalent to the requested configuration, then a + timer is started to repeat the update after a short time. +*/ +class ConfigurationUpdater +{ +public: + /** Create a new ConfigurationUpdater object that notifies configuration + changes and the start and end of updates via the given broadcaster. + */ + ConfigurationUpdater( + const std::shared_ptr& rpBroadcaster, + const std::shared_ptr& rpResourceManager, + const css::uno::Reference& + rxControllerManager); + ~ConfigurationUpdater(); + + /** Request an update of the current configuration so that it looks like + the given requested configuration. It checks whether an update of + the current configuration can be done. Calls UpdateConfiguration() + if that is the case. Otherwise it schedules a later call to + UpdateConfiguration(). + */ + void RequestUpdate(const css::uno::Reference& + rxRequestedConfiguration); + + const css::uno::Reference& + GetCurrentConfiguration() const + { + return mxCurrentConfiguration; + } + + friend class ConfigurationUpdaterLock; + /** Return a lock of the called ConfigurationUpdater. While the + returned object exists no update of the current configuration is + made. + */ + std::shared_ptr GetLock(); + +private: + /** A reference to the XControllerManager is kept so that + UpdateConfiguration() has access to the other sub controllers. + */ + css::uno::Reference mxControllerManager; + + std::shared_ptr mpBroadcaster; + + /** The current configuration holds the resources that are currently + active. It is modified during an update. + */ + css::uno::Reference mxCurrentConfiguration; + + /** The requested configuration holds the resources that have been + requested to activate or to deactivate since the last update. It is + (usually) not modified during an update. This configuration is + maintained by the ConfigurationController and given to the + ConfigurationUpdater in the RequestUpdate() method. + */ + css::uno::Reference mxRequestedConfiguration; + + /** This flag is set to when an update of the current + configuration was requested (because the last request in the queue + was processed) but could not be executed because the + ConfigurationController was locked. A call to UpdateConfiguration() + resets the flag to . + */ + bool mbUpdatePending; + + /** This flag is set to while the UpdateConfiguration() method + is running. It is used to prevent reentrance problems with this + method. + */ + bool mbUpdateBeingProcessed; + + /** The ConfigurationController is locked when this count has a value + larger then zero. If the controller is locked then updates of the + current configuration are not made. + */ + sal_Int32 mnLockCount; + + /** This timer is used to check from time to time whether the requested + configuration and the current configuration are identical and request + an update when they are not. + This is used to overcome problems with resources that become + available asynchronously. + */ + Timer maUpdateTimer; + + /** The number of failed updates (those after which the current + configuration is not equivalent to the requested configuration) is + used to determine how long to wait before another update is made. + */ + sal_Int32 mnFailedUpdateCount; + + std::shared_ptr mpResourceManager; + + /** This method does the main work of an update. It calls the sub + controllers that are responsible for the various types of resources + and tells them to update their active resources. It notifies + listeners about the start and end of the configuration update. + */ + void UpdateConfiguration(); + + /** Basically calls UpdaterStart() andUpdateEnd() and makes some debug + output. + */ + void UpdateCore(const ConfigurationClassifier& rClassifier); + + /** Check for all pure anchors if they have at least one child. + Childless pure anchors are deactivated. + This affects only the current configuration. + */ + void CheckPureAnchors( + const css::uno::Reference& rxConfiguration, + ::std::vector>& + rResourcesToDeactivate); + + /** Remove from the requested configuration all pure anchors that have no + child. Requested but not yet activated anchors can not be removed + because without the actual resource the 'pureness' of an anchor can + not be determined. + */ + void CleanRequestedConfiguration(); + + /** Check the success of a recently executed configuration update. + When the update failed then start the timer. + */ + void CheckUpdateSuccess(); + + /** This method sets the mbUpdateBeingProcessed member that is used to + prevent reentrance problems. This method allows function objects + easily and safely to modify the variable. + */ + void SetUpdateBeingProcessed(bool bValue); + + /** Return whether it is possible to do an update of the configuration. + This takes into account whether another update is currently being + executed, the lock count, and whether the configuration controller + is still valid. + */ + bool IsUpdatePossible() const; + + /** Lock updates of the current configuration. For intermediate requests + for updates mbUpdatePending is set to . + */ + void LockUpdates(); + + /** When an update was requested since the last LockUpdates() call then + RequestUpdate() is called. + */ + void UnlockUpdates(); + + DECL_LINK(TimeoutHandler, Timer*, void); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx new file mode 100644 index 000000000..fa6d41503 --- /dev/null +++ b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.cxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include "GenericConfigurationChangeRequest.hxx" + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +GenericConfigurationChangeRequest::GenericConfigurationChangeRequest ( + const Reference& rxResourceId, + const Mode eMode) + : mxResourceId(rxResourceId), + meMode(eMode) +{ + if ( ! rxResourceId.is() || rxResourceId->getResourceURL().isEmpty()) + throw css::lang::IllegalArgumentException(); +} + +GenericConfigurationChangeRequest::~GenericConfigurationChangeRequest() noexcept +{ +} + +void SAL_CALL GenericConfigurationChangeRequest::execute ( + const Reference& rxConfiguration) +{ + if (!rxConfiguration.is()) + return; + + switch (meMode) + { + case Activation: + rxConfiguration->addResource(mxResourceId); + break; + + case Deactivation: + rxConfiguration->removeResource(mxResourceId); + break; + } +} + +OUString SAL_CALL GenericConfigurationChangeRequest::getName() +{ + return OUString::Concat("GenericConfigurationChangeRequest ") + + (meMode==Activation + ? std::u16string_view(u"activate ") : std::u16string_view(u"deactivate ")) + + FrameworkHelper::ResourceIdToString(mxResourceId); +} + +void SAL_CALL GenericConfigurationChangeRequest::setName (const OUString&) +{ + // Ignored. +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx new file mode 100644 index 000000000..3caa7a8ca --- /dev/null +++ b/sd/source/ui/framework/configuration/GenericConfigurationChangeRequest.hxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::drawing::framework { class XConfiguration; } +namespace com::sun::star::drawing::framework { class XResourceId; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeRequest, + css::container::XNamed + > GenericConfigurationChangeRequestInterfaceBase; + +/** This implementation of the XConfigurationChangeRequest interface + represents a single explicit request for a configuration change. On its + execution it may result in other, implicit, configuration changes. For + example this is the case when the deactivation of a unique resource is + requested: the resources linked to it have to be deactivated as well. +*/ +class GenericConfigurationChangeRequest final + : public GenericConfigurationChangeRequestInterfaceBase +{ +public: + /** This enum specified whether the activation or deactivation of a + resource is requested. + */ + enum Mode { Activation, Deactivation }; + + /** Create a new object that represents the request for activation or + deactivation of the specified resource. + @param rxsResourceId + Id of the resource that is to be activated or deactivated. + @param eMode + The mode specifies whether to activate or to deactivate the + resource. + @throws css::css::lang::IllegalArgumentException + */ + GenericConfigurationChangeRequest ( + const css::uno::Reference& + rxResourceId, + const Mode eMode); + + virtual ~GenericConfigurationChangeRequest() noexcept override; + + // XConfigurationChangeOperation + + /** The requested configuration change is executed on the given + configuration. Additionally to the explicitly requested change + other changes have to be made as well. See class description for an + example. + @param rxConfiguration + The configuration to which the requested change is made. + */ + virtual void SAL_CALL execute ( + const css::uno::Reference& rxConfiguration) override; + + // XNamed + + /** Return a human readable string representation. This is used for + debugging purposes. + */ + virtual OUString SAL_CALL getName() override; + + /** This call is ignored because the XNamed interface is (mis)used to + give access to a human readable name for debugging purposes. + */ + virtual void SAL_CALL setName (const OUString& rName) override; + +private: + const css::uno::Reference mxResourceId; + const Mode meMode; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx b/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx new file mode 100644 index 000000000..4817c1360 --- /dev/null +++ b/sd/source/ui/framework/configuration/ResourceFactoryManager.cxx @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ResourceFactoryManager.hxx" +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +#undef VERBOSE +//#define VERBOSE 1 + +namespace sd::framework { + +ResourceFactoryManager::ResourceFactoryManager (const Reference& rxManager) + : mxControllerManager(rxManager) +{ + // Create the URL transformer. + Reference xContext(::comphelper::getProcessComponentContext()); + mxURLTransformer = util::URLTransformer::create(xContext); +} + +ResourceFactoryManager::~ResourceFactoryManager() +{ + for (auto& rXInterfaceResource : maFactoryMap) + { + Reference xComponent (rXInterfaceResource.second, UNO_QUERY); + rXInterfaceResource.second = nullptr; + if (xComponent.is()) + xComponent->dispose(); + } + + Reference xComponent (mxURLTransformer, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); +} + +void ResourceFactoryManager::AddFactory ( + const OUString& rsURL, + const Reference& rxFactory) +{ + if ( ! rxFactory.is()) + throw lang::IllegalArgumentException(); + if (rsURL.isEmpty()) + throw lang::IllegalArgumentException(); + + std::scoped_lock aGuard (maMutex); + + if (rsURL.indexOf('*') >= 0 || rsURL.indexOf('?') >= 0) + { + // The URL is a URL pattern not a single URL. + maFactoryPatternList.emplace_back(rsURL, rxFactory); + +#if defined VERBOSE && VERBOSE>=1 + SAL_INFO("sd","ResourceFactoryManager::AddFactory pattern " << rsURL << std::hex << rxFactory.get()); +#endif + } + else + { + maFactoryMap[rsURL] = rxFactory; + +#if defined VERBOSE && VERBOSE>=1 + SAL_INFO("sd", "ResourceFactoryManager::AddFactory fixed " << rsURL << " 0x" << std::hex << rxFactory.get()); +#endif + } +} + +void ResourceFactoryManager::RemoveFactoryForURL ( + const OUString& rsURL) +{ + if (rsURL.isEmpty()) + throw lang::IllegalArgumentException(); + + std::scoped_lock aGuard (maMutex); + + FactoryMap::iterator iFactory (maFactoryMap.find(rsURL)); + if (iFactory != maFactoryMap.end()) + { + maFactoryMap.erase(iFactory); + } + else + { + // The URL may be a pattern. Look that up. + auto iPattern = std::find_if(maFactoryPatternList.begin(), maFactoryPatternList.end(), + [&rsURL](const FactoryPatternList::value_type& rPattern) { return rPattern.first == rsURL; }); + if (iPattern != maFactoryPatternList.end()) + { + // Found the pattern. Remove it. + maFactoryPatternList.erase(iPattern); + } + } +} + +void ResourceFactoryManager::RemoveFactoryForReference( + const Reference& rxFactory) +{ + std::scoped_lock aGuard (maMutex); + + // Collect a list with all keys that map to the given factory. + ::std::vector aKeys; + for (const auto& rFactory : maFactoryMap) + if (rFactory.second == rxFactory) + aKeys.push_back(rFactory.first); + + // Remove the entries whose keys we just have collected. + for (const auto& rKey : aKeys) + maFactoryMap.erase(rKey); + + // Remove the pattern entries whose factories are identical to the given + // factory. + maFactoryPatternList.erase( + std::remove_if( + maFactoryPatternList.begin(), + maFactoryPatternList.end(), + [&] (FactoryPatternList::value_type const& it) { return it.second == rxFactory; }), + maFactoryPatternList.end()); +} + +Reference ResourceFactoryManager::GetFactory ( + const OUString& rsCompleteURL) +{ + OUString sURLBase (rsCompleteURL); + if (mxURLTransformer.is()) + { + util::URL aURL; + aURL.Complete = rsCompleteURL; + if (mxURLTransformer->parseStrict(aURL)) + sURLBase = aURL.Main; + } + + Reference xFactory = FindFactory(sURLBase); + + if ( ! xFactory.is() && mxControllerManager.is()) + { + Reference xModuleController(mxControllerManager->getModuleController()); + if (xModuleController.is()) + { + // Ask the module controller to provide a factory of the + // requested view type. Note that this can (and should) cause + // intermediate calls to AddFactory(). + xModuleController->requestResource(sURLBase); + + xFactory = FindFactory(sURLBase); + } + } + + return xFactory; +} + +Reference ResourceFactoryManager::FindFactory (const OUString& rsURLBase) +{ + std::scoped_lock aGuard (maMutex); + FactoryMap::const_iterator iFactory (maFactoryMap.find(rsURLBase)); + if (iFactory != maFactoryMap.end()) + return iFactory->second; + else + { + // Check the URL patterns. + auto iPattern = std::find_if(maFactoryPatternList.begin(), maFactoryPatternList.end(), + [&rsURLBase](const FactoryPatternList::value_type& rPattern) { + WildCard aWildCard (rPattern.first); + return aWildCard.Matches(rsURLBase); + }); + if (iPattern != maFactoryPatternList.end()) + return iPattern->second; + } + return nullptr; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx b/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx new file mode 100644 index 000000000..61daf383b --- /dev/null +++ b/sd/source/ui/framework/configuration/ResourceFactoryManager.hxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include +#include +#include + +#include +#include + +namespace com::sun::star::drawing::framework { class XControllerManager; } +namespace com::sun::star::drawing::framework { class XResourceFactory; } +namespace com::sun::star::util { class XURLTransformer; } + +namespace sd::framework { + +/** Container of resource factories of the drawing framework. +*/ +class ResourceFactoryManager +{ +public: + explicit ResourceFactoryManager ( + const css::uno::Reference& rxManager); + + ~ResourceFactoryManager(); + + /** Register a resource factory for one type of resource. + @param rsURL + The type of the resource that will be created by the factory. + @param rxFactory + The factory that will create resource objects of the specified type. + @throws css::uno::RuntimeException + */ + void AddFactory ( + const OUString& rsURL, + const css::uno::Reference& rxFactory); + + /** Unregister the specified factory. + @param rsURL + Unregister only the factory for this URL. When the same factory + is registered for other URLs then these remain registered. + @throws css::uno::RuntimeException + */ + void RemoveFactoryForURL( + const OUString& rsURL); + + /** Unregister the specified factory. + @param rxFactory + Unregister the this factory for all URLs that it has been + registered for. + @throws css::uno::RuntimeException + */ + void RemoveFactoryForReference( + const css::uno::Reference& rxFactory); + + /** Return a factory that can create resources specified by the given URL. + @param rsCompleteURL + This URL specifies the type of the resource. It may contain arguments. + @return + When a factory for the specified URL has been registered by a + previous call to AddFactory() then a reference to that factory + is returned. Otherwise an empty reference is returned. + @throws css::uno::RuntimeException + */ + css::uno::Reference GetFactory ( + const OUString& rsURL); + +private: + std::mutex maMutex; + typedef std::unordered_map< + OUString, + css::uno::Reference > FactoryMap; + FactoryMap maFactoryMap; + + typedef ::std::vector< + ::std::pair< + OUString, + css::uno::Reference > > + FactoryPatternList; + FactoryPatternList maFactoryPatternList; + + css::uno::Reference mxControllerManager; + css::uno::Reference mxURLTransformer; + + /** Look up the factory for the given URL. + @param rsURLBase + The css::tools::URL.Main part of a URL. Arguments have to be + stripped off by the caller. + @return + When the factory has not yet been added then return NULL. + @throws css::uno::RuntimeException + */ + css::uno::Reference FindFactory ( + const OUString& rsURLBase); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/ResourceId.cxx b/sd/source/ui/framework/configuration/ResourceId.cxx new file mode 100644 index 000000000..1845b353f --- /dev/null +++ b/sd/source/ui/framework/configuration/ResourceId.cxx @@ -0,0 +1,503 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +/** When the USE_OPTIMIZATIONS symbol is defined then at some optimizations + are activated that work only together with XResourceId objects that are + implemented by the ResourceId class. For other implementations of when + the USE_OPTIMIZATIONS symbol is not defined then alternative code is + used instead. +*/ +#define USE_OPTIMIZATIONS + +namespace sd::framework { + +//===== ResourceId ============================================================ + +WeakReference ResourceId::mxURLTransformerWeak; + +ResourceId::ResourceId() + : maResourceURLs(0) +{ +} + +ResourceId::ResourceId ( + std::vector&& rResourceURLs) + : maResourceURLs(std::move(rResourceURLs)) +{ + ParseResourceURL(); +} + +ResourceId::ResourceId ( + const OUString& rsResourceURL) + : maResourceURLs(1, rsResourceURL) +{ + // Handle the special case of an empty resource URL. + if (rsResourceURL.isEmpty()) + maResourceURLs.clear(); + ParseResourceURL(); +} + +ResourceId::ResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL) + : maResourceURLs(2) +{ + maResourceURLs[0] = rsResourceURL; + maResourceURLs[1] = rsAnchorURL; + ParseResourceURL(); +} + +ResourceId::ResourceId ( + const OUString& rsResourceURL, + const OUString& rsFirstAnchorURL, + const Sequence& rAnchorURLs) + : maResourceURLs(2+rAnchorURLs.getLength()) +{ + maResourceURLs[0] = rsResourceURL; + maResourceURLs[1] = rsFirstAnchorURL; + std::copy(rAnchorURLs.begin(), rAnchorURLs.end(), std::next(maResourceURLs.begin(), 2)); + ParseResourceURL(); +} + +ResourceId::~ResourceId() +{ + mpURL.reset(); +} + +OUString SAL_CALL + ResourceId::getResourceURL() +{ + if (!maResourceURLs.empty()) + return maResourceURLs[0]; + else + return OUString(); +} + +util::URL SAL_CALL + ResourceId::getFullResourceURL() +{ + if (mpURL != nullptr) + return *mpURL; + + Reference xURLTransformer (mxURLTransformerWeak); + if (xURLTransformer.is() && !maResourceURLs.empty() ) + { + mpURL.reset(new util::URL); + mpURL->Complete = maResourceURLs[0]; + xURLTransformer->parseStrict(*mpURL); + return *mpURL; + } + + util::URL aURL; + if (!maResourceURLs.empty()) + aURL.Complete = maResourceURLs[0]; + return aURL; +} + +sal_Bool SAL_CALL + ResourceId::hasAnchor() +{ + return maResourceURLs.size()>1; +} + +Reference SAL_CALL + ResourceId::getAnchor() +{ + ::rtl::Reference rResourceId (new ResourceId()); + const sal_Int32 nAnchorCount (maResourceURLs.size()-1); + if (nAnchorCount > 0) + { + rResourceId->maResourceURLs.resize(nAnchorCount); + for (sal_Int32 nIndex=0; nIndexmaResourceURLs[nIndex] = maResourceURLs[nIndex+1]; + } + return rResourceId; +} + +Sequence SAL_CALL + ResourceId::getAnchorURLs() +{ + const sal_Int32 nAnchorCount (maResourceURLs.size() - 1); + if (nAnchorCount > 0) + { + Sequence aAnchorURLs (nAnchorCount); + std::copy_n(maResourceURLs.begin() + 1, nAnchorCount, aAnchorURLs.getArray()); + return aAnchorURLs; + } + else + return Sequence(); +} + +OUString SAL_CALL + ResourceId::getResourceTypePrefix() +{ + if (!maResourceURLs.empty() ) + { + // Return the "private:resource//" prefix. + + // Get the prefix that ends with the second "/". + const OUString& rsResourceURL (maResourceURLs[0]); + sal_Int32 nPrefixEnd (rsResourceURL.indexOf('/')); + if (nPrefixEnd >= 0) + nPrefixEnd = rsResourceURL.indexOf('/', nPrefixEnd+1) + 1; + else + nPrefixEnd = 0; + + return rsResourceURL.copy(0,nPrefixEnd); + } + else + return OUString(); +} + +sal_Int16 SAL_CALL + ResourceId::compareTo (const Reference& rxResourceId) +{ + sal_Int16 nResult (0); + + if ( ! rxResourceId.is()) + { + // The empty reference is interpreted as empty resource id object. + if (!maResourceURLs.empty()) + nResult = +1; + else + nResult = 0; + } + else + { + ResourceId* pId = nullptr; +#ifdef USE_OPTIMIZATIONS + pId = dynamic_cast(rxResourceId.get()); +#endif + if (pId != nullptr) + { + // We have direct access to the implementation of the given + // resource id object. + nResult = CompareToLocalImplementation(*pId); + } + else + { + // We have to do the comparison via the UNO interface of the + // given resource id object. + nResult = CompareToExternalImplementation(rxResourceId); + } + } + + return nResult; +} + +sal_Int16 ResourceId::CompareToLocalImplementation (const ResourceId& rId) const +{ + sal_Int16 nResult (0); + + const sal_uInt32 nLocalURLCount (maResourceURLs.size()); + const sal_uInt32 nURLCount(rId.maResourceURLs.size()); + + // Start comparison with the top most anchors. + for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1; + nIndex>=0 && nLocalIndex>=0; + --nIndex,--nLocalIndex) + { + const OUString sLocalURL (maResourceURLs[nLocalIndex]); + const OUString sURL (rId.maResourceURLs[nIndex]); + const sal_Int32 nLocalResult (sURL.compareTo(sLocalURL)); + if (nLocalResult != 0) + { + if (nLocalResult < 0) + nResult = -1; + else + nResult = +1; + break; + } + } + + if (nResult == 0) + { + // No difference found yet. When the lengths are the same then the + // two resource ids are equivalent. Otherwise the shorter comes + // first. + if (nLocalURLCount != nURLCount) + { + if (nLocalURLCount < nURLCount) + nResult = -1; + else + nResult = +1; + } + } + + return nResult; +} + +sal_Int16 ResourceId::CompareToExternalImplementation (const Reference& rxId) const +{ + sal_Int16 nResult (0); + + const Sequence aAnchorURLs (rxId->getAnchorURLs()); + const sal_uInt32 nLocalURLCount (maResourceURLs.size()); + const sal_uInt32 nURLCount(1+aAnchorURLs.getLength()); + + // Start comparison with the top most anchors. + sal_Int32 nLocalResult (0); + for (sal_Int32 nIndex=nURLCount-1,nLocalIndex=nLocalURLCount-1; + nIndex>=0&&nLocalIndex>=0; + --nIndex,--nLocalIndex) + { + if (nIndex == 0 ) + nLocalResult = maResourceURLs[nIndex].compareTo(rxId->getResourceURL()); + else + nLocalResult = maResourceURLs[nIndex].compareTo(aAnchorURLs[nIndex-1]); + if (nLocalResult != 0) + { + if (nLocalResult < 0) + nResult = -1; + else + nResult = +1; + break; + } + } + + if (nResult == 0) + { + // No difference found yet. When the lengths are the same then the + // two resource ids are equivalent. Otherwise the shorter comes + // first. + if (nLocalURLCount != nURLCount) + { + if (nLocalURLCount < nURLCount) + nResult = -1; + else + nResult = +1; + } + } + + return nResult; +} + +sal_Bool SAL_CALL + ResourceId::isBoundTo ( + const Reference& rxResourceId, + AnchorBindingMode eMode) +{ + if ( ! rxResourceId.is()) + { + // An empty reference is interpreted as empty resource id. + return IsBoundToAnchor(nullptr, nullptr, eMode); + } + + ResourceId* pId = nullptr; +#ifdef USE_OPTIMIZATIONS + pId = dynamic_cast(rxResourceId.get()); +#endif + if (pId != nullptr) + { + return IsBoundToAnchor(pId->maResourceURLs, eMode); + } + else + { + const OUString sResourceURL (rxResourceId->getResourceURL()); + const Sequence aAnchorURLs (rxResourceId->getAnchorURLs()); + return IsBoundToAnchor(&sResourceURL, &aAnchorURLs, eMode); + } +} + +sal_Bool SAL_CALL + ResourceId::isBoundToURL ( + const OUString& rsAnchorURL, + AnchorBindingMode eMode) +{ + return IsBoundToAnchor(&rsAnchorURL, nullptr, eMode); +} + +Reference SAL_CALL + ResourceId::clone() +{ + return new ResourceId(std::vector(maResourceURLs)); +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL ResourceId::initialize (const Sequence& aArguments) +{ + for (const auto& rArgument : aArguments) + { + OUString sResourceURL; + if (rArgument >>= sResourceURL) + maResourceURLs.push_back(sResourceURL); + else + { + Reference xAnchor; + if (rArgument >>= xAnchor) + { + if (xAnchor.is()) + { + maResourceURLs.push_back(xAnchor->getResourceURL()); + const Sequence aAnchorURLs (xAnchor->getAnchorURLs()); + maResourceURLs.insert( maResourceURLs.end(), aAnchorURLs.begin(), aAnchorURLs.end() ); + } + } + } + } + ParseResourceURL(); +} + +OUString ResourceId::getImplementationName() +{ + return "com.sun.star.comp.Draw.framework.ResourceId"; +} + +sal_Bool ResourceId::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence ResourceId::getSupportedServiceNames() +{ + return css::uno::Sequence{ + "com.sun.star.drawing.framework.ResourceId"}; +} + +/** When eMode is DIRECTLY then the anchor of the called object and the + anchor represented by the given sequence of anchor URLs have to be + identical. When eMode is RECURSIVE then the anchor of the called + object has to start with the given anchor URLs. +*/ +bool ResourceId::IsBoundToAnchor ( + const OUString* psFirstAnchorURL, + const Sequence* paAnchorURLs, + AnchorBindingMode eMode) const +{ + const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1); + const bool bHasFirstAnchorURL (psFirstAnchorURL!=nullptr); + const sal_uInt32 nAnchorURLCount ((bHasFirstAnchorURL?1:0) + + (paAnchorURLs!=nullptr ? paAnchorURLs->getLength() : 0)); + + // Check the lengths. + if (nLocalAnchorURLCountgetLength(); + while (nOffset < nCount) + { + if ( maResourceURLs[nLocalAnchorURLCount - nOffset] != + (*paAnchorURLs)[nCount - 1 - nOffset] ) + { + return false; + } + ++nOffset; + } + } + if (bHasFirstAnchorURL) + { + if ( *psFirstAnchorURL != maResourceURLs[nLocalAnchorURLCount - nOffset] ) + return false; + } + + return true; +} + +bool ResourceId::IsBoundToAnchor ( + const ::std::vector& rAnchorURLs, + AnchorBindingMode eMode) const +{ + const sal_uInt32 nLocalAnchorURLCount (maResourceURLs.size() - 1); + const sal_uInt32 nAnchorURLCount (rAnchorURLs.size()); + + // Check the lengths. + if (nLocalAnchorURLCount aGuard (::osl::Mutex::getGlobalMutex()); + Reference xURLTransformer (mxURLTransformerWeak); + if ( ! xURLTransformer.is()) + { + // Create the URL transformer. + Reference xContext(::comphelper::getProcessComponentContext()); + xURLTransformer.set(util::URLTransformer::create(xContext)); + mxURLTransformerWeak = xURLTransformer; + SdGlobalResourceContainer::Instance().AddResource( + Reference(xURLTransformer,UNO_QUERY)); + } + + if (xURLTransformer.is() && !maResourceURLs.empty() ) + { + mpURL.reset(new util::URL); + mpURL->Complete = maResourceURLs[0]; + xURLTransformer->parseStrict(*mpURL); + if (mpURL->Main == maResourceURLs[0]) + mpURL.reset(); + else + maResourceURLs[0] = mpURL->Main; + } +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_ResourceID_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::framework::ResourceId()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/UpdateRequest.cxx b/sd/source/ui/framework/configuration/UpdateRequest.cxx new file mode 100644 index 000000000..b6c5e8c42 --- /dev/null +++ b/sd/source/ui/framework/configuration/UpdateRequest.cxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "UpdateRequest.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework +{ +UpdateRequest::UpdateRequest() noexcept {} + +UpdateRequest::~UpdateRequest() noexcept {} + +void SAL_CALL UpdateRequest::execute(const Reference&) +{ + // Do nothing here. The configuration is updated when the request queue + // becomes empty. +} + +OUString SAL_CALL UpdateRequest::getName() { return "UpdateRequest"; } + +void SAL_CALL UpdateRequest::setName(const OUString&) +{ + // Ignored. +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/UpdateRequest.hxx b/sd/source/ui/framework/configuration/UpdateRequest.hxx new file mode 100644 index 000000000..712167154 --- /dev/null +++ b/sd/source/ui/framework/configuration/UpdateRequest.hxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::drawing::framework { class XConfiguration; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeRequest, + css::container::XNamed + > UpdateRequestInterfaceBase; + +/** This update request is used to request configuration updates + asynchronous when no other requests are being processed. When there are + other requests then we can simply wait until the last one is executed: + the configuration is updated when the request queue becomes empty. This + is use by this implementation as well. The execute() method does not + really do anything. This request just triggers the update of the + configuration when it is removed as last request from the queue. +*/ +class UpdateRequest final + : public UpdateRequestInterfaceBase +{ +public: + UpdateRequest() noexcept; + virtual ~UpdateRequest() noexcept override; + + // XConfigurationChangeOperation + + virtual void SAL_CALL execute ( + const css::uno::Reference& rxConfiguration) override; + + // XNamed + + /** Return a human readable string representation. This is used for + debugging purposes. + */ + virtual OUString SAL_CALL getName() override; + + /** This call is ignored because the XNamed interface is (mis)used to + give access to a human readable name for debugging purposes. + */ + virtual void SAL_CALL setName (const OUString& rName) override; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/configuration/debugtrace.hxx b/sd/source/ui/framework/configuration/debugtrace.hxx new file mode 100644 index 000000000..b520d0ff3 --- /dev/null +++ b/sd/source/ui/framework/configuration/debugtrace.hxx @@ -0,0 +1,15 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +/// Centrally define activation of configuration debug traces. +#define DEBUG_SD_CONFIGURATION_TRACE 0 + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicPaneFactory.cxx b/sd/source/ui/framework/factories/BasicPaneFactory.cxx new file mode 100644 index 000000000..c01d315a3 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicPaneFactory.cxx @@ -0,0 +1,432 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include "BasicPaneFactory.hxx" + +#include "ChildWindowPane.hxx" +#include "FrameWindowPane.hxx" +#include "FullScreenPane.hxx" + +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + enum PaneId { + CenterPaneId, + FullScreenPaneId, + LeftImpressPaneId, + LeftDrawPaneId + }; + + const sal_Int32 gnConfigurationUpdateStartEvent(0); + const sal_Int32 gnConfigurationUpdateEndEvent(1); +} + +namespace sd::framework { + +/** Store URL, XPane reference and (local) PaneId for every pane factory + that is registered at the PaneController. +*/ +class BasicPaneFactory::PaneDescriptor +{ +public: + OUString msPaneURL; + Reference mxPane; + PaneId mePaneId; + /** The mbReleased flag is set when the pane has been released. Some + panes are just hidden and destroyed. When the pane is reused this + flag is reset. + */ + bool mbIsReleased; + + bool CompareURL(std::u16string_view rsPaneURL) const { return msPaneURL == rsPaneURL; } + bool ComparePane(const Reference& rxPane) const { return mxPane == rxPane; } +}; + +class BasicPaneFactory::PaneContainer + : public ::std::vector +{ +public: + PaneContainer() {} +}; + +//===== PaneFactory =========================================================== + +BasicPaneFactory::BasicPaneFactory ( + const Reference& rxContext) + : mxComponentContext(rxContext), + mpViewShellBase(nullptr), + mpPaneContainer(new PaneContainer) +{ +} + +BasicPaneFactory::~BasicPaneFactory() +{ +} + +void BasicPaneFactory::disposing(std::unique_lock&) +{ + Reference xCC (mxConfigurationControllerWeak); + if (xCC.is()) + { + xCC->removeResourceFactoryForReference(this); + xCC->removeConfigurationChangeListener(this); + mxConfigurationControllerWeak.clear(); + } + + for (const auto& rDescriptor : *mpPaneContainer) + { + if (rDescriptor.mbIsReleased) + { + Reference xComponent (rDescriptor.mxPane, UNO_QUERY); + if (xComponent.is()) + { + xComponent->removeEventListener(this); + xComponent->dispose(); + } + } + } +} + +void SAL_CALL BasicPaneFactory::initialize (const Sequence& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference xController (aArguments[0], UNO_QUERY_THROW); + + // Tunnel through the controller to obtain access to the ViewShellBase. + try + { + Reference xTunnel (xController, UNO_QUERY_THROW); + if (auto pController = comphelper::getFromUnoTunnel(xTunnel)) + mpViewShellBase = pController->GetViewShellBase(); + } + catch(RuntimeException&) + {} + + Reference xCM (xController, UNO_QUERY_THROW); + Reference xCC (xCM->getConfigurationController()); + mxConfigurationControllerWeak = xCC; + + // Add pane factories for the two left panes (one for Impress and one for + // Draw) and the center pane. + if (xController.is() && xCC.is()) + { + PaneDescriptor aDescriptor; + aDescriptor.msPaneURL = FrameworkHelper::msCenterPaneURL; + aDescriptor.mePaneId = CenterPaneId; + aDescriptor.mbIsReleased = false; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msFullScreenPaneURL; + aDescriptor.mePaneId = FullScreenPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msLeftImpressPaneURL; + aDescriptor.mePaneId = LeftImpressPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + + aDescriptor.msPaneURL = FrameworkHelper::msLeftDrawPaneURL; + aDescriptor.mePaneId = LeftDrawPaneId; + mpPaneContainer->push_back(aDescriptor); + xCC->addResourceFactory(aDescriptor.msPaneURL, this); + } + + // Register as configuration change listener. + if (xCC.is()) + { + xCC->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any(gnConfigurationUpdateStartEvent)); + xCC->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any(gnConfigurationUpdateEndEvent)); + } + } + catch (RuntimeException&) + { + Reference xCC (mxConfigurationControllerWeak); + if (xCC.is()) + xCC->removeResourceFactoryForReference(this); + } +} + +//===== XPaneFactory ========================================================== + +Reference SAL_CALL BasicPaneFactory::createResource ( + const Reference& rxPaneId) +{ + ThrowIfDisposed(); + + Reference xPane; + + // Based on the ResourceURL of the given ResourceId look up the + // corresponding factory descriptor. + PaneContainer::iterator iDescriptor ( + ::std::find_if ( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { + return rPane.CompareURL(rxPaneId->getResourceURL()); + } )); + + if (iDescriptor == mpPaneContainer->end()) + { + // The requested pane can not be created by any of the factories + // managed by the called BasicPaneFactory object. + throw lang::IllegalArgumentException("BasicPaneFactory::createPane() called for unknown resource id", + nullptr, + 0); + } + + if (iDescriptor->mxPane.is()) + { + // The pane has already been created and is still active (has + // not yet been released). This should not happen. + xPane = iDescriptor->mxPane; + } + else + { + // Create a new pane. + switch (iDescriptor->mePaneId) + { + case CenterPaneId: + xPane = CreateFrameWindowPane(rxPaneId); + break; + + case FullScreenPaneId: + xPane = CreateFullScreenPane(mxComponentContext, rxPaneId); + break; + + case LeftImpressPaneId: + case LeftDrawPaneId: + xPane = CreateChildWindowPane( + rxPaneId, + *iDescriptor); + break; + } + iDescriptor->mxPane = xPane; + + // Listen for the pane being disposed. + Reference xComponent (xPane, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(this); + } + iDescriptor->mbIsReleased = false; + + + return xPane; +} + +void SAL_CALL BasicPaneFactory::releaseResource ( + const Reference& rxPane) +{ + ThrowIfDisposed(); + + // Based on the given XPane reference look up the corresponding factory + // descriptor. + PaneContainer::iterator iDescriptor ( + ::std::find_if( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { return rPane.ComparePane(rxPane); } )); + + if (iDescriptor == mpPaneContainer->end()) + { + // The given XPane reference is either empty or the pane was not + // created by any of the factories managed by the called + // BasicPaneFactory object. + throw lang::IllegalArgumentException("BasicPaneFactory::releasePane() called for pane that was not created by same factory.", + nullptr, + 0); + } + + // The given pane was created by one of the factories. Child + // windows are just hidden and will be reused when requested later. + // Other windows are disposed and their reference is reset so that + // on the next createPane() call for the same pane type the pane is + // created anew. + ChildWindowPane* pChildWindowPane = dynamic_cast(rxPane.get()); + if (pChildWindowPane != nullptr) + { + iDescriptor->mbIsReleased = true; + pChildWindowPane->Hide(); + } + else + { + iDescriptor->mxPane = nullptr; + Reference xComponent (rxPane, UNO_QUERY); + if (xComponent.is()) + { + // We are disposing the pane and do not have to be informed of + // that. + xComponent->removeEventListener(this); + xComponent->dispose(); + } + } + +} + +//===== XConfigurationChangeListener ========================================== + +void SAL_CALL BasicPaneFactory::notifyConfigurationChange ( + const ConfigurationChangeEvent& /* rEvent */ ) +{ + // FIXME: nothing to do +} + +//===== lang::XEventListener ================================================== + +void SAL_CALL BasicPaneFactory::disposing ( + const lang::EventObject& rEventObject) +{ + if (mxConfigurationControllerWeak.get() == rEventObject.Source) + { + mxConfigurationControllerWeak.clear(); + } + else + { + // Has one of the panes been disposed? If so, then release the + // reference to that pane, but not the pane descriptor. + Reference xPane (rEventObject.Source, UNO_QUERY); + PaneContainer::iterator iDescriptor ( + ::std::find_if ( + mpPaneContainer->begin(), + mpPaneContainer->end(), + [&] (PaneDescriptor const& rPane) { return rPane.ComparePane(xPane); } )); + if (iDescriptor != mpPaneContainer->end()) + { + iDescriptor->mxPane = nullptr; + } + } +} + +Reference BasicPaneFactory::CreateFrameWindowPane ( + const Reference& rxPaneId) +{ + Reference xPane; + + if (mpViewShellBase != nullptr) + { + xPane = new FrameWindowPane(rxPaneId, mpViewShellBase->GetViewWindow()); + } + + return xPane; +} + +Reference BasicPaneFactory::CreateFullScreenPane ( + const Reference& rxComponentContext, + const Reference& rxPaneId) +{ + Reference xPane ( + new FullScreenPane( + rxComponentContext, + rxPaneId, + mpViewShellBase->GetViewWindow())); + + return xPane; +} + +Reference BasicPaneFactory::CreateChildWindowPane ( + const Reference& rxPaneId, + const PaneDescriptor& rDescriptor) +{ + Reference xPane; + + if (mpViewShellBase != nullptr) + { + // Create the corresponding shell and determine the id of the child window. + sal_uInt16 nChildWindowId = 0; + ::std::unique_ptr pShell; + switch (rDescriptor.mePaneId) + { + case LeftImpressPaneId: + pShell.reset(new LeftImpressPaneShell()); + nChildWindowId = ::sd::LeftPaneImpressChildWindow::GetChildWindowId(); + break; + + case LeftDrawPaneId: + pShell.reset(new LeftDrawPaneShell()); + nChildWindowId = ::sd::LeftPaneDrawChildWindow::GetChildWindowId(); + break; + + default: + break; + } + + // With shell and child window id create the ChildWindowPane + // wrapper. + if (pShell != nullptr) + { + xPane = new ChildWindowPane( + rxPaneId, + nChildWindowId, + *mpViewShellBase, + std::move(pShell)); + } + } + + return xPane; +} + +void BasicPaneFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("BasicPaneFactory object has already been disposed", + const_cast(static_cast(this))); + } +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicPaneFactory_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::framework::BasicPaneFactory(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicPaneFactory.hxx b/sd/source/ui/framework/factories/BasicPaneFactory.hxx new file mode 100644 index 000000000..317776e48 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicPaneFactory.hxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd { + +class ViewShellBase; +} + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::lang::XInitialization, + css::drawing::framework::XResourceFactory, + css::drawing::framework::XConfigurationChangeListener + > BasicPaneFactoryInterfaceBase; + +/** This factory provides the frequently used standard panes + private:resource/pane/CenterPane + private:resource/pane/FullScreenPane + private:resource/pane/LeftImpressPane + private:resource/pane/LeftDrawPane + There are two left panes because this is (seems to be) the only way to + show different titles for the left pane in Draw and Impress. +*/ +class BasicPaneFactory + : public BasicPaneFactoryInterfaceBase +{ +public: + explicit BasicPaneFactory ( + const css::uno::Reference& rxContext); + virtual ~BasicPaneFactory() override; + + virtual void disposing(std::unique_lock&) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence& aArguments) override; + + // XResourceFactory + + virtual css::uno::Reference + SAL_CALL createResource ( + const css::uno::Reference& rxPaneId) override; + + virtual void SAL_CALL + releaseResource ( + const css::uno::Reference& rxPane) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // lang::XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEventObject) override; + +private: + css::uno::Reference mxComponentContext; + css::uno::WeakReference + mxConfigurationControllerWeak; + ViewShellBase* mpViewShellBase; + class PaneDescriptor; + class PaneContainer; + std::unique_ptr mpPaneContainer; + + /** Create a new instance of FrameWindowPane. + @param rPaneId + There is only one frame window so this id is just checked to + have the correct value. + */ + css::uno::Reference + CreateFrameWindowPane ( + const css::uno::Reference& rxPaneId); + + /** Create a new pane that represents the center pane in full screen + mode. + */ + css::uno::Reference + CreateFullScreenPane ( + const css::uno::Reference& rxComponentContext, + const css::uno::Reference& rxPaneId); + + /** Create a new instance of ChildWindowPane. + @param rPaneId + The ResourceURL member defines which side pane to create. + */ + css::uno::Reference + CreateChildWindowPane ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxPaneId, + const PaneDescriptor& rDescriptor); + + /// @throws css::lang::DisposedException + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicToolBarFactory.cxx b/sd/source/ui/framework/factories/BasicToolBarFactory.cxx new file mode 100644 index 000000000..af79a88ea --- /dev/null +++ b/sd/source/ui/framework/factories/BasicToolBarFactory.cxx @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "BasicToolBarFactory.hxx" + +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +//===== BasicToolBarFactory =================================================== + +BasicToolBarFactory::BasicToolBarFactory () +{ +} + +BasicToolBarFactory::~BasicToolBarFactory() +{ +} + +void BasicToolBarFactory::disposing(std::unique_lock&) +{ + Shutdown(); +} + +void BasicToolBarFactory::Shutdown() +{ + Reference xComponent (mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(static_cast(this)); + if (mxConfigurationController.is()) + { + mxConfigurationController->removeResourceFactoryForReference(this); + mxConfigurationController = nullptr; + } +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL BasicToolBarFactory::initialize (const Sequence& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + mxController.set(aArguments[0], UNO_QUERY_THROW); + + utl::MediaDescriptor aDescriptor (mxController->getModel()->getArgs()); + if ( ! aDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_PREVIEW, + false)) + { + // Register the factory for its supported tool bars. + Reference xControllerManager(mxController, UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + mxConfigurationController->addResourceFactory( + FrameworkHelper::msViewTabBarURL, this); + } + + Reference xComponent (mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast(this)); + } + else + { + // The view shell is in preview mode and thus does not need + // the view tab bar. + mxConfigurationController = nullptr; + } + } + catch (RuntimeException&) + { + Shutdown(); + throw; + } +} + +//----- lang::XEventListener -------------------------------------------------- + +void SAL_CALL BasicToolBarFactory::disposing ( + const lang::EventObject& rEventObject) +{ + if (rEventObject.Source == mxConfigurationController) + mxConfigurationController = nullptr; +} + +//===== XPaneFactory ========================================================== + +Reference SAL_CALL BasicToolBarFactory::createResource ( + const Reference& rxToolBarId) +{ + ThrowIfDisposed(); + + if (rxToolBarId->getResourceURL() != FrameworkHelper::msViewTabBarURL) + throw lang::IllegalArgumentException(); + + Reference xToolBar = new ViewTabBar(rxToolBarId, mxController); + return xToolBar; +} + +void SAL_CALL BasicToolBarFactory::releaseResource ( + const Reference& rxToolBar) +{ + ThrowIfDisposed(); + + Reference xComponent (rxToolBar, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); +} + +void BasicToolBarFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("BasicToolBarFactory object has already been disposed", + const_cast(static_cast(this))); + } +} + +} // end of namespace sd::framework + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicToolBarFactory_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::framework::BasicToolBarFactory); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicToolBarFactory.hxx b/sd/source/ui/framework/factories/BasicToolBarFactory.hxx new file mode 100644 index 000000000..fdaf92788 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicToolBarFactory.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::frame { class XController; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { class XConfigurationController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XResourceFactory, + css::lang::XInitialization, + css::lang::XEventListener + > BasicToolBarFactoryInterfaceBase; + +/** This factory provides some of the frequently used tool bars: + private:resource/toolbar/ViewTabBar +*/ +class BasicToolBarFactory + : public BasicToolBarFactoryInterfaceBase +{ +public: + BasicToolBarFactory (); + virtual ~BasicToolBarFactory() override; + + virtual void disposing(std::unique_lock&) override; + + // ToolBarFactory + + virtual css::uno::Reference SAL_CALL + createResource ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxToolBarId) override; + + virtual void SAL_CALL + releaseResource ( + const css::uno::Reference& + rxToolBar) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence& aArguments) override; + + // lang::XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEventObject) override; + +private: + css::uno::Reference mxConfigurationController; + css::uno::Reference mxController; + + void Shutdown(); + + /// @throws css::lang::DisposedException + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicViewFactory.cxx b/sd/source/ui/framework/factories/BasicViewFactory.cxx new file mode 100644 index 000000000..425cb4446 --- /dev/null +++ b/sd/source/ui/framework/factories/BasicViewFactory.cxx @@ -0,0 +1,518 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "BasicViewFactory.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//===== ViewDescriptor ======================================================== + +class BasicViewFactory::ViewDescriptor +{ +public: + Reference mxView; + std::shared_ptr mpViewShell; + Reference mxViewId; + static bool CompareView (const std::shared_ptr& rpDescriptor, + const Reference& rxView) + { return rpDescriptor->mxView.get() == rxView.get(); } +}; + +//===== BasicViewFactory::ViewShellContainer ================================== + +class BasicViewFactory::ViewShellContainer + : public ::std::vector > +{ +public: + ViewShellContainer() {}; +}; + +class BasicViewFactory::ViewCache + : public ::std::vector > +{ +public: + ViewCache() {}; +}; + +//===== ViewFactory =========================================================== + +BasicViewFactory::BasicViewFactory () + : mpViewShellContainer(new ViewShellContainer()), + mpBase(nullptr), + mpFrameView(nullptr), + mpWindow(VclPtr::Create(nullptr,WB_STDWORK)), + mpViewCache(std::make_shared()), + mxLocalPane(new Pane(Reference(), mpWindow.get())) +{ +} + +BasicViewFactory::~BasicViewFactory() +{ +} + +void BasicViewFactory::disposing(std::unique_lock&) +{ + // Disconnect from the frame view. + if (mpFrameView != nullptr) + { + mpFrameView->Disconnect(); + mpFrameView = nullptr; + } + + // Release the view cache. + for (const auto& rxView : *mpViewCache) + { + ReleaseView(rxView, true); + } + + // Release the view shell container. At this point no one other than us + // should hold references to the view shells (at the moment this is a + // trivial requirement, because no one other than us holds a shared + // pointer). + // ViewShellContainer::const_iterator iView; + for (const auto& rxView : *mpViewShellContainer) + { + OSL_ASSERT(rxView->mpViewShell.use_count() == 1); + } + mpViewShellContainer.reset(); +} + +Reference SAL_CALL BasicViewFactory::createResource ( + const Reference& rxViewId) +{ + Reference xView; + const bool bIsCenterPane ( + rxViewId->isBoundToURL(FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)); + + // Get the pane for the anchor URL. + Reference xPane; + if (mxConfigurationController.is()) + xPane.set(mxConfigurationController->getResource(rxViewId->getAnchor()), UNO_QUERY); + + // For main views use the frame view of the last main view. + ::sd::FrameView* pFrameView = nullptr; + if (xPane.is() && bIsCenterPane) + { + pFrameView = mpFrameView; + } + + // Get Window pointer for XWindow of the pane. + vcl::Window* pWindow = nullptr; + if (xPane.is()) + pWindow = VCLUnoHelper::GetWindow(xPane->getWindow()); + + // Get the view frame. + SfxViewFrame* pFrame = nullptr; + if (mpBase != nullptr) + pFrame = mpBase->GetViewFrame(); + + if (pFrame != nullptr && mpBase!=nullptr && pWindow!=nullptr) + { + // Try to get the view from the cache. + std::shared_ptr pDescriptor (GetViewFromCache(rxViewId, xPane)); + + // When the requested view is not in the cache then create a new view. + if (pDescriptor == nullptr) + { + pDescriptor = CreateView(rxViewId, *pFrame, *pWindow, xPane, pFrameView, bIsCenterPane); + } + + if (pDescriptor != nullptr) + xView = pDescriptor->mxView; + + mpViewShellContainer->push_back(pDescriptor); + + if (bIsCenterPane) + ActivateCenterView(pDescriptor); + else + pWindow->Resize(); + } + + return xView; +} + +void SAL_CALL BasicViewFactory::releaseResource (const Reference& rxView) +{ + if ( ! rxView.is()) + throw lang::IllegalArgumentException(); + + if (!rxView.is() || !mpBase) + return; + + ViewShellContainer::iterator iViewShell ( + ::std::find_if( + mpViewShellContainer->begin(), + mpViewShellContainer->end(), + [&] (std::shared_ptr const& pVD) { + return ViewDescriptor::CompareView(pVD, rxView); + } )); + if (iViewShell == mpViewShellContainer->end()) + { + throw lang::IllegalArgumentException(); + } + + std::shared_ptr pViewShell ((*iViewShell)->mpViewShell); + + if ((*iViewShell)->mxViewId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + // Obtain a pointer to and connect to the frame view of the + // view. The next view, that is created, will be + // initialized with this frame view. + if (mpFrameView == nullptr) + { + mpFrameView = pViewShell->GetFrameView(); + if (mpFrameView) + mpFrameView->Connect(); + } + + // With the view in the center pane the sub controller is + // released, too. + mpBase->GetDrawController().SetSubController( + Reference()); + + SfxViewShell* pSfxViewShell = pViewShell->GetViewShell(); + if (pSfxViewShell != nullptr) + pSfxViewShell->DisconnectAllClients(); + } + + ReleaseView(*iViewShell, false); + + mpViewShellContainer->erase(iViewShell); +} + +void SAL_CALL BasicViewFactory::initialize (const Sequence& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference xController (aArguments[0], UNO_QUERY_THROW); + + // Tunnel through the controller to obtain a ViewShellBase. + Reference xTunnel (xController, UNO_QUERY_THROW); + ::sd::DrawController* pController = comphelper::getFromUnoTunnel(xTunnel); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + // Register the factory for its supported views. + Reference xCM (xController,UNO_QUERY_THROW); + mxConfigurationController = xCM->getConfigurationController(); + if ( ! mxConfigurationController.is()) + throw RuntimeException(); + mxConfigurationController->addResourceFactory(FrameworkHelper::msImpressViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msDrawViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msOutlineViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msNotesViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msHandoutViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msPresentationViewURL, this); + mxConfigurationController->addResourceFactory(FrameworkHelper::msSlideSorterURL, this); + } + catch (RuntimeException&) + { + mpBase = nullptr; + if (mxConfigurationController.is()) + mxConfigurationController->removeResourceFactoryForReference(this); + throw; + } +} + +std::shared_ptr BasicViewFactory::CreateView ( + const Reference& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + const Reference& rxPane, + FrameView* pFrameView, + const bool bIsCenterPane) +{ + auto pDescriptor = std::make_shared(); + + pDescriptor->mpViewShell = CreateViewShell( + rxViewId, + rFrame, + rWindow, + pFrameView); + pDescriptor->mxViewId = rxViewId; + + if (pDescriptor->mpViewShell != nullptr) + { + pDescriptor->mpViewShell->Init(bIsCenterPane); + mpBase->GetViewShellManager()->ActivateViewShell(pDescriptor->mpViewShell.get()); + + Reference xWindow(rxPane->getWindow()); + rtl::Reference wrapper(new ViewShellWrapper( + pDescriptor->mpViewShell, + rxViewId, + xWindow)); + + // register ViewShellWrapper on pane window + if (xWindow.is()) + { + xWindow->addWindowListener(wrapper); + if (pDescriptor->mpViewShell != nullptr) + { + pDescriptor->mpViewShell->Resize(); + } + } + + pDescriptor->mxView = wrapper.get(); + } + + return pDescriptor; +} + +std::shared_ptr BasicViewFactory::CreateViewShell ( + const Reference& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + FrameView* pFrameView) +{ + std::shared_ptr pViewShell; + const OUString& rsViewURL (rxViewId->getResourceURL()); + if (rsViewURL == FrameworkHelper::msImpressViewURL) + { + pViewShell = + std::make_shared( + *mpBase, + &rWindow, + PageKind::Standard, + pFrameView); + pViewShell->GetContentWindow()->set_id("impress_win"); + } + else if (rsViewURL == FrameworkHelper::msDrawViewURL) + { + pViewShell = std::shared_ptr( + new GraphicViewShell(*mpBase, &rWindow, pFrameView), + o3tl::default_delete()); + pViewShell->GetContentWindow()->set_id("draw_win"); + } + else if (rsViewURL == FrameworkHelper::msOutlineViewURL) + { + pViewShell = + std::make_shared( + &rFrame, + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("outline_win"); + } + else if (rsViewURL == FrameworkHelper::msNotesViewURL) + { + pViewShell = + std::make_shared( + *mpBase, + &rWindow, + PageKind::Notes, + pFrameView); + pViewShell->GetContentWindow()->set_id("notes_win"); + } + else if (rsViewURL == FrameworkHelper::msHandoutViewURL) + { + pViewShell = + std::make_shared( + *mpBase, + &rWindow, + PageKind::Handout, + pFrameView); + pViewShell->GetContentWindow()->set_id("handout_win"); + } + else if (rsViewURL == FrameworkHelper::msPresentationViewURL) + { + pViewShell = + std::make_shared( + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("presentation_win"); + } + else if (rsViewURL == FrameworkHelper::msSlideSorterURL) + { + pViewShell = ::sd::slidesorter::SlideSorterViewShell::Create ( + &rFrame, + *mpBase, + &rWindow, + pFrameView); + pViewShell->GetContentWindow()->set_id("slidesorter"); + } + + return pViewShell; +} + +void BasicViewFactory::ReleaseView ( + const std::shared_ptr& rpDescriptor, + bool bDoNotCache) +{ + bool bIsCacheable (!bDoNotCache && IsCacheable(rpDescriptor)); + + if (bIsCacheable) + { + Reference xResource (rpDescriptor->mxView, UNO_QUERY); + if (xResource.is()) + { + if (mxLocalPane.is()) + if (xResource->relocateToAnchor(mxLocalPane)) + mpViewCache->push_back(rpDescriptor); + else + bIsCacheable = false; + else + bIsCacheable = false; + } + else + { + bIsCacheable = false; + } + } + + if ( ! bIsCacheable) + { + // Shut down the current view shell. + rpDescriptor->mpViewShell->Shutdown (); + mpBase->GetDocShell()->Disconnect(rpDescriptor->mpViewShell.get()); + mpBase->GetViewShellManager()->DeactivateViewShell(rpDescriptor->mpViewShell.get()); + + Reference xComponent (rpDescriptor->mxView, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + } +} + +bool BasicViewFactory::IsCacheable (const std::shared_ptr& rpDescriptor) +{ + bool bIsCacheable (false); + + Reference xResource (rpDescriptor->mxView, UNO_QUERY); + if (xResource.is()) + { + static ::std::vector > s_aCacheableResources = [&]() + { + ::std::vector > tmp; + FrameworkHelper::Instance(*mpBase); + + // The slide sorter and the task panel are cacheable and relocatable. + tmp.push_back(FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftDrawPaneURL)); + tmp.push_back(FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, FrameworkHelper::msLeftImpressPaneURL)); + return tmp; + }(); + + bIsCacheable = std::any_of(s_aCacheableResources.begin(), s_aCacheableResources.end(), + [&rpDescriptor](const Reference& rxId) { return rxId->compareTo(rpDescriptor->mxViewId) == 0; }); + } + + return bIsCacheable; +} + +std::shared_ptr BasicViewFactory::GetViewFromCache ( + const Reference& rxViewId, + const Reference& rxPane) +{ + std::shared_ptr pDescriptor; + + // Search for the requested view in the cache. + ViewCache::iterator iEntry = std::find_if(mpViewCache->begin(), mpViewCache->end(), + [&rxViewId](const ViewCache::value_type& rxEntry) { return rxEntry->mxViewId->compareTo(rxViewId) == 0; }); + if (iEntry != mpViewCache->end()) + { + pDescriptor = *iEntry; + mpViewCache->erase(iEntry); + } + + // When the view has been found then relocate it to the given pane and + // remove it from the cache. + if (pDescriptor != nullptr) + { + bool bRelocationSuccessful (false); + Reference xResource (pDescriptor->mxView, UNO_QUERY); + if (xResource.is() && rxPane.is()) + { + if (xResource->relocateToAnchor(rxPane)) + bRelocationSuccessful = true; + } + + if ( ! bRelocationSuccessful) + { + ReleaseView(pDescriptor, true); + pDescriptor.reset(); + } + } + + return pDescriptor; +} + +void BasicViewFactory::ActivateCenterView ( + const std::shared_ptr& rpDescriptor) +{ + mpBase->GetDocShell()->Connect(rpDescriptor->mpViewShell.get()); + + // During the creation of the new sub-shell, resize requests were not + // forwarded to it because it was not yet registered. Therefore, we + // have to request a resize now. + rpDescriptor->mpViewShell->UIFeatureChanged(); + if (mpBase->GetDocShell()->IsInPlaceActive()) + mpBase->GetViewFrame()->Resize(true); + + mpBase->GetDrawController().SetSubController( + rpDescriptor->mpViewShell->CreateSubController()); +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_BasicViewFactory_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::framework::BasicViewFactory); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/BasicViewFactory.hxx b/sd/source/ui/framework/factories/BasicViewFactory.hxx new file mode 100644 index 000000000..ccd5cbbda --- /dev/null +++ b/sd/source/ui/framework/factories/BasicViewFactory.hxx @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +#include +#include + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XPane; } + +namespace sd { +class ViewShell; +class ViewShellBase; +class FrameView; +} +class SfxViewFrame; +namespace vcl { class Window; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XResourceFactory, + css::lang::XInitialization + > BasicViewFactoryInterfaceBase; + +/** Factory for the frequently used standard views of the drawing framework: + private:resource/view/ + private:resource/view/ImpressView + private:resource/view/GraphicView + private:resource/view/OutlineView + private:resource/view/NotesView + private:resource/view/HandoutView + private:resource/view/SlideSorter + private:resource/view/PresentationView + private:resource/view/TaskPane + For some views in some panes this class also acts as a cache. +*/ +class BasicViewFactory + : public BasicViewFactoryInterfaceBase +{ +public: + BasicViewFactory (); + virtual ~BasicViewFactory() override; + + virtual void disposing(std::unique_lock&) override; + + // XViewFactory + + virtual css::uno::Reference + SAL_CALL createResource ( + const css::uno::Reference& rxViewId) override; + + virtual void SAL_CALL releaseResource ( + const css::uno::Reference& xView) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence& aArguments) override; + +private: + css::uno::Reference + mxConfigurationController; + class ViewDescriptor; + class ViewShellContainer; + std::unique_ptr mpViewShellContainer; + ViewShellBase* mpBase; + FrameView* mpFrameView; + + class ViewCache; + ScopedVclPtr mpWindow; + std::shared_ptr mpViewCache; + + css::uno::Reference mxLocalPane; + + std::shared_ptr CreateView ( + const css::uno::Reference& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + const css::uno::Reference& rxPane, + FrameView* pFrameView, + const bool bIsCenterView); + + std::shared_ptr CreateViewShell ( + const css::uno::Reference& rxViewId, + SfxViewFrame& rFrame, + vcl::Window& rWindow, + FrameView* pFrameView); + + void ActivateCenterView ( + const std::shared_ptr& rpDescriptor); + + void ReleaseView ( + const std::shared_ptr& rpDescriptor, + bool bDoNotCache); + + bool IsCacheable ( + const std::shared_ptr& rpDescriptor); + + std::shared_ptr GetViewFromCache ( + const css::uno::Reference& rxViewId, + const css::uno::Reference& rxPane); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ChildWindowPane.cxx b/sd/source/ui/framework/factories/ChildWindowPane.cxx new file mode 100644 index 000000000..136f6fcb7 --- /dev/null +++ b/sd/source/ui/framework/factories/ChildWindowPane.cxx @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include + +#include "ChildWindowPane.hxx" + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +ChildWindowPane::ChildWindowPane ( + const Reference& rxPaneId, + sal_uInt16 nChildWindowId, + ViewShellBase& rViewShellBase, + ::std::unique_ptr && pShell) + : ChildWindowPaneInterfaceBase(rxPaneId,nullptr), + mnChildWindowId(nChildWindowId), + mrViewShellBase(rViewShellBase), + mpShell(std::move(pShell)), + mbHasBeenActivated(false) +{ + mrViewShellBase.GetViewShellManager()->ActivateShell(mpShell.get()); + + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame == nullptr) + return; + + if (mrViewShellBase.IsActive()) + { + if (pViewFrame->KnowsChildWindow(mnChildWindowId)) + { + if (pViewFrame->HasChildWindow(mnChildWindowId)) + { + // The ViewShellBase has already been activated. Make + // the child window visible as soon as possible. + pViewFrame->SetChildWindow(mnChildWindowId, true); + } + else + { + // The window is created asynchronously. Rely on the + // ConfigurationUpdater to try another update, and with + // that another request for this window, in a short + // time. + } + } + else + { + SAL_WARN("sd", "ChildWindowPane:not known"); + } + } + else + { + // The ViewShellBase has not yet been activated. Hide the + // window and wait a little before it is made visible. See + // comments in the GetWindow() method for an explanation. + pViewFrame->SetChildWindow(mnChildWindowId, false); + } +} + +ChildWindowPane::~ChildWindowPane() +{ +} + +void ChildWindowPane::Hide() +{ + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame != nullptr) + if (pViewFrame->KnowsChildWindow(mnChildWindowId)) + if (pViewFrame->HasChildWindow(mnChildWindowId)) + pViewFrame->SetChildWindow(mnChildWindowId, false); + + // Release the window because when the child window is shown again it + // may use a different window. + mxWindow = nullptr; +} + +void SAL_CALL ChildWindowPane::disposing() +{ + ::osl::MutexGuard aGuard (m_aMutex); + + mrViewShellBase.GetViewShellManager()->DeactivateShell(mpShell.get()); + mpShell.reset(); + + if (mxWindow.is()) + { + mxWindow->removeEventListener(this); + } + + Pane::disposing(); +} + +vcl::Window* ChildWindowPane::GetWindow() +{ + do + { + if (mxWindow.is()) + // Window already exists => nothing to do. + break; + + // When the window is not yet present then obtain it only when the + // shell has already been activated. The activation is not + // necessary for the code to work properly but is used to optimize + // the layouting and displaying of the window. When it is made + // visible too early then some layouting seems to be made twice or at + // an inconvenient time and the overall process of initializing the + // Impress takes longer. + if (!mbHasBeenActivated && mpShell != nullptr && !mpShell->IsActive()) + break; + + mbHasBeenActivated = true; + SfxViewFrame* pViewFrame = mrViewShellBase.GetViewFrame(); + if (pViewFrame == nullptr) + break; + // The view frame has to know the child window. This can be the + // case, when for example the document is in read-only mode: the + // task pane is then not available. + if ( ! pViewFrame->KnowsChildWindow(mnChildWindowId)) + break; + + pViewFrame->SetChildWindow(mnChildWindowId, true); + SfxChildWindow* pChildWindow = pViewFrame->GetChildWindow(mnChildWindowId); + if (pChildWindow == nullptr) + if (pViewFrame->HasChildWindow(mnChildWindowId)) + { + // The child window is not yet visible. Ask the view frame + // to show it and try again to get access to the child + // window. + pViewFrame->ShowChildWindow(mnChildWindowId); + pChildWindow = pViewFrame->GetChildWindow(mnChildWindowId); + } + + // When the child window is still not visible then we have to try later. + if (pChildWindow == nullptr) + break; + + // From the child window get the docking window and from that the + // content window that is the container for the actual content. + PaneDockingWindow* pDockingWindow = dynamic_cast( + pChildWindow->GetWindow()); + if (pDockingWindow == nullptr) + break; + + // At last, we have access to the window and its UNO wrapper. + mpWindow = &pDockingWindow->GetContentWindow(); + mxWindow = VCLUnoHelper::GetInterface(mpWindow); + + // Register as window listener to be informed when the child window + // is hidden. + if (mxWindow.is()) + mxWindow->addEventListener(this); + } + while (false); + + return mpWindow; +} + +Reference SAL_CALL ChildWindowPane::getWindow() +{ + if (mpWindow == nullptr || ! mxWindow.is()) + GetWindow(); + return Pane::getWindow(); +} + +IMPLEMENT_FORWARD_XINTERFACE2( + ChildWindowPane, + ChildWindowPaneInterfaceBase, + Pane); +IMPLEMENT_FORWARD_XTYPEPROVIDER2( + ChildWindowPane, + ChildWindowPaneInterfaceBase, + Pane); + +//----- XEventListener -------------------------------------------------------- + +void SAL_CALL ChildWindowPane::disposing (const lang::EventObject& rEvent) +{ + ThrowIfDisposed(); + + if (rEvent.Source == mxWindow) + { + // The window is gone but the pane remains alive. The next call to + // GetWindow() may create the window anew. + mxWindow = nullptr; + mpWindow = nullptr; + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ChildWindowPane.hxx b/sd/source/ui/framework/factories/ChildWindowPane.hxx new file mode 100644 index 000000000..082177757 --- /dev/null +++ b/sd/source/ui/framework/factories/ChildWindowPane.hxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include +#include +#include + +class SfxShell; + +namespace sd { class ViewShellBase; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::drawing::framework { class XResourceId; } + +namespace sd::framework { + +typedef ::cppu::ImplInheritanceHelper < + ::sd::framework::Pane, + css::lang::XEventListener + > ChildWindowPaneInterfaceBase; + +/** The ChildWindowPane listens to the child window and disposes itself when + the child window becomes inaccessible. This happens for instance when a + document is made read-only and the task pane is turned off. +*/ +class ChildWindowPane + : public ChildWindowPaneInterfaceBase +{ +public: + ChildWindowPane ( + const css::uno::Reference& rxPaneId, + sal_uInt16 nChildWindowId, + ViewShellBase& rViewShellBase, + ::std::unique_ptr && pShell); + virtual ~ChildWindowPane() override; + + /** Hide the pane. To make the pane visible again, call GetWindow(). + */ + void Hide(); + + virtual void SAL_CALL disposing() override; + + /** This returns the content window when the child window is already + visible. Otherwise is returned. In that case a later call + may return the requested window (making a child window visible is an + asynchronous process.) + Note that GetWindow() may return different Window pointers when + Hide() is called in between. + */ + virtual vcl::Window* GetWindow() override; + + /** The local getWindow() first calls GetWindow() to provide a valid + window pointer before forwarding the call to the base class. + */ + virtual css::uno::Reference + SAL_CALL getWindow() override; + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XEventListener + + virtual void SAL_CALL disposing( + const css::lang::EventObject& rEvent) override; + +private: + sal_uInt16 mnChildWindowId; + ViewShellBase& mrViewShellBase; + ::std::unique_ptr mpShell; + + /** This flag is set when the pane shell has been activated at least + once. It is used to optimize the start-up performance (by not + showing the window too early) and by not delaying its creation at + later times. + */ + bool mbHasBeenActivated; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FrameWindowPane.cxx b/sd/source/ui/framework/factories/FrameWindowPane.cxx new file mode 100644 index 000000000..1f4b387d8 --- /dev/null +++ b/sd/source/ui/framework/factories/FrameWindowPane.cxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "FrameWindowPane.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework +{ +FrameWindowPane::FrameWindowPane(const Reference& rxPaneId, vcl::Window* pWindow) + : Pane(rxPaneId, pWindow) +{ +} + +FrameWindowPane::~FrameWindowPane() noexcept {} + +sal_Bool SAL_CALL FrameWindowPane::isAnchorOnly() { return false; } + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FrameWindowPane.hxx b/sd/source/ui/framework/factories/FrameWindowPane.hxx new file mode 100644 index 000000000..67da37fdf --- /dev/null +++ b/sd/source/ui/framework/factories/FrameWindowPane.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace com::sun::star::drawing::framework +{ +class XResourceId; +} + +namespace sd::framework +{ +/** This subclass is not necessary anymore. We can remove it if that + remains so. +*/ +class FrameWindowPane : public Pane +{ +public: + FrameWindowPane(const css::uno::Reference& rxPaneId, + vcl::Window* pWindow); + virtual ~FrameWindowPane() noexcept override; + + /** A frame window typically can (and should) exists on its own without + children, if only to visualize that something (a view) is missing. + Therefore this method always returns . + */ + virtual sal_Bool SAL_CALL isAnchorOnly() override; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FullScreenPane.cxx b/sd/source/ui/framework/factories/FullScreenPane.cxx new file mode 100644 index 000000000..dbf34213f --- /dev/null +++ b/sd/source/ui/framework/factories/FullScreenPane.cxx @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "FullScreenPane.hxx" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +FullScreenPane::FullScreenPane ( + const Reference& rxComponentContext, + const Reference& rxPaneId, + const vcl::Window* pViewShellWindow) + : FrameWindowPane(rxPaneId,nullptr), + mxComponentContext(rxComponentContext) +{ + vcl::Window* pParent = nullptr; + mpWorkWindow.reset(VclPtr::Create( + + pParent, + 0)); // For debugging (non-fullscreen) use WB_BORDER | WB_MOVEABLE | WB_SIZEABLE)); + + if ( ! rxPaneId.is()) + throw lang::IllegalArgumentException(); + + sal_Int32 nScreenNumber = 1; + ExtractArguments(rxPaneId, nScreenNumber); + + if (!mpWorkWindow) + return; + + // Create a new top-level window that is displayed full screen. + mpWorkWindow->ShowFullScreenMode(true, nScreenNumber); + // For debugging (non-fullscreen) use mpWorkWindow->SetScreenNumber(nScreenNumber); + mpWorkWindow->SetMenuBarMode(MenuBarMode::Hide); + mpWorkWindow->SetBorderStyle(WindowBorderStyle::REMOVEBORDER); + mpWorkWindow->SetBackground(Wallpaper()); + // Don't show the window right now in order to allow the setting of an + // accessibility object: accessibility objects are typically + // requested by AT-tools when the window is shown. Chaining it + // afterwards may or may not work. + + // Add resize listener at the work window. + Link aWindowEventHandler (LINK(this, FullScreenPane, WindowEventHandler)); + mpWorkWindow->AddEventListener(aWindowEventHandler); + + // Set title and icon of the new window to those of the current window + // of the view shell. + if (pViewShellWindow != nullptr) + { + const SystemWindow* pSystemWindow = pViewShellWindow->GetSystemWindow(); + mpWorkWindow->SetText(pSystemWindow->GetText()); + mpWorkWindow->SetIcon(pSystemWindow->GetIcon()); + } + + // For some reason the VCL canvas can not paint into a WorkWindow. + // Therefore a child window is created that covers the WorkWindow + // completely. + mpWindow = VclPtr::Create(mpWorkWindow.get()); + mpWindow->SetPosSizePixel(Point(0,0), mpWorkWindow->GetSizePixel()); + mpWindow->SetBackground(Wallpaper()); + mxWindow = VCLUnoHelper::GetInterface(mpWindow); + + // Create the canvas. + mxCanvas = CreateCanvas(); + + mpWindow->GrabFocus(); +} + +FullScreenPane::~FullScreenPane() noexcept +{ +} + +void SAL_CALL FullScreenPane::disposing() +{ + mpWindow.disposeAndClear(); + + if (mpWorkWindow) + { + Link aWindowEventHandler (LINK(this, FullScreenPane, WindowEventHandler)); + mpWorkWindow->RemoveEventListener(aWindowEventHandler); + mpWorkWindow.disposeAndClear(); + } + + FrameWindowPane::disposing(); +} + +//----- XPane ----------------------------------------------------------------- + +sal_Bool SAL_CALL FullScreenPane::isVisible() +{ + ThrowIfDisposed(); + + if (mpWindow != nullptr) + return mpWindow->IsReallyVisible(); + else + return false; +} + +void SAL_CALL FullScreenPane::setVisible (const sal_Bool bIsVisible) +{ + ThrowIfDisposed(); + + if (mpWindow != nullptr) + mpWindow->Show(bIsVisible); + if (mpWorkWindow != nullptr) + mpWorkWindow->Show(bIsVisible); +} + +Reference SAL_CALL FullScreenPane::getAccessible() +{ + ThrowIfDisposed(); + + if (mpWorkWindow != nullptr) + return mpWorkWindow->GetAccessible(false); + else + return nullptr; +} + +void SAL_CALL FullScreenPane::setAccessible ( + const Reference& rxAccessible) +{ + ThrowIfDisposed(); + + if (mpWindow == nullptr) + return; + + Reference xInitializable (rxAccessible, UNO_QUERY); + if (xInitializable.is()) + { + vcl::Window* pParentWindow = mpWindow->GetParent(); + Reference xAccessibleParent; + if (pParentWindow != nullptr) + xAccessibleParent = pParentWindow->GetAccessible(); + Sequence aArguments{ Any(xAccessibleParent) }; + xInitializable->initialize(aArguments); + } + GetWindow()->SetAccessible(rxAccessible); +} + +IMPL_LINK(FullScreenPane, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowResize: + GetWindow()->SetPosPixel(Point(0,0)); + GetWindow()->SetSizePixel(Size( + mpWorkWindow->GetSizePixel().Width(), + mpWorkWindow->GetSizePixel().Height())); + break; + + case VclEventId::ObjectDying: + mpWorkWindow.disposeAndClear(); + break; + + default: break; + } +} + +Reference FullScreenPane::CreateCanvas() +{ + VclPtr pWindow = VCLUnoHelper::GetWindow(mxWindow); + if (!pWindow) + throw RuntimeException(); + + Sequence aArg{ // common: first any is VCL pointer to window (for VCL canvas) + Any(reinterpret_cast(pWindow.get())), + Any(css::awt::Rectangle()), + Any(false), + Any(mxWindow) + }; + + Reference xFactory ( + mxComponentContext->getServiceManager(), UNO_QUERY_THROW); + return Reference( + xFactory->createInstanceWithArguments("com.sun.star.rendering.SpriteCanvas.VCL", + aArg), + UNO_QUERY); +} + +void FullScreenPane::ExtractArguments ( + const Reference& rxPaneId, + sal_Int32& rnScreenNumberReturnValue) +{ + // Extract arguments from the resource URL. + const util::URL aURL = rxPaneId->getFullResourceURL(); + for (sal_Int32 nIndex{ 0 }; nIndex >= 0; ) + { + const std::u16string_view aToken = o3tl::getToken(aURL.Arguments, 0, '&', nIndex); + std::u16string_view sValue; + if (o3tl::starts_with(aToken, u"ScreenNumber=", &sValue)) + { + rnScreenNumberReturnValue = o3tl::toInt32(sValue); + } + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/FullScreenPane.hxx b/sd/source/ui/framework/factories/FullScreenPane.hxx new file mode 100644 index 000000000..b33804ee5 --- /dev/null +++ b/sd/source/ui/framework/factories/FullScreenPane.hxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "FrameWindowPane.hxx" +#include +#include + +class VclWindowEvent; + +namespace vcl { class Window; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::framework { + +/** The full screen pane creates a pane that covers the complete application + window, i.e. that hides menu bar, tool bars, status bars. +*/ +class FullScreenPane + : public FrameWindowPane +{ +public: + /** Create a new full screen pane. + @param rxComponentContext + Used for creating a new canvas. + @param rxPaneId + The resource id of the new pane. + @param pViewShellWindow + The top-level parent of this window is used to obtain title and + icon for the new top-level window. + */ + FullScreenPane ( + const css::uno::Reference& rxComponentContext, + const css::uno::Reference& rxPaneId, + const vcl::Window* pViewShellWindow); + virtual ~FullScreenPane() noexcept override; + + virtual void SAL_CALL disposing() override; + + //----- XPane ------------------------------------------------------------- + + virtual sal_Bool SAL_CALL isVisible() override; + + virtual void SAL_CALL setVisible (sal_Bool bIsVisible) override; + + virtual css::uno::Reference SAL_CALL getAccessible() override; + + virtual void SAL_CALL setAccessible ( + const css::uno::Reference& rxAccessible) override; + + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + +protected: + virtual css::uno::Reference + CreateCanvas() override; + +private: + css::uno::Reference mxComponentContext; + VclPtr mpWorkWindow; + + static void ExtractArguments ( + const css::uno::Reference& rxPaneId, + sal_Int32& rnScreenNumberReturnValue); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/Pane.cxx b/sd/source/ui/framework/factories/Pane.cxx new file mode 100644 index 000000000..a188f0e11 --- /dev/null +++ b/sd/source/ui/framework/factories/Pane.cxx @@ -0,0 +1,178 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd::framework { + +Pane::Pane ( + const Reference& rxPaneId, + vcl::Window* pWindow) + noexcept + : PaneInterfaceBase(m_aMutex), + mxPaneId(rxPaneId), + mpWindow(pWindow), + mxWindow(VCLUnoHelper::GetInterface(pWindow)) +{ +} + +Pane::~Pane() +{ +} + +void Pane::disposing() +{ + mxWindow = nullptr; + mpWindow = nullptr; +} + +vcl::Window* Pane::GetWindow() +{ + if (mxWindow.is()) + return mpWindow; + else + return nullptr; +} + +//----- XPane ----------------------------------------------------------------- + +Reference SAL_CALL Pane::getWindow() +{ + ThrowIfDisposed(); + + return mxWindow; +} + +Reference SAL_CALL Pane::getCanvas() +{ + ::osl::MutexGuard aGuard (m_aMutex); + ThrowIfDisposed(); + + if ( ! mxCanvas.is()) + mxCanvas = CreateCanvas(); + + return mxCanvas; +} + +//----- XPane2 ---------------------------------------------------------------- + +sal_Bool SAL_CALL Pane::isVisible() +{ + ThrowIfDisposed(); + + const vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + return pWindow->IsVisible(); + else + return false; +} + +void SAL_CALL Pane::setVisible (sal_Bool bIsVisible) +{ + ThrowIfDisposed(); + + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + pWindow->Show(bIsVisible); +} + +Reference SAL_CALL Pane::getAccessible() +{ + ThrowIfDisposed(); + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + return pWindow->GetAccessible(false); + else + return nullptr; +} + +void SAL_CALL Pane::setAccessible ( + const Reference& rxAccessible) +{ + ThrowIfDisposed(); + vcl::Window* pWindow = GetWindow(); + if (pWindow != nullptr) + pWindow->SetAccessible(rxAccessible); +} + +//----- XResource ------------------------------------------------------------- + +Reference SAL_CALL Pane::getResourceId() +{ + ThrowIfDisposed(); + + return mxPaneId; +} + +sal_Bool SAL_CALL Pane::isAnchorOnly() +{ + return true; +} + +//----- XUnoTunnel ------------------------------------------------------------ + +const Sequence& Pane::getUnoTunnelId() +{ + static const comphelper::UnoIdInit thePaneUnoTunnelId; + return thePaneUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL Pane::getSomething (const Sequence& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +Reference Pane::CreateCanvas() +{ + Reference xCanvas; + + if (mpWindow != nullptr) + { + ::cppcanvas::SpriteCanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createSpriteCanvas(*mpWindow)); + if (pCanvas) + xCanvas.set(pCanvas->getUNOSpriteCanvas()); + } + + return xCanvas; +} + +void Pane::ThrowIfDisposed() const +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + throw lang::DisposedException ("Pane object has already been disposed", + const_cast(static_cast(this))); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/PresentationFactory.cxx b/sd/source/ui/framework/factories/PresentationFactory.cxx new file mode 100644 index 000000000..8cf603809 --- /dev/null +++ b/sd/source/ui/framework/factories/PresentationFactory.cxx @@ -0,0 +1,192 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing::framework; + + +namespace sd::framework { + +namespace { + +typedef comphelper::WeakComponentImplHelper PresentationFactoryProviderInterfaceBase; + +class PresentationFactoryProvider + : public PresentationFactoryProviderInterfaceBase +{ +public: + PresentationFactoryProvider (); + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence& aArguments) override; +}; + +typedef comphelper::WeakComponentImplHelper PresentationViewInterfaceBase; + +/** The PresentationView is not an actual view, it is a marker whose + existence in a configuration indicates that a slideshow is running + (in another application window). +*/ +class PresentationView + : public PresentationViewInterfaceBase +{ +public: + explicit PresentationView (const Reference& rxViewId) + : mxResourceId(rxViewId) {}; + + // XView + + virtual Reference SAL_CALL getResourceId() override + { return mxResourceId; }; + + virtual sal_Bool SAL_CALL isAnchorOnly() override + { return false; } + +private: + Reference mxResourceId; +}; + +} // end of anonymous namespace. + +//===== PresentationFactory =================================================== + +constexpr OUStringLiteral gsPresentationViewURL = u"private:resource/view/Presentation"; + +PresentationFactory::PresentationFactory ( + const Reference& rxController) + : mxController(rxController) +{ +} + +PresentationFactory::~PresentationFactory() +{ +} + +//----- XViewFactory ---------------------------------------------------------- + +Reference SAL_CALL PresentationFactory::createResource ( + const Reference& rxViewId) +{ + ThrowIfDisposed(); + + if (rxViewId.is()) + if ( ! rxViewId->hasAnchor() && rxViewId->getResourceURL() == gsPresentationViewURL) + return new PresentationView(rxViewId); + + return Reference(); +} + +void SAL_CALL PresentationFactory::releaseResource ( + const Reference&) +{ + ThrowIfDisposed(); + + auto pController = comphelper::getFromUnoTunnel(mxController); + if (pController != nullptr) + { + ViewShellBase* pBase = pController->GetViewShellBase(); + if (pBase != nullptr) + SlideShow::Stop( *pBase ); + } +} + +//===== XConfigurationChangeListener ========================================== + +void SAL_CALL PresentationFactory::notifyConfigurationChange ( + const ConfigurationChangeEvent&) +{} + +//===== lang::XEventListener ================================================== + +void SAL_CALL PresentationFactory::disposing ( + const lang::EventObject&) +{} + +void PresentationFactory::ThrowIfDisposed() const +{ + if (m_bDisposed) + { + throw lang::DisposedException ("PresentationFactory object has already been disposed", + const_cast(static_cast(this))); + } +} + +namespace { + +//===== PresentationFactoryProvider =========================================== + +PresentationFactoryProvider::PresentationFactoryProvider () +{ +} + +// XInitialization + +void SAL_CALL PresentationFactoryProvider::initialize( + const Sequence& aArguments) +{ + if (!aArguments.hasElements()) + return; + + try + { + // Get the XController from the first argument. + Reference xController (aArguments[0], UNO_QUERY_THROW); + Reference xCM (xController, UNO_QUERY_THROW); + Reference xCC (xCM->getConfigurationController()); + if (xCC.is()) + xCC->addResourceFactory( + gsPresentationViewURL, + new PresentationFactory(xController)); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +} // end of anonymous namespace. + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_PresentationFactoryProvider_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::framework::PresentationFactoryProvider); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/factories/ViewShellWrapper.cxx b/sd/source/ui/framework/factories/ViewShellWrapper.cxx new file mode 100644 index 000000000..8f0fcd976 --- /dev/null +++ b/sd/source/ui/framework/factories/ViewShellWrapper.cxx @@ -0,0 +1,252 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::com::sun::star::awt::XWindow; + +namespace sd::framework { + +ViewShellWrapper::ViewShellWrapper ( + const std::shared_ptr& pViewShell, + const Reference& rxViewId, + const Reference& rxWindow) + : mpViewShell(pViewShell), + mpSlideSorterViewShell( + std::dynamic_pointer_cast< ::sd::slidesorter::SlideSorterViewShell >( pViewShell )), + mxViewId(rxViewId), + mxWindow(rxWindow) +{ +} + +ViewShellWrapper::~ViewShellWrapper() +{ +} + +void ViewShellWrapper::disposing(std::unique_lock&) +{ + SAL_INFO("sd.ui", "disposing ViewShellWrapper " << this); + Reference xWindow (mxWindow); + if (xWindow.is()) + { + SAL_INFO( + "sd.ui", + "removing ViewShellWrapper " << this << " from window listener at " + << mxWindow.get()); + xWindow->removeWindowListener(this); + } + + mpSlideSorterViewShell.reset(); + mpViewShell.reset(); +} + +uno::Any SAL_CALL ViewShellWrapper::queryInterface( const uno::Type & rType ) +{ + if( mpSlideSorterViewShell && + rType == cppu::UnoType::get() ) + { + uno::Reference xSupplier( this ); + return Any(xSupplier); + } + else + return ViewShellWrapperInterfaceBase::queryInterface( rType ); +} + +//----- XResource ------------------------------------------------------------- + +Reference SAL_CALL ViewShellWrapper::getResourceId() +{ + return mxViewId; +} + +sal_Bool SAL_CALL ViewShellWrapper::isAnchorOnly() +{ + return false; +} + +//----- XSelectionSupplier -------------------------------------------------- + +sal_Bool SAL_CALL ViewShellWrapper::select( const css::uno::Any& aSelection ) +{ + if (!mpSlideSorterViewShell) + return false; + + ::sd::slidesorter::controller::SlideSorterController& rSlideSorterController + = mpSlideSorterViewShell->GetSlideSorter().GetController(); + ::sd::slidesorter::controller::PageSelector& rSelector (rSlideSorterController.GetPageSelector()); + rSelector.DeselectAllPages(); + Sequence > xPages; + aSelection >>= xPages; + for (const auto& rPage : std::as_const(xPages)) + { + Reference xSet (rPage, UNO_QUERY); + if (xSet.is()) + { + try + { + Any aNumber = xSet->getPropertyValue("Number"); + sal_Int32 nPageNumber = 0; + aNumber >>= nPageNumber; + nPageNumber -=1; // Transform 1-based page numbers to 0-based ones. + rSelector.SelectPage(nPageNumber); + } + catch (const RuntimeException&) + { + } + } + } + + return true; +} + +uno::Any SAL_CALL ViewShellWrapper::getSelection() +{ + Any aResult; + + if (!mpSlideSorterViewShell) + return aResult; + + slidesorter::model::PageEnumeration aSelectedPages ( + slidesorter::model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mpSlideSorterViewShell->GetSlideSorter().GetModel())); + int nSelectedPageCount ( + mpSlideSorterViewShell->GetSlideSorter().GetController().GetPageSelector().GetSelectedPageCount()); + + Sequence > aPages(nSelectedPageCount); + auto aPagesRange = asNonConstRange(aPages); + int nIndex = 0; + while (aSelectedPages.HasMoreElements() && nIndexGetPage()->getUnoPage(); + } + aResult <<= aPages; + + return aResult; +} + +void SAL_CALL ViewShellWrapper::addSelectionChangeListener( const uno::Reference< view::XSelectionChangeListener >& ) +{ +} + +void SAL_CALL ViewShellWrapper::removeSelectionChangeListener( const uno::Reference< view::XSelectionChangeListener >& ) +{ +} + +//----- XRelocatableResource -------------------------------------------------- + +sal_Bool SAL_CALL ViewShellWrapper::relocateToAnchor ( + const Reference& xResource) +{ + bool bResult (false); + + Reference xPane (xResource, UNO_QUERY); + if (xPane.is()) + { + // Detach from the window of the old pane. + Reference xWindow (mxWindow); + if (xWindow.is()) + xWindow->removeWindowListener(this); + mxWindow = nullptr; + + if (mpViewShell != nullptr) + { + VclPtr pWindow = VCLUnoHelper::GetWindow(xPane->getWindow()); + if (pWindow && mpViewShell->RelocateToParentWindow(pWindow)) + { + bResult = true; + + // Attach to the window of the new pane. + xWindow = xPane->getWindow(); + if (xWindow.is()) + { + xWindow->addWindowListener(this); + mpViewShell->Resize(); + } + } + } + } + + return bResult; +} + +//----- XUnoTunnel ------------------------------------------------------------ + +const Sequence& ViewShellWrapper::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theViewShellWrapperUnoTunnelId; + return theViewShellWrapperUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ViewShellWrapper::getSomething (const Sequence& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +//===== awt::XWindowListener ================================================== + +void SAL_CALL ViewShellWrapper::windowResized (const awt::WindowEvent&) +{ + ViewShell* pViewShell (mpViewShell.get()); + if (pViewShell != nullptr) + pViewShell->Resize(); +} + +void SAL_CALL ViewShellWrapper::windowMoved (const awt::WindowEvent&) {} + +void SAL_CALL ViewShellWrapper::windowShown (const lang::EventObject&) +{ + ViewShell* pViewShell (mpViewShell.get()); + if (pViewShell != nullptr) + pViewShell->Resize(); +} + +void SAL_CALL ViewShellWrapper::windowHidden (const lang::EventObject&) {} + +//===== XEventListener ======================================================== + +void SAL_CALL ViewShellWrapper::disposing (const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxWindow) + mxWindow = nullptr; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/CenterViewFocusModule.cxx b/sd/source/ui/framework/module/CenterViewFocusModule.cxx new file mode 100644 index 000000000..e36f95e33 --- /dev/null +++ b/sd/source/ui/framework/module/CenterViewFocusModule.cxx @@ -0,0 +1,151 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "CenterViewFocusModule.hxx" + +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//===== CenterViewFocusModule ==================================================== + +CenterViewFocusModule::CenterViewFocusModule (Reference const & rxController) + : mbValid(false), + mpBase(nullptr), + mbNewViewCreated(false) +{ + Reference xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + // Check, if all required objects do exist. + if (mxConfigurationController.is() && mpBase!=nullptr) + { + mbValid = true; + } + } + + if (mbValid) + { + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any()); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any()); + } +} + +CenterViewFocusModule::~CenterViewFocusModule() +{ +} + +void CenterViewFocusModule::disposing(std::unique_lock&) +{ + if (mxConfigurationController.is()) + mxConfigurationController->removeConfigurationChangeListener(this); + + mbValid = false; + mxConfigurationController = nullptr; + mpBase = nullptr; +} + +void SAL_CALL CenterViewFocusModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (mbValid) + { + if (rEvent.Type == FrameworkHelper::msConfigurationUpdateEndEvent) + { + HandleNewView(rEvent.Configuration); + } + else if (rEvent.Type == FrameworkHelper::msResourceActivationEvent) + { + if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) + mbNewViewCreated = true; + } + } +} + +void CenterViewFocusModule::HandleNewView ( + const Reference& rxConfiguration) +{ + if (!mbNewViewCreated) + return; + + mbNewViewCreated = false; + // Make the center pane the active one. Tunnel through the + // controller to obtain a ViewShell pointer. + + Sequence > xViewIds (rxConfiguration->getResources( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + FrameworkHelper::msViewURLPrefix, + AnchorBindingMode_DIRECT)); + Reference xView; + if (xViewIds.hasElements()) + xView.set( mxConfigurationController->getResource(xViewIds[0]),UNO_QUERY); + if (mpBase!=nullptr) + { + auto pViewShellWrapper = comphelper::getFromUnoTunnel(xView); + if (pViewShellWrapper != nullptr) + { + std::shared_ptr pViewShell = pViewShellWrapper->GetViewShell(); + if (pViewShell != nullptr) + mpBase->GetViewShellManager()->MoveToTop(*pViewShell); + } + } +} + +void SAL_CALL CenterViewFocusModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is()) + if (rEvent.Source == mxConfigurationController) + { + mbValid = false; + mxConfigurationController = nullptr; + mpBase = nullptr; + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/CenterViewFocusModule.hxx b/sd/source/ui/framework/module/CenterViewFocusModule.hxx new file mode 100644 index 000000000..c6d5d348e --- /dev/null +++ b/sd/source/ui/framework/module/CenterViewFocusModule.hxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} +namespace com::sun::star::frame +{ +class XController; +} + +namespace sd +{ +class ViewShellBase; +} + +namespace sd::framework +{ +typedef comphelper::WeakComponentImplHelper + CenterViewFocusModuleInterfaceBase; + +/** This module waits for new views to be created for the center pane and + then moves the center view to the top most place on the shell stack. As + we are moving away from the shell stack this module may become obsolete + or has to be modified. +*/ +class CenterViewFocusModule final : public CenterViewFocusModuleInterfaceBase +{ +public: + explicit CenterViewFocusModule( + css::uno::Reference const& rxController); + virtual ~CenterViewFocusModule() override; + + virtual void disposing(std::unique_lock&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override; + +private: + class ViewShellContainer; + + bool mbValid; + css::uno::Reference + mxConfigurationController; + ViewShellBase* mpBase; + /** This flag indicates whether in the last configuration change cycle a + new view has been created and thus the center view has to be moved + to the top of the shell stack. + */ + bool mbNewViewCreated; + + /** At the end of an update of the current configuration this method + handles a new view in the center pane by moving the associated view + shell to the top of the shell stack. + */ + void HandleNewView( + const css::uno::Reference& rxConfiguration); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/DrawModule.cxx b/sd/source/ui/framework/module/DrawModule.cxx new file mode 100644 index 000000000..17f4671fc --- /dev/null +++ b/sd/source/ui/framework/module/DrawModule.cxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include "CenterViewFocusModule.hxx" +#include "SlideSorterModule.hxx" +#include "ToolBarModule.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework +{ +void DrawModule::Initialize(Reference const& rxController) +{ + new sd::framework::CenterViewFocusModule(rxController); + new sd::framework::SlideSorterModule(rxController, FrameworkHelper::msLeftDrawPaneURL); + new ToolBarModule(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ImpressModule.cxx b/sd/source/ui/framework/module/ImpressModule.cxx new file mode 100644 index 000000000..139b250fd --- /dev/null +++ b/sd/source/ui/framework/module/ImpressModule.cxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include "ViewTabBarModule.hxx" +#include "CenterViewFocusModule.hxx" +#include "SlideSorterModule.hxx" +#include "ToolBarModule.hxx" +#include "ShellStackGuard.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework { + +void ImpressModule::Initialize (Reference const & rxController) +{ + new CenterViewFocusModule(rxController); + new ViewTabBarModule( + rxController, + FrameworkHelper::CreateResourceId( + FrameworkHelper::msViewTabBarURL, + FrameworkHelper::msCenterPaneURL)); + new SlideSorterModule( + rxController, + FrameworkHelper::msLeftImpressPaneURL); + new ToolBarModule(rxController); + new ShellStackGuard(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ModuleController.cxx b/sd/source/ui/framework/module/ModuleController.cxx new file mode 100644 index 000000000..acd12ec8a --- /dev/null +++ b/sd/source/ui/framework/module/ModuleController.cxx @@ -0,0 +1,244 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::tools::ConfigurationAccess; + +namespace sd::framework { + +const sal_uInt32 snFactoryPropertyCount (2); +const sal_uInt32 snStartupPropertyCount (1); + +//===== ModuleController ====================================================== +Reference ModuleController::CreateInstance ( + const Reference& rxContext) +{ + return new ModuleController(rxContext); +} + +ModuleController::ModuleController (const Reference& rxContext) +{ + /** Load a list of URL to service mappings from the + /org.openoffice.Office.Impress/MultiPaneGUI/Framework/ResourceFactories + configuration entry. The mappings are stored in the + mpResourceToFactoryMap member. + */ + try + { + ConfigurationAccess aConfiguration ( + rxContext, + "/org.openoffice.Office.Impress/", + ConfigurationAccess::READ_ONLY); + Reference xFactories ( + aConfiguration.GetConfigurationNode("MultiPaneGUI/Framework/ResourceFactories"), + UNO_QUERY); + ::std::vector aProperties (snFactoryPropertyCount); + aProperties[0] = "ServiceName"; + aProperties[1] = "ResourceList"; + ConfigurationAccess::ForAll( + xFactories, + aProperties, + [this] (OUString const&, ::std::vector const& xs) { + return this->ProcessFactory(xs); + } ); + } + catch (Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +ModuleController::~ModuleController() noexcept +{ +} + +void ModuleController::disposing(std::unique_lock&) +{ + // Break the cyclic reference back to DrawController object + maLoadedFactories.clear(); + maResourceToFactoryMap.clear(); + mxController.clear(); +} + +void ModuleController::ProcessFactory (const ::std::vector& rValues) +{ + OSL_ASSERT(rValues.size() == snFactoryPropertyCount); + + // Get the service name of the factory. + OUString sServiceName; + rValues[0] >>= sServiceName; + + // Get all resource URLs that are created by the factory. + Reference xResources (rValues[1], UNO_QUERY); + ::std::vector aURLs; + tools::ConfigurationAccess::FillList( + xResources, + "URL", + aURLs); + + SAL_INFO("sd.fwk", __func__ << ": ModuleController::adding factory " << sServiceName); + + // Add the resource URLs to the map. + for (const auto& rResource : aURLs) + { + maResourceToFactoryMap[rResource] = sServiceName; + SAL_INFO("sd.fwk", __func__ << ": " << rResource); + } +} + +void ModuleController::InstantiateStartupServices() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + "/org.openoffice.Office.Impress/", + tools::ConfigurationAccess::READ_ONLY); + Reference xFactories ( + aConfiguration.GetConfigurationNode("MultiPaneGUI/Framework/StartupServices"), + UNO_QUERY); + ::std::vector aProperties (snStartupPropertyCount); + aProperties[0] = "ServiceName"; + tools::ConfigurationAccess::ForAll( + xFactories, + aProperties, + [this] (OUString const&, ::std::vector const& xs) { + return this->ProcessStartupService(xs); + } ); + } + catch (Exception&) + { + SAL_WARN("sd.fwk", "ERROR in ModuleController::InstantiateStartupServices"); + } +} + +void ModuleController::ProcessStartupService (const ::std::vector& rValues) +{ + OSL_ASSERT(rValues.size() == snStartupPropertyCount); + + try + { + // Get the service name of the startup service. + OUString sServiceName; + rValues[0] >>= sServiceName; + + // Instantiate service. + Reference xContext = + ::comphelper::getProcessComponentContext(); + + // Create the startup service. + Sequence aArguments{ Any(mxController) }; + // Note that when the new object will be destroyed at the end of + // this scope when it does not register itself anywhere. + // Typically it will add itself as ConfigurationChangeListener + // at the configuration controller. + xContext->getServiceManager()->createInstanceWithArgumentsAndContext(sServiceName, aArguments, xContext); + + SAL_INFO("sd.fwk", __func__ << ": ModuleController::created startup service " << sServiceName); + } + catch (Exception&) + { + SAL_WARN("sd.fwk", "ERROR in ModuleController::ProcessStartupServices"); + } +} + +//----- XModuleController ----------------------------------------------------- + +void SAL_CALL ModuleController::requestResource (const OUString& rsResourceURL) +{ + auto iFactory = maResourceToFactoryMap.find(rsResourceURL); + if (iFactory == maResourceToFactoryMap.end()) + return; + + // Check that the factory has already been loaded and not been + // destroyed in the meantime. + Reference xFactory; + auto iLoadedFactory = maLoadedFactories.find(iFactory->second); + if (iLoadedFactory != maLoadedFactories.end()) + xFactory.set(iLoadedFactory->second, UNO_QUERY); + if ( xFactory.is()) + return; + + // Create a new instance of the factory. + Reference xContext = + ::comphelper::getProcessComponentContext(); + + // Create the factory service. + Sequence aArguments{ Any(mxController) }; + try + { + xFactory = xContext->getServiceManager()->createInstanceWithArgumentsAndContext( + iFactory->second, + aArguments, + xContext); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd.fwk", "caught exception while creating factory"); + } + + // Remember that this factory has been instanced. + maLoadedFactories[iFactory->second] = xFactory; +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL ModuleController::initialize (const Sequence& aArguments) +{ + if (aArguments.hasElements()) + { + try + { + // Get the XController from the first argument. + mxController.set(aArguments[0], UNO_QUERY_THROW); + + InstantiateStartupServices(); + } + catch (RuntimeException&) + {} + } +} + +} // end of namespace sd::framework + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_framework_module_ModuleController_get_implementation( + css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + css::uno::Reference< css::uno::XInterface > xModCont ( sd::framework::ModuleController::CreateInstance(context) ); + xModCont->acquire(); + return xModCont.get(); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/PresentationModule.cxx b/sd/source/ui/framework/module/PresentationModule.cxx new file mode 100644 index 000000000..fb0ac0558 --- /dev/null +++ b/sd/source/ui/framework/module/PresentationModule.cxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include "CenterViewFocusModule.hxx" + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::framework +{ +void PresentationModule::Initialize(Reference const& rxController) +{ + new sd::framework::CenterViewFocusModule(rxController); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ShellStackGuard.cxx b/sd/source/ui/framework/module/ShellStackGuard.cxx new file mode 100644 index 000000000..83d73b055 --- /dev/null +++ b/sd/source/ui/framework/module/ShellStackGuard.cxx @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ShellStackGuard.hxx" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace sd::framework { + +//===== CenterViewFocusModule ==================================================== + +ShellStackGuard::ShellStackGuard (Reference const & rxController) + : mpBase(nullptr), + maPrinterPollingIdle("sd ShellStackGuard PrinterPollingIdle") +{ + Reference xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + } + + if (mxConfigurationController.is()) + { + // Listen for update starts so that the following update can be + // prevented in case of a printing printer. + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any()); + + // Prepare the printer polling. + maPrinterPollingIdle.SetInvokeHandler(LINK(this,ShellStackGuard,TimeoutHandler)); + } +} + +ShellStackGuard::~ShellStackGuard() +{ +} + +void ShellStackGuard::disposing(std::unique_lock&) +{ + if (mxConfigurationController) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } + mpBase = nullptr; +} + +void SAL_CALL ShellStackGuard::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (rEvent.Type == FrameworkHelper::msConfigurationUpdateStartEvent) + { + if (mpUpdateLock == nullptr && IsPrinting()) + { + // Prevent configuration updates while the printer is printing. + mpUpdateLock.reset(new ConfigurationController::Lock(mxConfigurationController)); + + // Start polling for the printer having finished printing. + maPrinterPollingIdle.Start(); + } + } +} + +void SAL_CALL ShellStackGuard::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is()) + if (rEvent.Source == mxConfigurationController) + { + mxConfigurationController = nullptr; + mpBase = nullptr; + } +} + +IMPL_LINK(ShellStackGuard, TimeoutHandler, Timer*, pIdle, void) +{ +#ifdef DEBUG + OSL_ASSERT(pIdle==&maPrinterPollingIdle); +#else + (void)pIdle; +#endif + if (mpUpdateLock == nullptr) + return; + + if ( ! IsPrinting()) + { + // Printing finished. Release the update lock. + mpUpdateLock.reset(); + } + else + { + // Wait long for the printing to finish. + maPrinterPollingIdle.Start(); + } +} + +bool ShellStackGuard::IsPrinting() const +{ + if (mpBase != nullptr) + { + SfxPrinter* pPrinter = mpBase->GetPrinter(); + if (pPrinter != nullptr + && pPrinter->IsPrinting()) + { + return true; + } + } + + return false; +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ShellStackGuard.hxx b/sd/source/ui/framework/module/ShellStackGuard.hxx new file mode 100644 index 000000000..72b7ed2c6 --- /dev/null +++ b/sd/source/ui/framework/module/ShellStackGuard.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} +namespace com::sun::star::frame +{ +class XController; +} + +namespace sd +{ +class ViewShellBase; +} + +namespace sd::framework +{ +typedef comphelper::WeakComponentImplHelper + ShellStackGuardInterfaceBase; + +/** This module locks updates of the current configuration in situations + when the shell stack must not be modified. + + On every start of a configuration update the ShellStackGuard checks the + printer. If it is printing the configuration update is locked. It then + polls the printer and unlocks updates when printing finishes. + + When in the future there are no resources left that use shells then this + module can be removed. +*/ +class ShellStackGuard : public ShellStackGuardInterfaceBase +{ +public: + explicit ShellStackGuard(css::uno::Reference const& rxController); + virtual ~ShellStackGuard() override; + + virtual void disposing(std::unique_lock&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing(const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference + mxConfigurationController; + ViewShellBase* mpBase; + std::unique_ptr mpUpdateLock; + Idle maPrinterPollingIdle; + + DECL_LINK(TimeoutHandler, Timer*, void); + + /** Return when the printer is printing. Return when + the printer is not printing, or there is no printer, or something + else went wrong. + */ + bool IsPrinting() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/SlideSorterModule.cxx b/sd/source/ui/framework/module/SlideSorterModule.cxx new file mode 100644 index 000000000..dbe30f0d3 --- /dev/null +++ b/sd/source/ui/framework/module/SlideSorterModule.cxx @@ -0,0 +1,313 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlideSorterModule.hxx" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + const sal_Int32 ResourceActivationRequestEvent = 0; + const sal_Int32 ResourceDeactivationRequestEvent = 1; +} + +namespace sd::framework { + +//===== SlideSorterModule ================================================== + +SlideSorterModule::SlideSorterModule ( + const Reference& rxController, + const OUString& rsLeftPaneURL) + : mxResourceId(FrameworkHelper::CreateResourceId(FrameworkHelper::msSlideSorterURL, rsLeftPaneURL)), + mxMainViewAnchorId(FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL)), + mxViewTabBarId(FrameworkHelper::CreateResourceId( + FrameworkHelper::msViewTabBarURL, + FrameworkHelper::msCenterPaneURL)), + mxControllerManager(rxController,UNO_QUERY) +{ + Reference xControllerManager (rxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + + if (mxConfigurationController.is()) + { + uno::Reference const xComppnent( + mxConfigurationController, UNO_QUERY_THROW); + xComppnent->addEventListener(this); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(ResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(ResourceDeactivationRequestEvent)); + } + } + if (!mxConfigurationController.is()) + return; + + UpdateViewTabBar(nullptr); + + if (SvtSlideSorterBarOptions().GetVisibleImpressView()) + AddActiveMainView(FrameworkHelper::msImpressViewURL); + if (SvtSlideSorterBarOptions().GetVisibleOutlineView()) + AddActiveMainView(FrameworkHelper::msOutlineViewURL); + if (SvtSlideSorterBarOptions().GetVisibleNotesView()) + AddActiveMainView(FrameworkHelper::msNotesViewURL); + if (SvtSlideSorterBarOptions().GetVisibleHandoutView()) + AddActiveMainView(FrameworkHelper::msHandoutViewURL); + if (SvtSlideSorterBarOptions().GetVisibleSlideSorterView()) + AddActiveMainView(FrameworkHelper::msSlideSorterURL); + if (SvtSlideSorterBarOptions().GetVisibleDrawView()) + AddActiveMainView(FrameworkHelper::msDrawViewURL); + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any()); +} + +SlideSorterModule::~SlideSorterModule() +{ +} + +void SlideSorterModule::SaveResourceState() +{ + SvtSlideSorterBarOptions().SetVisibleImpressView(IsResourceActive(FrameworkHelper::msImpressViewURL)); + SvtSlideSorterBarOptions().SetVisibleOutlineView(IsResourceActive(FrameworkHelper::msOutlineViewURL)); + SvtSlideSorterBarOptions().SetVisibleNotesView(IsResourceActive(FrameworkHelper::msNotesViewURL)); + SvtSlideSorterBarOptions().SetVisibleHandoutView(IsResourceActive(FrameworkHelper::msHandoutViewURL)); + SvtSlideSorterBarOptions().SetVisibleSlideSorterView(IsResourceActive(FrameworkHelper::msSlideSorterURL)); + SvtSlideSorterBarOptions().SetVisibleDrawView(IsResourceActive(FrameworkHelper::msDrawViewURL)); +} + +void SAL_CALL SlideSorterModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (rEvent.Type == FrameworkHelper::msResourceActivationEvent) + { + if (rEvent.ResourceId->compareTo(mxViewTabBarId) == 0) + { + // Update the view tab bar because the view tab bar has just + // become active. + UpdateViewTabBar(Reference(rEvent.ResourceObject,UNO_QUERY)); + } + else if (rEvent.ResourceId->getResourceTypePrefix() == + FrameworkHelper::msViewURLPrefix + && rEvent.ResourceId->isBoundTo( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + AnchorBindingMode_DIRECT)) + { + // Update the view tab bar because the view in the center pane + // has changed. + UpdateViewTabBar(nullptr); + } + return; + } + + OSL_ASSERT(rEvent.ResourceId.is()); + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case ResourceActivationRequestEvent: + if (rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, + AnchorBindingMode_DIRECT)) + { + // A resource directly bound to the center pane has been + // requested. + if (rEvent.ResourceId->getResourceTypePrefix() == + FrameworkHelper::msViewURLPrefix) + { + // The requested resource is a view. Show or hide the + // resource managed by this ResourceManager accordingly. + HandleMainViewSwitch( + rEvent.ResourceId->getResourceURL(), + true); + } + } + else if (rEvent.ResourceId->compareTo(mxResourceId) == 0) + { + // The resource managed by this ResourceManager has been + // explicitly been requested (maybe by us). Remember this + // setting. + HandleResourceRequest(true, rEvent.Configuration); + } + break; + + case ResourceDeactivationRequestEvent: + if (rEvent.ResourceId->compareTo(mxMainViewAnchorId) == 0) + { + HandleMainViewSwitch( + OUString(), + false); + } + else if (rEvent.ResourceId->compareTo(mxResourceId) == 0) + { + // The resource managed by this ResourceManager has been + // explicitly been requested to be hidden (maybe by us). + // Remember this setting. + HandleResourceRequest(false, rEvent.Configuration); + } + break; + } +} + +void SlideSorterModule::UpdateViewTabBar (const Reference& rxTabBar) +{ + if ( ! mxControllerManager.is()) + return; + + Reference xBar (rxTabBar); + if ( ! xBar.is()) + { + Reference xCC ( + mxControllerManager->getConfigurationController()); + if (xCC.is()) + xBar.set(xCC->getResource(mxViewTabBarId), UNO_QUERY); + } + + if (!xBar.is()) + return; + + TabBarButton aButtonA; + aButtonA.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msSlideSorterURL, + FrameworkHelper::msCenterPaneURL); + aButtonA.ButtonLabel = SdResId(STR_SLIDE_SORTER_MODE); + + TabBarButton aButtonB; + aButtonB.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msHandoutViewURL, + FrameworkHelper::msCenterPaneURL); + + if ( ! xBar->hasTabBarButton(aButtonA)) + xBar->addTabBarButtonAfter(aButtonA, aButtonB); +} + +void SlideSorterModule::AddActiveMainView ( + const OUString& rsMainViewURL) +{ + maActiveMainViewContainer.insert(rsMainViewURL); +} + +bool SlideSorterModule::IsResourceActive ( + const OUString& rsMainViewURL) +{ + return (maActiveMainViewContainer.find(rsMainViewURL) != maActiveMainViewContainer.end()); +} + +void SlideSorterModule::disposing(std::unique_lock&) +{ + if (mxConfigurationController.is()) + { + uno::Reference const xComponent(mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(this); + + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SlideSorterModule::HandleMainViewSwitch ( + const OUString& rsViewURL, + const bool bIsActivated) +{ + if (bIsActivated) + msCurrentMainViewURL = rsViewURL; + else + msCurrentMainViewURL.clear(); + + if (!mxConfigurationController.is()) + return; + + ConfigurationController::Lock aLock (mxConfigurationController); + + if (maActiveMainViewContainer.find(msCurrentMainViewURL) + != maActiveMainViewContainer.end()) + { + // Activate resource. + mxConfigurationController->requestResourceActivation( + mxResourceId->getAnchor(), + ResourceActivationMode_ADD); + mxConfigurationController->requestResourceActivation( + mxResourceId, + ResourceActivationMode_REPLACE); + } + else + { + mxConfigurationController->requestResourceDeactivation(mxResourceId); + } +} + +void SlideSorterModule::HandleResourceRequest( + bool bActivation, + const Reference& rxConfiguration) +{ + Sequence > aCenterViews = rxConfiguration->getResources( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL), + FrameworkHelper::msViewURLPrefix, + AnchorBindingMode_DIRECT); + if (aCenterViews.getLength() == 1) + { + if (bActivation) + { + maActiveMainViewContainer.insert(aCenterViews[0]->getResourceURL()); + } + else + { + maActiveMainViewContainer.erase(aCenterViews[0]->getResourceURL()); + } + } +} + +void SAL_CALL SlideSorterModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + SaveResourceState(); + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/SlideSorterModule.hxx b/sd/source/ui/framework/module/SlideSorterModule.hxx new file mode 100644 index 000000000..bec9f5c3c --- /dev/null +++ b/sd/source/ui/framework/module/SlideSorterModule.hxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XControllerManager; } +namespace com::sun::star::drawing::framework { class XTabBar; } +namespace com::sun::star::frame { class XController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > SlideSorterModuleBase; + +/** This module is responsible for showing the slide sorter bar and the + slide sorter view in the center pane. + + Manage the activation state of one resource depending on the view in the + center pane. The ResourceManager remembers in which configuration to + activate and in which to deactivate the resource. When the resource is + deactivated or activated manually by the user then the ResourceManager + detects this and remembers it for the future. +*/ +class SlideSorterModule final + : public SlideSorterModuleBase +{ +public: + SlideSorterModule ( + const css::uno::Reference& rxController, + const OUString& rsLeftPaneURL); + virtual ~SlideSorterModule() override; + + /** Remember the given URL as one of a center pane view for which to + activate the resource managed by the called object. + */ + void AddActiveMainView (const OUString& rsMainViewURL); + bool IsResourceActive (const OUString& rsMainViewURL); + void SaveResourceState(); + + virtual void disposing(std::unique_lock&) override; + + // XConfigurationChangeListener + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference + mxConfigurationController; + ::std::set maActiveMainViewContainer; + /// The resource managed by this class. + css::uno::Reference mxResourceId; + /// The anchor of the main view. + css::uno::Reference mxMainViewAnchorId; + OUString msCurrentMainViewURL; + css::uno::Reference mxViewTabBarId; + css::uno::Reference mxControllerManager; + + void HandleMainViewSwitch ( + const OUString& rsViewURL, + const bool bIsActivated); + void HandleResourceRequest( + bool bActivation, + const css::uno::Reference& rxConfiguration); + void UpdateViewTabBar ( + const css::uno::Reference& rxViewTabBar); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ToolBarModule.cxx b/sd/source/ui/framework/module/ToolBarModule.cxx new file mode 100644 index 000000000..3cecf7b03 --- /dev/null +++ b/sd/source/ui/framework/module/ToolBarModule.cxx @@ -0,0 +1,191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ToolBarModule.hxx" +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + const sal_Int32 gnConfigurationUpdateStartEvent(0); + const sal_Int32 gnConfigurationUpdateEndEvent(1); + const sal_Int32 gnResourceActivationRequestEvent(2); + const sal_Int32 gnResourceDeactivationRequestEvent(3); +} + +namespace sd::framework { + +//===== ToolBarModule ========================================================= + +ToolBarModule::ToolBarModule ( + const Reference& rxController) + : mpBase(nullptr), + mbMainViewSwitchUpdatePending(false) +{ + // Tunnel through the controller to obtain a ViewShellBase. + auto pController = comphelper::getFromUnoTunnel(rxController); + if (pController != nullptr) + mpBase = pController->GetViewShellBase(); + + Reference xControllerManager (rxController, UNO_QUERY); + if (!xControllerManager.is()) + return; + + mxConfigurationController = xControllerManager->getConfigurationController(); + if (!mxConfigurationController.is()) + return; + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateStartEvent, + Any(gnConfigurationUpdateStartEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any(gnConfigurationUpdateEndEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(gnResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(gnResourceDeactivationRequestEvent)); +} + +ToolBarModule::~ToolBarModule() +{ +} + +void ToolBarModule::disposing(std::unique_lock&) +{ + if (mxConfigurationController.is()) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SAL_CALL ToolBarModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!mxConfigurationController.is()) + return; + + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case gnConfigurationUpdateStartEvent: + HandleUpdateStart(); + break; + + case gnConfigurationUpdateEndEvent: + HandleUpdateEnd(); + break; + + case gnResourceActivationRequestEvent: + case gnResourceDeactivationRequestEvent: + // Remember the request for the activation or deactivation + // of the center pane view. When that happens then on end + // of the next configuration update the set of visible tool + // bars will be updated. + if ( ! mbMainViewSwitchUpdatePending) + if (rEvent.ResourceId->getResourceURL().match( + FrameworkHelper::msViewURLPrefix) + && rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + mbMainViewSwitchUpdatePending = true; + } + break; + } +} + +void ToolBarModule::HandleUpdateStart() +{ + // Lock the ToolBarManager and tell it to lock the ViewShellManager as + // well. This way the ToolBarManager can optimize the releasing of + // locks and arranging of updates of both tool bars and the view shell + // stack. + if (mpBase != nullptr) + { + std::shared_ptr pToolBarManager (mpBase->GetToolBarManager()); + mpToolBarManagerLock.reset(new ToolBarManager::UpdateLock(pToolBarManager)); + pToolBarManager->LockViewShellManager(); + } +} + +void ToolBarModule::HandleUpdateEnd() +{ + if (mbMainViewSwitchUpdatePending) + { + mbMainViewSwitchUpdatePending = false; + // Update the set of visible tool bars and deactivate those that are + // no longer visible. This is done before the old view shell is + // destroyed in order to avoid unnecessary updates of those tool + // bars. + std::shared_ptr pToolBarManager (mpBase->GetToolBarManager()); + std::shared_ptr pFrameworkHelper ( + FrameworkHelper::Instance(*mpBase)); + ViewShell* pViewShell + = pFrameworkHelper->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + if (pViewShell != nullptr) + { + pToolBarManager->MainViewShellChanged(*pViewShell); + pToolBarManager->SelectionHasChanged( + *pViewShell, + *pViewShell->GetView()); + pToolBarManager->PreUpdate(); + } + else + { + pToolBarManager->MainViewShellChanged(); + pToolBarManager->PreUpdate(); + } + } + + // Releasing the update lock of the ToolBarManager will let the + // ToolBarManager with the help of the ViewShellManager take care of + // updating tool bars and view shell with the minimal amount of + // shell stack modifications and tool bar updates. + mpToolBarManagerLock.reset(); +} + +void SAL_CALL ToolBarModule::disposing (const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ToolBarModule.hxx b/sd/source/ui/framework/module/ToolBarModule.hxx new file mode 100644 index 000000000..f9189657d --- /dev/null +++ b/sd/source/ui/framework/module/ToolBarModule.hxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::frame { class XController; } + +namespace sd { +class ViewShellBase; +} + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > ToolBarModuleInterfaceBase; + +/** This module is responsible for locking the ToolBarManager during + configuration updates and for triggering ToolBarManager updates. +*/ +class ToolBarModule final + : public ToolBarModuleInterfaceBase +{ +public: + /** Create a new module. + @param rxController + This is the access point to the drawing framework. + */ + explicit ToolBarModule ( + const css::uno::Reference& rxController); + virtual ~ToolBarModule() override; + + virtual void disposing(std::unique_lock&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxConfigurationController; + ViewShellBase* mpBase; + std::unique_ptr> mpToolBarManagerLock; + bool mbMainViewSwitchUpdatePending; + + void HandleUpdateStart(); + void HandleUpdateEnd(); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ViewTabBarModule.cxx b/sd/source/ui/framework/module/ViewTabBarModule.cxx new file mode 100644 index 000000000..4f5dd4828 --- /dev/null +++ b/sd/source/ui/framework/module/ViewTabBarModule.cxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ViewTabBarModule.hxx" + +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +namespace { + +const sal_Int32 ResourceActivationRequestEvent = 0; +const sal_Int32 ResourceDeactivationRequestEvent = 1; +const sal_Int32 ResourceActivationEvent = 2; + +} + +namespace sd::framework { + +//===== ViewTabBarModule ================================================== + +ViewTabBarModule::ViewTabBarModule ( + const Reference& rxController, + const Reference& rxViewTabBarId) + : mxViewTabBarId(rxViewTabBarId) +{ + Reference xControllerManager (rxController, UNO_QUERY); + + if (!xControllerManager.is()) + return; + + mxConfigurationController = xControllerManager->getConfigurationController(); + if (!mxConfigurationController.is()) + return; + + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationRequestEvent, + Any(ResourceActivationRequestEvent)); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationRequestEvent, + Any(ResourceDeactivationRequestEvent)); + + UpdateViewTabBar(nullptr); + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any(ResourceActivationEvent)); +} + +ViewTabBarModule::~ViewTabBarModule() +{ +} + +void ViewTabBarModule::disposing(std::unique_lock&) +{ + if (mxConfigurationController.is()) + { + mxConfigurationController->removeConfigurationChangeListener(this); + mxConfigurationController = nullptr; + } +} + +void SAL_CALL ViewTabBarModule::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!mxConfigurationController.is()) + return; + + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case ResourceActivationRequestEvent: + if (mxViewTabBarId->isBoundTo(rEvent.ResourceId, AnchorBindingMode_DIRECT)) + { + mxConfigurationController->requestResourceActivation( + mxViewTabBarId, + ResourceActivationMode_ADD); + } + break; + + case ResourceDeactivationRequestEvent: + if (mxViewTabBarId->isBoundTo(rEvent.ResourceId, AnchorBindingMode_DIRECT)) + { + mxConfigurationController->requestResourceDeactivation(mxViewTabBarId); + } + break; + + case ResourceActivationEvent: + if (rEvent.ResourceId->compareTo(mxViewTabBarId) == 0) + { + UpdateViewTabBar(Reference(rEvent.ResourceObject,UNO_QUERY)); + } + } +} + +void SAL_CALL ViewTabBarModule::disposing ( + const lang::EventObject& rEvent) +{ + if (mxConfigurationController.is() + && rEvent.Source == mxConfigurationController) + { + // Without the configuration controller this class can do nothing. + mxConfigurationController = nullptr; + dispose(); + } +} + +void ViewTabBarModule::UpdateViewTabBar (const Reference& rxTabBar) +{ + if (!mxConfigurationController.is()) + return; + + Reference xBar (rxTabBar); + if ( ! xBar.is()) + xBar.set( mxConfigurationController->getResource(mxViewTabBarId), UNO_QUERY); + + if (!xBar.is()) + return; + + TabBarButton aEmptyButton; + + Reference xAnchor (mxViewTabBarId->getAnchor()); + + TabBarButton aImpressViewButton; + aImpressViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msImpressViewURL, + xAnchor); + aImpressViewButton.ButtonLabel = SdResId(STR_NORMAL_MODE); + if ( ! xBar->hasTabBarButton(aImpressViewButton)) + xBar->addTabBarButtonAfter(aImpressViewButton, aEmptyButton); + + TabBarButton aOutlineViewButton; + aOutlineViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msOutlineViewURL, + xAnchor); + aOutlineViewButton.ButtonLabel = SdResId(STR_OUTLINE_MODE); + if ( ! xBar->hasTabBarButton(aOutlineViewButton)) + xBar->addTabBarButtonAfter(aOutlineViewButton, aImpressViewButton); + + TabBarButton aNotesViewButton; + aNotesViewButton.ResourceId = FrameworkHelper::CreateResourceId( + FrameworkHelper::msNotesViewURL, + xAnchor); + aNotesViewButton.ButtonLabel = SdResId(STR_NOTES_MODE); + if ( ! xBar->hasTabBarButton(aNotesViewButton)) + xBar->addTabBarButtonAfter(aNotesViewButton, aOutlineViewButton); +} + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/module/ViewTabBarModule.hxx b/sd/source/ui/framework/module/ViewTabBarModule.hxx new file mode 100644 index 000000000..bfb252b8d --- /dev/null +++ b/sd/source/ui/framework/module/ViewTabBarModule.hxx @@ -0,0 +1,83 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XTabBar; } +namespace com::sun::star::frame { class XController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > ViewTabBarModuleInterfaceBase; + +/** This module is responsible for showing the ViewTabBar above the view in + the center pane. +*/ +class ViewTabBarModule + : public ViewTabBarModuleInterfaceBase +{ +public: + /** Create a new module that controls the view tab bar above the view + in the specified pane. + @param rxController + This is the access point to the drawing framework. + @param rxViewTabBarId + This ResourceId specifies which tab bar is to be managed by the + new module. + */ + ViewTabBarModule ( + const css::uno::Reference& rxController, + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxViewTabBarId); + virtual ~ViewTabBarModule() override; + + virtual void disposing(std::unique_lock&) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEvent) override; + +private: + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxConfigurationController; + css::uno::Reference mxViewTabBarId; + + /** This is the place where the view tab bar is filled. Only missing + buttons are added, so it is safe to call this method multiple + times. + */ + void UpdateViewTabBar ( + const css::uno::Reference& rxTabBar); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/framework/tools/FrameworkHelper.cxx b/sd/source/ui/framework/tools/FrameworkHelper.cxx new file mode 100644 index 000000000..dceecd510 --- /dev/null +++ b/sd/source/ui/framework/tools/FrameworkHelper.cxx @@ -0,0 +1,952 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace { + +//----- CallbackCaller -------------------------------------------------------- + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfigurationChangeListener + > CallbackCallerInterfaceBase; + +/** A CallbackCaller registers as listener at an XConfigurationController + object and waits for the notification of one type of event. When that + event is received, or when the CallbackCaller detects at its + construction that the event will not be sent in the near future, the + actual callback object is called and the CallbackCaller destroys itself. +*/ +class CallbackCaller + : public CallbackCallerInterfaceBase +{ +public: + /** Create a new CallbackCaller object. This object controls its own + lifetime by acquiring a reference to itself in the constructor. + When it detects that the event will not be notified in the near + future (because the queue of pending configuration change operations + is empty and therefore no event will be sent int the near future, it + does not acquires a reference and thus initiates its destruction in + the constructor.) + @param rBase + This ViewShellBase object is used to determine the + XConfigurationController at which to register. + @param rsEventType + The event type which the callback is waiting for. + @param pCallback + The callback object which is to be notified. The caller will + typically release his reference to the caller so that when the + CallbackCaller dies (after having called the callback) the + callback is destroyed. + */ + CallbackCaller ( + const ::sd::ViewShellBase& rBase, + const OUString& rsEventType, + const ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter& rFilter, + const ::sd::framework::FrameworkHelper::Callback& rCallback); + + virtual void disposing(std::unique_lock&) override; + // XEventListener + virtual void SAL_CALL disposing (const lang::EventObject& rEvent) override; + // XConfigurationChangeListener + virtual void SAL_CALL notifyConfigurationChange (const ConfigurationChangeEvent& rEvent) override; + +private: + OUString msEventType; + Reference mxConfigurationController; + ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter maFilter; + ::sd::framework::FrameworkHelper::Callback maCallback; +}; + +//----- LifetimeController ---------------------------------------------------- + +typedef comphelper::WeakComponentImplHelper < + css::lang::XEventListener + > LifetimeControllerInterfaceBase; + +/** This class helps controlling the lifetime of the + FrameworkHelper. Register at a ViewShellBase object and an XController + object and call Dispose() at the associated FrameworkHelper object when + one of them and Release() when both of them are destroyed. +*/ +class LifetimeController + : public LifetimeControllerInterfaceBase, + public SfxListener +{ +public: + explicit LifetimeController (::sd::ViewShellBase& rBase); + virtual ~LifetimeController() override; + + /** XEventListener. This method is called when the frame::XController + is being destroyed. + */ + using WeakComponentImplHelperBase::disposing; + virtual void SAL_CALL disposing (const lang::EventObject& rEvent) override; + + /** This method is called when the ViewShellBase is being destroyed. + */ + virtual void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; + +private: + ::sd::ViewShellBase& mrBase; + bool mbListeningToViewShellBase; + bool mbListeningToController; + + /** When one or both of the mbListeningToViewShellBase and + mbListeningToController members were modified then call this method + to either dispose or release the associated FrameworkHelper. + */ + void Update(); +}; + +} // end of anonymous namespace + +namespace sd::framework { + +namespace { + + class FrameworkHelperAllPassFilter + { + public: + bool operator() (const css::drawing::framework::ConfigurationChangeEvent&) { return true; } + }; + + class FrameworkHelperResourceIdFilter + { + public: + explicit FrameworkHelperResourceIdFilter ( + const css::uno::Reference& rxResourceId); + bool operator() (const css::drawing::framework::ConfigurationChangeEvent& rEvent) + { return mxResourceId.is() && rEvent.ResourceId.is() + && mxResourceId->compareTo(rEvent.ResourceId) == 0; } + private: + css::uno::Reference mxResourceId; + }; + +} // end of anonymous namespace + +// Pane URLS. + +const OUString FrameworkHelper::msCenterPaneURL( msPaneURLPrefix + "CenterPane"); +const OUString FrameworkHelper::msFullScreenPaneURL( msPaneURLPrefix + "FullScreenPane"); +const OUString FrameworkHelper::msLeftImpressPaneURL( msPaneURLPrefix + "LeftImpressPane"); +const OUString FrameworkHelper::msLeftDrawPaneURL( msPaneURLPrefix + "LeftDrawPane"); + +// View URLs. + +const OUString FrameworkHelper::msImpressViewURL( msViewURLPrefix + "ImpressView"); +const OUString FrameworkHelper::msDrawViewURL( msViewURLPrefix + "GraphicView"); +const OUString FrameworkHelper::msOutlineViewURL( msViewURLPrefix + "OutlineView"); +const OUString FrameworkHelper::msNotesViewURL( msViewURLPrefix + "NotesView"); +const OUString FrameworkHelper::msHandoutViewURL( msViewURLPrefix + "HandoutView"); +const OUString FrameworkHelper::msSlideSorterURL( msViewURLPrefix + "SlideSorter"); +const OUString FrameworkHelper::msPresentationViewURL( msViewURLPrefix + "PresentationView"); +const OUString FrameworkHelper::msSidebarViewURL( msViewURLPrefix + "SidebarView"); + +// Tool bar URLs. + +const OUString FrameworkHelper::msViewTabBarURL( msToolBarURLPrefix + "ViewTabBar"); + +//----- helper ---------------------------------------------------------------- +namespace +{ + ::std::shared_ptr< ViewShell > lcl_getViewShell( const Reference< XResource >& i_rViewShellWrapper ) + { + ::std::shared_ptr< ViewShell > pViewShell; + try + { + Reference xViewTunnel( i_rViewShellWrapper, UNO_QUERY_THROW ); + if (auto pWrapper = comphelper::getFromUnoTunnel(xViewTunnel)) + pViewShell = pWrapper->GetViewShell(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + return pViewShell; + } + Reference< XResource > lcl_getFirstViewInPane( const Reference< XConfigurationController >& i_rConfigController, + const Reference< XResourceId >& i_rPaneId ) + { + try + { + Reference< XConfiguration > xConfiguration( i_rConfigController->getRequestedConfiguration(), UNO_SET_THROW ); + Sequence< Reference< XResourceId > > aViewIds( xConfiguration->getResources( + i_rPaneId, FrameworkHelper::msViewURLPrefix, AnchorBindingMode_DIRECT ) ); + if ( aViewIds.hasElements() ) + return i_rConfigController->getResource( aViewIds[0] ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + return nullptr; + } +} + +//----- FrameworkHelper::ViewURLMap ------------------------------------------- + +/** The ViewURLMap is used to translate between the view URLs used by the + drawing framework and the enums defined in the ViewShell class. +*/ +class FrameworkHelper::ViewURLMap + : public std::unordered_map< + OUString, + ViewShell::ShellType> +{ +public: + ViewURLMap() {} +}; + +//----- Framework::DisposeListener --------------------------------------------- + +namespace { + typedef comphelper::WeakComponentImplHelper < + css::lang::XEventListener + > FrameworkHelperDisposeListenerInterfaceBase; +} + +class FrameworkHelper::DisposeListener + : public FrameworkHelperDisposeListenerInterfaceBase +{ +public: + explicit DisposeListener (const ::std::shared_ptr& rpHelper); + + virtual void disposing(std::unique_lock&) override; + + virtual void SAL_CALL disposing (const lang::EventObject& rEventObject) override; + +private: + ::std::shared_ptr mpHelper; +}; + +//----- FrameworkHelper::Deleter ---------------------------------------------- + +class FrameworkHelper::Deleter +{ +public: + void operator()(FrameworkHelper* pObject) + { + delete pObject; + } +}; + +//----- FrameworkHelper ------------------------------------------------------- + +FrameworkHelper::ViewURLMap FrameworkHelper::maViewURLMap; + +FrameworkHelper::InstanceMap FrameworkHelper::maInstanceMap; + +::std::shared_ptr FrameworkHelper::Instance (ViewShellBase& rBase) +{ + + ::std::shared_ptr pHelper; + + InstanceMap::const_iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper == maInstanceMap.end()) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (iHelper == maInstanceMap.end()) + { + pHelper = ::std::shared_ptr( + new FrameworkHelper(rBase), + FrameworkHelper::Deleter()); + pHelper->Initialize(); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + maInstanceMap[&rBase] = pHelper; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pHelper = iHelper->second; + } + + return pHelper; +} + +void FrameworkHelper::DisposeInstance (const ViewShellBase& rBase) +{ + InstanceMap::iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper != maInstanceMap.end()) + { + iHelper->second->Dispose(); + } +} + +void FrameworkHelper::ReleaseInstance (const ViewShellBase& rBase) +{ + InstanceMap::iterator iHelper (maInstanceMap.find(&rBase)); + if (iHelper != maInstanceMap.end()) + maInstanceMap.erase(iHelper); +} + +FrameworkHelper::FrameworkHelper (ViewShellBase& rBase) + : mrBase(rBase) +{ + Reference xControllerManager (rBase.GetController(), UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + } + + new LifetimeController(mrBase); +} + +void FrameworkHelper::Initialize() +{ + mxDisposeListener = new DisposeListener(shared_from_this()); +} + +FrameworkHelper::~FrameworkHelper() +{ +} + +void FrameworkHelper::Dispose() +{ + if (mxDisposeListener.is()) + mxDisposeListener->dispose(); + mxConfigurationController = nullptr; +} + +bool FrameworkHelper::IsValid() const +{ + return mxConfigurationController.is(); +} + +::std::shared_ptr FrameworkHelper::GetViewShell (const OUString& rsPaneURL) +{ + if ( !mxConfigurationController.is() ) + return ::std::shared_ptr(); + + Reference xPaneId( CreateResourceId( rsPaneURL ) ); + return lcl_getViewShell( lcl_getFirstViewInPane( mxConfigurationController, xPaneId ) ); +} + +::std::shared_ptr FrameworkHelper::GetViewShell (const Reference& rxView) +{ + return lcl_getViewShell( rxView ); +} + +Reference FrameworkHelper::GetView (const Reference& rxPaneOrViewId) +{ + Reference xView; + + if ( ! rxPaneOrViewId.is() || ! mxConfigurationController.is()) + return nullptr; + + try + { + if (rxPaneOrViewId->getResourceURL().match(msViewURLPrefix)) + { + xView.set( mxConfigurationController->getResource( rxPaneOrViewId ), UNO_QUERY ); + } + else + { + xView.set( lcl_getFirstViewInPane( mxConfigurationController, rxPaneOrViewId ), UNO_QUERY ); + } + } + catch (lang::DisposedException&) + { + Dispose(); + } + catch (RuntimeException&) + { + } + + return xView; +} + +Reference FrameworkHelper::RequestView ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL) +{ + Reference xViewId; + + try + { + if (mxConfigurationController.is()) + { + mxConfigurationController->requestResourceActivation( + CreateResourceId(rsAnchorURL), + ResourceActivationMode_ADD); + xViewId = CreateResourceId(rsResourceURL, rsAnchorURL); + mxConfigurationController->requestResourceActivation( + xViewId, + ResourceActivationMode_REPLACE); + } + } + catch (lang::DisposedException&) + { + Dispose(); + xViewId = nullptr; + } + catch (RuntimeException&) + { + xViewId = nullptr; + } + + return xViewId; +} + +ViewShell::ShellType FrameworkHelper::GetViewId (const OUString& rsViewURL) +{ + if (maViewURLMap.empty()) + { + maViewURLMap[msImpressViewURL] = ViewShell::ST_IMPRESS; + maViewURLMap[msDrawViewURL] = ViewShell::ST_DRAW; + maViewURLMap[msOutlineViewURL] = ViewShell::ST_OUTLINE; + maViewURLMap[msNotesViewURL] = ViewShell::ST_NOTES; + maViewURLMap[msHandoutViewURL] = ViewShell::ST_HANDOUT; + maViewURLMap[msSlideSorterURL] = ViewShell::ST_SLIDE_SORTER; + maViewURLMap[msPresentationViewURL] = ViewShell::ST_PRESENTATION; + maViewURLMap[msSidebarViewURL] = ViewShell::ST_SIDEBAR; + } + ViewURLMap::const_iterator iView (maViewURLMap.find(rsViewURL)); + if (iView != maViewURLMap.end()) + return iView->second; + else + return ViewShell::ST_NONE; +} + +OUString FrameworkHelper::GetViewURL (ViewShell::ShellType eType) +{ + switch (eType) + { + case ViewShell::ST_IMPRESS : return msImpressViewURL; + case ViewShell::ST_DRAW : return msDrawViewURL; + case ViewShell::ST_OUTLINE : return msOutlineViewURL; + case ViewShell::ST_NOTES : return msNotesViewURL; + case ViewShell::ST_HANDOUT : return msHandoutViewURL; + case ViewShell::ST_SLIDE_SORTER : return msSlideSorterURL; + case ViewShell::ST_PRESENTATION : return msPresentationViewURL; + case ViewShell::ST_SIDEBAR : return msSidebarViewURL; + default: + return OUString(); + } +} + +namespace +{ + +void updateEditMode(const Reference &xView, const EditMode eEMode, bool updateFrameView) +{ + // Ensure we have the expected edit mode + // The check is only for DrawViewShell as OutlineViewShell + // and SlideSorterViewShell have no master mode + const ::std::shared_ptr pCenterViewShell (FrameworkHelper::GetViewShell(xView)); + DrawViewShell* pDrawViewShell + = dynamic_cast(pCenterViewShell.get()); + if (pDrawViewShell != nullptr) + { + pCenterViewShell->Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_START)); + + pDrawViewShell->ChangeEditMode(eEMode, pDrawViewShell->IsLayerModeActive()); + if (updateFrameView) + pDrawViewShell->WriteFrameViewData(); + + pCenterViewShell->Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_END)); + } +} + +void asyncUpdateEditMode(FrameworkHelper* const pHelper, const EditMode eEMode) +{ + Reference xPaneId ( + FrameworkHelper::CreateResourceId(framework::FrameworkHelper::msCenterPaneURL)); + Reference xView (pHelper->GetView(xPaneId)); + updateEditMode(xView, eEMode, true); +} + +} + +void FrameworkHelper::HandleModeChangeSlot ( + sal_uInt16 nSlotId, + SfxRequest const & rRequest) +{ + if ( ! mxConfigurationController.is()) + return; + + // Parameters are allowed for NotesMasterPage and SlideMasterPage + // for these command, transfor xxxxMasterPage with param = false + // to ActivatexxxxxMode + if (nSlotId == SID_NOTES_MASTER_MODE || nSlotId == SID_SLIDE_MASTER_MODE) + { + const SfxItemSet* pRequestArguments = rRequest.GetArgs(); + if (pRequestArguments) + { + const SfxBoolItem* pIsActive = rRequest.GetArg(nSlotId); + if (!pIsActive->GetValue ()) + { + if (nSlotId == SID_NOTES_MASTER_MODE) + nSlotId = SID_NOTES_MODE; + else + nSlotId = SID_NORMAL_MULTI_PANE_GUI; + } + } + } + + try + { + if ( ! mxConfigurationController.is()) + throw RuntimeException(); + + Reference xPaneId ( + CreateResourceId(framework::FrameworkHelper::msCenterPaneURL)); + Reference xView (GetView(xPaneId)); + + // Compute requested view + OUString sRequestedView; + switch (nSlotId) + { + // draw + case SID_DRAWINGMODE: + // impress + case SID_NORMAL_MULTI_PANE_GUI: + case SID_SLIDE_MASTER_MODE: + sRequestedView = FrameworkHelper::msImpressViewURL; + break; + + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + sRequestedView = FrameworkHelper::msNotesViewURL; + break; + + case SID_HANDOUT_MASTER_MODE: + sRequestedView = FrameworkHelper::msHandoutViewURL; + break; + + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + sRequestedView = FrameworkHelper::msSlideSorterURL; + break; + + case SID_OUTLINE_MODE: + sRequestedView = FrameworkHelper::msOutlineViewURL; + break; + } + + // Compute requested mode + EditMode eEMode = EditMode::Page; + if (nSlotId == SID_SLIDE_MASTER_MODE + || nSlotId == SID_NOTES_MASTER_MODE + || nSlotId == SID_HANDOUT_MASTER_MODE) + eEMode = EditMode::MasterPage; + // Ensure we have the expected view shell + if (!(xView.is() && xView->getResourceId()->getResourceURL() == sRequestedView)) + + { + const auto xId = CreateResourceId(sRequestedView, msCenterPaneURL); + mxConfigurationController->requestResourceActivation( + xId, + ResourceActivationMode_REPLACE); + RunOnResourceActivation(xId, std::bind(&asyncUpdateEditMode, this, eEMode)); + } + else + { + updateEditMode(xView, eEMode, false); + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void FrameworkHelper::RunOnConfigurationEvent( + const OUString& rsEventType, + const Callback& rCallback) +{ + RunOnEvent( + rsEventType, + FrameworkHelperAllPassFilter(), + rCallback); +} + +void FrameworkHelper::RunOnResourceActivation( + const css::uno::Reference& rxResourceId, + const Callback& rCallback) +{ + if (mxConfigurationController.is() + && mxConfigurationController->getResource(rxResourceId).is()) + { + rCallback(false); + } + else + { + RunOnEvent( + msResourceActivationEvent, + FrameworkHelperResourceIdFilter(rxResourceId), + rCallback); + } +} + +namespace { + +/** A callback that sets a flag to a specified value when the callback is + called. +*/ +class FlagUpdater +{ +public: + explicit FlagUpdater (bool& rFlag) : mrFlag(rFlag) {} + void operator() (bool) const {mrFlag = true;} +private: + bool& mrFlag; +}; + +} + +void FrameworkHelper::RequestSynchronousUpdate() +{ + rtl::Reference pCC ( + dynamic_cast(mxConfigurationController.get())); + if (pCC.is()) + pCC->RequestSynchronousUpdate(); +} + +void FrameworkHelper::WaitForEvent (const OUString& rsEventType) const +{ + bool bConfigurationUpdateSeen (false); + + RunOnEvent( + rsEventType, + FrameworkHelperAllPassFilter(), + FlagUpdater(bConfigurationUpdateSeen)); + + sal_uInt32 nStartTime = osl_getGlobalTimer(); + while ( ! bConfigurationUpdateSeen) + { + Application::Reschedule(); + + if( (osl_getGlobalTimer() - nStartTime) > 60000 ) + { + OSL_FAIL("FrameworkHelper::WaitForEvent(), no event for a minute? giving up!"); + break; + } + } +} + +void FrameworkHelper::WaitForUpdate() const +{ + WaitForEvent(msConfigurationUpdateEndEvent); +} + +void FrameworkHelper::RunOnEvent( + const OUString& rsEventType, + const ConfigurationChangeEventFilter& rFilter, + const Callback& rCallback) const +{ + new CallbackCaller(mrBase,rsEventType,rFilter,rCallback); +} + +void FrameworkHelper::disposing (const lang::EventObject& rEventObject) +{ + if (rEventObject.Source == mxConfigurationController) + mxConfigurationController = nullptr; +} + +void FrameworkHelper::UpdateConfiguration() +{ + if (!mxConfigurationController.is()) + return; + + try + { + if (mxConfigurationController.is()) + mxConfigurationController->update(); + } + catch (lang::DisposedException&) + { + Dispose(); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +OUString FrameworkHelper::ResourceIdToString (const Reference& rxResourceId) +{ + OUStringBuffer sString; + if (rxResourceId.is()) + { + sString.append(rxResourceId->getResourceURL()); + if (rxResourceId->hasAnchor()) + { + const Sequence aAnchorURLs (rxResourceId->getAnchorURLs()); + for (const auto& rAnchorURL : aAnchorURLs) + { + sString.append(" | "); + sString.append(rAnchorURL); + } + } + } + return sString.makeStringAndClear(); +} + +Reference FrameworkHelper::CreateResourceId (const OUString& rsResourceURL) +{ + return new ::sd::framework::ResourceId(rsResourceURL); +} + +Reference FrameworkHelper::CreateResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL) +{ + return new ::sd::framework::ResourceId(rsResourceURL, rsAnchorURL); +} + +Reference FrameworkHelper::CreateResourceId ( + const OUString& rsResourceURL, + const Reference& rxAnchorId) +{ + if (rxAnchorId.is()) + return new ::sd::framework::ResourceId( + rsResourceURL, + rxAnchorId->getResourceURL(), + rxAnchorId->getAnchorURLs()); + else + return new ::sd::framework::ResourceId(rsResourceURL); +} + +//----- FrameworkHelper::DisposeListener -------------------------------------- + +FrameworkHelper::DisposeListener::DisposeListener ( + const ::std::shared_ptr& rpHelper) + : mpHelper(rpHelper) +{ + Reference xComponent (mpHelper->mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(this); +} + +void FrameworkHelper::DisposeListener::disposing(std::unique_lock&) +{ + Reference xComponent (mpHelper->mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(this); + + mpHelper.reset(); +} + +void SAL_CALL FrameworkHelper::DisposeListener::disposing (const lang::EventObject& rEventObject) +{ + if (mpHelper != nullptr) + mpHelper->disposing(rEventObject); +} + +//===== FrameworkHelperResourceIdFilter ======================================= + +FrameworkHelperResourceIdFilter::FrameworkHelperResourceIdFilter ( + const Reference& rxResourceId) + : mxResourceId(rxResourceId) +{ +} + +} // end of namespace sd::framework + +namespace { + +//===== CallbackCaller ======================================================== + +CallbackCaller::CallbackCaller ( + const ::sd::ViewShellBase& rBase, + const OUString& rsEventType, + const ::sd::framework::FrameworkHelper::ConfigurationChangeEventFilter& rFilter, + const ::sd::framework::FrameworkHelper::Callback& rCallback) + : msEventType(rsEventType), + maFilter(rFilter), + maCallback(rCallback) +{ + try + { + Reference xControllerManager (rBase.GetController(), UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + if (mxConfigurationController->hasPendingRequests()) + mxConfigurationController->addConfigurationChangeListener(this,msEventType,Any()); + else + { + // There are no requests waiting to be processed. Therefore + // no event, especially not the one we are waiting for, will + // be sent in the near future and the callback would never be + // called. + // Call the callback now and tell him that the event it is + // waiting for was not sent. + mxConfigurationController = nullptr; + maCallback(false); + } + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void CallbackCaller::disposing(std::unique_lock&) +{ + try + { + if (mxConfigurationController.is()) + { + Reference xCC (mxConfigurationController); + mxConfigurationController = nullptr; + xCC->removeConfigurationChangeListener(this); + } + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +void SAL_CALL CallbackCaller::disposing (const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxConfigurationController) + { + mxConfigurationController = nullptr; + maCallback(false); + } +} + +void SAL_CALL CallbackCaller::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (!(rEvent.Type == msEventType && maFilter(rEvent))) + return; + + maCallback(true); + if (mxConfigurationController.is()) + { + // Reset the reference to the configuration controller so that + // dispose() will not try to remove the listener a second time. + Reference xCC (mxConfigurationController); + mxConfigurationController = nullptr; + + // Removing this object from the controller may very likely lead + // to its destruction, so no calls after that. + xCC->removeConfigurationChangeListener(this); + } +} + +//----- LifetimeController ------------------------------------------------- + +LifetimeController::LifetimeController (::sd::ViewShellBase& rBase) + : mrBase(rBase), + mbListeningToViewShellBase(false), + mbListeningToController(false) +{ + // Register as listener at the ViewShellBase. Because that is not done + // via a reference we have to increase the reference count manually. + // This is necessary even though listening to the XController did + // increase the reference count because the controller may release its + // reference to us before the ViewShellBase is destroyed. + StartListening(mrBase); + acquire(); + mbListeningToViewShellBase = true; + + Reference xComponent = rBase.GetController(); + if (xComponent.is()) + { + xComponent->addEventListener(this); + mbListeningToController = true; + } +} + +LifetimeController::~LifetimeController() +{ + OSL_ASSERT(!mbListeningToController && !mbListeningToViewShellBase); +} + +void SAL_CALL LifetimeController::disposing (const lang::EventObject&) +{ + mbListeningToController = false; + Update(); +} + +void LifetimeController::Notify (SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::Dying) + { + mbListeningToViewShellBase = false; + Update(); + release(); + } +} + +void LifetimeController::Update() +{ + if (mbListeningToViewShellBase && mbListeningToController) + { + // Both the controller and the ViewShellBase are alive. Keep + // waiting for their destruction. + } + else if (mbListeningToViewShellBase) + { + // The controller has been destroyed but the ViewShellBase is still + // alive. Dispose the associated FrameworkHelper but keep it around + // so that no new instance is created for the dying framework. + ::sd::framework::FrameworkHelper::DisposeInstance(mrBase); + } + else + { + // Both the controller and the ViewShellBase have been destroyed. + // Remove the FrameworkHelper so that the next call its Instance() + // method can create a new instance. + ::sd::framework::FrameworkHelper::ReleaseInstance(mrBase); + } +} + +} // end of anonymous namespace. + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/bulmaper.cxx b/sd/source/ui/func/bulmaper.cxx new file mode 100644 index 000000000..67a667891 --- /dev/null +++ b/sd/source/ui/func/bulmaper.cxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +//-> Fonts & Items +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//<- Fonts & Items +#include +#include +#include + +#include + +#define GetWhich(nSlot) rSet.GetPool()->GetWhich( nSlot ) + +void SdBulletMapper::MapFontsInNumRule( SvxNumRule& aNumRule, const SfxItemSet& rSet ) +{ + const sal_uInt16 nCount = aNumRule.GetLevelCount(); + for( sal_uInt16 nLevel = 0; nLevel < nCount; nLevel++ ) + { + const SvxNumberFormat& rSrcLevel = aNumRule.GetLevel(nLevel); + SvxNumberFormat aNewLevel( rSrcLevel ); + + if(rSrcLevel.GetNumberingType() != css::style::NumberingType::CHAR_SPECIAL && + rSrcLevel.GetNumberingType() != css::style::NumberingType::NUMBER_NONE ) + { + // if enumeration instead bullet is chosen, adjust bullet font to template font + + // to be implemented if module supports CJK + + vcl::Font aMyFont; + const SvxFontItem& rFItem = + static_cast(rSet.Get(GetWhich( sal_uInt16(SID_ATTR_CHAR_FONT) ))); + aMyFont.SetFamily(rFItem.GetFamily()); + aMyFont.SetFamilyName(rFItem.GetFamilyName()); + aMyFont.SetCharSet(rFItem.GetCharSet()); + aMyFont.SetPitch(rFItem.GetPitch()); + + const SvxFontHeightItem& rFHItem = + static_cast(rSet.Get(GetWhich( sal_uInt16(SID_ATTR_CHAR_FONTHEIGHT) ))); + aMyFont.SetFontSize(Size(0, rFHItem.GetHeight())); + + const SvxWeightItem& rWItem = + static_cast(rSet.Get(GetWhich( sal_uInt16(SID_ATTR_CHAR_WEIGHT) ))); + aMyFont.SetWeight(rWItem.GetWeight()); + + const SvxPostureItem& rPItem = + static_cast(rSet.Get(GetWhich( sal_uInt16(SID_ATTR_CHAR_POSTURE) ))); + aMyFont.SetItalic(rPItem.GetPosture()); + + const SvxUnderlineItem& rUItem = rSet.Get(GetWhich(SID_ATTR_CHAR_UNDERLINE)); + aMyFont.SetUnderline(rUItem.GetLineStyle()); + + const SvxOverlineItem& rOItem = static_cast(rSet.Get(GetWhich(SID_ATTR_CHAR_OVERLINE))); + aMyFont.SetOverline(rOItem.GetLineStyle()); + + const SvxCrossedOutItem& rCOItem = static_cast(rSet.Get(GetWhich(SID_ATTR_CHAR_STRIKEOUT))); + aMyFont.SetStrikeout(rCOItem.GetStrikeout()); + + const SvxContourItem& rCItem = static_cast(rSet.Get(GetWhich(SID_ATTR_CHAR_CONTOUR))); + aMyFont.SetOutline(rCItem.GetValue()); + + const SvxShadowedItem& rSItem = static_cast(rSet.Get(GetWhich(SID_ATTR_CHAR_SHADOWED))); + aMyFont.SetShadow(rSItem.GetValue()); + + aNewLevel.SetBulletFont(&aMyFont); + aNumRule.SetLevel(nLevel, aNewLevel ); + } + else if( rSrcLevel.GetNumberingType() == css::style::NumberingType::CHAR_SPECIAL ) + { + aNewLevel.SetPrefix(""); + aNewLevel.SetSuffix(""); + aNumRule.SetLevel(nLevel, aNewLevel ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuarea.cxx b/sd/source/ui/func/fuarea.cxx new file mode 100644 index 000000000..8dd7543e3 --- /dev/null +++ b/sd/source/ui/func/fuarea.cxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sd { + +FuArea::FuArea( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* _pView, SdDrawDocument* pDoc, SfxRequest& rReq) +: FuPoor(pViewSh, pWin, _pView, pDoc, rReq) +{ +} + +rtl::Reference FuArea::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* _pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuArea( pViewSh, pWin, _pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuArea::DoExecute( SfxRequest& rReq ) +{ + rReq.Ignore (); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if (pArgs) + return; + + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + bool bHasSlideBackground = mpViewShell->GetDoc()->GetDocumentType() == DocumentType::Impress; + VclPtr pDlg( + pFact->CreateSvxAreaTabDialog(mpViewShell->GetFrameWeld(), &aNewAttr, mpDoc, true, bHasSlideBackground)); + + pDlg->StartExecuteAsync([pDlg, pView = this->mpView, pViewShell = this->mpViewShell](sal_Int32 nResult){ + if (nResult == RET_OK) + { + pView->SetAttributes (*(pDlg->GetOutputItemSet ())); + + // attributes changed, update Listboxes in Objectbars + static const sal_uInt16 SidArray[] = { + SID_ATTR_FILL_STYLE, + SID_ATTR_FILL_COLOR, + SID_ATTR_FILL_GRADIENT, + SID_ATTR_FILL_HATCH, + SID_ATTR_FILL_BITMAP, + SID_ATTR_FILL_TRANSPARENCE, + SID_ATTR_FILL_FLOATTRANSPARENCE, + SID_ATTR_FILL_USE_SLIDE_BACKGROUND, + 0 }; + + pViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + } + + // deferred until the dialog ends + pViewShell->Cancel(); + + pDlg->disposeOnce(); + }); +} + +void FuArea::Activate() +{ +} + +void FuArea::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fubullet.cxx b/sd/source/ui/func/fubullet.cxx new file mode 100644 index 000000000..ab0cf7de8 --- /dev/null +++ b/sd/source/ui/func/fubullet.cxx @@ -0,0 +1,330 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace sd { + +const sal_Unicode CHAR_HARDBLANK = u'\x00A0'; +const sal_Unicode CHAR_HARDHYPHEN = u'\x2011'; +const sal_Unicode CHAR_SOFTHYPHEN = u'\x00AD'; +const sal_Unicode CHAR_RLM = u'\x200F'; +const sal_Unicode CHAR_LRM = u'\x200E'; +const sal_Unicode CHAR_ZWSP = u'\x200B'; +const sal_Unicode CHAR_WJ = u'\x2060'; +const sal_Unicode CHAR_NNBSP = u'\x202F'; //NARROW NO-BREAK SPACE + + +FuBullet::FuBullet ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* _pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, _pView, pDoc, rReq) +{ +} + +rtl::Reference FuBullet::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuBullet( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuBullet::DoExecute( SfxRequest& rReq ) +{ + if( rReq.GetSlot() == SID_CHARMAP ) + InsertSpecialCharacter(rReq); + else + { + sal_Unicode cMark = 0; + switch( rReq.GetSlot() ) + { + case FN_INSERT_SOFT_HYPHEN: cMark = CHAR_SOFTHYPHEN ; break; + case FN_INSERT_HARDHYPHEN: cMark = CHAR_HARDHYPHEN ; break; + case FN_INSERT_HARD_SPACE: cMark = CHAR_HARDBLANK ; break; + case FN_INSERT_NNBSP: cMark = CHAR_NNBSP ; break; + case SID_INSERT_RLM : cMark = CHAR_RLM ; break; + case SID_INSERT_LRM : cMark = CHAR_LRM ; break; + case SID_INSERT_ZWSP : cMark = CHAR_ZWSP ; break; + case SID_INSERT_WJ: cMark = CHAR_WJ; break; + } + + DBG_ASSERT( cMark != 0, "FuBullet::FuBullet(), illegal slot used!" ); + + if( cMark ) + InsertFormattingMark( cMark ); + } + +} + +void FuBullet::InsertFormattingMark( sal_Unicode cMark ) +{ + OutlinerView* pOV = nullptr; + ::Outliner* pOL = nullptr; + + // depending on ViewShell set Outliner and OutlinerView + if( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr) + { + pOV = mpView->GetTextEditOutlinerView(); + if (pOV) + pOL = mpView->GetTextEditOutliner(); + } + else if( dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr) + { + pOL = &static_cast(mpView)->GetOutliner(); + pOV = static_cast(mpView)->GetViewByWindow( + mpViewShell->GetActiveWindow()); + } + + // insert string + if(!(pOV && pOL)) + return; + + // prevent flickering + pOV->HideCursor(); + pOL->SetUpdateLayout(false); + + // remove old selected text + pOV->InsertText( "" ); + + // prepare undo + SfxUndoManager& rUndoMgr = pOL->GetUndoManager(); + rUndoMgr.EnterListAction(SdResId(STR_UNDO_INSERT_SPECCHAR), + "", 0, mpViewShell->GetViewShellBase().GetViewShellId() ); + + // insert given text + OUString aStr( cMark ); + pOV->InsertText( aStr, true); + + ESelection aSel = pOV->GetSelection(); + aSel.nStartPara = aSel.nEndPara; + aSel.nStartPos = aSel.nEndPos; + pOV->SetSelection(aSel); + + rUndoMgr.LeaveListAction(); + + // restart repainting + pOL->SetUpdateLayout(true); + pOV->ShowCursor(); +} + +void FuBullet::InsertSpecialCharacter( SfxRequest const & rReq ) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + const SfxStringItem* pItem = nullptr; + if( pArgs ) + pItem = pArgs->GetItemIfSet(SID_CHARMAP, false); + + OUString aChars; + vcl::Font aFont; + if ( pItem ) + { + aChars = pItem->GetValue(); + const SfxStringItem* pFontItem = pArgs->GetItemIfSet( SID_ATTR_SPECIALCHAR, false ); + if ( pFontItem ) + { + const OUString& aFontName = pFontItem->GetValue(); + aFont = vcl::Font( aFontName, Size(1,1) ); + } + else + { + SfxItemSet aFontAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aFontAttr ); + const SvxFontItem* pFItem = aFontAttr.GetItem( SID_ATTR_CHAR_FONT ); + if( pFItem ) + aFont = vcl::Font( pFItem->GetFamilyName(), pFItem->GetStyleName(), Size( 1, 1 ) ); + } + } + + if (aChars.isEmpty()) + { + SfxAllItemSet aSet( mpDoc->GetPool() ); + aSet.Put( SfxBoolItem( FN_PARAM_1, false ) ); + + SfxItemSet aFontAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aFontAttr ); + const SvxFontItem* pFontItem = aFontAttr.GetItem( SID_ATTR_CHAR_FONT ); + if( pFontItem ) + aSet.Put( *pFontItem ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + auto xFrame = mpViewShell ? mpViewShell->GetFrame()->GetFrame().GetFrameInterface() : nullptr; + ScopedVclPtr pDlg( pFact->CreateCharMapDialog(mpView->GetViewShell()->GetFrameWeld(), aSet, + xFrame) ); + + // If a character is selected, it can be shown + // pDLg->SetFont( ); + // pDlg->SetChar( ); + pDlg->Execute(); + return; + } + + if (aChars.isEmpty()) + return; + + OutlinerView* pOV = nullptr; + ::Outliner* pOL = nullptr; + + // determine depending on ViewShell Outliner and OutlinerView + if(dynamic_cast< const DrawViewShell *>( mpViewShell )) + { + pOV = mpView->GetTextEditOutlinerView(); + if (pOV) + { + pOL = mpView->GetTextEditOutliner(); + } + } + else if(dynamic_cast< const OutlineViewShell *>( mpViewShell )) + { + pOL = &static_cast(mpView)->GetOutliner(); + pOV = static_cast(mpView)->GetViewByWindow( + mpViewShell->GetActiveWindow()); + } + + // insert special character + if (!pOV) + return; + + // prevent flicker + pOV->HideCursor(); + pOL->SetUpdateLayout(false); + + /* remember old attributes: + To do that, remove selected area before (it has to go anyway). + With that, we get unique attributes (and since there is no + DeleteSelected() in OutlinerView, it is deleted by inserting an + empty string). */ + pOV->InsertText( "" ); + + SfxItemSetFixed aOldSet( mpDoc->GetPool() ); + aOldSet.Put( pOV->GetAttribs() ); + + SfxUndoManager& rUndoMgr = pOL->GetUndoManager(); + ViewShellId nViewShellId = mpViewShell ? mpViewShell->GetViewShellBase().GetViewShellId() : ViewShellId(-1); + rUndoMgr.EnterListAction(SdResId(STR_UNDO_INSERT_SPECCHAR), + "", 0, nViewShellId ); + pOV->InsertText(aChars, true); + + // set attributes (set font) + SfxItemSet aSet(pOL->GetEmptyItemSet()); + SvxFontItem aFontItem (aFont.GetFamilyType(), aFont.GetFamilyName(), + aFont.GetStyleName(), aFont.GetPitch(), + aFont.GetCharSet(), + EE_CHAR_FONTINFO); + aSet.Put(aFontItem); + aFontItem.SetWhich(EE_CHAR_FONTINFO_CJK); + aSet.Put(aFontItem); + aFontItem.SetWhich(EE_CHAR_FONTINFO_CTL); + aSet.Put(aFontItem); + pOV->SetAttribs(aSet); + + ESelection aSel = pOV->GetSelection(); + aSel.nStartPara = aSel.nEndPara; + aSel.nStartPos = aSel.nEndPos; + pOV->SetSelection(aSel); + + // do not go ahead with setting attributes of special characters + pOV->GetOutliner()->QuickSetAttribs(aOldSet, aSel); + + rUndoMgr.LeaveListAction(); + + // show it again + pOL->SetUpdateLayout(true); + pOV->ShowCursor(); +} + +void FuBullet::GetSlotState( SfxItemSet& rSet, ViewShell const * pViewShell, SfxViewFrame* pViewFrame ) +{ + if( !(SfxItemState::DEFAULT == rSet.GetItemState( SID_CHARMAP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CHARMAP_CONTROL ) || + SfxItemState::DEFAULT == rSet.GetItemState( FN_INSERT_SOFT_HYPHEN ) || + SfxItemState::DEFAULT == rSet.GetItemState( FN_INSERT_HARDHYPHEN ) || + SfxItemState::DEFAULT == rSet.GetItemState( FN_INSERT_HARD_SPACE ) || + SfxItemState::DEFAULT == rSet.GetItemState( FN_INSERT_NNBSP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_INSERT_RLM ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_INSERT_LRM ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_INSERT_WJ ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_INSERT_ZWSP ))) + return; + + ::sd::View* pView = pViewShell ? pViewShell->GetView() : nullptr; + OutlinerView* pOLV = pView ? pView->GetTextEditOutlinerView() : nullptr; + + const bool bTextEdit = pOLV; + + SvtCTLOptions aCTLOptions; + const bool bCtlEnabled = aCTLOptions.IsCTLFontEnabled(); + + if(!bTextEdit ) + { + rSet.DisableItem(FN_INSERT_SOFT_HYPHEN); + rSet.DisableItem(FN_INSERT_HARDHYPHEN); + rSet.DisableItem(FN_INSERT_HARD_SPACE); + rSet.DisableItem(FN_INSERT_NNBSP); + rSet.DisableItem(SID_INSERT_WJ); + rSet.DisableItem(SID_INSERT_ZWSP); + } + + if( !bTextEdit && (dynamic_cast( pViewShell ) == nullptr) ) + { + rSet.DisableItem(SID_CHARMAP); + rSet.DisableItem(SID_CHARMAP_CONTROL); + } + + if(!bTextEdit || !bCtlEnabled ) + { + rSet.DisableItem(SID_INSERT_RLM); + rSet.DisableItem(SID_INSERT_LRM); + } + + if( pViewFrame ) + { + SfxBindings& rBindings = pViewFrame->GetBindings(); + + rBindings.SetVisibleState( SID_INSERT_RLM, bCtlEnabled ); + rBindings.SetVisibleState( SID_INSERT_LRM, bCtlEnabled ); + } +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuchar.cxx b/sd/source/ui/func/fuchar.cxx new file mode 100644 index 000000000..3935f64a1 --- /dev/null +++ b/sd/source/ui/func/fuchar.cxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuChar::FuChar ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuChar::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuChar( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuChar::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SfxItemSet aEditAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aEditAttr ); + + SfxItemSetFixed aNewAttr(mpViewShell->GetPool()); + aNewAttr.Put( aEditAttr, false ); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( pFact->CreateSdTabCharDialog(mpViewShell->GetFrameWeld(), &aNewAttr, mpDoc->GetDocSh() ) ); + if (rReq.GetSlot() == SID_CHAR_DLG_EFFECT) + { + pDlg->SetCurPageId("RID_SVXPAGE_CHAR_EFFECTS"); + } + + sal_uInt16 nResult = pDlg->Execute(); + + if( nResult != RET_OK ) + return; + + const SfxItemSet* pOutputSet = pDlg->GetOutputItemSet(); + SfxItemSet aOtherSet( *pOutputSet ); + + // and now the reverse process + const SvxBrushItem* pBrushItem = aOtherSet.GetItem( SID_ATTR_BRUSH_CHAR ); + + if ( pBrushItem ) + { + SvxColorItem aBackColorItem( pBrushItem->GetColor(), EE_CHAR_BKGCOLOR ); + aOtherSet.ClearItem( SID_ATTR_BRUSH_CHAR ); + aOtherSet.Put( aBackColorItem ); + } + + rReq.Done( aOtherSet ); + pArgs = rReq.GetArgs(); + } + mpView->SetAttributes(*pArgs); + + // invalidate the Slots which are in DrTxtObjBar + static const sal_uInt16 SidArray[] = { + SID_ATTR_CHAR_FONT, + SID_ATTR_CHAR_POSTURE, + SID_ATTR_CHAR_WEIGHT, + SID_ATTR_CHAR_SHADOWED, + SID_ATTR_CHAR_STRIKEOUT, + SID_ATTR_CHAR_UNDERLINE, + SID_ATTR_CHAR_FONTHEIGHT, + SID_ATTR_CHAR_COLOR, + SID_ATTR_CHAR_KERNING, + SID_ATTR_CHAR_CASEMAP, + SID_SET_SUPER_SCRIPT, + SID_SET_SUB_SCRIPT, + SID_ATTR_CHAR_BACK_COLOR, + 0 }; + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + + if( mpDoc->GetOnlineSpell() ) + { + if( SfxItemState::SET == pArgs->GetItemState(EE_CHAR_LANGUAGE, false ) || + SfxItemState::SET == pArgs->GetItemState(EE_CHAR_LANGUAGE_CJK, false ) || + SfxItemState::SET == pArgs->GetItemState(EE_CHAR_LANGUAGE_CTL, false ) ) + { + mpDoc->StopOnlineSpelling(); + mpDoc->StartOnlineSpelling(); + } + } +} + +void FuChar::Activate() +{ +} + +void FuChar::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fucon3d.cxx b/sd/source/ui/func/fucon3d.cxx new file mode 100644 index 000000000..fb844548f --- /dev/null +++ b/sd/source/ui/func/fucon3d.cxx @@ -0,0 +1,474 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace com::sun::star; + +namespace sd { + + +FuConstruct3dObject::FuConstruct3dObject ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuConstruct(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuConstruct3dObject::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstruct3dObject* pFunc; + rtl::Reference xFunc( pFunc = new FuConstruct3dObject( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent); + return xFunc; +} + +void FuConstruct3dObject::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); +} + +E3dCompoundObject* FuConstruct3dObject::ImpCreateBasic3DShape() +{ + E3dCompoundObject* p3DObj = nullptr; + + switch (nSlotId) + { + default: + case SID_3D_CUBE: + { + p3DObj = new E3dCubeObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B3DPoint(-2500, -2500, -2500), + ::basegfx::B3DVector(5000, 5000, 5000)); + break; + } + + case SID_3D_SPHERE: + { + p3DObj = new E3dSphereObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B3DPoint(0, 0, 0), + ::basegfx::B3DVector(5000, 5000, 5000)); + break; + } + + case SID_3D_SHELL: + { + XPolygon aXPoly(Point (0, 1250), 2500, 2500, 0_deg100, 9000_deg100, false); + aXPoly.Scale(5.0, 5.0); + + ::basegfx::B2DPolygon aB2DPolygon(aXPoly.getB2DPolygon()); + if(aB2DPolygon.areControlPointsUsed()) + { + aB2DPolygon = ::basegfx::utils::adaptiveSubdivideByAngle(aB2DPolygon); + } + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aB2DPolygon)); + + /* this is an open object, therefore it has to be handled double- + sided by default */ + p3DObj->SetMergedItem(makeSvx3DDoubleSidedItem(true)); + break; + } + + case SID_3D_HALF_SPHERE: + { + XPolygon aXPoly(Point (0, 1250), 2500, 2500, 0_deg100, 9000_deg100, false); + aXPoly.Scale(5.0, 5.0); + + aXPoly.Insert(0, Point (2400*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (2000*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (1500*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (1000*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (500*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (250*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (50*5, 1250*5), PolyFlags::Normal); + aXPoly.Insert(0, Point (0, 1250*5), PolyFlags::Normal); + + ::basegfx::B2DPolygon aB2DPolygon(aXPoly.getB2DPolygon()); + if(aB2DPolygon.areControlPointsUsed()) + { + aB2DPolygon = ::basegfx::utils::adaptiveSubdivideByAngle(aB2DPolygon); + } + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aB2DPolygon)); + break; + } + + case SID_3D_TORUS: + { + ::basegfx::B2DPolygon aB2DPolygon(::basegfx::utils::createPolygonFromCircle(::basegfx::B2DPoint(1000.0, 0.0), 500.0)); + if(aB2DPolygon.areControlPointsUsed()) + { + aB2DPolygon = ::basegfx::utils::adaptiveSubdivideByAngle(aB2DPolygon); + } + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aB2DPolygon)); + break; + } + + case SID_3D_CYLINDER: + { + ::basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(::basegfx::B2DPoint(0, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(450*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(500*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(500*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(450*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(0, -1000*5)); + aInnerPoly.setClosed(true); + + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aInnerPoly)); + break; + } + + case SID_3D_CONE: + { + ::basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(::basegfx::B2DPoint(0, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(25*5, -900*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, -800*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, -600*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, -200*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 200*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 600*5)); + aInnerPoly.append(::basegfx::B2DPoint(500*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(0, 1000*5)); + aInnerPoly.setClosed(true); + + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aInnerPoly)); + break; + } + + case SID_3D_PYRAMID: + { + ::basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(::basegfx::B2DPoint(0, -1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(25*5, -900*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, -800*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, -600*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, -200*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 200*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 600*5)); + aInnerPoly.append(::basegfx::B2DPoint(500*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(400*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(300*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(200*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(100*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(50*5, 1000*5)); + aInnerPoly.append(::basegfx::B2DPoint(0, 1000*5)); + aInnerPoly.setClosed(true); + + p3DObj = new E3dLatheObj( + mpView->getSdrModelFromSdrView(), + mpView->Get3DDefaultAttributes(), + ::basegfx::B2DPolyPolygon(aInnerPoly)); + p3DObj->SetMergedItem(makeSvx3DHorizontalSegmentsItem(4)); + break; + } + } + + return p3DObj; +} + +void FuConstruct3dObject::ImpPrepareBasic3DShape(E3dCompoundObject const * p3DObj, E3dScene *pScene) +{ + Camera3D aCamera = pScene->GetCamera (); + + // get transformed BoundVolume of the new object + basegfx::B3DRange aBoundVol; + basegfx::B3DRange aObjVol(p3DObj->GetBoundVolume()); + aObjVol.transform(p3DObj->GetTransform()); + aBoundVol.expand(aObjVol); + double fDeepth(aBoundVol.getDepth()); + + aCamera.SetPRP(::basegfx::B3DPoint(0.0, 0.0, 1000.0)); + aCamera.SetPosition(::basegfx::B3DPoint(0.0, 0.0, mpView->GetDefaultCamPosZ() + fDeepth / 2)); + aCamera.SetFocalLength(mpView->GetDefaultCamFocal()); + pScene->SetCamera(aCamera); + basegfx::B3DHomMatrix aTransformation; + + switch (nSlotId) + { + case SID_3D_CUBE: + { + aTransformation.rotate(basegfx::deg2rad(20), 0.0, 0.0); + } + break; + + case SID_3D_SPHERE: + { + } + break; + + case SID_3D_SHELL: + case SID_3D_HALF_SPHERE: + { + aTransformation.rotate(basegfx::deg2rad(200), 0.0, 0.0); + } + break; + + case SID_3D_CYLINDER: + case SID_3D_CONE: + case SID_3D_PYRAMID: + { + } + break; + + case SID_3D_TORUS: + { + aTransformation.rotate(basegfx::deg2rad(90), 0.0, 0.0); + } + break; + + default: + { + } + break; + } + + pScene->SetTransform(aTransformation * pScene->GetTransform()); + + SfxItemSet aAttr (mpViewShell->GetPool()); + pScene->SetMergedItemSetAndBroadcast(aAttr); +} + +bool FuConstruct3dObject::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + weld::WaitObject aWait(mpViewShell->GetFrameWeld()); + + E3dCompoundObject* p3DObj = ImpCreateBasic3DShape(); + E3dScene* pScene = mpView->SetCurrent3DObj(p3DObj); + + ImpPrepareBasic3DShape(p3DObj, pScene); + bReturn = mpView->BegCreatePreparedObject(aPnt, nDrgLog, pScene); + + SdrObject* pObj = mpView->GetCreateObj(); + + if (pObj) + { + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj); + + // extract LineStyle + aAttr.Put(XLineStyleItem (drawing::LineStyle_NONE)); + + pObj->SetMergedItemSet(aAttr); + } + } + + return bReturn; +} + +bool FuConstruct3dObject::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + + if ( mpView->IsCreateObj() && rMEvt.IsLeft() ) + { + if( mpView->EndCreateObj( SdrCreateCmd::ForceEnd ) ) + { + bReturn = true; + } + else + { + //Drag was too small to create object, so insert default object at click pos + Point aClickPos(mpWindow->PixelToLogic(rMEvt.GetPosPixel())); + sal_uInt32 nDefaultObjectSize(1000); + sal_Int32 nCenterOffset(-sal_Int32(nDefaultObjectSize / 2)); + aClickPos.AdjustX(nCenterOffset); + aClickPos.AdjustY(nCenterOffset); + + SdrPageView *pPV = mpView->GetSdrPageView(); + + if(mpView->IsSnapEnabled()) + aClickPos = mpView->GetSnapPos(aClickPos, pPV); + + ::tools::Rectangle aNewObjectRectangle(aClickPos, Size(nDefaultObjectSize, nDefaultObjectSize)); + SdrObjectUniquePtr pObjDefault = CreateDefaultObject(nSlotId, aNewObjectRectangle); + + bReturn = mpView->InsertObjectAtView(pObjDefault.release(), *pPV); + } + } + bReturn = FuConstruct::MouseButtonUp(rMEvt) || bReturn; + + if (!bPermanent) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstruct3dObject::Activate() +{ + mpView->SetCurrentObj(SdrObjKind::NONE); + + FuConstruct::Activate(); +} + +SdrObjectUniquePtr FuConstruct3dObject::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + + E3dCompoundObject* p3DObj = ImpCreateBasic3DShape(); + + // E3dView::SetCurrent3DObj part + // get transformed BoundVolume of the object + basegfx::B3DRange aObjVol(p3DObj->GetBoundVolume()); + aObjVol.transform(p3DObj->GetTransform()); + basegfx::B3DRange aVolume(aObjVol); + double fW(aVolume.getWidth()); + double fH(aVolume.getHeight()); + ::tools::Rectangle a3DRect(0, 0, static_cast<::tools::Long>(fW), static_cast<::tools::Long>(fH)); + std::unique_ptr< E3dScene, SdrObjectFreeOp > pScene(new E3dScene(*mpDoc)); + + // copied code from E3dView::InitScene + double fCamZ(aVolume.getMaxZ() + ((fW + fH) / 4.0)); + Camera3D aCam(pScene->GetCamera()); + aCam.SetAutoAdjustProjection(false); + aCam.SetViewWindow(- fW / 2, - fH / 2, fW, fH); + ::basegfx::B3DPoint aLookAt; + double fDefaultCamPosZ = mpView->GetDefaultCamPosZ(); + ::basegfx::B3DPoint aCamPos(0.0, 0.0, fCamZ < fDefaultCamPosZ ? fDefaultCamPosZ : fCamZ); + aCam.SetPosAndLookAt(aCamPos, aLookAt); + aCam.SetFocalLength(mpView->GetDefaultCamFocal()); + pScene->SetCamera(aCam); + pScene->InsertObject(p3DObj); + pScene->NbcSetSnapRect(a3DRect); + ImpPrepareBasic3DShape(p3DObj, pScene.get()); + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, p3DObj); + aAttr.Put(XLineStyleItem (drawing::LineStyle_NONE)); + p3DObj->SetMergedItemSet(aAttr); + + // make object interactive at once + pScene->SetBoundAndSnapRectsDirty(); + + // Take care of restrictions for the rectangle + ::tools::Rectangle aRect(rRectangle); + + switch(nID) + { + case SID_3D_CUBE: + case SID_3D_SPHERE: + case SID_3D_TORUS: + { + // force quadratic + ImpForceQuadratic(aRect); + break; + } + + case SID_3D_SHELL: + case SID_3D_HALF_SPHERE: + { + // force horizontal layout + break; + } + + case SID_3D_CYLINDER: + case SID_3D_CONE: + case SID_3D_PYRAMID: + { + // force vertical layout + break; + } + } + + // use changed rectangle, not original one + pScene->SetLogicRect(aRect); + + return SdrObjectUniquePtr(pScene.release()); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconarc.cxx b/sd/source/ui/func/fuconarc.cxx new file mode 100644 index 000000000..b5be93f9b --- /dev/null +++ b/sd/source/ui/func/fuconarc.cxx @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include + +using namespace com::sun::star; + +namespace sd { + + +FuConstructArc::FuConstructArc ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuConstruct( pViewSh, pWin, pView, pDoc, rReq ) +{ +} + +rtl::Reference FuConstructArc::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructArc* pFunc; + rtl::Reference xFunc( pFunc = new FuConstructArc( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent); + return xFunc; +} + +void FuConstructArc::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); + + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (!pArgs) + return; + + const SfxUInt32Item* pCenterX = rReq.GetArg(ID_VAL_CENTER_X); + const SfxUInt32Item* pCenterY = rReq.GetArg(ID_VAL_CENTER_Y); + const SfxUInt32Item* pAxisX = rReq.GetArg(ID_VAL_AXIS_X); + const SfxUInt32Item* pAxisY = rReq.GetArg(ID_VAL_AXIS_Y); + const SfxUInt32Item* pPhiStart = rReq.GetArg(ID_VAL_ANGLESTART); + const SfxUInt32Item* pPhiEnd = rReq.GetArg(ID_VAL_ANGLEEND); + + ::tools::Rectangle aNewRectangle (pCenterX->GetValue () - pAxisX->GetValue () / 2, + pCenterY->GetValue () - pAxisY->GetValue () / 2, + pCenterX->GetValue () + pAxisX->GetValue () / 2, + pCenterY->GetValue () + pAxisY->GetValue () / 2); + + Activate(); // sets aObjKind + SdrCircObj* pNewCircle = + new SdrCircObj( + mpView->getSdrModelFromSdrView(), + ToSdrCircKind(mpView->GetCurrentObjIdentifier()), + aNewRectangle, + Degree100(pPhiStart->GetValue() * 10), + Degree100(pPhiEnd->GetValue() * 10)); + SdrPageView *pPV = mpView->GetSdrPageView(); + + mpView->InsertObjectAtView(pNewCircle, *pPV, SdrInsertFlags::SETDEFLAYER); +} + +bool FuConstructArc::MouseButtonDown( const MouseEvent& rMEvt ) +{ + bool bReturn = FuConstruct::MouseButtonDown( rMEvt ); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegCreateObj(aPnt, nullptr, nDrgLog); + + SdrObject* pObj = mpView->GetCreateObj(); + + if (pObj) + { + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj); + + pObj->SetMergedItemSet(aAttr); + } + + bReturn = true; + } + return bReturn; +} + +bool FuConstructArc::MouseButtonUp( const MouseEvent& rMEvt ) +{ + bool bReturn = false; + bool bCreated = false; + + if ( mpView->IsCreateObj() && rMEvt.IsLeft() ) + { + const size_t nCount = mpView->GetSdrPageView()->GetObjList()->GetObjCount(); + + if (mpView->EndCreateObj(SdrCreateCmd::NextPoint) ) + { + if (nCount != mpView->GetSdrPageView()->GetObjList()->GetObjCount()) + { + bCreated = true; + } + } + + bReturn = true; + } + + bReturn = FuConstruct::MouseButtonUp (rMEvt) || bReturn; + + if (!bPermanent && bCreated) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstructArc::Activate() +{ + SdrObjKind aObjKind; + + switch( nSlotId ) + { + case SID_DRAW_ARC : + case SID_DRAW_CIRCLEARC: + { + aObjKind = SdrObjKind::CircleArc; + } + break; + + case SID_DRAW_PIE : + case SID_DRAW_PIE_NOFILL : + case SID_DRAW_CIRCLEPIE : + case SID_DRAW_CIRCLEPIE_NOFILL: + { + aObjKind = SdrObjKind::CircleSection; + } + break; + + case SID_DRAW_ELLIPSECUT : + case SID_DRAW_ELLIPSECUT_NOFILL: + case SID_DRAW_CIRCLECUT : + case SID_DRAW_CIRCLECUT_NOFILL : + { + aObjKind = SdrObjKind::CircleCut; + } + break; + + default: + { + aObjKind = SdrObjKind::CircleArc; + } + break; + } + + mpView->SetCurrentObj(aObjKind); + + FuConstruct::Activate(); +} + +SdrObjectUniquePtr FuConstructArc::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if(pObj) + { + if( dynamic_cast< const SdrCircObj *>( pObj.get() ) != nullptr) + { + ::tools::Rectangle aRect(rRectangle); + + if(SID_DRAW_ARC == nID || + SID_DRAW_CIRCLEARC == nID || + SID_DRAW_CIRCLEPIE == nID || + SID_DRAW_CIRCLEPIE_NOFILL == nID || + SID_DRAW_CIRCLECUT == nID || + SID_DRAW_CIRCLECUT_NOFILL == nID) + { + // force quadratic + ImpForceQuadratic(aRect); + } + + pObj->SetLogicRect(aRect); + + SfxItemSet aAttr(mpDoc->GetPool()); + aAttr.Put(makeSdrCircStartAngleItem(9000_deg100)); + aAttr.Put(makeSdrCircEndAngleItem(0_deg100)); + + if(SID_DRAW_PIE_NOFILL == nID || + SID_DRAW_CIRCLEPIE_NOFILL == nID || + SID_DRAW_ELLIPSECUT_NOFILL == nID || + SID_DRAW_CIRCLECUT_NOFILL == nID) + { + aAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + + pObj->SetMergedItemSet(aAttr); + } + else + { + OSL_FAIL("Object is NO circle object"); + } + } + + return pObj; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconbez.cxx b/sd/source/ui/func/fuconbez.cxx new file mode 100644 index 000000000..b123e9c2d --- /dev/null +++ b/sd/source/ui/func/fuconbez.cxx @@ -0,0 +1,556 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace ::com::sun::star::uno; + +namespace sd { + +/*//Extra attributes coming from parameters + sal_uInt16 mnTransparence; // Default: 0 + OUString msColor; // Default: "" + sal_uInt16 mnWidth; // Default: 0 + OUString msShapeName; // Default: ""*/ +FuConstructBezierPolygon::FuConstructBezierPolygon ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuConstruct(pViewSh, pWin, pView, pDoc, rReq), + nEditMode(SID_BEZIER_MOVE), + mnTransparence(0), + mnWidth(0) +{ +} + +namespace{ + +/// Checks to see if the request has a parameter of IsSticky:bool=true +/// It means that the selected command/button will stay selected after use +bool isSticky(const SfxRequest& rReq) +{ + const SfxItemSet *pArgs = rReq.GetArgs (); + if (pArgs) + { + const SfxBoolItem* pIsSticky = rReq.GetArg(FN_PARAM_4); + if (pIsSticky && pIsSticky->GetValue()) + return true; + } + + return false; +} + +} + +rtl::Reference FuConstructBezierPolygon::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructBezierPolygon* pFunc; + rtl::Reference xFunc( pFunc = new FuConstructBezierPolygon( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent || isSticky(rReq)); + return xFunc; +} + +void FuConstructBezierPolygon::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + return; + + const SfxUnoAnyItem* pPoolItem = pArgs->GetItemIfSet( SID_ADD_MOTION_PATH ); + if( pPoolItem ) + maTargets = pPoolItem->GetValue(); + + if (nSlotId != SID_DRAW_FREELINE_NOFILL) + return; + + const SfxUInt16Item* pTransparence = rReq.GetArg(FN_PARAM_1); + const SfxStringItem* pColor = rReq.GetArg(FN_PARAM_2); + const SfxUInt16Item* pWidth = rReq.GetArg(FN_PARAM_3); + const SfxStringItem* pShapeName = rReq.GetArg(SID_SHAPE_NAME); + + if (pTransparence && pTransparence->GetValue() > 0) + { + mnTransparence = pTransparence->GetValue(); + } + if (pColor && !pColor->GetValue().isEmpty()) + { + msColor = pColor->GetValue(); + } + if (pWidth && pWidth->GetValue() > 0) + { + mnWidth = pWidth->GetValue(); + } + if (pShapeName && !pShapeName->GetValue().isEmpty()) + { + msShapeName = pShapeName->GetValue(); + } +} + +bool FuConstructBezierPolygon::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::Handle || rMEvt.IsMod1()) + { + mpView->SetEditMode(SdrViewEditMode::Edit); + } + else + { + mpView->SetEditMode(SdrViewEditMode::Create); + } + + if (aVEvt.meEvent == SdrEventKind::BeginTextEdit) + { + // here, we do not allow text input + aVEvt.meEvent = SdrEventKind::BeginDragObj; + mpView->EnableExtendedMouseEventDispatcher(false); + } + else + { + mpView->EnableExtendedMouseEventDispatcher(true); + } + + if (eHit == SdrHitKind::MarkedObject && nEditMode == SID_BEZIER_INSERT) + { + // insert gluepoint + mpView->BegInsObjPoint(aMDPos, rMEvt.IsMod1()); + } + else + { + mpView->MouseButtonDown(rMEvt, mpWindow->GetOutDev()); + + SdrObject* pObj = mpView->GetCreateObj(); + + if (pObj) + { + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj); + SetAttributes(aAttr, pObj); + pObj->SetMergedItemSet(aAttr); + } + } + + return bReturn; +} + +bool FuConstructBezierPolygon::MouseButtonUp(const MouseEvent& rMEvt ) +{ + bool bReturn = false; + bool bCreated = false; + + SdrViewEvent aVEvt; + mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONUP, aVEvt); + + const size_t nCount = mpView->GetSdrPageView()->GetObjList()->GetObjCount(); + + if (mpView->IsInsObjPoint()) + { + mpView->EndInsObjPoint(SdrCreateCmd::ForceEnd); + } + else + { + mpView->MouseButtonUp(rMEvt, mpWindow->GetOutDev()); + } + + if (aVEvt.meEvent == SdrEventKind::EndCreate) + { + bReturn = true; + + if (nCount+1 == mpView->GetSdrPageView()->GetObjList()->GetObjCount()) + { + bCreated = true; + } + + // trick to suppress FuDraw::DoubleClick + bMBDown = false; + + } + + bReturn = FuConstruct::MouseButtonUp(rMEvt) || bReturn; + + bool bDeleted = false; + if( bCreated && maTargets.hasValue() ) + { + SdrPathObj* pPathObj = dynamic_cast< SdrPathObj* >( mpView->GetSdrPageView()->GetObjList()->GetObj( nCount ) ); + SdPage* pPage = dynamic_cast< SdPage* >( pPathObj ? pPathObj->getSdrPageFromSdrObject() : nullptr ); + if( pPage ) + { + std::shared_ptr< sd::MainSequence > pMainSequence( pPage->getMainSequence() ); + if( pMainSequence ) + { + Sequence< Any > aTargets; + maTargets >>= aTargets; + + sal_Int32 nTCount = aTargets.getLength(); + if( nTCount > 1 ) + { + const Any* pTarget = aTargets.getConstArray(); + double fDuration = 0.0; + *pTarget++ >>= fDuration; + bool bFirst = true; + + OUString sPresetId; + switch(nSlotId) + { + case SID_DRAW_BEZIER_NOFILL: + sPresetId = "libo-motionpath-curve"; + break; + case SID_DRAW_POLYGON_NOFILL: + sPresetId = "libo-motionpath-polygon"; + break; + case SID_DRAW_FREELINE_NOFILL: + sPresetId = "libo-motionpath-freeform-line"; + break; + } + + while( --nTCount ) + { + CustomAnimationEffectPtr pCreated( pMainSequence->append( *pPathObj, *pTarget++, fDuration, sPresetId) ); + if( bFirst ) + bFirst = false; + else + pCreated->setNodeType( css::presentation::EffectNodeType::WITH_PREVIOUS ); + } + } + } + } + mpView->DeleteMarked(); + bDeleted = true; + } + + if ((!bPermanent && bCreated) || bDeleted) + { + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + return bReturn; +} + +void FuConstructBezierPolygon::Activate() +{ + mpView->EnableExtendedMouseEventDispatcher(true); + + SdrObjKind eKind; + + switch (nSlotId) + { + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_XPOLYGON_NOFILL: + { + eKind = SdrObjKind::PolyLine; + } + break; + + case SID_DRAW_POLYGON: + case SID_DRAW_XPOLYGON: + { + eKind = SdrObjKind::Polygon; + } + break; + + case SID_DRAW_BEZIER_NOFILL: + { + eKind = SdrObjKind::PathLine; + } + break; + + case SID_DRAW_BEZIER_FILL: + { + eKind = SdrObjKind::PathFill; + } + break; + + case SID_DRAW_FREELINE_NOFILL: + { + eKind = SdrObjKind::FreehandLine; + } + break; + + case SID_DRAW_FREELINE: + { + eKind = SdrObjKind::FreehandFill; + } + break; + + default: + { + eKind = SdrObjKind::PathLine; + } + break; + } + + mpView->SetCurrentObj(eKind); + + FuConstruct::Activate(); +} + +void FuConstructBezierPolygon::Deactivate() +{ + mpView->EnableExtendedMouseEventDispatcher(false); + + FuConstruct::Deactivate(); +} + +void FuConstructBezierPolygon::SelectionHasChanged() +{ + FuDraw::SelectionHasChanged(); + + mpViewShell->GetViewShellBase().GetToolBarManager()->SelectionHasChanged( + *mpViewShell, + *mpView); +} + +namespace { +/// Returns the color based on the color names listed in core/include/tools/color.hxx +/// Feel free to extend with more color names from color.hxx +Color strToColor(std::u16string_view sColor) +{ + Color aColor = COL_AUTO; + + if (sColor == u"COL_GRAY") + aColor = COL_GRAY; + else if (sColor == u"COL_GRAY3") + aColor = COL_GRAY3; + else if (sColor == u"COL_GRAY7") + aColor = COL_GRAY7; + + return aColor; +} +} + +void FuConstructBezierPolygon::SetAttributes(SfxItemSet& rAttr, SdrObject *pObj) +{ + if (nSlotId == SID_DRAW_FREELINE_NOFILL) + { + if (mnTransparence > 0 && mnTransparence <= 100) + rAttr.Put(XLineTransparenceItem(mnTransparence)); + if (!msColor.isEmpty()) + rAttr.Put(XLineColorItem(OUString(), strToColor(msColor))); + if (mnWidth > 0) + rAttr.Put(XLineWidthItem(mnWidth)); + if (!msShapeName.isEmpty()) + pObj->SetName(msShapeName); + } +} + +/** + * Set current bezier edit mode + */ +void FuConstructBezierPolygon::SetEditMode(sal_uInt16 nMode) +{ + nEditMode = nMode; + ForcePointer(); + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_BEZIER_MOVE); + rBindings.Invalidate(SID_BEZIER_INSERT); +} + +SdrObjectUniquePtr FuConstructBezierPolygon::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + // case SID_DRAW_POLYGON: + // case SID_DRAW_POLYGON_NOFILL: + // case SID_DRAW_XPOLYGON: + // case SID_DRAW_XPOLYGON_NOFILL: + // case SID_DRAW_FREELINE: + // case SID_DRAW_FREELINE_NOFILL: + // case SID_DRAW_BEZIER_FILL: // BASIC + // case SID_DRAW_BEZIER_NOFILL: // BASIC + + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if(pObj) + { + if( auto pPathObj = dynamic_cast< SdrPathObj *>( pObj.get() ) ) + { + basegfx::B2DPolyPolygon aPoly; + + switch(nID) + { + case SID_DRAW_BEZIER_FILL: + { + const sal_Int32 nWdt(rRectangle.GetWidth() / 2); + const sal_Int32 nHgt(rRectangle.GetHeight() / 2); + const basegfx::B2DPolygon aInnerPoly(basegfx::utils::createPolygonFromEllipse(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y()), nWdt, nHgt)); + + aPoly.append(aInnerPoly); + break; + } + case SID_DRAW_BEZIER_NOFILL: + { + basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom())); + + const basegfx::B2DPoint aCenterBottom(rRectangle.Center().X(), rRectangle.Bottom()); + aInnerPoly.appendBezierSegment( + aCenterBottom, + aCenterBottom, + basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y())); + + const basegfx::B2DPoint aCenterTop(rRectangle.Center().X(), rRectangle.Top()); + aInnerPoly.appendBezierSegment( + aCenterTop, + aCenterTop, + basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top())); + + aPoly.append(aInnerPoly); + break; + } + case SID_DRAW_FREELINE: + case SID_DRAW_FREELINE_NOFILL: + { + basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom())); + + aInnerPoly.appendBezierSegment( + basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top()), + basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Top()), + basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y())); + + aInnerPoly.appendBezierSegment( + basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom()), + basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom()), + basegfx::B2DPoint(rRectangle.Right(), rRectangle.Top())); + + if(SID_DRAW_FREELINE == nID) + { + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom())); + } + else + { + aInnerPoly.setClosed(true); + } + + aPoly.append(aInnerPoly); + break; + } + case SID_DRAW_XPOLYGON: + case SID_DRAW_XPOLYGON_NOFILL: + { + basegfx::B2DPolygon aInnerPoly; + + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Top())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Center().Y())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Center().Y())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Right(), rRectangle.Bottom())); + + if(SID_DRAW_XPOLYGON_NOFILL == nID) + { + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom())); + } + else + { + aInnerPoly.setClosed(true); + } + + aPoly.append(aInnerPoly); + break; + } + case SID_DRAW_POLYGON: + case SID_DRAW_POLYGON_NOFILL: + { + basegfx::B2DPolygon aInnerPoly; + const sal_Int32 nWdt(rRectangle.GetWidth()); + const sal_Int32 nHgt(rRectangle.GetHeight()); + + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Bottom())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 30) / 100, rRectangle.Top() + (nHgt * 70) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left(), rRectangle.Top() + (nHgt * 15) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 65) / 100, rRectangle.Top())); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + nWdt, rRectangle.Top() + (nHgt * 30) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 80) / 100, rRectangle.Top() + (nHgt * 50) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Left() + (nWdt * 80) / 100, rRectangle.Top() + (nHgt * 75) / 100)); + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Bottom(), rRectangle.Right())); + + if(SID_DRAW_POLYGON_NOFILL == nID) + { + aInnerPoly.append(basegfx::B2DPoint(rRectangle.Center().X(), rRectangle.Bottom())); + } + else + { + aInnerPoly.setClosed(true); + } + + aPoly.append(aInnerPoly); + break; + } + } + + pPathObj->SetPathPoly(aPoly); + } + else + { + OSL_FAIL("Object is NO path object"); + } + + pObj->SetLogicRect(rRectangle); + } + + return pObj; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconcs.cxx b/sd/source/ui/func/fuconcs.cxx new file mode 100644 index 000000000..806960dd2 --- /dev/null +++ b/sd/source/ui/func/fuconcs.cxx @@ -0,0 +1,261 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sd { + + +FuConstructCustomShape::FuConstructCustomShape ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) : + FuConstruct(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuConstructCustomShape::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructCustomShape* pFunc; + rtl::Reference xFunc( pFunc = new FuConstructCustomShape( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent( bPermanent ); + return xFunc; +} + +void FuConstructCustomShape::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if ( pArgs ) + { + const SfxStringItem& rItm = static_cast(pArgs->Get( rReq.GetSlot() )); + aCustomShape = rItm.GetValue(); + } + + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); +} + +bool FuConstructCustomShape::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + mpView->BegCreateObj(aPnt, nullptr, nDrgLog); + + SdrObject* pObj = mpView->GetCreateObj(); + if ( pObj ) + { + SetAttributes( pObj ); + bool bForceFillStyle = true; + bool bForceNoFillStyle = false; + if ( static_cast(pObj)->UseNoFillStyle() ) + { + bForceFillStyle = false; + bForceNoFillStyle = true; + } + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet( aAttr, pObj, bForceFillStyle, bForceNoFillStyle ); + pObj->SetMergedItemSet(aAttr); + } + } + + return bReturn; +} + +bool FuConstructCustomShape::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn(false); + + if(mpView->IsCreateObj() && rMEvt.IsLeft()) + { + SdrObject* pObj = mpView->GetCreateObj(); + if( pObj && mpView->EndCreateObj( SdrCreateCmd::ForceEnd ) ) + { + bReturn = true; + } + else + { + //Drag was too small to create object, so insert default object at click pos + Point aClickPos(mpWindow->PixelToLogic(rMEvt.GetPosPixel())); + sal_uInt32 nDefaultObjectSize(1000); + sal_Int32 nCenterOffset(-sal_Int32(nDefaultObjectSize / 2)); + aClickPos.AdjustX(nCenterOffset); + aClickPos.AdjustY(nCenterOffset); + + SdrPageView *pPV = mpView->GetSdrPageView(); + if(mpView->IsSnapEnabled()) + aClickPos = mpView->GetSnapPos(aClickPos, pPV); + + ::tools::Rectangle aNewObjectRectangle(aClickPos, Size(nDefaultObjectSize, nDefaultObjectSize)); + SdrObjectUniquePtr pObjDefault = CreateDefaultObject(nSlotId, aNewObjectRectangle); + + bReturn = mpView->InsertObjectAtView(pObjDefault.release(), *pPV); + } + } + bReturn = FuConstruct::MouseButtonUp (rMEvt) || bReturn; + + if (!bPermanent) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstructCustomShape::Activate() +{ + mpView->SetCurrentObj( SdrObjKind::CustomShape ); + FuConstruct::Activate(); +} + +/** + * set attribute for the object to be created + */ +void FuConstructCustomShape::SetAttributes( SdrObject* pObj ) +{ + bool bAttributesAppliedFromGallery = false; + + if ( GalleryExplorer::GetSdrObjCount( GALLERY_THEME_POWERPOINT ) ) + { + std::vector< OUString > aObjList; + if ( GalleryExplorer::FillObjListTitle( GALLERY_THEME_POWERPOINT, aObjList ) ) + { + for ( std::vector::size_type i = 0; i < aObjList.size(); i++ ) + { + if ( aObjList[ i ].equalsIgnoreAsciiCase( aCustomShape ) ) + { + FmFormModel aFormModel; + SfxItemPool& rPool(aFormModel.GetItemPool()); + rPool.FreezeIdRanges(); + + if ( GalleryExplorer::GetSdrObj( GALLERY_THEME_POWERPOINT, i, &aFormModel ) ) + { + const SdrPage* pPage = aFormModel.GetPage( 0 ); + if ( pPage ) + { + const SdrObject* pSourceObj = pPage->GetObj( 0 ); + if( pSourceObj ) + { + const SfxItemSet& rSource = pSourceObj->GetMergedItemSet(); + SfxItemSetFixed< + // Ranges from SdrAttrObj: + SDRATTR_START, SDRATTR_SHADOW_LAST, + SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST, + SDRATTR_TEXTDIRECTION, + SDRATTR_TEXTDIRECTION, + // Graphic attributes, 3D properties, + // CustomShape properties: + SDRATTR_GRAF_FIRST, + SDRATTR_CUSTOMSHAPE_LAST, + // Range from SdrTextObj: + EE_ITEMS_START, EE_ITEMS_END> aDest(pObj->getSdrModelFromSdrObject().GetItemPool()); + aDest.Set( rSource ); + pObj->SetMergedItemSet( aDest ); + // Enables Word-wrap by default (tdf#134369) + pObj->SetMergedItem( SdrOnOffItem( SDRATTR_TEXT_WORDWRAP, true ) ); + Degree100 nAngle = pSourceObj->GetRotateAngle(); + if ( nAngle ) + pObj->NbcRotate( pObj->GetSnapRect().Center(), nAngle ); + bAttributesAppliedFromGallery = true; + } + } + } + break; + } + } + } + } + if ( !bAttributesAppliedFromGallery ) + { + pObj->SetMergedItem( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + pObj->SetMergedItem( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + pObj->SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) ); + pObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( false ) ); + static_cast(pObj)->MergeDefaultAttributes( &aCustomShape ); + } +} + +const OUString& FuConstructCustomShape::GetShapeType() const +{ + return aCustomShape; +} + +SdrObjectUniquePtr FuConstructCustomShape::CreateDefaultObject(const sal_uInt16, const ::tools::Rectangle& rRectangle) +{ + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if( pObj ) + { + ::tools::Rectangle aRect( rRectangle ); + if ( doConstructOrthogonal() ) + ImpForceQuadratic( aRect ); + pObj->SetLogicRect( aRect ); + SetAttributes( pObj.get() ); + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj.get()); + pObj->SetMergedItemSet(aAttr); + } + return pObj; +} + +// #i33136# +bool FuConstructCustomShape::doConstructOrthogonal() const +{ + return SdrObjCustomShape::doConstructOrthogonal(aCustomShape); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconnct.cxx b/sd/source/ui/func/fuconnct.cxx new file mode 100644 index 000000000..fc95b4907 --- /dev/null +++ b/sd/source/ui/func/fuconnct.cxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuConnectionDlg::FuConnectionDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuConnectionDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuConnectionDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuConnectionDlg::DoExecute( SfxRequest& rReq ) +{ + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSfxDialog(rReq.GetFrameWeld(), aNewAttr, mpView, RID_SVXPAGE_CONNECTION)); + + if( pDlg->Execute() == RET_OK ) + { + rReq.Done( *pDlg->GetOutputItemSet() ); + pArgs = rReq.GetArgs(); + } + } + if( pArgs ) + mpView->SetAttributes( *pArgs ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconrec.cxx b/sd/source/ui/func/fuconrec.cxx new file mode 100644 index 000000000..d93ef2849 --- /dev/null +++ b/sd/source/ui/func/fuconrec.cxx @@ -0,0 +1,1096 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace com::sun::star; + +namespace sd { + + +FuConstructRectangle::FuConstructRectangle ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuConstruct(pViewSh, pWin, pView, pDoc, rReq) + , mnFillTransparence(0) + , mnLineStyle(SAL_MAX_UINT16) +{ +} + +namespace{ + +/// Checks to see if the request has a parameter of IsSticky:bool=true +/// It means that the selected command/button will stay selected after use +bool isSticky(const SfxRequest& rReq) +{ + const SfxItemSet *pArgs = rReq.GetArgs (); + if (pArgs) + { + const SfxBoolItem* pIsSticky = rReq.GetArg(FN_PARAM_4); + if (pIsSticky && pIsSticky->GetValue()) + return true; + } + + return false; +} + +} + +rtl::Reference FuConstructRectangle::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructRectangle* pFunc; + rtl::Reference xFunc( pFunc = new FuConstructRectangle( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent || isSticky(rReq)); + return xFunc; +} + +void FuConstructRectangle::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); + + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (pArgs) + { + switch (nSlotId) + { + case SID_DRAW_ELLIPSE : + { + const SfxUInt32Item* pCenterX = rReq.GetArg(ID_VAL_CENTER_X); + const SfxUInt32Item* pCenterY = rReq.GetArg(ID_VAL_CENTER_Y); + const SfxUInt32Item* pAxisX = rReq.GetArg(ID_VAL_AXIS_X); + const SfxUInt32Item* pAxisY = rReq.GetArg(ID_VAL_AXIS_Y); + + if (!pCenterX || !pCenterY || !pAxisX || !pAxisY) + break; + + ::tools::Rectangle aNewRectangle (pCenterX->GetValue () - pAxisX->GetValue () / 2, + pCenterY->GetValue () - pAxisY->GetValue () / 2, + pCenterX->GetValue () + pAxisX->GetValue () / 2, + pCenterY->GetValue () + pAxisY->GetValue () / 2); + SdrCircObj *pNewCircle = new SdrCircObj( + mpView->getSdrModelFromSdrView(), + SdrCircKind::Full, + aNewRectangle); + SdrPageView *pPV = mpView->GetSdrPageView(); + + mpView->InsertObjectAtView(pNewCircle, *pPV, SdrInsertFlags::SETDEFLAYER | SdrInsertFlags::SETDEFATTR); + } + break; + + case SID_DRAW_RECT : + { + const SfxUInt32Item* pMouseStartX = rReq.GetArg(ID_VAL_MOUSESTART_X); + const SfxUInt32Item* pMouseStartY = rReq.GetArg(ID_VAL_MOUSESTART_Y); + const SfxUInt32Item* pMouseEndX = rReq.GetArg(ID_VAL_MOUSEEND_X); + const SfxUInt32Item* pMouseEndY = rReq.GetArg(ID_VAL_MOUSEEND_Y); + const SfxUInt16Item* pFillTransparence = rReq.GetArg(FN_PARAM_1); + const SfxStringItem* pFillColor = rReq.GetArg(FN_PARAM_2); + const SfxUInt16Item* pLineStyle = rReq.GetArg(FN_PARAM_3); + const SfxStringItem* pShapeName = rReq.GetArg(SID_SHAPE_NAME); + + if (pFillTransparence && pFillTransparence->GetValue() > 0) + { + mnFillTransparence = pFillTransparence->GetValue(); + } + if (pFillColor && !pFillColor->GetValue().isEmpty()) + { + msFillColor = pFillColor->GetValue(); + } + if (pLineStyle) + { + mnLineStyle = pLineStyle->GetValue(); + } + if (pShapeName && !pShapeName->GetValue().isEmpty()) + { + msShapeName = pShapeName->GetValue(); + } + + if (!pMouseStartX || !pMouseStartY || !pMouseEndX || !pMouseEndY) + break; + + ::tools::Rectangle aNewRectangle (pMouseStartX->GetValue (), + pMouseStartY->GetValue (), + pMouseEndX->GetValue (), + pMouseEndY->GetValue ()); + SdrRectObj *pNewRect = new SdrRectObj( + mpView->getSdrModelFromSdrView(), + aNewRectangle); + SdrPageView *pPV = mpView->GetSdrPageView(); + + mpView->InsertObjectAtView(pNewRect, *pPV, SdrInsertFlags::SETDEFLAYER | SdrInsertFlags::SETDEFATTR); + } + break; + } + } + + if (nSlotId == SID_TOOL_CONNECTOR || + nSlotId == SID_CONNECTOR_ARROW_START || + nSlotId == SID_CONNECTOR_ARROW_END || + nSlotId == SID_CONNECTOR_ARROWS || + nSlotId == SID_CONNECTOR_CIRCLE_START || + nSlotId == SID_CONNECTOR_CIRCLE_END || + nSlotId == SID_CONNECTOR_CIRCLES || + nSlotId == SID_CONNECTOR_LINE || + nSlotId == SID_CONNECTOR_LINE_ARROW_START || + nSlotId == SID_CONNECTOR_LINE_ARROW_END || + nSlotId == SID_CONNECTOR_LINE_ARROWS || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINE_CIRCLES || + nSlotId == SID_CONNECTOR_CURVE || + nSlotId == SID_CONNECTOR_CURVE_ARROW_START || + nSlotId == SID_CONNECTOR_CURVE_ARROW_END || + nSlotId == SID_CONNECTOR_CURVE_ARROWS || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_START || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_END || + nSlotId == SID_CONNECTOR_CURVE_CIRCLES || + nSlotId == SID_CONNECTOR_LINES || + nSlotId == SID_CONNECTOR_LINES_ARROW_START || + nSlotId == SID_CONNECTOR_LINES_ARROW_END || + nSlotId == SID_CONNECTOR_LINES_ARROWS || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINES_CIRCLES || + nSlotId == SID_LINE_ARROW_START || + nSlotId == SID_LINE_ARROW_END || + nSlotId == SID_LINE_ARROWS || + nSlotId == SID_LINE_ARROW_CIRCLE || + nSlotId == SID_LINE_CIRCLE_ARROW || + nSlotId == SID_LINE_ARROW_SQUARE || + nSlotId == SID_LINE_SQUARE_ARROW ) + { + mpView->UnmarkAll(); + } +} + +bool FuConstructRectangle::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if (mpView->GetCurrentObjIdentifier() == SdrObjKind::Caption) + { + Size aCaptionSize(846, 846); // (4x2)cm + bReturn = mpView->BegCreateCaptionObj(aPnt, aCaptionSize, + nullptr, nDrgLog); + } + else + { + mpView->BegCreateObj(aPnt, nullptr, nDrgLog); + } + + SdrObject* pObj = mpView->GetCreateObj(); + + if (pObj) + { + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj); + SetAttributes(aAttr, pObj); + SetLineEnds(aAttr, *pObj); + pObj->SetMergedItemSet(aAttr); + + if( nSlotId == SID_DRAW_CAPTION_VERTICAL ) + static_cast(pObj)->SetVerticalWriting( true ); + } + } + return bReturn; +} + +bool FuConstructRectangle::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn(false); + + if(mpView->IsCreateObj() && rMEvt.IsLeft()) + { + SdrObject* pObj = mpView->GetCreateObj(); + + if(pObj && mpView->EndCreateObj(SdrCreateCmd::ForceEnd)) + { + if(SID_DRAW_MEASURELINE == nSlotId) + { + SdrLayerAdmin& rAdmin = mpDoc->GetLayerAdmin(); + pObj->SetLayer(rAdmin.GetLayerID(sUNO_LayerName_measurelines)); + } + + // init text position when vertical caption object is created + if( dynamic_cast< const SdrCaptionObj *>( pObj ) != nullptr && SID_DRAW_CAPTION_VERTICAL == nSlotId) + { + // draw text object, needs to be initialized when vertical text is used + SfxItemSet aSet(pObj->GetMergedItemSet()); + + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + + // Correct the value of SDRATTR_TEXTDIRECTION to avoid SetItemSet + // calling SetVerticalWriting() again since this item may not yet + // be set at the object and thus may differ from vertical state of + // the object. + aSet.Put(SvxWritingModeItem(css::text::WritingMode_TB_RL, SDRATTR_TEXTDIRECTION)); + pObj->SetMergedItemSet(aSet); + } + + bReturn = true; + } + else + { + //Drag was too small to create object, so insert default object at click pos + Point aClickPos(mpWindow->PixelToLogic(rMEvt.GetPosPixel())); + sal_uInt32 nDefaultObjectSize(1500); + sal_Int32 nCenterOffset(-sal_Int32(nDefaultObjectSize / 2)); + aClickPos.AdjustX(nCenterOffset); + aClickPos.AdjustY(nCenterOffset); + + SdrPageView *pPV = mpView->GetSdrPageView(); + if(mpView->IsSnapEnabled()) + aClickPos = mpView->GetSnapPos(aClickPos, pPV); + + ::tools::Rectangle aNewObjectRectangle(aClickPos, Size(nDefaultObjectSize, nDefaultObjectSize)); + SdrObjectUniquePtr pObjDefault = CreateDefaultObject(nSlotId, aNewObjectRectangle); + + bReturn = mpView->InsertObjectAtView(pObjDefault.release(), *pPV); + } + } + + bReturn = FuConstruct::MouseButtonUp (rMEvt) || bReturn; + + if (!bPermanent) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstructRectangle::Activate() +{ + SdrObjKind aObjKind; + + switch (nSlotId) + { + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_END: + case SID_LINE_ARROWS: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_SQUARE_ARROW: + mpView->SetGlueVisible(); + [[fallthrough]]; + case SID_DRAW_LINE : + case SID_DRAW_XLINE: + aObjKind = SdrObjKind::Line; + break; + + case SID_DRAW_MEASURELINE: + { + aObjKind = SdrObjKind::Measure; + } + break; + + case SID_DRAW_RECT : + case SID_DRAW_RECT_NOFILL : + case SID_DRAW_RECT_ROUND : + case SID_DRAW_RECT_ROUND_NOFILL: + case SID_DRAW_SQUARE : + case SID_DRAW_SQUARE_NOFILL : + case SID_DRAW_SQUARE_ROUND : + case SID_DRAW_SQUARE_ROUND_NOFILL: + { + aObjKind = SdrObjKind::Rectangle; + } + break; + + case SID_DRAW_ELLIPSE : + case SID_DRAW_ELLIPSE_NOFILL: + case SID_DRAW_CIRCLE : + case SID_DRAW_CIRCLE_NOFILL : + { + aObjKind = SdrObjKind::CircleOrEllipse; + } + break; + + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + { + aObjKind = SdrObjKind::Caption; + } + break; + + case SID_TOOL_CONNECTOR: + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_CURVE: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_CONNECTOR_CURVE_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLES: + case SID_CONNECTOR_LINES: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLES: + { + aObjKind = SdrObjKind::Edge; + mpView->SetGlueVisible(); + } + break; + case SID_INSERT_SIGNATURELINE: + { + aObjKind = SdrObjKind::Graphic; + } + break; + + default: + { + aObjKind = SdrObjKind::Rectangle; + } + break; + } + + mpView->SetCurrentObj(aObjKind); + + FuConstruct::Activate(); +} + +void FuConstructRectangle::Deactivate() +{ + if( nSlotId == SID_TOOL_CONNECTOR || + nSlotId == SID_CONNECTOR_ARROW_START || + nSlotId == SID_CONNECTOR_ARROW_END || + nSlotId == SID_CONNECTOR_ARROWS || + nSlotId == SID_CONNECTOR_CIRCLE_START || + nSlotId == SID_CONNECTOR_CIRCLE_END || + nSlotId == SID_CONNECTOR_CIRCLES || + nSlotId == SID_CONNECTOR_LINE || + nSlotId == SID_CONNECTOR_LINE_ARROW_START || + nSlotId == SID_CONNECTOR_LINE_ARROW_END || + nSlotId == SID_CONNECTOR_LINE_ARROWS || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINE_CIRCLES || + nSlotId == SID_CONNECTOR_CURVE || + nSlotId == SID_CONNECTOR_CURVE_ARROW_START || + nSlotId == SID_CONNECTOR_CURVE_ARROW_END || + nSlotId == SID_CONNECTOR_CURVE_ARROWS || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_START || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_END || + nSlotId == SID_CONNECTOR_CURVE_CIRCLES || + nSlotId == SID_CONNECTOR_LINES || + nSlotId == SID_CONNECTOR_LINES_ARROW_START || + nSlotId == SID_CONNECTOR_LINES_ARROW_END || + nSlotId == SID_CONNECTOR_LINES_ARROWS || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINES_CIRCLES || + nSlotId == SID_LINE_ARROW_START || + nSlotId == SID_LINE_ARROW_END || + nSlotId == SID_LINE_ARROWS || + nSlotId == SID_LINE_ARROW_CIRCLE || + nSlotId == SID_LINE_CIRCLE_ARROW || + nSlotId == SID_LINE_ARROW_SQUARE || + nSlotId == SID_LINE_SQUARE_ARROW ) + { + mpView->SetGlueVisible( false ); + } + FuConstruct::Deactivate(); + + if (nSlotId != SID_INSERT_SIGNATURELINE) + { + return; + } + + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() < 1) + { + // Just switching pages, no signature rectangle yet. + return; + } + + // Finished drawing a signature rectangle, now set it up. + if (!mpViewShell) + { + return; + } + + uno::Reference xCertificate + = svx::SignatureLineHelper::getSignatureCertificate(mpViewShell->GetObjectShell(), + mpViewShell->GetFrameWeld()); + if (!xCertificate.is()) + { + return; + } + + svx::SignatureLineHelper::setShapeCertificate(mpView, xCertificate); + + // Update infobar to offer "finish signing". + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + if (pFrame && pFrame->HasInfoBarWithID(u"readonly")) + { + pFrame->RemoveInfoBar(u"readonly"); + pFrame->AppendReadOnlyInfobar(); + } +} + +namespace { +/// Returns the color based on the color names listed in core/include/tools/color.hxx +/// Feel free to extend with more color names from color.hxx +Color strToColor(std::u16string_view sColor) +{ + Color aColor = COL_AUTO; + + if (sColor == u"COL_GRAY") + aColor = COL_GRAY; + else if (sColor == u"COL_GRAY3") + aColor = COL_GRAY3; + else if (sColor == u"COL_GRAY7") + aColor = COL_GRAY7; + + return aColor; +} +} + +/** + * set attribute for the object to be created + */ +void FuConstructRectangle::SetAttributes(SfxItemSet& rAttr, SdrObject* pObj) +{ + if (nSlotId == SID_DRAW_RECT_ROUND || + nSlotId == SID_DRAW_RECT_ROUND_NOFILL || + nSlotId == SID_DRAW_SQUARE_ROUND || + nSlotId == SID_DRAW_SQUARE_ROUND_NOFILL) + { + // round corner + rAttr.Put(makeSdrEckenradiusItem(500)); + } + else if (nSlotId == SID_CONNECTOR_LINE || + nSlotId == SID_CONNECTOR_LINE_ARROW_START || + nSlotId == SID_CONNECTOR_LINE_ARROW_END || + nSlotId == SID_CONNECTOR_LINE_ARROWS || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINE_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINE_CIRCLES) + { + // direct connector + rAttr.Put(SdrEdgeKindItem(SdrEdgeKind::OneLine)); + } + else if (nSlotId == SID_CONNECTOR_LINES || + nSlotId == SID_CONNECTOR_LINES_ARROW_START || + nSlotId == SID_CONNECTOR_LINES_ARROW_END || + nSlotId == SID_CONNECTOR_LINES_ARROWS || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_START || + nSlotId == SID_CONNECTOR_LINES_CIRCLE_END || + nSlotId == SID_CONNECTOR_LINES_CIRCLES) + { + // line connector + rAttr.Put(SdrEdgeKindItem(SdrEdgeKind::ThreeLines)); + } + else if (nSlotId == SID_CONNECTOR_CURVE || + nSlotId == SID_CONNECTOR_CURVE_ARROW_START || + nSlotId == SID_CONNECTOR_CURVE_ARROW_END || + nSlotId == SID_CONNECTOR_CURVE_ARROWS || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_START || + nSlotId == SID_CONNECTOR_CURVE_CIRCLE_END || + nSlotId == SID_CONNECTOR_CURVE_CIRCLES) + { + // curve connector + rAttr.Put(SdrEdgeKindItem(SdrEdgeKind::Bezier)); + } + else if ( nSlotId == SID_DRAW_CAPTION || nSlotId == SID_DRAW_CAPTION_VERTICAL ) + { + // legend object + Size aSize(pObj->GetLogicRect().GetSize()); + rAttr.Put( makeSdrTextMinFrameHeightItem( aSize.Height() ) ); + rAttr.Put( makeSdrTextMinFrameWidthItem( aSize.Width() ) ); + rAttr.Put( makeSdrTextAutoGrowHeightItem( true ) ); + rAttr.Put( makeSdrTextAutoGrowWidthItem( true ) ); + + // Support full with for vertical caption objects, too + if(SID_DRAW_CAPTION == nSlotId) + rAttr.Put( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_BLOCK ) ); + else + rAttr.Put( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_BLOCK ) ); + + rAttr.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + rAttr.Put( makeSdrTextLeftDistItem( 100 ) ); + rAttr.Put( makeSdrTextRightDistItem( 100 ) ); + rAttr.Put( makeSdrTextUpperDistItem( 100 ) ); + rAttr.Put( makeSdrTextLowerDistItem( 100 ) ); + } + else if (nSlotId == SID_DRAW_MEASURELINE) + { + // dimension line + SdPage* pPage = static_cast( mpView->GetSdrPageView()->GetPage() ); + OUString aName(SdResId(STR_POOLSHEET_MEASURE)); + SfxStyleSheet* pSheet( + static_cast< SfxStyleSheet* >( + pPage->getSdrModelFromSdrPage().GetStyleSheetPool()->Find(aName, SfxStyleFamily::Para))); + DBG_ASSERT(pSheet, "StyleSheet missing"); + + if (pSheet) + { + pObj->SetStyleSheet(pSheet, false); + } + + SdrLayerAdmin& rAdmin = mpDoc->GetLayerAdmin(); + pObj->SetLayer(rAdmin.GetLayerID(sUNO_LayerName_measurelines)); + } + else if (nSlotId == SID_DRAW_RECT) + { + if (mnFillTransparence > 0 && mnFillTransparence <= 100) + rAttr.Put(XFillTransparenceItem(mnFillTransparence)); + if (!msFillColor.isEmpty()) + rAttr.Put(XFillColorItem(OUString(), strToColor(msFillColor))); + if (!msShapeName.isEmpty()) + pObj->SetName(msShapeName); + + switch(mnLineStyle) + { + case 0: + rAttr.Put( XLineStyleItem(css::drawing::LineStyle_NONE ) ); + break; + case 1: + rAttr.Put( XLineStyleItem(css::drawing::LineStyle_SOLID ) ); + break; + case 2: + rAttr.Put( XLineStyleItem(css::drawing::LineStyle_DASH ) ); + break; + default: + // Leave it to the defaults + break; + } + } + else if (nSlotId == SID_INSERT_SIGNATURELINE) + { + // Avoid the default solid fill and line, we'll set a graphic instead. + rAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + rAttr.Put(XLineStyleItem(drawing::LineStyle_NONE)); + } +} + +/** + * set line starts and ends for the object to be created + */ +static ::basegfx::B2DPolyPolygon getPolygon(TranslateId pResId, const SdrModel& rModel) +{ + ::basegfx::B2DPolyPolygon aRetval; + XLineEndListRef pLineEndList(rModel.GetLineEndList()); + + if( pLineEndList.is() ) + { + OUString aArrowName(SvxResId(pResId)); + ::tools::Long nCount = pLineEndList->Count(); + ::tools::Long nIndex; + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + const XLineEndEntry* pEntry = pLineEndList->GetLineEnd(nIndex); + if( pEntry->GetName() == aArrowName ) + { + aRetval = pEntry->GetLineEnd(); + break; + } + } + } + + return aRetval; +} + +void FuConstructRectangle::SetLineEnds(SfxItemSet& rAttr, SdrObject const & rObj) +{ + if ( !((rObj.GetObjIdentifier() == SdrObjKind::Edge && + nSlotId != SID_TOOL_CONNECTOR && + nSlotId != SID_CONNECTOR_LINE && + nSlotId != SID_CONNECTOR_LINES && + nSlotId != SID_CONNECTOR_CURVE) || + nSlotId == SID_LINE_ARROW_START || + nSlotId == SID_LINE_ARROW_END || + nSlotId == SID_LINE_ARROWS || + nSlotId == SID_LINE_ARROW_CIRCLE || + nSlotId == SID_LINE_CIRCLE_ARROW || + nSlotId == SID_LINE_ARROW_SQUARE || + nSlotId == SID_LINE_SQUARE_ARROW) ) + return; + + // set attributes of line start and ends + SdrModel& rModel(rObj.getSdrModelFromSdrObject()); + + // arrowhead + ::basegfx::B2DPolyPolygon aArrow( getPolygon( RID_SVXSTR_ARROW, rModel ) ); + if( !aArrow.count() ) + { + ::basegfx::B2DPolygon aNewArrow; + aNewArrow.append(::basegfx::B2DPoint(10.0, 0.0)); + aNewArrow.append(::basegfx::B2DPoint(0.0, 30.0)); + aNewArrow.append(::basegfx::B2DPoint(20.0, 30.0)); + aNewArrow.setClosed(true); + aArrow.append(aNewArrow); + } + + // Circles + ::basegfx::B2DPolyPolygon aCircle( getPolygon( RID_SVXSTR_CIRCLE, rModel ) ); + if( !aCircle.count() ) + { + ::basegfx::B2DPolygon aNewCircle = ::basegfx::utils::createPolygonFromEllipse(::basegfx::B2DPoint(0.0, 0.0), 250.0, 250.0); + aNewCircle.setClosed(true); + aCircle.append(aNewCircle); + } + + // Square + ::basegfx::B2DPolyPolygon aSquare( getPolygon( RID_SVXSTR_SQUARE, rModel ) ); + if( !aSquare.count() ) + { + ::basegfx::B2DPolygon aNewSquare; + aNewSquare.append(::basegfx::B2DPoint(0.0, 0.0)); + aNewSquare.append(::basegfx::B2DPoint(10.0, 0.0)); + aNewSquare.append(::basegfx::B2DPoint(10.0, 10.0)); + aNewSquare.append(::basegfx::B2DPoint(0.0, 10.0)); + aNewSquare.setClosed(true); + aSquare.append(aNewSquare); + } + + SfxItemSet aSet( mpDoc->GetPool() ); + mpView->GetAttributes( aSet ); + + // #i3908# Here, the default Line Start/End width for arrow construction is + // set. To have the same value in all situations (construction) in i3908 + // it was decided to change the default to 0.03 cm for all situations. + ::tools::Long nWidth = 300; // (1/100th mm) + + // determine line width and calculate with it the line end width + if( aSet.GetItemState( XATTR_LINEWIDTH ) != SfxItemState::DONTCARE ) + { + ::tools::Long nValue = aSet.Get( XATTR_LINEWIDTH ).GetValue(); + if( nValue > 0 ) + nWidth = nValue * 3; + } + + switch (nSlotId) + { + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_LINE_ARROWS: + { + // connector with arrow ends + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineStartWidthItem(nWidth)); + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_ARROW_SQUARE: + { + // connector with arrow start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_LINE_ARROW_END: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_SQUARE_ARROW: + { + // connector with arrow end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_ARROW), aArrow)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_LINES_CIRCLES: + case SID_CONNECTOR_CURVE_CIRCLES: + { + // connector with circle ends + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineStartWidthItem(nWidth)); + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_START: + { + // connector with circle start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLE_END: + { + // connector with circle ends + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + } + + // and again, for the still missing ends + switch (nSlotId) + { + case SID_LINE_ARROW_CIRCLE: + { + // circle end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_LINE_CIRCLE_ARROW: + { + // circle start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_CIRCLE), aCircle)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + + case SID_LINE_ARROW_SQUARE: + { + // square end + rAttr.Put(XLineEndItem(SvxResId(RID_SVXSTR_SQUARE), aSquare)); + rAttr.Put(XLineEndWidthItem(nWidth)); + } + break; + + case SID_LINE_SQUARE_ARROW: + { + // square start + rAttr.Put(XLineStartItem(SvxResId(RID_SVXSTR_SQUARE), aSquare)); + rAttr.Put(XLineStartWidthItem(nWidth)); + } + break; + } +} + +SdrObjectUniquePtr FuConstructRectangle::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + DBG_ASSERT( (nID != SID_DRAW_FONTWORK) && (nID != SID_DRAW_FONTWORK_VERTICAL ), "FuConstRectangle::CreateDefaultObject can not create Fontwork shapes!" ); + + // case SID_DRAW_LINE: + // case SID_DRAW_XLINE: + // case SID_DRAW_MEASURELINE: + // case SID_LINE_ARROW_START: + // case SID_LINE_ARROW_END: + // case SID_LINE_ARROWS: + // case SID_LINE_ARROW_CIRCLE: + // case SID_LINE_CIRCLE_ARROW: + // case SID_LINE_ARROW_SQUARE: + // case SID_LINE_SQUARE_ARROW: + // case SID_DRAW_RECT: + // case SID_DRAW_RECT_NOFILL: + // case SID_DRAW_RECT_ROUND: + // case SID_DRAW_RECT_ROUND_NOFILL: + // case SID_DRAW_SQUARE: + // case SID_DRAW_SQUARE_NOFILL: + // case SID_DRAW_SQUARE_ROUND: + // case SID_DRAW_SQUARE_ROUND_NOFILL: + // case SID_DRAW_ELLIPSE: + // case SID_DRAW_ELLIPSE_NOFILL: + // case SID_DRAW_CIRCLE: + // case SID_DRAW_CIRCLE_NOFILL: + // case SID_DRAW_CAPTION: + // case SID_DRAW_CAPTION_VERTICAL: + // case SID_TOOL_CONNECTOR: + // case SID_CONNECTOR_ARROW_START: + // case SID_CONNECTOR_ARROW_END: + // case SID_CONNECTOR_ARROWS: + // case SID_CONNECTOR_CIRCLE_START: + // case SID_CONNECTOR_CIRCLE_END: + // case SID_CONNECTOR_CIRCLES: + // case SID_CONNECTOR_LINE: + // case SID_CONNECTOR_LINE_ARROW_START: + // case SID_CONNECTOR_LINE_ARROW_END: + // case SID_CONNECTOR_LINE_ARROWS: + // case SID_CONNECTOR_LINE_CIRCLE_START: + // case SID_CONNECTOR_LINE_CIRCLE_END: + // case SID_CONNECTOR_LINE_CIRCLES: + // case SID_CONNECTOR_CURVE: + // case SID_CONNECTOR_CURVE_ARROW_START: + // case SID_CONNECTOR_CURVE_ARROW_END: + // case SID_CONNECTOR_CURVE_ARROWS: + // case SID_CONNECTOR_CURVE_CIRCLE_START: + // case SID_CONNECTOR_CURVE_CIRCLE_END: + // case SID_CONNECTOR_CURVE_CIRCLES: + // case SID_CONNECTOR_LINES: + // case SID_CONNECTOR_LINES_ARROW_START: + // case SID_CONNECTOR_LINES_ARROW_END: + // case SID_CONNECTOR_LINES_ARROWS: + // case SID_CONNECTOR_LINES_CIRCLE_START: + // case SID_CONNECTOR_LINES_CIRCLE_END: + // case SID_CONNECTOR_LINES_CIRCLES: + + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if(pObj) + { + ::tools::Rectangle aRect(rRectangle); + + if(SID_DRAW_SQUARE == nID || + SID_DRAW_SQUARE_NOFILL == nID || + SID_DRAW_SQUARE_ROUND == nID || + SID_DRAW_SQUARE_ROUND_NOFILL == nID || + SID_DRAW_CIRCLE == nID || + SID_DRAW_CIRCLE_NOFILL == nID) + { + // force quadratic + ImpForceQuadratic(aRect); + } + + Point aStart = aRect.TopLeft(); + Point aEnd = aRect.BottomRight(); + + switch(nID) + { + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_END: + case SID_LINE_ARROWS: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_SQUARE_ARROW: + { + if( auto pPathObj = dynamic_cast( pObj.get() ) ) + { + sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2); + + ::basegfx::B2DPolygon aB2DPolygon; + aB2DPolygon.append(::basegfx::B2DPoint(aStart.X(), nYMiddle)); + aB2DPolygon.append(::basegfx::B2DPoint(aEnd.X(), nYMiddle)); + pPathObj->SetPathPoly(::basegfx::B2DPolyPolygon(aB2DPolygon)); + } + else + { + OSL_FAIL("Object is NO line object"); + } + + break; + } + + case SID_DRAW_MEASURELINE: + { + if( auto pMeasureObj = dynamic_cast< SdrMeasureObj *>( pObj.get() ) ) + { + sal_Int32 nYMiddle((aRect.Top() + aRect.Bottom()) / 2); + pMeasureObj->SetPoint(Point(aStart.X(), nYMiddle), 0); + pMeasureObj->SetPoint(Point(aEnd.X(), nYMiddle), 1); + } + else + { + OSL_FAIL("Object is NO measure object"); + } + + break; + } + + case SID_TOOL_CONNECTOR: + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_CURVE: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_CONNECTOR_CURVE_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLES: + case SID_CONNECTOR_LINES: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLES: + { + if( auto pEdgeObj = dynamic_cast< SdrEdgeObj *>( pObj.get() ) ) + { + pEdgeObj->SetTailPoint(false, aStart); + pEdgeObj->SetTailPoint(true, aEnd); + } + else + { + OSL_FAIL("Object is NO connector object"); + } + + break; + } + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + { + if( auto pCaptionObj = dynamic_cast< SdrCaptionObj *>( pObj.get() ) ) + { + bool bIsVertical(SID_DRAW_CAPTION_VERTICAL == nID); + + pCaptionObj->SetVerticalWriting(bIsVertical); + + if(bIsVertical) + { + SfxItemSet aSet(pObj->GetMergedItemSet()); + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_CENTER)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + pObj->SetMergedItemSet(aSet); + } + + // The default text is not inserted anymore. + + pCaptionObj->SetLogicRect(aRect); + pCaptionObj->SetTailPos( + aRect.TopLeft() - Point(aRect.GetWidth() / 2, aRect.GetHeight() / 2)); + } + else + { + OSL_FAIL("Object is NO caption object"); + } + + break; + } + + default: + { + pObj->SetLogicRect(aRect); + + break; + } + } + + SfxItemSet aAttr(mpDoc->GetPool()); + SetStyleSheet(aAttr, pObj.get()); + SetAttributes(aAttr, pObj.get()); + SetLineEnds(aAttr, *pObj); + pObj->SetMergedItemSet(aAttr); + } + + return pObj; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconstr.cxx b/sd/source/ui/func/fuconstr.cxx new file mode 100644 index 000000000..9d6f36320 --- /dev/null +++ b/sd/source/ui/func/fuconstr.cxx @@ -0,0 +1,414 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + + +FuConstruct::FuConstruct ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuDraw(pViewSh, pWin, pView, pDoc, rReq), + bSelectionChanged(false) +{ +} + +bool FuConstruct::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + + bMBDown = true; + bSelectionChanged = false; + + if ( mpView->IsAction() ) + { + return true; + } + + bFirstMouseMove = true; + aDragTimer.Start(); + + aMDPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + sal_uInt16 nHitLog = sal_uInt16 (mpWindow->PixelToLogic(Size(HITPIX,0)).Width()); + + if (rMEvt.IsLeft() && mpView->IsExtendedMouseEventDispatcherEnabled()) + { + mpWindow->CaptureMouse(); + + SdrHdl* pHdl = mpView->PickHandle(aMDPos); + + if ( pHdl != nullptr || mpView->IsMarkedHit(aMDPos, nHitLog) ) + { + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + bReturn = true; + } + else if ( mpView->AreObjectsMarked() ) + { + mpView->UnmarkAll(); + bReturn = true; + } + } + + return bReturn; +} + +bool FuConstruct::MouseMove(const MouseEvent& rMEvt) +{ + FuDraw::MouseMove(rMEvt); + + if (aDragTimer.IsActive() ) + { + if( bFirstMouseMove ) + bFirstMouseMove = false; + else + aDragTimer.Stop(); + } + + Point aPix(rMEvt.GetPosPixel()); + Point aPnt( mpWindow->PixelToLogic(aPix) ); + + if ( mpView->IsAction() ) + { + ForceScroll(aPix); + mpView->MovAction(aPnt); + } + + return true; +} + +bool FuConstruct::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = true; + + if (aDragTimer.IsActive() ) + { + aDragTimer.Stop(); + bIsInDragMode = false; + } + + FuDraw::MouseButtonUp(rMEvt); + + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + if ( mpView && mpView->IsDragObj() ) + { + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bDragWithCopy = (rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (bDragWithCopy) + { + bDragWithCopy = !mpView->IsPresObjSelected(false); + } + + mpView->SetDragWithCopy(bDragWithCopy); + mpView->EndDragObj( mpView->IsDragWithCopy() ); + } + else if ( mpView && mpView->IsMarkObj() ) + { + mpView->EndMarkObj(); + } + else + { + bReturn = false; + } + + if ( mpView && !mpView->IsAction() ) + { + mpWindow->ReleaseMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if ( !mpView->AreObjectsMarked() ) + { + SdrPageView* pPV; + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + + SdrObject* pObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV); + if (!pObj) + { + mpView->MarkObj(aPnt, nHitLog); + } + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + else if (rMEvt.IsLeft() && !rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() && + !bSelectionChanged && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + // toggle between selection and rotation + SdrObject* pSingleObj = nullptr; + + if (mpView->GetMarkedObjectList().GetMarkCount()==1) + { + pSingleObj = mpView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + } + + const bool bTiledRendering = comphelper::LibreOfficeKit::isActive(); + if (!bTiledRendering && (mpView->GetDragMode() == SdrDragMode::Move && mpView->IsRotateAllowed() && + (mpViewShell->GetFrameView()->IsClickChangeRotation() || + (pSingleObj && pSingleObj->GetObjInventor()==SdrInventor::E3d)))) + { + mpView->SetDragMode(SdrDragMode::Rotate); + } + else + { + mpView->SetDragMode(SdrDragMode::Move); + } + } + } + + sal_uInt16 nClicks = rMEvt.GetClicks(); + + if (nClicks == 2 && rMEvt.IsLeft() && bMBDown && + !rMEvt.IsMod1() && !rMEvt.IsMod2() && !rMEvt.IsShift() ) + { + DoubleClick(rMEvt); + } + bMBDown = false; + + return bReturn; +} + +void FuConstruct::Activate() +{ + mpView->SetEditMode(SdrViewEditMode::Create); + FuDraw::Activate(); +} + +void FuConstruct::Deactivate() +{ + FuDraw::Deactivate(); + mpView->SetEditMode(SdrViewEditMode::Edit); +} + +/** + * set style sheet for the object to be created + */ +void FuConstruct::SetStyleSheet(SfxItemSet& rAttr, SdrObject* pObj) +{ + bool bUseFillStyle, bUseNoFillStyle; + bUseFillStyle = bUseNoFillStyle = false; + + switch( nSlotId ) + { + case SID_DRAW_RECT: + case SID_DRAW_RECT_ROUND: + case SID_DRAW_SQUARE: + case SID_DRAW_SQUARE_ROUND: + case SID_DRAW_ELLIPSE: + case SID_DRAW_PIE: + case SID_DRAW_ELLIPSECUT: + case SID_DRAW_CIRCLE: + case SID_DRAW_CIRCLEPIE: + case SID_DRAW_CIRCLECUT: + case SID_DRAW_POLYGON: + case SID_DRAW_XPOLYGON: + case SID_DRAW_FREELINE: + case SID_DRAW_BEZIER_FILL: + { + bUseFillStyle = true; + break; + } + case SID_DRAW_RECT_NOFILL: + case SID_DRAW_RECT_ROUND_NOFILL: + case SID_DRAW_SQUARE_NOFILL: + case SID_DRAW_SQUARE_ROUND_NOFILL: + case SID_DRAW_ELLIPSE_NOFILL: + case SID_DRAW_PIE_NOFILL: + case SID_DRAW_ELLIPSECUT_NOFILL: + case SID_DRAW_CIRCLE_NOFILL: + case SID_DRAW_CIRCLEPIE_NOFILL: + case SID_DRAW_CIRCLECUT_NOFILL: + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_XPOLYGON_NOFILL: + case SID_DRAW_FREELINE_NOFILL: + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_CURVE: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_CONNECTOR_CURVE_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLES: + case SID_CONNECTOR_LINES: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLES: + case SID_DRAW_BEZIER_NOFILL: + case SID_LINE_ARROW_END: + { + bUseNoFillStyle = true; + break; + } + } + SetStyleSheet( rAttr, pObj, bUseFillStyle, bUseNoFillStyle ); +} + +void FuConstruct::SetStyleSheet( SfxItemSet& rAttr, SdrObject* pObj, + const bool bForceFillStyle, const bool bForceNoFillStyle ) +{ + SdPage* pPage = static_cast(mpView->GetSdrPageView()->GetPage()); + if ( pPage->IsMasterPage() && pPage->GetPageKind() == PageKind::Standard && + mpDoc->GetDocumentType() == DocumentType::Impress ) + { + /********************************************** + * Objects was created on the slide master page + ***********************************************/ + OUString aName( pPage->GetLayoutName() ); + sal_Int32 n = aName.indexOf(SD_LT_SEPARATOR) + SD_LT_SEPARATOR.getLength(); + aName = OUString::Concat(aName.subView(0, n)) + STR_LAYOUT_BACKGROUNDOBJECTS; + SfxStyleSheet* pSheet( + static_cast< SfxStyleSheet* >( + pPage->getSdrModelFromSdrPage().GetStyleSheetPool()->Find(aName, SfxStyleFamily::Page))); + DBG_ASSERT(pSheet, "StyleSheet missing"); + if (pSheet) + { + // applying style sheet for background objects + pObj->SetStyleSheet(pSheet, false); + SfxItemSet& rSet = pSheet->GetItemSet(); + const XFillStyleItem& rFillStyle = rSet.Get(XATTR_FILLSTYLE); + if ( bForceFillStyle ) + { + if (rFillStyle.GetValue() == drawing::FillStyle_NONE) + rAttr.Put(XFillStyleItem(drawing::FillStyle_SOLID)); + } + else if ( bForceNoFillStyle ) + { + if (rFillStyle.GetValue() != drawing::FillStyle_NONE) + rAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + } + } + else + { + /*********************************** + * object was created on normal page + ************************************/ + if ( bForceNoFillStyle ) + { + OUString aName(SdResId(STR_POOLSHEET_OBJWITHOUTFILL)); + SfxStyleSheet* pSheet( + static_cast< SfxStyleSheet* >( + pPage->getSdrModelFromSdrPage().GetStyleSheetPool()->Find(aName, SfxStyleFamily::Para))); + DBG_ASSERT(pSheet, "Stylesheet missing"); + if (pSheet) + { + pObj->SetStyleSheet(pSheet, false); + SfxItemSet aAttr(mpView->GetDefaultAttr()); + aAttr.Put(pSheet->GetItemSet().Get(XATTR_FILLSTYLE)); + pObj->SetMergedItemSet(aAttr); + } + else + { + SfxItemSet aAttr(mpView->GetDefaultAttr()); + rAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + pObj->SetMergedItemSet(aAttr); + } + } + else + { + // Creating an object with fill. + SdrPage* pThemePage = pPage; + if (pThemePage->TRG_HasMasterPage()) + { + pThemePage = &pThemePage->TRG_GetMasterPage(); + } + + svx::Theme* pTheme = pThemePage->getSdrPageProperties().GetTheme(); + if (pTheme) + { + // We construct an object on a page where the master page has a theme. Take the + // accent1 color from that theme, make sure it has priority over the shape's + // document-global style. + SfxItemSet aAttr(mpView->GetDefaultAttr()); + + aAttr.Put(XFillStyleItem(css::drawing::FillStyle_SOLID)); + + svx::ThemeColorType eColorType = svx::ThemeColorType::ACCENT1; + Color aColor = pTheme->GetColor(eColorType); + XFillColorItem aFillColorItem("", aColor); + aFillColorItem.GetThemeColor().SetThemeIndex(static_cast(eColorType)); + aAttr.Put(aFillColorItem); + + aAttr.Put(XLineStyleItem(css::drawing::LineStyle_SOLID)); + + // Line color is 50% darker than the fill color. + aColor.ApplyTintOrShade(-5000); + XLineColorItem aLineColorItem("", aColor); + // TODO no theme or theme effect for line colors yet. + aAttr.Put(aLineColorItem); + + pObj->SetMergedItemSet(aAttr); + } + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuconuno.cxx b/sd/source/ui/func/fuconuno.cxx new file mode 100644 index 000000000..afa4523c6 --- /dev/null +++ b/sd/source/ui/func/fuconuno.cxx @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +namespace sd { + + +FuConstructUnoControl::FuConstructUnoControl ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuConstruct(pViewSh, pWin, pView, pDoc, rReq) + , nInventor(SdrInventor::Unknown) + , nIdentifier(SdrObjKind::NONE) +{ +} + +rtl::Reference FuConstructUnoControl::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuConstructUnoControl* pFunc; + rtl::Reference xFunc( pFunc = new FuConstructUnoControl( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent(bPermanent); + return xFunc; +} + +void FuConstructUnoControl::DoExecute( SfxRequest& rReq ) +{ + FuConstruct::DoExecute( rReq ); + + const SfxUInt32Item* pInventorItem = rReq.GetArg(SID_FM_CONTROL_INVENTOR); + const SfxUInt16Item* pIdentifierItem = rReq.GetArg(SID_FM_CONTROL_IDENTIFIER); + if( pInventorItem ) + nInventor = static_cast(pInventorItem->GetValue()); + if( pIdentifierItem ) + nIdentifier = static_cast(pIdentifierItem->GetValue()); + + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); +} + +bool FuConstructUnoControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + bool bReturn = FuConstruct::MouseButtonDown(rMEvt); + + if ( rMEvt.IsLeft() && !mpView->IsAction() ) + { + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + mpWindow->CaptureMouse(); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegCreateObj(aPnt, nullptr, nDrgLog); + bReturn = true; + } + return bReturn; +} + +bool FuConstructUnoControl::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + + if ( mpView->IsCreateObj() && rMEvt.IsLeft() ) + { + mpView->EndCreateObj(SdrCreateCmd::ForceEnd); + bReturn = true; + } + + bReturn = (FuConstruct::MouseButtonUp(rMEvt) || bReturn); + + if (!bPermanent) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + return bReturn; +} + +void FuConstructUnoControl::Activate() +{ + mpView->SetCurrentObj( nIdentifier, nInventor ); + + aNewPointer = PointerStyle::DrawRect; + aOldPointer = mpWindow->GetPointer(); + mpWindow->SetPointer( aNewPointer ); + + aOldLayer = mpView->GetActiveLayer(); + mpView->SetActiveLayer(sUNO_LayerName_controls); + + FuConstruct::Activate(); +} + +void FuConstructUnoControl::Deactivate() +{ + FuConstruct::Deactivate(); + mpView->SetActiveLayer( aOldLayer ); + mpWindow->SetPointer( aOldPointer ); +} + +SdrObjectUniquePtr FuConstructUnoControl::CreateDefaultObject(const sal_uInt16, const ::tools::Rectangle& rRectangle) +{ + // case SID_FM_CREATE_CONTROL: + + SdrObjectUniquePtr pObj(SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier())); + + if(pObj) + { + pObj->SetLogicRect(rRectangle); + } + + return pObj; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fucopy.cxx b/sd/source/ui/func/fucopy.cxx new file mode 100644 index 000000000..99e5f7b87 --- /dev/null +++ b/sd/source/ui/func/fucopy.cxx @@ -0,0 +1,288 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + + +FuCopy::FuCopy ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuCopy::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuCopy( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuCopy::DoExecute( SfxRequest& rReq ) +{ + if( !mpView->AreObjectsMarked() ) + return; + + // Undo + OUString aString = mpView->GetDescriptionOfMarkedObjects() + + " " + SdResId( STR_UNDO_COPYOBJECTS ); + mpView->BegUndo( aString ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SfxItemSetFixed aSet( mpViewShell->GetPool() ); + + // indicate color attribute + SfxItemSet aAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aAttr ); + + if( const XFillStyleItem* pFillStyleItem = aAttr.GetItemIfSet( XATTR_FILLSTYLE ) ) + { + drawing::FillStyle eStyle = pFillStyleItem->GetValue(); + + const XFillColorItem* pFillColorItem; + if( eStyle == drawing::FillStyle_SOLID && + (pFillColorItem = aAttr.GetItemIfSet( XATTR_FILLCOLOR )) ) + { + XColorItem aXColorItem( ATTR_COPY_START_COLOR, pFillColorItem->GetName(), + pFillColorItem->GetColorValue() ); + aSet.Put( aXColorItem ); + + } + } + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateCopyDlg(mpViewShell->GetFrameWeld(), aSet, mpView )); + + sal_uInt16 nResult = pDlg->Execute(); + + switch( nResult ) + { + case RET_OK: + pDlg->GetAttr( aSet ); + rReq.Done( aSet ); + pArgs = rReq.GetArgs(); + break; + + default: + { + pDlg.disposeAndClear(); + mpView->EndUndo(); + return; // Cancel + } + } + } + + ::tools::Rectangle aRect; + sal_Int32 lWidth = 0, lHeight = 0, lSizeX = 0, lSizeY = 0; + Degree100 lAngle(0); + sal_uInt16 nNumber = 0; + Color aStartColor, aEndColor; + bool bColor = false; + + if (pArgs) + { + // Count + if( const SfxUInt16Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_NUMBER ) ) + nNumber = pPoolItem->GetValue(); + + // translation + if( const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_MOVE_X ) ) + lSizeX = pPoolItem->GetValue(); + if( const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_MOVE_Y ) ) + lSizeY = pPoolItem->GetValue(); + if( const SdrAngleItem* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_ANGLE ) ) + lAngle = pPoolItem->GetValue(); + + // scale + if( const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_WIDTH ) ) + lWidth = pPoolItem->GetValue(); + if( const SfxInt32Item* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_HEIGHT ) ) + lHeight = pPoolItem->GetValue(); + + // start/end color + if( const XColorItem* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_START_COLOR ) ) + { + aStartColor = pPoolItem->GetColorValue(); + bColor = true; + } + if( const XColorItem* pPoolItem = pArgs->GetItemIfSet( ATTR_COPY_END_COLOR ) ) + { + aEndColor = pPoolItem->GetColorValue(); + if( aStartColor == aEndColor ) + bColor = false; + } + } + + // remove handles + //HMHmpView->HideMarkHdl(); + + std::unique_ptr pProgress; + bool bWaiting = false; + + if( nNumber > 1 ) + { + OUString aStr = SdResId( STR_OBJECTS ) + + " " + SdResId( STR_UNDO_COPYOBJECTS ); + + pProgress.reset(new SfxProgress( mpDocSh, aStr, nNumber )); + mpDocSh->SetWaitCursor( true ); + bWaiting = true; + } + + const SdrMarkList aMarkList( mpView->GetMarkedObjectList() ); + const size_t nMarkCount = aMarkList.GetMarkCount(); + SdrObject* pObj = nullptr; + + // calculate number of possible copies + aRect = mpView->GetAllMarkedRect(); + + if( lWidth < 0 ) + { + ::tools::Long nTmp = ( aRect.Right() - aRect.Left() ) / -lWidth; + nNumber = static_cast(std::min( nTmp, static_cast<::tools::Long>(nNumber) )); + } + + if( lHeight < 0 ) + { + ::tools::Long nTmp = ( aRect.Bottom() - aRect.Top() ) / -lHeight; + nNumber = static_cast(std::min( nTmp, static_cast<::tools::Long>(nNumber) )); + } + + for( sal_uInt16 i = 1; i <= nNumber; i++ ) + { + if( pProgress ) + pProgress->SetState( i ); + + aRect = mpView->GetAllMarkedRect(); + + if( ( 1 == i ) && bColor ) + { + SfxItemSetFixed aNewSet( mpViewShell->GetPool() ); + aNewSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + aNewSet.Put( XFillColorItem( OUString(), aStartColor ) ); + mpView->SetAttributes( aNewSet ); + } + + // make a copy of selected objects + mpView->CopyMarked(); + + // get newly selected objects + SdrMarkList aCopyMarkList( mpView->GetMarkedObjectList() ); + const size_t nCopyMarkCount = aMarkList.GetMarkCount(); + + // set protection flags at marked copies to null + for( size_t j = 0; j < nCopyMarkCount; ++j ) + { + pObj = aCopyMarkList.GetMark( j )->GetMarkedSdrObj(); + + if( pObj ) + { + pObj->SetMoveProtect( false ); + pObj->SetResizeProtect( false ); + } + } + + Fraction aWidth( aRect.Right() - aRect.Left() + lWidth, aRect.Right() - aRect.Left() ); + Fraction aHeight( aRect.Bottom() - aRect.Top() + lHeight, aRect.Bottom() - aRect.Top() ); + + if( mpView->IsResizeAllowed() ) + mpView->ResizeAllMarked( aRect.TopLeft(), aWidth, aHeight ); + + if( mpView->IsRotateAllowed() ) + mpView->RotateAllMarked( aRect.Center(), lAngle ); + + if( mpView->IsMoveAllowed() ) + mpView->MoveAllMarked( Size( lSizeX, lSizeY ) ); + + // set protection flags at marked copies to original values + if( nMarkCount == nCopyMarkCount ) + { + for( size_t j = 0; j < nMarkCount; ++j ) + { + SdrObject* pSrcObj = aMarkList.GetMark( j )->GetMarkedSdrObj(); + SdrObject* pDstObj = aCopyMarkList.GetMark( j )->GetMarkedSdrObj(); + + if( pSrcObj && pDstObj && + ( pSrcObj->GetObjInventor() == pDstObj->GetObjInventor() ) && + ( pSrcObj->GetObjIdentifier() == pDstObj->GetObjIdentifier() ) ) + { + pDstObj->SetMoveProtect( pSrcObj->IsMoveProtect() ); + pDstObj->SetResizeProtect( pSrcObj->IsResizeProtect() ); + } + } + } + + if( bColor ) + { + // probably room for optimizations, but may can lead to rounding errors + sal_uInt8 nRed = aStartColor.GetRed() + static_cast( ( static_cast<::tools::Long>(aEndColor.GetRed()) - static_cast<::tools::Long>(aStartColor.GetRed()) ) * static_cast<::tools::Long>(i) / static_cast<::tools::Long>(nNumber) ); + sal_uInt8 nGreen = aStartColor.GetGreen() + static_cast( ( static_cast<::tools::Long>(aEndColor.GetGreen()) - static_cast<::tools::Long>(aStartColor.GetGreen()) ) * static_cast<::tools::Long>(i) / static_cast<::tools::Long>(nNumber) ); + sal_uInt8 nBlue = aStartColor.GetBlue() + static_cast( ( static_cast<::tools::Long>(aEndColor.GetBlue()) - static_cast<::tools::Long>(aStartColor.GetBlue()) ) * static_cast<::tools::Long>(i) / static_cast<::tools::Long>(nNumber) ); + Color aNewColor( nRed, nGreen, nBlue ); + SfxItemSetFixed aNewSet( mpViewShell->GetPool() ); + aNewSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + aNewSet.Put( XFillColorItem( OUString(), aNewColor ) ); + mpView->SetAttributes( aNewSet ); + } + } + + pProgress.reset(); + + if ( bWaiting ) + mpDocSh->SetWaitCursor( false ); + + // show handles + mpView->AdjustMarkHdl(); //HMH sal_True ); + //HMHpView->ShowMarkHdl(); + + mpView->EndUndo(); +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fucushow.cxx b/sd/source/ui/func/fucushow.cxx new file mode 100644 index 000000000..eb3b12211 --- /dev/null +++ b/sd/source/ui/func/fucushow.cxx @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +namespace sd { + + +FuCustomShowDlg::FuCustomShowDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor( pViewSh, pWin, pView, pDoc, rReq ) +{ +} + +rtl::Reference FuCustomShowDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuCustomShowDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuCustomShowDlg::DoExecute( SfxRequest& ) +{ + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = mpViewShell->GetActiveWindow(); + ScopedVclPtr pDlg( pFact->CreateSdCustomShowDlg(pWin ? pWin->GetFrameWeld() : nullptr, *mpDoc) ); + sal_uInt16 nRet = pDlg->Execute(); + mpDoc->SetChanged(); + sd::PresentationSettings& rSettings = mpDoc->getPresentationSettings(); + + if( nRet == RET_YES ) + { + // If the custom show is not set by default + if (!rSettings.mbCustomShow) + { + rSettings.mbStartCustomShow = true; + rSettings.mbCustomShow = pDlg->IsCustomShow(); + } + + mpViewShell->SetStartShowWithDialog(true); + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_PRESENTATION, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + if (nRet == RET_OK) + { + if (mpDoc->GetCustomShowList()) + { + if (!pDlg->IsCustomShow()) + { + rSettings.mbCustomShow = false; + rSettings.mbAll = true; + } + } + } + pDlg.disposeAndClear(); +} + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fudraw.cxx b/sd/source/ui/func/fudraw.cxx new file mode 100644 index 000000000..8beb753f6 --- /dev/null +++ b/sd/source/ui/func/fudraw.cxx @@ -0,0 +1,820 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace sd { + + +/** + * Base-class for all drawmodul-specific functions + */ +FuDraw::FuDraw(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) + , aNewPointer(PointerStyle::Arrow) + , aOldPointer(PointerStyle::Arrow) + , bMBDown(false) + , bDragHelpLine(false) + , nHelpLine(0) + , bPermanent(false) +{ +} + +FuDraw::~FuDraw() +{ + mpView->BrkAction(); +} + + +/** + * Code shared by MouseButtonDown and MouseMove + */ +void FuDraw::DoModifiers(const MouseEvent& rMEvt, bool bSnapModPressed) +{ + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bGridSnap = pFrameView->IsGridSnap(); + bGridSnap = (bSnapModPressed != bGridSnap); + + if (mpView->IsGridSnap() != bGridSnap) + mpView->SetGridSnap(bGridSnap); + + bool bBordSnap = pFrameView->IsBordSnap(); + bBordSnap = (bSnapModPressed != bBordSnap); + + if (mpView->IsBordSnap() != bBordSnap) + mpView->SetBordSnap(bBordSnap); + + bool bHlplSnap = pFrameView->IsHlplSnap(); + bHlplSnap = (bSnapModPressed != bHlplSnap); + + if (mpView->IsHlplSnap() != bHlplSnap) + mpView->SetHlplSnap(bHlplSnap); + + bool bOFrmSnap = pFrameView->IsOFrmSnap(); + bOFrmSnap = (bSnapModPressed != bOFrmSnap); + + if (mpView->IsOFrmSnap() != bOFrmSnap) + mpView->SetOFrmSnap(bOFrmSnap); + + bool bOPntSnap = pFrameView->IsOPntSnap(); + bOPntSnap = (bSnapModPressed != bOPntSnap); + + if (mpView->IsOPntSnap() != bOPntSnap) + mpView->SetOPntSnap(bOPntSnap); + + bool bOConSnap = pFrameView->IsOConSnap(); + bOConSnap = (bSnapModPressed != bOConSnap); + + if (mpView->IsOConSnap() != bOConSnap) + mpView->SetOConSnap(bOConSnap); + + bool bAngleSnap = rMEvt.IsShift() == !pFrameView->IsAngleSnapEnabled(); + + if (mpView->IsAngleSnapEnabled() != bAngleSnap) + mpView->SetAngleSnapEnabled(bAngleSnap); + + bool bCenter = rMEvt.IsMod2(); + + if ( mpView->IsCreate1stPointAsCenter() != bCenter || + mpView->IsResizeAtCenter() != bCenter ) + { + mpView->SetCreate1stPointAsCenter(bCenter); + mpView->SetResizeAtCenter(bCenter); + } +} + + +bool FuDraw::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + bool bReturn = false; + bDragHelpLine = false; + aMDPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + if ( rMEvt.IsLeft() ) + { + FrameView* pFrameView = mpViewShell->GetFrameView(); + + bool bOrtho = false; + + bool bRestricted = true; + + if (mpView->IsDragObj()) + { + // object is dragged (move, resize,...) + const SdrHdl* pHdl = mpView->GetDragStat().GetHdl(); + + if (!pHdl || (!pHdl->IsCornerHdl() && !pHdl->IsVertexHdl())) + { + // Move + bRestricted = false; + } + } + + // #i33136# + if(bRestricted && doConstructOrthogonal()) + { + // Restrict movement: + // rectangle->square, ellipse->circle, etc. + bOrtho = !rMEvt.IsShift(); + } + else + { + bOrtho = rMEvt.IsShift() != pFrameView->IsOrtho(); + } + if (!mpView->IsSnapEnabled()) + mpView->SetSnapEnabled(true); + + bool bSnapModPressed = rMEvt.IsMod1(); + if (mpView->IsOrtho() != bOrtho) + mpView->SetOrtho(bOrtho); + + DoModifiers(rMEvt, bSnapModPressed); + + SdrPageView* pPV = nullptr; + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + + // look only for HelpLines when they are visible (!) + bool bHelpLine(false); + if(mpView->IsHlplVisible()) + bHelpLine = mpView->PickHelpLine(aMDPos, nHitLog, *mpWindow->GetOutDev(), nHelpLine, pPV); + bool bHitHdl = (mpView->PickHandle(aMDPos) != nullptr); + + if ( bHelpLine + && !mpView->IsCreateObj() + && ((mpView->GetEditMode() == SdrViewEditMode::Edit && !bHitHdl) || (rMEvt.IsShift() && bSnapModPressed)) ) + { + mpWindow->CaptureMouse(); + mpView->BegDragHelpLine(nHelpLine, pPV); + bDragHelpLine = mpView->IsDragHelpLine(); + bReturn = true; + } + } + ForcePointer(&rMEvt); + + return bReturn; +} + +bool FuDraw::MouseMove(const MouseEvent& rMEvt) +{ + FrameView* pFrameView = mpViewShell->GetFrameView(); + Point aPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + bool bOrtho = false; + bool bRestricted = true; + + if (mpView->IsDragObj()) + { + // object is dragged (move, resize, ...) + const SdrHdl* pHdl = mpView->GetDragStat().GetHdl(); + + if (!pHdl || (!pHdl->IsCornerHdl() && !pHdl->IsVertexHdl())) + { + // Move + bRestricted = false; + } + } + + if (mpView->IsAction()) + { + // #i33136# and fdo#88339 + if(bRestricted && doConstructOrthogonal()) + { + // Scale proportionally by default: + // rectangle->square, ellipse->circle, images, etc. + bOrtho = !rMEvt.IsShift(); + } + else + { + bOrtho = rMEvt.IsShift() != pFrameView->IsOrtho(); + } + + bool bSnapModPressed = rMEvt.IsMod2(); + mpView->SetDragWithCopy(rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (mpView->IsOrtho() != bOrtho) + mpView->SetOrtho(bOrtho); + DoModifiers(rMEvt, bSnapModPressed); + + + if ( mpView->IsDragHelpLine() ) + mpView->MovDragHelpLine(aPos); + } + + bool bReturn = mpView->MouseMove(rMEvt, mpWindow->GetOutDev()); + + if (mpView->IsAction()) + { + // Because the flag set back if necessary in MouseMove + if (mpView->IsOrtho() != bOrtho) + mpView->SetOrtho(bOrtho); + } + + ForcePointer(&rMEvt); + + return bReturn; +} + +bool FuDraw::MouseButtonUp(const MouseEvent& rMEvt) +{ + if (mpView && mpView->IsDragHelpLine()) + mpView->EndDragHelpLine(); + + if ( bDragHelpLine ) + { + ::tools::Rectangle aOutputArea(Point(0,0), mpWindow->GetOutputSizePixel()); + + if (mpView && !aOutputArea.Contains(rMEvt.GetPosPixel())) + mpView->GetSdrPageView()->DeleteHelpLine(nHelpLine); + + mpWindow->ReleaseMouse(); + } + + if (mpView) + { + FrameView* pFrameView = mpViewShell->GetFrameView(); + mpView->SetOrtho( pFrameView->IsOrtho() ); + mpView->SetAngleSnapEnabled( pFrameView->IsAngleSnapEnabled() ); + mpView->SetSnapEnabled(true); + mpView->SetCreate1stPointAsCenter(false); + mpView->SetResizeAtCenter(false); + mpView->SetDragWithCopy(pFrameView->IsDragWithCopy()); + mpView->SetGridSnap(pFrameView->IsGridSnap()); + mpView->SetBordSnap(pFrameView->IsBordSnap()); + mpView->SetHlplSnap(pFrameView->IsHlplSnap()); + mpView->SetOFrmSnap(pFrameView->IsOFrmSnap()); + mpView->SetOPntSnap(pFrameView->IsOPntSnap()); + mpView->SetOConSnap(pFrameView->IsOConSnap()); + } + + bIsInDragMode = false; + ForcePointer(&rMEvt); + FuPoor::MouseButtonUp(rMEvt); + + return false; +} + +/** + * Process keyboard input + * @returns sal_True if a KeyEvent is being processed, sal_False otherwise + */ +bool FuDraw::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + switch ( rKEvt.GetKeyCode().GetCode() ) + { + case KEY_ESCAPE: + { + bReturn = FuDraw::cancel(); + } + break; + + case KEY_DELETE: + case KEY_BACKSPACE: + { + if (!mpDocSh->IsReadOnly()) + { + if (mpView->IsPresObjSelected(false, true, false, true)) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(mpWindow->GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + // wait-mousepointer while deleting object + weld::WaitObject aWait(mpViewShell->GetFrameWeld()); + // delete object + mpView->DeleteMarked(); + } + } + bReturn = true; + } + break; + + case KEY_TAB: + { + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if ( !aCode.IsMod1() && !aCode.IsMod2() ) + { + // Moved next line which was a bugfix itself into + // the scope which really does the object selection travel + // and thus is allowed to call SelectionHasChanged(). + + // Switch to FuSelect. + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + // changeover to the next object + if(!mpView->MarkNextObj( !aCode.IsShift() )) + { + //If there is only one object, don't do the UnmarkAllObj() & MarkNextObj(). + if ( mpView->HasMultipleMarkableObjects() && mpView->AreObjectsMarked() ) + { + // No next object: go over open end and get first from + // the other side + mpView->UnmarkAllObj(); + mpView->MarkNextObj(!aCode.IsShift()); + } + } + + if(mpView->AreObjectsMarked()) + mpView->MakeVisible(mpView->GetAllMarkedRect(), *mpWindow); + + bReturn = true; + } + } + break; + + case KEY_END: + { + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if ( aCode.IsMod1() ) + { + // mark last object + mpView->UnmarkAllObj(); + mpView->MarkNextObj(); + + if(mpView->AreObjectsMarked()) + mpView->MakeVisible(mpView->GetAllMarkedRect(), *mpWindow); + + bReturn = true; + } + } + break; + + case KEY_HOME: + { + vcl::KeyCode aCode = rKEvt.GetKeyCode(); + + if ( aCode.IsMod1() ) + { + // mark first object + mpView->UnmarkAllObj(); + mpView->MarkNextObj(true); + + if(mpView->AreObjectsMarked()) + mpView->MakeVisible(mpView->GetAllMarkedRect(), *mpWindow); + + bReturn = true; + } + } + break; + + default: + break; + } + + if (!bReturn) + { + bReturn = FuPoor::KeyInput(rKEvt); + } + else + { + mpWindow->ReleaseMouse(); + } + + return bReturn; +} + +void FuDraw::Activate() +{ + FuPoor::Activate(); + ForcePointer(); +} + +/** + * Toggle mouse-pointer + */ +void FuDraw::ForcePointer(const MouseEvent* pMEvt) +{ + Point aPnt; + sal_uInt16 nModifier = 0; + bool bLeftDown = false; + bool bDefPointer = true; + + if (pMEvt) + { + aPnt = mpWindow->PixelToLogic(pMEvt->GetPosPixel()); + nModifier = pMEvt->GetModifier(); + bLeftDown = pMEvt->IsLeft(); + } + else + { + aPnt = mpWindow->PixelToLogic(mpWindow->GetPointerPosPixel()); + } + + if (mpView->IsDragObj()) + { + if (SD_MOD()->GetWaterCan() && !mpView->PickHandle(aPnt)) + { + // water can mode + bDefPointer = false; + mpWindow->SetPointer(PointerStyle::Fill); + } + } + else + { + SdrHdl* pHdl = mpView->PickHandle(aPnt); + + if (SD_MOD()->GetWaterCan() && !pHdl) + { + // water can mode + bDefPointer = false; + mpWindow->SetPointer(PointerStyle::Fill); + } + else if (!pHdl && + mpViewShell->GetViewFrame()->HasChildWindow(SvxBmpMaskChildWindow::GetChildWindowId())) + { + // pipette mode + SfxChildWindow* pWnd = mpViewShell->GetViewFrame()->GetChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + SvxBmpMask* pMask = pWnd ? static_cast(pWnd->GetWindow()) : nullptr; + if (pMask && pMask->IsEyedropping()) + { + bDefPointer = false; + mpWindow->SetPointer(PointerStyle::RefHand); + } + } + else if (!mpView->IsAction()) + { + SdrObject* pObj = nullptr; + SdrPageView* pPV = nullptr; + SdrViewEvent aVEvt; + SdrHitKind eHit = SdrHitKind::NONE; + SdrDragMode eDragMode = mpView->GetDragMode(); + + if (pMEvt) + { + eHit = mpView->PickAnything(*pMEvt, SdrMouseEventKind::MOVE, aVEvt); + } + + if ((eDragMode == SdrDragMode::Rotate) && (eHit == SdrHitKind::MarkedObject)) + { + // The goal of this request is show always the rotation arrow for 3D-objects at rotation mode + // Independent of the settings at Tools->Options->Draw "Objects always moveable" + // 2D-objects acquit in another way. Otherwise, the rotation of 3d-objects around any axes + // wouldn't be possible per default. + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + SdrObject* pObject = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if ((dynamic_cast(pObject) != nullptr) && (rMarkList.GetMarkCount() == 1)) + { + mpWindow->SetPointer(PointerStyle::Rotate); + bDefPointer = false; // Otherwise it'll be called Joe's routine and the mousepointer will reconfigurate again + } + } + + if (eHit == SdrHitKind::NONE) + { + // found nothing -> look after at the masterpage + pObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER); + } + else if (eHit == SdrHitKind::UnmarkedObject) + { + pObj = aVEvt.mpObj; + } + else if (eHit == SdrHitKind::TextEditObj && dynamic_cast< const FuSelection *>( this ) != nullptr) + { + SdrObjKind nSdrObjKind = aVEvt.mpObj->GetObjIdentifier(); + + if ( nSdrObjKind != SdrObjKind::Text && + nSdrObjKind != SdrObjKind::TitleText && + nSdrObjKind != SdrObjKind::OutlineText && + aVEvt.mpObj->IsEmptyPresObj() ) + { + pObj = nullptr; + bDefPointer = false; + mpWindow->SetPointer(PointerStyle::Arrow); + } + } + + if (pObj && pMEvt && !pMEvt->IsMod2() + && dynamic_cast(this) != nullptr) + { + // test for ImageMap + bDefPointer = !SetPointer(pObj, aPnt); + + if (bDefPointer + && (dynamic_cast(pObj) != nullptr + || dynamic_cast(pObj) != nullptr)) + { + // take a glance into the group + pObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV, + SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::DEEP); + if (pObj) + bDefPointer = !SetPointer(pObj, aPnt); + } + } + } + } + + if (bDefPointer) + { + mpWindow->SetPointer(mpView->GetPreferredPointer( + aPnt, mpWindow->GetOutDev(), nModifier, bLeftDown)); + } +} + +/** + * Set cursor to pointer when in clickable area of an ImageMap + * + * @return True when pointer was set + */ +bool FuDraw::SetPointer(const SdrObject* pObj, const Point& rPos) +{ + bool bImageMapInfo = SvxIMapInfo::GetIMapInfo(pObj) != nullptr; + + if (!bImageMapInfo) + return false; + + const SdrLayerIDSet* pVisiLayer = &mpView->GetSdrPageView()->GetVisibleLayers(); + sal_uInt16 nHitLog(sal_uInt16(mpWindow->PixelToLogic(Size(HITPIX, 0)).Width())); + ::tools::Long n2HitLog(nHitLog * 2); + Point aHitPosR(rPos); + Point aHitPosL(rPos); + Point aHitPosT(rPos); + Point aHitPosB(rPos); + + aHitPosR.AdjustX(n2HitLog); + aHitPosL.AdjustX(-n2HitLog); + aHitPosT.AdjustY(n2HitLog); + aHitPosB.AdjustY(-n2HitLog); + + if (!pObj->IsClosedObj() + || (SdrObjectPrimitiveHit(*pObj, aHitPosR, nHitLog, *mpView->GetSdrPageView(), pVisiLayer, + false) + && SdrObjectPrimitiveHit(*pObj, aHitPosL, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false) + && SdrObjectPrimitiveHit(*pObj, aHitPosT, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false) + && SdrObjectPrimitiveHit(*pObj, aHitPosB, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false))) + { + // hit inside the object (without margin) or open object + if (SvxIMapInfo::GetHitIMapObject(pObj, rPos)) + { + mpWindow->SetPointer(PointerStyle::RefHand); + return true; + } + } + + return false; +} + +/** + * Response of doubleclick + */ +void FuDraw::DoubleClick(const MouseEvent& rMEvt) +{ + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + + if ( mpView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::OLE2) + { + // activate OLE-object + SfxInt16Item aItem(SID_OBJECT, 0); + mpViewShell->GetViewFrame()-> + GetDispatcher()->ExecuteList(SID_OBJECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + } + else if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::Graphic && pObj->IsEmptyPresObj() ) + { + mpViewShell->GetViewFrame()-> + GetDispatcher()->Execute( SID_INSERT_GRAPHIC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + else if ( ( dynamic_cast< const SdrTextObj *>( pObj ) != nullptr || dynamic_cast< const SdrObjGroup *>( pObj ) != nullptr ) && + !SD_MOD()->GetWaterCan() && + mpViewShell->GetFrameView()->IsDoubleClickTextEdit() && + !mpDocSh->IsReadOnly()) + { + SfxUInt16Item aItem(SID_TEXTEDIT, 2); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_TEXTEDIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + } + else if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::Group) + { + // hit group -> select subobject + mpView->UnMarkAll(); + mpView->MarkObj(aMDPos, nHitLog, rMEvt.IsShift(), true); + } + } + } + else + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); +} + +bool FuDraw::RequestHelp(const HelpEvent& rHEvt) +{ + bool bReturn = false; + + if (Help::IsBalloonHelpEnabled() || Help::IsQuickHelpEnabled()) + { + SdrViewEvent aVEvt; + + MouseEvent aMEvt(mpWindow->GetPointerPosPixel(), 1, MouseEventModifiers::NONE, MOUSE_LEFT); + + SdrHitKind eHit = mpView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + SdrObject* pObj = aVEvt.mpObj; + + if (eHit != SdrHitKind::NONE && pObj != nullptr) + { + Point aPosPixel = rHEvt.GetMousePosPixel(); + + bReturn = SetHelpText(pObj, aPosPixel, aVEvt); + + if (!bReturn && (dynamic_cast< const SdrObjGroup *>( pObj ) != nullptr || dynamic_cast< const E3dScene* >(pObj) != nullptr)) + { + // take a glance into the group + SdrPageView* pPV = nullptr; + + Point aPos(mpWindow->PixelToLogic(mpWindow->ScreenToOutputPixel(aPosPixel))); + + pObj = mpView->PickObj(aPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::DEEP); + if (pObj) + bReturn = SetHelpText(pObj, aPosPixel, aVEvt); + } + } + } + + if (!bReturn) + { + bReturn = FuPoor::RequestHelp(rHEvt); + } + + if (!bReturn) + bReturn = mpView->RequestHelp(rHEvt); + + return bReturn; +} + +bool FuDraw::SetHelpText(const SdrObject* pObj, const Point& rPosPixel, const SdrViewEvent& rVEvt) +{ + OUString aHelpText; + Point aPos(mpWindow->PixelToLogic(mpWindow->ScreenToOutputPixel(rPosPixel))); + IMapObject* pIMapObj = SvxIMapInfo::GetHitIMapObject(pObj, aPos); + + if (!rVEvt.mpURLField && !pIMapObj) + return false; + + OUString aURL; + if (rVEvt.mpURLField) + aURL = INetURLObject::decode(rVEvt.mpURLField->GetURL(), + INetURLObject::DecodeMechanism::WithCharset); + else if (pIMapObj) + { + aURL = pIMapObj->GetAltText() + + " (" + + INetURLObject::decode(pIMapObj->GetURL(), + INetURLObject::DecodeMechanism::WithCharset) + + ")"; + } + else + return false; + + aHelpText = SfxHelp::GetURLHelpText(aURL); + + if (aHelpText.isEmpty()) + return false; + + ::tools::Rectangle aLogicPix = mpWindow->LogicToPixel(pObj->GetLogicRect()); + ::tools::Rectangle aScreenRect(mpWindow->OutputToScreenPixel(aLogicPix.TopLeft()), + mpWindow->OutputToScreenPixel(aLogicPix.BottomRight())); + + if (Help::IsBalloonHelpEnabled()) + Help::ShowBalloon( static_cast(mpWindow), rPosPixel, aScreenRect, aHelpText); + else if (Help::IsQuickHelpEnabled()) + Help::ShowQuickHelp( static_cast(mpWindow), aScreenRect, aHelpText); + + return true; +} + +/** is called when the current function should be aborted.

+ This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted +*/ +bool FuDraw::cancel() +{ + bool bReturn = false; + + if ( mpView->IsAction() ) + { + mpView->BrkAction(); + bReturn = true; + } + else if ( mpView->IsTextEdit() ) + { + mpView->SdrEndTextEdit(); + bReturn = true; + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_DEC_INDENT ); + rBindings.Invalidate( SID_INC_INDENT ); + rBindings.Invalidate( SID_PARASPACE_INCREASE ); + rBindings.Invalidate( SID_PARASPACE_DECREASE ); + } + else if ( mpView->AreObjectsMarked() ) + { + const SdrHdlList& rHdlList = mpView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + const_cast(rHdlList).ResetFocusHdl(); + } + else + { + mpView->UnmarkAll(); + } + + // Switch to FuSelect. + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + bReturn = true; + } + + return bReturn; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fudspord.cxx b/sd/source/ui/func/fudspord.cxx new file mode 100644 index 000000000..f129c523c --- /dev/null +++ b/sd/source/ui/func/fudspord.cxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace sd { + + +FuDisplayOrder::FuDisplayOrder( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq) +: FuPoor(pViewSh, pWin, pView, pDoc, rReq) +, maPtr(PointerStyle::Arrow) +, mpRefObj(nullptr) +{ +} + +FuDisplayOrder::~FuDisplayOrder() +{ +} + +void FuDisplayOrder::implClearOverlay() +{ + mpOverlay.reset(); +} + +rtl::Reference FuDisplayOrder::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuDisplayOrder( pViewSh, pWin, pView, pDoc, rReq ) ); + return xFunc; +} + +bool FuDisplayOrder::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + return true; +} + +bool FuDisplayOrder::MouseMove(const MouseEvent& rMEvt) +{ + SdrPageView* pPV; + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + SdrObject* pPickObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV); + if (pPickObj) + { + if (mpRefObj != pPickObj) + { + // delete current overlay + implClearOverlay(); + + // create new one + mpOverlay.reset( new SdrDropMarkerOverlay(*mpView, *pPickObj) ); + + // remember referenced object + mpRefObj = pPickObj; + } + } + else + { + mpRefObj = nullptr; + implClearOverlay(); + } + + return true; +} + +bool FuDisplayOrder::MouseButtonUp(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + SdrPageView* pPV = nullptr; + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + mpRefObj = mpView->PickObj(aPnt, mpView->getHitTolLog(), pPV); + if (mpRefObj) + { + if (nSlotId == SID_BEFORE_OBJ) + { + mpView->PutMarkedInFrontOfObj(mpRefObj); + } + else + { + mpView->PutMarkedBehindObj(mpRefObj); + } + } + + mpViewShell->Cancel(); + + return true; +} + +void FuDisplayOrder::Activate() +{ + maPtr = mpWindow->GetPointer(); + mpWindow->SetPointer( PointerStyle::RefHand ); +} + +void FuDisplayOrder::Deactivate() +{ + mpWindow->SetPointer( maPtr ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuediglu.cxx b/sd/source/ui/func/fuediglu.cxx new file mode 100644 index 000000000..5d9d61447 --- /dev/null +++ b/sd/source/ui/func/fuediglu.cxx @@ -0,0 +1,471 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace sd { + + +FuEditGluePoints::FuEditGluePoints ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuDraw(pViewSh, pWin, pView, pDoc, rReq) + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + ,bBeginInsertPoint(false), + oldPoint(0,0) +{ +} + +rtl::Reference FuEditGluePoints::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ) +{ + FuEditGluePoints* pFunc; + rtl::Reference xFunc( pFunc = new FuEditGluePoints( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + pFunc->SetPermanent( bPermanent ); + return xFunc; +} + +void FuEditGluePoints::DoExecute( SfxRequest& rReq ) +{ + FuDraw::DoExecute( rReq ); + mpView->SetInsGluePointMode(false); + mpViewShell->GetViewShellBase().GetToolBarManager()->AddToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msGluePointsToolBar); +} + +FuEditGluePoints::~FuEditGluePoints() +{ + mpView->BrkAction(); + mpView->UnmarkAllGluePoints(); + mpView->SetInsGluePointMode(false); +} + +bool FuEditGluePoints::MouseButtonDown(const MouseEvent& rMEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + + if (mpView->IsAction()) + { + if (rMEvt.IsRight()) + mpView->BckAction(); + + return true; + } + + if (rMEvt.IsLeft()) + { + bReturn = true; + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpWindow->CaptureMouse(); + + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::Handle) + { + // drag handle + SdrHdl* pHdl = aVEvt.mpHdl; + + if (mpView->IsGluePointMarked(aVEvt.mpObj, aVEvt.mnGlueId) && rMEvt.IsShift()) + { + mpView->UnmarkGluePoint(aVEvt.mpObj, aVEvt.mnGlueId); + pHdl = nullptr; + } + + if (pHdl) + { + // drag handle + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + } + else if (eHit == SdrHitKind::MarkedObject && mpView->IsInsGluePointMode()) + { + // insert gluepoints + mpView->BegInsGluePoint(aMDPos); + } + else if (eHit == SdrHitKind::MarkedObject && rMEvt.IsMod1()) + { + // select gluepoints + if (!rMEvt.IsShift()) + mpView->UnmarkAllGluePoints(); + + mpView->BegMarkGluePoints(aMDPos); + } + else if (eHit == SdrHitKind::MarkedObject && !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + // move object + mpView->BegDragObj(aMDPos, nullptr, nullptr, nDrgLog); + } + else if (eHit == SdrHitKind::Gluepoint) + { + // select gluepoints + if (!rMEvt.IsShift()) + mpView->UnmarkAllGluePoints(); + + mpView->MarkGluePoint(aVEvt.mpObj, aVEvt.mnGlueId, false); + SdrHdl* pHdl = mpView->GetGluePointHdl(aVEvt.mpObj, aVEvt.mnGlueId); + + if (pHdl) + { + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + } + } + else + { + // select or drag object + if (!rMEvt.IsShift() && !rMEvt.IsMod2() && eHit == SdrHitKind::UnmarkedObject) + { + mpView->UnmarkAllObj(); + } + + bool bMarked = false; + + if (!rMEvt.IsMod1()) + { + if (rMEvt.IsMod2()) + { + bMarked = mpView->MarkNextObj(aMDPos, nHitLog, rMEvt.IsShift()); + } + else + { + bMarked = mpView->MarkObj(aMDPos, nHitLog, rMEvt.IsShift()); + } + } + + if (bMarked && + (!rMEvt.IsShift() || eHit == SdrHitKind::MarkedObject)) + { + // move object + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + else if (mpView->AreObjectsMarked()) + { + // select gluepoint + if (!rMEvt.IsShift()) + mpView->UnmarkAllGluePoints(); + + mpView->BegMarkGluePoints(aMDPos); + } + else + { + // select object + mpView->BegMarkObj(aMDPos); + } + } + + ForcePointer(&rMEvt); + } + + return bReturn; +} + +bool FuEditGluePoints::MouseMove(const MouseEvent& rMEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + + FuDraw::MouseMove(rMEvt); + + if (mpView->IsAction()) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt( mpWindow->PixelToLogic(aPix) ); + ForceScroll(aPix); + mpView->MovAction(aPnt); + } + + ForcePointer(&rMEvt); + + return true; +} + +bool FuEditGluePoints::MouseButtonUp(const MouseEvent& rMEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + + bool bReturn = false; + + if (mpView->IsAction()) + { + bReturn = true; + mpView->EndAction(); + } + + FuDraw::MouseButtonUp(rMEvt); + + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + Point aPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + if (std::abs(aMDPos.X() - aPos.X()) < nDrgLog && + std::abs(aMDPos.Y() - aPos.Y()) < nDrgLog && + !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::NONE) + { + // click on position: deselect + mpView->UnmarkAllObj(); + } + } + + mpWindow->ReleaseMouse(); + + return bReturn; +} + +/** + * Process keyboard input + * @returns sal_True if a KeyEvent is being processed, sal_False otherwise + */ +bool FuEditGluePoints::KeyInput(const KeyEvent& rKEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + + bool bReturn = false; + + switch (rKEvt.GetKeyCode().GetCode()) + { + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + if(rKEvt.GetKeyCode().IsShift()&& mpView->IsInsGluePointMode() ){ + ::tools::Long nX = 0; + ::tools::Long nY = 0; + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + if (nCode == KEY_UP) + { + // scroll up + nX = 0; + nY =-1; + } + else if (nCode == KEY_DOWN) + { + // scroll down + nX = 0; + nY = 1; + } + else if (nCode == KEY_LEFT) + { + // scroll left + nX =-1; + nY = 0; + } + else if (nCode == KEY_RIGHT) + { + // scroll right + nX = 1; + nY = 0; + } + Point centerPoint; + ::tools::Rectangle rect = mpView->GetMarkedObjRect(); + centerPoint = mpWindow->LogicToPixel(rect.Center()); + Point aPoint = bBeginInsertPoint? oldPoint:centerPoint; + Point ePoint = aPoint + Point(nX,nY); + mpWindow->SetPointerPosPixel(ePoint); + //simulate mouse move action + MouseEvent eMevt(ePoint, 1, MouseEventModifiers::DRAGMOVE, MOUSE_LEFT, 0); + MouseMove(eMevt); + oldPoint = ePoint; + bBeginInsertPoint = true; + bReturn = true; + } + } + break; + case KEY_RETURN: + if(rKEvt.GetKeyCode().IsShift() && mpView->IsInsGluePointMode() ) + { + if(bBeginInsertPoint) + { + mpWindow->SetPointerPosPixel(oldPoint); + //simulate mouse button down action + MouseEvent aMevt(oldPoint, 1, + MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::DRAGMOVE, + MOUSE_LEFT, KEY_SHIFT); + // MT IA2: Not used? + // sal_uInt16 ubuttons = aMevt.GetButtons(); + // sal_uInt16 uMod = aMevt.GetModifier(); + MouseButtonDown(aMevt); + mpWindow->CaptureMouse(); + //simulate mouse button up action + MouseEvent rMEvt(oldPoint+Point(0,0), 1, + MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::ENTERWINDOW, + MOUSE_LEFT, KEY_SHIFT); + MouseButtonUp(rMEvt); + bReturn= true; + } + } + break; + } + + if(!bReturn) + bReturn = FuDraw::KeyInput(rKEvt); + + return bReturn; +} + +//Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, and +//SHIFT+ENTER key to decide the position and draw the new insert point +void FuEditGluePoints::ForcePointer(const MouseEvent* pMEvt) +{ + if(bBeginInsertPoint && pMEvt) + { + MouseEvent aMEvt(pMEvt->GetPosPixel(), pMEvt->GetClicks(), + pMEvt->GetMode(), pMEvt->GetButtons(), pMEvt->GetModifier() & ~KEY_SHIFT); + FuDraw::ForcePointer(&aMEvt); + } + else + { + FuDraw::ForcePointer(pMEvt); + } +} + +bool FuEditGluePoints::Command(const CommandEvent& rCEvt) +{ + mpView->SetActualWin( mpWindow->GetOutDev() ); + return FuPoor::Command( rCEvt ); +} + +void FuEditGluePoints::Activate() +{ + mpView->SetGluePointEditMode(); + FuDraw::Activate(); +} + +void FuEditGluePoints::Deactivate() +{ + mpView->SetGluePointEditMode( false ); + FuDraw::Deactivate(); +} + +void FuEditGluePoints::ReceiveRequest(SfxRequest& rReq) +{ + switch (rReq.GetSlot()) + { + case SID_GLUE_INSERT_POINT: + { + mpView->SetInsGluePointMode(!mpView->IsInsGluePointMode()); + } + break; + + case SID_GLUE_ESCDIR_LEFT: + { + mpView->SetMarkedGluePointsEscDir( SdrEscapeDirection::LEFT, + !mpView->IsMarkedGluePointsEscDir( SdrEscapeDirection::LEFT ) ); + } + break; + + case SID_GLUE_ESCDIR_RIGHT: + { + mpView->SetMarkedGluePointsEscDir( SdrEscapeDirection::RIGHT, + !mpView->IsMarkedGluePointsEscDir( SdrEscapeDirection::RIGHT ) ); + } + break; + + case SID_GLUE_ESCDIR_TOP: + { + mpView->SetMarkedGluePointsEscDir( SdrEscapeDirection::TOP, + !mpView->IsMarkedGluePointsEscDir( SdrEscapeDirection::TOP ) ); + } + break; + + case SID_GLUE_ESCDIR_BOTTOM: + { + mpView->SetMarkedGluePointsEscDir( SdrEscapeDirection::BOTTOM, + !mpView->IsMarkedGluePointsEscDir( SdrEscapeDirection::BOTTOM ) ); + } + break; + + case SID_GLUE_PERCENT: + { + const SfxItemSet* pSet = rReq.GetArgs(); + const SfxPoolItem& rItem = pSet->Get(SID_GLUE_PERCENT); + bool bPercent = static_cast(rItem).GetValue(); + mpView->SetMarkedGluePointsPercent(bPercent); + } + break; + + case SID_GLUE_HORZALIGN_CENTER: + { + mpView->SetMarkedGluePointsAlign(false, SdrAlign::HORZ_CENTER); + } + break; + + case SID_GLUE_HORZALIGN_LEFT: + { + mpView->SetMarkedGluePointsAlign(false, SdrAlign::HORZ_LEFT); + } + break; + + case SID_GLUE_HORZALIGN_RIGHT: + { + mpView->SetMarkedGluePointsAlign(false, SdrAlign::HORZ_RIGHT); + } + break; + + case SID_GLUE_VERTALIGN_CENTER: + { + mpView->SetMarkedGluePointsAlign(true, SdrAlign::VERT_CENTER); + } + break; + + case SID_GLUE_VERTALIGN_TOP: + { + mpView->SetMarkedGluePointsAlign(true, SdrAlign::VERT_TOP); + } + break; + + case SID_GLUE_VERTALIGN_BOTTOM: + { + mpView->SetMarkedGluePointsAlign(true, SdrAlign::VERT_BOTTOM); + } + break; + } + + // at the end, call base class + FuPoor::ReceiveRequest(rReq); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuexecuteinteraction.cxx b/sd/source/ui/func/fuexecuteinteraction.cxx new file mode 100644 index 000000000..d1956fcf5 --- /dev/null +++ b/sd/source/ui/func/fuexecuteinteraction.cxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace css; + +namespace sd +{ +FuExecuteInteraction::FuExecuteInteraction(ViewShell* pViewSh, ::sd::Window* pWin, + ::sd::View* pView, SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuExecuteInteraction::Create(ViewShell* pViewSh, ::sd::Window* pWin, + ::sd::View* pView, SdDrawDocument* pDoc, + SfxRequest& rReq) +{ + rtl::Reference xFunc(new FuExecuteInteraction(pViewSh, pWin, pView, pDoc, rReq)); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuExecuteInteraction::DoExecute(SfxRequest&) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() != 1) + return; + + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + if (dynamic_cast(mpDocSh) != nullptr + || dynamic_cast(mpView) == nullptr) + return; + + SdAnimationInfo* pInfo = SdDrawDocument::GetAnimationInfo(pObj); + if (!pInfo) + return; + + switch (pInfo->meClickAction) + { + case presentation::ClickAction_BOOKMARK: + { + // Jump to Bookmark (Page or Object) + SfxStringItem aItem(SID_NAVIGATOR_OBJECT, pInfo->GetBookmark()); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_OBJECT, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_DOCUMENT: + { + OUString sBookmark(pInfo->GetBookmark()); + // Jump to document + if (!sBookmark.isEmpty()) + { + SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName()); + SfxStringItem aStrItem(SID_FILE_NAME, sBookmark); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + SfxBoolItem aBrowseItem(SID_BROWSE, true); + pFrame->GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + } + break; + + case presentation::ClickAction_PREVPAGE: + { + // Jump to the previous page + SfxUInt16Item aItem(SID_NAVIGATOR_PAGE, PAGE_PREVIOUS); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_PAGE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_NEXTPAGE: + { + // Jump to the next page + SfxUInt16Item aItem(SID_NAVIGATOR_PAGE, PAGE_NEXT); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_PAGE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_FIRSTPAGE: + { + // Jump to the first page + SfxUInt16Item aItem(SID_NAVIGATOR_PAGE, PAGE_FIRST); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_PAGE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_LASTPAGE: + { + // Jump to the last page + SfxUInt16Item aItem(SID_NAVIGATOR_PAGE, PAGE_LAST); + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_NAVIGATOR_PAGE, SfxCallMode::SLOT | SfxCallMode::RECORD, { &aItem }); + } + break; + + case presentation::ClickAction_SOUND: + { +#if HAVE_FEATURE_AVMEDIA + try + { + mxPlayer.set(avmedia::MediaWindow::createPlayer(pInfo->GetBookmark(), "" /*TODO?*/), + uno::UNO_SET_THROW); + mxPlayer->start(); + } + catch (uno::Exception&) + { + } +#endif + } + break; + + case presentation::ClickAction_VERB: + { + // Assign verb + mpView->UnmarkAll(); + mpView->MarkObj(pObj, mpView->GetSdrPageView()); + DrawViewShell* pDrViewSh = static_cast(mpViewShell); + pDrViewSh->DoVerb(static_cast(pInfo->mnVerb)); + } + break; + + case presentation::ClickAction_PROGRAM: + { + OUString aBaseURL = GetDocSh()->GetMedium()->GetBaseURL(); + INetURLObject aURL(::URIHelper::SmartRel2Abs( + INetURLObject(aBaseURL), pInfo->GetBookmark(), URIHelper::GetMaybeFileHdl(), true, + false, INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous)); + + if (INetProtocol::File == aURL.GetProtocol()) + { + SfxStringItem aUrl(SID_FILE_NAME, + aURL.GetMainURL(INetURLObject::DecodeMechanism::NONE)); + SfxBoolItem aBrowsing(SID_BROWSE, true); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + pViewFrm->GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aUrl, &aBrowsing }); + } + } + break; + +#if HAVE_FEATURE_SCRIPTING + case presentation::ClickAction_MACRO: + { + // Execute macro + OUString aMacro = pInfo->GetBookmark(); + + if (SfxApplication::IsXScriptURL(aMacro)) + { + uno::Any aRet; + uno::Sequence aOutArgsIndex; + uno::Sequence aParams; + uno::Sequence aOutArgs; + + mpDocSh->CallXScript(aMacro, aParams, aRet, aOutArgsIndex, aOutArgs); + } + else + { + // aMacro has got following format: + // "Macroname.Modulname.Libname.Documentname" or + // "Macroname.Modulname.Libname.Applicationname" + sal_Int32 nIdx{ 0 }; + const std::u16string_view aMacroName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aMacro, 0, '.', nIdx); + + // Currently the "Call" method only resolves modulename+macroname + mpDocSh->GetBasic()->Call(OUString::Concat(aModulName) + "." + aMacroName); + } + } + break; +#endif + + default: + break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuexpand.cxx b/sd/source/ui/func/fuexpand.cxx new file mode 100644 index 000000000..822174ed9 --- /dev/null +++ b/sd/source/ui/func/fuexpand.cxx @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + + +FuExpandPage::FuExpandPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuExpandPage::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuExpandPage( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuExpandPage::DoExecute( SfxRequest& ) +{ + if ( mpView && mpView->IsTextEdit() ) + mpView->SdrEndTextEdit(); + + // find selected page (only standard pages) + SdPage* pActualPage = nullptr; + sal_uInt16 i = 0; + sal_uInt16 nCount = mpDoc->GetSdPageCount(PageKind::Standard); + + while (!pActualPage && i < nCount) + { + if (mpDoc->GetSdPage(i, PageKind::Standard)->IsSelected()) + { + pActualPage = mpDoc->GetSdPage(i, PageKind::Standard); + } + + i++; + } + + if (!pActualPage) + return; + + SdOutliner aOutliner( mpDoc, OutlinerMode::OutlineObject ); + aOutliner.SetUpdateLayout(false); + aOutliner.EnableUndo(false); + + if (mpDocSh) + aOutliner.SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + aOutliner.SetDefTab( mpDoc->GetDefaultTabulator() ); + aOutliner.SetStyleSheetPool(static_cast(mpDoc->GetStyleSheetPool())); + + SdrLayerIDSet aVisibleLayers = pActualPage->TRG_GetMasterPageVisibleLayers(); + sal_uInt16 nActualPageNum = pActualPage->GetPageNum(); + SdPage* pActualNotesPage = static_cast(mpDoc->GetPage(nActualPageNum + 1)); + SdrTextObj* pActualOutline = static_cast(pActualPage->GetPresObj(PresObjKind::Outline)); + + if (pActualOutline) + { + const bool bUndo = mpView->IsUndoEnabled(); + + if( bUndo ) + mpView->BegUndo(SdResId(STR_UNDO_EXPAND_PAGE)); + + // set current structuring-object into outliner + OutlinerParaObject* pParaObj = pActualOutline->GetOutlinerParaObject(); + aOutliner.SetText(*pParaObj); + + // remove hard paragraph- and character attributes + SfxItemSetFixed aEmptyEEAttr(mpDoc->GetPool()); + sal_Int32 nParaCount1 = aOutliner.GetParagraphCount(); + + for (sal_Int32 nPara = 0; nPara < nParaCount1; nPara++) + { + aOutliner.RemoveCharAttribs(nPara); + aOutliner.SetParaAttribs(nPara, aEmptyEEAttr); + } + + sal_uInt16 nPos = 2; + Paragraph* pPara = aOutliner.GetParagraph( 0 ); + + while (pPara) + { + sal_Int32 nParaPos = aOutliner.GetAbsPos( pPara ); + sal_Int16 nDepth = aOutliner.GetDepth( nParaPos ); + if ( nDepth == 0 ) + { + // page with title & structuring! + rtl::Reference pPage = mpDoc->AllocSdPage(false); + pPage->SetSize(pActualPage->GetSize() ); + pPage->SetBorder(pActualPage->GetLeftBorder(), + pActualPage->GetUpperBorder(), + pActualPage->GetRightBorder(), + pActualPage->GetLowerBorder() ); + pPage->SetName(OUString()); + + // insert page after current page + mpDoc->InsertPage(pPage.get(), nActualPageNum + nPos); + nPos++; + + if( bUndo ) + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoNewPage(*pPage)); + + // use MasterPage of the current page + pPage->TRG_SetMasterPage(pActualPage->TRG_GetMasterPage()); + pPage->SetLayoutName(pActualPage->GetLayoutName()); + pPage->SetAutoLayout(AUTOLAYOUT_TITLE_CONTENT, true); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + + // notes-page + rtl::Reference pNotesPage = mpDoc->AllocSdPage(false); + pNotesPage->SetSize(pActualNotesPage->GetSize()); + pNotesPage->SetBorder(pActualNotesPage->GetLeftBorder(), + pActualNotesPage->GetUpperBorder(), + pActualNotesPage->GetRightBorder(), + pActualNotesPage->GetLowerBorder() ); + pNotesPage->SetPageKind(PageKind::Notes); + pNotesPage->SetName(OUString()); + + // insert page after current page + mpDoc->InsertPage(pNotesPage.get(), nActualPageNum + nPos); + nPos++; + + if( bUndo ) + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoNewPage(*pNotesPage)); + + // use MasterPage of the current page + pNotesPage->TRG_SetMasterPage(pActualNotesPage->TRG_GetMasterPage()); + pNotesPage->SetLayoutName(pActualNotesPage->GetLayoutName()); + pNotesPage->SetAutoLayout(pActualNotesPage->GetAutoLayout(), true); + pNotesPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + + // create title text objects + SdrTextObj* pTextObj = static_cast(pPage->GetPresObj(PresObjKind::Title)); + SAL_WARN_IF(!pTextObj, "sd.core", "worrying lack of PresObjKind::Title object"); + if (!pTextObj) + continue; + + std::optional pOutlinerParaObject = aOutliner.CreateParaObject( nParaPos, 1); + pOutlinerParaObject->SetOutlinerMode(OutlinerMode::TitleObject); + + if( pOutlinerParaObject->GetDepth(0) != -1 ) + { + std::unique_ptr pTempOutl = SdrMakeOutliner(OutlinerMode::TitleObject, *mpDoc); + + pTempOutl->SetText( *pOutlinerParaObject ); + + pOutlinerParaObject.reset(); + + pTempOutl->SetDepth( pTempOutl->GetParagraph( 0 ), -1 ); + + pOutlinerParaObject = pTempOutl->CreateParaObject(); + } + + pTextObj->SetOutlinerParaObject(std::move(pOutlinerParaObject)); + + pTextObj->SetEmptyPresObj(false); + + SfxStyleSheet* pSheet = pPage->GetStyleSheetForPresObj(PresObjKind::Title); + pTextObj->NbcSetStyleSheet(pSheet, false); + + SdrTextObj* pOutlineObj = nullptr; + sal_Int32 nChildCount = aOutliner.GetChildCount(pPara); + if (nChildCount > 0) + pOutlineObj = static_cast( pPage->GetPresObj(PresObjKind::Outline) ); + if (pOutlineObj) + { + // create structuring text objects + std::optional pOPO = aOutliner.CreateParaObject(++nParaPos, nChildCount); + + std::unique_ptr pTempOutl = SdrMakeOutliner(OutlinerMode::OutlineObject, *mpDoc); + pTempOutl->SetText( *pOPO ); + + sal_Int32 nParaCount2 = pTempOutl->GetParagraphCount(); + sal_Int32 nPara; + for( nPara = 0; nPara < nParaCount2; nPara++ ) + { + pTempOutl->SetDepth ( + pTempOutl->GetParagraph( nPara ), + pTempOutl->GetDepth( nPara ) - 1); + } + + pOPO = pTempOutl->CreateParaObject(); + pTempOutl.reset(); + + pOutlineObj->SetOutlinerParaObject( std::move(pOPO) ); + pOutlineObj->SetEmptyPresObj(false); + + // remove hard attributes (Flag to sal_True) + SfxItemSet aAttr(mpDoc->GetPool()); + aAttr.Put(XLineStyleItem(drawing::LineStyle_NONE)); + aAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + pOutlineObj->SetMergedItemSet(aAttr); + } + } + + pPara = aOutliner.GetParagraph( ++nParaPos ); + } + + if( bUndo ) + mpView->EndUndo(); + } + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_DELETE_PAGE, SfxCallMode::SYNCHRON | SfxCallMode::RECORD); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuformatpaintbrush.cxx b/sd/source/ui/func/fuformatpaintbrush.cxx new file mode 100644 index 000000000..40bde764f --- /dev/null +++ b/sd/source/ui/func/fuformatpaintbrush.cxx @@ -0,0 +1,276 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace sd { + + +FuFormatPaintBrush::FuFormatPaintBrush( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +: FuText(pViewSh, pWin, pView, pDoc, rReq) +, mbPermanent( false ) +, mbOldIsQuickTextEditMode( true ) +{ +} + +rtl::Reference FuFormatPaintBrush::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuFormatPaintBrush( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute( rReq ); + return xFunc; +} + +void FuFormatPaintBrush::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet *pArgs = rReq.GetArgs(); + if( pArgs && pArgs->Count() >= 1 ) + { + mbPermanent = pArgs->Get(SID_FORMATPAINTBRUSH).GetValue(); + } + + if( mpView ) + { + mpView->TakeFormatPaintBrush( mxItemSet ); + } +} + +void FuFormatPaintBrush::implcancel() +{ + if( mpViewShell && mpViewShell->GetViewFrame() ) + { + SfxViewFrame* pViewFrame = mpViewShell->GetViewFrame(); + pViewFrame->GetBindings().Invalidate(SID_FORMATPAINTBRUSH); + pViewFrame->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } +} + +static void unmarkimpl( SdrView* pView ) +{ + pView->SdrEndTextEdit(); + pView->UnMarkAll(); +} + +bool FuFormatPaintBrush::MouseButtonDown(const MouseEvent& rMEvt) +{ + if(mpView&&mpWindow) + { + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if( (eHit == SdrHitKind::TextEdit) || (eHit == SdrHitKind::TextEditObj && ( mpViewShell->GetFrameView()->IsQuickEdit() || dynamic_cast(aVEvt.mpObj) != nullptr ) )) + { + SdrPageView* pPV=nullptr; + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + SdrObject* pPickObj = mpView->PickObj(mpWindow->PixelToLogic(rMEvt.GetPosPixel()),nHitLog, pPV, SdrSearchOptions::PICKMARKABLE); + if( (pPickObj != nullptr) && !pPickObj->IsEmptyPresObj() ) + { + // if we text hit another shape than the one currently selected, unselect the old one now + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() > 0 ) + { + if( rMarkList.GetMarkCount() == 1 ) + { + if( rMarkList.GetMark(0)->GetMarkedSdrObj() != pPickObj ) + { + + // if current selected shape is not that of the hit text edit, deselect it + unmarkimpl( mpView ); + } + } + else + { + // more than one shape selected, deselect all of them + unmarkimpl( mpView ); + } + } + MouseEvent aMEvt( rMEvt.GetPosPixel(), rMEvt.GetClicks(), rMEvt.GetMode(), rMEvt.GetButtons(), 0 ); + return FuText::MouseButtonDown(aMEvt); + } + + if (aVEvt.mpObj == nullptr) + aVEvt.mpObj = pPickObj; + } + + unmarkimpl( mpView ); + + if (aVEvt.mpObj) + { + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + mpView->MarkObj(mpWindow->PixelToLogic( rMEvt.GetPosPixel() ), nHitLog, false/*bToggle*/); + return true; + } + } + return false; +} + +bool FuFormatPaintBrush::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = false; + if( mpWindow && mpView ) + { + if ( mpView->IsTextEdit() ) + { + bReturn = FuText::MouseMove( rMEvt ); + mpWindow->SetPointer(PointerStyle::Fill); + } + else + { + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + SdrPageView* pPV=nullptr; + SdrObject* pObj = mpView->PickObj(mpWindow->PixelToLogic( rMEvt.GetPosPixel() ),nHitLog, pPV, SdrSearchOptions::PICKMARKABLE); + if (pObj && HasContentForThisType(pObj->GetObjInventor(),pObj->GetObjIdentifier()) ) + mpWindow->SetPointer(PointerStyle::Fill); + else + mpWindow->SetPointer(PointerStyle::Arrow); + } + } + return bReturn; +} + +bool FuFormatPaintBrush::MouseButtonUp(const MouseEvent& rMEvt) +{ + if( mxItemSet && mpView && mpView->AreObjectsMarked() ) + { + bool bNoCharacterFormats = false; + bool bNoParagraphFormats = false; + { + if( (rMEvt.GetModifier()&KEY_MOD1) && (rMEvt.GetModifier()&KEY_SHIFT) ) + bNoCharacterFormats = true; + else if( rMEvt.GetModifier() & KEY_MOD1 ) + bNoParagraphFormats = true; + } + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + if( pOLV ) + pOLV->MouseButtonUp(rMEvt); + + Paste( bNoCharacterFormats, bNoParagraphFormats ); + if(mpViewShell) + mpViewShell->GetViewFrame()->GetBindings().Invalidate(SID_FORMATPAINTBRUSH); + + if( mbPermanent ) + return true; + } + + implcancel(); + return true; +} + +bool FuFormatPaintBrush::KeyInput(const KeyEvent& rKEvt) +{ + if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE) + { + implcancel(); + return true; + } + return FuPoor::KeyInput(rKEvt); +} + +void FuFormatPaintBrush::Activate() +{ + mbOldIsQuickTextEditMode = mpViewShell->GetFrameView()->IsQuickEdit(); + if( !mbOldIsQuickTextEditMode ) + { + mpViewShell->GetFrameView()->SetQuickEdit(true); + mpView->SetQuickTextEditMode(true); + } +} + +void FuFormatPaintBrush::Deactivate() +{ + if( !mbOldIsQuickTextEditMode ) + { + mpViewShell->GetFrameView()->SetQuickEdit(false); + mpView->SetQuickTextEditMode(false); + } +} + +bool FuFormatPaintBrush::HasContentForThisType( SdrInventor nObjectInventor, SdrObjKind nObjectIdentifier ) const +{ + if (mxItemSet == nullptr) + return false; + if( !mpView || (!SdrObjEditView::SupportsFormatPaintbrush( nObjectInventor, nObjectIdentifier) ) ) + return false; + return true; +} + +void FuFormatPaintBrush::Paste( bool bNoCharacterFormats, bool bNoParagraphFormats ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( !(mxItemSet && ( rMarkList.GetMarkCount() == 1 )) ) + return; + + SdrObject* pObj( nullptr ); + bool bUndo = mpDoc->IsUndoEnabled(); + + if( bUndo && !mpView->GetTextEditOutlinerView() ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + // n685123: ApplyFormatPaintBrush itself would store undo information + // except in a few cases (?) + if( pObj ) + { + OUString sLabel( mpViewShell->GetViewShellBase().RetrieveLabelFromCommand(".uno:FormatPaintbrush" ) ); + mpDoc->BegUndo( sLabel ); + if (dynamic_cast< sdr::table::SdrTableObj* >( pObj ) == nullptr) + mpDoc->AddUndo( mpDoc->GetSdrUndoFactory().CreateUndoAttrObject( *pObj, false, true ) ); + } + + mpView->ApplyFormatPaintBrush( *mxItemSet, bNoCharacterFormats, bNoParagraphFormats ); + + if( pObj ) + { + mpDoc->EndUndo(); + } +} + +/* static */ void FuFormatPaintBrush::GetMenuState( DrawViewShell const & rDrawViewShell, SfxItemSet &rSet ) +{ + const SdrMarkList& rMarkList = rDrawViewShell.GetDrawView()->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( pObj && SdrObjEditView::SupportsFormatPaintbrush(pObj->GetObjInventor(),pObj->GetObjIdentifier()) ) + return; + } + rSet.DisableItem( SID_FORMATPAINTBRUSH ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuhhconv.cxx b/sd/source/ui/func/fuhhconv.cxx new file mode 100644 index 000000000..a312439bb --- /dev/null +++ b/sd/source/ui/func/fuhhconv.cxx @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +class SfxRequest; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; + +namespace sd { + + +FuHangulHanjaConversion::FuHangulHanjaConversion ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDocument, + SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDocument, rReq), + pSdOutliner(nullptr), + bOwnOutliner(false) +{ + if ( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr ) + { + bOwnOutliner = true; + pSdOutliner = new SdOutliner( mpDoc, OutlinerMode::TextObject ); + } + else if ( dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr ) + { + bOwnOutliner = false; + pSdOutliner = mpDoc->GetOutliner(); + } + + if (pSdOutliner) + pSdOutliner->PrepareSpelling(); +} + +FuHangulHanjaConversion::~FuHangulHanjaConversion() +{ + if (pSdOutliner) + pSdOutliner->EndConversion(); + + if (bOwnOutliner) + delete pSdOutliner; +} + +rtl::Reference FuHangulHanjaConversion::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuHangulHanjaConversion( pViewSh, pWin, pView, pDoc, rReq ) ); + return xFunc; +} + +/** + * Search and replace + */ +void FuHangulHanjaConversion::StartConversion( LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font *pTargetFont, sal_Int32 nOptions, bool bIsInteractive ) +{ + + mpView->BegUndo(SdResId(STR_UNDO_HANGULHANJACONVERSION)); + + ViewShellBase* pBase = dynamic_cast( SfxViewShell::Current() ); + if (pBase != nullptr) + mpViewShell = pBase->GetMainViewShell().get(); + + if( mpViewShell ) + { + if ( pSdOutliner && dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr && !bOwnOutliner ) + { + pSdOutliner->EndConversion(); + + bOwnOutliner = true; + pSdOutliner = new SdOutliner( mpDoc, OutlinerMode::TextObject ); + pSdOutliner->BeginConversion(); + } + else if ( pSdOutliner && dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr && bOwnOutliner ) + { + pSdOutliner->EndConversion(); + delete pSdOutliner; + + bOwnOutliner = false; + pSdOutliner = mpDoc->GetOutliner(); + pSdOutliner->BeginConversion(); + } + + if (pSdOutliner) + pSdOutliner->StartConversion(nSourceLanguage, nTargetLanguage, pTargetFont, nOptions, bIsInteractive ); + } + + // Due to changing between edit mode, notes mode, and handout mode the + // view has most likely changed. Get the new one. + mpViewShell = pBase ? pBase->GetMainViewShell().get() : nullptr; + if (mpViewShell != nullptr) + { + mpView = mpViewShell->GetView(); + mpWindow = mpViewShell->GetActiveWindow(); + } + else + { + mpView = nullptr; + mpWindow = nullptr; + } + + if (mpView != nullptr) + mpView->EndUndo(); +} + +void FuHangulHanjaConversion::ConvertStyles( LanguageType nTargetLanguage, const vcl::Font *pTargetFont ) +{ + if( !mpDoc ) + return; + + SfxStyleSheetBasePool* pStyleSheetPool = mpDoc->GetStyleSheetPool(); + if( !pStyleSheetPool ) + return; + + SfxStyleSheetBase* pStyle = pStyleSheetPool->First(SfxStyleFamily::All); + while( pStyle ) + { + SfxItemSet& rSet = pStyle->GetItemSet(); + + const bool bHasParent = !pStyle->GetParent().isEmpty(); + + if( !bHasParent || rSet.GetItemState( EE_CHAR_LANGUAGE_CJK, false ) == SfxItemState::SET ) + rSet.Put( SvxLanguageItem( nTargetLanguage, EE_CHAR_LANGUAGE_CJK ) ); + + if( pTargetFont && + ( !bHasParent || rSet.GetItemState( EE_CHAR_FONTINFO_CJK, false ) == SfxItemState::SET ) ) + { + // set new font attribute + SvxFontItem aFontItem( rSet.Get( EE_CHAR_FONTINFO_CJK ) ); + aFontItem.SetFamilyName( pTargetFont->GetFamilyName()); + aFontItem.SetFamily( pTargetFont->GetFamilyType()); + aFontItem.SetStyleName( pTargetFont->GetStyleName()); + aFontItem.SetPitch( pTargetFont->GetPitch()); + aFontItem.SetCharSet( pTargetFont->GetCharSet()); + rSet.Put( aFontItem ); + } + + pStyle = pStyleSheetPool->Next(); + } + + mpDoc->SetLanguage( nTargetLanguage, EE_CHAR_LANGUAGE_CJK ); +} + +void FuHangulHanjaConversion::StartChineseConversion() +{ + //open ChineseTranslationDialog + Reference< XComponentContext > xContext( + ::cppu::defaultBootstrap_InitialComponentContext() ); //@todo get context from calc if that has one + if(!xContext.is()) + return; + + Reference< lang::XMultiComponentFactory > xMCF( xContext->getServiceManager() ); + if(!xMCF.is()) + return; + + Reference< ui::dialogs::XExecutableDialog > xDialog( + xMCF->createInstanceWithContext("com.sun.star.linguistic2.ChineseTranslationDialog" + , xContext), UNO_QUERY); + Reference< lang::XInitialization > xInit( xDialog, UNO_QUERY ); + if( xInit.is() ) + { + // initialize dialog + Reference< awt::XWindow > xDialogParentWindow; + Sequence aSeq(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", uno::Any(xDialogParentWindow)} + })); + xInit->initialize( aSeq ); + + //execute dialog + sal_Int16 nDialogRet = xDialog->execute(); + if( RET_OK == nDialogRet ) + { + //get some parameters from the dialog + bool bToSimplified = true; + bool bUseVariants = true; + bool bCommonTerms = true; + Reference< beans::XPropertySet > xProp( xDialog, UNO_QUERY ); + if( xProp.is() ) + { + try + { + xProp->getPropertyValue( "IsDirectionToSimplified" ) >>= bToSimplified; + xProp->getPropertyValue( "IsUseCharacterVariants" ) >>= bUseVariants; + xProp->getPropertyValue( "IsTranslateCommonTerms" ) >>= bCommonTerms; + } + catch( Exception& ) + { + } + } + + //execute translation + LanguageType nSourceLang = bToSimplified ? LANGUAGE_CHINESE_TRADITIONAL : LANGUAGE_CHINESE_SIMPLIFIED; + LanguageType nTargetLang = bToSimplified ? LANGUAGE_CHINESE_SIMPLIFIED : LANGUAGE_CHINESE_TRADITIONAL; + sal_Int32 nOptions = bUseVariants ? i18n::TextConversionOption::USE_CHARACTER_VARIANTS : 0; + if( !bCommonTerms ) + nOptions = nOptions | i18n::TextConversionOption::CHARACTER_BY_CHARACTER; + + vcl::Font aTargetFont = OutputDevice::GetDefaultFont( + DefaultFontType::CJK_PRESENTATION, + nTargetLang, GetDefaultFontFlags::OnlyOne ); + + StartConversion( nSourceLang, nTargetLang, &aTargetFont, nOptions, false ); + ConvertStyles( nTargetLang, &aTargetFont ); + } + } + Reference< lang::XComponent > xComponent( xDialog, UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); +} +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuinsert.cxx b/sd/source/ui/func/fuinsert.cxx new file mode 100644 index 000000000..919814d56 --- /dev/null +++ b/sd/source/ui/func/fuinsert.cxx @@ -0,0 +1,767 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace com::sun::star; + +namespace sd { + + +FuInsertGraphic::FuInsertGraphic ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq, + bool replaceExistingImage) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq), + mbReplaceExistingImage(replaceExistingImage) +{ +} + +rtl::Reference FuInsertGraphic::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq, bool replaceExistingImage ) +{ + rtl::Reference xFunc( new FuInsertGraphic( pViewSh, pWin, pView, pDoc, rReq, replaceExistingImage ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertGraphic::DoExecute( SfxRequest& rReq ) +{ + OUString aFileName; + Graphic aGraphic; + + bool bAsLink = false; + ErrCode nError = ERRCODE_GRFILTER_OPENERROR; + + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + + if ( pArgs && + pArgs->GetItemState( SID_INSERT_GRAPHIC, true, &pItem ) == SfxItemState::SET ) + { + aFileName = static_cast(pItem)->GetValue(); + + OUString aFilterName; + if ( const SfxStringItem* pFilterItem = pArgs->GetItemIfSet( FN_PARAM_FILTER ) ) + aFilterName = pFilterItem->GetValue(); + + if ( pArgs->GetItemState( FN_PARAM_1, true, &pItem ) == SfxItemState::SET ) + bAsLink = static_cast(pItem)->GetValue(); + + nError = GraphicFilter::LoadGraphic( aFileName, aFilterName, aGraphic, &GraphicFilter::GetGraphicFilter() ); + } + else + { + SvxOpenGraphicDialog aDlg(SdResId(STR_INSERTGRAPHIC), mpWindow ? mpWindow->GetFrameWeld() : nullptr); + + if( aDlg.Execute() != ERRCODE_NONE ) + return; // cancel dialog + + nError = aDlg.GetGraphic(aGraphic); + bAsLink = aDlg.IsAsLink(); + aFileName = aDlg.GetPath(); + } + + if( nError == ERRCODE_NONE ) + { + GraphicNativeMetadata aMetadata; + if ( aMetadata.read(aGraphic) ) + { + const Degree10 aRotation = aMetadata.getRotation(); + if (aRotation) + { + GraphicNativeTransform aTransform( aGraphic ); + aTransform.rotate( aRotation ); + } + } + if( dynamic_cast< DrawViewShell *>( mpViewShell ) ) + { + sal_Int8 nAction = DND_ACTION_COPY; + SdrObject* pPickObj = nullptr; + if (mbReplaceExistingImage) + pPickObj = mpView->GetSelectedSingleObject( mpView->GetPage() ); + if (pPickObj) + nAction = DND_ACTION_LINK; + else + { + pPickObj = mpView->GetEmptyPresentationObject( PresObjKind::Graphic ); + if (pPickObj) + nAction = DND_ACTION_LINK; + } + + Point aPos = mpWindow->GetVisibleCenter(); + SdrGrafObj* pGrafObj = mpView->InsertGraphic(aGraphic, nAction, aPos, pPickObj, nullptr); + + if(pGrafObj && bAsLink ) + { + // really store as link only? + if( officecfg::Office::Common::Misc::ShowLinkWarningDialog::get() ) + { + SvxLinkWarningDialog aWarnDlg(mpWindow->GetFrameWeld(), aFileName); + if (aWarnDlg.run() != RET_OK) + return; // don't store as link + } + + // store as link + pGrafObj->SetGraphicLink(aFileName); + } + } + } + else + { + SdGRFFilter::HandleGraphicFilterError( nError, GraphicFilter::GetGraphicFilter().GetLastError() ); + } +} + +FuInsertClipboard::FuInsertClipboard ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuInsertClipboard::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuInsertClipboard( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertClipboard::DoExecute( SfxRequest& ) +{ + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mpWindow ) ); + SotClipboardFormatId nFormatId; + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreatePasteDialog(mpViewShell->GetFrameWeld())); + pDlg->Insert( SotClipboardFormatId::EMBED_SOURCE, OUString() ); + pDlg->Insert( SotClipboardFormatId::LINK_SOURCE, OUString() ); + pDlg->Insert( SotClipboardFormatId::DRAWING, OUString() ); + pDlg->Insert( SotClipboardFormatId::SVXB, OUString() ); + pDlg->Insert( SotClipboardFormatId::GDIMETAFILE, OUString() ); + pDlg->Insert( SotClipboardFormatId::BITMAP, OUString() ); + pDlg->Insert( SotClipboardFormatId::NETSCAPE_BOOKMARK, OUString() ); + pDlg->Insert( SotClipboardFormatId::STRING, OUString() ); + pDlg->Insert( SotClipboardFormatId::HTML, OUString() ); + pDlg->Insert( SotClipboardFormatId::RTF, OUString() ); + pDlg->Insert( SotClipboardFormatId::RICHTEXT, OUString() ); + pDlg->Insert( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT, OUString() ); + + //TODO/MBA: testing + nFormatId = pDlg->GetFormat( aDataHelper ); + if( nFormatId == SotClipboardFormatId::NONE || !aDataHelper.GetTransferable().is() ) + return; + + sal_Int8 nAction = DND_ACTION_COPY; + DrawViewShell* pDrViewSh = nullptr; + + if (!mpView->InsertData( aDataHelper, + mpWindow->PixelToLogic( ::tools::Rectangle( Point(), mpWindow->GetOutputSizePixel() ).Center() ), + nAction, false, nFormatId )) + { + pDrViewSh = dynamic_cast(mpViewShell); + } + + if (!pDrViewSh) + return; + + INetBookmark aINetBookmark( "", "" ); + + if( ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::FILEGRPDESCRIPTOR, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::UNIFORMRESOURCELOCATOR, aINetBookmark ) ) ) + { + pDrViewSh->InsertURLField( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), "" ); + } +} + +FuInsertOLE::FuInsertOLE ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuInsertOLE::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuInsertOLE( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertOLE::DoExecute( SfxRequest& rReq ) +{ + if ( nSlotId == SID_ATTR_TABLE || + nSlotId == SID_INSERT_DIAGRAM || + nSlotId == SID_INSERT_MATH ) + { + PresObjKind ePresObjKind = (nSlotId == SID_INSERT_DIAGRAM) ? PresObjKind::Chart : PresObjKind::Object; + + SdrObject* pPickObj = mpView->GetEmptyPresentationObject( ePresObjKind ); + + // insert diagram or Calc table + OUString aObjName; + SvGlobalName aName; + if (nSlotId == SID_INSERT_DIAGRAM) + aName = SvGlobalName( SO3_SCH_CLASSID); + else if (nSlotId == SID_ATTR_TABLE) + aName = SvGlobalName(SO3_SC_CLASSID); + else if (nSlotId == SID_INSERT_MATH) + aName = SvGlobalName(SO3_SM_CLASSID); + + uno::Reference < embed::XEmbeddedObject > xObj = mpViewShell->GetViewFrame()->GetObjectShell()-> + GetEmbeddedObjectContainer().CreateEmbeddedObject( aName.GetByteSequence(), aObjName ); + if ( xObj.is() ) + { + // Create default chart type. + uno::Reference xChartDoc(xObj->getComponent(), uno::UNO_QUERY); + if (xChartDoc.is()) + xChartDoc->createDefaultChart(); + + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + + ::tools::Rectangle aRect; + if( pPickObj ) + { + aRect = pPickObj->GetLogicRect(); + + awt::Size aSz; + aSz.Width = aRect.GetWidth(); + aSz.Height = aRect.GetHeight(); + xObj->setVisualAreaSize( nAspect, aSz ); + } + else + { + awt::Size aSz; + try + { + aSz = xObj->getVisualAreaSize( nAspect ); + } + catch ( embed::NoVisualAreaSizeException& ) + { + // the default size will be set later + } + + Size aSize( aSz.Width, aSz.Height ); + + if (aSize.IsEmpty()) + { + // rectangle with balanced edge ratio + aSize.setWidth( 14100 ); + aSize.setHeight( 10000 ); + Size aTmp = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aUnit)); + aSz.Width = aTmp.Width(); + aSz.Height = aTmp.Height(); + xObj->setVisualAreaSize( nAspect, aSz ); + } + else + { + aSize = OutputDevice::LogicToLogic(aSize, MapMode(aUnit), MapMode(MapUnit::Map100thMM)); + } + + Point aPos = mpWindow->GetVisibleCenter(); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + aRect = ::tools::Rectangle(aPos, aSize); + } + + SdrOle2Obj* pOleObj = new SdrOle2Obj( + mpView->getSdrModelFromSdrView(), + svt::EmbeddedObjectRef( xObj, nAspect ), + aObjName, + aRect); + SdrPageView* pPV = mpView->GetSdrPageView(); + + // if we have a pick obj we need to make this new ole a pres obj replacing the current pick obj + if( pPickObj ) + { + SdPage* pPage = static_cast< SdPage* >(pPickObj->getSdrPageFromSdrObject()); + if(pPage && pPage->IsPresObj(pPickObj)) + { + pPage->InsertPresObj( pOleObj, ePresObjKind ); + pOleObj->SetUserCall(pPickObj->GetUserCall()); + } + + // #i123468# we need to end text edit before replacing the object. There cannot yet + // being text typed (else it would not be an EmptyPresObj anymore), but it may be + // in text edit mode + if (mpView->IsTextEdit()) + { + mpView->SdrEndTextEdit(); + } + } + + bool bRet = true; + if( pPickObj ) + mpView->ReplaceObjectAtView(pPickObj, *pPV, pOleObj ); + else + bRet = mpView->InsertObjectAtView(pOleObj, *pPV, SdrInsertFlags::SETDEFLAYER); + + if (bRet && !comphelper::LibreOfficeKit::isActive()) + { + // Let the chart be activated after the inserting (unless + // via LibreOfficeKit) + if (nSlotId == SID_INSERT_DIAGRAM) + { + pOleObj->SetProgName( "StarChart"); + } + else if (nSlotId == SID_ATTR_TABLE) + { + pOleObj->SetProgName( "StarCalc" ); + } + else if (nSlotId == SID_INSERT_MATH) + { + pOleObj->SetProgName( "StarMath" ); + } + + pOleObj->SetLogicRect(aRect); + Size aTmp( OutputDevice::LogicToLogic(aRect.GetSize(), MapMode(MapUnit::Map100thMM), MapMode(aUnit)) ); + awt::Size aVisualSize; + aVisualSize.Width = aTmp.Width(); + aVisualSize.Height = aTmp.Height(); + xObj->setVisualAreaSize( nAspect, aVisualSize ); + mpViewShell->ActivateObject(pOleObj, embed::EmbedVerbs::MS_OLEVERB_SHOW); + + if (nSlotId == SID_INSERT_DIAGRAM) + { + // note, that this call modified the chart model which + // results in a change notification. So call this after + // everything else is finished. + ChartHelper::AdaptDefaultsForChart( xObj ); + } + } + } + else + { + ErrorHandler::HandleError(* new StringErrorInfo(ERRCODE_SFX_OLEGENERAL, + "" ) ); + } + } + else + { + // insert object + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + bool bCreateNew = false; + uno::Reference < embed::XEmbeddedObject > xObj; + uno::Reference < embed::XStorage > xStorage = comphelper::OStorageHelper::GetTemporaryStorage(); + SvObjectServerList aServerLst; + OUString aName; + + OUString aIconMediaType; + uno::Reference< io::XInputStream > xIconMetaFile; + + const SfxGlobalNameItem* pNameItem = rReq.GetArg(SID_INSERT_OBJECT); + if ( nSlotId == SID_INSERT_OBJECT && pNameItem ) + { + const SvGlobalName& aClassName = pNameItem->GetValue(); + xObj = mpViewShell->GetViewFrame()->GetObjectShell()-> + GetEmbeddedObjectContainer().CreateEmbeddedObject( aClassName.GetByteSequence(), aName ); + } + else + { + switch ( nSlotId ) + { + case SID_INSERT_OBJECT : + { + aServerLst.FillInsertObjects(); + if (mpDoc->GetDocumentType() == DocumentType::Draw) + { + aServerLst.Remove( GraphicDocShell::Factory().GetClassId() ); + } + else + { + aServerLst.Remove( DrawDocShell::Factory().GetClassId() ); + } + + [[fallthrough]]; + } + case SID_INSERT_FLOATINGFRAME : + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( + pFact->CreateInsertObjectDialog( mpViewShell->GetFrameWeld(), SD_MOD()->GetSlotPool()->GetSlot(nSlotId)->GetCommandString(), + xStorage, &aServerLst )); + pDlg->Execute(); + bCreateNew = pDlg->IsCreateNew(); + xObj = pDlg->GetObject(); + + xIconMetaFile = pDlg->GetIconIfIconified( &aIconMediaType ); + if ( xIconMetaFile.is() ) + nAspect = embed::Aspects::MSOLE_ICON; + + if ( xObj.is() ) + mpViewShell->GetObjectShell()->GetEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aName ); + + break; + } + } + } + + try + { + if (xObj.is()) + { + bool bInsertNewObject = true; + + Size aSize; + MapUnit aMapUnit = MapUnit::Map100thMM; + if ( nAspect != embed::Aspects::MSOLE_ICON ) + { + awt::Size aSz; + try + { + aSz = xObj->getVisualAreaSize( nAspect ); + } + catch( embed::NoVisualAreaSizeException& ) + { + // the default size will be set later + } + + aSize =Size( aSz.Width, aSz.Height ); + + aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + if (aSize.IsEmpty()) + { + // rectangle with balanced edge ratio + aSize.setWidth( 14100 ); + aSize.setHeight( 10000 ); + Size aTmp = OutputDevice::LogicToLogic(aSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + aSz.Width = aTmp.Width(); + aSz.Height = aTmp.Height(); + xObj->setVisualAreaSize( nAspect, aSz ); + } + else + { + aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM)); + } + } + + if ( mpView->AreObjectsMarked() ) + { + // as an empty OLE object available? + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + if (pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::OLE2) + { + if ( !static_cast(pObj)->GetObjRef().is() ) + { + // the empty OLE object gets a new IPObj + bInsertNewObject = false; + pObj->SetEmptyPresObj(false); + static_cast(pObj)->SetOutlinerParaObject(std::nullopt); + static_cast(pObj)->SetObjRef(xObj); + static_cast(pObj)->SetPersistName(aName); + static_cast(pObj)->SetName(aName); + static_cast(pObj)->SetAspect(nAspect); + ::tools::Rectangle aRect = static_cast(pObj)->GetLogicRect(); + + if ( nAspect == embed::Aspects::MSOLE_ICON ) + { + if( xIconMetaFile.is() ) + static_cast(pObj)->SetGraphicToObj( xIconMetaFile, aIconMediaType ); + } + else + { + Size aTmp = OutputDevice::LogicToLogic(aRect.GetSize(), MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + awt::Size aSz( aTmp.Width(), aTmp.Height() ); + xObj->setVisualAreaSize( nAspect, aSz ); + } + } + } + } + } + + if (bInsertNewObject) + { + // we create a new OLE object + SdrPageView* pPV = mpView->GetSdrPageView(); + Size aPageSize = pPV->GetPage()->GetSize(); + + // get the size from the iconified object + ::svt::EmbeddedObjectRef aObjRef( xObj, nAspect ); + if ( nAspect == embed::Aspects::MSOLE_ICON ) + { + aObjRef.SetGraphicStream( xIconMetaFile, aIconMediaType ); + MapMode aMapMode( MapUnit::Map100thMM ); + aSize = aObjRef.GetSize( &aMapMode ); + } + + Point aPnt ((aPageSize.Width() - aSize.Width()) / 2, + (aPageSize.Height() - aSize.Height()) / 2); + ::tools::Rectangle aRect (aPnt, aSize); + SdrOle2Obj* pObj = new SdrOle2Obj( + mpView->getSdrModelFromSdrView(), + aObjRef, + aName, + aRect); + + if( mpView->InsertObjectAtView(pObj, *pPV, SdrInsertFlags::SETDEFLAYER) ) + { + // Math objects change their object size during InsertObject. + // New size must be set in SdrObject, or a wrong scale will be set at + // ActivateObject. + + if ( nAspect != embed::Aspects::MSOLE_ICON ) + { + try + { + awt::Size aSz = xObj->getVisualAreaSize( nAspect ); + + Size aNewSize = OutputDevice::LogicToLogic( Size( aSz.Width, aSz.Height ), + MapMode( aMapUnit ), MapMode( MapUnit::Map100thMM ) ); + if ( aNewSize != aSize ) + { + aRect.SetSize( aNewSize ); + pObj->SetLogicRect( aRect ); + } + } + catch( embed::NoVisualAreaSizeException& ) + {} + } + + if (bCreateNew) + { + pObj->SetLogicRect(aRect); + + if ( nAspect != embed::Aspects::MSOLE_ICON ) + { + Size aTmp = OutputDevice::LogicToLogic(aRect.GetSize(), MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + awt::Size aSz( aTmp.Width(), aTmp.Height() ); + xObj->setVisualAreaSize( nAspect, aSz ); + } + + mpViewShell->ActivateObject(pObj, embed::EmbedVerbs::MS_OLEVERB_SHOW); + } + + Size aVisSizePixel = mpWindow->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = mpWindow->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + mpViewShell->VisAreaChanged(aVisAreaWin); + mpDocSh->SetVisArea(aVisAreaWin); + } + } + } + } + catch (uno::Exception&) + { + // For some reason the object can not be inserted. For example + // because it is password protected and is not properly unlocked. + } + } +} + +FuInsertAVMedia::FuInsertAVMedia( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuInsertAVMedia::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuInsertAVMedia( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertAVMedia::DoExecute( SfxRequest& rReq ) +{ +#if HAVE_FEATURE_AVMEDIA + OUString aURL; + const SfxItemSet* pReqArgs = rReq.GetArgs(); + bool bAPI = false; + + const SvxSizeItem* pSizeItem = rReq.GetArg(FN_PARAM_1); + const SfxBoolItem* pLinkItem = rReq.GetArg(FN_PARAM_2); + const bool bSizeUnknown = !pSizeItem; + Size aPrefSize; + + if( pReqArgs ) + { + const SfxStringItem* pStringItem = dynamic_cast( &pReqArgs->Get( rReq.GetSlot() ) ); + + if( pStringItem ) + { + aURL = pStringItem->GetValue(); + bAPI = !aURL.isEmpty(); + } + } + + bool bLink(pLinkItem ? pLinkItem->GetValue() : true); + if (!(bAPI + || ::avmedia::MediaWindow::executeMediaURLDialog(mpWindow ? mpWindow->GetFrameWeld() : nullptr, aURL, & bLink) + )) + return; + + if (!bSizeUnknown) + { + aPrefSize = pSizeItem->GetSize(); + } + else + { + // If we don't have a size then try and find that out, the resulted might be deliver async, so dispatch a follow up + // effort to insert the video, this time with a size. + if( mpWindow ) + mpWindow->EnterWait(); + + css::uno::Reference xDispatchProvider(mpViewShell->GetViewFrame()->GetFrame().GetFrameInterface(), css::uno::UNO_QUERY); + + rtl::Reference xPlayerListener(new avmedia::PlayerListener( + [xDispatchProvider, aURL, bLink](const css::uno::Reference& rPlayer){ + css::awt::Size aSize = rPlayer->getPreferredPlayerWindowSize(); + avmedia::MediaWindow::dispatchInsertAVMedia(xDispatchProvider, aSize, aURL, bLink); + })); + + const bool bIsMediaURL = ::avmedia::MediaWindow::isMediaURL(aURL, "", true, xPlayerListener); + + if( mpWindow ) + mpWindow->LeaveWait(); + + if (!bIsMediaURL && !bAPI) + ::avmedia::MediaWindow::executeFormatErrorBox(mpWindow->GetFrameWeld()); + + return; + } + + InsertMediaURL(aURL, aPrefSize, bLink); + +#else + (void)rReq; +#endif +} + +#if HAVE_FEATURE_AVMEDIA +void FuInsertAVMedia::InsertMediaURL(const OUString& rURL, const Size& rPrefSize, bool bLink) +{ + if( mpWindow ) + mpWindow->EnterWait(); + + Point aPos; + Size aSize; + sal_Int8 nAction = DND_ACTION_COPY; + + if (rPrefSize.Width() && rPrefSize.Height()) + { + if( mpWindow ) + aSize = mpWindow->PixelToLogic(rPrefSize, MapMode(MapUnit::Map100thMM)); + else + aSize = Application::GetDefaultDevice()->PixelToLogic(rPrefSize, MapMode(MapUnit::Map100thMM)); + } + else + aSize = Size( 5000, 5000 ); + + if( mpWindow ) + { + aPos = mpWindow->PixelToLogic( ::tools::Rectangle( aPos, mpWindow->GetOutputSizePixel() ).Center() ); + aPos.AdjustX( -(aSize.Width() >> 1) ); + aPos.AdjustY( -(aSize.Height() >> 1) ); + } + + mpView->InsertMediaURL(rURL, nAction, aPos, aSize, bLink); + + if( mpWindow ) + mpWindow->LeaveWait(); +} +#endif + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuinsfil.cxx b/sd/source/ui/func/fuinsfil.cxx new file mode 100644 index 000000000..6569514cf --- /dev/null +++ b/sd/source/ui/func/fuinsfil.cxx @@ -0,0 +1,725 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star; + +typedef ::std::pair< OUString, OUString > FilterDesc; + +namespace +{ + +OUString lcl_GetExtensionsList ( ::std::vector< FilterDesc > const& rFilterDescList ) +{ + OUStringBuffer aExtensions; + + for (const auto& rFilterDesc : rFilterDescList) + { + OUString sWildcard = rFilterDesc.second; + + if ( aExtensions.indexOf( sWildcard ) == -1 ) + { + if ( !aExtensions.isEmpty() ) + aExtensions.append(";"); + aExtensions.append(sWildcard); + } + + } + + return aExtensions.makeStringAndClear(); +} + +void lcl_AddFilter ( ::std::vector< FilterDesc >& rFilterDescList, + const std::shared_ptr& pFilter ) +{ + if (pFilter) + rFilterDescList.emplace_back( pFilter->GetUIName(), pFilter->GetDefaultExtension() ); +} + +} + +namespace sd { + + +FuInsertFile::FuInsertFile ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuInsertFile::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuInsertFile( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuInsertFile::DoExecute( SfxRequest& rReq ) +{ + SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher(); + ::std::vector< FilterDesc > aFilterVector; + ::std::vector< OUString > aOtherFilterVector; + const SfxItemSet* pArgs = rReq.GetArgs (); + + FuInsertFile::GetSupportedFilterVector( aOtherFilterVector ); + + if (!pArgs) + { + sfx2::FileDialogHelper aFileDialog( + ui::dialogs::TemplateDescription::FILEOPEN_SIMPLE, + FileDialogFlags::Insert, mpWindow ? mpWindow->GetFrameWeld() : nullptr); + aFileDialog.SetContext(sfx2::FileDialogHelper::DrawImpressInsertFile); + Reference< XFilePicker > xFilePicker( aFileDialog.GetFilePicker() ); + Reference< XFilterManager > xFilterManager( xFilePicker, UNO_QUERY ); + OUString aOwnCont; + OUString aOtherCont; + + aFileDialog.SetTitle( SdResId(STR_DLG_INSERT_PAGES_FROM_FILE) ); + + if( mpDoc->GetDocumentType() == DocumentType::Impress ) + { + aOwnCont = "simpress"; + aOtherCont = "sdraw"; + } + else + { + aOtherCont = "simpress"; + aOwnCont = "sdraw" ; + } + + SfxFilterMatcher aMatch( aOwnCont ); + + if( xFilterManager.is() ) + { + // Get filter for current format + try + { + // Get main filter + std::shared_ptr pFilter = SfxFilter::GetDefaultFilterFromFactory( aOwnCont ); + lcl_AddFilter( aFilterVector, pFilter ); + + // get template filter + if( mpDoc->GetDocumentType() == DocumentType::Impress ) + pFilter = DrawDocShell::Factory().GetTemplateFilter(); + else + pFilter = GraphicDocShell::Factory().GetTemplateFilter(); + lcl_AddFilter( aFilterVector, pFilter ); + + // get cross filter + pFilter = SfxFilter::GetDefaultFilterFromFactory( aOtherCont ); + lcl_AddFilter( aFilterVector, pFilter ); + + // get Powerpoint filter + pFilter = aMatch.GetFilter4Extension( ".ppt" ); + lcl_AddFilter( aFilterVector, pFilter ); + + // Get other draw/impress filters + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARIMPRESS_60, SfxFilterFlags::IMPORT, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARIMPRESS_60, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARDRAW_60, SfxFilterFlags::IMPORT, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARDRAW_60, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARDRAW, SfxFilterFlags::IMPORT, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + pFilter = aMatch.GetFilter4ClipBoardId( SotClipboardFormatId::STARDRAW, SfxFilterFlags::TEMPLATEPATH ); + lcl_AddFilter( aFilterVector, pFilter ); + + // add additional supported filters + for( const auto& rOtherFilter : aOtherFilterVector ) + { + if( ( pFilter = rMatcher.GetFilter4Mime( rOtherFilter ) ) != nullptr ) + lcl_AddFilter( aFilterVector, pFilter ); + } + + // set "All supported formats" as the default filter + OUString aAllSpec( SdResId( STR_ALL_SUPPORTED_FORMATS ) ); + OUString aExtensions = lcl_GetExtensionsList( aFilterVector ); + OUString aGUIName = aAllSpec + " (" + aExtensions + ")"; + + xFilterManager->appendFilter( aGUIName, aExtensions ); + xFilterManager->setCurrentFilter( aAllSpec ); + + // append individual filters + for( const auto& rFilter : aFilterVector ) + { + xFilterManager->appendFilter( rFilter.first, rFilter.second ); + } + + // end with "All files" as fallback + xFilterManager->appendFilter( SdResId( STR_ALL_FILES ), "*.*" ); + } + catch (const IllegalArgumentException&) + { + } + } + + if( aFileDialog.Execute() != ERRCODE_NONE ) + return; + else + { + aFilterName = aFileDialog.GetCurrentFilter(); + aFile = aFileDialog.GetPath(); + } + } + else + { + const SfxStringItem* pFileName = rReq.GetArg(ID_VAL_DUMMY0); + const SfxStringItem* pFilterName = rReq.GetArg(ID_VAL_DUMMY1); + + aFile = pFileName->GetValue (); + + if( pFilterName ) + aFilterName = pFilterName->GetValue (); + } + + mpDocSh->SetWaitCursor( true ); + + std::unique_ptr xMedium(new SfxMedium(aFile, StreamMode::READ | StreamMode::NOCREATE)); + std::shared_ptr pFilter; + + SfxGetpApp()->GetFilterMatcher().GuessFilter(*xMedium, pFilter); + + bool bDrawMode = dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr; + bool bInserted = false; + + if( pFilter ) + { + xMedium->SetFilter( pFilter ); + aFilterName = pFilter->GetFilterName(); + + if( xMedium->IsStorage() || ( xMedium->GetInStream() && SotStorage::IsStorageFile( xMedium->GetInStream() ) ) ) + { + if ( pFilter->GetServiceName() == "com.sun.star.presentation.PresentationDocument" || + pFilter->GetServiceName() == "com.sun.star.drawing.DrawingDocument" ) + { + // Draw, Impress or PowerPoint document + // the ownership of the Medium is transferred + if( bDrawMode ) + InsSDDinDrMode(xMedium.release()); + else + InsSDDinOlMode(xMedium.release()); + + // ownership of pMedium has changed in this case + bInserted = true; + } + } + else + { + bool bFound = ( ::std::find( aOtherFilterVector.begin(), aOtherFilterVector.end(), pFilter->GetMimeType() ) != aOtherFilterVector.end() ); + if( !bFound && + ( aFilterName.indexOf( "Text" ) != -1 || + aFilterName.indexOf( "Rich" ) != -1 || + aFilterName.indexOf( "RTF" ) != -1 || + aFilterName.indexOf( "HTML" ) != -1 ) ) + { + bFound = true; + } + + if( bFound ) + { + if( bDrawMode ) + InsTextOrRTFinDrMode(xMedium.get()); + else + InsTextOrRTFinOlMode(xMedium.get()); + + bInserted = true; + xMedium.reset(); + } + } + } + + mpDocSh->SetWaitCursor( false ); + + if( !bInserted ) + { + std::unique_ptr xErrorBox(Application::CreateMessageDialog(mpWindow->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + } +} + +bool FuInsertFile::InsSDDinDrMode(SfxMedium* pMedium) +{ + bool bOK = false; + + mpDocSh->SetWaitCursor( false ); + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + weld::Window* pParent = mpViewShell ? mpViewShell->GetFrameWeld() : nullptr; + ScopedVclPtr pDlg( pFact->CreateSdInsertPagesObjsDlg(pParent, mpDoc, pMedium, aFile) ); + + sal_uInt16 nRet = pDlg->Execute(); + + mpDocSh->SetWaitCursor( true ); + + if( nRet == RET_OK ) + { + /* list with page names (if NULL, then all pages) + First, insert pages */ + std::vector aBookmarkList = pDlg->GetList( 1 ); // pages + bool bLink = pDlg->IsLink(); + SdPage* pPage = nullptr; + ::sd::View* pView = mpViewShell ? mpViewShell->GetView() : nullptr; + + if (pView) + { + if( auto pOutlineView = dynamic_cast( pView )) + { + pPage = pOutlineView->GetActualPage(); + } + else + { + pPage = static_cast(pView->GetSdrPageView()->GetPage()); + } + } + + sal_uInt16 nPos = 0xFFFF; + + if (pPage && !pPage->IsMasterPage()) + { + if (pPage->GetPageKind() == PageKind::Standard) + { + nPos = pPage->GetPageNum() + 2; + } + else if (pPage->GetPageKind() == PageKind::Notes) + { + nPos = pPage->GetPageNum() + 1; + } + } + + bool bNameOK; + std::vector aExchangeList; + std::vector aObjectBookmarkList = pDlg->GetList( 2 ); // objects + + /* if pBookmarkList is NULL, we insert selected pages, and/or selected + objects or everything. */ + if( !aBookmarkList.empty() || aObjectBookmarkList.empty() ) + { + /* To ensure that all page names are unique, we check the ones we + want to insert and insert them into a substitution list if + necessary. + bNameOK is sal_False if the user has canceled. */ + bNameOK = mpView->GetExchangeList( aExchangeList, aBookmarkList, 0 ); + + if( bNameOK ) + bOK = mpDoc->InsertBookmarkAsPage( aBookmarkList, &aExchangeList, + bLink, false/*bReplace*/, nPos, + false, nullptr, true, true, false ); + + aBookmarkList.clear(); + aExchangeList.clear(); + } + + // to ensure ... (see above) + bNameOK = mpView->GetExchangeList( aExchangeList, aObjectBookmarkList, 1 ); + + if( bNameOK ) + bOK = mpDoc->InsertBookmarkAsObject( aObjectBookmarkList, aExchangeList, + nullptr, nullptr, false ); + + if( pDlg->IsRemoveUnnecessaryMasterPages() ) + mpDoc->RemoveUnnecessaryMasterPages(); + } + + return bOK; +} + +void FuInsertFile::InsTextOrRTFinDrMode(SfxMedium* pMedium) +{ + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( pFact->CreateSdInsertPagesObjsDlg(mpViewShell->GetFrameWeld(), mpDoc, nullptr, aFile) ); + + mpDocSh->SetWaitCursor( false ); + + sal_uInt16 nRet = pDlg->Execute(); + mpDocSh->SetWaitCursor( true ); + + if( nRet != RET_OK ) + return; + + // selected file format: text, RTF or HTML (default is text) + EETextFormat nFormat = EETextFormat::Text; + + if( aFilterName.indexOf( "Rich") != -1 ) + nFormat = EETextFormat::Rtf; + else if( aFilterName.indexOf( "HTML" ) != -1 ) + nFormat = EETextFormat::Html; + + /* create our own outline since: + - it is possible that the document outliner is actually used in the + structuring mode + - the draw outliner of the drawing engine has to draw something in + between + - the global outliner could be used in SdPage::CreatePresObj */ + SdOutliner aOutliner( mpDoc, OutlinerMode::TextObject ); + + // set reference device + aOutliner.SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + SdPage* pPage = static_cast(mpViewShell)->GetActualPage(); + aLayoutName = pPage->GetLayoutName(); + sal_Int32 nIndex = aLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + aLayoutName = aLayoutName.copy(0, nIndex); + + aOutliner.SetPaperSize(pPage->GetSize()); + + SvStream* pStream = pMedium->GetInStream(); + assert(pStream && "No InStream!"); + pStream->Seek( 0 ); + + ErrCode nErr = aOutliner.Read( *pStream, pMedium->GetBaseURL(), nFormat, mpDocSh->GetHeaderAttributes() ); + + if (nErr || aOutliner.GetEditEngine().GetText().isEmpty()) + { + std::unique_ptr xErrorBox(Application::CreateMessageDialog(mpWindow->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + } + else + { + // is it a master page? + if (static_cast(mpViewShell)->GetEditMode() == EditMode::MasterPage && + !pPage->IsMasterPage()) + { + pPage = static_cast(&(pPage->TRG_GetMasterPage())); + } + + assert(pPage && "page not found"); + + // if editing is going on right now, let it flow into this text object + OutlinerView* pOutlinerView = mpView->GetTextEditOutlinerView(); + if( pOutlinerView ) + { + SdrObject* pObj = mpView->GetTextEditObject(); + if( pObj && + pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::TitleText && + aOutliner.GetParagraphCount() > 1 ) + { + // in title objects, only one paragraph is allowed + while ( aOutliner.GetParagraphCount() > 1 ) + { + Paragraph* pPara = aOutliner.GetParagraph( 0 ); + sal_uLong nLen = aOutliner.GetText( pPara ).getLength(); + aOutliner.QuickDelete( ESelection( 0, nLen, 1, 0 ) ); + aOutliner.QuickInsertLineBreak( ESelection( 0, nLen, 0, nLen ) ); + } + } + } + + std::optional pOPO = aOutliner.CreateParaObject(); + + if (pOutlinerView) + { + pOutlinerView->InsertText(*pOPO); + } + else + { + SdrRectObj* pTO = new SdrRectObj( + mpView->getSdrModelFromSdrView(), + SdrObjKind::Text); + pTO->SetOutlinerParaObject(std::move(pOPO)); + + const bool bUndo = mpView->IsUndoEnabled(); + if( bUndo ) + mpView->BegUndo(SdResId(STR_UNDO_INSERT_TEXTFRAME)); + pPage->InsertObject(pTO); + + /* can be bigger as the maximal allowed size: + limit object size if necessary */ + Size aSize(aOutliner.CalcTextSize()); + Size aMaxSize = mpDoc->GetMaxObjSize(); + aSize.setHeight( std::min(aSize.Height(), aMaxSize.Height()) ); + aSize.setWidth( std::min(aSize.Width(), aMaxSize.Width()) ); + aSize = mpWindow->LogicToPixel(aSize); + + // put it at the center of the window + Size aTemp(mpWindow->GetOutputSizePixel()); + Point aPos(aTemp.Width() / 2, aTemp.Height() / 2); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + aSize = mpWindow->PixelToLogic(aSize); + aPos = mpWindow->PixelToLogic(aPos); + pTO->SetLogicRect(::tools::Rectangle(aPos, aSize)); + + if (pDlg->IsLink()) + { + pTO->SetTextLink(aFile, aFilterName ); + } + + if( bUndo ) + { + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoInsertObject(*pTO)); + mpView->EndUndo(); + } + } + } +} + +void FuInsertFile::InsTextOrRTFinOlMode(SfxMedium* pMedium) +{ + // selected file format: text, RTF or HTML (default is text) + EETextFormat nFormat = EETextFormat::Text; + + if( aFilterName.indexOf( "Rich") != -1 ) + nFormat = EETextFormat::Rtf; + else if( aFilterName.indexOf( "HTML" ) != -1 ) + nFormat = EETextFormat::Html; + + ::Outliner& rDocliner = static_cast(mpView)->GetOutliner(); + + std::vector aSelList; + rDocliner.GetView(0)->CreateSelectionList(aSelList); + + Paragraph* pPara = aSelList.empty() ? nullptr : *(aSelList.begin()); + + // what should we insert? + while (pPara && !Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE)) + pPara = rDocliner.GetParent(pPara); + + sal_Int32 nTargetPos = rDocliner.GetAbsPos(pPara) + 1; + + // apply layout of predecessor page + sal_uInt16 nPage = 0; + pPara = rDocliner.GetParagraph( rDocliner.GetAbsPos( pPara ) - 1 ); + while (pPara) + { + sal_Int32 nPos = rDocliner.GetAbsPos( pPara ); + if ( Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) ) + nPage++; + pPara = rDocliner.GetParagraph( nPos - 1 ); + } + SdPage* pPage = mpDoc->GetSdPage(nPage, PageKind::Standard); + aLayoutName = pPage->GetLayoutName(); + sal_Int32 nIndex = aLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + aLayoutName = aLayoutName.copy(0, nIndex); + + /* create our own outline since: + - it is possible that the document outliner is actually used in the + structuring mode + - the draw outliner of the drawing engine has to draw something in + between + - the global outliner could be used in SdPage::CreatePresObj */ + ::Outliner aOutliner( &mpDoc->GetItemPool(), OutlinerMode::OutlineObject ); + aOutliner.SetStyleSheetPool(static_cast(mpDoc->GetStyleSheetPool())); + + // set reference device + aOutliner.SetRefDevice(SD_MOD()->GetVirtualRefDevice()); + aOutliner.SetPaperSize(Size(0x7fffffff, 0x7fffffff)); + + SvStream* pStream = pMedium->GetInStream(); + DBG_ASSERT( pStream, "No InStream!" ); + pStream->Seek( 0 ); + + ErrCode nErr = aOutliner.Read(*pStream, pMedium->GetBaseURL(), nFormat, mpDocSh->GetHeaderAttributes()); + + if (nErr || aOutliner.GetEditEngine().GetText().isEmpty()) + { + std::unique_ptr xErrorBox(Application::CreateMessageDialog(mpWindow->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, SdResId(STR_READ_DATA_ERROR))); + xErrorBox->run(); + } + else + { + sal_Int32 nParaCount = aOutliner.GetParagraphCount(); + + // for progress bar: number of level-0-paragraphs + sal_uInt16 nNewPages = 0; + pPara = aOutliner.GetParagraph( 0 ); + while (pPara) + { + sal_Int32 nPos = aOutliner.GetAbsPos( pPara ); + if( Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) ) + nNewPages++; + pPara = aOutliner.GetParagraph( ++nPos ); + } + + mpDocSh->SetWaitCursor( false ); + + std::optional pProgress( std::in_place, mpDocSh, SdResId(STR_CREATE_PAGES), nNewPages); + pProgress->SetState( 0, 100 ); + + nNewPages = 0; + + ViewShellId nViewShellId = mpViewShell ? mpViewShell->GetViewShellBase().GetViewShellId() : ViewShellId(-1); + rDocliner.GetUndoManager().EnterListAction( + SdResId(STR_UNDO_INSERT_FILE), OUString(), 0, nViewShellId ); + + sal_Int32 nSourcePos = 0; + SfxStyleSheet* pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + Paragraph* pSourcePara = aOutliner.GetParagraph( 0 ); + while (pSourcePara) + { + sal_Int32 nPos = aOutliner.GetAbsPos( pSourcePara ); + sal_Int16 nDepth = aOutliner.GetDepth( nPos ); + + // only take the last paragraph if it is filled + if (nSourcePos < nParaCount - 1 || + !aOutliner.GetText(pSourcePara).isEmpty()) + { + rDocliner.Insert( aOutliner.GetText(pSourcePara), nTargetPos, nDepth ); + OUString aStyleSheetName( pStyleSheet->GetName() ); + aStyleSheetName = aStyleSheetName.subView( 0, aStyleSheetName.getLength()-1 ) + + OUString::number( nDepth <= 0 ? 1 : nDepth+1 ); + SfxStyleSheetBasePool* pStylePool = mpDoc->GetStyleSheetPool(); + SfxStyleSheet* pOutlStyle = static_cast( pStylePool->Find( aStyleSheetName, pStyleSheet->GetFamily() ) ); + rDocliner.SetStyleSheet( nTargetPos, pOutlStyle ); + } + + if( Outliner::HasParaFlag( pSourcePara, ParaFlag::ISPAGE ) ) + { + nNewPages++; + pProgress->SetState( nNewPages ); + } + + pSourcePara = aOutliner.GetParagraph( ++nPos ); + nTargetPos++; + nSourcePos++; + } + + rDocliner.GetUndoManager().LeaveListAction(); + + pProgress.reset(); + + mpDocSh->SetWaitCursor( true ); + } +} + +bool FuInsertFile::InsSDDinOlMode(SfxMedium* pMedium) +{ + OutlineView* pOlView = static_cast(mpView); + + // transfer Outliner content to SdDrawDocument + pOlView->PrepareClose(); + + // read in like in the character mode + if (InsSDDinDrMode(pMedium)) + { + ::Outliner* pOutliner = pOlView->GetViewByWindow(mpWindow)->GetOutliner(); + + // cut notification links temporarily + Link aOldParagraphInsertedHdl = pOutliner->GetParaInsertedHdl(); + pOutliner->SetParaInsertedHdl( Link()); + Link aOldParagraphRemovingHdl = pOutliner->GetParaRemovingHdl(); + pOutliner->SetParaRemovingHdl( Link()); + Link aOldDepthChangedHdl = pOutliner->GetDepthChangedHdl(); + pOutliner->SetDepthChangedHdl( Link<::Outliner::DepthChangeHdlParam,void>()); + Link<::Outliner*,void> aOldBeginMovingHdl = pOutliner->GetBeginMovingHdl(); + pOutliner->SetBeginMovingHdl( Link<::Outliner*,void>()); + Link<::Outliner*,void> aOldEndMovingHdl = pOutliner->GetEndMovingHdl(); + pOutliner->SetEndMovingHdl( Link<::Outliner*,void>()); + + Link aOldStatusEventHdl = pOutliner->GetStatusEventHdl(); + pOutliner->SetStatusEventHdl(Link()); + + pOutliner->Clear(); + pOlView->FillOutliner(); + + // set links again + pOutliner->SetParaInsertedHdl(aOldParagraphInsertedHdl); + pOutliner->SetParaRemovingHdl(aOldParagraphRemovingHdl); + pOutliner->SetDepthChangedHdl(aOldDepthChangedHdl); + pOutliner->SetBeginMovingHdl(aOldBeginMovingHdl); + pOutliner->SetEndMovingHdl(aOldEndMovingHdl); + pOutliner->SetStatusEventHdl(aOldStatusEventHdl); + + return true; + } + else + return false; +} + +void FuInsertFile::GetSupportedFilterVector( ::std::vector< OUString >& rFilterVector ) +{ + SfxFilterMatcher& rMatcher = SfxGetpApp()->GetFilterMatcher(); + std::shared_ptr pSearchFilter; + + rFilterVector.clear(); + + if( ( pSearchFilter = rMatcher.GetFilter4Mime( "text/plain" )) != nullptr ) + rFilterVector.push_back( pSearchFilter->GetMimeType() ); + + if( ( pSearchFilter = rMatcher.GetFilter4Mime( "application/rtf" ) ) != nullptr ) + rFilterVector.push_back( pSearchFilter->GetMimeType() ); + + if( ( pSearchFilter = rMatcher.GetFilter4Mime( "text/html" ) ) != nullptr ) + rFilterVector.push_back( pSearchFilter->GetMimeType() ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuline.cxx b/sd/source/ui/func/fuline.cxx new file mode 100644 index 000000000..da9cc795f --- /dev/null +++ b/sd/source/ui/func/fuline.cxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuLine::FuLine ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuLine::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuLine( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuLine::DoExecute( SfxRequest& rReq ) +{ + rReq.Ignore(); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if (pArgs) + return; + + const SdrObject* pObj = nullptr; + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + bool bHasMarked = mpView->AreObjectsMarked(); + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + VclPtr pDlg( pFact->CreateSvxLineTabDialog(mpViewShell->GetFrameWeld(), &aNewAttr, mpDoc, pObj, bHasMarked) ); + + pDlg->StartExecuteAsync([pDlg, this](sal_Int32 nResult){ + if (nResult == RET_OK) + { + mpView->SetAttributes (*(pDlg->GetOutputItemSet ())); + + // some attributes are changed, we have to update the listboxes in the objectbars + static const sal_uInt16 SidArray[] = { + SID_ATTR_LINE_STYLE, // ( SID_SVX_START + 169 ) + SID_ATTR_LINE_DASH, // ( SID_SVX_START + 170 ) + SID_ATTR_LINE_WIDTH, // ( SID_SVX_START + 171 ) + SID_ATTR_LINE_COLOR, // ( SID_SVX_START + 172 ) + SID_ATTR_LINE_START, // ( SID_SVX_START + 173 ) + SID_ATTR_LINE_END, // ( SID_SVX_START + 174 ) + SID_ATTR_LINE_TRANSPARENCE, // (SID_SVX_START+1107) + SID_ATTR_LINE_JOINT, // (SID_SVX_START+1110) + SID_ATTR_LINE_CAP, // (SID_SVX_START+1111) + 0 }; + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + } + + // deferred until the dialog ends + mpViewShell->Cancel(); + + pDlg->disposeOnce(); + }); +} + +void FuLine::Activate() +{ +} + +void FuLine::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fulinend.cxx b/sd/source/ui/func/fulinend.cxx new file mode 100644 index 000000000..34fe63161 --- /dev/null +++ b/sd/source/ui/func/fulinend.cxx @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuLineEnd::FuLineEnd(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuLineEnd::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuLineEnd( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuLineEnd::DoExecute( SfxRequest& ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() != 1 ) + return; + + const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + const SdrObject* pNewObj; + SdrObjectUniquePtr pConvPolyObj; + + if( dynamic_cast< const SdrPathObj *>( pObj ) != nullptr ) + { + pNewObj = pObj; + } + else + { + SdrObjTransformInfoRec aInfoRec; + pObj->TakeObjInfo( aInfoRec ); + + if( aInfoRec.bCanConvToPath && + pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() != SdrObjKind::Group ) + // bCanConvToPath is sal_True for group objects, + // but it crashes on ConvertToPathObj()! + { + pConvPolyObj = pObj->ConvertToPolyObj( true, false ); + pNewObj = pConvPolyObj.get(); + + if( !pNewObj || dynamic_cast< const SdrPathObj *>( pNewObj ) == nullptr ) + return; // Cancel, additional security, but it does not help + // for group objects + } + else return; // Cancel + } + + const ::basegfx::B2DPolyPolygon aPolyPolygon = static_cast(pNewObj)->GetPathPoly(); + + // Delete the created poly-object + pConvPolyObj.reset(); + + XLineEndListRef pLineEndList = mpDoc->GetLineEndList(); + + OUString aNewName( SdResId( STR_LINEEND ) ); + OUString aDesc( SdResId( STR_DESC_LINEEND ) ); + OUString aName; + + ::tools::Long nCount = pLineEndList->Count(); + ::tools::Long j = 1; + bool bDifferent = false; + + while( !bDifferent ) + { + aName = aNewName + " " + OUString::number(j++); + bDifferent = true; + for( ::tools::Long i = 0; i < nCount && bDifferent; i++ ) + { + if( aName == pLineEndList->GetLineEnd( i )->GetName() ) + bDifferent = false; + } + } + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( pFact->CreateSvxNameDialog( nullptr, aName, aDesc ) ); + + pDlg->SetEditHelpId( HID_SD_NAMEDIALOG_LINEEND ); + + if( pDlg->Execute() != RET_OK ) + return; + + pDlg->GetName( aName ); + bDifferent = true; + + for( ::tools::Long i = 0; i < nCount && bDifferent; i++ ) + { + if( aName == pLineEndList->GetLineEnd( i )->GetName() ) + bDifferent = false; + } + + if( bDifferent ) + { + pLineEndList->Insert(std::make_unique(aPolyPolygon, aName)); + } + else + { + std::unique_ptr xWarn(Application::CreateMessageDialog(mpWindow ? mpWindow->GetFrameWeld() : nullptr, + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + } +} + +void FuLineEnd::Activate() +{ +} + +void FuLineEnd::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fulink.cxx b/sd/source/ui/func/fulink.cxx new file mode 100644 index 000000000..8a6d726de --- /dev/null +++ b/sd/source/ui/func/fulink.cxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include + +#include +#include +#include + +class SfxRequest; + +namespace sd { + + +FuLink::FuLink ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuLink::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuLink( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuLink::DoExecute( SfxRequest& ) +{ + sfx2::LinkManager* pLinkManager = mpDoc->GetLinkManager(); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateLinksDialog(mpViewShell->GetFrameWeld(), pLinkManager)); + pDlg->Execute(); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_MANAGE_LINKS ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fumeasur.cxx b/sd/source/ui/func/fumeasur.cxx new file mode 100644 index 000000000..27afd0f7a --- /dev/null +++ b/sd/source/ui/func/fumeasur.cxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuMeasureDlg::FuMeasureDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuMeasureDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuMeasureDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuMeasureDlg::DoExecute( SfxRequest& rReq ) +{ + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSfxDialog(rReq.GetFrameWeld(), aNewAttr, mpView, RID_SVXPAGE_MEASURE)); + + if( pDlg->Execute() == RET_OK ) + { + rReq.Done( *pDlg->GetOutputItemSet() ); + pArgs = rReq.GetArgs(); + } + } + + if( pArgs ) + mpView->SetAttributes( *pArgs ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fumorph.cxx b/sd/source/ui/func/fumorph.cxx new file mode 100644 index 000000000..c2f94b440 --- /dev/null +++ b/sd/source/ui/func/fumorph.cxx @@ -0,0 +1,508 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include + +using namespace com::sun::star; + +namespace sd { + +FuMorph::FuMorph ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuMorph::Create( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq +) +{ + rtl::Reference xFunc( new FuMorph( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuMorph::DoExecute( SfxRequest& ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if(rMarkList.GetMarkCount() != 2) + return; + + // create clones + SdrObject* pObj1 = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SdrObject* pObj2 = rMarkList.GetMark(1)->GetMarkedSdrObj(); + SdrObject* pCloneObj1(pObj1->CloneSdrObject(pObj1->getSdrModelFromSdrObject())); + SdrObject* pCloneObj2(pObj2->CloneSdrObject(pObj2->getSdrModelFromSdrObject())); + + // delete text at clone, otherwise we do not get a correct PathObj + pCloneObj1->SetOutlinerParaObject(std::nullopt); + pCloneObj2->SetOutlinerParaObject(std::nullopt); + + // create path objects + SdrObjectUniquePtr pPolyObj1 = pCloneObj1->ConvertToPolyObj(false, false); + SdrObjectUniquePtr pPolyObj2 = pCloneObj2->ConvertToPolyObj(false, false); + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( pFact->CreateMorphDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, pObj1, pObj2) ); + if(pPolyObj1 && pPolyObj2 && (pDlg->Execute() == RET_OK)) + { + B2DPolyPolygonList_impl aPolyPolyList; + ::basegfx::B2DPolyPolygon aPolyPoly1; + ::basegfx::B2DPolyPolygon aPolyPoly2; + + pDlg->SaveSettings(); + + // #i48168# Not always is the pPolyObj1/pPolyObj2 a SdrPathObj, it may also be a group object + // containing SdrPathObjs. To get the polygons, I add two iters here + SdrObjListIter aIter1(*pPolyObj1); + SdrObjListIter aIter2(*pPolyObj2); + + while(aIter1.IsMore()) + { + SdrObject* pObj = aIter1.Next(); + if(auto pPathObj = dynamic_cast< SdrPathObj *>( pObj )) + aPolyPoly1.append(pPathObj->GetPathPoly()); + } + + while(aIter2.IsMore()) + { + SdrObject* pObj = aIter2.Next(); + if(auto pPathObj = dynamic_cast< SdrPathObj *>( pObj )) + aPolyPoly2.append(pPathObj->GetPathPoly()); + } + + // perform morphing + if(aPolyPoly1.count() && aPolyPoly2.count()) + { + aPolyPoly1 = ::basegfx::utils::correctOrientations(aPolyPoly1); + aPolyPoly1.removeDoublePoints(); + ::basegfx::B2VectorOrientation eIsClockwise1(::basegfx::utils::getOrientation(aPolyPoly1.getB2DPolygon(0))); + + aPolyPoly2 = ::basegfx::utils::correctOrientations(aPolyPoly2); + aPolyPoly2.removeDoublePoints(); + ::basegfx::B2VectorOrientation eIsClockwise2(::basegfx::utils::getOrientation(aPolyPoly2.getB2DPolygon(0))); + + // set same orientation + if(eIsClockwise1 != eIsClockwise2) + aPolyPoly2.flip(); + + // force same poly count + if(aPolyPoly1.count() < aPolyPoly2.count()) + ImpAddPolys(aPolyPoly1, aPolyPoly2); + else if(aPolyPoly2.count() < aPolyPoly1.count()) + ImpAddPolys(aPolyPoly2, aPolyPoly1); + + // use orientation flag from dialog + if(!pDlg->IsOrientationFade()) + aPolyPoly2.flip(); + + // force same point counts + for( sal_uInt32 a(0); a < aPolyPoly1.count(); a++ ) + { + ::basegfx::B2DPolygon aSub1(aPolyPoly1.getB2DPolygon(a)); + ::basegfx::B2DPolygon aSub2(aPolyPoly2.getB2DPolygon(a)); + + if(aSub1.count() < aSub2.count()) + ImpEqualizePolyPointCount(aSub1, aSub2); + else if(aSub2.count() < aSub1.count()) + ImpEqualizePolyPointCount(aSub2, aSub1); + + aPolyPoly1.setB2DPolygon(a, aSub1); + aPolyPoly2.setB2DPolygon(a, aSub2); + } + + ImpMorphPolygons(aPolyPoly1, aPolyPoly2, pDlg->GetFadeSteps(), aPolyPolyList); + + OUString aString = mpView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_UNDO_MORPHING); + + mpView->BegUndo(aString); + ImpInsertPolygons(aPolyPolyList, pDlg->IsAttributeFade(), pObj1, pObj2); + mpView->EndUndo(); + } + } + SdrObject::Free( pCloneObj1 ); + SdrObject::Free( pCloneObj2 ); +} + +static ::basegfx::B2DPolygon ImpGetExpandedPolygon( + const ::basegfx::B2DPolygon& rCandidate, + sal_uInt32 nNum +) +{ + if(rCandidate.count() && nNum && rCandidate.count() != nNum) + { + // length of step in dest poly + ::basegfx::B2DPolygon aRetval; + const double fStep(::basegfx::utils::getLength(rCandidate) / static_cast(rCandidate.isClosed() ? nNum : nNum - 1)); + double fDestPos(0.0); + double fSrcPos(0.0); + sal_uInt32 nSrcPos(0); + sal_uInt32 nSrcPosNext((nSrcPos + 1 == rCandidate.count()) ? 0 : nSrcPos + 1); + double fNextSrcLen(::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength()); + + for(sal_uInt32 b(0); b < nNum; b++) + { + // calc fDestPos in source + while(fSrcPos + fNextSrcLen < fDestPos) + { + fSrcPos += fNextSrcLen; + nSrcPos++; + nSrcPosNext = (nSrcPos + 1 == rCandidate.count()) ? 0 : nSrcPos + 1; + fNextSrcLen = ::basegfx::B2DVector(rCandidate.getB2DPoint(nSrcPos) - rCandidate.getB2DPoint(nSrcPosNext)).getLength(); + } + + // fDestPos is between fSrcPos and (fSrcPos + fNextSrcLen) + const double fLenA((fDestPos - fSrcPos) / fNextSrcLen); + const ::basegfx::B2DPoint aOld1(rCandidate.getB2DPoint(nSrcPos)); + const ::basegfx::B2DPoint aOld2(rCandidate.getB2DPoint(nSrcPosNext)); + ::basegfx::B2DPoint aNewPoint(basegfx::interpolate(aOld1, aOld2, fLenA)); + aRetval.append(aNewPoint); + + // next step + fDestPos += fStep; + } + + if(aRetval.count() >= 3) + { + aRetval.setClosed(rCandidate.isClosed()); + } + + return aRetval; + } + else + { + return rCandidate; + } +} + +/** + * make the point count of the polygons equal in adding points + */ +void FuMorph::ImpEqualizePolyPointCount( + ::basegfx::B2DPolygon& rSmall, + const ::basegfx::B2DPolygon& rBig +) +{ + // create poly with equal point count + const sal_uInt32 nCnt(rBig.count()); + ::basegfx::B2DPolygon aPoly1(ImpGetExpandedPolygon(rSmall, nCnt)); + + // create transformation for rBig to do the compare + const ::basegfx::B2DRange aSrcSize(::basegfx::utils::getRange(rBig)); + const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter()); + const ::basegfx::B2DRange aDstSize(::basegfx::utils::getRange(rSmall)); + const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter()); + + basegfx::B2DHomMatrix aTrans(basegfx::utils::createTranslateB2DHomMatrix(-aSrcPos.getX(), -aSrcPos.getY())); + aTrans.scale(aDstSize.getWidth() / aSrcSize.getWidth(), aDstSize.getHeight() / aSrcSize.getHeight()); + aTrans.translate(aDstPos.getX(), aDstPos.getY()); + + // transpose points to have smooth linear blending + ::basegfx::B2DPolygon aPoly2; + aPoly2.append(::basegfx::B2DPoint(), nCnt); + sal_uInt32 nInd(ImpGetNearestIndex(aPoly1, aTrans * rBig.getB2DPoint(0))); + + for(sal_uInt32 a(0); a < nCnt; a++) + { + aPoly2.setB2DPoint((a + nCnt - nInd) % nCnt, aPoly1.getB2DPoint(a)); + } + + aPoly2.setClosed(rBig.isClosed()); + rSmall = aPoly2; +} + +sal_uInt32 FuMorph::ImpGetNearestIndex( + const ::basegfx::B2DPolygon& rPoly, + const ::basegfx::B2DPoint& rPos +) +{ + double fMinDist = 0.0; + sal_uInt32 nActInd = 0; + + for(sal_uInt32 a(0); a < rPoly.count(); a++) + { + double fNewDist(::basegfx::B2DVector(rPoly.getB2DPoint(a) - rPos).getLength()); + + if(!a || fNewDist < fMinDist) + { + fMinDist = fNewDist; + nActInd = a; + } + } + + return nActInd; +} + +/** + * add to a point reduced polys until count is same + */ +void FuMorph::ImpAddPolys( + ::basegfx::B2DPolyPolygon& rSmaller, + const ::basegfx::B2DPolyPolygon& rBigger +) +{ + while(rSmaller.count() < rBigger.count()) + { + const ::basegfx::B2DPolygon& aToBeCopied(rBigger.getB2DPolygon(rSmaller.count())); + const ::basegfx::B2DRange aToBeCopiedPolySize(::basegfx::utils::getRange(aToBeCopied)); + ::basegfx::B2DPoint aNewPoint(aToBeCopiedPolySize.getCenter()); + ::basegfx::B2DPolygon aNewPoly; + + const ::basegfx::B2DRange aSrcSize(::basegfx::utils::getRange(rBigger.getB2DPolygon(0))); + const ::basegfx::B2DPoint aSrcPos(aSrcSize.getCenter()); + const ::basegfx::B2DRange aDstSize(::basegfx::utils::getRange(rSmaller.getB2DPolygon(0))); + const ::basegfx::B2DPoint aDstPos(aDstSize.getCenter()); + aNewPoint = aNewPoint - aSrcPos + aDstPos; + + for(sal_uInt32 a(0); a < aToBeCopied.count(); a++) + { + aNewPoly.append(aNewPoint); + } + + rSmaller.append(aNewPoly); + } +} + +/** + * create group object with morphed polygons + */ +void FuMorph::ImpInsertPolygons( + B2DPolyPolygonList_impl& rPolyPolyList3D, + bool bAttributeFade, + const SdrObject* pObj1, + const SdrObject* pObj2 +) +{ + Color aStartFillCol; + Color aEndFillCol; + Color aStartLineCol; + Color aEndLineCol; + ::tools::Long nStartLineWidth = 0; + ::tools::Long nEndLineWidth = 0; + SdrPageView* pPageView = mpView->GetSdrPageView(); + SfxItemPool & rPool = pObj1->GetObjectItemPool(); + SfxItemSetFixed aSet1( rPool ); + SfxItemSet aSet2( aSet1 ); + bool bLineColor = false; + bool bFillColor = false; + bool bLineWidth = false; + bool bIgnoreLine = false; + bool bIgnoreFill = false; + + aSet1.Put(pObj1->GetMergedItemSet()); + aSet2.Put(pObj2->GetMergedItemSet()); + + const drawing::LineStyle eLineStyle1 = aSet1.Get(XATTR_LINESTYLE).GetValue(); + const drawing::LineStyle eLineStyle2 = aSet2.Get(XATTR_LINESTYLE).GetValue(); + const drawing::FillStyle eFillStyle1 = aSet1.Get(XATTR_FILLSTYLE).GetValue(); + const drawing::FillStyle eFillStyle2 = aSet2.Get(XATTR_FILLSTYLE).GetValue(); + + if ( bAttributeFade ) + { + if ( ( eLineStyle1 != drawing::LineStyle_NONE ) && ( eLineStyle2 != drawing::LineStyle_NONE ) ) + { + bLineWidth = bLineColor = true; + + aStartLineCol = aSet1.Get(XATTR_LINECOLOR).GetColorValue(); + aEndLineCol = aSet2.Get(XATTR_LINECOLOR).GetColorValue(); + + nStartLineWidth = aSet1.Get(XATTR_LINEWIDTH).GetValue(); + nEndLineWidth = aSet2.Get(XATTR_LINEWIDTH).GetValue(); + } + else if ( ( eLineStyle1 == drawing::LineStyle_NONE ) && ( eLineStyle2 == drawing::LineStyle_NONE ) ) + bIgnoreLine = true; + + if ( ( eFillStyle1 == drawing::FillStyle_SOLID ) && ( eFillStyle2 == drawing::FillStyle_SOLID ) ) + { + bFillColor = true; + aStartFillCol = aSet1.Get(XATTR_FILLCOLOR).GetColorValue(); + aEndFillCol = aSet2.Get(XATTR_FILLCOLOR).GetColorValue(); + } + else if ( ( eFillStyle1 == drawing::FillStyle_NONE ) && ( eFillStyle2 == drawing::FillStyle_NONE ) ) + bIgnoreFill = true; + } + + if ( !pPageView ) + return; + + SfxItemSet aSet( aSet1 ); + std::unique_ptr xObjGroup(new SdrObjGroup(mpView->getSdrModelFromSdrView())); + SdrObjList* pObjList = xObjGroup->GetSubList(); + const size_t nCount = rPolyPolyList3D.size(); + const double fStep = 1. / ( nCount + 1 ); + const double fDelta = nEndLineWidth - nStartLineWidth; + double fFactor = fStep; + + aSet.Put( XLineStyleItem( drawing::LineStyle_SOLID ) ); + aSet.Put( XFillStyleItem( drawing::FillStyle_SOLID ) ); + + for ( size_t i = 0; i < nCount; i++, fFactor += fStep ) + { + const ::basegfx::B2DPolyPolygon& rPolyPoly3D = rPolyPolyList3D[ i ]; + SdrPathObj* pNewObj = new SdrPathObj( + mpView->getSdrModelFromSdrView(), + SdrObjKind::Polygon, + rPolyPoly3D); + + // line color + if ( bLineColor ) + { + const basegfx::BColor aLineColor(basegfx::interpolate(aStartLineCol.getBColor(), aEndLineCol.getBColor(), fFactor)); + aSet.Put( XLineColorItem( "", Color(aLineColor))); + } + else if ( bIgnoreLine ) + aSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + + // fill color + if ( bFillColor ) + { + const basegfx::BColor aFillColor(basegfx::interpolate(aStartFillCol.getBColor(), aEndFillCol.getBColor(), fFactor)); + aSet.Put( XFillColorItem( "", Color(aFillColor))); + } + else if ( bIgnoreFill ) + aSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); + + // line width + if ( bLineWidth ) + aSet.Put( XLineWidthItem( nStartLineWidth + static_cast<::tools::Long>( fFactor * fDelta + 0.5 ) ) ); + + pNewObj->SetMergedItemSetAndBroadcast(aSet); + + pObjList->InsertObject( pNewObj ); + } + + if ( nCount ) + { + pObjList->InsertObject( + pObj1->CloneSdrObject(pObj1->getSdrModelFromSdrObject()), + 0 ); + pObjList->InsertObject( + pObj2->CloneSdrObject(pObj2->getSdrModelFromSdrObject()) ); + + mpView->DeleteMarked(); + mpView->InsertObjectAtView(xObjGroup.release(), *pPageView, SdrInsertFlags:: SETDEFLAYER); + } +} + +/** + * create single morphed PolyPolygon + */ +::basegfx::B2DPolyPolygon FuMorph::ImpCreateMorphedPolygon( + const ::basegfx::B2DPolyPolygon& rPolyPolyStart, + const ::basegfx::B2DPolyPolygon& rPolyPolyEnd, + double fMorphingFactor +) +{ + ::basegfx::B2DPolyPolygon aNewPolyPolygon; + const double fFactor = 1.0 - fMorphingFactor; + + for(sal_uInt32 a(0); a < rPolyPolyStart.count(); a++) + { + const ::basegfx::B2DPolygon& aPolyStart(rPolyPolyStart.getB2DPolygon(a)); + const ::basegfx::B2DPolygon& aPolyEnd(rPolyPolyEnd.getB2DPolygon(a)); + const sal_uInt32 nCount(aPolyStart.count()); + ::basegfx::B2DPolygon aNewPolygon; + + for(sal_uInt32 b(0); b < nCount; b++) + { + const ::basegfx::B2DPoint& aPtStart(aPolyStart.getB2DPoint(b)); + const ::basegfx::B2DPoint& aPtEnd(aPolyEnd.getB2DPoint(b)); + aNewPolygon.append(aPtEnd + ((aPtStart - aPtEnd) * fFactor)); + } + + aNewPolygon.setClosed(aPolyStart.isClosed() && aPolyEnd.isClosed()); + aNewPolyPolygon.append(aNewPolygon); + } + + return aNewPolyPolygon; +} + +/** + * create morphed PolyPolygons + */ +void FuMorph::ImpMorphPolygons( + const ::basegfx::B2DPolyPolygon& rPolyPoly1, + const ::basegfx::B2DPolyPolygon& rPolyPoly2, + const sal_uInt16 nSteps, + B2DPolyPolygonList_impl& rPolyPolyList3D +) +{ + if(!nSteps) + return; + + const ::basegfx::B2DRange aStartPolySize(::basegfx::utils::getRange(rPolyPoly1)); + const ::basegfx::B2DPoint aStartCenter(aStartPolySize.getCenter()); + const ::basegfx::B2DRange aEndPolySize(::basegfx::utils::getRange(rPolyPoly2)); + const ::basegfx::B2DPoint aEndCenter(aEndPolySize.getCenter()); + const ::basegfx::B2DPoint aDelta(aEndCenter - aStartCenter); + const double fFactor(1.0 / (nSteps + 1)); + double fValue(0.0); + + for(sal_uInt16 i(0); i < nSteps; i++) + { + fValue += fFactor; + ::basegfx::B2DPolyPolygon aNewPolyPoly2D = ImpCreateMorphedPolygon(rPolyPoly1, rPolyPoly2, fValue); + + const ::basegfx::B2DRange aNewPolySize(::basegfx::utils::getRange(aNewPolyPoly2D)); + const ::basegfx::B2DPoint aNewS(aNewPolySize.getCenter()); + const ::basegfx::B2DPoint aRealS(aStartCenter + (aDelta * fValue)); + const ::basegfx::B2DPoint aDiff(aRealS - aNewS); + + aNewPolyPoly2D.transform(basegfx::utils::createTranslateB2DHomMatrix(aDiff)); + rPolyPolyList3D.push_back( std::move(aNewPolyPoly2D) ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/funavig.cxx b/sd/source/ui/func/funavig.cxx new file mode 100644 index 000000000..bd0cdb7c3 --- /dev/null +++ b/sd/source/ui/func/funavig.cxx @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuNavigation::FuNavigation ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuNavigation::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuNavigation( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuNavigation::DoExecute( SfxRequest& rReq ) +{ + bool bSlideShow = SlideShow::IsRunning( mpViewShell->GetViewShellBase() ); + + switch ( rReq.GetSlot() ) + { + case SID_GO_TO_FIRST_PAGE: + { + if (!mpView->IsTextEdit() + && dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr + && !bSlideShow) + { + // jump to first page + static_cast(mpViewShell)->SwitchPage(0); + } + } + break; + + case SID_GO_TO_PREVIOUS_PAGE: + { + if( !bSlideShow) + if( auto pDrawViewShell = dynamic_cast( mpViewShell ) ) + { + // With no modifier pressed we move to the previous + // slide. + mpView->SdrEndTextEdit(); + + // Previous page. + SdPage* pPage = pDrawViewShell->GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + + if (nSdPage > 0) + { + // Switch the page and send events regarding + // deactivation the old page and activating the new + // one. + TabControl& rPageTabControl = + static_cast(mpViewShell) + ->GetPageTabControl(); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendDeactivatePageEvent (); + static_cast(mpViewShell)->SwitchPage(nSdPage - 1); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendActivatePageEvent (); + } + } + } + break; + + case SID_GO_TO_NEXT_PAGE: + { + if( !bSlideShow) + if( auto pDrawViewShell = dynamic_cast( mpViewShell )) + { + // With no modifier pressed we move to the next slide. + mpView->SdrEndTextEdit(); + + // Next page. + SdPage* pPage = pDrawViewShell->GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + + if (nSdPage < mpDoc->GetSdPageCount(pPage->GetPageKind()) - 1) + { + // Switch the page and send events regarding + // deactivation the old page and activating the new + // one. + TabControl& rPageTabControl = + static_cast(mpViewShell)->GetPageTabControl(); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendDeactivatePageEvent (); + static_cast(mpViewShell)->SwitchPage(nSdPage + 1); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendActivatePageEvent (); + } + } + } + break; + + case SID_GO_TO_LAST_PAGE: + { + if (!mpView->IsTextEdit() && !bSlideShow) + if (auto pDrawViewShell = dynamic_cast( mpViewShell )) + { + // jump to last page + SdPage* pPage = pDrawViewShell->GetActualPage(); + pDrawViewShell->SwitchPage(mpDoc->GetSdPageCount( + pPage->GetPageKind()) - 1); + } + } + break; + } + // Refresh toolbar icons + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_GO_TO_FIRST_PAGE); + rBindings.Invalidate(SID_GO_TO_PREVIOUS_PAGE); + rBindings.Invalidate(SID_GO_TO_NEXT_PAGE); + rBindings.Invalidate(SID_GO_TO_LAST_PAGE); +} + + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuoaprms.cxx b/sd/source/ui/func/fuoaprms.cxx new file mode 100644 index 000000000..0feaabfb4 --- /dev/null +++ b/sd/source/ui/func/fuoaprms.cxx @@ -0,0 +1,800 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace sd { + + +#define ATTR_MISSING 0 ///< Attribute missing +#define ATTR_MIXED 1 ///< Attribute ambiguous (on multi-selection) +#define ATTR_SET 2 ///< Attribute unique + +FuObjectAnimationParameters::FuObjectAnimationParameters ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuObjectAnimationParameters::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuObjectAnimationParameters( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuObjectAnimationParameters::DoExecute( SfxRequest& rReq ) +{ + SfxUndoManager* pUndoMgr = mpViewShell->GetViewFrame()->GetObjectShell()->GetUndoManager(); + + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + short nAnimationSet = ATTR_MISSING; + short nEffectSet = ATTR_MISSING; + short nTextEffectSet = ATTR_MISSING; + short nSpeedSet = ATTR_MISSING; + short nFadeColorSet = ATTR_MISSING; + short nFadeOutSet = ATTR_MISSING; + short nInvisibleSet = ATTR_MISSING; + short nSoundOnSet = ATTR_MISSING; + short nSoundFileSet = ATTR_MISSING; + short nPlayFullSet = ATTR_MISSING; + short nClickActionSet = ATTR_MISSING; + short nBookmarkSet = ATTR_MISSING; + + short nSecondEffectSet = ATTR_MISSING; + short nSecondSpeedSet = ATTR_MISSING; + short nSecondSoundOnSet = ATTR_MISSING; + short nSecondPlayFullSet = ATTR_MISSING; + + // defaults (for Undo-Action) + presentation::AnimationEffect eEffect = presentation::AnimationEffect_NONE; + presentation::AnimationEffect eTextEffect = presentation::AnimationEffect_NONE; + presentation::AnimationSpeed eSpeed = presentation::AnimationSpeed_MEDIUM; + bool bActive = false; + bool bFadeOut = false; + Color aFadeColor = COL_LIGHTGRAY; + bool bInvisible = false; + bool bSoundOn = false; + OUString aSound; + bool bPlayFull = false; + presentation::ClickAction eClickAction = presentation::ClickAction_NONE; + OUString aBookmark; + + presentation::AnimationEffect eSecondEffect = presentation::AnimationEffect_NONE; + presentation::AnimationSpeed eSecondSpeed = presentation::AnimationSpeed_MEDIUM; + bool bSecondSoundOn = false; + bool bSecondPlayFull = false; + + SdAnimationInfo* pInfo; + SdrMark* pMark; + + // inspect first object + pMark = rMarkList.GetMark(0); + pInfo = SdDrawDocument::GetAnimationInfo(pMark->GetMarkedSdrObj()); + if( pInfo ) + { + bActive = pInfo->mbActive; + nAnimationSet = ATTR_SET; + + eEffect = pInfo->meEffect; + nEffectSet = ATTR_SET; + + eTextEffect = pInfo->meTextEffect; + nTextEffectSet = ATTR_SET; + + eSpeed = pInfo->meSpeed; + nSpeedSet = ATTR_SET; + + bFadeOut = pInfo->mbDimPrevious; + nFadeOutSet = ATTR_SET; + + aFadeColor = pInfo->maDimColor; + nFadeColorSet = ATTR_SET; + + bInvisible = pInfo->mbDimHide; + nInvisibleSet = ATTR_SET; + + bSoundOn = pInfo->mbSoundOn; + nSoundOnSet = ATTR_SET; + + aSound = pInfo->maSoundFile; + nSoundFileSet = ATTR_SET; + + bPlayFull = pInfo->mbPlayFull; + nPlayFullSet = ATTR_SET; + + eClickAction = pInfo->meClickAction; + nClickActionSet = ATTR_SET; + + aBookmark = pInfo->GetBookmark(); + nBookmarkSet = ATTR_SET; + + eSecondEffect = pInfo->meSecondEffect; + nSecondEffectSet = ATTR_SET; + + eSecondSpeed = pInfo->meSecondSpeed; + nSecondSpeedSet = ATTR_SET; + + bSecondSoundOn = pInfo->mbSecondSoundOn; + nSecondSoundOnSet = ATTR_SET; + + bSecondPlayFull = pInfo->mbSecondPlayFull; + nSecondPlayFullSet = ATTR_SET; + } + + // if necessary, inspect more objects + for( size_t nObject = 1; nObject < nCount; ++nObject ) + { + pMark = rMarkList.GetMark( nObject ); + SdrObject* pObject = pMark->GetMarkedSdrObj(); + pInfo = SdDrawDocument::GetAnimationInfo(pObject); + if( pInfo ) + { + if( bActive != pInfo->mbActive ) + nAnimationSet = ATTR_MIXED; + + if( eEffect != pInfo->meEffect ) + nEffectSet = ATTR_MIXED; + + if( eTextEffect != pInfo->meTextEffect ) + nTextEffectSet = ATTR_MIXED; + + if( eSpeed != pInfo->meSpeed ) + nSpeedSet = ATTR_MIXED; + + if( bFadeOut != pInfo->mbDimPrevious ) + nFadeOutSet = ATTR_MIXED; + + if( aFadeColor != pInfo->maDimColor ) + nFadeColorSet = ATTR_MIXED; + + if( bInvisible != pInfo->mbDimHide ) + nInvisibleSet = ATTR_MIXED; + + if( bSoundOn != pInfo->mbSoundOn ) + nSoundOnSet = ATTR_MIXED; + + if( aSound != pInfo->maSoundFile ) + nSoundFileSet = ATTR_MIXED; + + if( bPlayFull != pInfo->mbPlayFull ) + nPlayFullSet = ATTR_MIXED; + + if( eClickAction != pInfo->meClickAction ) + nClickActionSet = ATTR_MIXED; + + if( aBookmark != pInfo->GetBookmark() ) + nBookmarkSet = ATTR_MIXED; + + if( eSecondEffect != pInfo->meSecondEffect ) + nSecondEffectSet = ATTR_MIXED; + + if( eSecondSpeed != pInfo->meSecondSpeed ) + nSecondSpeedSet = ATTR_MIXED; + + if( bSecondSoundOn != pInfo->mbSecondSoundOn ) + nSecondSoundOnSet = ATTR_MIXED; + + if( bSecondPlayFull != pInfo->mbSecondPlayFull ) + nSecondPlayFullSet = ATTR_MIXED; + } + else + { + if (nAnimationSet == ATTR_SET && bActive) + nAnimationSet = ATTR_MIXED; + + if (nEffectSet == ATTR_SET && eEffect != presentation::AnimationEffect_NONE) + nEffectSet = ATTR_MIXED; + + if (nTextEffectSet == ATTR_SET && eTextEffect != presentation::AnimationEffect_NONE) + nTextEffectSet = ATTR_MIXED; + + if (nSpeedSet == ATTR_SET) + nSpeedSet = ATTR_MIXED; + + if (nFadeOutSet == ATTR_SET && bFadeOut) + nFadeOutSet = ATTR_MIXED; + + if (nFadeColorSet == ATTR_SET) + nFadeColorSet = ATTR_MIXED; + + if (nInvisibleSet == ATTR_SET && bInvisible) + nInvisibleSet = ATTR_MIXED; + + if (nSoundOnSet == ATTR_SET && bSoundOn) + nSoundOnSet = ATTR_MIXED; + + if (nSoundFileSet == ATTR_SET) + nSoundFileSet = ATTR_MIXED; + + if (nPlayFullSet == ATTR_SET && bPlayFull) + nPlayFullSet = ATTR_MIXED; + + if (nClickActionSet == ATTR_SET && eClickAction != presentation::ClickAction_NONE) + nClickActionSet = ATTR_MIXED; + + if (nBookmarkSet == ATTR_SET) + nBookmarkSet = ATTR_MIXED; + + if (nSecondEffectSet == ATTR_SET && eSecondEffect != presentation::AnimationEffect_NONE) + nSecondEffectSet = ATTR_MIXED; + + if (nSecondSpeedSet == ATTR_SET) + nSecondSpeedSet = ATTR_MIXED; + + if (nSecondSoundOnSet == ATTR_SET && bSecondSoundOn) + nSecondSoundOnSet = ATTR_MIXED; + + if (nSecondPlayFullSet == ATTR_SET && bSecondPlayFull) + nSecondPlayFullSet = ATTR_MIXED; + } + } + + /* Exactly two objects with path effect? + Then, only the animation info at the moved object is valid. */ + if (nCount == 2) + { + SdrObject* pObject1 = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SdrObject* pObject2 = rMarkList.GetMark(1)->GetMarkedSdrObj(); + SdrObjKind eKind1 = pObject1->GetObjIdentifier(); + SdrObjKind eKind2 = pObject2->GetObjIdentifier(); + SdAnimationInfo* pInfo1 = SdDrawDocument::GetAnimationInfo(pObject1); + SdAnimationInfo* pInfo2 = SdDrawDocument::GetAnimationInfo(pObject2); + pInfo = nullptr; + + if (pObject1->GetObjInventor() == SdrInventor::Default && + ((eKind1 == SdrObjKind::Line) || // 2 point line + (eKind1 == SdrObjKind::PolyLine) || // Polygon + (eKind1 == SdrObjKind::PathLine)) && // Bezier curve + (pInfo2 && pInfo2->meEffect == presentation::AnimationEffect_PATH)) + { + pInfo = pInfo2; + } + + if (pObject2->GetObjInventor() == SdrInventor::Default && + ((eKind2 == SdrObjKind::Line) || // 2 point line + (eKind2 == SdrObjKind::PolyLine) || // Polygon + (eKind2 == SdrObjKind::PathLine)) && // Bezier curve + (pInfo1 && pInfo1->meEffect == presentation::AnimationEffect_PATH)) + { + pInfo = pInfo1; + } + + if (pInfo) + { + bActive = pInfo->mbActive; nAnimationSet = ATTR_SET; + eEffect = pInfo->meEffect; nEffectSet = ATTR_SET; + eTextEffect = pInfo->meTextEffect; nTextEffectSet = ATTR_SET; + eSpeed = pInfo->meSpeed; nSpeedSet = ATTR_SET; + bFadeOut = pInfo->mbDimPrevious; nFadeOutSet = ATTR_SET; + aFadeColor = pInfo->maDimColor; nFadeColorSet = ATTR_SET; + bInvisible = pInfo->mbDimHide; nInvisibleSet = ATTR_SET; + bSoundOn = pInfo->mbSoundOn; nSoundOnSet = ATTR_SET; + aSound = pInfo->maSoundFile; nSoundFileSet = ATTR_SET; + bPlayFull = pInfo->mbPlayFull; nPlayFullSet = ATTR_SET; + eClickAction = pInfo->meClickAction; nClickActionSet = ATTR_SET; + aBookmark = pInfo->GetBookmark(); nBookmarkSet = ATTR_SET; + eSecondEffect = pInfo->meSecondEffect; nSecondEffectSet = ATTR_SET; + eSecondSpeed = pInfo->meSecondSpeed; nSecondSpeedSet = ATTR_SET; + bSecondSoundOn = pInfo->mbSecondSoundOn; nSecondSoundOnSet = ATTR_SET; + bSecondPlayFull = pInfo->mbSecondPlayFull; nSecondPlayFullSet = ATTR_SET; + } + } + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if(!pArgs) + { + // fill ItemSet for dialog + SfxItemSetFixed aSet(mpDoc->GetPool()); + + // fill the set + if (nAnimationSet == ATTR_SET) + aSet.Put( SfxBoolItem( ATTR_ANIMATION_ACTIVE, bActive)); + else if (nAnimationSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_ACTIVE); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_ACTIVE, false)); + + if (nEffectSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_EFFECT, static_cast(eEffect))); + else if (nEffectSet == ATTR_MIXED) + aSet.InvalidateItem( ATTR_ANIMATION_EFFECT ); + else + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_EFFECT, sal_uInt16(presentation::AnimationEffect_NONE))); + + if (nTextEffectSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_TEXTEFFECT, static_cast(eTextEffect))); + else if (nTextEffectSet == ATTR_MIXED) + aSet.InvalidateItem( ATTR_ANIMATION_TEXTEFFECT ); + else + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_TEXTEFFECT, sal_uInt16(presentation::AnimationEffect_NONE))); + + if (nSpeedSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ANIMATION_SPEED, static_cast(eSpeed))); + else + aSet.InvalidateItem(ATTR_ANIMATION_SPEED); + + if (nFadeOutSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ANIMATION_FADEOUT, bFadeOut)); + else if (nFadeOutSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_FADEOUT); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_FADEOUT, false)); + + if (nFadeColorSet == ATTR_SET) + aSet.Put(SvxColorItem(aFadeColor, ATTR_ANIMATION_COLOR)); + else if (nFadeColorSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_COLOR); + else + aSet.Put(SvxColorItem(COL_LIGHTGRAY, ATTR_ANIMATION_COLOR)); + + if (nInvisibleSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ANIMATION_INVISIBLE, bInvisible)); + else if (nInvisibleSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_INVISIBLE); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_INVISIBLE, false)); + + if (nSoundOnSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ANIMATION_SOUNDON, bSoundOn)); + else if (nSoundOnSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_SOUNDON); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_SOUNDON, false)); + + if (nSoundFileSet == ATTR_SET) + aSet.Put(SfxStringItem(ATTR_ANIMATION_SOUNDFILE, aSound)); + else + aSet.InvalidateItem(ATTR_ANIMATION_SOUNDFILE); + + if (nPlayFullSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ANIMATION_PLAYFULL, bPlayFull)); + else if (nPlayFullSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ANIMATION_PLAYFULL); + else + aSet.Put(SfxBoolItem(ATTR_ANIMATION_PLAYFULL, false)); + + if (nClickActionSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ACTION, static_cast(eClickAction))); + else if (nClickActionSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ACTION); + else + aSet.Put(SfxUInt16Item(ATTR_ACTION, sal_uInt16(presentation::ClickAction_NONE))); + + if (nBookmarkSet == ATTR_SET) + aSet.Put(SfxStringItem(ATTR_ACTION_FILENAME, aBookmark)); + else + aSet.InvalidateItem(ATTR_ACTION_FILENAME); + + if (nSecondEffectSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ACTION_EFFECT, static_cast(eSecondEffect))); + else if (nSecondEffectSet == ATTR_MIXED) + aSet.InvalidateItem( ATTR_ACTION_EFFECT ); + else + aSet.Put(SfxUInt16Item(ATTR_ACTION_EFFECT, sal_uInt16(presentation::AnimationEffect_NONE))); + + if (nSecondSpeedSet == ATTR_SET) + aSet.Put(SfxUInt16Item(ATTR_ACTION_EFFECTSPEED, static_cast(eSecondSpeed))); + else + aSet.InvalidateItem(ATTR_ACTION_EFFECTSPEED); + + if (nSecondSoundOnSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ACTION_SOUNDON, bSecondSoundOn)); + else if (nSecondSoundOnSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ACTION_SOUNDON); + else + aSet.Put(SfxBoolItem(ATTR_ACTION_SOUNDON, false)); + + if (nSecondPlayFullSet == ATTR_SET) + aSet.Put(SfxBoolItem(ATTR_ACTION_PLAYFULL, bSecondPlayFull)); + else if (nSecondPlayFullSet == ATTR_MIXED) + aSet.InvalidateItem(ATTR_ACTION_PLAYFULL); + else + aSet.Put(SfxBoolItem(ATTR_ACTION_PLAYFULL, false)); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( pFact->CreatSdActionDialog(mpViewShell->GetFrameWeld(), &aSet, mpView) ); + + short nResult = pDlg->Execute(); + + if( nResult != RET_OK ) + return; + + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + pArgs = rReq.GetArgs(); + } + + // evaluation of the ItemSets + if (pArgs->GetItemState(ATTR_ANIMATION_ACTIVE) == SfxItemState::SET) + { + bActive = static_cast(pArgs->Get(ATTR_ANIMATION_ACTIVE)).GetValue(); + nAnimationSet = ATTR_SET; + } + else + nAnimationSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_EFFECT) == SfxItemState::SET) + { + eEffect = static_cast(static_cast( pArgs-> + Get(ATTR_ANIMATION_EFFECT)).GetValue()); + nEffectSet = ATTR_SET; + } + else + nEffectSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_TEXTEFFECT) == SfxItemState::SET) + { + eTextEffect = static_cast(static_cast( pArgs-> + Get(ATTR_ANIMATION_TEXTEFFECT)).GetValue()); + nTextEffectSet = ATTR_SET; + } + else + nTextEffectSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_SPEED) == SfxItemState::SET) + { + eSpeed = static_cast(static_cast( pArgs-> + Get(ATTR_ANIMATION_SPEED)).GetValue()); + nSpeedSet = ATTR_SET; + } + else + nSpeedSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_FADEOUT) == SfxItemState::SET) + { + bFadeOut = static_cast(pArgs->Get(ATTR_ANIMATION_FADEOUT)).GetValue(); + nFadeOutSet = ATTR_SET; + } + else + nFadeOutSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_INVISIBLE) == SfxItemState::SET) + { + bInvisible = static_cast(pArgs->Get(ATTR_ANIMATION_INVISIBLE)).GetValue(); + nInvisibleSet = ATTR_SET; + } + else + nInvisibleSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_SOUNDON) == SfxItemState::SET) + { + bSoundOn = static_cast(pArgs->Get(ATTR_ANIMATION_SOUNDON)).GetValue(); + nSoundOnSet = ATTR_SET; + } + else + nSoundOnSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_SOUNDFILE) == SfxItemState::SET) + { + aSound = static_cast(pArgs->Get(ATTR_ANIMATION_SOUNDFILE)).GetValue(); + nSoundFileSet = ATTR_SET; + } + else + nSoundFileSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_COLOR) == SfxItemState::SET) + { + aFadeColor = static_cast(pArgs->Get(ATTR_ANIMATION_COLOR)).GetValue(); + nFadeColorSet = ATTR_SET; + } + else + nFadeColorSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ANIMATION_PLAYFULL) == SfxItemState::SET) + { + bPlayFull = static_cast(pArgs->Get(ATTR_ANIMATION_PLAYFULL)).GetValue(); + nPlayFullSet = ATTR_SET; + } + else + nPlayFullSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION) == SfxItemState::SET) + { + eClickAction = static_cast(static_cast(pArgs-> + Get(ATTR_ACTION)).GetValue()); + nClickActionSet = ATTR_SET; + } + else + nClickActionSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_FILENAME) == SfxItemState::SET) + { + aBookmark = static_cast(pArgs-> + Get(ATTR_ACTION_FILENAME)).GetValue(); + nBookmarkSet = ATTR_SET; + } + else + nBookmarkSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_EFFECT) == SfxItemState::SET) + { + eSecondEffect = static_cast(static_cast( pArgs-> + Get(ATTR_ACTION_EFFECT)).GetValue()); + nSecondEffectSet = ATTR_SET; + } + else + nSecondEffectSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_EFFECTSPEED) == SfxItemState::SET) + { + eSecondSpeed = static_cast(static_cast( pArgs-> + Get(ATTR_ACTION_EFFECTSPEED)).GetValue()); + nSecondSpeedSet = ATTR_SET; + } + else + nSecondSpeedSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_SOUNDON) == SfxItemState::SET) + { + bSecondSoundOn = static_cast(pArgs->Get(ATTR_ACTION_SOUNDON)).GetValue(); + nSecondSoundOnSet = ATTR_SET; + } + else + nSecondSoundOnSet = ATTR_MISSING; + + if (pArgs->GetItemState(ATTR_ACTION_PLAYFULL) == SfxItemState::SET) + { + bSecondPlayFull = static_cast(pArgs->Get(ATTR_ACTION_PLAYFULL)).GetValue(); + nSecondPlayFullSet = ATTR_SET; + } + else + nSecondPlayFullSet = ATTR_MISSING; + + // if any attribute is chosen + if (!(nEffectSet == ATTR_SET || + nTextEffectSet == ATTR_SET || + nSpeedSet == ATTR_SET || + nAnimationSet == ATTR_SET || + nFadeOutSet == ATTR_SET || + nFadeColorSet == ATTR_SET || + nInvisibleSet == ATTR_SET || + nSoundOnSet == ATTR_SET || + nSoundFileSet == ATTR_SET || + nPlayFullSet == ATTR_SET || + nClickActionSet == ATTR_SET || + nBookmarkSet == ATTR_SET || + nSecondEffectSet == ATTR_SET || + nSecondSpeedSet == ATTR_SET || + nSecondSoundOnSet == ATTR_SET || + nSecondPlayFullSet == ATTR_SET)) + return; + + // String for undo-group and list-action + OUString aComment(SdResId(STR_UNDO_ANIMATION)); + + // with 'following curves', we have an additional UndoAction + // therefore cling? here + pUndoMgr->EnterListAction(aComment, aComment, 0, mpViewShell->GetViewShellBase().GetViewShellId()); + + // create undo group + std::unique_ptr pUndoGroup(new SdUndoGroup(mpDoc)); + pUndoGroup->SetComment(aComment); + + // for the path effect, remember some stuff + SdrPathObj* pPath = nullptr; + if (eEffect == presentation::AnimationEffect_PATH && nEffectSet == ATTR_SET) + { + DBG_ASSERT(nCount == 2, "This effect expects two selected objects"); + SdrObject* pObject1 = rMarkList.GetMark(0)->GetMarkedSdrObj(); + SdrObject* pObject2 = rMarkList.GetMark(1)->GetMarkedSdrObj(); + SdrObjKind eKind1 = pObject1->GetObjIdentifier(); + SdrObjKind eKind2 = pObject2->GetObjIdentifier(); + SdrObject* pRunningObj = nullptr; + + if (pObject1->GetObjInventor() == SdrInventor::Default && + ((eKind1 == SdrObjKind::Line) || // 2 point line + (eKind1 == SdrObjKind::PolyLine) || // Polygon + (eKind1 == SdrObjKind::PathLine))) // Bezier curve + { + pPath = static_cast(pObject1); + pRunningObj = pObject2; + } + + if (pObject2->GetObjInventor() == SdrInventor::Default && + ((eKind2 == SdrObjKind::Line) || // 2 point line + (eKind2 == SdrObjKind::PolyLine) || // Polygon + (eKind2 == SdrObjKind::PathLine))) // Bezier curve + { + pPath = static_cast(pObject2); + pRunningObj = pObject1; + } + + assert(pRunningObj && pPath && "no curve found"); + + // push the running object to the end of the curve + if (pRunningObj) + { + ::tools::Rectangle aCurRect(pRunningObj->GetLogicRect()); + Point aCurCenter(aCurRect.Center()); + const ::basegfx::B2DPolyPolygon& rPolyPolygon = pPath->GetPathPoly(); + sal_uInt32 nNoOfPolygons(rPolyPolygon.count()); + const ::basegfx::B2DPolygon& aPolygon(rPolyPolygon.getB2DPolygon(nNoOfPolygons - 1)); + sal_uInt32 nPoints(aPolygon.count()); + const ::basegfx::B2DPoint aNewB2DCenter(aPolygon.getB2DPoint(nPoints - 1)); + const Point aNewCenter(FRound(aNewB2DCenter.getX()), FRound(aNewB2DCenter.getY())); + Size aDistance(aNewCenter.X() - aCurCenter.X(), aNewCenter.Y() - aCurCenter.Y()); + pRunningObj->Move(aDistance); + + pUndoMgr->AddUndoAction(mpDoc->GetSdrUndoFactory().CreateUndoMoveObject( *pRunningObj, aDistance)); + } + } + + for (size_t nObject = 0; nObject < nCount; ++nObject) + { + SdrObject* pObject = rMarkList.GetMark(nObject)->GetMarkedSdrObj(); + + pInfo = SdDrawDocument::GetAnimationInfo(pObject); + + bool bCreated = false; + if( !pInfo ) + { + pInfo = SdDrawDocument::GetShapeUserData(*pObject,true); + bCreated = true; + } + + // path object for 'following curves'? + if (eEffect == presentation::AnimationEffect_PATH && pObject == pPath) + { + SdAnimationPrmsUndoAction* pAction = new SdAnimationPrmsUndoAction + (mpDoc, pObject, bCreated); + pAction->SetActive(pInfo->mbActive, pInfo->mbActive); + pAction->SetEffect(pInfo->meEffect, pInfo->meEffect); + pAction->SetTextEffect(pInfo->meTextEffect, pInfo->meTextEffect); + pAction->SetSpeed(pInfo->meSpeed, pInfo->meSpeed); + pAction->SetDim(pInfo->mbDimPrevious, pInfo->mbDimPrevious); + pAction->SetDimColor(pInfo->maDimColor, pInfo->maDimColor); + pAction->SetDimHide(pInfo->mbDimHide, pInfo->mbDimHide); + pAction->SetSoundOn(pInfo->mbSoundOn, pInfo->mbSoundOn); + pAction->SetSound(pInfo->maSoundFile, pInfo->maSoundFile); + pAction->SetPlayFull(pInfo->mbPlayFull, pInfo->mbPlayFull); + pAction->SetClickAction(pInfo->meClickAction, pInfo->meClickAction); + pAction->SetBookmark(pInfo->GetBookmark(), pInfo->GetBookmark()); + pAction->SetVerb(pInfo->mnVerb, pInfo->mnVerb); + pAction->SetSecondEffect(pInfo->meSecondEffect, pInfo->meSecondEffect); + pAction->SetSecondSpeed(pInfo->meSecondSpeed, pInfo->meSecondSpeed); + pAction->SetSecondSoundOn(pInfo->mbSecondSoundOn, pInfo->mbSecondSoundOn); + pAction->SetSecondPlayFull(pInfo->mbSecondPlayFull, pInfo->mbSecondPlayFull); + pUndoGroup->AddAction(pAction); + + } + else + { + + // create undo action with old and new sizes + SdAnimationPrmsUndoAction* pAction = new SdAnimationPrmsUndoAction + (mpDoc, pObject, bCreated); + pAction->SetActive(pInfo->mbActive, bActive); + pAction->SetEffect(pInfo->meEffect, eEffect); + pAction->SetTextEffect(pInfo->meTextEffect, eTextEffect); + pAction->SetSpeed(pInfo->meSpeed, eSpeed); + pAction->SetDim(pInfo->mbDimPrevious, bFadeOut); + pAction->SetDimColor(pInfo->maDimColor, aFadeColor); + pAction->SetDimHide(pInfo->mbDimHide, bInvisible); + pAction->SetSoundOn(pInfo->mbSoundOn, bSoundOn); + pAction->SetSound(pInfo->maSoundFile, aSound); + pAction->SetPlayFull(pInfo->mbPlayFull, bPlayFull); + pAction->SetClickAction(pInfo->meClickAction, eClickAction); + pAction->SetBookmark(pInfo->GetBookmark(), aBookmark); + pAction->SetVerb(pInfo->mnVerb, static_cast(pInfo->GetBookmark().toInt32()) ); + pAction->SetSecondEffect(pInfo->meSecondEffect, eSecondEffect); + pAction->SetSecondSpeed(pInfo->meSecondSpeed, eSecondSpeed); + pAction->SetSecondSoundOn(pInfo->mbSecondSoundOn, bSecondSoundOn); + pAction->SetSecondPlayFull(pInfo->mbSecondPlayFull,bSecondPlayFull); + pUndoGroup->AddAction(pAction); + + // insert new values at info block of the object + if (nAnimationSet == ATTR_SET) + pInfo->mbActive = bActive; + + if (nEffectSet == ATTR_SET) + pInfo->meEffect = eEffect; + + if (nTextEffectSet == ATTR_SET) + pInfo->meTextEffect = eTextEffect; + + if (nSpeedSet == ATTR_SET) + pInfo->meSpeed = eSpeed; + + if (nFadeOutSet == ATTR_SET) + pInfo->mbDimPrevious = bFadeOut; + + if (nFadeColorSet == ATTR_SET) + pInfo->maDimColor = aFadeColor; + + if (nInvisibleSet == ATTR_SET) + pInfo->mbDimHide = bInvisible; + + if (nSoundOnSet == ATTR_SET) + pInfo->mbSoundOn = bSoundOn; + + if (nSoundFileSet == ATTR_SET) + pInfo->maSoundFile = aSound; + + if (nPlayFullSet == ATTR_SET) + pInfo->mbPlayFull = bPlayFull; + + if (nClickActionSet == ATTR_SET) + pInfo->meClickAction = eClickAction; + + if (nBookmarkSet == ATTR_SET) + pInfo->SetBookmark( aBookmark ); + + if (nSecondEffectSet == ATTR_SET) + pInfo->meSecondEffect = eSecondEffect; + + if (nSecondSpeedSet == ATTR_SET) + pInfo->meSecondSpeed = eSecondSpeed; + + if (nSecondSoundOnSet == ATTR_SET) + pInfo->mbSecondSoundOn = bSecondSoundOn; + + if (nSecondPlayFullSet == ATTR_SET) + pInfo->mbSecondPlayFull = bSecondPlayFull; + + if (eClickAction == presentation::ClickAction_VERB) + pInfo->mnVerb = static_cast(aBookmark.toInt32()); + } + } + // Set the Undo Group in of the Undo Manager + pUndoMgr->AddUndoAction(std::move(pUndoGroup)); + pUndoMgr->LeaveListAction(); + + // Model changed + mpDoc->SetChanged(); + // not seen, therefore we do not need to invalidate at the bindings +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuolbull.cxx b/sd/source/ui/func/fuolbull.cxx new file mode 100644 index 000000000..beb57db5b --- /dev/null +++ b/sd/source/ui/func/fuolbull.cxx @@ -0,0 +1,340 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace svx::sidebar; +namespace sd { + +FuBulletAndPosition::FuBulletAndPosition(ViewShell* pViewShell, ::sd::Window* pWindow, + ::sd::View* pView, SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewShell, pWindow, pView, pDoc, rReq) +{ +} + +rtl::Reference FuBulletAndPosition::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuBulletAndPosition( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuBulletAndPosition::DoExecute( SfxRequest& rReq ) +{ + const sal_uInt16 nSId = rReq.GetSlot(); + if ( nSId == FN_SVX_SET_BULLET || nSId == FN_SVX_SET_NUMBER ) + { + SetCurrentBulletsNumbering(rReq); + return; + } + + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxStringItem* pPageItem = SfxItemSet::GetItem(pArgs, FN_PARAM_1, false); + + if ( pArgs && !pPageItem ) + { + /* not direct to pOlView; therefore, SdDrawView::SetAttributes can catch + changes to master page and redirect to a template */ + mpView->SetAttributes(*pArgs); + return; + } + + // fill ItemSet for Dialog + SfxItemSet aEditAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aEditAttr ); + + SfxItemSetFixed aNewAttr( mpViewShell->GetPool() ); + aNewAttr.Put( aEditAttr, false ); + + auto pView = mpView; + + // create and execute dialog + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSvxBulletAndPositionDlg(mpViewShell->GetFrameWeld(), &aNewAttr, mpView)); + sal_uInt16 nResult = pDlg->Execute(); + + if( nResult == RET_OK ) + { + OutlinerView* pOLV = pView->GetTextEditOutlinerView(); + + std::unique_ptr> aGuard; + + if (OutlineView* pOutlineView = dynamic_cast(pView)) + { + pOLV = pOutlineView->GetViewByWindow(mpViewShell->GetActiveWindow()); + aGuard.reset(new OutlineViewModelChangeGuard(*pOutlineView)); + } + + if( pOLV ) + pOLV->EnsureNumberingIsOn(); + + const SfxItemSet pOutputSet( *pDlg->GetOutputItemSet( &aNewAttr ) ); + pView->SetAttributes(pOutputSet, /*bReplaceAll=*/false, /*bSlide*/ pDlg->IsSlideScope(), /*bMaster=*/pDlg->IsApplyToMaster()); + } + + rReq.Done(); +} + +void FuBulletAndPosition::SetCurrentBulletsNumbering(SfxRequest& rReq) +{ + if (!mpDoc || !mpView) + return; + + const sal_uInt16 nSId = rReq.GetSlot(); + if ( nSId != FN_SVX_SET_BULLET && nSId != FN_SVX_SET_NUMBER ) + { + // unexpected SfxRequest + return; + } + + const SfxUInt16Item* pItem = rReq.GetArg(nSId); + if ( !pItem ) + { + rReq.Done(); + return; + } + + SfxItemSetFixed aNewAttr( mpViewShell->GetPool() ); + { + SfxItemSet aEditAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aEditAttr ); + aNewAttr.Put( aEditAttr, false ); + } + + const DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >(mpViewShell); + //Init bullet level in "Customize" tab page in bullet dialog in master page view + const bool bInMasterView = pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::MasterPage; + if ( bInMasterView ) + { + SdrObject* pObj = mpView->GetTextEditObject(); + if( pObj && pObj->GetObjIdentifier() == SdrObjKind::OutlineText ) + { + const sal_uInt16 nLevel = mpView->GetSelectionLevel(); + if( nLevel != 0xFFFF ) + { + //save the itemset value + SfxItemSet aStoreSet( aNewAttr ); + aNewAttr.ClearItem(); + //extend range + aNewAttr.MergeRange( SID_PARAM_NUM_PRESET, SID_PARAM_CUR_NUM_LEVEL ); + aNewAttr.Put( aStoreSet ); + //put current level user selected + aNewAttr.Put( SfxUInt16Item( SID_PARAM_CUR_NUM_LEVEL, nLevel ) ); + } + } + } + + sal_uInt16 nIdx = pItem->GetValue(); + bool bToggle = false; + if( nIdx == sal_uInt16(0xFFFF) ) + { + // If the nIdx is (sal_uInt16)0xFFFF, means set bullet status to on/off + nIdx = 1; + bToggle = true; + } + nIdx--; + + TypedWhichId nNumItemId = SID_ATTR_NUMBERING_RULE; + const SfxPoolItem* pTmpItem = GetNumBulletItem( aNewAttr, nNumItemId ); + std::unique_ptr pNumRule; + if ( pTmpItem ) + { + pNumRule.reset(new SvxNumRule(static_cast(pTmpItem)->GetNumRule())); + + // get numbering rule corresponding to and apply the needed number formats to + NBOTypeMgrBase* pNumRuleMgr = + NBOutlineTypeMgrFact::CreateInstance( + nSId == FN_SVX_SET_BULLET ? NBOType::Bullets : NBOType::Numbering ); + if ( pNumRuleMgr ) + { + sal_uInt16 nActNumLvl = sal_uInt16(0xFFFF); + if(const SfxUInt16Item* pNumLevelItem = aNewAttr.GetItemIfSet(SID_PARAM_CUR_NUM_LEVEL, false)) + nActNumLvl = pNumLevelItem->GetValue(); + + pNumRuleMgr->SetItems(&aNewAttr); + SvxNumRule aTmpRule( *pNumRule ); + if ( nSId == FN_SVX_SET_BULLET && bToggle && nIdx==0 ) + { + // for toggling bullets get default numbering rule + pNumRuleMgr->ApplyNumRule( aTmpRule, nIdx, nActNumLvl, true ); + } + else + { + pNumRuleMgr->ApplyNumRule( aTmpRule, nIdx, nActNumLvl ); + } + + sal_uInt16 nMask = 1; + for(sal_uInt16 i = 0; i < pNumRule->GetLevelCount(); i++) + { + if(nActNumLvl & nMask) + { + const SvxNumberFormat& aFmt(aTmpRule.GetLevel(i)); + pNumRule->SetLevel(i, aFmt); + } + nMask <<= 1; + } + } + } + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + std::unique_ptr> aGuard; + if (OutlineView* pView = dynamic_cast(mpView)) + { + pOLV = pView->GetViewByWindow(mpViewShell->GetActiveWindow()); + aGuard.reset(new OutlineViewModelChangeGuard(*pView)); + } + + SdrOutliner* pOwner = bInMasterView ? mpView->GetTextEditOutliner() : nullptr; + const bool bOutlinerUndoEnabled = pOwner && !pOwner->IsInUndo() && pOwner->IsUndoEnabled(); + SdrModel* pSdrModel = bInMasterView ? mpView->GetModel() : nullptr; + const bool bModelUndoEnabled = pSdrModel && pSdrModel->IsUndoEnabled(); + + if ( bOutlinerUndoEnabled ) + { + pOwner->UndoActionStart( OLUNDO_ATTR ); + } + else if ( bModelUndoEnabled ) + { + pSdrModel->BegUndo(); + } + + if ( pOLV ) + { + pOLV->ToggleBulletsNumbering( bToggle, nSId == FN_SVX_SET_BULLET, bInMasterView ? nullptr : pNumRule.get() ); + } + else + { + mpView->ChangeMarkedObjectsBulletsNumbering( bToggle, nSId == FN_SVX_SET_BULLET, bInMasterView ? nullptr : pNumRule.get() ); + } + + if (bInMasterView && pNumRule) + { + SfxItemSetFixed aSetAttr( mpViewShell->GetPool() ); + aSetAttr.Put(SvxNumBulletItem( *pNumRule, nNumItemId )); + mpView->SetAttributes(aSetAttr); + } + + if( bOutlinerUndoEnabled ) + { + pOwner->UndoActionEnd(); + } + else if ( bModelUndoEnabled ) + { + pSdrModel->EndUndo(); + } + + pNumRule.reset(); + rReq.Done(); +} + +const SvxNumBulletItem* FuBulletAndPosition::GetNumBulletItem(SfxItemSet& aNewAttr, TypedWhichId& nNumItemId) +{ + const SvxNumBulletItem* pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + + if(pTmpItem) + return pTmpItem; + + nNumItemId = aNewAttr.GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE); + pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + if (pTmpItem) + return pTmpItem; + + bool bOutliner = false; + bool bTitle = false; + + if( mpView ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + for(size_t nNum = 0; nNum < nCount; ++nNum) + { + SdrObject* pObj = rMarkList.GetMark(nNum)->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + switch(pObj->GetObjIdentifier()) + { + case SdrObjKind::TitleText: + bTitle = true; + break; + case SdrObjKind::OutlineText: + bOutliner = true; + break; + default: + break; + } + } + } + } + + const SvxNumBulletItem *pItem = nullptr; + if(bOutliner) + { + SfxStyleSheetBasePool* pSSPool = mpView->GetDocSh()->GetStyleSheetPool(); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( STR_LAYOUT_OUTLINE + " 1", SfxStyleFamily::Pseudo); + if( pFirstStyleSheet ) + pItem = pFirstStyleSheet->GetItemSet().GetItemIfSet(EE_PARA_NUMBULLET, false); + } + + if( pItem == nullptr ) + pItem = aNewAttr.GetPool()->GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET); + + //DBG_ASSERT( pItem, "No EE_PARA_NUMBULLET in the Pool!" ); + + aNewAttr.Put(pItem->CloneSetWhich(EE_PARA_NUMBULLET)); + + if(bTitle && aNewAttr.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET ) + { + const SvxNumBulletItem* pBulletItem = aNewAttr.GetItem(EE_PARA_NUMBULLET); + const SvxNumRule& rLclRule = pBulletItem->GetNumRule(); + SvxNumRule aNewRule( rLclRule ); + aNewRule.SetFeatureFlag( SvxNumRuleFlags::NO_NUMBERS ); + + SvxNumBulletItem aNewItem( std::move(aNewRule), EE_PARA_NUMBULLET ); + aNewAttr.Put(aNewItem); + } + + pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + return pTmpItem; +} + + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuoltext.cxx b/sd/source/ui/func/fuoltext.cxx new file mode 100644 index 000000000..fe64cac47 --- /dev/null +++ b/sd/source/ui/func/fuoltext.cxx @@ -0,0 +1,305 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace sd { + +const sal_uInt16 SidArray[] = { + SID_STYLE_FAMILY2, + SID_STYLE_FAMILY3, + SID_STYLE_FAMILY5, + SID_STYLE_UPDATE_BY_EXAMPLE, + SID_CUT, + SID_COPY, + SID_PASTE, + SID_SELECTALL, + SID_ATTR_CHAR_FONT, + SID_ATTR_CHAR_POSTURE, + SID_ATTR_CHAR_WEIGHT, + SID_ATTR_CHAR_SHADOWED, + SID_ATTR_CHAR_STRIKEOUT, + SID_ATTR_CHAR_UNDERLINE, + SID_ATTR_CHAR_FONTHEIGHT, + SID_ATTR_CHAR_COLOR, + SID_ATTR_CHAR_KERNING, + SID_OUTLINE_UP, + SID_OUTLINE_DOWN, + SID_OUTLINE_LEFT, + SID_OUTLINE_RIGHT, + //SID_OUTLINE_FORMAT, + SID_OUTLINE_COLLAPSE_ALL, + //SID_OUTLINE_BULLET, + SID_OUTLINE_COLLAPSE, + SID_OUTLINE_EXPAND_ALL, + SID_OUTLINE_EXPAND, + SID_SET_SUPER_SCRIPT, + SID_SET_SUB_SCRIPT, + SID_HYPERLINK_GETLINK, + SID_DEC_INDENT, + SID_INC_INDENT, + SID_PARASPACE_INCREASE, + SID_PARASPACE_DECREASE, + SID_SCALE, + SID_STATUS_PAGE, + SID_STATUS_LAYOUT, + SID_EXPAND_PAGE, + SID_SUMMARY_PAGE, + 0 }; + + +FuOutlineText::FuOutlineText(ViewShell* pViewShell, ::sd::Window* pWindow, + ::sd::View* pView, SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewShell, pWindow, pView, pDoc, rReq), + pOutlineViewShell (static_cast(pViewShell)), + pOutlineView (static_cast(pView)) +{ +} + +/** + * forward to OutlinerView + */ +bool FuOutlineText::Command(const CommandEvent& rCEvt) +{ + bool bResult = false; + + OutlinerView* pOlView = + static_cast(mpView)->GetViewByWindow(mpWindow); + DBG_ASSERT (pOlView, "no OutlineView found"); + + if (pOlView) + { + pOlView->Command(rCEvt); // unfortunately, we do not get a return value + bResult = true; + } + return bResult; +} + + +rtl::Reference FuOutlineText::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuOutlineText( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute( rReq ); + return xFunc; +} + +bool FuOutlineText::MouseButtonDown(const MouseEvent& rMEvt) +{ + mpWindow->GrabFocus(); + + bool bReturn = pOutlineView->GetViewByWindow(mpWindow)->MouseButtonDown(rMEvt); + + if (bReturn) + { + // Now the attributes of the current text position can be different + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + } + else + { + bReturn = FuPoor::MouseButtonDown(rMEvt); + } + + return bReturn; +} + +bool FuOutlineText::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = pOutlineView->GetViewByWindow(mpWindow)->MouseMove(rMEvt); + + if (!bReturn) + { + bReturn = FuPoor::MouseMove(rMEvt); + } + + return bReturn; +} + +bool FuOutlineText::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = pOutlineView->GetViewByWindow(mpWindow)->MouseButtonUp(rMEvt); + + if (bReturn) + { + // Now the attributes of the current text position can be different + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + } + else + { + const SvxFieldItem* pFieldItem = pOutlineView->GetViewByWindow( mpWindow )->GetFieldUnderMousePointer(); + if( pFieldItem ) + { + const SvxFieldData* pField = pFieldItem->GetField(); + + if( auto pURLField = dynamic_cast< const SvxURLField *>( pField ) ) + { + bReturn = true; + mpWindow->ReleaseMouse(); + SfxStringItem aStrItem( SID_FILE_NAME, pURLField->GetURL() ); + SfxStringItem aReferer( SID_REFERER, mpDocSh->GetMedium()->GetName() ); + SfxBoolItem aBrowseItem( SID_BROWSE, true ); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + + if ( rMEvt.IsMod1() ) + { + // open in new frame + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aBrowseItem, &aReferer }); + } + else + { + // open in current frame + SfxFrameItem aFrameItem( SID_DOCFRAME, pFrame ); + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + } + } + } + + if( !bReturn ) + bReturn = FuPoor::MouseButtonUp(rMEvt); + + return bReturn; +} + +/** + * Process keyboard input + * @returns sal_True if a KeyEvent is being processed, sal_False otherwise + */ +bool FuOutlineText::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + sal_uInt16 nKeyGroup = rKEvt.GetKeyCode().GetGroup(); + if( !mpDocSh->IsReadOnly() || nKeyGroup == KEYGROUP_CURSOR ) + { + mpWindow->GrabFocus(); + + std::unique_ptr> aGuard; + if( (nKeyGroup != KEYGROUP_CURSOR) && (nKeyGroup != KEYGROUP_FKEYS) ) + aGuard.reset( new OutlineViewModelChangeGuard( *pOutlineView ) ); + + bReturn = pOutlineView->GetViewByWindow(mpWindow)->PostKeyEvent(rKEvt); + + if (bReturn) + { + UpdateForKeyPress (rKEvt); + } + else + { + bReturn = FuPoor::KeyInput(rKEvt); + } + } + + return bReturn; +} + +void FuOutlineText::UpdateForKeyPress (const KeyEvent& rEvent) +{ + // Attributes at the current text position may have changed. + mpViewShell->GetViewFrame()->GetBindings().Invalidate(SidArray); + + bool bUpdatePreview = true; + switch (rEvent.GetKeyCode().GetCode()) + { + // When just the cursor has been moved the preview only changes when + // it moved to entries of another page. To prevent unnecessary + // updates we check this here. This is an early rejection test, so + // missing a key is not a problem. + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + case KEY_HOME: + case KEY_END: + case KEY_PAGEUP: + case KEY_PAGEDOWN: + { + SdPage* pCurrentPage = pOutlineViewShell->GetActualPage(); + bUpdatePreview = (pCurrentPage != pOutlineViewShell->GetActualPage()); + } + break; + } + if (bUpdatePreview) + pOutlineViewShell->UpdatePreview (pOutlineViewShell->GetActualPage()); +} + +/** + * Cut object to clipboard + */ +void FuOutlineText::DoCut() +{ + pOutlineView->GetViewByWindow(mpWindow)->Cut(); +} + +/** + * Copy object to clipboard + */ +void FuOutlineText::DoCopy() +{ + pOutlineView->GetViewByWindow(mpWindow)->Copy(); +} + +/** + * Paste object from clipboard + */ +void FuOutlineText::DoPaste() +{ + pOutlineView->GetViewByWindow(mpWindow)->PasteSpecial(); +} + +/** + * Paste object as unformatted text from clipboard + */ +void FuOutlineText::DoPasteUnformatted() +{ + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mpViewShell->GetActiveWindow() ) ); + if (aDataHelper.GetTransferable().is()) + { + OUString aText; + if (aDataHelper.GetString(SotClipboardFormatId::STRING, aText)) + pOutlineView->GetViewByWindow(mpWindow)->InsertText(aText); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fupage.cxx b/sd/source/ui/func/fupage.cxx new file mode 100644 index 000000000..5427e6b7d --- /dev/null +++ b/sd/source/ui/func/fupage.cxx @@ -0,0 +1,648 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +// arrange Tab-Page + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace com::sun::star; + +namespace sd { + +// 50 cm 28350 +// adapted from writer +#define MAXHEIGHT 28350 +#define MAXWIDTH 28350 + + +static void mergeItemSetsImpl( SfxItemSet& rTarget, const SfxItemSet& rSource ) +{ + const WhichRangesContainer& rRanges = rSource.GetRanges(); + sal_uInt16 p1, p2; + for (sal_Int32 i = 0; i < rRanges.size(); ++i) + { + p1 = rRanges[i].first; + p2 = rRanges[i].second; + + // make ranges discrete + while(i < rRanges.size()-1 && (rRanges[i+1].first - p2 == 1)) + { + p2 = rRanges[i+1].second; + ++i; + } + rTarget.MergeRange( p1, p2 ); + } + + rTarget.Put(rSource); +} + +FuPage::FuPage( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq ) +: FuPoor(pViewSh, pWin, pView, pDoc, rReq), + mrReq(rReq), + mpArgs( rReq.GetArgs() ), + mbPageBckgrdDeleted( false ), + mbMasterPage( false ), + mbDisplayBackgroundTabPage( true ), + mpPage(nullptr), + mpDrawViewShell(nullptr) +{ +} + +rtl::Reference FuPage::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuPage( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuPage::DoExecute(SfxRequest& rReq) +{ + mpDrawViewShell = dynamic_cast(mpViewShell); + DBG_ASSERT( mpDrawViewShell, "sd::FuPage::FuPage(), called without a current DrawViewShell!" ); + + if( mpDrawViewShell ) + { + mbMasterPage = mpDrawViewShell->GetEditMode() == EditMode::MasterPage; + // we don't really want to format page background with SID_ATTR_PAGE[_SIZE] slots + mbDisplayBackgroundTabPage = ( mpDrawViewShell->GetPageKind() == PageKind::Standard) && + ( nSlotId != SID_ATTR_PAGE_SIZE) && ( nSlotId != SID_ATTR_PAGE ); + mpPage = mpDrawViewShell->getCurrentPage(); + } + + if( !mpPage ) + return; + + // if there are no arguments given, open the dialog + if (!mpArgs || mpArgs->GetItemState(SID_SELECT_BACKGROUND) == SfxItemState::SET) + { + mpView->SdrEndTextEdit(); + mpArgs = ExecuteDialog(mpWindow ? mpWindow->GetFrameWeld() : nullptr, rReq); + } + + // if we now have arguments, apply them to current page + if( mpArgs ) + { + ApplyItemSet( mpArgs ); + } +} + +FuPage::~FuPage() +{ +} + +void FuPage::Activate() +{ +} + +void FuPage::Deactivate() +{ +} + +void MergePageBackgroundFilling(SdPage *pPage, SdStyleSheet *pStyleSheet, bool bMasterPage, SfxItemSet& rMergedAttr) +{ + if (bMasterPage) + { + if (pStyleSheet) + mergeItemSetsImpl(rMergedAttr, pStyleSheet->GetItemSet()); + } + else + { + // Only this page, get attributes for background fill + const SfxItemSet& rBackgroundAttributes = pPage->getSdrPageProperties().GetItemSet(); + + if(drawing::FillStyle_NONE != rBackgroundAttributes.Get(XATTR_FILLSTYLE).GetValue()) + { + // page attributes are used, take them + rMergedAttr.Put(rBackgroundAttributes); + } + else + { + if(pStyleSheet + && drawing::FillStyle_NONE != pStyleSheet->GetItemSet().Get(XATTR_FILLSTYLE).GetValue()) + { + // if the page has no fill style, use the settings from the + // background stylesheet (if used) + mergeItemSetsImpl(rMergedAttr, pStyleSheet->GetItemSet()); + } + else + { + // no fill style from page, start with no fill style + rMergedAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + } + } +} + +const SfxItemSet* FuPage::ExecuteDialog(weld::Window* pParent, const SfxRequest& rReq) +{ + if (!mpDrawViewShell) + return nullptr; + + SfxItemSetFixed< + XATTR_FILL_FIRST, XATTR_FILL_LAST, + EE_PARA_WRITINGDIR, EE_PARA_WRITINGDIR, + SID_ATTR_BORDER_OUTER, SID_ATTR_BORDER_OUTER, + SID_ATTR_BORDER_SHADOW, SID_ATTR_BORDER_SHADOW, + SID_ATTR_PAGE, SID_ATTR_PAGE_SHARED, + SID_ATTR_CHAR_GRABBAG, SID_ATTR_CHAR_GRABBAG, + SID_ATTR_PAGE_COLOR, SID_ATTR_PAGE_FILLSTYLE + > aNewAttr(mpDoc->GetPool()); + // Keep it sorted + aNewAttr.MergeRange(mpDoc->GetPool().GetWhich(SID_ATTR_LRSPACE), + mpDoc->GetPool().GetWhich(SID_ATTR_ULSPACE)); + + // Retrieve additional data for dialog + + SvxShadowItem aShadowItem(SID_ATTR_BORDER_SHADOW); + aNewAttr.Put( aShadowItem ); + SvxBoxItem aBoxItem( SID_ATTR_BORDER_OUTER ); + aNewAttr.Put( aBoxItem ); + + aNewAttr.Put( SvxFrameDirectionItem( + mpDoc->GetDefaultWritingMode() == css::text::WritingMode_RL_TB ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, + EE_PARA_WRITINGDIR ) ); + + // Retrieve page-data for dialog + + SvxPageItem aPageItem( SID_ATTR_PAGE ); + aPageItem.SetDescName( mpPage->GetName() ); + aPageItem.SetPageUsage( SvxPageUsage::All ); + aPageItem.SetLandscape( mpPage->GetOrientation() == Orientation::Landscape ); + aPageItem.SetNumType( mpDoc->GetPageNumType() ); + aNewAttr.Put( aPageItem ); + + // size + maSize = mpPage->GetSize(); + SvxSizeItem aSizeItem( SID_ATTR_PAGE_SIZE, maSize ); + aNewAttr.Put( aSizeItem ); + + // Max size + SvxSizeItem aMaxSizeItem( SID_ATTR_PAGE_MAXSIZE, Size( MAXWIDTH, MAXHEIGHT ) ); + aNewAttr.Put( aMaxSizeItem ); + + // paperbin + SvxPaperBinItem aPaperBinItem( SID_ATTR_PAGE_PAPERBIN, static_cast(mpPage->GetPaperBin()) ); + aNewAttr.Put( aPaperBinItem ); + + SvxLRSpaceItem aLRSpaceItem( static_cast(mpPage->GetLeftBorder()), static_cast(mpPage->GetRightBorder()), 0, 0, mpDoc->GetPool().GetWhich(SID_ATTR_LRSPACE)); + aNewAttr.Put( aLRSpaceItem ); + + SvxULSpaceItem aULSpaceItem( static_cast(mpPage->GetUpperBorder()), static_cast(mpPage->GetLowerBorder()), mpDoc->GetPool().GetWhich(SID_ATTR_ULSPACE)); + aNewAttr.Put( aULSpaceItem ); + + // Application + bool bScale = mpDoc->GetDocumentType() != DocumentType::Draw; + aNewAttr.Put( SfxBoolItem( SID_ATTR_PAGE_EXT1, bScale ) ); + + bool bFullSize = mpPage->IsMasterPage() ? + mpPage->IsBackgroundFullSize() : static_cast(mpPage->TRG_GetMasterPage()).IsBackgroundFullSize(); + + SfxGrabBagItem grabBag(SID_ATTR_CHAR_GRABBAG); + grabBag.GetGrabBag()["BackgroundFullSize"] <<= bFullSize; + + if (mpDoc->GetDocumentType() == DocumentType::Impress && mpPage->IsMasterPage()) + { + // A master slide may have a theme. + svx::Theme* pTheme = mpPage->getSdrPageProperties().GetTheme(); + if (pTheme) + { + uno::Any aTheme; + pTheme->ToAny(aTheme); + grabBag.GetGrabBag()["Theme"] = aTheme; + } + } + + aNewAttr.Put(grabBag); + + // Merge ItemSet for dialog + + const WhichRangesContainer& rRanges = aNewAttr.GetRanges(); + sal_uInt16 p1 = rRanges[0].first, p2 = rRanges[0].second; + sal_Int32 idx = 1; + while(idx < rRanges.size() && (rRanges[idx].first - p2 == 1)) + { + p2 = rRanges[idx].second; + ++idx; + } + SfxItemSet aMergedAttr( *aNewAttr.GetPool(), p1, p2 ); + + mergeItemSetsImpl( aMergedAttr, aNewAttr ); + + SdStyleSheet* pStyleSheet = mpPage->getPresentationStyle(HID_PSEUDOSHEET_BACKGROUND); + + // merge page background filling to the dialogs input set + if( mbDisplayBackgroundTabPage ) + { + MergePageBackgroundFilling(mpPage, pStyleSheet, mbMasterPage, aMergedAttr); + } + + std::optional< SfxItemSet > pTempSet; + + const sal_uInt16 nId = GetSlotID(); + if (nId == SID_SAVE_BACKGROUND) + { + const XFillStyleItem& rStyleItem = aMergedAttr.Get(XATTR_FILLSTYLE); + if (drawing::FillStyle_BITMAP == rStyleItem.GetValue()) + { + const XFillBitmapItem& rBitmap = aMergedAttr.Get(XATTR_FILLBITMAP); + const GraphicObject& rGraphicObj = rBitmap.GetGraphicObject(); + GraphicHelper::ExportGraphic(pParent, rGraphicObj.GetGraphic(), ""); + } + } + else if (nId == SID_SELECT_BACKGROUND) + { + Graphic aGraphic; + ErrCode nError = ERRCODE_GRFILTER_OPENERROR; + + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem; + + if (pArgs && pArgs->GetItemState(SID_SELECT_BACKGROUND, true, &pItem) == SfxItemState::SET) + { + OUString aFileName(static_cast(pItem)->GetValue()); + OUString aFilterName; + + if (const SfxStringItem* pFilterItem = pArgs->GetItemIfSet(FN_PARAM_FILTER)) + aFilterName = pFilterItem->GetValue(); + + nError = GraphicFilter::LoadGraphic(aFileName, aFilterName, aGraphic, + &GraphicFilter::GetGraphicFilter()); + } + else + { + SvxOpenGraphicDialog aDlg(SdResId(STR_SET_BACKGROUND_PICTURE), pParent); + + nError = aDlg.Execute(); + if (nError == ERRCODE_NONE) + { + nError = aDlg.GetGraphic(aGraphic); + } + } + + if (nError == ERRCODE_NONE) + { + pTempSet.emplace( mpDoc->GetPool(), svl::Items ); + + pTempSet->Put( XFillStyleItem( drawing::FillStyle_BITMAP ) ); + + // MigrateItemSet makes sure the XFillBitmapItem will have a unique name + SfxItemSetFixed aMigrateSet( mpDoc->GetPool() ); + aMigrateSet.Put(XFillBitmapItem("background", aGraphic)); + SdrModel::MigrateItemSet( &aMigrateSet, &*pTempSet, mpDoc ); + + pTempSet->Put( XFillBmpStretchItem( true )); + pTempSet->Put( XFillBmpTileItem( false )); + } + } + + else + { + bool bIsImpressDoc = mpDrawViewShell->GetDoc()->GetDocumentType() == DocumentType::Impress; + + // create the dialog + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( pFact->CreateSdTabPageDialog(mpViewShell->GetFrameWeld(), &aMergedAttr, mpDocSh, mbDisplayBackgroundTabPage, bIsImpressDoc, mbMasterPage) ); + if( pDlg->Execute() == RET_OK ) + pTempSet.emplace( *pDlg->GetOutputItemSet() ); + } + + if (pTempSet && pStyleSheet) + { + pStyleSheet->AdjustToFontHeight(*pTempSet); + + if( mbDisplayBackgroundTabPage ) + { + // if some fillstyle-items are not set in the dialog, then + // try to use the items before + bool bChanges = false; + for( sal_uInt16 i=XATTR_FILL_FIRST; iGetItemState( i ) == SfxItemState::DEFAULT ) + pTempSet->Put( aMergedAttr.Get( i ) ); + else + if( aMergedAttr.GetItem( i ) != pTempSet->GetItem( i ) ) + bChanges = true; + } + } + + // if the background for this page was set to invisible, the background-object has to be deleted, too. + const XFillStyleItem* pTempFillStyleItem = pTempSet->GetItem(XATTR_FILLSTYLE); + assert(pTempFillStyleItem); + if (pTempFillStyleItem->GetValue() == drawing::FillStyle_NONE) + mbPageBckgrdDeleted = true; + else + { + if (pTempSet->GetItemState(XATTR_FILLSTYLE) == SfxItemState::DEFAULT) + { + const XFillStyleItem* pMergedFillStyleItem = aMergedAttr.GetItem(XATTR_FILLSTYLE); + assert(pMergedFillStyleItem); + if (pMergedFillStyleItem->GetValue() == drawing::FillStyle_NONE) + mbPageBckgrdDeleted = true; + } + } + + const XFillGradientItem* pTempGradItem = pTempSet->GetItem(XATTR_FILLGRADIENT); + if (pTempGradItem && pTempGradItem->GetName().isEmpty()) + { + // MigrateItemSet guarantees unique gradient names + SfxItemSetFixed aMigrateSet( mpDoc->GetPool() ); + aMigrateSet.Put( XFillGradientItem("gradient", pTempGradItem->GetGradientValue()) ); + SdrModel::MigrateItemSet( &aMigrateSet, &*pTempSet, mpDoc); + } + + const XFillHatchItem* pTempHatchItem = pTempSet->GetItem(XATTR_FILLHATCH); + if (pTempHatchItem && pTempHatchItem->GetName().isEmpty()) + { + // MigrateItemSet guarantees unique hatch names + SfxItemSetFixed aMigrateSet( mpDoc->GetPool() ); + aMigrateSet.Put( XFillHatchItem("hatch", pTempHatchItem->GetHatchValue()) ); + SdrModel::MigrateItemSet( &aMigrateSet, &*pTempSet, mpDoc); + } + + if( !mbMasterPage && bChanges && mbPageBckgrdDeleted ) + { + mpBackgroundObjUndoAction.reset( new SdBackgroundObjUndoAction( + *mpDoc, *mpPage, mpPage->getSdrPageProperties().GetItemSet()) ); + + if(!mpPage->IsMasterPage()) + { + // on normal pages, switch off fill attribute usage + SdrPageProperties& rPageProperties = mpPage->getSdrPageProperties(); + rPageProperties.ClearItem( XATTR_FILLBITMAP ); + rPageProperties.ClearItem( XATTR_FILLGRADIENT ); + rPageProperties.ClearItem( XATTR_FILLHATCH ); + rPageProperties.PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + } + } + + + /* Special treatment: reset the INVALIDS to + NULL-Pointer (otherwise INVALIDs or pointer point + to DefaultItems in the template; both would + prevent the attribute inheritance) */ + pTempSet->ClearInvalidItems(); + + if( mbMasterPage ) + { + mpDocSh->GetUndoManager()->AddUndoAction(std::make_unique( + mpDoc, static_cast(pStyleSheet), &(*pTempSet))); + pStyleSheet->GetItemSet().Put( *pTempSet ); + sdr::properties::CleanupFillProperties( pStyleSheet->GetItemSet() ); + pStyleSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + + // if background filling is set to master pages then clear from page set + if( mbMasterPage ) + { + for( sal_uInt16 nWhich = XATTR_FILL_FIRST; nWhich <= XATTR_FILL_LAST; nWhich++ ) + { + pTempSet->ClearItem( nWhich ); + } + pTempSet->Put(XFillStyleItem(drawing::FillStyle_NONE)); + } + + if( const SvxFrameDirectionItem* pItem = pTempSet->GetItemIfSet( EE_PARA_WRITINGDIR, false ) ) + { + SvxFrameDirection nVal = pItem->GetValue(); + mpDoc->SetDefaultWritingMode( nVal == SvxFrameDirection::Horizontal_RL_TB ? css::text::WritingMode_RL_TB : css::text::WritingMode_LR_TB ); + } + + mpDoc->SetChanged(); + + // BackgroundFill of Masterpage: no hard attributes allowed + SdrPage& rUsedMasterPage = mpPage->IsMasterPage() ? *mpPage : mpPage->TRG_GetMasterPage(); + OSL_ENSURE(rUsedMasterPage.IsMasterPage(), "No MasterPage (!)"); + rUsedMasterPage.getSdrPageProperties().ClearItem(); + OSL_ENSURE(nullptr != rUsedMasterPage.getSdrPageProperties().GetStyleSheet(), + "MasterPage without StyleSheet detected (!)"); + } + + aNewAttr.Put(*pTempSet); + mrReq.Done( aNewAttr ); + + return mrReq.GetArgs(); + } + else + { + return nullptr; + } +} + +void FuPage::ApplyItemSet( const SfxItemSet* pArgs ) +{ + if (!pArgs || !mpDrawViewShell) + return; + + // Set new page-attributes + PageKind ePageKind = mpDrawViewShell->GetPageKind(); + const SfxPoolItem* pPoolItem; + bool bSetPageSizeAndBorder = false; + Size aNewSize(maSize); + sal_Int32 nLeft = -1, nRight = -1, nUpper = -1, nLower = -1; + bool bScaleAll = true; + Orientation eOrientation = mpPage->GetOrientation(); + SdPage* pMasterPage = mpPage->IsMasterPage() ? mpPage : &static_cast(mpPage->TRG_GetMasterPage()); + bool bFullSize = pMasterPage->IsBackgroundFullSize(); + sal_uInt16 nPaperBin = mpPage->GetPaperBin(); + + if( pArgs->GetItemState(SID_ATTR_PAGE, true, &pPoolItem) == SfxItemState::SET ) + { + mpDoc->SetPageNumType(static_cast(pPoolItem)->GetNumType()); + + eOrientation = static_cast(pPoolItem)->IsLandscape() ? + Orientation::Landscape : Orientation::Portrait; + + if( mpPage->GetOrientation() != eOrientation ) + bSetPageSizeAndBorder = true; + + mpDrawViewShell->ResetActualPage(); + } + + if( pArgs->GetItemState(SID_ATTR_PAGE_SIZE, true, &pPoolItem) == SfxItemState::SET ) + { + aNewSize = static_cast(pPoolItem)->GetSize(); + + if( mpPage->GetSize() != aNewSize ) + bSetPageSizeAndBorder = true; + } + + if( pArgs->GetItemState(mpDoc->GetPool().GetWhich(SID_ATTR_LRSPACE), + true, &pPoolItem) == SfxItemState::SET ) + { + nLeft = static_cast(pPoolItem)->GetLeft(); + nRight = static_cast(pPoolItem)->GetRight(); + + if( mpPage->GetLeftBorder() != nLeft || mpPage->GetRightBorder() != nRight ) + bSetPageSizeAndBorder = true; + + } + + if( pArgs->GetItemState(mpDoc->GetPool().GetWhich(SID_ATTR_ULSPACE), + true, &pPoolItem) == SfxItemState::SET ) + { + nUpper = static_cast(pPoolItem)->GetUpper(); + nLower = static_cast(pPoolItem)->GetLower(); + + if( mpPage->GetUpperBorder() != nUpper || mpPage->GetLowerBorder() != nLower ) + bSetPageSizeAndBorder = true; + } + + if( pArgs->GetItemState(mpDoc->GetPool().GetWhich(SID_ATTR_PAGE_EXT1), true, &pPoolItem) == SfxItemState::SET ) + { + bScaleAll = static_cast(pPoolItem)->GetValue(); + } + + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_CHAR_GRABBAG, true, &pPoolItem)) + { + SfxGrabBagItem const*const pGrabBag(static_cast(pPoolItem)); + if (pGrabBag->GetGrabBag().find("BackgroundFullSize")->second >>= bFullSize) + { + if (pMasterPage->IsBackgroundFullSize() != bFullSize) + { + bSetPageSizeAndBorder = true; + } + } + + if (mpDoc->GetDocumentType() == DocumentType::Impress && mpPage->IsMasterPage()) + { + // The item set may have a theme. + auto it = pGrabBag->GetGrabBag().find("Theme"); + if (it != pGrabBag->GetGrabBag().end()) + { + std::unique_ptr pTheme = svx::Theme::FromAny(it->second); + pMasterPage->getSdrPageProperties().SetTheme(std::move(pTheme)); + } + else + { + SAL_WARN("sd.ui", "FuPage::ApplyItemSet: got no theme"); + } + } + } + + // Paper Bin + if( pArgs->GetItemState(mpDoc->GetPool().GetWhich(SID_ATTR_PAGE_PAPERBIN), true, &pPoolItem) == SfxItemState::SET ) + { + nPaperBin = static_cast(pPoolItem)->GetValue(); + + if( mpPage->GetPaperBin() != nPaperBin ) + bSetPageSizeAndBorder = true; + } + + if (nLeft == -1 && nUpper != -1) + { + bSetPageSizeAndBorder = true; + nLeft = mpPage->GetLeftBorder(); + nRight = mpPage->GetRightBorder(); + } + else if (nLeft != -1 && nUpper == -1) + { + bSetPageSizeAndBorder = true; + nUpper = mpPage->GetUpperBorder(); + nLower = mpPage->GetLowerBorder(); + } + + if( bSetPageSizeAndBorder || !mbMasterPage ) + mpDrawViewShell->SetPageSizeAndBorder(ePageKind, aNewSize, nLeft, nRight, nUpper, nLower, bScaleAll, eOrientation, nPaperBin, bFullSize ); + + // if bMasterPage==sal_False then create a background-object for this page with the + // properties set in the dialog before, but if mbPageBckgrdDeleted==sal_True then + // the background of this page was set to invisible, so it would be a mistake + // to create a new background-object for this page ! + + if( mbDisplayBackgroundTabPage ) + { + if( !mbMasterPage && !mbPageBckgrdDeleted ) + { + // Only this page + mpBackgroundObjUndoAction.reset( new SdBackgroundObjUndoAction( + *mpDoc, *mpPage, mpPage->getSdrPageProperties().GetItemSet()) ); + SfxItemSet aSet( *pArgs ); + sdr::properties::CleanupFillProperties(aSet); + mpPage->getSdrPageProperties().ClearItem(); + mpPage->getSdrPageProperties().PutItemSet(aSet); + } + } + + // add undo action for background object + if( mpBackgroundObjUndoAction ) + { + // set merge flag, because a SdUndoGroupAction could have been inserted before + mpDocSh->GetUndoManager()->AddUndoAction( std::move(mpBackgroundObjUndoAction), true ); + } + + // Objects can not be bigger than ViewSize + Size aPageSize = mpDoc->GetSdPage(0, ePageKind)->GetSize(); + Size aViewSize(aPageSize.Width() * 3, aPageSize.Height() * 2); + mpDoc->SetMaxObjSize(aViewSize); + + // if necessary, we tell Preview the new context + mpDrawViewShell->UpdatePreview( mpDrawViewShell->GetActualPage() ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuparagr.cxx b/sd/source/ui/func/fuparagr.cxx new file mode 100644 index 000000000..ac5d87636 --- /dev/null +++ b/sd/source/ui/func/fuparagr.cxx @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +namespace sd { + + +FuParagraph::FuParagraph ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuParagraph::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuParagraph( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuParagraph::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + + OutlinerView* pOutlView = mpView->GetTextEditOutlinerView(); + ::Outliner* pOutliner = mpView->GetTextEditOutliner(); + + if( !pArgs ) + { + SfxItemSet aEditAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aEditAttr ); + SfxItemPool *pPool = aEditAttr.GetPool(); + SfxItemSetFixed aNewAttr( *pPool ); + + aNewAttr.Put( aEditAttr ); + + // left border is offset + const ::tools::Long nOff = aNewAttr.Get( EE_PARA_LRSPACE ).GetTextLeft(); + // conversion since TabulatorTabPage always uses Twips! + SfxInt32Item aOff( SID_ATTR_TABSTOP_OFFSET, nOff ); + aNewAttr.Put( aOff ); + + if( pOutlView && pOutliner ) + { + ESelection eSelection = pOutlView->GetSelection(); + aNewAttr.Put( SfxInt16Item( ATTR_NUMBER_NEWSTART_AT, pOutliner->GetNumberingStartValue( eSelection.nStartPara ) ) ); + aNewAttr.Put( SfxBoolItem( ATTR_NUMBER_NEWSTART, pOutliner->IsParaIsNumberingRestart( eSelection.nStartPara ) ) ); + } + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSdParagraphTabDlg(mpViewShell->GetFrameWeld(), &aNewAttr)); + + sal_uInt16 nResult = pDlg->Execute(); + + switch( nResult ) + { + case RET_OK: + { + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + + pArgs = rReq.GetArgs(); + } + break; + + default: + return; // Cancel + } + } + mpView->SetAttributes( *pArgs ); + + if( pOutlView && pOutliner ) + { + ESelection eSelection = pOutlView->GetSelection(); + + if( const SfxBoolItem* pItem = pArgs->GetItemIfSet( ATTR_NUMBER_NEWSTART, false ) ) + { + const bool bNewStart = pItem->GetValue(); + pOutliner->SetParaIsNumberingRestart( eSelection.nStartPara, bNewStart ); + } + + if( const SfxInt16Item* pItem = pArgs->GetItemIfSet( ATTR_NUMBER_NEWSTART_AT, false ) ) + { + const sal_Int16 nStartAt = pItem->GetValue(); + pOutliner->SetNumberingStartValue( eSelection.nStartPara, nStartAt ); + } + } + + // invalidate slots + static const sal_uInt16 SidArray[] = { + SID_ATTR_TABSTOP, + SID_ATTR_PARA_ADJUST_LEFT, + SID_ATTR_PARA_ADJUST_RIGHT, + SID_ATTR_PARA_ADJUST_CENTER, + SID_ATTR_PARA_ADJUST_BLOCK, + SID_ATTR_PARA_LINESPACE, + SID_ATTR_PARA_LINESPACE_10, + SID_ATTR_PARA_LINESPACE_15, + SID_ATTR_PARA_LINESPACE_20, + SID_ATTR_PARA_ULSPACE, + SID_ATTR_PARA_LRSPACE, + SID_DEC_INDENT, + SID_INC_INDENT, + SID_ATTR_PARA_LEFT_TO_RIGHT, + SID_ATTR_PARA_RIGHT_TO_LEFT, + SID_RULER_TEXT_RIGHT_TO_LEFT, + SID_PARASPACE_INCREASE, + SID_PARASPACE_DECREASE, + 0 }; + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); +} + +void FuParagraph::Activate() +{ +} + +void FuParagraph::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fupoor.cxx b/sd/source/ui/func/fupoor.cxx new file mode 100644 index 000000000..e901d07a6 --- /dev/null +++ b/sd/source/ui/func/fupoor.cxx @@ -0,0 +1,1135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include + +using namespace ::com::sun::star; + +namespace sd { + + +FuPoor::FuPoor ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDrDoc, + SfxRequest& rReq) + : mpView(pView), + mpViewShell(pViewSh), + mpWindow(pWin), + mpDocSh( pDrDoc->GetDocSh() ), + mpDoc(pDrDoc), + nSlotId( rReq.GetSlot() ), + aScrollTimer("sd FuPoor aScrollTimer"), + aDragTimer("sd FuPoor aDragTimer"), + bIsInDragMode(false), + bNoScrollUntilInside (true), + aDelayToScrollTimer("sd FuPoor aDelayToScrollTimer"), + bScrollable (false), + bDelayActive (false), + bFirstMouseMove (false), + // remember MouseButton state + mnCode(0) +{ + ReceiveRequest(rReq); + + aScrollTimer.SetInvokeHandler( LINK(this, FuPoor, ScrollHdl) ); + aScrollTimer.SetTimeout(SELENG_AUTOREPEAT_INTERVAL); + + aDragTimer.SetInvokeHandler( LINK(this, FuPoor, DragHdl) ); + aDragTimer.SetTimeout(SELENG_DRAGDROP_TIMEOUT); + + aDelayToScrollTimer.SetInvokeHandler( LINK(this, FuPoor, DelayHdl) ); + aDelayToScrollTimer.SetTimeout(2000); +} + +FuPoor::~FuPoor() +{ + aDragTimer.Stop(); + aScrollTimer.Stop(); + aDelayToScrollTimer.Stop(); +} + +void FuPoor::Activate() +{ +} + +void FuPoor::Deactivate() +{ + aDragTimer.Stop(); + aScrollTimer.Stop(); + aDelayToScrollTimer.Stop (); + bScrollable = bDelayActive = false; + + if (mpWindow && mpWindow->IsMouseCaptured()) + mpWindow->ReleaseMouse(); +} + +void FuPoor::SetWindow(::sd::Window* pWin) +{ + mpWindow = pWin; +} + +/** + * scroll when approached the border of the window; is called by MouseMove + */ +void FuPoor::ForceScroll(const Point& aPixPos) +{ + aScrollTimer.Stop(); + + if ( mpView->IsDragHelpLine() || mpView->IsSetPageOrg() || + SlideShow::IsRunning( mpViewShell->GetViewShellBase() ) ) + return; + + Point aPos = mpWindow->OutputToScreenPixel(aPixPos); + const ::tools::Rectangle& rRect = mpViewShell->GetAllWindowRect(); + + if ( bNoScrollUntilInside ) + { + if ( rRect.Contains(aPos) ) + bNoScrollUntilInside = false; + } + else + { + short dx = 0, dy = 0; + + if ( aPos.X() <= rRect.Left() ) dx = -1; + if ( aPos.X() >= rRect.Right() ) dx = 1; + if ( aPos.Y() <= rRect.Top() ) dy = -1; + if ( aPos.Y() >= rRect.Bottom() ) dy = 1; + + if ( dx != 0 || dy != 0 ) + { + if (bScrollable) + { + // scroll action in derived class + mpViewShell->ScrollLines(dx, dy); + aScrollTimer.Start(); + } + else if (! bDelayActive) StartDelayToScrollTimer (); + } + } +} + +/** + * timer handler for window scrolling + */ +IMPL_LINK_NOARG(FuPoor, ScrollHdl, Timer *, void) +{ + Point aPnt(mpWindow->GetPointerPosPixel()); + + // use remembered MouseButton state to create correct + // MouseEvents for this artificial MouseMove. + MouseMove(MouseEvent(aPnt, 1, MouseEventModifiers::NONE, GetMouseButtonCode())); +} + +/** + * handle keyboard events + * @returns sal_True if the event was handled, sal_False otherwise + */ +bool FuPoor::KeyInput(const KeyEvent& rKEvt) +{ + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + bool bReturn = false; + bool bSlideShow = SlideShow::IsRunning( mpViewShell->GetViewShellBase() ); + + switch (nCode) + { + case KEY_RETURN: + { + if(rKEvt.GetKeyCode().IsMod1()) + { + if( auto pDrawViewShell = dynamic_cast( mpViewShell )) + { + SdPage* pActualPage = pDrawViewShell->GetActualPage(); + SdrTextObj* pCandidate = nullptr; + + if(pActualPage) + { + SdrObjListIter aIter(pActualPage, SdrIterMode::DeepNoGroups); + + while(aIter.IsMore() && !pCandidate) + { + SdrObject* pObj = aIter.Next(); + + if(auto pTextObj = dynamic_cast( pObj )) + { + SdrInventor nInv(pObj->GetObjInventor()); + SdrObjKind nKnd(pObj->GetObjIdentifier()); + + if(SdrInventor::Default == nInv && + (SdrObjKind::TitleText == nKnd || SdrObjKind::OutlineText == nKnd || SdrObjKind::Text == nKnd)) + { + pCandidate = pTextObj; + } + } + } + } + + if(pCandidate) + { + mpView->UnMarkAll(); + mpView->MarkObj(pCandidate, mpView->GetSdrPageView()); + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_ATTR_CHAR, SfxCallMode::ASYNCHRON); + } + else + { + // insert a new page with the same page layout + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_INSERTPAGE_QUICK, SfxCallMode::ASYNCHRON); + } + + // consumed + bReturn = true; + } + } + else + { + // activate OLE object on RETURN for selected object + // activate text edit on RETURN for selected object + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if( !mpView->IsTextEdit() && 1 == rMarkList.GetMarkCount() ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + if( dynamic_cast< const SdrOle2Obj* >( pObj ) && !mpDocSh->IsUIActive() ) + { + //HMHmpView->HideMarkHdl(); + mpViewShell->ActivateObject(static_cast(pObj), css::embed::EmbedVerbs::MS_OLEVERB_PRIMARY); + } + else if( pObj && pObj->IsEmptyPresObj() && dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr ) + { + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_INSERT_GRAPHIC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + else + { + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_ATTR_CHAR, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + + // consumed + bReturn = true; + } + } + } + break; + + case KEY_TAB: + { + // handle Mod1 and Mod2 to get travelling running on different systems + if(rKEvt.GetKeyCode().IsMod1() || rKEvt.GetKeyCode().IsMod2()) + { + // do something with a selected handle? + const SdrHdlList& rHdlList = mpView->GetHdlList(); + bool bForward(!rKEvt.GetKeyCode().IsShift()); + + const_cast(rHdlList).TravelFocusHdl(bForward); + + // guarantee visibility of focused handle + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + Point aHdlPosition(pHdl->GetPos()); + ::tools::Rectangle aVisRect(aHdlPosition - Point(100, 100), Size(200, 200)); + mpView->MakeVisible(aVisRect, *mpWindow); + } + + // consumed + bReturn = true; + } + } + break; + + case KEY_ESCAPE: + { + bReturn = FuPoor::cancel(); + } + break; + + case KEY_ADD: + { + if (!mpView->IsTextEdit() && !bSlideShow && !mpDocSh->IsUIActive()) + { + // increase zoom + mpViewShell->SetZoom(mpWindow->GetZoom() * 3 / 2); + + if( auto pViewShell = dynamic_cast( mpViewShell )) + pViewShell->SetZoomOnPage(false); + + bReturn = true; + } + } + break; + + case KEY_SUBTRACT: + { + if (!mpView->IsTextEdit() && !bSlideShow && !mpDocSh->IsUIActive()) + { + // decrease zoom + mpViewShell->SetZoom(mpWindow->GetZoom() * 2 / 3); + + if( auto pViewShell = dynamic_cast( mpViewShell )) + pViewShell->SetZoomOnPage(false); + + bReturn = true; + } + } + break; + + case KEY_MULTIPLY: + { + if (!mpView->IsTextEdit() && !bSlideShow) + { + // zoom to page + mpViewShell->GetViewFrame()->GetDispatcher()-> + Execute(SID_SIZE_PAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + bReturn = true; + } + } + break; + + case KEY_DIVIDE: + { + if (!mpView->IsTextEdit() && !bSlideShow) + { + // zoom to selected objects + mpViewShell->GetViewFrame()->GetDispatcher()-> + Execute(SID_SIZE_OPTIMAL, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + bReturn = true; + } + } + break; + + case KEY_POINT: + { + ZoomList* pZoomList = mpViewShell->GetZoomList(); + + if (!mpView->IsTextEdit() && pZoomList->IsNextPossible() && !bSlideShow && !mpDocSh->IsUIActive()) + { + // use next ZoomRect + mpViewShell->SetZoomRect(pZoomList->GetNextZoomRect()); + bReturn = true; + } + } + break; + + case KEY_COMMA: + { + ZoomList* pZoomList = mpViewShell->GetZoomList(); + + if (!mpView->IsTextEdit() && pZoomList->IsPreviousPossible() && !bSlideShow && !mpDocSh->IsUIActive()) + { + // use previous ZoomRect + mpViewShell->SetZoomRect(pZoomList->GetPreviousZoomRect()); + bReturn = true; + } + } + break; + + case KEY_HOME: + { + if (!mpView->IsTextEdit() && !bSlideShow) + if (auto pDrawViewShell = dynamic_cast( mpViewShell )) + { + // jump to first page + pDrawViewShell->SwitchPage(0); + bReturn = true; + } + } + break; + + case KEY_END: + { + if (!mpView->IsTextEdit() && !bSlideShow) + if (auto pDrawViewShell = dynamic_cast( mpViewShell )) + { + // jump to last page + SdPage* pPage = pDrawViewShell->GetActualPage(); + pDrawViewShell->SwitchPage(mpDoc->GetSdPageCount( + pPage->GetPageKind()) - 1); + bReturn = true; + } + } + break; + + case KEY_PAGEUP: + { + if( rKEvt.GetKeyCode().IsMod1() && rKEvt.GetKeyCode().IsMod2() ) + break; + if( bSlideShow) + break; + + if( auto pDrawViewShell = dynamic_cast( mpViewShell ) ) + { + // The page-up key switches layers or pages depending on the + // modifier key. + if ( ! rKEvt.GetKeyCode().GetModifier()) + { + // With no modifier pressed we move to the previous + // slide. + mpView->SdrEndTextEdit(); + + // Previous page. + bReturn = true; + SdPage* pPage = pDrawViewShell->GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + + if (nSdPage > 0) + { + // Switch the page and send events regarding + // deactivation the old page and activating the new + // one. + TabControl& rPageTabControl = + pDrawViewShell->GetPageTabControl(); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendDeactivatePageEvent (); + pDrawViewShell->SwitchPage(nSdPage - 1); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendActivatePageEvent (); + } + } + else if (rKEvt.GetKeyCode().IsMod1()) + { + // With the CONTROL modifier we switch layers. + if (pDrawViewShell->IsLayerModeActive()) + { + // Moves to the previous layer. + SwitchLayer (-1); + } + } + } + } + break; + + case KEY_PAGEDOWN: + { + if( rKEvt.GetKeyCode().IsMod1() && rKEvt.GetKeyCode().IsMod2() ) + break; + if(dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr && !bSlideShow) + { + // The page-down key switches layers or pages depending on the + // modifier key. + if ( ! rKEvt.GetKeyCode().GetModifier()) + { + // With no modifier pressed we move to the next slide. + mpView->SdrEndTextEdit(); + + // Next page. + bReturn = true; + SdPage* pPage = static_cast(mpViewShell)->GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + + if (nSdPage < mpDoc->GetSdPageCount(pPage->GetPageKind()) - 1) + { + // Switch the page and send events regarding + // deactivation the old page and activating the new + // one. + TabControl& rPageTabControl = + static_cast(mpViewShell)->GetPageTabControl(); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendDeactivatePageEvent (); + static_cast(mpViewShell)->SwitchPage(nSdPage + 1); + if (rPageTabControl.IsReallyShown()) + rPageTabControl.SendActivatePageEvent (); + } + } + else if (rKEvt.GetKeyCode().IsMod1()) + { + // With the CONTROL modifier we switch layers. + if (static_cast(mpViewShell)->IsLayerModeActive()) + { + // With the layer mode active pressing page-down + // moves to the next layer. + SwitchLayer (+1); + } + } + } + } + break; + + // change select state when focus is on poly point + case KEY_SPACE: + { + const SdrHdlList& rHdlList = mpView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + if(pHdl) + { + if(pHdl->GetKind() == SdrHdlKind::Poly) + { + // rescue ID of point with focus + sal_uInt32 nPol(pHdl->GetPolyNum()); + sal_uInt32 nPnt(pHdl->GetPointNum()); + + if(mpView->IsPointMarked(*pHdl)) + { + if(rKEvt.GetKeyCode().IsShift()) + { + mpView->UnmarkPoint(*pHdl); + } + } + else + { + if(!rKEvt.GetKeyCode().IsShift()) + { + mpView->UnmarkAllPoints(); + } + + mpView->MarkPoint(*pHdl); + } + + if(nullptr == rHdlList.GetFocusHdl()) + { + // restore point with focus + SdrHdl* pNewOne = nullptr; + + for(size_t a = 0; !pNewOne && a < rHdlList.GetHdlCount(); ++a) + { + SdrHdl* pAct = rHdlList.GetHdl(a); + + if(pAct + && pAct->GetKind() == SdrHdlKind::Poly + && pAct->GetPolyNum() == nPol + && pAct->GetPointNum() == nPnt) + { + pNewOne = pAct; + } + } + + if(pNewOne) + { + const_cast(rHdlList).SetFocusHdl(pNewOne); + } + } + + bReturn = true; + } + } + } + break; + + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + if (!mpView->IsTextEdit() && !bSlideShow) + { + ::tools::Long nX = 0; + ::tools::Long nY = 0; + + if (nCode == KEY_UP) + { + // scroll up + nX = 0; + nY =-1; + } + else if (nCode == KEY_DOWN) + { + // scroll down + nX = 0; + nY = 1; + } + else if (nCode == KEY_LEFT) + { + // scroll left + nX =-1; + nY = 0; + } + else if (nCode == KEY_RIGHT) + { + // scroll right + nX = 1; + nY = 0; + } + + if (mpView->AreObjectsMarked() && !rKEvt.GetKeyCode().IsMod1() && + !mpDocSh->IsReadOnly()) + { + const SdrHdlList& rHdlList = mpView->GetHdlList(); + SdrHdl* pHdl = rHdlList.GetFocusHdl(); + + bool bIsMoveOfConnectedHandle(false); + bool bOldSuppress = false; + SdrEdgeObj* pEdgeObj = nullptr; + if(pHdl) + pEdgeObj = dynamic_cast( pHdl->GetObj() ); + + if(pEdgeObj && 0 == pHdl->GetPolyNum()) + { + if(0 == pHdl->GetPointNum()) + { + if(pEdgeObj->GetConnection(true).GetObject()) + { + bIsMoveOfConnectedHandle = true; + } + } + if(1 == pHdl->GetPointNum()) + { + if(pEdgeObj->GetConnection(false).GetObject()) + { + bIsMoveOfConnectedHandle = true; + } + } + } + + if(pEdgeObj) + { + // Suppress default connects to inside object and object center + bOldSuppress = pEdgeObj->GetSuppressDefaultConnect(); + pEdgeObj->SetSuppressDefaultConnect(true); + } + + if(bIsMoveOfConnectedHandle) + { + sal_uInt16 nMarkHdSiz(mpView->GetMarkHdlSizePixel()); + Size aHalfConSiz(nMarkHdSiz + 1, nMarkHdSiz + 1); + aHalfConSiz = mpWindow->PixelToLogic(aHalfConSiz); + + if(100 < aHalfConSiz.Width()) + nX *= aHalfConSiz.Width(); + else + nX *= 100; + + if(100 < aHalfConSiz.Height()) + nY *= aHalfConSiz.Height(); + else + nY *= 100; + } + else if(rKEvt.GetKeyCode().IsMod2()) + { + // move in 1 pixel distance + Size aLogicSizeOnePixel = mpWindow->PixelToLogic(Size(1,1)); + nX *= aLogicSizeOnePixel.Width(); + nY *= aLogicSizeOnePixel.Height(); + } + else if(rKEvt.GetKeyCode().IsShift()) + { + nX *= 1000; + nY *= 1000; + } + else + { + // old, fixed move distance + nX *= 100; + nY *= 100; + } + + if(nullptr == pHdl) + { + // only take action when move is allowed + if(mpView->IsMoveAllowed()) + { + // restrict movement to WorkArea + const ::tools::Rectangle& rWorkArea = mpView->GetWorkArea(); + + if(!rWorkArea.IsEmpty()) + { + ::tools::Rectangle aMarkRect(mpView->GetMarkedObjRect()); + aMarkRect.Move(nX, nY); + + if(!aMarkRect.Contains(rWorkArea)) + { + if(aMarkRect.Left() < rWorkArea.Left()) + { + nX += rWorkArea.Left() - aMarkRect.Left(); + } + + if(aMarkRect.Right() > rWorkArea.Right()) + { + nX -= aMarkRect.Right() - rWorkArea.Right(); + } + + if(aMarkRect.Top() < rWorkArea.Top()) + { + nY += rWorkArea.Top() - aMarkRect.Top(); + } + + if(aMarkRect.Bottom() > rWorkArea.Bottom()) + { + nY -= aMarkRect.Bottom() - rWorkArea.Bottom(); + } + } + } + + // no handle selected + if(0 != nX || 0 != nY) + { + mpView->MoveAllMarked(Size(nX, nY)); + + mpView->MakeVisible(mpView->GetAllMarkedRect(), *mpWindow); + } + } + } + else + { + // move handle with index nHandleIndex + if (nX || nY) + { + // now move the Handle (nX, nY) + Point aStartPoint(pHdl->GetPos()); + Point aEndPoint(pHdl->GetPos() + Point(nX, nY)); + const SdrDragStat& rDragStat = mpView->GetDragStat(); + + // start dragging + mpView->BegDragObj(aStartPoint, nullptr, pHdl, 0); + + if(mpView->IsDragObj()) + { + bool bWasNoSnap = rDragStat.IsNoSnap(); + bool bWasSnapEnabled = mpView->IsSnapEnabled(); + + // switch snapping off + if(!bWasNoSnap) + const_cast(rDragStat).SetNoSnap(); + if(bWasSnapEnabled) + mpView->SetSnapEnabled(false); + + mpView->MovAction(aEndPoint); + mpView->EndDragObj(); + + // restore snap + if(!bWasNoSnap) + const_cast(rDragStat).SetNoSnap(bWasNoSnap); + if(bWasSnapEnabled) + mpView->SetSnapEnabled(bWasSnapEnabled); + } + + // make moved handle visible + ::tools::Rectangle aVisRect(aEndPoint - Point(100, 100), Size(200, 200)); + mpView->MakeVisible(aVisRect, *mpWindow); + } + } + + if(pEdgeObj) + { + // Restore original suppress value + pEdgeObj->SetSuppressDefaultConnect(bOldSuppress); + } + } + else + { + // scroll page + mpViewShell->ScrollLines(nX, nY); + } + + bReturn = true; + } + } + break; + } + + if (bReturn) + { + mpWindow->ReleaseMouse(); + } + + // when a text-editable object is selected and the + // input character is printable, activate text edit on that object + // and feed character to object + if(!bReturn && !mpDocSh->IsReadOnly()) + { + if (!mpView->IsTextEdit()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if(1 == rMarkList.GetMarkCount()) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + // #i118485# allow TextInput for OLEs, too + if( dynamic_cast< const SdrTextObj *>( pObj ) != nullptr && pObj->HasTextEdit()) + { + // use common IsSimpleCharInput from the EditEngine. + bool bPrintable(EditEngine::IsSimpleCharInput(rKEvt)); + + if(bPrintable) + { + // try to activate textedit mode for the selected object + SfxStringItem aInputString(SID_ATTR_CHAR, OUString(rKEvt.GetCharCode())); + + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_ATTR_CHAR, + SfxCallMode::ASYNCHRON, + { &aInputString }); + + // consumed + bReturn = true; + } + } + } + else + { + // test if there is a title object there. If yes, try to + // set it to edit mode and start typing... + DrawViewShell* pDrawViewShell = dynamic_cast(mpViewShell); + if (pDrawViewShell && EditEngine::IsSimpleCharInput(rKEvt)) + { + SdPage* pActualPage = pDrawViewShell->GetActualPage(); + SdrTextObj* pCandidate = nullptr; + + if(pActualPage) + { + SdrObjListIter aIter(pActualPage, SdrIterMode::DeepNoGroups); + + while(aIter.IsMore() && !pCandidate) + { + SdrObject* pObj = aIter.Next(); + + if(auto pTextObj = dynamic_cast< SdrTextObj *>( pObj )) + { + SdrInventor nInv(pObj->GetObjInventor()); + SdrObjKind nKnd(pObj->GetObjIdentifier()); + + if(SdrInventor::Default == nInv && SdrObjKind::TitleText == nKnd) + { + pCandidate = pTextObj; + } + } + } + } + + // when candidate found and candidate is untouched, start editing text... + if(pCandidate && pCandidate->IsEmptyPresObj()) + { + mpView->UnMarkAll(); + mpView->MarkObj(pCandidate, mpView->GetSdrPageView()); + SfxStringItem aInputString(SID_ATTR_CHAR, OUString(rKEvt.GetCharCode())); + + mpViewShell->GetViewFrame()->GetDispatcher()->ExecuteList( + SID_ATTR_CHAR, + SfxCallMode::ASYNCHRON, + { &aInputString }); + + // consumed + bReturn = true; + } + } + } + } + } + + return bReturn; +} + +bool FuPoor::MouseMove(const MouseEvent& ) +{ + return false; +} + +void FuPoor::SelectionHasChanged() +{ + const SdrHdlList& rHdlList = mpView->GetHdlList(); + const_cast(rHdlList).ResetFocusHdl(); +} + +/** + * Cut object to clipboard + */ +void FuPoor::DoCut() +{ + if (mpView) + { + mpView->DoCut(); + } +} + +/** + * Copy object to clipboard + */ +void FuPoor::DoCopy() +{ + if (mpView) + { + mpView->DoCopy(); + } +} + +/** + * Paste object from clipboard + */ +void FuPoor::DoPaste() +{ + if (mpView) + { + mpView->DoPaste(mpWindow); + } +} + +/** + * Paste unformatted text from clipboard + */ +void FuPoor::DoPasteUnformatted() +{ + if (mpView) + { + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mpViewShell->GetActiveWindow() ) ); + if (aDataHelper.GetTransferable().is()) + { + sal_Int8 nAction = DND_ACTION_COPY; + mpView->InsertData( aDataHelper, + mpWindow->PixelToLogic( ::tools::Rectangle( Point(), mpWindow->GetOutputSizePixel() ).Center() ), + nAction, false, SotClipboardFormatId::STRING); + } + } +} + +/** + * Timer handler for Drag&Drop + */ +IMPL_LINK_NOARG(FuPoor, DragHdl, Timer *, void) +{ + if( !mpView ) + return; + + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + SdrHdl* pHdl = mpView->PickHandle(aMDPos); + + if ( pHdl==nullptr && mpView->IsMarkedHit(aMDPos, nHitLog) + && !mpView->IsPresObjSelected(false) ) + { + mpWindow->ReleaseMouse(); + bIsInDragMode = true; + mpView->StartDrag( aMDPos, mpWindow ); + } +} + +bool FuPoor::Command(const CommandEvent& rCEvt) +{ + return mpView->Command(rCEvt,mpWindow); +} + +/** + * Timer handler for window scrolling + */ +IMPL_LINK_NOARG(FuPoor, DelayHdl, Timer *, void) +{ + aDelayToScrollTimer.Stop (); + bScrollable = true; + + Point aPnt(mpWindow->GetPointerPosPixel()); + + // use remembered MouseButton state to create correct + // MouseEvents for this artificial MouseMove. + MouseMove(MouseEvent(aPnt, 1, MouseEventModifiers::NONE, GetMouseButtonCode())); +} + +bool FuPoor::MouseButtonUp (const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + aDelayToScrollTimer.Stop (); + bScrollable = bDelayActive = false; + return bScrollable; +} + +bool FuPoor::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + return false; +} + +void FuPoor::StartDelayToScrollTimer () +{ + bDelayActive = true; + aDelayToScrollTimer.Start (); +} + +bool FuPoor::RequestHelp(const HelpEvent& rHEvt) +{ + bool bReturn = false; + + SdrPageView* pPV = mpView->GetSdrPageView(); + + if (pPV) + { + SdPage* pPage = static_cast( pPV->GetPage() ); + + if (pPage) + { + bReturn = FmFormPage::RequestHelp(mpWindow, mpView, rHEvt); + } + } + + return bReturn; +} + +void FuPoor::ReceiveRequest(SfxRequest& /*rReq*/) +{ +} + +SdrObjectUniquePtr FuPoor::CreateDefaultObject(const sal_uInt16, const ::tools::Rectangle& ) +{ + // empty base implementation + return nullptr; +} + +void FuPoor::ImpForceQuadratic(::tools::Rectangle& rRect) +{ + if(rRect.GetWidth() > rRect.GetHeight()) + { + rRect = ::tools::Rectangle( + Point(rRect.Left() + ((rRect.GetWidth() - rRect.GetHeight()) / 2), rRect.Top()), + Size(rRect.GetHeight(), rRect.GetHeight())); + } + else + { + rRect = ::tools::Rectangle( + Point(rRect.Left(), rRect.Top() + ((rRect.GetHeight() - rRect.GetWidth()) / 2)), + Size(rRect.GetWidth(), rRect.GetWidth())); + } +} + +void FuPoor::SwitchLayer (sal_Int32 nOffset) +{ + auto pDrawViewShell = dynamic_cast( mpViewShell ); + if(!pDrawViewShell) + return; + + // Calculate the new index. + sal_Int32 nIndex = pDrawViewShell->GetActiveTabLayerIndex() + nOffset; + + // Make sure the new index lies inside the range of valid indices. + if (nIndex < 0) + nIndex = 0; + else if (nIndex >= pDrawViewShell->GetTabLayerCount ()) + nIndex = pDrawViewShell->GetTabLayerCount() - 1; + + // Set the new active layer. + if (nIndex != pDrawViewShell->GetActiveTabLayerIndex ()) + { + LayerTabBar* pLayerTabControl = + static_cast(mpViewShell)->GetLayerTabControl(); + if (pLayerTabControl != nullptr) + pLayerTabControl->SendDeactivatePageEvent (); + + pDrawViewShell->SetActiveTabLayerIndex (nIndex); + + if (pLayerTabControl != nullptr) + pLayerTabControl->SendActivatePageEvent (); + } +} + +/** is called when the current function should be aborted.

+ This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted +*/ +bool FuPoor::cancel() +{ + if ( dynamic_cast< const FuSelection *>( this ) == nullptr ) + { + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + return true; + } + + return false; +} + +// #i33136# +bool FuPoor::doConstructOrthogonal() const +{ + // Check whether a media object is selected + bool bResizeKeepRatio = false; + // tdf#89758 Avoid interactive crop preview from being proportionally scaled by default. + if (mpView->AreObjectsMarked() && mpView->GetDragMode() != SdrDragMode::Crop) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1) + { + SdrObjKind aObjIdentifier = rMarkList.GetMark(0)->GetMarkedSdrObj()->GetObjIdentifier(); + bResizeKeepRatio = aObjIdentifier == SdrObjKind::Graphic || + aObjIdentifier == SdrObjKind::Media || + aObjIdentifier == SdrObjKind::OLE2; + } + } + SdrHdl* pHdl = mpView->PickHandle(aMDPos); + // Resize proportionally when media is selected and the user drags on a corner + if (pHdl) + bResizeKeepRatio = bResizeKeepRatio && pHdl->IsCornerHdl(); + + return ( + bResizeKeepRatio || + SID_DRAW_XLINE == nSlotId || + SID_DRAW_CIRCLEARC == nSlotId || + SID_DRAW_SQUARE == nSlotId || + SID_DRAW_SQUARE_NOFILL == nSlotId || + SID_DRAW_SQUARE_ROUND == nSlotId || + SID_DRAW_SQUARE_ROUND_NOFILL == nSlotId || + SID_DRAW_CIRCLE == nSlotId || + SID_DRAW_CIRCLE_NOFILL == nSlotId || + SID_DRAW_CIRCLEPIE == nSlotId || + SID_DRAW_CIRCLEPIE_NOFILL == nSlotId || + SID_DRAW_CIRCLECUT == nSlotId || + SID_DRAW_CIRCLECUT_NOFILL == nSlotId || + SID_DRAW_XPOLYGON == nSlotId || + SID_DRAW_XPOLYGON_NOFILL == nSlotId || + SID_3D_CUBE == nSlotId || + SID_3D_SPHERE == nSlotId || + SID_3D_SHELL == nSlotId || + SID_3D_HALF_SPHERE == nSlotId || + SID_3D_TORUS == nSlotId || + SID_3D_CYLINDER == nSlotId || + SID_3D_CONE == nSlotId || + SID_3D_PYRAMID == nSlotId); +} + +void FuPoor::DoExecute( SfxRequest& ) +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuprlout.cxx b/sd/source/ui/func/fuprlout.cxx new file mode 100644 index 000000000..c436b78f0 --- /dev/null +++ b/sd/source/ui/func/fuprlout.cxx @@ -0,0 +1,277 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd +{ + + +#define DOCUMENT_TOKEN '#' + +FuPresentationLayout::FuPresentationLayout ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuPresentationLayout::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuPresentationLayout( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuPresentationLayout::DoExecute( SfxRequest& rReq ) +{ + // prevent selected objects or objects which are under editing from disappearing + mpView->SdrEndTextEdit(); + + if(mpView->GetSdrPageView()) + { + mpView->UnmarkAll(); + } + + bool bError = false; + + /* if we are on a master page, the changes apply for all pages and notes- + pages who are using the relevant layout */ + bool bOnMaster = false; + if (DrawViewShell *pShell = dynamic_cast(mpViewShell)) + { + EditMode eEditMode = pShell->GetEditMode(); + if (eEditMode == EditMode::MasterPage) + bOnMaster = true; + } + + std::vector aUnselect; + if (!bOnMaster) + { + //We later rely on IsSelected, so transfer the selection here + //into the document + slidesorter::SlideSorterViewShell* pSlideSorterViewShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(mpViewShell->GetViewShellBase()); + if (pSlideSorterViewShell) + { + std::shared_ptr xSelection( + pSlideSorterViewShell->GetPageSelection()); + if (xSelection) + { + for (SdPage *pPage : *xSelection) + { + if (pPage->IsSelected() || pPage->GetPageKind() != PageKind::Standard) + continue; + mpDoc->SetSelected(pPage, true); + aUnselect.push_back(pPage); + } + } + } + } + + std::vector aSelectedPages; + std::vector aSelectedPageNums; + // determine the active pages + for (sal_uInt16 nPage = 0; nPage < mpDoc->GetSdPageCount(PageKind::Standard); nPage++) + { + SdPage* pPage = mpDoc->GetSdPage(nPage, PageKind::Standard); + if (pPage->IsSelected()) + { + aSelectedPages.push_back(pPage); + aSelectedPageNums.push_back(nPage); + } + } + + bool bMasterPage = bOnMaster; + bool bCheckMasters = false; + + // call dialog + bool bLoad = false; // appear the new master pages? + OUString aFile; + + SfxItemSetFixed aSet(mpDoc->GetPool() ); + + aSet.Put( SfxBoolItem( ATTR_PRESLAYOUT_LOAD, bLoad)); + aSet.Put( SfxBoolItem( ATTR_PRESLAYOUT_MASTER_PAGE, bMasterPage ) ); + aSet.Put( SfxBoolItem( ATTR_PRESLAYOUT_CHECK_MASTERS, bCheckMasters ) ); + + if (!aSelectedPages.empty()) + { + OUString aOldLayoutName(aSelectedPages.back()->GetLayoutName()); + sal_Int32 nPos = aOldLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aOldLayoutName = aOldLayoutName.copy(0, nPos); + aSet.Put(SfxStringItem(ATTR_PRESLAYOUT_NAME, aOldLayoutName)); + } + + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (pArgs) + { + if (pArgs->GetItemState(ATTR_PRESLAYOUT_LOAD) == SfxItemState::SET) + bLoad = static_cast(pArgs->Get(ATTR_PRESLAYOUT_LOAD)).GetValue(); + if( pArgs->GetItemState( ATTR_PRESLAYOUT_MASTER_PAGE ) == SfxItemState::SET ) + bMasterPage = pArgs->Get( ATTR_PRESLAYOUT_MASTER_PAGE ).GetValue(); + if( pArgs->GetItemState( ATTR_PRESLAYOUT_CHECK_MASTERS ) == SfxItemState::SET ) + bCheckMasters = static_cast( pArgs->Get( ATTR_PRESLAYOUT_CHECK_MASTERS ) ).GetValue(); + if (pArgs->GetItemState(ATTR_PRESLAYOUT_NAME) == SfxItemState::SET) + aFile = pArgs->Get(ATTR_PRESLAYOUT_NAME).GetValue(); + } + else + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSdPresLayoutDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, mpDocSh, aSet)); + + sal_uInt16 nResult = pDlg->Execute(); + + switch (nResult) + { + case RET_OK: + { + pDlg->GetAttr(aSet); + if (aSet.GetItemState(ATTR_PRESLAYOUT_LOAD) == SfxItemState::SET) + bLoad = static_cast(aSet.Get(ATTR_PRESLAYOUT_LOAD)).GetValue(); + if( aSet.GetItemState( ATTR_PRESLAYOUT_MASTER_PAGE ) == SfxItemState::SET ) + bMasterPage = aSet.Get( ATTR_PRESLAYOUT_MASTER_PAGE ).GetValue(); + if( aSet.GetItemState( ATTR_PRESLAYOUT_CHECK_MASTERS ) == SfxItemState::SET ) + bCheckMasters = static_cast(aSet.Get( ATTR_PRESLAYOUT_CHECK_MASTERS ) ).GetValue(); + if (aSet.GetItemState(ATTR_PRESLAYOUT_NAME) == SfxItemState::SET) + aFile = aSet.Get(ATTR_PRESLAYOUT_NAME).GetValue(); + } + break; + + default: + bError = true; + } + } + + if (bError) + return; + + mpDocSh->SetWaitCursor( true ); + + /* Here, we only exchange masterpages, therefore the current page + remains the current page. To prevent calling PageOrderChangedHint + during insertion and extraction of the masterpages, we block. */ + /* That isn't quite right. If the masterpageview is active and you are + removing a masterpage, it's possible that you are removing the + current masterpage. So you have to call ResetActualPage ! */ + if( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr && !bCheckMasters ) + static_cast(mpView)->BlockPageOrderChangedHint(true); + + if (bLoad) + { + sal_Int32 nIdx{ 0 }; + OUString aFileName = aFile.getToken(0, DOCUMENT_TOKEN, nIdx); + SdDrawDocument* pTempDoc = mpDoc->OpenBookmarkDoc( aFileName ); + + // #69581: If I chose the standard-template I got no filename and so I get no + // SdDrawDocument-Pointer. But the method SetMasterPage is able to handle + // a NULL-pointer as a Standard-template ( look at SdDrawDocument::SetMasterPage ) + OUString aLayoutName; + if( pTempDoc ) + aLayoutName = aFile.getToken(0, DOCUMENT_TOKEN, nIdx); + for (auto nSelectedPage : aSelectedPageNums) + mpDoc->SetMasterPage(nSelectedPage, aLayoutName, pTempDoc, bMasterPage, bCheckMasters); + mpDoc->CloseBookmarkDoc(); + } + else + { + // use master page with the layout name aFile from current Doc + for (auto nSelectedPage : aSelectedPageNums) + mpDoc->SetMasterPage(nSelectedPage, aFile, mpDoc, bMasterPage, bCheckMasters); + } + + // remove blocking + if( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr && !bCheckMasters ) + static_cast(mpView)->BlockPageOrderChangedHint(false); + + // if the master page was visible, show it again + if (!aSelectedPages.empty()) + { + if (bOnMaster) + { + if( auto pDrawViewShell = dynamic_cast( mpViewShell )) + { + ::sd::View* pView = pDrawViewShell->GetView(); + for (auto pSelectedPage : aSelectedPages) + { + sal_uInt16 nPgNum = pSelectedPage->TRG_GetMasterPage().GetPageNum(); + + if (static_cast(mpViewShell)->GetPageKind() == PageKind::Notes) + nPgNum++; + + pView->HideSdrPage(); + pView->ShowSdrPage(pView->GetModel()->GetMasterPage(nPgNum)); + } + } + + // force update of TabBar + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_MASTERPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + else + { + for (auto pSelectedPage : aSelectedPages) + pSelectedPage->SetAutoLayout(pSelectedPage->GetAutoLayout()); + } + } + + //Undo transfer to document selection + for (auto pPage : aUnselect) + mpDoc->SetSelected(pPage, false); + + + // fake a mode change to repaint the page tab bar + if( auto pDrawViewSh = dynamic_cast( mpViewShell ) ) + { + EditMode eMode = pDrawViewSh->GetEditMode(); + bool bLayer = pDrawViewSh->IsLayerModeActive(); + pDrawViewSh->ChangeEditMode( eMode, !bLayer ); + pDrawViewSh->ChangeEditMode( eMode, bLayer ); + } + + mpDocSh->SetWaitCursor( false ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuprobjs.cxx b/sd/source/ui/func/fuprobjs.cxx new file mode 100644 index 000000000..6042d1fbc --- /dev/null +++ b/sd/source/ui/func/fuprobjs.cxx @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuPresentationObjects::FuPresentationObjects ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuPresentationObjects::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuPresentationObjects( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuPresentationObjects::DoExecute( SfxRequest& ) +{ + OutlineViewShell* pOutlineViewShell = dynamic_cast< OutlineViewShell* >( mpViewShell ); + DBG_ASSERT( pOutlineViewShell, "sd::FuPresentationObjects::DoExecute(), does not work without an OutlineViewShell!"); + if( !pOutlineViewShell ) + return; + + /* does the selections end in a unique presentation layout? + if not, it is not allowed to edit the templates */ + SfxItemSetFixed aSet(mpDoc->GetItemPool() ); + pOutlineViewShell->GetStatusBarState( aSet ); + OUString aLayoutName = static_cast(aSet.Get(SID_STATUS_LAYOUT)).GetValue(); + DBG_ASSERT(!aLayoutName.isEmpty(), "Layout not defined"); + + bool bUnique = false; + sal_Int16 nDepth, nTmp; + OutlineView* pOlView = static_cast(pOutlineViewShell->GetView()); + OutlinerView* pOutlinerView = pOlView->GetViewByWindow( static_cast(mpWindow) ); + ::Outliner* pOutl = pOutlinerView->GetOutliner(); + + std::vector aSelList; + pOutlinerView->CreateSelectionList(aSelList); + + Paragraph* pPara = aSelList.empty() ? nullptr : aSelList.front(); + + nDepth = pOutl->GetDepth(pOutl->GetAbsPos( pPara ) ); + bool bPage = ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ); + + for( const auto& rpPara : aSelList ) + { + nTmp = pOutl->GetDepth( pOutl->GetAbsPos( rpPara ) ); + + if( nDepth != nTmp ) + { + bUnique = false; + break; + } + + if( ::Outliner::HasParaFlag( rpPara, ParaFlag::ISPAGE ) != bPage ) + { + bUnique = false; + break; + } + bUnique = true; + } + + if( !bUnique ) + return; + + OUString aStyleName = aLayoutName + SD_LT_SEPARATOR; + PresentationObjects ePO; + + if( bPage ) + { + ePO = PresentationObjects::Title; + aStyleName += STR_LAYOUT_TITLE; + } + else + { + ePO = static_cast( static_cast(PresentationObjects::Outline_1) + nDepth - 1 ); + aStyleName += STR_LAYOUT_OUTLINE + " " + OUString::number(nDepth); + } + + SfxStyleSheetBasePool* pStyleSheetPool = mpDocSh->GetStyleSheetPool(); + SfxStyleSheetBase* pStyleSheet = pStyleSheetPool->Find( aStyleName, SfxStyleFamily::Page ); + DBG_ASSERT(pStyleSheet, "StyleSheet missing"); + + if( !pStyleSheet ) + return; + + SfxStyleSheetBase& rStyleSheet = *pStyleSheet; + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSdPresLayoutTemplateDlg(mpDocSh, mpViewShell->GetFrameWeld(), + false, rStyleSheet, ePO, pStyleSheetPool)); + if( pDlg->Execute() == RET_OK ) + { + const SfxItemSet* pOutSet = pDlg->GetOutputItemSet(); + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique(mpDoc, static_cast(pStyleSheet), pOutSet)); + + pStyleSheet->GetItemSet().Put( *pOutSet ); + static_cast( pStyleSheet )->Broadcast( SfxHint( SfxHintId::DataChanged ) ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuscale.cxx b/sd/source/ui/func/fuscale.cxx new file mode 100644 index 000000000..d4730b243 --- /dev/null +++ b/sd/source/ui/func/fuscale.cxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuScale::FuScale ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuScale::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuScale( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuScale::DoExecute( SfxRequest& rReq ) +{ + sal_Int16 nValue; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SfxItemSetFixed aNewAttr( mpDoc->GetPool() ); + std::unique_ptr pZoomItem; + SvxZoomEnableFlags nZoomValues = SvxZoomEnableFlags::ALL; + + nValue = static_cast(mpWindow->GetZoom()); + + // zoom on page size? + if( dynamic_cast< DrawViewShell *>( mpViewShell ) && + static_cast(mpViewShell)->IsZoomOnPage() ) + { + pZoomItem.reset(new SvxZoomItem( SvxZoomType::WHOLEPAGE, nValue )); + } + else + { + pZoomItem.reset(new SvxZoomItem( SvxZoomType::PERCENT, nValue )); + } + + // limit range + if( mpViewShell ) + { + if( dynamic_cast< DrawViewShell *>( mpViewShell ) != nullptr ) + { + SdrPageView* pPageView = mpView->GetSdrPageView(); + if( pPageView && pPageView->GetObjList()->GetObjCount() == 0 ) + { + nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL; + } + } + else if( dynamic_cast< OutlineViewShell *>( mpViewShell ) != nullptr ) + { + nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL; + nZoomValues &= ~SvxZoomEnableFlags::WHOLEPAGE; + nZoomValues &= ~SvxZoomEnableFlags::PAGEWIDTH; + } + } + + pZoomItem->SetValueSet( nZoomValues ); + aNewAttr.Put( std::move(pZoomItem) ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSvxZoomDialog(rReq.GetFrameWeld(), aNewAttr)); + pDlg->SetLimits( static_cast(mpWindow->GetMinZoom()), static_cast(mpWindow->GetMaxZoom()) ); + sal_uInt16 nResult = pDlg->Execute(); + switch( nResult ) + { + case RET_CANCEL: + { + rReq.Ignore (); + return; // Cancel + } + default: + { + rReq.Ignore (); + } + break; + } + + const SfxItemSet aArgs (*(pDlg->GetOutputItemSet ())); + + pDlg.disposeAndClear(); + + if (!mpViewShell) + return; + + switch ( aArgs.Get (SID_ATTR_ZOOM).GetType ()) + { + case SvxZoomType::PERCENT: + { + nValue = aArgs.Get (SID_ATTR_ZOOM).GetValue (); + + mpViewShell->SetZoom( nValue ); + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArrayZoom ); + } + break; + + case SvxZoomType::OPTIMAL: + { + if( dynamic_cast< DrawViewShell *>( mpViewShell ) != nullptr ) + { + // name confusion: SID_SIZE_ALL -> zoom onto all objects + // --> the program offers it as optimal + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_ALL, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + } + break; + + case SvxZoomType::PAGEWIDTH: + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_PAGE_WIDTH, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + break; + + case SvxZoomType::WHOLEPAGE: + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + break; + default: + break; + } + } + else if(mpViewShell && (pArgs->Count () == 1)) + { + const SfxUInt32Item* pScale = rReq.GetArg(ID_VAL_ZOOM); + mpViewShell->SetZoom (pScale->GetValue ()); + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArrayZoom ); + } + +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusearch.cxx b/sd/source/ui/func/fusearch.cxx new file mode 100644 index 000000000..73a112bf4 --- /dev/null +++ b/sd/source/ui/func/fusearch.cxx @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SfxRequest; + +namespace sd { + +const sal_uInt16 SidArraySpell[] = { + SID_DRAWINGMODE, + SID_OUTLINE_MODE, + SID_SLIDE_SORTER_MODE, + SID_NOTES_MODE, + SID_HANDOUT_MASTER_MODE, + SID_SLIDE_MASTER_MODE, + SID_NOTES_MASTER_MODE, + 0 }; + +FuSearch::FuSearch ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq), + m_pSdOutliner(nullptr), + m_bOwnOutliner(false) +{ +} + +FuSearch* FuSearch::createPtr(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq) +{ + FuSearch* xFunc( new FuSearch( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSearch::DoExecute( SfxRequest& ) +{ + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArraySpell ); + + if ( dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr ) + { + m_bOwnOutliner = true; + m_pSdOutliner = new SdOutliner( mpDoc, OutlinerMode::TextObject ); + } + else if ( dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr ) + { + m_bOwnOutliner = false; + m_pSdOutliner = mpDoc->GetOutliner(); + } + + if (m_pSdOutliner) + m_pSdOutliner->PrepareSpelling(); +} + +FuSearch::~FuSearch() +{ + if ( ! mpDocSh->IsInDestruction() && mpDocSh->GetViewShell()!=nullptr) + mpDocSh->GetViewShell()->GetViewFrame()->GetBindings().Invalidate( SidArraySpell ); + + if (m_pSdOutliner) + m_pSdOutliner->EndSpelling(); + + if (m_bOwnOutliner) + delete m_pSdOutliner; +} + +void FuSearch::SearchAndReplace( const SvxSearchItem* pSearchItem ) +{ + ViewShellBase* pBase = dynamic_cast( SfxViewShell::Current() ); + ViewShell* pViewShell = nullptr; + if (pBase != nullptr) + pViewShell = pBase->GetMainViewShell().get(); + + if (pViewShell == nullptr) + return; + + if (m_pSdOutliner && dynamic_cast(pViewShell) && !m_bOwnOutliner) + { + m_pSdOutliner->EndSpelling(); + + m_bOwnOutliner = true; + m_pSdOutliner = new SdOutliner(mpDoc, OutlinerMode::TextObject); + m_pSdOutliner->PrepareSpelling(); + } + else if (m_pSdOutliner && dynamic_cast(pViewShell) && m_bOwnOutliner) + { + m_pSdOutliner->EndSpelling(); + delete m_pSdOutliner; + + m_bOwnOutliner = false; + m_pSdOutliner = mpDoc->GetOutliner(); + m_pSdOutliner->PrepareSpelling(); + } + + if (m_pSdOutliner) + { + bool bEndSpelling = m_pSdOutliner->StartSearchAndReplace(pSearchItem); + + if (bEndSpelling) + { + m_pSdOutliner->EndSpelling(); + m_pSdOutliner->PrepareSpelling(); + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusel.cxx b/sd/source/ui/func/fusel.cxx new file mode 100644 index 000000000..a525f3bfc --- /dev/null +++ b/sd/source/ui/func/fusel.cxx @@ -0,0 +1,1328 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +using namespace ::com::sun::star; + +namespace sd { + +FuSelection::FuSelection ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuDraw(pViewSh, pWin, pView, pDoc, rReq), + bTempRotation(false), + bSelectionChanged(false), + pHdl(nullptr), + bSuppressChangesOfSelection(false), + bMirrorSide0(false), + nEditMode(SID_BEZIER_MOVE), + pWaterCanCandidate(nullptr) + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + ,bBeginInsertPoint(false), + oldPoint(0,0) + ,bMovedToCenterPoint(false) +{ +} + +rtl::Reference FuSelection::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuSelection( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSelection::DoExecute( SfxRequest& rReq ) +{ + FuDraw::DoExecute( rReq ); + + // Select object bar + SelectionHasChanged(); +} + +FuSelection::~FuSelection() +{ + mpView->UnmarkAllPoints(); + mpView->ResetCreationActive(); + + if ( mpView->GetDragMode() != SdrDragMode::Move ) + { + mpView->SetDragMode(SdrDragMode::Move); + } +} + +namespace { + bool lcl_followHyperlinkAllowed(const MouseEvent& rMEvt) { + if (!rMEvt.IsMod1() && SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink)) + return false; + if (rMEvt.IsMod1() && !SvtSecurityOptions::IsOptionSet(SvtSecurityOptions::EOption::CtrlClickHyperlink)) + return false; + return true; + } +} + +bool FuSelection::MouseButtonDown(const MouseEvent& rMEvt) +{ + pHdl = nullptr; + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + bool bWaterCan = SD_MOD()->GetWaterCan(); + const bool bReadOnly = mpDocSh->IsReadOnly(); + // When the right mouse button is pressed then only select objects + // (and deselect others) as a preparation for showing the context + // menu. + const bool bSelectionOnly = rMEvt.IsRight(); + + bMBDown = true; + bSelectionChanged = false; + + if ( mpView->IsAction() ) + { + if ( rMEvt.IsRight() ) + mpView->BckAction(); + return true; + } + + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + + if (comphelper::LibreOfficeKit::isActive()) + { + // When tiled rendering, we always work in logic units, use the non-pixel constants. + nDrgLog = DRGLOG; + nHitLog = HITLOG; + } + + // The following code is executed for right clicks as well as for left + // clicks in order to modify the selection for the right button as a + // preparation for the context menu. The functions BegMarkObject() and + // BegDragObject(), however, are not called for right clicks because a) + // it makes no sense and b) to have IsAction() return sal_False when called + // from Command() which is a prerequisite for the context menu. + if ((rMEvt.IsLeft() || rMEvt.IsRight()) + && !mpView->IsAction() + && (mpView->IsFrameDragSingles() || !mpView->HasMarkablePoints())) + { + /****************************************************************** + * NO BEZIER_EDITOR + ******************************************************************/ + mpWindow->CaptureMouse(); + pHdl = mpView->PickHandle(aMDPos); + + Degree100 nAngle0 = GetAngle(aMDPos - mpView->GetRef1()); + nAngle0 -= 27000_deg100; + nAngle0 = NormAngle36000(nAngle0); + bMirrorSide0 = nAngle0 < 18000_deg100; + + if (!pHdl && mpView->Is3DRotationCreationActive()) + { + /****************************************************************** + * If 3D-rotation bodies are about to be created, + * end creation now. + ******************************************************************/ + bSuppressChangesOfSelection = true; + mpWindow->EnterWait(); + mpView->End3DCreation(); + bSuppressChangesOfSelection = false; + mpView->ResetCreationActive(); + mpWindow->LeaveWait(); + } + + bool bTextEdit = false; + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::TextEditObj && (mpViewShell->GetFrameView()->IsQuickEdit() || dynamic_cast< sdr::table::SdrTableObj* >(aVEvt.mpObj) != nullptr)) + { + bTextEdit = true; + } + + // When clicking into a URl field, also go to text edit mode (when not following the link) + if (!bTextEdit && eHit == SdrHitKind::UrlField && !rMEvt.IsMod2() && !lcl_followHyperlinkAllowed(rMEvt)) + bTextEdit = true; + + bool bPreventModify = mpDocSh->IsReadOnly(); + if (bPreventModify && mpDocSh->GetSignPDFCertificate().is()) + { + // If the just added signature line shape is selected, allow moving / resizing it. + bPreventModify = false; + } + + if(!bTextEdit + && !bPreventModify + && ((mpView->IsMarkedHit(aMDPos, nHitLog) && !rMEvt.IsShift() && !rMEvt.IsMod2()) || pHdl != nullptr) + && (rMEvt.GetClicks() != 2) + ) + { + if (!pHdl && mpView->Is3DRotationCreationActive()) + { + // Switch between 3D-rotation body -> selection + mpView->ResetCreationActive(); + } + else if (bWaterCan) + { + // Remember the selected object for proper handling in + // MouseButtonUp(). + pWaterCanCandidate = pickObject (aMDPos); + } + else + { + // hit handle or marked object + bFirstMouseMove = true; + aDragTimer.Start(); + } + + if ( ! rMEvt.IsRight()) + if (mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog)) + mpView->GetDragMethod()->SetShiftPressed( rMEvt.IsShift() ); + bReturn = true; + } + else + { + SdrPageView* pPV = nullptr; + SdrObject* pObj = !rMEvt.IsMod2() ? mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::PICKMACRO) : nullptr; + if (pObj) + { + mpView->BegMacroObj(aMDPos, nHitLog, pObj, pPV, mpWindow); + bReturn = true; + } + else if ( bTextEdit ) + { + SdrObjKind nSdrObjKind = aVEvt.mpObj->GetObjIdentifier(); + + if (aVEvt.mpObj->GetObjInventor() == SdrInventor::Default && + (nSdrObjKind == SdrObjKind::Text || + nSdrObjKind == SdrObjKind::TitleText || + nSdrObjKind == SdrObjKind::OutlineText || + !aVEvt.mpObj->IsEmptyPresObj())) + { + // Seamless Editing: branch to text input + if (!rMEvt.IsShift()) + mpView->UnmarkAll(); + + SfxUInt16Item aItem(SID_TEXTEDIT, 1); + mpViewShell->GetViewFrame()->GetDispatcher()-> + ExecuteList(SID_TEXTEDIT, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aItem }); + return bReturn; // CAUTION, due to the synchronous slot the object is deleted now + } + } + else if ( !rMEvt.IsMod2() && rMEvt.GetClicks() == 1 && + aVEvt.meEvent == SdrEventKind::ExecuteUrl ) + { + mpWindow->ReleaseMouse(); + + // If tiled rendering, let client handles URL execution and early returns. + if (comphelper::LibreOfficeKit::isActive()) + { + SfxViewShell& rSfxViewShell = mpViewShell->GetViewShellBase(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_HYPERLINK_CLICKED, aVEvt.mpURLField->GetURL().toUtf8().getStr()); + return true; + } + + if (!lcl_followHyperlinkAllowed(rMEvt)) + return true; + + SfxStringItem aStrItem(SID_FILE_NAME, aVEvt.mpURLField->GetURL()); + SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName()); + SfxBoolItem aBrowseItem( SID_BROWSE, true ); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + mpWindow->ReleaseMouse(); + + if (rMEvt.IsMod1()) + { + // Open in new frame + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aBrowseItem, &aReferer }); + } + else + { + // Open in current frame + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + + bReturn = true; + } + else if(!rMEvt.IsMod2() + && dynamic_cast< const DrawViewShell *>( mpViewShell ) != nullptr + ) + { + pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER); + if (pObj) + { + // Handle ImageMap click when not just selecting + if (!bSelectionOnly) + { + if (lcl_followHyperlinkAllowed(rMEvt)) + bReturn = HandleImageMapClick(pObj, aMDPos); + } + + if (!bReturn + && (dynamic_cast(pObj) != nullptr + || dynamic_cast(pObj) != nullptr)) + { + if (rMEvt.GetClicks() == 1) + { + // Look into the group + pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, + SdrSearchOptions::ALSOONMASTER + | SdrSearchOptions::DEEP); + if (pObj && lcl_followHyperlinkAllowed(rMEvt)) + bReturn = HandleImageMapClick(pObj, aMDPos); + } + else if (!bReadOnly && rMEvt.GetClicks() == 2) + { + // New: double click on selected Group object + // enter group + if (!bSelectionOnly + && pObj->getSdrPageFromSdrObject() == pPV->GetPage()) + bReturn = pPV->EnterGroup(pObj); + } + } + } + + // #i71727# replaced else here with two possibilities, once the original else (!pObj) + // and also ignoring the found object when it's on a masterpage + if(!pObj || (pObj->getSdrPageFromSdrObject() && pObj->getSdrPageFromSdrObject()->IsMasterPage())) + { + if(mpView->IsGroupEntered() && 2 == rMEvt.GetClicks()) + { + // New: double click on empty space/on obj on MasterPage, leave group + mpView->LeaveOneGroup(); + bReturn = true; + } + } + } + + if (!bReturn) + { + if (bWaterCan) + { + if ( ! (rMEvt.IsShift() || rMEvt.IsMod2())) + { + // Find the object under the current mouse position + // and store it for the MouseButtonUp() method to + // evaluate. + pWaterCanCandidate = pickObject (aMDPos); + } + } + else + { + bReturn = true; + bool bDeactivateOLE = false; + + if ( !rMEvt.IsShift() && !rMEvt.IsMod2() ) + { + OSL_ASSERT (mpViewShell->GetViewShell()!=nullptr); + Client* pIPClient = static_cast( + mpViewShell->GetViewShell()->GetIPClient()); + + if (pIPClient && pIPClient->IsObjectInPlaceActive()) + { + // OLE-object gets deactivated in subsequent UnmarkAll() + bDeactivateOLE = true; + } + + mpView->UnmarkAll(); + } + + bool bMarked = false; + + if ( !rMEvt.IsMod1() && !bDeactivateOLE) + { + if ( rMEvt.IsMod2() ) + { + bMarked = mpView->MarkNextObj(aMDPos, nHitLog, rMEvt.IsShift() ); + } + else + { + bool bToggle = false; + + if (rMEvt.IsShift() && mpView->GetMarkedObjectList().GetMarkCount() > 1) + { + // No Toggle on single selection + bToggle = true; + } + + bMarked = mpView->MarkObj(aMDPos, nHitLog, bToggle); + } + } + + if( !bDeactivateOLE ) + { + if ( !bReadOnly && + bMarked && + (!rMEvt.IsShift() || mpView->IsMarkedHit(aMDPos, nHitLog))) + { + /********************************************************** + * Move object + **********************************************************/ + aDragTimer.Start(); + + pHdl=mpView->PickHandle(aMDPos); + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + } + else + { + /********************************************************** + * Select object + **********************************************************/ + if ( ! rMEvt.IsRight()) + mpView->BegMarkObj(aMDPos); + } + } + + if( bMarked && bTempRotation && (nSlotId == SID_OBJECT_ROTATE) && !rMEvt.IsShift() && (rMEvt.GetClicks() != 2) ) + { + nSlotId = SID_OBJECT_SELECT; + Activate(); + } + } + } + } + } + else if ( !bReadOnly + && (rMEvt.IsLeft() || rMEvt.IsRight()) + && !mpView->IsAction()) + { + /********************************************************************** + * BEZIER-EDITOR + **********************************************************************/ + mpWindow->CaptureMouse(); + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::Handle && aVEvt.mpHdl->GetKind() == SdrHdlKind::BezierWeight) + { + /****************************************************************** + * Drag Handle + ******************************************************************/ + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + else if (eHit == SdrHitKind::MarkedObject && nEditMode == SID_BEZIER_INSERT) + { + /****************************************************************** + * Insert gluepoint + ******************************************************************/ + mpView->BegInsObjPoint(aMDPos, rMEvt.IsMod1()); + } + else if (eHit == SdrHitKind::MarkedObject && rMEvt.IsMod1()) + { + /****************************************************************** + * Select gluepoint + ******************************************************************/ + if (!rMEvt.IsShift()) + mpView->UnmarkAllPoints(); + + if ( ! rMEvt.IsRight()) + mpView->BegMarkPoints(aMDPos); + } + else if (eHit == SdrHitKind::MarkedObject && !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + /****************************************************************** + * Move object + ******************************************************************/ + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, nullptr, nDrgLog); + } + else if (eHit == SdrHitKind::Handle) + { + /****************************************************************** + * Select gluepoint + ******************************************************************/ + if (!mpView->IsPointMarked(*aVEvt.mpHdl) || rMEvt.IsShift()) + { + if (!rMEvt.IsShift()) + { + mpView->UnmarkAllPoints(); + pHdl = mpView->PickHandle(aMDPos); + } + else + { + if (mpView->IsPointMarked(*aVEvt.mpHdl)) + { + mpView->UnmarkPoint(*aVEvt.mpHdl); + pHdl = nullptr; + } + else + { + pHdl = mpView->PickHandle(aMDPos); + } + } + + if (pHdl) + { + mpView->MarkPoint(*pHdl); + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + + } + } + else + { + // Point IS marked and NO shift is pressed. Start + // dragging of selected point(s) + pHdl = mpView->PickHandle(aMDPos); + if(pHdl && ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, pHdl, nDrgLog); + } + } + else + { + /****************************************************************** + * Select or drag object + ******************************************************************/ + if (!rMEvt.IsShift() && !rMEvt.IsMod2() && eHit == SdrHitKind::UnmarkedObject) + { + mpView->UnmarkAllObj(); + } + + bool bMarked = false; + + if (!rMEvt.IsMod1()) + { + if (rMEvt.IsMod2()) + { + bMarked = mpView->MarkNextObj(aMDPos, nHitLog, rMEvt.IsShift()); + } + else + { + bMarked = mpView->MarkObj(aMDPos, nHitLog, rMEvt.IsShift()); + } + } + + if (bMarked && + (!rMEvt.IsShift() || eHit == SdrHitKind::MarkedObject)) + { + // Move object + if ( ! rMEvt.IsRight()) + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + else if (mpView->AreObjectsMarked()) + { + /************************************************************** + * Select gluepoint + **************************************************************/ + if (!rMEvt.IsShift()) + mpView->UnmarkAllPoints(); + + if ( ! rMEvt.IsRight()) + mpView->BegMarkPoints(aMDPos); + } + else + { + /************************************************************** + * Select object + **************************************************************/ + if ( ! rMEvt.IsRight()) + mpView->BegMarkObj(aMDPos); + } + + ForcePointer(&rMEvt); + } + } + + if (!bIsInDragMode) + { + ForcePointer(&rMEvt); + } + + return bReturn; +} + +bool FuSelection::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = FuDraw::MouseMove(rMEvt); + + if (aDragTimer.IsActive()) + { + if(bFirstMouseMove) + { + bFirstMouseMove = false; + } + else + { + aDragTimer.Stop(); + } + } + + if (mpView->IsAction()) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt(mpWindow->PixelToLogic(aPix)); + + ForceScroll(aPix); + + if (mpView->IsInsObjPoint()) + { + mpView->MovInsObjPoint(aPnt); + } + else + { + mpView->MovAction(aPnt); + } + } + + ForcePointer(&rMEvt); + + return bReturn; +} + +bool FuSelection::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + // When the right mouse button is pressed then only select objects + // (and deselect others) as a preparation for showing the context + // menu. + const bool bSelectionOnly = rMEvt.IsRight(); + + if (aDragTimer.IsActive() ) + { + aDragTimer.Stop(); + bIsInDragMode = false; + } + + if( !mpView ) + return false; + + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + sal_uInt16 nHitLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(HITPIX,0)).Width() ); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if (mpView->IsFrameDragSingles() || !mpView->HasMarkablePoints()) + { + /********************************************************************** + * NO BEZIER_EDITOR + **********************************************************************/ + if ( mpView->IsDragObj() ) + { + /****************************************************************** + * Object was moved + ******************************************************************/ + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bDragWithCopy = (rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (bDragWithCopy) + { + bDragWithCopy = !mpView->IsPresObjSelected(false); + } + + mpView->SetDragWithCopy(bDragWithCopy); + bool bWasDragged(mpView->EndDragObj( mpView->IsDragWithCopy() )); + + mpView->ForceMarkedToAnotherPage(); + + if (!rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() && + !bSelectionChanged && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + /************************************************************* + * If a user wants to click on an object in front of a marked + * one, he releases the mouse button immediately + **************************************************************/ + SdrPageView* pPV; + SdrObject* pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::BEFOREMARK); + if (pObj && pPV->IsObjMarkable(pObj)) + { + mpView->UnmarkAllObj(); + mpView->MarkObj(pObj,pPV); + return true; + } + + // check for single object selected + SdrObject* pSingleObj = nullptr; + + if (mpView->GetMarkedObjectList().GetMarkCount()==1) + { + pSingleObj = mpView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + } + + // Check for click on svx::diagram::DiagramFrameHdl + // - if we hit a SdrHdl + // - if it was not moved + // - if single object is selected + // - and it is a Diagram + if(pHdl && !bWasDragged && nullptr != pSingleObj && pSingleObj->isDiagram()) + { + svx::diagram::DiagramFrameHdl* pDiagramFrameHdl(dynamic_cast(pHdl)); + if(nullptr != pDiagramFrameHdl) + { + // let the DiagramFrameHdl decide what to do + svx::diagram::DiagramFrameHdl::clicked(aPnt); + } + } + + /************************************************************** + * Toggle between selection and rotation + **************************************************************/ + if (nSlotId == SID_OBJECT_SELECT + && !comphelper::LibreOfficeKit::isActive() + && mpView->IsRotateAllowed() + + && (rMEvt.GetClicks() != 2) + && (mpViewShell->GetFrameView()->IsClickChangeRotation() + || (pSingleObj + && pSingleObj->GetObjInventor()==SdrInventor::E3d)) + && ! bSelectionOnly) + + { + bTempRotation = true; + nSlotId = SID_OBJECT_ROTATE; + Activate(); + } + else if (nSlotId == SID_OBJECT_ROTATE) + { + nSlotId = SID_OBJECT_SELECT; + Activate(); + } + } + else if (nSlotId == SID_CONVERT_TO_3D_LATHE) + { + if (!pHdl) + { + bSuppressChangesOfSelection = true; + mpView->Start3DCreation(); + bSuppressChangesOfSelection = false; + } + else if (pHdl->GetKind() != SdrHdlKind::MirrorAxis && + pHdl->GetKind() != SdrHdlKind::Ref1 && + pHdl->GetKind() != SdrHdlKind::Ref2 && mpView->Is3DRotationCreationActive()) + { + /********************************************************* + * If 3D-rotation bodies are about to be created, + * end creation now + **********************************************************/ + Degree100 nAngle1 = GetAngle(aPnt - mpView->GetRef1()); + nAngle1 -= 27000_deg100; + nAngle1 = NormAngle36000(nAngle1); + bool bMirrorSide1 = nAngle1 < 18000_deg100; + + if (bMirrorSide0 != bMirrorSide1) + { + bSuppressChangesOfSelection = true; + mpWindow->EnterWait(); + mpView->End3DCreation(); + bSuppressChangesOfSelection = false; + nSlotId = SID_OBJECT_SELECT; + mpWindow->LeaveWait(); + Activate(); + } + } + } + } + else if (rMEvt.IsMod1() + && !rMEvt.IsMod2() + && std::abs(aPnt.X() - aMDPos.X()) < nDrgLog + && std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + // Enter group + mpView->MarkObj(aPnt, nHitLog, rMEvt.IsShift(), rMEvt.IsMod1()); + } + + if (mpView->IsAction() ) + { + mpView->EndAction(); + } + + if( SD_MOD()->GetWaterCan() ) + { + if( rMEvt.IsRight() ) + { + // In watering-can mode, on press onto right mouse button, an undo is executed + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_UNDO, SfxCallMode::ASYNCHRON ); + } + else if (pWaterCanCandidate != nullptr) + { + // Is the candidate object still under the mouse? + if (pickObject (aPnt) == pWaterCanCandidate) + { + SdStyleSheetPool* pPool = static_cast( + mpDocSh->GetStyleSheetPool()); + if (pPool != nullptr) + { + SfxStyleSheet* pStyleSheet = static_cast( + pPool->GetActualStyleSheet()); + if (pStyleSheet != nullptr && mpView->IsUndoEnabled() ) + { + // Added UNDOs for the WaterCan mode. This was never done in + // the past, thus it was missing all the time. + std::unique_ptr pUndoAttr = mpDoc->GetSdrUndoFactory().CreateUndoAttrObject(*pWaterCanCandidate, true, true); + mpView->BegUndo(pUndoAttr->GetComment()); + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoGeoObject(*pWaterCanCandidate)); + mpView->AddUndo(std::move(pUndoAttr)); + + pWaterCanCandidate->SetStyleSheet (pStyleSheet, false); + + mpView->EndUndo(); + } + } + } + } + // else when there has been no object under the mouse when the + // button was pressed then nothing happens even when there is + // one now. + } + + sal_uInt16 nClicks = rMEvt.GetClicks(); + + if (nClicks == 2 && rMEvt.IsLeft() && bMBDown && + !rMEvt.IsMod1() && !rMEvt.IsShift() ) + { + DoubleClick(rMEvt); + } + + bMBDown = false; + + ForcePointer(&rMEvt); + pHdl = nullptr; + mpWindow->ReleaseMouse(); + SdrObject* pSingleObj = nullptr; + const size_t nMarkCount = mpView->GetMarkedObjectList().GetMarkCount(); + + if (nMarkCount==1) + { + pSingleObj = mpView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + } + + if ( (nSlotId != SID_OBJECT_SELECT && nMarkCount==0) || + ( mpView->GetDragMode() == SdrDragMode::Crook && + !mpView->IsCrookAllowed( mpView->IsCrookNoContortion() ) ) || + ( mpView->GetDragMode() == SdrDragMode::Shear && + !mpView->IsShearAllowed() && !mpView->IsDistortAllowed() ) || + ( nSlotId==SID_CONVERT_TO_3D_LATHE && pSingleObj && + (pSingleObj->GetObjInventor() != SdrInventor::Default || + pSingleObj->GetObjIdentifier() == SdrObjKind::Measure) ) ) + { + bReturn = true; + ForcePointer(&rMEvt); + pHdl = nullptr; + mpWindow->ReleaseMouse(); + FuDraw::MouseButtonUp(rMEvt); + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::SYNCHRON); + return bReturn; // CAUTION, due to the synchronous slot, the object is deleted now. + } + + FuDraw::MouseButtonUp(rMEvt); + } + else + { + /********************************************************************** + * BEZIER_EDITOR + **********************************************************************/ + if ( mpView->IsAction() ) + { + if ( mpView->IsInsObjPoint() ) + { + mpView->EndInsObjPoint(SdrCreateCmd::ForceEnd); + } + else if ( mpView->IsDragObj() ) + { + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bDragWithCopy = (rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (bDragWithCopy) + { + bDragWithCopy = !mpView->IsPresObjSelected(false); + } + + mpView->SetDragWithCopy(bDragWithCopy); + mpView->EndDragObj( mpView->IsDragWithCopy() ); + } + else + { + mpView->EndAction(); + + sal_uInt16 nDrgLog2 = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + Point aPos = mpWindow->PixelToLogic( rMEvt.GetPosPixel() ); + + if (std::abs(aMDPos.X() - aPos.X()) < nDrgLog2 && + std::abs(aMDPos.Y() - aPos.Y()) < nDrgLog2 && + !rMEvt.IsShift() && !rMEvt.IsMod2()) + { + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::NONE) + { + // Click on the same place - unselect + mpView->UnmarkAllObj(); + } + } + } + } + else if (!rMEvt.IsShift() && rMEvt.IsMod1() && !rMEvt.IsMod2() && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + // Enter group + mpView->MarkObj(aPnt, nHitLog, false, rMEvt.IsMod1()); + } + + ForcePointer(&rMEvt); + pHdl = nullptr; + mpWindow->ReleaseMouse(); + + FuDraw::MouseButtonUp(rMEvt); + } + + return bReturn; +} + +/** + * Process keyboard input + * @returns sal_True if a KeyEvent is being processed, sal_False otherwise + */ +bool FuSelection::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + switch (rKEvt.GetKeyCode().GetCode()) + { + case KEY_ESCAPE: + { + bReturn = FuSelection::cancel(); + } + break; + //add keyboard operation for insert points in drawing curve + case KEY_UP: + case KEY_DOWN: + case KEY_LEFT: + case KEY_RIGHT: + { + if(rKEvt.GetKeyCode().IsShift()&&(nEditMode == SID_BEZIER_INSERT)){ + ::tools::Long nX = 0; + ::tools::Long nY = 0; + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + if (nCode == KEY_UP) + { + // scroll up + nX = 0; + nY =-1; + } + else if (nCode == KEY_DOWN) + { + // scroll down + nX = 0; + nY = 1; + } + else if (nCode == KEY_LEFT) + { + // scroll left + nX =-1; + nY = 0; + } + else if (nCode == KEY_RIGHT) + { + // scroll right + nX = 1; + nY = 0; + } + + Point centerPoint; + ::tools::Rectangle rect = mpView->GetMarkedObjRect(); + centerPoint = mpWindow->LogicToPixel(rect.Center()); + Point aPoint = bMovedToCenterPoint? oldPoint:centerPoint; + Point ePoint = aPoint + Point(nX,nY); + mpWindow->SetPointerPosPixel(ePoint); + //simulate mouse move action + MouseEvent eMevt(ePoint, 1, MouseEventModifiers::DRAGMOVE, MOUSE_LEFT, 0); + MouseMove(eMevt); + oldPoint = ePoint; + bMovedToCenterPoint = true; + bReturn = true; + } + } + break; + case KEY_RETURN: + if(rKEvt.GetKeyCode().IsShift()&&(nEditMode == SID_BEZIER_INSERT)) + { + if(!bBeginInsertPoint) + { + //simulate mouse button down action + MouseEvent aMevt(oldPoint, 1, + MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::DRAGMOVE, + MOUSE_LEFT, KEY_SHIFT); + MouseButtonDown(aMevt); + mpWindow->CaptureMouse(); + bBeginInsertPoint = true; + } + else + { + //simulate mouse button up action + MouseEvent rMEvt(oldPoint, 1, + MouseEventModifiers::SIMPLEMOVE | MouseEventModifiers::ENTERWINDOW, + MOUSE_LEFT, KEY_SHIFT); + MouseButtonUp(rMEvt); + bBeginInsertPoint = false; + } + bReturn= true; + } + break; + } + if (!bReturn) + { + bReturn = FuDraw::KeyInput(rKEvt); + + if(mpView->GetMarkedObjectList().GetMarkCount() == 0) + { + mpView->ResetCreationActive(); + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + } + + return bReturn; + +} + +void FuSelection::Activate() +{ + SdrDragMode eMode; + mpView->ResetCreationActive(); + mpView->SetEditMode(SdrViewEditMode::Edit); + + switch( nSlotId ) + { + case SID_OBJECT_ROTATE: + { + eMode = SdrDragMode::Rotate; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_MIRROR: + { + eMode = SdrDragMode::Mirror; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_CROP: + { + eMode = SdrDragMode::Crop; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_TRANSPARENCE: + { + eMode = SdrDragMode::Transparence; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_GRADIENT: + { + eMode = SdrDragMode::Gradient; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_SHEAR: + { + eMode = SdrDragMode::Shear; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + + case SID_OBJECT_CROOK_ROTATE: + { + eMode = SdrDragMode::Crook; + + if ( mpView->GetDragMode() != eMode ) + { + mpView->SetDragMode(eMode); + mpView->SetCrookMode(SdrCrookMode::Rotate); + } + } + break; + + case SID_OBJECT_CROOK_SLANT: + { + eMode = SdrDragMode::Crook; + + if ( mpView->GetDragMode() != eMode ) + { + mpView->SetDragMode(eMode); + mpView->SetCrookMode(SdrCrookMode::Slant); + } + } + break; + + case SID_OBJECT_CROOK_STRETCH: + { + eMode = SdrDragMode::Crook; + + if ( mpView->GetDragMode() != eMode ) + { + mpView->SetDragMode(eMode); + mpView->SetCrookMode(SdrCrookMode::Stretch); + } + } + break; + + case SID_CONVERT_TO_3D_LATHE: + { + eMode = SdrDragMode::Mirror; + bSuppressChangesOfSelection = true; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + + if (!mpView->Is3DRotationCreationActive()) + mpView->Start3DCreation(); + + bSuppressChangesOfSelection = false; + } + break; + + default: + { + eMode = SdrDragMode::Move; + + if ( mpView->GetDragMode() != eMode ) + mpView->SetDragMode(eMode); + } + break; + } + + if (nSlotId != SID_OBJECT_ROTATE) + { + bTempRotation = false; + } + + FuDraw::Activate(); +} + +void FuSelection::SelectionHasChanged() +{ + bSelectionChanged = true; + + FuDraw::SelectionHasChanged(); + + if (mpView->Is3DRotationCreationActive() && !bSuppressChangesOfSelection) + { + // Switch rotation body -> selection + mpView->ResetCreationActive(); + nSlotId = SID_OBJECT_SELECT; + Activate(); + } + + // Activate the right tool bar for the current context of the view. + mpViewShell->GetViewShellBase().GetToolBarManager()->SelectionHasChanged(*mpViewShell, *mpView); +} + +/** + * Set current bezier edit mode + */ +void FuSelection::SetEditMode(sal_uInt16 nMode) +{ + nEditMode = nMode; + + if (nEditMode == SID_BEZIER_INSERT) + { + mpView->SetInsObjPointMode(true); + } + else + { + mpView->SetInsObjPointMode(false); + } + + ForcePointer(); + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_BEZIER_MOVE); + rBindings.Invalidate(SID_BEZIER_INSERT); +} + +/** + * Execute ImageMap interaction + */ +bool FuSelection::HandleImageMapClick(const SdrObject* pObj, const Point& rPos) +{ + bool bClosed = pObj->IsClosedObj(); + bool bFilled = false; + + if (bClosed) + { + SfxItemSet aSet(mpDoc->GetPool()); + + aSet.Put(pObj->GetMergedItemSet()); + + const XFillStyleItem& rFillStyle = aSet.Get(XATTR_FILLSTYLE); + bFilled = rFillStyle.GetValue() != drawing::FillStyle_NONE; + } + + const SdrLayerIDSet* pVisiLayer = &mpView->GetSdrPageView()->GetVisibleLayers(); + sal_uInt16 nHitLog = sal_uInt16(mpWindow->PixelToLogic(Size(HITPIX, 0)).Width()); + const ::tools::Long n2HitLog = nHitLog * 2; + Point aHitPosR(rPos); + Point aHitPosL(rPos); + Point aHitPosT(rPos); + Point aHitPosB(rPos); + + aHitPosR.AdjustX(n2HitLog); + aHitPosL.AdjustX(-n2HitLog); + aHitPosT.AdjustY(n2HitLog); + aHitPosB.AdjustY(-n2HitLog); + + if (!bClosed || !bFilled + || (SdrObjectPrimitiveHit(*pObj, aHitPosR, nHitLog, *mpView->GetSdrPageView(), pVisiLayer, + false) + && SdrObjectPrimitiveHit(*pObj, aHitPosL, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false) + && SdrObjectPrimitiveHit(*pObj, aHitPosT, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false) + && SdrObjectPrimitiveHit(*pObj, aHitPosB, nHitLog, *mpView->GetSdrPageView(), + pVisiLayer, false))) + { + if (SvxIMapInfo::GetIMapInfo(pObj)) + { + const IMapObject* pIMapObj = SvxIMapInfo::GetHitIMapObject(pObj, rPos); + + if (pIMapObj && !pIMapObj->GetURL().isEmpty()) + { + // Jump to Document + mpWindow->ReleaseMouse(); + SfxStringItem aStrItem(SID_FILE_NAME, pIMapObj->GetURL()); + SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName()); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + SfxBoolItem aBrowseItem(SID_BROWSE, true); + mpWindow->ReleaseMouse(); + pFrame->GetDispatcher()->ExecuteList( + SID_OPENDOC, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + + return true; + } + } + } + + return false; +} + +/** is called when the current function should be aborted.

+ This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted +*/ +bool FuSelection::cancel() +{ + if (mpView->Is3DRotationCreationActive()) + { + mpView->ResetCreationActive(); + mpViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + return true; + } + else + { + return false; + } +} + +SdrObject* FuSelection::pickObject (const Point& rTestPoint) +{ + SdrPageView* pPageView; + sal_uInt16 nHitLog = sal_uInt16 (mpWindow->PixelToLogic(Size(HITPIX,0)).Width()); + return mpView->PickObj(rTestPoint, nHitLog, pPageView, SdrSearchOptions::PICKMARKABLE); +} + +void FuSelection::ForcePointer(const MouseEvent* pMEvt) +{ + if(bMovedToCenterPoint && !bBeginInsertPoint && pMEvt) + { + MouseEvent aMEvt(pMEvt->GetPosPixel(), pMEvt->GetClicks(), + pMEvt->GetMode(), pMEvt->GetButtons(), pMEvt->GetModifier() & ~KEY_SHIFT); + FuDraw::ForcePointer(&aMEvt); + } + else + { + FuDraw::ForcePointer(pMEvt); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusldlg.cxx b/sd/source/ui/func/fusldlg.cxx new file mode 100644 index 000000000..c0269b08a --- /dev/null +++ b/sd/source/ui/func/fusldlg.cxx @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + +#define ITEMVALUE(ItemSet,Id,Cast) static_cast((ItemSet).Get(Id)).GetValue() + + +FuSlideShowDlg::FuSlideShowDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor( pViewSh, pWin, pView, pDoc, rReq ) +{ +} + +rtl::Reference FuSlideShowDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuSlideShowDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSlideShowDlg::DoExecute( SfxRequest& ) +{ + PresentationSettings& rPresentationSettings = mpDoc->getPresentationSettings(); + + SfxItemSetFixed aDlgSet( mpDoc->GetPool() ); + std::vector aPageNameList(mpDoc->GetSdPageCount( PageKind::Standard )); + const OUString& rPresPage = rPresentationSettings.maPresPage; + OUString aFirstPage; + SdPage* pPage = nullptr; + ::tools::Long nPage; + + for( nPage = mpDoc->GetSdPageCount( PageKind::Standard ) - 1; nPage >= 0; nPage-- ) + { + pPage = mpDoc->GetSdPage( static_cast(nPage), PageKind::Standard ); + OUString aStr( pPage->GetName() ); + + if ( aStr.isEmpty() ) + { + aStr = SdResId( STR_PAGE ) + OUString::number( nPage + 1 ); + } + + aPageNameList[ nPage ] = aStr; + + // is this our (existing) first page? + if ( rPresPage == aStr ) + aFirstPage = rPresPage; + else if ( pPage->IsSelected() && aFirstPage.isEmpty() ) + aFirstPage = aStr; + } + SdCustomShowList* pCustomShowList = mpDoc->GetCustomShowList(); // No Create + + if( aFirstPage.isEmpty() && pPage ) + aFirstPage = pPage->GetName(); + + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_ALL, rPresentationSettings.mbAll ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_CUSTOMSHOW, rPresentationSettings.mbCustomShow ) ); + aDlgSet.Put( SfxStringItem( ATTR_PRESENT_DIANAME, aFirstPage ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_ENDLESS, rPresentationSettings.mbEndless ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_MANUEL, rPresentationSettings.mbManual ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_MOUSE, rPresentationSettings.mbMouseVisible ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_PEN, rPresentationSettings.mbMouseAsPen ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_ANIMATION_ALLOWED, rPresentationSettings.mbAnimationAllowed ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_CHANGE_PAGE, !rPresentationSettings.mbLockedPages ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_ALWAYS_ON_TOP, rPresentationSettings.mbAlwaysOnTop ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_FULLSCREEN, rPresentationSettings.mbFullScreen ) ); + aDlgSet.Put( SfxUInt32Item( ATTR_PRESENT_PAUSE_TIMEOUT, rPresentationSettings.mnPauseTimeout ) ); + aDlgSet.Put( SfxBoolItem( ATTR_PRESENT_SHOW_PAUSELOGO, rPresentationSettings.mbShowPauseLogo ) ); + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + aDlgSet.Put( SfxInt32Item( ATTR_PRESENT_DISPLAY, pOptions->GetDisplay() ) ); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( pFact->CreateSdStartPresentationDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, aDlgSet, aPageNameList, pCustomShowList) ); + if( pDlg->Execute() != RET_OK ) + return; + + ::tools::Long nValue32; + bool bValue; + bool bValuesChanged = false; + + pDlg->GetAttr( aDlgSet ); + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_ALL, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbAll ) + { + bValuesChanged = true; + rPresentationSettings.mbAll = bValue; + // remove any previous existing slide + rPresentationSettings.maPresPage.clear(); + } + + if (!rPresentationSettings.mbAll) + { + OUString aPage = ITEMVALUE( aDlgSet, ATTR_PRESENT_DIANAME, SfxStringItem ); + if( aPage != rPresentationSettings.maPresPage ) + { + bValuesChanged = true; + rPresentationSettings.maPresPage = aPage; + } + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_CUSTOMSHOW, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbCustomShow ) + { + bValuesChanged = true; + rPresentationSettings.mbCustomShow = bValue; + rPresentationSettings.mbStartCustomShow = false; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_ENDLESS, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbEndless ) + { + bValuesChanged = true; + rPresentationSettings.mbEndless = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_MANUEL, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbManual ) + { + bValuesChanged = true; + rPresentationSettings.mbManual = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_MOUSE, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbMouseVisible ) + { + bValuesChanged = true; + rPresentationSettings.mbMouseVisible = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_PEN, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbMouseAsPen ) + { + bValuesChanged = true; + rPresentationSettings.mbMouseAsPen = bValue; + } + + bValue = !ITEMVALUE( aDlgSet, ATTR_PRESENT_CHANGE_PAGE, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbLockedPages ) + { + bValuesChanged = true; + rPresentationSettings.mbLockedPages = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_ANIMATION_ALLOWED, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbAnimationAllowed ) + { + bValuesChanged = true; + rPresentationSettings.mbAnimationAllowed = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_ALWAYS_ON_TOP, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbAlwaysOnTop ) + { + bValuesChanged = true; + rPresentationSettings.mbAlwaysOnTop = bValue; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_FULLSCREEN, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbFullScreen ) + { + bValuesChanged = true; + rPresentationSettings.mbFullScreen = bValue; + } + + nValue32 = ITEMVALUE( aDlgSet, ATTR_PRESENT_PAUSE_TIMEOUT, SfxUInt32Item ); + if( nValue32 != rPresentationSettings.mnPauseTimeout ) + { + bValuesChanged = true; + rPresentationSettings.mnPauseTimeout = nValue32; + } + + bValue = ITEMVALUE( aDlgSet, ATTR_PRESENT_SHOW_PAUSELOGO, SfxBoolItem ); + if ( bValue != rPresentationSettings.mbShowPauseLogo ) + { + bValuesChanged = true; + rPresentationSettings.mbShowPauseLogo = bValue; + } + + pOptions->SetDisplay( ITEMVALUE( aDlgSet, ATTR_PRESENT_DISPLAY, SfxInt32Item ) ); + + // is something has changed, we set the modified flag + if ( bValuesChanged ) + mpDoc->SetChanged(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusnapln.cxx b/sd/source/ui/func/fusnapln.cxx new file mode 100644 index 000000000..ee51d78ce --- /dev/null +++ b/sd/source/ui/func/fusnapln.cxx @@ -0,0 +1,196 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + + +FuSnapLine::FuSnapLine(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq) : + FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuSnapLine::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuSnapLine( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSnapLine::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nHelpLine = 0; + bool bCreateNew = true; + + // Get index of snap line or snap point from the request. + const SfxUInt32Item* pHelpLineIndex = rReq.GetArg(ID_VAL_INDEX); + if (pHelpLineIndex != nullptr) + { + nHelpLine = static_cast(pHelpLineIndex->GetValue()); + // Reset the argument pointer to trigger the display of the dialog. + pArgs = nullptr; + } + + SdrPageView* pPV = mpView->GetSdrPageView(); + + if (!pArgs) + { + SfxItemSetFixed aNewAttr(mpViewShell->GetPool()); + bool bLineExist (false); + Point aLinePos; + + if (pHelpLineIndex == nullptr) + { + // The index of the snap line is not provided as argument to the + // request. Determine it from the mouse position. + + aLinePos = static_cast(mpViewShell)->GetMousePos(); + + if ( aLinePos.X() >= 0 ) + { + aLinePos = mpWindow->PixelToLogic(aLinePos); + sal_uInt16 nHitLog = static_cast(mpWindow->PixelToLogic(Size(HITPIX,0)).Width()); + bLineExist = mpView->PickHelpLine(aLinePos, nHitLog, *mpWindow->GetOutDev(), nHelpLine, pPV); + if ( bLineExist ) + aLinePos = (pPV->GetHelpLines())[nHelpLine].GetPos(); + else + pPV = mpView->GetSdrPageView(); + + pPV->LogicToPagePos(aLinePos); + } + else + aLinePos = Point(0,0); + } + else + { + assert(pPV!=nullptr); + aLinePos = (pPV->GetHelpLines())[nHelpLine].GetPos(); + pPV->LogicToPagePos(aLinePos); + bLineExist = true; + } + aNewAttr.Put(SfxInt32Item(ATTR_SNAPLINE_X, aLinePos.X())); + aNewAttr.Put(SfxInt32Item(ATTR_SNAPLINE_Y, aLinePos.Y())); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = mpViewShell->GetActiveWindow(); + ScopedVclPtr pDlg( pFact->CreateSdSnapLineDlg(pWin ? pWin->GetFrameWeld() : nullptr, aNewAttr, mpView) ); + + if ( bLineExist ) + { + pDlg->HideRadioGroup(); + + const SdrHelpLine& rHelpLine = (pPV->GetHelpLines())[nHelpLine]; + + if ( rHelpLine.GetKind() == SdrHelpLineKind::Point ) + { + pDlg->SetText(SdResId(STR_SNAPDLG_SETPOINT)); + pDlg->SetInputFields(true, true); + } + else + { + pDlg->SetText(SdResId(STR_SNAPDLG_SETLINE)); + + if ( rHelpLine.GetKind() == SdrHelpLineKind::Vertical ) + pDlg->SetInputFields(true, false); + else + pDlg->SetInputFields(false, true); + } + bCreateNew = false; + } + else + pDlg->HideDeleteBtn(); + + sal_uInt16 nResult = pDlg->Execute(); + + pDlg->GetAttr(aNewAttr); + pDlg.disposeAndClear(); + + switch( nResult ) + { + case RET_OK: + rReq.Done(aNewAttr); + pArgs = rReq.GetArgs(); + break; + + case RET_SNAP_DELETE: + // delete snap object + if ( !bCreateNew ) + pPV->DeleteHelpLine(nHelpLine); + [[fallthrough]]; + default: + return; + } + } + Point aHlpPos; + + aHlpPos.setX( static_cast( pArgs->Get(ATTR_SNAPLINE_X)).GetValue() ); + aHlpPos.setY( static_cast( pArgs->Get(ATTR_SNAPLINE_Y)).GetValue() ); + pPV->PagePosToLogic(aHlpPos); + + if ( bCreateNew ) + { + SdrHelpLineKind eKind; + + pPV = mpView->GetSdrPageView(); + + switch ( static_cast(static_cast( + pArgs->Get(ATTR_SNAPLINE_KIND)).GetValue()) ) + { + case SnapKind::Horizontal : eKind = SdrHelpLineKind::Horizontal; break; + case SnapKind::Vertical : eKind = SdrHelpLineKind::Vertical; break; + default : eKind = SdrHelpLineKind::Point; break; + } + pPV->InsertHelpLine(SdrHelpLine(eKind, aHlpPos)); + } + else + { + const SdrHelpLine& rHelpLine = (pPV->GetHelpLines())[nHelpLine]; + pPV->SetHelpLine(nHelpLine, SdrHelpLine(rHelpLine.GetKind(), aHlpPos)); + } +} + +void FuSnapLine::Activate() +{ +} + +void FuSnapLine::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fusumry.cxx b/sd/source/ui/func/fusumry.cxx new file mode 100644 index 000000000..9b160099c --- /dev/null +++ b/sd/source/ui/func/fusumry.cxx @@ -0,0 +1,229 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + + +FuSummaryPage::FuSummaryPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuSummaryPage::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuSummaryPage( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuSummaryPage::DoExecute( SfxRequest& ) +{ + std::unique_ptr pOutl; + rtl::Reference pSummaryPage; + sal_uInt16 i = 0; + sal_uInt16 nFirstPage = SDRPAGE_NOTFOUND; + sal_uInt16 nSelectedPages = 0; + sal_uInt16 nCount = mpDoc->GetSdPageCount(PageKind::Standard); + + while (i < nCount && nSelectedPages <= 1) + { + /* How many pages are selected? + exactly one: pool everything from this page + otherwise: only pool the selected pages */ + SdPage* pActualPage = mpDoc->GetSdPage(i, PageKind::Standard); + + if (pActualPage->IsSelected()) + { + if (nFirstPage == SDRPAGE_NOTFOUND) + { + nFirstPage = i; + } + + nSelectedPages++; + } + + i++; + } + + bool bBegUndo = false; + + SfxStyleSheet* pStyle = nullptr; + + for (i = nFirstPage; i < nCount; i++) + { + SdPage* pActualPage = mpDoc->GetSdPage(i, PageKind::Standard); + + if (nSelectedPages <= 1 || pActualPage->IsSelected()) + { + SdPage* pActualNotesPage = mpDoc->GetSdPage(i, PageKind::Notes); + SdrTextObj* pTextObj = static_cast( pActualPage->GetPresObj(PresObjKind::Title) ); + + if (pTextObj && !pTextObj->IsEmptyPresObj()) + { + if (!pSummaryPage) + { + // insert "table of content"-page and create outliner + const bool bUndo = mpView->IsUndoEnabled(); + + if( bUndo ) + { + mpView->BegUndo(SdResId(STR_UNDO_SUMMARY_PAGE)); + bBegUndo = true; + } + + SdrLayerIDSet aVisibleLayers = pActualPage->TRG_GetMasterPageVisibleLayers(); + + // page with title & structuring! + pSummaryPage = mpDoc->AllocSdPage(false); + pSummaryPage->SetSize(pActualPage->GetSize() ); + pSummaryPage->SetBorder(pActualPage->GetLeftBorder(), + pActualPage->GetUpperBorder(), + pActualPage->GetRightBorder(), + pActualPage->GetLowerBorder() ); + + // insert page at the back + mpDoc->InsertPage(pSummaryPage.get(), nCount * 2 + 1); + if( bUndo ) + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoNewPage(*pSummaryPage)); + + // use MasterPage of the current page + pSummaryPage->TRG_SetMasterPage(pActualPage->TRG_GetMasterPage()); + pSummaryPage->SetLayoutName(pActualPage->GetLayoutName()); + pSummaryPage->SetAutoLayout(AUTOLAYOUT_TITLE_CONTENT, true); + pSummaryPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + pSummaryPage->setHeaderFooterSettings(pActualPage->getHeaderFooterSettings()); + + // notes-page + rtl::Reference pNotesPage = mpDoc->AllocSdPage(false); + pNotesPage->SetSize(pActualNotesPage->GetSize()); + pNotesPage->SetBorder(pActualNotesPage->GetLeftBorder(), + pActualNotesPage->GetUpperBorder(), + pActualNotesPage->GetRightBorder(), + pActualNotesPage->GetLowerBorder() ); + pNotesPage->SetPageKind(PageKind::Notes); + + // insert page at the back + mpDoc->InsertPage(pNotesPage.get(), nCount * 2 + 2); + + if( bUndo ) + mpView->AddUndo(mpDoc->GetSdrUndoFactory().CreateUndoNewPage(*pNotesPage)); + + // use MasterPage of the current page + pNotesPage->TRG_SetMasterPage(pActualNotesPage->TRG_GetMasterPage()); + pNotesPage->SetLayoutName(pActualNotesPage->GetLayoutName()); + pNotesPage->SetAutoLayout(pActualNotesPage->GetAutoLayout(), true); + pNotesPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + pNotesPage->setHeaderFooterSettings(pActualNotesPage->getHeaderFooterSettings()); + + pOutl.reset(new SdOutliner( mpDoc, OutlinerMode::OutlineObject )); + pOutl->SetUpdateLayout(false); + pOutl->EnableUndo(false); + + if (mpDocSh) + pOutl->SetRefDevice(SD_MOD()->GetVirtualRefDevice()); + + pOutl->SetDefTab( mpDoc->GetDefaultTabulator() ); + pOutl->SetStyleSheetPool(static_cast(mpDoc->GetStyleSheetPool())); + pStyle = pSummaryPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + pOutl->SetStyleSheet( 0, pStyle ); + } + + // add text + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + // #118876#, check if the OutlinerParaObject is created successfully + if( pParaObj ) + { + pParaObj->SetOutlinerMode( OutlinerMode::OutlineObject ); + pOutl->AddText(*pParaObj); + } + } + } + } + + if (!pSummaryPage) + return; + + SdrTextObj* pTextObj = static_cast( pSummaryPage->GetPresObj(PresObjKind::Outline) ); + + if (!pTextObj) + return; + + // remove hard break- and character attributes + SfxItemSetFixed aEmptyEEAttr(mpDoc->GetPool()); + sal_Int32 nParaCount = pOutl->GetParagraphCount(); + + for (sal_Int32 nPara = 0; nPara < nParaCount; nPara++) + { + pOutl->SetStyleSheet( nPara, pStyle ); + pOutl->RemoveCharAttribs(nPara); + pOutl->SetParaAttribs(nPara, aEmptyEEAttr); + pOutl->SetDepth(pOutl->GetParagraph(nPara), 0); + } + + pTextObj->SetOutlinerParaObject( pOutl->CreateParaObject() ); + pTextObj->SetEmptyPresObj(false); + + // remove hard attributes (Flag to sal_True) + SfxItemSet aAttr(mpDoc->GetPool()); + aAttr.Put(XLineStyleItem(drawing::LineStyle_NONE)); + aAttr.Put(XFillStyleItem(drawing::FillStyle_NONE)); + pTextObj->SetMergedItemSet(aAttr); + + if( bBegUndo ) + mpView->EndUndo(); + pOutl.reset(); + + DrawViewShell* pDrawViewShell= dynamic_cast< DrawViewShell* >( mpViewShell ); + if(pDrawViewShell) + { + pDrawViewShell->SwitchPage( (pSummaryPage->GetPageNum() - 1) / 2); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futempl.cxx b/sd/source/ui/func/futempl.cxx new file mode 100644 index 000000000..2c0c22ecd --- /dev/null +++ b/sd/source/ui/func/futempl.cxx @@ -0,0 +1,638 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star::uno; +using namespace com::sun::star::container; +using namespace com::sun::star::beans; +using namespace com::sun::star::style; + +namespace sd +{ + + +FuTemplate::FuTemplate ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ) + : FuPoor( pViewSh, pWin, pView, pDoc, rReq ) +{ +} + +rtl::Reference FuTemplate::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuTemplate( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuTemplate::DoExecute( SfxRequest& rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nSId = rReq.GetSlot(); + + // get StyleSheet parameter + SfxStyleSheetBasePool* pSSPool = mpDoc->GetDocSh()->GetStyleSheetPool(); + SfxStyleSheetBase* pStyleSheet = nullptr; + + const SfxPoolItem* pItem; + SfxStyleFamily nFamily = SfxStyleFamily(USHRT_MAX); + if( pArgs && SfxItemState::SET == pArgs->GetItemState( SID_STYLE_FAMILY, + false, &pItem )) + { + nFamily = static_cast( pArgs->Get( SID_STYLE_FAMILY ).GetValue()); + } + else if( pArgs && SfxItemState::SET == pArgs->GetItemState( SID_STYLE_FAMILYNAME, + false, &pItem )) + { + OUString sFamily = pArgs->Get( SID_STYLE_FAMILYNAME ).GetValue(); + if (sFamily == "graphics") + nFamily = SfxStyleFamily::Para; + else + nFamily = SfxStyleFamily::Pseudo; + } + + OUString aStyleName; + sal_uInt16 nRetMask = static_cast(SfxStyleSearchBits::All); + + switch( nSId ) + { + case SID_STYLE_APPLY: + case SID_STYLE_EDIT: + case SID_STYLE_DELETE: + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + case SID_STYLE_FAMILY: + case SID_STYLE_NEW_BY_EXAMPLE: + { + const SfxStringItem* pNameItem = rReq.GetArg(SID_APPLY_STYLE); + const SfxStringItem* pFamilyItem = rReq.GetArg(SID_STYLE_FAMILYNAME); + if ( pFamilyItem && pNameItem ) + { + try + { + Reference< XStyleFamiliesSupplier > xModel(mpDoc->GetDocSh()->GetModel(), UNO_QUERY_THROW ); + Reference< XNameAccess > xCont( xModel->getStyleFamilies() ); + Reference< XNameAccess > xStyles( xCont->getByName(pFamilyItem->GetValue()), UNO_QUERY_THROW ); + Reference< XPropertySet > xInfo( xStyles->getByName( pNameItem->GetValue() ), UNO_QUERY_THROW ); + + OUString aUIName; + xInfo->getPropertyValue( "DisplayName" ) >>= aUIName; + if ( !aUIName.isEmpty() ) + rReq.AppendItem( SfxStringItem( nSId, aUIName ) ); + } + catch( Exception& ) + { + } + } + + if (pArgs && pArgs->GetItemState(nSId) == SfxItemState::SET) + aStyleName = static_cast( pArgs->Get( nSId ) ).GetValue(); + } + } + + switch( nSId ) + { + case SID_STYLE_NEW: + { + SfxStyleSheetBase *p = pSSPool->Find(aStyleName, nFamily ); + if(p) + { + pSSPool->Remove(p); + p = nullptr; + } + pStyleSheet = &pSSPool->Make( aStyleName, nFamily, SfxStyleSearchBits::UserDefined ); + + if (pArgs && pArgs->GetItemState(SID_STYLE_REFERENCE) == SfxItemState::SET) + { + OUString aParentName( pArgs->Get(SID_STYLE_REFERENCE).GetValue()); + pStyleSheet->SetParent(aParentName); + } + else + { + pStyleSheet->SetParent(SdResId(STR_STANDARD_STYLESHEET_NAME)); + } + } + break; + + case SID_STYLE_NEW_BY_EXAMPLE: + { + // at the moment, the dialog to enter the name of the template is still opened + SfxStyleSheetBase *p = pSSPool->Find(aStyleName, nFamily ); + if(p) + { + pSSPool->Remove(p); + p = nullptr; + } + pStyleSheet = &pSSPool->Make( aStyleName, nFamily, SfxStyleSearchBits::UserDefined ); + pStyleSheet->SetParent(SdResId(STR_STANDARD_STYLESHEET_NAME)); + } + break; + + case SID_STYLE_EDIT: + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + break; + + case SID_STYLE_DELETE: + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + if( pStyleSheet ) + { + pSSPool->Remove( pStyleSheet ); + nRetMask = sal_uInt16(true); + mpDoc->SetChanged(); + } + else + { + nRetMask = sal_uInt16(false); + } + break; + + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + pStyleSheet->SetHidden( nSId == SID_STYLE_HIDE ); + nRetMask = sal_uInt16(true); + break; + + case SID_STYLE_APPLY: + // apply the template to the document + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + + // do not set presentation styles, they will be set implicit + if ( pStyleSheet && pStyleSheet->GetFamily() != SfxStyleFamily::Pseudo ) + { + SfxStyleSheet* pOldStyleSheet = mpView->GetStyleSheet(); + OUString aStr; + + if( // if the object had no style sheet, allow all + !pOldStyleSheet || + + // allow if old and new style sheet has same family + pStyleSheet->GetFamily() == pOldStyleSheet->GetFamily() || + + // allow if old was background objects and new is graphics + (pStyleSheet->GetFamily() == SfxStyleFamily::Para && pOldStyleSheet->GetHelpId( aStr ) == HID_PSEUDOSHEET_BACKGROUNDOBJECTS) || + + // allow if old was presentation and we are a drawing document + (pOldStyleSheet->GetFamily() == SfxStyleFamily::Page && mpDoc->GetDocumentType() == DocumentType::Draw) ) + { + mpView->SetStyleSheet( static_cast(pStyleSheet)); + mpDoc->SetChanged(); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_FAMILY2 ); + } + } + break; + + case SID_STYLE_WATERCAN: + { + if( !SD_MOD()->GetWaterCan() ) + { + if (pArgs && pArgs->GetItemState( nSId ) == SfxItemState::SET) + { + aStyleName = static_cast( pArgs->Get( nSId ) ).GetValue(); + SD_MOD()->SetWaterCan( true ); + pStyleSheet = pSSPool->Find( aStyleName, nFamily); + } + // no presentation object templates, they are only allowed implicitly + if( pStyleSheet && pStyleSheet->GetFamily() != SfxStyleFamily::Pseudo ) + { + static_cast( pSSPool )->SetActualStyleSheet( pStyleSheet ); + + // we switch explicitly into selection mode + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + + } + else + SD_MOD()->SetWaterCan( false ); + } + else + { + SD_MOD()->SetWaterCan( false ); + // we have to re-enable to tools-bar + mpViewShell->Invalidate(); + } + } + break; + + default: + break; + } + + switch( nSId ) + { + case SID_STYLE_NEW: + case SID_STYLE_EDIT: + { + PresentationObjects ePO = PresentationObjects::Outline_1; + + if( pStyleSheet ) + { + ScopedVclPtr pStdDlg; + ScopedVclPtr pPresDlg; + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + + SfxStyleFamily eFamily = pStyleSheet->GetFamily(); + + if (eFamily == SfxStyleFamily::Para) + { + pStdDlg.disposeAndReset(pFact ? pFact->CreateSdTabTemplateDlg(mpViewShell->GetFrameWeld(), mpDoc->GetDocSh(), *pStyleSheet, mpDoc, mpView) : nullptr); + } + else if (eFamily == SfxStyleFamily::Pseudo) + { + OUString aName(pStyleSheet->GetName()); + bool bBackground = false; + bool bOldDocInOtherLanguage = false; + + if (aName == SdResId(STR_PSEUDOSHEET_TITLE)) + { + ePO = PresentationObjects::Title; + } + else if (aName == SdResId(STR_PSEUDOSHEET_SUBTITLE)) + { + ePO = PresentationObjects::Subtitle; + } + else if (aName == + SdResId(STR_PSEUDOSHEET_BACKGROUND)) + { + bBackground = true; + ePO = PresentationObjects::Background; + } + else if (aName == + SdResId(STR_PSEUDOSHEET_BACKGROUNDOBJECTS)) + { + ePO = PresentationObjects::BackgroundObjects; + } + else if (aName == + SdResId(STR_PSEUDOSHEET_NOTES)) + { + ePO = PresentationObjects::Notes; + } + else if(aName.indexOf(SdResId(STR_PSEUDOSHEET_OUTLINE)) != -1) + { + OUString aOutlineStr(SdResId(STR_PSEUDOSHEET_OUTLINE)); + // determine number, mind the blank between name and number + std::u16string_view aNumStr(aName.subView(aOutlineStr.getLength() + 1)); + sal_uInt16 nLevel = static_cast(o3tl::toInt32(aNumStr)); + switch (nLevel) + { + case 1: ePO = PresentationObjects::Outline_1; break; + case 2: ePO = PresentationObjects::Outline_2; break; + case 3: ePO = PresentationObjects::Outline_3; break; + case 4: ePO = PresentationObjects::Outline_4; break; + case 5: ePO = PresentationObjects::Outline_5; break; + case 6: ePO = PresentationObjects::Outline_6; break; + case 7: ePO = PresentationObjects::Outline_7; break; + case 8: ePO = PresentationObjects::Outline_8; break; + case 9: ePO = PresentationObjects::Outline_9; break; + } + } + else + { + OSL_FAIL("StyleSheet from older version with different language"); + bOldDocInOtherLanguage = true; + } + + if( !bOldDocInOtherLanguage ) + { + pPresDlg.disposeAndReset(pFact ? pFact->CreateSdPresLayoutTemplateDlg(mpDocSh, mpViewShell->GetFrameWeld(), bBackground, *pStyleSheet, ePO, pSSPool ) : nullptr); + } + } + + sal_uInt16 nResult = RET_CANCEL; + const SfxItemSet* pOutSet = nullptr; + if (pStdDlg) + { + nResult = pStdDlg->Execute(); + pOutSet = pStdDlg->GetOutputItemSet(); + } + else if( pPresDlg ) + { + nResult = pPresDlg->Execute(); + pOutSet = pPresDlg->GetOutputItemSet(); + } + + switch( nResult ) + { + case RET_OK: + { + nRetMask = static_cast(pStyleSheet->GetMask()); + + if (eFamily == SfxStyleFamily::Pseudo) + { + SfxItemSet aTempSet(*pOutSet); + /* Extract SvxBrushItem out of set and insert SvxColorItem */ + const SvxBrushItem* pBrushItem = aTempSet.GetItem( SID_ATTR_BRUSH_CHAR ); + + if ( pBrushItem ) + { + SvxColorItem aBackColorItem(pBrushItem->GetColor(), EE_CHAR_BKGCOLOR); + aTempSet.ClearItem( EE_CHAR_BKGCOLOR ); + aTempSet.Put( aBackColorItem ); + } + static_cast(pStyleSheet)->AdjustToFontHeight(aTempSet); + + /* Special treatment: reset the INVALIDS to + NULL-Pointer (otherwise INVALIDs or pointer point + to DefaultItems in the template; both would + prevent the attribute inheritance) */ + aTempSet.ClearInvalidItems(); + + // EE_PARA_NUMBULLET item is only valid in first outline template + if( (ePO >= PresentationObjects::Outline_2) && (ePO <= PresentationObjects::Outline_9) ) + { + if (aTempSet.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET) + { + SvxNumRule aRule(aTempSet.GetItem(EE_PARA_NUMBULLET)->GetNumRule()); + + OUString sStyleName(SdResId(STR_PSEUDOSHEET_OUTLINE) + " 1"); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( sStyleName, SfxStyleFamily::Pseudo); + + if(pFirstStyleSheet) + { + pFirstStyleSheet->GetItemSet().Put( SvxNumBulletItem( aRule, EE_PARA_NUMBULLET )); + SdStyleSheet* pRealSheet = static_cast(pFirstStyleSheet)->GetRealStyleSheet(); + pRealSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + + aTempSet.ClearItem( EE_PARA_NUMBULLET ); + } + } + + pStyleSheet->GetItemSet().Put(aTempSet); + SdStyleSheet::BroadcastSdStyleSheetChange(pStyleSheet, ePO, pSSPool); + } + + SfxItemSet& rAttr = pStyleSheet->GetItemSet(); + + sdr::properties::CleanupFillProperties( rAttr ); + + // check for unique names of named items for xml + if( rAttr.GetItemState( XATTR_FILLBITMAP ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_FILLBITMAP ); + std::unique_ptr pNewItem = static_cast(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_LINEDASH ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_LINEDASH ); + std::unique_ptr pNewItem = static_cast(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_LINESTART ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_LINESTART ); + std::unique_ptr pNewItem = static_cast(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_LINEEND ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_LINEEND ); + std::unique_ptr pNewItem = static_cast(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_FILLGRADIENT ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_FILLGRADIENT ); + std::unique_ptr pNewItem = static_cast(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_FILLFLOATTRANSPARENCE ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_FILLFLOATTRANSPARENCE ); + std::unique_ptr pNewItem = static_cast(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + if( rAttr.GetItemState( XATTR_FILLHATCH ) == SfxItemState::SET ) + { + const SfxPoolItem* pOldItem = rAttr.GetItem( XATTR_FILLHATCH ); + std::unique_ptr pNewItem = static_cast(pOldItem)->checkForUniqueItem( mpDoc ); + if( pNewItem ) + { + rAttr.Put( std::move(pNewItem) ); + } + } + + static_cast( pStyleSheet )->Broadcast( SfxHint( SfxHintId::DataChanged ) ); + + DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >( mpViewShell ); + if( pDrawViewShell ) + { + PageKind ePageKind = pDrawViewShell->GetPageKind(); + if( ePageKind == PageKind::Notes || ePageKind == PageKind::Handout ) + { + SdPage* pPage = mpViewShell->GetActualPage(); + + if(pDrawViewShell->GetEditMode() == EditMode::MasterPage) + { + pPage = static_cast((&(pPage->TRG_GetMasterPage()))); + } + + if( pPage ) + { + SdrObjListIter aIter( pPage ); + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + if( dynamic_cast< const SdrPageObj *>( pObj ) != nullptr ) + { + // repaint only + pObj->ActionChanged(); + // pObj->SendRepaintBroadcast(); + } + } + } + } + } + + if( mpDoc->GetOnlineSpell() ) + { + if( SfxItemState::SET == rAttr.GetItemState(EE_CHAR_LANGUAGE, false ) || + SfxItemState::SET == rAttr.GetItemState(EE_CHAR_LANGUAGE_CJK, false ) || + SfxItemState::SET == rAttr.GetItemState(EE_CHAR_LANGUAGE_CTL, false ) ) + { + mpDoc->StopOnlineSpelling(); + mpDoc->StartOnlineSpelling(); + } + } + + mpDoc->SetChanged(); + } + break; + + default: + { + if( nSId == SID_STYLE_NEW ) + pSSPool->Remove( pStyleSheet ); + } + return; // Cancel + } + } + } + break; + + case SID_STYLE_NEW_BY_EXAMPLE: + { + if( pStyleSheet ) + { + nRetMask = static_cast(pStyleSheet->GetMask()); + SfxItemSet aCoreSet( mpDoc->GetPool() ); + mpView->GetAttributes( aCoreSet, true ); + + // if the object had a template, this becomes parent of the new template + SfxStyleSheet* pOldStyle = mpView->GetStyleSheet(); + + // if pOldStyle == pStyleSheet -> recursion + if( pOldStyle != pStyleSheet ) + { + if (pOldStyle) + { + pStyleSheet->SetParent(pOldStyle->GetName()); + } + + SfxItemSet* pStyleSet = &pStyleSheet->GetItemSet(); + pStyleSet->Put(aCoreSet); + + /* apply template (but not when somebody is editing a text. + To do this, the edit engine had to be capable to use + templates on a character level. */ + if (!mpView->GetTextEditObject()) + { + mpView->SetStyleSheet( static_cast(pStyleSheet)); + } + + static_cast( pStyleSheet )->Broadcast( SfxHint( SfxHintId::DataChanged ) ); + mpDoc->SetChanged(); + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_FAMILY2 ); + } + } + } + break; + + case SID_STYLE_UPDATE_BY_EXAMPLE: + { + if ((mpView->AreObjectsMarked() && mpView->GetMarkedObjectList().GetMarkCount() == 1) || + dynamic_cast< const OutlineView *>( mpView ) != nullptr) + { + pStyleSheet = mpView->GetStyleSheet(); + + if( pStyleSheet ) + { + nRetMask = static_cast(pStyleSheet->GetMask()); + SfxItemSet aCoreSet( mpDoc->GetPool() ); + mpView->GetAttributes( aCoreSet ); + + SfxItemSet* pStyleSet = &pStyleSheet->GetItemSet(); + pStyleSet->Put( aCoreSet ); + + mpView->SetStyleSheet( static_cast(pStyleSheet)); + + static_cast( pStyleSheet )->Broadcast( SfxHint( SfxHintId::DataChanged ) ); + mpDoc->SetChanged(); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_FAMILY2 ); + } + } + } + break; + + } + if( nRetMask != static_cast(SfxStyleSearchBits::All) ) + rReq.SetReturnValue( SfxUInt16Item( nSId, nRetMask ) ); +} + +void FuTemplate::Activate() +{ +} + +void FuTemplate::Deactivate() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futext.cxx b/sd/source/ui/func/futext.cxx new file mode 100644 index 000000000..725bf96e1 --- /dev/null +++ b/sd/source/ui/func/futext.cxx @@ -0,0 +1,1464 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +namespace sd { + +const sal_uInt16 SidArray[] = { + SID_STYLE_FAMILY2, // 5542 + SID_STYLE_FAMILY5, // 5545 + SID_REDO, // 5700 + SID_UNDO, // 5701 + SID_CUT, // 5710 + SID_COPY, // 5711 + SID_ATTR_TABSTOP, // 10002 + SID_ATTR_CHAR_FONT, // 10007 + SID_ATTR_CHAR_POSTURE, // 10008 + SID_ATTR_CHAR_WEIGHT, // 10009 + SID_ATTR_CHAR_SHADOWED, // 10010 + SID_ATTR_CHAR_STRIKEOUT, // 10013 + SID_ATTR_CHAR_UNDERLINE, // 10014 + SID_ATTR_CHAR_FONTHEIGHT, // 10015 + SID_ATTR_CHAR_COLOR, // 10017 + SID_ATTR_CHAR_KERNING, // 10018 + SID_ATTR_CHAR_CASEMAP, // 10019 + SID_ATTR_PARA_ADJUST_LEFT, // 10028 + SID_ATTR_PARA_ADJUST_RIGHT, // 10029 + SID_ATTR_PARA_ADJUST_CENTER, // 10030 + SID_ATTR_PARA_ADJUST_BLOCK, // 10031 + SID_ATTR_PARA_LINESPACE_10, // 10034 + SID_ATTR_PARA_LINESPACE_15, // 10035 + SID_ATTR_PARA_LINESPACE_20, // 10036 + SID_ATTR_PARA_ULSPACE, // 10042 + SID_ATTR_PARA_LRSPACE, // 10043 + SID_ATTR_TRANSFORM_POS_X, // 10088 + SID_ATTR_TRANSFORM_POS_Y, // 10089 + SID_ATTR_TRANSFORM_WIDTH, // 10090 + SID_ATTR_TRANSFORM_HEIGHT, // 10091 + SID_ATTR_TRANSFORM_ROT_X, // 10093 + SID_ATTR_TRANSFORM_ROT_Y, // 10094 + SID_ATTR_TRANSFORM_ANGLE, // 10095 //Added + SID_OUTLINE_UP, // 10150 + SID_OUTLINE_DOWN, // 10151 + SID_OUTLINE_LEFT, // 10152 + SID_OUTLINE_RIGHT, // 10153 + SID_ATTR_TRANSFORM_PROTECT_POS, // 10236 + SID_ATTR_TRANSFORM_PROTECT_SIZE, // 10237 //Added + SID_FORMTEXT_STYLE, // 10257 + SID_SET_SUPER_SCRIPT, // 10294 + SID_SET_SUB_SCRIPT, // 10295 + SID_ATTR_TRANSFORM_AUTOWIDTH, // 10310 + SID_ATTR_TRANSFORM_AUTOHEIGHT, // 10311 //Added + SID_HYPERLINK_GETLINK, // 10361 + SID_DEC_INDENT, // 10461 + SID_INC_INDENT, // 10462 + SID_CHARMAP, // 10503 + SID_TEXTDIRECTION_LEFT_TO_RIGHT, // 10907 + SID_TEXTDIRECTION_TOP_TO_BOTTOM, // 10908 + SID_ATTR_PARA_LEFT_TO_RIGHT, // 10950 + SID_ATTR_PARA_RIGHT_TO_LEFT, // 10951 + SID_PARASPACE_INCREASE, // 11145 + SID_PARASPACE_DECREASE, // 11146 + FN_NUM_BULLET_ON, // 20138 + 0 }; + + +/** + * base class for text functions + */ +FuText::FuText( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +: FuConstruct(pViewSh, pWin, pView, pDoc, rReq) +, bFirstObjCreated(false) +, bJustEndedEdit(false) +, rRequest (rReq) +{ +} + +rtl::Reference FuText::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuText( pViewSh, pWin, pView, pDoc, rReq ) ); + return xFunc; +} + +void FuText::disposing() +{ + if(mpView) + { + if(mpView->SdrEndTextEdit() == SdrEndTextEditKind::Deleted) + mxTextObj.reset(nullptr); + + // reset the RequestHandler of the used Outliner to the handler of the document + ::Outliner* pOutliner = mpView->GetTextEditOutliner(); + + if (pOutliner) + pOutliner->SetStyleSheetPool(static_cast(mpDoc->GetStyleSheetPool())); + } +} + +/************************************************************************* +|* +|* Execute functionality of this class: +|* +|* #71422: Start the functionality of this class in this method +|* and not in the ctor. +|* If you construct an object of this class and you put the +|* address of this object to pFuActual you've got a problem, +|* because some methods inside DoExecute use the pFuActual-Pointer. +|* If the code inside DoExecute is executed inside the ctor, +|* the value of pFuActual is not right. And the value will not +|* be right until the ctor finished !!! +|* +\************************************************************************/ +void FuText::DoExecute( SfxRequest& ) +{ + mpViewShell->GetViewShellBase().GetToolBarManager()->SetToolBarShell( + ToolBarManager::ToolBarGroup::Function, + ToolbarId::Draw_Text_Toolbox_Sd); + + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Edit); + + MouseEvent aMEvt(mpWindow->GetPointerPosPixel()); + + if (nSlotId == SID_TEXTEDIT) + { + // Try to select an object + SdrPageView* pPV = mpView->GetSdrPageView(); + SdrViewEvent aVEvt; + mpView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + mpView->MarkObj(aVEvt.mpRootObj, pPV); + + mxTextObj.reset( dynamic_cast< SdrTextObj* >( aVEvt.mpObj ) ); + } + else if (mpView->AreObjectsMarked()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + mxTextObj.reset( dynamic_cast< SdrTextObj* >( pObj ) ); + } + } + + // check for table + if (mpView->AreObjectsMarked()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( pObj && (pObj->GetObjInventor() == SdrInventor::Default ) && (pObj->GetObjIdentifier() == SdrObjKind::Table) ) + { + mpViewShell->GetViewShellBase().GetToolBarManager()->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Table_Toolbox); + } + } + } + + bool bQuickDrag = true; + + const SfxItemSet* pArgs = rRequest.GetArgs(); + + if (pArgs + + // test for type before using + && SID_TEXTEDIT == nSlotId + && SfxItemState::SET == pArgs->GetItemState(SID_TEXTEDIT) + + && static_cast(pArgs->Get(SID_TEXTEDIT)).GetValue() == 2) + { + // Selection by doubleclick -> don't allow QuickDrag + bQuickDrag = false; + } + + SetInEditMode(aMEvt, bQuickDrag); +} + +bool FuText::MouseButtonDown(const MouseEvent& rMEvt) +{ + bMBDown = true; + bJustEndedEdit = false; + + bool bReturn = FuDraw::MouseButtonDown(rMEvt); + + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + // handle URL also during the text editing + if (rMEvt.GetClicks() == 1 && rMEvt.IsLeft() && rMEvt.IsMod1()) + { + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if (mxTextObj.is() && pOLV && pOLV->GetFieldUnderMousePointer()) + { + const SvxFieldItem* pFieldItem = pOLV->GetFieldUnderMousePointer(); + if (pFieldItem) + { + const SvxFieldData* pField = pFieldItem->GetField(); + + if (auto pURLField = dynamic_cast< const SvxURLField *>( pField )) + { + eHit = SdrHitKind::MarkedObject; + aVEvt.meEvent = SdrEventKind::ExecuteUrl; + aVEvt.mpURLField = pURLField; + } + } + } + } + + if (eHit == SdrHitKind::TextEdit) + { + // hit text -> SdrView handles event + if (mpView->MouseButtonDown(rMEvt, mpWindow->GetOutDev())) + return true; + } + + if (rMEvt.GetClicks() == 1) + { + if (mpView->IsTextEdit() && eHit != SdrHitKind::MarkedObject && eHit != SdrHitKind::Handle) + { + // finish text input + if(mpView->SdrEndTextEdit() == SdrEndTextEditKind::Deleted) + { + /* Bugfix from MBA: during a double click onto the unused? area + in text mode, we get with the second click eHit = + SdrHitKind::TextEditObj since it goes to the TextObject which was + created with the first click. But this is removed by + SdrEndTextEdit since it is empty. But it is still in the mark + list. The call MarkObj further below accesses then the dead + object. As a simple fix, we determine eHit after + SdrEndTextEdit again, this returns then SdrHitKind::NONE. */ + mxTextObj.reset(nullptr); + eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + } + + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Edit); + } + + if (rMEvt.IsLeft() || rMEvt.IsRight()) + { + mpWindow->CaptureMouse(); + SdrPageView* pPV = mpView->GetSdrPageView(); + + if (eHit == SdrHitKind::TextEdit) + { + SetInEditMode(rMEvt, false); + } + else + { + // Don't remark table when clicking in it, mark change triggers a lot of updating + bool bMarkChanges = true; + rtl::Reference< sdr::SelectionController > xSelectionController(mpView->getSelectionController()); + if (eHit == SdrHitKind::TextEditObj && xSelectionController.is()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if (rMarkList.GetMarkCount() == 1 && rMarkList.GetMark(0)->GetMarkedSdrObj() == aVEvt.mpRootObj) + bMarkChanges = false; + } + + if (eHit != SdrHitKind::Handle) + { + // deselect selection + if (!rMEvt.IsShift() && eHit == SdrHitKind::TextEditObj) + { + if(bMarkChanges) + { + mpView->UnmarkAll(); + mpView->SetDragMode(SdrDragMode::Move); + } + } + } + + if ( aVEvt.meEvent == SdrEventKind::ExecuteUrl || + eHit == SdrHitKind::Handle || + eHit == SdrHitKind::MarkedObject || + eHit == SdrHitKind::TextEditObj || + ( eHit == SdrHitKind::UnmarkedObject && bFirstObjCreated && + !bPermanent ) ) + { + // Handle, hit marked or unmarked object + if (eHit == SdrHitKind::TextEditObj) + { + /* hit text of unmarked object: + select object and set to EditMode */ + if (bMarkChanges) + mpView->MarkObj(aVEvt.mpRootObj, pPV); + + if (auto pSdrTextObj = dynamic_cast(aVEvt.mpObj)) + { + mxTextObj.reset( pSdrTextObj ); + } + + SetInEditMode(rMEvt, true); + } + else if (aVEvt.meEvent == SdrEventKind::ExecuteUrl && !rMEvt.IsMod2()) + { + // execute URL + mpWindow->ReleaseMouse(); + SfxStringItem aStrItem(SID_FILE_NAME, aVEvt.mpURLField->GetURL()); + SfxStringItem aReferer(SID_REFERER, mpDocSh->GetMedium()->GetName()); + SfxBoolItem aBrowseItem( SID_BROWSE, true ); + SfxViewFrame* pFrame = mpViewShell->GetViewFrame(); + mpWindow->ReleaseMouse(); + + if (rMEvt.IsMod1()) + { + // open in new frame + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aBrowseItem, &aReferer }); + } + else + { + // open in current frame + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + pFrame->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + } + else + { + // drag object or handle + + // #i78748# + // do the EndTextEdit first, it will delete the handles and force a + // recreation. This will make aVEvt.mpHdl to point to a deleted handle, + // thus it is necessary to reset it and to get it again. + + // #i112855# + // cl: I'm not sure why we checked here also for mxTextObj->GetOutlinerParaObject + // this caused SdrEndTextEdit() to be called also when not in text editing and + // this does not make sense and caused troubles. (see issue 112855) + + if( mpView->IsTextEdit() ) + { + mpView->SdrEndTextEdit(); + bJustEndedEdit = true; + + if(aVEvt.mpHdl) + { + // force new handle identification, the pointer will be dead here + // since SdrEndTextEdit has reset (deleted) the handles. + aVEvt.mpHdl = nullptr; + mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + } + } + + if (!aVEvt.mpHdl) + { + if( eHit == SdrHitKind::UnmarkedObject ) + { + if ( !rMEvt.IsShift() ) + mpView->UnmarkAll(); + + mpView->MarkObj(aVEvt.mpRootObj, pPV); + } + + // Drag object + bFirstMouseMove = true; + aDragTimer.Start(); + } + + if ( ! rMEvt.IsRight()) + { + // we need to pick again since SdrEndTextEdit can rebuild the handles list + eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + if( (eHit == SdrHitKind::Handle) || (eHit == SdrHitKind::MarkedObject) ) + { + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegDragObj(aMDPos, nullptr, aVEvt.mpHdl, nDrgLog); + } + } + bReturn = true; + } + } + else if ( nSlotId != SID_TEXTEDIT && + (bPermanent || !bFirstObjCreated) ) + { + // create object + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Create); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegCreateObj(aMDPos, nullptr, nDrgLog); + } + else + { + // select + if( !rMEvt.IsShift() ) + mpView->UnmarkAll(); + + mpView->BegMarkObj( aMDPos ); + } + } + } + } + else if ( rMEvt.GetClicks() == 2 && !mpView->IsTextEdit() ) + { + MouseEvent aMEvt( mpWindow->GetPointerPosPixel() ); + SetInEditMode( aMEvt, false ); + } + + if (!bIsInDragMode) + { + ForcePointer(&rMEvt); + mpViewShell->GetViewFrame()->GetBindings().Invalidate(SidArray); + } + + return bReturn; +} + +bool FuText::MouseMove(const MouseEvent& rMEvt) +{ + bool bReturn = FuDraw::MouseMove(rMEvt); + + if (aDragTimer.IsActive() ) + { + if( bFirstMouseMove ) + bFirstMouseMove = false; + else + aDragTimer.Stop(); + } + + if (!bReturn && mpView->IsAction() && !mpDocSh->IsReadOnly()) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt(mpWindow->PixelToLogic(aPix)); + + ForceScroll(aPix); + mpView->MovAction(aPnt); + } + + ForcePointer(&rMEvt); + + return bReturn; +} + +void FuText::ImpSetAttributesForNewTextObject(SdrTextObj* pTxtObj) +{ + if(mpDoc->GetDocumentType() == DocumentType::Impress) + { + if( nSlotId == SID_ATTR_CHAR ) + { + /* Create Impress text object (rescales to line height) + We get the correct height during the subsequent creation of the + object, otherwise we draw too much */ + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameHeightItem(0)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + aSet.Put(makeSdrTextAutoGrowHeightItem(true)); + pTxtObj->SetMergedItemSet(aSet); + pTxtObj->AdjustTextFrameWidthAndHeight(); + aSet.Put(makeSdrTextMaxFrameHeightItem(pTxtObj->GetLogicRect().GetSize().Height())); + pTxtObj->SetMergedItemSet(aSet); + const SfxViewShell* pCurrentViewShell = SfxViewShell::Current(); + if (pCurrentViewShell && (pCurrentViewShell->isLOKMobilePhone() || pCurrentViewShell->isLOKTablet())) + pTxtObj->SetText(SdResId(STR_PRESOBJ_TEXT_EDIT_MOBILE)); + } + else if( nSlotId == SID_ATTR_CHAR_VERTICAL ) + { + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameWidthItem(0)); + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + + // Needs to be set since default is SDRTEXTHORZADJUST_BLOCK + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + pTxtObj->SetMergedItemSet(aSet); + pTxtObj->AdjustTextFrameWidthAndHeight(); + aSet.Put(makeSdrTextMaxFrameWidthItem(pTxtObj->GetLogicRect().GetSize().Width())); + pTxtObj->SetMergedItemSet(aSet); + } + } + else + { + if( nSlotId == SID_ATTR_CHAR_VERTICAL ) + { + // draw text object, needs to be initialized when vertical text is used + SfxItemSet aSet(mpViewShell->GetPool()); + + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + + // Set defaults for vertical click-n'drag text object, pool defaults are: + // SdrTextVertAdjustItem: SDRTEXTVERTADJUST_TOP + // SdrTextHorzAdjustItem: SDRTEXTHORZADJUST_BLOCK + // Analog to that: + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BLOCK)); + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + + pTxtObj->SetMergedItemSet(aSet); + } + } +} + +void FuText::ImpSetAttributesFitToSize(SdrTextObj* pTxtObj) +{ + // FitToSize (fit to frame) + SfxItemSetFixed aSet(mpViewShell->GetPool()); + aSet.Put(SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_PROPORTIONAL)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + pTxtObj->SetMergedItemSet(aSet); + pTxtObj->AdjustTextFrameWidthAndHeight(); +} + +void FuText::ImpSetAttributesFitToSizeVertical(SdrTextObj* pTxtObj) +{ + SfxItemSetFixed aSet(mpViewShell->GetPool()); + aSet.Put(SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_PROPORTIONAL)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + pTxtObj->SetMergedItemSet(aSet); + pTxtObj->AdjustTextFrameWidthAndHeight(); +} + +void FuText::ImpSetAttributesFitCommon(SdrTextObj* pTxtObj) +{ + // Normal Textobject + if (mpDoc->GetDocumentType() != DocumentType::Impress) + return; + + if( nSlotId == SID_ATTR_CHAR ) + { + // Impress text object (rescales to line height) + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameHeightItem(0)); + aSet.Put(makeSdrTextMaxFrameHeightItem(0)); + aSet.Put(makeSdrTextAutoGrowHeightItem(true)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + pTxtObj->SetMergedItemSet(aSet); + } + else if( nSlotId == SID_ATTR_CHAR_VERTICAL ) + { + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameWidthItem(0)); + aSet.Put(makeSdrTextMaxFrameWidthItem(0)); + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + pTxtObj->SetMergedItemSet(aSet); + } + + pTxtObj->AdjustTextFrameWidthAndHeight(); +} + +bool FuText::MouseButtonUp(const MouseEvent& rMEvt) +{ + bool bReturn = false; + if (aDragTimer.IsActive()) + { + aDragTimer.Stop(); + bIsInDragMode = false; + } + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + + Point aPnt( mpWindow->PixelToLogic( rMEvt.GetPosPixel() ) ); + + if( (mpView && mpView->MouseButtonUp(rMEvt, mpWindow->GetOutDev())) || rMEvt.GetClicks() == 2 ) + return true; // handle event from SdrView + + bool bEmptyTextObj = false; + + if (mxTextObj.is()) + { + bool bReset = true; + + if (mpView) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1 + && ( rMarkList.GetMark(0)->GetMarkedSdrObj() == mxTextObj.get()) ) + { + if( mxTextObj.is() && !GetTextObj()->GetOutlinerParaObject() ) + bEmptyTextObj = true; + else + bFirstObjCreated = true; + bReset = false; + } + } + + if (bReset) + { + mxTextObj.reset(nullptr); + } + } + + if( mpView && mpView->IsDragObj()) + { + // object was moved + FrameView* pFrameView = mpViewShell->GetFrameView(); + bool bDragWithCopy = (rMEvt.IsMod1() && pFrameView->IsDragWithCopy()); + + if (bDragWithCopy) + { + bDragWithCopy = !mpView->IsPresObjSelected(false); + } + + mpView->SetDragWithCopy(bDragWithCopy); + mpView->EndDragObj( mpView->IsDragWithCopy() ); + mpView->ForceMarkedToAnotherPage(); + mpView->SetCurrentObj(SdrObjKind::Text); + + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if (bJustEndedEdit) + { + bJustEndedEdit = false; + FuPoor::cancel(); + } + if ((rMEvt.GetClicks() != 2) && + !rMEvt.IsShift() && !rMEvt.IsMod1() && !rMEvt.IsMod2() && !rMEvt.IsRight() && + std::abs(aPnt.X() - aMDPos.X()) < nDrgLog && + std::abs(aPnt.Y() - aMDPos.Y()) < nDrgLog) + { + /************************************************************* + * From text mode, you don't want to rotate immediately. + **************************************************************/ + SdrPageView* pPV; + SdrObject* pObj = mpView->PickObj(aMDPos, mpView->getHitTolLog(), pPV, SdrSearchOptions::ALSOONMASTER | SdrSearchOptions::BEFOREMARK); + if (pObj && pPV->IsObjMarkable(pObj)) + { + mpView->UnmarkAllObj(); + mpView->MarkObj(pObj,pPV); + return bReturn; + } + } + } + else if( mpView && mpView->IsCreateObj() && rMEvt.IsLeft()) + { + // object was created + mxTextObj.reset( dynamic_cast< SdrTextObj* >( mpView->GetCreateObj() ) ); + + if( mxTextObj.is() ) + { + //AW outliner needs to be set to vertical when there is no + // outliner object up to now; also it needs to be set back to not + // vertical when there was a vertical one used last time. + OutlinerParaObject* pOPO = GetTextObj()->GetOutlinerParaObject(); + SdrOutliner& rOutl(mxTextObj->getSdrModelFromSdrObject().GetDrawOutliner(GetTextObj())); + bool bVertical((pOPO && pOPO->IsEffectivelyVertical()) + || nSlotId == SID_ATTR_CHAR_VERTICAL + || nSlotId == SID_TEXT_FITTOSIZE_VERTICAL); + rOutl.SetVertical(bVertical); + + // Before ImpSetAttributesForNewTextObject the vertical writing mode + // needs to be set at the object. This is done here at the OutlinerParaObject + // directly to not mirror the layout text items involved. These items will be set + // from ImpSetAttributesForNewTextObject and below. + OutlinerParaObject* pPara = GetTextObj()->GetOutlinerParaObject(); + + if(!pPara) + { + GetTextObj()->ForceOutlinerParaObject(); + pPara = GetTextObj()->GetOutlinerParaObject(); + } + + if(pPara && bVertical != pPara->IsEffectivelyVertical()) + { + // set ParaObject orientation accordingly + pPara->SetVertical(bVertical); + } + + ImpSetAttributesForNewTextObject(GetTextObj()); + } + + if (!mpView->EndCreateObj(SdrCreateCmd::ForceEnd)) + { + // it was not possible to create text object + mxTextObj.reset(nullptr); + } + else if (nSlotId == SID_TEXT_FITTOSIZE) + { + ImpSetAttributesFitToSize(GetTextObj()); + + SetInEditMode(rMEvt, false); + } + else if ( nSlotId == SID_TEXT_FITTOSIZE_VERTICAL ) + { + ImpSetAttributesFitToSizeVertical(GetTextObj()); + + SetInEditMode(rMEvt, false); + } + else + { + ImpSetAttributesFitCommon(GetTextObj()); + + // thereby the handles and the gray frame are correct + mpView->AdjustMarkHdl(); + mpView->PickHandle(aPnt); + SetInEditMode(rMEvt, false); + } + } + else if ( mpView && mpView->IsAction()) + { + mpView->EndAction(); + } + + ForcePointer(&rMEvt); + mpWindow->ReleaseMouse(); + sal_uInt16 nDrgLog1 = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + + if ( mpView && !mpView->AreObjectsMarked() && + std::abs(aMDPos.X() - aPnt.X()) < nDrgLog1 && + std::abs(aMDPos.Y() - aPnt.Y()) < nDrgLog1 && + !rMEvt.IsShift() && !rMEvt.IsMod2() ) + { + SdrPageView* pPV2 = mpView->GetSdrPageView(); + SdrViewEvent aVEvt; + mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + mpView->MarkObj(aVEvt.mpRootObj, pPV2); + } + + if ( !mxTextObj.is() && mpView ) + { + if ( ( (!bEmptyTextObj && bPermanent) || + (!bFirstObjCreated && !bPermanent) ) && + !mpDocSh->IsReadOnly() && + nSlotId != SID_TEXTEDIT ) + { + // text body (left-justified AutoGrow) + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Create); + sal_uInt16 nDrgLog = sal_uInt16 ( mpWindow->PixelToLogic(Size(DRGPIX,0)).Width() ); + mpView->BegCreateObj(aMDPos, nullptr, nDrgLog); + + bool bSnapEnabled = mpView->IsSnapEnabled(); + + if (bSnapEnabled) + mpView->SetSnapEnabled(false); + + aPnt.AdjustX(nDrgLog + nDrgLog ); + aPnt.AdjustY(nDrgLog + nDrgLog ); + mpView->MovAction(aPnt); + + mxTextObj.reset( dynamic_cast< SdrTextObj* >( mpView->GetCreateObj() ) ); + + if(mxTextObj.is()) + { + GetTextObj()->SetDisableAutoWidthOnDragging(true); + } + + if(!mpView->EndCreateObj(SdrCreateCmd::ForceEnd)) + { + mxTextObj.reset(nullptr); + } + + if(bSnapEnabled) + mpView->SetSnapEnabled(bSnapEnabled); + + if(mxTextObj.is()) + { + SfxItemSet aSet(mpViewShell->GetPool()); + aSet.Put(makeSdrTextMinFrameHeightItem(0)); + aSet.Put(makeSdrTextMinFrameWidthItem(0)); + aSet.Put(makeSdrTextAutoGrowHeightItem(true)); + aSet.Put(makeSdrTextAutoGrowWidthItem(true)); + + if(nSlotId == SID_ATTR_CHAR_VERTICAL) + { + // Here, all items which need to be different from pool default need to be set + // again on the newly created text object. + // Since this is a simple click text object, it is first created, then SetVertical() + // is used, then ImpSetAttributesForNewTextObject is called and then the object is + // deleted again since not the minimum drag distance was travelled. Then, a new + // click text object is created and thus all that stuff needs to be set again here. + + // Before using the new object the vertical writing mode + // needs to be set. This is done here at the OutlinerParaObject + // directly to not mirror the layout text items involved. These items will be set + // below. + OutlinerParaObject* pPara = GetTextObj()->GetOutlinerParaObject(); + + if(!pPara) + { + GetTextObj()->ForceOutlinerParaObject(); + pPara = GetTextObj()->GetOutlinerParaObject(); + } + + if(pPara && !pPara->IsEffectivelyVertical()) + { + // set ParaObject orientation accordingly + pPara->SetVertical(true); + } + + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + + // Analog to the else case below, for vertical simple click texts + // one of the default set items from ImpSetAttributesForNewTextObject + // needs to be adapted to non-block mode. + const SfxItemSet& rSet = mpView->GetDefaultAttr(); + SvxFrameDirection eDirection = rSet.Get(EE_PARA_WRITINGDIR).GetValue(); + + if(SvxFrameDirection::Horizontal_RL_TB == eDirection || SvxFrameDirection::Vertical_RL_TB == eDirection) + { + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_BOTTOM)); + } + else + { + aSet.Put(SdrTextVertAdjustItem(SDRTEXTVERTADJUST_TOP)); + } + } + else + { + // This is for Format/Page settings. Since this also leads + // to the object defaults to be changed, i think this code can be + // removed. CL. wanted to take a look before adding this. + + // Look in the object defaults if left-to-right is wanted. If + // yes, set text anchoring to right to let the box grow to left. + const SfxItemSet& rSet = mpView->GetDefaultAttr(); + SvxFrameDirection eDirection = rSet.Get(EE_PARA_WRITINGDIR).GetValue(); + + if(SvxFrameDirection::Horizontal_RL_TB == eDirection) + { + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_RIGHT)); + } + else + { + aSet.Put(SdrTextHorzAdjustItem(SDRTEXTHORZADJUST_LEFT)); + } + } + + GetTextObj()->SetMergedItemSet(aSet); + GetTextObj()->SetDisableAutoWidthOnDragging(true); + SetInEditMode(rMEvt, false); + } + + bFirstObjCreated = true; + } + else + { + // switch to selection + if (mpView->SdrEndTextEdit() == SdrEndTextEditKind::Deleted) + { + mxTextObj.reset(nullptr); + } + + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + } + if (bJustEndedEdit) + { + bJustEndedEdit = false; + FuPoor::cancel(); + } + bMBDown = false; + FuConstruct::MouseButtonUp(rMEvt); + return bReturn; +} + +/** + * handle keyboard events + * @returns sal_True if the event was handled, sal_False otherwise + */ +bool FuText::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + vcl::KeyCode nCode = rKEvt.GetKeyCode(); + bool bShift = nCode.IsShift(); + + if(mxTextObj.is()) + { + // maybe object is deleted, test if it's equal to the selected object + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + SdrObject* pSelectedObj = nullptr; + + if(1 == rMarkList.GetMarkCount()) + { + SdrMark* pMark = rMarkList.GetMark(0); + pSelectedObj = pMark->GetMarkedSdrObj(); + } + + if(mxTextObj.get() != pSelectedObj) + { + mxTextObj.reset(nullptr); + } + } + + if ( mxTextObj.is() && mxTextObj->GetObjInventor() == SdrInventor::Default && mxTextObj->GetObjIdentifier() == SdrObjKind::TitleText && rKEvt.GetKeyCode().GetCode() == KEY_RETURN ) + { + // title text object: always soft breaks + bShift = true; + } + + sal_uInt16 nKey = nCode.GetCode(); + vcl::KeyCode aKeyCode (nKey, bShift, nCode.IsMod1(), nCode.IsMod2(), nCode.IsMod3() ); + KeyEvent aKEvt(rKEvt.GetCharCode(), aKeyCode); + + bool bOK = true; + + if (mpDocSh->IsReadOnly()) + { + bOK = !EditEngine::DoesKeyChangeText(aKEvt); + } + if( aKeyCode.GetCode() == KEY_PAGEUP || aKeyCode.GetCode() == KEY_PAGEDOWN ) + { + bOK = false; // default handling in base class + } + + if (bOK && mpView->KeyInput(aKEvt, mpWindow) ) + { + bReturn = true; + + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArray ); + + } + else if (aKeyCode == KEY_ESCAPE) + { + bReturn = cancel(); + } + + if( bPermanent ) + { + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Create); + } + + if (!bReturn) + { + bReturn = FuDraw::KeyInput(aKEvt); + } + + return bReturn; +} + +void FuText::Activate() +{ + mpView->SetQuickTextEditMode(mpViewShell->GetFrameView()->IsQuickEdit()); + + // #i89661# it's no longer necessary to make it so big here, it's fine tuned + // for text objects in SdrMarkView::CheckSingleSdrObjectHit + mpView->SetHitTolerancePixel( 2 * HITPIX ); + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if (pOLV) + pOLV->ShowCursor(/*bGotoCursor=*/true, /*bActivate=*/true); + + FuConstruct::Activate(); + + if( pOLV ) + mpView->SetEditMode(SdrViewEditMode::Edit); +} + +void FuText::Deactivate() +{ + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if (pOLV) + pOLV->HideCursor(/*bDeactivate=*/true); + + mpView->SetHitTolerancePixel( HITPIX ); + + FuConstruct::Deactivate(); +} + +/** + * Sets the object into the edit mode. + */ +void FuText::SetInEditMode(const MouseEvent& rMEvt, bool bQuickDrag) +{ + SdrPageView* pPV = mpView->GetSdrPageView(); + if( mxTextObj.is() && (mxTextObj->getSdrPageFromSdrObject() == pPV->GetPage()) ) + { + mpView->SetCurrentObj(SdrObjKind::Text); + + if( bPermanent ) + mpView->SetEditMode(SdrViewEditMode::Create); + else + mpView->SetEditMode(SdrViewEditMode::Edit); + + bool bEmptyOutliner = false; + + if (!GetTextObj()->GetOutlinerParaObject() && mpView->GetTextEditOutliner()) + { + ::Outliner* pOutl = mpView->GetTextEditOutliner(); + sal_Int32 nParagraphCnt = pOutl->GetParagraphCount(); + Paragraph* p1stPara = pOutl->GetParagraph( 0 ); + + if (nParagraphCnt==1 && p1stPara) + { + // with only one paragraph + if (pOutl->GetText(p1stPara).isEmpty()) + { + bEmptyOutliner = true; + } + } + } + + if (GetTextObj() != mpView->GetTextEditObject() || bEmptyOutliner) + { + SdrInventor nInv = mxTextObj->GetObjInventor(); + SdrObjKind nSdrObjKind = mxTextObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && GetTextObj()->HasTextEdit() && + (nSdrObjKind == SdrObjKind::Text || + nSdrObjKind == SdrObjKind::TitleText || + nSdrObjKind == SdrObjKind::OutlineText || !mxTextObj->IsEmptyPresObj() ) ) + { + // create new outliner (owned by SdrObjEditView) + std::unique_ptr pOutl = SdrMakeOutliner(OutlinerMode::OutlineObject, *mpDoc); + + if (bEmptyOutliner) + mpView->SdrEndTextEdit(true); + + SdrTextObj* pTextObj = GetTextObj(); + if( pTextObj ) + { + OutlinerParaObject* pOPO = pTextObj->GetOutlinerParaObject(); + if( pOPO && pOPO->IsEffectivelyVertical() ) + { + pOutl->SetVertical(pOPO->GetVertical()); + pOutl->SetRotation(pOPO->GetRotation()); + } + else if (nSlotId == SID_ATTR_CHAR_VERTICAL || nSlotId == SID_TEXT_FITTOSIZE_VERTICAL) + pOutl->SetVertical( true ); + + if( pTextObj->getTextCount() > 1 ) + { + Point aPix(rMEvt.GetPosPixel()); + Point aPnt(mpWindow->PixelToLogic(aPix)); + pTextObj->setActiveText( pTextObj->CheckTextHit(aPnt ) ); + } + + if (mpView->SdrBeginTextEdit(pTextObj, pPV, mpWindow, true, pOutl.release()) && mxTextObj->GetObjInventor() == SdrInventor::Default) + { + //tdf#102293 flush overlay before going on to pass clicks down to + //the outline view which will want to paint selections + for (sal_uInt32 b = 0; b < pPV->PageWindowCount(); ++b) + { + const SdrPageWindow& rPageWindow = *pPV->GetPageWindow(b); + if (!rPageWindow.GetPaintWindow().OutputToWindow()) + continue; + const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); + if (!xManager.is()) + continue; + xManager->flush(); + } + + bFirstObjCreated = true; + DeleteDefaultText(); + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + nSdrObjKind = mxTextObj->GetObjIdentifier(); + + SdrViewEvent aVEvt; + SdrHitKind eHit = mpView->PickAnything(rMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + + if (eHit == SdrHitKind::TextEdit) + { + // hit text + if (nSdrObjKind == SdrObjKind::Text || + nSdrObjKind == SdrObjKind::TitleText || + nSdrObjKind == SdrObjKind::OutlineText || + nSdrObjKind == SdrObjKind::Table || + nSlotId == SID_TEXTEDIT || + !bQuickDrag) + { + pOLV->MouseButtonDown(rMEvt); + pOLV->MouseMove(rMEvt); + pOLV->MouseButtonUp(rMEvt); + } + + if (mpViewShell->GetFrameView()->IsQuickEdit() && bQuickDrag && GetTextObj()->GetOutlinerParaObject()) + { + pOLV->MouseButtonDown(rMEvt); + } + } + else + { + // Move cursor to end of text + ESelection aNewSelection(EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND, EE_PARA_NOT_FOUND, EE_INDEX_NOT_FOUND); + if (pOLV != nullptr) + pOLV->SetSelection(aNewSelection); + } + } + else + { + mpView->RestoreDefaultText( mxTextObj.get() ); + } + } + } + } + } + else + { + mxTextObj.reset(nullptr); + } +} + +/** + * Text entry is started, if necessary delete the default text. + */ +void FuText::DeleteDefaultText() +{ + if ( !(mxTextObj.is() && mxTextObj->IsEmptyPresObj()) ) + return; + + SdPage* pPage = static_cast( mxTextObj->getSdrPageFromSdrObject() ); + + if (!pPage) + return; + + PresObjKind ePresObjKind = pPage->GetPresObjKind(mxTextObj.get()); + + if ( !(ePresObjKind == PresObjKind::Title || + ePresObjKind == PresObjKind::Outline || + ePresObjKind == PresObjKind::Notes || + ePresObjKind == PresObjKind::Text ) || + pPage->IsMasterPage() ) + return; + + ::Outliner* pOutliner = mpView->GetTextEditOutliner(); + SfxStyleSheet* pSheet = pOutliner->GetStyleSheet( 0 ); + bool bIsUndoEnabled = pOutliner->IsUndoEnabled(); + if( bIsUndoEnabled ) + pOutliner->EnableUndo(false); + + pOutliner->SetText( OUString(), pOutliner->GetParagraph( 0 ) ); + + if( bIsUndoEnabled ) + pOutliner->EnableUndo(true); + + if (pSheet && + (ePresObjKind == PresObjKind::Notes || ePresObjKind == PresObjKind::Text)) + pOutliner->SetStyleSheet(0, pSheet); + + mxTextObj->SetEmptyPresObj(true); +} + +bool FuText::RequestHelp(const HelpEvent& rHEvt) +{ + bool bReturn = false; + + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if ((Help::IsBalloonHelpEnabled() || Help::IsQuickHelpEnabled()) && + mxTextObj.is() && pOLV && pOLV->GetFieldUnderMousePointer()) + { + OUString aHelpText; + const SvxFieldItem* pFieldItem = pOLV->GetFieldUnderMousePointer(); + const SvxFieldData* pField = pFieldItem->GetField(); + + if (auto pURLField = dynamic_cast< const SvxURLField *>( pField )) + { + // URL-Field + aHelpText = INetURLObject::decode( pURLField->GetURL(), INetURLObject::DecodeMechanism::WithCharset ); + } + if (!aHelpText.isEmpty()) + { + ::tools::Rectangle aLogicPix = mpWindow->LogicToPixel(mxTextObj->GetLogicRect()); + ::tools::Rectangle aScreenRect(mpWindow->OutputToScreenPixel(aLogicPix.TopLeft()), + mpWindow->OutputToScreenPixel(aLogicPix.BottomRight())); + + if (Help::IsBalloonHelpEnabled()) + { + Help::ShowBalloon( static_cast(mpWindow), rHEvt.GetMousePosPixel(), aScreenRect, aHelpText); + bReturn = true; + } + else if (Help::IsQuickHelpEnabled()) + { + Help::ShowQuickHelp( static_cast(mpWindow), aScreenRect, aHelpText); + bReturn = true; + } + } + } + + if (!bReturn) + { + bReturn = FuConstruct::RequestHelp(rHEvt); + } + + return bReturn; +} + +void FuText::ReceiveRequest(SfxRequest& rReq) +{ + nSlotId = rReq.GetSlot(); + + // then we call the base class (besides others, nSlotId is NOT set there) + FuPoor::ReceiveRequest(rReq); + + if (!(nSlotId == SID_TEXTEDIT || mpViewShell->GetFrameView()->IsQuickEdit() || SID_ATTR_CHAR == nSlotId)) + return; + + MouseEvent aMEvt(mpWindow->GetPointerPosPixel()); + + mxTextObj.reset(nullptr); + + if (nSlotId == SID_TEXTEDIT) + { + // are we currently editing? + mxTextObj.reset( mpView->GetTextEditObject() ); + + if (!mxTextObj.is()) + { + // Try to select an object + SdrPageView* pPV = mpView->GetSdrPageView(); + SdrViewEvent aVEvt; + mpView->PickAnything(aMEvt, SdrMouseEventKind::BUTTONDOWN, aVEvt); + mpView->MarkObj(aVEvt.mpRootObj, pPV); + + if (auto pSdrTextObj = dynamic_cast(aVEvt.mpObj)) + { + mxTextObj.reset( pSdrTextObj ); + } + } + } + else if (mpView->AreObjectsMarked()) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + if( auto pTextObj = dynamic_cast( pObj )) + { + mxTextObj.reset( pTextObj ); + } + } + } + + bool bQuickDrag = true; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if (pArgs + + // test for type before using + && SID_TEXTEDIT == nSlotId + && SfxItemState::SET == pArgs->GetItemState(SID_TEXTEDIT) + + && static_cast( pArgs->Get(SID_TEXTEDIT)).GetValue() == 2) + { + // selection with double click -> do not allow QuickDrag + bQuickDrag = false; + } + + SetInEditMode(aMEvt, bQuickDrag); +} + +void FuText::DoubleClick(const MouseEvent& ) +{ + // Nothing to do +} + +/** Removed the insertion of default text and putting a new text + object directly into edit mode. +*/ +SdrObjectUniquePtr FuText::CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) +{ + SdrObjectUniquePtr pObj( SdrObjFactory::MakeNewObject( + mpView->getSdrModelFromSdrView(), + mpView->GetCurrentObjInventor(), + mpView->GetCurrentObjIdentifier(), + nullptr) ); + + if(pObj) + { + if( auto pText = dynamic_cast< SdrTextObj *>( pObj.get() ) ) + { + pText->SetLogicRect(rRectangle); + + bool bVertical = (SID_ATTR_CHAR_VERTICAL == nID || SID_TEXT_FITTOSIZE_VERTICAL == nID); + pText->SetVerticalWriting(bVertical); + + ImpSetAttributesForNewTextObject(pText); + + if (nSlotId == SID_TEXT_FITTOSIZE) + { + ImpSetAttributesFitToSize(pText); + } + else if ( nSlotId == SID_TEXT_FITTOSIZE_VERTICAL ) + { + ImpSetAttributesFitToSizeVertical(pText); + } + else + { + ImpSetAttributesFitCommon(pText); + } + + // Put text object into edit mode. + SdrPageView* pPV = mpView->GetSdrPageView(); + mpView->SdrBeginTextEdit(pText, pPV); + } + else + { + OSL_FAIL("Object is NO text object"); + } + } + + return pObj; +} + +/** is called when the current function should be aborted.

+ This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted +*/ +bool FuText::cancel() +{ + if ( mpView->IsTextEdit() ) + { + if(mpView->SdrEndTextEdit() == SdrEndTextEditKind::Deleted) + mxTextObj.reset(nullptr); + + mpView->SetCurrentObj(SdrObjKind::Text); + mpView->SetEditMode(SdrViewEditMode::Edit); + return true; + } + else + { + return false; + } +} + +void FuText::ChangeFontSize( bool bGrow, OutlinerView* pOLV, const FontList* pFontList, ::sd::View* pView ) +{ + if( !pFontList || !pView ) + return; + + if( pOLV ) + { + pOLV->GetEditView().ChangeFontSize( bGrow, pFontList ); + } + else + { + + pView->BegUndo(SdResId(bGrow ? STR_GROW_FONT_SIZE : STR_SHRINK_FONT_SIZE)); + const SdrMarkList& rMarkList = pView->GetMarkedObjectList(); + for( size_t nMark = 0; nMark < rMarkList.GetMarkCount(); ++nMark ) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( rMarkList.GetMark(nMark)->GetMarkedSdrObj() ); + if( pTextObj ) + { + rtl::Reference xSelectionController(pView->getSelectionController()); + if (xSelectionController.is() && xSelectionController->ChangeFontSize(bGrow, pFontList)) + { + continue; + } + for( sal_Int32 nText = 0; nText < pTextObj->getTextCount(); nText++ ) + { + pTextObj->setActiveText( nText ); + + // Put text object into edit mode. + SdrPageView* pPV = pView->GetSdrPageView(); + pView->SdrBeginTextEdit(pTextObj, pPV); + + pOLV = pView->GetTextEditOutlinerView(); + if( pOLV ) + { + EditEngine* pEditEngine = pOLV->GetEditView().GetEditEngine(); + if( pEditEngine ) + { + ESelection aSel; + aSel.nEndPara = pEditEngine->GetParagraphCount()-1; + aSel.nEndPos = pEditEngine->GetTextLen(aSel.nEndPara); + pOLV->SetSelection(aSel); + } + + ChangeFontSize( bGrow, pOLV, pFontList, pView ); + } + + pView->SdrEndTextEdit(); + } + + SfxItemSet aShapeSet( pTextObj->GetMergedItemSet() ); + if( EditView::ChangeFontSize( bGrow, aShapeSet, pFontList ) ) + { + pTextObj->SetObjectItemNoBroadcast( aShapeSet.Get( EE_CHAR_FONTHEIGHT ) ); + pTextObj->SetObjectItemNoBroadcast( aShapeSet.Get( EE_CHAR_FONTHEIGHT_CJK ) ); + pTextObj->SetObjectItemNoBroadcast( aShapeSet.Get( EE_CHAR_FONTHEIGHT_CTL ) ); + } + } + } + pView->EndUndo(); + } +} + +void FuText::InvalidateBindings() +{ + mpViewShell->GetViewFrame()->GetBindings().Invalidate(SidArray); +} + + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futhes.cxx b/sd/source/ui/func/futhes.cxx new file mode 100644 index 000000000..78d2ae693 --- /dev/null +++ b/sd/source/ui/func/futhes.cxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +class SfxRequest; + +namespace sd { + + +FuThesaurus::FuThesaurus( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq ) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuThesaurus::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuThesaurus( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuThesaurus::DoExecute(SfxRequest& rReq) +{ + SfxErrorContext aContext(ERRCTX_SVX_LINGU_THESAURUS, OUString(), + mpWindow->GetFrameWeld(), RID_SVXERRCTX, SvxResLocale()); + + if (dynamic_cast< DrawViewShell *>( mpViewShell )) + { + SdrTextObj* pTextObj = nullptr; + + if ( mpView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 ) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + pTextObj = dynamic_cast( pObj ); + } + } + + ::Outliner* pOutliner = mpView->GetTextEditOutliner(); + const OutlinerView* pOutlView = mpView->GetTextEditOutlinerView(); + + if ( pTextObj && pOutliner && pOutlView ) + { + if ( !pOutliner->GetSpeller().is() ) + { + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + pOutliner->SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + pOutliner->SetHyphenator( xHyphenator ); + + pOutliner->SetDefaultLanguage( mpDoc->GetLanguage( EE_CHAR_LANGUAGE ) ); + } + + EESpellState eState = const_cast(pOutlView)->StartThesaurus(rReq.GetFrameWeld()); + DBG_ASSERT(eState != EESpellState::NoSpeller, "No SpellChecker"); + } + } + else if (dynamic_cast< OutlineViewShell *>( mpViewShell )) + { + Outliner* pOutliner = mpDoc->GetOutliner(); + OutlinerView* pOutlView = pOutliner->GetView(0); + + if ( !pOutliner->GetSpeller().is() ) + { + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + pOutliner->SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + pOutliner->SetHyphenator( xHyphenator ); + + pOutliner->SetDefaultLanguage( mpDoc->GetLanguage( EE_CHAR_LANGUAGE ) ); + } + + EESpellState eState = pOutlView->StartThesaurus(rReq.GetFrameWeld()); + DBG_ASSERT(eState != EESpellState::NoSpeller, "No SpellChecker"); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futransf.cxx b/sd/source/ui/func/futransf.cxx new file mode 100644 index 000000000..8c565a3b8 --- /dev/null +++ b/sd/source/ui/func/futransf.cxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace sd; + +FuTransform::FuTransform(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuTransform::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuTransform( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +namespace { + +void setUndo(::sd::View* pView, const SfxItemSet* pArgs, bool addPageMargin) +{ + // Undo + OUString aString = pView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_TRANSFORM); + pView->BegUndo(aString); + pView->SetGeoAttrToMarked(*pArgs, addPageMargin); + pView->SetAttributes(*pArgs); + pView->EndUndo(); +} + +} + +void FuTransform::DoExecute( SfxRequest& rReq ) +{ + if (!mpView->AreObjectsMarked()) + return; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if (pArgs) + { + // If this comes from LOK, that means the shape is moved by mouse + // only then pArgs is pre-set. + setUndo(mpView, pArgs, comphelper::LibreOfficeKit::isActive()); + return; + } + + // --------- itemset for size and position -------- + SfxItemSet aSet( mpView->GetGeoAttrFromMarked() ); + VclPtr pDlg; + + bool bWelded = false; + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( rMarkList.GetMarkCount() == 1 && + pObj->GetObjInventor() == SdrInventor::Default && + pObj->GetObjIdentifier() == SdrObjKind::Caption ) + { + // --------- itemset for caption -------- + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + pDlg.reset(pFact->CreateCaptionDialog(mpViewShell->GetFrameWeld(), mpView)); + + const WhichRangesContainer& pRange = pDlg->GetInputRanges( *aNewAttr.GetPool() ); + SfxItemSet aCombSet( *aNewAttr.GetPool(), pRange ); + aCombSet.Put( aNewAttr ); + aCombSet.Put( aSet ); + pDlg->SetInputSet( &aCombSet ); + } + else + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + pDlg.reset(pFact->CreateSvxTransformTabDialog(mpViewShell->GetFrameWeld(), &aSet, mpView)); + bWelded = true; + } + + assert(pDlg && "there must be a dialog at this point"); + + auto pRequest = std::make_shared(rReq); + rReq.Ignore(); // the 'old' request is not relevant any more + + pDlg->StartExecuteAsync([bWelded, pDlg, pRequest, this](sal_Int32 nResult){ + if (nResult == RET_OK) + { + pRequest->Done(*(pDlg->GetOutputItemSet())); + // Page margin is already calculated at this point. + setUndo(mpView, pRequest->GetArgs(), false); + } + + // deferred until the dialog ends + mpViewShell->Invalidate(SID_RULER_OBJECT); + mpViewShell->Cancel(); + if (bWelded) + pDlg->disposeOnce(); + }); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/futxtatt.cxx b/sd/source/ui/func/futxtatt.cxx new file mode 100644 index 000000000..56f8c2569 --- /dev/null +++ b/sd/source/ui/func/futxtatt.cxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include + +namespace sd { + + +FuTextAttrDlg::FuTextAttrDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuTextAttrDlg::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuTextAttrDlg( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuTextAttrDlg::DoExecute( SfxRequest& rReq ) +{ + SfxItemSet aNewAttr( mpDoc->GetPool() ); + mpView->GetAttributes( aNewAttr ); + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if( !pArgs ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateTextTabDialog(rReq.GetFrameWeld(), &aNewAttr, mpView)); + + sal_uInt16 nResult = pDlg->Execute(); + + switch( nResult ) + { + case RET_OK: + { + rReq.Done( *( pDlg->GetOutputItemSet() ) ); + + pArgs = rReq.GetArgs(); + } + break; + + default: + return; // Cancel + } + } + mpView->SetAttributes( *pArgs ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuvect.cxx b/sd/source/ui/func/fuvect.cxx new file mode 100644 index 000000000..64840810c --- /dev/null +++ b/sd/source/ui/func/fuvect.cxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include + +namespace sd +{ + + +FuVectorize::FuVectorize ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor (pViewSh, pWin, pView, pDoc, rReq) +{ +} + +rtl::Reference FuVectorize::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuVectorize( pViewSh, pWin, pView, pDoc, rReq ) ); + xFunc->DoExecute(rReq); + return xFunc; +} + +void FuVectorize::DoExecute( SfxRequest& ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() != 1 ) + return; + + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + auto pSdrGrafObj = dynamic_cast< const SdrGrafObj *>( pObj ); + if( !pSdrGrafObj ) + return; + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( + pFact->CreateSdVectorizeDlg(mpWindow ? mpWindow->GetFrameWeld() : nullptr, + pSdrGrafObj->GetGraphic().GetBitmapEx().GetBitmap(), mpDocSh ) ); + if( pDlg->Execute() != RET_OK ) + return; + + const GDIMetaFile& rMtf = pDlg->GetGDIMetaFile(); + SdrPageView* pPageView = mpView->GetSdrPageView(); + + if( pPageView && rMtf.GetActionSize() ) + { + SdrGrafObj* pVectObj = static_cast( pObj->CloneSdrObject(pObj->getSdrModelFromSdrObject()) ); + OUString aStr = mpView->GetDescriptionOfMarkedObjects() + + " " + SdResId( STR_UNDO_VECTORIZE ); + mpView->BegUndo( aStr ); + pVectObj->SetGraphic( rMtf ); + mpView->ReplaceObjectAtView( pObj, *pPageView, pVectObj ); + mpView->EndUndo(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/fuzoom.cxx b/sd/source/ui/func/fuzoom.cxx new file mode 100644 index 000000000..871d594d3 --- /dev/null +++ b/sd/source/ui/func/fuzoom.cxx @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace sd { + +const sal_uInt16 SidArrayZoom[] = { + SID_ATTR_ZOOM, + SID_ZOOM_OUT, + SID_ZOOM_IN, + 0 }; + + +FuZoom::FuZoom( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq) + : FuPoor(pViewSh, pWin, pView, pDoc, rReq), + bVisible(false), + bStartDrag(false), + aPtr(PointerStyle::Arrow) +{ +} + +FuZoom::~FuZoom() +{ + if (bVisible) + { + // Hide ZoomRect + mpViewShell->DrawMarkRect(aZoomRect); + + bVisible = false; + bStartDrag = false; + } +} + +rtl::Reference FuZoom::Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ) +{ + rtl::Reference xFunc( new FuZoom( pViewSh, pWin, pView, pDoc, rReq ) ); + return xFunc; +} + +bool FuZoom::MouseButtonDown(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + mpWindow->CaptureMouse(); + bStartDrag = true; + + aBeginPosPix = rMEvt.GetPosPixel(); + aBeginPos = mpWindow->PixelToLogic(aBeginPosPix); + aZoomRect.SetSize( Size( 0, 0 ) ); + aZoomRect.SetPos( aBeginPos ); + + return true; +} + +bool FuZoom::MouseMove(const MouseEvent& rMEvt) +{ + if (rMEvt.IsShift()) + mpWindow->SetPointer(PointerStyle::Hand); + else if (nSlotId != SID_ZOOM_PANNING) + mpWindow->SetPointer(PointerStyle::Magnify); + + if (bStartDrag) + { + if (bVisible) + { + mpViewShell->DrawMarkRect(aZoomRect); + } + + Point aPosPix = rMEvt.GetPosPixel(); + ForceScroll(aPosPix); + + aEndPos = mpWindow->PixelToLogic(aPosPix); + aBeginPos = mpWindow->PixelToLogic(aBeginPosPix); + + if (nSlotId == SID_ZOOM_PANNING || (rMEvt.IsShift() && !bVisible) ) + { + // Panning + + Point aScroll = aBeginPos - aEndPos; + + if (aScroll.X() != 0 || aScroll.Y() != 0) + { + Size aWorkSize = mpView->GetWorkArea().GetSize(); + Size aPageSize = mpView->GetSdrPageView()->GetPage()->GetSize(); + if (aWorkSize.Width() != 0 && aWorkSize.Height() != 0 && + aPageSize.Width() != 0 && aPageSize.Height() != 0) + { + aScroll.setX( aScroll.X() / ( aWorkSize.Width() / aPageSize.Width()) ); + aScroll.setY( aScroll.Y() / ( aWorkSize.Height() / aPageSize.Height()) ); + mpViewShell->Scroll(aScroll.X(), aScroll.Y()); + aBeginPosPix = aPosPix; + } + } + } + else + { + ::tools::Rectangle aRect(aBeginPos, aEndPos); + aZoomRect = aRect; + aZoomRect.Justify(); + mpViewShell->DrawMarkRect(aZoomRect); + bVisible = true; + } + } + + return bStartDrag; +} + +bool FuZoom::MouseButtonUp(const MouseEvent& rMEvt) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode(rMEvt.GetButtons()); + + if (bVisible) + { + // Hide ZoomRect + mpViewShell->DrawMarkRect(aZoomRect); + bVisible = false; + } + + Point aPosPix = rMEvt.GetPosPixel(); + + if(SID_ZOOM_PANNING != nSlotId && !rMEvt.IsShift()) + { + // Zoom + Size aZoomSizePixel = mpWindow->LogicToPixel(aZoomRect).GetSize(); + sal_uLong nTol = DRGPIX + DRGPIX; + + if ( ( aZoomSizePixel.Width() < static_cast<::tools::Long>(nTol) && aZoomSizePixel.Height() < static_cast<::tools::Long>(nTol) ) || rMEvt.IsMod1() ) + { + // click at place: double zoom factor + Point aPos = mpWindow->PixelToLogic(aPosPix); + Size aSize = mpWindow->PixelToLogic(mpWindow->GetOutputSizePixel()); + if ( rMEvt.IsMod1() ) + { + aSize.setWidth( aSize.Width() * 2 ); + aSize.setHeight( aSize.Height() * 2 ); + } + else + { + aSize.setWidth( aSize.Width() / 2 ); + aSize.setHeight( aSize.Height() / 2 ); + } + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + aZoomRect.SetPos(aPos); + aZoomRect.SetSize(aSize); + } + + mpViewShell->SetZoomRect(aZoomRect); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArrayZoom ); + } + + ::tools::Rectangle aVisAreaWin = mpWindow->PixelToLogic(::tools::Rectangle(Point(0,0), + mpWindow->GetOutputSizePixel())); + mpViewShell->GetZoomList()->InsertZoomRect(aVisAreaWin); + + bStartDrag = false; + mpWindow->ReleaseMouse(); + + return true; +} + +void FuZoom::Activate() +{ + aPtr = mpWindow->GetPointer(); + + if (nSlotId == SID_ZOOM_PANNING) + { + mpWindow->SetPointer(PointerStyle::Hand); + } + else + { + mpWindow->SetPointer(PointerStyle::Magnify); + } +} + +void FuZoom::Deactivate() +{ + mpWindow->SetPointer( aPtr ); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SidArrayZoom ); +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/sdundogr.cxx b/sd/source/ui/func/sdundogr.cxx new file mode 100644 index 000000000..a2e97386d --- /dev/null +++ b/sd/source/ui/func/sdundogr.cxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +SdUndoGroup::~SdUndoGroup() = default; + +bool SdUndoGroup::Merge(SfxUndoAction* pNextAction) +{ + bool bRet = false; + + if (auto pSdUndoAction = dynamic_cast(pNextAction)) + { + SdUndoAction* pClone = pSdUndoAction->Clone(); + + if (pClone) + { + AddAction(pClone); + bRet = true; + } + } + + return bRet; +} + +/** + * Undo, reverse order of execution + */ +void SdUndoGroup::Undo() +{ + ::tools::Long nLast = aCtn.size(); + for (::tools::Long nAction = nLast - 1; nAction >= 0; nAction--) + { + aCtn[nAction]->Undo(); + } +} + +void SdUndoGroup::Redo() +{ + size_t nLast = aCtn.size(); + for (size_t nAction = 0; nAction < nLast; nAction++) + { + aCtn[nAction]->Redo(); + } +} + +void SdUndoGroup::AddAction(SdUndoAction* pAction) { aCtn.emplace_back(pAction); } + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/smarttag.cxx b/sd/source/ui/func/smarttag.cxx new file mode 100644 index 000000000..ff5382116 --- /dev/null +++ b/sd/source/ui/func/smarttag.cxx @@ -0,0 +1,333 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +namespace sd +{ + +SmartTag::SmartTag( ::sd::View& rView ) +: mrView( rView ) +, mbSelected( false ) +{ + SmartTagReference xThis( this ); + mrView.getSmartTags().add( xThis ); +} + +SmartTag::~SmartTag() +{ +} + +bool SmartTag::MouseButtonDown( const MouseEvent&, SmartHdl& ) +{ + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool SmartTag::KeyInput( const KeyEvent& /*rKEvt*/ ) +{ + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool SmartTag::Command( const CommandEvent& /*rCEvt*/ ) +{ + return false; +} + +void SmartTag::addCustomHandles( SdrHdlList& /*rHandlerList*/ ) +{ +} + +void SmartTag::select() +{ + mbSelected = true; +} + +void SmartTag::deselect() +{ + mbSelected = false; +} + +void SmartTag::disposing() +{ + SmartTagReference xThis( this ); + mrView.getSmartTags().remove( xThis ); +} + +bool SmartTag::getContext( SdrViewContext& /*rContext*/ ) +{ + return false; +} + +sal_Int32 SmartTag::GetMarkablePointCount() const +{ + return 0; +} + +sal_Int32 SmartTag::GetMarkedPointCount() const +{ + return 0; +} + +bool SmartTag::MarkPoint(SdrHdl& /*rHdl*/, bool /*bUnmark*/ ) +{ + return false; +} + +bool SmartTag::MarkPoints(const ::tools::Rectangle* /*pRect*/, bool /*bUnmark*/ ) +{ + return false; +} + +void SmartTag::CheckPossibilities() +{ +} + +SmartTagSet::SmartTagSet( View& rView ) +: mrView( rView ) +{ +} + +SmartTagSet::~SmartTagSet() +{ +} + +void SmartTagSet::add( const SmartTagReference& xTag ) +{ + maSet.insert( xTag ); + mrView.InvalidateAllWin(); + + if( xTag == mxMouseOverTag ) + mxMouseOverTag.clear(); + + if( xTag == mxSelectedTag ) + mxSelectedTag.clear(); +} + +void SmartTagSet::remove( const SmartTagReference& xTag ) +{ + std::set< SmartTagReference >::iterator aIter( maSet.find( xTag ) ); + if( aIter != maSet.end() ) + maSet.erase( aIter ); + mrView.InvalidateAllWin(); + + if( xTag == mxMouseOverTag ) + mxMouseOverTag.clear(); + + if( xTag == mxSelectedTag ) + mxSelectedTag.clear(); +} + +void SmartTagSet::Dispose() +{ + std::set< SmartTagReference > aSet; + aSet.swap( maSet ); + for( auto& rxItem : aSet ) + rxItem->Dispose(); + mrView.InvalidateAllWin(); + mxMouseOverTag.clear(); + mxSelectedTag.clear(); +} + +void SmartTagSet::select( const SmartTagReference& xTag ) +{ + if( mxSelectedTag == xTag ) + return; + + if( mxSelectedTag.is() ) + mxSelectedTag->deselect(); + + mxSelectedTag = xTag; + mxSelectedTag->select(); + mrView.SetPossibilitiesDirty(); + if( mrView.GetMarkedObjectCount() > 0 ) + mrView.UnmarkAllObj(); + else + mrView.updateHandles(); +} + +void SmartTagSet::deselect() +{ + if( mxSelectedTag.is() ) + { + mxSelectedTag->deselect(); + mxSelectedTag.clear(); + mrView.SetPossibilitiesDirty(); + mrView.updateHandles(); + } +} + +bool SmartTagSet::MouseButtonDown( const MouseEvent& rMEvt ) +{ + Point aMDPos( mrView.GetViewShell()->GetActiveWindow()->PixelToLogic( rMEvt.GetPosPixel() ) ); + SdrHdl* pHdl = mrView.PickHandle(aMDPos); + + // check if a smart tag is selected and no handle is hit + if( mxSelectedTag.is() && !pHdl ) + { + // deselect smart tag + deselect(); + return false; + } + + // if a smart tag handle is hit, forward event to its smart tag + SmartHdl* pSmartHdl = dynamic_cast< SmartHdl* >( pHdl ); + if(pSmartHdl && pSmartHdl->getTag().is() ) + { + SmartTagReference xTag( pSmartHdl->getTag() ); + return xTag->MouseButtonDown( rMEvt, *pSmartHdl ); + } + + return false; +} + +bool SmartTagSet::KeyInput( const KeyEvent& rKEvt ) +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->KeyInput( rKEvt ); + else if( rKEvt.GetKeyCode().GetCode() == KEY_SPACE ) + { + SmartHdl* pSmartHdl = dynamic_cast< SmartHdl* >( mrView.GetHdlList().GetFocusHdl() ); + if( pSmartHdl ) + { + const_cast< SdrHdlList& >( mrView.GetHdlList() ).ResetFocusHdl(); + const SmartTagReference& xTag( pSmartHdl->getTag() ); + select( xTag ); + return true; + } + } + + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool SmartTagSet::Command( const CommandEvent& rCEvt ) +{ + if( rCEvt.IsMouseEvent() ) + { + Point aMDPos( mrView.GetViewShell()->GetActiveWindow()->PixelToLogic( rCEvt.GetMousePosPixel() ) ); + SdrHdl* pHdl = mrView.PickHandle(aMDPos); + + if( pHdl ) + { + // if a smart tag handle is hit, forward event to its smart tag + SmartHdl* pSmartHdl = dynamic_cast< SmartHdl* >( pHdl ); + if(pSmartHdl && pSmartHdl->getTag().is() ) + { + const SmartTagReference& xTag( pSmartHdl->getTag() ); + return xTag->Command( rCEvt ); + } + } + } + else + { + if( mxSelectedTag.is() ) + return mxSelectedTag->Command( rCEvt ); + + } + + return false; +} + +void SmartTagSet::addCustomHandles( SdrHdlList& rHandlerList ) +{ + for( auto& rxItem : maSet ) + rxItem->addCustomHandles( rHandlerList ); +} + +/** returns true if the currently selected smart tag has + a special context, returned in rContext. */ +bool SmartTagSet::getContext( SdrViewContext& rContext ) const +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->getContext( rContext ); + else + return false; +} + +// support point editing + +bool SmartTagSet::HasMarkablePoints() const +{ + return GetMarkablePointCount() != 0; +} + +sal_uLong SmartTagSet::GetMarkablePointCount() const +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->GetMarkablePointCount(); + return 0; +} + +bool SmartTagSet::HasMarkedPoints() const +{ + return GetMarkedPointCount() != 0; +} + +sal_uLong SmartTagSet::GetMarkedPointCount() const +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->GetMarkedPointCount(); + else + return 0; +} + +bool SmartTagSet::MarkPoint(SdrHdl& rHdl, bool bUnmark ) +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->MarkPoint( rHdl, bUnmark ); + + return false; +} + +bool SmartTagSet::MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) +{ + if( mxSelectedTag.is() ) + return mxSelectedTag->MarkPoints( pRect, bUnmark ); + return false; +} + +void SmartTagSet::CheckPossibilities() +{ + if( mxSelectedTag.is() ) + mxSelectedTag->CheckPossibilities(); +} + +SmartHdl::SmartHdl( const SmartTagReference& xTag, SdrObject* pObject, const Point& rPnt, SdrHdlKind eNewKind /*=SdrHdlKind::Move*/ ) +: SdrHdl( rPnt, eNewKind ) +, mxSmartTag( xTag ) +{ + SetObj( pObject ); +} + +SmartHdl::SmartHdl( const SmartTagReference& xTag, const Point& rPnt, SdrHdlKind eNewKind /*=SdrHdlKind::Move*/ ) +: SdrHdl( rPnt, eNewKind ) +, mxSmartTag( xTag ) +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/undoback.cxx b/sd/source/ui/func/undoback.cxx new file mode 100644 index 000000000..768ca2ec2 --- /dev/null +++ b/sd/source/ui/func/undoback.cxx @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include + +SdBackgroundObjUndoAction::SdBackgroundObjUndoAction( + SdDrawDocument& rDoc, + SdPage& rPage, + const SfxItemSet& rItemSet) +: SdUndoAction(&rDoc), + mrPage(rPage), + mpItemSet(std::make_unique(rItemSet)), + mbHasFillBitmap(false) +{ + OUString aString( SdResId( STR_UNDO_CHANGE_PAGEFORMAT ) ); + SetComment( aString ); + saveFillBitmap(*mpItemSet); +} + +void SdBackgroundObjUndoAction::ImplRestoreBackgroundObj() +{ + std::unique_ptr pNew = std::make_unique(mrPage.getSdrPageProperties().GetItemSet()); + mrPage.getSdrPageProperties().ClearItem(); + if (bool(mpFillBitmapItem)) + restoreFillBitmap(*mpItemSet); + mpFillBitmapItem.reset(); + mbHasFillBitmap = false; + mrPage.getSdrPageProperties().PutItemSet(*mpItemSet); + mpItemSet = std::move(pNew); + saveFillBitmap(*mpItemSet); + + // tell the page that it's visualization has changed + mrPage.ActionChanged(); +} + +void SdBackgroundObjUndoAction::Undo() +{ + ImplRestoreBackgroundObj(); +} + +void SdBackgroundObjUndoAction::Redo() +{ + ImplRestoreBackgroundObj(); +} + +SdUndoAction* SdBackgroundObjUndoAction::Clone() const +{ + std::unique_ptr pCopy = std::make_unique(*mpDoc, mrPage, *mpItemSet); + if (mpFillBitmapItem) + pCopy->mpFillBitmapItem.reset(mpFillBitmapItem->Clone()); + pCopy->mbHasFillBitmap = mbHasFillBitmap; + return pCopy.release(); +} + +void SdBackgroundObjUndoAction::saveFillBitmap(SfxItemSet &rItemSet) +{ + if (const XFillBitmapItem *pItem = rItemSet.GetItemIfSet(XATTR_FILLBITMAP, false)) + mpFillBitmapItem.reset(pItem->Clone()); + if (bool(mpFillBitmapItem)) + { + if (const XFillStyleItem* pItem = rItemSet.GetItemIfSet(XATTR_FILLSTYLE, false)) + mbHasFillBitmap = pItem->GetValue() == css::drawing::FillStyle_BITMAP; + rItemSet.ClearItem(XATTR_FILLBITMAP); + if (mbHasFillBitmap) + rItemSet.ClearItem(XATTR_FILLSTYLE); + } +} + +void SdBackgroundObjUndoAction::restoreFillBitmap(SfxItemSet &rItemSet) +{ + rItemSet.Put(*mpFillBitmapItem); + if (mbHasFillBitmap) + rItemSet.Put(XFillStyleItem(css::drawing::FillStyle_BITMAP)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/undoheaderfooter.cxx b/sd/source/ui/func/undoheaderfooter.cxx new file mode 100644 index 000000000..e0183dac3 --- /dev/null +++ b/sd/source/ui/func/undoheaderfooter.cxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + + +SdHeaderFooterUndoAction::SdHeaderFooterUndoAction( SdDrawDocument* pDoc, SdPage* pPage, const sd::HeaderFooterSettings& rNewSettings ) +: SdUndoAction(pDoc), + mpPage(pPage), + maOldSettings(pPage->getHeaderFooterSettings()), + maNewSettings(rNewSettings) +{ +} + +SdHeaderFooterUndoAction::~SdHeaderFooterUndoAction() +{ +} + +void SdHeaderFooterUndoAction::Undo() +{ + mpPage->setHeaderFooterSettings( maOldSettings ); + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + pViewFrm->GetDispatcher()->Execute( SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); +} + +void SdHeaderFooterUndoAction::Redo() +{ + mpPage->setHeaderFooterSettings( maNewSettings ); + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + pViewFrm->GetDispatcher()->Execute( SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/undolayer.cxx b/sd/source/ui/func/undolayer.cxx new file mode 100644 index 000000000..b29142ee8 --- /dev/null +++ b/sd/source/ui/func/undolayer.cxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + + +SdLayerModifyUndoAction::SdLayerModifyUndoAction( + SdDrawDocument* _pDoc, SdrLayer* pLayer, + const OUString& rOldLayerName, const OUString& rOldLayerTitle, const OUString& rOldLayerDesc, bool bOldIsVisible, bool bOldIsLocked, bool bOldIsPrintable, + const OUString& rNewLayerName, const OUString& rNewLayerTitle, const OUString& rNewLayerDesc, bool bNewIsVisible, bool bNewIsLocked, bool bNewIsPrintable ) +: SdUndoAction( _pDoc ), + mpLayer( pLayer ), + maOldLayerName( rOldLayerName ), + maOldLayerTitle( rOldLayerTitle ), + maOldLayerDesc( rOldLayerDesc ), + mbOldIsVisible( bOldIsVisible ), + mbOldIsLocked( bOldIsLocked ), + mbOldIsPrintable( bOldIsPrintable ), + maNewLayerName( rNewLayerName ), + maNewLayerTitle( rNewLayerTitle ), + maNewLayerDesc( rNewLayerDesc ), + mbNewIsVisible( bNewIsVisible ), + mbNewIsLocked( bNewIsLocked ), + mbNewIsPrintable( bNewIsPrintable ) +{ + OUString aString(SdResId(STR_MODIFYLAYER)); + SetComment(aString); +} + +void SdLayerModifyUndoAction::Undo() +{ + ::sd::DrawDocShell* pDocSh = mpDoc->GetDocSh(); + if( pDocSh ) + { + ::sd::DrawViewShell* pDrViewSh = dynamic_cast< ::sd::DrawViewShell*> ( pDocSh->GetViewShell() ); + if( pDrViewSh ) + { + pDrViewSh->ModifyLayer( mpLayer, maOldLayerName, maOldLayerTitle, maOldLayerDesc, mbOldIsVisible, mbOldIsLocked, mbOldIsPrintable ); + } + } +} + +void SdLayerModifyUndoAction::Redo() +{ + ::sd::DrawDocShell* pDocSh = mpDoc->GetDocSh(); + if( pDocSh ) + { + ::sd::DrawViewShell* pDrViewSh = dynamic_cast< ::sd::DrawViewShell* >( pDocSh->GetViewShell() ); + if( pDrViewSh ) + { + pDrViewSh->ModifyLayer( mpLayer, maNewLayerName, maNewLayerTitle, maNewLayerDesc, mbNewIsVisible, mbNewIsLocked, mbNewIsPrintable ); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/undopage.cxx b/sd/source/ui/func/undopage.cxx new file mode 100644 index 000000000..174747bf6 --- /dev/null +++ b/sd/source/ui/func/undopage.cxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + + +SdPageFormatUndoAction::~SdPageFormatUndoAction() +{ +} + +void SdPageFormatUndoAction::Undo() +{ + ::tools::Rectangle aOldBorderRect(mnOldLeft, mnOldUpper, mnOldRight, mnOldLower); + mpPage->ScaleObjects(maOldSize, aOldBorderRect, mbNewScale); + mpPage->SetSize(maOldSize); + mpPage->SetLeftBorder(mnOldLeft); + mpPage->SetRightBorder(mnOldRight); + mpPage->SetUpperBorder(mnOldUpper); + mpPage->SetLowerBorder(mnOldLower); + mpPage->SetOrientation(meOldOrientation); + mpPage->SetPaperBin( mnOldPaperBin ); + + mpPage->SetBackgroundFullSize( mbOldFullSize ); + if( !mpPage->IsMasterPage() ) + static_cast( mpPage->TRG_GetMasterPage() ).SetBackgroundFullSize( mbOldFullSize ); + +} + +void SdPageFormatUndoAction::Redo() +{ + ::tools::Rectangle aNewBorderRect(mnNewLeft, mnNewUpper, mnNewRight, mnNewLower); + mpPage->ScaleObjects(maNewSize, aNewBorderRect, mbNewScale); + mpPage->SetSize(maNewSize); + mpPage->SetLeftBorder(mnNewLeft); + mpPage->SetRightBorder(mnNewRight); + mpPage->SetUpperBorder(mnNewUpper); + mpPage->SetLowerBorder(mnNewLower); + mpPage->SetOrientation(meNewOrientation); + mpPage->SetPaperBin( mnNewPaperBin ); + + mpPage->SetBackgroundFullSize( mbNewFullSize ); + if( !mpPage->IsMasterPage() ) + static_cast( mpPage->TRG_GetMasterPage() ).SetBackgroundFullSize( mbNewFullSize ); + +} + +SdPageLRUndoAction::~SdPageLRUndoAction() +{ +} + +void SdPageLRUndoAction::Undo() +{ + mpPage->SetLeftBorder(mnOldLeft); + mpPage->SetRightBorder(mnOldRight); +} + +void SdPageLRUndoAction::Redo() +{ + mpPage->SetLeftBorder(mnNewLeft); + mpPage->SetRightBorder(mnNewRight); +} + +SdPageULUndoAction::~SdPageULUndoAction() +{ +} + +void SdPageULUndoAction::Undo() +{ + mpPage->SetUpperBorder(mnOldUpper); + mpPage->SetLowerBorder(mnOldLower); +} + +/** + * UL-Redo() + */ +void SdPageULUndoAction::Redo() +{ + mpPage->SetUpperBorder(mnNewUpper); + mpPage->SetLowerBorder(mnNewLower); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/unmovss.cxx b/sd/source/ui/func/unmovss.cxx new file mode 100644 index 000000000..d21f83b39 --- /dev/null +++ b/sd/source/ui/func/unmovss.cxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +SdMoveStyleSheetsUndoAction::SdMoveStyleSheetsUndoAction( SdDrawDocument* pTheDoc, StyleSheetCopyResultVector& rTheStyles, bool bInserted) +: SdUndoAction(pTheDoc) +, mbMySheets( !bInserted ) +{ + maStyles.swap( rTheStyles ); + + maListOfChildLists.resize( maStyles.size() ); + // create list with lists of style sheet children + std::size_t i = 0; + for (const auto& a : maStyles) + { + maListOfChildLists[i++] = SdStyleSheetPool::CreateChildList(a.m_xStyleSheet.get()); + } +} + +void SdMoveStyleSheetsUndoAction::Undo() +{ + SfxStyleSheetBasePool* pPool = mpDoc->GetStyleSheetPool(); + + if (mbMySheets) + { + // the styles have to be inserted in the pool + + // first insert all styles to the pool + for (auto& a : maStyles) + { + if (!a.m_bCreatedByCopy) // tdf#119259, existed before this action, so leave it alone + continue; + pPool->Insert(a.m_xStyleSheet.get()); + } + + // now assign the children again + std::vector< SdStyleSheetVector >::iterator childlistiter( maListOfChildLists.begin() ); + for (const auto& a : maStyles) + { + OUString aParent(a.m_xStyleSheet->GetName()); + for( auto& rxChild : *childlistiter ) + { + rxChild->SetParent(aParent); + } + ++childlistiter; + } + } + else + { + // remove the styles again from the pool + for (auto& a : maStyles) + { + if (!a.m_bCreatedByCopy) // tdf#119259, existed before this action, so leave it alone + continue; + pPool->Remove(a.m_xStyleSheet.get()); + } + } + mbMySheets = !mbMySheets; +} + +void SdMoveStyleSheetsUndoAction::Redo() +{ + Undo(); +} + +SdMoveStyleSheetsUndoAction::~SdMoveStyleSheetsUndoAction() +{ +} + +OUString SdMoveStyleSheetsUndoAction::GetComment() const +{ + return OUString(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/unoaprms.cxx b/sd/source/ui/func/unoaprms.cxx new file mode 100644 index 000000000..3bf7d98a6 --- /dev/null +++ b/sd/source/ui/func/unoaprms.cxx @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + + +void SdAnimationPrmsUndoAction::Undo() +{ + // no new info created: restore data + if (!bInfoCreated) + { + SdDrawDocument* pDoc(dynamic_cast< SdDrawDocument* >(&pObject->getSdrModelFromSdrObject())); + SdAnimationInfo* pInfo = pDoc ? SdDrawDocument::GetAnimationInfo(pObject) : nullptr; + if (pInfo) + { + pInfo->mbActive = bOldActive; + pInfo->meEffect = eOldEffect; + pInfo->meTextEffect = eOldTextEffect; + pInfo->meSpeed = eOldSpeed; + pInfo->mbDimPrevious = bOldDimPrevious; + pInfo->maDimColor = aOldDimColor; + pInfo->mbDimHide = bOldDimHide; + pInfo->mbSoundOn = bOldSoundOn; + pInfo->maSoundFile = aOldSoundFile; + pInfo->mbPlayFull = bOldPlayFull; + pInfo->meClickAction = eOldClickAction; + pInfo->SetBookmark( aOldBookmark ); + pInfo->mnVerb = nOldVerb; + + pInfo->meSecondEffect = eOldSecondEffect; + pInfo->meSecondSpeed = eOldSecondSpeed; + pInfo->mbSecondSoundOn = bOldSecondSoundOn; + pInfo->mbSecondPlayFull = bOldSecondPlayFull; + } + } + // info was created by action: delete info + else + { + pObject->DeleteUserData(0); + } + // force ModelHasChanged() in order to update effect window (animation order) + pObject->SetChanged(); + pObject->BroadcastObjectChange(); +} + +void SdAnimationPrmsUndoAction::Redo() +{ + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pObject,true); + + pInfo->mbActive = bNewActive; + pInfo->meEffect = eNewEffect; + pInfo->meTextEffect = eNewTextEffect; + pInfo->meSpeed = eNewSpeed; + pInfo->mbDimPrevious = bNewDimPrevious; + pInfo->maDimColor = aNewDimColor; + pInfo->mbDimHide = bNewDimHide; + pInfo->mbSoundOn = bNewSoundOn; + pInfo->maSoundFile = aNewSoundFile; + pInfo->mbPlayFull = bNewPlayFull; + pInfo->meClickAction = eNewClickAction; + pInfo->SetBookmark( aNewBookmark ); + pInfo->mnVerb = nNewVerb; + + pInfo->meSecondEffect = eNewSecondEffect; + pInfo->meSecondSpeed = eNewSecondSpeed; + pInfo->mbSecondSoundOn = bNewSecondSoundOn; + pInfo->mbSecondPlayFull = bNewSecondPlayFull; + + // force ModelHasChanged() in order to update effect window (animation order) + pObject->SetChanged(); + pObject->BroadcastObjectChange(); +} + +SdAnimationPrmsUndoAction::~SdAnimationPrmsUndoAction() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/func/unprlout.cxx b/sd/source/ui/func/unprlout.cxx new file mode 100644 index 000000000..218883349 --- /dev/null +++ b/sd/source/ui/func/unprlout.cxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include + + +SdPresentationLayoutUndoAction::SdPresentationLayoutUndoAction( + SdDrawDocument* pTheDoc, + const OUString& aTheOldLayoutName, + const OUString& aTheNewLayoutName, + AutoLayout eTheOldAutoLayout, + AutoLayout eTheNewAutoLayout, + bool bSet, + SdPage* pThePage): + SdUndoAction(pTheDoc) +{ + aOldLayoutName = aTheOldLayoutName; + aNewLayoutName = aTheNewLayoutName; + eOldAutoLayout = eTheOldAutoLayout; + eNewAutoLayout = eTheNewAutoLayout; + bSetAutoLayout = bSet; + + DBG_ASSERT(pThePage, "No Page set!"); + pPage = pThePage; + aComment = SdResId(STR_UNDO_SET_PRESLAYOUT); +} + +void SdPresentationLayoutUndoAction::Undo() +{ + pPage->SetPresentationLayout(aOldLayoutName, true, true, true); + if (bSetAutoLayout) + pPage->SetAutoLayout(eOldAutoLayout, true); +} + +void SdPresentationLayoutUndoAction::Redo() +{ + pPage->SetPresentationLayout(aNewLayoutName); + if (bSetAutoLayout) + pPage->SetAutoLayout(eNewAutoLayout, true); +} + +SdPresentationLayoutUndoAction::~SdPresentationLayoutUndoAction() +{ +} + +OUString SdPresentationLayoutUndoAction::GetComment() const +{ + return aComment; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleDocumentViewBase.hxx b/sd/source/ui/inc/AccessibleDocumentViewBase.hxx new file mode 100644 index 000000000..0db25b689 --- /dev/null +++ b/sd/source/ui/inc/AccessibleDocumentViewBase.hxx @@ -0,0 +1,324 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SD_SOURCE_UI_INC_ACCESSIBLEDOCUMENTVIEWBASE_HXX +#define INCLUDED_SD_SOURCE_UI_INC_ACCESSIBLEDOCUMENTVIEWBASE_HXX + +#include +#include +#include +#include "AccessibleViewForwarder.hxx" +#include +#include + +#include +#include +#include +#include + +#include + +#include "Window.hxx" + +namespace com::sun::star::accessibility { class XAccessible; } +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::awt { class XWindow; } + +class VclWindowEvent; + +namespace sd { +class ViewShell; +} + +namespace accessibility { + +/** Base class for the various document views of the Draw and + Impress applications. + +

The different view modes of the Draw and Impress applications + are made accessible by derived classes. When the view mode is + changed then the object representing the document view is + disposed and replaced by a new instance of the then appropriate + derived class.

+ +

This base class also manages an optionally active accessible OLE + object. If you overwrite the getAccessibleChildCount + and getAccessibleChild methods then make sure to first + call the corresponding method of this class and adapt your child count + and indices accordingly. Only one active OLE object is allowed at a + time. This class does not listen for disposing calls at the moment + because it does not use the accessible OLE object directly and trusts on + getting informed through VCL window events.

+ +

This class implements three kinds of listeners: +

  1. The property change listener is not used directly but exists as + convenience for derived classes. May be moved to those classes + instead.
  2. +
  3. As window listener it waits for changes of the window geometry and + forwards those as view forwarder changes.
  4. +
  5. As focus listener it keeps track of the focus to give this class and + derived classes the opportunity to set and remove the focus to/from + shapes.
  6. +
+

+*/ +class AccessibleDocumentViewBase + : public AccessibleContextBase, + public AccessibleComponentBase, + public AccessibleSelectionBase, + public IAccessibleViewForwarderListener, + public css::beans::XPropertyChangeListener, + public css::awt::XWindowListener, + public css::awt::XFocusListener, + public css::accessibility::XAccessibleExtendedAttributes +{ +public: + //===== internal ======================================================== + + /** Create a new object. Note that the caller has to call the + Init method directly after this constructor has finished. + @param pSdWindow + The window whose content is to be made accessible. + @param pViewShell + The view shell associated with the given window. + @param rxController + The controller from which to get the model. + @param rxParent + The accessible parent of the new object. Note that this parent does + not necessarily correspond with the parent of the given window. + */ + AccessibleDocumentViewBase ( + ::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const css::uno::Reference& rxController, + const css::uno::Reference& rxParent); + + virtual ~AccessibleDocumentViewBase() override; + + /** Initialize a new object. Call this method directly after creating a + new object. It finished the initialization begun in the constructor + but which needs a fully created object. + */ + virtual void Init(); + + /** Define callback for listening to window child events of VCL. + Listen for creation or destruction of OLE objects. + */ + DECL_LINK( WindowChildEventListener, VclWindowEvent&, void ); + + //===== IAccessibleViewForwarderListener ================================ + + /** A view forwarder change is signalled for instance when any of the + window events is received. Thus, instead of overriding the four + windowResized... methods it will be sufficient in most cases just to + override this method. + */ + virtual void ViewForwarderChanged() override; + + //===== XAccessibleContext ============================================== + + virtual css::uno::Reference SAL_CALL + getAccessibleParent() override; + + /** This implementation returns either 1 or 0 depending on whether there + is an active accessible OLE object or not. + */ + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + /** This implementation either returns the active accessible OLE object + if it exists and the given index is 0 or throws an exception. + */ + virtual css::uno::Reference SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + //===== XAccessibleComponent ============================================ + + virtual css::uno::Reference SAL_CALL + getAccessibleAtPoint (const css::awt::Point& aPoint) override; + + virtual css::awt::Rectangle SAL_CALL getBounds() override; + + virtual css::awt::Point SAL_CALL getLocation() override; + + virtual css::awt::Point SAL_CALL getLocationOnScreen() override; + + virtual css::awt::Size SAL_CALL getSize() override; + + //===== XInterface ====================================================== + + virtual css::uno::Any SAL_CALL + queryInterface (const css::uno::Type & rType) override; + + virtual void SAL_CALL + acquire() + noexcept override; + + virtual void SAL_CALL + release() + noexcept override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + + //===== XTypeProvider =================================================== + + virtual css::uno::Sequence< css::uno::Type> SAL_CALL + getTypes() override; + + //===== lang::XEventListener ============================================ + + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + //===== XPropertyChangeListener ========================================= + + virtual void SAL_CALL + propertyChange (const css::beans::PropertyChangeEvent& rEventObject) override; + + //===== XWindowListener ================================================= + + virtual void SAL_CALL + windowResized (const css::awt::WindowEvent& e) override; + + virtual void SAL_CALL + windowMoved (const css::awt::WindowEvent& e) override; + + virtual void SAL_CALL + windowShown (const css::lang::EventObject& e) override; + + virtual void SAL_CALL + windowHidden (const css::lang::EventObject& e) override; + + //===== XFocusListener ================================================= + + virtual void SAL_CALL focusGained (const css::awt::FocusEvent& e) override; + virtual void SAL_CALL focusLost (const css::awt::FocusEvent& e) override; + //----------------------------xAttribute---------------------------- + virtual css::uno::Any SAL_CALL getExtendedAttributes() override; + ::sd::ViewShell* mpViewShell; +private: + + // return the member maMutex; + virtual ::osl::Mutex& + implGetMutex() override; + + // return ourself as context in default case + virtual css::uno::Reference< css::accessibility::XAccessibleContext > + implGetAccessibleContext() override; + + // return sal_False in default case + virtual bool + implIsSelected( sal_Int32 nAccessibleChildIndex ) override; + + // return nothing in default case + virtual void + implSelect( sal_Int32 nAccessibleChildIndex, bool bSelect ) override; + +protected: + /// The API window that is made accessible. + css::uno::Reference< css::awt::XWindow> + mxWindow; + + /// The controller of the window in which this view is displayed. + css::uno::Reference< css::frame::XController> + mxController; + + /// Model of the document. + css::uno::Reference < css::frame::XModel> + mxModel; + + // Bundle of information that is passed down the shape tree. + AccessibleShapeTreeInfo maShapeTreeInfo; + + /// The view forwarder passed to the children manager. + AccessibleViewForwarder maViewForwarder; + + /** Accessible OLE object. Set or removed by the + SetAccessibleOLEObject method. + */ + css::uno::Reference< css::accessibility::XAccessible> + mxAccessibleOLEObject; + + Link maWindowLink; + + // This method is called from the component helper base class while + // disposing. + virtual void SAL_CALL disposing() override; + + /** Create a name string. The current name is not modified and, + therefore, no events are sent. This method is usually called once + by the getAccessibleName method of the base class. + @return + A name string. + */ + virtual OUString + CreateAccessibleName () override; + + /** This method is called when (after) the frame containing this + document has been activated. Can be used to send FOCUSED state + changes for the currently selected element. + + Note: Currently used as a substitute for FocusGained. Should be + renamed in the future. + */ + virtual void Activated(); + + /** This method is called when (before or after?) the frame containing + this document has been deactivated. Can be used to send FOCUSED + state changes for the currently selected element. + + Note: Currently used as a substitute for FocusLost. Should be + renamed in the future. + */ + virtual void Deactivated(); + + /** Set or remove the currently active accessible OLE object. + @param xOLEObject + If this is a valid reference then a child event is send that + informs the listeners of a new child. If there has already been + an active accessible OLE object then this is removed first and + appropriate events are sent. + + If this is an empty reference then the currently active + accessible OLE object (if there is one) is removed. + */ + void SetAccessibleOLEObject ( + const css::uno::Reference& xOLEObject); + +public: + void SwitchViewActivated() { Activated(); } + virtual sal_Int32 SAL_CALL getForeground( ) override; + + virtual sal_Int32 SAL_CALL getBackground( ) override; + virtual void impl_dispose(); +}; + +} // end of namespace accessibility + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleDrawDocumentView.hxx b/sd/source/ui/inc/AccessibleDrawDocumentView.hxx new file mode 100644 index 000000000..202edd0ea --- /dev/null +++ b/sd/source/ui/inc/AccessibleDrawDocumentView.hxx @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "AccessibleDocumentViewBase.hxx" + +#include + +namespace accessibility { class AccessiblePageShape; } +namespace accessibility { class ChildrenManager; } + +namespace accessibility { + +/** This class makes draw documents in the general view modes + accessible. It passes all shapes on the current draw page to a + children manager and additionally creates a new shape that + represents the actual draw page. + + Please see the documentation of the base class for further + explanations of the individual methods. +*/ +class AccessibleDrawDocumentView final : + public AccessibleDocumentViewBase + ,public css::accessibility::XAccessibleGroupPosition +{ +public: + //===== internal ======================================================== + + AccessibleDrawDocumentView (::sd::Window* pSdWindow, + ::sd::ViewShell* pViewShell, + const css::uno::Reference& rxController, + const css::uno::Reference& rxParent); + + virtual ~AccessibleDrawDocumentView() override; + + /** Complete the initialization begun in the constructor. + */ + virtual void Init() override; + + //===== IAccessibleViewForwarderListener ================================ + + virtual void ViewForwarderChanged() override; + + //===== XAccessibleContext ============================================== + + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + virtual OUString SAL_CALL + getAccessibleName() override; + + //===== lang::XEventListener ============================================ + + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + //===== XPropertyChangeListener ========================================= + + virtual void SAL_CALL + propertyChange (const css::beans::PropertyChangeEvent& rEventObject) override; + //===== XInterface ====================================================== + + virtual css::uno::Any SAL_CALL + queryInterface (const css::uno::Type & rType) override; + + virtual void SAL_CALL + acquire() + noexcept override; + + virtual void SAL_CALL + release() + noexcept override; + + //===== XAccessibleGroupPosition ========================================= + virtual css::uno::Sequence< sal_Int32 > SAL_CALL + getGroupPosition( const css::uno::Any& rAny ) override; + virtual OUString SAL_CALL getObjectLink( const css::uno::Any& accoject ) override; + +private: + + //===== XServiceInfo ==================================================== + + virtual OUString SAL_CALL + getImplementationName() override; + + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + + virtual bool + implIsSelected( sal_Int32 nAccessibleChildIndex ) override; + + /** Select or deselect the specified child or all children if the given + index has the special value ACCESSIBLE_SELECTION_CHILD_ALL. + Selecting or deselecting a child sets or resets the + SELECTED state and selects or deselects the UNO shape + being made accessible by the child. + @param nAccessibleChildIndex + Index of the child to select or deselect. If the parameter has + the value ACCESSIBLE_SELECTION_CHILD_ALL then all children are + selected or deselected. + @param bSelect + Indicates whether to select or deselect the specified child + reps. children. + */ + virtual void + implSelect( sal_Int32 nAccessibleChildIndex, bool bSelect ) override; + + ::sd::ViewShell* mpSdViewSh; + + /** This object manages the shapes of the represented draw page. It is + responsible to determine the visible shapes and create on demand the + accessible objects representing them. + */ + std::unique_ptr mpChildrenManager; + + // This method is called from the component helper base class while + // disposing. + virtual void SAL_CALL disposing() override; + + /** Create a shape the represents the page as seen on the screen. + */ + rtl::Reference CreateDrawPageShape(); + + /// Create an accessible name that contains the current view mode. + virtual OUString + CreateAccessibleName () override; + + /** Make sure that the currently focused shape sends a FOCUSED state + change event indicating that it has (regained) the focus. + */ + virtual void Activated() override; + + /** Make sure that the currently focused shape sends a FOCUSED state + change event indicating that it has lost the focus. + */ + virtual void Deactivated() override; + + virtual void impl_dispose() override; + + void UpdateAccessibleName(); +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleOutlineEditSource.hxx b/sd/source/ui/inc/AccessibleOutlineEditSource.hxx new file mode 100644 index 000000000..d13d27e97 --- /dev/null +++ b/sd/source/ui/inc/AccessibleOutlineEditSource.hxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class OutlinerView; +class SdrOutliner; +class SdrView; +namespace vcl { class Window; } + +namespace accessibility +{ + /** Implementation of the SvxEditSource interface in the SdOutlineView + + This class connects the SdOutlineView and its EditEngine + outliner with the AccessibleTextHelper, which provides all + necessary functionality to make the outliner text accessible + + @see SvxEditSource + @see SvxViewForwarder + */ + class AccessibleOutlineEditSource final : public SvxEditSource, public SvxViewForwarder, public SfxBroadcaster, public SfxListener + { + public: + /// Create an SvxEditSource interface for the given Outliner + AccessibleOutlineEditSource( + SdrOutliner& rOutliner, + SdrView& rView, + OutlinerView& rOutlView, + const vcl::Window& rViewWindow ); + virtual ~AccessibleOutlineEditSource() override; + + /// This method is disabled and always returns NULL + virtual std::unique_ptr Clone() const override; + virtual SvxTextForwarder* GetTextForwarder() override; + virtual SvxViewForwarder* GetViewForwarder() override; + virtual SvxEditViewForwarder* GetEditViewForwarder( bool bCreate = false ) override; + virtual void UpdateData() override; + virtual SfxBroadcaster& GetBroadcaster() const override; + + // the view forwarder + virtual bool IsValid() const override; + virtual Point LogicToPixel( const Point& rPoint, const MapMode& rMapMode ) const override; + virtual Point PixelToLogic( const Point& rPoint, const MapMode& rMapMode ) const override; + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + private: + AccessibleOutlineEditSource( const AccessibleOutlineEditSource& ) = delete; + AccessibleOutlineEditSource& operator=( const AccessibleOutlineEditSource& ) = delete; + + DECL_LINK( NotifyHdl, EENotify&, void ); + + SdrView& mrView; + const vcl::Window& mrWindow; + SdrOutliner* mpOutliner; + OutlinerView* mpOutlinerView; + + SvxOutlinerForwarder mTextForwarder; + SvxDrawOutlinerViewForwarder mViewForwarder; + + }; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleOutlineView.hxx b/sd/source/ui/inc/AccessibleOutlineView.hxx new file mode 100644 index 000000000..5fa1df7c5 --- /dev/null +++ b/sd/source/ui/inc/AccessibleOutlineView.hxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "AccessibleDocumentViewBase.hxx" +#include + +namespace sd { class OutlineViewShell; } + +namespace accessibility { + +/** This class makes the Impress outline view accessible. + + Please see the documentation of the base class for further + explanations of the individual methods. This class is a mere + wrapper around the AccessibleTextHelper class; as basically the + Outline View is a big Outliner. +*/ +class AccessibleOutlineView final + : public AccessibleDocumentViewBase +{ +public: + AccessibleOutlineView ( + ::sd::Window* pSdWindow, + ::sd::OutlineViewShell* pViewShell, + const css::uno::Reference& rxController, + const css::uno::Reference& rxParent); + + virtual ~AccessibleOutlineView() override; + + /** Complete the initialization begun in the constructor. + */ + virtual void Init() override; + + //===== IAccessibleViewForwarderListener ================================ + + virtual void ViewForwarderChanged() override; + + //===== XAccessibleContext ============================================== + + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + virtual OUString SAL_CALL + getAccessibleName() override; + //===== XAccessibleEventBroadcaster ======================================== + + virtual void SAL_CALL + addAccessibleEventListener ( + const css::uno::Reference& xListener) override; + + virtual void SAL_CALL + removeAccessibleEventListener ( + const css::uno::Reference& xListener) override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + //===== lang::XEventListener ============================================ + + using AccessibleDocumentViewBase::disposing; + + //===== XPropertyChangeListener ========================================= + + virtual void SAL_CALL + propertyChange (const css::beans::PropertyChangeEvent& rEventObject) override; + +private: + + // overridden to detect focus changes + virtual void Activated() override; + + // overridden to detect focus changes + virtual void Deactivated() override; + + // declared, but not defined + AccessibleOutlineView( const AccessibleOutlineView& ); + AccessibleOutlineView& operator= ( const AccessibleOutlineView& ); + + // This method is called from the component helper base class while disposing. + virtual void SAL_CALL disposing() override; + + /// Create an accessible name that contains the current view mode. + virtual OUString + CreateAccessibleName () override; + + /// Invalidate text helper, updates visible children + void UpdateChildren(); + + AccessibleTextHelper maTextHelper; + +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessiblePageShape.hxx b/sd/source/ui/inc/AccessiblePageShape.hxx new file mode 100644 index 000000000..164fb96fe --- /dev/null +++ b/sd/source/ui/inc/AccessiblePageShape.hxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace com::sun::star::accessibility { class XAccessible; } +namespace com::sun::star::drawing { class XDrawPage; } +namespace accessibility { class AccessibleShapeTreeInfo; } + +namespace accessibility { + +/** A page shape represents the actual page as seen on the screen. +*/ +class AccessiblePageShape + : public AccessibleShape +{ +public: + //===== internal ======================================================== + + /** Create a new accessible object that makes the given shape accessible. + @param rxParent + The accessible parent object. It will be used, for example when + the getIndexInParent method is called. + @param rShapeTreeInfo + Bundle of information passed to this shape and all of its descendants. + @attention + Always call the init method after creating a + new accessible shape. This is one way to overcome the potential + problem of registering the new object with e.g. event + broadcasters. That would delete the new object if a broadcaster + would not keep a strong reference to the new object. + */ + AccessiblePageShape ( + const css::uno::Reference& rxPage, + const css::uno::Reference& rxParent, + const AccessibleShapeTreeInfo& rShapeTreeInfo); + + virtual ~AccessiblePageShape() override; + + //===== XAccessibleContext ============================================== + + /// Returns always 0 because there can be no children. + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + /** Return the specified child. + @param nIndex + Index of the requested child. + @return + Reference of the requested child which is the accessible object + of a visible shape. + @throws IndexOutOfBoundsException + Throws always an exception because there are no children. + */ + virtual css::uno::Reference SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + //===== XAccessibleComponent ============================================ + + virtual css::awt::Rectangle SAL_CALL getBounds() override; + + virtual sal_Int32 SAL_CALL getForeground() override; + + virtual sal_Int32 SAL_CALL getBackground() override; + + //===== XComponent ====================================================== + + virtual void SAL_CALL + dispose() override; + + //===== XServiceInfo ==================================================== + + virtual OUString SAL_CALL + getImplementationName() override; + + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + + using AccessibleShape::disposing; + +protected: + /** Create a base name string that contains the accessible name. + */ + virtual OUString + CreateAccessibleBaseName() override; + + virtual OUString + CreateAccessibleName() override; + +private: + css::uno::Reference mxPage; + + AccessiblePageShape (const AccessiblePageShape&) = delete; + AccessibleShape& operator= (const AccessiblePageShape&) = delete; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessiblePresentationGraphicShape.hxx b/sd/source/ui/inc/AccessiblePresentationGraphicShape.hxx new file mode 100644 index 000000000..91e573835 --- /dev/null +++ b/sd/source/ui/inc/AccessiblePresentationGraphicShape.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace accessibility { class AccessibleShapeInfo; } +namespace accessibility { class AccessibleShapeTreeInfo; } + +namespace accessibility { + +/** This class makes Impress shapes accessible. +*/ +class AccessiblePresentationGraphicShape + : public AccessibleGraphicShape +{ +public: + //===== internal ======================================================== + AccessiblePresentationGraphicShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo); + virtual ~AccessiblePresentationGraphicShape() override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + //===== internal ======================================================== + + /// Create a name string that contains the accessible name. + virtual OUString + CreateAccessibleBaseName () override; + + /// Return this object's role. + virtual sal_Int16 SAL_CALL getAccessibleRole () override; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessiblePresentationOLEShape.hxx b/sd/source/ui/inc/AccessiblePresentationOLEShape.hxx new file mode 100644 index 000000000..a8ac60deb --- /dev/null +++ b/sd/source/ui/inc/AccessiblePresentationOLEShape.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace accessibility { + +/** This class makes Impress shapes accessible. +*/ +class AccessiblePresentationOLEShape + : public AccessibleOLEShape +{ +public: + //===== internal ======================================================== + AccessiblePresentationOLEShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo); + virtual ~AccessiblePresentationOLEShape() override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + //===== internal ======================================================== + + /// Create a name string that contains the accessible name. + virtual OUString + CreateAccessibleBaseName () override; + + /// Return this object's role. + virtual sal_Int16 SAL_CALL getAccessibleRole () override; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessiblePresentationShape.hxx b/sd/source/ui/inc/AccessiblePresentationShape.hxx new file mode 100644 index 000000000..4a6447ae9 --- /dev/null +++ b/sd/source/ui/inc/AccessiblePresentationShape.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace accessibility { + +/** This class makes Impress shapes accessible. +*/ +class AccessiblePresentationShape + : public AccessibleShape +{ +public: + //===== internal ======================================================== + AccessiblePresentationShape ( + const AccessibleShapeInfo& rShapeInfo, + const AccessibleShapeTreeInfo& rShapeTreeInfo); + virtual ~AccessiblePresentationShape() override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + //===== internal ======================================================== + + /// Create a name string that contains the accessible name. + virtual OUString + CreateAccessibleBaseName () override; + + OUString GetStyle() const override; + +private: + AccessiblePresentationShape (const AccessiblePresentationShape&) = delete; + + AccessiblePresentationShape& operator= (const AccessiblePresentationShape&) = delete; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleSlideSorterObject.hxx b/sd/source/ui/inc/AccessibleSlideSorterObject.hxx new file mode 100644 index 000000000..6da56a152 --- /dev/null +++ b/sd/source/ui/inc/AccessibleSlideSorterObject.hxx @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class SdPage; +namespace sd::slidesorter { class SlideSorter; } + +namespace accessibility { + +typedef comphelper::WeakComponentImplHelper< + css::accessibility::XAccessible, + css::accessibility::XAccessibleEventBroadcaster, + css::accessibility::XAccessibleContext, + css::accessibility::XAccessibleComponent, + css::lang::XServiceInfo > AccessibleSlideSorterObjectBase; + +/** This class makes page objects of the slide sorter accessible. +*/ +class AccessibleSlideSorterObject + : public AccessibleSlideSorterObjectBase +{ +public: + /** Create a new object that represents a page object in the slide + sorter. + @param rxParent + The accessible parent. + @param rSlideSorter + The slide sorter whose model manages the page. + @param nPageNumber + The number of the page in the range [0,nPageCount). + */ + AccessibleSlideSorterObject( + const css::uno::Reference& rxParent, + ::sd::slidesorter::SlideSorter& rSlideSorter, + sal_uInt16 nPageNumber); + virtual ~AccessibleSlideSorterObject() override; + + /** Return the page that is made accessible by the called object. + */ + SdPage* GetPage() const; + + /** The page number as given to the constructor. + */ + sal_uInt16 GetPageNumber() const { return mnPageNumber;} + + void FireAccessibleEvent ( + short nEventId, + const css::uno::Any& rOldValue, + const css::uno::Any& rNewValue); + + virtual void disposing(std::unique_lock&) override; + + //===== XAccessible ======================================================= + + virtual css::uno::Reference SAL_CALL + getAccessibleContext() override; + + //===== XAccessibleEventBroadcaster ======================================= + virtual void SAL_CALL + addAccessibleEventListener( + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL + removeAccessibleEventListener( + const css::uno::Reference& rxListener ) override; + + //===== XAccessibleContext ============================================== + + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleParent() override; + + virtual sal_Int32 SAL_CALL + getAccessibleIndexInParent() override; + + virtual sal_Int16 SAL_CALL + getAccessibleRole() override; + + virtual OUString SAL_CALL + getAccessibleDescription() override; + + virtual OUString SAL_CALL + getAccessibleName() override; + + virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet> SAL_CALL + getAccessibleRelationSet() override; + + virtual css::uno::Reference< css::accessibility::XAccessibleStateSet> SAL_CALL + getAccessibleStateSet() override; + + virtual css::lang::Locale SAL_CALL + getLocale() override; + + //===== XAccessibleComponent ================================================ + + virtual sal_Bool SAL_CALL containsPoint ( + const css::awt::Point& aPoint) override; + + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL + getAccessibleAtPoint ( + const css::awt::Point& aPoint) override; + + virtual css::awt::Rectangle SAL_CALL getBounds() override; + + virtual css::awt::Point SAL_CALL getLocation() override; + + virtual css::awt::Point SAL_CALL getLocationOnScreen() override; + + virtual css::awt::Size SAL_CALL getSize() override; + + virtual void SAL_CALL grabFocus() override; + + virtual sal_Int32 SAL_CALL getForeground() override; + + virtual sal_Int32 SAL_CALL getBackground() override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + /** Return whether the specified service is supported by this class. + */ + virtual sal_Bool SAL_CALL + supportsService (const OUString& sServiceName) override; + + /** Returns a list of all supported services. + */ + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + +private: + css::uno::Reference mxParent; + sal_uInt16 mnPageNumber; + ::sd::slidesorter::SlideSorter& mrSlideSorter; + sal_uInt32 mnClientId; + + /** Check whether or not the object has been disposed (or is in the + state of being disposed). If that is the case then + DisposedException is thrown to inform the (indirect) caller of the + foul deed. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed(); + + /** Check whether or not the object has been disposed (or is in the + state of being disposed). + + @return sal_True, if the object is disposed or in the course + of being disposed. Otherwise, sal_False is returned. + */ + bool IsDisposed() const; +}; + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleSlideSorterView.hxx b/sd/source/ui/inc/AccessibleSlideSorterView.hxx new file mode 100644 index 000000000..85003b72d --- /dev/null +++ b/sd/source/ui/inc/AccessibleSlideSorterView.hxx @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace sd::slidesorter { class SlideSorter; } + +namespace accessibility { + +class AccessibleSlideSorterObject; + +typedef ::cppu::WeakComponentImplHelper< + css::accessibility::XAccessible, + css::accessibility::XAccessibleEventBroadcaster, + css::accessibility::XAccessibleContext, + css::accessibility::XAccessibleComponent, + css::accessibility::XAccessibleSelection, + css::lang::XServiceInfo + > AccessibleSlideSorterViewBase; + +/** This class makes the SlideSorterViewShell accessible. It uses objects + of the AccessibleSlideSorterObject class to the make the page objects + accessible. +*/ +class AccessibleSlideSorterView + : public cppu::BaseMutex, + public AccessibleSlideSorterViewBase +{ +public: + AccessibleSlideSorterView( + ::sd::slidesorter::SlideSorter& rSlideSorter, + vcl::Window* pParentWindow); + + void Init(); + + virtual ~AccessibleSlideSorterView() override; + + /** This method acts like a dispose call. It sends a disposing to all + of its listeners. It may be called twice. + */ + void Destroyed(); + + void FireAccessibleEvent ( + short nEventId, + const css::uno::Any& rOldValue, + const css::uno::Any& rNewValue); + + virtual void SAL_CALL disposing() override; + + /** Return the implementation object of the specified child. + @param nIndex + Index of the child for which to return the implementation object. + */ + AccessibleSlideSorterObject* GetAccessibleChildImplementation (sal_Int32 nIndex); + + //===== XAccessible ======================================================= + + virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL + getAccessibleContext() override; + + //===== XAccessibleEventBroadcaster ======================================= + virtual void SAL_CALL + addAccessibleEventListener( + const css::uno::Reference< css::accessibility::XAccessibleEventListener >& rxListener) override; + + virtual void SAL_CALL + removeAccessibleEventListener( + const css::uno::Reference< css::accessibility::XAccessibleEventListener >& rxListener ) override; + + //===== XAccessibleContext ============================================== + + /// Return the number of currently visible children. + virtual sal_Int32 SAL_CALL + getAccessibleChildCount() override; + + /// Return the specified child or throw exception. + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleChild (sal_Int32 nIndex) override; + + /// Return a reference to the parent. + virtual css::uno::Reference< css::accessibility::XAccessible> SAL_CALL + getAccessibleParent() override; + + /// Return this objects index among the parents children. + virtual sal_Int32 SAL_CALL + getAccessibleIndexInParent() override; + + /// Return this object's role. + virtual sal_Int16 SAL_CALL + getAccessibleRole() override; + + /// Return this object's description. + virtual OUString SAL_CALL + getAccessibleDescription() override; + + /// Return the object's current name. + virtual OUString SAL_CALL + getAccessibleName() override; + + /// Return NULL to indicate that an empty relation set. + virtual css::uno::Reference< + css::accessibility::XAccessibleRelationSet> SAL_CALL + getAccessibleRelationSet() override; + + /// Return the set of current states. + virtual css::uno::Reference< + css::accessibility::XAccessibleStateSet> SAL_CALL + getAccessibleStateSet() override; + + /** Return the parents locale or throw exception if this object has no + parent yet/anymore. + */ + virtual css::lang::Locale SAL_CALL + getLocale() override; + + //===== XAccessibleComponent ================================================ + + /** The default implementation uses the result of + getBounds to determine whether the given point lies + inside this object. + */ + virtual sal_Bool SAL_CALL containsPoint ( + const css::awt::Point& aPoint) override; + + /** The default implementation returns an empty reference. + */ + virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL + getAccessibleAtPoint ( + const css::awt::Point& aPoint) override; + + /** The default implementation returns an empty rectangle. + */ + virtual css::awt::Rectangle SAL_CALL getBounds() override; + + /** The default implementation uses the result of + getBounds to determine the location. + */ + virtual css::awt::Point SAL_CALL getLocation() override; + + /** The default implementation returns an empty position, i.e. the + * result of the default constructor of css::awt::Point. + */ + virtual css::awt::Point SAL_CALL getLocationOnScreen() override; + + /** The default implementation uses the result of + getBounds to determine the size. + */ + virtual css::awt::Size SAL_CALL getSize() override; + + /** The default implementation does nothing. + */ + virtual void SAL_CALL grabFocus() override; + + /** Returns black as the default foreground color. + */ + virtual sal_Int32 SAL_CALL getForeground() override; + + /** Returns white as the default background color. + */ + virtual sal_Int32 SAL_CALL getBackground() override; + + //===== XAccessibleSelection ============================================== + + virtual void SAL_CALL + selectAccessibleChild (sal_Int32 nChildIndex) override; + + virtual sal_Bool SAL_CALL + isAccessibleChildSelected( sal_Int32 nChildIndex ) override; + + virtual void SAL_CALL + clearAccessibleSelection( ) override; + + virtual void SAL_CALL + selectAllAccessibleChildren( ) override; + + virtual sal_Int32 SAL_CALL + getSelectedAccessibleChildCount( ) override; + + virtual css::uno::Reference< + css::accessibility::XAccessible > SAL_CALL + getSelectedAccessibleChild( sal_Int32 nSelectedChildIndex ) override; + + virtual void SAL_CALL + deselectAccessibleChild( sal_Int32 nSelectedChildIndex ) override; + + //===== XServiceInfo ==================================================== + + /** Returns an identifier for the implementation of this object. + */ + virtual OUString SAL_CALL + getImplementationName() override; + + /** Return whether the specified service is supported by this class. + */ + virtual sal_Bool SAL_CALL + supportsService (const OUString& sServiceName) override; + + /** Returns a list of all supported services. + */ + virtual css::uno::Sequence< OUString> SAL_CALL + getSupportedServiceNames() override; + + void SwitchViewActivated(); +private: + class Implementation; + ::std::unique_ptr mpImpl; + + ::sd::slidesorter::SlideSorter& mrSlideSorter; + + sal_uInt32 mnClientId; + + VclPtr mpContentWindow; + + /** Check whether or not the object has been disposed (or is in the + state of being disposed). If that is the case then + DisposedException is thrown to inform the (indirect) caller of the + foul deed. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AccessibleViewForwarder.hxx b/sd/source/ui/inc/AccessibleViewForwarder.hxx new file mode 100644 index 000000000..c791921e6 --- /dev/null +++ b/sd/source/ui/inc/AccessibleViewForwarder.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SdrPaintView; +class OutputDevice; + +namespace accessibility +{ +/**

This class provides the means to transform between internal coordinates + and screen coordinates without giving direct access to the underlying + view. It represents a certain window. A call to + GetVisArea returns the corresponding visible + rectangle.

+ + @attention + Note, that modifications of the underlying view that lead to + different transformations between internal and screen coordinates or + change the validity of the forwarder have to be signaled separately. +*/ +class AccessibleViewForwarder final : public IAccessibleViewForwarder +{ +public: + //===== internal ======================================================== + + AccessibleViewForwarder(SdrPaintView* pView, const OutputDevice& rDevice); + + virtual ~AccessibleViewForwarder() override; + + //===== IAccessibleViewforwarder ======================================== + + /** Returns the area of the underlying document that is visible in the + * corresponding window. + + @return + The rectangle of the visible part of the document. + */ + virtual ::tools::Rectangle GetVisibleArea() const override; + + /** Transform the specified point from internal coordinates to an + absolute screen position. + + @param rPoint + Point in internal coordinates. + + @return + The same point but in screen coordinates relative to the upper + left corner of the (current) screen. + */ + virtual Point LogicToPixel(const Point& rPoint) const override; + + /** Transform the specified size from internal coordinates to a screen + * position. + + @param rSize + Size in internal coordinates. + + @return + The same size but in screen coordinates. + */ + virtual Size LogicToPixel(const Size& rSize) const override; + +private: + SdrPaintView* mpView; + sal_uInt16 mnWindowId; + + AccessibleViewForwarder(AccessibleViewForwarder const&) = delete; + AccessibleViewForwarder& operator=(AccessibleViewForwarder const&) = delete; +}; + +} // end of namespace accessibility + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/AnimationChildWindow.hxx b/sd/source/ui/inc/AnimationChildWindow.hxx new file mode 100644 index 000000000..1223dfdbd --- /dev/null +++ b/sd/source/ui/inc/AnimationChildWindow.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace vcl { class Window; } +class SfxBindings; + +namespace sd { + +class AnimationChildWindow + : public SfxChildWindow +{ +public: + AnimationChildWindow( + vcl::Window*, + sal_uInt16, + SfxBindings*, + SfxChildWinInfo*); + + SFX_DECL_CHILDWINDOW_WITHID(AnimationChildWindow); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/BezierObjectBar.hxx b/sd/source/ui/inc/BezierObjectBar.hxx new file mode 100644 index 000000000..a030576eb --- /dev/null +++ b/sd/source/ui/inc/BezierObjectBar.hxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd +{ +class View; +class ViewShell; + +class BezierObjectBar final : public SfxShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDDRAWBEZIEROBJECTBAR) + + BezierObjectBar(ViewShell* pSdViewShell, View* pSdView); + virtual ~BezierObjectBar() override; + + void GetAttrState(SfxItemSet& rSet); + void Execute(SfxRequest& rReq); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + View* mpView; + ViewShell* mpViewSh; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/BreakDlg.hxx b/sd/source/ui/inc/BreakDlg.hxx new file mode 100644 index 000000000..ee2a8b15d --- /dev/null +++ b/sd/source/ui/inc/BreakDlg.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace sd +{ +class DrawDocShell; +class DrawView; + +/** + * dialog to break meta files + */ +class BreakDlg : public SfxDialogController +{ +public: + BreakDlg(weld::Window* pWindow, DrawView* pDrView, DrawDocShell* pShell, + sal_uLong nSumActionCount, sal_uLong nObjCount); + + virtual short run() override; + +private: + std::unique_ptr m_xFiObjInfo; + std::unique_ptr m_xFiActInfo; + std::unique_ptr m_xFiInsInfo; + std::unique_ptr m_xBtnCancel; + + DrawView* m_pDrView; + + bool m_bCancel; + + Idle m_aUpdateIdle; + std::unique_ptr m_xProgrInfo; + std::unique_ptr m_xProgress; + + DECL_LINK(CancelButtonHdl, weld::Button&, void); + DECL_LINK(UpDate, void*, bool); + DECL_LINK(InitialUpdate, Timer*, void); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/BulletAndPositionDlg.hxx b/sd/source/ui/inc/BulletAndPositionDlg.hxx new file mode 100644 index 000000000..6dde73753 --- /dev/null +++ b/sd/source/ui/inc/BulletAndPositionDlg.hxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include "View.hxx" +#include + +#define MN_GALLERY_ENTRY 100 + +class ColorListBox; +class SvxNumValueSet; +class SvxNumRule; +class SvxBmpNumValueSet; +class SvxBrushItem; +class SdDrawDocument; + +namespace sd +{ +class View; +} + +/// Main class for handling the bullets, numbering format and their position. +class SvxBulletAndPositionDlg : public weld::GenericDialogController +{ + OUString m_sNumCharFmtName; + + Timer aInvalidateTimer; + + std::unique_ptr pActNum; + std::unique_ptr pSaveNum; + const SfxItemSet& rFirstStateSet; + + Size aInitSize[SVX_MAX_NUM]; + + bool bLastWidthModified : 1; + bool bModified : 1; + bool bInInitControl : 1; // workaround for Modify-error, is said to be corrected from 391 on + bool bLabelAlignmentPosAndSpaceModeActive; + bool bApplyToMaster; + + std::vector aGrfNames; + vcl::Font aActBulletFont; + + sal_uInt8 nBullet; + sal_uInt16 nActNumLvl; + weld::Window* p_Window; + TypedWhichId nNumItemId; + MapUnit eCoreUnit; + + SvxNumberingPreview m_aPreviewWIN; + std::unique_ptr m_xGrid; + std::unique_ptr m_xLevelLB; + std::unique_ptr m_xFmtLB; + std::unique_ptr m_xPrefixFT; + std::unique_ptr m_xPrefixED; + std::unique_ptr m_xSuffixFT; + std::unique_ptr m_xSuffixED; + std::unique_ptr m_xBeforeAfter; + std::unique_ptr m_xBulColorFT; + std::unique_ptr m_xBulColLB; + std::unique_ptr m_xBulRelSizeFT; + std::unique_ptr m_xBulRelSizeMF; + std::unique_ptr m_xStartFT; + std::unique_ptr m_xStartED; + std::unique_ptr m_xBulletFT; + std::unique_ptr m_xBulletPB; + std::unique_ptr m_xBitmapMB; + std::unique_ptr m_xWidthFT; + std::unique_ptr m_xWidthMF; + std::unique_ptr m_xHeightFT; + std::unique_ptr m_xHeightMF; + std::unique_ptr m_xRatioCB; + std::unique_ptr m_xGalleryMenu; + std::unique_ptr m_xPreviewWIN; + std::unique_ptr m_xDistBorderFT; + std::unique_ptr m_xDistBorderMF; + std::unique_ptr m_xRelativeCB; + std::unique_ptr m_xIndentFT; + std::unique_ptr m_xIndentMF; + std::unique_ptr m_xLeftTB; + std::unique_ptr m_xCenterTB; + std::unique_ptr m_xRightTB; + std::unique_ptr m_xSlideRB; + std::unique_ptr m_xSelectionRB; + std::unique_ptr m_xApplyToMaster; + std::unique_ptr m_xReset; + + void InitControls(); + /** To switch between the numbering type + 0 - Number; + 1 - Bullet; + 2 - Bitmap; */ + void SwitchNumberType(sal_uInt8 nType); + void CheckForStartValue_Impl(sal_uInt16 nNumberingType); + + DECL_LINK(NumberTypeSelectHdl_Impl, weld::ComboBox&, void); + DECL_LINK(LevelHdl_Impl, weld::TreeView&, void); + DECL_LINK(PopupActivateHdl_Impl, weld::Toggleable&, void); + DECL_LINK(GraphicHdl_Impl, const OString&, void); + DECL_LINK(BulletHdl_Impl, weld::Button&, void); + DECL_LINK(SizeHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(RatioHdl_Impl, weld::Toggleable&, void); + DECL_LINK(EditModifyHdl_Impl, weld::Entry&, void); + DECL_LINK(SpinModifyHdl_Impl, weld::SpinButton&, void); + DECL_LINK(BulColorHdl_Impl, ColorListBox&, void); + DECL_LINK(BulRelSizeHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(PreviewInvalidateHdl_Impl, Timer*, void); + DECL_LINK(DistanceHdl_Impl, weld::MetricSpinButton&, void); + DECL_LINK(RelativeHdl_Impl, weld::Toggleable&, void); + DECL_LINK(SelectLeftAlignmentHdl_Impl, weld::Toggleable&, void); + DECL_LINK(SelectCenterAlignmentHdl_Impl, weld::Toggleable&, void); + DECL_LINK(SelectRightAlignmentHdl_Impl, weld::Toggleable&, void); + DECL_LINK(ApplyToMasterHdl_Impl, weld::Toggleable&, void); + DECL_LINK(ResetHdl_Impl, weld::Button&, void); + void EditModifyHdl_Impl(const weld::Entry*); + void InitPosAndSpaceMode(); + void SetAlignmentHdl_Impl(SvxAdjust); + +public: + SvxBulletAndPositionDlg(weld::Window* pWindow, const SfxItemSet& rSet, const ::sd::View* pView); + virtual ~SvxBulletAndPositionDlg() override; + + SfxItemSet* GetOutputItemSet(SfxItemSet* rSet); + bool IsApplyToMaster() const; + bool IsSlideScope() const; + void Reset(const SfxItemSet* rSet); + + void SetCharFmt(const OUString& rNumName) { m_sNumCharFmtName = rNumName; } + void SetMetric(FieldUnit eSet); + + void SetModified(bool bRepaint = true); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/Client.hxx b/sd/source/ui/inc/Client.hxx new file mode 100644 index 000000000..6b999068e --- /dev/null +++ b/sd/source/ui/inc/Client.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +class SdrOle2Obj; + +namespace sd +{ +class ViewShell; + +class Client : public SfxInPlaceClient +{ + ViewShell* mpViewShell; + SdrOle2Obj* pSdrOle2Obj; + + virtual void ObjectAreaChanged() override; + virtual void RequestNewObjectArea(::tools::Rectangle&) override; + virtual void ViewChanged() override; + +public: + Client(SdrOle2Obj* pObj, ViewShell* pSdViewShell, vcl::Window* pWindow); + virtual ~Client() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ClientView.hxx b/sd/source/ui/inc/ClientView.hxx new file mode 100644 index 000000000..7a52053ba --- /dev/null +++ b/sd/source/ui/inc/ClientView.hxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "drawview.hxx" + +namespace sd +{ +/** + * The SdClientView is used for DrawDocShell::Draw() + */ +class ClientView : public DrawView +{ +public: + ClientView(DrawDocShell* pDocSh, OutputDevice* pOutDev); + virtual ~ClientView() override; + + /* if the view should not do an Invalidate() on the windows, you have to + override the following two methods and do something different */ + virtual void InvalidateOneWin(OutputDevice& rWin) override; + virtual void InvalidateOneWin(OutputDevice& rWin, const ::tools::Rectangle& rRect) override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/CustomAnimationList.hxx b/sd/source/ui/inc/CustomAnimationList.hxx new file mode 100644 index 000000000..ca9673fd7 --- /dev/null +++ b/sd/source/ui/inc/CustomAnimationList.hxx @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace com::sun::star::drawing { class XShape; } + +struct ImplSVEvent; + +namespace sd { + +typedef std::shared_ptr< CustomAnimationEffect > CustomAnimationEffectPtr; + +class ICustomAnimationListController +{ +public: + virtual void onSelect() = 0; + virtual void onDoubleClick() = 0; + virtual void onContextMenu(const OString &rIdent) = 0; + virtual void onDragNDropComplete( std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore ) = 0; + virtual ~ICustomAnimationListController() {} +}; + +class CustomAnimationList; +class CustomAnimationListEntryItem; + +class CustomAnimationListDropTarget : public DropTargetHelper +{ +private: + CustomAnimationList& m_rTreeView; + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +public: + CustomAnimationListDropTarget(CustomAnimationList& rTreeView); +}; + +class CustomAnimationList : public ISequenceListener +{ + friend class CustomAnimationListEntryItem; + friend struct stl_append_effect_func; + +public: + explicit CustomAnimationList(std::unique_ptr xTreeView, + std::unique_ptr xLabel, + std::unique_ptr xScrolledWindow); + virtual ~CustomAnimationList(); + + // methods + + /** selects or deselects the given effect. + Selections of other effects are not changed */ + void select( const CustomAnimationEffectPtr& pEffect ); + + /** populates the list with all effects from the given MainSequence */ + void update( const MainSequencePtr& pMainSequence ); + + void update(); + + EffectSequence getSelection() const; + + // events + void onSelectionChanged(const css::uno::Any& rSelection); + + void Select(); + + virtual void notify_change() override; + + bool isExpanded( const CustomAnimationEffectPtr& pEffect ) const; + bool isVisible( const CustomAnimationEffectPtr& pEffect ) const; + + // clears all entries from the listbox + void clear(); + + void setController( ICustomAnimationListController* pController ) + { + mpController = pController; + }; + + sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt); + sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt); + + void set_sensitive(bool bSensitive) { mxTreeView->set_sensitive(bSensitive); } + int get_height_rows(int nRows) { return mxTreeView->get_height_rows(nRows); } + int get_approximate_digit_width() const { return mxTreeView->get_approximate_digit_width(); } + void set_size_request(int nWidth, int nHeight) + { + mxTreeView->set_size_request(nWidth, nHeight); + mxEmptyLabel->set_size_request(nWidth, nHeight); + } + void unselect_all() { mxTreeView->unselect_all(); } + weld::TreeView& get_widget() { return *mxTreeView; } + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(ExpandHdl, const weld::TreeIter&, bool); + DECL_LINK(PostExpandHdl, void*, void); + DECL_LINK(CollapseHdl, const weld::TreeIter&, bool); + DECL_LINK(PostCollapseHdl, void*, void); + +private: + std::unique_ptr mxTreeView; + CustomAnimationListDropTarget maDropTargetHelper; + std::unique_ptr mxEmptyLabel; + std::unique_ptr mxEmptyLabelParent; + std::vector> mxEntries; + std::vector> lastSelectedEntries; + + bool mbIgnorePaint; + + DECL_LINK(SelectHdl, weld::TreeView&, void); + DECL_LINK(CommandHdl, const CommandEvent&, bool); + DECL_LINK(DoubleClickHdl, weld::TreeView&, bool); + DECL_LINK(DragBeginHdl, bool&, bool); + DECL_STATIC_LINK(CustomAnimationList, CustomRenderHdl, weld::TreeView::render_args, void); + DECL_STATIC_LINK(CustomAnimationList, CustomGetSizeHdl, weld::TreeView::get_size_args, Size); + + void ExecuteContextMenuAction(const OString& rSelectedPopupEntry); + + /** appends the given effect to the list*/ + void append( CustomAnimationEffectPtr pEffect ); + + ICustomAnimationListController* mpController; + + MainSequencePtr mpMainSequence; + + css::uno::Reference< css::drawing::XShape > mxLastTargetShape; + sal_Int32 mnLastGroupId; + ImplSVEvent* mnPostExpandEvent; + ImplSVEvent* mnPostCollapseEvent; + + std::unique_ptr mxLastParentEntry; + + // drag & drop + std::unique_ptr mxDndEffectDragging; + std::vector> mDndEffectsSelected; +}; + +OUString getPropertyName( sal_Int32 nPropertyType ); + +OUString getShapeDescription( const css::uno::Reference< css::drawing::XShape >& xShape, bool bWithText ); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/CustomAnimationPane.hxx b/sd/source/ui/inc/CustomAnimationPane.hxx new file mode 100644 index 000000000..5e2d69658 --- /dev/null +++ b/sd/source/ui/inc/CustomAnimationPane.hxx @@ -0,0 +1,179 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include "CustomAnimationList.hxx" +#include + +#include + +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XDrawView; } +namespace weld { class ComboBox; } +namespace com::sun::star::animations { class XAnimationNode; } +namespace sd::tools { class EventMultiplexerEvent; } + +enum class PathKind { NONE, CURVE, POLYGON, FREEFORM }; + +namespace sd { + +class MotionPathTag; +class SdPropertySubControl; +class STLPropertySet; +class ViewShellBase; + +typedef std::vector< rtl::Reference< MotionPathTag > > MotionPathTagVector; + +class CustomAnimationPane : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow + , public ICustomAnimationListController +{ + friend class MotionPathTag; +public: + CustomAnimationPane(weld::Widget* pParent, ViewShellBase& rBase); + virtual ~CustomAnimationPane() override; + + // ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + + // callbacks + void onSelectionChanged(); + void onChangeCurrentPage(); + void onAdd(); + void onRemove(); + void onChangeStart(); + void onChangeStart( sal_Int16 nNodeType ); + void onChangeSpeed(); + + // methods + void preview( const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ); + void remove( CustomAnimationEffectPtr const & pEffect ); + + // ICustomAnimationListController + virtual void onSelect() override; + virtual void onDoubleClick() override; + virtual void onContextMenu(const OString& rIdent) override; + virtual void onDragNDropComplete( std::vector< CustomAnimationEffectPtr > pEffectsDragged, CustomAnimationEffectPtr pEffectInsertBefore ) override; + + void addUndo(); + + double getDuration() const; + void updatePathFromMotionPathTag( const rtl::Reference< MotionPathTag >& xTag ); + +private: + void initialize(); + void addListener(); + void removeListener(); + void updateControls(); + void updateMotionPathTags(); + + void showOptions(const OString& sPage = OString()); + void moveSelection( bool bUp ); + void onPreview( bool bForcePreview ); + + std::unique_ptr createSelectionSet(); + void changeSelection( STLPropertySet const * pResultSet, STLPropertySet const * pOldSet ); + + static css::uno::Any getProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect ); + static bool setProperty1Value( sal_Int32 nType, const CustomAnimationEffectPtr& pEffect, const css::uno::Any& rValue ); + sal_Int32 fillAnimationLB( bool bHasText ); + PathKind getCreatePathKind() const; + void createPath( PathKind eKind, std::vector< ::com::sun::star::uno::Any >& rTargets, double fDuration ); + + DECL_LINK( implControlListBoxHdl, weld::ComboBox&, void ); + DECL_LINK( implClickHdl, weld::Button&, void ); + DECL_LINK( implToggleHdl, weld::Toggleable&, void ); + DECL_LINK( implPropertyHdl, LinkParamNone*, void ); + DECL_LINK( EventMultiplexerListener, tools::EventMultiplexerEvent&, void ); + DECL_LINK( lateInitCallback, Timer *, void ); + DECL_LINK( DurationModifiedHdl, weld::MetricSpinButton&, void ); + DECL_LINK( DelayModifiedHdl, weld::MetricSpinButton&, void ); + DECL_LINK( DelayLoseFocusHdl, weld::Widget&, void ); + DECL_LINK( UpdateAnimationLB, weld::ComboBox&, void ); + DECL_LINK( AnimationSelectHdl, weld::TreeView&, void ); + DECL_LINK( SelectionHandler, Timer*, void ); + void implControlHdl(const weld::Widget* pControl); + +private: + ViewShellBase& mrBase; + + // UI Elements + std::unique_ptr mxFTAnimation; + std::unique_ptr mxCustomAnimationList; + std::unique_ptr mxPBAddEffect; + std::unique_ptr mxPBRemoveEffect; + std::unique_ptr mxPBMoveUp; + std::unique_ptr mxPBMoveDown; + std::unique_ptr mxFTCategory; + std::unique_ptr mxLBCategory; + std::unique_ptr mxFTEffect; + std::unique_ptr mxLBAnimation; + std::unique_ptr mxFTStart; + std::unique_ptr mxLBStart; + std::unique_ptr mxFTProperty; + std::unique_ptr mxLBSubControl; + std::unique_ptr mxPlaceholderBox; + std::unique_ptr mxPBPropertyMore; + std::unique_ptr mxFTDuration; + std::unique_ptr mxCBXDuration; + std::unique_ptr mxFTStartDelay; + std::unique_ptr mxMFStartDelay; + std::unique_ptr mxCBAutoPreview; + std::unique_ptr mxPBPlay; + + Idle maIdle; + + OUString maStrModify; + OUString maStrProperty; + + sal_Int32 mnLastSelectedAnimation; + sal_Int32 mnPropertyType; + static sal_Int32 const gnMotionPathPos = 3; + sal_Int32 mnCurvePathPos; + sal_Int32 mnPolygonPathPos; + sal_Int32 mnFreeformPathPos; + + EffectSequence maListSelection; + css::uno::Any maViewSelection; + + MainSequencePtr mpMainSequence; + + css::uno::Reference< css::drawing::XDrawPage > mxCurrentPage; + css::uno::Reference< css::drawing::XDrawView > mxView; + + /** The CustomAnimationPresets is initialized either on demand or + after a short time after the construction of a new object of this + class. This timer is responsible for the later. + */ + Timer maLateInitTimer; + + MotionPathTagVector maMotionPathTags; + + ScopeLock maSelectionLock; +}; + +void fillRepeatComboBox(weld::ComboBox& rBox); + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DocumentRenderer.hxx b/sd/source/ui/inc/DocumentRenderer.hxx new file mode 100644 index 000000000..7cbeefc79 --- /dev/null +++ b/sd/source/ui/inc/DocumentRenderer.hxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd { class ViewShellBase; } + +namespace sd { + +typedef comphelper::WeakComponentImplHelper < + css::view::XRenderable + > DocumentRendererInterfaceBase; + +class DocumentRenderer final + : public DocumentRendererInterfaceBase +{ +public: + DocumentRenderer (ViewShellBase& rBase); + virtual ~DocumentRenderer() override; + + // XRenderable + virtual sal_Int32 SAL_CALL getRendererCount ( + const css::uno::Any& aSelection, + const css::uno::Sequence& xOptions) override; + + virtual css::uno::Sequence SAL_CALL getRenderer ( + sal_Int32 nRenderer, + const css::uno::Any& rSelection, + const css::uno::Sequence& rxOptions) override; + + virtual void SAL_CALL render ( + sal_Int32 nRenderer, + const css::uno::Any& rSelection, + const css::uno::Sequence& rxOptions) override; + +private: + class Implementation; + std::unique_ptr mpImpl; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DrawController.hxx b/sd/source/ui/inc/DrawController.hxx new file mode 100644 index 000000000..2c15e26eb --- /dev/null +++ b/sd/source/ui/inc/DrawController.hxx @@ -0,0 +1,327 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace com::sun::star::drawing { class XDrawSubController; } +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XModuleController; } +namespace com::sun::star::drawing { class XLayer; } +namespace osl { class Mutex; } + +class SdPage; + +namespace sd { + +typedef ::cppu::ImplInheritanceHelper < + SfxBaseController, + css::view::XSelectionSupplier, + css::lang::XServiceInfo, + css::drawing::XDrawView, + css::view::XSelectionChangeListener, + css::view::XFormLayerAccess, + css::drawing::framework::XControllerManager, + css::lang::XUnoTunnel + > DrawControllerInterfaceBase; + +class BroadcastHelperOwner +{ +public: + explicit BroadcastHelperOwner (::osl::Mutex& rMutex) : maBroadcastHelper(rMutex) {}; + ::cppu::OBroadcastHelper maBroadcastHelper; +}; + +class ViewShellBase; + +/** The DrawController is the UNO controller for Impress and Draw. It + relies objects that implement the DrawSubController interface for view + specific behaviour. The life time of the DrawController is roughly that + of ViewShellBase but note that the DrawController can (in the case of a + reload) outlive the ViewShellBase. + + The implementation of the XControllerManager interface is not yet in its + final form. +*/ +class DrawController final + : public DrawControllerInterfaceBase, + private BroadcastHelperOwner, + public ::cppu::OPropertySetHelper +{ +public: + enum PropertyHandle { + PROPERTY_WORKAREA = 0, + PROPERTY_SUB_CONTROLLER = 1, + PROPERTY_CURRENTPAGE = 2, + PROPERTY_MASTERPAGEMODE = 3, + PROPERTY_LAYERMODE = 4, + PROPERTY_ACTIVE_LAYER = 5, + PROPERTY_ZOOMTYPE = 6, + PROPERTY_ZOOMVALUE = 7, + PROPERTY_VIEWOFFSET = 8, + PROPERTY_DRAWVIEWMODE = 9 + ,PROPERTY_UPDATEACC = 10 + ,PROPERTY_PAGE_CHANGE = 11 + }; + + /** Create a new DrawController object for the given ViewShellBase. + */ + explicit DrawController (ViewShellBase& rBase) noexcept; + + virtual ~DrawController() noexcept override; + + /** Replace the currently used sub controller with the given one. This + new sub controller is used from now on for the view (that is the + main view shell to be precise) specific tasks. Call this method + with a suitable sub controller whenever the view shell in the center + pane is exchanged. + @param pSubController + The ViewShell specific sub controller or NULL when (temporarily + while switching to another one) there is no ViewShell displayed + in the center pane. + */ + void SetSubController ( + const css::uno::Reference& rxSubController); + + /** Call this method when the VisArea has changed. + */ + void FireVisAreaChanged (const ::tools::Rectangle& rVisArea) noexcept; + + /** Call this method when the selection has changed. + */ + void FireSelectionChangeListener() noexcept; + + /** Call this method when the edit mode has changed. + */ + void FireChangeEditMode (bool bMasterPageMode) noexcept; + + /** Call this method when the layer mode has changed. + */ + void FireChangeLayerMode (bool bLayerMode) noexcept; + + /** Call this method when there is a new current page. + */ + void FireSwitchCurrentPage (SdPage* pCurrentPage) noexcept; + + /** Broadcast a sidebar context change that is caused by a view + switch. + */ + void BroadcastContextChange() const; + void NotifyAccUpdate(); + void fireChangeLayer( css::uno::Reference< css::drawing::XLayer>* pCurrentLayer ) noexcept; + // change the parameter to int + //void fireSwitchCurrentPage( String pageName) throw(); + void fireSwitchCurrentPage( sal_Int32 pageIndex) noexcept; + bool IsDisposing() const { return mbDisposing; } + + /** Return a pointer to the ViewShellBase object that the DrawController + is connected to. + @return + The returned pointer is after a call to + ReleaseViewShellBase(). + */ + ViewShellBase* GetViewShellBase() { return mpBase;} + + /** This method is typically called from the destructor of ViewShellBase + to tell the DrawController that it and its members must not access + the ViewShellBase anymore. + After this call the DrawController is semi-disposed. + */ + void ReleaseViewShellBase(); + + static const css::uno::Sequence& getUnoTunnelId(); + + DECLARE_XINTERFACE() + DECLARE_XTYPEPROVIDER() + + // XComponent + virtual void SAL_CALL dispose() override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // XController + virtual sal_Bool SAL_CALL suspend( sal_Bool Suspend ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XSelectionSupplier + virtual sal_Bool SAL_CALL select( const css::uno::Any& aSelection ) override; + virtual css::uno::Any SAL_CALL getSelection( ) override; + virtual void SAL_CALL addSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + virtual void SAL_CALL removeSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + + // XFormLayerAccess + virtual css::uno::Reference< css::form::runtime::XFormController > SAL_CALL getFormController( const css::uno::Reference< css::form::XForm >& Form ) override; + virtual sal_Bool SAL_CALL isFormDesignMode( ) override; + virtual void SAL_CALL setFormDesignMode( sal_Bool DesignMode ) override; + + // XControlAccess + virtual css::uno::Reference< css::awt::XControl > SAL_CALL getControl( const css::uno::Reference< css::awt::XControlModel >& xModel ) override; + + // XDrawView + virtual void SAL_CALL + setCurrentPage ( + const css::uno::Reference< + css::drawing::XDrawPage >& xPage) override; + + virtual css::uno::Reference< + css::drawing::XDrawPage > SAL_CALL + getCurrentPage() override; + + // lang::XEventListener + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + // view::XSelectionChangeListener + virtual void SAL_CALL + selectionChanged (const css::lang::EventObject& rEvent) override; + + // XControllerManager + + virtual css::uno::Reference SAL_CALL + getConfigurationController() override; + + virtual css::uno::Reference SAL_CALL + getModuleController() override; + + // XUnoTunnel + + virtual sal_Int64 SAL_CALL getSomething (const css::uno::Sequence& rId) override; + +private: + /** This method must return the name to index table. This table + contains all property names and types of this object. + */ + virtual ::cppu::IPropertyArrayHelper & SAL_CALL getInfoHelper() override; + + static void FillPropertyTable ( + ::std::vector< css::beans::Property>& rProperties); + + /** + * The same as getFastPropertyValue, but return the value through + * rValue and nHandle is always valid. + */ + virtual void SAL_CALL getFastPropertyValue( + css::uno::Any& rValue, + sal_Int32 nHandle ) const override; + + /** Convert the value rValue and return the result in rConvertedValue and the + old value in rOldValue. + After this call the vetoable listeners are notified. + + @param rConvertedValue + The converted value. Only set if return is true. + @param rOldValue + The old value. Only set if return is true. + @param nHandle + The handle of the property. + @return + if the value is converted successfully. + @throws IllegalArgumentException + */ + virtual sal_Bool SAL_CALL convertFastPropertyValue( + css::uno::Any & rConvertedValue, + css::uno::Any & rOldValue, + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + /** The same as setFastPropertyValue, but no exception is thrown and nHandle + is always valid. You must not broadcast the changes in this method. + */ + virtual void SAL_CALL setFastPropertyValue_NoBroadcast( + sal_Int32 nHandle, + const css::uno::Any& rValue ) override; + + /** When the called object has been disposed already this method throws + a Disposed exception and does not return. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed() const; + + using cppu::OPropertySetHelper::disposing; + using cppu::OPropertySetHelper::getFastPropertyValue; + + css::uno::Reference< css::drawing::XLayer>* mpCurrentLayer; + + const css::uno::Type m_aSelectionTypeIdentifier; + + /** This pointer to the ViewShellBase can be NULL (after a call to + ReleaseViewShellBase()). + */ + ViewShellBase* mpBase; + + ::tools::Rectangle maLastVisArea; + ::unotools::WeakReference mpCurrentPage; + bool mbMasterPageMode; + bool mbLayerMode; + + /** This flag indicates whether the called DrawController is being + disposed or already has been disposed. + */ + bool mbDisposing; + + ::std::unique_ptr< ::cppu::IPropertyArrayHelper> mpPropertyArrayHelper; + + /** The current sub controller. May be NULL. + */ + css::uno::Reference mxSubController; + + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxConfigurationController; + css::uno::Reference< + css::drawing::framework::XModuleController> mxModuleController; + + /** Send an event to all relevant property listeners that a + property has changed its value. The fire() method of the + OPropertySetHelper is wrapped by this method to handle + exceptions thrown by called listeners. + */ + void FirePropertyChange ( + sal_Int32 nHandle, + const css::uno::Any& rNewValue, + const css::uno::Any& rOldValue); + + void ProvideFrameworkControllers(); + void DisposeFrameworkControllers(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DrawDocShell.hxx b/sd/source/ui/inc/DrawDocShell.hxx new file mode 100644 index 000000000..15fa5ebd4 --- /dev/null +++ b/sd/source/ui/inc/DrawDocShell.hxx @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include "fupoor.hxx" + +class FontList; +class SdDrawDocument; +class SdPage; +class SfxPrinter; +struct SpellCallbackInfo; +class AbstractSvxNameDialog; +class SfxUndoManager; + +namespace sd { + +class FrameView; +class ViewShell; +class DrawViewShell; + +// DrawDocShell +class SD_DLLPUBLIC DrawDocShell : public SfxObjectShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDDRAWDOCSHELL) + SFX_DECL_OBJECTFACTORY(); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + DrawDocShell ( + SfxObjectCreateMode eMode, + bool bSdDataObj, + DocumentType); + + DrawDocShell ( + SfxModelFlags nModelCreationFlags, + bool bSdDataObj, + DocumentType); + + DrawDocShell ( + SdDrawDocument* pDoc, + SfxObjectCreateMode eMode, + bool bSdDataObj, + DocumentType); + virtual ~DrawDocShell() override; + + void UpdateRefDevice(); + virtual void Activate( bool bMDI ) override; + virtual void Deactivate( bool bMDI ) override; + virtual bool InitNew( const css::uno::Reference< css::embed::XStorage >& xStorage ) override; + virtual bool ImportFrom(SfxMedium &rMedium, + css::uno::Reference const& xInsertPosition) + override; + virtual bool ConvertFrom( SfxMedium &rMedium ) override; + virtual bool Save() override; + virtual bool SaveAsOwnFormat( SfxMedium& rMedium ) override; + virtual bool ConvertTo( SfxMedium &rMedium ) override; + virtual bool SaveCompleted( const css::uno::Reference< css::embed::XStorage >& xStorage ) override; + + virtual bool Load( SfxMedium &rMedium ) override; + virtual bool LoadFrom( SfxMedium& rMedium ) override; + virtual bool SaveAs( SfxMedium &rMedium ) override; + + virtual ::tools::Rectangle GetVisArea(sal_uInt16 nAspect) const override; + virtual void Draw(OutputDevice*, const JobSetup& rSetup, sal_uInt16 nAspect) override; + virtual SfxUndoManager* GetUndoManager() override; + virtual Printer* GetDocumentPrinter() override; + virtual void OnDocumentPrinterChanged(Printer* pNewPrinter) override; + virtual SfxStyleSheetBasePool* GetStyleSheetPool() override; + virtual void FillClass(SvGlobalName* pClassName, SotClipboardFormatId* pFormat, OUString* pFullTypeName, sal_Int32 nFileFormat, bool bTemplate = false ) const override; + virtual void SetModified( bool = true ) override; + virtual std::shared_ptr CreateDocumentInfoDialog(weld::Window* pParent, + const SfxItemSet &rSet) override; + + using SfxObjectShell::GetVisArea; + using SfxShell::GetViewShell; + + sd::ViewShell* GetViewShell() { return mpViewShell; } + ::sd::FrameView* GetFrameView(); + + SdDrawDocument* GetDoc() { return mpDoc;} + DocumentType GetDocumentType() const { return meDocType; } + + SfxPrinter* GetPrinter(bool bCreate); + void SetPrinter(SfxPrinter *pNewPrinter); + void UpdateFontList(); + + bool IsInDestruction() const { return mbInDestruction; } + + void CancelSearching(); + + void Execute( SfxRequest& rReq ); + void GetState(SfxItemSet&); + + void Connect(sd::ViewShell* pViewSh); + void Disconnect(sd::ViewShell const * pViewSh); + void UpdateTablePointers(); + + void GotoBookmark(std::u16string_view rBookmark); + + BitmapEx GetPagePreviewBitmap(SdPage* pPage); + + /** checks, if the given name is a valid new name for a slide + +

If the name is invalid, an SvxNameDialog pops up that + queries again for a new name until it is ok or the user chose + Cancel.

+ + @param pWin is necessary to pass to the SvxNameDialog in + case an invalid name was entered. + @param rName the new name that is to be set for a slide. This string + may be set to an empty string (see below). + + @return sal_True, if the new name is unique. Note that if the user entered + a default name of a not-yet-existing slide (e.g. 'Slide 17'), + sal_True is returned, but rName is set to an empty string. + */ + bool CheckPageName(weld::Window* pWin, OUString& rName ); + + void SetSlotFilter(bool bEnable = false, o3tl::span pSIDs = o3tl::span()) { mbFilterEnable = bEnable; mpFilterSIDs = pSIDs; } + void ApplySlotFilter() const; + + SfxStyleFamily GetStyleFamily() const { return mnStyleFamily; } + void SetStyleFamily( SfxStyleFamily nSF ) { mnStyleFamily = nSF; } + + /** executes the SID_OPENDOC slot to let the framework open a document + with the given URL and this document as a referer */ + void OpenBookmark( const OUString& rBookmarkURL ); + + /** checks, if the given name is a valid new name for a slide + +

This method does not pop up any dialog (like CheckPageName).

+ + @param rInOutPageName the new name for a slide that is to be renamed. + This string will be set to an empty string if + bResetStringIfStandardName is true and the name is of the + form of any, possibly not-yet existing, standard slide + (e.g. 'Slide 17') + + @param bResetStringIfStandardName if true allows setting rInOutPageName + to an empty string, which returns true and implies that the + slide will later on get a new standard name (with a free + slide number). + + @return true, if the new name is unique. If bResetStringIfStandardName + is true, the return value is also true, if the slide name is + a standard name (see above) + */ + bool IsNewPageNameValid( OUString & rInOutPageName, bool bResetStringIfStandardName = false ); + + /** checks, if the given name is a *unique* name for an *existing* slide + + @param rPageName the name of an existing slide + + @return true, if the name is unique and the slide exists + */ + bool IsPageNameUnique(std::u16string_view rPagName) const; + + /** Return the reference device for the current document. When the + inherited implementation returns a device then this is passed to the + caller. Otherwise the returned value depends on the printer + independent layout mode and will usually be either a printer or a + virtual device used for screen rendering. + @return + Returns NULL when the current document has no reference device. + */ + virtual OutputDevice* GetDocumentRefDev() override; + + DECL_DLLPRIVATE_LINK( RenameSlideHdl, AbstractSvxNameDialog&, bool ); + + // ExecuteSpellPopup now handled by DrawDocShell + DECL_DLLPRIVATE_LINK( OnlineSpellCallback, SpellCallbackInfo&, void ); + + void ClearUndoBuffer(); + + std::vector GetThemeColors() override; + +protected: + + SdDrawDocument* mpDoc; + std::unique_ptr mpUndoManager; + VclPtr mpPrinter; + ::sd::ViewShell* mpViewShell; + std::unique_ptr mpFontList; + DocumentType meDocType; + SfxStyleFamily mnStyleFamily; + o3tl::span + mpFilterSIDs; + bool mbFilterEnable; + bool mbSdDataObj; + bool mbInDestruction; + bool mbOwnPrinter; + + bool mbOwnDocument; // if true, we own mpDoc and will delete it in our d'tor + void Construct(bool bClipboard); +private: + static void setEditMode(DrawViewShell* pDrawViewShell, bool isMasterPage); +}; + +#ifndef SV_DECL_DRAW_DOC_SHELL_DEFINED +#define SV_DECL_DRAW_DOC_SHELL_DEFINED +typedef ::tools::SvRef DrawDocShellRef; +#endif + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DrawSubController.hxx b/sd/source/ui/inc/DrawSubController.hxx new file mode 100644 index 000000000..d748d6378 --- /dev/null +++ b/sd/source/ui/inc/DrawSubController.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd { + + class DrawSubControllerInterfaceBase : public ::cppu::WeakComponentImplHelper< + css::drawing::XDrawSubController, + css::lang::XServiceInfo > + { + public: + DrawSubControllerInterfaceBase( ::osl::Mutex& aMutex ) + : ::cppu::WeakComponentImplHelper< + css::drawing::XDrawSubController, + css::lang::XServiceInfo >( aMutex ) {} + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override = 0; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override = 0; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override = 0; + }; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/DrawViewShell.hxx b/sd/source/ui/inc/DrawViewShell.hxx new file mode 100644 index 000000000..c56a0f33e --- /dev/null +++ b/sd/source/ui/inc/DrawViewShell.hxx @@ -0,0 +1,513 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include "ViewShell.hxx" +#include "tools/AsynchronousCall.hxx" +#include "TabControl.hxx" +#include +#include +#include +#include +#include + +namespace svx::sidebar { class SelectionChangeHandler; } +namespace com::sun::star::lang { class XEventListener; } +namespace com::sun::star::scanner { class XScannerManager2; } + +class Outliner; +class SdPage; +class SdStyleSheet; +class SdrExternalToolEdit; +class TabBar; +class SdrObject; +class SdrPageView; +class TransferableDataHelper; +class TransferableClipboardListener; +class AbstractSvxNameDialog; +class SdrLayer; +class SvxClipboardFormatItem; +struct ESelection; +class AbstractSvxObjectNameDialog; + +namespace sd { + +class DrawView; +class LayerTabBar; +class Ruler; +class AnnotationManager; +class ViewOverlayManager; + +#define CHECK_RANGE(nMin, nValue, nMax) ((nValue >= nMin) && (nValue <= nMax)) + +/** Base class of the stacked shells that provide graphical views to + Draw and Impress documents and editing functionality. In contrast + to this other stacked shells are responsible for showing an + overview over several slides or a textual + overview over the text in an Impress document (OutlineViewShell). +*/ +class SAL_DLLPUBLIC_RTTI DrawViewShell + : public ViewShell, + public SfxListener, + public utl::ConfigurationListener +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDDRAWVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + /** Create a new stackable shell that may take some information + (e.g. the frame view) from the given previous shell. + @param ePageKind + This parameter gives the initial page kind that the new shell + will show. + @param pFrameView + The frame view that makes it possible to pass information from + one view shell to the next. + */ + DrawViewShell ( + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + PageKind ePageKind, + FrameView* pFrameView); + + virtual ~DrawViewShell() override; + + virtual void Init (bool bIsMainViewShell) override; + + virtual void Shutdown() override; + + void PrePaint() override; + virtual void Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) override; + + /** Arrange and resize the GUI elements like rulers, sliders, and + buttons as well as the actual document view according to the size of + the enclosing window and current sizes of buttons, rulers, and + sliders. + */ + virtual void ArrangeGUIElements() override; + + void HidePage(); + + virtual bool KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin) override; + virtual void MouseMove(const MouseEvent& rMEvt, ::sd::Window* pWin) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt, ::sd::Window* pWin) override; + virtual void Command(const CommandEvent& rCEvt, ::sd::Window* pWin) override; + bool IsMouseButtonDown() const { return mbMouseButtonDown; } + bool IsMouseSelecting() const { return mbMouseSelecting; } + + virtual void Resize() override; + + void ShowMousePosInfo(const ::tools::Rectangle& rRect, ::sd::Window const * pWin); + + virtual void ChangeEditMode (EditMode eMode, bool bIsLayerModeActive); + + virtual void SetZoom( ::tools::Long nZoom ) override; + virtual void SetZoomRect( const ::tools::Rectangle& rZoomRect ) override; + + void InsertURLField(const OUString& rURL, const OUString& rText, const OUString& rTarget); + void InsertURLButton(const OUString& rURL, const OUString& rText, const OUString& rTarget, + const Point* pPos); + + void SelectionHasChanged(); + void ModelHasChanged(); + virtual void Activate(bool bIsMDIActivate) override; + virtual void Deactivate(bool IsMDIActivate) override; + virtual void UIActivating( SfxInPlaceClient* ) override; + virtual void UIDeactivated( SfxInPlaceClient* ) override; + OUString GetSelectionText( bool bCompleteWords ); + bool HasSelection( bool bText ) const; + + //If we are editing a PresObjKind::Outline return the Outliner and fill rSel + //with the current selection + ::Outliner* GetOutlinerForMasterPageOutlineTextObj(ESelection &rSel); + + void ExecCtrl(SfxRequest& rReq); + void GetCtrlState(SfxItemSet& rSet); + void GetDrawAttrState(SfxItemSet& rSet); + void GetMenuState(SfxItemSet& rSet); + void GetTableMenuState(SfxItemSet& rSet); + /** Set the items of the given item set that are related to + switching the editing mode to the correct values. +

This function also sets the states of the mode buttons + (those at the upper right corner) accordingly.

+ */ + void GetModeSwitchingMenuState (SfxItemSet &rSet); + void GetAttrState(SfxItemSet& rSet); + void GetSnapItemState(SfxItemSet& rSet); + + void SetPageProperties (SfxRequest& rReq); + void GetPageProperties(SfxItemSet& rSet); + void GetMarginProperties(SfxItemSet& rSet); + + void GetState (SfxItemSet& rSet); + void Execute (SfxRequest& rReq); + + void ExecStatusBar(SfxRequest& rReq); + void GetStatusBarState(SfxItemSet& rSet); + + void ExecOptionsBar(SfxRequest& rReq); + void GetOptionsBarState(SfxItemSet& rSet); + + void ExecRuler(SfxRequest& rReq); + void GetRulerState(SfxItemSet& rSet); + + void ExecFormText(SfxRequest& rReq); + void GetFormTextState(SfxItemSet& rSet); + + void ExecAnimationWin(SfxRequest& rReq); + void GetAnimationWinState(SfxItemSet& rSet); + + void ExecNavigatorWin(SfxRequest& rReq); + void GetNavigatorWinState(SfxItemSet& rSet); + + void ExecutePropPanelAttr (SfxRequest const & rReq); + void GetStatePropPanelAttr(SfxItemSet& rSet); + + void ExecEffectWin(SfxRequest& rReq); + + void Update3DWindow(); + void AssignFrom3DWindow(); + + void ExecGallery(SfxRequest const & rReq); + + void ExecBmpMask( SfxRequest const & rReq ); + void GetBmpMaskState( SfxItemSet& rSet ); + + void ExecIMap( SfxRequest const & rReq ); + void GetIMapState( SfxItemSet& rSet ); + + void FuTemporary(SfxRequest& rReq); + void FuPermanent(SfxRequest& rReq); + void FuSupport(SfxRequest& rReq); + void FuDeleteSelectedObjects(); + void FuSupportRotate(SfxRequest const & rReq); + void FuTable(SfxRequest& rReq); + + void AttrExec (SfxRequest& rReq); + void AttrState (SfxItemSet& rSet); + + void ExecGoToNextPage (SfxRequest& rReq); + void GetStateGoToNextPage (SfxItemSet& rSet); + + void ExecGoToPreviousPage (SfxRequest& rReq); + void GetStateGoToPreviousPage (SfxItemSet& rSet); + + void ExecGoToFirstPage (SfxRequest& rReq); + void GetStateGoToFirstPage (SfxItemSet& rSet); + + void ExecGoToLastPage (SfxRequest& rReq); + void GetStateGoToLastPage (SfxItemSet& rSet); + + SD_DLLPUBLIC void ExecChar(SfxRequest& rReq); + + void ExecuteAnnotation (SfxRequest const & rRequest); + void GetAnnotationState (SfxItemSet& rItemSet); + + void StartRulerDrag (const Ruler& rRuler, const MouseEvent& rMEvt); + + virtual bool PrepareClose( bool bUI = true ) override; + + PageKind GetPageKind() const { return mePageKind; } + void SetPageKind( PageKind ePageKind ) { mePageKind = ePageKind; } + const Point& GetMousePos() const { return maMousePos; } + + EditMode GetEditMode() const { return meEditMode; } + virtual SdPage* GetActualPage() override { return mpActualPage; } + + /// inherited from sd::ViewShell + virtual SdPage* getCurrentPage() const override; + + void ResetActualPage(); + void ResetActualLayer(); + bool SwitchPage(sal_uInt16 nPage, bool bAllowChangeFocus = true); + bool IsSwitchPageAllowed() const; + + /** + * Mark the desired page as selected (1), deselected (0), toggle (2). + * nPage refers to the page in question. + */ + bool SelectPage(sal_uInt16 nPage, sal_uInt16 nSelect); + bool IsSelected(sal_uInt16 nPage); + bool IsVisible(sal_uInt16 nPage); + + void GotoBookmark(std::u16string_view rBookmark); + //Realize multi-selection of objects, If object is marked, the + //corresponding entry is set true, else the corresponding entry is set + //false. + void FreshNavigatrTree(); + void MakeVisible(const ::tools::Rectangle& rRect, vcl::Window& rWin); + + virtual void ReadFrameViewData(FrameView* pView) override; + virtual void WriteFrameViewData() override; + + virtual ErrCode DoVerb(sal_Int32 nVerb) override; + virtual bool ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb) override; + + void SetZoomOnPage( bool bZoom ) { mbZoomOnPage = bZoom; } + bool IsZoomOnPage() const { return mbZoomOnPage; } + static void CheckLineTo (SfxRequest& rReq); + void SetChildWindowState( SfxItemSet& rSet ); + + void UpdateIMapDlg( SdrObject* pObj ); + + void LockInput(); + void UnlockInput(); + bool IsInputLocked() const { return mnLockCount > 0; } + + sal_uInt16 GetCurPagePos() const { return maTabControl->GetCurPagePos(); } + + /** Show controls of the UI or hide them, depending on the given flag. + Do not call this method directly. Call the method at ViewShellBase + instead. + */ + virtual void ShowUIControls (bool bVisible) override; + + void ScannerEvent(); + + bool IsLayerModeActive() const { return mbIsLayerModeActive;} + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt, DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt, DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ) override; + + virtual void WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& ) override; + virtual void ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& ) override; + + virtual void VisAreaChanged(const ::tools::Rectangle& rRect) override; + + /** Create an accessible object representing the specified window. + @param pWindow + The returned object makes the document displayed in this window + accessible. + @return + Returns an AccessibleDrawDocumentView object. + */ + virtual css::uno::Reference + CreateAccessibleDocumentView (::sd::Window* pWindow) override; + + /** Return the number of layers managed by the layer tab control. This + will usually differ from the number of layers managed by the layer + administrator. + @return + The number of layers managed by the layer tab control. The + returned value is independent of whether the layer mode is + currently active and the tab control is visible. + */ + int GetTabLayerCount() const; + + /** Return the numerical id of the currently active layer as seen by the + layer tab control. + @return + The returned id is a number between zero (inclusive) and the + number of layers as returned by the + GetTabLayerCount method (exclusive). + */ + int GetActiveTabLayerIndex() const; + + /** Set the active layer at the layer tab control and update the control + accordingly to reflect the change on screen. + @param nId + The id is expected to be a number between zero (inclusive) and + the number of layers as returned by the + GetTabLayerCount method (exclusive). Note that + Invalid values are ignored. No exception is thrown in that case. + */ + void SetActiveTabLayerIndex (int nId); + + /** Return a pointer to the tab control for pages. + */ + TabControl& GetPageTabControl() { return *maTabControl; } + + /** Return a pointer to the tab control for layers. + */ + SD_DLLPUBLIC LayerTabBar* GetLayerTabControl(); // export for unit test + + /** Renames the given slide using an SvxNameDialog + + @param nPageId the index of the page in the SdTabControl. + @param rName the new name of the slide. + + @return false, if the new name is invalid for some reason. + +

Implemented in drviews8.cxx.

+ */ + bool RenameSlide( sal_uInt16 nPageId, const OUString & rName ); + + /** modifies the given layer with the given values */ + void ModifyLayer( SdrLayer* pLayer, const OUString& rLayerName, const OUString& rLayerTitle, const OUString& rLayerDesc, bool bIsVisible, bool bIsLocked, bool bIsPrintable ); + + virtual css::uno::Reference CreateSubController() override; + + DrawView* GetDrawView() const { return mpDrawView.get(); } + + /** Relocation to a new parent window is not supported for DrawViewShell + objects so this method always returns . + */ + virtual bool RelocateToParentWindow (vcl::Window* pParentWindow) override; + + OUString const & GetSidebarContextName() const; + + bool IsInSwitchPage() const { return mbIsInSwitchPage; } + + //move this method to ViewShell. + //void NotifyAccUpdate(); +protected: + std::unique_ptr mpDrawView; + SdPage* mpActualPage; + ::tools::Rectangle maMarkRect; + Point maMousePos; + VclPtr maTabControl; + EditMode meEditMode; + PageKind mePageKind; + // tdf#137445 at context menu popup time set if the EditHyperlink entry + // should be disabled and use that state if queried about it if + // EditHyperlink is dispatched from the menu. So ignoring where the mouse + // currently happens to be when the menu was dismissed. + std::optional moAtContextMenu_DisableEditHyperlink; + bool mbZoomOnPage; + bool mbIsRulerDrag; + sal_uLong mnLockCount; + bool mbReadOnly; + static bool mbPipette; + + DECL_DLLPRIVATE_LINK( ClipboardChanged, TransferableDataHelper*, void ); + DECL_DLLPRIVATE_LINK( TabSplitHdl, TabBar *, void ); + DECL_DLLPRIVATE_LINK( NameObjectHdl, AbstractSvxObjectNameDialog&, bool ); + DECL_DLLPRIVATE_LINK( RenameSlideHdl, AbstractSvxNameDialog&, bool ); + + void DeleteActualPage(); + void DeleteActualLayer(); + + virtual VclPtr CreateHRuler(::sd::Window* pWin) override; + virtual VclPtr CreateVRuler(::sd::Window* pWin) override; + virtual void UpdateHRuler() override; + virtual void UpdateVRuler() override; + virtual void SetZoomFactor(const Fraction& rZoomX, const Fraction& rZoomY) override; + + void SetupPage( Size const &rSize, ::tools::Long nLeft, ::tools::Long nRight, ::tools::Long nUpper, ::tools::Long nLower, + bool bSize, bool bMargin, bool bScaleAll ); + + void GetMenuStateSel(SfxItemSet& rSet); + +private: + /** This flag controls whether the layer mode is active, i.e. the layer + dialog is visible. + */ + bool mbIsLayerModeActive; + + /** This item contains the clipboard formats of the current clipboard + content that are supported both by that content and by the + DrawViewShell. + */ + ::std::unique_ptr mpCurrentClipboardFormats; + + /** On some occasions it is necessary to make SwitchPage calls + asynchronously. + */ + tools::AsynchronousCall maAsynchronousSwitchPageCall; + + /** This flag is used to prevent nested calls to SwitchPage(). + */ + bool mbIsInSwitchPage; + + RotateTransliteration m_aRotateCase; + + /** Listen for selection changes and broadcast context changes for the sidebar. + */ + ::rtl::Reference mpSelectionChangeHandler; + + void Construct (DrawDocShell* pDocSh, PageKind ePageKind); + + /** Depending on the given request create a new page or duplicate an + existing one. See ViewShell::CreateOrDuplicatePage() for more + information. + */ + virtual SdPage* CreateOrDuplicatePage ( + SfxRequest& rRequest, + PageKind ePageKind, + SdPage* pPage, + const sal_Int32 nInsertPosition = -1) override; + + void DuplicateSelectedSlides (SfxRequest& rRequest); + + css::uno::Reference< css::scanner::XScannerManager2 > mxScannerManager; + css::uno::Reference< css::lang::XEventListener > mxScannerListener; + rtl::Reference mxClipEvtLstnr; + bool mbPastePossible; + bool mbMouseButtonDown; + bool mbMouseSelecting; + + virtual void Notify (SfxBroadcaster& rBC, const SfxHint& rHint) override; + + /** Stop a running slide show. + */ + void StopSlideShow(); + + /** Show the context menu for snap lines and points. Because snap lines + can not be selected the index of the snap line/point for which the + popup menu is opened has to be passed to the processing slot + handlers. This can be done only by manually showing the popup menu. + @param pParent + The parent for the context menu. + @param rRect + The location at which to display the context menu. + @param rPageView + The page view is used to access the help lines. + @param nSnapLineIndex + Index of the snap line or snap point for which to show the + context menu. + */ + void ShowSnapLineContextMenu(weld::Window* pParent, const ::tools::Rectangle& rRect, + SdrPageView& rPageView, const sal_uInt16 nSnapLineIndex); + + using ViewShell::Notify; + + ::std::unique_ptr< AnnotationManager > mpAnnotationManager; + ::std::unique_ptr< ViewOverlayManager > mpViewOverlayManager; + + std::vector> m_ExternalEdits; + + virtual void ConfigurationChanged( utl::ConfigurationBroadcaster* pCb, ConfigurationHints ) override; + + void ConfigureAppBackgroundColor( svtools::ColorConfig* pColorConfig = nullptr ); + + /// return true if "Edit Hyperlink" in context menu should be disabled + bool ShouldDisableEditHyperlink() const; + /// force "Edit Hyperlink" to true, with the expectation that SID_EDIT_HYPERLINK is + /// later Invalidated to reset it back to its natural value + void EnableEditHyperlink(); + + // The colour of the area behind the slide (used to be called "Wiese") + Color mnAppBackgroundColor; +}; + + /// Merge the background properties together and deposit the result in rMergeAttr + void MergePageBackgroundFilling(SdPage *pPage, SdStyleSheet *pStyleSheet, bool bMasterPage, SfxItemSet& rMergedAttr); + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/EventMultiplexer.hxx b/sd/source/ui/inc/EventMultiplexer.hxx new file mode 100644 index 000000000..d6d79d11b --- /dev/null +++ b/sd/source/ui/inc/EventMultiplexer.hxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +template class Link; + +namespace sd +{ +class ViewShellBase; +} + +enum class EventMultiplexerEventId +{ + /** The EventMultiplexer itself is being disposed. Called for a live + EventMultiplexer. Removing a listener as response is not necessary, + though. + */ + Disposing, + + /** The selection in the center pane has changed. + */ + EditViewSelection, + + /** The selection in the slide sorter has changed, regardless of whether + the slide sorter is displayed in the left pane or the center pane. + */ + SlideSortedSelection, + + /** The current page has changed. + */ + CurrentPageChanged, + + /** The current MainViewShell (the ViewShell displayed in the center + pane) has been removed. + */ + MainViewRemoved, + + /** A new ViewShell has been made the MainViewShell. + */ + MainViewAdded, + + /** A new ViewShell is being displayed in one of the panes. Note that + for the ViewShell in the center pane both this event type and + EventId::MainViewAdded is broadcasted. + */ + ViewAdded, + + /** Edit mode was (or is being) switched to normal mode. Find + EventId::EditModeMaster below. + */ + EditModeNormal, + + /** One or more pages have been inserted into or deleted from the model. + */ + PageOrder, + + /** Text editing in one of the shapes in the MainViewShell has started. + */ + BeginTextEdit, + + /** Text editing in one of the shapes in the MainViewShell has ended. + */ + EndTextEdit, + + /** A UNO controller has been attached to the UNO frame. + */ + ControllerAttached, + + /** A UNO controller has been detached to the UNO frame. + */ + ControllerDetached, + + /** The state of a shape has changed. The page is available in the user data. + */ + ShapeChanged, + + /** A shape has been inserted to a page. The page is available in the + user data. + */ + ShapeInserted, + + /** A shape has been removed from a page. The page is available in the + user data. + */ + ShapeRemoved, + + /** A configuration update has been completed. + */ + ConfigurationUpdated, + + /** Edit mode was (or is being) switched to master mode. + */ + EditModeMaster, +}; + +namespace sd::tools +{ +class EventMultiplexerEvent +{ +public: + EventMultiplexerEventId meEventId; + const void* mpUserData; + + EventMultiplexerEvent(EventMultiplexerEventId eEventId, const void* pUserData); +}; + +/** This convenience class makes it easy to listen to various events that + originally are broadcasted via different channels. + + There is usually one EventMultiplexer instance per ViewShellBase(). + Call the laters GetEventMultiplexer() method to get access to that + instance. +*/ +class EventMultiplexer +{ +public: + /** Create new EventMultiplexer for the given ViewShellBase object. + */ + EventMultiplexer(ViewShellBase& rBase); + ~EventMultiplexer(); + + /** Add an event listener that will be informed about the specified + event types. + @param rCallback + The callback to call as soon as one of the event specified by + aEventTypeSet is received by the EventMultiplexer. + */ + void AddEventListener(const Link& rCallback); + + /** Remove an event listener for the specified event types. + */ + void RemoveEventListener(const Link& rCallback); + + /** This method is used for out-of-line events. An event of the + specified type will be sent to all listeners that are registered for + that type. + @param eEventId + The type of the event. + @param pUserData + Some data sent to the listeners along with the event. + */ + void MultiplexEvent(EventMultiplexerEventId eEventId, void const* pUserData); + +private: + class Implementation; + rtl::Reference mpImpl; +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/FormShellManager.hxx b/sd/source/ui/inc/FormShellManager.hxx new file mode 100644 index 000000000..b2c03b3de --- /dev/null +++ b/sd/source/ui/inc/FormShellManager.hxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ViewShellManager.hxx" + +#include +#include +#include + +class VclWindowEvent; +class FmFormShell; +namespace vcl +{ +class Window; +} + +namespace sd::tools +{ +class EventMultiplexerEvent; +} + +namespace sd +{ +class ViewShellBase; + +/** This simple class is responsible for putting the form shell above or + below the main view shell on the shell stack maintained by the ObjectBarManager. + + The form shell is moved above the view shell when the form shell is + activated, i.e. the FormControlActivated handler is called. + + It is moved below the view shell when the main window of the + main view shell is focused. + + The form shell is created and destroyed by the ViewShellManager by using + a factory object provided by the FormShellManager. +*/ +class FormShellManager : public SfxListener +{ +public: + FormShellManager(ViewShellBase& rBase); + virtual ~FormShellManager() override; + + /** Typically called by a ShellFactory. It tells the + FormShellManager which form shell to manage. + @param pFormShell + This may be to disconnect the ViewShellManager from the + form shell. + */ + void SetFormShell(FmFormShell* pFormShell); + + /** Return the form shell last set with SetFormShell(). + @return + The result may be when the SetFormShell() method has not + yet been called or was last called with . + */ + FmFormShell* GetFormShell() { return mpFormShell; } + +private: + ViewShellBase& mrBase; + + /** Ownership of the form shell lies with the ViewShellManager. This + reference is kept so that the FormShellManager can detect when a new + form shell is passed to SetFormShell(). + */ + FmFormShell* mpFormShell; + + /** Remember whether the form shell is currently above or below the main + view shell. + */ + bool mbFormShellAboveViewShell; + + /** The factory is remembered so that it removed from the + ViewShellManager when the FormShellManager is destroyed. + */ + ViewShellManager::SharedShellFactory mpSubShellFactory; + + bool mbIsMainViewChangePending; + + VclPtr mpMainViewShellWindow; + + /** Register at window of center pane and at the form shell that + represents the form tool bar. The former informs this manager about + the deselection of the form shell. The later informs about its + selection. + */ + void RegisterAtCenterPane(); + + /** Unregister the listeners that were registered in + RegisterAtCenterPane(). + */ + void UnregisterAtCenterPane(); + + /** This call back is called by the application window (among others) + when the window gets the focus. In this case the form shell is + moved to the bottom of the shell stack. + */ + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + + /** This call back is called when view in the center pane is replaced. + When this happens then we unregister at the window of the old and + register at the window of the new shell. + */ + DECL_LINK(ConfigurationUpdateHandler, ::sd::tools::EventMultiplexerEvent&, void); + + /** This call back is called by the form shell when it gets the focus. + In this case the form shell is moved to the top of the shell stack. + */ + DECL_LINK(FormControlActivated, LinkParamNone*, void); + + /** This method is called by the form shell when that is destroyed. It + acts as a last resort against referencing a dead form shell. With + the factory working properly this method should not be necessary + (and may be removed in the future.) + */ + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/FrameView.hxx b/sd/source/ui/inc/FrameView.hxx new file mode 100644 index 000000000..8226746a3 --- /dev/null +++ b/sd/source/ui/inc/FrameView.hxx @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ViewShell.hxx" +#include +#include + +class SdDrawDocument; +class SdOptions; + +namespace sd { + +/** + * View for MDIFrame + */ +class SD_DLLPUBLIC FrameView + : public SdrView +{ +public: + FrameView(SdDrawDocument* pDrawDoc, FrameView* pFrameView = nullptr ); + FrameView(const FrameView& rFrameView); + virtual ~FrameView() override; + + void Connect(); + void Disconnect(); + + void Update(SdOptions const * pOptions); + + void SetStandardHelpLines(const SdrHelpLineList& rHelpLines) + { maStandardHelpLines = rHelpLines; } + const SdrHelpLineList& GetStandardHelpLines() const { return maStandardHelpLines; } + void SetNotesHelpLines(const SdrHelpLineList& rHelpLines) + { maNotesHelpLines = rHelpLines; } + const SdrHelpLineList& GetNotesHelpLines() const { return maNotesHelpLines; } + void SetHandoutHelpLines(const SdrHelpLineList& rHelpLines) + { maHandoutHelpLines = rHelpLines; } + const SdrHelpLineList& GetHandoutHelpLines() const { return maHandoutHelpLines; } + + void SetVisibleLayers(const SdrLayerIDSet& rVisibleLayers) + { maVisibleLayers = rVisibleLayers; } + const SdrLayerIDSet& GetVisibleLayers() const { return maVisibleLayers; } + + void SetLockedLayers(const SdrLayerIDSet& rLockedLayers) + { maLockedLayers = rLockedLayers; } + const SdrLayerIDSet& GetLockedLayers() const { return maLockedLayers; } + + void SetPrintableLayers(const SdrLayerIDSet& rPrintableLayers) + { maPrintableLayers = rPrintableLayers; } + const SdrLayerIDSet& GetPrintableLayers() const { return maPrintableLayers; } + + void SetRuler(const bool bRulerOn) + { mbRuler = bRulerOn; } + bool HasRuler() const { return mbRuler; } + + void SetNoColors(const bool bNoCol) + { mbNoColors = bNoCol; } + bool IsNoColors() const { return mbNoColors; } + + void SetNoAttribs(const bool bNoAttr) + { mbNoAttribs = bNoAttr; } + bool IsNoAttribs() const { return mbNoAttribs; } + + void SetVisArea(const ::tools::Rectangle& rVisArea) + { maVisArea = rVisArea; } + const ::tools::Rectangle& GetVisArea() const { return maVisArea; } + + void SetPageKind(PageKind eKind) { mePageKind = eKind; } + PageKind GetPageKind() const { return mePageKind; } + + /** is used in FrameView::ReadUserDataSequence() only to store the + page kind that was selected while last saving this document */ + void SetPageKindOnLoad(PageKind eKind) { mePageKindOnLoad = eKind; } + + /** can be used to get the page kind that was selected on last save of this document */ + PageKind GetPageKindOnLoad() const { return mePageKindOnLoad; } + + void SetSelectedPage (sal_uInt16 nPage); + sal_uInt16 GetSelectedPage () const { return mnSelectedPage;} + + /** is used in FrameView::ReadUserDataSequence() only to store the + page that was selected while last saving this document */ + void SetSelectedPageOnLoad (sal_uInt16 nPage) { mnSelectedPageOnLoad = nPage; } + + /** can be used to get the page that was selected on last save of this document */ + sal_uInt16 GetSelectedPageOnLoad () const { return mnSelectedPageOnLoad; } + + void SetViewShEditMode(EditMode eMode); + EditMode GetViewShEditMode () const; + + /** Remember the edit mode of the main view shell at the time when the + document is loaded. + */ + void SetViewShEditModeOnLoad (const EditMode eMode); + + /** Return the value of the edit mode as it was when the document was + loaded. + */ + EditMode GetViewShEditModeOnLoad() const { return meEditModeOnLoad;} + + void SetLayerMode(bool bMode) + { mbLayerMode = bMode; } + bool IsLayerMode() const { return mbLayerMode; } + + void SetQuickEdit(bool bQEdit) + { mbQuickEdit = bQEdit; } + bool IsQuickEdit() const { return mbQuickEdit; } + + void SetDoubleClickTextEdit( bool bOn ) { mbDoubleClickTextEdit = bOn; } + bool IsDoubleClickTextEdit() const { return mbDoubleClickTextEdit; } + + void SetClickChangeRotation( bool bOn ) { mbClickChangeRotation = bOn; } + bool IsClickChangeRotation() const { return mbClickChangeRotation; } + + /** Remember the type of the view shell that was (or soon will be) + previously associated with this frame view. + @param eType + The type of the previous view shell or ViewShell::ST_NONE to + indicate that there is no previous view shell. + */ + void SetPreviousViewShellType (ViewShell::ShellType eType); + + /** Return the type of the view shell previously associated with this + frame view. + */ + ViewShell::ShellType GetPreviousViewShellType() const { return mePreviousViewShellType;} + + /** Remember the type of the view shell at the time when the document is + loaded or, rather, when the ViewShellBase is constructed. + */ + void SetViewShellTypeOnLoad (ViewShell::ShellType eType); + + ViewShell::ShellType GetViewShellTypeOnLoad() const { return meViewShellTypeOnLoad;} + + void SetPresentationViewShellId(sal_uInt16 nId) + { mnPresViewShellId = nId; } + sal_uInt16 GetPresentationViewShellId() const { return mnPresViewShellId; } + + void SetSlidesPerRow(sal_uInt16 nSlides) { mnSlidesPerRow = nSlides; } + sal_uInt16 GetSlidesPerRow() const { return mnSlidesPerRow; } + + void SetDrawMode(DrawModeFlags nNewDrawMode) { mnDrawMode = nNewDrawMode; }; + DrawModeFlags GetDrawMode() const { return mnDrawMode; }; + + void SetIsNavigatorShowingAllShapes (const bool bIsNavigatorShowingAllShapes); + bool IsNavigatorShowingAllShapes() const { return mbIsNavigatorShowingAllShapes;} + + void WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& ); + void ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& ); + +private: + sal_uInt16 mnRefCount; + bool mbRuler; + SdrLayerIDSet maVisibleLayers; + SdrLayerIDSet maLockedLayers; + SdrLayerIDSet maPrintableLayers; + SdrHelpLineList maStandardHelpLines; + SdrHelpLineList maNotesHelpLines; + SdrHelpLineList maHandoutHelpLines; + bool mbNoColors; ///< structuring mode + bool mbNoAttribs; ///< structuring mode + ::tools::Rectangle maVisArea; ///< visible area + PageKind mePageKind; ///< kind of page (standard, notes, handout) + sal_uInt16 mnSelectedPage; + PageKind mePageKindOnLoad; + sal_uInt16 mnSelectedPageOnLoad; + EditMode mePageEditMode; ///< edit mode in drawing mode (Page/MasterPage) + // EditMode meStandardEditMode; ///< edit mode in drawing mode (Page/MasterPage) + // EditMode meNotesEditMode; ///< edit mode in notes mode (Page/MasterPage) + // EditMode meHandoutEditMode; ///< edit mode in handout mode (Page/MasterPage) + EditMode meEditModeOnLoad; + bool mbLayerMode; ///< layer on/off + bool mbQuickEdit; ///< QuickEdit on/off + bool mbDoubleClickTextEdit; ///< text mode after double click + bool mbClickChangeRotation; ///< single click switches between selection/rotation mode + sal_uInt16 mnPresViewShellId; ///< ViewShell from which the presentation was started + sal_uInt16 mnSlidesPerRow; ///< slides per row on the slide-desk + DrawModeFlags mnDrawMode; ///< draw mode for the normal window + /** Remember whether the navigator shows all shapes () or only + the names ones (). Not persistent. + */ + bool mbIsNavigatorShowingAllShapes; + + /** The type of the previous view shell. The (default) value + ViewShell::ST_NONE indicates that there was no previous view shell. + Note that this value is used only temporarily and is not saved or + restored. + */ + ViewShell::ShellType mePreviousViewShellType; + + ViewShell::ShellType meViewShellTypeOnLoad; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/GraphicDocShell.hxx b/sd/source/ui/inc/GraphicDocShell.hxx new file mode 100644 index 000000000..40df981cf --- /dev/null +++ b/sd/source/ui/inc/GraphicDocShell.hxx @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include "DrawDocShell.hxx" +#include +#include + +namespace sd +{ +/** + * document shell for draw documents + */ +class SD_DLLPUBLIC GraphicDocShell : public DrawDocShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDGRAPHICDOCSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + SFX_DECL_OBJECTFACTORY(); + + GraphicDocShell(SfxObjectCreateMode eMode); + + GraphicDocShell(SfxModelFlags nModelCreationFlags); + + virtual ~GraphicDocShell() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/GraphicObjectBar.hxx b/sd/source/ui/inc/GraphicObjectBar.hxx new file mode 100644 index 000000000..7d53a86d2 --- /dev/null +++ b/sd/source/ui/inc/GraphicObjectBar.hxx @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd { + +class View; +class ViewShell; + +class GraphicObjectBar final + : public SfxShell +{ +public: + SFX_DECL_INTERFACE( SD_IF_SDDRAWGRAFOBJECTBAR ) + + GraphicObjectBar (const ViewShell* pSdViewShell, ::sd::View* pSdView); + virtual ~GraphicObjectBar() override; + + void GetAttrState( SfxItemSet& rSet ); + void Execute( SfxRequest& rReq ); + + void GetFilterState( SfxItemSet& rSet ); + void ExecuteFilter( SfxRequest const & rReq ); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + ::sd::View* mpView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/GraphicViewShell.hxx b/sd/source/ui/inc/GraphicViewShell.hxx new file mode 100644 index 000000000..d730c2dd9 --- /dev/null +++ b/sd/source/ui/inc/GraphicViewShell.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "DrawViewShell.hxx" + +namespace vcl +{ +class Window; +} + +namespace sd +{ +/** View shell of the Draw application. + +

This class is an example of how not to do it: specialization by + inheritance. A graphic view shell is similar to a draw view shell + but lacks some of its features. Thus is should be at most a base + class of DrawViewShell. There even is special case code in + ViewShell that turns off some of the features for GraphicViewShell + instances.

+*/ +class SAL_DLLPUBLIC_RTTI GraphicViewShell final : public DrawViewShell +{ +public: + SFX_DECL_VIEWFACTORY(GraphicViewShell); + SFX_DECL_INTERFACE(SD_IF_SDGRAPHICVIEWSHELL) + + /** Create a new view shell for the Draw application. + @param rViewShellBase + The new object will be stacked on this view shell base. + @param pFrameView + The frame view that makes it possible to pass information from + one view shell to the next. + */ + GraphicViewShell(ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, + FrameView* pFrameView); + + virtual ~GraphicViewShell() override; + + /** Override this method in order to have the layer mode always active. + */ + virtual void ChangeEditMode(EditMode eMode, bool bIsLayerModeActive) override; + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + void ConstructGraphicViewShell(); + virtual void ArrangeGUIElements() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/GraphicViewShellBase.hxx b/sd/source/ui/inc/GraphicViewShellBase.hxx new file mode 100644 index 000000000..89a96cf51 --- /dev/null +++ b/sd/source/ui/inc/GraphicViewShellBase.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ViewShellBase.hxx" + +namespace sd +{ +/** This class exists to be able to register another factory that + creates the view shell for the Draw application. +*/ +class GraphicViewShellBase : public ViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(GraphicViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + GraphicViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~GraphicViewShellBase() override; + + /** Callback function for general slot calls. + */ + virtual void Execute(SfxRequest& rRequest) override; + +protected: + virtual void InitializeFramework() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ImpressViewShellBase.hxx b/sd/source/ui/inc/ImpressViewShellBase.hxx new file mode 100644 index 000000000..80070e7c8 --- /dev/null +++ b/sd/source/ui/inc/ImpressViewShellBase.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ViewShellBase.hxx" + +namespace sd +{ +/** This class implements a few features that exist only for the Impress + application. +*/ +class ImpressViewShellBase : public ViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(ImpressViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + ImpressViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~ImpressViewShellBase() override; + + /** Callback function for general slot calls. + */ + virtual void Execute(SfxRequest& rRequest) override; + +protected: + virtual void InitializeFramework() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/LayerTabBar.hxx b/sd/source/ui/inc/LayerTabBar.hxx new file mode 100644 index 000000000..297a9302d --- /dev/null +++ b/sd/source/ui/inc/LayerTabBar.hxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace sd { + +/** + * TabBar for layer administration + */ +class DrawViewShell; + +class SAL_DLLPUBLIC_RTTI LayerTabBar final + : public TabBar, + public DropTargetHelper +{ +public: + LayerTabBar ( + DrawViewShell* pDrViewSh, + vcl::Window* pParent); + virtual void dispose() override; + virtual ~LayerTabBar() override; + + /** Inform all listeners of this control that the current layer has been + activated. Call this method after switching the current layer and is + not done elsewhere (like when using ctrl + page up/down keys). + */ + void SendActivatePageEvent(); + + /** Inform all listeners of this control that the current layer has been + deactivated. Call this method before switching the current layer + and is not done elsewhere (like when using ctrl page up/down keys). + */ + void SendDeactivatePageEvent(); + + // Expects not-localized, real layer name in rText. Generates a localized layer name + // that will be displayed on the tab of the LayerTabBar and writes the real name + // to maAuxiliaryText. In case you want no entry in maAuxiliaryText, use method from TabBar. + virtual void InsertPage( sal_uInt16 nPageId, const OUString& rText, + TabBarPageBits nBits = TabBarPageBits::NONE, + sal_uInt16 nPos = TabBar::APPEND ) override; + virtual void SetPageText( sal_uInt16 nPageId, const OUString& rText ) override; + + // Returns the real layer name if exists and empty OUString otherwise. + OUString GetLayerName(sal_uInt16 nPageId) const; + + // Used e.g. in DeleteActualLayer() to test whether deleting is allowed. + static bool IsRealNameOfStandardLayer(std::u16string_view rName); + + // Used e.g. in validity test of user entered names + static bool IsLocalizedNameOfStandardLayer(std::u16string_view rName); + + // In case rName is one of the sUNO_LayerName_*, it generates a localized name, + // otherwise it returns value of rName. + static OUString convertToLocalizedName(const OUString& rName); + + // TabBar + virtual void Select() override; + virtual void DoubleClick() override; + + SD_DLLPUBLIC virtual void MouseButtonDown(const MouseEvent& rMEvt) override; // export for unit test + + virtual void Command(const CommandEvent& rCEvt) override; + + virtual bool StartRenaming() override; + virtual TabBarAllowRenamingReturnCode AllowRenaming() override; + virtual void EndRenaming() override; + + virtual void ActivatePage() override; + + // DropTargetHelper + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +private: + DrawViewShell* pDrViewSh; + + // Expects not-localized, real layer name in rText and writes it to maAuxiliaryText. + void SetLayerName( sal_uInt16 nPageId, const OUString& rText ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/MasterPageObserver.hxx b/sd/source/ui/inc/MasterPageObserver.hxx new file mode 100644 index 000000000..96f4a3741 --- /dev/null +++ b/sd/source/ui/inc/MasterPageObserver.hxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include "tools/SdGlobalResourceContainer.hxx" +#include +#include + +namespace osl +{ +class Mutex; +} + +class SdDrawDocument; + +namespace sd +{ +class MasterPageObserverEvent; + +/** This singleton observes all registered documents for changes in the used + master pages and in turn informs its listeners about it. One such + listener is the master page selector control in the tool panel that + shows the recently used master pages. +*/ +class MasterPageObserver : public SdGlobalResource +{ +public: + typedef ::std::set MasterPageNameSet; + + /** Return the single instance of this class. + */ + static MasterPageObserver& Instance(); + + /** The master page observer will listen to events of this document and + detect changes of the use of master pages. + */ + void RegisterDocument(SdDrawDocument& rDocument); + + /** The master page observer will stop to listen to events of this + document. + */ + void UnregisterDocument(SdDrawDocument& rDocument); + + /** Add a listener that is informed of master pages that are newly + assigned to slides or become unassigned. + @param rEventListener + The event listener to call for future events. Call + RemoveEventListener() before the listener is destroyed. + */ + void AddEventListener(const Link& rEventListener); + + /** Remove the given listener from the list of listeners. + @param rEventListener + After this method returns the given listener is not called back + from this object. Passing a listener that has not + been registered before is safe and is silently ignored. + */ + void RemoveEventListener(const Link& rEventListener); + +private: + class Implementation; + ::std::unique_ptr mpImpl; + + MasterPageObserver(); + virtual ~MasterPageObserver() override; + + MasterPageObserver(const MasterPageObserver&) = delete; + + MasterPageObserver& operator=(const MasterPageObserver&) = delete; +}; + +/** Objects of this class are sent to listeners of the MasterPageObserver + singleton when the list of master pages of one document has changed. +*/ +class MasterPageObserverEvent +{ +public: + enum EventType + { + /// Master page already exists when document is registered. + ET_MASTER_PAGE_EXISTS, + /// Master page has been added to a document. + ET_MASTER_PAGE_ADDED, + /// Master page has been removed from to a document. + ET_MASTER_PAGE_REMOVED + }; + + EventType meType; + const OUString& mrMasterPageName; + + MasterPageObserverEvent(EventType eType, const OUString& rMasterPageName) + : meType(eType) + , mrMasterPageName(rMasterPageName) + { + } +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/MediaObjectBar.hxx b/sd/source/ui/inc/MediaObjectBar.hxx new file mode 100644 index 000000000..b7c56ef00 --- /dev/null +++ b/sd/source/ui/inc/MediaObjectBar.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SfxInterface; +class SfxItemSet; +class SfxModule; +class SfxRequest; + +namespace sd { + +class View; +class ViewShell; + +class MediaObjectBar final + : public SfxShell +{ +public: + SFX_DECL_INTERFACE( SD_IF_SDDRAWMEDIAOBJECTBAR ) + + MediaObjectBar (const ViewShell* pSdViewShell, ::sd::View* pSdView); + virtual ~MediaObjectBar() override; + + void GetState( SfxItemSet& rSet ); + void Execute( SfxRequest const & rReq ); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + ::sd::View* mpView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/NavigatorChildWindow.hxx b/sd/source/ui/inc/NavigatorChildWindow.hxx new file mode 100644 index 000000000..4199cab67 --- /dev/null +++ b/sd/source/ui/inc/NavigatorChildWindow.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace vcl { class Window; } +class SfxBindings; + +namespace sd { + +class SdNavigatorWrapper final : public SfxNavigatorWrapper +{ +public: + SdNavigatorWrapper(vcl::Window *pParent, sal_uInt16 nId, + SfxBindings* pBindings, SfxChildWinInfo* pInfo); + SFX_DECL_CHILDWINDOW(SdNavigatorWrapper); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlineBulletDlg.hxx b/sd/source/ui/inc/OutlineBulletDlg.hxx new file mode 100644 index 000000000..512d45f9a --- /dev/null +++ b/sd/source/ui/inc/OutlineBulletDlg.hxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd +{ +class View; + +/** + * Bullet-Tab-Dialog + */ +class OutlineBulletDlg : public SfxTabDialogController +{ +public: + OutlineBulletDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View* pView); + virtual ~OutlineBulletDlg() override; + + const SfxItemSet* GetBulletOutputItemSet() const; + +protected: + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; + +private: + SfxItemSet m_aInputSet; + std::unique_ptr m_xOutputSet; + bool m_bTitle; + ::sd::View* m_pSdView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlineView.hxx b/sd/source/ui/inc/OutlineView.hxx new file mode 100644 index 000000000..058f6323a --- /dev/null +++ b/sd/source/ui/inc/OutlineView.hxx @@ -0,0 +1,230 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "View.hxx" + +class SdPage; +class SdrPage; +class SdrTextObj; +class SfxProgress; +struct PasteOrDropInfos; +class EditView; + +namespace sd::tools { + class EventMultiplexerEvent; +} + +namespace sd { + +class DrawDocShell; +class OutlineViewShell; +class OutlineViewModelChangeGuard; + +const int MAX_OUTLINERVIEWS = 4; + +/** + * Derivative of ::sd::View for the outline mode +|* +\************************************************************************/ + +class OutlineView + : public ::sd::View +{ + friend class OutlineViewModelChangeGuard; +public: + OutlineView (DrawDocShell& rDocSh, + vcl::Window* pWindow, + OutlineViewShell& rOutlineViewSh); + virtual ~OutlineView() override; + + /** This method is called by the view shell that owns the view to tell + the view that it can safely connect to the application. + This method must not be called before the view shell is on the shell + stack. + */ + void ConnectToApplication(); + void DisconnectFromApplication(); + + + static SdrTextObj* GetTitleTextObject(SdrPage const * pPage); + static SdrTextObj* GetOutlineTextObject(SdrPage const * pPage); + + static SdrTextObj* CreateTitleTextObject(SdPage* pPage); + static SdrTextObj* CreateOutlineTextObject(SdPage* pPage); + + virtual void AddWindowToPaintView(OutputDevice* pWin, vcl::Window* pWindow) override; + virtual void DeleteWindowFromPaintView(OutputDevice* pWin) override; + + OutlinerView* GetViewByWindow(vcl::Window const * pWin) const; + SdrOutliner& GetOutliner() { return mrOutliner; } + + Paragraph* GetPrevTitle(const Paragraph* pPara); + Paragraph* GetNextTitle(const Paragraph* pPara); + SdPage* GetActualPage(); + SdPage* GetPageForParagraph( Paragraph* pPara ); + Paragraph* GetParagraphForPage( ::Outliner const & rOutl, SdPage const * pPage ); + + /** selects the paragraph for the given page at the outliner view*/ + void SetActualPage( SdPage const * pActual ); + + void Paint (const ::tools::Rectangle& rRect, ::sd::Window const * pWin); + + // Callbacks for LINKs + DECL_LINK( ParagraphInsertedHdl, ::Outliner::ParagraphHdlParam, void ); + DECL_LINK( ParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, void ); + DECL_LINK( DepthChangedHdl, ::Outliner::DepthChangeHdlParam, void ); + DECL_LINK( StatusEventHdl, EditStatus&, void ); + DECL_LINK( BeginMovingHdl, ::Outliner *, void ); + DECL_LINK( EndMovingHdl, ::Outliner *, void ); + DECL_LINK( RemovingPagesHdl, OutlinerView *, bool ); + DECL_LINK( IndentingPagesHdl, OutlinerView *, bool ); + DECL_LINK( BeginDropHdl, EditView*, void ); + DECL_LINK( EndDropHdl, EditView*, void ); + DECL_LINK( PaintingFirstLineHdl, PaintFirstLineInfo*, void ); + + sal_uLong GetPaperWidth() const { return mnPaperWidth;} + + void PrepareClose(); + + virtual void GetAttributes( SfxItemSet& rTargetSet, bool bOnlyHardAttr = false ) const override; + virtual bool SetAttributes(const SfxItemSet& rSet, bool bReplaceAll = false, bool bSlide = false, bool bMaster = false) override; + + void FillOutliner(); + void SetLinks(); + void ResetLinks() const; + + SfxStyleSheet* GetStyleSheet() const override; + + void SetSelectedPages(); + + virtual sal_Int8 AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + SdrLayerID nLayer) override; + virtual sal_Int8 ExecuteDrop ( + const ExecuteDropEvent& rEvt, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) override; + + // Re-implement GetScriptType for this view to get correct results + virtual SvtScriptType GetScriptType() const override; + + /** After this method has been called with following changes of + the current page are ignored in that the corresponding text is not + selected. + This is used to suppress unwanted side effects between selection and + cursor position. + */ + void IgnoreCurrentPageChanges (bool bIgnore); + + /** creates and inserts an empty slide for the given paragraph. */ + SdPage* InsertSlideForParagraph( Paragraph* pPara ); + + void UpdateParagraph( sal_Int32 nPara ); + +protected: + virtual void OnBeginPasteOrDrop( PasteOrDropInfos* pInfo ) override; + virtual void OnEndPasteOrDrop( PasteOrDropInfos* pInfo ) override; + +private: + /** call this method before you do anything that can modify the outliner + and or the drawing document model. It will create needed undo actions */ + void BeginModelChange(); + + /** call this method after BeginModelChange(), when all possible model + changes are done. */ + void EndModelChange(); + + /** merge edit engine undo actions if possible */ + void TryToMergeUndoActions(); + + /** updates all changes in the outliner model to the draw model */ + void UpdateDocument(); + + OutlineViewShell& mrOutlineViewShell; + SdrOutliner& mrOutliner; + std::unique_ptr mpOutlinerViews[MAX_OUTLINERVIEWS]; + + std::vector maOldParaOrder; + std::vector maSelectedParas; + + sal_Int32 mnPagesToProcess; // for the progress bar + sal_Int32 mnPagesProcessed; + + bool mbFirstPaint; + + sal_uLong mnPaperWidth; + + std::unique_ptr mpProgress; + + /** stores the last used document color. + this is changed in onUpdateStyleSettings() + */ + Color maDocColor; + + /** updates the high contrast settings and document color if they changed. + @param bForceUpdate forces the method to set all style settings + */ + void onUpdateStyleSettings( bool bForceUpdate ); + + /** this link is called from the vcl application when the stylesettings + change. Its only purpose is to call onUpdateStyleSettings() then. + */ + DECL_LINK( AppEventListenerHdl, VclSimpleEvent&, void ); + + DECL_LINK(EventMultiplexerListener, sd::tools::EventMultiplexerEvent&, void); + + /** holds a model guard during drag and drop between BeginMovingHdl and EndMovingHdl */ + std::unique_ptr> maDragAndDropModelGuard; + + SvxLRSpaceItem maLRSpaceItem; + Image maSlideImage; +}; + +// calls IgnoreCurrentPageChangesLevel with true in ctor and with false in dtor +class OutlineViewPageChangesGuard +{ +public: + OutlineViewPageChangesGuard( OutlineView* pView ); + ~OutlineViewPageChangesGuard(); +private: + OutlineView* mpView; +}; + +// calls BeginModelChange() on c'tor and EndModelChange() on d'tor +class OutlineViewModelChangeGuard +{ +public: + OutlineViewModelChangeGuard( OutlineView& rView ); + ~OutlineViewModelChangeGuard() COVERITY_NOEXCEPT_FALSE; +private: + OutlineView& mrView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlineViewShell.hxx b/sd/source/ui/inc/OutlineViewShell.hxx new file mode 100644 index 000000000..6bc230189 --- /dev/null +++ b/sd/source/ui/inc/OutlineViewShell.hxx @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ViewShell.hxx" +#include + +class SdPage; +class TransferableDataHelper; +class TransferableClipboardListener; + +namespace sd { class OutlineView; } + +namespace sd { + +/** Show a textual overview of the text contents of all slides. +*/ +class OutlineViewShell + : public ViewShell +{ +public: + + SFX_DECL_VIEWFACTORY(OutlineViewShell); + SFX_DECL_INTERFACE(SD_IF_SDOUTLINEVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + /** Create a new view shell for the outline mode. + @param rViewShellBase + The new object will be stacked on this view shell base. + @param pFrameView + The frame view that makes it possible to pass information from + one view shell to the next. + */ + OutlineViewShell ( + SfxViewFrame* pFrame, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameView); + + virtual ~OutlineViewShell() override; + + virtual void Shutdown() override; + + virtual void Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) override; + + /** Arrange and resize the GUI elements like rulers, sliders, and + buttons as well as the actual document view according to the size of + the enclosing window and current sizes of buttons, rulers, and + sliders. + */ + virtual void ArrangeGUIElements() override; + + virtual bool PrepareClose( bool bUI = true ) override; + + virtual void VirtHScrollHdl(ScrollBar* pHScroll) override; + virtual void VirtVScrollHdl(ScrollBar* pVHScroll) override; + + virtual void Activate( bool IsMDIActivate ) override; + virtual void Deactivate( bool IsMDIActivate ) override; + + virtual SdPage* GetActualPage() override; + + /// inherited from sd::ViewShell + virtual SdPage* getCurrentPage() const override; + + void ExecCtrl(SfxRequest &rReq); + void GetCtrlState(SfxItemSet &rSet); + // FIXME non-virtual override??? + void GetMenuState(SfxItemSet &rSet); + void GetAttrState(SfxItemSet &rSet); + void GetState (SfxItemSet& rSet); + + static void ExecStatusBar(SfxRequest& rReq); + void GetStatusBarState(SfxItemSet& rSet); + + void FuTemporary(SfxRequest &rReq); + void FuTemporaryModify(SfxRequest &rReq); + void FuPermanent(SfxRequest &rReq); + void FuSupport(SfxRequest &rReq); + + virtual void SetZoom(::tools::Long nZoom) override; + virtual void SetZoomRect(const ::tools::Rectangle& rZoomRect) override; + + void Execute(SfxRequest& rReq); + + virtual void ReadFrameViewData(FrameView* pView) override; + virtual void WriteFrameViewData() override; + + virtual void Command( const CommandEvent& rCEvt, ::sd::Window* pWin ) override; + virtual bool KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) override; + + ErrCode ReadRtf(SvStream& rInput); + + virtual void WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& ) override; + virtual void ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& ) override; + + /** this method is called when the visible area of the view from this viewshell is changed */ + virtual void VisAreaChanged(const ::tools::Rectangle& rRect) override; + + /** Create an accessible object representing the specified window. + @param pWindow + The returned object makes the document displayed in this window + accessible. + @return + Returns an AccessibleDrawDocumentView object. + */ + virtual css::uno::Reference + CreateAccessibleDocumentView (::sd::Window* pWindow) override; + + /** Update the preview to show the specified page. + */ + virtual void UpdatePreview (SdPage* pPage) override; + + virtual css::uno::Reference CreateSubController() override; + + /** Make the given page the new current page. This method + notifies the controller and adapts the selection of the + model. + @param pPage + The new current page. Pass NULL when there is no current page. + */ + void SetCurrentPage (SdPage* pPage); + + void UpdateTitleObject( SdPage* pPage, Paragraph const * pPara ); + void UpdateOutlineObject( SdPage* pPage, Paragraph* pPara ); + +private: + OUString m_StrOldPageName; + std::unique_ptr pOlView; + SdPage* pLastPage; // For efficient processing of the preview + rtl::Reference mxClipEvtLstnr; + bool bPastePossible; + bool mbInitialized; + + void Construct(); + DECL_LINK( ClipboardChanged, TransferableDataHelper*, void ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlineViewShellBase.hxx b/sd/source/ui/inc/OutlineViewShellBase.hxx new file mode 100644 index 000000000..13527d80d --- /dev/null +++ b/sd/source/ui/inc/OutlineViewShellBase.hxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ImpressViewShellBase.hxx" + +namespace sd +{ +/** This class exists to be able to register a factory that + creates an outline view shell as default. +*/ +class OutlineViewShellBase : public ImpressViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(OutlineViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + OutlineViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~OutlineViewShellBase() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/OutlinerIteratorImpl.hxx b/sd/source/ui/inc/OutlinerIteratorImpl.hxx new file mode 100644 index 000000000..00be547c0 --- /dev/null +++ b/sd/source/ui/inc/OutlinerIteratorImpl.hxx @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SdDrawDocument; +class SdPage; +class SdrObjListIter; + +namespace sd { + +class ViewShell; + +namespace outliner { + +/** Base class for the polymorphic implementation class of the + Iterator class. The iterators based on this class are + basically uni directional iterators. Their direction can, however, be + reversed at any point of their life time. +*/ +class IteratorImplBase +{ +public: + /** The constructor stores the given arguments to be used by the derived + classes. + @param pDocument + The document provides the information to be iterated on. + @param pViewShellWeak + Some information has to be taken from the view shell. + @param bDirectionIsForward + This flag defines the iteration direction. When then + the direction is forwards otherwise it is backwards. + */ + IteratorImplBase (SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward); + IteratorImplBase (SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward, PageKind ePageKind, EditMode eEditMode); + virtual ~IteratorImplBase(); + + /** Advance to the next text of the current object or to the next object. + This takes the iteration direction into + account. The new object pointed to can be retrieved (among other + information) by calling the GetPosition method. + */ + virtual void GotoNextText() = 0; + /** Return an object that describes the current object. + @return + The returned object describes the current object pointed to by + the iterator. See the description of + IteratorPosition for details on the available + information. + */ + virtual const IteratorPosition& GetPosition(); + /** Create an exact copy of this object. No argument should be + specified when called from the outside. It then creates an object + first and passes that to the inherited Clone() + methods to fill in class specific information. + @return + Returns a copy of this object. When this method is called with + an argument then this value will be returned. + */ + virtual IteratorImplBase* Clone (IteratorImplBase* pObject=nullptr) const; + /** Test the equality of the this object and the given iterator. Two + iterators are taken to be equal when they point to the same object. + Iteration direction is not taken into account. + @param rIterator + The iterator to compare to. + @return + When both iterators are equal is returned, otherwise. + */ + virtual bool operator== (const IteratorImplBase& rIterator) const; + /** This method is used by the equality operator. It is part of a "multimethod" pattern. + @param rIterator + The iterator to compare to. + @return + Returns when both iterators point to the same object. + */ + virtual bool IsEqualSelection(const IteratorImplBase& rIterator) const; + /** Reverse the direction of iteration. The current object stays the same. + */ + virtual void Reverse(); + +protected: + /// The current position as returned by GetPosition(). + IteratorPosition maPosition; + /// The document on whose data the iterator operates. + SdDrawDocument* mpDocument; + /// Necessary secondary source of information. + std::weak_ptr mpViewShellWeak; + /// Specifies the search direction. + bool mbDirectionIsForward; +}; + +/** Iterator all objects that belong to the current mark list + a.k.a. selection. It is assumed that all marked objects belong to the + same page. It is further assumed that the mark list does not change + while an iterator is alive. It is therefore the responsibility of an + iterator's owner to handle the case of a changed mark list. + +

For documentation of the methods please refer to the base class + IteratorImplBase.

+*/ +class SelectionIteratorImpl + : public IteratorImplBase +{ +public: + SelectionIteratorImpl ( + const ::std::vector< ::tools::WeakReference >& rObjectList, + sal_Int32 nObjectIndex, + SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward); + SelectionIteratorImpl (const SelectionIteratorImpl& rObject); + virtual ~SelectionIteratorImpl() override; + + virtual void GotoNextText() override; + virtual const IteratorPosition& GetPosition() override; + virtual IteratorImplBase* Clone (IteratorImplBase* pObject = nullptr) const override; + virtual bool operator== (const IteratorImplBase& rIterator) const override; + +private: + const ::std::vector<::tools::WeakReference>& mrObjectList; + sal_Int32 mnObjectIndex; + + /** Compare the given iterator with this object. This method handles + only the case that the given iterator is an instance of this class. + @param rIterator + The iterator to compare to. + @return + Returns when both iterators point to the same object. + */ + virtual bool IsEqualSelection(const IteratorImplBase& rIterator) const override; + + IteratorImplBase& operator= (const IteratorImplBase& rIterator); +}; + +/** Iterator for iteration over all objects in a single view. On reaching + the last object on the last page (or the first object on the first page) + the view is *not* switched. Further calls to the + GotoNextObject() method will be ignored. + +

For documentation of the methods please refer to the base class + IteratorImplBase.

+*/ +class ViewIteratorImpl : public IteratorImplBase +{ +public: + ViewIteratorImpl ( + sal_Int32 nPageIndex, + SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward); + ViewIteratorImpl ( + sal_Int32 nPageIndex, + SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward, + PageKind ePageKind, + EditMode eEditMode); + virtual ~ViewIteratorImpl() override; + + virtual void GotoNextText() override; + virtual IteratorImplBase* Clone (IteratorImplBase* pObject = nullptr) const override; + virtual void Reverse() override; + +protected: + /** Set up page pointer and object list iterator for the specified + page. + @param nPageIndex + Index of the new page. It may lie outside the valid range for + page indices. + */ + void SetPage (sal_Int32 nPageIndex); + +private: + /// Indicates whether a page changed occurred on switching to current page. + bool mbPageChangeOccurred; + /// Pointer to the page associated with the current page index. May be NULL. + SdPage* mpPage; + /// Iterator of all objects on the current page. + std::unique_ptr mpObjectIterator; + + // Don't use this operator. + ViewIteratorImpl& operator= (const ViewIteratorImpl&) = delete; +}; + +/** Iterator for iteration over all objects in all views. It automatically + switches views when reaching the end/beginning of a view. + +

For documentation of the methods please refer to the base class + IteratorImplBase.

+*/ +class DocumentIteratorImpl : public ViewIteratorImpl +{ +public: + DocumentIteratorImpl ( + sal_Int32 nPageIndex, + PageKind ePageKind, + EditMode eEditMode, + SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward); + virtual ~DocumentIteratorImpl() override; + + virtual void GotoNextText() override; + virtual IteratorImplBase* Clone (IteratorImplBase* pObject = nullptr) const override; + +private: + /// Number of pages in the view that is specified by maPosition. + sal_Int32 mnPageCount; + + // Don't use this operator. + DocumentIteratorImpl& operator= (const DocumentIteratorImpl& ) = delete; +}; + +} } // end of namespace ::sd::outliner + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PaneChildWindows.hxx b/sd/source/ui/inc/PaneChildWindows.hxx new file mode 100644 index 000000000..e323353ee --- /dev/null +++ b/sd/source/ui/inc/PaneChildWindows.hxx @@ -0,0 +1,65 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd { + +/// Base class of Impress and Draw left sidebars/panes. +class PaneChildWindow + : public SfxChildWindow +{ +public: + PaneChildWindow ( + vcl::Window* pParentWindow, + sal_uInt16 nId, + SfxBindings* pBindings, + SfxChildWinInfo* pInfo, + TranslateId pTitleBarResId); + virtual ~PaneChildWindow() override; +}; + +/// The slide-sorter sidebar (on the left) in Impress. +class LeftPaneImpressChildWindow + : public PaneChildWindow +{ +public: + LeftPaneImpressChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId, SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + + SFX_DECL_CHILDWINDOW_WITHID(LeftPaneImpressChildWindow); +}; + +/// The pages sidebar (on the left) in Draw. +class LeftPaneDrawChildWindow + : public PaneChildWindow +{ +public: + LeftPaneDrawChildWindow(vcl::Window* pParentWindow, sal_uInt16 nId, SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + + SFX_DECL_CHILDWINDOW_WITHID(LeftPaneDrawChildWindow); +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PaneDockingWindow.hxx b/sd/source/ui/inc/PaneDockingWindow.hxx new file mode 100644 index 000000000..c69cb6f94 --- /dev/null +++ b/sd/source/ui/inc/PaneDockingWindow.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "titledockwin.hxx" + +namespace sd { + + class PaneDockingWindow : public ::sd::TitledDockingWindow +{ +public: + /** Create a new docking window. + @param pBindings + Used, among others, to determine the ViewShellBase and + PaneManager that manage the new docking window. + @param pChildWindow + This child window is the logical container for the new docking + window. + @param pParent + The parent window of the new docking window. + @param rsTitle + the initial title + */ + PaneDockingWindow ( + SfxBindings *pBindings, + SfxChildWindow *pChildWindow, + vcl::Window* pParent, + const OUString& rsTitle); + + virtual ~PaneDockingWindow() override; + virtual void StateChanged( StateChangedType nType ) override; + virtual void MouseButtonDown (const MouseEvent& rEvent) override; + /** When docked the given range is passed to the parent SplitWindow. + */ + void SetValidSizeRange (const Range& rValidSizeRange); + + enum Orientation { HorizontalOrientation, VerticalOrientation, UnknownOrientation }; + /** When the PaneDockingWindow is docked and managed by a split window + it can derive its orientation from the orientation of the split + window and return either HorizontalOrientation or + VerticalOrientation. + Otherwise UnknownOrientation is returned. + */ + Orientation GetOrientation() const; +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PaneShells.hxx b/sd/source/ui/inc/PaneShells.hxx new file mode 100644 index 000000000..fe5809a8f --- /dev/null +++ b/sd/source/ui/inc/PaneShells.hxx @@ -0,0 +1,63 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd +{ +/** Shell that displays the left pane for Impress. The shell does not do + anything else and has especially no slots. +*/ +class LeftImpressPaneShell : public SfxShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDLEFTIMPRESSPANESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + LeftImpressPaneShell(); + virtual ~LeftImpressPaneShell() override; +}; + +/** Shell that displays the left pane for Draw. The shell does not do + anything else and has especially no slots. +*/ +class LeftDrawPaneShell : public SfxShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDLEFTDRAWPANESHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + LeftDrawPaneShell(); + virtual ~LeftDrawPaneShell() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PresentationViewShell.hxx b/sd/source/ui/inc/PresentationViewShell.hxx new file mode 100644 index 000000000..f37b31e08 --- /dev/null +++ b/sd/source/ui/inc/PresentationViewShell.hxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "DrawViewShell.hxx" + +namespace sd +{ +/** This view shell is responsible for showing the presentation of an + Impress document. +*/ +class PresentationViewShell : public DrawViewShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDPRESVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + PresentationViewShell(ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, + FrameView* pFrameView); + virtual ~PresentationViewShell() override; + + /** This method is used by a simple class that passes some + arguments from the creator of the new view shell to the new view + shell object by waiting for its asynchronous creation. + @param pFrameView + The frame view that is typically used by the creating object and + that shall be shared by the created view shell. + */ + void FinishInitialization(FrameView* pFrameView); + + virtual void Resize() override; + +protected: + virtual VclPtr CreateHRuler(::sd::Window* pWin) override; + virtual VclPtr CreateVRuler(::sd::Window* pWin) override; + +private: + ::tools::Rectangle maOldVisArea; + ImplSVEvent* mnAbortSlideShowEvent; + + virtual void Activate(bool bIsMDIActivate) override; + virtual void Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) override; + + DECL_LINK(AbortSlideShowHdl, void*, void); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PresentationViewShellBase.hxx b/sd/source/ui/inc/PresentationViewShellBase.hxx new file mode 100644 index 000000000..684e5ee61 --- /dev/null +++ b/sd/source/ui/inc/PresentationViewShellBase.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ViewShellBase.hxx" + +namespace sd +{ +/** This class exists to be able to register another factory that + creates the view shell for the presentation. +*/ +class PresentationViewShellBase : public ViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(PresentationViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + PresentationViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~PresentationViewShellBase() override; + +protected: + virtual void InitializeFramework() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/PreviewRenderer.hxx b/sd/source/ui/inc/PreviewRenderer.hxx new file mode 100644 index 000000000..245f0b638 --- /dev/null +++ b/sd/source/ui/inc/PreviewRenderer.hxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +class SdPage; +class VirtualDevice; + +namespace sd { + +class DrawDocShell; +class DrawView; + +class PreviewRenderer + : public SfxListener +{ +public: + /** Create a new preview renderer that takes some of its initial values + from the given output device. + @param bPaintFrame + When (the default) then a frame is painted around the + preview. This makes the actual preview smaller. + */ + PreviewRenderer(const bool bPaintFrame = true); + + virtual ~PreviewRenderer() override; + + /** Render a page with the given pixel size. + Use this version when only the width of the preview is known to the + caller. The height is then calculated according to the aspect + ratio of the given page. + @param pPage + The page to render. + @param nWidth + The width of the preview in device coordinates. + The high contrast mode of the application is + ignored and the preview is rendered in normal mode. + */ + Image RenderPage ( + const SdPage* pPage, + const sal_Int32 nWidth); + + /** Render a page with the given pixel size. + @param pPage + The page to render. + @param aPreviewPixelSize + The size in device coordinates of the preview. + @param bObeyHighContrastMode + When then the high contrast mode of the application is + ignored and the preview is rendered in normal mode. When + and high contrast mode is active then the preview is + rendered in high contrast mode. + @param bDisplayPresentationObjects + When then the PresObj place holders are not displayed + in the returned preview. + */ + Image RenderPage ( + const SdPage* pPage, + const Size aPreviewPixelSize, + const bool bObeyHighContrastMode, + const bool bDisplayPresentationObjects = true); + + /** Render an image that contains the given substitution text instead of a + slide preview. + @param aPreviewPixelSize + The size in device coordinates of the image. + */ + Image RenderSubstitution ( + const Size& rPreviewPixelSize, + const OUString& sSubstitutionText); + + /** Scale the given bitmap by keeping its aspect ratio to the desired + width. Add a frame to it afterwards. + */ + Image ScaleBitmap ( + const BitmapEx& rBitmap, + int nWidth); + +protected: + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + +private: + ScopedVclPtr mpPreviewDevice; + ::std::unique_ptr mpView; + DrawDocShell* mpDocShellOfView; + const Color maFrameColor; + const bool mbHasFrame; + static const int snSubstitutionTextSize; + // Width of the frame that is painted around the preview. + static const int snFrameWidth; + + bool Initialize ( + const SdPage* pPage, + const Size& rPixelSize, + const bool bObeyHighContrastMode); + void PaintPage ( + const SdPage* pPage, + const bool bDisplayPresentationObjects); + void PaintSubstitutionText (const OUString& rSubstitutionText); + void PaintFrame(); + + /** Set up the map mode so that the given page is renderer into a bitmap + with the specified width. + @param rPage + The page for which the preview is created. + @param rPixelSize + The size of the resulting preview bitmap. Note that this size + includes the frame. The actual preview is smaller accordingly. + */ + void SetupOutputSize (const SdPage& rPage, const Size& rPixelSize); + + /** When mpView is empty then create a new view and initialize it. + Otherwise just initialize it. + */ + void ProvideView (DrawDocShell* pDocShell); +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/RemoteServer.hxx b/sd/source/ui/inc/RemoteServer.hxx new file mode 100644 index 000000000..965bf7a27 --- /dev/null +++ b/sd/source/ui/inc/RemoteServer.hxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include + +#include +#include + +#include + +namespace osl { class Mutex; } +namespace com::sun::star::presentation { class XSlideShowController; } +namespace com::sun::star::uno { template class Reference; } + +/** +* The port for use for the main communication between LibO and remote control app. +*/ +#define PORT 1599 + +namespace sd +{ + class BufferedStreamSocket; + class Communicator; + + struct ClientInfo + { + OUString mName; + + bool mbIsAlreadyAuthorised; + + ClientInfo( const OUString& rName, + const bool bIsAlreadyAuthorised ) : + mName( rName ), + mbIsAlreadyAuthorised( bIsAlreadyAuthorised ) {} + + virtual ~ClientInfo() {}; + }; + + struct ClientInfoInternal; + + class RemoteServer final : public salhelper::Thread + { + public: + // Internal setup + static void setup(); + + // For slideshowimpl to inform us. + static void presentationStarted( const css::uno::Reference< + css::presentation::XSlideShowController > &rController ); + static void presentationStopped(); + + // For the control dialog + SD_DLLPUBLIC static std::vector< std::shared_ptr< ClientInfo > > getClients(); + SD_DLLPUBLIC static bool connectClient( const std::shared_ptr< ClientInfo >& pClient, + std::u16string_view aPin ); + SD_DLLPUBLIC static void deauthoriseClient( const std::shared_ptr< ClientInfo >& pClient ); + + /// ensure that discoverability (eg. for Bluetooth) is enabled + SD_DLLPUBLIC static void ensureDiscoverable(); + /// restore the state of discoverability from before ensureDiscoverable + SD_DLLPUBLIC static void restoreDiscoverable(); + + // For the communicator + static void removeCommunicator( Communicator const * pCommunicator ); + private: + RemoteServer(); + virtual ~RemoteServer() override; + static RemoteServer *spServer; + static ::osl::Mutex sDataMutex; + static ::std::vector sCommunicators; + osl::AcceptorSocket mSocket; + + ::std::vector< std::shared_ptr< ClientInfoInternal > > mAvailableClients; + + void execute() override; + void handleAcceptedConnection( BufferedStreamSocket *pSocket ) ; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/Ruler.hxx b/sd/source/ui/inc/Ruler.hxx new file mode 100644 index 000000000..5cf1d18bb --- /dev/null +++ b/sd/source/ui/inc/Ruler.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd { + +class DrawViewShell; +class RulerCtrlItem; +class Window; + +class Ruler final + : public SvxRuler +{ +public: + Ruler ( + DrawViewShell& rViewSh, + vcl::Window* pParent, + ::sd::Window* pWin, + SvxRulerSupportFlags nRulerFlags, + SfxBindings& rBindings, + WinBits nWinStyle); + virtual ~Ruler() override; + virtual void dispose() override; + + void SetNullOffset(const Point& rOffset); + + bool IsHorizontal() const { return bHorz; } + + using ::Ruler::SetNullOffset; + +private: + DrawViewShell* pDrViewShell; + std::unique_ptr pCtrlItem; + bool bHorz; + + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void Command(const CommandEvent& rCEvt) override; + virtual void ExtraDown() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SdUnoDrawView.hxx b/sd/source/ui/inc/SdUnoDrawView.hxx new file mode 100644 index 000000000..6b62e4cb2 --- /dev/null +++ b/sd/source/ui/inc/SdUnoDrawView.hxx @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "DrawSubController.hxx" +#include + +class SdXImpressDocument; +namespace com::sun::star::drawing { class XLayer; } + +namespace sd { + +class DrawViewShell; +class View; + +/** This class implements the DrawViewShell specific part of the controller. +*/ +class SdUnoDrawView final + : private cppu::BaseMutex, + public DrawSubControllerInterfaceBase +{ +public: + SdUnoDrawView ( + DrawViewShell& rViewShell, + View& rView) noexcept; + virtual ~SdUnoDrawView() noexcept override; + + // XSelectionSupplier + + virtual sal_Bool SAL_CALL select ( + const css::uno::Any& aSelection) override; + + virtual css::uno::Any SAL_CALL getSelection() override; + + virtual void SAL_CALL addSelectionChangeListener ( + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL removeSelectionChangeListener ( + const css::uno::Reference& rxListener) override; + + // XDrawView + + virtual void SAL_CALL setCurrentPage ( + const css::uno::Reference& xPage) override; + + virtual css::uno::Reference SAL_CALL getCurrentPage() override; + + // XFastPropertySet + + virtual void SAL_CALL setFastPropertyValue ( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + + virtual css::uno::Any SAL_CALL getFastPropertyValue ( + sal_Int32 nHandle) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + /** Return a reference to the active layer object. + @return + The returned value may be empty when the internal state of this + view is not valid (like during destruction.) + */ + css::uno::Reference< css::drawing::XLayer> getActiveLayer() const; + +private: + bool getMasterPageMode() const noexcept; + void setMasterPageMode(bool MasterPageMode_) noexcept; + bool getLayerMode() const noexcept; + void setLayerMode(bool LayerMode_) noexcept; + /** Make the specified object the active layer. + @param rxLayer + The new layer object. + @throws css::uno::RuntimeException + */ + void setActiveLayer (const css::uno::Reference< css::drawing::XLayer>& rxLayer); + + void SetZoom( sal_Int16 nZoom ); + sal_Int16 GetZoom() const; + + void SetViewOffset(const css::awt::Point& rWinPos ); + css::awt::Point GetViewOffset() const; + + void SetZoomType( sal_Int16 nType ); + + css::uno::Any getDrawViewMode() const; + + SdXImpressDocument* GetModel() const noexcept; + + DrawViewShell& mrDrawViewShell; + sd::View& mrView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SdUnoOutlineView.hxx b/sd/source/ui/inc/SdUnoOutlineView.hxx new file mode 100644 index 000000000..2789cabee --- /dev/null +++ b/sd/source/ui/inc/SdUnoOutlineView.hxx @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "DrawSubController.hxx" +#include + +namespace sd { + +class OutlineViewShell; + +/** This class implements the OutlineViewShell specific part of the controller. +*/ +class SdUnoOutlineView final + : private cppu::BaseMutex, + public DrawSubControllerInterfaceBase +{ +public: + SdUnoOutlineView ( + OutlineViewShell& rViewShell) noexcept; + virtual ~SdUnoOutlineView() noexcept override; + + virtual void SAL_CALL disposing() override; + + // XSelectionSupplier + + virtual sal_Bool SAL_CALL select ( + const css::uno::Any& aSelection) override; + + virtual css::uno::Any SAL_CALL getSelection() override; + + virtual void SAL_CALL addSelectionChangeListener ( + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL removeSelectionChangeListener ( + const css::uno::Reference& rxListener) override; + + // XDrawView + + virtual void SAL_CALL setCurrentPage ( + const css::uno::Reference& xPage) override; + + virtual css::uno::Reference SAL_CALL getCurrentPage() override; + + // XFastPropertySet + + virtual void SAL_CALL setFastPropertyValue ( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + + virtual css::uno::Any SAL_CALL getFastPropertyValue ( + sal_Int32 nHandle) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + OutlineViewShell& mrOutlineViewShell; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SdUnoSlideView.hxx b/sd/source/ui/inc/SdUnoSlideView.hxx new file mode 100644 index 000000000..7ca40a1ab --- /dev/null +++ b/sd/source/ui/inc/SdUnoSlideView.hxx @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "DrawSubController.hxx" +#include + +namespace sd::slidesorter { class SlideSorter; } +namespace com::sun::star::drawing { class XDrawPage; } + +namespace sd { + +/** This class implements the SlideSorter specific part of the + controller. + */ +class SdUnoSlideView final + : private cppu::BaseMutex, + public DrawSubControllerInterfaceBase +{ +public: + SdUnoSlideView ( + slidesorter::SlideSorter& rSlideSorter) noexcept; + virtual ~SdUnoSlideView() noexcept override; + + // XSelectionSupplier + + virtual sal_Bool SAL_CALL select (const css::uno::Any& aSelection) override; + + virtual css::uno::Any SAL_CALL getSelection() override; + + virtual void SAL_CALL addSelectionChangeListener ( + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL removeSelectionChangeListener ( + const css::uno::Reference& rxListener) override; + + // XDrawView + + virtual void SAL_CALL setCurrentPage ( + const css::uno::Reference< css::drawing::XDrawPage >& xPage) override; + + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL + getCurrentPage() override; + + // XFastPropertySet + + virtual void SAL_CALL setFastPropertyValue ( + sal_Int32 nHandle, + const css::uno::Any& rValue) override; + + virtual css::uno::Any SAL_CALL getFastPropertyValue ( + sal_Int32 nHandle) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + +private: + slidesorter::SlideSorter& mrSlideSorter; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ShellFactory.hxx b/sd/source/ui/inc/ShellFactory.hxx new file mode 100644 index 000000000..fc05c41ab --- /dev/null +++ b/sd/source/ui/inc/ShellFactory.hxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd +{ +typedef ToolbarId ShellId; + +template class ShellFactory +{ +public: + /** This abstract virtual class needs a destructor so that the + destructors of derived classes are called. + */ + virtual ~ShellFactory(){}; + + /** Create a new instance of a view shell for the given id that will + be stacked onto the given view shell base. + @return + Return the new view shell or NULL when a creation is not + possible. + */ + virtual ShellType* CreateShell(ShellId nId) = 0; + + /** Tell the factory that a shell is no longer in use. It may destroy + it or put it for future use in a cache. + */ + virtual void ReleaseShell(ShellType* pShell) = 0; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideSorter.hxx b/sd/source/ui/inc/SlideSorter.hxx new file mode 100644 index 000000000..9ed70cf9b --- /dev/null +++ b/sd/source/ui/inc/SlideSorter.hxx @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace vcl { class Window; } +namespace com::sun::star::frame { class XController; } +namespace rtl { template class Reference; } + +namespace sd { +class ViewShell; +class ViewShellBase; +class Window; +class FuPoor; +} + +namespace sd::slidesorter::model { class SlideSorterModel; } + +namespace sd::slidesorter::view { + class SlideSorterView; + class Theme; +} + +namespace sd::slidesorter::controller { + class SlideSorterController; + class SlotManager; + class Properties; +} + +namespace sd::slidesorter { + +/** Show previews for all the slides in a document and allow the user to + insert or delete slides and modify the order of the slides. + + This class is a facade for the model, view, and controller classes. + It is a hub that allows access to the various parts of a slide sorter. + + Note that this class is not in its final state. +*/ +class SlideSorter final +{ + friend class controller::SlotManager; +public: + ~SlideSorter(); + + /// Forbid copy construction and copy assignment + SlideSorter(const SlideSorter&) = delete; + SlideSorter& operator=(const SlideSorter&) = delete; + + /** Return whether the called SlideSorter object is valid and calling + its Get(Model,View,Controller) methods is safe. When is + called then no other methods should be called. + Calling this method should be necessary only during startup and + shutdown (when that can be detected). + */ + bool IsValid() const { return mbIsValid;} + + /** Create a new slide sorter that is strongly coupled to the given view + shell. Use this function for a slide sorter in the left pane. + @param rViewShell + Typically a SlideSorterViewShell object. + @param rpContentWindow + Typically the content window of the ViewShell. + @param rpHorizontalScrollBar + Typically the horizontal scroll bar of the ViewShell. + @param rpVerticalScrollBar + Typically the vertical scroll bar of the ViewShell. + @param rpScrollBarBox + The little square enclosed by the two scroll bars. Typically + the one from the ViewShell. + */ + static std::shared_ptr CreateSlideSorter ( + ViewShell& rViewShell, + sd::Window* pContentWindow, + ScrollBar* pHorizontalScrollBar, + ScrollBar* pVerticalScrollBar, + ScrollBarBox* pScrollBarBox); + + /** Create a new slide sorter that is loosely coupled to the given view + shell. The view shell may even be missing. + @param rBase + ViewShellBase object of the enclosing application. + @param pViewShell + Supply when at hand. + @param rParentWindow + The parent window of the internally created content window and + scroll bars. + */ + static std::shared_ptr CreateSlideSorter ( + ViewShellBase& rBase, + vcl::Window& rParentWindow); + + /** Return the control of the vertical scroll bar. + */ + const VclPtr& GetVerticalScrollBar() const { return mpVerticalScrollBar;} + + /** Return the control of the horizontal scroll bar. + */ + const VclPtr& GetHorizontalScrollBar() const { return mpHorizontalScrollBar;} + + /** Return the scroll bar filler that paints the little square that is + enclosed by the two scroll bars. + */ + const VclPtr& GetScrollBarFiller (void) const { return mpScrollBarBox;} + + /** Return the content window. This is a sibling and is geometrically + enclosed by the scroll bars. + */ + const VclPtr& GetContentWindow() const { return mpContentWindow;} + + model::SlideSorterModel& GetModel() const; + + view::SlideSorterView& GetView() const; + + // Exported for unit test + SD_DLLPUBLIC controller::SlideSorterController& GetController() const; + + /** Return the view shell that was given at construction. + @return + May be empty. + */ + ViewShell* GetViewShell() const { return mpViewShell;} + + /** Return the XController object of the main view. + */ + css::uno::Reference + GetXController() const; + + /** Return the ViewShellBase object. + @return + May be empty. + */ + ViewShellBase* GetViewShellBase() const { return mpViewShellBase;} + + void Paint (const ::tools::Rectangle& rRepaintArea); + + /** Place and size the controls and windows. You may want to call this + method when something has changed that for instance affects the + visibility state of the scroll bars. + */ + void ArrangeGUIElements ( + const Point& rOffset, + const Size& rSize); + + void RelocateToWindow (vcl::Window* pWindow); + + /** Set the current function at the view shell or, when it is not + present, set it at the content window. This method supports the use + of functions even when there is no SlideSorterViewShell. + */ + void SetCurrentFunction (const rtl::Reference& rpFunction); + + /** Return a collection of properties that are used throughout the slide + sorter. + */ + std::shared_ptr const & GetProperties() const; + + /** Return the active theme which gives access to colors and fonts. + */ + std::shared_ptr const & GetTheme() const; + +private: + /** This virtual method makes it possible to create a specialization of + the slide sorter view shell that works with its own implementation + of model, view, and controller. The default implementation simply + calls the CreateModel(), CreateView(), and CreateController() + methods in this order. + */ + void CreateModelViewController(); + + /** Create the model for the view shell. When called from the default + implementation of CreateModelViewController() then neither view nor + controller do exist. Test their pointers when in doubt. + */ + model::SlideSorterModel* CreateModel(); + + bool mbIsValid; + + std::unique_ptr mpSlideSorterController; + std::unique_ptr mpSlideSorterModel; + std::unique_ptr mpSlideSorterView; + css::uno::WeakReference mxControllerWeak; + ViewShell* mpViewShell; + ViewShellBase* mpViewShellBase; + VclPtr mpContentWindow; + VclPtr mpHorizontalScrollBar; + VclPtr mpVerticalScrollBar; + VclPtr mpScrollBarBox; + + /** Some slide sorter wide properties that are used in different + classes. + */ + std::shared_ptr mpProperties; + std::shared_ptr mpTheme; + + SlideSorter ( + ViewShell& rViewShell, + sd::Window* pContentWindow, + ScrollBar* pHorizontalScrollBar, + ScrollBar* pVerticalScrollBar, + ScrollBarBox* pScrollBarBox); + SlideSorter ( + ViewShellBase& rBase, + vcl::Window& rParentWindow); + + void Init(); + /** Create the controls for the slide sorter. This are the tab bar + for switching the edit mode, the scroll bar, and the actual + slide sorter view window. + This method is usually called exactly one time from the + constructor. + */ + void SetupControls(); + + /** This method is usually called exactly one time from the + constructor. + */ + void SetupListeners(); + + /** Release the listeners that have been installed in SetupListeners(). + */ + void ReleaseListeners(); +}; + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideSorterViewShell.hxx b/sd/source/ui/inc/SlideSorterViewShell.hxx new file mode 100644 index 000000000..64808d434 --- /dev/null +++ b/sd/source/ui/inc/SlideSorterViewShell.hxx @@ -0,0 +1,232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ViewShell.hxx" +#include +#include +#include +#include +#include + +namespace sd::slidesorter::controller { class SlotManager; } + +namespace sd::slidesorter { + +class SlideSorter; + +class SAL_DLLPUBLIC_RTTI SlideSorterViewShell final + : public ViewShell +{ + friend class controller::SlotManager; + +public: + SFX_DECL_INTERFACE(SD_IF_SDSLIDESORTERVIEWSHELL) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + static std::shared_ptr Create( + SfxViewFrame* pFrame, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameView); + + virtual ~SlideSorterViewShell() override; + + /** Late initialization that has to be called after a new instance has + completed its construction. + */ + virtual void Init (bool bIsMainViewShell) override; + + /** Return a slide sorter that is currently displayed in one of the + panes that belong to the given ViewShellBase object. + When there is only one slide sorter visible then that one is + returned. When two (or more) are visible then the one in the center + pane is returned. When no slidesorter is visible then NULL is + returned. + */ + // Exported for unit test + SD_DLLPUBLIC static SlideSorterViewShell* GetSlideSorter(ViewShellBase& rBase); + + virtual SdPage* GetActualPage() override; + + /// inherited from sd::ViewShell + virtual SdPage* getCurrentPage() const override; + + void ExecCtrl (SfxRequest& rRequest); + void GetCtrlState (SfxItemSet &rSet); + void FuSupport (SfxRequest& rRequest); + void FuTemporary (SfxRequest& rRequest); + void GetStatusBarState (SfxItemSet& rSet); + void FuPermanent (SfxRequest& rRequest); + void GetAttrState (SfxItemSet& rSet); + static void ExecStatusBar (SfxRequest& rRequest); + virtual void Command (const CommandEvent& rEvent, ::sd::Window* pWindow) override; + void GetMenuState (SfxItemSet &rSet); + void GetClipboardState (SfxItemSet &rSet); + + virtual void ReadFrameViewData (FrameView* pView) override; + virtual void WriteFrameViewData() override; + + /** Set the zoom factor. The given value is clipped against an upper + bound. + @param nZoom + An integer percent value, i.e. nZoom/100 is the actual zoom + factor. + */ + virtual void SetZoom (::tools::Long nZoom) override; + virtual void SetZoomRect (const ::tools::Rectangle& rZoomRect) override; + + /** This is a callback method used by the active window to delegate its + Paint() call to. This view shell itself delegates it to the view. + */ + virtual void Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) override; + + /** Place and size the controls and windows. You may want to call this + method when something has changed that for instance affects the + visibility state of the scroll bars. + */ + virtual void ArrangeGUIElements() override; + + virtual void Activate (bool IsMDIActivate) override; + virtual void Deactivate (bool IsMDIActivate) override; + + /** Move slides up and down. Mainly uno commands. */ + void ExecMovePageUp (SfxRequest& rReq); + void GetStateMovePageUp (SfxItemSet& rSet); + + void ExecMovePageDown (SfxRequest& rReq); + void GetStateMovePageDown (SfxItemSet& rSet); + + void ExecMovePageFirst (SfxRequest& rReq); + void GetStateMovePageFirst (SfxItemSet& rSet); + + void ExecMovePageLast (SfxRequest& rReq); + void GetStateMovePageLast (SfxItemSet& rSet); + + + //===== Drag and Drop ===================================================== + + void StartDrag ( + const Point& rDragPt, + vcl::Window* pWindow ); + virtual sal_Int8 AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer ) override; + virtual sal_Int8 ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) override; + + typedef ::std::vector PageSelection; + + /** Return the set of selected pages. + */ + std::shared_ptr GetPageSelection() const; + + void SetPageSelection (const std::shared_ptr& rSelection); + + /** Add a listener that is called when the selection of the slide sorter + changes. + @param rListener + When this method is called multiple times for the same listener + the second and all following calls are ignored. Each listener + is added only once. + */ + void AddSelectionChangeListener (const Link& rListener); + + /** Remove a listener that was called when the selection of the slide + sorter changes. + @param rListener + It is safe to pass a listener that was not added are has been + removed previously. Such calls are ignored. + */ + void RemoveSelectionChangeListener (const Link& rListener); + + virtual css::uno::Reference CreateSubController() override; + + /** Create an accessible object representing the specified window. + @param pWindow + The returned object makes the document displayed in this window + accessible. + @return + Returns an AccessibleSlideSorterView object. + */ + virtual css::uno::Reference + CreateAccessibleDocumentView (::sd::Window* pWindow) override; + // handle SlideSorterView specially because AccessibleSlideSorterView doesn't inherit from AccessibleDocumentViewBase + virtual void SwitchViewFireFocus( const css::uno::Reference< css::accessibility::XAccessible >& xAcc ) override; + + // Exported for unit test + SD_DLLPUBLIC SlideSorter& GetSlideSorter() const; + + /** Try to relocate all toplevel window elements to the given parent + window. + */ + virtual bool RelocateToParentWindow (vcl::Window* pParentWindow) override; + +private: + + /** Override this method to handle a missing tool bar correctly. + This is the case when the slide sorter is not the main view shell. + */ + virtual SfxUndoManager* ImpGetUndoManager() const override; + + std::shared_ptr mpSlideSorter; + bool mbIsArrangeGUIElementsPending; + + SlideSorterViewShell ( + SfxViewFrame* pFrame, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameView); + void Initialize(); + + /** This method overwrites the one from our base class: We do our own + scroll bar and the base class call is thus unnecessary. It simply + calls UpdateScrollBars(false). + */ + virtual void UpdateScrollBars() override; + + void PostMoveSlidesActions(const std::shared_ptr &rpSelection); + + void MainViewEndEditAndUnmarkAll(); + + /** Select the same pages in the document as are selected in the + SlideSorterViewShell + + return the page numbers of the first and last selected pages + */ + std::pair SyncPageSelectionToDocument(const std::shared_ptr &rpSelection); +}; + +typedef std::shared_ptr SharedPageSelection; + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideSorterViewShellBase.hxx b/sd/source/ui/inc/SlideSorterViewShellBase.hxx new file mode 100644 index 000000000..e1ca1b57b --- /dev/null +++ b/sd/source/ui/inc/SlideSorterViewShellBase.hxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ImpressViewShellBase.hxx" + +namespace sd +{ +/** This class exists to be able to register a factory that creates a + slide sorter view shell as default. +*/ +class SlideSorterViewShellBase final : public ImpressViewShellBase +{ +public: + SFX_DECL_VIEWFACTORY(SlideSorterViewShellBase); + + /** This constructor is used by the view factory of the SFX + macros. + */ + SlideSorterViewShellBase(SfxViewFrame* pFrame, SfxViewShell* pOldShell); + virtual ~SlideSorterViewShellBase() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SlideTransitionPane.hxx b/sd/source/ui/inc/SlideTransitionPane.hxx new file mode 100644 index 000000000..2b6ea8f93 --- /dev/null +++ b/sd/source/ui/inc/SlideTransitionPane.hxx @@ -0,0 +1,137 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include "SlideSorterViewShell.hxx" + +#include +#include +#include +#include + +#include +#include + +class SdDrawDocument; + +namespace com::sun::star::drawing { class XDrawView; } +namespace com::sun::star::frame { class XModel; } +namespace sd::tools { class EventMultiplexerEvent; } + +namespace sd +{ + +class TransitionPane; +class ViewShellBase; + +namespace impl +{ + struct TransitionEffect; +} + +class SlideTransitionPane final : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow +{ +public: + explicit SlideTransitionPane( + weld::Widget* pParent, + ViewShellBase & rBase); + virtual ~SlideTransitionPane() override; + + // ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + + void onSelectionChanged(); + void onChangeCurrentPage(); + +private: + void updateControls(); + void updateControlState(); + void updateVariants(size_t nPresetOffset); + + void updateSoundList(); + void openSoundFileDialog(); + + impl::TransitionEffect getTransitionEffectFromControls() const; + + void applyToSelectedPages(bool bPreview); + void playCurrentEffect(); + + void addListener(); + void removeListener(); + + ::sd::slidesorter::SharedPageSelection getSelectedPages() const; + + void Initialize(SdDrawDocument* pDoc); + + DECL_LINK( ApplyToAllButtonClicked, weld::Button&, void ); + DECL_LINK( PlayButtonClicked, weld::Button&, void ); + DECL_LINK( AutoPreviewClicked, weld::Toggleable&, void ); + + DECL_LINK( TransitionSelected, ValueSet*, void ); + DECL_LINK( AdvanceSlideRadioButtonToggled, weld::Toggleable&, void ); + DECL_LINK( AdvanceTimeModified, weld::MetricSpinButton&, void ); + DECL_LINK( VariantListBoxSelected, weld::ComboBox&, void ); + DECL_LINK( DurationModifiedHdl, weld::MetricSpinButton&, void ); + DECL_LINK( DurationLoseFocusHdl, weld::Widget&, void ); + DECL_LINK( SoundListBoxSelected, weld::ComboBox&, void ); + DECL_LINK( LoopSoundBoxChecked, weld::Toggleable&, void ); + DECL_LINK( EventMultiplexerListener, tools::EventMultiplexerEvent&, void ); + DECL_LINK(LateInitCallback, Timer *, void); + + ViewShellBase & mrBase; + SdDrawDocument * mpDrawDoc; + + std::unique_ptr mxVS_TRANSITION_ICONS; + std::unique_ptr mxVS_TRANSITION_ICONSWin; + std::unique_ptr mxFT_VARIANT; + std::unique_ptr mxLB_VARIANT; + std::unique_ptr mxFT_duration; + std::unique_ptr mxCBX_duration; + std::unique_ptr mxFT_SOUND; + std::unique_ptr mxLB_SOUND; + std::unique_ptr mxCB_LOOP_SOUND; + std::unique_ptr mxRB_ADVANCE_ON_MOUSE; + std::unique_ptr mxRB_ADVANCE_AUTO; + std::unique_ptr mxMF_ADVANCE_AUTO_AFTER; + std::unique_ptr mxPB_APPLY_TO_ALL; + std::unique_ptr mxPB_PLAY; + std::unique_ptr mxCB_AUTO_PREVIEW; + + css::uno::Reference< css::drawing::XDrawView > mxView; + css::uno::Reference< css::frame::XModel > mxModel; + + bool mbHasSelection; + bool mbUpdatingControls; + bool mbIsMainViewChangePending; + + std::vector maSoundList; + mutable OUString maCurrentSoundFile; + + // How many variants each transition set has + std::map< OUString, int > m_aNumVariants; + + Timer maLateInitTimer; +}; + +} // namespace sd + +// INCLUDED_SD_SOURCE_UI_ANIMATIONS_SLIDETRANSITIONPANE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/SpellDialogChildWindow.hxx b/sd/source/ui/inc/SpellDialogChildWindow.hxx new file mode 100644 index 000000000..3d2163a7e --- /dev/null +++ b/sd/source/ui/inc/SpellDialogChildWindow.hxx @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SdOutliner; + +namespace sd +{ +/** This derivation of the svx::SpellDialogChildWindow base class + provides Draw and Impress specific implementations of + GetNextWrongSentence() and ApplyChangedSentence(). +*/ +class SpellDialogChildWindow final : public svx::SpellDialogChildWindow, public SfxListener +{ +public: + SpellDialogChildWindow(vcl::Window* pParent, sal_uInt16 nId, SfxBindings* pBindings, + SfxChildWinInfo* pInfo); + virtual ~SpellDialogChildWindow() override; + + /** This method makes the one from the base class public so that + it can be called from the view shell when one is created. + */ + void InvalidateSpellDialog(); + + // SfxListener + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + + SFX_DECL_CHILDWINDOW_WITHID(SpellDialogChildWindow); + +private: + /** Iterate over the sentences in all text shapes and stop at the + next sentence with spelling errors. While doing so the view + mode may be changed and text shapes are set into edit mode. + */ + virtual svx::SpellPortions GetNextWrongSentence(bool bRecheck) override; + + /** This method is responsible for merging corrections made in the + spelling dialog back into the document. + */ + virtual void ApplyChangedSentence(const svx::SpellPortions& rChanged, bool bRecheck) override; + virtual void GetFocus() override; + virtual void LoseFocus() override; + + /** This outliner is used to do the main work of iterating over a + document and finding sentences with spelling errors. + */ + SdOutliner* mpSdOutliner; + + /** When this flag is then eventually we have to destroy + the outliner in mpSdOutliner. + */ + bool mbOwnOutliner; + + /** Provide an outliner in the mpSdOutliner data member. When the + view shell has changed since the last call this include the + deletion/release of formerly created/obtained one prior to + construction/obtaining of a new one. + */ + void ProvideOutliner(); + + void EndSpellingAndClearOutliner(); +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/TabControl.hxx b/sd/source/ui/inc/TabControl.hxx new file mode 100644 index 000000000..5e5eba7bb --- /dev/null +++ b/sd/source/ui/inc/TabControl.hxx @@ -0,0 +1,107 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd { + +/** + * TabControl-Class for page switch + */ + +class DrawViewShell; + +class TabControl final + : public TabBar, + public DragSourceHelper, + public DropTargetHelper +{ +public: + TabControl (DrawViewShell* pDrViewSh, vcl::Window* pParent); + virtual void dispose() override; + virtual ~TabControl() override; + + /** Inform all listeners of this control that the current page has been + activated. Call this method after switching the current page and is + not done elsewhere (like when using page up/down keys). + */ + void SendActivatePageEvent(); + + /** Inform all listeners of this control that the current page has been + deactivated. Call this method before switching the current page and + is not done elsewhere (like when using page up/down keys). + */ + void SendDeactivatePageEvent(); + +private: + DrawViewShell* pDrViewSh; + bool bInternalMove; + + // TabBar + virtual void Select() override; + virtual void DoubleClick() override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Command(const CommandEvent& rCEvt) override; + + virtual bool StartRenaming() override; + virtual TabBarAllowRenamingReturnCode AllowRenaming() override; + virtual void EndRenaming() override; + + virtual void ActivatePage() override; + virtual bool DeactivatePage() override; + + // DragSourceHelper + virtual void StartDrag( sal_Int8 nAction, const Point& rPosPixel ) override; + + // DropTargetHelper + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + + // nested class to implement the TransferableHelper + class TabControlTransferable final : public TransferableHelper + { + public: + explicit TabControlTransferable( TabControl& rParent ) : + mrParent( rParent ) {} + private: + + TabControl& mrParent; + + virtual ~TabControlTransferable() override; + + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual void DragFinished( sal_Int8 nDropAction ) override; + + }; + + friend class TabControl::TabControlTransferable; + + void DragFinished(); + + using TabBar::StartDrag; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/TableDesignPane.hxx b/sd/source/ui/inc/TableDesignPane.hxx new file mode 100644 index 000000000..042eb6137 --- /dev/null +++ b/sd/source/ui/inc/TableDesignPane.hxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace com::sun::star::beans { class XPropertySet; } +namespace com::sun::star::container { class XIndexAccess; } +namespace com::sun::star::drawing { class XDrawView; } + +namespace sd +{ + +namespace tools { +class EventMultiplexerEvent; +} + +class ViewShellBase; + +enum TableCheckBox : sal_uInt16 +{ + CB_HEADER_ROW = 0, + CB_TOTAL_ROW = 1, + CB_BANDED_ROWS = 2, + CB_FIRST_COLUMN = 3, + CB_LAST_COLUMN = 4, + CB_BANDED_COLUMNS = 5, + CB_COUNT = CB_BANDED_COLUMNS + 1 +}; + +class TableValueSet final : public ValueSet +{ +private: + bool m_bModal; +public: + TableValueSet(std::unique_ptr pScrolledWindow); + virtual void Resize() override; + virtual void StyleUpdated() override; + void updateSettings(); + void setModal(bool bModal) { m_bModal = bModal; } +}; + +class TableDesignWidget final +{ +public: + TableDesignWidget(weld::Builder& rBuilder, ViewShellBase& rBase); + ~TableDesignWidget(); + + // callbacks + void onSelectionChanged(); + + void ApplyOptions(); + void ApplyStyle(); + +private: + void addListener(); + void removeListener(); + void updateControls(); + + void FillDesignPreviewControl(); + + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void); + DECL_LINK(implValueSetHdl, ValueSet*, void); + DECL_LINK(implCheckBoxHdl, weld::Toggleable&, void); + + ViewShellBase& mrBase; + + std::unique_ptr m_xValueSet; + std::unique_ptr m_xValueSetWin; + std::unique_ptr m_aCheckBoxes[CB_COUNT]; + + css::uno::Reference< css::beans::XPropertySet > mxSelectedTable; + css::uno::Reference< css::drawing::XDrawView > mxView; + css::uno::Reference< css::container::XIndexAccess > mxTableFamily; +}; + +class TableDesignPane final : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow +{ +private: + std::unique_ptr m_xImpl; +public: + TableDesignPane( weld::Widget* pParent, ViewShellBase& rBase ) + : PanelLayout(pParent, "TableDesignPanel", + "modules/simpress/ui/tabledesignpanel.ui") + , m_xImpl(new TableDesignWidget(*m_xBuilder, rBase)) + { + } + virtual css::ui::LayoutSize GetHeightForWidth(const sal_Int32 /*nWidth*/) override + { + sal_Int32 nMinimumHeight = get_preferred_size().Height(); + return css::ui::LayoutSize(nMinimumHeight, -1, nMinimumHeight); + } +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/TemplateScanner.hxx b/sd/source/ui/inc/TemplateScanner.hxx new file mode 100644 index 000000000..034f000a6 --- /dev/null +++ b/sd/source/ui/inc/TemplateScanner.hxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "tools/AsynchronousTask.hxx" +#include +#include + +#include +#include + +namespace com::sun::star::ucb +{ +class XContent; +class XCommandEnvironment; +} + +namespace com::sun::star::sdbc +{ +class XResultSet; +} + +namespace sd +{ +/** Representation of a template or layout file. +*/ +class TemplateEntry +{ +public: + TemplateEntry(const OUString& rsTitle, const OUString& rsPath) + : msTitle(rsTitle) + , msPath(rsPath) + { + } + + OUString msTitle; + OUString msPath; +}; + +/** This class scans the template folders for impress templates. There are + two ways to use this class. + 1. The old and deprecated way is to call Scan() to scan all templates + and collect the supported ones in a tree structure. This structure is + returned by GetFolderList(). + 2. The new way implements the AsynchronousTask interface. Call + RunNextStep() as long HasNextStep() returns . After every step + GetLastAddedEntry() returns the template that was scanned (and has a + supported format) last. When a step does not add a new template then + the value of the previous step is returned. +*/ +class TemplateScanner final : public ::sd::tools::AsynchronousTask +{ +public: + /** Create a new template scanner and prepare but do not execute the scanning. + */ + TemplateScanner(); + + /** The destructor deletes any remaining entries of the local list of + templates. + */ + virtual ~TemplateScanner(); + + /** Implementation of the AsynchronousTask interface method. + */ + virtual void RunNextStep() override; + + /** Implementation of the AsynchronousTask interface method. + */ + virtual bool HasNextStep() override; + + /** Return the TemplateDir object that was last added to + mpTemplateEntries. + @return + is returned either before the template scanning is + started or after it has ended. + */ + const TemplateEntry* GetLastAddedEntry() const + { + return mpTemplateEntries.empty() ? nullptr : mpTemplateEntries.back().get(); + } + +private: + /** The current state determines which step will be executed next by + RunNextStep(). + */ + enum State + { + INITIALIZE_SCANNING, + INITIALIZE_FOLDER_SCANNING, + GATHER_FOLDER_LIST, + SCAN_FOLDER, + INITIALIZE_ENTRY_SCAN, + SCAN_ENTRY, + DONE, + ERROR + }; + State meState; + + ::ucbhelper::Content maFolderContent; + ::std::vector> mpTemplateEntries; + + /** The folders that are collected by GatherFolderList(). + */ + class FolderDescriptorList; + std::unique_ptr mpFolderDescriptors; + + /** Set of state variables used by the methods + InitializeFolderScanning(), GatherFolderList(), ScanFolder(), + InitializeEntryScanning(), and ScanEntry(). + */ + css::uno::Reference mxTemplateRoot; + css::uno::Reference mxFolderEnvironment; + css::uno::Reference mxEntryEnvironment; + css::uno::Reference mxFolderResultSet; + css::uno::Reference mxEntryResultSet; + + /** Obtain the root folder of the template folder hierarchy. The result + is stored in mxTemplateRoot for later use. + */ + State GetTemplateRoot(); + + /** Initialize the scanning of folders. This is called exactly once. + @return + Returns one of the two states ERROR or GATHER_FOLDER_LIST. + */ + State InitializeFolderScanning(); + + /** Collect all available top-level folders in an ordered list which can + then be processed by ScanFolder(). + @return + Returns one of the two states ERROR or SCAN_FOLDER. + */ + State GatherFolderList(); + + /** From the list of top-level folders collected by GatherFolderList() + the one with highest priority is processed. + @return + Returns one of the states ERROR, DONE, or INITIALIZE_ENTRY_SCAN. + */ + State ScanFolder(); + + /** Initialize the scanning of entries of a top-level folder. + @return + Returns one of the states ERROR or SCAN_ENTRY. + */ + State InitializeEntryScanning(); + + /** Scan one entry. When this entry matches the recognized template + types it is appended to the result set. + @return + Returns one of the states ERROR, SCAN_ENTRY, or SCAN_FOLDER. + */ + State ScanEntry(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/TextObjectBar.hxx b/sd/source/ui/inc/TextObjectBar.hxx new file mode 100644 index 000000000..61394834f --- /dev/null +++ b/sd/source/ui/inc/TextObjectBar.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd { + +class View; +class ViewShell; + +class TextObjectBar final + : public SfxShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDDRAWTEXTOBJECTBAR) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + TextObjectBar ( + ViewShell* pSdViewShell, + SfxItemPool& rItemPool, + ::sd::View* pSdView); + virtual ~TextObjectBar() override; + + void GetAttrState( SfxItemSet& rSet ); + void GetCharState( SfxItemSet& rSet ); + void Execute( SfxRequest &rReq ); + +private: + ViewShell* mpViewShell; + ::sd::View* mpView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ToolBarManager.hxx b/sd/source/ui/inc/ToolBarManager.hxx new file mode 100644 index 000000000..45f4532fb --- /dev/null +++ b/sd/source/ui/inc/ToolBarManager.hxx @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ShellFactory.hxx" +#include + +#include +#include + +class SdrView; +namespace sd { class ViewShell; } +namespace sd::tools { class EventMultiplexer; } + +namespace sd { + +class ViewShellBase; +class ViewShellManager; + +/** Manage the set of visible tool bars (and object bars). Usually they + belong to the current view in the center pane. + + Tool bars are managed in groups. Each group can be set, reset, or + modified independently of the others. This allows for instance to + replace the toolbars associated with the current function independently + from those associated with the main view. + + The ToolBarManager has two high level methods which contain the + knowledge about which tool bars to show in a specific context. + When the view in the center pane changes then MainViewShellChanged() + sets up the tool bars for the new view. On changes of the selection the + SelectionHasChanged() method shows the tool bars for the new context. + + The update of the actually visible tool bars to the set currently + required by the main view shell and its functions is divided into two + parts, PreUpdate() and PostUpdate(). This are to be called before + respectively after the update of the view shell stack. The reason for + this is to save time by not updating tool bars that will not be visible + in a short time on a view shell switch. +*/ +class ToolBarManager + : public std::enable_shared_from_this +{ +public: + /** Use this method instead of the constructor to create new objects of + this class. + */ + static std::shared_ptr Create ( + ViewShellBase& rBase, + const std::shared_ptr& rpMultiplexer, + const std::shared_ptr& rpViewShellManager); + + ~ToolBarManager(); + + /** Call this method prior to the destructor to prevent the + ToolBarManager from accessing the ViewShellManager or the + XLayoutManager when those are possibly not well and alive anymore + (like during the destruction of the ViewShellBase.) + */ + void Shutdown(); + + /** When the view in the center pane changes then this method sets up + the initial set of tool bars for the new view. + The ToolBarManager listens for view switching itself and then calls + MainViewShellChanged(). Calling this method from the outside should + not be necessary. + @param nShellType + The type of the new main view shell. + */ + void MainViewShellChanged (); + void MainViewShellChanged (const ViewShell& rMainViewShell); + + /** Call this method when the selection has changed to update the more + temporary tool bars (those in the ToolBarGroup::Function group.) + */ + void SelectionHasChanged ( + const ViewShell& rViewShell, + const SdrView& rView); + + /** The set of tool bars that are handled by this manager class. + */ + constexpr static OUStringLiteral msToolBar = u"toolbar"; // Draw_Toolbox_Sd, 23011 + constexpr static OUStringLiteral msOptionsToolBar = u"optionsbar"; + // Draw_Options_Toolbox, 23020 + constexpr static OUStringLiteral msCommonTaskToolBar = u"commontaskbar"; + // Draw_CommonTask_Toolbox, 23021 + constexpr static OUStringLiteral msViewerToolBar = u"viewerbar"; // Draw_Viewer_Toolbox, 23023 + constexpr static OUStringLiteral msSlideSorterToolBar = u"slideviewtoolbar"; + // Slide_Toolbox, 23012 + constexpr static OUStringLiteral msSlideSorterObjectBar = u"slideviewobjectbar"; + // Slide_Obj_Toolbox, 23014 + constexpr static OUStringLiteral msOutlineToolBar = u"outlinetoolbar"; // Outline_Toolbox, 23017 + constexpr static OUStringLiteral msMasterViewToolBar = u"masterviewtoolbar"; + // SID_MASTERPAGE, 27053 + constexpr static OUStringLiteral msDrawingObjectToolBar = u"drawingobjectbar"; + // Draw_Obj_Toolbox, 23013 + constexpr static OUStringLiteral msGluePointsToolBar = u"gluepointsobjectbar"; + // Gluepoints_Toolbox, 23019 + constexpr static OUStringLiteral msTextObjectBar = u"textobjectbar"; + // Draw_Text_Toolbox_Sd, 23016 + constexpr static OUStringLiteral msBezierObjectBar = u"bezierobjectbar"; + // Bezier_Toolbox_Sd, 23015 + constexpr static OUStringLiteral msGraphicObjectBar = u"graphicobjectbar"; + // Draw_Graf_Toolbox, 23030 + constexpr static OUStringLiteral msMediaObjectBar = u"mediaobjectbar"; + // Draw_Media_Toolbox, 23031 + constexpr static OUStringLiteral msTableObjectBar = u"tableobjectbar"; + // Draw_Table_Toolbox, 23018 + + /** The set of tool bar groups. + */ + enum class ToolBarGroup { + Permanent, + Function, + CommonTask, + MasterMode, + LAST = MasterMode + }; + + /** Reset the set of visible object bars in the specified group. Tool + bars in other groups are not affected. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + Only the tool bars in this group are rest. + */ + void ResetToolBars (ToolBarGroup eGroup); + + /** Reset all tool bars, regardless of the group they belong to. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + */ + void ResetAllToolBars(); + + /** Add the tool bar with the given name to the specified group of tool + bars. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + The new tool bar is added to this group. + @param rsToolBarName + The base name of the tool bar. A proper prefix (like + private:resource/toolbar/) is added. The name may be one of the + ones defined above. Other names are allowed as well. + */ + void AddToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName); + + /** Add the tool bar shell to the shell stack. This method basically + forwards the call to the ViewShellManager. + For some tool bar shells additional tool bars are made visible. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + The group is used for the actual tool bars. + @param nToolBarId + Id of the tool bar shell. + */ + void AddToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId); + + /** Remove the tool bar with the given name from the specified group. + If the tool bar is not visible then nothing happens. + If the tool bar is a member of another group then nothing happens + either. + */ + void RemoveToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName); + + /** This is basically a shortcut for ResetToolBars(),AddToolBar(). The + main difference is, that all sub shells of the specified parent + shell are deactivated as well. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + The new tool bar is added to this group. + @param rsToolBarName + The base name of the tool bar. A proper prefix (like + private:resource/toolbar/) is added. The name may be one of the + ones defined above. Other names are allowed as well. + */ + void SetToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName); + + /** This is basically a shortcut for ResetToolBars(),AddToolBar(). The + main difference is, that all sub shells of the specified parent + shell are deactivated as well. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param rParentShell + When this shell is not the main view then the method returns + immediately. + @param eGroup + The group is currently not used. + @param nToolBarId + Id of the tool bar shell. + */ + void SetToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId); + + void PreUpdate(); + + /** Request an update of the active tool bars. The update is made + asynchronously. + */ + void RequestUpdate(); + + /** This is a hint for the ToolBarManager to improve the performance + when it updates its tool bars when its own lock is released. Taking + control of the release of the update lock of the ViewShellManager + avoids some shell stack modifications and tool bar updates. + */ + void LockViewShellManager(); + + /** Use this class to prevent the visible tool bars from being updated + (and thus causing repaints and GUI rearrangements) when several tool + bar operations are made in a row. + */ + class UpdateLock { public: + UpdateLock(const std::shared_ptr& rpManager) + : mpManager(rpManager) { mpManager->LockUpdate(); } + ~UpdateLock() COVERITY_NOEXCEPT_FALSE { mpManager->UnlockUpdate(); } + private: + std::shared_ptr mpManager; + }; + friend class UpdateLock; + + void ToolBarsDestroyed(); + +private: + class Implementation; + std::unique_ptr mpImpl; + + /** The ViewShellBase is used to get the XLayoutManager and to determine + the plug in mode. + */ + ToolBarManager(); + + void LockUpdate(); + void UnlockUpdate(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/View.hxx b/sd/source/ui/inc/View.hxx new file mode 100644 index 000000000..4e530e3f9 --- /dev/null +++ b/sd/source/ui/inc/View.hxx @@ -0,0 +1,300 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "smarttag.hxx" +#include "fusearch.hxx" + +class SdDrawDocument; +class SdPage; +class SdrOle2Obj; +class SdrGrafObj; +class SdrMediaObj; +class OutputDevice; +class ImageMap; +class Graphic; +class SdrOutliner; + +namespace avmedia { class PlayerListener; } + +namespace sd { + +class DrawDocShell; +class ViewShell; +class Window; +class ViewClipboard; + +//For master view we want to force that master +//textboxes have readonly text, because the +//text is the auto-generated click-here-to-edit +//and it doesn't help to change it +class OutlinerMasterViewFilter +{ +private: + SdrOutliner *m_pOutl; + bool m_bReadOnly; +public: + OutlinerMasterViewFilter() + : m_pOutl(nullptr) + , m_bReadOnly(false) + { + } + void Start(SdrOutliner *pOutl); + void End(); +}; + +class SearchContext +{ +private: + rtl::Reference maFunctionSearch; + +public: + rtl::Reference& getFunctionSearch() + { + return maFunctionSearch; + } + + void setSearchFunction(rtl::Reference const & xFunction) + { + resetSearchFunction(); + maFunctionSearch = xFunction; + } + + void resetSearchFunction() + { + if (maFunctionSearch.is()) + maFunctionSearch->Dispose(); + } +}; + +class SAL_DLLPUBLIC_RTTI View : public FmFormView +{ +public: + + View ( + SdDrawDocument& rDrawDoc, + OutputDevice* pOutDev, + ViewShell* pViewSh=nullptr); + virtual ~View() override; + + void CompleteRedraw( OutputDevice* pOutDev, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector = nullptr) override; + + virtual void GetAttributes( SfxItemSet& rTargetSet, bool bOnlyHardAttr = false ) const; + virtual bool SetAttributes(const SfxItemSet& rSet, bool bReplaceAll = false, bool bSlide = false, bool bMaster = false); + virtual void MarkListHasChanged() override; + void SelectAll(); + void DoCut(); + void DoCopy(); + void DoPaste(::sd::Window* pWindow=nullptr); + virtual void DoConnect(SdrOle2Obj* pOleObj) override; + virtual bool SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr = false); + void StartDrag( const Point& rStartPos, vcl::Window* pWindow ); + virtual void DragFinished( sal_Int8 nDropAction ); + virtual sal_Int8 AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + SdrLayerID nLayer); + virtual sal_Int8 ExecuteDrop ( + const ExecuteDropEvent& rEvt, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer); + + css::uno::Reference + CreateClipboardDataObject (); + css::uno::Reference + CreateDragDataObject (::sd::View*, vcl::Window& rWindow, + const Point& rDragPos); + css::uno::Reference + CreateSelectionDataObject (::sd::View*); + + // update clipboard to what is selected + void UpdateSelectionClipboard(); + + // release content of clipboard, if we own the content + void ClearSelectionClipboard(); + + DrawDocShell* GetDocSh() const { return mpDocSh; } + inline SdDrawDocument& GetDoc() const; + ViewShell* GetViewShell() const { return mpViewSh; } + SfxViewShell* GetSfxViewShell() const override; + + // Create a local UndoManager + std::unique_ptr createLocalTextUndoManager() override; + + virtual bool SdrBeginTextEdit(SdrObject* pObj, SdrPageView* pPV = nullptr, vcl::Window* pWin = nullptr, bool bIsNewObj = false, + SdrOutliner* pGivenOutliner = nullptr, OutlinerView* pGivenOutlinerView = nullptr, + bool bDontDeleteOutliner = false, bool bOnlyOneView = false, bool bGrabFocus = true) override; + + virtual SdrEndTextEditKind SdrEndTextEdit(bool bDontDeleteReally = false) override; + + bool RestoreDefaultText( SdrTextObj* pTextObj ); + + bool InsertData( const TransferableDataHelper& rDataHelper, + const Point& rPos, sal_Int8& rDnDAction, bool bDrag, + SotClipboardFormatId nFormat = SotClipboardFormatId::NONE, + sal_uInt16 nPage = SDRPAGE_NOTFOUND, SdrLayerID nLayer = SDRLAYER_NOTFOUND ); + /** gets the metafile from the given transferable helper and insert it as a graphic shape. + @param bOptimize if set to true, the metafile is analyzed and if only one bitmap action is + present, then is inserted as a single graphic. + */ + bool InsertMetaFile( const TransferableDataHelper& rDataHelper, + const Point& rInsertPos, + ImageMap const * pImageMap, bool bOptimize ); + SdrGrafObj* InsertGraphic( const Graphic& rGraphic, + sal_Int8& rAction, const Point& rPos, + SdrObject* pSelectedObj, ImageMap const * pImageMap ); + void InsertMediaURL( const OUString& rMediaURL, sal_Int8& rAction, + const Point& rPos, const Size& rSize, + bool const bLink ); + SdrMediaObj* InsertMediaObj( const OUString& rURL, const OUString& rMimeType, sal_Int8& rAction, + const Point& rPos, const Size& rSize ); + + bool PasteRTFTable( const ::tools::SvRef& xStm, SdrPage* pPage, SdrInsertFlags nPasteOptions ); + + bool IsPresObjSelected(bool bOnPage = true, bool bOnMasterPage = true, bool bCheckPresObjListOnly = false, bool bCheckLayoutOnly = false) const; + + void SetMarkedOriginalSize(); + + bool IsMorphingAllowed() const; + bool IsVectorizeAllowed() const; + + virtual SfxStyleSheet* GetStyleSheet() const; + + /** return parameter: + pExchangeList == NULL -> all names are unique + bNameOK == false -> cancel by user + nType == 0 -> pages + nType == 1 -> objects + nType == 2 -> pages and objects */ + + bool GetExchangeList( std::vector &rExchangeList, + std::vector &rBookmarkList, + const sal_uInt16 nType ); + + virtual void onAccessibilityOptionsChanged() override; + + /** returns true if we have an undo manager and there is an open list undo action */ + bool isRecordingUndo() const; + + virtual void AddCustomHdl() override; + + SmartTagSet& getSmartTags() { return maSmartTags; } + void updateHandles(); + + virtual SdrViewContext GetContext() const override; + virtual bool HasMarkablePoints() const override; + virtual sal_Int32 GetMarkablePointCount() const override; + virtual bool HasMarkedPoints() const override; + virtual bool MarkPoint(SdrHdl& rHdl, bool bUnmark=false) override; + virtual void CheckPossibilities() override; + virtual bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) override; + using SdrMarkView::MarkPoints; + + bool ShouldToggleOn( + const bool bBulletOnOffMode, + const bool bNormalBullet); + + /** change the bullets/numbering of the marked objects + + @param bToggle + true: just toggle the current bullets/numbering on --> off resp. off --> on + + @param bHandleBullets + true: handle bullets + false: handle numbering + + @param pNumRule + numbering rule which needs to be applied. can be 0. + */ + void ChangeMarkedObjectsBulletsNumbering( + const bool bToggle, + const bool bHandleBullets, + const SvxNumRule* pNumRule); + + void SetPossibilitiesDirty() { m_bPossibilitiesDirty = true; } + void SetMoveAllowed( bool bSet ) { m_bMoveAllowed = bSet; } + void SetMoveProtected( bool bSet ) { m_bMoveProtect = bSet; } + void SetResizeFreeAllowed( bool bSet ) { m_bResizeFreeAllowed = bSet; } + void SetResizePropAllowed( bool bSet ) { m_bResizePropAllowed = bSet; } + void SetResizeProtected( bool bSet ) { m_bResizeProtect = bSet; } + + SdrObject* GetEmptyPresentationObject( PresObjKind eKind ); + SdPage* GetPage(); + SdrObject* GetSelectedSingleObject(SdPage const * pPage); + void SetAuthor(const OUString& rAuthor) { m_sAuthor = rAuthor; } + const OUString& GetAuthor() const { return m_sAuthor; } + + SearchContext& getSearchContext() { return maSearchContext; } +protected: + DECL_DLLPRIVATE_LINK( OnParagraphInsertedHdl, ::Outliner::ParagraphHdlParam, void ); + DECL_DLLPRIVATE_LINK( OnParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, void ); + + virtual void OnBeginPasteOrDrop( PasteOrDropInfos* pInfo ) override; + virtual void OnEndPasteOrDrop( PasteOrDropInfos* pInfo ) override; + + SdDrawDocument& mrDoc; + DrawDocShell* mpDocSh; + ViewShell* mpViewSh; + std::unique_ptr mpDragSrcMarkList; + SdrObject* mpDropMarkerObj; + std::unique_ptr mpDropMarker; + sal_uInt16 mnDragSrcPgNum; + Point maDropPos; + ::std::vector maDropFileVector; + sal_Int8 mnAction; + Idle maDropErrorIdle; + Idle maDropInsertFileIdle; + rtl::Reference mxDropMediaSizeListener; + sal_uInt16 mnLockRedrawSmph; + bool mbIsDropAllowed; + + DECL_DLLPRIVATE_LINK( DropErrorHdl, Timer*, void ); + DECL_DLLPRIVATE_LINK( DropInsertFileHdl, Timer*, void ); + DECL_DLLPRIVATE_LINK( ExecuteNavigatorDrop, void*, void ); + + void ImplClearDrawDropMarker(); + + SmartTagSet maSmartTags; + +private: + ::std::unique_ptr mpClipboard; + OutlinerMasterViewFilter maMasterViewFilter; + SearchContext maSearchContext; + + OUString m_sAuthor; +}; + +SdDrawDocument& View::GetDoc() const +{ + return mrDoc; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewClipboard.hxx b/sd/source/ui/inc/ViewClipboard.hxx new file mode 100644 index 000000000..f16c0ad33 --- /dev/null +++ b/sd/source/ui/inc/ViewClipboard.hxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SdPage; +class SdTransferable; + +namespace sd +{ +class View; + +/** Handle clipboard related tasks for the draw view. +*/ +class ViewClipboard +{ +public: + ViewClipboard(::sd::View& rView); + virtual ~ViewClipboard(); + + /** Handle the drop of a drag-and-drop action where the transferable + contains a set of pages. + */ + void HandlePageDrop(const SdTransferable& rTransferable); + +protected: + ::sd::View& mrView; + + /** Return the first master page of the given transferable. When the + bookmark list of the transferable contains at least one non-master + page then NULL is returned. + */ + static SdPage* GetFirstMasterPage(const SdTransferable& rTransferable); + + /** Assign the (first) master page of the given transferable to the + (...) slide. + */ + void AssignMasterPage(const SdTransferable& rTransferable, SdPage const* pMasterPage); + + /** Return an index of a page after which the pages of the transferable + are to be inserted into the target document. + */ + virtual sal_uInt16 DetermineInsertPosition(); + + /** Insert the slides in the given transferable behind the last selected + slide or, when the selection is empty, behind the last slide. + @param rTransferable + This transferable defines which pages to insert. + @param nInsertPosition + The pages of the transferable will be inserted behind the page + with this index. + @return + Returns the number of inserted slides. + */ + sal_uInt16 InsertSlides(const SdTransferable& rTransferable, sal_uInt16 nInsertPosition); +}; + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShell.hxx b/sd/source/ui/inc/ViewShell.hxx new file mode 100644 index 000000000..1eeede9e2 --- /dev/null +++ b/sd/source/ui/inc/ViewShell.hxx @@ -0,0 +1,559 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include "View.hxx" +#include "fupoor.hxx" +#include + +#include + +class SdPage; +class SvxRuler; +class SdrOle2Obj; // for the ones, who have undefined parts of SVDRAW +class SdDrawDocument; +class SvxNumBulletItem; + +namespace weld +{ + class Window; +} + +namespace com::sun::star::drawing { class XDrawSubController; } + +namespace sd { + +class DrawDocShell; +class FrameView; +class LayerTabBar; +class ViewShellBase; +class Window; +class WindowUpdater; +class ZoomList; + +#undef OUTPUT_DRAWMODE_COLOR +#undef OUTPUT_DRAWMODE_CONTRAST + +const DrawModeFlags OUTPUT_DRAWMODE_COLOR = DrawModeFlags::Default; +const DrawModeFlags OUTPUT_DRAWMODE_GRAYSCALE + = DrawModeFlags::GrayLine | DrawModeFlags::GrayFill + | DrawModeFlags::BlackText | DrawModeFlags::GrayBitmap + | DrawModeFlags::GrayGradient; +const DrawModeFlags OUTPUT_DRAWMODE_BLACKWHITE + = DrawModeFlags::BlackLine | DrawModeFlags::BlackText + | DrawModeFlags::WhiteFill | DrawModeFlags::GrayBitmap + | DrawModeFlags::WhiteGradient; +const DrawModeFlags OUTPUT_DRAWMODE_CONTRAST + = DrawModeFlags::SettingsLine | DrawModeFlags::SettingsFill + | DrawModeFlags::SettingsText | DrawModeFlags::SettingsGradient; + +/** Base class of the stacked shell hierarchy. + +

Despite its name this class is not a descendant of SfxViewShell + but of SfxShell. Its name expresses the fact that it acts like a + view shell. Being a stacked shell rather than being an actual view shell + there can be several instances of this class that +

    +
  • all are based on the same view shell and thus show the same + document and share common view functionality and
  • +
  • are all visible at the same time and live in the same + frame.
  • +

      + +

      This class replaces the former ViewShell class.

      +*/ +class SAL_DLLPUBLIC_RTTI ViewShell + : public SfxShell +{ +public: + enum ShellType { + ST_NONE, + ST_DRAW, // The Draw application. + ST_IMPRESS, // Main view of the Impress application. + ST_NOTES, + ST_HANDOUT, + ST_OUTLINE, + ST_SLIDE_SORTER, + ST_PRESENTATION, + ST_SIDEBAR + }; + static const int MAX_HSPLIT_CNT = 1; + static const int MAX_VSPLIT_CNT = 1; + static const int MIN_SCROLLBAR_SIZE = 50; + + + ViewShell ( + vcl::Window* pParentWindow, + ViewShellBase& rViewShellBase); + virtual ~ViewShell() override; + + /** The Init method has to be called from the outside directly + after a new object of this class has been created. It can be + used for that part of the initialisation that can be run only + after the creation of the new object is finished. This + includes registration as listener at event broadcasters. + + Derived classes should call this method at the head of their + Init() methods. + @param bIsMainViewShell + This flag tells the Init() method whether the new ViewShell will + be the main view shell. + */ + virtual void Init (bool bIsMainViewShell); + + /** The Exit() method has to be called before the destructor so that the + view shell is still a valid object and can safely call methods that + rely on that. + */ + void Exit(); + + void Cancel(); + + /** Return the window that is the parent of all controls of this view + shell. This may or may not be the window of the frame. + */ + vcl::Window* GetParentWindow() const { return mpParentWindow; } + + sd::Window* GetContentWindow() const; + + ::sd::View* GetView() const { return mpView; } + inline SdrView* GetDrawView() const; + SD_DLLPUBLIC DrawDocShell* GetDocSh() const; + + SdDrawDocument* GetDoc() const; + + SD_DLLPUBLIC SfxViewFrame* GetViewFrame() const; + + /** The active window is usually the mpContentWindow. When there is a + show running then the active window is a ShowWindow. + */ + ::sd::Window* GetActiveWindow() const { return mpActiveWindow;} + SD_DLLPUBLIC weld::Window* GetFrameWeld() const; + + /** Set the active window. When the shell is displayed in the center + pane then the window of the ViewShellBase is also set to the given + window. + */ + void SetActiveWindow (::sd::Window* pWindow); + + /** Return the rectangle that encloses all windows of the view. That + excludes the controls in the frame like rulers, scroll bars, tab + bar, and buttons. + @return + The rectangle is returned in screen coordinates, i.e. pixel + values relative to the upper left corner of the screen?. + */ + const ::tools::Rectangle& GetAllWindowRect(); + + // Mouse- & Key-Events + virtual void PrePaint(); + virtual void Paint (const ::tools::Rectangle& rRect, ::sd::Window* pWin); + virtual bool KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin); + virtual void MouseMove(const MouseEvent& rMEvt, ::sd::Window* pWin); + virtual void MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin); + virtual void MouseButtonDown(const MouseEvent& rMEvt, ::sd::Window* pWin); + virtual void Command(const CommandEvent& rCEvt, ::sd::Window* pWin); + bool RequestHelp( const HelpEvent& rEvt ); + bool Notify( NotifyEvent const & rNEvt, ::sd::Window* pWin ); + + bool HandleScrollCommand(const CommandEvent& rCEvt, ::sd::Window* pWin); + + void SetUIUnit(FieldUnit eUnit); + void SetDefTabHRuler( sal_uInt16 nDefTab ); + + const SvxNumBulletItem* GetNumBulletItem(SfxItemSet& aNewAttr, TypedWhichId& nNumItemId); + + bool HasRuler() const { return mbHasRulers;} + void SetRuler(bool bRuler); + // Hides horizontal, vertical scrollbar as well as scrollbox + void SetScrollBarsVisible(bool bVisible); + + /** Set internal values of all scroll bars that determine thumb size and + position. The external values like size and position of the scroll + bar controls are not modified. + */ + virtual void UpdateScrollBars(); + void Scroll(::tools::Long nX, ::tools::Long nY); + void ScrollLines(::tools::Long nX, ::tools::Long nY); + virtual void SetZoom(::tools::Long nZoom); + ::tools::Long GetZoom() const; + virtual void SetZoomRect(const ::tools::Rectangle& rZoomRect); + void InitWindows(const Point& rViewOrigin, const Size& rViewSize, + const Point& rWinPos, bool bUpdate = false); + void InvalidateWindows(); + /** This method is still used by the OutlineViewShell to update the + model according to the content of the outline view. This in turn + updates the previews in the slide sorter. + */ + virtual void UpdatePreview (SdPage* pPage); + + void DrawMarkRect(const ::tools::Rectangle& rRect) const; + + void ExecReq( SfxRequest &rReq ); + + ZoomList* GetZoomList() { return mpZoomList.get();} + + FrameView* GetFrameView() { return mpFrameView; } + /** Setting a frame view triggers ReadFrameViewData() for the new + frame. + @param pFrameView + The new frame view that replaces the old one. + */ + void SetFrameView (FrameView* pFrameView); + virtual void ReadFrameViewData(FrameView* pView); + virtual void WriteFrameViewData(); + void WriteUserData(); + void ReadUserData(); + + virtual bool ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb); + + /** @returns + current or selected page or 0. This method + will fail in master page mode. + + @deprecated, please use getCurrentPage(); + */ + virtual SdPage* GetActualPage() = 0; + + /** @returns + current or selected page or 0. + */ + virtual SdPage* getCurrentPage() const = 0; + + const rtl::Reference& GetOldFunction() const { return mxOldFunction; } + bool HasOldFunction() const { return mxOldFunction.is(); } + const rtl::Reference& GetCurrentFunction() const { return mxCurrentFunction; } + bool HasCurrentFunction( sal_uInt16 nSID ) { return mxCurrentFunction.is() && (mxCurrentFunction->GetSlotID() == nSID ); } + bool HasCurrentFunction() const { return mxCurrentFunction.is(); } + + void SetCurrentFunction(const rtl::Reference& xFunction); + void SetOldFunction(const rtl::Reference& xFunction); + void DeactivateCurrentFunction( bool bPermanent = false ); + + void SetPageSizeAndBorder(PageKind ePageKind, const Size& rNewSize, + ::tools::Long nLeft, ::tools::Long nRight, ::tools::Long nUpper, ::tools::Long nLower, + bool bScaleAll, Orientation eOrient, sal_uInt16 nPaperBin, + bool bBackgroundFullSize ); + + void SetStartShowWithDialog( bool bIn ) { mbStartShowWithDialog = bIn; } + bool IsStartShowWithDialog() const { return mbStartShowWithDialog; } + + sal_uInt16 GetPrintedHandoutPageNum() const { return mnPrintedHandoutPageNum; } + void SetPrintedHandoutPageNum (sal_uInt16 nPageNumber) {mnPrintedHandoutPageNum=nPageNumber; } + + sal_uInt16 GetPrintedHandoutPageCount() const { return mnPrintedHandoutPageCount; } + void SetPrintedHandoutPageCount (sal_uInt16 nPageCount) {mnPrintedHandoutPageCount=nPageCount; } + + virtual bool PrepareClose( bool bUI = true ); + + void GetMenuState(SfxItemSet& rSet); + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt, DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ); + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt, DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ); + + virtual void WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& ); + virtual void ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& ); + + /** this method is called when the visible area of the view from this viewshell is changed */ + virtual void VisAreaChanged(const ::tools::Rectangle& rRect); + + /** Create an accessible object representing the specified window. + Override this method to provide view mode specific objects. The + default implementation returns an empty reference. + @param pWindow + Make the document displayed in this window accessible. + @return + This default implementation returns an empty reference. + */ + virtual css::uno::Reference + CreateAccessibleDocumentView (::sd::Window* pWindow); + + virtual void SwitchViewFireFocus( const css::uno::Reference< css::accessibility::XAccessible >& xAcc ); + void SwitchActiveViewFireFocus( ); + // Move these two methods from DrawViewShell to enable slide show view + void NotifyAccUpdate(); + void fireSwitchCurrentPage(sal_Int32 pageIndex); + void SetWinViewPos(const Point& rWinPos); + Point const & GetWinViewPos() const; + Point const & GetViewOrigin() const; + + /** Return the window updater of this view shell. + @return + In rare circumstances the returned pointer may be , + i.e. when no memory is available anymore. + */ + ::sd::WindowUpdater* GetWindowUpdater() const; + + /** Return the border that is drawn around the actual document view. + The border contains typically rulers and scroll bars. + */ + SvBorder GetBorder(); + + /** Notify the view shell that its parent window has been resized. + The ViewShell places and resizes its UI elements accordingly. + The new size can be obtained from the parent window. + */ + virtual void Resize(); + + /** Set position and size of the GUI elements that are controlled by + the view shell like rulers and scroll bars as well as the actual + document view according to the position and size that were given + with the last Resize() call. + */ + virtual void ArrangeGUIElements(); + + // virtual void OuterResizePixel(const Point &rPos, const Size &rSize); + // virtual void InnerResizePixel(const Point &rPos, const Size &rSize); + + // Exported for unit test + SD_DLLPUBLIC ViewShellBase& GetViewShellBase() const; + + /** Return when the called view shell is the main sub shell of + its ViewShellBase object, i.e. is display in the center pane. This + convenience function is equivalent to comparing the this pointer to + the result of ViewShellBase::GetViewShell(PT_CENTER). + */ + bool IsMainViewShell() const; + + /** Set or reset the flag that indicates whether the called shell is the + one displayed in the center pane. By default this flag is set to + . For the main view shell it thus has to be set to . + */ + void SetIsMainViewShell (bool bIsMainViewShell); + + /** Return a sub controller that implements the view shell specific + part of the DrawController. + */ + virtual css::uno::Reference CreateSubController() = 0; + + /** Return the type of the shell. + */ + SD_DLLPUBLIC ShellType GetShellType() const; //Export for unit test + + /** This method is more or less an alias to Deactivate(). It is called + before an object of this class is taken from the stack of view + shells. + +

      When this method is not called before a view shell is taken from + a stack then the Deactivate() call from the SFX as a response to + RemoveSubShell() comes too late when the view shell is not on the + stack anymore.

      + */ + virtual void Shutdown(); + + /** This function is called from the underlying ViewShellBase + object to handle a verb execution request. + */ + virtual ErrCode DoVerb(sal_Int32 nVerb); + + virtual void UIActivating( SfxInPlaceClient* ); + virtual void UIDeactivated( SfxInPlaceClient* ); + + /** Show controls of the UI or hide them, depending on the given flag. + As a result the border is adapted. + */ + virtual void ShowUIControls (bool bVisible); + bool IsPageFlipMode() const; + + /** Set the given window as new parent window. This is not possible for + all views, so the return value tells the caller if the relocation + was successful. + */ + virtual bool RelocateToParentWindow (vcl::Window* pParentWindow); + + /** Depending on the given request create a new page or duplicate an + existing one. A new page is created behind the given slide. + @param rRequest + The request as passed to an Execute() method. Its arguments are + evaluated. Its slot id determines whether to create or + duplicate a slide. + @param pPage + This page is either duplicated or becomes the predecessor of the + new slide. If NULL a duplication request is ignored. A new + slide is inserted as first slide. + @param nInsertPosition + When -1 (the default) then insert after pPage. Otherwise insert + before the given index (of a standard page). + @return + The new slide is returned. If for some reason a new page can + not be created then NULL is returned. + */ + virtual SdPage* CreateOrDuplicatePage ( + SfxRequest& rRequest, + PageKind ePageKind, + SdPage* pPage, + const sal_Int32 nInsertPosition = -1); + + /// Allows adjusting the point or mark of the selection to a document coordinate. + void SetCursorMm100Position(const Point& rPosition, bool bPoint, bool bClearMark); + /// Gets the current selection + css::uno::Reference GetSelectionTransferrable() const; + /// Allows starting or ending a graphic move or resize action. + void SetGraphicMm100Position(bool bStart, const Point& rPosition); + + class Implementation; + +protected: + /** must be called in the beginning of each subclass d'tor. + disposes and clears both current and old function. */ + void DisposeFunctions(); + + friend class ViewShellBase; + + /** Window inside the rulers and scroll bars that shows a view of the + document. + */ + + VclPtr mpContentWindow; + + /// Horizontal scroll bar for the current slide is displayed when needed. + VclPtr mpHorizontalScrollBar; + /// Vertical scroll bar for whole document is always visible. + VclPtr mpVerticalScrollBar; + /// Horizontal ruler is not shown by default. + VclPtr mpHorizontalRuler; + /// Vertical ruler is not shown by default. + VclPtr mpVerticalRuler; + /// Filler of the little square enclosed by the two scroll bars. + VclPtr mpScrollBarBox; + /// Layer tab bar. + VclPtr mpLayerTabBar; + + /// This flag controls whether the rulers are visible. + bool mbHasRulers; + + /// The active window. + VclPtr< ::sd::Window> mpActiveWindow; + ::sd::View* mpView; + FrameView* mpFrameView; + + rtl::Reference mxCurrentFunction; + rtl::Reference mxOldFunction; + std::unique_ptr mpZoomList; + + Point maViewPos; + Size maViewSize; + Size maScrBarWH; + + bool mbStartShowWithDialog; // presentation is started by dialog + sal_uInt16 mnPrintedHandoutPageNum; // Page number of the handout page that is to be printed. + sal_uInt16 mnPrintedHandoutPageCount; // Page count of the handout pages that are to be printed. + + //af bool bPrintDirectSelected; // Print only selected objects in direct print + //afString sPageRange; // pagerange if selected objects in direct print + + /** Area covered by all windows, i.e. the area of the parent window + without the controls at the borders like rulers, scroll bars, tab + bar, buttons. + This rectangle may be set in window coordinates (i.e. pixel values + relative to the parent window). It is transformed by every call to + GetAllWindowRectangle() into screen coordinates (relative to the + upper left corner of the screen. + */ + ::tools::Rectangle maAllWindowRectangle; + + /// The type of the shell. Returned by GetShellType(). + ShellType meShellType; + + std::unique_ptr> mpImpl; + + // Support methods for centralized UNDO/REDO + virtual SfxUndoManager* ImpGetUndoManager() const; + void ImpGetUndoStrings(SfxItemSet &rSet) const; + void ImpGetRedoStrings(SfxItemSet &rSet) const; + void ImpSidUndo(SfxRequest& rReq); + void ImpSidRedo(SfxRequest& rReq); + + DECL_DLLPRIVATE_LINK( HScrollHdl, ScrollBar *, void ); + DECL_DLLPRIVATE_LINK( VScrollHdl, ScrollBar *, void ); + + // virtual scroll handler, here, derivative classes can add themselves here + virtual void VirtHScrollHdl(ScrollBar* pHScroll); + virtual void VirtVScrollHdl(ScrollBar* pVScroll); + + // virtual functions ruler handling + virtual VclPtr CreateHRuler(::sd::Window* pWin); + virtual VclPtr CreateVRuler(::sd::Window* pWin); + virtual void UpdateHRuler(); + virtual void UpdateVRuler(); + + virtual void Activate(bool IsMDIActivate) override; + virtual void Deactivate(bool IsMDIActivate) override; + + virtual void SetZoomFactor( const Fraction &rZoomX, + const Fraction &rZoomY ); + + /** + This must be called after the ctor, but before anything else. + It's the part of construction that is dependent + on showing the top-level window. + + Showing a window with a11y enabled causes various callbacks + to be triggered. + + Due to the "virtual methods are not virtual during constructors" + problem, this is a disaster to call from the ctor + + i.e. construct calls Show, and if a11y is enabled this + reenters the not-fully constructed object and calls + CreateAccessibleDocumentView, so if construct is called + from the ctor then if a derived class is constructed the base-case + CreateAccessibleDocumentView is used, not the derived + CreateAccessibleDocumentView. i.e. run smoketest under a11y with + debugging assertions enabled + */ + void doShow(); + +private: + VclPtr mpParentWindow; + /** This window updater is used to keep all relevant windows up to date + with reference to the digit language used to display digits in text + shapes. + */ + ::std::unique_ptr< ::sd::WindowUpdater> mpWindowUpdater; + + /** Code common to all constructors. It generally is a bad idea + to call this function from outside a constructor. + */ + void construct(); + + /** Create the rulers. + */ + void SetupRulers(); +}; + +SdrView* ViewShell::GetDrawView() const +{ + return static_cast(mpView); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShellBase.hxx b/sd/source/ui/inc/ViewShellBase.hxx new file mode 100644 index 000000000..eab26ec8a --- /dev/null +++ b/sd/source/ui/inc/ViewShellBase.hxx @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class SdDrawDocument; +class SfxRequest; + +namespace sd::tools { +class EventMultiplexer; +} + +namespace sd { + +class DrawController; +class DrawDocShell; +class FormShellManager; +class ToolBarManager; +class ViewShell; +class ViewShellManager; +class ViewTabBar; + +/** SfxViewShell descendant that the stacked Draw/Impress shells are + based on. + +

      The "base" part of the name does not mean that this is a base + class of some class hierarchy. It rather is the base of the + stacked shells.

      + +

      This class starts as a new and relatively small class. Over + time as much code as possible should be moved from the stacked + shells to this class.

      +*/ +class ViewShellBase + : public SfxViewShell +{ +public: + SFX_DECL_INTERFACE(SD_IF_SDVIEWSHELLBASE) + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + +public: + /** This constructor is used by the view factory of the SFX macros. + Note that LateInit() has to be called after the constructor + terminates and before doing anything else. + */ + ViewShellBase ( + SfxViewFrame *pFrame, + SfxViewShell* pOldShell); + + virtual ~ViewShellBase() override; + + /** This method is part of the object construction. It HAS to be called + after the constructor has created a new object. + */ + void LateInit (const OUString& rsDefaultView); + + std::shared_ptr const & GetViewShellManager() const; + + /** Return the main view shell stacked on the called ViewShellBase + object. This is usually the view shell displayed in the center + pane. + */ + std::shared_ptr GetMainViewShell() const; + + /** When given a view frame this static method returns the + corresponding sd::ViewShellBase object. + @return + When the SfxViewShell of the given frame is not a + ViewShellBase object then NULL is returned. + */ + static ViewShellBase* GetViewShellBase (SfxViewFrame const * pFrame); + + DrawDocShell* GetDocShell() const { return mpDocShell;} + SdDrawDocument* GetDocument() const { return mpDocument;} + + /** Callback function for general slot calls. At the moment these are + slots for switching the pane docking windows on and off. + */ + virtual void Execute (SfxRequest& rRequest); + + /** Callback function for retrieving item values related to certain + slots. This is the companion of Execute() and handles the slots + concerned with showing the pane docking windows. + */ + void GetState (SfxItemSet& rSet); + + /* override these from SfxViewShell */ + virtual OUString GetSelectionText(bool = false, bool bOnlyASample = false) override; + virtual bool HasSelection(bool = true ) const override; + + SvBorder GetBorder (bool bOuterResize); + virtual void InnerResizePixel (const Point& rOrigin, const Size& rSize, bool inplaceEditModeChange) override; + virtual void OuterResizePixel (const Point& rOrigin, const Size& rSize) override; + + /** This call is forwarded to the main sub-shell. + */ + virtual ErrCode DoVerb(sal_Int32 nVerb) override; + + /** Return a new renderer that can be used for example for printing the + document. + */ + virtual css::uno::Reference GetRenderable() override; + + /// Forwarded to the print manager. + virtual SfxPrinter* GetPrinter (bool bCreate = false) override; + + /// Forwarded to the print manager. + virtual sal_uInt16 SetPrinter ( + SfxPrinter* pNewPrinter, + SfxPrinterChangeFlags nDiffFlags = SFX_PRINTER_ALL) override; + + /// Forward methods to main sub shell. + virtual void WriteUserDataSequence ( + css::uno::Sequence< css::beans::PropertyValue >&) override; + + /** Pass the given properties to the main view shell. After that we + ensure that the right view shell type is displayed in the center + pane. + */ + virtual void ReadUserDataSequence ( + const css::uno::Sequence< css::beans::PropertyValue >&) override; + + virtual void UIActivating( SfxInPlaceClient* ) override; + virtual void UIDeactivated( SfxInPlaceClient* ) override; + virtual void Activate (bool IsMDIActivate) override; + using SfxViewShell::Deactivate; + virtual void SetZoomFactor ( + const Fraction &rZoomX, + const Fraction &rZoomY) override; + virtual bool PrepareClose (bool bUI = true) override; + virtual void WriteUserData (OUString&, bool bBrowse = false) override; + virtual void ReadUserData (const OUString&, bool bBrowse = false) override; + virtual SdrView* GetDrawView() const override; + + /** When is given, then the mouse shape is set to hour glass (or + whatever the busy shape looks like on the system.) + */ + void SetBusyState (bool bBusy); + + /** Call this method when the controls of this view shell or the + embedded sub shell need to be rearranged. This is necessary + e.g. when the border has been modified (UpdateBorder() calls this + method). + + This method is like ResizePixel() with no arguments. + */ + void Rearrange(); + + /** Update the border that is set with SfxViewShell::SetBorderPixel(). + This is done by adding the border used by the ViewShellBase itself + with the border used by the main view shell. + + @param bForce if true the borders are also updated if old border + and new border are same. + */ + void UpdateBorder ( bool bForce = false ); + + /** With this method the UI controls can be turned on or off. It is + used by the FuSlideShow to hide the UI controls while showing a + non-full-screen or in-window presentation in the center pane. + */ + void ShowUIControls (bool bVisible); + + /** Return an event multiplexer. It is a single class that forwards + events from various sources. This method must not be called before + LateInit() has terminated. + */ + std::shared_ptr const & GetEventMultiplexer() const; + + /** returns the complete area of the current view relative to the frame + window + */ + const ::tools::Rectangle& getClientRectangle() const; + + std::shared_ptr const & GetToolBarManager() const; + std::shared_ptr const & GetFormShellManager() const; + + DrawController& GetDrawController() const; + + void SetViewTabBar (const ::rtl::Reference& rViewTabBar); + + /** Return the window that is used by the main view shell to display its + view and other UI elements, like scroll bars and rulers. Ownership + of that window remains with the called ViewShellBase object. + */ + vcl::Window* GetViewWindow(); + + /** returns the ui descriptive name for the given uno slot. The result is taken from the configuration + and not cached, so do not use it excessive (f.e. in status updates) */ + OUString RetrieveLabelFromCommand( const OUString& aCmdURL ) const; + /// See SfxViewShell::getPart(). + int getPart() const override; + /// See SfxViewShell::NotifyCursor(). + void NotifyCursor(SfxViewShell* pViewShell) const override; + + void setLOKVisibleArea(const ::tools::Rectangle& rArea) { maLOKVisibleArea = rArea; } + virtual ::tools::Rectangle getLOKVisibleArea() const override { return maLOKVisibleArea; } + +protected: + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + + virtual void InitializeFramework(); + +private: + class Implementation; + std::unique_ptr mpImpl; + DrawDocShell* mpDocShell; + SdDrawDocument* mpDocument; + ::tools::Rectangle maLOKVisibleArea; + + /** Determine from the properties of the document shell the initial type + of the view shell in the center pane. We use this method to avoid + starting with the wrong type. When ReadUserDataSequence() is called + we check that the right type is active and change again if that is + not the case because something went wrong. + */ + OUString GetInitialViewShellType() const; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShellHint.hxx b/sd/source/ui/inc/ViewShellHint.hxx new file mode 100644 index 000000000..05a0c8328 --- /dev/null +++ b/sd/source/ui/inc/ViewShellHint.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd +{ +/** Local derivation of the SfxHint class that defines some hint ids that + are used by the ViewShell class and its descendants. +*/ +class ViewShellHint final : public SfxHint +{ +public: + enum HintId + { + // Indicate that a page resize is about to begin. + HINT_PAGE_RESIZE_START, + // Indicate that a page resize has been completed. + HINT_PAGE_RESIZE_END, + // Indicate that an edit mode change is about to begin. + HINT_CHANGE_EDIT_MODE_START, + // Indicate that an edit mode change has been completed. + HINT_CHANGE_EDIT_MODE_END, + + HINT_COMPLEX_MODEL_CHANGE_START, + HINT_COMPLEX_MODEL_CHANGE_END + }; + + ViewShellHint(HintId nHintId); + + HintId GetHintId() const { return meHintId; } + +private: + HintId meHintId; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShellImplementation.hxx b/sd/source/ui/inc/ViewShellImplementation.hxx new file mode 100644 index 000000000..b4a02c3d5 --- /dev/null +++ b/sd/source/ui/inc/ViewShellImplementation.hxx @@ -0,0 +1,150 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ViewShell.hxx" +#include "ViewShellManager.hxx" +#include "ToolBarManager.hxx" +#include +#include + +class SvxIMapDlg; + +namespace sd +{ +/** This class contains (will contain) the implementation of methods that + have not be accessible from the outside. +*/ +class ViewShell::Implementation +{ +public: + bool mbIsMainViewShell; + /// Set to true when the ViewShell::Init() method has been called. + bool mbIsInitialized; + /** Set to true while ViewShell::ArrangeGUIElements() is being + executed. It is used as guard against recursive execution. + */ + bool mbArrangeActive; + + /** Remember a link to the sub shell factory, so that it can be + unregistered at the ViewShellManager when a ViewShell is deleted. + */ + ViewShellManager::SharedShellFactory mpSubShellFactory; + + /** This update lock for the ToolBarManager exists in order to avoid + problems with tool bars being displayed while the mouse button is + pressed. With docked tool bars this can lead to a size change of + the view. This would change the relative mouse coordinates and thus + interpret every mouse click as move command. + */ + class ToolBarManagerLock + { + public: + /** Create a new instance. This allows the mpSelf member to be set + automatically. + */ + static std::shared_ptr + Create(const std::shared_ptr& rpManager); + /** Release the lock. When the UI is captured + (Application::IsUICaptured() returns ) then the lock is + released later asynchronously. + @param bForce + When this flag is then the lock is released even + when IsUICaptured() returns . + */ + void Release(bool bForce = false); + DECL_DLLPRIVATE_LINK(TimeoutCallback, Timer*, void); + + private: + ::std::unique_ptr> + mpLock; + /** The timer is used both as a safe guard to unlock the update lock + when Release() is not called explicitly. It is also used to + defer the release of the lock to a time when the UI is not + captured. + */ + Timer maTimer; + /** The shared_ptr to this allows the ToolBarManagerLock to control + its own lifetime. This, of course, does work only when no one + holds another shared_ptr longer than only temporary. + */ + std::shared_ptr mpSelf; + ToolBarManagerLock(const std::shared_ptr& rpManager); + ~ToolBarManagerLock(); + + class Deleter; + friend class Deleter; + }; + // The member is not a unique_ptr because it takes over its own life time + // control. + std::weak_ptr mpUpdateLockForMouse; + + Implementation(ViewShell& rViewShell); + ~Implementation() COVERITY_NOEXCEPT_FALSE; + + /** Process the SID_MODIFY slot. + */ + void ProcessModifyPageSlot(SfxRequest& rRequest, SdPage* pCurrentPage, PageKind ePageKind); + + /** Assign the given layout to the given page. This method is at the + moment merely a front end for ProcessModifyPageSlot. + @param pPage + If a NULL pointer is given then this call is ignored. + */ + void AssignLayout(SfxRequest const& rRequest, PageKind ePageKind); + + /** Determine the view id of the view shell. This corresponds to the + view id stored in the SfxViewFrame class. + + We can not use the view of that class because with the introduction + of the multi pane GUI we do not switch the SfxViewShell anymore when + switching the view in the center pane. The view id of the + SfxViewFrame is thus not modified and we can not set it from the + outside. + + The view id is still needed for the SFX to determine on start up + (e.g. after loading a document) which ViewShellBase sub class to + use. These sub classes--like OutlineViewShellBase--exist only to be + used by the SFX as factories. They only set the initial pane + configuration, nothing more. + + So what we do here in essence is to return one of the + ViewShellFactoryIds that can be used to select the factory that + creates the ViewShellBase subclass with the initial pane + configuration that has in the center pane a view shell of the same + type as mrViewShell. + */ + SfxInterfaceId GetViewId() const; + + /** Return a pointer to the image map dialog that is displayed in some + child window. + @return + Returns when the image map dialog is not available. + */ + static SvxIMapDlg* GetImageMapDialog(); + +private: + ViewShell& mrViewShell; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewShellManager.hxx b/sd/source/ui/inc/ViewShellManager.hxx new file mode 100644 index 000000000..a2c8f1ef2 --- /dev/null +++ b/sd/source/ui/inc/ViewShellManager.hxx @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "ShellFactory.hxx" +#include +#include + +class FmFormShell; +class SfxShell; + +namespace sd +{ +class ViewShell; +class ViewShellBase; + +/** The ViewShellManager has the responsibility to manage the view shells + and sub shells on the SFX shell stack. They form a two level hierarchy + (the underlying ViewShellBase, the only true SfxViewShell descendant, + forms a third level.) On the first level there are the view shells + (what formerly was called view shell, anyway; nowadays they are derived + from SfxShell) and shells for panes. On the second level there are sub + shells (also derived from SfxShell) that usually are tool bars. + +

      On the SFX shell stack the regular sub shells are placed above their + view shells. The FormShell is a special case. With the SetFormShell() + method it can be placed directly above or below one of the view + shells.

      + +

      Shells managed by this class are created by factories or are given + directly to Activate... methods. For the sub shells there is one + factory for every view shell. Factories are added or removed via the + (Add|Remove)SubShellFactory() methods. The FormShell is managed with the + factory of its view shell.

      +*/ +class ViewShellManager +{ +public: + typedef std::shared_ptr> SharedShellFactory; + + ViewShellManager(ViewShellBase& rBase); + + /** Before the destructor is called the method Shutdown() has to have + been called. + */ + ~ViewShellManager(); + + /** Tell a ViewShellManager object to prepare to be deleted, i.e. to + destroy all of its resources and to ignore all following calls. + Use this when the owner of the view shell manager is about being + destroyed but the view shell manager itself can not yet be deleted. + */ + void Shutdown(); + + /** Set the factory for sub shells of the specified view shell. + */ + void AddSubShellFactory(ViewShell const* pViewShell, const SharedShellFactory& rpFactory); + void RemoveSubShellFactory(ViewShell const* pViewShell, const SharedShellFactory& rpFactory); + + /** Activate the given view shell. + */ + void ActivateViewShell(ViewShell* pViewShell); + + /** Activate the given shell which is not a view shell. For view shells + use the ActivateViewShell() method. + */ + void ActivateShell(SfxShell* pShell); + + /** Deactivate the specified shell, i.e. take it and all of its + object bars from the shell stack. + @param pShell + The shell to deactivate. + */ + void DeactivateViewShell(const ViewShell* pShell); + + /** Deactivate the specified shell. The shell is not destroyed. + */ + void DeactivateShell(const SfxShell* pShell); + + /** Associate the form shell with a view shell and their relative + position. This method does not change the shell stack, it just + stores the given values for the next shell stack update. + @param pParentShell + The view shell of the form shell. + @param pFormShell + The form shell. + @param bAbove + When then the form shell will be placed directly above + pViewShell on the SFX shell stack. Otherwise the form shell is + placed directly below the view shell. + */ + void SetFormShell(const ViewShell* pParentShell, FmFormShell* pFormShell, bool bAbove); + + /** Activate the specified shell as sub shell for the given view shell. + The sub shell factory associated with the view shell is used to + create the sub shell. + @param rParentShell + The new sub shell will be placed above this view shell. + @param nId + This id is used only with the factory registered for the parent + view shell. + */ + void ActivateSubShell(const ViewShell& rParentShell, ShellId nId); + + /** Deactivate the specified sub shell. + */ + void DeactivateSubShell(const ViewShell& rParentShell, ShellId nId); + + /** Send all sub shells of the specified view shell an Invalidate() + call. This does not modify the shell stack. + */ + void InvalidateAllSubShells(ViewShell const* pViewShell); + + /** Move the specified view shell to the top most position on the stack + of view shells in relation to the other view shells. After this the + only shells that are higher on the stack are its object bars. + + Call this method after a focus change to bring a view mode view + shell and is associated tool bar shells to the top of the + stack. + + The mbKeepMainViewShellOnTop flag is not obeyed. + + @param nId + The id of the shell to move to the top. + */ + void MoveToTop(const ViewShell& rShell); + + /** Return the first, i.e. top most, view shell that has been activated + under the given id. + @param nId + The id of the shell for which to return a pointer. + @return + When the specified shell is currently not active then NULL is + returned. + */ + SfxShell* GetShell(ShellId nId) const; + + /** Return the top-most shell on the SFX shell stack regardless of + whether that is a view shell or a sub shell. + */ + SfxShell* GetTopShell() const; + + /** Return the top-most active view shell on the internal shell stack. + */ + SfxShell* GetTopViewShell() const; + + /** Use this class to safely lock updates of the view shell stack. + */ + class UpdateLock + { + public: + UpdateLock(const std::shared_ptr& rpManager) + : mpManager(rpManager) + { + mpManager->LockUpdate(); + } + ~UpdateLock() COVERITY_NOEXCEPT_FALSE { mpManager->UnlockUpdate(); } + + private: + std::shared_ptr mpManager; + }; + friend class UpdateLock; + +private: + class Implementation; + std::unique_ptr> + mpImpl; + bool mbValid; + + void LockUpdate(); + void UnlockUpdate(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ViewTabBar.hxx b/sd/source/ui/inc/ViewTabBar.hxx new file mode 100644 index 000000000..ca9db932e --- /dev/null +++ b/sd/source/ui/inc/ViewTabBar.hxx @@ -0,0 +1,184 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; } +namespace com::sun::star::frame { class XController; } +namespace vcl { class Window; } + +namespace sd { + class ViewShellBase; + class ViewTabBar; +} + +namespace sd { + +class TabBarControl final : public InterimItemWindow +{ +public: + TabBarControl(vcl::Window* pParentWindow, const ::rtl::Reference& rpViewTabBar); + virtual void dispose() override; + virtual ~TabBarControl() override; + weld::Notebook& GetNotebook() { return *mxTabControl; } + int GetAllocatedWidth() const { return mnAllocatedWidth; } +private: + std::unique_ptr mxTabControl; + ::rtl::Reference mpViewTabBar; + int mnAllocatedWidth; + + DECL_LINK(ActivatePageHdl, const OString&, void); + DECL_LINK(NotebookSizeAllocHdl, const Size&, void); +}; + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XToolBar, + css::drawing::framework::XTabBar, + css::drawing::framework::XConfigurationChangeListener, + css::lang::XUnoTunnel + > ViewTabBarInterfaceBase; + +/** Tab control for switching between views in the center pane. +*/ +class ViewTabBar final + : public ViewTabBarInterfaceBase +{ +public: + ViewTabBar ( + const css::uno::Reference< css::drawing::framework::XResourceId>& rxViewTabBarId, + const css::uno::Reference< css::frame::XController>& rxController); + virtual ~ViewTabBar() override; + + virtual void disposing(std::unique_lock&) override; + + const VclPtr& GetTabControl() const { return mpTabControl; } + + bool ActivatePage(size_t nIndex); + + //----- drawing::framework::XConfigurationChangeListener ------------------ + + virtual void SAL_CALL + notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + //----- XEventListener ---------------------------------------------------- + + virtual void SAL_CALL disposing( + const css::lang::EventObject& rEvent) override; + + //----- XTabBar ----------------------------------------------------------- + + virtual void + SAL_CALL addTabBarButtonAfter ( + const css::drawing::framework::TabBarButton& rButton, + const css::drawing::framework::TabBarButton& rAnchor) override; + + virtual void + SAL_CALL appendTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) override; + + virtual void + SAL_CALL removeTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) override; + + virtual sal_Bool + SAL_CALL hasTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) override; + + virtual css::uno::Sequence + SAL_CALL getTabBarButtons() override; + + //----- XResource --------------------------------------------------------- + + virtual css::uno::Reference< + css::drawing::framework::XResourceId> SAL_CALL getResourceId() override; + + virtual sal_Bool SAL_CALL isAnchorOnly() override; + + //----- XUnoTunnel -------------------------------------------------------- + + static const css::uno::Sequence& getUnoTunnelId(); + + virtual sal_Int64 SAL_CALL getSomething (const css::uno::Sequence& rId) override; + + /** The returned value is calculated as the difference between the + total height of the control and the height of its first tab page. + This can be considered a hack. + This procedure works only when the control is visible. Calling this + method when the control is not visible results in returning a + default value. + To be on the safe side wait for this control to become visible and + the call this method again. + */ + int GetHeight() const; + + void UpdateActiveButton(); + + void AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton, + const css::drawing::framework::TabBarButton& rAnchor); + void AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton); + void RemoveTabBarButton ( + const css::drawing::framework::TabBarButton& rButton); + bool HasTabBarButton ( + const css::drawing::framework::TabBarButton& rButton); + css::uno::Sequence + GetTabBarButtons(); + +private: + VclPtr mpTabControl; + css::uno::Reference mxController; + css::uno::Reference mxConfigurationController; + typedef ::std::vector TabBarButtonList; + TabBarButtonList maTabBarButtons; + css::uno::Reference mxViewTabBarId; + ViewShellBase* mpViewShellBase; + int mnNoteBookWidthPadding; + + void AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton, + sal_Int32 nPosition); + void UpdateTabBarButtons(); + + /** This method is called from the constructor to get the window for an + anchor ResourceId and pass it to our base class. It has to be + static because it must not access any of the, not yet initialized + members. + */ + static vcl::Window* GetAnchorWindow( + const css::uno::Reference& rxViewTabBarId, + const css::uno::Reference& rxController); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/Window.hxx b/sd/source/ui/inc/Window.hxx new file mode 100644 index 000000000..f1beddbc6 --- /dev/null +++ b/sd/source/ui/inc/Window.hxx @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +class OutlinerView; + +namespace sd +{ +class ViewShell; + +/** An SdWindow contains the actual working area of ViewShell. + +

      The zoom factor used by this class controls how much the page and the + shapes on it are scaled down (<100%) or up (>100%) when displayed on the + output device represented by the OutputDevicebase class. A + zoom factor of 100% would result (with a correctly set DPI value for an + output device) in a one to one mapping of the internal coordinates that + are stored in 100th of mm. The zoom factor is stored in the map mode + member of the OutputDevice base class. It is calculated to + be an integer percent value. +*/ +class Window : public vcl::Window, public ::DropTargetHelper +{ +public: + Window(vcl::Window* pParent); + virtual ~Window() override; + virtual void dispose() override; + + void SetViewShell(ViewShell* pViewSh); + ViewShell* GetViewShell(); + + /** Set the zoom factor to the specified value and center the display + area around the zoom center. + @param nZoom + The zoom factor is given as integral percent value. + */ + void SetZoomIntegral(::tools::Long nZoom); + + /** This internally used method performs the actual adaptation of the + window's map mode to the specified zoom factor. + @param nZoom + The zoom factor is given as integral percent value. + @return + When the given zoom factor lies outside the valid range enclosed + by the minimal zoom factor previously calculated by + CalcMinZoom and a constant upper value it is + forced into that interval. Therefore the returned value is a + valid zoom factor. + */ + ::tools::Long SetZoomFactor(::tools::Long nZoom); + + /** This method is called when the whole page shall be displayed in the + window. Position and zoom factor are set so that the given + rectangle is displayed as large as possible in the window while at + the same time maintaining the rectangle's aspect ratio and adding a + small space at all its four sides (about 3% of width and height). + The map mode is adapted accordingly. + @param rZoomRect + The rectangle is expected to be given relative to the upper left + corner of the window in logical coordinates (100th of mm). + @return + The new zoom factor is returned as integral percent value. + */ + ::tools::Long SetZoomRect(const ::tools::Rectangle& rZoomRect); + + ::tools::Long GetZoomForRect(const ::tools::Rectangle& rZoomRect); + + void SetMinZoomAutoCalc(bool bAuto); + + /** Calculate the minimal zoom factor as the value at which the + application area would completely fill the window. All values set + manually or programmatically are set to this value if they are + smaller. If the currently used zoom factor is smaller than the minimal zoom + factor than set the minimal zoom factor as the new current zoom + factor. + +

      This calculation is performed only when the + bMinZoomAutoCalc is set (to ).

      + */ + void CalcMinZoom(); + void SetMinZoom(::tools::Long nMin); + ::tools::Long GetMinZoom() const { return mnMinZoom; } + void SetMaxZoom(::tools::Long nMax); + ::tools::Long GetMaxZoom() const { return mnMaxZoom; } + + ::tools::Long GetZoom() const; + + const Point& GetWinViewPos() const { return maWinPos; } + const Point& GetViewOrigin() const { return maViewOrigin; } + const Size& GetViewSize() const { return maViewSize; } + void SetWinViewPos(const Point& rPnt); + void SetViewOrigin(const Point& rPnt); + void SetViewSize(const Size& rSize); + void SetCenterAllowed(bool bIsAllowed); + + /** Calculate origin of the map mode according to the size of the view + and window (its size in model coordinates; that takes the zoom + factor into account), and the bCenterAllowed flag. When it is not + set then nothing is changed. When in any direction the window is + larger than the view or the value of aWinPos in this direction is -1 + then the window is centered in this direction. + */ + void UpdateMapOrigin(bool bInvalidate = true); + + void UpdateMapMode(); + + double GetVisibleX() const; // interface for ScrollBars + double GetVisibleY() const; + void SetVisibleXY(double fX, double fY); + double GetVisibleWidth() const; + double GetVisibleHeight() const; + Point GetVisibleCenter(); + double GetScrlLineWidth() const; + double GetScrlLineHeight() const; + double GetScrlPageWidth() const; + double GetScrlPageHeight() const; + void GrabFocus(); + virtual void DataChanged(const DataChangedEvent& rDCEvt) override; + + // DropTargetHelper + virtual sal_Int8 AcceptDrop(const AcceptDropEvent& rEvt) override; + virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent& rEvt) override; + + /** The DropScroll() method is used by AcceptDrop() to scroll the + content of the window while dragging and dropping. With this method + you can control whether DropScroll() shall be used. + */ + void SetUseDropScroll(bool bUseDropScroll); + void DropScroll(const Point& rMousePos); + virtual void KeyInput(const KeyEvent& rKEvt) override; + +private: + OutlinerView* GetOutlinerView() const; + +protected: + Point maWinPos; + Point maViewOrigin; + Size maViewSize; + Size maPrevSize; // contains previous window size in logical coords + sal_uInt16 mnMinZoom; + sal_uInt16 mnMaxZoom; + + /** This flag tells whether to re-calculate the minimal zoom factor + depending on the current zoom factor. Its default value is now false. + */ + bool mbMinZoomAutoCalc; + bool mbCenterAllowed; + ::tools::Long mnTicks; + + ViewShell* mpViewShell; + bool mbUseDropScroll; + + virtual void Resize() override; + virtual void PrePaint(vcl::RenderContext& rRenderContext) override; + virtual void Paint(vcl::RenderContext& rRenderContext, + const ::tools::Rectangle& rRect) override; + virtual void MouseMove(const MouseEvent& rMEvt) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void Command(const CommandEvent& rCEvt) override; + virtual void RequestHelp(const HelpEvent& rEvt) override; + virtual void LoseFocus() override; + virtual bool EventNotify(NotifyEvent& rNEvt) override; + + /** Create an accessibility object that makes this window accessible. + + @return + The returned reference is empty if an accessible object could + not be created. + */ + virtual css::uno::Reference CreateAccessible() override; + + OUString GetSurroundingText() const override; + Selection GetSurroundingTextSelection() const override; + bool DeleteSurroundingText(const Selection& rSelection) override; + + /// @see Window::LogicInvalidate(). + void LogicInvalidate(const ::tools::Rectangle* pRectangle) override; + /// Same as MouseButtonDown(), but coordinates are in logic unit. + virtual void LogicMouseButtonDown(const MouseEvent& rMouseEvent) override; + /// Same as MouseButtonUp(), but coordinates are in logic unit. + virtual void LogicMouseButtonUp(const MouseEvent& rMouseEvent) override; + /// Same as MouseMove(), but coordinates are in logic unit. + virtual void LogicMouseMove(const MouseEvent& rMouseEvent) override; + + FactoryFunction GetUITestFactory() const override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/WindowUpdater.hxx b/sd/source/ui/inc/WindowUpdater.hxx new file mode 100644 index 000000000..2545af79f --- /dev/null +++ b/sd/source/ui/inc/WindowUpdater.hxx @@ -0,0 +1,124 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +namespace vcl +{ +class Window; +} +class OutputDevice; +class SdDrawDocument; + +namespace sd +{ +/** The purpose of the WindowUpdater is to update output + devices to take care of modified global values. These values are + monitored for changes. At the moment this is + the digit language that defines the glyphs to use to render digits. + Other values may be added in the future. + +

      The methods of this class have not been included into the + ViewShell class in order to not clutter its interface any + further. This class accesses some of ViewShell data + members directly and thus is declared as its friend.

      + +

      Windows that are to be kept up-to-date have to be registered via the + RegisterWindow() method. When a document is given then + this document is reformatted when the monitored option changes.

      +*/ +class WindowUpdater final : public utl::ConfigurationListener +{ +public: + explicit WindowUpdater(); + virtual ~WindowUpdater() noexcept override; + + /** Add the given device to the list of devices which will be updated + when one of the monitored values changes. + @param pWindow + This device is added to the device list if it is not and + when it is not already a member of that list. + */ + void RegisterWindow(vcl::Window* pWindow); + + /** Remove the given device from the list of devices which will be updated + when one of the monitored values changes. + @param pWindow + This device is removed from the device list when it is a member + of that list. + */ + void UnregisterWindow(vcl::Window* pWindow); + + /** Set the document so that it is reformatted when one of the monitored + values changes. + @param pDocument + When is given document reformatting will not take + place in the future. + */ + void SetDocument(SdDrawDocument* pDocument); + + /** Update the given output device and update all text objects of the + view shell if not told otherwise. + @param pWindow + The device to update. When the given pointer is NULL then + nothing is done. + */ + void Update(OutputDevice* pDevice) const; + + /** Callback that waits for notifications of a + SvtCTLOptions object. + */ + virtual void ConfigurationChanged(utl::ConfigurationBroadcaster*, + ConfigurationHints nHint) override; + +private: + /// Options to monitor for changes. + SvtCTLOptions maCTLOptions; + + /// The document rendered in the output devices. + SdDrawDocument* mpDocument; + + WindowUpdater(const WindowUpdater& rUpdater) = delete; + + WindowUpdater operator=(const WindowUpdater& rUpdater) = delete; + + /** Type and data member for a list of devices that have to be kept + up-to-date. + */ + typedef ::std::vector> tWindowList; + tWindowList maWindowList; + + /** The central method of this class. Update the given output device. + It is the task of the caller to initiate a reformatting of the + document that is rendered on this device to reflect the changes. + @param pWindow + The output device to update. When it is then the call + is ignored. + */ + void UpdateWindow(OutputDevice* pDevice) const; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/animobjs.hxx b/sd/source/ui/inc/animobjs.hxx new file mode 100644 index 000000000..b44a5fb3d --- /dev/null +++ b/sd/source/ui/inc/animobjs.hxx @@ -0,0 +1,163 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class SdDrawDocument; + +namespace sd { + +class AnimationControllerItem; +class View; + +enum BitmapAdjustment +{ + BA_LEFT_UP, + BA_LEFT, + BA_LEFT_DOWN, + BA_UP, + BA_CENTER, + BA_DOWN, + BA_RIGHT_UP, + BA_RIGHT, + BA_RIGHT_DOWN +}; + +class SdDisplay : public weld::CustomWidgetController +{ +private: + BitmapEx aBitmapEx; + Fraction aScale; + +public: + SdDisplay(); + virtual ~SdDisplay() override; + + virtual void Paint( vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect ) override; + + void SetBitmapEx( BitmapEx const * pBmpEx ); + void SetScale( const Fraction& rFrac ); + + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; +}; + +class AnimationWindow : public SfxDockingWindow +{ + friend class AnimationChildWindow; + friend class AnimationControllerItem; + +public: + AnimationWindow(SfxBindings* pBindings, SfxChildWindow *pCW, vcl::Window* pParent); + virtual ~AnimationWindow() override; + virtual void dispose() override; + + void AddObj( ::sd::View& rView ); + void CreateAnimObj( ::sd::View& rView ); + + virtual void DataChanged( const DataChangedEvent& rDCEvt ) override; + +protected: + virtual bool Close() override; + virtual void Resize() override; + +private: + std::unique_ptr m_xCtlDisplay; + std::unique_ptr m_xCtlDisplayWin; + std::unique_ptr m_xBtnFirst; + std::unique_ptr m_xBtnReverse; + std::unique_ptr m_xBtnStop; + std::unique_ptr m_xBtnPlay; + std::unique_ptr m_xBtnLast; + std::unique_ptr m_xNumFldBitmap; + std::unique_ptr m_xTimeField; + std::unique_ptr m_xFormatter; + std::unique_ptr m_xLbLoopCount; + std::unique_ptr m_xBtnGetOneObject; + std::unique_ptr m_xBtnGetAllObjects; + std::unique_ptr m_xBtnRemoveBitmap; + std::unique_ptr m_xBtnRemoveAll; + std::unique_ptr m_xFiCount; + + std::unique_ptr m_xRbtGroup; + std::unique_ptr m_xRbtBitmap; + std::unique_ptr m_xFtAdjustment; + std::unique_ptr m_xLbAdjustment; + std::unique_ptr m_xBtnCreateGroup; + std::unique_ptr m_xBtnHelp; + + ::std::vector< ::std::pair > m_FrameList; + static const size_t EMPTY_FRAMELIST; + size_t m_nCurrentFrame; + std::unique_ptr pMyDoc; + + bool bMovie; + bool bAllObjects; + + std::unique_ptr pControllerItem; + + ScopeLock maPlayLock; + + DECL_LINK( ClickFirstHdl, weld::Button&, void ); + DECL_LINK( ClickStopHdl, weld::Button&, void ); + DECL_LINK( ClickPlayHdl, weld::Button&, void ); + DECL_LINK( ClickLastHdl, weld::Button&, void ); + DECL_LINK( ClickGetObjectHdl, weld::Button&, void ); + DECL_LINK( ClickRemoveBitmapHdl, weld::Button&, void ); + DECL_LINK( ClickRbtHdl, weld::Toggleable&, void ); + DECL_LINK( ClickHelpHdl, weld::Button&, void ); + DECL_LINK( ClickCreateGroupHdl, weld::Button&, void ); + DECL_LINK( ModifyBitmapHdl, weld::SpinButton&, void ); + DECL_LINK( ModifyTimeHdl, weld::FormattedSpinButton&, void ); + + void UpdateControl(bool bDisableCtrls = false); + void ResetAttrs(); + void WaitInEffect( sal_uLong nMilliSeconds, sal_uLong nTime, + SfxProgress* pStbMgr ) const; + Fraction GetScale(); +}; + +/** + * ControllerItem for Animator + */ +class AnimationControllerItem : public SfxControllerItem +{ + +public: + AnimationControllerItem( sal_uInt16, AnimationWindow*, SfxBindings* ); + +protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pState ) override; +private: + VclPtr pAnimationWin; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/annotationmanager.hxx b/sd/source/ui/inc/annotationmanager.hxx new file mode 100644 index 000000000..6f47efd1f --- /dev/null +++ b/sd/source/ui/inc/annotationmanager.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SfxRequest; +class SfxItemSet; + +namespace sd +{ +class ViewShellBase; +class AnnotationManagerImpl; + +class AnnotationManager +{ +public: + AnnotationManager(ViewShellBase& rViewShellBase); + ~AnnotationManager(); + + void ExecuteAnnotation(SfxRequest const& rRequest); + void GetAnnotationState(SfxItemSet& rItemSet); + +private: + ::rtl::Reference mxImpl; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/assclass.hxx b/sd/source/ui/inc/assclass.hxx new file mode 100644 index 000000000..2b366e971 --- /dev/null +++ b/sd/source/ui/inc/assclass.hxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +#define MAX_PAGES 10 + +namespace weld { class Widget; } + +class SD_DLLPUBLIC Assistent +{ + /** contains for every page the controls, which have to be + connected? correctly */ + std::vector maPages[MAX_PAGES]; + + /// number of pages + int mnPages; + + int mnCurrentPage; + + std::unique_ptr mpPageStatus; + +public: + + Assistent(int nNoOfPage); + + bool IsEnabled ( int nPage ) const; + void EnablePage( int nPage ); + void DisablePage( int nPage ); + + /// adds a control to the specified page + bool InsertControl(int nDestPage, weld::Widget* pUsedControl); + + void NextPage(); + + void PreviousPage(); + + bool GotoPage(const int nPageToGo); + + bool IsLastPage() const; + + bool IsFirstPage() const; + + int GetCurrentPage() const { return mnCurrentPage;} +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/bulmaper.hxx b/sd/source/ui/inc/bulmaper.hxx new file mode 100644 index 000000000..3de99d262 --- /dev/null +++ b/sd/source/ui/inc/bulmaper.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SfxItemSet; +class SvxNumRule; + +class SD_DLLPUBLIC SdBulletMapper +{ +public: + /* #i35937# + static void PreMapNumBulletForDialog( SfxItemSet& rSet ); + static void PostMapNumBulletForDialog( SfxItemSet& rSet ); +*/ + static void MapFontsInNumRule(SvxNumRule& aNumRule, const SfxItemSet& rSet); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/copydlg.hxx b/sd/source/ui/inc/copydlg.hxx new file mode 100644 index 000000000..7d8274743 --- /dev/null +++ b/sd/source/ui/inc/copydlg.hxx @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class ColorListBox; + +namespace sd +{ +class View; + +/** + * dialog to adjust screen + */ +class CopyDlg : public SfxDialogController +{ +public: + CopyDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, ::sd::View* pView); + virtual ~CopyDlg() override; + + void GetAttr(SfxItemSet& rOutAttrs); + void Reset(); + +private: + const SfxItemSet& mrOutAttrs; + Fraction maUIScale; + ::sd::View* mpView; + + std::unique_ptr m_xNumFldCopies; + std::unique_ptr m_xBtnSetViewData; + std::unique_ptr m_xMtrFldMoveX; + std::unique_ptr m_xMtrFldMoveY; + std::unique_ptr m_xMtrFldAngle; + std::unique_ptr m_xMtrFldWidth; + std::unique_ptr m_xMtrFldHeight; + std::unique_ptr m_xFtEndColor; + std::unique_ptr m_xBtnSetDefault; + std::unique_ptr m_xLbStartColor; + std::unique_ptr m_xLbEndColor; + + DECL_LINK(SelectColorHdl, ColorListBox&, void); + DECL_LINK(SetViewData, weld::Button&, void); + DECL_LINK(SetDefault, weld::Button&, void); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/createtableobjectbar.hxx b/sd/source/ui/inc/createtableobjectbar.hxx new file mode 100644 index 000000000..8fc21f19f --- /dev/null +++ b/sd/source/ui/inc/createtableobjectbar.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SfxShell; + +namespace sd +{ +class View; +class ViewShell; +} + +namespace sd::ui::table +{ +SfxShell* CreateTableObjectBar(ViewShell& rShell, ::sd::View* pView); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/custsdlg.hxx b/sd/source/ui/inc/custsdlg.hxx new file mode 100644 index 000000000..52ae87852 --- /dev/null +++ b/sd/source/ui/inc/custsdlg.hxx @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SdDrawDocument; +class SdCustomShow; +class SdCustomShowList; + +class SdCustomShowDlg : public weld::GenericDialogController +{ +private: + SdDrawDocument& rDoc; + SdCustomShowList* pCustomShowList; + + std::unique_ptr m_xLbCustomShows; + std::unique_ptr m_xBtnNew; + std::unique_ptr m_xBtnEdit; + std::unique_ptr m_xBtnRemove; + std::unique_ptr m_xBtnCopy; + std::unique_ptr m_xBtnHelp; + std::unique_ptr m_xBtnStartShow; + std::unique_ptr m_xBtnOK; + + void CheckState(); + + DECL_LINK( ClickButtonHdl, weld::Button&, void ); + DECL_LINK( SelectListBoxHdl, weld::TreeView&, void ); + DECL_LINK( StartShowHdl, weld::Button&, void ); + void SelectHdl(void const *); + +public: + SdCustomShowDlg(weld::Window* pWindow, SdDrawDocument& rDrawDoc); + virtual ~SdCustomShowDlg() override; + bool IsCustomShow() const; +}; + +class SdDefineCustomShowDlg : public weld::GenericDialogController +{ +private: + SdDrawDocument& rDoc; + std::unique_ptr& rpCustomShow; + bool bModified; + OUString aOldName; + + std::unique_ptr m_xEdtName; + std::unique_ptr m_xLbPages; + std::unique_ptr m_xBtnAdd; + std::unique_ptr m_xBtnRemove; + std::unique_ptr m_xLbCustomPages; + std::unique_ptr m_xDropTargetHelper; + std::unique_ptr m_xBtnOK; + std::unique_ptr m_xBtnCancel; + std::unique_ptr m_xBtnHelp; + + void CheckState(); + void CheckCustomShow(); + + DECL_LINK( ClickButtonHdl, weld::Button&, void ); + DECL_LINK( ClickButtonEditHdl, weld::Entry&, void ); + DECL_LINK( ClickButtonHdl3, weld::TreeView&, void ); + DECL_LINK( ClickButtonHdl4, weld::TreeView&, void ); + DECL_LINK( OKHdl, weld::Button&, void ); + void ClickButtonHdl2(void const *); + +public: + + SdDefineCustomShowDlg(weld::Window* pWindow, SdDrawDocument& rDrawDoc, std::unique_ptr& rpCS); + virtual ~SdDefineCustomShowDlg() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/diactrl.hxx b/sd/source/ui/inc/diactrl.hxx new file mode 100644 index 000000000..12e76762e --- /dev/null +++ b/sd/source/ui/inc/diactrl.hxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace com::sun::star::frame +{ +class XFrame; +} +class SfxUInt16Item; + +// SdPagesField: + +class SdPagesField final : public InterimItemWindow +{ +private: + std::unique_ptr m_xWidget; + css::uno::Reference m_xFrame; + + DECL_LINK(ModifyHdl, weld::SpinButton&, void); + DECL_STATIC_LINK(SdPagesField, OutputHdl, weld::SpinButton&, void); + DECL_LINK(spin_button_input, int* result, bool); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + +public: + SdPagesField(vcl::Window* pParent, const css::uno::Reference& rFrame); + virtual void dispose() override; + void set_sensitive(bool bSensitive); + virtual ~SdPagesField() override; + + void UpdatePagesField(const SfxUInt16Item* pItem); +}; + +// SdTbxCtlDiaPages: + +class SdTbxCtlDiaPages : public SfxToolBoxControl +{ +public: + virtual void StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState) override; + virtual VclPtr CreateItemWindow(vcl::Window* pParent) override; + + SFX_DECL_TOOLBOX_CONTROL(); + + SdTbxCtlDiaPages(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx); + virtual ~SdTbxCtlDiaPages() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/dlg_char.hxx b/sd/source/ui/inc/dlg_char.hxx new file mode 100644 index 000000000..36e791a09 --- /dev/null +++ b/sd/source/ui/inc/dlg_char.hxx @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SfxItemSet; +class SfxObjectShell; + +/** + * Character-Tab-Dialog + */ +class SdCharDlg : public SfxTabDialogController +{ +private: + const SfxObjectShell& rDocShell; + + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; + +public: + SdCharDlg(weld::Window* pParent, const SfxItemSet* pAttr, const SfxObjectShell* pDocShell); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/dlgfield.hxx b/sd/source/ui/inc/dlgfield.hxx new file mode 100644 index 000000000..769dc12c7 --- /dev/null +++ b/sd/source/ui/inc/dlgfield.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class SvxFieldData; +class SvxLanguageBox; + +/** + * dialog to adjust field-commands + */ +class SdModifyFieldDlg : public weld::GenericDialogController +{ +private: + SfxItemSet m_aInputSet; + const SvxFieldData* m_pField; + + std::unique_ptr m_xRbtFix; + std::unique_ptr m_xRbtVar; + std::unique_ptr m_xLbLanguage; + std::unique_ptr m_xLbFormat; + + void FillFormatList(); + void FillControls(); + + DECL_LINK(LanguageChangeHdl, weld::ComboBox&, void); + +public: + SdModifyFieldDlg(weld::Window* pWindow, const SvxFieldData* pInField, const SfxItemSet& rSet); + virtual ~SdModifyFieldDlg() override; + + SvxFieldData* GetField(); + SfxItemSet GetItemSet() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/dlgpage.hxx b/sd/source/ui/inc/dlgpage.hxx new file mode 100644 index 000000000..718ccf0c6 --- /dev/null +++ b/sd/source/ui/inc/dlgpage.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SfxObjectShell; +enum class ChangeType; + +/** + * Page configuration-tab-dialog + */ +class SdPageDlg : public SfxTabDialogController +{ +private: + bool mbIsImpressDoc; + + XColorListRef mpColorList; + XGradientListRef mpGradientList; + XHatchListRef mpHatchingList; + XBitmapListRef mpBitmapList; + XPatternListRef mpPatternList; +public: + + SdPageDlg(SfxObjectShell const * pDocSh, weld::Window* pParent, const SfxItemSet* pAttr, bool bAreaPage, bool bIsImpressDoc, bool bIsImpressMaster); + + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/dlgsnap.hxx b/sd/source/ui/inc/dlgsnap.hxx new file mode 100644 index 000000000..97fe09ccb --- /dev/null +++ b/sd/source/ui/inc/dlgsnap.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +/************************************************************************/ + +class SfxItemSet; +namespace sd { + class View; +} + +/** + * dialog to adjust snap- lines and points + */ +class SdSnapLineDlg : public weld::GenericDialogController +{ +private: + int nXValue; + int nYValue; + Fraction aUIScale; + + std::unique_ptr m_xFtX; + std::unique_ptr m_xMtrFldX; + std::unique_ptr m_xFtY; + std::unique_ptr m_xMtrFldY; + std::unique_ptr m_xRadioGroup; + std::unique_ptr m_xRbPoint; + std::unique_ptr m_xRbVert; + std::unique_ptr m_xRbHorz; + std::unique_ptr m_xBtnDelete; + + DECL_LINK(ClickHdl, weld::Button&, void); + DECL_LINK(ToggleHdl, weld::Toggleable&, void); + +public: + SdSnapLineDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, ::sd::View const * pView); + virtual ~SdSnapLineDlg() override; + + void GetAttr(SfxItemSet& rOutAttrs); + + void HideRadioGroup(); + void HideDeleteBtn() { m_xBtnDelete->hide(); } + void SetInputFields(bool bEnableX, bool bEnableY); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/drawview.hxx b/sd/source/ui/inc/drawview.hxx new file mode 100644 index 000000000..daa5cc026 --- /dev/null +++ b/sd/source/ui/inc/drawview.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "View.hxx" + +#include + +namespace sd { + +class DrawDocShell; +class DrawViewShell; + +/** + * Derivative of ::sd::View; contains also a pointer to the document + */ +class SD_DLLPUBLIC DrawView : public ::sd::View +{ +public: + + DrawView ( + DrawDocShell* pDocSh, + OutputDevice* pOutDev, + DrawViewShell* pShell); + virtual ~DrawView() override; + + virtual void MarkListHasChanged() override; + void CompleteRedraw(OutputDevice* pOutDev, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector = nullptr) override; + + virtual bool SetAttributes(const SfxItemSet& rSet, bool bReplaceAll = false, bool bSlide = false, bool bMaster = false) override; + void SetMasterAttributes(SdrObject* pObject, const SdPage& rPage, SfxItemSet rSet, SfxStyleSheetBasePool* pStShPool, bool& bOk, bool bMaster, bool bSlide); + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + + void BlockPageOrderChangedHint(bool bBlock); + + bool SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr = false) override; + + virtual void MakeVisible(const ::tools::Rectangle& rRect, vcl::Window& rWin) override; + virtual void HideSdrPage() override; // SdrPageView* pPV); + + virtual void DeleteMarked() override; // from SdrView +protected: + virtual void ModelHasChanged() override; + +private: + DrawDocShell* mpDocShell; + DrawViewShell* mpDrawViewShell; + + sal_uInt16 mnPOCHSmph; ///< for blocking PageOrderChangedHint +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/filedlg.hxx b/sd/source/ui/inc/filedlg.hxx new file mode 100644 index 000000000..6a22d6ba5 --- /dev/null +++ b/sd/source/ui/inc/filedlg.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +#include + +class SdFileDialog_Imp; + +/******************************************************************************/ + +/** + The class SdOpenSoundFileDialog wraps the FileDialogHelper, displaying the + FILEOPEN_PLAY dialog template and performing the 'preview' functionality + (playing the selected sound file). The interface is a downstripped version + of the aforementioned class, with similar semantics. + */ +class SD_DLLPUBLIC SdOpenSoundFileDialog +{ + const std::unique_ptr mpImpl; + + SdOpenSoundFileDialog(const SdOpenSoundFileDialog&) = delete; + SdOpenSoundFileDialog& operator=(const SdOpenSoundFileDialog&) = delete; + +public: + SdOpenSoundFileDialog(weld::Window* pParent); + ~SdOpenSoundFileDialog(); + + ErrCode Execute(); + OUString GetPath() const; + void SetPath(const OUString& rPath); + // WIP, please don't remove, dear Clang plugins + bool IsInsertAsLinkSelected() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/Configuration.hxx b/sd/source/ui/inc/framework/Configuration.hxx new file mode 100644 index 000000000..8f33ef431 --- /dev/null +++ b/sd/source/ui/inc/framework/Configuration.hxx @@ -0,0 +1,181 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace com::sun::star::util { class XCloneable; } +namespace com::sun::star::drawing::framework { class XConfigurationControllerBroadcaster; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XConfiguration, + css::container::XNamed, + css::lang::XServiceInfo + > ConfigurationInterfaceBase; + +/** A configuration describes the resources of an application like panes, + views, and tool bars and their relationships that are currently active + or are requested to be activated. Resources are specified by URLs rather + than references so that not only the current configuration but also a + requested configuration can be represented. + + A resource URL describes the type of a resource, not its actual + instance. For resources, like panes, that are unique with respect to an + application frame, that does not mean much of a difference. For other + resources like views, that may have more than one instance per + application frame, this is different. To identify them unambiguously a + second URL, one of a unique resource, is necessary. This second URL is + called the anchor of the first. The two types of resources are called + unique and linked respectively. + + Direct manipulation of a configuration object is not advised with the + exception of the configuration controller and objects that implement the + XConfigurationChangeOperation interface. +*/ +class Configuration final + : public ConfigurationInterfaceBase +{ +public: + /** Create a new configuration with a broadcaster that is used to send + events about requested configuration changes. + @param rxBroadcaster + This broadcaster is typically the same as the one used by the + ConfigurationController. + @param bBroadcastRequestEvents + When this is then modifications to the configuration + trigger the broadcasting of "ResourceActivationRequestEvent" and + "ResourceDeactivationRequestEvent". When this flag is + then events with type "ResourceActivationEvent" and + "ResourceDeactivationEvent" are broadcasted. + */ + Configuration (const css::uno::Reference& rxBroadcaster, + bool bBroadcastRequestEvents); + virtual ~Configuration() override; + + virtual void disposing(std::unique_lock&) override; + + // XConfiguration + + virtual void SAL_CALL addResource ( + const css::uno::Reference& + rxResourceId) override; + + virtual void SAL_CALL removeResource( + const css::uno::Reference& + rxResourceId) override; + + virtual css::uno::Sequence< css::uno::Reference< + css::drawing::framework::XResourceId> > SAL_CALL getResources ( + const css::uno::Reference& rxAnchorId, + const OUString& rsResourceURLPrefix, + css::drawing::framework::AnchorBindingMode eMode) override; + + virtual sal_Bool SAL_CALL hasResource ( + const css::uno::Reference& + rxResourceId) override; + + // XCloneable + + virtual css::uno::Reference + SAL_CALL createClone() override; + + // XNamed + + /** Return a human readable string representation. This is used for + debugging purposes. + */ + virtual OUString SAL_CALL getName() override; + + /** This call is ignored because the XNamed interface is (mis)used to + give access to a human readable name for debugging purposes. + */ + virtual void SAL_CALL setName (const OUString& rName) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + +private: + class ResourceContainer; + /** The resource container holds the URLs of unique resource and of + resource linked to unique resources. + */ + std::unique_ptr mpResourceContainer; + + /** The broadcaster used for notifying listeners of requests for + configuration changes. + */ + css::uno::Reference + mxBroadcaster; + + bool mbBroadcastRequestEvents; + + /** This private variant of the constructor is used for cloning a + Configuration object. + @param rResourceContainer + The new Configuration is created with a copy of the elements in + this container. + */ + Configuration (const css::uno::Reference& rxBroadcaster, + bool bBroadcastRequestEvents, + const ResourceContainer& rResourceContainer); + + /** Send an event to all interested listeners that a resource has been + added or removed. The event is sent to the listeners via the + ConfigurationController. + @param rxResourceId + The resource that is added to or removed from the configuration. + @param bActivation + This specifies whether an activation or deactivation is + broadcasted. The mbBroadcastRequestEvents member is also taken + into account when the actual event type field is determined. + */ + void PostEvent ( + const css::uno::Reference& rxResourceId, + const bool bActivation); + + /** When the called object has already been disposed this method throws + an exception and does not return. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed() const; +}; + +/** Return whether the two given configurations contain the same resource + ids. The order of resource ids is ignored. Empty references are + treated like empty configurations. +*/ +bool AreConfigurationsEquivalent ( + const css::uno::Reference& rxConfiguration1, + const css::uno::Reference& rxConfiguration2); + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ConfigurationController.hxx b/sd/source/ui/inc/framework/ConfigurationController.hxx new file mode 100644 index 000000000..2fe2f48d0 --- /dev/null +++ b/sd/source/ui/inc/framework/ConfigurationController.hxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include + +#include + + +namespace com::sun::star::drawing::framework { class XConfiguration; } +namespace com::sun::star::drawing::framework { class XConfigurationChangeRequest; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; } + +namespace sd::framework { + +typedef ::cppu::WeakComponentImplHelper < + css::drawing::framework::XConfigurationController, + css::lang::XInitialization + > ConfigurationControllerInterfaceBase; + +/** The configuration controller is responsible for maintaining the current + configuration. + + @see css::drawing::framework::XConfigurationController + for an extended documentation. +*/ +class ConfigurationController + : private cppu::BaseMutex, + public ConfigurationControllerInterfaceBase +{ +public: + ConfigurationController() noexcept; + virtual ~ConfigurationController() noexcept override; + ConfigurationController(const ConfigurationController&) = delete; + ConfigurationController& operator=(const ConfigurationController&) = delete; + + virtual void SAL_CALL disposing() override; + + void ProcessEvent(); + + /** Normally the requested changes of the configuration are executed + asynchronously. However, there is at least one situation (searching + with the Outliner) where the surrounding code does not cope with + this. So, instead of calling Reschedule until the global event loop + executes the configuration update, this method does (almost) the + same without the reschedules. + + Do not use this method until there is absolutely no other way. + */ + void RequestSynchronousUpdate(); + + // XConfigurationController + + virtual void SAL_CALL lock() override; + + virtual void SAL_CALL unlock() override; + + virtual void SAL_CALL requestResourceActivation ( + const css::uno::Reference& rxResourceId, + css::drawing::framework::ResourceActivationMode eMode) override; + + virtual void SAL_CALL requestResourceDeactivation ( + const css::uno::Reference& + rxResourceId) override; + + virtual css::uno::Reference + SAL_CALL getResource ( + const css::uno::Reference& rxResourceId) override; + + virtual void SAL_CALL update() override; + + virtual css::uno::Reference< + css::drawing::framework::XConfiguration> + SAL_CALL getRequestedConfiguration() override; + + virtual css::uno::Reference< + css::drawing::framework::XConfiguration> + SAL_CALL getCurrentConfiguration() override; + + virtual void SAL_CALL restoreConfiguration ( + const css::uno::Reference& + rxConfiguration) override; + + // XConfigurationControllerBroadcaster + + virtual void SAL_CALL addConfigurationChangeListener ( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeListener>& rxListener, + const OUString& rsEventType, + const css::uno::Any& rUserData) override; + + virtual void SAL_CALL removeConfigurationChangeListener ( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeListener>& rxListener) override; + + virtual void SAL_CALL notifyEvent ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // XConfigurationRequestQueue + + virtual sal_Bool SAL_CALL hasPendingRequests() override; + + virtual void SAL_CALL postChangeRequest ( + const css::uno::Reference< + css::drawing::framework::XConfigurationChangeRequest>& rxRequest) override; + + // XResourceFactoryManager + + virtual void SAL_CALL addResourceFactory( + const OUString& sResourceURL, + const css::uno::Reference& rxResourceFactory) override; + + virtual void SAL_CALL removeResourceFactoryForURL( + const OUString& sResourceURL) override; + + virtual void SAL_CALL removeResourceFactoryForReference( + const css::uno::Reference& rxResourceFactory) override; + + virtual css::uno::Reference + SAL_CALL getResourceFactory ( + const OUString& sResourceURL) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence& rArguments) override; + + /** Use this class instead of calling lock() and unlock() directly in + order to be exception safe. + */ + class Lock + { + public: + Lock (const css::uno::Reference< + css::drawing::framework::XConfigurationController>& rxController); + ~Lock(); + private: + css::uno::Reference< + css::drawing::framework::XConfigurationController> mxController; + }; + +private: + class Implementation; + std::unique_ptr mpImplementation; + bool mbIsDisposed; + + /** When the called object has already been disposed this method throws + an exception and does not return. + + @throws css::lang::DisposedException + @throws css::uno::RuntimeException + */ + void ThrowIfDisposed () const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/DrawModule.hxx b/sd/source/ui/inc/framework/DrawModule.hxx new file mode 100644 index 000000000..79a59b4f9 --- /dev/null +++ b/sd/source/ui/inc/framework/DrawModule.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace com::sun::star::frame +{ +class XController; +} +namespace com::sun::star::uno +{ +template class Reference; +} + +namespace sd::framework +{ +/** The task of this module is to instantiate all modules that belong to the + Draw application. +*/ +class DrawModule +{ +public: + static void Initialize(css::uno::Reference const& rxController); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/FrameworkHelper.hxx b/sd/source/ui/inc/framework/FrameworkHelper.hxx new file mode 100644 index 000000000..c9bf981bb --- /dev/null +++ b/sd/source/ui/inc/framework/FrameworkHelper.hxx @@ -0,0 +1,340 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace com::sun::star::drawing::framework { class XConfigurationController; } +namespace com::sun::star::drawing::framework { class XResourceId; } +namespace com::sun::star::drawing::framework { class XView; } +namespace com::sun::star::drawing::framework { struct ConfigurationChangeEvent; } + +namespace sd { +class ViewShellBase; +} + + +namespace sd::framework { + +/** The FrameworkHelper is a convenience class that simplifies the + access to the drawing framework. + It has three main tasks: + 1. Provide frequently used strings of resource URLs and event names. + 2. Provide shortcuts for accessing the sd framework. + 3. Ease the migration to the drawing framework. + + Note that a FrameworkHelper disposes itself when one of the resource + controllers called by it throws a DisposedException. +*/ +class FrameworkHelper + : public std::enable_shared_from_this, + public SdGlobalResource +{ +public: + // URLs of frequently used panes. + static constexpr OUStringLiteral msPaneURLPrefix = u"private:resource/pane/"; + static const OUString msCenterPaneURL; + static const OUString msFullScreenPaneURL; + static const OUString msLeftImpressPaneURL; + static const OUString msLeftDrawPaneURL; + + // URLs of frequently used views. + static constexpr OUStringLiteral msViewURLPrefix = u"private:resource/view/"; + static const OUString msImpressViewURL; + static const OUString msDrawViewURL; + static const OUString msOutlineViewURL; + static const OUString msNotesViewURL; + static const OUString msHandoutViewURL; + static const OUString msSlideSorterURL; + static const OUString msPresentationViewURL; + static const OUString msSidebarViewURL; + + // URLs of frequently used tool bars. + static constexpr OUStringLiteral msToolBarURLPrefix = u"private:resource/toolbar/"; + static const OUString msViewTabBarURL; + + // Names of frequently used events. + static constexpr OUStringLiteral msResourceActivationRequestEvent + = u"ResourceActivationRequested"; + static constexpr OUStringLiteral msResourceDeactivationRequestEvent + = u"ResourceDeactivationRequest"; + static constexpr OUStringLiteral msResourceActivationEvent = u"ResourceActivation"; + static constexpr OUStringLiteral msResourceDeactivationEvent = u"ResourceDeactivation"; + static constexpr OUStringLiteral msResourceDeactivationEndEvent = u"ResourceDeactivationEnd"; + static constexpr OUStringLiteral msConfigurationUpdateStartEvent = u"ConfigurationUpdateStart"; + static constexpr OUStringLiteral msConfigurationUpdateEndEvent = u"ConfigurationUpdateEnd"; + + /** Return the FrameworkHelper object that is associated with the given + ViewShellBase. If such an object does not yet exist, a new one is + created. + */ + static ::std::shared_ptr Instance (ViewShellBase& rBase); + + /** Mark the FrameworkHelper object for the given ViewShellBase as + disposed. A following ReleaseInstance() call will destroy the + FrameworkHelper object. + + Do not call this method. It is an internally used method that can + not be made private. + */ + static void DisposeInstance (const ViewShellBase& rBase); + + /** Destroy the FrameworkHelper object for the given ViewShellBase. + + Do not call this method. It is an internally used method that can + not be made private. + */ + static void ReleaseInstance (const ViewShellBase& rBase); + + /** Return an identifier for the given view URL. This identifier can be + used in a switch statement. See GetViewURL() for a mapping in the + opposite direction. + */ + static ViewShell::ShellType GetViewId (const OUString& rsViewURL); + + /** Return a view URL for the given identifier. See GetViewId() for a + mapping in the opposite direction. + */ + static OUString GetViewURL (ViewShell::ShellType eType); + + /** Return a ViewShell pointer for the given XView reference. This + assumes that the given reference is implemented by the + ViewShellWrapper class that supports the XTunnel interface. + @return + When the ViewShell pointer can not be inferred from the given + reference then an empty pointer is returned. + */ + static ::std::shared_ptr GetViewShell ( + const css::uno::Reference& rxView); + + typedef ::std::function + ConfigurationChangeEventFilter; + typedef ::std::function Callback; + typedef ::std::function< + void ( + const css::uno::Reference< + css::drawing::framework::XResourceId>&) + > ResourceFunctor; + + /** Test whether the called FrameworkHelper object is valid. + @return + When the object has already been disposed then is returned. + */ + bool IsValid() const; + + /** Return a pointer to the view shell that is displayed in the + specified pane. See GetView() for a variant that returns a + reference to XView instead of a ViewShell pointer. + @return + An empty pointer is returned when for example the specified pane + does not exist or is not visible or does not show a view or one + of the involved objects does not support XUnoTunnel (where + necessary). + */ + ::std::shared_ptr GetViewShell (const OUString& rsPaneURL); + + /** Return a reference to the view that is displayed in the specified + pane. See GetViewShell () for a variant that returns a ViewShell + pointer instead of a reference to XView. + @param rxPaneOrViewId + When this ResourceId specifies a view then that view is + returned. When it belongs to a pane then one view in that pane + is returned. + @return + An empty reference is returned when for example the specified pane + does not exist or is not visible or does not show a view or one + of the involved objects does not support XTunnel (where + necessary). + */ + css::uno::Reference GetView ( + const css::uno::Reference& rxPaneOrViewId); + + /** Request the specified view to be displayed in the specified pane. + When the pane is not visible its creation is also requested. The + update that creates the actual view object is done asynchronously. + @param rsResourceURL + The resource URL of the view to show. + @param rsAnchorURL + The URL of the pane in which to show the view. + @return + The resource id of the requested view is returned. With that + the caller can, for example, call RunOnResourceActivation() to + do some initialization after the requested view becomes active. + */ + css::uno::Reference RequestView ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL); + + /** Process a slot call that requests a view shell change. + */ + void HandleModeChangeSlot ( + sal_uInt16 nSlotId, + SfxRequest const & rRequest); + + /** Run the given callback when the specified event is notified by the + ConfigurationManager. When there are no pending requests and + therefore no events would be notified (in the foreseeable future) + then the callback is called immediately. + The callback is called with a flag that tells the callback whether + the event it waits for has been sent. + */ + void RunOnConfigurationEvent( + const OUString& rsEventType, + const Callback& rCallback); + + /** Run the given callback when the specified resource has been + activated. When the resource is active already when this method is + called then rCallback is called before this method returns. + @param rxResourceId + Wait for the activation of this resource before calling + rCallback. + @param rCallback + The callback to be called when the resource is activated. + + */ + void RunOnResourceActivation( + const css::uno::Reference& rxResourceId, + const Callback& rCallback); + + /** Normally the requested changes of the configuration are executed + asynchronously. However, there is at least one situation (searching + with the Outliner) where the surrounding code does not cope with + this. So, instead of calling Reschedule until the global event loop + executes the configuration update, this method does (almost) the + same without the reschedules. + + Do not use this method until there is absolutely no other way. + */ + void RequestSynchronousUpdate(); + + /** Block until the specified event is notified by the configuration + controller. When the configuration controller is not processing any + requests the method returns immediately. + */ + void WaitForEvent (const OUString& rsEventName) const; + + /** This is a short cut for WaitForEvent(msConfigurationUpdateEndEvent). + Call this method to execute the pending requests. + */ + void WaitForUpdate() const; + + /** Explicit request for an update of the current configuration. Call + this method when one of the resources managed by the sd framework + has been activated or deactivated from the outside, i.e. not by the + framework itself. An example for this is a click on the closer + button of one of the side panes. + */ + void UpdateConfiguration(); + + /** Return a string representation of the given XResourceId object. + */ + static OUString ResourceIdToString ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxResourceId); + + /** Create a new XResourceId object for the given resource URL. + */ + static css::uno::Reference< + css::drawing::framework::XResourceId> + CreateResourceId ( + const OUString& rsResourceURL); + + /** Create a new XResourceId object for the given resource URL and a + single anchor URL. + */ + static css::uno::Reference< + css::drawing::framework::XResourceId> + CreateResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL); + + /** Create a new XResourceId object for the given resource URL. + */ + static css::uno::Reference< + css::drawing::framework::XResourceId> + CreateResourceId ( + const OUString& rsResourceURL, + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxAnchor); + + const css::uno::Reference& + GetConfigurationController() const { return mxConfigurationController;} + +private: + typedef ::std::map< + const ViewShellBase*, + ::std::shared_ptr > InstanceMap; + /** The instance map holds (at least) one FrameworkHelper instance for + every ViewShellBase object. + */ + static InstanceMap maInstanceMap; + class ViewURLMap; + static ViewURLMap maViewURLMap; + + ViewShellBase& mrBase; + css::uno::Reference + mxConfigurationController; + + class DisposeListener; + friend class DisposeListener; + css::uno::Reference + mxDisposeListener; + + FrameworkHelper (ViewShellBase& rBase); + FrameworkHelper (const FrameworkHelper& rHelper) = delete; + virtual ~FrameworkHelper() override; + class Deleter; friend class Deleter; + FrameworkHelper& operator= (const FrameworkHelper& rHelper) = delete; + + void Initialize(); + + void Dispose(); + + /** Run the given callback when an event of the specified type is + received from the ConfigurationController or when the + ConfigurationController has no pending change requests. + @param rsEventType + Run rCallback only on this event. + @param rFilter + This filter has to return in order for rCallback to be + called. + @param rCallback + The callback functor to be called. + */ + void RunOnEvent( + const OUString& rsEventType, + const ConfigurationChangeEventFilter& rFilter, + const Callback& rCallback) const; + + /** This disposing method is forwarded from the inner DisposeListener class. + */ + void disposing (const css::lang::EventObject& rEventObject); +}; + +} // end of namespace sd::framework + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ImpressModule.hxx b/sd/source/ui/inc/framework/ImpressModule.hxx new file mode 100644 index 000000000..da7ede9d9 --- /dev/null +++ b/sd/source/ui/inc/framework/ImpressModule.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace com::sun::star::frame +{ +class XController; +} +namespace com::sun::star::uno +{ +template class Reference; +} + +namespace sd::framework +{ +/** The task of this module is to instantiate all modules that belong to the + Impress application. +*/ +class ImpressModule +{ +public: + static void Initialize(css::uno::Reference const& rxController); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ModuleController.hxx b/sd/source/ui/inc/framework/ModuleController.hxx new file mode 100644 index 000000000..4efc6cc15 --- /dev/null +++ b/sd/source/ui/inc/framework/ModuleController.hxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace com::sun::star::frame { class XController; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XModuleController, + css::lang::XInitialization + > ModuleControllerInterfaceBase; + +/** The ModuleController has two tasks: + + 1. It reads the + org.openoffice.Office.Impress/MultiPaneGUI/Framework/ResourceFactories + configuration data that maps from resource URLs to service names of + factories that can create resources for the URLs. When the + configuration controller wants to create a resource for which it does + not have a factory, it asks the ModuleController to provide one. The + ModuleController looks up the service name registered for the URL of the + resource and instantiates this service. The service is expected to + register on its creation a factory for the resource in question. + + 2. The ModuleController reads on its creation + org.openoffice.Office.Impress/MultiPaneGUI/Framework/StartupServices + configuration data and instantiates all listed services. These services + can then register as listeners at the ConfigurationController or do + whatever they like. +*/ +class ModuleController final + : public ModuleControllerInterfaceBase +{ +public: + static css::uno::Reference< + css::drawing::framework::XModuleController> + CreateInstance ( + const css::uno::Reference& + rxContext); + + virtual void disposing(std::unique_lock&) override; + + // XModuleController + + virtual void SAL_CALL requestResource(const OUString& rsResourceURL) override; + + // XInitialization + + virtual void SAL_CALL initialize( + const css::uno::Sequence& aArguments) override; + +private: + css::uno::Reference< + css::frame::XController> mxController; + + std::unordered_map maResourceToFactoryMap; + std::unordered_map> maLoadedFactories; + + /// @throws std::exception + ModuleController ( + const css::uno::Reference& rxContext); + ModuleController (const ModuleController&) = delete; + virtual ~ModuleController() noexcept override; + + /** Called for every entry in the ResourceFactories configuration entry. + */ + void ProcessFactory (const ::std::vector& rValues); + + /** Instantiate all startup services that are found in the + /org.openoffice.Office.Impress/MultiPaneGUI/Framework/StartupServices + configuration entry. This method is called once when a new + ModuleController object is created. + */ + void InstantiateStartupServices(); + + /** Called for one entry in the StartupServices configuration list this + method instantiates the service described by the entry. It does not + hold references to the new object so that the object will be + destroyed on function exit when it does not register itself + somewhere. It typically will register as + XConfigurationChangeListener at the configuration controller. + */ + void ProcessStartupService (const ::std::vector& rValues); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/Pane.hxx b/sd/source/ui/inc/framework/Pane.hxx new file mode 100644 index 000000000..9e8ee25a1 --- /dev/null +++ b/sd/source/ui/inc/framework/Pane.hxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace sd::framework { + +typedef ::cppu::WeakComponentImplHelper < + css::drawing::framework::XPane, + css::drawing::framework::XPane2, + css::lang::XUnoTunnel + > PaneInterfaceBase; + +/** A pane is a wrapper for a window and possibly for a tab bar (for view + switching). Panes are unique resources. + + This class has two responsibilities: + 1. It implements the XPane interface. This is the most important + interface of this class for API based views (of which there not that + many yet) because it gives access to the XWindow. + 2. It gives access to the underlying VCL Window by implementing the + XUnoTunnel interface. This is necessary at the moment and in the + foreseeable future because many parts of the Draw and Impress views rely + on direct access on the Window class. +*/ +class Pane + : protected cppu::BaseMutex, + public PaneInterfaceBase +{ +public: + /** Create a new Pane object that wraps the given window. + @param rsPaneURL + The URL that is used by the configuration to identify the pane. + The given URL has to be valid. + @param pWindow + The VCL Window (usually this really is an sd::Window) that is + wrapped by the new Pane object. The given pointer must not be + NULL. + */ + Pane ( + const css::uno::Reference& rxPaneId, + vcl::Window* pWindow) + noexcept; + virtual ~Pane() override; + + virtual void SAL_CALL disposing() override; + + static const css::uno::Sequence& getUnoTunnelId(); + + /** This method is typically used together with the XUnoTunnel to obtain + a Window pointer from an XPane object. + */ + virtual vcl::Window* GetWindow(); + + //----- XPane ------------------------------------------------------------- + + /** For a UNO API based implementation of a view this may the most + important method of this class because the view is only interested + in the window of the pane. + */ + virtual css::uno::Reference + SAL_CALL getWindow() override; + + virtual css::uno::Reference + SAL_CALL getCanvas() override; + + //----- XPane2 ------------------------------------------------------------- + + virtual sal_Bool SAL_CALL isVisible() override; + + virtual void SAL_CALL setVisible (sal_Bool bIsVisible) override; + + virtual css::uno::Reference SAL_CALL getAccessible() override; + + virtual void SAL_CALL setAccessible ( + const css::uno::Reference& rxAccessible) override; + + //----- XResource --------------------------------------------------------- + + virtual css::uno::Reference + SAL_CALL getResourceId() override; + + /** For the typical pane it makes no sense to be displayed without a + view. Therefore this default implementation returns always . + */ + virtual sal_Bool SAL_CALL isAnchorOnly() override; + + //----- XUnoTunnel -------------------------------------------------------- + + virtual sal_Int64 SAL_CALL getSomething (const css::uno::Sequence& rId) override; + +protected: + css::uno::Reference mxPaneId; + VclPtr mpWindow; + css::uno::Reference mxWindow; + css::uno::Reference mxCanvas; + + /** Override this method, not getCanvas(), when you want to provide a + different canvas. + + @throws css::uno::RuntimeException + */ + virtual css::uno::Reference + CreateCanvas(); + + /** Throw DisposedException when the object has already been disposed or + is currently being disposed. Otherwise this method returns + normally. + + @throws css::lang::DisposedException + */ + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/PresentationFactory.hxx b/sd/source/ui/inc/framework/PresentationFactory.hxx new file mode 100644 index 000000000..897825c8a --- /dev/null +++ b/sd/source/ui/inc/framework/PresentationFactory.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::frame { class XController; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::framework::XResourceFactory, + css::drawing::framework::XConfigurationChangeListener + > PresentationFactoryInterfaceBase; + +/** This factory creates a marker view whose existence in a configuration + indicates that a slideshow is running (in another but associated + application window). +*/ +class PresentationFactory final + : public PresentationFactoryInterfaceBase +{ +public: + PresentationFactory ( + const css::uno::Reference& rxController); + virtual ~PresentationFactory() override; + + // XResourceFactory + + virtual css::uno::Reference + SAL_CALL createResource ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxViewId) override; + + virtual void SAL_CALL releaseResource ( + const css::uno::Reference& xView) override; + + // XConfigurationChangeListener + + virtual void SAL_CALL notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + // lang::XEventListener + + using WeakComponentImplHelperBase::disposing; + virtual void SAL_CALL disposing ( + const css::lang::EventObject& rEventObject) override; + +private: + css::uno::Reference mxController; + + /// @throws css::lang::DisposedException + void ThrowIfDisposed() const; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/PresentationModule.hxx b/sd/source/ui/inc/framework/PresentationModule.hxx new file mode 100644 index 000000000..f6dcfbc69 --- /dev/null +++ b/sd/source/ui/inc/framework/PresentationModule.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace com::sun::star::frame +{ +class XController; +} +namespace com::sun::star::uno +{ +template class Reference; +} + +namespace sd::framework +{ +/** The task of this module is to instantiate all modules that belong to the + fullscreen presentation. +*/ +class PresentationModule +{ +public: + static void Initialize(css::uno::Reference const& rxController); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ResourceId.hxx b/sd/source/ui/inc/framework/ResourceId.hxx new file mode 100644 index 000000000..98b456c76 --- /dev/null +++ b/sd/source/ui/inc/framework/ResourceId.hxx @@ -0,0 +1,213 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include +#include +#include +#include + +#include + +namespace com::sun::star::util { class XURLTransformer; } +namespace com::sun::star::uno { template class WeakReference; } + +namespace sd::framework { + +typedef ::cppu::WeakImplHelper < + css::drawing::framework::XResourceId, + css::lang::XInitialization, + css::lang::XServiceInfo + > ResourceIdInterfaceBase; + +/** Implementation of the css::drawing::framework::ResourceId + service and the css::drawing::framework::XResourceId + interface. +*/ +class ResourceId + : public ResourceIdInterfaceBase +{ +public: + /** Create a new, empty resource id. + */ + ResourceId(); + + /** Create a new resource id that is described by the given URLs. + @param rsResourceURLs + The first URL specifies the type of resource. The other URLs + describe its anchor. + The set of URLs may be empty. The result is then the same as + returned by ResourceId() default constructor. + */ + ResourceId (std::vector&& rsResourceURLs); + + /** Create a new resource id that has an empty anchor. + @param rsResourceURL + When this resource URL is empty then the resulting ResourceId + object is identical to when the ResourceId() default constructor + had been called. + */ + ResourceId ( + const OUString& rsResourceURL); + + /** Create a new resource id for the given resource type and an anchor + that is specified by a single URL. This constructor can be used for + example for views that are bound to panes. + @param rsResourceURL + The URL of the actual resource. + @param rsAnchorURL + The single URL of the anchor. + */ + ResourceId ( + const OUString& rsResourceURL, + const OUString& rsAnchorURL); + + /** Create a new resource id with an anchor that consists of a sequence + of URLs that is extended by a further URL. + @param rsResourceURL + The URL of the actual resource. + @param rsFirstAnchorURL + This URL extends the anchor given by rAnchorURLs. + @param rAnchorURLs + An anchor as it is returned by XResourceId::getAnchorURLs(). + */ + ResourceId ( + const OUString& rsResourceURL, + const OUString& rsFirstAnchorURL, + const css::uno::Sequence& rAnchorURLs); + + virtual ~ResourceId() override; + + //===== XResourceId ======================================================= + + virtual OUString SAL_CALL + getResourceURL() override; + + virtual css::util::URL SAL_CALL + getFullResourceURL() override; + + virtual sal_Bool SAL_CALL + hasAnchor() override; + + virtual css::uno::Reference< + css::drawing::framework::XResourceId> SAL_CALL + getAnchor() override; + + virtual css::uno::Sequence SAL_CALL + getAnchorURLs() override; + + virtual OUString SAL_CALL + getResourceTypePrefix() override; + + virtual sal_Int16 SAL_CALL + compareTo (const css::uno::Reference< + css::drawing::framework::XResourceId>& rxResourceId) override; + + virtual sal_Bool SAL_CALL + isBoundTo ( + const css::uno::Reference< + css::drawing::framework::XResourceId>& rxResourceId, + css::drawing::framework::AnchorBindingMode eMode) override; + + virtual sal_Bool SAL_CALL + isBoundToURL ( + const OUString& rsAnchorURL, + css::drawing::framework::AnchorBindingMode eMode) override; + + virtual css::uno::Reference< + css::drawing::framework::XResourceId> SAL_CALL + clone() override; + + //===== XInitialization =================================================== + + void SAL_CALL initialize ( + const css::uno::Sequence& aArguments) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + +private: + /** The set of URLs that consist of the resource URL at index 0 and the + anchor URLs and indices 1 and above. + */ + std::vector maResourceURLs; + + std::unique_ptr mpURL; + + static css::uno::WeakReference mxURLTransformerWeak; + + /** Compare the called ResourceId object to the given ResourceId object. + This uses the implementation of both objects to speed up the + comparison. + */ + sal_Int16 CompareToLocalImplementation (const ResourceId& rId) const; + + /** Compare the called ResourceId object to the given XResourceId object + reference. The comparison is done via the UNO interface. Namely, + it uses the getResourceURL() and the getAnchorURLs() methods to get + access to the URLs of the given object. + */ + sal_Int16 CompareToExternalImplementation (const css::uno::Reference< + css::drawing::framework::XResourceId>& rxId) const; + + /** Return whether the called ResourceId object is bound to the anchor + consisting of the URLs given by psFirstAnchorURL and paAnchorURLs. + @param psFirstAnchorURL + Optional first URL of the anchor. This can be missing or present + independently of paAnchorURLs. + @param paAnchorURLs + Optional set of additional anchor URLs. This can be missing or + present independently of psFirstAnchorURL. + @param eMode + This specifies whether the called resource has to be directly + bound to the given anchor in order to return or whether + it can be bound indirectly, too. + */ + bool IsBoundToAnchor ( + const OUString* psFirstAnchorURL, + const css::uno::Sequence* paAnchorURLs, + css::drawing::framework::AnchorBindingMode eMode) const; + + /** Return whether the called ResourceId object is bound to the anchor + consisting of the URLs in rResourceURLs. + @param rResourceURLs + A possibly empty list of anchor URLs. + @param eMode + This specifies whether the called resource has to be directly + bound to the given anchor in order to return or whether + it can be bound indirectly, too. + */ + bool IsBoundToAnchor ( + const ::std::vector& rResourceURLs, + css::drawing::framework::AnchorBindingMode eMode) const; + + void ParseResourceURL(); +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/framework/ViewShellWrapper.hxx b/sd/source/ui/inc/framework/ViewShellWrapper.hxx new file mode 100644 index 000000000..43dca4d67 --- /dev/null +++ b/sd/source/ui/inc/framework/ViewShellWrapper.hxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace sd { class ViewShell; } +namespace sd::slidesorter { class SlideSorterViewShell; } +namespace com::sun::star::awt { class XWindow; } + +namespace sd::framework { + +typedef comphelper::WeakComponentImplHelper < css::lang::XUnoTunnel + , css::awt::XWindowListener + , css::view::XSelectionSupplier + , css::drawing::framework::XRelocatableResource + , css::drawing::framework::XView + > ViewShellWrapperInterfaceBase; + +/** This class wraps ViewShell objects and makes them look like an XView. + Most importantly it provides a tunnel to the ViewShell implementation. + Then it forwards size changes of the pane window to the view shell. +*/ +class ViewShellWrapper final : public ViewShellWrapperInterfaceBase +{ +public: + /** Create a new ViewShellWrapper object that wraps the given ViewShell + object. + @param pViewShell + The ViewShell object to wrap. + @param rsViewURL + URL of the view type of the wrapped view shell. + @param rxWindow + This window reference is optional. When a valid reference is + given then size changes of the referenced window are forwarded + to the ViewShell object. + */ + ViewShellWrapper ( + const ::std::shared_ptr& pViewShell, + const css::uno::Reference& rxViewId, + const css::uno::Reference& rxWindow); + virtual ~ViewShellWrapper() override; + + virtual void disposing(std::unique_lock&) override; + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + + static const css::uno::Sequence& getUnoTunnelId(); + + /** This method is typically used together with the XUnoTunnel interface + to obtain a pointer to the wrapped ViewShell object for a given + XView object. + */ + const ::std::shared_ptr& GetViewShell() const { return mpViewShell;} + + // XUnoTunnel + + virtual sal_Int64 SAL_CALL getSomething (const css::uno::Sequence& rId) override; + + // XResource + + virtual css::uno::Reference + SAL_CALL getResourceId() override; + + virtual sal_Bool SAL_CALL isAnchorOnly() override; + + // XSelectionSupplier + + virtual sal_Bool SAL_CALL select( const css::uno::Any& aSelection ) override; + virtual css::uno::Any SAL_CALL getSelection() override; + virtual void SAL_CALL addSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + virtual void SAL_CALL removeSelectionChangeListener( const css::uno::Reference< css::view::XSelectionChangeListener >& xListener ) override; + + // XRelocatableResource + + virtual sal_Bool SAL_CALL relocateToAnchor ( + const css::uno::Reference< + css::drawing::framework::XResource>& xResource) override; + + // XWindowListener + + virtual void SAL_CALL windowResized( + const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowMoved( + const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowShown( + const css::lang::EventObject& rEvent) override; + + virtual void SAL_CALL windowHidden( + const css::lang::EventObject& rEvent) override; + + // XEventListener + + virtual void SAL_CALL disposing( + const css::lang::EventObject& rEvent) override; + +private: + ::std::shared_ptr< ViewShell > mpViewShell; + ::std::shared_ptr< ::sd::slidesorter::SlideSorterViewShell > mpSlideSorterViewShell; + const css::uno::Reference< css::drawing::framework::XResourceId > mxViewId; + css::uno::Reference mxWindow; +}; + +} // end of namespace sd::framework + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuarea.hxx b/sd/source/ui/inc/fuarea.hxx new file mode 100644 index 000000000..38f0b48b6 --- /dev/null +++ b/sd/source/ui/inc/fuarea.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuArea : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual void Activate() override; + virtual void Deactivate() override; + +private: + FuArea ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + virtual void DoExecute( SfxRequest& rReq ) override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fubullet.hxx b/sd/source/ui/inc/fubullet.hxx new file mode 100644 index 000000000..a8b771f06 --- /dev/null +++ b/sd/source/ui/inc/fubullet.hxx @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SfxItemSet; +class SfxViewFrame; + +namespace sd { + +class ViewShell; + +class FuBullet : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + static void GetSlotState( SfxItemSet& rSet, ViewShell const * pViewShell, SfxViewFrame* pViewFrame ); + +private: + FuBullet ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + void InsertSpecialCharacter( SfxRequest const & rReq ); + void InsertFormattingMark( sal_Unicode cMark ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuchar.hxx b/sd/source/ui/inc/fuchar.hxx new file mode 100644 index 000000000..10331b87d --- /dev/null +++ b/sd/source/ui/inc/fuchar.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuChar + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual void DoExecute( SfxRequest& rReq ) override; + + virtual void Activate() override; + virtual void Deactivate() override; + +private: + FuChar ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fucon3d.hxx b/sd/source/ui/inc/fucon3d.hxx new file mode 100644 index 000000000..545b19327 --- /dev/null +++ b/sd/source/ui/inc/fucon3d.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fuconstr.hxx" + +class E3dCompoundObject; +class E3dScene; +class SdDrawDocument; +class SfxRequest; + +namespace sd { + +class FuConstruct3dObject + : public FuConstruct +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstruct3dObject ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + void ImpPrepareBasic3DShape(E3dCompoundObject const * p3DObj, E3dScene *pScene); + E3dCompoundObject* ImpCreateBasic3DShape(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconarc.hxx b/sd/source/ui/inc/fuconarc.hxx new file mode 100644 index 000000000..9a1beae89 --- /dev/null +++ b/sd/source/ui/inc/fuconarc.hxx @@ -0,0 +1,54 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fuconstr.hxx" + +namespace sd { + +class FuConstructArc final + : public FuConstruct +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstructArc ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconbez.hxx b/sd/source/ui/inc/fuconbez.hxx new file mode 100644 index 000000000..fe9aceae2 --- /dev/null +++ b/sd/source/ui/inc/fuconbez.hxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include "fuconstr.hxx" + +class SdDrawDocument; + +namespace sd { + +class FuConstructBezierPolygon final + : public FuConstruct +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + virtual void SelectionHasChanged() override; + + void SetEditMode(sal_uInt16 nMode); + sal_uInt16 GetEditMode() const { return nEditMode; } + + /** + * set attribute for the object to be created + */ + void SetAttributes(SfxItemSet& rAttr, SdrObject* pObj); + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstructBezierPolygon ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + sal_uInt16 nEditMode; + css::uno::Any maTargets; // used for creating a path for custom animations + + //Extra attributes coming from parameters + sal_uInt16 mnTransparence; // Default: 0 + OUString msColor; // Default: "" + sal_uInt16 mnWidth; // Default: 0 + OUString msShapeName; // Default: "" +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconcs.hxx b/sd/source/ui/inc/fuconcs.hxx new file mode 100644 index 000000000..f4f4ef30e --- /dev/null +++ b/sd/source/ui/inc/fuconcs.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fuconstr.hxx" +#include + +class SdDrawDocument; + +namespace sd { + +class FuConstructCustomShape final + : public FuConstruct +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + + void SetAttributes( SdrObject* pObj ); + const OUString& GetShapeType() const; + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + + // #i33136# + virtual bool doConstructOrthogonal() const override; + +private: + FuConstructCustomShape ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + OUString aCustomShape; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconnct.hxx b/sd/source/ui/inc/fuconnct.hxx new file mode 100644 index 000000000..f8000e5bc --- /dev/null +++ b/sd/source/ui/inc/fuconnct.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuConnectionDlg + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuConnectionDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconrec.hxx b/sd/source/ui/inc/fuconrec.hxx new file mode 100644 index 000000000..caf8ac7eb --- /dev/null +++ b/sd/source/ui/inc/fuconrec.hxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fuconstr.hxx" + +class SdDrawDocument; +class SfxItemSet; + +namespace sd { + +/** + * draw rectangle + */ +class FuConstructRectangle final + : public FuConstruct +{ +private: + //Extra attributes coming from parameters + sal_uInt16 mnFillTransparence; // Default: 0 + OUString msFillColor; // Default: "" + sal_uInt16 mnLineStyle; // Default: SAL_MAX_UINT16 + OUString msShapeName; // Default: "" + +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + void SetAttributes(SfxItemSet& rAttr, SdrObject* pObj); + void SetLineEnds(SfxItemSet& rAttr, SdrObject const & rObj); + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstructRectangle ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconstr.hxx b/sd/source/ui/inc/fuconstr.hxx new file mode 100644 index 000000000..743c6cd5b --- /dev/null +++ b/sd/source/ui/inc/fuconstr.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fudraw.hxx" + +class SdrObject; +class SfxItemSet; + +namespace sd { + +class FuConstruct : public FuDraw +{ +public: + + // Mouse Events + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + virtual void SelectionHasChanged() override { bSelectionChanged = true; } + + // SJ: setting stylesheet, the use of a filled or unfilled style + // is determined by the member nSlotId : + void SetStyleSheet(SfxItemSet& rAttr, SdrObject* pObj); + + // SJ: setting stylesheet, the use of a filled or unfilled style + // is determined by the parameters bUseFillStyle and bUseNoFillStyle : + void SetStyleSheet( SfxItemSet& rAttr, SdrObject* pObj, + const bool bUseFillStyle, const bool bUseNoFillStyle ); + +protected: + FuConstruct (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +private: + bool bSelectionChanged; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuconuno.hxx b/sd/source/ui/inc/fuconuno.hxx new file mode 100644 index 000000000..df8e4d415 --- /dev/null +++ b/sd/source/ui/inc/fuconuno.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fuconstr.hxx" +#include + +enum class SdrInventor : sal_uInt32; + +namespace sd { + +/** + * draw control + */ +class FuConstructUnoControl final + : public FuConstruct +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + +private: + FuConstructUnoControl( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + OUString aOldLayer; + SdrInventor nInventor; + SdrObjKind nIdentifier; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fucopy.hxx b/sd/source/ui/inc/fucopy.hxx new file mode 100644 index 000000000..89b950a8f --- /dev/null +++ b/sd/source/ui/inc/fucopy.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuCopy + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuCopy ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fucushow.hxx b/sd/source/ui/inc/fucushow.hxx new file mode 100644 index 000000000..005aea218 --- /dev/null +++ b/sd/source/ui/inc/fucushow.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuCustomShowDlg + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuCustomShowDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fudraw.hxx b/sd/source/ui/inc/fudraw.hxx new file mode 100644 index 000000000..e1d25a521 --- /dev/null +++ b/sd/source/ui/inc/fudraw.hxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +struct SdrViewEvent; +class SdrObject; + +namespace sd { + +/** + * Base class for all Draw specific functions + */ +class FuDraw + : public FuPoor +{ +public: + + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool RequestHelp(const HelpEvent& rHEvt) override; + + virtual void Activate() override; + + virtual void ForcePointer(const MouseEvent* pMEvt = nullptr); + + virtual void DoubleClick(const MouseEvent& rMEvt); + + bool SetPointer(const SdrObject* pObj, const Point& rPos); + bool SetHelpText(const SdrObject* pObj, const Point& rPos, const SdrViewEvent& rVEvt); + + void SetPermanent(bool bSet) { bPermanent = bSet; } + + /** is called when the current function should be aborted.

      + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted + */ + virtual bool cancel() override; + +protected: + FuDraw (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + virtual ~FuDraw() override; + + PointerStyle aNewPointer; + PointerStyle aOldPointer; + bool bMBDown; + bool bDragHelpLine; + sal_uInt16 nHelpLine; + bool bPermanent; + +private: + void DoModifiers(const MouseEvent& rMEvt, bool bSnapModPressed); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fudspord.hxx b/sd/source/ui/inc/fudspord.hxx new file mode 100644 index 000000000..2c6089442 --- /dev/null +++ b/sd/source/ui/inc/fudspord.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdrDropMarkerOverlay; +class SdrObject; + +namespace sd { + +class FuDisplayOrder final + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + // Mouse- & Key-Events + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + +private: + virtual ~FuDisplayOrder() override; + void implClearOverlay(); + + PointerStyle maPtr; + SdrObject* mpRefObj; + std::unique_ptr mpOverlay; + + FuDisplayOrder ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuediglu.hxx b/sd/source/ui/inc/fuediglu.hxx new file mode 100644 index 000000000..19c32cefd --- /dev/null +++ b/sd/source/ui/inc/fuediglu.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fudraw.hxx" + +namespace sd { + +class FuEditGluePoints final + : public FuDraw +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, bool bPermanent ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool Command(const CommandEvent& rCEvt) override; + virtual void ReceiveRequest(SfxRequest& rReq) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + virtual void ForcePointer(const MouseEvent* pMEvt = nullptr) override; + +private: + bool bBeginInsertPoint; + Point oldPoint; + + FuEditGluePoints ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + virtual ~FuEditGluePoints() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuexecuteinteraction.hxx b/sd/source/ui/inc/fuexecuteinteraction.hxx new file mode 100644 index 000000000..1fb733b55 --- /dev/null +++ b/sd/source/ui/inc/fuexecuteinteraction.hxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +#include + +namespace sd +{ +class FuExecuteInteraction : public FuPoor +{ +public: + static rtl::Reference Create(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq); + virtual void DoExecute(SfxRequest& rReq) override; + +private: + FuExecuteInteraction(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, + SdDrawDocument* pDoc, SfxRequest& rReq); + + css::uno::Reference mxPlayer; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuexpand.hxx b/sd/source/ui/inc/fuexpand.hxx new file mode 100644 index 000000000..ccdffb661 --- /dev/null +++ b/sd/source/ui/inc/fuexpand.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuExpandPage + : public FuPoor +{ + public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuExpandPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuformatpaintbrush.hxx b/sd/source/ui/inc/fuformatpaintbrush.hxx new file mode 100644 index 000000000..0de28d4a8 --- /dev/null +++ b/sd/source/ui/inc/fuformatpaintbrush.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "futext.hxx" + +namespace sd { + +class DrawViewShell; + +class FuFormatPaintBrush : public FuText +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool KeyInput(const KeyEvent& rKEvt) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + static void GetMenuState( DrawViewShell const & rDrawViewShell, SfxItemSet &rSet ); + +private: + FuFormatPaintBrush ( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq); + + void DoExecute( SfxRequest& rReq ) override; + + bool HasContentForThisType( SdrInventor nObjectInventor, SdrObjKind nObjectIdentifier ) const; + void Paste( bool, bool ); + + void implcancel(); + + std::shared_ptr mxItemSet; + bool mbPermanent; + bool mbOldIsQuickTextEditMode; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuhhconv.hxx b/sd/source/ui/inc/fuhhconv.hxx new file mode 100644 index 000000000..a009b3023 --- /dev/null +++ b/sd/source/ui/inc/fuhhconv.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdOutliner; + +namespace sd { + +class FuHangulHanjaConversion final : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + void StartConversion( LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font *pTargetFont, sal_Int32 nOptions, bool bIsInteractive ); + + void StartChineseConversion(); + + void ConvertStyles( LanguageType nTargetLanguage, const vcl::Font *pTargetFont ); + +private: + virtual ~FuHangulHanjaConversion() override; + + SdOutliner* pSdOutliner; + bool bOwnOutliner; + + FuHangulHanjaConversion ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuinsert.hxx b/sd/source/ui/inc/fuinsert.hxx new file mode 100644 index 000000000..a2b335961 --- /dev/null +++ b/sd/source/ui/inc/fuinsert.hxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include "fupoor.hxx" + +namespace sd { + +class FuInsertGraphic + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, + ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq, + bool replaceExistingImage); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuInsertGraphic ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq, + bool replaceExistingImage); + + bool mbReplaceExistingImage; +}; + +/************************************************************************/ + +class FuInsertClipboard + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuInsertClipboard ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +/************************************************************************/ + +class FuInsertOLE + : public FuPoor +{ + public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuInsertOLE ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +/************************************************************************/ + +class FuInsertAVMedia + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuInsertAVMedia ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +#if HAVE_FEATURE_AVMEDIA + void InsertMediaURL(const OUString& rURL, const Size& rPrefSize, bool bLink); +#endif +}; +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuinsfil.hxx b/sd/source/ui/inc/fuinsfil.hxx new file mode 100644 index 000000000..f29b0764d --- /dev/null +++ b/sd/source/ui/inc/fuinsfil.hxx @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" +#include +#include + +class SfxMedium; + +namespace sd { + +class FuInsertFile + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + static void GetSupportedFilterVector( ::std::vector< OUString >& rFilterVector ); + +private: + FuInsertFile ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + OUString aLayoutName; ///< layout name of the currently inserted page + OUString aFilterName; ///< chosen file filter + OUString aFile; ///< chosen file name + + void InsTextOrRTFinOlMode(SfxMedium* pMedium); + bool InsSDDinOlMode(SfxMedium* pMedium); + void InsTextOrRTFinDrMode(SfxMedium* pMedium); + bool InsSDDinDrMode(SfxMedium* pMedium); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuline.hxx b/sd/source/ui/inc/fuline.hxx new file mode 100644 index 000000000..459ff83ee --- /dev/null +++ b/sd/source/ui/inc/fuline.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuLine + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuLine ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fulinend.hxx b/sd/source/ui/inc/fulinend.hxx new file mode 100644 index 000000000..ea17a559a --- /dev/null +++ b/sd/source/ui/inc/fulinend.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuLineEnd + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuLineEnd ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fulink.hxx b/sd/source/ui/inc/fulink.hxx new file mode 100644 index 000000000..38c9d0c07 --- /dev/null +++ b/sd/source/ui/inc/fulink.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuLink + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuLink ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fumeasur.hxx b/sd/source/ui/inc/fumeasur.hxx new file mode 100644 index 000000000..1900dba17 --- /dev/null +++ b/sd/source/ui/inc/fumeasur.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuMeasureDlg + : public FuPoor +{ + public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuMeasureDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fumorph.hxx b/sd/source/ui/inc/fumorph.hxx new file mode 100644 index 000000000..2d896b8eb --- /dev/null +++ b/sd/source/ui/inc/fumorph.hxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" +#include + +#include + +namespace basegfx { + class B2DPolygon; + class B2DPoint; +} + +namespace sd { + +class FuMorph + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + typedef ::std::vector< ::basegfx::B2DPolyPolygon > B2DPolyPolygonList_impl; + + FuMorph ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + void ImpInsertPolygons( + B2DPolyPolygonList_impl& rPolyPolyList3D, + bool bAttributeFade, + const SdrObject* pObj1, + const SdrObject* pObj2 + ); + + static ::basegfx::B2DPolyPolygon ImpCreateMorphedPolygon( + const ::basegfx::B2DPolyPolygon& rPolyPolyStart, + const ::basegfx::B2DPolyPolygon& rPolyPolyEnd, + double fMorphingFactor + ); + + static void ImpMorphPolygons( + const ::basegfx::B2DPolyPolygon& rPolyPoly1, + const ::basegfx::B2DPolyPolygon& rPolyPoly2, + const sal_uInt16 nSteps, + B2DPolyPolygonList_impl& rPolyPolyList3D + ); + + static void ImpAddPolys( + ::basegfx::B2DPolyPolygon& rSmaller, + const ::basegfx::B2DPolyPolygon& rBigger + ); + + static void ImpEqualizePolyPointCount( + ::basegfx::B2DPolygon& rSmall, + const ::basegfx::B2DPolygon& rBig + ); + + static sal_uInt32 ImpGetNearestIndex( + const ::basegfx::B2DPolygon& rPoly, + const ::basegfx::B2DPoint& rPos + ); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/funavig.hxx b/sd/source/ui/inc/funavig.hxx new file mode 100644 index 000000000..d717ce202 --- /dev/null +++ b/sd/source/ui/inc/funavig.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuNavigation + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuNavigation ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuoaprms.hxx b/sd/source/ui/inc/fuoaprms.hxx new file mode 100644 index 000000000..09e69c8d1 --- /dev/null +++ b/sd/source/ui/inc/fuoaprms.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuObjectAnimationParameters + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuObjectAnimationParameters ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuolbull.hxx b/sd/source/ui/inc/fuolbull.hxx new file mode 100644 index 000000000..1ed73cf91 --- /dev/null +++ b/sd/source/ui/inc/fuolbull.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdDrawDocument; +class SfxRequest; +class SfxItemSet; +class SfxPoolItem; +class SvxNumBulletItem; + +namespace sd { + +class View; +class ViewShell; + +/** + * bullet functions in outline mode + */ + +class FuBulletAndPosition + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuBulletAndPosition ( + ViewShell* pViewShell, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + void SetCurrentBulletsNumbering(SfxRequest& rReq); + + const SvxNumBulletItem* GetNumBulletItem(SfxItemSet& aNewAttr, TypedWhichId& nNumItemId); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuoltext.hxx b/sd/source/ui/inc/fuoltext.hxx new file mode 100644 index 000000000..288bcf190 --- /dev/null +++ b/sd/source/ui/inc/fuoltext.hxx @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdDrawDocument; +class SfxRequest; + +namespace sd { + +class View; +class ViewShell; +class OutlineView; +class OutlineViewShell; + +/** + * text functions in outline mode + */ +class FuOutlineText final + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + virtual bool Command(const CommandEvent& rCEvt) override; + + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void DoCut() override; + virtual void DoCopy() override; + virtual void DoPaste() override; + virtual void DoPasteUnformatted() override; + + /** Call this method when the text in the outliner (may) has changed. + It will invalidate some slots of the view frame and update the + preview in the slide sorter. + */ + void UpdateForKeyPress (const KeyEvent& rEvent); + +private: + FuOutlineText ( + ViewShell* pViewShell, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + OutlineViewShell* pOutlineViewShell; + OutlineView* pOutlineView; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fupage.hxx b/sd/source/ui/inc/fupage.hxx new file mode 100644 index 000000000..8540d80d7 --- /dev/null +++ b/sd/source/ui/inc/fupage.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include "fupoor.hxx" +#include + +class SfxItemSet; +class SdBackgroundObjUndoAction; +class SdPage; + +namespace sd { +class DrawViewShell; + +class FuPage + : public FuPoor +{ + public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + virtual void Activate() override; + virtual void Deactivate() override; + + const SfxItemSet* ExecuteDialog(weld::Window* pParent, const SfxRequest& rReq); + +protected: + virtual ~FuPage() override; + +private: + FuPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq ); + + void ApplyItemSet( const SfxItemSet* pArgs ); + + SfxRequest& mrReq; + const SfxItemSet* mpArgs; + std::unique_ptr + mpBackgroundObjUndoAction; + Size maSize; + bool mbPageBckgrdDeleted; + bool mbMasterPage; + bool mbDisplayBackgroundTabPage; + SdPage* mpPage; + DrawViewShell* mpDrawViewShell; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuparagr.hxx b/sd/source/ui/inc/fuparagr.hxx new file mode 100644 index 000000000..559dd0de6 --- /dev/null +++ b/sd/source/ui/inc/fuparagr.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuParagraph + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuParagraph ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fupoor.hxx b/sd/source/ui/inc/fupoor.hxx new file mode 100644 index 000000000..553f6688d --- /dev/null +++ b/sd/source/ui/inc/fupoor.hxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +class SdDrawDocument; +class SfxRequest; +class CommandEvent; +class HelpEvent; +class KeyEvent; +class MouseEvent; + +namespace sd { + +class DrawDocShell; +class View; +class ViewShell; +class Window; + +/** + * Base class for all functions + */ +class FuPoor : public SimpleReferenceComponent +{ +public: + static const int HITPIX = 2; // hit tolerance in pixel + static const int HITLOG = 53; // hit tolerance in mm100 + static const int DRGPIX = 2; // minimal drag move in pixel + static const int DRGLOG = 53; // minimal drag move in mm100 + + + virtual void DoExecute( SfxRequest& rReq ); + + void SetMouseButtonCode(sal_uInt16 nNew) { if(nNew != mnCode) mnCode = nNew; } + sal_uInt16 GetMouseButtonCode() const { return mnCode; } + + DrawDocShell* GetDocSh() { return mpDocSh; } + + virtual void DoCut(); + virtual void DoCopy(); + virtual void DoPaste(); + virtual void DoPasteUnformatted(); + + // mouse & key events; return value = sal_True: event has been handled + virtual bool KeyInput(const KeyEvent& rKEvt); + virtual bool MouseMove(const MouseEvent& ); + virtual bool MouseButtonUp(const MouseEvent& rMEvt); + + // moved from inline to *.cxx + virtual bool MouseButtonDown(const MouseEvent& rMEvt); + + virtual bool Command(const CommandEvent& rCEvt); + virtual bool RequestHelp(const HelpEvent& rHEvt); + virtual void ReceiveRequest(SfxRequest& rReq); + + virtual void Activate(); ///< activates the function + virtual void Deactivate(); ///< deactivates the function + + void SetWindow(::sd::Window* pWin); + + virtual void SelectionHasChanged(); + + sal_uInt16 GetSlotID() const { return nSlotId; } + + void StartDelayToScrollTimer (); + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle); + + /** is called when the current function should be aborted.

      + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted + */ + virtual bool cancel(); + + // #i33136# + /** Decide if the object to be created should be created + orthogonal. Default implementation uses nSlotID + to decide. May be overridden to use other criteria + for this decision + + @returns true if the to be created object should be orthogonal. + */ + virtual bool doConstructOrthogonal() const; + +protected: + /** + @param pViewSh + May be NULL. + */ + FuPoor (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + virtual ~FuPoor() override; + + DECL_LINK( DelayHdl, Timer *, void ); + + static void ImpForceQuadratic(::tools::Rectangle& rRect); + + /** Switch to another layer. The layer to switch to is specified by an + offset relative to the active layer. With respect to the layer bar + control at the lower left of the document window positive values + move to the right and negative values move to the left. + +

      Switching the layer is independent of the view's layer mode. The + layers are switched even when the layer mode is turned off and the + layer control is not visible.

      + @param nOffset + If the offset is positive skip that many layers in selecting the + next layer. If it is negative then select a previous one. An + offset or zero does not change the current layer. If the + resulting index lies outside the valid range of indices then it + is set to either the minimal or maximal valid index, whichever + is nearer. + */ + void SwitchLayer (sal_Int32 nOffset); + + ::sd::View* mpView; + ViewShell* mpViewShell; + VclPtr< ::sd::Window> mpWindow; + DrawDocShell* mpDocSh; + SdDrawDocument* mpDoc; + + sal_uInt16 nSlotId; + + Timer aScrollTimer; ///< for auto-scrolling + DECL_LINK( ScrollHdl, Timer *, void ); + void ForceScroll(const Point& aPixPos); + + Timer aDragTimer; ///< for Drag&Drop + DECL_LINK(DragHdl, Timer *, void); + bool bIsInDragMode; + Point aMDPos; ///< position of MouseButtonDown + + /// Flag to prevent auto-scrolling until one drags from outside into the window + bool bNoScrollUntilInside; + + /// timer to delay scrolling (~ 1 sec) when dragging out of the window + Timer aDelayToScrollTimer; + bool bScrollable; + bool bDelayActive; + bool bFirstMouseMove; + + /// member to hold state of the mouse buttons for creation of own MouseEvents (like in ScrollHdl) + +private: + sal_uInt16 mnCode; + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuprlout.hxx b/sd/source/ui/inc/fuprlout.hxx new file mode 100644 index 000000000..183ea124f --- /dev/null +++ b/sd/source/ui/inc/fuprlout.hxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdDrawDocument; +class SfxRequest; + +namespace sd { + +class View; +class ViewShell; + +class FuPresentationLayout + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuPresentationLayout ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuprobjs.hxx b/sd/source/ui/inc/fuprobjs.hxx new file mode 100644 index 000000000..732c73526 --- /dev/null +++ b/sd/source/ui/inc/fuprobjs.hxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SdDrawDocument; +class SfxRequest; + +namespace sd { + +class View; +class ViewShell; + +class FuPresentationObjects + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuPresentationObjects ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuscale.hxx b/sd/source/ui/inc/fuscale.hxx new file mode 100644 index 000000000..9b70492b8 --- /dev/null +++ b/sd/source/ui/inc/fuscale.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuScale + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuScale ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusearch.hxx b/sd/source/ui/inc/fusearch.hxx new file mode 100644 index 000000000..7088dd776 --- /dev/null +++ b/sd/source/ui/inc/fusearch.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +class SvxSearchItem; +class SdOutliner; + +namespace sd { + +class FuSearch final : public FuPoor +{ +public: + + static FuSearch* createPtr(ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq); + + virtual void DoExecute( SfxRequest& rReq ) override; + + void SearchAndReplace( const SvxSearchItem* pSearchItem ); + +private: + virtual ~FuSearch() override; + + SdOutliner* m_pSdOutliner; + bool m_bOwnOutliner; + + FuSearch ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusel.hxx b/sd/source/ui/inc/fusel.hxx new file mode 100644 index 000000000..3896a6d42 --- /dev/null +++ b/sd/source/ui/inc/fusel.hxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fudraw.hxx" + +namespace com::sun::star::media { class XPlayer; } + +class SdrHdl; +class SdrObject; + +namespace sd { + +class FuSelection final + : public FuDraw +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + // Mouse- & Key-Events + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; + + virtual void SelectionHasChanged() override; + + void SetEditMode(sal_uInt16 nMode); + sal_uInt16 GetEditMode() const { return nEditMode; } + + bool HandleImageMapClick(const SdrObject* pObj, const Point& rPos); + + /** is called when the current function should be aborted.

      + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted + */ + virtual bool cancel() override; + + //let mouse cursor move + virtual void ForcePointer(const MouseEvent* pMEvt = nullptr) override; + +private: + FuSelection (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + + virtual ~FuSelection() override; + + bool bTempRotation; + bool bSelectionChanged; + SdrHdl* pHdl; + bool bSuppressChangesOfSelection; + bool bMirrorSide0; + sal_uInt16 nEditMode; + + /** This pointer stores a candidate for assigning a style in the water + can mode between mouse button down and mouse button up. + */ + SdrObject* pWaterCanCandidate; + + /** Find the object under the given test point without selecting it. + @param rTestPoint + The coordinates at which to search for a shape. + @return + The shape at the test point. When there is no shape at this + position then NULL is returned. + */ + SdrObject* pickObject (const Point& rTestPoint); + //Add Shift+UP/DOWN/LEFT/RIGHT key to move the position of insert point, + //and SHIFT+ENTER key to decide the position and draw the new insert point + bool bBeginInsertPoint; + Point oldPoint; + //let mouse cursor move + bool bMovedToCenterPoint; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusldlg.hxx b/sd/source/ui/inc/fusldlg.hxx new file mode 100644 index 000000000..89f0e69ad --- /dev/null +++ b/sd/source/ui/inc/fusldlg.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuSlideShowDlg + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuSlideShowDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusnapln.hxx b/sd/source/ui/inc/fusnapln.hxx new file mode 100644 index 000000000..aba91f696 --- /dev/null +++ b/sd/source/ui/inc/fusnapln.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuSnapLine + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuSnapLine ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fusumry.hxx b/sd/source/ui/inc/fusumry.hxx new file mode 100644 index 000000000..3a20d3813 --- /dev/null +++ b/sd/source/ui/inc/fusumry.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuSummaryPage + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuSummaryPage ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futempl.hxx b/sd/source/ui/inc/futempl.hxx new file mode 100644 index 000000000..e51447f7b --- /dev/null +++ b/sd/source/ui/inc/futempl.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuTemplate + : public FuPoor +{ +public: + + virtual void Activate() override; + virtual void Deactivate() override; + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuTemplate ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futext.hxx b/sd/source/ui/inc/futext.hxx new file mode 100644 index 000000000..f101e07a0 --- /dev/null +++ b/sd/source/ui/inc/futext.hxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fuconstr.hxx" +#include + +class SdrTextObj; +class FontList; +class OutlinerView; + +namespace sd { + +/** + * Base class for text functions + */ +class FuText + : public FuConstruct +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + virtual bool RequestHelp(const HelpEvent& rHEvt) override; + virtual void ReceiveRequest(SfxRequest& rReq) override; + virtual void DoubleClick(const MouseEvent& rMEvt) override; + + virtual void Activate() override; ///< activates the function + virtual void Deactivate() override; ///< deactivates the function + + void SetInEditMode(const MouseEvent& rMEvt, bool bQuickDrag); + void DeleteDefaultText(); + SdrTextObj* GetTextObj() { return mxTextObj.get(); } + + virtual SdrObjectUniquePtr CreateDefaultObject(const sal_uInt16 nID, const ::tools::Rectangle& rRectangle) override; + + /** is called when the current function should be aborted.

      + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns true if an active function was aborted + */ + virtual bool cancel() override; + + static void ChangeFontSize( bool, OutlinerView*, const FontList*, ::sd::View* ); + + void InvalidateBindings(); + + +protected: + FuText (ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); + +private: + virtual void disposing() override; + + ::tools::WeakReference + mxTextObj; + bool bFirstObjCreated; + bool bJustEndedEdit; + + SfxRequest& rRequest; + + void ImpSetAttributesForNewTextObject(SdrTextObj* pTxtObj); + void ImpSetAttributesFitToSize(SdrTextObj* pTxtObj); + void ImpSetAttributesFitToSizeVertical(SdrTextObj* pTxtObj); + void ImpSetAttributesFitCommon(SdrTextObj* pTxtObj); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futhes.hxx b/sd/source/ui/inc/futhes.hxx new file mode 100644 index 000000000..3b9533ddd --- /dev/null +++ b/sd/source/ui/inc/futhes.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuThesaurus + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuThesaurus ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futransf.hxx b/sd/source/ui/inc/futransf.hxx new file mode 100644 index 000000000..dd7ae19a0 --- /dev/null +++ b/sd/source/ui/inc/futransf.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuTransform + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuTransform ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/futxtatt.hxx b/sd/source/ui/inc/futxtatt.hxx new file mode 100644 index 000000000..584e59c0d --- /dev/null +++ b/sd/source/ui/inc/futxtatt.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuTextAttrDlg + : public FuPoor +{ + public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + FuTextAttrDlg ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuvect.hxx b/sd/source/ui/inc/fuvect.hxx new file mode 100644 index 000000000..0501fb224 --- /dev/null +++ b/sd/source/ui/inc/fuvect.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +class FuVectorize + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + virtual void DoExecute( SfxRequest& rReq ) override; + +private: + + FuVectorize ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/fuzoom.hxx b/sd/source/ui/inc/fuzoom.hxx new file mode 100644 index 000000000..54c352dd3 --- /dev/null +++ b/sd/source/ui/inc/fuzoom.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "fupoor.hxx" + +namespace sd { + +extern const sal_uInt16 SidArrayZoom[]; + +class FuZoom final + : public FuPoor +{ +public: + + static rtl::Reference Create( ViewShell* pViewSh, ::sd::Window* pWin, ::sd::View* pView, SdDrawDocument* pDoc, SfxRequest& rReq ); + + // Mouse- & Key-Events + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + virtual void Activate() override; ///< activates the function + virtual void Deactivate() override; ///< deactivates the function + +private: + virtual ~FuZoom() override; + + Point aBeginPosPix; + Point aBeginPos; + Point aEndPos; + ::tools::Rectangle aZoomRect; + bool bVisible; + bool bStartDrag; + PointerStyle aPtr; + + FuZoom ( + ViewShell* pViewSh, + ::sd::Window* pWin, + ::sd::View* pView, + SdDrawDocument* pDoc, + SfxRequest& rReq); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/gluectrl.hxx b/sd/source/ui/inc/gluectrl.hxx new file mode 100644 index 000000000..58aa13944 --- /dev/null +++ b/sd/source/ui/inc/gluectrl.hxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +enum class SdrEscapeDirection; + +/** + * GluePointEscDirLB + */ +class GlueEscDirLB final : public InterimItemWindow +{ +private: + css::uno::Reference m_xFrame; + std::unique_ptr m_xWidget; + + DECL_LINK(SelectHdl, weld::ComboBox&, void); + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + +public: + GlueEscDirLB(vcl::Window* pParent, const css::uno::Reference& rFrame); + virtual void dispose() override; + virtual ~GlueEscDirLB() override; + + void set_active(int nPos) { m_xWidget->set_active(nPos); } + void set_sensitive(bool bSensitive); + + void Fill(); +}; + +/** + * Toolbox controller for glue-point escape direction + */ +class SdTbxCtlGlueEscDir : public SfxToolBoxControl +{ +private: + static sal_uInt16 GetEscDirPos(SdrEscapeDirection nEscDir); + +public: + virtual void StateChangedAtToolBoxControl(sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pState) override; + virtual VclPtr CreateItemWindow(vcl::Window* pParent) override; + + SFX_DECL_TOOLBOX_CONTROL(); + + SdTbxCtlGlueEscDir(sal_uInt16 nSlotId, ToolBoxItemId nId, ToolBox& rTbx); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/headerfooterdlg.hxx b/sd/source/ui/inc/headerfooterdlg.hxx new file mode 100644 index 000000000..e794712d3 --- /dev/null +++ b/sd/source/ui/inc/headerfooterdlg.hxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include + +#include + +class SdUndoGroup; + +namespace sd +{ +class ViewShell; + +class HeaderFooterTabPage; + +class HeaderFooterDialog : public weld::GenericDialogController +{ +private: + DECL_LINK( ActivatePageHdl, const OString&, void ); + DECL_LINK( ClickApplyToAllHdl, weld::Button&, void ); + DECL_LINK( ClickApplyHdl, weld::Button&, void ); + DECL_LINK( ClickCancelHdl, weld::Button&, void ); + + HeaderFooterSettings maSlideSettings; + HeaderFooterSettings maNotesHandoutSettings; + + SdDrawDocument* mpDoc; + SdPage* mpCurrentPage; + ViewShell* mpViewShell; + + std::unique_ptr mxTabCtrl; + std::unique_ptr mxPBApplyToAll; + std::unique_ptr mxPBApply; + std::unique_ptr mxPBCancel; + std::unique_ptr mxSlideTabPage; + std::unique_ptr mxNotesHandoutsTabPage; + + void apply( bool bToAll, bool bForceSlides ); + void change( SdUndoGroup* pUndoGroup, SdPage* pPage, const HeaderFooterSettings& rNewSettings ); + +public: + HeaderFooterDialog(ViewShell* pViewShell, weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage); + virtual ~HeaderFooterDialog() override; + + void ApplyToAll(); + void Apply(); + + virtual short run() override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/ins_paste.hxx b/sd/source/ui/inc/ins_paste.hxx new file mode 100644 index 000000000..5031d09b0 --- /dev/null +++ b/sd/source/ui/inc/ins_paste.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +// SdInsertPasteDlg +class SdInsertPasteDlg : public weld::GenericDialogController +{ +private: + std::unique_ptr m_xRbBefore; + std::unique_ptr m_xRbAfter; + +public: + SdInsertPasteDlg(weld::Window* pWindow); + virtual ~SdInsertPasteDlg() override; + bool IsInsertBefore() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/inspagob.hxx b/sd/source/ui/inc/inspagob.hxx new file mode 100644 index 000000000..d906a10f7 --- /dev/null +++ b/sd/source/ui/inc/inspagob.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SdDrawDocument; +class SdPageObjsTLV; +class SfxMedium; + +class SdInsertPagesObjsDlg : public weld::GenericDialogController +{ +private: + SfxMedium* m_pMedium; + const SdDrawDocument* m_pDoc; + const OUString& m_rName; + + std::unique_ptr m_xLbTree; + std::unique_ptr m_xCbxLink; + std::unique_ptr m_xCbxMasters; + + void Reset(); + DECL_LINK(SelectObjectHdl, weld::TreeView&, void); + +public: + SdInsertPagesObjsDlg(weld::Window* pParent, const SdDrawDocument* pDoc, + SfxMedium* pSfxMedium, const OUString& rFileName); + virtual ~SdInsertPagesObjsDlg() override; + + /** returns the list + nType == 0 -> pages + nType == 1 -> objects */ + + std::vector GetList ( const sal_uInt16 nType ) ; + + bool IsLink() const; + bool IsRemoveUnnecessaryMasterPages() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/layeroptionsdlg.hxx b/sd/source/ui/inc/layeroptionsdlg.hxx new file mode 100644 index 000000000..300994937 --- /dev/null +++ b/sd/source/ui/inc/layeroptionsdlg.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +class SfxItemSet; + +class SD_DLLPUBLIC SdInsertLayerDlg : public weld::GenericDialogController +{ +private: + std::unique_ptr m_xEdtName; + std::unique_ptr m_xEdtTitle; + std::unique_ptr m_xEdtDesc; + std::unique_ptr m_xCbxVisible; + std::unique_ptr m_xCbxPrintable; + std::unique_ptr m_xCbxLocked; + std::unique_ptr m_xNameFrame; + +public: + + SdInsertLayerDlg(weld::Window* pWindow, const SfxItemSet& rInAttrs, + bool bDeletable, const OUString& rStr); + virtual ~SdInsertLayerDlg() override; + + void GetAttr( SfxItemSet& rOutAttrs ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/masterlayoutdlg.hxx b/sd/source/ui/inc/masterlayoutdlg.hxx new file mode 100644 index 000000000..0acbb18d7 --- /dev/null +++ b/sd/source/ui/inc/masterlayoutdlg.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include + +#include + +class SdDrawDocument; +class SdPage; + +namespace sd +{ + +class MasterLayoutDialog : public weld::GenericDialogController +{ +private: + SdDrawDocument* mpDoc; + SdPage* mpCurrentPage; + + std::unique_ptr mxCBDate; + std::unique_ptr mxCBPageNumber; + std::unique_ptr mxCBSlideNumber; + std::unique_ptr mxCBHeader; + std::unique_ptr mxCBFooter; + + bool mbOldHeader; + bool mbOldFooter; + bool mbOldDate; + bool mbOldPageNumber; + + void applyChanges(); + void remove( PresObjKind eKind ); + void create( PresObjKind eKind ); + +public: + MasterLayoutDialog(weld::Window* pParent, SdDrawDocument* pDoc, SdPage* pCurrentPage); + virtual ~MasterLayoutDialog() override; + + virtual short run() override; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/morphdlg.hxx b/sd/source/ui/inc/morphdlg.hxx new file mode 100644 index 000000000..77b20d718 --- /dev/null +++ b/sd/source/ui/inc/morphdlg.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SdrObject; + +namespace sd { + +class MorphDlg : public weld::GenericDialogController +{ +public: + MorphDlg(weld::Window* pParent, const SdrObject* pObj1, const SdrObject* pObj2); + virtual ~MorphDlg() override; + + void SaveSettings() const; + sal_uInt16 GetFadeSteps() const { return static_cast(m_xMtfSteps->get_value()); } + bool IsAttributeFade() const { return m_xCbxAttributes->get_active(); } + bool IsOrientationFade() const { return m_xCbxOrientation->get_active(); } + +private: + std::unique_ptr m_xMtfSteps; + std::unique_ptr m_xCbxAttributes; + std::unique_ptr m_xCbxOrientation; + + void LoadSettings(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/navigatr.hxx b/sd/source/ui/inc/navigatr.hxx new file mode 100644 index 000000000..0e500eb0f --- /dev/null +++ b/sd/source/ui/inc/navigatr.hxx @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include "sdtreelb.hxx" +#include + +// forward +namespace vcl { class Window; } + +namespace sd { +class DrawDocShell; +class SdNavigatorFloat; +} +class Menu; +class SdNavigatorControllerItem; +class SdPageNameControllerItem; + +enum class NavState { + NONE = 0x000000, + TableUpdate = 0x000100, + BtnFirstEnabled = 0x001000, + BtnFirstDisabled = 0x002000, + BtnPrevEnabled = 0x004000, + BtnPrevDisabled = 0x008000, + BtnLastEnabled = 0x010000, + BtnLastDisabled = 0x020000, + BtnNextEnabled = 0x040000, + BtnNextDisabled = 0x080000, +}; +namespace o3tl { + template<> struct typed_flags : is_typed_flags {}; +} + +class NavDocInfo +{ +public: + NavDocInfo() + : bName(false) + , bActive(false) + , mpDocShell(nullptr) + { + } + + bool HasName() const { return bName; } + bool IsActive() const { return bActive; } + + void SetName( bool bOn ) { bName = bOn; } + void SetActive( bool bOn ) { bActive = bOn; } + + ::sd::DrawDocShell* GetDrawDocShell() {return mpDocShell;} + +private: + friend class SdNavigatorWin; + bool bName : 1; + bool bActive : 1; + ::sd::DrawDocShell* mpDocShell; +}; + +namespace sd { + +class SdNavigatorFloat : public SfxNavigator +{ +private: + std::unique_ptr m_xNavWin; + bool m_bSetInitialFocusOnActivate; + +public: + SdNavigatorFloat(SfxBindings* _pBindings, SfxChildWindow* pMgr, + vcl::Window* pParent, SfxChildWinInfo* pInfo); + void InitTreeLB(const SdDrawDocument* pDoc); + void FreshTree(const SdDrawDocument* pDoc); + virtual void Activate() override; + virtual void dispose() override; + virtual ~SdNavigatorFloat() override; +}; + +} + +class SD_DLLPUBLIC SdNavigatorWin : public PanelLayout +{ +public: + typedef ::std::function UpdateRequestFunctor; + + /** Create a new instance of the navigator. + @param bUseActiveUpdate + When , the default, then the SdNavigatorWin object + will make a SID_NAVIGATOR_INIT call whenever it thinks an + update is necessary. When the navigator will + rely on others to trigger updates. + */ + SdNavigatorWin(weld::Widget* pParent, SfxBindings* pBindings, SfxNavigator* pNavigatorDlg); + void SetUpdateRequestFunctor(const UpdateRequestFunctor& rUpdateRequest); + virtual ~SdNavigatorWin() override; + + void InitTreeLB( const SdDrawDocument* pDoc ); + void RefreshDocumentLB( const OUString* pDocName = nullptr ); + void FirstFocus(); + + bool InsertFile(const OUString& rFileName); + + NavigatorDragType GetNavigatorDragType(); + SdPageObjsTLV& GetObjects(); + +private: + friend class SdNavigatorFloat; + friend class SdNavigatorControllerItem; + friend class SdPageNameControllerItem; + + std::unique_ptr mxToolbox; + std::unique_ptr mxTlbObjects; + std::unique_ptr mxLbDocs; + std::unique_ptr mxDragModeMenu; + std::unique_ptr mxShapeMenu; + + VclPtr mxNavigatorDlg; + + bool mbDocImported; + OUString maDropFileName; + NavigatorDragType meDragType; + std::vector maDocList; + SfxBindings* mpBindings; + std::unique_ptr mpNavigatorCtrlItem; + std::unique_ptr mpPageNameCtrlItem; + + /** This flag controls whether all shapes or only the named shapes are + shown. + */ + // bool mbShowAllShapes; + + static OUString GetDragTypeSdBmpId(NavigatorDragType eDT); + NavDocInfo* GetDocInfo(); + + DECL_DLLPRIVATE_LINK( SelectToolboxHdl, const OString&, void ); + DECL_DLLPRIVATE_LINK( DropdownClickToolBoxHdl, const OString&, void ); + DECL_DLLPRIVATE_LINK( ClickObjectHdl, weld::TreeView&, bool ); + DECL_DLLPRIVATE_LINK( SelectDocumentHdl, weld::ComboBox&, void ); + DECL_DLLPRIVATE_LINK( MenuSelectHdl, const OString&, void ); + DECL_DLLPRIVATE_LINK( ShapeFilterCallback, const OString&, void ); + DECL_DLLPRIVATE_LINK( KeyInputHdl, const KeyEvent&, bool ); + + void SetDragImage(); + +public: + //when object is marked , fresh the corresponding entry tree . + void FreshTree ( const SdDrawDocument* pDoc ); + + virtual weld::Window* GetFrameWeld() const override; +}; + +/** + * ControllerItem for Navigator + */ +class SdNavigatorControllerItem : public SfxControllerItem +{ +public: + SdNavigatorControllerItem( sal_uInt16, SdNavigatorWin*, SfxBindings*, + const SdNavigatorWin::UpdateRequestFunctor& rUpdateRequest); + +protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pState ) override; + +private: + SdNavigatorWin* pNavigatorWin; + const SdNavigatorWin::UpdateRequestFunctor maUpdateRequest; +}; + +/** + * ControllerItem for Navigator to show the page in the TreeLB + */ +class SdPageNameControllerItem : public SfxControllerItem +{ +public: + SdPageNameControllerItem( sal_uInt16, SdNavigatorWin*, SfxBindings*); + +protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pState ) override; + +private: + SdNavigatorWin* pNavigatorWin; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/optsitem.hxx b/sd/source/ui/inc/optsitem.hxx new file mode 100644 index 000000000..979b90b78 --- /dev/null +++ b/sd/source/ui/inc/optsitem.hxx @@ -0,0 +1,580 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class SdOptions; + +namespace sd { +class FrameView; +} + +class SdOptionsGeneric; + +class SD_DLLPUBLIC SdOptionsItem : public ::utl::ConfigItem +{ + +private: + + const SdOptionsGeneric& mrParent; + + virtual void ImplCommit() override; + +public: + + SdOptionsItem( const SdOptionsGeneric& rParent, const OUString& rSubTree ); + virtual ~SdOptionsItem() override; + + SdOptionsItem(SdOptionsItem const &) = default; + SdOptionsItem(SdOptionsItem &&) = default; + SdOptionsItem & operator =(SdOptionsItem const &) = delete; // due to ConfigItem + SdOptionsItem & operator =(SdOptionsItem &&) = delete; // due to ConfigItem + + virtual void Notify( const css::uno::Sequence& aPropertyNames) override; + + css::uno::Sequence< css::uno::Any > GetProperties( const css::uno::Sequence< OUString >& rNames ); + bool PutProperties( const css::uno::Sequence< OUString >& rNames, + const css::uno::Sequence< css::uno::Any>& rValues ); + using ConfigItem::SetModified; +}; + +class SD_DLLPUBLIC SdOptionsGeneric +{ +friend class SdOptionsItem; + +private: + + OUString maSubTree; + std::unique_ptr + mpCfgItem; + bool mbImpress; + bool mbInit : 1; + bool mbEnableModify : 1; + + SAL_DLLPRIVATE void Commit( SdOptionsItem& rCfgItem ) const; + SAL_DLLPRIVATE css::uno::Sequence< OUString > GetPropertyNames() const; + +protected: + + void Init() const; + void OptionsChanged() { if( mpCfgItem && mbEnableModify ) mpCfgItem->SetModified(); } + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const = 0; + virtual bool ReadData( const css::uno::Any* pValues ) = 0; + virtual bool WriteData( css::uno::Any* pValues ) const = 0; + +public: + + SdOptionsGeneric(bool bImpress, const OUString& rSubTree); + SdOptionsGeneric(SdOptionsGeneric const &); + virtual ~SdOptionsGeneric(); + + SdOptionsGeneric& operator=( SdOptionsGeneric const & ); + + bool IsImpress() const { return mbImpress; } + + void EnableModify( bool bModify ) { mbEnableModify = bModify; } + + void Store(); + + static bool isMetricSystem(); +}; + +class SD_DLLPUBLIC SdOptionsLayout : public SdOptionsGeneric +{ +private: + + bool bRuler; // Layout/Display/Ruler + bool bMoveOutline; // Layout/Display/Contour + bool bDragStripes; // Layout/Display/Guide + bool bHandlesBezier; // Layout/Display/Bezier + bool bHelplines; // Layout/Display/Helpline + sal_uInt16 nMetric; // Layout/Other/MeasureUnit + sal_uInt16 nDefTab; // Layout/Other/TabStop + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + SdOptionsLayout(bool bImpress, bool bUseConfig); + + bool operator==( const SdOptionsLayout& rOpt ) const; + + bool IsRulerVisible() const { Init(); return bRuler; } + bool IsMoveOutline() const { Init(); return bMoveOutline; } + bool IsDragStripes() const { Init(); return bDragStripes; } + bool IsHandlesBezier() const { Init(); return bHandlesBezier; } + bool IsHelplines() const { Init(); return bHelplines; } + sal_uInt16 GetMetric() const { Init(); return( ( 0xffff == nMetric ) ? static_cast(SfxModule::GetCurrentFieldUnit()) : nMetric ); } + sal_uInt16 GetDefTab() const { Init(); return nDefTab; } + + void SetRulerVisible( bool bOn ) { if( bRuler != bOn ) { OptionsChanged(); bRuler = bOn; } } + void SetMoveOutline( bool bOn ) { if( bMoveOutline != bOn ) { OptionsChanged(); bMoveOutline = bOn; } } + void SetDragStripes( bool bOn ) { if( bDragStripes != bOn ) { OptionsChanged(); bDragStripes = bOn; } } + void SetHandlesBezier( bool bOn ) { if( bHandlesBezier != bOn ) { OptionsChanged(); bHandlesBezier = bOn; } } + void SetHelplines( bool bOn ) { if( bHelplines != bOn ) { OptionsChanged(); bHelplines = bOn; } } + void SetMetric( sal_uInt16 nInMetric ) { if( nMetric != nInMetric ) { OptionsChanged(); nMetric = nInMetric; } } + void SetDefTab( sal_uInt16 nTab ) { if( nDefTab != nTab ) { OptionsChanged(); nDefTab = nTab; } } +}; + +class SD_DLLPUBLIC SdOptionsLayoutItem : public SfxPoolItem +{ +public: + + explicit SdOptionsLayoutItem(); + SdOptionsLayoutItem( SdOptions const * pOpts, ::sd::FrameView const * pView ); + + virtual SdOptionsLayoutItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void SetOptions( SdOptions* pOpts ) const; + + SdOptionsLayout& GetOptionsLayout() { return maOptionsLayout; } +private: + SdOptionsLayout maOptionsLayout; +}; + +class SdOptionsContents : public SdOptionsGeneric +{ +private: +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + SdOptionsContents(bool bImpress); + + bool operator==( const SdOptionsContents& rOpt ) const; +}; + +class SD_DLLPUBLIC SdOptionsMisc : public SdOptionsGeneric +{ +private: + + sal_Int32 nDefaultObjectSizeWidth; + sal_Int32 nDefaultObjectSizeHeight; + + bool bStartWithTemplate : 1; // Misc/NewDoc/AutoPilot + bool bMarkedHitMovesAlways : 1; // Misc/ObjectMoveable + bool bMoveOnlyDragging : 1; // Currently, not in use !!! + bool bCrookNoContortion : 1; // Misc/NoDistort + bool bQuickEdit : 1; // Misc/TextObject/QuickEditing + bool bMasterPageCache : 1; // Misc/BackgroundCache + bool bDragWithCopy : 1; // Misc/CopyWhileMoving + bool bPickThrough : 1; // Misc/TextObject/Selectable + bool bDoubleClickTextEdit : 1; // Misc/DclickTextedit + bool bClickChangeRotation : 1; // Misc/RotateClick + bool bEnableSdremote : 1; // Misc/Start/EnableSdremote + bool bEnablePresenterScreen : 1; // Misc/Start/EnablePresenterDisplay + bool bSolidDragging : 1; // Misc/ModifyWithAttributes + bool bSummationOfParagraphs : 1; // misc/SummationOfParagraphs + bool bTabBarVisible : 1; // Misc/TabBarVisible + bool bShowUndoDeleteWarning : 1; // Misc/ShowUndoDeleteWarning + // #i75315# + bool bSlideshowRespectZOrder : 1; // Misc/SlideshowRespectZOrder + bool bShowComments : 1; // Misc/ShowComments + + bool bPreviewNewEffects; + bool bPreviewChangedEffects; + bool bPreviewTransitions; + + + sal_Int32 mnDisplay; + + sal_Int32 mnPenColor; + double mnPenWidth; + + /** This value controls the device to use for formatting documents. + The currently supported values are 0 for the current printer or 1 + for the printer independent virtual device the can be retrieved from + the modules. + */ + sal_uInt16 mnPrinterIndependentLayout; // Misc/Compatibility/PrinterIndependentLayout +// Misc + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + SdOptionsMisc(bool bImpress, bool bUseConfig); + + bool operator==( const SdOptionsMisc& rOpt ) const; + + bool IsStartWithTemplate() const { Init(); return bStartWithTemplate; } + bool IsMarkedHitMovesAlways() const { Init(); return bMarkedHitMovesAlways; } + bool IsMoveOnlyDragging() const { Init(); return bMoveOnlyDragging; } + bool IsCrookNoContortion() const { Init(); return bCrookNoContortion; } + bool IsQuickEdit() const { Init(); return bQuickEdit; } + bool IsMasterPagePaintCaching() const { Init(); return bMasterPageCache; } + bool IsDragWithCopy() const { Init(); return bDragWithCopy; } + bool IsPickThrough() const { Init(); return bPickThrough; } + bool IsDoubleClickTextEdit() const { Init(); return bDoubleClickTextEdit; } + bool IsClickChangeRotation() const { Init(); return bClickChangeRotation; } + bool IsEnableSdremote() const { Init(); return bEnableSdremote; } + bool IsEnablePresenterScreen() const { Init(); return bEnablePresenterScreen; } + bool IsSolidDragging() const { Init(); return bSolidDragging; } + bool IsSummationOfParagraphs() const { Init(); return bSummationOfParagraphs; }; + bool IsTabBarVisible() const { Init(); return bTabBarVisible; }; + + /** Return the currently selected printer independent layout mode. + @return + Returns 1 for printer independent layout enabled and 0 when it + is disabled. Other values are reserved for future use. + */ + sal_uInt16 GetPrinterIndependentLayout() const { Init(); return mnPrinterIndependentLayout; }; + bool IsShowUndoDeleteWarning() const { Init(); return bShowUndoDeleteWarning; } + bool IsSlideshowRespectZOrder() const { Init(); return bSlideshowRespectZOrder; } + sal_Int32 GetDefaultObjectSizeWidth() const { Init(); return nDefaultObjectSizeWidth; } + sal_Int32 GetDefaultObjectSizeHeight() const { Init(); return nDefaultObjectSizeHeight; } + + bool IsPreviewNewEffects() const { Init(); return bPreviewNewEffects; } + bool IsPreviewChangedEffects() const { Init(); return bPreviewChangedEffects; } + bool IsPreviewTransitions() const { Init(); return bPreviewTransitions; } + + sal_Int32 GetDisplay() const; + void SetDisplay( sal_Int32 nDisplay ); + + sal_Int32 GetPresentationPenColor() const { Init(); return mnPenColor; } + void SetPresentationPenColor( sal_Int32 nPenColor ) { if( mnPenColor != nPenColor ) { OptionsChanged(); mnPenColor = nPenColor; } } + + double GetPresentationPenWidth() const { Init(); return mnPenWidth; } + void SetPresentationPenWidth( double nPenWidth ) { if( mnPenWidth != nPenWidth ) { OptionsChanged(); mnPenWidth = nPenWidth; } } + + void SetStartWithTemplate( bool bOn ) { if( bStartWithTemplate != bOn ) { OptionsChanged(); bStartWithTemplate = bOn; } } + void SetMarkedHitMovesAlways( bool bOn ) { if( bMarkedHitMovesAlways != bOn ) { OptionsChanged(); bMarkedHitMovesAlways = bOn; } } + void SetMoveOnlyDragging( bool bOn ) { if( bMoveOnlyDragging != bOn ) { OptionsChanged(); bMoveOnlyDragging = bOn; } } + void SetCrookNoContortion( bool bOn ) { if( bCrookNoContortion != bOn ) { OptionsChanged(); bCrookNoContortion = bOn; } } + void SetQuickEdit( bool bOn ) { if( bQuickEdit != bOn ) { OptionsChanged(); bQuickEdit = bOn; } } + void SetMasterPagePaintCaching( bool bOn ) { if( bMasterPageCache != bOn ) { OptionsChanged(); bMasterPageCache = bOn; } } + void SetDragWithCopy( bool bOn ) { if( bDragWithCopy != bOn ) { OptionsChanged(); bDragWithCopy = bOn; } } + void SetPickThrough( bool bOn ) { if( bPickThrough != bOn ) { OptionsChanged(); bPickThrough = bOn; } } + void SetDoubleClickTextEdit( bool bOn ) { if( bDoubleClickTextEdit != bOn ) { OptionsChanged(); bDoubleClickTextEdit = bOn; } } + void SetClickChangeRotation( bool bOn ) { if( bClickChangeRotation != bOn ) { OptionsChanged(); bClickChangeRotation = bOn; } } + void SetEnableSdremote( bool bOn ) { if( bEnableSdremote != bOn ) { OptionsChanged(); bEnableSdremote = bOn; } } + void SetEnablePresenterScreen( bool bOn ) { if( bEnablePresenterScreen != bOn ) { OptionsChanged(); bEnablePresenterScreen = bOn; } } + void SetSummationOfParagraphs( bool bOn ){ if ( bOn != bSummationOfParagraphs ) { OptionsChanged(); bSummationOfParagraphs = bOn; } } + void SetTabBarVisible( bool bOn ){ if ( bOn != bTabBarVisible ) { OptionsChanged(); bTabBarVisible = bOn; } } + /** Set the printer independent layout mode. + @param nOn + The default value is to switch printer independent layout on, + hence the parameters name. Use 0 for turning it off. Other + values are reserved for future use. + */ + void SetPrinterIndependentLayout (sal_uInt16 nOn ){ if ( nOn != mnPrinterIndependentLayout ) { OptionsChanged(); mnPrinterIndependentLayout = nOn; } } + void SetSolidDragging( bool bOn ) { if( bSolidDragging != bOn ) { OptionsChanged(); bSolidDragging = bOn; } } + void SetShowUndoDeleteWarning( bool bOn ) { if( bShowUndoDeleteWarning != bOn ) { OptionsChanged(); bShowUndoDeleteWarning = bOn; } } + void SetSlideshowRespectZOrder( bool bOn ) { if( bSlideshowRespectZOrder != bOn ) { OptionsChanged(); bSlideshowRespectZOrder = bOn; } } + void SetDefaultObjectSizeWidth( sal_Int32 nWidth ) { if( nDefaultObjectSizeWidth != nWidth ) { OptionsChanged(); nDefaultObjectSizeWidth = nWidth; } } + void SetDefaultObjectSizeHeight( sal_Int32 nHeight ) { if( nDefaultObjectSizeHeight != nHeight ) { OptionsChanged(); nDefaultObjectSizeHeight = nHeight; } } + + void SetPreviewNewEffects( bool bOn ) { if( bPreviewNewEffects != bOn ) { OptionsChanged(); bPreviewNewEffects = bOn; } } + void SetPreviewChangedEffects( bool bOn ) { if( bPreviewChangedEffects != bOn ) { OptionsChanged(); bPreviewChangedEffects = bOn; } } + void SetPreviewTransitions( bool bOn ) { if( bPreviewTransitions != bOn ) { OptionsChanged(); bPreviewTransitions = bOn; } } + + bool IsShowComments() const { Init(); return bShowComments; } + void SetShowComments( bool bShow ) { if( bShowComments != bShow ) { OptionsChanged(); bShowComments = bShow; } } +}; + +class SD_DLLPUBLIC SdOptionsMiscItem : public SfxPoolItem +{ +public: + + explicit SdOptionsMiscItem(); + SdOptionsMiscItem( SdOptions const * pOpts, ::sd::FrameView const * pView ); + + virtual SdOptionsMiscItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void SetOptions( SdOptions* pOpts ) const; + + SdOptionsMisc& GetOptionsMisc() { return maOptionsMisc; } + const SdOptionsMisc& GetOptionsMisc() const { return maOptionsMisc; } +private: + SdOptionsMisc maOptionsMisc; +}; + +class SD_DLLPUBLIC SdOptionsSnap : public SdOptionsGeneric +{ +private: + + bool bSnapHelplines : 1; // Snap/Object/SnapLine + bool bSnapBorder : 1; // Snap/Object/PageMargin + bool bSnapFrame : 1; // Snap/Object/ObjectFrame + bool bSnapPoints : 1; // Snap/Object/ObjectPoint + bool bOrtho : 1; // Snap/Position/CreatingMoving + bool bBigOrtho : 1; // Snap/Position/ExtendEdges + bool bRotate : 1; // Snap/Position/Rotating + sal_Int16 nSnapArea; // Snap/Object/Range + Degree100 nAngle; // Snap/Position/RotatingValue + Degree100 nBezAngle; // Snap/Position/PointReduction + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + SdOptionsSnap(bool bImpress, bool bUseConfig); + + bool operator==( const SdOptionsSnap& rOpt ) const; + + bool IsSnapHelplines() const { Init(); return bSnapHelplines; } + bool IsSnapBorder() const { Init(); return bSnapBorder; } + bool IsSnapFrame() const { Init(); return bSnapFrame; } + bool IsSnapPoints() const { Init(); return bSnapPoints; } + bool IsOrtho() const { Init(); return bOrtho; } + bool IsBigOrtho() const { Init(); return bBigOrtho; } + bool IsRotate() const { Init(); return bRotate; } + sal_Int16 GetSnapArea() const { Init(); return nSnapArea; } + Degree100 GetAngle() const { Init(); return nAngle; } + Degree100 GetEliminatePolyPointLimitAngle() const { Init(); return nBezAngle; } + + void SetSnapHelplines( bool bOn ) { if( bSnapHelplines != bOn ) { OptionsChanged(); bSnapHelplines = bOn; } } + void SetSnapBorder( bool bOn ) { if( bSnapBorder != bOn ) { OptionsChanged(); bSnapBorder = bOn; } } + void SetSnapFrame( bool bOn ) { if( bSnapFrame != bOn ) { OptionsChanged(); bSnapFrame = bOn; } } + void SetSnapPoints( bool bOn ) { if( bSnapPoints != bOn ) { OptionsChanged(); bSnapPoints = bOn; } } + void SetOrtho( bool bOn ) { if( bOrtho != bOn ) { OptionsChanged(); bOrtho = bOn; } } + void SetBigOrtho( bool bOn ) { if( bBigOrtho != bOn ) { OptionsChanged(); bBigOrtho = bOn; } } + void SetRotate( bool bOn ) { if( bRotate != bOn ) { OptionsChanged(); bRotate = bOn; } } + void SetSnapArea( sal_Int16 nIn ) { if( nSnapArea != nIn ) { OptionsChanged(); nSnapArea = nIn; } } + void SetAngle( Degree100 nIn ) { if( nAngle != nIn ) { OptionsChanged(); nAngle = nIn; } } + void SetEliminatePolyPointLimitAngle( Degree100 nIn ) { if( nBezAngle != nIn ) { OptionsChanged(); nBezAngle = nIn; } } +}; + +class SD_DLLPUBLIC SdOptionsSnapItem : public SfxPoolItem +{ +public: + + explicit SdOptionsSnapItem(); + SdOptionsSnapItem( SdOptions const * pOpts, ::sd::FrameView const * pView ); + + virtual SdOptionsSnapItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void SetOptions( SdOptions* pOpts ) const; + + SdOptionsSnap& GetOptionsSnap() { return maOptionsSnap; } +private: + SdOptionsSnap maOptionsSnap; +}; + +class SdOptionsZoom : public SdOptionsGeneric +{ +private: + + sal_Int32 nX; // Zoom/ScaleX + sal_Int32 nY; // Zoom/ScaleY + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + explicit SdOptionsZoom(bool bImpress); + + void GetScale( sal_Int32& rX, sal_Int32& rY ) const { Init(); rX = nX; rY = nY; } + void SetScale( sal_Int32 nInX, sal_Int32 nInY ) { if( nX != nInX || nY != nInY ) { OptionsChanged(); nX = nInX; nY = nInY; } } +}; + +class SdOptionsGrid : public SdOptionsGeneric, public SvxOptionsGrid +{ +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + explicit SdOptionsGrid(bool bImpress); + virtual ~SdOptionsGrid() override; + + void SetDefaults(); + + sal_uInt32 GetFieldDrawX() const { Init(); return SvxOptionsGrid::GetFieldDrawX(); } + sal_uInt32 GetFieldDivisionX() const { Init(); return SvxOptionsGrid::GetFieldDivisionX(); } + sal_uInt32 GetFieldDrawY() const { Init(); return SvxOptionsGrid::GetFieldDrawY(); } + sal_uInt32 GetFieldDivisionY() const { Init(); return SvxOptionsGrid::GetFieldDivisionY(); } + sal_uInt32 GetFieldSnapX() const { Init(); return SvxOptionsGrid::GetFieldSnapX(); } + sal_uInt32 GetFieldSnapY() const { Init(); return SvxOptionsGrid::GetFieldSnapY(); } + bool IsUseGridSnap() const { Init(); return SvxOptionsGrid::GetUseGridSnap(); } + bool IsSynchronize() const { Init(); return SvxOptionsGrid::GetSynchronize(); } + bool IsGridVisible() const { Init(); return SvxOptionsGrid::GetGridVisible(); } + bool IsEqualGrid() const { Init(); return SvxOptionsGrid::GetEqualGrid(); } + + void SetFieldDrawX( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldDrawX() ) { OptionsChanged(); SvxOptionsGrid::SetFieldDrawX( nSet ); } } + void SetFieldDivisionX( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldDivisionX() ) { OptionsChanged(); SvxOptionsGrid::SetFieldDivisionX( nSet ); } } + void SetFieldDrawY( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldDrawY() ) { OptionsChanged(); SvxOptionsGrid::SetFieldDrawY( nSet ); } } + void SetFieldDivisionY( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldDivisionY() ) { OptionsChanged(); SvxOptionsGrid::SetFieldDivisionY( nSet ); } } + void SetFieldSnapX( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldSnapX() ) { OptionsChanged(); SvxOptionsGrid::SetFieldSnapX( nSet ); } } + void SetFieldSnapY( sal_uInt32 nSet ) { if( nSet != SvxOptionsGrid::GetFieldSnapY() ) { OptionsChanged(); SvxOptionsGrid::SetFieldSnapY( nSet ); } } + void SetUseGridSnap( bool bSet ) { if( bSet != SvxOptionsGrid::GetUseGridSnap() ) { OptionsChanged(); SvxOptionsGrid::SetUseGridSnap( bSet ); } } + void SetSynchronize( bool bSet ) { if( bSet != SvxOptionsGrid::GetSynchronize() ) { OptionsChanged(); SvxOptionsGrid::SetSynchronize( bSet ); } } + void SetGridVisible( bool bSet ) { if( bSet != SvxOptionsGrid::GetGridVisible() ) { OptionsChanged(); SvxOptionsGrid::SetGridVisible( bSet ); } } + void SetEqualGrid( bool bSet ) { if( bSet != SvxOptionsGrid::GetEqualGrid() ) { OptionsChanged(); SvxOptionsGrid::SetEqualGrid( bSet ); } } +}; + +class SdOptionsGridItem : public SvxGridItem +{ + +public: + explicit SdOptionsGridItem( SdOptions const * pOpts ); + + void SetOptions( SdOptions* pOpts ) const; +}; + +class SD_DLLPUBLIC SdOptionsPrint : public SdOptionsGeneric +{ +private: + + bool bDraw : 1; // Print/Content/Drawing + bool bNotes : 1; // Print/Content/Note + bool bHandout : 1; // Print/Content/Handout + bool bOutline : 1; // Print/Content/Outline + bool bDate : 1; // Print/Other/Date + bool bTime : 1; // Print/Other/Time + bool bPagename : 1; // Print/Other/PageName + bool bHiddenPages : 1; // Print/Other/HiddenPage + bool bPagesize : 1; // Print/Page/PageSize + bool bPagetile : 1; // Print/Page/PageTile + bool bWarningPrinter : 1; // These flags you get + bool bWarningSize : 1; // from the common options, + bool bWarningOrientation : 1; // currently org.openoffice.Office.Common.xml (class OfaMiscCfg ; sfx2/misccfg.hxx ) + bool bBooklet : 1; // Print/Page/Booklet + bool bFront : 1; // Print/Page/BookletFront + bool bBack : 1; // Print/Page/BookletFront + bool bCutPage : 1; // NOT persistent !!! + bool bPaperbin : 1; // Print/Other/FromPrinterSetup + bool mbHandoutHorizontal : 1; // Order Page previews on Handout Pages horizontal + sal_uInt16 mnHandoutPages; // Number of page previews on handout page (only 1/2/4/6/9 are supported) + sal_uInt16 nQuality; // Print/Other/Quality + +protected: + + virtual void GetPropNameArray( const char**& ppNames, sal_uLong& rCount ) const override; + virtual bool ReadData( const css::uno::Any* pValues ) override; + virtual bool WriteData( css::uno::Any* pValues ) const override; + +public: + + SdOptionsPrint(bool bImpress, bool bUseConfig); + + bool operator==( const SdOptionsPrint& rOpt ) const; + + bool IsDraw() const { Init(); return bDraw; } + bool IsNotes() const { Init(); return bNotes; } + bool IsHandout() const { Init(); return bHandout; } + bool IsOutline() const { Init(); return bOutline; } + bool IsDate() const { Init(); return bDate; } + bool IsTime() const { Init(); return bTime; } + bool IsPagename() const { Init(); return bPagename; } + bool IsHiddenPages() const { Init(); return bHiddenPages; } + bool IsPagesize() const { Init(); return bPagesize; } + bool IsPagetile() const { Init(); return bPagetile; } + bool IsWarningPrinter() const { Init(); return bWarningPrinter; } + bool IsWarningSize() const { Init(); return bWarningSize; } + bool IsWarningOrientation() const { Init(); return bWarningOrientation; } + bool IsBooklet() const { Init(); return bBooklet; } + bool IsFrontPage() const { Init(); return bFront; } + bool IsBackPage() const { Init(); return bBack; } + bool IsCutPage() const { Init(); return bCutPage; } + bool IsPaperbin() const { Init(); return bPaperbin; } + sal_uInt16 GetOutputQuality() const { Init(); return nQuality; } + bool IsHandoutHorizontal() const { Init(); return mbHandoutHorizontal; } + sal_uInt16 GetHandoutPages() const { Init(); return mnHandoutPages; } + + void SetDraw( bool bOn ) { if( bDraw != bOn ) { OptionsChanged(); bDraw = bOn; } } + void SetNotes( bool bOn ) { if( bNotes != bOn ) { OptionsChanged(); bNotes = bOn; } } + void SetHandout( bool bOn ) { if( bHandout != bOn ) { OptionsChanged(); bHandout = bOn; } } + void SetOutline( bool bOn ) { if( bOutline != bOn ) { OptionsChanged(); bOutline = bOn; } } + void SetDate( bool bOn ) { if( bDate != bOn ) { OptionsChanged(); bDate = bOn; } } + void SetTime( bool bOn ) { if( bTime != bOn ) { OptionsChanged(); bTime = bOn; } } + void SetPagename( bool bOn ) { if( bPagename != bOn ) { OptionsChanged(); bPagename = bOn; } } + void SetHiddenPages( bool bOn ) { if( bHiddenPages != bOn ) { OptionsChanged(); bHiddenPages = bOn; } } + void SetPagesize( bool bOn ) { if( bPagesize != bOn ) { OptionsChanged(); bPagesize = bOn; } } + void SetPagetile( bool bOn ) { if( bPagetile != bOn ) { OptionsChanged(); bPagetile = bOn; } } + void SetWarningPrinter( bool bOn ) { if( bWarningPrinter != bOn ) { OptionsChanged(); bWarningPrinter = bOn; } } + void SetWarningSize( bool bOn ) { if( bWarningSize != bOn ) { OptionsChanged(); bWarningSize = bOn; } } + void SetWarningOrientation( bool bOn) { if( bWarningOrientation != bOn ) { OptionsChanged(); bWarningOrientation = bOn; } } + void SetBooklet( bool bOn ) { if( bBooklet != bOn ) { OptionsChanged(); bBooklet = bOn; } } + void SetFrontPage( bool bOn ) { if( bFront != bOn ) { OptionsChanged(); bFront = bOn; } } + void SetBackPage( bool bOn ) { if( bBack != bOn ) { OptionsChanged(); bBack = bOn; } } + void SetCutPage( bool bOn ) { if( bCutPage != bOn ) { OptionsChanged(); bCutPage = bOn; } } + void SetPaperbin( bool bOn ) { if( bPaperbin != bOn ) { OptionsChanged(); bPaperbin = bOn; } } + void SetOutputQuality( sal_uInt16 nInQuality ) { if( nQuality != nInQuality ) { OptionsChanged(); nQuality = nInQuality; } } + void SetHandoutHorizontal( bool bHandoutHorizontal ) { if( mbHandoutHorizontal != bHandoutHorizontal ) { OptionsChanged(); mbHandoutHorizontal = bHandoutHorizontal; } } + void SetHandoutPages( sal_uInt16 nHandoutPages ) { if( nHandoutPages != mnHandoutPages ) { OptionsChanged(); mnHandoutPages = nHandoutPages; } } +}; + +class SD_DLLPUBLIC SdOptionsPrintItem : public SfxPoolItem +{ +public: + + explicit SdOptionsPrintItem(); + explicit SdOptionsPrintItem( SdOptions const * pOpts ); + + virtual SdOptionsPrintItem* Clone( SfxItemPool *pPool = nullptr ) const override; + virtual bool operator==( const SfxPoolItem& ) const override; + + void SetOptions( SdOptions* pOpts ) const; + + SdOptionsPrint& GetOptionsPrint() { return maOptionsPrint; } + const SdOptionsPrint& GetOptionsPrint() const { return maOptionsPrint; } +private: + SdOptionsPrint maOptionsPrint; +}; + +class SdOptions : public SdOptionsLayout, public SdOptionsContents, + public SdOptionsMisc, public SdOptionsSnap, + public SdOptionsZoom, public SdOptionsGrid, + public SdOptionsPrint +{ +public: + + explicit SdOptions(bool bImpress); + virtual ~SdOptions() override; + + void StoreConfig(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/paragr.hxx b/sd/source/ui/inc/paragr.hxx new file mode 100644 index 000000000..30304d800 --- /dev/null +++ b/sd/source/ui/inc/paragr.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +/** + * Paragraph-Tab-Dialog + */ +class SdParagraphDlg : public SfxTabDialogController +{ +private: + virtual void PageCreated(const OString& rId, SfxTabPage& rPage) override; + +public: + SdParagraphDlg(weld::Window* pParent, const SfxItemSet* pAttr); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/pgjump.hxx b/sd/source/ui/inc/pgjump.hxx new file mode 100644 index 000000000..c4d434caa --- /dev/null +++ b/sd/source/ui/inc/pgjump.hxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +enum PageJump +{ + PAGE_NONE, + PAGE_FIRST, + PAGE_PREVIOUS, + PAGE_NEXT, + PAGE_LAST +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/present.hxx b/sd/source/ui/inc/present.hxx new file mode 100644 index 000000000..00c78ac79 --- /dev/null +++ b/sd/source/ui/inc/present.hxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SfxItemSet; +class SdCustomShowList; + +/** + * Dialog to define optionsm_xnd to start the presentation + */ +class SdStartPresentationDlg : public weld::GenericDialogController +{ +private: + SdCustomShowList* pCustomShowList; + const SfxItemSet& rOutAttrs; + sal_Int32 mnMonitors; + + std::unique_ptr m_xRbtAll; + std::unique_ptr m_xRbtAtDia; + std::unique_ptr m_xRbtCustomshow; + std::unique_ptr m_xLbDias; + std::unique_ptr m_xLbCustomshow; + + std::unique_ptr m_xRbtStandard; + std::unique_ptr m_xRbtWindow; + std::unique_ptr m_xRbtAuto; + std::unique_ptr m_xTmfPause; + std::unique_ptr m_xFormatter; + std::unique_ptr m_xCbxAutoLogo; + + std::unique_ptr m_xCbxManuel; + std::unique_ptr m_xCbxMousepointer; + std::unique_ptr m_xCbxPen; + std::unique_ptr m_xCbxAnimationAllowed; + std::unique_ptr m_xCbxChangePage; + std::unique_ptr m_xCbxAlwaysOnTop; + + std::unique_ptr m_xFtMonitor; + std::unique_ptr m_xLBMonitor; + + std::unique_ptr m_xMonitor; + std::unique_ptr m_xAllMonitors; + std::unique_ptr m_xMonitorExternal; + std::unique_ptr m_xExternal; + + DECL_LINK(ChangeRangeHdl, weld::Toggleable&, void); + DECL_LINK(ClickWindowPresentationHdl, weld::Toggleable&, void); + void ChangePause(); + DECL_LINK(ChangePauseHdl, weld::FormattedSpinButton&, void); + + void InitMonitorSettings(); + enum DisplayType { + EXTERNAL_IS_NUMBER, + MONITOR_NORMAL, + MONITOR_IS_EXTERNAL, + }; + sal_Int32 InsertDisplayEntry(const OUString &aName, + sal_Int32 nDisplay); + OUString GetDisplayName( sal_Int32 nDisplay, + DisplayType eType ); +public: + SdStartPresentationDlg(weld::Window* pWindow, + const SfxItemSet& rInAttrs, + const std::vector &rPageNames, + SdCustomShowList* pCSList); + virtual ~SdStartPresentationDlg() override; + void GetAttr( SfxItemSet& rOutAttrs ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/prltempl.hxx b/sd/source/ui/inc/prltempl.hxx new file mode 100644 index 000000000..efd50e4f7 --- /dev/null +++ b/sd/source/ui/inc/prltempl.hxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +class SfxObjectShell; +class SfxStyleSheetBase; +class SfxStyleSheetBasePool; + +/** + * Template-Tab-Dialog + */ +class SdPresLayoutTemplateDlg final : public SfxTabDialogController +{ +private: + const SfxObjectShell* mpDocShell; + + XColorListRef pColorTab; + XGradientListRef pGradientList; + XHatchListRef pHatchingList; + XBitmapListRef pBitmapList; + XPatternListRef pPatternList; + XDashListRef pDashList; + XLineEndListRef pLineEndList; + + PresentationObjects ePO; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + + // for mapping with the new SvxNumBulletItem + SfxItemSet aInputSet; + std::unique_ptr pOutSet; + + sal_uInt16 GetOutlineLevel() const; + +public: + SdPresLayoutTemplateDlg(SfxObjectShell const * pDocSh, weld::Window* pParent, bool bBackground, SfxStyleSheetBase& rStyleBase, PresentationObjects ePO, SfxStyleSheetBasePool* pSSPool); + virtual ~SdPresLayoutTemplateDlg() override; + + const SfxItemSet* GetOutputItemSet() const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/prntopts.hxx b/sd/source/ui/inc/prntopts.hxx new file mode 100644 index 000000000..51c3a3603 --- /dev/null +++ b/sd/source/ui/inc/prntopts.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SdPrintOptions final : public SfxTabPage +{ + friend class SdModule; + +private: + std::unique_ptr m_xFrmContent; + std::unique_ptr m_xCbxDraw; + std::unique_ptr m_xCbxNotes; + std::unique_ptr m_xCbxHandout; + std::unique_ptr m_xCbxOutline; + std::unique_ptr m_xRbtColor; + std::unique_ptr m_xRbtGrayscale; + std::unique_ptr m_xRbtBlackWhite; + std::unique_ptr m_xCbxPagename; + std::unique_ptr m_xCbxDate; + std::unique_ptr m_xCbxTime; + std::unique_ptr m_xCbxHiddenPages; + std::unique_ptr m_xRbtDefault; + std::unique_ptr m_xRbtPagesize; + std::unique_ptr m_xRbtPagetile; + std::unique_ptr m_xRbtBooklet; + std::unique_ptr m_xCbxFront; + std::unique_ptr m_xCbxBack; + std::unique_ptr m_xCbxPaperbin; + + DECL_LINK(ClickCheckboxHdl, weld::Toggleable&, void); + DECL_LINK(ClickBookletHdl, weld::Toggleable&, void); + + void updateControls(); + +public: + SdPrintOptions(weld::Container* pPage, weld::DialogController* pController, + const SfxItemSet& rInAttrs); + virtual ~SdPrintOptions() override; + + static std::unique_ptr + Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet*); + + virtual bool FillItemSet(SfxItemSet*) override; + virtual void Reset(const SfxItemSet*) override; + + void SetDrawMode(); + virtual void PageCreated(const SfxAllItemSet& aSet) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/pubdlg.hxx b/sd/source/ui/inc/pubdlg.hxx new file mode 100644 index 000000000..1f0b7274c --- /dev/null +++ b/sd/source/ui/inc/pubdlg.hxx @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include "assclass.hxx" + +#include +#include + +class ListBox; +class Edit; +class SdHtmlAttrPreview; +class SdPublishingDesign; +class ButtonSet; + +namespace com::sun::star::beans +{ +struct PropertyValue; +} +namespace com::sun::star::uno +{ +template class Sequence; +} + +// ********************************************************************* +// Html-Export Autopilot +// ********************************************************************* +// should turn this into a wizard +class SdPublishingDlg final : public weld::GenericDialogController +{ +private: + // page 1 controls + std::unique_ptr m_xPage1; + std::unique_ptr m_xPage1_Title; + std::unique_ptr m_xPage1_NewDesign; + std::unique_ptr m_xPage1_OldDesign; + std::unique_ptr m_xPage1_Designs; + std::unique_ptr m_xPage1_DelDesign; + std::unique_ptr m_xPage1_Desc; + + // page 2 controls + std::unique_ptr m_xPage2; + std::unique_ptr m_xPage2Frame2; + std::unique_ptr m_xPage2Frame3; + std::unique_ptr m_xPage2Frame4; + std::unique_ptr m_xPage2_Title; + std::unique_ptr m_xPage2_Standard; + std::unique_ptr m_xPage2_Frames; + std::unique_ptr m_xPage2_SingleDocument; + std::unique_ptr m_xPage2_Kiosk; + std::unique_ptr m_xPage2_WebCast; + std::unique_ptr m_xPage2_Standard_FB; + std::unique_ptr m_xPage2_Frames_FB; + std::unique_ptr m_xPage2_Kiosk_FB; + std::unique_ptr m_xPage2_WebCast_FB; + + std::unique_ptr m_xPage2_Title_Html; + std::unique_ptr m_xPage2_Content; + std::unique_ptr m_xPage2_Notes; + + std::unique_ptr m_xPage2_Title_WebCast; + std::unique_ptr m_xPage2_ASP; + std::unique_ptr m_xPage2_PERL; + std::unique_ptr m_xPage2_URL_txt; + std::unique_ptr m_xPage2_URL; + std::unique_ptr m_xPage2_CGI_txt; + std::unique_ptr m_xPage2_CGI; + std::unique_ptr m_xPage2_Index_txt; + std::unique_ptr m_xPage2_Index; + std::unique_ptr m_xPage2_Title_Kiosk; + std::unique_ptr m_xPage2_ChgDefault; + std::unique_ptr m_xPage2_ChgAuto; + std::unique_ptr m_xPage2_Duration_txt; + std::unique_ptr m_xPage2_Duration; + std::unique_ptr m_xFormatter; + std::unique_ptr m_xPage2_Endless; + + // page 3 controls + std::unique_ptr m_xPage3; + std::unique_ptr m_xPage3_Title1; + std::unique_ptr m_xPage3_Png; + std::unique_ptr m_xPage3_Gif; + std::unique_ptr m_xPage3_Jpg; + std::unique_ptr m_xPage3_Quality_txt; + std::unique_ptr m_xPage3_Quality; + std::unique_ptr m_xPage3_Title2; + std::unique_ptr m_xPage3_Resolution_1; + std::unique_ptr m_xPage3_Resolution_2; + std::unique_ptr m_xPage3_Resolution_3; + std::unique_ptr m_xPage3_Resolution_4; + std::unique_ptr m_xPage3_Title3; + std::unique_ptr m_xPage3_SldSound; + std::unique_ptr m_xPage3_HiddenSlides; + + // page 4 controls + std::unique_ptr m_xPage4; + std::unique_ptr m_xPage4_Title1; + std::unique_ptr m_xPage4_Author_txt; + std::unique_ptr m_xPage4_Author; + std::unique_ptr m_xPage4_Email_txt; + std::unique_ptr m_xPage4_Email; + std::unique_ptr m_xPage4_WWW_txt; + std::unique_ptr m_xPage4_WWW; + std::unique_ptr m_xPage4_Title2; + std::unique_ptr m_xPage4_Misc; + std::unique_ptr m_xPage4_Download; + + // page 5 controls + std::unique_ptr m_xPage5; + std::unique_ptr m_xPage5_Title; + std::unique_ptr m_xPage5_TextOnly; + std::unique_ptr m_xPage5_Buttons; + std::unique_ptr m_xPage5_ButtonsWnd; + + // page 6 controls + std::unique_ptr m_xPage6; + std::unique_ptr m_xPage6_Title; + std::unique_ptr m_xPage6_Default; + std::unique_ptr m_xPage6_User; + std::unique_ptr m_xPage6_Back; + std::unique_ptr m_xPage6_Text; + std::unique_ptr m_xPage6_Link; + std::unique_ptr m_xPage6_VLink; + std::unique_ptr m_xPage6_ALink; + std::unique_ptr m_xPage6_DocColors; + std::unique_ptr m_xPage6_Preview; + std::unique_ptr m_xPage6_PreviewWnd; + + std::unique_ptr m_xButtonSet; + + // standard controls + std::unique_ptr m_xLastPageButton; + std::unique_ptr m_xNextPageButton; + std::unique_ptr m_xFinishButton; + + Assistent aAssistentFunc; + + bool m_bImpress; + bool m_bButtonsDirty; + + void SetDefaults(); + void CreatePages(); + + Color m_aBackColor, m_aTextColor, m_aLinkColor; + Color m_aVLinkColor, m_aALinkColor; + + void ChangePage(); + void UpdatePage(); + + std::vector m_aDesignList; + bool m_bDesignListDirty; + SdPublishingDesign* m_pDesign; + void Load(); + bool Save(); + + void GetDesign(SdPublishingDesign* pDesign); + void SetDesign(SdPublishingDesign const* pDesign); + + void LoadPreviewButtons(); + + DECL_LINK(FinishHdl, weld::Button&, void); + DECL_LINK(NextPageHdl, weld::Button&, void); + DECL_LINK(LastPageHdl, weld::Button&, void); + + DECL_LINK(DesignHdl, weld::Toggleable&, void); + DECL_LINK(DesignSelectHdl, weld::TreeView&, void); + DECL_LINK(DesignDeleteHdl, weld::Button&, void); + DECL_LINK(BaseHdl, weld::Toggleable&, void); + DECL_LINK(ContentHdl, weld::Toggleable&, void); + DECL_LINK(GfxFormatHdl, weld::Toggleable&, void); + DECL_LINK(ResolutionHdl, weld::Toggleable&, void); + DECL_LINK(ButtonsHdl, ValueSet*, void); + DECL_LINK(ColorHdl, weld::Button&, void); + DECL_LINK(WebServerHdl, weld::Toggleable&, void); + DECL_LINK(SlideChgHdl, weld::Toggleable&, void); + +public: + SdPublishingDlg(weld::Window* pWindow, DocumentType eDocType); + virtual ~SdPublishingDlg() override; + + void GetParameterSequence(css::uno::Sequence& rParams); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/registerinterfaces.hxx b/sd/source/ui/inc/registerinterfaces.hxx new file mode 100644 index 000000000..5a8be7dc0 --- /dev/null +++ b/sd/source/ui/inc/registerinterfaces.hxx @@ -0,0 +1,30 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd::ui::table +{ +void RegisterInterfaces(const SfxModule* pMod); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/scalectrl.hxx b/sd/source/ui/inc/scalectrl.hxx new file mode 100644 index 000000000..2a0ed5b28 --- /dev/null +++ b/sd/source/ui/inc/scalectrl.hxx @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SdScaleControl final : public SfxStatusBarControl +{ +public: + SdScaleControl(sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb); + virtual ~SdScaleControl() override; + + virtual void StateChangedAtStatusBarControl(sal_uInt16 nSID, SfxItemState eState, + const SfxPoolItem* pState) override; + + SFX_DECL_STATUSBAR_CONTROL(); + +private: + virtual void Command(const CommandEvent& rCEvt) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdpopup.hxx b/sd/source/ui/inc/sdpopup.hxx new file mode 100644 index 000000000..0eccc914d --- /dev/null +++ b/sd/source/ui/inc/sdpopup.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SvxFieldData; + +/** + * PopupMenu for editing field-commands + */ +class SdFieldPopup +{ +private: + std::unique_ptr m_xBuilder; + std::unique_ptr m_xPopup; + const SvxFieldData* m_pField; + + void Fill( LanguageType eLanguage ); + +public: + SdFieldPopup(const SvxFieldData* pInField, LanguageType eLanguage); + void Execute(weld::Window* pParent, const tools::Rectangle& rRect); + ~SdFieldPopup(); + + SvxFieldData* GetField(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdpreslt.hxx b/sd/source/ui/inc/sdpreslt.hxx new file mode 100644 index 000000000..77187a0b3 --- /dev/null +++ b/sd/source/ui/inc/sdpreslt.hxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +class SfxItemSet; +class ValueSet; +namespace weld { class CustomWeld; } + +namespace sd { +class DrawDocShell; +} + +class SdPresLayoutDlg final + : public weld::GenericDialogController +{ +public: + SdPresLayoutDlg( + ::sd::DrawDocShell* pDocShell, + weld::Window* pWindow, + const SfxItemSet& rInAttrs); + virtual ~SdPresLayoutDlg() override; + + void GetAttr(SfxItemSet& rOutAttrs); + + DECL_LINK(ClickLayoutHdl, ValueSet*, void); + DECL_LINK(ClickLoadHdl, weld::Button&, void); + +private: + ::sd::DrawDocShell* mpDocSh; + + const SfxItemSet& mrOutAttrs; + + std::vector maLayoutNames; + + OUString maName; ///< layout name or file name + tools::Long mnLayoutCount; ///< number of master pages in the document + const OUString maStrNone; + + std::unique_ptr m_xCbxMasterPage; + std::unique_ptr m_xCbxCheckMasters; + std::unique_ptr m_xBtnLoad; + std::unique_ptr m_xVS; + std::unique_ptr m_xVSWin; + + void FillValueSet(); + void Reset(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdtreelb.hxx b/sd/source/ui/inc/sdtreelb.hxx new file mode 100644 index 000000000..ea59ed0b5 --- /dev/null +++ b/sd/source/ui/inc/sdtreelb.hxx @@ -0,0 +1,395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "sdxfer.hxx" +#include +#include + +class SdDrawDocument; +class SfxMedium; +class SfxViewFrame; +class SdNavigatorWin; +class SdrObject; +class SdrObjList; +class SdPage; +struct ImplSVEvent; + +namespace sd { +class ViewShell; + +class DrawDocShell; +#ifndef SV_DECL_DRAW_DOC_SHELL_DEFINED +#define SV_DECL_DRAW_DOC_SHELL_DEFINED +typedef ::tools::SvRef DrawDocShellRef; +#endif +} +namespace svt { + class AcceleratorExecute; +} + +class SdPageObjsTLVDropTarget final : public DropTargetHelper +{ +private: + weld::TreeView& m_rTreeView; + + virtual sal_Int8 AcceptDrop( const AcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const ExecuteDropEvent& rEvt ) override; + +public: + SdPageObjsTLVDropTarget(weld::TreeView& rTreeView); +}; + +class SD_DLLPUBLIC SdPageObjsTLV +{ +private: + static bool SAL_DLLPRIVATE bIsInDrag; ///< static, in the case the navigator is deleted in ExecuteDrag + + std::unique_ptr m_xTreeView; + std::unique_ptr m_xScratchIter; + std::unique_ptr m_xDropTargetHelper; + std::unique_ptr<::svt::AcceleratorExecute> m_xAccel; + SdNavigatorWin* m_pNavigator; + const SdDrawDocument* m_pDoc; + SdDrawDocument* m_pBookmarkDoc; + SfxMedium* m_pMedium; + SfxMedium* m_pOwnMedium; + bool m_bLinkableSelected; + bool m_bShowAllShapes; + + /** This flag controls whether to show all pages. + */ + bool m_bShowAllPages; + + /** + * If changing the selection should also result in navigating to the + * relevant shape. + */ + bool m_bSelectionHandlerNavigates; + + /** + * If navigation should not only select the relevant shape but also change + * focus to it. + */ + bool m_bNavigationGrabsFocus; + + SelectionMode m_eSelectionMode; + + ImplSVEvent* m_nSelectEventId; + ImplSVEvent* m_nRowActivateEventId; + + OUString m_aDocName; + ::sd::DrawDocShellRef m_xBookmarkDocShRef; ///< for the loading of bookmarks + Link m_aChangeHdl; + Link m_aRowActivatedHdl; + Link m_aKeyPressHdl; + + /** Return the name of the object. When the object has no user supplied + name and the bCreate flag is then a name is created + automatically. Additionally the mbShowAllShapes flag is taken into + account when there is no user supplied name. When this flag is + then no name is created. + @param pObject + When this is NULL then an empty string is returned, regardless + of the value of bCreate. + @param bCreate + This flag controls for objects without user supplied name + whether a name is created. When a name is created then this + name is not stored in the object. + */ + OUString GetObjectName ( + const SdrObject* pObject, + const bool bCreate = true) const; + + void CloseBookmarkDoc(); + + DECL_DLLPRIVATE_LINK(RequestingChildrenHdl, const weld::TreeIter&, bool); + DECL_DLLPRIVATE_LINK(SelectHdl, weld::TreeView&, void); + DECL_DLLPRIVATE_LINK(AsyncSelectHdl, void*, void); + DECL_DLLPRIVATE_LINK(RowActivatedHdl, weld::TreeView&, bool); + DECL_DLLPRIVATE_LINK(AsyncRowActivatedHdl, void*, void); + DECL_DLLPRIVATE_LINK(DragBeginHdl, bool&, bool); + DECL_DLLPRIVATE_LINK(KeyInputHdl, const KeyEvent&, bool); + + /** Determine whether the specified page belongs to the current show + which is either the standard show or a custom show. + @param pPage + Pointer to the page for which to check whether it belongs to the + show. + @return + Returns if there is no custom show or if the current + show does not contain the specified page at least once. + */ + bool PageBelongsToCurrentShow (const SdPage* pPage) const; + + bool DoDrag(); + static void OnDragFinished(); + + // DragSourceHelper + bool StartDrag(); + +public: + + SdPageObjsTLV(std::unique_ptr xTreeview); + ~SdPageObjsTLV(); + + void set_sensitive(bool bSensitive) + { + m_xTreeView->set_sensitive(bSensitive); + } + + void hide() + { + m_xTreeView->hide(); + } + + void show() + { + m_xTreeView->show(); + } + + void grab_focus() + { + m_xTreeView->grab_focus(); + } + + void set_size_request(int nWidth, int nHeight) + { + m_xTreeView->set_size_request(nWidth, nHeight); + } + + float get_approximate_digit_width() const + { + return m_xTreeView->get_approximate_digit_width(); + } + + DECL_LINK(MousePressHdl, const MouseEvent&, bool); + DECL_LINK(MouseReleaseHdl, const MouseEvent&, bool); + + void Select(); + + int get_height_rows(int nRows) const + { + return m_xTreeView->get_height_rows(nRows); + } + + void set_selection_mode(SelectionMode eMode) + { + m_eSelectionMode = eMode; + m_xTreeView->set_selection_mode(eMode); + } + + SelectionMode get_selection_mode() const + { + return m_eSelectionMode; + } + + void connect_row_activated(const Link& rLink) + { + m_aRowActivatedHdl = rLink; + } + + void connect_key_press(const Link& rLink) + { + m_aKeyPressHdl = rLink; + } + + bool HasSelectedChildren(std::u16string_view rName); + bool SelectEntry(std::u16string_view rName); + + OUString get_selected_text() const + { + return m_xTreeView->get_selected_text(); + } + + bool get_selected() const + { + return m_xTreeView->get_selected(nullptr); + } + + int count_selected_rows() const + { + return m_xTreeView->count_selected_rows(); + } + + void connect_changed(const Link& rLink) + { + m_aChangeHdl = rLink; + } + + bool is_selected(const weld::TreeIter& rIter) const + { + return m_xTreeView->is_selected(rIter); + } + + bool get_iter_first(weld::TreeIter& rIter) const + { + return m_xTreeView->get_iter_first(rIter); + } + + std::unique_ptr make_iterator() + { + return m_xTreeView->make_iterator(); + } + + bool get_visible() const + { + return m_xTreeView->get_visible(); + } + + void unselect_all() + { + m_xTreeView->unselect_all(); + } + + void SetViewFrame(const SfxViewFrame* pViewFrame); + + void Fill(const SdDrawDocument*, bool bAllPages, const OUString& rDocName); + void Fill(const SdDrawDocument*, SfxMedium* pSfxMedium, const OUString& rDocName); + + void SetShowAllShapes (const bool bShowAllShapes, const bool bFill); + bool GetShowAllShapes() const { return m_bShowAllShapes; } + + bool IsNavigationGrabsFocus() const { return m_bNavigationGrabsFocus; } + bool IsEqualToDoc(const SdDrawDocument* pInDoc); + /// Visits rList recursively and tries to advance rEntry accordingly. + bool IsEqualToShapeList(std::unique_ptr& rEntry, const SdrObjList& rList, + std::u16string_view rListName); + + static bool IsInDrag(); + + /** Return the view shell that is linked to the given doc shell. + Call this method when the there is a chance that the doc shell + has been disconnected from the view shell (but not the other + way round.) + @return + May return when the link between view shell and + doc shell has been severed. + */ + static ::sd::ViewShell* GetViewShellForDocShell (::sd::DrawDocShell &rDocShell); + + /** Add one list box entry for the parent of the given shapes and one child entry for + each of the given shapes. + @param rList + The container of shapes that are to be inserted. + @param pShape + The parent shape or NULL when the parent is a page. + @param rsName + The name to be displayed for the new parent node. + @param bIsExcluded + Some pages can be excluded (from the show?). + @param pParentEntry + The parent entry of the new parent entry. + */ + void AddShapeList ( + const SdrObjList& rList, + const SdrObject* pShape, + const OUString& rsName, + const bool bIsExcluded, + const weld::TreeIter* pParentEntry); + + /** Add the given object to a transferable object so that the object can + be dragged and dropped without having a name. + */ + void AddShapeToTransferable ( + SdTransferable& rTransferable, + const SdrObject& rObject) const; + + /** return selected entries + nDepth == 0 -> pages + nDepth == 1 -> objects */ + + std::vector GetSelectEntryList(const int nDepth) const; + + SdDrawDocument* GetBookmarkDoc(SfxMedium* pMedium = nullptr); + + bool IsLinkableSelected() const { return m_bLinkableSelected; } + + void InsertEntry(const OUString &rName, const OUString &rExpander) + { + m_xTreeView->insert(nullptr, -1, &rName, nullptr, nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, rExpander); + } + + void InsertEntry(const weld::TreeIter* pParent, const OUString& rId, const OUString &rName, const OUString &rExpander, weld::TreeIter* pEntry = nullptr) + { + m_xTreeView->insert(pParent, -1, &rName, &rId, nullptr, nullptr, false, m_xScratchIter.get()); + m_xTreeView->set_image(*m_xScratchIter, rExpander); + if (pEntry) + m_xTreeView->copy_iterator(*m_xScratchIter, *pEntry); + } + + //Mark Current Entry + void SetSdNavigator(SdNavigatorWin* pNavigator); + + void clear() + { + m_xTreeView->clear(); + } + + // nested class to implement the TransferableHelper + class SAL_DLLPRIVATE SdPageObjsTransferable final : public SdTransferable + { + public: + SdPageObjsTransferable( + const INetBookmark& rBookmark, + ::sd::DrawDocShell& rDocShell, + NavigatorDragType eDragType ); + ::sd::DrawDocShell& GetDocShell() const { return mrDocShell;} + NavigatorDragType GetDragType() const { return meDragType;} + + static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId(); + static SdPageObjsTransferable* getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData ) noexcept; + /** Return a temporary transferable data flavor that is used + internally in the navigator for reordering entries. Its + lifetime ends with the office application. + */ + static SotClipboardFormatId GetListBoxDropFormatId(); + + private: + /** Temporary drop flavor id that is used internally in the + navigator. + */ + static SotClipboardFormatId mnListBoxDropFormatId; + + INetBookmark const maBookmark; + ::sd::DrawDocShell& mrDocShell; + NavigatorDragType const meDragType; + virtual ~SdPageObjsTransferable() override; + + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual void DragFinished( sal_Int8 nDropAction ) override; + + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& rId ) override; + }; + + friend class SdPageObjsTLV::SdPageObjsTransferable; + +private: + rtl::Reference m_xHelper; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdundogr.hxx b/sd/source/ui/inc/sdundogr.hxx new file mode 100644 index 000000000..466182faf --- /dev/null +++ b/sd/source/ui/inc/sdundogr.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include + +class SD_DLLPUBLIC SdUndoGroup final : public SdUndoAction +{ + std::vector> aCtn; +public: + SdUndoGroup(SdDrawDocument* pSdDrawDocument) + : SdUndoAction(pSdDrawDocument) + {} + virtual ~SdUndoGroup() override; + + virtual bool Merge( SfxUndoAction* pNextAction ) override; + + virtual void Undo() override; + virtual void Redo() override; + + void AddAction(SdUndoAction* pAction); + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/sdxfer.hxx b/sd/source/ui/inc/sdxfer.hxx new file mode 100644 index 000000000..5e25ba682 --- /dev/null +++ b/sd/source/ui/inc/sdxfer.hxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +// SdTransferable +class SdDrawDocument; +class SdrObject; +class INetBookmark; +class ImageMap; +class VirtualDevice; + +namespace sd { +class DrawDocShell; +class View; +} + +class SAL_DLLPUBLIC_RTTI SdTransferable : public TransferDataContainer, public SfxListener +{ +public: + + SdTransferable( SdDrawDocument* pSrcDoc, ::sd::View* pWorkView, bool bInitOnGetData ); + virtual ~SdTransferable() override; + + void SetDocShell( const SfxObjectShellRef& rRef ) { maDocShellRef = rRef; } + const SfxObjectShellRef& GetDocShell() const { return maDocShellRef; } + + void SetWorkDocument( const SdDrawDocument* pWorkDoc ) { mpSdDrawDocument = mpSdDrawDocumentIntern = const_cast(pWorkDoc); } + const SdDrawDocument* GetWorkDocument() const { return mpSdDrawDocument; } + + void SetView(const ::sd::View* pView); + const ::sd::View* GetView() const { return mpSdView; } + + void SetObjectDescriptor( std::unique_ptr pObjDesc ); + + void SetStartPos( const Point& rStartPos ) { maStartPos = rStartPos; } + const Point& GetStartPos() const { return maStartPos; } + + void SetInternalMove( bool bSet ) { mbInternalMove = bSet; } + bool IsInternalMove() const { return mbInternalMove; } + + bool HasSourceDoc( const SdDrawDocument* pDoc ) const { return( mpSourceDoc == pDoc ); } + + void SetPageBookmarks( std::vector&& rPageBookmarks, bool bPersistent ); + bool IsPageTransferable() const { return mbPageTransferable; } + bool HasPageBookmarks() const { return( mpPageDocShell && ( !maPageBookmarks.empty() ) ); } + const std::vector& GetPageBookmarks() const { return maPageBookmarks; } + ::sd::DrawDocShell* GetPageDocShell() const { return mpPageDocShell; } + + bool SetTableRTF( SdDrawDocument* ); + + static const css::uno::Sequence< sal_Int8 >& getUnoTunnelId(); + static SdTransferable* getImplementation( const css::uno::Reference< css::uno::XInterface >& rxData ) noexcept; + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + virtual void DragFinished( sal_Int8 nDropAction ) override; + SdDrawDocument* GetSourceDoc() const { return mpSourceDoc;} + + /** User data objects can be used to store information temporarily + at the transferable. The slide sorter uses this to store + previews of the slides that are referenced by the + transferable. + */ + class UserData {public:virtual~UserData(){}}; + + /** Add a user data object. When it was added before (and not + removed) then this call is ignored. + */ + void AddUserData (const std::shared_ptr& rpData); + + /** Return the number of user data objects. + */ + sal_Int32 GetUserDataCount() const; + + /** Return the specified user data object. When the index is not + valid, ie not in the range [0,count) then an empty pointer is + returned. + */ + std::shared_ptr GetUserData (const sal_Int32 nIndex) const; + + // XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething(const css::uno::Sequence< sal_Int8 >& rId) override; + +protected: + + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual bool WriteObject( tools::SvRef& rxOStm, void* pUserObject, sal_uInt32 nUserObjectId, const css::datatransfer::DataFlavor& rFlavor ) override; + virtual void ObjectReleased() override final; + +private: + + SfxObjectShellRef maDocShellRef; + ::sd::DrawDocShell* mpPageDocShell; + std::vector maPageBookmarks; + std::unique_ptr mpOLEDataHelper; + std::unique_ptr mpObjDesc; + const ::sd::View* mpSdView; + ::sd::View* mpSdViewIntern; + SdDrawDocument* mpSdDrawDocument; + SdDrawDocument* mpSdDrawDocumentIntern; + SdDrawDocument* mpSourceDoc; + VclPtr mpVDev; + std::unique_ptr mpBookmark; + std::unique_ptr mpGraphic; + std::unique_ptr mpImageMap; + ::tools::Rectangle maVisArea; + Point maStartPos; + bool mbInternalMove : 1; + bool mbOwnDocument : 1; + bool mbOwnView : 1; + bool mbLateInit : 1; + bool mbPageTransferable : 1; + bool mbPageTransferablePersistent : 1; + ::std::vector > maUserData; + + SdTransferable( const SdTransferable& ) = delete; + SdTransferable& operator=( const SdTransferable& ) = delete; + + void CreateObjectReplacement( SdrObject* pObj ); + void CreateData(); + +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/slideshow.hxx b/sd/source/ui/inc/slideshow.hxx new file mode 100644 index 000000000..35b93afe8 --- /dev/null +++ b/sd/source/ui/inc/slideshow.hxx @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +#include + +#include + +#include + +#include +#include + +namespace com::sun::star { + namespace drawing { + class XDrawPage; + } + namespace animations { + class XAnimationNode; + } +} +class SdDrawDocument; +class KeyEvent; +class OutputDevice; +class Size; +namespace vcl { class Window; } +class SfxRequest; +class WorkWindow; +class CommandSwipeData; +class CommandLongPressData; +struct ImplSVEvent; + +// TODO: Remove +#define PAGE_NO_END 65535 + +/* Definition of SlideShow class */ + +namespace sd +{ + +class SlideshowImpl; +class View; +class ViewShell; +class ViewShellBase; +struct PresentationSettingsEx; +class FrameView; + +enum AnimationMode +{ + ANIMATIONMODE_SHOW, + ANIMATIONMODE_PREVIEW +}; + +typedef comphelper::WeakComponentImplHelper< css::presentation::XPresentation2, css::lang::XServiceInfo > SlideshowBase; + +class SlideShow final : public SlideshowBase +{ +public: + /// used by the model to create a slideshow for it + static rtl::Reference< SlideShow > Create( SdDrawDocument* pDoc ); + + // static helper api + static rtl::Reference< SlideShow > GetSlideShow( SdDrawDocument const * pDocument ); + static rtl::Reference< SlideShow > GetSlideShow( SdDrawDocument const & rDocument ); + static rtl::Reference< SlideShow > GetSlideShow( ViewShellBase const & rBase ); + + static css::uno::Reference< css::presentation::XSlideShowController > GetSlideShowController(ViewShellBase const & rBase ); + + static bool StartPreview( ViewShellBase const & rBase, + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ); + + static void Stop( ViewShellBase const & rBase ); + + /// returns true if there is a running presentation for the given ViewShellBase + static bool IsRunning( ViewShellBase const & rBase ); + + /// returns true if there is a running presentation inside the given ViewShell + /// returns false even if there is a running presentation but in another ViewShell + static bool IsRunning( const ViewShell& rViewShell ); + + // helper api + + void startPreview( + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ); + + // uno api + + virtual void disposing(std::unique_lock&) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XPresentation + virtual void SAL_CALL start( ) override; + virtual void SAL_CALL end() override; + virtual void SAL_CALL rehearseTimings( ) override; + + // XPresentation2 + virtual void SAL_CALL startWithArguments(const css::uno::Sequence< css::beans::PropertyValue >& Arguments) override; + virtual sal_Bool SAL_CALL isRunning( ) override; + virtual css::uno::Reference< css::presentation::XSlideShowController > SAL_CALL getController( ) override; + + // legacy api + + // actions + void jumpToPageNumber( sal_Int32 nPage ); // a.k.a. FuSlideShow::JumpToPage() + void jumpToPageIndex( sal_Int32 nIndex ); + void jumpToBookmark( const OUString& sBookmark ); // a.k.a. FuSlideShow::JumpToBookmark() + + /** sets or clears the pause state of the running slideshow. + !!!! This should only be called by the SdShowWindow !!!!*/ + void pause( bool bPause ); + bool swipe(const CommandSwipeData &rSwipeData); + bool longpress(const CommandLongPressData& rLongPressData); + + // settings + bool isFullScreen() const; // a.k.a. FuSlideShow::IsFullScreen() + OutputDevice* getShowWindow(); // a.k.a. FuSlideShow::GetShowWindow() + int getAnimationMode() const; // a.k.a. FuSlideShow::GetAnimationMode() + sal_Int32 getCurrentPageNumber() const; // a.k.a. FuSlideShow::GetCurrentPage() + + // events + void resize( const Size &rSize ); + // return false if the activate failed. callers should call end in response to failure + bool activate(ViewShellBase& rBase); + void deactivate(); + void paint(); + + bool keyInput(const KeyEvent& rKEvt); + + bool dependsOn( ViewShellBase const * pViewShellBase ); + + static sal_Int32 GetDisplay(); + + bool IsExitAfterPresenting() const; + void SetExitAfterPresenting(bool bExit); + +private: + SlideShow( SdDrawDocument* pDoc ); + + DECL_LINK( StartInPlacePresentationConfigurationHdl, void *, void ); + void StartInPlacePresentationConfigurationCallback(); + + void StartInPlacePresentation(); + void StartFullscreenPresentation(); + + /// @throws css::uno::RuntimeException + void ThrowIfDisposed() const; + + void CreateController( ViewShell* pViewSh, ::sd::View* pView, vcl::Window* pParentWindow ); + WorkWindow *GetWorkWindow(); + + SlideShow(const SlideShow&) = delete; + SlideShow& operator=( const SlideShow& ) = delete; + + SvxItemPropertySet maPropSet; + + rtl::Reference< SlideshowImpl > mxController; + /** This flag is used together with mxController.is() to prevent + multiple instances of the slide show for one document. The flag + covers the time before mxController is set. + */ + bool mbIsInStartup; + SdDrawDocument* mpDoc; + + std::shared_ptr< PresentationSettingsEx > mxCurrentSettings; + + ViewShellBase* mpCurrentViewShellBase; + ViewShellBase* mpFullScreenViewShellBase; + FrameView* mpFullScreenFrameView; + ImplSVEvent * mnInPlaceConfigEvent; +}; + +namespace slideshowhelp +{ + SD_DLLPUBLIC void ShowSlideShow(SfxRequest const& rReq, SdDrawDocument& rDoc); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/smarttag.hxx b/sd/source/ui/inc/smarttag.hxx new file mode 100644 index 000000000..6e27979d6 --- /dev/null +++ b/sd/source/ui/inc/smarttag.hxx @@ -0,0 +1,171 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +class KeyEvent; +class MouseEvent; + +namespace sd +{ +class View; +class SmartHdl; + +/** a smart tag represents a visual user interface element on the documents edit view + that is not part of the document. It uses derivations from SmartHdl for its visuals. + A SmartTag adds himself to the given view if created. It removes himself if it + is disposed before the view is disposed. + + Derive from this class to implement your own smart tag. +*/ +class SmartTag : public SimpleReferenceComponent +{ + friend class SmartTagSet; + +public: + explicit SmartTag(::sd::View& rView); + virtual ~SmartTag() override; + + /** returns true if the SmartTag consumes this event. */ + virtual bool MouseButtonDown(const MouseEvent&, SmartHdl&); + + /** returns true if the SmartTag consumes this event. */ + virtual bool KeyInput(const KeyEvent& rKEvt); + + /** returns true if the SmartTag consumes this event. */ + virtual bool Command(const CommandEvent& rCEvt); + + /** returns true if this smart tag is currently selected */ + bool isSelected() const { return mbSelected; } + + ::sd::View& getView() const { return mrView; } + +protected: + virtual sal_Int32 GetMarkablePointCount() const; + virtual sal_Int32 GetMarkedPointCount() const; + virtual bool MarkPoint(SdrHdl& rHdl, bool bUnmark); + virtual void CheckPossibilities(); + virtual bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark); + + virtual void addCustomHandles(SdrHdlList& rHandlerList); + virtual void select(); + virtual void deselect(); + virtual bool getContext(SdrViewContext& rContext); + + virtual void disposing() override; + + ::sd::View& mrView; + bool mbSelected; + +private: + SmartTag(const SmartTag&) = delete; + SmartTag& operator=(const SmartTag&) = delete; +}; + +typedef rtl::Reference SmartTagReference; + +/** class to administrate the available smart tags for a single view. */ +class SmartTagSet +{ + friend class SmartTag; + +public: + explicit SmartTagSet(::sd::View& rView); + ~SmartTagSet(); + + /** selects the given smart tag and updates all handles */ + void select(const SmartTagReference& xTag); + + /** deselects the current selected smart tag and updates all handles */ + void deselect(); + + /** returns the currently selected tag or an empty reference. */ + const SmartTagReference& getSelected() const { return mxSelectedTag; } + + /** returns true if a SmartTag consumes this event. */ + bool MouseButtonDown(const MouseEvent&); + + /** returns true if a SmartTag consumes this event. */ + bool KeyInput(const KeyEvent& rKEvt); + + /** returns true if a SmartTag consumes this event. */ + bool Command(const CommandEvent& rCEvt); + + /** disposes all smart tags and clears the set */ + void Dispose(); + + /** adds the handles from all smart tags to the given list */ + void addCustomHandles(SdrHdlList& rHandlerList); + + /** returns true if the currently selected smart tag has + a special context, returned in rContext. */ + bool getContext(SdrViewContext& rContext) const; + + // support point editing + bool HasMarkablePoints() const; + sal_uLong GetMarkablePointCount() const; + bool HasMarkedPoints() const; + sal_uLong GetMarkedPointCount() const; + bool MarkPoint(SdrHdl& rHdl, bool bUnmark); + bool MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark); + + void CheckPossibilities(); + +private: + SmartTagSet(const SmartTagSet&) = delete; + SmartTagSet& operator=(const SmartTagSet&) = delete; + + /** adds a new smart tag to this set */ + void add(const SmartTagReference& xTag); + + /** removes the given smart tag from this set */ + void remove(const SmartTagReference& xTag); + + std::set maSet; + + ::sd::View& mrView; + SmartTagReference mxSelectedTag; + SmartTagReference mxMouseOverTag; +}; + +/** a derivation from this handle is the visual representation for a smart tag. + One smart tag can have more than one handle. +*/ +class SmartHdl : public SdrHdl +{ +public: + SmartHdl(const SmartTagReference& xTag, SdrObject* pObject, const Point& rPnt, + SdrHdlKind eNewKind); + SmartHdl(const SmartTagReference& xTag, const Point& rPnt, SdrHdlKind eNewKind); + + const SmartTagReference& getTag() const { return mxSmartTag; } + +private: + SmartTagReference mxSmartTag; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tablefunction.hxx b/sd/source/ui/inc/tablefunction.hxx new file mode 100644 index 000000000..fe32f16b5 --- /dev/null +++ b/sd/source/ui/inc/tablefunction.hxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + +namespace sd +{ +void CreateTableFromRTF(SvStream& rStream, SdDrawDocument* pModel); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tabtempl.hxx b/sd/source/ui/inc/tabtempl.hxx new file mode 100644 index 000000000..d32388d7b --- /dev/null +++ b/sd/source/ui/inc/tabtempl.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class SdrModel; +class SfxObjectShell; +class SdrView; + +/** + * Template-Tab-Dialog + */ +class SdTabTemplateDlg final : public SfxStyleDialogController +{ +private: + const SfxObjectShell& rDocShell; + SdrView* pSdrView; + + XColorListRef pColorList; + XGradientListRef pGradientList; + XHatchListRef pHatchingList; + XBitmapListRef pBitmapList; + XPatternListRef pPatternList; + XDashListRef pDashList; + XLineEndListRef pLineEndList; + + virtual void PageCreated(const OString& rId, SfxTabPage &rPage) override; + virtual void RefreshInputSet() override; + +public: + SdTabTemplateDlg(weld::Window* pParent, + const SfxObjectShell* pDocShell, + SfxStyleSheetBase& rStyleBase, + SdrModel const * pModel, + SdrView* pView); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/titledockwin.hxx b/sd/source/ui/inc/titledockwin.hxx new file mode 100644 index 000000000..59e7e04b3 --- /dev/null +++ b/sd/source/ui/inc/titledockwin.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class ToolBox; + +namespace sd +{ + class TitledDockingWindow : public SfxDockingWindow + { + public: + TitledDockingWindow( + SfxBindings* i_pBindings, SfxChildWindow* i_pChildWindow, + vcl::Window* i_pParent + ); + + virtual ~TitledDockingWindow() override; + virtual void dispose() override; + + /** sets a title to be displayed in the docking window + */ + void SetTitle( const OUString& i_rTitle ); + + /** returns the content window, which is to be used as parent window for any content to be displayed + in the docking window. + */ + vcl::Window& GetContentWindow() { return *m_aContentWindow; } + const vcl::Window& GetContentWindow() const { return *m_aContentWindow; } + + /** Return the border that is painted around the inner window as + decoration. + */ + const SvBorder& GetDecorationBorder() const { return m_aBorder; } + + protected: + // Window overridables + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& i_rArea) override; + virtual void Resize() override; + virtual void StateChanged( StateChangedType i_nType ) override; + virtual void DataChanged( const DataChangedEvent& i_rDataChangedEvent ) override; + virtual void SetText( const OUString& i_rText ) override; + + virtual void ApplySettings(vcl::RenderContext& rRenderContext) override; + protected: + /** internal version of ResetToolBox + */ + void impl_resetToolBox(); + + private: + DECL_LINK(OnToolboxItemSelected, ToolBox*, void); + + void impl_layout(); + + private: + OUString m_sTitle; + VclPtr m_aToolbox; + VclPtr m_aContentWindow; + + /** The border that is painted around the inner window. The bevel + shadow lines are part of the border, so where the border is 0 no + such line is painted. + */ + SvBorder m_aBorder; + + /** Height of the title bar. Calculated in impl_layout(). + */ + int m_nTitleBarHeight; + + }; + +} // namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tmplctrl.hxx b/sd/source/ui/inc/tmplctrl.hxx new file mode 100644 index 000000000..bdf6eed86 --- /dev/null +++ b/sd/source/ui/inc/tmplctrl.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include + +class SdTemplateControl final : public SfxStatusBarControl +{ +public: + SdTemplateControl( sal_uInt16 nSlotId, sal_uInt16 nId, StatusBar& rStb ); + virtual ~SdTemplateControl() override; + + virtual void StateChangedAtStatusBarControl( sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState ) override; + virtual void Paint( const UserDrawEvent& rEvt ) override; + + SFX_DECL_STATUSBAR_CONTROL(); + +private: + virtual void Command( const CommandEvent& rCEvt ) override; + + OUString msTemplate; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/AsynchronousCall.hxx b/sd/source/ui/inc/tools/AsynchronousCall.hxx new file mode 100644 index 000000000..fa4c18020 --- /dev/null +++ b/sd/source/ui/inc/tools/AsynchronousCall.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + +namespace sd::tools +{ +/** Store a function object and execute it asynchronous. + + The features of this class are: + a) It provides a wrapper around a VCL Timer so that generic function + objects can be used. + b) When more than one function objects are posted to be executed later + then the pending ones are erased and only the last one will actually be + executed. + + Use this class like this: + aInstanceOfAsynchronousCall.Post( + ::std::bind( + ::std::mem_fun(&DrawViewShell::SwitchPage), + pDrawViewShell, + 11)); +*/ +class AsynchronousCall +{ +public: + /** Create a new asynchronous call. Each object of this class processes + one (semantical) type of call. + */ + AsynchronousCall(); + + ~AsynchronousCall(); + + /** Post a function object that is to be executed asynchronously. When + this method is called while the current function object has not been + executed then the latter is destroyed and only the given function + object will be executed. + @param rFunction + The function object that may be called asynchronously in the + near future. + */ + typedef ::std::function AsynchronousFunction; + void Post(const AsynchronousFunction& rFunction); + +private: + Timer maTimer; + /** The function object that will be executed when the TimerCallback + function is called the next time. This pointer may be NULL. + */ + ::std::unique_ptr mpFunction; + DECL_LINK(TimerCallback, Timer*, void); +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/AsynchronousTask.hxx b/sd/source/ui/inc/tools/AsynchronousTask.hxx new file mode 100644 index 000000000..696423439 --- /dev/null +++ b/sd/source/ui/inc/tools/AsynchronousTask.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +namespace sd::tools +{ +/** Interface for the asynchronous execution of a task. This interface + allows a controller to run the task either timer based with a fixed + amount of time between the steps or thread based one step right after + the other. +*/ +class AsynchronousTask +{ +public: + /** Run the next step of the task. After HasNextStep() returns false + this method should ignore further calls. + */ + virtual void RunNextStep() = 0; + + /** Return when there is at least one more step to execute. + When the task has been executed completely then is + returned. + */ + virtual bool HasNextStep() = 0; + +protected: + ~AsynchronousTask() {} +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/ConfigurationAccess.hxx b/sd/source/ui/inc/tools/ConfigurationAccess.hxx new file mode 100644 index 000000000..b86a30fff --- /dev/null +++ b/sd/source/ui/inc/tools/ConfigurationAccess.hxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include + +namespace com::sun::star::container { class XHierarchicalNameAccess; } +namespace com::sun::star::container { class XNameAccess; } +namespace com::sun::star::lang { class XMultiServiceFactory; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::tools { + +/** This class gives access to the configuration. Create an object of this + class for one node of the configuration. This will be the root node. + Its children are then accessible through the new ConfigurationAccess + object. +*/ +class ConfigurationAccess +{ +public: + enum WriteMode { READ_WRITE, READ_ONLY }; + + /** Create a new object to access the configuration entries below the + given root. + @param rsRootName + Name of the root. + @param eMode + This flag specifies whether to give read-write or read-only + access. + */ + ConfigurationAccess( + const OUString& rsRootName, + const WriteMode eMode); + + ConfigurationAccess( + const css::uno::Reference& rxContext, + const OUString& rsRootName, + const WriteMode eMode); + + /** Return a configuration node below the root of the called object. + @param rsPathToNode + The relative path from the root (as given the constructor) to + the node. + @return + The type of the returned node varies with the requested node. + It is empty when the node was not found. + */ + css::uno::Any GetConfigurationNode ( + const OUString& rsPathToNode); + + /** Return a configuration node below the given node. + @param rxNode + The node that acts as root to the given relative path. + @param rsPathToNode + The relative path from the given node to the requested node. + @return + The type of the returned node varies with the requested node. + It is empty when the node was not found. + */ + static css::uno::Any GetConfigurationNode ( + const css::uno::Reference& rxNode, + const OUString& rsPathToNode); + + /** Write any changes that have been made back to the configuration. + This call is ignored when the called ConfigurationAccess object was + not create with read-write mode. + */ + void CommitChanges(); + + /** This functor is typically called for every item in a set. Its two + parameters are the name of key item (often of no further interest) + and the value of the item. + */ + typedef ::std::function&) > Functor; + + /** Execute a functor for all elements of the given container. + @param rxContainer + The container is a XNameAccess to a list of the configuration. + This can be a node returned by GetConfigurationNode(). + @param rArguments + The functor is called with arguments that are children of each + element of the container. The set of children is specified in this + list. + @param rFunctor + The functor to be executed for some or all of the elements in + the given container. + */ + static void ForAll ( + const css::uno::Reference& rxContainer, + const ::std::vector& rArguments, + const Functor& rFunctor); + + /** Fill a list with the string contents of all sub-elements in the given container. + @param rxContainer + The container is a XNameAccess to a list of the configuration. + This can be a node returned by GetConfigurationNode(). + @param rsArgument + This specifies which string children of the elements in the + container are to be inserted into the list. The specified child + has to be of type string. + @param rList + The list to be filled. + */ + static void FillList( + const css::uno::Reference& rxContainer, + const OUString& rsArgument, + ::std::vector& rList); + +private: + css::uno::Reference mxRoot; + + void Initialize ( + const css::uno::Reference& rxProvider, + const OUString& rsRootName, + const WriteMode eMode); +}; + +} // end of namespace sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/GraphicSizeCheck.hxx b/sd/source/ui/inc/tools/GraphicSizeCheck.hxx new file mode 100644 index 000000000..44f78e4eb --- /dev/null +++ b/sd/source/ui/inc/tools/GraphicSizeCheck.hxx @@ -0,0 +1,116 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#pragma once + +#include +#include +#include +#include + +namespace sd +{ +/** + * Class responsible to check if a graphic object violates the size + * constraints and store the results. + */ +class GraphicSizeViolation final +{ +private: + SdrGrafObj* m_pGraphicObject; + + sal_Int32 m_nLowDPILimit = 0; + sal_Int32 m_nHighDPILimit = 0; + + sal_Int32 m_nDPIX = 0; + sal_Int32 m_nDPIY = 0; + +public: + GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* pGraphicObject); + bool check(); + + const OUString& getGraphicName(); + + SdrGrafObj* getObject() const { return m_pGraphicObject; } + + bool isDPITooLow() { return m_nDPIX < m_nLowDPILimit || m_nDPIY < m_nLowDPILimit; } + + bool isDPITooHigh() { return m_nDPIX > m_nHighDPILimit || m_nDPIY > m_nHighDPILimit; } + + sal_Int32 getDPIX() { return m_nDPIX; } + + sal_Int32 getDPIY() { return m_nDPIY; } +}; + +/** + * Run the graphic size checks for all the graphic objects in the DOM + * and store a list of violations. + */ +class GraphicSizeCheck final +{ +private: + SdDrawDocument* m_pDocument; + std::vector> m_aGraphicSizeViolationList; + +public: + GraphicSizeCheck(SdDrawDocument* pDocument) + : m_pDocument(pDocument) + { + } + + void check(); + + std::vector>& getViolationList() + { + return m_aGraphicSizeViolationList; + } +}; + +/** The UI part of the GraphicSizeViolation used by GenericCheckDialog */ +class GraphicSizeCheckGUIEntry : public svx::CheckData +{ +private: + SdDrawDocument* m_pDocument; + std::unique_ptr m_pViolation; + +public: + GraphicSizeCheckGUIEntry(SdDrawDocument* pDocument, + std::unique_ptr&& pViolation) + : m_pDocument(pDocument) + , m_pViolation(std::move(pViolation)) + { + } + + OUString getText() override; + + bool canMarkObject() override { return true; } + + void markObject() override; + + bool hasProperties() override { return true; } + + void runProperties() override; +}; + +/** + * The UI part presenting the graphic size check results, which is + * used by GenericCheckDialog + */ +class GraphicSizeCheckGUIResult : public svx::CheckDataCollection +{ +public: + GraphicSizeCheckGUIResult(SdDrawDocument* m_pDocument); + + OUString getTitle() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/IconCache.hxx b/sd/source/ui/inc/tools/IconCache.hxx new file mode 100644 index 000000000..fef994764 --- /dev/null +++ b/sd/source/ui/inc/tools/IconCache.hxx @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd +{ +/** This simple class stores frequently used icons so that the classes that + use the icons do not have to store them in every one of their + instances. + + Icons are addressed over their resource id and are loaded on demand. + + This cache acts like a singleton with a lifetime equal to that of the sd + module. +*/ +class IconCache final : public SdGlobalResource +{ +public: + /** The lifetime of the returned reference is limited to that of the sd + module. + */ + static IconCache& Instance(); + + /** Return the icon with the given resource id. + @return + The returned Image may be empty when there is no icon for the + given id or an error occurred. Should not happen under normal + circumstances. + */ + Image GetIcon(const OUString& rResourceId); + +private: + class Implementation; + ::std::unique_ptr mpImpl; + + /** The constructor creates the one instance of the cache and registers + it at the SdGlobalResourceContainer to limit is lifetime to that of + the sd module. + */ + IconCache(); + + /** This destructor is called by SdGlobalResourceContainer. + */ + virtual ~IconCache() override; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/IdleDetection.hxx b/sd/source/ui/inc/tools/IdleDetection.hxx new file mode 100644 index 000000000..decf5ff26 --- /dev/null +++ b/sd/source/ui/inc/tools/IdleDetection.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace vcl { class Window; } + +namespace sd::tools { + enum class IdleState { + /** When GetIdleState() returns this value, then the system is idle. + */ + Idle = 0x0000, + + /** There are system event pending. + */ + SystemEventPending = 0x0001, + + /** A full screen slide show is running and is active. In contrast + there may be a full screen show be running in an inactive window, + i.e. in the background. + */ + FullScreenShowActive = 0x0002, + + /** A slide show is running in a window. + */ + WindowShowActive = 0x0004, + + /** A window is being painted. + */ + WindowPainting = 0x0008, + }; +} // end of namespace ::sd::tools +namespace o3tl { + template<> struct typed_flags<::sd::tools::IdleState> : is_typed_flags<::sd::tools::IdleState, 0x0f> {}; +} + +namespace sd::tools { + +/** Detect whether the system is idle and some time consuming operation may + be carried out. This class distinguishes between different states of + idle-ness. +*/ +class IdleDetection +{ +public: + /** Determine whether the system is idle. + @param pWindow + When a valid Window pointer is given then it is checked + whether the window is currently being painting. + @return + This method either returns IdleState::Idle or a combination of + IdleStates values or-ed together that describe what the system + is currently doing so that the caller can decide what to do. + */ + static IdleState GetIdleState (const vcl::Window* pWindow); + +private: + /** Check whether there are input events pending. + */ + static IdleState CheckInputPending(); + + /** Check whether a slide show is running full screen or in a window. + */ + static IdleState CheckSlideShowRunning(); + + static IdleState CheckWindowPainting (const vcl::Window& rWindow); +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/PropertySet.hxx b/sd/source/ui/inc/tools/PropertySet.hxx new file mode 100644 index 000000000..c432783da --- /dev/null +++ b/sd/source/ui/inc/tools/PropertySet.hxx @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace sd::tools { + +typedef ::cppu::WeakComponentImplHelper < + css::beans::XPropertySet +> PropertySetInterfaceBase; + +/** A very simple implementation of the XPropertySet interface. It does not + support constrained properties and thus does not support vetoable + listeners. It does not support the optional property set info. + + In order to use it you have to derive from this class and implement the + GetPropertyValue() and SetPropertyValue() methods. +*/ +class PropertySet + : protected ::cppu::BaseMutex, + public PropertySetInterfaceBase +{ +public: + explicit PropertySet(); + virtual ~PropertySet() override; + + virtual void SAL_CALL disposing() override; + + // XPropertySet + + virtual css::uno::Reference + SAL_CALL getPropertySetInfo() override; + + virtual void SAL_CALL setPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rsPropertyValue) override; + + virtual css::uno::Any SAL_CALL getPropertyValue (const OUString& rsPropertyName) override; + + virtual void SAL_CALL addPropertyChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL removePropertyChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL addVetoableChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL removeVetoableChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference& rxListener) override; + +protected: + /** Return the requested property value. + @throw css::beans::UnknownPropertyException when the + property is not supported. + */ + virtual css::uno::Any GetPropertyValue (const OUString& rsPropertyName) = 0; + /** Set the given property value. + @return the old value. + @throw css::beans::UnknownPropertyException when the + property is not supported. + */ + virtual css::uno::Any SetPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rValue) = 0; + +private: + typedef ::std::multimap > ChangeListenerContainer; + std::unique_ptr mpChangeListeners; + + /** Call all listeners that are registered for the given property name. + Call this method with an empty property name to call listeners that + are registered for all properties. + */ + void CallListeners ( + const OUString& rsPropertyName, + const css::beans::PropertyChangeEvent& rEvent); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/SdGlobalResourceContainer.hxx b/sd/source/ui/inc/tools/SdGlobalResourceContainer.hxx new file mode 100644 index 000000000..a582ffa72 --- /dev/null +++ b/sd/source/ui/inc/tools/SdGlobalResourceContainer.hxx @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::uno +{ +template class Reference; +} +namespace com::sun::star::uno +{ +class XInterface; +} + +namespace sd +{ +class SdGlobalResource +{ +public: + virtual ~SdGlobalResource() COVERITY_NOEXCEPT_FALSE{}; +}; + +/** The purpose of this container is to hold references to resources that + are globally available to all interested objects and to destroy them + when the sd module is destroyed. Examples for resources can be + containers of bitmaps or the container of master pages used by the + MasterPagesSelector objects in the task panel. + + It works like a singleton in that there is one instance per sd module. + Resources can be added (by themselves or their owners) to the + container. The main task of the container is the destruction of all + resources that have been added to it. + + As you may note, there is no method to get a resource from the + container. It is the task of the resource to provide other means of + access to it. + + The reason for this design is not to have to change the SdModule + destructor every time when there is a new resource. This is done by + reversing the dependency between module and resource: the resource knows + about the module--this container class to be more precisely--and tells + it to destroy the resource when the sd module is at the end of its + lifetime. +*/ +class SdGlobalResourceContainer final +{ +public: + static SdGlobalResourceContainer& Instance(); + + /** Add a resource to the container. The ownership of the resource is + transferred to the container. The resource is destroyed when the + container is destroyed, i.e. when the sd module is destroyed. + + When in doubt, use the shared_ptr variant of this method. + */ + void AddResource(::std::unique_ptr pResource); + + /** Add a resource to the container. By using a shared_ptr and + releasing it only when the SgGlobalResourceContainer is destroyed + the given resource is kept alive at least that long. When at the + time of the destruction of SgGlobalResourceContainer no other + references exist the resource is destroyed as well. + */ + void AddResource(const std::shared_ptr& pResource); + + /** Add a resource that is implemented as UNO object. Destruction + (when the sd modules is unloaded) is done by a) calling dispose() + when the XComponent is supported and by b) releasing the reference. + */ + void AddResource(const css::uno::Reference& rxResource); + +private: + friend class SdGlobalResourceContainerInstance; + friend struct o3tl::default_delete; + + class Implementation; + ::std::unique_ptr mpImpl; + + SdGlobalResourceContainer(); + ~SdGlobalResourceContainer(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/SlotStateListener.hxx b/sd/source/ui/inc/tools/SlotStateListener.hxx new file mode 100644 index 000000000..54a2e463d --- /dev/null +++ b/sd/source/ui/inc/tools/SlotStateListener.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace com::sun::star::frame { class XDispatch; } +namespace com::sun::star::frame { class XDispatchProvider; } +namespace com::sun::star::frame { class XStatusListener; } +namespace com::sun::star::frame { struct FeatureStateEvent; } + +namespace sd::tools { + +typedef comphelper::WeakComponentImplHelper< + css::frame::XStatusListener + > SlotStateListenerInterfaceBase; + +/** Listen for state changes of slots. This class has been created in order + to be informed when the support for vertical writing changes but it can + be used to relay state changes of other slots as well. +*/ +class SlotStateListener final + : public SlotStateListenerInterfaceBase +{ +public: + /** This convenience version of the constructor takes all parameters + that are necessary to observe a single slot. See descriptions of + the SetCallback(), ConnectToFrame(), and ObserveSlot() methods for + explanations about the parameters. + */ + SlotStateListener ( + Link const & rCallback, + const css::uno::Reference& rxDispatchProvider, + const OUString& rSlotName); + + /** The constructor de-registers all remaining listeners. Usually a prior + dispose() call should have done that already. + */ + virtual ~SlotStateListener() override; + + /** Set the callback to the given value. Whenever one of the observed + slots changes its state this callback is informed about it. + Changing the callback does not release the listeners. + @throws DisposedException + */ + void SetCallback (const Link& rCallback); + + /** Set the frame whose slots shall be observed. When an object of this + class is already observing slots of another frame then these + listeners are released first. + @throws DisposedException + */ + void ConnectToDispatchProvider ( + const css::uno::Reference& rxDispatchProvider); + + /** Observe the slot specified by the given name. Note that + ConnectToFrame() has to have been called earlier. + @param rSlotName + The name of the slot to observe. An example is + ".uno:VerticalTextState". + @throws DisposedException + */ + void ObserveSlot (const OUString& rSlotName); + + //===== frame::XStatusListener ========================================== + + /** Called by slot state change broadcasters. In turn the callback is + informed about the state change. + @throws DisposedException + */ + virtual void SAL_CALL + statusChanged ( + const css::frame::FeatureStateEvent& rState) override; + + //===== lang::XEventListener ============================================ + + virtual void SAL_CALL + disposing(const css::lang::EventObject& rEvent) override; + +private: + /** This method is called by the WeakComponentImplHelper base class in + reaction to a XComponent::dispose() call. It releases all currently + active listeners. + */ + virtual void disposing(std::unique_lock&) override; + + Link maCallback; + + /** Remember the URLs that describe slots whose state changes we are + listening to. + */ + std::vector maRegisteredURLList; + + css::uno::WeakReference mxDispatchProviderWeak; + + /** Deregister all currently active state change listeners. + */ + void ReleaseListeners(); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); + + /** Transform the given string into a URL object. + */ + static css::util::URL MakeURL (const OUString& rSlotName); + + /** Return an XDispatch object for the given URL. + */ + css::uno::Reference + GetDispatch ( + const css::util::URL& rURL) const; +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tools/TimerBasedTaskExecution.hxx b/sd/source/ui/inc/tools/TimerBasedTaskExecution.hxx new file mode 100644 index 000000000..bbff549f1 --- /dev/null +++ b/sd/source/ui/inc/tools/TimerBasedTaskExecution.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +namespace sd::tools { + +class AsynchronousTask; + +/** Execute an AsynchronousTask timer based, i.e. every + nMillisecondsBetweenSteps milliseconds as much steps are executed as fit + into a nMaxTimePerStep millisecond interval. + + When a task is executed completely, i.e. HasNextStep() returns , + the TimerBasedTaskExecution destroys itself. This, of course, works + only if the creating instance does not hold a shared_ptr to that object. +*/ +class TimerBasedTaskExecution +{ +public: + /** Create a new object of this class. + @param rpTask + The AsynchronousTask that is to be executed. + @param nMillisecondsBetweenSteps + Wait at least this long between the execution of steps. Note + that more than one step may be executed in succession. + @param nMaxTimePerStep + The maximal time for executing steps without yielding control. + */ + static std::shared_ptr Create ( + const std::shared_ptr& rpTask, + sal_uInt32 nMillisecondsBetweenSteps, + sal_uInt32 nMaxTimePerStep); + + /** Stop the execution of the task and release the shared pointer to + itself so that it will eventually be destroyed. + */ + void Release(); + + /** Convenience method that calls Release() on the given task. It + checks the given weak_ptr for being expired and catches bad_weak_ptr + exceptions. + */ + static void ReleaseTask (const std::weak_ptr& rpTask); + +private: + std::shared_ptr mpTask; + Timer maTimer; + /** This shared_ptr to this is used to destroy a TimerBasedTaskExecution + object when its task has been executed completely. + */ + std::shared_ptr mpSelf; + sal_uInt32 mnMaxTimePerStep; + + TimerBasedTaskExecution ( + const std::shared_ptr& rpTask, + sal_uInt32 nMillisecondsBetweenSteps, + sal_uInt32 nMaxTimePerStep); + ~TimerBasedTaskExecution(); + + class Deleter; + friend class Deleter; + + DECL_LINK(TimerCallback, Timer *, void); +}; + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tpaction.hxx b/sd/source/ui/inc/tpaction.hxx new file mode 100644 index 000000000..893192d25 --- /dev/null +++ b/sd/source/ui/inc/tpaction.hxx @@ -0,0 +1,104 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include "sdtreelb.hxx" + +#include + +namespace sd { + class View; +} +class SdDrawDocument; + +/** + * Effect-SingleTab-Dialog + */ +class SdActionDlg final : public SfxSingleTabDialogController +{ +public: + SdActionDlg(weld::Window* pParent, const SfxItemSet* pAttr, ::sd::View const * pView); +}; + +/** + * Interaction-Tab-Page + */ +class SdTPAction final : public SfxTabPage +{ +private: + const ::sd::View* mpView; + SdDrawDocument* mpDoc; + XColorListRef pColList; + + bool bTreeUpdated; + std::vector maCurrentActions; + OUString aLastFile; + ::std::vector< tools::Long > aVerbVector; + + std::unique_ptr m_xLbAction; + std::unique_ptr m_xFtTree; // jump destination controls + std::unique_ptr m_xLbTree; + std::unique_ptr m_xLbTreeDocument; + std::unique_ptr m_xLbOLEAction; + std::unique_ptr m_xFrame; + std::unique_ptr m_xEdtSound; + std::unique_ptr m_xEdtBookmark; + std::unique_ptr m_xEdtDocument; + std::unique_ptr m_xEdtProgram; + std::unique_ptr m_xEdtMacro; + std::unique_ptr m_xBtnSearch; + std::unique_ptr m_xBtnSeek; + + DECL_LINK( ClickSearchHdl, weld::Button&, void ); + DECL_LINK( ClickActionHdl, weld::ComboBox&, void ); + DECL_LINK( SelectTreeHdl, weld::TreeView&, void ); + DECL_LINK( CheckFileHdl, weld::Widget&, void ); + + void UpdateTree(); + void OpenFileDialog(); + css::presentation::ClickAction GetActualClickAction(); + void SetActualClickAction( css::presentation::ClickAction eCA ); + void SetEditText( OUString const & rStr ); + OUString GetEditText( bool bURL = false ); +public: + SD_DLLPUBLIC static TranslateId GetClickActionSdResId(css::presentation::ClickAction eCA); + + SdTPAction(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs); + virtual ~SdTPAction() override; + + static std::unique_ptr Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& ); + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet * ) override; + + virtual void ActivatePage( const SfxItemSet& rSet ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + + void Construct(); + + void SetView( const ::sd::View* pSdView ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/tpoption.hxx b/sd/source/ui/inc/tpoption.hxx new file mode 100644 index 000000000..8657db27a --- /dev/null +++ b/sd/source/ui/inc/tpoption.hxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +/** + * Option-Tab-Page: Snap + */ +class SdTpOptionsSnap final : public SvxGridTabPage +{ +public: + SdTpOptionsSnap(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs); + static std::unique_ptr Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* ); + virtual ~SdTpOptionsSnap() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet * ) override; +}; + +/** + * Option-Tab-Page: Contents + */ +class SdTpOptionsContents final : public SfxTabPage +{ +private: + std::unique_ptr m_xCbxRuler; + std::unique_ptr m_xCbxDragStripes; + std::unique_ptr m_xCbxHandlesBezier; + std::unique_ptr m_xCbxMoveOutline; + +public: + SdTpOptionsContents(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs); + static std::unique_ptr Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* ); + virtual ~SdTpOptionsContents() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet * ) override; +}; + +/** + * Option-Tab-Page: View + */ + +class SdTpOptionsMisc final : public SfxTabPage +{ + friend class SdModule; + +private: + sal_uInt32 nWidth; + sal_uInt32 nHeight; + OUString aInfo1; + OUString aInfo2; + + MapUnit ePoolUnit; + + std::unique_ptr m_xCbxQuickEdit; + std::unique_ptr m_xCbxPickThrough; + + std::unique_ptr m_xNewDocumentFrame; + std::unique_ptr m_xCbxStartWithTemplate; + + std::unique_ptr m_xCbxMasterPageCache; + std::unique_ptr m_xCbxCopy; + std::unique_ptr m_xCbxMarkedHitMovesAlways; + std::unique_ptr m_xPresentationFrame; + + std::unique_ptr m_xLbMetric; + std::unique_ptr m_xMtrFldTabstop; + + std::unique_ptr m_xCbxEnableSdremote; + std::unique_ptr m_xCbxEnablePresenterScreen; + std::unique_ptr m_xCbxUsePrinterMetrics; + std::unique_ptr m_xCbxCompatibility; + + //Scale + std::unique_ptr m_xScaleFrame; + std::unique_ptr m_xCbScale; + std::unique_ptr m_xNewDocLb; + std::unique_ptr m_xFiInfo1; + std::unique_ptr m_xMtrFldOriginalWidth; + std::unique_ptr m_xWidthLb; + std::unique_ptr m_xHeightLb; + std::unique_ptr m_xFiInfo2; + std::unique_ptr m_xMtrFldOriginalHeight; + std::unique_ptr m_xCbxDistort; + std::unique_ptr m_xMtrFldInfo1; + std::unique_ptr m_xMtrFldInfo2; + + static OUString GetScale( sal_Int32 nX, sal_Int32 nY ); + static bool SetScale( std::u16string_view aScale, sal_Int32& rX, sal_Int32& rY ); + + DECL_LINK( SelectMetricHdl_Impl, weld::ComboBox&, void ); + + /** Enable or disable the controls in the compatibility section of the + 'general' tab page depending on whether there is at least one + document. + */ + void UpdateCompatibilityControls(); + + virtual void ActivatePage( const SfxItemSet& rSet ) override; + virtual DeactivateRC DeactivatePage( SfxItemSet* pSet ) override; + +public: + SdTpOptionsMisc(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rInAttrs); + static std::unique_ptr Create( weld::Container* pPage, weld::DialogController* pController, const SfxItemSet* ); + virtual ~SdTpOptionsMisc() override; + + virtual bool FillItemSet( SfxItemSet* ) override; + virtual void Reset( const SfxItemSet * ) override; + + /** Hide Impress specific controls, make Draw specific controls visible + and arrange the visible controls. Do not call this method or the + SetImpressMode() method more than once. + */ + void SetDrawMode(); + + /** Hide Draw specific controls, make Impress specific controls visible + and arrange the visible controls. Do not call this method or the + SetDrawMode() method more than once. + */ + void SetImpressMode(); + virtual void PageCreated(const SfxAllItemSet& aSet) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/uiobject.hxx b/sd/source/ui/inc/uiobject.hxx new file mode 100644 index 000000000..06cb6105f --- /dev/null +++ b/sd/source/ui/inc/uiobject.hxx @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include "Window.hxx" + +class ImpressWindowUIObject final : public WindowUIObject +{ +public: + ImpressWindowUIObject(const VclPtr& xWindow); + + virtual StringMap get_state() override; + + virtual void execute(const OUString& rAction, const StringMap& rParameters) override; + + virtual std::unique_ptr get_child(const OUString& rID) override; + + virtual std::set get_children() const override; + + static std::unique_ptr create(vcl::Window* pWindow); + +private: + virtual OUString get_name() const override; + + VclPtr mxWindow; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unchss.hxx b/sd/source/ui/inc/unchss.hxx new file mode 100644 index 000000000..7c3845f8b --- /dev/null +++ b/sd/source/ui/inc/unchss.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include + +class SfxItemSet; +class SfxStyleSheet; +class SdDrawDocument; + +class StyleSheetUndoAction final : public SdUndoAction +{ + SfxStyleSheet* mpStyleSheet; + + std::unique_ptr mpNewSet; + std::unique_ptr mpOldSet; + +public: + StyleSheetUndoAction(SdDrawDocument* pTheDoc, SfxStyleSheet* pTheStyleSheet, + const SfxItemSet* pTheNewItemSet); + + virtual void Undo() override; + virtual void Redo() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/undoback.hxx b/sd/source/ui/inc/undoback.hxx new file mode 100644 index 000000000..afbb13eca --- /dev/null +++ b/sd/source/ui/inc/undoback.hxx @@ -0,0 +1,58 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include + +class SdDrawDocument; +class SdPage; +class SfxItemSet; +class SfxPoolItem; + +class SdBackgroundObjUndoAction final : public SdUndoAction +{ +private: + + SdPage& mrPage; + std::unique_ptr mpItemSet; + std::unique_ptr mpFillBitmapItem; + bool mbHasFillBitmap; + + void ImplRestoreBackgroundObj(); + void saveFillBitmap(SfxItemSet &rItemSet); + void restoreFillBitmap(SfxItemSet &rItemSet); + +public: + SdBackgroundObjUndoAction( + SdDrawDocument& rDoc, + SdPage& rPage, + const SfxItemSet& rItemSet); + + virtual void Undo() override; + virtual void Redo() override; + + virtual SdUndoAction* Clone() const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/undoheaderfooter.hxx b/sd/source/ui/inc/undoheaderfooter.hxx new file mode 100644 index 000000000..2c8c9c8e9 --- /dev/null +++ b/sd/source/ui/inc/undoheaderfooter.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class SdDrawDocument; + +/************************************************************************/ + +class SD_DLLPUBLIC SdHeaderFooterUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + + const sd::HeaderFooterSettings maOldSettings; + const sd::HeaderFooterSettings maNewSettings; + +public: + SdHeaderFooterUndoAction( SdDrawDocument* pDoc, SdPage* pPage, const sd::HeaderFooterSettings& rNewSettings ); + virtual ~SdHeaderFooterUndoAction() override; + + virtual void Undo() override; + virtual void Redo() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/undolayer.hxx b/sd/source/ui/inc/undolayer.hxx new file mode 100644 index 000000000..431f60d4f --- /dev/null +++ b/sd/source/ui/inc/undolayer.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SdDrawDocument; +class SdrLayer; + +/************************************************************************/ + +class SdLayerModifyUndoAction final : public SdUndoAction +{ + +public: + SdLayerModifyUndoAction( SdDrawDocument* _pDoc, SdrLayer* pLayer, + const OUString& rOldLayerName, const OUString& rOldLayerTitle, const OUString& rOldLayerDesc, bool bOldIsVisible, bool bOldIsLocked, bool bOldIsPrintable, + const OUString& rNewLayerName, const OUString& rNewLayerTitle, const OUString& rNewLayerDesc, bool bNewIsVisible, bool bNewIsLocked, bool bNewIsPrintable ); + + virtual void Undo() override; + virtual void Redo() override; + +private: + SdrLayer* mpLayer; + OUString maOldLayerName; + OUString maOldLayerTitle; + OUString maOldLayerDesc; + bool mbOldIsVisible; + bool mbOldIsLocked; + bool mbOldIsPrintable; + OUString maNewLayerName; + OUString maNewLayerTitle; + OUString maNewLayerDesc; + bool mbNewIsVisible; + bool mbNewIsLocked; + bool mbNewIsPrintable; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/undopage.hxx b/sd/source/ui/inc/undopage.hxx new file mode 100644 index 000000000..87d5b5b21 --- /dev/null +++ b/sd/source/ui/inc/undopage.hxx @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +class SdDrawDocument; +class SdPage; + +/************************************************************************/ + +class SdPageFormatUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + + Size maOldSize; + sal_Int32 mnOldLeft; + sal_Int32 mnOldRight; + sal_Int32 mnOldUpper; + sal_Int32 mnOldLower; + Orientation meOldOrientation; + sal_uInt16 mnOldPaperBin; + bool mbOldFullSize; + + Size maNewSize; + sal_Int32 mnNewLeft; + sal_Int32 mnNewRight; + sal_Int32 mnNewUpper; + sal_Int32 mnNewLower; + bool mbNewScale; + Orientation meNewOrientation; + sal_uInt16 mnNewPaperBin; + bool mbNewFullSize; + +public: + SdPageFormatUndoAction( SdDrawDocument* pDoc, + SdPage* pThePage, + const Size& rOldSz, + sal_Int32 nOldLft, + sal_Int32 nOldRgt, + sal_Int32 nOldUpr, + sal_Int32 nOldLwr, + Orientation eOldOrient, + sal_uInt16 nOPaperBin, + bool bOFullSize, + + const Size& rNewSz, + sal_Int32 nNewLft, + sal_Int32 nNewRgt, + sal_Int32 nNewUpr, + sal_Int32 nNewLwr, + bool bNewScl, + Orientation eNewOrient, + sal_uInt16 nNPaperBin, + bool bNFullSize + ) : + SdUndoAction(pDoc), + mpPage (pThePage), + maOldSize (rOldSz), + mnOldLeft (nOldLft), + mnOldRight (nOldRgt), + mnOldUpper (nOldUpr), + mnOldLower (nOldLwr), + meOldOrientation(eOldOrient), + mnOldPaperBin (nOPaperBin), + mbOldFullSize (bOFullSize), + + maNewSize (rNewSz), + mnNewLeft (nNewLft), + mnNewRight (nNewRgt), + mnNewUpper (nNewUpr), + mnNewLower (nNewLwr), + mbNewScale (bNewScl), + meNewOrientation(eNewOrient), + mnNewPaperBin (nNPaperBin), + mbNewFullSize (bNFullSize) + + {} + virtual ~SdPageFormatUndoAction() override; + + virtual void Undo() override; + virtual void Redo() override; +}; + +/************************************************************************/ + +class SdPageLRUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + + sal_Int32 mnOldLeft; + sal_Int32 mnOldRight; + sal_Int32 mnNewLeft; + sal_Int32 mnNewRight; + +public: + SdPageLRUndoAction( SdDrawDocument* pDoc, SdPage* pThePage, + sal_Int32 nOldLft, sal_Int32 nOldRgt, + sal_Int32 nNewLft, sal_Int32 nNewRgt ) : + SdUndoAction(pDoc), + mpPage (pThePage), + mnOldLeft (nOldLft), + mnOldRight (nOldRgt), + mnNewLeft (nNewLft), + mnNewRight (nNewRgt) + {} + virtual ~SdPageLRUndoAction() override; + + virtual void Undo() override; + virtual void Redo() override; +}; + +/************************************************************************/ + +class SdPageULUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + + sal_Int32 mnOldUpper; + sal_Int32 mnOldLower; + sal_Int32 mnNewUpper; + sal_Int32 mnNewLower; + +public: + SdPageULUndoAction( SdDrawDocument* pDoc, SdPage* pThePage, + sal_Int32 nOldUpr, sal_Int32 nOldLwr, + sal_Int32 nNewUpr, sal_Int32 nNewLwr ) : + SdUndoAction(pDoc), + mpPage (pThePage), + mnOldUpper (nOldUpr), + mnOldLower (nOldLwr), + mnNewUpper (nNewUpr), + mnNewLower (nNewLwr) + {} + virtual ~SdPageULUndoAction() override; + + virtual void Undo() override; + virtual void Redo() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unmodpg.hxx b/sd/source/ui/inc/unmodpg.hxx new file mode 100644 index 000000000..9248642bf --- /dev/null +++ b/sd/source/ui/inc/unmodpg.hxx @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +class SdDrawDocument; +class SdPage; + +class ModifyPageUndoAction final : public SdUndoAction +{ + SdPage* mpPage; + OUString maOldName; + OUString maNewName; + AutoLayout meOldAutoLayout; + AutoLayout meNewAutoLayout; + bool mbOldBckgrndVisible; + bool mbNewBckgrndVisible; + bool mbOldBckgrndObjsVisible; + bool mbNewBckgrndObjsVisible; + +public: + ModifyPageUndoAction( + SdDrawDocument* pTheDoc, + SdPage* pThePage, + const OUString& aTheNewName, + AutoLayout eTheNewAutoLayout, + bool bTheNewBckgrndVisible, + bool bTheNewBckgrndObjsVisible); + + virtual ~ModifyPageUndoAction() override; + virtual void Undo() override; + virtual void Redo() override; +}; + +class RenameLayoutTemplateUndoAction final : public SdUndoAction +{ +public: + RenameLayoutTemplateUndoAction( + SdDrawDocument* pDocument, + const OUString& rOldLayoutName, + const OUString& rNewLayoutName); + + virtual void Undo() override; + virtual void Redo() override; + + virtual OUString GetComment() const override; + +private: + OUString maOldName; + OUString maNewName; + const OUString maComment; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unmovss.hxx b/sd/source/ui/inc/unmovss.hxx new file mode 100644 index 000000000..93e87cd40 --- /dev/null +++ b/sd/source/ui/inc/unmovss.hxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class SdDrawDocument; + +class SdMoveStyleSheetsUndoAction final : public SdUndoAction +{ + StyleSheetCopyResultVector maStyles; + std::vector< SdStyleSheetVector > maListOfChildLists; + bool mbMySheets; + +public: + SdMoveStyleSheetsUndoAction(SdDrawDocument* pTheDoc, StyleSheetCopyResultVector& rTheStyles, bool bInserted); + + virtual ~SdMoveStyleSheetsUndoAction() override; + virtual void Undo() override; + virtual void Redo() override; + + virtual OUString GetComment() const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unoaprms.hxx b/sd/source/ui/inc/unoaprms.hxx new file mode 100644 index 000000000..9ad327668 --- /dev/null +++ b/sd/source/ui/inc/unoaprms.hxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +class SdDrawDocument; +class SdrObject; + +class SdAnimationPrmsUndoAction final : public SdUndoAction +{ + SdrObject* pObject; + bool bOldActive; + bool bNewActive; + bool bOldDimPrevious; + bool bNewDimPrevious; + bool bOldDimHide; + bool bNewDimHide; + bool bOldSoundOn; + bool bNewSoundOn; + bool bOldSecondSoundOn; + bool bNewSecondSoundOn; + bool bOldPlayFull; + bool bNewPlayFull; + bool bOldSecondPlayFull; + bool bNewSecondPlayFull; + css::presentation::AnimationEffect eOldEffect; + css::presentation::AnimationEffect eNewEffect; + css::presentation::AnimationEffect eOldTextEffect; + css::presentation::AnimationEffect eNewTextEffect; + css::presentation::AnimationSpeed eOldSpeed; + css::presentation::AnimationSpeed eNewSpeed; + css::presentation::AnimationEffect eOldSecondEffect; + css::presentation::AnimationEffect eNewSecondEffect; + css::presentation::AnimationSpeed eOldSecondSpeed; + css::presentation::AnimationSpeed eNewSecondSpeed; + Color aOldDimColor; + Color aNewDimColor; + OUString aOldSoundFile; + OUString aNewSoundFile; + css::presentation::ClickAction eOldClickAction; + css::presentation::ClickAction eNewClickAction; + OUString aOldBookmark; + OUString aNewBookmark; + sal_uInt16 nOldVerb; + sal_uInt16 nNewVerb; + + bool bInfoCreated; + +public: + SdAnimationPrmsUndoAction(SdDrawDocument* pTheDoc, SdrObject* pObj, + bool bCreated) + : SdUndoAction(pTheDoc) + , pObject(pObj) + , bOldActive(false) + , bNewActive(false) + , bOldDimPrevious(false) + , bNewDimPrevious(false) + , bOldDimHide(false) + , bNewDimHide(false) + , bOldSoundOn(false) + , bNewSoundOn(false) + , bOldSecondSoundOn(false) + , bNewSecondSoundOn(false) + , bOldPlayFull(false) + , bNewPlayFull(false) + , bOldSecondPlayFull(false) + , bNewSecondPlayFull(false) + , eOldEffect(css::presentation::AnimationEffect_NONE) + , eNewEffect(css::presentation::AnimationEffect_NONE) + , eOldTextEffect(css::presentation::AnimationEffect_NONE) + , eNewTextEffect(css::presentation::AnimationEffect_NONE) + , eOldSpeed(css::presentation::AnimationSpeed_SLOW) + , eNewSpeed(css::presentation::AnimationSpeed_SLOW) + , eOldSecondEffect(css::presentation::AnimationEffect_NONE) + , eNewSecondEffect(css::presentation::AnimationEffect_NONE) + , eOldSecondSpeed(css::presentation::AnimationSpeed_SLOW) + , eNewSecondSpeed(css::presentation::AnimationSpeed_SLOW) + , eOldClickAction(css::presentation::ClickAction_NONE) + , eNewClickAction(css::presentation::ClickAction_NONE) + , nOldVerb(0) + , nNewVerb(0) + , bInfoCreated(bCreated) + { + } + + void SetActive(bool bTheOldActive, bool bTheNewActive) + { bOldActive = bTheOldActive; bNewActive = bTheNewActive; } + void SetEffect(css::presentation::AnimationEffect eTheOldEffect, css::presentation::AnimationEffect eTheNewEffect) + { eOldEffect = eTheOldEffect; eNewEffect = eTheNewEffect; } + void SetTextEffect(css::presentation::AnimationEffect eTheOldEffect, css::presentation::AnimationEffect eTheNewEffect) + { eOldTextEffect = eTheOldEffect; eNewTextEffect = eTheNewEffect; } + void SetSpeed(css::presentation::AnimationSpeed eTheOldSpeed, css::presentation::AnimationSpeed eTheNewSpeed) + { eOldSpeed = eTheOldSpeed; eNewSpeed = eTheNewSpeed; } + void SetDim(bool bTheOldDim, bool bTheNewDim) + { bOldDimPrevious = bTheOldDim; bNewDimPrevious = bTheNewDim; } + void SetDimColor(Color aTheOldDimColor, Color aTheNewDimColor) + { aOldDimColor = aTheOldDimColor; aNewDimColor = aTheNewDimColor; } + void SetDimHide(bool bTheOldDimHide, bool bTheNewDimHide) + { bOldDimHide = bTheOldDimHide; bNewDimHide = bTheNewDimHide; } + void SetSoundOn(bool bTheOldSoundOn, bool bTheNewSoundOn) + { bOldSoundOn = bTheOldSoundOn; bNewSoundOn = bTheNewSoundOn; } + void SetSound(const OUString& aTheOldSound, const OUString& aTheNewSound) + { aOldSoundFile = aTheOldSound; aNewSoundFile = aTheNewSound; } + void SetPlayFull(bool bTheOldPlayFull, bool bTheNewPlayFull) + { bOldPlayFull = bTheOldPlayFull; bNewPlayFull = bTheNewPlayFull; } + void SetClickAction(css::presentation::ClickAction eTheOldAction, css::presentation::ClickAction eTheNewAction) + { eOldClickAction = eTheOldAction; eNewClickAction = eTheNewAction; } + void SetBookmark(const OUString& aTheOldBookmark, const OUString& aTheNewBookmark) + { aOldBookmark = aTheOldBookmark; aNewBookmark = aTheNewBookmark; } + void SetVerb(sal_uInt16 nTheOldVerb, sal_uInt16 nTheNewVerb) + { nOldVerb = nTheOldVerb; nNewVerb = nTheNewVerb; } + void SetSecondEffect(css::presentation::AnimationEffect eTheOldEffect, css::presentation::AnimationEffect eTheNewEffect) + { eOldSecondEffect = eTheOldEffect; eNewSecondEffect = eTheNewEffect; } + void SetSecondSpeed(css::presentation::AnimationSpeed eTheOldSpeed, css::presentation::AnimationSpeed eTheNewSpeed) + { eOldSecondSpeed = eTheOldSpeed; eNewSecondSpeed = eTheNewSpeed; } + void SetSecondSoundOn(bool bTheOldSoundOn, bool bTheNewSoundOn) + { bOldSecondSoundOn = bTheOldSoundOn; bNewSecondSoundOn = bTheNewSoundOn; } + void SetSecondPlayFull(bool bTheOldPlayFull, bool bTheNewPlayFull) + { bOldSecondPlayFull = bTheOldPlayFull; bNewSecondPlayFull = bTheNewPlayFull; } + + virtual ~SdAnimationPrmsUndoAction() override; + virtual void Undo() override; + virtual void Redo() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unokywds.hxx b/sd/source/ui/inc/unokywds.hxx new file mode 100644 index 000000000..37a03d3d6 --- /dev/null +++ b/sd/source/ui/inc/unokywds.hxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +// SdUnoPseudoStyleFamily +inline constexpr OUStringLiteral sUNO_PseudoSheet_Background = u"background"; + +// SdLayer +inline constexpr OUStringLiteral sUNO_LayerName_background = u"background"; +inline constexpr OUStringLiteral sUNO_LayerName_background_objects = u"backgroundobjects"; +inline constexpr OUStringLiteral sUNO_LayerName_layout = u"layout"; +inline constexpr OUStringLiteral sUNO_LayerName_controls = u"controls"; +inline constexpr OUStringLiteral sUNO_LayerName_measurelines = u"measurelines"; + +// services +inline constexpr OUStringLiteral sUNO_Service_FillProperties + = u"com.sun.star.drawing.FillProperties"; +inline constexpr OUStringLiteral sUNO_Service_PageBackground + = u"com.sun.star.drawing.PageBackground"; +inline constexpr OUStringLiteral sUNO_Service_ImageMapRectangleObject + = u"com.sun.star.image.ImageMapRectangleObject"; +inline constexpr OUStringLiteral sUNO_Service_ImageMapCircleObject + = u"com.sun.star.image.ImageMapCircleObject"; +inline constexpr OUStringLiteral sUNO_Service_ImageMapPolygonObject + = u"com.sun.star.image.ImageMapPolygonObject"; + +// properties +inline constexpr OUStringLiteral sUNO_Prop_ForbiddenCharacters = u"ForbiddenCharacters"; +inline constexpr OUStringLiteral sUNO_Prop_MapUnit = u"MapUnit"; +inline constexpr OUStringLiteral sUNO_Prop_VisibleArea = u"VisibleArea"; +inline constexpr OUStringLiteral sUNO_Prop_TabStop = u"TabStop"; +inline constexpr OUStringLiteral sUNO_Prop_CharLocale = u"CharLocale"; +inline constexpr OUStringLiteral sUNO_Prop_AutomContFocus = u"AutomaticControlFocus"; +inline constexpr OUStringLiteral sUNO_Prop_ApplyFrmDsgnMode = u"ApplyFormDesignMode"; +inline constexpr OUStringLiteral sUNO_Prop_IsBackgroundVisible = u"IsBackgroundVisible"; +inline constexpr OUStringLiteral sUNO_Prop_IsBackgroundObjectsVisible + = u"IsBackgroundObjectsVisible"; +inline constexpr OUStringLiteral sUNO_Prop_UserDefinedAttributes = u"UserDefinedAttributes"; +inline constexpr OUStringLiteral sUNO_Prop_BookmarkURL = u"BookmarkURL"; +inline constexpr OUStringLiteral sUNO_Prop_RuntimeUID = u"RuntimeUID"; +inline constexpr OUStringLiteral sUNO_Prop_HasValidSignatures = u"HasValidSignatures"; +inline constexpr OUStringLiteral sUNO_Prop_InteropGrabBag = u"InteropGrabBag"; +inline constexpr OUStringLiteral sUNO_Prop_Theme = u"Theme"; + +// view settings +inline constexpr OUStringLiteral sUNO_View_ViewId = u"ViewId"; +inline constexpr OUStringLiteral sUNO_View_SnapLinesDrawing = u"SnapLinesDrawing"; +inline constexpr OUStringLiteral sUNO_View_SnapLinesNotes = u"SnapLinesNotes"; +inline constexpr OUStringLiteral sUNO_View_SnapLinesHandout = u"SnapLinesHandout"; +inline constexpr OUStringLiteral sUNO_View_RulerIsVisible = u"RulerIsVisible"; +inline constexpr OUStringLiteral sUNO_View_PageKind = u"PageKind"; +inline constexpr OUStringLiteral sUNO_View_SelectedPage = u"SelectedPage"; +inline constexpr OUStringLiteral sUNO_View_IsLayerMode = u"IsLayerMode"; +inline constexpr OUStringLiteral sUNO_View_IsDoubleClickTextEdit = u"IsDoubleClickTextEdit"; +inline constexpr OUStringLiteral sUNO_View_IsClickChangeRotation = u"IsClickChangeRotation"; +inline constexpr OUStringLiteral sUNO_View_SlidesPerRow = u"SlidesPerRow"; +inline constexpr OUStringLiteral sUNO_View_EditMode = u"EditMode"; +inline const char sUNO_View_EditModeStandard[] = "EditModeStandard"; // To be deprecated +// inline const char sUNO_View_EditModeNotes[] = "EditModeNotes"; +// inline const char sUNO_View_EditModeHandout[] = "EditModeHandout"; + +inline constexpr OUStringLiteral sUNO_View_GridIsVisible = u"GridIsVisible"; +inline constexpr OUStringLiteral sUNO_View_GridIsFront = u"GridIsFront"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToGrid = u"IsSnapToGrid"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToPageMargins = u"IsSnapToPageMargins"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToSnapLines = u"IsSnapToSnapLines"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToObjectFrame = u"IsSnapToObjectFrame"; +inline constexpr OUStringLiteral sUNO_View_IsSnapToObjectPoints = u"IsSnapToObjectPoints"; +inline constexpr OUStringLiteral sUNO_View_IsPlusHandlesAlwaysVisible + = u"IsPlusHandlesAlwaysVisible"; +inline constexpr OUStringLiteral sUNO_View_IsFrameDragSingles = u"IsFrameDragSingles"; +inline constexpr OUStringLiteral sUNO_View_EliminatePolyPointLimitAngle + = u"EliminatePolyPointLimitAngle"; +inline constexpr OUStringLiteral sUNO_View_IsEliminatePolyPoints = u"IsEliminatePolyPoints"; +inline const char sUNO_View_ActiveLayer[] = "ActiveLayer"; +inline constexpr OUStringLiteral sUNO_View_NoAttribs = u"NoAttribs"; +inline constexpr OUStringLiteral sUNO_View_NoColors = u"NoColors"; +inline constexpr OUStringLiteral sUNO_View_GridCoarseWidth = u"GridCoarseWidth"; +inline constexpr OUStringLiteral sUNO_View_GridCoarseHeight = u"GridCoarseHeight"; +inline constexpr OUStringLiteral sUNO_View_GridFineWidth = u"GridFineWidth"; +inline constexpr OUStringLiteral sUNO_View_GridFineHeight = u"GridFineHeight"; +inline constexpr OUStringLiteral sUNO_View_IsAngleSnapEnabled = u"IsAngleSnapEnabled"; +inline constexpr OUStringLiteral sUNO_View_SnapAngle = u"SnapAngle"; +inline constexpr OUStringLiteral sUNO_View_GridSnapWidthXNumerator = u"GridSnapWidthXNumerator"; +inline constexpr OUStringLiteral sUNO_View_GridSnapWidthXDenominator = u"GridSnapWidthXDenominator"; +inline constexpr OUStringLiteral sUNO_View_GridSnapWidthYNumerator = u"GridSnapWidthYNumerator"; +inline constexpr OUStringLiteral sUNO_View_GridSnapWidthYDenominator = u"GridSnapWidthYDenominator"; +inline constexpr OUStringLiteral sUNO_View_VisibleLayers = u"VisibleLayers"; +inline constexpr OUStringLiteral sUNO_View_PrintableLayers = u"PrintableLayers"; +inline constexpr OUStringLiteral sUNO_View_LockedLayers = u"LockedLayers"; + +inline constexpr OUStringLiteral sUNO_View_VisibleAreaTop = u"VisibleAreaTop"; +inline constexpr OUStringLiteral sUNO_View_VisibleAreaLeft = u"VisibleAreaLeft"; +inline constexpr OUStringLiteral sUNO_View_VisibleAreaWidth = u"VisibleAreaWidth"; +inline constexpr OUStringLiteral sUNO_View_VisibleAreaHeight = u"VisibleAreaHeight"; + +inline constexpr OUStringLiteral sUNO_View_ZoomOnPage = u"ZoomOnPage"; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unomodel.hxx b/sd/source/ui/inc/unomodel.hxx new file mode 100644 index 000000000..cf88666f6 --- /dev/null +++ b/sd/source/ui/inc/unomodel.hxx @@ -0,0 +1,406 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include + +#include +#include +#include +#include + +namespace com::sun::star::container { class XNameContainer; } +namespace com::sun::star::i18n { class XForbiddenCharacters; } +namespace com::sun::star::presentation { class XPresentation; } + +class SdDrawDocument; +class SdPage; +class SvxItemPropertySet; + +namespace sd { +class DrawDocShell; +class DrawViewShell; +} + +extern OUString getPageApiName( SdPage const * pPage ); +extern OUString getPageApiNameFromUiName( const OUString& rUIName ); + +class SD_DLLPUBLIC SdXImpressDocument final : public SfxBaseModel, // implements SfxListener, OWEAKOBJECT & other + public SvxFmMSFactory, + public css::drawing::XDrawPageDuplicator, + public css::drawing::XLayerSupplier, + public css::drawing::XMasterPagesSupplier, + public css::drawing::XDrawPagesSupplier, + public css::presentation::XPresentationSupplier, + public css::presentation::XCustomPresentationSupplier, + public css::document::XLinkTargetSupplier, + public css::beans::XPropertySet, + public css::style::XStyleFamiliesSupplier, + public css::lang::XServiceInfo, + public css::ucb::XAnyCompareFactory, + public css::presentation::XHandoutMasterSupplier, + public css::view::XRenderable, + public vcl::ITiledRenderable +{ + friend class SdDrawPagesAccess; + friend class SdMasterPagesAccess; + friend class SdLayerManager; + +private: + ::sd::DrawDocShell* mpDocShell; + SdDrawDocument* mpDoc; + bool mbDisposed; + + css::uno::Reference create( + OUString const & aServiceSpecifier, OUString const & referer); + + /// @throws css::uno::RuntimeException + SdPage* InsertSdPage( sal_uInt16 nPage, bool bDuplicate ); + + const bool mbImpressDoc; + bool mbClipBoard; + + css::uno::WeakReference< css::drawing::XDrawPages > mxDrawPagesAccess; + css::uno::WeakReference< css::drawing::XDrawPages > mxMasterPagesAccess; + css::uno::WeakReference< css::container::XNameAccess > mxLayerManager; + css::uno::WeakReference< css::container::XNameContainer > mxCustomPresentationAccess; + css::uno::WeakReference< css::i18n::XForbiddenCharacters > mxForbiddenCharacters; + css::uno::Reference< css::container::XNameAccess > mxLinks; + + css::uno::Reference< css::uno::XInterface > mxDashTable; + css::uno::Reference< css::uno::XInterface > mxGradientTable; + css::uno::Reference< css::uno::XInterface > mxHatchTable; + css::uno::Reference< css::uno::XInterface > mxBitmapTable; + css::uno::Reference< css::uno::XInterface > mxTransGradientTable; + css::uno::Reference< css::uno::XInterface > mxMarkerTable; + css::uno::Reference< css::uno::XInterface > mxDrawingPool; + + const SvxItemPropertySet* mpPropSet; + + css::uno::Sequence< css::uno::Type > maTypeSequence; + + OUString maBuildId; + + void initializeDocument(); + + sd::DrawViewShell* GetViewShell(); + + /** abstract SdrModel provider */ + virtual SdrModel& getSdrModelFromUnoModel() const override; + +public: + SdXImpressDocument(::sd::DrawDocShell* pShell, bool bClipBoard); + SdXImpressDocument(SdDrawDocument* pDoc, bool bClipBoard); + virtual ~SdXImpressDocument() noexcept override; + + static rtl::Reference< SdXImpressDocument > GetModel( SdDrawDocument const & rDoc ); + + // intern + bool operator==( const SdXImpressDocument& rModel ) const { return mpDoc == rModel.mpDoc; } + bool operator!=( const SdXImpressDocument& rModel ) const { return mpDoc != rModel.mpDoc; } + + ::sd::DrawDocShell* GetDocShell() const { return mpDocShell; } + SdDrawDocument* GetDoc() const { return mpDoc; } + bool IsImpressDocument() const { return mbImpressDoc; } + + void SetModified() noexcept; + + css::uno::Reference< css::i18n::XForbiddenCharacters > getForbiddenCharsTable(); + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + UNO3_GETIMPLEMENTATION_DECL(SdXImpressDocument) + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XModel + virtual void SAL_CALL lockControllers( ) override; + virtual void SAL_CALL unlockControllers( ) override; + virtual sal_Bool SAL_CALL hasControllersLocked( ) override; + virtual css::uno::Reference < css::container::XIndexAccess > SAL_CALL getViewData() override; + virtual void SAL_CALL setViewData( const css::uno::Reference < css::container::XIndexAccess >& aData ) override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XDrawPageDuplicator + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL duplicate( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) override; + + // XDrawPagesSupplier + virtual css::uno::Reference< css::drawing::XDrawPages > SAL_CALL getDrawPages( ) override; + + // XMasterPagesSupplier + virtual css::uno::Reference< css::drawing::XDrawPages > SAL_CALL getMasterPages( ) override; + + // XLayerManagerSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getLayerManager( ) override; + + // XCustomPresentationSupplier + virtual css::uno::Reference< css::container::XNameContainer > SAL_CALL getCustomPresentations( ) override; + + // XHandoutMasterSupplier + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getHandoutMasterPage( ) override; + + // XPresentationSupplier + virtual css::uno::Reference< css::presentation::XPresentation > SAL_CALL getPresentation( ) override; + + // XMultiServiceFactory ( SvxFmMSFactory ) + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( const OUString& aServiceSpecifier ) override; + virtual css::uno::Reference SAL_CALL + createInstanceWithArguments( + OUString const & ServiceSpecifier, + css::uno::Sequence const & Arguments) override; + virtual css::uno::Sequence< OUString > SAL_CALL getAvailableServiceNames( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XLinkTargetSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getLinks( ) override; + + // XStyleFamiliesSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getStyleFamilies( ) override; + + // XAnyCompareFactory + virtual css::uno::Reference< css::ucb::XAnyCompare > SAL_CALL createAnyCompareByName( const OUString& PropertyName ) override; + + // XRenderable + virtual sal_Int32 SAL_CALL getRendererCount( const css::uno::Any& aSelection, const css::uno::Sequence< css::beans::PropertyValue >& xOptions ) override; + virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getRenderer( sal_Int32 nRenderer, const css::uno::Any& aSelection, const css::uno::Sequence< css::beans::PropertyValue >& xOptions ) override; + virtual void SAL_CALL render( sal_Int32 nRenderer, const css::uno::Any& aSelection, const css::uno::Sequence< css::beans::PropertyValue >& xOptions ) override; + + // ITiledRenderable + virtual void paintTile( VirtualDevice& rDevice, + int nOutputWidth, + int nOutputHeight, + int nTilePosX, + int nTilePosY, + tools::Long nTileWidth, + tools::Long nTileHeight ) override; + virtual Size getDocumentSize() override; + virtual void setPart( int nPart, bool bAllowChangeFocus = true ) override; + virtual int getPart() override; + virtual int getParts() override; + virtual OUString getPartName( int nPart ) override; + virtual OUString getPartHash( int nPart ) override; + virtual VclPtr getDocWindow() override; + bool isMasterViewMode(); + + virtual void setPartMode( int nPartMode ) override; + + /// @see vcl::ITiledRenderable::initializeForTiledRendering(). + virtual void initializeForTiledRendering(const css::uno::Sequence& rArguments) override; + /// @see vcl::ITiledRenderable::postKeyEvent(). + virtual void postKeyEvent(int nType, int nCharCode, int nKeyCode) override; + /// @see vcl::ITiledRenderable::postMouseEvent(). + virtual void postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier) override; + /// @see vcl::ITiledRenderable::setTextSelection(). + virtual void setTextSelection(int nType, int nX, int nY) override; + /// @see vcl::ITiledRenderable::getSelection(). + virtual css::uno::Reference getSelection() override; + /// @see vcl::ITiledRenderable::setGraphicSelection(). + virtual void setGraphicSelection(int nType, int nX, int nY) override; + /// @see lok::Document::resetSelection(). + virtual void resetSelection() override; + /// @see vcl::ITiledRenderable::setClientVisibleArea(). + virtual void setClientVisibleArea(const tools::Rectangle& rRectangle) override; + /// @see vcl::ITiledRenderable::setClipboard(). + virtual void setClipboard(const css::uno::Reference& xClipboard) override; + /// @see vcl::ITiledRenderable::isMimeTypeSupported(). + virtual bool isMimeTypeSupported() override; + /// @see vcl::ITiledRenderable::getPointer(). + virtual PointerStyle getPointer() override; + /// @see vcl::ITiledRenderable::getPostIts(). + virtual void getPostIts(tools::JsonWriter& /*rJsonWriter*/) override; + /// @see vcl::ITiledRenderable::selectPart(). + virtual void selectPart(int nPart, int nSelect) override; + /// @see vcl::ITiledRenderable::moveSelectedParts(). + virtual void moveSelectedParts(int nPosition, bool bDuplicate) override; + /// @see vcl::ITiledRenderable::getPartInfo(). + virtual OUString getPartInfo(int nPart) override; + /// @see vcl::ITiledRenderable::isDisposed(). + virtual bool isDisposed() const override + { + return mbDisposed; + } + + // XComponent + + /** This dispose implementation releases the resources held by the + called object and forwards the call to its base class. + When close() has not yet been called then this is done first. As a + consequence the implementation has to cope with being called twice + and still has to forward the second call to the base class. + See also comments of issue 27847. + */ + virtual void SAL_CALL dispose() override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdDrawPagesAccess final : public ::cppu::WeakImplHelper< css::drawing::XDrawPages, css::container::XNameAccess, css::lang::XServiceInfo, css::lang::XComponent > +{ +private: + SdXImpressDocument* mpModel; + +public: + SdDrawPagesAccess( SdXImpressDocument& rMyModel ) noexcept; + virtual ~SdDrawPagesAccess() noexcept override; + + // XDrawPages + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL insertNewByIndex( sal_Int32 nIndex ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdMasterPagesAccess final : public ::cppu::WeakImplHelper< css::drawing::XDrawPages, css::lang::XServiceInfo, css::lang::XComponent > +{ +private: + SdXImpressDocument* mpModel; + +public: + SdMasterPagesAccess( SdXImpressDocument& rMyModel ) noexcept; + virtual ~SdMasterPagesAccess() noexcept override; + + // XDrawPages + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL insertNewByIndex( sal_Int32 nIndex ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XDrawPage >& xPage ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdDocLinkTargets final : public ::cppu::WeakImplHelper< css::container::XNameAccess, + css::lang::XServiceInfo , css::lang::XComponent > +{ +private: + SdXImpressDocument* mpModel; + +public: + SdDocLinkTargets( SdXImpressDocument& rMyModel ) noexcept; + virtual ~SdDocLinkTargets() noexcept override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + // intern + /// @throws std::exception + SdPage* FindPage( std::u16string_view rName ) const; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unopage.hxx b/sd/source/ui/inc/unopage.hxx new file mode 100644 index 000000000..af09e5982 --- /dev/null +++ b/sd/source/ui/inc/unopage.hxx @@ -0,0 +1,304 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "unosrch.hxx" +#include + +class SdrObject; +class SdXImpressDocument; + +class SdGenericDrawPage : public SvxFmDrawPage, + public SdUnoSearchReplaceShape, + public css::drawing::XShapeCombiner, + public css::drawing::XShapeBinder, + public css::container::XNamed, + public css::beans::XPropertySet, + public css::beans::XMultiPropertySet, + public css::animations::XAnimationNodeSupplier, + public css::office::XAnnotationAccess, + public css::document::XLinkTargetSupplier +{ +private: + SdXImpressDocument* mpDocModel; + SdrModel* mpSdrModel; + bool mbIsImpressDocument; + sal_Int16 mnTempPageNumber; // for printing handouts + + void UpdateModel(); + +protected: + friend class SdXImpressDocument; + + const SvxItemPropertySet* mpPropSet; + + /// @throws css::lang::IllegalArgumentException + virtual void setBackground( const css::uno::Any& rValue ); + /// @throws std::exception + virtual void getBackground( css::uno::Any& rValue ); + + OUString getBookmarkURL() const; + void setBookmarkURL( std::u16string_view rURL ); + + void SetLeftBorder( sal_Int32 nValue ); + void SetRightBorder( sal_Int32 nValue ); + void SetUpperBorder( sal_Int32 nValue ); + void SetLowerBorder( sal_Int32 nValue ); + + void SetWidth( sal_Int32 nWidth ); + void SetHeight( sal_Int32 nHeight ); + + bool IsImpressDocument() const; + + virtual void disposing() noexcept override; + + css::uno::Any getNavigationOrder(); + void setNavigationOrder( const css::uno::Any& rValue ); + + /// @throws css::uno::RuntimeException + void throwIfDisposed() const; + +public: + SdGenericDrawPage(SdXImpressDocument* pModel, SdPage* pInPage, const SvxItemPropertySet* pSet); + virtual ~SdGenericDrawPage() noexcept override; + + // intern + bool isValid() const { return (SvxDrawPage::mpPage != nullptr) && (mpModel != nullptr); } + + SdPage* GetPage() const { return static_cast(SvxDrawPage::mpPage); } + SdXImpressDocument* GetModel() const; + + static const css::uno::Sequence< sal_Int8 > & getUnoTunnelId() noexcept; + virtual sal_Int64 SAL_CALL getSomething( const css::uno::Sequence< sal_Int8 >& aIdentifier ) override; + + // this is called whenever a SdrObject must be created for an empty api shape wrapper + virtual SdrObject *CreateSdrObject_( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + + // SvxFmDrawPage + virtual css::uno::Reference CreateShape(SdrObject *pObj) const override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL release() noexcept override; + + // XShapeCombiner + virtual css::uno::Reference< css::drawing::XShape > SAL_CALL combine( const css::uno::Reference< css::drawing::XShapes >& xShapes ) override; + virtual void SAL_CALL split( const css::uno::Reference< css::drawing::XShape >& xGroup ) override; + + // XShapeBinder + virtual css::uno::Reference< css::drawing::XShape > SAL_CALL bind( const css::uno::Reference< css::drawing::XShapes >& xShapes ) override; + virtual void SAL_CALL unbind( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XMultiPropertySet + virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override; + virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + + // XLinkTargetSupplier + virtual css::uno::Reference< css::container::XNameAccess > SAL_CALL getLinks( ) override; + + // XServiceInfo + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XAnimationNodeSupplier + virtual css::uno::Reference< css::animations::XAnimationNode > SAL_CALL getAnimationNode( ) override; + + // XAnnotationAccess: + virtual css::uno::Reference< css::office::XAnnotation > SAL_CALL createAndInsertAnnotation() override; + virtual void SAL_CALL removeAnnotation(const css::uno::Reference< css::office::XAnnotation > & annotation) override; + virtual css::uno::Reference< css::office::XAnnotationEnumeration > SAL_CALL createAnnotationEnumeration() override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdDrawPage final : public css::drawing::XMasterPageTarget, + public css::presentation::XPresentationPage, + public SdGenericDrawPage +{ +private: + css::uno::Sequence< css::uno::Type > maTypeSequence; + + virtual void setBackground( const css::uno::Any& rValue ) override; + virtual void getBackground( css::uno::Any& rValue ) override; +public: + SdDrawPage(SdXImpressDocument* pModel, SdPage* pInPage); + virtual ~SdDrawPage() noexcept override; + + UNO3_GETIMPLEMENTATION_DECL( SdDrawPage ) + + static OUString getPageApiName( SdPage const * pPage ); + static OUString getPageApiNameFromUiName( const OUString& rUIName ); + static OUString getUiNameFromPageApiName( const OUString& rApiName ); + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XMasterPageTarget + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getMasterPage( ) override; + virtual void SAL_CALL setMasterPage( const css::uno::Reference< css::drawing::XDrawPage >& xMasterPage ) override; + + // XPresentationPage + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getNotesPage( ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XShapes + virtual void SAL_CALL add( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XShape >& xShape ) override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdMasterPage final : public css::presentation::XPresentationPage, + public SdGenericDrawPage +{ +private: + css::uno::Sequence< css::uno::Type > maTypeSequence; + + virtual void setBackground( const css::uno::Any& rValue ) override; + virtual void getBackground( css::uno::Any& rValue ) override; + +public: + SdMasterPage(SdXImpressDocument* pModel, SdPage* pInPage); + virtual ~SdMasterPage() noexcept override; + + UNO3_GETIMPLEMENTATION_DECL(SdMasterPage) + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XPresentationPage + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getNotesPage( ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + + // XShapes + virtual void SAL_CALL add( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XShape >& xShape ) override; +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdPageLinkTargets final : public ::cppu::WeakImplHelper< css::container::XNameAccess, + css::lang::XServiceInfo > +{ +private: + css::uno::Reference< css::drawing::XDrawPage > mxPage; + SdGenericDrawPage* mpUnoPage; + +public: + SdPageLinkTargets( SdGenericDrawPage* pUnoPage ) noexcept; + virtual ~SdPageLinkTargets() noexcept override; + + // intern + SdrObject* FindObject( std::u16string_view rName ) const noexcept; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; +}; + +OUString getUiNameFromPageApiNameImpl( const OUString& rApiName ); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unoprnms.hxx b/sd/source/ui/inc/unoprnms.hxx new file mode 100644 index 000000000..1d3a90552 --- /dev/null +++ b/sd/source/ui/inc/unoprnms.hxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + + +#define UNO_NAME_PAGE_BACKGROUND "Background" +#define UNO_NAME_PAGE_LEFT "BorderLeft" +#define UNO_NAME_PAGE_RIGHT "BorderRight" +#define UNO_NAME_PAGE_TOP "BorderTop" +#define UNO_NAME_PAGE_BOTTOM "BorderBottom" +#define UNO_NAME_PAGE_CHANGE "Change" +#define UNO_NAME_PAGE_DURATION "Duration" +#define UNO_NAME_PAGE_EFFECT "Effect" +#define UNO_NAME_PAGE_HEIGHT "Height" +#define UNO_NAME_PAGE_LAYOUT "Layout" +#define UNO_NAME_PAGE_NUMBER "Number" +#define UNO_NAME_PAGE_ORIENTATION "Orientation" +#define UNO_NAME_PAGE_SPEED "Speed" +#define UNO_NAME_PAGE_TRANSITION_DURATION "TransitionDuration" +#define UNO_NAME_PAGE_WIDTH "Width" +#define UNO_NAME_PAGE_PREVIEW "Preview" +#define UNO_NAME_PAGE_PREVIEWBITMAP "PreviewBitmap" +#define UNO_NAME_PAGE_PREVIEWMETAFILE "PreviewMetafile" +#define UNO_NAME_PAGE_VISIBLE "Visible" + +#define UNO_NAME_OBJ_BOOKMARK "Bookmark" +#define UNO_NAME_OBJ_DIMCOLOR "DimColor" +#define UNO_NAME_OBJ_DIMHIDE "DimHide" +#define UNO_NAME_OBJ_DIMPREV "DimPrevious" +#define UNO_NAME_OBJ_EFFECT "Effect" +#define UNO_NAME_OBJ_ISEMPTYPRESOBJ "IsEmptyPresentationObject" +#define UNO_NAME_OBJ_ISPRESOBJ "IsPresentationObject" +#define UNO_NAME_OBJ_CLICKACTION "OnClick" +#define UNO_NAME_OBJ_PLAYFULL "PlayFull" +#define UNO_NAME_OBJ_PRESORDER "PresentationOrder" +#define UNO_NAME_OBJ_SOUNDFILE "Sound" +#define UNO_NAME_OBJ_SOUNDON "SoundOn" +#define UNO_NAME_OBJ_SPEED "Speed" +#define UNO_NAME_OBJ_TEXTEFFECT "TextEffect" +#define UNO_NAME_OBJ_BLUESCREEN "TransparentColor" +#define UNO_NAME_OBJ_VERB "Verb" +#define UNO_NAME_OBJ_STYLE "Style" +#define UNO_NAME_OBJ_MASTERDEPENDENT "IsPlaceholderDependent" +#define UNO_NAME_OBJ_ANIMATIONPATH "AnimationPath" +#define UNO_NAME_OBJ_LEGACYFRAGMENT "LegacyFragment" + +#define UNO_NAME_LAYER_LOCKED "IsLocked" +#define UNO_NAME_LAYER_PRINTABLE "IsPrintable" +#define UNO_NAME_LAYER_VISIBLE "IsVisible" +#define UNO_NAME_LAYER_NAME "Name" + + +#define UNO_NAME_SEARCH_BACKWARDS "SearchBackwards" +#define UNO_NAME_SEARCH_CASE "SearchCaseSensitive" +#define UNO_NAME_SEARCH_WORDS "SearchWords" + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unosrch.hxx b/sd/source/ui/inc/unosrch.hxx new file mode 100644 index 000000000..6dcf681cb --- /dev/null +++ b/sd/source/ui/inc/unosrch.hxx @@ -0,0 +1,134 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +namespace com::sun::star::drawing { class XDrawPage; } +namespace com::sun::star::drawing { class XShape; } +namespace com::sun::star::text { class XTextRange; } +namespace com::sun::star::util { class XSearchDescriptor; } + +class SvxItemPropertySet; +class SdUnoSearchReplaceDescriptor; + +/** this class implements a search or replace operation on a given page or a given sdrobj */ +class SdUnoSearchReplaceShape : public css::util::XReplaceable +{ +protected: + css::drawing::XDrawPage* mpPage; + + css::uno::Reference< css::text::XTextRange > Search( const css::uno::Reference< css::text::XTextRange >& xText, SdUnoSearchReplaceDescriptor* pDescr ); + bool Search( const OUString& rText, sal_Int32& nStartPos, sal_Int32& nEndPos, SdUnoSearchReplaceDescriptor* pDescr ) noexcept; + static ESelection GetSelection( const css::uno::Reference< css::text::XTextRange >& xTextRange ) noexcept; + static css::uno::Reference< css::drawing::XShape > GetShape( const css::uno::Reference< css::text::XTextRange >& xTextRange ) noexcept; + css::uno::Reference< css::drawing::XShape > GetNextShape( const css::uno::Reference< css::container::XIndexAccess >& xShapes, const css::uno::Reference< css::drawing::XShape >& xCurrentShape ) noexcept; + css::uno::Reference< css::drawing::XShape > GetCurrentShape() const noexcept; + +public: + // danger, this c'tor is only usable if the given shape or page is derived + // from this class!!! + SdUnoSearchReplaceShape( css::drawing::XDrawPage* xPage ) noexcept; + virtual ~SdUnoSearchReplaceShape() noexcept; + + // XReplaceable + virtual css::uno::Reference< css::util::XReplaceDescriptor > SAL_CALL createReplaceDescriptor( ) override; + virtual sal_Int32 SAL_CALL replaceAll( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) override; + + // XSearchable + virtual css::uno::Reference< css::util::XSearchDescriptor > SAL_CALL createSearchDescriptor( ) override; + virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL findAll( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL findFirst( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL findNext( const css::uno::Reference< css::uno::XInterface >& xStartAt, const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) override; +}; + +/* ================================================================= */ + +/** this class holds the parameters and status of a search or replace operation performed + by class SdUnoSearchReplaceShape */ + +class SdUnoSearchReplaceDescriptor final : public ::cppu::WeakImplHelper< css::lang::XUnoTunnel, css::util::XReplaceDescriptor > // public css::util::XSearchDescriptor, css::beans::XPropertySet +{ + std::unique_ptr mpPropSet; + + bool mbBackwards; + bool mbCaseSensitive; + bool mbWords; + + OUString maSearchStr; + OUString maReplaceStr; + +public: + /// @throws css::uno::RuntimeException + SdUnoSearchReplaceDescriptor(); + virtual ~SdUnoSearchReplaceDescriptor() noexcept override; + + bool IsCaseSensitive() const { return mbCaseSensitive; } + bool IsWords() const { return mbWords; } + + UNO3_GETIMPLEMENTATION_DECL( SdUnoSearchReplaceDescriptor ) + + // XSearchDescriptor + virtual OUString SAL_CALL getSearchString( ) override; + virtual void SAL_CALL setSearchString( const OUString& aString ) override; + + // XReplaceDescriptor + virtual OUString SAL_CALL getReplaceString( ) override; + virtual void SAL_CALL setReplaceString( const OUString& aReplaceString ) override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; +}; + +/* ================================================================= */ + +/** this class holds a sequence that is a result from a find all and + lets people access it through the XIndexAccess Interface. */ +class SdUnoFindAllAccess final : public ::cppu::WeakImplHelper< css::container::XIndexAccess > // public css::container::XElementAccess +{ + css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > maSequence; + +public: + SdUnoFindAllAccess( css::uno::Sequence< css::uno::Reference< css::uno::XInterface > > const & rSequence ) noexcept; + virtual ~SdUnoFindAllAccess() noexcept override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/unprlout.hxx b/sd/source/ui/inc/unprlout.hxx new file mode 100644 index 000000000..8d75204f1 --- /dev/null +++ b/sd/source/ui/inc/unprlout.hxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +class SdDrawDocument; +class SdPage; + +class SdPresentationLayoutUndoAction final : public SdUndoAction +{ + OUString aOldLayoutName; + OUString aNewLayoutName; + AutoLayout eOldAutoLayout; + AutoLayout eNewAutoLayout; + bool bSetAutoLayout; // sal_True: change AutoLayout + SdPage* pPage; + OUString aComment; + +public: + SdPresentationLayoutUndoAction(SdDrawDocument* pTheDoc, + const OUString& aTheOldLayoutName, + const OUString& aTheNewLayoutName, + AutoLayout eTheOldAutoLayout, + AutoLayout eTheNewAutoLayout, + bool bSet, + SdPage* pThePage); + + virtual ~SdPresentationLayoutUndoAction() override; + virtual void Undo() override; + virtual void Redo() override; + + virtual OUString GetComment() const override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/vectdlg.hxx b/sd/source/ui/inc/vectdlg.hxx new file mode 100644 index 000000000..ac7a1bfd4 --- /dev/null +++ b/sd/source/ui/inc/vectdlg.hxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd { +class DrawDocShell; +} + +/****************************************************************************** +|* +|* SdVectorizeDlg +|* +\******************************************************************************/ + +class SdVectorizeDlg final : public weld::GenericDialogController +{ + ::sd::DrawDocShell* m_pDocSh; + Bitmap aBmp; + Bitmap aPreviewBmp; + GDIMetaFile aMtf; + + GraphCtrl m_aBmpWin; + GraphCtrl m_aMtfWin; + + std::unique_ptr m_xNmLayers; + std::unique_ptr m_xMtReduce; + std::unique_ptr m_xFtFillHoles; + std::unique_ptr m_xMtFillHoles; + std::unique_ptr m_xCbFillHoles; + std::unique_ptr m_xBmpWin; + std::unique_ptr m_xMtfWin; + std::unique_ptr m_xPrgs; + std::unique_ptr m_xBtnOK; + std::unique_ptr m_xBtnPreview; + + void LoadSettings(); + void SaveSettings() const; + void InitPreviewBmp(); + + static ::tools::Rectangle GetRect( const Size& rDispSize, const Size& rBmpSize ); + Bitmap GetPreparedBitmap( Bitmap const & rBmp, Fraction& rScale ); + void Calculate( Bitmap const & rBmp, GDIMetaFile& rMtf ); + static void AddTile( BitmapReadAccess const * pRAcc, GDIMetaFile& rMtf, + tools::Long nPosX, tools::Long nPosY, tools::Long nWidth, tools::Long nHeight ); + + DECL_LINK( ProgressHdl, tools::Long, void ); + DECL_LINK( ClickPreviewHdl, weld::Button&, void ); + DECL_LINK( ClickOKHdl, weld::Button&, void ); + DECL_LINK( ToggleHdl, weld::Toggleable&, void ); + DECL_LINK( ModifyHdl, weld::SpinButton&, void ); + DECL_LINK( MetricModifyHdl, weld::MetricSpinButton&, void ); + +public: + + SdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell); + virtual ~SdVectorizeDlg() override; + + const GDIMetaFile& GetGDIMetaFile() const { return aMtf; } +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/view/viewoverlaymanager.hxx b/sd/source/ui/inc/view/viewoverlaymanager.hxx new file mode 100644 index 000000000..3a5c98deb --- /dev/null +++ b/sd/source/ui/inc/view/viewoverlaymanager.hxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +#include + +namespace sd +{ +class SmartTag; +} +namespace sd::tools +{ +class EventMultiplexerEvent; +} +namespace sd +{ +class ViewShellBase; +} +struct ImplSVEvent; + +namespace sd +{ +typedef std::vector> ViewTagVector; + +class ViewOverlayManager final : public SfxListener +{ +public: + ViewOverlayManager(ViewShellBase& rViewShellBase); + virtual ~ViewOverlayManager() override; + + void onZoomChanged(); + void UpdateTags(); + + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void); + DECL_LINK(UpdateTagsHdl, void*, void); + + bool CreateTags(); + bool DisposeTags(); + + virtual void Notify(SfxBroadcaster& rBC, const SfxHint& rHint) override; + +private: + ViewShellBase& mrBase; + ImplSVEvent* mnUpdateTagsEvent; + + ViewTagVector maTagVector; +}; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/inc/zoomlist.hxx b/sd/source/ui/inc/zoomlist.hxx new file mode 100644 index 000000000..b7f7da79d --- /dev/null +++ b/sd/source/ui/inc/zoomlist.hxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +namespace sd +{ +class ViewShell; + +class ZoomList +{ +public: + ZoomList(ViewShell* pViewShell); + + void InsertZoomRect(const ::tools::Rectangle& rRect); + ::tools::Rectangle const& GetNextZoomRect(); + ::tools::Rectangle const& GetPreviousZoomRect(); + bool IsNextPossible() const; + bool IsPreviousPossible() const; + +private: + ViewShell* mpViewShell; + sal_uInt32 mnCurPos; + + std::vector<::tools::Rectangle> maRectangles; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/CanvasUpdateRequester.cxx b/sd/source/ui/presenter/CanvasUpdateRequester.cxx new file mode 100644 index 000000000..2271ba781 --- /dev/null +++ b/sd/source/ui/presenter/CanvasUpdateRequester.cxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "CanvasUpdateRequester.hxx" +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::presenter { + +//===== CanvasUpdateRequester::Deleter ======================================== + +class CanvasUpdateRequester::Deleter +{ +public: + void operator() (CanvasUpdateRequester* pObject) { delete pObject; } +}; + +//===== CanvasUpdateRequester ================================================= + +std::shared_ptr CanvasUpdateRequester::Instance ( + const Reference& rxSharedCanvas) +{ + // this global must not own anything or we crash on shutdown + static std::vector, + std::weak_ptr>> s_RequesterMap; + for (auto it = s_RequesterMap.begin(); it != s_RequesterMap.end(); ) + { + uno::Reference const xCanvas(it->first); + if (!xCanvas.is()) + { + it = s_RequesterMap.erase(it); // remove stale entry + } + else if (xCanvas == rxSharedCanvas) + { + std::shared_ptr pRequester(it->second); + if (pRequester) + { + return pRequester; + } + else + { + std::shared_ptr const pNew( + new CanvasUpdateRequester(rxSharedCanvas), Deleter()); + it->second = pNew; + return pNew; + } + } + else + { + ++it; + } + } + + // No requester for the given canvas found. Create a new one. + std::shared_ptr pRequester ( + new CanvasUpdateRequester(rxSharedCanvas), Deleter()); + s_RequesterMap.emplace_back(rxSharedCanvas, pRequester); + return pRequester; +} + + +CanvasUpdateRequester::CanvasUpdateRequester ( + const Reference& rxCanvas) + : mxCanvas(rxCanvas) + , m_pUserEventId(nullptr) + , mbUpdateFlag(false) +{ + Reference xComponent (mxCanvas, UNO_QUERY); + if (xComponent.is()) + { + //xComponent->addEventListener(this); + } +} + +CanvasUpdateRequester::~CanvasUpdateRequester() +{ + assert(m_pUserEventId == nullptr); +} + +void CanvasUpdateRequester::RequestUpdate (const bool bUpdateAll) +{ + if (m_pUserEventId == nullptr) + { + m_pThis = shared_from_this(); // keep instance alive until dispatch + mbUpdateFlag = bUpdateAll; + m_pUserEventId = Application::PostUserEvent(LINK(this, CanvasUpdateRequester, Callback)); + } + else + { + mbUpdateFlag |= bUpdateAll; + } +} + +IMPL_LINK_NOARG(CanvasUpdateRequester, Callback, void*, void) +{ + m_pUserEventId = nullptr; + if (mxCanvas.is()) + { + mxCanvas->updateScreen(mbUpdateFlag); + mbUpdateFlag = false; + } + assert(m_pThis); + m_pThis.reset(); // possibly delete "this" +} + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/CanvasUpdateRequester.hxx b/sd/source/ui/presenter/CanvasUpdateRequester.hxx new file mode 100644 index 000000000..ebb582ead --- /dev/null +++ b/sd/source/ui/presenter/CanvasUpdateRequester.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::rendering +{ +class XSpriteCanvas; +} + +struct ImplSVEvent; + +namespace sd::presenter +{ +/** Each UpdateRequester handles update requests (calls to + XCanvas::updateScreen()) for one shared canvas (a canvas that has one or + more PresenterCanvas wrappers). Multiple calls are collected and lead + to a single call to updateScreen. +*/ +class CanvasUpdateRequester : public std::enable_shared_from_this +{ +public: + CanvasUpdateRequester(const CanvasUpdateRequester&) = delete; + CanvasUpdateRequester& operator=(const CanvasUpdateRequester&) = delete; + + /** @return the Canvas UpdateRequester object for the given shared canvas. + A new object is created when it does not already exist. + */ + static std::shared_ptr + Instance(const css::uno::Reference& rxCanvas); + + void RequestUpdate(const bool bUpdateAll); + +private: + explicit CanvasUpdateRequester( + const css::uno::Reference& rxCanvas); + ~CanvasUpdateRequester(); + class Deleter; + friend class Deleter; + + /// keep instance alive waiting for event dispatch + std::shared_ptr m_pThis; + css::uno::Reference mxCanvas; + ImplSVEvent* m_pUserEventId; + bool mbUpdateFlag; + + DECL_LINK(Callback, void*, void); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterCanvas.cxx b/sd/source/ui/presenter/PresenterCanvas.cxx new file mode 100644 index 000000000..f586969bc --- /dev/null +++ b/sd/source/ui/presenter/PresenterCanvas.cxx @@ -0,0 +1,790 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PresenterCanvas.hxx" +#include "CanvasUpdateRequester.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::presenter { + +//===== PresenterCustomSprite ================================================= + +/** Wrapper around a sprite that is displayed on a PresenterCanvas. +*/ +namespace { + typedef comphelper::WeakComponentImplHelper < + css::rendering::XCustomSprite + > PresenterCustomSpriteInterfaceBase; + +class PresenterCustomSprite final + : public PresenterCustomSpriteInterfaceBase +{ +public: + PresenterCustomSprite ( + const rtl::Reference& rpCanvas, + const Reference& rxSprite, + const Reference& rxBaseWindow); + PresenterCustomSprite(const PresenterCustomSprite&) = delete; + PresenterCustomSprite& operator=(const PresenterCustomSprite&) = delete; + virtual void disposing(std::unique_lock&) override; + + // XSprite + + virtual void SAL_CALL setAlpha (double nAlpha) override; + + virtual void SAL_CALL move (const geometry::RealPoint2D& rNewPos, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState) override; + + virtual void SAL_CALL transform (const geometry::AffineMatrix2D& rTransformation) override; + + virtual void SAL_CALL clip (const Reference& rClip) override; + + virtual void SAL_CALL setPriority (double nPriority) override; + + virtual void SAL_CALL show() override; + + virtual void SAL_CALL hide() override; + + // XCustomSprite + + virtual Reference SAL_CALL getContentCanvas() override; + +private: + rtl::Reference mpCanvas; + Reference mxSprite; + Reference mxBaseWindow; + geometry::RealPoint2D maPosition; + + /// @throws css::lang::DisposedException + void ThrowIfDisposed(); +}; + +} + +//===== PresenterCanvas ======================================================= + +PresenterCanvas::PresenterCanvas ( + const Reference& rxUpdateCanvas, + const Reference& rxUpdateWindow, + const Reference& rxSharedCanvas, + const Reference& rxSharedWindow, + const Reference& rxWindow) + : mxUpdateCanvas(rxUpdateCanvas), + mxUpdateWindow(rxUpdateWindow), + mxSharedCanvas(rxSharedCanvas), + mxSharedWindow(rxSharedWindow), + mxWindow(rxWindow), + mbOffsetUpdatePending(true) +{ + if (mxWindow.is()) + mxWindow->addWindowListener(this); + + if (mxUpdateCanvas.is()) + { + m_pUpdateRequester = CanvasUpdateRequester::Instance(mxUpdateCanvas); + } +} + +PresenterCanvas::~PresenterCanvas() +{ +} + +void PresenterCanvas::disposing(std::unique_lock&) +{ + if (mxWindow.is()) + { + mxWindow->removeWindowListener(this); + mxWindow.clear(); + } +} + +//----- XCanvas --------------------------------------------------------------- + +void SAL_CALL PresenterCanvas::clear() +{ + ThrowIfDisposed(); + // ToDo: Clear the area covered by the child window. A simple forward + // would clear the whole shared canvas. +} + +void SAL_CALL PresenterCanvas::drawPoint ( + const css::geometry::RealPoint2D& aPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + mxSharedCanvas->drawPoint(aPoint,MergeViewState(aViewState),aRenderState); +} + +void SAL_CALL PresenterCanvas::drawLine ( + const css::geometry::RealPoint2D& aStartPoint, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + mxSharedCanvas->drawLine(aStartPoint,aEndPoint,MergeViewState(aViewState),aRenderState); +} + +void SAL_CALL PresenterCanvas::drawBezier ( + const css::geometry::RealBezierSegment2D& aBezierSegment, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + mxSharedCanvas->drawBezier(aBezierSegment,aEndPoint,MergeViewState(aViewState),aRenderState); +} + +css::uno::Reference SAL_CALL PresenterCanvas::drawPolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference SAL_CALL PresenterCanvas::strokePolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::rendering::StrokeAttributes& aStrokeAttributes) +{ + ThrowIfDisposed(); + return mxSharedCanvas->strokePolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState, aStrokeAttributes); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::strokeTexturedPolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence< css::rendering::Texture >& aTextures, + const css::rendering::StrokeAttributes& aStrokeAttributes) +{ + ThrowIfDisposed(); + return mxSharedCanvas->strokeTexturedPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState, aTextures, aStrokeAttributes); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::strokeTextureMappedPolyPolygon( + const css::uno::Reference& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence& aTextures, + const css::uno::Reference& xMapping, + const css::rendering::StrokeAttributes& aStrokeAttributes) +{ + ThrowIfDisposed(); + return mxSharedCanvas->strokeTextureMappedPolyPolygon( + xPolyPolygon, + MergeViewState(aViewState), + aRenderState, + aTextures, + xMapping, + aStrokeAttributes); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::queryStrokeShapes( + const css::uno::Reference& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::rendering::StrokeAttributes& aStrokeAttributes) +{ + ThrowIfDisposed(); + return mxSharedCanvas->queryStrokeShapes( + xPolyPolygon, MergeViewState(aViewState), aRenderState, aStrokeAttributes); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::fillPolyPolygon( + const css::uno::Reference& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->fillPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::fillTexturedPolyPolygon( + const css::uno::Reference& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence& xTextures) +{ + ThrowIfDisposed(); + return mxSharedCanvas->fillTexturedPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState, xTextures); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::fillTextureMappedPolyPolygon( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence< css::rendering::Texture >& xTextures, + const css::uno::Reference< css::geometry::XMapping2D >& xMapping) +{ + ThrowIfDisposed(); + return mxSharedCanvas->fillTextureMappedPolyPolygon( + xPolyPolygon, MergeViewState(aViewState), aRenderState, xTextures, xMapping); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::createFont( + const css::rendering::FontRequest& aFontRequest, + const css::uno::Sequence< css::beans::PropertyValue >& aExtraFontProperties, + const css::geometry::Matrix2D& aFontMatrix) +{ + ThrowIfDisposed(); + return mxSharedCanvas->createFont( + aFontRequest, aExtraFontProperties, aFontMatrix); +} + +css::uno::Sequence SAL_CALL + PresenterCanvas::queryAvailableFonts( + const css::rendering::FontInfo& aFilter, + const css::uno::Sequence< css::beans::PropertyValue >& aFontProperties) +{ + ThrowIfDisposed(); + return mxSharedCanvas->queryAvailableFonts(aFilter, aFontProperties); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::drawText( + const css::rendering::StringContext& aText, + const css::uno::Reference< css::rendering::XCanvasFont >& xFont, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + ::sal_Int8 nTextDirection) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawText( + aText, xFont, MergeViewState(aViewState), aRenderState, nTextDirection); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::drawTextLayout( + const css::uno::Reference< css::rendering::XTextLayout >& xLayoutetText, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawTextLayout( + xLayoutetText, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::drawBitmap( + const css::uno::Reference< css::rendering::XBitmap >& xBitmap, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawBitmap( + xBitmap, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::drawBitmapModulated( + const css::uno::Reference< css::rendering::XBitmap>& xBitmap, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) +{ + ThrowIfDisposed(); + return mxSharedCanvas->drawBitmapModulated( + xBitmap, MergeViewState(aViewState), aRenderState); +} + +css::uno::Reference SAL_CALL + PresenterCanvas::getDevice() +{ + ThrowIfDisposed(); + return mxSharedCanvas->getDevice(); +} + +//----- XSpriteCanvas --------------------------------------------------------- + +Reference SAL_CALL + PresenterCanvas::createSpriteFromAnimation ( + const css::uno::Reference& rAnimation) +{ + ThrowIfDisposed(); + + Reference xSpriteCanvas (mxSharedCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + return xSpriteCanvas->createSpriteFromAnimation(rAnimation); + else + return nullptr; +} + +Reference SAL_CALL + PresenterCanvas::createSpriteFromBitmaps ( + const css::uno::Sequence< + css::uno::Reference< css::rendering::XBitmap > >& rAnimationBitmaps, + ::sal_Int8 nInterpolationMode) +{ + ThrowIfDisposed(); + + Reference xSpriteCanvas (mxSharedCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + return xSpriteCanvas->createSpriteFromBitmaps(rAnimationBitmaps, nInterpolationMode); + else + return nullptr; +} + +Reference SAL_CALL + PresenterCanvas::createCustomSprite ( + const css::geometry::RealSize2D& rSpriteSize) +{ + ThrowIfDisposed(); + + Reference xSpriteCanvas (mxSharedCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + return new PresenterCustomSprite( + this, + xSpriteCanvas->createCustomSprite(rSpriteSize), + mxSharedWindow); + else if (mxUpdateCanvas.is()) + return new PresenterCustomSprite( + this, + mxUpdateCanvas->createCustomSprite(rSpriteSize), + mxUpdateWindow); + else + return nullptr; +} + +Reference SAL_CALL + PresenterCanvas::createClonedSprite ( + const css::uno::Reference< css::rendering::XSprite >& rxOriginal) +{ + ThrowIfDisposed(); + + Reference xSpriteCanvas (mxSharedCanvas, UNO_QUERY); + if (xSpriteCanvas.is()) + return xSpriteCanvas->createClonedSprite(rxOriginal); + if (mxUpdateCanvas.is()) + return mxUpdateCanvas->createClonedSprite(rxOriginal); + return nullptr; +} + +sal_Bool SAL_CALL PresenterCanvas::updateScreen (sal_Bool bUpdateAll) +{ + ThrowIfDisposed(); + + mbOffsetUpdatePending = true; + if (m_pUpdateRequester != nullptr) + { + m_pUpdateRequester->RequestUpdate(bUpdateAll); + return true; + } + else + { + return false; + } +} + +//----- XEventListener -------------------------------------------------------- + +void SAL_CALL PresenterCanvas::disposing (const css::lang::EventObject& rEvent) +{ + ThrowIfDisposed(); + if (rEvent.Source == mxWindow) + mxWindow = nullptr; +} + +//----- XWindowListener ------------------------------------------------------- + +void SAL_CALL PresenterCanvas::windowResized (const css::awt::WindowEvent&) +{ + ThrowIfDisposed(); + mbOffsetUpdatePending = true; +} + +void SAL_CALL PresenterCanvas::windowMoved (const css::awt::WindowEvent&) +{ + ThrowIfDisposed(); + mbOffsetUpdatePending = true; +} + +void SAL_CALL PresenterCanvas::windowShown (const css::lang::EventObject&) +{ + ThrowIfDisposed(); + mbOffsetUpdatePending = true; +} + +void SAL_CALL PresenterCanvas::windowHidden (const css::lang::EventObject&) +{ + ThrowIfDisposed(); +} + +//----- XBitmap --------------------------------------------------------------- + +geometry::IntegerSize2D SAL_CALL PresenterCanvas::getSize() +{ + ThrowIfDisposed(); + + if (mxWindow.is()) + { + const awt::Rectangle aWindowBox (mxWindow->getPosSize()); + return geometry::IntegerSize2D(aWindowBox.Width, aWindowBox.Height); + } + else + return geometry::IntegerSize2D(0,0); +} + +sal_Bool SAL_CALL PresenterCanvas::hasAlpha() +{ + Reference xBitmap (mxSharedCanvas, UNO_QUERY); + if (xBitmap.is()) + return xBitmap->hasAlpha(); + else + return false; +} + +Reference SAL_CALL PresenterCanvas::getScaledBitmap( + const css::geometry::RealSize2D&, + sal_Bool) +{ + ThrowIfDisposed(); + + // Not implemented. + + return nullptr; +} + +rendering::ViewState PresenterCanvas::MergeViewState ( + const rendering::ViewState& rViewState) +{ + // Make sure the offset is up-to-date. + if (mbOffsetUpdatePending) + maOffset = GetOffset(mxSharedWindow); + return MergeViewState(rViewState, maOffset); +} + +css::rendering::ViewState PresenterCanvas::MergeViewState ( + const css::rendering::ViewState& rViewState, + const css::awt::Point& rOffset) +{ + // Early rejects. + if ( ! mxSharedCanvas.is()) + return rViewState; + + Reference xDevice (mxSharedCanvas->getDevice()); + if ( ! xDevice.is()) + return rViewState; + + // Create a modifiable copy of the given view state. + rendering::ViewState aViewState (rViewState); + + // Prepare the local clip rectangle. + ::basegfx::B2DRectangle aWindowRange (GetClipRectangle(aViewState.AffineTransform, rOffset)); + + // Adapt the offset of the view state. + aViewState.AffineTransform.m02 += rOffset.X; + aViewState.AffineTransform.m12 += rOffset.Y; + + // Adapt the clip polygon. + if ( ! aViewState.Clip.is()) + { + // Cancel out the later multiplication with the view state + // transformation. + aViewState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + xDevice, + ::basegfx::B2DPolyPolygon(::basegfx::utils::createPolygonFromRect(aWindowRange))); + } + else + { + // Have to compute the intersection of the given clipping polygon in + // the view state and the local clip rectangle. + + // Clip the view state clipping polygon against the local clip rectangle. + const ::basegfx::B2DPolyPolygon aClipPolygon ( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D( + aViewState.Clip)); + const ::basegfx::B2DPolyPolygon aClippedClipPolygon ( + ::basegfx::utils::clipPolyPolygonOnRange( + aClipPolygon, + aWindowRange, + true, /* bInside */ + false /* bStroke */)); + + aViewState.Clip = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + xDevice, + aClippedClipPolygon); + } + + return aViewState; +} + +awt::Point PresenterCanvas::GetOffset (const Reference& rxBaseWindow) +{ + mbOffsetUpdatePending = false; + if (mxWindow.is() && rxBaseWindow.is()) + { + VclPtr pWindow = VCLUnoHelper::GetWindow(mxWindow); + VclPtr pSharedWindow = VCLUnoHelper::GetWindow(rxBaseWindow); + if (pWindow && pSharedWindow) + { + ::tools::Rectangle aBox = pWindow->GetWindowExtentsRelative(pSharedWindow); + + // Calculate offset of this canvas with respect to the shared + // canvas. + return awt::Point(aBox.Left(), aBox.Top()); + } + } + + return awt::Point(0, 0); +} + +::basegfx::B2DRectangle PresenterCanvas::GetClipRectangle ( + const css::geometry::AffineMatrix2D& rViewTransform, + const awt::Point& rOffset) +{ + VclPtr pWindow = VCLUnoHelper::GetWindow(mxWindow); + if (!pWindow) + return ::basegfx::B2DRectangle(); + + VclPtr pSharedWindow = VCLUnoHelper::GetWindow(mxSharedWindow); + if (!pSharedWindow) + return ::basegfx::B2DRectangle(); + + // Get the bounding box of the window and create a range in the + // coordinate system of the child window. + // Use the window extents. + ::tools::Rectangle aLocalClip = pWindow->GetWindowExtentsRelative(pSharedWindow); + + // The local clip rectangle is used to clip the view state clipping + // polygon. + ::basegfx::B2DRectangle aWindowRectangle ( + aLocalClip.Left() - rOffset.X, + aLocalClip.Top() - rOffset.Y, + aLocalClip.Right() - rOffset.X + 1, + aLocalClip.Bottom() - rOffset.Y + 1); + + // Calculate the inverted view state transformation to cancel out a + // later transformation of the local clip polygon with the view state + // transformation. + ::basegfx::B2DHomMatrix aInvertedViewStateTransformation; + ::basegfx::unotools::homMatrixFromAffineMatrix( + aInvertedViewStateTransformation, + rViewTransform); + if (aInvertedViewStateTransformation.invert()) + { + // Cancel out the later multiplication with the view state + // transformation. + aWindowRectangle.transform(aInvertedViewStateTransformation); + } + + return aWindowRectangle; +} + +Reference PresenterCanvas::UpdateSpriteClip ( + const Reference& rxOriginalClip, + const geometry::RealPoint2D& rLocation) +{ + // Check used resources and just return the original clip when not + // every one of them is available. + if ( ! mxWindow.is()) + return rxOriginalClip; + + Reference xDevice (mxSharedCanvas->getDevice()); + if ( ! xDevice.is()) + return rxOriginalClip; + + // Determine the bounds of the clip rectangle (the window border) in the + // coordinate system of the sprite. + const awt::Rectangle aWindowBox (mxWindow->getPosSize()); + const double nMinX (-rLocation.X); + const double nMinY (-rLocation.Y); + const double nMaxX (aWindowBox.Width-rLocation.X); + const double nMaxY (aWindowBox.Height-rLocation.Y); + + // Create a clip polygon. + Reference xPolygon; + if (rxOriginalClip.is()) + { + // Combine the original clip with the window clip. + const ::basegfx::B2DPolyPolygon aOriginalClip ( + ::basegfx::unotools::b2DPolyPolygonFromXPolyPolygon2D(rxOriginalClip)); + ::basegfx::B2DRectangle aWindowRange (nMinX, nMinY, nMaxX, nMaxY); + const ::basegfx::B2DPolyPolygon aClippedClipPolygon ( + ::basegfx::utils::clipPolyPolygonOnRange( + aOriginalClip, + aWindowRange, + true, /* bInside */ + false /* bStroke */)); + xPolygon = ::basegfx::unotools::xPolyPolygonFromB2DPolyPolygon( + xDevice, + aClippedClipPolygon); + } + else + { + // Create a new clip polygon from the window clip rectangle. + Sequence > aPoints + { + { + { nMinX,nMinY }, + { nMaxX,nMinY }, + { nMaxX,nMaxY }, + { nMinX,nMaxY } + } + }; + Reference xLinePolygon( + xDevice->createCompatibleLinePolyPolygon(aPoints)); + if (xLinePolygon.is()) + xLinePolygon->setClosed(0, true); + xPolygon = xLinePolygon; + } + + return xPolygon; +} + +void PresenterCanvas::ThrowIfDisposed() +{ + if (m_bDisposed || ! mxSharedCanvas.is()) + { + throw lang::DisposedException ("PresenterCanvas object has already been disposed", + static_cast(this)); + } +} + +//===== PresenterCustomSprite ================================================= + +PresenterCustomSprite::PresenterCustomSprite ( + const rtl::Reference& rpCanvas, + const Reference& rxSprite, + const Reference& rxBaseWindow) + : mpCanvas(rpCanvas), + mxSprite(rxSprite), + mxBaseWindow(rxBaseWindow), + maPosition(0,0) +{ +} + +void PresenterCustomSprite::disposing(std::unique_lock&) +{ + Reference xComponent (mxSprite, UNO_QUERY); + mxSprite = nullptr; + if (xComponent.is()) + xComponent->dispose(); + mpCanvas.clear(); +} + +//----- XSprite --------------------------------------------------------------- + +void SAL_CALL PresenterCustomSprite::setAlpha (const double nAlpha) +{ + ThrowIfDisposed(); + mxSprite->setAlpha(nAlpha); +} + +void SAL_CALL PresenterCustomSprite::move ( + const geometry::RealPoint2D& rNewPos, + const rendering::ViewState& rViewState, + const rendering::RenderState& rRenderState) +{ + ThrowIfDisposed(); + maPosition = rNewPos; + mxSprite->move( + rNewPos, + mpCanvas->MergeViewState(rViewState, mpCanvas->GetOffset(mxBaseWindow)), + rRenderState); + // Clip sprite against window bounds. This call is necessary because + // sprite clipping is done in the coordinate system of the sprite. + // Therefore, after each change of the sprites location the window + // bounds have to be transformed into the sprites coordinate system. + clip(nullptr); +} + +void SAL_CALL PresenterCustomSprite::transform (const geometry::AffineMatrix2D& rTransformation) +{ + ThrowIfDisposed(); + mxSprite->transform(rTransformation); +} + +void SAL_CALL PresenterCustomSprite::clip (const Reference& rxClip) +{ + ThrowIfDisposed(); + // The clip region is expected in the coordinate system of the sprite. + // UpdateSpriteClip() integrates the window bounds, transformed into the + // sprites coordinate system, with the given clip. + mxSprite->clip(mpCanvas->UpdateSpriteClip(rxClip, maPosition)); +} + +void SAL_CALL PresenterCustomSprite::setPriority (const double nPriority) +{ + ThrowIfDisposed(); + mxSprite->setPriority(nPriority); +} + +void SAL_CALL PresenterCustomSprite::show() +{ + ThrowIfDisposed(); + mxSprite->show(); +} + +void SAL_CALL PresenterCustomSprite::hide() +{ + ThrowIfDisposed(); + mxSprite->hide(); +} + +//----- XCustomSprite --------------------------------------------------------- + +Reference PresenterCustomSprite::getContentCanvas() +{ + ThrowIfDisposed(); + return mxSprite->getContentCanvas(); +} + +void PresenterCustomSprite::ThrowIfDisposed() +{ + if (m_bDisposed || ! mxSprite.is()) + { + throw lang::DisposedException ("PresenterCustomSprite object has already been disposed", + static_cast(this)); + } +} + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterCanvas.hxx b/sd/source/ui/presenter/PresenterCanvas.hxx new file mode 100644 index 000000000..da2f51a79 --- /dev/null +++ b/sd/source/ui/presenter/PresenterCanvas.hxx @@ -0,0 +1,320 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace sd::presenter { class CanvasUpdateRequester; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::geometry { struct AffineMatrix2D; } + +namespace sd::presenter { + +typedef comphelper::WeakComponentImplHelper < + css::rendering::XSpriteCanvas, + css::rendering::XBitmap, + css::awt::XWindowListener +> PresenterCanvasInterfaceBase; + +/** Wrapper around a shared canvas that forwards most of its methods to the + shared canvas. Most notable differences are: + 1. The transformation of the ViewState of forwarded calls is modified by adding + an offset. + 2. The clip polygon of the ViewState of forwarded calls is intersected + with a clip rectangle that can be set via SetClip(). + 3. Calls to updateScreen() are collected. One call to the updateScreen() + method of the shared canvas is made asynchronously. + + The canvas can use different canvases for sharing and for sprite + construction. This allows the shared canvas to be a canvas of sprite itself. +*/ +class PresenterCanvas + : public PresenterCanvasInterfaceBase +{ +public: + /** This constructor is used when a PresenterCanvas object is created + directly, typically by the PresenterCanvasFactory. + @param rxUpdateCanvas + This canvas is used to call updateScreen() at and to create + sprites. In the typical case this canvas is identical to the + rxSharedCanvas argument. + @param rxUpdateWindow + The window that belongs to the canvas given by the + rxUpdateCanvas argument. + @param rxSharedCanvas + The canvas that is wrapped by the new instance of this class. + Typically this is a regular XSpriteCanvas and then is identical + to the one given by the rxUpdateCanvas argument. It may be the + canvas of a sprite which does not support the XSpriteCanvas + interface. In that case the canvas that created the sprite can + be given as rxUpdateCanvas argument to allow to create further + sprites and to have proper calls to updateScreen(). + @param rxSharedWindow + The window that belongs to the canvas given by the + rxSharedCanvas argument. + @param rxWindow + The window that is represented by the new PresenterCanvas + object. It is expected to be a direct descendant of + rxSharedWindow. Its position inside rxSharedWindow defines the + offset of the canvas implemented by the new PresenterCanvas + object and rxSharedCanvas. + */ + PresenterCanvas ( + const css::uno::Reference& rxUpdateCanvas, + const css::uno::Reference& rxUpdateWindow, + const css::uno::Reference& rxSharedCanvas, + const css::uno::Reference& rxSharedWindow, + const css::uno::Reference& rxWindow); + virtual ~PresenterCanvas() override; + PresenterCanvas(const PresenterCanvas&) = delete; + PresenterCanvas& operator=(const PresenterCanvas&) = delete; + + virtual void disposing(std::unique_lock&) override; + + css::awt::Point GetOffset (const css::uno::Reference& rxBaseWindow); + + /** Merge the given view state with the view state that translates the + (virtual) child canvas to the shared canvas. + */ + css::rendering::ViewState MergeViewState ( + const css::rendering::ViewState& rViewState, + const css::awt::Point& raOffset); + + /** Called by custom sprites to update their clip polygon so that they + are clipped at the borders of the canvas. This method has to be + called after each change of the sprite location so that the bounds + of the canvas can be transformed into the coordinate system of the + sprite. + */ + css::uno::Reference UpdateSpriteClip ( + const css::uno::Reference& rxOriginalClip, + const css::geometry::RealPoint2D& rLocation); + + // XCanvas + + virtual void SAL_CALL clear() override; + + virtual void SAL_CALL drawPoint ( + const css::geometry::RealPoint2D& aPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual void SAL_CALL drawLine ( + const css::geometry::RealPoint2D& aStartPoint, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual void SAL_CALL drawBezier ( + const css::geometry::RealBezierSegment2D& aBezierSegment, + const css::geometry::RealPoint2D& aEndPoint, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference SAL_CALL drawPolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference SAL_CALL strokePolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::rendering::StrokeAttributes& aStrokeAttributes) override; + + virtual css::uno::Reference SAL_CALL + strokeTexturedPolyPolygon ( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence< css::rendering::Texture >& aTextures, + const css::rendering::StrokeAttributes& aStrokeAttributes) override; + + virtual css::uno::Reference SAL_CALL + strokeTextureMappedPolyPolygon( + const css::uno::Reference& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence& aTextures, + const css::uno::Reference& xMapping, + const css::rendering::StrokeAttributes& aStrokeAttributes) override; + + virtual css::uno::Reference SAL_CALL + queryStrokeShapes( + const css::uno::Reference& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::rendering::StrokeAttributes& aStrokeAttributes) override; + + virtual css::uno::Reference SAL_CALL + fillPolyPolygon( + const css::uno::Reference& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference SAL_CALL + fillTexturedPolyPolygon( + const css::uno::Reference& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence& xTextures) override; + + virtual css::uno::Reference SAL_CALL + fillTextureMappedPolyPolygon( + const css::uno::Reference< css::rendering::XPolyPolygon2D >& xPolyPolygon, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + const css::uno::Sequence< css::rendering::Texture >& xTextures, + const css::uno::Reference< css::geometry::XMapping2D >& xMapping) override; + + virtual css::uno::Reference SAL_CALL + createFont( + const css::rendering::FontRequest& aFontRequest, + const css::uno::Sequence< css::beans::PropertyValue >& aExtraFontProperties, + const css::geometry::Matrix2D& aFontMatrix) override; + + virtual css::uno::Sequence SAL_CALL + queryAvailableFonts( + const css::rendering::FontInfo& aFilter, + const css::uno::Sequence< css::beans::PropertyValue >& aFontProperties) override; + + virtual css::uno::Reference SAL_CALL + drawText( + const css::rendering::StringContext& aText, + const css::uno::Reference< css::rendering::XCanvasFont >& xFont, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState, + ::sal_Int8 nTextDirection) override; + + virtual css::uno::Reference SAL_CALL + drawTextLayout( + const css::uno::Reference< css::rendering::XTextLayout >& xLayoutetText, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference SAL_CALL + drawBitmap( + const css::uno::Reference< css::rendering::XBitmap >& xBitmap, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference SAL_CALL + drawBitmapModulated( + const css::uno::Reference< css::rendering::XBitmap>& xBitmap, + const css::rendering::ViewState& aViewState, + const css::rendering::RenderState& aRenderState) override; + + virtual css::uno::Reference SAL_CALL + getDevice() override; + + // XSpriteCanvas + + css::uno::Reference< css::rendering::XAnimatedSprite > SAL_CALL + createSpriteFromAnimation ( + const css::uno::Reference< css::rendering::XAnimation >& animation) override; + + css::uno::Reference< css::rendering::XAnimatedSprite > SAL_CALL + createSpriteFromBitmaps ( + const css::uno::Sequence< + css::uno::Reference< css::rendering::XBitmap > >& animationBitmaps, + ::sal_Int8 interpolationMode) override; + + css::uno::Reference< css::rendering::XCustomSprite > SAL_CALL + createCustomSprite ( + const css::geometry::RealSize2D& spriteSize) override; + + css::uno::Reference< css::rendering::XSprite > SAL_CALL + createClonedSprite ( + const css::uno::Reference< css::rendering::XSprite >& original) override; + + sal_Bool SAL_CALL updateScreen (sal_Bool bUpdateAll) override; + + // XEventListener + + virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override; + + // XWindowListener + + virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override; + + virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override; + + // XBitmap + + virtual css::geometry::IntegerSize2D SAL_CALL getSize() override; + + virtual sal_Bool SAL_CALL hasAlpha() override; + + virtual css::uno::Reference SAL_CALL getScaledBitmap( + const css::geometry::RealSize2D& rNewSize, + sal_Bool bFast) override; + +private: + css::uno::Reference mxUpdateCanvas; + css::uno::Reference mxUpdateWindow; + css::uno::Reference mxSharedCanvas; + css::uno::Reference mxSharedWindow; + + /** The window for which a canvas is emulated. + */ + css::uno::Reference mxWindow; + + /** Offset of the emulated canvas with respect to the shared canvas. + */ + css::awt::Point maOffset; + + /** The UpdateRequester is used by updateScreen() to schedule + updateScreen() calls at the shared canvas. + */ + std::shared_ptr m_pUpdateRequester; + + /** When this flag is true (it is set to true after every call to + updateScreen()) then the next call to MergeViewState updates the + maOffset member. A possible optimization would set this flag only + to true when one of the windows between mxWindow and mxSharedWindow + changes its position. + */ + bool mbOffsetUpdatePending; + + ::basegfx::B2DRectangle GetClipRectangle ( + const css::geometry::AffineMatrix2D& rViewTransform, + const css::awt::Point& rOffset); + + css::rendering::ViewState MergeViewState (const css::rendering::ViewState& rViewState); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterHelper.cxx b/sd/source/ui/presenter/PresenterHelper.cxx new file mode 100644 index 000000000..a93113a75 --- /dev/null +++ b/sd/source/ui/presenter/PresenterHelper.cxx @@ -0,0 +1,466 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include "PresenterHelper.hxx" +#include "PresenterCanvas.hxx" +#include +#include +#include +#include +#include +#include + + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::presenter { + +//===== PresenterHelper ======================================================= + +PresenterHelper::PresenterHelper ( + const Reference& rxContext) + : mxComponentContext(rxContext) +{ +} + +PresenterHelper::~PresenterHelper() +{ +} + +//----- XInitialize ----------------------------------------------------------- + +void SAL_CALL PresenterHelper::initialize (const Sequence&) {} + +//----- XPaneHelper ---------------------------------------------------- + +Reference SAL_CALL PresenterHelper::createWindow ( + const Reference& rxParentWindow, + sal_Bool bCreateSystemChildWindow, + sal_Bool bInitiallyVisible, + sal_Bool bEnableChildTransparentMode, + sal_Bool bEnableParentClip) +{ + VclPtr pParentWindow(VCLUnoHelper::GetWindow(rxParentWindow)); + + // Create a new window. + VclPtr pWindow; + if (bCreateSystemChildWindow) + { + pWindow = VclPtr::Create(pParentWindow, WB_SYSTEMCHILDWINDOW); + } + else + { + pWindow = VclPtr::Create(pParentWindow); + } + Reference xWindow (pWindow->GetComponentInterface(), UNO_QUERY); + + if (bEnableChildTransparentMode) + { + // Make the frame window transparent and make the parent able to + // draw behind it. + if (pParentWindow) + pParentWindow->EnableChildTransparentMode(); + } + + pWindow->Show(bInitiallyVisible); + + pWindow->SetMapMode(MapMode(MapUnit::MapPixel)); + pWindow->SetBackground(); + if ( ! bEnableParentClip) + { + pWindow->SetParentClipMode(ParentClipMode::NoClip); + pWindow->SetPaintTransparent(true); + } + else + { + pWindow->SetParentClipMode(ParentClipMode::Clip); + pWindow->SetPaintTransparent(false); + } + + return xWindow; +} + +Reference SAL_CALL PresenterHelper::createSharedCanvas ( + const Reference& rxUpdateCanvas, + const Reference& rxUpdateWindow, + const Reference& rxSharedCanvas, + const Reference& rxSharedWindow, + const Reference& rxWindow) +{ + if ( ! rxSharedCanvas.is() + || ! rxSharedWindow.is() + || ! rxWindow.is()) + { + throw RuntimeException("illegal argument", static_cast(this)); + } + + if (rxWindow == rxSharedWindow) + return rxSharedCanvas; + else + return new PresenterCanvas( + rxUpdateCanvas, + rxUpdateWindow, + rxSharedCanvas, + rxSharedWindow, + rxWindow); +} + +Reference SAL_CALL PresenterHelper::createCanvas ( + const Reference& rxWindow, + sal_Int16, + const OUString& rsOptionalCanvasServiceName) +{ + // No shared window is given or an explicit canvas service name is + // specified. Create a new canvas. + VclPtr pWindow = VCLUnoHelper::GetWindow(rxWindow); + if (!pWindow) + throw RuntimeException(); + + Sequence aArg{ // common: first any is VCL pointer to window (for VCL canvas) + Any(reinterpret_cast(pWindow.get())), + Any(css::awt::Rectangle()), + Any(false), + Any(rxWindow) + }; + + Reference xFactory ( + mxComponentContext->getServiceManager(), UNO_QUERY_THROW); + return Reference( + xFactory->createInstanceWithArguments( + !rsOptionalCanvasServiceName.isEmpty() + ? rsOptionalCanvasServiceName + : OUString("com.sun.star.rendering.Canvas.VCL"), + aArg), + UNO_QUERY); +} + +void SAL_CALL PresenterHelper::toTop ( + const Reference& rxWindow) +{ + VclPtr pWindow = VCLUnoHelper::GetWindow(rxWindow); + if (pWindow) + { + pWindow->ToTop(); + pWindow->SetZOrder(nullptr, ZOrderFlags::Last); + } +} + +namespace { + +struct IdMapEntry { + char const * sid; + rtl::OUStringConstExpr bmpid; +}; + +} + +Reference SAL_CALL PresenterHelper::loadBitmap ( + const OUString& id, + const Reference& rxCanvas) +{ + if ( ! rxCanvas.is()) + return nullptr; + + static IdMapEntry const map[] = { + { "bitmaps/Background.png", BMP_PRESENTERSCREEN_BACKGROUND }, + { "bitmaps/Animation.png", + BMP_PRESENTERSCREEN_ANIMATION }, + { "bitmaps/Transition.png", + BMP_PRESENTERSCREEN_TRANSITION }, + { "bitmaps/BorderActiveBottom.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_BOTTOM }, + { "bitmaps/BorderActiveBottomCallout.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_BOTTOM_CALLOUT }, + { "bitmaps/BorderActiveBottomLeft.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_BOTTOM_LEFT }, + { "bitmaps/BorderActiveBottomRight.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_BOTTOM_RIGHT }, + { "bitmaps/BorderActiveLeft.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_LEFT }, + { "bitmaps/BorderActiveRight.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_RIGHT }, + { "bitmaps/BorderActiveTop.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_TOP }, + { "bitmaps/BorderActiveTopLeft.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_TOP_LEFT }, + { "bitmaps/BorderActiveTopRight.png", + BMP_PRESENTERSCREEN_BORDER_ACTIVE_TOP_RIGHT }, + { "bitmaps/BorderBottom.png", BMP_PRESENTERSCREEN_BORDER_BOTTOM }, + { "bitmaps/BorderBottomLeft.png", + BMP_PRESENTERSCREEN_BORDER_BOTTOM_LEFT }, + { "bitmaps/BorderBottomRight.png", + BMP_PRESENTERSCREEN_BORDER_BOTTOM_RIGHT }, + { "bitmaps/BorderCurrentSlideBottom.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_BOTTOM }, + { "bitmaps/BorderCurrentSlideBottomLeft.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_BOTTOM_LEFT }, + { "bitmaps/BorderCurrentSlideBottomRight.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_BOTTOM_RIGHT }, + { "bitmaps/BorderCurrentSlideLeft.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_LEFT }, + { "bitmaps/BorderCurrentSlideRight.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_RIGHT }, + { "bitmaps/BorderCurrentSlideTop.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_TOP }, + { "bitmaps/BorderCurrentSlideTopLeft.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_TOP_LEFT }, + { "bitmaps/BorderCurrentSlideTopRight.png", + BMP_PRESENTERSCREEN_BORDER_CURRENT_SLIDE_TOP_RIGHT }, + { "bitmaps/BorderLeft.png", BMP_PRESENTERSCREEN_BORDER_LEFT }, + { "bitmaps/BorderRight.png", BMP_PRESENTERSCREEN_BORDER_RIGHT }, + { "bitmaps/BorderToolbarBottom.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_BOTTOM }, + { "bitmaps/BorderToolbarLeft.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_LEFT }, + { "bitmaps/BorderToolbarRight.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_RIGHT }, + { "bitmaps/BorderToolbarTop.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_TOP }, + { "bitmaps/BorderToolbarTopLeft.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_TOP_LEFT }, + { "bitmaps/BorderToolbarTopRight.png", + BMP_PRESENTERSCREEN_BORDER_TOOLBAR_TOP_RIGHT }, + { "bitmaps/BorderTop.png", BMP_PRESENTERSCREEN_BORDER_TOP }, + { "bitmaps/BorderTopLeft.png", BMP_PRESENTERSCREEN_BORDER_TOP_LEFT }, + { "bitmaps/BorderTopRight.png", BMP_PRESENTERSCREEN_BORDER_TOP_RIGHT }, + { "bitmaps/ButtonEffectNextDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_EFFECT_NEXT_DISABLED }, + { "bitmaps/ButtonEffectNextMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_EFFECT_NEXT_MOUSE_OVER }, + { "bitmaps/ButtonEffectNextNormal.png", + BMP_PRESENTERSCREEN_BUTTON_EFFECT_NEXT_NORMAL }, + { "bitmaps/ButtonEffectNextSelected.png", + BMP_PRESENTERSCREEN_BUTTON_EFFECT_NEXT_SELECTED }, + { "bitmaps/ButtonFrameCenterMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_CENTER_MOUSE_OVER }, + { "bitmaps/ButtonFrameCenterNormal.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_CENTER_NORMAL }, + { "bitmaps/ButtonFrameLeftMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_LEFT_MOUSE_OVER }, + { "bitmaps/ButtonFrameLeftNormal.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_LEFT_NORMAL }, + { "bitmaps/ButtonFrameRightMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_RIGHT_MOUSE_OVER }, + { "bitmaps/ButtonFrameRightNormal.png", + BMP_PRESENTERSCREEN_BUTTON_FRAME_RIGHT_NORMAL }, + { "bitmaps/ButtonHelpDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_HELP_DISABLED }, + { "bitmaps/ButtonHelpMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_HELP_MOUSE_OVER }, + { "bitmaps/ButtonHelpNormal.png", + BMP_PRESENTERSCREEN_BUTTON_HELP_NORMAL }, + { "bitmaps/ButtonHelpSelected.png", + BMP_PRESENTERSCREEN_BUTTON_HELP_SELECTED }, + { "bitmaps/ButtonExitPresenterMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_EXIT_PRESENTER_MOUSE_OVER }, + { "bitmaps/ButtonExitPresenterNormal.png", + BMP_PRESENTERSCREEN_BUTTON_EXIT_PRESENTER_NORMAL }, + { "bitmaps/ButtonMinusDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_MINUS_DISABLED }, + { "bitmaps/ButtonMinusMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_MINUS_MOUSE_OVER }, + { "bitmaps/ButtonMinusNormal.png", + BMP_PRESENTERSCREEN_BUTTON_MINUS_NORMAL }, + { "bitmaps/ButtonMinusSelected.png", + BMP_PRESENTERSCREEN_BUTTON_MINUS_SELECTED }, + { "bitmaps/ButtonNotesDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_NOTES_DISABLED }, + { "bitmaps/ButtonNotesMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_NOTES_MOUSE_OVER }, + { "bitmaps/ButtonNotesNormal.png", + BMP_PRESENTERSCREEN_BUTTON_NOTES_NORMAL }, + { "bitmaps/ButtonNotesSelected.png", + BMP_PRESENTERSCREEN_BUTTON_NOTES_SELECTED }, + { "bitmaps/ButtonPlusDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_PLUS_DISABLED }, + { "bitmaps/ButtonPlusMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_PLUS_MOUSE_OVER }, + { "bitmaps/ButtonPlusNormal.png", + BMP_PRESENTERSCREEN_BUTTON_PLUS_NORMAL }, + { "bitmaps/ButtonPlusSelected.png", + BMP_PRESENTERSCREEN_BUTTON_PLUS_SELECTED }, + { "bitmaps/ButtonSlideNextDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_NEXT_DISABLED }, + { "bitmaps/ButtonSlideNextMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_NEXT_MOUSE_OVER }, + { "bitmaps/ButtonSlideNextNormal.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_NEXT_NORMAL }, + { "bitmaps/ButtonSlidePreviousDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_PREVIOUS_DISABLED }, + { "bitmaps/ButtonSlidePreviousMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_PREVIOUS_MOUSE_OVER }, + { "bitmaps/ButtonSlidePreviousNormal.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_PREVIOUS_NORMAL }, + { "bitmaps/ButtonSlidePreviousSelected.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_PREVIOUS_SELECTED }, + { "bitmaps/ButtonSlideSorterDisabled.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_SORTER_DISABLED }, + { "bitmaps/ButtonSlideSorterMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_SORTER_MOUSE_OVER }, + { "bitmaps/ButtonSlideSorterNormal.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_SORTER_NORMAL }, + { "bitmaps/ButtonSlideSorterSelected.png", + BMP_PRESENTERSCREEN_BUTTON_SLIDE_SORTER_SELECTED }, + { "bitmaps/ButtonSwitchMonitorMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_SWITCH_MONITOR_MOUSE_OVER }, + { "bitmaps/ButtonSwitchMonitorNormal.png", + BMP_PRESENTERSCREEN_BUTTON_SWITCH_MONITOR_NORMAL }, + { "bitmaps/ButtonRestartTimerMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_RESTART_TIMER_MOUSE_OVER }, + { "bitmaps/ButtonRestartTimerNormal.png", + BMP_PRESENTERSCREEN_BUTTON_RESTART_TIMER_NORMAL }, + { "bitmaps/ButtonPauseTimerMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_PAUSE_TIMER_MOUSE_OVER }, + { "bitmaps/ButtonPauseTimerNormal.png", + BMP_PRESENTERSCREEN_BUTTON_PAUSE_TIMER_NORMAL }, + { "bitmaps/ButtonResumeTimerMouseOver.png", + BMP_PRESENTERSCREEN_BUTTON_RESUME_TIMER_MOUSE_OVER }, + { "bitmaps/ButtonResumeTimerNormal.png", + BMP_PRESENTERSCREEN_BUTTON_RESUME_TIMER_NORMAL }, + { "bitmaps/LabelMouseOverCenter.png", + BMP_PRESENTERSCREEN_LABEL_MOUSE_OVER_CENTER }, + { "bitmaps/LabelMouseOverLeft.png", + BMP_PRESENTERSCREEN_LABEL_MOUSE_OVER_LEFT }, + { "bitmaps/LabelMouseOverRight.png", + BMP_PRESENTERSCREEN_LABEL_MOUSE_OVER_RIGHT }, + { "bitmaps/ScrollbarArrowDownDisabled.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_DOWN_DISABLED }, + { "bitmaps/ScrollbarArrowDownMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_DOWN_MOUSE_OVER }, + { "bitmaps/ScrollbarArrowDownNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_DOWN_NORMAL }, + { "bitmaps/ScrollbarArrowDownSelected.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_DOWN_SELECTED }, + { "bitmaps/ScrollbarArrowUpDisabled.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_UP_DISABLED }, + { "bitmaps/ScrollbarArrowUpMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_UP_MOUSE_OVER }, + { "bitmaps/ScrollbarArrowUpNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_UP_NORMAL }, + { "bitmaps/ScrollbarArrowUpSelected.png", + BMP_PRESENTERSCREEN_SCROLLBAR_ARROW_UP_SELECTED }, + { "bitmaps/ScrollbarPagerMiddleMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_PAGER_MIDDLE_MOUSE_OVER }, + { "bitmaps/ScrollbarPagerMiddleNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_PAGER_MIDDLE_NORMAL }, + { "bitmaps/ScrollbarThumbBottomMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_BOTTOM_MOUSE_OVER }, + { "bitmaps/ScrollbarThumbBottomNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_BOTTOM_NORMAL }, + { "bitmaps/ScrollbarThumbMiddleMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_MIDDLE_MOUSE_OVER }, + { "bitmaps/ScrollbarThumbMiddleNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_MIDDLE_NORMAL }, + { "bitmaps/ScrollbarThumbTopMouseOver.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_TOP_MOUSE_OVER }, + { "bitmaps/ScrollbarThumbTopNormal.png", + BMP_PRESENTERSCREEN_SCROLLBAR_THUMB_TOP_NORMAL }, + { "bitmaps/ViewBackground.png", BMP_PRESENTERSCREEN_VIEW_BACKGROUND }, + { "bitmaps/Separator.png", + BMP_PRESENTERSCREEN_SEPARATOR } + }; + OUString bmpid; + for (std::size_t i = 0; i != SAL_N_ELEMENTS(map); ++i) { + if (id.equalsAscii(map[i].sid)) { + bmpid = map[i].bmpid; + break; + } + } + if (bmpid.isEmpty()) { + return nullptr; + } + + ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex()); + + const cppcanvas::CanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createCanvas(rxCanvas)); + + if (pCanvas) + { + BitmapEx aBitmapEx(bmpid); + cppcanvas::BitmapSharedPtr xBitmap( + cppcanvas::VCLFactory::createBitmap(pCanvas, + aBitmapEx)); + if (!xBitmap) + return nullptr; + return xBitmap->getUNOBitmap(); + } + + return nullptr; +} + +void SAL_CALL PresenterHelper::captureMouse ( + const Reference& rxWindow) +{ + ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex()); + + // Capture the mouse (if not already done.) + VclPtr pWindow = VCLUnoHelper::GetWindow(rxWindow); + if (pWindow && ! pWindow->IsMouseCaptured()) + { + pWindow->CaptureMouse(); + } +} + +void SAL_CALL PresenterHelper::releaseMouse (const Reference& rxWindow) +{ + ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex()); + + // Release the mouse (if not already done.) + VclPtr pWindow = VCLUnoHelper::GetWindow(rxWindow); + if (pWindow && pWindow->IsMouseCaptured()) + { + pWindow->ReleaseMouse(); + } +} + +awt::Rectangle PresenterHelper::getWindowExtentsRelative ( + const Reference& rxChildWindow, + const Reference& rxParentWindow) +{ + VclPtr pChildWindow = VCLUnoHelper::GetWindow(rxChildWindow); + VclPtr pParentWindow = VCLUnoHelper::GetWindow(rxParentWindow); + if (pChildWindow && pParentWindow) + { + ::tools::Rectangle aBox (pChildWindow->GetWindowExtentsRelative(pParentWindow)); + return awt::Rectangle(aBox.Left(),aBox.Top(),aBox.GetWidth(),aBox.GetHeight()); + } + else + return awt::Rectangle(); +} + +} // end of namespace ::sd::presenter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_PresenterHelper_get_implementation(css::uno::XComponentContext* context, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::presenter::PresenterHelper(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterHelper.hxx b/sd/source/ui/presenter/PresenterHelper.hxx new file mode 100644 index 000000000..cee7e39fb --- /dev/null +++ b/sd/source/ui/presenter/PresenterHelper.hxx @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::uno { class XComponentContext; } + +namespace sd::presenter { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XInitialization, + css::drawing::XPresenterHelper +> PresenterHelperInterfaceBase; + +/** Implementation of the XPresenterHelper interface: functionality that can + not be implemented in an extension. +*/ +class PresenterHelper final + : public PresenterHelperInterfaceBase +{ +public: + explicit PresenterHelper (const css::uno::Reference& rxContext); + virtual ~PresenterHelper() override; + PresenterHelper(const PresenterHelper&) = delete; + PresenterHelper& operator=(const PresenterHelper&) = delete; + + // XInitialize + + virtual void SAL_CALL initialize (const css::uno::Sequence& rArguments) override; + + // XPresenterHelper + + virtual css::uno::Reference SAL_CALL createWindow ( + const css::uno::Reference& rxParentWindow, + sal_Bool bCreateSystemChildWindow, + sal_Bool bInitiallyVisible, + sal_Bool bEnableChildTransparentMode, + sal_Bool bEnableParentClip) override; + + virtual css::uno::Reference SAL_CALL createSharedCanvas ( + const css::uno::Reference& rxUpdateCanvas, + const css::uno::Reference& rxUpdateWindow, + const css::uno::Reference& rxSharedCanvas, + const css::uno::Reference& rxSharedWindow, + const css::uno::Reference& rxWindow) override; + + virtual css::uno::Reference SAL_CALL createCanvas ( + const css::uno::Reference& rxWindow, + sal_Int16 nRequestedCanvasFeatures, + const OUString& rsOptionalCanvasServiceName) override; + + virtual void SAL_CALL toTop ( + const css::uno::Reference& rxWindow) override; + + virtual css::uno::Reference SAL_CALL loadBitmap ( + const OUString& rsURL, + const css::uno::Reference& rxCanvas) override; + + virtual void SAL_CALL captureMouse (const css::uno::Reference& rxWindow) override; + + virtual void SAL_CALL releaseMouse (const css::uno::Reference& rxWindow) override; + + virtual css::awt::Rectangle SAL_CALL getWindowExtentsRelative ( + const css::uno::Reference& rxChildWindow, + const css::uno::Reference& rxParentWindow) override; + +private: + css::uno::Reference mxComponentContext; +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterPreviewCache.cxx b/sd/source/ui/presenter/PresenterPreviewCache.cxx new file mode 100644 index 000000000..fd29cdbfa --- /dev/null +++ b/sd/source/ui/presenter/PresenterPreviewCache.cxx @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PresenterPreviewCache.hxx" + +#include +#include +#include +#include +#include +#include +#include + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::sd::slidesorter::cache; + +namespace sd::presenter { + +class PresenterPreviewCache::PresenterCacheContext : public CacheContext +{ +public: + PresenterCacheContext(); + + void SetDocumentSlides ( + const Reference& rxSlides, + const Reference& rxDocument); + void SetVisibleSlideRange ( + const sal_Int32 nFirstVisibleSlideIndex, + const sal_Int32 nLastVisibleSlideIndex); + const SdrPage* GetPage (const sal_Int32 nSlideIndex) const; + void AddPreviewCreationNotifyListener (const Reference& rxListener); + void RemovePreviewCreationNotifyListener (const Reference& rxListener); + + // CacheContext + virtual void NotifyPreviewCreation (CacheKey aKey) override; + virtual bool IsIdle() override; + virtual bool IsVisible (CacheKey aKey) override; + virtual const SdrPage* GetPage (CacheKey aKey) override; + virtual std::shared_ptr > GetEntryList (bool bVisible) override; + virtual sal_Int32 GetPriority (CacheKey aKey) override; + virtual css::uno::Reference GetModel() override; + +private: + Reference mxSlides; + Reference mxDocument; + sal_Int32 mnFirstVisibleSlideIndex; + sal_Int32 mnLastVisibleSlideIndex; + typedef ::std::vector > ListenerContainer; + ListenerContainer maListeners; + + void CallListeners (const sal_Int32 nSlideIndex); +}; + +//===== PresenterPreviewCache ================================================= + +PresenterPreviewCache::PresenterPreviewCache () + : maPreviewSize(Size(200,200)), + mpCacheContext(std::make_shared()), + mpCache(std::make_shared(maPreviewSize, Bitmap::HasFastScale(), mpCacheContext)) +{ +} + +PresenterPreviewCache::~PresenterPreviewCache() +{ +} + +//----- XInitialize ----------------------------------------------------------- + +void SAL_CALL PresenterPreviewCache::initialize (const Sequence& rArguments) +{ + if (rArguments.hasElements()) + throw RuntimeException(); +} + +//----- XSlidePreviewCache ---------------------------------------------------- + +void SAL_CALL PresenterPreviewCache::setDocumentSlides ( + const Reference& rxSlides, + const Reference& rxDocument) +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCacheContext != nullptr); + + mpCacheContext->SetDocumentSlides(rxSlides, rxDocument); +} + +void SAL_CALL PresenterPreviewCache::setVisibleRange ( + sal_Int32 nFirstVisibleSlideIndex, + sal_Int32 nLastVisibleSlideIndex) +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCacheContext != nullptr); + + mpCacheContext->SetVisibleSlideRange (nFirstVisibleSlideIndex, nLastVisibleSlideIndex); +} + +void SAL_CALL PresenterPreviewCache::setPreviewSize ( + const css::geometry::IntegerSize2D& rSize) +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCache != nullptr); + + maPreviewSize = Size(rSize.Width, rSize.Height); + mpCache->ChangeSize(maPreviewSize, Bitmap::HasFastScale()); +} + +Reference SAL_CALL PresenterPreviewCache::getSlidePreview ( + sal_Int32 nSlideIndex, + const Reference& rxCanvas) +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCacheContext != nullptr); + + cppcanvas::CanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createCanvas(rxCanvas)); + + const SdrPage* pPage = mpCacheContext->GetPage(nSlideIndex); + if (pPage == nullptr) + throw RuntimeException(); + + const BitmapEx aPreview (mpCache->GetPreviewBitmap(pPage,true)); + if (aPreview.IsEmpty()) + return nullptr; + else + return cppcanvas::VCLFactory::createBitmap( + pCanvas, + aPreview)->getUNOBitmap(); +} + +void SAL_CALL PresenterPreviewCache::addPreviewCreationNotifyListener ( + const Reference& rxListener) +{ + if (m_bDisposed) + return; + if (rxListener.is()) + mpCacheContext->AddPreviewCreationNotifyListener(rxListener); +} + +void SAL_CALL PresenterPreviewCache::removePreviewCreationNotifyListener ( + const css::uno::Reference& rxListener) +{ + ThrowIfDisposed(); + mpCacheContext->RemovePreviewCreationNotifyListener(rxListener); +} + +void SAL_CALL PresenterPreviewCache::pause() +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCache != nullptr); + mpCache->Pause(); +} + +void SAL_CALL PresenterPreviewCache::resume() +{ + ThrowIfDisposed(); + OSL_ASSERT(mpCache != nullptr); + mpCache->Resume(); +} + +void PresenterPreviewCache::ThrowIfDisposed() +{ + if (m_bDisposed) + { + throw lang::DisposedException ("PresenterPreviewCache object has already been disposed", + static_cast(this)); + } +} + +//===== PresenterPreviewCache::PresenterCacheContext ========================== + +PresenterPreviewCache::PresenterCacheContext::PresenterCacheContext() + : mnFirstVisibleSlideIndex(-1), + mnLastVisibleSlideIndex(-1) +{ +} + +void PresenterPreviewCache::PresenterCacheContext::SetDocumentSlides ( + const Reference& rxSlides, + const Reference& rxDocument) +{ + mxSlides = rxSlides; + mxDocument = rxDocument; + mnFirstVisibleSlideIndex = -1; + mnLastVisibleSlideIndex = -1; +} + +void PresenterPreviewCache::PresenterCacheContext::SetVisibleSlideRange ( + const sal_Int32 nFirstVisibleSlideIndex, + const sal_Int32 nLastVisibleSlideIndex) +{ + if (nFirstVisibleSlideIndex > nLastVisibleSlideIndex || nFirstVisibleSlideIndex<0) + { + mnFirstVisibleSlideIndex = -1; + mnLastVisibleSlideIndex = -1; + } + else + { + mnFirstVisibleSlideIndex = nFirstVisibleSlideIndex; + mnLastVisibleSlideIndex = nLastVisibleSlideIndex; + } + if (mxSlides.is() && mnLastVisibleSlideIndex >= mxSlides->getCount()) + mnLastVisibleSlideIndex = mxSlides->getCount() - 1; +} + +void PresenterPreviewCache::PresenterCacheContext::AddPreviewCreationNotifyListener ( + const Reference& rxListener) +{ + maListeners.push_back(rxListener); +} + +void PresenterPreviewCache::PresenterCacheContext::RemovePreviewCreationNotifyListener ( + const Reference& rxListener) +{ + auto iListener = std::find(maListeners.begin(), maListeners.end(), rxListener); + if (iListener != maListeners.end()) + maListeners.erase(iListener); +} + +//----- CacheContext ---------------------------------------------------------- + +void PresenterPreviewCache::PresenterCacheContext::NotifyPreviewCreation ( + CacheKey aKey) +{ + if ( ! mxSlides.is()) + return; + const sal_Int32 nCount(mxSlides->getCount()); + for (sal_Int32 nIndex=0; nIndex > + PresenterPreviewCache::PresenterCacheContext::GetEntryList (bool bVisible) +{ + auto pKeys = std::make_shared>(); + + if ( ! mxSlides.is()) + return pKeys; + + const sal_Int32 nFirstIndex (bVisible ? mnFirstVisibleSlideIndex : 0); + const sal_Int32 nLastIndex (bVisible ? mnLastVisibleSlideIndex : mxSlides->getCount()-1); + + if (nFirstIndex < 0) + return pKeys; + + for (sal_Int32 nIndex=nFirstIndex; nIndex<=nLastIndex; ++nIndex) + { + pKeys->push_back(GetPage(nIndex)); + } + + return pKeys; +} + +sal_Int32 PresenterPreviewCache::PresenterCacheContext::GetPriority (CacheKey aKey) +{ + if ( ! mxSlides.is()) + return 0; + + const sal_Int32 nCount (mxSlides->getCount()); + + for (sal_Int32 nIndex=mnFirstVisibleSlideIndex; nIndex<=mnLastVisibleSlideIndex; ++nIndex) + if (aKey == GetPage(nIndex)) + return -nCount-1+nIndex; + + for (sal_Int32 nIndex=0; nIndex<=nCount; ++nIndex) + if (aKey == GetPage(nIndex)) + return nIndex; + + return 0; +} + +Reference PresenterPreviewCache::PresenterCacheContext::GetModel() +{ + return mxDocument; +} + +const SdrPage* PresenterPreviewCache::PresenterCacheContext::GetPage ( + const sal_Int32 nSlideIndex) const +{ + if ( ! mxSlides.is()) + return nullptr; + if (nSlideIndex < 0 || nSlideIndex >= mxSlides->getCount()) + return nullptr; + + Reference xSlide (mxSlides->getByIndex(nSlideIndex), UNO_QUERY); + const SdPage* pPage = SdPage::getImplementation(xSlide); + return pPage; +} + +void PresenterPreviewCache::PresenterCacheContext::CallListeners ( + const sal_Int32 nIndex) +{ + ListenerContainer aListeners (maListeners); + for (const auto& rxListener : aListeners) + { + try + { + rxListener->notifyPreviewCreation(nIndex); + } + catch (lang::DisposedException&) + { + RemovePreviewCreationNotifyListener(rxListener); + } + } +} + +} // end of namespace ::sd::presenter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_PresenterPreviewCache_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::presenter::PresenterPreviewCache); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterPreviewCache.hxx b/sd/source/ui/presenter/PresenterPreviewCache.hxx new file mode 100644 index 000000000..4f8c52280 --- /dev/null +++ b/sd/source/ui/presenter/PresenterPreviewCache.hxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace sd::slidesorter::cache { class PageCache; } + +namespace sd::presenter { + +typedef comphelper::WeakComponentImplHelper< + css::lang::XInitialization, + css::drawing::XSlidePreviewCache +> PresenterPreviewCacheInterfaceBase; + +/** Uno API wrapper around the slide preview cache. +*/ +class PresenterPreviewCache final + : public PresenterPreviewCacheInterfaceBase +{ +public: + PresenterPreviewCache (); + virtual ~PresenterPreviewCache() override; + PresenterPreviewCache(const PresenterPreviewCache&) = delete; + PresenterPreviewCache& operator=(const PresenterPreviewCache&) = delete; + + // XInitialize + + /** Accepts no arguments. All values that are necessary to set up a + preview cache can be provided via methods. + */ + virtual void SAL_CALL initialize (const css::uno::Sequence& rArguments) override; + + // XSlidePreviewCache + + virtual void SAL_CALL setDocumentSlides ( + const css::uno::Reference& rxSlides, + const css::uno::Reference& rxDocument) override; + + virtual void SAL_CALL setVisibleRange ( + sal_Int32 nFirstVisibleSlideIndex, + sal_Int32 nLastVisibleSlideIndex) override; + + virtual void SAL_CALL setPreviewSize ( + const css::geometry::IntegerSize2D& rSize) override; + + virtual css::uno::Reference SAL_CALL + getSlidePreview ( + sal_Int32 nSlideIndex, + const css::uno::Reference& rxCanvas) override; + + virtual void SAL_CALL addPreviewCreationNotifyListener ( + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL removePreviewCreationNotifyListener ( + const css::uno::Reference& rxListener) override; + + virtual void SAL_CALL pause() override; + + virtual void SAL_CALL resume() override; + +private: + class PresenterCacheContext; + Size maPreviewSize; + std::shared_ptr mpCacheContext; + std::shared_ptr mpCache; + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterTextView.cxx b/sd/source/ui/presenter/PresenterTextView.cxx new file mode 100644 index 000000000..affa21b03 --- /dev/null +++ b/sd/source/ui/presenter/PresenterTextView.cxx @@ -0,0 +1,466 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PresenterTextView.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +constexpr OUStringLiteral gsTextPropertyName(u"Text"); +constexpr OUStringLiteral gsBitmapPropertyName(u"Bitmap"); +constexpr OUStringLiteral gsSizePropertyName(u"Size"); +constexpr OUStringLiteral gsBackgroundColorPropertyName(u"BackgroundColor"); +constexpr OUStringLiteral gsTextColorPropertyName(u"TextColor"); +constexpr OUStringLiteral gsFontDescriptorPropertyName(u"FontDescriptor"); +constexpr OUStringLiteral gsTopPropertyName(u"Top"); +constexpr OUStringLiteral gsTopRelativePropertyName(u"RelativeTop"); +constexpr OUStringLiteral gsTotalHeightPropertyName(u"TotalHeight"); + +namespace sd::presenter { + +// PresenterTextView::Implementation +class PresenterTextView::Implementation +{ +public: + Implementation(); + ~Implementation(); + + void SetCanvas (const cppcanvas::CanvasSharedPtr& rCanvas); + void SetSize (const Size aSize); + void SetBackgroundColor (const Color aColor); + void SetTextColor (const Color aColor); + void SetFontDescriptor (const awt::FontDescriptor& rFontDescriptor); + sal_Int32 GetTop() const { return mnTop;} + void SetTop (const sal_Int32 nTop); + void SetText (const OUString& Text); + sal_Int32 ParseDistance (const OUString& rsDistance) const; + Reference const & GetBitmap(); + sal_Int32 GetTotalHeight(); + +private: + Reference mxBitmap; + cppcanvas::CanvasSharedPtr mpCanvas; + VclPtr mpOutputDevice; + std::unique_ptr mpEditEngine; + rtl::Reference mpEditEngineItemPool; + Size maSize; + OUString msText; + sal_Int32 mnTop; + sal_Int32 mnTotalHeight; + + void CheckTop(); +}; + +// PresenterTextView +PresenterTextView::PresenterTextView () + : mpImplementation(new Implementation()) +{ +} + +PresenterTextView::~PresenterTextView() +{ +} + +void SAL_CALL PresenterTextView::disposing() +{ + mpImplementation.reset(); +} + +// XInitialization +void SAL_CALL PresenterTextView::initialize (const Sequence& rArguments) +{ + ThrowIfDisposed(); + + if (rArguments.getLength() != 1) + { + throw RuntimeException("PresenterTextView: invalid number of arguments", + static_cast(this)); + } + + Reference xCanvas (rArguments[0], UNO_QUERY_THROW); + mpImplementation->SetCanvas( + cppcanvas::VCLFactory::createCanvas(xCanvas)); +} + +Any PresenterTextView::GetPropertyValue (const OUString& rsPropertyName) +{ + ThrowIfDisposed(); + + if (rsPropertyName == gsBitmapPropertyName) + { + return Any(mpImplementation->GetBitmap()); + } + else if (rsPropertyName == gsTopPropertyName) + { + return Any(mpImplementation->GetTop()); + } + else if (rsPropertyName == gsTotalHeightPropertyName) + { + return Any(mpImplementation->GetTotalHeight()); + } + + return Any(); +} + +Any PresenterTextView::SetPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rValue) +{ + ThrowIfDisposed(); + + Any aOldValue; + if (rsPropertyName == gsTextPropertyName) + { + OUString sText; + if (rValue >>= sText) + mpImplementation->SetText(sText); + } + else if (rsPropertyName == gsSizePropertyName) + { + awt::Size aSize; + if (rValue >>= aSize) + mpImplementation->SetSize(Size(aSize.Width,aSize.Height)); + } + else if (rsPropertyName == gsBackgroundColorPropertyName) + { + ::Color aColor; + if (rValue >>= aColor) + mpImplementation->SetBackgroundColor(aColor); + } + else if (rsPropertyName == gsTextColorPropertyName) + { + ::Color aColor; + if (rValue >>= aColor) + mpImplementation->SetTextColor(aColor); + } + else if (rsPropertyName == gsFontDescriptorPropertyName) + { + awt::FontDescriptor aFontDescriptor; + if (rValue >>= aFontDescriptor) + mpImplementation->SetFontDescriptor(aFontDescriptor); + } + else if (rsPropertyName == gsTopPropertyName) + { + sal_Int32 nTop = 0; + if (rValue >>= nTop) + mpImplementation->SetTop(nTop); + } + else if (rsPropertyName == gsTopRelativePropertyName) + { + OUString sDistance; + if (rValue >>= sDistance) + mpImplementation->SetTop( + mpImplementation->GetTop() + + mpImplementation->ParseDistance(sDistance)); + } + return aOldValue; +} + +void PresenterTextView::ThrowIfDisposed() +{ + if (PresenterTextViewInterfaceBase::rBHelper.bDisposed + || PresenterTextViewInterfaceBase::rBHelper.bInDispose || mpImplementation == nullptr) + { + throw lang::DisposedException ("PresenterTextView object has already been disposed", + static_cast(this)); + } +} + +// PresenterTextView::Implementation +PresenterTextView::Implementation::Implementation() + : mpOutputDevice(VclPtr::Create(*Application::GetDefaultDevice(), DeviceFormat::DEFAULT, DeviceFormat::DEFAULT)), + mpEditEngineItemPool(EditEngine::CreatePool()), + maSize(100,100), + mnTop(0), + mnTotalHeight(-1) +{ + mpOutputDevice->SetMapMode(MapMode(MapUnit::MapPixel)); + + // set fonts to be used + SvtLinguOptions aOpt; + SvtLinguConfig().GetOptions( aOpt ); + + struct FontDta { + LanguageType nFallbackLang; + LanguageType nLang; + DefaultFontType nFontType; + sal_uInt16 nFontInfoId; + } aTable[3] = + { + // info to get western font to be used + { LANGUAGE_ENGLISH_US, LANGUAGE_NONE, + DefaultFontType::SERIF, EE_CHAR_FONTINFO }, + // info to get CJK font to be used + { LANGUAGE_JAPANESE, LANGUAGE_NONE, + DefaultFontType::CJK_TEXT, EE_CHAR_FONTINFO_CJK }, + // info to get CTL font to be used + { LANGUAGE_ARABIC_SAUDI_ARABIA, LANGUAGE_NONE, + DefaultFontType::CTL_TEXT, EE_CHAR_FONTINFO_CTL } + }; + aTable[0].nLang = MsLangId::resolveSystemLanguageByScriptType(aOpt.nDefaultLanguage, css::i18n::ScriptType::LATIN); + aTable[1].nLang = MsLangId::resolveSystemLanguageByScriptType(aOpt.nDefaultLanguage_CJK, css::i18n::ScriptType::ASIAN); + aTable[2].nLang = MsLangId::resolveSystemLanguageByScriptType(aOpt.nDefaultLanguage_CTL, css::i18n::ScriptType::COMPLEX); + + for (const FontDta & rFntDta : aTable) + { + LanguageType nLang = (LANGUAGE_NONE == rFntDta.nLang) ? + rFntDta.nFallbackLang : rFntDta.nLang; + vcl::Font aFont = OutputDevice::GetDefaultFont( + rFntDta.nFontType, nLang, GetDefaultFontFlags::OnlyOne); + mpEditEngineItemPool->SetPoolDefaultItem( + SvxFontItem( + aFont.GetFamilyType(), + aFont.GetFamilyName(), + aFont.GetStyleName(), + aFont.GetPitch(), + aFont.GetCharSet(), + rFntDta.nFontInfoId)); + } + + mpEditEngine.reset( new EditEngine (mpEditEngineItemPool.get()) ); + + mpEditEngine->EnableUndo (true); + mpEditEngine->SetDefTab (sal_uInt16( + Application::GetDefaultDevice()->GetTextWidth("XXXX"))); + + mpEditEngine->SetControlWord( + EEControlBits(mpEditEngine->GetControlWord() | EEControlBits::AUTOINDENTING) & + EEControlBits(~EEControlBits::UNDOATTRIBS) & + EEControlBits(~EEControlBits::PASTESPECIAL) ); + + mpEditEngine->SetWordDelimiters (" .=+-*/(){}[];\""); + mpEditEngine->SetRefMapMode(MapMode(MapUnit::MapPixel)); + mpEditEngine->SetPaperSize (Size(800, 0)); + mpEditEngine->EraseVirtualDevice(); + mpEditEngine->ClearModifyFlag(); +} + +PresenterTextView::Implementation::~Implementation() +{ + mpEditEngine.reset(); + mpEditEngineItemPool.clear(); + mpOutputDevice.disposeAndClear(); +} + +void PresenterTextView::Implementation::SetCanvas (const cppcanvas::CanvasSharedPtr& rpCanvas) +{ + mpCanvas = rpCanvas; + mxBitmap = nullptr; +} + +void PresenterTextView::Implementation::SetSize (const Size aSize) +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + + maSize = aSize; + mpEditEngine->SetPaperSize(maSize); + mnTotalHeight = -1; + mxBitmap = nullptr; +} + +void PresenterTextView::Implementation::SetBackgroundColor (const Color aColor) +{ + mxBitmap = nullptr; + + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + DBG_ASSERT(mpEditEngineItemPool!=nullptr, "EditEngineItemPool missing"); + mpEditEngine->SetBackgroundColor(aColor); + mpEditEngine->EnableAutoColor(false); + mpEditEngine->ForceAutoColor(false); +} + +void PresenterTextView::Implementation::SetTextColor (const Color aColor) +{ + mxBitmap = nullptr; + + DBG_ASSERT(mpEditEngineItemPool!=nullptr, "EditEngineItemPool missing"); + mpEditEngineItemPool->SetPoolDefaultItem(SvxColorItem(aColor, EE_CHAR_COLOR)); +} + +void PresenterTextView::Implementation::SetFontDescriptor ( + const awt::FontDescriptor& rFontDescriptor) +{ + mxBitmap = nullptr; + + DBG_ASSERT(mpEditEngineItemPool!=nullptr, "EditEngineItemPool missing"); + + const sal_Int32 nFontHeight = rFontDescriptor.Height; + + SvxFontHeightItem aFontHeight( + Application::GetDefaultDevice()->LogicToPixel( + Size(0, nFontHeight), MapMode (MapUnit::MapPoint)).Height(), + 100, + EE_CHAR_FONTHEIGHT); + mpEditEngineItemPool->SetPoolDefaultItem( aFontHeight); + aFontHeight.SetWhich (EE_CHAR_FONTHEIGHT_CJK); + mpEditEngineItemPool->SetPoolDefaultItem( aFontHeight); + aFontHeight.SetWhich (EE_CHAR_FONTHEIGHT_CTL); + mpEditEngineItemPool->SetPoolDefaultItem( aFontHeight); + + SvxFontItem aSvxFontItem (EE_CHAR_FONTINFO); + aSvxFontItem.SetFamilyName( rFontDescriptor.Name ); + mpEditEngineItemPool->SetPoolDefaultItem(aSvxFontItem); + + mnTotalHeight = -1; + mxBitmap = nullptr; + + CheckTop(); + mnTotalHeight = -1; +} + +void PresenterTextView::Implementation::SetTop (const sal_Int32 nTop) +{ + if (nTop == mnTop) + return; + + mnTop = nTop; + mxBitmap = nullptr; + CheckTop(); +} + +void PresenterTextView::Implementation::SetText (const OUString& rText) +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + msText = rText; + mpEditEngine->SetPaperSize(maSize); + mnTotalHeight = -1; + mxBitmap = nullptr; +} + +sal_Int32 PresenterTextView::Implementation::ParseDistance (const OUString& rsDistance) const +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + sal_Int32 nDistance (0); + if (rsDistance.endsWith("px")) + { + nDistance = o3tl::toInt32(rsDistance.subView(0,rsDistance.getLength()-2)); + } + else if (rsDistance.endsWith("l")) + { + const sal_Int32 nLines (o3tl::toInt32(rsDistance.subView(0,rsDistance.getLength()-1))); + // Take the height of the first line as the height of every line. + const sal_uInt32 nFirstLineHeight (mpEditEngine->GetLineHeight(0)); + nDistance = nFirstLineHeight * nLines; + } + + return nDistance; +} + +Reference const & PresenterTextView::Implementation::GetBitmap() +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + + if ( ! mxBitmap.is()) + { + mpOutputDevice.disposeAndClear(); + mpOutputDevice = VclPtr::Create(*Application::GetDefaultDevice(), + DeviceFormat::DEFAULT, DeviceFormat::DEFAULT); + mpOutputDevice->SetMapMode(MapMode(MapUnit::MapPixel)); + mpOutputDevice->SetOutputSizePixel(maSize); + mpOutputDevice->SetLineColor(); + mpOutputDevice->SetFillColor(); + mpOutputDevice->SetBackground(Wallpaper()); + mpOutputDevice->Erase(); + + MapMode aMapMode (mpOutputDevice->GetMapMode()); + aMapMode.SetOrigin(Point(0,0)); + mpOutputDevice->SetMapMode(aMapMode); + const ::tools::Rectangle aWindowBox (Point(0,0), maSize); + mpOutputDevice->DrawRect(aWindowBox); + + mpEditEngine->Clear(); + mpEditEngine->SetText(msText); + mpEditEngine->SetPaperSize(maSize); + + mpEditEngine->Draw(*mpOutputDevice, aWindowBox, Point(0,mnTop)); + + const BitmapEx aBitmap (mpOutputDevice->GetBitmapEx(Point(0,0), maSize)); + mxBitmap = cppcanvas::VCLFactory::createBitmap( + mpCanvas, + aBitmap + )->getUNOBitmap(); + } + return mxBitmap; +} + +sal_Int32 PresenterTextView::Implementation::GetTotalHeight() +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + + if (mnTotalHeight < 0) + { + if ( ! mxBitmap.is()) + GetBitmap(); + mnTotalHeight = mpEditEngine->GetTextHeight(); + } + return mnTotalHeight; +} + +void PresenterTextView::Implementation::CheckTop() +{ + DBG_ASSERT(mpEditEngine!=nullptr, "EditEngine missing"); + + if (mpEditEngine!=nullptr && mnTotalHeight < 0) + mnTotalHeight = mpEditEngine->GetTextHeight(); + if (mpEditEngine!=nullptr && mnTop >= mnTotalHeight) + mnTop = mnTotalHeight - mpEditEngine->GetLineHeight(0); + + if (mnTotalHeight < maSize.Height()) + mnTop = 0; + + if (mnTotalHeight - mnTop < maSize.Height()) + mnTop = mnTotalHeight - maSize.Height(); + + if (mnTop < 0) + mnTop = 0; +} + +} // end of namespace ::sd::presenter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_PresenterTextView_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::presenter::PresenterTextView); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/PresenterTextView.hxx b/sd/source/ui/presenter/PresenterTextView.hxx new file mode 100644 index 000000000..28b68aaa5 --- /dev/null +++ b/sd/source/ui/presenter/PresenterTextView.hxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace sd::presenter { + +typedef ::cppu::ImplInheritanceHelper < + tools::PropertySet, + css::lang::XInitialization +> PresenterTextViewInterfaceBase; + +/** Render text into bitmaps. An edit engine is used to render the text. + This service is used by the presenter screen to render the notes view. +*/ +class PresenterTextView + : public PresenterTextViewInterfaceBase +{ +public: + PresenterTextView (); + virtual ~PresenterTextView() override; + PresenterTextView(const PresenterTextView&) = delete; + PresenterTextView& operator=(const PresenterTextView&) = delete; + + // XInitialization + + virtual void SAL_CALL initialize (const css::uno::Sequence& rArguments) override; + +protected: + virtual void SAL_CALL disposing() override; + + virtual css::uno::Any GetPropertyValue ( + const OUString& rsPropertyName) override; + virtual css::uno::Any SetPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rValue) override; + +private: + class Implementation; + std::unique_ptr mpImplementation; + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/SlideRenderer.cxx b/sd/source/ui/presenter/SlideRenderer.cxx new file mode 100644 index 000000000..1b57b195a --- /dev/null +++ b/sd/source/ui/presenter/SlideRenderer.cxx @@ -0,0 +1,201 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlideRenderer.hxx" +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::presenter { + +//===== SlideRenderer ========================================================== + +SlideRenderer::SlideRenderer () +{ +} + +SlideRenderer::~SlideRenderer() +{ +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL SlideRenderer::initialize (const Sequence& rArguments) +{ + ThrowIfDisposed(); + + if (rArguments.hasElements()) + { + throw RuntimeException("SlideRenderer: invalid number of arguments", + static_cast(this)); + } +} + +OUString SlideRenderer::getImplementationName() +{ + return "com.sun.star.comp.Draw.SlideRenderer"; +} + +sal_Bool SlideRenderer::supportsService(OUString const & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +css::uno::Sequence SlideRenderer::getSupportedServiceNames() +{ + return {"com.sun.star.drawing.SlideRenderer"}; +} + +//----- XSlideRenderer -------------------------------------------------------- + +Reference SlideRenderer::createPreview ( + const Reference& rxSlide, + const awt::Size& rMaximalSize, + sal_Int16 nSuperSampleFactor) +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + return VCLUnoHelper::CreateBitmap( + CreatePreview(rxSlide, rMaximalSize, nSuperSampleFactor)); +} + +Reference SlideRenderer::createPreviewForCanvas ( + const Reference& rxSlide, + const awt::Size& rMaximalSize, + sal_Int16 nSuperSampleFactor, + const Reference& rxCanvas) +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + cppcanvas::CanvasSharedPtr pCanvas ( + cppcanvas::VCLFactory::createCanvas(rxCanvas)); + if (pCanvas) + return cppcanvas::VCLFactory::createBitmap( + pCanvas, + CreatePreview(rxSlide, rMaximalSize, nSuperSampleFactor))->getUNOBitmap(); + else + return nullptr; +} + +awt::Size SAL_CALL SlideRenderer::calculatePreviewSize ( + double nSlideAspectRatio, + const awt::Size& rMaximalSize) +{ + if (rMaximalSize.Width <= 0 + || rMaximalSize.Height <= 0 + || nSlideAspectRatio <= 0) + { + return awt::Size(0,0); + } + + const double nWindowAspectRatio (double(rMaximalSize.Width) / double(rMaximalSize.Height)); + if (nSlideAspectRatio < nWindowAspectRatio) + return awt::Size( + sal::static_int_cast(rMaximalSize.Height * nSlideAspectRatio), + rMaximalSize.Height); + else + return awt::Size( + rMaximalSize.Width, + sal::static_int_cast(rMaximalSize.Width / nSlideAspectRatio)); +} + +BitmapEx SlideRenderer::CreatePreview ( + const Reference& rxSlide, + const awt::Size& rMaximalSize, + sal_Int16 nSuperSampleFactor) +{ + const SdPage* pPage = SdPage::getImplementation(rxSlide); + if (pPage == nullptr) + throw lang::IllegalArgumentException("SlideRenderer::createPreview() called with invalid slide", + static_cast(this), + 0); + + // Determine the size of the current slide and its aspect ratio. + Size aPageSize = pPage->GetSize(); + if (aPageSize.Height() <= 0) + throw lang::IllegalArgumentException("SlideRenderer::createPreview() called with invalid size", + static_cast(this), + 1); + + // Compare with the aspect ratio of the window (which rMaximalSize + // assumed to be) and calculate the size of the preview so that it + // a) will have the aspect ratio of the page and + // b) will be as large as possible. + awt::Size aPreviewSize (calculatePreviewSize( + double(aPageSize.Width()) / double(aPageSize.Height()), + rMaximalSize)); + if (aPreviewSize.Width <= 0 || aPreviewSize.Height <= 0) + return BitmapEx(); + + // Make sure that the super sample factor has a sane value. + sal_Int16 nFactor (nSuperSampleFactor); + if (nFactor < 1) + nFactor = 1; + else if (nFactor > 10) + nFactor = 10; + + // Create the preview. When the super sample factor n is greater than 1 + // then a preview is created in size (n*width, n*height) and then scaled + // down to (width, height). This is a poor mans antialiasing for the + // time being. When we have true antialiasing support this workaround + // can be removed. + const Image aPreview = maPreviewRenderer.RenderPage ( + pPage, + Size(aPreviewSize.Width*nFactor, aPreviewSize.Height*nFactor), + true); + if (nFactor == 1) + return aPreview.GetBitmapEx(); + else + { + BitmapEx aScaledPreview = aPreview.GetBitmapEx(); + aScaledPreview.Scale( + Size(aPreviewSize.Width,aPreviewSize.Height), + BmpScaleFlag::BestQuality); + return aScaledPreview; + } +} + +void SlideRenderer::ThrowIfDisposed() +{ + if (m_bDisposed) + { + throw lang::DisposedException ("SlideRenderer object has already been disposed", + static_cast(this)); + } +} + +} // end of namespace ::sd::presenter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_SlideRenderer_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::presenter::SlideRenderer); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/presenter/SlideRenderer.hxx b/sd/source/ui/presenter/SlideRenderer.hxx new file mode 100644 index 000000000..d39434421 --- /dev/null +++ b/sd/source/ui/presenter/SlideRenderer.hxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace com::sun::star::drawing { class XDrawPage; } + +namespace sd::presenter { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::XSlideRenderer, + css::lang::XInitialization, + css::lang::XServiceInfo +> SlideRendererInterfaceBase; + +/** Render single slides into bitmaps. +*/ +class SlideRenderer final + : public SlideRendererInterfaceBase +{ +public: + SlideRenderer (); + virtual ~SlideRenderer() override; + SlideRenderer(const SlideRenderer&) = delete; + SlideRenderer& operator=(const SlideRenderer&) = delete; + + // XInitialization + + virtual void SAL_CALL initialize (const css::uno::Sequence& rArguments) override; + + OUString SAL_CALL getImplementationName() override; + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override; + + css::uno::Sequence SAL_CALL getSupportedServiceNames() override; + + // XSlideRenderer + + virtual css::uno::Reference SAL_CALL createPreview ( + const css::uno::Reference& rxSlide, + const css::awt::Size& rMaximumPreviewPixelSize, + sal_Int16 nSuperSampleFactor) override; + + virtual css::uno::Reference SAL_CALL createPreviewForCanvas ( + const css::uno::Reference& rxSlide, + const css::awt::Size& rMaximumPreviewPixelSize, + sal_Int16 nSuperSampleFactor, + const css::uno::Reference& rxCanvas) override; + + virtual css::awt::Size SAL_CALL calculatePreviewSize ( + double nSlideAspectRatio, + const css::awt::Size& rMaximumPreviewPixelSize) override; + +private: + PreviewRenderer maPreviewRenderer; + + /// @throws css::uno::RuntimeException + BitmapEx CreatePreview ( + const css::uno::Reference& rxSlide, + const css::awt::Size& rMaximumPreviewPixelSize, + sal_Int16 nSuperSampleFactor); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::presenter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/AvahiNetworkService.cxx b/sd/source/ui/remotecontrol/AvahiNetworkService.cxx new file mode 100644 index 000000000..7708e6eb7 --- /dev/null +++ b/sd/source/ui/remotecontrol/AvahiNetworkService.cxx @@ -0,0 +1,209 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#if ENABLE_DBUS +#include +#endif + +#include + +#include "AvahiNetworkService.hxx" +#include "ZeroconfService.hxx" + +using namespace sd; + +static AvahiClient *client = nullptr; +static AvahiThreadedPoll *threaded_poll = nullptr; +static AvahiEntryGroup *group = nullptr; +static AvahiNetworkService *avahiService = nullptr; + +static bool create_services(AvahiClient *c); + +static void entry_group_callback(AvahiEntryGroup *g, AvahiEntryGroupState state, AVAHI_GCC_UNUSED void *userdata) { + assert(g == group || group == nullptr); + group = g; + + /* Called whenever the entry group state changes */ + + switch (state) { + case AVAHI_ENTRY_GROUP_ESTABLISHED : + /* The entry group has been established successfully */ + SAL_INFO( "sdremote.wifi", "Service '" << avahiService->getName() << "' successfully established." ); + break; + + case AVAHI_ENTRY_GROUP_COLLISION : { + char *n; + + /* A service name collision with a remote service + * happened. Let's pick a new name */ + n = avahi_alternative_service_name(avahiService->getName().c_str()); + avahiService->setName(n); + + SAL_INFO( "sdremote.wifi", "Service name collision, renaming service to '" << avahiService->getName() << "'"); + + /* And recreate the services */ + create_services(avahi_entry_group_get_client(g)); + break; + } + + case AVAHI_ENTRY_GROUP_FAILURE : + + SAL_WARN("sdremote.wifi", "Entry group failure: " << avahi_strerror(avahi_client_errno(avahi_entry_group_get_client(g)))); + + /* Some kind of failure happened while we were registering our services */ + avahi_threaded_poll_quit(threaded_poll); + break; + + case AVAHI_ENTRY_GROUP_UNCOMMITED: + case AVAHI_ENTRY_GROUP_REGISTERING: + ; + } +} + +static bool create_services(AvahiClient *c) { + assert(c); + + /* If this is the first time we're called, let's create a new + * entry group if necessary */ + if(!client) + return false; + + if (!group) + if (!(group = avahi_entry_group_new(c, entry_group_callback, nullptr))) { + SAL_WARN("sdremote.wifi", "avahi_entry_group_new() failed: " << avahi_strerror(avahi_client_errno(c))); + avahiService->clear(); + return false; + } + + /* If the group is empty (either because it was just created, or + * because it was reset previously, add our entries. */ + + if (avahi_entry_group_is_empty(group)) { + SAL_INFO("sdremote.wifi", "Adding service '" << avahiService->getName() << "'"); + char r[128]; + int nRandom = comphelper::rng::uniform_int_distribution(0, std::numeric_limits::max()); + snprintf(r, sizeof(r), "random=%i", nRandom); + int ret = avahi_entry_group_add_service( + group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, static_cast(0), + avahiService->getName().c_str(), kREG_TYPE, nullptr, nullptr, 1599, "local", r, nullptr + ); + if (ret < 0) { + + if (ret == AVAHI_ERR_COLLISION){ + /* A service name collision with a local service happened. Let's + * pick a new name */ + char *n = avahi_alternative_service_name(avahiService->getName().c_str()); + avahiService->setName(n); + + SAL_WARN("sdremote.wifi", "Service name collision, renaming service to '" << avahiService->getName() << "'"); + + avahi_entry_group_reset(group); + + return create_services(c); + } + + SAL_WARN("sdremote.wifi", "Failed to add _impressremote._tcp service: " << avahi_strerror(ret)); + avahiService->clear(); + return false; + } + + /* Tell the server to register the service */ + if ((ret = avahi_entry_group_commit(group)) < 0) { + SAL_WARN("sdremote.wifi", "Failed to commit entry group: " << avahi_strerror(ret)); + avahiService->clear(); + return false; + } + } + + return true; //Services we're already created +} + +static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { + assert(c); + + /* Called whenever the client or server state changes */ + + switch (state) { + case AVAHI_CLIENT_S_RUNNING: + create_services(c); + break; + case AVAHI_CLIENT_FAILURE: + SAL_WARN("sdremote.wifi", "Client failure: " << avahi_strerror(avahi_client_errno(c))); + avahiService->clear(); + break; + case AVAHI_CLIENT_S_COLLISION: + case AVAHI_CLIENT_S_REGISTERING: + if (group) + avahi_entry_group_reset(group); + break; + case AVAHI_CLIENT_CONNECTING: + ; + } +} + +void AvahiNetworkService::setup() { +#if ENABLE_DBUS + // Sure, without ENABLE_DBUS it probably makes no sense to try to use this Avahi stuff either, + // but this is just a stop-gap measure to get this to even compile for now with the probably + // pointless combination of configurable options --enable-avahi --enable-dbus --disable-gui. + + // Avahi internally uses D-Bus, which requires the following in order to be + // thread-safe (and we potentially access D-Bus from different threads in + // different places of the code base): + if (!dbus_threads_init_default()) { + throw std::bad_alloc(); + } +#endif + + int error = 0; + avahiService = this; + if (!(threaded_poll = avahi_threaded_poll_new())) { + SAL_WARN("sdremote.wifi", "avahi_threaded_poll_new '" << avahiService->getName() << "' failed"); + return; + } + + if (!(client = avahi_client_new(avahi_threaded_poll_get(threaded_poll), static_cast(0), client_callback, nullptr, &error))) { + SAL_WARN("sdremote.wifi", "avahi_client_new failed"); + return; + } + + if(!create_services(client)) + return; + + /* Finally, start the event loop thread */ + if (avahi_threaded_poll_start(threaded_poll) < 0) { + SAL_WARN("sdremote.wifi", "avahi_threaded_poll_start failed"); + return; + } +} + +void AvahiNetworkService::clear() { + /* Call this when the app shuts down */ + if(threaded_poll) + avahi_threaded_poll_stop(threaded_poll); + if(client) + avahi_client_free(client); + if(threaded_poll) + avahi_threaded_poll_free(threaded_poll); +} diff --git a/sd/source/ui/remotecontrol/AvahiNetworkService.hxx b/sd/source/ui/remotecontrol/AvahiNetworkService.hxx new file mode 100644 index 000000000..374a27a3a --- /dev/null +++ b/sd/source/ui/remotecontrol/AvahiNetworkService.hxx @@ -0,0 +1,25 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include "ZeroconfService.hxx" + +namespace sd { + + class AvahiNetworkService : public ZeroconfService + { + public: + AvahiNetworkService(const std::string& aname = "", unsigned int aport = 1599) + : ZeroconfService(aname, aport){} + + void clear() override; + void setup() override; + }; +} diff --git a/sd/source/ui/remotecontrol/BluetoothServer.cxx b/sd/source/ui/remotecontrol/BluetoothServer.cxx new file mode 100644 index 000000000..fc3eeff54 --- /dev/null +++ b/sd/source/ui/remotecontrol/BluetoothServer.cxx @@ -0,0 +1,1521 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "BluetoothServer.hxx" + +#include +#include +#include +#include + +#include + +#ifdef LINUX_BLUETOOTH + #include + #include + #include + #include + #include + #include + #include + #include + #include "BluetoothServiceRecord.hxx" + #include "BufferedStreamSocket.hxx" +#endif + +#ifdef _WIN32 + // LO vs WinAPI conflict + #undef WB_LEFT + #undef WB_RIGHT + #include + #include + #include "BufferedStreamSocket.hxx" +#endif + +#ifdef MACOSX + #include + #include + #include + #import + #import + #import + #import + #include + #import "OSXBluetooth.h" + #include "OSXBluetoothWrapper.hxx" +#endif + +#include "Communicator.hxx" + +using namespace sd; + +#ifdef LINUX_BLUETOOTH + +namespace { + +struct DBusObject { + OString maBusName; + OString maPath; + OString maInterface; + + DBusObject() { } + DBusObject( const char *pBusName, const char *pPath, const char *pInterface ) + : maBusName( pBusName ), maPath( pPath ), maInterface( pInterface ) { } + + DBusMessage *getMethodCall( const char *pName ) + { + return dbus_message_new_method_call( maBusName.getStr(), maPath.getStr(), + maInterface.getStr(), pName ); + } + std::unique_ptr cloneForInterface( const char *pInterface ) + { + std::unique_ptr pObject(new DBusObject()); + + pObject->maBusName = maBusName; + pObject->maPath = maPath; + pObject->maInterface = pInterface; + + return pObject; + } +}; + +} + +static std::unique_ptr getBluez5Adapter(DBusConnection *pConnection); + +struct sd::BluetoothServer::Impl { + // the glib mainloop running in the thread + GMainContext *mpContext; + DBusConnection *mpConnection; + std::unique_ptr mpService; + enum class BluezVersion { BLUEZ4, BLUEZ5, UNKNOWN }; + BluezVersion maBluezVersion; + + Impl() + : mpContext( g_main_context_new() ) + , mpConnection( nullptr ) + , maBluezVersion( BluezVersion::UNKNOWN ) + { } + + std::unique_ptr getAdapter() + { + if (mpService) + { + return mpService->cloneForInterface( "org.bluez.Adapter" ); + } + else if (spServer->mpImpl->maBluezVersion == BluezVersion::BLUEZ5) + { + return getBluez5Adapter(mpConnection); + } + else + { + return nullptr; + } + } +}; + +static DBusConnection * +dbusConnectToNameOnBus() +{ + DBusError aError; + DBusConnection *pConnection; + + dbus_error_init( &aError ); + + pConnection = dbus_bus_get( DBUS_BUS_SYSTEM, &aError ); + if( !pConnection || dbus_error_is_set( &aError )) + { + SAL_WARN( "sdremote.bluetooth", "failed to get dbus system bus: " << aError.message ); + dbus_error_free( &aError ); + return nullptr; + } + + return pConnection; +} + +static DBusMessage * +sendUnrefAndWaitForReply( DBusConnection *pConnection, DBusMessage *pMsg ) +{ + DBusPendingCall *pPending = nullptr; + + if( !pMsg || !dbus_connection_send_with_reply( pConnection, pMsg, &pPending, + -1 /* default timeout */ ) ) + { + SAL_WARN( "sdremote.bluetooth", "Memory allocation failed on message send" ); + dbus_message_unref( pMsg ); + return nullptr; + } + dbus_connection_flush( pConnection ); + dbus_message_unref( pMsg ); + + dbus_pending_call_block( pPending ); // block for reply + + pMsg = dbus_pending_call_steal_reply( pPending ); + if( !pMsg ) + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + + dbus_pending_call_unref( pPending ); + return pMsg; +} + +static bool +isBluez5Available(DBusConnection *pConnection) +{ + DBusMessage *pMsg; + + // Simplest ways to check whether we have Bluez 5+ is to check + // that we can obtain adapters using the new interfaces. + // The first two error checks however don't tell us anything as they should + // succeed as long as dbus is working correctly. + pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" ); + if (!pMsg) + { + SAL_INFO("sdremote.bluetooth", "No GetManagedObjects call created"); + return false; + } + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + if (!pMsg) + { + SAL_INFO("sdremote.bluetooth", "No reply received"); + return false; + } + + // If dbus is working correctly and we aren't on bluez 5 this is where we + // should actually get the error. + if (dbus_message_get_error_name( pMsg )) + { + SAL_INFO( "sdremote.bluetooth", "GetManagedObjects call failed with \"" + << dbus_message_get_error_name( pMsg ) + << "\" -- we don't seem to have Bluez 5 available"); + return false; + } + SAL_INFO("sdremote.bluetooth", "GetManagedObjects call seems to have succeeded -- we must be on Bluez 5"); + dbus_message_unref(pMsg); + return true; +} + +static std::unique_ptr +getBluez5Adapter(DBusConnection *pConnection) +{ + DBusMessage *pMsg; + // This returns a list of objects where we need to find the first + // org.bluez.Adapter1 . + pMsg = DBusObject( "org.bluez", "/", "org.freedesktop.DBus.ObjectManager" ).getMethodCall( "GetManagedObjects" ); + if (!pMsg) + return nullptr; + + const gchar* const pInterfaceType = "org.bluez.Adapter1"; + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + DBusMessageIter aObjectIterator; + if (pMsg && dbus_message_iter_init(pMsg, &aObjectIterator)) + { + if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aObjectIterator)) + { + DBusMessageIter aObject; + dbus_message_iter_recurse(&aObjectIterator, &aObject); + do + { + if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aObject)) + { + DBusMessageIter aContainerIter; + dbus_message_iter_recurse(&aObject, &aContainerIter); + char *pPath = nullptr; + do + { + if (DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type(&aContainerIter)) + { + dbus_message_iter_get_basic(&aContainerIter, &pPath); + SAL_INFO( "sdremote.bluetooth", "Something retrieved: '" + << pPath << "' '"); + } + else if (DBUS_TYPE_ARRAY == dbus_message_iter_get_arg_type(&aContainerIter)) + { + DBusMessageIter aInnerIter; + dbus_message_iter_recurse(&aContainerIter, &aInnerIter); + do + { + if (DBUS_TYPE_DICT_ENTRY == dbus_message_iter_get_arg_type(&aInnerIter)) + { + DBusMessageIter aInnerInnerIter; + dbus_message_iter_recurse(&aInnerIter, &aInnerInnerIter); + do + { + if (DBUS_TYPE_STRING == dbus_message_iter_get_arg_type(&aInnerInnerIter)) + { + char* pMessage; + + dbus_message_iter_get_basic(&aInnerInnerIter, &pMessage); + if (pMessage == std::string_view("org.bluez.Adapter1")) + { + dbus_message_unref(pMsg); + if (pPath) + { + return std::make_unique( "org.bluez", pPath, pInterfaceType ); + } + assert(false); // We should already have pPath provided for us. + } + } + } + while (dbus_message_iter_next(&aInnerInnerIter)); + } + } + while (dbus_message_iter_next(&aInnerIter)); + } + } + while (dbus_message_iter_next(&aContainerIter)); + } + } + while (dbus_message_iter_next(&aObject)); + } + dbus_message_unref(pMsg); + } + + return nullptr; +} + +static DBusObject * +bluez4GetDefaultService( DBusConnection *pConnection ) +{ + DBusMessage *pMsg; + DBusMessageIter it; + const gchar* const pInterfaceType = "org.bluez.Service"; + + // org.bluez.manager only exists for bluez 4. + // getMethodCall should return NULL if there is any issue e.g. the + // if org.bluez.manager doesn't exist. + pMsg = DBusObject( "org.bluez", "/", "org.bluez.Manager" ).getMethodCall( "DefaultAdapter" ); + + if (!pMsg) + { + SAL_WARN("sdremote.bluetooth", "Couldn't retrieve DBusObject for DefaultAdapter"); + return nullptr; + } + + SAL_INFO("sdremote.bluetooth", "successfully retrieved org.bluez.Manager.DefaultAdapter, attempting to use."); + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if(!pMsg || !dbus_message_iter_init( pMsg, &it ) ) + { + return nullptr; + } + + // This works for Bluez 4 + if( DBUS_TYPE_OBJECT_PATH == dbus_message_iter_get_arg_type( &it ) ) + { + const char *pObjectPath = nullptr; + dbus_message_iter_get_basic( &it, &pObjectPath ); + SAL_INFO( "sdremote.bluetooth", "DefaultAdapter retrieved: '" + << pObjectPath << "' '" << pInterfaceType << "'" ); + dbus_message_unref( pMsg ); + return new DBusObject( "org.bluez", pObjectPath, pInterfaceType ); + } + // Some form of error, e.g. if we have bluez 5 we get a message that + // this method doesn't exist. + else if ( DBUS_TYPE_STRING == dbus_message_iter_get_arg_type( &it ) ) + { + const char *pMessage = nullptr; + dbus_message_iter_get_basic( &it, &pMessage ); + SAL_INFO( "sdremote.bluetooth", "Error message: '" + << pMessage << "' '" << pInterfaceType << "'" ); + } + else + { + SAL_INFO( "sdremote.bluetooth", "invalid type of reply to DefaultAdapter: '" + << static_cast(dbus_message_iter_get_arg_type( &it )) << "'" ); + } + dbus_message_unref(pMsg); + return nullptr; +} + +static bool +bluez4RegisterServiceRecord( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pServiceRecord ) +{ + DBusMessage *pMsg; + DBusMessageIter it; + + pMsg = pAdapter->getMethodCall( "AddRecord" ); + dbus_message_iter_init_append( pMsg, &it ); + dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pServiceRecord ); + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if( !pMsg || !dbus_message_iter_init( pMsg, &it ) || + dbus_message_iter_get_arg_type( &it ) != DBUS_TYPE_UINT32 ) + { + SAL_WARN( "sdremote.bluetooth", "SDP registration failed" ); + return false; + } + + // We ignore the uint de-registration handle we get back: + // bluez will clean us up automatically on exit + + return true; +} + +static void +bluezCreateAttachListeningSocket( GMainContext *pContext, GPollFD *pSocketFD ) +{ + int nSocket; + + pSocketFD->fd = -1; + + if( ( nSocket = socket( AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM ) ) < 0 ) + { + SAL_WARN( "sdremote.bluetooth", "failed to open bluetooth socket with error " << nSocket ); + return; + } + + sockaddr_rc aAddr; + // Initialize whole structure. Mainly to appease valgrind, which + // doesn't know about the padding at the end of sockaddr_rc which + // it will dutifully check for definedness. But also the standard + // definition of BDADDR_ANY is unusable in C++ code, so just use + // memset to set aAddr.rc_bdaddr to 0. + memset( &aAddr, 0, sizeof( aAddr ) ); + aAddr.rc_family = AF_BLUETOOTH; + aAddr.rc_channel = 5; + + int a; + if ( ( a = bind( nSocket, reinterpret_cast(&aAddr), sizeof(aAddr) ) ) < 0 ) { + SAL_WARN( "sdremote.bluetooth", "bind failed with error" << a ); + close( nSocket ); + return; + } + + if ( ( a = listen( nSocket, 1 ) ) < 0 ) + { + SAL_WARN( "sdremote.bluetooth", "listen failed with error" << a ); + close( nSocket ); + return; + } + + // set non-blocking behaviour ... + if( fcntl( nSocket, F_SETFL, O_NONBLOCK) < 0 ) + { + close( nSocket ); + return; + } + + pSocketFD->fd = nSocket; + pSocketFD->events = G_IO_IN | G_IO_PRI; + pSocketFD->revents = 0; + + g_main_context_add_poll( pContext, pSocketFD, G_PRIORITY_DEFAULT ); +} + +static void +bluezDetachCloseSocket( GMainContext *pContext, GPollFD *pSocketFD ) +{ + if( pSocketFD->fd >= 0 ) + { + close( pSocketFD->fd ); + g_main_context_remove_poll( pContext, pSocketFD ); + pSocketFD->fd = -1; + } +} + +#endif // LINUX_BLUETOOTH + +#if defined(MACOSX) + +OSXBluetoothWrapper::OSXBluetoothWrapper( IOBluetoothRFCOMMChannel* channel ) : + mpChannel(channel), + mnMTU(0), + mHaveBytes(), + mMutex(), + mBuffer() +{ + // silly enough, can't write more than mnMTU bytes at once + mnMTU = [channel getMTU]; + + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::OSXBluetoothWrapper(): mnMTU=" << mnMTU ); +} + +sal_Int32 OSXBluetoothWrapper::readLine( OString& aLine ) +{ + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine()" ); + + while( true ) + { + { + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: entering mutex" ); + ::osl::MutexGuard aQueueGuard( mMutex ); + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: entered mutex" ); + +#ifdef SAL_LOG_INFO + // We should have in the sal logging some standard way to + // output char buffers with non-printables escaped. + std::ostringstream s; + if (mBuffer.size() > 0) + { + for (unsigned char *p = reinterpret_cast(mBuffer.data()); p != reinterpret_cast(mBuffer.data()) + mBuffer.size(); p++) + { + if (*p == '\n') + s << "\\n"; + else if (*p < ' ' || *p >= 0x7F) + s << "\\0x" << std::hex << std::setw(2) << std::setfill('0') << static_cast(*p) << std::setfill(' ') << std::setw(1) << std::dec; + else + s << *p; + } + } + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine mBuffer: \"" << s.str() << "\"" ); +#endif + + // got enough bytes to return a line? + std::vector::iterator aIt; + if ( (aIt = find( mBuffer.begin(), mBuffer.end(), '\n' )) + != mBuffer.end() ) + { + sal_uInt64 aLocation = aIt - mBuffer.begin(); + + aLine = OString( &(*mBuffer.begin()), aLocation ); + + mBuffer.erase( mBuffer.begin(), aIt + 1 ); // Also delete the empty line + + // yeps + SAL_INFO( "sdremote.bluetooth", " returning, got \"" << OStringToOUString( aLine, RTL_TEXTENCODING_UTF8 ) << "\"" ); + return aLine.getLength() + 1; + } + + // nope - wait some more (after releasing the mutex) + SAL_INFO( "sdremote.bluetooth", " resetting mHaveBytes" ); + mHaveBytes.reset(); + SAL_INFO( "sdremote.bluetooth", " leaving mutex" ); + } + + SAL_INFO( "sdremote.bluetooth", " waiting for mHaveBytes" ); + mHaveBytes.wait(); + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::readLine: got mHaveBytes" ); + } +} + +sal_Int32 OSXBluetoothWrapper::write( const void* pBuffer, sal_uInt32 n ) +{ + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::write(" << pBuffer << ", " << n << ") mpChannel=" << mpChannel ); + + char const * ptr = static_cast(pBuffer); + sal_uInt32 nBytesWritten = 0; + + if (mpChannel == nil) + return 0; + + while( nBytesWritten < n ) + { + int toWrite = n - nBytesWritten; + toWrite = toWrite <= mnMTU ? toWrite : mnMTU; + if ( [mpChannel writeSync:const_cast(ptr) length:toWrite] != kIOReturnSuccess ) + { + SAL_INFO( "sdremote.bluetooth", " [mpChannel writeSync:" << static_cast(ptr) << " length:" << toWrite << "] returned error, total written " << nBytesWritten ); + return nBytesWritten; + } + ptr += toWrite; + nBytesWritten += toWrite; + } + SAL_INFO( "sdremote.bluetooth", " total written " << nBytesWritten ); + return nBytesWritten; +} + +void OSXBluetoothWrapper::appendData(void* pBuffer, size_t len) +{ + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData(" << pBuffer << ", " << len << ")" ); + + if( len ) + { + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData: entering mutex" ); + ::osl::MutexGuard aQueueGuard( mMutex ); + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::appendData: entered mutex" ); + mBuffer.insert(mBuffer.begin()+mBuffer.size(), + static_cast(pBuffer), static_cast(pBuffer)+len); + SAL_INFO( "sdremote.bluetooth", " setting mHaveBytes" ); + mHaveBytes.set(); + SAL_INFO( "sdremote.bluetooth", " leaving mutex" ); + } +} + +void OSXBluetoothWrapper::channelClosed() +{ + SAL_INFO( "sdremote.bluetooth", "OSXBluetoothWrapper::channelClosed()" ); + + mpChannel = nil; +} + +void incomingCallback( void *userRefCon, + IOBluetoothUserNotificationRef, + IOBluetoothObjectRef objectRef ) +{ + SAL_INFO( "sdremote.bluetooth", "incomingCallback()" ); + + BluetoothServer* pServer = static_cast(userRefCon); + + IOBluetoothRFCOMMChannel* channel = [IOBluetoothRFCOMMChannel withRFCOMMChannelRef:reinterpret_cast(objectRef)]; + + OSXBluetoothWrapper* socket = new OSXBluetoothWrapper( channel); + Communicator* pCommunicator = new Communicator( std::unique_ptr(socket) ); + pServer->addCommunicator( pCommunicator ); + + ChannelDelegate* delegate = [[ChannelDelegate alloc] initWithCommunicatorAndSocket: pCommunicator socket: socket]; + [channel setDelegate: delegate]; + [delegate retain]; + + pCommunicator->launch(); +} + +void BluetoothServer::addCommunicator( Communicator* pCommunicator ) +{ + mpCommunicators->push_back( pCommunicator ); +} + +#endif // MACOSX + +#ifdef LINUX_BLUETOOTH + +extern "C" { + static gboolean ensureDiscoverable_cb(gpointer) + { + BluetoothServer::doEnsureDiscoverable(); + return FALSE; // remove source + } + static gboolean restoreDiscoverable_cb(gpointer) + { + BluetoothServer::doRestoreDiscoverable(); + return FALSE; // remove source + } +} + +/* + * Bluez 4 uses custom methods for setting properties, whereas Bluez 5+ + * implements properties using the generic "org.freedesktop.DBus.Properties" + * interface -- hence we have a specific Bluez 4 function to deal with the + * old style of reading properties. + */ +static bool +getBluez4BooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pPropertyName, bool *pBoolean ) +{ + *pBoolean = false; + + if( !pAdapter ) + return false; + + DBusMessage *pMsg; + pMsg = sendUnrefAndWaitForReply( pConnection, + pAdapter->getMethodCall( "GetProperties" ) ); + + DBusMessageIter it; + if( !pMsg || !dbus_message_iter_init( pMsg, &it ) ) + { + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + return false; + } + + if( DBUS_TYPE_ARRAY != dbus_message_iter_get_arg_type( &it ) ) + { + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + return false; + } + + DBusMessageIter arrayIt; + dbus_message_iter_recurse( &it, &arrayIt ); + + while( dbus_message_iter_get_arg_type( &arrayIt ) == DBUS_TYPE_DICT_ENTRY ) + { + DBusMessageIter dictIt; + dbus_message_iter_recurse( &arrayIt, &dictIt ); + + const char *pName = nullptr; + if( dbus_message_iter_get_arg_type( &dictIt ) == DBUS_TYPE_STRING ) + { + dbus_message_iter_get_basic( &dictIt, &pName ); + if( pName != nullptr && !strcmp( pName, pPropertyName ) ) + { + SAL_INFO( "sdremote.bluetooth", "hit " << pPropertyName << " property" ); + dbus_message_iter_next( &dictIt ); + dbus_bool_t bBool = false; + + if( dbus_message_iter_get_arg_type( &dictIt ) == DBUS_TYPE_VARIANT ) + { + DBusMessageIter variantIt; + dbus_message_iter_recurse( &dictIt, &variantIt ); + + if( dbus_message_iter_get_arg_type( &variantIt ) == DBUS_TYPE_BOOLEAN ) + { + dbus_message_iter_get_basic( &variantIt, &bBool ); + SAL_INFO( "sdremote.bluetooth", "" << pPropertyName << " is " << bBool ); + *pBoolean = bBool; + return true; + } + else + SAL_WARN( "sdremote.bluetooth", "" << pPropertyName << " type " << + dbus_message_iter_get_arg_type( &variantIt ) ); + } + else + SAL_WARN( "sdremote.bluetooth", "variant type ? " << + dbus_message_iter_get_arg_type( &dictIt ) ); + } + else + { + const char *pStr = pName ? pName : ""; + SAL_INFO( "sdremote.bluetooth", "property '" << pStr << "'" ); + } + } + else + SAL_WARN( "sdremote.bluetooth", "unexpected property key type " + << dbus_message_iter_get_arg_type( &dictIt ) ); + dbus_message_iter_next( &arrayIt ); + } + dbus_message_unref( pMsg ); + + return false; +} + +/* + * This gets an org.freedesktop.DBus.Properties boolean + * (as opposed to the old Bluez 4 custom properties methods as visible above). + */ +static bool +getDBusBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pPropertyName, bool *pBoolean ) +{ + assert( pAdapter ); + + *pBoolean = false; + bool bRet = false; + + std::unique_ptr< DBusObject > pProperties ( + pAdapter->cloneForInterface( "org.freedesktop.DBus.Properties" ) ); + + DBusMessage *pMsg = pProperties->getMethodCall( "Get" ); + + DBusMessageIter itIn; + dbus_message_iter_init_append( pMsg, &itIn ); + const char* pInterface = "org.bluez.Adapter1"; + dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pInterface ); + dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pPropertyName ); + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + DBusMessageIter it; + if( !pMsg || !dbus_message_iter_init( pMsg, &it ) ) + { + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + return false; + } + + if( DBUS_TYPE_VARIANT != dbus_message_iter_get_arg_type( &it ) ) + { + SAL_WARN( "sdremote.bluetooth", "invalid return type" ); + } + else + { + DBusMessageIter variantIt; + dbus_message_iter_recurse( &it, &variantIt ); + + if( dbus_message_iter_get_arg_type( &variantIt ) == DBUS_TYPE_BOOLEAN ) + { + dbus_bool_t bBool = false; + dbus_message_iter_get_basic( &variantIt, &bBool ); + SAL_INFO( "sdremote.bluetooth", "" << pPropertyName << " is " << bBool ); + *pBoolean = bBool; + bRet = true; + } + else + { + SAL_WARN( "sdremote.bluetooth", "" << pPropertyName << " type " << + dbus_message_iter_get_arg_type( &variantIt ) ); + } + + const char* pError = dbus_message_get_error_name( pMsg ); + if ( pError ) + { + SAL_WARN( "sdremote.bluetooth", + "Get failed for " << pPropertyName << " on " << + pAdapter->maPath << " with error: " << pError ); + } + } + dbus_message_unref( pMsg ); + + return bRet; +} + +static void +setDBusBooleanProperty( DBusConnection *pConnection, DBusObject *pAdapter, + const char *pPropertyName, bool bBoolean ) +{ + assert( pAdapter ); + + std::unique_ptr< DBusObject > pProperties( + pAdapter->cloneForInterface( "org.freedesktop.DBus.Properties" ) ); + + DBusMessage *pMsg = pProperties->getMethodCall( "Set" ); + + DBusMessageIter itIn; + dbus_message_iter_init_append( pMsg, &itIn ); + const char* pInterface = "org.bluez.Adapter1"; + dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pInterface ); + dbus_message_iter_append_basic( &itIn, DBUS_TYPE_STRING, &pPropertyName ); + + { + DBusMessageIter varIt; + dbus_message_iter_open_container( &itIn, DBUS_TYPE_VARIANT, + DBUS_TYPE_BOOLEAN_AS_STRING, &varIt ); + dbus_bool_t bDBusBoolean = bBoolean; + dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bDBusBoolean ); + dbus_message_iter_close_container( &itIn, &varIt ); + } + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if( !pMsg ) + { + SAL_WARN( "sdremote.bluetooth", "no valid reply / timeout" ); + } + else + { + const char* pError = dbus_message_get_error_name( pMsg ); + if ( pError ) + { + SAL_WARN( "sdremote.bluetooth", + "Set failed for " << pPropertyName << " on " << + pAdapter->maPath << " with error: " << pError ); + } + dbus_message_unref( pMsg ); + } +} + +static bool +getDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter ) +{ + if (pAdapter->maInterface == "org.bluez.Adapter") // Bluez 4 + { + bool bDiscoverable; + if( getBluez4BooleanProperty(pConnection, pAdapter, "Discoverable", &bDiscoverable ) ) + return bDiscoverable; + } + else if (pAdapter->maInterface == "org.bluez.Adapter1") // Bluez 5 + { + bool bDiscoverable; + if ( getDBusBooleanProperty(pConnection, pAdapter, "Discoverable", &bDiscoverable ) ) + return bDiscoverable; + } + return false; +} + +static void +setDiscoverable( DBusConnection *pConnection, DBusObject *pAdapter, bool bDiscoverable ) +{ + SAL_INFO( "sdremote.bluetooth", "setDiscoverable to " << bDiscoverable ); + + if (pAdapter->maInterface == "org.bluez.Adapter") // Bluez 4 + { + bool bPowered = false; + if( !getBluez4BooleanProperty( pConnection, pAdapter, "Powered", &bPowered ) || !bPowered ) + return; // nothing to do + + DBusMessage *pMsg; + DBusMessageIter it, varIt; + + // set timeout to zero + pMsg = pAdapter->getMethodCall( "SetProperty" ); + dbus_message_iter_init_append( pMsg, &it ); + const char *pTimeoutStr = "DiscoverableTimeout"; + dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pTimeoutStr ); + dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT, + DBUS_TYPE_UINT32_AS_STRING, &varIt ); + dbus_uint32_t nTimeout = 0; + dbus_message_iter_append_basic( &varIt, DBUS_TYPE_UINT32, &nTimeout ); + dbus_message_iter_close_container( &it, &varIt ); + dbus_connection_send( pConnection, pMsg, nullptr ); // async send - why not ? + dbus_message_unref( pMsg ); + + // set discoverable value + pMsg = pAdapter->getMethodCall( "SetProperty" ); + dbus_message_iter_init_append( pMsg, &it ); + const char *pDiscoverableStr = "Discoverable"; + dbus_message_iter_append_basic( &it, DBUS_TYPE_STRING, &pDiscoverableStr ); + dbus_message_iter_open_container( &it, DBUS_TYPE_VARIANT, + DBUS_TYPE_BOOLEAN_AS_STRING, &varIt ); + dbus_bool_t bValue = bDiscoverable; + dbus_message_iter_append_basic( &varIt, DBUS_TYPE_BOOLEAN, &bValue ); + dbus_message_iter_close_container( &it, &varIt ); // async send - why not ? + dbus_connection_send( pConnection, pMsg, nullptr ); + dbus_message_unref( pMsg ); + } + else if (pAdapter->maInterface == "org.bluez.Adapter1") // Bluez 5 + { + setDBusBooleanProperty(pConnection, pAdapter, "Discoverable", bDiscoverable ); + } +} + +static std::unique_ptr +registerWithDefaultAdapter( DBusConnection *pConnection ) +{ + std::unique_ptr pService(bluez4GetDefaultService( pConnection )); + if( pService ) + { + if( !bluez4RegisterServiceRecord( pConnection, pService.get(), + bluetooth_service_record ) ) + { + return nullptr; + } + } + + return pService; +} + +static void ProfileUnregisterFunction +(DBusConnection *, void *) +{ + // We specifically don't need to do anything here. +} + +static DBusHandlerResult ProfileMessageFunction +(DBusConnection *pConnection, DBusMessage *pMessage, void *user_data) +{ + SAL_INFO("sdremote.bluetooth", "ProfileMessageFunction||" << dbus_message_get_interface(pMessage) << "||" << dbus_message_get_member(pMessage)); + + if (dbus_message_get_interface(pMessage) == std::string_view("org.bluez.Profile1")) + { + if (dbus_message_get_member(pMessage) == std::string_view("Release")) + { + return DBUS_HANDLER_RESULT_HANDLED; + } + else if (dbus_message_get_member(pMessage) == std::string_view("NewConnection")) + { + if (!dbus_message_has_signature(pMessage, "oha{sv}")) + { + SAL_WARN("sdremote.bluetooth", "wrong signature for NewConnection"); + } + + DBusMessageIter it; + if (!dbus_message_iter_init(pMessage, &it)) + SAL_WARN( "sdremote.bluetooth", "error init dbus" ); + else + { + char* pPath; + dbus_message_iter_get_basic(&it, &pPath); + SAL_INFO("sdremote.bluetooth", "Adapter path:" << pPath); + + if (!dbus_message_iter_next(&it)) + SAL_WARN("sdremote.bluetooth", "not enough parameters passed"); + + // DBUS_TYPE_UNIX_FD == 'h' -- doesn't exist in older versions + // of dbus (< 1.3?) hence defined manually for now + if ('h' == dbus_message_iter_get_arg_type(&it)) + { + + int nDescriptor; + dbus_message_iter_get_basic(&it, &nDescriptor); + std::vector* pCommunicators = static_cast*>(user_data); + + // Bluez gives us non-blocking sockets, but our code relies + // on blocking behaviour. + (void)fcntl(nDescriptor, F_SETFL, fcntl(nDescriptor, F_GETFL) & ~O_NONBLOCK); + + SAL_INFO( "sdremote.bluetooth", "connection accepted " << nDescriptor); + Communicator* pCommunicator = new Communicator( std::make_unique( nDescriptor ) ); + pCommunicators->push_back( pCommunicator ); + pCommunicator->launch(); + } + + // For some reason an (empty?) reply is expected. + DBusMessage* pRet = dbus_message_new_method_return(pMessage); + dbus_connection_send(pConnection, pRet, nullptr); + dbus_message_unref(pRet); + + // We could read the remote profile version and features here + // (i.e. they are provided as part of the DBusMessage), + // however for us they are irrelevant (as our protocol handles + // equivalent functionality independently of whether we're on + // bluetooth or normal network connection). + return DBUS_HANDLER_RESULT_HANDLED; + } + } + else if (dbus_message_get_member(pMessage) == std::string_view("RequestDisconnection")) + { + return DBUS_HANDLER_RESULT_HANDLED; + } + } + SAL_WARN("sdremote.bluetooth", "Couldn't handle message correctly."); + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + +} + +static void +setupBluez5Profile1(DBusConnection* pConnection, std::vector* pCommunicators) +{ + bool bErr; + + SAL_INFO("sdremote.bluetooth", "Attempting to register our org.bluez.Profile1"); + static DBusObjectPathVTable aVTable; + aVTable.unregister_function = ProfileUnregisterFunction; + aVTable.message_function = ProfileMessageFunction; + + // dbus_connection_try_register_object_path could be used but only exists for + // dbus >= 1.2 -- we really shouldn't be trying this twice in any case. + // (dbus_connection_try_register_object_path also returns an error with more + // information which could be useful for debugging purposes.) + bErr = !dbus_connection_register_object_path(pConnection, "/org/libreoffice/bluez/profile1", &aVTable, pCommunicators); + + if (bErr) + { + SAL_WARN("sdremote.bluetooth", "Failed to register Bluez 5 Profile1 callback, bluetooth won't work."); + } + + dbus_connection_flush( pConnection ); +} + +static void +unregisterBluez5Profile(DBusConnection* pConnection) +{ + DBusMessage* pMsg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.ProfileManager1", "UnregisterProfile"); + DBusMessageIter it; + dbus_message_iter_init_append(pMsg, &it); + + const char *pPath = "/org/libreoffice/bluez/profile1"; + dbus_message_iter_append_basic(&it, DBUS_TYPE_OBJECT_PATH, &pPath); + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + if (pMsg) + dbus_message_unref(pMsg); + + dbus_connection_unregister_object_path( pConnection, "/org/libreoffice/bluez/profile1"); + + dbus_connection_flush(pConnection); +} + +static bool +registerBluez5Profile(DBusConnection* pConnection, std::vector* pCommunicators) +{ + setupBluez5Profile1(pConnection, pCommunicators); + + DBusMessage *pMsg; + DBusMessageIter it; + + pMsg = dbus_message_new_method_call("org.bluez", "/org/bluez", + "org.bluez.ProfileManager1", "RegisterProfile"); + dbus_message_iter_init_append(pMsg, &it); + + const char *pPath = "/org/libreoffice/bluez/profile1"; + dbus_message_iter_append_basic(&it, DBUS_TYPE_OBJECT_PATH, &pPath); + const char *pUUID = "spp"; // Bluez translates this to 0x1101 for spp + dbus_message_iter_append_basic(&it, DBUS_TYPE_STRING, &pUUID); + + DBusMessageIter aOptionsIter; + dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "{sv}", &aOptionsIter); + + DBusMessageIter aEntry; + + { + dbus_message_iter_open_container(&aOptionsIter, DBUS_TYPE_DICT_ENTRY, nullptr, &aEntry); + + const char *pString = "Name"; + dbus_message_iter_append_basic(&aEntry, DBUS_TYPE_STRING, &pString); + + const char *pValue = "LibreOffice Impress Remote"; + DBusMessageIter aValue; + dbus_message_iter_open_container(&aEntry, DBUS_TYPE_VARIANT, "s", &aValue); + dbus_message_iter_append_basic(&aValue, DBUS_TYPE_STRING, &pValue); + dbus_message_iter_close_container(&aEntry, &aValue); + dbus_message_iter_close_container(&aOptionsIter, &aEntry); + } + + dbus_message_iter_close_container(&it, &aOptionsIter); + + // Other properties that we could set (but don't, since they appear + // to be useless for us): + // "Service": "0x1101" (not needed, but we used to have it in the manually defined profile). + // "Role": setting this to "server" breaks things, although we think we're a server? + // "Channel": seems to be dealt with automatically (but we used to use 5 in the manual profile). + + bool bSuccess = true; + + pMsg = sendUnrefAndWaitForReply( pConnection, pMsg ); + + DBusError aError; + dbus_error_init(&aError); + if (pMsg && dbus_set_error_from_message( &aError, pMsg )) + { + bSuccess = false; + SAL_WARN("sdremote.bluetooth", + "Failed to register our Profile1 with bluez ProfileManager " + << (aError.message ? aError.message : "")); + } + + dbus_error_free(&aError); + if (pMsg) + dbus_message_unref(pMsg); + + dbus_connection_flush(pConnection); + + return bSuccess; +} + +#endif // LINUX_BLUETOOTH + +BluetoothServer::BluetoothServer( std::vector* pCommunicators ) + : meWasDiscoverable( UNKNOWN ), + mpCommunicators( pCommunicators ) +{ +#ifdef LINUX_BLUETOOTH + // D-Bus requires the following in order to be thread-safe (and we + // potentially access D-Bus from different threads in different places of + // the code base): + if (!dbus_threads_init_default()) { + throw std::bad_alloc(); + } + + mpImpl.reset(new BluetoothServer::Impl()); +#endif +} + +BluetoothServer::~BluetoothServer() +{ +} + +void BluetoothServer::ensureDiscoverable() +{ +#ifdef LINUX_BLUETOOTH + // Push it all across into our mainloop + if( !spServer ) + return; + GSource *pIdle = g_idle_source_new(); + g_source_set_callback( pIdle, ensureDiscoverable_cb, nullptr, nullptr ); + g_source_set_priority( pIdle, G_PRIORITY_DEFAULT ); + g_source_attach( pIdle, spServer->mpImpl->mpContext ); + g_source_unref( pIdle ); +#endif +} + +void BluetoothServer::restoreDiscoverable() +{ +#ifdef LINUX_BLUETOOTH + // Push it all across into our mainloop + if( !spServer ) + return; + GSource *pIdle = g_idle_source_new(); + g_source_set_callback( pIdle, restoreDiscoverable_cb, nullptr, nullptr ); + g_source_set_priority( pIdle, G_PRIORITY_DEFAULT_IDLE ); + g_source_attach( pIdle, spServer->mpImpl->mpContext ); + g_source_unref( pIdle ); +#endif +} + +void BluetoothServer::doEnsureDiscoverable() +{ +#ifdef LINUX_BLUETOOTH + if (!spServer->mpImpl->mpConnection || + spServer->meWasDiscoverable != UNKNOWN ) + return; + + // Find out if we are discoverable already ... + std::unique_ptr pAdapter = spServer->mpImpl->getAdapter(); + if( !pAdapter ) + return; + + bool bDiscoverable = getDiscoverable(spServer->mpImpl->mpConnection, pAdapter.get() ); + + spServer->meWasDiscoverable = bDiscoverable ? DISCOVERABLE : NOT_DISCOVERABLE; + if( !bDiscoverable ) + setDiscoverable( spServer->mpImpl->mpConnection, pAdapter.get(), true ); +#endif +} + +void BluetoothServer::doRestoreDiscoverable() +{ + if( spServer->meWasDiscoverable == NOT_DISCOVERABLE ) + { +#ifdef LINUX_BLUETOOTH + std::unique_ptr pAdapter = spServer->mpImpl->getAdapter(); + if( !pAdapter ) + return; + setDiscoverable( spServer->mpImpl->mpConnection, pAdapter.get(), false ); +#endif + } + spServer->meWasDiscoverable = UNKNOWN; +} + +// We have to have all our clients shut otherwise we can't +// re-bind to the same port number it appears. +void BluetoothServer::cleanupCommunicators() +{ + for (auto& rpCommunicator : *mpCommunicators) + rpCommunicator->forceClose(); + // the hope is that all the threads then terminate cleanly and + // clean themselves up. +} + +void SAL_CALL BluetoothServer::run() +{ + SAL_INFO( "sdremote.bluetooth", "BluetoothServer::run called" ); + osl::Thread::setName("BluetoothServer"); +#ifdef LINUX_BLUETOOTH + DBusConnection *pConnection = dbusConnectToNameOnBus(); + if( !pConnection ) + return; + + // For either implementation we need to poll the dbus fd + int fd = -1; + GPollFD aDBusFD; + if( dbus_connection_get_unix_fd( pConnection, &fd ) && fd >= 0 ) + { + aDBusFD.fd = fd; + aDBusFD.events = G_IO_IN | G_IO_PRI; + g_main_context_add_poll( mpImpl->mpContext, &aDBusFD, G_PRIORITY_DEFAULT ); + } + else + SAL_WARN( "sdremote.bluetooth", "failed to poll for incoming dbus signals" ); + + if (isBluez5Available(pConnection)) + { + SAL_INFO("sdremote.bluetooth", "Using Bluez 5"); + registerBluez5Profile(pConnection, mpCommunicators); + mpImpl->mpConnection = pConnection; + mpImpl->maBluezVersion = Impl::BluezVersion::BLUEZ5; + + // We don't need to listen to adapter changes anymore -- profile + // registration is done globally for the entirety of bluez, so we only + // need adapters when setting discoverability, which can be done + // dynamically without the need to listen for changes. + + // TODO: exit on SD deinit + // Probably best to do that in SdModule::~SdModule? + while (true) + { + aDBusFD.revents = 0; + g_main_context_iteration( mpImpl->mpContext, true ); + if( aDBusFD.revents ) + { + dbus_connection_read_write( pConnection, 0 ); + while (DBUS_DISPATCH_DATA_REMAINS == dbus_connection_get_dispatch_status( pConnection )) + dbus_connection_dispatch( pConnection ); + } + if ((false)) break; + // silence Clang -Wunreachable-code after loop (TODO: proper + // fix?) + } + unregisterBluez5Profile( pConnection ); + g_main_context_unref( mpImpl->mpContext ); + mpImpl->mpConnection = nullptr; + mpImpl->mpContext = nullptr; + return; + } + + // Otherwise we could be on Bluez 4 and continue as usual. + mpImpl->maBluezVersion = Impl::BluezVersion::BLUEZ4; + + // Try to setup the default adapter, otherwise wait for add/remove signal + mpImpl->mpService = registerWithDefaultAdapter( pConnection ); + // listen for connection state and power changes - we need to close + // and re-create our socket code on suspend / resume, enable/disable + DBusError aError; + dbus_error_init( &aError ); + dbus_bus_add_match( pConnection, "type='signal',interface='org.bluez.Manager'", &aError ); + dbus_connection_flush( pConnection ); + + // Try to setup the default adapter, otherwise wait for add/remove signal + mpImpl->mpService = registerWithDefaultAdapter( pConnection ); + + // poll on our bluetooth socket - if we can. + GPollFD aSocketFD; + if( mpImpl->mpService ) + bluezCreateAttachListeningSocket( mpImpl->mpContext, &aSocketFD ); + + mpImpl->mpConnection = pConnection; + + while( true ) + { + aDBusFD.revents = 0; + aSocketFD.revents = 0; + g_main_context_iteration( mpImpl->mpContext, true ); + + SAL_INFO( "sdremote.bluetooth", "main-loop spin " + << aDBusFD.revents << " " << aSocketFD.revents ); + if( aDBusFD.revents ) + { + dbus_connection_read_write( pConnection, 0 ); + DBusMessage *pMsg = dbus_connection_pop_message( pConnection ); + if( pMsg ) + { + if( dbus_message_is_signal( pMsg, "org.bluez.Manager", "AdapterRemoved" ) ) + { + SAL_WARN( "sdremote.bluetooth", "lost adapter - cleaning up sockets" ); + bluezDetachCloseSocket( mpImpl->mpContext, &aSocketFD ); + cleanupCommunicators(); + } + else if( dbus_message_is_signal( pMsg, "org.bluez.Manager", "AdapterAdded" ) || + dbus_message_is_signal( pMsg, "org.bluez.Manager", "DefaultAdapterChanged" ) ) + { + SAL_WARN( "sdremote.bluetooth", "gained adapter - re-generating sockets" ); + bluezDetachCloseSocket( mpImpl->mpContext, &aSocketFD ); + cleanupCommunicators(); + mpImpl->mpService = registerWithDefaultAdapter( pConnection ); + if( mpImpl->mpService ) + bluezCreateAttachListeningSocket( mpImpl->mpContext, &aSocketFD ); + } + else + SAL_INFO( "sdremote.bluetooth", "unknown incoming dbus message, " + " type: " << dbus_message_get_type( pMsg ) + << " path: '" << dbus_message_get_path( pMsg ) + << "' interface: '" << dbus_message_get_interface( pMsg ) + << "' member: '" << dbus_message_get_member( pMsg ) ); + } + dbus_message_unref( pMsg ); + } + + if( aSocketFD.revents ) + { + sockaddr_rc aRemoteAddr; + socklen_t aRemoteAddrLen = sizeof(aRemoteAddr); + + SAL_INFO( "sdremote.bluetooth", "performing accept" ); + int nClient = accept( aSocketFD.fd, reinterpret_cast(&aRemoteAddr), &aRemoteAddrLen); + if ( nClient < 0 && errno != EAGAIN ) + { + SAL_WARN( "sdremote.bluetooth", "accept failed with errno " << errno ); + } else { + SAL_INFO( "sdremote.bluetooth", "connection accepted " << nClient ); + Communicator* pCommunicator = new Communicator( std::make_unique( nClient ) ); + mpCommunicators->push_back( pCommunicator ); + pCommunicator->launch(); + } + } + if ((false)) break; + // silence Clang -Wunreachable-code after loop (TODO: proper fix?) + } + + unregisterBluez5Profile( pConnection ); + g_main_context_unref( mpImpl->mpContext ); + mpImpl->mpConnection = nullptr; + mpImpl->mpContext = nullptr; + +#elif defined(_WIN32) + WORD wVersionRequested; + WSADATA wsaData; + + wVersionRequested = MAKEWORD(2, 2); + + if ( WSAStartup(wVersionRequested, &wsaData) ) + { + return; // winsock dll couldn't be loaded + } + + int aSocket = socket( AF_BTH, SOCK_STREAM, BTHPROTO_RFCOMM ); + if ( !aSocket ) + { + WSACleanup(); + return; + } + SOCKADDR_BTH aAddr; + aAddr.addressFamily = AF_BTH; + aAddr.btAddr = 0; + aAddr.serviceClassId = GUID_NULL; + aAddr.port = BT_PORT_ANY; // Select any free socket. + if ( bind( aSocket, reinterpret_cast(&aAddr), sizeof(aAddr) ) == SOCKET_ERROR ) + { + closesocket( aSocket ); + WSACleanup(); + return; + } + + SOCKADDR_BTH aName; + int aNameSize = sizeof(aName); + getsockname( aSocket, reinterpret_cast(&aName), &aNameSize ); // Retrieve the local address and port + + CSADDR_INFO aAddrInfo = {}; + aAddrInfo.LocalAddr.lpSockaddr = reinterpret_cast(&aName); + aAddrInfo.LocalAddr.iSockaddrLength = sizeof( SOCKADDR_BTH ); + aAddrInfo.iSocketType = SOCK_STREAM; + aAddrInfo.iProtocol = BTHPROTO_RFCOMM; + + // To be used for setting a custom UUID once available. +// GUID uuid; +// uuid.Data1 = 0x00001101; +// memset( &uuid, 0x1000 + UUID*2^96, sizeof( GUID ) ); +// uuid.Data2 = 0; +// uuid.Data3 = 0x1000; +// ULONGLONG aData4 = 0x800000805F9B34FB; +// memcpy( uuid.Data4, &aData4, sizeof(uuid.Data4) ); + + WSAQUERYSETW aRecord = {}; + aRecord.dwSize = sizeof(aRecord); + aRecord.lpszServiceInstanceName = const_cast( + L"LibreOffice Impress Remote Control"); + aRecord.lpszComment = const_cast( + L"Remote control of presentations over bluetooth."); + aRecord.lpServiceClassId = const_cast(&SerialPortServiceClass_UUID); + aRecord.dwNameSpace = NS_BTH; + aRecord.dwNumberOfCsAddrs = 1; + aRecord.lpcsaBuffer = &aAddrInfo; + if (WSASetServiceW( &aRecord, RNRSERVICE_REGISTER, 0 ) == SOCKET_ERROR) + { + closesocket( aSocket ); + WSACleanup(); + return; + } + + if ( listen( aSocket, 1 ) == SOCKET_ERROR ) + { + closesocket( aSocket ); + WSACleanup(); + return; + } + + SOCKADDR_BTH aRemoteAddr; + int aRemoteAddrLen = sizeof(aRemoteAddr); + while ( true ) + { + SOCKET socket; + if ( (socket = accept(aSocket, reinterpret_cast(&aRemoteAddr), &aRemoteAddrLen)) == INVALID_SOCKET ) + { + closesocket( aSocket ); + WSACleanup(); + return; + } else { + Communicator* pCommunicator = new Communicator( std::make_unique( socket) ); + mpCommunicators->push_back( pCommunicator ); + pCommunicator->launch(); + } + } + +#elif defined(MACOSX) + // Build up dictionary at run-time instead of bothering with a + // .plist file, using the Objective-C API + + // Compare to BluetoothServiceRecord.hxx + + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + + NSDictionary *dict = + [NSDictionary dictionaryWithObjectsAndKeys: + + // Service class ID list + [NSArray arrayWithObject: + [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassSerialPort]], + @"0001 - ServiceClassIDList", + + // Protocol descriptor list + [NSArray arrayWithObjects: + [NSArray arrayWithObject: [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16L2CAP]], + [NSArray arrayWithObjects: + [IOBluetoothSDPUUID uuid16: kBluetoothL2CAPPSMRFCOMM], + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 1], + @"DataElementSize", + [NSNumber numberWithInt: 1], + @"DataElementType", + [NSNumber numberWithInt: 5], // RFCOMM port number, will be replaced if necessary automatically + @"DataElementValue", + nil], + nil], + nil], + @"0004 - Protocol descriptor list", + + // Browse group list + [NSArray arrayWithObject: + [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassPublicBrowseGroup]], + @"0005 - BrowseGroupList", + + // Language base attribute ID list + [NSArray arrayWithObjects: + [NSData dataWithBytes: "en" length: 2], + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 2], + @"DataElementSize", + [NSNumber numberWithInt: 1], + @"DataElementType", + [NSNumber numberWithInt: 0x006a], // encoding + @"DataElementValue", + nil], + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 2], + @"DataElementSize", + [NSNumber numberWithInt: 1], + @"DataElementType", + [NSNumber numberWithInt: 0x0100], // offset + @"DataElementValue", + nil], + nil], + @"0006 - LanguageBaseAttributeIDList", + + // Bluetooth profile descriptor list + [NSArray arrayWithObject: + [NSArray arrayWithObjects: + [IOBluetoothSDPUUID uuid16: kBluetoothSDPUUID16ServiceClassSerialPort], + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInt: 2], + @"DataElementSize", + [NSNumber numberWithInt: 1], + @"DataElementType", + [NSNumber numberWithInt: 0x0100], // version number ? + @"DataElementValue", + nil], + nil]], + @"0009 - BluetoothProfileDescriptorList", + + // Attributes pointed to by the LanguageBaseAttributeIDList + @"LibreOffice Impress Remote Control", + @"0100 - ServiceName", + @"The Document Foundation", + @"0102 - ProviderName", + nil]; + + // Create service + IOBluetoothSDPServiceRecordRef serviceRecordRef; + SAL_WNODEPRECATED_DECLARATIONS_PUSH //TODO: 10.9 IOBluetoothAddServiceDict + IOReturn rc = IOBluetoothAddServiceDict(reinterpret_cast(dict), &serviceRecordRef); + SAL_WNODEPRECATED_DECLARATIONS_POP + + SAL_INFO("sdremote.bluetooth", "IOBluetoothAddServiceDict returned " << rc); + + if (rc == kIOReturnSuccess) + { + IOBluetoothSDPServiceRecord *serviceRecord = + [IOBluetoothSDPServiceRecord withSDPServiceRecordRef: serviceRecordRef]; + + BluetoothRFCOMMChannelID channelID; + [serviceRecord getRFCOMMChannelID: &channelID]; + + BluetoothSDPServiceRecordHandle serviceRecordHandle; + [serviceRecord getServiceRecordHandle: &serviceRecordHandle]; + + // Register callback for incoming connections + IOBluetoothRegisterForFilteredRFCOMMChannelOpenNotifications( + incomingCallback, + this, + channelID, + kIOBluetoothUserNotificationChannelDirectionIncoming); + + [serviceRecord release]; + } + + [pool release]; + + (void) mpCommunicators; +#else + (void) mpCommunicators; // avoid warnings about unused member +#endif +} + +BluetoothServer *sd::BluetoothServer::spServer = nullptr; + +void BluetoothServer::setup( std::vector* pCommunicators ) +{ + if (spServer) + return; + + spServer = new BluetoothServer( pCommunicators ); + spServer->create(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/BluetoothServer.hxx b/sd/source/ui/remotecontrol/BluetoothServer.hxx new file mode 100644 index 000000000..9e20bfa51 --- /dev/null +++ b/sd/source/ui/remotecontrol/BluetoothServer.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include +#include + +#include + +#if (defined(LINUX) && !defined(__FreeBSD_kernel__)) && ENABLE_DBUS && DBUS_HAVE_GLIB +# define LINUX_BLUETOOTH +#endif + +namespace sd +{ + class Communicator; + + class BluetoothServer: + public osl::Thread + { + public: + static void setup( std::vector* pCommunicators ); + + /// ensure that Bluetooth discoverability is on + static void ensureDiscoverable(); + /// restore the state of discoverability from before ensureDiscoverable + static void restoreDiscoverable(); + + // called by C / idle callbacks + static void doEnsureDiscoverable(); + static void doRestoreDiscoverable(); + +#if defined(MACOSX) + void addCommunicator( Communicator* pCommunicator ); +#endif + private: + explicit BluetoothServer( std::vector* pCommunicators ); + virtual ~BluetoothServer() override; + + enum { UNKNOWN, DISCOVERABLE, NOT_DISCOVERABLE } meWasDiscoverable; + static BluetoothServer *spServer; + +#ifdef LINUX_BLUETOOTH + struct Impl; + std::unique_ptr mpImpl; +#endif + virtual void SAL_CALL run() override; + + void cleanupCommunicators(); + std::vector* mpCommunicators; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/BluetoothServer.mm b/sd/source/ui/remotecontrol/BluetoothServer.mm new file mode 100644 index 000000000..28288ff6f --- /dev/null +++ b/sd/source/ui/remotecontrol/BluetoothServer.mm @@ -0,0 +1 @@ +#include "BluetoothServer.cxx" \ No newline at end of file diff --git a/sd/source/ui/remotecontrol/BluetoothServiceRecord.hxx b/sd/source/ui/remotecontrol/BluetoothServiceRecord.hxx new file mode 100644 index 000000000..c1a00fb3b --- /dev/null +++ b/sd/source/ui/remotecontrol/BluetoothServiceRecord.hxx @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +// FIXME: look into sharing definitions across OS's (i.e. UUID and port ). +// Look into dynamically determining which ports are available. + +// SDP is a Service Description Protocol cf. +// http://developer.bluetooth.org/TechnologyOverview/Pages/DI.aspx +// This is an XML representation, an alternative would be a +// binary SDP record. + +// for numbers see: +// https://www.bluetooth.org/Technical/AssignedNumbers/service_discovery.htm + +const char * const bluetooth_service_record = + "" + "" + "" // Service class ID list + "" + "" // an assigned service class meaning: 'serial port' + // we could add our own 'LibreOffice remote' service + // class here too in future ... + "" + "" + "" // Protocol Descriptor list + "" + "" + "" // L2CAP Protocol descriptor + "" + "" + "" // enumeration value of RFCOMM protocol + "" // RFCOMM port number + "" + "" + "" + "" // Browse Group List + "" + "" // public browse class + "" + "" + "" // Language Base Attribute ID List + "" + "" // code_ISO639 + "" // encoding 0x6a + "" // base_offset ie. points to below => + "" + "" + "" // Bluetooth Profile Descriptor List + "" + "" + "" // 'serial port' UUID as above + ""// version number 1.0 ? + "" + "" + "" + // Attribute identifiers are pointed to by the Language Base Attribute ID List + // id+0 = ServiceName, id+1 = ServiceDescription, id+2=ProviderName + "" + "" + "" + "" + "" + "" + "" + ; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx b/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx new file mode 100644 index 000000000..64ad5eb8d --- /dev/null +++ b/sd/source/ui/remotecontrol/BufferedStreamSocket.cxx @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "BufferedStreamSocket.hxx" + +#include +#include +#include + +#ifdef _WIN32 + // LO vs WinAPI conflict + #undef WB_LEFT + #undef WB_RIGHT + + #include +#else + #include + #include +#endif +using namespace sd; +using namespace osl; + +BufferedStreamSocket::BufferedStreamSocket( const osl::StreamSocket &aSocket ): + StreamSocket( aSocket ), + aRet( 0 ), + aRead( 0 ), + mSocket( 0 ), + usingCSocket( false ) +{ +} + +BufferedStreamSocket::BufferedStreamSocket( int aSocket ): + aRet( 0 ), + aRead( 0 ), + mSocket( aSocket ), + usingCSocket( true ) +{ +} + +BufferedStreamSocket::~BufferedStreamSocket() { + close(); +} + +void BufferedStreamSocket::getPeerAddr(osl::SocketAddr& rAddr) +{ + assert ( !usingCSocket ); + StreamSocket::getPeerAddr( rAddr ); +} + +sal_Int32 BufferedStreamSocket::write( const void* pBuffer, sal_uInt32 n ) +{ + if ( !usingCSocket ) + return StreamSocket::write( pBuffer, n ); + else + return ::send( + mSocket, +#if defined(_WIN32) + static_cast(pBuffer), +#else + pBuffer, +#endif + static_cast(n), 0 ); +} + +void BufferedStreamSocket::close() +{ + if( usingCSocket && mSocket != -1 ) + { +#ifdef _WIN32 + ::closesocket( mSocket ); +#else + ::close( mSocket ); +#endif + mSocket = -1; + } + else + ::osl::StreamSocket::close(); +} + +sal_Int32 BufferedStreamSocket::readLine( OString& aLine ) +{ + while ( true ) + { + // Process buffer first in case data already present. + std::vector::iterator aIt; + if ( (aIt = find( aBuffer.begin(), aBuffer.end(), '\n' )) + != aBuffer.end() ) + { + sal_uInt64 aLocation = aIt - aBuffer.begin(); + + aLine = OString( &(*aBuffer.begin()), aLocation ); + + aBuffer.erase( aBuffer.begin(), aIt + 1 ); // Also delete the empty line + aRead -= (aLocation + 1); + + SAL_INFO( "sdremote.bluetooth", "recv line '" << aLine << "'" ); + + return aLine.getLength() + 1; + } + + // Then try and receive if nothing present + aBuffer.resize( aRead + 100 ); + if ( !usingCSocket) + aRet = StreamSocket::recv( &aBuffer[aRead], 100 ); + else + aRet = ::recv( mSocket, &aBuffer[aRead], 100, 0 ); + + SAL_INFO( "sdremote.bluetooth", "recv " << aRet << " aBuffer len " << aBuffer.size() ); + if ( aRet <= 0 ) + { + return 0; + } + // Prevent buffer from growing massively large. + if ( aRead > MAX_LINE_LENGTH ) + { + aBuffer.clear(); + return 0; + } + aRead += aRet; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/BufferedStreamSocket.hxx b/sd/source/ui/remotecontrol/BufferedStreamSocket.hxx new file mode 100644 index 000000000..6abf7ec1b --- /dev/null +++ b/sd/source/ui/remotecontrol/BufferedStreamSocket.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include "IBluetoothSocket.hxx" +#include +#include + +#define MAX_LINE_LENGTH 20000 + +namespace sd +{ + + /** + * [A wrapper for an osl StreamSocket to allow reading lines.] + * + * Currently wraps either an osl StreamSocket or a standard c socket, + * allowing reading and writing for our purposes. Should eventually be + * returned to being a StreamSocket wrapper if/when Bluetooth is + * integrated into osl Sockets. + */ + class BufferedStreamSocket final : + public IBluetoothSocket, + private ::osl::StreamSocket + { + public: + /** + * Create a BufferedStreamSocket on top of an + * osl::StreamSocket. + */ + explicit BufferedStreamSocket( const osl::StreamSocket &aSocket ); + /** + * Create a BufferedStreamSocket on top of a POSIX or WinSock socket. + */ + explicit BufferedStreamSocket( int aSocket ); + BufferedStreamSocket( const BufferedStreamSocket &aSocket ); + + ~BufferedStreamSocket(); + + /** + * Blocks until a line is read. + * Returns whatever the last call of recv returned, i.e. 0 or less + * if there was a problem in communications. + */ + virtual sal_Int32 readLine( OString& aLine ) override; + + virtual sal_Int32 write( const void* pBuffer, sal_uInt32 n ) override; + + virtual void close() override; + + void getPeerAddr(osl::SocketAddr&); + private: + sal_Int32 aRet, aRead; + std::vector aBuffer; + int mSocket; + bool usingCSocket; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Communicator.cxx b/sd/source/ui/remotecontrol/Communicator.cxx new file mode 100644 index 000000000..59509ed3c --- /dev/null +++ b/sd/source/ui/remotecontrol/Communicator.cxx @@ -0,0 +1,154 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Communicator.hxx" +#include "IBluetoothSocket.hxx" +#include "Listener.hxx" +#include "Receiver.hxx" +#include "Transmitter.hxx" +#include + +using namespace sd; +using namespace com::sun::star; +using namespace osl; + +Communicator::Communicator( std::unique_ptr pSocket ): + Thread( "CommunicatorThread" ), + mpSocket( std::move(pSocket) ) +{ +} + +Communicator::~Communicator() +{ +} + +/// Close the underlying socket from another thread to force +/// an early exit / termination +void Communicator::forceClose() +{ + if( mpSocket ) + mpSocket->close(); +} + +// Run as a thread +void Communicator::execute() +{ + pTransmitter.reset( new Transmitter( mpSocket.get() ) ); + pTransmitter->create(); + + pTransmitter->addMessage( "LO_SERVER_SERVER_PAIRED\n\n", + Transmitter::PRIORITY_HIGH ); + + pTransmitter->addMessage( "LO_SERVER_INFO\n" LIBO_VERSION_DOTTED "\n\n", + Transmitter::PRIORITY_HIGH ); + + Receiver aReceiver( pTransmitter.get() ); + try { + uno::Reference< frame::XDesktop2 > xFramesSupplier = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); + uno::Reference< frame::XFrame > xFrame = xFramesSupplier->getActiveFrame(); + + uno::Reference xPS; + if( xFrame.is() ) + xPS.set( xFrame->getController()->getModel(), uno::UNO_QUERY ); + uno::Reference xPresentation; + if( xPS.is() ) + xPresentation.set( xPS->getPresentation(), uno::UNO_QUERY ); + if ( xPresentation.is() && xPresentation->isRunning() ) + { + presentationStarted( xPresentation->getController() ); + OString aBuffer = + "slideshow_info\n" + + OUStringToOString( ::comphelper::DocumentInfo::getDocumentTitle( xFrame->getController()->getModel() ), RTL_TEXTENCODING_UTF8 ) + + "\n\n"; + + pTransmitter->addMessage( aBuffer.getStr(), Transmitter::PRIORITY_LOW ); + } + else + { + pTransmitter->addMessage( "slideshow_finished\n\n", + Transmitter::PRIORITY_HIGH ); + } + } + catch (uno::RuntimeException &) + { + } + + sal_uInt64 aRet; + std::vector aCommand; + while ( true ) + { + OString aLine; + aRet = mpSocket->readLine( aLine ); + if ( aRet == 0 ) + { + break; // I.e. transmission finished. + } + if ( aLine.getLength() ) + { + aCommand.push_back( aLine ); + } + else + { + aReceiver.pushCommand( aCommand ); + aCommand.clear(); + } + } + + SAL_INFO ("sdremote", "Exiting transmission loop"); + + disposeListener(); + + pTransmitter->notifyFinished(); + pTransmitter->join(); + pTransmitter = nullptr; + + mpSocket->close(); + mpSocket.reset(); + + RemoteServer::removeCommunicator( this ); +} + +void Communicator::informListenerDestroyed() +{ + if ( pTransmitter ) + pTransmitter->addMessage( "slideshow_finished\n\n", + Transmitter::PRIORITY_HIGH ); +} + +void Communicator::presentationStarted( const css::uno::Reference< + css::presentation::XSlideShowController > &rController ) +{ + if ( pTransmitter ) + { + mListener.set( new Listener( this, pTransmitter.get() ) ); + mListener->init( rController ); + } +} + +void Communicator::disposeListener() +{ + if ( mListener.is() ) + { + mListener->dispose(); + mListener = nullptr; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Communicator.hxx b/sd/source/ui/remotecontrol/Communicator.hxx new file mode 100644 index 000000000..f8f23c58c --- /dev/null +++ b/sd/source/ui/remotecontrol/Communicator.hxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include + +#include +#include + +namespace com::sun::star::uno { template class Reference; } +namespace com::sun::star::presentation { class XSlideShowController; } +namespace sd { struct IBluetoothSocket; } + +namespace sd +{ + + class Transmitter; + class Listener; + + /** Class used for communication with one single client, dealing with all + * tasks specific to this client. + * + * Needs to be created, then started using launch(), disposes itself. + */ + class Communicator : public salhelper::Thread + { + public: + explicit Communicator( std::unique_ptr pSocket ); + virtual ~Communicator() override; + + void presentationStarted( const css::uno::Reference< + css::presentation::XSlideShowController > &rController ); + void informListenerDestroyed(); + void disposeListener(); + void forceClose(); + + private: + void execute() override; + std::unique_ptr mpSocket; + + std::unique_ptr pTransmitter; + rtl::Reference mListener; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/DiscoveryService.cxx b/sd/source/ui/remotecontrol/DiscoveryService.cxx new file mode 100644 index 000000000..bdd0b51c8 --- /dev/null +++ b/sd/source/ui/remotecontrol/DiscoveryService.cxx @@ -0,0 +1,186 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include + +#include +#include +#include + +#include "DiscoveryService.hxx" +#include "ZeroconfService.hxx" + +#ifdef _WIN32 + // LO vs WinAPI conflict + #undef WB_LEFT + #undef WB_RIGHT + + #include + #include + + #include "WINNetworkService.hxx" + typedef int socklen_t; +#else + #include + #include + #include +#endif + +#ifdef MACOSX + #include + #include + #import + #include + #import "OSXNetworkService.hxx" +#endif + +#if HAVE_FEATURE_AVAHI + #include "AvahiNetworkService.hxx" +#endif + +using namespace osl; +using namespace sd; + +DiscoveryService::DiscoveryService() + : mSocket(-1) + , zService(nullptr) +{ +} + +DiscoveryService::~DiscoveryService() +{ + if (mSocket != -1) + { +#ifdef _WIN32 + closesocket( mSocket ); +#else + close( mSocket ); +#endif + } + + if (zService) + zService->clear(); +} + +void DiscoveryService::setupSockets() +{ + +#ifdef MACOSX + // Bonjour for OSX + zService = new OSXNetworkService(); + zService->setup(); +#endif + +#if HAVE_FEATURE_AVAHI + // Avahi for Linux + char hostname[1024]; + hostname[1023] = '\0'; + gethostname(hostname, 1023); + + zService = new AvahiNetworkService(hostname); + zService->setup(); +#endif + +#ifdef _WIN32 + zService = new WINNetworkService(); + zService->setup(); +#endif + + // Old implementation for backward compatibility matter + mSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); + if (mSocket == -1) + { + SAL_WARN("sd", "DiscoveryService: socket failed: " << errno); + return; // would be better to throw, but unsure if caller handles that + } + + sockaddr_in aAddr = {}; + aAddr.sin_family = AF_INET; + aAddr.sin_addr.s_addr = htonl(INADDR_ANY); + aAddr.sin_port = htons( PORT_DISCOVERY ); + + int rc = bind( mSocket, reinterpret_cast(&aAddr), sizeof(sockaddr_in) ); + + if (rc) + { + SAL_WARN("sd", "DiscoveryService: bind failed: " << errno); + return; // would be better to throw, but unsure if caller handles that + } + + struct ip_mreq multicastRequest; + + multicastRequest.imr_multiaddr.s_addr = htonl((239U << 24) | 1U); // 239.0.0.1 + multicastRequest.imr_interface.s_addr = htonl(INADDR_ANY); + + rc = setsockopt( mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + #ifdef _WIN32 + reinterpret_cast(&multicastRequest), + #else + &multicastRequest, + #endif + sizeof(multicastRequest)); + + if (rc) + { + SAL_WARN("sd", "DiscoveryService: setsockopt failed: " << errno); + return; // would be better to throw, but unsure if caller handles that + } +} + +void SAL_CALL DiscoveryService::run() +{ + osl::Thread::setName("DiscoveryService"); + + setupSockets(); + + // Kept for backward compatibility + while ( true ) + { + char aBuffer[BUFFER_SIZE] = {}; + sockaddr_in aAddr; + socklen_t aLen = sizeof( aAddr ); + if(recvfrom( mSocket, aBuffer, BUFFER_SIZE, 0, reinterpret_cast(&aAddr), &aLen ) > 0) + { + OString aString( aBuffer, strlen( "LOREMOTE_SEARCH" ) ); + if ( aString == "LOREMOTE_SEARCH" ) + { + OString aStringBuffer = "LOREMOTE_ADVERTISE\n" + + OUStringToOString(osl::SocketAddr::getLocalHostname(), RTL_TEXTENCODING_UTF8 ) + + "\n\n"; + if ( sendto( mSocket, aStringBuffer.getStr(), + aStringBuffer.getLength(), 0, reinterpret_cast(&aAddr), + sizeof(aAddr) ) <= 0 ) + { + // Write error or closed socket -- we are done. + return; + } + } + } + else + { + // Read error or closed socket -- we are done. + return; + } + } +} + +DiscoveryService *sd::DiscoveryService::spService = nullptr; + +void DiscoveryService::setup() +{ + if (spService) + return; + + spService = new DiscoveryService(); + spService->create(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/DiscoveryService.hxx b/sd/source/ui/remotecontrol/DiscoveryService.hxx new file mode 100644 index 000000000..4b235fe89 --- /dev/null +++ b/sd/source/ui/remotecontrol/DiscoveryService.hxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include + +namespace sd { class ZeroconfService; } + +namespace sd +{ + class DiscoveryService : public osl::Thread + { + public: + static void setup(); + + private: + DiscoveryService(); + virtual ~DiscoveryService() override; + + /** + * Networking related setup -- must be run within our own thread + * to prevent the application blocking (fdo#75328). + */ + void setupSockets(); + + static DiscoveryService *spService; + virtual void SAL_CALL run() override; + int mSocket; + + ZeroconfService * zService; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/DiscoveryService.mm b/sd/source/ui/remotecontrol/DiscoveryService.mm new file mode 100644 index 000000000..3cad7cdfb --- /dev/null +++ b/sd/source/ui/remotecontrol/DiscoveryService.mm @@ -0,0 +1 @@ +#include "DiscoveryService.cxx" \ No newline at end of file diff --git a/sd/source/ui/remotecontrol/IBluetoothSocket.hxx b/sd/source/ui/remotecontrol/IBluetoothSocket.hxx new file mode 100644 index 000000000..4b75a1e82 --- /dev/null +++ b/sd/source/ui/remotecontrol/IBluetoothSocket.hxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include + +namespace sd +{ + /** Interface for bluetooth data io + */ + struct IBluetoothSocket + { + IBluetoothSocket() = default; + virtual ~IBluetoothSocket() {} + IBluetoothSocket(const IBluetoothSocket&) = delete; + IBluetoothSocket& operator=(const IBluetoothSocket&) = delete; + + /** Blocks until a line is read. + + @return whatever the last call of recv returned, i.e. 0 or less + if there was a problem in communications. + */ + virtual sal_Int32 readLine(OString& aLine) = 0; + + /** Write a number of bytes + + @return number of bytes actually written + */ + virtual sal_Int32 write( const void* pBuffer, sal_uInt32 n ) = 0; + + virtual void close() {}; + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/ImagePreparer.cxx b/sd/source/ui/remotecontrol/ImagePreparer.cxx new file mode 100644 index 000000000..ba8d2c1f3 --- /dev/null +++ b/sd/source/ui/remotecontrol/ImagePreparer.cxx @@ -0,0 +1,255 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "ImagePreparer.hxx" +#include "Transmitter.hxx" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::sd; +using namespace ::osl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +ImagePreparer::ImagePreparer( + const uno::Reference& rxController, + Transmitter *aTransmitter ) + : Timer("sd ImagePreparer"), + xController( rxController ), + pTransmitter( aTransmitter ) +{ + SAL_INFO( "sdremote", "ImagePreparer - start" ); + SetTimeout( 50 ); + mnSendingSlide = 0; + Start(); +} + +ImagePreparer::~ImagePreparer() +{ + SAL_INFO( "sdremote", "ImagePreparer - stop" ); + Stop(); +} + +void ImagePreparer::Invoke() +{ + sal_uInt32 aSlides = xController->getSlideCount(); + SAL_INFO( "sdremote", "ImagePreparer " << xController->isRunning() << + " sending slide " << mnSendingSlide << " of " << aSlides ); + if ( xController->isRunning() && // not stopped/disposed of. + mnSendingSlide < aSlides ) + { + sendPreview( mnSendingSlide ); + sendNotes( mnSendingSlide ); + mnSendingSlide++; + Start(); + } + else + Stop(); +} + +void ImagePreparer::sendPreview( sal_uInt32 aSlideNumber ) +{ + sal_uInt64 aSize; + uno::Sequence aImageData = preparePreview( aSlideNumber, 320, 240, + aSize ); + if ( !xController->isRunning() ) + return; + + OUStringBuffer aStrBuffer; + ::comphelper::Base64::encode( aStrBuffer, aImageData ); + + OString aEncodedShortString = OUStringToOString( + aStrBuffer.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ); + + // Start the writing + OString aBuffer = "slide_preview\n" + + OString::number(aSlideNumber) + + "\n" + aEncodedShortString + "\n\n"; + pTransmitter->addMessage( aBuffer, + Transmitter::PRIORITY_LOW ); + +} + +uno::Sequence ImagePreparer::preparePreview( + sal_uInt32 aSlideNumber, sal_uInt32 aWidth, sal_uInt32 aHeight, + sal_uInt64 &rSize ) +{ + OUString aFileURL; + FileBase::createTempFile( nullptr, nullptr, &aFileURL ); + + uno::Reference< drawing::XGraphicExportFilter > xFilter = + drawing::GraphicExportFilter::create( ::comphelper::getProcessComponentContext() ); + + if ( !xController->isRunning() ) + return uno::Sequence(); + + uno::Reference< lang::XComponent > xSourceDoc( + xController->getSlideByIndex( aSlideNumber ), + uno::UNO_QUERY_THROW ); + + xFilter->setSourceDocument( xSourceDoc ); + + uno::Sequence< beans::PropertyValue > aFilterData{ + comphelper::makePropertyValue("PixelWidth", aWidth), + comphelper::makePropertyValue("PixelHeight", aHeight), + comphelper::makePropertyValue("ColorMode", sal_Int32(0)) // 0: Color, 1: B&W + }; + + uno::Sequence< beans::PropertyValue > aProps{ + comphelper::makePropertyValue("MediaType", OUString( "image/png" )), + comphelper::makePropertyValue("URL", aFileURL), + comphelper::makePropertyValue("FilterData", aFilterData) + }; + + xFilter->filter( aProps ); + + File aFile(aFileURL); + if (aFile.open(0) != osl::File::E_None) + return uno::Sequence(); + + sal_uInt64 aRead; + rSize = 0; + aFile.getSize( rSize ); + uno::Sequence aContents( rSize ); + + aFile.read( aContents.getArray(), rSize, aRead ); + if (aRead != rSize) + aContents.realloc(aRead); + + aFile.close(); + File::remove( aFileURL ); + return aContents; + +} + +void ImagePreparer::sendNotes( sal_uInt32 aSlideNumber ) +{ + + OString aNotes = prepareNotes( aSlideNumber ); + + if ( aNotes.isEmpty() ) + return; + + if ( !xController->isRunning() ) + return; + + // Start the writing + OString aBuffer = + "slide_notes\n" + + OString::number( static_cast(aSlideNumber) ) + + "\n" + "" + + aNotes + + "" + "\n\n"; + pTransmitter->addMessage( aBuffer, + Transmitter::PRIORITY_LOW ); +} + +// Code copied from sdremote/source/presenter/PresenterNotesView.cxx +OString ImagePreparer::prepareNotes( sal_uInt32 aSlideNumber ) +{ + OUStringBuffer aRet; + + if ( !xController->isRunning() ) + return ""; + + uno::Reference aNotesPage; + uno::Reference< drawing::XDrawPage > xSourceDoc( + xController->getSlideByIndex( aSlideNumber ), + uno::UNO_SET_THROW ); + uno::Reference xPresentationPage( + xSourceDoc, UNO_QUERY); + if (xPresentationPage.is()) + aNotesPage = xPresentationPage->getNotesPage(); + else + return ""; + + static constexpr OUStringLiteral sNotesShapeName ( + u"com.sun.star.presentation.NotesShape" ); + static constexpr OUStringLiteral sTextShapeName ( + u"com.sun.star.drawing.TextShape" ); + + if (aNotesPage.is()) + { + + // Iterate over all shapes and find the one that holds the text. + sal_Int32 nCount (aNotesPage->getCount()); + for (sal_Int32 nIndex=0; nIndex xServiceName ( + aNotesPage->getByIndex(nIndex), UNO_QUERY); + if (xServiceName.is() + && xServiceName->getServiceName() == sNotesShapeName) + { + uno::Reference xText (xServiceName, UNO_QUERY); + if (xText.is()) + { + aRet.append(xText->getString()); + aRet.append("
      "); + } + } + else + { + uno::Reference xShapeDescriptor ( + aNotesPage->getByIndex(nIndex), UNO_QUERY); + if (xShapeDescriptor.is()) + { + OUString sType (xShapeDescriptor->getShapeType()); + if (sType == sNotesShapeName || sType == sTextShapeName) + { + uno::Reference xText ( + aNotesPage->getByIndex(nIndex), UNO_QUERY); + if (xText.is()) + { + aRet.append(xText->getString()); + aRet.append("
      "); + } + } + } + } + } + } + // Replace all newlines with tags + for ( sal_Int32 i = 0; i < aRet.getLength(); i++ ) + { + if ( aRet[i] == '\n' ) + { + aRet[i]= '<'; + aRet.insert( i+1, "br/>" ); + } + } + return OUStringToOString( + aRet.makeStringAndClear(), RTL_TEXTENCODING_UTF8 ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/ImagePreparer.hxx b/sd/source/ui/remotecontrol/ImagePreparer.hxx new file mode 100644 index 000000000..146eba073 --- /dev/null +++ b/sd/source/ui/remotecontrol/ImagePreparer.hxx @@ -0,0 +1,45 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include + +namespace com::sun::star::presentation { class XSlideShowController; } +namespace sd { class Transmitter; } + +namespace sd +{ + +class ImagePreparer : private Timer +{ + sal_uInt32 mnSendingSlide; +public: + ImagePreparer( const + css::uno::Reference& + rxController, sd::Transmitter *aTransmitter ); + virtual ~ImagePreparer() override; + +private: + css::uno::Reference xController; + Transmitter *pTransmitter; + + virtual void Invoke() override; + + void sendPreview( sal_uInt32 aSlideNumber ); + css::uno::Sequence preparePreview( sal_uInt32 aSlideNumber, + sal_uInt32 aWidth, sal_uInt32 aHeight, sal_uInt64 &rSize ); + + void sendNotes( sal_uInt32 aSlideNumber ); + OString prepareNotes( sal_uInt32 aSlideNumber ); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Listener.cxx b/sd/source/ui/remotecontrol/Listener.cxx new file mode 100644 index 000000000..3753ed9b5 --- /dev/null +++ b/sd/source/ui/remotecontrol/Listener.cxx @@ -0,0 +1,133 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +#include "Communicator.hxx" +#include "Listener.hxx" +#include "ImagePreparer.hxx" +#include "Transmitter.hxx" + +#include + +using namespace sd; +using namespace ::com::sun::star::presentation; + +Listener::Listener( const ::rtl::Reference& rCommunicator, + sd::Transmitter *aTransmitter ): + mCommunicator( rCommunicator ), + pTransmitter( nullptr ) +{ + pTransmitter = aTransmitter; +} + +Listener::~Listener() +{ +} + +void Listener::init( const css::uno::Reference< css::presentation::XSlideShowController >& aController) +{ + if ( aController.is() ) + { + mController.set( aController ); + aController->addSlideShowListener( this ); + + sal_Int32 aSlides = aController->getSlideCount(); + sal_Int32 aCurrentSlide = aController->getCurrentSlideIndex(); + OString aBuffer = "slideshow_started\n" + + OString::number( aSlides ) + "\n" + + OString::number( aCurrentSlide ) + "\n\n"; + + pTransmitter->addMessage( aBuffer, + Transmitter::PRIORITY_HIGH ); + + { + SolarMutexGuard aGuard; + /* ImagePreparer* pPreparer = */ new ImagePreparer( aController, pTransmitter ); + } + } + else + { + SAL_INFO( "sdremote", "Listener::init but no controller - so no preview push queued" ); + } +} + +//----- XAnimationListener ---------------------------------------------------- + +void SAL_CALL Listener::beginEvent(const css::uno::Reference< + css::animations::XAnimationNode >& ) +{} + +void SAL_CALL Listener::endEvent( const css::uno::Reference< + css::animations::XAnimationNode >& ) +{} + +void SAL_CALL Listener::repeat( const css::uno::Reference< + css::animations::XAnimationNode >&, ::sal_Int32 ) +{} + +//----- XSlideShowListener ---------------------------------------------------- + +void SAL_CALL Listener::paused() +{ +} + +void SAL_CALL Listener::resumed() +{ +} + +void SAL_CALL Listener::slideEnded (sal_Bool) +{ +} + +void SAL_CALL Listener::hyperLinkClicked (const OUString &) +{ +} + +void SAL_CALL Listener::slideTransitionStarted() +{ + sal_Int32 aSlide = mController->getCurrentSlideIndex(); + + OString aBuilder = "slide_updated\n" + + OString::number( aSlide ) + + "\n\n"; + + if ( pTransmitter ) + { + pTransmitter->addMessage( aBuilder, + Transmitter::PRIORITY_HIGH ); + } +} + +void SAL_CALL Listener::slideTransitionEnded() +{ +} + +void SAL_CALL Listener::slideAnimationsEnded() +{ +} + +void Listener::disposing(std::unique_lock&) +{ + pTransmitter = nullptr; + if ( mController.is() ) + { + mController->removeSlideShowListener( this ); + mController = nullptr; + } + mCommunicator->informListenerDestroyed(); +} + +void SAL_CALL Listener::disposing ( + const css::lang::EventObject&) +{ + dispose(); +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Listener.hxx b/sd/source/ui/remotecontrol/Listener.hxx new file mode 100644 index 000000000..58d7483f6 --- /dev/null +++ b/sd/source/ui/remotecontrol/Listener.hxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include + +#include +#include +#include + +namespace com::sun::star::presentation { class XSlideShowController; } +namespace sd { class Communicator; } +namespace sd { class Transmitter; } + +namespace sd { +/** + * Slide show listener. This class can also be used for anything else that is + * specific to the lifetime of one slideshow while a client is connected. + */ +class Listener + : public comphelper::WeakComponentImplHelper< css::presentation::XSlideShowListener > +{ +public: + Listener( const ::rtl::Reference& rServer, sd::Transmitter *aTransmitter ); + virtual ~Listener() override; + void init( const css::uno::Reference< css::presentation::XSlideShowController >& aController ); + + // XAnimationListener + virtual void SAL_CALL beginEvent(const css::uno::Reference< + css::animations::XAnimationNode >& rNode ) override; + virtual void SAL_CALL endEvent( const css::uno::Reference< + css::animations::XAnimationNode >& rNode ) override; + virtual void SAL_CALL repeat( const css::uno::Reference< + css::animations::XAnimationNode >& rNode, ::sal_Int32 Repeat ) override; + + // XSlideShowListener + virtual void SAL_CALL paused( ) override; + virtual void SAL_CALL resumed( ) override; + virtual void SAL_CALL slideTransitionStarted( ) override; + virtual void SAL_CALL slideTransitionEnded( ) override; + virtual void SAL_CALL slideAnimationsEnded( ) override; + virtual void SAL_CALL slideEnded(sal_Bool bReverse) override; + virtual void SAL_CALL hyperLinkClicked( const OUString& hyperLink ) override; + + // XEventListener + virtual void disposing(std::unique_lock&) override; + virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override; + +private: + rtl::Reference mCommunicator; + sd::Transmitter *pTransmitter; + css::uno::Reference< css::presentation::XSlideShowController > mController; +}; +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXBluetooth.h b/sd/source/ui/remotecontrol/OSXBluetooth.h new file mode 100644 index 000000000..64f095f6c --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXBluetooth.h @@ -0,0 +1,30 @@ +/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#import + +#include "IBluetoothSocket.hxx" +#include "Communicator.hxx" +#include "OSXBluetoothWrapper.hxx" + +@interface ChannelDelegate : NSObject +{ + sd::Communicator* pCommunicator; + sd::OSXBluetoothWrapper* pSocket; +} + +- (id) initWithCommunicatorAndSocket:(sd::Communicator*)communicator socket:(sd::OSXBluetoothWrapper*)socket; +- (void) rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength; +- (void) rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel; + +@end + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXBluetooth.mm b/sd/source/ui/remotecontrol/OSXBluetooth.mm new file mode 100644 index 000000000..8b705c50b --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXBluetooth.mm @@ -0,0 +1,53 @@ +/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include +#include + +#include +#import +#include + +#include "OSXBluetooth.h" + +@implementation ChannelDelegate + +- (id) initWithCommunicatorAndSocket:(sd::Communicator*)communicator socket:(sd::OSXBluetoothWrapper*)socket +{ + pCommunicator = communicator; + pSocket = socket; + return self; +} + +- (void) rfcommChannelData:(IOBluetoothRFCOMMChannel*)rfcommChannel data:(void *)dataPointer length:(size_t)dataLength +{ + (void) rfcommChannel; + + if ( pSocket ) + { + pSocket->appendData(dataPointer, dataLength); + } +} + +- (void) rfcommChannelClosed:(IOBluetoothRFCOMMChannel*)rfcommChannel +{ + (void) rfcommChannel; + + SAL_INFO( "sdremote.bluetooth", "ChannelDelegate::rfcommChannelClosed()"); + + if ( pSocket ) + { + pSocket->channelClosed(); + } + pCommunicator = nullptr; + pSocket = nullptr; +} + +@end + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXBluetoothWrapper.hxx b/sd/source/ui/remotecontrol/OSXBluetoothWrapper.hxx new file mode 100644 index 000000000..26e1349f0 --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXBluetoothWrapper.hxx @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include +#include + +#include "IBluetoothSocket.hxx" + +namespace sd +{ + class OSXBluetoothWrapper : public IBluetoothSocket + { + IOBluetoothRFCOMMChannel* mpChannel; + int mnMTU; + osl::Condition mHaveBytes; + osl::Mutex mMutex; + std::vector mBuffer; + + public: + OSXBluetoothWrapper( IOBluetoothRFCOMMChannel* channel ); + virtual sal_Int32 readLine( OString& aLine ) override; + virtual sal_Int32 write( const void* pBuffer, sal_uInt32 len ) override; + void appendData(void* pBuffer, size_t len ); + void channelClosed(); + }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXNetworkService.h b/sd/source/ui/remotecontrol/OSXNetworkService.h new file mode 100644 index 000000000..7298d901b --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXNetworkService.h @@ -0,0 +1,30 @@ +/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include + +#include +#import +#import +#import +#include + +@interface OSXBonjourService : NSObject +{ + NSNetService* netService; +} + +- (void)publishImpressRemoteServiceOnLocalNetworkWithName:(NSString*)sName; + +@end + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/OSXNetworkService.hxx b/sd/source/ui/remotecontrol/OSXNetworkService.hxx new file mode 100644 index 000000000..78ab13eff --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXNetworkService.hxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#import +#include +#import "OSXNetworkService.h" + +#include "ZeroconfService.hxx" + +namespace sd { + class OSXNetworkService : public ZeroconfService + { + private: + OSXBonjourService *osxservice; + public: + OSXNetworkService(const std::string& aname = "", unsigned int aport = 1599) + : ZeroconfService(aname, aport){} + + void clear() override { + [osxservice dealloc]; + } + void setup() override { + osxservice = [[OSXBonjourService alloc] init]; + [osxservice publishImpressRemoteServiceOnLocalNetworkWithName: @""]; + }; + }; +} diff --git a/sd/source/ui/remotecontrol/OSXNetworkService.mm b/sd/source/ui/remotecontrol/OSXNetworkService.mm new file mode 100644 index 000000000..51cbd8c99 --- /dev/null +++ b/sd/source/ui/remotecontrol/OSXNetworkService.mm @@ -0,0 +1,43 @@ +/* -*- Mode: ObjC; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include + +#include + #import + #import "OSXNetworkService.h" +#include + +@implementation OSXBonjourService + +- (void) publishImpressRemoteServiceOnLocalNetworkWithName:(NSString *)sName +{ + netService = [[NSNetService alloc] initWithDomain:@"local" type:@"_impressremote._tcp" name:sName port:1599]; + + if (netService != nil) + { + [netService setDelegate:self]; + [netService scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; + [netService publish]; + } +} + +-(void)netService:(NSNetService *)aNetService + didNotPublish:(NSDictionary *)dict { + NSLog(@"Service %p did not publish: %@", aNetService, dict); +} + +- (void)dealloc { + [netService stop]; + [netService release]; + [super dealloc]; +} + +@end + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Receiver.cxx b/sd/source/ui/remotecontrol/Receiver.cxx new file mode 100644 index 000000000..dd92e8e99 --- /dev/null +++ b/sd/source/ui/remotecontrol/Receiver.cxx @@ -0,0 +1,207 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include "Receiver.hxx" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace sd; +using namespace ::osl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::beans; + +Receiver::Receiver( Transmitter *aTransmitter ) : Timer("sd Receiver") +{ + pTransmitter = aTransmitter; + SetTimeout( 0 ); +} + +Receiver::~Receiver() +{ +} + +// Bounce the commands to the main thread to avoid threading woes +void Receiver::pushCommand( const std::vector &rCommand ) +{ + SolarMutexGuard aGuard; + maExecQueue.push_back( rCommand ); + Start(); +} + +void Receiver::Invoke() +{ + if( !maExecQueue.empty() ) + { + std::vector< OString > aCommands( maExecQueue.front() ); + maExecQueue.pop_front(); + if( !aCommands.empty() ) + executeCommand( aCommands ); + Start(); + } + else + Stop(); +} + +void Receiver::executeCommand( const std::vector &aCommand ) +{ + uno::Reference xSlideShowController; + uno::Reference xPresentation; + uno::Reference xSlideShow; + try { + uno::Reference< frame::XDesktop2 > xFramesSupplier = frame::Desktop::create( ::comphelper::getProcessComponentContext() ); + uno::Reference< frame::XFrame > xFrame ( xFramesSupplier->getActiveFrame(), uno::UNO_SET_THROW ); + uno::Reference xPS ( xFrame->getController()->getModel(), uno::UNO_QUERY_THROW); + xPresentation.set( xPS->getPresentation(), uno::UNO_QUERY_THROW); + // Throws an exception if no slideshow running + xSlideShowController.set( xPresentation->getController(), uno::UNO_SET_THROW ); + xSlideShow.set( xSlideShowController->getSlideShow(), uno::UNO_SET_THROW ); + } + catch (uno::RuntimeException &) + { + } + + if ( aCommand[0] == "transition_next" ) + { + if ( xSlideShowController.is() ) + xSlideShowController->gotoNextEffect(); + } + else if ( aCommand[0] == "transition_previous" ) + { + if ( xSlideShowController.is() ) + xSlideShowController->gotoPreviousEffect(); + } + else if ( aCommand[0] == "goto_slide" ) + { + // FIXME: if 0 returned, then not a valid number + sal_Int32 aSlide = aCommand[1].toInt32(); + if ( xSlideShowController.is() && + xSlideShowController->getCurrentSlideIndex() != aSlide ) + { + xSlideShowController->gotoSlideIndex( aSlide ); + } + } + else if ( aCommand[0] == "presentation_start" ) + { + if ( xPresentation.is() ) + xPresentation->start(); + } + else if ( aCommand[0] == "presentation_stop" ) + { + if ( xPresentation.is() ) + xPresentation->end(); + } + else if ( aCommand[0] == "presentation_blank_screen" ) + { + if ( aCommand.size() > 1 ) + { +// aColour = FIXME: get the colour in some format from this string +// Determine the formatting first. + } + if ( xSlideShowController.is() ) + { + xSlideShowController->blankScreen( 0 ); // Default is black + } + } + else if (aCommand[0] == "pointer_started" ) + { + // std::cerr << "pointer_started" << std::endl; + float x = aCommand[1].toFloat(); + float y = aCommand[2].toFloat(); + SolarMutexGuard aSolarGuard; + + const css::geometry::RealPoint2D pos(x,y); + // std::cerr << "Pointer at ("<setProperty(beans::PropertyValue("PointerPosition", -1, Any(pos), + beans::PropertyState_DIRECT_VALUE)); + } + catch (Exception&) + { + TOOLS_WARN_EXCEPTION("sdremote", "sd::SlideShowImpl::setPointerPosition()"); + } + + try + { + xSlideShow->setProperty(beans::PropertyValue("PointerVisible", -1, Any(true), + beans::PropertyState_DIRECT_VALUE)); + } + catch (Exception&) + { + TOOLS_WARN_EXCEPTION("sdremote", "sd::SlideShowImpl::setPointerMode()"); + } + } + + SAL_INFO( "sdremote", "Pointer started, we display the pointer on screen" ); + } + else if (aCommand[0] == "pointer_dismissed" ) + { + SolarMutexGuard aSolarGuard; + if (xSlideShow.is()) try + { + xSlideShow->setProperty( + beans::PropertyValue( "PointerVisible" , + -1, + Any( false ), + beans::PropertyState_DIRECT_VALUE ) ); + } + catch ( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sdremote", "sd::SlideShowImpl::setPointerMode()" ); + } + + SAL_INFO( "sdremote", "Pointer dismissed, we hide the pointer on screen" ); + } + else if (aCommand[0] == "pointer_coordination" ) + { + float x = aCommand[1].toFloat(); + float y = aCommand[2].toFloat(); + + SAL_INFO( "sdremote", "Pointer at ("<setProperty( + beans::PropertyValue( "PointerPosition" , + -1, + Any( pos ), + beans::PropertyState_DIRECT_VALUE ) ); + } + catch ( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sdremote", "sd::SlideShowImpl::setPointerPosition()" ); + } + } + else if ( aCommand[0] == "presentation_resume" ) + { + if ( xSlideShowController.is() ) + { + xSlideShowController->resume(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Receiver.hxx b/sd/source/ui/remotecontrol/Receiver.hxx new file mode 100644 index 000000000..d3fadf0da --- /dev/null +++ b/sd/source/ui/remotecontrol/Receiver.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#pragma once + +#include +#include + +#include +#include + +namespace sd { class Transmitter; } + +namespace sd +{ + +// Timer is protected by the solar mutex => so are we. +class Receiver : private Timer +{ + std::deque< std::vector< OString > > maExecQueue; +public: + explicit Receiver( Transmitter *aTransmitter ); + virtual ~Receiver() override; + virtual void Invoke() override; + void pushCommand( const std::vector &rCommand ); + static void executeCommand( const std::vector &aCommand ); + +private: + Transmitter *pTransmitter; +}; + +} diff --git a/sd/source/ui/remotecontrol/Server.cxx b/sd/source/ui/remotecontrol/Server.cxx new file mode 100644 index 000000000..53bf0352c --- /dev/null +++ b/sd/source/ui/remotecontrol/Server.cxx @@ -0,0 +1,373 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "DiscoveryService.hxx" +#include "Listener.hxx" +#include +#include "BluetoothServer.hxx" +#include "Communicator.hxx" +#include "BufferedStreamSocket.hxx" + +using namespace sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::osl; +using namespace ::comphelper; + +namespace sd { + /** + * Used to keep track of clients that have attempted to connect, but haven't + * yet been approved. + */ + struct ClientInfoInternal: + ClientInfo + { + BufferedStreamSocket *mpStreamSocket; + OUString mPin; + + ClientInfoInternal( const OUString& rName, + BufferedStreamSocket *pSocket, + const OUString& rPin ): + ClientInfo( rName, false ), + mpStreamSocket( pSocket ), + mPin( rPin ) {} + }; +} + +RemoteServer::RemoteServer() : + Thread( "RemoteServerThread" ) +{ + SAL_INFO( "sdremote", "Instantiated RemoteServer" ); +} + +RemoteServer::~RemoteServer() +{ +} + +void RemoteServer::execute() +{ + SAL_INFO( "sdremote", "RemoteServer::execute called" ); + osl::SocketAddr aAddr( "0.0.0.0", PORT ); + if ( !mSocket.bind( aAddr ) ) + { + SAL_WARN( "sdremote", "bind failed" << mSocket.getErrorAsString() ); + spServer = nullptr; + return; + } + + if ( !mSocket.listen(3) ) + { + SAL_WARN( "sdremote", "listen failed" << mSocket.getErrorAsString() ); + spServer = nullptr; + return; + } + while ( true ) + { + StreamSocket aSocket; + SAL_INFO( "sdremote", "waiting on accept" ); + if ( mSocket.acceptConnection( aSocket ) == osl_Socket_Error ) + { + SAL_WARN( "sdremote", "accept failed" << mSocket.getErrorAsString() ); + spServer = nullptr; + return; // Closed, or other issue. + } + BufferedStreamSocket *pSocket = new BufferedStreamSocket( aSocket); + handleAcceptedConnection( pSocket ); + } + SAL_INFO( "sdremote", "shutting down RemoteServer" ); + spServer = nullptr; // Object is destroyed when Thread::execute() ends. +} + +void RemoteServer::handleAcceptedConnection( BufferedStreamSocket *pSocket ) +{ + OString aLine; + if ( ! ( pSocket->readLine( aLine) + && aLine == "LO_SERVER_CLIENT_PAIR" + && pSocket->readLine( aLine ) ) ) + { + SAL_INFO( "sdremote", "client failed to send LO_SERVER_CLIENT_PAIR, ignoring" ); + delete pSocket; + return; + } + + OString aName( aLine ); + + if ( ! pSocket->readLine( aLine ) ) + { + delete pSocket; + return; + } + OString aPin( aLine ); + + SocketAddr aClientAddr; + pSocket->getPeerAddr( aClientAddr ); + + do + { + // Read off any additional non-empty lines + // We know that we at least have the empty termination line to read. + if ( ! pSocket->readLine( aLine ) ) { + delete pSocket; + return; + } + } + while ( aLine.getLength() > 0 ); + + MutexGuard aGuard( sDataMutex ); + std::shared_ptr< ClientInfoInternal > pClient = + std::make_shared( + OStringToOUString( aName, RTL_TEXTENCODING_UTF8 ), + pSocket, OStringToOUString( aPin, RTL_TEXTENCODING_UTF8 ) ); + mAvailableClients.push_back( pClient ); + + // Check if we already have this server. + Reference< XNameAccess > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get(); + const Sequence< OUString > aNames = xConfig->getElementNames(); + for ( const auto& rName : aNames ) + { + if ( rName == pClient->mName ) + { + Reference xSetItem( xConfig->getByName(rName), UNO_QUERY ); + Any axPin(xSetItem->getByName("PIN")); + OUString sPin; + axPin >>= sPin; + + if ( sPin == pClient->mPin ) { + SAL_INFO( "sdremote", "client found on validated list -- connecting" ); + connectClient( pClient, sPin ); + return; + } + } + } + + // Pin not found so inform the client. + SAL_INFO( "sdremote", "client not found on validated list" ); + pSocket->write( "LO_SERVER_VALIDATING_PIN\n\n", + strlen( "LO_SERVER_VALIDATING_PIN\n\n" ) ); +} + +RemoteServer *sd::RemoteServer::spServer = nullptr; +::osl::Mutex sd::RemoteServer::sDataMutex; +::std::vector sd::RemoteServer::sCommunicators; + +void RemoteServer::setup() +{ + if (spServer) + return; + + spServer = new RemoteServer(); + spServer->launch(); + +#ifdef ENABLE_SDREMOTE_BLUETOOTH + sd::BluetoothServer::setup( &sCommunicators ); +#endif +} + +void RemoteServer::presentationStarted( const css::uno::Reference< + css::presentation::XSlideShowController > &rController ) +{ + if ( !spServer ) + return; + MutexGuard aGuard( sDataMutex ); + for ( const auto& rpCommunicator : sCommunicators ) + { + rpCommunicator->presentationStarted( rController ); + } +} +void RemoteServer::presentationStopped() +{ + if ( !spServer ) + return; + MutexGuard aGuard( sDataMutex ); + for ( const auto& rpCommunicator : sCommunicators ) + { + rpCommunicator->disposeListener(); + } +} + +void RemoteServer::removeCommunicator( Communicator const * mCommunicator ) +{ + if ( !spServer ) + return; + MutexGuard aGuard( sDataMutex ); + auto aIt = std::find(sCommunicators.begin(), sCommunicators.end(), mCommunicator); + if (aIt != sCommunicators.end()) + sCommunicators.erase( aIt ); +} + +std::vector< std::shared_ptr< ClientInfo > > RemoteServer::getClients() +{ + SAL_INFO( "sdremote", "RemoteServer::getClients() called" ); + std::vector< std::shared_ptr< ClientInfo > > aClients; + if ( spServer ) + { + MutexGuard aGuard( sDataMutex ); + aClients.assign( spServer->mAvailableClients.begin(), + spServer->mAvailableClients.end() ); + } + else + { + SAL_INFO( "sdremote", "No remote server instance => no remote clients" ); + } + // We also need to provide authorised clients (no matter whether or not + // they are actually available), so that they can be de-authorised if + // necessary. We specifically want these to be at the end of the list + // since the user is more likely to be trying to connect a new remote + // than removing an existing remote. + // We can also be sure that pre-authorised clients will not be on the + // available clients list, as they get automatically connected if seen. + // TODO: we should probably add some sort of extra labelling to mark + // authorised AND connected client. + Reference< XNameAccess > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get(); + const Sequence< OUString > aNames = xConfig->getElementNames(); + std::transform(aNames.begin(), aNames.end(), std::back_inserter(aClients), + [](const OUString& rName) -> std::shared_ptr { + return std::make_shared(rName, true); }); + + return aClients; +} + +bool RemoteServer::connectClient( const std::shared_ptr< ClientInfo >& pClient, std::u16string_view aPin ) +{ + SAL_INFO( "sdremote", "RemoteServer::connectClient called" ); + if ( !spServer ) + return false; + + ClientInfoInternal* apClient = dynamic_cast< ClientInfoInternal* >( pClient.get() ); + if ( !apClient ) + // could happen if we try to "connect" an already authorised client + { + return false; + } + + if ( apClient->mPin == aPin ) + { + // Save in settings first + std::shared_ptr< ConfigurationChanges > aChanges = ConfigurationChanges::create(); + Reference< XNameContainer > const xConfig = officecfg::Office::Impress::Misc::AuthorisedRemotes::get( aChanges ); + + Reference xChildFactory ( + xConfig, UNO_QUERY); + Reference xChild( xChildFactory->createInstance(), UNO_QUERY); + Any aValue; + if (xChild.is()) + { + // Check whether the client is already saved + Sequence< OUString > aNames = xConfig->getElementNames(); + if (comphelper::findValue(aNames, apClient->mName) != -1) + xConfig->replaceByName( apClient->mName, Any( xChild ) ); + else + xConfig->insertByName( apClient->mName, Any( xChild ) ); + aValue <<= apClient->mPin; + xChild->replaceByName("PIN", aValue); + aChanges->commit(); + } + + Communicator* pCommunicator = new Communicator( std::unique_ptr(apClient->mpStreamSocket) ); + MutexGuard aGuard( sDataMutex ); + + sCommunicators.push_back( pCommunicator ); + + auto aIt = std::find(spServer->mAvailableClients.begin(), spServer->mAvailableClients.end(), pClient); + if (aIt != spServer->mAvailableClients.end()) + spServer->mAvailableClients.erase( aIt ); + pCommunicator->launch(); + return true; + } + else + { + return false; + } +} + +void RemoteServer::deauthoriseClient( const std::shared_ptr< ClientInfo >& pClient ) +{ + // TODO: we probably want to forcefully disconnect at this point too? + // But possibly via a separate function to allow just disconnecting from + // the UI. + + SAL_INFO( "sdremote", "RemoteServer::deauthoriseClient called" ); + + if ( !pClient->mbIsAlreadyAuthorised ) + // We can't remove unauthorised clients from the authorised list... + { + return; + } + + std::shared_ptr< ConfigurationChanges > aChanges = ConfigurationChanges::create(); + Reference< XNameContainer > const xConfig = + officecfg::Office::Impress::Misc::AuthorisedRemotes::get( aChanges ); + + xConfig->removeByName( pClient->mName ); + aChanges->commit(); +} + +void SdDLL::RegisterRemotes() +{ + SAL_INFO( "sdremote", "SdDLL::RegisterRemotes called" ); + + // The remote server is likely of no use in headless mode. And as only + // one instance of the server can actually own the appropriate ports its + // probably best to not even try to do so from our headless instance + // (i.e. as to avoid blocking expected usage). + // It could perhaps be argued that we would still need the remote + // server for tiled rendering of presentations, but even then this + // implementation would not be of much use, i.e. would be controlling + // the purely imaginary headless presentation -- instead we'd need + // to have some sort of mechanism of plugging in our tiled rendering + // client to be controlled by the remote server, or provide an + // alternative implementation. + if ( Application::IsHeadlessModeEnabled() ) + return; + + if ( !officecfg::Office::Impress::Misc::Start::EnableSdremote::get() ) + return; + + sd::RemoteServer::setup(); + sd::DiscoveryService::setup(); +} + +void RemoteServer::ensureDiscoverable() +{ + // FIXME: we could also enable listening on our WiFi + // socket here to significantly reduce the attack surface. +#ifdef ENABLE_SDREMOTE_BLUETOOTH + BluetoothServer::ensureDiscoverable(); +#endif +} + +void RemoteServer::restoreDiscoverable() +{ +#ifdef ENABLE_SDREMOTE_BLUETOOTH + BluetoothServer::restoreDiscoverable(); +#endif +} +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Transmitter.cxx b/sd/source/ui/remotecontrol/Transmitter.cxx new file mode 100644 index 000000000..cca6a3bee --- /dev/null +++ b/sd/source/ui/remotecontrol/Transmitter.cxx @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#include "Transmitter.hxx" +#include "IBluetoothSocket.hxx" +#include + +using namespace osl; // Sockets etc. +using namespace sd; + +Transmitter::Transmitter( IBluetoothSocket* aSocket ) + : pStreamSocket( aSocket ), + mFinishRequested( false ) +{ +} + +void SAL_CALL Transmitter::run() +{ + osl_setThreadName("bluetooth Transmitter"); + + while ( true ) + { + mProcessingRequired.wait(); + + ::osl::MutexGuard aGuard( mMutex ); + + if ( mFinishRequested ) { + return; + } + if ( !mHighPriority.empty() ) + { + OString aMessage( mHighPriority.front() ); + mHighPriority.pop(); + SAL_INFO( "sdremote.bluetooth", "write high prio line '" << aMessage << "'" ); + pStreamSocket->write( aMessage.getStr(), aMessage.getLength() ); + } + else if ( !mLowPriority.empty() ) + { + OString aMessage( mLowPriority.front() ); + mLowPriority.pop(); + SAL_INFO( "sdremote.bluetooth", "write normal line '" << aMessage << "'" ); + pStreamSocket->write( aMessage.getStr(), aMessage.getLength() ); + } + + if ( mLowPriority.empty() && mHighPriority.empty()) + { + mProcessingRequired.reset(); + } + } +} + +void Transmitter::notifyFinished() +{ + ::osl::MutexGuard aGuard( mMutex ); + mFinishRequested = true; + mProcessingRequired.set(); +} + +Transmitter::~Transmitter() +{ +} + +void Transmitter::addMessage( const OString& aMessage, const Priority aPriority ) +{ + ::osl::MutexGuard aGuard( mMutex ); + switch ( aPriority ) + { + case PRIORITY_LOW: + mLowPriority.push( aMessage ); + break; + case PRIORITY_HIGH: + mHighPriority.push( aMessage ); + break; + } + if ( !mProcessingRequired.check() ) + { + mProcessingRequired.set(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/Transmitter.hxx b/sd/source/ui/remotecontrol/Transmitter.hxx new file mode 100644 index 000000000..c24f5a5a4 --- /dev/null +++ b/sd/source/ui/remotecontrol/Transmitter.hxx @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace sd { struct IBluetoothSocket; } + +namespace sd +{ + +class Transmitter +: public osl::Thread +{ +public: + enum Priority { PRIORITY_LOW = 1, PRIORITY_HIGH }; + explicit Transmitter( ::sd::IBluetoothSocket* aSocket ); + virtual ~Transmitter() override; + void addMessage( const OString& aMessage, const Priority aPriority ); + void notifyFinished(); + +private: + virtual void SAL_CALL run() override; + + ::sd::IBluetoothSocket* pStreamSocket; + + ::osl::Condition mProcessingRequired; + + ::osl::Mutex mMutex; + /** + * Used to indicate that we're done and the transmitter loop should exit. + * All access must be guarded my `mMutex`. + */ + bool mFinishRequested; + /// Queue for low priority messages. All access must be guarded my `mMutex`. + std::queue mLowPriority; + /// Queue for high priority messages. All access must be guarded my `mMutex`. + std::queue mHighPriority; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/remotecontrol/WINNetworkService.cxx b/sd/source/ui/remotecontrol/WINNetworkService.cxx new file mode 100644 index 000000000..bd2decf62 --- /dev/null +++ b/sd/source/ui/remotecontrol/WINNetworkService.cxx @@ -0,0 +1,19 @@ +#include +#include +#include "WINNetworkService.hxx" +#include + +void sd::WINNetworkService::setup() +{ + DNSServiceErrorType err = DNSServiceRegister(&client, 0, 0, nullptr, kREG_TYPE, "local", nullptr, 1599, 1, "", nullptr, this ); + + if (kDNSServiceErr_NoError != err) + SAL_WARN("sdremote.wifi", "DNSServiceRegister failed: " << err); + + // Fail silently +} + +void sd::WINNetworkService::clear() +{ + DNSServiceRefDeallocate(client); +} diff --git a/sd/source/ui/remotecontrol/WINNetworkService.hxx b/sd/source/ui/remotecontrol/WINNetworkService.hxx new file mode 100644 index 000000000..3d096dc0f --- /dev/null +++ b/sd/source/ui/remotecontrol/WINNetworkService.hxx @@ -0,0 +1,23 @@ +#pragma once + +#include +#undef WB_LEFT +#undef WB_RIGHT +#include +#include "ZeroconfService.hxx" + +namespace sd{ + class WINNetworkService : public ZeroconfService + { + private: + DNSServiceRef client; + + public: + WINNetworkService(const std::string& aname = "", unsigned int aport = 1599) + : ZeroconfService(aname, aport), client(nullptr) {} + + void clear() override; + void setup() override; + + }; +} diff --git a/sd/source/ui/remotecontrol/ZeroconfService.hxx b/sd/source/ui/remotecontrol/ZeroconfService.hxx new file mode 100644 index 000000000..a595d0b58 --- /dev/null +++ b/sd/source/ui/remotecontrol/ZeroconfService.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +#ifndef ZEROCONF_SERVICE +#define ZEROCONF_SERVICE + +#include + +/** +* The port used by LO's custom remote server discovery protocol. +*/ +#define PORT_DISCOVERY 1598 +#define BUFFER_SIZE 200 + +#define kREG_TYPE "_impressremote._tcp" + +struct sockaddr_in; + +typedef unsigned int uint; + +namespace sd{ + + class ZeroconfService + { + protected: + std::string name; + uint port; + + public: + explicit ZeroconfService(const std::string& aname, uint aport) + :name(aname), port(aport){} + virtual ~ZeroconfService(){} + + const std::string& getName() const {return name;} + void setName(const char * n) {name = n;} + + // Clean up the service when closing + virtual void clear() = 0; + // Bonjour for OSX, Avahi for Linux + virtual void setup() = 0; + }; + +} +#endif diff --git a/sd/source/ui/sidebar/AllMasterPagesSelector.cxx b/sd/source/ui/sidebar/AllMasterPagesSelector.cxx new file mode 100644 index 000000000..76e056120 --- /dev/null +++ b/sd/source/ui/sidebar/AllMasterPagesSelector.cxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "AllMasterPagesSelector.hxx" +#include +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" +#include + +#include + +namespace { + +using namespace sd::sidebar; + +int GetURLPriority (const SharedMasterPageDescriptor& rpDescriptor) +{ + int nPriority (0); + switch (rpDescriptor->GetURLClassification()) + { + case MasterPageDescriptor::URLCLASS_USER: nPriority = 0; break; + case MasterPageDescriptor::URLCLASS_LAYOUT: nPriority = 1; break; + case MasterPageDescriptor::URLCLASS_PRESENTATION: nPriority = 2; break; + case MasterPageDescriptor::URLCLASS_OTHER: nPriority = 3; break; + case MasterPageDescriptor::URLCLASS_UNKNOWN: nPriority = 4; break; + default: + case MasterPageDescriptor::URLCLASS_UNDETERMINED: nPriority = 5; break; + } + return nPriority; +} + +class MasterPageDescriptorOrder +{ +public: + bool operator() ( + const SharedMasterPageDescriptor& rp1, + const SharedMasterPageDescriptor& rp2) const + { + if (rp1->meOrigin == MasterPageContainer::DEFAULT) + return true; + else if (rp2->meOrigin == MasterPageContainer::DEFAULT) + return false; + else if (rp1->GetURLClassification() == rp2->GetURLClassification()) + return rp1->mnTemplateIndex < rp2->mnTemplateIndex; + else + return GetURLPriority(rp1) < GetURLPriority(rp2); + } +}; + +} // end of anonymous namespace + +namespace sd::sidebar { + +class AllMasterPagesSelector::SortedMasterPageDescriptorList + : public ::std::set +{ +public: + SortedMasterPageDescriptorList() {} +}; + +std::unique_ptr AllMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared(); + + auto xSelector(std::make_unique( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId(HID_SD_TASK_PANE_PREVIEW_ALL); + + return xSelector; +} + +AllMasterPagesSelector::AllMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr& rpContainer, + const css::uno::Reference& rxSidebar) + : MasterPagesSelector(pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanelall.ui", "allvalueset"), + mpSortedMasterPages(new SortedMasterPageDescriptorList()) +{ + MasterPagesSelector::Fill(); +} + +AllMasterPagesSelector::~AllMasterPagesSelector() +{ +} + +void AllMasterPagesSelector::Fill (ItemList& rItemList) +{ + if (mpSortedMasterPages->empty()) + UpdateMasterPageList(); + UpdatePageSet(rItemList); +} + +void AllMasterPagesSelector::NotifyContainerChangeEvent ( + const MasterPageContainerChangeEvent& rEvent) +{ + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::CHILD_ADDED: + AddItem(rEvent.maChildToken); + MasterPagesSelector::Fill(); + break; + + case MasterPageContainerChangeEvent::EventType::INDEX_CHANGED: + mpSortedMasterPages->clear(); + MasterPagesSelector::Fill(); + break; + + default: + MasterPagesSelector::NotifyContainerChangeEvent(rEvent); + break; + } +} + +void AllMasterPagesSelector::UpdateMasterPageList() +{ + mpSortedMasterPages->clear(); + int nTokenCount = mpContainer->GetTokenCount(); + for (int i=0; iGetTokenForIndex(i)); +} + +void AllMasterPagesSelector::AddItem (MasterPageContainer::Token aToken) +{ + switch (mpContainer->GetOriginForToken(aToken)) + { + case MasterPageContainer::DEFAULT: + case MasterPageContainer::TEMPLATE: + // Templates are added only when coming from the + // MasterPageContainerFiller so that they have an id which + // defines their place in the list. Templates (pre) loaded from + // RecentlyUsedMasterPages are ignored (they will be loaded + // later by the MasterPageContainerFiller.) + if (mpContainer->GetTemplateIndexForToken(aToken) >= 0) + mpSortedMasterPages->insert(mpContainer->GetDescriptorForToken(aToken)); + break; + + default: + break; + } +} + +void AllMasterPagesSelector::UpdatePageSet (ItemList& rItemList) +{ + for (const auto& rxDescriptor : *mpSortedMasterPages) + rItemList.push_back(rxDescriptor->maToken); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/AllMasterPagesSelector.hxx b/sd/source/ui/sidebar/AllMasterPagesSelector.hxx new file mode 100644 index 000000000..982a2ec52 --- /dev/null +++ b/sd/source/ui/sidebar/AllMasterPagesSelector.hxx @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "MasterPagesSelector.hxx" + +#include + +namespace sd::sidebar { + +/** Show a list of all available master pages so that the user can assign + them to the document. +*/ +class AllMasterPagesSelector + : public MasterPagesSelector +{ + friend class VclPtrInstance; +public: + static std::unique_ptr Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference& rxSidebar); + + AllMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr& rpContainer, + const css::uno::Reference& rxSidebar); + virtual ~AllMasterPagesSelector() override; + + + /** Scan the set of templates for the ones whose first master pages are + shown by this control and store them in the MasterPageContainer. + */ + virtual void Fill (ItemList& rItemList) override; + +protected: + virtual void NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent) override; + +private: + /** The list of master pages displayed by this class. + */ + class SortedMasterPageDescriptorList; + ::std::unique_ptr mpSortedMasterPages; + + void AddItem (MasterPageContainer::Token aToken); + + /** Add all items in the internal master page list into the given list. + */ + void UpdatePageSet (ItemList& rItemList); + + /** Update the internal list of master pages that are to show in the + control. + */ + void UpdateMasterPageList(); + + using MasterPagesSelector::Fill; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx b/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx new file mode 100644 index 000000000..269099edd --- /dev/null +++ b/sd/source/ui/sidebar/CurrentMasterPagesSelector.cxx @@ -0,0 +1,263 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "CurrentMasterPagesSelector.hxx" +#include "PreviewValueSet.hxx" +#include +#include +#include +#include +#include "MasterPageContainer.hxx" +#include "MasterPageContainerProviders.hxx" +#include "MasterPageDescriptor.hxx" +#include +#include +#include + +#include + +#include + +using namespace ::com::sun::star; + +namespace sd::sidebar { + +std::unique_ptr CurrentMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared(); + + auto xSelector(std::make_unique( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId( HID_SD_TASK_PANE_PREVIEW_CURRENT ); + + return xSelector; +} + +CurrentMasterPagesSelector::CurrentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr& rpContainer, + const css::uno::Reference& rxSidebar) + : MasterPagesSelector (pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanel.ui", "usedvalueset") +{ + Link aLink (LINK(this,CurrentMasterPagesSelector,EventMultiplexerListener)); + rBase.GetEventMultiplexer()->AddEventListener(aLink); +} + +CurrentMasterPagesSelector::~CurrentMasterPagesSelector() +{ + if (mrDocument.GetDocSh() != nullptr) + { + EndListening(*mrDocument.GetDocSh()); + } + else + { + OSL_ASSERT(mrDocument.GetDocSh() != nullptr); + } + + Link aLink (LINK(this,CurrentMasterPagesSelector,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->RemoveEventListener(aLink); +} + +void CurrentMasterPagesSelector::LateInit() +{ + MasterPagesSelector::LateInit(); + MasterPagesSelector::Fill(); + if (mrDocument.GetDocSh() != nullptr) + { + StartListening(*mrDocument.GetDocSh()); + } + else + { + OSL_ASSERT(mrDocument.GetDocSh() != nullptr); + } +} + +void CurrentMasterPagesSelector::Fill (ItemList& rItemList) +{ + sal_uInt16 nPageCount = mrDocument.GetMasterSdPageCount(PageKind::Standard); + // Remember the names of the master pages that have been inserted to + // avoid double insertion. + ::std::set aMasterPageNames; + for (sal_uInt16 nIndex=0; nIndexGetName()); + if (!aMasterPageNames.insert(sName).second) + continue; + + // Look up the master page in the container and, when it is not yet + // in it, insert it. + MasterPageContainer::Token aToken = mpContainer->GetTokenForPageObject(pMasterPage); + if (aToken == MasterPageContainer::NIL_TOKEN) + { + SharedMasterPageDescriptor pDescriptor = std::make_shared( + MasterPageContainer::MASTERPAGE, + nIndex, + OUString(), + pMasterPage->GetName(), + OUString(), + pMasterPage->IsPrecious(), + std::make_shared(pMasterPage), + std::make_shared()); + aToken = mpContainer->PutMasterPage(pDescriptor); + } + + rItemList.push_back(aToken); + } +} + +OUString CurrentMasterPagesSelector::GetContextMenuUIFile() const +{ + return "modules/simpress/ui/currentmastermenu.ui"; +} + +void CurrentMasterPagesSelector::UpdateSelection() +{ + // Iterate over all pages and for the selected ones put the name of + // their master page into a set. + sal_uInt16 nPageCount = mrDocument.GetSdPageCount(PageKind::Standard); + ::std::set aNames; + sal_uInt16 nIndex; + bool bLoop (true); + for (nIndex=0; nIndexIsSelected()) + { + if ( ! pPage->TRG_HasMasterPage()) + { + // One of the pages has no master page. This is an + // indicator for that this method is called in the middle of + // a document change and that the model is not in a valid + // state. Therefore we stop update the selection and wait + // for another call to UpdateSelection when the model is + // valid again. + bLoop = false; + } + else + { + SdrPage& rMasterPage (pPage->TRG_GetMasterPage()); + assert(dynamic_cast(&rMasterPage)); + aNames.insert(static_cast(rMasterPage).GetName()); + } + } + } + + // Find the items for the master pages in the set. + sal_uInt16 nItemCount (mxPreviewValueSet->GetItemCount()); + for (nIndex=1; nIndex<=nItemCount && bLoop; nIndex++) + { + OUString sName (mxPreviewValueSet->GetItemText (nIndex)); + if (aNames.find(sName) != aNames.end()) + { + mxPreviewValueSet->SelectItem (nIndex); + } + } +} + +void CurrentMasterPagesSelector::ExecuteCommand(const OString &rIdent) +{ + if (rIdent == "delete") + { + // Check once again that the master page can safely be deleted, + // i.e. is not used. + SdPage* pMasterPage = GetSelectedMasterPage(); + if (pMasterPage != nullptr + && mrDocument.GetMasterPageUserCount(pMasterPage) == 0) + { + // Removing the precious flag so that the following call to + // RemoveUnnecessaryMasterPages() will remove this master page. + pMasterPage->SetPrecious(false); + mrDocument.RemoveUnnecessaryMasterPages(pMasterPage); + } + } + else + MasterPagesSelector::ExecuteCommand(rIdent); +} + +void CurrentMasterPagesSelector::ProcessPopupMenu(weld::Menu& rMenu) +{ + // Disable the delete entry when there is only one master page. + if (mrDocument.GetMasterPageUserCount(GetSelectedMasterPage()) > 0) + rMenu.set_sensitive("delete", false); + + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(mrBase.GetMainViewShell())); + if (pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::MasterPage) + { + rMenu.set_sensitive("edit", false); + } + + MasterPagesSelector::ProcessPopupMenu(rMenu); +} + +IMPL_LINK(CurrentMasterPagesSelector,EventMultiplexerListener, + sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::EditModeNormal: + case EventMultiplexerEventId::EditModeMaster: + case EventMultiplexerEventId::SlideSortedSelection: + UpdateSelection(); + break; + + case EventMultiplexerEventId::PageOrder: + // This is tricky. If a master page is removed, moved, or + // added we have to wait until both the notes master page + // and the standard master page have been removed, moved, + // or added. We do this by looking at the number of master + // pages which has to be odd in the consistent state (the + // handout master page is always present). If the number is + // even we ignore the hint. + if (mrBase.GetDocument()->GetMasterPageCount()%2 == 1) + MasterPagesSelector::Fill(); + break; + + case EventMultiplexerEventId::ShapeChanged: + case EventMultiplexerEventId::ShapeInserted: + case EventMultiplexerEventId::ShapeRemoved: + InvalidatePreview(static_cast(rEvent.mpUserData)); + break; + default: break; + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx b/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx new file mode 100644 index 000000000..cd7c27734 --- /dev/null +++ b/sd/source/ui/sidebar/CurrentMasterPagesSelector.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include "MasterPagesSelector.hxx" + +namespace sd::tools { class EventMultiplexerEvent; } + +namespace sd::sidebar { + +/** Show the master pages currently used by a SdDrawDocument. +*/ +class CurrentMasterPagesSelector + : public MasterPagesSelector, + public SfxListener +{ + friend class VclPtrInstance; +public: + static std::unique_ptr Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference& rxSidebar); + + CurrentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr& rpContainer, + const css::uno::Reference& rxSidebar); + virtual ~CurrentMasterPagesSelector() override; + + /** Set the selection so that the master page is selected that is + used by the currently selected page of the document in the + center pane. + */ + void UpdateSelection(); + + /** Copy all master pages that are to be shown into the given list. + */ + virtual void Fill (ItemList& rItemList) override; + + using MasterPagesSelector::Fill; + +protected: + virtual OUString GetContextMenuUIFile() const override; + + virtual void ProcessPopupMenu(weld::Menu& rMenu) override; + virtual void ExecuteCommand(const OString &rIdent) override; + +private: + virtual void LateInit() override; + + DECL_LINK(EventMultiplexerListener,sd::tools::EventMultiplexerEvent&, void); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/DocumentHelper.cxx b/sd/source/ui/sidebar/DocumentHelper.cxx new file mode 100644 index 000000000..00c028868 --- /dev/null +++ b/sd/source/ui/sidebar/DocumentHelper.cxx @@ -0,0 +1,536 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "DocumentHelper.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace sd::sidebar { + +SdPage* DocumentHelper::CopyMasterPageToLocalDocument ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage) +{ + SdPage* pNewMasterPage = nullptr; + + do + { + if (pMasterPage == nullptr) + break; + + // Check the presence of the source document. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + + // When the given master page already belongs to the target document + // then there is nothing more to do. + if (&rSourceDocument == &rTargetDocument) + { + pNewMasterPage = pMasterPage; + break; + } + + // Test if the master pages of both the slide and its notes page are + // present. This is not the case when we are called during the + // creation of the slide master page because then the notes master + // page is not there. + sal_uInt16 nSourceMasterPageCount = rSourceDocument.GetMasterPageCount(); + if (nSourceMasterPageCount%2 == 0) + // There should be 1 handout page + n slide masters + n notes + // masters = 2*n+1. An even value indicates that a new slide + // master but not yet the notes master has been inserted. + break; + sal_uInt16 nIndex = pMasterPage->GetPageNum(); + if (nSourceMasterPageCount <= nIndex+1) + break; + // Get the slide master page. + if (pMasterPage != static_cast( + rSourceDocument.GetMasterPage(nIndex))) + break; + // Get the notes master page. + SdPage* pNotesMasterPage = static_cast( + rSourceDocument.GetMasterPage(nIndex+1)); + if (pNotesMasterPage == nullptr) + break; + + // Check if a master page with the same name as that of the given + // master page already exists. + bool bPageExists (false); + sal_uInt16 nMasterPageCount(rTargetDocument.GetMasterSdPageCount(PageKind::Standard)); + for (sal_uInt16 nMaster=0; nMasterGetName() == pMasterPage->GetName()) + { + bPageExists = true; + pNewMasterPage = pCandidate; + break; + } + } + if (bPageExists) + break; + + // Create a new slide (and its notes page.) + uno::Reference xSlideSupplier ( + rTargetDocument.getUnoModel(), uno::UNO_QUERY); + if ( ! xSlideSupplier.is()) + break; + uno::Reference xSlides = + xSlideSupplier->getDrawPages(); + if ( ! xSlides.is()) + break; + xSlides->insertNewByIndex (xSlides->getCount()); + + // Set a layout. + SdPage* pSlide = rTargetDocument.GetSdPage( + rTargetDocument.GetSdPageCount(PageKind::Standard)-1, + PageKind::Standard); + if (pSlide == nullptr) + break; + pSlide->SetAutoLayout(AUTOLAYOUT_TITLE, true); + + // Create a copy of the master page and the associated notes + // master page and insert them into our document. + pNewMasterPage = AddMasterPage(rTargetDocument, pMasterPage); + if (pNewMasterPage==nullptr) + break; + SdPage* pNewNotesMasterPage + = AddMasterPage(rTargetDocument, pNotesMasterPage); + if (pNewNotesMasterPage==nullptr) + break; + + // Make the connection from the new slide to the master page + // (and do the same for the notes page.) + rTargetDocument.SetMasterPage ( + rTargetDocument.GetSdPageCount(PageKind::Standard)-1, + pNewMasterPage->GetName(), + &rTargetDocument, + false, // Connect the new master page with the new slide but + // do not modify other (master) pages. + true); + } + while (false); + + // We are not interested in any automatisms for our modified internal + // document. + rTargetDocument.SetChanged(false); + + return pNewMasterPage; +} + +SdPage* DocumentHelper::GetSlideForMasterPage (SdPage const * pMasterPage) +{ + SdPage* pCandidate = nullptr; + + SdDrawDocument* pDocument = nullptr; + if (pMasterPage != nullptr) + pDocument = dynamic_cast< SdDrawDocument* >(&pMasterPage->getSdrModelFromSdrPage()); + + // Iterate over all pages and check if it references the given master + // page. + if (pDocument!=nullptr && pDocument->GetSdPageCount(PageKind::Standard) > 0) + { + // In most cases a new slide has just been inserted so start with + // the last page. + sal_uInt16 nPageIndex (pDocument->GetSdPageCount(PageKind::Standard)-1); + bool bFound (false); + while ( ! bFound) + { + pCandidate = pDocument->GetSdPage( + nPageIndex, + PageKind::Standard); + if (pCandidate != nullptr) + { + if (static_cast(&pCandidate->TRG_GetMasterPage()) + == pMasterPage) + { + bFound = true; + break; + } + } + + if (nPageIndex == 0) + break; + else + nPageIndex --; + } + + // If no page was found, that referenced the given master page, reset + // the pointer that is returned. + if ( ! bFound) + pCandidate = nullptr; + } + + return pCandidate; +} + +SdPage* DocumentHelper::AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage) +{ + rtl::Reference pClonedMasterPage; + + if (pMasterPage!=nullptr) + { + try + { + // Duplicate the master page. + pClonedMasterPage = static_cast(pMasterPage->CloneSdrPage(rTargetDocument).get()); + + // Copy the necessary styles. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + ProvideStyles(rSourceDocument, rTargetDocument, pClonedMasterPage.get()); + + // Copy the precious flag. + pClonedMasterPage->SetPrecious(pMasterPage->IsPrecious()); + + // Now that the styles are available we can insert the cloned + // master page. + rTargetDocument.InsertMasterPage (pClonedMasterPage.get()); + } + catch(const uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + pClonedMasterPage = nullptr; + } + catch(const ::std::exception& e) + { + pClonedMasterPage = nullptr; + SAL_WARN("sd", "caught general exception " << e.what()); + } + catch(...) + { + pClonedMasterPage = nullptr; + SAL_WARN("sd", "caught general exception"); + } + } + + return pClonedMasterPage.get(); +} + +void DocumentHelper::ProvideStyles ( + SdDrawDocument const & rSourceDocument, + SdDrawDocument& rTargetDocument, + SdPage const * pPage) +{ + // Get the layout name of the given page. + OUString sLayoutName (pPage->GetLayoutName()); + sal_Int32 nIndex = sLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + sLayoutName = sLayoutName.copy(0, nIndex); + + // Copy the style sheet from source to target document. + SdStyleSheetPool* pSourceStyleSheetPool = + static_cast(rSourceDocument.GetStyleSheetPool()); + SdStyleSheetPool* pTargetStyleSheetPool = + static_cast(rTargetDocument.GetStyleSheetPool()); + StyleSheetCopyResultVector aCreatedStyles; + pTargetStyleSheetPool->CopyLayoutSheets ( + sLayoutName, + *pSourceStyleSheetPool, + aCreatedStyles); + + // Add an undo action for the copied style sheets. + if( !aCreatedStyles.empty() ) + { + SfxUndoManager* pUndoManager = rTargetDocument.GetDocSh()->GetUndoManager(); + if (pUndoManager != nullptr) + { + pUndoManager->AddUndoAction ( + std::make_unique( + &rTargetDocument, + aCreatedStyles, + true)); + } + } +} + +void DocumentHelper::AssignMasterPageToPageList ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr >& rpPageList) +{ + if (pMasterPage == nullptr || !pMasterPage->IsMasterPage()) + return; + + // Make the layout name by stripping out the layout postfix from the + // layout name of the given master page. + OUString sFullLayoutName(pMasterPage->GetLayoutName()); + OUString sBaseLayoutName (sFullLayoutName); + sal_Int32 nIndex = sBaseLayoutName.indexOf(SD_LT_SEPARATOR); + if( nIndex != -1 ) + sBaseLayoutName = sBaseLayoutName.copy(0, nIndex); + + if (rpPageList->empty()) + return; + + // Create a second list that contains only the valid pointers to + // pages for which an assignment is necessary. + ::std::vector aCleanedList; + for (const auto& rpPage : *rpPageList) + { + OSL_ASSERT(rpPage!=nullptr && &rpPage->getSdrModelFromSdrPage() == &rTargetDocument); + if (rpPage != nullptr && rpPage->GetLayoutName() != sFullLayoutName) + { + aCleanedList.push_back(rpPage); + } + } + if (aCleanedList.empty() ) + return; + + ViewShellId nViewShellId(-1); + if (sd::ViewShell* pViewShell = rTargetDocument.GetDocSh()->GetViewShell()) + nViewShellId = pViewShell->GetViewShellBase().GetViewShellId(); + SfxUndoManager* pUndoMgr = rTargetDocument.GetDocSh()->GetUndoManager(); + if( pUndoMgr ) + pUndoMgr->EnterListAction(SdResId(STR_UNDO_SET_PRESLAYOUT), OUString(), 0, nViewShellId); + + SdPage* pMasterPageInDocument = ProvideMasterPage(rTargetDocument,pMasterPage,rpPageList); + if (pMasterPageInDocument == nullptr) + return; + + // Assign the master pages to the given list of pages. + for (const auto& rpPage : aCleanedList) + { + AssignMasterPageToPage ( + pMasterPageInDocument, + sBaseLayoutName, + rpPage); + } + + if( pUndoMgr ) + pUndoMgr->LeaveListAction(); +} + +SdPage* DocumentHelper::AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage, + sal_uInt16 nInsertionIndex) +{ + rtl::Reference pClonedMasterPage; + + if (pMasterPage!=nullptr) + { + // Duplicate the master page. + pClonedMasterPage = static_cast(pMasterPage->CloneSdrPage(rTargetDocument).get()); + + // Copy the precious flag. + pClonedMasterPage->SetPrecious(pMasterPage->IsPrecious()); + + // Copy the necessary styles. + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + ProvideStyles(rSourceDocument, rTargetDocument, pClonedMasterPage.get()); + + // Now that the styles are available we can insert the cloned + // master page. + rTargetDocument.InsertMasterPage (pClonedMasterPage.get(), nInsertionIndex); + + // Adapt the size of the new master page to that of the pages in + // the document. + Size aNewSize (rTargetDocument.GetSdPage(0, pMasterPage->GetPageKind())->GetSize()); + ::tools::Rectangle aBorders ( + pClonedMasterPage->GetLeftBorder(), + pClonedMasterPage->GetUpperBorder(), + pClonedMasterPage->GetRightBorder(), + pClonedMasterPage->GetLowerBorder()); + pClonedMasterPage->ScaleObjects(aNewSize, aBorders, true); + pClonedMasterPage->SetSize(aNewSize); + pClonedMasterPage->CreateTitleAndLayout(true); + } + + return pClonedMasterPage.get(); +} + +/** In here we have to handle three cases: + 1. pPage is a normal slide. We can use SetMasterPage to assign the + master pages to it. + 2. pPage is a master page that is used by at least one slide. We can + assign the master page to these slides. + 3. pPage is a master page that is currently not used by any slide. + We can delete that page and add copies of the given master pages + instead. + + For points 2 and 3 where one master page A is assigned to another B we have + to keep in mind that the master page that page A has already been + inserted into the target document. +*/ +void DocumentHelper::AssignMasterPageToPage ( + SdPage const * pMasterPage, + std::u16string_view rsBaseLayoutName, + SdPage* pPage) +{ + // Leave early when the parameters are invalid. + if (pPage == nullptr || pMasterPage == nullptr) + return; + + SdDrawDocument& rDocument(dynamic_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + + if ( ! pPage->IsMasterPage()) + { + // 1. Remove the background object (so that, if it exists, does + // not override the new master page) and assign the master page to + // the regular slide. + rDocument.GetDocSh()->GetUndoManager()->AddUndoAction( + std::make_unique( + rDocument, *pPage, pPage->getSdrPageProperties().GetItemSet()), + true); + pPage->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + + rDocument.SetMasterPage ( + (pPage->GetPageNum()-1)/2, + rsBaseLayoutName, + &rDocument, + false, + false); + } + else + { + // Find first slide that uses the master page. + SdPage* pSlide = nullptr; + sal_uInt16 nPageCount = rDocument.GetSdPageCount(PageKind::Standard); + for (sal_uInt16 nPage=0; nPageTRG_HasMasterPage() + && &(pCandidate->TRG_GetMasterPage()) == pPage) + { + pSlide = static_cast(pCandidate); + } + } + + if (pSlide != nullptr) + { + // 2. Assign the given master pages to the first slide that was + // found above that uses the master page. + rDocument.SetMasterPage ( + (pSlide->GetPageNum()-1)/2, + rsBaseLayoutName, + &rDocument, + false, + false); + } + else + { + // 3. Replace the master page A by a copy of the given master + // page B. + rDocument.RemoveUnnecessaryMasterPages ( + pPage); + } + } +} + +SdPage* DocumentHelper::ProvideMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr >& rpPageList) +{ + // Make sure that both the master page and its notes master exist + // in the source document. If one is missing then return without + // making any changes. + if (pMasterPage == nullptr) + { + // The caller should make sure that the master page is valid. + OSL_ASSERT(pMasterPage != nullptr); + return nullptr; + } + SdDrawDocument& rSourceDocument(static_cast< SdDrawDocument& >(pMasterPage->getSdrModelFromSdrPage())); + SdPage* pNotesMasterPage = static_cast( + rSourceDocument.GetMasterPage(pMasterPage->GetPageNum()+1)); + if (pNotesMasterPage == nullptr) + { + // The model is not in a valid state. Maybe a new master page + // is being (not finished yet) created? Return without making + // any changes. + return nullptr; + } + + SdPage* pMasterPageInDocument = nullptr; + // Search for a master page with the same name as the given one in + // the target document. + const OUString sMasterPageLayoutName (pMasterPage->GetLayoutName()); + for (sal_uInt16 nIndex=0,nCount=rTargetDocument.GetMasterPageCount(); nIndex(rTargetDocument.GetMasterPage(nIndex)); + if (pCandidate && sMasterPageLayoutName == pCandidate->GetLayoutName()) + { + // The requested master page does already exist in the + // target document, return it. + return pCandidate; + } + } + + // The given master page does not already belong to the target + // document so we have to create copies and insert them into the + // target document. + + // Determine the position where the new master pages are inserted. + // By default they are inserted at the end. When we assign to a + // master page then insert after the last of the (selected) pages. + sal_uInt16 nInsertionIndex = rTargetDocument.GetMasterPageCount(); + if (rpPageList->front()->IsMasterPage()) + { + nInsertionIndex = rpPageList->back()->GetPageNum(); + } + + // Clone the master page. + if (&pMasterPage->getSdrModelFromSdrPage() != &rTargetDocument) + { + pMasterPageInDocument = AddMasterPage (rTargetDocument, pMasterPage, nInsertionIndex); + if( rTargetDocument.IsUndoEnabled() ) + rTargetDocument.AddUndo( + rTargetDocument.GetSdrUndoFactory().CreateUndoNewPage(*pMasterPageInDocument)); + } + else + pMasterPageInDocument = pMasterPage; + + // Clone the notes master. + if (&pNotesMasterPage->getSdrModelFromSdrPage() != &rTargetDocument) + { + SdPage* pClonedNotesMasterPage + = AddMasterPage (rTargetDocument, pNotesMasterPage, nInsertionIndex+1); + if( rTargetDocument.IsUndoEnabled() ) + rTargetDocument.AddUndo( + rTargetDocument.GetSdrUndoFactory().CreateUndoNewPage(*pClonedNotesMasterPage)); + } + + return pMasterPageInDocument; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/DocumentHelper.hxx b/sd/source/ui/sidebar/DocumentHelper.hxx new file mode 100644 index 000000000..61ba5f810 --- /dev/null +++ b/sd/source/ui/sidebar/DocumentHelper.hxx @@ -0,0 +1,108 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include +#include + +class SdDrawDocument; +class SdPage; + +namespace sd::sidebar { + +/** A collection of methods supporting the handling of master pages. +*/ +class DocumentHelper +{ +public: + /** Return a copy of the given master page in the given document. + */ + static SdPage* CopyMasterPageToLocalDocument ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage); + + /** Return and, when not yet present, create a slide that uses the given + master page. + */ + static SdPage* GetSlideForMasterPage (SdPage const * pMasterPage); + + /** Copy the styles used by the given page from the source document to + the target document. + */ + static void ProvideStyles ( + SdDrawDocument const & rSourceDocument, + SdDrawDocument& rTargetDocument, + SdPage const * pPage); + + /** Assign the given master page to the list of pages. + @param rTargetDocument + The document that is the owner of the pages in rPageList. + @param pMasterPage + This master page will usually be a member of the list of all + available master pages as provided by the MasterPageContainer. + @param rPageList + The pages to which to assign the master page. These pages may + be slides or master pages themselves. + */ + static void AssignMasterPageToPageList ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr >& rPageList); + +private: + static SdPage* AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage); + static SdPage* AddMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage const * pMasterPage, + sal_uInt16 nInsertionIndex); + static SdPage* ProvideMasterPage ( + SdDrawDocument& rTargetDocument, + SdPage* pMasterPage, + const std::shared_ptr >& rpPageList); + + /** Assign the given master page to the given page. + @param pMasterPage + In contrast to AssignMasterPageToPageList() this page is assumed + to be in the target document, i.e. the same document that pPage + is in. The caller will usually call AddMasterPage() to create a + clone of a master page in another document to create it. + @param rsBaseLayoutName + The layout name of the given master page. It is given so that + it has not to be created on every call. It could be generated + from the given master page, though. + @param pPage + The page to which to assign the master page. It can be a slide + or a master page itself. + */ + static void AssignMasterPageToPage ( + SdPage const * pMasterPage, + std::u16string_view rsBaseLayoutName, + SdPage* pPage); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/IDisposable.hxx b/sd/source/ui/sidebar/IDisposable.hxx new file mode 100644 index 000000000..e2c1afe27 --- /dev/null +++ b/sd/source/ui/sidebar/IDisposable.hxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +namespace sd::sidebar +{ +class IDisposable +{ +public: + virtual ~IDisposable(); +}; + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/ISidebarReceiver.hxx b/sd/source/ui/sidebar/ISidebarReceiver.hxx new file mode 100644 index 000000000..bf51cbe12 --- /dev/null +++ b/sd/source/ui/sidebar/ISidebarReceiver.hxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +namespace sd::sidebar +{ +class ISidebarReceiver +{ +public: + virtual ~ISidebarReceiver(); +}; + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/LayoutMenu.cxx b/sd/source/ui/sidebar/LayoutMenu.cxx new file mode 100644 index 000000000..23521df0e --- /dev/null +++ b/sd/source/ui/sidebar/LayoutMenu.cxx @@ -0,0 +1,728 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "LayoutMenu.hxx" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using namespace ::sd::slidesorter; +using ::sd::framework::FrameworkHelper; + +namespace sd::sidebar { + +namespace { + +struct snew_slide_value_info +{ + rtl::OUStringConstExpr msBmpResId; + TranslateId mpStrResId; + WritingMode meWritingMode; + AutoLayout maAutoLayout; +}; + +} + +constexpr OUStringLiteral EMPTY = u""; + +const snew_slide_value_info notes[] = +{ + {BMP_SLIDEN_01, STR_AUTOLAYOUT_NOTES, WritingMode_LR_TB, + AUTOLAYOUT_NOTES}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info handout[] = +{ + {BMP_SLIDEH_01, STR_AUTOLAYOUT_HANDOUT1, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT1}, + {BMP_SLIDEH_02, STR_AUTOLAYOUT_HANDOUT2, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT2}, + {BMP_SLIDEH_03, STR_AUTOLAYOUT_HANDOUT3, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT3}, + {BMP_SLIDEH_04, STR_AUTOLAYOUT_HANDOUT4, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT4}, + {BMP_SLIDEH_06, STR_AUTOLAYOUT_HANDOUT6, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT6}, + {BMP_SLIDEH_09, STR_AUTOLAYOUT_HANDOUT9, WritingMode_LR_TB, + AUTOLAYOUT_HANDOUT9}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE}, +}; + +const snew_slide_value_info standard[] = +{ + {BMP_LAYOUT_EMPTY, STR_AUTOLAYOUT_NONE, WritingMode_LR_TB, AUTOLAYOUT_NONE}, + {BMP_LAYOUT_HEAD03, STR_AUTOLAYOUT_TITLE, WritingMode_LR_TB, AUTOLAYOUT_TITLE}, + {BMP_LAYOUT_HEAD02, STR_AUTOLAYOUT_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT}, + {BMP_LAYOUT_HEAD02A, STR_AUTOLAYOUT_2CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT}, + {BMP_LAYOUT_HEAD01, STR_AUTOLAYOUT_ONLY_TITLE, WritingMode_LR_TB, AUTOLAYOUT_TITLE_ONLY}, + {BMP_LAYOUT_TEXTONLY, STR_AUTOLAYOUT_ONLY_TEXT, WritingMode_LR_TB, AUTOLAYOUT_ONLY_TEXT}, + {BMP_LAYOUT_HEAD03B, STR_AUTOLAYOUT_2CONTENT_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT_CONTENT}, + {BMP_LAYOUT_HEAD03C, STR_AUTOLAYOUT_CONTENT_2CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT_2CONTENT}, + {BMP_LAYOUT_HEAD03A, STR_AUTOLAYOUT_2CONTENT_OVER_CONTENT,WritingMode_LR_TB, AUTOLAYOUT_TITLE_2CONTENT_OVER_CONTENT}, + {BMP_LAYOUT_HEAD02B, STR_AUTOLAYOUT_CONTENT_OVER_CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_CONTENT_OVER_CONTENT}, + {BMP_LAYOUT_HEAD04, STR_AUTOLAYOUT_4CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_4CONTENT}, + {BMP_LAYOUT_HEAD06, STR_AUTOLAYOUT_6CONTENT, WritingMode_LR_TB, AUTOLAYOUT_TITLE_6CONTENT}, + + // vertical + {BMP_LAYOUT_VERTICAL02, STR_AL_VERT_TITLE_TEXT_CHART, WritingMode_TB_RL, AUTOLAYOUT_VTITLE_VCONTENT_OVER_VCONTENT}, + {BMP_LAYOUT_VERTICAL01, STR_AL_VERT_TITLE_VERT_OUTLINE, WritingMode_TB_RL, AUTOLAYOUT_VTITLE_VCONTENT}, + {BMP_LAYOUT_HEAD02, STR_AL_TITLE_VERT_OUTLINE, WritingMode_TB_RL, AUTOLAYOUT_TITLE_VCONTENT}, + {BMP_LAYOUT_HEAD02A, STR_AL_TITLE_VERT_OUTLINE_CLIPART, WritingMode_TB_RL, AUTOLAYOUT_TITLE_2VTEXT}, + {EMPTY, {}, WritingMode_LR_TB, AUTOLAYOUT_NONE} +}; + +class LayoutValueSet : public ValueSet +{ +private: + LayoutMenu& mrMenu; + + /** Calculate the number of displayed rows. This depends on the given + item size, the given number of columns, and the size of the + control. Note that this is not the number of rows managed by the + valueset. This number may be larger. In that case a vertical + scroll bar is displayed. + */ + int CalculateRowCount(const Size& rItemSize, int nColumnCount); + +public: + LayoutValueSet(LayoutMenu& rMenu) + : ValueSet(nullptr) + , mrMenu(rMenu) + { + } + + virtual void Resize() override; + + virtual bool Command(const CommandEvent& rEvent) override; +}; + +LayoutMenu::LayoutMenu ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference& rxSidebar) + : PanelLayout( pParent, "LayoutPanel", "modules/simpress/ui/layoutpanel.ui" ), + mrBase(rViewShellBase), + mxLayoutValueSet(new LayoutValueSet(*this)), + mxLayoutValueSetWin(new weld::CustomWeld(*m_xBuilder, "layoutvalueset", *mxLayoutValueSet)), + mbIsMainViewChangePending(false), + mxSidebar(rxSidebar), + mbIsDisposed(false) +{ + implConstruct( *mrBase.GetDocument()->GetDocSh() ); + SAL_INFO("sd.ui", "created LayoutMenu at " << this); + + mxLayoutValueSet->SetStyle(mxLayoutValueSet->GetStyle() | WB_ITEMBORDER | WB_FLATVALUESET | WB_TABSTOP); + + mxLayoutValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); +} + +void LayoutMenu::implConstruct( DrawDocShell& rDocumentShell ) +{ + OSL_ENSURE( mrBase.GetDocument()->GetDocSh() == &rDocumentShell, + "LayoutMenu::implConstruct: hmm?" ); + // if this fires, then my assumption that the rDocumentShell parameter to our first ctor is superfluous ... + (void) rDocumentShell; + + mxLayoutValueSet->SetStyle ( + ( mxLayoutValueSet->GetStyle() & ~(WB_ITEMBORDER) ) + | WB_TABSTOP + | WB_MENUSTYLEVALUESET + | WB_NO_DIRECTSELECT + ); + mxLayoutValueSet->SetExtraSpacing(2); + mxLayoutValueSet->SetSelectHdl (LINK(this, LayoutMenu, ClickHandler)); + InvalidateContent(); + + Link<::sd::tools::EventMultiplexerEvent&,void> aEventListenerLink (LINK(this,LayoutMenu,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->AddEventListener(aEventListenerLink); + + mxLayoutValueSet->SetHelpId(HID_SD_TASK_PANE_PREVIEW_LAYOUTS); + mxLayoutValueSet->SetAccessibleName(SdResId(STR_TASKPANEL_LAYOUT_MENU_TITLE)); + + Link aStateChangeLink (LINK(this,LayoutMenu,StateChangeHandler)); + mxListener = new ::sd::tools::SlotStateListener( + aStateChangeLink, + Reference(mrBase.GetController()->getFrame(), UNO_QUERY), + ".uno:VerticalTextState"); +} + +LayoutMenu::~LayoutMenu() +{ + SAL_INFO("sd.ui", "destroying LayoutMenu at " << this); + Dispose(); + mxLayoutValueSetWin.reset(); + mxLayoutValueSet.reset(); +} + +void LayoutMenu::Dispose() +{ + if (mbIsDisposed) + return; + + SAL_INFO("sd.ui", "disposing LayoutMenu at " << this); + + mbIsDisposed = true; + + Reference xComponent (mxListener, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + + Clear(); + Link aLink (LINK(this,LayoutMenu,EventMultiplexerListener)); + mrBase.GetEventMultiplexer()->RemoveEventListener (aLink); +} + +AutoLayout LayoutMenu::GetSelectedAutoLayout() const +{ + AutoLayout aResult = AUTOLAYOUT_NONE; + + if (!mxLayoutValueSet->IsNoSelection() && mxLayoutValueSet->GetSelectedItemId()!=0) + { + AutoLayout* pLayout = static_cast(mxLayoutValueSet->GetItemData(mxLayoutValueSet->GetSelectedItemId())); + if (pLayout != nullptr) + aResult = *pLayout; + } + + return aResult; +} + +ui::LayoutSize LayoutMenu::GetHeightForWidth (const sal_Int32 nWidth) +{ + sal_Int32 nPreferredHeight = 200; + if (mxLayoutValueSet->GetItemCount()>0) + { + Image aImage = mxLayoutValueSet->GetItemImage(mxLayoutValueSet->GetItemId(0)); + Size aItemSize = mxLayoutValueSet->CalcItemSizePixel(aImage.GetSizePixel()); + if (nWidth>0 && aItemSize.Width()>0) + { + aItemSize.AdjustWidth(8 ); + aItemSize.AdjustHeight(8 ); + int nColumnCount = nWidth / aItemSize.Width(); + if (nColumnCount <= 0) + nColumnCount = 1; + else if (nColumnCount > 4) + nColumnCount = 4; + int nRowCount = (mxLayoutValueSet->GetItemCount() + nColumnCount-1) / nColumnCount; + nPreferredHeight = nRowCount * aItemSize.Height(); + } + } + return ui::LayoutSize(nPreferredHeight,nPreferredHeight,nPreferredHeight); +} + +void LayoutValueSet::Resize() +{ + Size aWindowSize = GetOutputSizePixel(); + if (IsVisible() && aWindowSize.Width() > 0) + { + // Calculate the number of rows and columns. + if (GetItemCount() > 0) + { + Image aImage = GetItemImage(GetItemId(0)); + Size aItemSize = CalcItemSizePixel ( + aImage.GetSizePixel()); + aItemSize.AdjustWidth(8 ); + aItemSize.AdjustHeight(8 ); + int nColumnCount = aWindowSize.Width() / aItemSize.Width(); + if (nColumnCount < 1) + nColumnCount = 1; + else if (nColumnCount > 4) + nColumnCount = 4; + + int nRowCount = CalculateRowCount (aItemSize, nColumnCount); + + SetColCount(nColumnCount); + SetLineCount(nRowCount); + } + } + + ValueSet::Resize(); +} + +bool LayoutValueSet::Command(const CommandEvent& rEvent) +{ + if (rEvent.GetCommand() != CommandEventId::ContextMenu) + return false; + + // As a preparation for the context menu the item under the mouse is + // selected. + if (rEvent.IsMouseEvent()) + { + sal_uInt16 nIndex = GetItemId(rEvent.GetMousePosPixel()); + if (nIndex > 0) + SelectItem(nIndex); + } + + mrMenu.ShowContextMenu(rEvent.IsMouseEvent() ? &rEvent.GetMousePosPixel() : nullptr); + return true; +} + +void LayoutMenu::InsertPageWithLayout (AutoLayout aLayout) +{ + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + return; + + SfxViewFrame* pViewFrame = mrBase.GetViewFrame(); + if (pViewFrame == nullptr) + return; + + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if (pDispatcher == nullptr) + return; + + // Call SID_INSERTPAGE with the right arguments. This is because + // the popup menu can not call this slot with arguments directly. + SfxRequest aRequest (CreateRequest(SID_INSERTPAGE, aLayout)); + if (aRequest.GetArgs() != nullptr) + { + pDispatcher->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + *aRequest.GetArgs()); + } + UpdateSelection(); +} + +void LayoutMenu::InvalidateContent() +{ + // Throw away the current set and fill the menu anew according to the + // current settings (this includes the support for vertical writing.) + Fill(); + + if (mxSidebar.is()) + mxSidebar->requestLayout(); + + // set selection inside the control during Impress start up + UpdateSelection(); +} + +int LayoutValueSet::CalculateRowCount (const Size&, int nColumnCount) +{ + int nRowCount = 0; + + if (GetItemCount() > 0 && nColumnCount > 0) + { + nRowCount = (GetItemCount() + nColumnCount - 1) / nColumnCount; + if (nRowCount < 1) + nRowCount = 1; + } + + return nRowCount; +} + +IMPL_LINK_NOARG(LayoutMenu, ClickHandler, ValueSet*, void) +{ + AssignLayoutToSelectedSlides( GetSelectedAutoLayout() ); +} + +/** The specified layout is assigned to the current page of the view shell + in the center pane. +*/ +void LayoutMenu::AssignLayoutToSelectedSlides (AutoLayout aLayout) +{ + using namespace ::sd::slidesorter; + using namespace ::sd::slidesorter::controller; + + do + { + // The view shell in the center pane has to be present. + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell == nullptr) + break; + + // Determine if the current view is in an invalid master page mode. + // The handout view is always in master page mode and therefore not + // invalid. + bool bMasterPageMode (false); + switch (pMainViewShell->GetShellType()) + { + case ViewShell::ST_NOTES: + case ViewShell::ST_IMPRESS: + { + DrawViewShell* pDrawViewShell = static_cast(pMainViewShell); + if (pDrawViewShell->GetEditMode() == EditMode::MasterPage) + bMasterPageMode = true; + break; + } + default: + break; + } + if (bMasterPageMode) + break; + + // Get a list of all selected slides and call the SID_MODIFYPAGE + // slot for all of them. + ::sd::slidesorter::SharedPageSelection pPageSelection; + + // Get a list of selected pages. + // First we try to obtain this list from a slide sorter. This is + // possible only some of the view shells in the center pane. When + // no valid slide sorter is available then ask the main view shell + // for its current page. + SlideSorterViewShell* pSlideSorter = nullptr; + switch (pMainViewShell->GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_NOTES: + case ViewShell::ST_SLIDE_SORTER: + pSlideSorter = SlideSorterViewShell::GetSlideSorter(mrBase); + break; + default: + break; + } + if (pSlideSorter != nullptr) + { + // There is a slide sorter visible so get the list of selected pages from it. + pPageSelection = pSlideSorter->GetPageSelection(); + } + + if( (pSlideSorter == nullptr) || !pPageSelection || pPageSelection->empty() ) + { + // No valid slide sorter available. Ask the main view shell for + // its current page. + pPageSelection = std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>(); + pPageSelection->push_back(pMainViewShell->GetActualPage()); + } + + if (pPageSelection->empty()) + break; + + for (const auto& rpPage : *pPageSelection) + { + if (rpPage == nullptr) + continue; + + // Call the SID_ASSIGN_LAYOUT slot with all the necessary parameters. + SfxRequest aRequest (mrBase.GetViewFrame(), SID_ASSIGN_LAYOUT); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATPAGE, (rpPage->GetPageNum()-1)/2)); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATLAYOUT, aLayout)); + pMainViewShell->ExecuteSlot (aRequest, false); + } + } + while(false); +} + +SfxRequest LayoutMenu::CreateRequest ( + sal_uInt16 nSlotId, + AutoLayout aLayout) +{ + SfxRequest aRequest (mrBase.GetViewFrame(), nSlotId); + + do + { + SdrLayerAdmin& rLayerAdmin (mrBase.GetDocument()->GetLayerAdmin()); + SdrLayerID aBackground (rLayerAdmin.GetLayerID(sUNO_LayerName_background)); + SdrLayerID aBackgroundObject (rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects)); + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + break; + SdPage* pPage = pViewShell->GetActualPage(); + if (pPage == nullptr) + break; + + SdrLayerIDSet aVisibleLayers (pPage->TRG_GetMasterPageVisibleLayers()); + + aRequest.AppendItem( + SfxStringItem (ID_VAL_PAGENAME, OUString()));//pPage->GetName())); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATLAYOUT, aLayout)); + aRequest.AppendItem( + SfxBoolItem(ID_VAL_ISPAGEBACK, aVisibleLayers.IsSet(aBackground))); + aRequest.AppendItem( + SfxBoolItem( + ID_VAL_ISPAGEOBJ, + aVisibleLayers.IsSet(aBackgroundObject))); + } + while (false); + + return aRequest; +} + +void LayoutMenu::Fill() +{ + bool bVertical = SvtCJKOptions::IsVerticalTextEnabled(); + SdDrawDocument* pDocument = mrBase.GetDocument(); + bool bRightToLeft = (pDocument!=nullptr + && pDocument->GetDefaultWritingMode() == WritingMode_RL_TB); + + // Get URL of the view in the center pane. + OUString sCenterPaneViewName; + try + { + Reference xControllerManager ( + Reference(&mrBase.GetDrawController()), UNO_QUERY_THROW); + Reference xPaneId (ResourceId::create( + ::comphelper::getProcessComponentContext(), + FrameworkHelper::msCenterPaneURL)); + Reference xView (FrameworkHelper::Instance(mrBase)->GetView(xPaneId)); + if (xView.is()) + sCenterPaneViewName = xView->getResourceId()->getResourceURL(); + } + catch (RuntimeException&) + {} + + const snew_slide_value_info* pInfo = nullptr; + if (sCenterPaneViewName == framework::FrameworkHelper::msNotesViewURL) + { + pInfo = notes; + } + else if (sCenterPaneViewName == framework::FrameworkHelper::msHandoutViewURL) + { + pInfo = handout; + } + else if (sCenterPaneViewName == framework::FrameworkHelper::msImpressViewURL + || sCenterPaneViewName == framework::FrameworkHelper::msSlideSorterURL) + { + pInfo = standard; + } + else + { + pInfo = nullptr; + } + + Clear(); + for (sal_uInt16 i=1; pInfo!=nullptr && pInfo->mpStrResId; i++, pInfo++) + { + if ((WritingMode_TB_RL != pInfo->meWritingMode) || bVertical) + { + Image aImg("private:graphicrepository/" + static_cast(pInfo->msBmpResId)); + + if (bRightToLeft && (WritingMode_TB_RL != pInfo->meWritingMode)) + { // FIXME: avoid interpolating RTL layouts. + BitmapEx aRTL = aImg.GetBitmapEx(); + aRTL.Mirror(BmpMirrorFlags::Horizontal); + aImg = Image(aRTL); + } + + mxLayoutValueSet->InsertItem(i, aImg, SdResId(pInfo->mpStrResId)); + mxLayoutValueSet->SetItemData (i, new AutoLayout(pInfo->maAutoLayout)); + } + } +} + +void LayoutMenu::Clear() +{ + for (size_t nId=1; nId<=mxLayoutValueSet->GetItemCount(); nId++) + delete static_cast(mxLayoutValueSet->GetItemData(nId)); + mxLayoutValueSet->Clear(); +} + +void LayoutMenu::ShowContextMenu(const Point* pPos) +{ + if (SD_MOD()->GetWaterCan()) + return; + + // Determine the position where to show the menu. + Point aMenuPosition; + if (pPos) + { + auto nItemId = mxLayoutValueSet->GetItemId(*pPos); + if (nItemId <= 0) + return; + mxLayoutValueSet->SelectItem(nItemId); + aMenuPosition = *pPos; + } + else + { + if (mxLayoutValueSet->GetSelectedItemId() == sal_uInt16(-1)) + return; + ::tools::Rectangle aBBox(mxLayoutValueSet->GetItemRect(mxLayoutValueSet->GetSelectedItemId())); + aMenuPosition = aBBox.Center(); + } + + // Setup the menu. + ::tools::Rectangle aRect(aMenuPosition, Size(1, 1)); + weld::Widget* pPopupParent = mxLayoutValueSet->GetDrawingArea(); + std::unique_ptr xBuilder(Application::CreateBuilder(pPopupParent, "modules/simpress/ui/layoutmenu.ui")); + std::unique_ptr xMenu(xBuilder->weld_menu("menu")); + + // Disable the SID_INSERTPAGE_LAYOUT_MENU item when + // the document is read-only. + const SfxPoolItem* pItem = nullptr; + const SfxItemState aState ( + mrBase.GetViewFrame()->GetDispatcher()->QueryState(SID_INSERTPAGE, pItem)); + if (aState == SfxItemState::DISABLED) + xMenu->set_sensitive("insert", false); + + // Show the menu. + OnMenuItemSelected(xMenu->popup_at_rect(pPopupParent, aRect)); +} + +IMPL_LINK_NOARG(LayoutMenu, StateChangeHandler, const OUString&, void) +{ + InvalidateContent(); +} + +void LayoutMenu::OnMenuItemSelected(std::string_view ident) +{ + if (ident.empty()) + return; + + if (ident == "apply") + { + AssignLayoutToSelectedSlides(GetSelectedAutoLayout()); + } + else if (ident == "insert") + { + // Add arguments to this slot and forward it to the main view + // shell. + InsertPageWithLayout(GetSelectedAutoLayout()); + } +} + +// Selects an appropriate layout of the slide inside control. +// +// Method may be called several times with the same item-id to be selected - +// only once the actually state of the control will be changed. +// +void LayoutMenu::UpdateSelection() +{ + bool bItemSelected = false; + + do + { + // Get current page of main view. + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + break; + + SdPage* pCurrentPage = pViewShell->getCurrentPage(); + if (pCurrentPage == nullptr) + break; + + // Get layout of current page. + AutoLayout aLayout (pCurrentPage->GetAutoLayout()); + if (aLayoutAUTOLAYOUT_END) + break; + + // Find the entry of the menu for to the layout. + const sal_uInt16 nItemCount = mxLayoutValueSet->GetItemCount(); + for (sal_uInt16 nId=1; nId<=nItemCount; nId++) + { + if (*static_cast(mxLayoutValueSet->GetItemData(nId)) == aLayout) + { + // do not set selection twice to the same item + if (mxLayoutValueSet->GetSelectedItemId() != nId) + { + mxLayoutValueSet->SetNoSelection(); + mxLayoutValueSet->SelectItem(nId); + } + + bItemSelected = true; // no need to call SetNoSelection() + break; + } + } + } + while (false); + + if (!bItemSelected) + mxLayoutValueSet->SetNoSelection(); +} + +IMPL_LINK(LayoutMenu, EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + // tdf#89890 During changes of the Layout of the slide when focus is not set inside main area + // we do not receive notification EventMultiplexerEventId::CurrentPageChanged, but we receive the following 3 notification types. + // => let's make UpdateSelection() also when some shape is changed (during Layout changes) + case EventMultiplexerEventId::ShapeChanged: + case EventMultiplexerEventId::ShapeInserted: + case EventMultiplexerEventId::ShapeRemoved: + UpdateSelection(); + break; + + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::SlideSortedSelection: + UpdateSelection(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxLayoutValueSet->Invalidate(); // redraw without focus + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending) + { + mbIsMainViewChangePending = false; + InvalidateContent(); + } + break; + + default: + break; + } +} + +void LayoutMenu::DataChanged(const DataChangedEvent& rEvent) +{ + PanelLayout::DataChanged(rEvent); + Fill(); + mxLayoutValueSet->StyleUpdated(); + mxLayoutValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); +} + +} // end of namespace ::sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/LayoutMenu.hxx b/sd/source/ui/sidebar/LayoutMenu.hxx new file mode 100644 index 000000000..4cc916858 --- /dev/null +++ b/sd/source/ui/sidebar/LayoutMenu.hxx @@ -0,0 +1,157 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace com::sun::star::frame +{ +class XStatusListener; +} +namespace com::sun::star::ui +{ +class XSidebar; +} + +namespace sd +{ +class DrawDocShell; +class ViewShellBase; +} + +namespace sd::tools +{ +class EventMultiplexerEvent; +} + +namespace sd::sidebar +{ +class LayoutValueSet; + +class LayoutMenu : public PanelLayout, public sfx2::sidebar::ILayoutableWindow +{ +public: + /** Create a new layout menu. Depending on the given flag it + displays its own scroll bar or lets a surrounding window + handle that. + @param i_pParent + the parent node in the control tree + @param i_rPanelViewShell + the view shell of the task pane. + */ + LayoutMenu(weld::Widget* pParent, ViewShellBase& rViewShellBase, + const css::uno::Reference& rxSidebar); + virtual ~LayoutMenu() override; + + void Dispose(); + + /** Return a numerical value representing the currently selected + layout. + */ + AutoLayout GetSelectedAutoLayout() const; + + // From ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth(const sal_Int32 nWidth) override; + + /** Call this method when the set of displayed layouts is not up-to-date + anymore. It will re-assemble this set according to the current + settings. + */ + void InvalidateContent(); + + /** The context menu is requested over this ShowContextMenu() method. + */ + void ShowContextMenu(const Point* pPos); + + /** Call Fill() when switching to or from high contrast mode so that the + correct set of icons is displayed. + */ + virtual void DataChanged(const DataChangedEvent& rEvent) override; + +private: + ViewShellBase& mrBase; + + std::unique_ptr mxLayoutValueSet; + std::unique_ptr mxLayoutValueSetWin; + + /** If we are asked for the preferred window size, then use this + many columns for the calculation. + */ + css::uno::Reference mxListener; + bool mbIsMainViewChangePending; + css::uno::Reference mxSidebar; + bool mbIsDisposed; + + /** Fill the value set with the layouts that are applicable to the + current main view shell. + */ + void Fill(); + + /** Remove all items from the value set. + */ + void Clear(); + + /** Assign the given layout to all selected slides of a slide sorter. + If no slide sorter is active then this call is ignored. The slide + sorter in the center pane is preferred if the choice exists. + */ + void AssignLayoutToSelectedSlides(AutoLayout aLayout); + + /** Insert a new page with the given layout. The page is inserted via + the main view shell, i.e. its SID_INSERTPAGE slot is called. If it + does not support this slot then inserting a new page does not take + place. The new page is inserted after the currently active one (the + one returned by ViewShell::GetActualPage().) + */ + void InsertPageWithLayout(AutoLayout aLayout); + + /** Create a request structure that can be used with the SID_INSERTPAGE + and SID_MODIFYPAGE slots. The parameters are set so that the given + layout is assigned to the current page of the main view shell. + @param nSlotId + Supported slots are SID_INSERTPAGE and SID_MODIFYPAGE. + @param aLayout + Layout of the page to insert or to assign. + */ + SfxRequest CreateRequest(sal_uInt16 nSlotId, AutoLayout aLayout); + + /** Select the layout that is used by the current page. + */ + void UpdateSelection(); + + // internal ctor + void implConstruct(DrawDocShell& rDocumentShell); + + /** When clicked then set the current page of the view in the center pane. + */ + DECL_LINK(ClickHandler, ValueSet*, void); + DECL_LINK(StateChangeHandler, const OUString&, void); + DECL_LINK(EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, void); + void OnMenuItemSelected(std::string_view ident); +}; + +} // end of namespace ::sd::toolpanel + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainer.cxx b/sd/source/ui/sidebar/MasterPageContainer.cxx new file mode 100644 index 000000000..20d852807 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainer.cxx @@ -0,0 +1,958 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "MasterPageContainer.hxx" + +#include "MasterPageContainerProviders.hxx" +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerFiller.hxx" +#include "MasterPageContainerQueue.hxx" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +typedef ::std::vector MasterPageContainerType; + +} // end of anonymous namespace + +namespace sd::sidebar { + +/** Inner implementation class of the MasterPageContainer. +*/ +class MasterPageContainer::Implementation + : public SdGlobalResource, + public MasterPageContainerFiller::ContainerAdapter, + public MasterPageContainerQueue::ContainerAdapter +{ +public: + mutable ::osl::Mutex maMutex; + + static std::weak_ptr mpInstance; + MasterPageContainerType maContainer; + + static std::shared_ptr Instance(); + + void LateInit(); + void AddChangeListener (const Link& rLink); + void RemoveChangeListener (const Link& rLink); + void UpdatePreviewSizePixel(); + const Size& GetPreviewSizePixel (PreviewSize eSize) const; + + bool HasToken (Token aToken) const; + SharedMasterPageDescriptor GetDescriptor (MasterPageContainer::Token aToken) const; + virtual Token PutMasterPage (const SharedMasterPageDescriptor& rDescriptor) override; + void InvalidatePreview (Token aToken); + Image GetPreviewForToken ( + Token aToken, + PreviewSize ePreviewSize); + PreviewState GetPreviewState (Token aToken) const; + bool RequestPreview (Token aToken); + + Reference GetModel(); + SdDrawDocument* GetDocument(); + + void FireContainerChange ( + MasterPageContainerChangeEvent::EventType eType, + Token aToken); + + virtual bool UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) override; + + void ReleaseDescriptor (Token aToken); + + /** Called by the MasterPageContainerFiller to notify that all master + pages from template documents have been added. + */ + virtual void FillingDone() override; + +private: + Implementation(); + virtual ~Implementation() override; + + class Deleter { public: + void operator() (Implementation* pObject) { delete pObject; } + }; + friend class Deleter; + + enum class InitializationState { NotInitialized, Initializing, Initialized }; + InitializationState meInitializationState; + + std::unique_ptr mpRequestQueue; + css::uno::Reference mxModel; + SdDrawDocument* mpDocument; + PreviewRenderer maPreviewRenderer; + /** Remember whether the first page object has already been used to + determine the correct size ratio. + */ + bool mbFirstPageObjectSeen; + + // The widths for the previews contain two pixels for the border that is + // painted around the preview. + static const int SMALL_PREVIEW_WIDTH = 72 + 2; + static const int LARGE_PREVIEW_WIDTH = 2*72 + 2; + + /** This substitution of page preview shows "Preparing preview" and is + shown as long as the actual previews are not being present. + */ + Image maLargePreviewBeingCreated; + Image maSmallPreviewBeingCreated; + + /** This substitution of page preview is shown when a preview can not be + created and thus is not available. + */ + Image maLargePreviewNotAvailable; + Image maSmallPreviewNotAvailable; + + ::std::vector> maChangeListeners; + + // We have to remember the tasks for initialization and filling in case + // a MasterPageContainer object is destroyed before these tasks have + // been completed. + std::weak_ptr mpFillerTask; + + Size maSmallPreviewSizePixel; + Size maLargePreviewSizePixel; + + Image GetPreviewSubstitution(TranslateId pId, PreviewSize ePreviewSize); + + void CleanContainer(); +}; + +//===== MasterPageContainer =================================================== + +std::weak_ptr + MasterPageContainer::Implementation::mpInstance; + +std::shared_ptr + MasterPageContainer::Implementation::Instance() +{ + std::shared_ptr pInstance; + + if (Implementation::mpInstance.expired()) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (Implementation::mpInstance.expired()) + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pInstance = std::shared_ptr( + new MasterPageContainer::Implementation(), + MasterPageContainer::Implementation::Deleter()); + SdGlobalResourceContainer::Instance().AddResource(pInstance); + Implementation::mpInstance = pInstance; + } + else + pInstance = std::shared_ptr( + Implementation::mpInstance); + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + pInstance = std::shared_ptr( + Implementation::mpInstance); + } + + DBG_ASSERT(pInstance != nullptr, + "MasterPageContainer::Implementation::Instance(): instance is nullptr"); + return pInstance; +} + +MasterPageContainer::MasterPageContainer() + : mpImpl(Implementation::Instance()), + mePreviewSize(SMALL) +{ + mpImpl->LateInit(); +} + +MasterPageContainer::~MasterPageContainer() +{ +} + +void MasterPageContainer::AddChangeListener (const Link& rLink) +{ + mpImpl->AddChangeListener(rLink); +} + +void MasterPageContainer::RemoveChangeListener (const Link& rLink) +{ + mpImpl->RemoveChangeListener(rLink); +} + +void MasterPageContainer::SetPreviewSize (PreviewSize eSize) +{ + mePreviewSize = eSize; + mpImpl->FireContainerChange( + MasterPageContainerChangeEvent::EventType::SIZE_CHANGED, + NIL_TOKEN); +} + +Size const & MasterPageContainer::GetPreviewSizePixel() const +{ + return mpImpl->GetPreviewSizePixel(mePreviewSize); +} + +MasterPageContainer::Token MasterPageContainer::PutMasterPage ( + const std::shared_ptr& rDescriptor) +{ + return mpImpl->PutMasterPage(rDescriptor); +} + +void MasterPageContainer::AcquireToken (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + { + ++pDescriptor->mnUseCount; + } +} + +void MasterPageContainer::ReleaseToken (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (!pDescriptor) + return; + + OSL_ASSERT(pDescriptor->mnUseCount>0); + --pDescriptor->mnUseCount; + if (pDescriptor->mnUseCount > 0) + return; + + switch (pDescriptor->meOrigin) + { + case DEFAULT: + case TEMPLATE: + default: + break; + + case MASTERPAGE: + mpImpl->ReleaseDescriptor(aToken); + break; + } +} + +int MasterPageContainer::GetTokenCount() const +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->maContainer.size(); +} + +bool MasterPageContainer::HasToken (Token aToken) const +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->HasToken(aToken); +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForIndex (int nIndex) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (HasToken(nIndex)) + aResult = mpImpl->maContainer[nIndex]->maToken; + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForURL ( + const OUString& sURL) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (!sURL.isEmpty()) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::URLComparator(sURL))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForStyleName (const OUString& sStyleName) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (!sStyleName.isEmpty()) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::StyleNameComparator(sStyleName))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +MasterPageContainer::Token MasterPageContainer::GetTokenForPageObject ( + const SdPage* pPage) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Token aResult (NIL_TOKEN); + if (pPage != nullptr) + { + MasterPageContainerType::iterator iEntry ( + ::std::find_if ( + mpImpl->maContainer.begin(), + mpImpl->maContainer.end(), + MasterPageDescriptor::PageObjectComparator(pPage))); + if (iEntry != mpImpl->maContainer.end()) + aResult = (*iEntry)->maToken; + } + return aResult; +} + +OUString MasterPageContainer::GetURLForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msURL; + else + return OUString(); +} + +OUString MasterPageContainer::GetPageNameForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msPageName; + else + return OUString(); +} + +OUString MasterPageContainer::GetStyleNameForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->msStyleName; + else + return OUString(); +} + +SdPage* MasterPageContainer::GetPageObjectForToken ( + MasterPageContainer::Token aToken, + bool bLoad) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SdPage* pPageObject = nullptr; + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + { + pPageObject = pDescriptor->mpMasterPage; + if (pPageObject == nullptr) + { + // The page object is not (yet) present. Call + // UpdateDescriptor() to trigger the PageObjectProvider() to + // provide it. + if (bLoad) + mpImpl->GetModel(); + if (mpImpl->UpdateDescriptor(pDescriptor,bLoad,false, true)) + pPageObject = pDescriptor->mpMasterPage; + } + } + return pPageObject; +} + +MasterPageContainer::Origin MasterPageContainer::GetOriginForToken (Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->meOrigin; + else + return UNKNOWN; +} + +sal_Int32 MasterPageContainer::GetTemplateIndexForToken (Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + SharedMasterPageDescriptor pDescriptor = mpImpl->GetDescriptor(aToken); + if (pDescriptor) + return pDescriptor->mnTemplateIndex; + else + return -1; +} + +std::shared_ptr MasterPageContainer::GetDescriptorForToken ( + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (mpImpl->maMutex); + + return mpImpl->GetDescriptor(aToken); +} + +void MasterPageContainer::InvalidatePreview (MasterPageContainer::Token aToken) +{ + mpImpl->InvalidatePreview(aToken); +} + +Image MasterPageContainer::GetPreviewForToken (MasterPageContainer::Token aToken) +{ + return mpImpl->GetPreviewForToken(aToken,mePreviewSize); +} + +MasterPageContainer::PreviewState MasterPageContainer::GetPreviewState (Token aToken) +{ + return mpImpl->GetPreviewState(aToken); +} + +bool MasterPageContainer::RequestPreview (Token aToken) +{ + return mpImpl->RequestPreview(aToken); +} + +//==== Implementation ================================================ + +MasterPageContainer::Implementation::Implementation() + : meInitializationState(InitializationState::NotInitialized), + mpDocument(nullptr), + mbFirstPageObjectSeen(false) +{ + UpdatePreviewSizePixel(); +} + +MasterPageContainer::Implementation::~Implementation() +{ + // When the initializer or filler tasks are still running then we have + // to stop them now in order to prevent them from calling us back. + tools::TimerBasedTaskExecution::ReleaseTask(mpFillerTask); + + mpRequestQueue.reset(); + + uno::Reference xCloseable (mxModel, uno::UNO_QUERY); + if (xCloseable.is()) + { + try + { + xCloseable->close(true); + } + catch (const css::util::CloseVetoException&) + { + } + } + mxModel = nullptr; +} + +void MasterPageContainer::Implementation::LateInit() +{ + const ::osl::MutexGuard aGuard (maMutex); + + if (meInitializationState != InitializationState::NotInitialized) + return; + + meInitializationState = InitializationState::Initializing; + + OSL_ASSERT(Instance().get()==this); + mpRequestQueue.reset(MasterPageContainerQueue::Create( + std::shared_ptr(Instance()))); + + mpFillerTask = ::sd::tools::TimerBasedTaskExecution::Create( + std::make_shared(*this), + 5, + 50); + + meInitializationState = InitializationState::Initialized; +} + +void MasterPageContainer::Implementation::AddChangeListener (const Link& rLink) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ::std::vector>::iterator iListener ( + ::std::find(maChangeListeners.begin(),maChangeListeners.end(),rLink)); + if (iListener == maChangeListeners.end()) + maChangeListeners.push_back(rLink); + +} + +void MasterPageContainer::Implementation::RemoveChangeListener (const Link& rLink) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ::std::vector>::iterator iListener ( + ::std::find(maChangeListeners.begin(),maChangeListeners.end(),rLink)); + if (iListener != maChangeListeners.end()) + maChangeListeners.erase(iListener); +} + +void MasterPageContainer::Implementation::UpdatePreviewSizePixel() +{ + const ::osl::MutexGuard aGuard (maMutex); + + // The default aspect ratio is 4:3 + int nWidth (4); + int nHeight (3); + + // Search for the first entry with an existing master page. + auto iDescriptor = std::find_if(maContainer.begin(), maContainer.end(), + [](const SharedMasterPageDescriptor& rxDescriptor) { + return rxDescriptor != nullptr && rxDescriptor->mpMasterPage != nullptr; + }); + if (iDescriptor != maContainer.end()) + { + Size aPageSize ((*iDescriptor)->mpMasterPage->GetSize()); + OSL_ASSERT(!aPageSize.IsEmpty()); + if (aPageSize.Width() > 0) + nWidth = aPageSize.Width(); + if (aPageSize.Height() > 0) + nHeight = aPageSize.Height(); + mbFirstPageObjectSeen = true; + } + + maSmallPreviewSizePixel.setWidth( SMALL_PREVIEW_WIDTH ); + maLargePreviewSizePixel.setWidth( LARGE_PREVIEW_WIDTH ); + + int nNewSmallHeight ((maSmallPreviewSizePixel.Width()-2) * nHeight / nWidth + 2); + int nNewLargeHeight ((maLargePreviewSizePixel.Width()-2) * nHeight / nWidth + 2); + + if (nNewSmallHeight!=maSmallPreviewSizePixel.Height() + || nNewLargeHeight!=maLargePreviewSizePixel.Height()) + { + maSmallPreviewSizePixel.setHeight( nNewSmallHeight ); + maLargePreviewSizePixel.setHeight( nNewLargeHeight ); + FireContainerChange( + MasterPageContainerChangeEvent::EventType::SIZE_CHANGED, + NIL_TOKEN); + } +} + +const Size& MasterPageContainer::Implementation::GetPreviewSizePixel (PreviewSize eSize) const +{ + if (eSize == SMALL) + return maSmallPreviewSizePixel; + else + return maLargePreviewSizePixel; +} + +MasterPageContainer::Token MasterPageContainer::Implementation::PutMasterPage ( + const SharedMasterPageDescriptor& rpDescriptor) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Token aResult (NIL_TOKEN); + + // Get page object and preview when that is inexpensive. + UpdateDescriptor(rpDescriptor,false,false, false); + + // Look up the new MasterPageDescriptor and either insert it or update + // an already existing one. + MasterPageContainerType::iterator aEntry ( + ::std::find_if ( + maContainer.begin(), + maContainer.end(), + MasterPageDescriptor::AllComparator(rpDescriptor))); + if (aEntry == maContainer.end()) + { + // Insert a new MasterPageDescriptor. + bool bIgnore(rpDescriptor->mpPageObjectProvider == nullptr + && rpDescriptor->msURL.isEmpty()); + + if ( ! bIgnore) + { + CleanContainer(); + + aResult = maContainer.size(); + rpDescriptor->SetToken(aResult); + + // Templates are precious, i.e. we lock them so that they will + // not be destroyed when (temporarily) no one references them. + // They will only be deleted when the container is destroyed. + switch (rpDescriptor->meOrigin) + { + case TEMPLATE: + case DEFAULT: + ++rpDescriptor->mnUseCount; + break; + + default: + break; + } + + maContainer.push_back(rpDescriptor); + aEntry = maContainer.end()-1; + + FireContainerChange(MasterPageContainerChangeEvent::EventType::CHILD_ADDED,aResult); + } + } + else + { + // Update an existing MasterPageDescriptor. + aResult = (*aEntry)->maToken; + std::unique_ptr > pEventTypes( + (*aEntry)->Update(*rpDescriptor)); + if (pEventTypes != nullptr && !pEventTypes->empty()) + { + // One or more aspects of the descriptor have changed. Send + // appropriate events to the listeners. + UpdateDescriptor(*aEntry,false,false, true); + + for (const auto& rEventType : *pEventTypes) + { + FireContainerChange(rEventType, (*aEntry)->maToken); + } + } + } + + return aResult; +} + +bool MasterPageContainer::Implementation::HasToken (Token aToken) const +{ + return aToken>=0 + && o3tl::make_unsigned(aToken)=0 && o3tl::make_unsigned(aToken)maSmallPreview = Image(); + pDescriptor->maLargePreview = Image(); + RequestPreview(aToken); + } +} + +Image MasterPageContainer::Implementation::GetPreviewForToken ( + MasterPageContainer::Token aToken, + PreviewSize ePreviewSize) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Image aPreview; + PreviewState ePreviewState (GetPreviewState(aToken)); + + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + + // When the preview is missing but inexpensively creatable then do that + // now. + if (pDescriptor) + { + if (ePreviewState == PS_CREATABLE) + if (UpdateDescriptor(pDescriptor, false,false, true)) + if (pDescriptor->maLargePreview.GetSizePixel().Width() != 0) + ePreviewState = PS_AVAILABLE; + + switch (ePreviewState) + { + case PS_AVAILABLE: + aPreview = pDescriptor->GetPreview(ePreviewSize); + break; + + case PS_PREPARING: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION, + ePreviewSize); + break; + + case PS_CREATABLE: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION, + ePreviewSize); + break; + + case PS_NOT_AVAILABLE: + aPreview = GetPreviewSubstitution( + STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION, + ePreviewSize); + if (ePreviewSize == SMALL) + pDescriptor->maSmallPreview = aPreview; + else + pDescriptor->maLargePreview = aPreview; + break; + } + } + + return aPreview; +} + +MasterPageContainer::PreviewState MasterPageContainer::Implementation::GetPreviewState ( + Token aToken) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + PreviewState eState (PS_NOT_AVAILABLE); + + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + if (pDescriptor) + { + if (pDescriptor->maLargePreview.GetSizePixel().Width() != 0) + eState = PS_AVAILABLE; + else if (pDescriptor->mpPreviewProvider != nullptr) + { + // The preview does not exist but can be created. When that is + // not expensive then do it at once. + if (mpRequestQueue->HasRequest(aToken)) + eState = PS_PREPARING; + else + eState = PS_CREATABLE; + } + else + eState = PS_NOT_AVAILABLE; + } + + return eState; +} + +bool MasterPageContainer::Implementation::RequestPreview (Token aToken) +{ + SharedMasterPageDescriptor pDescriptor = GetDescriptor(aToken); + if (pDescriptor) + return mpRequestQueue->RequestPreview(pDescriptor); + else + return false; +} + +Reference MasterPageContainer::Implementation::GetModel() +{ + const ::osl::MutexGuard aGuard (maMutex); + + if ( ! mxModel.is()) + { + // Create a new model. + mxModel.set( + ::comphelper::getProcessServiceFactory()->createInstance( + "com.sun.star.presentation.PresentationDocument"), + uno::UNO_QUERY); + + // Initialize the model. + uno::Reference xLoadable (mxModel,uno::UNO_QUERY); + if (xLoadable.is()) + xLoadable->initNew(); + + // Use its tunnel to get a pointer to its core implementation. + uno::Reference xUnoTunnel (mxModel, uno::UNO_QUERY); + if (auto pSdXImpressDocument = comphelper::getFromUnoTunnel(xUnoTunnel)) + { + mpDocument = pSdXImpressDocument->GetDoc(); + } + + // Create a default page. + uno::Reference xSlideSupplier (mxModel, uno::UNO_QUERY); + if (xSlideSupplier.is()) + { + uno::Reference xSlides = + xSlideSupplier->getDrawPages(); + if (xSlides.is()) + { + uno::Reference xNewPage (xSlides->insertNewByIndex(0)); + uno::Reference xProperties(xNewPage, uno::UNO_QUERY); + if (xProperties.is()) + xProperties->setPropertyValue( + "Layout", + Any(sal_Int16(AUTOLAYOUT_TITLE))); + } + } + } + return mxModel; +} + +SdDrawDocument* MasterPageContainer::Implementation::GetDocument() +{ + GetModel(); + return mpDocument; +} + +Image MasterPageContainer::Implementation::GetPreviewSubstitution ( + TranslateId pId, + PreviewSize ePreviewSize) +{ + const ::osl::MutexGuard aGuard (maMutex); + + Image aPreview; + + if (pId == STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION) + { + Image& rPreview (ePreviewSize==SMALL + ? maSmallPreviewBeingCreated + : maLargePreviewBeingCreated); + if (rPreview.GetSizePixel().Width() == 0) + { + rPreview = maPreviewRenderer.RenderSubstitution( + ePreviewSize==SMALL ? maSmallPreviewSizePixel : maLargePreviewSizePixel, + SdResId(STR_TASKPANEL_PREPARING_PREVIEW_SUBSTITUTION)); + } + aPreview = rPreview; + } + else if (pId == STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION) + { + Image& rPreview (ePreviewSize==SMALL + ? maSmallPreviewNotAvailable + : maLargePreviewNotAvailable); + if (rPreview.GetSizePixel().Width() == 0) + { + rPreview = maPreviewRenderer.RenderSubstitution( + ePreviewSize==SMALL ? maSmallPreviewSizePixel : maLargePreviewSizePixel, + SdResId(STR_TASKPANEL_NOT_AVAILABLE_SUBSTITUTION)); + } + aPreview = rPreview; + } + + return aPreview; +} + +void MasterPageContainer::Implementation::CleanContainer() +{ + // Remove the empty elements at the end of the container. The empty + // elements in the middle can not be removed because that would + // invalidate the references still held by others. + int nIndex (maContainer.size()-1); + while (nIndex>=0 && !maContainer[nIndex]) + --nIndex; + maContainer.resize(++nIndex); +} + +void MasterPageContainer::Implementation::FireContainerChange ( + MasterPageContainerChangeEvent::EventType eType, + Token aToken) +{ + ::std::vector> aCopy(maChangeListeners); + MasterPageContainerChangeEvent aEvent; + aEvent.meEventType = eType; + aEvent.maChildToken = aToken; + for (const auto& rListener : aCopy) + rListener.Call(aEvent); +} + +bool MasterPageContainer::Implementation::UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) +{ + const ::osl::MutexGuard aGuard (maMutex); + + // We have to create the page object when the preview provider needs it + // and the caller needs the preview. + bForcePageObject |= (bForcePreview + && rpDescriptor->mpPreviewProvider->NeedsPageObject() + && rpDescriptor->mpMasterPage==nullptr); + + // Define a cost threshold so that an update or page object or preview + // that is at least this cost are made at once. Updates with higher cost + // are scheduled for later. + sal_Int32 nCostThreshold (mpRequestQueue->IsEmpty() ? 5 : 0); + + // Update the page object (which may be used for the preview update). + if (bForcePageObject) + GetDocument(); + int nPageObjectModified (rpDescriptor->UpdatePageObject( + (bForcePageObject ? -1 : nCostThreshold), + mpDocument)); + if (nPageObjectModified == 1 && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::DATA_CHANGED, + rpDescriptor->maToken); + if (nPageObjectModified == -1 && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::CHILD_REMOVED, + rpDescriptor->maToken); + if (nPageObjectModified && ! mbFirstPageObjectSeen) + UpdatePreviewSizePixel(); + + // Update the preview. + bool bPreviewModified (rpDescriptor->UpdatePreview( + (bForcePreview ? -1 : nCostThreshold), + maSmallPreviewSizePixel, + maLargePreviewSizePixel, + maPreviewRenderer)); + + if (bPreviewModified && bSendEvents) + FireContainerChange( + MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED, + rpDescriptor->maToken); + + return nPageObjectModified || bPreviewModified; +} + +void MasterPageContainer::Implementation::ReleaseDescriptor (Token aToken) +{ + if (aToken>=0 && o3tl::make_unsigned(aToken)ProcessAllRequests(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainer.hxx b/sd/source/ui/sidebar/MasterPageContainer.hxx new file mode 100644 index 000000000..9de4eb6bc --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainer.hxx @@ -0,0 +1,199 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +class SdPage; + +namespace sd::sidebar +{ +class MasterPageDescriptor; +class MasterPageContainerChangeEvent; + +/** This container manages the master pages used by the MasterPagesSelector + controls. It uses internally a singleton implementation object. + Therefore, all MasterPageContainer object operator on the same set of + master pages. Each MasterPageContainer, however, has its own + PreviewSize value and thus can independently switch between large and + small previews. + + The container maintains its own document to store master page objects. + + For each master page container stores its URL, preview bitmap, page + name, and, if available, the page object. + + Entries are accessed via a Token, which is mostly a numerical index but + whose values do not necessarily have to be consecutive. +*/ +class MasterPageContainer final +{ +public: + typedef int Token; + static const Token NIL_TOKEN = -1; + + MasterPageContainer(); + ~MasterPageContainer(); + + void AddChangeListener(const Link& rLink); + void RemoveChangeListener(const Link& rLink); + + enum PreviewSize + { + SMALL, + LARGE + }; + /** There are two different preview sizes, a small one and a large one. + Which one is used by the called container can be changed with this + method. + When the preview size is changed then all change listeners are + notified of this. + */ + void SetPreviewSize(PreviewSize eSize); + + /** Returns the preview size. + */ + PreviewSize GetPreviewSize() const { return mePreviewSize; } + + /** Return the preview size in pixels. + */ + Size const& GetPreviewSizePixel() const; + + enum PreviewState + { + PS_AVAILABLE, + PS_CREATABLE, + PS_PREPARING, + PS_NOT_AVAILABLE + }; + PreviewState GetPreviewState(Token aToken); + + /** This method is typically called for entries in the container for + which GetPreviewState() returns OS_CREATABLE. The creation of the + preview is then scheduled to be executed asynchronously at a later + point in time. When the preview is available the change listeners + will be notified. + */ + bool RequestPreview(Token aToken); + + /** Each entry of the container is either the first page of a template + document or is a master page of an Impress document. + */ + enum Origin + { + MASTERPAGE, // Master page of a document. + TEMPLATE, // First page of a template file. + DEFAULT, // Empty master page with default style. + UNKNOWN + }; + + /** Put the master page identified and described by the given parameters + into the container. When there already is a master page with the + given URL, page name, or object pointer (when that is not NULL) then + the existing entry is replaced/updated by the given one. Otherwise + a new entry is inserted. + */ + Token PutMasterPage(const std::shared_ptr& rDescriptor); + void AcquireToken(Token aToken); + void ReleaseToken(Token aToken); + + /** This and the GetTokenForIndex() methods can be used to iterate over + all members of the container. + */ + int GetTokenCount() const; + + /** Determine whether the container has a member for the given token. + */ + bool HasToken(Token aToken) const; + + /** Return a token for an index in the range + 0 <= index < GetTokenCount(). + */ + Token GetTokenForIndex(int nIndex); + + Token GetTokenForURL(const OUString& sURL); + Token GetTokenForStyleName(const OUString& sStyleName); + Token GetTokenForPageObject(const SdPage* pPage); + + OUString GetURLForToken(Token aToken); + OUString GetPageNameForToken(Token aToken); + OUString GetStyleNameForToken(Token aToken); + SdPage* GetPageObjectForToken(Token aToken, bool bLoad); + Origin GetOriginForToken(Token aToken); + sal_Int32 GetTemplateIndexForToken(Token aToken); + std::shared_ptr GetDescriptorForToken(Token aToken); + + void InvalidatePreview(Token aToken); + + /** Return a preview for the specified token. When the preview is not + present then the PreviewProvider associated with the token is + executed only when that is not expensive. It is the responsibility + of the caller to call RequestPreview() to do the same + (asynchronously) for expensive PreviewProviders. + Call GetPreviewState() to find out if that is necessary. + @param aToken + This token specifies for which master page to return the preview. + Tokens are returned for example by the GetTokenFor...() methods. + @return + The returned image is the requested preview or a substitution. + */ + Image GetPreviewForToken(Token aToken); + +private: + class Implementation; + std::shared_ptr mpImpl; + PreviewSize mePreviewSize; +}; + +/** For some changes to the set of master pages in a MasterPageContainer or + to the data stored for each master page one or more events are sent to + registered listeners. + Each event has an event type and a token that tells the listener where + the change took place. +*/ +class MasterPageContainerChangeEvent +{ +public: + enum class EventType + { + // A master page was added to the container. + CHILD_ADDED, + // A master page was removed from the container. + CHILD_REMOVED, + // The preview of a master page has changed. + PREVIEW_CHANGED, + // The size of a preview has changed. + SIZE_CHANGED, + // Some of the data stored for a master page has changed. + DATA_CHANGED, + // The TemplateIndex of a master page has changed. + INDEX_CHANGED, + } meEventType; + + // Token of the container entry whose data changed or which was added or + // removed. + MasterPageContainer::Token maChildToken; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerFiller.cxx b/sd/source/ui/sidebar/MasterPageContainerFiller.cxx new file mode 100644 index 000000000..3568d9c71 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerFiller.cxx @@ -0,0 +1,168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "MasterPageContainerFiller.hxx" + +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerProviders.hxx" +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::sidebar { + +MasterPageContainerFiller::MasterPageContainerFiller (ContainerAdapter& rpAdapter) + : mrContainerAdapter(rpAdapter), + meState(INITIALIZE_TEMPLATE_SCANNER), + mpLastAddedEntry(nullptr), + mnIndex(1) +{ + // Add one entry for the default master page. We use temporarily the + // DefaultPagePreviewProvider to prevent the rendering (and the + // expensive creation) of the default page. It is replaced later on by + // another. + SharedMasterPageDescriptor pDescriptor = std::make_shared( + MasterPageContainer::DEFAULT, + 0, + OUString(), + OUString(), + OUString(), + false, + std::make_shared(), + std::make_shared()); + mrContainerAdapter.PutMasterPage(pDescriptor); +} + +MasterPageContainerFiller::~MasterPageContainerFiller() +{ +} + +void MasterPageContainerFiller::RunNextStep() +{ + switch (meState) + { + case INITIALIZE_TEMPLATE_SCANNER: + mpScannerTask.reset(new TemplateScanner()); + meState = SCAN_TEMPLATE; + break; + + case SCAN_TEMPLATE: + meState = ScanTemplate(); + break; + + case ADD_TEMPLATE: + meState = AddTemplate(); + break; + + case DONE: + case ERROR: + default: + break; + } + + // When the state has just been set to DONE or ERROR then tell the + // container that no more templates will be coming and stop the + // scanning. + switch (meState) + { + case DONE: + case ERROR: + if (mpScannerTask != nullptr) + { + mrContainerAdapter.FillingDone(); + mpScannerTask.reset(); + } + break; + default: + break; + } +} + +bool MasterPageContainerFiller::HasNextStep() +{ + switch (meState) + { + case DONE: + case ERROR: + return false; + + default: + return true; + } +} + +MasterPageContainerFiller::State MasterPageContainerFiller::ScanTemplate() +{ + State eState (ERROR); + + if (mpScannerTask != nullptr) + { + if (mpScannerTask->HasNextStep()) + { + mpScannerTask->RunNextStep(); + if (mpScannerTask->GetLastAddedEntry() != mpLastAddedEntry) + { + mpLastAddedEntry = mpScannerTask->GetLastAddedEntry(); + if (mpLastAddedEntry != nullptr) + eState = ADD_TEMPLATE; + else + eState = SCAN_TEMPLATE; + } + else + eState = SCAN_TEMPLATE; + } + else + eState = DONE; + } + + return eState; +} + +MasterPageContainerFiller::State MasterPageContainerFiller::AddTemplate() +{ + if (mpLastAddedEntry != nullptr) + { + SharedMasterPageDescriptor pDescriptor = std::make_shared( + MasterPageContainer::TEMPLATE, + mnIndex, + mpLastAddedEntry->msPath, + mpLastAddedEntry->msTitle, + OUString(), + false, + std::make_shared(mpLastAddedEntry->msPath), + std::make_shared(mpLastAddedEntry->msPath)); + // For user supplied templates we use a different preview provider: + // The preview in the document shows not only shapes on the master + // page but also shapes on the foreground. This is misleading and + // therefore these previews are discarded and created directly from + // the page objects. + if (pDescriptor->GetURLClassification() == MasterPageDescriptor::URLCLASS_USER) + pDescriptor->mpPreviewProvider = std::make_shared(); + + mrContainerAdapter.PutMasterPage(pDescriptor); + ++mnIndex; + } + + return SCAN_TEMPLATE; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerFiller.hxx b/sd/source/ui/sidebar/MasterPageContainerFiller.hxx new file mode 100644 index 000000000..b08452ab6 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerFiller.hxx @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" +#include + +namespace sd +{ +class TemplateScanner; +class TemplateEntry; +} + +namespace sd::sidebar +{ +/** Fill a MasterPageContainer with information about the available master + pages. These are provided by one default page and from the existing + Impress templates. This is done asynchronously. +*/ +class MasterPageContainerFiller : public ::sd::tools::AsynchronousTask +{ +public: + class ContainerAdapter + { + public: + virtual MasterPageContainer::Token + PutMasterPage(const SharedMasterPageDescriptor& rpDescriptor) + = 0; + /** This method is called when all Impress templates have been added + to the container via the PutMasterPage() method. + */ + virtual void FillingDone() = 0; + + protected: + ~ContainerAdapter() {} + }; + + explicit MasterPageContainerFiller(ContainerAdapter& rContainerAdapter); + virtual ~MasterPageContainerFiller(); + + /** Run the next step of the task. After HasNextStep() returns false + this method should ignore further calls. + */ + virtual void RunNextStep() override; + + /** Return when there is at least one more step to execute. + When the task has been executed completely then is + returned. + */ + virtual bool HasNextStep() override; + +private: + ContainerAdapter& mrContainerAdapter; + // Remember what the next step has to do. + enum State + { + INITIALIZE_TEMPLATE_SCANNER, + SCAN_TEMPLATE, + ADD_TEMPLATE, + ERROR, + DONE + } meState; + ::std::unique_ptr mpScannerTask; + const TemplateEntry* mpLastAddedEntry; + int mnIndex; + + State ScanTemplate(); + State AddTemplate(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerProviders.cxx b/sd/source/ui/sidebar/MasterPageContainerProviders.cxx new file mode 100644 index 000000000..785536daa --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerProviders.cxx @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "MasterPageContainerProviders.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::sidebar { + +//===== PagePreviewProvider =================================================== + +PagePreviewProvider::PagePreviewProvider() +{ +} + +Image PagePreviewProvider::operator () ( + int nWidth, + SdPage* pPage, + ::sd::PreviewRenderer& rRenderer) +{ + Image aPreview; + + if (pPage != nullptr) + { + // Use the given renderer to create a preview of the given page + // object. + aPreview = rRenderer.RenderPage( + pPage, + nWidth); + } + + return aPreview; +} + +int PagePreviewProvider::GetCostIndex() +{ + return 5; +} + +bool PagePreviewProvider::NeedsPageObject() +{ + return true; +} + +//===== TemplatePreviewProvider =============================================== + +TemplatePreviewProvider::TemplatePreviewProvider (const OUString& rsURL) + : msURL(rsURL) +{ +} + +Image TemplatePreviewProvider::operator() ( + int, + SdPage*, + ::sd::PreviewRenderer&) +{ + return Image(ThumbnailView::readThumbnail(msURL)); +} + +int TemplatePreviewProvider::GetCostIndex() +{ + return 10; +} + +bool TemplatePreviewProvider::NeedsPageObject() +{ + return false; +} + +//===== TemplatePageObjectProvider ============================================= + +TemplatePageObjectProvider::TemplatePageObjectProvider (const OUString& rsURL) + : msURL(rsURL) +{ +} + +SdPage* TemplatePageObjectProvider::operator() (SdDrawDocument*) +{ + SdPage* pPage = nullptr; + + mxDocumentShell = nullptr; + try + { + // Load the template document and return its first page. + ::sd::DrawDocShell* pDocumentShell = LoadDocument (msURL); + if (pDocumentShell != nullptr) + { + SdDrawDocument* pDocument = pDocumentShell->GetDoc(); + if (pDocument != nullptr) + { + pPage = pDocument->GetMasterSdPage(0, PageKind::Standard); + // In order to make the newly loaded master page deletable + // when copied into documents it is marked as no "precious". + // When it is modified then it is marked as "precious". + if (pPage != nullptr) + pPage->SetPrecious(false); + } + } + } + catch (const uno::RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + pPage = nullptr; + } + + return pPage; +} + +::sd::DrawDocShell* TemplatePageObjectProvider::LoadDocument (const OUString& sFileName) +{ + SfxApplication* pSfxApp = SfxGetpApp(); + std::unique_ptr pSet(new SfxAllItemSet (pSfxApp->GetPool())); + pSet->Put (SfxBoolItem (SID_TEMPLATE, true)); + pSet->Put (SfxBoolItem (SID_PREVIEW, true)); + if (pSfxApp->LoadTemplate (mxDocumentShell, sFileName, std::move(pSet))) + { + mxDocumentShell = nullptr; + } + SfxObjectShell* pShell = mxDocumentShell; + return dynamic_cast< ::sd::DrawDocShell *>( pShell ); +} + +int TemplatePageObjectProvider::GetCostIndex() +{ + return 20; +} + +//===== DefaultPageObjectProvider ============================================== + +DefaultPageObjectProvider::DefaultPageObjectProvider() +{ +} + +SdPage* DefaultPageObjectProvider::operator () (SdDrawDocument* pContainerDocument) +{ + SdPage* pLocalMasterPage = nullptr; + if (pContainerDocument != nullptr) + { + SdPage* pLocalSlide = pContainerDocument->GetSdPage(0, PageKind::Standard); + if (pLocalSlide!=nullptr && pLocalSlide->TRG_HasMasterPage()) + pLocalMasterPage = dynamic_cast(&pLocalSlide->TRG_GetMasterPage()); + } + + if (pLocalMasterPage == nullptr) + { + SAL_WARN( "sd", "can not create master page for slide"); + } + + return pLocalMasterPage; +} + +int DefaultPageObjectProvider::GetCostIndex() +{ + return 15; +} + +//===== ExistingPageProvider ================================================== + +ExistingPageProvider::ExistingPageProvider (SdPage* pPage) + : mpPage(pPage) +{ +} + +SdPage* ExistingPageProvider::operator() (SdDrawDocument*) +{ + return mpPage; +} + +int ExistingPageProvider::GetCostIndex() +{ + return 0; +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerProviders.hxx b/sd/source/ui/sidebar/MasterPageContainerProviders.hxx new file mode 100644 index 000000000..b76076e15 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerProviders.hxx @@ -0,0 +1,175 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class Image; +class SdDrawDocument; +class SdPage; +namespace sd +{ +class PreviewRenderer; +} +namespace sd +{ +class DrawDocShell; +} + +namespace sd::sidebar +{ +/** Interface for a provider of page objects. It is used by the + MasterPageDescriptor to create master page objects on demand. +*/ +class PageObjectProvider +{ +public: + /** Return a master page either by returning an already existing one, by + creating a new page, or by loading a document. + @param pDocument + The document of the MasterPageContainer. It may be used to + create new pages. + */ + virtual SdPage* operator()(SdDrawDocument* pDocument) = 0; + + /** An abstract value for the expected cost of providing a master page + object. + @return + A value of 0 represents for the lowest cost, i.e. an almost + immediate return. Positive values stand for higher costs. + Negative values are not supported. + */ + virtual int GetCostIndex() = 0; + +protected: + ~PageObjectProvider() {} +}; + +class PreviewProvider +{ +public: + /** Create a preview image in the specified width. + @param nWidth + Requested width of the preview. The calling method can cope + with other sizes as well but the resulting image quality is + better when the returned image has the requested size. + @param pPage + Page object for which a preview is requested. This may be NULL + when the page object is expensive to get and the PreviewProvider + does not need this object (NeedsPageObject() returns false.) + @param rRenderer + This PreviewRenderer may be used by the PreviewProvider to + create a preview image. + */ + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) = 0; + + /** Return a value that indicates how expensive the creation of a + preview image is. The higher the returned value the more expensive + is the preview creation. Return 0 when the preview is already + present and can be returned immediately. + */ + virtual int GetCostIndex() = 0; + + /** Return whether the page object passed is necessary to create a + preview. + */ + virtual bool NeedsPageObject() = 0; + +protected: + ~PreviewProvider() {} +}; + +/** Provide previews of existing page objects by rendering them. +*/ +class PagePreviewProvider : public PreviewProvider +{ +public: + PagePreviewProvider(); + virtual ~PagePreviewProvider() {} + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) override; + virtual int GetCostIndex() override; + virtual bool NeedsPageObject() override; + +private: +}; + +/** Provide master page objects for template documents for which only the + URL is given. +*/ +class TemplatePageObjectProvider : public PageObjectProvider +{ +public: + explicit TemplatePageObjectProvider(const OUString& rsURL); + virtual ~TemplatePageObjectProvider(){}; + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; + +private: + OUString msURL; + SfxObjectShellLock mxDocumentShell; + ::sd::DrawDocShell* LoadDocument(const OUString& sFileName); +}; + +/** Provide previews for template documents by loading the thumbnails from + the documents. +*/ +class TemplatePreviewProvider : public PreviewProvider +{ +public: + explicit TemplatePreviewProvider(const OUString& rsURL); + virtual ~TemplatePreviewProvider(){}; + virtual Image operator()(int nWidth, SdPage* pPage, ::sd::PreviewRenderer& rRenderer) override; + virtual int GetCostIndex() override; + virtual bool NeedsPageObject() override; + +private: + OUString msURL; +}; + +/** Create an empty default master page. +*/ +class DefaultPageObjectProvider : public PageObjectProvider +{ +public: + DefaultPageObjectProvider(); + virtual ~DefaultPageObjectProvider() {} + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; +}; + +/** This implementation of the PageObjectProvider simply returns an already + existing master page object. +*/ +class ExistingPageProvider : public PageObjectProvider +{ +public: + explicit ExistingPageProvider(SdPage* pPage); + virtual ~ExistingPageProvider() {} + virtual SdPage* operator()(SdDrawDocument* pDocument) override; + virtual int GetCostIndex() override; + +private: + SdPage* mpPage; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerQueue.cxx b/sd/source/ui/sidebar/MasterPageContainerQueue.cxx new file mode 100644 index 000000000..229f3d972 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerQueue.cxx @@ -0,0 +1,263 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "MasterPageContainerQueue.hxx" +#include "MasterPageContainerProviders.hxx" + +#include + +#include + +namespace sd::sidebar { + +const sal_Int32 MasterPageContainerQueue::snDelayedCreationTimeout (15); +const sal_Int32 MasterPageContainerQueue::snDelayedCreationTimeoutWhenNotIdle (100); +const sal_Int32 MasterPageContainerQueue::snMasterPagePriorityBoost (5); +const sal_Int32 MasterPageContainerQueue::snWaitForMoreRequestsPriorityThreshold (-10); +sal_uInt32 MasterPageContainerQueue::snWaitForMoreRequestsCount(15); + +//===== MasterPageContainerQueue::PreviewCreationRequest ====================== + +class MasterPageContainerQueue::PreviewCreationRequest +{ +public: + PreviewCreationRequest (const SharedMasterPageDescriptor& rpDescriptor, int nPriority) + : mpDescriptor(rpDescriptor), + mnPriority(nPriority) + {} + SharedMasterPageDescriptor mpDescriptor; + int mnPriority; + class Compare + { + public: + bool operator() (const PreviewCreationRequest& r1,const PreviewCreationRequest& r2) const + { + if (r1.mnPriority != r2.mnPriority) + { + // Prefer requests with higher priority. + return r1.mnPriority > r2.mnPriority; + } + else + { + // Prefer tokens that have been earlier created (those with lower + // value). + return r1.mpDescriptor->maToken < r2.mpDescriptor->maToken; + } + } + }; + class CompareToken + { + public: + MasterPageContainer::Token maToken; + explicit CompareToken(MasterPageContainer::Token aToken) : maToken(aToken) {} + bool operator() (const PreviewCreationRequest& rRequest) const + { return maToken==rRequest.mpDescriptor->maToken; } + }; +}; + +//===== MasterPageContainerQueue::RequestQueue ================================ + +class MasterPageContainerQueue::RequestQueue + : public ::std::set +{ +public: + RequestQueue() {} +}; + +//===== MasterPageContainerQueue ============================================== + +MasterPageContainerQueue* MasterPageContainerQueue::Create ( + const std::weak_ptr& rpContainer) +{ + MasterPageContainerQueue* pQueue = new MasterPageContainerQueue(rpContainer); + pQueue->LateInit(); + return pQueue; +} + +MasterPageContainerQueue::MasterPageContainerQueue ( + const std::weak_ptr& rpContainer) + : mpWeakContainer(rpContainer), + mpRequestQueue(new RequestQueue()), + maDelayedPreviewCreationTimer("sd MasterPageContainerQueue maDelayedPreviewCreationTimer"), + mnRequestsServedCount(0) +{ +} + +MasterPageContainerQueue::~MasterPageContainerQueue() +{ + maDelayedPreviewCreationTimer.Stop(); + while ( ! mpRequestQueue->empty()) + mpRequestQueue->erase(mpRequestQueue->begin()); +} + +void MasterPageContainerQueue::LateInit() +{ + // Set up the timer for the delayed creation of preview bitmaps. + maDelayedPreviewCreationTimer.SetTimeout (snDelayedCreationTimeout); + maDelayedPreviewCreationTimer.SetInvokeHandler( + LINK(this,MasterPageContainerQueue,DelayedPreviewCreation) ); +} + +bool MasterPageContainerQueue::RequestPreview (const SharedMasterPageDescriptor& rpDescriptor) +{ + bool bSuccess (false); + if (rpDescriptor + && rpDescriptor->maLargePreview.GetSizePixel().Width() == 0) + { + sal_Int32 nPriority (CalculatePriority(rpDescriptor)); + + // Add a new or replace an existing request. + RequestQueue::iterator iRequest (::std::find_if( + mpRequestQueue->begin(), + mpRequestQueue->end(), + PreviewCreationRequest::CompareToken(rpDescriptor->maToken))); + // When a request for the same token exists then the lowest of the + // two priorities is used. + if (iRequest != mpRequestQueue->end()) + if (iRequest->mnPriority < nPriority) + { + mpRequestQueue->erase(iRequest); + iRequest = mpRequestQueue->end(); + } + + // Add a new request when none exists (or has just been erased). + if (iRequest == mpRequestQueue->end()) + { + mpRequestQueue->insert(PreviewCreationRequest(rpDescriptor,nPriority)); + maDelayedPreviewCreationTimer.Start(); + bSuccess = true; + } + } + return bSuccess; +} + +sal_Int32 MasterPageContainerQueue::CalculatePriority ( + const SharedMasterPageDescriptor& rpDescriptor) +{ + sal_Int32 nPriority; + + // The cost is used as a starting value. + int nCost (0); + if (rpDescriptor->mpPreviewProvider != nullptr) + { + nCost = rpDescriptor->mpPreviewProvider->GetCostIndex(); + if (rpDescriptor->mpPreviewProvider->NeedsPageObject()) + if (rpDescriptor->mpPageObjectProvider != nullptr) + nCost += rpDescriptor->mpPageObjectProvider->GetCostIndex(); + } + + // Its negative value is used so that requests with a low cost are + // preferred over those with high costs. + nPriority = -nCost; + + // Add a term that introduces an order based on the appearance in the + // AllMasterPagesSelector. + nPriority -= rpDescriptor->maToken / 3; + + // Process requests for the CurrentMasterPagesSelector first. + if (rpDescriptor->meOrigin == MasterPageContainer::MASTERPAGE) + nPriority += snMasterPagePriorityBoost; + + return nPriority; +} + +IMPL_LINK(MasterPageContainerQueue, DelayedPreviewCreation, Timer*, pTimer, void) +{ + bool bIsShowingFullScreenShow (false); + bool bWaitForMoreRequests (false); + + do + { + if (mpRequestQueue->empty()) + break; + + // First check whether the system is idle. + tools::IdleState nIdleState (tools::IdleDetection::GetIdleState(nullptr)); + if (nIdleState != tools::IdleState::Idle) + { + if (nIdleState & tools::IdleState::FullScreenShowActive) + bIsShowingFullScreenShow = true; + break; + } + + PreviewCreationRequest aRequest (*mpRequestQueue->begin()); + + // Check if the request should really be processed right now. + // Reasons to not do it are when its cost is high and not many other + // requests have been inserted into the queue that would otherwise + // be processed first. + if (aRequest.mnPriority < snWaitForMoreRequestsPriorityThreshold + && (mnRequestsServedCount+mpRequestQueue->size() < snWaitForMoreRequestsCount)) + { + // Wait for more requests before this one is processed. Note + // that the queue processing is not started anew when this + // method is left. That is done when the next request is + // inserted. + bWaitForMoreRequests = true; + break; + } + + mpRequestQueue->erase(mpRequestQueue->begin()); + + if (aRequest.mpDescriptor) + { + mnRequestsServedCount += 1; + if ( ! mpWeakContainer.expired()) + { + std::shared_ptr pContainer (mpWeakContainer); + if (pContainer != nullptr) + pContainer->UpdateDescriptor(aRequest.mpDescriptor,false,true,true); + } + } + } + while (false); + + if (!mpRequestQueue->empty() && ! bWaitForMoreRequests) + { + int nTimeout (snDelayedCreationTimeout); + if (bIsShowingFullScreenShow) + nTimeout = snDelayedCreationTimeoutWhenNotIdle; + maDelayedPreviewCreationTimer.SetTimeout(nTimeout); + pTimer->Start(); + } +} + +bool MasterPageContainerQueue::HasRequest (MasterPageContainer::Token aToken) const +{ + return std::any_of( + mpRequestQueue->begin(), + mpRequestQueue->end(), + PreviewCreationRequest::CompareToken(aToken)); +} + +bool MasterPageContainerQueue::IsEmpty() const +{ + return mpRequestQueue->empty(); +} + +void MasterPageContainerQueue::ProcessAllRequests() +{ + snWaitForMoreRequestsCount = 0; + if (!mpRequestQueue->empty()) + maDelayedPreviewCreationTimer.Start(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageContainerQueue.hxx b/sd/source/ui/sidebar/MasterPageContainerQueue.hxx new file mode 100644 index 000000000..6b9b0adca --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageContainerQueue.hxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "MasterPageContainer.hxx" +#include "MasterPageDescriptor.hxx" + +#include + +#include + +namespace sd::sidebar { + +/** The queue stores and processes all requests from a MasterPageContainer + for the creation of previews. + The order of request processing and its timing is controlled by a + heuristic that uses values given with each request and which is + controlled by various parameters that are described below. +*/ +class MasterPageContainerQueue final +{ +public: + class ContainerAdapter { + public: + virtual bool UpdateDescriptor ( + const SharedMasterPageDescriptor& rpDescriptor, + bool bForcePageObject, + bool bForcePreview, + bool bSendEvents) = 0; + + protected: + ~ContainerAdapter() {} + }; + + static MasterPageContainerQueue* Create ( + const std::weak_ptr& rpContainer); + ~MasterPageContainerQueue(); + + /** This method is typically called for entries in the container for + which GetPreviewState() returns OS_CREATABLE. The creation of the + preview is then scheduled to be executed asynchronously at a later + point in time. When the preview is available the change listeners + will be notified. + */ + bool RequestPreview (const SharedMasterPageDescriptor& rDescriptor); + + /** Return when there is a request currently in the queue for + the given token. + */ + bool HasRequest (MasterPageContainer::Token aToken) const; + + /** Return when there is at least one request in the queue. + */ + bool IsEmpty() const; + + /** After this call the queue does not wait anymore for requests with + higher priority when only a small number of requests with lower + priority are present. This method should be called when all + templates are inserted into the MasterPageContainer. + */ + void ProcessAllRequests(); + +private: + std::weak_ptr mpWeakContainer; + class PreviewCreationRequest; + class RequestQueue; + std::unique_ptr mpRequestQueue; + Timer maDelayedPreviewCreationTimer; + sal_uInt32 mnRequestsServedCount; + + // There are a couple of values that define various aspects of the + // heuristic that defines the order and timing in which requests for + // preview creation are processed. + + /** The time to wait (in milliseconds) between the creation of previews. + */ + static const sal_Int32 snDelayedCreationTimeout; + + /** The time to wait when the system is not idle. + */ + static const sal_Int32 snDelayedCreationTimeoutWhenNotIdle; + + /** Requests for previews of master pages in a document have their + priority increased by this value. + */ + static const sal_Int32 snMasterPagePriorityBoost; + + /** When only requests which a priority lower than this threshold exist + and not many requests have been made yet then wait with processing + them until more requests are present. + */ + static const sal_Int32 snWaitForMoreRequestsPriorityThreshold; + + /** When only requests which a priority lower than a threshold exist + and not more requests than this number have been made or already + processed then wait with processing them until more requests are + present. + */ + static sal_uInt32 snWaitForMoreRequestsCount; + + explicit MasterPageContainerQueue (const std::weak_ptr& rpContainer); + void LateInit(); + + /** Calculate the priority that defines the order in which requests + are processed. + */ + static sal_Int32 CalculatePriority (const SharedMasterPageDescriptor& rDescriptor); + + DECL_LINK(DelayedPreviewCreation, Timer *, void); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageDescriptor.cxx b/sd/source/ui/sidebar/MasterPageDescriptor.cxx new file mode 100644 index 000000000..2c0c23eb7 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageDescriptor.cxx @@ -0,0 +1,341 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "MasterPageDescriptor.hxx" +#include "MasterPageContainerProviders.hxx" + +#include "DocumentHelper.hxx" +#include +#include +#include +#include + +namespace sd::sidebar { + +//===== MasterPageDescriptor ================================================== + +MasterPageDescriptor::MasterPageDescriptor ( + MasterPageContainer::Origin eOrigin, + const sal_Int32 nTemplateIndex, + std::u16string_view rsURL, + const OUString& rsPageName, + const OUString& rsStyleName, + const bool bIsPrecious, + const std::shared_ptr& rpPageObjectProvider, + const std::shared_ptr& rpPreviewProvider) + : maToken(MasterPageContainer::NIL_TOKEN), + meOrigin(eOrigin), + msURL(INetURLObject(rsURL).GetMainURL(INetURLObject::DecodeMechanism::Unambiguous)), + msPageName(rsPageName), + msStyleName(rsStyleName), + mbIsPrecious(bIsPrecious), + mpMasterPage(nullptr), + mpSlide(nullptr), + mpPreviewProvider(rpPreviewProvider), + mpPageObjectProvider(rpPageObjectProvider), + mnTemplateIndex(nTemplateIndex), + meURLClassification(URLCLASS_UNDETERMINED), + mnUseCount(0) +{ +} + +void MasterPageDescriptor::SetToken (MasterPageContainer::Token aToken) +{ + maToken = aToken; +} + +const Image& MasterPageDescriptor::GetPreview (MasterPageContainer::PreviewSize eSize) const +{ + if (eSize == MasterPageContainer::SMALL) + return maSmallPreview; + else + return maLargePreview; +} + +::std::unique_ptr > + MasterPageDescriptor::Update ( + const MasterPageDescriptor& rDescriptor) +{ + bool bDataChanged (false); + bool bIndexChanged (false); + bool bPreviewChanged (false); + + if (meOrigin==MasterPageContainer::UNKNOWN + && rDescriptor.meOrigin!=MasterPageContainer::UNKNOWN) + { + meOrigin = rDescriptor.meOrigin; + bIndexChanged = true; + } + + if (msURL.isEmpty() && !rDescriptor.msURL.isEmpty()) + { + msURL = rDescriptor.msURL; + bDataChanged = true; + } + + if (msPageName.isEmpty() && !rDescriptor.msPageName.isEmpty()) + { + msPageName = rDescriptor.msPageName; + bDataChanged = true; + } + + if (msStyleName.isEmpty() && !rDescriptor.msStyleName.isEmpty()) + { + msStyleName = rDescriptor.msStyleName; + bDataChanged = true; + } + + if (mpPageObjectProvider == nullptr && rDescriptor.mpPageObjectProvider != nullptr) + { + mpPageObjectProvider = rDescriptor.mpPageObjectProvider; + bDataChanged = true; + } + + if (mpPreviewProvider == nullptr && rDescriptor.mpPreviewProvider != nullptr) + { + mpPreviewProvider = rDescriptor.mpPreviewProvider; + bPreviewChanged = true; + } + + if (mnTemplateIndex<0 && rDescriptor.mnTemplateIndex>=0) + { + mnTemplateIndex = rDescriptor.mnTemplateIndex; + bIndexChanged = true; + } + + // Prepare the list of event types that will be returned. + ::std::unique_ptr > pResult; + if (bDataChanged || bIndexChanged || bPreviewChanged) + { + pResult.reset(new std::vector); + if (bDataChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::DATA_CHANGED); + if (bIndexChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::INDEX_CHANGED); + if (bPreviewChanged) + pResult->push_back(MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED); + } + + return pResult; +} + +int MasterPageDescriptor::UpdatePageObject ( + sal_Int32 nCostThreshold, + SdDrawDocument* pDocument) +{ + int nModified = 0; + + // Update the page object when that is not yet known. + if (mpMasterPage == nullptr && mpPageObjectProvider != nullptr + && (nCostThreshold < 0 || mpPageObjectProvider->GetCostIndex() <= nCostThreshold)) + { + // Note that pDocument may be NULL. + + SdPage* pPage = (*mpPageObjectProvider)(pDocument); + if (meOrigin == MasterPageContainer::MASTERPAGE) + { + mpMasterPage = pPage; + if (mpMasterPage != nullptr) + mpMasterPage->SetPrecious(mbIsPrecious); + } + else + { + // Master pages from templates are copied into the local document. + if (pDocument != nullptr) + mpMasterPage = DocumentHelper::CopyMasterPageToLocalDocument(*pDocument,pPage); + mpSlide = DocumentHelper::GetSlideForMasterPage(mpMasterPage); + } + + if (mpMasterPage != nullptr) + { + // Update page name and style name. + if (msPageName.isEmpty()) + msPageName = mpMasterPage->GetName(); + msStyleName = mpMasterPage->GetName(); + + // Delete an existing substitution. The next request for a preview + // will create the real one. + maSmallPreview = Image(); + maLargePreview = Image(); + mpPreviewProvider = std::make_shared(); + } + else + { + SAL_WARN( "sd", "UpdatePageObject: master page is NULL"); + return -1; + } + + nModified = 1; + } + + return nModified; +} + +bool MasterPageDescriptor::UpdatePreview ( + sal_Int32 nCostThreshold, + const Size& rSmallSize, + const Size& rLargeSize, + ::sd::PreviewRenderer& rRenderer) +{ + bool bModified (false); + + // Update the preview when that is not yet known. + if (maLargePreview.GetSizePixel().Width() == 0 && mpPreviewProvider != nullptr + && (nCostThreshold < 0 || mpPreviewProvider->GetCostIndex() <= nCostThreshold)) + { + SdPage* pPage = mpSlide; + if (pPage == nullptr) + { + pPage = mpMasterPage; + } + //TODO: Notify LOOL of preview updates. + maLargePreview = (*mpPreviewProvider)( + rLargeSize.Width(), + pPage, + rRenderer); + if (maLargePreview.GetSizePixel().Width() > 0) + { + // Create the small preview by scaling the large one down. + maSmallPreview = rRenderer.ScaleBitmap( + maLargePreview.GetBitmapEx(), + rSmallSize.Width()); + // The large preview may not have the desired width. Scale it + // accordingly. + if (maLargePreview.GetSizePixel().Width() != rLargeSize.Width()) + maLargePreview = rRenderer.ScaleBitmap( + maLargePreview.GetBitmapEx(), + rLargeSize.Width()); + bModified = true; + } + } + + return bModified; +} + +MasterPageDescriptor::URLClassification MasterPageDescriptor::GetURLClassification() +{ + if (meURLClassification == URLCLASS_UNDETERMINED) + { + if (msURL.isEmpty()) + meURLClassification = URLCLASS_UNKNOWN; + else if (msURL.indexOf("presnt")>=0) + { + meURLClassification = URLCLASS_PRESENTATION; + } + else if (msURL.indexOf("layout")>=0) + { + meURLClassification = URLCLASS_LAYOUT; + } + else if (msURL.indexOf("educate")>=0) + { + meURLClassification = URLCLASS_OTHER; + } + else + { + meURLClassification = URLCLASS_USER; + } + } + + return meURLClassification; +} + +//===== URLComparator ========================================================= + +MasterPageDescriptor::URLComparator::URLComparator (const OUString& sURL) + : msURL(sURL) +{ +} + +bool MasterPageDescriptor::URLComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->msURL == msURL; +} + +// ===== StyleNameComparator ================================================== + +MasterPageDescriptor::StyleNameComparator::StyleNameComparator (const OUString& sStyleName) + : msStyleName(sStyleName) +{ +} + +bool MasterPageDescriptor::StyleNameComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->msStyleName == msStyleName; +} + +//===== PageObjectComparator ================================================== + +MasterPageDescriptor::PageObjectComparator::PageObjectComparator (const SdPage* pPageObject) + : mpMasterPage(pPageObject) +{ +} + +bool MasterPageDescriptor::PageObjectComparator::operator() ( + const SharedMasterPageDescriptor& rDescriptor) +{ + if (!rDescriptor) + return false; + else + return rDescriptor->mpMasterPage==mpMasterPage; +} + +//===== AllComparator ========================================================= + +MasterPageDescriptor::AllComparator::AllComparator(const SharedMasterPageDescriptor& rDescriptor) + : mpDescriptor(rDescriptor) +{ +} + +bool MasterPageDescriptor::AllComparator::operator() (const SharedMasterPageDescriptor&rDescriptor) +{ + if (!rDescriptor) + return false; + else + { + // Take URL, page name, style name, and page object into account + // when comparing two descriptors. When two descriptors are + // identical in any of these values then there are thought of as + // equivalent. Only the Origin has to be the same in both + // descriptors. + return mpDescriptor->meOrigin == rDescriptor->meOrigin + && ((!mpDescriptor->msURL.isEmpty() && mpDescriptor->msURL == rDescriptor->msURL) + || (!mpDescriptor->msPageName.isEmpty() + && mpDescriptor->msPageName == rDescriptor->msPageName) + || (!mpDescriptor->msStyleName.isEmpty() + && mpDescriptor->msStyleName == rDescriptor->msStyleName) + || (mpDescriptor->mpMasterPage != nullptr + && mpDescriptor->mpMasterPage == rDescriptor->mpMasterPage) + || (mpDescriptor->mpPageObjectProvider != nullptr + && rDescriptor->mpPageObjectProvider != nullptr + && mpDescriptor->mpPageObjectProvider == rDescriptor->mpPageObjectProvider)); + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageDescriptor.hxx b/sd/source/ui/sidebar/MasterPageDescriptor.hxx new file mode 100644 index 000000000..62717e528 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageDescriptor.hxx @@ -0,0 +1,231 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "MasterPageContainer.hxx" +#include + +namespace sd { class PreviewRenderer; } +class SdDrawDocument; + +namespace sd::sidebar { + +class PageObjectProvider; +class PreviewProvider; + +class MasterPageDescriptor; +typedef std::shared_ptr SharedMasterPageDescriptor; + +/** A collection of data that is stored for every master page in the + MasterpageContainer. +*/ +class MasterPageDescriptor +{ +public: + MasterPageDescriptor ( + MasterPageContainer::Origin eOrigin, + const sal_Int32 nTemplateIndex, + std::u16string_view rURL, + const OUString& rPageName, + const OUString& rStyleName, + const bool bIsPrecious, + const std::shared_ptr& rpPageObjectProvider, + const std::shared_ptr& rpPreviewProvider); + + void SetToken (MasterPageContainer::Token aToken); + + /** Update the called MasterPageDescriptor object with values from the + given one. Only those values are updated that have default values + in the called object and that have non-default values in the given + one. + @return + Returns a list of event types for which event notifications have + to be sent to listeners. The list may be empty or NULL. + */ + ::std::unique_ptr > + Update ( + const MasterPageDescriptor& rDescriptor); + + /** This convenience method returns either a small or a large preview, + depending on the given size specifier. + Note that the previews are not created when they are not present. + @return + The returned preview may be empty. + */ + const Image& GetPreview (MasterPageContainer::PreviewSize ePreviewSize) const; + + /** Use the PreviewProvider to get access to a preview of the master + page. + + Note that this is only done, when either bForce is or + the PreviewProvider::GetCostIndex() returns 0. + + The small preview is created by scaling the large one, not by + calling PreviewProvider::operator() a second time. + + It is the responsibility of the caller to call UpdatePageObject() + before calling this method when the PreviewProvider can only work + when the master page object is present, i.e. its NeedsPageObject() + method returns . + + @param nCostThreshold + When this is zero or positive then the preview is created only + when the preview provider has a cost equal to or smaller than + this threshold. A negative value forces the preview to be + created, regardless of the cost. + @param rSmallSize + Size of the small preview. + @param rLargeSize + Size of the large preview. + @param rRenderer + A PreviewRenderer object that may be used to create a preview. + @return + When the previews are successfully provided then is + returned. + */ + bool UpdatePreview ( + sal_Int32 nCostThreshold, + const Size& rSmallSize, + const Size& rLargeSize, + ::sd::PreviewRenderer& rRenderer); + + /** Use the PageObjectProvider to get access to the master page object. + + Note that this is only done, when either bForce is or the + PreviewProvider::GetCostIndex() returns 0. + + @param nCostThreshold + When this is zero or positive then the page object is created + only when the page object provider has a cost equal to or + smaller than this threshold. A negative value forces the + page object be created, regardless of the cost. + @param pDocument + This document of the MasterPageContainer may be used to create + a page object with or store one in. + @return + When the master page object is successfully provided then + 1 is returned, on no change then a 0 is provided, + on a masterpage-error a -1 is provided. + */ + int UpdatePageObject ( + sal_Int32 nCostThreshold, + SdDrawDocument* pDocument); + + enum URLClassification { + URLCLASS_USER, + URLCLASS_LAYOUT, + URLCLASS_PRESENTATION, + URLCLASS_OTHER, + URLCLASS_UNKNOWN, + URLCLASS_UNDETERMINED + }; + + URLClassification GetURLClassification(); + + /** The Token under which the MasterPageContainer gives access to the + object. + */ + MasterPageContainer::Token maToken; + + /** A rough specification of the origin of the master page. + */ + MasterPageContainer::Origin meOrigin; + + /** The URL is not empty for master pages loaded from a template + document. + */ + OUString msURL; + + /** Taken from the title of the template file. + */ + OUString msPageName; + + /** Taken from the master page object. + */ + OUString msStyleName; + + const bool mbIsPrecious; + + /** The actual master page. + */ + SdPage* mpMasterPage; + + /** A slide that uses the master page. + */ + SdPage* mpSlide; + + /** A small (the default size) preview of the master page. May be + empty. When this smaller preview is not empty then the larger one + is not empty, too. + */ + Image maSmallPreview; + + /** A large preview of the master page. May be empty. When this larger + preview is not empty then the smaller one is not empty, too. + */ + Image maLargePreview; + + /** The preview provider. May be empty. May be replaced during the + lifetime of a MasterPageDescriptor object. + */ + std::shared_ptr mpPreviewProvider; + + /** The master page provider. May be empty. May be replaced during + the lifetime of a MasterPageDescriptor object. + */ + std::shared_ptr mpPageObjectProvider; + + /** This index represents the order in which templates are provided via + the TemplateScanner. It defines the order in which the entries in + the AllMasterPagesSelector are displayed. The default value is -1. + */ + sal_Int32 mnTemplateIndex; + + URLClassification meURLClassification; + + sal_Int32 mnUseCount; + + class URLComparator { public: + OUString msURL; + explicit URLComparator (const OUString& sURL); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class StyleNameComparator { public: + OUString msStyleName; + explicit StyleNameComparator (const OUString& sStyleName); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class PageObjectComparator { public: + const SdPage* mpMasterPage; + explicit PageObjectComparator (const SdPage* pPageObject); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + }; + class AllComparator { public: + explicit AllComparator(const SharedMasterPageDescriptor& rDescriptor); + bool operator() (const SharedMasterPageDescriptor& rDescriptor); + private: + SharedMasterPageDescriptor mpDescriptor; + }; + +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPageObserver.cxx b/sd/source/ui/sidebar/MasterPageObserver.cxx new file mode 100644 index 000000000..017a0bcdf --- /dev/null +++ b/sd/source/ui/sidebar/MasterPageObserver.cxx @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + +class MasterPageObserver::Implementation + : public SfxListener +{ +public: + /** The single instance of this class. It is created on demand when + Instance() is called for the first time. + */ + static MasterPageObserver* mpInstance; + + /** The master page observer will listen to events of this document and + detect changes of the use of master pages. + */ + void RegisterDocument (SdDrawDocument& rDocument); + + /** The master page observer will stop to listen to events of this + document. + */ + void UnregisterDocument (SdDrawDocument& rDocument); + + /** Add a listener that is informed of master pages that are newly + assigned to slides or become unassigned. + @param rEventListener + The event listener to call for future events. Call + RemoveEventListener() before the listener is destroyed. + */ + void AddEventListener (const Link& rEventListener); + + /** Remove the given listener from the list of listeners. + @param rEventListener + After this method returns the given listener is not called back + from this object. Passing a listener that has not + been registered before is safe and is silently ignored. + */ + void RemoveEventListener (const Link& rEventListener); + +private: + ::std::vector> maListeners; + + struct DrawDocHash { + size_t operator()(SdDrawDocument* argument) const + { return reinterpret_cast(argument); } + }; + typedef std::unordered_map + MasterPageContainer; + MasterPageContainer maUsedMasterPages; + + virtual void Notify( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) override; + + void AnalyzeUsedMasterPages (SdDrawDocument& rDocument); + + void SendEvent (MasterPageObserverEvent& rEvent); +}; + +MasterPageObserver* MasterPageObserver::Implementation::mpInstance = nullptr; + +//===== MasterPageObserver ==================================================== + +MasterPageObserver& MasterPageObserver::Instance() +{ + if (Implementation::mpInstance == nullptr) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (Implementation::mpInstance == nullptr) + { + MasterPageObserver* pInstance = new MasterPageObserver (); + SdGlobalResourceContainer::Instance().AddResource ( + ::std::unique_ptr(pInstance)); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + Implementation::mpInstance = pInstance; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + + DBG_ASSERT(Implementation::mpInstance!=nullptr, + "MasterPageObserver::Instance(): instance is NULL"); + return *Implementation::mpInstance; +} + +void MasterPageObserver::RegisterDocument (SdDrawDocument& rDocument) +{ + mpImpl->RegisterDocument (rDocument); +} + +void MasterPageObserver::UnregisterDocument (SdDrawDocument& rDocument) +{ + mpImpl->UnregisterDocument (rDocument); +} + +void MasterPageObserver::AddEventListener (const Link& rEventListener) +{ + + mpImpl->AddEventListener (rEventListener); +} + +void MasterPageObserver::RemoveEventListener (const Link& rEventListener) +{ + mpImpl->RemoveEventListener (rEventListener); +} + +MasterPageObserver::MasterPageObserver() + : mpImpl (new Implementation) +{} + +MasterPageObserver::~MasterPageObserver() +{} + +//===== MasterPageObserver::Implementation ==================================== + +void MasterPageObserver::Implementation::RegisterDocument ( + SdDrawDocument& rDocument) +{ + // Gather the names of all the master pages in the given document. + MasterPageContainer::mapped_type aMasterPageSet; + sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard); + for (sal_uInt16 nIndex=0; nIndexGetName()); + } + + bool bAlreadyExists = maUsedMasterPages.find(&rDocument) != maUsedMasterPages.end(); + maUsedMasterPages[&rDocument] = aMasterPageSet; + + if (!bAlreadyExists) + StartListening (rDocument); +} + +void MasterPageObserver::Implementation::UnregisterDocument ( + SdDrawDocument& rDocument) +{ + EndListening (rDocument); + + MasterPageContainer::iterator aMasterPageDescriptor(maUsedMasterPages.find(&rDocument)); + if(aMasterPageDescriptor != maUsedMasterPages.end()) + maUsedMasterPages.erase(aMasterPageDescriptor); +} + +void MasterPageObserver::Implementation::AddEventListener ( + const Link& rEventListener) +{ + if (::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener) != maListeners.end()) + return; + + maListeners.push_back (rEventListener); + + // Tell the new listener about all the master pages that are + // currently in use. + for (const auto& rDocument : maUsedMasterPages) + { + ::std::set::reverse_iterator aNameIterator; + for (aNameIterator=rDocument.second.rbegin(); + aNameIterator!=rDocument.second.rend(); + ++aNameIterator) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_EXISTS, + *aNameIterator); + SendEvent (aEvent); + } + } +} + +void MasterPageObserver::Implementation::RemoveEventListener ( + const Link& rEventListener) +{ + maListeners.erase ( + ::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener)); +} + +void MasterPageObserver::Implementation::Notify( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast(&rHint); + + switch (pSdrHint->GetKind()) + { + case SdrHintKind::PageOrderChange: + // Process the modified set of pages only when the number of + // standard and notes master pages are equal. This test + // filters out events that are sent in between the insertion + // of a new standard master page and a new notes master + // page. + if (auto pDrawDocument = dynamic_cast( &rBroadcaster )) + { + if (pDrawDocument->GetMasterSdPageCount(PageKind::Standard) + == pDrawDocument->GetMasterSdPageCount(PageKind::Notes)) + { + AnalyzeUsedMasterPages (*pDrawDocument); + } + } + break; + + default: + break; + } +} + +void MasterPageObserver::Implementation::AnalyzeUsedMasterPages ( + SdDrawDocument& rDocument) +{ + // Create a set of names of the master pages used by the given document. + sal_uInt16 nMasterPageCount = rDocument.GetMasterSdPageCount(PageKind::Standard); + ::std::set aCurrentMasterPages; + for (sal_uInt16 nIndex=0; nIndexGetName()); + } + + std::vector aNewMasterPages; + std::vector aRemovedMasterPages; + MasterPageContainer::iterator aOldMasterPagesDescriptor ( + maUsedMasterPages.find(&rDocument)); + if (aOldMasterPagesDescriptor == maUsedMasterPages.end()) + return; + + // Send events about the newly used master pages. + ::std::set_difference ( + aCurrentMasterPages.begin(), + aCurrentMasterPages.end(), + aOldMasterPagesDescriptor->second.begin(), + aOldMasterPagesDescriptor->second.end(), + std::back_inserter(aNewMasterPages)); + for (const auto& aNewMasterPage : aNewMasterPages) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_ADDED, + aNewMasterPage); + SendEvent (aEvent); + } + + // Send events about master pages that are not used any longer. + ::std::set_difference ( + aOldMasterPagesDescriptor->second.begin(), + aOldMasterPagesDescriptor->second.end(), + aCurrentMasterPages.begin(), + aCurrentMasterPages.end(), + std::back_inserter(aRemovedMasterPages)); + for (const auto& aRemovedMasterPage : aRemovedMasterPages) + { + MasterPageObserverEvent aEvent ( + MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED, + aRemovedMasterPage); + SendEvent (aEvent); + } + + // Store the new list of master pages. + aOldMasterPagesDescriptor->second = aCurrentMasterPages; +} + +void MasterPageObserver::Implementation::SendEvent ( + MasterPageObserverEvent& rEvent) +{ + for (const auto& aLink : maListeners) + { + aLink.Call(rEvent); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPagesSelector.cxx b/sd/source/ui/sidebar/MasterPagesSelector.cxx new file mode 100644 index 000000000..979726910 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPagesSelector.cxx @@ -0,0 +1,620 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include "MasterPagesSelector.hxx" + +#include "MasterPageContainer.hxx" +#include "DocumentHelper.hxx" +#include +#include +#include +#include + +#include +#include +#include "PreviewValueSet.hxx" +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::text; + +namespace sd::sidebar { + + /** menu entry that is executed as default action when the left mouse button is + clicked over a master page. + */ +constexpr OStringLiteral gsDefaultClickAction = "applyselect"; + +MasterPagesSelector::MasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr& rpContainer, + const css::uno::Reference& rxSidebar, + const OUString& rUIFileName, + const OString& rValueSetName) + : PanelLayout( pParent, "MasterPagePanel", rUIFileName ), + mpContainer(rpContainer), + mxPreviewValueSet(new PreviewValueSet), + mxPreviewValueSetWin(new weld::CustomWeld(*m_xBuilder, rValueSetName, *mxPreviewValueSet)), + mrDocument(rDocument), + mrBase(rBase), + mxSidebar(rxSidebar) +{ + mxPreviewValueSet->SetSelectHdl ( + LINK(this, MasterPagesSelector, ClickHandler)); + mxPreviewValueSet->SetContextMenuHandler ( + LINK(this, MasterPagesSelector, ContextMenuHandler)); + mxPreviewValueSet->SetStyle(mxPreviewValueSet->GetStyle() | WB_NO_DIRECTSELECT); + + if (mxPreviewValueSet->GetDrawingArea()->get_ref_device().GetDPIScaleFactor() > 1) + mpContainer->SetPreviewSize(MasterPageContainer::LARGE); + + mxPreviewValueSet->SetPreviewSize(mpContainer->GetPreviewSizePixel()); + mxPreviewValueSet->Show(); + + mxPreviewValueSet->SetColor(sfx2::sidebar::Theme::GetColor(sfx2::sidebar::Theme::Color_PanelBackground)); + + Link aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener)); + mpContainer->AddChangeListener(aChangeListener); +} + +MasterPagesSelector::~MasterPagesSelector() +{ + Clear(); + UpdateLocks(ItemList()); + + Link aChangeListener (LINK(this,MasterPagesSelector,ContainerChangeListener)); + mpContainer->RemoveChangeListener(aChangeListener); + mpContainer.reset(); + mxPreviewValueSetWin.reset(); + mxPreviewValueSet.reset(); +} + +void MasterPagesSelector::LateInit() +{ +} + +sal_Int32 MasterPagesSelector::GetPreferredHeight (sal_Int32 nWidth) +{ + const ::osl::MutexGuard aGuard (maMutex); + + return mxPreviewValueSet->GetPreferredHeight (nWidth); +} + +void MasterPagesSelector::UpdateLocks (const ItemList& rItemList) +{ + ItemList aNewLockList; + + // In here we first lock the master pages in the given list and then + // release the locks acquired in a previous call to this method. When + // this were done the other way round the lock count of some master + // pages might drop temporarily to 0 and would lead to unnecessary + // deletion and re-creation of MasterPageDescriptor objects. + + // Lock the master pages in the given list. + for (const auto& rItem : rItemList) + { + mpContainer->AcquireToken(rItem); + aNewLockList.push_back(rItem); + } + + // Release the previously locked master pages. + for (const auto& rPage : maLockedMasterPages) + mpContainer->ReleaseToken(rPage); + + maLockedMasterPages.swap(aNewLockList); +} + +void MasterPagesSelector::Fill() +{ + ::std::unique_ptr pItemList (new ItemList); + + Fill(*pItemList); + + UpdateLocks(*pItemList); + UpdateItemList(std::move(pItemList)); +} + +OUString MasterPagesSelector::GetContextMenuUIFile() const +{ + return "modules/simpress/ui/mastermenu.ui"; +} + +IMPL_LINK_NOARG(MasterPagesSelector, ClickHandler, ValueSet*, void) +{ + // We use the framework to assign the clicked-on master page because we + // so use the same mechanism as the context menu does (where we do not + // have the option to call the assignment method directly.) + ExecuteCommand(gsDefaultClickAction); +} + +IMPL_LINK(MasterPagesSelector, ContextMenuHandler, const Point*, pPos, void) +{ + if (pPos) + { + // Here we only prepare the display of the context menu: on right + // click the item under the mouse is selected. + mxPreviewValueSet->GrabFocus(); + mxPreviewValueSet->ReleaseMouse(); + + sal_uInt16 nIndex = mxPreviewValueSet->GetItemId(*pPos); + if (nIndex > 0) + mxPreviewValueSet->SelectItem(nIndex); + } + + // Now do the actual display of the context menu + ShowContextMenu(pPos); +} + +void MasterPagesSelector::ShowContextMenu(const Point* pPos) +{ + // Use the currently selected item and show the popup menu in its + // center. + const sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + if (nIndex <= 0) + return; + + // The position of the upper left corner of the context menu is + // taken either from the mouse position (when the command was sent + // as reaction to a right click) or in the center of the selected + // item (when the command was sent as reaction to Shift+F10.) + Point aPosition; + if (!pPos) + { + ::tools::Rectangle aBBox (mxPreviewValueSet->GetItemRect(nIndex)); + aPosition = aBBox.Center(); + } + else + aPosition = *pPos; + + // Setup the menu. + weld::Widget* pParent = mxPreviewValueSet->GetDrawingArea(); + std::unique_ptr xBuilder(Application::CreateBuilder(pParent, GetContextMenuUIFile())); + std::unique_ptr xMenu(xBuilder->weld_menu("menu")); + ProcessPopupMenu(*xMenu); + ::tools::Rectangle aRect(aPosition, Size(1,1)); + // Show the menu. + ExecuteCommand(xMenu->popup_at_rect(pParent, aRect)); +} + +void MasterPagesSelector::ProcessPopupMenu(weld::Menu& rMenu) +{ + // Disable some entries. + if (mpContainer->GetPreviewSize() == MasterPageContainer::SMALL) + rMenu.set_sensitive("small", false); + else + rMenu.set_sensitive("large", false); +} + +void MasterPagesSelector::ExecuteCommand(const OString &rIdent) +{ + if (rIdent == "applyall") + { + mrBase.SetBusyState (true); + AssignMasterPageToAllSlides (GetSelectedMasterPage()); + mrBase.SetBusyState (false); + } + else if (rIdent == "applyselect") + { + mrBase.SetBusyState (true); + AssignMasterPageToSelectedSlides (GetSelectedMasterPage()); + mrBase.SetBusyState (false); + } + else if (rIdent == "large") + { + mrBase.SetBusyState (true); + mpContainer->SetPreviewSize(MasterPageContainer::LARGE); + mrBase.SetBusyState (false); + if (mxSidebar.is()) + mxSidebar->requestLayout(); + } + else if (rIdent == "small") + { + mrBase.SetBusyState (true); + mpContainer->SetPreviewSize(MasterPageContainer::SMALL); + mrBase.SetBusyState (false); + if (mxSidebar.is()) + mxSidebar->requestLayout(); + } + else if (rIdent == "edit") + { + using namespace ::com::sun::star; + uno::Reference xSelectedMaster; + SdPage* pMasterPage = GetSelectedMasterPage(); + assert(pMasterPage); //rhbz#902884 + if (pMasterPage) + xSelectedMaster.set(pMasterPage->getUnoPage(), uno::UNO_QUERY); + SfxViewFrame* pViewFrame = mrBase.GetViewFrame(); + if (pViewFrame != nullptr && xSelectedMaster.is()) + { + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if (pDispatcher != nullptr) + { + sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + pDispatcher->Execute(SID_MASTERPAGE, SfxCallMode::SYNCHRON); + mxPreviewValueSet->SelectItem (nIndex); + mrBase.GetDrawController().setCurrentPage(xSelectedMaster); + } + } + } +} + +IMPL_LINK(MasterPagesSelector, ContainerChangeListener, MasterPageContainerChangeEvent&, rEvent, void) +{ + NotifyContainerChangeEvent(rEvent); +} + +SdPage* MasterPagesSelector::GetSelectedMasterPage() +{ + const ::osl::MutexGuard aGuard (maMutex); + + SdPage* pMasterPage = nullptr; + sal_uInt16 nIndex = mxPreviewValueSet->GetSelectedItemId(); + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + pMasterPage = mpContainer->GetPageObjectForToken(pData->second, true); + } + return pMasterPage; +} + +/** Assemble a list of all slides of the document and pass it to + AssignMasterPageToPageList(). +*/ +void MasterPagesSelector::AssignMasterPageToAllSlides (SdPage* pMasterPage) +{ + if (pMasterPage == nullptr) + return; + + sal_uInt16 nPageCount = mrDocument.GetSdPageCount(PageKind::Standard); + if (nPageCount == 0) + return; + + // Get a list of all pages. As a little optimization we only + // include pages that do not already have the given master page + // assigned. + OUString sFullLayoutName(pMasterPage->GetLayoutName()); + ::sd::slidesorter::SharedPageSelection pPageList = + std::make_shared<::sd::slidesorter::SlideSorterViewShell::PageSelection>(); + for (sal_uInt16 nPageIndex=0; nPageIndexGetLayoutName() != sFullLayoutName) + { + pPageList->push_back (pPage); + } + } + + AssignMasterPageToPageList(pMasterPage, pPageList); +} + +/** Assemble a list of the currently selected slides (selected in a visible + slide sorter) and pass it to AssignMasterPageToPageList(). +*/ +void MasterPagesSelector::AssignMasterPageToSelectedSlides ( + SdPage* pMasterPage) +{ + using namespace ::sd::slidesorter; + using namespace ::sd::slidesorter::controller; + + if (pMasterPage == nullptr) + return; + + // Find a visible slide sorter. + SlideSorterViewShell* pSlideSorter = SlideSorterViewShell::GetSlideSorter(mrBase); + if (pSlideSorter == nullptr) + return; + + // Get a list of selected pages. + SharedPageSelection pPageSelection = pSlideSorter->GetPageSelection(); + if (pPageSelection->empty()) + return; + + AssignMasterPageToPageList(pMasterPage, pPageSelection); + + // Restore the previous selection. + pSlideSorter->SetPageSelection(pPageSelection); +} + +void MasterPagesSelector::AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr>& rPageList) +{ + DocumentHelper::AssignMasterPageToPageList(mrDocument, pMasterPage, rPageList); +} + +void MasterPagesSelector::NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent) +{ + const ::osl::MutexGuard aGuard (maMutex); + + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::SIZE_CHANGED: + mxPreviewValueSet->SetPreviewSize(mpContainer->GetPreviewSizePixel()); + UpdateAllPreviews(); + break; + + case MasterPageContainerChangeEvent::EventType::PREVIEW_CHANGED: + { + int nIndex (GetIndexForToken(rEvent.maChildToken)); + if (nIndex >= 0) + { + mxPreviewValueSet->SetItemImage ( + static_cast(nIndex), + mpContainer->GetPreviewForToken(rEvent.maChildToken)); + mxPreviewValueSet->Invalidate(mxPreviewValueSet->GetItemRect(static_cast(nIndex))); + } + } + break; + + case MasterPageContainerChangeEvent::EventType::DATA_CHANGED: + { + InvalidateItem(rEvent.maChildToken); + Fill(); + } + break; + + case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED: + { + int nIndex (GetIndexForToken(rEvent.maChildToken)); + SetItem(nIndex, MasterPageContainer::NIL_TOKEN); + break; + } + + default: + break; + } +} + +MasterPagesSelector::UserData* MasterPagesSelector::GetUserData (int nIndex) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + if (nIndex>0 && o3tl::make_unsigned(nIndex)<=mxPreviewValueSet->GetItemCount()) + return static_cast(mxPreviewValueSet->GetItemData(static_cast(nIndex))); + else + return nullptr; +} + +void MasterPagesSelector::SetUserData (int nIndex, std::unique_ptr pData) +{ + const ::osl::MutexGuard aGuard (maMutex); + + delete GetUserData(nIndex); + mxPreviewValueSet->SetItemData(static_cast(nIndex), pData.release()); +} + +void MasterPagesSelector::SetItem ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + RemoveTokenToIndexEntry(nIndex,aToken); + + if (nIndex <= 0) + return; + + if (aToken != MasterPageContainer::NIL_TOKEN) + { + Image aPreview (mpContainer->GetPreviewForToken(aToken)); + MasterPageContainer::PreviewState eState (mpContainer->GetPreviewState(aToken)); + + if (aPreview.GetSizePixel().Width()>0) + { + if (mxPreviewValueSet->GetItemPos(nIndex) != VALUESET_ITEM_NOTFOUND) + { + mxPreviewValueSet->SetItemImage(nIndex,aPreview); + mxPreviewValueSet->SetItemText(nIndex, mpContainer->GetPageNameForToken(aToken)); + } + else + { + mxPreviewValueSet->InsertItem ( + nIndex, + aPreview, + mpContainer->GetPageNameForToken(aToken), + nIndex); + } + SetUserData(nIndex, std::make_unique(nIndex,aToken)); + + AddTokenToIndexEntry(nIndex,aToken); + } + + if (eState == MasterPageContainer::PS_CREATABLE) + mpContainer->RequestPreview(aToken); + } + else + { + mxPreviewValueSet->RemoveItem(nIndex); + } + +} + +void MasterPagesSelector::AddTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + maTokenToValueSetIndex[aToken] = nIndex; +} + +void MasterPagesSelector::RemoveTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aNewToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + // Get the token that the index pointed to previously. + MasterPageContainer::Token aOldToken (pData->second); + + if (aNewToken != aOldToken + && nIndex == GetIndexForToken(aOldToken)) + { + maTokenToValueSetIndex[aOldToken] = 0; + } + } +} + +void MasterPagesSelector::InvalidatePreview (const SdPage* pPage) +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + MasterPageContainer::Token aToken (pData->second); + if (pPage == mpContainer->GetPageObjectForToken(aToken,false)) + { + mpContainer->InvalidatePreview(aToken); + mpContainer->RequestPreview(aToken); + break; + } + } + } +} + +void MasterPagesSelector::UpdateAllPreviews() +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + if (pData != nullptr) + { + MasterPageContainer::Token aToken (pData->second); + mxPreviewValueSet->SetItemImage( + nIndex, + mpContainer->GetPreviewForToken(aToken)); + if (mpContainer->GetPreviewState(aToken) == MasterPageContainer::PS_CREATABLE) + mpContainer->RequestPreview(aToken); + } + } + mxPreviewValueSet->Rearrange(); +} + +void MasterPagesSelector::ClearPageSet() +{ + const ::osl::MutexGuard aGuard (maMutex); + + for (size_t nIndex=1; nIndex<=mxPreviewValueSet->GetItemCount(); nIndex++) + { + UserData* pData = GetUserData(nIndex); + delete pData; + } + mxPreviewValueSet->Clear(); +} + +void MasterPagesSelector::SetHelpId( const OString& aId ) +{ + const ::osl::MutexGuard aGuard (maMutex); + + mxPreviewValueSet->SetHelpId( aId ); +} + +sal_Int32 MasterPagesSelector::GetIndexForToken (MasterPageContainer::Token aToken) const +{ + const ::osl::MutexGuard aGuard (maMutex); + + TokenToValueSetIndex::const_iterator iIndex (maTokenToValueSetIndex.find(aToken)); + if (iIndex != maTokenToValueSetIndex.end()) + return iIndex->second; + else + return -1; +} + +void MasterPagesSelector::Clear() +{ + const ::osl::MutexGuard aGuard (maMutex); + + ClearPageSet(); +} + +void MasterPagesSelector::InvalidateItem (MasterPageContainer::Token aToken) +{ + const ::osl::MutexGuard aGuard (maMutex); + + auto iItem = std::find(maCurrentItemList.begin(), maCurrentItemList.end(), aToken); + if (iItem != maCurrentItemList.end()) + *iItem = MasterPageContainer::NIL_TOKEN; +} + +void MasterPagesSelector::UpdateItemList (::std::unique_ptr && pNewItemList) +{ + const ::osl::MutexGuard aGuard (maMutex); + + ItemList::const_iterator iNewItem (pNewItemList->begin()); + ItemList::const_iterator iCurrentItem (maCurrentItemList.begin()); + ItemList::const_iterator iNewEnd (pNewItemList->end()); + ItemList::const_iterator iCurrentEnd (maCurrentItemList.end()); + sal_uInt16 nIndex (1); + + // Update existing items. + for ( ; iNewItem!=iNewEnd && iCurrentItem!=iCurrentEnd; ++iNewItem, ++iCurrentItem,++nIndex) + { + if (*iNewItem != *iCurrentItem) + { + SetItem(nIndex,*iNewItem); + } + } + + // Append new items. + for ( ; iNewItem!=iNewEnd; ++iNewItem,++nIndex) + { + SetItem(nIndex,*iNewItem); + } + + // Remove trailing items. + for ( ; iCurrentItem!=iCurrentEnd; ++iCurrentItem,++nIndex) + { + SetItem(nIndex,MasterPageContainer::NIL_TOKEN); + } + + maCurrentItemList.swap(*pNewItemList); + + mxPreviewValueSet->Rearrange(); + if (mxSidebar.is()) + mxSidebar->requestLayout(); +} + +css::ui::LayoutSize MasterPagesSelector::GetHeightForWidth (const sal_Int32 nWidth) +{ + const sal_Int32 nHeight (GetPreferredHeight(nWidth)); + return css::ui::LayoutSize(nHeight,nHeight,nHeight); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/MasterPagesSelector.hxx b/sd/source/ui/sidebar/MasterPagesSelector.hxx new file mode 100644 index 000000000..1b6932789 --- /dev/null +++ b/sd/source/ui/sidebar/MasterPagesSelector.hxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include "MasterPageContainer.hxx" +#include "PreviewValueSet.hxx" +#include +#include + +#include + +namespace com::sun::star::ui { class XSidebar; } +class MouseEvent; +class SdDrawDocument; +class SdPage; + +namespace sd { +class ViewShellBase; +} + +namespace sd::sidebar { + +/** Base class of a menu that lets the user select from a list of + templates or designs that are loaded from files. +*/ +class MasterPagesSelector : public PanelLayout + , public sfx2::sidebar::ILayoutableWindow +{ +public: + MasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr& rpContainer, + const css::uno::Reference& rxSidebar, + const OUString& rUIFileName, + const OString& rValueSetName); + virtual ~MasterPagesSelector() override; + + virtual void LateInit(); + + sal_Int32 GetPreferredHeight (sal_Int32 nWidth); + + /** Make the selector empty. This method clear the value set from any + entries. Override this method to add functionality, especially to + destroy objects set as data items at the value set. + */ + void ClearPageSet(); + + void SetHelpId( const OString& aId ); + + /** Mark the preview that belongs to the given index as not up-to-date + anymore with respect to page content or preview size. + The implementation of this method will either sunchronously or + asynchronously call UpdatePreview(). + @param nIndex + Index into the value set control that is used for displaying the + previews. + */ + void InvalidatePreview (const SdPage* pPage); + + void UpdateAllPreviews(); + + void ShowContextMenu(const Point* pPos); + + // ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + +protected: + mutable ::osl::Mutex maMutex; + std::shared_ptr mpContainer; + + std::unique_ptr mxPreviewValueSet; + std::unique_ptr mxPreviewValueSetWin; + + SdDrawDocument& mrDocument; + ViewShellBase& mrBase; + + SdPage* GetSelectedMasterPage(); + + /** Assign the given master page to all slides of the document. + @param pMasterPage + The master page to assign to all slides. + */ + void AssignMasterPageToAllSlides (SdPage* pMasterPage); + + /** Assign the given master page to all slides that are selected in a + slide sorter that is displayed in the lef or center pane. When both + panes display a slide sorter then the one in the center pane is + used. + */ + void AssignMasterPageToSelectedSlides (SdPage* pMasterPage); + + virtual void AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr>& rPageList); + + virtual void NotifyContainerChangeEvent (const MasterPageContainerChangeEvent& rEvent); + + typedef ::std::pair UserData; + UserData* GetUserData (int nIndex) const; + void SetUserData (int nIndex, std::unique_ptr pData); + + sal_Int32 GetIndexForToken (MasterPageContainer::Token aToken) const; + typedef ::std::vector ItemList; + void UpdateItemList (::std::unique_ptr && pList); + void Clear(); + /** Invalidate the specified item so that on the next Fill() this item + is updated. + */ + void InvalidateItem (MasterPageContainer::Token aToken); + + // For every item in the ValueSet we store its associated token. This + // allows a faster access and easier change tracking. + ItemList maCurrentItemList; + typedef ::std::map TokenToValueSetIndex; + TokenToValueSetIndex maTokenToValueSetIndex; + + ItemList maLockedMasterPages; + /** Lock master pages in the given list and release locks that were + previously acquired. + */ + void UpdateLocks (const ItemList& rItemList); + + void Fill(); + virtual void Fill (ItemList& rItemList) = 0; + + /** Give derived classes the opportunity to provide their own context + menu. If they do then they probably have to provide their own + Execute() and GetState() methods as well. + */ + virtual OUString GetContextMenuUIFile() const; + + virtual void ProcessPopupMenu(weld::Menu& rMenu); + virtual void ExecuteCommand(const OString& rIdent); + +private: + css::uno::Reference mxSidebar; + + /** The offset between ValueSet index and MasterPageContainer::Token + last seen. This value is used heuristically to speed up the lookup + of an index for a token. + */ + DECL_LINK(ClickHandler, ValueSet*, void); + DECL_LINK(ContextMenuHandler, const Point*, void); + DECL_LINK(ContainerChangeListener, MasterPageContainerChangeEvent&, void); + + void SetItem ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); + void AddTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); + void RemoveTokenToIndexEntry ( + sal_uInt16 nIndex, + MasterPageContainer::Token aToken); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/NavigatorWrapper.cxx b/sd/source/ui/sidebar/NavigatorWrapper.cxx new file mode 100644 index 000000000..95d4a66ae --- /dev/null +++ b/sd/source/ui/sidebar/NavigatorWrapper.cxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "NavigatorWrapper.hxx" +#include +#include + +namespace sd::sidebar { + +NavigatorWrapper::NavigatorWrapper ( + weld::Widget* pParent, + sd::ViewShellBase& rViewShellBase, + SfxBindings* pBindings) + : SdNavigatorWin(pParent, pBindings, nullptr) + , mrViewShellBase(rViewShellBase) +{ + SetUpdateRequestFunctor( + [this] () { return this->UpdateNavigator(); }); +} + +css::ui::LayoutSize NavigatorWrapper::GetHeightForWidth (const sal_Int32) +{ + return css::ui::LayoutSize(-1,-1,-1); +} + +void NavigatorWrapper::UpdateNavigator() +{ + InitTreeLB(mrViewShellBase.GetDocument()); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/NavigatorWrapper.hxx b/sd/source/ui/sidebar/NavigatorWrapper.hxx new file mode 100644 index 000000000..6632d796f --- /dev/null +++ b/sd/source/ui/sidebar/NavigatorWrapper.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include + +class SfxBindings; +namespace sd { class ViewShellBase; } + +namespace sd::sidebar { + +/** Present the navigator as control that can be displayed inside the + sidebar. + This wrapper has two main responsibilities: + - Watch for document changes and update the navigator when one + happens. + - Forward size changes from sidebar to navigator. +*/ +class NavigatorWrapper + : public SdNavigatorWin, + public sfx2::sidebar::ILayoutableWindow +{ +public: + NavigatorWrapper ( + weld::Widget* pParent, + sd::ViewShellBase& rViewShellBase, + SfxBindings* pBindings); + + // From ILayoutableWindow + virtual css::ui::LayoutSize GetHeightForWidth (const sal_Int32 nWidth) override; + +private: + ViewShellBase& mrViewShellBase; + + void UpdateNavigator(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PageMarginUtils.hxx b/sd/source/ui/sidebar/PageMarginUtils.hxx new file mode 100644 index 000000000..9a1f83493 --- /dev/null +++ b/sd/source/ui/sidebar/PageMarginUtils.hxx @@ -0,0 +1,159 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#include +#include +#define SDPAGE_NO_MARGIN 0 +#define SDPAGE_NARROW_VALUE 635 +#define SDPAGE_MODERATE_LR 955 +#define SDPAGE_NORMAL_VALUE 1000 +#define SDPAGE_WIDE_VALUE1 1270 +#define SDPAGE_WIDE_VALUE2 2540 +#define SDPAGE_WIDE_VALUE3 1590 +#define SDPAGE_UNIT_THRESHOLD 5 + +namespace sd::sidebar{ + +bool IsNone( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NO_MARGIN ) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NO_MARGIN) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNone( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NO_MARGIN; + nPageRightMargin = SDPAGE_NO_MARGIN; + nPageTopMargin = SDPAGE_NO_MARGIN; + nPageBottomMargin = SDPAGE_NO_MARGIN; +} + +bool IsNarrow( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NARROW_VALUE) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNarrow( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NARROW_VALUE; + nPageRightMargin = SDPAGE_NARROW_VALUE; + nPageTopMargin = SDPAGE_NARROW_VALUE; + nPageBottomMargin = SDPAGE_NARROW_VALUE; +} + +bool IsModerate( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_MODERATE_LR) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_MODERATE_LR) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetModerate( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_MODERATE_LR; + nPageRightMargin = SDPAGE_MODERATE_LR; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsNormal075( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_NORMAL_VALUE) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal075( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_NORMAL_VALUE; + nPageRightMargin = SDPAGE_NORMAL_VALUE; + nPageTopMargin = SDPAGE_NORMAL_VALUE; + nPageBottomMargin = SDPAGE_NORMAL_VALUE; +} + +bool IsNormal100( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal100( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE1; + nPageRightMargin = SDPAGE_WIDE_VALUE1; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsNormal125( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE3) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE3) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetNormal125( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE3; + nPageRightMargin = SDPAGE_WIDE_VALUE3; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} + +bool IsWide( const ::tools::Long nPageLeftMargin, const ::tools::Long nPageRightMargin, + const ::tools::Long nPageTopMargin, const ::tools::Long nPageBottomMargin ) +{ + return( std::abs(nPageLeftMargin - SDPAGE_WIDE_VALUE2) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageRightMargin - SDPAGE_WIDE_VALUE2) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageTopMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD && + std::abs(nPageBottomMargin - SDPAGE_WIDE_VALUE1) <= SDPAGE_UNIT_THRESHOLD ); +} + +void SetWide( ::tools::Long& nPageLeftMargin, ::tools::Long& nPageRightMargin, + ::tools::Long& nPageTopMargin, ::tools::Long& nPageBottomMargin ) +{ + nPageLeftMargin = SDPAGE_WIDE_VALUE2; + nPageRightMargin = SDPAGE_WIDE_VALUE2; + nPageTopMargin = SDPAGE_WIDE_VALUE1; + nPageBottomMargin = SDPAGE_WIDE_VALUE1; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PanelFactory.cxx b/sd/source/ui/sidebar/PanelFactory.cxx new file mode 100644 index 000000000..c7ca8c25c --- /dev/null +++ b/sd/source/ui/sidebar/PanelFactory.cxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PanelFactory.hxx" +#include +#include +#include +#include "LayoutMenu.hxx" +#include "CurrentMasterPagesSelector.hxx" +#include "RecentMasterPagesSelector.hxx" +#include "AllMasterPagesSelector.hxx" +#include +#include "NavigatorWrapper.hxx" +#include +#include +#include "SlideBackground.hxx" + +#include +#include +#include +#include +#include + +using namespace css; +using namespace css::uno; +using namespace ::sd::framework; + +namespace sd::sidebar { + +//----- PanelFactory -------------------------------------------------------- + +PanelFactory::PanelFactory() +{ +} + +PanelFactory::~PanelFactory() +{ +} + +// XUIElementFactory + +Reference SAL_CALL PanelFactory::createUIElement ( + const OUString& rsUIElementResourceURL, + const css::uno::Sequence& rArguments) +{ + // Process arguments. + const ::comphelper::NamedValueCollection aArguments (rArguments); + Reference xFrame (aArguments.getOrDefault("Frame", Reference())); + Reference xParentWindow (aArguments.getOrDefault("ParentWindow", Reference())); + Reference xSidebar (aArguments.getOrDefault("Sidebar", Reference())); + + // Throw exceptions when the arguments are not as expected. + weld::Widget* pParent(nullptr); + if (weld::TransportAsXWindow* pTunnel = dynamic_cast(xParentWindow.get())) + pParent = pTunnel->getWidget(); + + if (!pParent) + throw RuntimeException( + "PanelFactory::createUIElement called without ParentWindow"); + if ( ! xFrame.is()) + throw RuntimeException( + "PanelFactory::createUIElement called without XFrame"); + + // Tunnel through the controller to obtain a ViewShellBase. + ViewShellBase* pBase = nullptr; + auto pController = comphelper::getFromUnoTunnel(xFrame->getController()); + if (pController != nullptr) + pBase = pController->GetViewShellBase(); + if (pBase == nullptr) + throw RuntimeException("can not get ViewShellBase for frame"); + + // Get bindings from given arguments. + const sal_uInt64 nBindingsValue (aArguments.getOrDefault("SfxBindings", sal_uInt64(0))); + SfxBindings* pBindings = reinterpret_cast(nBindingsValue); + + // Create a framework view. + std::unique_ptr xControl; + css::ui::LayoutSize aLayoutSize (-1,-1,-1); + + /** Note that these names have to be identical to (the tail of) + the entries in officecfg/registry/data/org/openoffice/Office/Impress.xcu + for the TaskPanelFactory. + */ + if (rsUIElementResourceURL.endsWith("/CustomAnimations")) + xControl = std::make_unique(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/Layouts")) + xControl = std::make_unique(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/AllMasterPages")) + xControl = AllMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/RecentMasterPages")) + xControl = RecentMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/UsedMasterPages")) + xControl = CurrentMasterPagesSelector::Create(pParent, *pBase, xSidebar); + else if (rsUIElementResourceURL.endsWith("/SlideTransitions")) + xControl = std::make_unique(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/TableDesign")) + xControl = std::make_unique(pParent, *pBase); + else if (rsUIElementResourceURL.endsWith("/NavigatorPanel")) + xControl = std::make_unique(pParent, *pBase, pBindings); + else if (rsUIElementResourceURL.endsWith("/SlideBackgroundPanel")) + xControl = std::make_unique(pParent, *pBase, xFrame, pBindings); + + if (!xControl) + throw lang::IllegalArgumentException(); + + // Create a wrapper around the control that implements the + // necessary UNO interfaces. + return sfx2::sidebar::SidebarPanelBase::Create( + rsUIElementResourceURL, + xFrame, + std::move(xControl), + aLayoutSize); +} + +} // end of namespace sd::sidebar + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +org_openoffice_comp_Draw_framework_PanelFactory_get_implementation(css::uno::XComponentContext* /*context*/, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::sidebar::PanelFactory); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PanelFactory.hxx b/sd/source/ui/sidebar/PanelFactory.hxx new file mode 100644 index 000000000..77fc17dbc --- /dev/null +++ b/sd/source/ui/sidebar/PanelFactory.hxx @@ -0,0 +1,49 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include + +#include + +namespace sd::sidebar { + +typedef comphelper::WeakComponentImplHelper < + css::ui::XUIElementFactory + > PanelFactoryInterfaceBase; + +class PanelFactory final + : public PanelFactoryInterfaceBase +{ +public: + explicit PanelFactory (); + virtual ~PanelFactory() override; + PanelFactory(const PanelFactory&) = delete; + PanelFactory& operator=(const PanelFactory&) = delete; + + // XUIElementFactory + + css::uno::Reference SAL_CALL createUIElement ( + const OUString& rsResourceURL, + const css::uno::Sequence& rArguments) override; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PreviewValueSet.cxx b/sd/source/ui/sidebar/PreviewValueSet.cxx new file mode 100644 index 000000000..f752d60eb --- /dev/null +++ b/sd/source/ui/sidebar/PreviewValueSet.cxx @@ -0,0 +1,127 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PreviewValueSet.hxx" +#include + +namespace sd::sidebar { + +const int gnBorderWidth(3); +const int gnBorderHeight(3); + +PreviewValueSet::PreviewValueSet() + : ValueSet(nullptr) + , maPreviewSize(10,10) +{ + SetStyle ( + GetStyle() + & ~(WB_ITEMBORDER)// | WB_MENUSTYLEVALUESET) + // | WB_FLATVALUESET); + ); +} + +void PreviewValueSet::SetDrawingArea(weld::DrawingArea* pDrawingArea) +{ + ValueSet::SetDrawingArea(pDrawingArea); + + SetColCount(2); + SetExtraSpacing (2); +} + +PreviewValueSet::~PreviewValueSet() +{ +} + +void PreviewValueSet::SetPreviewSize (const Size& rSize) +{ + maPreviewSize = rSize; +} + +void PreviewValueSet::SetContextMenuHandler(const Link& rLink) +{ + maContextMenuHandler = rLink; +} + +bool PreviewValueSet::Command(const CommandEvent& rEvent) +{ + if (rEvent.GetCommand() != CommandEventId::ContextMenu) + return ValueSet::Command(rEvent); + maContextMenuHandler.Call(rEvent.IsMouseEvent() ? &rEvent.GetMousePosPixel() : nullptr); + return true; +} + +void PreviewValueSet::Resize() +{ + ValueSet::Resize(); + + Size aWindowSize (GetOutputSizePixel()); + if (!aWindowSize.IsEmpty()) + { + Rearrange(); + } +} + +void PreviewValueSet::Rearrange() +{ + sal_uInt16 nNewColumnCount (CalculateColumnCount ( + GetOutputSizePixel().Width())); + sal_uInt16 nNewRowCount (CalculateRowCount (nNewColumnCount)); + + SetFormat(); + SetColCount(nNewColumnCount); + SetLineCount(nNewRowCount); +} + +sal_uInt16 PreviewValueSet::CalculateColumnCount (int nWidth) const +{ + int nColumnCount = 0; + if (nWidth > 0) + { + nColumnCount = nWidth / (maPreviewSize.Width() + 2*gnBorderWidth); + if (nColumnCount < 1) + nColumnCount = 1; + } + return static_cast(nColumnCount); +} + +sal_uInt16 PreviewValueSet::CalculateRowCount (sal_uInt16 nColumnCount) const +{ + int nRowCount = 0; + int nItemCount = GetItemCount(); + if (nColumnCount > 0) + { + nRowCount = (nItemCount+nColumnCount-1) / nColumnCount; + if (nRowCount < 1) + nRowCount = 1; + } + + return static_cast(nRowCount); +} + +sal_Int32 PreviewValueSet::GetPreferredHeight (sal_Int32 nWidth) +{ + int nRowCount (CalculateRowCount(CalculateColumnCount(nWidth))); + int nItemHeight (maPreviewSize.Height()); + + return nRowCount * (nItemHeight + 2*gnBorderHeight); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/PreviewValueSet.hxx b/sd/source/ui/sidebar/PreviewValueSet.hxx new file mode 100644 index 000000000..adab3c78a --- /dev/null +++ b/sd/source/ui/sidebar/PreviewValueSet.hxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd::sidebar +{ +/** Adapt the svtools valueset to the needs of the master page controls. +*/ +class PreviewValueSet : public ValueSet +{ +public: + explicit PreviewValueSet(); + virtual void SetDrawingArea(weld::DrawingArea* pDrawingArea) override; + virtual ~PreviewValueSet() override; + + void SetContextMenuHandler(const Link& rLink); + + virtual void Resize() override; + virtual bool Command(const CommandEvent& rEvent) override; + + void SetPreviewSize(const Size& rSize); + + sal_Int32 GetPreferredHeight(sal_Int32 nWidth); + + /** Set the number of rows and columns according to the current number + of items. Call this method when new items have been inserted. + */ + void Rearrange(); + +private: + Link maContextMenuHandler; + Size maPreviewSize; + + sal_uInt16 CalculateColumnCount(int nWidth) const; + sal_uInt16 CalculateRowCount(sal_uInt16 nColumnCount) const; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx b/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx new file mode 100644 index 000000000..6e5a46c73 --- /dev/null +++ b/sd/source/ui/sidebar/RecentMasterPagesSelector.cxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "RecentMasterPagesSelector.hxx" + +#include +#include "RecentlyUsedMasterPages.hxx" +#include +#include +#include +#include + +namespace sd::sidebar { + +std::unique_ptr RecentMasterPagesSelector::Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference& rxSidebar) +{ + SdDrawDocument* pDocument = rViewShellBase.GetDocument(); + if (pDocument == nullptr) + return nullptr; + + auto pContainer = std::make_shared(); + + auto xSelector(std::make_unique( + pParent, + *pDocument, + rViewShellBase, + pContainer, + rxSidebar)); + xSelector->LateInit(); + xSelector->SetHelpId(HID_SD_TASK_PANE_PREVIEW_RECENT); + + return xSelector; +} + +RecentMasterPagesSelector::RecentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr& rpContainer, + const css::uno::Reference& rxSidebar) + : MasterPagesSelector (pParent, rDocument, rBase, rpContainer, rxSidebar, "modules/simpress/ui/masterpagepanelrecent.ui", "recentvalueset") +{ +} + +RecentMasterPagesSelector::~RecentMasterPagesSelector() +{ + RecentlyUsedMasterPages::Instance().RemoveEventListener ( + LINK(this,RecentMasterPagesSelector,MasterPageListListener)); +} + +void RecentMasterPagesSelector::LateInit() +{ + MasterPagesSelector::LateInit(); + + MasterPagesSelector::Fill(); + RecentlyUsedMasterPages::Instance().AddEventListener ( + LINK(this,RecentMasterPagesSelector,MasterPageListListener)); +} + +IMPL_LINK_NOARG(RecentMasterPagesSelector, MasterPageListListener, LinkParamNone*, void) +{ + MasterPagesSelector::Fill(); +} + +void RecentMasterPagesSelector::Fill (ItemList& rItemList) +{ + // Create a set of names of the master pages used by the document. + MasterPageObserver::MasterPageNameSet aCurrentNames; + sal_uInt16 nMasterPageCount = mrDocument.GetMasterSdPageCount(PageKind::Standard); + for (sal_uInt16 nIndex=0; nIndexGetName()); + } + + // Insert the recently used master pages that are currently not used. + RecentlyUsedMasterPages& rInstance (RecentlyUsedMasterPages::Instance()); + int nPageCount = rInstance.GetMasterPageCount(); + for (int nIndex=0; nIndexGetStyleNameForToken(aToken)); + if (sStyleName.isEmpty() + || aCurrentNames.find(sStyleName) == aCurrentNames.end()) + { + rItemList.push_back(aToken); + } + } + } +} + +void RecentMasterPagesSelector::AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr >& rpPageList) +{ + sal_uInt16 nSelectedItemId = mxPreviewValueSet->GetSelectedItemId(); + + MasterPagesSelector::AssignMasterPageToPageList(pMasterPage, rpPageList); + + // Restore the selection. + if (mxPreviewValueSet->GetItemCount() > 0) + { + if (mxPreviewValueSet->GetItemCount() >= nSelectedItemId) + mxPreviewValueSet->SelectItem(nSelectedItemId); + else + mxPreviewValueSet->SelectItem(mxPreviewValueSet->GetItemCount()); + } +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx b/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx new file mode 100644 index 000000000..6dbc3a2aa --- /dev/null +++ b/sd/source/ui/sidebar/RecentMasterPagesSelector.hxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "MasterPagesSelector.hxx" + +namespace sd::sidebar { + +/** Show the recently used master pages (that are not currently used). +*/ +class RecentMasterPagesSelector final + : public MasterPagesSelector +{ + friend class VclPtrInstance; +public: + static std::unique_ptr Create ( + weld::Widget* pParent, + ViewShellBase& rViewShellBase, + const css::uno::Reference& rxSidebar); + + RecentMasterPagesSelector ( + weld::Widget* pParent, + SdDrawDocument& rDocument, + ViewShellBase& rBase, + const std::shared_ptr& rpContainer, + const css::uno::Reference& rxSidebar); + virtual ~RecentMasterPagesSelector() override; + +private: + DECL_LINK(MasterPageListListener, LinkParamNone*, void); + virtual void Fill (ItemList& rItemList) override; + + using sd::sidebar::MasterPagesSelector::Fill; + + /** Forward this call to the base class but save and restore the + currently selected item. + Assign the given master page to the list of pages. + @param pMasterPage + This master page will usually be a member of the list of all + available master pages as provided by the MasterPageContainer. + @param rPageList + The pages to which to assign the master page. These pages may + be slides or master pages themselves. + */ + virtual void AssignMasterPageToPageList ( + SdPage* pMasterPage, + const std::shared_ptr >& rpPageList) override; + + virtual void LateInit() override; +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx b/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx new file mode 100644 index 000000000..268658823 --- /dev/null +++ b/sd/source/ui/sidebar/RecentlyUsedMasterPages.cxx @@ -0,0 +1,366 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "RecentlyUsedMasterPages.hxx" +#include "MasterPageContainerProviders.hxx" +#include +#include "MasterPageDescriptor.hxx" +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::std; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +OUString GetPathToImpressConfigurationRoot() +{ + return "/org.openoffice.Office.Impress/"; +} +OUString GetPathToSetNode() +{ + return "MultiPaneGUI/ToolPanel/RecentlyUsedMasterPages"; +} + +} // end of anonymous namespace + +namespace sd::sidebar { + +RecentlyUsedMasterPages* RecentlyUsedMasterPages::mpInstance = nullptr; + +RecentlyUsedMasterPages& RecentlyUsedMasterPages::Instance() +{ + if (mpInstance == nullptr) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard (aMutexFunctor()); + if (mpInstance == nullptr) + { + RecentlyUsedMasterPages* pInstance = new RecentlyUsedMasterPages(); + pInstance->LateInit(); + SdGlobalResourceContainer::Instance().AddResource ( + ::std::unique_ptr(pInstance)); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + mpInstance = pInstance; + } + } + else { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + + return *mpInstance; +} + +constexpr size_t gnMaxListSize(8); + +RecentlyUsedMasterPages::RecentlyUsedMasterPages() + : mpContainer(std::make_shared()) +{ +} + +RecentlyUsedMasterPages::~RecentlyUsedMasterPages() +{ + Link aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener)); + mpContainer->RemoveChangeListener(aLink); + + MasterPageObserver::Instance().RemoveEventListener( + LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener)); +} + +void RecentlyUsedMasterPages::LateInit() +{ + Link aLink (LINK(this,RecentlyUsedMasterPages,MasterPageContainerChangeListener)); + mpContainer->AddChangeListener(aLink); + + LoadPersistentValues (); + MasterPageObserver::Instance().AddEventListener( + LINK(this,RecentlyUsedMasterPages,MasterPageChangeListener)); +} + +void RecentlyUsedMasterPages::LoadPersistentValues() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + GetPathToImpressConfigurationRoot(), + tools::ConfigurationAccess::READ_ONLY); + Reference xSet ( + aConfiguration.GetConfigurationNode(GetPathToSetNode()), + UNO_QUERY); + if ( ! xSet.is()) + return; + + static const OUStringLiteral sURLMemberName(u"URL"); + static const OUStringLiteral sNameMemberName(u"Name"); + OUString sURL; + OUString sName; + + // Read the names and URLs of the master pages. + const Sequence aKeys (xSet->getElementNames()); + mvMasterPages.clear(); + mvMasterPages.reserve(aKeys.getLength()); + for (const auto& rKey : aKeys) + { + Reference xSetItem ( + xSet->getByName(rKey), UNO_QUERY); + if (xSetItem.is()) + { + Any aURL (xSetItem->getByName(sURLMemberName)); + Any aName (xSetItem->getByName(sNameMemberName)); + aURL >>= sURL; + aName >>= sName; + SharedMasterPageDescriptor pDescriptor = std::make_shared( + MasterPageContainer::TEMPLATE, + -1, + sURL, + OUString(), + sName, + false, + std::make_shared(sURL), + std::make_shared(sURL)); + // For user supplied templates we use a different + // preview provider: The preview in the document shows + // not only shapes on the master page but also shapes on + // the foreground. This is misleading and therefore + // these previews are discarded and created directly + // from the page objects. + if (pDescriptor->GetURLClassification() == MasterPageDescriptor::URLCLASS_USER) + pDescriptor->mpPreviewProvider = std::make_shared(); + MasterPageContainer::Token aToken (mpContainer->PutMasterPage(pDescriptor)); + mvMasterPages.emplace_back(aToken,sURL,sName); + } + } + + ResolveList(); + } + catch (Exception&) + { + // Ignore exception. + } +} + +void RecentlyUsedMasterPages::SavePersistentValues() +{ + try + { + tools::ConfigurationAccess aConfiguration ( + GetPathToImpressConfigurationRoot(), + tools::ConfigurationAccess::READ_WRITE); + Reference xSet ( + aConfiguration.GetConfigurationNode(GetPathToSetNode()), + UNO_QUERY); + if ( ! xSet.is()) + return; + + // Clear the set. + const Sequence aKeys (xSet->getElementNames()); + for (const auto& rKey : aKeys) + xSet->removeByName (rKey); + + // Fill it with the URLs of this object. + static const OUStringLiteral sURLMemberName(u"URL"); + static const OUStringLiteral sNameMemberName(u"Name"); + Any aValue; + Reference xChildFactory ( + xSet, UNO_QUERY); + if ( ! xChildFactory.is()) + return; + sal_Int32 nIndex(0); + for (const auto& rDescriptor : mvMasterPages) + { + // Create new child. + OUString sKey = "index_" + OUString::number(nIndex); + Reference xChild( + xChildFactory->createInstance(), UNO_QUERY); + if (xChild.is()) + { + xSet->insertByName (sKey, Any(xChild)); + + aValue <<= rDescriptor.msURL; + xChild->replaceByName (sURLMemberName, aValue); + + aValue <<= rDescriptor.msName; + xChild->replaceByName (sNameMemberName, aValue); + } + ++nIndex; + } + + // Write the data back to disk. + aConfiguration.CommitChanges(); + } + catch (Exception&) + { + // Ignore exception. + } +} + +void RecentlyUsedMasterPages::AddEventListener (const Link& rEventListener) +{ + if (::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener) == maListeners.end()) + { + maListeners.push_back (rEventListener); + } +} + +void RecentlyUsedMasterPages::RemoveEventListener (const Link& rEventListener) +{ + maListeners.erase ( + ::std::find ( + maListeners.begin(), + maListeners.end(), + rEventListener)); +} + +int RecentlyUsedMasterPages::GetMasterPageCount() const +{ + return mvMasterPages.size(); +} + +MasterPageContainer::Token RecentlyUsedMasterPages::GetTokenForIndex (sal_uInt32 nIndex) const +{ + if(nIndexGetTokenForStyleName(rEvent.mrMasterPageName)); + break; + + case MasterPageObserverEvent::ET_MASTER_PAGE_REMOVED: + // Do not change the list of recently master pages (the deleted + // page was recently used) but tell the listeners. They may want + // to update their lists. + SendEvent(); + break; + } +} + +IMPL_LINK(RecentlyUsedMasterPages, MasterPageContainerChangeListener, + MasterPageContainerChangeEvent&, rEvent, void) +{ + switch (rEvent.meEventType) + { + case MasterPageContainerChangeEvent::EventType::CHILD_ADDED: + case MasterPageContainerChangeEvent::EventType::CHILD_REMOVED: + case MasterPageContainerChangeEvent::EventType::INDEX_CHANGED: + ResolveList(); + break; + + default: + // Ignored. + break; + } +} + +void RecentlyUsedMasterPages::AddMasterPage ( + MasterPageContainer::Token aToken) +{ + // For the page to be inserted the token has to be valid and the page + // has to have a valid URL. This excludes master pages that do not come + // from template files. + if (aToken == MasterPageContainer::NIL_TOKEN + || mpContainer->GetURLForToken(aToken).isEmpty()) + return; + + MasterPageList::iterator aIterator ( + ::std::find_if(mvMasterPages.begin(),mvMasterPages.end(), + Descriptor::TokenComparator(aToken))); + if (aIterator != mvMasterPages.end()) + { + // When an entry for the given token already exists then remove + // it now and insert it later at the head of the list. + mvMasterPages.erase (aIterator); + } + + mvMasterPages.insert(mvMasterPages.begin(), + Descriptor( + aToken, + mpContainer->GetURLForToken(aToken), + mpContainer->GetStyleNameForToken(aToken))); + + // Shorten list to maximal size. + while (mvMasterPages.size() > gnMaxListSize) + { + mvMasterPages.pop_back (); + } + + SavePersistentValues (); + SendEvent(); +} + +void RecentlyUsedMasterPages::ResolveList() +{ + bool bNotify (false); + + for (auto& rDescriptor : mvMasterPages) + { + if (rDescriptor.maToken == MasterPageContainer::NIL_TOKEN) + { + MasterPageContainer::Token aToken (mpContainer->GetTokenForURL(rDescriptor.msURL)); + rDescriptor.maToken = aToken; + if (aToken != MasterPageContainer::NIL_TOKEN) + bNotify = true; + } + else + { + if ( ! mpContainer->HasToken(rDescriptor.maToken)) + { + rDescriptor.maToken = MasterPageContainer::NIL_TOKEN; + bNotify = true; + } + } + } + + if (bNotify) + SendEvent(); +} + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx b/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx new file mode 100644 index 000000000..e95e66ccb --- /dev/null +++ b/sd/source/ui/sidebar/RecentlyUsedMasterPages.hxx @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +#include "MasterPageContainer.hxx" + +namespace sd { +class MasterPageObserverEvent; +} + +namespace sd::sidebar { + +/** This singleton holds a list of the most recently used master pages. +*/ +class RecentlyUsedMasterPages + : public SdGlobalResource +{ +public: + /** Return the single instance of this class. + */ + static RecentlyUsedMasterPages& Instance(); + + void AddEventListener (const Link& rEventListener); + void RemoveEventListener (const Link& rEventListener); + + int GetMasterPageCount() const; + MasterPageContainer::Token GetTokenForIndex (sal_uInt32 nIndex) const; + +private: + class Descriptor + { + public: + OUString msURL; + OUString msName; + ::sd::sidebar::MasterPageContainer::Token maToken; + Descriptor (::sd::sidebar::MasterPageContainer::Token aToken, + const OUString& rsURL, const OUString& rsName) + : msURL(rsURL), + msName(rsName), + maToken(aToken) + {} + + class TokenComparator + { + public: + explicit TokenComparator(::sd::sidebar::MasterPageContainer::Token aToken) + : maToken(aToken) {} + bool operator () (const Descriptor& rDescriptor) + { return maToken==rDescriptor.maToken; } + + private: + ::sd::sidebar::MasterPageContainer::Token maToken; + }; + }; + + /** The single instance of this class. It is created on demand when + Instance() is called for the first time. + */ + static RecentlyUsedMasterPages* mpInstance; + + ::std::vector> maListeners; + + typedef ::std::vector MasterPageList; + MasterPageList mvMasterPages; + std::shared_ptr mpContainer; + + RecentlyUsedMasterPages(); + virtual ~RecentlyUsedMasterPages() override; + + /** Call this method after a new object has been created. + */ + void LateInit(); + + RecentlyUsedMasterPages (const RecentlyUsedMasterPages&) = delete; + + RecentlyUsedMasterPages& operator= (const RecentlyUsedMasterPages&) = delete; + + void SendEvent(); + DECL_LINK(MasterPageChangeListener, MasterPageObserverEvent&, void); + DECL_LINK(MasterPageContainerChangeListener, MasterPageContainerChangeEvent&, void); + + /** Add a descriptor for the specified master page to the end of the + list of most recently used master pages. When the page is already a + member of that list the associated descriptor is moved to the end of + the list to make it the most recently used entry. + */ + void AddMasterPage(MasterPageContainer::Token aToken); + + /** Load the list of recently used master pages from the registry where + it was saved to make it persistent. + */ + void LoadPersistentValues(); + + /** Save the list of recently used master pages to the registry to make + it persistent. + */ + void SavePersistentValues(); + + void ResolveList(); +}; + +} // end of namespace sd::sidebar + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/SlideBackground.cxx b/sd/source/ui/sidebar/SlideBackground.cxx new file mode 100644 index 000000000..742275bab --- /dev/null +++ b/sd/source/ui/sidebar/SlideBackground.cxx @@ -0,0 +1,1286 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include "SlideBackground.hxx" +#include +#include +#include +#include +#include +#include +#include "PageMarginUtils.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::com::sun::star; + +using ::com::sun::star::uno::Reference; + +namespace sd::sidebar { + +namespace { + +enum eFillStyle +{ + NONE, + SOLID, + GRADIENT, + HATCH, + BITMAP, + PATTERN +}; + +} + +SlideBackground::SlideBackground( + weld::Widget* pParent, + ViewShellBase& rBase, + const css::uno::Reference& rxFrame, + SfxBindings* pBindings) : + PanelLayout( pParent, "SlideBackgroundPanel", "modules/simpress/ui/sidebarslidebackground.ui" ), + mrBase( rBase ), + mxPaperSizeBox(new SvxPaperSizeListBox(m_xBuilder->weld_combo_box("paperformat"))), + mxPaperOrientation(m_xBuilder->weld_combo_box("orientation")), + mxMasterSlide(m_xBuilder->weld_combo_box("masterslide")), + mxBackgroundLabel(m_xBuilder->weld_label("label3")), + mxFillStyle(m_xBuilder->weld_combo_box("fillstyle")), + mxFillLB(new ColorListBox(m_xBuilder->weld_menu_button("fillattr"), [this]{ return GetFrameWeld(); })), + mxFillAttr(m_xBuilder->weld_combo_box("fillattr1")), + mxFillGrad1(new ColorListBox(m_xBuilder->weld_menu_button("fillattr2"), [this]{ return GetFrameWeld(); })), + mxFillGrad2(new ColorListBox(m_xBuilder->weld_menu_button("fillattr3"), [this]{ return GetFrameWeld(); })), + mxInsertImage(m_xBuilder->weld_button("button2")), + mxDspMasterBackground(m_xBuilder->weld_check_button("displaymasterbackground")), + mxDspMasterObjects(m_xBuilder->weld_check_button("displaymasterobjects")), + mxCloseMaster(m_xBuilder->weld_button("closemasterslide")), + mxEditMaster(m_xBuilder->weld_button("masterslidebutton")), + mxMasterLabel(m_xBuilder->weld_label("masterlabel")), + mxMarginSelectBox(m_xBuilder->weld_combo_box("marginLB")), + mxCustomEntry(m_xBuilder->weld_label("customlabel")), + mxMarginLabel(m_xBuilder->weld_label("labelmargin")), + maPaperSizeController(SID_ATTR_PAGE_SIZE, *pBindings, *this), + maPaperOrientationController(SID_ATTR_PAGE, *pBindings, *this), + maPaperMarginLRController(SID_ATTR_PAGE_LRSPACE, *pBindings, *this), + maPaperMarginULController(SID_ATTR_PAGE_ULSPACE, *pBindings, *this), + maBckColorController(SID_ATTR_PAGE_COLOR, *pBindings, *this), + maBckGradientController(SID_ATTR_PAGE_GRADIENT, *pBindings, *this), + maBckHatchController(SID_ATTR_PAGE_HATCH, *pBindings, *this), + maBckBitmapController(SID_ATTR_PAGE_BITMAP, *pBindings, *this), + maBckFillStyleController(SID_ATTR_PAGE_FILLSTYLE, *pBindings, *this), + maBckImageController(SID_SELECT_BACKGROUND, *pBindings, *this), + maDspBckController(SID_DISPLAY_MASTER_BACKGROUND, *pBindings, *this), + maDspObjController(SID_DISPLAY_MASTER_OBJECTS, *pBindings, *this), + maMetricController(SID_ATTR_METRIC, *pBindings, *this), + maCloseMasterController(SID_CLOSE_MASTER_VIEW, *pBindings, *this), + mpPageItem( new SvxPageItem(SID_ATTR_PAGE) ), + mbSwitchModeToNormal(false), + mbSwitchModeToMaster(false), + mxFrame(rxFrame), + maDrawOtherContext(vcl::EnumContext::Application::Draw, vcl::EnumContext::Context::DrawPage), + maDrawMasterContext(vcl::EnumContext::Application::Draw, vcl::EnumContext::Context::MasterPage), + maImpressOtherContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::DrawPage), + maImpressMasterContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::MasterPage), + maImpressHandoutContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::HandoutPage), + maImpressNotesContext(vcl::EnumContext::Application::Impress, vcl::EnumContext::Context::NotesPage), + mbTitle(false), + mpPageLRMarginItem( new SvxLongLRSpaceItem( 0, 0, SID_ATTR_PAGE_LRSPACE ) ), + mpPageULMarginItem( new SvxLongULSpaceItem( 0, 0, SID_ATTR_PAGE_ULSPACE ) ), + m_nPageLeftMargin(0), + m_nPageRightMargin(0), + m_nPageTopMargin(0), + m_nPageBottomMargin(0), + meFUnit(GetModuleFieldUnit()), + mpBindings(pBindings) +{ + //let the listbox shrink to any size so the sidebar isn't forced to grow to + //the size of the longest master slide name in the document + mxMasterSlide->set_size_request(42, -1); + + maCustomEntry = mxCustomEntry->get_label(); + + addListener(); + Initialize(); +} + +bool SlideBackground::IsDraw() +{ + return ( maContext == maDrawMasterContext || + maContext == maDrawOtherContext ); +} + +bool SlideBackground::IsImpress() +{ + return ( maContext == maImpressMasterContext || + maContext == maImpressOtherContext || + maContext == maImpressHandoutContext || + maContext == maImpressNotesContext ); +} + +FieldUnit SlideBackground::GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState) +{ + FieldUnit eUnit; + + if (pState && eState >= SfxItemState::DEFAULT) + eUnit = static_cast(static_cast(pState)->GetValue()); + else + eUnit = GetModuleFieldUnit(); + + return eUnit; +} + +void SlideBackground::SetMarginsFieldUnit() +{ + auto nSelected = mxMarginSelectBox->get_active(); + mxMarginSelectBox->clear(); + + const LocaleDataWrapper& rLocaleData = Application::GetSettings().GetLocaleDataWrapper(); + + if (IsInch(meFUnit)) + { + OUString sSuffix = weld::MetricSpinButton::MetricToString(FieldUnit::INCH); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_PAGEFORMATPANEL_MARGINS_INCH); ++i) + { + OUString sMeasurement = rLocaleData.getNum(RID_PAGEFORMATPANEL_MARGINS_INCH[i].second, 2, true, false) + sSuffix; + mxMarginSelectBox->append_text(SdResId(RID_PAGEFORMATPANEL_MARGINS_INCH[i].first).replaceFirst("%1", sMeasurement)); + } + } + else + { + OUString sSuffix = " " + weld::MetricSpinButton::MetricToString(FieldUnit::CM); + for (size_t i = 0; i < SAL_N_ELEMENTS(RID_PAGEFORMATPANEL_MARGINS_CM); ++i) + { + OUString sMeasurement = rLocaleData.getNum(RID_PAGEFORMATPANEL_MARGINS_CM[i].second, 2, true, false) + sSuffix; + mxMarginSelectBox->append_text(SdResId(RID_PAGEFORMATPANEL_MARGINS_CM[i].first).replaceFirst("%1", sMeasurement)); + } + } + + mxMarginSelectBox->set_active(nSelected); +} + +void SlideBackground::Initialize() +{ + SvxFillTypeBox::Fill(*mxFillStyle); + + SetMarginsFieldUnit(); + + mxPaperSizeBox->FillPaperSizeEntries( PaperSizeApp::Draw ); + mxPaperSizeBox->connect_changed(LINK(this,SlideBackground,PaperSizeModifyHdl)); + mxPaperOrientation->connect_changed(LINK(this,SlideBackground,PaperSizeModifyHdl)); + mxEditMaster->connect_clicked(LINK(this, SlideBackground, EditMasterHdl)); + mxCloseMaster->connect_clicked(LINK(this, SlideBackground, CloseMasterHdl)); + mxInsertImage->connect_clicked(LINK(this, SlideBackground, SelectBgHdl)); + meUnit = maPaperSizeController.GetCoreMetric(); + + mxMasterSlide->connect_changed(LINK(this, SlideBackground, AssignMasterPage)); + + mxFillStyle->connect_changed(LINK(this, SlideBackground, FillStyleModifyHdl)); + mxFillLB->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillGrad1->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillGrad2->SetSelectHdl(LINK(this, SlideBackground, FillColorHdl)); + mxFillAttr->connect_changed(LINK(this, SlideBackground, FillBackgroundHdl)); + + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell) + { + FrameView *pFrameView = pMainViewShell->GetFrameView(); + + if ( pFrameView->GetViewShEditMode() == EditMode::Page ) + { + SdPage* mpPage = pMainViewShell->getCurrentPage(); + populateMasterSlideDropdown(); + + OUString aLayoutName( mpPage->GetLayoutName() ); + aLayoutName = aLayoutName.copy(0,aLayoutName.indexOf(SD_LT_SEPARATOR)); + mxMasterSlide->set_active_text(aLayoutName); + } + } + + mxFillStyle->set_active(static_cast< sal_Int32 >(NONE)); + + mxDspMasterBackground->connect_toggled(LINK(this, SlideBackground, DspBackground)); + mxDspMasterObjects->connect_toggled(LINK(this, SlideBackground, DspObjects)); + + //margins + mxMarginSelectBox->connect_changed(LINK(this, SlideBackground, ModifyMarginHdl)); + + Update(); + UpdateMarginBox(); +} + +void SlideBackground::DumpAsPropertyTree(::tools::JsonWriter& rJsonWriter) +{ + if (mxPaperSizeBox->get_active() == -1) + { + mpBindings->Update(SID_ATTR_PAGE_SIZE); + } + + PanelLayout::DumpAsPropertyTree(rJsonWriter); +} + +void SlideBackground::HandleContextChange( + const vcl::EnumContext& rContext) +{ + if (maContext == rContext) + return; + maContext = rContext; + + if ( IsImpress() ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERSLIDE_LABEL)); + + // margin selector is only for Draw + mxMarginSelectBox->hide(); + mxMarginLabel->hide(); + + if ( maContext == maImpressMasterContext ) + { + mxCloseMaster->show(); + mxEditMaster->hide(); + mxMasterSlide->set_sensitive(false); + mxMasterSlide->clear(); + mxDspMasterBackground->set_sensitive(false); + mxDspMasterObjects->set_sensitive(false); + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + mxInsertImage->show(); + + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + } + else if ( maContext == maImpressHandoutContext || maContext == maImpressNotesContext ) + { + mxCloseMaster->hide(); + mxEditMaster->hide(); + mxMasterSlide->set_sensitive(false); + mxMasterSlide->clear(); + mxDspMasterBackground->set_sensitive(false); + mxDspMasterObjects->set_sensitive(false); + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + mxInsertImage->hide(); + } + else if (maContext == maImpressOtherContext) + { + mxCloseMaster->hide(); + mxEditMaster->show(); + mxMasterSlide->set_sensitive(true); + populateMasterSlideDropdown(); + mxDspMasterBackground->set_sensitive(true); + mxDspMasterObjects->set_sensitive(true); + mxFillStyle->show(); + mxBackgroundLabel->show(); + mxInsertImage->show(); + } + + // Need to do a relayouting, otherwise the panel size is not updated after show / hide controls + if (m_pPanel) + m_pPanel->TriggerDeckLayouting(); + + } + else if ( IsDraw() ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERPAGE_LABEL)); + mxDspMasterBackground->hide(); + mxDspMasterObjects->hide(); + + if (maContext == maDrawOtherContext) + { + mxEditMaster->hide(); + mxFillStyle->show(); + mxBackgroundLabel->show(); + } + else if (maContext == maDrawMasterContext) + { + mxFillStyle->hide(); + mxBackgroundLabel->hide(); + } + } + + // The Insert Image button in the sidebar issues .uno:SelectBackground, + // which when invoked without arguments will open the file-open-dialog + // to prompt the user to select a file. This is useless in LOOL. + // Hide for now so the user will only be able to use the menu to insert + // background image, which prompts the user for file selection in the browser. + if (comphelper::LibreOfficeKit::isActive()) + mxInsertImage->hide(); +} + +void SlideBackground::Update() +{ + eFillStyle nPos = static_cast(mxFillStyle->get_active()); + + if(maContext != maImpressOtherContext && maContext != maDrawOtherContext) + nPos = NONE; + + SfxObjectShell* pSh = SfxObjectShell::Current(); + if (!pSh) + return; + + switch(nPos) + { + case NONE: + { + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + } + break; + case SOLID: + { + mxFillAttr->hide(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + mxFillLB->show(); + const Color aColor = GetColorSetOrDefault(); + mxFillLB->SelectEntry(aColor); + } + break; + case GRADIENT: + { + mxFillLB->hide(); + mxFillAttr->hide(); + mxFillGrad1->show(); + mxFillGrad2->show(); + + const XGradient xGradient = GetGradientSetOrDefault(); + const Color aStartColor = xGradient.GetStartColor(); + mxFillGrad1->SelectEntry(aStartColor); + const Color aEndColor = xGradient.GetEndColor(); + mxFillGrad2->SelectEntry(aEndColor); + } + break; + + case HATCH: + { + mxFillLB->hide(); + mxFillAttr->show(); + mxFillAttr->clear(); + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_HATCH_LIST)->GetHatchList()); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + + const OUString aHatchName = GetHatchingSetOrDefault(); + mxFillAttr->set_active_text( aHatchName ); + } + break; + + case BITMAP: + case PATTERN: + { + mxFillLB->hide(); + mxFillAttr->show(); + mxFillAttr->clear(); + mxFillGrad1->hide(); + mxFillGrad2->hide(); + OUString aName; + if(nPos == BITMAP) + { + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_BITMAP_LIST)->GetBitmapList()); + aName = GetBitmapSetOrDefault(); + } + else if(nPos == PATTERN) + { + SvxFillAttrBox::Fill(*mxFillAttr, pSh->GetItem(SID_PATTERN_LIST)->GetPatternList()); + aName = GetPatternSetOrDefault(); + } + mxFillAttr->set_active_text( aName ); + } + break; + default: + break; + } + + // Need to do a relayouting, otherwise the panel size is not updated after show / hide controls + if (m_pPanel) + m_pPanel->TriggerDeckLayouting(); +} + +void SlideBackground::UpdateMarginBox() +{ + m_nPageLeftMargin = mpPageLRMarginItem->GetLeft(); + m_nPageRightMargin = mpPageLRMarginItem->GetRight(); + m_nPageTopMargin = mpPageULMarginItem->GetUpper(); + m_nPageBottomMargin = mpPageULMarginItem->GetLower(); + + int nCustomIndex = mxMarginSelectBox->find_text(maCustomEntry); + + if( IsNone(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(0); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNarrow(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(1); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsModerate(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(2); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal075(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(3); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal100(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(4); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsNormal125(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(5); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else if( IsWide(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin) ) + { + mxMarginSelectBox->set_active(6); + if (nCustomIndex != -1) + mxMarginSelectBox->remove(nCustomIndex); + } + else + { + if (nCustomIndex == -1) + mxMarginSelectBox->append_text(maCustomEntry); + mxMarginSelectBox->set_active_text(maCustomEntry); + } +} + +void SlideBackground::SetPanelTitle( const OUString& rTitle ) +{ + Reference xController( mxFrame->getController(), uno::UNO_QUERY); + if ( !xController.is() ) + return; + + Reference xSidebarProvider = xController->getSidebar(); + if ( !xSidebarProvider.is() ) + return; + + Reference xDecks = xSidebarProvider->getDecks(); + if ( !xDecks.is() ) + return; + + Reference xDeck ( xDecks->getByName("PropertyDeck"), uno::UNO_QUERY); + if ( !xDeck.is() ) + return; + + Reference xPanels = xDeck->getPanels(); + if ( !xPanels.is() ) + return; + + if (xPanels->hasByName("SlideBackgroundPanel")) + { + Reference xPanel ( xPanels->getByName("SlideBackgroundPanel"), uno::UNO_QUERY); + if ( !xPanel.is() ) + return; + + xPanel->setTitle( rTitle ); + } +} + +void SlideBackground::addListener() +{ + Link aLink( LINK(this, SlideBackground, EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener( aLink ); +} + +void SlideBackground::removeListener() +{ + Link aLink( LINK(this, SlideBackground, EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(SlideBackground, EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + // add more events as per requirement + // Master Page change triggers a shape change event. Solves sync problem. + case EventMultiplexerEventId::ShapeChanged: + populateMasterSlideDropdown(); + break; + case EventMultiplexerEventId::EditModeNormal: + mbSwitchModeToNormal = true; + break; + case EventMultiplexerEventId::EditModeMaster: + mbSwitchModeToMaster = true; + break; + case EventMultiplexerEventId::EditViewSelection: + case EventMultiplexerEventId::EndTextEdit: + { + if ( mbSwitchModeToMaster ) + { + if( IsImpress() ) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_MASTERPAGE_NAME)); + mbSwitchModeToMaster = false; + } + else if ( mbSwitchModeToNormal ) + { + if( IsImpress() ) + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_PAGE_NAME)); + mbSwitchModeToNormal = false; + } + + } + break; + case EventMultiplexerEventId::CurrentPageChanged: + { + static const sal_uInt16 SidArray[] = { + SID_ATTR_PAGE_COLOR, + SID_ATTR_PAGE_GRADIENT, + SID_ATTR_PAGE_HATCH, + SID_ATTR_PAGE_BITMAP, + SID_ATTR_PAGE_FILLSTYLE, + SID_DISPLAY_MASTER_BACKGROUND, + SID_DISPLAY_MASTER_OBJECTS, + 0 }; + updateMasterSlideSelection(); + GetBindings()->Invalidate( SidArray ); + } + break; + case EventMultiplexerEventId::ViewAdded: + { + if(!mbTitle) + { + if( IsDraw() ) + { + mxCloseMaster->hide(); + mxEditMaster->hide(); + if( maContext == maDrawMasterContext) + SetPanelTitle(SdResId(STR_MASTERPAGE_NAME)); + else + SetPanelTitle(SdResId(STR_PAGE_NAME)); + } + else if ( maContext == maImpressOtherContext || maContext == maImpressMasterContext ) + { + if( maContext == maImpressMasterContext ) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + } + else if ( maContext == maImpressNotesContext ) + { + mxMasterLabel->set_label(SdResId(STR_MASTERSLIDE_LABEL)); + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + + if (pMainViewShell) + { + DrawViewShell* pDrawViewShell = static_cast(pMainViewShell); + if ( pDrawViewShell->GetEditMode() == EditMode::MasterPage) + SetPanelTitle(SdResId(STR_MASTERSLIDE_NAME)); + else // EditMode::Page + SetPanelTitle(SdResId(STR_SLIDE_NAME)); + } + } + mbTitle = true; + } + } + break; + default: + break; + } +} + +void SlideBackground::populateMasterSlideDropdown() +{ + mxMasterSlide->clear(); + ::sd::DrawDocShell* pDocSh = dynamic_cast<::sd::DrawDocShell*>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = pDocSh ? pDocSh->GetDoc() : nullptr; + sal_uInt16 nCount = pDoc ? pDoc->GetMasterPageCount() : 0; + for( sal_uInt16 nLayout = 0; nLayout < nCount; nLayout++ ) + { + SdPage* pMaster = static_cast(pDoc->GetMasterPage(nLayout)); + if( pMaster->GetPageKind() == PageKind::Standard) + { + OUString aLayoutName(pMaster->GetLayoutName()); + aLayoutName = aLayoutName.copy(0,aLayoutName.indexOf(SD_LT_SEPARATOR)); + mxMasterSlide->append_text(aLayoutName); + } + } + updateMasterSlideSelection(); +} + +void SlideBackground::updateMasterSlideSelection() +{ + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + SdPage* pPage = pMainViewShell ? pMainViewShell->getCurrentPage() : nullptr; + if (pPage != nullptr && pPage->TRG_HasMasterPage()) + { + SdrPage& rMasterPage (pPage->TRG_GetMasterPage()); + SdPage* pMasterPage = static_cast(&rMasterPage); + mxMasterSlide->set_active_text(pMasterPage->GetName()); + } +} + +SlideBackground::~SlideBackground() +{ + removeListener(); + + mxCustomEntry.reset(); + mxMarginLabel.reset(); + mxPaperSizeBox.reset(); + mxPaperOrientation.reset(); + mxMasterSlide.reset(); + mxBackgroundLabel.reset(); + mxFillAttr.reset(); + mxFillGrad1.reset(); + mxFillGrad2.reset(); + mxFillStyle.reset(); + mxFillLB.reset(); + mxInsertImage.reset(); + mxMarginSelectBox.reset(); + mxDspMasterBackground.reset(); + mxDspMasterObjects.reset(); + mxMasterLabel.reset(); + mxEditMaster.reset(); + mxCloseMaster.reset(); + + maPaperSizeController.dispose(); + maPaperOrientationController.dispose(); + maPaperMarginLRController.dispose(); + maPaperMarginULController.dispose(); + maBckColorController.dispose(); + maBckGradientController.dispose(); + maBckHatchController.dispose(); + maBckBitmapController.dispose(); + maBckFillStyleController.dispose(); + maBckImageController.dispose(); + maDspBckController.dispose(); + maDspObjController.dispose(); + maMetricController.dispose(); + maCloseMasterController.dispose(); + + mpPageItem.reset(); + mpColorItem.reset(); + mpHatchItem.reset(); + mpBitmapItem.reset(); + mpPageLRMarginItem.reset(); + mpPageULMarginItem.reset(); +} + +void SlideBackground::ExecuteMarginLRChange(const ::tools::Long mnPageLeftMargin, const ::tools::Long mnPageRightMargin) +{ + mpPageLRMarginItem->SetLeft(mnPageLeftMargin); + mpPageLRMarginItem->SetRight(mnPageRightMargin); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_LRSPACE, SfxCallMode::RECORD, { mpPageLRMarginItem.get() } ); +} + +void SlideBackground::ExecuteMarginULChange(const ::tools::Long mnPageTopMargin, const ::tools::Long mnPageBottomMargin) +{ + mpPageULMarginItem->SetUpper(mnPageTopMargin); + mpPageULMarginItem->SetLower(mnPageBottomMargin); + GetBindings()->GetDispatcher()->ExecuteList( SID_ATTR_PAGE_ULSPACE, SfxCallMode::RECORD, { mpPageULMarginItem.get() } ); +} + +Color const & SlideBackground::GetColorSetOrDefault() +{ + // Tango Sky Blue 1, to be consistent w/ area fill panel (b/c COL_AUTO for slides is transparent) + if ( !mpColorItem ) + mpColorItem.reset( new XFillColorItem( OUString(), Color(0x72, 0x9f, 0xcf) ) ); + + return mpColorItem->GetColorValue(); +} + +XGradient const & SlideBackground::GetGradientSetOrDefault() +{ + if( !mpGradientItem ) + { + XGradient aGradient; + OUString aGradientName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxGradientListItem * pGradListItem = pSh->GetItem(SID_GRADIENT_LIST); + aGradient = pGradListItem->GetGradientList()->GetGradient(0)->GetGradient(); + aGradientName = pGradListItem->GetGradientList()->GetGradient(0)->GetName(); + } + mpGradientItem.reset( new XFillGradientItem( aGradientName, aGradient ) ); + } + + return mpGradientItem->GetGradientValue(); +} + +OUString const & SlideBackground::GetHatchingSetOrDefault() +{ + if( !mpHatchItem ) + { + XHatch aHatch; + OUString aHatchName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxHatchListItem * pHatchListItem = pSh->GetItem(SID_HATCH_LIST); + aHatch = pHatchListItem->GetHatchList()->GetHatch(0)->GetHatch(); + aHatchName = pHatchListItem->GetHatchList()->GetHatch(0)->GetName(); + } + mpHatchItem.reset( new XFillHatchItem( aHatchName, aHatch ) ); + } + + return mpHatchItem->GetName(); +} + +OUString const & SlideBackground::GetBitmapSetOrDefault() +{ + if( !mpBitmapItem || mpBitmapItem->isPattern()) + { + GraphicObject aGraphObj; + OUString aBmpName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxBitmapListItem * pBmpListItem = pSh->GetItem(SID_BITMAP_LIST); + aGraphObj = pBmpListItem->GetBitmapList()->GetBitmap(0)->GetGraphicObject(); + aBmpName = pBmpListItem->GetBitmapList()->GetBitmap(0)->GetName(); + } + mpBitmapItem.reset( new XFillBitmapItem( aBmpName, aGraphObj ) ); + } + + return mpBitmapItem->GetName(); +} + +OUString const & SlideBackground::GetPatternSetOrDefault() +{ + if( !mpBitmapItem || !(mpBitmapItem->isPattern())) + { + GraphicObject aGraphObj; + OUString aPtrnName; + if (SfxObjectShell* pSh = SfxObjectShell::Current()) + { + const SvxPatternListItem * pPtrnListItem = pSh->GetItem(SID_PATTERN_LIST); + aGraphObj = pPtrnListItem->GetPatternList()->GetBitmap(0)->GetGraphicObject(); + aPtrnName = pPtrnListItem->GetPatternList()->GetBitmap(0)->GetName(); + } + mpBitmapItem.reset( new XFillBitmapItem( aPtrnName, aGraphObj ) ); + } + + return mpBitmapItem->GetName(); +} + +void SlideBackground::NotifyItemUpdate( + const sal_uInt16 nSID, + const SfxItemState eState, + const SfxPoolItem* pState) +{ + switch(nSID) + { + + case SID_ATTR_PAGE_COLOR: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(SOLID)); + mpColorItem.reset(pState ? static_cast< XFillColorItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + + case SID_ATTR_PAGE_HATCH: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(HATCH)); + mpHatchItem.reset(pState ? static_cast < XFillHatchItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + + case SID_ATTR_PAGE_GRADIENT: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32>(GRADIENT)); + mpGradientItem.reset(pState ? static_cast< XFillGradientItem* >(pState->Clone()) : nullptr); + Update(); + } + } + break; + case SID_ATTR_PAGE_BITMAP: + { + if(eState >= SfxItemState::DEFAULT) + { + mpBitmapItem.reset(pState ? static_cast< XFillBitmapItem* >(pState->Clone()) : nullptr); + if(mpBitmapItem) + { + if(mpBitmapItem->isPattern()) + mxFillStyle->set_active(static_cast< sal_Int32 >(PATTERN)); + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + } + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + Update(); + } + } + break; + + case SID_ATTR_PAGE_FILLSTYLE: + { + const XFillStyleItem* pFillStyleItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pFillStyleItem = dynamic_cast< const XFillStyleItem* >(pState); + if (pFillStyleItem) + { + css::drawing::FillStyle eXFS = pFillStyleItem->GetValue(); + switch(eXFS) + { + case drawing::FillStyle_NONE: + mxFillStyle->set_active(static_cast< sal_Int32 >(NONE)); + break; + case drawing::FillStyle_SOLID: + mxFillStyle->set_active(static_cast< sal_Int32 >(SOLID)); + break; + case drawing::FillStyle_GRADIENT: + mxFillStyle->set_active(static_cast< sal_Int32 >(GRADIENT)); + break; + case drawing::FillStyle_HATCH: + mxFillStyle->set_active(static_cast< sal_Int32 >(HATCH)); + break; + case drawing::FillStyle_BITMAP: + { + if(mpBitmapItem->isPattern()) + mxFillStyle->set_active(static_cast< sal_Int32 >(PATTERN)); + else + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + } + break; + default: + break; + } + Update(); + } + } + break; + + case SID_ATTR_PAGE_SIZE: + { + const SvxSizeItem* pSizeItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pSizeItem = dynamic_cast(pState); + if (pSizeItem) + { + Size aPaperSize = pSizeItem->GetSize(); + if (mxPaperOrientation->get_active() == 0) + Swap(aPaperSize); + + Paper ePaper = SvxPaperInfo::GetSvxPaper(aPaperSize, meUnit); + mxPaperSizeBox->set_active_id( ePaper ); + } + } + break; + + case SID_ATTR_PAGE: + { + const SvxPageItem* pPageItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pPageItem = dynamic_cast(pState); + if (pPageItem) + { + mpPageItem.reset(pPageItem->Clone()); + bool bIsLandscape = mpPageItem->IsLandscape(); + mxPaperOrientation->set_active( bIsLandscape ? 0 : 1 ); + } + } + break; + + case SID_ATTR_PAGE_LRSPACE: + { + const SvxLongLRSpaceItem* pLRItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pLRItem = dynamic_cast(pState); + if (pLRItem) + { + mpPageLRMarginItem.reset( static_cast(pState->Clone()) ); + UpdateMarginBox(); + } + } + break; + + case SID_ATTR_PAGE_ULSPACE: + { + const SvxLongULSpaceItem* pULItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pULItem = dynamic_cast(pState); + if (pULItem) + { + mpPageULMarginItem.reset( static_cast(pState->Clone()) ); + UpdateMarginBox(); + } + } + break; + + case SID_DISPLAY_MASTER_BACKGROUND: + { + const SfxBoolItem* pBoolItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pBoolItem = dynamic_cast< const SfxBoolItem* >(pState); + if (pBoolItem) + mxDspMasterBackground->set_active(pBoolItem->GetValue()); + } + break; + case SID_DISPLAY_MASTER_OBJECTS: + { + const SfxBoolItem* pBoolItem = nullptr; + if (eState >= SfxItemState::DEFAULT) + pBoolItem = dynamic_cast< const SfxBoolItem* >(pState); + if (pBoolItem) + mxDspMasterObjects->set_active(pBoolItem->GetValue()); + } + break; + case SID_SELECT_BACKGROUND: + { + if(eState >= SfxItemState::DEFAULT) + { + mxFillStyle->set_active(static_cast< sal_Int32 >(BITMAP)); + Update(); + } + } + break; + case SID_ATTR_METRIC: + { + FieldUnit eFUnit = GetCurrentUnit(eState, pState); + if (meFUnit != eFUnit) + { + meFUnit = eFUnit; + SetMarginsFieldUnit(); + UpdateMarginBox(); + } + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, FillStyleModifyHdl, weld::ComboBox&, void) +{ + const eFillStyle nPos = static_cast(mxFillStyle->get_active()); + Update(); + + switch (nPos) + { + case NONE: + { + const XFillStyleItem aXFillStyleItem(drawing::FillStyle_NONE); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_FILLSTYLE, SfxCallMode::RECORD, { &aXFillStyleItem }); + } + break; + + case SOLID: + { + if (mpColorItem) + { + const XFillColorItem aItem( OUString(), mpColorItem->GetColorValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLOR, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case GRADIENT: + { + if (mpGradientItem) + { + const XFillGradientItem aItem( mpGradientItem->GetName(), mpGradientItem->GetGradientValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_GRADIENT, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case HATCH: + { + if (mpHatchItem) + { + const XFillHatchItem aItem( mpHatchItem->GetName(), mpHatchItem->GetHatchValue() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_HATCH, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + case BITMAP: + case PATTERN: + { + if (mpBitmapItem) + { + const XFillBitmapItem aItem( mpBitmapItem->GetName(), mpBitmapItem->GetGraphicObject() ); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_BITMAP, SfxCallMode::RECORD, { &aItem }); + } + } + break; + + default: + break; + } +//TODO mxFillStyle->Selected(); +} + +IMPL_LINK_NOARG(SlideBackground, PaperSizeModifyHdl, weld::ComboBox&, void) +{ + const Paper ePaper = mxPaperSizeBox->get_active_id(); + Size aSize(SvxPaperInfo::GetPaperSize(ePaper, meUnit)); + + if (mxPaperOrientation->get_active() == 0) + Swap(aSize); + + mpPageItem->SetLandscape(mxPaperOrientation->get_active() == 0); + const SvxSizeItem aSizeItem(SID_ATTR_PAGE_SIZE, aSize); + // Page/slide properties dialog (FuPage::ExecuteDialog and ::ApplyItemSet) misuses + // SID_ATTR_PAGE_EXT1 to distinguish between Impress and Draw, as for whether to fit + // objects to paper size. Until that is handled somehow better, we do the same here + const SfxBoolItem aFitObjs(SID_ATTR_PAGE_EXT1, IsImpress()); + + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_SIZE, SfxCallMode::RECORD, + { &aSizeItem, mpPageItem.get(), &aFitObjs }); + + // Notify LOK clients of the page size change. + if (!comphelper::LibreOfficeKit::isActive()) + return; + + SfxViewShell* pViewShell = SfxViewShell::GetFirst(); + while (pViewShell) + { + if (pViewShell->GetDocId() == mrBase.GetDocId()) + { + SdXImpressDocument* pDoc = comphelper::getFromUnoTunnel(pViewShell->GetCurrentDocument()); + SfxLokHelper::notifyDocumentSizeChangedAllViews(pDoc); + } + pViewShell = SfxViewShell::GetNext(*pViewShell); + } +} + +IMPL_LINK_NOARG(SlideBackground, FillColorHdl, ColorListBox&, void) +{ + const drawing::FillStyle eXFS = static_cast(mxFillStyle->get_active()); + switch(eXFS) + { + case drawing::FillStyle_SOLID: + { + XFillColorItem aItem(OUString(), mxFillLB->GetSelectEntryColor()); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_COLOR, SfxCallMode::RECORD, { &aItem }); + } + break; + case drawing::FillStyle_GRADIENT: + { + XGradient aGradient; + aGradient.SetStartColor(mxFillGrad1->GetSelectEntryColor()); + aGradient.SetEndColor(mxFillGrad2->GetSelectEntryColor()); + + // the name doesn't really matter, it'll be converted to unique one eventually, + // but it has to be non-empty + XFillGradientItem aItem("gradient", aGradient); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_GRADIENT, SfxCallMode::RECORD, { &aItem }); + } + break; + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, FillBackgroundHdl, weld::ComboBox&, void) +{ + const eFillStyle nFillPos = static_cast(mxFillStyle->get_active()); + SfxObjectShell* pSh = SfxObjectShell::Current(); + if (!pSh) + return; + switch(nFillPos) + { + + case HATCH: + { + const SvxHatchListItem * pHatchListItem = pSh->GetItem(SID_HATCH_LIST); + sal_uInt16 nPos = mxFillAttr->get_active(); + XHatch aHatch = pHatchListItem->GetHatchList()->GetHatch(nPos)->GetHatch(); + const OUString aHatchName = pHatchListItem->GetHatchList()->GetHatch(nPos)->GetName(); + + XFillHatchItem aItem(aHatchName, aHatch); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_HATCH, SfxCallMode::RECORD, { &aItem }); + } + break; + + case BITMAP: + case PATTERN: + { + sal_Int16 nPos = mxFillAttr->get_active(); + GraphicObject aBitmap; + OUString aName; + if( nFillPos == BITMAP ) + { + SvxBitmapListItem const * pBitmapListItem = pSh->GetItem(SID_BITMAP_LIST); + aBitmap = pBitmapListItem->GetBitmapList()->GetBitmap(nPos)->GetGraphicObject(); + aName = pBitmapListItem->GetBitmapList()->GetBitmap(nPos)->GetName(); + } + else if( nFillPos == PATTERN ) + { + SvxPatternListItem const * pPatternListItem = pSh->GetItem(SID_PATTERN_LIST); + aBitmap = pPatternListItem->GetPatternList()->GetBitmap(nPos)->GetGraphicObject(); + aName = pPatternListItem->GetPatternList()->GetBitmap(nPos)->GetName(); + } + XFillBitmapItem aItem(aName, aBitmap); + GetBindings()->GetDispatcher()->ExecuteList(SID_ATTR_PAGE_BITMAP, SfxCallMode::RECORD, { &aItem }); + } + break; + + default: + break; + } +} + +IMPL_LINK_NOARG(SlideBackground, AssignMasterPage, weld::ComboBox&, void) +{ + ::sd::DrawDocShell* pDocSh = dynamic_cast<::sd::DrawDocShell*>( SfxObjectShell::Current() ); + SdDrawDocument* pDoc = pDocSh ? pDocSh->GetDoc() : nullptr; + if (!pDoc) + return; + + auto pSSVS = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(mrBase); + if (pSSVS == nullptr) + return; + + auto& rSSController = pSSVS->GetSlideSorter().GetController(); + auto& rPageSelector = rSSController.GetPageSelector(); + + for( sal_uInt16 nPage = 0; nPage < pDoc->GetSdPageCount(PageKind::Standard); nPage++ ) + { + if (rPageSelector.IsPageSelected(nPage)) + { + OUString aLayoutName(mxMasterSlide->get_active_text()); + pDoc->SetMasterPage(nPage, aLayoutName, pDoc, false, false); + } + } +} + +IMPL_LINK_NOARG(SlideBackground, EditMasterHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_SLIDE_MASTER_MODE, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, SelectBgHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_SELECT_BACKGROUND, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, CloseMasterHdl, weld::Button&, void) +{ + GetBindings()->GetDispatcher()->Execute( SID_CLOSE_MASTER_VIEW, SfxCallMode::RECORD ); +} + +IMPL_LINK_NOARG(SlideBackground, DspBackground, weld::Toggleable&, void) +{ + bool IsChecked = mxDspMasterBackground->get_active(); + const SfxBoolItem aBoolItem(SID_DISPLAY_MASTER_BACKGROUND, IsChecked); + GetBindings()->GetDispatcher()->ExecuteList(SID_DISPLAY_MASTER_BACKGROUND, SfxCallMode::RECORD, { &aBoolItem }); +} + +IMPL_LINK_NOARG(SlideBackground, DspObjects, weld::Toggleable&, void) +{ + bool IsChecked = mxDspMasterObjects->get_active(); + const SfxBoolItem aBoolItem(SID_DISPLAY_MASTER_OBJECTS,IsChecked); + GetBindings()->GetDispatcher()->ExecuteList(SID_DISPLAY_MASTER_OBJECTS, SfxCallMode::RECORD, { &aBoolItem, &aBoolItem }); +} + +IMPL_LINK_NOARG( SlideBackground, ModifyMarginHdl, weld::ComboBox&, void ) +{ + bool bApplyNewPageMargins = true; + switch ( mxMarginSelectBox->get_active() ) + { + case 0: + SetNone(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 1: + SetNarrow(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 2: + SetModerate(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 3: + SetNormal075(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 4: + SetNormal100(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 5: + SetNormal125(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + case 6: + SetWide(m_nPageLeftMargin, m_nPageRightMargin, m_nPageTopMargin, m_nPageBottomMargin); + break; + default: + bApplyNewPageMargins = false; + break; + } + + if(bApplyNewPageMargins) + { + ExecuteMarginLRChange(m_nPageLeftMargin, m_nPageRightMargin); + ExecuteMarginULChange(m_nPageTopMargin, m_nPageBottomMargin); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/sidebar/SlideBackground.hxx b/sd/source/ui/sidebar/SlideBackground.hxx new file mode 100644 index 000000000..25af0a4af --- /dev/null +++ b/sd/source/ui/sidebar/SlideBackground.hxx @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include + +namespace sd { class ViewShellBase; } +namespace sd::tools { class EventMultiplexerEvent; } + +class ColorListBox; +class SvxPageItem; +class SvxLongLRSpaceItem; +class SvxLongULSpaceItem; +class XFillColorItem; +class XGradient; +class XFillGradientItem; +class XFillBitmapItem; +class XFillHatchItem; + +namespace sd::sidebar { + +class SlideBackground : + public PanelLayout, + public ::sfx2::sidebar::IContextChangeReceiver, + public ::sfx2::sidebar::ControllerItem::ItemUpdateReceiverInterface +{ +public: + SlideBackground( + weld::Widget* pParent, + ViewShellBase& rBase, + const css::uno::Reference& rxFrame, + SfxBindings* pBindings ); + virtual ~SlideBackground() override; + SfxBindings* GetBindings() { return mpBindings; } + // Window + virtual void NotifyItemUpdate( + const sal_uInt16 nSID, + const SfxItemState eState, + const SfxPoolItem* pState) override; + + virtual void GetControlState( + const sal_uInt16 /*nSId*/, + boost::property_tree::ptree& /*rState*/) override {}; + + virtual void HandleContextChange( + const vcl::EnumContext& rContext) override; + virtual void DumpAsPropertyTree(::tools::JsonWriter&) override; + +private: + + ViewShellBase& mrBase; + + std::unique_ptr mxPaperSizeBox; + std::unique_ptr mxPaperOrientation; + std::unique_ptr mxMasterSlide; + std::unique_ptr mxBackgroundLabel; + std::unique_ptr mxFillStyle; + std::unique_ptr mxFillLB; + std::unique_ptr mxFillAttr; + std::unique_ptr mxFillGrad1; + std::unique_ptr mxFillGrad2; + std::unique_ptr mxInsertImage; + std::unique_ptr mxDspMasterBackground; + std::unique_ptr mxDspMasterObjects; + std::unique_ptr mxCloseMaster; + std::unique_ptr mxEditMaster; + std::unique_ptr mxMasterLabel; + std::unique_ptr mxMarginSelectBox; + std::unique_ptr mxCustomEntry; + std::unique_ptr mxMarginLabel; + + ::sfx2::sidebar::ControllerItem maPaperSizeController; + ::sfx2::sidebar::ControllerItem maPaperOrientationController; + ::sfx2::sidebar::ControllerItem maPaperMarginLRController; + ::sfx2::sidebar::ControllerItem maPaperMarginULController; + ::sfx2::sidebar::ControllerItem maBckColorController; + ::sfx2::sidebar::ControllerItem maBckGradientController; + ::sfx2::sidebar::ControllerItem maBckHatchController; + ::sfx2::sidebar::ControllerItem maBckBitmapController; + ::sfx2::sidebar::ControllerItem maBckFillStyleController; + ::sfx2::sidebar::ControllerItem maBckImageController; + ::sfx2::sidebar::ControllerItem maDspBckController; + ::sfx2::sidebar::ControllerItem maDspObjController; + ::sfx2::sidebar::ControllerItem maMetricController; + ::sfx2::sidebar::ControllerItem maCloseMasterController; + + std::unique_ptr< SvxPageItem > mpPageItem; + std::unique_ptr< XFillColorItem > mpColorItem; + std::unique_ptr< XFillGradientItem > mpGradientItem; + std::unique_ptr< XFillHatchItem > mpHatchItem; + std::unique_ptr< XFillBitmapItem > mpBitmapItem; + + bool mbSwitchModeToNormal; + bool mbSwitchModeToMaster; + + css::uno::Reference mxFrame; + vcl::EnumContext maContext; + vcl::EnumContext maDrawOtherContext; + vcl::EnumContext maDrawMasterContext; + vcl::EnumContext maImpressOtherContext; + vcl::EnumContext maImpressMasterContext; + vcl::EnumContext maImpressHandoutContext; + vcl::EnumContext maImpressNotesContext; + bool mbTitle; + std::unique_ptr mpPageLRMarginItem; + std::unique_ptr mpPageULMarginItem; + ::tools::Long m_nPageLeftMargin; + ::tools::Long m_nPageRightMargin; + ::tools::Long m_nPageTopMargin; + ::tools::Long m_nPageBottomMargin; + FieldUnit meFUnit; + OUString maCustomEntry; + + SfxBindings* mpBindings; + + MapUnit meUnit; + + DECL_LINK(FillBackgroundHdl, weld::ComboBox&, void); + DECL_LINK(FillStyleModifyHdl, weld::ComboBox&, void); + DECL_LINK(PaperSizeModifyHdl, weld::ComboBox&, void); + DECL_LINK(FillColorHdl, ColorListBox&, void); + DECL_LINK(AssignMasterPage, weld::ComboBox&, void); + DECL_LINK(DspBackground, weld::Toggleable&, void); + DECL_LINK(DspObjects, weld::Toggleable&, void); + DECL_LINK(CloseMasterHdl, weld::Button&, void); + DECL_LINK(EditMasterHdl, weld::Button&, void); + DECL_LINK(SelectBgHdl, weld::Button&, void); + DECL_LINK(EventMultiplexerListener, tools::EventMultiplexerEvent&, void ); + DECL_LINK( ModifyMarginHdl, weld::ComboBox&, void ); + + void Initialize(); + void Update(); + void UpdateMarginBox(); + void SetPanelTitle(const OUString& rTitle); + void SetMarginsFieldUnit(); + + Color const & GetColorSetOrDefault(); + XGradient const & GetGradientSetOrDefault(); + OUString const & GetHatchingSetOrDefault(); + OUString const & GetBitmapSetOrDefault(); + OUString const & GetPatternSetOrDefault(); + bool IsDraw(); + bool IsImpress(); + void addListener(); + void removeListener(); + void ExecuteMarginLRChange(const ::tools::Long mnPageLeftMargin, const ::tools::Long mnPageRightMargin); + void ExecuteMarginULChange(const ::tools::Long mnPageTopMargin, const ::tools::Long mnPageBottomMargin); + void populateMasterSlideDropdown(); + void updateMasterSlideSelection(); + + static FieldUnit GetCurrentUnit(SfxItemState eState, const SfxPoolItem* pState); +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/PaneHider.cxx b/sd/source/ui/slideshow/PaneHider.cxx new file mode 100644 index 000000000..85858c0b6 --- /dev/null +++ b/sd/source/ui/slideshow/PaneHider.cxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "PaneHider.hxx" + +#include +#include +#include "slideshowimpl.hxx" +#include + +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::lang::DisposedException; + +namespace sd +{ +PaneHider::PaneHider(const ViewShell& rViewShell, SlideshowImpl* pSlideShow) +{ + // Hide the left and right pane windows when a slideshow exists and is + // not full screen. + if (pSlideShow == nullptr || pSlideShow->isFullScreen()) + return; + + try + { + Reference xControllerManager( + rViewShell.GetViewShellBase().GetController(), UNO_QUERY_THROW); + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + // Get and save the current configuration. + mxConfiguration = mxConfigurationController->getRequestedConfiguration(); + if (mxConfiguration.is()) + { + // Iterate over the resources and deactivate the panes. + const Sequence> aResources(mxConfiguration->getResources( + nullptr, framework::FrameworkHelper::msPaneURLPrefix, + AnchorBindingMode_DIRECT)); + for (const Reference& xPaneId : aResources) + { + if (xPaneId->getResourceURL() != FrameworkHelper::msCenterPaneURL) + { + mxConfigurationController->requestResourceDeactivation(xPaneId); + } + } + } + } + FrameworkHelper::Instance(rViewShell.GetViewShellBase())->WaitForUpdate(); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } +} + +PaneHider::~PaneHider() +{ + if (mxConfiguration.is() && mxConfigurationController.is()) + { + try + { + mxConfigurationController->restoreConfiguration(mxConfiguration); + } + catch (DisposedException&) + { + // When the configuration controller is already disposed then + // there is no point in restoring the configuration. + } + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/PaneHider.hxx b/sd/source/ui/slideshow/PaneHider.hxx new file mode 100644 index 000000000..a2d3cabb0 --- /dev/null +++ b/sd/source/ui/slideshow/PaneHider.hxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace com::sun::star::drawing::framework +{ +class XConfiguration; +} +namespace com::sun::star::drawing::framework +{ +class XConfigurationController; +} + +namespace sd +{ +class ViewShell; +class SlideshowImpl; + +/** Hide the windows of the side panes and restore the original visibility + later. Used by the in-window slide show in order to use the whole frame + window for the show. +*/ +class PaneHider +{ +public: + /** The constructor hides all side panes that belong to the + ViewShellBase of the given view shell. + */ + PaneHider(const ViewShell& rViewShell, SlideshowImpl* pSlideShow); + + /** Restore the original visibility of the side panes. + */ + ~PaneHider(); + +private: + /** Remember whether the visibility states of the windows of the panes + has been modified and have to be restored. + */ + + css::uno::Reference + mxConfigurationController; + css::uno::Reference mxConfiguration; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/SlideShowRestarter.cxx b/sd/source/ui/slideshow/SlideShowRestarter.cxx new file mode 100644 index 000000000..b8c61ba48 --- /dev/null +++ b/sd/source/ui/slideshow/SlideShowRestarter.cxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include "SlideShowRestarter.hxx" + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using ::sd::framework::FrameworkHelper; + +namespace sd { + +SlideShowRestarter::SlideShowRestarter ( + const ::rtl::Reference& rpSlideShow, + ViewShellBase* pViewShellBase) + : mnEventId(nullptr), + mpSlideShow(rpSlideShow), + mpViewShellBase(pViewShellBase), + mnDisplayCount(Application::GetScreenCount()), + mpDispatcher(pViewShellBase->GetViewFrame()->GetDispatcher()), + mnCurrentSlideNumber(0) +{ +} + +SlideShowRestarter::~SlideShowRestarter() +{ +} + +void SlideShowRestarter::Restart (bool bForce) +{ + // Prevent multiple and concurrently restarts. + if (mnEventId != nullptr) + return; + + if (bForce) + mnDisplayCount = 0; + + // Remember the current slide in order to restore it after the slide + // show has been restarted. + if (mpSlideShow.is()) + mnCurrentSlideNumber = mpSlideShow->getCurrentPageNumber(); + + // Remember a shared pointer to this object to prevent its destruction + // before the whole restarting process has finished. + mpSelf = shared_from_this(); + + // We do not know in what situation this method was called. So, in + // order to be able to cleanly stop the presentation, we do that + // asynchronously. + mnEventId = Application::PostUserEvent( + LINK(this, SlideShowRestarter, EndPresentation)); +} + +IMPL_LINK_NOARG(SlideShowRestarter, EndPresentation, void*, void) +{ + mnEventId = nullptr; + if (!mpSlideShow.is()) + return; + + if (mnDisplayCount == static_cast(Application::GetScreenCount())) + return; + + bool bIsExitAfterPresenting = mpSlideShow->IsExitAfterPresenting(); + mpSlideShow->SetExitAfterPresenting(false); + mpSlideShow->end(); + mpSlideShow->SetExitAfterPresenting(bIsExitAfterPresenting); + + // The following piece of code should not be here because the + // slide show should be aware of the existence of the presenter + // console (which is displayed in the FullScreenPane). But the + // timing has to be right and I did not find a better place for + // it. + + // Wait for the full screen pane, which displays the presenter + // console, to disappear. Only when it is gone, call + // InitiatePresenterStart(), in order to begin the asynchronous + // restart of the slide show. + if (mpViewShellBase == nullptr) + return; + + ::std::shared_ptr pHelper( + FrameworkHelper::Instance(*mpViewShellBase)); + if (pHelper->GetConfigurationController()->getResource( + FrameworkHelper::CreateResourceId(FrameworkHelper::msFullScreenPaneURL)).is()) + { + ::sd::framework::ConfigurationController::Lock aLock ( + pHelper->GetConfigurationController()); + + pHelper->RunOnConfigurationEvent( + FrameworkHelper::msConfigurationUpdateEndEvent, + ::std::bind(&SlideShowRestarter::StartPresentation, shared_from_this())); + pHelper->UpdateConfiguration(); + } + else + { + StartPresentation(); + } +} + +void SlideShowRestarter::StartPresentation() +{ + //rhbz#1091117 crash because we're exiting the application, and this is + //being called during the configuration update event on exit. At this point + //newly created objects won't get disposed called on them, because the + //disposer is doing its last execution of that now. + if (mpViewShellBase && mpViewShellBase->GetDrawController().IsDisposing()) + return; + + if (mpDispatcher == nullptr && mpViewShellBase!=nullptr) + mpDispatcher = mpViewShellBase->GetViewFrame()->GetDispatcher(); + + // Start the slide show on the saved current slide. + if (mpDispatcher != nullptr) + { + mpDispatcher->Execute(SID_PRESENTATION, SfxCallMode::ASYNCHRON); + if (mpSlideShow.is()) + { + Sequence aProperties{ comphelper::makePropertyValue("FirstPage", + "page" + OUString::number(mnCurrentSlideNumber+1)) }; + mpSlideShow->startWithArguments(aProperties); + } + mpSelf.reset(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/SlideShowRestarter.hxx b/sd/source/ui/slideshow/SlideShowRestarter.hxx new file mode 100644 index 000000000..e8e97a600 --- /dev/null +++ b/sd/source/ui/slideshow/SlideShowRestarter.hxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd +{ +class SlideShow; +} +namespace sd +{ +class ViewShellBase; +} +class SfxDispatcher; +struct ImplSVEvent; + +namespace sd +{ +/** This class is used when a display is removed or added to restart the + slide show. This is necessary at least with DirectX because + deactivating a display invalidates DirectX resources. Accessing those + leads to a crash. + + During a restart a possibly installed presenter extension is given the + opportunity to show or hide depending on the number of available displays. +*/ +class SlideShowRestarter : public std::enable_shared_from_this +{ +public: + /** Create a new SlideShowRestarter object. + @param rpSlideShow + The slide show is used to determine the current slide, which is + restored after the restart, and of course to stop and start the + slide show. + @param pViewShellBase + Used to get access to a slot dispatcher. + */ + SlideShowRestarter(const ::rtl::Reference& rpSlideShow, + ViewShellBase* pViewShellBase); + virtual ~SlideShowRestarter(); + + /** Restarting the slide show is an asynchronous multi step process + which is started by calling this method. + @param bForce + Used to force a re-start, even if the display count is unchanged. + */ + void Restart(bool bForce); + +private: + ImplSVEvent* mnEventId; + ::rtl::Reference mpSlideShow; + ViewShellBase* mpViewShellBase; + ::std::shared_ptr mpSelf; + sal_Int32 mnDisplayCount; + SfxDispatcher* mpDispatcher; + sal_Int32 mnCurrentSlideNumber; + + DECL_LINK(EndPresentation, void*, void); + + /** Restart the presentation on the slide last shown before the restart + was initiated. + */ + void StartPresentation(); +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/showwin.cxx b/sd/source/ui/slideshow/showwin.cxx new file mode 100644 index 000000000..35c0a4027 --- /dev/null +++ b/sd/source/ui/slideshow/showwin.cxx @@ -0,0 +1,629 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include "showwindow.hxx" +#include "slideshowimpl.hxx" + +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; + +namespace sd { + +const sal_uInt64 HIDE_MOUSE_TIMEOUT = 10000; +const sal_uInt64 SHOW_MOUSE_TIMEOUT = 1000; + +ShowWindow::ShowWindow( const ::rtl::Reference< SlideshowImpl >& xController, vcl::Window* pParent ) +: ::sd::Window( pParent ) +, maPauseTimer("sd ShowWindow maPauseTimer") +, maMouseTimer("sd ShowWindow maMouseTimer") +, mnPauseTimeout( SLIDE_NO_TIMEOUT ) +, mnRestartPageIndex( PAGE_NO_END ) +, meShowWindowMode(SHOWWINDOWMODE_NORMAL) +, mbShowNavigatorAfterSpecialMode( false ) +, mbMouseAutoHide(true) +, mbMouseCursorHidden(false) +, mnFirstMouseMove(0) +, mxController( xController ) +{ + GetOutDev()->SetOutDevViewType( OutDevViewType::SlideShow ); + + // Do never mirror the preview window. This explicitly includes right + // to left writing environments. + EnableRTL (false); + + MapMode aMap(GetMapMode()); + aMap.SetMapUnit(MapUnit::Map100thMM); + SetMapMode(aMap); + + // set HelpId + SetHelpId( HID_SD_WIN_PRESENTATION ); + + maPauseTimer.SetInvokeHandler( LINK( this, ShowWindow, PauseTimeoutHdl ) ); + maPauseTimer.SetTimeout( 1000 ); + maMouseTimer.SetInvokeHandler( LINK( this, ShowWindow, MouseTimeoutHdl ) ); + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + + maShowBackground = Wallpaper( COL_BLACK ); + SetBackground(); // avoids that VCL paints any background! + GetParent()->Show(); + AddEventListener( LINK( this, ShowWindow, EventHdl ) ); +} + +ShowWindow::~ShowWindow() +{ + disposeOnce(); +} + +void ShowWindow::dispose() +{ + maPauseTimer.Stop(); + maMouseTimer.Stop(); + ::sd::Window::dispose(); +} + +void ShowWindow::KeyInput(const KeyEvent& rKEvt) +{ + bool bReturn = false; + + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + bReturn = true; + } + else if( SHOWWINDOWMODE_END == meShowWindowMode ) + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case KEY_PAGEUP: + case KEY_LEFT: + case KEY_UP: + case KEY_P: + case KEY_HOME: + case KEY_END: + case awt::Key::CONTEXTMENU: + // these keys will be handled by the slide show even + // while in end mode + break; + default: + TerminateShow(); + bReturn = true; + } + } + else if( SHOWWINDOWMODE_BLANK == meShowWindowMode ) + { + bool bFakeKeyPress = rKEvt.GetKeyCode().GetFullCode() == 0; + // Ignore workaround of https://gitlab.gnome.org/GNOME/gtk/issues/1785 + // See calls to GtkSalFrame::makeFakeKeyPress (Fixed in GTK 2.34) + if (!bFakeKeyPress) + RestartShow(); + bReturn = true; + } + else if( SHOWWINDOWMODE_PAUSE == meShowWindowMode ) + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case KEY_ESCAPE: + TerminateShow(); + bReturn = true; + break; + case KEY_PAGEUP: + case KEY_RIGHT: + case KEY_UP: + case KEY_P: + case KEY_HOME: + case KEY_END: + case awt::Key::CONTEXTMENU: + // these keys will be handled by the slide show even + // while in end mode + break; + default: + RestartShow(); + bReturn = true; + break; + } + } + + if( !bReturn ) + { + if( mxController.is() ) + bReturn = mxController->keyInput(rKEvt); + + if( !bReturn ) + { + if( mpViewShell ) + { + mpViewShell->KeyInput(rKEvt,this); + } + else + { + Window::KeyInput(rKEvt); + } + } + } + + if( mpViewShell ) + mpViewShell->SetActiveWindow( this ); +} + +void ShowWindow::MouseButtonDown(const MouseEvent& /*rMEvt*/) +{ + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + } + else if( mpViewShell ) + { + mpViewShell->SetActiveWindow( this ); + } +} + +void ShowWindow::MouseMove(const MouseEvent& /*rMEvt*/) +{ + if( mbMouseAutoHide ) + { + if( mbMouseCursorHidden ) + { + if( mnFirstMouseMove ) + { + // if this is not the first mouse move while hidden, see if + // enough time has pasted to show mouse pointer again + sal_uInt64 nTime = ::tools::Time::GetSystemTicks(); + if( (nTime - mnFirstMouseMove) >= SHOW_MOUSE_TIMEOUT ) + { + ShowPointer( true ); + mnFirstMouseMove = 0; + mbMouseCursorHidden = false; + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } + else + { + // if this is the first mouse move, note current + // time and start idle timer to cancel show mouse pointer + // again if not enough mouse movement is measured + mnFirstMouseMove = ::tools::Time::GetSystemTicks(); + maMouseTimer.SetTimeout( 2*SHOW_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } + else + { + // current mousemove restarts the idle timer to hide the mouse + maMouseTimer.Start(); + } + } + + if( mpViewShell ) + mpViewShell->SetActiveWindow( this ); +} + +void ShowWindow::MouseButtonUp(const MouseEvent& rMEvt) +{ + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode ) + { + TerminateShow(); + } + else if( (SHOWWINDOWMODE_END == meShowWindowMode) && !rMEvt.IsRight() ) + { + TerminateShow(); + } + else if( (( SHOWWINDOWMODE_BLANK == meShowWindowMode ) || ( SHOWWINDOWMODE_PAUSE == meShowWindowMode )) + && !rMEvt.IsRight() ) + { + RestartShow(); + } + else + { + if( mxController.is() ) + mxController->mouseButtonUp( rMEvt ); + } +} + +/** + * if FuSlideShow is still available, forward it + */ +void ShowWindow::Paint(vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) +{ + if( (meShowWindowMode == SHOWWINDOWMODE_NORMAL) || (meShowWindowMode == SHOWWINDOWMODE_PREVIEW) ) + { + if( mxController.is() ) + { + mxController->paint(); + } + else if(mpViewShell ) + { + mpViewShell->Paint(rRect, this); + } + } + else + { + GetOutDev()->DrawWallpaper( rRect, maShowBackground ); + + if( SHOWWINDOWMODE_END == meShowWindowMode ) + { + DrawEndScene(); + } + else if( SHOWWINDOWMODE_PAUSE == meShowWindowMode ) + { + DrawPauseScene( false ); + } + else if( SHOWWINDOWMODE_BLANK == meShowWindowMode ) + { + // just blank through background color => nothing to be done here + } + } +} + +void ShowWindow::LoseFocus() +{ + Window::LoseFocus(); + + if( SHOWWINDOWMODE_PREVIEW == meShowWindowMode) + TerminateShow(); +} + +void ShowWindow::SetEndMode() +{ + if( !(( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView()) ) + return; + + DeleteWindowFromPaintView(); + meShowWindowMode = SHOWWINDOWMODE_END; + maShowBackground = Wallpaper( COL_BLACK ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + Invalidate(); +} + +bool ShowWindow::SetPauseMode( sal_Int32 nTimeout, Graphic const * pLogo ) +{ + rtl::Reference< SlideShow > xSlideShow; + + if( mpViewShell ) + xSlideShow = SlideShow::GetSlideShow( mpViewShell->GetViewShellBase() ); + + if( xSlideShow.is() && !nTimeout ) + { + xSlideShow->jumpToPageIndex( 0 ); + } + else if( ( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView() ) + { + DeleteWindowFromPaintView(); + mnPauseTimeout = nTimeout; + mnRestartPageIndex = 0; + meShowWindowMode = SHOWWINDOWMODE_PAUSE; + maShowBackground = Wallpaper( COL_BLACK ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + if( pLogo ) + maLogo = *pLogo; + + Invalidate(); + + if( SLIDE_NO_TIMEOUT != mnPauseTimeout ) + maPauseTimer.Start(); + } + + return( SHOWWINDOWMODE_PAUSE == meShowWindowMode ); +} + +bool ShowWindow::SetBlankMode( sal_Int32 nPageIndexToRestart, const Color& rBlankColor ) +{ + if( ( SHOWWINDOWMODE_NORMAL == meShowWindowMode ) && mpViewShell && mpViewShell->GetView() ) + { + DeleteWindowFromPaintView(); + mnRestartPageIndex = nPageIndexToRestart; + meShowWindowMode = SHOWWINDOWMODE_BLANK; + maShowBackground = Wallpaper( rBlankColor ); + + // hide navigator if it is visible + if( mpViewShell->GetViewFrame()->GetChildWindow( SID_NAVIGATOR ) ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR, false ); + mbShowNavigatorAfterSpecialMode = true; + } + + Invalidate(); + } + + return( SHOWWINDOWMODE_BLANK == meShowWindowMode ); +} + +void ShowWindow::SetPreviewMode() +{ + meShowWindowMode = SHOWWINDOWMODE_PREVIEW; +} + +void ShowWindow::TerminateShow() +{ + maLogo.Clear(); + maPauseTimer.Stop(); + maMouseTimer.Stop(); + GetOutDev()->Erase(); + maShowBackground = Wallpaper( COL_BLACK ); + meShowWindowMode = SHOWWINDOWMODE_NORMAL; + mnPauseTimeout = SLIDE_NO_TIMEOUT; + + if( mpViewShell ) + { + // show navigator? + if( mbShowNavigatorAfterSpecialMode ) + { + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR ); + mbShowNavigatorAfterSpecialMode = false; + } + } + + if( mxController.is() ) + mxController->endPresentation(); + + mnRestartPageIndex = PAGE_NO_END; +} + +void ShowWindow::RestartShow() +{ + RestartShow( mnRestartPageIndex ); +} + +void ShowWindow::RestartShow( sal_Int32 nPageIndexToRestart ) +{ + ShowWindowMode eOldShowWindowMode = meShowWindowMode; + + maLogo.Clear(); + maPauseTimer.Stop(); + GetOutDev()->Erase(); + maShowBackground = Wallpaper( COL_BLACK ); + meShowWindowMode = SHOWWINDOWMODE_NORMAL; + mnPauseTimeout = SLIDE_NO_TIMEOUT; + + if( mpViewShell ) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( mpViewShell->GetViewShellBase() ) ); + + if( xSlideShow.is() ) + { + AddWindowToPaintView(); + + if( SHOWWINDOWMODE_BLANK == eOldShowWindowMode || SHOWWINDOWMODE_END == eOldShowWindowMode ) + { + xSlideShow->pause(false); + Invalidate(); + } + else + { + xSlideShow->jumpToPageIndex( nPageIndexToRestart ); + } + } + } + + mnRestartPageIndex = PAGE_NO_END; + + // show navigator? + if( mbShowNavigatorAfterSpecialMode ) + { + if (mpViewShell) + mpViewShell->GetViewFrame()->ShowChildWindow( SID_NAVIGATOR ); + mbShowNavigatorAfterSpecialMode = false; + } +} + +void ShowWindow::DrawPauseScene( bool bTimeoutOnly ) +{ + const MapMode& rMap = GetMapMode(); + const Point aOutOrg( PixelToLogic( Point() ) ); + const Size aOutSize( GetOutDev()->GetOutputSize() ); + const Size aTextSize(OutputDevice::LogicToLogic(Size(0, 14), MapMode(MapUnit::MapPoint), rMap)); + const Size aOffset(OutputDevice::LogicToLogic(Size(1000, 1000), MapMode(MapUnit::Map100thMM), rMap)); + OUString aText( SdResId( STR_PRES_PAUSE ) ); + bool bDrawn = false; + + vcl::Font aFont( GetSettings().GetStyleSettings().GetMenuFont() ); + const vcl::Font aOldFont( GetFont() ); + + aFont.SetFontSize( aTextSize ); + aFont.SetColor( COL_WHITE ); + aFont.SetCharSet( aOldFont.GetCharSet() ); + aFont.SetLanguage( aOldFont.GetLanguage() ); + + if( !bTimeoutOnly && ( maLogo.GetType() != GraphicType::NONE ) ) + { + Size aGrfSize; + + if (maLogo.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) + aGrfSize = PixelToLogic( maLogo.GetPrefSize() ); + else + aGrfSize = OutputDevice::LogicToLogic( maLogo.GetPrefSize(), maLogo.GetPrefMapMode(), rMap ); + + const Point aGrfPos( std::max( aOutOrg.X() + aOutSize.Width() - aGrfSize.Width() - aOffset.Width(), aOutOrg.X() ), + std::max( aOutOrg.Y() + aOutSize.Height() - aGrfSize.Height() - aOffset.Height(), aOutOrg.Y() ) ); + + if( maLogo.IsAnimated() ) + maLogo.StartAnimation(*GetOutDev(), aGrfPos, aGrfSize, reinterpret_cast(this)); + else + maLogo.Draw(*GetOutDev(), aGrfPos, aGrfSize); + } + + if( SLIDE_NO_TIMEOUT != mnPauseTimeout ) + { + MapMode aVMap( rMap ); + ScopedVclPtrInstance< VirtualDevice > pVDev( *GetOutDev() ); + + aVMap.SetOrigin( Point() ); + pVDev->SetMapMode( aVMap ); + pVDev->SetBackground( Wallpaper( COL_BLACK ) ); + + // set font first, to determine real output height + pVDev->SetFont( aFont ); + + const Size aVDevSize( aOutSize.Width(), pVDev->GetTextHeight() ); + + if( pVDev->SetOutputSize( aVDevSize ) ) + { + // Note: if performance gets an issue here, we can use NumberFormatter directly + SvtSysLocale aSysLocale; + const LocaleDataWrapper& aLocaleData = aSysLocale.GetLocaleData(); + + aText += " ( " + aLocaleData.getDuration( ::tools::Time( 0, 0, mnPauseTimeout ) ) + " )"; + pVDev->DrawText( Point( aOffset.Width(), 0 ), aText ); + GetOutDev()->DrawOutDev( Point( aOutOrg.X(), aOffset.Height() ), aVDevSize, Point(), aVDevSize, *pVDev ); + bDrawn = true; + } + } + + if( !bDrawn ) + { + SetFont( aFont ); + GetOutDev()->DrawText( Point( aOutOrg.X() + aOffset.Width(), aOutOrg.Y() + aOffset.Height() ), aText ); + SetFont( aOldFont ); + } +} + +void ShowWindow::DrawEndScene() +{ + const vcl::Font aOldFont( GetFont() ); + vcl::Font aFont( GetSettings().GetStyleSettings().GetMenuFont() ); + + const Point aOutOrg( PixelToLogic( Point() ) ); + const Size aTextSize(OutputDevice::LogicToLogic(Size(0, 14), MapMode(MapUnit::MapPoint), GetMapMode())); + const OUString aText( SdResId( STR_PRES_SOFTEND ) ); + + aFont.SetFontSize( aTextSize ); + aFont.SetColor( COL_WHITE ); + aFont.SetCharSet( aOldFont.GetCharSet() ); + aFont.SetLanguage( aOldFont.GetLanguage() ); + SetFont( aFont ); + GetOutDev()->DrawText( Point( aOutOrg.X() + aTextSize.Height(), aOutOrg.Y() + aTextSize.Height() ), aText ); + SetFont( aOldFont ); +} + +IMPL_LINK( ShowWindow, PauseTimeoutHdl, Timer*, pTimer, void ) +{ + if( !( --mnPauseTimeout ) ) + RestartShow(); + else + { + DrawPauseScene( true ); + pTimer->Start(); + } +} + +IMPL_LINK_NOARG(ShowWindow, MouseTimeoutHdl, Timer *, void) +{ + if( mbMouseCursorHidden ) + { + // not enough mouse movements since first recording so + // cancel show mouse pointer for now + mnFirstMouseMove = 0; + } + else + { + // mouse has been idle too long, hide pointer + ShowPointer( false ); + mbMouseCursorHidden = true; + } +} + +IMPL_LINK( ShowWindow, EventHdl, VclWindowEvent&, rEvent, void ) +{ + if( mbMouseAutoHide ) + { + if (rEvent.GetId() == VclEventId::WindowShow) + { + maMouseTimer.SetTimeout( HIDE_MOUSE_TIMEOUT ); + maMouseTimer.Start(); + } + } +} + +void ShowWindow::DeleteWindowFromPaintView() +{ + if( mpViewShell->GetView() ) + mpViewShell->GetView()->DeleteWindowFromPaintView( GetOutDev() ); + + sal_uInt16 nChild = GetChildCount(); + while( nChild-- ) + GetChild( nChild )->Show( false ); +} + +void ShowWindow::AddWindowToPaintView() +{ + if( mpViewShell->GetView() ) + mpViewShell->GetView()->AddWindowToPaintView( GetOutDev(), nullptr ); + + sal_uInt16 nChild = GetChildCount(); + while( nChild-- ) + GetChild( nChild )->Show(); +} + +// Override the sd::Window's CreateAccessible to create a different accessible object +css::uno::Reference + ShowWindow::CreateAccessible() +{ + css::uno::Reference< css::accessibility::XAccessible > xAcc = GetAccessible(false); + if (xAcc) + { + return xAcc; + } + if (mpViewShell != nullptr) + { + xAcc = mpViewShell->CreateAccessibleDocumentView (this); + SetAccessible(xAcc); + return xAcc; + } + else + { + SAL_WARN("sd", "::sd::Window::CreateAccessible: no view shell"); + return vcl::Window::CreateAccessible (); + } +} +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/showwindow.hxx b/sd/source/ui/slideshow/showwindow.hxx new file mode 100644 index 000000000..f0f88228b --- /dev/null +++ b/sd/source/ui/slideshow/showwindow.hxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +#include + +namespace sd { + +class SlideshowImpl; + +#define SLIDE_NO_TIMEOUT SAL_MAX_INT32 + +enum ShowWindowMode +{ + SHOWWINDOWMODE_NORMAL = 0, + SHOWWINDOWMODE_PAUSE = 1, + SHOWWINDOWMODE_END = 2, + SHOWWINDOWMODE_BLANK = 3, + SHOWWINDOWMODE_PREVIEW = 4 +}; + +class ShowWindow + : public ::sd::Window +{ + +public: + ShowWindow ( const ::rtl::Reference< ::sd::SlideshowImpl >& xController, vcl::Window* pParent ); + virtual ~ShowWindow() override; + virtual void dispose() override; + + void SetEndMode(); + bool SetPauseMode( sal_Int32 nTimeoutSec, Graphic const * pLogo = nullptr ); + bool SetBlankMode( sal_Int32 nPageIndexToRestart, const Color& rBlankColor ); + + const Color& GetBlankColor() const { return maShowBackground.GetColor(); } + + void SetPreviewMode(); + + void SetMouseAutoHide( bool bMouseAutoHide ) { mbMouseAutoHide = bMouseAutoHide; } + + ShowWindowMode GetShowWindowMode() const { return meShowWindowMode; } + + void RestartShow( sal_Int32 nPageIndexToRestart ); + + virtual void LoseFocus() override; + + virtual void KeyInput(const KeyEvent& rKEvt) override; + virtual void MouseMove(const MouseEvent& rMEvt) override; + virtual void MouseButtonUp(const MouseEvent& rMEvt) override; + virtual void MouseButtonDown(const MouseEvent& rMEvt) override; + virtual void Paint(vcl::RenderContext& rRenderContext, const ::tools::Rectangle& rRect) override; + /// Override the sd::Window's CreateAccessible to create a different accessible object + virtual css::uno::Reference + CreateAccessible() override; + + void TerminateShow(); + void RestartShow(); + +private: + void DrawPauseScene( bool bTimeoutOnly ); + void DrawEndScene(); + + void DeleteWindowFromPaintView(); + void AddWindowToPaintView(); + +private: + Timer maPauseTimer; + Timer maMouseTimer; + Wallpaper maShowBackground; + Graphic maLogo; + sal_uLong mnPauseTimeout; + sal_Int32 mnRestartPageIndex; + ShowWindowMode meShowWindowMode; + bool mbShowNavigatorAfterSpecialMode; + bool mbMouseAutoHide; + bool mbMouseCursorHidden; + sal_uInt64 mnFirstMouseMove; + + DECL_LINK( PauseTimeoutHdl, Timer*, void ); + DECL_LINK(MouseTimeoutHdl, Timer *, void); + DECL_LINK( EventHdl, VclWindowEvent&, void ); + + ::rtl::Reference< SlideshowImpl > mxController; +}; + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshow.cxx b/sd/source/ui/slideshow/slideshow.cxx new file mode 100644 index 000000000..fa14c4de6 --- /dev/null +++ b/sd/source/ui/slideshow/slideshow.cxx @@ -0,0 +1,1191 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include "slideshowimpl.hxx" +#include +#include +#include +#include +#include +#include +#include "SlideShowRestarter.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using ::com::sun::star::presentation::XSlideShowController; +using ::sd::framework::FrameworkHelper; +using ::com::sun::star::awt::XWindow; +using namespace ::sd; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::animations; +using namespace ::com::sun::star::drawing::framework; + +namespace { + /** This local version of the work window overrides DataChanged() so that it + can restart the slide show when a display is added or removed. + */ + class FullScreenWorkWindow : public WorkWindow + { + public: + FullScreenWorkWindow ( + const ::rtl::Reference& rpSlideShow, + ViewShellBase* pViewShellBase) + : WorkWindow(nullptr, WB_HIDE | WB_CLIPCHILDREN), + mpRestarter(std::make_shared(rpSlideShow, pViewShellBase)) + {} + + void Restart(bool bForce) + { + mpRestarter->Restart(bForce); + } + + virtual void DataChanged (const DataChangedEvent& rEvent) override + { + if (rEvent.GetType() == DataChangedEventType::DISPLAY) + Restart(false); + } + + private: + ::std::shared_ptr mpRestarter; + }; +} + +static const SfxItemPropertyMapEntry* ImplGetPresentationPropertyMap() +{ + // NOTE: First member must be sorted + static const SfxItemPropertyMapEntry aPresentationPropertyMap_Impl[] = + { + { u"AllowAnimations", ATTR_PRESENT_ANIMATION_ALLOWED, cppu::UnoType::get(), 0, 0 }, + { u"CustomShow", ATTR_PRESENT_CUSTOMSHOW, ::cppu::UnoType::get(), 0, 0 }, + { u"Display", ATTR_PRESENT_DISPLAY, ::cppu::UnoType::get(), 0, 0 }, + { u"FirstPage", ATTR_PRESENT_DIANAME, ::cppu::UnoType::get(), 0, 0 }, + { u"IsAlwaysOnTop", ATTR_PRESENT_ALWAYS_ON_TOP, cppu::UnoType::get(), 0, 0 }, + { u"IsAutomatic", ATTR_PRESENT_MANUEL, cppu::UnoType::get(), 0, 0 }, + { u"IsEndless", ATTR_PRESENT_ENDLESS, cppu::UnoType::get(), 0, 0 }, + { u"IsFullScreen", ATTR_PRESENT_FULLSCREEN, cppu::UnoType::get(), 0, 0 }, + { u"IsShowAll", ATTR_PRESENT_ALL, cppu::UnoType::get(), 0, 0 }, + { u"IsMouseVisible", ATTR_PRESENT_MOUSE, cppu::UnoType::get(), 0, 0 }, + { u"IsShowLogo", ATTR_PRESENT_SHOW_PAUSELOGO, cppu::UnoType::get(), 0, 0 }, + { u"IsTransitionOnClick", ATTR_PRESENT_CHANGE_PAGE, cppu::UnoType::get(), 0, 0 }, + { u"Pause", ATTR_PRESENT_PAUSE_TIMEOUT, ::cppu::UnoType::get(), 0, 0 }, + { u"StartWithNavigator", ATTR_PRESENT_NAVIGATOR, cppu::UnoType::get(), 0, 0 }, + { u"UsePen", ATTR_PRESENT_PEN, cppu::UnoType::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + return aPresentationPropertyMap_Impl; +} + + +SlideShow::SlideShow( SdDrawDocument* pDoc ) +: maPropSet(ImplGetPresentationPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool()) +, mbIsInStartup(false) +, mpDoc( pDoc ) +, mpCurrentViewShellBase( nullptr ) +, mpFullScreenViewShellBase( nullptr ) +, mpFullScreenFrameView( nullptr ) +, mnInPlaceConfigEvent( nullptr ) +{ +} + +void SlideShow::ThrowIfDisposed() const +{ + if( mpDoc == nullptr ) + throw DisposedException(); +} + +/// used by the model to create a slideshow for it +rtl::Reference< SlideShow > SlideShow::Create( SdDrawDocument* pDoc ) +{ + return new SlideShow( pDoc ); +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( SdDrawDocument const * pDocument ) +{ + rtl::Reference< SlideShow > xRet; + + if( pDocument ) + xRet = GetSlideShow( *pDocument ); + + return xRet; +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( SdDrawDocument const & rDocument ) +{ + return rtl::Reference< SlideShow >( + dynamic_cast< SlideShow* >( rDocument.getPresentation().get() ) ); +} + +rtl::Reference< SlideShow > SlideShow::GetSlideShow( ViewShellBase const & rBase ) +{ + return GetSlideShow( rBase.GetDocument() ); +} + +css::uno::Reference< css::presentation::XSlideShowController > SlideShow::GetSlideShowController(ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + + Reference< XSlideShowController > xRet; + if( xSlideShow.is() ) + xRet = xSlideShow->getController(); + + return xRet; +} + +bool SlideShow::StartPreview( ViewShellBase const & rBase, + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + if( !xSlideShow.is() ) + return false; + + xSlideShow->startPreview( xDrawPage, xAnimationNode ); + return true; +} + +void SlideShow::Stop( ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + if( xSlideShow.is() ) + xSlideShow->end(); +} + +bool SlideShow::IsRunning( ViewShellBase const & rBase ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rBase ) ); + return xSlideShow.is() && xSlideShow->isRunning(); +} + +bool SlideShow::IsRunning( const ViewShell& rViewShell ) +{ + rtl::Reference< SlideShow > xSlideShow( GetSlideShow( rViewShell.GetViewShellBase() ) ); + return xSlideShow.is() && xSlideShow->isRunning() && (xSlideShow->mxController->getViewShell() == &rViewShell); +} + +void SlideShow::CreateController( ViewShell* pViewSh, ::sd::View* pView, vcl::Window* pParentWindow ) +{ + SAL_INFO_IF( !mxController.is(), "sd.slideshow", "sd::SlideShow::CreateController(), clean up old controller first!" ); + + Reference< XPresentation2 > xThis( this ); + + rtl::Reference xController ( + new SlideshowImpl(xThis, pViewSh, pView, mpDoc, pParentWindow)); + + // Reset mbIsInStartup. From here mxController.is() is used to prevent + // multiple slide show instances for one document. + mxController = xController; + mbIsInStartup = false; + +} + +// XServiceInfo +OUString SAL_CALL SlideShow::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SlideShow"; +} + +sal_Bool SAL_CALL SlideShow::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SlideShow::getSupportedServiceNames( ) +{ + return { "com.sun.star.presentation.Presentation" }; +} + +// XPropertySet +Reference< XPropertySetInfo > SAL_CALL SlideShow::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + static Reference< XPropertySetInfo > xInfo = maPropSet.getPropertySetInfo(); + return xInfo; + } + +void SAL_CALL SlideShow::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + sd::PresentationSettings& rPresSettings = mpDoc->getPresentationSettings(); + + const SfxItemPropertyMapEntry* pEntry = maPropSet.getPropertyMapEntry(aPropertyName); + + if( pEntry && ((pEntry->nFlags & PropertyAttribute::READONLY) != 0) ) + throw PropertyVetoException(); + + bool bValuesChanged = false; + bool bIllegalArgument = true; + + switch( pEntry ? pEntry->nWID : -1 ) + { + case ATTR_PRESENT_ALL: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbAll != bVal ) + { + rPresSettings.mbAll = bVal; + bValuesChanged = true; + if( bVal ) + rPresSettings.mbCustomShow = false; + } + } + break; + } + case ATTR_PRESENT_CHANGE_PAGE: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( bVal == rPresSettings.mbLockedPages ) + { + bValuesChanged = true; + rPresSettings.mbLockedPages = !bVal; + } + } + break; + } + + case ATTR_PRESENT_ANIMATION_ALLOWED: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if(rPresSettings.mbAnimationAllowed != bVal) + { + bValuesChanged = true; + rPresSettings.mbAnimationAllowed = bVal; + } + } + break; + } + case ATTR_PRESENT_CUSTOMSHOW: + { + OUString aShowName; + if( aValue >>= aShowName ) + { + bIllegalArgument = false; + + SdCustomShowList* pCustomShowList = mpDoc->GetCustomShowList(); + if(pCustomShowList) + { + SdCustomShow* pCustomShow; + for( pCustomShow = pCustomShowList->First(); pCustomShow != nullptr; pCustomShow = pCustomShowList->Next() ) + { + if( pCustomShow->GetName() == aShowName ) + break; + } + + rPresSettings.mbCustomShow = true; + bValuesChanged = true; + } + } + break; + } + case ATTR_PRESENT_ENDLESS: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbEndless != bVal) + { + bValuesChanged = true; + rPresSettings.mbEndless = bVal; + } + } + break; + } + case ATTR_PRESENT_FULLSCREEN: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + if( rPresSettings.mbFullScreen != bVal) + { + bValuesChanged = true; + rPresSettings.mbFullScreen = bVal; + } + } + break; + } + case ATTR_PRESENT_DIANAME: + { + OUString aPresPage; + aValue >>= aPresPage; + bIllegalArgument = false; + if( (rPresSettings.maPresPage != aPresPage) || !rPresSettings.mbCustomShow || !rPresSettings.mbAll ) + { + bValuesChanged = true; + rPresSettings.maPresPage = getUiNameFromPageApiNameImpl(aPresPage); + rPresSettings.mbCustomShow = false; + rPresSettings.mbAll = false; + } + break; + } + case ATTR_PRESENT_MANUEL: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbManual != bVal) + { + bValuesChanged = true; + rPresSettings.mbManual = bVal; + } + } + break; + } + case ATTR_PRESENT_MOUSE: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + if( rPresSettings.mbMouseVisible != bVal) + { + bValuesChanged = true; + rPresSettings.mbMouseVisible = bVal; + } + } + break; + } + case ATTR_PRESENT_ALWAYS_ON_TOP: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbAlwaysOnTop != bVal) + { + bValuesChanged = true; + rPresSettings.mbAlwaysOnTop = bVal; + } + } + break; + } + case ATTR_PRESENT_NAVIGATOR: + bIllegalArgument = false; + //ignored, but exists in some older documents + break; + case ATTR_PRESENT_PEN: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if(rPresSettings.mbMouseAsPen != bVal) + { + bValuesChanged = true; + rPresSettings.mbMouseAsPen = bVal; + } + } + break; + } + case ATTR_PRESENT_PAUSE_TIMEOUT: + { + sal_Int32 nValue = 0; + if( (aValue >>= nValue) && (nValue >= 0) ) + { + bIllegalArgument = false; + if( rPresSettings.mnPauseTimeout != nValue ) + { + bValuesChanged = true; + rPresSettings.mnPauseTimeout = nValue; + } + } + break; + } + case ATTR_PRESENT_SHOW_PAUSELOGO: + { + bool bVal = false; + + if( aValue >>= bVal ) + { + bIllegalArgument = false; + + if( rPresSettings.mbShowPauseLogo != bVal ) + { + bValuesChanged = true; + rPresSettings.mbShowPauseLogo = bVal; + } + } + break; + } + case ATTR_PRESENT_DISPLAY: + { + sal_Int32 nDisplay = 0; + if( aValue >>= nDisplay ) + { + bIllegalArgument = false; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + pOptions->SetDisplay( nDisplay ); + + FullScreenWorkWindow *pWin = dynamic_cast(GetWorkWindow()); + if( !pWin ) + return; + pWin->Restart(true); + } + break; + } + + default: + throw UnknownPropertyException( OUString::number(pEntry ? pEntry->nWID : -1), static_cast(this)); + } + + if( bIllegalArgument ) + throw IllegalArgumentException(); + + if( bValuesChanged ) + mpDoc->SetChanged(); +} + +Any SAL_CALL SlideShow::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + const sd::PresentationSettings& rPresSettings = mpDoc->getPresentationSettings(); + + const SfxItemPropertyMapEntry* pEntry = maPropSet.getPropertyMapEntry(PropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case ATTR_PRESENT_ALL: + return Any( !rPresSettings.mbCustomShow && rPresSettings.mbAll ); + case ATTR_PRESENT_CHANGE_PAGE: + return Any( !rPresSettings.mbLockedPages ); + case ATTR_PRESENT_ANIMATION_ALLOWED: + return Any( rPresSettings.mbAnimationAllowed ); + case ATTR_PRESENT_CUSTOMSHOW: + { + SdCustomShowList* pList = mpDoc->GetCustomShowList(); + SdCustomShow* pShow = (pList && rPresSettings.mbCustomShow) ? pList->GetCurObject() : nullptr; + OUString aShowName; + + if(pShow) + aShowName = pShow->GetName(); + + return Any( aShowName ); + } + case ATTR_PRESENT_ENDLESS: + return Any( rPresSettings.mbEndless ); + case ATTR_PRESENT_FULLSCREEN: + return Any( rPresSettings.mbFullScreen ); + case ATTR_PRESENT_DIANAME: + { + OUString aSlideName; + + if( !rPresSettings.mbCustomShow && !rPresSettings.mbAll ) + aSlideName = getPageApiNameFromUiName( rPresSettings.maPresPage ); + + return Any( aSlideName ); + } + case ATTR_PRESENT_MANUEL: + return Any( rPresSettings.mbManual ); + case ATTR_PRESENT_MOUSE: + return Any( rPresSettings.mbMouseVisible ); + case ATTR_PRESENT_ALWAYS_ON_TOP: + return Any( rPresSettings.mbAlwaysOnTop ); + case ATTR_PRESENT_NAVIGATOR: + return Any( false ); + case ATTR_PRESENT_PEN: + return Any( rPresSettings.mbMouseAsPen ); + case ATTR_PRESENT_PAUSE_TIMEOUT: + return Any( rPresSettings.mnPauseTimeout ); + case ATTR_PRESENT_SHOW_PAUSELOGO: + return Any( rPresSettings.mbShowPauseLogo ); + case ATTR_PRESENT_DISPLAY: + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + return Any(pOptions->GetDisplay()); + } + + default: + throw UnknownPropertyException( OUString::number(pEntry ? pEntry->nWID : -1), static_cast(this)); + } +} + +void SAL_CALL SlideShow::addPropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::removePropertyChangeListener( const OUString& , const Reference< XPropertyChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::addVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) +{ +} + +void SAL_CALL SlideShow::removeVetoableChangeListener( const OUString& , const Reference< XVetoableChangeListener >& ) +{ +} + +// XPresentation + +void SAL_CALL SlideShow::start() +{ + const Sequence< PropertyValue > aArguments; + startWithArguments( aArguments ); +} + +WorkWindow *SlideShow::GetWorkWindow() +{ + if( !mpFullScreenViewShellBase ) + return nullptr; + + PresentationViewShell* pShell = dynamic_cast(mpFullScreenViewShellBase->GetMainViewShell().get()); + + if( !pShell || !pShell->GetViewFrame() ) + return nullptr; + + return dynamic_cast(pShell->GetViewFrame()->GetFrame().GetWindow().GetParent()); +} + +bool SlideShow::IsExitAfterPresenting() const +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + return mpDoc->IsExitAfterPresenting(); +} + +void SlideShow::SetExitAfterPresenting(bool bExit) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + mpDoc->SetExitAfterPresenting(bExit); +} + +void SAL_CALL SlideShow::end() +{ + SolarMutexGuard aGuard; + + // The mbIsInStartup flag should have been reset during the start of the + // slide show. Reset it here just in case that something has horribly + // gone wrong. + assert(!mbIsInStartup); + + rtl::Reference< SlideshowImpl > xController( mxController ); + if( !xController.is() ) + return; + + mxController.clear(); + + if( mpFullScreenFrameView ) + { + delete mpFullScreenFrameView; + mpFullScreenFrameView = nullptr; + } + + ViewShellBase* pFullScreenViewShellBase = mpFullScreenViewShellBase; + mpFullScreenViewShellBase = nullptr; + + // dispose before fullscreen window changes screens + // (potentially). If this needs to be moved behind + // pWorkWindow->StartPresentationMode() again, read issue + // pWorkWindow->i94007 & implement the solution outlined + // there. + xController->dispose(); + + if( pFullScreenViewShellBase ) + { + PresentationViewShell* pShell = dynamic_cast(pFullScreenViewShellBase->GetMainViewShell().get()); + + if( pShell && pShell->GetViewFrame() ) + { + WorkWindow* pWorkWindow = dynamic_cast(pShell->GetViewFrame()->GetFrame().GetWindow().GetParent()); + if( pWorkWindow ) + { + pWorkWindow->StartPresentationMode( (mxController.is() && mxController->maPresSettings.mbAlwaysOnTop) + ? PresentationFlags::HideAllApps : PresentationFlags::NONE ); + } + } + } + + if( pFullScreenViewShellBase ) + { + PresentationViewShell* pShell = nullptr; + { + // Get the shell pointer in its own scope to be sure that + // the shared_ptr to the shell is released before DoClose() + // is called. + ::std::shared_ptr pSharedView (pFullScreenViewShellBase->GetMainViewShell()); + pShell = dynamic_cast(pSharedView.get()); + } + if( pShell && pShell->GetViewFrame() ) + pShell->GetViewFrame()->DoClose(); + } + else if( mpCurrentViewShellBase ) + { + ViewShell* pViewShell = mpCurrentViewShellBase->GetMainViewShell().get(); + + if( pViewShell ) + { + FrameView* pFrameView = pViewShell->GetFrameView(); + + if( pFrameView && (pFrameView->GetPresentationViewShellId() != SID_VIEWSHELL0) ) + { + ViewShell::ShellType ePreviousType (pFrameView->GetPreviousViewShellType()); + pFrameView->SetPreviousViewShellType(ViewShell::ST_NONE); + + pFrameView->SetPresentationViewShellId(SID_VIEWSHELL0); + pFrameView->SetPreviousViewShellType(pViewShell->GetShellType()); + + framework::FrameworkHelper::Instance(*mpCurrentViewShellBase)->RequestView( + framework::FrameworkHelper::GetViewURL(ePreviousType), + framework::FrameworkHelper::msCenterPaneURL); + + pViewShell->GetViewFrame()->GetBindings().InvalidateAll( true ); + } + } + } + + if( mpCurrentViewShellBase ) + { + if (ViewShell* const pViewShell = mpCurrentViewShellBase->GetMainViewShell().get()) + { + // invalidate the view shell so the presentation slot will be re-enabled + // and the rehearsing will be updated + pViewShell->Invalidate(); + + if( xController->meAnimationMode ==ANIMATIONMODE_SHOW ) + { + // switch to the previously visible Slide + DrawViewShell* pDrawViewShell = dynamic_cast( pViewShell ); + if( pDrawViewShell ) + pDrawViewShell->SwitchPage( static_cast(xController->getRestoreSlide()) ); + else + { + Reference xDrawView ( + Reference(&mpCurrentViewShellBase->GetDrawController()), UNO_QUERY); + if (xDrawView.is()) + xDrawView->setCurrentPage( + Reference( + mpDoc->GetSdPage(xController->getRestoreSlide(), PageKind::Standard)->getUnoPage(), + UNO_QUERY)); + } + } + + if( pViewShell->GetDoc()->IsExitAfterPresenting() ) + { + pViewShell->GetDoc()->SetExitAfterPresenting( false ); + + Reference xProvider(pViewShell->GetViewShellBase().GetController()->getFrame(), + UNO_QUERY); + if( xProvider.is() ) + { + util::URL aURL; + aURL.Complete = ".uno:CloseFrame"; + + uno::Reference< frame::XDispatch > xDispatch( + xProvider->queryDispatch( + aURL, OUString(), 0)); + if( xDispatch.is() ) + { + xDispatch->dispatch(aURL, + uno::Sequence< beans::PropertyValue >()); + } + } + } + + // In case mbMouseAsPen was set, a new layer DrawnInSlideshow might have been generated + // during slideshow, which is not known to FrameView yet. + if (any2bool(getPropertyValue("UsePen")) + && pViewShell->GetDoc()->GetLayerAdmin().GetLayer("DrawnInSlideshow")) + { + SdrLayerIDSet aDocLayerIDSet; + pViewShell->GetDoc()->GetLayerAdmin().getVisibleLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetVisibleLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetVisibleLayers(aDocLayerIDSet); + } + pViewShell->GetDoc()->GetLayerAdmin().getPrintableLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetPrintableLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetPrintableLayers(aDocLayerIDSet); + } + pViewShell->GetDoc()->GetLayerAdmin().getLockedLayersODF(aDocLayerIDSet); + if (pViewShell->GetFrameView()->GetLockedLayers() != aDocLayerIDSet) + { + pViewShell->GetFrameView()->SetLockedLayers(aDocLayerIDSet); + } + pViewShell->InvalidateWindows(); + } + + // Fire the acc focus event when focus is switched back. The above method + // mpCurrentViewShellBase->GetWindow()->GrabFocus() will set focus to WorkWindow + // instead of the sd::window, so here call Shell's method to fire the focus event + pViewShell->SwitchActiveViewFireFocus(); + } + } + mpCurrentViewShellBase = nullptr; +} + +void SAL_CALL SlideShow::rehearseTimings() +{ + Sequence< PropertyValue > aArguments{ comphelper::makePropertyValue("RehearseTimings", true) }; + startWithArguments( aArguments ); +} + +// XPresentation2 + +void SAL_CALL SlideShow::startWithArguments(const Sequence< PropertyValue >& rArguments) +{ + SolarMutexGuard aGuard; + ThrowIfDisposed(); + + // Stop a running show before starting a new one. + if( mxController.is() ) + { + assert(!mbIsInStartup); + end(); + } + else if (mbIsInStartup) + { + // We are already somewhere in process of starting a slide show but + // have not yet got to the point where mxController is set. There + // is not yet a slide show to end so return silently. + return; + } + + // Prevent multiple instance of the SlideShow class for one document. + mbIsInStartup = true; + + mxCurrentSettings = std::make_shared( mpDoc->getPresentationSettings() ); + mxCurrentSettings->SetArguments( rArguments ); + + // if there is no view shell base set, use the current one or the first using this document + if( mpCurrentViewShellBase == nullptr ) + { + // first check current + ::sd::ViewShellBase* pBase = ::sd::ViewShellBase::GetViewShellBase( SfxViewFrame::Current() ); + if( pBase && pBase->GetDocument() == mpDoc ) + { + mpCurrentViewShellBase = pBase; + } + else + { + // current is not ours, so get first from ours + mpCurrentViewShellBase = ::sd::ViewShellBase::GetViewShellBase( SfxViewFrame::GetFirst( mpDoc->GetDocSh() ) ); + } + } + + // #i118456# make sure TextEdit changes get pushed to model. + // mpDrawView is tested against NULL above already. + if(mpCurrentViewShellBase) + { + ViewShell* pViewShell = mpCurrentViewShellBase->GetMainViewShell().get(); + + if(pViewShell && pViewShell->GetView()) + { + pViewShell->GetView()->SdrEndTextEdit(); + } + } + + // Start either a full-screen or an in-place show. + if(mxCurrentSettings->mbFullScreen && !mxCurrentSettings->mbPreview) + StartFullscreenPresentation(); + else + StartInPlacePresentation(); + +} + +sal_Bool SAL_CALL SlideShow::isRunning( ) +{ + SolarMutexGuard aGuard; + return mxController.is() && mxController->isRunning(); +} + +Reference< XSlideShowController > SAL_CALL SlideShow::getController( ) +{ + ThrowIfDisposed(); + + return mxController; +} + +// XComponent + +void SlideShow::disposing(std::unique_lock&) +{ + SolarMutexGuard aGuard; + + if( mnInPlaceConfigEvent ) + { + Application::RemoveUserEvent( mnInPlaceConfigEvent ); + mnInPlaceConfigEvent = nullptr; + } + + if( mxController.is() ) + { + mxController->dispose(); + mxController.clear(); + } + + mpCurrentViewShellBase = nullptr; + mpFullScreenViewShellBase = nullptr; + mpDoc = nullptr; +} + +void SlideShow::startPreview( const Reference< XDrawPage >& xDrawPage, const Reference< XAnimationNode >& xAnimationNode ) +{ + Sequence< PropertyValue > aArguments{ + comphelper::makePropertyValue("Preview", true), + comphelper::makePropertyValue("FirstPage", xDrawPage), + comphelper::makePropertyValue("AnimationNode", xAnimationNode), + comphelper::makePropertyValue("ParentWindow", Reference< XWindow >()), + }; + + startWithArguments( aArguments ); +} + +OutputDevice* SlideShow::getShowWindow() +{ + return mxController.is() ? mxController->mpShowWindow->GetOutDev() : nullptr; +} + +int SlideShow::getAnimationMode() const +{ + return mxController.is() ? mxController->meAnimationMode : ANIMATIONMODE_SHOW; +} + +void SlideShow::jumpToPageIndex( sal_Int32 nPageIndex ) +{ + if( mxController.is() ) + mxController->displaySlideIndex( nPageIndex ); +} + +void SlideShow::jumpToPageNumber( sal_Int32 nPageNumber ) +{ + if( mxController.is() ) + mxController->displaySlideNumber( nPageNumber ); +} + +sal_Int32 SlideShow::getCurrentPageNumber() const +{ + return mxController.is() ? mxController->getCurrentSlideNumber() : 0; +} + +void SlideShow::jumpToBookmark( const OUString& sBookmark ) +{ + if( mxController.is() ) + mxController->jumpToBookmark( sBookmark ); +} + +bool SlideShow::isFullScreen() const +{ + return mxController.is() && mxController->maPresSettings.mbFullScreen; +} + +void SlideShow::resize( const Size &rSize ) +{ + if( mxController.is() ) + mxController->resize( rSize ); +} + +bool SlideShow::activate( ViewShellBase& rBase ) +{ + if( (mpFullScreenViewShellBase == &rBase) && !mxController.is() ) + { + ::std::shared_ptr pShell = std::dynamic_pointer_cast(rBase.GetMainViewShell()); + if (pShell != nullptr) + { + pShell->FinishInitialization( mpFullScreenFrameView ); + mpFullScreenFrameView = nullptr; + + CreateController( pShell.get(), pShell->GetView(), rBase.GetViewWindow() ); + + if (!mxController->startShow(mxCurrentSettings.get())) + return false; + + pShell->Resize(); + // Defer the sd::ShowWindow's GrabFocus to here. so that the accessible event can be fired correctly. + pShell->GetActiveWindow()->GrabFocus(); + } + } + + if( mxController.is() ) + mxController->activate(); + + return true; +} + +void SlideShow::deactivate() +{ + mxController->deactivate(); +} + +bool SlideShow::keyInput(const KeyEvent& rKEvt) +{ + return mxController.is() && mxController->keyInput(rKEvt); +} + +void SlideShow::paint() +{ + if( mxController.is() ) + mxController->paint(); +} + +void SlideShow::pause( bool bPause ) +{ + if( mxController.is() ) + { + if( bPause ) + mxController->pause(); + else + mxController->resume(); + } +} + +bool SlideShow::swipe(const CommandSwipeData& rSwipeData) +{ + return mxController.is() && mxController->swipe(rSwipeData); +} + +bool SlideShow::longpress(const CommandLongPressData& rLongPressData) +{ + return mxController.is() && mxController->longpress(rLongPressData); +} + +void SlideShow::StartInPlacePresentationConfigurationCallback() +{ + if( mnInPlaceConfigEvent != nullptr ) + Application::RemoveUserEvent( mnInPlaceConfigEvent ); + + mnInPlaceConfigEvent = Application::PostUserEvent( LINK( this, SlideShow, StartInPlacePresentationConfigurationHdl ) ); +} + +IMPL_LINK_NOARG(SlideShow, StartInPlacePresentationConfigurationHdl, void*, void) +{ + mnInPlaceConfigEvent = nullptr; + StartInPlacePresentation(); +} + +void SlideShow::StartInPlacePresentation() +{ + if( mpCurrentViewShellBase ) + { + // Save the current view shell type so that it can be restored after the + // show has ended. If there already is a saved shell type then that is + // not overwritten. + + ViewShell::ShellType eShell = ViewShell::ST_NONE; + + ::std::shared_ptr pHelper(FrameworkHelper::Instance(*mpCurrentViewShellBase)); + ::std::shared_ptr pMainViewShell(pHelper->GetViewShell(FrameworkHelper::msCenterPaneURL)); + + if( pMainViewShell ) + eShell = pMainViewShell->GetShellType(); + + if( eShell != ViewShell::ST_IMPRESS ) + { + // Switch temporary to a DrawViewShell which supports the in-place presentation. + + if( pMainViewShell ) + { + FrameView* pFrameView = pMainViewShell->GetFrameView(); + pFrameView->SetPresentationViewShellId(SID_VIEWSHELL1); + pFrameView->SetPreviousViewShellType (pMainViewShell->GetShellType()); + pFrameView->SetPageKind (PageKind::Standard); + } + + pHelper->RequestView( FrameworkHelper::msImpressViewURL, FrameworkHelper::msCenterPaneURL ); + pHelper->RunOnConfigurationEvent( + FrameworkHelper::msConfigurationUpdateEndEvent, + [this] (bool const) { return this->StartInPlacePresentationConfigurationCallback(); } ); + return; + } + else + { + vcl::Window* pParentWindow = mxCurrentSettings->mpParentWindow; + if( pParentWindow == nullptr ) + pParentWindow = mpCurrentViewShellBase->GetViewWindow(); + + CreateController( pMainViewShell.get(), pMainViewShell->GetView(), pParentWindow ); + } + } + else if( mxCurrentSettings->mpParentWindow ) + { + // no current view shell, but parent window + CreateController( nullptr, nullptr, mxCurrentSettings->mpParentWindow ); + } + + if( !mxController.is() ) + return; + + bool bSuccess = false; + if( mxCurrentSettings && mxCurrentSettings->mbPreview ) + { + bSuccess = mxController->startPreview(mxCurrentSettings->mxStartPage, mxCurrentSettings->mxAnimationNode, mxCurrentSettings->mpParentWindow ); + } + else + { + bSuccess = mxController->startShow(mxCurrentSettings.get()); + } + + if( !bSuccess ) + end(); + else + { + if( mpCurrentViewShellBase && ( !mxCurrentSettings || ( mxCurrentSettings && !mxCurrentSettings->mbPreview ) ) ) + mpCurrentViewShellBase->GetWindow()->GrabFocus(); + } +} + +void SlideShow::StartFullscreenPresentation( ) +{ + // Create the top level window in which the PresentationViewShell(Base) + // will be created. This is done here explicitly so that we can make it + // fullscreen. + const sal_Int32 nDisplay (GetDisplay()); + VclPtr pWorkWindow = VclPtr::Create(this, mpCurrentViewShellBase); + pWorkWindow->SetBackground(Wallpaper(COL_BLACK)); + OUString Title(SdResId(STR_FULLSCREEN_SLIDESHOW)); + Title = Title.replaceFirst("%s", + mpCurrentViewShellBase->GetDocShell()->GetTitle(SFX_TITLE_DETECT)); + pWorkWindow->SetText(Title); + pWorkWindow->StartPresentationMode( true, mpDoc->getPresentationSettings().mbAlwaysOnTop ? PresentationFlags::HideAllApps : PresentationFlags::NONE, nDisplay); + // pWorkWindow->ShowFullScreenMode(sal_False, nDisplay); + + if (!pWorkWindow->IsVisible()) + return; + + // Initialize the new presentation view shell with a copy of the + // frame view of the current view shell. This avoids that + // changes made by the presentation have an effect on the other + // view shells. + FrameView* pOriginalFrameView = nullptr; + ::std::shared_ptr xShell(mpCurrentViewShellBase->GetMainViewShell()); + if (xShell) + pOriginalFrameView = xShell->GetFrameView(); + + delete mpFullScreenFrameView; + mpFullScreenFrameView = new FrameView(mpDoc, pOriginalFrameView); + + // The new frame is created hidden. To make it visible and activate the + // new view shell--a prerequisite to process slot calls and initialize + // its panes--a GrabFocus() has to be called later on. + SfxFrame* pNewFrame = SfxFrame::CreateHidden( *mpDoc->GetDocSh(), *pWorkWindow, PRESENTATION_FACTORY_ID ); + pNewFrame->SetPresentationMode(true); + + mpFullScreenViewShellBase = static_cast(pNewFrame->GetCurrentViewFrame()->GetViewShell()); + if(mpFullScreenViewShellBase != nullptr) + { + // The following GrabFocus() is responsible for activating the + // new view shell. Without it the screen remains blank (under + // Windows and some Linux variants.) + mpFullScreenViewShellBase->GetWindow()->GrabFocus(); + } +} + +/// convert configuration setting display concept to real screens +sal_Int32 SlideShow::GetDisplay() +{ + sal_Int32 nDisplay = 0; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + if( pOptions ) + nDisplay = pOptions->GetDisplay(); + + if( nDisplay < 0 ) + nDisplay = -1; + else if( nDisplay == 0) + nDisplay = static_cast(Application::GetDisplayExternalScreen()); + else + nDisplay--; + + SAL_INFO("sd", "Presenting on real screen " << nDisplay); + + return nDisplay; +} + +bool SlideShow::dependsOn( ViewShellBase const * pViewShellBase ) +{ + return mxController.is() && (pViewShellBase == mpCurrentViewShellBase) && mpFullScreenViewShellBase; +} + +Reference< presentation::XPresentation2 > CreatePresentation( const SdDrawDocument& rDocument ) +{ + return Reference< presentation::XPresentation2 >( SlideShow::Create( const_cast< SdDrawDocument* >( &rDocument ) ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowimpl.cxx b/sd/source/ui/slideshow/slideshowimpl.cxx new file mode 100644 index 000000000..89a8ac95f --- /dev/null +++ b/sd/source/ui/slideshow/slideshowimpl.cxx @@ -0,0 +1,3349 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "slideshowimpl.hxx" +#include "slideshowviewimpl.hxx" +#include "PaneHider.hxx" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define CM_SLIDES 21 + +using ::com::sun::star::animations::XAnimationNode; +using ::com::sun::star::animations::XAnimationListener; +using ::com::sun::star::awt::XWindow; +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::beans; + +namespace sd +{ +/** Slots, which will be disabled in the slide show and are managed by Sfx. + Have to be sorted in the order of the SIDs */ +sal_uInt16 const pAllowed[] = +{ + SID_OPENDOC , // 5501 ///< that internally jumps work + SID_JUMPTOMARK , // 5598 + SID_OPENHYPERLINK , // 6676 + SID_PRESENTATION_END // 27218 +}; + +class AnimationSlideController +{ +public: + enum Mode { ALL, FROM, CUSTOM, PREVIEW }; + +public: + AnimationSlideController( Reference< XIndexAccess > const & xSlides, Mode eMode ); + + void setStartSlideNumber( sal_Int32 nSlideNumber ) { mnStartSlideNumber = nSlideNumber; } + sal_Int32 getStartSlideIndex() const; + + sal_Int32 getCurrentSlideNumber() const; + sal_Int32 getCurrentSlideIndex() const; + + sal_Int32 getSlideIndexCount() const { return maSlideNumbers.size(); } + sal_Int32 getSlideNumberCount() const { return mnSlideCount; } + + sal_Int32 getSlideNumber( sal_Int32 nSlideIndex ) const; + + void insertSlideNumber( sal_Int32 nSlideNumber, bool bVisible = true ); + void setPreviewNode( const Reference< XAnimationNode >& xPreviewNode ); + + bool jumpToSlideIndex( sal_Int32 nNewSlideIndex ); + bool jumpToSlideNumber( sal_Int32 nNewSlideIndex ); + + bool nextSlide(); + bool previousSlide(); + + void displayCurrentSlide( const Reference< XSlideShow >& xShow, + const Reference< XDrawPagesSupplier>& xDrawPages, + const bool bSkipAllMainSequenceEffects ); + + sal_Int32 getNextSlideIndex() const; + sal_Int32 getPreviousSlideIndex() const; + + bool isVisibleSlideNumber( sal_Int32 nSlideNumber ) const; + + Reference< XDrawPage > getSlideByNumber( sal_Int32 nSlideNumber ) const; + + sal_Int32 getNextSlideNumber() const; + + bool hasSlides() const { return !maSlideNumbers.empty(); } + +private: + bool getSlideAPI( sal_Int32 nSlideNumber, Reference< XDrawPage >& xSlide, Reference< XAnimationNode >& xAnimNode ); + sal_Int32 findSlideIndex( sal_Int32 nSlideNumber ) const; + + bool isValidIndex( sal_Int32 nIndex ) const { return (nIndex >= 0) && (o3tl::make_unsigned(nIndex) < maSlideNumbers.size()); } + bool isValidSlideNumber( sal_Int32 nSlideNumber ) const { return (nSlideNumber >= 0) && (nSlideNumber < mnSlideCount); } + +private: + Mode meMode; + sal_Int32 mnStartSlideNumber; + std::vector< sal_Int32 > maSlideNumbers; + std::vector< bool > maSlideVisible; + std::vector< bool > maSlideVisited; + Reference< XAnimationNode > mxPreviewNode; + sal_Int32 mnSlideCount; + sal_Int32 mnCurrentSlideIndex; + sal_Int32 mnHiddenSlideNumber; + Reference< XIndexAccess > mxSlides; +}; + +Reference< XDrawPage > AnimationSlideController::getSlideByNumber( sal_Int32 nSlideNumber ) const +{ + Reference< XDrawPage > xSlide; + if( mxSlides.is() && (nSlideNumber >= 0) && (nSlideNumber < mxSlides->getCount()) ) + mxSlides->getByIndex( nSlideNumber ) >>= xSlide; + return xSlide; +} + +bool AnimationSlideController::isVisibleSlideNumber( sal_Int32 nSlideNumber ) const +{ + sal_Int32 nIndex = findSlideIndex( nSlideNumber ); + + if( nIndex != -1 ) + return maSlideVisible[ nIndex ]; + else + return false; +} + +void AnimationSlideController::setPreviewNode( const Reference< XAnimationNode >& xPreviewNode ) +{ + mxPreviewNode = xPreviewNode; +} + +AnimationSlideController::AnimationSlideController( Reference< XIndexAccess > const & xSlides, Mode eMode ) +: meMode( eMode ) +, mnStartSlideNumber(-1) +, mnSlideCount( 0 ) +, mnCurrentSlideIndex(0) +, mnHiddenSlideNumber( -1 ) +, mxSlides( xSlides ) +{ + if( mxSlides.is() ) + mnSlideCount = xSlides->getCount(); +} + +sal_Int32 AnimationSlideController::getStartSlideIndex() const +{ + if( mnStartSlideNumber >= 0 ) + { + sal_Int32 nIndex; + const sal_Int32 nCount = maSlideNumbers.size(); + + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + if( maSlideNumbers[nIndex] == mnStartSlideNumber ) + return nIndex; + } + } + + return 0; +} + +sal_Int32 AnimationSlideController::getCurrentSlideNumber() const +{ + if( mnHiddenSlideNumber != -1 ) + return mnHiddenSlideNumber; + else if( !maSlideNumbers.empty() ) + return maSlideNumbers[mnCurrentSlideIndex]; + else + return 0; +} + +sal_Int32 AnimationSlideController::getCurrentSlideIndex() const +{ + if( mnHiddenSlideNumber != -1 ) + return -1; + else + return mnCurrentSlideIndex; +} + +bool AnimationSlideController::jumpToSlideIndex( sal_Int32 nNewSlideIndex ) +{ + if( isValidIndex( nNewSlideIndex ) ) + { + mnCurrentSlideIndex = nNewSlideIndex; + mnHiddenSlideNumber = -1; + maSlideVisited[mnCurrentSlideIndex] = true; + return true; + } + else + { + return false; + } +} + +bool AnimationSlideController::jumpToSlideNumber( sal_Int32 nNewSlideNumber ) +{ + sal_Int32 nIndex = findSlideIndex( nNewSlideNumber ); + if( isValidIndex( nIndex ) ) + { + return jumpToSlideIndex( nIndex ); + } + else if( (nNewSlideNumber >= 0) && (nNewSlideNumber < mnSlideCount) ) + { + // jump to a hidden slide + mnHiddenSlideNumber = nNewSlideNumber; + return true; + } + else + { + return false; + } +} + +sal_Int32 AnimationSlideController::getSlideNumber( sal_Int32 nSlideIndex ) const +{ + if( isValidIndex( nSlideIndex ) ) + return maSlideNumbers[nSlideIndex]; + else + return -1; +} + +void AnimationSlideController::insertSlideNumber( sal_Int32 nSlideNumber, bool bVisible /* = true */ ) +{ + DBG_ASSERT( isValidSlideNumber( nSlideNumber ), "sd::AnimationSlideController::insertSlideNumber(), illegal index" ); + if( isValidSlideNumber( nSlideNumber ) ) + { + maSlideNumbers.push_back( nSlideNumber ); + maSlideVisible.push_back( bVisible ); + maSlideVisited.push_back( false ); + } +} + +bool AnimationSlideController::getSlideAPI( sal_Int32 nSlideNumber, Reference< XDrawPage >& xSlide, Reference< XAnimationNode >& xAnimNode ) +{ + if( isValidSlideNumber( nSlideNumber ) ) try + { + xSlide.set( mxSlides->getByIndex(nSlideNumber), UNO_QUERY_THROW ); + + if( meMode == PREVIEW ) + { + xAnimNode = mxPreviewNode; + } + else + { + Reference< animations::XAnimationNodeSupplier > xAnimNodeSupplier( xSlide, UNO_QUERY_THROW ); + xAnimNode = xAnimNodeSupplier->getAnimationNode(); + } + + return true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::AnimationSlideController::getSlideAPI()" ); + } + + return false; +} + +sal_Int32 AnimationSlideController::findSlideIndex( sal_Int32 nSlideNumber ) const +{ + sal_Int32 nIndex; + const sal_Int32 nCount = maSlideNumbers.size(); + + for( nIndex = 0; nIndex < nCount; nIndex++ ) + { + if( maSlideNumbers[nIndex] == nSlideNumber ) + return nIndex; + } + + return -1; +} + +sal_Int32 AnimationSlideController::getNextSlideIndex() const +{ + switch( meMode ) + { + case ALL: + { + sal_Int32 nNewSlideIndex = mnCurrentSlideIndex + 1; + if( isValidIndex( nNewSlideIndex ) ) + { + // if the current slide is not excluded, make sure the + // next slide is also not excluded. + // if the current slide is excluded, we want to go + // to the next slide, even if this is also excluded. + if( maSlideVisible[mnCurrentSlideIndex] ) + { + while( isValidIndex( nNewSlideIndex ) ) + { + if( maSlideVisible[nNewSlideIndex] ) + break; + + nNewSlideIndex++; + } + } + } + return isValidIndex( nNewSlideIndex ) ? nNewSlideIndex : -1; + } + + case FROM: + case CUSTOM: + return mnHiddenSlideNumber == -1 ? mnCurrentSlideIndex + 1 : mnCurrentSlideIndex; + + default: + case PREVIEW: + return -1; + + } +} + +sal_Int32 AnimationSlideController::getNextSlideNumber() const +{ + sal_Int32 nNextSlideIndex = getNextSlideIndex(); + if( isValidIndex( nNextSlideIndex ) ) + { + return maSlideNumbers[nNextSlideIndex]; + } + else + { + return -1; + } +} + +bool AnimationSlideController::nextSlide() +{ + return jumpToSlideIndex( getNextSlideIndex() ); +} + +sal_Int32 AnimationSlideController::getPreviousSlideIndex() const +{ + sal_Int32 nNewSlideIndex = mnCurrentSlideIndex - 1; + + switch( meMode ) + { + case ALL: + { + // make sure the previous slide is visible + // or was already visited + while( isValidIndex( nNewSlideIndex ) ) + { + if( maSlideVisible[nNewSlideIndex] || maSlideVisited[nNewSlideIndex] ) + break; + + nNewSlideIndex--; + } + + break; + } + + case PREVIEW: + return -1; + + default: + break; + } + + return nNewSlideIndex; +} + +bool AnimationSlideController::previousSlide() +{ + return jumpToSlideIndex( getPreviousSlideIndex() ); +} + +void AnimationSlideController::displayCurrentSlide( const Reference< XSlideShow >& xShow, + const Reference< XDrawPagesSupplier>& xDrawPages, + const bool bSkipAllMainSequenceEffects ) +{ + const sal_Int32 nCurrentSlideNumber = getCurrentSlideNumber(); + + if( !(xShow.is() && (nCurrentSlideNumber != -1 )) ) + return; + + Reference< XDrawPage > xSlide; + Reference< XAnimationNode > xAnimNode; + ::std::vector aProperties; + + const sal_Int32 nNextSlideNumber = getNextSlideNumber(); + if( getSlideAPI( nNextSlideNumber, xSlide, xAnimNode ) ) + { + Sequence< Any > aValue{ Any(xSlide), Any(xAnimNode) }; + aProperties.emplace_back( "Prefetch" , + -1, + Any(aValue), + PropertyState_DIRECT_VALUE); + } + if (bSkipAllMainSequenceEffects) + { + // Add one property that prevents the slide transition from being + // shown (to speed up the transition to the previous slide) and + // one to show all main sequence effects so that the user can + // continue to undo effects. + aProperties.emplace_back( "SkipAllMainSequenceEffects", + -1, + Any(true), + PropertyState_DIRECT_VALUE); + aProperties.emplace_back("SkipSlideTransition", + -1, + Any(true), + PropertyState_DIRECT_VALUE); + } + + if( getSlideAPI( nCurrentSlideNumber, xSlide, xAnimNode ) ) + xShow->displaySlide( xSlide, xDrawPages, xAnimNode, comphelper::containerToSequence(aProperties) ); +} + +constexpr OUStringLiteral gsOnClick( u"OnClick" ); +constexpr OUStringLiteral gsBookmark( u"Bookmark" ); +constexpr OUStringLiteral gsVerb( u"Verb" ); + +SlideshowImpl::SlideshowImpl( const Reference< XPresentation2 >& xPresentation, ViewShell* pViewSh, ::sd::View* pView, SdDrawDocument* pDoc, vcl::Window* pParentWindow ) +: mxModel(pDoc->getUnoModel(),UNO_QUERY_THROW) +, maUpdateTimer("SlideShowImpl maUpdateTimer") +, maInputFreezeTimer("SlideShowImpl maInputFreezeTimer") +, maDeactivateTimer("SlideShowImpl maDeactivateTimer") +, mpView(pView) +, mpViewShell(pViewSh) +, mpDocSh(pDoc->GetDocSh()) +, mpDoc(pDoc) +, mpParentWindow(pParentWindow) +, mpShowWindow(nullptr) +, mnRestoreSlide(0) +, maPresSize( -1, -1 ) +, meAnimationMode(ANIMATIONMODE_SHOW) +, mpOldActiveWindow(nullptr) +, mnChildMask( 0 ) +, mbDisposed(false) +, mbAutoSaveWasOn(false) +, mbRehearseTimings(false) +, mbIsPaused(false) +, mbWasPaused(false) +, mbInputFreeze(false) +, mbActive(false) +, maPresSettings( pDoc->getPresentationSettings() ) +, mnUserPaintColor( 0x80ff0000L ) +, mbUsePen(false) +, mdUserPaintStrokeWidth ( 150.0 ) +, mnEndShowEvent(nullptr) +, mnContextMenuEvent(nullptr) +, mxPresentation( xPresentation ) +{ + if( mpViewShell ) + mpOldActiveWindow = mpViewShell->GetActiveWindow(); + + maUpdateTimer.SetInvokeHandler(LINK(this, SlideshowImpl, updateHdl)); + // Priority must not be too high or we'll starve input handling etc. + maUpdateTimer.SetPriority(TaskPriority::REPAINT); + + maDeactivateTimer.SetInvokeHandler(LINK(this, SlideshowImpl, deactivateHdl)); + maDeactivateTimer.SetTimeout( 20 ); + + maInputFreezeTimer.SetInvokeHandler( LINK( this, SlideshowImpl, ReadyForNextInputHdl ) ); + maInputFreezeTimer.SetTimeout( 20 ); + + // no autosave during show + if( officecfg::Office::Common::Save::Document::AutoSave::get() ) + mbAutoSaveWasOn = true; + + Application::AddEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) ); + + mbUsePen = maPresSettings.mbMouseAsPen; + + SdOptions* pOptions = SD_MOD()->GetSdOptions(DocumentType::Impress); + if( pOptions ) + { + mnUserPaintColor = pOptions->GetPresentationPenColor(); + mdUserPaintStrokeWidth = pOptions->GetPresentationPenWidth(); + } +} + +SlideshowImpl::~SlideshowImpl() +{ + SdModule *pModule = SD_MOD(); + //rhbz#806663 SlideshowImpl can outlive SdModule + SdOptions* pOptions = pModule ? + pModule->GetSdOptions(DocumentType::Impress) : nullptr; + if( pOptions ) + { + pOptions->SetPresentationPenColor(mnUserPaintColor); + pOptions->SetPresentationPenWidth(mdUserPaintStrokeWidth); + } + + Application::RemoveEventListener( LINK( this, SlideshowImpl, EventListenerHdl ) ); + + maDeactivateTimer.Stop(); + + if( !mbDisposed ) + { + OSL_FAIL("SlideshowImpl::~SlideshowImpl(), component was not disposed!"); + std::unique_lock g(m_aMutex); + disposing(g); + } +} + +void SlideshowImpl::disposing(std::unique_lock&) +{ +#ifdef ENABLE_SDREMOTE + RemoteServer::presentationStopped(); +#endif + if( mxShow.is() && mpDoc ) + NotifyDocumentEvent( + *mpDoc, + "OnEndPresentation" ); + + if( mbAutoSaveWasOn ) + setAutoSaveState( true ); + + if( mnEndShowEvent ) + Application::RemoveUserEvent( mnEndShowEvent ); + if( mnContextMenuEvent ) + Application::RemoveUserEvent( mnContextMenuEvent ); + + maInputFreezeTimer.Stop(); + + SolarMutexGuard aSolarGuard; + + if( !mxShow.is() ) + return; + + if( mxPresentation.is() ) + mxPresentation->end(); + + maUpdateTimer.Stop(); + + removeShapeEvents(); + + if( mxListenerProxy.is() ) + mxListenerProxy->removeAsSlideShowListener(); + + try + { + if( mxView.is() ) + mxShow->removeView( mxView ); + + Reference< XComponent > xComponent( mxShow, UNO_QUERY ); + if( xComponent.is() ) + xComponent->dispose(); + + if( mxView.is() ) + mxView->dispose(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::stop()" ); + } + + mxShow.clear(); + mxView.clear(); + mxListenerProxy.clear(); + mpSlideController.reset(); + + // take DrawView from presentation window, but give the old window back + if( mpShowWindow && mpView ) + mpView->DeleteWindowFromPaintView( mpShowWindow->GetOutDev() ); + + if( mpView ) + mpView->SetAnimationPause( false ); + + if( mpViewShell ) + { + mpViewShell->SetActiveWindow(mpOldActiveWindow); + if (mpShowWindow) + mpShowWindow->SetViewShell( nullptr ); + } + + if( mpView ) + mpView->InvalidateAllWin(); + + if( maPresSettings.mbFullScreen ) + { +#if HAVE_FEATURE_SCRIPTING + // restore StarBASICErrorHdl + StarBASIC::SetGlobalErrorHdl(maStarBASICGlobalErrorHdl); + maStarBASICGlobalErrorHdl = Link(); +#endif + } + else + { + if( mpShowWindow ) + mpShowWindow->Hide(); + } + + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mpDocSh->SetSlotFilter(); + mpDocSh->ApplySlotFilter(); + + Help::EnableContextHelp(); + Help::EnableExtHelp(); + + showChildWindows(); + mnChildMask = 0; + } + + // show current window again + if( mpViewShell && dynamic_cast< PresentationViewShell *>( mpViewShell ) == nullptr) + { + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mpViewShell->GetViewShellBase().ShowUIControls (true); + mpPaneHider.reset(); + } + else if( meAnimationMode == ANIMATIONMODE_PREVIEW ) + { + mpViewShell->ShowUIControls(true); + } + } + + if( mpShowWindow ) + mpShowWindow->Hide(); + mpShowWindow.disposeAndClear(); + + if ( mpViewShell ) + { + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + ::sd::Window* pActWin = mpViewShell->GetActiveWindow(); + + if (pActWin) + { + Size aVisSizePixel = pActWin->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = pActWin->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + mpViewShell->VisAreaChanged(aVisAreaWin); + if (mpView) + mpView->VisAreaChanged(pActWin->GetOutDev()); + pActWin->GrabFocus(); + } + } + + // restart the custom show dialog if he started us + if( mpViewShell->IsStartShowWithDialog() && getDispatcher() ) + { + mpViewShell->SetStartShowWithDialog( false ); + getDispatcher()->Execute( SID_CUSTOMSHOW_DLG, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } + + mpViewShell->GetViewShellBase().UpdateBorder(true); + } + + if( mpShowWindow ) + { + mpShowWindow.disposeAndClear(); + } + + setActiveXToolbarsVisible( true ); + + mbDisposed = true; +} + +bool SlideshowImpl::startPreview( + const Reference< XDrawPage >& xDrawPage, + const Reference< XAnimationNode >& xAnimationNode, + vcl::Window * pParent ) +{ + bool bRet = false; + + try + { + const Reference xServiceInfo( xDrawPage, UNO_QUERY ); + if (xServiceInfo.is()) { + const Sequence supportedServices( + xServiceInfo->getSupportedServiceNames() ); + if (comphelper::findValue(supportedServices, "com.sun.star.drawing.MasterPage") != -1) { + OSL_FAIL("sd::SlideshowImpl::startPreview() " + "not allowed on master page!"); + return false; + } + } + + mxPreviewDrawPage = xDrawPage; + mxPreviewAnimationNode = xAnimationNode; + meAnimationMode = ANIMATIONMODE_PREVIEW; + + maPresSettings.mbAll = false; + maPresSettings.mbEndless = false; + maPresSettings.mbCustomShow = false; + maPresSettings.mbManual = false; + maPresSettings.mbMouseVisible = false; + maPresSettings.mbMouseAsPen = false; + maPresSettings.mbLockedPages = false; + maPresSettings.mbAlwaysOnTop = false; + maPresSettings.mbFullScreen = false; + maPresSettings.mbAnimationAllowed = true; + maPresSettings.mnPauseTimeout = 0; + maPresSettings.mbShowPauseLogo = false; + + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + mpSlideController = std::make_shared( xSlides, AnimationSlideController::PREVIEW ); + + sal_Int32 nSlideNumber = 0; + Reference< XPropertySet > xSet( mxPreviewDrawPage, UNO_QUERY_THROW ); + xSet->getPropertyValue( "Number" ) >>= nSlideNumber; + mpSlideController->insertSlideNumber( nSlideNumber-1 ); + mpSlideController->setPreviewNode( xAnimationNode ); + + mpShowWindow = VclPtr::Create( this, ((pParent == nullptr) && mpViewShell) ? mpParentWindow.get() : pParent ); + if( mpViewShell ) + { + mpViewShell->SetActiveWindow( mpShowWindow ); + mpShowWindow->SetViewShell (mpViewShell); + mpViewShell->ShowUIControls (false); + } + + if( mpView ) + { + mpView->AddWindowToPaintView( mpShowWindow->GetOutDev(), nullptr ); + mpView->SetAnimationPause( true ); + } + + // call resize handler + if( pParent ) + { + maPresSize = pParent->GetSizePixel(); + } + else if( mpViewShell ) + { + ::tools::Rectangle aContentRect (mpViewShell->GetViewShellBase().getClientRectangle()); + if (AllSettings::GetLayoutRTL()) + { + aContentRect.SetLeft( aContentRect.Right() ); + aContentRect.AdjustRight(aContentRect.Right() ); + } + maPresSize = aContentRect.GetSize(); + mpShowWindow->SetPosPixel( aContentRect.TopLeft() ); + } + else + { + OSL_FAIL("sd::SlideshowImpl::startPreview(), I need either a parent window or a viewshell!"); + } + resize( maPresSize ); + + sal_Int32 nPropertyCount = 1; + if( mxPreviewAnimationNode.is() ) + nPropertyCount++; + + Sequence< beans::PropertyValue > aProperties(nPropertyCount); + auto pProperties = aProperties.getArray(); + pProperties[0].Name = "AutomaticAdvancement"; + pProperties[0].Value <<= 1.0; // one second timeout + + if( mxPreviewAnimationNode.is() ) + { + pProperties[1].Name = "NoSlideTransitions"; + pProperties[1].Value <<= true; + } + + bRet = startShowImpl( aProperties ); + + if( mpShowWindow != nullptr && meAnimationMode == ANIMATIONMODE_PREVIEW ) + mpShowWindow->SetPreviewMode(); + + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startPreview()" ); + bRet = false; + } + + return bRet; +} + +bool SlideshowImpl::startShow( PresentationSettingsEx const * pPresSettings ) +{ + const rtl::Reference xKeepAlive(this); + + DBG_ASSERT( !mxShow.is(), "sd::SlideshowImpl::startShow(), called twice!" ); + if( mxShow.is() ) + return true; + DBG_ASSERT( mpParentWindow!=nullptr, "sd::SlideshowImpl::startShow() called without parent window" ); + if (mpParentWindow == nullptr) + return false; + + // Autoplay (pps/ppsx) + if (mpViewShell->GetDoc()->IsStartWithPresentation()){ + mpViewShell->GetDoc()->SetExitAfterPresenting(true); + } + + bool bRet = false; + + try + { + if( pPresSettings ) + { + maPresSettings = *pPresSettings; + mbRehearseTimings = pPresSettings->mbRehearseTimings; + } + + OUString aPresSlide( maPresSettings.maPresPage ); + SdPage* pStartPage = mpViewShell->GetActualPage(); + bool bStartWithActualSlide = pStartPage; + + // times should be measured? + if( mbRehearseTimings ) + { + maPresSettings.mbEndless = false; + maPresSettings.mbManual = true; + maPresSettings.mbMouseVisible = true; + maPresSettings.mbMouseAsPen = false; + maPresSettings.mnPauseTimeout = 0; + maPresSettings.mbShowPauseLogo = false; + } + + if( pStartPage ) + { + if( pStartPage->GetPageKind() == PageKind::Notes ) + { + // we are in notes page mode, so get + // the corresponding draw page + const sal_uInt16 nPgNum = ( pStartPage->GetPageNum() - 2 ) >> 1; + pStartPage = mpDoc->GetSdPage( nPgNum, PageKind::Standard ); + } + } + + if( bStartWithActualSlide ) + { + if ( aPresSlide.isEmpty()) + { + // no preset slide yet, so pick current on one + aPresSlide = pStartPage->GetName(); + // if the starting slide is hidden, we can't set slide controller to ALL mode + maPresSettings.mbAll = !pStartPage->IsExcluded(); + } + + if( meAnimationMode != ANIMATIONMODE_SHOW ) + { + if( pStartPage->GetPageKind() == PageKind::Standard ) + { + maPresSettings.mbAll = false; + } + } + } + + // build page list + createSlideList( maPresSettings.mbAll, aPresSlide ); + + // remember Slide number from where the show was started + if( pStartPage ) + mnRestoreSlide = ( pStartPage->GetPageNum() - 1 ) / 2; + + if( mpSlideController->hasSlides() ) + { + // hide child windows + hideChildWindows(); + + mpShowWindow = VclPtr::Create( this, mpParentWindow ); + mpShowWindow->SetMouseAutoHide( !maPresSettings.mbMouseVisible ); + mpViewShell->SetActiveWindow( mpShowWindow ); + mpShowWindow->SetViewShell (mpViewShell); + mpViewShell->GetViewShellBase().ShowUIControls (false); + // Hide the side panes for in-place presentations. + if ( ! maPresSettings.mbFullScreen) + mpPaneHider.reset(new PaneHider(*mpViewShell,this)); + + // these Slots are forbidden in other views for this document + if( mpDocSh ) + { + mpDocSh->SetSlotFilter( true, pAllowed ); + mpDocSh->ApplySlotFilter(); + } + + Help::DisableContextHelp(); + Help::DisableExtHelp(); + + if( maPresSettings.mbFullScreen ) + { +#if HAVE_FEATURE_SCRIPTING + // disable basic ide error handling + maStarBASICGlobalErrorHdl = StarBASIC::GetGlobalErrorHdl(); + StarBASIC::SetGlobalErrorHdl( Link() ); +#endif + } + + // call resize handler + maPresSize = mpParentWindow->GetSizePixel(); + if (!maPresSettings.mbFullScreen) + { + const ::tools::Rectangle& aClientRect = mpViewShell->GetViewShellBase().getClientRectangle(); + maPresSize = aClientRect.GetSize(); + mpShowWindow->SetPosPixel( aClientRect.TopLeft() ); + resize( maPresSize ); + } + + // #i41824# + // Note: In FullScreen Mode the OS (window manager) sends a resize to + // the WorkWindow once it actually resized it to full size. The + // WorkWindow propagates the resize to the DrawViewShell which calls + // resize() at the SlideShow (this). Calling resize here results in a + // temporary display of a black window in the window's default size + + if( mpView ) + { + mpView->AddWindowToPaintView( mpShowWindow->GetOutDev(), nullptr ); + mpView->SetAnimationPause( true ); + } + + SfxBindings* pBindings = getBindings(); + if( pBindings ) + { + pBindings->Invalidate( SID_PRESENTATION ); + pBindings->Invalidate( SID_REHEARSE_TIMINGS ); + } + + // Defer the sd::ShowWindow's GrabFocus to SlideShow::activate. so that the accessible event can be fired correctly. + //mpShowWindow->GrabFocus(); + + std::vector aProperties; + aProperties.reserve( 4 ); + + aProperties.emplace_back( "AdvanceOnClick" , + -1, Any( !maPresSettings.mbLockedPages ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "ImageAnimationsAllowed" , + -1, Any( maPresSettings.mbAnimationAllowed ), + beans::PropertyState_DIRECT_VALUE ); + + const bool bZOrderEnabled( + SD_MOD()->GetSdOptions( mpDoc->GetDocumentType() )->IsSlideshowRespectZOrder() ); + aProperties.emplace_back( "DisableAnimationZOrder" , + -1, Any( !bZOrderEnabled ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "ForceManualAdvance" , + -1, Any( maPresSettings.mbManual ), + beans::PropertyState_DIRECT_VALUE ); + + if( mbUsePen ) + { + aProperties.emplace_back( "UserPaintColor" , + // User paint color is black by default. + -1, Any( mnUserPaintColor ), + beans::PropertyState_DIRECT_VALUE ); + + aProperties.emplace_back( "UserPaintStrokeWidth" , + // User paint color is black by default. + -1, Any( mdUserPaintStrokeWidth ), + beans::PropertyState_DIRECT_VALUE ); + } + + if (mbRehearseTimings) { + aProperties.emplace_back( "RehearseTimings" , + -1, Any(true), beans::PropertyState_DIRECT_VALUE ); + } + + bRet = startShowImpl( Sequence( + aProperties.data(), aProperties.size() ) ); + + } + + setActiveXToolbarsVisible( false ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startShow()" ); + bRet = false; + } + + return bRet; +} + +bool SlideshowImpl::startShowImpl( const Sequence< beans::PropertyValue >& aProperties ) +{ + try + { + mxShow.set( createSlideShow(), UNO_SET_THROW ); + + mxView = new SlideShowView( + *mpShowWindow, + mpDoc, + meAnimationMode, + this, + maPresSettings.mbFullScreen); + + // try add wait symbol to properties: + const Reference xSpriteCanvas( + mxView->getCanvas() ); + if (xSpriteCanvas.is()) + { + BitmapEx waitSymbolBitmap(BMP_WAIT_ICON); + const Reference xBitmap( + vcl::unotools::xBitmapFromBitmapEx( waitSymbolBitmap ) ); + if (xBitmap.is()) + { + mxShow->setProperty( + beans::PropertyValue( "WaitSymbolBitmap" , + -1, + Any( xBitmap ), + beans::PropertyState_DIRECT_VALUE ) ); + } + + BitmapEx pointerSymbolBitmap(BMP_POINTER_ICON); + const Reference xPointerBitmap( + vcl::unotools::xBitmapFromBitmapEx( pointerSymbolBitmap ) ); + if (xPointerBitmap.is()) + { + mxShow->setProperty( + beans::PropertyValue( "PointerSymbolBitmap" , + -1, + Any( xPointerBitmap ), + beans::PropertyState_DIRECT_VALUE ) ); + } + } + + for( const auto& rProp : aProperties ) + mxShow->setProperty( rProp ); + + mxShow->addView( mxView ); + + mxListenerProxy.set( new SlideShowListenerProxy( this, mxShow ) ); + mxListenerProxy->addAsSlideShowListener(); + + NotifyDocumentEvent( + *mpDoc, + "OnStartPresentation"); + displaySlideIndex( mpSlideController->getStartSlideIndex() ); + + return true; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::startShowImpl()" ); + return false; + } +} + +/** called only by the slideshow view when the first paint event occurs. + This actually starts the slideshow. */ +void SlideshowImpl::onFirstPaint() +{ + if( mpShowWindow ) + { + /* + mpShowWindow->SetBackground( Wallpaper( COL_BLACK ) ); + mpShowWindow->Erase(); + mpShowWindow->SetBackground(); + */ + } + + SolarMutexGuard aSolarGuard; + maUpdateTimer.SetTimeout( sal_uLong(100) ); + maUpdateTimer.Start(); +} + +void SlideshowImpl::paint() +{ + if( mxView.is() ) try + { + awt::PaintEvent aEvt; + // aEvt.UpdateRect = TODO + mxView->paint( aEvt ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::paint()" ); + } +} + +void SAL_CALL SlideshowImpl::addSlideShowListener( const Reference< XSlideShowListener >& xListener ) +{ + if( mxListenerProxy.is() ) + mxListenerProxy->addSlideShowListener( xListener ); +} + +void SAL_CALL SlideshowImpl::removeSlideShowListener( const Reference< XSlideShowListener >& xListener ) +{ + if( mxListenerProxy.is() ) + mxListenerProxy->removeSlideShowListener( xListener ); +} + +void SlideshowImpl::slideEnded(const bool bReverse) +{ + if (bReverse) + gotoPreviousSlide(true); + else + gotoNextSlide(); +} + +bool SlideshowImpl::swipe(const CommandSwipeData &rSwipeData) +{ + if (mbUsePen || mnContextMenuEvent) + return false; + double nVelocityX = rSwipeData.getVelocityX(); + // tdf#108475 make it swipe only if some reasonable movement was involved + if (fabs(nVelocityX) < 50) + return false; + if (nVelocityX > 0) + { + gotoPreviousSlide(); + } + else + { + gotoNextEffect(); + } + //a swipe is followed by a mouse up, tell the view to ignore that mouse up as we've reacted + //to the swipe instead + mxView->ignoreNextMouseReleased(); + return true; +} + +bool SlideshowImpl::longpress(const CommandLongPressData &rLongPressData) +{ + if (mnContextMenuEvent) + return false; + + maPopupMousePos = Point(rLongPressData.getX(), rLongPressData.getY()); + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + + return true; +} + +void SlideshowImpl::removeShapeEvents() +{ + if( !(mxShow.is() && mxListenerProxy.is()) ) + return; + + try + { + for( const auto& rEntry : maShapeEventMap ) + { + mxListenerProxy->removeShapeEventListener( rEntry.first ); + mxShow->setShapeCursor( rEntry.first, awt::SystemPointer::ARROW ); + } + + maShapeEventMap.clear(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::removeShapeEvents()" ); + } +} + +void SlideshowImpl::registerShapeEvents(sal_Int32 nSlideNumber) +{ + if( nSlideNumber < 0 ) + return; + + try + { + Reference< XDrawPagesSupplier > xDrawPages( mxModel, UNO_QUERY_THROW ); + Reference< XIndexAccess > xPages( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + + Reference< XShapes > xDrawPage; + xPages->getByIndex(nSlideNumber) >>= xDrawPage; + + if( xDrawPage.is() ) + { + Reference< XMasterPageTarget > xMasterPageTarget( xDrawPage, UNO_QUERY ); + if( xMasterPageTarget.is() ) + { + Reference< XShapes > xMasterPage = xMasterPageTarget->getMasterPage(); + if( xMasterPage.is() ) + registerShapeEvents( xMasterPage ); + } + registerShapeEvents( xDrawPage ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::registerShapeEvents()" ); + } +} + +void SlideshowImpl::registerShapeEvents( Reference< XShapes > const & xShapes ) +{ + try + { + const sal_Int32 nShapeCount = xShapes->getCount(); + sal_Int32 nShape; + for( nShape = 0; nShape < nShapeCount; nShape++ ) + { + Reference< XShape > xShape; + xShapes->getByIndex( nShape ) >>= xShape; + + if( xShape.is() && xShape->getShapeType() == "com.sun.star.drawing.GroupShape" ) + { + Reference< XShapes > xSubShapes( xShape, UNO_QUERY ); + if( xSubShapes.is() ) + registerShapeEvents( xSubShapes ); + } + + Reference< XPropertySet > xSet( xShape, UNO_QUERY ); + if( !xSet.is() ) + continue; + + Reference< XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + if( !xSetInfo.is() || !xSetInfo->hasPropertyByName( gsOnClick ) ) + continue; + + WrappedShapeEventImplPtr pEvent = std::make_shared(); + xSet->getPropertyValue( gsOnClick ) >>= pEvent->meClickAction; + + switch( pEvent->meClickAction ) + { + case ClickAction_PREVPAGE: + case ClickAction_NEXTPAGE: + case ClickAction_FIRSTPAGE: + case ClickAction_LASTPAGE: + case ClickAction_STOPPRESENTATION: + break; + case ClickAction_BOOKMARK: + if( xSetInfo->hasPropertyByName( gsBookmark ) ) + xSet->getPropertyValue( gsBookmark ) >>= pEvent->maStrBookmark; + if( getSlideNumberForBookmark( pEvent->maStrBookmark ) == -1 ) + continue; + break; + case ClickAction_DOCUMENT: + case ClickAction_SOUND: + case ClickAction_PROGRAM: + case ClickAction_MACRO: + if( xSetInfo->hasPropertyByName( gsBookmark ) ) + xSet->getPropertyValue( gsBookmark ) >>= pEvent->maStrBookmark; + break; + case ClickAction_VERB: + if( xSetInfo->hasPropertyByName( gsVerb ) ) + xSet->getPropertyValue( gsVerb ) >>= pEvent->mnVerb; + break; + default: + continue; // skip all others + } + + maShapeEventMap[ xShape ] = pEvent; + + if( mxListenerProxy.is() ) + mxListenerProxy->addShapeEventListener( xShape ); + mxShow->setShapeCursor( xShape, awt::SystemPointer::REFHAND ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::registerShapeEvents()" ); + } +} + +void SlideshowImpl::displayCurrentSlide (const bool bSkipAllMainSequenceEffects) +{ + stopSound(); + removeShapeEvents(); + + if( mpSlideController && mxShow.is() ) + { + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), + UNO_QUERY_THROW ); + mpSlideController->displayCurrentSlide( mxShow, xDrawPages, bSkipAllMainSequenceEffects ); + registerShapeEvents(mpSlideController->getCurrentSlideNumber()); + update(); + + } + // send out page change event and notify to update all acc info for current page + if (mpViewShell) + { + sal_Int32 currentPageIndex = getCurrentSlideIndex(); + mpViewShell->fireSwitchCurrentPage(currentPageIndex); + mpViewShell->NotifyAccUpdate(); + } +} + +void SlideshowImpl::endPresentation() +{ + if( maPresSettings.mbMouseAsPen) + { + Reference< XMultiServiceFactory > xDocFactory(mpDoc->getUnoModel(), UNO_QUERY ); + if( xDocFactory.is() ) + mxShow->registerUserPaintPolygons(xDocFactory); + } + + if( !mnEndShowEvent ) + mnEndShowEvent = Application::PostUserEvent( LINK(this, SlideshowImpl, endPresentationHdl) ); +} + +IMPL_LINK_NOARG(SlideshowImpl, endPresentationHdl, void*, void) +{ + mnEndShowEvent = nullptr; + + stopSound(); + + if( mxPresentation.is() ) + mxPresentation->end(); +} + +void SAL_CALL SlideshowImpl::pause() +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + return; + + try + { + mbIsPaused = true; + if( mxShow.is() ) + { + mxShow->pause(true); + + if( mxListenerProxy.is() ) + mxListenerProxy->paused(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::pause()" ); + } +} + +void SAL_CALL SlideshowImpl::resume() +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) try + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK || mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow(); + } + else + { + mbIsPaused = false; + if( mxShow.is() ) + { + mxShow->pause(false); + update(); + + if( mxListenerProxy.is() ) + mxListenerProxy->resumed(); + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::resume()" ); + } +#ifdef ENABLE_SDREMOTE + RemoteServer::presentationStarted( this ); +#endif +} + +sal_Bool SAL_CALL SlideshowImpl::isPaused() +{ + SolarMutexGuard aSolarGuard; + return mbIsPaused; +} + +void SAL_CALL SlideshowImpl::blankScreen( sal_Int32 nColor ) +{ + SolarMutexGuard aSolarGuard; + + if( mpShowWindow && mpSlideController ) + { + if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), Color(ColorTransparency, nColor) ) ) + { + pause(); + } + } +} + +// XShapeEventListener + +void SlideshowImpl::click( const Reference< XShape >& xShape ) +{ + SolarMutexGuard aSolarGuard; + + WrappedShapeEventImplPtr pEvent = maShapeEventMap[xShape]; + if( !pEvent ) + return; + + switch( pEvent->meClickAction ) + { + case ClickAction_PREVPAGE: gotoPreviousSlide(); break; + case ClickAction_NEXTPAGE: gotoNextSlide(); break; + case ClickAction_FIRSTPAGE: gotoFirstSlide(); break; + case ClickAction_LASTPAGE: gotoLastSlide(); break; + case ClickAction_STOPPRESENTATION: endPresentation(); break; + case ClickAction_BOOKMARK: + { + gotoBookmark( pEvent->maStrBookmark ); + } + break; + case ClickAction_SOUND: + { +#if HAVE_FEATURE_AVMEDIA + try + { + mxPlayer.set(avmedia::MediaWindow::createPlayer(pEvent->maStrBookmark, ""/*TODO?*/), uno::UNO_SET_THROW ); + mxPlayer->start(); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::click()" ); + } +#endif + } + break; + + case ClickAction_DOCUMENT: + { + OUString aBookmark( pEvent->maStrBookmark ); + + sal_Int32 nPos = aBookmark.indexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL( aBookmark.copy( 0, nPos+1 ) ); + OUString aName( aBookmark.copy( nPos+1 ) ); + aURL += getUiNameFromPageApiNameImpl( aName ); + aBookmark = aURL; + } + + mpDocSh->OpenBookmark( aBookmark ); + } + break; + + case ClickAction_PROGRAM: + { + INetURLObject aURL( + ::URIHelper::SmartRel2Abs( + INetURLObject(mpDocSh->GetMedium()->GetBaseURL()), + pEvent->maStrBookmark, ::URIHelper::GetMaybeFileHdl(), true, + false, INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ) ); + + if( INetProtocol::File == aURL.GetProtocol() ) + { + SfxStringItem aUrl( SID_FILE_NAME, aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + SfxBoolItem aBrowsing( SID_BROWSE, true ); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + { + SfxUnoFrameItem aDocFrame(SID_FILLFRAME, pViewFrm->GetFrame().GetFrameInterface()); + pViewFrm->GetDispatcher()->ExecuteList( SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aUrl, &aBrowsing }, { &aDocFrame } ); + } + } + } + break; + +#if HAVE_FEATURE_SCRIPTING + case presentation::ClickAction_MACRO: + { + const OUString aMacro( pEvent->maStrBookmark ); + + if ( SfxApplication::IsXScriptURL( aMacro ) ) + { + Any aRet; + Sequence< sal_Int16 > aOutArgsIndex; + Sequence< Any > aOutArgs; + Sequence< Any >* pInArgs = new Sequence< Any >(0); + mpDocSh->CallXScript( aMacro, *pInArgs, aRet, aOutArgsIndex, aOutArgs); + } + else + { + // aMacro has the following syntax: + // "Macroname.Modulname.Libname.Documentname" or + // "Macroname.Modulname.Libname.Applicationname" + sal_Int32 nIdx{ 0 }; + const std::u16string_view aMacroName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aMacro, 0, '.', nIdx); + + // todo: is the limitation still given that only + // Modulname+Macroname can be used here? + OUString aExecMacro = OUString::Concat(aModulName) + "." + aMacroName; + mpDocSh->GetBasic()->Call(aExecMacro); + } + } + break; +#endif + + case ClickAction_VERB: + { + // todo, better do it async? + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + SdrOle2Obj* pOleObject = dynamic_cast< SdrOle2Obj* >(pObj); + if (pOleObject && mpViewShell ) + mpViewShell->ActivateObject(pOleObject, pEvent->mnVerb); + } + break; + default: + break; + } +} + +sal_Int32 SlideshowImpl::getSlideNumberForBookmark( const OUString& rStrBookmark ) +{ + bool bIsMasterPage; + OUString aBookmark = getUiNameFromPageApiNameImpl( rStrBookmark ); + sal_uInt16 nPgNum = mpDoc->GetPageByName( aBookmark, bIsMasterPage ); + + if( nPgNum == SDRPAGE_NOTFOUND ) + { + // Is the bookmark an object? + SdrObject* pObj = mpDoc->GetObj( aBookmark ); + + if( pObj ) + { + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + bIsMasterPage = pObj->getSdrPageFromSdrObject()->IsMasterPage(); + } + } + + if( (nPgNum == SDRPAGE_NOTFOUND) || bIsMasterPage || static_cast(mpDoc->GetPage(nPgNum))->GetPageKind() != PageKind::Standard ) + return -1; + + return ( nPgNum - 1) >> 1; +} + +void SlideshowImpl::hyperLinkClicked( OUString const& aHyperLink ) +{ + OUString aBookmark( aHyperLink ); + + sal_Int32 nPos = aBookmark.indexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL( aBookmark.copy( 0, nPos+1 ) ); + OUString aName( aBookmark.copy( nPos+1 ) ); + aURL += getUiNameFromPageApiNameImpl( aName ); + aBookmark = aURL; + } + + mpDocSh->OpenBookmark( aBookmark ); +} + +void SlideshowImpl::displaySlideNumber( sal_Int32 nSlideNumber ) +{ + if( mpSlideController ) + { + if( mpSlideController->jumpToSlideNumber( nSlideNumber ) ) + { + displayCurrentSlide(); + } + } +} + +/** nSlideIndex == -1 displays current slide again */ +void SlideshowImpl::displaySlideIndex( sal_Int32 nSlideIndex ) +{ + if( mpSlideController ) + { + if( (nSlideIndex == -1) || mpSlideController->jumpToSlideIndex( nSlideIndex ) ) + { + displayCurrentSlide(); + } + } +} + +void SlideshowImpl::jumpToBookmark( const OUString& sBookmark ) +{ + sal_Int32 nSlideNumber = getSlideNumberForBookmark( sBookmark ); + if( nSlideNumber != -1 ) + displaySlideNumber( nSlideNumber ); +} + +sal_Int32 SlideshowImpl::getCurrentSlideNumber() const +{ + return mpSlideController ? mpSlideController->getCurrentSlideNumber() : -1; +} + +sal_Bool SAL_CALL SlideshowImpl::isEndless() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbEndless; +} + +void SlideshowImpl::update() +{ + startUpdateTimer(); +} + +void SlideshowImpl::startUpdateTimer() +{ + SolarMutexGuard aSolarGuard; + maUpdateTimer.SetTimeout( 0 ); + maUpdateTimer.Start(); +} + +/** this timer is called 20ms after a new slide was displayed. + This is used to unfreeze user input that was disabled after + slide change to skip input that was buffered during slide + transition preparation */ +IMPL_LINK_NOARG(SlideshowImpl, ReadyForNextInputHdl, Timer *, void) +{ + mbInputFreeze = false; +} + +/** if I catch someone someday who calls this method by hand + and not by using the timer, I will personally punish this + person seriously, even if this person is me. +*/ +IMPL_LINK_NOARG(SlideshowImpl, updateHdl, Timer *, void) +{ + updateSlideShow(); +} + +void SlideshowImpl::updateSlideShow() +{ + // prevent me from deletion when recursing (App::EnableYieldMode does) + const rtl::Reference xKeepAlive(this); + + Reference< XSlideShow > xShow( mxShow ); + if ( ! xShow.is()) + return; + + try + { + double fUpdate = 0.0; + if( !xShow->update(fUpdate) ) + fUpdate = -1.0; + + if (mxShow.is() && (fUpdate >= 0.0)) + { + if (::basegfx::fTools::equalZero(fUpdate)) + { + // Make sure idle tasks don't starve when we don't have to wait. + // Don't process any events generated after invoking the function. + Application::Reschedule(/*bHandleAllCurrentEvents=*/true); + } + else + { + // Avoid busy loop when the previous call to update() + // returns a small positive number but not 0 (which is + // handled above). Also, make sure that calls to update() + // have a minimum frequency. + // => Allow up to 60 frames per second. Call at least once + // every 4 seconds. + const static sal_Int32 nMaximumFrameCount (60); + const static double nMinimumTimeout (1.0 / nMaximumFrameCount); + const static double nMaximumTimeout (4.0); + fUpdate = std::clamp(fUpdate, nMinimumTimeout, nMaximumTimeout); + + // Make sure that the maximum frame count has not been set + // too high (only then conversion to milliseconds and long + // integer may lead to zero value.) + OSL_ASSERT(static_cast(fUpdate * 1000.0) > 0); + } + + // Use our high resolution timers for the asynchronous callback. + maUpdateTimer.SetTimeout(static_cast(fUpdate * 1000.0)); + maUpdateTimer.Start(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::updateSlideShow()" ); + } +} + +bool SlideshowImpl::keyInput(const KeyEvent& rKEvt) +{ + if( !mxShow.is() || mbInputFreeze ) + return false; + + bool bRet = true; + + try + { + const int nKeyCode = rKEvt.GetKeyCode().GetCode(); + switch( nKeyCode ) + { + case awt::Key::CONTEXTMENU: + if( !mnContextMenuEvent ) + { + if( mpShowWindow ) + maPopupMousePos = mpShowWindow->GetPointerState().maPos; + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } + break; + + // cancel show + case KEY_ESCAPE: + case KEY_SUBTRACT: + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + endPresentation(); + break; + + // advance show + case KEY_PAGEDOWN: + if(rKEvt.GetKeyCode().IsMod2()) + { + gotoNextSlide(); + break; + } + [[fallthrough]]; + case KEY_SPACE: + case KEY_RIGHT: + case KEY_DOWN: + gotoNextEffect(); + break; + + case KEY_RETURN: + { + if( !maCharBuffer.isEmpty() ) + { + if( mpSlideController ) + { + if( mpSlideController->jumpToSlideNumber( maCharBuffer.toInt32() - 1 ) ) + displayCurrentSlide(); + } + maCharBuffer.clear(); + } + else + { + gotoNextEffect(); + } + } + break; + + // numeric: add to buffer + case KEY_0: + case KEY_1: + case KEY_2: + case KEY_3: + case KEY_4: + case KEY_5: + case KEY_6: + case KEY_7: + case KEY_8: + case KEY_9: + maCharBuffer += OUStringChar( rKEvt.GetCharCode() ); + break; + + case KEY_PAGEUP: + if(rKEvt.GetKeyCode().IsMod2()) + { + gotoPreviousSlide(); + break; + } + [[fallthrough]]; + case KEY_LEFT: + case KEY_UP: + case KEY_BACKSPACE: + gotoPreviousEffect(); + break; + + case KEY_P: + setUsePen( !mbUsePen ); + break; + + // tdf#149351 Ctrl+A disables pointer as pen mode + case KEY_A: + if(rKEvt.GetKeyCode().IsMod1()) + { + setUsePen( false ); + break; + } + break; + + case KEY_E: + setEraseAllInk( true ); + updateSlideShow(); + break; + + case KEY_HOME: + gotoFirstSlide(); + break; + + case KEY_END: + gotoLastSlide(); + break; + + case KEY_B: + case KEY_W: + case KEY_POINT: + case KEY_COMMA: + { + blankScreen( ((nKeyCode == KEY_W ) || (nKeyCode == KEY_COMMA)) ? 0x00ffffff : 0x00000000 ); + } + break; + + default: + bRet = false; + break; + } + } + catch( Exception& ) + { + bRet = false; + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::keyInput()" ); + } + + return bRet; +} + +IMPL_LINK( SlideshowImpl, EventListenerHdl, VclSimpleEvent&, rSimpleEvent, void ) +{ + if( !mxShow.is() || mbInputFreeze ) + return; + + if( !((rSimpleEvent.GetId() == VclEventId::WindowCommand) && static_cast(&rSimpleEvent)->GetData()) ) + return; + + const CommandEvent& rEvent = *static_cast(static_cast(&rSimpleEvent)->GetData()); + + if( rEvent.GetCommand() != CommandEventId::Media ) + return; + + CommandMediaData* pMediaData = rEvent.GetMediaData(); + pMediaData->SetPassThroughToOS(false); + switch (pMediaData->GetMediaId()) + { +#if defined( MACOSX ) + case MediaCommand::Menu: + if( !mnContextMenuEvent ) + { + if( mpShowWindow ) + maPopupMousePos = mpShowWindow->GetPointerState().maPos; + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } + break; + case MediaCommand::VolumeDown: + gotoPreviousSlide(); + break; + case MediaCommand::VolumeUp: + gotoNextEffect(); + break; +#endif + case MediaCommand::NextTrack: + gotoNextEffect(); + break; + case MediaCommand::Pause: + if( !mbIsPaused ) + blankScreen(0); + break; + case MediaCommand::Play: + if( mbIsPaused ) + resume(); + break; + + case MediaCommand::PlayPause: + if( mbIsPaused ) + resume(); + else + blankScreen(0); + break; + case MediaCommand::PreviousTrack: + gotoPreviousSlide(); + break; + case MediaCommand::NextTrackHold: + gotoLastSlide(); + break; + + case MediaCommand::Rewind: + gotoFirstSlide(); + break; + case MediaCommand::Stop: + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + endPresentation(); + break; + default: + pMediaData->SetPassThroughToOS(true); + break; + } +} + +void SlideshowImpl::mouseButtonUp(const MouseEvent& rMEvt) +{ + if( rMEvt.IsRight() && !mnContextMenuEvent ) + { + maPopupMousePos = rMEvt.GetPosPixel(); + mnContextMenuEvent = Application::PostUserEvent( LINK( this, SlideshowImpl, ContextMenuHdl ) ); + } +} + +IMPL_LINK_NOARG(SlideshowImpl, ContextMenuHdl, void*, void) +{ + mnContextMenuEvent = nullptr; + + if (mpSlideController == nullptr) + return; + + mbWasPaused = mbIsPaused; + if( !mbWasPaused ) + pause(); + + std::unique_ptr xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/slidecontextmenu.ui")); + std::unique_ptr xMenu(xBuilder->weld_menu("menu")); + OUString sNextImage(BMP_MENU_NEXT), sPrevImage(BMP_MENU_PREV); + xMenu->insert(0, "next", SdResId(RID_SVXSTR_MENU_NEXT), &sNextImage, nullptr, nullptr, TRISTATE_INDET); + xMenu->insert(1, "prev", SdResId(RID_SVXSTR_MENU_PREV), &sPrevImage, nullptr, nullptr, TRISTATE_INDET); + + // Adding button to display if in Pen mode + xMenu->set_active("pen", mbUsePen); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + xMenu->set_visible("next", mpSlideController->getNextSlideIndex() != -1); + xMenu->set_visible("prev", (mpSlideController->getPreviousSlideIndex() != -1 ) || (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK)); + xMenu->set_visible("edit", mpViewShell->GetDoc()->IsStartWithPresentation()); + + std::unique_ptr xPageMenu(xBuilder->weld_menu("gotomenu")); + OUString sFirstImage(BMP_MENU_FIRST), sLastImage(BMP_MENU_LAST); + xPageMenu->insert(0, "first", SdResId(RID_SVXSTR_MENU_FIRST), &sFirstImage, nullptr, nullptr, TRISTATE_INDET); + xPageMenu->insert(1, "last", SdResId(RID_SVXSTR_MENU_LAST), &sLastImage, nullptr, nullptr, TRISTATE_INDET); + + // populate slide goto list + const sal_Int32 nPageNumberCount = mpSlideController->getSlideNumberCount(); + if( nPageNumberCount <= 1 ) + { + xMenu->set_visible("goto", false); + } + else + { + sal_Int32 nCurrentSlideNumber = mpSlideController->getCurrentSlideNumber(); + if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + nCurrentSlideNumber = -1; + + xPageMenu->set_visible("first", mpSlideController->getSlideNumber(0) != nCurrentSlideNumber); + xPageMenu->set_visible("last", mpSlideController->getSlideNumber(mpSlideController->getSlideIndexCount() - 1) != nCurrentSlideNumber); + + sal_Int32 nPageNumber; + + for( nPageNumber = 0; nPageNumber < nPageNumberCount; nPageNumber++ ) + { + if( mpSlideController->isVisibleSlideNumber( nPageNumber ) ) + { + SdPage* pPage = mpDoc->GetSdPage(static_cast(nPageNumber), PageKind::Standard); + if (pPage) + { + OUString sId(OUString::number(CM_SLIDES + nPageNumber)); + xPageMenu->append_check(sId, pPage->GetName()); + if (nPageNumber == nCurrentSlideNumber) + xPageMenu->set_active(sId.toUtf8(), true); + } + } + } + } + + std::unique_ptr xBlankMenu(xBuilder->weld_menu("screenmenu")); + + if (mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK) + { + xBlankMenu->set_active((mpShowWindow->GetBlankColor() == COL_WHITE) ? "white" : "black", true); + } + + std::unique_ptr xWidthMenu(xBuilder->weld_menu("widthmenu")); + + // populate color width list + sal_Int32 nIterator; + double nWidth; + + nWidth = 4.0; + for( nIterator = 1; nIterator < 6; nIterator++) + { + switch(nIterator) + { + case 1: + nWidth = 4.0; + break; + case 2: + nWidth = 100.0; + break; + case 3: + nWidth = 150.0; + break; + case 4: + nWidth = 200.0; + break; + case 5: + nWidth = 400.0; + break; + default: + break; + } + + if (nWidth == mdUserPaintStrokeWidth) + xWidthMenu->set_active(OString::number(nWidth), true); + } + + ::tools::Rectangle aRect(maPopupMousePos, Size(1,1)); + weld::Window* pParent = weld::GetPopupParent(*mpShowWindow, aRect); + ContextMenuSelectHdl(xMenu->popup_at_rect(pParent, aRect)); + + if( mxView.is() ) + mxView->ignoreNextMouseReleased(); + + if( !mbWasPaused ) + resume(); +} + +void SlideshowImpl::ContextMenuSelectHdl(std::string_view rMenuId) +{ + if (rMenuId == "prev") + { + gotoPreviousSlide(); + mbWasPaused = false; + } + else if(rMenuId == "next") + { + gotoNextSlide(); + mbWasPaused = false; + } + else if (rMenuId == "first") + { + gotoFirstSlide(); + mbWasPaused = false; + } + else if (rMenuId == "last") + { + gotoLastSlide(); + mbWasPaused = false; + } + else if (rMenuId == "black" || rMenuId == "white") + { + const Color aBlankColor(rMenuId == "white" ? COL_WHITE : COL_BLACK); + if( mbWasPaused ) + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_BLANK ) + { + if( mpShowWindow->GetBlankColor() == aBlankColor ) + { + mbWasPaused = false; + mpShowWindow->RestartShow(); + return; + } + } + mpShowWindow->RestartShow(); + } + if( mpShowWindow->SetBlankMode( mpSlideController->getCurrentSlideIndex(), aBlankColor ) ) + { + pause(); + mbWasPaused = true; + } + } + else if (rMenuId == "color") + { + //Open a color picker based on SvColorDialog + ::Color aColor( ColorTransparency, mnUserPaintColor ); + SvColorDialog aColorDlg; + aColorDlg.SetColor( aColor ); + + if (aColorDlg.Execute(mpShowWindow->GetFrameWeld())) + { + aColor = aColorDlg.GetColor(); + setPenColor(sal_Int32(aColor)); + } + mbWasPaused = false; + } + else if (rMenuId == "4") + { + setPenWidth(4.0); + mbWasPaused = false; + } + else if (rMenuId == "100") + { + setPenWidth(100.0); + mbWasPaused = false; + } + else if (rMenuId == "150") + { + setPenWidth(150.0); + mbWasPaused = false; + } + else if (rMenuId == "200") + { + setPenWidth(200.0); + mbWasPaused = false; + } + else if (rMenuId == "400") + { + setPenWidth(400.0); + mbWasPaused = false; + } + else if (rMenuId == "erase") + { + setEraseAllInk(true); + mbWasPaused = false; + } + else if (rMenuId == "pen") + { + setUsePen(!mbUsePen); + mbWasPaused = false; + } + else if (rMenuId == "edit") + { + // When in autoplay mode (pps/ppsx), offer editing of the presentation + // Turn autostart off, else Impress will close when exiting the Presentation + mpViewShell->GetDoc()->SetExitAfterPresenting(false); + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + { + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + } + endPresentation(); + } + else if (rMenuId == "end") + { + // in case the user cancels the presentation, switch to current slide + // in edit mode + if( mpSlideController && (ANIMATIONMODE_SHOW == meAnimationMode) ) + { + if( mpSlideController->getCurrentSlideNumber() != -1 ) + { + mnRestoreSlide = mpSlideController->getCurrentSlideNumber(); + } + } + endPresentation(); + } + else if (!rMenuId.empty()) + { + sal_Int32 nPageNumber = o3tl::toInt32(rMenuId) - CM_SLIDES; + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_END) || (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow( nPageNumber ); + } + else if( nPageNumber != mpSlideController->getCurrentSlideNumber() ) + { + displaySlideNumber( nPageNumber ); + } + mbWasPaused = false; + } +} + +Reference< XSlideShow > SlideshowImpl::createSlideShow() +{ + Reference< XSlideShow > xShow; + + try + { + Reference< uno::XComponentContext > xContext = + ::comphelper::getProcessComponentContext(); + + xShow.set( presentation::SlideShow::create(xContext), UNO_SET_THROW ); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::createSlideShow()" ); + } + + return xShow; +} + +void SlideshowImpl::createSlideList( bool bAll, std::u16string_view rPresSlide ) +{ + const sal_uInt16 nSlideCount = mpDoc->GetSdPageCount( PageKind::Standard ); + + if( !nSlideCount ) + return; + + SdCustomShow* pCustomShow; + + if( mpDoc->GetCustomShowList() && maPresSettings.mbCustomShow ) + pCustomShow = mpDoc->GetCustomShowList()->GetCurObject(); + else + pCustomShow = nullptr; + + // create animation slide controller + AnimationSlideController::Mode eMode = + ( pCustomShow && !pCustomShow->PagesVector().empty() ) ? AnimationSlideController::CUSTOM : + (bAll ? AnimationSlideController::ALL : AnimationSlideController::FROM); + + Reference< XDrawPagesSupplier > xDrawPages( mpDoc->getUnoModel(), UNO_QUERY_THROW ); + Reference< XIndexAccess > xSlides( xDrawPages->getDrawPages(), UNO_QUERY_THROW ); + mpSlideController = std::make_shared( xSlides, eMode ); + + if( eMode != AnimationSlideController::CUSTOM ) + { + sal_Int32 nFirstVisibleSlide = 0; + + // normal presentation + if( !rPresSlide.empty() ) + { + sal_Int32 nSlide; + bool bTakeNextAvailable = false; + + for( nSlide = 0, nFirstVisibleSlide = -1; + ( nSlide < nSlideCount ) && ( -1 == nFirstVisibleSlide ); nSlide++ ) + { + SdPage* pTestSlide = mpDoc->GetSdPage( static_cast(nSlide), PageKind::Standard ); + + if( pTestSlide->GetName() == rPresSlide ) + { + if( pTestSlide->IsExcluded() ) + bTakeNextAvailable = true; + else + nFirstVisibleSlide = nSlide; + } + else if( bTakeNextAvailable && !pTestSlide->IsExcluded() ) + nFirstVisibleSlide = nSlide; + } + + if( -1 == nFirstVisibleSlide ) + nFirstVisibleSlide = 0; + } + + for( sal_Int32 i = 0; i < nSlideCount; i++ ) + { + bool bVisible = ! mpDoc->GetSdPage( static_cast(i), PageKind::Standard )->IsExcluded(); + if( bVisible || (eMode == AnimationSlideController::ALL) ) + mpSlideController->insertSlideNumber( i, bVisible ); + } + + mpSlideController->setStartSlideNumber( nFirstVisibleSlide ); + } + else + { + if( meAnimationMode != ANIMATIONMODE_SHOW && !rPresSlide.empty() ) + { + sal_Int32 nSlide; + for( nSlide = 0; nSlide < nSlideCount; nSlide++ ) + if( rPresSlide == mpDoc->GetSdPage( static_cast(nSlide), PageKind::Standard )->GetName() ) + break; + + if( nSlide < nSlideCount ) + mpSlideController->insertSlideNumber( static_cast(nSlide) ); + } + + for( const auto& rpPage : pCustomShow->PagesVector() ) + { + const sal_uInt16 nSdSlide = ( rpPage->GetPageNum() - 1 ) / 2; + + if( ! mpDoc->GetSdPage( nSdSlide, PageKind::Standard )->IsExcluded()) + mpSlideController->insertSlideNumber( nSdSlide ); + } + } +} + +typedef sal_uInt16 (*FncGetChildWindowId)(); + +const FncGetChildWindowId aShowChildren[] = +{ + &AnimationChildWindow::GetChildWindowId, + &Svx3DChildWindow::GetChildWindowId, + &SvxFontWorkChildWindow::GetChildWindowId, + &SvxColorChildWindow::GetChildWindowId, + &SvxSearchDialogWrapper::GetChildWindowId, + &SvxBmpMaskChildWindow::GetChildWindowId, + &SvxIMapDlgChildWindow::GetChildWindowId, + &SvxHlinkDlgWrapper::GetChildWindowId, + &SfxInfoBarContainerChild::GetChildWindowId +}; + +void SlideshowImpl::hideChildWindows() +{ + mnChildMask = 0; + + if( ANIMATIONMODE_SHOW != meAnimationMode ) + return; + + SfxViewFrame* pViewFrame = getViewFrame(); + + if( !pViewFrame ) + return; + + for( sal_uLong i = 0; i < SAL_N_ELEMENTS( aShowChildren ); i++ ) + { + const sal_uInt16 nId = ( *aShowChildren[ i ] )(); + + if( pViewFrame->GetChildWindow( nId ) ) + { + pViewFrame->SetChildWindow( nId, false ); + mnChildMask |= ::tools::ULong(1) << i; + } + } +} + +void SlideshowImpl::showChildWindows() +{ + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + SfxViewFrame* pViewFrame = getViewFrame(); + if( pViewFrame ) + { + for( sal_uLong i = 0; i < SAL_N_ELEMENTS(aShowChildren); i++ ) + { + if( mnChildMask & ( ::tools::ULong(1) << i ) ) + pViewFrame->SetChildWindow( ( *aShowChildren[ i ] )(), true ); + } + } + } +} + +SfxViewFrame* SlideshowImpl::getViewFrame() const +{ + return mpViewShell ? mpViewShell->GetViewFrame() : nullptr; +} + +SfxDispatcher* SlideshowImpl::getDispatcher() const +{ + return (mpViewShell && mpViewShell->GetViewFrame()) ? mpViewShell->GetViewFrame()->GetDispatcher() : nullptr; +} + +SfxBindings* SlideshowImpl::getBindings() const +{ + return (mpViewShell && mpViewShell->GetViewFrame()) ? &mpViewShell->GetViewFrame()->GetBindings() : nullptr; +} + +void SlideshowImpl::resize( const Size& rSize ) +{ + maPresSize = rSize; + + if(mpShowWindow) + { + mpShowWindow->SetSizePixel( maPresSize ); + mpShowWindow->Show(); + } + + if( mxView.is() ) try + { + awt::WindowEvent aEvt; + mxView->windowResized(aEvt); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::resize()" ); + } +} + +void SlideshowImpl::setActiveXToolbarsVisible( bool bVisible ) +{ + // in case of ActiveX control the toolbars should not be visible if slide show runs in window mode + // actually it runs always in window mode in case of ActiveX control + if ( !(!maPresSettings.mbFullScreen && mpDocSh && mpDocSh->GetMedium()) ) + return; + + const SfxBoolItem* pItem = SfxItemSet::GetItem(mpDocSh->GetMedium()->GetItemSet(), SID_VIEWONLY, false); + if ( !(pItem && pItem->GetValue()) ) + return; + + // this is a plugin/activex mode, no toolbars should be visible during slide show + // after the end of slide show they should be visible again + SfxViewFrame* pViewFrame = getViewFrame(); + if( !pViewFrame ) + return; + + try + { + Reference< frame::XLayoutManager > xLayoutManager; + Reference< beans::XPropertySet > xFrameProps( pViewFrame->GetFrame().GetFrameInterface(), UNO_QUERY_THROW ); + if ( ( xFrameProps->getPropertyValue( "LayoutManager" ) + >>= xLayoutManager ) + && xLayoutManager.is() ) + { + xLayoutManager->setVisible( bVisible ); + } + } + catch( uno::Exception& ) + {} +} + +void SAL_CALL SlideshowImpl::activate() +{ + SolarMutexGuard aSolarGuard; + + maDeactivateTimer.Stop(); + + if( mbActive || !mxShow.is() ) + return; + + mbActive = true; + + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + if( mbAutoSaveWasOn ) + setAutoSaveState( false ); + + if( mpShowWindow ) + { + SfxViewFrame* pViewFrame = getViewFrame(); + SfxDispatcher* pDispatcher = pViewFrame ? pViewFrame->GetDispatcher() : nullptr; + + hideChildWindows(); + + if( pDispatcher ) + { + // filter all forbidden slots + pDispatcher->SetSlotFilter( SfxSlotFilterState::ENABLED, pAllowed ); + } + + if( getBindings() ) + getBindings()->InvalidateAll(true); + + mpShowWindow->GrabFocus(); + } + } + + resume(); +} + +void SAL_CALL SlideshowImpl::deactivate() +{ + SolarMutexGuard aSolarGuard; + + if( mbActive && mxShow.is() ) + { + maDeactivateTimer.Start(); + } +} + +IMPL_LINK_NOARG(SlideshowImpl, deactivateHdl, Timer *, void) +{ + if( !(mbActive && mxShow.is()) ) + return; + + mbActive = false; + + pause(); + + if( ANIMATIONMODE_SHOW == meAnimationMode ) + { + if( mbAutoSaveWasOn ) + setAutoSaveState( true ); + + if( mpShowWindow ) + { + showChildWindows(); + } + } +} + +sal_Bool SAL_CALL SlideshowImpl::isActive() +{ + SolarMutexGuard aSolarGuard; + return mbActive; +} + +void SlideshowImpl::setAutoSaveState( bool bOn) +{ + try + { + uno::Reference xContext( ::comphelper::getProcessComponentContext() ); + + uno::Reference< util::XURLTransformer > xParser(util::URLTransformer::create(xContext)); + util::URL aURL; + aURL.Complete = "vnd.sun.star.autorecovery:/setAutoSaveState"; + xParser->parseStrict(aURL); + + Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue("AutoSaveState", bOn) }; + + uno::Reference< frame::XDispatch > xAutoSave = frame::theAutoRecovery::get(xContext); + xAutoSave->dispatch(aURL, aArgs); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::setAutoSaveState()"); + } +} + +Reference< XDrawPage > SAL_CALL SlideshowImpl::getCurrentSlide() +{ + SolarMutexGuard aSolarGuard; + + Reference< XDrawPage > xSlide; + if( mxShow.is() && mpSlideController ) + { + sal_Int32 nSlide = getCurrentSlideNumber(); + if( (nSlide >= 0) && (nSlide < mpSlideController->getSlideNumberCount() ) ) + xSlide = mpSlideController->getSlideByNumber( nSlide ); + } + + return xSlide; +} + +sal_Int32 SAL_CALL SlideshowImpl::getNextSlideIndex() +{ + SolarMutexGuard aSolarGuard; + + if( mxShow.is() ) + { + return mpSlideController->getNextSlideIndex(); + } + else + { + return -1; + } +} + +sal_Int32 SAL_CALL SlideshowImpl::getCurrentSlideIndex() +{ + return mpSlideController ? mpSlideController->getCurrentSlideIndex() : -1; +} + +// css::presentation::XSlideShowController: + +::sal_Int32 SAL_CALL SlideshowImpl::getSlideCount() +{ + return mpSlideController ? mpSlideController->getSlideIndexCount() : 0; +} + +Reference< XDrawPage > SAL_CALL SlideshowImpl::getSlideByIndex(::sal_Int32 Index) +{ + if ((mpSlideController == nullptr) || (Index < 0) + || (Index >= mpSlideController->getSlideIndexCount())) + throw IndexOutOfBoundsException(); + + return mpSlideController->getSlideByNumber( mpSlideController->getSlideNumber( Index ) ); +} + +sal_Bool SAL_CALL SlideshowImpl::getAlwaysOnTop() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbAlwaysOnTop; +} + +void SAL_CALL SlideshowImpl::setAlwaysOnTop( sal_Bool bAlways ) +{ + SolarMutexGuard aSolarGuard; + if( maPresSettings.mbAlwaysOnTop != bool(bAlways) ) + { + maPresSettings.mbAlwaysOnTop = bAlways; + // todo, can this be changed while running? + } +} + +sal_Bool SAL_CALL SlideshowImpl::isFullScreen() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbFullScreen; +} + +sal_Bool SAL_CALL SlideshowImpl::getMouseVisible() +{ + SolarMutexGuard aSolarGuard; + return maPresSettings.mbMouseVisible; +} + +void SAL_CALL SlideshowImpl::setMouseVisible( sal_Bool bVisible ) +{ + SolarMutexGuard aSolarGuard; + if( maPresSettings.mbMouseVisible != bool(bVisible) ) + { + maPresSettings.mbMouseVisible = bVisible; + if( mpShowWindow ) + mpShowWindow->SetMouseAutoHide( !maPresSettings.mbMouseVisible ); + } +} + +sal_Bool SAL_CALL SlideshowImpl::getUsePen() +{ + SolarMutexGuard aSolarGuard; + return mbUsePen; +} + +void SAL_CALL SlideshowImpl::setUsePen( sal_Bool bMouseAsPen ) +{ + SolarMutexGuard aSolarGuard; + mbUsePen = bMouseAsPen; + if( !mxShow.is() ) + return; + + try + { + // For Pencolor; + Any aValue; + if( mbUsePen ) + aValue <<= mnUserPaintColor; + beans::PropertyValue aPenProp; + aPenProp.Name = "UserPaintColor"; + aPenProp.Value = aValue; + mxShow->setProperty( aPenProp ); + + //for StrokeWidth : + if( mbUsePen ) + { + beans::PropertyValue aPenPropWidth; + aPenPropWidth.Name = "UserPaintStrokeWidth"; + aPenPropWidth.Value <<= mdUserPaintStrokeWidth; + mxShow->setProperty( aPenPropWidth ); + + // for Pen Mode + beans::PropertyValue aPenPropSwitchPenMode; + aPenPropSwitchPenMode.Name = "SwitchPenMode"; + aPenPropSwitchPenMode.Value <<= true; + mxShow->setProperty( aPenPropSwitchPenMode ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::setUsePen()" ); + } +} + +double SAL_CALL SlideshowImpl::getPenWidth() +{ + SolarMutexGuard aSolarGuard; + return mdUserPaintStrokeWidth; +} + +void SAL_CALL SlideshowImpl::setPenWidth( double dStrokeWidth ) +{ + SolarMutexGuard aSolarGuard; + mdUserPaintStrokeWidth = dStrokeWidth; + setUsePen( true ); // enable pen mode, update color and width +} + +sal_Int32 SAL_CALL SlideshowImpl::getPenColor() +{ + SolarMutexGuard aSolarGuard; + return mnUserPaintColor; +} + +void SAL_CALL SlideshowImpl::setPenColor( sal_Int32 nColor ) +{ + SolarMutexGuard aSolarGuard; + mnUserPaintColor = nColor; + setUsePen( true ); // enable pen mode, update color +} + +void SAL_CALL SlideshowImpl::setEraseAllInk(sal_Bool bEraseAllInk) +{ + if( !bEraseAllInk ) + return; + + SolarMutexGuard aSolarGuard; + if( !mxShow.is() ) + return; + + try + { + beans::PropertyValue aPenPropEraseAllInk; + aPenPropEraseAllInk.Name = "EraseAllInk"; + aPenPropEraseAllInk.Value <<= bEraseAllInk; + mxShow->setProperty( aPenPropEraseAllInk ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd.slideshow", "sd::SlideshowImpl::setEraseAllInk()" ); + } +} + +// XSlideShowController Methods +sal_Bool SAL_CALL SlideshowImpl::isRunning( ) +{ + SolarMutexGuard aSolarGuard; + return mxShow.is(); +} + +void SAL_CALL SlideshowImpl::gotoNextEffect( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController && mpShowWindow) ) + return; + + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( eMode == SHOWWINDOWMODE_END ) + { + endPresentation(); + } + else if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + mxShow->nextEffect(); + update(); + } +} + +void SAL_CALL SlideshowImpl::gotoPreviousEffect( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController && mpShowWindow) ) + return; + + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + mxShow->previousEffect(); + update(); + } +} + +void SAL_CALL SlideshowImpl::gotoFirstSlide( ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mpShowWindow && mpSlideController) ) + return; + + if( mbIsPaused ) + resume(); + + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + if( mpSlideController->getSlideIndexCount() ) + mpShowWindow->RestartShow( 0); + } + else + { + displaySlideIndex( 0 ); + } +} + +void SAL_CALL SlideshowImpl::gotoNextSlide( ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + // if this is a show, ignore user inputs and + // start 20ms timer to reenable inputs to filter + // buffered inputs during slide transition + if( meAnimationMode == ANIMATIONMODE_SHOW ) + { + mbInputFreeze = true; + maInputFreezeTimer.Start(); + } + + if( mpSlideController ) + { + if( mpSlideController->nextSlide() ) + { + displayCurrentSlide(); + } + else + { + stopSound(); + + if( meAnimationMode == ANIMATIONMODE_PREVIEW ) + { + endPresentation(); + } + else if( maPresSettings.mbEndless ) + { + if( maPresSettings.mnPauseTimeout ) + { + if( mpShowWindow ) + { + if ( maPresSettings.mbShowPauseLogo ) + { + Graphic aGraphic(SfxApplication::GetApplicationLogo(360)); + mpShowWindow->SetPauseMode( maPresSettings.mnPauseTimeout, &aGraphic ); + } + else + mpShowWindow->SetPauseMode( maPresSettings.mnPauseTimeout ); + } + } + else + { + displaySlideIndex( 0 ); + } + } + else + { + if( mpShowWindow ) + { + mpShowWindow->SetEndMode(); + if( !mpViewShell->GetDoc()->IsStartWithPresentation() ) + pause(); + } + } + } + } + } +} + +void SAL_CALL SlideshowImpl::gotoPreviousSlide( ) +{ + gotoPreviousSlide(false); +} + +void SlideshowImpl::gotoPreviousSlide (const bool bSkipAllMainSequenceEffects) +{ + SolarMutexGuard aSolarGuard; + + if( !(mxShow.is() && mpSlideController) ) + return; + + try + { + if( mbIsPaused ) + resume(); + + const ShowWindowMode eMode = mpShowWindow->GetShowWindowMode(); + if( eMode == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow( mpSlideController->getCurrentSlideIndex() ); + } + else if( (eMode == SHOWWINDOWMODE_PAUSE) || (eMode == SHOWWINDOWMODE_BLANK) ) + { + mpShowWindow->RestartShow(); + } + else + { + if( mpSlideController->previousSlide()) + displayCurrentSlide(bSkipAllMainSequenceEffects); + else if (bSkipAllMainSequenceEffects) + { + // We could not go to the previous slide (probably because + // the current slide is already the first one). We still + // have to call displayCurrentSlide because the calling + // slideshow can not determine whether there is a previous + // slide or not and has already prepared for a slide change. + // This slide change has to be completed now, even when + // changing to the same slide. + // Note that in this special case we do NOT pass + // bSkipAllMainSequenceEffects because we display the same + // slide as before and do not want to show all its effects. + displayCurrentSlide(); + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::gotoPreviousSlide()" ); + } +} + +void SAL_CALL SlideshowImpl::gotoLastSlide() +{ + SolarMutexGuard aSolarGuard; + + if( !mpSlideController ) + return; + + if( mbIsPaused ) + resume(); + + const sal_Int32 nLastSlideIndex = mpSlideController->getSlideIndexCount() - 1; + if( nLastSlideIndex >= 0 ) + { + if( mpShowWindow->GetShowWindowMode() == SHOWWINDOWMODE_END ) + { + mpShowWindow->RestartShow( nLastSlideIndex ); + } + else + { + displaySlideIndex( nLastSlideIndex ); + } + } +} + +void SAL_CALL SlideshowImpl::gotoBookmark( const OUString& rBookmark ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + sal_Int32 nSlideNumber = getSlideNumberForBookmark( rBookmark ); + if( nSlideNumber != -1 ) + displaySlideNumber( nSlideNumber ); +} + +void SAL_CALL SlideshowImpl::gotoSlide( const Reference< XDrawPage >& xSlide ) +{ + SolarMutexGuard aSolarGuard; + + if( !(mpSlideController && xSlide.is()) ) + return; + + if( mbIsPaused ) + resume(); + + const sal_Int32 nSlideCount = mpSlideController->getSlideNumberCount(); + for( sal_Int32 nSlide = 0; nSlide < nSlideCount; nSlide++ ) + { + if( mpSlideController->getSlideByNumber( nSlide ) == xSlide ) + { + displaySlideNumber( nSlide ); + } + } +} + +void SAL_CALL SlideshowImpl::gotoSlideIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aSolarGuard; + + if( mbIsPaused ) + resume(); + + displaySlideIndex( nIndex ); +} + +void SAL_CALL SlideshowImpl::stopSound( ) +{ + SolarMutexGuard aSolarGuard; + + try + { + if( mxPlayer.is() ) + { + mxPlayer->stop(); + mxPlayer.clear(); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideshowImpl::stopSound()" ); + } +} + +// XIndexAccess + +::sal_Int32 SAL_CALL SlideshowImpl::getCount( ) +{ + return getSlideCount(); +} + +css::uno::Any SAL_CALL SlideshowImpl::getByIndex( ::sal_Int32 Index ) +{ + return Any( getSlideByIndex( Index ) ); +} + +css::uno::Type SAL_CALL SlideshowImpl::getElementType( ) +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SlideshowImpl::hasElements( ) +{ + return getSlideCount() != 0; +} + +Reference< XSlideShow > SAL_CALL SlideshowImpl::getSlideShow() +{ + return mxShow; +} + +PresentationSettingsEx::PresentationSettingsEx( const PresentationSettingsEx& r ) +: PresentationSettings( r ) +, mbRehearseTimings(r.mbRehearseTimings) +, mbPreview(r.mbPreview) +, mpParentWindow( nullptr ) +{ +} + +PresentationSettingsEx::PresentationSettingsEx( PresentationSettings const & r ) +: PresentationSettings( r ) +, mbRehearseTimings(false) +, mbPreview(false) +, mpParentWindow(nullptr) +{ +} + +void PresentationSettingsEx::SetArguments( const Sequence< PropertyValue >& rArguments ) +{ + for( const PropertyValue& rValue : rArguments ) + { + SetPropertyValue( rValue.Name, rValue.Value ); + } +} + +void PresentationSettingsEx::SetPropertyValue( std::u16string_view rProperty, const Any& rValue ) +{ + if ( rProperty == u"RehearseTimings" ) + { + if( rValue >>= mbRehearseTimings ) + return; + } + else if ( rProperty == u"Preview" ) + { + if( rValue >>= mbPreview ) + return; + } + else if ( rProperty == u"AnimationNode" ) + { + if( rValue >>= mxAnimationNode ) + return; + } + else if ( rProperty == u"ParentWindow" ) + { + Reference< XWindow > xWindow; + if( rValue >>= xWindow ) + { + mpParentWindow = xWindow.is() ? VCLUnoHelper::GetWindow( xWindow ) + : nullptr; + return; + } + } + else if ( rProperty == u"AllowAnimations" ) + { + if( rValue >>= mbAnimationAllowed ) + return; + } + else if ( rProperty == u"FirstPage" ) + { + OUString aPresPage; + if( rValue >>= aPresPage ) + { + maPresPage = getUiNameFromPageApiNameImpl(aPresPage); + mbCustomShow = false; + mbAll = false; + return; + } + else + { + if( rValue >>= mxStartPage ) + return; + } + } + else if ( rProperty == u"IsAlwaysOnTop" ) + { + if( rValue >>= mbAlwaysOnTop ) + return; + } + else if ( rProperty == u"IsAutomatic" ) + { + if( rValue >>= mbManual ) + return; + } + else if ( rProperty == u"IsEndless" ) + { + if( rValue >>= mbEndless ) + return; + } + else if ( rProperty == u"IsFullScreen" ) + { + if( rValue >>= mbFullScreen ) + return; + } + else if ( rProperty == u"IsMouseVisible" ) + { + if( rValue >>= mbMouseVisible ) + return; + } + else if ( rProperty == u"Pause" ) + { + sal_Int32 nPause = -1; + if( (rValue >>= nPause) && (nPause >= 0) ) + { + mnPauseTimeout = nPause; + return; + } + } + else if ( rProperty == u"UsePen" ) + { + if( rValue >>= mbMouseAsPen ) + return; + } + throw IllegalArgumentException(); +} + +// XAnimationListener + +SlideShowListenerProxy::SlideShowListenerProxy( const rtl::Reference< SlideshowImpl >& xController, const css::uno::Reference< css::presentation::XSlideShow >& xSlideShow ) +: maListeners( m_aMutex ) +, mxController( xController ) +, mxSlideShow( xSlideShow ) +{ +} + +SlideShowListenerProxy::~SlideShowListenerProxy() +{ +} + +void SlideShowListenerProxy::addAsSlideShowListener() +{ + if( mxSlideShow.is() ) + { + Reference< XSlideShowListener > xSlideShowListener( this ); + mxSlideShow->addSlideShowListener( xSlideShowListener ); + } +} + +void SlideShowListenerProxy::removeAsSlideShowListener() +{ + if( mxSlideShow.is() ) + { + Reference< XSlideShowListener > xSlideShowListener( this ); + mxSlideShow->removeSlideShowListener( xSlideShowListener ); + } +} + +void SlideShowListenerProxy::addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + if( mxSlideShow.is() ) + { + Reference< XShapeEventListener > xListener( this ); + mxSlideShow->addShapeEventListener( xListener, xShape ); + } +} + +void SlideShowListenerProxy::removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ) +{ + if( mxSlideShow.is() ) + { + Reference< XShapeEventListener > xListener( this ); + mxSlideShow->removeShapeEventListener( xListener, xShape ); + } +} + +void SlideShowListenerProxy::addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener ) +{ + maListeners.addInterface(xListener); +} + +void SlideShowListenerProxy::removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& xListener ) +{ + maListeners.removeInterface(xListener); +} + +void SAL_CALL SlideShowListenerProxy::beginEvent( const Reference< XAnimationNode >& xNode ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference const& xListener) { + return xListener->beginEvent(xNode); + } ); + } +} + +void SAL_CALL SlideShowListenerProxy::endEvent( const Reference< XAnimationNode >& xNode ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference const& xListener) { + return xListener->endEvent(xNode); + } ); + } +} + +void SAL_CALL SlideShowListenerProxy::repeat( const Reference< XAnimationNode >& xNode, ::sal_Int32 nRepeat ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference const& xListener) { + return xListener->repeat(xNode, nRepeat); + } ); + } +} + +// css::presentation::XSlideShowListener: + +void SAL_CALL SlideShowListenerProxy::paused( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference const& xListener) + { + xListener->paused(); + }); +} + +void SAL_CALL SlideShowListenerProxy::resumed( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference const& xListener) + { + xListener->resumed(); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideTransitionStarted( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference const& xListener) + { + xListener->slideTransitionStarted(); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideTransitionEnded( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference const& xListener) + { + xListener->slideTransitionEnded (); + }); +} + +void SAL_CALL SlideShowListenerProxy::slideAnimationsEnded( ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + + maListeners.forEach( + [](uno::Reference const& xListener) + { + xListener->slideAnimationsEnded (); + }); +} + +void SlideShowListenerProxy::slideEnded(sal_Bool bReverse) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference const& xListener) { + return xListener->slideEnded(bReverse); + } ); + } + } + + { + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->slideEnded(bReverse); + } +} + +void SlideShowListenerProxy::hyperLinkClicked( OUString const& aHyperLink ) +{ + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if( maListeners.getLength() >= 0 ) + { + maListeners.forEach( + [&] (Reference const& xListener) { + return xListener->hyperLinkClicked(aHyperLink); + } ); + } + } + + { + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->hyperLinkClicked(aHyperLink); + } +} + +// XEventListener + +void SAL_CALL SlideShowListenerProxy::disposing( const css::lang::EventObject& aDisposeEvent ) +{ + maListeners.disposeAndClear( aDisposeEvent ); + mxController.clear(); + mxSlideShow.clear(); +} + +// XShapeEventListener + +void SAL_CALL SlideShowListenerProxy::click( const Reference< XShape >& xShape, const css::awt::MouseEvent& /*aOriginalEvent*/ ) +{ + SolarMutexGuard aSolarGuard; + if( mxController.is() ) + mxController->click(xShape ); +} + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowimpl.hxx b/sd/source/ui/slideshow/slideshowimpl.hxx new file mode 100644 index 000000000..eeec8a3fd --- /dev/null +++ b/sd/source/ui/slideshow/slideshowimpl.hxx @@ -0,0 +1,342 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "showwindow.hxx" + +#include + +namespace com::sun::star::frame { class XModel; } +namespace com::sun::star::media { class XPlayer; } +namespace sd { class DrawDocShell; } +namespace sd { class ViewShell; } + +class SfxBindings; +class SfxDispatcher; +class SfxViewFrame; +class StarBASIC; + +namespace sd +{ +class SlideShowView; +class AnimationSlideController; +class PaneHider; + +struct PresentationSettingsEx : public PresentationSettings +{ + bool mbRehearseTimings; + bool mbPreview; + VclPtr mpParentWindow; + css::uno::Reference< css::drawing::XDrawPage > mxStartPage; + css::uno::Reference< css::animations::XAnimationNode > mxAnimationNode; + + PresentationSettingsEx( const PresentationSettingsEx& ); + explicit PresentationSettingsEx( PresentationSettings const & ); + + /// @throws css::lang::IllegalArgumentException + void SetArguments( const css::uno::Sequence< css::beans::PropertyValue >& rArguments ); + + /// @throws css::lang::IllegalArgumentException + void SetPropertyValue( std::u16string_view rProperty, const css::uno::Any& rValue ); +}; + +struct WrappedShapeEventImpl +{ + css::presentation::ClickAction meClickAction; + sal_Int32 mnVerb; + OUString maStrBookmark; + WrappedShapeEventImpl() : meClickAction( css::presentation::ClickAction_NONE ), mnVerb( 0 ) {}; +}; + +typedef std::shared_ptr< WrappedShapeEventImpl > WrappedShapeEventImplPtr; + +class SlideShowListenerProxy : private ::cppu::BaseMutex, + public ::cppu::WeakImplHelper< css::presentation::XSlideShowListener, css::presentation::XShapeEventListener > +{ +public: + SlideShowListenerProxy( const rtl::Reference< SlideshowImpl >& xController, const css::uno::Reference< css::presentation::XSlideShow >& xSlideShow ); + virtual ~SlideShowListenerProxy() override; + + void addAsSlideShowListener(); + void removeAsSlideShowListener(); + + void addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ); + void removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ); + + void addShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ); + void removeShapeEventListener( const css::uno::Reference< css::drawing::XShape >& xShape ); + + // css::animations::XAnimationListener + virtual void SAL_CALL beginEvent( const css::uno::Reference< css::animations::XAnimationNode >& Node ) override; + virtual void SAL_CALL endEvent( const css::uno::Reference< css::animations::XAnimationNode >& Node ) override; + virtual void SAL_CALL repeat( const css::uno::Reference< css::animations::XAnimationNode >& Node, ::sal_Int32 Repeat ) override; + + // css::presentation::XSlideShowListener: + virtual void SAL_CALL paused() override; + virtual void SAL_CALL resumed() override; + virtual void SAL_CALL slideTransitionStarted() override; + virtual void SAL_CALL slideTransitionEnded() override; + virtual void SAL_CALL slideAnimationsEnded() override; + virtual void SAL_CALL slideEnded(sal_Bool bReverse) override; + virtual void SAL_CALL hyperLinkClicked(const OUString & hyperLink) override; + + // css::lang::XEventListener: + virtual void SAL_CALL disposing(const css::lang::EventObject & Source) override; + + // css::presentation::XShapeEventListener: + virtual void SAL_CALL click(const css::uno::Reference< css::drawing::XShape > & xShape, const css::awt::MouseEvent & aOriginalEvent) override; + +private: + ::comphelper::OInterfaceContainerHelper3 maListeners; + rtl::Reference< SlideshowImpl > mxController; + css::uno::Reference< css::presentation::XSlideShow > mxSlideShow; +}; + +typedef comphelper::WeakComponentImplHelper< css::presentation::XSlideShowController, css::container::XIndexAccess > SlideshowImplBase; + +class SlideshowImpl final : public SlideshowImplBase +{ +friend class SlideShow; +friend class SlideShowView; + +public: + explicit SlideshowImpl( const css::uno::Reference< css::presentation::XPresentation2 >& xPresentation, ViewShell* pViewSh, ::sd::View* pView, SdDrawDocument* pDoc, vcl::Window* pParentWindow); + + // css::presentation::XSlideShowController: + virtual sal_Bool SAL_CALL getAlwaysOnTop() override; + virtual void SAL_CALL setAlwaysOnTop( sal_Bool _alwaysontop ) override; + virtual sal_Bool SAL_CALL getMouseVisible() override; + virtual void SAL_CALL setMouseVisible( sal_Bool _mousevisible ) override; + virtual sal_Bool SAL_CALL getUsePen() override; + virtual void SAL_CALL setUsePen( sal_Bool _usepen ) override; + virtual ::sal_Int32 SAL_CALL getPenColor() override; + virtual void SAL_CALL setPenColor( ::sal_Int32 _pencolor ) override; + virtual double SAL_CALL getPenWidth() override; + virtual void SAL_CALL setPenWidth( double dStrokeWidth ) override; + /// @throws css::uno::RuntimeException + virtual void SAL_CALL setEraseAllInk( sal_Bool bEraseAllInk ) override; + virtual sal_Bool SAL_CALL isRunning( ) override; + virtual ::sal_Int32 SAL_CALL getSlideCount( ) override; + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getSlideByIndex( ::sal_Int32 Index ) override; + virtual void SAL_CALL addSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ) override; + virtual void SAL_CALL removeSlideShowListener( const css::uno::Reference< css::presentation::XSlideShowListener >& Listener ) override; + virtual void SAL_CALL gotoNextEffect( ) override; + virtual void SAL_CALL gotoPreviousEffect( ) override; + virtual void SAL_CALL gotoFirstSlide( ) override; + virtual void SAL_CALL gotoNextSlide( ) override; + virtual void SAL_CALL gotoPreviousSlide( ) override; + virtual void SAL_CALL gotoLastSlide( ) override; + virtual void SAL_CALL gotoBookmark( const OUString& Bookmark ) override; + virtual void SAL_CALL gotoSlide( const css::uno::Reference< css::drawing::XDrawPage >& Page ) override; + virtual void SAL_CALL gotoSlideIndex( ::sal_Int32 Index ) override; + virtual void SAL_CALL stopSound( ) override; + virtual void SAL_CALL pause( ) override; + virtual void SAL_CALL resume( ) override; + virtual sal_Bool SAL_CALL isPaused( ) override; + virtual void SAL_CALL blankScreen( ::sal_Int32 Color ) override; + virtual void SAL_CALL activate( ) override; + virtual void SAL_CALL deactivate( ) override; + virtual sal_Bool SAL_CALL isActive( ) override; + virtual css::uno::Reference< css::drawing::XDrawPage > SAL_CALL getCurrentSlide( ) override; + virtual ::sal_Int32 SAL_CALL getCurrentSlideIndex( ) override; + virtual ::sal_Int32 SAL_CALL getNextSlideIndex( ) override; + virtual sal_Bool SAL_CALL isEndless( ) override; + virtual sal_Bool SAL_CALL isFullScreen( ) override; + virtual css::uno::Reference< css::presentation::XSlideShow > SAL_CALL getSlideShow( ) override; + + // XIndexAccess + virtual ::sal_Int32 SAL_CALL getCount( ) override; + virtual css::uno::Any SAL_CALL getByIndex( ::sal_Int32 Index ) override; + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // will be called from the SlideShowListenerProxy when this event is fired from the XSlideShow + void slideEnded(const bool bReverse); + /// @throws css::uno::RuntimeException + void hyperLinkClicked(const OUString & hyperLink); + void click(const css::uno::Reference< css::drawing::XShape > & xShape); + bool swipe(const CommandSwipeData &rSwipeData); + bool longpress(const CommandLongPressData& rLongPressData); + + /// ends the presentation async + void endPresentation(); + + ViewShell* getViewShell() const { return mpViewShell; } + + void paint(); + bool keyInput(const KeyEvent& rKEvt); + void mouseButtonUp(const MouseEvent& rMEvt); + +private: + SlideshowImpl(SlideshowImpl const &) = delete; + void operator =(SlideshowImpl const &) = delete; + + virtual ~SlideshowImpl() override; + + // override WeakComponentImplHelperBase::disposing() + // This function is called upon disposing the component, + // if your component needs special work when it becomes + // disposed, do it here. + virtual void disposing(std::unique_lock&) override; + + // internal + bool startShow( PresentationSettingsEx const * pPresSettings ); + bool startPreview( + const css::uno::Reference< css::drawing::XDrawPage >& xDrawPage, + const css::uno::Reference< css::animations::XAnimationNode >& xAnimationNode, + vcl::Window* pParent ); + + /** forces an async call to update in the main thread */ + void startUpdateTimer(); + + void update(); + + void createSlideList( bool bAll, std::u16string_view rPresSlide ); + + void displayCurrentSlide (const bool bSkipAllMainSequenceEffects = false); + + void displaySlideNumber( sal_Int32 nSlide ); + void displaySlideIndex( sal_Int32 nIndex ); + sal_Int32 getCurrentSlideNumber() const; + bool isInputFreezed() const { return mbInputFreeze; } + + void jumpToBookmark( const OUString& sBookmark ); + + void hideChildWindows(); + void showChildWindows(); + + void resize( const Size& rSize ); + + void setActiveXToolbarsVisible( bool bVisible ); + + DECL_LINK( updateHdl, Timer *, void ); + DECL_LINK( ReadyForNextInputHdl, Timer *, void ); + DECL_LINK( endPresentationHdl, void*, void ); + void ContextMenuSelectHdl(std::string_view rIdent); + DECL_LINK( ContextMenuHdl, void*, void ); + DECL_LINK( deactivateHdl, Timer *, void ); + DECL_LINK( EventListenerHdl, VclSimpleEvent&, void ); + + /** called only by the slideshow view when the first paint event occurs. + This actually starts the slideshow. */ + void onFirstPaint(); + + ::tools::Long getRestoreSlide() const { return mnRestoreSlide; } + +private: + bool startShowImpl( + const css::uno::Sequence< css::beans::PropertyValue >& aProperties ); + + SfxViewFrame* getViewFrame() const; + SfxDispatcher* getDispatcher() const; + SfxBindings* getBindings() const; + + sal_Int32 getSlideNumberForBookmark( const OUString& rStrBookmark ); + + void removeShapeEvents(); + void registerShapeEvents( sal_Int32 nSlideNumber ); + /// @throws css::uno::Exception + void registerShapeEvents( css::uno::Reference< css::drawing::XShapes > const & xShapes ); + + static css::uno::Reference< css::presentation::XSlideShow > createSlideShow(); + + static void setAutoSaveState( bool bOn ); + void gotoPreviousSlide (const bool bSkipAllMainSequenceEffects); + + /** Called by our maUpdateTimer's updateHdl handler this method is + responsible to call the slideshow update() method and, depending on + its return value, wait for a certain amount of time before another + call to update() is scheduled. + */ + void updateSlideShow(); + + css::uno::Reference< css::presentation::XSlideShow > mxShow; + rtl::Reference mxView; + css::uno::Reference< css::frame::XModel > mxModel; + + Timer maUpdateTimer; + Timer maInputFreezeTimer; + Timer maDeactivateTimer; + + ::sd::View* mpView; + ViewShell* mpViewShell; + DrawDocShell* mpDocSh; + SdDrawDocument* mpDoc; + + VclPtr mpParentWindow; + VclPtr mpShowWindow; + + std::shared_ptr< AnimationSlideController > mpSlideController; + + ::tools::Long mnRestoreSlide; + Point maPopupMousePos; + Size maPresSize; + AnimationMode meAnimationMode; + OUString maCharBuffer; + VclPtr< ::sd::Window> mpOldActiveWindow; + Link maStarBASICGlobalErrorHdl; + ::tools::ULong mnChildMask; + bool mbDisposed; + bool mbAutoSaveWasOn; + bool mbRehearseTimings; + bool mbIsPaused; + bool mbWasPaused; // used to cache pause state during context menu + bool mbInputFreeze; + bool mbActive; + + PresentationSettings maPresSettings; + sal_Int32 mnUserPaintColor; + + bool mbUsePen; + double mdUserPaintStrokeWidth; + + std::map< css::uno::Reference< css::drawing::XShape >, WrappedShapeEventImplPtr > + maShapeEventMap; + + css::uno::Reference< css::drawing::XDrawPage > mxPreviewDrawPage; + css::uno::Reference< css::animations::XAnimationNode > mxPreviewAnimationNode; + + css::uno::Reference< css::media::XPlayer > mxPlayer; + + ::std::unique_ptr mpPaneHider; + + ImplSVEvent * mnEndShowEvent; + ImplSVEvent * mnContextMenuEvent; + + css::uno::Reference< css::presentation::XPresentation2 > mxPresentation; + ::rtl::Reference< SlideShowListenerProxy > mxListenerProxy; +}; + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowviewimpl.cxx b/sd/source/ui/slideshow/slideshowviewimpl.cxx new file mode 100644 index 000000000..d6addc3f8 --- /dev/null +++ b/sd/source/ui/slideshow/slideshowviewimpl.cxx @@ -0,0 +1,626 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "slideshowviewimpl.hxx" +#include "slideshowimpl.hxx" +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::WeakReference; +using ::com::sun::star::uno::Exception; + +using namespace ::com::sun::star; + +namespace sd +{ + +void SlideShowViewMouseListeners::notify( std::unique_lock& rGuard, const WrappedMouseEvent& rEvent ) +{ + forEach(rGuard, + [&rEvent] (const Reference& rListener) + { + switch( rEvent.meType ) + { + case WrappedMouseEvent::PRESSED: + rListener->mousePressed( rEvent.maEvent ); + break; + + case WrappedMouseEvent::RELEASED: + rListener->mouseReleased( rEvent.maEvent ); + break; + + case WrappedMouseEvent::ENTERED: + rListener->mouseEntered( rEvent.maEvent ); + break; + + case WrappedMouseEvent::EXITED: + rListener->mouseExited( rEvent.maEvent ); + break; + } + }); +} + + +void SlideShowViewMouseMotionListeners::notify( std::unique_lock& rGuard,const WrappedMouseMotionEvent& rEvent ) +{ + forEach(rGuard, + [&rEvent] (const Reference< awt::XMouseMotionListener >& rListener) + { + switch( rEvent.meType ) + { + case WrappedMouseMotionEvent::DRAGGED: + rListener->mouseDragged( rEvent.maEvent ); + break; + + case WrappedMouseMotionEvent::MOVED: + rListener->mouseMoved( rEvent.maEvent ); + break; + } + }); +} + +// SlideShowView +SlideShowView::SlideShowView( ShowWindow& rOutputWindow, + SdDrawDocument* pDoc, + AnimationMode eAnimationMode, + SlideshowImpl* pSlideShow, + bool bFullScreen ) +: mpCanvas( ::cppcanvas::VCLFactory::createSpriteCanvas( rOutputWindow ) ), + mxWindow( VCLUnoHelper::GetInterface( &rOutputWindow ), uno::UNO_SET_THROW ), + mxWindowPeer( mxWindow, uno::UNO_QUERY_THROW ), + mpSlideShow( pSlideShow ), + mrOutputWindow( rOutputWindow ), + mpDoc( pDoc ), + mbIsMouseMotionListener( false ), + meAnimationMode( eAnimationMode ), + mbFirstPaint( true ), + mbMousePressedEaten( false ) +{ + mxWindow->addWindowListener( this ); + mxWindow->addMouseListener( this ); + + mxPointer = awt::Pointer::create( ::comphelper::getProcessComponentContext() ); + + getTransformation(); + + // #i48939# only switch on kind of hacky scroll optimization, when + // running fullscreen. this minimizes the probability that other + // windows partially cover the show. + if( bFullScreen ) + { + try + { + Reference< beans::XPropertySet > xCanvasProps( getCanvas(), + uno::UNO_QUERY_THROW ); + xCanvasProps->setPropertyValue("UnsafeScrolling", + uno::Any( true ) ); + } + catch( uno::Exception& ) + { + } + } + + mTranslationOffset.Width = 0; + mTranslationOffset.Height = 0; +} + +// Dispose all internal references +void SlideShowView::disposing(std::unique_lock& rGuard) +{ + mpSlideShow = nullptr; + + // deregister listeners + if( mxWindow.is() ) + { + mxWindow->removeWindowListener( this ); + mxWindow->removeMouseListener( this ); + + if( mbIsMouseMotionListener ) + mxWindow->removeMouseMotionListener( this ); + } + + mpCanvas.reset(); + mxWindow.clear(); + + // clear all listener containers + disposingImpl(rGuard); +} + +// Disposing our broadcaster +void SAL_CALL SlideShowView::disposing( const lang::EventObject& ) +{ + std::unique_lock aGuard( m_aMutex ); + + disposingImpl(aGuard); +} + +// Disposing our broadcaster +void SlideShowView::disposingImpl(std::unique_lock& rGuard) +{ + // notify all listeners that _we_ are going down (send a disposing()), + // then delete listener containers: + lang::EventObject const evt( static_cast(this) ); + if (!maViewListeners.empty()) + { + auto tmp = std::move(maViewListeners); + rGuard.unlock(); + for( const auto& rxListener : tmp ) + { + Reference< util::XModifyListener > xListener( rxListener ); + if( xListener.is() ) + xListener->disposing( evt ); + } + rGuard.lock(); + } + if (maPaintListeners.getLength(rGuard)) + { + maPaintListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } + if (maMouseListeners.getLength(rGuard)) + { + maMouseListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } + if (maMouseMotionListeners.getLength(rGuard)) + { + maMouseMotionListeners.disposeAndClear( rGuard, evt ); + rGuard.lock(); + } +} + +void SlideShowView::paint( const awt::PaintEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + + if( mbFirstPaint ) + { + mbFirstPaint = false; + SlideshowImpl* pSlideShow = mpSlideShow; + aGuard.unlock(); + if( pSlideShow ) + pSlideShow->onFirstPaint(); + } + else + { + // Change event source, to enable listeners to match event + // with view + awt::PaintEvent aEvent( e ); + aEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + maPaintListeners.notifyEach( aGuard, &css::awt::XPaintListener::windowPaint, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +// XSlideShowView methods +Reference< rendering::XSpriteCanvas > SAL_CALL SlideShowView::getCanvas( ) +{ + std::unique_lock aGuard( m_aMutex ); + + return mpCanvas ? mpCanvas->getUNOSpriteCanvas() : Reference< rendering::XSpriteCanvas >(); +} + +void SAL_CALL SlideShowView::clear() +{ + // paint background in black + std::unique_lock aGuard( m_aMutex ); + SolarMutexGuard aSolarGuard; + + // fill the bounds rectangle in black + + const Size aWindowSize( mrOutputWindow.GetSizePixel() ); + + ::basegfx::B2DPolygon aPoly( ::basegfx::utils::createPolygonFromRect( + ::basegfx::B2DRectangle(0.0,0.0, + aWindowSize.Width(), + aWindowSize.Height() ) ) ); + ::cppcanvas::PolyPolygonSharedPtr pPolyPoly( + ::cppcanvas::BaseGfxFactory::createPolyPolygon( mpCanvas, aPoly ) ); + + if( pPolyPoly ) + { + pPolyPoly->setRGBAFillColor( 0x000000FFU ); + pPolyPoly->draw(); + } +} + +geometry::IntegerSize2D SAL_CALL SlideShowView::getTranslationOffset( ) +{ + return mTranslationOffset; +} + +geometry::AffineMatrix2D SAL_CALL SlideShowView::getTransformation( ) +{ + std::unique_lock aGuard( m_aMutex ); + SolarMutexGuard aSolarGuard; + + const Size& rTmpSize( mrOutputWindow.GetSizePixel() ); + + if (rTmpSize.IsEmpty()) + { + return geometry::AffineMatrix2D (1,0,0,0,1,0); + } + + const Size aWindowSize( mrOutputWindow.GetSizePixel() ); + Size aOutputSize( aWindowSize ); + + if( meAnimationMode != ANIMATIONMODE_SHOW ) + { + aOutputSize.setWidth( static_cast<::tools::Long>( aOutputSize.Width() / 1.03 ) ); + aOutputSize.setHeight( static_cast<::tools::Long>( aOutputSize.Height() / 1.03 ) ); + } + + SdPage* pP = mpDoc->GetSdPage( 0, PageKind::Standard ); + Size aPageSize( pP->GetSize() ); + + const double page_ratio = static_cast(aPageSize.Width()) / static_cast(aPageSize.Height()); + const double output_ratio = static_cast(aOutputSize.Width()) / static_cast(aOutputSize.Height()); + + if( page_ratio > output_ratio ) + { + aOutputSize.setHeight( ( aOutputSize.Width() * aPageSize.Height() ) / aPageSize.Width() ); + } + else if( page_ratio < output_ratio ) + { + aOutputSize.setWidth( ( aOutputSize.Height() * aPageSize.Width() ) / aPageSize.Height() ); + } + + Point aOutputOffset( ( aWindowSize.Width() - aOutputSize.Width() ) >> 1, + ( aWindowSize.Height() - aOutputSize.Height() ) >> 1 ); + + // Reduce available width by one, as the slides might actually + // render one pixel wider and higher as aPageSize below specifies + // (when shapes of page size have visible border lines) + aOutputSize.AdjustWidth( -1 ); + aOutputSize.AdjustHeight( -1 ); + + // Record mTranslationOffset + mTranslationOffset.Height = aOutputOffset.Y(); + mTranslationOffset.Width = aOutputOffset.X(); + + // scale presentation into available window rect (minus 10%); center in the window + const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createScaleTranslateB2DHomMatrix( + aOutputSize.Width(), aOutputSize.Height(), aOutputOffset.X(), aOutputOffset.Y())); + + geometry::AffineMatrix2D aRes; + + return ::basegfx::unotools::affineMatrixFromHomMatrix( aRes, aMatrix ); +} + +void SAL_CALL SlideShowView::addTransformationChangedListener( const Reference< util::XModifyListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + WeakReference< util::XModifyListener > xWeak( xListener ); + if( std::find( maViewListeners.begin(), maViewListeners.end(), xWeak ) == maViewListeners.end() ) + maViewListeners.push_back( xWeak ); +} + +void SAL_CALL SlideShowView::removeTransformationChangedListener( const Reference< util::XModifyListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + WeakReference< util::XModifyListener > xWeak( xListener ); + auto aIter( std::find( maViewListeners.begin(), maViewListeners.end(), xWeak ) ); + if( aIter != maViewListeners.end() ) + maViewListeners.erase( aIter ); +} + +void SAL_CALL SlideShowView::addPaintListener( const Reference< awt::XPaintListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maPaintListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removePaintListener( const Reference< awt::XPaintListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maPaintListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::addMouseListener( const Reference< awt::XMouseListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removeMouseListener( const Reference< awt::XMouseListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseListeners.removeInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::addMouseMotionListener( const Reference< awt::XMouseMotionListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + + if( !mbIsMouseMotionListener && mxWindow.is() ) + { + // delay motion event registration, until we really + // need it + mbIsMouseMotionListener = true; + mxWindow->addMouseMotionListener( this ); + } + + maMouseMotionListeners.addInterface( aGuard, xListener ); +} + +void SAL_CALL SlideShowView::removeMouseMotionListener( const Reference< awt::XMouseMotionListener >& xListener ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (!m_bDisposed) + maMouseMotionListeners.removeInterface( aGuard, xListener ); + + // TODO(P1): Might be nice to deregister for mouse motion + // events, when the last listener is gone. +} + +void SAL_CALL SlideShowView::setMouseCursor( sal_Int16 nPointerShape ) +{ + std::unique_lock aGuard( m_aMutex ); + + // forward to window + if( mxPointer.is() ) + mxPointer->setType( nPointerShape ); + + if( mxWindowPeer.is() ) + mxWindowPeer->setPointer( mxPointer ); +} + +awt::Rectangle SAL_CALL SlideShowView::getCanvasArea( ) +{ + awt::Rectangle aRectangle; + + if( mxWindow.is() ) + return mxWindow->getPosSize(); + + aRectangle.X = aRectangle.Y = aRectangle.Width = aRectangle.Height = 0; + + return aRectangle; +} + +void SlideShowView::updateimpl( std::unique_lock& rGuard, SlideshowImpl* pSlideShow ) +{ + if( !pSlideShow ) + return; + + ::rtl::Reference< SlideshowImpl > xKeepAlive( pSlideShow ); + + if( mbFirstPaint ) + { + mbFirstPaint = false; + SlideshowImpl* pTmpSlideShow = mpSlideShow; + rGuard.unlock(); + if( pTmpSlideShow ) + pTmpSlideShow->onFirstPaint(); + } else + rGuard.unlock(); + + pSlideShow->startUpdateTimer(); +} + +// XWindowListener methods +void SAL_CALL SlideShowView::windowResized( const awt::WindowEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + + if (m_bDisposed) + return; + + if (!maViewListeners.empty()) + { + // Change event source, to enable listeners to match event + // with view + awt::WindowEvent aEvent( e ); + aEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + auto aIter( maViewListeners.begin() ); + while( aIter != maViewListeners.end() ) + { + Reference< util::XModifyListener > xListener( *aIter ); + if( xListener.is() ) + { + aGuard.unlock(); + xListener->modified( aEvent ); + aGuard.lock(); + ++aIter; + } + else + { + aIter = maViewListeners.erase( aIter ); + } + } + } + + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::windowMoved( const awt::WindowEvent& ) +{ + // ignored +} + +void SAL_CALL SlideShowView::windowShown( const lang::EventObject& ) +{ + // ignored +} + +void SAL_CALL SlideShowView::windowHidden( const lang::EventObject& ) +{ + // ignored +} + +// XMouseListener implementation +void SAL_CALL SlideShowView::mousePressed( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + if( mpSlideShow && mpSlideShow->isInputFreezed() ) + { + mbMousePressedEaten = true; + } + else + { + mbMousePressedEaten = false; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::PRESSED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +void SAL_CALL SlideShowView::mouseReleased( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + if( mbMousePressedEaten ) + { + // if mouse button down was ignored, also ignore mouse button up + mbMousePressedEaten = false; + } + else if( mpSlideShow && !mpSlideShow->isInputFreezed() ) + { + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::RELEASED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! + } +} + +void SAL_CALL SlideShowView::mouseEntered( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::ENTERED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::mouseExited( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseEvent aEvent; + aEvent.meType = WrappedMouseEvent::EXITED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +// XMouseMotionListener implementation +void SAL_CALL SlideShowView::mouseDragged( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseMotionEvent aEvent; + aEvent.meType = WrappedMouseMotionEvent::DRAGGED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseMotionListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +void SAL_CALL SlideShowView::mouseMoved( const awt::MouseEvent& e ) +{ + std::unique_lock aGuard( m_aMutex ); + if (m_bDisposed) + return; + + // Change event source, to enable listeners to match event + // with view + WrappedMouseMotionEvent aEvent; + aEvent.meType = WrappedMouseMotionEvent::MOVED; + aEvent.maEvent = e; + aEvent.maEvent.Source = static_cast< ::cppu::OWeakObject* >( this ); + + maMouseMotionListeners.notify( aGuard, aEvent ); + updateimpl( aGuard, mpSlideShow ); // warning: clears guard! +} + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slideshow/slideshowviewimpl.hxx b/sd/source/ui/slideshow/slideshowviewimpl.hxx new file mode 100644 index 000000000..3a5018be4 --- /dev/null +++ b/sd/source/ui/slideshow/slideshowviewimpl.hxx @@ -0,0 +1,182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace com::sun::star::awt { class XPointer; } +namespace com::sun::star::awt { class XWindow; } +namespace com::sun::star::awt { class XWindowPeer; } +namespace com::sun::star::awt { struct WindowEvent; } +namespace com::sun::star::rendering { class XSpriteCanvas; } +class SdDrawDocument; + +namespace sd +{ + +struct WrappedMouseEvent : public css::lang::EventObject +{ + enum EventType + { + PRESSED, + RELEASED, + ENTERED, + EXITED + }; + + EventType meType; + css::awt::MouseEvent maEvent; +}; + +struct WrappedMouseMotionEvent : public css::lang::EventObject +{ + enum EventType + { + DRAGGED, + MOVED + }; + + EventType meType; + css::awt::MouseEvent maEvent; +}; + +// SlideShowViewPaintListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XPaintListener > SlideShowViewPaintListeners; + + +// SlideShowViewMouseListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XMouseListener > SlideShowViewMouseListeners_Base; + +class SlideShowViewMouseListeners : public SlideShowViewMouseListeners_Base +{ +public: + void notify(std::unique_lock& rGuard, const WrappedMouseEvent& rEvent); +}; + + +// SlideShowViewMouseMotionListeners +typedef ::comphelper::OInterfaceContainerHelper4< css::awt::XMouseMotionListener > SlideShowViewMouseMotionListeners_Base; + +class SlideShowViewMouseMotionListeners : public SlideShowViewMouseMotionListeners_Base +{ +public: + void notify( std::unique_lock& rGuard, const WrappedMouseMotionEvent& rEvent ); +}; + +// SlideShowView +class ShowWindow; +class SlideshowImpl; + +typedef comphelper::WeakComponentImplHelper< css::presentation::XSlideShowView, + css::awt::XWindowListener, + css::awt::XMouseListener, + css::awt::XMouseMotionListener > SlideShowView_Base; + +class SlideShowView final : public SlideShowView_Base +{ +public: + SlideShowView( ShowWindow& rOutputWindow, + SdDrawDocument* pDoc, + AnimationMode eAnimationMode, + SlideshowImpl* pSlideShow, + bool bFullScreen ); + + void ignoreNextMouseReleased() { mbMousePressedEaten = true; } + + /// Dispose all internal references + virtual void disposing(std::unique_lock&) override; + + /// Disposing our broadcaster + virtual void SAL_CALL disposing( const css::lang::EventObject& ) override; + + /// @throws css::uno::RuntimeException + void paint( const css::awt::PaintEvent& e ); + + // XSlideShowView methods + virtual css::uno::Reference< css::rendering::XSpriteCanvas > SAL_CALL getCanvas( ) override; + virtual void SAL_CALL clear( ) override; + virtual css::geometry::AffineMatrix2D SAL_CALL getTransformation( ) override; + virtual css::geometry::IntegerSize2D SAL_CALL getTranslationOffset( ) override; + virtual void SAL_CALL addTransformationChangedListener( const css::uno::Reference< css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL removeTransformationChangedListener( const css::uno::Reference< css::util::XModifyListener >& xListener ) override; + virtual void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override; + virtual void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override; + virtual void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override; + virtual void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override; + virtual void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override; + virtual void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override; + virtual void SAL_CALL setMouseCursor( sal_Int16 nPointerShape ) override; + virtual css::awt::Rectangle SAL_CALL getCanvasArea( ) override; + + // XWindowListener methods + virtual void SAL_CALL windowResized( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowMoved( const css::awt::WindowEvent& e ) override; + virtual void SAL_CALL windowShown( const css::lang::EventObject& e ) override; + virtual void SAL_CALL windowHidden( const css::lang::EventObject& e ) override; + + // XMouseListener implementation + virtual void SAL_CALL mousePressed( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseReleased( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseEntered( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseExited( const css::awt::MouseEvent& e ) override; + + // XMouseMotionListener implementation + virtual void SAL_CALL mouseDragged( const css::awt::MouseEvent& e ) override; + virtual void SAL_CALL mouseMoved( const css::awt::MouseEvent& e ) override; + +protected: + virtual ~SlideShowView() override {} + +private: + void updateimpl( std::unique_lock& rGuard, SlideshowImpl* pSlideShow ); + + void disposingImpl( std::unique_lock& ); + + ::cppcanvas::SpriteCanvasSharedPtr mpCanvas; + css::uno::Reference< css::awt::XWindow > mxWindow; + css::uno::Reference< css::awt::XWindowPeer > mxWindowPeer; + css::uno::Reference< css::awt::XPointer > mxPointer; + SlideshowImpl* mpSlideShow; + ShowWindow& mrOutputWindow; + std::vector< css::uno::WeakReference< css::util::XModifyListener > > + maViewListeners; + SlideShowViewPaintListeners maPaintListeners; + SlideShowViewMouseListeners maMouseListeners; + SlideShowViewMouseMotionListeners maMouseMotionListeners; + SdDrawDocument* mpDoc; + bool mbIsMouseMotionListener; + AnimationMode meAnimationMode; + bool mbFirstPaint; + bool mbMousePressedEaten; + css::geometry::IntegerSize2D mTranslationOffset; +}; + +} // namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx new file mode 100644 index 000000000..87c727408 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCache.cxx @@ -0,0 +1,550 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include "SlsBitmapCache.hxx" +#include "SlsCacheCompactor.hxx" +#include "SlsBitmapCompressor.hxx" +#include "SlsCacheConfiguration.hxx" + +#include + +// Define the default value for the maximal cache size that is used for +// previews that are currently not visible. The visible previews are all +// held in memory at all times. This default is used only when the +// configuration does not have a value. +const sal_Int32 MAXIMAL_CACHE_SIZE = 4*1024L*1024L; + +using namespace ::com::sun::star::uno; + +namespace sd::slidesorter::cache { + +class BitmapCache::CacheEntry +{ +public: + CacheEntry(const BitmapEx& rBitmap, sal_Int32 nLastAccessTime, bool bIsPrecious); + CacheEntry(sal_Int32 nLastAccessTime, bool bIsPrecious); + inline void Recycle (const CacheEntry& rEntry); + inline sal_Int32 GetMemorySize() const; + void Compress (const std::shared_ptr& rpCompressor); + inline void Decompress(); + + bool IsUpToDate() const { return mbIsUpToDate; } + void SetUpToDate (bool bIsUpToDate) { mbIsUpToDate = bIsUpToDate; } + sal_Int32 GetAccessTime() const { return mnLastAccessTime; } + void SetAccessTime (sal_Int32 nAccessTime) { mnLastAccessTime = nAccessTime; } + + const BitmapEx& GetPreview() const { return maPreview; } + inline void SetPreview (const BitmapEx& rPreview); + bool HasPreview() const; + + const BitmapEx& GetMarkedPreview() const { return maMarkedPreview; } + inline void SetMarkedPreview (const BitmapEx& rMarkePreview); + + bool HasReplacement() const { return (mpReplacement != nullptr); } + inline bool HasLosslessReplacement() const; + void Invalidate() { mpReplacement.reset(); mpCompressor.reset(); mbIsUpToDate = false; } + bool IsPrecious() const { return mbIsPrecious; } + void SetPrecious (bool bIsPrecious) { mbIsPrecious = bIsPrecious; } + +private: + BitmapEx maPreview; + BitmapEx maMarkedPreview; + std::shared_ptr mpReplacement; + std::shared_ptr mpCompressor; + bool mbIsUpToDate; + sal_Int32 mnLastAccessTime; + // When this flag is set then the bitmap is not modified by a cache + // compactor. + bool mbIsPrecious; +}; + +namespace { + +class CacheHash { +public: + size_t operator()(const BitmapCache::CacheKey& p) const + { return reinterpret_cast(p); } +}; + +} + +class BitmapCache::CacheBitmapContainer + : public std::unordered_map +{ +public: + CacheBitmapContainer() {} +}; + +namespace { + +typedef ::std::vector< + ::std::pair< ::sd::slidesorter::cache::BitmapCache::CacheKey, + ::sd::slidesorter::cache::BitmapCache::CacheEntry> + > SortableBitmapContainer; + + /** Compare elements of the bitmap cache according to their last access + time. + */ + class AccessTimeComparator + { + public: + bool operator () ( + const SortableBitmapContainer::value_type& e1, + const SortableBitmapContainer::value_type& e2) + { + return e1.second.GetAccessTime() < e2.second.GetAccessTime(); + } + }; + +} // end of anonymous namespace + +//===== BitmapCache ========================================================= + +BitmapCache::BitmapCache () + : mpBitmapContainer(new CacheBitmapContainer()), + mnNormalCacheSize(0), + mnPreciousCacheSize(0), + mnCurrentAccessTime(0), + mnMaximalNormalCacheSize(MAXIMAL_CACHE_SIZE), + mbIsFull(false) +{ + Any aCacheSize (CacheConfiguration::Instance()->GetValue("CacheSize")); + if (aCacheSize.has()) + aCacheSize >>= mnMaximalNormalCacheSize; + + mpCacheCompactor = CacheCompactor::Create(*this,mnMaximalNormalCacheSize); +} + +BitmapCache::~BitmapCache() +{ + Clear(); +} + +void BitmapCache::Clear() +{ + ::osl::MutexGuard aGuard (maMutex); + + mpBitmapContainer->clear(); + mnNormalCacheSize = 0; + mnPreciousCacheSize = 0; + mnCurrentAccessTime = 0; +} + +bool BitmapCache::HasBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + return (iEntry != mpBitmapContainer->end() + && (iEntry->second.HasPreview() || iEntry->second.HasReplacement())); +} + +bool BitmapCache::BitmapIsUpToDate (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + bool bIsUpToDate = false; + CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey)); + if (aIterator != mpBitmapContainer->end()) + bIsUpToDate = aIterator->second.IsUpToDate(); + + return bIsUpToDate; +} + +BitmapEx BitmapCache::GetBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry == mpBitmapContainer->end()) + { + // Create an empty bitmap for the given key that acts as placeholder + // until we are given the real one. Mark it as not being up to date. + SetBitmap(rKey, BitmapEx(), false); + iEntry = mpBitmapContainer->find(rKey); + iEntry->second.SetUpToDate(false); + } + else + { + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + + // Maybe we have to decompress the preview. + if ( ! iEntry->second.HasPreview() && iEntry->second.HasReplacement()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Decompress(); + UpdateCacheSize(iEntry->second, ADD); + } + } + return iEntry->second.GetPreview(); +} + +BitmapEx BitmapCache::GetMarkedBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + return iEntry->second.GetMarkedPreview(); + } + else + return BitmapEx(); +} + +void BitmapCache::ReleaseBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator aIterator (mpBitmapContainer->find(rKey)); + if (aIterator != mpBitmapContainer->end()) + { + UpdateCacheSize(aIterator->second, REMOVE); + mpBitmapContainer->erase(aIterator); + } +} + +bool BitmapCache::InvalidateBitmap (const CacheKey& rKey) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + iEntry->second.SetUpToDate(false); + + // When there is a preview then we release the replacement. The + // preview itself is kept until a new one is created. + if (iEntry->second.HasPreview()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Invalidate(); + UpdateCacheSize(iEntry->second, ADD); + } + return true; + } + else + return false; +} + +void BitmapCache::InvalidateCache() +{ + ::osl::MutexGuard aGuard (maMutex); + + for (auto& rEntry : *mpBitmapContainer) + { + rEntry.second.Invalidate(); + } + ReCalculateTotalCacheSize(); +} + +void BitmapCache::SetBitmap ( + const CacheKey& rKey, + const BitmapEx& rPreview, + bool bIsPrecious) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.SetPreview(rPreview); + iEntry->second.SetUpToDate(true); + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + } + else + { + iEntry = mpBitmapContainer->emplace( + rKey, + CacheEntry(rPreview, mnCurrentAccessTime++, bIsPrecious) + ).first; + } + + if (iEntry != mpBitmapContainer->end()) + UpdateCacheSize(iEntry->second, ADD); +} + +void BitmapCache::SetMarkedBitmap ( + const CacheKey& rKey, + const BitmapEx& rPreview) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.SetMarkedPreview(rPreview); + iEntry->second.SetAccessTime(mnCurrentAccessTime++); + UpdateCacheSize(iEntry->second, ADD); + } +} + +void BitmapCache::SetPrecious (const CacheKey& rKey, bool bIsPrecious) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end()) + { + if (iEntry->second.IsPrecious() != bIsPrecious) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.SetPrecious(bIsPrecious); + UpdateCacheSize(iEntry->second, ADD); + } + } + else if (bIsPrecious) + { + iEntry = mpBitmapContainer->emplace( + rKey, + CacheEntry(BitmapEx(), mnCurrentAccessTime++, bIsPrecious) + ).first; + UpdateCacheSize(iEntry->second, ADD); + } +} + +void BitmapCache::ReCalculateTotalCacheSize() +{ + ::osl::MutexGuard aGuard (maMutex); + + mnNormalCacheSize = 0; + mnPreciousCacheSize = 0; + for (const auto& rEntry : *mpBitmapContainer) + { + if (rEntry.second.IsPrecious()) + mnPreciousCacheSize += rEntry.second.GetMemorySize(); + else + mnNormalCacheSize += rEntry.second.GetMemorySize(); + } + mbIsFull = (mnNormalCacheSize >= mnMaximalNormalCacheSize); + + SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << "/" << mnPreciousCacheSize); +} + +void BitmapCache::Recycle (const BitmapCache& rCache) +{ + ::osl::MutexGuard aGuard (maMutex); + + for (const auto& rOtherEntry : *rCache.mpBitmapContainer) + { + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rOtherEntry.first)); + if (iEntry == mpBitmapContainer->end()) + { + iEntry = mpBitmapContainer->emplace( + rOtherEntry.first, + CacheEntry(mnCurrentAccessTime++, true) + ).first; + UpdateCacheSize(iEntry->second, ADD); + } + if (iEntry != mpBitmapContainer->end()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Recycle(rOtherEntry.second); + UpdateCacheSize(iEntry->second, ADD); + } + } +} + +BitmapCache::CacheIndex BitmapCache::GetCacheIndex() const +{ + ::osl::MutexGuard aGuard (maMutex); + + // Create a copy of the bitmap container. + SortableBitmapContainer aSortedContainer; + aSortedContainer.reserve(mpBitmapContainer->size()); + + // Copy the relevant entries. + for (const auto& rEntry : *mpBitmapContainer) + { + if ( rEntry.second.IsPrecious()) + continue; + + if ( ! rEntry.second.HasPreview()) + continue; + + aSortedContainer.emplace_back(rEntry.first, rEntry.second); + } + + // Sort the remaining entries. + ::std::sort(aSortedContainer.begin(), aSortedContainer.end(), AccessTimeComparator()); + + // Return a list with the keys of the sorted entries. + CacheIndex aIndex; + aIndex.reserve(aSortedContainer.size()); + for (const auto& rIndexEntry : aSortedContainer) + aIndex.push_back(rIndexEntry.first); + return aIndex; +} + +void BitmapCache::Compress ( + const CacheKey& rKey, + const std::shared_ptr& rpCompressor) +{ + ::osl::MutexGuard aGuard (maMutex); + + CacheBitmapContainer::iterator iEntry (mpBitmapContainer->find(rKey)); + if (iEntry != mpBitmapContainer->end() && iEntry->second.HasPreview()) + { + UpdateCacheSize(iEntry->second, REMOVE); + iEntry->second.Compress(rpCompressor); + UpdateCacheSize(iEntry->second, ADD); + } +} + +void BitmapCache::UpdateCacheSize (const CacheEntry& rEntry, CacheOperation eOperation) +{ + sal_Int32 nEntrySize (rEntry.GetMemorySize()); + sal_Int32& rCacheSize (rEntry.IsPrecious() ? mnPreciousCacheSize : mnNormalCacheSize); + switch (eOperation) + { + case ADD: + rCacheSize += nEntrySize; + if ( ! rEntry.IsPrecious() && mnNormalCacheSize>mnMaximalNormalCacheSize) + { + mbIsFull = true; + SAL_INFO("sd.sls", __func__ << ": cache size is " << mnNormalCacheSize << " > " << mnMaximalNormalCacheSize); + mpCacheCompactor->RequestCompaction(); + } + break; + + case REMOVE: + rCacheSize -= nEntrySize; + if (mnNormalCacheSize < mnMaximalNormalCacheSize) + mbIsFull = false; + break; + + default: + assert(false); + break; + } +} + +//===== CacheEntry ============================================================ + +BitmapCache::CacheEntry::CacheEntry( + sal_Int32 nLastAccessTime, + bool bIsPrecious) + : mbIsUpToDate(true), + mnLastAccessTime(nLastAccessTime), + mbIsPrecious(bIsPrecious) +{ +} + +BitmapCache::CacheEntry::CacheEntry( + const BitmapEx& rPreview, + sal_Int32 nLastAccessTime, + bool bIsPrecious) + : maPreview(rPreview), + mbIsUpToDate(true), + mnLastAccessTime(nLastAccessTime), + mbIsPrecious(bIsPrecious) +{ +} + +inline void BitmapCache::CacheEntry::Recycle (const CacheEntry& rEntry) +{ + if ((rEntry.HasPreview() || rEntry.HasLosslessReplacement()) + && ! (HasPreview() || HasLosslessReplacement())) + { + maPreview = rEntry.maPreview; + maMarkedPreview = rEntry.maMarkedPreview; + mpReplacement = rEntry.mpReplacement; + mpCompressor = rEntry.mpCompressor; + mnLastAccessTime = rEntry.mnLastAccessTime; + mbIsUpToDate = rEntry.mbIsUpToDate; + } +} + +inline sal_Int32 BitmapCache::CacheEntry::GetMemorySize() const +{ + sal_Int32 nSize (0); + nSize += maPreview.GetSizeBytes(); + nSize += maMarkedPreview.GetSizeBytes(); + if (mpReplacement != nullptr) + nSize += mpReplacement->GetMemorySize(); + return nSize; +} + +void BitmapCache::CacheEntry::Compress (const std::shared_ptr& rpCompressor) +{ + if ( maPreview.IsEmpty()) + return; + + if (mpReplacement == nullptr) + { + mpReplacement = rpCompressor->Compress(maPreview); + +#ifdef DEBUG_SD_SLSBITMAPCACHE + sal_uInt32 nOldSize (maPreview.GetSizeBytes()); + sal_uInt32 nNewSize (mpReplacement.get()!=NULL ? mpReplacement->GetMemorySize() : 0); + if (nOldSize == 0) + nOldSize = 1; + sal_Int32 nRatio (100L * nNewSize / nOldSize); + SAL_INFO("sd.sls", __func__ << ": compressing bitmap for " << %x << " from " << nOldSize << " to " << nNewSize << " bytes (" << nRatio << "%)"); +#endif + + mpCompressor = rpCompressor; + } + + maPreview.SetEmpty(); + maMarkedPreview.SetEmpty(); +} + +inline void BitmapCache::CacheEntry::Decompress() +{ + if (mpReplacement != nullptr && mpCompressor != nullptr && maPreview.IsEmpty()) + { + maPreview = mpCompressor->Decompress(*mpReplacement); + maMarkedPreview.SetEmpty(); + if ( ! mpCompressor->IsLossless()) + mbIsUpToDate = false; + } +} + +inline void BitmapCache::CacheEntry::SetPreview (const BitmapEx& rPreview) +{ + maPreview = rPreview; + maMarkedPreview.SetEmpty(); + mpReplacement.reset(); + mpCompressor.reset(); +} + +bool BitmapCache::CacheEntry::HasPreview() const +{ + return ! maPreview.IsEmpty(); +} + +inline void BitmapCache::CacheEntry::SetMarkedPreview (const BitmapEx& rMarkedPreview) +{ + maMarkedPreview = rMarkedPreview; +} + +inline bool BitmapCache::CacheEntry::HasLosslessReplacement() const +{ + return mpReplacement != nullptr && mpCompressor != nullptr && mpCompressor->IsLossless(); +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx new file mode 100644 index 000000000..98b0bcb53 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCache.hxx @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class SdrPage; + +namespace sd::slidesorter::cache +{ +class CacheCompactor; +class BitmapCompressor; + +/** This low level cache is the actual bitmap container. It supports a + precious flag for every preview bitmap and keeps track of total sizes + for all previews with/without this flag. The precious flag is used by + compaction algorithms to determine which previews may be compressed or + even discarded and which have to remain in their original form. The + precious flag is usually set for the visible previews. + + Additionally to the actual preview there is an optional marked preview. + This is used for slides excluded from the slide show which have a preview + that shows a mark (some sort of bitmap overlay) to that effect. +*/ +class BitmapCache +{ +public: + /** The key for looking up preview bitmaps is a pointer to an SdrPage + object. The prior use of PageObjectViewObjectContact objects (which + ultimately use them) turned out to be less suitable because their + life time is shorter then that of the page objects. Frequent + destruction and re-creation of the preview bitmaps was the result. + */ + typedef const SdrPage* CacheKey; + class CacheEntry; + class CacheBitmapContainer; + typedef ::std::vector CacheIndex; + + /** Create a new cache for bitmap objects. + The default value from the configuration is used. + When that does not exist then an internal default value is + used. + */ + explicit BitmapCache(); + + /** The destructor clears the cache and releases all bitmaps still in it. + */ + ~BitmapCache(); + + /** Remove all preview bitmaps from the cache. After this call the + cache is empty. + */ + void Clear(); + + /** Return when the cache is full, i.e. the cache compactor had + to be run. + */ + bool IsFull() const { return mbIsFull; } + + /** Return the memory size that is occupied by all non-precious bitmaps + in the cache. + */ + sal_Int32 GetSize() const { return mnNormalCacheSize; } + + /** Return when a preview bitmap exists for the given key. + */ + bool HasBitmap(const CacheKey& rKey); + + /** Return when a preview bitmap exists for the given key and + when it is up-to-date. + */ + bool BitmapIsUpToDate(const CacheKey& rKey); + + /** Return the preview bitmap for the given contact object. + */ + BitmapEx GetBitmap(const CacheKey& rKey); + + /** Return the marked preview bitmap for the given contact object. + */ + BitmapEx GetMarkedBitmap(const CacheKey& rKey); + + /** Release the reference to the preview bitmap that is associated with + the given key. + */ + void ReleaseBitmap(const CacheKey& rKey); + + /** Mark the specified preview bitmap as not being up-to-date + anymore. + @return + When the key references a page in the cache then + return . When the key is not known then + is returned. + */ + bool InvalidateBitmap(const CacheKey& rKey); + + /** Mark all preview bitmaps as not being up-to-date anymore. + */ + void InvalidateCache(); + + /** Add or replace a bitmap for the given key. + */ + void SetBitmap(const CacheKey& rKey, const BitmapEx& rPreview, bool bIsPrecious); + + /** Add or replace a marked bitmap for the given key. + */ + void SetMarkedBitmap(const CacheKey& rKey, const BitmapEx& rPreview); + + /** Mark the specified preview bitmap as precious, i.e. that it must not + be compressed or otherwise removed from the cache. + */ + void SetPrecious(const CacheKey& rKey, bool bIsPrecious); + + /** Calculate the cache size. This should rarely be necessary because + the cache size is tracked with each modification of preview + bitmaps. + */ + void ReCalculateTotalCacheSize(); + + /** Use the previews in the given cache to initialize missing previews. + */ + void Recycle(const BitmapCache& rCache); + + /** Return a list of sorted cache keys that represent an index into (a + part of) the cache. The entries of the index are sorted according + to last access times with the least recently access time first. + Entries with the precious flag set are omitted. + Entries with that have no preview bitmaps are omitted. + */ + CacheIndex GetCacheIndex() const; + + /** Compress the specified preview bitmap with the given bitmap + compressor. A reference to the compressor is stored for later + decompression. + */ + void Compress(const CacheKey& rKey, const std::shared_ptr& rpCompressor); + +private: + mutable ::osl::Mutex maMutex; + + std::unique_ptr mpBitmapContainer; + + /** Total size of bytes that are occupied by bitmaps in the cache for + whom the slides are currently not inside the visible area. + */ + sal_Int32 mnNormalCacheSize; + + /** Total size of bytes that are occupied by bitmaps in the cache for + whom the slides are currently visible. + */ + sal_Int32 mnPreciousCacheSize; + + /** At the moment the access time is not an actual time or date value + but a counter that is increased with every access. It thus defines + the same ordering as a true time. + */ + sal_Int32 mnCurrentAccessTime; + + /** The maximal cache size for the off-screen preview bitmaps. When + mnNormalCacheSize grows larger than this value then the + mpCacheCompactor member is used to reduce the cache size. + */ + sal_Int32 mnMaximalNormalCacheSize; + + /** The cache compactor is used to reduce the number of bytes used by + off-screen preview bitmaps. + */ + ::std::unique_ptr mpCacheCompactor; + + /** This flag stores if the cache is or recently was full, i.e. the + cache compactor has or had to be run in order to reduce the cache + size to the allowed value. + */ + bool mbIsFull; + + /** Update mnNormalCacheSize or mnPreciousCacheSize according to the + precious flag of the specified preview bitmap and the specified + operation. + */ + enum CacheOperation + { + ADD, + REMOVE + }; + void UpdateCacheSize(const CacheEntry& rKey, CacheOperation eOperation); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx new file mode 100644 index 000000000..d4da935dd --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.cxx @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsBitmapCompressor.hxx" + +#include +#include +#include +#include + +namespace sd::slidesorter::cache { + +//===== NoBitmapCompression =================================================== + +/** This dummy replacement simply stores a shared pointer to the original + preview bitmap. +*/ +class NoBitmapCompression::DummyReplacement + : public BitmapReplacement +{ +public: + BitmapEx maPreview; + + explicit DummyReplacement (const BitmapEx& rPreview) : maPreview(rPreview) { } + virtual ~DummyReplacement() {} + virtual sal_Int32 GetMemorySize() const override { return maPreview.GetSizeBytes(); } +}; + +std::shared_ptr NoBitmapCompression::Compress (const BitmapEx& rBitmap) const +{ + return std::make_shared(rBitmap); +} + +BitmapEx NoBitmapCompression::Decompress (const BitmapReplacement& rBitmapData) const +{ + return dynamic_cast(rBitmapData).maPreview; +} + +bool NoBitmapCompression::IsLossless() const +{ + return true; +} + +//===== CompressionByDeletion ================================================= + +std::shared_ptr CompressionByDeletion::Compress (const BitmapEx& ) const +{ + return std::shared_ptr(); +} + +BitmapEx CompressionByDeletion::Decompress (const BitmapReplacement& ) const +{ + // Return a NULL pointer. This will eventually lead to a request for + // the creation of a new one. + return BitmapEx(); +} + +bool CompressionByDeletion::IsLossless() const +{ + return false; +} + +//===== ResolutionReduction =================================================== + +/** Store a scaled down bitmap together with the original size. +*/ +class ResolutionReduction::ResolutionReducedReplacement : public BitmapReplacement +{ +public: + BitmapEx maPreview; + Size maOriginalSize; + + virtual ~ResolutionReducedReplacement(); + virtual sal_Int32 GetMemorySize() const override; +}; + +ResolutionReduction::ResolutionReducedReplacement::~ResolutionReducedReplacement() +{ +} + +sal_Int32 ResolutionReduction::ResolutionReducedReplacement::GetMemorySize() const +{ + return maPreview.GetSizeBytes(); +} + +std::shared_ptr ResolutionReduction::Compress ( + const BitmapEx& rBitmap) const +{ + auto pResult = std::make_shared(); + pResult->maPreview = rBitmap; + Size aSize (rBitmap.GetSizePixel()); + pResult->maOriginalSize = aSize; + if (aSize.Width()>0 && aSize.Width()maPreview.Scale(Size(mnWidth,nHeight)); + } + + return pResult; +} + +BitmapEx ResolutionReduction::Decompress (const BitmapReplacement& rBitmapData) const +{ + BitmapEx aResult; + + const ResolutionReducedReplacement* pData ( + dynamic_cast(&rBitmapData)); + + if ( pData && ! pData->maPreview.IsEmpty()) + { + aResult = pData->maPreview; + if (pData->maOriginalSize.Width() > mnWidth) + aResult.Scale(pData->maOriginalSize); + } + + return aResult; +} + +bool ResolutionReduction::IsLossless() const +{ + return false; +} + +//===== PNGCompression ======================================================== + +class PngCompression::PngReplacement : public BitmapReplacement +{ +public: + void* mpData; + sal_Int32 mnDataSize; + PngReplacement() + : mpData(nullptr), + mnDataSize(0) + {} + virtual ~PngReplacement() + { + delete [] static_cast(mpData); + } + virtual sal_Int32 GetMemorySize() const override + { + return mnDataSize; + } +}; + +std::shared_ptr PngCompression::Compress (const BitmapEx& rBitmap) const +{ + vcl::PNGWriter aWriter(rBitmap); + SvMemoryStream aStream (32768, 32768); + aWriter.Write(aStream); + + auto pResult = std::make_shared(); + pResult->mnDataSize = aStream.Tell(); + pResult->mpData = new char[pResult->mnDataSize]; + memcpy(pResult->mpData, aStream.GetData(), pResult->mnDataSize); + + return pResult; +} + +BitmapEx PngCompression::Decompress ( + const BitmapReplacement& rBitmapData) const +{ + BitmapEx aResult; + const PngReplacement* pData (dynamic_cast(&rBitmapData)); + if (pData != nullptr) + { + SvMemoryStream aStream (pData->mpData, pData->mnDataSize, StreamMode::READ); + vcl::PngImageReader aReader (aStream); + aResult = aReader.read().GetBitmap(); + } + + return aResult; +} + +bool PngCompression::IsLossless() const +{ + return true; +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx new file mode 100644 index 000000000..4754bead9 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapCompressor.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +class BitmapEx; + +namespace sd::slidesorter::cache +{ +class BitmapReplacement; + +/** This interface class provides the minimal method set for classes that + implement the compression and decompression of preview bitmaps. +*/ +class BitmapCompressor +{ +public: + /** Compress the given bitmap into a replacement format that is specific + to the compressor class. + */ + virtual std::shared_ptr Compress(const BitmapEx& rBitmap) const = 0; + + /** Decompress the given replacement data into a preview bitmap. + Depending on the compression technique the returned bitmap may + differ from the original bitmap given to the Compress() method. It + may even of the wrong size or empty or the NULL pointer. It is the + task of the caller to create a new preview bitmap if the returned + one is not as desired. + */ + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const = 0; + + /** Return whether the compression and decompression is lossless. This + value is used by the caller of Decompress() to decide whether to use + the returned bitmap as is or if a new preview has to be created. + */ + virtual bool IsLossless() const = 0; + +protected: + ~BitmapCompressor() {} +}; + +/** Interface for preview bitmap replacements. Each bitmap + compressor/decompressor has to provide an implementation that is + suitable to store the compressed bitmaps. +*/ +class BitmapReplacement +{ +public: + virtual sal_Int32 GetMemorySize() const { return 0; } + +protected: + ~BitmapReplacement() {} +}; + +/** This is one trivial bitmap compressor. It stores bitmaps unmodified + instead of compressing them. + This compressor is lossless. +*/ +class NoBitmapCompression : public BitmapCompressor +{ + class DummyReplacement; + +public: + virtual ~NoBitmapCompression() {} + virtual std::shared_ptr Compress(const BitmapEx& rpBitmap) const override; + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override; + virtual bool IsLossless() const override; +}; + +/** This is another trivial bitmap compressor. Instead of compressing a + bitmap, it throws the bitmap away. Its Decompress() method returns a + NULL pointer. The caller has to create a new preview bitmap instead. + This compressor clearly is not lossless. +*/ +class CompressionByDeletion : public BitmapCompressor +{ +public: + virtual ~CompressionByDeletion() {} + virtual std::shared_ptr Compress(const BitmapEx& rBitmap) const override; + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override; + virtual bool IsLossless() const override; +}; + +/** Compress a preview bitmap by reducing its resolution. While the aspect + ratio is maintained the horizontal resolution is scaled down to 100 + pixels. + This compressor is not lossless. +*/ +class ResolutionReduction : public BitmapCompressor +{ + class ResolutionReducedReplacement; + static const sal_Int32 mnWidth = 100; + +public: + virtual ~ResolutionReduction() {} + virtual std::shared_ptr Compress(const BitmapEx& rpBitmap) const override; + /** Scale the replacement bitmap up to the original size. + */ + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override; + virtual bool IsLossless() const override; +}; + +/** Compress preview bitmaps using the PNG format. + This compressor is lossless. +*/ +class PngCompression : public BitmapCompressor +{ + class PngReplacement; + +public: + virtual ~PngCompression() {} + virtual std::shared_ptr Compress(const BitmapEx& rBitmap) const override; + virtual BitmapEx Decompress(const BitmapReplacement& rBitmapData) const override; + virtual bool IsLossless() const override; +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx new file mode 100644 index 000000000..a9182c2a2 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.cxx @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsBitmapFactory.hxx" + +#include +#include +#include + +namespace sd::slidesorter::view { +class SlideSorterView; +class PageObjectViewObjectContact; +} + +namespace sd::slidesorter::cache { + +BitmapFactory::BitmapFactory() + : maRenderer(false) +{ +} + +BitmapFactory::~BitmapFactory() +{ +} + +BitmapEx BitmapFactory::CreateBitmap ( + const SdPage& rPage, + const Size& rPixelSize, + const bool bDoSuperSampling) +{ + Size aSize (rPixelSize); + if (bDoSuperSampling) + { + // Supersampling factor + int aSuperSamplingFactor = 2; + aSize.setWidth( aSize.Width() * aSuperSamplingFactor ); + aSize.setHeight( aSize.Height() * aSuperSamplingFactor ); + } + + BitmapEx aPreview (maRenderer.RenderPage ( + &rPage, + aSize, + true, + false).GetBitmapEx()); + if (bDoSuperSampling) + { + aPreview.Scale(rPixelSize, BmpScaleFlag::BestQuality); + } + + return aPreview; +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx new file mode 100644 index 000000000..c1733b825 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsBitmapFactory.hxx @@ -0,0 +1,46 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class SdPage; +class Size; + +namespace sd::slidesorter::cache +{ +/** This factory class creates preview bitmaps for page objects. It is + merely an adapter for the PreviewRenderer. +*/ +class BitmapFactory +{ +public: + BitmapFactory(); + ~BitmapFactory(); + + BitmapEx CreateBitmap(const SdPage& rPage, const Size& rPixelSize, const bool bDoSuperSampling); + +private: + PreviewRenderer maRenderer; +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx new file mode 100644 index 000000000..79ab9fab2 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.cxx @@ -0,0 +1,189 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "SlsCacheCompactor.hxx" + +#include "SlsBitmapCompressor.hxx" +#include "SlsBitmapCache.hxx" +#include "SlsCacheConfiguration.hxx" + +#include +#include +#include + +using namespace ::com::sun::star::uno; + +namespace { + +/** This is a trivial implementation of the CacheCompactor interface class. + It ignores calls to RequestCompaction() and thus will never decrease the + total size of off-screen preview bitmaps. +*/ +class NoCacheCompaction + : public ::sd::slidesorter::cache::CacheCompactor +{ +public: + NoCacheCompaction ( + ::sd::slidesorter::cache::BitmapCache& rCache, + sal_Int32 nMaximalCacheSize) + : CacheCompactor(rCache, nMaximalCacheSize) + {} + + virtual void RequestCompaction() override { /* Ignored */ }; + +protected: + virtual void Run() override { /* Do nothing */ }; +}; + +/** This implementation of the CacheCompactor interface class uses one of + several bitmap compression algorithms to reduce the number of the bytes + of the off-screen previews in the bitmap cache. See the documentation + of CacheCompactor::Create() for more details on configuration properties + that control the choice of compression algorithm. +*/ +class CacheCompactionByCompression + : public ::sd::slidesorter::cache::CacheCompactor +{ +public: + CacheCompactionByCompression ( + ::sd::slidesorter::cache::BitmapCache& rCache, + sal_Int32 nMaximalCacheSize, + const std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor>& rpCompressor); + +protected: + virtual void Run() override; + +private: + std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor> mpCompressor; +}; + +} // end of anonymous namespace + +namespace sd::slidesorter::cache { + +::std::unique_ptr CacheCompactor::Create ( + BitmapCache& rCache, + sal_Int32 nMaximalCacheSize) +{ + static const char sNone[] = "None"; + + std::shared_ptr pCompressor; + OUString sCompressionPolicy("PNGCompression"); + Any aCompressionPolicy (CacheConfiguration::Instance()->GetValue("CompressionPolicy")); + if (aCompressionPolicy.has()) + aCompressionPolicy >>= sCompressionPolicy; + if (sCompressionPolicy == sNone) + pCompressor = std::make_shared(); + else if (sCompressionPolicy == "Erase") + pCompressor = std::make_shared(); + else if (sCompressionPolicy == "ResolutionReduction") + pCompressor = std::make_shared(); + else + pCompressor = std::make_shared(); + + ::std::unique_ptr pCompactor; + OUString sCompactionPolicy("Compress"); + Any aCompactionPolicy (CacheConfiguration::Instance()->GetValue("CompactionPolicy")); + if (aCompactionPolicy.has()) + aCompactionPolicy >>= sCompactionPolicy; + if (sCompactionPolicy == sNone) + pCompactor.reset(new NoCacheCompaction(rCache,nMaximalCacheSize)); + else + pCompactor.reset(new CacheCompactionByCompression(rCache,nMaximalCacheSize,pCompressor)); + + return pCompactor; +} + +void CacheCompactor::RequestCompaction() +{ + if ( ! mbIsCompactionRunning && ! maCompactionTimer.IsActive()) + maCompactionTimer.Start(); +} + +CacheCompactor::CacheCompactor( + BitmapCache& rCache, + sal_Int32 nMaximalCacheSize) + : mrCache(rCache), + mnMaximalCacheSize(nMaximalCacheSize), + maCompactionTimer("sd CacheCompactor maCompactionTimer"), + mbIsCompactionRunning(false) +{ + maCompactionTimer.SetTimeout(100); + maCompactionTimer.SetInvokeHandler(LINK(this,CacheCompactor,CompactionCallback)); +} + +IMPL_LINK_NOARG(CacheCompactor, CompactionCallback, Timer *, void) +{ + mbIsCompactionRunning = true; + + try + { + Run(); + } + catch (const css::uno::RuntimeException&) + { + } + catch (const css::uno::Exception&) + { + } + + mbIsCompactionRunning = false; +} + +} // end of namespace ::sd::slidesorter::cache + +namespace { + +//===== CacheCompactionByCompression ========================================== + +CacheCompactionByCompression::CacheCompactionByCompression ( + ::sd::slidesorter::cache::BitmapCache& rCache, + sal_Int32 nMaximalCacheSize, + const std::shared_ptr< ::sd::slidesorter::cache::BitmapCompressor>& rpCompressor) + : CacheCompactor(rCache,nMaximalCacheSize), + mpCompressor(rpCompressor) +{ +} + +void CacheCompactionByCompression::Run() +{ + if (mrCache.GetSize() <= mnMaximalCacheSize) + return; + + SAL_INFO("sd.sls", __func__ << ": bitmap cache uses too much space: " << mrCache.GetSize() << " > " << mnMaximalCacheSize); + + ::sd::slidesorter::cache::BitmapCache::CacheIndex aIndex ( + mrCache.GetCacheIndex()); + for (const auto& rpIndex : aIndex) + { + if (rpIndex == nullptr) + continue; + + mrCache.Compress(rpIndex, mpCompressor); + if (mrCache.GetSize() < mnMaximalCacheSize) + break; + } + mrCache.ReCalculateTotalCacheSize(); + SAL_INFO("sd.sls", __func__ << ": there are now " << mrCache.GetSize() << " bytes occupied"); +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx new file mode 100644 index 000000000..d694ae1a1 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsCacheCompactor.hxx @@ -0,0 +1,87 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd::slidesorter::cache { + +class BitmapCache; + +/** This is an interface class whose implementations are created via the + Create() factory method. +*/ +class CacheCompactor +{ +public: + virtual ~CacheCompactor() {}; + + /** Create a new instance of the CacheCompactor interface class. The + type of compaction algorithm used depends on values from the + configuration: the SlideSorter/PreviewCache/CompactionPolicy + property of the Impress.xcs file currently supports the values + "None" and "Compress". With the later the CompressionPolicy + property is evaluated which implementation of the BitmapCompress + interface class to use as bitmap compressor. + @param rCache + The bitmap cache on which to operate. + @param nMaximalCacheSize + The total number of bytes the off-screen bitmaps in the cache + may have. When the Run() method is (indirectly) called the + compactor tries to reduce that summed size of off-screen bitmaps + under this number. However, it is not guaranteed that this + works in all cases. + */ + static ::std::unique_ptr Create ( + BitmapCache& rCache, + sal_Int32 nMaximalCacheSize); + + /** Request a compaction of the off-screen previews in the bitmap + cache. This calls via a timer the Run() method. + */ + virtual void RequestCompaction(); + +protected: + BitmapCache& mrCache; + sal_Int32 mnMaximalCacheSize; + + CacheCompactor( + BitmapCache& rCache, + sal_Int32 nMaximalCacheSize); + + /** This method actually tries to reduce the total number of bytes used + by the off-screen preview bitmaps. + */ + virtual void Run() = 0; + +private: + /** This timer is used to collect calls to RequestCompaction() and + eventually call Run(). + */ + Timer maCompactionTimer; + bool mbIsCompactionRunning; + DECL_LINK(CompactionCallback, Timer *, void); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx new file mode 100644 index 000000000..fd08c7627 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.cxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsCacheConfiguration.hxx" +#include + +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::slidesorter::cache { + +namespace +{ + typedef std::shared_ptr CacheConfigSharedPtr; + CacheConfigSharedPtr& theInstance() + { + static CacheConfigSharedPtr SINGLETON; + return SINGLETON; + } +} + +std::weak_ptr CacheConfiguration::mpWeakInstance; + +std::shared_ptr CacheConfiguration::Instance() +{ + SolarMutexGuard aSolarGuard; + CacheConfigSharedPtr &rInstancePtr = theInstance(); + if (!rInstancePtr) + { + // Maybe somebody else kept a previously created instance alive. + if ( ! mpWeakInstance.expired()) + rInstancePtr = std::shared_ptr(mpWeakInstance); + if (!rInstancePtr) + { + // We have to create a new instance. + rInstancePtr.reset(new CacheConfiguration()); + mpWeakInstance = rInstancePtr; + // Prepare to release this instance in the near future. + rInstancePtr->m_ReleaseTimer.SetInvokeHandler( + LINK(rInstancePtr.get(),CacheConfiguration,TimerCallback)); + rInstancePtr->m_ReleaseTimer.SetTimeout(5000 /* 5s */); + rInstancePtr->m_ReleaseTimer.Start(); + } + } + return rInstancePtr; +} + +CacheConfiguration::CacheConfiguration() + : m_ReleaseTimer("sd::CacheConfiguration maReleaseTimer") +{ + // Get the cache size from configuration. + try + { + // Obtain access to the configuration. + Reference xProvider = + configuration::theDefaultProvider::get( ::comphelper::getProcessComponentContext() ); + + // Obtain access to Impress configuration. + Sequence aCreationArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(OUString("/org.openoffice.Office.Impress/"))}, + {"depth", Any(sal_Int32(-1))} + })); + + Reference xRoot (xProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aCreationArguments)); + if ( ! xRoot.is()) + return; + Reference xHierarchy (xRoot, UNO_QUERY); + if ( ! xHierarchy.is()) + return; + + // Get the node for the slide sorter preview cache. + mxCacheNode.set( xHierarchy->getByHierarchicalName("MultiPaneGUI/SlideSorter/PreviewCache"), UNO_QUERY); + } + catch (RuntimeException &) + { + } + catch (Exception &) + { + } +} + +Any CacheConfiguration::GetValue (const OUString& rName) +{ + Any aResult; + + if (mxCacheNode != nullptr) + { + try + { + aResult = mxCacheNode->getByName(rName); + } + catch (Exception &) + { + } + } + + return aResult; +} + +IMPL_STATIC_LINK_NOARG(CacheConfiguration, TimerCallback, Timer *, void) +{ + CacheConfigSharedPtr &rInstancePtr = theInstance(); + // Release our reference to the instance. + rInstancePtr.reset(); + // note: if there are no other references to the instance, m_ReleaseTimer + // will be deleted now +} + +void CacheConfiguration::Shutdown() +{ + CacheConfigSharedPtr &rInstancePtr = theInstance(); + rInstancePtr.reset(); + assert(mpWeakInstance.expired()); // ensure m_ReleaseTimer is destroyed +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx new file mode 100644 index 000000000..d53bcd713 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsCacheConfiguration.hxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace com::sun::star::container +{ +class XNameAccess; +} + +namespace sd::slidesorter::cache +{ +/** A very simple and easy-to-use access to configuration entries regarding + the slide sorter cache. +*/ +class CacheConfiguration +{ +public: + /** Return an instance to this class. The reference is released after 5 + seconds. Subsequent calls to this function will create a new + instance. + */ + static std::shared_ptr Instance(); + + static void Shutdown(); + + /** Look up the specified value in + MultiPaneGUI/SlideSorter/PreviewCache. When the specified value + does not exist then an empty Any is returned. + */ + css::uno::Any GetValue(const OUString& rName); + +private: + /** When a caller holds a reference after we have released ours we use + this weak pointer to avoid creating a new instance. + */ + static std::weak_ptr mpWeakInstance; + Timer m_ReleaseTimer; + css::uno::Reference mxCacheNode; + + CacheConfiguration(); + + DECL_STATIC_LINK(CacheConfiguration, TimerCallback, Timer*, void); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx new file mode 100644 index 000000000..6275754fa --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.cxx @@ -0,0 +1,278 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsGenericPageCache.hxx" + +#include "SlsQueueProcessor.hxx" +#include "SlsRequestPriorityClass.hxx" +#include "SlsRequestFactory.hxx" +#include "SlsBitmapCache.hxx" +#include +#include + +namespace sd::slidesorter::cache { + +GenericPageCache::GenericPageCache ( + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext) + : maRequestQueue(rpCacheContext), + mpCacheContext(rpCacheContext), + maPreviewSize(rPreviewSize), + mbDoSuperSampling(bDoSuperSampling) +{ + // A large size may indicate an error of the caller. After all we + // are creating previews. + DBG_ASSERT (maPreviewSize.Width()<1000 && maPreviewSize.Height()<1000, + "GenericPageCache<>::GetPreviewBitmap(): bitmap requested with large width. " + "This may indicate an error."); +} + +GenericPageCache::~GenericPageCache() +{ + if (mpQueueProcessor != nullptr) + mpQueueProcessor->Stop(); + maRequestQueue.Clear(); + mpQueueProcessor.reset(); + + if (mpBitmapCache != nullptr) + PageCacheManager::Instance()->ReleaseCache(mpBitmapCache); + mpBitmapCache.reset(); +} + +void GenericPageCache::ProvideCacheAndProcessor() +{ + if (mpBitmapCache == nullptr) + mpBitmapCache = PageCacheManager::Instance()->GetCache( + mpCacheContext->GetModel(), + maPreviewSize); + + if (mpQueueProcessor == nullptr) + mpQueueProcessor.reset(new QueueProcessor( + maRequestQueue, + mpBitmapCache, + maPreviewSize, + mbDoSuperSampling, + mpCacheContext)); +} + +void GenericPageCache::ChangePreviewSize ( + const Size& rPreviewSize, + const bool bDoSuperSampling) +{ + if (rPreviewSize==maPreviewSize && bDoSuperSampling==mbDoSuperSampling) + return; + + // A large size may indicate an error of the caller. After all we + // are creating previews. + DBG_ASSERT (maPreviewSize.Width()<1000 && maPreviewSize.Height()<1000, + "GenericPageCache<>::GetPreviewBitmap(): bitmap requested with large width. " + "This may indicate an error."); + + if (mpBitmapCache != nullptr) + { + mpBitmapCache = PageCacheManager::Instance()->ChangeSize( + mpBitmapCache, maPreviewSize, rPreviewSize); + if (mpQueueProcessor != nullptr) + { + mpQueueProcessor->SetPreviewSize(rPreviewSize, bDoSuperSampling); + mpQueueProcessor->SetBitmapCache(mpBitmapCache); + } + } + maPreviewSize = rPreviewSize; + mbDoSuperSampling = bDoSuperSampling; +} + +BitmapEx GenericPageCache::GetPreviewBitmap ( + const CacheKey aKey, + const bool bResize) +{ + assert(aKey != nullptr); + + BitmapEx aPreview; + bool bMayBeUpToDate = true; + ProvideCacheAndProcessor(); + const SdrPage* pPage = mpCacheContext->GetPage(aKey); + if (mpBitmapCache->HasBitmap(pPage)) + { + aPreview = mpBitmapCache->GetBitmap(pPage); + const Size aBitmapSize (aPreview.GetSizePixel()); + if (aBitmapSize != maPreviewSize) + { + // Scale the bitmap to the desired size when that is possible, + // i.e. the bitmap is not empty. + if (bResize && !aBitmapSize.IsEmpty()) + { + aPreview.Scale(maPreviewSize); + } + bMayBeUpToDate = false; + } + else + bMayBeUpToDate = true; + } + else + bMayBeUpToDate = false; + + // Request the creation of a correctly sized preview bitmap. We do this + // even when the size of the bitmap in the cache is correct because its + // content may be not up-to-date anymore. + RequestPreviewBitmap(aKey, bMayBeUpToDate); + + return aPreview; +} + +BitmapEx GenericPageCache::GetMarkedPreviewBitmap ( + const CacheKey aKey) +{ + assert(aKey != nullptr); + + ProvideCacheAndProcessor(); + const SdrPage* pPage = mpCacheContext->GetPage(aKey); + BitmapEx aMarkedPreview (mpBitmapCache->GetMarkedBitmap(pPage)); + + return aMarkedPreview; +} + +void GenericPageCache::SetMarkedPreviewBitmap ( + const CacheKey aKey, + const BitmapEx& rMarkedBitmap) +{ + assert(aKey != nullptr); + + ProvideCacheAndProcessor(); + const SdrPage* pPage = mpCacheContext->GetPage(aKey); + mpBitmapCache->SetMarkedBitmap(pPage, rMarkedBitmap); +} + +void GenericPageCache::RequestPreviewBitmap ( + const CacheKey aKey, + const bool bMayBeUpToDate) +{ + assert(aKey != nullptr); + + const SdrPage* pPage = mpCacheContext->GetPage(aKey); + + ProvideCacheAndProcessor(); + + // Determine if the available bitmap is up to date. + bool bIsUpToDate = false; + if (bMayBeUpToDate) + bIsUpToDate = mpBitmapCache->BitmapIsUpToDate (pPage); + if (bIsUpToDate) + { + const BitmapEx aPreview (mpBitmapCache->GetBitmap(pPage)); + if (aPreview.IsEmpty() || aPreview.GetSizePixel()!=maPreviewSize) + bIsUpToDate = false; + } + + if ( bIsUpToDate) + return; + + // No, the bitmap is not up-to-date. Request a new one. + RequestPriorityClass ePriorityClass (NOT_VISIBLE); + if (mpCacheContext->IsVisible(aKey)) + { + if (mpBitmapCache->HasBitmap(pPage)) + ePriorityClass = VISIBLE_OUTDATED_PREVIEW; + else + ePriorityClass = VISIBLE_NO_PREVIEW; + } + maRequestQueue.AddRequest(aKey, ePriorityClass); + mpQueueProcessor->Start(ePriorityClass); +} + +bool GenericPageCache::InvalidatePreviewBitmap (const CacheKey aKey) +{ + // Invalidate the page in all caches that reference it, not just this one. + std::shared_ptr pCacheManager ( + cache::PageCacheManager::Instance()); + if (pCacheManager) + return pCacheManager->InvalidatePreviewBitmap( + mpCacheContext->GetModel(), + aKey); + else if (mpBitmapCache != nullptr) + return mpBitmapCache->InvalidateBitmap(mpCacheContext->GetPage(aKey)); + else + return false; +} + +void GenericPageCache::InvalidateCache () +{ + if (!mpBitmapCache) + return; + + // When the cache is being invalidated then it makes no sense to + // continue creating preview bitmaps. However, this may be + // re-started below. + mpQueueProcessor->Stop(); + maRequestQueue.Clear(); + + // Mark the previews in the cache as not being up-to-date anymore. + // Depending on the given bUpdateCache flag we start to create new + // preview bitmaps. + mpBitmapCache->InvalidateCache(); + RequestFactory()(maRequestQueue, mpCacheContext); +} + +void GenericPageCache::SetPreciousFlag ( + const CacheKey aKey, + const bool bIsPrecious) +{ + ProvideCacheAndProcessor(); + + // Change the request priority class according to the new precious flag. + if (bIsPrecious) + { + if (mpBitmapCache->HasBitmap(mpCacheContext->GetPage(aKey))) + maRequestQueue.ChangeClass(aKey,VISIBLE_OUTDATED_PREVIEW); + else + maRequestQueue.ChangeClass(aKey,VISIBLE_NO_PREVIEW); + } + else + { + if (mpBitmapCache->IsFull()) + { + // When the bitmap cache is full then requests for slides that + // are not visible are removed. + maRequestQueue.RemoveRequest(aKey); + } + else + maRequestQueue.ChangeClass(aKey,NOT_VISIBLE); + } + + mpBitmapCache->SetPrecious(mpCacheContext->GetPage(aKey), bIsPrecious); +} + +void GenericPageCache::Pause() +{ + ProvideCacheAndProcessor(); + if (mpQueueProcessor != nullptr) + mpQueueProcessor->Pause(); +} + +void GenericPageCache::Resume() +{ + ProvideCacheAndProcessor(); + if (mpQueueProcessor != nullptr) + mpQueueProcessor->Resume(); +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx new file mode 100644 index 000000000..900d40268 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsGenericPageCache.hxx @@ -0,0 +1,152 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "SlsRequestQueue.hxx" +#include + +#include + +namespace sd::slidesorter::cache { + +class BitmapCache; +class QueueProcessor; + +/** This basically is the implementation class for the PageCache class. +*/ +class GenericPageCache +{ +public: + /** The page cache is created with a reference to the SlideSorter and + thus has access to both view and model. This allows the cache to + fill itself with requests for all pages or just the visible ones. + @param rPreviewSize + The size of the previews is expected in pixel values. + @param bDoSuperSampling + When the previews are rendered larger and then scaled + down to the requested size to improve image quality. + */ + GenericPageCache ( + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext); + + ~GenericPageCache(); + + /** Change the size of the preview bitmaps. This may be caused by a + resize of the slide sorter window or a change of the number of + columns. + */ + void ChangePreviewSize ( + const Size& rPreviewSize, + const bool bDoSuperSampling); + + /** Request a preview bitmap for the specified page object in the + specified size. The returned bitmap may be a preview of the preview, + i.e. either a scaled (up or down) version of a previous preview (of + the wrong size) or an empty bitmap. In this case a request for the + generation of a new preview is created and inserted into the request + queue. When the preview is available the page shape will be told to + paint itself again. When it then calls this method again if + receives the correctly sized preview bitmap. + @param rRequestData + This data is used to determine the preview. + @param bResize + When then when the available bitmap has not the + requested size, it is scaled before it is returned. When + then the bitmap is returned in the wrong size and it is + the task of the caller to scale it. + @return + Returns a bitmap that is either empty, contains a scaled (up or + down) version or is the requested bitmap. + */ + BitmapEx GetPreviewBitmap ( + const CacheKey aKey, + const bool bResize); + BitmapEx GetMarkedPreviewBitmap ( + const CacheKey aKey); + void SetMarkedPreviewBitmap ( + const CacheKey aKey, + const BitmapEx& rMarkedBitmap); + + /** When the requested preview bitmap does not yet exist or is not + up-to-date then the rendering of one is scheduled. Otherwise this + method does nothing. + @param rRequestData + This data is used to determine the preview. + @param bMayBeUpToDate + This flag helps the method to determine whether an existing + preview that matches the request is up to date. If the caller + knows that it is not then by passing he tells us that we + do not have to check the up-to-date flag a second time. If + unsure use . + */ + void RequestPreviewBitmap ( + const CacheKey aKey, + const bool bMayBeUpToDate); + + /** Tell the cache to replace the bitmap associated with the given + request data with a new one that reflects recent changes in the + content of the page object. + @return + When the key is known then return . + */ + bool InvalidatePreviewBitmap (const CacheKey aKey); + + /** Call this method when all preview bitmaps have to be generated anew. + This is the case when the size of the page objects on the screen has + changed or when the model has changed. + */ + void InvalidateCache (); + + /** With the precious flag you can control whether a bitmap can be + removed from the cache or reduced in size to make room for other + bitmaps or is so precious that it will not be touched. A typical + use is to set the precious flag for the visible pages. + */ + void SetPreciousFlag (const CacheKey aKey, const bool bIsPrecious); + + void Pause(); + void Resume(); + +private: + std::shared_ptr mpBitmapCache; + + RequestQueue maRequestQueue; + + std::unique_ptr mpQueueProcessor; + + SharedCacheContext mpCacheContext; + + /** The current size of preview bitmaps. + */ + Size maPreviewSize; + + bool mbDoSuperSampling; + + /** Both bitmap cache and queue processor are created on demand by this + method. + */ + void ProvideCacheAndProcessor(); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsPageCache.cxx b/sd/source/ui/slidesorter/cache/SlsPageCache.cxx new file mode 100644 index 000000000..82b1b8ae4 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsPageCache.cxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include "SlsGenericPageCache.hxx" +#include + +using namespace ::com::sun::star; + +namespace sd::slidesorter::cache { + +//===== PageCache ============================================================= + +PageCache::PageCache ( + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext) + : mpImplementation( + new GenericPageCache( + rPreviewSize, + bDoSuperSampling, + rpCacheContext)) +{ +} + +PageCache::~PageCache() +{ +} + +void PageCache::ChangeSize ( + const Size& rPreviewSize, + const bool bDoSuperSampling) +{ + mpImplementation->ChangePreviewSize(rPreviewSize, bDoSuperSampling); +} + +BitmapEx PageCache::GetPreviewBitmap ( + const CacheKey aKey, + const bool bResize) +{ + return mpImplementation->GetPreviewBitmap(aKey, bResize); +} + +BitmapEx PageCache::GetMarkedPreviewBitmap ( + const CacheKey aKey) +{ + return mpImplementation->GetMarkedPreviewBitmap(aKey); +} + +void PageCache::SetMarkedPreviewBitmap ( + const CacheKey aKey, + const BitmapEx& rMarkedBitmap) +{ + mpImplementation->SetMarkedPreviewBitmap(aKey, rMarkedBitmap); +} + +void PageCache::RequestPreviewBitmap (const CacheKey aKey) +{ + return mpImplementation->RequestPreviewBitmap(aKey, true); +} + +void PageCache::InvalidatePreviewBitmap ( + const CacheKey aKey) +{ + if (mpImplementation->InvalidatePreviewBitmap(aKey)) + RequestPreviewBitmap(aKey); +} + +void PageCache::InvalidateCache() +{ + mpImplementation->InvalidateCache(); +} + +void PageCache::SetPreciousFlag ( + const CacheKey aKey, + const bool bIsPrecious) +{ + mpImplementation->SetPreciousFlag(aKey, bIsPrecious); +} + +void PageCache::Pause() +{ + mpImplementation->Pause(); +} + +void PageCache::Resume() +{ + mpImplementation->Resume(); +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx b/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx new file mode 100644 index 000000000..45afd93c9 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsPageCacheManager.cxx @@ -0,0 +1,420 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include "SlsBitmapCache.hxx" + +#include +#include +#include +#include + +namespace { + +/** Collection of data that is stored for all active preview caches. +*/ +class CacheDescriptor +{ +public: + ::sd::slidesorter::cache::PageCacheManager::DocumentKey mpDocument; + Size maPreviewSize; + + CacheDescriptor( + ::sd::slidesorter::cache::PageCacheManager::DocumentKey const & pDocument, + const Size& rPreviewSize) + :mpDocument(pDocument),maPreviewSize(rPreviewSize) + {} + /// Test for equality with respect to all members. + class Equal {public: bool operator() ( + const CacheDescriptor& rDescriptor1, const CacheDescriptor& rDescriptor2) const { + return rDescriptor1.mpDocument==rDescriptor2.mpDocument + && rDescriptor1.maPreviewSize==rDescriptor2.maPreviewSize; + } }; + /// Hash function that takes all members into account. + class Hash {public: size_t operator() (const CacheDescriptor& rDescriptor) const { + return reinterpret_cast(rDescriptor.mpDocument.get()) + rDescriptor.maPreviewSize.Width(); + } }; +}; + +/** Collection of data that is stored for the inactive, recently used + caches. +*/ +class RecentlyUsedCacheDescriptor +{ +public: + Size maPreviewSize; + std::shared_ptr< ::sd::slidesorter::cache::BitmapCache> mpCache; + + RecentlyUsedCacheDescriptor( + const Size& rPreviewSize, + const std::shared_ptr< ::sd::slidesorter::cache::BitmapCache>& rpCache) + :maPreviewSize(rPreviewSize),mpCache(rpCache) + {} +}; + +/** The list of recently used caches is organized as queue. When elements + are added the list is shortened to the maximally allowed number of + elements by removing the least recently used elements. +*/ +typedef ::std::deque RecentlyUsedQueue; + +/** Compare the caches by preview size. Those that match the given size + come first, then, regardless of the given size, the largest ones before + the smaller ones. +*/ +class BestFittingCacheComparer +{ +public: + explicit BestFittingCacheComparer (const Size& rPreferredSize) + : maPreferredSize(rPreferredSize) + {} + bool operator()(const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement1, + const ::sd::slidesorter::cache::PageCacheManager::BestFittingPageCaches::value_type& rElement2) + { + if (rElement2.first == maPreferredSize) + return false; + else if (rElement1.first == maPreferredSize) + return true; + else + return (rElement1.first.Width()*rElement1.first.Height() + > rElement2.first.Width()*rElement2.first.Height()); + } + +private: + Size maPreferredSize; +}; + +} // end of anonymous namespace + +namespace sd::slidesorter::cache { + +/** Container for the active caches. +*/ +class PageCacheManager::PageCacheContainer + : public std::unordered_map, + CacheDescriptor::Hash, + CacheDescriptor::Equal> +{ +public: + PageCacheContainer() {} + + /** Compare entries in the cache container with respect to the cache + address only. + */ + class CompareWithCache { public: + explicit CompareWithCache(const std::shared_ptr& rpCache) + : mpCache(rpCache) {} + bool operator () (const PageCacheContainer::value_type& rValue) const + { return rValue.second == mpCache; } + private: + std::shared_ptr mpCache; + }; +}; + +/** The recently used caches are stored in one queue for each document. +*/ +class PageCacheManager::RecentlyUsedPageCaches +{ +public: + typedef DocumentKey key_type; + typedef RecentlyUsedQueue mapped_type; + typedef std::map::iterator iterator; +private: + std::map maMap; +public: + RecentlyUsedPageCaches () {}; + + iterator end() { return maMap.end(); } + void clear() { maMap.clear(); } + iterator find(const key_type& key) { return maMap.find(key); } + template + std::pair emplace(Args&&... args) { return maMap.emplace(std::forward(args)...); } +}; + +class PageCacheManager::Deleter +{ +public: + void operator() (PageCacheManager* pObject) { delete pObject; } +}; + +//===== PageCacheManager ==================================================== + +std::weak_ptr PageCacheManager::mpInstance; + +std::shared_ptr PageCacheManager::Instance() +{ + std::shared_ptr pInstance; + + ::osl::MutexGuard aGuard (::osl::Mutex::getGlobalMutex()); + + pInstance = mpInstance.lock(); + if (pInstance == nullptr) + { + pInstance = std::shared_ptr( + new PageCacheManager(), + PageCacheManager::Deleter()); + mpInstance = pInstance; + } + + return pInstance; +} + +PageCacheManager::PageCacheManager() + : mpPageCaches(new PageCacheContainer()), + mpRecentlyUsedPageCaches(new RecentlyUsedPageCaches()) +{ +} + +PageCacheManager::~PageCacheManager() +{ +} + +std::shared_ptr PageCacheManager::GetCache ( + const DocumentKey& pDocument, + const Size& rPreviewSize) +{ + std::shared_ptr pResult; + + // Look for the cache in the list of active caches. + CacheDescriptor aKey (pDocument, rPreviewSize); + PageCacheContainer::iterator iCache (mpPageCaches->find(aKey)); + if (iCache != mpPageCaches->end()) + pResult = iCache->second; + + // Look for the cache in the list of recently used caches. + if (pResult == nullptr) + pResult = GetRecentlyUsedCache(pDocument, rPreviewSize); + + // Create the cache when no suitable one does exist. + if (pResult == nullptr) + pResult = std::make_shared(); + + // The cache may be newly created and thus empty or is old and may + // contain previews that are not up-to-date. Recycle previews from + // other caches to fill in the holes. + Recycle(pResult, pDocument,rPreviewSize); + + // Put the new (or old) cache into the container. + mpPageCaches->emplace(aKey, pResult); + + return pResult; +} + +void PageCacheManager::Recycle ( + const std::shared_ptr& rpCache, + const DocumentKey& pDocument, + const Size& rPreviewSize) +{ + BestFittingPageCaches aCaches; + + // Add bitmap caches from active caches. + for (auto& rActiveCache : *mpPageCaches) + { + if (rActiveCache.first.mpDocument == pDocument) + aCaches.emplace_back( + rActiveCache.first.maPreviewSize, rActiveCache.second); + } + + // Add bitmap caches from recently used caches. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + for (const auto& rRecentCache : iQueue->second) + aCaches.emplace_back( + rRecentCache.maPreviewSize, rRecentCache.mpCache); + } + + ::std::sort(aCaches.begin(), aCaches.end(), BestFittingCacheComparer(rPreviewSize)); + + for (const auto& rBestCache : aCaches) + { + rpCache->Recycle(*rBestCache.second); + } +} + +void PageCacheManager::ReleaseCache (const std::shared_ptr& rpCache) +{ + PageCacheContainer::iterator iCache (::std::find_if( + mpPageCaches->begin(), + mpPageCaches->end(), + PageCacheContainer::CompareWithCache(rpCache))); + + if (iCache != mpPageCaches->end()) + { + assert(iCache->second == rpCache); + + PutRecentlyUsedCache(iCache->first.mpDocument,iCache->first.maPreviewSize,rpCache); + + mpPageCaches->erase(iCache); + } +} + +std::shared_ptr PageCacheManager::ChangeSize ( + const std::shared_ptr& rpCache, + const Size&, + const Size& rNewPreviewSize) +{ + std::shared_ptr pResult; + + if (rpCache != nullptr) + { + // Look up the given cache in the list of active caches. + PageCacheContainer::iterator iCacheToChange (::std::find_if( + mpPageCaches->begin(), + mpPageCaches->end(), + PageCacheContainer::CompareWithCache(rpCache))); + if (iCacheToChange != mpPageCaches->end()) + { + assert(iCacheToChange->second == rpCache); + + // Now, we can change the preview size of the existing one by + // removing the cache from the list and re-insert it with the + // updated size. + const ::sd::slidesorter::cache::PageCacheManager::DocumentKey aKey ( + iCacheToChange->first.mpDocument); + mpPageCaches->erase(iCacheToChange); + mpPageCaches->emplace( + CacheDescriptor(aKey,rNewPreviewSize), + rpCache); + + pResult = rpCache; + } + else + { + assert(iCacheToChange != mpPageCaches->end()); + } + } + + return pResult; +} + +bool PageCacheManager::InvalidatePreviewBitmap ( + const DocumentKey& pDocument, + const SdrPage* pKey) +{ + bool bHasChanged (false); + + if (pDocument!=nullptr) + { + // Iterate over all caches that are currently in use and invalidate + // the previews in those that belong to the document. + for (auto& rCache : *mpPageCaches) + if (rCache.first.mpDocument == pDocument) + bHasChanged |= rCache.second->InvalidateBitmap(pKey); + + // Invalidate the previews in the recently used caches belonging to + // the given document. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + for (const auto& rCache2 : iQueue->second) + bHasChanged |= rCache2.mpCache->InvalidateBitmap(pKey); + } + } + + return bHasChanged; +} + +void PageCacheManager::InvalidateAllPreviewBitmaps (const DocumentKey& pDocument) +{ + if (pDocument == nullptr) + return; + + // Iterate over all caches that are currently in use and invalidate the + // previews in those that belong to the document. + for (auto& rCache : *mpPageCaches) + if (rCache.first.mpDocument == pDocument) + rCache.second->InvalidateCache(); + + // Invalidate the previews in the recently used caches belonging to the + // given document. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + for (const auto& rCache2 : iQueue->second) + rCache2.mpCache->InvalidateCache(); + } +} + +void PageCacheManager::InvalidateAllCaches() +{ + // Iterate over all caches that are currently in use and invalidate + // them. + for (auto& rCache : *mpPageCaches) + rCache.second->InvalidateCache(); + + // Remove all recently used caches, there is not much sense in storing + // invalidated and unused caches. + mpRecentlyUsedPageCaches->clear(); +} + +void PageCacheManager::ReleasePreviewBitmap (const SdrPage* pPage) +{ + for (auto& rCache : *mpPageCaches) + rCache.second->ReleaseBitmap(pPage); +} + +std::shared_ptr PageCacheManager::GetRecentlyUsedCache ( + const DocumentKey& pDocument, + const Size& rPreviewSize) +{ + std::shared_ptr pCache; + + // Look for the cache in the list of recently used caches. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + RecentlyUsedQueue::iterator iCache = std::find_if(iQueue->second.begin(), iQueue->second.end(), + [&rPreviewSize](const RecentlyUsedCacheDescriptor& rCache) { return rCache.maPreviewSize == rPreviewSize; }); + if (iCache != iQueue->second.end()) + { + pCache = iCache->mpCache; + iQueue->second.erase(iCache); + } + } + + return pCache; +} + +void PageCacheManager::PutRecentlyUsedCache( + DocumentKey const & pDocument, + const Size& rPreviewSize, + const std::shared_ptr& rpCache) +{ + // Look up the list of recently used caches for the given document. + RecentlyUsedPageCaches::iterator iQueue (mpRecentlyUsedPageCaches->find(pDocument)); + if (iQueue == mpRecentlyUsedPageCaches->end()) + iQueue = mpRecentlyUsedPageCaches->emplace( + pDocument, RecentlyUsedQueue() + ).first; + + if (iQueue != mpRecentlyUsedPageCaches->end()) + { + iQueue->second.push_front(RecentlyUsedCacheDescriptor(rPreviewSize,rpCache)); + // Shorten the list of recently used caches to the allowed maximal length. + while (iQueue->second.size() > mnMaximalRecentlyCacheCount) + iQueue->second.pop_back(); + } +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx new file mode 100644 index 000000000..077c48709 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.cxx @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsQueueProcessor.hxx" +#include "SlsRequestQueue.hxx" +#include "SlsBitmapCache.hxx" + +#include +#include +#include + +namespace sd::slidesorter::cache { + +//===== QueueProcessor ====================================================== + +QueueProcessor::QueueProcessor ( + RequestQueue& rQueue, + const std::shared_ptr& rpCache, + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext) + : maTimer("sd::QueueProcessor maTimer"), + maPreviewSize(rPreviewSize), + mbDoSuperSampling(bDoSuperSampling), + mpCacheContext(rpCacheContext), + mrQueue(rQueue), + mpCache(rpCache), + mbIsPaused(false) +{ + maTimer.SetInvokeHandler (LINK(this,QueueProcessor,ProcessRequestHdl)); + maTimer.SetTimeout (10); +} + +QueueProcessor::~QueueProcessor() +{ +} + +void QueueProcessor::Start (int nPriorityClass) +{ + if (mbIsPaused) + return; + if ( ! maTimer.IsActive()) + { + if (nPriorityClass == 0) + maTimer.SetTimeout (10); + else + maTimer.SetTimeout (100); + maTimer.Start(); + } +} + +void QueueProcessor::Stop() +{ + if (maTimer.IsActive()) + maTimer.Stop(); +} + +void QueueProcessor::Pause() +{ + mbIsPaused = true; +} + +void QueueProcessor::Resume() +{ + mbIsPaused = false; + if ( ! mrQueue.IsEmpty()) + Start(mrQueue.GetFrontPriorityClass()); +} + +void QueueProcessor::SetPreviewSize ( + const Size& rPreviewSize, + const bool bDoSuperSampling) +{ + maPreviewSize = rPreviewSize; + mbDoSuperSampling = bDoSuperSampling; +} + +IMPL_LINK_NOARG(QueueProcessor, ProcessRequestHdl, Timer *, void) +{ + ProcessRequests(); +} + +void QueueProcessor::ProcessRequests() +{ + assert(mpCacheContext); + + // Never process more than one request at a time in order to prevent the + // lock up of the edit view. + if ( ! mrQueue.IsEmpty() + && ! mbIsPaused + && mpCacheContext->IsIdle()) + { + CacheKey aKey = nullptr; + RequestPriorityClass ePriorityClass (NOT_VISIBLE); + { + ::osl::MutexGuard aGuard (mrQueue.GetMutex()); + + if ( ! mrQueue.IsEmpty()) + { + // Get the request with the highest priority from the queue. + ePriorityClass = mrQueue.GetFrontPriorityClass(); + aKey = mrQueue.GetFront(); + mrQueue.PopFront(); + } + } + + if (aKey != nullptr) + ProcessOneRequest(aKey, ePriorityClass); + } + + // Schedule the processing of the next element(s). + { + ::osl::MutexGuard aGuard (mrQueue.GetMutex()); + if ( ! mrQueue.IsEmpty()) + Start(mrQueue.GetFrontPriorityClass()); + else + { + comphelper::ProfileZone aZone("QueueProcessor finished processing all elements"); + } + } +} + +void QueueProcessor::ProcessOneRequest ( + CacheKey aKey, + const RequestPriorityClass ePriorityClass) +{ + try + { + std::scoped_lock aGuard (maMutex); + + // Create a new preview bitmap and store it in the cache. + if (mpCache != nullptr && mpCacheContext) + { + const SdPage* pSdPage = dynamic_cast(mpCacheContext->GetPage(aKey)); + if (pSdPage != nullptr) + { + const BitmapEx aPreview ( + maBitmapFactory.CreateBitmap(*pSdPage, maPreviewSize, mbDoSuperSampling)); + mpCache->SetBitmap (pSdPage, aPreview, ePriorityClass!=NOT_VISIBLE); + + // Initiate a repaint of the new preview. + mpCacheContext->NotifyPreviewCreation(aKey); + } + } + } + catch (css::uno::Exception &) + { + TOOLS_WARN_EXCEPTION( "sd", "QueueProcessor"); + } +} + +void QueueProcessor::SetBitmapCache ( + const std::shared_ptr& rpCache) +{ + mpCache = rpCache; +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx new file mode 100644 index 000000000..0035bcbce --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsQueueProcessor.hxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include "SlsRequestPriorityClass.hxx" +#include "SlsBitmapFactory.hxx" + +#include +#include + +namespace sd::slidesorter::cache { + +class BitmapCache; +class RequestQueue; + +/** This queue processor is timer based, i.e. when an entry is added to the + queue and the processor is started with Start() in the base class a + timer is started that eventually calls ProcessRequest(). This is + repeated until the queue is empty or Stop() is called. +*/ +class QueueProcessor final +{ +public: + QueueProcessor ( + RequestQueue& rQueue, + const std::shared_ptr& rpCache, + const Size& rPreviewSize, + const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext); + ~QueueProcessor(); + + /** Start the processor. This implementation is timer based and waits + a defined amount of time that depends on the given argument before + the next entry in the queue is processed. + @param nPriorityClass + A priority class of 0 tells the processor that a high priority + request is waiting in the queue. The time to wait is thus + shorter then that for a low priority request (denoted by a value + of 1.) When the timer is already running it is not modified. + */ + void Start (int nPriorityClass); + void Stop(); + void Pause(); + void Resume(); + + void SetPreviewSize ( + const Size& rSize, + const bool bDoSuperSampling); + + /** Use this method when the page cache is (maybe) using a different + BitmapCache. This is usually necessary after calling + PageCacheManager::ChangeSize(). + */ + void SetBitmapCache (const std::shared_ptr& rpCache); + +private: + /** This mutex is used to guard the queue processor. Be careful not to + mix its use with that of the solar mutex. + */ + std::mutex maMutex; + + Timer maTimer; + DECL_LINK(ProcessRequestHdl, Timer *, void); + Size maPreviewSize; + bool mbDoSuperSampling; + SharedCacheContext mpCacheContext; + RequestQueue& mrQueue; + std::shared_ptr mpCache; + BitmapFactory maBitmapFactory; + bool mbIsPaused; + + void ProcessRequests(); + void ProcessOneRequest ( + CacheKey aKey, + const RequestPriorityClass ePriorityClass); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx b/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx new file mode 100644 index 000000000..6fc6cabc9 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestFactory.cxx @@ -0,0 +1,50 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsRequestFactory.hxx" +#include "SlsRequestQueue.hxx" + +namespace sd::slidesorter::cache { + +void RequestFactory::operator()( + RequestQueue& rRequestQueue, + const SharedCacheContext& rpCacheContext) +{ + std::shared_ptr > aKeys; + + // Add the requests for the visible pages. + aKeys = rpCacheContext->GetEntryList(true); + if (aKeys != nullptr) + { + for (const auto& rKey : *aKeys) + rRequestQueue.AddRequest(rKey, VISIBLE_NO_PREVIEW); + } + + // Add the requests for the non-visible pages. + aKeys = rpCacheContext->GetEntryList(false); + if (aKeys != nullptr) + { + for (const auto& rKey : *aKeys) + rRequestQueue.AddRequest(rKey, NOT_VISIBLE); + } +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx b/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx new file mode 100644 index 000000000..3f4207725 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestFactory.hxx @@ -0,0 +1,36 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd::slidesorter::cache +{ +class RequestQueue; + +class RequestFactory +{ +public: + void operator()(RequestQueue& rRequestQueue, const SharedCacheContext& rpCacheContext); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx b/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx new file mode 100644 index 000000000..2c84ecbcf --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestPriorityClass.hxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +namespace sd::slidesorter::cache +{ +/** Each request for a preview creation has a priority. This enum defines + the available priorities. The special values MIN_CLASS and MAX_CLASS + are/can be used for validation and have to be kept up-to-date. +*/ +enum RequestPriorityClass +{ + MIN_CLASS = 0, + + // The slide is visible. A preview does not yet exist. + VISIBLE_NO_PREVIEW = MIN_CLASS, + // The slide is visible. A preview exists but is not up-to-date anymore. + VISIBLE_OUTDATED_PREVIEW, + // The slide is not visible. + NOT_VISIBLE, + + MAX_CLASS = NOT_VISIBLE +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx b/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx new file mode 100644 index 000000000..931c1a8f6 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestQueue.cxx @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsRequestQueue.hxx" + +#include + +#include + +#include + +namespace sd::slidesorter::cache { + +namespace { + +/** This class extends the actual request data with additional information + that is used by the priority queues. +*/ +class Request +{ +public: + Request ( + CacheKey aKey, sal_Int32 nPriority, RequestPriorityClass eClass) + : maKey(aKey), mnPriorityInClass(nPriority), meClass(eClass) + {} + /** Sort requests according to priority classes and then to priorities. + */ + class Comparator { public: + bool operator() (const Request& rRequest1, const Request& rRequest2) + const + { + if (rRequest1.meClass == rRequest2.meClass) + { + if (rRequest1.mnPriorityInClass == rRequest2.mnPriorityInClass) + { + return rRequest1.maKey < rRequest2.maKey; + } + return rRequest1.mnPriorityInClass > rRequest2.mnPriorityInClass; + } + return rRequest1.meClass < rRequest2.meClass; + } + }; + /** Request data is compared arbitrarily by their addresses in memory. + This just establishes an order so that the STL containers are happy. + The order is not semantically interpreted. + */ + class DataComparator + { + public: + explicit DataComparator (const CacheKey aKey) + : maKey(aKey) + { + } + bool operator() (const Request& rRequest) const + { + return maKey == rRequest.maKey; + } + private: + const CacheKey maKey; + }; + + CacheKey maKey; + sal_Int32 mnPriorityInClass; + RequestPriorityClass meClass; +}; + +} + +class RequestQueue::Container + : public ::std::set< + Request, + Request::Comparator> +{ +}; + +//===== GenericRequestQueue ================================================= + +RequestQueue::RequestQueue (const SharedCacheContext& rpCacheContext) + : mpRequestQueue(new Container), + mpCacheContext(rpCacheContext), + mnMinimumPriority(0), + mnMaximumPriority(1) +{ +} + +RequestQueue::~RequestQueue() +{ + Clear(); +} + +void RequestQueue::AddRequest ( + CacheKey aKey, + RequestPriorityClass eRequestClass) +{ + ::osl::MutexGuard aGuard (maMutex); + + assert(eRequestClass>=MIN_CLASS && eRequestClass<=MAX_CLASS); + + // If the request is already a member of the queue then remove it so + // that the following insertion will use the new prioritization. +#if OSL_DEBUG_LEVEL >=2 + bool bRemoved = +#endif + RemoveRequest(aKey); + + // The priority of the request inside its priority class is defined by + // the page number. This ensures a strict top-to-bottom, left-to-right + // order. + sal_Int32 nPriority (mpCacheContext->GetPriority(aKey)); + Request aRequest (aKey, nPriority, eRequestClass); + + std::pair ret = mpRequestQueue->insert(aRequest); + bool bInserted = ret.second; + + if (bInserted) + { + SdrPage *pPage = const_cast(aRequest.maKey); + pPage->AddPageUser(*this); + } + +#if OSL_DEBUG_LEVEL >=2 + SAL_INFO("sd.sls", __func__ << ": " << (bRemoved?"replaced":"added") + << " request for page " << ((aKey->GetPageNum()-1)/2) + << " with priority class " << static_cast(eRequestClass)); +#endif +} + +void RequestQueue::PageInDestruction(const SdrPage& rPage) +{ + //remove any requests pending for this page which is going away now + RemoveRequest(&rPage); +} + +#if OSL_DEBUG_LEVEL >=2 +bool +#else +void +#endif +RequestQueue::RemoveRequest( + CacheKey aKey) +{ + ::osl::MutexGuard aGuard (maMutex); +#if OSL_DEBUG_LEVEL >=2 + bool bIsRemoved = false; +#endif + while(true) + { + Container::const_iterator aRequestIterator = ::std::find_if ( + mpRequestQueue->begin(), + mpRequestQueue->end(), + Request::DataComparator(aKey)); + if (aRequestIterator != mpRequestQueue->end()) + { + if (aRequestIterator->mnPriorityInClass == mnMinimumPriority+1) + mnMinimumPriority++; + else if (aRequestIterator->mnPriorityInClass == mnMaximumPriority-1) + mnMaximumPriority--; + + SdrPage *pPage = const_cast(aRequestIterator->maKey); + pPage->RemovePageUser(*this); + mpRequestQueue->erase(aRequestIterator); +#if OSL_DEBUG_LEVEL >=2 + bIsRemoved = true; +#endif + } + else + break; + } +#if OSL_DEBUG_LEVEL >=2 + return bIsRemoved; +#endif + +} + +void RequestQueue::ChangeClass ( + CacheKey aKey, + RequestPriorityClass eNewRequestClass) +{ + ::osl::MutexGuard aGuard (maMutex); + + assert(eNewRequestClass>=MIN_CLASS && eNewRequestClass<=MAX_CLASS); + + Container::const_iterator iRequest ( + ::std::find_if ( + mpRequestQueue->begin(), + mpRequestQueue->end(), + Request::DataComparator(aKey))); + if (iRequest!=mpRequestQueue->end() && iRequest->meClass!=eNewRequestClass) + { + AddRequest(aKey, eNewRequestClass); + } +} + +CacheKey RequestQueue::GetFront() +{ + ::osl::MutexGuard aGuard (maMutex); + + if (mpRequestQueue->empty()) + throw css::uno::RuntimeException("RequestQueue::GetFront(): queue is empty", + nullptr); + + return mpRequestQueue->begin()->maKey; +} + +RequestPriorityClass RequestQueue::GetFrontPriorityClass() +{ + ::osl::MutexGuard aGuard (maMutex); + + if (mpRequestQueue->empty()) + throw css::uno::RuntimeException("RequestQueue::GetFrontPriorityClass(): queue is empty", + nullptr); + + return mpRequestQueue->begin()->meClass; +} + +void RequestQueue::PopFront() +{ + ::osl::MutexGuard aGuard (maMutex); + + if ( mpRequestQueue->empty()) + return; + + Container::const_iterator aIter(mpRequestQueue->begin()); + SdrPage *pPage = const_cast(aIter->maKey); + pPage->RemovePageUser(*this); + mpRequestQueue->erase(aIter); + + // Reset the priority counter if possible. + if (mpRequestQueue->empty()) + { + mnMinimumPriority = 0; + mnMaximumPriority = 1; + } +} + +bool RequestQueue::IsEmpty() +{ + ::osl::MutexGuard aGuard (maMutex); + return mpRequestQueue->empty(); +} + +void RequestQueue::Clear() +{ + ::osl::MutexGuard aGuard (maMutex); + + for (const auto& rItem : *mpRequestQueue) + { + SdrPage *pPage = const_cast(rItem.maKey); + pPage->RemovePageUser(*this); + } + + mpRequestQueue->clear(); + mnMinimumPriority = 0; + mnMaximumPriority = 1; +} + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx b/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx new file mode 100644 index 000000000..618ba5801 --- /dev/null +++ b/sd/source/ui/slidesorter/cache/SlsRequestQueue.hxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include "SlsRequestPriorityClass.hxx" +#include +#include +#include + +#include + +namespace sd::slidesorter::cache +{ +/** The request queue stores requests that are described by the Request + sorted according to priority class and then priority. +*/ +class RequestQueue : public sdr::PageUser +{ +public: + explicit RequestQueue(const SharedCacheContext& rpCacheContext); + virtual ~RequestQueue(); + + /** Insert a request with highest or lowest priority in its priority + class. When the request is already present then it is first + removed. This effect is then a re-prioritization. + @param aKey + The request. + @param eRequestClass + The priority class in which to insert the request with highest + or lowest priority. + @param bInsertWithHighestPriority + When this flag is the request is inserted with highest + priority in its class. When the request is inserted + with lowest priority. + */ + void AddRequest(CacheKey aKey, RequestPriorityClass eRequestClass); + + /** Remove the specified request from the queue. + @param aKey + It is OK when the specified request is not a member of the + queue. + */ +#if OSL_DEBUG_LEVEL >= 2 + bool +#else + void +#endif + RemoveRequest(CacheKey aKey); + + /** Change the priority class of the specified request. + */ + void ChangeClass(CacheKey aKey, RequestPriorityClass eNewRequestClass); + + /** Get the request with the highest priority int the highest priority class. + */ + CacheKey GetFront(); + + // For debugging. + RequestPriorityClass GetFrontPriorityClass(); + + /** Really a synonym for RemoveRequest(GetFront()); + */ + void PopFront(); + + /** Returns when there is no element in the queue. + */ + bool IsEmpty(); + + /** Remove all requests from the queue. This resets the minimum and + maximum priorities to their default values. + */ + void Clear(); + + /** Return the mutex that guards the access to the priority queue. + */ + ::osl::Mutex& GetMutex() { return maMutex; } + + /** Ensure we don't hand out a page deleted before anyone got a + chance to process it + */ + virtual void PageInDestruction(const SdrPage& rPage) override; + +private: + ::osl::Mutex maMutex; + class Container; + std::unique_ptr mpRequestQueue; + SharedCacheContext mpCacheContext; + + /** A lower bound of the lowest priority of all elements in the queues. + The start value is 0. It is assigned and then decreased every time + when an element is inserted or marked as the request with lowest + priority. + */ + int mnMinimumPriority; + /** An upper bound of the highest priority of all elements in the queues. + The start value is 1. It is assigned and then increased every time + when an element is inserted or marked as the request with highest + priority. + */ + int mnMaximumPriority; +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlideSorterController.cxx b/sd/source/ui/slidesorter/controller/SlideSorterController.cxx new file mode 100644 index 000000000..5c851f183 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlideSorterController.cxx @@ -0,0 +1,910 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include "SlsListener.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::sd::slidesorter::model; +using namespace ::sd::slidesorter::view; +using namespace ::sd::slidesorter::controller; +using namespace ::basegfx; + +namespace sd::slidesorter::controller { + +SlideSorterController::SlideSorterController (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mrModel(mrSlideSorter.GetModel()), + mrView(mrSlideSorter.GetView()), + mpInsertionIndicatorHandler(std::make_shared(rSlideSorter)), + mpAnimator(std::make_shared(rSlideSorter)), + mpVisibleAreaManager(new VisibleAreaManager(rSlideSorter)), + mnModelChangeLockCount(0), + mbIsForcedRearrangePending(false), + mbContextMenuOpen(false), + mbPostModelChangePending(false), + mnCurrentPageBeforeSwitch(0), + mpEditModeChangeMasterPage(nullptr), + mnPaintEntranceCount(0) +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + OSL_ASSERT(pWindow); + if (!pWindow) + return; + + // The whole background is painted by the view and controls. + vcl::Window* pParentWindow = pWindow->GetParent(); + OSL_ASSERT(pParentWindow!=nullptr); + pParentWindow->SetBackground (Wallpaper()); + + // Connect the view with the window that has been created by our base + // class. + pWindow->SetBackground(Wallpaper()); + pWindow->SetCenterAllowed(false); + pWindow->SetMapMode(MapMode(MapUnit::MapPixel)); + pWindow->SetViewSize(mrView.GetModelArea().GetSize()); +} + +void SlideSorterController::Init() +{ + mpCurrentSlideManager = std::make_shared(mrSlideSorter); + mpPageSelector.reset(new PageSelector(mrSlideSorter)); + mpFocusManager.reset(new FocusManager(mrSlideSorter)); + mpSlotManager = std::make_shared(mrSlideSorter); + mpScrollBarManager.reset(new ScrollBarManager(mrSlideSorter)); + mpSelectionManager = std::make_shared(mrSlideSorter); + mpClipboard.reset(new Clipboard(mrSlideSorter)); + + // Create the selection function. + SfxRequest aRequest ( + SID_OBJECT_SELECT, + SfxCallMode::SLOT, + mrModel.GetDocument()->GetItemPool()); + mrSlideSorter.SetCurrentFunction(CreateSelectionFunction(aRequest)); + + mpListener = new Listener(mrSlideSorter); + + mpPageSelector->GetCoreSelection(); + GetSelectionManager()->SelectionHasChanged(); +} + +SlideSorterController::~SlideSorterController() +{ + try + { + uno::Reference xComponent = mpListener; + if (xComponent.is()) + xComponent->dispose(); + } + catch( uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideSorterController::~SlideSorterController()" ); + } + + // dispose should have been called by now so that nothing is to be done + // to shut down cleanly. +} + +void SlideSorterController::Dispose() +{ + mpInsertionIndicatorHandler->End(Animator::AM_Immediate); + mpClipboard.reset(); + mpSelectionManager.reset(); + mpAnimator->Dispose(); +} + +model::SharedPageDescriptor SlideSorterController::GetPageAt ( + const Point& aWindowPosition) +{ + sal_Int32 nHitPageIndex (mrView.GetPageIndexAtPoint(aWindowPosition)); + model::SharedPageDescriptor pDescriptorAtPoint; + if (nHitPageIndex >= 0) + { + pDescriptorAtPoint = mrModel.GetPageDescriptor(nHitPageIndex); + + // Depending on a property we may have to check that the mouse is no + // just over the page object but over the preview area. + if (pDescriptorAtPoint + && ! pDescriptorAtPoint->HasState(PageDescriptor::ST_Selected)) + { + // Make sure that the mouse is over the preview area. + if ( ! mrView.GetLayouter().GetPageObjectLayouter()->GetBoundingBox( + pDescriptorAtPoint, + view::PageObjectLayouter::Part::Preview, + view::PageObjectLayouter::WindowCoordinateSystem).Contains(aWindowPosition)) + { + pDescriptorAtPoint.reset(); + } + } + } + + return pDescriptorAtPoint; +} + +PageSelector& SlideSorterController::GetPageSelector() +{ + OSL_ASSERT(mpPageSelector != nullptr); + return *mpPageSelector; +} + +FocusManager& SlideSorterController::GetFocusManager() +{ + OSL_ASSERT(mpFocusManager != nullptr); + return *mpFocusManager; +} + +Clipboard& SlideSorterController::GetClipboard() +{ + OSL_ASSERT(mpClipboard != nullptr); + return *mpClipboard; +} + +ScrollBarManager& SlideSorterController::GetScrollBarManager() +{ + OSL_ASSERT(mpScrollBarManager != nullptr); + return *mpScrollBarManager; +} + +std::shared_ptr const & SlideSorterController::GetCurrentSlideManager() const +{ + OSL_ASSERT(mpCurrentSlideManager != nullptr); + return mpCurrentSlideManager; +} + +std::shared_ptr const & SlideSorterController::GetSlotManager() const +{ + OSL_ASSERT(mpSlotManager != nullptr); + return mpSlotManager; +} + +std::shared_ptr const & SlideSorterController::GetSelectionManager() const +{ + OSL_ASSERT(mpSelectionManager != nullptr); + return mpSelectionManager; +} + +std::shared_ptr const & + SlideSorterController::GetInsertionIndicatorHandler() const +{ + OSL_ASSERT(mpInsertionIndicatorHandler != nullptr); + return mpInsertionIndicatorHandler; +} + +void SlideSorterController::Paint ( + const ::tools::Rectangle& rBBox, + vcl::Window* pWindow) +{ + if (mnPaintEntranceCount != 0) + return; + + ++mnPaintEntranceCount; + + try + { + mrView.CompleteRedraw(pWindow->GetOutDev(), vcl::Region(rBBox)); + } + catch (const Exception&) + { + // Ignore all exceptions. + } + + --mnPaintEntranceCount; +} + +void SlideSorterController::FuTemporary (SfxRequest& rRequest) +{ + mpSlotManager->FuTemporary (rRequest); +} + +void SlideSorterController::FuPermanent (SfxRequest &rRequest) +{ + mpSlotManager->FuPermanent (rRequest); +} + +void SlideSorterController::FuSupport (SfxRequest &rRequest) +{ + mpSlotManager->FuSupport (rRequest); +} + +bool SlideSorterController::Command ( + const CommandEvent& rEvent, + ::sd::Window* pWindow) +{ + bool bEventHasBeenHandled = false; + + if (pWindow == nullptr) + return false; + + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell == nullptr) + return false; + + switch (rEvent.GetCommand()) + { + case CommandEventId::ContextMenu: + { + SdPage* pPage = nullptr; + OUString aPopupId; + + model::PageEnumeration aSelectedPages ( + PageEnumerationProvider::CreateSelectedPagesEnumeration(mrModel)); + if (aSelectedPages.HasMoreElements()) + pPage = aSelectedPages.GetNextElement()->GetPage(); + + if (mrModel.GetEditMode() == EditMode::Page) + { + if (pPage != nullptr) + aPopupId = "pagepane"; + else + aPopupId = "pagepanenosel"; + } + else if (pPage != nullptr) + aPopupId = "pagepanemaster"; + else + aPopupId = "pagepanenoselmaster"; + + std::unique_ptr> xContext; + if (pPage == nullptr) + { + // When there is no selection, then we show the insertion + // indicator so that the user knows where a page insertion + // would take place. + mpInsertionIndicatorHandler->Start(false); + mpInsertionIndicatorHandler->UpdateIndicatorIcon(SD_MOD()->pTransferClip); + mpInsertionIndicatorHandler->UpdatePosition( + pWindow->PixelToLogic(rEvent.GetMousePosPixel()), + InsertionIndicatorHandler::MoveMode); + xContext.reset(new InsertionIndicatorHandler::ForceShowContext( + mpInsertionIndicatorHandler)); + } + + pWindow->ReleaseMouse(); + + Point aMenuLocation (0,0); + if (!rEvent.IsMouseEvent()) + { + // The event is not a mouse event. Use the center of the + // focused page as top left position of the context menu. + model::SharedPageDescriptor pDescriptor ( + GetFocusManager().GetFocusedPageDescriptor()); + if (pDescriptor) + { + ::tools::Rectangle aBBox ( + mrView.GetLayouter().GetPageObjectLayouter()->GetBoundingBox ( + pDescriptor, + PageObjectLayouter::Part::PageObject, + PageObjectLayouter::ModelCoordinateSystem)); + aMenuLocation = aBBox.Center(); + } + } + + if (SfxDispatcher* pDispatcher = pViewShell->GetDispatcher()) + { + mbContextMenuOpen = true; + if (!rEvent.IsMouseEvent()) + pDispatcher->ExecutePopup(aPopupId, pWindow, &aMenuLocation); + else + pDispatcher->ExecutePopup(aPopupId, pWindow); + mbContextMenuOpen = false; + mrSlideSorter.GetView().UpdatePageUnderMouse(); + ::rtl::Reference pFunction(GetCurrentSelectionFunction()); + if (pFunction.is()) + pFunction->ResetMouseAnchor(); + } + if (pPage == nullptr) + { + // Remember the position of the insertion indicator before + // it is hidden, so that a pending slide insertion slot call + // finds the right place to insert a new slide. + GetSelectionManager()->SetInsertionPosition( + GetInsertionIndicatorHandler()->GetInsertionPageIndex()); + } + xContext.reset(); + bEventHasBeenHandled = true; + } + break; + + case CommandEventId::Wheel: + { + const CommandWheelData* pData = rEvent.GetWheelData(); + if (pData == nullptr) + return false; + if (pData->IsMod1()) + { + // We do not support zooming with control+mouse wheel. + return false; + } + // Determine whether to scroll horizontally or vertically. This + // depends on the orientation of the scroll bar and the + // IsHoriz() flag of the event. + if ((mrSlideSorter.GetView().GetOrientation()==view::Layouter::HORIZONTAL) + == pData->IsHorz()) + { + GetScrollBarManager().Scroll( + ScrollBarManager::Orientation_Vertical, + -pData->GetNotchDelta()); + } + else + { + GetScrollBarManager().Scroll( + ScrollBarManager::Orientation_Horizontal, + -pData->GetNotchDelta()); + } + mrSlideSorter.GetView().UpdatePageUnderMouse(rEvent.GetMousePosPixel()); + + bEventHasBeenHandled = true; + } + break; + + default: break; + } + + return bEventHasBeenHandled; +} + +void SlideSorterController::LockModelChange() +{ + mnModelChangeLockCount += 1; +} + +void SlideSorterController::UnlockModelChange() +{ + mnModelChangeLockCount -= 1; + if (mnModelChangeLockCount==0 && mbPostModelChangePending) + { + PostModelChange(); + } +} + +void SlideSorterController::PreModelChange() +{ + // Prevent PreModelChange to execute more than once per model lock. + if (mbPostModelChangePending) + return; + + if (mrSlideSorter.GetViewShell() != nullptr) + mrSlideSorter.GetViewShell()->Broadcast( + ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START)); + + GetCurrentSlideManager()->PrepareModelChange(); + + if (mrSlideSorter.GetContentWindow()) + mrView.PreModelChange(); + + mbPostModelChangePending = true; +} + +void SlideSorterController::PostModelChange() +{ + mbPostModelChangePending = false; + mrModel.Resync(); + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + GetCurrentSlideManager()->HandleModelChange(); + + mrView.PostModelChange (); + + pWindow->SetViewOrigin (Point (0,0)); + pWindow->SetViewSize (mrView.GetModelArea().GetSize()); + + // The visibility of the scroll bars may have to be changed. Then + // the size of the view has to change, too. Let Rearrange() handle + // that. + Rearrange(mbIsForcedRearrangePending); + } + + if (mrSlideSorter.GetViewShell() != nullptr) + mrSlideSorter.GetViewShell()->Broadcast( + ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END)); +} + +void SlideSorterController::HandleModelChange() +{ + // Ignore this call when the document is not in a valid state, i.e. has + // not the same number of regular and notes pages. + bool bIsDocumentValid = (mrModel.GetDocument()->GetPageCount() % 2 == 1); + + if (bIsDocumentValid) + { + ModelChangeLock aLock (*this); + PreModelChange(); + } +} + +IMPL_LINK(SlideSorterController, ApplicationEventHandler, VclSimpleEvent&, rEvent, void) +{ + auto windowEvent = dynamic_cast(&rEvent); + if (windowEvent != nullptr) { + WindowEventHandler(*windowEvent); + } +} +IMPL_LINK(SlideSorterController, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + vcl::Window* pWindow = rEvent.GetWindow(); + sd::Window *pActiveWindow (mrSlideSorter.GetContentWindow().get()); + switch (rEvent.GetId()) + { + case VclEventId::WindowActivate: + case VclEventId::WindowShow: + if (pActiveWindow && pWindow == pActiveWindow->GetParent()) + mrView.RequestRepaint(); + break; + + case VclEventId::WindowHide: + if (pActiveWindow && pWindow == pActiveWindow->GetParent()) + mrView.SetPageUnderMouse(SharedPageDescriptor()); + break; + + case VclEventId::WindowGetFocus: + if (pActiveWindow) + if (pWindow == pActiveWindow) + GetFocusManager().ShowFocus(false); + break; + + case VclEventId::WindowLoseFocus: + if (pActiveWindow && pWindow == pActiveWindow) + { + GetFocusManager().HideFocus(); + mrView.GetToolTip().Hide(); + + //don't scroll back to the selected slide when we lose + //focus due to a temporary active context menu + if (!mbContextMenuOpen) + { + // Select the current slide so that it is properly + // visualized when the focus is moved to the edit view. + GetPageSelector().SelectPage(GetCurrentSlideManager()->GetCurrentSlide()); + } + } + break; + + case VclEventId::ApplicationDataChanged: + { + // Invalidate the preview cache. + cache::PageCacheManager::Instance()->InvalidateAllCaches(); + + // Update the draw mode. + DrawModeFlags nDrawMode (Application::GetSettings().GetStyleSettings().GetHighContrastMode() + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR); + if (mrSlideSorter.GetViewShell() != nullptr) + mrSlideSorter.GetViewShell()->GetFrameView()->SetDrawMode(nDrawMode); + if (pActiveWindow != nullptr) + pActiveWindow->GetOutDev()->SetDrawMode(nDrawMode); + mrView.HandleDrawModeChange(); + + // When the system font has changed a layout has to be done. + mrView.Resize(); + + // Update theme colors. + mrSlideSorter.GetProperties()->HandleDataChangeEvent(); + mrSlideSorter.GetTheme()->Update(mrSlideSorter.GetProperties()); + mrView.HandleDataChangeEvent(); + } + break; + + default: + break; + } +} + +void SlideSorterController::GetCtrlState (SfxItemSet& rSet) +{ + if (rSet.GetItemState(SID_RELOAD) != SfxItemState::UNKNOWN) + { + // let SFx en-/disable "last version" + SfxViewFrame* pSlideViewFrame = SfxViewFrame::Current(); + DBG_ASSERT(pSlideViewFrame!=nullptr, + "SlideSorterController::GetCtrlState: ViewFrame not found"); + if (pSlideViewFrame) + { + pSlideViewFrame->GetSlotState (SID_RELOAD, nullptr, &rSet); + } + else // MI says: no MDIFrame --> disable + { + rSet.DisableItem(SID_RELOAD); + } + } + + // Output quality. + if (rSet.GetItemState(SID_OUTPUT_QUALITY_COLOR)==SfxItemState::DEFAULT + ||rSet.GetItemState(SID_OUTPUT_QUALITY_GRAYSCALE)==SfxItemState::DEFAULT + ||rSet.GetItemState(SID_OUTPUT_QUALITY_BLACKWHITE)==SfxItemState::DEFAULT + ||rSet.GetItemState(SID_OUTPUT_QUALITY_CONTRAST)==SfxItemState::DEFAULT) + { + if (mrSlideSorter.GetContentWindow()) + { + DrawModeFlags nMode = mrSlideSorter.GetContentWindow()->GetOutDev()->GetDrawMode(); + sal_uInt16 nQuality = 0; + + if (nMode == sd::OUTPUT_DRAWMODE_COLOR) { + nQuality = 0; + } else if (nMode == sd::OUTPUT_DRAWMODE_GRAYSCALE) { + nQuality = 1; + } else if (nMode == sd::OUTPUT_DRAWMODE_BLACKWHITE) { + nQuality = 2; + } else if (nMode == sd::OUTPUT_DRAWMODE_CONTRAST) { + nQuality = 3; + } + + rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_COLOR, nQuality==0)); + rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_GRAYSCALE, nQuality==1)); + rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_BLACKWHITE, nQuality==2)); + rSet.Put (SfxBoolItem (SID_OUTPUT_QUALITY_CONTRAST, nQuality==3)); + } + } + + if (rSet.GetItemState(SID_MAIL_SCROLLBODY_PAGEDOWN) == SfxItemState::DEFAULT) + { + rSet.Put (SfxBoolItem( SID_MAIL_SCROLLBODY_PAGEDOWN, true)); + } +} + +void SlideSorterController::GetStatusBarState (SfxItemSet& rSet) +{ + mpSlotManager->GetStatusBarState (rSet); +} + +void SlideSorterController::ExecCtrl (SfxRequest& rRequest) +{ + mpSlotManager->ExecCtrl (rRequest); +} + +void SlideSorterController::GetAttrState (SfxItemSet& rSet) +{ + mpSlotManager->GetAttrState (rSet); +} + +void SlideSorterController::UpdateAllPages() +{ + // Do a redraw. + mrSlideSorter.GetContentWindow()->Invalidate(); +} + +void SlideSorterController::Resize (const ::tools::Rectangle& rAvailableSpace) +{ + if (maTotalWindowArea != rAvailableSpace) + { + maTotalWindowArea = rAvailableSpace; + Rearrange(true); + } +} + +void SlideSorterController::Rearrange (bool bForce) +{ + if (maTotalWindowArea.IsEmpty()) + return; + + if (mnModelChangeLockCount>0) + { + mbIsForcedRearrangePending |= bForce; + return; + } + else + mbIsForcedRearrangePending = false; + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (!pWindow) + return; + + if (bForce) + mrView.UpdateOrientation(); + + // Place the scroll bars. + ::tools::Rectangle aNewContentArea = GetScrollBarManager().PlaceScrollBars( + maTotalWindowArea, + mrView.GetOrientation() != view::Layouter::VERTICAL, + mrView.GetOrientation() != view::Layouter::HORIZONTAL); + + bool bSizeHasChanged (false); + // Only when bForce is not true we have to test for a size change in + // order to determine whether the window and the view have to be resized. + if ( ! bForce) + { + ::tools::Rectangle aCurrentContentArea (pWindow->GetPosPixel(), pWindow->GetOutputSizePixel()); + bSizeHasChanged = (aNewContentArea != aCurrentContentArea); + } + if (bForce || bSizeHasChanged) + { + // The browser window gets the remaining space. + pWindow->SetPosSizePixel (aNewContentArea.TopLeft(), aNewContentArea.GetSize()); + mrView.Resize(); + } + + // Adapt the scroll bars to the new zoom factor of the browser + // window and the arrangement of the page objects. + GetScrollBarManager().UpdateScrollBars(!bForce); + + // Keep the current slide in the visible area. + GetVisibleAreaManager().RequestCurrentSlideVisible(); + + mrView.RequestRepaint(); +} + +rtl::Reference SlideSorterController::CreateSelectionFunction (SfxRequest& rRequest) +{ + rtl::Reference xFunc( SelectionFunction::Create(mrSlideSorter, rRequest) ); + return xFunc; +} + +::rtl::Reference SlideSorterController::GetCurrentSelectionFunction() const +{ + rtl::Reference pFunction (mrSlideSorter.GetViewShell()->GetCurrentFunction()); + return ::rtl::Reference(dynamic_cast(pFunction.get())); +} + +void SlideSorterController::PrepareEditModeChange() +{ + // Before we throw away the page descriptors we prepare for selecting + // descriptors in the other mode and for restoring the current + // selection when switching back to the current mode. + if (mrModel.GetEditMode() != EditMode::Page) + return; + + maSelectionBeforeSwitch.clear(); + + // Search for the first selected page and determine the master page + // used by its page object. It will be selected after the switch. + // In the same loop the current selection is stored. + PageEnumeration aSelectedPages ( + PageEnumerationProvider::CreateSelectedPagesEnumeration(mrModel)); + while (aSelectedPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + SdPage* pPage = pDescriptor->GetPage(); + // Remember the master page of the first selected descriptor. + if (pPage!=nullptr && mpEditModeChangeMasterPage==nullptr) + mpEditModeChangeMasterPage = &static_cast( + pPage->TRG_GetMasterPage()); + + maSelectionBeforeSwitch.push_back(pPage); + } + + // Remember the current page. + if (mrSlideSorter.GetViewShell() != nullptr) + mnCurrentPageBeforeSwitch = (mrSlideSorter.GetViewShell()->GetViewShellBase() + .GetMainViewShell()->GetActualPage()->GetPageNum()-1)/2; +} + +void SlideSorterController::ChangeEditMode (EditMode eEditMode) +{ + if (mrModel.GetEditMode() != eEditMode) + { + ModelChangeLock aLock (*this); + PreModelChange(); + // Do the actual edit mode switching. + bool bResult = mrModel.SetEditMode(eEditMode); + if (bResult) + HandleModelChange(); + } +} + +void SlideSorterController::FinishEditModeChange() +{ + if (mrModel.GetEditMode() == EditMode::MasterPage) + { + mpPageSelector->DeselectAllPages(); + + // Search for the master page that was determined in + // PrepareEditModeChange() and make it the current page. + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + if (pDescriptor->GetPage() == mpEditModeChangeMasterPage) + { + GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor); + mpPageSelector->SelectPage(pDescriptor); + break; + } + } + } + else + { + PageSelector::BroadcastLock aBroadcastLock (*mpPageSelector); + + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(mnCurrentPageBeforeSwitch)); + GetCurrentSlideManager()->SwitchCurrentSlide(pDescriptor); + + // Restore the selection. + mpPageSelector->DeselectAllPages(); + for (const auto& rpPage : maSelectionBeforeSwitch) + { + mpPageSelector->SelectPage(rpPage); + } + maSelectionBeforeSwitch.clear( ); + } + mpEditModeChangeMasterPage = nullptr; +} + +void SlideSorterController::PageNameHasChanged (int nPageIndex, const OUString& rsOldName) +{ + // Request a repaint for the page object whose name has changed. + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + mrView.RequestRepaint(pDescriptor); + + // Get a pointer to the corresponding accessible object and notify + // that of the name change. + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if ( ! pWindow) + return; + + css::uno::Reference< css::accessibility::XAccessible > + xAccessible (pWindow->GetAccessible(false)); + if ( ! xAccessible.is()) + return; + + // Now comes a small hack. We assume that the accessible object is + // an instantiation of AccessibleSlideSorterView and cast it to that + // class. The cleaner alternative to this cast would be a new member + // in which we would store the last AccessibleSlideSorterView object + // created by SlideSorterViewShell::CreateAccessibleDocumentView(). + // But then there is no guaranty that the accessible object obtained + // from the window really is that instance last created by + // CreateAccessibleDocumentView(). + // However, the dynamic cast together with the check of the result + // being NULL should be safe enough. + ::accessibility::AccessibleSlideSorterView* pAccessibleView + = dynamic_cast< ::accessibility::AccessibleSlideSorterView*>(xAccessible.get()); + if (pAccessibleView == nullptr) + return; + + ::accessibility::AccessibleSlideSorterObject* pChild + = pAccessibleView->GetAccessibleChildImplementation(nPageIndex); + if (pChild == nullptr || pChild->GetPage() == nullptr) + return; + + OUString sNewName (pChild->GetPage()->GetName()); + pChild->FireAccessibleEvent( + css::accessibility::AccessibleEventId::NAME_CHANGED, + Any(rsOldName), + Any(sNewName)); +} + +void SlideSorterController::SetDocumentSlides (const Reference& rxSlides) +{ + if (mrModel.GetDocumentSlides() != rxSlides) + { + ModelChangeLock aLock (*this); + PreModelChange(); + + mrModel.SetDocumentSlides(rxSlides); + } +} + +VisibleAreaManager& SlideSorterController::GetVisibleAreaManager() const +{ + OSL_ASSERT(mpVisibleAreaManager); + return *mpVisibleAreaManager; +} + +void SlideSorterController::CheckForMasterPageAssignment() +{ + if (mrModel.GetPageCount()%2==0) + return; + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + if (pDescriptor->UpdateMasterPage()) + { + mrView.GetPreviewCache()->InvalidatePreviewBitmap ( + pDescriptor->GetPage()); + } + } +} + +void SlideSorterController::CheckForSlideTransitionAssignment() +{ + if (mrModel.GetPageCount()%2==0) + return; + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + if (pDescriptor->UpdateTransitionFlag()) + { + mrView.GetPreviewCache()->InvalidatePreviewBitmap ( + pDescriptor->GetPage()); + } + } +} + +//===== SlideSorterController::ModelChangeLock ================================ + +SlideSorterController::ModelChangeLock::ModelChangeLock ( + SlideSorterController& rController) + : mpController(&rController) +{ + mpController->LockModelChange(); +} + +SlideSorterController::ModelChangeLock::~ModelChangeLock() COVERITY_NOEXCEPT_FALSE +{ + Release(); +} + +void SlideSorterController::ModelChangeLock::Release() +{ + if (mpController != nullptr) + { + mpController->UnlockModelChange(); + mpController = nullptr; + } +} + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsAnimationFunction.cxx b/sd/source/ui/slidesorter/controller/SlsAnimationFunction.cxx new file mode 100644 index 000000000..31978baf7 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsAnimationFunction.cxx @@ -0,0 +1,129 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +namespace sd::slidesorter::controller { + +//===== AnimationBezierFunction =============================================== + +AnimationBezierFunction::AnimationBezierFunction ( + const double nX1, + const double nY1) + : mnX1(nX1), + mnY1(nY1), + mnX2(1-nY1), + mnY2(1-nX1) +{ +} + +::basegfx::B2DPoint AnimationBezierFunction::operator() (const double nT) +{ + return ::basegfx::B2DPoint( + EvaluateComponent(nT, mnX1, mnX2), + EvaluateComponent(nT, mnY1, mnY2)); +} + +double AnimationBezierFunction::EvaluateComponent ( + const double nT, + const double nV1, + const double nV2) +{ + const double nS (1-nT); + + // While the control point values 1 and 2 are explicitly given the start + // and end values are implicitly given. + const double nV0 (0); + const double nV3 (1); + + const double nV01 (nS*nV0 + nT*nV1); + const double nV12 (nS*nV1 + nT*nV2); + const double nV23 (nS*nV2 + nT*nV3); + + const double nV012 (nS*nV01 + nT*nV12); + const double nV123 (nS*nV12 + nT*nV23); + + const double nV0123 (nS*nV012 + nT*nV123); + + return nV0123; +} + +//===== AnimationParametricFunction =========================================== + +AnimationParametricFunction::AnimationParametricFunction (const ParametricFunction& rFunction) +{ + const sal_Int32 nSampleCount (64); + + // Sample the given parametric function. + ::std::vector aPoints; + aPoints.reserve(nSampleCount); + for (sal_Int32 nIndex=0; nIndex nX1 && nIndex(nX * maY.size())); + const double nX0 (nIndex0 / double(maY.size()-1)); + const sal_uInt32 nIndex1 (nIndex0 + 1); + const double nX1 (nIndex1 / double(maY.size()-1)); + + if (nIndex0<=0) + return maY[0]; + else if (o3tl::make_unsigned(nIndex0)>=maY.size() || nIndex1>=maY.size()) + return maY[maY.size()-1]; + + const double nU ((nX-nX1) / (nX0 - nX1)); + return maY[nIndex0]*nU + maY[nIndex1]*(1-nU); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsAnimator.cxx b/sd/source/ui/slidesorter/controller/SlsAnimator.cxx new file mode 100644 index 000000000..b400ec4dc --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsAnimator.cxx @@ -0,0 +1,280 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +namespace sd::slidesorter::controller { + +/** Handle one animation function by using a timer for frequent calls to + the animations operator(). +*/ +class Animator::Animation +{ +public: + Animation ( + const Animator::AnimationFunctor& rAnimation, + const double nStartOffset, + const double nDuration, + const double nGlobalTime, + const Animator::AnimationId nAnimationId, + const Animator::FinishFunctor& rFinishFunctor); + /** Run next animation step. If animation has reached its end it is + expired. + */ + bool Run (const double nGlobalTime); + + /** Typically called when an animation has finished, but also from + Animator::Disposed(). The finish functor is called and the + animation is marked as expired to prevent another run. + */ + void Expire(); + bool IsExpired() const { return mbIsExpired;} + + Animator::AnimationFunctor maAnimation; + Animator::FinishFunctor maFinishFunctor; + const Animator::AnimationId mnAnimationId; + const double mnDuration; + const double mnEnd; + const double mnGlobalTimeAtStart; + bool mbIsExpired; +}; + +Animator::Animator (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + maIdle("sd slidesorter controller Animator"), + mbIsDisposed(false), + mnNextAnimationId(0) +{ + maIdle.SetPriority(TaskPriority::REPAINT); + maIdle.SetInvokeHandler(LINK(this,Animator,TimeoutHandler)); +} + +Animator::~Animator() +{ + if ( ! mbIsDisposed) + { + OSL_ASSERT(mbIsDisposed); + Dispose(); + } +} + +void Animator::Dispose() +{ + mbIsDisposed = true; + + AnimationList aCopy (maAnimations); + for (const auto& rxAnimation : aCopy) + rxAnimation->Expire(); + + maIdle.Stop(); + if (mpDrawLock) + { + mpDrawLock->Dispose(); + mpDrawLock.reset(); + } +} + +Animator::AnimationId Animator::AddAnimation ( + const AnimationFunctor& rAnimation, + const FinishFunctor& rFinishFunctor) +{ + // When the animator is already disposed then ignore this call + // silently (well, we show an assertion, but do not throw an exception.) + OSL_ASSERT( ! mbIsDisposed); + if (mbIsDisposed) + return -1; + + std::shared_ptr pAnimation = + std::make_shared( + rAnimation, + 0, + 300 / 1000.0, + maElapsedTime.getElapsedTime(), + ++mnNextAnimationId, + rFinishFunctor); + maAnimations.push_back(pAnimation); + + RequestNextFrame(); + + return pAnimation->mnAnimationId; +} + +void Animator::RemoveAnimation (const Animator::AnimationId nId) +{ + OSL_ASSERT( ! mbIsDisposed); + + const AnimationList::iterator iAnimation (::std::find_if( + maAnimations.begin(), + maAnimations.end(), + [nId] (std::shared_ptr const& pAnim) + { return nId == pAnim->mnAnimationId; })); + if (iAnimation != maAnimations.end()) + { + OSL_ASSERT((*iAnimation)->mnAnimationId == nId); + (*iAnimation)->Expire(); + maAnimations.erase(iAnimation); + } + + if (maAnimations.empty()) + { + // Reset the animation id when we can. + mnNextAnimationId = 0; + + // No more animations => we do not have to suppress painting + // anymore. + mpDrawLock.reset(); + } +} + +void Animator::RemoveAllAnimations() +{ + for (auto const& it : maAnimations) + { + it->Expire(); + } + maAnimations.clear(); + mnNextAnimationId = 0; + + // No more animations => we do not have to suppress painting + // anymore. + mpDrawLock.reset(); +} + +bool Animator::ProcessAnimations (const double nTime) +{ + bool bExpired (false); + + OSL_ASSERT( ! mbIsDisposed); + if (mbIsDisposed) + return bExpired; + + AnimationList aCopy (maAnimations); + for (const auto& rxAnimation : aCopy) + { + bExpired |= rxAnimation->Run(nTime); + } + + return bExpired; +} + +void Animator::CleanUpAnimationList() +{ + OSL_ASSERT( ! mbIsDisposed); + if (mbIsDisposed) + return; + + AnimationList aActiveAnimations; + + for (const auto& rxAnimation : maAnimations) + { + if ( ! rxAnimation->IsExpired()) + aActiveAnimations.push_back(rxAnimation); + } + + maAnimations.swap(aActiveAnimations); +} + +void Animator::RequestNextFrame () +{ + if ( ! maIdle.IsActive()) + { + // Prevent redraws except for the ones in TimeoutHandler. While the + // Animator is active it will schedule repaints regularly. Repaints + // in between would only lead to visual artifacts. + mpDrawLock.reset(new view::SlideSorterView::DrawLock(mrSlideSorter)); + maIdle.Start(); + } +} + +IMPL_LINK_NOARG(Animator, TimeoutHandler, Timer *, void) +{ + if (mbIsDisposed) + return; + + if (ProcessAnimations(maElapsedTime.getElapsedTime())) + CleanUpAnimationList(); + + // Unlock the draw lock. This should lead to a repaint. + mpDrawLock.reset(); + + if (!maAnimations.empty()) + RequestNextFrame(); +} + +//===== Animator::Animation =================================================== + +Animator::Animation::Animation ( + const Animator::AnimationFunctor& rAnimation, + const double nStartOffset, + const double nDuration, + const double nGlobalTime, + const Animator::AnimationId nId, + const Animator::FinishFunctor& rFinishFunctor) + : maAnimation(rAnimation), + maFinishFunctor(rFinishFunctor), + mnAnimationId(nId), + mnDuration(nDuration), + mnEnd(nGlobalTime + nDuration + nStartOffset), + mnGlobalTimeAtStart(nGlobalTime + nStartOffset), + mbIsExpired(false) +{ + Run(nGlobalTime); +} + +bool Animator::Animation::Run (const double nGlobalTime) +{ + if ( ! mbIsExpired) + { + if (mnDuration > 0) + { + if (nGlobalTime >= mnEnd) + { + maAnimation(1.0); + Expire(); + } + else if (nGlobalTime >= mnGlobalTimeAtStart) + { + maAnimation((nGlobalTime - mnGlobalTimeAtStart) / mnDuration); + } + } + else if (mnDuration < 0) + { + // Animations without end have to be expired by their owner. + maAnimation(nGlobalTime); + } + } + + return mbIsExpired; +} + +void Animator::Animation::Expire() +{ + if ( ! mbIsExpired) + { + mbIsExpired = true; + if (maFinishFunctor) + maFinishFunctor(); + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsClipboard.cxx b/sd/source/ui/slidesorter/controller/SlsClipboard.cxx new file mode 100644 index 000000000..160077e64 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsClipboard.cxx @@ -0,0 +1,918 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd::slidesorter::controller { + +namespace { +/** Temporarily deactivate slide tracking of the VisibleAreaManager. + This is used as a workaround to avoid unwanted repositioning of + the visible area when the selection of slides is copied to the + clipboard (cloning of slides leads to model change notifications + for the original model.) +*/ +class TemporarySlideTrackingDeactivator +{ +public: + explicit TemporarySlideTrackingDeactivator (SlideSorterController& rController) + : mrController(rController), + mbIsCurrentSlideTrackingActive ( + mrController.GetVisibleAreaManager().IsCurrentSlideTrackingActive()) + { + if (mbIsCurrentSlideTrackingActive) + mrController.GetVisibleAreaManager().DeactivateCurrentSlideTracking(); + } + ~TemporarySlideTrackingDeactivator() + { + if (mbIsCurrentSlideTrackingActive) + mrController.GetVisibleAreaManager().ActivateCurrentSlideTracking(); + } + +private: + SlideSorterController& mrController; + const bool mbIsCurrentSlideTrackingActive; +}; +} // end of anonymous namespace + +class Clipboard::UndoContext +{ +public: + UndoContext ( + SdDrawDocument* pDocument, + const std::shared_ptr& rpMainViewShell) + : mpDocument(pDocument), + mpMainViewShell(rpMainViewShell) + { + if (mpDocument!=nullptr && mpDocument->IsUndoEnabled()) + { + if (mpMainViewShell && mpMainViewShell->GetShellType() == ViewShell::ST_DRAW) + mpDocument->BegUndo(SdResId(STRING_DRAG_AND_DROP_PAGES)); + else + mpDocument->BegUndo(SdResId(STRING_DRAG_AND_DROP_SLIDES)); + } + } + + ~UndoContext() + { + if (mpDocument!=nullptr && mpDocument->IsUndoEnabled()) + mpDocument->EndUndo(); + if (mpMainViewShell && mpMainViewShell->GetViewFrame()!=nullptr) + { + SfxBindings& rBindings = mpMainViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_UNDO); + rBindings.Invalidate(SID_REDO); + } + } +private: + SdDrawDocument* mpDocument; + std::shared_ptr mpMainViewShell; +}; + +Clipboard::Clipboard (SlideSorter& rSlideSorter) + : ViewClipboard(rSlideSorter.GetView()), + mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mnDragFinishedUserEventId(nullptr) +{ +} + +Clipboard::~Clipboard() +{ + if (mnDragFinishedUserEventId != nullptr) + Application::RemoveUserEvent(mnDragFinishedUserEventId); +} + +/** With the current implementation the forwarded calls to the current + function will come back eventually to call the local Do(Cut|Copy|Paste) + methods. A shortcut is possible but would be an unclean hack. +*/ +void Clipboard::HandleSlotCall (SfxRequest& rRequest) +{ + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + rtl::Reference xFunc; + if (pViewShell != nullptr) + xFunc = pViewShell->GetCurrentFunction(); + switch (rRequest.GetSlot()) + { + case SID_CUT: + if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage) + { + if(xFunc.is()) + xFunc->DoCut(); + else + DoCut(); + } + rRequest.Done(); + break; + + case SID_COPY: + if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage) + { + if(xFunc.is()) + xFunc->DoCopy(); + else + DoCopy(); + } + rRequest.Done(); + break; + + case SID_PASTE: + // Prevent redraws while inserting pages from the clipboard + // because the intermediate inconsistent state might lead to + // a crash. + if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage) + { + view::SlideSorterView::DrawLock aLock (mrSlideSorter); + SelectionObserver::Context aContext (mrSlideSorter); + if(xFunc.is()) + xFunc->DoPaste(); + else + DoPaste(); + } + rRequest.Done(); + break; + + case SID_DELETE: + DoDelete(); + rRequest.Done(); + break; + } +} + +void Clipboard::DoCut () +{ + if (mrSlideSorter.GetModel().GetPageCount() > 1) + { + DoCopy(); + DoDelete(); + } +} + +void Clipboard::DoDelete() +{ + if (mrSlideSorter.GetModel().GetPageCount() > 1) + { + mrController.GetSelectionManager()->DeleteSelectedPages(); + } +} + +void Clipboard::DoCopy () +{ + CreateSlideTransferable( nullptr, false ); +} + +void Clipboard::DoPaste () +{ + SdTransferable* pClipTransferable = SD_MOD()->pTransferClip; + + if (pClipTransferable==nullptr || !pClipTransferable->IsPageTransferable()) + return; + + sal_Int32 nInsertPosition = GetInsertionPosition(); + + if (nInsertPosition >= 0) + { + // Paste the pages from the clipboard. + sal_Int32 nInsertPageCount = PasteTransferable(nInsertPosition); + // Select the pasted pages and make the first of them the + // current page. + mrSlideSorter.GetContentWindow()->GrabFocus(); + SelectPageRange(nInsertPosition, nInsertPageCount); + } +} + +sal_Int32 Clipboard::GetInsertionPosition () +{ + sal_Int32 nInsertPosition = -1; + + // Determine the insertion position: + // a) When the insertion indicator is visible, then at that position. + // b) When the focus indicator is visible, then before or after the + // focused page, depending on user input to a dialog. + // c) When there is a selection but no focus, then after the + // selection. + // d) After the last page when there is no selection and no focus. + + std::shared_ptr pInsertionIndicatorHandler ( + mrController.GetInsertionIndicatorHandler()); + if (pInsertionIndicatorHandler->IsActive()) + { + // Use the insertion index of an active insertion indicator. + nInsertPosition = pInsertionIndicatorHandler->GetInsertionPageIndex(); + } + else if (mrController.GetSelectionManager()->GetInsertionPosition() >= 0) + { + // Use the insertion index of an insertion indicator that has been + // deactivated a short while ago. + nInsertPosition = mrController.GetSelectionManager()->GetInsertionPosition(); + } + else if (mrController.GetFocusManager().IsFocusShowing()) + { + // Use the focus to determine the insertion position. + vcl::Window* pWin = mrSlideSorter.GetContentWindow(); + SdInsertPasteDlg aDialog(pWin ? pWin->GetFrameWeld() : nullptr); + if (aDialog.run() == RET_OK) + { + nInsertPosition = mrController.GetFocusManager().GetFocusedPageIndex(); + if (!aDialog.IsInsertBefore()) + nInsertPosition ++; + } + } + + return nInsertPosition; +} + +sal_Int32 Clipboard::PasteTransferable (sal_Int32 nInsertPosition) +{ + SdTransferable* pClipTransferable = SD_MOD()->pTransferClip; + model::SlideSorterModel& rModel (mrSlideSorter.GetModel()); + bool bMergeMasterPages = !pClipTransferable->HasSourceDoc (rModel.GetDocument()); + sal_uInt16 nInsertIndex (rModel.GetCoreIndex(nInsertPosition)); + sal_Int32 nInsertPageCount (0); + if (pClipTransferable->HasPageBookmarks()) + { + const std::vector &rBookmarkList = pClipTransferable->GetPageBookmarks(); + const SolarMutexGuard aGuard; + + nInsertPageCount = static_cast(rBookmarkList.size()); + rModel.GetDocument()->InsertBookmarkAsPage( + rBookmarkList, + nullptr, + false, + false, + nInsertIndex, + false, + pClipTransferable->GetPageDocShell(), + true, + bMergeMasterPages, + false); + } + else + { + SfxObjectShell* pShell = pClipTransferable->GetDocShell().get(); + DrawDocShell* pDataDocSh = static_cast(pShell); + SdDrawDocument* pDataDoc = pDataDocSh->GetDoc(); + + if (pDataDoc!=nullptr + && pDataDoc->GetSdPageCount(PageKind::Standard)) + { + const SolarMutexGuard aGuard; + + bMergeMasterPages = (pDataDoc != rModel.GetDocument()); + nInsertPageCount = pDataDoc->GetSdPageCount( PageKind::Standard ); + rModel.GetDocument()->InsertBookmarkAsPage( + std::vector(), + nullptr, + false, + false, + nInsertIndex, + false, + pDataDocSh, + true, + bMergeMasterPages, + false); + } + } + mrController.HandleModelChange(); + return nInsertPageCount; +} + +void Clipboard::SelectPageRange (sal_Int32 nFirstIndex, sal_Int32 nPageCount) +{ + // Select the newly inserted pages. That are the nInsertPageCount pages + // after the nInsertIndex position. + PageSelector& rSelector (mrController.GetPageSelector()); + rSelector.DeselectAllPages(); + for (sal_Int32 i=0; iSwitchCurrentSlide(pDescriptor); + } + } + } +} + +void Clipboard::CreateSlideTransferable ( + vcl::Window* pWindow, + bool bDrag) +{ + std::vector aBookmarkList; + + // Insert all selected pages into a bookmark list and remember them in + // maPagesToRemove for possible later removal. + model::PageEnumeration aSelectedPages + (model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + SdDrawDocument* const pDocument = mrSlideSorter.GetModel().GetDocument(); + DrawDocShell* const pDataDocSh = pDocument->GetDocSh(); + + sal_Int32 nUniqueID = 0; + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + + //ensure that the slides have unique names + const OUString sOrigName = pDescriptor->GetPage()->GetName(); + if ( pDataDocSh && !pDataDocSh->IsPageNameUnique( sOrigName ) ) + { + OUString sUniqueName; + bool bUnique = false; + while ( !bUnique ) + { + sUniqueName = sOrigName + "_clipboard" + OUString::number(nUniqueID++); + bUnique = pDataDocSh->IsNewPageNameValid( sUniqueName ); + if ( bUnique ) + pDescriptor->GetPage()->SetName(sUniqueName); + } + } + + aBookmarkList.push_back(pDescriptor->GetPage()->GetName()); + maPagesToRemove.push_back (pDescriptor->GetPage()); + } + + // Create a small set of representatives of the selection for which + // previews are included into the transferable so that an insertion + // indicator can be rendered. + aSelectedPages.Rewind(); + ::std::vector aRepresentatives; + aRepresentatives.reserve(3); + std::shared_ptr pPreviewCache ( + mrSlideSorter.GetView().GetPreviewCache()); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + if ( ! pDescriptor || pDescriptor->GetPage()==nullptr) + continue; + BitmapEx aPreview (pPreviewCache->GetPreviewBitmap(pDescriptor->GetPage(), false)); + aRepresentatives.emplace_back( + aPreview, + pDescriptor->HasState(model::PageDescriptor::ST_Excluded)); + if (aRepresentatives.size() >= 3) + break; + } + + if (aBookmarkList.empty()) + return; + + mrSlideSorter.GetView().BrkAction(); + rtl::Reference pTransferable = TransferableData::CreateTransferable ( + pDocument, + dynamic_cast(mrSlideSorter.GetViewShell()), + std::move(aRepresentatives)); + + if (bDrag) + SD_MOD()->pTransferDrag = pTransferable.get(); + else + SD_MOD()->pTransferClip = pTransferable.get(); + + pDocument->CreatingDataObj (pTransferable.get()); + pTransferable->SetWorkDocument(pDocument->AllocSdDrawDocument()); + std::unique_ptr pObjDesc(new TransferableObjectDescriptor); + pTransferable->GetWorkDocument()->GetDocSh() + ->FillTransferableObjectDescriptor (*pObjDesc); + + if (pDataDocSh != nullptr) + pObjDesc->maDisplayName = pDataDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + + vcl::Window* pActionWindow = pWindow; + if (pActionWindow == nullptr) + { + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != nullptr) + pActionWindow = pViewShell->GetActiveWindow(); + } + + assert(pActionWindow); + + pTransferable->SetStartPos (pActionWindow->PixelToLogic( + pActionWindow->GetPointerPosPixel())); + pTransferable->SetObjectDescriptor (std::move(pObjDesc)); + + { + TemporarySlideTrackingDeactivator aDeactivator (mrController); + pTransferable->SetPageBookmarks (std::move(aBookmarkList), !bDrag); + } + + if (bDrag) + { + pTransferable->SetView (&mrSlideSorter.GetView()); + pTransferable->StartDrag (pActionWindow, DND_ACTION_COPY | DND_ACTION_MOVE); + } + else + pTransferable->CopyToClipboard (pActionWindow); + + pDocument->CreatingDataObj(nullptr); +} + +std::shared_ptr Clipboard::CreateTransferableUserData (SdTransferable* pTransferable) +{ + do + { + SdPageObjsTLV::SdPageObjsTransferable* pTreeListBoxTransferable + = dynamic_cast(pTransferable); + if (pTreeListBoxTransferable == nullptr) + break; + + // Find view shell for the document of the transferable. + ::sd::ViewShell* pViewShell + = SdPageObjsTLV::GetViewShellForDocShell(pTreeListBoxTransferable->GetDocShell()); + if (pViewShell == nullptr) + break; + + // Find slide sorter for the document of the transferable. + SlideSorterViewShell* pSlideSorterViewShell + = SlideSorterViewShell::GetSlideSorter(pViewShell->GetViewShellBase()); + if (pSlideSorterViewShell == nullptr) + break; + SlideSorter& rSlideSorter (pSlideSorterViewShell->GetSlideSorter()); + + // Get bookmark from transferable. + TransferableDataHelper aDataHelper (pTransferable); + INetBookmark aINetBookmark; + if ( ! aDataHelper.GetINetBookmark(SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark)) + break; + const OUString sURL (aINetBookmark.GetURL()); + const sal_Int32 nIndex (sURL.indexOf('#')); + if (nIndex == -1) + break; + OUString sBookmark (sURL.copy(nIndex+1)); + + // Make sure that the bookmark points to a page. + SdDrawDocument* pTransferableDocument = rSlideSorter.GetModel().GetDocument(); + if (pTransferableDocument == nullptr) + break; + bool bIsMasterPage = false; + const sal_uInt16 nPageIndex (pTransferableDocument->GetPageByName(sBookmark, bIsMasterPage)); + if (nPageIndex == SDRPAGE_NOTFOUND) + break; + + // Create preview. + ::std::vector aRepresentatives; + aRepresentatives.reserve(1); + std::shared_ptr pPreviewCache ( + rSlideSorter.GetView().GetPreviewCache()); + model::SharedPageDescriptor pDescriptor (rSlideSorter.GetModel().GetPageDescriptor((nPageIndex-1)/2)); + if ( ! pDescriptor || pDescriptor->GetPage()==nullptr) + break; + BitmapEx aPreview (pPreviewCache->GetPreviewBitmap(pDescriptor->GetPage(), false)); + aRepresentatives.emplace_back( + aPreview, + pDescriptor->HasState(model::PageDescriptor::ST_Excluded)); + + // Remember the page in maPagesToRemove so that it can be removed + // when drag and drop action is "move". + Clipboard& rOtherClipboard (pSlideSorterViewShell->GetSlideSorter().GetController().GetClipboard()); + rOtherClipboard.maPagesToRemove.clear(); + rOtherClipboard.maPagesToRemove.push_back(pDescriptor->GetPage()); + + // Create the new transferable. + std::shared_ptr pNewTransferable = + std::make_shared( + pSlideSorterViewShell, + std::move(aRepresentatives)); + pTransferable->SetWorkDocument(pTreeListBoxTransferable->GetSourceDoc()->AllocSdDrawDocument()); + // pTransferable->SetView(&mrSlideSorter.GetView()); + + // Set page bookmark list. + std::vector aPageBookmarks { sBookmark }; + pTransferable->SetPageBookmarks(std::move(aPageBookmarks), false); + + // Replace the view referenced by the transferable with the + // corresponding slide sorter view. + pTransferable->SetView(&pSlideSorterViewShell->GetSlideSorter().GetView()); + + return pNewTransferable; + } + while (false); + + return std::shared_ptr(); +} + +void Clipboard::StartDrag ( + const Point& rPosition, + vcl::Window* pWindow) +{ + maPagesToRemove.clear(); + CreateSlideTransferable(pWindow, true); + + mrController.GetInsertionIndicatorHandler()->UpdatePosition( + rPosition, + InsertionIndicatorHandler::UnknownMode); +} + +void Clipboard::DragFinished (sal_Int8 nDropAction) +{ + if (mnDragFinishedUserEventId == nullptr) + { + mnDragFinishedUserEventId = Application::PostUserEvent( + LINK(this, Clipboard, ProcessDragFinished), + reinterpret_cast(nDropAction)); + } +} + +IMPL_LINK(Clipboard, ProcessDragFinished, void*, pUserData, void) +{ + const sal_Int8 nDropAction (static_cast(reinterpret_cast(pUserData))); + + mnDragFinishedUserEventId = nullptr; + + // Hide the substitution display and insertion indicator. + ::rtl::Reference pFunction (mrController.GetCurrentSelectionFunction()); + if (pFunction.is()) + pFunction->NotifyDragFinished(); + + PageSelector& rSelector (mrController.GetPageSelector()); + if ((nDropAction & DND_ACTION_MOVE) != 0 + && ! maPagesToRemove.empty()) + { + // Remove the pages that have been moved to another place (possibly + // in the same document.) + rSelector.DeselectAllPages(); + for (const auto& rpDraggedPage : maPagesToRemove) + { + rSelector.SelectPage(rpDraggedPage); + } + mrController.GetSelectionManager()->DeleteSelectedPages(); + } + mxUndoContext.reset(); + mxSelectionObserverContext.reset(); +} + +sal_Int8 Clipboard::AcceptDrop ( + const AcceptDropEvent& rEvent, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + sal_Int8 nAction (DND_ACTION_NONE); + + const Clipboard::DropType eDropType (IsDropAccepted()); + + switch (eDropType) + { + case DT_PAGE: + case DT_PAGE_FROM_NAVIGATOR: + { + // Accept a drop. + nAction = rEvent.mnAction; + + // Use the copy action when the drop action is the default, i.e. not + // explicitly set to move or link, and when the source and + // target models are not the same. + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + if (pDragTransferable != nullptr + && pDragTransferable->IsPageTransferable() + && ((rEvent.maDragEvent.DropAction + & css::datatransfer::dnd::DNDConstants::ACTION_DEFAULT) != 0) + && (mrSlideSorter.GetModel().GetDocument()->GetDocSh() + != pDragTransferable->GetPageDocShell())) + { + nAction = DND_ACTION_COPY; + } + else if (IsInsertionTrivial(pDragTransferable, nAction)) + { + nAction = DND_ACTION_NONE; + } + + // Show the insertion marker and the substitution for a drop. + SelectionFunction* pSelectionFunction = dynamic_cast( + mrSlideSorter.GetViewShell()->GetCurrentFunction().get()); + if (pSelectionFunction != nullptr) + pSelectionFunction->MouseDragged(rEvent, nAction); + + // Scroll the window when the mouse reaches the window border. + // mrController.GetScrollBarManager().AutoScroll (rEvent.maPosPixel); + } + break; + + case DT_SHAPE: + nAction = ExecuteOrAcceptShapeDrop( + DC_ACCEPT, + rEvent.maPosPixel, + &rEvent, + rTargetHelper, + pTargetWindow, + nPage, + nLayer); + break; + + default: + case DT_NONE: + nAction = DND_ACTION_NONE; + break; + } + + return nAction; +} + +sal_Int8 Clipboard::ExecuteDrop ( + const ExecuteDropEvent& rEvent, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + sal_Int8 nResult = DND_ACTION_NONE; + mxUndoContext.reset(); + const Clipboard::DropType eDropType (IsDropAccepted()); + + switch (eDropType) + { + case DT_PAGE: + case DT_PAGE_FROM_NAVIGATOR: + { + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + const Point aEventModelPosition ( + pTargetWindow->PixelToLogic (rEvent.maPosPixel)); + const sal_Int32 nXOffset (std::abs (pDragTransferable->GetStartPos().X() + - aEventModelPosition.X())); + const sal_Int32 nYOffset (std::abs (pDragTransferable->GetStartPos().Y() + - aEventModelPosition.Y())); + bool bContinue = + ( pDragTransferable->GetView() != &mrSlideSorter.GetView() ) + || ( nXOffset >= 2 && nYOffset >= 2 ); + + std::shared_ptr pInsertionIndicatorHandler( + mrController.GetInsertionIndicatorHandler()); + // Get insertion position and then turn off the insertion indicator. + pInsertionIndicatorHandler->UpdatePosition(aEventModelPosition, rEvent.mnAction); + // sal_uInt16 nIndex = DetermineInsertPosition(*pDragTransferable); + + // Do not process the insertion when it is trivial, + // i.e. would insert pages at their original place. + if (IsInsertionTrivial(pDragTransferable, rEvent.mnAction)) + bContinue = false; + + // Tell the insertion indicator handler to hide before the model + // is modified. Doing it later may result in page objects whose + // animation state is not properly reset because they are then + // in another run then before the model change. + pInsertionIndicatorHandler->End(Animator::AM_Immediate); + + if (bContinue) + { + SlideSorterController::ModelChangeLock aModelChangeLock (mrController); + + // Handle a general drop operation. + mxUndoContext.reset(new UndoContext ( + mrSlideSorter.GetModel().GetDocument(), + mrSlideSorter.GetViewShell()->GetViewShellBase().GetMainViewShell())); + mxSelectionObserverContext.reset(new SelectionObserver::Context(mrSlideSorter)); + + if (rEvent.mnAction == DND_ACTION_MOVE) + { + SdDrawDocument* pDoc = mrSlideSorter.GetModel().GetDocument(); + const bool bDoesMakePageObjectsNamesUnique = pDoc->DoesMakePageObjectsNamesUnique(); + pDoc->DoMakePageObjectsNamesUnique(false); + HandlePageDrop(*pDragTransferable); + pDoc->DoMakePageObjectsNamesUnique(bDoesMakePageObjectsNamesUnique); + } + else + HandlePageDrop(*pDragTransferable); + + nResult = rEvent.mnAction; + + // We leave the undo context alive for when moving or + // copying inside one view then the actions in + // NotifyDragFinished should be covered as well as + // well as the ones above. + } + + // When the pages originated in another slide sorter then + // only that is notified automatically about the drag + // operation being finished. Because the target slide sorter + // has be notified, too, add a callback for that. + std::shared_ptr pSlideSorterTransferable ( + TransferableData::GetFromTransferable(pDragTransferable)); + assert(pSlideSorterTransferable); + if (pSlideSorterTransferable + && pSlideSorterTransferable->GetSourceViewShell() != mrSlideSorter.GetViewShell()) + { + DragFinished(nResult); + } + + // Notify the receiving selection function that drag-and-drop is + // finished and the substitution handler can be released. + ::rtl::Reference pFunction ( + mrController.GetCurrentSelectionFunction()); + if (pFunction.is()) + pFunction->NotifyDragFinished(); + } + break; + + case DT_SHAPE: + nResult = ExecuteOrAcceptShapeDrop( + DC_EXECUTE, + rEvent.maPosPixel, + &rEvent, + rTargetHelper, + pTargetWindow, + nPage, + nLayer); + break; + + default: + case DT_NONE: + break; + } + + return nResult; +} + +bool Clipboard::IsInsertionTrivial ( + SdTransferable const * pTransferable, + const sal_Int8 nDndAction) const +{ + std::shared_ptr pSlideSorterTransferable ( + TransferableData::GetFromTransferable(pTransferable)); + if (pSlideSorterTransferable + && pSlideSorterTransferable->GetSourceViewShell() != mrSlideSorter.GetViewShell()) + return false; + return mrController.GetInsertionIndicatorHandler()->IsInsertionTrivial(nDndAction); +} + +void Clipboard::Abort() +{ + if (mxSelectionObserverContext) + { + mxSelectionObserverContext->Abort(); + mxSelectionObserverContext.reset(); + } +} + +sal_uInt16 Clipboard::DetermineInsertPosition () +{ + // Tell the model to move the dragged pages behind the one with the + // index nInsertionIndex which first has to be transformed into an index + // understandable by the document. + const sal_Int32 nInsertionIndex ( + mrController.GetInsertionIndicatorHandler()->GetInsertionPageIndex()); + + // Convert to insertion index to that of an SdModel. + if (nInsertionIndex >= 0) + return mrSlideSorter.GetModel().GetCoreIndex(nInsertionIndex); + else + return 0; +} + +Clipboard::DropType Clipboard::IsDropAccepted() const +{ + const SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + if (pDragTransferable == nullptr) + return DT_NONE; + + if (pDragTransferable->IsPageTransferable()) + { + if (mrSlideSorter.GetModel().GetEditMode() != EditMode::MasterPage) + return DT_PAGE; + else + return DT_NONE; + } + + const SdPageObjsTLV::SdPageObjsTransferable* pPageObjsTransferable + = dynamic_cast(pDragTransferable); + if (pPageObjsTransferable != nullptr) + return DT_PAGE_FROM_NAVIGATOR; + + return DT_SHAPE; +} + +sal_Int8 Clipboard::ExecuteOrAcceptShapeDrop ( + DropCommand eCommand, + const Point& rPosition, + const void* pDropEvent, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + sal_Int8 nResult = 0; + + // The dropping of a shape is accepted or executed only when there is + // DrawViewShell available to which we can forward this call. This has + // technical reasons: The actual code to accept or execute a shape drop + // is implemented in the ViewShell class and uses the page view of the + // main edit view. This is not possible without a DrawViewShell. + std::shared_ptr pDrawViewShell; + if (mrSlideSorter.GetViewShell() != nullptr) + pDrawViewShell = std::dynamic_pointer_cast( + mrSlideSorter.GetViewShell()->GetViewShellBase().GetMainViewShell()); + if (pDrawViewShell != nullptr + && (pDrawViewShell->GetShellType() == ViewShell::ST_IMPRESS + || pDrawViewShell->GetShellType() == ViewShell::ST_DRAW)) + { + // The drop is only accepted or executed when it takes place over a + // page object. Therefore we replace a missing page number by the + // number of the page under the mouse. + if (nPage == SDRPAGE_NOTFOUND) + { + model::SharedPageDescriptor pDescriptor ( + mrSlideSorter.GetModel().GetPageDescriptor( + mrSlideSorter.GetView().GetPageIndexAtPoint(rPosition))); + if (pDescriptor) + nPage = pDescriptor->GetPageIndex(); + } + + // Now comes the code that is different for the Execute and Accept: + // We simply forward the call to the AcceptDrop() or ExecuteDrop() + // methods of the DrawViewShell in the center pane. + if (nPage != SDRPAGE_NOTFOUND) + switch (eCommand) + { + case DC_ACCEPT: + nResult = pDrawViewShell->AcceptDrop( + *static_cast(pDropEvent), + rTargetHelper, + pTargetWindow, + nPage, + nLayer); + break; + + case DC_EXECUTE: + nResult = pDrawViewShell->ExecuteDrop( + *static_cast(pDropEvent), + rTargetHelper, + pTargetWindow, + nPage, + nLayer); + break; + } + } + + return nResult; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx b/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx new file mode 100644 index 000000000..9203c06e8 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsCurrentSlideManager.cxx @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +using namespace ::sd::slidesorter::model; + +namespace sd::slidesorter::controller { + +CurrentSlideManager::CurrentSlideManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mnCurrentSlideIndex(-1), + maSwitchPageDelayTimer("sd CurrentSlideManager maSwitchPageDelayTimer") +{ + maSwitchPageDelayTimer.SetTimeout(100); + maSwitchPageDelayTimer.SetInvokeHandler(LINK(this,CurrentSlideManager,SwitchPageCallback)); +} + +CurrentSlideManager::~CurrentSlideManager() +{ +} + +void CurrentSlideManager::NotifyCurrentSlideChange (const SdPage* pPage) +{ + if (pPage != nullptr) + NotifyCurrentSlideChange( + mrSlideSorter.GetModel().GetIndex( + Reference( + const_cast(pPage)->getUnoPage(), + UNO_QUERY))); + else + NotifyCurrentSlideChange(-1); +} + +void CurrentSlideManager::NotifyCurrentSlideChange (const sal_Int32 nSlideIndex) +{ + if (mnCurrentSlideIndex == nSlideIndex) + return; + + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter.GetController().GetPageSelector()); + + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + + ReleaseCurrentSlide(); + AcquireCurrentSlide(nSlideIndex); + + // Update the selection. + if (mpCurrentSlide) + { + mrSlideSorter.GetController().GetPageSelector().SelectPage(mpCurrentSlide); + mrSlideSorter.GetController().GetFocusManager().SetFocusedPage(mpCurrentSlide); + } +} + +void CurrentSlideManager::ReleaseCurrentSlide() +{ + if (mpCurrentSlide) + mrSlideSorter.GetView().SetState(mpCurrentSlide, PageDescriptor::ST_Current, false); + + mpCurrentSlide.reset(); + mnCurrentSlideIndex = -1; +} + +void CurrentSlideManager::AcquireCurrentSlide (const sal_Int32 nSlideIndex) +{ + mnCurrentSlideIndex = nSlideIndex; + + // if current slide valid + if (mnCurrentSlideIndex >= 0 && mnCurrentSlideIndexGetPage()->GetPageNum()-1)/2); + + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != nullptr && pViewShell->IsMainViewShell()) + { + // The slide sorter is the main view. + FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + pFrameView->SetSelectedPage(sal::static_int_cast(mnCurrentSlideIndex)); + mrSlideSorter.GetController().GetPageSelector().SetCoreSelection(); + } + + // We do not tell the XController/ViewShellBase about the new + // slide right away. This is done asynchronously after a short + // delay to allow for more slide switches in the slide sorter. + // This goes under the assumption that slide switching inside + // the slide sorter is fast (no expensive redraw of the new page + // (unless the preview of the new slide is not yet preset)) and + // that slide switching in the edit view is slow (all shapes of + // the new slide have to be repainted.) + maSwitchPageDelayTimer.Start(); + + // We have to store the (index of the) new current slide at + // the tab control because there are other asynchronous + // notifications of the slide switching that otherwise + // overwrite the correct value. + SetCurrentSlideAtTabControl(mpCurrentSlide); + + if (bUpdateSelection) + { + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor); + } + mrSlideSorter.GetController().GetFocusManager().SetFocusedPage(rpDescriptor); +} + +void CurrentSlideManager::SetCurrentSlideAtViewShellBase (const SharedPageDescriptor& rpDescriptor) +{ + OSL_ASSERT(rpDescriptor); + + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + DrawViewShell* pDrawViewShell = dynamic_cast( + pBase->GetMainViewShell().get()); + if (pDrawViewShell != nullptr) + { + sal_uInt16 nPageNumber = (rpDescriptor->GetPage()->GetPageNum()-1)/2; + pDrawViewShell->SwitchPage(nPageNumber); + TabControl& rPageTabControl = pDrawViewShell->GetPageTabControl(); + rPageTabControl.SetCurPageId(rPageTabControl.GetPageId(nPageNumber)); + } + } +} + +void CurrentSlideManager::SetCurrentSlideAtTabControl (const SharedPageDescriptor& rpDescriptor) +{ + OSL_ASSERT(rpDescriptor); + + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(pBase->GetMainViewShell())); + if (pDrawViewShell) + { + sal_uInt16 nPageNumber = (rpDescriptor->GetPage()->GetPageNum()-1)/2; + TabControl& rPageTabControl = pDrawViewShell->GetPageTabControl(); + rPageTabControl.SetCurPageId(rPageTabControl.GetPageId(nPageNumber)); + } + } +} + +void CurrentSlideManager::SetCurrentSlideAtXController (const SharedPageDescriptor& rpDescriptor) +{ + OSL_ASSERT(rpDescriptor); + + try + { + Reference xSet (mrSlideSorter.GetXController(), UNO_QUERY); + if (xSet.is()) + { + Any aPage; + aPage <<= rpDescriptor->GetPage()->getUnoPage(); + xSet->setPropertyValue( "CurrentPage", aPage ); + } + } + catch (const Exception&) + { + // We have not been able to set the current page at the main view. + // This is sad but still leaves us in a valid state. Therefore, + // this exception is silently ignored. + } +} + +void CurrentSlideManager::PrepareModelChange() +{ + mpCurrentSlide.reset(); +} + +void CurrentSlideManager::HandleModelChange() +{ + if (mnCurrentSlideIndex >= 0) + { + mpCurrentSlide = mrSlideSorter.GetModel().GetPageDescriptor(mnCurrentSlideIndex); + if (mpCurrentSlide) + mrSlideSorter.GetView().SetState(mpCurrentSlide, PageDescriptor::ST_Current, true); + } +} + +IMPL_LINK_NOARG(CurrentSlideManager, SwitchPageCallback, Timer *, void) +{ + if (mpCurrentSlide) + { + // Set current page. At the moment we have to do this in two + // different ways. The UNO way is the preferable one but, alas, + // it does not work always correctly (after some kinds of model + // changes). Therefore, we call DrawViewShell::SwitchPage(), + // too. + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell==nullptr || ! pViewShell->IsMainViewShell()) + SetCurrentSlideAtViewShellBase(mpCurrentSlide); + SetCurrentSlideAtXController(mpCurrentSlide); + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.cxx b/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.cxx new file mode 100644 index 000000000..f447c5656 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.cxx @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsDragAndDropContext.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd::slidesorter::controller { + +DragAndDropContext::DragAndDropContext (SlideSorter& rSlideSorter) + : mpTargetSlideSorter(&rSlideSorter), + mnInsertionIndex(-1) +{ + // No Drag-and-Drop for master pages. + if (rSlideSorter.GetModel().GetEditMode() != EditMode::Page) + return; + + // For properly handling transferables created by the navigator we + // need additional information. For this a user data object is + // created that contains the necessary information. + SdTransferable* pTransferable = SD_MOD()->pTransferDrag; + SdPageObjsTLV::SdPageObjsTransferable* pTreeListBoxTransferable + = dynamic_cast(pTransferable); + if (pTreeListBoxTransferable!=nullptr && !TransferableData::GetFromTransferable(pTransferable)) + { + pTransferable->AddUserData( + sd::slidesorter::controller::Clipboard::CreateTransferableUserData(pTransferable)); + } + + rSlideSorter.GetController().GetInsertionIndicatorHandler()->UpdateIndicatorIcon(pTransferable); +} + +DragAndDropContext::~DragAndDropContext() COVERITY_NOEXCEPT_FALSE +{ + SetTargetSlideSorter(); +} + +void DragAndDropContext::Dispose() +{ + mnInsertionIndex = -1; +} + +void DragAndDropContext::UpdatePosition ( + const Point& rMousePosition, + const InsertionIndicatorHandler::Mode eMode, + const bool bAllowAutoScroll) +{ + if (mpTargetSlideSorter == nullptr) + return; + + if (mpTargetSlideSorter->GetProperties()->IsUIReadOnly()) + return; + + // Convert window coordinates into model coordinates (we need the + // window coordinates for auto-scrolling because that remains + // constant while scrolling.) + sd::Window *pWindow = mpTargetSlideSorter->GetContentWindow().get(); + const Point aMouseModelPosition (pWindow->PixelToLogic(rMousePosition)); + std::shared_ptr pInsertionIndicatorHandler ( + mpTargetSlideSorter->GetController().GetInsertionIndicatorHandler()); + + bool bDoAutoScroll = bAllowAutoScroll + && mpTargetSlideSorter->GetController().GetScrollBarManager().AutoScroll( + rMousePosition, + [this, eMode, rMousePosition] () { + return this->UpdatePosition(rMousePosition, eMode, false); + }); + + if (!bDoAutoScroll) + { + pInsertionIndicatorHandler->UpdatePosition(aMouseModelPosition, eMode); + + // Remember the new insertion index. + mnInsertionIndex = pInsertionIndicatorHandler->GetInsertionPageIndex(); + if (pInsertionIndicatorHandler->IsInsertionTrivial(mnInsertionIndex, eMode)) + mnInsertionIndex = -1; + } +} + +void DragAndDropContext::SetTargetSlideSorter() +{ + if (mpTargetSlideSorter != nullptr) + { + mpTargetSlideSorter->GetController().GetScrollBarManager().StopAutoScroll(); + mpTargetSlideSorter->GetController().GetInsertionIndicatorHandler()->End( + Animator::AM_Animated); + } + + mpTargetSlideSorter = nullptr; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.hxx b/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.hxx new file mode 100644 index 000000000..cbeb11f8b --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsDragAndDropContext.hxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class Point; + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** A DragAndDropContext object handles an active drag and drop operation. + When the mouse is moved from one slide sorter window to another the + target SlideSorter object is exchanged accordingly. +*/ +class DragAndDropContext +{ +public: + /** Create a substitution display of the currently selected pages or, + when provided, the pages in the transferable. + */ + explicit DragAndDropContext(SlideSorter& rSlideSorter); + ~DragAndDropContext() COVERITY_NOEXCEPT_FALSE; + + /** Call this method (for example as reaction to ESC key press) to avoid + processing (ie moving or inserting) the substitution when the called + DragAndDropContext object is destroyed. + */ + void Dispose(); + + /** Move the substitution display by the distance the mouse has + travelled since the last call to this method or to + CreateSubstitution(). The given point becomes the new anchor. + */ + void UpdatePosition(const Point& rMousePosition, const InsertionIndicatorHandler::Mode eMode, + const bool bAllowAutoScroll); + + void SetTargetSlideSorter(); + +private: + SlideSorter* mpTargetSlideSorter; + sal_Int32 mnInsertionIndex; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsFocusManager.cxx b/sd/source/ui/slidesorter/controller/SlsFocusManager.cxx new file mode 100644 index 000000000..59027f5a8 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsFocusManager.cxx @@ -0,0 +1,245 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace sd::slidesorter::controller { + +FocusManager::FocusManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mnPageIndex(0), + mbPageIsFocused(false) +{ + if (mrSlideSorter.GetModel().GetPageCount() > 0) + mnPageIndex = 0; +} + +FocusManager::~FocusManager() +{ +} + +void FocusManager::MoveFocus (FocusMoveDirection eDirection) +{ + if (!(mnPageIndex >= 0 && mbPageIsFocused)) + return; + + HideFocusIndicator (GetFocusedPageDescriptor()); + + const sal_Int32 nColumnCount (mrSlideSorter.GetView().GetLayouter().GetColumnCount()); + const sal_Int32 nPageCount (mrSlideSorter.GetModel().GetPageCount()); + switch (eDirection) + { + case FocusMoveDirection::Left: + if (mnPageIndex > 0) + mnPageIndex -= 1; + break; + + case FocusMoveDirection::Right: + if (mnPageIndex < nPageCount-1) + mnPageIndex += 1; + break; + + case FocusMoveDirection::Up: + { + const sal_Int32 nCandidate (mnPageIndex - nColumnCount); + if (nCandidate >= 0) + { + // Move the focus the previous row. + mnPageIndex = nCandidate; + } + } + break; + + case FocusMoveDirection::Down: + { + const sal_Int32 nCandidate (mnPageIndex + nColumnCount); + if (nCandidate < nPageCount) + { + // Move the focus to the next row. + mnPageIndex = nCandidate; + } + } + break; + } + + if (mnPageIndex < 0) + { + OSL_ASSERT(mnPageIndex>=0); + mnPageIndex = 0; + } + else if (mnPageIndex >= nPageCount) + { + OSL_ASSERT(mnPageIndex= 0) + { + if (mbPageIsFocused) + HideFocus (); + else + ShowFocus (); + } + return mbPageIsFocused; +} + +bool FocusManager::HasFocus() const +{ + return mrSlideSorter.GetContentWindow()->HasFocus(); +} + +model::SharedPageDescriptor FocusManager::GetFocusedPageDescriptor() const +{ + return mrSlideSorter.GetModel().GetPageDescriptor(mnPageIndex); +} + +bool FocusManager::SetFocusedPage (const model::SharedPageDescriptor& rpDescriptor) +{ + if (rpDescriptor) + { + FocusHider aFocusHider (*this); + mnPageIndex = (rpDescriptor->GetPage()->GetPageNum()-1)/2; + return true; + } + return false; +} + +void FocusManager::SetFocusedPage (sal_Int32 nPageIndex) +{ + FocusHider aFocusHider (*this); + mnPageIndex = nPageIndex; +} + +bool FocusManager::SetFocusedPageToCurrentPage() +{ + return SetFocusedPage(mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()); +} + +bool FocusManager::IsFocusShowing() const +{ + return HasFocus() && mbPageIsFocused; +} + +void FocusManager::HideFocusIndicator (const model::SharedPageDescriptor& rpDescriptor) +{ + if (rpDescriptor) + { + mrSlideSorter.GetView().SetState(rpDescriptor, model::PageDescriptor::ST_Focused, false); + + // Hide focus should also fire the focus event, Currently, only accessibility add the focus listener + NotifyFocusChangeListeners(); + } +} + +void FocusManager::ShowFocusIndicator ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bScrollToFocus) +{ + if (!rpDescriptor) + return; + + mrSlideSorter.GetView().SetState(rpDescriptor, model::PageDescriptor::ST_Focused, true); + + if (bScrollToFocus) + { + // Scroll the focused page object into the visible area and repaint + // it, so that the focus indicator becomes visible. + mrSlideSorter.GetController().GetVisibleAreaManager().RequestVisible(rpDescriptor,true); + } + mrSlideSorter.GetView().RequestRepaint(rpDescriptor); + + NotifyFocusChangeListeners(); +} + +void FocusManager::AddFocusChangeListener (const Link& rListener) +{ + if (::std::find (maFocusChangeListeners.begin(), maFocusChangeListeners.end(), rListener) + == maFocusChangeListeners.end()) + { + maFocusChangeListeners.push_back (rListener); + } +} + +void FocusManager::RemoveFocusChangeListener (const Link& rListener) +{ + maFocusChangeListeners.erase ( + ::std::find (maFocusChangeListeners.begin(), maFocusChangeListeners.end(), rListener)); +} + +void FocusManager::NotifyFocusChangeListeners() const +{ + // Create a copy of the listener list to be safe when that is modified. + ::std::vector> aListeners (maFocusChangeListeners); + + // Tell the selection change listeners that the selection has changed. + for (const auto& rListener : aListeners) + { + rListener.Call(nullptr); + } +} + +FocusManager::FocusHider::FocusHider (FocusManager& rManager) +: mbFocusVisible(rManager.IsFocusShowing()) +, mrManager(rManager) +{ + mrManager.HideFocus(); +} + +FocusManager::FocusHider::~FocusHider() COVERITY_NOEXCEPT_FALSE +{ + if (mbFocusVisible) + mrManager.ShowFocus(); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsInsertionIndicatorHandler.cxx b/sd/source/ui/slidesorter/controller/SlsInsertionIndicatorHandler.cxx new file mode 100644 index 000000000..ff1a05ef1 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsInsertionIndicatorHandler.cxx @@ -0,0 +1,243 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star::datatransfer::dnd::DNDConstants; + +namespace sd::slidesorter::controller { + +InsertionIndicatorHandler::InsertionIndicatorHandler (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mpInsertionIndicatorOverlay(std::make_shared(rSlideSorter)), + meMode(MoveMode), + mbIsInsertionTrivial(false), + mbIsActive(false), + mbIsReadOnly(mrSlideSorter.GetModel().IsReadOnly()), + mbIsOverSourceView(true), + maIconSize(0,0), + mbIsForcedShow(false) +{ +} + +InsertionIndicatorHandler::~InsertionIndicatorHandler() COVERITY_NOEXCEPT_FALSE +{ +} + +void InsertionIndicatorHandler::Start (const bool bIsOverSourceView) +{ + if (mbIsActive) + { + OSL_ASSERT(!mbIsActive); + } + + mbIsReadOnly = mrSlideSorter.GetModel().IsReadOnly(); + if (mbIsReadOnly) + return; + + mbIsActive = true; + mbIsOverSourceView = bIsOverSourceView; +} + +void InsertionIndicatorHandler::End (const controller::Animator::AnimationMode eMode) +{ + if (mbIsForcedShow || ! mbIsActive || mbIsReadOnly) + return; + + GetInsertAnimator()->Reset(eMode); + + mbIsActive = false; + // maInsertPosition = view::InsertPosition(); + meMode = UnknownMode; + + mpInsertionIndicatorOverlay->Hide(); + mpInsertionIndicatorOverlay = std::make_shared(mrSlideSorter); +} + +void InsertionIndicatorHandler::ForceShow() +{ + mbIsForcedShow = true; +} + +void InsertionIndicatorHandler::ForceEnd() +{ + mbIsForcedShow = false; + End(Animator::AM_Immediate); +} + +void InsertionIndicatorHandler::UpdateIndicatorIcon (const SdTransferable* pTransferable) +{ + mpInsertionIndicatorOverlay->Create(pTransferable); + maIconSize = mpInsertionIndicatorOverlay->GetSize(); +} + +InsertionIndicatorHandler::Mode InsertionIndicatorHandler::GetModeFromDndAction ( + const sal_Int8 nDndAction) +{ + if ((nDndAction & ACTION_MOVE) != 0) + return MoveMode; + else if ((nDndAction & ACTION_COPY) != 0) + return CopyMode; + else + return UnknownMode; +} + +void InsertionIndicatorHandler::UpdatePosition ( + const Point& rMouseModelPosition, + const Mode eMode) +{ + if ( ! mbIsActive) + return; + + if (mbIsReadOnly) + return; + + SetPosition(rMouseModelPosition, eMode); +} + +void InsertionIndicatorHandler::UpdatePosition ( + const Point& rMouseModelPosition, + const sal_Int8 nDndAction) +{ + UpdatePosition(rMouseModelPosition, GetModeFromDndAction(nDndAction)); +} + +sal_Int32 InsertionIndicatorHandler::GetInsertionPageIndex() const +{ + if (mbIsReadOnly) + return -1; + else + return maInsertPosition.GetIndex(); +} + +void InsertionIndicatorHandler::SetPosition ( + const Point& rPoint, + const Mode eMode) +{ + view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter()); + + const view::InsertPosition aInsertPosition (rLayouter.GetInsertPosition( + rPoint, + maIconSize, + mrSlideSorter.GetModel())); + + if (maInsertPosition == aInsertPosition && meMode == eMode) + return; + + maInsertPosition = aInsertPosition; + meMode = eMode; + mbIsInsertionTrivial = IsInsertionTrivial(maInsertPosition.GetIndex(), eMode); + if (maInsertPosition.GetIndex()>=0 && ! mbIsInsertionTrivial) + { + mpInsertionIndicatorOverlay->SetLocation(maInsertPosition.GetLocation()); + + GetInsertAnimator()->SetInsertPosition(maInsertPosition); + mpInsertionIndicatorOverlay->Show(); + } + else + { + GetInsertAnimator()->Reset(Animator::AM_Animated); + mpInsertionIndicatorOverlay->Hide(); + } +} + +std::shared_ptr const & InsertionIndicatorHandler::GetInsertAnimator() +{ + if ( ! mpInsertAnimator) + mpInsertAnimator = std::make_shared(mrSlideSorter); + return mpInsertAnimator; +} + +bool InsertionIndicatorHandler::IsInsertionTrivial ( + const sal_Int32 nInsertionIndex, + const Mode eMode) const +{ + if (eMode == CopyMode) + return false; + else if (eMode == UnknownMode) + return true; + + if ( ! mbIsOverSourceView) + return false; + + // Iterate over all selected pages and check whether there are + // holes. While we do this we remember the indices of the first and + // last selected page as preparation for the next step. + sal_Int32 nCurrentIndex = -1; + sal_Int32 nFirstIndex = -1; + sal_Int32 nLastIndex = -1; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + + // Get the page number and compare it to the last one. + const sal_Int32 nPageNumber (pDescriptor->GetPageIndex()); + if (nCurrentIndex>=0 && nPageNumber>(nCurrentIndex+1)) + return false; + else + nCurrentIndex = nPageNumber; + + // Remember indices of the first and last page of the selection. + if (nFirstIndex == -1) + nFirstIndex = nPageNumber; + nLastIndex = nPageNumber; + } + + // When we come here then the selection has no holes. We still have + // to check that the insertion position is not directly in front or + // directly behind the selection and thus moving the selection there + // would not change the model. + return nInsertionIndex >= nFirstIndex && nInsertionIndex <= (nLastIndex+1); +} + +bool InsertionIndicatorHandler::IsInsertionTrivial (const sal_Int8 nDndAction) +{ + return IsInsertionTrivial(GetInsertionPageIndex(), GetModeFromDndAction(nDndAction)); +} + +//===== InsertionIndicatorHandler::ForceShowContext =========================== + +InsertionIndicatorHandler::ForceShowContext::ForceShowContext ( + const std::shared_ptr& rpHandler) + : mpHandler(rpHandler) +{ + mpHandler->ForceShow(); +} + +InsertionIndicatorHandler::ForceShowContext::~ForceShowContext() COVERITY_NOEXCEPT_FALSE +{ + mpHandler->ForceEnd(); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsListener.cxx b/sd/source/ui/slidesorter/controller/SlsListener.cxx new file mode 100644 index 000000000..000f42da2 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsListener.cxx @@ -0,0 +1,597 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsListener.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +namespace sd::slidesorter::controller { + +Listener::Listener ( + SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mpBase(mrSlideSorter.GetViewShellBase()), + mbListeningToDocument (false), + mbListeningToUNODocument (false), + mbListeningToController (false), + mbListeningToFrame (false), + mbIsMainViewChangePending(false) +{ + StartListening(*mrSlideSorter.GetModel().GetDocument()); + StartListening(*mrSlideSorter.GetModel().GetDocument()->GetDocSh()); + mbListeningToDocument = true; + + // Connect to the UNO document. + Reference xBroadcaster ( + mrSlideSorter.GetModel().GetDocument()->getUnoModel(), uno::UNO_QUERY); + if (xBroadcaster.is()) + { + xBroadcaster->addEventListener (this); + mbListeningToUNODocument = true; + } + + // Listen for disposing events from the document. + Reference xComponent (xBroadcaster, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener ( + Reference( + static_cast(this), UNO_QUERY)); + + // Connect to the frame to listen for controllers being exchanged. + bool bIsMainViewShell (false); + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != nullptr) + bIsMainViewShell = pViewShell->IsMainViewShell(); + if ( ! bIsMainViewShell) + { + // Listen to changes of certain properties. + Reference xFrame; + Reference xController (mrSlideSorter.GetXController()); + if (xController.is()) + xFrame = xController->getFrame(); + mxFrameWeak = xFrame; + if (xFrame.is()) + { + xFrame->addFrameActionListener(Reference(this)); + mbListeningToFrame = true; + } + + // Connect to the current controller. + ConnectToController (); + } + + // Listen for hints of the MainViewShell as well. If that is not yet + // present then the EventMultiplexer will tell us when it is available. + if (mpBase != nullptr) + { + ViewShell* pMainViewShell = mpBase->GetMainViewShell().get(); + if (pMainViewShell != nullptr + && pMainViewShell!=pViewShell) + { + StartListening(*pMainViewShell); + } + + Link aLink (LINK(this, Listener, EventMultiplexerCallback)); + mpBase->GetEventMultiplexer()->AddEventListener(aLink); + } +} + +Listener::~Listener() +{ + DBG_ASSERT( !mbListeningToDocument && !mbListeningToUNODocument && !mbListeningToFrame, + "sd::Listener::~Listener(), disposing() was not called, ask DBO!" ); +} + +void Listener::ReleaseListeners() +{ + if (mbListeningToDocument) + { + EndListening(*mrSlideSorter.GetModel().GetDocument()->GetDocSh()); + EndListening(*mrSlideSorter.GetModel().GetDocument()); + mbListeningToDocument = false; + } + + if (mbListeningToUNODocument) + { + Reference xBroadcaster ( + mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY); + if (xBroadcaster.is()) + xBroadcaster->removeEventListener (this); + + // Remove the dispose listener. + Reference xComponent (xBroadcaster, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener ( + Reference( + static_cast(this), UNO_QUERY)); + + mbListeningToUNODocument = false; + } + + if (mbListeningToFrame) + { + // Listen to changes of certain properties. + Reference xFrame (mxFrameWeak); + if (xFrame.is()) + { + xFrame->removeFrameActionListener(Reference(this)); + mbListeningToFrame = false; + } + } + + DisconnectFromController (); + + if (mpBase != nullptr) + { + Link aLink (LINK(this, Listener, EventMultiplexerCallback)); + mpBase->GetEventMultiplexer()->RemoveEventListener(aLink); + } +} + +void Listener::ConnectToController() +{ + ViewShell* pShell = mrSlideSorter.GetViewShell(); + + // Register at the controller of the main view shell (if we are that not + // ourself). + if (pShell!=nullptr && pShell->IsMainViewShell()) + return; + + Reference xController (mrSlideSorter.GetXController()); + + // Listen to changes of certain properties. + Reference xSet (xController, UNO_QUERY); + if (xSet.is()) + { + try + { + xSet->addPropertyChangeListener("CurrentPage", this); + } + catch (beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + try + { + xSet->addPropertyChangeListener("IsMasterPageMode", this); + } + catch (beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } + + // Listen for disposing events. + if (xController.is()) + { + xController->addEventListener ( + Reference(static_cast(this), UNO_QUERY)); + + mxControllerWeak = xController; + mbListeningToController = true; + } +} + +void Listener::DisconnectFromController() +{ + if (!mbListeningToController) + return; + + Reference xController = mxControllerWeak; + Reference xSet (xController, UNO_QUERY); + try + { + // Remove the property listener. + if (xSet.is()) + { + xSet->removePropertyChangeListener( "CurrentPage", this ); + xSet->removePropertyChangeListener( "IsMasterPageMode", this); + } + + // Remove the dispose listener. + if (xController.is()) + xController->removeEventListener ( + Reference( + static_cast(this), UNO_QUERY)); + } + catch (beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + + mbListeningToController = false; + mxControllerWeak = Reference(); +} + +void Listener::Notify ( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast(&rHint); + switch (pSdrHint->GetKind()) + { + case SdrHintKind::ModelCleared: + if (&rBroadcaster == mrSlideSorter.GetModel().GetDocument()) + { // rhbz#965646 stop listening to dying document + EndListening(rBroadcaster); + return; + } + break; + case SdrHintKind::PageOrderChange: + if (&rBroadcaster == mrSlideSorter.GetModel().GetDocument()) + HandleModelChange(pSdrHint->GetPage()); + break; + + default: + break; + } + } + else if (rHint.GetId() == SfxHintId::DocChanged) + { + mrController.CheckForMasterPageAssignment(); + mrController.CheckForSlideTransitionAssignment(); + } + else if (auto pViewShellHint = dynamic_cast(&rHint)) + { + switch (pViewShellHint->GetHintId()) + { + case ViewShellHint::HINT_PAGE_RESIZE_START: + // Initiate a model change but do nothing (well, not much) + // until we are told that all slides have been resized. + mpModelChangeLock.reset(new SlideSorterController::ModelChangeLock(mrController), + o3tl::default_delete()); + mrController.HandleModelChange(); + break; + + case ViewShellHint::HINT_PAGE_RESIZE_END: + // All slides have been resized. The model has to be updated. + mpModelChangeLock.reset(); + break; + + case ViewShellHint::HINT_CHANGE_EDIT_MODE_START: + mrController.PrepareEditModeChange(); + break; + + case ViewShellHint::HINT_CHANGE_EDIT_MODE_END: + mrController.FinishEditModeChange(); + break; + + case ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START: + mpModelChangeLock.reset(new SlideSorterController::ModelChangeLock(mrController), + o3tl::default_delete()); + break; + + case ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END: + mpModelChangeLock.reset(); + break; + } + } +} + +IMPL_LINK(Listener, EventMultiplexerCallback, ::sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::MainViewRemoved: + { + if (mpBase != nullptr) + { + ViewShell* pMainViewShell = mpBase->GetMainViewShell().get(); + if (pMainViewShell != nullptr) + EndListening(*pMainViewShell); + } + } + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending && mpBase != nullptr) + { + mbIsMainViewChangePending = false; + ViewShell* pMainViewShell = mpBase->GetMainViewShell().get(); + if (pMainViewShell != nullptr + && pMainViewShell!=mrSlideSorter.GetViewShell()) + { + StartListening (*pMainViewShell); + } + } + break; + + case EventMultiplexerEventId::ControllerAttached: + { + ConnectToController(); + // mrController.GetPageSelector().GetCoreSelection(); + UpdateEditMode(); + } + break; + + case EventMultiplexerEventId::ControllerDetached: + DisconnectFromController(); + break; + + case EventMultiplexerEventId::ShapeChanged: + case EventMultiplexerEventId::ShapeInserted: + case EventMultiplexerEventId::ShapeRemoved: + HandleShapeModification(static_cast(rEvent.mpUserData)); + break; + + case EventMultiplexerEventId::EndTextEdit: + if (rEvent.mpUserData != nullptr) + { + const SdrObject* pObject = static_cast(rEvent.mpUserData); + HandleShapeModification(pObject->getSdrPageFromSdrObject()); + } + break; + + default: + break; + } +} + +//===== lang::XEventListener ================================================ + +void SAL_CALL Listener::disposing ( + const lang::EventObject& rEventObject) +{ + if ((mbListeningToDocument || mbListeningToUNODocument) + && mrSlideSorter.GetModel().GetDocument()!=nullptr + && rEventObject.Source + == mrSlideSorter.GetModel().GetDocument()->getUnoModel()) + { + mbListeningToDocument = false; + mbListeningToUNODocument = false; + } + else if (mbListeningToController) + { + Reference xController (mxControllerWeak); + if (rEventObject.Source == xController) + { + mbListeningToController = false; + } + } +} + +//===== document::XEventListener ============================================ + +void SAL_CALL Listener::notifyEvent ( + const document::EventObject& ) +{ +} + +//===== beans::XPropertySetListener ========================================= + +void SAL_CALL Listener::propertyChange ( + const PropertyChangeEvent& rEvent) +{ + if (m_bDisposed) + { + throw lang::DisposedException ("SlideSorterController object has already been disposed", + static_cast(this)); + } + + if (rEvent.PropertyName == "CurrentPage") + { + Any aCurrentPage = rEvent.NewValue; + Reference xPageSet (aCurrentPage, UNO_QUERY); + if (xPageSet.is()) + { + try + { + Any aPageNumber = xPageSet->getPropertyValue ("Number"); + sal_Int32 nCurrentPage = 0; + aPageNumber >>= nCurrentPage; + // The selection is already set but we call SelectPage() + // nevertheless in order to make the new current page the + // last recently selected page of the PageSelector. This is + // used when making the selection visible. + mrController.GetCurrentSlideManager()->NotifyCurrentSlideChange(nCurrentPage-1); + mrController.GetPageSelector().SelectPage(nCurrentPage-1); + } + catch (beans::UnknownPropertyException&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + catch (lang::DisposedException&) + { + // Something is already disposed. There is not much we can + // do, except not to crash. + } + } + } + else if (rEvent.PropertyName == "IsMasterPageMode") + { + bool bIsMasterPageMode = false; + rEvent.NewValue >>= bIsMasterPageMode; + mrController.ChangeEditMode ( + bIsMasterPageMode ? EditMode::MasterPage : EditMode::Page); + } +} + +//===== frame::XFrameActionListener ========================================== + +void SAL_CALL Listener::frameAction (const frame::FrameActionEvent& rEvent) +{ + switch (rEvent.Action) + { + case frame::FrameAction_COMPONENT_DETACHING: + DisconnectFromController(); + break; + + case frame::FrameAction_COMPONENT_REATTACHED: + { + ConnectToController(); + mrController.GetPageSelector().GetCoreSelection(); + UpdateEditMode(); + } + break; + + default: + break; + } +} + +//===== accessibility::XAccessibleEventListener ============================== + +void SAL_CALL Listener::notifyEvent ( + const AccessibleEventObject& ) +{ +} + +void Listener::disposing(std::unique_lock&) +{ + ReleaseListeners(); +} + +void Listener::UpdateEditMode() +{ + // When there is a new controller then the edit mode may have changed at + // the same time. + Reference xController (mxControllerWeak); + Reference xSet (xController, UNO_QUERY); + bool bIsMasterPageMode = false; + if (xSet != nullptr) + { + try + { + Any aValue (xSet->getPropertyValue( "IsMasterPageMode" )); + aValue >>= bIsMasterPageMode; + } + catch (beans::UnknownPropertyException&) + { + // When the property is not supported then the master page mode + // is not supported, too. + bIsMasterPageMode = false; + } + } + mrController.ChangeEditMode ( + bIsMasterPageMode ? EditMode::MasterPage : EditMode::Page); +} + +void Listener::HandleModelChange (const SdrPage* pPage) +{ + // Notify model and selection observer about the page. The return value + // of the model call acts as filter as to which events to pass to the + // selection observer. + if (mrSlideSorter.GetModel().NotifyPageEvent(pPage)) + { + // The page of the hint belongs (or belonged) to the model. + + // Tell the cache manager that the preview bitmaps for a deleted + // page can be removed from all caches. + if (pPage!=nullptr && ! pPage->IsInserted()) + cache::PageCacheManager::Instance()->ReleasePreviewBitmap(pPage); + + mrController.GetSelectionManager()->GetSelectionObserver()->NotifyPageEvent(pPage); + } + + // Tell the controller about the model change only when the document is + // in a sane state, not just in the middle of a larger change. + SdDrawDocument* pDocument (mrSlideSorter.GetModel().GetDocument()); + if (pDocument != nullptr + && pDocument->GetMasterSdPageCount(PageKind::Standard) == pDocument->GetMasterSdPageCount(PageKind::Notes)) + { + // A model change can make updates of some text fields necessary + // (like page numbers and page count.) Invalidate all previews in + // the cache to cope with this. Doing this on demand would be a + // nice optimization. + cache::PageCacheManager::Instance()->InvalidateAllPreviewBitmaps(pDocument->getUnoModel()); + + mrController.HandleModelChange(); + } +} + +void Listener::HandleShapeModification (const SdrPage* pPage) +{ + if (pPage == nullptr) + return; + + // Invalidate the preview of the page (in all slide sorters that display + // it.) + std::shared_ptr pCacheManager (cache::PageCacheManager::Instance()); + if ( ! pCacheManager) + return; + SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument(); + if (pDocument == nullptr) + { + OSL_ASSERT(pDocument!=nullptr); + return; + } + pCacheManager->InvalidatePreviewBitmap(pDocument->getUnoModel(), pPage); + mrSlideSorter.GetView().GetPreviewCache()->RequestPreviewBitmap(pPage); + + // When the page is a master page then invalidate the previews of all + // pages that are linked to this master page. + if (!pPage->IsMasterPage()) + return; + + for (sal_uInt16 nIndex=0,nCount=pDocument->GetSdPageCount(PageKind::Standard); + nIndexGetSdPage(nIndex, PageKind::Standard); + if (pCandidate!=nullptr && pCandidate->TRG_HasMasterPage()) + { + if (&pCandidate->TRG_GetMasterPage() == pPage) + pCacheManager->InvalidatePreviewBitmap(pDocument->getUnoModel(), pCandidate); + } + else + { + OSL_ASSERT(pCandidate!=nullptr && pCandidate->TRG_HasMasterPage()); + } + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsListener.hxx b/sd/source/ui/slidesorter/controller/SlsListener.hxx new file mode 100644 index 000000000..eff02cf19 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsListener.hxx @@ -0,0 +1,164 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +class SdrPage; + +namespace sd { +class ViewShellBase; +} + +namespace sd::tools { class EventMultiplexerEvent; } +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::controller { + +typedef comphelper::WeakComponentImplHelper< + css::document::XEventListener, + css::beans::XPropertyChangeListener, + css::accessibility::XAccessibleEventListener, + css::frame::XFrameActionListener + > ListenerInterfaceBase; + +/** Listen for events of various types and sources and react to them. This + class is a part of the controller. + + When the view shell in the center pane is replaced by another the + associated controller is replaced as well. Therefore we have to + register at the frame and on certain FrameActionEvents to stop listening + to the old controller and register as listener at the new one. +*/ +class Listener + : public ListenerInterfaceBase, + public SfxListener +{ +public: + explicit Listener (SlideSorter& rSlideSorter); + virtual ~Listener() override; + + /** Connect to the current controller of the view shell as listener. + This method is called once during initialization and every time a + FrameActionEvent signals the current controller being exchanged. + When the connection is successful then the flag + mbListeningToController is set to . + */ + void ConnectToController(); + + /** Disconnect from the current controller of the view shell as + listener. This method is called once during initialization and + every time a FrameActionEvent signals the current controller being + exchanged. When this method terminates then mbListeningToController + is . + */ + void DisconnectFromController(); + + virtual void Notify ( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) override; + + //===== lang::XEventListener ============================================ + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + //===== document::XEventListener ======================================== + virtual void SAL_CALL + notifyEvent ( + const css::document::EventObject& rEventObject) override; + + //===== beans::XPropertySetListener ===================================== + virtual void SAL_CALL + propertyChange ( + const css::beans::PropertyChangeEvent& rEvent) override; + + //===== accessibility::XAccessibleEventListener ========================== + virtual void SAL_CALL + notifyEvent ( + const css::accessibility::AccessibleEventObject& + rEvent) override; + + //===== frame::XFrameActionListener ====================================== + /** For certain actions the listener connects to a new controller of the + frame it is listening to. This usually happens when the view shell + in the center pane is replaced by another view shell. + */ + virtual void SAL_CALL + frameAction (const css::frame::FrameActionEvent& rEvent) override; + + virtual void disposing(std::unique_lock&) override; + +private: + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + ViewShellBase* mpBase; + + /// Remember whether we are listening to the document. + bool mbListeningToDocument; + /// Remember whether we are listening to the UNO document. + bool mbListeningToUNODocument; + /// Remember whether we are listening to the UNO controller. + bool mbListeningToController; + /// Remember whether we are listening to the frame. + bool mbListeningToFrame; + bool mbIsMainViewChangePending; + + css::uno::WeakReference< css::frame::XController> mxControllerWeak; + css::uno::WeakReference< css::frame::XFrame> mxFrameWeak; + + /** This object is used to lock the model between some + events. It is references counted in order to cope with events that + are expected but never sent. + */ + std::shared_ptr mpModelChangeLock; + + void ReleaseListeners(); + + /** Called when the edit mode has changed. Update model accordingly. + */ + void UpdateEditMode(); + + /** Handle a change in the order of slides or when the set of slides has + changed, i.e. a slide has been created. + */ + void HandleModelChange (const SdrPage* pPage); + + /** Handle a modification to a shape on the given page. When this is a + regular page then update its preview. When it is a master page then + additionally update the previews of all pages linked to it. + */ + void HandleShapeModification (const SdrPage* pPage); + + DECL_LINK(EventMultiplexerCallback, tools::EventMultiplexerEvent&, void); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsPageSelector.cxx b/sd/source/ui/slidesorter/controller/SlsPageSelector.cxx new file mode 100644 index 000000000..21affcf2f --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsPageSelector.cxx @@ -0,0 +1,386 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::sd::slidesorter::model; +using namespace ::sd::slidesorter::view; + +namespace sd::slidesorter::controller { + +PageSelector::PageSelector (SlideSorter& rSlideSorter) + : mrModel(rSlideSorter.GetModel()), + mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mnSelectedPageCount(0), + mnBroadcastDisableLevel(0), + mbSelectionChangeBroadcastPending(false), + mnUpdateLockCount(0), + mbIsUpdateCurrentPagePending(true) +{ + CountSelectedPages (); +} + +void PageSelector::SelectAllPages() +{ + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + PageSelector::UpdateLock aLock (*this); + + int nPageCount = mrModel.GetPageCount(); + for (int nPageIndex=0; nPageIndexGetCoreSelection()) + { + mrSlideSorter.GetController().GetVisibleAreaManager().RequestVisible(pDescriptor); + mrSlideSorter.GetView().RequestRepaint(pDescriptor); + bSelectionHasChanged = true; + } + + if (pDescriptor->HasState(PageDescriptor::ST_Selected)) + mnSelectedPageCount++; + } + + if (bSelectionHasChanged) + { + if (mnBroadcastDisableLevel > 0) + mbSelectionChangeBroadcastPending = true; + else + mrController.GetSelectionManager()->SelectionHasChanged(); + } +} + +void PageSelector::SetCoreSelection() +{ + model::PageEnumeration aAllPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aAllPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + pDescriptor->SetCoreSelection(); + } +} + +void PageSelector::SelectPage (int nPageIndex) +{ + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + SelectPage(pDescriptor); +} + +void PageSelector::SelectPage (const SdPage* pPage) +{ + const sal_Int32 nPageIndex (mrModel.GetIndex(pPage)); + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor && pDescriptor->GetPage()==pPage) + SelectPage(pDescriptor); +} + +void PageSelector::SelectPage (const SharedPageDescriptor& rpDescriptor) +{ + if (!rpDescriptor + || !mrSlideSorter.GetView().SetState(rpDescriptor, PageDescriptor::ST_Selected, true)) + return; + + ++mnSelectedPageCount; + mrSlideSorter.GetController().GetVisibleAreaManager().RequestVisible(rpDescriptor,true); + mrSlideSorter.GetView().RequestRepaint(rpDescriptor); + + mpMostRecentlySelectedPage = rpDescriptor; + if (mpSelectionAnchor == nullptr) + mpSelectionAnchor = rpDescriptor; + + if (mnBroadcastDisableLevel > 0) + mbSelectionChangeBroadcastPending = true; + else + mrController.GetSelectionManager()->SelectionHasChanged(); + UpdateCurrentPage(); + + CheckConsistency(); +} + +void PageSelector::DeselectPage (int nPageIndex) +{ + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + DeselectPage(pDescriptor); +} + +void PageSelector::DeselectPage ( + const SharedPageDescriptor& rpDescriptor, + const bool bUpdateCurrentPage) +{ + if (!rpDescriptor + || !mrSlideSorter.GetView().SetState(rpDescriptor, PageDescriptor::ST_Selected, false)) + return; + + --mnSelectedPageCount; + mrSlideSorter.GetController().GetVisibleAreaManager().RequestVisible(rpDescriptor); + mrSlideSorter.GetView().RequestRepaint(rpDescriptor); + if (mpMostRecentlySelectedPage == rpDescriptor) + mpMostRecentlySelectedPage.reset(); + if (mnBroadcastDisableLevel > 0) + mbSelectionChangeBroadcastPending = true; + else + mrController.GetSelectionManager()->SelectionHasChanged(); + if (bUpdateCurrentPage) + UpdateCurrentPage(); + + CheckConsistency(); +} + +void PageSelector::CheckConsistency() const +{ + int nSelectionCount (0); + for (int nPageIndex=0,nPageCount=mrModel.GetPageCount(); nPageIndexHasState(PageDescriptor::ST_Selected)) + ++nSelectionCount; + } + if (nSelectionCount!=mnSelectedPageCount) + { + // #i120020# The former call to assert(..) internally calls + // SlideSorterModel::GetPageDescriptor which will crash in this situation + // (only in non-pro code). All what is wanted there is to assert it (the + // error is already detected), so do this directly. + OSL_ENSURE(false, "PageSelector: Consistency error (!)"); + } +} + +bool PageSelector::IsPageSelected(int nPageIndex) +{ + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + return pDescriptor->HasState(PageDescriptor::ST_Selected); + else + return false; +} + +bool PageSelector::IsPageVisible(int nPageIndex) +{ + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nPageIndex)); + if (pDescriptor) + return pDescriptor->HasState(PageDescriptor::ST_Visible); + else + return false; +} + +int PageSelector::GetPageCount() const +{ + return mrModel.GetPageCount(); +} + +void PageSelector::CountSelectedPages() +{ + mnSelectedPageCount = 0; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration(mrModel)); + while (aSelectedPages.HasMoreElements()) + { + mnSelectedPageCount++; + aSelectedPages.GetNextElement(); + } +} + +void PageSelector::EnableBroadcasting() +{ + if (mnBroadcastDisableLevel > 0) + mnBroadcastDisableLevel --; + if (mnBroadcastDisableLevel==0 && mbSelectionChangeBroadcastPending) + { + mrController.GetSelectionManager()->SelectionHasChanged(); + mbSelectionChangeBroadcastPending = false; + } +} + +void PageSelector::DisableBroadcasting() +{ + mnBroadcastDisableLevel ++; +} + +std::shared_ptr PageSelector::GetPageSelection() const +{ + auto pSelection = std::make_shared(); + pSelection->reserve(GetSelectedPageCount()); + + int nPageCount = GetPageCount(); + for (int nIndex=0; nIndexHasState(PageDescriptor::ST_Selected)) + pSelection->push_back(pDescriptor->GetPage()); + } + + return pSelection; +} + +void PageSelector::SetPageSelection ( + const std::shared_ptr& rpSelection, + const bool bUpdateCurrentPage) +{ + for (const auto& rpPage : *rpSelection) + SelectPage(rpPage); + if (bUpdateCurrentPage) + UpdateCurrentPage(); +} + +void PageSelector::UpdateCurrentPage (const bool bUpdateOnlyWhenPending) +{ + if (mnUpdateLockCount > 0) + { + mbIsUpdateCurrentPagePending = true; + return; + } + + if ( ! mbIsUpdateCurrentPagePending && bUpdateOnlyWhenPending) + return; + + mbIsUpdateCurrentPagePending = false; + + // Make the first selected page the current page. + SharedPageDescriptor pCurrentPageDescriptor; + const sal_Int32 nPageCount (GetPageCount()); + for (sal_Int32 nIndex=0; nIndexHasState(PageDescriptor::ST_Selected)) + { + pCurrentPageDescriptor = pDescriptor; + break; + } + } + + if (!pCurrentPageDescriptor) + return; + + // Switching the current slide normally sets also the + // selection to just the new current slide. To prevent that, + // we store (and at the end of this scope restore) the current + // selection. + std::shared_ptr pSelection (GetPageSelection()); + + mrController.GetCurrentSlideManager()->SwitchCurrentSlide(pCurrentPageDescriptor); + + // Restore the selection and prevent a recursive call to + // UpdateCurrentPage(). + SetPageSelection(pSelection, false); +} + +//===== PageSelector::UpdateLock ============================================== + +PageSelector::UpdateLock::UpdateLock (SlideSorter const & rSlideSorter) + : mpSelector(&rSlideSorter.GetController().GetPageSelector()) +{ + ++mpSelector->mnUpdateLockCount; +} + +PageSelector::UpdateLock::UpdateLock (PageSelector& rSelector) + : mpSelector(&rSelector) +{ + ++mpSelector->mnUpdateLockCount; +} + +PageSelector::UpdateLock::~UpdateLock() +{ + Release(); +} + +void PageSelector::UpdateLock::Release() +{ + if (mpSelector != nullptr) + { + --mpSelector->mnUpdateLockCount; + OSL_ASSERT(mpSelector->mnUpdateLockCount >= 0); + if (mpSelector->mnUpdateLockCount == 0) + mpSelector->UpdateCurrentPage(true); + + mpSelector = nullptr; + } +} + +//===== PageSelector::BroadcastLock ============================================== + +PageSelector::BroadcastLock::BroadcastLock (SlideSorter const & rSlideSorter) + : mrSelector(rSlideSorter.GetController().GetPageSelector()) +{ + mrSelector.DisableBroadcasting(); +} + +PageSelector::BroadcastLock::BroadcastLock (PageSelector& rSelector) + : mrSelector(rSelector) +{ + mrSelector.DisableBroadcasting(); +} + +PageSelector::BroadcastLock::~BroadcastLock() +{ + mrSelector.EnableBroadcasting(); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsProperties.cxx b/sd/source/ui/slidesorter/controller/SlsProperties.cxx new file mode 100644 index 000000000..f1152a373 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsProperties.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +namespace sd::slidesorter::controller { + +Properties::Properties() + : mbIsHighlightCurrentSlide(false), + mbIsShowSelection(true), + mbIsShowFocus(true), + mbIsCenterSelection(false), + mbIsSmoothSelectionScrolling(true), + mbIsSuspendPreviewUpdatesDuringFullScreenPresentation(true), + maBackgroundColor(Application::GetSettings().GetStyleSettings().GetWindowColor()), + maTextColor(Application::GetSettings().GetStyleSettings().GetActiveTextColor()), + maSelectionColor(Application::GetSettings().GetStyleSettings().GetHighlightColor()), + maHighlightColor(Application::GetSettings().GetStyleSettings().GetMenuHighlightColor()), + mbIsUIReadOnly(false) +{ +} + +void Properties::HandleDataChangeEvent() +{ + maBackgroundColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + maTextColor = Application::GetSettings().GetStyleSettings().GetActiveTextColor(); + maSelectionColor = Application::GetSettings().GetStyleSettings().GetHighlightColor(); + maHighlightColor = Application::GetSettings().GetStyleSettings().GetMenuHighlightColor(); +} + +void Properties::SetHighlightCurrentSlide (const bool bIsHighlightCurrentSlide) +{ + mbIsHighlightCurrentSlide = bIsHighlightCurrentSlide; +} + +void Properties::SetShowSelection (const bool bIsShowSelection) +{ + mbIsShowSelection = bIsShowSelection; +} + +void Properties::SetShowFocus (const bool bIsShowFocus) +{ + mbIsShowFocus = bIsShowFocus; +} + +void Properties::SetCenterSelection (const bool bIsCenterSelection) +{ + mbIsCenterSelection = bIsCenterSelection; +} + +void Properties::SetSmoothSelectionScrolling (const bool bIsSmoothSelectionScrolling) +{ + mbIsSmoothSelectionScrolling = bIsSmoothSelectionScrolling; +} + +void Properties::SetSuspendPreviewUpdatesDuringFullScreenPresentation (const bool bFlag) +{ + mbIsSuspendPreviewUpdatesDuringFullScreenPresentation = bFlag; +} + +void Properties::SetBackgroundColor (const Color& rColor) +{ + maBackgroundColor = rColor; +} + +void Properties::SetTextColor (const Color& rColor) +{ + maTextColor = rColor; +} + +void Properties::SetSelectionColor (const Color& rColor) +{ + maSelectionColor = rColor; +} + +void Properties::SetHighlightColor (const Color& rColor) +{ + maHighlightColor = rColor; +} + +void Properties::SetUIReadOnly (const bool bIsUIReadOnly) +{ + mbIsUIReadOnly = bIsUIReadOnly; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsScrollBarManager.cxx b/sd/source/ui/slidesorter/controller/SlsScrollBarManager.cxx new file mode 100644 index 000000000..83192414f --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsScrollBarManager.cxx @@ -0,0 +1,608 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace sd::slidesorter::controller { + +constexpr double gnHorizontalScrollFactor(0.15); +constexpr double gnVerticalScrollFactor(0.25); + +ScrollBarManager::ScrollBarManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mpHorizontalScrollBar(mrSlideSorter.GetHorizontalScrollBar()), + mpVerticalScrollBar(mrSlideSorter.GetVerticalScrollBar()), + mnHorizontalPosition (0), + mnVerticalPosition (0), + maScrollBorder (20,20), + mpScrollBarFiller(mrSlideSorter.GetScrollBarFiller()), + maAutoScrollTimer("sd ScrollBarManager maAutoScrollTimer"), + maAutoScrollOffset(0,0), + mbIsAutoScrollActive(false), + mpContentWindow(mrSlideSorter.GetContentWindow()) +{ + // Hide the scroll bars by default to prevent display errors while + // switching between view shells: In the short time between initiating + // such a switch and the final rearrangement of UI controls the scroll + // bars and the filler where displayed in the upper left corner of the + // ViewTabBar. + mpHorizontalScrollBar->Hide(); + mpVerticalScrollBar->Hide(); + mpScrollBarFiller->Hide(); + + maAutoScrollTimer.SetTimeout(25); + maAutoScrollTimer.SetInvokeHandler ( + LINK(this, ScrollBarManager, AutoScrollTimeoutHandler)); +} + +ScrollBarManager::~ScrollBarManager() +{ +} + +void ScrollBarManager::Connect() +{ + if (mpVerticalScrollBar != nullptr) + { + mpVerticalScrollBar->SetScrollHdl ( + LINK(this, ScrollBarManager, VerticalScrollBarHandler)); + } + if (mpHorizontalScrollBar != nullptr) + { + mpHorizontalScrollBar->SetScrollHdl( + LINK(this, ScrollBarManager, HorizontalScrollBarHandler)); + } +} + +void ScrollBarManager::Disconnect() +{ + if (mpVerticalScrollBar != nullptr) + { + mpVerticalScrollBar->SetScrollHdl( Link() ); + } + if (mpHorizontalScrollBar != nullptr) + { + mpHorizontalScrollBar->SetScrollHdl( Link() ); + } +} + +/** Placing the scroll bars is an iterative process. The visibility of one + scroll bar affects the remaining size and thus may lead to the other + scroll bar becoming visible. + + First we determine the visibility of the horizontal scroll bar. After + that we do the same for the vertical scroll bar. To have an initial + value for the required size we call the layouter before that. When one + of the two scroll bars is made visible then the size of the browser + window changes and a second call to the layouter becomes necessary. + That call is made anyway after this method returns. +*/ +::tools::Rectangle ScrollBarManager::PlaceScrollBars ( + const ::tools::Rectangle& rAvailableArea, + const bool bIsHorizontalScrollBarAllowed, + const bool bIsVerticalScrollBarAllowed) +{ + ::tools::Rectangle aRemainingSpace (DetermineScrollBarVisibilities( + rAvailableArea, + bIsHorizontalScrollBarAllowed, + bIsVerticalScrollBarAllowed)); + + if (mpHorizontalScrollBar!=nullptr && mpHorizontalScrollBar->IsVisible()) + PlaceHorizontalScrollBar (rAvailableArea); + + if (mpVerticalScrollBar!=nullptr && mpVerticalScrollBar->IsVisible()) + PlaceVerticalScrollBar (rAvailableArea); + + if (mpScrollBarFiller!=nullptr && mpScrollBarFiller->IsVisible()) + PlaceFiller (rAvailableArea); + + return aRemainingSpace; +} + +void ScrollBarManager::PlaceHorizontalScrollBar (const ::tools::Rectangle& aAvailableArea) +{ + // Save the current relative position. + mnHorizontalPosition = double(mpHorizontalScrollBar->GetThumbPos()) + / double(mpHorizontalScrollBar->GetRange().Len()); + + // Place the scroll bar. + Size aScrollBarSize (mpHorizontalScrollBar->GetSizePixel()); + mpHorizontalScrollBar->SetPosSizePixel ( + Point(aAvailableArea.Left(), + aAvailableArea.Bottom()-aScrollBarSize.Height()+1), + Size (aAvailableArea.GetWidth() - GetVerticalScrollBarWidth(), + aScrollBarSize.Height())); + + // Restore the relative position. + mpHorizontalScrollBar->SetThumbPos( + static_cast<::tools::Long>(0.5 + mnHorizontalPosition * mpHorizontalScrollBar->GetRange().Len())); +} + +void ScrollBarManager::PlaceVerticalScrollBar (const ::tools::Rectangle& aArea) +{ + const sal_Int32 nThumbPosition (mpVerticalScrollBar->GetThumbPos()); + + // Place the scroll bar. + Size aScrollBarSize (mpVerticalScrollBar->GetSizePixel()); + Point aPosition (aArea.Right()-aScrollBarSize.Width()+1, aArea.Top()); + Size aSize (aScrollBarSize.Width(), aArea.GetHeight() - GetHorizontalScrollBarHeight()); + mpVerticalScrollBar->SetPosSizePixel(aPosition, aSize); + + // Restore the position. + mpVerticalScrollBar->SetThumbPos(static_cast<::tools::Long>(nThumbPosition)); + mnVerticalPosition = nThumbPosition / double(mpVerticalScrollBar->GetRange().Len()); +} + +void ScrollBarManager::PlaceFiller (const ::tools::Rectangle& aArea) +{ + mpScrollBarFiller->SetPosSizePixel( + Point( + aArea.Right()-mpVerticalScrollBar->GetSizePixel().Width()+1, + aArea.Bottom()-mpHorizontalScrollBar->GetSizePixel().Height()+1), + Size ( + mpVerticalScrollBar->GetSizePixel().Width(), + mpHorizontalScrollBar->GetSizePixel().Height())); +} + +void ScrollBarManager::UpdateScrollBars(bool bUseScrolling) +{ + ::tools::Rectangle aModelArea (mrSlideSorter.GetView().GetModelArea()); + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + Size aWindowModelSize (pWindow->PixelToLogic(pWindow->GetSizePixel())); + + // The horizontal scroll bar is only shown when the window is + // horizontally smaller than the view. + if (mpHorizontalScrollBar != nullptr && mpHorizontalScrollBar->IsVisible()) + { + mpHorizontalScrollBar->Show(); + mpHorizontalScrollBar->SetRange ( + Range(aModelArea.Left(), aModelArea.Right())); + mnHorizontalPosition = + double(mpHorizontalScrollBar->GetThumbPos()) + / double(mpHorizontalScrollBar->GetRange().Len()); + + mpHorizontalScrollBar->SetVisibleSize (aWindowModelSize.Width()); + + const ::tools::Long nWidth (mpContentWindow->PixelToLogic( + mpContentWindow->GetSizePixel()).Width()); + // Make the line size about 10% of the visible width. + mpHorizontalScrollBar->SetLineSize (nWidth / 10); + // Make the page size about 90% of the visible width. + mpHorizontalScrollBar->SetPageSize ((nWidth * 9) / 10); + } + else + { + mnHorizontalPosition = 0; + } + + // The vertical scroll bar is always shown. + if (mpVerticalScrollBar != nullptr && mpVerticalScrollBar->IsVisible()) + { + mpVerticalScrollBar->SetRange ( + Range(aModelArea.Top(), aModelArea.Bottom())); + mnVerticalPosition = + double(mpVerticalScrollBar->GetThumbPos()) + / double(mpVerticalScrollBar->GetRange().Len()); + + mpVerticalScrollBar->SetVisibleSize (aWindowModelSize.Height()); + + const ::tools::Long nHeight (mpContentWindow->PixelToLogic( + mpContentWindow->GetSizePixel()).Height()); + // Make the line size about 10% of the visible height. + mpVerticalScrollBar->SetLineSize (nHeight / 10); + // Make the page size about 90% of the visible height. + mpVerticalScrollBar->SetPageSize ((nHeight * 9) / 10); + } + else + { + mnVerticalPosition = 0; + } + + double nEps (::std::numeric_limits::epsilon()); + if (fabs(mnHorizontalPosition-pWindow->GetVisibleX()) > nEps + || fabs(mnVerticalPosition-pWindow->GetVisibleY()) > nEps) + { + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + if (bUseScrolling) + pWindow->SetVisibleXY(mnHorizontalPosition, mnVerticalPosition); + else + SetWindowOrigin(mnHorizontalPosition, mnVerticalPosition); + } +} + +IMPL_LINK(ScrollBarManager, VerticalScrollBarHandler, ScrollBar*, pScrollBar, void) +{ + if (pScrollBar!=nullptr + && pScrollBar==mpVerticalScrollBar.get() + && pScrollBar->IsVisible() + && mrSlideSorter.GetContentWindow()) + { + double nRelativePosition = double(pScrollBar->GetThumbPos()) + / double(pScrollBar->GetRange().Len()); + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + mrSlideSorter.GetContentWindow()->SetVisibleXY(-1, nRelativePosition); + mrSlideSorter.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking(); + } +} + +IMPL_LINK(ScrollBarManager, HorizontalScrollBarHandler, ScrollBar*, pScrollBar, void) +{ + if (pScrollBar!=nullptr + && pScrollBar==mpHorizontalScrollBar.get() + && pScrollBar->IsVisible() + && mrSlideSorter.GetContentWindow()) + { + double nRelativePosition = double(pScrollBar->GetThumbPos()) + / double(pScrollBar->GetRange().Len()); + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + mrSlideSorter.GetContentWindow()->SetVisibleXY(nRelativePosition, -1); + mrSlideSorter.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking(); + } +} + +void ScrollBarManager::SetWindowOrigin ( + double nHorizontalPosition, + double nVerticalPosition) +{ + mnHorizontalPosition = nHorizontalPosition; + mnVerticalPosition = nVerticalPosition; + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + Size aViewSize (pWindow->GetViewSize()); + Point aOrigin ( + static_cast<::tools::Long>(mnHorizontalPosition * aViewSize.Width()), + static_cast<::tools::Long>(mnVerticalPosition * aViewSize.Height())); + + pWindow->SetWinViewPos (aOrigin); + pWindow->UpdateMapMode (); + pWindow->Invalidate (); +} + +/** Determining the visibility of the scroll bars is quite complicated. The + visibility of one influences that of the other because showing a scroll + bar makes the available space smaller and may lead to the need of + displaying the other. + To solve this we test all four combinations of showing or hiding each + scroll bar and use the best one. The best one is that combination that + a) shows the least number of scroll bars with preference of showing the + vertical over showing the horizontal and + b) when not showing a scroll bar the area used by the page objects fits + into the available area in the scroll bars orientation. +*/ +::tools::Rectangle ScrollBarManager::DetermineScrollBarVisibilities ( + const ::tools::Rectangle& rAvailableArea, + const bool bIsHorizontalScrollBarAllowed, + const bool bIsVerticalScrollBarAllowed) +{ + // Test which combination of scroll bars is the best. + bool bShowHorizontal = false; + bool bShowVertical = false; + if (mrSlideSorter.GetModel().GetPageCount() == 0) + { + // No pages => no scroll bars. + } + else if (TestScrollBarVisibilities(false, false, rAvailableArea)) + { + // Nothing to be done. + } + else if (bIsHorizontalScrollBarAllowed + && TestScrollBarVisibilities(true, false, rAvailableArea)) + { + bShowHorizontal = true; + } + else if (bIsVerticalScrollBarAllowed + && TestScrollBarVisibilities(false, true, rAvailableArea)) + { + bShowVertical = true; + } + else + { + bShowHorizontal = true; + bShowVertical = true; + } + + // Make the visibility of the scroll bars permanent. + mpVerticalScrollBar->Show(bShowVertical); + mpHorizontalScrollBar->Show(bShowHorizontal); + mpScrollBarFiller->Show(bShowVertical && bShowHorizontal); + + // Adapt the remaining space accordingly. + ::tools::Rectangle aRemainingSpace (rAvailableArea); + if (bShowVertical) + aRemainingSpace.AdjustRight( -(mpVerticalScrollBar->GetSizePixel().Width()) ); + if (bShowHorizontal) + aRemainingSpace.AdjustBottom( -(mpHorizontalScrollBar->GetSizePixel().Height()) ); + + return aRemainingSpace; +} + +bool ScrollBarManager::TestScrollBarVisibilities ( + bool bHorizontalScrollBarVisible, + bool bVerticalScrollBarVisible, + const ::tools::Rectangle& rAvailableArea) +{ + model::SlideSorterModel& rModel (mrSlideSorter.GetModel()); + + // Adapt the available size by subtracting the sizes of the scroll bars + // visible in this combination. + Size aBrowserSize (rAvailableArea.GetSize()); + if (bHorizontalScrollBarVisible) + aBrowserSize.AdjustHeight( -(mpHorizontalScrollBar->GetSizePixel().Height()) ); + if (bVerticalScrollBarVisible) + aBrowserSize.AdjustWidth( -(mpVerticalScrollBar->GetSizePixel().Width()) ); + + // Tell the view to rearrange its page objects and check whether the + // page objects can be shown without clipping. + bool bRearrangeSuccess (mrSlideSorter.GetView().GetLayouter().Rearrange ( + mrSlideSorter.GetView().GetOrientation(), + aBrowserSize, + rModel.GetPageDescriptor(0)->GetPage()->GetSize(), + rModel.GetPageCount())); + + if (bRearrangeSuccess) + { + Size aPageSize = mrSlideSorter.GetView().GetLayouter().GetTotalBoundingBox().GetSize(); + Size aWindowModelSize = mpContentWindow->PixelToLogic(aBrowserSize); + + // The content may be clipped, i.e. not fully visible, in one + // direction only when the scroll bar is visible in that direction. + if (aPageSize.Width() > aWindowModelSize.Width()) + if ( ! bHorizontalScrollBarVisible) + return false; + if (aPageSize.Height() > aWindowModelSize.Height()) + if ( ! bVerticalScrollBarVisible) + return false; + + return true; + } + else + return false; +} + +void ScrollBarManager::SetTopLeft(const Point& rNewTopLeft) +{ + if (( ! mpVerticalScrollBar + || mpVerticalScrollBar->GetThumbPos() == rNewTopLeft.Y()) + && ( ! mpHorizontalScrollBar + || mpHorizontalScrollBar->GetThumbPos() == rNewTopLeft.X())) + return; + + // Flush pending repaints before scrolling to avoid temporary artifacts. + mrSlideSorter.GetContentWindow()->PaintImmediately(); + + if (mpVerticalScrollBar) + { + mpVerticalScrollBar->SetThumbPos(rNewTopLeft.Y()); + mnVerticalPosition = rNewTopLeft.Y() / double(mpVerticalScrollBar->GetRange().Len()); + } + if (mpHorizontalScrollBar) + { + mpHorizontalScrollBar->SetThumbPos(rNewTopLeft.X()); + mnHorizontalPosition = rNewTopLeft.X() / double(mpHorizontalScrollBar->GetRange().Len()); + } + + mrSlideSorter.GetContentWindow()->SetVisibleXY(mnHorizontalPosition, mnVerticalPosition); + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); +} + +int ScrollBarManager::GetVerticalScrollBarWidth() const +{ + if (mpVerticalScrollBar != nullptr && mpVerticalScrollBar->IsVisible()) + return mpVerticalScrollBar->GetSizePixel().Width(); + else + return 0; +} + +int ScrollBarManager::GetHorizontalScrollBarHeight() const +{ + if (mpHorizontalScrollBar != nullptr && mpHorizontalScrollBar->IsVisible()) + return mpHorizontalScrollBar->GetSizePixel().Height(); + else + return 0; +} + +void ScrollBarManager::CalcAutoScrollOffset (const Point& rMouseWindowPosition) +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + + int nDx = 0; + int nDy = 0; + + Size aWindowSize = pWindow->GetOutputSizePixel(); + ::tools::Rectangle aWindowArea (pWindow->GetPosPixel(), aWindowSize); + ::tools::Rectangle aViewPixelArea ( + pWindow->LogicToPixel(mrSlideSorter.GetView().GetModelArea())); + + if (aWindowSize.Width() > maScrollBorder.Width() * 3 + && mpHorizontalScrollBar != nullptr + && mpHorizontalScrollBar->IsVisible()) + { + if (rMouseWindowPosition.X() < maScrollBorder.Width() + && aWindowArea.Left() > aViewPixelArea.Left()) + { + nDx = -1 + static_cast(gnHorizontalScrollFactor + * (rMouseWindowPosition.X() - maScrollBorder.Width())); + } + + if (rMouseWindowPosition.X() >= (aWindowSize.Width() - maScrollBorder.Width()) + && aWindowArea.Right() < aViewPixelArea.Right()) + { + nDx = 1 + static_cast(gnHorizontalScrollFactor + * (rMouseWindowPosition.X() - aWindowSize.Width() + + maScrollBorder.Width())); + } + } + + if (aWindowSize.Height() > maScrollBorder.Height() * 3 + && aWindowSize.Height() < aViewPixelArea.GetHeight()) + { + if (rMouseWindowPosition.Y() < maScrollBorder.Height() + && aWindowArea.Top() > aViewPixelArea.Top()) + { + nDy = -1 + static_cast(gnVerticalScrollFactor + * (rMouseWindowPosition.Y() - maScrollBorder.Height())); + } + + if (rMouseWindowPosition.Y() >= (aWindowSize.Height() - maScrollBorder.Height()) + && aWindowArea.Bottom() < aViewPixelArea.Bottom()) + { + nDy = 1 + static_cast(gnVerticalScrollFactor + * (rMouseWindowPosition.Y() - aWindowSize.Height() + + maScrollBorder.Height())); + } + } + + maAutoScrollOffset = Size(nDx,nDy); +} + +bool ScrollBarManager::AutoScroll ( + const Point& rMouseWindowPosition, + const ::std::function& rAutoScrollFunctor) +{ + maAutoScrollFunctor = rAutoScrollFunctor; + CalcAutoScrollOffset(rMouseWindowPosition); + bool bResult (true); + if ( ! mbIsAutoScrollActive) + bResult = RepeatAutoScroll(); + + return bResult; +} + +void ScrollBarManager::StopAutoScroll() +{ + maAutoScrollTimer.Stop(); + mbIsAutoScrollActive = false; +} + +bool ScrollBarManager::RepeatAutoScroll() +{ + if (maAutoScrollOffset != Size(0,0)) + { + if (mrSlideSorter.GetViewShell() != nullptr) + { + mrSlideSorter.GetViewShell()->Scroll( + maAutoScrollOffset.Width(), + maAutoScrollOffset.Height()); + mrSlideSorter.GetView().InvalidatePageObjectVisibilities(); + + if (maAutoScrollFunctor) + maAutoScrollFunctor(); + + mbIsAutoScrollActive = true; + maAutoScrollTimer.Start(); + + return true; + } + } + + clearAutoScrollFunctor(); + mbIsAutoScrollActive = false; + return false; +} + +void ScrollBarManager::clearAutoScrollFunctor() +{ + maAutoScrollFunctor = ::std::function(); +} + +IMPL_LINK_NOARG(ScrollBarManager, AutoScrollTimeoutHandler, Timer *, void) +{ + RepeatAutoScroll(); +} + +void ScrollBarManager::Scroll( + const Orientation eOrientation, + const sal_Int32 nDistance) +{ + bool bIsVertical (false); + switch (eOrientation) + { + case Orientation_Horizontal: bIsVertical = false; break; + case Orientation_Vertical: bIsVertical = true; break; + default: + OSL_ASSERT(eOrientation==Orientation_Horizontal || eOrientation==Orientation_Vertical); + return; + } + + Point aNewTopLeft ( + mpHorizontalScrollBar ? mpHorizontalScrollBar->GetThumbPos() : 0, + mpVerticalScrollBar ? mpVerticalScrollBar->GetThumbPos() : 0); + + view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter()); + + // Calculate estimate of new location. + if (bIsVertical) + aNewTopLeft.AdjustY(nDistance * rLayouter.GetPageObjectSize().Height() ); + else + aNewTopLeft.AdjustX(nDistance * rLayouter.GetPageObjectSize().Width() ); + + // Adapt location to show whole slides. + if (bIsVertical) + if (nDistance > 0) + { + const sal_Int32 nIndex (rLayouter.GetIndexAtPoint( + Point(aNewTopLeft.X(), aNewTopLeft.Y()+mpVerticalScrollBar->GetVisibleSize()), + true)); + aNewTopLeft.setY( rLayouter.GetPageObjectBox(nIndex,true).Bottom() + - mpVerticalScrollBar->GetVisibleSize() ); + } + else + { + const sal_Int32 nIndex (rLayouter.GetIndexAtPoint( + Point(aNewTopLeft.X(), aNewTopLeft.Y()), + true)); + aNewTopLeft.setY( rLayouter.GetPageObjectBox(nIndex,true).Top() ); + } + else + if (nDistance > 0) + { + const sal_Int32 nIndex (rLayouter.GetIndexAtPoint( + Point(aNewTopLeft.X()+mpVerticalScrollBar->GetVisibleSize(), aNewTopLeft.Y()), + true)); + aNewTopLeft.setX( rLayouter.GetPageObjectBox(nIndex,true).Right() + - mpVerticalScrollBar->GetVisibleSize() ); + } + else + { + const sal_Int32 nIndex (rLayouter.GetIndexAtPoint( + Point(aNewTopLeft.X(), aNewTopLeft.Y()), + true)); + aNewTopLeft.setX( rLayouter.GetPageObjectBox(nIndex,true).Left() ); + } + + mrSlideSorter.GetController().GetVisibleAreaManager().DeactivateCurrentSlideTracking(); + SetTopLeft(aNewTopLeft); +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx new file mode 100644 index 000000000..c710a4c1b --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionFunction.cxx @@ -0,0 +1,1485 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include +#include "SlsDragAndDropContext.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { +const sal_uInt32 SINGLE_CLICK (0x00000001); +const sal_uInt32 DOUBLE_CLICK (0x00000002); +const sal_uInt32 LEFT_BUTTON (0x00000010); +const sal_uInt32 RIGHT_BUTTON (0x00000020); +const sal_uInt32 MIDDLE_BUTTON (0x00000040); +const sal_uInt32 BUTTON_DOWN (0x00000100); +const sal_uInt32 BUTTON_UP (0x00000200); +const sal_uInt32 MOUSE_MOTION (0x00000400); +const sal_uInt32 MOUSE_DRAG (0x00000800); +// The rest leaves the lower 16 bit untouched so that it can be used with +// key codes. +const sal_uInt32 OVER_SELECTED_PAGE (0x00010000); +const sal_uInt32 OVER_UNSELECTED_PAGE (0x00020000); +const sal_uInt32 SHIFT_MODIFIER (0x00200000); +const sal_uInt32 CONTROL_MODIFIER (0x00400000); + +// Some absent events are defined so they can be expressed explicitly. +const sal_uInt32 NO_MODIFIER (0x00000000); +const sal_uInt32 NOT_OVER_PAGE (0x00000000); + +// Masks +const sal_uInt32 MODIFIER_MASK (SHIFT_MODIFIER | CONTROL_MODIFIER); + +} // end of anonymous namespace + +// Define some macros to make the following switch statement more readable. +#define ANY_MODIFIER(code) \ + code|NO_MODIFIER: \ + case code|SHIFT_MODIFIER: \ + case code|CONTROL_MODIFIER + +namespace sd::slidesorter::controller { + +//===== SelectionFunction::EventDescriptor ==================================== + +class SelectionFunction::EventDescriptor +{ +public: + Point maMousePosition; + Point maMouseModelPosition; + model::SharedPageDescriptor mpHitDescriptor; + SdrPage* mpHitPage; + sal_uInt32 mnEventCode; + InsertionIndicatorHandler::Mode meDragMode; + bool mbIsLeaving; + + EventDescriptor ( + sal_uInt32 nEventType, + const MouseEvent& rEvent, + SlideSorter const & rSlideSorter); + EventDescriptor ( + sal_uInt32 nEventType, + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction, + SlideSorter const & rSlideSorter); + +private: + /** Compute a numerical code that describes a mouse event and that can + be used for fast look up of the appropriate reaction. + */ + sal_uInt32 EncodeMouseEvent (const MouseEvent& rEvent) const; + + /** Compute a numerical code that describes the current state like + whether the selection rectangle is visible or whether the page under + the mouse or the one that has the focus is selected. + */ + sal_uInt32 EncodeState() const; +}; + +//===== SelectionFunction::ModeHandler ======================================== + +class SelectionFunction::ModeHandler +{ +public: + ModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const bool bIsMouseOverIndicatorAllowed); + virtual ~ModeHandler() COVERITY_NOEXCEPT_FALSE; + + virtual Mode GetMode() const = 0; + virtual void Abort() = 0; + virtual void ProcessEvent (EventDescriptor& rDescriptor); + + /** Set the selection to exactly the specified page and also set it as + the current page. + */ + void SetCurrentPage (const model::SharedPageDescriptor& rpDescriptor); + + /// Deselect all pages. + void DeselectAllPages(); + void SelectOnePage (const model::SharedPageDescriptor& rpDescriptor); + + /** When the view on which this selection function is working is the + main view then the view is switched to the regular editing view. + */ + void SwitchView (const model::SharedPageDescriptor& rpDescriptor); + + void StartDrag ( + const Point& rMousePosition); + + bool IsMouseOverIndicatorAllowed() const { return mbIsMouseOverIndicatorAllowed;} + +protected: + SlideSorter& mrSlideSorter; + SelectionFunction& mrSelectionFunction; + + virtual bool ProcessButtonDownEvent (EventDescriptor& rDescriptor); + virtual bool ProcessButtonUpEvent (EventDescriptor& rDescriptor); + virtual bool ProcessMotionEvent (EventDescriptor& rDescriptor); + virtual bool ProcessDragEvent (EventDescriptor& rDescriptor); + virtual bool HandleUnprocessedEvent (EventDescriptor& rDescriptor); + + void ReprocessEvent (EventDescriptor& rDescriptor); + +private: + const bool mbIsMouseOverIndicatorAllowed; +}; + +namespace { + +/** This is the default handler for processing events. It activates the + multi selection or drag-and-drop when the right conditions are met. +*/ +class NormalModeHandler : public SelectionFunction::ModeHandler +{ +public: + NormalModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction); + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + + void ResetButtonDownLocation(); + +protected: + virtual bool ProcessButtonDownEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessMotionEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + ::std::optional maButtonDownLocation; + + /** Select all pages between and including the selection anchor and the + specified page. + */ + void RangeSelect (const model::SharedPageDescriptor& rpDescriptor); +}; + +/** Handle events during a multi selection, which typically is started by + pressing the left mouse button when not over a page. +*/ +class MultiSelectionModeHandler : public SelectionFunction::ModeHandler +{ +public: + /** Start a rectangle selection at the given position. + */ + MultiSelectionModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMouseModelPosition, + const sal_uInt32 nEventCode); + + virtual ~MultiSelectionModeHandler() override; + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + virtual void ProcessEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + + enum SelectionMode { SM_Normal, SM_Add, SM_Toggle }; + + void SetSelectionMode (const SelectionMode eSelectionMode); + void SetSelectionModeFromModifier (const sal_uInt32 nEventCode); + +protected: + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessMotionEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool HandleUnprocessedEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + SelectionMode meSelectionMode; + Point maSecondCorner; + PointerStyle maSavedPointer; + bool mbAutoScrollInstalled; + sal_Int32 mnAnchorIndex; + sal_Int32 mnSecondIndex; + + void UpdateModelPosition (const Point& rMouseModelPosition); + void UpdateSelection(); + + /** Update the rectangle selection so that the given position becomes + the new second point of the selection rectangle. + */ + void UpdatePosition ( + const Point& rMousePosition, + const bool bAllowAutoScroll); + + void UpdateSelectionState ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bIsInSelection) const; +}; + +/** Handle events during drag-and-drop. +*/ +class DragAndDropModeHandler : public SelectionFunction::ModeHandler +{ +public: + DragAndDropModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMousePosition, + vcl::Window* pWindow); + virtual ~DragAndDropModeHandler() override; + + virtual SelectionFunction::Mode GetMode() const override; + virtual void Abort() override; + +protected: + virtual bool ProcessButtonUpEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + virtual bool ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) override; + +private: + std::unique_ptr> mpDragAndDropContext; +}; + +} + +//===== SelectionFunction ===================================================== + + +SelectionFunction::SelectionFunction ( + SlideSorter& rSlideSorter, + SfxRequest& rRequest) + : FuPoor ( + rSlideSorter.GetViewShell(), + rSlideSorter.GetContentWindow(), + &rSlideSorter.GetView(), + rSlideSorter.GetModel().GetDocument(), + rRequest), + mrSlideSorter(rSlideSorter), + mrController(mrSlideSorter.GetController()), + mnShiftKeySelectionAnchor(-1), + mpModeHandler(std::make_shared(rSlideSorter, *this)) +{ +} + +SelectionFunction::~SelectionFunction() +{ + mpModeHandler.reset(); +} + +rtl::Reference SelectionFunction::Create( + SlideSorter& rSlideSorter, + SfxRequest& rRequest) +{ + rtl::Reference xFunc( new SelectionFunction( rSlideSorter, rRequest ) ); + return xFunc; +} + +bool SelectionFunction::MouseButtonDown (const MouseEvent& rEvent) +{ + // remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + aMDPos = rEvent.GetPosPixel(); + + // mpWindow->CaptureMouse(); + + ProcessMouseEvent(BUTTON_DOWN, rEvent); + + return true; +} + +bool SelectionFunction::MouseMove (const MouseEvent& rEvent) +{ + ProcessMouseEvent(MOUSE_MOTION, rEvent); + return true; +} + +bool SelectionFunction::MouseButtonUp (const MouseEvent& rEvent) +{ + mrController.GetScrollBarManager().StopAutoScroll (); + + ProcessMouseEvent(BUTTON_UP, rEvent); + + return true; +} + +void SelectionFunction::NotifyDragFinished() +{ + SwitchToNormalMode(); +} + +bool SelectionFunction::KeyInput (const KeyEvent& rEvent) +{ + view::SlideSorterView::DrawLock aDrawLock (mrSlideSorter); + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter); + PageSelector::UpdateLock aLock (mrSlideSorter); + FocusManager& rFocusManager (mrController.GetFocusManager()); + bool bResult = false; + + const vcl::KeyCode& rCode (rEvent.GetKeyCode()); + switch (rCode.GetCode()) + { + case KEY_RETURN: + { + model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor()); + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (rFocusManager.HasFocus() && pDescriptor && pViewShell!=nullptr) + { + // The Return key triggers different functions depending on + // whether the slide sorter is the main view or displayed in + // the right pane. + if (pViewShell->IsMainViewShell()) + { + mpModeHandler->SetCurrentPage(pDescriptor); + mpModeHandler->SwitchView(pDescriptor); + } + else if (pViewShell->GetDispatcher() != nullptr) + { + pViewShell->GetDispatcher()->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + bResult = true; + } + break; + } + + case KEY_TAB: + if ( ! rFocusManager.IsFocusShowing()) + { + rFocusManager.ShowFocus(); + bResult = true; + } + break; + + case KEY_ESCAPE: + // When there is an active multiselection or drag-and-drop + // operation then stop that. + mpModeHandler->Abort(); + SwitchToNormalMode(); + bResult = true; + break; + + case KEY_SPACE: + { + // Toggle the selection state. + model::SharedPageDescriptor pDescriptor (rFocusManager.GetFocusedPageDescriptor()); + if (pDescriptor && rCode.IsMod1()) + { + if (pDescriptor->HasState(model::PageDescriptor::ST_Selected)) + mrController.GetPageSelector().DeselectPage(pDescriptor, false); + else + mrController.GetPageSelector().SelectPage(pDescriptor); + } + bResult = true; + } + break; + + // Move the focus indicator left. + case KEY_LEFT: + MoveFocus(FocusManager::FocusMoveDirection::Left, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator right. + case KEY_RIGHT: + MoveFocus(FocusManager::FocusMoveDirection::Right, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator up. + case KEY_UP: + MoveFocus(FocusManager::FocusMoveDirection::Up, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Move the focus indicator down. + case KEY_DOWN: + MoveFocus(FocusManager::FocusMoveDirection::Down, rCode.IsShift(), rCode.IsMod1()); + bResult = true; + break; + + // Go to previous page. No wrap around. + case KEY_PAGEUP: + GotoNextPage(-1); + bResult = true; + break; + + // Go to next page. No wrap around... + case KEY_PAGEDOWN: + GotoNextPage(+1); + bResult = true; + break; + + case KEY_HOME: + GotoPage(0); + bResult = true; + break; + + case KEY_END: + GotoPage(mrSlideSorter.GetModel().GetPageCount()-1); + bResult = true; + break; + + case KEY_DELETE: + case KEY_BACKSPACE: + { + if (mrSlideSorter.GetProperties()->IsUIReadOnly()) + break; + + mrController.GetSelectionManager()->DeleteSelectedPages(rCode.GetCode()==KEY_DELETE); + + mnShiftKeySelectionAnchor = -1; + bResult = true; + } + break; + + case KEY_F10: + if (rCode.IsShift()) + { + mpModeHandler->SelectOnePage( + mrSlideSorter.GetController().GetFocusManager().GetFocusedPageDescriptor()); + } + break; + + default: + break; + } + + if ( ! bResult) + bResult = FuPoor::KeyInput(rEvent); + + return bResult; +} + +void SelectionFunction::MoveFocus ( + const FocusManager::FocusMoveDirection eDirection, + const bool bIsShiftDown, + const bool bIsControlDown) +{ + // Remember the anchor of shift key multi selection. + if (bIsShiftDown) + { + if (mnShiftKeySelectionAnchor<0) + { + model::SharedPageDescriptor pFocusedDescriptor ( + mrController.GetFocusManager().GetFocusedPageDescriptor()); + mnShiftKeySelectionAnchor = pFocusedDescriptor->GetPageIndex(); + } + } + else if ( ! bIsControlDown) + ResetShiftKeySelectionAnchor(); + + mrController.GetFocusManager().MoveFocus(eDirection); + + PageSelector& rSelector (mrController.GetPageSelector()); + model::SharedPageDescriptor pFocusedDescriptor ( + mrController.GetFocusManager().GetFocusedPageDescriptor()); + if (bIsShiftDown) + { + // When shift is pressed then select all pages in the range between + // the currently and the previously focused pages, including them. + if (pFocusedDescriptor) + { + sal_Int32 nPageRangeEnd (pFocusedDescriptor->GetPageIndex()); + model::PageEnumeration aPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration( + mrSlideSorter.GetModel())); + while (aPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aPages.GetNextElement()); + if (pDescriptor) + { + const sal_Int32 nPageIndex(pDescriptor->GetPageIndex()); + if ((nPageIndex>=mnShiftKeySelectionAnchor && nPageIndex<=nPageRangeEnd) + || (nPageIndex<=mnShiftKeySelectionAnchor && nPageIndex>=nPageRangeEnd)) + { + rSelector.SelectPage(pDescriptor); + } + else + { + rSelector.DeselectPage(pDescriptor); + } + } + } + } + } + else if (bIsControlDown) + { + // When control is pressed then do not alter the selection or the + // current page, just move the focus. + } + else + { + // Without shift just select the focused page. + mpModeHandler->SelectOnePage(pFocusedDescriptor); + } +} + +void SelectionFunction::DoCut() +{ + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrController.GetClipboard().DoCut(); + } +} + +void SelectionFunction::DoCopy() +{ + mrController.GetClipboard().DoCopy(); +} + +void SelectionFunction::DoPaste() +{ + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrController.GetClipboard().DoPaste(); + } +} + +bool SelectionFunction::cancel() +{ + mrController.GetFocusManager().ToggleFocus(); + return true; +} + +void SelectionFunction::GotoNextPage (int nOffset) +{ + model::SharedPageDescriptor pDescriptor + = mrController.GetCurrentSlideManager()->GetCurrentSlide(); + if (pDescriptor) + { + SdPage* pPage = pDescriptor->GetPage(); + OSL_ASSERT(pPage!=nullptr); + sal_Int32 nIndex = (pPage->GetPageNum()-1) / 2; + GotoPage(nIndex + nOffset); + } + ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::GotoPage (int nIndex) +{ + sal_uInt16 nPageCount = static_cast(mrSlideSorter.GetModel().GetPageCount()); + + if (nIndex >= nPageCount) + nIndex = nPageCount - 1; + if (nIndex < 0) + nIndex = 0; + + mrController.GetFocusManager().SetFocusedPage(nIndex); + model::SharedPageDescriptor pNextPageDescriptor ( + mrSlideSorter.GetModel().GetPageDescriptor (nIndex)); + if (pNextPageDescriptor) + mpModeHandler->SetCurrentPage(pNextPageDescriptor); + else + { + OSL_ASSERT(pNextPageDescriptor); + } + ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::ProcessMouseEvent (sal_uInt32 nEventType, const MouseEvent& rEvent) +{ + // #95491# remember button state for creation of own MouseEvents + SetMouseButtonCode (rEvent.GetButtons()); + + EventDescriptor aEventDescriptor (nEventType, rEvent, mrSlideSorter); + ProcessEvent(aEventDescriptor); +} + +void SelectionFunction::MouseDragged ( + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction) +{ + EventDescriptor aEventDescriptor (MOUSE_DRAG, rEvent, nDragAction, mrSlideSorter); + ProcessEvent(aEventDescriptor); +} + +void SelectionFunction::ProcessEvent (EventDescriptor& rDescriptor) +{ + // The call to ProcessEvent may switch to another mode handler. + // Prevent the untimely destruction of the called handler by acquiring a + // temporary reference here. + std::shared_ptr pModeHandler (mpModeHandler); + pModeHandler->ProcessEvent(rDescriptor); +} + +static bool Match ( + const sal_uInt32 nEventCode, + const sal_uInt32 nPositivePattern) +{ + return (nEventCode & nPositivePattern)==nPositivePattern; +} + +void SelectionFunction::SwitchToNormalMode() +{ + if (mpModeHandler->GetMode() != NormalMode) + SwitchMode(std::make_shared(mrSlideSorter, *this)); +} + +void SelectionFunction::SwitchToDragAndDropMode (const Point& rMousePosition) +{ + if (mpModeHandler->GetMode() == DragAndDropMode) + return; + + SwitchMode(std::make_shared(mrSlideSorter, *this, rMousePosition, mpWindow)); +} + +void SelectionFunction::SwitchToMultiSelectionMode ( + const Point& rMousePosition, + const sal_uInt32 nEventCode) +{ + if (mpModeHandler->GetMode() != MultiSelectionMode) + SwitchMode(std::make_shared(mrSlideSorter, *this, rMousePosition, nEventCode)); +} + +void SelectionFunction::SwitchMode (const std::shared_ptr& rpHandler) +{ + // Not all modes allow mouse over indicator. + if (mpModeHandler->IsMouseOverIndicatorAllowed() != rpHandler->IsMouseOverIndicatorAllowed()) + { + if ( ! rpHandler->IsMouseOverIndicatorAllowed()) + { + mrSlideSorter.GetView().SetPageUnderMouse(model::SharedPageDescriptor()); + } + else + mrSlideSorter.GetView().UpdatePageUnderMouse(); + } + + mpModeHandler = rpHandler; +} + +void SelectionFunction::ResetShiftKeySelectionAnchor() +{ + mnShiftKeySelectionAnchor = -1; +} + +void SelectionFunction::ResetMouseAnchor() +{ + if (mpModeHandler && mpModeHandler->GetMode() == NormalMode) + { + std::shared_ptr pHandler ( + std::dynamic_pointer_cast(mpModeHandler)); + if (pHandler) + pHandler->ResetButtonDownLocation(); + } +} + +//===== EventDescriptor ======================================================= + +SelectionFunction::EventDescriptor::EventDescriptor ( + const sal_uInt32 nEventType, + const MouseEvent& rEvent, + SlideSorter const & rSlideSorter) + : maMousePosition(rEvent.GetPosPixel()), + mpHitPage(), + mnEventCode(nEventType), + meDragMode(InsertionIndicatorHandler::MoveMode), + mbIsLeaving(false) +{ + maMouseModelPosition = rSlideSorter.GetContentWindow()->PixelToLogic(maMousePosition); + mpHitDescriptor = rSlideSorter.GetController().GetPageAt(maMousePosition); + if (mpHitDescriptor) + { + mpHitPage = mpHitDescriptor->GetPage(); + } + + mnEventCode |= EncodeMouseEvent(rEvent); + mnEventCode |= EncodeState(); + + // Detect the mouse leaving the window. When not button is pressed then + // we can call IsLeaveWindow at the event. Otherwise we have to make an + // explicit test. + mbIsLeaving = rEvent.IsLeaveWindow() + || ! ::tools::Rectangle(Point(0,0), + rSlideSorter.GetContentWindow()->GetOutputSizePixel()).Contains(maMousePosition); +} + +SelectionFunction::EventDescriptor::EventDescriptor ( + const sal_uInt32 nEventType, + const AcceptDropEvent& rEvent, + const sal_Int8 nDragAction, + SlideSorter const & rSlideSorter) + : maMousePosition(rEvent.maPosPixel), + mpHitPage(), + mnEventCode(nEventType), + meDragMode(InsertionIndicatorHandler::GetModeFromDndAction(nDragAction)), + mbIsLeaving(false) +{ + maMouseModelPosition = rSlideSorter.GetContentWindow()->PixelToLogic(maMousePosition); + mpHitDescriptor = rSlideSorter.GetController().GetPageAt(maMousePosition); + if (mpHitDescriptor) + { + mpHitPage = mpHitDescriptor->GetPage(); + } + + mnEventCode |= EncodeState(); + + // Detect the mouse leaving the window. When not button is pressed then + // we can call IsLeaveWindow at the event. Otherwise we have to make an + // explicit test. + mbIsLeaving = rEvent.mbLeaving + || ! ::tools::Rectangle(Point(0,0), + rSlideSorter.GetContentWindow()->GetOutputSizePixel()).Contains(maMousePosition); +} + +sal_uInt32 SelectionFunction::EventDescriptor::EncodeMouseEvent ( + const MouseEvent& rEvent) const +{ + // Initialize with the type of mouse event. + sal_uInt32 nEventCode (mnEventCode & (BUTTON_DOWN | BUTTON_UP | MOUSE_MOTION)); + + // Detect the affected button. + switch (rEvent.GetButtons()) + { + case MOUSE_LEFT: nEventCode |= LEFT_BUTTON; break; + case MOUSE_RIGHT: nEventCode |= RIGHT_BUTTON; break; + case MOUSE_MIDDLE: nEventCode |= MIDDLE_BUTTON; break; + } + + // Detect the number of clicks. + switch (rEvent.GetClicks()) + { + case 1: nEventCode |= SINGLE_CLICK; break; + case 2: nEventCode |= DOUBLE_CLICK; break; + } + + // Detect pressed modifier keys. + if (rEvent.IsShift()) + nEventCode |= SHIFT_MODIFIER; + if (rEvent.IsMod1()) + nEventCode |= CONTROL_MODIFIER; + + return nEventCode; +} + +sal_uInt32 SelectionFunction::EventDescriptor::EncodeState() const +{ + sal_uInt32 nEventCode (0); + + // Detect whether the event has happened over a page object. + if (mpHitPage!=nullptr && mpHitDescriptor) + { + if (mpHitDescriptor->HasState(model::PageDescriptor::ST_Selected)) + nEventCode |= OVER_SELECTED_PAGE; + else + nEventCode |= OVER_UNSELECTED_PAGE; + } + + return nEventCode; +} + +//===== SelectionFunction::ModeHandler ======================================== + +SelectionFunction::ModeHandler::ModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const bool bIsMouseOverIndicatorAllowed) + : mrSlideSorter(rSlideSorter), + mrSelectionFunction(rSelectionFunction), + mbIsMouseOverIndicatorAllowed(bIsMouseOverIndicatorAllowed) +{ +} + +SelectionFunction::ModeHandler::~ModeHandler() COVERITY_NOEXCEPT_FALSE +{ +} + +void SelectionFunction::ModeHandler::ReprocessEvent (EventDescriptor& rDescriptor) +{ + mrSelectionFunction.ProcessEvent(rDescriptor); +} + +void SelectionFunction::ModeHandler::ProcessEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter); + PageSelector::UpdateLock aUpdateLock (mrSlideSorter); + + bool bIsProcessed (false); + switch (rDescriptor.mnEventCode & (BUTTON_DOWN | BUTTON_UP | MOUSE_MOTION | MOUSE_DRAG)) + { + case BUTTON_DOWN: + bIsProcessed = ProcessButtonDownEvent(rDescriptor); + break; + + case BUTTON_UP: + bIsProcessed = ProcessButtonUpEvent(rDescriptor); + break; + + case MOUSE_MOTION: + bIsProcessed = ProcessMotionEvent(rDescriptor); + break; + + case MOUSE_DRAG: + bIsProcessed = ProcessDragEvent(rDescriptor); + break; + } + + if ( ! bIsProcessed) + HandleUnprocessedEvent(rDescriptor); +} + +bool SelectionFunction::ModeHandler::ProcessButtonDownEvent (EventDescriptor&) +{ + return false; +} + +bool SelectionFunction::ModeHandler::ProcessButtonUpEvent (EventDescriptor&) +{ + mrSelectionFunction.SwitchToNormalMode(); + return false; +} + +bool SelectionFunction::ModeHandler::ProcessMotionEvent (EventDescriptor& rDescriptor) +{ + if (mbIsMouseOverIndicatorAllowed) + mrSlideSorter.GetView().UpdatePageUnderMouse(rDescriptor.maMousePosition); + + if (rDescriptor.mbIsLeaving) + { + mrSelectionFunction.SwitchToNormalMode(); + mrSlideSorter.GetView().SetPageUnderMouse(model::SharedPageDescriptor()); + + return true; + } + else + return false; +} + +bool SelectionFunction::ModeHandler::ProcessDragEvent (EventDescriptor&) +{ + return false; +} + +bool SelectionFunction::ModeHandler::HandleUnprocessedEvent (EventDescriptor&) +{ + return false; +} + +void SelectionFunction::ModeHandler::SetCurrentPage ( + const model::SharedPageDescriptor& rpDescriptor) +{ + SelectOnePage(rpDescriptor); + mrSlideSorter.GetController().GetCurrentSlideManager()->SwitchCurrentSlide(rpDescriptor); +} + +void SelectionFunction::ModeHandler::DeselectAllPages() +{ + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + mrSelectionFunction.ResetShiftKeySelectionAnchor(); +} + +void SelectionFunction::ModeHandler::SelectOnePage ( + const model::SharedPageDescriptor& rpDescriptor) +{ + DeselectAllPages(); + mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor); +} + +void SelectionFunction::ModeHandler::SwitchView (const model::SharedPageDescriptor& rpDescriptor) +{ + // Switch to the draw view. This is done only when the current + // view is the main view. + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell==nullptr || !pViewShell->IsMainViewShell()) + return; + + if (rpDescriptor && rpDescriptor->GetPage()!=nullptr) + { + mrSlideSorter.GetModel().GetDocument()->SetSelected(rpDescriptor->GetPage(), true); + pViewShell->GetFrameView()->SetSelectedPage( + (rpDescriptor->GetPage()->GetPageNum()-1)/2); + } + if (mrSlideSorter.GetViewShellBase() != nullptr) + framework::FrameworkHelper::Instance(*mrSlideSorter.GetViewShellBase())->RequestView( + framework::FrameworkHelper::msImpressViewURL, + framework::FrameworkHelper::msCenterPaneURL); +} + +void SelectionFunction::ModeHandler::StartDrag ( + const Point& rMousePosition) +{ + // Do not start a drag-and-drop operation when one is already active. + // (when dragging pages from one document into another, pressing a + // modifier key can trigger a MouseMotion event in the originating + // window (focus still in there). Together with the mouse button pressed + // (drag-and-drop is active) this triggers the start of drag-and-drop.) + if (SD_MOD()->pTransferDrag != nullptr) + return; + + if ( ! mrSlideSorter.GetProperties()->IsUIReadOnly()) + { + mrSelectionFunction.SwitchToDragAndDropMode(rMousePosition); + } +} + +//===== NormalModeHandler ===================================================== + +NormalModeHandler::NormalModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction) + : ModeHandler(rSlideSorter, rSelectionFunction, true) +{ +} + +SelectionFunction::Mode NormalModeHandler::GetMode() const +{ + return SelectionFunction::NormalMode; +} + +void NormalModeHandler::Abort() +{ +} + +bool NormalModeHandler::ProcessButtonDownEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // Remember the location where the left button is pressed. With + // that we can filter away motion events that are caused by key + // presses. We also can tune the minimal motion distance that + // triggers a drag-and-drop operation. + if ((rDescriptor.mnEventCode & BUTTON_DOWN) != 0) + maButtonDownLocation = rDescriptor.maMousePosition; + + switch (rDescriptor.mnEventCode) + { + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE: + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + break; + + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_SELECTED_PAGE: + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | OVER_UNSELECTED_PAGE: + // A double click always shows the selected slide in the center + // pane in an edit view. + SetCurrentPage(rDescriptor.mpHitDescriptor); + SwitchView(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | SHIFT_MODIFIER: + case BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | SHIFT_MODIFIER: + // Range selection with the shift modifier. + RangeSelect(rDescriptor.mpHitDescriptor); + break; + + // Right button for context menu. + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE: + // Single right click and shift+F10 select as preparation to + // show the context menu. Change the selection only when the + // page under the mouse is not selected. In this case the + // selection is set to this single page. Otherwise the + // selection is not modified. + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + // Do not change the selection. Just adjust the insertion indicator. + break; + + case BUTTON_DOWN | RIGHT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE: + // Remember the current selection so that when a multi selection + // is started, we can restore the previous selection. + mrSlideSorter.GetModel().SaveCurrentSelection(); + DeselectAllPages(); + break; + + case ANY_MODIFIER(BUTTON_DOWN | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE): + // Remember the current selection so that when a multi selection + // is started, we can restore the previous selection. + mrSlideSorter.GetModel().SaveCurrentSelection(); + DeselectAllPages(); + break; + + case BUTTON_DOWN | LEFT_BUTTON | DOUBLE_CLICK | NOT_OVER_PAGE: + { + // Insert a new slide: + // First of all we need to set the insertion indicator which sets the + // position where the new slide will be inserted. + std::shared_ptr pInsertionIndicatorHandler + = mrSlideSorter.GetController().GetInsertionIndicatorHandler(); + + pInsertionIndicatorHandler->Start(false); + pInsertionIndicatorHandler->UpdatePosition( + rDescriptor.maMousePosition, + InsertionIndicatorHandler::MoveMode); + + mrSlideSorter.GetController().GetSelectionManager()->SetInsertionPosition( + pInsertionIndicatorHandler->GetInsertionPageIndex()); + + mrSlideSorter.GetViewShell()->GetDispatcher()->Execute( + SID_INSERTPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + pInsertionIndicatorHandler->End(Animator::AM_Immediate); + + break; + } + + default: + return false; + } + return true; +} + +bool NormalModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + bool bIsProcessed (true); + switch (rDescriptor.mnEventCode) + { + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE: + SetCurrentPage(rDescriptor.mpHitDescriptor); + break; + + // Multi selection with the control modifier. + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE | CONTROL_MODIFIER: + mrSlideSorter.GetController().GetPageSelector().DeselectPage( + rDescriptor.mpHitDescriptor); + break; + + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE | CONTROL_MODIFIER: + mrSlideSorter.GetController().GetPageSelector().SelectPage( + rDescriptor.mpHitDescriptor); + mrSlideSorter.GetView().SetPageUnderMouse(rDescriptor.mpHitDescriptor); + break; + case BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE: + break; + + default: + bIsProcessed = false; + break; + } + mrSelectionFunction.SwitchToNormalMode(); + return bIsProcessed; +} + +bool NormalModeHandler::ProcessMotionEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (ModeHandler::ProcessMotionEvent(rDescriptor)) + return true; + + bool bIsProcessed (true); + switch (rDescriptor.mnEventCode) + { + // A mouse motion without visible substitution starts that. + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_UNSELECTED_PAGE): + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | OVER_SELECTED_PAGE): + { + if (maButtonDownLocation) + { + const sal_Int32 nDistance(std::max( + std::abs(maButtonDownLocation->X() - rDescriptor.maMousePosition.X()), + std::abs(maButtonDownLocation->Y() - rDescriptor.maMousePosition.Y()))); + if (nDistance > 3) + StartDrag(rDescriptor.maMousePosition); + } + break; + } + + // A mouse motion not over a page starts a rectangle selection. + case ANY_MODIFIER(MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK | NOT_OVER_PAGE): + mrSelectionFunction.SwitchToMultiSelectionMode( + rDescriptor.maMouseModelPosition, + rDescriptor.mnEventCode); + break; + + default: + bIsProcessed = false; + break; + } + return bIsProcessed; +} + +bool NormalModeHandler::ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) +{ + mrSelectionFunction.SwitchToDragAndDropMode(rDescriptor.maMousePosition); + ReprocessEvent(rDescriptor); + return true; +} + +void NormalModeHandler::RangeSelect (const model::SharedPageDescriptor& rpDescriptor) +{ + PageSelector::UpdateLock aLock (mrSlideSorter); + PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + + model::SharedPageDescriptor pAnchor (rSelector.GetSelectionAnchor()); + DeselectAllPages(); + + if (!pAnchor) + return; + + // Select all pages between the anchor and the given one, including + // the two. + const sal_uInt16 nAnchorIndex ((pAnchor->GetPage()->GetPageNum()-1) / 2); + const sal_uInt16 nOtherIndex ((rpDescriptor->GetPage()->GetPageNum()-1) / 2); + + // Iterate over all pages in the range. Start with the anchor + // page. This way the PageSelector will recognize it again as + // anchor (the first selected page after a DeselectAllPages() + // becomes the anchor.) + const sal_uInt16 nStep ((nAnchorIndex < nOtherIndex) ? +1 : -1); + sal_uInt16 nIndex (nAnchorIndex); + while (true) + { + rSelector.SelectPage(nIndex); + if (nIndex == nOtherIndex) + break; + nIndex = nIndex + nStep; + } +} + +void NormalModeHandler::ResetButtonDownLocation() +{ + maButtonDownLocation = ::std::optional(); +} + +//===== MultiSelectionModeHandler ============================================= + +MultiSelectionModeHandler::MultiSelectionModeHandler ( + SlideSorter& rSlideSorter, + SelectionFunction& rSelectionFunction, + const Point& rMouseModelPosition, + const sal_uInt32 nEventCode) + : ModeHandler(rSlideSorter, rSelectionFunction, false), + meSelectionMode(SM_Normal), + maSecondCorner(rMouseModelPosition), + maSavedPointer(mrSlideSorter.GetContentWindow()->GetPointer()), + mbAutoScrollInstalled(false), + mnAnchorIndex(-1), + mnSecondIndex(-1) +{ + + mrSlideSorter.GetContentWindow()->SetPointer(PointerStyle::Text); + SetSelectionModeFromModifier(nEventCode); +} + +MultiSelectionModeHandler::~MultiSelectionModeHandler() +{ + if (mbAutoScrollInstalled) + { + //a call to this handler's MultiSelectionModeHandler::UpdatePosition + //may be still waiting to be called back + mrSlideSorter.GetController().GetScrollBarManager().clearAutoScrollFunctor(); + } + mrSlideSorter.GetContentWindow()->SetPointer(maSavedPointer); +} + +SelectionFunction::Mode MultiSelectionModeHandler::GetMode() const +{ + return SelectionFunction::MultiSelectionMode; +} + +void MultiSelectionModeHandler::Abort() +{ + mrSlideSorter.GetView().RequestRepaint(mrSlideSorter.GetModel().RestoreSelection()); +} + +void MultiSelectionModeHandler::ProcessEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // During a multi selection we do not want sudden jumps of the + // visible area caused by moving newly selected pages into view. + // Therefore disable that temporarily. The disabled object is + // released at the end of the event processing, after the focus and + // current slide have been updated. + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + + ModeHandler::ProcessEvent(rDescriptor); +} + +bool MultiSelectionModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (mbAutoScrollInstalled) + { + //a call to this handler's MultiSelectionModeHandler::UpdatePosition + //may be still waiting to be called back + mrSlideSorter.GetController().GetScrollBarManager().clearAutoScrollFunctor(); + mbAutoScrollInstalled = false; + } + + if (Match(rDescriptor.mnEventCode, BUTTON_UP | LEFT_BUTTON | SINGLE_CLICK)) + { + mrSelectionFunction.SwitchToNormalMode(); + return true; + } + else + return false; +} + +bool MultiSelectionModeHandler::ProcessMotionEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + // The selection rectangle is visible. Handle events accordingly. + if (Match(rDescriptor.mnEventCode, MOUSE_MOTION | LEFT_BUTTON | SINGLE_CLICK)) + { + SetSelectionModeFromModifier(rDescriptor.mnEventCode); + UpdatePosition(rDescriptor.maMousePosition, true); + return true; + } + else + return false; +} + +bool MultiSelectionModeHandler::HandleUnprocessedEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if ( ! ModeHandler::HandleUnprocessedEvent(rDescriptor)) + { + // If the event has not been processed then stop multi selection. + mrSelectionFunction.SwitchToNormalMode(); + ReprocessEvent(rDescriptor); + } + return true; +} + +void MultiSelectionModeHandler::UpdatePosition ( + const Point& rMousePosition, + const bool bAllowAutoScroll) +{ + VisibleAreaManager::TemporaryDisabler aDisabler (mrSlideSorter); + + // Convert window coordinates into model coordinates (we need the + // window coordinates for auto-scrolling because that remains + // constant while scrolling.) + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + const Point aMouseModelPosition (pWindow->PixelToLogic(rMousePosition)); + + bool bDoAutoScroll = bAllowAutoScroll && mrSlideSorter.GetController().GetScrollBarManager().AutoScroll( + rMousePosition, + [this, &rMousePosition] () { return this->UpdatePosition(rMousePosition, false); }); + + if (!bDoAutoScroll) + UpdateModelPosition(aMouseModelPosition); + + mbAutoScrollInstalled |= bDoAutoScroll; +} + +void MultiSelectionModeHandler::SetSelectionModeFromModifier ( + const sal_uInt32 nEventCode) +{ + switch (nEventCode & MODIFIER_MASK) + { + case NO_MODIFIER: + SetSelectionMode(SM_Normal); + break; + + case SHIFT_MODIFIER: + SetSelectionMode(SM_Add); + break; + + case CONTROL_MODIFIER: + SetSelectionMode(SM_Toggle); + break; + } +} + +void MultiSelectionModeHandler::SetSelectionMode (const SelectionMode eSelectionMode) +{ + if (meSelectionMode != eSelectionMode) + { + meSelectionMode = eSelectionMode; + UpdateSelection(); + } +} + +void MultiSelectionModeHandler::UpdateSelectionState ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bIsInSelection) const +{ + // Determine whether the page was selected before the rectangle + // selection was started. + const bool bWasSelected (rpDescriptor->HasState(model::PageDescriptor::ST_WasSelected)); + + // Combine the two selection states depending on the selection mode. + bool bSelect (false); + switch(meSelectionMode) + { + case SM_Normal: + bSelect = bIsInSelection; + break; + + case SM_Add: + bSelect = bIsInSelection || bWasSelected; + break; + + case SM_Toggle: + if (bIsInSelection) + bSelect = !bWasSelected; + else + bSelect = bWasSelected; + break; + } + + // Set the new selection state. + if (bSelect) + mrSlideSorter.GetController().GetPageSelector().SelectPage(rpDescriptor); + else + mrSlideSorter.GetController().GetPageSelector().DeselectPage(rpDescriptor); +} + +void MultiSelectionModeHandler::UpdateModelPosition (const Point& rMouseModelPosition) +{ + maSecondCorner = rMouseModelPosition; + UpdateSelection(); +} + +void MultiSelectionModeHandler::UpdateSelection() +{ + view::SlideSorterView::DrawLock aLock (mrSlideSorter); + + model::SlideSorterModel& rModel (mrSlideSorter.GetModel()); + const sal_Int32 nPageCount (rModel.GetPageCount()); + + const sal_Int32 nIndexUnderMouse ( + mrSlideSorter.GetView().GetLayouter().GetIndexAtPoint ( + maSecondCorner, + false, + false)); + if (nIndexUnderMouse < 0 || nIndexUnderMouse >= nPageCount) + return; + + if (mnAnchorIndex < 0) + mnAnchorIndex = nIndexUnderMouse; + mnSecondIndex = nIndexUnderMouse; + + Range aRange (mnAnchorIndex, mnSecondIndex); + aRange.Justify(); + + for (sal_Int32 nIndex=0; nIndexpTransferDrag; + if (pDragTransferable==nullptr && mrSlideSorter.GetViewShell() != nullptr) + { + SlideSorterViewShell* pSlideSorterViewShell + = dynamic_cast(mrSlideSorter.GetViewShell()); + if (pSlideSorterViewShell != nullptr) + pSlideSorterViewShell->StartDrag(rMousePosition, pWindow); + pDragTransferable = SD_MOD()->pTransferDrag; + } + + mpDragAndDropContext.reset(new DragAndDropContext(mrSlideSorter)); + mrSlideSorter.GetController().GetInsertionIndicatorHandler()->Start( + pDragTransferable != nullptr + && pDragTransferable->GetView()==&mrSlideSorter.GetView()); +} + +DragAndDropModeHandler::~DragAndDropModeHandler() +{ + if (mpDragAndDropContext) + { + // Disconnect the substitution handler from this selection function. + mpDragAndDropContext->SetTargetSlideSorter(); + mpDragAndDropContext.reset(); + } + mrSlideSorter.GetController().GetInsertionIndicatorHandler()->End(Animator::AM_Animated); +} + +SelectionFunction::Mode DragAndDropModeHandler::GetMode() const +{ + return SelectionFunction::DragAndDropMode; +} + +void DragAndDropModeHandler::Abort() +{ + mrSlideSorter.GetController().GetClipboard().Abort(); + if (mpDragAndDropContext) + mpDragAndDropContext->Dispose(); + // mrSlideSorter.GetView().RequestRepaint(mrSlideSorter.GetModel().RestoreSelection()); +} + +bool DragAndDropModeHandler::ProcessButtonUpEvent ( + SelectionFunction::EventDescriptor& rDescriptor) +{ + if (Match(rDescriptor.mnEventCode, BUTTON_UP | LEFT_BUTTON)) + { + // The following Process() call may lead to the destruction + // of rDescriptor.mpHitDescriptor so release our reference to it. + rDescriptor.mpHitDescriptor.reset(); + mrSelectionFunction.SwitchToNormalMode(); + return true; + } + else + return false; +} + +bool DragAndDropModeHandler::ProcessDragEvent (SelectionFunction::EventDescriptor& rDescriptor) +{ + OSL_ASSERT(mpDragAndDropContext); + + if (rDescriptor.mbIsLeaving) + { + mrSelectionFunction.SwitchToNormalMode(); + } + else if (mpDragAndDropContext) + { + mpDragAndDropContext->UpdatePosition( + rDescriptor.maMousePosition, + rDescriptor.meDragMode, true); + } + + return true; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx new file mode 100644 index 000000000..e1f75b21c --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionManager.cxx @@ -0,0 +1,309 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::uno; +using namespace ::sd::slidesorter::model; +using namespace ::sd::slidesorter::view; +using namespace ::sd::slidesorter::controller; + +namespace sd::slidesorter::controller { + +SelectionManager::SelectionManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mrController(rSlideSorter.GetController()), + mnInsertionPosition(-1), + mpSelectionObserver(std::make_shared(rSlideSorter)) +{ +} + +SelectionManager::~SelectionManager() +{ +} + +void SelectionManager::DeleteSelectedPages (const bool bSelectFollowingPage) +{ + // Create some locks to prevent updates of the model, view, selection + // state while modifying any of them. + SlideSorterController::ModelChangeLock aLock (mrController); + SlideSorterView::DrawLock aDrawLock (mrSlideSorter); + PageSelector::UpdateLock aSelectionLock (mrSlideSorter); + + // Hide focus. + bool bIsFocusShowing = mrController.GetFocusManager().IsFocusShowing(); + if (bIsFocusShowing) + mrController.GetFocusManager().ToggleFocus(); + + // Store pointers to all selected page descriptors. This is necessary + // because the pages get deselected when the first one is deleted. + model::PageEnumeration aPageEnumeration ( + PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter.GetModel())); + ::std::vector aSelectedPages; + sal_Int32 nNewCurrentSlide (-1); + while (aPageEnumeration.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aPageEnumeration.GetNextElement()); + aSelectedPages.push_back(pDescriptor->GetPage()); + if (bSelectFollowingPage || nNewCurrentSlide<0) + nNewCurrentSlide = pDescriptor->GetPageIndex(); + } + if (aSelectedPages.empty()) + return; + + // Determine the slide to select (and thereby make the current slide) + // after the deletion. + if (bSelectFollowingPage) + nNewCurrentSlide -= aSelectedPages.size() - 1; + else + --nNewCurrentSlide; + + const auto pViewShell = mrSlideSorter.GetViewShell(); + const auto pDrawViewShell = pViewShell ? std::dynamic_pointer_cast(pViewShell->GetViewShellBase().GetMainViewShell()) : nullptr; + const auto pDrawView = pDrawViewShell ? pDrawViewShell->GetDrawView() : nullptr; + + if (pDrawView) + pDrawView->BlockPageOrderChangedHint(true); + + // Proper naming for the undo action + OUString sUndoComment(SdResId(STR_UNDO_DELETEPAGES)); + if (mrSlideSorter.GetView().GetDoc().GetDocumentType() == DocumentType::Draw) + sUndoComment = SdResId(STR_UNDO_DELETEPAGES_DRAW); + + // The actual deletion of the selected pages is done in one of two + // helper functions. They are specialized for normal respectively for + // master pages. + mrSlideSorter.GetView().BegUndo (sUndoComment); + if (mrSlideSorter.GetModel().GetEditMode() == EditMode::Page) + DeleteSelectedNormalPages(aSelectedPages); + else + DeleteSelectedMasterPages(aSelectedPages); + mrSlideSorter.GetView().EndUndo (); + + mrController.HandleModelChange(); + aLock.Release(); + if (pDrawView) + { + assert(pDrawViewShell); + pDrawView->BlockPageOrderChangedHint(false); + pDrawViewShell->ResetActualPage(); + } + + // Show focus and move it to next valid location. + if (bIsFocusShowing) + mrController.GetFocusManager().ToggleFocus(); + + // Set the new current slide. + if (nNewCurrentSlide < 0) + nNewCurrentSlide = 0; + else if (nNewCurrentSlide >= mrSlideSorter.GetModel().GetPageCount()) + nNewCurrentSlide = mrSlideSorter.GetModel().GetPageCount()-1; + mrController.GetPageSelector().CountSelectedPages(); + mrController.GetPageSelector().SelectPage(nNewCurrentSlide); + mrController.GetFocusManager().SetFocusedPage(nNewCurrentSlide); +} + +void SelectionManager::DeleteSelectedNormalPages (const ::std::vector& rSelectedPages) +{ + // Prepare the deletion via the UNO API. + OSL_ASSERT(mrSlideSorter.GetModel().GetEditMode() == EditMode::Page); + + try + { + Reference xDrawPagesSupplier( mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY_THROW ); + Reference xPages( xDrawPagesSupplier->getDrawPages(), UNO_SET_THROW ); + + // Iterate over all pages that were selected when this method was called + // and delete the draw page the notes page. The iteration is done in + // reverse order so that when one slide is not deleted (to avoid an + // empty document) the remaining slide is the first one. + ::std::vector::const_reverse_iterator aI; + for (aI=rSelectedPages.rbegin(); aI!=rSelectedPages.rend(); ++aI) + { + // Do not delete the last slide in the document. + if (xPages->getCount() <= 1) + break; + + const sal_uInt16 nPage (model::FromCoreIndex((*aI)->GetPageNum())); + + Reference< XDrawPage > xPage( xPages->getByIndex( nPage ), UNO_QUERY_THROW ); + xPages->remove(xPage); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "SelectionManager::DeleteSelectedNormalPages()"); + } +} + +void SelectionManager::DeleteSelectedMasterPages (const ::std::vector& rSelectedPages) +{ + // Prepare the deletion via the UNO API. + OSL_ASSERT(mrSlideSorter.GetModel().GetEditMode() == EditMode::MasterPage); + + try + { + Reference xDrawPagesSupplier( mrSlideSorter.GetModel().GetDocument()->getUnoModel(), UNO_QUERY_THROW ); + Reference xPages( xDrawPagesSupplier->getMasterPages(), UNO_SET_THROW ); + + // Iterate over all pages that were selected when this method was called + // and delete the draw page the notes page. The iteration is done in + // reverse order so that when one slide is not deleted (to avoid an + // empty document) the remaining slide is the first one. + ::std::vector::const_reverse_iterator aI; + for (aI=rSelectedPages.rbegin(); aI!=rSelectedPages.rend(); ++aI) + { + // Do not delete the last slide in the document. + if (xPages->getCount() <= 1) + break; + + const sal_uInt16 nPage (model::FromCoreIndex((*aI)->GetPageNum())); + + Reference< XDrawPage > xPage( xPages->getByIndex( nPage ), UNO_QUERY_THROW ); + xPages->remove(xPage); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "SelectionManager::DeleteSelectedMasterPages()"); + } +} + +void SelectionManager::SelectionHasChanged () +{ + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell == nullptr) + return; + + pViewShell->Invalidate (SID_EXPAND_PAGE); + pViewShell->Invalidate (SID_SUMMARY_PAGE); + pViewShell->Invalidate(SID_SHOW_SLIDE); + pViewShell->Invalidate(SID_HIDE_SLIDE); + pViewShell->Invalidate(SID_DELETE_PAGE); + pViewShell->Invalidate(SID_DELETE_MASTER_PAGE); + pViewShell->Invalidate(SID_ASSIGN_LAYOUT); + + // StatusBar + pViewShell->Invalidate (SID_STATUS_PAGE); + pViewShell->Invalidate (SID_STATUS_LAYOUT); + pViewShell->Invalidate (SID_SCALE); + + OSL_ASSERT(mrController.GetCurrentSlideManager()); + SharedPageDescriptor pDescriptor(mrController.GetCurrentSlideManager()->GetCurrentSlide()); + if (pDescriptor) + pViewShell->UpdatePreview(pDescriptor->GetPage()); + + // Tell the selection change listeners that the selection has changed. + for (const auto& rLink : maSelectionChangeListeners) + { + rLink.Call(nullptr); + } + + // Reset the insertion position: until set again it is calculated from + // the current selection. + mnInsertionPosition = -1; +} + +void SelectionManager::AddSelectionChangeListener (const Link& rListener) +{ + if (::std::find ( + maSelectionChangeListeners.begin(), + maSelectionChangeListeners.end(), + rListener) == maSelectionChangeListeners.end()) + { + maSelectionChangeListeners.push_back (rListener); + } +} + +void SelectionManager::RemoveSelectionChangeListener(const Link& rListener) +{ + maSelectionChangeListeners.erase ( + ::std::find ( + maSelectionChangeListeners.begin(), + maSelectionChangeListeners.end(), + rListener)); +} + +sal_Int32 SelectionManager::GetInsertionPosition() const +{ + sal_Int32 nInsertionPosition (mnInsertionPosition); + if (nInsertionPosition < 0) + { + model::PageEnumeration aSelectedPages + (model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + // Initialize (for the case of an empty selection) with the position + // at the end of the document. + nInsertionPosition = mrSlideSorter.GetModel().GetPageCount(); + while (aSelectedPages.HasMoreElements()) + { + const sal_Int32 nPosition (aSelectedPages.GetNextElement()->GetPage()->GetPageNum()); + // Convert *2+1 index to straight index (n-1)/2 after the page + // (+1). + nInsertionPosition = model::FromCoreIndex(nPosition) + 1; + } + + } + return nInsertionPosition; +} + +void SelectionManager::SetInsertionPosition (const sal_Int32 nInsertionPosition) +{ + if (nInsertionPosition < 0) + mnInsertionPosition = -1; + else if (nInsertionPosition > mrSlideSorter.GetModel().GetPageCount()) + { + // Assert but then ignore invalid values. + OSL_ASSERT(nInsertionPosition<=mrSlideSorter.GetModel().GetPageCount()); + return; + } + else + mnInsertionPosition = nInsertionPosition; +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsSelectionObserver.cxx b/sd/source/ui/slidesorter/controller/SlsSelectionObserver.cxx new file mode 100644 index 000000000..8fb0493a0 --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSelectionObserver.cxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd::slidesorter::controller +{ +SelectionObserver::Context::Context(SlideSorter const& rSlideSorter) + : mpSelectionObserver( + rSlideSorter.GetController().GetSelectionManager()->GetSelectionObserver()) +{ + if (mpSelectionObserver) + mpSelectionObserver->StartObservation(); +} + +SelectionObserver::Context::~Context() COVERITY_NOEXCEPT_FALSE +{ + if (mpSelectionObserver) + mpSelectionObserver->EndObservation(); +} + +void SelectionObserver::Context::Abort() +{ + if (mpSelectionObserver) + { + mpSelectionObserver->AbortObservation(); + mpSelectionObserver.reset(); + } +} + +//===== SelectionObserver ===================================================== + +SelectionObserver::SelectionObserver(SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter) + , mbIsObservationActive(false) + , mbPageEventOccurred(false) +{ +} + +SelectionObserver::~SelectionObserver() {} + +void SelectionObserver::NotifyPageEvent(const SdrPage* pSdrPage) +{ + if (!mbIsObservationActive) + return; + + mbPageEventOccurred = true; + + const SdPage* pPage = dynamic_cast(pSdrPage); + if (pPage == nullptr) + return; + + //NotifyPageEvent is called for add, remove, *and* change position so for + //the change position case we must ensure we don't end up with the slide + //duplicated in our list + std::vector::iterator iPage( + std::find(maInsertedPages.begin(), maInsertedPages.end(), pPage)); + if (iPage != maInsertedPages.end()) + maInsertedPages.erase(iPage); + + if (pPage->IsInserted()) + maInsertedPages.push_back(pPage); +} + +void SelectionObserver::StartObservation() +{ + OSL_ASSERT(!mbIsObservationActive); + maInsertedPages.clear(); + mbIsObservationActive = true; +} + +void SelectionObserver::AbortObservation() +{ + OSL_ASSERT(mbIsObservationActive); + mbIsObservationActive = false; + maInsertedPages.clear(); +} + +void SelectionObserver::EndObservation() +{ + OSL_ASSERT(mbIsObservationActive); + mbIsObservationActive = false; + + if (!mbPageEventOccurred) + return; + + PageSelector& rSelector(mrSlideSorter.GetController().GetPageSelector()); + PageSelector::UpdateLock aUpdateLock(mrSlideSorter); + rSelector.DeselectAllPages(); + if (!maInsertedPages.empty()) + { + // Select the inserted pages. + for (const auto& rpPage : maInsertedPages) + { + rSelector.SelectPage(rpPage); + } + maInsertedPages.clear(); + } + + aUpdateLock.Release(); + FocusManager& rFocusManager = mrSlideSorter.GetController().GetFocusManager(); + bool bSuccess = rFocusManager.SetFocusedPageToCurrentPage(); + // tdf#129346 nothing currently selected, select something, if possible + // but (tdf#129346) only if setting focus to current page failed + if (rSelector.GetPageCount() && rSelector.GetSelectedPageCount() == 0) + { + if (bSuccess) + rSelector.SelectPage(rFocusManager.GetFocusedPageDescriptor()); + else + rSelector.SelectPage(0); + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsSlotManager.cxx b/sd/source/ui/slidesorter/controller/SlsSlotManager.cxx new file mode 100644 index 000000000..52e05557e --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsSlotManager.cxx @@ -0,0 +1,1284 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + +namespace sd::slidesorter::controller { + +namespace { + +/** The state of a set of slides with respect to being excluded from the + slide show. +*/ +enum SlideExclusionState {UNDEFINED, EXCLUDED, INCLUDED, MIXED}; + +/** Return for the given set of slides whether they included are + excluded from the slide show. +*/ +SlideExclusionState GetSlideExclusionState (model::PageEnumeration& rPageSet); + +} // end of anonymous namespace + + +namespace { + +void collectUIInformation(std::map&& aParameters, const OUString& rAction) +{ + EventDescription aDescription; + aDescription.aID = "impress_win_or_draw_win"; + aDescription.aParameters = std::move(aParameters); + aDescription.aAction = rAction; + aDescription.aKeyWord = "ImpressWindowUIObject"; + aDescription.aParent = "MainWindow"; + + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +SlotManager::SlotManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter) +{ +} + +void SlotManager::FuTemporary (SfxRequest& rRequest) +{ + SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument(); + + SlideSorterViewShell* pShell + = dynamic_cast(mrSlideSorter.GetViewShell()); + if (pShell == nullptr) + return; + + switch (rRequest.GetSlot()) + { + case SID_PRESENTATION: + case SID_PRESENTATION_CURRENT_SLIDE: + case SID_REHEARSE_TIMINGS: + slideshowhelp::ShowSlideShow(rRequest, *mrSlideSorter.GetModel().GetDocument()); + pShell->Cancel(); + rRequest.Done(); + break; + + case SID_HIDE_SLIDE: + ChangeSlideExclusionState(model::SharedPageDescriptor(), true); + break; + + case SID_SHOW_SLIDE: + ChangeSlideExclusionState(model::SharedPageDescriptor(), false); + break; + + case SID_PAGES_PER_ROW: + if (rRequest.GetArgs() != nullptr) + { + const SfxUInt16Item* pPagesPerRow = rRequest.GetArg(SID_PAGES_PER_ROW); + if (pPagesPerRow != nullptr) + { + sal_Int32 nColumnCount = pPagesPerRow->GetValue(); + // Force the given number of columns by setting + // the minimal and maximal number of columns to + // the same value. + mrSlideSorter.GetView().GetLayouter().SetColumnCount ( + nColumnCount, nColumnCount); + // Force a repaint and re-layout. + pShell->ArrangeGUIElements (); + // Rearrange the UI-elements controlled by the + // controller and force a rearrangement of the + // view. + mrSlideSorter.GetController().Rearrange(true); + } + } + rRequest.Done(); + break; + + case SID_SELECTALL: + mrSlideSorter.GetController().GetPageSelector().SelectAllPages(); + rRequest.Done(); + break; + + case SID_SLIDE_TRANSITIONS_PANEL: + { + // First make sure that the sidebar is visible + pShell->GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + ::sfx2::sidebar::Sidebar::ShowPanel( + u"SdSlideTransitionPanel", + pShell->GetViewFrame()->GetFrame().GetFrameInterface()); + rRequest.Ignore (); + break; + } + + case SID_MASTER_SLIDES_PANEL: + { + // First make sure that the sidebar is visible + pShell->GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + ::sfx2::sidebar::Sidebar::ShowPanel( + u"SdAllMasterPagesPanel", + pShell->GetViewFrame()->GetFrame().GetFrameInterface()); + rRequest.Ignore (); + break; + } + + case SID_PRESENTATION_DLG: + FuSlideShowDlg::Create ( + pShell, + mrSlideSorter.GetContentWindow(), + &mrSlideSorter.GetView(), + pDocument, + rRequest); + break; + + case SID_CUSTOMSHOW_DLG: + FuCustomShowDlg::Create ( + pShell, + mrSlideSorter.GetContentWindow(), + &mrSlideSorter.GetView(), + pDocument, + rRequest); + break; + + case SID_EXPAND_PAGE: + FuExpandPage::Create ( + pShell, + mrSlideSorter.GetContentWindow(), + &mrSlideSorter.GetView(), + pDocument, + rRequest); + break; + + case SID_SUMMARY_PAGE: + FuSummaryPage::Create ( + pShell, + mrSlideSorter.GetContentWindow(), + &mrSlideSorter.GetView(), + pDocument, + rRequest); + break; + + case SID_INSERTPAGE: + case SID_INSERT_MASTER_PAGE: + InsertSlide(rRequest); + rRequest.Done(); + break; + + case SID_DUPLICATE_PAGE: + DuplicateSelectedSlides(rRequest); + rRequest.Done(); + break; + + case SID_DELETE_PAGE: + case SID_DELETE_MASTER_PAGE: + case SID_DELETE: // we need SID_CUT to handle the delete key + // (DEL -> accelerator -> SID_CUT). + if (mrSlideSorter.GetModel().GetPageCount() > 1) + { + mrSlideSorter.GetView().EndTextEditAllViews(); + mrSlideSorter.GetController().GetSelectionManager()->DeleteSelectedPages(); + } + + rRequest.Done(); + break; + + case SID_RENAMEPAGE: + case SID_RENAME_MASTER_PAGE: + RenameSlide (rRequest); + rRequest.Done (); + break; + + case SID_ASSIGN_LAYOUT: + { + pShell->mpImpl->AssignLayout( rRequest, PageKind::Standard ); + rRequest.Done (); + } + break; + + case SID_PHOTOALBUM: + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = mrSlideSorter.GetContentWindow(); + ScopedVclPtr pDlg(pFact->CreateSdPhotoAlbumDialog( + pWin ? pWin->GetFrameWeld() : nullptr, + pDocument)); + pDlg->Execute(); + rRequest.Done (); + } + break; + + case SID_REMOTE_DLG: + { +#ifdef ENABLE_SDREMOTE + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = mrSlideSorter.GetContentWindow(); + ScopedVclPtr pDlg(pFact->CreateRemoteDialog(pWin ? pWin->GetFrameWeld() : nullptr)); + pDlg->Execute(); +#endif + } + break; + + default: + break; + } +} + +void SlotManager::FuPermanent (SfxRequest& rRequest) +{ + ViewShell* pShell = mrSlideSorter.GetViewShell(); + if (pShell == nullptr) + return; + + if(pShell->GetCurrentFunction().is()) + { + rtl::Reference xEmpty; + if (pShell->GetOldFunction() == pShell->GetCurrentFunction()) + pShell->SetOldFunction(xEmpty); + + pShell->GetCurrentFunction()->Deactivate(); + pShell->SetCurrentFunction(xEmpty); + } + + switch(rRequest.GetSlot()) + { + case SID_OBJECT_SELECT: + pShell->SetCurrentFunction( SelectionFunction::Create(mrSlideSorter, rRequest) ); + rRequest.Done(); + break; + + default: + break; + } + + if(pShell->GetOldFunction().is()) + { + pShell->GetOldFunction()->Deactivate(); + rtl::Reference xEmpty; + pShell->SetOldFunction(xEmpty); + } + + if(pShell->GetCurrentFunction().is()) + { + pShell->GetCurrentFunction()->Activate(); + pShell->SetOldFunction(pShell->GetCurrentFunction()); + } + + //! that's only until ENUM-Slots ?are + // Invalidate( SID_OBJECT_SELECT ); +} + +void SlotManager::FuSupport (SfxRequest& rRequest) +{ + switch (rRequest.GetSlot()) + { + case SID_STYLE_FAMILY: + if (rRequest.GetArgs() != nullptr) + { + SdDrawDocument* pDocument + = mrSlideSorter.GetModel().GetDocument(); + if (pDocument != nullptr) + { + const SfxPoolItem& rItem ( + rRequest.GetArgs()->Get(SID_STYLE_FAMILY)); + pDocument->GetDocSh()->SetStyleFamily( + static_cast(static_cast(rItem).GetValue())); + } + } + break; + + case SID_PASTE: + { + SdTransferable* pTransferClip = SD_MOD()->pTransferClip; + if( pTransferClip ) + { + SfxObjectShell* pTransferDocShell = pTransferClip->GetDocShell().get(); + + DrawDocShell* pDocShell = dynamic_cast(pTransferDocShell); + if (pDocShell && pDocShell->GetDoc()->GetPageCount() > 1) + { + mrSlideSorter.GetController().GetClipboard().HandleSlotCall(rRequest); + break; + } + } + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(pBase->GetMainViewShell())); + if (pDrawViewShell != nullptr) + pDrawViewShell->FuSupport(rRequest); + } + } + break; + + case SID_CUT: + case SID_COPY: + case SID_DELETE: + mrSlideSorter.GetView().EndTextEditAllViews(); + mrSlideSorter.GetController().GetClipboard().HandleSlotCall(rRequest); + break; + + case SID_DRAWINGMODE: + case SID_NOTES_MODE: + case SID_HANDOUT_MASTER_MODE: + case SID_SLIDE_SORTER_MODE: + case SID_OUTLINE_MODE: + { + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + framework::FrameworkHelper::Instance(*pBase)->HandleModeChangeSlot( + rRequest.GetSlot(), rRequest); + rRequest.Done(); + } + break; + } + + case SID_UNDO: + { + SlideSorterViewShell* pViewShell + = dynamic_cast(mrSlideSorter.GetViewShell()); + if (pViewShell != nullptr) + { + pViewShell->ImpSidUndo (rRequest); + } + break; + } + + case SID_REDO: + { + SlideSorterViewShell* pViewShell + = dynamic_cast(mrSlideSorter.GetViewShell()); + if (pViewShell != nullptr) + { + pViewShell->ImpSidRedo (rRequest); + } + break; + } + + default: + break; + } +} + +void SlotManager::ExecCtrl (SfxRequest& rRequest) +{ + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + sal_uInt16 nSlot = rRequest.GetSlot(); + switch (nSlot) + { + case SID_RELOAD: + { + // empty Undo-Manager + mrSlideSorter.GetModel().GetDocument()->GetDocSh()->ClearUndoBuffer(); + + // normal forwarding to ViewFrame for execution + if (pViewShell != nullptr) + pViewShell->GetViewFrame()->ExecuteSlot(rRequest); + + // has to be finished right away + return; + } + + case SID_OUTPUT_QUALITY_COLOR: + case SID_OUTPUT_QUALITY_GRAYSCALE: + case SID_OUTPUT_QUALITY_BLACKWHITE: + case SID_OUTPUT_QUALITY_CONTRAST: + { + // flush page cache + if (pViewShell != nullptr) + pViewShell->ExecReq (rRequest); + break; + } + + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + if (pViewShell != nullptr) + pViewShell->ExecReq (rRequest); + break; + } + + case SID_OPT_LOCALE_CHANGED: + { + mrSlideSorter.GetController().UpdateAllPages(); + if (pViewShell != nullptr) + pViewShell->UpdatePreview (pViewShell->GetActualPage()); + rRequest.Done(); + break; + } + + case SID_SEARCH_DLG: + // We have to handle the SID_SEARCH_DLG slot explicitly because + // in some cases (when the slide sorter is displayed in the + // center pane) we want to disable the search dialog. Therefore + // we have to handle the execution of that slot as well. + // We try to do that by forwarding the request to the view frame + // of the view shell. + if (pViewShell != nullptr) + pViewShell->GetViewFrame()->ExecuteSlot(rRequest); + break; + + default: + break; + } +} + +void SlotManager::GetAttrState (SfxItemSet& rSet) +{ + // Iterate over all items. + SfxWhichIter aIter (rSet); + sal_uInt16 nWhich = aIter.FirstWhich(); + while (nWhich) + { + sal_uInt16 nSlotId (nWhich); + if (SfxItemPool::IsWhich(nWhich) && mrSlideSorter.GetViewShell()!=nullptr) + nSlotId = mrSlideSorter.GetViewShell()->GetPool().GetSlotId(nWhich); + switch (nSlotId) + { + case SID_PAGES_PER_ROW: + rSet.Put ( + SfxUInt16Item ( + nSlotId, + static_cast(mrSlideSorter.GetView().GetLayouter().GetColumnCount()) + ) + ); + break; + } + nWhich = aIter.NextWhich(); + } +} + +void SlotManager::GetMenuState (SfxItemSet& rSet) +{ + EditMode eEditMode = mrSlideSorter.GetModel().GetEditMode(); + ViewShell* pShell = mrSlideSorter.GetViewShell(); + DrawDocShell* pDocShell = mrSlideSorter.GetModel().GetDocument()->GetDocSh(); + + if (pShell!=nullptr && pShell->GetCurrentFunction().is()) + { + sal_uInt16 nSId = pShell->GetCurrentFunction()->GetSlotID(); + + rSet.Put( SfxBoolItem( nSId, true ) ); + } + rSet.Put( SfxBoolItem( SID_DRAWINGMODE, false ) ); + rSet.Put( SfxBoolItem( SID_SLIDE_SORTER_MODE, true ) ); + rSet.Put( SfxBoolItem( SID_OUTLINE_MODE, false ) ); + rSet.Put( SfxBoolItem( SID_NOTES_MODE, false ) ); + rSet.Put( SfxBoolItem( SID_HANDOUT_MASTER_MODE, false ) ); + + if (pShell!=nullptr && pShell->IsMainViewShell()) + { + rSet.DisableItem(SID_SPELL_DIALOG); + rSet.DisableItem(SID_SEARCH_DLG); + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_EXPAND_PAGE)) + { + bool bDisable = true; + if (eEditMode == EditMode::Page) + { + // At least one of the selected pages has to contain an outline + // presentation objects in order to enable the expand page menu + // entry. + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + SdPage* pPage = aSelectedPages.GetNextElement()->GetPage(); + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Outline); + if (pObj!=nullptr ) + { + if( !pObj->IsEmptyPresObj() ) + { + bDisable = false; + } + else + { + // check if the object is in edit, then if it's temporarily not empty + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + if( pTextObj ) + { + if( pTextObj->CanCreateEditOutlinerParaObject() ) + { + bDisable = false; + } + } + } + } + } + } + + if (bDisable) + rSet.DisableItem (SID_EXPAND_PAGE); + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_SUMMARY_PAGE)) + { + bool bDisable = true; + if (eEditMode == EditMode::Page) + { + // At least one of the selected pages has to contain a title + // presentation objects in order to enable the summary page menu + // entry. + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + SdPage* pPage = aSelectedPages.GetNextElement()->GetPage(); + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Title); + + if (pObj!=nullptr && !pObj->IsEmptyPresObj()) + bDisable = false; + } + } + if (bDisable) + rSet.DisableItem (SID_SUMMARY_PAGE); + } + + // starting of presentation possible? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PRESENTATION ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_REHEARSE_TIMINGS ) ) + { + bool bDisable = true; + model::PageEnumeration aAllPages ( + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrSlideSorter.GetModel())); + while (aAllPages.HasMoreElements()) + { + SdPage* pPage = aAllPages.GetNextElement()->GetPage(); + + if( !pPage->IsExcluded() ) + bDisable = false; + } + if( bDisable || pDocShell->IsPreview()) + { + rSet.DisableItem( SID_PRESENTATION ); + rSet.DisableItem( SID_REHEARSE_TIMINGS ); + } + } + + // Disable the rename slots when there are no or more than one slides/master + // pages selected; disable the duplicate slot when there are no slides + // selected: + if (rSet.GetItemState(SID_RENAMEPAGE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_RENAME_MASTER_PAGE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DUPLICATE_PAGE) == SfxItemState::DEFAULT) + { + int n = mrSlideSorter.GetController().GetPageSelector() + .GetSelectedPageCount(); + if (n != 1) + { + rSet.DisableItem(SID_RENAMEPAGE); + rSet.DisableItem(SID_RENAME_MASTER_PAGE); + } + if (n == 0) + { + rSet.DisableItem(SID_DUPLICATE_PAGE); + } + } + + if (rSet.GetItemState(SID_HIDE_SLIDE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_SHOW_SLIDE) == SfxItemState::DEFAULT) + { + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + const SlideExclusionState eState (GetSlideExclusionState(aSelectedPages)); + switch (eState) + { + case MIXED: + // Show both entries. + break; + + case EXCLUDED: + rSet.DisableItem(SID_HIDE_SLIDE); + break; + + case INCLUDED: + rSet.DisableItem(SID_SHOW_SLIDE); + break; + + case UNDEFINED: + rSet.DisableItem(SID_HIDE_SLIDE); + rSet.DisableItem(SID_SHOW_SLIDE); + break; + } + } + + if (eEditMode == EditMode::MasterPage) + { + // Disable some slots when in master page mode. + rSet.DisableItem(SID_ASSIGN_LAYOUT); + rSet.DisableItem(SID_INSERTPAGE); + + if (rSet.GetItemState(SID_DUPLICATE_PAGE) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_DUPLICATE_PAGE); + } +} + +void SlotManager::GetClipboardState ( SfxItemSet& rSet) +{ + SdTransferable* pTransferClip = SD_MOD()->pTransferClip; + + if (rSet.GetItemState(SID_PASTE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_PASTE_SPECIAL) == SfxItemState::DEFAULT) + { + // no own clipboard data? + if ( !pTransferClip || !pTransferClip->GetDocShell().is() ) + { + rSet.DisableItem(SID_PASTE); + rSet.DisableItem(SID_PASTE_SPECIAL); + } + else + { + SfxObjectShell* pTransferDocShell = pTransferClip->GetDocShell().get(); + + if( !pTransferDocShell || static_cast(pTransferDocShell)->GetDoc()->GetPageCount() <= 1 ) + { + bool bIsPastingSupported (false); + + // No or just one page. Check if there is anything that can be + // pasted via a DrawViewShell. + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase != nullptr) + { + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(pBase->GetMainViewShell())); + if (pDrawViewShell != nullptr) + { + TransferableDataHelper aDataHelper ( + TransferableDataHelper::CreateFromSystemClipboard( + pDrawViewShell->GetActiveWindow())); + if (aDataHelper.GetFormatCount() > 0) + bIsPastingSupported = true; + } + } + + if ( ! bIsPastingSupported) + { + rSet.DisableItem(SID_PASTE); + rSet.DisableItem(SID_PASTE_SPECIAL); + } + } + } + } + + // Cut, copy and paste of master pages is not yet implemented properly + if (rSet.GetItemState(SID_COPY) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_PASTE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_PASTE_SPECIAL) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_CUT) == SfxItemState::DEFAULT) + { + if (mrSlideSorter.GetModel().GetEditMode() == EditMode::MasterPage) + { + if (rSet.GetItemState(SID_CUT) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_CUT); + if (rSet.GetItemState(SID_COPY) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_COPY); + if (rSet.GetItemState(SID_PASTE) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_PASTE); + if (rSet.GetItemState(SID_PASTE_SPECIAL) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_PASTE_SPECIAL); + } + } + + ViewShellBase* pBase = mrSlideSorter.GetViewShellBase(); + if (pBase && pBase->GetObjectShell()->isContentExtractionLocked()) + { + rSet.DisableItem(SID_COPY); + rSet.DisableItem(SID_CUT); + } + + // Cut, copy, and delete page are disabled when there is no selection. + if (!(rSet.GetItemState(SID_CUT) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_COPY) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DELETE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DELETE_PAGE) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DELETE_MASTER_PAGE) == SfxItemState::DEFAULT)) + return; + + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + + // For copy to work we have to have at least one selected page. + if ( ! aSelectedPages.HasMoreElements()) + rSet.DisableItem(SID_COPY); + + bool bDisable = false; + // The operations that lead to the deletion of a page are valid if + // a) there is at least one selected page + // b) deleting the selected pages leaves at least one page in the + // document + // c) selected master pages must not be used by slides. + + // Test a). + if ( ! aSelectedPages.HasMoreElements()) + bDisable = true; + // Test b): Count the number of selected pages. It has to be less + // than the number of all pages. + else if (mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount() + >= mrSlideSorter.GetController().GetPageSelector().GetPageCount()) + bDisable = true; + // Test c): Iterate over the selected pages and look for a master + // page that is used by at least one page. + else while (aSelectedPages.HasMoreElements()) + { + SdPage* pPage = aSelectedPages.GetNextElement()->GetPage(); + int nUseCount (mrSlideSorter.GetModel().GetDocument() + ->GetMasterPageUserCount(pPage)); + if (nUseCount > 0) + { + bDisable = true; + break; + } + } + + if (bDisable) + { + rSet.DisableItem(SID_CUT); + rSet.DisableItem(SID_DELETE_PAGE); + rSet.DisableItem(SID_DELETE_MASTER_PAGE); + } +} + +void SlotManager::GetStatusBarState (SfxItemSet& rSet) +{ + // page view and layout + SdPage* pPage = nullptr; + sal_uInt16 nSelectedPages = mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount(); + + //Set number of slides + if (nSelectedPages > 0) + { + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + OUString aPageStr; + if (pDescriptor) + { + pPage = pDescriptor->GetPage(); + sal_uInt16 nFirstPage = (pPage->GetPageNum()/2) + 1; + sal_Int32 nPageCount = mrSlideSorter.GetModel().GetPageCount(); + sal_Int32 nActivePageCount = static_cast(mrSlideSorter.GetModel().GetDocument()->GetActiveSdPageCount()); + + aPageStr = (nPageCount == nActivePageCount) ? SdResId(STR_SD_PAGE_COUNT) : SdResId(STR_SD_PAGE_COUNT_CUSTOM); + + aPageStr = aPageStr.replaceFirst("%1", OUString::number(nFirstPage)); + aPageStr = aPageStr.replaceFirst("%2", OUString::number(nPageCount)); + if(nPageCount != nActivePageCount) + aPageStr = aPageStr.replaceFirst("%3", OUString::number(nActivePageCount)); + } + rSet.Put( SfxStringItem( SID_STATUS_PAGE, aPageStr ) ); + } + //Set layout + if (nSelectedPages == 1 && pPage != nullptr) + { + SdPage* pFirstPage = pPage; + OUString aLayoutStr = pFirstPage->GetLayoutName(); + sal_Int32 nIndex = aLayoutStr.indexOf( SD_LT_SEPARATOR ); + if( nIndex != -1 ) + aLayoutStr = aLayoutStr.copy(0, nIndex); + rSet.Put( SfxStringItem( SID_STATUS_LAYOUT, aLayoutStr ) ); + } + //Scale value + const Fraction& aUIScale = mrSlideSorter.GetModel().GetDocument()->GetUIScale(); + OUString aString = OUString::number(aUIScale.GetNumerator()) + + ":" + OUString::number(aUIScale.GetDenominator()); + rSet.Put( SfxStringItem( SID_SCALE, aString ) ); +} + +void SlotManager::RenameSlide(const SfxRequest& rRequest) +{ + View* pDrView = &mrSlideSorter.GetView(); + + if ( pDrView->IsTextEdit() ) + { + pDrView->SdrEndTextEdit(); + } + + SdPage* pSelectedPage = nullptr; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + if (aSelectedPages.HasMoreElements()) + pSelectedPage = aSelectedPages.GetNextElement()->GetPage(); + if (pSelectedPage == nullptr) + return; + + // tdf#107183 Set different dialog titles when renaming + // master slides or normal ones + OUString aTitle; + if( rRequest.GetSlot() == SID_RENAME_MASTER_PAGE ) + aTitle = SdResId( STR_TITLE_RENAMEMASTER ); + else if (pDrView->GetDoc().GetDocumentType() == DocumentType::Draw) + aTitle = SdResId( STR_TITLE_RENAMEPAGE ); + else + aTitle = SdResId( STR_TITLE_RENAMESLIDE ); + + OUString aDescr( SdResId( STR_DESC_RENAMESLIDE ) ); + OUString aPageName = pSelectedPage->GetName(); + + if(rRequest.GetArgs()) + { + OUString aName = rRequest.GetArgs()->GetItem(SID_RENAMEPAGE)->GetValue(); + + bool bResult = RenameSlideFromDrawViewShell(pSelectedPage->GetPageNum()/2, aName ); + DBG_ASSERT( bResult, "Couldn't rename slide or page" ); + } + else + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + vcl::Window* pWin = mrSlideSorter.GetContentWindow(); + ScopedVclPtr aNameDlg(pFact->CreateSvxNameDialog( + pWin ? pWin->GetFrameWeld() : nullptr, + aPageName, aDescr)); + OUString aOldName; + aNameDlg->GetName( aOldName ); + aNameDlg->SetText( aTitle ); + aNameDlg->SetCheckNameHdl( LINK( this, SlotManager, RenameSlideHdl ), true ); + aNameDlg->SetCheckNameTooltipHdl( LINK( this, SlotManager, RenameSlideTooltipHdl ) ); + aNameDlg->SetEditHelpId( HID_SD_NAMEDIALOG_PAGE ); + + if( aNameDlg->Execute() == RET_OK ) + { + OUString aNewName; + aNameDlg->GetName( aNewName ); + if (aNewName != aPageName) + { + bool bResult = + RenameSlideFromDrawViewShell( + pSelectedPage->GetPageNum()/2, aNewName ); + DBG_ASSERT( bResult, "Couldn't rename slide or page" ); + } + } + OUString aNewName; + aNameDlg->GetName( aNewName ); + collectUIInformation({{"OldName", aOldName}, {"NewName", aNewName}}, "RENAME"); + aNameDlg.disposeAndClear(); + } + // Tell the slide sorter about the name change (necessary for + // accessibility.) + mrSlideSorter.GetController().PageNameHasChanged( + (pSelectedPage->GetPageNum()-1)/2, aPageName); +} + +IMPL_LINK(SlotManager, RenameSlideHdl, AbstractSvxNameDialog&, rDialog, bool) +{ + OUString aNewName; + rDialog.GetName( aNewName ); + + model::SharedPageDescriptor pDescriptor ( + mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()); + SdPage* pCurrentPage = nullptr; + if (pDescriptor) + pCurrentPage = pDescriptor->GetPage(); + + return (pCurrentPage!=nullptr && aNewName == pCurrentPage->GetName()) + || (mrSlideSorter.GetViewShell() + && mrSlideSorter.GetViewShell()->GetDocSh()->IsNewPageNameValid( aNewName ) ); +} + +IMPL_STATIC_LINK_NOARG(SlotManager, RenameSlideTooltipHdl, AbstractSvxNameDialog&, OUString) +{ + return SdResId(STR_TOOLTIP_RENAME); +} + +bool SlotManager::RenameSlideFromDrawViewShell( sal_uInt16 nPageId, const OUString & rName ) +{ + bool bOutDummy; + SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument(); + if( pDocument->GetPageByName( rName, bOutDummy ) != SDRPAGE_NOTFOUND ) + return false; + + SdPage* pPageToRename = nullptr; + + SfxUndoManager* pManager = pDocument->GetDocSh()->GetUndoManager(); + + if( mrSlideSorter.GetModel().GetEditMode() == EditMode::Page ) + { + model::SharedPageDescriptor pDescriptor ( + mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()); + if (pDescriptor) + pPageToRename = pDescriptor->GetPage(); + + if (pPageToRename != nullptr) + { + // Undo + SdPage* pUndoPage = pPageToRename; + SdrLayerAdmin & rLayerAdmin = pDocument->GetLayerAdmin(); + SdrLayerID nBackground = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID nBgObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = pPageToRename->TRG_GetMasterPageVisibleLayers(); + + // (#67720#) + pManager->AddUndoAction( + std::make_unique( + pDocument, pUndoPage, rName, pUndoPage->GetAutoLayout(), + aVisibleLayers.IsSet( nBackground ), + aVisibleLayers.IsSet( nBgObj ))); + + // rename + pPageToRename->SetName( rName ); + + // also rename notes-page + SdPage* pNotesPage = pDocument->GetSdPage( nPageId, PageKind::Notes ); + if (pNotesPage != nullptr) + pNotesPage->SetName (rName); + } + } + else + { + // rename MasterPage -> rename LayoutTemplate + pPageToRename = pDocument->GetMasterSdPage( nPageId, PageKind::Standard ); + if (pPageToRename != nullptr) + { + const OUString aOldLayoutName( pPageToRename->GetLayoutName() ); + pManager->AddUndoAction( std::make_unique( pDocument, aOldLayoutName, rName ) ); + pDocument->RenameLayoutTemplate( aOldLayoutName, rName ); + } + } + + bool bSuccess = pPageToRename!=nullptr && ( rName == pPageToRename->GetName() ); + + if( bSuccess ) + { + // user edited page names may be changed by the page so update control + // aTabControl.SetPageText( nPageId, rName ); + + // set document to modified state + pDocument->SetChanged(); + + // inform navigator about change + if (mrSlideSorter.GetViewShell() && mrSlideSorter.GetViewShell()->GetViewFrame()) + mrSlideSorter.GetViewShell()->GetViewFrame()->GetBindings().Invalidate( + SID_NAVIGATOR_STATE, true); + } + + return bSuccess; +} + +/** Insert a slide. The insertion position depends on a) the selection and + b) the mouse position when there is no selection. + + When there is a selection then insertion takes place after the last + slide of the selection. For this to work all but the last selected + slide are deselected first. + + Otherwise, when there is no selection but the insertion marker is visible + the slide is inserted at that position. The slide before that marker is + selected first. + + When both the selection and the insertion marker are not visible--can + that happen?--the new slide is inserted after the last slide. +*/ +void SlotManager::InsertSlide (SfxRequest& rRequest) +{ + const sal_Int32 nInsertionIndex (GetInsertionPosition()); + + PageSelector::BroadcastLock aBroadcastLock (mrSlideSorter); + + SdPage* pNewPage = nullptr; + if (mrSlideSorter.GetModel().GetEditMode() == EditMode::Page) + { + SlideSorterViewShell* pShell = dynamic_cast( + mrSlideSorter.GetViewShell()); + if (pShell != nullptr) + { + pNewPage = pShell->CreateOrDuplicatePage ( + rRequest, + PageKind::Standard, + nInsertionIndex>=0 + ? mrSlideSorter.GetModel().GetPageDescriptor(nInsertionIndex)->GetPage() + : nullptr); + } + } + else + { + // Use the API to create a new page. + SdDrawDocument* pDocument = mrSlideSorter.GetModel().GetDocument(); + Reference xMasterPagesSupplier ( + pDocument->getUnoModel(), UNO_QUERY); + if (xMasterPagesSupplier.is()) + { + Reference xMasterPages ( + xMasterPagesSupplier->getMasterPages()); + if (xMasterPages.is()) + { + xMasterPages->insertNewByIndex (nInsertionIndex+1); + + // Create shapes for the default layout. + pNewPage = pDocument->GetMasterSdPage( + static_cast(nInsertionIndex+1), PageKind::Standard); + pNewPage->CreateTitleAndLayout (true,true); + } + } + } + if (pNewPage == nullptr) + return; + + // When a new page has been inserted then select it, make it the + // current page, and focus it. + view::SlideSorterView::DrawLock aDrawLock (mrSlideSorter); + PageSelector::UpdateLock aUpdateLock (mrSlideSorter); + mrSlideSorter.GetController().GetPageSelector().DeselectAllPages(); + mrSlideSorter.GetController().GetPageSelector().SelectPage(pNewPage); + collectUIInformation({{"POS", OUString::number(nInsertionIndex + 2)}}, "Insert_New_Page_or_Slide"); +} + +void SlotManager::DuplicateSelectedSlides (SfxRequest& rRequest) +{ + // Create a list of the pages that are to be duplicated. The process of + // duplication alters the selection. + sal_Int32 nInsertPosition (0); + ::std::vector aPagesToDuplicate; + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + if (pDescriptor && pDescriptor->GetPage()) + { + aPagesToDuplicate.push_back(pDescriptor->GetPage()); + nInsertPosition = pDescriptor->GetPage()->GetPageNum()+2; + } + } + + // Duplicate the pages in aPagesToDuplicate and collect the newly + // created pages in aPagesToSelect. + const bool bUndo (aPagesToDuplicate.size()>1 && mrSlideSorter.GetView().IsUndoEnabled()); + if (bUndo) + mrSlideSorter.GetView().BegUndo(SdResId(STR_INSERTPAGE)); + + ::std::vector aPagesToSelect; + for(const auto& rpPage : aPagesToDuplicate) + { + aPagesToSelect.push_back( + mrSlideSorter.GetViewShell()->CreateOrDuplicatePage( + rRequest, PageKind::Standard, rpPage, nInsertPosition)); + nInsertPosition += 2; + } + aPagesToDuplicate.clear(); + + if (bUndo) + mrSlideSorter.GetView().EndUndo(); + + // Set the selection to the pages in aPagesToSelect. + PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + rSelector.DeselectAllPages(); + for (auto const& it: aPagesToSelect) + { + rSelector.SelectPage(it); + } + + collectUIInformation({{"POS", OUString::number(nInsertPosition + 2)}}, "Duplicate"); +} + +void SlotManager::ChangeSlideExclusionState ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bExcludeSlide) +{ + if (rpDescriptor) + { + mrSlideSorter.GetView().SetState( + rpDescriptor, + model::PageDescriptor::ST_Excluded, + bExcludeSlide); + } + else + { + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + mrSlideSorter.GetView().SetState( + pDescriptor, + model::PageDescriptor::ST_Excluded, + bExcludeSlide); + } + } + + SfxBindings& rBindings (mrSlideSorter.GetViewShell()->GetViewFrame()->GetBindings()); + rBindings.Invalidate(SID_PRESENTATION); + rBindings.Invalidate(SID_REHEARSE_TIMINGS); + rBindings.Invalidate(SID_HIDE_SLIDE); + rBindings.Invalidate(SID_SHOW_SLIDE); + mrSlideSorter.GetModel().GetDocument()->SetChanged(); +} + +sal_Int32 SlotManager::GetInsertionPosition() const +{ + PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + + // The insertion indicator is preferred. After all the user explicitly + // used it to define the insertion position. + if (mrSlideSorter.GetController().GetInsertionIndicatorHandler()->IsActive()) + { + // Select the page before the insertion indicator. + return mrSlideSorter.GetController().GetInsertionIndicatorHandler()->GetInsertionPageIndex() + - 1; + } + + // Is there a stored insertion position? + else if (mrSlideSorter.GetController().GetSelectionManager()->GetInsertionPosition() >= 0) + { + return mrSlideSorter.GetController().GetSelectionManager()->GetInsertionPosition() - 1; + } + + // Use the index of the last selected slide. + else if (rSelector.GetSelectedPageCount() > 0) + { + for (int nIndex=rSelector.GetPageCount()-1; nIndex>=0; --nIndex) + if (rSelector.IsPageSelected(nIndex)) + return nIndex; + + // We should never get here. + OSL_ASSERT(false); + return rSelector.GetPageCount() - 1; + } + + // Select the last page when there is at least one page. + else if (rSelector.GetPageCount() > 0) + { + return rSelector.GetPageCount() - 1; + } + + // Hope for the best that CreateOrDuplicatePage() can cope with an empty + // selection. + else + { + // We should never get here because there has to be at least one page. + OSL_ASSERT(false); + return -1; + } +} + +void SlotManager::NotifyEditModeChange() +{ + SfxBindings& rBindings (mrSlideSorter.GetViewShell()->GetViewFrame()->GetBindings()); + rBindings.Invalidate(SID_PRESENTATION); + rBindings.Invalidate(SID_INSERTPAGE); + rBindings.Invalidate(SID_DUPLICATE_PAGE); +} + +namespace { + +SlideExclusionState GetSlideExclusionState (model::PageEnumeration& rPageSet) +{ + SlideExclusionState eState (UNDEFINED); + + // Get toggle state of the selected pages. + while (rPageSet.HasMoreElements() && eState!=MIXED) + { + const bool bState = rPageSet.GetNextElement()->GetPage()->IsExcluded(); + switch (eState) + { + case UNDEFINED: + // Use the first selected page to set the initial value. + eState = bState ? EXCLUDED : INCLUDED; + break; + + case EXCLUDED: + // The pages before where all not part of the show, + // this one is. + if ( ! bState) + eState = MIXED; + break; + + case INCLUDED: + // The pages before where all part of the show, + // this one is not. + if (bState) + eState = MIXED; + break; + + default: + // No need to change anything. + break; + } + } + + return eState; +} + +} // end of anonymous namespace + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsTransferableData.cxx b/sd/source/ui/slidesorter/controller/SlsTransferableData.cxx new file mode 100644 index 000000000..f4b89a5ab --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsTransferableData.cxx @@ -0,0 +1,86 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +namespace sd::slidesorter::controller { + +rtl::Reference TransferableData::CreateTransferable ( + SdDrawDocument* pSrcDoc, + SlideSorterViewShell* pViewShell, + ::std::vector&& rRepresentatives) +{ + rtl::Reference pTransferable = new SdTransferable (pSrcDoc, nullptr, false/*bInitOnGetData*/); + auto pData = std::make_shared(pViewShell, std::move(rRepresentatives)); + pTransferable->AddUserData(pData); + return pTransferable; +} + +std::shared_ptr TransferableData::GetFromTransferable (const SdTransferable* pTransferable) +{ + if (pTransferable) + { + for (sal_Int32 nIndex=0,nCount=pTransferable->GetUserDataCount(); nIndex xData = + std::dynamic_pointer_cast(pTransferable->GetUserData(nIndex)); + if (xData) + return xData; + } + } + return std::shared_ptr(); +} + +TransferableData::TransferableData ( + SlideSorterViewShell* pViewShell, + ::std::vector&& rRepresentatives) + : mpViewShell(pViewShell), + maRepresentatives(std::move(rRepresentatives)) +{ + if (mpViewShell != nullptr) + StartListening(*mpViewShell); +} + +TransferableData::~TransferableData() +{ + if (mpViewShell != nullptr) + EndListening(*mpViewShell); +} + +void TransferableData::Notify (SfxBroadcaster&, const SfxHint& rHint) +{ + if (mpViewShell) + { + if (rHint.GetId() == SfxHintId::Dying) + { + // This hint may come either from the ViewShell or from the + // document (registered by SdTransferable). We do not know + // which but both are sufficient to disconnect from the + // ViewShell. + EndListening(*mpViewShell); + mpViewShell = nullptr; + } + } +} + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/controller/SlsVisibleAreaManager.cxx b/sd/source/ui/slidesorter/controller/SlsVisibleAreaManager.cxx new file mode 100644 index 000000000..6f85f362d --- /dev/null +++ b/sd/source/ui/slidesorter/controller/SlsVisibleAreaManager.cxx @@ -0,0 +1,234 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd::slidesorter::controller { + +namespace { + class VisibleAreaScroller + { + public: + VisibleAreaScroller ( + SlideSorter& rSlideSorter, + const Point& rStart, + const Point& rEnd); + void operator() (const double nValue); + private: + SlideSorter& mrSlideSorter; + Point maStart; + const Point maEnd; + const ::std::function maAccelerationFunction; + }; + +} // end of anonymous namespace + +VisibleAreaManager::VisibleAreaManager (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mbIsCurrentSlideTrackingActive(true), + mnDisableCount(0) +{ +} + +VisibleAreaManager::~VisibleAreaManager() +{ +} + +void VisibleAreaManager::ActivateCurrentSlideTracking() +{ + mbIsCurrentSlideTrackingActive = true; +} + +void VisibleAreaManager::DeactivateCurrentSlideTracking() +{ + mbIsCurrentSlideTrackingActive = false; +} + +void VisibleAreaManager::RequestVisible ( + const model::SharedPageDescriptor& rpDescriptor, + const bool bForce) +{ + if (!rpDescriptor) + return; + + if (mnDisableCount == 0) + { + maVisibleRequests.push_back( + mrSlideSorter.GetView().GetLayouter().GetPageObjectBox( + rpDescriptor->GetPageIndex(), + true)); + } + if (bForce && ! mbIsCurrentSlideTrackingActive) + ActivateCurrentSlideTracking(); + MakeVisible(); +} + +void VisibleAreaManager::RequestCurrentSlideVisible() +{ + if (mbIsCurrentSlideTrackingActive && mnDisableCount==0) + RequestVisible( + mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()); +} + +void VisibleAreaManager::MakeVisible() +{ + if (maVisibleRequests.empty()) + return; + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if ( ! pWindow) + return; + const Point aCurrentTopLeft (pWindow->PixelToLogic(Point(0,0))); + + const ::std::optional aNewVisibleTopLeft (GetRequestedTopLeft()); + maVisibleRequests.clear(); + if ( ! aNewVisibleTopLeft) + return; + + maRequestedVisibleTopLeft = *aNewVisibleTopLeft; + VisibleAreaScroller aAnimation( + mrSlideSorter, + aCurrentTopLeft, + maRequestedVisibleTopLeft); + // Execute the animation at its final value. + aAnimation(1.0); +} + +::std::optional VisibleAreaManager::GetRequestedTopLeft() const +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if ( ! pWindow) + return ::std::optional(); + + // Get the currently visible area and the model area. + const ::tools::Rectangle aVisibleArea (pWindow->PixelToLogic( + ::tools::Rectangle( + Point(0,0), + pWindow->GetOutputSizePixel()))); + const ::tools::Rectangle aModelArea (mrSlideSorter.GetView().GetModelArea()); + + sal_Int32 nVisibleTop (aVisibleArea.Top()); + const sal_Int32 nVisibleWidth (aVisibleArea.GetWidth()); + sal_Int32 nVisibleLeft (aVisibleArea.Left()); + const sal_Int32 nVisibleHeight (aVisibleArea.GetHeight()); + + // Find the longest run of boxes whose union fits into the visible area. + for (const auto& rBox : maVisibleRequests) + { + if (nVisibleTop+nVisibleHeight <= rBox.Bottom()) + nVisibleTop = rBox.Bottom()-nVisibleHeight; + if (nVisibleTop > rBox.Top()) + nVisibleTop = rBox.Top(); + + if (nVisibleLeft+nVisibleWidth <= rBox.Right()) + nVisibleLeft = rBox.Right()-nVisibleWidth; + if (nVisibleLeft > rBox.Left()) + nVisibleLeft = rBox.Left(); + + // Make sure the visible area does not move outside the model area. + if (nVisibleTop + nVisibleHeight > aModelArea.Bottom()) + nVisibleTop = aModelArea.Bottom() - nVisibleHeight; + if (nVisibleTop < aModelArea.Top()) + nVisibleTop = aModelArea.Top(); + + if (nVisibleLeft + nVisibleWidth > aModelArea.Right()) + nVisibleLeft = aModelArea.Right() - nVisibleWidth; + if (nVisibleLeft < aModelArea.Left()) + nVisibleLeft = aModelArea.Left(); + } + + const Point aRequestedTopLeft (nVisibleLeft, nVisibleTop); + if (aRequestedTopLeft == aVisibleArea.TopLeft()) + return ::std::optional(); + else + return ::std::optional(aRequestedTopLeft); +} + +//===== VisibleAreaManager::TemporaryDisabler ================================= + +VisibleAreaManager::TemporaryDisabler::TemporaryDisabler (SlideSorter const & rSlideSorter) + : mrVisibleAreaManager(rSlideSorter.GetController().GetVisibleAreaManager()) +{ + ++mrVisibleAreaManager.mnDisableCount; +} + +VisibleAreaManager::TemporaryDisabler::~TemporaryDisabler() +{ + --mrVisibleAreaManager.mnDisableCount; +} + +//===== VerticalVisibleAreaScroller =========================================== + +namespace { + +const sal_Int32 gnMaxScrollDistance = 300; + +VisibleAreaScroller::VisibleAreaScroller ( + SlideSorter& rSlideSorter, + const Point& rStart, + const Point& rEnd) + : mrSlideSorter(rSlideSorter), + maStart(rStart), + maEnd(rEnd), + maAccelerationFunction( + controller::AnimationParametricFunction( + controller::AnimationBezierFunction (0.1,0.6))) +{ + // When the distance to scroll is larger than a threshold then first + // jump to within this distance of the final value and start the + // animation from there. + if (std::abs(rStart.X()-rEnd.X()) > gnMaxScrollDistance) + { + if (rStart.X() < rEnd.X()) + maStart.setX( rEnd.X()-gnMaxScrollDistance ); + else + maStart.setX( rEnd.X()+gnMaxScrollDistance ); + } + if (std::abs(rStart.Y()-rEnd.Y()) > gnMaxScrollDistance) + { + if (rStart.Y() < rEnd.Y()) + maStart.setY( rEnd.Y()-gnMaxScrollDistance ); + else + maStart.setY( rEnd.Y()+gnMaxScrollDistance ); + } +} + +void VisibleAreaScroller::operator() (const double nTime) +{ + const double nLocalTime (maAccelerationFunction(nTime)); + mrSlideSorter.GetController().GetScrollBarManager().SetTopLeft( + Point( + sal_Int32(0.5 + maStart.X() * (1.0 - nLocalTime) + maEnd.X() * nLocalTime), + sal_Int32 (0.5 + maStart.Y() * (1.0 - nLocalTime) + maEnd.Y() * nLocalTime))); +} + +} // end of anonymous namespace + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/cache/SlsCacheContext.hxx b/sd/source/ui/slidesorter/inc/cache/SlsCacheContext.hxx new file mode 100644 index 000000000..12993fdbb --- /dev/null +++ b/sd/source/ui/slidesorter/inc/cache/SlsCacheContext.hxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace com::sun::star::uno +{ +class XInterface; +} + +class SdrPage; + +namespace sd::slidesorter::cache +{ +typedef const SdrPage* CacheKey; + +/** This interface allows the individualisation of different instances of + the PreviewCache. +*/ +class CacheContext +{ +public: + virtual ~CacheContext() {} + + /** This method is called when the asynchronous creation of a preview + has been finished. + @param aKey + The key of the page for which the preview has been created. + */ + virtual void NotifyPreviewCreation(CacheKey aKey) = 0; + + /** Called to determine whether the system is idle and a preview can be + created without annoying the user. + */ + virtual bool IsIdle() = 0; + + /** This method is used to determine whether a page is currently visible + or not. It is called when the cache becomes too large and some + previews have to be released or scaled down. + */ + virtual bool IsVisible(CacheKey aKey) = 0; + + /** Return the page associated with the given key. Note that different + keys may map to a single page (this may be the case with custom + slide shows.) + */ + virtual const SdrPage* GetPage(CacheKey aKey) = 0; + + /** This method is used when the request queue is filled. It asks for + the list of visible entries and maybe for the list of not visible + entries and creates preview creation requests for them. + @param bVisible + When this is then the implementation can decide whether + to allow rendering of previews that are not visible (ahead of + time). When not then return an empty pointer or an empty vector. + */ + virtual std::shared_ptr> GetEntryList(bool bVisible) = 0; + + /** Return the priority that defines the order in which previews are + created for different keys/pages. Typically the visible pages come + first, then top-down, left-to-right. + */ + virtual sal_Int32 GetPriority(CacheKey aKey) = 0; + + /** Return the model to which the pages belong for which the called + cache manages the previews. Different caches that belong to the + same model but have different preview sizes may access previews of + each other in order to create fast previews of the previews. + */ + virtual css::uno::Reference GetModel() = 0; +}; + +typedef std::shared_ptr SharedCacheContext; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/cache/SlsPageCache.hxx b/sd/source/ui/slidesorter/inc/cache/SlsPageCache.hxx new file mode 100644 index 000000000..4fb596195 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/cache/SlsPageCache.hxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class Size; + +namespace sd::slidesorter::cache +{ +class GenericPageCache; + +/** The page cache is responsible for the creation and storage of preview + bitmaps of pages that are shown by the slide sorter. + +

      Bitmaps for previews and a cache are used to speed up the display + (painting) of the slide sorter. But, of course, we have to limit this + time-space-tradeoff by limiting the amount of space that can be use to + store bitmaps.

      + +

      There are several strategies employed by this class to shorten the + perceived time that is used to paint the slide sorter: +

        +
      • Rendering pages ahead of time. Additionally to rendering the + visible slides we try to render part or all of the slides that are not + (yet) visible. This, of course, makes sense only when the computer is + otherwise idle while doing that.
      • +
      • When the size of the slides on the screen changes we mark the + bitmaps as needing an update but use them while the new bitmap in the + correct size is not available.
      • +
      • Give the UI the chance to handle user events between the rendering + of differe slides.
      • +
      • Limit the amount of space that may be used for storing preview + bitmaps and throw.
      • +

        + +

        There is another somewhat similar methods for requesting new previews: + GetPreviewBitmap() schedules a re-rendering (when necessary) and + returns the preview what is currently available, either as a preview of + the preview or, when nothing has changed since the last call, as the + final thing. +

        +*/ +class PageCache +{ +public: + /** The page cache is created with a reference to the slide sorter so + that it has access to both the view and the model and so can fill + itself with requests for all or just the visible pages. + + It is the task of the PageCacheManager to create new objects of this + class. + */ + PageCache(const Size& rPreviewSize, const bool bDoSuperSampling, + const SharedCacheContext& rpCacheContext); + + ~PageCache(); + + void ChangeSize(const Size& rPreviewSize, const bool bDoSuperSampling); + + /** Request a preview bitmap for the specified page object in the + specified size. The returned bitmap may be a preview of the + preview, i.e. either a scaled (up or down) version of a previous + preview (of the wrong size) or an empty bitmap. In this case a + request for the generation of a new preview is created and inserted + into the request queue. When the preview is available in the right + size the page shape will be told to paint itself again. When it + then calls this method again if receives the correctly sized preview + bitmap. + @param rRequestData + This data is used to determine the preview. + @param bResize + When then when the available bitmap has not the + requested size, it is scaled before it is returned. When + then the bitmap is returned in the wrong size and it is + the task of the caller to scale it. + @return + Returns a bitmap that is either empty, contains a scaled (up or + down) version or is the requested bitmap. + */ + BitmapEx GetPreviewBitmap(const CacheKey aKey, const bool bResize); + + BitmapEx GetMarkedPreviewBitmap(const CacheKey aKey); + void SetMarkedPreviewBitmap(const CacheKey aKey, const BitmapEx& rBitmap); + + /** When the requested preview bitmap does not yet exist or is not + up-to-date then the rendering of one is scheduled. Otherwise this + method does nothing. + */ + void RequestPreviewBitmap(const CacheKey aKey); + + /** Tell the cache that the bitmap associated with the given request + data is not up-to-date anymore. This will invalidate all previews + in other caches that represent the same page as well. + A new preview is requested and will lead + eventually to a repaint of the associated page object. + */ + void InvalidatePreviewBitmap(const CacheKey aKey); + + /** Call this method when all preview bitmaps have to be generated anew. + This is the case when the size of the page objects on the screen has + changed or when the model has changed. + */ + void InvalidateCache(); + + /** With the precious flag you can control whether a bitmap can be + removed or reduced in size to make room for other bitmaps or is so + precious that it will not touched. A typical use is to set the + precious flag for exactly the visible pages. + */ + void SetPreciousFlag(const CacheKey aKey, const bool bIsPrecious); + + void Pause(); + void Resume(); + +private: + std::unique_ptr mpImplementation; +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/cache/SlsPageCacheManager.hxx b/sd/source/ui/slidesorter/inc/cache/SlsPageCacheManager.hxx new file mode 100644 index 000000000..eaddea5b2 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/cache/SlsPageCacheManager.hxx @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +class Size; +class SdrPage; + +namespace sd::slidesorter::cache { + +class BitmapCache; + +/** Provide and manage the preview bitmap caches for all slide sorter + instances. There is one cache per active slide sorter plus a small + number of caches that are no longer in use. The later are kept to speed + up the switching between views. +*/ +class PageCacheManager +{ +public: + typedef std::vector< std::pair > > BestFittingPageCaches; + typedef css::uno::Reference DocumentKey; + + /** Return the one instance of the PageCacheManager class. + */ + static std::shared_ptr Instance(); + + /** Look up the cache for the given model in which the previews have the + specified size. If no such cache exists, then one is created. When + a new BitmapCache is created its Recycle() method is called with a + sorted list of existing caches from which the new one initialize its + previews. + @return + The returned cache lives as long as somebody keeps a shared + pointer and the ReleaseCache() method has not been called. + */ + std::shared_ptr GetCache ( + const DocumentKey& pDocument, + const Size& rPreviewSize); + + /** Tell the cache manager to release its own reference to the specified + cache. After that the cache will live as long as the caller (and + maybe others) holds its reference. + */ + void ReleaseCache (const std::shared_ptr& rpCache); + + /** This is an information to the cache manager that the size of preview + bitmaps in the specified cache has changed. + + */ + std::shared_ptr ChangeSize ( + const std::shared_ptr& rpCache, + const Size& rOldPreviewSize, + const Size& rNewPreviewSize); + + /** Invalidate the preview bitmap for one slide that belongs to the + specified document. The bitmaps for this slide in all caches are + marked as out-of-date and will be re-created when they are requested + the next time. + */ + bool InvalidatePreviewBitmap ( + const DocumentKey& pDocument, + const SdrPage* pPage); + + /** Invalidate the preview bitmaps for all slides that belong to the + specified document. This is necessary after model changes that + affect e.g. page number fields. + */ + void InvalidateAllPreviewBitmaps (const DocumentKey& pDocument); + + /** Invalidate all the caches that are currently in use and destroy + those that are not. This is used for example when the high contrast + mode is turned on or off. + */ + void InvalidateAllCaches(); + + /** Call this method when a page has been deleted and its preview + is not needed anymore. + */ + void ReleasePreviewBitmap (const SdrPage* pPage); + +private: + /** Singleton instance of the cache manager. Note that this is a weak + pointer. The (implementation class of) ViewShellBase holds a + shared_ptr so that the cache manager has the same life time as the + ViewShellBase. + */ + static std::weak_ptr mpInstance; + + /// List of active caches. + class PageCacheContainer; + std::unique_ptr mpPageCaches; + + /// List of inactive, recently used caches. + class RecentlyUsedPageCaches; + std::unique_ptr mpRecentlyUsedPageCaches; + + /** The maximal number of recently used caches that are kept alive after + they have become inactive, i.e. after they are not used anymore by a + slide sorter. + */ + static const sal_uInt32 mnMaximalRecentlyCacheCount = 2; + + PageCacheManager(); + ~PageCacheManager(); + + class Deleter; + friend class Deleter; + + std::shared_ptr GetRecentlyUsedCache( + const DocumentKey& pDocument, + const Size& rSize); + + /** Add the given cache to the list of recently used caches for the + document. There is one such list per document. Each least has at + most mnMaximalRecentlyCacheCount members. + */ + void PutRecentlyUsedCache( + DocumentKey const & pDocument, + const Size& rPreviewSize, + const std::shared_ptr& rpCache); + + /** This method is used internally to initialize a newly created + BitmapCache with already existing previews. + */ + void Recycle ( + const std::shared_ptr& rpCache, + const DocumentKey& pDocument, + const Size& rPreviewSize); +}; + +} // end of namespace ::sd::slidesorter::cache + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlideSorterController.hxx b/sd/source/ui/slidesorter/inc/controller/SlideSorterController.hxx new file mode 100644 index 000000000..10c2aa13e --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlideSorterController.hxx @@ -0,0 +1,327 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +namespace com::sun::star::container +{ +class XIndexAccess; +} +namespace com::sun::star::uno +{ +template class Reference; +} +namespace sd +{ +class FuPoor; +} +namespace sd +{ +class Window; +} +namespace vcl +{ +class Window; +} + +namespace sd::slidesorter +{ +class SlideSorter; +} +namespace sd::slidesorter::view +{ +class SlideSorterView; +} +namespace sd::slidesorter::model +{ +class SlideSorterModel; +} + +class CommandEvent; +class SdPage; +class SfxItemSet; +class SfxRequest; +class VclSimpleEvent; +class VclWindowEvent; + +namespace sd::slidesorter::controller +{ +class Animator; +class Clipboard; +class CurrentSlideManager; +class FocusManager; +class InsertionIndicatorHandler; +class Listener; +class PageSelector; +class ScrollBarManager; +class SelectionFunction; +class SelectionManager; +class SlotManager; +class VisibleAreaManager; + +class SlideSorterController final +{ +public: + /** Create a new controller for the slide sorter. + @param pParentWindow + The window that contains the controls of the new + controller. + */ + SlideSorterController(SlideSorter& rSlideSorter); + + /** Late initialization. Call this method once a new object has been + created. + */ + void Init(); + + ~SlideSorterController(); + + void Dispose(); + + /** Place and size the scroll bars and the browser window so that the + given rectangle is filled. + */ + void Resize(const ::tools::Rectangle& rAvailableSpace); + + /** Determine which of the UI elements--the scroll bars, the scroll bar + filler, the actual slide sorter view--are visible and place them in + the area last passed to Resize(). + @param bForce + When is given ( is the default) then the content + window and with it the SlideSorterView is resized event when its + size does not change (the size does change when the visibility + of scroll bars changes.) + */ + void Rearrange(bool bForce); + + /** Return the descriptor of the page that is rendered under the + given position. This takes the IsOnlyPreviewTriggersMouseOver + property into account. + @return + Returns a pointer to a page descriptor instead of a + reference because when no page is found at the position + then NULL is returned to indicate this. + */ + model::SharedPageDescriptor GetPageAt(const Point& rPixelPosition); + + // Exported for unit test + SD_DLLPUBLIC PageSelector& GetPageSelector(); + FocusManager& GetFocusManager(); + // Exported for unit test + SD_DLLPUBLIC controller::Clipboard& GetClipboard(); + + /** Return the object that manages the scroll bars. + */ + ScrollBarManager& GetScrollBarManager(); + + std::shared_ptr const& GetCurrentSlideManager() const; + std::shared_ptr const& GetSlotManager() const; + std::shared_ptr const& GetSelectionManager() const; + std::shared_ptr const& GetInsertionIndicatorHandler() const; + + /** This method forwards the call to the SlideSorterView and executes + pending operations like moving selected pages into the visible area. + */ + void Paint(const ::tools::Rectangle& rRect, vcl::Window* pWin); + + void FuTemporary(SfxRequest& rRequest); + void FuPermanent(SfxRequest& rRequest); + void FuSupport(SfxRequest& rRequest); + bool Command(const CommandEvent& rEvent, ::sd::Window* pWindow); + + void GetCtrlState(SfxItemSet& rSet); + void GetStatusBarState(SfxItemSet& rSet); + + void ExecCtrl(SfxRequest& rRequest); + void GetAttrState(SfxItemSet& rSet); + + /** Create an object of this inner class to prevent updates due to model + changes. + */ + class ModelChangeLock + { + public: + ModelChangeLock(SlideSorterController& rController); + ~ModelChangeLock() COVERITY_NOEXCEPT_FALSE; + void Release(); + + private: + SlideSorterController* mpController; + }; + friend class ModelChangeLock; + + /** Handle a change of the model, that is, handle the removal and + insertion of whole pages or a change of the edit mode. + + This method is a convenience function that simply calls + PreModelChange() and then PostModelChange(). + */ + void HandleModelChange(); + + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + DECL_LINK(ApplicationEventHandler, VclSimpleEvent&, void); + + /** Update the display of all pages. This involves a redraw and + releasing previews and caches. + */ + void UpdateAllPages(); + + /** This factory method creates a selection function. + */ + rtl::Reference CreateSelectionFunction(SfxRequest& rRequest); + + /** When the current function of the view shell is the slide sorter + selection function then return a reference to it. Otherwise return + an empty reference. + */ + ::rtl::Reference GetCurrentSelectionFunction() const; + + /** Prepare for a change of the edit mode. Depending on the current + edit mode we may save the selection so that it can be restored when + later changing back to the current edit mode. + */ + void PrepareEditModeChange(); + + /** Set a new edit mode and return whether the edit mode really + has been changed. For proper saving and restoring of the selection + this method should be called between calls to + PrepareEditModeChange() and FinishEditModeChange(). + */ + void ChangeEditMode(EditMode eEditMode); + + /** Finish the change of the edit mode. Here we may select a page or + restore a previously saved selection. + */ + void FinishEditModeChange(); + + /** Call this method when the name of one of the pages has changed. + This is then notified to the accessibility object, when that exists. + @param nPageIndex + The index of the page whose name has been changed. + @param rsOldName + The old name of the page. The new name can be taken from the + page object. + */ + void PageNameHasChanged(int nPageIndex, const OUString& rsOldName); + + /** Provide the set of pages to be displayed in the slide sorter. The + GetDocumentSlides() method can be found only in the SlideSorterModel. + */ + void SetDocumentSlides(const css::uno::Reference& rxSlides); + + /** Return an Animator object. + */ + const std::shared_ptr& GetAnimator() const { return mpAnimator; } + + VisibleAreaManager& GetVisibleAreaManager() const; + + void CheckForMasterPageAssignment(); + void CheckForSlideTransitionAssignment(); + +private: + SlideSorter& mrSlideSorter; + model::SlideSorterModel& mrModel; + view::SlideSorterView& mrView; + std::unique_ptr mpPageSelector; + std::unique_ptr mpFocusManager; + std::shared_ptr mpSlotManager; + std::unique_ptr mpScrollBarManager; + mutable std::shared_ptr mpCurrentSlideManager; + std::shared_ptr mpSelectionManager; + std::unique_ptr mpClipboard; + std::shared_ptr mpInsertionIndicatorHandler; + std::shared_ptr mpAnimator; + std::unique_ptr mpVisibleAreaManager; + + // The listener listens to UNO events and thus is a UNO object. + ::rtl::Reference mpListener; + + int mnModelChangeLockCount; + bool mbIsForcedRearrangePending; + bool mbContextMenuOpen; + + bool mbPostModelChangePending; + + /** This array stores the indices of the selected page descriptors at + the time when the edit mode is switched to EditMode::MasterPage. With this + we can restore the selection when switching back to EditMode::Page mode. + */ + ::std::vector maSelectionBeforeSwitch; + /// The current page before the edit mode is switched to EditMode::MasterPage. + int mnCurrentPageBeforeSwitch; + + /** The master page to select after the edit mode is changed. This + member is used to pass the pointer from PrepareEditModeChange() to + FinishEditModeChange(). + */ + SdPage* mpEditModeChangeMasterPage; + + /** This rectangle in the parent window encloses scroll bars and slide + sorter window. It is set when Resize() is called. + */ + ::tools::Rectangle maTotalWindowArea; + + /** This counter is used to avoid processing of reentrant calls to + Paint(). + */ + sal_Int32 mnPaintEntranceCount; + + /** Prepare for several model changes, i.e. prevent time-consuming and + non-critical operations like repaints until UnlockModelChange() is + called. Critical operations like releasing references to pages that + do not exist anymore are executed. + */ + void LockModelChange(); + + /** Further calls to HandleModelChange() will result in a full featured + update of model, view, and controller. When HandleModelChange() has + been called since the last LockModelChange() then this is done right + away to bring the view up-to-date. + */ + void UnlockModelChange(); + + /** Prepare for a model change. This method does all the things that + need to be done _before_ the model changes, e.g. because they need + access to the model data before the change. + */ + void PreModelChange(); + + /** Complete a model change. This includes the recreation of data + structures that depend on the model and the request for a repaint to + show the changes. + */ + void PostModelChange(); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsAnimationFunction.hxx b/sd/source/ui/slidesorter/inc/controller/SlsAnimationFunction.hxx new file mode 100644 index 000000000..b4847de1a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsAnimationFunction.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + + +namespace sd::slidesorter::controller { + +class AnimationBezierFunction +{ +public: + /** Create a cubic bezier curve whose start and end points are given + implicitly as P0=(0,0) and P3=(1,1). The second control point is + implicitly given as P2=(1-nY1,1-nX1). + */ + AnimationBezierFunction ( + const double nX1, + const double nY1); + + ::basegfx::B2DPoint operator() (const double nT); + +private: + const double mnX1; + const double mnY1; + const double mnX2; + const double mnY2; + + static double EvaluateComponent ( + const double nT, + const double nV1, + const double nV2); +}; + +/** Turn a parametric function into one whose y-Values depend on its + x-Values. Note a lot of interpolation takes place. The resulting + accuracy should be good enough for the purpose of acceleration + function for animations. +*/ +class AnimationParametricFunction +{ +public: + typedef ::std::function ParametricFunction; + AnimationParametricFunction (const ParametricFunction& rFunction); + + double operator() (const double nX); + +private: + /** y-Values of the parametric function given to the constructor + evaluated (and interpolated) for evenly spaced x-Values. + */ + ::std::vector maY; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsAnimator.hxx b/sd/source/ui/slidesorter/inc/controller/SlsAnimator.hxx new file mode 100644 index 000000000..8c9ec9e81 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsAnimator.hxx @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::controller { + +/** Experimental class for simple eye candy animations. +*/ +class Animator +{ +public: + /** In some circumstances we have to avoid animation and jump to the + final animation state immediately. Use this enum instead of a bool + to be more expressive. + */ + enum AnimationMode { AM_Animated, AM_Immediate }; + + explicit Animator (SlideSorter& rSlideSorter); + ~Animator(); + Animator(const Animator&) = delete; + Animator& operator=(const Animator&) = delete; + + /** When disposed the animator will stop its work immediately and not + process any timer events anymore. + */ + void Dispose(); + + /** An animation object is called with values between 0 and 1 as single + argument to its operator() method. + */ + typedef ::std::function AnimationFunctor; + typedef ::std::function FinishFunctor; + + typedef sal_Int32 AnimationId; + static const AnimationId NotAnAnimationId = -1; + + /** Schedule a new animation for execution. The () operator of that + animation will be called with increasing values between 0 and 1 for + the specified duration. + @param rAnimation + The animation operation. + */ + AnimationId AddAnimation ( + const AnimationFunctor& rAnimation, + const FinishFunctor& rFinishFunctor); + + /** Abort and remove an animation. In order to reduce the bookkeeping + on the caller side, it is OK to call this method with an animation + function that is not currently being animated. Such a call is + silently ignored. + */ + void RemoveAnimation (const AnimationId nAnimationId); + + /** A typical use case for this method is the temporary shutdown of the + slidesorter when the slide sorter bar is put into a cache due to a + change of the edit mode. + */ + void RemoveAllAnimations(); + +private: + SlideSorter& mrSlideSorter; + Idle maIdle; + bool mbIsDisposed; + class Animation; + typedef ::std::vector > AnimationList; + AnimationList maAnimations; + ::canvas::tools::ElapsedTime maElapsedTime; + + std::unique_ptr> mpDrawLock; + + AnimationId mnNextAnimationId; + + DECL_LINK(TimeoutHandler, Timer *, void); + + /** Execute one step of every active animation. + @param nTime + Time measured in milliseconds with some arbitrary reference point. + @return + When one or more animation has finished then is + returned. Call CleanUpAnimationList() in this case. + */ + bool ProcessAnimations (const double nTime); + + /** Remove animations that have expired. + */ + void CleanUpAnimationList(); + + void RequestNextFrame(); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx b/sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx new file mode 100644 index 000000000..6ced17486 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsClipboard.hxx @@ -0,0 +1,208 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +#include + +class SfxRequest; +struct AcceptDropEvent; +class DropTargetHelper; +struct ExecuteDropEvent; +struct ImplSVEvent; +class Point; +class SdPage; +namespace vcl { class Window; } + +namespace sd { +class Window; +} + +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::controller { + +class SlideSorterController; + +class SAL_DLLPUBLIC_RTTI Clipboard final + : public ViewClipboard +{ +public: + Clipboard (SlideSorter& rSlideSorter); + virtual ~Clipboard() override; + + /** Create a slide sorter transferable from the given sd + transferable. The returned transferable is set up with all + information necessary so that it can be dropped on a slide sorter. + */ + static std::shared_ptr CreateTransferableUserData (SdTransferable* pTransferable); + + void HandleSlotCall (SfxRequest& rRequest); + + void DoCut (); + // Exported for unit test + SD_DLLPUBLIC void DoCopy(); + // Exported for unit test + SD_DLLPUBLIC void DoPaste(); + void DoDelete (); + + void StartDrag ( + const Point& rDragPt, + vcl::Window* pWindow ); + + void DragFinished ( + sal_Int8 nDropAction); + + sal_Int8 AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer ); + + sal_Int8 ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer ); + + void Abort(); + +private: + virtual sal_uInt16 DetermineInsertPosition () override; + + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + + typedef ::std::vector PageList; + /** Remember the pages that are dragged to another document or to + another place in the same document so that they can be removed after + a move operation. + */ + PageList maPagesToRemove; + + /** Used when a drop is executed to combine all undo actions into one. + Typically created in ExecuteDrop() and released in DragFinish(). + */ + class UndoContext; + std::unique_ptr mxUndoContext; + + std::unique_ptr> mxSelectionObserverContext; + ImplSVEvent * mnDragFinishedUserEventId; + + void CreateSlideTransferable ( + vcl::Window* pWindow, + bool bDrag); + + /** Determine the position of where to insert the pages in the current + transferable of the sd module. + @return + The index in the range [0,n] (both inclusive) with n the number + of pages is returned. + */ + sal_Int32 GetInsertionPosition (); + + /** Paste the pages of the transferable of the sd module at the given + position. + @param nInsertPosition + The position at which to insert the pages. The valid range is + [0,n] (both inclusive) with n the number of pages in the + document. + @return + The number of inserted pages is returned. + */ + sal_Int32 PasteTransferable (sal_Int32 nInsertPosition); + + /** Select a range of pages of the model. Typically usage is the + selection of newly inserted pages. + @param nFirstIndex + The index of the first page to select. + @param nPageCount + The number of pages to select. + */ + void SelectPageRange (sal_Int32 nFirstIndex, sal_Int32 nPageCount); + + /** Return when the current transferable in the current state of + the slidesorter is acceptable to be pasted. For this the + transferable has to + a) exist, + b) contain one or more regular draw pages, no master pages. + When master pages are involved, either in the transferable or in the + slide sorter (by it displaying master pages) the drop of the + transferable is not accepted. The reason is the missing + implementation of proper handling master pages copy-and-paste. + */ + enum DropType { DT_PAGE, DT_PAGE_FROM_NAVIGATOR, DT_SHAPE, DT_NONE }; + DropType IsDropAccepted() const; + + /** This method contains the code for AcceptDrop() and ExecuteDrop() shapes. + There are only minor differences for the two cases at this level. + @param eCommand + This parameter specifies whether to do an AcceptDrop() or + ExecuteDrop(). + @param rPosition + Since the event is given as void pointer we can not take the + mouse position from it. The caller has to supply it in this + parameter. + @param pDropEvent + Event though the AcceptDropEvent and ExecuteDropEvent are very + similar they do not have a common base class. Because of that + we have to use a void* to pass these structs. + @param nPage + When the page number is given as 0xffff then it is replaced by + the number of the page at the mouse position. If the mouse is + not over a page then neither AcceptDrop() nor ExecuteDrop() are + executed. + */ + enum DropCommand { DC_ACCEPT, DC_EXECUTE }; + sal_Int8 ExecuteOrAcceptShapeDrop ( + DropCommand eCommand, + const Point& rPosition, + const void* pDropEvent , + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer); + + /** Return whether the insertion defined by the transferable is + trivial, ie would not change either source nor target document. + */ + bool IsInsertionTrivial ( + SdTransferable const * pTransferable, + const sal_Int8 nDndAction) const; + + /** Asynchronous part of DragFinished. The argument is the sal_Int8 + nDropAction, disguised as void*. + */ + DECL_DLLPRIVATE_LINK(ProcessDragFinished, void*, void); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsCurrentSlideManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsCurrentSlideManager.hxx new file mode 100644 index 000000000..0c5b3b243 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsCurrentSlideManager.hxx @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +class SdPage; + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** Manage the current slide. This includes setting the according flags at + the PageDescriptor objects and setting the current slide at the main + view shell. + + Switching pages is triggered only after a little delay. This allows + fast travelling through a larger set of slides without having to wait + for the edit view to update its content after every slide change. +*/ +class CurrentSlideManager +{ +public: + /** Create a new CurrentSlideManager object that manages the current + slide for the given SlideSorter. + */ + CurrentSlideManager(SlideSorter& rSlideSorter); + + ~CurrentSlideManager(); + + /** Call this when the current page of the main view shell has been + switched. Use SwitchCurrentSlide() to initiate such a switch. + */ + void NotifyCurrentSlideChange(const sal_Int32 nSlideIndex); + void NotifyCurrentSlideChange(const SdPage* pPage); + + /** Call this method to switch the current page of the main view shell + to the given slide. Use CurrentSlideHasChanged() when the current + slide change has been initiated by someone else. + @param nSlideIndex + Zero based index in the range [0,number-of-slides). + The page selection is cleared and only the new + current slide is selected. + */ + void SwitchCurrentSlide(const sal_Int32 nSlideIndex); + void SwitchCurrentSlide(const model::SharedPageDescriptor& rpSlide, + const bool bUpdateSelection = false); + + /** Return the page descriptor for the current slide. Note, that when + there is no current slide then the returned pointer is empty. + */ + const model::SharedPageDescriptor& GetCurrentSlide() const { return mpCurrentSlide; } + + /** Release all references to model data. + */ + void PrepareModelChange(); + + /** Modify inner state in reaction to a change of the SlideSorterModel. + */ + void HandleModelChange(); + +private: + SlideSorter& mrSlideSorter; + sal_Int32 mnCurrentSlideIndex; + model::SharedPageDescriptor mpCurrentSlide; + /** Timer to control the delay after which to ask + XController/ViewShellBase to switch to another slide. + */ + Timer maSwitchPageDelayTimer; + + void SetCurrentSlideAtViewShellBase(const model::SharedPageDescriptor& rpSlide); + void SetCurrentSlideAtTabControl(const model::SharedPageDescriptor& rpSlide); + void SetCurrentSlideAtXController(const model::SharedPageDescriptor& rpSlide); + + /** When switching from one slide to a new current slide then this + method releases all ties to the old slide. + */ + void ReleaseCurrentSlide(); + + /** When switching from one slide to a new current slide then this + method connects to the new current slide. + */ + void AcquireCurrentSlide(const sal_Int32 nSlideIndex); + + DECL_LINK(SwitchPageCallback, Timer*, void); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsFocusManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsFocusManager.hxx new file mode 100644 index 000000000..180b7143f --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsFocusManager.hxx @@ -0,0 +1,211 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include +#include + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** This class manages the focus of the slide sorter. There is the focus + page which is or is not focused. Initialized to point to the first page + it can be set to other pages by using the MoveFocus() method. The + focused state of the focus page can be toggled with the ToggleFocus() + method. +*/ +class FocusManager +{ +public: + /** Create a new focus manager that operates on the pages of the model + associated with the given controller. The focus page is set to the + first page. Focused state is off. + */ + FocusManager(SlideSorter& rSlideSorter); + + ~FocusManager(); + + enum class FocusMoveDirection + { + Left, + Right, + Up, + Down + }; + + /** Move the focus from the currently focused page to one that is + displayed adjacent to it, either vertically or horizontally. + @param eDirection + Direction in which to move the focus. Wrap around is done + differently when moving vertically or horizontally. Vertical + wrap around takes place in the same column, i.e. when you are + in the top row and move up you come out in the bottom row in the + same column. Horizontal wrap around moves to the next + (FocusMoveDirection::Right) or previous (FocusMoveDirection::Left) page. Moving to the right + from the last page goes to the first page and vice versa. + The current page index is set to the nearest valid + page index. + */ + void MoveFocus(FocusMoveDirection eDirection); + + /** Show the focus indicator of the current slide. + @param bScrollToFocus + When (the default) then the view is scrolled so that the + focus rectangle lies inside its visible area. + */ + void ShowFocus(const bool bScrollToFocus = true); + + /** Hide the focus indicator. + */ + void HideFocus(); + + /** Toggle the focused state of the current slide. + @return + Returns the focused state of the focus page after the call. + */ + bool ToggleFocus(); + + /** Return whether the window managed by the called focus manager has + the input focus of the application. + */ + bool HasFocus() const; + + /** Return the descriptor of the page that currently has the focus. + @return + When there is no page that currently has the focus then NULL is + returned. + */ + model::SharedPageDescriptor GetFocusedPageDescriptor() const; + + /** Return the index of the page that currently has the focus as it is + accepted by the slide sorter model. + @return + When there is no page that currently has the focus then -1 is + returned. + */ + sal_Int32 GetFocusedPageIndex() const { return mnPageIndex; } + + /** Set the focused page to the one described by the given page + descriptor. The visibility of the focus indicator is not modified. + @param rDescriptor + One of the page descriptors that are currently managed by the + SlideSorterModel. + */ + bool SetFocusedPage(const model::SharedPageDescriptor& rDescriptor); + + /** Set the focused page to the one described by the given page + index. The visibility of the focus indicator is not modified. + @param nPageIndex + A valid page index that is understood by the SlideSorterModel. + */ + void SetFocusedPage(sal_Int32 nPageIndex); + + bool SetFocusedPageToCurrentPage(); + + /** Return when the focus indicator is currently shown. A + prerequisite is that the window managed by this focus manager has + the input focus as indicated by a return value of + HasFocus(). It is not necessary that the focus indicator is + visible. It may have been scrolled outside the visible area. + */ + bool IsFocusShowing() const; + + /** Add a listener that is called when the focus is shown or hidden or + set to another page object. + @param rListener + When this method is called multiple times for the same listener + the second and all following calls are ignored. Each listener + is added only once. + */ + void AddFocusChangeListener(const Link& rListener); + + /** Remove a focus change listener. + @param rListener + It is safe to pass a listener that was not added are has been + removed previously. Such calls are ignored. + */ + void RemoveFocusChangeListener(const Link& rListener); + + /** Create an instance of this class to temporarily hide the focus + indicator. It is restored to its former visibility state when the + FocusHider is destroyed. + */ + class FocusHider + { + public: + FocusHider(FocusManager&); + ~FocusHider() COVERITY_NOEXCEPT_FALSE; + + private: + bool mbFocusVisible; + FocusManager& mrManager; + }; + +private: + SlideSorter& mrSlideSorter; + + /** Index of the page that may be focused. It is -1 when the model + contains no page. + */ + sal_Int32 mnPageIndex; + + /** This flag indicates whether the page pointed to by mpFocusDescriptor + has the focus. + */ + bool mbPageIsFocused; + + ::std::vector> maFocusChangeListeners; + + /** Reset the focus state of the given descriptor and request a repaint + so that the focus indicator is hidden. + @param pDescriptor + When NULL is given then the call is ignored. + */ + void HideFocusIndicator(const model::SharedPageDescriptor& rpDescriptor); + + /** Set the focus state of the given descriptor, scroll it into the + visible area and request a repaint so that the focus indicator is + made visible. + @param pDescriptor + When NULL is given then the call is ignored. + @param bScrollToFocus + When (the default) then the view is scrolled so that the + focus rectangle lies inside its visible area. + */ + void ShowFocusIndicator(const model::SharedPageDescriptor& rpDescriptor, + const bool bScrollToFocus); + + /** Call all currently registered listeners that a focus change has + happened. The focus may be hidden or shown or moved from one page + object to another. + */ + void NotifyFocusChangeListeners() const; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsInsertionIndicatorHandler.hxx b/sd/source/ui/slidesorter/inc/controller/SlsInsertionIndicatorHandler.hxx new file mode 100644 index 000000000..43f2d2f6a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsInsertionIndicatorHandler.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +namespace sd::slidesorter { class SlideSorter; } +namespace sd::slidesorter::view { +class InsertAnimator; +class InsertionIndicatorOverlay; +} + +class SdTransferable; + +namespace sd::slidesorter::controller { + +/** Manage the visibility and location of the insertion indicator. Its + actual display is controlled by the InsertionIndicatorOverlay. +*/ +class InsertionIndicatorHandler +{ +public: + InsertionIndicatorHandler (SlideSorter& rSlideSorter); + ~InsertionIndicatorHandler() COVERITY_NOEXCEPT_FALSE; + + enum Mode { CopyMode, MoveMode, UnknownMode }; + static Mode GetModeFromDndAction (const sal_Int8 nDndAction); + + /** Activate the insertion marker at the given coordinates. + */ + void Start (const bool bIsOverSourceView); + + /** Deactivate the insertion marker. + */ + void End (const controller::Animator::AnimationMode eMode); + + /** This context make sure that the insertion indicator is shown + (provided that the clipboard is not empty) while the context is + alive. Typically used while a context menu is displayed. + */ + class ForceShowContext + { + public: + ForceShowContext (const std::shared_ptr& rpHandler); + ~ForceShowContext() COVERITY_NOEXCEPT_FALSE; + private: + const std::shared_ptr mpHandler; + }; + + /** Update the indicator icon from the current transferable (from the + clipboard or an active drag and drop operation.) + */ + void UpdateIndicatorIcon (const SdTransferable* pTransferable); + + /** Set the position of the insertion marker to the given coordinates. + */ + void UpdatePosition ( + const Point& rMouseModelPosition, + const Mode eMode); + void UpdatePosition ( + const Point& rMouseModelPosition, + const sal_Int8 nDndAction); + + /** Return whether the insertion marker is active. + */ + bool IsActive() const { return mbIsActive;} + + /** Return the insertion index that corresponds with the current + graphical location of the insertion indicator. + */ + sal_Int32 GetInsertionPageIndex() const; + + /** Determine whether moving the current selection to the current + position of the insertion marker would alter the document. This + would be the case when the selection is not consecutive or would be + moved to a position outside and not adjacent to the selection. + */ + bool IsInsertionTrivial ( + const sal_Int32 nInsertionIndex, + const Mode eMode) const; + /** This method is like the other variant. It operates implicitly + on the current insertion index as would be returned by + GetInsertionPageIndex(). + */ + bool IsInsertionTrivial (const sal_Int8 nDndAction); + +private: + SlideSorter& mrSlideSorter; + std::shared_ptr mpInsertAnimator; + std::shared_ptr mpInsertionIndicatorOverlay; + view::InsertPosition maInsertPosition; + Mode meMode; + bool mbIsInsertionTrivial; + bool mbIsActive; + bool mbIsReadOnly; + bool mbIsOverSourceView; + Size maIconSize; + bool mbIsForcedShow; + + void SetPosition ( + const Point& rPoint, + const Mode eMode); + std::shared_ptr const & GetInsertAnimator(); + + /** Make the insertion indicator visible (that is the show part) and + keep it visible, even when the mouse leaves the window (that is the + force part). We need this when a context menu is displayed (mouse + over the popup menu triggers a mouse leave event) while the + insertion indicator remains visible in the background. + + In effect all calls to End() are ignored until ForceEnd() is called. + */ + void ForceShow(); + void ForceEnd(); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsPageSelector.hxx b/sd/source/ui/slidesorter/inc/controller/SlsPageSelector.hxx new file mode 100644 index 000000000..6a4b75004 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsPageSelector.hxx @@ -0,0 +1,219 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + +#include + +class SdPage; + +namespace sd::slidesorter +{ +class SlideSorter; +} +namespace sd::slidesorter::model +{ +class SlideSorterModel; +} + +namespace sd::slidesorter::controller +{ +class SlideSorterController; + +/** A sub-controller that handles page selection of the slide browser. + Selecting a page does not make it the current page (of the main view) + automatically as this would not be desired in a multi selection. This + has to be done explicitly by calling the + CurrentSlideManager::SetCurrentSlide() method. + + Indices of pages relate always to the number of all pages in the model + (as returned by GetPageCount()) not just the selected pages. +*/ +class PageSelector +{ +public: + explicit PageSelector(SlideSorter& rSlideSorter); + PageSelector(const PageSelector&) = delete; + PageSelector& operator=(const PageSelector&) = delete; + + // Exported for unit test + SD_DLLPUBLIC void SelectAllPages(); + SD_DLLPUBLIC void DeselectAllPages(); + + /** Update the selection state of all page descriptors to be the same as + that of the corresponding pages of the SdPage objects and issue + redraw requests where necessary. + */ + void GetCoreSelection(); + + /** Update the selection state of the SdPage objects to be the same as + that of the corresponding page descriptors. + */ + void SetCoreSelection(); + + /** Select the specified descriptor. The selection state of the other + descriptors is not affected. + */ + void SelectPage(int nPageIndex); + /** Select the descriptor that is associated with the given page. The + selection state of the other descriptors is not affected. + */ + void SelectPage(const SdPage* pPage); + /** Select the specified descriptor. The selection state of the other + descriptors is not affected. + */ + void SelectPage(const model::SharedPageDescriptor& rpDescriptor); + + /** Return whether the specified page is selected. This convenience + method is a substitute for + SlideSorterModel::GetPageDescriptor(i)->HasState(ST_Selected) is + included here to make this class more self contained. + */ + SD_DLLPUBLIC bool IsPageSelected(int nPageIndex); + + /** Return whether the specified page is visible. This convenience + method is a substitute for + SlideSorterModel::GetPageDescriptor(i)->HasState(ST_Visible) is + included here to make this class more self contained. + */ + bool IsPageVisible(int nPageIndex); + + /** Deselect the descriptor that is associated with the given page. + The current page is updated to the first slide + of the remaining selection. + */ + void DeselectPage(int nPageIndex); + void DeselectPage(const model::SharedPageDescriptor& rpDescriptor, + const bool bUpdateCurrentPage = true); + + /** This convenience method returns the same number of pages that + SlideSorterModel.GetPageCount() returns. It is included here so + that it is self contained for iterating over all pages to select or + deselect them. + */ + int GetPageCount() const; + int GetSelectedPageCount() const { return mnSelectedPageCount; } + + /** Return the anchor for a range selection. This usually is the first + selected page after all pages have been deselected. + @return + The returned anchor may be NULL. + */ + const model::SharedPageDescriptor& GetSelectionAnchor() const { return mpSelectionAnchor; } + + typedef ::std::vector PageSelection; + + /** Return an object that describes the current selection. The caller + can use that object to later restore the selection. + @return + The object returned describes the selection via indices. So + even if pages are exchanged a later call to SetPageSelection() + is valid. + */ + std::shared_ptr GetPageSelection() const; + + /** Restore a page selection according to the given selection object. + @param rSelection + Typically obtained by calling GetPageSelection() this object + is used to restore the selection. If pages were exchanged since + the last call to GetPageSelection() it is still valid to call + this method with the selection. When pages have been inserted + or removed the result may be unexpected. + @param bUpdateCurrentPage + When (the default value) then after setting the + selection update the current page to the first page of the + selection. + When called from within UpdateCurrentPage() then this flag is + used to prevent a recursion loop. + */ + void SetPageSelection(const std::shared_ptr& rSelection, + const bool bUpdateCurrentPage); + + /** Call this method after the model has changed to set the number + of selected pages. + */ + void CountSelectedPages(); + + /** Use the UpdateLock whenever you do a complex selection, i.e. call + more than one method in a row. An active lock prevents intermediate + changes of the current slide. + */ + class UpdateLock + { + public: + UpdateLock(SlideSorter const& rSlideSorter); + UpdateLock(PageSelector& rPageSelector); + ~UpdateLock(); + void Release(); + + private: + PageSelector* mpSelector; + }; + + class BroadcastLock + { + public: + BroadcastLock(SlideSorter const& rSlideSorter); + BroadcastLock(PageSelector& rPageSelector); + ~BroadcastLock(); + + private: + PageSelector& mrSelector; + }; + +private: + model::SlideSorterModel& mrModel; + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + int mnSelectedPageCount; + int mnBroadcastDisableLevel; + bool mbSelectionChangeBroadcastPending; + model::SharedPageDescriptor mpMostRecentlySelectedPage; + /// Anchor for a range selection. + model::SharedPageDescriptor mpSelectionAnchor; + sal_Int32 mnUpdateLockCount; + bool mbIsUpdateCurrentPagePending; + + /** Enable the broadcasting of selection change events. This calls the + SlideSorterController::SelectionHasChanged() method to do the actual + work. When EnableBroadcasting has been called as many times as + DisableBroadcasting() was called before and the selection has been + changed in the meantime, this change will be broadcasted. + */ + void EnableBroadcasting(); + + /** Disable the broadcasting of selection change events. Subsequent + changes of the selection will set a flag that triggers the sending + of events when EnableBroadcasting() is called. + */ + void DisableBroadcasting(); + + void UpdateCurrentPage(const bool bUpdateOnlyWhenPending = false); + + void CheckConsistency() const; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsProperties.hxx b/sd/source/ui/slidesorter/inc/controller/SlsProperties.hxx new file mode 100644 index 000000000..344ac67f3 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsProperties.hxx @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd::slidesorter::controller +{ +/** An extensible set of properties used throughout the slide sorter. +*/ +class Properties +{ +public: + Properties(); + + /** Call this method after receiving a VclEventId::ApplicationDataChanged + event. + */ + void HandleDataChangeEvent(); + + /** When this method returns then the current slide is + highlighted in the view. The default value is . + */ + bool IsHighlightCurrentSlide() const { return mbIsHighlightCurrentSlide; } + void SetHighlightCurrentSlide(const bool bIsHighlightCurrentSlide); + + /** When this method returns then the selection is indicated in + the view (typically by drawing rectangles around the selected + slides.) The default value is . + */ + bool IsShowSelection() const { return mbIsShowSelection; } + void SetShowSelection(const bool bIsShowSelection); + + /** When this method returns then the focusdselection is indicated in + the view (typically by drawing dotted rectangles around the selected + slides.) The default value is . + */ + bool IsShowFocus() const { return mbIsShowFocus; } + void SetShowFocus(const bool bIsShowFocus); + + /** When this method returns then on a selection change the + visible area is adapted so that the selected slides are shown + centered in the view. This can be used to center the current slide + by selecting only the current slide. The default value is . + */ + bool IsCenterSelection() const { return mbIsCenterSelection; } + void SetCenterSelection(const bool bIsCenterSelection); + + /** When this method returns then the view may try to change the + visible area by scrolling it smoothly on the screen. Experimental. + Default value is . + */ + bool IsSmoothSelectionScrolling() const { return mbIsSmoothSelectionScrolling; } + void SetSmoothSelectionScrolling(const bool bIsSmoothSelectionScrolling); + + /** When this method returns then during a full screen + presentation the previews in a slide sorter are not updated. + Default value is . + */ + bool IsSuspendPreviewUpdatesDuringFullScreenPresentation() const + { + return mbIsSuspendPreviewUpdatesDuringFullScreenPresentation; + } + void SetSuspendPreviewUpdatesDuringFullScreenPresentation(const bool bFlag); + + /** Return the background color. + */ + const Color& GetBackgroundColor() const { return maBackgroundColor; } + void SetBackgroundColor(const Color& rColor); + + /** Return the text color. + */ + const Color& GetTextColor() const { return maTextColor; } + void SetTextColor(const Color& rColor); + + /** Return the color in which selections are to be painted. + */ + const Color& GetSelectionColor() const { return maSelectionColor; } + void SetSelectionColor(const Color& rColor); + + /** Return the color used for highlighting e.g. the current slide. + */ + const Color& GetHighlightColor() const { return maHighlightColor; } + void SetHighlightColor(const Color& rColor); + + /** The UI can be set to be read only independently from the model status. + Used for instance in the presenter view. + */ + bool IsUIReadOnly() const { return mbIsUIReadOnly; } + void SetUIReadOnly(const bool bIsUIReadOnly); + +private: + bool mbIsHighlightCurrentSlide; + bool mbIsShowSelection; + bool mbIsShowFocus; + bool mbIsCenterSelection; + bool mbIsSmoothSelectionScrolling; + bool mbIsSuspendPreviewUpdatesDuringFullScreenPresentation; + Color maBackgroundColor; + Color maTextColor; + Color maSelectionColor; + Color maHighlightColor; + bool mbIsUIReadOnly; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsScrollBarManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsScrollBarManager.hxx new file mode 100644 index 000000000..853d98f4a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsScrollBarManager.hxx @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace sd { class Window; } + +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::controller { + +/** Manage the horizontal and vertical scroll bars. Listen for events, set + their sizes, place them in the window, determine their visibilities. + +

        Handle auto scrolling, i.e. the scrolling of the window when the + mouse comes near the window border while dragging a selection.

        + +

        In order to make the slide sorter be used in the task pane with its + own vertical scrollbars the vertical scrollbar of the use of the slide + sorter is optional. When using it the available area in a window is + used and the vertical scrollbar is displayed when that area is not large + enough. When the vertical scrollbar is not used then the available area + is assumed to be modifiable. In that case the PlaceScrollBars() method + may return an area larger than the one given.

        +*/ +class ScrollBarManager +{ +public: + /** Create a new scroll bar manager that manages three controls: the + horizontal scroll bar, the vertical scroll bar, and the little + window that fills the gap at the bottom right corner that is left + between the two scroll bars. Call LateInitialization() after + constructing a new object. + */ + ScrollBarManager (SlideSorter& rSlideSorter); + + ~ScrollBarManager(); + + /** Register listeners at the scroll bars. This method is called after + startup of a new slide sorter object or after a reactivation of a + slide sorter that for example is taken from a cache. + */ + void Connect(); + + /** Remove listeners from the scroll bars. This method is called when + the slide sorter is destroyed or when it is suspended, e.g. put + into a cache for later reuse. + */ + void Disconnect(); + + /** Set up the scroll bar, i.e. thumb size and position. Call this + method when the content of the browser window changed, i.e. pages + were inserted or deleted, the layout or the zoom factor has + changed. + @param bScrollToCurrentPosition + When then scroll the window to the new offset that is + defined by the scroll bars. Otherwise the new offset is simply + set and the whole window is repainted. + */ + void UpdateScrollBars ( + bool bScrollToCurrentPosition); + + /** Place the scroll bars inside the given area. When the available + area is not large enough for the content to display the horizontal + and/or vertical scroll bar is enabled. + @param rAvailableArea + The scroll bars will be placed inside this rectangle. It is + expected to be given in pixel relative to its parent. + @param bIsHorizontalScrollBarAllowed + Only when this flag is the horizontal scroll may be + displayed. + @param bIsVerticalScrollBarAllowed + Only when this flag is the horizontal scroll may be + displayed. + @return + Returns the space that remains after the scroll bars are + placed. + */ + ::tools::Rectangle PlaceScrollBars ( + const ::tools::Rectangle& rAvailableArea, + const bool bIsHorizontalScrollBarAllowed, + const bool bIsVerticalScrollBarAllowed); + + /** Update the vertical and horizontal scroll bars so that the visible + area has the given top and left values. + */ + void SetTopLeft (const Point& rNewTopLeft); + + /** Return the width of the vertical scroll bar, which--when + shown--should be fixed in contrast to its height. + @return + Returns 0 when the vertical scroll bar is not shown or does not + exist, otherwise its width in pixel is returned. + */ + int GetVerticalScrollBarWidth() const; + + /** Return the height of the horizontal scroll bar, which--when + shown--should be fixed in contrast to its width. + @return + Returns 0 when the vertical scroll bar is not shown or does not + exist, otherwise its height in pixel is returned. + */ + int GetHorizontalScrollBarHeight() const; + + /** Call this method to scroll a window while the mouse is in dragging a + selection. If the mouse is near the window border or is outside the + window then scroll the window accordingly. + @param rMouseWindowPosition + The mouse position for which the scroll amount is calculated. + @param rAutoScrollFunctor + Every time when the window is scrolled then this functor is executed. + @return + When the window is scrolled then this method returns . + When the window is not changed then is returned. + */ + bool AutoScroll ( + const Point& rMouseWindowPosition, + const ::std::function& rAutoScrollFunctor); + + void StopAutoScroll(); + + void clearAutoScrollFunctor(); + + enum Orientation { Orientation_Horizontal, Orientation_Vertical }; + /** Scroll the slide sorter by setting the thumbs of the scroll bars and + by moving the content of the content window. + @param eOrientation + Defines whether to scroll horizontally or vertically. + @param nDistance + distance in slides. + */ + void Scroll( + const Orientation eOrientation, + const sal_Int32 nDistance); + +private: + SlideSorter& mrSlideSorter; + + /** The horizontal scroll bar. Note that is used but not owned by + objects of this class. It is given to the constructor. + */ + VclPtr mpHorizontalScrollBar; + + /** The vertical scroll bar. Note that is used but not owned by + objects of this class. It is given to the constructor. + */ + VclPtr mpVerticalScrollBar; + + /// Relative horizontal position of the visible area in the view. + double mnHorizontalPosition; + /// Relative vertical position of the visible area in the view. + double mnVerticalPosition; + /** The width and height of the border at the inside of the window which + when entered while in drag mode leads to a scrolling of the window. + */ + Size maScrollBorder; + /** The only task of this little window is to paint the little square at + the bottom right corner left by the two scroll bars (when both are + visible). + */ + VclPtr mpScrollBarFiller; + + /** The auto scroll timer is used for keep scrolling the window when the + mouse reaches its border while dragging a selection. When the mouse + is not moved the timer issues events to keep scrolling. + */ + Timer maAutoScrollTimer; + Size maAutoScrollOffset; + bool mbIsAutoScrollActive; + + /** The content window is the one whose view port is controlled by the + scroll bars. + */ + VclPtr mpContentWindow; + + ::std::function maAutoScrollFunctor; + + void SetWindowOrigin ( + double nHorizontalPosition, + double nVerticalPosition); + + /** Determine the visibility of the scroll bars so that the window + content is not clipped in any dimension without showing a scroll + bar. + @param rAvailableArea + The area in which the scroll bars, the scroll bar filler, and + the SlideSorterView will be placed. + @return + The area that is enclosed by the scroll bars is returned. It + will be filled with the SlideSorterView. + */ + ::tools::Rectangle DetermineScrollBarVisibilities( + const ::tools::Rectangle& rAvailableArea, + const bool bIsHorizontalScrollBarAllowed, + const bool bIsVerticalScrollBarAllowed); + + /** Typically called by DetermineScrollBarVisibilities() this method + tests a specific configuration of the two scroll bars being visible + or hidden. + @return + When the window content can be shown with only being clipped in + an orientation where the scroll bar would be shown then + is returned. + */ + bool TestScrollBarVisibilities ( + bool bHorizontalScrollBarVisible, + bool bVerticalScrollBarVisible, + const ::tools::Rectangle& rAvailableArea); + + void CalcAutoScrollOffset (const Point& rMouseWindowPosition); + bool RepeatAutoScroll(); + + DECL_LINK(HorizontalScrollBarHandler, ScrollBar*, void); + DECL_LINK(VerticalScrollBarHandler, ScrollBar*, void); + DECL_LINK(AutoScrollTimeoutHandler, Timer *, void); + + void PlaceHorizontalScrollBar (const ::tools::Rectangle& aArea); + void PlaceVerticalScrollBar (const ::tools::Rectangle& aArea); + void PlaceFiller (const ::tools::Rectangle& aArea); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsSelectionFunction.hxx b/sd/source/ui/slidesorter/inc/controller/SlsSelectionFunction.hxx new file mode 100644 index 000000000..7d80fbd26 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsSelectionFunction.hxx @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd::slidesorter +{ +class SlideSorter; +} + +struct AcceptDropEvent; + +namespace sd::slidesorter::controller +{ +class SlideSorterController; + +class SelectionFunction final : public FuPoor +{ +public: + SelectionFunction(const SelectionFunction&) = delete; + SelectionFunction& operator=(const SelectionFunction&) = delete; + + static rtl::Reference Create(SlideSorter& rSlideSorter, SfxRequest& rRequest); + + // Mouse- & Key-Events + virtual bool KeyInput(const KeyEvent& rKEvt) override; + virtual bool MouseMove(const MouseEvent& rMEvt) override; + virtual bool MouseButtonUp(const MouseEvent& rMEvt) override; + virtual bool MouseButtonDown(const MouseEvent& rMEvt) override; + + /// Forward to the clipboard manager. + virtual void DoCut() override; + + /// Forward to the clipboard manager. + virtual void DoCopy() override; + + /// Forward to the clipboard manager. + virtual void DoPaste() override; + + /** is called when the current function should be aborted.

        + This is used when a function gets a KEY_ESCAPE but can also + be called directly. + + @returns + true if an active function was aborted + */ + virtual bool cancel() override; + + void MouseDragged(const AcceptDropEvent& rEvent, const sal_Int8 nDragAction); + + /** Turn of substitution display and insertion indicator. + */ + void NotifyDragFinished(); + + class EventDescriptor; + class ModeHandler; + friend class ModeHandler; + enum Mode + { + NormalMode, + MultiSelectionMode, + DragAndDropMode + }; + void SwitchToNormalMode(); + void SwitchToDragAndDropMode(const Point& rMousePosition); + void SwitchToMultiSelectionMode(const Point& rMousePosition, const sal_uInt32 nEventCode); + + void ResetShiftKeySelectionAnchor(); + /** Special case handling for when the context menu is hidden. This + method will reinitialize the current mouse position to prevent the + mouse motion during the time the context menu is displayed from + being interpreted as drag-and-drop start. + */ + void ResetMouseAnchor(); + +private: + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + + SelectionFunction(SlideSorter& rSlideSorter, SfxRequest& rRequest); + + virtual ~SelectionFunction() override; + + /** Remember the slide where the shift key was pressed and started a + multiselection via keyboard. + */ + sal_Int32 mnShiftKeySelectionAnchor; + + /** The selection function can be in one of several mutually + exclusive modes. + */ + std::shared_ptr mpModeHandler; + + /** Make the slide nOffset slides away of the current one the new + current slide. When the new index is outside the range of valid + page numbers it is clipped to that range. + @param nOffset + When nOffset is negative then go back. When nOffset if positive go + forward. When it is zero then ignore the call. + */ + void GotoNextPage(int nOffset); + + /** Make the slide with the given index the new current slide. + @param nIndex + Index of the new current slide. When the new index is outside + the range of valid page numbers it is clipped to that range. + */ + void GotoPage(int nIndex); + + void ProcessMouseEvent(sal_uInt32 nEventType, const MouseEvent& rEvent); + + // What follows are a couple of helper methods that are used by + // ProcessMouseEvent(). + + void ProcessEvent(EventDescriptor& rEvent); + + void MoveFocus(const FocusManager::FocusMoveDirection eDirection, const bool bIsShiftDown, + const bool bIsControlDown); + + void SwitchMode(const std::shared_ptr& rpHandler); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsSelectionManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsSelectionManager.hxx new file mode 100644 index 000000000..4f52d49e6 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsSelectionManager.hxx @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +class SdPage; + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +class SlideSorterController; +class SelectionObserver; + +/** This class is a part of the controller and handles the selection of + slides. +

        It has methods to modify the selected slides (delete them or + move them to other places in the document), change the visible area so + to make the selected slides visible, tell listeners when the selection + changes.

        +*/ +class SelectionManager +{ +public: + /** Create a new SelectionManager for the given slide sorter. + */ + SelectionManager(SlideSorter& rSlideSorter); + + ~SelectionManager(); + + /** Delete the currently selected slides. When this method returns the + selection is empty. + @param bSelectFollowingPage + When then after deleting the selected pages make the + slide after the last selected page the new current page. + When then make the first slide before the selected + pages the new current slide. + */ + void DeleteSelectedPages(const bool bSelectFollowingPage = true); + + /** Call this method after the selection has changed (possible several + calls to the PageSelector) to invalidate the relevant slots and send + appropriate events. + */ + void SelectionHasChanged(); + + /** Add a listener that is called when the selection of the slide sorter + changes. + @param rListener + When this method is called multiple times for the same listener + the second and all following calls are ignored. Each listener + is added only once. + */ + void AddSelectionChangeListener(const Link& rListener); + + /** Remove a listener that was called when the selection of the slide + sorter changes. + @param rListener + It is safe to pass a listener that was not added are has been + removed previously. Such calls are ignored. + */ + void RemoveSelectionChangeListener(const Link& rListener); + + /** Return the position where to insert pasted slides based on the + current selection. When there is a selection then the insert + position is behind the last slide. When the selection is empty then + most of the time the insert position is at the end of the document. + There is an exception right after the display of a popup-menu. The + position of the associated insertion marker is stored here and reset + the next time the selection changes. + */ + sal_Int32 GetInsertionPosition() const; + + /** Store an insertion position temporarily. It is reset when the + selection changes the next time. + */ + void SetInsertionPosition(const sal_Int32 nInsertionPosition); + + const std::shared_ptr& GetSelectionObserver() const + { + return mpSelectionObserver; + } + +private: + SlideSorter& mrSlideSorter; + SlideSorterController& mrController; + + ::std::vector> maSelectionChangeListeners; + + /** The insertion position is only temporarily valid. Negative values + indicate that the explicit insertion position is not valid. In this + case GetInsertionPosition() calculates it from the current selection. + */ + sal_Int32 mnInsertionPosition; + + std::shared_ptr mpSelectionObserver; + + /** Delete the given list of normal pages. This method is a helper + function for DeleteSelectedPages(). + @param rSelectedNormalPages + A list of normal pages. Supplying master pages is an error. + */ + void DeleteSelectedNormalPages(const ::std::vector& rSelectedNormalPages); + + /** Delete the given list of master pages. This method is a helper + function for DeleteSelectedPages(). + @param rSelectedMasterPages + A list of master pages. Supplying normal pages is an error. + */ + void DeleteSelectedMasterPages(const ::std::vector& rSelectedMasterPages); +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsSelectionObserver.hxx b/sd/source/ui/slidesorter/inc/controller/SlsSelectionObserver.hxx new file mode 100644 index 000000000..11742b890 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsSelectionObserver.hxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd::slidesorter +{ +class SlideSorter; +} + +class SdrPage; +class SdPage; + +namespace sd::slidesorter::controller +{ +/** Observe insertions and deletions of pages between calls to + StartObservation() and EndObservation(). When the later is called + the selection is set to just the newly inserted pages. +*/ +class SelectionObserver final +{ +public: + SelectionObserver(SlideSorter& rSlideSorter); + ~SelectionObserver(); + + void NotifyPageEvent(const SdrPage* pPage); + void StartObservation(); + void AbortObservation(); + void EndObservation(); + + /** Use this little class instead of calling StartObservation and + EndObservation directly so that EndObservation is not forgotten or + omitted due to an exception or some break or return in the middle of + code. + */ + class Context + { + public: + Context(SlideSorter const& rSlideSorter); + ~Context() COVERITY_NOEXCEPT_FALSE; + void Abort(); + + private: + std::shared_ptr mpSelectionObserver; + }; + +private: + SlideSorter& mrSlideSorter; + bool mbIsObservationActive; + bool mbPageEventOccurred; + + ::std::vector maInsertedPages; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsSlotManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsSlotManager.hxx new file mode 100644 index 000000000..57de8422a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsSlotManager.hxx @@ -0,0 +1,98 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include +#include + +class AbstractSvxNameDialog; +class SfxItemSet; +class SfxRequest; + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** This manager takes over the work of handling slot calls from the + controller of the slide sorter. +*/ +class SlotManager +{ +public: + /** Create a new slot manager that handles slot calls for the controller + of a slide sorter. + @param rController + The controller for which to handle the slot calls. + */ + SlotManager(SlideSorter& rSlideSorter); + + void FuTemporary(SfxRequest& rRequest); + void FuPermanent(SfxRequest& rRequest); + void FuSupport(SfxRequest& rRequest); + void GetMenuState(SfxItemSet& rSet); + void GetClipboardState(SfxItemSet& rSet); + void GetStatusBarState(SfxItemSet& rSet); + void ExecCtrl(SfxRequest& rRequest); + void GetAttrState(SfxItemSet& rSet); + + /** Exclude or include one slide or all selected slides. + @param rpDescriptor + When the pointer is empty then apply the new state to all + selected pages. Otherwise apply the new state to just the + specified state. + */ + void ChangeSlideExclusionState(const model::SharedPageDescriptor& rpDescriptor, + const bool bExcludeSlide); + + /** Call this after a change from normal mode to master mode or back. + The affected slots are invalidated. + */ + void NotifyEditModeChange(); + +private: + /// The controller for which we manage the slot calls. + SlideSorter& mrSlideSorter; + + /** The implementation is a copy of the code for SID_RENAMEPAGE in + drviews2.cxx. + */ + void RenameSlide(const SfxRequest& rRequest); + DECL_LINK(RenameSlideHdl, AbstractSvxNameDialog&, bool); + DECL_STATIC_LINK(SlotManager, RenameSlideTooltipHdl, AbstractSvxNameDialog&, OUString); + bool RenameSlideFromDrawViewShell(sal_uInt16 nPageId, const OUString& rName); + + /** Handle SID_INSERTPAGE slot calls. + */ + void InsertSlide(SfxRequest& rRequest); + + void DuplicateSelectedSlides(SfxRequest& rRequest); + + /** Use one of several ways to determine where to insert a new page. + This can be the current selection or the insertion indicator. + */ + sal_Int32 GetInsertionPosition() const; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsTransferableData.hxx b/sd/source/ui/slidesorter/inc/controller/SlsTransferableData.hxx new file mode 100644 index 000000000..863c2fe73 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsTransferableData.hxx @@ -0,0 +1,78 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include + +#include + +class SdDrawDocument; +namespace sd::slidesorter { class SlideSorterViewShell; } + +namespace sd::slidesorter::controller { + +/** Represent previews and other information so that they can be + attached to an existing transferable. +*/ +class TransferableData final + : public SdTransferable::UserData, + public SfxListener +{ +public: + class Representative + { + public: + Representative (const BitmapEx& rBitmap, const bool bIsExcluded) + : maBitmap(rBitmap), mbIsExcluded(bIsExcluded) {} + + BitmapEx maBitmap; + bool mbIsExcluded; + }; + + static rtl::Reference CreateTransferable ( + SdDrawDocument* pSrcDoc, + SlideSorterViewShell* pViewShell, + ::std::vector&& rRepresentatives); + + static std::shared_ptr GetFromTransferable (const SdTransferable* pTransferable); + + TransferableData ( + SlideSorterViewShell* pViewShell, + ::std::vector&& rRepresentatives); + virtual ~TransferableData() override; + + const ::std::vector& GetRepresentatives() const { return maRepresentatives;} + + /** Return the view shell for which the transferable was created. + */ + SlideSorterViewShell* GetSourceViewShell() const { return mpViewShell;} + +private: + SlideSorterViewShell* mpViewShell; + const ::std::vector maRepresentatives; + + virtual void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override; +}; + +} // end of namespace ::sd::slidesorter::controller + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/controller/SlsVisibleAreaManager.hxx b/sd/source/ui/slidesorter/inc/controller/SlsVisibleAreaManager.hxx new file mode 100644 index 000000000..d9f5845af --- /dev/null +++ b/sd/source/ui/slidesorter/inc/controller/SlsVisibleAreaManager.hxx @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::controller +{ +/** Manage requests for scrolling page objects into view. +*/ +class VisibleAreaManager +{ +public: + explicit VisibleAreaManager(SlideSorter& rSlideSorter); + ~VisibleAreaManager(); + VisibleAreaManager(const VisibleAreaManager&) = delete; + VisibleAreaManager& operator=(const VisibleAreaManager&) = delete; + + void ActivateCurrentSlideTracking(); + void DeactivateCurrentSlideTracking(); + bool IsCurrentSlideTrackingActive() const { return mbIsCurrentSlideTrackingActive; } + + /** Request the current slide to be moved into the visible area. + This request is only obeyed when the current slide tracking is + active. + @see ActivateCurrentSlideTracking() and DeactivateCurrentSlideTracking() + */ + void RequestCurrentSlideVisible(); + + /** Request to make the specified page object visible. + */ + void RequestVisible(const model::SharedPageDescriptor& rpDescriptor, const bool bForce = false); + + /** Temporarily disable the update of the visible area. + */ + class TemporaryDisabler + { + public: + explicit TemporaryDisabler(SlideSorter const& rSlideSorter); + ~TemporaryDisabler(); + + private: + VisibleAreaManager& mrVisibleAreaManager; + }; + +private: + SlideSorter& mrSlideSorter; + + /** List of rectangle that someone wants to be moved into the visible + area. + Cleared on every call to ForgetVisibleRequests() and MakeVisible(). + */ + ::std::vector<::tools::Rectangle> maVisibleRequests; + + Point maRequestedVisibleTopLeft; + bool mbIsCurrentSlideTrackingActive; + int mnDisableCount; + + void MakeVisible(); + ::std::optional GetRequestedTopLeft() const; +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlideSorterModel.hxx b/sd/source/ui/slidesorter/inc/model/SlideSorterModel.hxx new file mode 100644 index 000000000..90223a1bc --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlideSorterModel.hxx @@ -0,0 +1,227 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +class SdDrawDocument; +class SdrPage; +class SdPage; +namespace sd::slidesorter +{ +class SlideSorter; +} +namespace com::sun::star::container +{ +class XIndexAccess; +} +namespace com::sun::star::drawing +{ +class XDrawPage; +} + +namespace sd::slidesorter::model +{ +inline sal_Int32 FromCoreIndex(const sal_uInt16 nCoreIndex) { return (nCoreIndex - 1) / 2; } + +/** The model of the slide sorter gives access to the slides that are to be + displayed in the slide sorter view. Via the SetDocumentSlides() method + this set of slides can be modified (but do not call it directly, use + SlideSorterController::SetDocumentSlides() instead.) +*/ +class SlideSorterModel final +{ +public: + SlideSorterModel(SlideSorter& rSlideSorter); + + ~SlideSorterModel(); + void Dispose(); + + /** This method is present to let the view create a ShowView for + displaying slides. + */ + SdDrawDocument* GetDocument(); + + /** Set a new edit mode and return whether the edit mode really + has been changed. When the edit mode is changed then the + previous page descriptor list is replaced by a new one which + has to be repainted. + @return + A return value of indicates that the edit mode has + changed and thus the page descriptor list has been set up + to reflect that change. A repaint is necessary. + */ + bool SetEditMode(EditMode eEditMode); + + EditMode GetEditMode() const { return meEditMode; } + + /** Return the number of slides in the document regardless of whether + they are visible or not or whether they are hidden or not. + The number of slides depends on the set of slides available through + the XIndexAccess given to SetDocumentSlides(). + */ + sal_Int32 GetPageCount() const; + + /** Return a page descriptor for the page with the specified index. + Page descriptors are created on demand. The page descriptor is + found (or not found) in constant time. + @param nPageIndex + The index of the requested slide. The valid values + are 0 to GetPageCount()-1. + @param bCreate + When and the requested page descriptor is missing then + it is created. When then an empty reference is + returned for missing descriptors. + @return + When the given index is not valid, i.e. lower than zero or + larger than or equal to the number of pages then an empty + reference is returned. Note that the page count may change + between calls to GetPageCount() and GetPageDescriptor(). + */ + SharedPageDescriptor GetPageDescriptor(const sal_Int32 nPageIndex, + const bool bCreate = true) const; + + /** Return a page descriptor for the given XDrawPage. Page descriptors + are created on demand. The page descriptor is found (or not found) + in (at most) linear time. Note that all page descriptors in front of + the one associated with the given XDrawPage are created when not yet + present. When the XDrawPage is not found then all descriptors are + created. + @return + Returns the index to the requested page descriptor or -1 when + there is no such page descriptor. + */ + sal_Int32 GetIndex(const css::uno::Reference& rxSlide) const; + + /** Return a page descriptor for the given SdrPage. Page descriptors + are created on demand. The page descriptor is found (or not found) + in (at most) linear time. Note that all page descriptors in front of + the one associated with the given XDrawPage are created when not yet + present. When the SdrPage is not found then all descriptors are + created. + @return + Returns the index to the requested page descriptor or -1 when + there is no such page descriptor. + */ + sal_Int32 GetIndex(const SdrPage* pPage) const; + + /** Return an index for accessing an SdrModel that corresponds to the + given SlideSorterModel index. In many cases we just have to apply + the n*2+1 magic. Only when a special model is set, like a custom + slide show, then the returned value is different. + */ + sal_uInt16 GetCoreIndex(const sal_Int32 nIndex) const; + + /** Call this method after the document has changed its structure. This + will get the model in sync with the SdDrawDocument. This method + tries not to throw away too much information already gathered. This + is especially important for previews of complex pages that take some + time to create. + */ + void Resync(); + + /** Delete all descriptors that currently are in the container. The size + of the container, however, is not altered. Use the AdaptSize + method for that. + */ + void ClearDescriptorList(); + + /** Set the selection of the document to exactly that of the called model. + */ + void SynchronizeDocumentSelection(); + + /** Set the selection of the called model to exactly that of the document. + */ + void SynchronizeModelSelection(); + + /** Return the mutex so that the caller can lock it and then safely + access the model. + */ + ::osl::Mutex& GetMutex() { return maMutex; } + + /** Set the XIndexAccess from which the called SlideSorterModel takes + its pages. + @param rxSlides + The set of slides accessible through this XIndexAccess are not + necessarily the same as the ones of the XModel of the + XController (although it typically is a subset). + */ + void SetDocumentSlides(const css::uno::Reference& rxSlides); + + /** Return the set of pages that is currently displayed by the slide sorter. + */ + css::uno::Reference GetDocumentSlides() const; + + /** This method is called when the edit mode has changed. It calls + SetDocumentSlides() with the set of slides or master pages obtained + from the model of the XController. + */ + void UpdatePageList(); + + bool IsReadOnly() const; + + /** The current selection is saved by copying the ST_Selected state into + ST_WasSelected for slides. + */ + void SaveCurrentSelection(); + + /** The current selection is restored from the ST_WasSelected state from + the slides. + @returns + The returned region has to be repainted to reflect the updated + selection states. + */ + vcl::Region RestoreSelection(); + + /** Typically called from controller::Listener this method handles the + insertion and deletion of single pages. + @return + Returns when the given page is relevant for the current + page kind and edit mode. + */ + bool NotifyPageEvent(const SdrPage* pPage); + +private: + mutable ::osl::Mutex maMutex; + SlideSorter& mrSlideSorter; + css::uno::Reference mxSlides; + EditMode meEditMode; + mutable ::std::vector maPageDescriptors; + + /** Resize the descriptor container according to current values of + page kind and edit mode. + */ + void AdaptSize(); + + SdPage* GetPage(const sal_Int32 nCoreIndex) const; + void InsertSlide(SdPage* pPage, bool bMarkSelected); + // return if this page was marked as selected before being removed + bool DeleteSlide(const SdPage* pPage); + void UpdateIndices(const sal_Int32 nFirstIndex); +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsEnumeration.hxx b/sd/source/ui/slidesorter/inc/model/SlsEnumeration.hxx new file mode 100644 index 000000000..289f85911 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsEnumeration.hxx @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd::slidesorter::model +{ +/** Interface to generic enumerations. Designed to operate on shared + pointers. Therefore GetNextElement() returns T and not T&. +*/ +template class Enumeration +{ +public: + virtual ~Enumeration() {} + + virtual bool HasMoreElements() const = 0; + /** Returns T instead of T& so that it can handle shared pointers. + */ + virtual T GetNextElement() = 0; + virtual void Rewind() = 0; + virtual ::std::unique_ptr> Clone() = 0; +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsPageDescriptor.hxx b/sd/source/ui/slidesorter/inc/model/SlsPageDescriptor.hxx new file mode 100644 index 000000000..4f3be3b42 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsPageDescriptor.hxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +#include + +namespace com::sun::star::drawing { class XDrawPage; } + +class SdPage; +class SdrPage; + +namespace sd::slidesorter::model { + +/** Each PageDescriptor object represents the preview of one draw page, + slide, or master page of a Draw or Impress document as they are displayed + in the slide sorter. This class gives access to some associated + information like prerendered preview or position on the screen. + +

        Bounding boxes of page objects come in four varieties: + Model and screen/pixel coordinates and the bounding boxes of the actual + page objects and the larger bounding boxes that include page names and + fade symbol.

        +*/ +class PageDescriptor + : public ::std::enable_shared_from_this +{ +public: + /** Create a PageDescriptor for the given SdPage object. + @param rxPage + The page that is represented by the new PageDescriptor object. + @param pPage + The page pointer can in some situations not be detected from + rxPage, e.g. after undo of page deletion. Therefore supply it + separately. + @param nIndex + This index is displayed in the view as page number. It is not + necessarily the page index (not even when you add or subtract 1 + or use (x-1)/2 magic). + */ + PageDescriptor ( + const css::uno::Reference& rxPage, + SdPage* pPage, + const sal_Int32 nIndex); + + ~PageDescriptor(); + + /** Return the page that is represented by the descriptor as SdPage pointer . + */ + SdPage* GetPage() const { return mpPage;} + + /** Return the page that is represented by the descriptor as XDrawPage reference. + */ + const css::uno::Reference& GetXDrawPage() const { return mxPage;} + + /** Returns the index of the page as it is displayed in the view as page + number. The value may differ from the index returned by the + XDrawPage when there are hidden slides and the XIndexAccess used to + access the model filters them out. + */ + sal_Int32 GetPageIndex() const { return mnIndex;} + void SetPageIndex (const sal_Int32 nIndex); + + bool UpdateMasterPage(); + bool UpdateTransitionFlag(); + + enum State { ST_Visible, ST_Selected, ST_WasSelected, + ST_Focused, ST_MouseOver, ST_Current, ST_Excluded }; + + bool HasState (const State eState) const; + + bool SetState (const State eState, const bool bStateValue); + + /** Set the internal mbIsSelected flag to the selection state of the + page. Use this method to synchronize a page descriptor with the + page it describes and determine whether a redraw to update the + selection indicator is necessary. + @return + When the two selection states were different is + returned. When they were the same this method returns + . + */ + bool GetCoreSelection(); + + /** Set the selection flags of the SdPage objects to the corresponding + selection states of the page descriptors. + */ + void SetCoreSelection(); + + VisualState& GetVisualState() { return maVisualState;} + + ::tools::Rectangle GetBoundingBox() const; + Point GetLocation (const bool bIgnoreLocation) const; + void SetBoundingBox (const ::tools::Rectangle& rBoundingBox); + +private: + SdPage* mpPage; + css::uno::Reference mxPage; + SdrPage const* mpMasterPage; + + /** This index is displayed as page number in the view. It may or may + not be the actual page index. + */ + sal_Int32 mnIndex; + + ::tools::Rectangle maBoundingBox; + VisualState maVisualState; + + bool mbIsSelected : 1; + bool mbWasSelected : 1; + bool mbIsVisible : 1; + bool mbIsFocused : 1; + bool mbIsCurrent : 1; + bool mbIsMouseOver : 1; + bool mbHasTransition : 1; + + PageDescriptor (const PageDescriptor& rDescriptor) = delete; + + PageDescriptor& operator= (const PageDescriptor& rDescriptor) = delete; +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsPageEnumeration.hxx b/sd/source/ui/slidesorter/inc/model/SlsPageEnumeration.hxx new file mode 100644 index 000000000..6901a9ff1 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsPageEnumeration.hxx @@ -0,0 +1,95 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include + +namespace sd::slidesorter::model +{ +class SlideSorterModel; + +/** Public class of page enumerations that delegates its calls to an + implementation object that can filter pages by using a given predicate. + + @see PageEnumerationProvider + The PageEnumerationProvider has methods for creating different types + of page enumerations. +*/ +class PageEnumeration final : public Enumeration +{ +public: + /** Create a new page enumeration that enumerates a subset of the pages + of the given model. + @param rModel + The new page enumeration enumerates the pages of this model. + @param rPredicate + This predicate determines which pages to include in the + enumeration. Pages for which rPredicate returns are + exclude. + */ + typedef ::std::function PagePredicate; + static PageEnumeration Create(const SlideSorterModel& rModel, const PagePredicate& rPredicate); + + /** This copy constructor creates a copy of the given enumeration. + */ + PageEnumeration(const PageEnumeration& rEnumeration); + + virtual ~PageEnumeration() override; + + /** Create and return an exact copy of the called object. + */ + virtual ::std::unique_ptr> Clone() override; + + PageEnumeration& operator=(const PageEnumeration& rEnumeration); + + /** Return when the enumeration has more elements, i.e. it is + save to call GetNextElement() at least one more time. + */ + virtual bool HasMoreElements() const override; + + /** Return the next element of the enumeration. Call the + HasMoreElements() before to make sure that there exists at least one + more element. Calling this method with HasMoreElements() returning + is an error. + */ + virtual SharedPageDescriptor GetNextElement() override; + + /** Rewind the enumeration so that the next call to GetNextElement() + will return its first element. + */ + virtual void Rewind() override; + +private: + /// Implementation object. + ::std::unique_ptr> mpImpl; + + /** This constructor expects an implementation object that holds + the predicate that filters the pages. + */ + PageEnumeration(::std::unique_ptr>&& pImpl); +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsPageEnumerationProvider.hxx b/sd/source/ui/slidesorter/inc/model/SlsPageEnumerationProvider.hxx new file mode 100644 index 000000000..b6de98d13 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsPageEnumerationProvider.hxx @@ -0,0 +1,51 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd::slidesorter::model +{ +class SlideSorterModel; + +/** Collection of methods that create enumeration of slides. +*/ +class PageEnumerationProvider +{ +public: + /** The returned enumeration of slides iterates over all slides of the + given model. + */ + static PageEnumeration CreateAllPagesEnumeration(const SlideSorterModel& rModel); + + /** The returned enumeration of slides iterates over the currently + selected slides of the given model. + */ + static PageEnumeration CreateSelectedPagesEnumeration(const SlideSorterModel& rModel); + + /** The returned enumeration of slides iterates over the slides + (partially) inside the visible area. + */ + static PageEnumeration CreateVisiblePagesEnumeration(const SlideSorterModel& rModel); +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsSharedPageDescriptor.hxx b/sd/source/ui/slidesorter/inc/model/SlsSharedPageDescriptor.hxx new file mode 100644 index 000000000..6ee1e2b22 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsSharedPageDescriptor.hxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd::slidesorter::model +{ +class PageDescriptor; + +typedef std::shared_ptr SharedPageDescriptor; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/model/SlsVisualState.hxx b/sd/source/ui/slidesorter/inc/model/SlsVisualState.hxx new file mode 100644 index 000000000..89eae16ca --- /dev/null +++ b/sd/source/ui/slidesorter/inc/model/SlsVisualState.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd::slidesorter::model +{ +/** This class gives access to values related to the visualization of page + objects. This includes animation state when blending from one state to + another. +*/ +class VisualState +{ +public: + VisualState(const sal_Int32 nPageId); + + const Point& GetLocationOffset() const { return maLocationOffset; } + void SetLocationOffset(const Point& rPoint); + + sal_Int32 mnPageId; // For debugging + +private: + Point maLocationOffset; +}; + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlideSorterView.hxx b/sd/source/ui/slidesorter/inc/view/SlideSorterView.hxx new file mode 100644 index 000000000..0f3493ab3 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlideSorterView.hxx @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace sd::slidesorter::cache { class PageCache; } +namespace sd::slidesorter::model { class SlideSorterModel; } +namespace sd { class Window; } +namespace sd::slidesorter { class SlideSorter; } +namespace sd::slidesorter::view { + +class LayeredDevice; +class PageObjectPainter; +class ToolTip; + +class SlideSorterView final + : public sd::View +{ +public: + + /** Create a new view for the slide sorter. + @param rViewShell + This reference is simply passed to the base class and not used + by this class. + + */ + explicit SlideSorterView (SlideSorter& rSlideSorter); + void Init(); + + virtual ~SlideSorterView() override; + void Dispose(); + + SlideSorterView(const SlideSorterView&) = delete; + SlideSorterView& operator=(const SlideSorterView&) = delete; + + /** Set the general way of layouting the page objects. Note that this + method does not trigger any repaints or layouts. + */ + bool SetOrientation (const Layouter::Orientation eOrientation); + Layouter::Orientation GetOrientation() const { return meOrientation;} + + void RequestRepaint(); + void RequestRepaint (const model::SharedPageDescriptor& rDescriptor); + void RequestRepaint (const ::tools::Rectangle& rRepaintBox); + void RequestRepaint (const vcl::Region& rRepaintRegion); + + ::tools::Rectangle GetModelArea() const; + + /** Return the index of the page that is rendered at the given position. + @param rPosition + The position is expected to be in pixel coordinates. + @return + The returned index is -1 when there is no page object at the + given position. + */ + sal_Int32 GetPageIndexAtPoint (const Point& rPosition) const; + + view::Layouter& GetLayouter(); + + virtual void ModelHasChanged() override; + + /** This method is typically called before a model change takes place. + All references to model data are released. PostModelChange() has to + be called to complete the handling of the model change. When the + calls to Pre- and PostModelChange() are very close to each other you + may call HandleModelChange() instead. + */ + void PreModelChange(); + + /** This method is typically called after a model change took place. + References to model data are re-allocated. Call this method only + after PreModelChange() has been called. + */ + void PostModelChange(); + + /** This method is a convenience function that simply calls + PreModelChange() and then PostModelChange(). + */ + void HandleModelChange(); + + void HandleDrawModeChange(); + + void Resize(); + virtual void CompleteRedraw ( + OutputDevice* pDevice, + const vcl::Region& rPaintArea, + sdr::contact::ViewObjectContactRedirector* pRedirector = nullptr) override; + void Paint (OutputDevice& rDevice, const ::tools::Rectangle& rRepaintArea); + + virtual void ConfigurationChanged ( + utl::ConfigurationBroadcaster* pBroadcaster, + ConfigurationHints nHint) override; + + void HandleDataChangeEvent(); + + void Layout(); + /** This tells the view that it has to re-determine the visibility of + the page objects before painting them the next time. + */ + void InvalidatePageObjectVisibilities(); + + std::shared_ptr const & GetPreviewCache(); + + /** Return the range of currently visible page objects including the + first and last one in that range. + @return + The returned pair of page object indices is empty when the + second index is lower than the first. + */ + Range const & GetVisiblePageRange(); + + /** Add a shape to the page. Typically used from inside + PostModelChange(). + */ + // void AddSdrObject (SdrObject& rObject); + + /** Add a listener that is called when the set of visible slides. + @param rListener + When this method is called multiple times for the same listener + the second and all following calls are ignored. Each listener + is added only once. + */ + void AddVisibilityChangeListener (const Link& rListener); + + /** Remove a listener that is called when the set of visible slides changes. + @param rListener + It is safe to pass a listener that was not added or has been + removed previously. Such calls are ignored. + */ + void RemoveVisibilityChangeListener (const Link& rListener); + + /** The page under the mouse is not highlighted in some contexts. Call + this method on context changes. + */ + void UpdatePageUnderMouse (); + void UpdatePageUnderMouse (const Point& rMousePosition); + void SetPageUnderMouse (const model::SharedPageDescriptor& rpDescriptor); + + bool SetState ( + const model::SharedPageDescriptor& rpDescriptor, + const model::PageDescriptor::State eState, + const bool bStateValue); + + void UpdateOrientation(); + + std::shared_ptr const & GetPageObjectPainter(); + const std::shared_ptr& GetLayeredDevice() const { return mpLayeredDevice;} + + class DrawLock + { + public: + DrawLock (SlideSorter const & rSlideSorter); + ~DrawLock(); + /** When the DrawLock is disposed then it will not request a repaint + on destruction. + */ + void Dispose(); + private: + view::SlideSorterView& mrView; + VclPtr mpWindow; + }; + + ToolTip& GetToolTip() const; + + virtual void DragFinished (sal_Int8 nDropAction) override; + +private: + SlideSorter& mrSlideSorter; + model::SlideSorterModel& mrModel; + bool mbIsDisposed; + ::std::unique_ptr mpLayouter; + bool mbPageObjectVisibilitiesValid; + std::shared_ptr mpPreviewCache; + std::shared_ptr mpLayeredDevice; + Range maVisiblePageRange; + Size maPreviewSize; + bool mbPreciousFlagUpdatePending; + Layouter::Orientation meOrientation; + model::SharedPageDescriptor mpPageUnderMouse; + std::shared_ptr mpPageObjectPainter; + vcl::Region maRedrawRegion; + SharedILayerPainter mpBackgroundPainter; + std::unique_ptr> mpToolTip; + bool mbIsRearrangePending; + ::std::vector> maVisibilityChangeListeners; + + /** Determine the visibility of all page objects. + */ + void DeterminePageObjectVisibilities(); + + void UpdatePreciousFlags(); + void RequestRearrange(); + void Rearrange(); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsILayerPainter.hxx b/sd/source/ui/slidesorter/inc/view/SlsILayerPainter.hxx new file mode 100644 index 000000000..57b90af0a --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsILayerPainter.hxx @@ -0,0 +1,53 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +class OutputDevice; +namespace tools { class Rectangle; } + +namespace sd::slidesorter::view { + +class ILayerInvalidator +{ +public: + virtual ~ILayerInvalidator() {} + + virtual void Invalidate (const ::tools::Rectangle& rInvalidationBox) = 0; +}; +typedef std::shared_ptr SharedILayerInvalidator; + +class ILayerPainter +{ +public: + virtual ~ILayerPainter() {} + + virtual void SetLayerInvalidator ( + const SharedILayerInvalidator& rpInvalidator) = 0; + virtual void Paint ( + OutputDevice& rDevice, + const ::tools::Rectangle& rRepaintArea) = 0; +}; +typedef std::shared_ptr SharedILayerPainter; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsInsertAnimator.hxx b/sd/source/ui/slidesorter/inc/view/SlsInsertAnimator.hxx new file mode 100644 index 000000000..c74d06cb9 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsInsertAnimator.hxx @@ -0,0 +1,59 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd::slidesorter::view +{ +class InsertPosition; + +/** Animate the positions of page objects to make room at the insert + position while a move or copy operation takes place. +*/ +class InsertAnimator +{ +public: + explicit InsertAnimator(SlideSorter& rSlideSorter); + InsertAnimator(const InsertAnimator&) = delete; + InsertAnimator& operator=(const InsertAnimator&) = delete; + + /** Set the position at which we have to make room for the display of an + icon. + */ + void SetInsertPosition(const InsertPosition& rInsertPosition); + + /** Restore the normal position of all page objects. + @param eMode + This flag controls whether to start an animation that ends in the + normal positions of all slides (AM_Animated) or to restore the + normal positions immediately (AM_Immediate). + */ + void Reset(const controller::Animator::AnimationMode eMode); + +private: + class Implementation; + std::shared_ptr mpImplementation; +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsInsertionIndicatorOverlay.hxx b/sd/source/ui/slidesorter/inc/view/SlsInsertionIndicatorOverlay.hxx new file mode 100644 index 000000000..3f4cc2218 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsInsertionIndicatorOverlay.hxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +class OutputDevice; +class SdTransferable; + +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::view { + +class FramePainter; + +/** The insertion indicator is painted as a vertical or horizontal bar + in the space between slides. +*/ +class InsertionIndicatorOverlay final + : public ILayerPainter, + public std::enable_shared_from_this +{ +public: + InsertionIndicatorOverlay (SlideSorter& rSlideSorter); + virtual ~InsertionIndicatorOverlay() override; + + virtual void SetLayerInvalidator (const SharedILayerInvalidator& rpInvalidator) override; + + void Create (const SdTransferable* pTransferable); + + /** Given a position in model coordinates this method calculates the + insertion marker both as an index in the document and as a location + used for drawing the insertion indicator. + */ + void SetLocation (const Point& rPosition); + + Size GetSize() const; + + virtual void Paint ( + OutputDevice& rDevice, + const ::tools::Rectangle& rRepaintArea) override; + + bool IsVisible() const { return mbIsVisible;} + void Hide(); + void Show(); + + ::tools::Rectangle GetBoundingBox() const; + +private: + SlideSorter& mrSlideSorter; + bool mbIsVisible; + SharedILayerInvalidator mpLayerInvalidator; + // Center of the insertion indicator. + Point maLocation; + BitmapEx maIcon; + std::unique_ptr mpShadowPainter; + + Point PaintRepresentatives ( + OutputDevice& rContent, + const Size& rPreviewSize, + const sal_Int32 nOffset, + const ::std::vector& rPages) const; + void PaintPageCount ( + OutputDevice& rDevice, + const sal_Int32 nSelectionCount, + const Size& rPreviewSize, + const Point& rFirstPageOffset) const; + /** Setup the insertion indicator by creating the icon. It consists of + scaled down previews of some of the selected pages. + */ + void Create ( + const ::std::vector& rPages, + const sal_Int32 nSelectionCount); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsLayouter.hxx b/sd/source/ui/slidesorter/inc/view/SlsLayouter.hxx new file mode 100644 index 000000000..b91ae83c5 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsLayouter.hxx @@ -0,0 +1,237 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include + +namespace sd { class Window; } +namespace sd::slidesorter::model { class SlideSorterModel; } +namespace sd::slidesorter::view { class PageObjectLayouter; } +namespace sd::slidesorter::view { class Theme; } + +namespace sd::slidesorter::view { + +class InsertPosition; + +/** Calculate the size and position of page objects displayed by a slide + sorter. The layouter takes into account various input values: + 1.) Size of the window in which the slide sorter is displayed. + 2.) Desired and minimal and maximal widths of page objects. + 3.) Minimal and maximal number of columns. + 4.) Vertical and horizontal gaps between objects in adjacent columns. + 5.) Borders around every page object. + 6.) Vertical and horizontal borders between enclosing page and outer + page objects. + From these, it calculates various output values: + 1.) The width of page objects. + 2.) The number of columns. + 3.) The size of the enclosing page. + +

        Sizes and lengths are all in pixel except where explicitly stated + otherwise.

        + +

        The GetIndex... methods may return indices that are larger than or + equal to (zero based) the number of pages. This is so because the + number of pages is not known to the class instances. Indices are + calculated with reference to the general grid layout of page + objects.

        +*/ +class Layouter +{ +public: + enum Orientation { HORIZONTAL, VERTICAL, GRID }; + + Layouter ( + sd::Window *rpWindow, + const std::shared_ptr& rpTheme); + ~Layouter(); + + std::shared_ptr const & GetPageObjectLayouter() const; + /** Set the interval of valid column counts. When nMinimalColumnCount + <= nMaximalColumnCount is not fulfilled then the call is ignored. + @param nMinimalColumnCount + The default value is 1. The question whether higher values make + any sense is left to the caller. + @param nMaximalColumnCount + The default value is 5. + */ + void SetColumnCount (sal_Int32 nMinimalColumnCount, + sal_Int32 nMaximalColumnCount); + + /** Central method of this class. It takes the input values and + calculates the output values. Both given sizes must not be 0 in any + dimension or the call is ignored. + @param eOrientation + This defines the generally layout and specifies whether there may + be more than one row or more than one column. + @param rWindowSize + The size of the window in pixels that the slide sorter is + displayed in. This can differ from the size of mpWindow during + detection of whether or not the scroll bars should be visible. + @param rPreviewModelSize + Size of each page in model coordinates. + @param rpWindow + The map mode of this window is adapted to the new layout of the + page objects. + @return + The return value indicates whether the Get... methods can be + used to obtain valid values (). + */ + bool Rearrange ( + const Orientation eOrientation, + const Size& rWindowSize, + const Size& rPreviewModelSize, + const sal_uInt32 nPageCount); + + /** Return the number of columns. + */ + sal_Int32 GetColumnCount() const; + + sal_Int32 GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const; + + Size const & GetPageObjectSize() const; + + /** Return the bounding box in window coordinates of the nIndex-th page + object. + */ + ::tools::Rectangle GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap) const; + + /** Return the bounding box in model coordinates of the page that + contains the given amount of page objects. + */ + ::tools::Rectangle GetTotalBoundingBox() const; + + /** Return the index of the first fully or partially visible page + object. This takes into account only the vertical dimension. + @return + The second index may be larger than the number of existing + page objects. + */ + Range GetRangeOfVisiblePageObjects (const ::tools::Rectangle& rVisibleArea) const; + + /** Return the index of the page object that is rendered at the given + point. + @param rPosition + The position is expected to be in model coordinates relative to + the page origin. + @param bIncludePageBorders + When then include the page borders into the calculation, + i.e. when a point lies in the border of a page object but not on + the actual page area the index of that page is returned; + otherwise -1 would be returned to indicate that no page object + has been hit. + @param bClampToValidRange + When then values outside the valid range [0,mnPageCount) + are mapped to 0 (when smaller than 0) or mnPageCount-1 when + equal to or larger than mnPageCount. + When then -1 is returned for values outside the valid range. + @return + The returned index may be larger than the number of existing + page objects. + */ + sal_Int32 GetIndexAtPoint ( + const Point& rModelPosition, + const bool bIncludePageBorders, + const bool bClampToValidRange = true) const; + + /** Return an object that describes the logical and visual properties of + where to do an insert operation when the user would release the + mouse button at the given position after a drag operation and of + where and how to display an insertion indicator. + @param rModelPosition + The position in the model coordinate system for which to + determine the insertion page index. The position does not have + to be over a page object to return a valid value. + @param rIndicatorSize + The size of the insertion indicator. This size is used to adapt + the location when at the left or right of a row or at the top or + bottom of a column. + @param rModel + The model is used to get access to the selection states of the + pages. This in turn is used to determine the visual bounding + boxes. + */ + InsertPosition GetInsertPosition ( + const Point& rModelPosition, + const Size& rIndicatorSize, + model::SlideSorterModel const & rModel) const; + + Range GetValidHorizontalSizeRange() const; + Range GetValidVerticalSizeRange() const; + + class Implementation; + +private: + std::unique_ptr mpImplementation; + VclPtr mpWindow; +}; + +/** Collect all values concerning the logical and visual properties of the + insertion position that is used for drag-and-drop and copy-and-paste. +*/ +class InsertPosition +{ +public: + InsertPosition(); + bool operator== (const InsertPosition& rInsertPosition) const; + bool operator!= (const InsertPosition& rInsertPosition) const; + + void SetLogicalPosition ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const sal_Int32 nIndex, + const bool bIsAtRunStart, + const bool bIsAtRunEnd, + const bool bIsExtraSpaceNeeded); + void SetGeometricalPosition( + const Point& rLocation, + const Point& rLeadingOffset, + const Point& rTrailingOffset); + + sal_Int32 GetRow() const { return mnRow; } + sal_Int32 GetColumn() const { return mnColumn; } + sal_Int32 GetIndex() const { return mnIndex; } + const Point& GetLocation() const { return maLocation; } + const Point& GetLeadingOffset() const { return maLeadingOffset; } + const Point& GetTrailingOffset() const { return maTrailingOffset; } + bool IsAtRunStart() const { return mbIsAtRunStart; } + bool IsAtRunEnd() const { return mbIsAtRunEnd; } + bool IsExtraSpaceNeeded() const { return mbIsExtraSpaceNeeded; } + +private: + sal_Int32 mnRow; + sal_Int32 mnColumn; + sal_Int32 mnIndex; + bool mbIsAtRunStart : 1; + bool mbIsAtRunEnd : 1; + bool mbIsExtraSpaceNeeded : 1; + Point maLocation; + Point maLeadingOffset; + Point maTrailingOffset; +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsPageObjectLayouter.hxx b/sd/source/ui/slidesorter/inc/view/SlsPageObjectLayouter.hxx new file mode 100644 index 000000000..8bb77a988 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsPageObjectLayouter.hxx @@ -0,0 +1,144 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace vcl { class Font; } +namespace sd { class Window; } + +namespace sd::slidesorter::view { + +/** In contrast to the Layouter that places page objects in the view, the + PageObjectLayouter places the parts of individual page objects like page + number area, borders, preview. +*/ +class PageObjectLayouter +{ +public: + /** Create a new PageObjectLayouter object. + @param rPageObjectSize + In general either the width or the height will be 0 in order to + signal that this size component has to be calculated from the other. + This calculation will make the preview as large as possible. + @param nPageCount + The page count is used to determine how wide the page number + area has to be, how many digits to except for the largest page number. + */ + PageObjectLayouter( + const Size& rPageObjectWindowSize, + const Size& rPreviewModelSize, + sd::Window *pWindow, + const sal_Int32 nPageCount); + ~PageObjectLayouter(); + + enum class Part { + // The focus indicator is painted outside the actual page object. + FocusIndicator, + // This is the outer bounding box that includes the preview, page + // number, title. + PageObject, + // Bounding box of the actual preview. + Preview, + // Bounding box of the page number. + PageNumber, + // Indicator whether or not there is a slide transition associated + // with this slide. + TransitionEffectIndicator, + // Indicator whether or not there is a custom animation associated + // with this slide. + CustomAnimationEffectIndicator + }; + /** Two coordinate systems are supported. They differ only in + translation not in scale. Both relate to pixel values in the window. + A position in the model coordinate system does not change when the window content is + scrolled up or down. In the window coordinate system (relative + to the top left point of the window)scrolling leads to different values. + */ + enum CoordinateSystem { + WindowCoordinateSystem, + ModelCoordinateSystem + }; + + /** Return the bounding box of the page object or one of its graphical + parts. + @param rWindow + This device is used to translate between model and window + coordinates. + @param rpPageDescriptor + The page for which to calculate the bounding box. This may be + NULL. When it is NULL then a generic bounding box is calculated + for the location (0,0). + @param ePart + The part of the page object for which to return the bounding + box. + @param eCoordinateSystem + The bounding box can be returned in model and in pixel + (window) coordinates. + @param bIgnoreLocation + Return a position ignoring the slides' location, ie. as if + we were the first slide. + */ + ::tools::Rectangle GetBoundingBox ( + const model::SharedPageDescriptor& rpPageDescriptor, + const Part ePart, + const CoordinateSystem eCoordinateSystem, + bool bIgnoreLocation = false); + + /// the size of the embedded preview: position independent, in window coordinate system + Size GetPreviewSize(); + + /// the maximum size of each tile, also position independent, in window coordinate system + Size GetGridMaxSize(); + + const Image& GetTransitionEffectIcon() const { return maTransitionEffectIcon;} + const Image& GetCustomAnimationEffectIcon() const { return maCustomAnimationEffectIcon;} + +private: + ::tools::Rectangle GetBoundingBox ( + const Point& rPageObjectLocation, + const Part ePart, + const CoordinateSystem eCoordinateSystem); + +private: + VclPtr mpWindow; + ::tools::Rectangle maFocusIndicatorBoundingBox; + ::tools::Rectangle maPageObjectBoundingBox; + ::tools::Rectangle maPageNumberAreaBoundingBox; + ::tools::Rectangle maPreviewBoundingBox; + ::tools::Rectangle maTransitionEffectBoundingBox; + ::tools::Rectangle maCustomAnimationEffectBoundingBox; + const Image maTransitionEffectIcon; + const Image maCustomAnimationEffectIcon; + const std::shared_ptr mpPageNumberFont; + + Size GetPageNumberAreaSize (const int nPageCount); + ::tools::Rectangle CalculatePreviewBoundingBox ( + Size& rPageObjectSize, + const Size& rPreviewModelSize, + const sal_Int32 nPageNumberAreaWidth, + const sal_Int32 nFocusIndicatorWidth); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsPageObjectPainter.hxx b/sd/source/ui/slidesorter/inc/view/SlsPageObjectPainter.hxx new file mode 100644 index 000000000..747c09500 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsPageObjectPainter.hxx @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd::slidesorter::cache { class PageCache; } +namespace sd::slidesorter { class SlideSorter; } + +namespace sd::slidesorter::view { + +class Layouter; +class PageObjectLayouter; +class FramePainter; + +class PageObjectPainter +{ +public: + PageObjectPainter (const SlideSorter& rSlideSorter); + ~PageObjectPainter(); + + void PaintPageObject ( + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor); + + /** Called when the theme changes, either because it is replaced with + another or because the system colors have changed. So, even when + the given theme is the same object as the one already in use by this + painter everything that depends on the theme is updated. + */ + void SetTheme (const std::shared_ptr& rpTheme); + + /** Return a preview bitmap for the given page descriptor. When the + page is excluded from the show then the preview is marked + accordingly. + @rpDescriptor + Defines the page for which to return the preview. + @pReferenceDevice + When not then this reference device is used to created a + compatible bitmap. + @return + The returned bitmap may have a different size then the preview area. + */ + BitmapEx GetPreviewBitmap ( + const model::SharedPageDescriptor& rpDescriptor, + const OutputDevice* pReferenceDevice) const; + +private: + const Layouter& mrLayouter; + std::shared_ptr mpCache; + std::shared_ptr mpTheme; + std::shared_ptr mpPageNumberFont; + std::unique_ptr mpShadowPainter; + std::unique_ptr mpFocusBorderPainter; + + void PaintBackground ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const; + void PaintPreview ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const; + void PaintPageNumber ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const; + static void PaintTransitionEffect ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor); + static void PaintCustomAnimationEffect ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor); + void PaintBorder ( + OutputDevice& rDevice, + const Theme::GradientColorType eColorType, + const ::tools::Rectangle& rBox) const; + void PaintBackgroundDetail( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const; + + static BitmapEx CreateMarkedPreview( + const Size& rSize, + const BitmapEx& rPreview, + const BitmapEx& rOverlay, + const OutputDevice* pReferenceDevice); + + /** Update the local pointer to the page object layouter to the + one owned by the general layouter. + Return when after the call we have a valid page object layouter. + */ + bool UpdatePageObjectLayouter(); +}; + +} // end of namespace sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsTheme.hxx b/sd/source/ui/slidesorter/inc/view/SlsTheme.hxx new file mode 100644 index 000000000..efb7b2a3e --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsTheme.hxx @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +#include + +namespace vcl { class Font; } + +namespace sd::slidesorter::controller { class Properties; } + +namespace sd::slidesorter::view { + +const int Theme_FocusIndicatorWidth = 3; + +/** Collection of colors and styles that are used to paint the slide sorter + view. +*/ +class Theme +{ +public: + Theme (const std::shared_ptr& rpProperties); + + /** Call this method to update some colors as response to a change of + a system color change. + */ + void Update ( + const std::shared_ptr& rpProperties); + + // BitmapEx GetInsertIndicatorIcon() const; + + enum FontType { + Font_PageNumber, + Font_PageCount + }; + static std::shared_ptr GetFont ( + const FontType eType, + const OutputDevice& rDevice); + + enum ColorType { + Color_Background, + Color_PageNumberDefault, + Color_PageNumberHover, + Color_PageNumberHighContrast, + Color_PageNumberBrightBackground, + Color_PageNumberDarkBackground, + Color_Selection, + Color_PreviewBorder, + Color_PageCountFontColor, + ColorType_Size_ + }; + Color GetColor (const ColorType eType); + + enum GradientColorType { + Gradient_NormalPage, + Gradient_SelectedPage, + Gradient_SelectedAndFocusedPage, + Gradient_MouseOverPage, + Gradient_MouseOverSelected, + Gradient_MouseOverSelectedAndFocusedPage, + Gradient_FocusedPage, + GradientColorType_Size_ + }; + enum class GradientColorClass { + Border1, + Border2, + Fill1, + Fill2 + }; + Color GetGradientColor ( + const GradientColorType eType, + const GradientColorClass eClass); + void SetGradient ( + const GradientColorType eType, + const Color aBaseColor, + const sal_Int32 nSaturationOverride, + const sal_Int32 nBrightnessOverride, + const sal_Int32 nFillStartOffset, + const sal_Int32 nFillEndOffset, + const sal_Int32 nBorderStartOffset, + const sal_Int32 nBorderEndOffset); + + enum IconType + { + Icon_RawShadow, + Icon_RawInsertShadow, + Icon_HideSlideOverlay, + Icon_FocusBorder, + IconType_Size_ + }; + const BitmapEx& GetIcon (const IconType eType); + +private: + class GradientDescriptor + { + public: + Color maFillColor1; + Color maFillColor2; + Color maBorderColor1; + Color maBorderColor2; + }; + Color maBackgroundColor; + ::std::vector maGradients; + ::std::vector maIcons; + ::std::vector maColor; + + GradientDescriptor& GetGradient (const GradientColorType eType); + /** Guarded initialization of the specified icon in the maIcons + container. + */ + void InitializeIcon(const IconType eType, const OUString& rResourceId); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/inc/view/SlsToolTip.hxx b/sd/source/ui/slidesorter/inc/view/SlsToolTip.hxx new file mode 100644 index 000000000..6c3557e64 --- /dev/null +++ b/sd/source/ui/slidesorter/inc/view/SlsToolTip.hxx @@ -0,0 +1,75 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::view +{ +/** Manage the display of tool tips. The tool tip text changes when the + mouse is moved from slide to slide or from button to button. + After the mouse enters a slide the first display of the tool tip is + delayed for a short time in order to not draw attention from the slide + or its button bar. +*/ +class ToolTip +{ +public: + ToolTip(SlideSorter& rSlideSorter); + ~ToolTip(); + + /** Set a new page. This modifies the default help text. After a page + change a timer is started to delay the display of the tool tip for + the new page. + @param rpPage + When this is empty then the tool tip is hidden. + */ + void SetPage(const model::SharedPageDescriptor& rpPage); + + /** Hide the tool tip. + @return + Returns whether the tool tip was visible at the time this method + was called. + */ + bool Hide(); + +private: + SlideSorter& mrSlideSorter; + model::SharedPageDescriptor mpDescriptor; + OUString msCurrentHelpText; + void* mnHelpWindowHandle; + Timer maShowTimer; + Timer maHiddenTimer; + + void DoShow(); + + DECL_LINK(DelayTrigger, Timer*, void); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlideSorterModel.cxx b/sd/source/ui/slidesorter/model/SlideSorterModel.cxx new file mode 100644 index 000000000..4d3e79656 --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlideSorterModel.cxx @@ -0,0 +1,676 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::slidesorter::model { + +namespace { + bool PrintModel (const SlideSorterModel& rModel) + { + for (sal_Int32 nIndex=0,nCount=rModel.GetPageCount(); nIndexGetPageIndex() << " " + << pDescriptor->GetVisualState().mnPageId << " " + << FromCoreIndex(pDescriptor->GetPage()->GetPageNum()) + << " " << pDescriptor->GetPage()); + } + else + { + SAL_INFO("sd.sls", nIndex); + } + } + + return true; + } + bool CheckModel (const SlideSorterModel& rModel) + { + for (sal_Int32 nIndex=0,nCount=rModel.GetPageCount(); nIndexGetPageIndex()) + { + PrintModel(rModel); + assert(nIndex == pDescriptor->GetPageIndex()); + return false; + } + if (nIndex != pDescriptor->GetVisualState().mnPageId) + { + PrintModel(rModel); + assert(nIndex == pDescriptor->GetVisualState().mnPageId); + return false; + } + } + + return true; + } +} + +namespace { + +void collectUIInformation(const OUString& num, const OUString& rAction) +{ + EventDescription aDescription; + aDescription.aID = "impress_win_or_draw_win"; + aDescription.aParameters = {{"POS", num}}; + aDescription.aAction = rAction; + aDescription.aKeyWord = "ImpressWindowUIObject"; + aDescription.aParent = "MainWindow"; + + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +SlideSorterModel::SlideSorterModel (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + meEditMode(EditMode::Page), + maPageDescriptors(0) +{ +} + +SlideSorterModel::~SlideSorterModel() +{ + ClearDescriptorList (); +} + +void SlideSorterModel::Dispose() +{ + ClearDescriptorList (); +} + +SdDrawDocument* SlideSorterModel::GetDocument() +{ + if (mrSlideSorter.GetViewShellBase() != nullptr) + return mrSlideSorter.GetViewShellBase()->GetDocument(); + else + return nullptr; +} + +bool SlideSorterModel::SetEditMode (EditMode eEditMode) +{ + bool bEditModeChanged = false; + if (meEditMode != eEditMode) + { + meEditMode = eEditMode; + UpdatePageList(); + bEditModeChanged = true; + } + return bEditModeChanged; +} + +sal_Int32 SlideSorterModel::GetPageCount() const +{ + return maPageDescriptors.size(); +} + +SharedPageDescriptor SlideSorterModel::GetPageDescriptor ( + const sal_Int32 nPageIndex, + const bool bCreate) const +{ + ::osl::MutexGuard aGuard (maMutex); + + SharedPageDescriptor pDescriptor; + + if (nPageIndex>=0 && nPageIndex( + Reference(mxSlides->getByIndex(nPageIndex),UNO_QUERY), + pPage, + nPageIndex); + maPageDescriptors[nPageIndex] = pDescriptor; + } + } + + return pDescriptor; +} + +sal_Int32 SlideSorterModel::GetIndex (const Reference& rxSlide) const +{ + ::osl::MutexGuard aGuard (maMutex); + + // First try to guess the right index. + Reference xSet (rxSlide, UNO_QUERY); + if (xSet.is()) + { + try + { + const Any aNumber (xSet->getPropertyValue("Number")); + sal_Int16 nNumber (-1); + aNumber >>= nNumber; + nNumber -= 1; + SharedPageDescriptor pDescriptor (GetPageDescriptor(nNumber, false)); + if (pDescriptor + && pDescriptor->GetXDrawPage() == rxSlide) + { + return nNumber; + } + } + catch (uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd"); + } + } + + // Guess was wrong, iterate over all slides and search for the right + // one. + const sal_Int32 nCount (maPageDescriptors.size()); + for (sal_Int32 nIndex=0; nIndexGetXDrawPage() == rxSlide) + return nIndex; + } + + return -1; +} + +sal_Int32 SlideSorterModel::GetIndex (const SdrPage* pPage) const +{ + if (pPage == nullptr) + return -1; + + ::osl::MutexGuard aGuard (maMutex); + + // First try to guess the right index. + sal_Int16 nNumber ((pPage->GetPageNum()-1)/2); + SharedPageDescriptor pDescriptor (GetPageDescriptor(nNumber, false)); + if (pDescriptor + && pDescriptor->GetPage() == pPage) + { + return nNumber; + } + + // Guess was wrong, iterate over all slides and search for the right + // one. + const sal_Int32 nCount (maPageDescriptors.size()); + for (sal_Int32 nIndex=0; nIndexGetPage() == pPage) + return nIndex; + } + + return -1; +} + +sal_uInt16 SlideSorterModel::GetCoreIndex (const sal_Int32 nIndex) const +{ + SharedPageDescriptor pDescriptor (GetPageDescriptor(nIndex)); + if (pDescriptor) + return pDescriptor->GetPage()->GetPageNum(); + else + return mxSlides->getCount()*2+1; +} + +/** For now this method uses a trivial algorithm: throw away all descriptors + and create them anew (on demand). The main problem that we are facing + when designing a better algorithm is that we can not compare pointers to + pages stored in the PageDescriptor objects and those obtained from the + document: pages may have been deleted and others may have been created + at the exact same memory locations. +*/ +void SlideSorterModel::Resync() +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check if document and this model really differ. + bool bIsUpToDate (true); + SdDrawDocument* pDocument = GetDocument(); + if (pDocument!=nullptr && maPageDescriptors.size()==pDocument->GetSdPageCount(PageKind::Standard)) + { + for (sal_Int32 nIndex=0,nCount=maPageDescriptors.size(); nIndexGetPage() + != GetPage(nIndex)) + { + SAL_INFO("sd.sls", "page " << nIndex << " differs"); + bIsUpToDate = false; + break; + } + } + } + else + { + bIsUpToDate = false; + } + + if ( ! bIsUpToDate) + { + SynchronizeDocumentSelection(); // Try to make the current selection persistent. + ClearDescriptorList (); + AdaptSize(); + SynchronizeModelSelection(); + mrSlideSorter.GetController().GetPageSelector().CountSelectedPages(); + } + CheckModel(*this); +} + +void SlideSorterModel::ClearDescriptorList() +{ + ::std::vector aDescriptors; + + { + ::osl::MutexGuard aGuard (maMutex); + aDescriptors.swap(maPageDescriptors); + } + + for (auto& rxDescriptor : aDescriptors) + { + if (rxDescriptor != nullptr) + { + if (rxDescriptor.use_count() > 1) + { + SAL_INFO( + "sd.sls", + "trying to delete page descriptor that is still used with" + " count " << rxDescriptor.use_count()); + // No assertion here because that can hang the office when + // opening a dialog from here. + } + rxDescriptor.reset(); + } + } +} + +void SlideSorterModel::SynchronizeDocumentSelection() +{ + ::osl::MutexGuard aGuard (maMutex); + + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + const bool bIsSelected (pDescriptor->HasState(PageDescriptor::ST_Selected)); + pDescriptor->GetPage()->SetSelected(bIsSelected); + } +} + +void SlideSorterModel::SynchronizeModelSelection() +{ + ::osl::MutexGuard aGuard (maMutex); + + PageEnumeration aAllPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this)); + while (aAllPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aAllPages.GetNextElement()); + const bool bIsSelected (pDescriptor->GetPage()->IsSelected()); + pDescriptor->SetState(PageDescriptor::ST_Selected, bIsSelected); + } +} + +void SlideSorterModel::SetDocumentSlides ( + const Reference& rxSlides) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Make the current selection persistent and then release the + // current set of pages. + SynchronizeDocumentSelection(); + mxSlides = nullptr; + ClearDescriptorList (); + + // Reset the current page to cause everybody to release references to it. + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange(-1); + + // Set the new set of pages. + mxSlides = rxSlides; + AdaptSize(); + SynchronizeModelSelection(); + mrSlideSorter.GetController().GetPageSelector().CountSelectedPages(); + + model::PageEnumeration aSelectedPages ( + model::PageEnumerationProvider::CreateSelectedPagesEnumeration(*this)); + if (aSelectedPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + pDescriptor->GetPage()); + } + + ViewShell* pViewShell = mrSlideSorter.GetViewShell(); + if (pViewShell != nullptr) + { + SdPage* pPage = pViewShell->getCurrentPage(); + if (pPage != nullptr) + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + pPage); + else + { + // No current page. This can only be when the slide sorter is + // the main view shell. Get current slide form frame view. + const FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + pFrameView->GetSelectedPage()); + else + { + // No frame view. As a last resort use the first slide as + // current slide. + mrSlideSorter.GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + sal_Int32(0)); + } + } + } + + mrSlideSorter.GetController().GetSlotManager()->NotifyEditModeChange(); +} + +Reference SlideSorterModel::GetDocumentSlides() const +{ + ::osl::MutexGuard aGuard (maMutex); + return mxSlides; +} + +void SlideSorterModel::UpdatePageList() +{ + ::osl::MutexGuard aGuard (maMutex); + + Reference xPages; + + // Get the list of pages according to the edit mode. + Reference xController (mrSlideSorter.GetXController()); + if (xController.is()) + { + switch (meEditMode) + { + case EditMode::MasterPage: + { + Reference xSupplier ( + xController->getModel(), UNO_QUERY); + if (xSupplier.is()) + { + xPages = xSupplier->getMasterPages(); + } + } + break; + + case EditMode::Page: + { + Reference xSupplier ( + xController->getModel(), UNO_QUERY); + if (xSupplier.is()) + { + xPages = xSupplier->getDrawPages(); + } + } + break; + + default: + // We should never get here. + assert(false); + break; + } + } + + mrSlideSorter.GetController().SetDocumentSlides(xPages); +} + +void SlideSorterModel::AdaptSize() +{ + if (mxSlides.is()) + maPageDescriptors.resize(mxSlides->getCount()); + else + maPageDescriptors.resize(0); +} + +bool SlideSorterModel::IsReadOnly() const +{ + if (mrSlideSorter.GetViewShellBase() != nullptr + && mrSlideSorter.GetViewShellBase()->GetDocShell()) + return mrSlideSorter.GetViewShellBase()->GetDocShell()->IsReadOnly(); + else + return true; +} + +void SlideSorterModel::SaveCurrentSelection() +{ + PageEnumeration aPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this)); + while (aPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aPages.GetNextElement()); + pDescriptor->SetState( + PageDescriptor::ST_WasSelected, + pDescriptor->HasState(PageDescriptor::ST_Selected)); + } +} + +vcl::Region SlideSorterModel::RestoreSelection() +{ + vcl::Region aRepaintRegion; + PageEnumeration aPages (PageEnumerationProvider::CreateAllPagesEnumeration(*this)); + while (aPages.HasMoreElements()) + { + SharedPageDescriptor pDescriptor (aPages.GetNextElement()); + if (pDescriptor->SetState( + PageDescriptor::ST_Selected, + pDescriptor->HasState(PageDescriptor::ST_WasSelected))) + { + aRepaintRegion.Union(pDescriptor->GetBoundingBox()); + } + } + return aRepaintRegion; +} + +bool SlideSorterModel::NotifyPageEvent (const SdrPage* pSdrPage) +{ + ::osl::MutexGuard aGuard (maMutex); + + SdPage* pPage = const_cast(dynamic_cast(pSdrPage)); + if (pPage == nullptr) + return false; + + // We are only interested in pages that are currently served by this + // model. + if (pPage->GetPageKind() != PageKind::Standard) + return false; + if (pPage->IsMasterPage() != (meEditMode==EditMode::MasterPage)) + return false; + + //NotifyPageEvent is called for add, remove, *and* change position so for + //the change position case we must ensure we don't end up with the slide + //duplicated in our list + bool bSelected = DeleteSlide(pPage); + if (pPage->IsInserted()) + { + InsertSlide(pPage, bSelected); + } + CheckModel(*this); + + return true; +} + +void SlideSorterModel::InsertSlide(SdPage* pPage, bool bMarkSelected) +{ + // Find the index at which to insert the given page. + sal_uInt16 nCoreIndex (pPage->GetPageNum()); + sal_Int32 nIndex (FromCoreIndex(nCoreIndex)); + if (pPage != GetPage(nIndex)) + return; + + // Check that the pages in the document before and after the given page + // are present in this model. + if (nIndex>0) + if (GetPage(nIndex-1) != GetPageDescriptor(nIndex-1)->GetPage()) + return; + if (nIndex < static_cast(maPageDescriptors.size()) -1) + if (GetPage(nIndex+1) != GetPageDescriptor(nIndex)->GetPage()) + return; + + auto iter = maPageDescriptors.begin() + nIndex; + + // Insert the given page at index nIndex + iter = maPageDescriptors.insert( + iter, + std::make_shared( + Reference(mxSlides->getByIndex(nIndex),UNO_QUERY), + pPage, + nIndex)); + + if (bMarkSelected) + (*iter)->SetState(PageDescriptor::ST_Selected, true); + + // Update page indices. + UpdateIndices(nIndex+1); +} + +bool SlideSorterModel::DeleteSlide (const SdPage* pPage) +{ + sal_Int32 nIndex(0); + + // Caution, GetIndex() may be negative since it uses GetPageNumber()-1 + // for calculation, so do this only when page is inserted, else the + // GetPageNumber() will be zero and thus GetIndex() == -1 + if(pPage->IsInserted()) + { + nIndex = GetIndex(pPage); + } + else + { + // if not inserted, search for page + for(; nIndex < static_cast(maPageDescriptors.size()); nIndex++) + { + if(maPageDescriptors[nIndex]->GetPage() == pPage) + { + break; + } + } + } + + bool bMarkedSelected(false); + + if(nIndex >= 0 && o3tl::make_unsigned(nIndex) < maPageDescriptors.size()) + { + if (maPageDescriptors[nIndex]) + if (maPageDescriptors[nIndex]->GetPage() != pPage) + return false; + + auto iter = maPageDescriptors.begin() + nIndex; + bMarkedSelected = (*iter)->HasState(PageDescriptor::ST_Selected); + maPageDescriptors.erase(iter); + UpdateIndices(nIndex); + + collectUIInformation(OUString::number(nIndex + 1), "Delete_Slide_or_Page"); + } + return bMarkedSelected; +} + +void SlideSorterModel::UpdateIndices (const sal_Int32 nFirstIndex) +{ + for (sal_Int32 nDescriptorIndex=0,nCount=maPageDescriptors.size(); + nDescriptorIndexGetPageIndex()!=nDescriptorIndex) + { + assert(rpDescriptor->GetPageIndex()==nDescriptorIndex); + } + } + else + { + rpDescriptor->SetPageIndex(nDescriptorIndex); + } + } + } +} + +SdPage* SlideSorterModel::GetPage (const sal_Int32 nSdIndex) const +{ + SdDrawDocument* pModel = const_cast(this)->GetDocument(); + if (pModel != nullptr) + { + if (meEditMode == EditMode::Page) + return pModel->GetSdPage (static_cast(nSdIndex), PageKind::Standard); + else + return pModel->GetMasterSdPage (static_cast(nSdIndex), PageKind::Standard); + } + else + return nullptr; +} + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlsPageDescriptor.cxx b/sd/source/ui/slidesorter/model/SlsPageDescriptor.cxx new file mode 100644 index 000000000..5118cf58e --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlsPageDescriptor.cxx @@ -0,0 +1,226 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +namespace sd::slidesorter::model { + +PageDescriptor::PageDescriptor ( + const Reference& rxPage, + SdPage* pPage, + const sal_Int32 nIndex) + : mpPage(pPage), + mxPage(rxPage), + mpMasterPage(nullptr), + mnIndex(nIndex), + maVisualState(nIndex), + mbIsSelected(false), + mbWasSelected(false), + mbIsVisible(false), + mbIsFocused(false), + mbIsCurrent(false), + mbIsMouseOver(false), + mbHasTransition(false) +{ + assert(mpPage); + assert(mpPage == SdPage::getImplementation(rxPage)); + if (mpPage != nullptr) + { + if (mpPage->TRG_HasMasterPage()) + mpMasterPage = &mpPage->TRG_GetMasterPage(); + if (mpPage->getTransitionType() > 0) + mbHasTransition = true; + } +} + +PageDescriptor::~PageDescriptor() +{ +} + +void PageDescriptor::SetPageIndex (const sal_Int32 nNewIndex) +{ + mnIndex = nNewIndex; + maVisualState.mnPageId = nNewIndex; +} + +bool PageDescriptor::UpdateMasterPage() +{ + const SdrPage* pMaster = nullptr; + if (mpPage!=nullptr && mpPage->TRG_HasMasterPage()) + pMaster = &mpPage->TRG_GetMasterPage(); + if (mpMasterPage != pMaster) + { + mpMasterPage = pMaster; + return true; + } + else + return false; +} + +bool PageDescriptor::UpdateTransitionFlag() +{ + bool bHasSlideTransition (false); + if (mpPage != nullptr) + bHasSlideTransition = mpPage->getTransitionType() > 0; + if (bHasSlideTransition != mbHasTransition) + { + mbHasTransition = bHasSlideTransition; + return true; + } + else + return false; +} + +bool PageDescriptor::HasState (const State eState) const +{ + switch (eState) + { + case ST_Visible: + return mbIsVisible; + + case ST_Selected: + return mbIsSelected; + + case ST_WasSelected: + return mbWasSelected; + + case ST_Focused: + return mbIsFocused; + + case ST_MouseOver: + return mbIsMouseOver; + + case ST_Current: + return mbIsCurrent; + + case ST_Excluded: + return mpPage!=nullptr && mpPage->IsExcluded(); + + default: + assert(false); + return false; + } +} + +bool PageDescriptor::SetState (const State eState, const bool bNewStateValue) +{ + bool bModified (false); + switch (eState) + { + case ST_Visible: + bModified = (bNewStateValue!=mbIsVisible); + if (bModified) + mbIsVisible = bNewStateValue; + break; + + case ST_Selected: + bModified = (bNewStateValue!=mbIsSelected); + if (bModified) + mbIsSelected = bNewStateValue; + break; + + case ST_WasSelected: + bModified = (bNewStateValue!=mbWasSelected); + if (bModified) + mbWasSelected = bNewStateValue; + break; + + case ST_Focused: + bModified = (bNewStateValue!=mbIsFocused); + if (bModified) + mbIsFocused = bNewStateValue; + break; + + case ST_MouseOver: + bModified = (bNewStateValue!=mbIsMouseOver); + if (bModified) + mbIsMouseOver = bNewStateValue; + break; + + case ST_Current: + bModified = (bNewStateValue!=mbIsCurrent); + if (bModified) + mbIsCurrent = bNewStateValue; + break; + + case ST_Excluded: + // This is a state of the page and has to be handled differently + // from the view-only states. + if (mpPage != nullptr) + if (bNewStateValue != mpPage->IsExcluded()) + { + mpPage->SetExcluded(bNewStateValue); + bModified = true; + } + break; + } + + return bModified; +} + +bool PageDescriptor::GetCoreSelection() +{ + if (mpPage!=nullptr && mpPage->IsSelected() != mbIsSelected) + return SetState(ST_Selected, !mbIsSelected); + else + return false; +} + +void PageDescriptor::SetCoreSelection() +{ + if (mpPage != nullptr) + if (HasState(ST_Selected)) + mpPage->SetSelected(true); + else + mpPage->SetSelected(false); + else + { + assert(mpPage!=nullptr); + } +} + +::tools::Rectangle PageDescriptor::GetBoundingBox() const +{ + ::tools::Rectangle aBox (maBoundingBox); + const Point aOffset (maVisualState.GetLocationOffset()); + aBox.Move(aOffset.X(), aOffset.Y()); + return aBox; +} + +Point PageDescriptor::GetLocation (const bool bIgnoreOffset) const +{ + if (bIgnoreOffset) + return maBoundingBox.TopLeft(); + else + return maBoundingBox.TopLeft() + maVisualState.GetLocationOffset(); +} + +void PageDescriptor::SetBoundingBox (const ::tools::Rectangle& rBoundingBox) +{ + maBoundingBox = rBoundingBox; +} + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlsPageEnumeration.cxx b/sd/source/ui/slidesorter/model/SlsPageEnumeration.cxx new file mode 100644 index 000000000..a67f057e7 --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlsPageEnumeration.cxx @@ -0,0 +1,202 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include + +#include +#include + +using namespace ::sd::slidesorter; +using namespace ::sd::slidesorter::model; + +namespace { + +class PageEnumerationImpl + : public Enumeration +{ +public: + PageEnumerationImpl ( + const SlideSorterModel& rModel, + const PageEnumeration::PagePredicate& rPredicate); + PageEnumerationImpl(const PageEnumerationImpl&) = delete; + PageEnumerationImpl& operator=(const PageEnumerationImpl&) = delete; + /** Create a copy of the called enumeration object. + */ + virtual ::std::unique_ptr > Clone() override; + + virtual bool HasMoreElements() const override; + virtual SharedPageDescriptor GetNextElement() override; + virtual void Rewind() override; + +private: + const SlideSorterModel& mrModel; + const PageEnumeration::PagePredicate maPredicate; + int mnIndex; + + /** This constructor sets the internal page index to the given value. + It does not call AdvanceToNextValidElement() to skip elements that + do not fulfill Predicate. + */ + PageEnumerationImpl ( + const SlideSorterModel& rModel, + const PageEnumeration::PagePredicate& rPredicate, + int nIndex); + + /** Skip all elements that do not fulfill Predicate starting with the + one pointed to by mnIndex. + */ + void AdvanceToNextValidElement(); +}; + +} // end of anonymous namespace + +namespace sd::slidesorter::model { + +PageEnumeration PageEnumeration::Create ( + const SlideSorterModel& rModel, + const PagePredicate& rPredicate) +{ + return PageEnumeration(::std::unique_ptr >( + new PageEnumerationImpl(rModel, rPredicate))); +} + +PageEnumeration::PageEnumeration ( + ::std::unique_ptr > && pImpl) + : mpImpl(std::move(pImpl)) +{ +} + +PageEnumeration::PageEnumeration (const PageEnumeration& rEnumeration ) +: sd::slidesorter::model::Enumeration() +{ + mpImpl = rEnumeration.mpImpl->Clone(); +} + +PageEnumeration::~PageEnumeration() +{ +} + +PageEnumeration& PageEnumeration::operator= ( + const PageEnumeration& rEnumeration) +{ + mpImpl = rEnumeration.mpImpl->Clone(); + return *this; +} + +::std::unique_ptr > PageEnumeration::Clone() +{ + return ::std::unique_ptr >( + new PageEnumeration (*this)); +} + +bool PageEnumeration::HasMoreElements() const +{ + return mpImpl->HasMoreElements(); +} + +SharedPageDescriptor PageEnumeration::GetNextElement() +{ + return mpImpl->GetNextElement(); +} + +void PageEnumeration::Rewind() +{ + return mpImpl->Rewind(); +} + +} // end of namespace ::sd::slidesorter::model + +namespace { + +PageEnumerationImpl::PageEnumerationImpl ( + const SlideSorterModel& rModel, + const PageEnumeration::PagePredicate& rPredicate) + : mrModel(rModel), + maPredicate(rPredicate), + mnIndex(0) +{ + Rewind(); +} + +PageEnumerationImpl::PageEnumerationImpl ( + const SlideSorterModel& rModel, + const PageEnumeration::PagePredicate& rPredicate, + int nIndex) + : mrModel(rModel), + maPredicate(rPredicate), + mnIndex(nIndex) +{ +} + +::std::unique_ptr > + PageEnumerationImpl::Clone() +{ + return ::std::unique_ptr >( + new PageEnumerationImpl(mrModel,maPredicate,mnIndex)); +} + +bool PageEnumerationImpl::HasMoreElements() const +{ + return (mnIndex < mrModel.GetPageCount()); +} + +SharedPageDescriptor PageEnumerationImpl::GetNextElement() +{ + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(mnIndex)); + + // Go to the following valid element. + mnIndex += 1; + AdvanceToNextValidElement(); + + return pDescriptor; +} + +void PageEnumerationImpl::Rewind() +{ + // Go to first valid element. + mnIndex = 0; + AdvanceToNextValidElement(); +} + +void PageEnumerationImpl::AdvanceToNextValidElement() +{ + while (mnIndex < mrModel.GetPageCount()) + { + SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(mnIndex)); + + // Test for the predicate being fulfilled. + if (pDescriptor && maPredicate(pDescriptor)) + { + // This predicate is valid. + break; + } + else + { + // Advance to next predicate. + mnIndex += 1; + } + } +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlsPageEnumerationProvider.cxx b/sd/source/ui/slidesorter/model/SlsPageEnumerationProvider.cxx new file mode 100644 index 000000000..800fa12db --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlsPageEnumerationProvider.cxx @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +namespace sd::slidesorter::model { + +namespace { + +class AllPagesPredicate +{ +public: + bool operator() (const SharedPageDescriptor&) const + { + return true; + } +}; + +class SelectedPagesPredicate +{ +public: + bool operator() (const SharedPageDescriptor& rpDescriptor) + { + return rpDescriptor->HasState(PageDescriptor::ST_Selected); + } +}; + +class VisiblePagesPredicate +{ +public: + bool operator() (const SharedPageDescriptor& rpDescriptor) + { + return rpDescriptor->HasState(PageDescriptor::ST_Visible); + } +}; + +} + +PageEnumeration PageEnumerationProvider::CreateAllPagesEnumeration ( + const SlideSorterModel& rModel) +{ + return PageEnumeration::Create(rModel, AllPagesPredicate()); +} + +PageEnumeration PageEnumerationProvider::CreateSelectedPagesEnumeration ( + const SlideSorterModel& rModel) +{ + return PageEnumeration::Create( + rModel, + SelectedPagesPredicate()); +} + +PageEnumeration PageEnumerationProvider::CreateVisiblePagesEnumeration ( + const SlideSorterModel& rModel) +{ + return PageEnumeration::Create( + rModel, + VisiblePagesPredicate()); +} + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/model/SlsVisualState.cxx b/sd/source/ui/slidesorter/model/SlsVisualState.cxx new file mode 100644 index 000000000..3e16823ff --- /dev/null +++ b/sd/source/ui/slidesorter/model/SlsVisualState.cxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +namespace sd::slidesorter::model +{ +VisualState::VisualState(const sal_Int32 nPageId) + : mnPageId(nPageId) + , maLocationOffset(0, 0) +{ +} + +void VisualState::SetLocationOffset(const Point& rOffset) +{ + if (maLocationOffset != rOffset) + { + maLocationOffset = rOffset; + } +} + +} // end of namespace ::sd::slidesorter::model + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/shell/SlideSorter.cxx b/sd/source/ui/slidesorter/shell/SlideSorter.cxx new file mode 100644 index 000000000..550a40469 --- /dev/null +++ b/sd/source/ui/slidesorter/shell/SlideSorter.cxx @@ -0,0 +1,456 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star; + +namespace sd::slidesorter { + +namespace { +class ContentWindow : public ::sd::Window +{ +public: + ContentWindow(vcl::Window& rParent, SlideSorter& rSlideSorter); + + void SetCurrentFunction (const rtl::Reference& rpFunction); + virtual void Paint(vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) override; + virtual void KeyInput (const KeyEvent& rEvent) override; + virtual void MouseMove (const MouseEvent& rEvent) override; + virtual void MouseButtonUp (const MouseEvent& rEvent) override; + virtual void MouseButtonDown (const MouseEvent& rEvent) override; + virtual void Command (const CommandEvent& rEvent) override; + virtual bool EventNotify (NotifyEvent& rEvent) override; + +private: + SlideSorter& mrSlideSorter; + rtl::Reference mpCurrentFunction; +}; +} + +//===== SlideSorter =========================================================== + +std::shared_ptr SlideSorter::CreateSlideSorter( + ViewShell& rViewShell, + sd::Window* pContentWindow, + ScrollBar* pHorizontalScrollBar, + ScrollBar* pVerticalScrollBar, + ScrollBarBox* pScrollBarBox) +{ + std::shared_ptr pSlideSorter( + new SlideSorter( + rViewShell, + pContentWindow, + pHorizontalScrollBar, + pVerticalScrollBar, + pScrollBarBox), + o3tl::default_delete()); + pSlideSorter->Init(); + return pSlideSorter; +} + +std::shared_ptr SlideSorter::CreateSlideSorter ( + ViewShellBase& rBase, + vcl::Window& rParentWindow) +{ + std::shared_ptr pSlideSorter( + new SlideSorter( + rBase, + rParentWindow), + o3tl::default_delete()); + pSlideSorter->Init(); + return pSlideSorter; +} + +SlideSorter::SlideSorter ( + ViewShell& rViewShell, + sd::Window* pContentWindow, + ScrollBar* pHorizontalScrollBar, + ScrollBar* pVerticalScrollBar, + ScrollBarBox* pScrollBarBox) + : mbIsValid(false), + mpViewShell(&rViewShell), + mpViewShellBase(&rViewShell.GetViewShellBase()), + mpContentWindow(pContentWindow), + mpHorizontalScrollBar(pHorizontalScrollBar), + mpVerticalScrollBar(pVerticalScrollBar), + mpScrollBarBox(pScrollBarBox), + mpProperties(std::make_shared()), + mpTheme(std::make_shared(mpProperties)) +{ +} + +SlideSorter::SlideSorter ( + ViewShellBase& rBase, + vcl::Window& rParentWindow) + : mbIsValid(false), + mpViewShell(nullptr), + mpViewShellBase(&rBase), + mpContentWindow(VclPtr::Create(rParentWindow,*this )), + mpHorizontalScrollBar(VclPtr::Create(&rParentWindow,WinBits(WB_HSCROLL | WB_DRAG))), + mpVerticalScrollBar(VclPtr::Create(&rParentWindow,WinBits(WB_VSCROLL | WB_DRAG))), + mpScrollBarBox(VclPtr::Create(&rParentWindow)), + mpProperties(std::make_shared()), + mpTheme(std::make_shared(mpProperties)) +{ +} + +void SlideSorter::Init() +{ + if (mpViewShellBase != nullptr) + mxControllerWeak = mpViewShellBase->GetController(); + + // Reinitialize colors in Properties with window specific values. + if (mpContentWindow) + { + mpProperties->SetBackgroundColor( + mpContentWindow->GetSettings().GetStyleSettings().GetWindowColor()); + mpProperties->SetTextColor( + mpContentWindow->GetSettings().GetStyleSettings().GetWindowTextColor()); + mpProperties->SetSelectionColor( + mpContentWindow->GetSettings().GetStyleSettings().GetMenuHighlightColor()); + mpProperties->SetHighlightColor( + mpContentWindow->GetSettings().GetStyleSettings().GetMenuHighlightColor()); + } + + CreateModelViewController (); + + SetupListeners (); + + // Initialize the window. + sd::Window *pContentWindow = GetContentWindow().get(); + if (!pContentWindow) + return; + + vcl::Window* pParentWindow = pContentWindow->GetParent(); + if (pParentWindow != nullptr) + pParentWindow->SetBackground(Wallpaper()); + pContentWindow->SetBackground(Wallpaper()); + pContentWindow->SetViewOrigin (Point(0,0)); + // We do our own scrolling while dragging a page selection. + pContentWindow->SetUseDropScroll (false); + // Change the winbits so that the active window accepts the focus. + pContentWindow->SetStyle ((pContentWindow->GetStyle() & ~WB_DIALOGCONTROL) | WB_TABSTOP); + pContentWindow->Hide(); + + // Set view pointer of base class. + SetupControls(); + + mbIsValid = true; +} + +SlideSorter::~SlideSorter() +{ + mbIsValid = false; + + ReleaseListeners(); + + // Dispose model, view and controller to avoid calls between them when + // they are being destructed and one or two of them are already gone. + mpSlideSorterController->Dispose(); + mpSlideSorterView->Dispose(); + mpSlideSorterModel->Dispose(); + + // Reset the auto pointers explicitly to control the order of destruction. + mpSlideSorterController.reset(); + mpSlideSorterView.reset(); + mpSlideSorterModel.reset(); + + mpHorizontalScrollBar.reset(); + mpVerticalScrollBar.reset(); + mpScrollBarBox.reset(); +} + +model::SlideSorterModel& SlideSorter::GetModel() const +{ + assert(mpSlideSorterModel); + return *mpSlideSorterModel; +} + +view::SlideSorterView& SlideSorter::GetView() const +{ + assert(mpSlideSorterView); + return *mpSlideSorterView; +} + +controller::SlideSorterController& SlideSorter::GetController() const +{ + assert(mpSlideSorterController); + return *mpSlideSorterController; +} + +Reference SlideSorter::GetXController() const +{ + Reference xController(mxControllerWeak); + return xController; +} + +void SlideSorter::Paint (const ::tools::Rectangle& rRepaintArea) +{ + GetController().Paint( + rRepaintArea, + GetContentWindow()); +} + +void SlideSorter::SetupControls() +{ + GetVerticalScrollBar()->Show(); +} + +void SlideSorter::SetupListeners() +{ + sd::Window *pWindow = GetContentWindow().get(); + if (pWindow) + { + vcl::Window* pParentWindow = pWindow->GetParent(); + if (pParentWindow != nullptr) + pParentWindow->AddEventListener( + LINK( + mpSlideSorterController.get(), + controller::SlideSorterController, + WindowEventHandler)); + pWindow->AddEventListener( + LINK( + mpSlideSorterController.get(), + controller::SlideSorterController, + WindowEventHandler)); + } + Application::AddEventListener( + LINK( + mpSlideSorterController.get(), + controller::SlideSorterController, + ApplicationEventHandler)); + + mpSlideSorterController->GetScrollBarManager().Connect(); +} + +void SlideSorter::ReleaseListeners() +{ + mpSlideSorterController->GetScrollBarManager().Disconnect(); + + sd::Window *pWindow (GetContentWindow().get()); + if (pWindow) + { + pWindow->RemoveEventListener( + LINK(mpSlideSorterController.get(), + controller::SlideSorterController, + WindowEventHandler)); + + vcl::Window* pParentWindow = pWindow->GetParent(); + if (pParentWindow != nullptr) + pParentWindow->RemoveEventListener( + LINK(mpSlideSorterController.get(), + controller::SlideSorterController, + WindowEventHandler)); + } + Application::RemoveEventListener( + LINK(mpSlideSorterController.get(), + controller::SlideSorterController, + ApplicationEventHandler)); +} + +void SlideSorter::CreateModelViewController() +{ + mpSlideSorterModel.reset(CreateModel()); + DBG_ASSERT(mpSlideSorterModel != nullptr, "Can not create model for slide browser"); + + mpSlideSorterView.reset(new view::SlideSorterView (*this)); + mpSlideSorterController.reset(new controller::SlideSorterController(*this)); + + // Now that model, view, and controller are constructed, do the + // initialization that relies on all three being in place. + mpSlideSorterController->Init(); + mpSlideSorterView->Init(); +} + +model::SlideSorterModel* SlideSorter::CreateModel() +{ + // Get pointers to the document. + ViewShellBase* pViewShellBase = GetViewShellBase(); + if (pViewShellBase != nullptr) + { + assert (pViewShellBase->GetDocument() != nullptr); + + return new model::SlideSorterModel(*this); + } + else + return nullptr; +} + +void SlideSorter::ArrangeGUIElements ( + const Point& rOffset, + const Size& rSize) +{ + Point aOrigin (rOffset); + + if (rSize.Width()>0 + && rSize.Height()>0 + && GetContentWindow() + && GetContentWindow()->IsVisible()) + { + // Prevent untimely redraws while the view is not yet correctly + // resized. + view::SlideSorterView::DrawLock aLock (*this); + GetContentWindow()->EnablePaint (false); + + mpSlideSorterController->Resize (::tools::Rectangle(aOrigin, rSize)); + + GetContentWindow()->EnablePaint (true); + } +} + +void SlideSorter::RelocateToWindow (vcl::Window* pParentWindow) +{ + // Stop all animations for they have been started for the old window. + mpSlideSorterController->GetAnimator()->RemoveAllAnimations(); + + ReleaseListeners(); + + if (mpViewShell) + { + mpViewShell->ViewShell::RelocateToParentWindow(pParentWindow); + } + + SetupControls(); + SetupListeners(); + + // For accessibility we have to shortly hide the content window. This + // triggers the construction of a new accessibility object for the new + // view shell. (One is created earlier while the constructor of the base + // class is executed. But because at that time the correct + // accessibility object can not be constructed we do that now.) + if (mpContentWindow) + { + mpContentWindow->Hide(); + mpContentWindow->Show(); + } +} + +void SlideSorter::SetCurrentFunction (const rtl::Reference& rpFunction) +{ + if (GetViewShell() != nullptr) + { + GetViewShell()->SetCurrentFunction(rpFunction); + GetViewShell()->SetOldFunction(rpFunction); + } + else + { + ContentWindow* pWindow = dynamic_cast(GetContentWindow().get()); + if (pWindow != nullptr) + pWindow->SetCurrentFunction(rpFunction); + } +} + +std::shared_ptr const & SlideSorter::GetProperties() const +{ + assert(mpProperties); + return mpProperties; +} + +std::shared_ptr const & SlideSorter::GetTheme() const +{ + assert(mpTheme); + return mpTheme; +} + +//===== ContentWindow ========================================================= + +namespace { + +ContentWindow::ContentWindow( + vcl::Window& rParent, + SlideSorter& rSlideSorter) + : ::sd::Window(&rParent), + mrSlideSorter(rSlideSorter) +{ + SetDialogControlFlags(GetDialogControlFlags() & ~DialogControlFlags::WantFocus); + SetStyle(GetStyle() | WB_NOPOINTERFOCUS); +} + +void ContentWindow::SetCurrentFunction (const rtl::Reference& rpFunction) +{ + mpCurrentFunction = rpFunction; +} + +void ContentWindow::Paint (vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) +{ + mrSlideSorter.Paint(rRect); +} + +void ContentWindow::KeyInput (const KeyEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->KeyInput(rEvent); +} + +void ContentWindow::MouseMove (const MouseEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->MouseMove(rEvent); +} + +void ContentWindow::MouseButtonUp(const MouseEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->MouseButtonUp(rEvent); +} + +void ContentWindow::MouseButtonDown(const MouseEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->MouseButtonDown(rEvent); +} + +void ContentWindow::Command(const CommandEvent& rEvent) +{ + if (mpCurrentFunction.is()) + mpCurrentFunction->Command(rEvent); +} + +bool ContentWindow::EventNotify(NotifyEvent&) +{ + return false; +} + +} // end of anonymous namespace + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/shell/SlideSorterService.cxx b/sd/source/ui/slidesorter/shell/SlideSorterService.cxx new file mode 100644 index 000000000..a086f3b9e --- /dev/null +++ b/sd/source/ui/slidesorter/shell/SlideSorterService.cxx @@ -0,0 +1,412 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlideSorterService.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::slidesorter::view::Layouter; + +namespace sd::slidesorter { + +//===== SlideSorterService ========================================================== + +SlideSorterService::SlideSorterService() +{ +} + +SlideSorterService::~SlideSorterService() +{ +} + +void SlideSorterService::disposing(std::unique_lock&) +{ + mpSlideSorter.reset(); + + if (mxParentWindow.is()) + { + mxParentWindow->removeWindowListener(this); + } +} + +//----- XInitialization ------------------------------------------------------- + +void SAL_CALL SlideSorterService::initialize (const Sequence& rArguments) +{ + ThrowIfDisposed(); + + if (rArguments.getLength() != 3) + { + throw RuntimeException("SlideSorterService: invalid number of arguments", + static_cast(this)); + } + + mxViewId.set(rArguments[0], UNO_QUERY_THROW); + + // Get the XController. + Reference xController (rArguments[1], UNO_QUERY_THROW); + + // Tunnel through the controller to obtain a ViewShellBase. + ViewShellBase* pBase = nullptr; + Reference xTunnel (xController, UNO_QUERY_THROW); + ::sd::DrawController* pController = comphelper::getFromUnoTunnel(xTunnel); + if (pController != nullptr) + pBase = pController->GetViewShellBase(); + + // Get the parent window. + mxParentWindow.set(rArguments[2], UNO_QUERY_THROW); + VclPtr pParentWindow = VCLUnoHelper::GetWindow(mxParentWindow); + + mxParentWindow->addWindowListener(this); + + if (pBase != nullptr && pParentWindow) + mpSlideSorter = SlideSorter::CreateSlideSorter( + *pBase, + *pParentWindow); + + Resize(); +} + +//----- XView ----------------------------------------------------------------- + +Reference SAL_CALL SlideSorterService::getResourceId() +{ + return mxViewId; +} + +sal_Bool SAL_CALL SlideSorterService::isAnchorOnly() +{ + return false; +} + +//----- XWindowListener ------------------------------------------------------- + +void SAL_CALL SlideSorterService::windowResized (const awt::WindowEvent&) +{ + ThrowIfDisposed(); + + Resize(); +} + +void SAL_CALL SlideSorterService::windowMoved (const awt::WindowEvent&) {} + +void SAL_CALL SlideSorterService::windowShown (const lang::EventObject&) +{ + ThrowIfDisposed(); + Resize(); +} + +void SAL_CALL SlideSorterService::windowHidden (const lang::EventObject&) +{ + ThrowIfDisposed(); +} + +//----- lang::XEventListener -------------------------------------------------- + +void SAL_CALL SlideSorterService::disposing (const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxParentWindow) + mxParentWindow = nullptr; +} + +//----- XDrawView ------------------------------------------------------------- + +void SAL_CALL SlideSorterService::setCurrentPage(const Reference& rxSlide) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr) + mpSlideSorter->GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + mpSlideSorter->GetModel().GetIndex(rxSlide)); +} + +Reference SAL_CALL SlideSorterService::getCurrentPage() +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr) + return mpSlideSorter->GetController().GetCurrentSlideManager()->GetCurrentSlide()->GetXDrawPage(); + else + return nullptr; +} + +//----- attributes ------------------------------------------------------------ + +Reference SAL_CALL SlideSorterService::getDocumentSlides() +{ + return mpSlideSorter->GetModel().GetDocumentSlides(); +} + +void SAL_CALL SlideSorterService::setDocumentSlides ( + const Reference& rxSlides) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetController().SetDocumentSlides(rxSlides); +} + +sal_Bool SAL_CALL SlideSorterService::getIsHighlightCurrentSlide() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsHighlightCurrentSlide(); +} + +void SAL_CALL SlideSorterService::setIsHighlightCurrentSlide (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + { + mpSlideSorter->GetProperties()->SetHighlightCurrentSlide(bValue); + controller::SlideSorterController::ModelChangeLock aLock (mpSlideSorter->GetController()); + mpSlideSorter->GetController().HandleModelChange(); + } +} + +sal_Bool SAL_CALL SlideSorterService::getIsShowSelection() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsShowSelection(); +} + +void SAL_CALL SlideSorterService::setIsShowSelection (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetShowSelection(bValue); +} + +sal_Bool SAL_CALL SlideSorterService::getIsShowFocus() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsShowFocus(); +} + +void SAL_CALL SlideSorterService::setIsShowFocus (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetShowFocus(bValue); +} + +sal_Bool SAL_CALL SlideSorterService::getIsCenterSelection() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsCenterSelection(); +} + +void SAL_CALL SlideSorterService::setIsCenterSelection (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetCenterSelection(bValue); +} + +sal_Bool SAL_CALL SlideSorterService::getIsSuspendPreviewUpdatesDuringFullScreenPresentation() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return true; + else + return mpSlideSorter->GetProperties() + ->IsSuspendPreviewUpdatesDuringFullScreenPresentation(); +} + +void SAL_CALL SlideSorterService::setIsSuspendPreviewUpdatesDuringFullScreenPresentation ( + sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties() + ->SetSuspendPreviewUpdatesDuringFullScreenPresentation(bValue); +} + +sal_Bool SAL_CALL SlideSorterService::getIsOrientationVertical() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return true; + else + return mpSlideSorter->GetView().GetOrientation() != Layouter::HORIZONTAL; +} + +void SAL_CALL SlideSorterService::setIsOrientationVertical (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetView().SetOrientation(bValue + ? Layouter::GRID + : Layouter::HORIZONTAL); +} + +sal_Bool SAL_CALL SlideSorterService::getIsSmoothScrolling() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return false; + else + return mpSlideSorter->GetProperties()->IsSmoothSelectionScrolling(); +} + +void SAL_CALL SlideSorterService::setIsSmoothScrolling (sal_Bool bValue) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetSmoothSelectionScrolling(bValue); +} + +sal_Int32 SAL_CALL SlideSorterService::getBackgroundColor() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return util::Color(); + else + return util::Color( + mpSlideSorter->GetProperties()->GetBackgroundColor()); +} + +void SAL_CALL SlideSorterService::setBackgroundColor (sal_Int32 aBackgroundColor) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetBackgroundColor(Color(ColorTransparency, aBackgroundColor)); +} + +sal_Int32 SAL_CALL SlideSorterService::getTextColor() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return util::Color(); + else + return util::Color( + mpSlideSorter->GetProperties()->GetTextColor()); +} + +void SAL_CALL SlideSorterService::setTextColor (sal_Int32 aTextColor) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetTextColor(Color(ColorTransparency, aTextColor)); +} + +sal_Int32 SAL_CALL SlideSorterService::getSelectionColor() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return util::Color(); + else + return util::Color( + mpSlideSorter->GetProperties()->GetSelectionColor()); +} + +void SAL_CALL SlideSorterService::setSelectionColor (sal_Int32 aSelectionColor) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetSelectionColor(Color(ColorTransparency, aSelectionColor)); +} + +sal_Int32 SAL_CALL SlideSorterService::getHighlightColor() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return util::Color(); + else + return util::Color( + mpSlideSorter->GetProperties()->GetHighlightColor()); +} + +void SAL_CALL SlideSorterService::setHighlightColor (sal_Int32 aHighlightColor) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetHighlightColor(Color(ColorTransparency, aHighlightColor)); +} + +sal_Bool SAL_CALL SlideSorterService::getIsUIReadOnly() +{ + ThrowIfDisposed(); + if (mpSlideSorter == nullptr || !mpSlideSorter->IsValid()) + return true; + else + return mpSlideSorter->GetProperties()->IsUIReadOnly(); +} + +void SAL_CALL SlideSorterService::setIsUIReadOnly (sal_Bool bIsUIReadOnly) +{ + ThrowIfDisposed(); + if (mpSlideSorter != nullptr && mpSlideSorter->IsValid()) + mpSlideSorter->GetProperties()->SetUIReadOnly(bIsUIReadOnly); +} + +void SlideSorterService::Resize() +{ + if (mxParentWindow.is()) + { + awt::Rectangle aWindowBox = mxParentWindow->getPosSize(); + mpSlideSorter->ArrangeGUIElements( + Point(0,0), + Size(aWindowBox.Width, aWindowBox.Height)); + } +} + +void SlideSorterService::ThrowIfDisposed() +{ + if (SlideSorterServiceInterfaceBase::m_bDisposed) + { + throw lang::DisposedException ("SlideSorterService object has already been disposed", + static_cast(this)); + } +} + +} // end of namespace ::sd::slidesorter + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_SlideSorter_get_implementation(css::uno::XComponentContext* /*context*/, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::slidesorter::SlideSorterService); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/shell/SlideSorterService.hxx b/sd/source/ui/slidesorter/shell/SlideSorterService.hxx new file mode 100644 index 000000000..579a5bae5 --- /dev/null +++ b/sd/source/ui/slidesorter/shell/SlideSorterService.hxx @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace com::sun::star::awt { class XWindow; } + +namespace sd::slidesorter { + +typedef comphelper::WeakComponentImplHelper < + css::drawing::XSlideSorterBase, + css::lang::XInitialization, + css::awt::XWindowListener +> SlideSorterServiceInterfaceBase; + +class SlideSorter; + +/** Implementation of the com.sun.star.drawing.SlideSorter service. +*/ +class SlideSorterService + : public SlideSorterServiceInterfaceBase +{ +public: + explicit SlideSorterService(); + virtual ~SlideSorterService() override; + SlideSorterService(const SlideSorterService&) = delete; + SlideSorterService& operator=(const SlideSorterService&) = delete; + virtual void disposing(std::unique_lock&) override; + + // XInitialization + + virtual void SAL_CALL initialize (const css::uno::Sequence& rArguments) override; + + // XResourceId + + css::uno::Reference SAL_CALL getResourceId() override; + + sal_Bool SAL_CALL isAnchorOnly() override; + + // XWindowListener + + virtual void SAL_CALL windowResized (const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowMoved (const css::awt::WindowEvent& rEvent) override; + + virtual void SAL_CALL windowShown (const css::lang::EventObject& rEvent) override; + + virtual void SAL_CALL windowHidden (const css::lang::EventObject& rEvent) override; + + // lang::XEventListener + virtual void SAL_CALL disposing (const css::lang::EventObject& rEvent) override; + + // XDrawView + + virtual void SAL_CALL setCurrentPage( + const css::uno::Reference& rxSlide) override; + + virtual css::uno::Reference SAL_CALL getCurrentPage() override; + + // Attributes + + virtual css::uno::Reference SAL_CALL getDocumentSlides() override; + + virtual void SAL_CALL setDocumentSlides ( + const css::uno::Reference& rxSlides) override; + + virtual sal_Bool SAL_CALL getIsHighlightCurrentSlide() override; + + virtual void SAL_CALL setIsHighlightCurrentSlide (sal_Bool bIsHighlightCurrentSlide) override; + + virtual sal_Bool SAL_CALL getIsShowSelection() override; + + virtual void SAL_CALL setIsShowSelection (sal_Bool bIsShowSelection) override; + + virtual sal_Bool SAL_CALL getIsCenterSelection() override; + + virtual void SAL_CALL setIsCenterSelection (sal_Bool bIsCenterSelection) override; + + virtual sal_Bool SAL_CALL getIsSuspendPreviewUpdatesDuringFullScreenPresentation() override; + + virtual void SAL_CALL setIsSuspendPreviewUpdatesDuringFullScreenPresentation ( + sal_Bool bIsSuspendPreviewUpdatesDuringFullScreenPresentation) override; + + virtual sal_Bool SAL_CALL getIsOrientationVertical() override; + + virtual void SAL_CALL setIsOrientationVertical (sal_Bool bIsOrientationVertical) override; + + virtual sal_Bool SAL_CALL getIsSmoothScrolling() override; + + virtual void SAL_CALL setIsSmoothScrolling (sal_Bool bIsOrientationVertical) override; + + virtual sal_Int32 SAL_CALL getBackgroundColor() override; + + virtual void SAL_CALL setBackgroundColor (sal_Int32 aBackgroundColor) override; + + virtual sal_Int32 SAL_CALL getTextColor() override; + + virtual void SAL_CALL setTextColor (sal_Int32 aTextColor) override; + + virtual sal_Int32 SAL_CALL getSelectionColor() override; + + virtual void SAL_CALL setSelectionColor (sal_Int32 aSelectionColor) override; + + virtual sal_Int32 SAL_CALL getHighlightColor() override; + + virtual void SAL_CALL setHighlightColor (sal_Int32 aHighlightColor) override; + + virtual sal_Bool SAL_CALL getIsUIReadOnly() override; + + virtual void SAL_CALL setIsUIReadOnly (sal_Bool bIsUIReadOnly) override; + + virtual sal_Bool SAL_CALL getIsShowFocus() override; + + virtual void SAL_CALL setIsShowFocus (sal_Bool bIsShowFocus) override; + +private: + std::shared_ptr mpSlideSorter; + css::uno::Reference mxViewId; + css::uno::Reference mxParentWindow; + + void Resize(); + + /** @throws css::lang::DisposedException when the object has already been + disposed. + */ + void ThrowIfDisposed(); +}; + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx b/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx new file mode 100644 index 000000000..af5bd5791 --- /dev/null +++ b/sd/source/ui/slidesorter/shell/SlideSorterViewShell.cxx @@ -0,0 +1,924 @@ +#/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::sd::slidesorter; +#define ShellClass_SlideSorterViewShell +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; +using ::vcl::EnumContext; +using namespace sfx2::sidebar; + +namespace sd::slidesorter { + +namespace { + +bool inChartContext(const sd::View* pView) +{ + if (!pView) + return false; + + SfxViewShell* pViewShell = pView->GetSfxViewShell(); + SidebarController* pSidebar = SidebarController::GetSidebarControllerForView(pViewShell); + if (pSidebar) + return pSidebar->hasChartContextCurrently(); + + return false; +} + +} // anonymous namespace + + +SFX_IMPL_INTERFACE(SlideSorterViewShell, SfxShell) + +void SlideSorterViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId()); +} + + +std::shared_ptr SlideSorterViewShell::Create ( + SfxViewFrame* pFrame, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameViewArgument) +{ + std::shared_ptr pViewShell; + try + { + pViewShell.reset( + new SlideSorterViewShell(pFrame,rViewShellBase,pParentWindow,pFrameViewArgument)); + pViewShell->Initialize(); + if (pViewShell->mpSlideSorter == nullptr) + pViewShell.reset(); + } + catch(Exception&) + { + pViewShell.reset(); + } + return pViewShell; +} + +SlideSorterViewShell::SlideSorterViewShell ( + SfxViewFrame* /*pFrame*/, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameViewArgument) + : ViewShell (pParentWindow, rViewShellBase), + mbIsArrangeGUIElementsPending(true) +{ + GetContentWindow()->set_id("slidesorter"); + meShellType = ST_SLIDE_SORTER; + + if (pFrameViewArgument != nullptr) + mpFrameView = pFrameViewArgument; + else + mpFrameView = new FrameView(GetDoc()); + GetFrameView()->Connect(); + + SetName ("SlideSorterViewShell"); + + pParentWindow->SetStyle(pParentWindow->GetStyle() | WB_DIALOGCONTROL); +} + +SlideSorterViewShell::~SlideSorterViewShell() +{ + DisposeFunctions(); + + try + { + ::sd::Window* pWindow = GetActiveWindow(); + if (pWindow!=nullptr) + { + css::uno::Reference xComponent ( + pWindow->GetAccessible(false), + css::uno::UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + } + } + catch( css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SlideSorterViewShell::~SlideSorterViewShell()" ); + } + GetFrameView()->Disconnect(); +} + +void SlideSorterViewShell::Initialize() +{ + mpSlideSorter = SlideSorter::CreateSlideSorter( + *this, + mpContentWindow, + mpHorizontalScrollBar, + mpVerticalScrollBar, + mpScrollBarBox); + mpView = &mpSlideSorter->GetView(); + + doShow(); + + SetPool( &GetDoc()->GetPool() ); + SetUndoManager( GetDoc()->GetDocSh()->GetUndoManager() ); + + // For accessibility we have to shortly hide the content window. + // This triggers the construction of a new accessibility object for + // the new view shell. (One is created earlier while the constructor + // of the base class is executed. At that time the correct + // accessibility object can not be constructed.) + sd::Window *pWindow (mpSlideSorter->GetContentWindow().get()); + if (pWindow) + { + pWindow->Hide(); + pWindow->Show(); + } +} + +void SlideSorterViewShell::Init (bool bIsMainViewShell) +{ + ViewShell::Init(bIsMainViewShell); + + // since the updatePageList will show focus, the window.show() must be called ahead. This show is deferred from Init() + ::sd::Window* pActiveWindow = GetActiveWindow(); + if (pActiveWindow) + pActiveWindow->Show(); + mpSlideSorter->GetModel().UpdatePageList(); + + if (mpContentWindow) + mpContentWindow->SetViewShell(this); +} + +SlideSorterViewShell* SlideSorterViewShell::GetSlideSorter (ViewShellBase& rBase) +{ + SlideSorterViewShell* pViewShell = nullptr; + + // Test the center and left pane for showing a slide sorter. + OUString aPaneURLs[] = { + FrameworkHelper::msCenterPaneURL, + FrameworkHelper::msFullScreenPaneURL, + FrameworkHelper::msLeftImpressPaneURL, + FrameworkHelper::msLeftDrawPaneURL, + OUString()}; + + try + { + std::shared_ptr pFrameworkHelper (FrameworkHelper::Instance(rBase)); + if (pFrameworkHelper->IsValid()) + for (int i=0; pViewShell==nullptr && !aPaneURLs[i].isEmpty(); ++i) + { + pViewShell = dynamic_cast( + pFrameworkHelper->GetViewShell(aPaneURLs[i]).get()); + } + } + catch (RuntimeException&) + {} + + return pViewShell; +} + +Reference SlideSorterViewShell::CreateSubController() +{ + Reference xSubController; + + if (IsMainViewShell()) + { + // Create uno controller for the main view shell. + xSubController.set( new SdUnoSlideView( *mpSlideSorter)); + } + + return xSubController; +} + +/** If there is a valid controller then create a new instance of + AccessibleSlideSorterView. Otherwise delegate this call + to the base class to return a default object (probably an empty + reference). +*/ +css::uno::Reference + SlideSorterViewShell::CreateAccessibleDocumentView (::sd::Window* pWindow) +{ + // When the view is not set then the initialization is not yet complete + // and we can not yet provide an accessibility object. + if (mpView == nullptr || mpSlideSorter == nullptr) + return nullptr; + + assert(mpSlideSorter); + + rtl::Reference<::accessibility::AccessibleSlideSorterView> pAccessibleView = + new ::accessibility::AccessibleSlideSorterView( + *mpSlideSorter, + pWindow); + + pAccessibleView->Init(); + + return pAccessibleView; +} + +void SlideSorterViewShell::SwitchViewFireFocus(const css::uno::Reference< css::accessibility::XAccessible >& xAcc ) +{ + if (xAcc) + { + ::accessibility::AccessibleSlideSorterView* pBase = static_cast< ::accessibility::AccessibleSlideSorterView* >(xAcc.get()); + if (pBase) + { + pBase->SwitchViewActivated(); + } + } +} + +SlideSorter& SlideSorterViewShell::GetSlideSorter() const +{ + assert(mpSlideSorter); + return *mpSlideSorter; +} + +bool SlideSorterViewShell::RelocateToParentWindow (vcl::Window* pParentWindow) +{ + assert(mpSlideSorter); + if ( ! mpSlideSorter) + return false; + + mpSlideSorter->RelocateToWindow(pParentWindow); + ReadFrameViewData(mpFrameView); + + return true; +} + +SfxUndoManager* SlideSorterViewShell::ImpGetUndoManager() const +{ + SfxShell* pObjectBar = GetViewShellBase().GetViewShellManager()->GetTopShell(); + if (pObjectBar != nullptr) + { + // When it exists then return the undo manager of the currently + // active object bar. The object bar is missing when the + // SlideSorterViewShell is not the main view shell. + return pObjectBar->GetUndoManager(); + } + else + { + // Return the undo manager of this shell when there is no object or + // tool bar. + return const_cast(this)->GetUndoManager(); + } +} + +SdPage* SlideSorterViewShell::getCurrentPage() const +{ + // since SlideSorterViewShell::GetActualPage() currently also + // returns master pages, which is a wrong behaviour for GetActualPage(), + // we can just use that for now + return const_cast(this)->GetActualPage(); +} + +SdPage* SlideSorterViewShell::GetActualPage() +{ + SdPage* pCurrentPage = nullptr; + + // 1. Try to get the current page from the view shell in the center pane + // (if we are that not ourself). + if ( ! IsMainViewShell()) + { + std::shared_ptr pMainViewShell = GetViewShellBase().GetMainViewShell(); + if (pMainViewShell != nullptr) + pCurrentPage = pMainViewShell->GetActualPage(); + } + + if (pCurrentPage == nullptr) + { + model::SharedPageDescriptor pDescriptor ( + mpSlideSorter->GetController().GetCurrentSlideManager()->GetCurrentSlide()); + if (pDescriptor) + pCurrentPage = pDescriptor->GetPage(); + } + + return pCurrentPage; +} + +void SlideSorterViewShell::GetMenuState ( SfxItemSet& rSet) +{ + ViewShell::GetMenuState(rSet); + assert(mpSlideSorter); + mpSlideSorter->GetController().GetSlotManager()->GetMenuState(rSet); +} + +void SlideSorterViewShell::GetClipboardState ( SfxItemSet& rSet) +{ + ViewShell::GetMenuState(rSet); + assert(mpSlideSorter); + mpSlideSorter->GetController().GetSlotManager()->GetClipboardState(rSet); +} + +void SlideSorterViewShell::ExecCtrl (SfxRequest& rRequest) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().ExecCtrl(rRequest); +} + +void SlideSorterViewShell::GetCtrlState (SfxItemSet& rSet) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetCtrlState(rSet); +} + +void SlideSorterViewShell::FuSupport (SfxRequest& rRequest) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().FuSupport(rRequest); +} + +/** We have to handle those slot calls here that need to have access to + private or protected members and methods of this class. +*/ +void SlideSorterViewShell::FuTemporary (SfxRequest& rRequest) +{ + assert(mpSlideSorter); + switch (rRequest.GetSlot()) + { + case SID_MODIFYPAGE: + { + SdPage* pCurrentPage = GetActualPage(); + if (pCurrentPage != nullptr) + mpImpl->ProcessModifyPageSlot ( + rRequest, + pCurrentPage, + PageKind::Standard); + Cancel(); + rRequest.Done (); + } + break; + + default: + mpSlideSorter->GetController().FuTemporary(rRequest); + break; + } +} + +void SlideSorterViewShell::GetStatusBarState (SfxItemSet& rSet) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetStatusBarState(rSet); +} + +void SlideSorterViewShell::FuPermanent (SfxRequest& rRequest) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().FuPermanent(rRequest); +} + +void SlideSorterViewShell::GetAttrState (SfxItemSet& rSet) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetAttrState(rSet); +} + +void SlideSorterViewShell::ExecStatusBar (SfxRequest& ) +{ +} + +void SlideSorterViewShell::Paint ( + const ::tools::Rectangle& rBBox, + ::sd::Window* pWindow) +{ + SetActiveWindow (pWindow); + assert(mpSlideSorter); + if (mpSlideSorter) + mpSlideSorter->GetController().Paint(rBBox,pWindow); +} + +void SlideSorterViewShell::ArrangeGUIElements() +{ + if (IsActive()) + { + assert(mpSlideSorter); + mpSlideSorter->ArrangeGUIElements(maViewPos, maViewSize); + mbIsArrangeGUIElementsPending = false; + } + else + mbIsArrangeGUIElementsPending = true; +} + +void SlideSorterViewShell::Activate (bool bIsMDIActivate) +{ + if(inChartContext(GetView())) + { + // Avoid context changes for chart during activation / deactivation. + const bool bIsContextBroadcasterEnabled (SfxShell::SetContextBroadcasterEnabled(false)); + + ViewShell::Activate(bIsMDIActivate); + + SfxShell::SetContextBroadcasterEnabled(bIsContextBroadcasterEnabled); + return; + } + + ViewShell::Activate(bIsMDIActivate); + if (mbIsArrangeGUIElementsPending) + ArrangeGUIElements(); + + // Determine and broadcast the context that belongs to the main view shell. + EnumContext::Context eContext = EnumContext::Context::Unknown; + std::shared_ptr pMainViewShell (GetViewShellBase().GetMainViewShell()); + ViewShell::ShellType eMainViewShellType ( + pMainViewShell + ? pMainViewShell->GetShellType() + : ViewShell::ST_NONE); + switch (eMainViewShellType) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_SLIDE_SORTER: + case ViewShell::ST_NOTES: + case ViewShell::ST_DRAW: + eContext = EnumContext::Context::DrawPage; + if( nullptr != dynamic_cast< const DrawViewShell *>( pMainViewShell.get() )) + { + DrawViewShell* pDrawViewShell = dynamic_cast(pMainViewShell.get()); + if (pDrawViewShell != nullptr) + eContext = EnumContext::GetContextEnum(pDrawViewShell->GetSidebarContextName()); + } + break; + + default: + break; + } + ContextChangeEventMultiplexer::NotifyContextChange( + &GetViewShellBase(), + eContext); +} + +void SlideSorterViewShell::Deactivate (bool /*bIsMDIActivate*/) +{ + // Save Settings - Specifically SlidesPerRow to retrieve it later + WriteFrameViewData(); +} + +void SlideSorterViewShell::Command ( + const CommandEvent& rEvent, + ::sd::Window* pWindow) +{ + assert(mpSlideSorter); + if ( ! mpSlideSorter->GetController().Command (rEvent, pWindow)) + ViewShell::Command (rEvent, pWindow); +} + +void SlideSorterViewShell::ReadFrameViewData (FrameView* pFrameView) +{ + assert(mpSlideSorter); + if (pFrameView != nullptr) + { + view::SlideSorterView& rView (mpSlideSorter->GetView()); + + sal_uInt16 nSlidesPerRow (pFrameView->GetSlidesPerRow()); + if (nSlidesPerRow > 0 + && rView.GetOrientation() == view::Layouter::GRID + && IsMainViewShell()) + { + rView.GetLayouter().SetColumnCount(nSlidesPerRow,nSlidesPerRow); + } + if (IsMainViewShell()) + mpSlideSorter->GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + mpFrameView->GetSelectedPage()); + mpSlideSorter->GetController().Rearrange(true); + + // DrawMode for 'main' window + if (GetActiveWindow()->GetOutDev()->GetDrawMode() != pFrameView->GetDrawMode() ) + GetActiveWindow()->GetOutDev()->SetDrawMode( pFrameView->GetDrawMode() ); + } + + // When this slide sorter is not displayed in the main window then we do + // not share the same frame view and have to find other ways to acquire + // certain values. + if ( ! IsMainViewShell()) + { + std::shared_ptr pMainViewShell = GetViewShellBase().GetMainViewShell(); + if (pMainViewShell != nullptr) + mpSlideSorter->GetController().GetCurrentSlideManager()->NotifyCurrentSlideChange( + pMainViewShell->getCurrentPage()); + } +} + +void SlideSorterViewShell::WriteFrameViewData() +{ + assert(mpSlideSorter); + if (mpFrameView == nullptr) + return; + + view::SlideSorterView& rView (mpSlideSorter->GetView()); + mpFrameView->SetSlidesPerRow(static_cast(rView.GetLayouter().GetColumnCount())); + + // DrawMode for 'main' window + if( mpFrameView->GetDrawMode() != GetActiveWindow()->GetOutDev()->GetDrawMode() ) + mpFrameView->SetDrawMode( GetActiveWindow()->GetOutDev()->GetDrawMode() ); + + SdPage* pActualPage = GetActualPage(); + if (pActualPage != nullptr) + { + if (IsMainViewShell()) + mpFrameView->SetSelectedPage((pActualPage->GetPageNum()- 1) / 2); + // else + // The slide sorter is not expected to switch the current page + // other than by double clicks. That is handled separately. + } + else + { + // We have no current page to set but at least we can make sure + // that the index of the frame view has a legal value. + if (mpFrameView->GetSelectedPage() >= mpSlideSorter->GetModel().GetPageCount()) + mpFrameView->SetSelectedPage(static_cast(mpSlideSorter->GetModel().GetPageCount())-1); + } +} + +void SlideSorterViewShell::SetZoom (::tools::Long ) +{ + // Ignored. + // The zoom scale is adapted internally to fit a number of columns in + // the window. +} + +void SlideSorterViewShell::SetZoomRect (const ::tools::Rectangle& rZoomRect) +{ + assert(mpSlideSorter); + Size aPageSize (mpSlideSorter->GetView().GetLayouter().GetPageObjectSize()); + + ::tools::Rectangle aRect(rZoomRect); + + if (aRect.GetWidth() < aPageSize.Width()) + { + ::tools::Long nWidthDiff = (aPageSize.Width() - aRect.GetWidth()) / 2; + + aRect.AdjustLeft( -nWidthDiff ); + aRect.AdjustRight(nWidthDiff ); + + if (aRect.Left() < 0) + { + aRect.SetPos(Point(0, aRect.Top())); + } + } + + if (aRect.GetHeight() < aPageSize.Height()) + { + ::tools::Long nHeightDiff = (aPageSize.Height() - aRect.GetHeight()) / 2; + + aRect.AdjustTop( -nHeightDiff ); + aRect.AdjustBottom(nHeightDiff ); + + if (aRect.Top() < 0) + { + aRect.SetPos(Point(aRect.Left(), 0)); + } + } + + ViewShell::SetZoomRect(aRect); + + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); +} + +void SlideSorterViewShell::UpdateScrollBars() +{ + // Do not call the overwritten method of the base class: We do all the + // scroll bar setup by ourselves. + mpSlideSorter->GetController().GetScrollBarManager().UpdateScrollBars(true); +} + +void SlideSorterViewShell::StartDrag ( + const Point& rDragPt, + vcl::Window* pWindow ) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetClipboard().StartDrag ( + rDragPt, + pWindow); +} + +sal_Int8 SlideSorterViewShell::AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + assert(mpSlideSorter); + return mpSlideSorter->GetController().GetClipboard().AcceptDrop ( + rEvt, + rTargetHelper, + pTargetWindow, + nPage, + nLayer); +} + +sal_Int8 SlideSorterViewShell::ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + assert(mpSlideSorter); + return mpSlideSorter->GetController().GetClipboard().ExecuteDrop ( + rEvt, + rTargetHelper, + pTargetWindow, + nPage, + nLayer); +} + +std::shared_ptr + SlideSorterViewShell::GetPageSelection() const +{ + assert(mpSlideSorter); + return mpSlideSorter->GetController().GetPageSelector().GetPageSelection(); +} + +void SlideSorterViewShell::SetPageSelection ( + const std::shared_ptr& rSelection) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetPageSelector().SetPageSelection(rSelection, true); +} + +void SlideSorterViewShell::AddSelectionChangeListener ( + const Link& rCallback) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetSelectionManager()->AddSelectionChangeListener(rCallback); +} + +void SlideSorterViewShell::RemoveSelectionChangeListener ( + const Link& rCallback) +{ + assert(mpSlideSorter); + mpSlideSorter->GetController().GetSelectionManager()->RemoveSelectionChangeListener(rCallback); +} + +void SlideSorterViewShell::MainViewEndEditAndUnmarkAll() +{ + std::shared_ptr pMainViewShell = GetViewShellBase().GetMainViewShell(); + DrawViewShell* pDrawViewShell = dynamic_cast(pMainViewShell.get()); + SdrView* pView = pDrawViewShell ? pDrawViewShell->GetDrawView() : nullptr; + if (pView) + { + pView->SdrEndTextEdit(); + pView->UnmarkAll(); + } +} + +std::pair SlideSorterViewShell::SyncPageSelectionToDocument(const std::shared_ptr &rpSelection) +{ + sal_uInt16 firstSelectedPageNo = SAL_MAX_UINT16; + sal_uInt16 lastSelectedPageNo = 0; + + GetDoc()->UnselectAllPages(); + for (auto& rpPage : *rpSelection) + { + // Check page number + sal_uInt16 pageNo = rpPage->GetPageNum(); + if (pageNo > lastSelectedPageNo) + lastSelectedPageNo = pageNo; + if (pageNo < firstSelectedPageNo) + firstSelectedPageNo = pageNo; + GetDoc()->SetSelected(rpPage, true); + } + + return std::make_pair(firstSelectedPageNo, lastSelectedPageNo); +} + +void SlideSorterViewShell::ExecMovePageFirst (SfxRequest& /*rReq*/) +{ + MainViewEndEditAndUnmarkAll(); + + std::shared_ptr xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + SyncPageSelectionToDocument(xSelection); + + // Moves selected pages after page -1 + GetDoc()->MovePages( sal_uInt16(-1) ); + + PostMoveSlidesActions(xSelection); +} + +void SlideSorterViewShell::GetStateMovePageFirst (SfxItemSet& rSet) +{ + if ( ! IsMainViewShell()) + { + std::shared_ptr pMainViewShell = GetViewShellBase().GetMainViewShell(); + DrawViewShell* pDrawViewShell = dynamic_cast(pMainViewShell.get()); + if (pDrawViewShell != nullptr && pDrawViewShell->GetPageKind() == PageKind::Handout) + { + rSet.DisableItem( SID_MOVE_PAGE_FIRST ); + rSet.DisableItem( SID_MOVE_PAGE_UP ); + return; + } + } + + std::shared_ptr xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + sal_uInt16 firstSelectedPageNo = SyncPageSelectionToDocument(xSelection).first; + // Now compute human page number from internal page number + firstSelectedPageNo = (firstSelectedPageNo - 1) / 2; + + if (firstSelectedPageNo == 0) + { + rSet.DisableItem( SID_MOVE_PAGE_FIRST ); + rSet.DisableItem( SID_MOVE_PAGE_UP ); + } +} + +void SlideSorterViewShell::ExecMovePageUp (SfxRequest& /*rReq*/) +{ + MainViewEndEditAndUnmarkAll(); + + std::shared_ptr xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + sal_uInt16 firstSelectedPageNo = SyncPageSelectionToDocument(xSelection).first; + + // In case no slide is selected + if (firstSelectedPageNo == SAL_MAX_UINT16) + return; + + // Now compute human page number from internal page number + firstSelectedPageNo = (firstSelectedPageNo - 1) / 2; + + if (firstSelectedPageNo == 0) + return; + + // Move pages before firstSelectedPageNo - 1 (so after firstSelectedPageNo - 2), + // remembering that -1 means at first, which is good. + GetDoc()->MovePages( firstSelectedPageNo - 2 ); + + PostMoveSlidesActions(xSelection); +} + +void SlideSorterViewShell::GetStateMovePageUp (SfxItemSet& rSet) +{ + GetStateMovePageFirst(rSet); +} + +void SlideSorterViewShell::ExecMovePageDown (SfxRequest& /*rReq*/) +{ + MainViewEndEditAndUnmarkAll(); + + std::shared_ptr xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + sal_uInt16 lastSelectedPageNo = SyncPageSelectionToDocument(xSelection).second; + + // Get page number of the last page + sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard); + + // Now compute human page number from internal page number + lastSelectedPageNo = (lastSelectedPageNo - 1) / 2; + if (lastSelectedPageNo == nNoOfPages - 1) + return; + + // Move to position after lastSelectedPageNo + GetDoc()->MovePages( lastSelectedPageNo + 1 ); + + PostMoveSlidesActions(xSelection); +} + +void SlideSorterViewShell::GetStateMovePageDown (SfxItemSet& rSet) +{ + GetStateMovePageLast( rSet ); +} + +void SlideSorterViewShell::ExecMovePageLast (SfxRequest& /*rReq*/) +{ + MainViewEndEditAndUnmarkAll(); + + std::shared_ptr xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + SyncPageSelectionToDocument(xSelection); + + // Get page number of the last page + sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard); + + // Move to position after last page No (=Number of pages - 1) + GetDoc()->MovePages( nNoOfPages - 1 ); + + PostMoveSlidesActions(xSelection); +} + +void SlideSorterViewShell::GetStateMovePageLast (SfxItemSet& rSet) +{ + std::shared_ptr pMainViewShell = GetViewShellBase().GetMainViewShell(); + DrawViewShell* pDrawViewShell = dynamic_cast(pMainViewShell.get()); + if (pDrawViewShell != nullptr && pDrawViewShell->GetPageKind() == PageKind::Handout) + { + rSet.DisableItem( SID_MOVE_PAGE_LAST ); + rSet.DisableItem( SID_MOVE_PAGE_DOWN ); + return; + } + + std::shared_ptr xSelection(GetPageSelection()); + + // SdDrawDocument MovePages is based on SdPage IsSelected, so + // transfer the SlideSorter selection to SdPages + sal_uInt16 lastSelectedPageNo = SyncPageSelectionToDocument(xSelection).second; + + // Get page number of the last page + sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard); + + // Now compute human page number from internal page number + lastSelectedPageNo = (lastSelectedPageNo - 1) / 2; + if (lastSelectedPageNo == nNoOfPages - 1) + { + rSet.DisableItem( SID_MOVE_PAGE_LAST ); + rSet.DisableItem( SID_MOVE_PAGE_DOWN ); + } +} + +void SlideSorterViewShell::PostMoveSlidesActions(const std::shared_ptr &rpSelection) +{ + sal_uInt16 nNoOfPages = GetDoc()->GetSdPageCount(PageKind::Standard); + for (sal_uInt16 nPage = 0; nPage < nNoOfPages; nPage++) + { + SdPage* pPage = GetDoc()->GetSdPage(nPage, PageKind::Standard); + GetDoc()->SetSelected(pPage, false); + } + + mpSlideSorter->GetController().GetPageSelector().DeselectAllPages(); + for (const auto& rpPage : *rpSelection) + { + mpSlideSorter->GetController().GetPageSelector().SelectPage(rpPage); + } + + // Refresh toolbar icons + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_MOVE_PAGE_FIRST); + rBindings.Invalidate(SID_MOVE_PAGE_UP); + rBindings.Invalidate(SID_MOVE_PAGE_DOWN); + rBindings.Invalidate(SID_MOVE_PAGE_LAST); + +} + +} // end of namespace ::sd::slidesorter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlideSorterView.cxx b/sd/source/ui/slidesorter/view/SlideSorterView.cxx new file mode 100644 index 000000000..390541e37 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlideSorterView.cxx @@ -0,0 +1,856 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include "SlsViewCacheContext.hxx" +#include "SlsLayeredDevice.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +//#define DEBUG_TIMING +#ifdef DEBUG_TIMING +#include +#include +#endif + +using namespace ::sd::slidesorter::model; +using namespace ::drawinglayer::primitive2d; + +namespace sd::slidesorter::view { + +namespace { + /** Wrapper around the SlideSorterView that supports the IPainter + interface and that allows the LayeredDevice to hold the + SlideSorterView (held as unique_ptr by the SlideSorter) as + shared_ptr. + */ + class Painter : public ILayerPainter + { + public: + explicit Painter (SlideSorterView& rView) : mrView(rView) {} + + virtual void Paint (OutputDevice& rDevice, const ::tools::Rectangle& rRepaintArea) override + { + mrView.Paint(rDevice,rRepaintArea); + } + + virtual void SetLayerInvalidator (const SharedILayerInvalidator&) override {} + + private: + SlideSorterView& mrView; + }; +} + +namespace { + +class BackgroundPainter + : public ILayerPainter +{ +public: + explicit BackgroundPainter (const Color& rBackgroundColor) : maBackgroundColor(rBackgroundColor) {} + BackgroundPainter(const BackgroundPainter&) = delete; + BackgroundPainter& operator=(const BackgroundPainter&) = delete; + + virtual void Paint (OutputDevice& rDevice, const ::tools::Rectangle& rRepaintArea) override + { + rDevice.SetFillColor(maBackgroundColor); + rDevice.SetLineColor(); + rDevice.DrawRect(rRepaintArea); + } + + virtual void SetLayerInvalidator (const SharedILayerInvalidator&) override {} + + void SetColor (const Color& rColor) { maBackgroundColor = rColor; } + +private: + Color maBackgroundColor; +}; + +} + +SlideSorterView::SlideSorterView (SlideSorter& rSlideSorter) + : ::sd::View ( + *rSlideSorter.GetModel().GetDocument(), + rSlideSorter.GetContentWindow()->GetOutDev(), + rSlideSorter.GetViewShell()), + mrSlideSorter(rSlideSorter), + mrModel(rSlideSorter.GetModel()), + mbIsDisposed(false), + mpLayouter (new Layouter(rSlideSorter.GetContentWindow(), rSlideSorter.GetTheme())), + mbPageObjectVisibilitiesValid (false), + mpLayeredDevice(std::make_shared(rSlideSorter.GetContentWindow())), + maVisiblePageRange(-1,-1), + maPreviewSize(0,0), + mbPreciousFlagUpdatePending(true), + meOrientation(Layouter::GRID), + mpBackgroundPainter( + std::make_shared(mrSlideSorter.GetTheme()->GetColor(Theme::Color_Background))), + mpToolTip(new ToolTip(mrSlideSorter)), + mbIsRearrangePending(true) +{ + // Hide the page that contains the page objects. + SetPageVisible (false); + + // Register the background painter on level 1 to avoid the creation of a + // background buffer. + mpLayeredDevice->RegisterPainter(mpBackgroundPainter, 1); + + // Wrap a shared_ptr-held-wrapper around this view and register it as + // painter at the layered device. There is no explicit destruction: in + // the SlideSorterView destructor the layered device is destroyed and + // with it the only reference to the wrapper which therefore is also + // destroyed. + SharedILayerPainter pPainter = std::make_shared(*this); + + // The painter is placed on level 1 to avoid buffering. This should be + // a little faster during animations because the previews are painted + // directly into the window, not via the buffer. + mpLayeredDevice->RegisterPainter(pPainter, 1); +} + +SlideSorterView::~SlideSorterView() +{ + if ( ! mbIsDisposed) + { + OSL_ASSERT(mbIsDisposed); + Dispose(); + } +} + +void SlideSorterView::Init() +{ + HandleModelChange(); +} + +void SlideSorterView::Dispose() +{ + mpLayeredDevice->Dispose(); + mpPreviewCache.reset(); + + SetPageUnderMouse(SharedPageDescriptor()); + + // Hide the page to avoid problems in the view when deleting + // visualized objects + HideSdrPage(); + + // Deletion of the objects and the page will be done in SdrModel + // destructor (as long as objects and pages are added) + + OSL_ASSERT(mpLayeredDevice.use_count() == 1); + mpLayeredDevice.reset(); + + mbIsDisposed = true; +} + +sal_Int32 SlideSorterView::GetPageIndexAtPoint (const Point& rWindowPosition) const +{ + sal_Int32 nIndex (-1); + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + nIndex = mpLayouter->GetIndexAtPoint(pWindow->PixelToLogic(rWindowPosition), false, false); + + // Clip the page index against the page count. + if (nIndex >= mrModel.GetPageCount()) + nIndex = -1; + } + + return nIndex; +} + +Layouter& SlideSorterView::GetLayouter() { return *mpLayouter; } + +void SlideSorterView::ModelHasChanged() +{ + // Ignore this call. Rely on hints sent by the model to get informed of + // model changes. +} + +void SlideSorterView::PreModelChange() +{ + // Reset the slide under the mouse. It will be re-set in PostModelChange(). + SetPageUnderMouse(SharedPageDescriptor()); +} + +void SlideSorterView::PostModelChange() +{ + // In PreModelChange() the page objects have been released. Here we + // create new ones. + ::osl::MutexGuard aGuard (mrModel.GetMutex()); + + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel); + + // The new page objects have to be scaled and positioned. + RequestRearrange(); + RequestRepaint(); +} + +/** At the moment for every model change all page objects are destroyed and + re-created again. This can be optimized by accepting hints that + describe the type of change so that existing page objects can be + reused. +*/ +void SlideSorterView::HandleModelChange() +{ + PreModelChange (); + PostModelChange(); +} + +void SlideSorterView::HandleDrawModeChange() +{ + // Replace the preview cache with a new and empty one. The + // PreviewRenderer that is used by the cache is replaced by this as + // well. + mpPreviewCache.reset(); + GetPreviewCache()->InvalidateCache(); + + RequestRepaint(); +} + +void SlideSorterView::HandleDataChangeEvent() +{ + GetPageObjectPainter()->SetTheme(mrSlideSorter.GetTheme()); + + // Update the color used by the background painter. + std::shared_ptr pPainter ( + std::dynamic_pointer_cast(mpBackgroundPainter)); + if (pPainter) + pPainter->SetColor(mrSlideSorter.GetTheme()->GetColor(Theme::Color_Background)); + + RequestRepaint(); +} + +void SlideSorterView::Resize() +{ + UpdateOrientation(); + + mpLayeredDevice->Resize(); + RequestRearrange(); +} + +void SlideSorterView::RequestRearrange() +{ + mbIsRearrangePending = true; + Rearrange(); +} + +void SlideSorterView::Rearrange() +{ + if ( ! mbIsRearrangePending) + return; + if (mrModel.GetPageCount() <= 0) + return; + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if ( ! pWindow) + return; + const Size aWindowSize (pWindow->GetSizePixel()); + if (aWindowSize.IsEmpty()) + return; + + const bool bRearrangeSuccess ( + mpLayouter->Rearrange ( + meOrientation, + aWindowSize, + mrModel.GetPageDescriptor(0)->GetPage()->GetSize(), + mrModel.GetPageCount())); + if (bRearrangeSuccess) + { + mbIsRearrangePending = false; + Layout(); + UpdatePageUnderMouse(); + // RequestRepaint(); + } +} + +void SlideSorterView::UpdateOrientation() +{ + // The layout of slides depends on whether the slide sorter is + // displayed in the center or the side pane. + if (mrSlideSorter.GetViewShell()->IsMainViewShell()) + SetOrientation(Layouter::GRID); + else + { + // Get access to the docking window. + vcl::Window* pWindow = mrSlideSorter.GetContentWindow(); + PaneDockingWindow* pDockingWindow = nullptr; + while (pWindow!=nullptr && pDockingWindow==nullptr) + { + pDockingWindow = dynamic_cast(pWindow); + pWindow = pWindow->GetParent(); + } + + if (pDockingWindow != nullptr) + { + const ::tools::Long nScrollBarSize ( + Application::GetSettings().GetStyleSettings().GetScrollBarSize()); + switch (pDockingWindow->GetOrientation()) + { + case PaneDockingWindow::HorizontalOrientation: + if (SetOrientation(Layouter::HORIZONTAL)) + { + const Range aRange (mpLayouter->GetValidVerticalSizeRange()); + pDockingWindow->SetValidSizeRange(Range( + aRange.Min() + nScrollBarSize, + aRange.Max() + nScrollBarSize)); + } + break; + + case PaneDockingWindow::VerticalOrientation: + if (SetOrientation(Layouter::VERTICAL)) + { + const Range aRange (mpLayouter->GetValidHorizontalSizeRange()); + pDockingWindow->SetValidSizeRange(Range( + aRange.Min() + nScrollBarSize, + aRange.Max() + nScrollBarSize)); + } + break; + + case PaneDockingWindow::UnknownOrientation: + if (SetOrientation(Layouter::GRID)) + { + const sal_Int32 nAdditionalSize (10); + pDockingWindow->SetMinOutputSizePixel(Size( + mpLayouter->GetValidHorizontalSizeRange().Min() + + nScrollBarSize + + nAdditionalSize, + mpLayouter->GetValidVerticalSizeRange().Min() + + nScrollBarSize + + nAdditionalSize)); + } + return; + } + } + else + { + // We are not placed in a docking window. One possible reason + // is that the slide sorter is temporarily into a cache and was + // reparented to a non-docking window. + SetOrientation(Layouter::GRID); + } + } +} + +void SlideSorterView::Layout () +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + // Set the model area, i.e. the smallest rectangle that includes all + // page objects. + const ::tools::Rectangle aViewBox (mpLayouter->GetTotalBoundingBox()); + pWindow->SetViewOrigin (aViewBox.TopLeft()); + pWindow->SetViewSize (aViewBox.GetSize()); + + std::shared_ptr pPageObjectLayouter( + mpLayouter->GetPageObjectLayouter()); + if (pPageObjectLayouter) + { + const Size aNewPreviewSize (mpLayouter->GetPageObjectLayouter()->GetPreviewSize()); + if (maPreviewSize != aNewPreviewSize && GetPreviewCache()) + { + mpPreviewCache->ChangeSize(aNewPreviewSize, Bitmap::HasFastScale()); + maPreviewSize = aNewPreviewSize; + } + } + + // Iterate over all page objects and place them relative to the + // containing page. + model::PageEnumeration aPageEnumeration ( + model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + while (aPageEnumeration.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aPageEnumeration.GetNextElement()); + pDescriptor->SetBoundingBox(mpLayouter->GetPageObjectBox(pDescriptor->GetPageIndex(), false)); + } + } + + InvalidatePageObjectVisibilities (); +} + +void SlideSorterView::InvalidatePageObjectVisibilities() +{ + mbPageObjectVisibilitiesValid = false; +} + +void SlideSorterView::DeterminePageObjectVisibilities() +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (!pWindow) + return; + + // Set this flag to true here so that an invalidate during the + // visibility calculation can correctly invalidate it again. + mbPageObjectVisibilitiesValid = true; + + ::tools::Rectangle aViewArea (pWindow->PixelToLogic(::tools::Rectangle(Point(0,0),pWindow->GetSizePixel()))); + const Range aRange (mpLayouter->GetRangeOfVisiblePageObjects(aViewArea)); + const Range aUnion( + ::std::min(maVisiblePageRange.Min(), aRange.Min()), + ::std::max(maVisiblePageRange.Max(), aRange.Max())); + + // For page objects that just dropped off the visible area we + // decrease the priority of pending requests for preview bitmaps. + if (maVisiblePageRange != aRange) + mbPreciousFlagUpdatePending |= true; + + model::SharedPageDescriptor pDescriptor; + for (::tools::Long nIndex=aUnion.Min(); nIndex<=aUnion.Max(); nIndex++) + { + pDescriptor = mrModel.GetPageDescriptor(nIndex); + if (pDescriptor) + SetState( + pDescriptor, + PageDescriptor::ST_Visible, + aRange.Contains(nIndex)); + } + + // Broadcast a change of the set of visible page objects. + if (maVisiblePageRange != aRange) + { + maVisiblePageRange = aRange; + + // Tell the listeners that the visibility of some objects has + // changed. + ::std::vector>& aChangeListeners (maVisibilityChangeListeners); + for (const auto& rLink : aChangeListeners) + { + rLink.Call(nullptr); + } + } + + // Restore the mouse over state. + UpdatePageUnderMouse(); +} + +void SlideSorterView::UpdatePreciousFlags() +{ + if (!mbPreciousFlagUpdatePending) + return; + + mbPreciousFlagUpdatePending = false; + + model::SharedPageDescriptor pDescriptor; + std::shared_ptr pCache = GetPreviewCache(); + sal_Int32 nPageCount (mrModel.GetPageCount()); + + for (int nIndex=0; nIndex<=nPageCount; ++nIndex) + { + pDescriptor = mrModel.GetPageDescriptor(nIndex); + if (pDescriptor) + { + pCache->SetPreciousFlag( + pDescriptor->GetPage(), + maVisiblePageRange.Contains(nIndex)); + } + else + { + // At least one cache entry can not be updated. Remember to + // repeat the whole updating later and leave the loop now. + mbPreciousFlagUpdatePending = true; + break; + } + } +} + +bool SlideSorterView::SetOrientation (const Layouter::Orientation eOrientation) +{ + if (meOrientation != eOrientation) + { + meOrientation = eOrientation; + return true; + } + else + return false; +} + +void SlideSorterView::RequestRepaint() +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + mpLayeredDevice->InvalidateAllLayers( + ::tools::Rectangle( + pWindow->PixelToLogic(Point(0,0)), + pWindow->PixelToLogic(pWindow->GetSizePixel()))); + pWindow->Invalidate(); + } +} + +void SlideSorterView::RequestRepaint (const model::SharedPageDescriptor& rpDescriptor) +{ + if (rpDescriptor) + RequestRepaint(rpDescriptor->GetBoundingBox()); +} + +void SlideSorterView::RequestRepaint (const ::tools::Rectangle& rRepaintBox) +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + mpLayeredDevice->InvalidateAllLayers(rRepaintBox); + pWindow->Invalidate(rRepaintBox); + } +} + +void SlideSorterView::RequestRepaint (const vcl::Region& rRepaintRegion) +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow) + { + mpLayeredDevice->InvalidateAllLayers(rRepaintRegion); + pWindow->Invalidate(rRepaintRegion); + } +} + +::tools::Rectangle SlideSorterView::GetModelArea() const +{ + return mpLayouter->GetTotalBoundingBox(); +} + +#ifdef DEBUG_TIMING +static ::canvas::tools::ElapsedTime gaTimer; +static const size_t gFrameTimeCount (10); +static size_t gFrameTimeIndex (0); +static ::std::vector gFrameTimes (gFrameTimeCount, 0); +static double gFrameTimeSum (0); +static const ::tools::Rectangle gFrameTimeBox (10,10,150,20); +static double gnLastFrameStart = 0; +#endif + +void SlideSorterView::CompleteRedraw ( + OutputDevice* pDevice, + const vcl::Region& rPaintArea, + sdr::contact::ViewObjectContactRedirector* pRedirector) +{ + (void)pRedirector; + + if (comphelper::LibreOfficeKit::isActive()) + return; + + if (pDevice == nullptr || pDevice!=mrSlideSorter.GetContentWindow()->GetOutDev()) + return; + +#ifdef DEBUG_TIMING + const double nStartTime (gaTimer.getElapsedTime()); + SAL_INFO("sd.timing", "SlideSorterView::CompleteRedraw start" << (mnLockRedrawSmph ? " locked" : "")); +#endif + + // The parent implementation of CompleteRedraw is called only when + // painting is locked. We do all the painting ourself. When painting + // is locked the parent implementation keeps track of the repaint + // requests and later, when painting is unlocked, calls CompleteRedraw + // for all missed repaints. + + if (mnLockRedrawSmph == 0) + { + if (mpLayeredDevice->HandleMapModeChange()) + DeterminePageObjectVisibilities(); + mpLayeredDevice->Repaint(rPaintArea); + } + else + { + maRedrawRegion.Union(rPaintArea); + } + +#ifdef DEBUG_TIMING + const double nEndTime (gaTimer.getElapsedTime()); + SAL_INFO("sd.timing", "SlideSorterView::CompleteRedraw end after " << (nEndTime-nStartTime)*1000 << " ms"); + gFrameTimeSum -= gFrameTimes[gFrameTimeIndex]; + gFrameTimes[gFrameTimeIndex] = nStartTime - gnLastFrameStart; + gnLastFrameStart = nStartTime; + gFrameTimeSum += gFrameTimes[gFrameTimeIndex]; + gFrameTimeIndex = (gFrameTimeIndex+1) % gFrameTimeCount; + + mrSlideSorter.GetContentWindow()->SetFillColor(COL_BLUE); + mrSlideSorter.GetContentWindow()->DrawRect(gFrameTimeBox); + mrSlideSorter.GetContentWindow()->SetTextColor(COL_WHITE); + mrSlideSorter.GetContentWindow()->DrawText( + gFrameTimeBox, + OUString::number(1 / (gFrameTimeSum / gFrameTimeCount)), + DrawTextFlags::Right | DrawTextFlags::VCenter); + // mrSlideSorter.GetContentWindow()->Invalidate(gFrameTimeBox); +#endif +} + +void SlideSorterView::Paint ( + OutputDevice& rDevice, + const ::tools::Rectangle& rRepaintArea) +{ + if (rRepaintArea.IsEmpty()) + return; + + if ( ! mpPageObjectPainter) + if ( ! GetPageObjectPainter()) + return; + + // Update the page visibilities when they have been invalidated. + if ( ! mbPageObjectVisibilitiesValid) + DeterminePageObjectVisibilities(); + + if (mbPreciousFlagUpdatePending) + UpdatePreciousFlags(); + + if (mbIsRearrangePending) + Rearrange(); + + // Paint all page objects that are fully or partially inside the + // repaint region. + const Range aRange (mpLayouter->GetRangeOfVisiblePageObjects(rRepaintArea)); + // Try to prefetch all graphics from the pages to paint. This will be done + // in threads to be more efficient than loading them on-demand one by one. + std::vector graphics; + for (::tools::Long nIndex=aRange.Min(); nIndex<=aRange.Max(); ++nIndex) + { + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nIndex)); + if (!pDescriptor || ! pDescriptor->HasState(PageDescriptor::ST_Visible)) + continue; + pDescriptor->GetPage()->getGraphicsForPrefetch(graphics); + } + // Handle also one page before and after to have those in advance on scrolling. + for (::tools::Long nIndex : { aRange.Min() - 1, aRange.Max() + 1 }) + { + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nIndex)); + if (!pDescriptor) + continue; + pDescriptor->GetPage()->getGraphicsForPrefetch(graphics); + } + if(graphics.size() > 1) // threading does not help with loading just one + GraphicFilter::GetGraphicFilter().MakeGraphicsAvailableThreaded(graphics); + + for (::tools::Long nIndex=aRange.Min(); nIndex<=aRange.Max(); ++nIndex) + { + model::SharedPageDescriptor pDescriptor (mrModel.GetPageDescriptor(nIndex)); + if (!pDescriptor || ! pDescriptor->HasState(PageDescriptor::ST_Visible)) + continue; + + mpPageObjectPainter->PaintPageObject(rDevice, pDescriptor); + } +} + +void SlideSorterView::ConfigurationChanged ( + utl::ConfigurationBroadcaster* pBroadcaster, + ConfigurationHints nHint) +{ + // Some changes of the configuration (some of the colors for example) + // may affect the previews. Throw away the old ones and create new ones. + cache::PageCacheManager::Instance()->InvalidateAllCaches(); + + ::sd::View::ConfigurationChanged(pBroadcaster, nHint); + RequestRepaint(); + +} + +std::shared_ptr const & SlideSorterView::GetPreviewCache() +{ + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow && mpPreviewCache == nullptr) + { + mpPreviewCache = + std::make_shared( + mpLayouter->GetPageObjectSize(), + Bitmap::HasFastScale(), + std::make_shared(mrSlideSorter)); + } + + return mpPreviewCache; +} + +Range const & SlideSorterView::GetVisiblePageRange() +{ + if ( ! mbPageObjectVisibilitiesValid) + DeterminePageObjectVisibilities(); + return maVisiblePageRange; +} + +void SlideSorterView::AddVisibilityChangeListener (const Link& rListener) +{ + if (::std::find ( + maVisibilityChangeListeners.begin(), + maVisibilityChangeListeners.end(), + rListener) == maVisibilityChangeListeners.end()) + { + maVisibilityChangeListeners.push_back(rListener); + } +} + +void SlideSorterView::RemoveVisibilityChangeListener(const Link&rListener) +{ + maVisibilityChangeListeners.erase ( + ::std::find ( + maVisibilityChangeListeners.begin(), + maVisibilityChangeListeners.end(), + rListener)); +} + +ToolTip& SlideSorterView::GetToolTip() const +{ + OSL_ASSERT(mpToolTip); + return *mpToolTip; +} + +void SlideSorterView::DragFinished (sal_Int8 nDropAction) +{ + mrSlideSorter.GetController().GetClipboard().DragFinished(nDropAction); + + View::DragFinished(nDropAction); +} + +void SlideSorterView::UpdatePageUnderMouse () +{ + VclPtr pVScrollBar (mrSlideSorter.GetVerticalScrollBar()); + VclPtr pHScrollBar (mrSlideSorter.GetHorizontalScrollBar()); + if ((pVScrollBar && pVScrollBar->IsVisible() && pVScrollBar->IsTracking()) + || (pHScrollBar && pHScrollBar->IsVisible() && pHScrollBar->IsTracking())) + { + // One of the scroll bars is tracking mouse movement. Do not + // highlight the slide under the mouse in this case. + SetPageUnderMouse(SharedPageDescriptor()); + return; + } + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (pWindow && pWindow->IsVisible() && ! pWindow->IsMouseCaptured()) + { + const Window::PointerState aPointerState (pWindow->GetPointerState()); + const ::tools::Rectangle aWindowBox (pWindow->GetPosPixel(), pWindow->GetSizePixel()); + if (aWindowBox.Contains(aPointerState.maPos)) + { + UpdatePageUnderMouse(aPointerState.maPos); + return; + } + } + + SetPageUnderMouse(SharedPageDescriptor()); +} + +void SlideSorterView::UpdatePageUnderMouse ( + const Point& rMousePosition) +{ + SetPageUnderMouse(mrSlideSorter.GetController().GetPageAt(rMousePosition)); +} + +void SlideSorterView::SetPageUnderMouse ( + const model::SharedPageDescriptor& rpDescriptor) +{ + if (mpPageUnderMouse == rpDescriptor) + return; + + if (mpPageUnderMouse) + SetState(mpPageUnderMouse, PageDescriptor::ST_MouseOver, false); + + mpPageUnderMouse = rpDescriptor; + + if (mpPageUnderMouse) + SetState(mpPageUnderMouse, PageDescriptor::ST_MouseOver, true); + + // Change the quick help text to display the name of the page under + // the mouse. + mpToolTip->SetPage(rpDescriptor); +} + +bool SlideSorterView::SetState ( + const model::SharedPageDescriptor& rpDescriptor, + const PageDescriptor::State eState, + const bool bStateValue) +{ + if ( ! rpDescriptor) + return false; + + const bool bModified (rpDescriptor->SetState(eState, bStateValue)); + if ( ! bModified) + return false; + + // When the page object is not visible (i.e. not on the screen then + // nothing has to be painted. + if (rpDescriptor->HasState(PageDescriptor::ST_Visible)) + { + // For most states a change of that state leads to visible + // difference and we have to request a repaint. + if (eState != PageDescriptor::ST_WasSelected) + RequestRepaint(rpDescriptor); + } + + return bModified; +} + +std::shared_ptr const & SlideSorterView::GetPageObjectPainter() +{ + if ( ! mpPageObjectPainter) + mpPageObjectPainter = std::make_shared(mrSlideSorter); + return mpPageObjectPainter; +} + +//===== SlideSorterView::DrawLock ============================================= + +SlideSorterView::DrawLock::DrawLock (SlideSorter const & rSlideSorter) + : mrView(rSlideSorter.GetView()), + mpWindow(rSlideSorter.GetContentWindow()) +{ + if (mrView.mnLockRedrawSmph == 0) + mrView.maRedrawRegion.SetEmpty(); + ++mrView.mnLockRedrawSmph; +} + +SlideSorterView::DrawLock::~DrawLock() +{ + OSL_ASSERT(mrView.mnLockRedrawSmph>0); + --mrView.mnLockRedrawSmph; + if (mrView.mnLockRedrawSmph == 0) + if (mpWindow) + { + mpWindow->Invalidate(mrView.maRedrawRegion); + } +} + +void SlideSorterView::DrawLock::Dispose() +{ + mpWindow.reset(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsFramePainter.cxx b/sd/source/ui/slidesorter/view/SlsFramePainter.cxx new file mode 100644 index 000000000..31c301868 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsFramePainter.cxx @@ -0,0 +1,225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsFramePainter.hxx" +#include +#include + +namespace sd::slidesorter::view { + +FramePainter::FramePainter (const BitmapEx& rShadowBitmap) + : maTopLeft(rShadowBitmap,-1,-1), + maTop(rShadowBitmap,0,-1), + maTopRight(rShadowBitmap,+1,-1), + maLeft(rShadowBitmap,-1,0), + maRight(rShadowBitmap,+1,0), + maBottomLeft(rShadowBitmap,-1,+1), + maBottom(rShadowBitmap,0,+1), + maBottomRight(rShadowBitmap,+1,+1), + maCenter(rShadowBitmap,0,0), + mbIsValid(false) +{ + if (rShadowBitmap.GetSizePixel().Width() == rShadowBitmap.GetSizePixel().Height() + && (rShadowBitmap.GetSizePixel().Width()-1)%2 == 0 + && ((rShadowBitmap.GetSizePixel().Width()-1)/2)%2 == 1) + { + mbIsValid = true; + } + else + { + OSL_ASSERT(rShadowBitmap.GetSizePixel().Width() == rShadowBitmap.GetSizePixel().Height()); + OSL_ASSERT((rShadowBitmap.GetSizePixel().Width()-1)%2 == 0); + OSL_ASSERT(((rShadowBitmap.GetSizePixel().Width()-1)/2)%2 == 1); + } +} + +FramePainter::~FramePainter() +{ +} + +void FramePainter::PaintFrame ( + OutputDevice& rDevice, + const ::tools::Rectangle& rBox) const +{ + if ( ! mbIsValid) + return; + + // Paint the shadow. + maTopLeft.PaintCorner(rDevice, rBox.TopLeft()); + maTopRight.PaintCorner(rDevice, rBox.TopRight()); + maBottomLeft.PaintCorner(rDevice, rBox.BottomLeft()); + maBottomRight.PaintCorner(rDevice, rBox.BottomRight()); + maLeft.PaintSide(rDevice, rBox.TopLeft(), rBox.BottomLeft(), maTopLeft, maBottomLeft); + maRight.PaintSide(rDevice, rBox.TopRight(), rBox.BottomRight(), maTopRight, maBottomRight); + maTop.PaintSide(rDevice, rBox.TopLeft(), rBox.TopRight(), maTopLeft, maTopRight); + maBottom.PaintSide(rDevice, rBox.BottomLeft(), rBox.BottomRight(), maBottomLeft, maBottomRight); + maCenter.PaintCenter(rDevice,rBox); +} + +void FramePainter::AdaptColor ( + const Color aNewColor) +{ + // Get the source color. + if (maCenter.maBitmap.IsEmpty()) + return; + const Color aSourceColor = maCenter.maBitmap.GetPixelColor(0,0); + + // Erase the center bitmap. + maCenter.maBitmap.SetEmpty(); + + // Replace the color in all bitmaps. + maTopLeft.maBitmap.Replace(aSourceColor, aNewColor); + maTop.maBitmap.Replace(aSourceColor, aNewColor); + maTopRight.maBitmap.Replace(aSourceColor, aNewColor); + maLeft.maBitmap.Replace(aSourceColor, aNewColor); + maCenter.maBitmap.Replace(aSourceColor, aNewColor); + maRight.maBitmap.Replace(aSourceColor, aNewColor); + maBottomLeft.maBitmap.Replace(aSourceColor, aNewColor); + maBottom.maBitmap.Replace(aSourceColor, aNewColor); + maBottomRight.maBitmap.Replace(aSourceColor, aNewColor); +} + +//===== FramePainter::OffsetBitmap ============================================ + +FramePainter::OffsetBitmap::OffsetBitmap ( + const BitmapEx& rBitmap, + const sal_Int32 nHorizontalPosition, + const sal_Int32 nVerticalPosition) +{ + OSL_ASSERT(nHorizontalPosition>=-1 && nHorizontalPosition<=+1); + OSL_ASSERT(nVerticalPosition>=-1 && nVerticalPosition<=+1); + + const sal_Int32 nS (1); + const sal_Int32 nC (::std::max(0,(rBitmap.GetSizePixel().Width()-nS)/2)); + const sal_Int32 nO (nC/2); + + const Point aOrigin( + nHorizontalPosition<0 ? 0 : (nHorizontalPosition == 0 ? nC : nC+nS), + nVerticalPosition<0 ? 0 : (nVerticalPosition == 0 ? nC : nC+nS)); + const Size aSize( + nHorizontalPosition==0 ? nS : nC, + nVerticalPosition==0 ? nS : nC); + maBitmap = BitmapEx(rBitmap, aOrigin, aSize); + if (maBitmap.IsEmpty()) + return; + maOffset = Point( + nHorizontalPosition<0 ? -nO : nHorizontalPosition>0 ? -nO : 0, + nVerticalPosition<0 ? -nO : nVerticalPosition>0 ? -nO : 0); + + // Enlarge the side bitmaps so that painting the frame requires less + // paint calls. + const sal_Int32 nSideBitmapSize (64); + if (nHorizontalPosition == 0 && nVerticalPosition == 0) + { + maBitmap.Scale(Size(nSideBitmapSize,nSideBitmapSize)); + } + else if (nHorizontalPosition == 0) + { + maBitmap.Scale(Size(nSideBitmapSize,aSize.Height())); + } + else if (nVerticalPosition == 0) + { + maBitmap.Scale(Size(maBitmap.GetSizePixel().Width(), nSideBitmapSize)); + } +} + +void FramePainter::OffsetBitmap::PaintCorner ( + OutputDevice& rDevice, + const Point& rAnchor) const +{ + if ( ! maBitmap.IsEmpty()) + rDevice.DrawBitmapEx(rAnchor+maOffset, maBitmap); +} + +void FramePainter::OffsetBitmap::PaintSide ( + OutputDevice& rDevice, + const Point& rAnchor1, + const Point& rAnchor2, + const OffsetBitmap& rCornerBitmap1, + const OffsetBitmap& rCornerBitmap2) const +{ + if (maBitmap.IsEmpty()) + return; + + const Size aBitmapSize (maBitmap.GetSizePixel()); + if (rAnchor1.Y() == rAnchor2.Y()) + { + // Side is horizontal. + const sal_Int32 nY (rAnchor1.Y() + maOffset.Y()); + const sal_Int32 nLeft ( + rAnchor1.X() + + rCornerBitmap1.maBitmap.GetSizePixel().Width() + + rCornerBitmap1.maOffset.X()); + const sal_Int32 nRight ( + rAnchor2.X() + + rCornerBitmap2.maOffset.X() + - 1); + for (sal_Int32 nX=nLeft; nX<=nRight; nX+=aBitmapSize.Width()) + { + rDevice.DrawBitmapEx( + Point(nX,nY), + Size(std::min(aBitmapSize.Width(),static_cast<::tools::Long>(nRight-nX+1)),aBitmapSize.Height()), + maBitmap); + } + } + else if (rAnchor1.X() == rAnchor2.X()) + { + // Side is vertical. + const sal_Int32 nX (rAnchor1.X() + maOffset.X()); + const sal_Int32 nTop ( + rAnchor1.Y() + + rCornerBitmap1.maBitmap.GetSizePixel().Height() + + rCornerBitmap1.maOffset.Y()); + const sal_Int32 nBottom ( + rAnchor2.Y() + + rCornerBitmap2.maOffset.Y() + - 1); + for (sal_Int32 nY=nTop; nY<=nBottom; nY+=aBitmapSize.Height()) + { + rDevice.DrawBitmapEx( + Point(nX,nY), + Size(aBitmapSize.Width(), std::min(aBitmapSize.Height(), static_cast<::tools::Long>(nBottom-nY+1))), + maBitmap); + } + } + else + { + // Diagonal sides indicates an error. + OSL_ASSERT(false); + } +} + +void FramePainter::OffsetBitmap::PaintCenter ( + OutputDevice& rDevice, + const ::tools::Rectangle& rBox) const +{ + const Size aBitmapSize (maBitmap.GetSizePixel()); + for (::tools::Long nY=rBox.Top(); nY<=rBox.Bottom(); nY+=aBitmapSize.Height()) + for (::tools::Long nX=rBox.Left(); nX<=rBox.Right(); nX+=aBitmapSize.Width()) + rDevice.DrawBitmapEx( + Point(nX,nY), + Size( + ::std::min(aBitmapSize.Width(), rBox.Right()-nX+1), + std::min(aBitmapSize.Height(), rBox.Bottom()-nY+1)), + maBitmap); +} + +} // end of namespace sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsFramePainter.hxx b/sd/source/ui/slidesorter/view/SlsFramePainter.hxx new file mode 100644 index 000000000..9398cb94e --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsFramePainter.hxx @@ -0,0 +1,109 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace sd::slidesorter::view { + +class FramePainter +{ +public: + explicit FramePainter (const BitmapEx& rBitmap); + ~FramePainter(); + + /** Paint a border around the given box by using a set of bitmaps for + the corners and sides. + */ + void PaintFrame (OutputDevice&rDevice, const ::tools::Rectangle& rBox) const; + + /** Special functionality that takes the color from the center + bitmap and replaces that color in all bitmaps by the given new + color. Alpha values are not modified. + The center bitmap is erased. + */ + void AdaptColor (const Color aNewColor); + +private: + /** Bitmap with offset that is used when the bitmap is painted. The bitmap + */ + class OffsetBitmap { + public: + BitmapEx maBitmap; + Point maOffset; + + /** Create one of the eight shadow bitmaps from one that combines + them all. This larger bitmap is expected to have dimension NxN + with N=1+2*M. Of this larger bitmap there are created four + corner bitmaps of size 2*M x 2*M and four side bitmaps of sizes + 1xM (top and bottom) and Mx1 (left and right). The corner + bitmaps have each one quadrant of size MxM that is painted under + the interior of the frame. + @param rBitmap + The larger bitmap of which the eight shadow bitmaps are cut + out from. + @param nHorizontalPosition + Valid values are -1 (left), 0 (center), and +1 (right). + @param nVerticalPosition + Valid values are -1 (top), 0 (center), and +1 (bottom). + */ + OffsetBitmap ( + const BitmapEx& rBitmap, + const sal_Int32 nHorizontalPosition, + const sal_Int32 nVerticalPosition); + + /** Use the given device to paint the bitmap at the location that is + the sum of the given anchor and the internal offset. + */ + void PaintCorner (OutputDevice& rDevice, const Point& rAnchor) const; + + /** Use the given device to paint the bitmap stretched between the + two given locations. Offsets of the adjacent corner bitmaps and + the offset of the side bitmap are used to determine the area + that is to be filled with the side bitmap. + */ + void PaintSide ( + OutputDevice& rDevice, + const Point& rAnchor1, + const Point& rAnchor2, + const OffsetBitmap& rCornerBitmap1, + const OffsetBitmap& rCornerBitmap2) const; + + /** Fill the given rectangle with the bitmap. + */ + void PaintCenter ( + OutputDevice& rDevice, + const ::tools::Rectangle& rBox) const; + }; + OffsetBitmap maTopLeft; + OffsetBitmap maTop; + OffsetBitmap maTopRight; + OffsetBitmap maLeft; + OffsetBitmap maRight; + OffsetBitmap maBottomLeft; + OffsetBitmap maBottom; + OffsetBitmap maBottomRight; + OffsetBitmap maCenter; + bool mbIsValid; +}; + +} // end of namespace sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsInsertAnimator.cxx b/sd/source/ui/slidesorter/view/SlsInsertAnimator.cxx new file mode 100644 index 000000000..361c55f05 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsInsertAnimator.cxx @@ -0,0 +1,428 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace sd::slidesorter::view { + +namespace { + +class PageObjectRun; + +class AnimatorAccess +{ +public: + virtual void AddRun (const std::shared_ptr& rRun) = 0; + virtual void RemoveRun (const std::shared_ptr& rRun) = 0; + virtual model::SlideSorterModel& GetModel () const = 0; + virtual view::SlideSorterView& GetView () const = 0; + virtual std::shared_ptr GetAnimator () = 0; + virtual VclPtr GetContentWindow () = 0; + +protected: + ~AnimatorAccess() COVERITY_NOEXCEPT_FALSE {} +}; + +/** Controller of the position offsets of all page objects in one row or one + column. +*/ +class PageObjectRun : public std::enable_shared_from_this +{ +public: + PageObjectRun ( + AnimatorAccess& rAnimatorAccess, + const sal_Int32 nRunIndex, + const sal_Int32 nStartIndex, + const sal_Int32 nEndIndex); + + void operator () (const double nTime); + + void UpdateOffsets( + const InsertPosition& rInsertPosition, + const view::Layouter& GetLayouter); + void ResetOffsets (const controller::Animator::AnimationMode eMode); + + /// Index of the row or column that this run represents. + sal_Int32 mnRunIndex; + /// The index at which to make place for the insertion indicator (-1 for + /// no indicator). + sal_Int32 mnLocalInsertIndex; + /// Index of the first page in the run. + sal_Int32 mnStartIndex; + /// Index of the last page in the run. + sal_Int32 mnEndIndex; + /// Offset of each item in the run at the start of the current animation. + ::std::vector maStartOffset; + /// Target offset of each item in the run at the end of the current animation. + ::std::vector maEndOffset; + /// Time at which the current animation started. + double mnStartTime; + + class Comparator + { + public: bool operator() ( + const std::shared_ptr& rpRunA, + const std::shared_ptr& rpRunB) const + { + return rpRunA->mnRunIndex < rpRunB->mnRunIndex; + } + }; +private: + controller::Animator::AnimationId mnAnimationId; + AnimatorAccess& mrAnimatorAccess; + ::std::function maAccelerationFunction; + + void RestartAnimation(); +}; +typedef std::shared_ptr SharedPageObjectRun; + +Point Blend (const Point& rPointA, const Point& rPointB, const double nT) +{ + return Point( + sal_Int32(rPointA.X() * (1-nT) + rPointB.X() * nT), + sal_Int32(rPointA.Y() * (1-nT) + rPointB.Y() * nT)); +} + +} // end of anonymous namespace + +class InsertAnimator::Implementation : public AnimatorAccess +{ +public: + explicit Implementation (SlideSorter& rSlideSorter); + virtual ~Implementation(); + + void SetInsertPosition ( + const InsertPosition& rInsertPosition, + const controller::Animator::AnimationMode eAnimationMode); + + virtual void AddRun (const std::shared_ptr& rRun) override; + virtual void RemoveRun (const std::shared_ptr& rRun) override; + + virtual model::SlideSorterModel& GetModel() const override { return mrModel; } + virtual view::SlideSorterView& GetView() const override { return mrView; } + virtual std::shared_ptr GetAnimator() override { return mpAnimator; } + virtual VclPtr GetContentWindow() override { return mrSlideSorter.GetContentWindow(); } + +private: + model::SlideSorterModel& mrModel; + view::SlideSorterView& mrView; + SlideSorter& mrSlideSorter; + std::shared_ptr mpAnimator; + typedef ::std::set RunContainer; + RunContainer maRuns; + InsertPosition maInsertPosition; + + SharedPageObjectRun GetRun ( + view::Layouter const & rLayouter, + const InsertPosition& rInsertPosition); + RunContainer::const_iterator FindRun (const sal_Int32 nRunIndex) const; +}; + +//===== InsertAnimator ======================================================== + +InsertAnimator::InsertAnimator (SlideSorter& rSlideSorter) + : mpImplementation(std::make_shared(rSlideSorter)) +{ +} + +void InsertAnimator::SetInsertPosition (const InsertPosition& rInsertPosition) +{ + mpImplementation->SetInsertPosition(rInsertPosition, controller::Animator::AM_Animated); +} + +void InsertAnimator::Reset (const controller::Animator::AnimationMode eMode) +{ + mpImplementation->SetInsertPosition(InsertPosition(), eMode); +} + +//===== InsertAnimator::Implementation ======================================== + +InsertAnimator::Implementation::Implementation (SlideSorter& rSlideSorter) + : mrModel(rSlideSorter.GetModel()), + mrView(rSlideSorter.GetView()), + mrSlideSorter(rSlideSorter), + mpAnimator(rSlideSorter.GetController().GetAnimator()) +{ +} + +InsertAnimator::Implementation::~Implementation() +{ + SetInsertPosition(InsertPosition(), controller::Animator::AM_Immediate); +} + +void InsertAnimator::Implementation::SetInsertPosition ( + const InsertPosition& rInsertPosition, + const controller::Animator::AnimationMode eMode) +{ + if (maInsertPosition == rInsertPosition) + return; + + SharedPageObjectRun pOldRun (GetRun(mrView.GetLayouter(), maInsertPosition)); + SharedPageObjectRun pCurrentRun (GetRun(mrView.GetLayouter(), rInsertPosition)); + maInsertPosition = rInsertPosition; + + // When the new insert position is in a different run then move the page + // objects in the old run to their default positions. + if (pOldRun != pCurrentRun && pOldRun) + pOldRun->ResetOffsets(eMode); + + if (pCurrentRun) + { + pCurrentRun->UpdateOffsets(rInsertPosition, mrView.GetLayouter()); + } +} + +SharedPageObjectRun InsertAnimator::Implementation::GetRun ( + view::Layouter const & rLayouter, + const InsertPosition& rInsertPosition) +{ + const sal_Int32 nRow (rInsertPosition.GetRow()); + if (nRow < 0) + return SharedPageObjectRun(); + + RunContainer::const_iterator iRun (maRuns.end()); + if (rLayouter.GetColumnCount() == 1) + { + // There is only one run that contains all slides. + if (maRuns.empty()) + maRuns.insert(std::make_shared( + *this, + 0, + 0, + mrModel.GetPageCount()-1)); + iRun = maRuns.begin(); + } + else + { + iRun = FindRun(nRow); + if (iRun == maRuns.end()) + { + // Create a new run. + const sal_Int32 nStartIndex (rLayouter.GetIndex(nRow, 0)); + const sal_Int32 nEndIndex (rLayouter.GetIndex(nRow, rLayouter.GetColumnCount()-1)); + if (nStartIndex <= nEndIndex) + { + iRun = maRuns.insert(std::make_shared( + *this, + nRow, + nStartIndex, + nEndIndex)).first; + OSL_ASSERT(iRun != maRuns.end()); + } + } + } + + if (iRun != maRuns.end()) + return *iRun; + else + return SharedPageObjectRun(); +} + +InsertAnimator::Implementation::RunContainer::const_iterator + InsertAnimator::Implementation::FindRun (const sal_Int32 nRunIndex) const +{ + return std::find_if( + maRuns.begin(), + maRuns.end(), + [nRunIndex] (std::shared_ptr const& rRun) + { return rRun->mnRunIndex == nRunIndex; }); +} + +void InsertAnimator::Implementation::AddRun (const std::shared_ptr& rRun) +{ + if (rRun) + { + maRuns.insert(rRun); + } + else + { + OSL_ASSERT(rRun); + } +} + +void InsertAnimator::Implementation::RemoveRun (const std::shared_ptr& rRun) +{ + if (rRun) + { + // Do not remove runs that show the space for the insertion indicator. + if (rRun->mnLocalInsertIndex == -1) + { + InsertAnimator::Implementation::RunContainer::const_iterator iRun (FindRun(rRun->mnRunIndex)); + if (iRun != maRuns.end()) + { + OSL_ASSERT(*iRun == rRun); + maRuns.erase(iRun); + } + } + } + else + { + OSL_ASSERT(rRun); + } +} + +//===== PageObjectRun ========================================================= + +PageObjectRun::PageObjectRun ( + AnimatorAccess& rAnimatorAccess, + const sal_Int32 nRunIndex, + const sal_Int32 nStartIndex, + const sal_Int32 nEndIndex) + : mnRunIndex(nRunIndex), + mnLocalInsertIndex(-1), + mnStartIndex(nStartIndex), + mnEndIndex(nEndIndex), + mnStartTime(-1), + mnAnimationId(controller::Animator::NotAnAnimationId), + mrAnimatorAccess(rAnimatorAccess), + maAccelerationFunction( + controller::AnimationParametricFunction( + controller::AnimationBezierFunction (0.1,0.7))) +{ + maStartOffset.resize(nEndIndex - nStartIndex + 1); + maEndOffset.resize(nEndIndex - nStartIndex + 1); +} + +void PageObjectRun::UpdateOffsets( + const InsertPosition& rInsertPosition, + const view::Layouter& rLayouter) +{ + const bool bIsVertical (rLayouter.GetColumnCount()==1); + const sal_Int32 nLocalInsertIndex(bIsVertical + ? rInsertPosition.GetRow() + : rInsertPosition.GetColumn()); + if (nLocalInsertIndex == mnLocalInsertIndex) + return; + + mnLocalInsertIndex = nLocalInsertIndex; + + model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); + const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1); + for (sal_Int32 nIndex=0; nIndexGetVisualState().GetLocationOffset(); + maEndOffset[nIndex] = nIndex < mnLocalInsertIndex + ? rInsertPosition.GetLeadingOffset() + : rInsertPosition.GetTrailingOffset(); + if (bIsVertical) + maEndOffset[nIndex].setX( 0 ); + else + maEndOffset[nIndex].setY( 0 ); + } + RestartAnimation(); +} + +void PageObjectRun::ResetOffsets (const controller::Animator::AnimationMode eMode) +{ + mnLocalInsertIndex = -1; + const sal_Int32 nRunLength (mnEndIndex - mnStartIndex + 1); + model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); + view::SlideSorterView& rView (mrAnimatorAccess.GetView()); + for (sal_Int32 nIndex=0; nIndexGetVisualState().GetLocationOffset(); + else + { + const ::tools::Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox()); + pDescriptor->GetVisualState().SetLocationOffset(Point(0,0)); + rView.RequestRepaint(aOldBoundingBox); + rView.RequestRepaint(pDescriptor); + } + } + maEndOffset[nIndex] = Point(0,0); + } + if (eMode == controller::Animator::AM_Animated) + RestartAnimation(); + else + mrAnimatorAccess.RemoveRun(shared_from_this()); +} + +void PageObjectRun::RestartAnimation() +{ + // Stop the current animation. + if (mnAnimationId != controller::Animator::NotAnAnimationId) + { + mrAnimatorAccess.GetAnimator()->RemoveAnimation(mnAnimationId); + } + + // Restart the animation. + mrAnimatorAccess.AddRun(shared_from_this()); + auto sharedThis(shared_from_this()); + mnAnimationId = mrAnimatorAccess.GetAnimator()->AddAnimation( + [this] (double const val) { (*this)(val); }, + [sharedThis] () { sharedThis->mrAnimatorAccess.RemoveRun(sharedThis); } + ); +} + +void PageObjectRun::operator () (const double nGlobalTime) +{ + if (mnStartTime < 0) + mnStartTime = nGlobalTime; + + double nLocalTime (nGlobalTime - mnStartTime); + if (nLocalTime > 1.0) + nLocalTime = 1.0; + nLocalTime = maAccelerationFunction(nLocalTime); + + model::SlideSorterModel& rModel (mrAnimatorAccess.GetModel()); + view::SlideSorterView& rView (mrAnimatorAccess.GetView()); + for (sal_Int32 nIndex=mnStartIndex; nIndex<=mnEndIndex; ++nIndex) + { + model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex)); + if ( ! pDescriptor) + continue; + const ::tools::Rectangle aOldBoundingBox (pDescriptor->GetBoundingBox()); + pDescriptor->GetVisualState().SetLocationOffset( + Blend( + maStartOffset[nIndex-mnStartIndex], + maEndOffset[nIndex-mnStartIndex], + nLocalTime)); + + // Request a repaint of the old and new bounding box (which largely overlap.) + rView.RequestRepaint(aOldBoundingBox); + rView.RequestRepaint(pDescriptor); + } + + // Call Flush to make + // a) animations a bit more smooth and + // b) on Mac without the Flush a Reset of the page locations is not properly + // visualized when the mouse leaves the window during drag-and-drop. + mrAnimatorAccess.GetContentWindow()->GetOutDev()->Flush(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx b/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx new file mode 100644 index 000000000..c1eb0ea90 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsInsertionIndicatorOverlay.cxx @@ -0,0 +1,360 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include "SlsFramePainter.hxx" +#include "SlsLayeredDevice.hxx" +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace { + +const double gnPreviewOffsetScale = 1.0 / 8.0; + +::tools::Rectangle GrowRectangle (const ::tools::Rectangle& rBox, const sal_Int32 nOffset) +{ + return ::tools::Rectangle ( + rBox.Left() - nOffset, + rBox.Top() - nOffset, + rBox.Right() + nOffset, + rBox.Bottom() + nOffset); +} + +sal_Int32 RoundToInt (const double nValue) { return sal_Int32(::rtl::math::round(nValue)); } + +} // end of anonymous namespace + +namespace sd::slidesorter::view { + +//===== InsertionIndicatorOverlay =========================================== + +const sal_Int32 gnShadowBorder = 3; +const sal_Int32 gnLayerIndex = 2; + +InsertionIndicatorOverlay::InsertionIndicatorOverlay (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mbIsVisible(false), + mpShadowPainter( + new FramePainter(mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_RawInsertShadow))) +{ +} + +InsertionIndicatorOverlay::~InsertionIndicatorOverlay() +{ + // cid#1491947 silence Uncaught exception + suppress_fun_call_w_exception(Hide()); +} + +void InsertionIndicatorOverlay::Create (const SdTransferable* pTransferable) +{ + if (pTransferable == nullptr) + return; + + std::shared_ptr pData ( + controller::TransferableData::GetFromTransferable(pTransferable)); + if ( ! pData) + return; + sal_Int32 nSelectionCount (0); + if (pTransferable->HasPageBookmarks()) + nSelectionCount = pTransferable->GetPageBookmarks().size(); + else + { + DrawDocShell* pDataDocShell = dynamic_cast(pTransferable->GetDocShell().get()); + if (pDataDocShell != nullptr) + { + SdDrawDocument* pDataDocument = pDataDocShell->GetDoc(); + if (pDataDocument != nullptr) + nSelectionCount = pDataDocument->GetSdPageCount(PageKind::Standard); + } + } + Create(pData->GetRepresentatives(), nSelectionCount); +} + +void InsertionIndicatorOverlay::Create ( + const ::std::vector& rRepresentatives, + const sal_Int32 nSelectionCount) +{ + view::Layouter& rLayouter (mrSlideSorter.GetView().GetLayouter()); + const std::shared_ptr& pPageObjectLayouter ( + rLayouter.GetPageObjectLayouter()); + std::shared_ptr pTheme (mrSlideSorter.GetTheme()); + const Size aOriginalPreviewSize (pPageObjectLayouter->GetPreviewSize()); + + const double nPreviewScale (0.5); + const Size aPreviewSize ( + RoundToInt(aOriginalPreviewSize.Width()*nPreviewScale), + RoundToInt(aOriginalPreviewSize.Height()*nPreviewScale)); + const sal_Int32 nOffset ( + RoundToInt(std::min(aPreviewSize.Width(),aPreviewSize.Height()) * gnPreviewOffsetScale)); + + // Determine size and offset depending on the number of previews. + sal_Int32 nCount (rRepresentatives.size()); + if (nCount > 0) + --nCount; + Size aIconSize( + aPreviewSize.Width() + 2 * gnShadowBorder + nCount*nOffset, + aPreviewSize.Height() + 2 * gnShadowBorder + nCount*nOffset); + + // Create virtual devices for bitmap and mask whose bitmaps later be + // combined to form the BitmapEx of the icon. + ScopedVclPtrInstance pContent( + *mrSlideSorter.GetContentWindow()->GetOutDev(), DeviceFormat::DEFAULT, DeviceFormat::DEFAULT); + pContent->SetOutputSizePixel(aIconSize); + + pContent->SetFillColor(); + pContent->SetLineColor(pTheme->GetColor(Theme::Color_PreviewBorder)); + const Point aOffset = PaintRepresentatives(*pContent, aPreviewSize, nOffset, rRepresentatives); + + PaintPageCount(*pContent, nSelectionCount, aPreviewSize, aOffset); + + maIcon = pContent->GetBitmapEx(Point(0,0), aIconSize); + maIcon.Scale(aIconSize); +} + +Point InsertionIndicatorOverlay::PaintRepresentatives ( + OutputDevice& rContent, + const Size& rPreviewSize, + const sal_Int32 nOffset, + const ::std::vector& rRepresentatives) const +{ + const Point aOffset (0,rRepresentatives.size()==1 ? -nOffset : 0); + + // Paint the pages. + Point aPageOffset (0,0); + double nTransparency (0); + const BitmapEx aExclusionOverlay (mrSlideSorter.GetTheme()->GetIcon(Theme::Icon_HideSlideOverlay)); + for (sal_Int32 nIndex=2; nIndex>=0; --nIndex) + { + if (rRepresentatives.size() <= o3tl::make_unsigned(nIndex)) + continue; + switch(nIndex) + { + case 0 : + aPageOffset = Point(0, nOffset); + nTransparency = 0.85; + break; + case 1: + aPageOffset = Point(nOffset, 0); + nTransparency = 0.75; + break; + case 2: + aPageOffset = Point(2*nOffset, 2*nOffset); + nTransparency = 0.65; + break; + } + aPageOffset += aOffset; + aPageOffset.AdjustX(gnShadowBorder ); + aPageOffset.AdjustY(gnShadowBorder ); + + // Paint the preview. + BitmapEx aPreview (rRepresentatives[nIndex].maBitmap); + aPreview.Scale(rPreviewSize, BmpScaleFlag::BestQuality); + rContent.DrawBitmapEx(aPageOffset, aPreview); + + // When the page is marked as excluded from the slide show then + // paint an overlay that visualizes this. + if (rRepresentatives[nIndex].mbIsExcluded) + { + const vcl::Region aSavedClipRegion (rContent.GetClipRegion()); + rContent.IntersectClipRegion(::tools::Rectangle(aPageOffset, rPreviewSize)); + // Paint bitmap tiled over the preview to mark it as excluded. + const sal_Int32 nIconWidth (aExclusionOverlay.GetSizePixel().Width()); + const sal_Int32 nIconHeight (aExclusionOverlay.GetSizePixel().Height()); + if (nIconWidth>0 && nIconHeight>0) + { + for (::tools::Long nX=0; nXPaintFrame(rContent, aBorderBox); + } + + return aPageOffset; +} + +void InsertionIndicatorOverlay::PaintPageCount ( + OutputDevice& rDevice, + const sal_Int32 nSelectionCount, + const Size& rPreviewSize, + const Point& rFirstPageOffset) const +{ + // Paint the number of slides. + std::shared_ptr pTheme (mrSlideSorter.GetTheme()); + std::shared_ptr pFont(Theme::GetFont(Theme::Font_PageCount, rDevice)); + if (!pFont) + return; + + OUString sNumber (OUString::number(nSelectionCount)); + + // Determine the size of the (painted) text and create a bounding + // box that centers the text on the first preview. + rDevice.SetFont(*pFont); + ::tools::Rectangle aTextBox; + rDevice.GetTextBoundRect(aTextBox, sNumber); + Point aTextOffset (aTextBox.TopLeft()); + Size aTextSize (aTextBox.GetSize()); + // Place text inside the first page preview. + Point aTextLocation(rFirstPageOffset); + // Center the text. + aTextLocation += Point( + (rPreviewSize.Width()-aTextBox.GetWidth())/2, + (rPreviewSize.Height()-aTextBox.GetHeight())/2); + aTextBox = ::tools::Rectangle(aTextLocation, aTextSize); + + // Paint background, border and text. + static const sal_Int32 nBorder = 5; + rDevice.SetFillColor(pTheme->GetColor(Theme::Color_Selection)); + rDevice.SetLineColor(pTheme->GetColor(Theme::Color_Selection)); + rDevice.DrawRect(GrowRectangle(aTextBox, nBorder)); + + rDevice.SetFillColor(); + rDevice.SetLineColor(pTheme->GetColor(Theme::Color_PageCountFontColor)); + rDevice.DrawRect(GrowRectangle(aTextBox, nBorder-1)); + + rDevice.SetTextColor(pTheme->GetColor(Theme::Color_PageCountFontColor)); + rDevice.DrawText(aTextBox.TopLeft()-aTextOffset, sNumber); +} + +void InsertionIndicatorOverlay::SetLocation (const Point& rLocation) +{ + const Point aTopLeft ( + rLocation - Point( + maIcon.GetSizePixel().Width()/2, + maIcon.GetSizePixel().Height()/2)); + if (maLocation != aTopLeft) + { + const ::tools::Rectangle aOldBoundingBox (GetBoundingBox()); + + maLocation = aTopLeft; + + if (mpLayerInvalidator && IsVisible()) + { + mpLayerInvalidator->Invalidate(aOldBoundingBox); + mpLayerInvalidator->Invalidate(GetBoundingBox()); + } + } +} + +void InsertionIndicatorOverlay::Paint ( + OutputDevice& rDevice, + const ::tools::Rectangle&) +{ + if ( ! IsVisible()) + return; + + rDevice.DrawImage(maLocation, Image(maIcon)); +} + +void InsertionIndicatorOverlay::SetLayerInvalidator (const SharedILayerInvalidator& rpInvalidator) +{ + mpLayerInvalidator = rpInvalidator; + + if (mbIsVisible && mpLayerInvalidator) + mpLayerInvalidator->Invalidate(GetBoundingBox()); +} + +void InsertionIndicatorOverlay::Show() +{ + if ( mbIsVisible) + return; + + mbIsVisible = true; + + std::shared_ptr pLayeredDevice ( + mrSlideSorter.GetView().GetLayeredDevice()); + if (pLayeredDevice) + { + pLayeredDevice->RegisterPainter(shared_from_this(), gnLayerIndex); + if (mpLayerInvalidator) + mpLayerInvalidator->Invalidate(GetBoundingBox()); + } +} + +void InsertionIndicatorOverlay::Hide() +{ + if (!mbIsVisible) + return; + + mbIsVisible = false; + + std::shared_ptr pLayeredDevice ( + mrSlideSorter.GetView().GetLayeredDevice()); + if (pLayeredDevice) + { + if (mpLayerInvalidator) + mpLayerInvalidator->Invalidate(GetBoundingBox()); + pLayeredDevice->RemovePainter(shared_from_this(), gnLayerIndex); + } +} + +::tools::Rectangle InsertionIndicatorOverlay::GetBoundingBox() const +{ + return ::tools::Rectangle(maLocation, maIcon.GetSizePixel()); +} + +Size InsertionIndicatorOverlay::GetSize() const +{ + return Size( + maIcon.GetSizePixel().Width() + 10, + maIcon.GetSizePixel().Height() + 10); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx b/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx new file mode 100644 index 000000000..b41bbe307 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsLayeredDevice.cxx @@ -0,0 +1,491 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsLayeredDevice.hxx" +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace sd::slidesorter::view { + +namespace { +const sal_Int32 gnMaximumLayerCount = 8; + +class LayerInvalidator : public ILayerInvalidator +{ +public: + LayerInvalidator ( + const std::shared_ptr& rpLayeredDevice, + sd::Window *pTargetWindow, + const int nLayer) + : mpLayeredDevice(rpLayeredDevice), + mpTargetWindow(pTargetWindow), + mnLayer(nLayer) + { + } + + virtual void Invalidate (const ::tools::Rectangle& rInvalidationBox) override + { + mpLayeredDevice->Invalidate(rInvalidationBox, mnLayer); + mpTargetWindow->Invalidate(rInvalidationBox); + } + +private: + const std::shared_ptr mpLayeredDevice; + VclPtr mpTargetWindow; + const int mnLayer; +}; + +void DeviceCopy ( + vcl::RenderContext& rTargetDevice, + vcl::RenderContext const & rSourceDevice, + const ::tools::Rectangle& rBox) +{ + rTargetDevice.DrawOutDev( + rBox.TopLeft(), + rBox.GetSize(), + rBox.TopLeft(), + rBox.GetSize(), + rSourceDevice); +} + +void ForAllRectangles (const vcl::Region& rRegion, const std::function& aFunction) +{ + OSL_ASSERT(aFunction); + RectangleVector aRectangles; + rRegion.GetRegionRectangles(aRectangles); + + if(aRectangles.empty()) + { + aFunction(::tools::Rectangle()); + } + else + { + for(const auto& rRect : aRectangles) + { + aFunction(rRect); + } + + //Region aMutableRegionCopy (rRegion); + //RegionHandle aHandle(aMutableRegionCopy.BeginEnumRects()); + //Rectangle aBox; + //while (aMutableRegionCopy.GetEnumRects(aHandle, aBox)) + // aFunction(aBox); + //aMutableRegionCopy.EndEnumRects(aHandle); + } +} + +class Layer +{ +public: + Layer(); + Layer(const Layer&) = delete; + Layer& operator=(const Layer&) = delete; + + void Initialize (sd::Window *pTargetWindow); + void InvalidateRectangle (const ::tools::Rectangle& rInvalidationBox); + void InvalidateRegion (const vcl::Region& rInvalidationRegion); + void Validate (const MapMode& rMapMode); + void Repaint ( + OutputDevice& rTargetDevice, + const ::tools::Rectangle& rRepaintRectangle); + void Resize (const Size& rSize); + void AddPainter (const SharedILayerPainter& rpPainter); + void RemovePainter (const SharedILayerPainter& rpPainter); + bool HasPainter() const; + void Dispose(); + +private: + ScopedVclPtr mpLayerDevice; + ::std::vector maPainters; + vcl::Region maInvalidationRegion; + + void ValidateRectangle (const ::tools::Rectangle& rBox); +}; +typedef std::shared_ptr SharedLayer; + +} // end of anonymous namespace + +class LayeredDevice::LayerContainer +{ +public: + LayerContainer() {} + + bool empty() const { return mvLayers.empty(); } + + size_t size() const { return mvLayers.size(); } + + const SharedLayer& back() const { return mvLayers.back(); } + + ::std::vector::const_iterator begin() const { return mvLayers.begin(); } + ::std::vector::const_iterator end() const { return mvLayers.end(); } + + void clear() { mvLayers.clear(); } + + void pop_back() { mvLayers.pop_back(); } + + void resize(size_t n) { mvLayers.resize(n); } + + SharedLayer& operator[](size_t i) { return mvLayers[i]; } + +private: + ::std::vector mvLayers; +}; + +//===== LayeredDevice ========================================================= + +LayeredDevice::LayeredDevice (const VclPtr& pTargetWindow) + : mpTargetWindow(pTargetWindow), + mpLayers(new LayerContainer()), + mpBackBuffer(VclPtr::Create(*mpTargetWindow->GetOutDev())), + maSavedMapMode(pTargetWindow->GetMapMode()) +{ + mpBackBuffer->SetOutputSizePixel(mpTargetWindow->GetSizePixel()); +} + +LayeredDevice::~LayeredDevice() +{ +} + +void LayeredDevice::Invalidate ( + const ::tools::Rectangle& rInvalidationArea, + const sal_Int32 nLayer) +{ + if (nLayer<0 || o3tl::make_unsigned(nLayer)>=mpLayers->size()) + { + OSL_ASSERT(nLayer>=0 && o3tl::make_unsigned(nLayer)size()); + return; + } + + (*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea); +} + +void LayeredDevice::InvalidateAllLayers (const ::tools::Rectangle& rInvalidationArea) +{ + for (size_t nLayer=0; nLayersize(); ++nLayer) + (*mpLayers)[nLayer]->InvalidateRectangle(rInvalidationArea); +} + +void LayeredDevice::InvalidateAllLayers (const vcl::Region& rInvalidationRegion) +{ + for (size_t nLayer=0; nLayersize(); ++nLayer) + (*mpLayers)[nLayer]->InvalidateRegion(rInvalidationRegion); +} + +void LayeredDevice::RegisterPainter ( + const SharedILayerPainter& rpPainter, + const sal_Int32 nLayer) +{ + OSL_ASSERT(mpLayers); + if ( ! rpPainter) + { + OSL_ASSERT(rpPainter); + return; + } + if (nLayer<0 || nLayer>=gnMaximumLayerCount) + { + OSL_ASSERT(nLayer>=0 && nLayer= mpLayers->size()) + { + const sal_Int32 nOldLayerCount (mpLayers->size()); + mpLayers->resize(nLayer+1); + + for (size_t nIndex=nOldLayerCount; nIndexsize(); ++nIndex) + (*mpLayers)[nIndex] = std::make_shared(); + } + + (*mpLayers)[nLayer]->AddPainter(rpPainter); + if (nLayer == 0) + (*mpLayers)[nLayer]->Initialize(mpTargetWindow); + + rpPainter->SetLayerInvalidator( + std::make_shared(shared_from_this(),mpTargetWindow,nLayer)); +} + +void LayeredDevice::RemovePainter ( + const SharedILayerPainter& rpPainter, + const sal_Int32 nLayer) +{ + if ( ! rpPainter) + { + OSL_ASSERT(rpPainter); + return; + } + if (nLayer<0 || o3tl::make_unsigned(nLayer)>=mpLayers->size()) + { + OSL_ASSERT(nLayer>=0 && o3tl::make_unsigned(nLayer)size()); + return; + } + + rpPainter->SetLayerInvalidator(SharedILayerInvalidator()); + + (*mpLayers)[nLayer]->RemovePainter(rpPainter); + + // Remove top most layers that do not contain any painters. + while ( ! mpLayers->empty() && ! mpLayers->back()->HasPainter()) + mpLayers->pop_back(); +} + +void LayeredDevice::Repaint (const vcl::Region& rRepaintRegion) +{ + // Validate the contents of all layers (that have their own devices.) + for (auto const& it : *mpLayers) + { + it->Validate(mpTargetWindow->GetMapMode()); + } + + ForAllRectangles(rRepaintRegion, + [this] (::tools::Rectangle const& r) { this->RepaintRectangle(r); }); +} + +void LayeredDevice::RepaintRectangle (const ::tools::Rectangle& rRepaintRectangle) +{ + if (mpLayers->empty()) + return; + else if (mpLayers->size() == 1) + { + // Just copy the main layer into the target device. + (*mpLayers)[0]->Repaint(*mpTargetWindow->GetOutDev(), rRepaintRectangle); + } + else + { + // Paint all layers first into the back buffer (to avoid flickering + // due to synchronous paints) and then copy that into the target + // device. + mpBackBuffer->SetMapMode(mpTargetWindow->GetMapMode()); + for (auto const& it : *mpLayers) + { + it->Repaint(*mpBackBuffer, rRepaintRectangle); + } + DeviceCopy(*mpTargetWindow->GetOutDev(), *mpBackBuffer, rRepaintRectangle); + } +} + +void LayeredDevice::Resize() +{ + const Size aSize (mpTargetWindow->GetSizePixel()); + mpBackBuffer->SetOutputSizePixel(aSize); + for (auto const& it : *mpLayers) + { + it->Resize(aSize); + } +} + +void LayeredDevice::Dispose() +{ + for (auto const& it : *mpLayers) + { + it->Dispose(); + } + mpLayers->clear(); +} + +bool LayeredDevice::HandleMapModeChange() +{ + const MapMode& rMapMode (mpTargetWindow->GetMapMode()); + if (maSavedMapMode == rMapMode) + return false; + + const ::tools::Rectangle aLogicWindowBox ( + mpTargetWindow->PixelToLogic(::tools::Rectangle(Point(0,0), mpTargetWindow->GetSizePixel()))); + if (maSavedMapMode.GetScaleX() != rMapMode.GetScaleX() + || maSavedMapMode.GetScaleY() != rMapMode.GetScaleY() + || maSavedMapMode.GetMapUnit() != rMapMode.GetMapUnit()) + { + // When the scale has changed then we have to paint everything. + InvalidateAllLayers(aLogicWindowBox); + } + else if (maSavedMapMode.GetOrigin() != rMapMode.GetOrigin()) + { + // Window has been scrolled. Adapt contents of backbuffers and + // layer devices. + const Point aDelta (rMapMode.GetOrigin() - maSavedMapMode.GetOrigin()); + mpBackBuffer->CopyArea( + aLogicWindowBox.TopLeft(), + mpTargetWindow->PixelToLogic(Point(0,0), maSavedMapMode), + aLogicWindowBox.GetSize()); + + // Invalidate the area(s) that have been exposed. + const ::tools::Rectangle aWindowBox (Point(0,0), mpTargetWindow->GetSizePixel()); + if (aDelta.Y() < 0) + InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle( + aWindowBox.Left(), + aWindowBox.Bottom()+aDelta.Y(), + aWindowBox.Right(), + aWindowBox.Bottom()))); + else if (aDelta.Y() > 0) + InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle( + aWindowBox.Left(), + aWindowBox.Top(), + aWindowBox.Right(), + aWindowBox.Top()+aDelta.Y()))); + if (aDelta.X() < 0) + InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle( + aWindowBox.Right()+aDelta.X(), + aWindowBox.Top(), + aWindowBox.Right(), + aWindowBox.Bottom()))); + else if (aDelta.X() > 0) + InvalidateAllLayers(mpTargetWindow->PixelToLogic(::tools::Rectangle( + aWindowBox.Left(), + aWindowBox.Top(), + aWindowBox.Left()+aDelta.X(), + aWindowBox.Bottom()))); + } + else + { + // Can this happen? Lets trigger a warning when it does. + OSL_ASSERT(false); + } + + maSavedMapMode = rMapMode; + + return true; +} + +//===== Layer ================================================================= + +Layer::Layer() +{ +} + +void Layer::Initialize (sd::Window *pTargetWindow) +{ +#if 0 + (void)pTargetWindow; +#else + if ( ! mpLayerDevice) + { + mpLayerDevice.disposeAndReset(VclPtr::Create(*pTargetWindow->GetOutDev())); + mpLayerDevice->SetOutputSizePixel(pTargetWindow->GetSizePixel()); + } +#endif +} + +void Layer::InvalidateRectangle (const ::tools::Rectangle& rInvalidationBox) +{ + maInvalidationRegion.Union(rInvalidationBox); +} + +void Layer::InvalidateRegion (const vcl::Region& rInvalidationRegion) +{ + maInvalidationRegion.Union(rInvalidationRegion); +} + +void Layer::Validate (const MapMode& rMapMode) +{ + if (mpLayerDevice && ! maInvalidationRegion.IsEmpty()) + { + vcl::Region aRegion (maInvalidationRegion); + maInvalidationRegion.SetEmpty(); + + mpLayerDevice->SetMapMode(rMapMode); + ForAllRectangles( + aRegion, + [this] (::tools::Rectangle const& r) { return this->ValidateRectangle(r); }); + } +} + +void Layer::ValidateRectangle (const ::tools::Rectangle& rBox) +{ + if ( ! mpLayerDevice) + return; + const vcl::Region aSavedClipRegion (mpLayerDevice->GetClipRegion()); + mpLayerDevice->IntersectClipRegion(rBox); + + for (const auto& rxPainter : maPainters) + { + rxPainter->Paint(*mpLayerDevice, rBox); + } + + mpLayerDevice->SetClipRegion(aSavedClipRegion); +} + +void Layer::Repaint ( + OutputDevice& rTargetDevice, + const ::tools::Rectangle& rRepaintRectangle) +{ + if (mpLayerDevice) + { + DeviceCopy(rTargetDevice, *mpLayerDevice, rRepaintRectangle); + } + else + { + for (auto const& it : maPainters) + { + it->Paint(rTargetDevice, rRepaintRectangle); + } + } +} + +void Layer::Resize (const Size& rSize) +{ + if (mpLayerDevice) + { + mpLayerDevice->SetOutputSizePixel(rSize); + maInvalidationRegion = ::tools::Rectangle(Point(0,0), rSize); + } +} + +void Layer::AddPainter (const SharedILayerPainter& rpPainter) +{ + OSL_ASSERT(::std::find(maPainters.begin(), maPainters.end(), rpPainter) == maPainters.end()); + + maPainters.push_back(rpPainter); +} + +void Layer::RemovePainter (const SharedILayerPainter& rpPainter) +{ + const ::std::vector::iterator iPainter ( + ::std::find(maPainters.begin(), maPainters.end(), rpPainter)); + if (iPainter != maPainters.end()) + { + maPainters.erase(iPainter); + } + else + { + SAL_WARN("sd", "LayeredDevice::RemovePainter called for painter that is not registered"); + } +} + +bool Layer::HasPainter() const +{ + return !maPainters.empty(); +} + +void Layer::Dispose() +{ + maPainters.clear(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx b/sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx new file mode 100644 index 000000000..5ec0d0e9f --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsLayeredDevice.hxx @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + +#include + +namespace sd { class Window; } +namespace tools { class Rectangle; } +namespace vcl { class Region; } + +class VirtualDevice; + +namespace sd::slidesorter::view { + +/** A simple wrapper around an OutputDevice that provides support for + independent layers and buffering. + Each layer may contain any number of painters. +*/ +class LayeredDevice + : public std::enable_shared_from_this + +{ +public: + explicit LayeredDevice (const VclPtr& pTargetWindow); + ~LayeredDevice (); + + void Invalidate ( + const ::tools::Rectangle& rInvalidationBox, + const sal_Int32 nLayer); + void InvalidateAllLayers ( + const ::tools::Rectangle& rInvalidationBox); + void InvalidateAllLayers ( + const vcl::Region& rInvalidationRegion); + + void RegisterPainter ( + const SharedILayerPainter& rPainter, + const sal_Int32 nLayer); + + void RemovePainter ( + const SharedILayerPainter& rPainter, + const sal_Int32 nLayer); + + bool HandleMapModeChange(); + void Repaint (const vcl::Region& rRepaintRegion); + + void Resize(); + + void Dispose(); + +private: + VclPtr mpTargetWindow; + class LayerContainer; + std::unique_ptr mpLayers; + ScopedVclPtr mpBackBuffer; + MapMode maSavedMapMode; + + void RepaintRectangle (const ::tools::Rectangle& rRepaintRectangle); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsLayouter.cxx b/sd/source/ui/slidesorter/view/SlsLayouter.cxx new file mode 100644 index 000000000..21f0be13c --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsLayouter.cxx @@ -0,0 +1,1225 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace sd::slidesorter::view { + +class Layouter::Implementation +{ +public: + VclPtr mpWindow; + static const sal_Int32 mnRequestedLeftBorder = 5; + static const sal_Int32 mnRequestedRightBorder = 5; + static const sal_Int32 mnRequestedTopBorder = 5; + static const sal_Int32 mnRequestedBottomBorder = 5; + sal_Int32 mnLeftBorder; + sal_Int32 mnRightBorder; + sal_Int32 mnTopBorder; + sal_Int32 mnBottomBorder; + static const sal_Int32 gnVerticalGap = 10 - 2*Theme_FocusIndicatorWidth; + static const sal_Int32 gnHorizontalGap = 10 - 2*Theme_FocusIndicatorWidth; + Size maMinimalSize; + Size maPreferredSize; + Size maMaximalSize; + sal_Int32 mnMinimalColumnCount; + sal_Int32 mnMaximalColumnCount; + sal_Int32 mnPageCount; + sal_Int32 mnColumnCount; + sal_Int32 mnRowCount; + /// The maximum number of columns. Can only be larger than the current + /// number of columns when there are not enough pages to fill all + /// available columns. + sal_Int32 mnMaxColumnCount; + /// The maximum number of rows. Can only be larger than the current + /// number of rows when there are not enough pages to fill all available + /// rows. + sal_Int32 mnMaxRowCount; + Size maPageObjectSize; + std::shared_ptr mpPageObjectLayouter; + std::shared_ptr mpTheme; + + /** Specify how the gap between two page objects is associated with the + page objects. + */ + enum GapMembership { + GM_NONE, // Gap is not associated with any page object. + GM_PREVIOUS, // The whole gap is associated with the previous page + // object (left or above the gap.) + GM_BOTH, // Half of the gap is associated with previous, half + // with the next page object. + GM_NEXT, // The whole gap is associated with the next page + // object (right or below the gap.) + GM_PAGE_BORDER + }; + + static Implementation* Create ( + const Implementation& rImplementation, + const Layouter::Orientation eOrientation); + + virtual Layouter::Orientation GetOrientation() const = 0; + + bool Rearrange ( + const Size& rWindowSize, + const Size& rPreviewModelSize, + const sal_uInt32 nPageCount); + + /** Calculate the row that the point with the given vertical coordinate + is over. The horizontal component is ignored. + @param nYPosition + Vertical position in model coordinates. + @param bIncludeBordersAndGaps + When this flag is then the area of borders and gaps are + interpreted as belonging to one of the rows. + @param eGapMembership + Specifies to what row the gap areas belong. Here GM_NONE + corresponds to bIncludeBordersAndGaps being . When + GM_BOTH is given then the upper half is associated to the row + above and the lower half to the row below. Values of + GM_PREVIOUS and GM_NEXT associate the whole gap area with the + row above or below respectively. + */ + sal_Int32 GetRowAtPosition ( + sal_Int32 nYPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const; + + /** Calculate the column that the point with the given horizontal + coordinate is over. The vertical component is ignored. + @param nXPosition + Horizontal position in model coordinates. + @param bIncludeBordersAndGaps + When this flag is then the area of borders and gaps are + interpreted as belonging to one of the columns. + @param eGapMembership + Specifies to what column the gap areas belong. + */ + sal_Int32 GetColumnAtPosition ( + sal_Int32 nXPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const; + + /** This method is typically called from GetRowAtPosition() and + GetColumnAtPosition() to handle a position that lies inside the gap + between two adjacent rows or columns. + @param nDistanceIntoGap + Vertical distance from the bottom of the upper row down into the + gap or horizontal distance from the right edge right into the + gap. + @param eGapMemberhship + This value decides what areas in the gap belong to which (or no) + row or column. + @param nIndex + The row index of the upper row or the column index of the left + column. + @param nGap + Width or height of the gap in model coordinates between the + page borders. + @return + Returns either the index of the upper row (as given as nRow), the + index of the lower row (nRow+1) or -1 to indicate that the + position belongs to no row. + */ + static sal_Int32 ResolvePositionInGap ( + sal_Int32 nDistanceIntoGap, + GapMembership eGapMembership, + sal_Int32 nIndex, + sal_Int32 nGap); + + /** Calculate the logical part of the insert position, i.e. the page + after which to insert. + */ + virtual void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const = 0; + + /** Calculate the geometrical part of the insert position, i.e. the + location of where to display the insertion indicator and the + distances about which the leading and trailing pages have to be + moved to make room for the indicator. + */ + void CalculateGeometricPosition ( + InsertPosition& rPosition, + const Size& rIndicatorSize, + const bool bIsVertical, + model::SlideSorterModel const & rModel) const; + + /** Return the bounding box of the preview or, when selected, of the page + object. Thus, it returns something like a visual bounding box. + */ + ::tools::Rectangle GetInnerBoundingBox ( + model::SlideSorterModel const & rModel, + const sal_Int32 nIndex) const; + + Range GetValidHorizontalSizeRange() const; + Range GetValidVerticalSizeRange() const; + + Range GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const; + sal_Int32 GetIndex ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const bool bClampToValidRange) const; + + ::tools::Rectangle GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap = false) const; + + ::tools::Rectangle GetPageObjectBox ( + const sal_Int32 nRow, + const sal_Int32 nColumn) const; + + ::tools::Rectangle AddBorderAndGap ( + const ::tools::Rectangle& rBoundingBox, + const sal_Int32 nRow, + const sal_Int32 nColumn) const; + + ::tools::Rectangle GetTotalBoundingBox() const; + + virtual ~Implementation(); + +protected: + Implementation ( + sd::Window *pWindow, + const std::shared_ptr& rpTheme); + explicit Implementation (const Implementation& rImplementation); + + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) = 0; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) = 0; + virtual Size CalculateTargetSize ( + const Size& rWindowSize) const = 0; + Size GetTargetSize ( + const Size& rWindowSize, + const bool bCalculateWidth, + const bool bCalculateHeight) const; + void CalculateVerticalLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const; +}; + +namespace { + +/** The vertical layouter has one column and as many rows as there are + pages. +*/ +class VerticalImplementation : public Layouter::Implementation +{ +public: + explicit VerticalImplementation (const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation() const override; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const override; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override; + virtual Size CalculateTargetSize ( + const Size& rWindowSize) const override; +}; + +/** The horizontal layouter has one row and as many columns as there are + pages. +*/ +class HorizontalImplementation : public Layouter::Implementation +{ +public: + explicit HorizontalImplementation(const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation() const override; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const override; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override; + virtual Size CalculateTargetSize ( + const Size& rWindowSize) const override; +}; + +/** The number of columns of the grid layouter is defined via a control in + the slide sorter tool bar. The number of rows is calculated from the + number of columns and the number of pages. +*/ +class GridImplementation : public Layouter::Implementation +{ +public: + GridImplementation ( + sd::Window *pWindow, + const std::shared_ptr& rpTheme); + explicit GridImplementation(const Implementation& rImplementation); + + virtual Layouter::Orientation GetOrientation() const override; + + void CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const override; + +protected: + virtual void CalculateRowAndColumnCount (const Size& rWindowSize) override; + virtual void CalculateMaxRowAndColumnCount (const Size& rWindowSize) override; + virtual Size CalculateTargetSize ( + const Size& rWindowSize) const override; +}; + +} + +//===== Layouter ============================================================== + +Layouter::Layouter ( + sd::Window *pWindow, + const std::shared_ptr& rpTheme) + : mpImplementation(new GridImplementation(pWindow, rpTheme)), + mpWindow(pWindow) +{ +} + +Layouter::~Layouter() +{ +} + +std::shared_ptr const & Layouter::GetPageObjectLayouter() const +{ + return mpImplementation->mpPageObjectLayouter; +} + +void Layouter::SetColumnCount ( + sal_Int32 nMinimalColumnCount, + sal_Int32 nMaximalColumnCount) +{ + if (nMinimalColumnCount <= nMaximalColumnCount) + { + mpImplementation->mnMinimalColumnCount = nMinimalColumnCount; + mpImplementation->mnMaximalColumnCount = nMaximalColumnCount; + } +} + +bool Layouter::Rearrange ( + const Orientation eOrientation, + const Size& rWindowSize, + const Size& rPageSize, + const sal_uInt32 nPageCount) +{ + OSL_ASSERT(mpWindow); + + if (eOrientation != mpImplementation->GetOrientation()) + mpImplementation.reset(Implementation::Create(*mpImplementation, eOrientation)); + + return mpImplementation->Rearrange(rWindowSize, rPageSize, nPageCount); +} + +sal_Int32 Layouter::GetColumnCount() const +{ + return mpImplementation->mnColumnCount; +} + +sal_Int32 Layouter::GetIndex (const sal_Int32 nRow, const sal_Int32 nColumn) const +{ + return mpImplementation->GetIndex(nRow,nColumn,true); +} + +Size const & Layouter::GetPageObjectSize() const +{ + return mpImplementation->maPageObjectSize; +} + +::tools::Rectangle Layouter::GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap) const +{ + return mpImplementation->GetPageObjectBox(nIndex, bIncludeBorderAndGap); +} + +::tools::Rectangle Layouter::GetTotalBoundingBox() const +{ + return mpImplementation->GetTotalBoundingBox(); +} + +InsertPosition Layouter::GetInsertPosition ( + const Point& rModelPosition, + const Size& rIndicatorSize, + model::SlideSorterModel const & rModel) const +{ + InsertPosition aPosition; + mpImplementation->CalculateLogicalInsertPosition( + rModelPosition, + aPosition); + mpImplementation->CalculateGeometricPosition( + aPosition, + rIndicatorSize, + GetColumnCount()==1, + rModel); + return aPosition; +} + +Range Layouter::GetValidHorizontalSizeRange() const +{ + return mpImplementation->GetValidHorizontalSizeRange(); +} + +Range Layouter::GetValidVerticalSizeRange() const +{ + return mpImplementation->GetValidVerticalSizeRange(); +} + +Range Layouter::GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const +{ + return mpImplementation->GetRangeOfVisiblePageObjects(aVisibleArea); +} + +sal_Int32 Layouter::GetIndexAtPoint ( + const Point& rPosition, + const bool bIncludePageBorders, + const bool bClampToValidRange) const +{ + const sal_Int32 nRow ( + mpImplementation->GetRowAtPosition ( + rPosition.Y(), + bIncludePageBorders, + bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE)); + const sal_Int32 nColumn ( + mpImplementation->GetColumnAtPosition ( + rPosition.X(), + bIncludePageBorders, + bIncludePageBorders ? Implementation::GM_PAGE_BORDER : Implementation::GM_NONE)); + + return mpImplementation->GetIndex(nRow,nColumn,bClampToValidRange); +} + +//===== Layouter::Implementation ============================================== + +Layouter::Implementation* Layouter::Implementation::Create ( + const Implementation& rImplementation, + const Layouter::Orientation eOrientation) +{ + switch (eOrientation) + { + case HORIZONTAL: return new HorizontalImplementation(rImplementation); + case VERTICAL: return new VerticalImplementation(rImplementation); + case GRID: + default: return new GridImplementation(rImplementation); + } +} + +Layouter::Implementation::Implementation ( + sd::Window *pWindow, + const std::shared_ptr& rpTheme) + : mpWindow(pWindow), + mnLeftBorder(5), + mnRightBorder(5), + mnTopBorder(5), + mnBottomBorder(5), + maMinimalSize(132,98), + maPreferredSize(200,150), + maMaximalSize(600,400), + mnMinimalColumnCount(1), + mnMaximalColumnCount(15), + mnPageCount(0), + mnColumnCount(1), + mnRowCount(0), + mnMaxColumnCount(0), + mnMaxRowCount(0), + maPageObjectSize(1,1), + mpTheme(rpTheme) +{ +} + +Layouter::Implementation::Implementation (const Implementation& rImplementation) + : mpWindow(rImplementation.mpWindow), + mnLeftBorder(rImplementation.mnLeftBorder), + mnRightBorder(rImplementation.mnRightBorder), + mnTopBorder(rImplementation.mnTopBorder), + mnBottomBorder(rImplementation.mnBottomBorder), + maMinimalSize(rImplementation.maMinimalSize), + maPreferredSize(rImplementation.maPreferredSize), + maMaximalSize(rImplementation.maMaximalSize), + mnMinimalColumnCount(rImplementation.mnMinimalColumnCount), + mnMaximalColumnCount(rImplementation.mnMaximalColumnCount), + mnPageCount(rImplementation.mnPageCount), + mnColumnCount(rImplementation.mnColumnCount), + mnRowCount(rImplementation.mnRowCount), + mnMaxColumnCount(rImplementation.mnMaxColumnCount), + mnMaxRowCount(rImplementation.mnMaxRowCount), + maPageObjectSize(rImplementation.maPageObjectSize), + mpTheme(rImplementation.mpTheme) +{ +} + +Layouter::Implementation::~Implementation() +{ +} + +bool Layouter::Implementation::Rearrange ( + const Size& rWindowSize, + const Size& rPreviewModelSize, + const sal_uInt32 nPageCount) +{ + mnPageCount = nPageCount; + + // Return early when the window or the model have not yet been initialized. + if (rWindowSize.IsEmpty()) + return false; + if (rPreviewModelSize.IsEmpty()) + return false; + + CalculateRowAndColumnCount(rWindowSize); + + // Update the border values. + mnLeftBorder = mnRequestedLeftBorder; + mnTopBorder = mnRequestedTopBorder; + mnRightBorder = mnRequestedRightBorder; + mnBottomBorder = mnRequestedBottomBorder; + if (mnColumnCount > 1) + { + int nMinimumBorderWidth = gnHorizontalGap/2; + if (mnLeftBorder < nMinimumBorderWidth) + mnLeftBorder = nMinimumBorderWidth; + if (mnRightBorder < nMinimumBorderWidth) + mnRightBorder = nMinimumBorderWidth; + } + else + { + int nMinimumBorderHeight = gnVerticalGap/2; + if (mnTopBorder < nMinimumBorderHeight) + mnTopBorder = nMinimumBorderHeight; + if (mnBottomBorder < nMinimumBorderHeight) + mnBottomBorder = nMinimumBorderHeight; + } + + mpPageObjectLayouter = + std::make_shared( + CalculateTargetSize(rWindowSize), + rPreviewModelSize, + mpWindow, + mnPageCount); + + maPageObjectSize = mpPageObjectLayouter->GetGridMaxSize(); + + CalculateMaxRowAndColumnCount(rWindowSize); + + return true; +} + +sal_Int32 Layouter::Implementation::GetRowAtPosition ( + sal_Int32 nYPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const +{ + sal_Int32 nRow = -1; + + const sal_Int32 nY = nYPosition - mnTopBorder; + if (nY >= 0) + { + // Vertical distance from one row to the next. + const sal_Int32 nRowOffset (maPageObjectSize.Height() + gnVerticalGap); + + // Calculate row consisting of page objects and gap below. + nRow = nY / nRowOffset; + + const sal_Int32 nDistanceIntoGap ((nY - nRow*nRowOffset) - maPageObjectSize.Height()); + // When inside the gap below then nYPosition is not over a page + // object. + if (nDistanceIntoGap > 0) + { + sal_Int32 nResolvedRow = ResolvePositionInGap( + nDistanceIntoGap, + eGapMembership, + nRow, + gnVerticalGap); + if (!bIncludeBordersAndGaps || nResolvedRow != -1) + nRow = nResolvedRow; + } + } + else if (bIncludeBordersAndGaps) + { + // We are in the top border area. Set nRow to the first row when + // the top border shall be considered to belong to the first row. + nRow = 0; + } + + return nRow; +} + +sal_Int32 Layouter::Implementation::GetColumnAtPosition ( + sal_Int32 nXPosition, + bool bIncludeBordersAndGaps, + GapMembership eGapMembership) const +{ + sal_Int32 nColumn = -1; + + sal_Int32 nX = nXPosition - mnLeftBorder; + if (nX >= 0) + { + // Horizontal distance from one column to the next. + const sal_Int32 nColumnOffset (maPageObjectSize.Width() + gnHorizontalGap); + + // Calculate row consisting of page objects and gap below. + nColumn = nX / nColumnOffset; + if (nColumn < 0) + nColumn = 0; + else if (nColumn >= mnColumnCount) + nColumn = mnColumnCount-1; + + const sal_Int32 nDistanceIntoGap ((nX - nColumn*nColumnOffset) - maPageObjectSize.Width()); + // When inside the gap at the right then nXPosition is not over a + // page object. + if (nDistanceIntoGap > 0) + { + sal_Int32 nResolvedColumn = ResolvePositionInGap( + nDistanceIntoGap, + eGapMembership, + nColumn, + gnHorizontalGap); + if (!bIncludeBordersAndGaps || nResolvedColumn != -1) + nColumn = nResolvedColumn; + } + } + else if (bIncludeBordersAndGaps) + { + // We are in the left border area. Set nColumn to the first column + // when the left border shall be considered to belong to the first + // column. + nColumn = 0; + } + return nColumn; +} + +sal_Int32 Layouter::Implementation::ResolvePositionInGap ( + sal_Int32 nDistanceIntoGap, + GapMembership eGapMembership, + sal_Int32 nIndex, + sal_Int32 nGap) +{ + switch (eGapMembership) + { + case GM_NONE: + // The gap is no man's land. + nIndex = -1; + break; + + case GM_BOTH: + { + // The lower half of the gap belongs to the next row or column. + sal_Int32 nFirstHalfGapWidth = nGap / 2; + if (nDistanceIntoGap > nFirstHalfGapWidth) + nIndex ++; + break; + } + + case GM_PREVIOUS: + // Row or column already at correct value. + break; + + case GM_NEXT: + // The complete gap belongs to the next row or column. + nIndex ++; + break; + + case GM_PAGE_BORDER: + if (nDistanceIntoGap > 0) + { + if (nDistanceIntoGap > nGap) + { + // Inside the border of the next row or column. + nIndex ++; + } + else + { + // Inside the gap between the page borders. + nIndex = -1; + } + } + break; + + default: + nIndex = -1; + } + + return nIndex; +} + +void Layouter::Implementation::CalculateGeometricPosition ( + InsertPosition& rPosition, + const Size& rIndicatorSize, + const bool bIsVertical, + model::SlideSorterModel const & rModel) const +{ + // 1. Determine right/bottom of the leading page and the left/top of the + // trailing page object and how to distribute the missing space. + sal_Int32 nLeadingLocation (0); + sal_Int32 nTrailingLocation (0); + bool bIsLeadingFixed (false); + bool bIsTrailingFixed (false); + sal_Int32 nSecondaryLocation (0); + const sal_Int32 nIndex (rPosition.GetIndex()); + + if (rPosition.IsAtRunStart()) + { + // Place indicator at the top of the column. + const ::tools::Rectangle aOuterBox (GetPageObjectBox(nIndex)); + const ::tools::Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex)); + if (bIsVertical) + { + nLeadingLocation = aOuterBox.Top(); + nTrailingLocation = aInnerBox.Top(); + nSecondaryLocation = aInnerBox.Center().X(); + } + else + { + nLeadingLocation = aOuterBox.Left(); + nTrailingLocation = aInnerBox.Left(); + nSecondaryLocation = aInnerBox.Center().Y(); + } + bIsLeadingFixed = true; + } + else if (rPosition.IsAtRunEnd()) + { + // Place indicator at the bottom/right of the column/row. + + const ::tools::Rectangle aOuterBox (GetPageObjectBox(nIndex-1)); + const ::tools::Rectangle aInnerBox (GetInnerBoundingBox(rModel, nIndex-1)); + if (bIsVertical) + { + nLeadingLocation = aInnerBox.Bottom(); + nTrailingLocation = aOuterBox.Bottom(); + nSecondaryLocation = aInnerBox.Center().X(); + } + else + { + nLeadingLocation = aInnerBox.Right(); + nTrailingLocation = aOuterBox.Right(); + nSecondaryLocation = aInnerBox.Center().Y(); + } + bIsTrailingFixed = true; + if ( ! rPosition.IsExtraSpaceNeeded()) + bIsLeadingFixed = true; + } + else + { + // Place indicator between two rows/columns. + const ::tools::Rectangle aBox1 (GetInnerBoundingBox(rModel, nIndex-1)); + const ::tools::Rectangle aBox2 (GetInnerBoundingBox(rModel, nIndex)); + if (bIsVertical) + { + nLeadingLocation = aBox1.Bottom(); + nTrailingLocation = aBox2.Top(); + nSecondaryLocation = (aBox1.Center().X() + aBox2.Center().X()) / 2; + } + else + { + nLeadingLocation = aBox1.Right(); + nTrailingLocation = aBox2.Left(); + nSecondaryLocation = (aBox1.Center().Y() + aBox2.Center().Y()) / 2; + } + } + + // 2. Calculate the location of the insert indicator and the offsets of + // leading and trailing pages. + const sal_Int32 nAvailableSpace (nTrailingLocation - nLeadingLocation); + const sal_Int32 nRequiredSpace (bIsVertical ? rIndicatorSize.Height():rIndicatorSize.Width()); + const sal_Int32 nMissingSpace (::std::max(sal_Int32(0), nRequiredSpace - nAvailableSpace)); + sal_Int32 nPrimaryLocation (0); + sal_Int32 nLeadingOffset (0); + sal_Int32 nTrailingOffset (0); + if (bIsLeadingFixed) + { + nPrimaryLocation = nLeadingLocation + nRequiredSpace/2; + if ( ! bIsTrailingFixed) + nTrailingOffset = nMissingSpace; + } + else if (bIsTrailingFixed) + { + nPrimaryLocation = nTrailingLocation - nRequiredSpace/2; + nLeadingOffset = -nMissingSpace; + } + else + { + nPrimaryLocation = (nLeadingLocation + nTrailingLocation) /2; + nLeadingOffset = -nMissingSpace/2; + nTrailingOffset = nMissingSpace + nLeadingOffset; + } + + if (bIsVertical) + { + rPosition.SetGeometricalPosition( + Point(nSecondaryLocation, nPrimaryLocation), + Point(0, nLeadingOffset), + Point(0, nTrailingOffset)); + } + else + { + rPosition.SetGeometricalPosition( + Point(nPrimaryLocation, nSecondaryLocation), + Point(nLeadingOffset, 0), + Point(nTrailingOffset, 0)); + } +} + +::tools::Rectangle Layouter::Implementation::GetInnerBoundingBox ( + model::SlideSorterModel const & rModel, + const sal_Int32 nIndex) const +{ + model::SharedPageDescriptor pDescriptor (rModel.GetPageDescriptor(nIndex)); + if ( ! pDescriptor) + return ::tools::Rectangle(); + + PageObjectLayouter::Part ePart = PageObjectLayouter::Part::Preview; + + if (pDescriptor->HasState(model::PageDescriptor::ST_Selected)) + ePart = PageObjectLayouter::Part::PageObject; + + return mpPageObjectLayouter->GetBoundingBox( + pDescriptor, ePart, + PageObjectLayouter::ModelCoordinateSystem, true); +} + +Range Layouter::Implementation::GetValidHorizontalSizeRange() const +{ + return Range( + mnLeftBorder + maMinimalSize.Width() + mnRightBorder, + mnLeftBorder + maMaximalSize.Width() + mnRightBorder); +} + +Range Layouter::Implementation::GetValidVerticalSizeRange() const +{ + return Range( + mnTopBorder + maMinimalSize.Height() + mnBottomBorder, + mnTopBorder + maMaximalSize.Height() + mnBottomBorder); +} + +Range Layouter::Implementation::GetRangeOfVisiblePageObjects (const ::tools::Rectangle& aVisibleArea) const +{ + // technically that's not empty, but it's the default, so... + if (aVisibleArea.IsEmpty()) + return Range(-1, -1); + + const sal_Int32 nRow0 (GetRowAtPosition(aVisibleArea.Top(), true, GM_NEXT)); + const sal_Int32 nCol0 (GetColumnAtPosition(aVisibleArea.Left(),true, GM_NEXT)); + const sal_Int32 nRow1 (GetRowAtPosition(aVisibleArea.Bottom(), true, GM_PREVIOUS)); + const sal_Int32 nCol1 (GetColumnAtPosition(aVisibleArea.Right(), true, GM_PREVIOUS)); + + // When start and end lie in different rows then the range may include + // slides outside (left or right of) the given area. + return Range(GetIndex(nRow0,nCol0,true), GetIndex(nRow1,nCol1,true)); +} + +Size Layouter::Implementation::GetTargetSize ( + const Size& rWindowSize, + const bool bCalculateWidth, + const bool bCalculateHeight) const +{ + if (mnColumnCount<=0 || mnRowCount<=0) + return maPreferredSize; + if ( ! (bCalculateWidth || bCalculateHeight)) + { + OSL_ASSERT(bCalculateWidth || bCalculateHeight); + return maPreferredSize; + } + + // Calculate the width of each page object. + Size aTargetSize (0,0); + if (bCalculateWidth) + aTargetSize.setWidth( + (rWindowSize.Width() - mnLeftBorder - mnRightBorder + - (mnColumnCount-1) * gnHorizontalGap) + / mnColumnCount); + else if (bCalculateHeight) + aTargetSize.setHeight( + (rWindowSize.Height() - mnTopBorder - mnBottomBorder + - (mnRowCount-1) * gnVerticalGap) + / mnRowCount); + + if (bCalculateWidth) + { + if (aTargetSize.Width() < maMinimalSize.Width()) + aTargetSize.setWidth(maMinimalSize.Width()); + else if (aTargetSize.Width() > maMaximalSize.Width()) + aTargetSize.setWidth(maMaximalSize.Width()); + } + else if (bCalculateHeight) + { + if (aTargetSize.Height() < maMinimalSize.Height()) + aTargetSize.setHeight(maMinimalSize.Height()); + else if (aTargetSize.Height() > maMaximalSize.Height()) + aTargetSize.setHeight(maMaximalSize.Height()); + } + + return aTargetSize; +} + +sal_Int32 Layouter::Implementation::GetIndex ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const bool bClampToValidRange) const +{ + if (nRow >= 0 && nColumn >= 0) + { + const sal_Int32 nIndex (nRow * mnColumnCount + nColumn); + if (nIndex >= mnPageCount) + if (bClampToValidRange) + return mnPageCount-1; + else + return -1; + else + return nIndex; + } + else if (bClampToValidRange) + return 0; + else + return -1; +} + +::tools::Rectangle Layouter::Implementation::GetPageObjectBox ( + const sal_Int32 nIndex, + const bool bIncludeBorderAndGap) const +{ + const sal_Int32 nRow (nIndex / mnColumnCount); + const sal_Int32 nColumn (nIndex % mnColumnCount); + + const ::tools::Rectangle aBoundingBox (GetPageObjectBox(nRow,nColumn)); + if (bIncludeBorderAndGap) + return AddBorderAndGap(aBoundingBox, nRow, nColumn); + else + return aBoundingBox; +} + +::tools::Rectangle Layouter::Implementation::GetPageObjectBox ( + const sal_Int32 nRow, + const sal_Int32 nColumn) const +{ + return ::tools::Rectangle( + Point (mnLeftBorder + + nColumn * maPageObjectSize.Width() + + std::max(nColumn,0) * gnHorizontalGap, + mnTopBorder + + nRow * maPageObjectSize.Height() + + std::max(nRow,0) * gnVerticalGap), + maPageObjectSize); +} + +::tools::Rectangle Layouter::Implementation::AddBorderAndGap ( + const ::tools::Rectangle& rBoundingBox, + const sal_Int32 nRow, + const sal_Int32 nColumn) const +{ + ::tools::Rectangle aBoundingBox (rBoundingBox); + + if (nColumn == 0) + aBoundingBox.SetLeft( 0 ); + else + aBoundingBox.AdjustLeft( -(gnHorizontalGap/2) ); + if (nColumn == mnColumnCount-1) + aBoundingBox.AdjustRight(mnRightBorder ); + else + aBoundingBox.AdjustRight(gnHorizontalGap/2 ); + if (nRow == 0) + aBoundingBox.SetTop( 0 ); + else + aBoundingBox.AdjustTop( -(gnVerticalGap/2) ); + if (nRow == mnRowCount-1) + aBoundingBox.AdjustBottom(mnBottomBorder ); + else + aBoundingBox.AdjustBottom(gnVerticalGap/2 ); + return aBoundingBox; +} + +::tools::Rectangle Layouter::Implementation::GetTotalBoundingBox() const +{ + sal_Int32 nHorizontalSize = 0; + sal_Int32 nVerticalSize = 0; + if (mnColumnCount > 0) + { + sal_Int32 nRowCount = (mnPageCount+mnColumnCount-1) / mnColumnCount; + nHorizontalSize = + mnLeftBorder + + mnRightBorder + + mnColumnCount * maPageObjectSize.Width(); + if (mnColumnCount > 1) + nHorizontalSize += (mnColumnCount-1) * gnHorizontalGap; + nVerticalSize = + mnTopBorder + + mnBottomBorder + + nRowCount * maPageObjectSize.Height(); + if (nRowCount > 1) + nVerticalSize += (nRowCount-1) * gnVerticalGap; + } + + return ::tools::Rectangle ( + Point(0,0), + Size (nHorizontalSize, nVerticalSize) + ); +} + +void Layouter::Implementation::CalculateVerticalLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + const sal_Int32 nY = rModelPosition.Y() - mnTopBorder + maPageObjectSize.Height()/2; + const sal_Int32 nRowHeight (maPageObjectSize.Height() + gnVerticalGap); + const sal_Int32 nRow (::std::min(mnPageCount, nY / nRowHeight)); + rPosition.SetLogicalPosition ( + nRow, + 0, + nRow, + (nRow == 0), + (nRow == mnRowCount), + (nRow >= mnMaxRowCount)); +} + +//===== HorizontalImplementation ================================================ + +HorizontalImplementation::HorizontalImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + +Layouter::Orientation HorizontalImplementation::GetOrientation() const +{ + return Layouter::HORIZONTAL; +} + +void HorizontalImplementation::CalculateRowAndColumnCount (const Size&) +{ + // Row and column count are fixed (for a given page count.) + mnColumnCount = mnPageCount; + mnRowCount = 1; +} + +void HorizontalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder) + / (maPageObjectSize.Width() + gnHorizontalGap); + mnMaxRowCount = 1; +} + +Size HorizontalImplementation::CalculateTargetSize ( + const Size& rWindowSize) const +{ + return Implementation::GetTargetSize(rWindowSize, false, true); +} + +void HorizontalImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2; + const sal_Int32 nColumnWidth (maPageObjectSize.Width() + gnHorizontalGap); + const sal_Int32 nColumn (::std::min(mnPageCount, nX / nColumnWidth)); + rPosition.SetLogicalPosition ( + 0, + nColumn, + nColumn, + (nColumn == 0), + (nColumn == mnColumnCount), + (nColumn >= mnMaxColumnCount)); +} + +//===== VerticalImplementation ================================================ + +VerticalImplementation::VerticalImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + +Layouter::Orientation VerticalImplementation::GetOrientation() const +{ + return Layouter::VERTICAL; +} + +void VerticalImplementation::CalculateRowAndColumnCount (const Size&) +{ + // Row and column count are fixed (for a given page count.) + mnRowCount = mnPageCount; + mnColumnCount = 1; + +} + +void VerticalImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder) + / (maPageObjectSize.Height() + gnVerticalGap); + mnMaxColumnCount = 1; +} + +Size VerticalImplementation::CalculateTargetSize ( + const Size& rWindowSize) const +{ + return Implementation::GetTargetSize(rWindowSize, true, false); +} + +void VerticalImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + return CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition); +} + +//===== GridImplementation ================================================ + +GridImplementation::GridImplementation ( + sd::Window *pWindow, + const std::shared_ptr& rpTheme) + : Implementation(pWindow, rpTheme) +{ +} + +GridImplementation::GridImplementation (const Implementation& rImplementation) + : Implementation(rImplementation) +{ +} + +Layouter::Orientation GridImplementation::GetOrientation() const +{ + return Layouter::GRID; +} + +void GridImplementation::CalculateRowAndColumnCount (const Size& rWindowSize) +{ + // Calculate the column count. + mnColumnCount + = (rWindowSize.Width() - mnRequestedLeftBorder - mnRequestedRightBorder) + / (maPreferredSize.Width() + gnHorizontalGap); + if (mnColumnCount < mnMinimalColumnCount) + mnColumnCount = mnMinimalColumnCount; + if (mnColumnCount > mnMaximalColumnCount) + mnColumnCount = mnMaximalColumnCount; + mnRowCount = (mnPageCount + mnColumnCount-1)/mnColumnCount; +} + +void GridImplementation::CalculateMaxRowAndColumnCount (const Size& rWindowSize) +{ + mnMaxColumnCount = (rWindowSize.Width() - mnLeftBorder - mnRightBorder) + / (maPageObjectSize.Width() + gnHorizontalGap); + mnMaxRowCount = (rWindowSize.Height() - mnTopBorder - mnBottomBorder) + / (maPageObjectSize.Height() + gnVerticalGap); +} + +Size GridImplementation::CalculateTargetSize ( + const Size& rWindowSize) const +{ + return Implementation::GetTargetSize(rWindowSize, true, true); +} + +void GridImplementation::CalculateLogicalInsertPosition ( + const Point& rModelPosition, + InsertPosition& rPosition) const +{ + if (mnColumnCount == 1) + { + CalculateVerticalLogicalInsertPosition(rModelPosition, rPosition); + } + else + { + // Handle the general case of more than one column. + sal_Int32 nRow (::std::min( + mnRowCount-1, + GetRowAtPosition (rModelPosition.Y(), true, GM_BOTH))); + const sal_Int32 nX = rModelPosition.X() - mnLeftBorder + maPageObjectSize.Width()/2; + const sal_Int32 nColumnWidth (maPageObjectSize.Width() + gnHorizontalGap); + sal_Int32 nColumn (::std::min(mnColumnCount, nX / nColumnWidth)); + sal_Int32 nIndex (nRow * mnColumnCount + nColumn); + bool bIsAtRunEnd (nColumn == mnColumnCount); + + if (nIndex >= mnPageCount) + { + nIndex = mnPageCount; + nRow = mnRowCount-1; + nColumn = ::std::min(::std::min(mnPageCount, mnColumnCount), nColumn); + bIsAtRunEnd = true; + } + + rPosition.SetLogicalPosition ( + nRow, + nColumn, + nIndex, + (nColumn == 0), + bIsAtRunEnd, + (nColumn >= mnMaxColumnCount)); + } +} + +//===== InsertPosition ======================================================== + +InsertPosition::InsertPosition() + : mnRow(-1), + mnColumn(-1), + mnIndex(-1), + mbIsAtRunStart(false), + mbIsAtRunEnd(false), + mbIsExtraSpaceNeeded(false), + maLocation(0,0), + maLeadingOffset(0,0), + maTrailingOffset(0,0) +{ +} + +bool InsertPosition::operator== (const InsertPosition& rInsertPosition) const +{ + // Do not compare the geometrical information (maLocation). + return mnRow==rInsertPosition.mnRow + && mnColumn==rInsertPosition.mnColumn + && mnIndex==rInsertPosition.mnIndex + && mbIsAtRunStart==rInsertPosition.mbIsAtRunStart + && mbIsAtRunEnd==rInsertPosition.mbIsAtRunEnd + && mbIsExtraSpaceNeeded==rInsertPosition.mbIsExtraSpaceNeeded; +} + +bool InsertPosition::operator!= (const InsertPosition& rInsertPosition) const +{ + return !operator==(rInsertPosition); +} + +void InsertPosition::SetLogicalPosition ( + const sal_Int32 nRow, + const sal_Int32 nColumn, + const sal_Int32 nIndex, + const bool bIsAtRunStart, + const bool bIsAtRunEnd, + const bool bIsExtraSpaceNeeded) +{ + mnRow = nRow; + mnColumn = nColumn; + mnIndex = nIndex; + mbIsAtRunStart = bIsAtRunStart; + mbIsAtRunEnd = bIsAtRunEnd; + mbIsExtraSpaceNeeded = bIsExtraSpaceNeeded; +} + +void InsertPosition::SetGeometricalPosition( + const Point& rLocation, + const Point& rLeadingOffset, + const Point& rTrailingOffset) +{ + maLocation = rLocation; + maLeadingOffset = rLeadingOffset; + maTrailingOffset = rTrailingOffset; +} + +} // end of namespace ::sd::slidesorter::namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsPageObjectLayouter.cxx b/sd/source/ui/slidesorter/view/SlsPageObjectLayouter.cxx new file mode 100644 index 000000000..b26eb0746 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsPageObjectLayouter.cxx @@ -0,0 +1,259 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +#include +#include + +namespace sd::slidesorter::view { + +namespace { +const sal_Int32 gnLeftPageNumberOffset = 2; +const sal_Int32 gnRightPageNumberOffset = 5; +const sal_Int32 gnOuterBorderWidth = 5; +const sal_Int32 gnInfoAreaMinWidth = 26; +} + +PageObjectLayouter::PageObjectLayouter ( + const Size& rPageObjectWindowSize, + const Size& rPageSize, + sd::Window *pWindow, + const sal_Int32 nPageCount) + : mpWindow(pWindow), + maTransitionEffectIcon(IconCache::Instance().GetIcon(BMP_FADE_EFFECT_INDICATOR)), + maCustomAnimationEffectIcon(IconCache::Instance().GetIcon(BMP_CUSTOM_ANIMATION_INDICATOR)), + mpPageNumberFont(Theme::GetFont(Theme::Font_PageNumber, *pWindow->GetOutDev())) +{ + const Size aPageNumberAreaSize (GetPageNumberAreaSize(nPageCount)); + + const int nMaximumBorderWidth (gnOuterBorderWidth); + const int nFocusIndicatorWidth (Theme_FocusIndicatorWidth); + + Size aPageObjectSize(rPageObjectWindowSize.Width(), rPageObjectWindowSize.Height()); + maPreviewBoundingBox = CalculatePreviewBoundingBox( + aPageObjectSize, + Size(rPageSize.Width(), rPageSize.Height()), + aPageNumberAreaSize.Width(), + nFocusIndicatorWidth); + maFocusIndicatorBoundingBox = ::tools::Rectangle(Point(0,0), aPageObjectSize); + maPageObjectBoundingBox = ::tools::Rectangle( + Point( + nFocusIndicatorWidth, + nFocusIndicatorWidth), + Size( + aPageObjectSize.Width()-2*nFocusIndicatorWidth, + aPageObjectSize.Height()-2*nFocusIndicatorWidth)); + + maPageNumberAreaBoundingBox = ::tools::Rectangle( + Point( + std::max(gnLeftPageNumberOffset, + sal_Int32(maPreviewBoundingBox.Left() + - gnRightPageNumberOffset + - aPageNumberAreaSize.Width())), + nMaximumBorderWidth), + aPageNumberAreaSize); + + const Size aIconSize (maTransitionEffectIcon.GetSizePixel()); + maTransitionEffectBoundingBox = ::tools::Rectangle( + Point( + (maPreviewBoundingBox.Left() - 2*aIconSize.Width()) / 2, + maPreviewBoundingBox.Bottom() - aIconSize.Height()), + aIconSize); + maCustomAnimationEffectBoundingBox = ::tools::Rectangle( + Point( + (maPreviewBoundingBox.Left() - 2*aIconSize.Width()) / 2, + maPreviewBoundingBox.Bottom() - 2*aIconSize.Height()), + aIconSize); +} + +PageObjectLayouter::~PageObjectLayouter() +{ +} + +::tools::Rectangle PageObjectLayouter::CalculatePreviewBoundingBox ( + Size& rPageObjectSize, + const Size& rPageSize, + const sal_Int32 nPageNumberAreaWidth, + const sal_Int32 nFocusIndicatorWidth) +{ + const sal_Int32 nIconWidth (maTransitionEffectIcon.GetSizePixel().Width()); + const sal_Int32 nLeftAreaWidth ( + ::std::max( + gnInfoAreaMinWidth, + gnRightPageNumberOffset + + ::std::max( + nPageNumberAreaWidth, + nIconWidth))); + sal_Int32 nPreviewWidth; + sal_Int32 nPreviewHeight; + const double nPageAspectRatio (double(rPageSize.Width()) / double(rPageSize.Height())); + if (rPageObjectSize.Height() == 0) + { + // Calculate height so that the preview fills the available + // horizontal space completely while observing the aspect ratio of + // the preview. + nPreviewWidth = rPageObjectSize.Width() + - nLeftAreaWidth - gnOuterBorderWidth - 2*nFocusIndicatorWidth - 1; + nPreviewHeight = ::basegfx::fround(nPreviewWidth / nPageAspectRatio); + rPageObjectSize.setHeight(nPreviewHeight + 2*gnOuterBorderWidth + 2*nFocusIndicatorWidth + 1); + } + else if (rPageObjectSize.Width() == 0) + { + // Calculate the width of the page object so that the preview fills + // the available vertical space completely while observing the + // aspect ratio of the preview. + nPreviewHeight = rPageObjectSize.Height() - 2*gnOuterBorderWidth - 2*nFocusIndicatorWidth - 1; + nPreviewWidth = ::basegfx::fround(nPreviewHeight * nPageAspectRatio); + rPageObjectSize.setWidth(nPreviewWidth + + nLeftAreaWidth + gnOuterBorderWidth + 2*nFocusIndicatorWidth + 1); + + } + else + { + // The size of the page object is given. Calculate the size of the + // preview. + nPreviewWidth = rPageObjectSize.Width() + - nLeftAreaWidth - gnOuterBorderWidth - 2*nFocusIndicatorWidth - 1; + nPreviewHeight = rPageObjectSize.Height() + - gnOuterBorderWidth - 2*nFocusIndicatorWidth - 1; + if (double(nPreviewWidth)/double(nPreviewHeight) > nPageAspectRatio) + nPreviewWidth = ::basegfx::fround(nPreviewHeight * nPageAspectRatio); + else + nPreviewHeight = ::basegfx::fround(nPreviewWidth / nPageAspectRatio); + } + // When the preview does not fill the available space completely then + // place it flush right and vertically centered. + const int nLeft (rPageObjectSize.Width() + - gnOuterBorderWidth - nPreviewWidth - nFocusIndicatorWidth - 1); + const int nTop ((rPageObjectSize.Height() - nPreviewHeight)/2); + return ::tools::Rectangle( + nLeft, + nTop, + nLeft + nPreviewWidth, + nTop + nPreviewHeight); +} + +::tools::Rectangle PageObjectLayouter::GetBoundingBox ( + const model::SharedPageDescriptor& rpPageDescriptor, + const Part ePart, + const CoordinateSystem eCoordinateSystem, + bool bIgnoreLocation) +{ + OSL_ASSERT(rpPageDescriptor); + Point aLocation(0,0); + if (rpPageDescriptor) + aLocation = rpPageDescriptor->GetLocation( bIgnoreLocation ); + return GetBoundingBox(aLocation, ePart, eCoordinateSystem); +} + +::tools::Rectangle PageObjectLayouter::GetBoundingBox ( + const Point& rPageObjectLocation, + const Part ePart, + const CoordinateSystem eCoordinateSystem) +{ + ::tools::Rectangle aBoundingBox; + switch (ePart) + { + case Part::FocusIndicator: + aBoundingBox = maFocusIndicatorBoundingBox; + break; + + case Part::PageObject: + aBoundingBox = maPageObjectBoundingBox; + break; + + case Part::Preview: + aBoundingBox = maPreviewBoundingBox; + break; + + case Part::PageNumber: + aBoundingBox = maPageNumberAreaBoundingBox; + break; + + case Part::TransitionEffectIndicator: + aBoundingBox = maTransitionEffectBoundingBox; + break; + case Part::CustomAnimationEffectIndicator: + aBoundingBox = maCustomAnimationEffectBoundingBox; + break; + } + + // Adapt coordinates to the requested coordinate system. + Point aLocation (rPageObjectLocation); + if (eCoordinateSystem == WindowCoordinateSystem) + aLocation += mpWindow->GetMapMode().GetOrigin(); + + return ::tools::Rectangle( + aBoundingBox.TopLeft() + aLocation, + aBoundingBox.BottomRight() + aLocation); +} + +Size PageObjectLayouter::GetPreviewSize () +{ + return GetBoundingBox(Point(0,0), PageObjectLayouter::Part::Preview, + WindowCoordinateSystem).GetSize(); +} + +Size PageObjectLayouter::GetGridMaxSize() +{ + return GetBoundingBox(Point(0,0), PageObjectLayouter::Part::FocusIndicator, + WindowCoordinateSystem).GetSize(); +} + +Size PageObjectLayouter::GetPageNumberAreaSize (const int nPageCount) +{ + OSL_ASSERT(mpWindow); + + // Set the correct font. + vcl::Font aOriginalFont (mpWindow->GetFont()); + if (mpPageNumberFont) + mpWindow->SetFont(*mpPageNumberFont); + + OUString sPageNumberTemplate; + if (nPageCount < 10) + sPageNumberTemplate = "9"; + else if (nPageCount < 100) + sPageNumberTemplate = "99"; + else if (nPageCount < 200) + // Just for the case that 1 is narrower than 9. + sPageNumberTemplate = "199"; + else if (nPageCount < 1000) + sPageNumberTemplate = "999"; + else + sPageNumberTemplate = "9999"; + // More than 9999 pages are not handled. + + const Size aSize ( + mpWindow->GetTextWidth(sPageNumberTemplate), + mpWindow->GetTextHeight()); + + mpWindow->SetFont(aOriginalFont); + + return aSize; +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx b/sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx new file mode 100644 index 000000000..feaf5a5fa --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsPageObjectPainter.cxx @@ -0,0 +1,442 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "SlsFramePainter.hxx" +#include +#include +#include +#include +#include +#include +#include + +using namespace ::drawinglayer::primitive2d; + +namespace sd::slidesorter::view { + +//===== PageObjectPainter ===================================================== + +PageObjectPainter::PageObjectPainter ( + const SlideSorter& rSlideSorter) + : mrLayouter(rSlideSorter.GetView().GetLayouter()), + mpCache(rSlideSorter.GetView().GetPreviewCache()), + mpTheme(rSlideSorter.GetTheme()), + mpPageNumberFont(Theme::GetFont(Theme::Font_PageNumber, *rSlideSorter.GetContentWindow()->GetOutDev())), + mpShadowPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_RawShadow))), + mpFocusBorderPainter(new FramePainter(mpTheme->GetIcon(Theme::Icon_FocusBorder))) +{ + // Replace the color (not the alpha values) in the focus border with a + // color derived from the current selection color. + Color aColor (mpTheme->GetColor(Theme::Color_Selection)); + sal_uInt16 nHue, nSat, nBri; + aColor.RGBtoHSB(nHue, nSat, nBri); + aColor = Color::HSBtoRGB(nHue, 28, 65); + mpFocusBorderPainter->AdaptColor(aColor); +} + +PageObjectPainter::~PageObjectPainter() +{ +} + +void PageObjectPainter::PaintPageObject ( + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) +{ + if (!UpdatePageObjectLayouter()) + return; + + PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get(); + // Turn off antialiasing to avoid the bitmaps from being + // shifted by fractions of a pixel and thus show blurry edges. + const AntialiasingFlags nSavedAntialiasingMode (rDevice.GetAntialiasing()); + rDevice.SetAntialiasing(nSavedAntialiasingMode & ~AntialiasingFlags::Enable); + + PaintBackground(pPageObjectLayouter, rDevice, rpDescriptor); + PaintPreview(pPageObjectLayouter, rDevice, rpDescriptor); + PaintPageNumber(pPageObjectLayouter, rDevice, rpDescriptor); + PaintTransitionEffect(pPageObjectLayouter, rDevice, rpDescriptor); + if (rpDescriptor->GetPage()->hasAnimationNode()) + PaintCustomAnimationEffect(pPageObjectLayouter, rDevice, rpDescriptor); + rDevice.SetAntialiasing(nSavedAntialiasingMode); +} + +bool PageObjectPainter::UpdatePageObjectLayouter() +{ + // The page object layouter is quite volatile. It may have been replaced + // since the last call. Update it now. + PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get(); + if ( ! pPageObjectLayouter) + { + OSL_FAIL("no page object layouter"); + return false; + } + + return true; +} + +void PageObjectPainter::SetTheme (const std::shared_ptr& rpTheme) +{ + mpTheme = rpTheme; +} + +void PageObjectPainter::PaintBackground ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const +{ + PaintBackgroundDetail(pPageObjectLayouter, rDevice, rpDescriptor); + + // Fill the interior of the preview area with the default background + // color of the page. + SdPage* pPage = rpDescriptor->GetPage(); + if (pPage != nullptr) + { + rDevice.SetFillColor(pPage->GetPageBackgroundColor(nullptr)); + rDevice.SetLineColor(pPage->GetPageBackgroundColor(nullptr)); + const ::tools::Rectangle aPreviewBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::ModelCoordinateSystem)); + rDevice.DrawRect(aPreviewBox); + } +} + +void PageObjectPainter::PaintPreview ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const +{ + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::ModelCoordinateSystem)); + + if (mpCache == nullptr) + return; + + const SdrPage* pPage = rpDescriptor->GetPage(); + mpCache->SetPreciousFlag(pPage, true); + + const BitmapEx aPreview (GetPreviewBitmap(rpDescriptor, &rDevice)); + if ( ! aPreview.IsEmpty()) + { + if (aPreview.GetSizePixel() != aBox.GetSize()) + rDevice.DrawBitmapEx(aBox.TopLeft(), aBox.GetSize(), aPreview); + else + rDevice.DrawBitmapEx(aBox.TopLeft(), aPreview); + } +} + +BitmapEx PageObjectPainter::CreateMarkedPreview ( + const Size& rSize, + const BitmapEx& rPreview, + const BitmapEx& rOverlay, + const OutputDevice* pReferenceDevice) +{ + ScopedVclPtr pDevice; + if (pReferenceDevice != nullptr) + pDevice.disposeAndReset(VclPtr::Create(*pReferenceDevice)); + else + pDevice.disposeAndReset(VclPtr::Create()); + pDevice->SetOutputSizePixel(rSize); + + pDevice->DrawBitmapEx(Point(0,0), rSize, rPreview); + + // Paint bitmap tiled over the preview to mark it as excluded. + const sal_Int32 nIconWidth (rOverlay.GetSizePixel().Width()); + const sal_Int32 nIconHeight (rOverlay.GetSizePixel().Height()); + if (nIconWidth>0 && nIconHeight>0) + { + for (::tools::Long nX=0; nXDrawBitmapEx(Point(nX,nY), rOverlay); + } + return pDevice->GetBitmapEx(Point(0,0), rSize); +} + +BitmapEx PageObjectPainter::GetPreviewBitmap ( + const model::SharedPageDescriptor& rpDescriptor, + const OutputDevice* pReferenceDevice) const +{ + const SdrPage* pPage = rpDescriptor->GetPage(); + const bool bIsExcluded (rpDescriptor->HasState(model::PageDescriptor::ST_Excluded)); + + if (bIsExcluded) + { + PageObjectLayouter *pPageObjectLayouter = mrLayouter.GetPageObjectLayouter().get(); + + BitmapEx aMarkedPreview (mpCache->GetMarkedPreviewBitmap(pPage)); + const ::tools::Rectangle aPreviewBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::ModelCoordinateSystem)); + if (aMarkedPreview.IsEmpty() || aMarkedPreview.GetSizePixel()!=aPreviewBox.GetSize()) + { + aMarkedPreview = CreateMarkedPreview( + aPreviewBox.GetSize(), + mpCache->GetPreviewBitmap(pPage,true), + mpTheme->GetIcon(Theme::Icon_HideSlideOverlay), + pReferenceDevice); + mpCache->SetMarkedPreviewBitmap(pPage, aMarkedPreview); + } + return aMarkedPreview; + } + else + { + return mpCache->GetPreviewBitmap(pPage,false); + } +} + +void PageObjectPainter::PaintPageNumber ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const +{ + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::PageNumber, + PageObjectLayouter::ModelCoordinateSystem)); + + // Determine the color of the page number. + Color aPageNumberColor (mpTheme->GetColor(Theme::Color_PageNumberDefault)); + if (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) || + rpDescriptor->HasState(model::PageDescriptor::ST_Selected)) + { + // Page number is painted on background for hover or selection or + // both. Each of these background colors has a predefined luminance + // which is compatible with the PageNumberHover color. + aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberHover); + } + else + { + const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background)); + const sal_Int32 nBackgroundLuminance (aBackgroundColor.GetLuminance()); + // When the background color is black then this is interpreted as + // high contrast mode and the font color is set to white. + if (nBackgroundLuminance == 0) + aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberHighContrast); + else + { + // Compare luminance of default page number color and background + // color. When the two are similar then use a darker + // (preferred) or brighter font color. + const sal_Int32 nFontLuminance (aPageNumberColor.GetLuminance()); + if (abs(nBackgroundLuminance - nFontLuminance) < 60) + { + if (nBackgroundLuminance > nFontLuminance-30) + aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberBrightBackground); + else + aPageNumberColor = mpTheme->GetColor(Theme::Color_PageNumberDarkBackground); + } + } + } + + // Paint the page number. + OSL_ASSERT(rpDescriptor->GetPage()!=nullptr); + const sal_Int32 nPageNumber ((rpDescriptor->GetPage()->GetPageNum() - 1) / 2 + 1); + const OUString sPageNumber(OUString::number(nPageNumber)); + rDevice.SetFont(*mpPageNumberFont); + rDevice.SetTextColor(aPageNumberColor); + rDevice.DrawText(aBox, sPageNumber, DrawTextFlags::Right | DrawTextFlags::VCenter); +} + +void PageObjectPainter::PaintTransitionEffect ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) +{ + const SdPage* pPage = rpDescriptor->GetPage(); + if (pPage!=nullptr && pPage->getTransitionType() > 0) + { + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::TransitionEffectIndicator, + PageObjectLayouter::ModelCoordinateSystem)); + + rDevice.DrawBitmapEx( + aBox.TopCenter(), + pPageObjectLayouter->GetTransitionEffectIcon().GetBitmapEx()); + } +} + +void PageObjectPainter::PaintCustomAnimationEffect ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) +{ + SdPage* pPage = rpDescriptor->GetPage(); + std::shared_ptr< MainSequence > aMainSequence = pPage->getMainSequence(); + EffectSequence::iterator aIter = aMainSequence->getBegin(); + EffectSequence::iterator aEnd = aMainSequence->getEnd(); + if ( aIter != aEnd ) + { + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::CustomAnimationEffectIndicator, + PageObjectLayouter::ModelCoordinateSystem)); + rDevice.DrawBitmapEx( + aBox.TopCenter(), + pPageObjectLayouter->GetCustomAnimationEffectIcon().GetBitmapEx()); + } +} + +void PageObjectPainter::PaintBackgroundDetail ( + PageObjectLayouter *pPageObjectLayouter, + OutputDevice& rDevice, + const model::SharedPageDescriptor& rpDescriptor) const +{ + enum State { None = 0x00, Selected = 0x01, MouseOver = 0x02, Focused = 0x04 }; + const int eState = + (rpDescriptor->HasState(model::PageDescriptor::ST_Selected) ? Selected : None) + | (rpDescriptor->HasState(model::PageDescriptor::ST_MouseOver) ? MouseOver : None) + | (rpDescriptor->HasState(model::PageDescriptor::ST_Focused) ? Focused : None); + + bool bHasFocusBorder; + Theme::GradientColorType eColorType; + + switch (eState) + { + case MouseOver | Selected | Focused: + eColorType = Theme::Gradient_MouseOverSelectedAndFocusedPage; + bHasFocusBorder = true; + break; + + case MouseOver | Selected: + eColorType = Theme::Gradient_MouseOverSelected; + bHasFocusBorder = false; + break; + + case MouseOver: + eColorType = Theme::Gradient_MouseOverPage; + bHasFocusBorder = false; + break; + + case MouseOver | Focused: + eColorType = Theme::Gradient_MouseOverPage; + bHasFocusBorder = true; + break; + + case Selected | Focused: + eColorType = Theme::Gradient_SelectedAndFocusedPage; + bHasFocusBorder = true; + break; + + case Selected: + eColorType = Theme::Gradient_SelectedPage; + bHasFocusBorder = false; + break; + + case Focused: + eColorType = Theme::Gradient_FocusedPage; + bHasFocusBorder = true; + break; + + case None: + default: + eColorType = Theme::Gradient_NormalPage; + bHasFocusBorder = false; + break; + } + + const ::tools::Rectangle aFocusSize (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::FocusIndicator, + PageObjectLayouter::ModelCoordinateSystem)); + + const ::tools::Rectangle aPageObjectBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::PageObject, + PageObjectLayouter::ModelCoordinateSystem)); + + // Fill the background with the background color of the slide sorter. + const Color aBackgroundColor (mpTheme->GetColor(Theme::Color_Background)); + rDevice.SetFillColor(aBackgroundColor); + rDevice.SetLineColor(aBackgroundColor); + rDevice.DrawRect(aFocusSize); + + // Paint the slide area with a linear gradient that starts some pixels + // below the top and ends some pixels above the bottom. + const Color aTopColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Fill1)); + const Color aBottomColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Fill2)); + if (aTopColor != aBottomColor) + { + Gradient gradient(GradientStyle::Linear, aTopColor, aBottomColor); + rDevice.DrawGradient(aPageObjectBox, gradient); + } + else + { + rDevice.SetFillColor(aTopColor); + rDevice.DrawRect(aPageObjectBox); + } + + // Paint the simple border and, for some backgrounds, the focus border. + if (bHasFocusBorder) + mpFocusBorderPainter->PaintFrame(rDevice, aPageObjectBox); + else + PaintBorder(rDevice, eColorType, aPageObjectBox); + + // Get bounding box of the preview around which a shadow is painted. + // Compensate for the border around the preview. + const ::tools::Rectangle aBox (pPageObjectLayouter->GetBoundingBox( + rpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::ModelCoordinateSystem)); + ::tools::Rectangle aFrameBox (aBox.Left()-1,aBox.Top()-1,aBox.Right()+1,aBox.Bottom()+1); + mpShadowPainter->PaintFrame(rDevice, aFrameBox); +} + +void PageObjectPainter::PaintBorder ( + OutputDevice& rDevice, + const Theme::GradientColorType eColorType, + const ::tools::Rectangle& rBox) const +{ + rDevice.SetFillColor(); + const sal_Int32 nBorderWidth (1); + for (int nIndex=0; nIndexGetGradientColor(eColorType, Theme::GradientColorClass::Border2)); + rDevice.DrawLine( + Point(rBox.Left()-nDelta, rBox.Top()-nDelta), + Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta)); + rDevice.DrawLine( + Point(rBox.Left()-nDelta, rBox.Bottom()+nDelta), + Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta)); + rDevice.DrawLine( + Point(rBox.Right()+nDelta, rBox.Bottom()+nDelta), + Point(rBox.Right()+nDelta, rBox.Top()-nDelta)); + + rDevice.SetLineColor(mpTheme->GetGradientColor(eColorType, Theme::GradientColorClass::Border1)); + rDevice.DrawLine( + Point(rBox.Left()-nDelta, rBox.Top()-nDelta), + Point(rBox.Right()+nDelta, rBox.Top()-nDelta)); + } +} + +} // end of namespace sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsTheme.cxx b/sd/source/ui/slidesorter/view/SlsTheme.cxx new file mode 100644 index 000000000..5172e6241 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsTheme.cxx @@ -0,0 +1,239 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace sd::slidesorter::view { + +const Color Black(0x000000); +const Color White(0xffffff); + +static Color ChangeLuminance (Color aColor, const int nValue) +{ + if (nValue > 0) + aColor.IncreaseLuminance(nValue); + else + aColor.DecreaseLuminance(-nValue); + return aColor; +} + +static Color HGBAdapt ( + const Color aColor, + const sal_Int32 nNewSaturation, + const sal_Int32 nNewBrightness) +{ + sal_uInt16 nHue (0); + sal_uInt16 nSaturation (0); + sal_uInt16 nBrightness (0); + aColor.RGBtoHSB(nHue, nSaturation, nBrightness); + return Color::HSBtoRGB( + nHue, + nNewSaturation>=0 ? nNewSaturation : nSaturation, + nNewBrightness>=0 ? nNewBrightness : nBrightness); +} + +Theme::Theme (const std::shared_ptr& rpProperties) + : maBackgroundColor(rpProperties->GetBackgroundColor()) +{ + maColor.resize(ColorType_Size_); + maColor[Color_Background] = maBackgroundColor; + maColor[Color_PageNumberDefault] = Color(0x0808080); + maColor[Color_PageNumberHover] = Color(0x4c4c4c); + maColor[Color_PageNumberHighContrast] = White; + maColor[Color_PageNumberBrightBackground] = Color(0x333333); + maColor[Color_PageNumberDarkBackground] = Color(0xcccccc); + maColor[Color_PreviewBorder] = Color(0x949599); + + Update(rpProperties); +} + +void Theme::Update (const std::shared_ptr& rpProperties) +{ + // Set up colors. + maBackgroundColor = rpProperties->GetBackgroundColor(); + + maColor[Color_Background] = maBackgroundColor; + + maGradients.resize(GradientColorType_Size_); + + maColor[Color_Background] = maBackgroundColor; + const Color aSelectionColor (rpProperties->GetSelectionColor()); + maColor[Color_Selection] = aSelectionColor; + if (aSelectionColor.IsBright()) + maColor[Color_PageCountFontColor] = Black; + else + maColor[Color_PageCountFontColor] = White; + + // Set up gradients. + SetGradient(Gradient_MouseOverPage, aSelectionColor, 0, 60, +80,+100, +50,+25); + SetGradient(Gradient_SelectedPage, aSelectionColor, 50, 50, +80,+100, +50,+25); + SetGradient(Gradient_FocusedPage, aSelectionColor, -1,-1, 0,0, -50,-75); + SetGradient(Gradient_MouseOverSelected, aSelectionColor, 55, 60, +80,+100, +50,+25); + SetGradient(Gradient_SelectedAndFocusedPage, aSelectionColor, 50, 50, +80,+100, -50,-75); + SetGradient(Gradient_MouseOverSelectedAndFocusedPage, aSelectionColor, 55, 60, +80,+100, -50,-75); + + SetGradient(Gradient_NormalPage, maBackgroundColor, -1,-1, 0,0, 0,0); + + // The focused gradient needs special handling because its fill color is + // like that of the NormalPage gradient. + GetGradient(Gradient_FocusedPage).maFillColor1 = GetGradient(Gradient_NormalPage).maFillColor1; + GetGradient(Gradient_FocusedPage).maFillColor2 = GetGradient(Gradient_NormalPage).maFillColor2; + + // Set up icons. + if (maIcons.empty()) + { + maIcons.resize(IconType_Size_); + + InitializeIcon(Icon_RawShadow, IMAGE_SHADOW); + InitializeIcon(Icon_RawInsertShadow, IMAGE_INSERT_SHADOW); + InitializeIcon(Icon_HideSlideOverlay, IMAGE_HIDE_SLIDE_OVERLAY); + InitializeIcon(Icon_FocusBorder, IMAGE_FOCUS_BORDER); + } +} + +std::shared_ptr Theme::GetFont ( + const FontType eType, + const OutputDevice& rDevice) +{ + std::shared_ptr pFont; + + switch (eType) + { + case Font_PageNumber: + pFont = std::make_shared(Application::GetSettings().GetStyleSettings().GetAppFont()); + pFont->SetTransparent(true); + pFont->SetWeight(WEIGHT_BOLD); + break; + + case Font_PageCount: + pFont = std::make_shared(Application::GetSettings().GetStyleSettings().GetAppFont()); + pFont->SetTransparent(true); + pFont->SetWeight(WEIGHT_NORMAL); + { + const Size aSize (pFont->GetFontSize()); + pFont->SetFontSize(Size(aSize.Width()*5/3, aSize.Height()*5/3)); + } + break; + } + + if (pFont) + { + // Transform the point size to pixel size. + const MapMode aFontMapMode (MapUnit::MapPoint); + const Size aFontSize (rDevice.LogicToPixel(pFont->GetFontSize(), aFontMapMode)); + + // Transform the font size to the logical coordinates of the device. + pFont->SetFontSize(rDevice.PixelToLogic(aFontSize)); + } + + return pFont; +} + +Color Theme::GetColor (const ColorType eType) +{ + if (sal_uInt32(eType)=0 || nBrightnessOverride>=0 + ? HGBAdapt(aBaseColor, nSaturationOverride, nBrightnessOverride) + : aBaseColor); + + rGradient.maFillColor1 = ChangeLuminance(aColor, nFillStartOffset); + rGradient.maFillColor2 = ChangeLuminance(aColor, nFillEndOffset); + rGradient.maBorderColor1 = ChangeLuminance(aColor, nBorderStartOffset); + rGradient.maBorderColor2 = ChangeLuminance(aColor, nBorderEndOffset); +} + +const BitmapEx& Theme::GetIcon (const IconType eType) +{ + if (size_t(eType)=0 && size_t(eType)=0 && size_t(eType)=0 && size_t(eType) +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sd::slidesorter::view { + +ToolTip::ToolTip (SlideSorter& rSlideSorter) + : mrSlideSorter(rSlideSorter), + mnHelpWindowHandle(nullptr), + maShowTimer("sd::slidesorter::view::ToolTip maShowTimer"), + maHiddenTimer("sd::slidesorter::view::ToolTip maHiddenTimer") +{ + maShowTimer.SetTimeout(HelpSettings::GetTipDelay()); + maShowTimer.SetInvokeHandler(LINK(this, ToolTip, DelayTrigger)); + maHiddenTimer.SetTimeout(HelpSettings::GetTipDelay()); +} + +ToolTip::~ToolTip() +{ + maShowTimer.Stop(); + maHiddenTimer.Stop(); + Hide(); +} + +void ToolTip::SetPage (const model::SharedPageDescriptor& rpDescriptor) +{ + if (mpDescriptor == rpDescriptor) + return; + + maShowTimer.Stop(); + bool bWasVisible = Hide(); + + if (bWasVisible) + { + maHiddenTimer.Start(); + } + + mpDescriptor = rpDescriptor; + + if (mpDescriptor) + { + SdPage* pPage = mpDescriptor->GetPage(); + OUString sHelpText; + if (pPage != nullptr) + sHelpText = pPage->GetName(); + else + { + OSL_ASSERT(mpDescriptor->GetPage() != nullptr); + } + if (sHelpText.isEmpty()) + { + sHelpText = SdResId(STR_PAGE) + + OUString::number(mpDescriptor->GetPageIndex()+1); + } + + msCurrentHelpText = sHelpText; + // show new tooltip immediately, if last one was recently hidden + if(maHiddenTimer.IsActive()) + DoShow(); + else + maShowTimer.Start(); + } + else + { + msCurrentHelpText.clear(); + } +} + +void ToolTip::DoShow() +{ + if (maShowTimer.IsActive()) + { + // The delay timer is active. Wait for it to trigger the showing of + // the tool tip. + return; + } + + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + if (msCurrentHelpText.isEmpty() || !pWindow) + return; + + ::tools::Rectangle aBox ( + mrSlideSorter.GetView().GetLayouter().GetPageObjectLayouter()->GetBoundingBox( + mpDescriptor, + PageObjectLayouter::Part::Preview, + PageObjectLayouter::WindowCoordinateSystem)); + + // Do not show the help text when the (lower edge of the ) preview + // is not visible. The tool tip itself may still be outside the + // window. + if (aBox.Bottom() >= pWindow->GetSizePixel().Height()) + return; + + vcl::Window* pParent (pWindow); + while (pParent!=nullptr && pParent->GetParent()!=nullptr) + pParent = pParent->GetParent(); + const Point aOffset (pWindow->GetWindowExtentsRelative(pParent).TopLeft()); + + // We do not know how high the tool tip will be but want its top + // edge not its bottom to be at a specific position (a little below + // the preview). Therefore we use a little trick and place the tool + // tip at the top of a rectangle that is placed below the preview. + aBox.Move(aOffset.X(), aOffset.Y() + aBox.GetHeight() + 3); + mnHelpWindowHandle = Help::ShowPopover( + pWindow, + aBox, + msCurrentHelpText, + QuickHelpFlags::Center | QuickHelpFlags::Top); +} + +bool ToolTip::Hide() +{ + if (mnHelpWindowHandle) + { + sd::Window *pWindow (mrSlideSorter.GetContentWindow().get()); + Help::HidePopover(pWindow, mnHelpWindowHandle); + mnHelpWindowHandle = nullptr; + return true; + } + else + return false; +} + +IMPL_LINK_NOARG(ToolTip, DelayTrigger, Timer *, void) +{ + DoShow(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsViewCacheContext.cxx b/sd/source/ui/slidesorter/view/SlsViewCacheContext.cxx new file mode 100644 index 000000000..ce27cec28 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsViewCacheContext.cxx @@ -0,0 +1,117 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "SlsViewCacheContext.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd::slidesorter::view { + +ViewCacheContext::ViewCacheContext (SlideSorter& rSlideSorter) + : mrModel(rSlideSorter.GetModel()), + mrSlideSorter(rSlideSorter) +{ +} + +ViewCacheContext::~ViewCacheContext() +{ +} + +void ViewCacheContext::NotifyPreviewCreation(cache::CacheKey aKey) +{ + const model::SharedPageDescriptor pDescriptor (GetDescriptor(aKey)); + if (pDescriptor) + { + // Force a repaint that will trigger their re-creation. + mrSlideSorter.GetView().RequestRepaint(pDescriptor); + } + else + { + // It is OK when a preview was created for a page that is not + // currently displayed because both normal and master pages are + // kept in the same cache. + } +} + +bool ViewCacheContext::IsIdle() +{ + tools::IdleState nIdleState (tools::IdleDetection::GetIdleState(mrSlideSorter.GetContentWindow())); + return nIdleState == tools::IdleState::Idle; +} + +bool ViewCacheContext::IsVisible (cache::CacheKey aKey) +{ + const model::SharedPageDescriptor pDescriptor (GetDescriptor(aKey)); + return pDescriptor && pDescriptor->HasState(model::PageDescriptor::ST_Visible); +} + +const SdrPage* ViewCacheContext::GetPage (cache::CacheKey aKey) +{ + return aKey; +} + +std::shared_ptr > ViewCacheContext::GetEntryList (bool bVisible) +{ + auto pKeys = std::make_shared>(); + + model::PageEnumeration aPageEnumeration ( + bVisible + ? model::PageEnumerationProvider::CreateVisiblePagesEnumeration(mrModel) + : model::PageEnumerationProvider::CreateAllPagesEnumeration(mrModel)); + + while (aPageEnumeration.HasMoreElements()) + { + model::SharedPageDescriptor pDescriptor (aPageEnumeration.GetNextElement()); + pKeys->push_back(pDescriptor->GetPage()); + } + + return pKeys; +} + +sal_Int32 ViewCacheContext::GetPriority (cache::CacheKey aKey) +{ + return - (aKey->GetPageNum()-1) / 2; +} + +model::SharedPageDescriptor ViewCacheContext::GetDescriptor (cache::CacheKey aKey) +{ + sal_uInt16 nPageIndex ((aKey->GetPageNum() - 1) / 2); + return mrModel.GetPageDescriptor(nPageIndex); +} + +css::uno::Reference ViewCacheContext::GetModel() +{ + if (mrModel.GetDocument() == nullptr) + return nullptr; + else + return mrModel.GetDocument()->getUnoModel(); +} + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/slidesorter/view/SlsViewCacheContext.hxx b/sd/source/ui/slidesorter/view/SlsViewCacheContext.hxx new file mode 100644 index 000000000..501517cb8 --- /dev/null +++ b/sd/source/ui/slidesorter/view/SlsViewCacheContext.hxx @@ -0,0 +1,61 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd::slidesorter::model +{ +class SlideSorterModel; +} +namespace sd::slidesorter +{ +class SlideSorter; +} + +namespace sd::slidesorter::view +{ +/** The cache context for the SlideSorter as used by Draw and Impress. See + the base class for documentation of the individual methods. +*/ +class ViewCacheContext : public cache::CacheContext +{ +public: + explicit ViewCacheContext(SlideSorter& rSlideSorter); + virtual ~ViewCacheContext() override; + virtual void NotifyPreviewCreation(cache::CacheKey aKey) override; + virtual bool IsIdle() override; + virtual bool IsVisible(cache::CacheKey aKey) override; + virtual const SdrPage* GetPage(cache::CacheKey aKey) override; + virtual std::shared_ptr> GetEntryList(bool bVisible) override; + virtual sal_Int32 GetPriority(cache::CacheKey aKey) override; + virtual css::uno::Reference GetModel() override; + +private: + model::SlideSorterModel& mrModel; + SlideSorter& mrSlideSorter; + + model::SharedPageDescriptor GetDescriptor(cache::CacheKey aKey); +}; + +} // end of namespace ::sd::slidesorter::view + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/table/TableDesignPane.cxx b/sd/source/ui/table/TableDesignPane.cxx new file mode 100644 index 000000000..82d4485fb --- /dev/null +++ b/sd/source/ui/table/TableDesignPane.cxx @@ -0,0 +1,763 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::view; +using namespace ::com::sun::star::style; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::ui; + +namespace sd { + +const sal_Int32 nPreviewColumns = 5; +const sal_Int32 nPreviewRows = 5; +const sal_Int32 nCellWidth = 12; // one pixel is shared with the next cell! +const sal_Int32 nCellHeight = 7; // one pixel is shared with the next cell! +const sal_Int32 nBitmapWidth = (nCellWidth * nPreviewColumns) - (nPreviewColumns - 1); +const sal_Int32 nBitmapHeight = (nCellHeight * nPreviewRows) - (nPreviewRows - 1); + +const std::string_view gPropNames[CB_COUNT] = +{ + "UseFirstRowStyle", + "UseLastRowStyle", + "UseBandingRowStyle", + "UseFirstColumnStyle", + "UseLastColumnStyle", + "UseBandingColumnStyle" +}; + +TableDesignWidget::TableDesignWidget(weld::Builder& rBuilder, ViewShellBase& rBase) + : mrBase(rBase) + , m_xValueSet(new TableValueSet(rBuilder.weld_scrolled_window("previewswin", true))) + , m_xValueSetWin(new weld::CustomWeld(rBuilder, "previews", *m_xValueSet)) +{ + m_xValueSet->SetStyle(m_xValueSet->GetStyle() | WB_NO_DIRECTSELECT | WB_FLATVALUESET | WB_ITEMBORDER); + m_xValueSet->SetExtraSpacing(8); + m_xValueSet->setModal(false); + m_xValueSet->SetColor(); + m_xValueSet->SetSelectHdl(LINK(this, TableDesignWidget, implValueSetHdl)); + + for (sal_uInt16 i = CB_HEADER_ROW; i <= CB_BANDED_COLUMNS; ++i) + { + m_aCheckBoxes[i] = rBuilder.weld_check_button(OString(gPropNames[i].data(), gPropNames[i].size())); + m_aCheckBoxes[i]->connect_toggled(LINK(this, TableDesignWidget, implCheckBoxHdl)); + } + + // get current controller and initialize listeners + try + { + mxView.set(mrBase.GetController(), UNO_QUERY); + addListener(); + + Reference< XController > xController( mrBase.GetController(), UNO_SET_THROW ); + Reference< XStyleFamiliesSupplier > xFamiliesSupp( xController->getModel(), UNO_QUERY_THROW ); + Reference< XNameAccess > xFamilies( xFamiliesSupp->getStyleFamilies() ); + mxTableFamily.set( xFamilies->getByName( "table" ), UNO_QUERY_THROW ); + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::CustomAnimationPane::CustomAnimationPane()" ); + } + + onSelectionChanged(); + updateControls(); +} + +TableDesignWidget::~TableDesignWidget() +{ + removeListener(); +} + +static SfxBindings* getBindings( ViewShellBase const & rBase ) +{ + if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() ) + return &rBase.GetMainViewShell()->GetViewFrame()->GetBindings(); + else + return nullptr; +} + +static SfxDispatcher* getDispatcher( ViewShellBase const & rBase ) +{ + if( rBase.GetMainViewShell() && rBase.GetMainViewShell()->GetViewFrame() ) + return rBase.GetMainViewShell()->GetViewFrame()->GetDispatcher(); + else + return nullptr; +} + +IMPL_LINK_NOARG(TableDesignWidget, implValueSetHdl, ValueSet*, void) +{ + ApplyStyle(); +} + +void TableDesignWidget::ApplyStyle() +{ + try + { + OUString sStyleName; + sal_Int32 nIndex = static_cast< sal_Int32 >( m_xValueSet->GetSelectedItemId() ) - 1; + + if( (nIndex >= 0) && (nIndex < mxTableFamily->getCount()) ) + { + Reference< XNameAccess > xNames( mxTableFamily, UNO_QUERY_THROW ); + sStyleName = xNames->getElementNames()[nIndex]; + } + + if( sStyleName.isEmpty() ) + return; + + SdrView* pView = mrBase.GetDrawView(); + if( mxSelectedTable.is() ) + { + if( pView ) + { + SfxRequest aReq( SID_TABLE_STYLE, SfxCallMode::SYNCHRON, SfxGetpApp()->GetPool() ); + aReq.AppendItem( SfxStringItem( SID_TABLE_STYLE, sStyleName ) ); + + const rtl::Reference< sdr::SelectionController >& xController( pView->getSelectionController() ); + if( xController.is() ) + xController->Execute( aReq ); + + SfxBindings* pBindings = getBindings( mrBase ); + if( pBindings ) + { + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); + } + } + } + else + { + SfxDispatcher* pDispatcher = getDispatcher( mrBase ); + SfxStringItem aArg( SID_TABLE_STYLE, sStyleName ); + pDispatcher->ExecuteList(SID_INSERT_TABLE, SfxCallMode::ASYNCHRON, + { &aArg }); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "TableDesignWidget::implValueSetHdl()"); + } +} + +IMPL_LINK_NOARG(TableDesignWidget, implCheckBoxHdl, weld::Toggleable&, void) +{ + ApplyOptions(); + FillDesignPreviewControl(); +} + +void TableDesignWidget::ApplyOptions() +{ + static const sal_uInt16 gParamIds[CB_COUNT] = + { + ID_VAL_USEFIRSTROWSTYLE, ID_VAL_USELASTROWSTYLE, ID_VAL_USEBANDINGROWSTYLE, + ID_VAL_USEFIRSTCOLUMNSTYLE, ID_VAL_USELASTCOLUMNSTYLE, ID_VAL_USEBANDINGCOLUMNSTYLE + }; + + if( !mxSelectedTable.is() ) + return; + + SfxRequest aReq( SID_TABLE_STYLE_SETTINGS, SfxCallMode::SYNCHRON, SfxGetpApp()->GetPool() ); + + for( sal_uInt16 i = CB_HEADER_ROW; i <= CB_BANDED_COLUMNS; ++i ) + { + aReq.AppendItem( SfxBoolItem( gParamIds[i], m_aCheckBoxes[i]->get_active() ) ); + } + + SdrView* pView = mrBase.GetDrawView(); + if( !pView ) + return; + + const rtl::Reference< sdr::SelectionController >& xController( pView->getSelectionController() ); + if( xController.is() ) + { + xController->Execute( aReq ); + + SfxBindings* pBindings = getBindings( mrBase ); + if( pBindings ) + { + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); + } + } +} + +void TableDesignWidget::onSelectionChanged() +{ + Reference< XPropertySet > xNewSelection; + + if( mxView.is() ) try + { + Reference< XSelectionSupplier > xSel( mxView, UNO_QUERY_THROW ); + Any aSel( xSel->getSelection() ); + Sequence< XShape > xShapeSeq; + if( aSel >>= xShapeSeq ) + { + if( xShapeSeq.getLength() == 1 ) + aSel <<= xShapeSeq[0]; + } + else + { + Reference< XShapes > xShapes( aSel, UNO_QUERY ); + if( xShapes.is() && (xShapes->getCount() == 1) ) + aSel = xShapes->getByIndex(0); + } + + Reference< XShapeDescriptor > xDesc( aSel, UNO_QUERY ); + if( xDesc.is() && ( xDesc->getShapeType() == "com.sun.star.drawing.TableShape" || xDesc->getShapeType() == "com.sun.star.presentation.TableShape" ) ) + { + xNewSelection.set( xDesc, UNO_QUERY ); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TableDesignWidget::onSelectionChanged()" ); + } + + if( mxSelectedTable != xNewSelection ) + { + mxSelectedTable = xNewSelection; + updateControls(); + } +} + +void TableValueSet::Resize() +{ + ValueSet::Resize(); + // Calculate the number of rows and columns. + if( GetItemCount() <= 0 ) + return; + + Size aValueSetSize = GetOutputSizePixel(); + + Image aImage = GetItemImage(GetItemId(0)); + Size aItemSize = aImage.GetSizePixel(); + + aItemSize.AdjustHeight(10 ); + int nColumnCount = (aValueSetSize.Width() - GetScrollWidth()) / aItemSize.Width(); + if (nColumnCount < 1) + nColumnCount = 1; + + int nRowCount = (GetItemCount() + nColumnCount - 1) / nColumnCount; + if (nRowCount < 1) + nRowCount = 1; + + int nVisibleRowCount = (aValueSetSize.Height()+2) / aItemSize.Height(); + + SetColCount (static_cast(nColumnCount)); + SetLineCount (static_cast(nRowCount)); + + if( !m_bModal ) + { + WinBits nStyle = GetStyle() & ~WB_VSCROLL; + if( nRowCount > nVisibleRowCount ) + { + nStyle |= WB_VSCROLL; + } + SetStyle( nStyle ); + } +} + +TableValueSet::TableValueSet(std::unique_ptr pScrolledWindow) + : ValueSet(std::move(pScrolledWindow)) + , m_bModal(false) +{ +} + +void TableValueSet::StyleUpdated() +{ + updateSettings(); +} + +void TableValueSet::updateSettings() +{ + if( !m_bModal ) + { + Color aColor = Application::GetSettings().GetStyleSettings().GetWindowColor(); + SetColor(aColor); + SetExtraSpacing(8); + } +} + +void TableDesignWidget::updateControls() +{ + static const bool gDefaults[CB_COUNT] = { true, false, true, false, false, false }; + + const bool bHasTable = mxSelectedTable.is(); + + for (sal_uInt16 i = CB_HEADER_ROW; i <= CB_BANDED_COLUMNS; ++i) + { + bool bUse = gDefaults[i]; + if( bHasTable ) try + { + mxSelectedTable->getPropertyValue( OUString::createFromAscii(gPropNames[i]) ) >>= bUse; + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TableDesignWidget::updateControls()"); + } + m_aCheckBoxes[i]->set_active(bUse); + m_aCheckBoxes[i]->set_sensitive(bHasTable); + } + + FillDesignPreviewControl(); + m_xValueSet->updateSettings(); + m_xValueSet->Resize(); + + sal_uInt16 nSelection = 0; + if( mxSelectedTable.is() ) + { + Reference< XNamed > xNamed( mxSelectedTable->getPropertyValue( "TableTemplate" ), UNO_QUERY ); + if( xNamed.is() ) + { + const OUString sStyleName( xNamed->getName() ); + + Reference< XNameAccess > xNames( mxTableFamily, UNO_QUERY ); + if( xNames.is() ) + { + Sequence< OUString > aNames( xNames->getElementNames() ); + sal_Int32 nIndex = comphelper::findValue(aNames, sStyleName); + if (nIndex != -1) + nSelection = static_cast(nIndex) + 1; + } + } + } + m_xValueSet->SelectItem( nSelection ); +} + +void TableDesignWidget::addListener() +{ + Link aLink( LINK(this,TableDesignWidget,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener( aLink ); +} + +void TableDesignWidget::removeListener() +{ + Link aLink( LINK(this,TableDesignWidget,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); +} + +IMPL_LINK(TableDesignWidget,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + case EventMultiplexerEventId::EditViewSelection: + onSelectionChanged(); + break; + + case EventMultiplexerEventId::MainViewRemoved: + mxView.clear(); + onSelectionChanged(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mxView.set( mrBase.GetController(), UNO_QUERY ); + onSelectionChanged(); + break; + + default: break; + } +} + +namespace { + +struct CellInfo +{ + Color maCellColor; + Color maTextColor; + std::shared_ptr maBorder; + + explicit CellInfo( const Reference< XStyle >& xStyle ); +}; + +} + +CellInfo::CellInfo( const Reference< XStyle >& xStyle ) +: maBorder(std::make_shared(SDRATTR_TABLE_BORDER)) +{ + SfxStyleSheet* pStyleSheet = SfxUnoStyleSheet::getUnoStyleSheet( xStyle ); + if( !pStyleSheet ) + return; + + SfxItemSet& rSet = pStyleSheet->GetItemSet(); + + // get style fill color + if( !GetDraftFillColor(rSet, maCellColor) ) + maCellColor = COL_TRANSPARENT; + + // get style text color + const SvxColorItem* pTextColor = rSet.GetItem(EE_CHAR_COLOR); + if( pTextColor ) + maTextColor = pTextColor->GetValue(); + else + maTextColor = COL_TRANSPARENT; + + // get border + const SvxBoxItem* pBoxItem = rSet.GetItem( SDRATTR_TABLE_BORDER ); + if( pBoxItem ) + maBorder.reset(pBoxItem->Clone()); +} + +typedef std::vector< std::shared_ptr< CellInfo > > CellInfoVector; +typedef std::shared_ptr< CellInfo > CellInfoMatrix[nPreviewColumns * nPreviewRows]; + +namespace { + +struct TableStyleSettings +{ + bool mbUseFirstRow; + bool mbUseLastRow; + bool mbUseFirstColumn; + bool mbUseLastColumn; + bool mbUseRowBanding; + bool mbUseColumnBanding; + + TableStyleSettings() + : mbUseFirstRow(true) + , mbUseLastRow(false) + , mbUseFirstColumn(false) + , mbUseLastColumn(false) + , mbUseRowBanding(true) + , mbUseColumnBanding(false) {} +}; + +} + +static void FillCellInfoVector( const Reference< XIndexAccess >& xTableStyle, CellInfoVector& rVector ) +{ + DBG_ASSERT( xTableStyle.is() && (xTableStyle->getCount() == sdr::table::style_count ), "sd::FillCellInfoVector(), invalid table style!" ); + if( !xTableStyle.is() ) + return; + + try + { + rVector.resize( sdr::table::style_count ); + + for( sal_Int32 nStyle = 0; nStyle < sdr::table::style_count; ++nStyle ) + { + Reference< XStyle > xStyle( xTableStyle->getByIndex( nStyle ), UNO_QUERY ); + if( xStyle.is() ) + rVector[nStyle] = std::make_shared( xStyle ); + } + } + catch(Exception&) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::FillCellInfoVector()"); + } +} + +static void FillCellInfoMatrix( const CellInfoVector& rStyle, const TableStyleSettings& rSettings, CellInfoMatrix& rMatrix ) +{ + for( sal_Int32 nRow = 0; nRow < nPreviewColumns; ++nRow ) + { + const bool bFirstRow = rSettings.mbUseFirstRow && (nRow == 0); + const bool bLastRow = rSettings.mbUseLastRow && (nRow == nPreviewColumns - 1); + + for( sal_Int32 nCol = 0; nCol < nPreviewColumns; ++nCol ) + { + std::shared_ptr< CellInfo > xCellInfo; + + // first and last row win first, if used and available + if( bFirstRow ) + { + xCellInfo = rStyle[sdr::table::first_row_style]; + } + else if( bLastRow ) + { + xCellInfo = rStyle[sdr::table::last_row_style]; + } + + if( !xCellInfo ) + { + // next come first and last column, if used and available + if( rSettings.mbUseFirstColumn && (nCol == 0) ) + { + xCellInfo = rStyle[sdr::table::first_column_style]; + } + else if( rSettings.mbUseLastColumn && (nCol == nPreviewColumns-1) ) + { + xCellInfo = rStyle[sdr::table::last_column_style]; + } + } + + if( !xCellInfo ) + { + if( rSettings.mbUseRowBanding ) + { + if( (nRow & 1) == 0 ) + { + xCellInfo = rStyle[sdr::table::even_rows_style]; + } + else + { + xCellInfo = rStyle[sdr::table::odd_rows_style]; + } + } + } + + if( !xCellInfo ) + { + if( rSettings.mbUseColumnBanding ) + { + if( (nCol & 1) == 0 ) + { + xCellInfo = rStyle[sdr::table::even_columns_style]; + } + else + { + xCellInfo = rStyle[sdr::table::odd_columns_style]; + } + } + } + + if( !xCellInfo ) + { + // use default cell style if non found yet + xCellInfo = rStyle[sdr::table::body_style]; + } + + rMatrix[(nCol * nPreviewColumns) + nRow] = xCellInfo; + } + } +} + +static BitmapEx CreateDesignPreview( const Reference< XIndexAccess >& xTableStyle, const TableStyleSettings& rSettings, bool bIsPageDark ) +{ + CellInfoVector aCellInfoVector(sdr::table::style_count); + FillCellInfoVector( xTableStyle, aCellInfoVector ); + + CellInfoMatrix aMatrix; + FillCellInfoMatrix( aCellInfoVector, rSettings, aMatrix ); + + // bbbbbbbbbbbb w = 12 pixel + // bccccccccccb h = 7 pixel + // bccccccccccb b = border color + // bcttttttttcb c = cell color + // bccccccccccb t = text color + // bccccccccccb + // bbbbbbbbbbbb + + ScopedVclPtr pVirDev(VclPtr::Create()); + Size aBmpSize(nBitmapWidth, nBitmapHeight); + pVirDev->SetOutputSizePixel(aBmpSize); + + pVirDev->SetBackground( bIsPageDark ? COL_BLACK : COL_WHITE ); + pVirDev->Erase(); + + // first draw cell background and text line previews + sal_Int32 nY = 0; + sal_Int32 nRow; + for( nRow = 0; nRow < nPreviewRows; ++nRow, nY += nCellHeight-1 ) + { + sal_Int32 nX = 0; + for( sal_Int32 nCol = 0; nCol < nPreviewColumns; ++nCol, nX += nCellWidth-1 ) + { + std::shared_ptr< CellInfo > xCellInfo(aMatrix[(nCol * nPreviewColumns) + nRow]); + + Color aTextColor( COL_AUTO ); + if( xCellInfo ) + { + // fill cell background + const ::tools::Rectangle aRect( nX, nY, nX + nCellWidth - 1, nY + nCellHeight - 1 ); + + if( xCellInfo->maCellColor != COL_TRANSPARENT ) + { + pVirDev->SetFillColor( xCellInfo->maCellColor ); + pVirDev->DrawRect( aRect ); + } + + aTextColor = xCellInfo->maTextColor; + } + + // draw text preview line + if( aTextColor == COL_AUTO ) + aTextColor = bIsPageDark ? COL_WHITE : COL_BLACK; + pVirDev->SetLineColor( aTextColor ); + const Point aPnt1( nX + 2, nY + ((nCellHeight - 1 ) >> 1) ); + const Point aPnt2( nX + nCellWidth - 3, aPnt1.Y() ); + pVirDev->DrawLine( aPnt1, aPnt2 ); + } + } + + // second draw border lines + nY = 0; + for( nRow = 0; nRow < nPreviewRows; ++nRow, nY += nCellHeight-1 ) + { + sal_Int32 nX = 0; + for( sal_Int32 nCol = 0; nCol < nPreviewColumns; ++nCol, nX += nCellWidth-1 ) + { + std::shared_ptr< CellInfo > xCellInfo(aMatrix[(nCol * nPreviewColumns) + nRow]); + + if( xCellInfo ) + { + const Point aPntTL( nX, nY ); + const Point aPntTR( nX + nCellWidth - 1, nY ); + const Point aPntBL( nX, nY + nCellHeight - 1 ); + const Point aPntBR( nX + nCellWidth - 1, nY + nCellHeight - 1 ); + + sal_Int32 border_diffs[8] = { 0,-1, 0,1, -1,0, 1,0 }; + sal_Int32* pDiff = &border_diffs[0]; + + // draw top border + for( SvxBoxItemLine nLine : o3tl::enumrange() ) + { + const ::editeng::SvxBorderLine* pBorderLine = xCellInfo->maBorder->GetLine(nLine); + if( !pBorderLine || ((pBorderLine->GetOutWidth() == 0) && (pBorderLine->GetInWidth()==0)) ) + continue; + + sal_Int32 nBorderCol = nCol + *pDiff++; + sal_Int32 nBorderRow = nRow + *pDiff++; + if( (nBorderCol >= 0) && (nBorderCol < nPreviewColumns) && (nBorderRow >= 0) && (nBorderRow < nPreviewRows) ) + { + // check border + std::shared_ptr< CellInfo > xBorderInfo(aMatrix[(nBorderCol * nPreviewColumns) + nBorderRow]); + if( xBorderInfo ) + { + const ::editeng::SvxBorderLine* pBorderLine2 = xBorderInfo->maBorder->GetLine(static_cast(static_cast(nLine)^1)); + if( pBorderLine2 && pBorderLine2->HasPriority(*pBorderLine) ) + continue; // other border line wins + } + } + + pVirDev->SetLineColor( pBorderLine->GetColor() ); + switch( nLine ) + { + case SvxBoxItemLine::TOP: pVirDev->DrawLine( aPntTL, aPntTR ); break; + case SvxBoxItemLine::BOTTOM: pVirDev->DrawLine( aPntBL, aPntBR ); break; + case SvxBoxItemLine::LEFT: pVirDev->DrawLine( aPntTL, aPntBL ); break; + case SvxBoxItemLine::RIGHT: pVirDev->DrawLine( aPntTR, aPntBR ); break; + } + } + } + } + } + + return pVirDev->GetBitmapEx(Point(0,0), aBmpSize); +} + +void TableDesignWidget::FillDesignPreviewControl() +{ + sal_uInt16 nSelectedItem = m_xValueSet->GetSelectedItemId(); + m_xValueSet->Clear(); + try + { + TableStyleSettings aSettings; + if( mxSelectedTable.is() ) + { + aSettings.mbUseFirstRow = m_aCheckBoxes[CB_HEADER_ROW]->get_active(); + aSettings.mbUseLastRow = m_aCheckBoxes[CB_TOTAL_ROW]->get_active(); + aSettings.mbUseRowBanding = m_aCheckBoxes[CB_BANDED_ROWS]->get_active(); + aSettings.mbUseFirstColumn = m_aCheckBoxes[CB_FIRST_COLUMN]->get_active(); + aSettings.mbUseLastColumn = m_aCheckBoxes[CB_LAST_COLUMN]->get_active(); + aSettings.mbUseColumnBanding = m_aCheckBoxes[CB_BANDED_COLUMNS]->get_active(); + } + + bool bIsPageDark = false; + if( mxView.is() ) + { + Reference< XPropertySet > xPageSet( mxView->getCurrentPage(), UNO_QUERY ); + if( xPageSet.is() ) + { + xPageSet->getPropertyValue("IsBackgroundDark") >>= bIsPageDark; + } + } + + sal_Int32 nCount = mxTableFamily->getCount(); + for( sal_Int32 nIndex = 0; nIndex < nCount; ++nIndex ) try + { + Reference< XIndexAccess > xTableStyle( mxTableFamily->getByIndex( nIndex ), UNO_QUERY ); + if( xTableStyle.is() ) + m_xValueSet->InsertItem( sal::static_int_cast( nIndex + 1 ), Image( CreateDesignPreview( xTableStyle, aSettings, bIsPageDark ) ) ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TableDesignWidget::FillDesignPreviewControl()"); + } + sal_Int32 nCols = 3; + sal_Int32 nRows = (nCount+2)/3; + m_xValueSet->SetColCount(nCols); + m_xValueSet->SetLineCount(nRows); + WinBits nStyle = m_xValueSet->GetStyle() & ~WB_VSCROLL; + m_xValueSet->SetStyle(nStyle); + + m_xValueSet->SetOptimalSize(); + weld::DrawingArea* pDrawingArea = m_xValueSet->GetDrawingArea(); + Size aSize = pDrawingArea->get_preferred_size(); + aSize.AdjustWidth(10 * nCols); + aSize.AdjustHeight(10 * nRows); + pDrawingArea->set_size_request(aSize.Width(), aSize.Height()); + + m_xValueSet->Resize(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::TableDesignWidget::FillDesignPreviewControl()"); + } + m_xValueSet->SelectItem(nSelectedItem); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/table/tablefunction.cxx b/sd/source/ui/table/tablefunction.cxx new file mode 100644 index 000000000..c19445429 --- /dev/null +++ b/sd/source/ui/table/tablefunction.cxx @@ -0,0 +1,292 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::sd; +using namespace sdr::table; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::linguistic2; + +namespace sd +{ + +static void apply_table_style( SdrTableObj* pObj, SdrModel const * pModel, const OUString& sTableStyle ) +{ + if( !(pModel && pObj) ) + return; + + Reference< XNameAccess > xPool( dynamic_cast< XNameAccess* >( pModel->GetStyleSheetPool() ) ); + if( !xPool.is() ) + return; + + try + { + Reference< XNameContainer > xTableFamily( xPool->getByName( "table" ), UNO_QUERY_THROW ); + OUString aStdName( "default" ); + if( !sTableStyle.isEmpty() ) + aStdName = sTableStyle; + Reference< XIndexAccess > xStyle( xTableFamily->getByName( aStdName ), UNO_QUERY_THROW ); + pObj->setTableStyle( xStyle ); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::apply_default_table_style()"); + } +} + +static void InsertTableImpl(const DrawViewShell* pShell, + ::sd::View* pView, + sal_Int32 nColumns, + sal_Int32 nRows, + const OUString& sTableStyle) +{ + ::tools::Rectangle aRect; + + SdrObject* pPickObj = pView->GetEmptyPresentationObject( PresObjKind::Table ); + if( pPickObj ) + { + aRect = pPickObj->GetLogicRect(); + aRect.setHeight( 200 ); + } + else + { + Size aSize( 14100, 2000 ); + + Point aPos; + ::tools::Rectangle aWinRect(aPos, pShell->GetActiveWindow()->GetOutputSizePixel()); + aWinRect = pShell->GetActiveWindow()->PixelToLogic(aWinRect); + + // make sure that the default size of the table fits on the paper and is inside the viewing area. + // if zoomed in close, don't make the table bigger than the viewing window. + Size aMaxSize = pShell->getCurrentPage()->GetSize(); + + if (comphelper::LibreOfficeKit::isActive()) + { + // aWinRect is nonsensical in the LOK case + aWinRect = ::tools::Rectangle(aPos, aMaxSize); + } + else + { + if( aMaxSize.Height() > aWinRect.getHeight() ) + aMaxSize.setHeight( aWinRect.getHeight() ); + if( aMaxSize.Width() > aWinRect.getWidth() ) + aMaxSize.setWidth( aWinRect.getWidth() ); + } + + if( aSize.Width() > aMaxSize.getWidth() ) + aSize.setWidth( aMaxSize.getWidth() ); + + // adjust height based on # of rows. + if( nRows > 0 ) + { + aSize.setHeight( aSize.Height() * nRows ); + if( aSize.Height() > aMaxSize.getHeight() ) + aSize.setHeight( aMaxSize.getHeight() ); + } + + aPos = aWinRect.Center(); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + aRect = ::tools::Rectangle(aPos, aSize); + } + + sdr::table::SdrTableObj* pObj = new sdr::table::SdrTableObj( + *pShell->GetDoc(), // TTTT should be reference + aRect, + nColumns, + nRows); + pObj->NbcSetStyleSheet( pShell->GetDoc()->GetDefaultStyleSheet(), true ); + apply_table_style( pObj, pShell->GetDoc(), sTableStyle ); + SdrPageView* pPV = pView->GetSdrPageView(); + + // #i123359# if an object is to be replaced/manipulated it may be that it is in text edit mode, + // so to be on the safe side call SdrEndTextEdit here + SdrTextObj* pCheckForTextEdit = dynamic_cast< SdrTextObj* >(pPickObj); + + if(pCheckForTextEdit && pCheckForTextEdit->IsInEditMode()) + { + pView->SdrEndTextEdit(); + } + + // if we have a pick obj we need to make this new ole a pres obj replacing the current pick obj + if( pPickObj ) + { + SdPage* pPage = static_cast< SdPage* >(pPickObj->getSdrPageFromSdrObject()); + if(pPage && pPage->IsPresObj(pPickObj)) + { + pObj->SetUserCall( pPickObj->GetUserCall() ); + pPage->InsertPresObj( pObj, PresObjKind::Table ); + } + } + + pShell->GetParentWindow()->GrabFocus(); + if( pPickObj ) + pView->ReplaceObjectAtView(pPickObj, *pPV, pObj ); + else + pView->InsertObjectAtView(pObj, *pPV, SdrInsertFlags::SETDEFLAYER); +} + +void DrawViewShell::FuTable(SfxRequest& rReq) +{ + switch( rReq.GetSlot() ) + { + case SID_INSERT_TABLE: + { + sal_Int32 nColumns = 0; + sal_Int32 nRows = 0; + OUString sTableStyle; + DrawViewShell* pShell = this; + ::sd::View* pView = mpView; + + const SfxUInt16Item* pCols = rReq.GetArg(SID_ATTR_TABLE_COLUMN); + const SfxUInt16Item* pRows = rReq.GetArg(SID_ATTR_TABLE_ROW); + const SfxStringItem* pStyle = rReq.GetArg(SID_TABLE_STYLE); + + if( pCols ) + nColumns = pCols->GetValue(); + + if( pRows ) + nRows = pRows->GetValue(); + + if( pStyle ) + sTableStyle = pStyle->GetValue(); + + if( (nColumns == 0) || (nRows == 0) ) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + std::shared_ptr pDlg( pFact->CreateSvxNewTableDialog(rReq.GetFrameWeld()) ); + + weld::DialogController::runAsync(pDlg->getDialogController(), + [pDlg, pShell, pView, sTableStyle] (sal_Int32 nResult) { + if (nResult == RET_OK) + { + sal_Int32 nColumnsIn = pDlg->getColumns(); + sal_Int32 nRowsIn = pDlg->getRows(); + + InsertTableImpl(pShell, pView, nColumnsIn, nRowsIn, sTableStyle); + } + }); + } + else + { + InsertTableImpl(pShell, pView, nColumns, nRows, sTableStyle); + } + + rReq.Ignore(); + SfxViewShell* pViewShell = GetViewShell(); + OSL_ASSERT (pViewShell!=nullptr); + SfxBindings& rBindings = pViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_INSERT_TABLE, true ); + break; + } + case SID_TABLEDESIGN: + { + // First make sure that the sidebar is visible + GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + ::sfx2::sidebar::Sidebar::ShowPanel( + u"SdTableDesignPanel", + GetViewFrame()->GetFrame().GetFrameInterface()); + + Cancel(); + rReq.Done (); + break; + } + default: + break; + } +} + +void DrawViewShell::GetTableMenuState( SfxItemSet &rSet ) +{ + OUString aActiveLayer = mpDrawView->GetActiveLayer(); + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + + if( + ( !aActiveLayer.isEmpty() && pPV && ( pPV->IsLayerLocked(aActiveLayer) || + !pPV->IsLayerVisible(aActiveLayer) ) ) || + SD_MOD()->GetWaterCan() ) + { + rSet.DisableItem( SID_INSERT_TABLE ); + } +} + +void CreateTableFromRTF( SvStream& rStream, SdDrawDocument* pModel ) +{ + rStream.Seek( 0 ); + + if( !pModel ) + return; + + SdrPage* pPage = pModel->GetPage(0); + if( !pPage ) + return; + + Size aSize( 200, 200 ); + ::tools::Rectangle aRect (Point(), aSize); + sdr::table::SdrTableObj* pObj = new sdr::table::SdrTableObj( + *pModel, + aRect, + 1, + 1); + pObj->NbcSetStyleSheet( pModel->GetDefaultStyleSheet(), true ); + apply_table_style( pObj, pModel, OUString() ); + + pPage->NbcInsertObject( pObj ); + + sdr::table::ImportAsRTF( rStream, *pObj ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/table/tableobjectbar.cxx b/sd/source/ui/table/tableobjectbar.cxx new file mode 100644 index 000000000..62d81d980 --- /dev/null +++ b/sd/source/ui/table/tableobjectbar.cxx @@ -0,0 +1,224 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "tableobjectbar.hxx" + +using namespace sd; +using namespace sd::ui::table; + +#define ShellClass_TableObjectBar +#include + +namespace sd::ui::table { + +/** creates a table object bar for the given ViewShell */ +SfxShell* CreateTableObjectBar( ViewShell& rShell, ::sd::View* pView ) +{ + return new TableObjectBar( &rShell, pView ); +} + +/** registers the interfaces from the table ui */ +void RegisterInterfaces(const SfxModule* pMod) +{ + TableObjectBar::RegisterInterface(pMod); +} + + +SFX_IMPL_INTERFACE(TableObjectBar, SfxShell) + +void TableObjectBar::InitInterface_Impl() +{ +} + +TableObjectBar::TableObjectBar( ViewShell* pSdViewShell, ::sd::View* pSdView ) +: SfxShell( pSdViewShell->GetViewShell() ) +, mpView( pSdView ) +, mpViewSh( pSdViewShell ) +{ + DrawDocShell* pDocShell = mpViewSh->GetDocSh(); + if( pDocShell ) + { + SetPool( &pDocShell->GetPool() ); + SetUndoManager( pDocShell->GetUndoManager() ); + } + SetRepeatTarget( mpView ); + SetName( SdResId( RID_DRAW_TABLE_TOOLBOX ) ); + SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::Table)); +} + +TableObjectBar::~TableObjectBar() +{ + SetRepeatTarget( nullptr ); +} + +void TableObjectBar::GetState( SfxItemSet& rSet ) +{ + if( mpView ) + { + rtl::Reference< sdr::SelectionController > xController( mpView->getSelectionController() ); + if( xController.is() ) + { + xController->GetState( rSet ); + } + } +} + +void TableObjectBar::GetAttrState( SfxItemSet& rSet ) +{ + DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >( mpViewSh ); + if( pDrawViewShell ) + pDrawViewShell->GetAttrState( rSet ); +} + +void TableObjectBar::Execute( SfxRequest& rReq ) +{ + if( !mpView ) + return; + + SdrView* pView = mpView; + SfxBindings* pBindings = &mpViewSh->GetViewFrame()->GetBindings(); + + rtl::Reference< sdr::SelectionController > xController( mpView->getSelectionController() ); + sal_uLong nSlotId = rReq.GetSlot(); + if( xController.is() ) + { + switch( nSlotId ) + { + case SID_TABLE_INSERT_ROW_DLG: + case SID_TABLE_INSERT_ROW_BEFORE: + case SID_TABLE_INSERT_ROW_AFTER: + case SID_TABLE_INSERT_COL_DLG: + case SID_TABLE_INSERT_COL_BEFORE: + case SID_TABLE_INSERT_COL_AFTER: + { + ScopedVclPtr pDlg; + if (nSlotId == SID_TABLE_INSERT_ROW_DLG || nSlotId == SID_TABLE_INSERT_COL_DLG) + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + vcl::Window* pWin = mpView->GetViewShell()->GetParentWindow(); + pDlg.disposeAndReset( pFact->CreateSvxInsRowColDlg(pWin ? pWin->GetFrameWeld() : nullptr, + nSlotId == SID_TABLE_INSERT_COL_DLG, + SD_MOD()->GetSlotPool()->GetSlot(nSlotId)->GetCommand()) ); + + if (pDlg->Execute() != 1) + break; + } + + sal_uInt16 nCount = 1; + bool bInsertAfter = (nSlotId == SID_TABLE_INSERT_ROW_AFTER) || (nSlotId == SID_TABLE_INSERT_COL_AFTER); + + if (nSlotId == SID_TABLE_INSERT_ROW_DLG) + { + nCount = pDlg->getInsertCount(); + bInsertAfter = !pDlg->isInsertBefore(); + } + else if (nSlotId == SID_TABLE_INSERT_COL_DLG) + { + nCount = pDlg->getInsertCount(); + bInsertAfter = !pDlg->isInsertBefore(); + } + + if (nSlotId == SID_TABLE_INSERT_ROW_DLG || nSlotId == SID_TABLE_INSERT_ROW_BEFORE || nSlotId == SID_TABLE_INSERT_ROW_AFTER) + nSlotId = SID_TABLE_INSERT_ROW; + else + nSlotId = SID_TABLE_INSERT_COL; + + rReq.AppendItem(SfxInt16Item(static_cast(nSlotId), nCount)); + rReq.AppendItem(SfxBoolItem(SID_TABLE_PARAM_INSERT_AFTER, bInsertAfter)); + + rReq.SetSlot( static_cast(nSlotId) ); + } + } + + xController->Execute( rReq ); + } + + // note: we may be deleted at this point, no more member access possible + + switch( rReq.GetSlot() ) + { + case SID_ATTR_BORDER: + case SID_TABLE_MERGE_CELLS: + case SID_TABLE_SPLIT_CELLS: + case SID_OPTIMIZE_TABLE: + case SID_TABLE_DELETE_ROW: + case SID_TABLE_DELETE_COL: + case SID_TABLE_DELETE_TABLE: + case SID_FORMAT_TABLE_DLG: + case SID_TABLE_INSERT_ROW: + case SID_TABLE_INSERT_COL: + { + pView->AdjustMarkHdl(); + pBindings->Invalidate( SID_TABLE_DELETE_ROW ); + pBindings->Invalidate( SID_TABLE_DELETE_COL ); + pBindings->Invalidate( SID_TABLE_DELETE_TABLE ); + pBindings->Invalidate( SID_FRAME_LINESTYLE ); + pBindings->Invalidate( SID_FRAME_LINECOLOR ); + pBindings->Invalidate( SID_ATTR_BORDER ); + pBindings->Invalidate( SID_ATTR_FILL_STYLE ); + pBindings->Invalidate( SID_ATTR_FILL_USE_SLIDE_BACKGROUND ); + pBindings->Invalidate( SID_ATTR_FILL_TRANSPARENCE ); + pBindings->Invalidate( SID_ATTR_FILL_FLOATTRANSPARENCE ); + pBindings->Invalidate( SID_TABLE_MERGE_CELLS ); + pBindings->Invalidate( SID_TABLE_SPLIT_CELLS ); + pBindings->Invalidate( SID_OPTIMIZE_TABLE ); + pBindings->Invalidate( SID_TABLE_VERT_BOTTOM ); + pBindings->Invalidate( SID_TABLE_VERT_CENTER ); + pBindings->Invalidate( SID_TABLE_VERT_NONE ); + break; + } + case SID_TABLE_VERT_BOTTOM: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_NONE: + { + pBindings->Invalidate( SID_TABLE_VERT_BOTTOM ); + pBindings->Invalidate( SID_TABLE_VERT_CENTER ); + pBindings->Invalidate( SID_TABLE_VERT_NONE ); + break; + } + } + + pBindings->Invalidate( SID_UNDO ); + pBindings->Invalidate( SID_REDO ); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/table/tableobjectbar.hxx b/sd/source/ui/table/tableobjectbar.hxx new file mode 100644 index 000000000..c5d513bff --- /dev/null +++ b/sd/source/ui/table/tableobjectbar.hxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +namespace sd { + +class View; +class ViewShell; + +} + +namespace sd::ui::table { + +class TableObjectBar final : public SfxShell +{ +public: + SFX_DECL_INTERFACE( SD_IF_SDDRAWTABLEOBJECTBAR ) + + TableObjectBar( ::sd::ViewShell* pSdViewShell, ::sd::View* pSdView); + virtual ~TableObjectBar() override; + + void GetState( SfxItemSet& rSet ); + void GetAttrState( SfxItemSet& rSet ); + void Execute( SfxRequest& rReq ); + +private: + /// SfxInterface initializer. + static void InitInterface_Impl(); + + ::sd::View* mpView; + ::sd::ViewShell* mpViewSh; +}; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/AsynchronousCall.cxx b/sd/source/ui/tools/AsynchronousCall.cxx new file mode 100644 index 000000000..b7b70f63a --- /dev/null +++ b/sd/source/ui/tools/AsynchronousCall.cxx @@ -0,0 +1,56 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +namespace sd::tools +{ +AsynchronousCall::AsynchronousCall() + : maTimer("sd AsynchronousCall") +{ + maTimer.SetInvokeHandler(LINK(this, AsynchronousCall, TimerCallback)); +} + +AsynchronousCall::~AsynchronousCall() +{ + mpFunction.reset(); + maTimer.Stop(); +} + +void AsynchronousCall::Post(const AsynchronousFunction& rFunction) +{ + mpFunction.reset(new AsynchronousFunction(rFunction)); + maTimer.SetTimeout(10); + maTimer.Start(); +} + +IMPL_LINK(AsynchronousCall, TimerCallback, Timer*, pTimer, void) +{ + if (pTimer == &maTimer) + { + ::std::unique_ptr pFunction; + pFunction.swap(mpFunction); + (*pFunction)(); + } +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/ConfigurationAccess.cxx b/sd/source/ui/tools/ConfigurationAccess.cxx new file mode 100644 index 000000000..d359aeb18 --- /dev/null +++ b/sd/source/ui/tools/ConfigurationAccess.cxx @@ -0,0 +1,173 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::tools { + +ConfigurationAccess::ConfigurationAccess ( + const Reference& rxContext, + const OUString& rsRootName, + const WriteMode eMode) +{ + Reference xProvider = + configuration::theDefaultProvider::get( rxContext ); + Initialize(xProvider, rsRootName, eMode); +} + +ConfigurationAccess::ConfigurationAccess ( + const OUString& rsRootName, + const WriteMode eMode) +{ + Reference xProvider = + configuration::theDefaultProvider::get( ::comphelper::getProcessComponentContext() ); + Initialize(xProvider, rsRootName, eMode); +} + +void ConfigurationAccess::Initialize ( + const Reference& rxProvider, + const OUString& rsRootName, + const WriteMode eMode) +{ + try + { + Sequence aCreationArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", Any(rsRootName)}, + {"depth", Any(sal_Int32(-1))} + })); + + OUString sAccessService; + if (eMode == READ_ONLY) + sAccessService = "com.sun.star.configuration.ConfigurationAccess"; + else + sAccessService = "com.sun.star.configuration.ConfigurationUpdateAccess"; + + mxRoot = rxProvider->createInstanceWithArguments( + sAccessService, + aCreationArguments); + } + catch (Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.tools"); + } +} + +Any ConfigurationAccess::GetConfigurationNode ( + const OUString& sPathToNode) +{ + return GetConfigurationNode( + Reference(mxRoot, UNO_QUERY), + sPathToNode); +} + +Any ConfigurationAccess::GetConfigurationNode ( + const css::uno::Reference& rxNode, + const OUString& sPathToNode) +{ + if (sPathToNode.isEmpty()) + return Any(rxNode); + + try + { + if (rxNode.is()) + { + return rxNode->getByHierarchicalName(sPathToNode); + } + } + catch (const Exception&) + { + TOOLS_WARN_EXCEPTION("sd", "caught exception while getting configuration node" << sPathToNode); + } + + return Any(); +} + +void ConfigurationAccess::CommitChanges() +{ + Reference xConfiguration (mxRoot, UNO_QUERY); + if (xConfiguration.is()) + xConfiguration->commitChanges(); +} + +void ConfigurationAccess::ForAll ( + const Reference& rxContainer, + const ::std::vector& rArguments, + const Functor& rFunctor) +{ + if (!rxContainer.is()) + return; + + ::std::vector aValues(rArguments.size()); + const Sequence aKeys (rxContainer->getElementNames()); + for (const OUString& rsKey : aKeys) + { + Reference xSetItem (rxContainer->getByName(rsKey), UNO_QUERY); + if (xSetItem.is()) + { + // Get from the current item of the container the children + // that match the names in the rArguments list. + for (size_t nValueIndex=0; nValueIndexgetByName(rArguments[nValueIndex]); + } + rFunctor(rsKey, aValues); + } +} + +void ConfigurationAccess::FillList( + const Reference& rxContainer, + const OUString& rsArgument, + ::std::vector& rList) +{ + try + { + if (rxContainer.is()) + { + Sequence aKeys (rxContainer->getElementNames()); + rList.resize(aKeys.getLength()); + for (sal_Int32 nItemIndex=0; nItemIndex xSetItem ( + rxContainer->getByName(aKeys[nItemIndex]), UNO_QUERY); + if (xSetItem.is()) + { + xSetItem->getByName(rsArgument) >>= rList[nItemIndex]; + } + } + } + } + catch (RuntimeException&) + {} +} + +} // end of namespace sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/EventMultiplexer.cxx b/sd/source/ui/tools/EventMultiplexer.cxx new file mode 100644 index 000000000..a61413ac6 --- /dev/null +++ b/sd/source/ui/tools/EventMultiplexer.cxx @@ -0,0 +1,661 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +using ::sd::framework::FrameworkHelper; + +class SdDrawDocument; + +namespace { +const sal_Int32 ResourceActivationEvent = 0; +const sal_Int32 ResourceDeactivationEvent = 1; +const sal_Int32 ConfigurationUpdateEvent = 2; +} + +namespace sd::tools { + +typedef comphelper::WeakComponentImplHelper< + css::beans::XPropertyChangeListener, + css::frame::XFrameActionListener, + css::view::XSelectionChangeListener, + css::drawing::framework::XConfigurationChangeListener + > EventMultiplexerImplementationInterfaceBase; + +class EventMultiplexer::Implementation + : public EventMultiplexerImplementationInterfaceBase, + public SfxListener +{ +public: + explicit Implementation (ViewShellBase& rBase); + virtual ~Implementation() override; + + void AddEventListener ( + const Link& rCallback); + + void RemoveEventListener ( + const Link& rCallback); + + void CallListeners (EventMultiplexerEvent& rEvent); + + //===== lang::XEventListener ============================================== + virtual void SAL_CALL + disposing (const css::lang::EventObject& rEventObject) override; + + //===== beans::XPropertySetListener ======================================= + virtual void SAL_CALL + propertyChange ( + const css::beans::PropertyChangeEvent& rEvent) override; + + //===== view::XSelectionChangeListener ==================================== + virtual void SAL_CALL + selectionChanged ( + const css::lang::EventObject& rEvent) override; + + //===== frame::XFrameActionListener ====================================== + /** For certain actions the listener connects to a new controller of the + frame it is listening to. This usually happens when the view shell + in the center pane is replaced by another view shell. + */ + virtual void SAL_CALL + frameAction (const css::frame::FrameActionEvent& rEvent) override; + + //===== drawing::framework::XConfigurationChangeListener ================== + virtual void SAL_CALL + notifyConfigurationChange ( + const css::drawing::framework::ConfigurationChangeEvent& rEvent) override; + + virtual void disposing(std::unique_lock&) override; + +protected: + virtual void Notify ( + SfxBroadcaster& rBroadcaster, + const SfxHint& rHint) override; + +private: + ViewShellBase& mrBase; + typedef ::std::vector> ListenerList; + ListenerList maListeners; + + /// Remember whether we are listening to the UNO controller. + bool mbListeningToController; + /// Remember whether we are listening to the frame. + bool mbListeningToFrame; + + css::uno::WeakReference mxControllerWeak; + css::uno::WeakReference mxFrameWeak; + SdDrawDocument* mpDocument; + css::uno::WeakReference + mxConfigurationControllerWeak; + + void ReleaseListeners(); + + void ConnectToController(); + void DisconnectFromController(); + + void CallListeners ( + EventMultiplexerEventId eId, + void const * pUserData = nullptr); + + DECL_LINK(SlideSorterSelectionChangeListener, LinkParamNone*, void); +}; + +constexpr OUStringLiteral aCurrentPagePropertyName = u"CurrentPage"; +constexpr OUStringLiteral aEditModePropertyName = u"IsMasterPageMode"; + +//===== EventMultiplexer ====================================================== + +EventMultiplexer::EventMultiplexer (ViewShellBase& rBase) + : mpImpl (new EventMultiplexer::Implementation(rBase)) +{ +} + +EventMultiplexer::~EventMultiplexer() +{ + try + { + mpImpl->dispose(); + } + catch (const RuntimeException&) + { + } + catch (const Exception&) + { + } +} + +void EventMultiplexer::AddEventListener ( + const Link& rCallback) +{ + mpImpl->AddEventListener(rCallback); +} + +void EventMultiplexer::RemoveEventListener ( + const Link& rCallback) +{ + mpImpl->RemoveEventListener(rCallback); +} + +void EventMultiplexer::MultiplexEvent( + EventMultiplexerEventId eEventId, + void const * pUserData ) +{ + EventMultiplexerEvent aEvent(eEventId, pUserData); + mpImpl->CallListeners(aEvent); +} + +//===== EventMultiplexer::Implementation ====================================== + +EventMultiplexer::Implementation::Implementation (ViewShellBase& rBase) + : mrBase (rBase), + mbListeningToController (false), + mbListeningToFrame (false), + mxControllerWeak(nullptr), + mxFrameWeak(nullptr), + mpDocument(nullptr) +{ + // Connect to the frame to listen for controllers being exchanged. + // Listen to changes of certain properties. + Reference xFrame = + mrBase.GetFrame()->GetFrame().GetFrameInterface(); + mxFrameWeak = xFrame; + if (xFrame.is()) + { + xFrame->addFrameActionListener ( Reference(this) ); + mbListeningToFrame = true; + } + + // Connect to the current controller. + ConnectToController (); + + // Listen for document changes. + mpDocument = mrBase.GetDocument(); + if (mpDocument != nullptr) + StartListening (*mpDocument); + + // Listen for configuration changes. + Reference xControllerManager ( + Reference(&mrBase.GetDrawController()), UNO_QUERY); + if (!xControllerManager.is()) + return; + + Reference xConfigurationController ( + xControllerManager->getConfigurationController()); + mxConfigurationControllerWeak = xConfigurationController; + if (!xConfigurationController.is()) + return; + + Reference xComponent (xConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->addEventListener(static_cast(this)); + + xConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any(ResourceActivationEvent)); + xConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceDeactivationEvent, + Any(ResourceDeactivationEvent)); + xConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msConfigurationUpdateEndEvent, + Any(ConfigurationUpdateEvent)); +} + +EventMultiplexer::Implementation::~Implementation() +{ + DBG_ASSERT( !mbListeningToFrame, + "sd::EventMultiplexer::Implementation::~Implementation(), disposing was not called!" ); +} + +void EventMultiplexer::Implementation::ReleaseListeners() +{ + if (mbListeningToFrame) + { + mbListeningToFrame = false; + + // Stop listening for changes of certain properties. + Reference xFrame (mxFrameWeak); + if (xFrame.is()) + { + xFrame->removeFrameActionListener ( + Reference(this) ); + } + } + + DisconnectFromController (); + + if (mpDocument != nullptr) + { + EndListening (*mpDocument); + mpDocument = nullptr; + } + + // Stop listening for configuration changes. + Reference xConfigurationController (mxConfigurationControllerWeak); + if (xConfigurationController.is()) + { + Reference xComponent (xConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->removeEventListener(static_cast(this)); + + xConfigurationController->removeConfigurationChangeListener(this); + } +} + +void EventMultiplexer::Implementation::AddEventListener ( + const Link& rCallback) +{ + for (auto const & i : maListeners) + if (i == rCallback) + return; + maListeners.push_back(rCallback); +} + +void EventMultiplexer::Implementation::RemoveEventListener ( + const Link& rCallback) +{ + auto iListener = std::find(maListeners.begin(), maListeners.end(), rCallback); + if (iListener != maListeners.end()) + maListeners.erase(iListener); +} + +void EventMultiplexer::Implementation::ConnectToController() +{ + // Just in case that we missed some event we now disconnect from the old + // controller. + DisconnectFromController (); + + // Register at the controller of the main view shell. + + // We have to store a (weak) reference to the controller so that we can + // unregister without having to ask the mrBase member (which at that + // time may be destroyed.) + Reference xController = mrBase.GetController(); + mxControllerWeak = mrBase.GetController(); + + try + { + // Listen for disposing events. + if (xController.is()) + { + xController->addEventListener ( + Reference( + static_cast(this), UNO_QUERY)); + mbListeningToController = true; + } + + // Listen to changes of certain properties. + Reference xSet (xController, UNO_QUERY); + if (xSet.is()) + { + try + { + xSet->addPropertyChangeListener(aCurrentPagePropertyName, this); + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd", "EventMultiplexer::ConnectToController: CurrentPage unknown"); + } + + try + { + xSet->addPropertyChangeListener(aEditModePropertyName, this); + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd", "EventMultiplexer::ConnectToController: IsMasterPageMode unknown"); + } + } + + // Listen for selection change events. + Reference xSelection (xController, UNO_QUERY); + if (xSelection.is()) + { + xSelection->addSelectionChangeListener(this); + } + } + catch (const lang::DisposedException&) + { + mbListeningToController = false; + } +} + +void EventMultiplexer::Implementation::DisconnectFromController() +{ + if (!mbListeningToController) + return; + + mbListeningToController = false; + + Reference xController = mxControllerWeak; + + Reference xSet (xController, UNO_QUERY); + // Remove the property listener. + if (xSet.is()) + { + try + { + xSet->removePropertyChangeListener(aCurrentPagePropertyName, this); + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd", "DisconnectFromController: CurrentPage unknown"); + } + + try + { + xSet->removePropertyChangeListener(aEditModePropertyName, this); + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd", "DisconnectFromController: IsMasterPageMode unknown"); + } + } + + // Remove selection change listener. + Reference xSelection (xController, UNO_QUERY); + if (xSelection.is()) + { + xSelection->removeSelectionChangeListener(this); + } + + // Remove listener for disposing events. + if (xController.is()) + { + xController->removeEventListener ( + Reference(static_cast(this), UNO_QUERY)); + } +} + +//===== lang::XEventListener ================================================ + +void SAL_CALL EventMultiplexer::Implementation::disposing ( + const lang::EventObject& rEventObject) +{ + if (mbListeningToController) + { + Reference xController (mxControllerWeak); + if (rEventObject.Source == xController) + { + mbListeningToController = false; + } + } + + Reference xConfigurationController ( + mxConfigurationControllerWeak); + if (xConfigurationController.is() + && rEventObject.Source == xConfigurationController) + { + mxConfigurationControllerWeak.clear(); + } +} + +//===== beans::XPropertySetListener ========================================= + +void SAL_CALL EventMultiplexer::Implementation::propertyChange ( + const beans::PropertyChangeEvent& rEvent) +{ + if (m_bDisposed) + { + throw lang::DisposedException ( + "SlideSorterController object has already been disposed", + static_cast(this)); + } + + if ( rEvent.PropertyName == aCurrentPagePropertyName ) + { + CallListeners(EventMultiplexerEventId::CurrentPageChanged); + } + else if ( rEvent.PropertyName == aEditModePropertyName ) + { + bool bIsMasterPageMode (false); + rEvent.NewValue >>= bIsMasterPageMode; + if (bIsMasterPageMode) + CallListeners(EventMultiplexerEventId::EditModeMaster); + else + CallListeners(EventMultiplexerEventId::EditModeNormal); + } +} + +//===== frame::XFrameActionListener ========================================== + +void SAL_CALL EventMultiplexer::Implementation::frameAction ( + const frame::FrameActionEvent& rEvent) +{ + Reference xFrame (mxFrameWeak); + if (rEvent.Frame != xFrame) + return; + + switch (rEvent.Action) + { + case frame::FrameAction_COMPONENT_DETACHING: + DisconnectFromController(); + CallListeners (EventMultiplexerEventId::ControllerDetached); + break; + + case frame::FrameAction_COMPONENT_REATTACHED: + CallListeners (EventMultiplexerEventId::ControllerDetached); + DisconnectFromController(); + ConnectToController(); + CallListeners (EventMultiplexerEventId::ControllerAttached); + break; + + case frame::FrameAction_COMPONENT_ATTACHED: + ConnectToController(); + CallListeners (EventMultiplexerEventId::ControllerAttached); + break; + + default: + break; + } +} + +//===== view::XSelectionChangeListener ======================================== + +void SAL_CALL EventMultiplexer::Implementation::selectionChanged ( + const lang::EventObject& ) +{ + CallListeners (EventMultiplexerEventId::EditViewSelection); +} + +//===== drawing::framework::XConfigurationChangeListener ================== + +void SAL_CALL EventMultiplexer::Implementation::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + sal_Int32 nEventType = 0; + rEvent.UserData >>= nEventType; + switch (nEventType) + { + case ResourceActivationEvent: + if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) + { + CallListeners (EventMultiplexerEventId::ViewAdded); + + if (rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + CallListeners (EventMultiplexerEventId::MainViewAdded); + } + + // Add selection change listener at slide sorter. + if (rEvent.ResourceId->getResourceURL() == FrameworkHelper::msSlideSorterURL) + { + slidesorter::SlideSorterViewShell* pViewShell + = dynamic_cast( + FrameworkHelper::GetViewShell( + Reference(rEvent.ResourceObject,UNO_QUERY)).get()); + if (pViewShell != nullptr) + pViewShell->AddSelectionChangeListener ( + LINK(this, + EventMultiplexer::Implementation, + SlideSorterSelectionChangeListener)); + } + } + break; + + case ResourceDeactivationEvent: + if (rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix)) + { + if (rEvent.ResourceId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + CallListeners (EventMultiplexerEventId::MainViewRemoved); + } + + // Remove selection change listener from slide sorter. Add + // selection change listener at slide sorter. + if (rEvent.ResourceId->getResourceURL() == FrameworkHelper::msSlideSorterURL) + { + slidesorter::SlideSorterViewShell* pViewShell + = dynamic_cast( + FrameworkHelper::GetViewShell( + Reference(rEvent.ResourceObject, UNO_QUERY)).get()); + if (pViewShell != nullptr) + pViewShell->RemoveSelectionChangeListener ( + LINK(this, + EventMultiplexer::Implementation, + SlideSorterSelectionChangeListener)); + } + } + break; + + case ConfigurationUpdateEvent: + CallListeners (EventMultiplexerEventId::ConfigurationUpdated); + break; + } + +} + +void EventMultiplexer::Implementation::disposing(std::unique_lock& rGuard) +{ + ListenerList aCopyListeners( maListeners ); + + rGuard.unlock(); + + EventMultiplexerEvent rEvent(EventMultiplexerEventId::Disposing, nullptr); + for (const auto& rListener : aCopyListeners) + rListener.Call(rEvent); + + rGuard.lock(); + + ReleaseListeners(); +} + +void EventMultiplexer::Implementation::Notify ( + SfxBroadcaster&, + const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast(&rHint); + switch (pSdrHint->GetKind()) + { + case SdrHintKind::ModelCleared: + case SdrHintKind::PageOrderChange: + CallListeners (EventMultiplexerEventId::PageOrder); + break; + + case SdrHintKind::SwitchToPage: + CallListeners (EventMultiplexerEventId::CurrentPageChanged); + break; + + case SdrHintKind::ObjectChange: + CallListeners(EventMultiplexerEventId::ShapeChanged, + static_cast(pSdrHint->GetPage())); + break; + + case SdrHintKind::ObjectInserted: + CallListeners(EventMultiplexerEventId::ShapeInserted, + static_cast(pSdrHint->GetPage())); + break; + + case SdrHintKind::ObjectRemoved: + CallListeners(EventMultiplexerEventId::ShapeRemoved, + static_cast(pSdrHint->GetPage())); + break; + default: + break; + } + } + else + { + if (rHint.GetId() == SfxHintId::Dying) + mpDocument = nullptr; + } +} + +void EventMultiplexer::Implementation::CallListeners ( + EventMultiplexerEventId eId, + void const * pUserData) +{ + EventMultiplexerEvent aEvent(eId, pUserData); + CallListeners(aEvent); +} + +void EventMultiplexer::Implementation::CallListeners (EventMultiplexerEvent& rEvent) +{ + ListenerList aCopyListeners( maListeners ); + for (const auto& rListener : aCopyListeners) + { + rListener.Call(rEvent); + } +} + +IMPL_LINK_NOARG(EventMultiplexer::Implementation, SlideSorterSelectionChangeListener, LinkParamNone*, void) +{ + CallListeners(EventMultiplexerEventId::SlideSortedSelection); +} + +//===== EventMultiplexerEvent ================================================= + +EventMultiplexerEvent::EventMultiplexerEvent ( + EventMultiplexerEventId eEventId, + const void* pUserData) + : meEventId(eEventId), + mpUserData(pUserData) +{ +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/GraphicSizeCheck.cxx b/sd/source/ui/tools/GraphicSizeCheck.cxx new file mode 100644 index 000000000..492a2d70b --- /dev/null +++ b/sd/source/ui/tools/GraphicSizeCheck.cxx @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sd +{ +namespace +{ +/** + * Interface for the visitor class, which handles each visited SdrObject + * in the DOM. + */ +class ModelTraverseHandler +{ +public: + virtual ~ModelTraverseHandler() {} + + virtual void handleSdrObject(SdrObject* pObject) = 0; +}; + +/** + * Traverses the DOM and calls a handler for each object (SdrObject) it + * encounters. + */ +class ModelTraverser +{ +private: + std::vector> m_pNodeHandler; + SdDrawDocument* m_pDocument; + +public: + ModelTraverser(SdDrawDocument* pDocument) + : m_pDocument(pDocument) + { + } + + void traverse() + { + if (!m_pDocument) + return; + + for (sal_uInt16 nPage = 0; nPage < m_pDocument->GetPageCount(); ++nPage) + { + SdrPage* pPage = m_pDocument->GetPage(nPage); + if (pPage) + { + for (size_t nObject = 0; nObject < pPage->GetObjCount(); ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject) + { + for (auto& pNodeHandler : m_pNodeHandler) + { + pNodeHandler->handleSdrObject(pObject); + } + } + } + } + } + } + + void addNodeHandler(std::shared_ptr pHandler) + { + m_pNodeHandler.push_back(pHandler); + } +}; +} + +GraphicSizeViolation::GraphicSizeViolation(sal_Int32 nDPI, SdrGrafObj* pGraphicObject) + : m_pGraphicObject(pGraphicObject) +{ + constexpr double fLowPercentage = 110; + constexpr double fHighPercentage = 50; + + m_nLowDPILimit = sal_Int32(100.0 / fLowPercentage * nDPI); + m_nHighDPILimit = sal_Int32(100.0 / fHighPercentage * nDPI); +} + +bool GraphicSizeViolation::check() +{ + Graphic aGraphic = m_pGraphicObject->GetGraphic(); + Size aSizePixel = aGraphic.GetSizePixel(); + Size aGraphicSize = m_pGraphicObject->GetLogicRect().GetSize(); + + double nSizeXInch + = o3tl::convert(double(aGraphicSize.Width()), o3tl::Length::mm100, o3tl::Length::in); + double nSizeYInch + = o3tl::convert(double(aGraphicSize.Height()), o3tl::Length::mm100, o3tl::Length::in); + + m_nDPIX = sal_Int32(aSizePixel.Width() / nSizeXInch); + m_nDPIY = sal_Int32(aSizePixel.Height() / nSizeYInch); + + return isDPITooLow() || isDPITooHigh(); +} + +const OUString& GraphicSizeViolation::getGraphicName() { return m_pGraphicObject->GetName(); } + +namespace +{ +class GraphicSizeCheckHandler : public ModelTraverseHandler +{ + sal_Int32 m_nDPI; + std::vector>& m_rGraphicSizeViolationList; + +public: + GraphicSizeCheckHandler( + sal_Int32 nDPI, + std::vector>& rGraphicSizeViolationList) + : m_nDPI(nDPI) + , m_rGraphicSizeViolationList(rGraphicSizeViolationList) + { + } + + void handleSdrObject(SdrObject* pObject) override + { + auto* pGraphicObject = dynamic_cast(pObject); + if (!pGraphicObject) + return; + + auto pEntry = std::make_unique(m_nDPI, pGraphicObject); + if (pEntry->check()) + { + m_rGraphicSizeViolationList.push_back(std::move(pEntry)); + } + } +}; + +} // end anonymous namespace + +void GraphicSizeCheck::check() +{ + if (!m_pDocument) + return; + + sal_Int32 nDPI = m_pDocument->getImagePreferredDPI(); + if (nDPI == 0) + return; + + auto pHandler = std::make_shared(nDPI, m_aGraphicSizeViolationList); + + ModelTraverser aModelTraverser(m_pDocument); + aModelTraverser.addNodeHandler(pHandler); + aModelTraverser.traverse(); +} + +OUString GraphicSizeCheckGUIEntry::getText() +{ + OUString sText; + + if (m_pViolation->isDPITooLow()) + { + sText = SdResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_LOW); + } + else if (m_pViolation->isDPITooHigh()) + { + sText = SdResId(STR_WARNING_GRAPHIC_PIXEL_COUNT_HIGH); + } + + sText = sText.replaceAll("%NAME%", m_pViolation->getGraphicName()); + sText = sText.replaceAll("%DPIX%", OUString::number(m_pViolation->getDPIX())); + sText = sText.replaceAll("%DPIY%", OUString::number(m_pViolation->getDPIY())); + + return sText; +} + +void GraphicSizeCheckGUIEntry::markObject() +{ + sd::ViewShell* pViewShell = m_pDocument->GetDocSh()->GetViewShell(); + SdrView* pView = pViewShell->GetView(); + pView->ShowSdrPage(m_pViolation->getObject()->getSdrPageFromSdrObject()); + pView->UnmarkAll(); + pView->MarkObj(m_pViolation->getObject(), pView->GetSdrPageView()); +} + +void GraphicSizeCheckGUIEntry::runProperties() +{ + markObject(); + sd::ViewShell* pViewShell = m_pDocument->GetDocSh()->GetViewShell(); + pViewShell->GetDispatcher()->Execute(SID_ATTR_GRAF_CROP, SfxCallMode::SYNCHRON); +} + +GraphicSizeCheckGUIResult::GraphicSizeCheckGUIResult(SdDrawDocument* pDocument) +{ + GraphicSizeCheck aCheck(pDocument); + aCheck.check(); + + auto& rCollection = getCollection(); + for (auto& rpViolation : aCheck.getViolationList()) + { + auto rGUIEntry + = std::make_unique(pDocument, std::move(rpViolation)); + rCollection.push_back(std::move(rGUIEntry)); + } +} + +OUString GraphicSizeCheckGUIResult::getTitle() +{ + return SdResId(STR_GRAPHIC_SIZE_CHECK_DIALOG_TITLE); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/IconCache.cxx b/sd/source/ui/tools/IconCache.cxx new file mode 100644 index 000000000..0ce80922b --- /dev/null +++ b/sd/source/ui/tools/IconCache.cxx @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include + +namespace sd +{ +//===== IconCache::Implementation ============================================= + +class IconCache::Implementation +{ +private: + friend class IconCache; + + /** This pointer holds a valid reference from first time that + IconCache::Instance() is called to the end of the sd module when the + cache is destroyed from SdGlobalResourceContainer. + */ + static IconCache* s_pIconCache; + + typedef std::unordered_map ImageContainer; + ImageContainer maContainer; + + Image GetIcon(const OUString& rResourceId); +}; + +IconCache* IconCache::Implementation::s_pIconCache = nullptr; + +Image IconCache::Implementation::GetIcon(const OUString& rResourceId) +{ + Image aResult; + ImageContainer::iterator iImage = maContainer.find(rResourceId); + if (iImage == maContainer.end()) + { + aResult = Image(StockImage::Yes, rResourceId); + maContainer[rResourceId] = aResult; + } + else + aResult = iImage->second; + return aResult; +} + +//===== IconCache ============================================================= + +//static +IconCache& IconCache::Instance() +{ + if (Implementation::s_pIconCache == nullptr) + { + ::osl::GetGlobalMutex aMutexFunctor; + ::osl::MutexGuard aGuard(aMutexFunctor()); + if (Implementation::s_pIconCache == nullptr) + { + IconCache* pCache = new IconCache(); + SdGlobalResourceContainer::Instance().AddResource( + ::std::unique_ptr(pCache)); + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + Implementation::s_pIconCache = pCache; + } + } + else + { + OSL_DOUBLE_CHECKED_LOCKING_MEMORY_BARRIER(); + } + + DBG_ASSERT(Implementation::s_pIconCache != nullptr, "IconCache::Instance(): instance is NULL"); + return *Implementation::s_pIconCache; +} + +Image IconCache::GetIcon(const OUString& rResourceId) { return mpImpl->GetIcon(rResourceId); } + +IconCache::IconCache() + : mpImpl(new Implementation) +{ +} + +IconCache::~IconCache() +{ + // empty +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/IdleDetection.cxx b/sd/source/ui/tools/IdleDetection.cxx new file mode 100644 index 000000000..988bd849b --- /dev/null +++ b/sd/source/ui/tools/IdleDetection.cxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include + +#include +#include + +using namespace ::com::sun::star; + +namespace sd::tools { + +IdleState IdleDetection::GetIdleState (const vcl::Window* pWindow) +{ + IdleState nResult (CheckInputPending() | CheckSlideShowRunning()); + if (pWindow != nullptr) + nResult |= CheckWindowPainting(*pWindow); + return nResult; +} + +IdleState IdleDetection::CheckInputPending() +{ + if (Application::AnyInput(VclInputFlags::MOUSE | VclInputFlags::KEYBOARD | VclInputFlags::PAINT)) + return IdleState::SystemEventPending; + else + return IdleState::Idle; +} + +IdleState IdleDetection::CheckSlideShowRunning() +{ + IdleState eResult (IdleState::Idle); + + // Iterate over all view frames. + for (SfxViewFrame* pViewFrame = SfxViewFrame::GetFirst(); + pViewFrame!=nullptr; + pViewFrame = SfxViewFrame::GetNext(*pViewFrame)) + { + // Ignore the current frame when it does not exist, is not valid, or + // is not active. + bool bIgnoreFrame (true); + uno::Reference xFrame (pViewFrame->GetFrame().GetFrameInterface()); + try + { + if (xFrame.is() && xFrame->isActive()) + bIgnoreFrame = false; + } + catch (const uno::RuntimeException&) + { + } + if (bIgnoreFrame) + continue; + + // Get sd::ViewShell from active frame. + ViewShellBase* pBase = ViewShellBase::GetViewShellBase(pViewFrame); + if (pBase != nullptr) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( *pBase ) ); + if( xSlideShow.is() && xSlideShow->isRunning() ) + { + if (xSlideShow->isFullScreen()) + eResult |= IdleState::FullScreenShowActive; + else + eResult |= IdleState::WindowShowActive; + } + } + } + + return eResult; +} + +IdleState IdleDetection::CheckWindowPainting (const vcl::Window& rWindow) +{ + if (rWindow.IsInPaint()) + return IdleState::WindowPainting; + else + return IdleState::Idle; +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/PreviewRenderer.cxx b/sd/source/ui/tools/PreviewRenderer.cxx new file mode 100644 index 000000000..be8d8ca20 --- /dev/null +++ b/sd/source/ui/tools/PreviewRenderer.cxx @@ -0,0 +1,532 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +const int PreviewRenderer::snSubstitutionTextSize = 11; +const int PreviewRenderer::snFrameWidth = 1; + +namespace { + /** This incarnation of the ViewObjectContactRedirector filters away all + PageObj objects, unconditionally. + */ + class ViewRedirector : public sdr::contact::ViewObjectContactRedirector + { + public: + ViewRedirector(); + + virtual void createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override; + }; +} + +//===== PreviewRenderer ======================================================= + +PreviewRenderer::PreviewRenderer ( + const bool bHasFrame) + : mpPreviewDevice (VclPtr::Create()), + mpDocShellOfView(nullptr), + maFrameColor (svtools::ColorConfig().GetColorValue(svtools::DOCBOUNDARIES).nColor), + mbHasFrame(bHasFrame) +{ + mpPreviewDevice->SetBackground(Wallpaper( + Application::GetSettings().GetStyleSettings().GetWindowColor())); +} + +PreviewRenderer::~PreviewRenderer() +{ + if (mpDocShellOfView != nullptr) + EndListening (*mpDocShellOfView); +} + +Image PreviewRenderer::RenderPage ( + const SdPage* pPage, + const sal_Int32 nWidth) +{ + if (pPage != nullptr) + { + const Size aPageModelSize (pPage->GetSize()); + const double nAspectRatio ( + double(aPageModelSize.Width()) / double(aPageModelSize.Height())); + const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0); + const sal_Int32 nHeight (sal::static_int_cast( + (nWidth - 2*nFrameWidth) / nAspectRatio + 2*nFrameWidth + 0.5)); + return RenderPage ( + pPage, + Size(nWidth,nHeight), + false/*bObeyHighContrastMode*/); + } + else + return Image(); +} + +Image PreviewRenderer::RenderPage ( + const SdPage* pPage, + Size aPixelSize, + const bool bObeyHighContrastMode, + const bool bDisplayPresentationObjects) +{ + Image aPreview; + + if (pPage != nullptr) + { + try + { + if (Initialize(pPage, aPixelSize, bObeyHighContrastMode)) + { + PaintPage(pPage, bDisplayPresentationObjects); + PaintSubstitutionText(""); + PaintFrame(); + + Size aSize (mpPreviewDevice->GetOutputSizePixel()); + aPreview = Image(mpPreviewDevice->GetBitmapEx( + mpPreviewDevice->PixelToLogic(Point(0,0)), + mpPreviewDevice->PixelToLogic(aSize))); + + mpView->HideSdrPage(); + } + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.tools"); + } + } + + return aPreview; +} + +Image PreviewRenderer::RenderSubstitution ( + const Size& rPreviewPixelSize, + const OUString& rSubstitutionText) +{ + Image aPreview; + + try + { + // Set size. + mpPreviewDevice->SetOutputSizePixel(rPreviewPixelSize); + + // Adjust contrast mode. + const bool bUseContrast ( + Application::GetSettings().GetStyleSettings().GetHighContrastMode()); + mpPreviewDevice->SetDrawMode (bUseContrast + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR); + + // Set a map mode that makes a typical substitution text completely + // visible. + MapMode aMapMode (mpPreviewDevice->GetMapMode()); + aMapMode.SetMapUnit(MapUnit::Map100thMM); + Fraction aFinalScale(25 * rPreviewPixelSize.Width(), 28000); + aMapMode.SetScaleX(aFinalScale); + aMapMode.SetScaleY(aFinalScale); + const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0); + aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic( + Point(nFrameWidth,nFrameWidth),aMapMode)); + mpPreviewDevice->SetMapMode (aMapMode); + + // Clear the background. + const ::tools::Rectangle aPaintRectangle ( + Point(0,0), + mpPreviewDevice->GetOutputSizePixel()); + mpPreviewDevice->EnableMapMode(false); + mpPreviewDevice->SetLineColor(); + svtools::ColorConfig aColorConfig; + mpPreviewDevice->SetFillColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor); + mpPreviewDevice->DrawRect (aPaintRectangle); + mpPreviewDevice->EnableMapMode(); + + // Paint substitution text and a frame around it. + PaintSubstitutionText (rSubstitutionText); + PaintFrame(); + + const Size aSize (mpPreviewDevice->GetOutputSizePixel()); + aPreview = Image(mpPreviewDevice->GetBitmapEx( + mpPreviewDevice->PixelToLogic(Point(0,0)), + mpPreviewDevice->PixelToLogic(aSize))); + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.tools"); + } + + return aPreview; +} + +bool PreviewRenderer::Initialize ( + const SdPage* pPage, + const Size& rPixelSize, + const bool bObeyHighContrastMode) +{ + if (!pPage) + return false; + + SetupOutputSize(*pPage, rPixelSize); + SdDrawDocument& rDocument(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + DrawDocShell* pDocShell = rDocument.GetDocSh(); + + if (!pDocShell) + return false; + + // Create view + ProvideView (pDocShell); + if (mpView == nullptr) + return false; + + // Adjust contrast mode. + bool bUseContrast (bObeyHighContrastMode + && Application::GetSettings().GetStyleSettings().GetHighContrastMode()); + mpPreviewDevice->SetDrawMode (bUseContrast + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR); + mpPreviewDevice->SetSettings(Application::GetSettings()); + + // Tell the view to show the given page. + SdPage* pNonConstPage = const_cast(pPage); + if (pPage->IsMasterPage()) + { + mpView->ShowSdrPage(mpView->GetModel()->GetMasterPage(pPage->GetPageNum())); + } + else + { + mpView->ShowSdrPage(pNonConstPage); + } + + // Make sure that a page view exists. + SdrPageView* pPageView = mpView->GetSdrPageView(); + + if (pPageView == nullptr) + return false; + + // #i121224# No need to set SetApplicationBackgroundColor (which is the color + // of the area 'behind' the page (formerly called 'Wiese') since the page previews + // produced exactly cover the page's area, so it would never be visible. What + // needs to be set is the ApplicationDocumentColor which is derived from + // svtools::DOCCOLOR normally + Color aApplicationDocumentColor; + + if (pPageView->GetApplicationDocumentColor() == COL_AUTO) + { + svtools::ColorConfig aColorConfig; + aApplicationDocumentColor = aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor; + } + else + { + aApplicationDocumentColor = pPageView->GetApplicationDocumentColor(); + } + + pPageView->SetApplicationDocumentColor(aApplicationDocumentColor); + SdrOutliner& rOutliner(rDocument.GetDrawOutliner()); + rOutliner.SetBackgroundColor(aApplicationDocumentColor); + rOutliner.SetDefaultLanguage(rDocument.GetLanguage(EE_CHAR_LANGUAGE)); + mpPreviewDevice->SetBackground(Wallpaper(aApplicationDocumentColor)); + mpPreviewDevice->Erase(); + + return true; +} + +void PreviewRenderer::PaintPage ( + const SdPage* pPage, + const bool bDisplayPresentationObjects) +{ + // Paint the page. + ::tools::Rectangle aPaintRectangle (Point(0,0), pPage->GetSize()); + vcl::Region aRegion (aPaintRectangle); + + // Turn off online spelling and redlining. + SdrOutliner* pOutliner = nullptr; + EEControlBits nSavedControlWord = EEControlBits::NONE; + if (mpDocShellOfView!=nullptr && mpDocShellOfView->GetDoc()!=nullptr) + { + pOutliner = &mpDocShellOfView->GetDoc()->GetDrawOutliner(); + nSavedControlWord = pOutliner->GetControlWord(); + pOutliner->SetControlWord(nSavedControlWord & ~EEControlBits::ONLINESPELLING); + } + + // Use a special redirector to prevent PresObj shapes from being painted. + std::unique_ptr pRedirector; + if ( ! bDisplayPresentationObjects) + pRedirector.reset(new ViewRedirector()); + + try + { + mpView->CompleteRedraw(mpPreviewDevice.get(), aRegion, pRedirector.get()); + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.tools"); + } + + // Restore the previous online spelling and redlining states. + if (pOutliner != nullptr) + pOutliner->SetControlWord(nSavedControlWord); +} + +void PreviewRenderer::PaintSubstitutionText (const OUString& rSubstitutionText) +{ + if (rSubstitutionText.isEmpty()) + return; + + // Set the font size. + const vcl::Font& rOriginalFont (mpPreviewDevice->GetFont()); + vcl::Font aFont (mpPreviewDevice->GetSettings().GetStyleSettings().GetAppFont()); + sal_Int32 nHeight (mpPreviewDevice->PixelToLogic(Size(0,snSubstitutionTextSize)).Height()); + aFont.SetFontHeight(nHeight); + mpPreviewDevice->SetFont (aFont); + + // Paint the substitution text. + ::tools::Rectangle aTextBox ( + Point(0,0), + mpPreviewDevice->PixelToLogic( + mpPreviewDevice->GetOutputSizePixel())); + DrawTextFlags const nTextStyle = + DrawTextFlags::Center + | DrawTextFlags::VCenter + | DrawTextFlags::MultiLine + | DrawTextFlags::WordBreak; + mpPreviewDevice->DrawText (aTextBox, rSubstitutionText, nTextStyle); + + // Restore the font. + mpPreviewDevice->SetFont (rOriginalFont); +} + +void PreviewRenderer::PaintFrame() +{ + if (mbHasFrame) + { + // Paint a frame around the preview. + ::tools::Rectangle aPaintRectangle ( + Point(0,0), + mpPreviewDevice->GetOutputSizePixel()); + mpPreviewDevice->EnableMapMode(false); + mpPreviewDevice->SetLineColor(maFrameColor); + mpPreviewDevice->SetFillColor(); + mpPreviewDevice->DrawRect(aPaintRectangle); + mpPreviewDevice->EnableMapMode(); + } +} + +void PreviewRenderer::SetupOutputSize ( + const SdPage& rPage, + const Size& rFramePixelSize) +{ + // First set the map mode to some arbitrary scale that is numerically + // stable. + MapMode aMapMode (mpPreviewDevice->GetMapMode()); + aMapMode.SetMapUnit(MapUnit::MapPixel); + + // Adapt it to the desired width. + const Size aPageModelSize (rPage.GetSize()); + if (!aPageModelSize.IsEmpty()) + { + const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0); + aMapMode.SetScaleX( + Fraction(rFramePixelSize.Width()-2*nFrameWidth-1, aPageModelSize.Width())); + aMapMode.SetScaleY( + Fraction(rFramePixelSize.Height()-2*nFrameWidth-1, aPageModelSize.Height())); + aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(Point(nFrameWidth,nFrameWidth),aMapMode)); + } + else + { + // We should never get here. + OSL_ASSERT(false); + aMapMode.SetScaleX(Fraction(1.0)); + aMapMode.SetScaleY(Fraction(1.0)); + } + mpPreviewDevice->SetMapMode (aMapMode); + mpPreviewDevice->SetOutputSizePixel(rFramePixelSize); +} + +void PreviewRenderer::ProvideView (DrawDocShell* pDocShell) +{ + if (pDocShell != mpDocShellOfView) + { + // Destroy the view that is connected to the current doc shell. + mpView.reset(); + + // Switch our attention, i.e. listening for DYING events, to + // the new doc shell. + if (mpDocShellOfView != nullptr) + EndListening (*mpDocShellOfView); + mpDocShellOfView = pDocShell; + if (mpDocShellOfView != nullptr) + StartListening (*mpDocShellOfView); + } + if (mpView == nullptr) + { + mpView.reset (new DrawView (pDocShell, mpPreviewDevice.get(), nullptr)); + } + mpView->SetPreviewRenderer(true); +#if 1 + mpView->SetPageVisible(false); + mpView->SetPageBorderVisible(); + mpView->SetBordVisible(false); + mpView->SetGridVisible(false); + mpView->SetHlplVisible(false); + mpView->SetGlueVisible(false); + +#else + // This works in the slide sorter but prevents the master page + // background being painted in the list of current master pages in the + // task manager. + mpView->SetPagePaintingAllowed(false); +#endif +} + +Image PreviewRenderer::ScaleBitmap ( + const BitmapEx& rBitmapEx, + int nWidth) +{ + Image aPreview; + + do + { + // Adjust contrast mode. + bool bUseContrast = Application::GetSettings().GetStyleSettings(). + GetHighContrastMode(); + mpPreviewDevice->SetDrawMode (bUseContrast + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR); + + // Set output size. + Size aSize (rBitmapEx.GetSizePixel()); + if (aSize.Width() <= 0) + break; + Size aFrameSize ( + nWidth, + static_cast<::tools::Long>((nWidth*1.0 * aSize.Height()) / aSize.Width() + 0.5)); + Size aPreviewSize (aFrameSize.Width()-2,aFrameSize.Height()-2); + MapMode aMapMode (mpPreviewDevice->GetMapMode()); + aMapMode.SetMapUnit(MapUnit::MapPixel); + aMapMode.SetOrigin (Point()); + aMapMode.SetScaleX (Fraction(1.0)); + aMapMode.SetScaleY (Fraction(1.0)); + mpPreviewDevice->SetMapMode (aMapMode); + mpPreviewDevice->SetOutputSize (aFrameSize); + + // Paint a frame around the preview. + mpPreviewDevice->SetLineColor (maFrameColor); + mpPreviewDevice->SetFillColor (); + mpPreviewDevice->DrawRect (::tools::Rectangle(Point(0,0), aFrameSize)); + + // Paint the bitmap scaled to the desired width. + BitmapEx aScaledBitmap(rBitmapEx); + aScaledBitmap.Scale (aPreviewSize, BmpScaleFlag::BestQuality); + mpPreviewDevice->DrawBitmapEx ( + Point(1,1), + aPreviewSize, + aScaledBitmap); + + // Get the resulting bitmap. + aPreview = Image(mpPreviewDevice->GetBitmapEx(Point(0,0), aFrameSize)); + } + while (false); + + return aPreview; +} + +void PreviewRenderer::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (!mpDocShellOfView) + return; + + if (rHint.GetId() == SfxHintId::Dying) + { + // The doc shell is dying. Our view uses its item pool and + // has to be destroyed as well. The next call to + // ProvideView will create a new one (for another + // doc shell, of course.) + mpView.reset(); + mpDocShellOfView = nullptr; + } +} + +//===== ViewRedirector ======================================================== + +namespace { + +ViewRedirector::ViewRedirector() +{ +} + +void ViewRedirector::createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject(); + + if (pObject==nullptr || pObject->getSdrPageFromSdrObject() == nullptr) + { + // not a SdrObject visualisation (maybe e.g. page) or no page + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + rOriginal, + rDisplayInfo, + rVisitor); + return; + } + + const bool bDoCreateGeometry (pObject->getSdrPageFromSdrObject()->checkVisibility( rOriginal, rDisplayInfo, true)); + + if ( ! bDoCreateGeometry + && (pObject->GetObjInventor() != SdrInventor::Default || pObject->GetObjIdentifier() != SdrObjKind::Page)) + { + return; + } + + if (pObject->IsEmptyPresObj()) + return; + + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + rOriginal, + rDisplayInfo, + rVisitor); +} + +} // end of anonymous namespace + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/PropertySet.cxx b/sd/source/ui/tools/PropertySet.cxx new file mode 100644 index 000000000..057b7dd96 --- /dev/null +++ b/sd/source/ui/tools/PropertySet.cxx @@ -0,0 +1,158 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd::tools { + +PropertySet::PropertySet() + : PropertySetInterfaceBase(m_aMutex), + mpChangeListeners(new ChangeListenerContainer) +{ +} + +PropertySet::~PropertySet() +{ +} + +void SAL_CALL PropertySet::disposing() +{ +} + +//----- XPropertySet ---------------------------------------------------------- + +Reference SAL_CALL PropertySet::getPropertySetInfo() +{ + return nullptr; +} + +void SAL_CALL PropertySet::setPropertyValue ( + const OUString& rsPropertyName, + const css::uno::Any& rsPropertyValue) +{ + ThrowIfDisposed(); + + Any aOldValue (SetPropertyValue(rsPropertyName,rsPropertyValue)); + if (aOldValue == rsPropertyValue) + return; + + // Inform listeners that are registered specifically for the + // property and those registered for any property. + beans::PropertyChangeEvent aEvent( + static_cast(this), + rsPropertyName, + false, + -1, + aOldValue, + rsPropertyValue); + CallListeners(rsPropertyName, aEvent); + CallListeners(OUString(), aEvent); +} + +Any SAL_CALL PropertySet::getPropertyValue (const OUString& rsPropertyName) +{ + ThrowIfDisposed(); + + return GetPropertyValue(rsPropertyName); +} + +void SAL_CALL PropertySet::addPropertyChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference& rxListener) +{ + if ( ! rxListener.is()) + throw lang::IllegalArgumentException(); + + if (rBHelper.bDisposed || rBHelper.bInDispose) + return; + + mpChangeListeners->emplace(rsPropertyName, rxListener); +} + +void SAL_CALL PropertySet::removePropertyChangeListener ( + const OUString& rsPropertyName, + const css::uno::Reference& rxListener) +{ + ::std::pair + aRange (mpChangeListeners->equal_range(rsPropertyName)); + + ChangeListenerContainer::iterator iListener ( + ::std::find_if( + aRange.first, + aRange.second, + [&rxListener] (const ChangeListenerContainer::value_type& listener) { + return listener.second == rxListener; + })); + + if (iListener == mpChangeListeners->end()) + { + throw lang::IllegalArgumentException(); + } + + mpChangeListeners->erase(iListener); + +} + +void SAL_CALL PropertySet::addVetoableChangeListener ( + const OUString&, + const css::uno::Reference&) +{ + // Constraint properties are not supported and thus no vetoable + // listeners. +} + +void SAL_CALL PropertySet::removeVetoableChangeListener ( + const OUString&, + const css::uno::Reference&) +{ + // Constraint properties are not supported and thus no vetoable + // listeners. +} + +void PropertySet::CallListeners ( + const OUString& rsPropertyName, + const beans::PropertyChangeEvent& rEvent) +{ + ::std::pair + aRange (mpChangeListeners->equal_range(rsPropertyName)); + ChangeListenerContainer::const_iterator iListener; + for (iListener=aRange.first; iListener!=aRange.second; ++iListener) + { + if (iListener->second.is()) + iListener->second->propertyChange(rEvent); + } +} + +void PropertySet::ThrowIfDisposed() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + { + throw lang::DisposedException ( + "PropertySet object has already been disposed", + static_cast(this)); + } +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/SdGlobalResourceContainer.cxx b/sd/source/ui/tools/SdGlobalResourceContainer.cxx new file mode 100644 index 000000000..a7dbc5f25 --- /dev/null +++ b/sd/source/ui/tools/SdGlobalResourceContainer.cxx @@ -0,0 +1,197 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include <../cache/SlsCacheConfiguration.hxx> + +#include +#include + +#include + +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +class SdGlobalResourceContainerInstance + : public comphelper::unique_disposing_solar_mutex_reset_ptr +{ +public: + SdGlobalResourceContainerInstance() + : comphelper::unique_disposing_solar_mutex_reset_ptr( + uno::Reference(frame::Desktop::create(comphelper::getProcessComponentContext()), uno::UNO_QUERY_THROW), + new SdGlobalResourceContainer, true) + { + } +}; + +namespace { + +SdGlobalResourceContainerInstance& theSdGlobalResourceContainerInstance() +{ + static SdGlobalResourceContainerInstance SINGLETON; + return SINGLETON; +} + +} // namespace + +//===== SdGlobalResourceContainer::Implementation ============================= + +class SdGlobalResourceContainer::Implementation +{ +private: + friend class SdGlobalResourceContainer; + + ::osl::Mutex maMutex; + + /** All instances of SdGlobalResource in this vector are owned by the + container and will be destroyed when the container is destroyed. + */ + std::vector> maResources; + + typedef ::std::vector > SharedResourceList; + SharedResourceList maSharedResources; + + typedef ::std::vector > XInterfaceResourceList; + XInterfaceResourceList maXInterfaceResources; +}; + +// static +SdGlobalResourceContainer& SdGlobalResourceContainer::Instance() +{ + SdGlobalResourceContainer *const pRet(theSdGlobalResourceContainerInstance().get()); + assert(pRet); // error if it has been deleted and is null + return *pRet; +} + +//===== SdGlobalResourceContainer ============================================= + +void SdGlobalResourceContainer::AddResource ( + ::std::unique_ptr pResource) +{ + ::osl::MutexGuard aGuard (mpImpl->maMutex); + + assert( std::none_of( + mpImpl->maResources.begin(), + mpImpl->maResources.end(), + [&](const std::unique_ptr& p) { return p == pResource; }) + && "duplicate resource?"); + + mpImpl->maResources.push_back(std::move(pResource)); +} + +void SdGlobalResourceContainer::AddResource ( + const std::shared_ptr& pResource) +{ + ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Implementation::SharedResourceList::iterator iResource = ::std::find ( + mpImpl->maSharedResources.begin(), + mpImpl->maSharedResources.end(), + pResource); + if (iResource == mpImpl->maSharedResources.end()) + mpImpl->maSharedResources.push_back(pResource); + else + { + SAL_WARN ("sd.tools", + "SdGlobalResourceContainer:AddResource(): Resource added twice."); + } +} + +void SdGlobalResourceContainer::AddResource (const Reference& rxResource) +{ + ::osl::MutexGuard aGuard (mpImpl->maMutex); + + Implementation::XInterfaceResourceList::iterator iResource = ::std::find ( + mpImpl->maXInterfaceResources.begin(), + mpImpl->maXInterfaceResources.end(), + rxResource); + if (iResource == mpImpl->maXInterfaceResources.end()) + mpImpl->maXInterfaceResources.push_back(rxResource); + else + { + SAL_WARN ("sd.tools", + "SdGlobalResourceContainer:AddResource(): Resource added twice."); + } +} + +SdGlobalResourceContainer::SdGlobalResourceContainer() + : mpImpl (new SdGlobalResourceContainer::Implementation) +{ +} + +SdGlobalResourceContainer::~SdGlobalResourceContainer() +{ + ::osl::MutexGuard aGuard (mpImpl->maMutex); + + // Release the resources in reversed order of their addition to the + // container. This is because a resource A added before resource B + // may have been created due to a request of B. Thus B depends on A and + // should be destroyed first. + for (auto iResource = mpImpl->maResources.rbegin(); + iResource != mpImpl->maResources.rend(); + ++iResource) + { + iResource->reset(); + } + + + // The SharedResourceList has not to be released manually. We just + // assert resources that are still held by someone other than us. + Implementation::SharedResourceList::reverse_iterator iSharedResource; + for (iSharedResource = mpImpl->maSharedResources.rbegin(); + iSharedResource != mpImpl->maSharedResources.rend(); + ++iSharedResource) + { + if (iSharedResource->use_count() > 1) + { + SdGlobalResource* pResource = iSharedResource->get(); + SAL_INFO( + "sd.tools", pResource << " " << iSharedResource->use_count()); + DBG_ASSERT(iSharedResource->use_count() == 1, + "SdGlobalResource still held in ~SdGlobalResourceContainer"); + } + } + + Implementation::XInterfaceResourceList::reverse_iterator iXInterfaceResource; + for (iXInterfaceResource = mpImpl->maXInterfaceResources.rbegin(); + iXInterfaceResource != mpImpl->maXInterfaceResources.rend(); + ++iXInterfaceResource) + { + Reference xComponent (*iXInterfaceResource, UNO_QUERY); + *iXInterfaceResource = nullptr; + if (xComponent.is()) + xComponent->dispose(); + } + + sd::slidesorter::cache::CacheConfiguration::Shutdown(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/SlotStateListener.cxx b/sd/source/ui/tools/SlotStateListener.cxx new file mode 100644 index 000000000..9b75b322e --- /dev/null +++ b/sd/source/ui/tools/SlotStateListener.cxx @@ -0,0 +1,153 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; + +namespace sd::tools { + +SlotStateListener::SlotStateListener ( + Link const & rCallback, + const uno::Reference& rxDispatchProvider, + const OUString& rSlotName) + : mxDispatchProviderWeak(nullptr) +{ + SetCallback(rCallback); + ConnectToDispatchProvider(rxDispatchProvider); + ObserveSlot(rSlotName); +} + +SlotStateListener::~SlotStateListener() +{ + ReleaseListeners(); +} + +void SlotStateListener::SetCallback (const Link& rCallback) +{ + ThrowIfDisposed(); + + maCallback = rCallback; +} + +void SlotStateListener::ConnectToDispatchProvider ( + const uno::Reference& rxDispatchProvider) +{ + ThrowIfDisposed(); + + // When we are listening to state changes of slots of another frame then + // release these listeners first. + if ( ! maRegisteredURLList.empty()) + ReleaseListeners(); + + mxDispatchProviderWeak = rxDispatchProvider; +} + +void SlotStateListener::ObserveSlot (const OUString& rSlotName) +{ + ThrowIfDisposed(); + + if (maCallback.IsSet()) + { + // Connect the state change listener. + util::URL aURL (MakeURL(rSlotName)); + uno::Reference xDispatch (GetDispatch(aURL)); + if (xDispatch.is()) + { + maRegisteredURLList.push_back(aURL); + xDispatch->addStatusListener(this,aURL); + } + } +} + +void SlotStateListener::disposing(std::unique_lock&) +{ + ReleaseListeners(); + mxDispatchProviderWeak.clear(); + maCallback = Link(); +} + +util::URL SlotStateListener::MakeURL (const OUString& rSlotName) +{ + util::URL aURL; + aURL.Complete = rSlotName; + + uno::Reference xTransformer(util::URLTransformer::create(::comphelper::getProcessComponentContext())); + xTransformer->parseStrict(aURL); + + return aURL; +} + +uno::Reference + SlotStateListener::GetDispatch (const util::URL& rURL) const +{ + uno::Reference xDispatch; + + uno::Reference xDispatchProvider (mxDispatchProviderWeak); + if (xDispatchProvider.is()) + xDispatch = xDispatchProvider->queryDispatch(rURL, OUString(), 0); + + return xDispatch; +} + +void SlotStateListener::statusChanged ( + const frame::FeatureStateEvent& rState) +{ + ThrowIfDisposed(); + OUString sSlotName (rState.FeatureURL.Complete); + maCallback.Call(sSlotName); +} + +void SlotStateListener::ReleaseListeners() +{ + for (const auto& rURL : maRegisteredURLList) + { + uno::Reference xDispatch (GetDispatch(rURL)); + if (xDispatch.is()) + { + xDispatch->removeStatusListener(this,rURL); + } + } +} + +//===== lang::XEventListener ================================================ + +void SAL_CALL SlotStateListener::disposing ( + const lang::EventObject& ) +{ +} + +void SlotStateListener::ThrowIfDisposed() +{ + if (m_bDisposed) + { + throw lang::DisposedException ("SlideSorterController object has already been disposed", + static_cast(this)); + } +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/tools/TimerBasedTaskExecution.cxx b/sd/source/ui/tools/TimerBasedTaskExecution.cxx new file mode 100644 index 000000000..ae1f2233f --- /dev/null +++ b/sd/source/ui/tools/TimerBasedTaskExecution.cxx @@ -0,0 +1,130 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +namespace sd::tools { + +/** Used by the shared_ptr instead of the private destructor. +*/ +class TimerBasedTaskExecution::Deleter +{ +public: + void operator() (TimerBasedTaskExecution* pObject) + { + delete pObject; + } +}; + +std::shared_ptr TimerBasedTaskExecution::Create ( + const std::shared_ptr& rpTask, + sal_uInt32 nMillisecondsBetweenSteps, + sal_uInt32 nMaxTimePerStep) +{ + std::shared_ptr pExecution( + new TimerBasedTaskExecution(rpTask,nMillisecondsBetweenSteps,nMaxTimePerStep), + Deleter()); + // Let the new object have a shared_ptr to itself, so that it can + // release itself when the AsynchronousTask has been executed + // completely. + if (pExecution->mpTask != nullptr) + pExecution->mpSelf = pExecution; + return pExecution; +} + +void TimerBasedTaskExecution::Release() +{ + maTimer.Stop(); + mpSelf.reset(); +} + +//static +void TimerBasedTaskExecution::ReleaseTask ( + const std::weak_ptr& rpExecution) +{ + if ( rpExecution.expired()) + return; + + try + { + std::shared_ptr pExecution (rpExecution); + pExecution->Release(); + } + catch (const std::bad_weak_ptr&) + { + // When a bad_weak_ptr has been thrown then the object pointed + // to by rpTask has been released right after we checked that it + // still existed. Too bad, but that means, that we have nothing + // more do. + } +} + +TimerBasedTaskExecution::TimerBasedTaskExecution ( + const std::shared_ptr& rpTask, + sal_uInt32 nMillisecondsBetweenSteps, + sal_uInt32 nMaxTimePerStep) + : mpTask(rpTask), + maTimer("sd TimerBasedTaskExecution maTimer"), + mnMaxTimePerStep(nMaxTimePerStep) +{ + maTimer.SetInvokeHandler( LINK(this,TimerBasedTaskExecution,TimerCallback) ); + maTimer.SetTimeout(nMillisecondsBetweenSteps); + maTimer.Start(); +} + +TimerBasedTaskExecution::~TimerBasedTaskExecution() +{ + maTimer.Stop(); +} + +IMPL_LINK_NOARG(TimerBasedTaskExecution, TimerCallback, Timer *, void) +{ + if (mpTask == nullptr) + return; + + if (mpTask->HasNextStep()) + { + // Execute as many steps as fit into the time span of length + // mnMaxTimePerStep. Note that the last step may take longer + // than allowed. + sal_uInt32 nStartTime (::tools::Time( ::tools::Time::SYSTEM ).GetMSFromTime()); + SAL_INFO("sd.tools", __func__ << ": starting TimerBasedTaskExecution at " << nStartTime); + do + { + mpTask->RunNextStep(); + sal_uInt32 nDuration (::tools::Time( ::tools::Time::SYSTEM ).GetMSFromTime()-nStartTime); + SAL_INFO("sd.tools", __func__ << ": executed step in " << nDuration); + if (nDuration > mnMaxTimePerStep) + break; + } + while (mpTask->HasNextStep()); + SAL_INFO("sd.tools", __func__ << ": TimerBasedTaskExecution sleeping"); + maTimer.Start(); + } + else + mpSelf.reset(); +} + +} // end of namespace ::sd::tools + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/uitest/uiobject.cxx b/sd/source/ui/uitest/uiobject.cxx new file mode 100644 index 000000000..afd130ee2 --- /dev/null +++ b/sd/source/ui/uitest/uiobject.cxx @@ -0,0 +1,183 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace +{ +class ImpressSdrObject : public SdrUIObject +{ +public: + ImpressSdrObject(const VclPtr& xImpressWin, const OUString& rName); + + SdrObject* get_object() override; + +private: + VclPtr mxWindow; + + OUString maName; +}; + +sd::DrawViewShell* getViewShell(const VclPtr& xWindow) +{ + sd::DrawViewShell* pViewShell = dynamic_cast(xWindow->GetViewShell()); + assert(pViewShell); + + return pViewShell; +} + +OUString getObjectName(SdrObject const* pObject) +{ + if (pObject->GetName().isEmpty()) + return "Unnamed Drawinglayer object " + OUString::number(pObject->GetOrdNum()); + else + return pObject->GetName(); +} + +SdrObject* getObject(const VclPtr& xWindow, std::u16string_view rName) +{ + SdrPage* pPage = getViewShell(xWindow)->getCurrentPage(); + + if (!pPage) + return nullptr; + + size_t nObjs = pPage->GetObjCount(); + for (size_t i = 0; i < nObjs; ++i) + { + SdrObject* pObj = pPage->GetObj(i); + if (rName == getObjectName(pObj)) + return pObj; + } + + return nullptr; +} +} + +ImpressSdrObject::ImpressSdrObject(const VclPtr& xImpressWin, const OUString& rName) + : mxWindow(xImpressWin) + , maName(rName) +{ +} + +SdrObject* ImpressSdrObject::get_object() { return getObject(mxWindow, maName); } + +ImpressWindowUIObject::ImpressWindowUIObject(const VclPtr& xWindow) + : WindowUIObject(xWindow) + , mxWindow(xWindow) +{ +} + +StringMap ImpressWindowUIObject::get_state() +{ + StringMap aMap = WindowUIObject::get_state(); + + aMap["SelectedText"] = getViewShell(mxWindow)->GetSelectionText(false); + aMap["CurrentSlide"] = OUString::number(getViewShell(mxWindow)->GetCurPagePos() + 1); + aMap["Zoom"] = OUString::number(getViewShell(mxWindow)->GetZoom()); + + return aMap; +} + +void ImpressWindowUIObject::execute(const OUString& rAction, const StringMap& rParameters) +{ + if (rAction == "SET") + { + if (rParameters.find("ZOOM") != rParameters.end()) + { + auto itr = rParameters.find("ZOOM"); + OUString aVal = itr->second; + sal_Int32 nVal = aVal.toInt32(); + getViewShell(mxWindow)->SetZoom(nVal); + } + } + else if (rAction == "GOTO") + { + if (rParameters.find("PAGE") != rParameters.end()) + { + auto itr = rParameters.find("PAGE"); + OUString aVal = itr->second; + sal_Int32 nVal = aVal.toInt32(); + getViewShell(mxWindow)->SwitchPage(nVal - 1); + } + } + else if (rAction == "SELECT") + { + if (rParameters.find("OBJECT") != rParameters.end()) + { + auto itr = rParameters.find("OBJECT"); + OUString aName = itr->second; + SdrObject* pObj = getObject(mxWindow, aName); + SdrPageView* pPageView = getViewShell(mxWindow)->GetView()->GetSdrPageView(); + getViewShell(mxWindow)->GetView()->MarkObj(pObj, pPageView); + } + } + else if (rAction == "SIDEBAR") + { + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + DBG_ASSERT(pViewFrm, "ImpressWindowUIObject::execute: no viewframe"); + pViewFrm->ShowChildWindow(SID_SIDEBAR); + + auto itr = rParameters.find("PANEL"); + if (itr != rParameters.end()) + { + OUString aVal = itr->second; + ::sfx2::sidebar::Sidebar::ShowPanel(aVal, pViewFrm->GetFrame().GetFrameInterface()); + } + } + else if (rAction == "DESELECT") + { + getViewShell(mxWindow)->GetView()->UnMarkAll(); + } + else + WindowUIObject::execute(rAction, rParameters); +} + +std::unique_ptr ImpressWindowUIObject::get_child(const OUString& rID) +{ + return std::unique_ptr(new ImpressSdrObject(mxWindow, rID)); +} + +std::set ImpressWindowUIObject::get_children() const +{ + SdrPage* pPage = getViewShell(mxWindow)->getCurrentPage(); + + std::set aRet; + if (!pPage) + return aRet; + + size_t nObjs = pPage->GetObjCount(); + for (size_t i = 0; i < nObjs; ++i) + { + SdrObject* pObject = pPage->GetObj(i); + aRet.insert(getObjectName(pObject)); + } + + return aRet; +} + +OUString ImpressWindowUIObject::get_name() const { return "ImpressWindowUIObject"; } + +std::unique_ptr ImpressWindowUIObject::create(vcl::Window* pWindow) +{ + sd::Window* pWin = dynamic_cast(pWindow); + assert(pWin); + return std::unique_ptr(new ImpressWindowUIObject(pWin)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/DrawController.cxx b/sd/source/ui/unoidl/DrawController.cxx new file mode 100644 index 000000000..e4afc842a --- /dev/null +++ b/sd/source/ui/unoidl/DrawController.cxx @@ -0,0 +1,815 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::std; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using vcl::EnumContext; + +namespace sd { + +DrawController::DrawController (ViewShellBase& rBase) noexcept + : DrawControllerInterfaceBase(&rBase), + BroadcastHelperOwner(SfxBaseController::m_aMutex), + OPropertySetHelper(BroadcastHelperOwner::maBroadcastHelper), + mpCurrentLayer(nullptr), + m_aSelectionTypeIdentifier( + cppu::UnoType::get()), + mpBase(&rBase), + mpCurrentPage(nullptr), + mbMasterPageMode(false), + mbLayerMode(false), + mbDisposing(false) +{ + ProvideFrameworkControllers(); +} + +DrawController::~DrawController() noexcept +{ +} + +void DrawController::SetSubController ( + const Reference& rxSubController) +{ + // Update the internal state. + mxSubController = rxSubController; + mpPropertyArrayHelper.reset(); + maLastVisArea = ::tools::Rectangle(); + + // Inform listeners about the changed state. + FireSelectionChangeListener(); +} + +// XInterface + +IMPLEMENT_FORWARD_XINTERFACE2( + DrawController, + DrawControllerInterfaceBase, + OPropertySetHelper); + +// XTypeProvider + +Sequence SAL_CALL DrawController::getTypes() +{ + ThrowIfDisposed(); + // OPropertySetHelper does not provide getTypes, so we have to + // implement this method manually and list its three interfaces. + OTypeCollection aTypeCollection ( + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get()); + + return ::comphelper::concatSequences( + SfxBaseController::getTypes(), + aTypeCollection.getTypes(), + DrawControllerInterfaceBase::getTypes()); +} + +IMPLEMENT_GET_IMPLEMENTATION_ID(DrawController); + +// XComponent + +void SAL_CALL DrawController::dispose() +{ + if( mbDisposing ) + return; + + SolarMutexGuard aGuard; + + if( mbDisposing ) + return; + + mbDisposing = true; + + std::shared_ptr pViewShell; + if (mpBase) + pViewShell = mpBase->GetMainViewShell(); + if ( pViewShell ) + { + pViewShell->DeactivateCurrentFunction(); + auto* pView = pViewShell->GetView(); + if (pView) + pView->getSearchContext().resetSearchFunction(); + } + pViewShell.reset(); + + // When the controller has not been detached from its view + // shell, i.e. mpViewShell is not NULL, then tell PaneManager + // and ViewShellManager to clear the shell stack. + if (mxSubController.is() && mpBase!=nullptr) + { + mpBase->DisconnectAllClients(); + mpBase->GetViewShellManager()->Shutdown(); + } + + OPropertySetHelper::disposing(); + + DisposeFrameworkControllers(); + + SfxBaseController::dispose(); +} + +void SAL_CALL DrawController::addEventListener( + const Reference& xListener) +{ + ThrowIfDisposed(); + SfxBaseController::addEventListener( xListener ); +} + +void SAL_CALL DrawController::removeEventListener ( + const Reference& aListener) +{ + if(!rBHelper.bDisposed && !rBHelper.bInDispose && !mbDisposing) + SfxBaseController::removeEventListener( aListener ); +} + +// XController +sal_Bool SAL_CALL DrawController::suspend( sal_Bool Suspend ) +{ + if( Suspend ) + { + ViewShellBase* pViewShellBase = GetViewShellBase(); + if( pViewShellBase ) + { + // do not allow suspend if a slideshow needs this controller! + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( *pViewShellBase ) ); + if( xSlideShow.is() && xSlideShow->dependsOn(pViewShellBase) ) + return false; + } + } + + return SfxBaseController::suspend( Suspend ); +} + +// XServiceInfo +OUString SAL_CALL DrawController::getImplementationName( ) +{ + // Do not throw an exception at the moment. This leads to a crash + // under Solaris on reload. See issue i70929 for details. + // ThrowIfDisposed(); + return "DrawController" ; +} + +constexpr OUStringLiteral ssServiceName = u"com.sun.star.drawing.DrawingDocumentDrawView"; + +sal_Bool SAL_CALL DrawController::supportsService (const OUString& rsServiceName) +{ + return cppu::supportsService(this, rsServiceName); +} + +Sequence SAL_CALL DrawController::getSupportedServiceNames() +{ + ThrowIfDisposed(); + Sequence aSupportedServices { ssServiceName }; + return aSupportedServices; +} + +//------ XSelectionSupplier -------------------------------------------- +sal_Bool SAL_CALL DrawController::select (const Any& aSelection) +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + if (mxSubController.is()) + return mxSubController->select(aSelection); + else + return false; +} + +Any SAL_CALL DrawController::getSelection() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + if (mxSubController.is()) + return mxSubController->getSelection(); + else + return Any(); +} + +void SAL_CALL DrawController::addSelectionChangeListener( + const Reference< view::XSelectionChangeListener >& xListener) +{ + if( mbDisposing ) + throw lang::DisposedException(); + + BroadcastHelperOwner::maBroadcastHelper.addListener (m_aSelectionTypeIdentifier, xListener); +} + +void SAL_CALL DrawController::removeSelectionChangeListener( + const Reference< view::XSelectionChangeListener >& xListener ) +{ + if (rBHelper.bDisposed) + throw lang::DisposedException(); + + BroadcastHelperOwner::maBroadcastHelper.removeListener (m_aSelectionTypeIdentifier, xListener); +} + +//===== lang::XEventListener ================================================ + +void SAL_CALL + DrawController::disposing (const lang::EventObject& ) +{ +} + +//===== view::XSelectionChangeListener ====================================== + +void SAL_CALL + DrawController::selectionChanged (const lang::EventObject& rEvent) +{ + ThrowIfDisposed(); + // Have to forward the event to our selection change listeners. + OInterfaceContainerHelper* pListeners = BroadcastHelperOwner::maBroadcastHelper.getContainer( + cppu::UnoType::get()); + if (!pListeners) + return; + + // Re-send the event to all of our listeners. + OInterfaceIteratorHelper aIterator (*pListeners); + while (aIterator.hasMoreElements()) + { + try + { + view::XSelectionChangeListener* pListener = + static_cast( + aIterator.next()); + if (pListener != nullptr) + pListener->selectionChanged (rEvent); + } + catch (const RuntimeException&) + { + } + } +} + +// XDrawView + +void SAL_CALL DrawController::setCurrentPage( const Reference< drawing::XDrawPage >& xPage ) +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + + if (mxSubController.is()) + mxSubController->setCurrentPage(xPage); +} + +Reference< drawing::XDrawPage > SAL_CALL DrawController::getCurrentPage() +{ + ThrowIfDisposed(); + SolarMutexGuard aGuard; + Reference xPage; + + // Get current page from sub controller. + if (mxSubController.is()) + xPage = mxSubController->getCurrentPage(); + + // When there is not yet a sub controller (during initialization) then fall back + // to the current page in mpCurrentPage. + if ( ! xPage.is() ) + if (rtl::Reference pPage = mpCurrentPage.get()) + xPage.set(pPage->getUnoPage(), UNO_QUERY); + + return xPage; +} + +void DrawController::FireVisAreaChanged (const ::tools::Rectangle& rVisArea) noexcept +{ + if( maLastVisArea == rVisArea ) + return; + + Any aNewValue; + aNewValue <<= awt::Rectangle( + rVisArea.Left(), + rVisArea.Top(), + rVisArea.GetWidth(), + rVisArea.GetHeight() ); + + Any aOldValue; + aOldValue <<= awt::Rectangle( + maLastVisArea.Left(), + maLastVisArea.Top(), + maLastVisArea.GetWidth(), + maLastVisArea.GetHeight() ); + + FirePropertyChange (PROPERTY_WORKAREA, aNewValue, aOldValue); + + maLastVisArea = rVisArea; +} + +void DrawController::FireSelectionChangeListener() noexcept +{ + OInterfaceContainerHelper * pLC = BroadcastHelperOwner::maBroadcastHelper.getContainer( + m_aSelectionTypeIdentifier); + if( !pLC ) + return; + + Reference< XInterface > xSource( static_cast(this) ); + const lang::EventObject aEvent( xSource ); + + // iterate over all listeners and send events + OInterfaceIteratorHelper aIt( *pLC); + while( aIt.hasMoreElements() ) + { + try + { + view::XSelectionChangeListener * pL = + static_cast(aIt.next()); + if (pL != nullptr) + pL->selectionChanged( aEvent ); + } + catch (const RuntimeException&) + { + } + } +} + +void DrawController::FireChangeEditMode (bool bMasterPageMode) noexcept +{ + if (bMasterPageMode != mbMasterPageMode ) + { + FirePropertyChange( + PROPERTY_MASTERPAGEMODE, + Any(bMasterPageMode), + Any(mbMasterPageMode)); + + mbMasterPageMode = bMasterPageMode; + } +} + +void DrawController::FireChangeLayerMode (bool bLayerMode) noexcept +{ + if (bLayerMode != mbLayerMode) + { + FirePropertyChange( + PROPERTY_LAYERMODE, + Any(bLayerMode), + Any(mbLayerMode)); + + mbLayerMode = bLayerMode; + } +} + +void DrawController::FireSwitchCurrentPage (SdPage* pNewCurrentPage) noexcept +{ + rtl::Reference pCurrentPage = mpCurrentPage.get(); + if (pNewCurrentPage == pCurrentPage.get()) + return; + + try + { + Any aNewValue ( + Any(Reference(pNewCurrentPage->getUnoPage(), UNO_QUERY))); + + Any aOldValue; + if (pCurrentPage != nullptr) + { + Reference xOldPage (pCurrentPage->getUnoPage(), UNO_QUERY); + aOldValue <<= xOldPage; + } + + FirePropertyChange(PROPERTY_CURRENTPAGE, aNewValue, aOldValue); + + mpCurrentPage = pNewCurrentPage; + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("sd", "sd::SdUnoDrawView::FireSwitchCurrentPage()"); + } +} + +void DrawController::NotifyAccUpdate() +{ + sal_Int32 nHandle = PROPERTY_UPDATEACC; + Any aNewValue, aOldValue; + fire (&nHandle, &aNewValue, &aOldValue, 1, false); +} + +void DrawController::fireChangeLayer( css::uno::Reference< css::drawing::XLayer>* pCurrentLayer ) noexcept +{ + if( pCurrentLayer != mpCurrentLayer ) + { + sal_Int32 nHandle = PROPERTY_ACTIVE_LAYER; + + Any aNewValue ( *pCurrentLayer); + + Any aOldValue ; + + fire (&nHandle, &aNewValue, &aOldValue, 1, false); + + mpCurrentLayer = pCurrentLayer; + } +} + +// This method is only called in slide show and outline view +//void DrawController::fireSwitchCurrentPage(String pageName ) throw() +void DrawController::fireSwitchCurrentPage(sal_Int32 pageIndex ) noexcept +{ + Any aNewValue; + Any aOldValue; + //OUString aPageName( pageName ); + //aNewValue <<= aPageName ; + aNewValue <<= pageIndex; + + // Use new property to handle page change event + sal_Int32 nHandles = PROPERTY_PAGE_CHANGE; + fire( &nHandles, &aNewValue, &aOldValue, 1, false ); +} + +void DrawController::FirePropertyChange ( + sal_Int32 nHandle, + const Any& rNewValue, + const Any& rOldValue) +{ + try + { + fire (&nHandle, &rNewValue, &rOldValue, 1, false); + } + catch (const RuntimeException&) + { + // Ignore this exception. Exceptions should be handled in the + // fire() function so that all listeners are called. This is + // not the case at the moment, so we simply ignore the + // exception. + } + +} + +void DrawController::BroadcastContextChange() const +{ + std::shared_ptr pViewShell (mpBase->GetMainViewShell()); + if ( ! pViewShell) + return; + + EnumContext::Context eContext (EnumContext::Context::Unknown); + switch (pViewShell->GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_DRAW: + if (mbMasterPageMode) + eContext = EnumContext::Context::MasterPage; + else + eContext = EnumContext::Context::DrawPage; + break; + + case ViewShell::ST_NOTES: + eContext = EnumContext::Context::NotesPage; + break; + + case ViewShell::ST_HANDOUT: + eContext = EnumContext::Context::HandoutPage; + break; + + case ViewShell::ST_OUTLINE: + eContext = EnumContext::Context::OutlineText; + break; + + case ViewShell::ST_SLIDE_SORTER: + eContext = EnumContext::Context::SlidesorterPage; + break; + + case ViewShell::ST_PRESENTATION: + case ViewShell::ST_NONE: + default: + eContext = EnumContext::Context::Empty; + break; + } + + ContextChangeEventMultiplexer::NotifyContextChange(mpBase, eContext); +} + +void DrawController::ReleaseViewShellBase() +{ + DisposeFrameworkControllers(); + mpBase = nullptr; +} + +//===== XControllerManager ============================================================== + +Reference SAL_CALL + DrawController::getConfigurationController() +{ + ThrowIfDisposed(); + + return mxConfigurationController; +} + +Reference SAL_CALL + DrawController::getModuleController() +{ + ThrowIfDisposed(); + + return mxModuleController; +} + +//===== XUnoTunnel ============================================================ + +const Sequence& DrawController::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theDrawControllerUnoTunnelId; + return theDrawControllerUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL DrawController::getSomething (const Sequence& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +//===== Properties ============================================================ + +void DrawController::FillPropertyTable ( + ::std::vector& rProperties) +{ + rProperties.emplace_back("VisibleArea", + PROPERTY_WORKAREA, + ::cppu::UnoType< css::awt::Rectangle>::get(), + beans::PropertyAttribute::BOUND | beans::PropertyAttribute::READONLY); + rProperties.emplace_back( + "SubController", + PROPERTY_SUB_CONTROLLER, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND); + rProperties.emplace_back( + "CurrentPage", + PROPERTY_CURRENTPAGE, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("IsLayerMode", + PROPERTY_LAYERMODE, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("IsMasterPageMode", + PROPERTY_MASTERPAGEMODE, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("ActiveLayer", + PROPERTY_ACTIVE_LAYER, + cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("ZoomValue", + PROPERTY_ZOOMVALUE, + ::cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("ZoomType", + PROPERTY_ZOOMTYPE, + ::cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("ViewOffset", + PROPERTY_VIEWOFFSET, + ::cppu::UnoType< css::awt::Point>::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back("DrawViewMode", + PROPERTY_DRAWVIEWMODE, + ::cppu::UnoType< css::awt::Point>::get(), + beans::PropertyAttribute::BOUND|beans::PropertyAttribute::READONLY|beans::PropertyAttribute::MAYBEVOID ); + // add new property to update current page's acc information + rProperties.emplace_back( "UpdateAcc", + PROPERTY_UPDATEACC, + ::cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); + rProperties.emplace_back( "PageChange", + PROPERTY_PAGE_CHANGE, + ::cppu::UnoType::get(), + beans::PropertyAttribute::BOUND ); +} + +IPropertyArrayHelper & DrawController::getInfoHelper() +{ + SolarMutexGuard aGuard; + + if (mpPropertyArrayHelper == nullptr) + { + ::std::vector aProperties; + FillPropertyTable(aProperties); + mpPropertyArrayHelper.reset(new OPropertyArrayHelper(comphelper::containerToSequence(aProperties), false)); + } + + return *mpPropertyArrayHelper; +} + +Reference < beans::XPropertySetInfo > DrawController::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + + static Reference < beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +uno::Reference< form::runtime::XFormController > SAL_CALL DrawController::getFormController( const uno::Reference< form::XForm >& Form ) +{ + SolarMutexGuard aGuard; + + FmFormShell* pFormShell = mpBase->GetFormShellManager()->GetFormShell(); + SdrView* pSdrView = mpBase->GetDrawView(); + std::shared_ptr pViewShell = mpBase->GetMainViewShell(); + ::sd::Window* pWindow = pViewShell ? pViewShell->GetActiveWindow() : nullptr; + + uno::Reference< form::runtime::XFormController > xController; + if ( pFormShell && pSdrView && pWindow ) + xController = FmFormShell::GetFormController( Form, *pSdrView, *pWindow->GetOutDev() ); + return xController; +} + +sal_Bool SAL_CALL DrawController::isFormDesignMode( ) +{ + SolarMutexGuard aGuard; + + bool bIsDesignMode = true; + + FmFormShell* pFormShell = mpBase->GetFormShellManager()->GetFormShell(); + if ( pFormShell ) + bIsDesignMode = pFormShell->IsDesignMode(); + + return bIsDesignMode; +} + +void SAL_CALL DrawController::setFormDesignMode( sal_Bool DesignMode ) +{ + SolarMutexGuard aGuard; + + FmFormShell* pFormShell = mpBase->GetFormShellManager()->GetFormShell(); + if ( pFormShell ) + pFormShell->SetDesignMode( DesignMode ); +} + +uno::Reference< awt::XControl > SAL_CALL DrawController::getControl( const uno::Reference< awt::XControlModel >& xModel ) +{ + SolarMutexGuard aGuard; + + FmFormShell* pFormShell = mpBase->GetFormShellManager()->GetFormShell(); + SdrView* pSdrView = mpBase->GetDrawView(); + std::shared_ptr pViewShell = mpBase->GetMainViewShell(); + ::sd::Window* pWindow = pViewShell ? pViewShell->GetActiveWindow() : nullptr; + + uno::Reference< awt::XControl > xControl; + if ( pFormShell && pSdrView && pWindow ) + pFormShell->GetFormControl( xModel, *pSdrView, *pWindow->GetOutDev(), xControl ); + return xControl; +} + +sal_Bool DrawController::convertFastPropertyValue ( + Any & rConvertedValue, + Any & rOldValue, + sal_Int32 nHandle, + const Any& rValue) +{ + bool bResult = false; + + if (nHandle == PROPERTY_SUB_CONTROLLER) + { + rOldValue <<= mxSubController; + rConvertedValue <<= Reference(rValue, UNO_QUERY); + bResult = (rOldValue != rConvertedValue); + } + else if (mxSubController.is()) + { + rConvertedValue = rValue; + try + { + rOldValue = mxSubController->getFastPropertyValue(nHandle); + bResult = (rOldValue != rConvertedValue); + } + catch (const beans::UnknownPropertyException&) + { + // The property is unknown and thus an illegal argument to this method. + throw css::lang::IllegalArgumentException(); + } + } + + return bResult; +} + +void DrawController::setFastPropertyValue_NoBroadcast ( + sal_Int32 nHandle, + const Any& rValue) +{ + SolarMutexGuard aGuard; + if (nHandle == PROPERTY_SUB_CONTROLLER) + SetSubController(Reference(rValue, UNO_QUERY)); + else if (mxSubController.is()) + mxSubController->setFastPropertyValue(nHandle, rValue); +} + +void DrawController::getFastPropertyValue ( + Any & rRet, + sal_Int32 nHandle ) const +{ + SolarMutexGuard aGuard; + + switch( nHandle ) + { + case PROPERTY_WORKAREA: + rRet <<= awt::Rectangle( + maLastVisArea.Left(), + maLastVisArea.Top(), + maLastVisArea.GetWidth(), + maLastVisArea.GetHeight()); + break; + + case PROPERTY_SUB_CONTROLLER: + rRet <<= mxSubController; + break; + + default: + if (mxSubController.is()) + rRet = mxSubController->getFastPropertyValue(nHandle); + break; + } +} + +void DrawController::ProvideFrameworkControllers() +{ + SolarMutexGuard aGuard; + try + { + Reference xController (this); + const Reference xContext ( + ::comphelper::getProcessComponentContext() ); + mxConfigurationController = ConfigurationController::create( + xContext, + xController); + mxModuleController = ModuleController::create( + xContext, + xController); + } + catch (const RuntimeException&) + { + mxConfigurationController = nullptr; + mxModuleController = nullptr; + } +} + +void DrawController::DisposeFrameworkControllers() +{ + Reference xComponent (mxModuleController, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); + + xComponent.set(mxConfigurationController, UNO_QUERY); + if (xComponent.is()) + xComponent->dispose(); +} + +void DrawController::ThrowIfDisposed() const +{ + if (rBHelper.bDisposed || rBHelper.bInDispose || mbDisposing) + { + SAL_WARN("sd", "Calling disposed DrawController object. Throwing exception:"); + throw lang::DisposedException ( + "DrawController object has already been disposed", + const_cast(static_cast(this))); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/SdUnoDrawView.cxx b/sd/source/ui/unoidl/SdUnoDrawView.cxx new file mode 100644 index 000000000..379a2956f --- /dev/null +++ b/sd/source/ui/unoidl/SdUnoDrawView.cxx @@ -0,0 +1,548 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include "unolayer.hxx" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; + +namespace sd { + +SdUnoDrawView::SdUnoDrawView( + DrawViewShell& rViewShell, + View& rView) noexcept + : DrawSubControllerInterfaceBase(m_aMutex), + mrDrawViewShell(rViewShell), + mrView(rView) +{ +} + +SdUnoDrawView::~SdUnoDrawView() noexcept +{ +} + +bool SdUnoDrawView::getMasterPageMode() const noexcept +{ + return (mrDrawViewShell.GetEditMode() == EditMode::MasterPage); +} + +void SdUnoDrawView::setMasterPageMode (bool bMasterPageMode) noexcept +{ + if ((mrDrawViewShell.GetEditMode() == EditMode::MasterPage) != bMasterPageMode) + { + mrDrawViewShell.ChangeEditMode ( + bMasterPageMode ? EditMode::MasterPage : EditMode::Page, + mrDrawViewShell.IsLayerModeActive()); + } +} + +bool SdUnoDrawView::getLayerMode() const noexcept +{ + return mrDrawViewShell.IsLayerModeActive(); +} + +void SdUnoDrawView::setLayerMode (bool bLayerMode) noexcept +{ + if (mrDrawViewShell.IsLayerModeActive() != bLayerMode) + { + mrDrawViewShell.ChangeEditMode ( + mrDrawViewShell.GetEditMode(), + bLayerMode); + } +} + +Reference SdUnoDrawView::getActiveLayer() const +{ + Reference xCurrentLayer; + + do + { + // Retrieve the layer manager from the model. + SdXImpressDocument* pModel = GetModel(); + if (pModel == nullptr) + break; + + SdDrawDocument* pSdModel = pModel->GetDoc(); + if (pSdModel == nullptr) + break; + + // From the model get the current SdrLayer object via the layer admin. + SdrLayerAdmin& rLayerAdmin = pSdModel->GetLayerAdmin (); + SdrLayer* pLayer = rLayerAdmin.GetLayer (mrView.GetActiveLayer()); + if (pLayer == nullptr) + break; + + // Get the corresponding XLayer object from the implementation + // object of the layer manager. + Reference xManager (pModel->getLayerManager(), uno::UNO_QUERY); + SdLayerManager* pManager = comphelper::getFromUnoTunnel (xManager); + if (pManager != nullptr) + xCurrentLayer = pManager->GetLayer (pLayer); + } + while (false); + + return xCurrentLayer; +} + +void SdUnoDrawView::setActiveLayer (const Reference& rxLayer) +{ + // Get the SdrLayer object corresponding to the given reference. + if ( ! rxLayer.is()) + return; + + SdLayer* pLayer = comphelper::getFromUnoTunnel (rxLayer); + if (pLayer == nullptr) + return; + + SdrLayer* pSdrLayer = pLayer->GetSdrLayer(); + if (pSdrLayer == nullptr) + return; + + // Set the new active layer and make the change visible. + mrView.SetActiveLayer (pSdrLayer->GetName()); + mrDrawViewShell.ResetActualLayer (); +} + +// XSelectionSupplier + +sal_Bool SAL_CALL SdUnoDrawView::select( const Any& aSelection ) +{ + bool bOk = true; + + ::std::vector aObjects; + + SdrPage* pSdrPage = nullptr; + + Reference< drawing::XShape > xShape; + aSelection >>= xShape; + + if(xShape.is()) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pObj ) + { + pSdrPage = pObj->getSdrPageFromSdrObject(); + aObjects.push_back( pObj ); + } + else + { + bOk = false; + } + } + else + { + Reference< drawing::XShapes > xShapes; + aSelection >>= xShapes; + if( xShapes.is() ) + { + const sal_uInt32 nCount = xShapes->getCount(); + for( sal_uInt32 i = 0; i < nCount; i++ ) + { + xShapes->getByIndex(i) >>= xShape; + if( xShape.is() ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(xShape); + if( !pObj ) + { + bOk = false; + break; + } + + if( pSdrPage == nullptr ) + { + pSdrPage = pObj->getSdrPageFromSdrObject(); + } + else if( pSdrPage != pObj->getSdrPageFromSdrObject() ) + { + bOk = false; + break; + } + + aObjects.push_back( pObj ); + } + } + } + } + + if( bOk ) + { + if( pSdrPage ) + { + setMasterPageMode( pSdrPage->IsMasterPage() ); + mrDrawViewShell.SwitchPage( (pSdrPage->GetPageNum() - 1) >> 1 ); + mrDrawViewShell.WriteFrameViewData(); + } + + SdrPageView *pPV = mrView.GetSdrPageView(); + + if(pPV) + { + // first deselect all + mrView.UnmarkAllObj( pPV ); + + for( SdrObject* pObj : aObjects ) + { + mrView.MarkObj( pObj, pPV ); + } + } + else + { + bOk = false; + } + } + + return bOk; +} + +Any SAL_CALL SdUnoDrawView::getSelection() +{ + Any aAny; + + if( mrView.IsTextEdit() ) + mrView.getTextSelection( aAny ); + + if( !aAny.hasValue() ) + { + const SdrMarkList& rMarkList = mrView.GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + if( nCount ) + { + Reference< drawing::XShapes > xShapes = drawing::ShapeCollection::create( + comphelper::getProcessComponentContext()); + for( size_t nNum = 0; nNum < nCount; ++nNum) + { + SdrMark *pMark = rMarkList.GetMark(nNum); + if(pMark==nullptr) + continue; + + SdrObject *pObj = pMark->GetMarkedSdrObj(); + if(pObj==nullptr || pObj->getSdrPageFromSdrObject() == nullptr) + continue; + + Reference< drawing::XDrawPage > xPage( pObj->getSdrPageFromSdrObject()->getUnoPage(), UNO_QUERY); + + if(!xPage.is()) + continue; + + SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel( xPage ); + + if(pDrawPage==nullptr) + continue; + + Reference< drawing::XShape > xShape( pObj->getUnoShape(), UNO_QUERY ); + + if(xShape.is()) + xShapes->add(xShape); + } + aAny <<= xShapes; + } + } + + return aAny; +} + +void SAL_CALL SdUnoDrawView::addSelectionChangeListener ( + const css::uno::Reference&) +{} + +void SAL_CALL SdUnoDrawView::removeSelectionChangeListener ( + const css::uno::Reference&) +{} + +void SdUnoDrawView::setFastPropertyValue ( + sal_Int32 nHandle, + const Any& rValue) +{ + switch( nHandle ) + { + case DrawController::PROPERTY_CURRENTPAGE: + { + Reference< drawing::XDrawPage > xPage; + rValue >>= xPage; + setCurrentPage( xPage ); + } + break; + + case DrawController::PROPERTY_MASTERPAGEMODE: + { + bool bValue = false; + rValue >>= bValue; + setMasterPageMode( bValue ); + } + break; + + case DrawController::PROPERTY_LAYERMODE: + { + bool bValue = false; + rValue >>= bValue; + setLayerMode( bValue ); + } + break; + case DrawController::PROPERTY_ACTIVE_LAYER: + { + Reference xLayer; + rValue >>= xLayer; + setActiveLayer (xLayer); + } + break; + case DrawController::PROPERTY_ZOOMVALUE: + { + sal_Int16 nZoom = 0; + rValue >>= nZoom; + SetZoom( nZoom ); + } + break; + case DrawController::PROPERTY_ZOOMTYPE: + { + sal_Int16 nType = 0; + rValue >>= nType; + SetZoomType( nType ); + } + break; + case DrawController::PROPERTY_VIEWOFFSET: + { + awt::Point aOffset; + rValue >>= aOffset; + SetViewOffset( aOffset ); + } + break; + default: + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast(this)); + } +} + +Any SAL_CALL SdUnoDrawView::getFastPropertyValue ( + sal_Int32 nHandle) +{ + Any aValue; + switch( nHandle ) + { + case DrawController::PROPERTY_CURRENTPAGE: + aValue <<= getCurrentPage(); + break; + + case DrawController::PROPERTY_MASTERPAGEMODE: + aValue <<= getMasterPageMode(); + break; + + case DrawController::PROPERTY_LAYERMODE: + aValue <<= getLayerMode(); + break; + + case DrawController::PROPERTY_ACTIVE_LAYER: + aValue <<= getActiveLayer(); + break; + + case DrawController::PROPERTY_ZOOMVALUE: + aValue <<= GetZoom(); + break; + case DrawController::PROPERTY_ZOOMTYPE: + aValue <<= sal_Int16(css::view::DocumentZoomType::BY_VALUE); + break; + case DrawController::PROPERTY_VIEWOFFSET: + aValue <<= GetViewOffset(); + break; + + case DrawController::PROPERTY_DRAWVIEWMODE: + aValue = getDrawViewMode(); + break; + + default: + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast(this)); + } + + return aValue; +} + +// XDrawView + +void SAL_CALL SdUnoDrawView::setCurrentPage ( + const Reference< drawing::XDrawPage >& xPage ) +{ + SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel( xPage ); + SdrPage *pSdrPage = pDrawPage ? pDrawPage->GetSdrPage() : nullptr; + + if(pSdrPage) + { + // End editing of text. Otherwise the edited text object would + // still be visible on the new page. + mrDrawViewShell.GetView()->SdrEndTextEdit(); + + setMasterPageMode( pSdrPage->IsMasterPage() ); + mrDrawViewShell.SwitchPage( (pSdrPage->GetPageNum() - 1) >> 1 ); + mrDrawViewShell.WriteFrameViewData(); + } +} + +Reference< drawing::XDrawPage > SAL_CALL SdUnoDrawView::getCurrentPage() +{ + Reference< drawing::XDrawPage > xPage; + + SdrPageView *pPV = mrView.GetSdrPageView(); + SdrPage* pPage = pPV ? pPV->GetPage() : nullptr; + + if(pPage) + xPage.set( pPage->getUnoPage(), UNO_QUERY ); + + return xPage; +} + +sal_Int16 SdUnoDrawView::GetZoom() const +{ + if (mrDrawViewShell.GetActiveWindow() ) + { + return static_cast(mrDrawViewShell.GetActiveWindow()->GetZoom()); + } + else + { + return 0; + } +} + +void SdUnoDrawView::SetZoom( sal_Int16 nZoom ) +{ + SvxZoomItem aZoomItem( SvxZoomType::PERCENT, nZoom ); + + SfxViewFrame* pViewFrame = mrDrawViewShell.GetViewFrame(); + if( pViewFrame ) + { + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if( pDispatcher ) + { + pDispatcher->ExecuteList(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, + { &aZoomItem }); + } + } +} + +void SdUnoDrawView::SetViewOffset(const awt::Point& rWinPos ) +{ + Point aWinPos( rWinPos.X, rWinPos.Y ); + aWinPos += mrDrawViewShell.GetViewOrigin(); + mrDrawViewShell.SetWinViewPos( aWinPos ); +} + +awt::Point SdUnoDrawView::GetViewOffset() const +{ + Point aRet = mrDrawViewShell.GetWinViewPos(); + aRet -= mrDrawViewShell.GetViewOrigin(); + + return awt::Point( aRet.X(), aRet.Y() ); +} + +void SdUnoDrawView::SetZoomType ( sal_Int16 nType ) +{ + SfxViewFrame* pViewFrame = mrDrawViewShell.GetViewFrame(); + if( !pViewFrame ) + return; + + SfxDispatcher* pDispatcher = pViewFrame->GetDispatcher(); + if( !pDispatcher ) + return; + + SvxZoomType eZoomType; + switch( nType ) + { + case css::view::DocumentZoomType::OPTIMAL: + eZoomType = SvxZoomType::OPTIMAL; + break; + + case css::view::DocumentZoomType::PAGE_WIDTH: + case css::view::DocumentZoomType::PAGE_WIDTH_EXACT: + eZoomType = SvxZoomType::PAGEWIDTH; + break; + + case css::view::DocumentZoomType::ENTIRE_PAGE: + eZoomType = SvxZoomType::WHOLEPAGE; + break; + + default: + return; + } + SvxZoomItem aZoomItem( eZoomType ); + pDispatcher->ExecuteList(SID_ATTR_ZOOM, SfxCallMode::SYNCHRON, + { &aZoomItem }); +} + +SdXImpressDocument* SdUnoDrawView::GetModel() const noexcept +{ + if (mrView.GetDocSh()!=nullptr) + { + Reference xModel (mrView.GetDocSh()->GetModel()); + return comphelper::getFromUnoTunnel(xModel); + } + else + return nullptr; +} + +Any SdUnoDrawView::getDrawViewMode() const +{ + Any aRet; + switch( mrDrawViewShell.GetPageKind() ) + { + case PageKind::Notes: aRet <<= DrawViewMode_NOTES; break; + case PageKind::Handout: aRet <<= DrawViewMode_HANDOUT; break; + case PageKind::Standard: aRet <<= DrawViewMode_DRAW; break; + } + return aRet; +} + +// XServiceInfo +OUString SAL_CALL SdUnoDrawView::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SdUnoDrawView" ; +} + +sal_Bool SAL_CALL SdUnoDrawView::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdUnoDrawView::getSupportedServiceNames( ) +{ + return { "com.sun.star.drawing.DrawingDocumentDrawView" }; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/SdUnoOutlineView.cxx b/sd/source/ui/unoidl/SdUnoOutlineView.cxx new file mode 100644 index 000000000..6b98f2140 --- /dev/null +++ b/sd/source/ui/unoidl/SdUnoOutlineView.cxx @@ -0,0 +1,156 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +SdUnoOutlineView::SdUnoOutlineView( + OutlineViewShell& rViewShell) noexcept + : DrawSubControllerInterfaceBase(m_aMutex), + mrOutlineViewShell(rViewShell) +{ +} + +SdUnoOutlineView::~SdUnoOutlineView() noexcept +{ +} + +void SAL_CALL SdUnoOutlineView::disposing() +{ +} + +//----- XSelectionSupplier ---------------------------------------------------- + +sal_Bool SAL_CALL SdUnoOutlineView::select( const Any& ) +{ + // todo: add selections for text ranges + return false; +} + +Any SAL_CALL SdUnoOutlineView::getSelection() +{ + Any aAny; + return aAny; +} + +void SAL_CALL SdUnoOutlineView::addSelectionChangeListener ( + const css::uno::Reference&) +{} + +void SAL_CALL SdUnoOutlineView::removeSelectionChangeListener ( + const css::uno::Reference&) +{} + +//----- XDrawView ------------------------------------------------------------- +void SAL_CALL SdUnoOutlineView::setCurrentPage ( + const Reference< drawing::XDrawPage >& xPage) +{ + SvxDrawPage* pDrawPage = comphelper::getFromUnoTunnel( xPage ); + SdrPage *pSdrPage = pDrawPage ? pDrawPage->GetSdrPage() : nullptr; + SdPage *pSdPage = dynamic_cast(pSdrPage); + + if (pSdPage != nullptr) + mrOutlineViewShell.SetCurrentPage(pSdPage); +} + +Reference< drawing::XDrawPage > SAL_CALL SdUnoOutlineView::getCurrentPage() +{ + Reference xPage; + + SdPage* pPage = mrOutlineViewShell.getCurrentPage(); + if (pPage != nullptr) + xPage.set(pPage->getUnoPage(), UNO_QUERY); + + return xPage; +} + +void SdUnoOutlineView::setFastPropertyValue ( + sal_Int32 nHandle, + const Any& rValue) +{ + switch( nHandle ) + { + case DrawController::PROPERTY_CURRENTPAGE: + { + Reference< drawing::XDrawPage > xPage; + rValue >>= xPage; + setCurrentPage( xPage ); + } + break; + + default: + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast(this)); + } +} + +Any SAL_CALL SdUnoOutlineView::getFastPropertyValue ( + sal_Int32 nHandle) +{ + Any aValue; + + switch( nHandle ) + { + case DrawController::PROPERTY_CURRENTPAGE: + { + SdPage* pPage = mrOutlineViewShell.GetActualPage(); + if (pPage != nullptr) + aValue <<= pPage->getUnoPage(); + } + break; + case DrawController::PROPERTY_VIEWOFFSET: + break; + + default: + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast(this)); + } + + return aValue; +} + +// XServiceInfo +OUString SAL_CALL SdUnoOutlineView::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SdUnoOutlineView"; +} + +sal_Bool SAL_CALL SdUnoOutlineView::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdUnoOutlineView::getSupportedServiceNames( ) +{ + return { "com.sun.star.presentation.OutlineView" }; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/SdUnoSlideView.cxx b/sd/source/ui/unoidl/SdUnoSlideView.cxx new file mode 100644 index 000000000..c30962ed7 --- /dev/null +++ b/sd/source/ui/unoidl/SdUnoSlideView.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +SdUnoSlideView::SdUnoSlideView ( + slidesorter::SlideSorter& rSlideSorter) noexcept + : DrawSubControllerInterfaceBase(m_aMutex), + mrSlideSorter(rSlideSorter) +{ +} + +SdUnoSlideView::~SdUnoSlideView() noexcept +{ +} + +//----- XSelectionSupplier ---------------------------------------------------- + +sal_Bool SAL_CALL SdUnoSlideView::select (const Any& aSelection) +{ + slidesorter::controller::SlideSorterController& rSlideSorterController + = mrSlideSorter.GetController(); + slidesorter::controller::PageSelector& rSelector (rSlideSorterController.GetPageSelector()); + rSelector.DeselectAllPages(); + Sequence > xPages; + aSelection >>= xPages; + for (const auto& rPage : std::as_const(xPages)) + { + Reference xSet (rPage, UNO_QUERY); + if (xSet.is()) + { + try + { + Any aNumber = xSet->getPropertyValue("Number"); + sal_Int32 nPageNumber = 0; + aNumber >>= nPageNumber; + nPageNumber -=1; // Transform 1-based page numbers to 0-based ones. + rSelector.SelectPage(nPageNumber); + } + catch (const RuntimeException&) + { + } + } + } + + return true; +} + +Any SAL_CALL SdUnoSlideView::getSelection() +{ + Any aResult; + + slidesorter::model::PageEnumeration aSelectedPages ( + slidesorter::model::PageEnumerationProvider::CreateSelectedPagesEnumeration( + mrSlideSorter.GetModel())); + int nSelectedPageCount ( + mrSlideSorter.GetController().GetPageSelector().GetSelectedPageCount()); + + Sequence > aPages(nSelectedPageCount); + auto aPagesRange = asNonConstRange(aPages); + int nIndex = 0; + while (aSelectedPages.HasMoreElements() && nIndexGetPage()->getUnoPage(); + } + aResult <<= aPages; + + return aResult; +} + +void SAL_CALL SdUnoSlideView::addSelectionChangeListener ( + const css::uno::Reference&) +{} + +void SAL_CALL SdUnoSlideView::removeSelectionChangeListener ( + const css::uno::Reference&) +{} + +//----- XDrawView ------------------------------------------------------------- + +void SAL_CALL SdUnoSlideView::setCurrentPage ( + const css::uno::Reference& rxDrawPage) +{ + Reference xProperties (rxDrawPage, UNO_QUERY); + if (xProperties.is()) + { + sal_uInt16 nPageNumber(0); + if (xProperties->getPropertyValue("Number") >>= nPageNumber) + { + mrSlideSorter.GetController().GetCurrentSlideManager()->SwitchCurrentSlide( + nPageNumber-1); + } + } +} + +css::uno::Reference SAL_CALL + SdUnoSlideView::getCurrentPage() +{ + return mrSlideSorter.GetController().GetCurrentSlideManager()->GetCurrentSlide()->GetXDrawPage(); +} + +//----- XFastPropertySet ------------------------------------------------------ + +void SdUnoSlideView::setFastPropertyValue ( + sal_Int32 nHandle, + const Any&) +{ + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast(this)); +} + +Any SAL_CALL SdUnoSlideView::getFastPropertyValue ( + sal_Int32 nHandle) +{ + if( nHandle != DrawController::PROPERTY_VIEWOFFSET ) + throw beans::UnknownPropertyException( OUString::number(nHandle), static_cast(this)); + + return Any(); +} + +// XServiceInfo +OUString SAL_CALL SdUnoSlideView::getImplementationName( ) +{ + return "com.sun.star.comp.sd.SdUnoSlideView"; +} + +sal_Bool SAL_CALL SdUnoSlideView::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdUnoSlideView::getSupportedServiceNames( ) +{ + return { "com.sun.star.presentation.SlidesView" }; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/UnoDocumentSettings.cxx b/sd/source/ui/unoidl/UnoDocumentSettings.cxx new file mode 100644 index 000000000..331f90b53 --- /dev/null +++ b/sd/source/ui/unoidl/UnoDocumentSettings.cxx @@ -0,0 +1,1431 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "UnoDocumentSettings.hxx" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::comphelper; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::document; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::i18n; + +namespace sd +{ + namespace { + + class DocumentSettings : public WeakImplHelper< XPropertySet, XMultiPropertySet, XServiceInfo >, + public comphelper::PropertySetHelper, + public DocumentSettingsSerializer + { + public: + explicit DocumentSettings( SdXImpressDocument* pModel ); + + // XInterface + virtual Any SAL_CALL queryInterface( const Type& aType ) override; + virtual void SAL_CALL acquire( ) noexcept override; + virtual void SAL_CALL release( ) noexcept override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XMultiPropertySet + virtual void SAL_CALL setPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Sequence< css::uno::Any >& aValues ) override; + virtual css::uno::Sequence< css::uno::Any > SAL_CALL getPropertyValues( const css::uno::Sequence< OUString >& aPropertyNames ) override; + virtual void SAL_CALL addPropertiesChangeListener( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertiesChangeListener( const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + virtual void SAL_CALL firePropertiesChangeEvent( const css::uno::Sequence< OUString >& aPropertyNames, const css::uno::Reference< css::beans::XPropertiesChangeListener >& xListener ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // DocumentSettingsSerializer cf. xmloff + virtual uno::Sequence + filterStreamsFromStorage(OUString const & referer, + const uno::Reference< embed::XStorage > &xStorage, + const uno::Sequence& aConfigProps ) override; + virtual uno::Sequence + filterStreamsToStorage(const uno::Reference< embed::XStorage > &xStorage, + const uno::Sequence& aConfigProps ) override; + + protected: + virtual void _setPropertyValues( const comphelper::PropertyMapEntry** ppEntries, const css::uno::Any* pValues ) override; + virtual void _getPropertyValues( const comphelper::PropertyMapEntry** ppEntries, css::uno::Any* pValue ) override; + + private: + bool LoadList( XPropertyListType t, const OUString &rPath, + const OUString &rReferer, + const uno::Reference< embed::XStorage > &xStorage ); + void AssignURL( XPropertyListType t, const Any* pValue, bool *pOk, bool *pChanged ); + void ExtractURL( XPropertyListType t, Any* pValue ); + rtl::Reference mxModel; + }; + + } + + Reference< XInterface > DocumentSettings_createInstance( SdXImpressDocument* pModel ) + noexcept + { + DBG_ASSERT( pModel, "I need a model for the DocumentSettings!" ); + return static_cast(new DocumentSettings( pModel )); + } + +namespace { + +enum SdDocumentSettingsPropertyHandles +{ + HANDLE_PRINTDRAWING, HANDLE_PRINTNOTES, HANDLE_PRINTHANDOUT, HANDLE_PRINTOUTLINE, HANDLE_MEASUREUNIT, HANDLE_SCALE_NUM, + HANDLE_SCALE_DOM, HANDLE_TABSTOP, HANDLE_PRINTPAGENAME, HANDLE_PRINTDATE, HANDLE_PRINTTIME, + HANDLE_PRINTHIDDENPAGES, HANDLE_PRINTFITPAGE, HANDLE_PRINTTILEPAGE, HANDLE_PRINTBOOKLET, HANDLE_PRINTBOOKLETFRONT, + HANDLE_PRINTBOOKLETBACK, HANDLE_PRINTQUALITY, HANDLE_COLORTABLEURL, HANDLE_DASHTABLEURL, HANDLE_LINEENDTABLEURL, HANDLE_HATCHTABLEURL, + HANDLE_GRADIENTTABLEURL, HANDLE_BITMAPTABLEURL, HANDLE_FORBIDDENCHARS, HANDLE_APPLYUSERDATA, HANDLE_SAVETHUMBNAIL, HANDLE_PAGENUMFMT, + HANDLE_PRINTERNAME, HANDLE_PRINTERJOB, HANDLE_PRINTERPAPERSIZE, HANDLE_PARAGRAPHSUMMATION, HANDLE_CHARCOMPRESS, HANDLE_ASIANPUNCT, + HANDLE_UPDATEFROMTEMPLATE, HANDLE_PRINTER_INDEPENDENT_LAYOUT + // #i33095# + ,HANDLE_LOAD_READONLY, HANDLE_MODIFY_PASSWD, HANDLE_SAVE_VERSION + ,HANDLE_SLIDESPERHANDOUT, HANDLE_HANDOUTHORIZONTAL, + HANDLE_EMBED_FONTS, HANDLE_EMBED_USED_FONTS, + HANDLE_EMBED_LATIN_SCRIPT_FONTS, HANDLE_EMBED_ASIAN_SCRIPT_FONTS, HANDLE_EMBED_COMPLEX_SCRIPT_FONTS, + HANDLE_IMAGE_PREFERRED_DPI +}; + +} + +#define MID_PRINTER 1 + + static rtl::Reference createSettingsInfoImpl( bool bIsDraw ) + { + static PropertyMapEntry const aImpressSettingsInfoMap[] = + { + { OUString("IsPrintDrawing"), HANDLE_PRINTDRAWING, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintNotes"), HANDLE_PRINTNOTES, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintHandout"), HANDLE_PRINTHANDOUT, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintOutline"), HANDLE_PRINTOUTLINE, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("SlidesPerHandout"), HANDLE_SLIDESPERHANDOUT, ::cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("HandoutsHorizontal"), HANDLE_HANDOUTHORIZONTAL, cppu::UnoType::get(), 0, MID_PRINTER }, + }; + + static PropertyMapEntry const aDrawSettingsInfoMap[] = + { + { OUString("MeasureUnit"), HANDLE_MEASUREUNIT, ::cppu::UnoType::get(), 0, 0 }, + { OUString("ScaleNumerator"), HANDLE_SCALE_NUM, ::cppu::UnoType::get(), 0, 0 }, + { OUString("ScaleDenominator"), HANDLE_SCALE_DOM, ::cppu::UnoType::get(), 0, 0 }, + }; + + static PropertyMapEntry const aCommonSettingsInfoMap[] = + { + { OUString("DefaultTabStop"), HANDLE_TABSTOP, ::cppu::UnoType::get(), 0, 0 }, + { OUString("PrinterName"), HANDLE_PRINTERNAME, ::cppu::UnoType::get(), 0, 0 }, + { OUString("PrinterSetup"), HANDLE_PRINTERJOB, cppu::UnoType>::get(), 0, MID_PRINTER }, + { OUString("PrinterPaperFromSetup"), HANDLE_PRINTERPAPERSIZE, cppu::UnoType::get(), 0, MID_PRINTER }, + + { OUString("IsPrintPageName"), HANDLE_PRINTPAGENAME, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintDate"), HANDLE_PRINTDATE, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintTime"), HANDLE_PRINTTIME, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintHiddenPages"), HANDLE_PRINTHIDDENPAGES, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintFitPage"), HANDLE_PRINTFITPAGE, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintTilePage"), HANDLE_PRINTTILEPAGE, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintBooklet"), HANDLE_PRINTBOOKLET, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintBookletFront"), HANDLE_PRINTBOOKLETFRONT, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("IsPrintBookletBack"), HANDLE_PRINTBOOKLETBACK, cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("PrintQuality"), HANDLE_PRINTQUALITY, ::cppu::UnoType::get(), 0, MID_PRINTER }, + { OUString("ColorTableURL"), HANDLE_COLORTABLEURL, ::cppu::UnoType::get(), 0, 0 }, + { OUString("DashTableURL"), HANDLE_DASHTABLEURL, ::cppu::UnoType::get(), 0, 0 }, + { OUString("LineEndTableURL"), HANDLE_LINEENDTABLEURL, ::cppu::UnoType::get(), 0, 0 }, + { OUString("HatchTableURL"), HANDLE_HATCHTABLEURL, ::cppu::UnoType::get(), 0, 0 }, + { OUString("GradientTableURL"), HANDLE_GRADIENTTABLEURL, ::cppu::UnoType::get(), 0, 0 }, + { OUString("BitmapTableURL"), HANDLE_BITMAPTABLEURL, ::cppu::UnoType::get(), 0, 0 }, + + { OUString("ForbiddenCharacters"), HANDLE_FORBIDDENCHARS, cppu::UnoType::get(), 0, 0 }, + { OUString("ApplyUserData"), HANDLE_APPLYUSERDATA, cppu::UnoType::get(), 0, 0 }, + { OUString("SaveThumbnail"), HANDLE_SAVETHUMBNAIL, cppu::UnoType::get(), 0, 0 }, + + { OUString("PageNumberFormat"), HANDLE_PAGENUMFMT, ::cppu::UnoType::get(), 0, 0 }, + { OUString("ParagraphSummation"), HANDLE_PARAGRAPHSUMMATION, cppu::UnoType::get(), 0, 0 }, + { OUString("CharacterCompressionType"),HANDLE_CHARCOMPRESS, ::cppu::UnoType::get(), 0, 0 }, + { OUString("IsKernAsianPunctuation"),HANDLE_ASIANPUNCT, cppu::UnoType::get(), 0, 0 }, + { OUString("UpdateFromTemplate"), HANDLE_UPDATEFROMTEMPLATE, cppu::UnoType::get(), 0, 0 }, + { OUString("PrinterIndependentLayout"),HANDLE_PRINTER_INDEPENDENT_LAYOUT,::cppu::UnoType::get(), 0, 0 }, + // --> #i33095# + { OUString("LoadReadonly"), HANDLE_LOAD_READONLY, cppu::UnoType::get(), 0, 0 }, + { OUString("ModifyPasswordInfo"), HANDLE_MODIFY_PASSWD, cppu::UnoType>::get(), 0, 0 }, + { OUString("SaveVersionOnClose"), HANDLE_SAVE_VERSION, cppu::UnoType::get(), 0, 0 }, + { OUString("EmbedFonts"), HANDLE_EMBED_FONTS, cppu::UnoType::get(), 0, 0 }, + { OUString("EmbedOnlyUsedFonts"), HANDLE_EMBED_USED_FONTS, cppu::UnoType::get(), 0, 0 }, + { OUString("EmbedLatinScriptFonts"), HANDLE_EMBED_LATIN_SCRIPT_FONTS, cppu::UnoType::get(), 0, 0 }, + { OUString("EmbedAsianScriptFonts"), HANDLE_EMBED_ASIAN_SCRIPT_FONTS, cppu::UnoType::get(), 0, 0 }, + { OUString("EmbedComplexScriptFonts"), HANDLE_EMBED_COMPLEX_SCRIPT_FONTS, cppu::UnoType::get(), 0, 0 }, + { OUString("ImagePreferredDPI"), HANDLE_IMAGE_PREFERRED_DPI, cppu::UnoType::get(), 0, 0 }, + }; + + rtl::Reference xInfo = new PropertySetInfo( aCommonSettingsInfoMap ); + if (bIsDraw) + xInfo->add( aDrawSettingsInfoMap ); + else + xInfo->add( aImpressSettingsInfoMap ); + + return xInfo; + } +} + +using namespace ::sd; + +DocumentSettings::DocumentSettings( SdXImpressDocument* pModel ) +: PropertySetHelper( createSettingsInfoImpl( !pModel->IsImpressDocument() ) ), + mxModel( pModel ) +{ +} + +bool DocumentSettings::LoadList( XPropertyListType t, const OUString &rInPath, + const OUString &rReferer, + const uno::Reference< embed::XStorage > &xStorage ) +{ + SdDrawDocument* pDoc = mxModel->GetDoc(); + + sal_Int32 nSlash = rInPath.lastIndexOf('/'); + OUString aPath, aName; + if (nSlash < 0) + aName = rInPath; + else { + aName = rInPath.copy( nSlash + 1 ); + aPath = rInPath.copy( 0, nSlash ); + } + + XPropertyListRef pList = XPropertyList::CreatePropertyList( + t, aPath, rReferer ); + pList->SetName( aName ); + + if( pList->LoadFrom( xStorage, rInPath, rReferer ) ) + { + pDoc->SetPropertyList( pList ); + return true; + } + + return false; +} + +void DocumentSettings::AssignURL( XPropertyListType t, const Any* pValue, + bool *pOk, bool *pChanged ) +{ + OUString aURL; + if( !( *pValue >>= aURL ) ) + return; + + if( LoadList( t, aURL, ""/*TODO?*/, uno::Reference< embed::XStorage >() ) ) + *pOk = *pChanged = true; +} + +struct { + const char *pName; + XPropertyListType t; +} const aURLPropertyNames[] = { + { "ColorTableURL", XPropertyListType::Color }, + { "DashTableURL", XPropertyListType::Dash }, + { "LineEndTableURL", XPropertyListType::LineEnd }, + { "HatchTableURL", XPropertyListType::Hatch }, + { "GradientTableURL", XPropertyListType::Gradient }, + { "BitmapTableURL", XPropertyListType::Bitmap } +}; + +static XPropertyListType getTypeOfName( std::u16string_view aName ) +{ + for(const auto & rURLPropertyName : aURLPropertyNames) { + if( o3tl::equalsAscii( aName, rURLPropertyName.pName ) ) + return rURLPropertyName.t; + } + return XPropertyListType::Unknown; +} + +static OUString getNameOfType( XPropertyListType t ) +{ + for(const auto & rURLPropertyName : aURLPropertyNames) { + if( t == rURLPropertyName.t ) + return OUString( rURLPropertyName.pName, + strlen( rURLPropertyName.pName ) - 3, + RTL_TEXTENCODING_ASCII_US ); + } + return OUString(); +} + +uno::Sequence + DocumentSettings::filterStreamsFromStorage( + OUString const & referer, + const uno::Reference< embed::XStorage > &xStorage, + const uno::Sequence& aConfigProps ) +{ + uno::Sequence aRet( aConfigProps.getLength() ); + auto aRetRange = asNonConstRange(aRet); + int nRet = 0; + for( const auto& rConfigProp : aConfigProps ) + { + XPropertyListType t = getTypeOfName( rConfigProp.Name ); + if (t == XPropertyListType::Unknown) + aRetRange[nRet++] = rConfigProp; + else + { + OUString aURL; + rConfigProp.Value >>= aURL; + LoadList( t, aURL, referer, xStorage ); + } + } + aRet.realloc( nRet ); + return aRet; +} + +uno::Sequence + DocumentSettings::filterStreamsToStorage( + const uno::Reference< embed::XStorage > &xStorage, + const uno::Sequence& aConfigProps ) +{ + uno::Sequence aRet( aConfigProps.getLength() ); + + bool bHasEmbed = false; + SdDrawDocument* pDoc = mxModel->GetDoc(); + for( size_t i = 0; i < SAL_N_ELEMENTS( aURLPropertyNames ); i++ ) + { + const XPropertyListRef& pList = pDoc->GetPropertyList( static_cast(i) ); + bHasEmbed = pList.is() && pList->IsEmbedInDocument(); + if( bHasEmbed ) + break; + } + if( !bHasEmbed ) + return aConfigProps; + + try { + // create Settings/ sub storage. + uno::Reference< embed::XStorage > xSubStorage = xStorage->openStorageElement( "Settings" , + embed::ElementModes::WRITE | embed::ElementModes::TRUNCATE ); + if( !xSubStorage.is() ) + return aRet; + + auto aRetRange = asNonConstRange(aRet); + // now populate it + for( sal_Int32 i = 0; i < aConfigProps.getLength(); i++ ) + { + XPropertyListType t = getTypeOfName( aConfigProps[i].Name ); + aRetRange[i] = aConfigProps[i]; + if (t != XPropertyListType::Unknown) { + const XPropertyListRef& pList = pDoc->GetPropertyList( t ); + if( !pList.is() || !pList->IsEmbedInDocument() ) + continue; // no change ... + else + { + // Such specific path construction is grim. + + OUString aName( getNameOfType( t ) ); + OUString aResult; + if( pList->SaveTo( xSubStorage, aName, &aResult ) ) + { + OUString aRealPath = "Settings/" + aResult; + aRetRange[i].Value <<= aRealPath; + } + } + } + } + + // surprisingly difficult to make it really exist + uno::Reference< embed::XTransactedObject > xTrans( xSubStorage, UNO_QUERY ); + if( xTrans.is() ) + xTrans->commit(); + if( xSubStorage.is() ) + xSubStorage->dispose(); + } catch (const uno::Exception &) { +// fprintf (stderr, "saving etc. exception '%s'\n", +// OUStringToOString(e.Message, RTL_TEXTENCODING_UTF8).getStr()); + } + + return aRet; +} + +// Most of the code reading/writing UNO document settings is the same in +// sd, sc and sw and it is mostly copy-pasted back and forth. +// TODO: Move _setPropertyValues and _getPropertyValues to some shared +// place, at least for the settings that are common to sd, sc and sw +void +DocumentSettings::_setPropertyValues(const PropertyMapEntry** ppEntries, + const Any* pValues) +{ + ::SolarMutexGuard aGuard; + + SdDrawDocument* pDoc = mxModel->GetDoc(); + ::sd::DrawDocShell* pDocSh = mxModel->GetDocShell(); + if( nullptr == pDoc || nullptr == pDocSh ) + { + throw RuntimeException("Document or Shell missing", + static_cast(this)); + } + + bool bValue = false; + bool bOk, bChanged = false, bOptionsChanged = false; + + SdOptionsPrintItem aOptionsPrintItem; + + VclPtr pPrinter = pDocSh->GetPrinter( false ); + if( pPrinter ) + { + SdOptionsPrintItem const * pPrinterOptions = pPrinter->GetOptions().GetItemIfSet( ATTR_OPTIONS_PRINT, false ); + if(pPrinterOptions) + aOptionsPrintItem.GetOptionsPrint() = pPrinterOptions->GetOptionsPrint(); + } + else + { + aOptionsPrintItem.SetOptions( SD_MOD()->GetSdOptions(pDoc->GetDocumentType()) ); + } + SdOptionsPrint& aPrintOpts = aOptionsPrintItem.GetOptionsPrint(); + + for( ; *ppEntries; ppEntries++, pValues++ ) + { + bOk = false; + + switch( (*ppEntries)->mnHandle ) + { + case HANDLE_COLORTABLEURL: + AssignURL( XPropertyListType::Color, pValues, &bOk, &bChanged ); + break; + + case HANDLE_DASHTABLEURL: + AssignURL( XPropertyListType::Dash, pValues, &bOk, &bChanged ); + break; + + case HANDLE_LINEENDTABLEURL: + AssignURL( XPropertyListType::LineEnd, pValues, &bOk, &bChanged ); + break; + + case HANDLE_HATCHTABLEURL: + AssignURL( XPropertyListType::Hatch, pValues, &bOk, &bChanged ); + break; + + case HANDLE_GRADIENTTABLEURL: + AssignURL( XPropertyListType::Gradient, pValues, &bOk, &bChanged ); + break; + + case HANDLE_BITMAPTABLEURL: + AssignURL( XPropertyListType::Bitmap, pValues, &bOk, &bChanged ); + break; + + case HANDLE_FORBIDDENCHARS: + bOk = true; + break; + + case HANDLE_APPLYUSERDATA: + { + bool bApplyUserData = false; + if( *pValues >>= bApplyUserData ) + { + bChanged = ( bApplyUserData != pDocSh->IsUseUserData() ); + pDocSh->SetUseUserData( bApplyUserData ); + bOk = true; + } + } + break; + case HANDLE_SAVETHUMBNAIL: + { + bool bSaveThumbnail = false; + if (*pValues >>= bSaveThumbnail) + { + bChanged = (bSaveThumbnail != pDocSh->IsUseThumbnailSave()); + pDocSh->SetUseThumbnailSave(bSaveThumbnail); + bOk = true; + } + } + break; + + case HANDLE_PRINTDRAWING: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsDraw() != bValue ) + { + aPrintOpts.SetDraw( bValue ); + bOptionsChanged = true; + } + + bOk = true; + } + break; + case HANDLE_PRINTNOTES: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsNotes() != bValue ) + { + aPrintOpts.SetNotes( bValue ); + bOptionsChanged = true; + } + + bOk = true; + } + break; + case HANDLE_PRINTHANDOUT: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsHandout() != bValue) + { + aPrintOpts.SetHandout( bValue ); + bOptionsChanged = true; + } + + bOk = true; + } + break; + case HANDLE_PRINTOUTLINE: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsOutline() != bValue) + { + aPrintOpts.SetOutline( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_SLIDESPERHANDOUT: + { + sal_Int16 nValue = 0; + if( (*pValues >>= nValue) && (nValue >= 1) && (nValue <= 9) ) + { + if( static_cast( aPrintOpts.GetHandoutPages() ) != nValue ) + { + aPrintOpts.SetHandoutPages( static_cast< sal_uInt16 >( nValue ) ); + bOptionsChanged = true; + } + bOk = true; + } + } + break; + case HANDLE_HANDOUTHORIZONTAL: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsHandoutHorizontal() != bValue ) + { + aPrintOpts.SetHandoutHorizontal( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + + case HANDLE_PRINTPAGENAME: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsPagename() != bValue) + { + aPrintOpts.SetPagename( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTDATE: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsDate() != bValue) + { + aPrintOpts.SetDate( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTTIME: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsDate() != bValue) + { + aPrintOpts.SetTime( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTHIDDENPAGES: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsHiddenPages() != bValue) + { + aPrintOpts.SetHiddenPages( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTFITPAGE: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsPagesize() != bValue) + { + aPrintOpts.SetPagesize( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTTILEPAGE: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsPagetile() != bValue) + { + aPrintOpts.SetPagetile( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTBOOKLET: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsBooklet() != bValue) + { + aPrintOpts.SetBooklet( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTBOOKLETFRONT: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsFrontPage() != bValue) + { + aPrintOpts.SetFrontPage( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTBOOKLETBACK: + if( *pValues >>= bValue ) + { + if( aPrintOpts.IsBackPage() != bValue) + { + aPrintOpts.SetBackPage( bValue ); + bOptionsChanged = true; + } + bOk = true; + } + break; + case HANDLE_PRINTQUALITY: + { + sal_Int32 nValue = 0; + if( *pValues >>= nValue ) + { + if( aPrintOpts.GetOutputQuality() != nValue) + { + aPrintOpts.SetOutputQuality( static_cast(nValue) ); + bOptionsChanged = true; + } + bOk = true; + } + } + break; + case HANDLE_MEASUREUNIT: + { + sal_Int16 nValue = 0; + if( *pValues >>= nValue ) + { + FieldUnit nFieldUnit; + if( SvxMeasureUnitToFieldUnit( nValue, nFieldUnit ) ) + { + pDoc->SetUIUnit( nFieldUnit ); + bOk = true; + } + } + } + break; + case HANDLE_SCALE_NUM: + { + sal_Int32 nValue = 0; + if( *pValues >>= nValue ) + { + Fraction aFract( nValue, pDoc->GetUIScale().GetDenominator() ); + pDoc->SetUIScale( aFract ); + bOk = true; + bChanged = true; + } + } + break; + case HANDLE_SCALE_DOM: + { + sal_Int32 nValue = 0; + if( *pValues >>= nValue ) + { + auto nNumerator = pDoc->GetUIScale().GetNumerator(); + assert(nNumerator != 0); + Fraction aFract(nNumerator, nValue); + pDoc->SetUIScale( aFract ); + bOk = true; + bChanged = true; + } + } + break; + + case HANDLE_TABSTOP: + { + sal_Int32 nValue = 0; + if( (*pValues >>= nValue) && (nValue >= 0) ) + { + pDoc->SetDefaultTabulator(static_cast(nValue)); + bOk = true; + bChanged = true; + } + } + break; + case HANDLE_PAGENUMFMT: + { + sal_Int32 nValue = 0; + if( (*pValues >>= nValue ) && (nValue >= css::style::NumberingType::CHARS_UPPER_LETTER ) && (nValue <= css::style::NumberingType::PAGE_DESCRIPTOR) ) + { + pDoc->SetPageNumType(static_cast(nValue)); + bOk = true; + bChanged = true; + } + } + break; + case HANDLE_PRINTERNAME: + { + OUString aPrinterName; + if( *pValues >>= aPrinterName ) + { + bOk = true; + if( !aPrinterName.isEmpty() && pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( true ); + if (pTempPrinter) + { + VclPtr pNewPrinter = VclPtr::Create( pTempPrinter->GetOptions().Clone(), aPrinterName ); + pDocSh->SetPrinter( pNewPrinter ); + } + } + } + } + break; + case HANDLE_PRINTERJOB: + { + Sequence < sal_Int8 > aSequence; + if ( *pValues >>= aSequence ) + { + bOk = true; + sal_uInt32 nSize = aSequence.getLength(); + if( nSize ) + { + SvMemoryStream aStream (aSequence.getArray(), nSize, StreamMode::READ ); + aStream.Seek ( STREAM_SEEK_TO_BEGIN ); + std::unique_ptr pItemSet; + + bool bPreferPrinterPapersize = false; + if( pPrinter ) + { + pItemSet = pPrinter->GetOptions().Clone(); + bPreferPrinterPapersize = pPrinter->GetPrinterSettingsPreferred(); + } + else + { + pItemSet = std::make_unique>(pDoc->GetPool()); + } + + pPrinter = SfxPrinter::Create ( aStream, std::move(pItemSet) ); + pPrinter->SetPrinterSettingsPreferred( bPreferPrinterPapersize ); + + MapMode aMM (pPrinter->GetMapMode()); + aMM.SetMapUnit(MapUnit::Map100thMM); + pPrinter->SetMapMode(aMM); + + pDocSh->SetPrinter( pPrinter ); + + pPrinter = nullptr; + } + } + } + break; + + case HANDLE_PRINTERPAPERSIZE: + { + bool bPreferPrinterPapersize; + if( *pValues >>= bPreferPrinterPapersize ) + { + bOk = true; + if( pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED ) + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( true ); + if (pTempPrinter) + pTempPrinter->SetPrinterSettingsPreferred( bPreferPrinterPapersize ); + } + } + } + break; + + case HANDLE_PARAGRAPHSUMMATION : + { + bool bIsSummationOfParagraphs = false; + if ( *pValues >>= bIsSummationOfParagraphs ) + { + bOk = true; + bChanged = true; + if ( pDoc->GetDocumentType() == DocumentType::Impress ) + { + EEControlBits nSum = bIsSummationOfParagraphs ? EEControlBits::ULSPACESUMMATION : EEControlBits::NONE; + EEControlBits nCntrl; + + pDoc->SetSummationOfParagraphs( bIsSummationOfParagraphs ); + SdDrawDocument* pDocument = pDocSh->GetDoc(); + SdrOutliner& rOutl = pDocument->GetDrawOutliner(); + nCntrl = rOutl.GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + rOutl.SetControlWord( nCntrl | nSum ); + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + { + nCntrl = pOutl->GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + pOutl->SetControlWord( nCntrl | nSum ); + } + pOutl = pDocument->GetInternalOutliner( false ); + if( pOutl ) + { + nCntrl = pOutl->GetControlWord() &~ EEControlBits::ULSPACESUMMATION; + pOutl->SetControlWord( nCntrl | nSum ); + } + } + } + } + break; + + case HANDLE_CHARCOMPRESS: + { + sal_Int16 nCharCompressType = 0; + if( *pValues >>= nCharCompressType ) + { + bOk = true; + + pDoc->SetCharCompressType( static_cast(nCharCompressType) ); + SdDrawDocument* pDocument = pDocSh->GetDoc(); + SdrOutliner& rOutl = pDocument->GetDrawOutliner(); + rOutl.SetAsianCompressionMode( static_cast(nCharCompressType) ); + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + { + pOutl->SetAsianCompressionMode( static_cast(nCharCompressType) ); + } + pOutl = pDocument->GetInternalOutliner( false ); + if( pOutl ) + { + pOutl->SetAsianCompressionMode( static_cast(nCharCompressType) ); + } + } + break; + + } + case HANDLE_ASIANPUNCT: + { + bool bAsianPunct = false; + if( *pValues >>= bAsianPunct ) + { + bOk = true; + + pDoc->SetKernAsianPunctuation( bAsianPunct ); + SdDrawDocument* pDocument = pDocSh->GetDoc(); + SdrOutliner& rOutl = pDocument->GetDrawOutliner(); + rOutl.SetKernAsianPunctuation( bAsianPunct ); + SdOutliner* pOutl = pDocument->GetOutliner( false ); + if( pOutl ) + { + pOutl->SetKernAsianPunctuation( bAsianPunct ); + } + pOutl = pDocument->GetInternalOutliner( false ); + if( pOutl ) + { + pOutl->SetKernAsianPunctuation( bAsianPunct ); + } + } + break; + + } + case HANDLE_UPDATEFROMTEMPLATE: + { + bool value = false; + if( *pValues >>= value ) + { + bChanged = ( value != pDocSh->IsQueryLoadTemplate() ); + pDocSh->SetQueryLoadTemplate( value ); + bOk = true; + } + } + break; + + case HANDLE_PRINTER_INDEPENDENT_LAYOUT: + { + // Just propagate the new printer independent layout mode to + // the document and determine it really differs from the old + // one. + sal_Int16 nOldValue = + static_cast(pDoc->GetPrinterIndependentLayout ()); + sal_Int16 nValue = 0; + if (*pValues >>= nValue) + { + pDoc->SetPrinterIndependentLayout (nValue); + bChanged = (nValue != nOldValue); + bOk = true; + } + } + break; + + // --> #i33095# + case HANDLE_LOAD_READONLY: + { + bool bNewValue = false; + if ( *pValues >>= bNewValue ) + { + bChanged = ( pDocSh->IsLoadReadonly() != bNewValue ); + pDocSh->SetLoadReadonly( bNewValue ); + bOk = true; + } + } + break; + + case HANDLE_MODIFY_PASSWD: + { + uno::Sequence< beans::PropertyValue > aInfo; + if ( !( *pValues >>= aInfo ) ) + throw lang::IllegalArgumentException( + "Value of type Sequence expected!", + uno::Reference< uno::XInterface >(), + 2 ); + + if ( !pDocSh->SetModifyPasswordInfo( aInfo ) ) + throw beans::PropertyVetoException( + "The hash is not allowed to be changed now!" ); + + bOk = true +; + + } + break; + + case HANDLE_SAVE_VERSION: + { + bool bNewValue = false; + if ( *pValues >>= bNewValue ) + { + bChanged = ( pDocSh->IsSaveVersionOnClose() != bNewValue ); + pDocSh->SetSaveVersionOnClose( bNewValue ); + bOk = true; + } + } + break; + + case HANDLE_EMBED_FONTS: + { + if (pValues->has()) + { + bool bNewValue = pValues->get(); + bChanged = (pDoc->IsEmbedFonts() != bNewValue); + pDoc->SetEmbedFonts(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_EMBED_USED_FONTS: + { + if (pValues->has()) + { + bool bNewValue = pValues->get(); + bChanged = (pDoc->IsEmbedUsedFontsOnly() != bNewValue); + pDoc->SetEmbedUsedFontsOnly(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_EMBED_LATIN_SCRIPT_FONTS: + { + if (pValues->has()) + { + bool bNewValue = pValues->get(); + bChanged = (pDoc->IsEmbedFontScriptLatin() != bNewValue); + pDoc->SetEmbedFontScriptLatin(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_EMBED_ASIAN_SCRIPT_FONTS: + { + if (pValues->has()) + { + bool bNewValue = pValues->get(); + bChanged = (pDoc->IsEmbedFontScriptAsian() != bNewValue); + pDoc->SetEmbedFontScriptAsian(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_EMBED_COMPLEX_SCRIPT_FONTS: + { + if (pValues->has()) + { + bool bNewValue = pValues->get(); + bChanged = (pDoc->IsEmbedFontScriptComplex() != bNewValue); + pDoc->SetEmbedFontScriptComplex(bNewValue); + bOk = true; + } + } + break; + + case HANDLE_IMAGE_PREFERRED_DPI: + { + if (pValues->has()) + { + auto nNewValue = pValues->get(); + bChanged = (pDoc->getImagePreferredDPI() != nNewValue); + pDoc->setImagePreferredDPI(nNewValue); + bOk = true; + } + } + break; + + default: + throw UnknownPropertyException( OUString::number((*ppEntries)->mnHandle), static_cast(this)); + } + + if( !bOk ) + throw IllegalArgumentException(); + } + + if( bOptionsChanged ) + { + if( !pPrinter ) + pPrinter = pDocSh->GetPrinter( true ); + SfxItemSet aNewOptions( pPrinter->GetOptions() ); + aNewOptions.Put( aOptionsPrintItem ); + pPrinter->SetOptions( aNewOptions ); + } + + if( bChanged || bOptionsChanged ) + mxModel->SetModified(); +} + +void DocumentSettings::ExtractURL( XPropertyListType t, Any* pValue ) +{ + XPropertyListRef pList = mxModel->GetDoc()->GetPropertyList( t ); + if( !pList.is() ) + return; + + INetURLObject aPathURL( pList->GetPath() ); + aPathURL.insertName( pList->GetName() ); + aPathURL.setExtension( pList->GetDefaultExt() ); + OUString aPath( aPathURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ) ); + *pValue <<= aPath; +} + +void +DocumentSettings::_getPropertyValues( + const PropertyMapEntry** ppEntries, Any* pValue) +{ + ::SolarMutexGuard aGuard; + + SdDrawDocument* pDoc = mxModel->GetDoc(); + ::sd::DrawDocShell* pDocSh = mxModel->GetDocShell(); + if( nullptr == pDoc || nullptr == pDocSh ) + { + throw RuntimeException("Document or Shell missing", + static_cast(this)); + } + + SdOptionsPrintItem aOptionsPrintItem; + + SfxPrinter* pPrinter = pDocSh->GetPrinter( false ); + if( pPrinter ) + { + SdOptionsPrintItem const * pPrinterOptions = pPrinter->GetOptions().GetItemIfSet( ATTR_OPTIONS_PRINT, false ); + if (pPrinterOptions) + aOptionsPrintItem.GetOptionsPrint() = pPrinterOptions->GetOptionsPrint(); + } + else + { + aOptionsPrintItem.SetOptions( SD_MOD()->GetSdOptions(pDoc->GetDocumentType()) ); + } + SdOptionsPrint& aPrintOpts = aOptionsPrintItem.GetOptionsPrint(); + + for( ; *ppEntries; ppEntries++, pValue++ ) + { + switch( (*ppEntries)->mnHandle ) + { + case HANDLE_COLORTABLEURL: + ExtractURL( XPropertyListType::Color, pValue ); + break; + case HANDLE_DASHTABLEURL: + ExtractURL( XPropertyListType::Dash, pValue ); + break; + case HANDLE_LINEENDTABLEURL: + ExtractURL( XPropertyListType::LineEnd, pValue ); + break; + case HANDLE_HATCHTABLEURL: + ExtractURL( XPropertyListType::Hatch, pValue ); + break; + case HANDLE_GRADIENTTABLEURL: + ExtractURL( XPropertyListType::Gradient, pValue ); + break; + case HANDLE_BITMAPTABLEURL: + ExtractURL( XPropertyListType::Bitmap, pValue ); + break; + case HANDLE_FORBIDDENCHARS: + *pValue <<= mxModel->getForbiddenCharsTable(); + break; + case HANDLE_APPLYUSERDATA: + *pValue <<= pDocSh->IsUseUserData(); + break; + case HANDLE_SAVETHUMBNAIL: + *pValue <<= pDocSh->IsUseThumbnailSave(); + break; + case HANDLE_PRINTDRAWING: + *pValue <<= aPrintOpts.IsDraw(); + break; + case HANDLE_PRINTNOTES: + *pValue <<= aPrintOpts.IsNotes(); + break; + case HANDLE_PRINTHANDOUT: + *pValue <<= aPrintOpts.IsHandout(); + break; + case HANDLE_PRINTOUTLINE: + *pValue <<= aPrintOpts.IsOutline(); + break; + case HANDLE_SLIDESPERHANDOUT: + *pValue <<= static_cast(aPrintOpts.GetHandoutPages()); + break; + case HANDLE_HANDOUTHORIZONTAL: + *pValue <<= aPrintOpts.IsHandoutHorizontal(); + break; + case HANDLE_PRINTPAGENAME: + *pValue <<= aPrintOpts.IsPagename(); + break; + case HANDLE_PRINTDATE: + *pValue <<= aPrintOpts.IsDate(); + break; + case HANDLE_PRINTTIME: + *pValue <<= aPrintOpts.IsTime(); + break; + case HANDLE_PRINTHIDDENPAGES: + *pValue <<= aPrintOpts.IsHiddenPages(); + break; + case HANDLE_PRINTFITPAGE: + *pValue <<= aPrintOpts.IsPagesize(); + break; + case HANDLE_PRINTTILEPAGE: + *pValue <<= aPrintOpts.IsPagetile(); + break; + case HANDLE_PRINTBOOKLET: + *pValue <<= aPrintOpts.IsBooklet(); + break; + case HANDLE_PRINTBOOKLETFRONT: + *pValue <<= aPrintOpts.IsFrontPage(); + break; + case HANDLE_PRINTBOOKLETBACK: + *pValue <<= aPrintOpts.IsBackPage(); + break; + case HANDLE_PRINTQUALITY: + *pValue <<= static_cast(aPrintOpts.GetOutputQuality()); + break; + case HANDLE_MEASUREUNIT: + { + short nMeasure; + SvxFieldUnitToMeasureUnit( pDoc->GetUIUnit(), nMeasure ); + *pValue <<= static_cast(nMeasure); + } + break; + case HANDLE_SCALE_NUM: + *pValue <<= pDoc->GetUIScale().GetNumerator(); + break; + case HANDLE_SCALE_DOM: + *pValue <<= pDoc->GetUIScale().GetDenominator(); + break; + case HANDLE_TABSTOP: + *pValue <<= static_cast(pDoc->GetDefaultTabulator()); + break; + case HANDLE_PAGENUMFMT: + *pValue <<= static_cast(pDoc->GetPageNumType()); + break; + case HANDLE_PRINTERNAME: + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( false ); + *pValue <<= pTempPrinter ? pTempPrinter->GetName() : OUString(); + } + break; + case HANDLE_PRINTERJOB: + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( false ); + if (pTempPrinter) + { + SvMemoryStream aStream; + pTempPrinter->Store( aStream ); + *pValue <<= Sequence< sal_Int8 >( static_cast< const sal_Int8* >( aStream.GetData() ), + aStream.TellEnd() ); + } + else + { + Sequence < sal_Int8 > aSequence; + *pValue <<= aSequence; + } + } + break; + + case HANDLE_PRINTERPAPERSIZE: + { + SfxPrinter *pTempPrinter = pDocSh->GetPrinter( false ); + *pValue <<= pTempPrinter && pTempPrinter->GetPrinterSettingsPreferred(); + } + break; + + case HANDLE_PARAGRAPHSUMMATION : + { + bool bIsSummationOfParagraphs = pDoc->IsSummationOfParagraphs(); + *pValue <<= bIsSummationOfParagraphs; + } + break; + + case HANDLE_CHARCOMPRESS: + { + *pValue <<= static_cast(pDoc->GetCharCompressType()); + break; + } + + case HANDLE_ASIANPUNCT: + { + *pValue <<= pDoc->IsKernAsianPunctuation(); + break; + } + + case HANDLE_UPDATEFROMTEMPLATE: + { + *pValue <<= pDocSh->IsQueryLoadTemplate(); + } + break; + + case HANDLE_PRINTER_INDEPENDENT_LAYOUT: + { + sal_Int16 nPrinterIndependentLayout = + static_cast(pDoc->GetPrinterIndependentLayout()); + *pValue <<= nPrinterIndependentLayout; + } + break; + + // --> #i33095# + case HANDLE_LOAD_READONLY: + { + *pValue <<= pDocSh->IsLoadReadonly(); + } + break; + + case HANDLE_MODIFY_PASSWD: + { + *pValue <<= pDocSh->GetModifyPasswordInfo(); + } + break; + + case HANDLE_SAVE_VERSION: + { + *pValue <<= pDocSh->IsSaveVersionOnClose(); + } + break; + + case HANDLE_EMBED_FONTS: + { + *pValue <<= pDoc->IsEmbedFonts(); + } + break; + + case HANDLE_EMBED_USED_FONTS: + { + *pValue <<= pDoc->IsEmbedUsedFontsOnly(); + } + break; + + case HANDLE_EMBED_LATIN_SCRIPT_FONTS: + { + *pValue <<= pDoc->IsEmbedFontScriptLatin(); + } + break; + + case HANDLE_EMBED_ASIAN_SCRIPT_FONTS: + { + *pValue <<= pDoc->IsEmbedFontScriptAsian(); + } + break; + + case HANDLE_EMBED_COMPLEX_SCRIPT_FONTS: + { + *pValue <<= pDoc->IsEmbedFontScriptComplex(); + } + break; + + case HANDLE_IMAGE_PREFERRED_DPI: + { + *pValue <<= pDoc->getImagePreferredDPI(); + } + break; + + default: + throw UnknownPropertyException( OUString::number((*ppEntries)->mnHandle), static_cast(this)); + } + } +} + +// XInterface +Any SAL_CALL DocumentSettings::queryInterface( const Type& aType ) +{ + return WeakImplHelper< XPropertySet, XMultiPropertySet, XServiceInfo >::queryInterface( aType ); +} + +void SAL_CALL DocumentSettings::acquire( ) noexcept +{ + WeakImplHelper< XPropertySet, XMultiPropertySet, XServiceInfo >::acquire(); +} + +void SAL_CALL DocumentSettings::release( ) noexcept +{ + WeakImplHelper< XPropertySet, XMultiPropertySet, XServiceInfo >::release(); +} + +// XPropertySet +Reference< XPropertySetInfo > SAL_CALL DocumentSettings::getPropertySetInfo( ) +{ + return PropertySetHelper::getPropertySetInfo(); +} + +void SAL_CALL DocumentSettings::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + PropertySetHelper::setPropertyValue( aPropertyName, aValue ); +} + +Any SAL_CALL DocumentSettings::getPropertyValue( const OUString& PropertyName ) +{ + return PropertySetHelper::getPropertyValue( PropertyName ); +} + +void SAL_CALL DocumentSettings::addPropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& xListener ) +{ + PropertySetHelper::addPropertyChangeListener( aPropertyName, xListener ); +} + +void SAL_CALL DocumentSettings::removePropertyChangeListener( const OUString& aPropertyName, const Reference< XPropertyChangeListener >& aListener ) +{ + PropertySetHelper::removePropertyChangeListener( aPropertyName, aListener ); +} + +void SAL_CALL DocumentSettings::addVetoableChangeListener( const OUString& PropertyName, const Reference< XVetoableChangeListener >& aListener ) +{ + PropertySetHelper::addVetoableChangeListener( PropertyName, aListener ); +} + +void SAL_CALL DocumentSettings::removeVetoableChangeListener( const OUString& PropertyName, const Reference< XVetoableChangeListener >& aListener ) +{ + PropertySetHelper::removeVetoableChangeListener( PropertyName, aListener ); +} + +// XMultiPropertySet +void SAL_CALL DocumentSettings::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues ) +{ + PropertySetHelper::setPropertyValues( aPropertyNames, aValues ); +} + +Sequence< Any > SAL_CALL DocumentSettings::getPropertyValues( const Sequence< OUString >& aPropertyNames ) +{ + return PropertySetHelper::getPropertyValues( aPropertyNames ); +} + +void SAL_CALL DocumentSettings::addPropertiesChangeListener( const Sequence< OUString >& aPropertyNames, const Reference< XPropertiesChangeListener >& xListener ) +{ + PropertySetHelper::addPropertiesChangeListener( aPropertyNames, xListener ); +} + +void SAL_CALL DocumentSettings::removePropertiesChangeListener( const Reference< XPropertiesChangeListener >& xListener ) +{ + PropertySetHelper::removePropertiesChangeListener( xListener ); +} + +void SAL_CALL DocumentSettings::firePropertiesChangeEvent( const Sequence< OUString >& aPropertyNames, const Reference< XPropertiesChangeListener >& xListener ) +{ + PropertySetHelper::firePropertiesChangeEvent( aPropertyNames, xListener ); +} + +// XServiceInfo +OUString SAL_CALL DocumentSettings::getImplementationName( ) +{ + return "com.sun.star.comp.Draw.DocumentSettings"; +} + +sal_Bool SAL_CALL DocumentSettings::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence< OUString > SAL_CALL DocumentSettings::getSupportedServiceNames( ) +{ + return { "com.sun.star.document.Settings" , + mxModel->IsImpressDocument()?OUString("com.sun.star.presentation.DocumentSettings"):OUString("com.sun.star.drawing.DocumentSettings") }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/UnoDocumentSettings.hxx b/sd/source/ui/unoidl/UnoDocumentSettings.hxx new file mode 100644 index 000000000..bba3c66b1 --- /dev/null +++ b/sd/source/ui/unoidl/UnoDocumentSettings.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +namespace com::sun::star::uno +{ +class XInterface; +} + +class SdXImpressDocument; + +namespace sd +{ +css::uno::Reference +DocumentSettings_createInstance(SdXImpressDocument* pDoc) noexcept; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/randomnode.cxx b/sd/source/ui/unoidl/randomnode.cxx new file mode 100644 index 000000000..a87ae1783 --- /dev/null +++ b/sd/source/ui/unoidl/randomnode.cxx @@ -0,0 +1,573 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +using ::osl::Mutex; +using ::osl::Guard; + +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::XInterface; +using ::com::sun::star::uno::WeakReference; +using ::com::sun::star::beans::NamedValue; +using ::com::sun::star::lang::IllegalArgumentException; +using ::com::sun::star::container::XEnumeration; +using ::com::sun::star::container::XEnumerationAccess; +using ::com::sun::star::util::XCloneable; +using ::com::sun::star::lang::XServiceInfo; +using ::com::sun::star::lang::XInitialization; +using ::com::sun::star::uno::Type; +using ::com::sun::star::uno::XWeak; +using ::com::sun::star::presentation::ParagraphTarget; +using ::com::sun::star::drawing::XShape; + +using namespace ::com::sun::star::animations; + +namespace sd +{ + +typedef ::cppu::WeakImplHelper< XTimeContainer, XEnumerationAccess, XCloneable, XServiceInfo, XInitialization > RandomAnimationNodeBase; + +namespace { + +class RandomAnimationNode : public RandomAnimationNodeBase +{ +public: + RandomAnimationNode( const RandomAnimationNode& rNode ); + explicit RandomAnimationNode( sal_Int16 nPresetClass ); + RandomAnimationNode(); + + void init( sal_Int16 nPresetClass ); + + // XInitialization + void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + // XChild + Reference< XInterface > SAL_CALL getParent( ) override; + void SAL_CALL setParent( const Reference< XInterface >& Parent ) override; + + // XCloneable + virtual Reference< XCloneable > SAL_CALL createClone() override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + + // XAnimationNode + ::sal_Int16 SAL_CALL getType() override; + Any SAL_CALL getBegin() override; + void SAL_CALL setBegin( const Any& _begin ) override; + Any SAL_CALL getDuration() override; + void SAL_CALL setDuration( const Any& _duration ) override; + Any SAL_CALL getEnd() override; + void SAL_CALL setEnd( const Any& _end ) override; + Any SAL_CALL getEndSync() override; + void SAL_CALL setEndSync( const Any& _endsync ) override; + Any SAL_CALL getRepeatCount() override; + void SAL_CALL setRepeatCount( const Any& _repeatcount ) override; + Any SAL_CALL getRepeatDuration() override; + void SAL_CALL setRepeatDuration( const Any& _repeatduration ) override; + ::sal_Int16 SAL_CALL getFill() override; + void SAL_CALL setFill( ::sal_Int16 _fill ) override; + ::sal_Int16 SAL_CALL getFillDefault() override; + void SAL_CALL setFillDefault( ::sal_Int16 _filldefault ) override; + ::sal_Int16 SAL_CALL getRestart() override; + void SAL_CALL setRestart( ::sal_Int16 _restart ) override; + ::sal_Int16 SAL_CALL getRestartDefault() override; + void SAL_CALL setRestartDefault( ::sal_Int16 _restartdefault ) override; + double SAL_CALL getAcceleration() override; + void SAL_CALL setAcceleration( double _acceleration ) override; + double SAL_CALL getDecelerate() override; + void SAL_CALL setDecelerate( double _decelerate ) override; + sal_Bool SAL_CALL getAutoReverse() override; + void SAL_CALL setAutoReverse( sal_Bool _autoreverse ) override; + Sequence< NamedValue > SAL_CALL getUserData() override; + void SAL_CALL setUserData( const Sequence< NamedValue >& _userdata ) override; + + // XElementAccess + virtual Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XEnumerationAccess + virtual Reference< XEnumeration > SAL_CALL createEnumeration() override; + + // XTimeContainer + Reference< XAnimationNode > SAL_CALL insertBefore( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& refChild ) override; + Reference< XAnimationNode > SAL_CALL insertAfter( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& refChild ) override; + Reference< XAnimationNode > SAL_CALL replaceChild( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& oldChild ) override; + Reference< XAnimationNode > SAL_CALL removeChild( const Reference< XAnimationNode >& oldChild ) override; + Reference< XAnimationNode > SAL_CALL appendChild( const Reference< XAnimationNode >& newChild ) override; + +private: + // our first, last and only protection from multi-threads! + Mutex maMutex; + + sal_Int16 mnPresetClass; + WeakReference mxParent; + + Any maBegin, maDuration, maEnd, maEndSync, maRepeatCount, maRepeatDuration, maTarget; + sal_Int16 mnFill, mnFillDefault, mnRestart, mnRestartDefault; + double mfAcceleration, mfDecelerate; + bool mbAutoReverse; + Sequence< NamedValue > maUserData; + + Reference< XAnimate > mxFirstNode; +}; + +} + +Reference< XInterface > RandomAnimationNode_createInstance( sal_Int16 nPresetClass ) +{ + Reference< XInterface > xInt( static_cast( new RandomAnimationNode( nPresetClass ) ) ); + return xInt; +} + +RandomAnimationNode::RandomAnimationNode( const RandomAnimationNode& rNode ) +: RandomAnimationNodeBase(rNode), + mnPresetClass( rNode.mnPresetClass ), + maBegin( rNode.maBegin ), + maDuration( rNode.maDuration ), + maEnd( rNode.maEnd ), + maEndSync( rNode.maEndSync ), + maRepeatCount( rNode.maRepeatCount ), + maRepeatDuration( rNode.maRepeatDuration ), + maTarget( rNode.maTarget ), + mnFill( rNode.mnFill ), + mnFillDefault( rNode.mnFillDefault ), + mnRestart( rNode.mnRestart ), + mnRestartDefault( rNode.mnRestartDefault ), + mfAcceleration( rNode.mfAcceleration ), + mfDecelerate( rNode.mfDecelerate ), + mbAutoReverse( rNode.mbAutoReverse ), + maUserData( rNode.maUserData ) +{ +} + +RandomAnimationNode::RandomAnimationNode( sal_Int16 nPresetClass ) +{ + init( nPresetClass ); +} + +RandomAnimationNode::RandomAnimationNode() +{ + init( 1 ); +} + +void RandomAnimationNode::init( sal_Int16 nPresetClass ) +{ + mnPresetClass = nPresetClass; + mnFill = AnimationFill::DEFAULT; + mnFillDefault = AnimationFill::INHERIT; + mnRestart = AnimationRestart::DEFAULT; + mnRestartDefault = AnimationRestart::INHERIT; + mfAcceleration = 0.0; + mfDecelerate = 0.0; + mbAutoReverse = false; +} + +// XInitialization +void SAL_CALL RandomAnimationNode::initialize( const Sequence< Any >& aArguments ) +{ + if( aArguments.getLength() != 1 ) + throw IllegalArgumentException(); + + if( aArguments[0].getValueType() == ::cppu::UnoType::get() ) + { + aArguments[0] >>= mnPresetClass; + } + else if( aArguments[0].getValueType() != ::cppu::UnoType::get() ) + { + Reference< XShape > xShape; + aArguments[0] >>= xShape; + if( !xShape.is() ) + throw IllegalArgumentException(); + } + maTarget = aArguments[0]; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getType() +{ + Guard< Mutex > aGuard( maMutex ); + return css::animations::AnimationNodeType::PAR; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getBegin() +{ + Guard< Mutex > aGuard( maMutex ); + return maBegin; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setBegin( const Any& _begin ) +{ + Guard< Mutex > aGuard( maMutex ); + maBegin = _begin; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getDuration() +{ + Guard< Mutex > aGuard( maMutex ); + return maDuration; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setDuration( const Any& _duration ) +{ + Guard< Mutex > aGuard( maMutex ); + maDuration = _duration; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getEnd() +{ + Guard< Mutex > aGuard( maMutex ); + return maEnd; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setEnd( const Any& _end ) +{ + Guard< Mutex > aGuard( maMutex ); + maEnd = _end; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getEndSync() +{ + Guard< Mutex > aGuard( maMutex ); + return maEndSync; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setEndSync( const Any& _endsync ) +{ + Guard< Mutex > aGuard( maMutex ); + maEndSync = _endsync; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getRepeatCount() +{ + Guard< Mutex > aGuard( maMutex ); + return maRepeatCount; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setRepeatCount( const Any& _repeatcount ) +{ + Guard< Mutex > aGuard( maMutex ); + maRepeatCount = _repeatcount; +} + +// XAnimationNode +Any SAL_CALL RandomAnimationNode::getRepeatDuration() +{ + Guard< Mutex > aGuard( maMutex ); + return maRepeatDuration; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setRepeatDuration( const Any& _repeatduration ) +{ + Guard< Mutex > aGuard( maMutex ); + maRepeatDuration = _repeatduration; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getFill() +{ + Guard< Mutex > aGuard( maMutex ); + return mnFill; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setFill( sal_Int16 _fill ) +{ + Guard< Mutex > aGuard( maMutex ); + mnFill = _fill; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getFillDefault() +{ + Guard< Mutex > aGuard( maMutex ); + return mnFillDefault; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setFillDefault( sal_Int16 _filldefault ) +{ + Guard< Mutex > aGuard( maMutex ); + mnFillDefault = _filldefault; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getRestart() +{ + Guard< Mutex > aGuard( maMutex ); + return mnRestart; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setRestart( sal_Int16 _restart ) +{ + Guard< Mutex > aGuard( maMutex ); + mnRestart = _restart; +} + +// XAnimationNode +sal_Int16 SAL_CALL RandomAnimationNode::getRestartDefault() +{ + Guard< Mutex > aGuard( maMutex ); + return mnRestartDefault; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setRestartDefault( sal_Int16 _restartdefault ) +{ + Guard< Mutex > aGuard( maMutex ); + mnRestartDefault = _restartdefault; +} + +// XAnimationNode +double SAL_CALL RandomAnimationNode::getAcceleration() +{ + Guard< Mutex > aGuard( maMutex ); + return mfAcceleration; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setAcceleration( double _acceleration ) +{ + Guard< Mutex > aGuard( maMutex ); + mfAcceleration = _acceleration; +} + +// XAnimationNode +double SAL_CALL RandomAnimationNode::getDecelerate() +{ + Guard< Mutex > aGuard( maMutex ); + return mfDecelerate; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setDecelerate( double _decelerate ) +{ + Guard< Mutex > aGuard( maMutex ); + mfDecelerate = _decelerate; +} + +// XAnimationNode +sal_Bool SAL_CALL RandomAnimationNode::getAutoReverse() +{ + Guard< Mutex > aGuard( maMutex ); + return mbAutoReverse; +} + +// XAnimationNode +void SAL_CALL RandomAnimationNode::setAutoReverse( sal_Bool _autoreverse ) +{ + Guard< Mutex > aGuard( maMutex ); + mbAutoReverse = _autoreverse; +} + +Sequence< NamedValue > SAL_CALL RandomAnimationNode::getUserData() +{ + Guard< Mutex > aGuard( maMutex ); + return maUserData; +} + +void SAL_CALL RandomAnimationNode::setUserData( const Sequence< NamedValue >& _userdata ) +{ + Guard< Mutex > aGuard( maMutex ); + maUserData = _userdata; +} + +// XChild +Reference< XInterface > SAL_CALL RandomAnimationNode::getParent() +{ + Guard< Mutex > aGuard( maMutex ); + return mxParent.get(); +} + +// XChild +void SAL_CALL RandomAnimationNode::setParent( const Reference< XInterface >& Parent ) +{ + Guard< Mutex > aGuard( maMutex ); + mxParent = Parent; +} + +// XCloneable +Reference< XCloneable > SAL_CALL RandomAnimationNode::createClone() +{ + Reference< XCloneable > xNewNode( new RandomAnimationNode( *this ) ); + return xNewNode; +} + +// XElementAccess +Type SAL_CALL RandomAnimationNode::getElementType() +{ + return cppu::UnoType::get(); +} + +// XElementAccess +sal_Bool SAL_CALL RandomAnimationNode::hasElements() +{ + return true; +} + +// XEnumerationAccess +Reference< XEnumeration > SAL_CALL RandomAnimationNode::createEnumeration() +{ + Guard< Mutex > aGuard( maMutex ); + + if( !maTarget.hasValue() && mxFirstNode.is() ) + { + Any aTarget( mxFirstNode->getTarget() ); + if( aTarget.hasValue() ) + { + maTarget = aTarget; + mxFirstNode.clear(); + } + } + + Reference< XEnumeration > xEnum; + + Reference< XEnumerationAccess > aEnumAccess( CustomAnimationPresets::getCustomAnimationPresets().getRandomPreset( mnPresetClass ), UNO_QUERY ); + + if( aEnumAccess.is() ) + { + Reference< XEnumeration > xEnumeration = aEnumAccess->createEnumeration(); + if( xEnumeration.is() ) + { + while( xEnumeration->hasMoreElements() ) + { + Reference< XAnimate > xAnimate( xEnumeration->nextElement(), UNO_QUERY ); + if( xAnimate.is() ) + xAnimate->setTarget( maTarget ); + } + } + xEnum = aEnumAccess->createEnumeration(); + } + else + { + // no presets? give empty node! + Reference< XParallelTimeContainer > xTimeContainer = ParallelTimeContainer::create( comphelper::getProcessComponentContext() ); + xEnum = xTimeContainer->createEnumeration(); + } + + return xEnum; +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::insertBefore( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& ) +{ + return appendChild( newChild ); +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::insertAfter( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& ) +{ + return appendChild( newChild ); +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::replaceChild( const Reference< XAnimationNode >& newChild, const Reference< XAnimationNode >& ) +{ + return appendChild( newChild ); +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::removeChild( const Reference< XAnimationNode >& oldChild ) +{ + return oldChild; +} + +// XTimeContainer +Reference< XAnimationNode > SAL_CALL RandomAnimationNode::appendChild( const Reference< XAnimationNode >& newChild ) +{ + Reference< XAnimate > xAnimate( newChild, UNO_QUERY ); + if( xAnimate.is() ) + { + Any aTarget( xAnimate->getTarget() ); + if( aTarget.hasValue() ) + maTarget = aTarget; + } + + if( !maTarget.hasValue() && !mxFirstNode.is() ) + mxFirstNode = xAnimate; + + return newChild; +} + +// XServiceInfo +OUString RandomAnimationNode::getImplementationName() +{ + return "sd::RandomAnimationNode" ; +} + +// XServiceInfo +sal_Bool RandomAnimationNode::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > RandomAnimationNode::getSupportedServiceNames() +{ + return { "com.sun.star.animations.ParallelTimeContainer", "com.sun.star.comp.sd.RandomAnimationNode" }; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +RandomAnimationNode_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new sd::RandomAnimationNode()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/sddetect.cxx b/sd/source/ui/unoidl/sddetect.cxx new file mode 100644 index 000000000..cfa50d141 --- /dev/null +++ b/sd/source/ui/unoidl/sddetect.cxx @@ -0,0 +1,160 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "sddetect.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::task; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::lang; +using utl::MediaDescriptor; + +SdFilterDetect::SdFilterDetect() +{ +} + +SdFilterDetect::~SdFilterDetect() +{ +} + +OUString SAL_CALL SdFilterDetect::detect( Sequence< beans::PropertyValue >& lDescriptor ) +{ + MediaDescriptor aMediaDesc( lDescriptor ); + OUString aTypeName = aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_TYPENAME, OUString() ); + uno::Reference< io::XInputStream > xInStream ( aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM], uno::UNO_QUERY ); + if ( !xInStream.is() ) + return OUString(); + + SfxMedium aMedium; + aMedium.UseInteractionHandler( false ); + aMedium.setStreamToLoadFrom( xInStream, true ); + + SvStream *pInStrm = aMedium.GetInStream(); + if ( !pInStrm || pInStrm->GetError() ) + return OUString(); + + if ( aTypeName.startsWith( "impress_MS_PowerPoint_97" ) ) + { + // Do not attempt to create an SotStorage on a + // 0-length stream as that would create the compound + // document header on the stream and effectively write to + // disk! + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + if ( pInStrm->remainingSize() == 0 ) + return OUString(); + + try + { + tools::SvRef aStorage = new SotStorage( pInStrm, false ); + if ( !aStorage->GetError() && aStorage->IsStream( "PowerPoint Document" ) ) + return aTypeName; + } + catch (const css::ucb::ContentCreationException&) + { + } + } + else + { + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + + const OUString aFileName( aMediaDesc.getUnpackedValueOrDefault( MediaDescriptor::PROP_URL, OUString() ) ); + GraphicDescriptor aDesc( *pInStrm, &aFileName ); + if( !aDesc.Detect() ) + { + INetURLObject aCheckURL( aFileName ); + if( aCheckURL.getExtension().equalsIgnoreAsciiCase("cgm") ) + { + sal_uInt8 n8; + pInStrm->Seek( STREAM_SEEK_TO_BEGIN ); + pInStrm->ReadUChar( n8 ); + if ( ( n8 & 0xf0 ) == 0 ) + // we are supporting binary cgm format only, so + // this is a small test to exclude cgm text + return "impress_CGM_Computer_Graphics_Metafile"; + } + } + else + { + OUString aShortName( GraphicDescriptor::GetImportFormatShortName( aDesc.GetFileFormat() ) ); + GraphicFilter &rGrfFilter = GraphicFilter::GetGraphicFilter(); + const OUString aName( rGrfFilter.GetImportFormatTypeName( rGrfFilter.GetImportFormatNumberForShortName( aShortName ) ) ); + + if ( aShortName.equalsIgnoreAsciiCase( "PCD" ) ) // there is a multiple pcd selection possible + { + sal_Int32 nBase = 2; // default Base0 + if ( aTypeName == "pcd_Photo_CD_Base4" ) + nBase = 1; + else if ( aTypeName == "pcd_Photo_CD_Base16" ) + nBase = 0; + FilterConfigItem aFilterConfigItem( u"Office.Common/Filter/Graphic/Import/PCD" ); + aFilterConfigItem.WriteInt32( "Resolution" , nBase ); + } + + SfxFilterMatcher aMatch("sdraw"); + std::shared_ptr pFilter = aMatch.GetFilter4FilterName( aName ); + if ( pFilter ) + return pFilter->GetRealTypeName(); + } + } + + return OUString(); +} + +// XServiceInfo +OUString SAL_CALL SdFilterDetect::getImplementationName() +{ + return "com.sun.star.comp.draw.FormatDetector"; +} + +// XServiceInfo +sal_Bool SAL_CALL SdFilterDetect::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +// XServiceInfo +Sequence< OUString > SAL_CALL SdFilterDetect::getSupportedServiceNames() +{ + return { "com.sun.star.frame.ExtendedTypeDetection" }; +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_draw_FormatDetector_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence const &) +{ + return cppu::acquire(new SdFilterDetect()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/sddetect.hxx b/sd/source/ui/unoidl/sddetect.hxx new file mode 100644 index 000000000..3d22cc12c --- /dev/null +++ b/sd/source/ui/unoidl/sddetect.hxx @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include + +#include + + +namespace com::sun::star::beans { struct PropertyValue; } + +class SdFilterDetect : public ::cppu::WeakImplHelper< css::document::XExtendedFilterDetection, css::lang::XServiceInfo > +{ +public: + SdFilterDetect(); + virtual ~SdFilterDetect() override; + + // XServiceInfo + + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XExtendedFilterDetect + + virtual OUString SAL_CALL detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unocpres.cxx b/sd/source/ui/unoidl/unocpres.cxx new file mode 100644 index 000000000..cc57b1e79 --- /dev/null +++ b/sd/source/ui/unoidl/unocpres.cxx @@ -0,0 +1,450 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "unocpres.hxx" +#include +#include +#include + +using namespace ::com::sun::star; + +uno::Reference< uno::XInterface > createUnoCustomShow( SdCustomShow* pShow ) +{ + return static_cast(new SdXCustomPresentation( pShow )); +} + +SdXCustomPresentation::SdXCustomPresentation() noexcept +: mpSdCustomShow(nullptr), mpModel(nullptr), + aDisposeListeners( aDisposeContainerMutex ), + bDisposing( false ) +{ +} + +SdXCustomPresentation::SdXCustomPresentation( SdCustomShow* pShow) noexcept +: mpSdCustomShow(pShow), mpModel(nullptr), + aDisposeListeners( aDisposeContainerMutex ), + bDisposing( false ) +{ +} + +SdXCustomPresentation::~SdXCustomPresentation() noexcept +{ +} + +UNO3_GETIMPLEMENTATION_IMPL( SdXCustomPresentation ); + +// XServiceInfo +OUString SAL_CALL SdXCustomPresentation::getImplementationName() +{ + return "SdXCustomPresentation" ; +} + +sal_Bool SAL_CALL SdXCustomPresentation::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdXCustomPresentation::getSupportedServiceNames() +{ + return { "com.sun.star.presentation.CustomPresentation" }; +} + +// XIndexContainer +void SAL_CALL SdXCustomPresentation::insertByIndex( sal_Int32 Index, const uno::Any& Element ) +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if( Index < 0 || o3tl::make_unsigned(Index) > ( mpSdCustomShow ? mpSdCustomShow->PagesVector().size() : 0 ) ) + throw lang::IndexOutOfBoundsException(); + + uno::Reference< drawing::XDrawPage > xPage; + Element >>= xPage; + + if(!xPage.is()) + throw lang::IllegalArgumentException(); + + SdDrawPage* pPage = comphelper::getFromUnoTunnel( xPage ); + + if(pPage) + { + if( nullptr == mpModel ) + mpModel = pPage->GetModel(); + + if( nullptr != mpModel && nullptr == mpSdCustomShow && mpModel->GetDoc() ) + mpSdCustomShow = new SdCustomShow; + + mpSdCustomShow->PagesVector().insert(mpSdCustomShow->PagesVector().begin() + Index, + static_cast(pPage->GetSdrPage())); + } + + if( mpModel ) + mpModel->SetModified(); +} + +void SAL_CALL SdXCustomPresentation::removeByIndex( sal_Int32 Index ) +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if(mpSdCustomShow) + { + uno::Reference< drawing::XDrawPage > xPage; + getByIndex( Index ) >>= xPage; + + if( xPage.is() ) + { + SvxDrawPage* pPage = comphelper::getFromUnoTunnel( xPage ); + if(pPage) + { + SdCustomShow::PageVec::iterator it = std::find( + mpSdCustomShow->PagesVector().begin(), + mpSdCustomShow->PagesVector().end(), + pPage->GetSdrPage()); + if (it != mpSdCustomShow->PagesVector().end()) + mpSdCustomShow->PagesVector().erase(it); + } + } + } + + if( mpModel ) + mpModel->SetModified(); +} + +// XIndexReplace +void SAL_CALL SdXCustomPresentation::replaceByIndex( sal_Int32 Index, const uno::Any& Element ) +{ + removeByIndex( Index ); + insertByIndex( Index, Element ); +} + +// XElementAccess +uno::Type SAL_CALL SdXCustomPresentation::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdXCustomPresentation::hasElements() +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + return getCount() > 0; +} + +// XIndexAccess +sal_Int32 SAL_CALL SdXCustomPresentation::getCount() +{ + SolarMutexGuard aGuard; + if( bDisposing ) + throw lang::DisposedException(); + + return mpSdCustomShow ? mpSdCustomShow->PagesVector().size() : 0; +} + +uno::Any SAL_CALL SdXCustomPresentation::getByIndex( sal_Int32 Index ) +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if (Index < 0 || !mpSdCustomShow || o3tl::make_unsigned(Index) >= mpSdCustomShow->PagesVector().size()) + throw lang::IndexOutOfBoundsException(); + + uno::Any aAny; + SdrPage * pPage = const_cast(mpSdCustomShow->PagesVector()[Index]); + + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xRef( pPage->getUnoPage(), uno::UNO_QUERY ); + aAny <<= xRef; + } + + return aAny; +} + +// XNamed +OUString SAL_CALL SdXCustomPresentation::getName() +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if(mpSdCustomShow) + return mpSdCustomShow->GetName(); + + return OUString(); +} + +void SAL_CALL SdXCustomPresentation::setName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + throw lang::DisposedException(); + + if(mpSdCustomShow) + mpSdCustomShow->SetName( aName ); +} + +// XComponent +void SAL_CALL SdXCustomPresentation::dispose() +{ + SolarMutexGuard aGuard; + + if( bDisposing ) + return; // caught a recursion + + bDisposing = true; + + uno::Reference< uno::XInterface > xSource( static_cast(this) ); + + lang::EventObject aEvt; + aEvt.Source = xSource; + aDisposeListeners.disposeAndClear(aEvt); + + mpSdCustomShow = nullptr; +} + +void SAL_CALL SdXCustomPresentation::addEventListener( const uno::Reference< lang::XEventListener >& xListener ) +{ + if( bDisposing ) + throw lang::DisposedException(); + + aDisposeListeners.addInterface(xListener); +} + +void SAL_CALL SdXCustomPresentation::removeEventListener( const uno::Reference< lang::XEventListener >& aListener ) +{ + if( !bDisposing ) + aDisposeListeners.removeInterface(aListener); +} + +/*===========================================================================* + * class SdXCustomPresentationAccess : public XCustomPresentationAccess, * + * public UsrObject * + *===========================================================================*/ + +SdXCustomPresentationAccess::SdXCustomPresentationAccess(SdXImpressDocument& rMyModel) noexcept +: mrModel(rMyModel) +{ +} + +SdXCustomPresentationAccess::~SdXCustomPresentationAccess() noexcept +{ +} + +// XServiceInfo +OUString SAL_CALL SdXCustomPresentationAccess::getImplementationName() +{ + return "SdXCustomPresentationAccess"; +} + +sal_Bool SAL_CALL SdXCustomPresentationAccess::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdXCustomPresentationAccess::getSupportedServiceNames() +{ + return { "com.sun.star.presentation.CustomPresentationAccess" }; +} + +// XSingleServiceFactory +uno::Reference< uno::XInterface > SAL_CALL SdXCustomPresentationAccess::createInstance() +{ + uno::Reference< uno::XInterface > xRef( static_cast(new SdXCustomPresentation()) ); + return xRef; +} + +uno::Reference< uno::XInterface > SAL_CALL SdXCustomPresentationAccess::createInstanceWithArguments( const uno::Sequence< uno::Any >& ) +{ + return createInstance(); +} + +// XNameContainer +void SAL_CALL SdXCustomPresentationAccess::insertByName( const OUString& aName, const uno::Any& aElement ) +{ + SolarMutexGuard aGuard; + + // get the documents custom show list + SdCustomShowList* pList = nullptr; + if(mrModel.GetDoc()) + pList = mrModel.GetDoc()->GetCustomShowList(true); + + // no list, no cookies + if( nullptr == pList) + throw uno::RuntimeException(); + + // do we have a container::XIndexContainer? + SdXCustomPresentation* pXShow = nullptr; + + uno::Reference< container::XIndexContainer > xContainer; + if( (aElement >>= xContainer) && xContainer.is() ) + pXShow = comphelper::getFromUnoTunnel(xContainer); + + if( nullptr == pXShow ) + throw lang::IllegalArgumentException(); + + // get the internal custom show from the api wrapper + SdCustomShow* pShow = pXShow->GetSdCustomShow(); + if( nullptr == pShow ) + { + pShow = new SdCustomShow( xContainer ); + pXShow->SetSdCustomShow( pShow ); + } + else + { + if( nullptr == pXShow->GetModel() || *pXShow->GetModel() != mrModel ) + throw lang::IllegalArgumentException(); + } + + // give it a name + pShow->SetName( aName); + + // check if this or another customshow with the same name already exists + for( SdCustomShow* pCompare = pList->First(); + pCompare; + pCompare = pList->Next() ) + { + if( pCompare == pShow || pCompare->GetName() == pShow->GetName() ) + throw container::ElementExistException(); + } + + pList->push_back(std::unique_ptr(pShow)); + + mrModel.SetModified(); +} + +void SAL_CALL SdXCustomPresentationAccess::removeByName( const OUString& Name ) +{ + SolarMutexGuard aGuard; + + SdCustomShow* pShow = getSdCustomShow(Name); + + SdCustomShowList* pList = GetCustomShowList(); + if(!pList || !pShow) + throw container::NoSuchElementException(); + + pList->erase( pShow ); + + mrModel.SetModified(); +} + +// XNameReplace +void SAL_CALL SdXCustomPresentationAccess::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + removeByName( aName ); + insertByName( aName, aElement ); +} + +// XNameAccess +uno::Any SAL_CALL SdXCustomPresentationAccess::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + SdCustomShow* pShow = getSdCustomShow(aName); + if(!pShow) + { + throw container::NoSuchElementException(); + } + + uno::Reference< container::XIndexContainer > xRef( pShow->getUnoCustomShow(), uno::UNO_QUERY ); + return uno::Any(xRef); +} + +uno::Sequence< OUString > SAL_CALL SdXCustomPresentationAccess::getElementNames() +{ + SolarMutexGuard aGuard; + + SdCustomShowList* pList = GetCustomShowList(); + const sal_uInt32 nCount = pList ? pList->size() : 0; + + uno::Sequence< OUString > aSequence( nCount ); + OUString* pStringList = aSequence.getArray(); + + sal_uInt32 nIdx = 0; + while( nIdx < nCount ) + { + const SdCustomShow* pShow = (*pList)[nIdx].get(); + pStringList[nIdx] = pShow->GetName(); + nIdx++; + } + + return aSequence; +} + +sal_Bool SAL_CALL SdXCustomPresentationAccess::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + return getSdCustomShow(aName) != nullptr; +} + +// XElementAccess +uno::Type SAL_CALL SdXCustomPresentationAccess::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdXCustomPresentationAccess::hasElements() +{ + SolarMutexGuard aGuard; + + SdCustomShowList* pList = GetCustomShowList(); + return pList && !pList->empty(); +} + +SdCustomShow * SdXCustomPresentationAccess::getSdCustomShow( std::u16string_view rName ) const noexcept +{ + sal_uInt32 nIdx = 0; + + SdCustomShowList* pList = GetCustomShowList(); + const sal_uInt32 nCount = pList ? pList->size() : 0; + + while( nIdx < nCount ) + { + SdCustomShow* pShow = (*pList)[nIdx].get(); + if( pShow->GetName() == rName ) + return pShow; + nIdx++; + } + return nullptr; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unocpres.hxx b/sd/source/ui/unoidl/unocpres.hxx new file mode 100644 index 000000000..e32dce956 --- /dev/null +++ b/sd/source/ui/unoidl/unocpres.hxx @@ -0,0 +1,147 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + + +class SdCustomShow; + +class SdXCustomPresentation : public ::cppu::WeakImplHelper< css::container::XIndexContainer, + css::container::XNamed, + css::lang::XUnoTunnel, + css::lang::XComponent, + css::lang::XServiceInfo > +{ +private: + SdCustomShow* mpSdCustomShow; + SdXImpressDocument* mpModel; + + // for xComponent + ::osl::Mutex aDisposeContainerMutex; + ::comphelper::OInterfaceContainerHelper3 aDisposeListeners; + bool bDisposing; + +public: + SdXCustomPresentation() noexcept; + explicit SdXCustomPresentation( SdCustomShow* mpSdCustomShow ) noexcept; + virtual ~SdXCustomPresentation() noexcept override; + + // internal + SdCustomShow* GetSdCustomShow() const noexcept { return mpSdCustomShow; } + void SetSdCustomShow( SdCustomShow* pShow ) noexcept { mpSdCustomShow = pShow; } + SdXImpressDocument* GetModel() const noexcept { return mpModel; } + + // uno helper + UNO3_GETIMPLEMENTATION_DECL(SdXCustomPresentation) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override; + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const css::uno::Any& Element ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XNamed + virtual OUString SAL_CALL getName( ) override; + virtual void SAL_CALL setName( const OUString& aName ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; +}; + +class SdXCustomPresentationAccess : public ::cppu::WeakImplHelper< css::container::XNameContainer, + css::lang::XSingleServiceFactory, + css::lang::XServiceInfo > +{ +private: + SdXImpressDocument& mrModel; + + // intern + inline SdCustomShowList* GetCustomShowList() const noexcept; + SdCustomShow * getSdCustomShow( std::u16string_view Name ) const noexcept; + +public: + explicit SdXCustomPresentationAccess(SdXImpressDocument& rMyModel) noexcept; + virtual ~SdXCustomPresentationAccess() noexcept override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XSingleServiceFactory + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstance( ) override; + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL createInstanceWithArguments( const css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XNameContainer + virtual void SAL_CALL insertByName( const OUString& aName, const css::uno::Any& aElement ) override; + virtual void SAL_CALL removeByName( const OUString& Name ) override; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; +}; + +inline SdCustomShowList* SdXCustomPresentationAccess::GetCustomShowList() const noexcept +{ + if(mrModel.GetDoc()) + return mrModel.GetDoc()->GetCustomShowList(); + else + return nullptr; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unodoc.cxx b/sd/source/ui/unoidl/unodoc.cxx new file mode 100644 index 000000000..cfb97c186 --- /dev/null +++ b/sd/source/ui/unoidl/unodoc.cxx @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include + +using namespace ::com::sun::star; + +// com.sun.star.comp.Draw.DrawingDocument + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +sd_DrawingDocument_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence const& args) +{ + SolarMutexGuard aGuard; + + SdDLL::Init(); + + css::uno::Reference xInterface = sfx2::createSfxModelInstance(args, + [](SfxModelFlags _nCreationFlags) + { + SfxObjectShell* pShell = new ::sd::GraphicDocShell( _nCreationFlags ); + return uno::Reference< uno::XInterface >( pShell->GetModel() ); + }); + xInterface->acquire(); + return xInterface.get(); +} + + +// com.sun.star.comp.Draw.PresentationDocument + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +sd_PresentationDocument_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence const& args) +{ + SolarMutexGuard aGuard; + + SdDLL::Init(); + + css::uno::Reference xInterface = sfx2::createSfxModelInstance(args, + [](SfxModelFlags _nCreationFlags) + { + SfxObjectShell* pShell = + new ::sd::DrawDocShell( + _nCreationFlags, false, DocumentType::Impress ); + return pShell->GetModel(); + }); + xInterface->acquire(); + return xInterface.get(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unolayer.cxx b/sd/source/ui/unoidl/unolayer.cxx new file mode 100644 index 000000000..c6bc3cdf3 --- /dev/null +++ b/sd/source/ui/unoidl/unolayer.cxx @@ -0,0 +1,707 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include "unolayer.hxx" + +#include +#include +#include +#include +#include +#include +#include + +// following ones for InsertSdPage() +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unowcntr.hxx" +#include + +using namespace ::com::sun::star; + +// class SdLayer +#define WID_LAYER_LOCKED 1 +#define WID_LAYER_PRINTABLE 2 +#define WID_LAYER_VISIBLE 3 +#define WID_LAYER_NAME 4 +#define WID_LAYER_TITLE 5 +#define WID_LAYER_DESC 6 + +static const SvxItemPropertySet* ImplGetSdLayerPropertySet() +{ + static const SfxItemPropertyMapEntry aSdLayerPropertyMap_Impl[] = + { + { u"" UNO_NAME_LAYER_LOCKED, WID_LAYER_LOCKED, cppu::UnoType::get(), 0, 0 }, + { u"" UNO_NAME_LAYER_PRINTABLE, WID_LAYER_PRINTABLE,cppu::UnoType::get(), 0, 0 }, + { u"" UNO_NAME_LAYER_VISIBLE, WID_LAYER_VISIBLE, cppu::UnoType::get(), 0, 0 }, + { u"" UNO_NAME_LAYER_NAME, WID_LAYER_NAME, ::cppu::UnoType::get(), 0, 0 }, + { u"Title", WID_LAYER_TITLE, ::cppu::UnoType::get(), 0, 0 }, + { u"Description", WID_LAYER_DESC, ::cppu::UnoType::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + static SvxItemPropertySet aSDLayerPropertySet_Impl( aSdLayerPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return &aSDLayerPropertySet_Impl; +} + +SdLayer::SdLayer(SdLayerManager* pLayerManager_, SdrLayer* pSdrLayer_) +: mxLayerManager(pLayerManager_) +, pLayer(pSdrLayer_) +, pPropSet(ImplGetSdLayerPropertySet()) +{ + // no defaults possible yet, a "set" would overwrite existing information + // in view, which is currently needed for saving, because pLayer is not updated + // from view. +} + +SdLayer::~SdLayer() noexcept +{ +} + +// uno helper +UNO3_GETIMPLEMENTATION_IMPL( SdLayer ); + +// XServiceInfo +OUString SAL_CALL SdLayer::getImplementationName() +{ + return "SdUnoLayer"; +} + +sal_Bool SAL_CALL SdLayer::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdLayer::getSupportedServiceNames() +{ + return { "com.sun.star.drawing.Layer" }; +} + +// beans::XPropertySet +uno::Reference< beans::XPropertySetInfo > SAL_CALL SdLayer::getPropertySetInfo( ) +{ + SolarMutexGuard aGuard; + return pPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdLayer::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + if(pLayer == nullptr || mxLayerManager == nullptr) + throw lang::DisposedException(); + + const SfxItemPropertyMapEntry* pEntry = pPropSet->getPropertyMapEntry(aPropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_LAYER_LOCKED: + { + pLayer->SetLockedODF( cppu::any2bool(aValue) ); + set(LOCKED, cppu::any2bool(aValue)); // changes the View, if any exists + break; + } + case WID_LAYER_PRINTABLE: + { + pLayer->SetPrintableODF( cppu::any2bool(aValue) ); + set(PRINTABLE, cppu::any2bool(aValue)); // changes the View, if any exists + break; + } + case WID_LAYER_VISIBLE: + { + pLayer->SetVisibleODF( cppu::any2bool(aValue) ); + set(VISIBLE, cppu::any2bool(aValue)); // changes the View, if any exists + break; + } + case WID_LAYER_NAME: + { + OUString aName; + if(!(aValue >>= aName)) + throw lang::IllegalArgumentException(); + + pLayer->SetName(aName); + mxLayerManager->UpdateLayerView(); + break; + } + + case WID_LAYER_TITLE: + { + OUString sTitle; + if(!(aValue >>= sTitle)) + throw lang::IllegalArgumentException(); + + pLayer->SetTitle(sTitle); + break; + } + + case WID_LAYER_DESC: + { + OUString sDescription; + if(!(aValue >>= sDescription)) + throw lang::IllegalArgumentException(); + + pLayer->SetDescription(sDescription); + break; + } + + default: + throw beans::UnknownPropertyException( aPropertyName, static_cast(this)); + } + + if( mxLayerManager->GetDocShell() ) + mxLayerManager->GetDocShell()->SetModified(); +} + +uno::Any SAL_CALL SdLayer::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + if(pLayer == nullptr || mxLayerManager == nullptr) + throw lang::DisposedException(); + + const SfxItemPropertyMapEntry* pEntry = pPropSet->getPropertyMapEntry(PropertyName); + + uno::Any aValue; + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_LAYER_LOCKED: + aValue <<= get( LOCKED ); + break; + case WID_LAYER_PRINTABLE: + aValue <<= get( PRINTABLE ); + break; + case WID_LAYER_VISIBLE: + aValue <<= get( VISIBLE ); + break; + case WID_LAYER_NAME: + { + OUString aRet(pLayer->GetName()); + aValue <<= aRet; + break; + } + case WID_LAYER_TITLE: + aValue <<= pLayer->GetTitle(); + break; + case WID_LAYER_DESC: + aValue <<= pLayer->GetDescription(); + break; + default: + throw beans::UnknownPropertyException( PropertyName, static_cast(this)); + } + + return aValue; +} + +void SAL_CALL SdLayer::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdLayer::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdLayer::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdLayer::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} + +bool SdLayer::get( LayerAttribute what ) noexcept +{ + if(pLayer && mxLayerManager.is()) + { + // Try 1. is an arbitrary page open? + ::sd::View *pView = mxLayerManager->GetView(); + SdrPageView* pSdrPageView = nullptr; + if(pView) + pSdrPageView = pView->GetSdrPageView(); + + if(pSdrPageView) + { + OUString aLayerName = pLayer->GetName(); + switch(what) + { + case VISIBLE: return pSdrPageView->IsLayerVisible(aLayerName); + case PRINTABLE: return pSdrPageView->IsLayerPrintable(aLayerName); + case LOCKED: return pSdrPageView->IsLayerLocked(aLayerName); + } + } + + // Try 2. get info from FrameView + if(mxLayerManager->GetDocShell()) + { + ::sd::FrameView *pFrameView = mxLayerManager->GetDocShell()->GetFrameView(); + if(pFrameView) + switch(what) + { + case VISIBLE: return pFrameView->GetVisibleLayers().IsSet(pLayer->GetID()); + case PRINTABLE: return pFrameView->GetPrintableLayers().IsSet(pLayer->GetID()); + case LOCKED: return pFrameView->GetLockedLayers().IsSet(pLayer->GetID()); + } + } + + // no view at all, e.g. Draw embedded as OLE in text document, ODF default values + switch(what) + { + case VISIBLE: return true; + case PRINTABLE: return true; + case LOCKED: return false; + } + + } + return false; //TODO: uno::Exception? +} + +void SdLayer::set( LayerAttribute what, bool flag ) noexcept +{ + if(!(pLayer && mxLayerManager.is())) + return; + + // Try 1. is an arbitrary page open? + ::sd::View *pView = mxLayerManager->GetView(); + SdrPageView* pSdrPageView = nullptr; + if(pView) + pSdrPageView = pView->GetSdrPageView(); + + if(pSdrPageView) + { + OUString aLayerName(pLayer->GetName()); + switch(what) + { + case VISIBLE: pSdrPageView->SetLayerVisible(aLayerName,flag); + break; + case PRINTABLE: pSdrPageView->SetLayerPrintable(aLayerName,flag); + break; + case LOCKED: pSdrPageView->SetLayerLocked(aLayerName,flag); + break; + } + } + + // Try 2. get info from FrameView + if(!mxLayerManager->GetDocShell()) + return; + + ::sd::FrameView *pFrameView = mxLayerManager->GetDocShell()->GetFrameView(); + + if(!pFrameView) + return; + + SdrLayerIDSet aNewLayers; + switch(what) + { + case VISIBLE: aNewLayers = pFrameView->GetVisibleLayers(); + break; + case PRINTABLE: aNewLayers = pFrameView->GetPrintableLayers(); + break; + case LOCKED: aNewLayers = pFrameView->GetLockedLayers(); + break; + } + + aNewLayers.Set(pLayer->GetID(),flag); + + switch(what) + { + case VISIBLE: pFrameView->SetVisibleLayers(aNewLayers); + break; + case PRINTABLE: pFrameView->SetPrintableLayers(aNewLayers); + break; + case LOCKED: pFrameView->SetLockedLayers(aNewLayers); + break; + } + return; + //TODO: uno::Exception? +} + +// css::container::XChild +uno::Reference SAL_CALL SdLayer::getParent() +{ + SolarMutexGuard aGuard; + + if( !mxLayerManager.is() ) + throw lang::DisposedException(); + + return uno::Reference (static_cast(mxLayerManager.get()), uno::UNO_QUERY); +} + +void SAL_CALL SdLayer::setParent (const uno::Reference& ) +{ + throw lang::NoSupportException (); +} + +// XComponent +void SAL_CALL SdLayer::dispose( ) +{ + mxLayerManager.clear(); + pLayer = nullptr; +} + +void SAL_CALL SdLayer::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL("not implemented!"); +} + +void SAL_CALL SdLayer::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL("not implemented!"); +} + +// class SdLayerManager +SdLayerManager::SdLayerManager( SdXImpressDocument& rMyModel ) noexcept +:mpModel( &rMyModel) +{ + mpLayers.reset(new SvUnoWeakContainer); +} + +SdLayerManager::~SdLayerManager() noexcept +{ + dispose(); +} + +// uno helper +UNO3_GETIMPLEMENTATION_IMPL( SdLayerManager ); + +// XComponent +void SAL_CALL SdLayerManager::dispose( ) +{ + mpModel = nullptr; + if( mpLayers ) + { + mpLayers->dispose(); + mpLayers.reset(); + } +} + +void SAL_CALL SdLayerManager::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL("not implemented!"); +} + +void SAL_CALL SdLayerManager::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL("not implemented!"); +} + +// XServiceInfo +OUString SAL_CALL SdLayerManager::getImplementationName() +{ + return "SdUnoLayerManager"; +} + +sal_Bool SAL_CALL SdLayerManager::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdLayerManager::getSupportedServiceNames() +{ + return {"com.sun.star.drawing.LayerManager"}; +} + +// XLayerManager +uno::Reference< drawing::XLayer > SAL_CALL SdLayerManager::insertNewByIndex( sal_Int32 nIndex ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + uno::Reference< drawing::XLayer > xLayer; + + if( mpModel->mpDoc ) + { + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + sal_uInt16 nLayerCnt = rLayerAdmin.GetLayerCount(); + sal_Int32 nLayer = nLayerCnt - 2 + 1; + OUString aLayerName; + + // Test for existing names + while( aLayerName.isEmpty() || rLayerAdmin.GetLayer( aLayerName ) ) + { + aLayerName = SdResId(STR_LAYER) + OUString::number(nLayer); + ++nLayer; + } + + SdrLayerAdmin& rLA=mpModel->mpDoc->GetLayerAdmin(); + const sal_Int32 nMax=rLA.GetLayerCount(); + if (nIndex>nMax) nIndex=nMax; + xLayer = GetLayer (rLA.NewLayer(aLayerName,static_cast(nIndex))); + mpModel->SetModified(); + } + return xLayer; +} + +void SAL_CALL SdLayerManager::remove( const uno::Reference< drawing::XLayer >& xLayer ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + SdLayer* pSdLayer = comphelper::getFromUnoTunnel(xLayer); + + if(pSdLayer && GetView()) + { + const SdrLayer* pSdrLayer = pSdLayer->GetSdrLayer(); + GetView()->DeleteLayer( pSdrLayer->GetName() ); + + UpdateLayerView(); + } + + mpModel->SetModified(); +} + +void SAL_CALL SdLayerManager::attachShapeToLayer( const uno::Reference< drawing::XShape >& xShape, const uno::Reference< drawing::XLayer >& xLayer ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + SdLayer* pSdLayer = comphelper::getFromUnoTunnel(xLayer); + SdrLayer* pSdrLayer = pSdLayer?pSdLayer->GetSdrLayer():nullptr; + if(pSdrLayer==nullptr) + return; + + SdrObject* pSdrObject = SdrObject::getSdrObjectFromXShape( xShape ); + + if(pSdrObject) + pSdrObject->SetLayer(pSdrLayer->GetID()); + + mpModel->SetModified(); +} + +uno::Reference< drawing::XLayer > SAL_CALL SdLayerManager::getLayerForShape( const uno::Reference< drawing::XShape >& xShape ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + uno::Reference< drawing::XLayer > xLayer; + + if(mpModel->mpDoc) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if(pObj) + { + SdrLayerID aId = pObj->GetLayer(); + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + xLayer = GetLayer (rLayerAdmin.GetLayerPerID(aId)); + } + } + return xLayer; +} + +// XIndexAccess +sal_Int32 SAL_CALL SdLayerManager::getCount() +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + if( mpModel->mpDoc ) + { + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + return rLayerAdmin.GetLayerCount(); + } + + return 0; +} + +uno::Any SAL_CALL SdLayerManager::getByIndex( sal_Int32 nLayer ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + if( nLayer >= getCount() || nLayer < 0 ) + throw lang::IndexOutOfBoundsException(); + + uno::Any aAny; + + if( mpModel->mpDoc ) + { + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + uno::Reference xLayer (GetLayer (rLayerAdmin.GetLayer(static_cast(nLayer)))); + aAny <<= xLayer; + } + return aAny; +} + +// XNameAccess +uno::Any SAL_CALL SdLayerManager::getByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + if( (mpModel == nullptr) || (mpModel->mpDoc == nullptr ) ) + throw lang::DisposedException(); + + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + SdrLayer* pLayer = rLayerAdmin.GetLayer(aName); + if( pLayer == nullptr ) + throw container::NoSuchElementException(); + + return uno::Any( GetLayer (pLayer) ); +} + +uno::Sequence< OUString > SAL_CALL SdLayerManager::getElementNames() +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + const sal_uInt16 nLayerCount = rLayerAdmin.GetLayerCount(); + + uno::Sequence< OUString > aSeq( nLayerCount ); + + OUString* pStrings = aSeq.getArray(); + + for( sal_uInt16 nLayer = 0; nLayer < nLayerCount; nLayer++ ) + { + SdrLayer* pLayer = rLayerAdmin.GetLayer( nLayer ); + if( pLayer ) + *pStrings++ = pLayer->GetName(); + } + + return aSeq; +} + +sal_Bool SAL_CALL SdLayerManager::hasByName( const OUString& aName ) +{ + SolarMutexGuard aGuard; + + if( mpModel == nullptr ) + throw lang::DisposedException(); + + SdrLayerAdmin& rLayerAdmin = mpModel->mpDoc->GetLayerAdmin(); + + return nullptr != rLayerAdmin.GetLayer(aName); +} + +// XElementAccess +uno::Type SAL_CALL SdLayerManager::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdLayerManager::hasElements() +{ + return getCount() > 0; +} + +/** + * If something was changed at the layers, this methods takes care that the + * changes are made visible in sdbcx::View. + */ +void SdLayerManager::UpdateLayerView() const noexcept +{ + if(!mpModel->mpDocShell) + return; + + ::sd::DrawViewShell* pDrViewSh = dynamic_cast< ::sd::DrawViewShell* >( mpModel->mpDocShell->GetViewShell()); + + if(pDrViewSh) + { + bool bLayerMode = pDrViewSh->IsLayerModeActive(); + pDrViewSh->ChangeEditMode(pDrViewSh->GetEditMode(), !bLayerMode); + pDrViewSh->ChangeEditMode(pDrViewSh->GetEditMode(), bLayerMode); + } + + mpModel->mpDoc->SetChanged(); +} + +/** */ +::sd::View* SdLayerManager::GetView() const noexcept +{ + if( mpModel->mpDocShell ) + { + ::sd::ViewShell* pViewSh = mpModel->mpDocShell->GetViewShell(); + if(pViewSh) + return pViewSh->GetView(); + } + return nullptr; +} + +namespace +{ +/** Compare two pointers to SdrLayer objects. + @param xRef + The implementing SdLayer class provides the first pointer by the + SdLayer::GetSdrLayer method. + @param pSearchData + This void pointer is the second pointer to an SdrLayer + object. + @return + Return if both pointers point to the same object. +*/ +bool compare_layers (const uno::WeakReference& xRef, void const * pSearchData) +{ + uno::Reference xLayer (xRef); + if (xLayer.is()) + { + SdLayer* pSdLayer = comphelper::getFromUnoTunnel (xRef); + if (pSdLayer != nullptr) + { + SdrLayer* pSdrLayer = pSdLayer->GetSdrLayer (); + if (pSdrLayer == static_cast(pSearchData)) + return true; + } + } + return false; +} +} + +/** Use the mpLayers container of weak references to either + retrieve and return a previously created XLayer object for + the given SdrLayer object or create and remember a new one. +*/ +uno::Reference SdLayerManager::GetLayer (SdrLayer* pLayer) +{ + uno::WeakReference xRef; + uno::Reference xLayer; + + // Search existing xLayer for the given pLayer. + if (mpLayers->findRef (xRef, static_cast(pLayer), compare_layers)) + xLayer.set(xRef, uno::UNO_QUERY); + + // Create the xLayer if necessary. + if ( ! xLayer.is()) + { + xLayer = new SdLayer (this, pLayer); + + // Remember the new xLayer for future calls. + uno::WeakReference wRef(xLayer); + mpLayers->insert(wRef); + } + + return xLayer; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unolayer.hxx b/sd/source/ui/unoidl/unolayer.hxx new file mode 100644 index 000000000..aa7d4891b --- /dev/null +++ b/sd/source/ui/unoidl/unolayer.hxx @@ -0,0 +1,169 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include + +#include +#include +#include + +#include + +class SdrLayer; +class SdLayerManager; +class SvUnoWeakContainer; + +namespace sd { +class View; +} +enum LayerAttribute { VISIBLE, PRINTABLE, LOCKED }; + +/*********************************************************************** +* * +***********************************************************************/ +class SdLayer : public ::cppu::WeakImplHelper< css::drawing::XLayer, + css::lang::XServiceInfo, + css::container::XChild, + css::lang::XUnoTunnel, + css::lang::XComponent > +{ +public: + SdLayer(SdLayerManager* pLayerManager_, SdrLayer* pSdrLayer_); + virtual ~SdLayer() noexcept override; + + // intern + SdrLayer* GetSdrLayer() const noexcept { return pLayer; } + + // uno helper + UNO3_GETIMPLEMENTATION_DECL( SdLayer ) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // css::beans::XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // css::container::XChild + + /** Returns the layer manager that manages this layer. + */ + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getParent( ) override; + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + + /** Not implemented. Always throws an exception. + @throws NoSupportException. + */ + virtual void SAL_CALL setParent( const css::uno::Reference< css::uno::XInterface >& Parent ) override; + +private: + rtl::Reference mxLayerManager; + SdrLayer* pLayer; + const SvxItemPropertySet* pPropSet; + + bool get( LayerAttribute what ) noexcept; + void set( LayerAttribute what, bool flag ) noexcept; + +}; + +/*********************************************************************** +* * +***********************************************************************/ + +class SdLayerManager : public ::cppu::WeakImplHelper< css::drawing::XLayerManager, + css::container::XNameAccess, + css::lang::XServiceInfo, + css::lang::XUnoTunnel, + css::lang::XComponent > +{ + friend class SdLayer; + +public: + explicit SdLayerManager( SdXImpressDocument& rMyModel ) noexcept; + virtual ~SdLayerManager() noexcept override; + + // uno helper + UNO3_GETIMPLEMENTATION_DECL( SdLayerManager ) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XLayerManager + virtual css::uno::Reference< css::drawing::XLayer > SAL_CALL insertNewByIndex( sal_Int32 nIndex ) override; + virtual void SAL_CALL remove( const css::uno::Reference< css::drawing::XLayer >& xLayer ) override; + virtual void SAL_CALL attachShapeToLayer( const css::uno::Reference< css::drawing::XShape >& xShape, const css::uno::Reference< css::drawing::XLayer >& xLayer ) override; + virtual css::uno::Reference< css::drawing::XLayer > SAL_CALL getLayerForShape( const css::uno::Reference< css::drawing::XShape >& xShape ) override; + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override ; + virtual css::uno::Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + /** Return the XLayer object that is associated with the + given SdrLayer object. If the requested object does + not yet exist it is created. All calls with the same argument + return the same object. + @param pLayer + The SdrLayer object for which to return the + associated XLayer object. + @return + The returned value is the unique XLayer object + associated with the specified argument. If no layer can be + created for the argument than an empty reference is returned. + */ + css::uno::Reference< css::drawing::XLayer> GetLayer (SdrLayer* pLayer); + + // XComponent + virtual void SAL_CALL dispose( ) override; + virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override; + virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override; + +private: + SdXImpressDocument* mpModel; + std::unique_ptr mpLayers; + + ::sd::View* GetView() const noexcept; + ::sd::DrawDocShell* GetDocShell() const noexcept { return mpModel->mpDocShell; } + void UpdateLayerView() const noexcept; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx new file mode 100644 index 000000000..758ce1380 --- /dev/null +++ b/sd/source/ui/unoidl/unomodel.cxx @@ -0,0 +1,3491 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "unopool.hxx" +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Support creation of GraphicStorageHandler and EmbeddedObjectResolver +#include +#include +#include +#include +#include "UnoDocumentSettings.hxx" + +#include +#include +#include +#include +#include + +#include +#include +#include "unolayer.hxx" +#include +#include "unocpres.hxx" +#include "unoobj.hxx" +#include +#include "unopback.hxx" +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace ::sd; + +TranslateId SdTPAction::GetClickActionSdResId( presentation::ClickAction eCA ) +{ + switch( eCA ) + { + case presentation::ClickAction_NONE: return STR_CLICK_ACTION_NONE; + case presentation::ClickAction_PREVPAGE: return STR_CLICK_ACTION_PREVPAGE; + case presentation::ClickAction_NEXTPAGE: return STR_CLICK_ACTION_NEXTPAGE; + case presentation::ClickAction_FIRSTPAGE: return STR_CLICK_ACTION_FIRSTPAGE; + case presentation::ClickAction_LASTPAGE: return STR_CLICK_ACTION_LASTPAGE; + case presentation::ClickAction_BOOKMARK: return STR_CLICK_ACTION_BOOKMARK; + case presentation::ClickAction_DOCUMENT: return STR_CLICK_ACTION_DOCUMENT; + case presentation::ClickAction_PROGRAM: return STR_CLICK_ACTION_PROGRAM; + case presentation::ClickAction_MACRO: return STR_CLICK_ACTION_MACRO; + case presentation::ClickAction_SOUND: return STR_CLICK_ACTION_SOUND; + case presentation::ClickAction_VERB: return STR_CLICK_ACTION_VERB; + case presentation::ClickAction_STOPPRESENTATION: return STR_CLICK_ACTION_STOPPRESENTATION; + default: OSL_FAIL( "No StringResource for ClickAction available!" ); + } + return {}; +} + +namespace { + +class SdUnoForbiddenCharsTable : public SvxUnoForbiddenCharsTable, + public SfxListener +{ +public: + explicit SdUnoForbiddenCharsTable(SdrModel* pModel); + virtual ~SdUnoForbiddenCharsTable() override; + + // SfxListener + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) noexcept override; +protected: + virtual void onChange() override; + +private: + SdrModel* mpModel; +}; + +} + +SdUnoForbiddenCharsTable::SdUnoForbiddenCharsTable( SdrModel* pModel ) +: SvxUnoForbiddenCharsTable( pModel->GetForbiddenCharsTable() ), mpModel( pModel ) +{ + StartListening( *pModel ); +} + +void SdUnoForbiddenCharsTable::onChange() +{ + if( mpModel ) + { + mpModel->ReformatAllTextObjects(); + } +} + +SdUnoForbiddenCharsTable::~SdUnoForbiddenCharsTable() +{ + SolarMutexGuard g; + + if( mpModel ) + EndListening( *mpModel ); +} + +void SdUnoForbiddenCharsTable::Notify( SfxBroadcaster&, const SfxHint& rHint ) noexcept +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast( &rHint ); + if( SdrHintKind::ModelCleared == pSdrHint->GetKind() ) + { + mpModel = nullptr; + } +} + +const sal_uInt16 WID_MODEL_LANGUAGE = 1; +const sal_uInt16 WID_MODEL_TABSTOP = 2; +const sal_uInt16 WID_MODEL_VISAREA = 3; +const sal_uInt16 WID_MODEL_MAPUNIT = 4; +const sal_uInt16 WID_MODEL_FORBCHARS = 5; +const sal_uInt16 WID_MODEL_CONTFOCUS = 6; +const sal_uInt16 WID_MODEL_DSGNMODE = 7; +const sal_uInt16 WID_MODEL_BASICLIBS = 8; +const sal_uInt16 WID_MODEL_RUNTIMEUID = 9; +const sal_uInt16 WID_MODEL_BUILDID = 10; +const sal_uInt16 WID_MODEL_HASVALIDSIGNATURES = 11; +const sal_uInt16 WID_MODEL_DIALOGLIBS = 12; +const sal_uInt16 WID_MODEL_FONTS = 13; +const sal_uInt16 WID_MODEL_INTEROPGRABBAG = 14; +const sal_uInt16 WID_MODEL_THEME = 15; + +static const SvxItemPropertySet* ImplGetDrawModelPropertySet() +{ + // Attention: the first parameter HAS TO BE sorted!!! + const static SfxItemPropertyMapEntry aDrawModelPropertyMap_Impl[] = + { + { u"BuildId", WID_MODEL_BUILDID, ::cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_CharLocale, WID_MODEL_LANGUAGE, ::cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_TabStop, WID_MODEL_TABSTOP, ::cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_VisibleArea, WID_MODEL_VISAREA, ::cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_MapUnit, WID_MODEL_MAPUNIT, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_ForbiddenCharacters, WID_MODEL_FORBCHARS, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_AutomContFocus, WID_MODEL_CONTFOCUS, cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_ApplyFrmDsgnMode, WID_MODEL_DSGNMODE, cppu::UnoType::get(), 0, 0}, + { u"BasicLibraries", WID_MODEL_BASICLIBS, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"DialogLibraries", WID_MODEL_DIALOGLIBS, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_RuntimeUID, WID_MODEL_RUNTIMEUID, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_HasValidSignatures, WID_MODEL_HASVALIDSIGNATURES, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"Fonts", WID_MODEL_FONTS, cppu::UnoType>::get(), beans::PropertyAttribute::READONLY, 0}, + { sUNO_Prop_InteropGrabBag, WID_MODEL_INTEROPGRABBAG, cppu::UnoType>::get(), 0, 0}, + { sUNO_Prop_Theme, WID_MODEL_THEME, cppu::UnoType>::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + static SvxItemPropertySet aDrawModelPropertySet_Impl( aDrawModelPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return &aDrawModelPropertySet_Impl; +} + +// this ctor is used from the DocShell +SdXImpressDocument::SdXImpressDocument(::sd::DrawDocShell* pShell, bool bClipBoard) +: SfxBaseModel( pShell ), + mpDocShell( pShell ), + mpDoc( pShell ? pShell->GetDoc() : nullptr ), + mbDisposed(false), + mbImpressDoc( pShell && pShell->GetDoc() && pShell->GetDoc()->GetDocumentType() == DocumentType::Impress ), + mbClipBoard( bClipBoard ), + mpPropSet( ImplGetDrawModelPropertySet() ) +{ + if( mpDoc ) + { + StartListening( *mpDoc ); + } + else + { + OSL_FAIL("DocShell is invalid"); + } +} + +SdXImpressDocument::SdXImpressDocument(SdDrawDocument* pDoc, bool bClipBoard) +: SfxBaseModel( nullptr ), + mpDocShell( nullptr ), + mpDoc( pDoc ), + mbDisposed(false), + mbImpressDoc( pDoc && pDoc->GetDocumentType() == DocumentType::Impress ), + mbClipBoard( bClipBoard ), + mpPropSet( ImplGetDrawModelPropertySet() ) +{ + if( mpDoc ) + { + StartListening( *mpDoc ); + } + else + { + OSL_FAIL("SdDrawDocument is invalid"); + } +} + +/*********************************************************************** +* * +***********************************************************************/ +SdXImpressDocument::~SdXImpressDocument() noexcept +{ +} + +// XInterface +uno::Any SAL_CALL SdXImpressDocument::queryInterface( const uno::Type & rType ) +{ + uno::Any aAny; + + if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (rType == cppu::UnoType::get()) + aAny <<= uno::Reference(this); + else if (mbImpressDoc && rType == cppu::UnoType::get()) + aAny <<= uno::Reference< presentation::XPresentationSupplier >(this); + else if (mbImpressDoc && rType == cppu::UnoType::get()) + aAny <<= uno::Reference< presentation::XCustomPresentationSupplier >(this); + else + return SfxBaseModel::queryInterface(rType); + + return aAny; +} + +void SAL_CALL SdXImpressDocument::acquire() noexcept +{ + SfxBaseModel::acquire(); +} + +void SAL_CALL SdXImpressDocument::release() noexcept +{ + if (osl_atomic_decrement( &m_refCount ) != 0) + return; + + // restore reference count: + osl_atomic_increment( &m_refCount ); + if(!mbDisposed) + { + try + { + dispose(); + } + catch (const uno::RuntimeException&) + { + // don't break throw () + TOOLS_WARN_EXCEPTION( "sd", "" ); + } + } + SfxBaseModel::release(); +} + +// XUnoTunnel +const css::uno::Sequence< sal_Int8 > & SdXImpressDocument::getUnoTunnelId() noexcept +{ + static const comphelper::UnoIdInit theSdXImpressDocumentUnoTunnelId; + return theSdXImpressDocumentUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL SdXImpressDocument::getSomething( const css::uno::Sequence< sal_Int8 >& rIdentifier ) +{ + if (comphelper::isUnoTunnelId(rIdentifier)) + return comphelper::getSomething_cast(mpDoc); + + return comphelper::getSomethingImpl(rIdentifier, this, + comphelper::FallbackToGetSomethingOf{}); +} + +// XTypeProvider +uno::Sequence< uno::Type > SAL_CALL SdXImpressDocument::getTypes( ) +{ + ::SolarMutexGuard aGuard; + + if( !maTypeSequence.hasElements() ) + { + uno::Sequence< uno::Type > aTypes( SfxBaseModel::getTypes() ); + aTypes = comphelper::concatSequences(aTypes, + uno::Sequence { + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get() }); + if( mbImpressDoc ) + { + aTypes = comphelper::concatSequences(aTypes, + uno::Sequence { + cppu::UnoType::get(), + cppu::UnoType::get(), + cppu::UnoType::get() }); + } + maTypeSequence = aTypes; + } + + return maTypeSequence; +} + +uno::Sequence< sal_Int8 > SAL_CALL SdXImpressDocument::getImplementationId( ) +{ + return css::uno::Sequence(); +} + +/*********************************************************************** +* * +***********************************************************************/ +void SdXImpressDocument::Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) +{ + if( mpDoc ) + { + if (rHint.GetId() == SfxHintId::ThisIsAnSdrHint) + { + const SdrHint* pSdrHint = static_cast( &rHint ); + if( hasEventListeners() ) + { + document::EventObject aEvent; + if( SvxUnoDrawMSFactory::createEvent( mpDoc, pSdrHint, aEvent ) ) + notifyEvent( aEvent ); + } + + if( pSdrHint->GetKind() == SdrHintKind::ModelCleared ) + { + if( mpDoc ) + EndListening( *mpDoc ); + mpDoc = nullptr; + mpDocShell = nullptr; + } + } + else + { + // did our SdDrawDocument just died? + if(rHint.GetId() == SfxHintId::Dying) + { + // yes, so we ask for a new one + if( mpDocShell ) + { + SdDrawDocument *pNewDoc = mpDocShell->GetDoc(); + + // is there a new one? + if( pNewDoc != mpDoc ) + { + mpDoc = pNewDoc; + if(mpDoc) + StartListening( *mpDoc ); + } + } + } + } + } + SfxBaseModel::Notify( rBC, rHint ); +} + +/****************************************************************************** +* * +******************************************************************************/ +SdPage* SdXImpressDocument::InsertSdPage( sal_uInt16 nPage, bool bDuplicate ) +{ + sal_uInt16 nPageCount = mpDoc->GetSdPageCount( PageKind::Standard ); + SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + + rtl::Reference pStandardPage; + + if( 0 == nPageCount ) + { + // this is only used for clipboard where we only have one page + pStandardPage = mpDoc->AllocSdPage(false); + + Size aDefSize(21000, 29700); // A4 portrait orientation + pStandardPage->SetSize( aDefSize ); + mpDoc->InsertPage(pStandardPage.get(), 0); + } + else + { + // here we determine the page after which we should insert + SdPage* pPreviousStandardPage = mpDoc->GetSdPage( std::min( static_cast(nPageCount - 1), nPage ), PageKind::Standard ); + SdrLayerIDSet aVisibleLayers = pPreviousStandardPage->TRG_GetMasterPageVisibleLayers(); + bool bIsPageBack = aVisibleLayers.IsSet( aBckgrnd ); + bool bIsPageObj = aVisibleLayers.IsSet( aBckgrndObj ); + + // AutoLayouts must be ready + mpDoc->StopWorkStartupDelay(); + + /* First we create a standard page and then a notes page. It is + guaranteed, that after a standard page the corresponding notes page + follows. */ + + sal_uInt16 nStandardPageNum = pPreviousStandardPage->GetPageNum() + 2; + SdPage* pPreviousNotesPage = static_cast( mpDoc->GetPage( nStandardPageNum - 1 ) ); + sal_uInt16 nNotesPageNum = nStandardPageNum + 1; + + /************************************************************** + * standard page + **************************************************************/ + if( bDuplicate ) + pStandardPage = static_cast( pPreviousStandardPage->CloneSdrPage(*mpDoc).get() ); + else + pStandardPage = mpDoc->AllocSdPage(false); + + pStandardPage->SetSize( pPreviousStandardPage->GetSize() ); + pStandardPage->SetBorder( pPreviousStandardPage->GetLeftBorder(), + pPreviousStandardPage->GetUpperBorder(), + pPreviousStandardPage->GetRightBorder(), + pPreviousStandardPage->GetLowerBorder() ); + pStandardPage->SetOrientation( pPreviousStandardPage->GetOrientation() ); + pStandardPage->SetName(OUString()); + + // insert page after current page + mpDoc->InsertPage(pStandardPage.get(), nStandardPageNum); + + if( !bDuplicate ) + { + // use MasterPage of the current page + pStandardPage->TRG_SetMasterPage(pPreviousStandardPage->TRG_GetMasterPage()); + pStandardPage->SetLayoutName( pPreviousStandardPage->GetLayoutName() ); + pStandardPage->SetAutoLayout(AUTOLAYOUT_NONE, true ); + } + + aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + aVisibleLayers.Set(aBckgrnd, bIsPageBack); + aVisibleLayers.Set(aBckgrndObj, bIsPageObj); + pStandardPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + + /************************************************************** + * notes page + **************************************************************/ + rtl::Reference pNotesPage; + + if( bDuplicate ) + pNotesPage = static_cast( pPreviousNotesPage->CloneSdrPage(*mpDoc).get() ); + else + pNotesPage = mpDoc->AllocSdPage(false); + + pNotesPage->SetSize( pPreviousNotesPage->GetSize() ); + pNotesPage->SetBorder( pPreviousNotesPage->GetLeftBorder(), + pPreviousNotesPage->GetUpperBorder(), + pPreviousNotesPage->GetRightBorder(), + pPreviousNotesPage->GetLowerBorder() ); + pNotesPage->SetOrientation( pPreviousNotesPage->GetOrientation() ); + pNotesPage->SetName(OUString()); + pNotesPage->SetPageKind(PageKind::Notes); + + // insert page after current page + mpDoc->InsertPage(pNotesPage.get(), nNotesPageNum); + + if( !bDuplicate ) + { + // use MasterPage of the current page + pNotesPage->TRG_SetMasterPage(pPreviousNotesPage->TRG_GetMasterPage()); + pNotesPage->SetLayoutName( pPreviousNotesPage->GetLayoutName() ); + pNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true ); + } + } + + SetModified(); + + return pStandardPage.get(); +} + +void SdXImpressDocument::SetModified() noexcept +{ + if( mpDoc ) + mpDoc->SetChanged(); +} + +// XModel +void SAL_CALL SdXImpressDocument::lockControllers( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + mpDoc->setLock(true); +} + +void SAL_CALL SdXImpressDocument::unlockControllers( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + if( mpDoc->isLocked() ) + { + mpDoc->setLock(false); + } +} + +sal_Bool SAL_CALL SdXImpressDocument::hasControllersLocked( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + return mpDoc->isLocked(); +} + +uno::Reference < container::XIndexAccess > SAL_CALL SdXImpressDocument::getViewData() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference < container::XIndexAccess > xRet( SfxBaseModel::getViewData() ); + + if( !xRet.is() ) + { + const std::vector> &rList = mpDoc->GetFrameViewList(); + + if( !rList.empty() ) + { + xRet = new comphelper::IndexedPropertyValuesContainer(); + + uno::Reference < container::XIndexContainer > xCont( xRet, uno::UNO_QUERY ); + DBG_ASSERT( xCont.is(), "SdXImpressDocument::getViewData() failed for OLE object" ); + if( xCont.is() ) + { + for( sal_uInt32 i = 0, n = rList.size(); i < n; i++ ) + { + ::sd::FrameView* pFrameView = rList[ i ].get(); + + uno::Sequence< beans::PropertyValue > aSeq; + pFrameView->WriteUserDataSequence( aSeq ); + xCont->insertByIndex( i, uno::Any( aSeq ) ); + } + } + } + } + + return xRet; +} + +void SAL_CALL SdXImpressDocument::setViewData( const uno::Reference < container::XIndexAccess >& xData ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + SfxBaseModel::setViewData( xData ); + if( !(mpDocShell && (mpDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) && xData.is()) ) + return; + + const sal_Int32 nCount = xData->getCount(); + + std::vector> &rViews = mpDoc->GetFrameViewList(); + + rViews.clear(); + + uno::Sequence< beans::PropertyValue > aSeq; + for( sal_Int32 nIndex = 0; nIndex < nCount; nIndex++ ) + { + if( xData->getByIndex( nIndex ) >>= aSeq ) + { + std::unique_ptr<::sd::FrameView> pFrameView(new ::sd::FrameView( mpDoc )); + pFrameView->ReadUserDataSequence( aSeq ); + rViews.push_back( std::move(pFrameView) ); + } + } +} + +// XDrawPageDuplicator +uno::Reference< drawing::XDrawPage > SAL_CALL SdXImpressDocument::duplicate( const uno::Reference< drawing::XDrawPage >& xPage ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + // get pPage from xPage and determine the Id (nPos ) afterwards + SvxDrawPage* pSvxPage = comphelper::getFromUnoTunnel( xPage ); + if( pSvxPage ) + { + SdPage* pPage = static_cast( pSvxPage->GetSdrPage() ); + sal_uInt16 nPos = pPage->GetPageNum(); + nPos = ( nPos - 1 ) / 2; + pPage = InsertSdPage( nPos, true ); + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + return xDrawPage; + } + } + + uno::Reference< drawing::XDrawPage > xDrawPage; + return xDrawPage; +} + +// XDrawPagesSupplier +uno::Reference< drawing::XDrawPages > SAL_CALL SdXImpressDocument::getDrawPages() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< drawing::XDrawPages > xDrawPages( mxDrawPagesAccess ); + + if( !xDrawPages.is() ) + { + initializeDocument(); + mxDrawPagesAccess = xDrawPages = new SdDrawPagesAccess(*this); + } + + return xDrawPages; +} + +// XMasterPagesSupplier +uno::Reference< drawing::XDrawPages > SAL_CALL SdXImpressDocument::getMasterPages() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< drawing::XDrawPages > xMasterPages( mxMasterPagesAccess ); + + if( !xMasterPages.is() ) + { + if ( !hasControllersLocked() ) + initializeDocument(); + mxMasterPagesAccess = xMasterPages = new SdMasterPagesAccess(*this); + } + + return xMasterPages; +} + +// XLayerManagerSupplier +uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getLayerManager( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< container::XNameAccess > xLayerManager( mxLayerManager ); + + if( !xLayerManager.is() ) + mxLayerManager = xLayerManager = new SdLayerManager(*this); + + return xLayerManager; +} + +// XCustomPresentationSupplier +uno::Reference< container::XNameContainer > SAL_CALL SdXImpressDocument::getCustomPresentations() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< container::XNameContainer > xCustomPres( mxCustomPresentationAccess ); + + if( !xCustomPres.is() ) + mxCustomPresentationAccess = xCustomPres = new SdXCustomPresentationAccess(*this); + + return xCustomPres; +} + +// XPresentationSupplier +uno::Reference< presentation::XPresentation > SAL_CALL SdXImpressDocument::getPresentation() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + return mpDoc->getPresentation(); +} + +// XHandoutMasterSupplier +uno::Reference< drawing::XDrawPage > SAL_CALL SdXImpressDocument::getHandoutMasterPage() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< drawing::XDrawPage > xPage; + + initializeDocument(); + SdPage* pPage = mpDoc->GetMasterSdPage(0, PageKind::Handout); + if (pPage) + xPage.set(pPage->getUnoPage(), uno::UNO_QUERY); + return xPage; +} + +// XMultiServiceFactory ( SvxFmMSFactory ) + +css::uno::Reference SdXImpressDocument::create( + OUString const & aServiceSpecifier, OUString const & referer) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + if( aServiceSpecifier == "com.sun.star.drawing.DashTable" ) + { + if( !mxDashTable.is() ) + mxDashTable = SvxUnoDashTable_createInstance( mpDoc ); + + return mxDashTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.GradientTable" ) + { + if( !mxGradientTable.is() ) + mxGradientTable = SvxUnoGradientTable_createInstance( mpDoc ); + + return mxGradientTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.HatchTable" ) + { + if( !mxHatchTable.is() ) + mxHatchTable = SvxUnoHatchTable_createInstance( mpDoc ); + + return mxHatchTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.BitmapTable" ) + { + if( !mxBitmapTable.is() ) + mxBitmapTable = SvxUnoBitmapTable_createInstance( mpDoc ); + + return mxBitmapTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.TransparencyGradientTable" ) + { + if( !mxTransGradientTable.is() ) + mxTransGradientTable = SvxUnoTransGradientTable_createInstance( mpDoc ); + + return mxTransGradientTable; + } + if( aServiceSpecifier == "com.sun.star.drawing.MarkerTable" ) + { + if( !mxMarkerTable.is() ) + mxMarkerTable = SvxUnoMarkerTable_createInstance( mpDoc ); + + return mxMarkerTable; + } + if( aServiceSpecifier == "com.sun.star.text.NumberingRules" ) + { + return uno::Reference< uno::XInterface >( SvxCreateNumRule( mpDoc ), uno::UNO_QUERY ); + } + if( aServiceSpecifier == "com.sun.star.drawing.Background" ) + { + return uno::Reference< uno::XInterface >( + static_cast(new SdUnoPageBackground( mpDoc ))); + } + + if( aServiceSpecifier == "com.sun.star.drawing.Defaults" ) + { + if( !mxDrawingPool.is() ) + mxDrawingPool = SdUnoCreatePool( mpDoc ); + + return mxDrawingPool; + + } + + if ( aServiceSpecifier == sUNO_Service_ImageMapRectangleObject ) + { + return SvUnoImageMapRectangleObject_createInstance( ImplGetSupportedMacroItems() ); + } + + if ( aServiceSpecifier == sUNO_Service_ImageMapCircleObject ) + { + return SvUnoImageMapCircleObject_createInstance( ImplGetSupportedMacroItems() ); + } + + if ( aServiceSpecifier == sUNO_Service_ImageMapPolygonObject ) + { + return SvUnoImageMapPolygonObject_createInstance( ImplGetSupportedMacroItems() ); + } + + if( aServiceSpecifier == "com.sun.star.document.Settings" || + ( !mbImpressDoc && ( aServiceSpecifier == "com.sun.star.drawing.DocumentSettings" ) ) || + ( mbImpressDoc && ( aServiceSpecifier == "com.sun.star.presentation.DocumentSettings" ) ) ) + { + return sd::DocumentSettings_createInstance( this ); + } + + if( aServiceSpecifier == "com.sun.star.text.TextField.DateTime" || + aServiceSpecifier == "com.sun.star.text.textfield.DateTime" ) + { + return static_cast(new SvxUnoTextField( text::textfield::Type::DATE )); + } + + if( aServiceSpecifier == "com.sun.star.presentation.TextField.Header" || + aServiceSpecifier == "com.sun.star.presentation.textfield.Header" ) + { + return static_cast(new SvxUnoTextField( text::textfield::Type::PRESENTATION_HEADER )); + } + + if( aServiceSpecifier == "com.sun.star.presentation.TextField.Footer" || + aServiceSpecifier == "com.sun.star.presentation.textfield.Footer" ) + { + return static_cast(new SvxUnoTextField( text::textfield::Type::PRESENTATION_FOOTER )); + } + + if( aServiceSpecifier == "com.sun.star.presentation.TextField.DateTime" || + aServiceSpecifier == "com.sun.star.presentation.textfield.DateTime" ) + { + return static_cast(new SvxUnoTextField( text::textfield::Type::PRESENTATION_DATE_TIME )); + } + + if( aServiceSpecifier == "com.sun.star.text.TextField.PageName" || + aServiceSpecifier == "com.sun.star.text.textfield.PageName" ) + { + return static_cast(new SvxUnoTextField( text::textfield::Type::PAGE_NAME )); + } + + if (aServiceSpecifier == "com.sun.star.text.TextField.DocInfo.Custom" || + aServiceSpecifier == "com.sun.star.text.textfield.DocInfo.Custom") + { + return static_cast(new SvxUnoTextField(text::textfield::Type::DOCINFO_CUSTOM)); + } + + if( aServiceSpecifier == "com.sun.star.xml.NamespaceMap" ) + { + static sal_uInt16 aWhichIds[] = { SDRATTR_XMLATTRIBUTES, EE_CHAR_XMLATTRIBS, EE_PARA_XMLATTRIBS, 0 }; + + return svx::NamespaceMap_createInstance( aWhichIds, &mpDoc->GetItemPool() ); + } + + // Support creation of GraphicStorageHandler and EmbeddedObjectResolver + if (aServiceSpecifier == "com.sun.star.document.ExportGraphicStorageHandler") + { + return static_cast(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Write )); + } + + if (aServiceSpecifier == "com.sun.star.document.ImportGraphicStorageHandler") + { + return static_cast(new SvXMLGraphicHelper( SvXMLGraphicHelperMode::Read )); + } + + if( aServiceSpecifier == "com.sun.star.document.ExportEmbeddedObjectResolver" ) + { + comphelper::IEmbeddedHelper* pPersist = mpDoc->GetPersist(); + if( nullptr == pPersist ) + throw lang::DisposedException(); + + return static_cast(new SvXMLEmbeddedObjectHelper( *pPersist, SvXMLEmbeddedObjectHelperMode::Write )); + } + + if( aServiceSpecifier == "com.sun.star.document.ImportEmbeddedObjectResolver" ) + { + comphelper::IEmbeddedHelper* pPersist = mpDoc->GetPersist(); + if( nullptr == pPersist ) + throw lang::DisposedException(); + + return static_cast(new SvXMLEmbeddedObjectHelper( *pPersist, SvXMLEmbeddedObjectHelperMode::Read )); + } + + uno::Reference< uno::XInterface > xRet; + + if( aServiceSpecifier.startsWith( "com.sun.star.presentation.") ) + { + const std::u16string_view aType( aServiceSpecifier.subView(26) ); + rtl::Reference pShape; + + SdrObjKind nType = SdrObjKind::Text; + // create a shape wrapper + if( o3tl::starts_with(aType, u"TitleTextShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"OutlinerShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"SubtitleShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"GraphicObjectShape" ) ) + { + nType = SdrObjKind::Graphic; + } + else if( o3tl::starts_with(aType, u"PageShape" ) ) + { + nType = SdrObjKind::Page; + } + else if( o3tl::starts_with(aType, u"OLE2Shape" ) ) + { + nType = SdrObjKind::OLE2; + } + else if( o3tl::starts_with(aType, u"ChartShape" ) ) + { + nType = SdrObjKind::OLE2; + } + else if( o3tl::starts_with(aType, u"CalcShape" ) ) + { + nType = SdrObjKind::OLE2; + } + else if( o3tl::starts_with(aType, u"TableShape" ) ) + { + nType = SdrObjKind::Table; + } + else if( o3tl::starts_with(aType, u"OrgChartShape" ) ) + { + nType = SdrObjKind::OLE2; + } + else if( o3tl::starts_with(aType, u"NotesShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"HandoutShape" ) ) + { + nType = SdrObjKind::Page; + } + else if( o3tl::starts_with(aType, u"FooterShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"HeaderShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"SlideNumberShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"DateTimeShape" ) ) + { + nType = SdrObjKind::Text; + } + else if( o3tl::starts_with(aType, u"MediaShape" ) ) + { + nType = SdrObjKind::Media; + } + else + { + throw lang::ServiceNotRegisteredException(); + } + + // create the API wrapper + pShape = CreateSvxShapeByTypeAndInventor( nType, SdrInventor::Default, referer ); + + // set shape type + if( pShape && !mbClipBoard ) + pShape->SetShapeType(aServiceSpecifier); + + xRet = static_cast(pShape.get()); + } + else if ( aServiceSpecifier == "com.sun.star.drawing.TableShape" ) + { + rtl::Reference pShape = CreateSvxShapeByTypeAndInventor( SdrObjKind::Table, SdrInventor::Default, referer ); + if( pShape && !mbClipBoard ) + pShape->SetShapeType(aServiceSpecifier); + + xRet = static_cast(pShape.get()); + } + else + { + xRet = SvxFmMSFactory::createInstance( aServiceSpecifier ); + } + + uno::Reference< drawing::XShape > xShape( xRet, uno::UNO_QUERY ); + SvxShape* pShape = xShape.is() ? comphelper::getFromUnoTunnel(xShape) : nullptr; + if (pShape) + { + xRet.clear(); + new SdXShape( pShape, this ); + xRet = xShape; + xShape.clear(); + } + + return xRet; +} + +uno::Reference< uno::XInterface > SAL_CALL SdXImpressDocument::createInstance( const OUString& aServiceSpecifier ) +{ + return create(aServiceSpecifier, ""); +} + +css::uno::Reference +SdXImpressDocument::createInstanceWithArguments( + OUString const & ServiceSpecifier, + css::uno::Sequence const & Arguments) +{ + OUString arg; + if ((ServiceSpecifier == "com.sun.star.drawing.GraphicObjectShape" + || ServiceSpecifier == "com.sun.star.drawing.MediaShape" + || ServiceSpecifier == "com.sun.star.presentation.MediaShape") + && Arguments.getLength() == 1 && (Arguments[0] >>= arg)) + { + return create(ServiceSpecifier, arg); + } + return SvxFmMSFactory::createInstanceWithArguments( + ServiceSpecifier, Arguments); +} + +uno::Sequence< OUString > SAL_CALL SdXImpressDocument::getAvailableServiceNames() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + const uno::Sequence< OUString > aSNS_ORG( SvxFmMSFactory::getAvailableServiceNames() ); + + uno::Sequence< OUString > aSNS_Common{ "com.sun.star.drawing.DashTable", + "com.sun.star.drawing.GradientTable", + "com.sun.star.drawing.HatchTable", + "com.sun.star.drawing.BitmapTable", + "com.sun.star.drawing.TransparencyGradientTable", + "com.sun.star.drawing.MarkerTable", + "com.sun.star.text.NumberingRules", + "com.sun.star.drawing.Background", + "com.sun.star.document.Settings", + sUNO_Service_ImageMapRectangleObject, + sUNO_Service_ImageMapCircleObject, + sUNO_Service_ImageMapPolygonObject, + "com.sun.star.xml.NamespaceMap", + + // Support creation of GraphicStorageHandler and EmbeddedObjectResolver + "com.sun.star.document.ExportGraphicStorageHandler", + "com.sun.star.document.ImportGraphicStorageHandler", + "com.sun.star.document.ExportEmbeddedObjectResolver", + "com.sun.star.document.ImportEmbeddedObjectResolver", + "com.sun.star.drawing.TableShape" }; + + uno::Sequence< OUString > aSNS_Specific; + + if(mbImpressDoc) + aSNS_Specific = { "com.sun.star.presentation.TitleTextShape", + "com.sun.star.presentation.OutlinerShape", + "com.sun.star.presentation.SubtitleShape", + "com.sun.star.presentation.GraphicObjectShape", + "com.sun.star.presentation.ChartShape", + "com.sun.star.presentation.PageShape", + "com.sun.star.presentation.OLE2Shape", + "com.sun.star.presentation.TableShape", + "com.sun.star.presentation.OrgChartShape", + "com.sun.star.presentation.NotesShape", + "com.sun.star.presentation.HandoutShape", + "com.sun.star.presentation.DocumentSettings", + "com.sun.star.presentation.FooterShape", + "com.sun.star.presentation.HeaderShape", + "com.sun.star.presentation.SlideNumberShape", + "com.sun.star.presentation.DateTimeShape", + "com.sun.star.presentation.CalcShape", + "com.sun.star.presentation.MediaShape" }; + else + aSNS_Specific = { "com.sun.star.drawing.DocumentSettings" }; + + return comphelper::concatSequences( aSNS_ORG, aSNS_Common, aSNS_Specific ); +} + +// lang::XServiceInfo +OUString SAL_CALL SdXImpressDocument::getImplementationName() +{ + return "SdXImpressDocument"; + /* // Matching the .component information: + return mbImpressDoc + ? OUString("com.sun.star.comp.Draw.PresentationDocument") + : OUString("com.sun.star.comp.Draw.DrawingDocument"); + */ +} + +sal_Bool SAL_CALL SdXImpressDocument::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdXImpressDocument::getSupportedServiceNames() +{ + ::SolarMutexGuard aGuard; + + return { "com.sun.star.document.OfficeDocument", + "com.sun.star.drawing.GenericDrawingDocument", + "com.sun.star.drawing.DrawingDocumentFactory", + mbImpressDoc?OUString("com.sun.star.presentation.PresentationDocument"):OUString("com.sun.star.drawing.DrawingDocument") }; +} + +// XPropertySet +uno::Reference< beans::XPropertySetInfo > SAL_CALL SdXImpressDocument::getPropertySetInfo( ) +{ + ::SolarMutexGuard aGuard; + return mpPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdXImpressDocument::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_MODEL_LANGUAGE: + { + lang::Locale aLocale; + if(!(aValue >>= aLocale)) + throw lang::IllegalArgumentException(); + + mpDoc->SetLanguage( LanguageTag::convertToLanguageType(aLocale), EE_CHAR_LANGUAGE ); + break; + } + case WID_MODEL_TABSTOP: + { + sal_Int32 nValue = 0; + if(!(aValue >>= nValue) || nValue < 0 ) + throw lang::IllegalArgumentException(); + + mpDoc->SetDefaultTabulator(static_cast(nValue)); + break; + } + case WID_MODEL_VISAREA: + { + SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh(); + if( !pEmbeddedObj ) + break; + + awt::Rectangle aVisArea; + if( !(aValue >>= aVisArea) || (aVisArea.Width < 0) || (aVisArea.Height < 0) ) + throw lang::IllegalArgumentException(); + + sal_Int32 nRight, nTop; + if (o3tl::checked_add(aVisArea.X, aVisArea.Width, nRight) || o3tl::checked_add(aVisArea.Y, aVisArea.Height, nTop)) + throw lang::IllegalArgumentException(); + + pEmbeddedObj->SetVisArea(::tools::Rectangle(aVisArea.X, aVisArea.Y, nRight, nTop)); + } + break; + case WID_MODEL_CONTFOCUS: + { + bool bFocus = false; + if( !(aValue >>= bFocus ) ) + throw lang::IllegalArgumentException(); + mpDoc->SetAutoControlFocus( bFocus ); + } + break; + case WID_MODEL_DSGNMODE: + { + bool bMode = false; + if( !(aValue >>= bMode ) ) + throw lang::IllegalArgumentException(); + mpDoc->SetOpenInDesignMode( bMode ); + } + break; + case WID_MODEL_BUILDID: + aValue >>= maBuildId; + return; + case WID_MODEL_MAPUNIT: + case WID_MODEL_BASICLIBS: + case WID_MODEL_RUNTIMEUID: // is read-only + case WID_MODEL_DIALOGLIBS: + case WID_MODEL_FONTS: + throw beans::PropertyVetoException(); + case WID_MODEL_INTEROPGRABBAG: + setGrabBagItem(aValue); + break; + case WID_MODEL_THEME: + { + SdrModel& rModel = getSdrModelFromUnoModel(); + std::unique_ptr pTheme = svx::Theme::FromAny(aValue); + rModel.SetTheme(std::move(pTheme)); + } + break; + default: + throw beans::UnknownPropertyException( aPropertyName, static_cast(this)); + } + + SetModified(); +} + +uno::Any SAL_CALL SdXImpressDocument::getPropertyValue( const OUString& PropertyName ) +{ + ::SolarMutexGuard aGuard; + + uno::Any aAny; + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_MODEL_LANGUAGE: + { + LanguageType eLang = mpDoc->GetLanguage( EE_CHAR_LANGUAGE ); + aAny <<= LanguageTag::convertToLocale( eLang); + break; + } + case WID_MODEL_TABSTOP: + aAny <<= static_cast(mpDoc->GetDefaultTabulator()); + break; + case WID_MODEL_VISAREA: + { + SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh(); + if( !pEmbeddedObj ) + break; + + const ::tools::Rectangle& aRect = pEmbeddedObj->GetVisArea(); + awt::Rectangle aVisArea( aRect.Left(), aRect.Top(), aRect.getWidth(), aRect.getHeight() ); + aAny <<= aVisArea; + } + break; + case WID_MODEL_MAPUNIT: + { + SfxObjectShell* pEmbeddedObj = mpDoc->GetDocSh(); + if( !pEmbeddedObj ) + break; + + sal_Int16 nMeasureUnit = 0; + SvxMapUnitToMeasureUnit( pEmbeddedObj->GetMapUnit(), nMeasureUnit ); + aAny <<= nMeasureUnit; + } + break; + case WID_MODEL_FORBCHARS: + { + aAny <<= getForbiddenCharsTable(); + } + break; + case WID_MODEL_CONTFOCUS: + aAny <<= mpDoc->GetAutoControlFocus(); + break; + case WID_MODEL_DSGNMODE: + aAny <<= mpDoc->GetOpenInDesignMode(); + break; + case WID_MODEL_BASICLIBS: + aAny <<= mpDocShell->GetBasicContainer(); + break; + case WID_MODEL_DIALOGLIBS: + aAny <<= mpDocShell->GetDialogContainer(); + break; + case WID_MODEL_RUNTIMEUID: + aAny <<= getRuntimeUID(); + break; + case WID_MODEL_BUILDID: + return uno::Any( maBuildId ); + case WID_MODEL_HASVALIDSIGNATURES: + aAny <<= hasValidSignatures(); + break; + case WID_MODEL_FONTS: + { + uno::Sequence aSeq; + int nSeqIndex = 0; + + sal_uInt16 const aWhichIds[] { EE_CHAR_FONTINFO, EE_CHAR_FONTINFO_CJK, + EE_CHAR_FONTINFO_CTL }; + + const SfxItemPool& rPool = mpDoc->GetPool(); + + for(sal_uInt16 nWhichId : aWhichIds) + { + sal_uInt32 nItems = rPool.GetItemCount2( nWhichId ); + + aSeq.realloc( aSeq.getLength() + nItems*5 + 5 ); + auto pSeq = aSeq.getArray(); + + for (const SfxPoolItem* pItem : rPool.GetItemSurrogates(nWhichId)) + { + const SvxFontItem *pFont = static_cast(pItem); + + pSeq[nSeqIndex++] <<= pFont->GetFamilyName(); + pSeq[nSeqIndex++] <<= pFont->GetStyleName(); + pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetFamily()); + pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetPitch()); + pSeq[nSeqIndex++] <<= sal_Int16(pFont->GetCharSet()); + } + + const SvxFontItem& rFont = static_cast(rPool.GetDefaultItem( nWhichId )); + + pSeq[nSeqIndex++] <<= rFont.GetFamilyName(); + pSeq[nSeqIndex++] <<= rFont.GetStyleName(); + pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetFamily()); + pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetPitch()); + pSeq[nSeqIndex++] <<= sal_Int16(rFont.GetCharSet()); + + } + + aSeq.realloc( nSeqIndex ); + aAny <<= aSeq; + break; + } + case WID_MODEL_INTEROPGRABBAG: + getGrabBagItem(aAny); + break; + case WID_MODEL_THEME: + { + SdrModel& rModel = getSdrModelFromUnoModel(); + svx::Theme* pTheme = rModel.GetTheme(); + if (pTheme) + { + pTheme->ToAny(aAny); + } + else + { + beans::PropertyValues aValues; + aAny <<= aValues; + } + break; + } + default: + throw beans::UnknownPropertyException( PropertyName, static_cast(this)); + } + + return aAny; +} + +void SAL_CALL SdXImpressDocument::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdXImpressDocument::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdXImpressDocument::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdXImpressDocument::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} + +// XLinkTargetSupplier +uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getLinks() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< container::XNameAccess > xLinks( mxLinks ); + if( !xLinks.is() ) + mxLinks = xLinks = new SdDocLinkTargets( *this ); + return xLinks; +} + +// XStyleFamiliesSupplier +uno::Reference< container::XNameAccess > SAL_CALL SdXImpressDocument::getStyleFamilies( ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + uno::Reference< container::XNameAccess > xStyles( dynamic_cast< container::XNameAccess* >( mpDoc->GetStyleSheetPool()) ); + return xStyles; +} + +// XAnyCompareFactory +uno::Reference< css::ucb::XAnyCompare > SAL_CALL SdXImpressDocument::createAnyCompareByName( const OUString& ) +{ + return SvxCreateNumRuleCompare(); +} + +// XRenderable +sal_Int32 SAL_CALL SdXImpressDocument::getRendererCount( const uno::Any& rSelection, + const uno::Sequence< beans::PropertyValue >& ) +{ + ::SolarMutexGuard aGuard; + sal_Int32 nRet = 0; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + if (mpDocShell) + { + uno::Reference< frame::XModel > xModel; + + rSelection >>= xModel; + + if( xModel == mpDocShell->GetModel() ) + nRet = mpDoc->GetSdPageCount( PageKind::Standard ); + else + { + uno::Reference< drawing::XShapes > xShapes; + + rSelection >>= xShapes; + + if( xShapes.is() && xShapes->getCount() ) + nRet = 1; + } + } + return nRet; +} + +uno::Sequence< beans::PropertyValue > SAL_CALL SdXImpressDocument::getRenderer( sal_Int32 , const uno::Any& , + const uno::Sequence< beans::PropertyValue >& rxOptions ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + bool bExportNotesPages = false; + for( const auto& rOption : rxOptions ) + { + if ( rOption.Name == "ExportNotesPages" ) + rOption.Value >>= bExportNotesPages; + } + uno::Sequence< beans::PropertyValue > aRenderer; + if (mpDocShell) + { + awt::Size aPageSize; + if ( bExportNotesPages ) + { + Size aNotesPageSize = mpDoc->GetSdPage( 0, PageKind::Notes )->GetSize(); + aPageSize = awt::Size( aNotesPageSize.Width(), aNotesPageSize.Height() ); + } + else + { + const ::tools::Rectangle aVisArea( mpDocShell->GetVisArea( embed::Aspects::MSOLE_DOCPRINT ) ); + aPageSize = awt::Size( aVisArea.GetWidth(), aVisArea.GetHeight() ); + } + aRenderer = { comphelper::makePropertyValue("PageSize", aPageSize) }; + } + return aRenderer; +} + +namespace { + +class ImplRenderPaintProc : public sdr::contact::ViewObjectContactRedirector +{ + const SdrLayerAdmin& rLayerAdmin; + SdrPageView* pSdrPageView; + +public: + bool IsVisible ( const SdrObject* pObj ) const; + bool IsPrintable( const SdrObject* pObj ) const; + + ImplRenderPaintProc(const SdrLayerAdmin& rLA, SdrPageView* pView); + + // all default implementations just call the same methods at the original. To do something + // different, override the method and at least do what the method does. + virtual void createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override; +}; + +} + +ImplRenderPaintProc::ImplRenderPaintProc(const SdrLayerAdmin& rLA, SdrPageView *const pView) + : rLayerAdmin(rLA) + , pSdrPageView(pView) +{ +} + +static sal_Int32 ImplPDFGetBookmarkPage( const OUString& rBookmark, SdDrawDocument const & rDoc ) +{ + sal_Int32 nPage = -1; + + OUString aBookmark( rBookmark ); + + if( rBookmark.startsWith("#") ) + aBookmark = rBookmark.copy( 1 ); + + // is the bookmark a page ? + bool bIsMasterPage; + sal_uInt16 nPgNum = rDoc.GetPageByName( aBookmark, bIsMasterPage ); + + if ( nPgNum == SDRPAGE_NOTFOUND ) + { + // is the bookmark an object ? + SdrObject* pObj = rDoc.GetObj( aBookmark ); + if (pObj) + nPgNum = pObj->getSdrPageFromSdrObject()->GetPageNum(); + } + if ( nPgNum != SDRPAGE_NOTFOUND ) + nPage = ( nPgNum - 1 ) / 2; + return nPage; +} + +static void ImplPDFExportComments( const uno::Reference< drawing::XDrawPage >& xPage, vcl::PDFExtOutDevData& rPDFExtOutDevData ) +{ + try + { + uno::Reference< office::XAnnotationAccess > xAnnotationAccess( xPage, uno::UNO_QUERY_THROW ); + uno::Reference< office::XAnnotationEnumeration > xAnnotationEnumeration( xAnnotationAccess->createAnnotationEnumeration() ); + + while( xAnnotationEnumeration->hasMoreElements() ) + { + uno::Reference< office::XAnnotation > xAnnotation( xAnnotationEnumeration->nextElement() ); + + geometry::RealPoint2D aRealPoint2D( xAnnotation->getPosition() ); + uno::Reference< text::XText > xText( xAnnotation->getTextRange() ); + + vcl::PDFNote aNote; + aNote.Title = xAnnotation->getAuthor(); + aNote.Contents = xText->getString(); + aNote.maModificationDate = xAnnotation->getDateTime(); + + rPDFExtOutDevData.CreateNote( ::tools::Rectangle( Point( static_cast< ::tools::Long >( aRealPoint2D.X * 100 ), + static_cast< ::tools::Long >( aRealPoint2D.Y * 100 ) ), Size( 1000, 1000 ) ), aNote ); + } + } + catch (const uno::Exception&) + { + } +} + +static void ImplPDFExportShapeInteraction( const uno::Reference< drawing::XShape >& xShape, SdDrawDocument& rDoc, vcl::PDFExtOutDevData& rPDFExtOutDevData ) +{ + if ( xShape->getShapeType() == "com.sun.star.drawing.GroupShape" ) + { + uno::Reference< container::XIndexAccess > xIndexAccess( xShape, uno::UNO_QUERY ); + if ( xIndexAccess.is() ) + { + sal_Int32 i, nCount = xIndexAccess->getCount(); + for ( i = 0; i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xSubShape( xIndexAccess->getByIndex( i ), uno::UNO_QUERY ); + if ( xSubShape.is() ) + ImplPDFExportShapeInteraction( xSubShape, rDoc, rPDFExtOutDevData ); + } + } + } + else + { + uno::Reference< beans::XPropertySet > xShapePropSet( xShape, uno::UNO_QUERY ); + if( xShapePropSet.is() ) + { + Size aPageSize( rDoc.GetSdPage( 0, PageKind::Standard )->GetSize() ); + Point aPoint( 0, 0 ); + ::tools::Rectangle aPageRect( aPoint, aPageSize ); + + awt::Point aShapePos( xShape->getPosition() ); + awt::Size aShapeSize( xShape->getSize() ); + ::tools::Rectangle aLinkRect( Point( aShapePos.X, aShapePos.Y ), Size( aShapeSize.Width, aShapeSize.Height ) ); + + // Handle linked videos. + if (xShape->getShapeType() == "com.sun.star.drawing.MediaShape" || xShape->getShapeType() == "com.sun.star.presentation.MediaShape") + { + OUString aMediaURL; + xShapePropSet->getPropertyValue("MediaURL") >>= aMediaURL; + if (!aMediaURL.isEmpty()) + { + sal_Int32 nScreenId = rPDFExtOutDevData.CreateScreen(aLinkRect, rPDFExtOutDevData.GetCurrentPageNumber()); + if (aMediaURL.startsWith("vnd.sun.star.Package:")) + { + OUString aTempFileURL; + xShapePropSet->getPropertyValue("PrivateTempFileURL") >>= aTempFileURL; + rPDFExtOutDevData.SetScreenStream(nScreenId, aTempFileURL); + } + else + rPDFExtOutDevData.SetScreenURL(nScreenId, aMediaURL); + } + } + + presentation::ClickAction eCa; + uno::Any aAny( xShapePropSet->getPropertyValue( "OnClick" ) ); + if ( aAny >>= eCa ) + { + OUString const actionName(SdResId(SdTPAction::GetClickActionSdResId(eCa))); + switch ( eCa ) + { + case presentation::ClickAction_LASTPAGE : + { + sal_Int32 nCount = rDoc.GetSdPageCount( PageKind::Standard ); + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nCount - 1, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + break; + case presentation::ClickAction_FIRSTPAGE : + { + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, 0, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + break; + case presentation::ClickAction_PREVPAGE : + { + sal_Int32 nDestPage = rPDFExtOutDevData.GetCurrentPageNumber(); + if ( nDestPage ) + nDestPage--; + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nDestPage, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + break; + case presentation::ClickAction_NEXTPAGE : + { + sal_Int32 nDestPage = rPDFExtOutDevData.GetCurrentPageNumber() + 1; + sal_Int32 nLastPage = rDoc.GetSdPageCount( PageKind::Standard ) - 1; + if ( nDestPage > nLastPage ) + nDestPage = nLastPage; + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nDestPage, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + break; + + case presentation::ClickAction_PROGRAM : + case presentation::ClickAction_BOOKMARK : + case presentation::ClickAction_DOCUMENT : + { + OUString aBookmark; + xShapePropSet->getPropertyValue( "Bookmark" ) >>= aBookmark; + if( !aBookmark.isEmpty() ) + { + switch( eCa ) + { + case presentation::ClickAction_DOCUMENT : + case presentation::ClickAction_PROGRAM : + { + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkURL( nLinkId, aBookmark ); + } + break; + case presentation::ClickAction_BOOKMARK : + { + sal_Int32 nPage = ImplPDFGetBookmarkPage( aBookmark, rDoc ); + if ( nPage != -1 ) + { + sal_Int32 nDestId = rPDFExtOutDevData.CreateDest( aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle ); + sal_Int32 nLinkId = rPDFExtOutDevData.CreateLink(aLinkRect, actionName); + rPDFExtOutDevData.SetLinkDest( nLinkId, nDestId ); + } + } + break; + default: + break; + } + } + } + break; + + case presentation::ClickAction_STOPPRESENTATION : + case presentation::ClickAction_SOUND : + case presentation::ClickAction_INVISIBLE : + case presentation::ClickAction_VERB : + case presentation::ClickAction_VANISH : + case presentation::ClickAction_MACRO : + default : + break; + } + } + } + } +} + +void ImplRenderPaintProc::createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject(); + if(!pObject) + { + // not an object, maybe a page + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor); + return; + } + SdrPage* pSdrPage(pObject->getSdrPageFromSdrObject()); + if(!pSdrPage) + return; + if(!pSdrPage->checkVisibility(rOriginal, rDisplayInfo, false)) + return; + if(!IsVisible(pObject) || !IsPrintable(pObject)) + return; + + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor); +} + +bool ImplRenderPaintProc::IsVisible( const SdrObject* pObj ) const +{ + bool bVisible = true; + SdrLayerID nLayerId = pObj->GetLayer(); + if( pSdrPageView ) + { + const SdrLayer* pSdrLayer = rLayerAdmin.GetLayerPerID( nLayerId ); + if ( pSdrLayer ) + { + const OUString& aLayerName = pSdrLayer->GetName(); + bVisible = pSdrPageView->IsLayerVisible( aLayerName ); + } + } + return bVisible; +} +bool ImplRenderPaintProc::IsPrintable( const SdrObject* pObj ) const +{ + bool bPrintable = true; + SdrLayerID nLayerId = pObj->GetLayer(); + if( pSdrPageView ) + { + const SdrLayer* pSdrLayer = rLayerAdmin.GetLayerPerID( nLayerId ); + if ( pSdrLayer ) + { + const OUString& aLayerName = pSdrLayer->GetName(); + bPrintable = pSdrPageView->IsLayerPrintable( aLayerName ); + } + } + return bPrintable; + +} + +namespace +{ + sal_Int16 CalcOutputPageNum(vcl::PDFExtOutDevData const * pPDFExtOutDevData, SdDrawDocument const *pDoc, sal_Int16 nPageNumber) + { + //export all pages, simple one to one case + if (pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportHiddenSlides()) + return nPageNumber-1; + //check all preceding pages, and only count non-hidden ones + sal_Int16 nRet = 0; + for (sal_Int16 i = 0; i < nPageNumber-1; ++i) + { + if (!pDoc->GetSdPage(i, PageKind::Standard)->IsExcluded()) + ++nRet; + } + return nRet; + } +} + +void SAL_CALL SdXImpressDocument::render( sal_Int32 nRenderer, const uno::Any& rSelection, + const uno::Sequence< beans::PropertyValue >& rxOptions ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpDoc ) + throw lang::DisposedException(); + + if (!mpDocShell) + return; + + uno::Reference< awt::XDevice > xRenderDevice; + const sal_Int32 nPageNumber = nRenderer + 1; + PageKind ePageKind = PageKind::Standard; + bool bExportNotesPages = false; + + for( const auto& rOption : rxOptions ) + { + if ( rOption.Name == "RenderDevice" ) + rOption.Value >>= xRenderDevice; + else if ( rOption.Name == "ExportNotesPages" ) + { + rOption.Value >>= bExportNotesPages; + if ( bExportNotesPages ) + ePageKind = PageKind::Notes; + } + } + + if( !(xRenderDevice.is() && nPageNumber && ( nPageNumber <= mpDoc->GetSdPageCount( ePageKind ) )) ) + return; + + VCLXDevice* pDevice = comphelper::getFromUnoTunnel( xRenderDevice ); + VclPtr< OutputDevice> pOut = pDevice ? pDevice->GetOutputDevice() : VclPtr< OutputDevice >(); + + if( !pOut ) + return; + + vcl::PDFExtOutDevData* pPDFExtOutDevData = dynamic_cast( pOut->GetExtOutDevData() ); + + if ( mpDoc->GetSdPage(static_cast(nPageNumber)-1, PageKind::Standard)->IsExcluded() && + !(pPDFExtOutDevData && pPDFExtOutDevData->GetIsExportHiddenSlides()) ) + return; + + if (pPDFExtOutDevData) + { + css::lang::Locale const docLocale(Application::GetSettings().GetLanguageTag().getLocale()); + pPDFExtOutDevData->SetDocumentLocale(docLocale); + } + + ::sd::ClientView aView( mpDocShell, pOut ); + ::tools::Rectangle aVisArea( Point(), mpDoc->GetSdPage( static_cast(nPageNumber) - 1, ePageKind )->GetSize() ); + vcl::Region aRegion( aVisArea ); + + ::sd::ViewShell* pOldViewSh = mpDocShell->GetViewShell(); + ::sd::View* pOldSdView = pOldViewSh ? pOldViewSh->GetView() : nullptr; + + if ( pOldSdView ) + pOldSdView->SdrEndTextEdit(); + + aView.SetHlplVisible( false ); + aView.SetGridVisible( false ); + aView.SetBordVisible( false ); + aView.SetPageVisible( false ); + aView.SetGlueVisible( false ); + + pOut->SetMapMode(MapMode(MapUnit::Map100thMM)); + pOut->IntersectClipRegion( aVisArea ); + + uno::Reference< frame::XModel > xModel; + rSelection >>= xModel; + + if( xModel == mpDocShell->GetModel() ) + { + aView.ShowSdrPage( mpDoc->GetSdPage( static_cast(nPageNumber) - 1, ePageKind )); + SdrPageView* pPV = aView.GetSdrPageView(); + + if( pOldSdView ) + { + SdrPageView* pOldPV = pOldSdView->GetSdrPageView(); + if( pPV && pOldPV ) + { + pPV->SetVisibleLayers( pOldPV->GetVisibleLayers() ); + pPV->SetPrintableLayers( pOldPV->GetPrintableLayers() ); + } + } + + ImplRenderPaintProc aImplRenderPaintProc( mpDoc->GetLayerAdmin(), + pPV); + + // background color for outliner :o + SdPage* pPage = pPV ? static_cast(pPV->GetPage()) : nullptr; + if( pPage ) + { + SdrOutliner& rOutl = mpDoc->GetDrawOutliner(); + bool bScreenDisplay(true); + + // #i75566# printing; suppress AutoColor BackgroundColor generation + // for visibility reasons by giving GetPageBackgroundColor() + // the needed hint + // #i75566# PDF export; suppress AutoColor BackgroundColor generation (see printing) + if (pOut && ((OUTDEV_PRINTER == pOut->GetOutDevType()) + || (OUTDEV_PDF == pOut->GetOutDevType()))) + bScreenDisplay = false; + + // #i75566# Name change GetBackgroundColor -> GetPageBackgroundColor and + // hint value if screen display. Only then the AutoColor mechanisms shall be applied + rOutl.SetBackgroundColor( pPage->GetPageBackgroundColor( pPV, bScreenDisplay ) ); + } + aView.SdrPaintView::CompleteRedraw( pOut, aRegion, &aImplRenderPaintProc ); + + if ( pPDFExtOutDevData && pPage ) + { + try + { + uno::Any aAny; + uno::Reference< drawing::XDrawPage > xPage( uno::Reference< drawing::XDrawPage >::query( pPage->getUnoPage() ) ); + if ( xPage.is() ) + { + if ( pPDFExtOutDevData->GetIsExportNotes() ) + ImplPDFExportComments( xPage, *pPDFExtOutDevData ); + uno::Reference< beans::XPropertySet > xPagePropSet( xPage, uno::UNO_QUERY ); + if( xPagePropSet.is() ) + { + // exporting object interactions to pdf + + // if necessary, the master page interactions will be exported first + bool bIsBackgroundObjectsVisible = false; // #i39428# IsBackgroundObjectsVisible not available for Draw + if ( mbImpressDoc && xPagePropSet->getPropertySetInfo()->hasPropertyByName( "IsBackgroundObjectsVisible" ) ) + xPagePropSet->getPropertyValue( "IsBackgroundObjectsVisible" ) >>= bIsBackgroundObjectsVisible; + if ( bIsBackgroundObjectsVisible && !pPDFExtOutDevData->GetIsExportNotesPages() ) + { + uno::Reference< drawing::XMasterPageTarget > xMasterPageTarget( xPage, uno::UNO_QUERY ); + if ( xMasterPageTarget.is() ) + { + uno::Reference< drawing::XDrawPage > xMasterPage = xMasterPageTarget->getMasterPage(); + if ( xMasterPage.is() ) + { + sal_Int32 i, nCount = xMasterPage->getCount(); + for ( i = 0; i < nCount; i++ ) + { + aAny = xMasterPage->getByIndex( i ); + uno::Reference< drawing::XShape > xShape; + if ( aAny >>= xShape ) + ImplPDFExportShapeInteraction( xShape, *mpDoc, *pPDFExtOutDevData ); + } + } + } + } + + // exporting slide page object interactions + sal_Int32 i, nCount = xPage->getCount(); + for ( i = 0; i < nCount; i++ ) + { + aAny = xPage->getByIndex( i ); + uno::Reference< drawing::XShape > xShape; + if ( aAny >>= xShape ) + ImplPDFExportShapeInteraction( xShape, *mpDoc, *pPDFExtOutDevData ); + } + + // exporting transition effects to pdf + if ( mbImpressDoc && !pPDFExtOutDevData->GetIsExportNotesPages() && pPDFExtOutDevData->GetIsExportTransitionEffects() ) + { + static const OUStringLiteral sEffect( u"Effect" ); + static const OUStringLiteral sSpeed ( u"Speed" ); + sal_Int32 nTime = 800; + presentation::AnimationSpeed aAs; + if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sSpeed ) ) + { + aAny = xPagePropSet->getPropertyValue( sSpeed ); + if ( aAny >>= aAs ) + { + switch( aAs ) + { + case presentation::AnimationSpeed_SLOW : nTime = 1500; break; + case presentation::AnimationSpeed_FAST : nTime = 300; break; + default: + case presentation::AnimationSpeed_MEDIUM : nTime = 800; + } + } + } + presentation::FadeEffect eFe; + vcl::PDFWriter::PageTransition eType = vcl::PDFWriter::PageTransition::Regular; + if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sEffect ) ) + { + aAny = xPagePropSet->getPropertyValue( sEffect ); + if ( aAny >>= eFe ) + { + switch( eFe ) + { + case presentation::FadeEffect_HORIZONTAL_LINES : + case presentation::FadeEffect_HORIZONTAL_CHECKERBOARD : + case presentation::FadeEffect_HORIZONTAL_STRIPES : eType = vcl::PDFWriter::PageTransition::BlindsHorizontal; break; + + case presentation::FadeEffect_VERTICAL_LINES : + case presentation::FadeEffect_VERTICAL_CHECKERBOARD : + case presentation::FadeEffect_VERTICAL_STRIPES : eType = vcl::PDFWriter::PageTransition::BlindsVertical; break; + + case presentation::FadeEffect_UNCOVER_TO_RIGHT : + case presentation::FadeEffect_UNCOVER_TO_UPPERRIGHT : + case presentation::FadeEffect_ROLL_FROM_LEFT : + case presentation::FadeEffect_FADE_FROM_UPPERLEFT : + case presentation::FadeEffect_MOVE_FROM_UPPERLEFT : + case presentation::FadeEffect_FADE_FROM_LEFT : + case presentation::FadeEffect_MOVE_FROM_LEFT : eType = vcl::PDFWriter::PageTransition::WipeLeftToRight; break; + + case presentation::FadeEffect_UNCOVER_TO_BOTTOM : + case presentation::FadeEffect_UNCOVER_TO_LOWERRIGHT : + case presentation::FadeEffect_ROLL_FROM_TOP : + case presentation::FadeEffect_FADE_FROM_UPPERRIGHT : + case presentation::FadeEffect_MOVE_FROM_UPPERRIGHT : + case presentation::FadeEffect_FADE_FROM_TOP : + case presentation::FadeEffect_MOVE_FROM_TOP : eType = vcl::PDFWriter::PageTransition::WipeTopToBottom; break; + + case presentation::FadeEffect_UNCOVER_TO_LEFT : + case presentation::FadeEffect_UNCOVER_TO_LOWERLEFT : + case presentation::FadeEffect_ROLL_FROM_RIGHT : + + case presentation::FadeEffect_FADE_FROM_LOWERRIGHT : + case presentation::FadeEffect_MOVE_FROM_LOWERRIGHT : + case presentation::FadeEffect_FADE_FROM_RIGHT : + case presentation::FadeEffect_MOVE_FROM_RIGHT : eType = vcl::PDFWriter::PageTransition::WipeRightToLeft; break; + + case presentation::FadeEffect_UNCOVER_TO_TOP : + case presentation::FadeEffect_UNCOVER_TO_UPPERLEFT : + case presentation::FadeEffect_ROLL_FROM_BOTTOM : + case presentation::FadeEffect_FADE_FROM_LOWERLEFT : + case presentation::FadeEffect_MOVE_FROM_LOWERLEFT : + case presentation::FadeEffect_FADE_FROM_BOTTOM : + case presentation::FadeEffect_MOVE_FROM_BOTTOM : eType = vcl::PDFWriter::PageTransition::WipeBottomToTop; break; + + case presentation::FadeEffect_OPEN_VERTICAL : eType = vcl::PDFWriter::PageTransition::SplitHorizontalInward; break; + case presentation::FadeEffect_CLOSE_HORIZONTAL : eType = vcl::PDFWriter::PageTransition::SplitHorizontalOutward; break; + + case presentation::FadeEffect_OPEN_HORIZONTAL : eType = vcl::PDFWriter::PageTransition::SplitVerticalInward; break; + case presentation::FadeEffect_CLOSE_VERTICAL : eType = vcl::PDFWriter::PageTransition::SplitVerticalOutward; break; + + case presentation::FadeEffect_FADE_TO_CENTER : eType = vcl::PDFWriter::PageTransition::BoxInward; break; + case presentation::FadeEffect_FADE_FROM_CENTER : eType = vcl::PDFWriter::PageTransition::BoxOutward; break; + + case presentation::FadeEffect_NONE : eType = vcl::PDFWriter::PageTransition::Regular; break; + + case presentation::FadeEffect_RANDOM : + case presentation::FadeEffect_DISSOLVE : + default: eType = vcl::PDFWriter::PageTransition::Dissolve; break; + } + } + } + + if ( xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sEffect ) || + xPagePropSet->getPropertySetInfo( )->hasPropertyByName( sSpeed ) ) + { + pPDFExtOutDevData->SetPageTransition( eType, nTime ); + } + } + } + } + + Size aPageSize( mpDoc->GetSdPage( 0, PageKind::Standard )->GetSize() ); + Point aPoint( 0, 0 ); + ::tools::Rectangle aPageRect( aPoint, aPageSize ); + + // resolving links found in this page by the method ImpEditEngine::Paint + std::vector< vcl::PDFExtOutDevBookmarkEntry >& rBookmarks = pPDFExtOutDevData->GetBookmarks(); + for ( const auto& rBookmark : rBookmarks ) + { + sal_Int32 nPage = ImplPDFGetBookmarkPage( rBookmark.aBookmark, *mpDoc ); + if ( nPage != -1 ) + { + if ( rBookmark.nLinkId != -1 ) + pPDFExtOutDevData->SetLinkDest( rBookmark.nLinkId, pPDFExtOutDevData->CreateDest( aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle ) ); + else + pPDFExtOutDevData->DescribeRegisteredDest( rBookmark.nDestId, aPageRect, nPage, vcl::PDFWriter::DestAreaType::FitRectangle ); + } + else + pPDFExtOutDevData->SetLinkURL( rBookmark.nLinkId, rBookmark.aBookmark ); + } + rBookmarks.clear(); + //---> #i56629, #i40318 + //get the page name, will be used as outline element in PDF bookmark pane + OUString aPageName = mpDoc->GetSdPage( static_cast(nPageNumber) - 1 , PageKind::Standard )->GetName(); + if( !aPageName.isEmpty() ) + { + // Destination PageNum + const sal_Int32 nDestPageNum = CalcOutputPageNum(pPDFExtOutDevData, mpDoc, nPageNumber); + + // insert the bookmark to this page into the NamedDestinations + if( pPDFExtOutDevData->GetIsExportNamedDestinations() ) + pPDFExtOutDevData->CreateNamedDest(aPageName, aPageRect, nDestPageNum); + + // add the name to the outline, (almost) same code as in sc/source/ui/unoobj/docuno.cxx + // issue #i40318. + + if( pPDFExtOutDevData->GetIsExportBookmarks() ) + { + // Destination Export + const sal_Int32 nDestId = + pPDFExtOutDevData->CreateDest(aPageRect , nDestPageNum); + + // Create a new outline item: + pPDFExtOutDevData->CreateOutlineItem( -1 , aPageName, nDestId ); + } + } + //<--- #i56629, #i40318 + } + catch (const uno::Exception&) + { + } + + } + } + else + { + uno::Reference< drawing::XShapes > xShapes; + rSelection >>= xShapes; + + if( xShapes.is() && xShapes->getCount() ) + { + SdrPageView* pPV = nullptr; + + ImplRenderPaintProc aImplRenderPaintProc( mpDoc->GetLayerAdmin(), + pOldSdView ? pOldSdView->GetSdrPageView() : nullptr); + + for( sal_uInt32 i = 0, nCount = xShapes->getCount(); i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xShape; + xShapes->getByIndex( i ) >>= xShape; + + if( xShape.is() ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pObj && pObj->getSdrPageFromSdrObject() + && aImplRenderPaintProc.IsVisible( pObj ) + && aImplRenderPaintProc.IsPrintable( pObj ) ) + { + if( !pPV ) + pPV = aView.ShowSdrPage( pObj->getSdrPageFromSdrObject() ); + + if( pPV ) + aView.MarkObj( pObj, pPV ); + } + } + } + aView.DrawMarkedObj(*pOut); + } + } +} + +DrawViewShell* SdXImpressDocument::GetViewShell() +{ + DrawViewShell* pViewSh = dynamic_cast(mpDocShell->GetViewShell()); + if (!pViewSh) + { + SAL_WARN("sd", "DrawViewShell not available!"); + return nullptr; + } + return pViewSh; +} + +void SdXImpressDocument::paintTile( VirtualDevice& rDevice, + int nOutputWidth, int nOutputHeight, + int nTilePosX, int nTilePosY, + ::tools::Long nTileWidth, ::tools::Long nTileHeight ) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + // Setup drawing layer to work properly. Since we use a custom VirtualDevice + // for the drawing, SdrPaintView::BeginCompleteRedraw() will call FindPaintWindow() + // unsuccessfully and use a temporary window that doesn't keep state. So patch + // the existing SdrPageWindow to use a temporary, and this way the state will be kept. + // Well, at least that's how I understand it based on Writer's RenderContextGuard, + // as the drawing layer classes lack documentation. + SdrPageWindow* patchedPageWindow = nullptr; + SdrPaintWindow* previousPaintWindow = nullptr; + std::unique_ptr temporaryPaintWindow; + if(SdrView* pDrawView = pViewSh->GetDrawView()) + { + if(SdrPageView* pSdrPageView = pDrawView->GetSdrPageView()) + { + patchedPageWindow = pSdrPageView->FindPageWindow(*getDocWindow()->GetOutDev()); + temporaryPaintWindow.reset(new SdrPaintWindow(*pDrawView, rDevice)); + if (patchedPageWindow) + previousPaintWindow = patchedPageWindow->patchPaintWindow(*temporaryPaintWindow); + } + } + + // Scaling. Must convert from pixels to twips. We know + // that VirtualDevices use a DPI of 96. + // We specifically calculate these scales first as we're still + // in TWIPs, and might as well minimize the number of conversions. + const Fraction scale = conversionFract(o3tl::Length::px, o3tl::Length::twip); + Fraction scaleX = Fraction(nOutputWidth, nTileWidth) * scale; + Fraction scaleY = Fraction(nOutputHeight, nTileHeight) * scale; + + // svx seems to be the only component that works natively in + // 100th mm rather than TWIP. It makes most sense just to + // convert here and in getDocumentSize, and leave the tiled + // rendering API working in TWIPs. + ::tools::Long nTileWidthHMM = convertTwipToMm100( nTileWidth ); + ::tools::Long nTileHeightHMM = convertTwipToMm100( nTileHeight ); + int nTilePosXHMM = convertTwipToMm100( nTilePosX ); + int nTilePosYHMM = convertTwipToMm100( nTilePosY ); + + MapMode aMapMode = rDevice.GetMapMode(); + aMapMode.SetMapUnit( MapUnit::Map100thMM ); + aMapMode.SetOrigin( Point( -nTilePosXHMM, + -nTilePosYHMM) ); + aMapMode.SetScaleX( scaleX ); + aMapMode.SetScaleY( scaleY ); + + rDevice.SetMapMode( aMapMode ); + + rDevice.SetOutputSizePixel( Size(nOutputWidth, nOutputHeight) ); + + Point aPoint(nTilePosXHMM, nTilePosYHMM); + Size aSize(nTileWidthHMM, nTileHeightHMM); + ::tools::Rectangle aRect(aPoint, aSize); + + pViewSh->GetView()->CompleteRedraw(&rDevice, vcl::Region(aRect)); + + LokChartHelper::PaintAllChartsOnTile(rDevice, nOutputWidth, nOutputHeight, + nTilePosX, nTilePosY, nTileWidth, nTileHeight); + + if(patchedPageWindow != nullptr) + patchedPageWindow->unpatchPaintWindow(previousPaintWindow); +} + +void SdXImpressDocument::selectPart(int nPart, int nSelect) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + pViewSh->SelectPage(nPart, nSelect); +} + +void SdXImpressDocument::moveSelectedParts(int nPosition, bool bDuplicate) +{ + // Duplicating is currently unsupported. + if (!bDuplicate) + mpDoc->MovePages(nPosition); +} + +OUString SdXImpressDocument::getPartInfo(int nPart) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return OUString(); + + const bool bIsVisible = pViewSh->IsVisible(nPart); + const bool bIsSelected = pViewSh->IsSelected(nPart); + const sal_Int16 nMasterPageCount= pViewSh->GetDoc()->GetMasterSdPageCount(pViewSh->GetPageKind()); + + OUString aPartInfo = "{ \"visible\": \"" + + OUString::number(static_cast(bIsVisible)) + + "\", \"selected\": \"" + + OUString::number(static_cast(bIsSelected)) + + "\", \"masterPageCount\": \"" + + OUString::number(nMasterPageCount) + + "\" }"; + return aPartInfo; +} + +void SdXImpressDocument::setPart( int nPart, bool bAllowChangeFocus ) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + pViewSh->SwitchPage( nPart, bAllowChangeFocus ); +} + +int SdXImpressDocument::getParts() +{ + if (!mpDoc) + return 0; + + if (isMasterViewMode()) + return mpDoc->GetMasterSdPageCount(PageKind::Standard); + + return mpDoc->GetSdPageCount(PageKind::Standard); +} + +int SdXImpressDocument::getPart() +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return 0; + + return pViewSh->GetViewShellBase().getPart(); +} + +OUString SdXImpressDocument::getPartName(int nPart) +{ + SdPage* pPage; + if (isMasterViewMode()) + pPage = mpDoc->GetMasterSdPage(nPart, PageKind::Standard); + else + pPage = mpDoc->GetSdPage(nPart, PageKind::Standard); + + if (!pPage) + { + SAL_WARN("sd", "DrawViewShell not available!"); + return OUString(); + } + + return pPage->GetName(); +} + +OUString SdXImpressDocument::getPartHash(int nPart) +{ + SdPage* pPage; + if (isMasterViewMode()) + pPage = mpDoc->GetMasterSdPage(nPart, PageKind::Standard); + else + pPage = mpDoc->GetSdPage(nPart, PageKind::Standard); + + if (!pPage) + { + SAL_WARN("sd", "DrawViewShell not available!"); + return OUString(); + } + + return OUString::number(pPage->GetHashCode()); +} + +bool SdXImpressDocument::isMasterViewMode() +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return false; + + if (pViewSh->GetDispatcher()) + { + const SfxBoolItem* isMasterViewMode = nullptr; + pViewSh->GetDispatcher()->QueryState(SID_SLIDE_MASTER_MODE, isMasterViewMode); + if (isMasterViewMode && isMasterViewMode->GetValue()) + return true; + } + return false; +} + +VclPtr SdXImpressDocument::getDocWindow() +{ + SolarMutexGuard aGuard; + DrawViewShell* pViewShell = GetViewShell(); + VclPtr pWindow; + if (pViewShell) + pWindow = pViewShell->GetActiveWindow(); + + LokChartHelper aChartHelper(pViewShell->GetViewShell()); + VclPtr pChartWindow = aChartHelper.GetWindow(); + if (pChartWindow) + pWindow = pChartWindow; + + return pWindow; +} + +void SdXImpressDocument::setPartMode( int nPartMode ) +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return; + + PageKind aPageKind( PageKind::Standard ); + switch ( nPartMode ) + { + case LOK_PARTMODE_SLIDES: + break; + case LOK_PARTMODE_NOTES: + aPageKind = PageKind::Notes; + break; + } + pViewSh->SetPageKind( aPageKind ); +} + +Size SdXImpressDocument::getDocumentSize() +{ + DrawViewShell* pViewSh = GetViewShell(); + if (!pViewSh) + return Size(); + + SdrView *pSdrView = pViewSh->GetView(); + if (!pSdrView) + return Size(); + + SdrPageView* pCurPageView = pSdrView->GetSdrPageView(); + if (!pCurPageView) + return Size(); + + Size aSize = pCurPageView->GetPageRect().GetSize(); + // Convert the size in 100th mm to TWIP + // See paintTile above for further info. + return o3tl::convert(aSize, o3tl::Length::mm100, o3tl::Length::twip); +} + +void SdXImpressDocument::getPostIts(::tools::JsonWriter& rJsonWriter) +{ + auto commentsNode = rJsonWriter.startNode("comments"); + // Return annotations on master pages too ? + const sal_uInt16 nMaxPages = mpDoc->GetPageCount(); + SdPage* pPage; + for (sal_uInt16 nPage = 0; nPage < nMaxPages; ++nPage) + { + pPage = static_cast(mpDoc->GetPage(nPage)); + const sd::AnnotationVector& aPageAnnotations = pPage->getAnnotations(); + + for (const uno::Reference& xAnnotation : aPageAnnotations) + { + sal_uInt32 nID = sd::getAnnotationId(xAnnotation); + OString nodeName = "comment" + OString::number(nID); + auto commentNode = rJsonWriter.startNode(nodeName.getStr()); + rJsonWriter.put("id", nID); + rJsonWriter.put("author", xAnnotation->getAuthor()); + rJsonWriter.put("dateTime", utl::toISO8601(xAnnotation->getDateTime())); + uno::Reference xText(xAnnotation->getTextRange()); + rJsonWriter.put("text", xText->getString()); + rJsonWriter.put("parthash", pPage->GetHashCode()); + geometry::RealPoint2D const & rPoint = xAnnotation->getPosition(); + geometry::RealSize2D const & rSize = xAnnotation->getSize(); + ::tools::Rectangle aRectangle(Point(rPoint.X * 100.0, rPoint.Y * 100.0), Size(rSize.Width * 100.0, rSize.Height * 100.0)); + aRectangle = o3tl::toTwips(aRectangle, o3tl::Length::mm100); + OString sRectangle = aRectangle.toString(); + rJsonWriter.put("rectangle", sRectangle.getStr()); + } + } +} + +void SdXImpressDocument::initializeForTiledRendering(const css::uno::Sequence& rArguments) +{ + SolarMutexGuard aGuard; + + if (DrawViewShell* pViewShell = GetViewShell()) + { + DrawView* pDrawView = pViewShell->GetDrawView(); + for (const beans::PropertyValue& rValue : rArguments) + { + if (rValue.Name == ".uno:ShowBorderShadow" && rValue.Value.has()) + pDrawView->SetPageShadowVisible(rValue.Value.get()); + else if (rValue.Name == ".uno:Author" && rValue.Value.has()) + pDrawView->SetAuthor(rValue.Value.get()); + else if (rValue.Name == ".uno:SpellOnline" && rValue.Value.has()) + mpDoc->SetOnlineSpell(rValue.Value.get()); + } + + // Disable comments if requested + SdOptions* pOptions = SD_MOD()->GetSdOptions(mpDoc->GetDocumentType()); + pOptions->SetShowComments(comphelper::LibreOfficeKit::isTiledAnnotations()); + + pViewShell->SetRuler(false); + pViewShell->SetScrollBarsVisible(false); + + if (sd::Window* pWindow = pViewShell->GetActiveWindow()) + { + // get the full page size in pixels + pWindow->EnableMapMode(); + Size aSize(pWindow->LogicToPixel(pDrawView->GetSdrPageView()->GetPage()->GetSize())); + // Disable map mode, so that it's possible to send mouse event + // coordinates in logic units + pWindow->EnableMapMode(false); + + // arrange UI elements again with new view size + pViewShell->GetParentWindow()->SetSizePixel(aSize); + pViewShell->Resize(); + } + + // Forces all images to be swapped in synchronously, this + // ensures that images are available when paintTile is called + // (whereas with async loading images start being loaded after + // we have painted the tile, resulting in an invalidate, followed + // by the tile being rerendered - which is wasteful and ugly). + pDrawView->SetSwapAsynchron(false); + } + + // when the "This document may contain formatting or content that cannot + // be saved..." dialog appears, it is auto-cancelled with tiled rendering, + // causing 'Save' being disabled; so let's always save to the original + // format + auto xChanges = comphelper::ConfigurationChanges::create(); + officecfg::Office::Common::Save::Document::WarnAlienFormat::set(false, xChanges); + xChanges->commit(); + + if (!getenv("LO_TESTNAME")) + SvtSlideSorterBarOptions().SetVisibleImpressView(true); +} + +void SdXImpressDocument::postKeyEvent(int nType, int nCharCode, int nKeyCode) +{ + SolarMutexGuard aGuard; + SfxLokHelper::postKeyEventAsync(getDocWindow(), nType, nCharCode, nKeyCode); +} + +void SdXImpressDocument::postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + constexpr double fScale = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::px); + + // check if user hit a chart which is being edited by him + LokChartHelper aChartHelper(pViewShell->GetViewShell()); + if (aChartHelper.postMouseEvent(nType, nX, nY, + nCount, nButtons, nModifier, + fScale, fScale)) + return; + + // check if the user hit a chart which is being edited by someone else + // and, if so, skip current mouse event + if (nType != LOK_MOUSEEVENT_MOUSEMOVE) + { + if (LokChartHelper::HitAny(Point(nX, nY))) + return; + } + + const Point aPos(Point(convertTwipToMm100(nX), convertTwipToMm100(nY))); + LokMouseEventData aMouseEventData(nType, aPos, nCount, MouseEventModifiers::SIMPLECLICK, + nButtons, nModifier); + SfxLokHelper::postMouseEventAsync(pViewShell->GetActiveWindow(), aMouseEventData); +} + +void SdXImpressDocument::setTextSelection(int nType, int nX, int nY) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + LokChartHelper aChartHelper(pViewShell->GetViewShell()); + if (aChartHelper.setTextSelection(nType, nX, nY)) + return; + + Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY)); + switch (nType) + { + case LOK_SETTEXTSELECTION_START: + pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/false, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_END: + pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/true, /*bClearMark=*/false); + break; + case LOK_SETTEXTSELECTION_RESET: + pViewShell->SetCursorMm100Position(aPoint, /*bPoint=*/true, /*bClearMark=*/true); + break; + default: + assert(false); + break; + } +} + +uno::Reference SdXImpressDocument::getSelection() +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return uno::Reference(); + + return pViewShell->GetSelectionTransferrable(); +} + +void SdXImpressDocument::setGraphicSelection(int nType, int nX, int nY) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + constexpr double fScale = o3tl::convert(1.0, o3tl::Length::twip, o3tl::Length::px); + + LokChartHelper aChartHelper(pViewShell->GetViewShell()); + if (aChartHelper.setGraphicSelection(nType, nX, nY, fScale, fScale)) + return; + + Point aPoint(convertTwipToMm100(nX), convertTwipToMm100(nY)); + switch (nType) + { + case LOK_SETGRAPHICSELECTION_START: + pViewShell->SetGraphicMm100Position(/*bStart=*/true, aPoint); + break; + case LOK_SETGRAPHICSELECTION_END: + pViewShell->SetGraphicMm100Position(/*bStart=*/false, aPoint); + break; + default: + assert(false); + break; + } +} + +void SdXImpressDocument::resetSelection() +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + SdrView* pSdrView = pViewShell->GetView(); + if (!pSdrView) + return; + + if (pSdrView->IsTextEdit()) + { + // Reset the editeng selection. + pSdrView->UnmarkAll(); + // Finish editing. + pSdrView->SdrEndTextEdit(); + } + // Reset graphic selection. + pSdrView->UnmarkAll(); +} + +void SdXImpressDocument::setClientVisibleArea(const ::tools::Rectangle& rRectangle) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + pViewShell->GetViewShellBase().setLOKVisibleArea(rRectangle); +} + +void SdXImpressDocument::setClipboard(const uno::Reference& xClipboard) +{ + SolarMutexGuard aGuard; + + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return; + + pViewShell->GetActiveWindow()->SetClipboard(xClipboard); +} + +bool SdXImpressDocument::isMimeTypeSupported() +{ + SolarMutexGuard aGuard; + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return false; + + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromSystemClipboard(pViewShell->GetActiveWindow())); + return EditEngine::HasValidData(aDataHelper.GetTransferable()); +} + +PointerStyle SdXImpressDocument::getPointer() +{ + SolarMutexGuard aGuard; + DrawViewShell* pViewShell = GetViewShell(); + if (!pViewShell) + return PointerStyle::Arrow; + + Window* pWindow = pViewShell->GetActiveWindow(); + if (!pWindow) + return PointerStyle::Arrow; + + return pWindow->GetPointer(); +} + +uno::Reference< i18n::XForbiddenCharacters > SdXImpressDocument::getForbiddenCharsTable() +{ + uno::Reference< i18n::XForbiddenCharacters > xForb(mxForbiddenCharacters); + + if( !xForb.is() ) + mxForbiddenCharacters = xForb = new SdUnoForbiddenCharsTable( mpDoc ); + + return xForb; +} + +void SdXImpressDocument::initializeDocument() +{ + if( mbClipBoard ) + return; + + switch( mpDoc->GetPageCount() ) + { + case 1: + { + // nasty hack to detect clipboard document + mbClipBoard = true; + break; + } + case 0: + { + mpDoc->CreateFirstPages(); + mpDoc->StopWorkStartupDelay(); + break; + } + } +} + +SdrModel& SdXImpressDocument::getSdrModelFromUnoModel() const +{ + OSL_ENSURE(GetDoc(), "No SdrModel in draw/Impress, should not happen"); + return *GetDoc(); // TTTT should be reference +} + +void SAL_CALL SdXImpressDocument::dispose() +{ + if( mbDisposed ) + return; + + ::SolarMutexGuard aGuard; + + if( mpDoc ) + { + EndListening( *mpDoc ); + mpDoc = nullptr; + } + + // Call the base class dispose() before setting the mbDisposed flag + // to true. The reason for this is that if close() has not yet been + // called this is done in SfxBaseModel::dispose(). At the end of + // that dispose() is called again. It is important to forward this + // second dispose() to the base class, too. + // As a consequence the following code has to be able to be run twice. + SfxBaseModel::dispose(); + mbDisposed = true; + + uno::Reference< container::XNameAccess > xLinks( mxLinks ); + if( xLinks.is() ) + { + uno::Reference< lang::XComponent > xComp( xLinks, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xLinks = nullptr; + } + + uno::Reference< drawing::XDrawPages > xDrawPagesAccess( mxDrawPagesAccess ); + if( xDrawPagesAccess.is() ) + { + uno::Reference< lang::XComponent > xComp( xDrawPagesAccess, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xDrawPagesAccess = nullptr; + } + + uno::Reference< drawing::XDrawPages > xMasterPagesAccess( mxMasterPagesAccess ); + if( xDrawPagesAccess.is() ) + { + uno::Reference< lang::XComponent > xComp( xMasterPagesAccess, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xDrawPagesAccess = nullptr; + } + + uno::Reference< container::XNameAccess > xLayerManager( mxLayerManager ); + if( xLayerManager.is() ) + { + uno::Reference< lang::XComponent > xComp( xLayerManager, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xLayerManager = nullptr; + } + + uno::Reference< container::XNameContainer > xCustomPresentationAccess( mxCustomPresentationAccess ); + if( xCustomPresentationAccess.is() ) + { + uno::Reference< lang::XComponent > xComp( xCustomPresentationAccess, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + + xCustomPresentationAccess = nullptr; + } + + mxDashTable = nullptr; + mxGradientTable = nullptr; + mxHatchTable = nullptr; + mxBitmapTable = nullptr; + mxTransGradientTable = nullptr; + mxMarkerTable = nullptr; + mxDrawingPool = nullptr; +} + + +SdDrawPagesAccess::SdDrawPagesAccess( SdXImpressDocument& rMyModel ) noexcept +: mpModel( &rMyModel) +{ +} + +SdDrawPagesAccess::~SdDrawPagesAccess() noexcept +{ +} + +// XIndexAccess +sal_Int32 SAL_CALL SdDrawPagesAccess::getCount() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + return mpModel->mpDoc->GetSdPageCount( PageKind::Standard ); +} + +uno::Any SAL_CALL SdDrawPagesAccess::getByIndex( sal_Int32 Index ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + uno::Any aAny; + + if( (Index < 0) || (Index >= mpModel->mpDoc->GetSdPageCount( PageKind::Standard ) ) ) + throw lang::IndexOutOfBoundsException(); + + SdPage* pPage = mpModel->mpDoc->GetSdPage( static_cast(Index), PageKind::Standard ); + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + aAny <<= xDrawPage; + } + + return aAny; +} + +// XNameAccess +uno::Any SAL_CALL SdDrawPagesAccess::getByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + if( !aName.isEmpty() ) + { + const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard ); + sal_uInt16 nPage; + for( nPage = 0; nPage < nCount; nPage++ ) + { + SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard ); + if(nullptr == pPage) + continue; + + if( aName == SdDrawPage::getPageApiName( pPage ) ) + { + uno::Any aAny; + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + aAny <<= xDrawPage; + return aAny; + } + } + } + + throw container::NoSuchElementException(); +} + +uno::Sequence< OUString > SAL_CALL SdDrawPagesAccess::getElementNames() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard ); + uno::Sequence< OUString > aNames( nCount ); + OUString* pNames = aNames.getArray(); + + sal_uInt16 nPage; + for( nPage = 0; nPage < nCount; nPage++ ) + { + SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard ); + *pNames++ = SdDrawPage::getPageApiName( pPage ); + } + + return aNames; +} + +sal_Bool SAL_CALL SdDrawPagesAccess::hasByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + const sal_uInt16 nCount = mpModel->mpDoc->GetSdPageCount( PageKind::Standard ); + sal_uInt16 nPage; + for( nPage = 0; nPage < nCount; nPage++ ) + { + SdPage* pPage = mpModel->mpDoc->GetSdPage( nPage, PageKind::Standard ); + if(nullptr == pPage) + continue; + + if( aName == SdDrawPage::getPageApiName( pPage ) ) + return true; + } + + return false; +} + +// XElementAccess +uno::Type SAL_CALL SdDrawPagesAccess::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdDrawPagesAccess::hasElements() +{ + return getCount() > 0; +} + +// XDrawPages + +/** + * Creates a new page with model at the specified position. + * @returns corresponding SdDrawPage + */ +uno::Reference< drawing::XDrawPage > SAL_CALL SdDrawPagesAccess::insertNewByIndex( sal_Int32 nIndex ) +{ + ::SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("insertNewByIndex"); + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + if( mpModel->mpDoc ) + { + SdPage* pPage = mpModel->InsertSdPage( static_cast(nIndex), false ); + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + return xDrawPage; + } + } + uno::Reference< drawing::XDrawPage > xDrawPage; + return xDrawPage; +} + +/** + * Removes the specified SdDrawPage from the model and the internal list. It + * only works, if there is at least one *normal* page in the model after + * removing this page. + */ +void SAL_CALL SdDrawPagesAccess::remove( const uno::Reference< drawing::XDrawPage >& xPage ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel || mpModel->mpDoc == nullptr ) + throw lang::DisposedException(); + + SdDrawDocument& rDoc = *mpModel->mpDoc; + + sal_uInt16 nPageCount = rDoc.GetSdPageCount( PageKind::Standard ); + if( nPageCount > 1 ) + { + // get pPage from xPage and determine the Id (nPos ) afterwards + SdDrawPage* pSvxPage = comphelper::getFromUnoTunnel( xPage ); + if( pSvxPage ) + { + SdPage* pPage = static_cast(pSvxPage->GetSdrPage()); + if(pPage && ( pPage->GetPageKind() == PageKind::Standard ) ) + { + sal_uInt16 nPage = pPage->GetPageNum(); + + SdPage* pNotesPage = static_cast< SdPage* >( rDoc.GetPage( nPage+1 ) ); + + bool bUndo = rDoc.IsUndoEnabled(); + if( bUndo ) + { + // Add undo actions and delete the pages. The order of adding + // the undo actions is important. + rDoc.BegUndo( SdResId( STR_UNDO_DELETEPAGES ) ); + rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage)); + rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + } + + rDoc.RemovePage( nPage ); // the page + rDoc.RemovePage( nPage ); // the notes page + + if( bUndo ) + { + rDoc.EndUndo(); + } + } + } + } + + mpModel->SetModified(); +} + +// XServiceInfo + +OUString SAL_CALL SdDrawPagesAccess::getImplementationName( ) +{ + return "SdDrawPagesAccess"; +} + +sal_Bool SAL_CALL SdDrawPagesAccess::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdDrawPagesAccess::getSupportedServiceNames( ) +{ + return { "com.sun.star.drawing.DrawPages" }; +} + +// XComponent +void SAL_CALL SdDrawPagesAccess::dispose( ) +{ + mpModel = nullptr; +} + +void SAL_CALL SdDrawPagesAccess::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +void SAL_CALL SdDrawPagesAccess::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + + +SdMasterPagesAccess::SdMasterPagesAccess( SdXImpressDocument& rMyModel ) noexcept +: mpModel(&rMyModel) +{ +} + +SdMasterPagesAccess::~SdMasterPagesAccess() noexcept +{ +} + +// XComponent +void SAL_CALL SdMasterPagesAccess::dispose( ) +{ + mpModel = nullptr; +} + +void SAL_CALL SdMasterPagesAccess::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +void SAL_CALL SdMasterPagesAccess::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +// XIndexAccess +sal_Int32 SAL_CALL SdMasterPagesAccess::getCount() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel->mpDoc ) + throw lang::DisposedException(); + + return mpModel->mpDoc->GetMasterSdPageCount(PageKind::Standard); +} + +/** + * Provides a drawing::XDrawPage interface for accessing the Masterpage at the + * specified position in the model. + */ +uno::Any SAL_CALL SdMasterPagesAccess::getByIndex( sal_Int32 Index ) +{ + ::SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("SdMasterPagesAccess::getByIndex"); + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + uno::Any aAny; + + if( (Index < 0) || (Index >= mpModel->mpDoc->GetMasterSdPageCount( PageKind::Standard ) ) ) + throw lang::IndexOutOfBoundsException(); + + SdPage* pPage = mpModel->mpDoc->GetMasterSdPage( static_cast(Index), PageKind::Standard ); + if( pPage ) + { + uno::Reference< drawing::XDrawPage > xDrawPage( pPage->getUnoPage(), uno::UNO_QUERY ); + aAny <<= xDrawPage; + } + + return aAny; +} + +// XElementAccess +uno::Type SAL_CALL SdMasterPagesAccess::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdMasterPagesAccess::hasElements() +{ + return getCount() > 0; +} + +// XDrawPages +uno::Reference< drawing::XDrawPage > SAL_CALL SdMasterPagesAccess::insertNewByIndex( sal_Int32 nInsertPos ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + uno::Reference< drawing::XDrawPage > xDrawPage; + + SdDrawDocument* pDoc = mpModel->mpDoc; + if( pDoc ) + { + // calculate internal index and check for range errors + const sal_Int32 nMPageCount = pDoc->GetMasterPageCount(); + nInsertPos = nInsertPos * 2 + 1; + if( nInsertPos < 0 || nInsertPos > nMPageCount ) + nInsertPos = nMPageCount; + + // now generate a unique name for the new masterpage + const OUString aStdPrefix( SdResId(STR_LAYOUT_DEFAULT_NAME) ); + OUString aPrefix( aStdPrefix ); + + bool bUnique = true; + + std::vector aPageNames; + for (sal_Int32 nMaster = 1; nMaster < nMPageCount; ++nMaster) + { + const SdPage* pPage = static_cast(pDoc->GetMasterPage(static_cast(nMaster))); + if (!pPage) + continue; + aPageNames.push_back(pPage->GetName()); + if (aPageNames.back() == aPrefix) + bUnique = false; + } + + sal_Int32 i = 0; + while (!bUnique) + { + aPrefix = aStdPrefix + " " + OUString::number(++i); + bUnique = std::find(aPageNames.begin(), aPageNames.end(), aPrefix) == aPageNames.end(); + } + + OUString aLayoutName = aPrefix + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + + // create styles + static_cast(pDoc->GetStyleSheetPool())->CreateLayoutStyleSheets( aPrefix ); + + // get the first page for initial size and border settings + SdPage* pPage = mpModel->mpDoc->GetSdPage( sal_uInt16(0), PageKind::Standard ); + SdPage* pRefNotesPage = mpModel->mpDoc->GetSdPage( sal_uInt16(0), PageKind::Notes); + + // create and insert new draw masterpage + rtl::Reference pMPage = mpModel->mpDoc->AllocSdPage(true); + pMPage->SetSize( pPage->GetSize() ); + pMPage->SetBorder( pPage->GetLeftBorder(), + pPage->GetUpperBorder(), + pPage->GetRightBorder(), + pPage->GetLowerBorder() ); + pMPage->SetLayoutName( aLayoutName ); + pDoc->InsertMasterPage(pMPage.get(), static_cast(nInsertPos)); + + { + // ensure default MasterPage fill + pMPage->EnsureMasterPageDefaultBackground(); + } + + xDrawPage.set( pMPage->getUnoPage(), uno::UNO_QUERY ); + + // create and insert new notes masterpage + rtl::Reference pMNotesPage = mpModel->mpDoc->AllocSdPage(true); + pMNotesPage->SetSize( pRefNotesPage->GetSize() ); + pMNotesPage->SetPageKind(PageKind::Notes); + pMNotesPage->SetBorder( pRefNotesPage->GetLeftBorder(), + pRefNotesPage->GetUpperBorder(), + pRefNotesPage->GetRightBorder(), + pRefNotesPage->GetLowerBorder() ); + pMNotesPage->SetLayoutName( aLayoutName ); + pDoc->InsertMasterPage(pMNotesPage.get(), static_cast(nInsertPos) + 1); + pMNotesPage->SetAutoLayout(AUTOLAYOUT_NOTES, true, true); + mpModel->SetModified(); + } + + return xDrawPage; +} + +/** + * Removes the specified SdMasterPage from the model and the internal list. It + * only works, if there is no *normal* page using this page as MasterPage in + * the model. + */ +void SAL_CALL SdMasterPagesAccess::remove( const uno::Reference< drawing::XDrawPage >& xPage ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel || mpModel->mpDoc == nullptr ) + throw lang::DisposedException(); + + SdMasterPage* pSdPage = comphelper::getFromUnoTunnel( xPage ); + if(pSdPage == nullptr) + return; + + SdPage* pPage = dynamic_cast< SdPage* > (pSdPage->GetSdrPage()); + + DBG_ASSERT( pPage && pPage->IsMasterPage(), "SdMasterPage is not masterpage?"); + + if( !pPage || !pPage->IsMasterPage() || (mpModel->mpDoc->GetMasterPageUserCount(pPage) > 0)) + return; //Todo: this should be excepted + + // only standard pages can be removed directly + if( pPage->GetPageKind() != PageKind::Standard ) + return; + + sal_uInt16 nPage = pPage->GetPageNum(); + + SdDrawDocument& rDoc = *mpModel->mpDoc; + + SdPage* pNotesPage = static_cast< SdPage* >( rDoc.GetMasterPage( nPage+1 ) ); + + bool bUndo = rDoc.IsUndoEnabled(); + if( bUndo ) + { + // Add undo actions and delete the pages. The order of adding + // the undo actions is important. + rDoc.BegUndo( SdResId( STR_UNDO_DELETEPAGES ) ); + rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pNotesPage)); + rDoc.AddUndo(rDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + } + + // remove both pages + rDoc.RemoveMasterPage( nPage ); + rDoc.RemoveMasterPage( nPage ); + + if( bUndo ) + { + rDoc.EndUndo(); + } +} + +// XServiceInfo + +OUString SAL_CALL SdMasterPagesAccess::getImplementationName( ) +{ + return "SdMasterPagesAccess"; +} + +sal_Bool SAL_CALL SdMasterPagesAccess::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdMasterPagesAccess::getSupportedServiceNames( ) +{ + return { "com.sun.star.drawing.MasterPages" }; +} + + +SdDocLinkTargets::SdDocLinkTargets( SdXImpressDocument& rMyModel ) noexcept +: mpModel( &rMyModel ) +{ +} + +SdDocLinkTargets::~SdDocLinkTargets() noexcept +{ +} + +// XComponent +void SAL_CALL SdDocLinkTargets::dispose( ) +{ + mpModel = nullptr; +} + +void SAL_CALL SdDocLinkTargets::addEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +void SAL_CALL SdDocLinkTargets::removeEventListener( const uno::Reference< lang::XEventListener >& ) +{ + OSL_FAIL( "not implemented!" ); +} + +// XNameAccess +uno::Any SAL_CALL SdDocLinkTargets::getByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + SdPage* pPage = FindPage( aName ); + + if( pPage == nullptr ) + throw container::NoSuchElementException(); + + uno::Any aAny; + + uno::Reference< beans::XPropertySet > xProps( pPage->getUnoPage(), uno::UNO_QUERY ); + if( xProps.is() ) + aAny <<= xProps; + + return aAny; +} + +uno::Sequence< OUString > SAL_CALL SdDocLinkTargets::getElementNames() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + SdDrawDocument* pDoc = mpModel->GetDoc(); + if( pDoc == nullptr ) + { + return { }; + } + + if( pDoc->GetDocumentType() == DocumentType::Draw ) + { + const sal_uInt16 nMaxPages = pDoc->GetSdPageCount( PageKind::Standard ); + const sal_uInt16 nMaxMasterPages = pDoc->GetMasterSdPageCount( PageKind::Standard ); + + uno::Sequence< OUString > aSeq( nMaxPages + nMaxMasterPages ); + OUString* pStr = aSeq.getArray(); + + sal_uInt16 nPage; + // standard pages + for( nPage = 0; nPage < nMaxPages; nPage++ ) + *pStr++ = pDoc->GetSdPage( nPage, PageKind::Standard )->GetName(); + + // master pages + for( nPage = 0; nPage < nMaxMasterPages; nPage++ ) + *pStr++ = pDoc->GetMasterSdPage( nPage, PageKind::Standard )->GetName(); + return aSeq; + } + else + { + const sal_uInt16 nMaxPages = pDoc->GetPageCount(); + const sal_uInt16 nMaxMasterPages = pDoc->GetMasterPageCount(); + + uno::Sequence< OUString > aSeq( nMaxPages + nMaxMasterPages ); + OUString* pStr = aSeq.getArray(); + + sal_uInt16 nPage; + // standard pages + for( nPage = 0; nPage < nMaxPages; nPage++ ) + *pStr++ = static_cast(pDoc->GetPage( nPage ))->GetName(); + + // master pages + for( nPage = 0; nPage < nMaxMasterPages; nPage++ ) + *pStr++ = static_cast(pDoc->GetMasterPage( nPage ))->GetName(); + return aSeq; + } +} + +sal_Bool SAL_CALL SdDocLinkTargets::hasByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + return FindPage( aName ) != nullptr; +} + +// container::XElementAccess +uno::Type SAL_CALL SdDocLinkTargets::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdDocLinkTargets::hasElements() +{ + ::SolarMutexGuard aGuard; + + if( nullptr == mpModel ) + throw lang::DisposedException(); + + return mpModel->GetDoc() != nullptr; +} + +SdPage* SdDocLinkTargets::FindPage( std::u16string_view rName ) const +{ + SdDrawDocument* pDoc = mpModel->GetDoc(); + if( pDoc == nullptr ) + return nullptr; + + const sal_uInt16 nMaxPages = pDoc->GetPageCount(); + const sal_uInt16 nMaxMasterPages = pDoc->GetMasterPageCount(); + + sal_uInt16 nPage; + SdPage* pPage; + + const bool bDraw = pDoc->GetDocumentType() == DocumentType::Draw; + + // standard pages + for( nPage = 0; nPage < nMaxPages; nPage++ ) + { + pPage = static_cast(pDoc->GetPage( nPage )); + if( (pPage->GetName() == rName) && (!bDraw || (pPage->GetPageKind() == PageKind::Standard)) ) + return pPage; + } + + // master pages + for( nPage = 0; nPage < nMaxMasterPages; nPage++ ) + { + pPage = static_cast(pDoc->GetMasterPage( nPage )); + if( (pPage->GetName() == rName) && (!bDraw || (pPage->GetPageKind() == PageKind::Standard)) ) + return pPage; + } + + return nullptr; +} + +// XServiceInfo +OUString SAL_CALL SdDocLinkTargets::getImplementationName() +{ + return "SdDocLinkTargets"; +} + +sal_Bool SAL_CALL SdDocLinkTargets::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdDocLinkTargets::getSupportedServiceNames() +{ + return { "com.sun.star.document.LinkTargets" }; +} + +rtl::Reference< SdXImpressDocument > SdXImpressDocument::GetModel( SdDrawDocument const & rDocument ) +{ + rtl::Reference< SdXImpressDocument > xRet; + ::sd::DrawDocShell* pDocShell(rDocument.GetDocSh()); + if( pDocShell ) + { + uno::Reference xModel(pDocShell->GetModel()); + + xRet.set( dynamic_cast< SdXImpressDocument* >( xModel.get() ) ); + } + + return xRet; +} + +void NotifyDocumentEvent( SdDrawDocument const & rDocument, const OUString& rEventName ) +{ + rtl::Reference< SdXImpressDocument > xModel( SdXImpressDocument::GetModel( rDocument ) ); + + if( xModel.is() ) + { + uno::Reference< uno::XInterface > xSource( static_cast( xModel.get() ) ); + css::document::EventObject aEvent( xSource, rEventName ); + xModel->notifyEvent(aEvent ); + } +} + +void NotifyDocumentEvent( SdDrawDocument const & rDocument, const OUString& rEventName, const uno::Reference< uno::XInterface >& xSource ) +{ + rtl::Reference< SdXImpressDocument > xModel( SdXImpressDocument::GetModel( rDocument ) ); + + if( xModel.is() ) + { + css::document::EventObject aEvent( xSource, rEventName ); + xModel->notifyEvent(aEvent ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unomodule.cxx b/sd/source/ui/unoidl/unomodule.cxx new file mode 100644 index 000000000..d862f7c9d --- /dev/null +++ b/sd/source/ui/unoidl/unomodule.cxx @@ -0,0 +1,132 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include "unomodule.hxx" +#include +#include +#include +#include + +using namespace ::com::sun::star; + + // XNotifyingDispatch +void SAL_CALL SdUnoModule::dispatchWithNotification( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& aArgs, const uno::Reference< frame::XDispatchResultListener >& xListener ) +{ + // there is no guarantee, that we are holded alive during this method! + // May the outside dispatch container will be updated by a CONTEXT_CHANGED + // asynchronous ... + uno::Reference< uno::XInterface > xThis(static_cast< frame::XNotifyingDispatch* >(this)); + + SolarMutexGuard aGuard; + SdDLL::Init(); + const SfxSlot* pSlot = SD_MOD()->GetInterface()->GetSlot( aURL.Complete ); + + sal_Int16 aState = frame::DispatchResultState::DONTKNOW; + if ( !pSlot ) + aState = frame::DispatchResultState::FAILURE; + else + { + SfxRequest aReq( pSlot, aArgs, SfxCallMode::SYNCHRON, SD_MOD()->GetPool() ); + const SfxPoolItem* pResult = SD_MOD()->ExecuteSlot( aReq ); + if ( pResult ) + aState = frame::DispatchResultState::SUCCESS; + else + aState = frame::DispatchResultState::FAILURE; + } + + if ( xListener.is() ) + { + xListener->dispatchFinished( + frame::DispatchResultEvent( + xThis, aState, uno::Any())); + } +} + // XDispatch +void SAL_CALL SdUnoModule::dispatch( const util::URL& aURL, const uno::Sequence< beans::PropertyValue >& aArgs ) +{ + dispatchWithNotification(aURL, aArgs, uno::Reference< frame::XDispatchResultListener >()); +} + +void SAL_CALL SdUnoModule::addStatusListener(const uno::Reference< frame::XStatusListener > &, const util::URL&) +{ +} + +void SAL_CALL SdUnoModule::removeStatusListener(const uno::Reference< frame::XStatusListener > &, const util::URL&) +{ +} + +uno::Sequence< uno::Reference< frame::XDispatch > > SAL_CALL SdUnoModule::queryDispatches( const uno::Sequence< frame::DispatchDescriptor >& seqDescripts ) +{ + sal_Int32 nCount = seqDescripts.getLength(); + uno::Sequence< uno::Reference< frame::XDispatch > > lDispatcher( nCount ); + + std::transform(seqDescripts.begin(), seqDescripts.end(), lDispatcher.getArray(), + [this](const frame::DispatchDescriptor& rDescr) -> uno::Reference { + return queryDispatch(rDescr.FeatureURL, rDescr.FrameName, rDescr.SearchFlags); }); + + return lDispatcher; +} + +// XDispatchProvider +uno::Reference< frame::XDispatch > SAL_CALL SdUnoModule::queryDispatch( const util::URL& aURL, const OUString&, sal_Int32 ) +{ + SolarMutexGuard aGuard; + SdDLL::Init(); + const SfxSlot* pSlot = SD_MOD()->GetInterface()->GetSlot( aURL.Complete ); + + uno::Reference< frame::XDispatch > xSlot; + if ( pSlot ) + xSlot = this; + + return xSlot; +} + +// XServiceInfo +OUString SAL_CALL SdUnoModule::getImplementationName( ) +{ + return "com.sun.star.comp.Draw.DrawingModule"; +} + +sal_Bool SAL_CALL SdUnoModule::supportsService( const OUString& sServiceName ) +{ + return cppu::supportsService(this, sServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdUnoModule::getSupportedServiceNames( ) +{ + return { "com.sun.star.drawing.ModuleDispatcher" }; +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_Draw_DrawingModule_get_implementation(css::uno::XComponentContext* , + css::uno::Sequence const &) +{ + SolarMutexGuard aGuard; + + return cppu::acquire(new SdUnoModule); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unomodule.hxx b/sd/source/ui/unoidl/unomodule.hxx new file mode 100644 index 000000000..bc78c6b19 --- /dev/null +++ b/sd/source/ui/unoidl/unomodule.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace com::sun::star::beans { struct PropertyValue; } +namespace com::sun::star::frame { struct DispatchDescriptor; } + +class SdUnoModule : public ::cppu::WeakImplHelper< css::frame::XDispatchProvider, css::frame::XNotifyingDispatch, css::lang::XServiceInfo > +{ +public: + SdUnoModule() {} + + // XnotifyingDispatch + virtual void SAL_CALL dispatchWithNotification( const css::util::URL& URL, const css::uno::Sequence< css::beans::PropertyValue >& Arguments, const css::uno::Reference< css::frame::XDispatchResultListener >& Listener ) override; + + // XDispatch + virtual void SAL_CALL dispatch( const css::util::URL& aURL, const css::uno::Sequence< css::beans::PropertyValue >& aArgs ) override; + virtual void SAL_CALL addStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) override; + virtual void SAL_CALL removeStatusListener(const css::uno::Reference< css::frame::XStatusListener > & xControl, const css::util::URL& aURL) override; + + // XDispatchProvider + virtual css::uno::Sequence< css::uno::Reference< css::frame::XDispatch > > SAL_CALL queryDispatches( const css::uno::Sequence< css::frame::DispatchDescriptor >& seqDescriptor ) override ; + virtual css::uno::Reference< css::frame::XDispatch > SAL_CALL queryDispatch( const css::util::URL & aURL , + const OUString & sTargetFrameName, + sal_Int32 eSearchFlags ) override; + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unoobj.cxx b/sd/source/ui/unoidl/unoobj.cxx new file mode 100644 index 000000000..6ed6729f9 --- /dev/null +++ b/sd/source/ui/unoidl/unoobj.cxx @@ -0,0 +1,1627 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "unoobj.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::sd; +using namespace ::com::sun::star; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::animations; + +using ::com::sun::star::uno::Any; +using ::com::sun::star::drawing::XShape; + +#define WID_EFFECT 1 +#define WID_SPEED 2 +#define WID_TEXTEFFECT 3 +#define WID_BOOKMARK 4 +#define WID_CLICKACTION 5 +#define WID_PLAYFULL 6 +#define WID_SOUNDFILE 7 +#define WID_SOUNDON 8 +#define WID_BLUESCREEN 9 +#define WID_VERB 10 +#define WID_DIMCOLOR 11 +#define WID_DIMHIDE 12 +#define WID_DIMPREV 13 +#define WID_PRESORDER 14 +#define WID_STYLE 15 +#define WID_ANIMPATH 16 +#define WID_IMAGEMAP 17 +#define WID_ISANIMATION 18 +#define WID_THAT_NEED_ANIMINFO 19 + +#define WID_ISEMPTYPRESOBJ 20 +#define WID_ISPRESOBJ 21 +#define WID_MASTERDEPEND 22 + +#define WID_NAVORDER 23 +#define WID_PLACEHOLDERTEXT 24 +#define WID_LEGACYFRAGMENT 25 + +#define IMPRESS_MAP_ENTRIES \ + { u"" UNO_NAME_OBJ_LEGACYFRAGMENT,WID_LEGACYFRAGMENT, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_ANIMATIONPATH, WID_ANIMPATH, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_BOOKMARK, WID_BOOKMARK, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_DIMCOLOR, WID_DIMCOLOR, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_DIMHIDE, WID_DIMHIDE, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_DIMPREV, WID_DIMPREV, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_EFFECT, WID_EFFECT, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_ISEMPTYPRESOBJ,WID_ISEMPTYPRESOBJ, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_ISPRESOBJ, WID_ISPRESOBJ, cppu::UnoType::get(), css::beans::PropertyAttribute::READONLY, 0},\ + { u"" UNO_NAME_OBJ_MASTERDEPENDENT,WID_MASTERDEPEND, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_CLICKACTION, WID_CLICKACTION, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_PLAYFULL, WID_PLAYFULL, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_PRESORDER, WID_PRESORDER, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_STYLE, WID_STYLE, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},\ + { u"" UNO_NAME_OBJ_SOUNDFILE, WID_SOUNDFILE, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_SOUNDON, WID_SOUNDON, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_SPEED, WID_SPEED, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_TEXTEFFECT, WID_TEXTEFFECT, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_BLUESCREEN, WID_BLUESCREEN, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_VERB, WID_VERB, cppu::UnoType::get(), 0, 0},\ + { u"IsAnimation", WID_ISANIMATION, cppu::UnoType::get(), 0, 0},\ + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType::get(), 0, 0},\ + { u"PlaceholderText", WID_PLACEHOLDERTEXT, cppu::UnoType::get(), 0, 0},\ + { u"", 0, css::uno::Type(), 0, 0 } + + static const SfxItemPropertyMapEntry* lcl_GetImpress_SdXShapePropertyGraphicMap_Impl() + { + + static const SfxItemPropertyMapEntry aImpress_SdXShapePropertyGraphicMap_Impl[] = + { + { u"ImageMap", WID_IMAGEMAP, cppu::UnoType::get(), 0, 0 }, + IMPRESS_MAP_ENTRIES + }; + return aImpress_SdXShapePropertyGraphicMap_Impl; + } + + static const SfxItemPropertyMapEntry* lcl_GetImpress_SdXShapePropertySimpleMap_Impl() + { + + static const SfxItemPropertyMapEntry aImpress_SdXShapePropertySimpleMap_Impl[] = + { + IMPRESS_MAP_ENTRIES + }; + return aImpress_SdXShapePropertySimpleMap_Impl; + } + + #define DRAW_MAP_ENTRIES\ + { u"" UNO_NAME_OBJ_BOOKMARK, WID_BOOKMARK, cppu::UnoType::get(), 0, 0},\ + { u"" UNO_NAME_OBJ_CLICKACTION, WID_CLICKACTION, cppu::UnoType::get(),0, 0},\ + { u"" UNO_NAME_OBJ_STYLE, WID_STYLE, cppu::UnoType::get(), css::beans::PropertyAttribute::MAYBEVOID, 0},\ + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType::get(), 0, 0},\ + { u"", 0, css::uno::Type(), 0, 0 } + + static const SfxItemPropertyMapEntry* lcl_GetDraw_SdXShapePropertySimpleMap_Impl() + { + static const SfxItemPropertyMapEntry aDraw_SdXShapePropertyMap_Impl[] = + { + DRAW_MAP_ENTRIES + }; + return aDraw_SdXShapePropertyMap_Impl; + } + static const SfxItemPropertyMapEntry* lcl_GetDraw_SdXShapePropertyGraphicMap_Impl() + { + static const SfxItemPropertyMapEntry aDraw_SdXShapePropertyGraphicMap_Impl[] = + { + { u"ImageMap", WID_IMAGEMAP, cppu::UnoType::get(), 0, 0 }, + DRAW_MAP_ENTRIES + }; + return aDraw_SdXShapePropertyGraphicMap_Impl; + } + static const SfxItemPropertyMapEntry* lcl_ImplGetShapePropertyMap( bool bImpress, bool bGraphicObj ) + { + const SfxItemPropertyMapEntry* pRet = nullptr; + if( bImpress ) + { + if( bGraphicObj ) + pRet = lcl_GetImpress_SdXShapePropertyGraphicMap_Impl(); + else + pRet = lcl_GetImpress_SdXShapePropertySimpleMap_Impl(); + } + else + { + if( bGraphicObj ) + pRet = lcl_GetDraw_SdXShapePropertyGraphicMap_Impl(); + else + pRet = lcl_GetDraw_SdXShapePropertySimpleMap_Impl(); + } + return pRet; + + } + static const SvxItemPropertySet* lcl_ImplGetShapePropertySet( bool bImpress, bool bGraphicObj ) + { + const SvxItemPropertySet* pRet = nullptr; + if( bImpress ) + { + if( bGraphicObj ) + { + static SvxItemPropertySet aImpress_SdXShapePropertyGraphicSet_Impl( lcl_GetImpress_SdXShapePropertyGraphicMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool()); + pRet = &aImpress_SdXShapePropertyGraphicSet_Impl; + } + else + { + static SvxItemPropertySet aImpress_SdXShapePropertySet_Impl(lcl_GetImpress_SdXShapePropertySimpleMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool()); + pRet = &aImpress_SdXShapePropertySet_Impl; + } + } + else + { + if( bGraphicObj ) + { + static SvxItemPropertySet aDraw_SdXShapePropertyGraphicSet_Impl(lcl_GetDraw_SdXShapePropertyGraphicMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool()); + pRet = &aDraw_SdXShapePropertyGraphicSet_Impl; + } + else + { + static SvxItemPropertySet aDraw_SdXShapePropertySet_Impl( lcl_GetDraw_SdXShapePropertySimpleMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool()); + pRet = &aDraw_SdXShapePropertySet_Impl; + } + } + return pRet; + } + static const SfxItemPropertyMapEntry* lcl_GetEmpty_SdXShapePropertyMap_Impl() + { + static const SfxItemPropertyMapEntry aEmpty_SdXShapePropertyMap_Impl[] = + { + { u"", 0, css::uno::Type(), 0, 0 } + }; + return aEmpty_SdXShapePropertyMap_Impl; + } + + static const SvxItemPropertySet* lcl_GetEmpty_SdXShapePropertySet_Impl() + { + static SvxItemPropertySet aEmptyPropSet( lcl_GetEmpty_SdXShapePropertyMap_Impl(), SdrObject::GetGlobalDrawObjectItemPool() ); + return &aEmptyPropSet; + } +const SvEventDescription* ImplGetSupportedMacroItems() +{ + static const SvEventDescription aMacroDescriptionsImpl[] = + { + { SvMacroItemId::OnMouseOver, "OnMouseOver" }, + { SvMacroItemId::OnMouseOut, "OnMouseOut" }, + { SvMacroItemId::NONE, nullptr } + }; + + return aMacroDescriptionsImpl; +} + +SdXShape::SdXShape(SvxShape* pShape, SdXImpressDocument* pModel) +: mpShape( pShape ), + mpPropSet( pModel? + lcl_ImplGetShapePropertySet(pModel->IsImpressDocument(), pShape->getShapeKind() == SdrObjKind::Graphic ) + : lcl_GetEmpty_SdXShapePropertySet_Impl() ), + mpMap( pModel? + lcl_ImplGetShapePropertyMap(pModel->IsImpressDocument(), pShape->getShapeKind() == SdrObjKind::Graphic ) + : lcl_GetEmpty_SdXShapePropertyMap_Impl() ), + mpModel(pModel) +{ + + pShape->setMaster( this ); +} + +SdXShape::~SdXShape() noexcept +{ +} + +void SdXShape::dispose() +{ + mpShape->setMaster( nullptr ); + delete this; +} + +uno::Any SAL_CALL SdXShape::queryInterface( const uno::Type & rType ) +{ + return mpShape->queryInterface( rType ); +} + +void SAL_CALL SdXShape::acquire() noexcept +{ + mpShape->acquire(); +} + +void SAL_CALL SdXShape::release() noexcept +{ + mpShape->release(); +} + +bool SdXShape::queryAggregation( const css::uno::Type & rType, css::uno::Any& aAny ) +{ + if( mpModel && mpModel ->IsImpressDocument() ) + { + if( rType == cppu::UnoType::get()) + { + aAny <<= uno::Reference< document::XEventsSupplier >(this); + return true; + } + } + + return false; +} + +uno::Sequence< uno::Type > SAL_CALL SdXShape::getTypes() +{ + if( mpModel && !mpModel->IsImpressDocument() ) + { + return mpShape->_getTypes(); + } + else + { + SdrObjKind nObjId = mpShape->getShapeKind(); + uno::Sequence< uno::Type > aTypes; + SdTypesCache& gImplTypesCache = SD_MOD()->gImplTypesCache; + SdTypesCache::iterator aIter( gImplTypesCache.find( nObjId ) ); + if( aIter == gImplTypesCache.end() ) + { + aTypes = mpShape->_getTypes(); + sal_uInt32 nCount = aTypes.getLength(); + aTypes.realloc( nCount+1 ); + aTypes.getArray()[nCount] = cppu::UnoType::get(); + + gImplTypesCache.insert(std::make_pair(nObjId, aTypes)); + } + else + { + // use the already computed implementation id + aTypes = (*aIter).second; + } + return aTypes; + } +} + +// XPropertyState +beans::PropertyState SAL_CALL SdXShape::getPropertyState( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + if( mpPropSet->getPropertyMapEntry(PropertyName) ) + { + return beans::PropertyState_DIRECT_VALUE; + } + else + { + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr || ( pObj->getSdrPageFromSdrObject()->IsMasterPage() && pObj->IsEmptyPresObj() ) ) + return beans::PropertyState_DEFAULT_VALUE; + + return mpShape->_getPropertyState( PropertyName ); + } +} + +void SAL_CALL SdXShape::setPropertyToDefault( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + if( mpPropSet->getPropertyMapEntry(PropertyName) ) + { + return; + } + else + { + mpShape->_setPropertyToDefault(PropertyName); + } +} + +uno::Any SAL_CALL SdXShape::getPropertyDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + if( mpPropSet->getPropertyMapEntry(aPropertyName) ) + { + return getPropertyValue( aPropertyName ); + } + else + { + uno::Any aRet( mpShape->_getPropertyDefault(aPropertyName) ); + return aRet; + } +} + +//XPropertySet +css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL SdXShape::getPropertySetInfo() +{ + SfxItemPropertyMapEntry const * nObjId = mpShape->getPropertyMapEntries(); + css::uno::Reference pInfo; + + SdExtPropertySetInfoCache& rCache = (mpModel && mpModel->IsImpressDocument()) ? + SD_MOD()->gImplImpressPropertySetInfoCache : SD_MOD()->gImplDrawPropertySetInfoCache; + + SdExtPropertySetInfoCache::iterator aIter( rCache.find( nObjId ) ); + if( aIter == rCache.end() ) + { + uno::Reference< beans::XPropertySetInfo > xInfo( mpShape->_getPropertySetInfo() ); + pInfo = new SfxExtItemPropertySetInfo( mpMap, xInfo->getProperties() ); + + rCache.insert(std::make_pair(nObjId, pInfo)); + } + else + { + // use the already computed implementation id + pInfo = (*aIter).second; + } + + return pInfo; +} + +void SAL_CALL SdXShape::setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName); + + if( pEntry ) + { + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj ) + { + SdAnimationInfo* pInfo = GetAnimationInfo(pEntry->nWID <= WID_THAT_NEED_ANIMINFO); + + switch(pEntry->nWID) + { + case WID_NAVORDER: + { + sal_Int32 nNavOrder = 0; + if(!(aValue >>= nNavOrder)) + throw lang::IllegalArgumentException(); + + SdrObjList* pObjList = pObj->getParentSdrObjListFromSdrObject(); + if( pObjList ) + pObjList->SetObjectNavigationPosition( *pObj, (nNavOrder < 0) ? SAL_MAX_UINT32 : static_cast< sal_uInt32 >( nNavOrder ) ); + break; + } + + case WID_EFFECT: + { + AnimationEffect eEffect; + if(!(aValue >>= eEffect)) + throw lang::IllegalArgumentException(); + + EffectMigration::SetAnimationEffect( mpShape, eEffect ); + break; + } + case WID_TEXTEFFECT: + { + AnimationEffect eEffect; + if(!(aValue >>= eEffect)) + throw lang::IllegalArgumentException(); + + EffectMigration::SetTextAnimationEffect( mpShape, eEffect ); + break; + } + case WID_SPEED: + { + AnimationSpeed eSpeed; + if(!(aValue>>=eSpeed)) + throw lang::IllegalArgumentException(); + + EffectMigration::SetAnimationSpeed( mpShape, eSpeed ); + break; + } + case WID_ISANIMATION: + { + bool bIsAnimation(false); + + if(!(aValue >>= bIsAnimation)) + { + throw lang::IllegalArgumentException(); + } + + if(bIsAnimation) + { + SdrObjGroup* pGroup = dynamic_cast< SdrObjGroup* >(pObj); + SdPage* pPage = pGroup ? dynamic_cast< SdPage* >(pGroup->getSdrPageFromSdrObject()) : nullptr; + + if (pPage) + { + // #i42894# Animated Group object, migrate that effect + EffectMigration::CreateAnimatedGroup(*pGroup, *pPage); + + // #i42894# unfortunately when doing this all group members have to + // be moved to the page as direct members, else the currently + // available forms of animation do not work. If it succeeds, + // the group is empty and can be removed and deleted + if(!pGroup->GetSubList()->GetObjCount()) + { + pPage->NbcRemoveObject(pGroup->GetOrdNum()); + + // always use SdrObject::Free(...) for SdrObjects (!) + SdrObject* pTemp(pGroup); + SdrObject::Free(pTemp); + } + } + } + //pInfo->mbIsMovie = bIsAnimation; + break; + } + case WID_BOOKMARK: + { + OUString aString; + if(!(aValue >>= aString)) + throw lang::IllegalArgumentException(); + + pInfo->SetBookmark( SdDrawPage::getUiNameFromPageApiName( aString ) ); + break; + } + case WID_CLICKACTION: + ::cppu::any2enum< presentation::ClickAction >( pInfo->meClickAction, aValue); + break; + +// TODO: WID_PLAYFULL: + case WID_SOUNDFILE: + { + OUString aString; + if(!(aValue >>= aString)) + throw lang::IllegalArgumentException(); + pInfo->maSoundFile = aString; + EffectMigration::UpdateSoundEffect( mpShape, pInfo ); + break; + } + + case WID_SOUNDON: + { + if( !(aValue >>= pInfo->mbSoundOn) ) + throw lang::IllegalArgumentException(); + EffectMigration::UpdateSoundEffect( mpShape, pInfo ); + break; + } + case WID_VERB: + { + sal_Int32 nVerb = 0; + if(!(aValue >>= nVerb)) + throw lang::IllegalArgumentException(); + + pInfo->mnVerb = static_cast(nVerb); + break; + } + case WID_DIMCOLOR: + { + sal_Int32 nColor = 0; + + if( !(aValue >>= nColor) ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetDimColor( mpShape, nColor ); + break; + } + case WID_DIMHIDE: + { + bool bDimHide = false; + if( !(aValue >>= bDimHide) ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetDimHide( mpShape, bDimHide ); + break; + } + case WID_DIMPREV: + { + bool bDimPrevious = false; + if( !(aValue >>= bDimPrevious) ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetDimPrevious( mpShape, bDimPrevious ); + break; + } + case WID_PRESORDER: + { + sal_Int32 nNewPos = 0; + if( !(aValue >>= nNewPos) ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetPresentationOrder( mpShape, nNewPos ); + break; + } + case WID_STYLE: + SetStyleSheet( aValue ); + break; + case WID_ISEMPTYPRESOBJ: + SetEmptyPresObj( ::cppu::any2bool(aValue) ); + break; + case WID_MASTERDEPEND: + SetMasterDepend( ::cppu::any2bool(aValue) ); + break; + + case WID_LEGACYFRAGMENT: + { + uno::Reference< io::XInputStream > xInputStream; + aValue >>= xInputStream; + if( xInputStream.is() ) + { + SvInputStream aStream( xInputStream ); + SdrObject* pObject = mpShape->GetSdrObject(); + SvxMSDffManager::ReadObjText( aStream, pObject ); + } + } + break; + + case WID_ANIMPATH: + { + uno::Reference< drawing::XShape > xShape( aValue, uno::UNO_QUERY ); + SdrPathObj* pObj2 = xShape.is() ? dynamic_cast(SdrObject::getSdrObjectFromXShape(xShape)) : nullptr; + + if( pObj2 == nullptr ) + throw lang::IllegalArgumentException(); + + EffectMigration::SetAnimationPath( mpShape, pObj2 ); + break; + } + case WID_IMAGEMAP: + { + SdDrawDocument* pDoc = mpModel?mpModel->GetDoc():nullptr; + if( pDoc ) + { + ImageMap aImageMap; + uno::Reference< uno::XInterface > xImageMap; + aValue >>= xImageMap; + + if( !xImageMap.is() || !SvUnoImageMap_fillImageMap( xImageMap, aImageMap ) ) + throw lang::IllegalArgumentException(); + + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(pObj); + if( pIMapInfo ) + { + // replace existing image map + pIMapInfo->SetImageMap( aImageMap ); + } + else + { + // insert new user data with image map + pObj->AppendUserData(std::unique_ptr(new SvxIMapInfo(aImageMap) )); + } + } + } + break; + } + } + } + else + { + mpShape->_setPropertyValue(aPropertyName, aValue); + } + + if( mpModel ) + mpModel->SetModified(); +} + +css::uno::Any SAL_CALL SdXShape::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aRet; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); + + if( pEntry && mpShape->GetSdrObject() ) + { + SdAnimationInfo* pInfo = GetAnimationInfo(); + + switch(pEntry->nWID) + { + case WID_NAVORDER: + { + const sal_uInt32 nNavOrder = mpShape->GetSdrObject()->GetNavigationPosition(); + aRet <<= nNavOrder == SAL_MAX_UINT32 ? static_cast(-1) : static_cast< sal_Int32 >(nNavOrder); + } + break; + case WID_EFFECT: + aRet <<= EffectMigration::GetAnimationEffect( mpShape ); + break; + case WID_TEXTEFFECT: + aRet <<= EffectMigration::GetTextAnimationEffect( mpShape ); + break; + case WID_ISPRESOBJ: + aRet <<= IsPresObj(); + break; + case WID_ISEMPTYPRESOBJ: + aRet <<= IsEmptyPresObj(); + break; + case WID_MASTERDEPEND: + aRet <<= IsMasterDepend(); + break; + case WID_SPEED: + aRet <<= EffectMigration::GetAnimationSpeed( mpShape ); + break; + case WID_ISANIMATION: + aRet <<= (pInfo && pInfo->mbIsMovie); + break; + case WID_PLACEHOLDERTEXT: + aRet <<= GetPlaceholderText(); + break; + case WID_BOOKMARK: + { + OUString aString; + SdDrawDocument* pDoc = mpModel ? mpModel->GetDoc() : nullptr; + if (pInfo && pDoc) + { + // is the bookmark a page? + bool bIsMasterPage; + if(pDoc->GetPageByName( pInfo->GetBookmark(), bIsMasterPage ) != SDRPAGE_NOTFOUND) + { + aString = SdDrawPage::getPageApiNameFromUiName( pInfo->GetBookmark() ); + } + else + { + aString = pInfo->GetBookmark() ; + sal_Int32 nPos = aString.lastIndexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL( aString.copy( 0, nPos+1 ) ); + OUString aName( aString.copy( nPos+1 ) ); + if(pDoc->GetPageByName( aName, bIsMasterPage ) != SDRPAGE_NOTFOUND) + { + aURL += SdDrawPage::getPageApiNameFromUiName( aName ); + aString = aURL; + } + } + } + } + + aRet <<= aString; + break; + } + case WID_CLICKACTION: + aRet <<= ( pInfo?pInfo->meClickAction:presentation::ClickAction_NONE ); + break; + case WID_PLAYFULL: + aRet <<= ( pInfo && pInfo->mbPlayFull ); + break; + case WID_SOUNDFILE: + aRet <<= EffectMigration::GetSoundFile( mpShape ); + break; + case WID_SOUNDON: + aRet <<= EffectMigration::GetSoundOn( mpShape ); + break; + case WID_BLUESCREEN: + aRet <<= pInfo ? pInfo->maBlueScreen : Color(0x00ffffff); + break; + case WID_VERB: + aRet <<= static_cast( pInfo?pInfo->mnVerb:0 ); + break; + case WID_DIMCOLOR: + aRet <<= EffectMigration::GetDimColor( mpShape ); + break; + case WID_DIMHIDE: + aRet <<= EffectMigration::GetDimHide( mpShape ); + break; + case WID_DIMPREV: + aRet <<= EffectMigration::GetDimPrevious( mpShape ); + break; + case WID_PRESORDER: + aRet <<= EffectMigration::GetPresentationOrder( mpShape ); + break; + case WID_STYLE: + aRet = GetStyleSheet(); + break; + case WID_IMAGEMAP: + { + uno::Reference< uno::XInterface > xImageMap; + + SdDrawDocument* pDoc = mpModel?mpModel->GetDoc():nullptr; + if( pDoc ) + { + + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo(mpShape->GetSdrObject()); + if( pIMapInfo ) + { + const ImageMap& rIMap = pIMapInfo->GetImageMap(); + xImageMap = SvUnoImageMap_createInstance( rIMap, ImplGetSupportedMacroItems() ); + } + else + { + xImageMap = SvUnoImageMap_createInstance(); + } + } + + aRet <<= uno::Reference< container::XIndexContainer >::query( xImageMap ); + break; + } + } + } + else + { + aRet = mpShape->_getPropertyValue(PropertyName); + } + + return aRet; +} + +/** */ +SdAnimationInfo* SdXShape::GetAnimationInfo( bool bCreate ) const +{ + SdAnimationInfo* pInfo = nullptr; + + SdrObject* pObj = mpShape->GetSdrObject(); + if(pObj) + pInfo = SdDrawDocument::GetShapeUserData(*pObj, bCreate); + + return pInfo; +} + +uno::Sequence< OUString > SAL_CALL SdXShape::getSupportedServiceNames() +{ + std::vector aAdd{ u"com.sun.star.presentation.Shape", + u"com.sun.star.document.LinkTarget" }; + + SdrObject* pObj = mpShape->GetSdrObject(); + if(pObj && pObj->GetObjInventor() == SdrInventor::Default ) + { + SdrObjKind nInventor = pObj->GetObjIdentifier(); + switch( nInventor ) + { + case SdrObjKind::TitleText: + aAdd.emplace_back(u"com.sun.star.presentation.TitleTextShape"); + break; + case SdrObjKind::OutlineText: + aAdd.emplace_back(u"com.sun.star.presentation.OutlinerShape"); + break; + default: ; + } + } + return comphelper::concatSequences(mpShape->_getSupportedServiceNames(), aAdd); +} + +/** checks if this is a presentation object + */ +bool SdXShape::IsPresObj() const +{ + SdrObject* pObj = mpShape->GetSdrObject(); + if(pObj) + { + SdPage* pPage = dynamic_cast(pObj->getSdrPageFromSdrObject()); + if(pPage) + return pPage->GetPresObjKind(pObj) != PresObjKind::NONE; + } + return false; +} + +/** checks if this presentation object is empty + */ +bool SdXShape::IsEmptyPresObj() const +{ + SdrObject* pObj = mpShape->GetSdrObject(); + if( (pObj != nullptr) && pObj->IsEmptyPresObj() ) + { + // check if the object is in edit, then if it's temporarily not empty + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + if( pTextObj == nullptr ) + return true; + + return !pTextObj->CanCreateEditOutlinerParaObject(); + } + + return false; +} + +OUString SdXShape::GetPlaceholderText() const +{ + // only possible if this actually *is* a presentation object + if( !IsPresObj() ) + return OUString(); + + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr ) + return OUString(); + + SdPage* pPage = dynamic_cast< SdPage* >(pObj->getSdrPageFromSdrObject()); + DBG_ASSERT( pPage, "no page?" ); + if( pPage == nullptr ) + return OUString(); + + return pPage->GetPresObjText( pPage->GetPresObjKind(pObj) ); +} + +/** sets/reset the empty status of a presentation object +*/ +void SdXShape::SetEmptyPresObj(bool bEmpty) +{ + // only possible if this actually *is* a presentation object + if( !IsPresObj() ) + return; + + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr ) + return; + + if( pObj->IsEmptyPresObj() == bEmpty ) + return; + + if(!bEmpty) + { + OutlinerParaObject* pOutlinerParaObject = pObj->GetOutlinerParaObject(); + const bool bVertical = pOutlinerParaObject && pOutlinerParaObject->IsEffectivelyVertical(); + + // really delete SdrOutlinerObj at pObj + pObj->NbcSetOutlinerParaObject(std::nullopt); + if( bVertical ) + if (auto pTextObj = dynamic_cast( pObj ) ) + pTextObj->SetVerticalWriting( true ); + + SdrGrafObj* pGraphicObj = dynamic_cast( pObj ); + if( pGraphicObj ) + { + Graphic aEmpty; + pGraphicObj->SetGraphic(aEmpty); + } + else + { + SdrOle2Obj* pOleObj = dynamic_cast< SdrOle2Obj* >( pObj ); + if( pOleObj ) + { + pOleObj->ClearGraphic(); + } + } + } + else + { + // now set an empty OutlinerParaObject at pObj without + // any content but with the style of the old OutlinerParaObjects + // first paragraph + do + { + SdDrawDocument* pDoc = mpModel?mpModel->GetDoc():nullptr; + DBG_ASSERT( pDoc, "no document?" ); + if( pDoc == nullptr) + break; + + SdOutliner* pOutliner = pDoc->GetInternalOutliner(); + DBG_ASSERT( pOutliner, "no outliner?" ); + if( pOutliner == nullptr ) + break; + + SdPage* pPage = dynamic_cast< SdPage* >(pObj->getSdrPageFromSdrObject()); + DBG_ASSERT( pPage, "no page?" ); + if( pPage == nullptr ) + break; + + OutlinerParaObject* pOutlinerParaObject = pObj->GetOutlinerParaObject(); + pOutliner->SetText( *pOutlinerParaObject ); + const bool bVertical = pOutliner->IsVertical(); + + pOutliner->Clear(); + pOutliner->SetVertical( bVertical ); + pOutliner->SetStyleSheetPool( static_cast(pDoc->GetStyleSheetPool()) ); + pOutliner->SetStyleSheet( 0, pPage->GetTextStyleSheetForObject( pObj ) ); + pOutliner->Insert( pPage->GetPresObjText( pPage->GetPresObjKind(pObj) ) ); + pObj->SetOutlinerParaObject( pOutliner->CreateParaObject() ); + pOutliner->Clear(); + } + while(false); + } + + pObj->SetEmptyPresObj(bEmpty); +} + +bool SdXShape::IsMasterDepend() const noexcept +{ + SdrObject* pObj = mpShape->GetSdrObject(); + return pObj && pObj->GetUserCall() != nullptr; +} + +void SdXShape::SetMasterDepend( bool bDepend ) noexcept +{ + if( IsMasterDepend() == bDepend ) + return; + + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj ) + { + if( bDepend ) + { + SdPage* pPage = dynamic_cast< SdPage* >(pObj->getSdrPageFromSdrObject()); + pObj->SetUserCall( pPage ); + } + else + { + pObj->SetUserCall( nullptr ); + } + } +} + +void SdXShape::SetStyleSheet( const uno::Any& rAny ) +{ + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr ) + throw beans::UnknownPropertyException(); + + uno::Reference< style::XStyle > xStyle( rAny, uno::UNO_QUERY ); + SfxStyleSheet* pStyleSheet = SfxUnoStyleSheet::getUnoStyleSheet( xStyle ); + + const SfxStyleSheet* pOldStyleSheet = pObj->GetStyleSheet(); + if( pOldStyleSheet == pStyleSheet ) + return; + + if( pStyleSheet == nullptr || (pStyleSheet->GetFamily() != SfxStyleFamily::Para && pStyleSheet->GetFamily() != SfxStyleFamily::Page) ) + throw lang::IllegalArgumentException(); + + pObj->SetStyleSheet( pStyleSheet, false ); + + SdDrawDocument* pDoc = mpModel? mpModel->GetDoc() : nullptr; + if( pDoc ) + { + ::sd::DrawDocShell* pDocSh = pDoc->GetDocSh(); + ::sd::ViewShell* pViewSh = pDocSh ? pDocSh->GetViewShell() : nullptr; + + if( pViewSh ) + pViewSh->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_FAMILY2 ); + } +} + +uno::Any SdXShape::GetStyleSheet() const +{ + SdrObject* pObj = mpShape->GetSdrObject(); + if( pObj == nullptr ) + throw beans::UnknownPropertyException(); + + SfxStyleSheet* pStyleSheet = pObj->GetStyleSheet(); + // it is possible for shapes inside a draw to have a presentation style + // but we don't want this for the api + if( (pStyleSheet == nullptr) || ((pStyleSheet->GetFamily() != SfxStyleFamily::Para) && !mpModel->IsImpressDocument()) ) + return Any(); + + return Any( uno::Reference< style::XStyle >( dynamic_cast< SfxUnoStyleSheet* >( pStyleSheet ) ) ); +} + +class SdUnoEventsAccess : public cppu::WeakImplHelper< css::container::XNameReplace, css::lang::XServiceInfo > +{ +private: + SdXShape* mpShape; + +public: + explicit SdUnoEventsAccess(SdXShape* pShape) noexcept; + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& aName, const css::uno::Any& aElement ) override; + + // XNameAccess + virtual css::uno::Any SAL_CALL getByName( const OUString& aName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual css::uno::Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; +}; + +// XEventsSupplier +uno::Reference< container::XNameReplace > SAL_CALL SdXShape::getEvents( ) +{ + return new SdUnoEventsAccess( this ); +} + +constexpr OUStringLiteral gaStrOnClick( u"OnClick" ); +constexpr OUStringLiteral gaStrServiceName( u"com.sun.star.documents.Events" ); +constexpr OUStringLiteral gaStrEventType( u"EventType" ); +constexpr OUStringLiteral gaStrPresentation( u"Presentation" ); +constexpr OUStringLiteral gaStrLibrary(u"Library"); +constexpr OUStringLiteral gaStrMacroName(u"MacroName"); +constexpr OUStringLiteral gaStrClickAction( u"ClickAction" ); +constexpr OUStringLiteral gaStrBookmark( u"Bookmark" ); +constexpr OUStringLiteral gaStrEffect( u"Effect" ); +constexpr OUStringLiteral gaStrPlayFull( u"PlayFull" ); +constexpr OUStringLiteral gaStrVerb( u"Verb" ); +constexpr OUStringLiteral gaStrSoundURL( u"SoundURL" ); +constexpr OUStringLiteral gaStrSpeed( u"Speed" ); +constexpr OUStringLiteral gaStrStarBasic( u"StarBasic" ); +constexpr OUStringLiteral gaStrScript( u"Script" ); + +SdUnoEventsAccess::SdUnoEventsAccess( SdXShape* pShape ) noexcept + : mpShape( pShape ) +{ +} + +namespace { + +enum class FoundFlags { + NONE = 0x0000, + ClickAction = 0x0001, + Bookmark = 0x0002, + Effect = 0x0004, + PlayFull = 0x0008, + Verb = 0x0010, + SoundUrl = 0x0020, + Speed = 0x0040, + EventType = 0x0080, + Macro = 0x0100, + Library = 0x0200, +}; + +} + +namespace o3tl { + template<> struct typed_flags : is_typed_flags {}; +} + +static void clearEventsInAnimationInfo( SdAnimationInfo* pInfo ) +{ + pInfo->SetBookmark( "" ); + pInfo->mbSecondSoundOn = false; + pInfo->mbSecondPlayFull = false; + pInfo->meClickAction = presentation::ClickAction_NONE; + pInfo->meSecondEffect = presentation::AnimationEffect_NONE; + pInfo->meSecondSpeed = presentation::AnimationSpeed_MEDIUM; + pInfo->mnVerb = 0; +} + +// XNameReplace +void SAL_CALL SdUnoEventsAccess::replaceByName( const OUString& aName, const uno::Any& aElement ) +{ + if( mpShape == nullptr || aName != gaStrOnClick ) + throw container::NoSuchElementException(); + + uno::Sequence< beans::PropertyValue > aProperties; + if( !aElement.hasValue() || aElement.getValueType() != getElementType() || !(aElement >>= aProperties) ) + throw lang::IllegalArgumentException(); + + FoundFlags nFound = FoundFlags::NONE; + + OUString aStrEventType; + presentation::ClickAction eClickAction = presentation::ClickAction_NONE; + presentation::AnimationEffect eEffect = presentation::AnimationEffect_NONE; + presentation::AnimationSpeed eSpeed = presentation::AnimationSpeed_MEDIUM; + OUString aStrSoundURL; + bool bPlayFull = false; + sal_Int32 nVerb = 0; + OUString aStrMacro; + OUString aStrLibrary; + OUString aStrBookmark; + + for( const beans::PropertyValue& rProperty : std::as_const(aProperties) ) + { + if( !( nFound & FoundFlags::EventType ) && rProperty.Name == gaStrEventType ) + { + if( rProperty.Value >>= aStrEventType ) + { + nFound |= FoundFlags::EventType; + continue; + } + } + else if( !( nFound & FoundFlags::ClickAction ) && rProperty.Name == gaStrClickAction ) + { + if( rProperty.Value >>= eClickAction ) + { + nFound |= FoundFlags::ClickAction; + continue; + } + } + else if( !( nFound & FoundFlags::Macro ) && ( rProperty.Name == gaStrMacroName || rProperty.Name == gaStrScript ) ) + { + if( rProperty.Value >>= aStrMacro ) + { + nFound |= FoundFlags::Macro; + continue; + } + } + else if( !( nFound & FoundFlags::Library ) && rProperty.Name == gaStrLibrary ) + { + if( rProperty.Value >>= aStrLibrary ) + { + nFound |= FoundFlags::Library; + continue; + } + } + else if( !( nFound & FoundFlags::Effect ) && rProperty.Name == gaStrEffect ) + { + if( rProperty.Value >>= eEffect ) + { + nFound |= FoundFlags::Effect; + continue; + } + } + else if( !( nFound & FoundFlags::Bookmark ) && rProperty.Name == gaStrBookmark ) + { + if( rProperty.Value >>= aStrBookmark ) + { + nFound |= FoundFlags::Bookmark; + continue; + } + } + else if( !( nFound & FoundFlags::Speed ) && rProperty.Name == gaStrSpeed ) + { + if( rProperty.Value >>= eSpeed ) + { + nFound |= FoundFlags::Speed; + continue; + } + } + else if( !( nFound & FoundFlags::SoundUrl ) && rProperty.Name == gaStrSoundURL ) + { + if( rProperty.Value >>= aStrSoundURL ) + { + nFound |= FoundFlags::SoundUrl; + continue; + } + } + else if( !( nFound & FoundFlags::PlayFull ) && rProperty.Name == gaStrPlayFull ) + { + if( rProperty.Value >>= bPlayFull ) + { + nFound |= FoundFlags::PlayFull; + continue; + } + } + else if( !( nFound & FoundFlags::Verb ) && rProperty.Name == gaStrVerb ) + { + if( rProperty.Value >>= nVerb ) + { + nFound |= FoundFlags::Verb; + continue; + } + } + + throw lang::IllegalArgumentException(); + } + + bool bOk = false; + do + { + if( !( nFound & FoundFlags::EventType ) ) + break; + + if( aStrEventType == gaStrPresentation ) + { + if( !( nFound & FoundFlags::ClickAction ) ) + break; + + SdAnimationInfo* pInfo = mpShape->GetAnimationInfo(); + if( presentation::ClickAction_NONE == eClickAction && nullptr == pInfo ) + { + bOk = true; + break; + } + + if( nullptr == pInfo ) + pInfo = mpShape->GetAnimationInfo( true ); + + DBG_ASSERT( pInfo, "shape animation info could not be created!" ); + if( nullptr == pInfo ) + break; + + clearEventsInAnimationInfo( pInfo ); + pInfo->meClickAction = eClickAction; + + switch( eClickAction ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_STOPPRESENTATION: + { + bOk = true; + } + break; + + case presentation::ClickAction_PROGRAM: + case presentation::ClickAction_BOOKMARK: + case presentation::ClickAction_DOCUMENT: + if( nFound & FoundFlags::Bookmark ) + { + if( eClickAction == presentation::ClickAction_BOOKMARK ) + { + aStrBookmark = getUiNameFromPageApiNameImpl( aStrBookmark ); + } + else if( eClickAction == presentation::ClickAction_DOCUMENT ) + { + sal_Int32 nPos = aStrBookmark.lastIndexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL = aStrBookmark.subView( 0, nPos+1 ) + + getUiNameFromPageApiNameImpl( aStrBookmark.copy( nPos+1 ) ); + aStrBookmark = aURL; + } + } + + pInfo->SetBookmark( aStrBookmark ); + bOk = true; + } + break; + + case presentation::ClickAction_MACRO: + if( nFound & FoundFlags::Macro ) + { + pInfo->SetBookmark( aStrMacro ); + bOk = true; + } + break; + + case presentation::ClickAction_VERB: + if( nFound & FoundFlags::Verb ) + { + pInfo->mnVerb = static_cast(nVerb); + bOk = true; + } + break; + + case presentation::ClickAction_VANISH: + if( !( nFound & FoundFlags::Effect ) ) + break; + + pInfo->meSecondEffect = eEffect; + pInfo->meSecondSpeed = nFound & FoundFlags::Speed ? eSpeed : presentation::AnimationSpeed_MEDIUM; + + bOk = true; + + [[fallthrough]]; + + case presentation::ClickAction_SOUND: + if( nFound & FoundFlags::SoundUrl ) + { + pInfo->SetBookmark( aStrSoundURL ); + if( eClickAction != presentation::ClickAction_SOUND ) + pInfo->mbSecondSoundOn = !aStrSoundURL.isEmpty(); + pInfo->mbSecondPlayFull = (nFound & FoundFlags::PlayFull) && bPlayFull; + + bOk = true; + } + break; + default: + break; + } + } + else + { + SdAnimationInfo* pInfo = mpShape->GetAnimationInfo( true ); + + DBG_ASSERT( pInfo, "shape animation info could not be created!" ); + if( nullptr == pInfo ) + break; + + clearEventsInAnimationInfo( pInfo ); + pInfo->meClickAction = presentation::ClickAction_MACRO; + + if ( SfxApplication::IsXScriptURL( aStrMacro ) ) + { + pInfo->SetBookmark( aStrMacro ); + } + else + { + sal_Int32 nIdx{ 0 }; + const std::u16string_view aLibName = o3tl::getToken(aStrMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aStrMacro, 0, '.', nIdx); + const std::u16string_view aMacroName = o3tl::getToken(aStrMacro, 0, '.', nIdx); + + OUStringBuffer sBuffer( + OUString::Concat(aMacroName) + OUStringChar('.') + aModulName + OUStringChar('.') + aLibName + OUStringChar('.') ); + + if ( aStrLibrary == "StarOffice" ) + { + sBuffer.append( "BASIC" ); + } + else + { + sBuffer.append( aStrLibrary ); + } + + pInfo->SetBookmark( sBuffer.makeStringAndClear() ); + } + bOk = true; + } + } + while(false); + + if( !bOk ) + throw lang::IllegalArgumentException(); +} + +// XNameAccess +uno::Any SAL_CALL SdUnoEventsAccess::getByName( const OUString& aName ) +{ + if( mpShape == nullptr || aName != gaStrOnClick ) + throw container::NoSuchElementException(); + + SdAnimationInfo* pInfo = mpShape->GetAnimationInfo(); + + presentation::ClickAction eClickAction = presentation::ClickAction_NONE; + if( pInfo ) + eClickAction = pInfo->meClickAction; + + sal_Int32 nPropertyCount = 2; + switch( eClickAction ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_STOPPRESENTATION: + break; + case presentation::ClickAction_PROGRAM: + case presentation::ClickAction_VERB: + case presentation::ClickAction_BOOKMARK: + case presentation::ClickAction_DOCUMENT: + case presentation::ClickAction_MACRO: + if ( !SfxApplication::IsXScriptURL( pInfo->GetBookmark() ) ) + nPropertyCount += 1; + break; + + case presentation::ClickAction_SOUND: + nPropertyCount += 2; + break; + + case presentation::ClickAction_VANISH: + nPropertyCount += 4; + break; + default: + break; + } + + uno::Sequence< beans::PropertyValue > aProperties( nPropertyCount ); + beans::PropertyValue* pProperties = aProperties.getArray(); + + uno::Any aAny; + + if( eClickAction == presentation::ClickAction_MACRO ) + { + if ( SfxApplication::IsXScriptURL( pInfo->GetBookmark() ) ) + { + // Scripting Framework URL + aAny <<= OUString(gaStrScript); + pProperties->Name = gaStrEventType; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + aAny <<= pInfo->GetBookmark(); + pProperties->Name = gaStrScript; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + } + else + { + // Old Basic macro URL + aAny <<= OUString(gaStrStarBasic); + pProperties->Name = gaStrEventType; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + OUString aMacro = pInfo->GetBookmark(); + + // aMacro has got following format: + // "Macroname.Modulname.Libname.Documentname" or + // "Macroname.Modulname.Libname.Applicationname" + sal_Int32 nIdx{ 0 }; + const std::u16string_view aMacroName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aModulName = o3tl::getToken(aMacro, 0, '.', nIdx); + const std::u16string_view aLibName = o3tl::getToken(aMacro, 0, '.', nIdx); + + OUString sBuffer = OUString::Concat(aLibName) + + "." + + aModulName + + "." + + aMacroName; + + aAny <<= sBuffer; + pProperties->Name = gaStrMacroName; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + aAny <<= OUString( "StarOffice" ); + pProperties->Name = gaStrLibrary; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + } + } + else + { + aAny <<= OUString(gaStrPresentation); + pProperties->Name = gaStrEventType; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + aAny <<= eClickAction; + pProperties->Name = gaStrClickAction; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + switch( eClickAction ) + { + case presentation::ClickAction_NONE: + case presentation::ClickAction_PREVPAGE: + case presentation::ClickAction_NEXTPAGE: + case presentation::ClickAction_FIRSTPAGE: + case presentation::ClickAction_LASTPAGE: + case presentation::ClickAction_INVISIBLE: + case presentation::ClickAction_STOPPRESENTATION: + break; + case presentation::ClickAction_BOOKMARK: + { + const OUString aStrBookmark( getPageApiNameFromUiName( pInfo->GetBookmark()) ); + pProperties->Name = gaStrBookmark; + pProperties->Handle = -1; + pProperties->Value <<= aStrBookmark; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + } + break; + + case presentation::ClickAction_DOCUMENT: + case presentation::ClickAction_PROGRAM: + { + OUString aString( pInfo->GetBookmark()); + sal_Int32 nPos = aString.lastIndexOf( '#' ); + if( nPos >= 0 ) + { + OUString aURL = aString.subView( 0, nPos+1 ) + + getPageApiNameFromUiName( aString.copy( nPos+1 ) ); + aString = aURL; + } + pProperties->Name = gaStrBookmark; + pProperties->Handle = -1; + pProperties->Value <<= aString; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + } + break; + + case presentation::ClickAction_VANISH: + aAny <<= pInfo->meSecondEffect; + pProperties->Name = gaStrEffect; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + aAny <<= pInfo->meSecondSpeed; + pProperties->Name = gaStrSpeed; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + [[fallthrough]]; + + case presentation::ClickAction_SOUND: + if( eClickAction == presentation::ClickAction_SOUND || pInfo->mbSecondSoundOn ) + { + aAny <<= pInfo->GetBookmark(); + pProperties->Name = gaStrSoundURL; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + pProperties++; + + pProperties->Name = gaStrPlayFull; + pProperties->Handle = -1; + pProperties->Value <<= pInfo->mbSecondPlayFull; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + } + break; + + case presentation::ClickAction_VERB: + aAny <<= static_cast(pInfo->mnVerb); + pProperties->Name = gaStrVerb; + pProperties->Handle = -1; + pProperties->Value = aAny; + pProperties->State = beans::PropertyState_DIRECT_VALUE; + break; + default: + break; + } + } + + aAny <<= aProperties; + return aAny; +} + +uno::Sequence< OUString > SAL_CALL SdUnoEventsAccess::getElementNames( ) +{ + return { gaStrOnClick }; +} + +sal_Bool SAL_CALL SdUnoEventsAccess::hasByName( const OUString& aName ) +{ + return aName == gaStrOnClick; +} + +// XElementAccess +uno::Type SAL_CALL SdUnoEventsAccess::getElementType( ) +{ + return cppu::UnoType>::get(); +} + +sal_Bool SAL_CALL SdUnoEventsAccess::hasElements( ) +{ + return true; +} + +// XServiceInfo +OUString SAL_CALL SdUnoEventsAccess::getImplementationName( ) +{ + return "SdUnoEventsAccess"; +} + +sal_Bool SAL_CALL SdUnoEventsAccess::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +uno::Sequence< OUString > SAL_CALL SdUnoEventsAccess::getSupportedServiceNames( ) +{ + return { gaStrServiceName }; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unoobj.hxx b/sd/source/ui/unoidl/unoobj.hxx new file mode 100644 index 000000000..3f01cbed1 --- /dev/null +++ b/sd/source/ui/unoidl/unoobj.hxx @@ -0,0 +1,100 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include +#include + +namespace com::sun::star::beans { class XPropertySetInfo; } + +class SdXImpressDocument; +class SdAnimationInfo; +class SvxItemPropertySet; +class SvxShape; +struct SfxItemPropertyMapEntry; + +class SdXShape : public SvxShapeMaster, + public css::document::XEventsSupplier +{ + friend class SdUnoEventsAccess; + +private: + SvxShape* mpShape; + const SvxItemPropertySet* mpPropSet; + const SfxItemPropertyMapEntry* mpMap; + SdXImpressDocument* mpModel; + + /// @throws css::lang::IllegalArgumentException + /// @throws css::beans::UnknownPropertyException + /// @throws css::uno::RuntimeException + void SetStyleSheet( const css::uno::Any& rAny ); + /// @throws css::beans::UnknownPropertyException + css::uno::Any GetStyleSheet() const; + + // Intern + /// @throws std::exception + SdAnimationInfo* GetAnimationInfo( bool bCreate = false ) const; + /// @throws std::exception + bool IsPresObj() const; + + bool IsEmptyPresObj() const; + void SetEmptyPresObj(bool bEmpty); + + bool IsMasterDepend() const noexcept; + void SetMasterDepend( bool bDepend ) noexcept; + + OUString GetPlaceholderText() const; + +public: + SdXShape(SvxShape* pShape, SdXImpressDocument* pModel); + virtual ~SdXShape() noexcept; + + virtual bool queryAggregation( const css::uno::Type & rType, css::uno::Any& aAny ) override; + virtual void dispose() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XServiceInfo + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + //XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + + //XPropertyState + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& PropertyName ) override; + virtual void SAL_CALL setPropertyToDefault( const OUString& PropertyName ) override; + virtual css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + + // XEventsSupplier + virtual css::uno::Reference< css::container::XNameReplace > SAL_CALL getEvents( ) override; +}; + +struct SvEventDescription; +const SvEventDescription* ImplGetSupportedMacroItems(); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopage.cxx b/sd/source/ui/unoidl/unopage.cxx new file mode 100644 index 000000000..8a9549c51 --- /dev/null +++ b/sd/source/ui/unoidl/unopage.cxx @@ -0,0 +1,3056 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unoobj.hxx" + +#include +#include +#include +#include "unopback.hxx" +#include +#include +#include +#include + +using ::com::sun::star::animations::XAnimationNode; +using ::com::sun::star::animations::XAnimationNodeSupplier; + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::office; + +namespace { + +// this are the ids for page properties +enum WID_PAGE +{ + WID_PAGE_LEFT, WID_PAGE_RIGHT, WID_PAGE_TOP, WID_PAGE_BOTTOM, WID_PAGE_WIDTH, + WID_PAGE_HEIGHT, WID_PAGE_EFFECT, WID_PAGE_CHANGE, WID_PAGE_SPEED, WID_PAGE_NUMBER, + WID_PAGE_ORIENT, WID_PAGE_LAYOUT, WID_PAGE_DURATION, WID_PAGE_HIGHRESDURATION, WID_PAGE_LDNAME, WID_PAGE_LDBITMAP, + WID_PAGE_BACK, WID_PAGE_PREVIEW, WID_PAGE_PREVIEWBITMAP, WID_PAGE_VISIBLE, WID_PAGE_SOUNDFILE, WID_PAGE_BACKFULL, + WID_PAGE_BACKVIS, WID_PAGE_BACKOBJVIS, WID_PAGE_USERATTRIBS, WID_PAGE_BOOKMARK, WID_PAGE_ISDARK, + WID_PAGE_HEADERVISIBLE, WID_PAGE_HEADERTEXT, WID_PAGE_FOOTERVISIBLE, WID_PAGE_FOOTERTEXT, + WID_PAGE_PAGENUMBERVISIBLE, WID_PAGE_DATETIMEVISIBLE, WID_PAGE_DATETIMEFIXED, + WID_PAGE_DATETIMETEXT, WID_PAGE_DATETIMEFORMAT, WID_TRANSITION_TYPE, WID_TRANSITION_SUBTYPE, + WID_TRANSITION_DIRECTION, WID_TRANSITION_FADE_COLOR, WID_TRANSITION_DURATION, WID_LOOP_SOUND, + WID_NAVORDER, WID_PAGE_PREVIEWMETAFILE, WID_PAGE_THEME +}; + +} + +constexpr OUStringLiteral sEmptyPageName = u"page"; + +// this function stores the property maps for draw pages in impress and draw +static const SvxItemPropertySet* ImplGetDrawPagePropertySet( bool bImpress, PageKind ePageKind ) +{ + static const SfxItemPropertyMapEntry aDrawPagePropertyMap_Impl[] = + { + { u"" UNO_NAME_PAGE_BACKGROUND, WID_PAGE_BACK, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID,0}, + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_CHANGE, WID_PAGE_CHANGE, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_DURATION, WID_PAGE_DURATION, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_EFFECT, WID_PAGE_EFFECT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LAYOUT, WID_PAGE_LAYOUT, ::cppu::UnoType::get(), 0, 0}, + { UNO_NAME_LINKDISPLAYBITMAP, WID_PAGE_LDBITMAP, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { UNO_NAME_LINKDISPLAYNAME, WID_PAGE_LDNAME, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType::get(),0, 0}, + { u"" UNO_NAME_PAGE_SPEED, WID_PAGE_SPEED, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_PREVIEW, WID_PAGE_PREVIEW, cppu::UnoType>::get(), css::beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_PREVIEWBITMAP, WID_PAGE_PREVIEWBITMAP, cppu::UnoType>::get(), css::beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_PREVIEWMETAFILE, WID_PAGE_PREVIEWMETAFILE, cppu::UnoType>::get(), css::beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_VISIBLE, WID_PAGE_VISIBLE, cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_OBJ_SOUNDFILE, WID_PAGE_SOUNDFILE, cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_IsBackgroundVisible, WID_PAGE_BACKVIS, cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_IsBackgroundObjectsVisible, WID_PAGE_BACKOBJVIS, cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_BookmarkURL, WID_PAGE_BOOKMARK, ::cppu::UnoType::get(), 0, 0}, + { u"HighResDuration", WID_PAGE_HIGHRESDURATION, ::cppu::UnoType::get(), 0, 0}, + { u"IsBackgroundDark" , WID_PAGE_ISDARK, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"IsFooterVisible", WID_PAGE_FOOTERVISIBLE, cppu::UnoType::get(), 0, 0}, + { u"FooterText", WID_PAGE_FOOTERTEXT, ::cppu::UnoType::get(), 0, 0}, + { u"IsPageNumberVisible", WID_PAGE_PAGENUMBERVISIBLE, cppu::UnoType::get(), 0, 0}, + { u"IsDateTimeVisible", WID_PAGE_DATETIMEVISIBLE, cppu::UnoType::get(), 0, 0}, + { u"IsDateTimeFixed", WID_PAGE_DATETIMEFIXED, cppu::UnoType::get(), 0, 0}, + { u"DateTimeText", WID_PAGE_DATETIMETEXT, ::cppu::UnoType::get(), 0, 0}, + { u"DateTimeFormat", WID_PAGE_DATETIMEFORMAT, ::cppu::UnoType::get(), 0, 0}, + { u"TransitionType", WID_TRANSITION_TYPE, ::cppu::UnoType::get(), 0, 0}, + { u"TransitionSubtype", WID_TRANSITION_SUBTYPE, ::cppu::UnoType::get(), 0, 0}, + { u"TransitionDirection", WID_TRANSITION_DIRECTION, ::cppu::UnoType::get(), 0, 0}, + { u"TransitionFadeColor", WID_TRANSITION_FADE_COLOR, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_TRANSITION_DURATION, WID_TRANSITION_DURATION, ::cppu::UnoType::get(), 0, 0}, + { u"LoopSound", WID_LOOP_SOUND, cppu::UnoType::get(), 0, 0}, + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType::get(),0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + +#define DRAW_PAGE_NOTES_PROPERTIES \ + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_LAYOUT, WID_PAGE_LAYOUT, ::cppu::UnoType::get(), 0, 0}, \ + { UNO_NAME_LINKDISPLAYBITMAP, WID_PAGE_LDBITMAP, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, \ + { UNO_NAME_LINKDISPLAYNAME, WID_PAGE_LDNAME, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType::get(),0, 0}, \ + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType::get(), 0, 0}, \ + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType::get(), 0, 0},\ + { u"IsHeaderVisible", WID_PAGE_HEADERVISIBLE, cppu::UnoType::get(), 0, 0}, \ + { u"HeaderText", WID_PAGE_HEADERTEXT, ::cppu::UnoType::get(), 0, 0}, \ + { u"IsBackgroundDark", WID_PAGE_ISDARK, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"IsFooterVisible", WID_PAGE_FOOTERVISIBLE, cppu::UnoType::get(), 0, 0}, \ + { u"FooterText", WID_PAGE_FOOTERTEXT, ::cppu::UnoType::get(), 0, 0}, \ + { u"IsPageNumberVisible", WID_PAGE_PAGENUMBERVISIBLE, cppu::UnoType::get(), 0, 0}, \ + { u"IsDateTimeVisible", WID_PAGE_DATETIMEVISIBLE, cppu::UnoType::get(), 0, 0}, \ + { u"IsDateTimeFixed", WID_PAGE_DATETIMEFIXED, cppu::UnoType::get(), 0, 0}, \ + { u"DateTimeText", WID_PAGE_DATETIMETEXT, ::cppu::UnoType::get(), 0, 0}, \ + { u"DateTimeFormat", WID_PAGE_DATETIMEFORMAT, ::cppu::UnoType::get(), 0, 0}, \ + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType::get(),0, 0}, \ + { u"", 0, css::uno::Type(), 0, 0 } + + static const SfxItemPropertyMapEntry aDrawPageNotesHandoutPropertyMap_Impl[] = + { + // this must be the first two entries so they can be excluded for PageKind::Standard + { u"" UNO_NAME_PAGE_BACKGROUND, WID_PAGE_BACK, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID,0}, + DRAW_PAGE_NOTES_PROPERTIES + }; + static const SfxItemPropertyMapEntry aDrawPageNotesHandoutPropertyNoBackMap_Impl[] = + { + DRAW_PAGE_NOTES_PROPERTIES + }; + +#define GRAPHIC_PAGE_PROPERTIES \ + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType::get(), 0, 0}, \ + { UNO_NAME_LINKDISPLAYBITMAP, WID_PAGE_LDBITMAP, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, \ + { UNO_NAME_LINKDISPLAYNAME, WID_PAGE_LDNAME, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType::get(),0, 0}, \ + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType::get(), 0, 0}, \ + { u"" UNO_NAME_PAGE_PREVIEW, WID_PAGE_PREVIEW, cppu::UnoType>::get(), css::beans::PropertyAttribute::READONLY, 0}, \ + { u"" UNO_NAME_PAGE_PREVIEWBITMAP, WID_PAGE_PREVIEWBITMAP, cppu::UnoType>::get(), css::beans::PropertyAttribute::READONLY, 0},\ + { u"" UNO_NAME_PAGE_PREVIEWMETAFILE, WID_PAGE_PREVIEWMETAFILE, cppu::UnoType>::get(), css::beans::PropertyAttribute::READONLY, 0},\ + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType::get(), 0, 0}, \ + { sUNO_Prop_BookmarkURL, WID_PAGE_BOOKMARK, ::cppu::UnoType::get(), 0, 0}, \ + { u"IsBackgroundDark", WID_PAGE_ISDARK, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, \ + { u"NavigationOrder", WID_NAVORDER, cppu::UnoType::get(),0, 0}, \ + { u"", 0, css::uno::Type(), 0, 0 } + + static const SfxItemPropertyMapEntry aGraphicPagePropertyMap_Impl[] = + { + { u"" UNO_NAME_PAGE_BACKGROUND, WID_PAGE_BACK, cppu::UnoType::get(), beans::PropertyAttribute::MAYBEVOID,0}, + GRAPHIC_PAGE_PROPERTIES + }; + static const SfxItemPropertyMapEntry aGraphicPagePropertyNoBackMap_Impl[] = + { + GRAPHIC_PAGE_PROPERTIES + }; + + bool bWithoutBackground = ePageKind != PageKind::Standard && ePageKind != PageKind::Handout; + const SvxItemPropertySet* pRet = nullptr; + if( bImpress ) + { + if( ePageKind == PageKind::Standard ) + { + //PageKind::Standard always has a background property + static SvxItemPropertySet aDrawPagePropertySet_Impl( aDrawPagePropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aDrawPagePropertySet_Impl; + } + else + { + if(bWithoutBackground) + { + static SvxItemPropertySet aDrawPageNotesHandoutPropertyNoBackSet_Impl( aDrawPageNotesHandoutPropertyNoBackMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aDrawPageNotesHandoutPropertyNoBackSet_Impl; + } + else + { + static SvxItemPropertySet aDrawPageNotesHandoutPropertySet_Impl( aDrawPageNotesHandoutPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aDrawPageNotesHandoutPropertySet_Impl; + } + } + } + else + { + if(bWithoutBackground) + { + static SvxItemPropertySet aGraphicPagePropertyNoBackSet_Impl( aGraphicPagePropertyNoBackMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aGraphicPagePropertyNoBackSet_Impl; + } + else + { + static SvxItemPropertySet aGraphicPagePropertySet_Impl( aGraphicPagePropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aGraphicPagePropertySet_Impl; + } + } + return pRet; +} + +/** this function stores the property map for master pages in impress and draw */ +static const SvxItemPropertySet* ImplGetMasterPagePropertySet( PageKind ePageKind ) +{ + static const SfxItemPropertyMapEntry aMasterPagePropertyMap_Impl[] = + { + { u"" UNO_NAME_PAGE_BACKGROUND, WID_PAGE_BACK, cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType::get(), 0, 0}, + { UNO_NAME_LINKDISPLAYBITMAP, WID_PAGE_LDBITMAP, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { UNO_NAME_LINKDISPLAYNAME, WID_PAGE_LDNAME, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType::get(),0, 0}, + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType::get(), 0, 0}, + { u"BackgroundFullSize", WID_PAGE_BACKFULL, cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType::get(), 0, 0}, + { u"IsBackgroundDark", WID_PAGE_ISDARK, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"Theme", WID_PAGE_THEME, cppu::UnoType>::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + static const SfxItemPropertyMapEntry aHandoutMasterPagePropertyMap_Impl[] = + { + { u"" UNO_NAME_PAGE_BOTTOM, WID_PAGE_BOTTOM, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LEFT, WID_PAGE_LEFT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_RIGHT, WID_PAGE_RIGHT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_TOP, WID_PAGE_TOP, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_HEIGHT, WID_PAGE_HEIGHT, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_ORIENTATION, WID_PAGE_ORIENT, ::cppu::UnoType::get(),0, 0}, + { u"" UNO_NAME_PAGE_NUMBER, WID_PAGE_NUMBER, ::cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"" UNO_NAME_PAGE_WIDTH, WID_PAGE_WIDTH, ::cppu::UnoType::get(), 0, 0}, + { u"" UNO_NAME_PAGE_LAYOUT, WID_PAGE_LAYOUT, ::cppu::UnoType::get(), 0, 0}, + { sUNO_Prop_UserDefinedAttributes,WID_PAGE_USERATTRIBS, cppu::UnoType::get(), 0, 0}, + { u"IsBackgroundDark", WID_PAGE_ISDARK, cppu::UnoType::get(), beans::PropertyAttribute::READONLY, 0}, + { u"IsHeaderVisible", WID_PAGE_HEADERVISIBLE, cppu::UnoType::get(), 0, 0}, + { u"HeaderText", WID_PAGE_HEADERTEXT, ::cppu::UnoType::get(), 0, 0}, + { u"IsFooterVisible", WID_PAGE_FOOTERVISIBLE, cppu::UnoType::get(), 0, 0}, + { u"FooterText", WID_PAGE_FOOTERTEXT, ::cppu::UnoType::get(), 0, 0}, + { u"IsPageNumberVisible", WID_PAGE_PAGENUMBERVISIBLE, cppu::UnoType::get(), 0, 0}, + { u"IsDateTimeVisible", WID_PAGE_DATETIMEVISIBLE, cppu::UnoType::get(), 0, 0}, + { u"IsDateTimeFixed", WID_PAGE_DATETIMEFIXED, cppu::UnoType::get(), 0, 0}, + { u"DateTimeText", WID_PAGE_DATETIMETEXT, ::cppu::UnoType::get(), 0, 0}, + { u"DateTimeFormat", WID_PAGE_DATETIMEFORMAT, ::cppu::UnoType::get(), 0, 0}, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + const SvxItemPropertySet* pRet = nullptr; + if( ePageKind == PageKind::Handout ) + { + static SvxItemPropertySet aHandoutMasterPagePropertySet_Impl( aHandoutMasterPagePropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aHandoutMasterPagePropertySet_Impl; + } + else + { + static SvxItemPropertySet aMasterPagePropertySet_Impl( aMasterPagePropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + pRet = &aMasterPagePropertySet_Impl; + } + return pRet; +} + +const css::uno::Sequence< sal_Int8 > & SdGenericDrawPage::getUnoTunnelId() noexcept +{ + static const comphelper::UnoIdInit theSdGenericDrawPageUnoTunnelId; + return theSdGenericDrawPageUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL SdGenericDrawPage::getSomething( const css::uno::Sequence< sal_Int8 >& rId ) +{ + return comphelper::getSomethingImpl(rId, this, + comphelper::FallbackToGetSomethingOf{}); +} + +SdGenericDrawPage::SdGenericDrawPage(SdXImpressDocument* _pModel, SdPage* pInPage, const SvxItemPropertySet* _pSet) +: SvxFmDrawPage( static_cast(pInPage) ), + SdUnoSearchReplaceShape(this), + mpDocModel( _pModel ), + mpSdrModel(nullptr), + mbIsImpressDocument(false), + mnTempPageNumber(0), + mpPropSet ( _pSet ) +{ + mpSdrModel = SvxFmDrawPage::mpModel; + if( mpDocModel ) + mbIsImpressDocument = mpDocModel->IsImpressDocument(); + +} + +SdGenericDrawPage::~SdGenericDrawPage() noexcept +{ +} + +void SdGenericDrawPage::throwIfDisposed() const +{ + if( (SvxFmDrawPage::mpModel == nullptr) || (mpDocModel == nullptr) || (SvxFmDrawPage::mpPage == nullptr) ) + throw lang::DisposedException(); +} + +SdXImpressDocument* SdGenericDrawPage::GetModel() const +{ + if( mpSdrModel != SvxFmDrawPage::mpModel ) + const_cast(this)->UpdateModel(); + return mpDocModel; +} + +bool SdGenericDrawPage::IsImpressDocument() const +{ + if( mpSdrModel != SvxFmDrawPage::mpModel ) + const_cast(this)->UpdateModel(); + return mbIsImpressDocument; +} + + +void SdGenericDrawPage::UpdateModel() +{ + mpSdrModel = SvxFmDrawPage::mpModel; + if( mpSdrModel ) + { + uno::Reference< uno::XInterface > xModel( SvxFmDrawPage::mpModel->getUnoModel() ); + mpDocModel = comphelper::getFromUnoTunnel( xModel ); + } + else + { + mpDocModel = nullptr; + } + mbIsImpressDocument = mpDocModel && mpDocModel->IsImpressDocument(); +} + +// this is called whenever a SdrObject must be created for an empty api shape wrapper +SdrObject * SdGenericDrawPage::CreateSdrObject_( const Reference< drawing::XShape >& xShape ) +{ + if( nullptr == SvxFmDrawPage::mpPage || !xShape.is() ) + return nullptr; + + OUString aType( xShape->getShapeType() ); + static const OUStringLiteral aPrefix( u"com.sun.star.presentation." ); + if( !aType.startsWith( aPrefix ) ) + { + SdrObject* pObj = SvxFmDrawPage::CreateSdrObject_( xShape ); + return pObj; + } + + aType = aType.copy( aPrefix.getLength() ); + + PresObjKind eObjKind = PresObjKind::NONE; + + if( aType == "TitleTextShape" ) + { + eObjKind = PresObjKind::Title; + } + else if( aType == "OutlinerShape" ) + { + eObjKind = PresObjKind::Outline; + } + else if( aType == "SubtitleShape" ) + { + eObjKind = PresObjKind::Text; + } + else if( aType == "OLE2Shape" ) + { + eObjKind = PresObjKind::Object; + } + else if( aType == "ChartShape" ) + { + eObjKind = PresObjKind::Chart; + } + else if( aType == "CalcShape" ) + { + eObjKind = PresObjKind::Calc; + } + else if( aType == "TableShape" ) + { + eObjKind = PresObjKind::Table; + } + else if( aType == "GraphicObjectShape" ) + { + eObjKind = PresObjKind::Graphic; + } + else if( aType == "OrgChartShape" ) + { + eObjKind = PresObjKind::OrgChart; + } + else if( aType == "PageShape" ) + { + if( GetPage()->GetPageKind() == PageKind::Notes && GetPage()->IsMasterPage() ) + eObjKind = PresObjKind::Title; + else + eObjKind = PresObjKind::Page; + } + else if( aType == "NotesShape" ) + { + eObjKind = PresObjKind::Notes; + } + else if( aType == "HandoutShape" ) + { + eObjKind = PresObjKind::Handout; + } + else if( aType == "FooterShape" ) + { + eObjKind = PresObjKind::Footer; + } + else if( aType == "HeaderShape" ) + { + eObjKind = PresObjKind::Header; + } + else if( aType == "SlideNumberShape" ) + { + eObjKind = PresObjKind::SlideNumber; + } + else if( aType == "DateTimeShape" ) + { + eObjKind = PresObjKind::DateTime; + } + else if( aType == "MediaShape" ) + { + eObjKind = PresObjKind::Media; + } + + ::tools::Rectangle aRect( eObjKind == PresObjKind::Title ? GetPage()->GetTitleRect() : GetPage()->GetLayoutRect() ); + + const awt::Point aPos( aRect.Left(), aRect.Top() ); + xShape->setPosition( aPos ); + + const awt::Size aSize( aRect.GetWidth(), aRect.GetHeight() ); + xShape->setSize( aSize ); + + SdrObject *pPresObj = nullptr; + if( (eObjKind == PresObjKind::Table) || (eObjKind == PresObjKind::Media) ) + { + pPresObj = SvxFmDrawPage::CreateSdrObject_( xShape ); + if( pPresObj ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + pPresObj->NbcSetStyleSheet(rDoc.GetDefaultStyleSheet(), true); + GetPage()->InsertPresObj( pPresObj, eObjKind ); + } + } + else + { + pPresObj = GetPage()->CreatePresObj( eObjKind, false, aRect ); + } + + if( pPresObj ) + pPresObj->SetUserCall( GetPage() ); + + return pPresObj; +} + +// XInterface +Any SAL_CALL SdGenericDrawPage::queryInterface( const uno::Type & rType ) +{ + Any aAny; + + if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (rType == cppu::UnoType::get()) + { + aAny <<= Reference(this); + } + else if (IsImpressDocument() && rType == cppu::UnoType::get()) + { + const PageKind ePageKind = GetPage() ? GetPage()->GetPageKind() : PageKind::Standard; + + if( ePageKind == PageKind::Standard ) + return Any( Reference< XAnimationNodeSupplier >( this ) ); + } + else + return SvxDrawPage::queryInterface( rType ); + + return aAny; +} + +// XPropertySet +Reference< beans::XPropertySetInfo > SAL_CALL SdGenericDrawPage::getPropertySetInfo() +{ + ::SolarMutexGuard aGuard; + throwIfDisposed(); + return mpPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdGenericDrawPage::setPropertyValue( const OUString& aPropertyName, const Any& aValue ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_NAVORDER: + setNavigationOrder( aValue ); + break; + case WID_PAGE_LEFT: + case WID_PAGE_RIGHT: + case WID_PAGE_TOP: + case WID_PAGE_BOTTOM: + case WID_PAGE_LAYOUT: + case WID_PAGE_DURATION: + case WID_PAGE_CHANGE: + { + sal_Int32 nValue = 0; + if(!(aValue >>= nValue)) + throw lang::IllegalArgumentException(); + + switch( pEntry->nWID ) + { + case WID_PAGE_LEFT: + SetLeftBorder(nValue); + break; + case WID_PAGE_RIGHT: + SetRightBorder( nValue ); + break; + case WID_PAGE_TOP: + SetUpperBorder( nValue ); + break; + case WID_PAGE_BOTTOM: + SetLowerBorder( nValue ); + break; + case WID_PAGE_CHANGE: + GetPage()->SetPresChange( static_cast(nValue) ); + break; + case WID_PAGE_LAYOUT: + GetPage()->SetAutoLayout( static_cast(nValue), true ); + break; + case WID_PAGE_DURATION: + GetPage()->SetTime(nValue); + break; + } + break; + } + case WID_PAGE_HIGHRESDURATION: + { + double fValue = 0; + if(!(aValue >>= fValue)) + throw lang::IllegalArgumentException(); + + GetPage()->SetTime(fValue); + break; + } + case WID_PAGE_WIDTH: + { + sal_Int32 nWidth = 0; + if(!(aValue >>= nWidth)) + throw lang::IllegalArgumentException(); + + SetWidth( nWidth ); + break; + } + case WID_PAGE_HEIGHT: + { + sal_Int32 nHeight = 0; + if(!(aValue >>= nHeight)) + throw lang::IllegalArgumentException(); + + SetHeight( nHeight ); + break; + } + case WID_PAGE_ORIENT: + { + sal_Int32 nEnum = 0; + if(!::cppu::enum2int( nEnum, aValue )) + throw lang::IllegalArgumentException(); + + Orientation eOri = (static_cast(nEnum) == view::PaperOrientation_PORTRAIT)?Orientation::Portrait:Orientation::Landscape; + + if( eOri != GetPage()->GetOrientation() ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetOrientation( eOri ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetOrientation( eOri ); + } + } + break; + } + case WID_PAGE_EFFECT: + { + sal_Int32 nEnum = 0; + if(!::cppu::enum2int( nEnum, aValue )) + throw lang::IllegalArgumentException(); + + GetPage()->SetFadeEffect( static_cast(nEnum) ); + break; + } + case WID_PAGE_BACK: + setBackground( aValue ); + break; + case WID_PAGE_SPEED: + { + sal_Int32 nEnum = 0; + if(!::cppu::enum2int( nEnum, aValue )) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionDuration( nEnum == 0 ? 3.0 : (nEnum == 1 ? 2.0 : 1.0 ) ); + break; + } + case WID_PAGE_VISIBLE : + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + GetPage()->SetExcluded( !bVisible ); + break; + } + case WID_PAGE_SOUNDFILE : + { + OUString aURL; + if( aValue >>= aURL ) + { + GetPage()->SetSoundFile( aURL ); + GetPage()->SetSound( !aURL.isEmpty() ); + break; + } + else + { + bool bStopSound = false; + if( aValue >>= bStopSound ) + { + GetPage()->SetStopSound( bStopSound ); + break; + } + } + + throw lang::IllegalArgumentException(); + } + case WID_LOOP_SOUND: + { + bool bLoop = false; + if( ! (aValue >>= bLoop) ) + throw lang::IllegalArgumentException(); + + GetPage()->SetLoopSound( bLoop ); + break; + } + case WID_PAGE_BACKFULL: + { + bool bFullSize = false; + if( ! ( aValue >>= bFullSize ) ) + throw lang::IllegalArgumentException(); + GetPage()->SetBackgroundFullSize( bFullSize ); + break; + } + case WID_PAGE_BACKVIS: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + SdrPage* pPage = GetPage(); + if( pPage ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + if( rDoc.GetMasterPageCount() ) + { + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aVisibleLayers.Set(rLayerAdmin.GetLayerID(sUNO_LayerName_background), bVisible); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + } + break; + } + case WID_PAGE_BACKOBJVIS: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + SdrPage* pPage = GetPage(); + if( pPage ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + if( rDoc.GetMasterPageCount() ) + { + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aVisibleLayers.Set(rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects), bVisible); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + } + + break; + } + case WID_PAGE_USERATTRIBS: + { + if( !GetPage()->setAlienAttributes( aValue ) ) + throw lang::IllegalArgumentException(); + break; + } + case WID_PAGE_BOOKMARK: + { + OUString aBookmarkURL; + if( ! ( aValue >>= aBookmarkURL ) ) + throw lang::IllegalArgumentException(); + + setBookmarkURL( aBookmarkURL ); + break; + } + + case WID_PAGE_HEADERVISIBLE: + case WID_PAGE_HEADERTEXT: + case WID_PAGE_FOOTERVISIBLE: + case WID_PAGE_FOOTERTEXT: + case WID_PAGE_PAGENUMBERVISIBLE: + case WID_PAGE_DATETIMEVISIBLE: + case WID_PAGE_DATETIMEFIXED: + case WID_PAGE_DATETIMETEXT: + case WID_PAGE_DATETIMEFORMAT: + { + sd::HeaderFooterSettings aHeaderFooterSettings( GetPage()->getHeaderFooterSettings() ); + + switch( pEntry->nWID ) + { + case WID_PAGE_HEADERVISIBLE: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbHeaderVisible = bVisible; + break; + } + case WID_PAGE_HEADERTEXT: + { + OUString aText; + if( ! ( aValue >>= aText ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.maHeaderText = aText; + break; + } + case WID_PAGE_FOOTERVISIBLE: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbFooterVisible = bVisible; + break; + } + case WID_PAGE_FOOTERTEXT: + { + OUString aText; + if( ! ( aValue >>= aText ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.maFooterText = aText; + break; + } + case WID_PAGE_PAGENUMBERVISIBLE: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbSlideNumberVisible = bVisible; + break; + } + case WID_PAGE_DATETIMEVISIBLE: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbDateTimeVisible = bVisible; + break; + } + case WID_PAGE_DATETIMEFIXED: + { + bool bVisible = false; + if( ! ( aValue >>= bVisible ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.mbDateTimeIsFixed = bVisible; + break; + } + case WID_PAGE_DATETIMETEXT: + { + OUString aText; + if( ! ( aValue >>= aText ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.maDateTimeText = aText; + break; + } + case WID_PAGE_DATETIMEFORMAT: + { + sal_Int32 nValue = 0; + if( ! ( aValue >>= nValue ) ) + throw lang::IllegalArgumentException(); + + aHeaderFooterSettings.meDateFormat = static_cast(nValue & 0x0f); + aHeaderFooterSettings.meTimeFormat = static_cast((nValue >> 4) & 0x0f); + break; + } + } + + if( !(aHeaderFooterSettings == GetPage()->getHeaderFooterSettings()) ) + GetPage()->setHeaderFooterSettings( aHeaderFooterSettings ); + + break; + } + + case WID_PAGE_NUMBER: + if( (GetPage()->GetPageKind() == PageKind::Handout) && !GetPage()->IsMasterPage() ) + { + if( !(aValue >>= mnTempPageNumber) ) + throw lang::IllegalArgumentException(); + + break; + } + throw beans::PropertyVetoException(); + + case WID_PAGE_LDBITMAP: + case WID_PAGE_LDNAME: + case WID_PAGE_ISDARK: + throw beans::PropertyVetoException(); + + case WID_TRANSITION_TYPE: + { + sal_Int16 nValue = 0; + if( ! ( aValue >>= nValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionType( nValue ); + break; + } + + case WID_TRANSITION_SUBTYPE: + { + sal_Int16 nValue = 0; + if( ! ( aValue >>= nValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionSubtype( nValue ); + break; + } + + case WID_TRANSITION_DIRECTION: + { + bool bValue = false; + if( ! ( aValue >>= bValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionDirection( bValue ); + break; + } + + case WID_TRANSITION_FADE_COLOR: + { + sal_Int32 nValue = 0; + if( ! ( aValue >>= nValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionFadeColor( nValue ); + break; + } + + case WID_TRANSITION_DURATION: + { + double fValue = 0.0; + if( ! ( aValue >>= fValue ) ) + throw lang::IllegalArgumentException(); + + GetPage()->setTransitionDuration( fValue ); + break; + } + + case WID_PAGE_THEME: + { + SdrPage* pPage = GetPage(); + std::unique_ptr pTheme = svx::Theme::FromAny(aValue); + pPage->getSdrPageProperties().SetTheme(std::move(pTheme)); + break; + } + + default: + throw beans::UnknownPropertyException( aPropertyName, static_cast(this)); + } + + GetModel()->SetModified(); +} + +Any SAL_CALL SdGenericDrawPage::getPropertyValue( const OUString& PropertyName ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + uno::Any aAny; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); + + sal_Int16 nEntry = pEntry ? pEntry->nWID : -1; + switch (nEntry) + { + case WID_NAVORDER: + aAny = getNavigationOrder(); + break; + case WID_PAGE_LEFT: + aAny <<= GetPage()->GetLeftBorder(); + break; + case WID_PAGE_RIGHT: + aAny <<= GetPage()->GetRightBorder(); + break; + case WID_PAGE_TOP: + aAny <<= GetPage()->GetUpperBorder(); + break; + case WID_PAGE_BOTTOM: + aAny <<= GetPage()->GetLowerBorder(); + break; + case WID_PAGE_WIDTH: + aAny <<= static_cast( GetPage()->GetSize().getWidth() ); + break; + case WID_PAGE_HEIGHT: + aAny <<= static_cast( GetPage()->GetSize().getHeight() ); + break; + case WID_PAGE_ORIENT: + aAny <<= + GetPage()->GetOrientation() == Orientation::Portrait + ? view::PaperOrientation_PORTRAIT + : view::PaperOrientation_LANDSCAPE; + break; + case WID_PAGE_EFFECT: + aAny <<= GetPage()->GetFadeEffect(); + break; + case WID_PAGE_CHANGE: + aAny <<= static_cast( GetPage()->GetPresChange() ); + break; + case WID_PAGE_SPEED: + { + const double fDuration = GetPage()->getTransitionDuration(); + aAny <<= presentation::AnimationSpeed( + fDuration < 2.0 ? 2 : fDuration > 2.0 ? 0 : 1); + } + break; + case WID_PAGE_LAYOUT: + aAny <<= static_cast( GetPage()->GetAutoLayout() ); + break; + case WID_PAGE_NUMBER: + { + const sal_uInt16 nPageNumber(GetPage()->GetPageNum()); + + if(nPageNumber > 0) + { + // for all other pages calculate the number + aAny <<= static_cast(static_cast((nPageNumber-1)>>1) + 1); + } + else + { + aAny <<= mnTempPageNumber; + } + } + break; + case WID_PAGE_DURATION: + aAny <<= static_cast( GetPage()->GetTime() + .5 ); + break; + case WID_PAGE_HIGHRESDURATION: + aAny <<= GetPage()->GetTime(); + break; + case WID_PAGE_LDNAME: + { + const OUString aName( GetPage()->GetName() ); + aAny <<= aName; + break; + } + case WID_PAGE_LDBITMAP: + { + Reference< awt::XBitmap > xBitmap(VCLUnoHelper::CreateBitmap(BitmapEx(BMP_PAGE))); + aAny <<= xBitmap; + } + break; + case WID_PAGE_BACK: + getBackground( aAny ); + break; + case WID_PAGE_PREVIEW : + case WID_PAGE_PREVIEWMETAFILE : + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + ::sd::DrawDocShell* pDocShell = rDoc.GetDocSh(); + if ( pDocShell ) + { + sal_uInt16 nPgNum = 0; + sal_uInt16 nPageCount = rDoc.GetSdPageCount( PageKind::Standard ); + sal_uInt16 nPageNumber = static_cast( ( GetPage()->GetPageNum() - 1 ) >> 1 ); + while( nPgNum < nPageCount ) + { + rDoc.SetSelected( rDoc.GetSdPage( nPgNum, PageKind::Standard ), nPgNum == nPageNumber ); + nPgNum++; + } + std::shared_ptr xMetaFile = pDocShell->GetPreviewMetaFile(); + if (xMetaFile) + { + Size aSize( GetPage()->GetSize() ); + xMetaFile->AddAction( new MetaFillColorAction( COL_WHITE, true ), 0 ); + xMetaFile->AddAction( new MetaRectAction( ::tools::Rectangle( Point(), aSize ) ), 1 ); + xMetaFile->SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + xMetaFile->SetPrefSize( aSize ); + + SvMemoryStream aDestStrm( 65535, 65535 ); + if (nEntry == WID_PAGE_PREVIEW) + // Preview: WMF format. + ConvertGDIMetaFileToWMF(*xMetaFile, aDestStrm, nullptr, false); + else + { + // PreviewMetafile: SVM format. + SvmWriter aWriter(aDestStrm); + aWriter.Write(*xMetaFile); + } + Sequence aSeq( static_cast(aDestStrm.GetData()), aDestStrm.Tell() ); + aAny <<= aSeq; + } + } + } + break; + + case WID_PAGE_PREVIEWBITMAP : + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + ::sd::DrawDocShell* pDocShell = rDoc.GetDocSh(); + if ( pDocShell ) + { + sal_uInt16 nPgNum = 0; + sal_uInt16 nPageCount = rDoc.GetSdPageCount( PageKind::Standard ); + sal_uInt16 nPageNumber = static_cast( ( GetPage()->GetPageNum() - 1 ) >> 1 ); + while( nPgNum < nPageCount ) + { + rDoc.SetSelected( rDoc.GetSdPage( nPgNum, PageKind::Standard ), nPgNum == nPageNumber ); + nPgNum++; + } + std::shared_ptr xMetaFile = pDocShell->GetPreviewMetaFile(); + BitmapEx aBitmap; + if (xMetaFile && xMetaFile->CreateThumbnail(aBitmap)) + { + SvMemoryStream aMemStream; + WriteDIB(aBitmap.GetBitmap(), aMemStream, false, false); + uno::Sequence aSeq( static_cast(aMemStream.GetData()), aMemStream.Tell() ); + aAny <<= aSeq; + } + } + } + break; + + case WID_PAGE_VISIBLE : + { + bool bVisible = !GetPage()->IsExcluded(); + aAny <<= bVisible; + break; + } + + case WID_PAGE_SOUNDFILE : + { + if( GetPage()->IsStopSound() ) + { + aAny <<= true; + } + else + { + OUString aURL; + if( GetPage()->IsSoundOn() ) + aURL = GetPage()->GetSoundFile(); + aAny <<= aURL; + } + break; + } + case WID_LOOP_SOUND: + { + aAny <<= GetPage()->IsLoopSound(); + break; + } + case WID_PAGE_BACKFULL: + { + bool bFullSize = GetPage()->IsBackgroundFullSize(); + aAny <<= bFullSize; + break; + } + case WID_PAGE_BACKVIS: + { + SdrPage* pPage = GetPage(); + if( pPage ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + if( rDoc.GetMasterPageCount() ) + { + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aAny <<= aVisibleLayers.IsSet(rLayerAdmin.GetLayerID(sUNO_LayerName_background)); + } + else + { + aAny <<= false; + } + } + break; + } + case WID_PAGE_BACKOBJVIS: + { + SdrPage* pPage = GetPage(); + if( pPage ) + { + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(pPage->getSdrModelFromSdrPage())); + if( rDoc.GetMasterPageCount() ) + { + SdrLayerAdmin& rLayerAdmin = rDoc.GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + aAny <<= aVisibleLayers.IsSet(rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects)); + } + else + { + aAny <<= false; + } + } + break; + } + case WID_PAGE_USERATTRIBS: + { + GetPage()->getAlienAttributes( aAny ); + break; + } + case WID_PAGE_BOOKMARK: + { + aAny <<= getBookmarkURL(); + break; + } + case WID_PAGE_ISDARK: + { + aAny <<= GetPage()->GetPageBackgroundColor().IsDark(); + break; + } + case WID_PAGE_HEADERVISIBLE: + aAny <<= GetPage()->getHeaderFooterSettings().mbHeaderVisible; + break; + case WID_PAGE_HEADERTEXT: + { + const OUString aText( GetPage()->getHeaderFooterSettings().maHeaderText ); + aAny <<= aText; + } + break; + case WID_PAGE_FOOTERVISIBLE: + aAny <<= GetPage()->getHeaderFooterSettings().mbFooterVisible; + break; + case WID_PAGE_FOOTERTEXT: + { + const OUString aText( GetPage()->getHeaderFooterSettings().maFooterText ); + aAny <<= aText; + } + break; + case WID_PAGE_PAGENUMBERVISIBLE: + aAny <<= GetPage()->getHeaderFooterSettings().mbSlideNumberVisible; + break; + case WID_PAGE_DATETIMEVISIBLE: + aAny <<= GetPage()->getHeaderFooterSettings().mbDateTimeVisible; + break; + case WID_PAGE_DATETIMEFIXED: + aAny <<= GetPage()->getHeaderFooterSettings().mbDateTimeIsFixed; + break; + case WID_PAGE_DATETIMETEXT: + { + const OUString aText( GetPage()->getHeaderFooterSettings().maDateTimeText ); + aAny <<= aText; + } + break; + case WID_PAGE_DATETIMEFORMAT: + { + auto const & rSettings = GetPage()->getHeaderFooterSettings(); + sal_Int32 x = static_cast(rSettings.meDateFormat) | (static_cast(rSettings.meTimeFormat) << 4); + aAny <<= x; + } + break; + + case WID_TRANSITION_TYPE: + aAny <<= GetPage()->getTransitionType(); + break; + + case WID_TRANSITION_SUBTYPE: + aAny <<= GetPage()->getTransitionSubtype(); + break; + + case WID_TRANSITION_DIRECTION: + aAny <<= GetPage()->getTransitionDirection(); + break; + + case WID_TRANSITION_FADE_COLOR: + aAny <<= GetPage()->getTransitionFadeColor(); + break; + + case WID_TRANSITION_DURATION: + aAny <<= GetPage()->getTransitionDuration(); + break; + + case WID_PAGE_THEME: + { + SdrPage* pPage = GetPage(); + svx::Theme* pTheme = pPage->getSdrPageProperties().GetTheme(); + if (pTheme) + { + pTheme->ToAny(aAny); + } + else + { + beans::PropertyValues aValues; + aAny <<= aValues; + } + break; + } + + default: + throw beans::UnknownPropertyException( PropertyName, static_cast(this)); + } + return aAny; +} + +void SAL_CALL SdGenericDrawPage::addPropertyChangeListener( const OUString& , const Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdGenericDrawPage::removePropertyChangeListener( const OUString& , const Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdGenericDrawPage::addVetoableChangeListener( const OUString& , const Reference< beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdGenericDrawPage::removeVetoableChangeListener( const OUString& , const Reference< beans::XVetoableChangeListener >& ) {} + +// XMultiPropertySet +void SAL_CALL SdGenericDrawPage::setPropertyValues( const Sequence< OUString >& aPropertyNames, const Sequence< Any >& aValues ) +{ + if( aPropertyNames.getLength() != aValues.getLength() ) + throw lang::IllegalArgumentException(); + + const OUString* pNames = aPropertyNames.getConstArray(); + const Any* pValues = aValues.getConstArray(); + sal_uInt32 nCount = aValues.getLength(); + while( nCount-- ) + { + try + { + setPropertyValue( *pNames++, *pValues++ ); + } + catch( beans::UnknownPropertyException& ) + { + // ignore for multi property set + // todo: optimize this! + } + } +} + +Sequence< Any > SAL_CALL SdGenericDrawPage::getPropertyValues( const Sequence< OUString >& aPropertyNames ) +{ + sal_Int32 nCount = aPropertyNames.getLength(); + Sequence< Any > aValues( nCount ); + std::transform(aPropertyNames.begin(), aPropertyNames.end(), aValues.getArray(), + [this](const OUString& rName) -> Any { + Any aValue; + try + { + aValue = getPropertyValue(rName); + } + catch( beans::UnknownPropertyException& ) + { + // ignore for multi property set + // todo: optimize this! + } + return aValue; + }); + return aValues; +} + +void SAL_CALL SdGenericDrawPage::addPropertiesChangeListener( const Sequence< OUString >& , const Reference< beans::XPropertiesChangeListener >& ) +{ +} + +void SAL_CALL SdGenericDrawPage::removePropertiesChangeListener( const Reference< beans::XPropertiesChangeListener >& ) +{ +} + +void SAL_CALL SdGenericDrawPage::firePropertiesChangeEvent( const Sequence< OUString >& , const Reference< beans::XPropertiesChangeListener >& ) +{ +} + +Reference< drawing::XShape > SdGenericDrawPage::CreateShape(SdrObject *pObj) const +{ + DBG_ASSERT( GetPage(), "SdGenericDrawPage::CreateShape(), can't create shape for disposed page!" ); + DBG_ASSERT( pObj, "SdGenericDrawPage::CreateShape(), invalid call with pObj == 0!" ); + + if (!pObj) + return Reference< drawing::XShape >(); + + if (GetPage()) + { + PresObjKind eKind = GetPage()->GetPresObjKind(pObj); + + rtl::Reference pShape; + + if(pObj->GetObjInventor() == SdrInventor::Default) + { + SdrObjKind nInventor = pObj->GetObjIdentifier(); + switch( nInventor ) + { + case SdrObjKind::TitleText: + pShape = new SvxShapeText( pObj ); + if( GetPage()->GetPageKind() == PageKind::Notes && GetPage()->IsMasterPage() ) + { + // fake an empty PageShape if it's a title shape on the master page + pShape->SetShapeType("com.sun.star.presentation.PageShape"); + } + else + { + pShape->SetShapeType("com.sun.star.presentation.TitleTextShape"); + } + eKind = PresObjKind::NONE; + break; + case SdrObjKind::OutlineText: + pShape = new SvxShapeText( pObj ); + pShape->SetShapeType("com.sun.star.presentation.OutlinerShape"); + eKind = PresObjKind::NONE; + break; + default: ; + } + } + + Reference< drawing::XShape > xShape( pShape ); + + if(!xShape.is()) + xShape = SvxFmDrawPage::CreateShape( pObj ); + + if( eKind != PresObjKind::NONE ) + { + OUString aShapeType("com.sun.star.presentation."); + + switch( eKind ) + { + case PresObjKind::Title: + aShapeType += "TitleTextShape"; + break; + case PresObjKind::Outline: + aShapeType += "OutlinerShape"; + break; + case PresObjKind::Text: + aShapeType += "SubtitleShape"; + break; + case PresObjKind::Graphic: + aShapeType += "GraphicObjectShape"; + break; + case PresObjKind::Object: + aShapeType += "OLE2Shape"; + break; + case PresObjKind::Chart: + aShapeType += "ChartShape"; + break; + case PresObjKind::OrgChart: + aShapeType += "OrgChartShape"; + break; + case PresObjKind::Calc: + aShapeType += "CalcShape"; + break; + case PresObjKind::Table: + aShapeType += "TableShape"; + break; + case PresObjKind::Media: + aShapeType += "MediaShape"; + break; + case PresObjKind::Page: + aShapeType += "PageShape"; + break; + case PresObjKind::Handout: + aShapeType += "HandoutShape"; + break; + case PresObjKind::Notes: + aShapeType += "NotesShape"; + break; + case PresObjKind::Footer: + aShapeType += "FooterShape"; + break; + case PresObjKind::Header: + aShapeType += "HeaderShape"; + break; + case PresObjKind::SlideNumber: + aShapeType += "SlideNumberShape"; + break; + case PresObjKind::DateTime: + aShapeType += "DateTimeShape"; + break; + // coverity[dead_error_begin] - following conditions exist to avoid compiler warning + case PresObjKind::NONE: + break; + } + + if( !pShape ) + pShape = comphelper::getFromUnoTunnel( xShape ); + + if( pShape ) + pShape->SetShapeType( aShapeType ); + } + + SvxShape *pSdShape = comphelper::getFromUnoTunnel(xShape); + if (pSdShape) + { + // SdXShape aggregates SvxShape + new SdXShape(pSdShape, GetModel()); + } + return xShape; + } + else + { + return SvxFmDrawPage::CreateShape( pObj ); + } + +} + +// XServiceInfo +Sequence< OUString > SAL_CALL SdGenericDrawPage::getSupportedServiceNames() +{ + return comphelper::concatSequences( + SvxFmDrawPage::getSupportedServiceNames(), + std::initializer_list{ u"com.sun.star.drawing.GenericDrawPage", + u"com.sun.star.document.LinkTarget", + u"com.sun.star.document.LinkTargetSupplier" }); +} + +// XLinkTargetSupplier +Reference< container::XNameAccess > SAL_CALL SdGenericDrawPage::getLinks( ) +{ + return new SdPageLinkTargets( this ); +} + +void SdGenericDrawPage::setBackground( const Any& ) +{ + OSL_FAIL( "Don't call me, I'm useless!" ); +} + +void SdGenericDrawPage::getBackground( Any& ) +{ + OSL_FAIL( "Don't call me, I'm useless!" ); +} + +OUString SdGenericDrawPage::getBookmarkURL() const +{ + OUStringBuffer aRet; + if( SvxFmDrawPage::mpPage ) + { + OUString aFileName( static_cast(SvxFmDrawPage::mpPage)->GetFileName() ); + if( !aFileName.isEmpty() ) + { + const OUString aBookmarkName( SdDrawPage::getPageApiNameFromUiName( static_cast(SvxFmDrawPage::mpPage)->GetBookmarkName() ) ); + aRet.append( aFileName ); + aRet.append( '#' ); + aRet.append( aBookmarkName ); + } + } + + return aRet.makeStringAndClear(); +} + +void SdGenericDrawPage::setBookmarkURL( std::u16string_view rURL ) +{ + if( !SvxFmDrawPage::mpPage ) + return; + + size_t nIndex = rURL.find( '#' ); + if( nIndex == std::u16string_view::npos ) + return; + + const OUString aFileName( rURL.substr( 0, nIndex ) ); + const OUString aBookmarkName( SdDrawPage::getUiNameFromPageApiName( OUString(rURL.substr( nIndex+1 )) ) ); + + if( !aFileName.isEmpty() && !aBookmarkName.isEmpty() ) + { + static_cast(SvxFmDrawPage::mpPage)->DisconnectLink(); + static_cast(SvxFmDrawPage::mpPage)->SetFileName( aFileName ); + static_cast(SvxFmDrawPage::mpPage)->SetBookmarkName( aBookmarkName ); + static_cast(SvxFmDrawPage::mpPage)->ConnectLink(); + } +} + +Reference< drawing::XShape > SAL_CALL SdGenericDrawPage::combine( const Reference< drawing::XShapes >& xShapes ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + DBG_ASSERT(SvxFmDrawPage::mpPage,"SdrPage is NULL! [CL]"); + DBG_ASSERT(mpView, "SdrView is NULL! [CL]"); + + Reference< drawing::XShape > xShape; + if(mpView==nullptr||!xShapes.is()||GetPage()==nullptr) + return xShape; + + SdrPageView* pPageView = mpView->ShowSdrPage( GetPage() ); + + SelectObjectsInView( xShapes, pPageView ); + + mpView->CombineMarkedObjects( false ); + + mpView->AdjustMarkHdl(); + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( pObj ) + xShape.set( pObj->getUnoShape(), UNO_QUERY ); + } + + mpView->HideSdrPage(); + + GetModel()->SetModified(); + + return xShape; +} + +void SAL_CALL SdGenericDrawPage::split( const Reference< drawing::XShape >& xGroup ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(mpView==nullptr||!xGroup.is()||GetPage()==nullptr) + return; + + SdrPageView* pPageView = mpView->ShowSdrPage( GetPage() ); + SelectObjectInView( xGroup, pPageView ); + mpView->DismantleMarkedObjects(); + mpView->HideSdrPage(); + + GetModel()->SetModified(); +} + +Reference< drawing::XShape > SAL_CALL SdGenericDrawPage::bind( const Reference< drawing::XShapes >& xShapes ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + uno::Reference< drawing::XShape > xShape; + if(mpView==nullptr||!xShapes.is()||GetPage()==nullptr) + return xShape; + + SdrPageView* pPageView = mpView->ShowSdrPage( GetPage() ); + + SelectObjectsInView( xShapes, pPageView ); + + mpView->CombineMarkedObjects(); + + mpView->AdjustMarkHdl(); + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( pObj ) + xShape.set( pObj->getUnoShape(), UNO_QUERY ); + } + + mpView->HideSdrPage(); + + GetModel()->SetModified(); + + return xShape; +} + +void SAL_CALL SdGenericDrawPage::unbind( const Reference< drawing::XShape >& xShape ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(mpView==nullptr||!xShape.is()||GetPage()==nullptr) + return; + + SdrPageView* pPageView = mpView->ShowSdrPage( GetPage() ); + SelectObjectInView( xShape, pPageView ); + mpView->DismantleMarkedObjects( true ); + mpView->HideSdrPage(); + + GetModel()->SetModified(); +} + +void SdGenericDrawPage::SetLeftBorder( sal_Int32 nValue ) +{ + if( nValue == GetPage()->GetLeftBorder() ) + return; + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetLeftBorder( nValue ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetLeftBorder( nValue ); + } +} + +void SdGenericDrawPage::SetRightBorder( sal_Int32 nValue ) +{ + if( nValue == GetPage()->GetRightBorder() ) + return; + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetRightBorder( nValue ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetRightBorder( nValue ); + } +} + +void SdGenericDrawPage::SetUpperBorder( sal_Int32 nValue ) +{ + if( nValue == GetPage()->GetUpperBorder() ) + return; + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetUpperBorder( nValue ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetUpperBorder( nValue ); + } +} + +void SdGenericDrawPage::SetLowerBorder( sal_Int32 nValue ) +{ + if( nValue == GetPage()->GetLowerBorder() ) + return; + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetLowerBorder( nValue ); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetLowerBorder( nValue ); + } +} + +static void refreshpage( SdDrawDocument* pDoc, const PageKind ePageKind ) +{ + ::sd::DrawDocShell* pDocShell = pDoc->GetDocSh(); + if ( !pDocShell ) + return; + + ::sd::ViewShell* pViewSh = pDocShell->GetViewShell(); + + if( !pViewSh ) + return; + + if( auto pDrawViewShell = dynamic_cast<::sd::DrawViewShell* >(pViewSh) ) + pDrawViewShell->ResetActualPage(); + + Size aPageSize = pDoc->GetSdPage(0, ePageKind)->GetSize(); + const tools::Long nWidth = aPageSize.Width(); + const tools::Long nHeight = aPageSize.Height(); + + Point aPageOrg(nWidth, nHeight / 2); + Size aViewSize(nWidth * 3, nHeight * 2); + + pDoc->SetMaxObjSize(aViewSize); + + pViewSh->InitWindows(aPageOrg, aViewSize, Point(-1, -1), true); + + pViewSh->UpdateScrollBars(); +} + +void SdGenericDrawPage::SetWidth( sal_Int32 nWidth ) +{ + Size aSize( GetPage()->GetSize() ); + if( aSize.getWidth() == nWidth ) + return; + + aSize.setWidth( nWidth ); + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetSize(aSize); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetSize(aSize); + } + + refreshpage( &rDoc, ePageKind ); +} + +void SdGenericDrawPage::SetHeight( sal_Int32 nHeight ) +{ + Size aSize( GetPage()->GetSize() ); + if( aSize.getHeight() == nHeight ) + return; + + aSize.setHeight( nHeight ); + + SdDrawDocument& rDoc(static_cast< SdDrawDocument& >(GetPage()->getSdrModelFromSdrPage())); + const PageKind ePageKind = GetPage()->GetPageKind(); + + sal_uInt16 i, nPageCnt = rDoc.GetMasterSdPageCount(ePageKind); + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetMasterSdPage(i, ePageKind); + pPage->SetSize(aSize); + } + + nPageCnt = rDoc.GetSdPageCount(ePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = rDoc.GetSdPage(i, ePageKind); + pPage->SetSize(aSize); + } + + refreshpage( &rDoc, ePageKind ); +} + +// XInterface +void SdGenericDrawPage::release() noexcept +{ + + OWeakAggObject::release(); +} + +// XComponent +void SdGenericDrawPage::disposing() noexcept +{ + mpDocModel = nullptr; + SvxFmDrawPage::disposing(); +} + +// XAnimationNodeSupplier +Reference< XAnimationNode > SAL_CALL SdGenericDrawPage::getAnimationNode() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + SdPage *pSdPage = static_cast(SvxFmDrawPage::mpPage); + + return pSdPage->getAnimationNode(); +} + +// SdPageLinkTargets +SdPageLinkTargets::SdPageLinkTargets( SdGenericDrawPage* pUnoPage ) noexcept +{ + mxPage = pUnoPage; + mpUnoPage = pUnoPage; +} + +SdPageLinkTargets::~SdPageLinkTargets() noexcept +{ +} + +// XElementAccess +uno::Type SAL_CALL SdPageLinkTargets::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdPageLinkTargets::hasElements() +{ + ::SolarMutexGuard aGuard; + + SdPage* pPage = mpUnoPage->GetPage(); + if( pPage != nullptr ) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OUString aStr( pObj->GetName() ); + if( aStr.isEmpty() ) + if (auto pOleObj = dynamic_cast< const SdrOle2Obj *>( pObj )) + aStr = pOleObj->GetPersistName(); + if( !aStr.isEmpty() ) + return true; + } + } + + return false; +} + +// container::XNameAccess + +// XNameAccess +Any SAL_CALL SdPageLinkTargets::getByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + SdPage* pPage = mpUnoPage->GetPage(); + if( pPage != nullptr ) + { + SdrObject* pObj = FindObject( aName ); + if( pObj ) + { + Reference< beans::XPropertySet > aRef( pObj->getUnoShape(), uno::UNO_QUERY ); + return Any( aRef ); + } + } + + throw container::NoSuchElementException(); +} + +Sequence< OUString > SAL_CALL SdPageLinkTargets::getElementNames() +{ + ::SolarMutexGuard aGuard; + + sal_uInt32 nObjCount = 0; + + SdPage* pPage = mpUnoPage->GetPage(); + if( pPage != nullptr ) + { + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OUString aStr( pObj->GetName() ); + if( aStr.isEmpty() ) + if (auto pOleObj = dynamic_cast< const SdrOle2Obj *>( pObj )) + aStr = pOleObj->GetPersistName(); + if( !aStr.isEmpty() ) + nObjCount++; + } + } + + Sequence< OUString > aSeq( nObjCount ); + if( nObjCount > 0 ) + { + OUString* pStr = aSeq.getArray(); + + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OUString aStr( pObj->GetName() ); + if( aStr.isEmpty() ) + if (auto pOleObj = dynamic_cast< const SdrOle2Obj *>( pObj )) + aStr = pOleObj->GetPersistName(); + if( !aStr.isEmpty() ) + *pStr++ = aStr; + } + } + + return aSeq; +} + +sal_Bool SAL_CALL SdPageLinkTargets::hasByName( const OUString& aName ) +{ + ::SolarMutexGuard aGuard; + + return FindObject( aName ) != nullptr; +} + +SdrObject* SdPageLinkTargets::FindObject( std::u16string_view rName ) const noexcept +{ + SdPage* pPage = mpUnoPage->GetPage(); + if( pPage == nullptr ) + return nullptr; + + SdrObjListIter aIter( pPage, SdrIterMode::DeepWithGroups ); + + while( aIter.IsMore() ) + { + SdrObject* pObj = aIter.Next(); + OUString aStr( pObj->GetName() ); + if( aStr.isEmpty() ) + if (auto pOleObj = dynamic_cast< const SdrOle2Obj *>( pObj )) + aStr = pOleObj->GetPersistName(); + if( !aStr.isEmpty() && (aStr == rName) ) + return pObj; + } + + return nullptr; +} + +// XServiceInfo +OUString SAL_CALL SdPageLinkTargets::getImplementationName() +{ + return "SdPageLinkTargets"; +} + +sal_Bool SAL_CALL SdPageLinkTargets::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +Sequence< OUString > SAL_CALL SdPageLinkTargets::getSupportedServiceNames() +{ + return { "com.sun.star.document.LinkTargets" }; +} + +// SdDrawPage +SdDrawPage::SdDrawPage(SdXImpressDocument* pModel, SdPage* pPage) + : SdGenericDrawPage( pModel, pPage, ImplGetDrawPagePropertySet( pModel->IsImpressDocument(), pPage->GetPageKind() ) ) +{ +} + +SdDrawPage::~SdDrawPage() noexcept +{ +} + +// XInterface +Any SAL_CALL SdDrawPage::queryInterface( const uno::Type & rType ) +{ + if( rType == cppu::UnoType::get() ) + { + return Any( Reference< drawing::XMasterPageTarget >( this ) ); + } + else if( IsImpressDocument() + && rType == cppu::UnoType::get() ) + { + SdPage * p = dynamic_cast(SvxDrawPage::mpPage); + if( p == nullptr || p->GetPageKind() != PageKind::Handout ) + { + return Any( Reference< presentation::XPresentationPage >( this ) ); + } + } + + return SdGenericDrawPage::queryInterface( rType ); +} + +void SAL_CALL SdDrawPage::acquire() noexcept +{ + SvxDrawPage::acquire(); +} + +void SAL_CALL SdDrawPage::release() noexcept +{ + SvxDrawPage::release(); +} + +UNO3_GETIMPLEMENTATION2_IMPL( SdDrawPage, SdGenericDrawPage ); + +// XTypeProvider +Sequence< uno::Type > SAL_CALL SdDrawPage::getTypes() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if( !maTypeSequence.hasElements() ) + { + const PageKind ePageKind = GetPage() ? GetPage()->GetPageKind() : PageKind::Standard; + bool bPresPage = IsImpressDocument() && ePageKind != PageKind::Handout; + + // Collect the types of this class. + ::std::vector aTypes; + aTypes.reserve(13); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + if( bPresPage ) + aTypes.push_back(cppu::UnoType::get()); + if( bPresPage && ePageKind == PageKind::Standard ) + aTypes.push_back(cppu::UnoType::get()); + + // Get types of base class. + // Join those types in a sequence. + return comphelper::concatSequences( + comphelper::containerToSequence(aTypes), + SdGenericDrawPage::getTypes() ); + } + + return maTypeSequence; +} + +Sequence< sal_Int8 > SAL_CALL SdDrawPage::getImplementationId() +{ + return css::uno::Sequence(); +} + +OUString SdDrawPage::getPageApiName( SdPage const * pPage ) +{ + return ::getPageApiName( pPage ); +} + +OUString getPageApiName( SdPage const * pPage ) +{ + OUString aPageName; + + if(pPage) + { + aPageName = pPage->GetRealName(); + + if( aPageName.isEmpty() ) + { + const sal_Int32 nPageNum = ( ( pPage->GetPageNum() - 1 ) >> 1 ) + 1; + aPageName = sEmptyPageName + OUString::number( nPageNum ); + } + } + + return aPageName; +} + +OUString getPageApiNameFromUiName( const OUString& rUIName ) +{ + OUString aApiName; + + OUString aDefPageName(SdResId(STR_PAGE) + " "); + + if( rUIName.startsWith( aDefPageName ) ) + { + aApiName = OUString::Concat(sEmptyPageName) + rUIName.subView( aDefPageName.getLength() ); + } + else + { + aApiName = rUIName; + } + + return aApiName; +} + +OUString SdDrawPage::getPageApiNameFromUiName( const OUString& rUIName ) +{ + return ::getPageApiNameFromUiName( rUIName ); +} + +OUString getUiNameFromPageApiNameImpl( const OUString& rApiName ) +{ + const OUString aDefPageName( sEmptyPageName ); + if( rApiName.startsWith( aDefPageName ) ) + { + std::u16string_view aNumber( rApiName.subView( aDefPageName.getLength() ) ); + + // create the page number + sal_Int32 nPageNumber = o3tl::toInt32(aNumber); + + // check if there are non number characters in the number part + const size_t nChars = aNumber.size(); + const sal_Unicode* pString = aNumber.data(); + for( size_t nChar = 0; nChar < nChars; nChar++, pString++ ) + { + if((*pString < '0') || (*pString > '9')) + { + // found a non number character, so this is not the default + // name for this page + nPageNumber = -1; + break; + } + } + + if( nPageNumber != -1) + { + return SdResId(STR_PAGE) + " " + aNumber; + } + } + + return rApiName; +} + +OUString SdDrawPage::getUiNameFromPageApiName( const OUString& rApiName ) +{ + return getUiNameFromPageApiNameImpl( rApiName ); +} + +// XServiceInfo +OUString SAL_CALL SdDrawPage::getImplementationName() +{ + return "SdDrawPage"; +} + +Sequence< OUString > SAL_CALL SdDrawPage::getSupportedServiceNames() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + std::vector aAdd{ u"com.sun.star.drawing.DrawPage" }; + + if( IsImpressDocument() ) + aAdd.emplace_back(u"com.sun.star.presentation.DrawPage"); + + return comphelper::concatSequences(SdGenericDrawPage::getSupportedServiceNames(), aAdd); +} + +sal_Bool SAL_CALL SdDrawPage::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +// XNamed +void SAL_CALL SdDrawPage::setName( const OUString& rName ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + DBG_ASSERT( GetPage() && !GetPage()->IsMasterPage(), "Don't call base implementation for masterpages!" ); + + OUString aName( rName ); + + if(!(GetPage() && GetPage()->GetPageKind() != PageKind::Notes)) + return; + + // check if this is the default 'page1234' name + OUString aNumber; + if(aName.startsWith( sEmptyPageName, &aNumber )) + { + // ok, it maybe is, aNumber is the number part after 'page' + + // create the page number + sal_Int32 nPageNumber = aNumber.toInt32(); + + // check if there are non number characters in the number part + const sal_Int32 nChars = aNumber.getLength(); + const sal_Unicode* pString = aNumber.getStr(); + sal_Int32 nChar; + for( nChar = 0; nChar < nChars; nChar++, pString++ ) + { + if((*pString < '0') || (*pString > '9')) + { + // found a non number character, so this is not the default + // name for this page + nPageNumber = -1; + break; + } + } + + if( nPageNumber == ( ( GetPage()->GetPageNum() - 1 ) >> 1 ) + 1 ) + aName.clear(); + } + else + { + OUString aDefaultPageName( SdResId(STR_PAGE) + " " ); + if( aName.startsWith( aDefaultPageName ) ) + aName.clear(); + } + + GetPage()->SetName( aName ); + + sal_uInt16 nNotesPageNum = (GetPage()->GetPageNum()-1)>>1; + if( GetModel()->GetDoc()->GetSdPageCount( PageKind::Notes ) > nNotesPageNum ) + { + SdPage* pNotesPage = GetModel()->GetDoc()->GetSdPage( nNotesPageNum, PageKind::Notes ); + if( pNotesPage ) + pNotesPage->SetName(aName); + } + + // fake a mode change to repaint the page tab bar + ::sd::DrawDocShell* pDocSh = GetModel()->GetDocShell(); + ::sd::ViewShell* pViewSh = pDocSh ? pDocSh->GetViewShell() : nullptr; + if( auto pDrawViewSh = dynamic_cast<::sd::DrawViewShell* >(pViewSh) ) + { + EditMode eMode = pDrawViewSh->GetEditMode(); + if( eMode == EditMode::Page ) + { + bool bLayer = pDrawViewSh->IsLayerModeActive(); + + pDrawViewSh->ChangeEditMode( eMode, !bLayer ); + pDrawViewSh->ChangeEditMode( eMode, bLayer ); + } + } + + GetModel()->SetModified(); +} + +OUString SAL_CALL SdDrawPage::getName() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + return getPageApiName( GetPage() ); +} + +// XMasterPageTarget +Reference< drawing::XDrawPage > SAL_CALL SdDrawPage::getMasterPage( ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(GetPage()) + { + Reference< drawing::XDrawPage > xPage; + + if(SvxFmDrawPage::mpPage->TRG_HasMasterPage()) + { + SdrPage& rMasterPage = SvxFmDrawPage::mpPage->TRG_GetMasterPage(); + xPage.set( rMasterPage.getUnoPage(), uno::UNO_QUERY ); + } + + return xPage; + } + return nullptr; +} + +void SAL_CALL SdDrawPage::setMasterPage( const Reference< drawing::XDrawPage >& xMasterPage ) +{ + ::SolarMutexGuard aGuard; + comphelper::ProfileZone aZone("setMasterPage"); + + throwIfDisposed(); + + if(!SvxFmDrawPage::mpPage) + return; + + SdMasterPage* pMasterPage = comphelper::getFromUnoTunnel( xMasterPage ); + if( !(pMasterPage && pMasterPage->isValid()) ) + return; + + SvxFmDrawPage::mpPage->TRG_ClearMasterPage(); + + SdPage* pSdPage = static_cast(pMasterPage->GetSdrPage()); + SvxFmDrawPage::mpPage->TRG_SetMasterPage(*pSdPage); + + SvxFmDrawPage::mpPage->SetBorder(pSdPage->GetLeftBorder(),pSdPage->GetUpperBorder(), + pSdPage->GetRightBorder(),pSdPage->GetLowerBorder() ); + + SvxFmDrawPage::mpPage->SetSize( pSdPage->GetSize() ); + SvxFmDrawPage::mpPage->SetOrientation( pSdPage->GetOrientation() ); + static_cast(SvxFmDrawPage::mpPage)->SetLayoutName( pSdPage->GetLayoutName() ); + + // set notes master also + SdPage* pNotesPage = GetModel()->GetDoc()->GetSdPage( (SvxFmDrawPage::mpPage->GetPageNum()-1)>>1, PageKind::Notes ); + + pNotesPage->TRG_ClearMasterPage(); + sal_uInt16 nNum = SvxFmDrawPage::mpPage->TRG_GetMasterPage().GetPageNum() + 1; + pNotesPage->TRG_SetMasterPage(*SvxFmDrawPage::mpPage->getSdrModelFromSdrPage().GetMasterPage(nNum)); + pNotesPage->SetLayoutName( pSdPage->GetLayoutName() ); + + GetModel()->SetModified(); +} + +// XPresentationPage +Reference< drawing::XDrawPage > SAL_CALL SdDrawPage::getNotesPage() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(SvxFmDrawPage::mpPage && GetModel()->GetDoc() && SvxFmDrawPage::mpPage->GetPageNum() ) + { + SdPage* pNotesPage = GetModel()->GetDoc()->GetSdPage( (SvxFmDrawPage::mpPage->GetPageNum()-1)>>1, PageKind::Notes ); + if( pNotesPage ) + { + Reference< drawing::XDrawPage > xPage( pNotesPage->getUnoPage(), uno::UNO_QUERY ); + return xPage; + } + } + return nullptr; +} + +// XIndexAccess +sal_Int32 SAL_CALL SdDrawPage::getCount() +{ + return SdGenericDrawPage::getCount(); +} + +Any SAL_CALL SdDrawPage::getByIndex( sal_Int32 Index ) +{ + return SdGenericDrawPage::getByIndex( Index ); +} + +// XElementAccess +uno::Type SAL_CALL SdDrawPage::getElementType() +{ + return SdGenericDrawPage::getElementType(); +} + +sal_Bool SAL_CALL SdDrawPage::hasElements() +{ + return SdGenericDrawPage::hasElements(); +} + +// XShapes +void SAL_CALL SdDrawPage::add( const Reference< drawing::XShape >& xShape ) +{ + SdGenericDrawPage::add( xShape ); +} + +void SAL_CALL SdDrawPage::remove( const Reference< drawing::XShape >& xShape ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pObj ) + { + GetPage()->RemovePresObj(pObj); + pObj->SetUserCall(nullptr); + } + + SdGenericDrawPage::remove( xShape ); +} + +void SdDrawPage::setBackground( const Any& rValue ) +{ + Reference< beans::XPropertySet > xSet; + + if( !(rValue >>= xSet) && !rValue.hasValue() ) + throw lang::IllegalArgumentException(); + + if( !xSet.is() ) + { + // the easy case, no background set. Set drawing::FillStyle_NONE to represent this + GetPage()->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + return; + } + + // is it our own implementation? + SdUnoPageBackground* pBack = comphelper::getFromUnoTunnel( xSet ); + + SfxItemSetFixed aSet( GetModel()->GetDoc()->GetPool() ); + + if( pBack ) + { + pBack->fillItemSet( static_cast(&GetPage()->getSdrModelFromSdrPage()), aSet ); + } + else + { + rtl::Reference pBackground = new SdUnoPageBackground(); + + Reference< beans::XPropertySetInfo > xSetInfo( xSet->getPropertySetInfo() ); + Reference< beans::XPropertySetInfo > xDestSetInfo( pBackground->getPropertySetInfo() ); + + const Sequence< beans::Property > aProperties( xDestSetInfo->getProperties() ); + + for( const beans::Property& rProp : aProperties ) + { + const OUString aPropName( rProp.Name ); + if( xSetInfo->hasPropertyByName( aPropName ) ) + pBackground->setPropertyValue( aPropName, + xSet->getPropertyValue( aPropName ) ); + } + + pBackground->fillItemSet( static_cast(&GetPage()->getSdrModelFromSdrPage()), aSet ); + } + + if( aSet.Count() == 0 ) + { + // no background fill, represent by setting drawing::FillStyle_NONE + GetPage()->getSdrPageProperties().PutItem(XFillStyleItem(drawing::FillStyle_NONE)); + } + else + { + // background fill, set at page (not sure if ClearItem is needed) + GetPage()->getSdrPageProperties().ClearItem(); + GetPage()->getSdrPageProperties().PutItemSet(aSet); + } + + // repaint only + SvxFmDrawPage::mpPage->ActionChanged(); +} + +// XAnnotationAccess: +Reference< XAnnotation > SAL_CALL SdGenericDrawPage::createAndInsertAnnotation() +{ + if( !GetPage() ) + throw DisposedException(); + + Reference< XAnnotation > xRet; + GetPage()->createAnnotation(xRet); + return xRet; +} + +void SAL_CALL SdGenericDrawPage::removeAnnotation(const Reference< XAnnotation > & annotation) +{ + GetPage()->removeAnnotation(annotation); +} + +Reference< XAnnotationEnumeration > SAL_CALL SdGenericDrawPage::createAnnotationEnumeration() +{ + return ::sd::createAnnotationEnumeration( std::vector(GetPage()->getAnnotations()) ); +} + +void SdDrawPage::getBackground(Any& rValue) +{ + const SfxItemSet& rFillAttributes = GetPage()->getSdrPageProperties().GetItemSet(); + + if(drawing::FillStyle_NONE == rFillAttributes.Get(XATTR_FILLSTYLE).GetValue()) + { + // no fill set (switched off by drawing::FillStyle_NONE), clear rValue to represent this + rValue.clear(); + } + else + { + // there is a fill set, export to rValue + Reference< beans::XPropertySet > xSet(new SdUnoPageBackground( + GetModel()->GetDoc(), + &GetPage()->getSdrPageProperties().GetItemSet())); + rValue <<= xSet; + } +} + +void SdGenericDrawPage::setNavigationOrder( const Any& rValue ) +{ + Reference< XIndexAccess > xIA( rValue, UNO_QUERY ); + if( xIA.is() ) + { + if( dynamic_cast< SdDrawPage* >( xIA.get() ) == this ) + { + if( GetPage()->HasObjectNavigationOrder() ) + GetPage()->ClearObjectNavigationOrder(); + + return; + } + else if( static_cast(xIA->getCount()) == GetPage()->GetObjCount() ) + { + GetPage()->SetNavigationOrder(xIA); + return; + } + } + throw IllegalArgumentException(); +} + +namespace { + +class SdNavigationOrderAccess : public ::cppu::WeakImplHelper< XIndexAccess > +{ +public: + explicit SdNavigationOrderAccess(SdrPage const * pPage); + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount( ) override; + virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override; + + // XElementAccess + virtual Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + +private: + std::vector< Reference< XShape > > maShapes; +}; + +} + +SdNavigationOrderAccess::SdNavigationOrderAccess( SdrPage const * pPage ) +: maShapes( pPage ? pPage->GetObjCount() : 0 ) +{ + if( pPage ) + { + const size_t nCount = pPage->GetObjCount(); + for( size_t nIndex = 0; nIndex < nCount; ++nIndex ) + { + SdrObject* pObj = pPage->GetObj( nIndex ); + sal_uInt32 nNavPos = pObj->GetNavigationPosition(); + DBG_ASSERT( !maShapes[nNavPos].is(), "sd::SdNavigationOrderAccess::SdNavigationOrderAccess(), duplicate navigation positions from core!" ); + maShapes[nNavPos].set( pObj->getUnoShape(), UNO_QUERY ); + } + } +} + +// XIndexAccess +sal_Int32 SAL_CALL SdNavigationOrderAccess::getCount( ) +{ + return static_cast< sal_Int32 >( maShapes.size() ); +} + +Any SAL_CALL SdNavigationOrderAccess::getByIndex( sal_Int32 Index ) +{ + if( (Index < 0) || (Index > getCount()) ) + throw IndexOutOfBoundsException(); + + return Any( maShapes[Index] ); +} + +// XElementAccess +Type SAL_CALL SdNavigationOrderAccess::getElementType( ) +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdNavigationOrderAccess::hasElements( ) +{ + return !maShapes.empty(); +} + +Any SdGenericDrawPage::getNavigationOrder() +{ + if( GetPage()->HasObjectNavigationOrder() ) + { + return Any( Reference< XIndexAccess >( new SdNavigationOrderAccess( GetPage() ) ) ); + } + else + { + return Any( Reference< XIndexAccess >( this ) ); + } +} + +SdMasterPage::SdMasterPage(SdXImpressDocument* pModel, SdPage* pPage) + : SdGenericDrawPage(pModel, pPage, ImplGetMasterPagePropertySet(pPage->GetPageKind())) +{ +} + +SdMasterPage::~SdMasterPage() noexcept +{ +} + +// XInterface +Any SAL_CALL SdMasterPage::queryInterface( const uno::Type & rType ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + uno::Any aAny; + + if( rType == cppu::UnoType::get() ) + aAny <<= Reference< container::XIndexAccess >(static_cast(this)); + else if( rType == cppu::UnoType::get() ) + aAny <<= Reference< container::XElementAccess >(static_cast(this)); + else if( rType == cppu::UnoType::get() ) + aAny <<= Reference< container::XNamed >(this); + else if( rType == cppu::UnoType::get() && + ( IsImpressDocument() && + GetPage() && GetPage()->GetPageKind() != PageKind::Handout) ) + aAny <<= Reference< presentation::XPresentationPage >( this ); + else + return SdGenericDrawPage::queryInterface( rType ); + + return aAny; +} + +void SAL_CALL SdMasterPage::acquire() noexcept +{ + SvxDrawPage::acquire(); +} + +void SAL_CALL SdMasterPage::release() noexcept +{ + SvxDrawPage::release(); +} + +UNO3_GETIMPLEMENTATION2_IMPL( SdMasterPage, SdGenericDrawPage ); + +// XTypeProvider +Sequence< uno::Type > SAL_CALL SdMasterPage::getTypes() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if( !maTypeSequence.hasElements() ) + { + const PageKind ePageKind = GetPage() ? GetPage()->GetPageKind() : PageKind::Standard; + bool bPresPage = IsImpressDocument() && SvxFmDrawPage::mpPage && ePageKind != PageKind::Handout; + + // Collect the types of this class. + ::std::vector aTypes; + aTypes.reserve(12); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + aTypes.push_back(cppu::UnoType::get()); + if( bPresPage ) + aTypes.push_back(cppu::UnoType::get()); + if( bPresPage && ePageKind == PageKind::Standard ) + aTypes.push_back(cppu::UnoType::get()); + + // Get types of base class. + // Join those types in a sequence. + return comphelper::concatSequences( + comphelper::containerToSequence(aTypes), + SdGenericDrawPage::getTypes() ); + } + + return maTypeSequence; +} + +Sequence< sal_Int8 > SAL_CALL SdMasterPage::getImplementationId() +{ + return css::uno::Sequence(); +} + +// XServiceInfo +OUString SAL_CALL SdMasterPage::getImplementationName() +{ + return "SdMasterPage"; +} + +Sequence< OUString > SAL_CALL SdMasterPage::getSupportedServiceNames() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + std::vector aAdd{ u"com.sun.star.drawing.MasterPage" }; + + if( SvxFmDrawPage::mpPage && static_cast(SvxFmDrawPage::mpPage)->GetPageKind() == PageKind::Handout ) + aAdd.emplace_back(u"com.sun.star.presentation.HandoutMasterPage"); + + return comphelper::concatSequences(SdGenericDrawPage::getSupportedServiceNames(), aAdd); +} + +sal_Bool SAL_CALL SdMasterPage::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +// XElementAccess +sal_Bool SAL_CALL SdMasterPage::hasElements() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if( SvxFmDrawPage::mpPage == nullptr ) + return false; + + return SvxFmDrawPage::mpPage->GetObjCount() > 0; +} + +uno::Type SAL_CALL SdMasterPage::getElementType() +{ + return SdGenericDrawPage::getElementType(); +} + +// XIndexAccess +sal_Int32 SAL_CALL SdMasterPage::getCount() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + return SdGenericDrawPage::getCount(); +} + +Any SAL_CALL SdMasterPage::getByIndex( sal_Int32 Index ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + return SdGenericDrawPage::getByIndex(Index); +} + +// intern +void SdMasterPage::setBackground( const Any& rValue ) +{ + // we need at least a beans::XPropertySet + Reference< beans::XPropertySet > xInputSet( rValue, UNO_QUERY ); + if( !xInputSet.is() ) + throw lang::IllegalArgumentException(); + + try + { + if( GetModel() && IsImpressDocument() ) + { + Reference< container::XNameAccess > xFamilies( GetModel()->getStyleFamilies(), UNO_SET_THROW ); + Reference< container::XNameAccess > xFamily( xFamilies->getByName( getName() ), UNO_QUERY_THROW ) ; + + Reference< beans::XPropertySet > xStyleSet( xFamily->getByName( sUNO_PseudoSheet_Background ), UNO_QUERY_THROW ); + + Reference< beans::XPropertySetInfo > xSetInfo( xInputSet->getPropertySetInfo(), UNO_SET_THROW ); + Reference< beans::XPropertyState > xSetStates( xInputSet, UNO_QUERY ); + + for( const auto pProp : ImplGetPageBackgroundPropertySet()->getPropertyMap().getPropertyEntries() ) + { + const OUString& rPropName = pProp->aName; + if( xSetInfo->hasPropertyByName( rPropName ) ) + { + if( !xSetStates.is() || xSetStates->getPropertyState( rPropName ) == beans::PropertyState_DIRECT_VALUE ) + xStyleSet->setPropertyValue( rPropName, xInputSet->getPropertyValue( rPropName ) ); + else + xSetStates->setPropertyToDefault( rPropName ); + } + } + } + else + { + // first fill an item set + // is it our own implementation? + SdUnoPageBackground* pBack = comphelper::getFromUnoTunnel( xInputSet ); + + SfxItemSetFixed aSet( GetModel()->GetDoc()->GetPool() ); + + if( pBack ) + { + pBack->fillItemSet( static_cast(&GetPage()->getSdrModelFromSdrPage()), aSet ); + } + else + { + rtl::Reference pBackground = new SdUnoPageBackground(); + + Reference< beans::XPropertySetInfo > xInputSetInfo( xInputSet->getPropertySetInfo(), UNO_SET_THROW ); + Reference< beans::XPropertySetInfo > xDestSetInfo( pBackground->getPropertySetInfo(), UNO_SET_THROW ); + + const uno::Sequence< beans::Property> aProperties( xDestSetInfo->getProperties() ); + + for( const beans::Property& rProp : aProperties ) + { + const OUString aPropName( rProp.Name ); + if( xInputSetInfo->hasPropertyByName( aPropName ) ) + pBackground->setPropertyValue( aPropName, xInputSet->getPropertyValue( aPropName ) ); + } + + pBackground->fillItemSet( static_cast(&SvxFmDrawPage::mpPage->getSdrModelFromSdrPage()), aSet ); + } + + // if we find the background style, copy the set to the background + SdDrawDocument* pDoc = static_cast(&SvxFmDrawPage::mpPage->getSdrModelFromSdrPage()); + SfxStyleSheetBasePool* pSSPool = pDoc->GetStyleSheetPool(); + if(pSSPool) + { + OUString aLayoutName( static_cast< SdPage* >( SvxFmDrawPage::mpPage )->GetLayoutName() ); + aLayoutName = OUString::Concat(aLayoutName.subView(0, aLayoutName.indexOf(SD_LT_SEPARATOR)+4)) + + STR_LAYOUT_BACKGROUND; + SfxStyleSheetBase* pStyleSheet = pSSPool->Find( aLayoutName, SfxStyleFamily::Page ); + + if( pStyleSheet ) + { + pStyleSheet->GetItemSet().Put( aSet ); + + // repaint only + SvxFmDrawPage::mpPage->ActionChanged(); + return; + } + } + + // if no background style is available, set at page directly. This + // is an error and should NOT happen (and will be asserted from the SdrPage) + GetPage()->getSdrPageProperties().PutItemSet(aSet); + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdMasterPage::setBackground()"); + } +} + +void SdMasterPage::getBackground( Any& rValue ) +{ + if( !GetModel() ) + return; + + try + { + if( IsImpressDocument() ) + { + Reference< container::XNameAccess > xFamilies( GetModel()->getStyleFamilies(), UNO_SET_THROW ); + Reference< container::XNameAccess > xFamily( xFamilies->getByName( getName() ), UNO_QUERY_THROW ); + + rValue <<= Reference< beans::XPropertySet >( xFamily->getByName( sUNO_PseudoSheet_Background ), UNO_QUERY_THROW ); + } + else + { + SdDrawDocument* pDoc = static_cast(&SvxFmDrawPage::mpPage->getSdrModelFromSdrPage()); + SfxStyleSheetBasePool* pSSPool = pDoc->GetStyleSheetPool(); + if(pSSPool) + { + OUString aLayoutName( static_cast< SdPage* >(SvxFmDrawPage::mpPage)->GetLayoutName() ); + aLayoutName = OUString::Concat(aLayoutName.subView(0, aLayoutName.indexOf(SD_LT_SEPARATOR)+4)) + + STR_LAYOUT_BACKGROUND; + SfxStyleSheetBase* pStyleSheet = pSSPool->Find( aLayoutName, SfxStyleFamily::Page ); + + if( pStyleSheet ) + { + SfxItemSet aStyleSet( pStyleSheet->GetItemSet()); + if( aStyleSet.Count() ) + { + rValue <<= Reference< beans::XPropertySet >( new SdUnoPageBackground( pDoc, &aStyleSet ) ); + return; + } + } + } + + // No style found, use fill attributes from page background. This + // should NOT happen and is an error + const SfxItemSet& rFallbackItemSet(SvxFmDrawPage::mpPage->getSdrPageProperties().GetItemSet()); + + if(drawing::FillStyle_NONE == rFallbackItemSet.Get(XATTR_FILLSTYLE).GetValue()) + { + rValue <<= Reference< beans::XPropertySet >( + new SdUnoPageBackground(GetModel()->GetDoc(), &rFallbackItemSet)); + } + else + { + rValue.clear(); + } + } + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::SdMasterPage::getBackground()"); + rValue.clear(); + } +} + +// XNamed +void SAL_CALL SdMasterPage::setName( const OUString& rName ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(!(SvxFmDrawPage::mpPage && GetPage()->GetPageKind() != PageKind::Notes)) + return; + + SdDrawDocument* pDoc = GetModel()->GetDoc(); + bool bOutDummy; + + // Slide Name has to be unique + if( pDoc && pDoc->GetPageByName( rName, bOutDummy ) != SDRPAGE_NOTFOUND ) + return; // throw Exception ? + + GetPage()->SetName( rName ); + + if( pDoc ) + pDoc->RenameLayoutTemplate( GetPage()->GetLayoutName(), rName ); + + // fake a mode change to repaint the page tab bar + ::sd::DrawDocShell* pDocSh = GetModel()->GetDocShell(); + ::sd::ViewShell* pViewSh = pDocSh ? pDocSh->GetViewShell() : nullptr; + if( auto pDrawViewSh = dynamic_cast< ::sd::DrawViewShell* >(pViewSh) ) + { + EditMode eMode = pDrawViewSh->GetEditMode(); + if( eMode == EditMode::MasterPage ) + { + bool bLayer = pDrawViewSh->IsLayerModeActive(); + + pDrawViewSh->ChangeEditMode( eMode, !bLayer ); + pDrawViewSh->ChangeEditMode( eMode, bLayer ); + } + } + + GetModel()->SetModified(); +} + +OUString SAL_CALL SdMasterPage::getName( ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(SvxFmDrawPage::mpPage) + { + OUString aLayoutName( GetPage()->GetLayoutName() ); + return aLayoutName.copy(0, aLayoutName.indexOf(SD_LT_SEPARATOR)); + } + + return OUString(); +} + +// XPresentationPage +Reference< drawing::XDrawPage > SAL_CALL SdMasterPage::getNotesPage() +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + if(SvxFmDrawPage::mpPage && GetModel()->GetDoc() ) + { + SdPage* pNotesPage = GetModel()->GetDoc()->GetMasterSdPage( (SvxFmDrawPage::mpPage->GetPageNum()-1)>>1, PageKind::Notes ); + if( pNotesPage ) + { + Reference< drawing::XDrawPage > xPage( pNotesPage->getUnoPage(), uno::UNO_QUERY ); + return xPage; + } + } + return nullptr; +} + +// XShapes +void SAL_CALL SdMasterPage::add( const Reference< drawing::XShape >& xShape ) +{ + SdGenericDrawPage::add( xShape ); +} + +void SAL_CALL SdMasterPage::remove( const Reference< drawing::XShape >& xShape ) +{ + ::SolarMutexGuard aGuard; + + throwIfDisposed(); + + SdrObject* pObj = SdrObject::getSdrObjectFromXShape( xShape ); + if( pObj && GetPage()->IsPresObj( pObj ) ) + GetPage()->RemovePresObj(pObj); + + SdGenericDrawPage::remove( xShape ); +} + +Reference< uno::XInterface > createUnoPageImpl( SdPage* pPage ) +{ + Reference< uno::XInterface > xPage; + + if( pPage ) + { + SdXImpressDocument* pModel = comphelper::getFromUnoTunnel( pPage->getSdrModelFromSdrPage().getUnoModel() ); + if( pModel ) + { + if( pPage->IsMasterPage() ) + { + xPage = static_cast(new SdMasterPage( pModel, pPage )); + } + else + { + xPage = static_cast(new SdDrawPage( pModel, pPage )); + } + } + } + + return xPage; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopback.cxx b/sd/source/ui/unoidl/unopback.cxx new file mode 100644 index 000000000..508b1f866 --- /dev/null +++ b/sd/source/ui/unoidl/unopback.cxx @@ -0,0 +1,410 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "unopback.hxx" +#include +#include + +using namespace ::com::sun::star; + +const SvxItemPropertySet* ImplGetPageBackgroundPropertySet() +{ + static const SfxItemPropertyMapEntry aPageBackgroundPropertyMap_Impl[] = + { + FILL_PROPERTIES + { u"", 0, css::uno::Type(), 0, 0 } + }; + + static SvxItemPropertySet aPageBackgroundPropertySet_Impl( aPageBackgroundPropertyMap_Impl, SdrObject::GetGlobalDrawObjectItemPool() ); + return &aPageBackgroundPropertySet_Impl; +} + +UNO3_GETIMPLEMENTATION_IMPL( SdUnoPageBackground ); + +SdUnoPageBackground::SdUnoPageBackground( + SdDrawDocument* pDoc /* = NULL */, + const SfxItemSet* pSet /* = NULL */) +: mpPropSet(ImplGetPageBackgroundPropertySet()), + mpDoc(pDoc) +{ + if( pDoc ) + { + StartListening( *pDoc ); + mpSet = std::make_unique>( pDoc->GetPool() ); + + if( pSet ) + mpSet->Put(*pSet); + } +} + +SdUnoPageBackground::~SdUnoPageBackground() noexcept +{ + SolarMutexGuard g; + + if( mpDoc ) + EndListening( *mpDoc ); +} + +void SdUnoPageBackground::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if (rHint.GetId() != SfxHintId::ThisIsAnSdrHint) + return; + const SdrHint* pSdrHint = static_cast( &rHint ); + + // delete item set if document is dying because then the pool + // will also die + if( pSdrHint->GetKind() == SdrHintKind::ModelCleared ) + { + mpSet.reset(); + mpDoc = nullptr; + } +} + +void SdUnoPageBackground::fillItemSet( SdDrawDocument* pDoc, SfxItemSet& rSet ) +{ + rSet.ClearItem(); + + if( mpSet == nullptr ) + { + StartListening( *pDoc ); + mpDoc = pDoc; + + mpSet = std::make_unique>( *rSet.GetPool() ); + + if( maUsrAnys.AreThereOwnUsrAnys() ) + { + for( const auto pProp : mpPropSet->getPropertyMap().getPropertyEntries() ) + { + uno::Any* pAny = maUsrAnys.GetUsrAnyForID( *pProp ); + if( pAny ) + { + const OUString & aPropertyName = pProp->aName; + switch( pProp->nWID ) + { + case XATTR_FILLFLOATTRANSPARENCE : + case XATTR_FILLGRADIENT : + { + if ( ( pAny->getValueType() == ::cppu::UnoType< css::awt::Gradient>::get() ) + && ( pProp->nMemberId == MID_FILLGRADIENT ) ) + { + setPropertyValue( aPropertyName, *pAny ); + } + else if ( ( pAny->getValueType() == ::cppu::UnoType::get() ) && + ( pProp->nMemberId == MID_NAME ) ) + { + setPropertyValue( aPropertyName, *pAny ); + } + } + break; + case XATTR_FILLHATCH : + { + if ( ( pAny->getValueType() == ::cppu::UnoType< css::drawing::Hatch>::get() ) + && ( pProp->nMemberId == MID_FILLHATCH ) ) + { + setPropertyValue( aPropertyName, *pAny ); + } + else if ( ( pAny->getValueType() == ::cppu::UnoType::get() ) && + ( pProp->nMemberId == MID_NAME ) ) + { + setPropertyValue( aPropertyName, *pAny ); + } + } + break; + case XATTR_FILLBITMAP : + { + if (pProp->nMemberId == MID_BITMAP && + (pAny->getValueType() == cppu::UnoType::get() || + pAny->getValueType() == cppu::UnoType::get())) + { + setPropertyValue( aPropertyName, *pAny ); + } + else if (pAny->getValueType() == ::cppu::UnoType::get() && pProp->nMemberId == MID_NAME) + { + setPropertyValue( aPropertyName, *pAny ); + } + } + break; + + default: + setPropertyValue( aPropertyName, *pAny ); + } + } + } + } + } + + rSet.Put( *mpSet ); +} + +// XServiceInfo +OUString SAL_CALL SdUnoPageBackground::getImplementationName() +{ + return "SdUnoPageBackground"; +} + +sal_Bool SAL_CALL SdUnoPageBackground::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService( this, ServiceName ); +} + +uno::Sequence< OUString > SAL_CALL SdUnoPageBackground::getSupportedServiceNames() +{ + return { sUNO_Service_PageBackground, sUNO_Service_FillProperties }; +} + +// XPropertySet +uno::Reference< beans::XPropertySetInfo > SAL_CALL SdUnoPageBackground::getPropertySetInfo() +{ + return mpPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdUnoPageBackground::setPropertyValue( const OUString& aPropertyName, const uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry( aPropertyName ); + + if( pEntry == nullptr ) + { + throw beans::UnknownPropertyException( aPropertyName, static_cast(this)); + } + + if( mpSet ) + { + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + drawing::BitmapMode eMode; + if( aValue >>= eMode ) + { + mpSet->Put( XFillBmpStretchItem( eMode == drawing::BitmapMode_STRETCH ) ); + mpSet->Put( XFillBmpTileItem( eMode == drawing::BitmapMode_REPEAT ) ); + return; + } + throw lang::IllegalArgumentException(); + } + + SfxItemPool& rPool = *mpSet->GetPool(); + SfxItemSet aSet( rPool, pEntry->nWID, pEntry->nWID); + aSet.Put( *mpSet ); + + if( !aSet.Count() ) + aSet.Put( rPool.GetDefaultItem( pEntry->nWID ) ); + + if( pEntry->nMemberId == MID_NAME && ( pEntry->nWID == XATTR_FILLBITMAP || pEntry->nWID == XATTR_FILLGRADIENT || pEntry->nWID == XATTR_FILLHATCH || pEntry->nWID == XATTR_FILLFLOATTRANSPARENCE ) ) + { + OUString aName; + if(!(aValue >>= aName )) + throw lang::IllegalArgumentException(); + + SvxShape::SetFillAttribute( pEntry->nWID, aName, aSet ); + } + else + { + SvxItemPropertySet_setPropertyValue( pEntry, aValue, aSet ); + } + + mpSet->Put( aSet ); + } + else + { + if(pEntry->nWID) + SvxItemPropertySet::setPropertyValue( pEntry, aValue, maUsrAnys ); + } +} + +uno::Any SAL_CALL SdUnoPageBackground::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aAny; + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry(PropertyName); + + if( pEntry == nullptr ) + { + throw beans::UnknownPropertyException( PropertyName, static_cast(this)); + } + + if( mpSet ) + { + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + const XFillBmpStretchItem* pStretchItem = mpSet->GetItem(XATTR_FILLBMP_STRETCH); + const XFillBmpTileItem* pTileItem = mpSet->GetItem(XATTR_FILLBMP_TILE); + + if( pStretchItem && pTileItem ) + { + if( pTileItem->GetValue() ) + aAny <<= drawing::BitmapMode_REPEAT; + else if( pStretchItem->GetValue() ) + aAny <<= drawing::BitmapMode_STRETCH; + else + aAny <<= drawing::BitmapMode_NO_REPEAT; + } + } + else + { + SfxItemPool& rPool = *mpSet->GetPool(); + SfxItemSet aSet( rPool, pEntry->nWID, pEntry->nWID); + aSet.Put( *mpSet ); + + if( !aSet.Count() ) + aSet.Put( rPool.GetDefaultItem( pEntry->nWID ) ); + + // get value from ItemSet + aAny = SvxItemPropertySet_getPropertyValue( pEntry, aSet ); + } + } + else + { + if(pEntry->nWID) + aAny = mpPropSet->getPropertyValue( pEntry, maUsrAnys ); + } + return aAny; +} + +void SAL_CALL SdUnoPageBackground::addPropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdUnoPageBackground::removePropertyChangeListener( const OUString& , const uno::Reference< beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdUnoPageBackground::addVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdUnoPageBackground::removeVetoableChangeListener( const OUString& , const uno::Reference< beans::XVetoableChangeListener >& ) {} + +// XPropertyState +beans::PropertyState SAL_CALL SdUnoPageBackground::getPropertyState( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry(PropertyName); + + if( pEntry == nullptr ) + throw beans::UnknownPropertyException( PropertyName, static_cast(this)); + + if( mpSet ) + { + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + if( mpSet->GetItemState( XATTR_FILLBMP_STRETCH, false ) == SfxItemState::SET || + mpSet->GetItemState( XATTR_FILLBMP_TILE, false ) == SfxItemState::SET ) + { + return beans::PropertyState_DIRECT_VALUE; + } + else + { + return beans::PropertyState_AMBIGUOUS_VALUE; + } + } + + switch( mpSet->GetItemState( pEntry->nWID, false ) ) + { + case SfxItemState::SET: + return beans::PropertyState_DIRECT_VALUE; + case SfxItemState::DEFAULT: + return beans::PropertyState_DEFAULT_VALUE; + default: +// case SfxItemState::DONTCARE: +// case SfxItemState::DISABLED: + return beans::PropertyState_AMBIGUOUS_VALUE; + } + } + else + { + if( nullptr == maUsrAnys.GetUsrAnyForID(*pEntry) ) + return beans::PropertyState_DEFAULT_VALUE; + else + return beans::PropertyState_DIRECT_VALUE; + } +} + +uno::Sequence< beans::PropertyState > SAL_CALL SdUnoPageBackground::getPropertyStates( const uno::Sequence< OUString >& aPropertyName ) +{ + SolarMutexGuard aGuard; + + sal_Int32 nCount = aPropertyName.getLength(); + + uno::Sequence< beans::PropertyState > aPropertyStateSequence( nCount ); + + std::transform(aPropertyName.begin(), aPropertyName.end(), aPropertyStateSequence.getArray(), + [this](const OUString& rName) -> beans::PropertyState { return getPropertyState(rName); }); + + return aPropertyStateSequence; +} + +void SAL_CALL SdUnoPageBackground::setPropertyToDefault( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry(PropertyName); + + if( pEntry == nullptr ) + throw beans::UnknownPropertyException( PropertyName, static_cast(this)); + + if( mpSet ) + { + if( pEntry->nWID == OWN_ATTR_FILLBMP_MODE ) + { + mpSet->ClearItem( XATTR_FILLBMP_STRETCH ); + mpSet->ClearItem( XATTR_FILLBMP_TILE ); + } + else + { + mpSet->ClearItem( pEntry->nWID ); + } + } +} + +uno::Any SAL_CALL SdUnoPageBackground::getPropertyDefault( const OUString& aPropertyName ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = getPropertyMapEntry(aPropertyName); + if( pEntry == nullptr || mpSet == nullptr ) + throw beans::UnknownPropertyException( aPropertyName, static_cast(this)); + + uno::Any aAny; + if (pEntry->nWID == OWN_ATTR_FILLBMP_MODE) + { + aAny <<= drawing::BitmapMode_REPEAT; + } + else + { + SfxItemPool& rPool = *mpSet->GetPool(); + SfxItemSet aSet(rPool, pEntry->nWID, pEntry->nWID); + aSet.Put(rPool.GetDefaultItem(pEntry->nWID)); + + aAny = SvxItemPropertySet_getPropertyValue(pEntry, aSet); + } + return aAny; +} + +/** this is used because our property map is not sorted yet */ +const SfxItemPropertyMapEntry* SdUnoPageBackground::getPropertyMapEntry( std::u16string_view rPropertyName ) const noexcept +{ + return mpPropSet->getPropertyMap().getByName(rPropertyName); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopback.hxx b/sd/source/ui/unoidl/unopback.hxx new file mode 100644 index 000000000..c70cc2fea --- /dev/null +++ b/sd/source/ui/unoidl/unopback.hxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +class SdDrawDocument; +class SdrModel; +class SfxItemSet; +class SvxItemPropertySet; +struct SfxItemPropertyMapEntry; + +const SvxItemPropertySet* ImplGetPageBackgroundPropertySet(); + +class SdUnoPageBackground final : public ::cppu::WeakImplHelper< + css::beans::XPropertySet, + css::lang::XServiceInfo, + css::beans::XPropertyState, + css::lang::XUnoTunnel>, + public SfxListener +{ + const SvxItemPropertySet* mpPropSet; + SvxItemPropertySetUsrAnys maUsrAnys; + std::unique_ptr mpSet; + SdrModel* mpDoc; + + const SfxItemPropertyMapEntry* getPropertyMapEntry( std::u16string_view rPropertyName ) const noexcept; +public: + SdUnoPageBackground( SdDrawDocument* pDoc = nullptr, const SfxItemSet* pSet = nullptr); + virtual ~SdUnoPageBackground() noexcept override; + + // internal + void fillItemSet( SdDrawDocument* pDoc, SfxItemSet& rSet ); + virtual void Notify( SfxBroadcaster& rBC, const SfxHint& rHint ) override; + + // uno helper + UNO3_GETIMPLEMENTATION_DECL( SdUnoPageBackground ) + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) override; + virtual css::uno::Any SAL_CALL getPropertyValue( const OUString& PropertyName ) override; + virtual void SAL_CALL addPropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& xListener ) override; + virtual void SAL_CALL removePropertyChangeListener( const OUString& aPropertyName, const css::uno::Reference< css::beans::XPropertyChangeListener >& aListener ) override; + virtual void SAL_CALL addVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + virtual void SAL_CALL removeVetoableChangeListener( const OUString& PropertyName, const css::uno::Reference< css::beans::XVetoableChangeListener >& aListener ) override; + + // XPropertyState + virtual css::beans::PropertyState SAL_CALL getPropertyState( const OUString& PropertyName ) override; + virtual css::uno::Sequence< css::beans::PropertyState > SAL_CALL getPropertyStates( const css::uno::Sequence< OUString >& aPropertyName ) override; + virtual void SAL_CALL setPropertyToDefault( const OUString& PropertyName ) override; + virtual css::uno::Any SAL_CALL getPropertyDefault( const OUString& aPropertyName ) override; +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopool.cxx b/sd/source/ui/unoidl/unopool.cxx new file mode 100644 index 000000000..7345dc45d --- /dev/null +++ b/sd/source/ui/unoidl/unopool.cxx @@ -0,0 +1,89 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include "unopool.hxx" + +using namespace ::com::sun::star; +using namespace ::cppu; +using namespace ::comphelper; + +static LanguageType SdUnoGetLanguage( const lang::Locale& rLocale ) +{ + // empty language -> LANGUAGE_SYSTEM + if ( rLocale.Language.getLength() == 0 ) + return LANGUAGE_SYSTEM; + + LanguageType eRet = LanguageTag::convertToLanguageType( rLocale, false); + if ( eRet == LANGUAGE_NONE ) + eRet = LANGUAGE_SYSTEM; //! or throw an exception? + + return eRet; +} + +namespace { + +class SdUnoDrawPool : public SvxUnoDrawPool +{ +public: + explicit SdUnoDrawPool(SdDrawDocument* pModel); + +protected: + virtual void putAny( SfxItemPool* pPool, const PropertyMapEntry* pEntry, const uno::Any& rValue ) override; + +private: + SdDrawDocument* mpDrawModel; +}; + +} + +SdUnoDrawPool::SdUnoDrawPool(SdDrawDocument* pModel) +: SvxUnoDrawPool( pModel ), mpDrawModel( pModel ) +{ +} + +void SdUnoDrawPool::putAny( SfxItemPool* pPool, const comphelper::PropertyMapEntry* pEntry, const uno::Any& rValue ) +{ + switch( pEntry->mnHandle ) + { + case EE_CHAR_LANGUAGE: + case EE_CHAR_LANGUAGE_CJK: + case EE_CHAR_LANGUAGE_CTL: + { + lang::Locale aLocale; + if( rValue >>= aLocale ) + mpDrawModel->SetLanguage( + SdUnoGetLanguage( aLocale ), + static_cast(pEntry->mnHandle) ); + } + } + SvxUnoDrawPool::putAny( pPool, pEntry, rValue ); +} + +uno::Reference< uno::XInterface > SdUnoCreatePool( SdDrawDocument* pDrawModel ) +{ + return static_cast(new SdUnoDrawPool( pDrawModel )); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unopool.hxx b/sd/source/ui/unoidl/unopool.hxx new file mode 100644 index 000000000..4188e80a9 --- /dev/null +++ b/sd/source/ui/unoidl/unopool.hxx @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include + +#include +#include + +css::uno::Reference SdUnoCreatePool(SdDrawDocument* pDrawModel); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unosrch.cxx b/sd/source/ui/unoidl/unosrch.cxx new file mode 100644 index 000000000..f1005819d --- /dev/null +++ b/sd/source/ui/unoidl/unosrch.cxx @@ -0,0 +1,778 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; + +#define WID_SEARCH_BACKWARDS 0 +#define WID_SEARCH_CASE 1 +#define WID_SEARCH_WORDS 2 + +static const SfxItemPropertyMapEntry* ImplGetSearchPropertyMap() +{ + static const SfxItemPropertyMapEntry aSearchPropertyMap_Impl[] = + { + { u"" UNO_NAME_SEARCH_BACKWARDS, WID_SEARCH_BACKWARDS, cppu::UnoType::get(), 0, 0 }, + { u"" UNO_NAME_SEARCH_CASE, WID_SEARCH_CASE, cppu::UnoType::get(), 0, 0 }, + { u"" UNO_NAME_SEARCH_WORDS, WID_SEARCH_WORDS, cppu::UnoType::get(), 0, 0 }, + { u"", 0, css::uno::Type(), 0, 0 } + }; + + return aSearchPropertyMap_Impl; +} + +namespace { + +class SearchContext_impl +{ + uno::Reference< drawing::XShapes > mxShapes; + sal_Int32 mnIndex; + +public: + SearchContext_impl(uno::Reference const& xShapes) + : mxShapes( xShapes ), mnIndex( -1 ) {} + + uno::Reference< drawing::XShape > firstShape() + { + mnIndex = -1; + return nextShape(); + } + + uno::Reference< drawing::XShape > nextShape() + { + uno::Reference< drawing::XShape > xShape; + mnIndex++; + if( mxShapes.is() && mxShapes->getCount() > mnIndex ) + { + mxShapes->getByIndex( mnIndex ) >>= xShape; + } + return xShape; + } +}; + +} + +/* ================================================================= */ +/** this class implements a search or replace operation on a given + page or a given sdrobj + */ + +SdUnoSearchReplaceShape::SdUnoSearchReplaceShape( drawing::XDrawPage* pPage ) noexcept + : mpPage(pPage) +{ +} + +SdUnoSearchReplaceShape::~SdUnoSearchReplaceShape() noexcept +{ +} + +// util::XReplaceable +uno::Reference< util::XReplaceDescriptor > SAL_CALL SdUnoSearchReplaceShape::createReplaceDescriptor() +{ + return new SdUnoSearchReplaceDescriptor; +} + +sal_Int32 SAL_CALL SdUnoSearchReplaceShape::replaceAll( const uno::Reference< util::XSearchDescriptor >& xDesc ) +{ + SdUnoSearchReplaceDescriptor* pDescr = comphelper::getFromUnoTunnel( xDesc ); + if( pDescr == nullptr ) + return 0; + + sal_Int32 nFound = 0; + + uno::Reference< drawing::XShapes > xShapes; + uno::Reference< drawing::XShape > xShape; + + std::vector aContexts; + if(mpPage) + { + xShapes = mpPage; + + if( xShapes->getCount() ) + { + aContexts.push_back(SearchContext_impl(xShapes)); + xShape = aContexts.back().firstShape(); + } + else + { + xShapes = nullptr; + } + } + + while( xShape.is() ) + { + // replace in xShape + uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY); + uno::Reference< text::XTextRange > xRange = xText; + uno::Reference< text::XTextRange > xFound; + + while( xRange.is() ) + { + xFound = Search( xRange, pDescr ); + if( !xFound.is() ) + break; + + xFound->setString( pDescr->getReplaceString() ); + xRange = xFound->getEnd(); + nFound++; + } + // done with xShape -> get next shape + + // test if it's a group + uno::Reference< drawing::XShapes > xGroupShape( xShape, uno::UNO_QUERY ); + if( xGroupShape.is() && ( xGroupShape->getCount() > 0 ) ) + { + aContexts.push_back(SearchContext_impl(xGroupShape)); + xShape = aContexts.back().firstShape(); + } + else + { + if (!aContexts.empty()) + xShape = aContexts.back().nextShape(); + else + xShape = nullptr; + } + + // test parent contexts for next shape if none + // is found in the current context + while (!aContexts.empty() && !xShape.is()) + { + aContexts.pop_back(); + if (!aContexts.empty()) + xShape = aContexts.back().nextShape(); + } + } + + return nFound; +} + +// XSearchable +uno::Reference< css::util::XSearchDescriptor > SAL_CALL SdUnoSearchReplaceShape::createSearchDescriptor( ) +{ + return new SdUnoSearchReplaceDescriptor; +} + +uno::Reference< css::container::XIndexAccess > SAL_CALL SdUnoSearchReplaceShape::findAll( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) +{ + SdUnoSearchReplaceDescriptor* pDescr = comphelper::getFromUnoTunnel( xDesc ); + if( pDescr == nullptr ) + return uno::Reference< container::XIndexAccess > (); + + sal_Int32 nSequence = 32; + sal_Int32 nFound = 0; + + uno::Sequence < uno::Reference< uno::XInterface > > aSeq( nSequence ); + + uno::Reference< uno::XInterface > * pArray = aSeq.getArray(); + + uno::Reference< drawing::XShapes > xShapes; + uno::Reference< drawing::XShape > xShape; + + std::vector aContexts; + if(mpPage) + { + xShapes = mpPage; + + if( xShapes->getCount() > 0 ) + { + aContexts.push_back(SearchContext_impl(xShapes)); + xShape = aContexts.back().firstShape(); + } + else + { + xShapes = nullptr; + } + } + + while( xShape.is() ) + { + // find in xShape + uno::Reference< text::XText > xText(xShape, uno::UNO_QUERY); + uno::Reference< text::XTextRange > xRange = xText; + uno::Reference< text::XTextRange > xFound; + + while( xRange.is() ) + { + xFound = Search( xRange, pDescr ); + if( !xFound.is() ) + break; + + if( nFound >= nSequence ) + { + nSequence += 32; + aSeq.realloc( nSequence ); + pArray = aSeq.getArray(); + } + + pArray[nFound++] = xFound; + + xRange = xFound->getEnd(); + } + // done with shape -> get next shape + + // test if it's a group + uno::Reference< drawing::XShapes > xGroupShape; + xGroupShape.set( xShape, uno::UNO_QUERY ); + + if( xGroupShape.is() && xGroupShape->getCount() > 0 ) + { + aContexts.push_back(SearchContext_impl(xGroupShape)); + xShape = aContexts.back().firstShape(); + } + else + { + if (!aContexts.empty()) + xShape = aContexts.back().nextShape(); + else + xShape = nullptr; + } + + // test parent contexts for next shape if none + // is found in the current context + while (!aContexts.empty() && !xShape.is()) + { + aContexts.pop_back(); + if (!aContexts.empty()) + xShape = aContexts.back().nextShape(); + } + } + + if( nFound != nSequence ) + aSeq.realloc( nFound ); + + uno::Reference xRet(new SdUnoFindAllAccess(aSeq)); + return xRet; +} + +uno::Reference< css::uno::XInterface > SAL_CALL SdUnoSearchReplaceShape::findFirst( const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) +{ + uno::Reference< text::XTextRange > xRange( GetCurrentShape(), uno::UNO_QUERY ); + if( xRange.is() ) + return findNext( xRange, xDesc ); + + return uno::Reference< uno::XInterface > (); +} + +uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetCurrentShape() const noexcept +{ + uno::Reference< drawing::XShape > xShape; + + if( mpPage && mpPage->getCount() > 0) + mpPage->getByIndex(0) >>= xShape; + + return xShape; + +} + +uno::Reference< css::uno::XInterface > SAL_CALL SdUnoSearchReplaceShape::findNext( const css::uno::Reference< css::uno::XInterface >& xStartAt, const css::uno::Reference< css::util::XSearchDescriptor >& xDesc ) +{ + SdUnoSearchReplaceDescriptor* pDescr = comphelper::getFromUnoTunnel( xDesc ); + + uno::Reference< uno::XInterface > xFound; + + uno::Reference< text::XTextRange > xRange( xStartAt, uno::UNO_QUERY ); + if(pDescr && xRange.is() ) + { + + uno::Reference< text::XTextRange > xCurrentRange( xStartAt, uno::UNO_QUERY ); + + uno::Reference< drawing::XShape > xCurrentShape( GetShape( xCurrentRange ) ); + + while(!xFound.is() && xRange.is()) + { + xFound = Search( xRange, pDescr ); + if(!xFound.is()) + { + // we need a new starting range now + xRange = nullptr; + + if(mpPage) + { + // we do a page wide search, so skip to the next shape here + // get next shape on our page + uno::Reference< drawing::XShape > xFound2( GetNextShape( mpPage, xCurrentShape ) ); + if( xFound2.is() && (xFound2.get() != xCurrentShape.get()) ) + xCurrentShape = xFound2; + else + xCurrentShape = nullptr; + + xRange.set( xCurrentShape, uno::UNO_QUERY ); + if(!(xCurrentShape.is() && (xRange.is()))) + xRange = nullptr; + } + else + { + // we search only in this shape, so end search if we have + // not found anything + } + } + } + } + return xFound; +} + +/** this method returns the shape that follows xCurrentShape in the shape collection xShapes. + It steps recursive into groupshapes and returns the xCurrentShape if it is the last + shape in this collection */ +uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetNextShape( const uno::Reference< container::XIndexAccess >& xShapes, const uno::Reference< drawing::XShape >& xCurrentShape ) noexcept +{ + uno::Reference< drawing::XShape > xFound; + + if(xShapes.is() && xCurrentShape.is()) + { + const sal_Int32 nCount = xShapes->getCount(); + for( sal_Int32 i = 0; i < nCount; i++ ) + { + uno::Reference< drawing::XShape > xSearchShape; + xShapes->getByIndex(i) >>= xSearchShape; + + if( xSearchShape.is() ) + { + uno::Reference< container::XIndexAccess > xGroup( xSearchShape, uno::UNO_QUERY ); + + if( xCurrentShape.get() == xSearchShape.get() ) + { + if( xGroup.is() && xGroup->getCount() > 0 ) + { + xGroup->getByIndex( 0 ) >>= xFound; + } + else + { + i++; + if( i < nCount ) + xShapes->getByIndex( i ) >>= xFound; + else + xFound = xCurrentShape; + } + + break; + } + else if( xGroup.is() ) + { + xFound = GetNextShape( xGroup, xCurrentShape ); + if( xFound.is() ) + { + if( xFound.get() == xCurrentShape.get() ) + { + // the current shape was found at the end of the group + i++; + if( i < nCount ) + { + xShapes->getByIndex(i) >>= xFound; + } + } + break; + } + } + } + } + } + + return xFound; +} + +uno::Reference< text::XTextRange > SdUnoSearchReplaceShape::Search( const uno::Reference< text::XTextRange >& xText, SdUnoSearchReplaceDescriptor* pDescr ) +{ + if(!xText.is()) + return uno::Reference< text::XTextRange > (); + + uno::Reference< text::XText > xParent( xText->getText() ); + + if( !xParent.is() ) + { + xParent.set( xText, uno::UNO_QUERY ); + } + + const OUString aText( xParent->getString() ); + + const sal_Int32 nTextLen = aText.getLength(); + + std::unique_ptr pConvertPos( new sal_Int32[nTextLen+2] ); + std::unique_ptr pConvertPara( new sal_Int32[nTextLen+2] ); + + sal_Int32* pPos = pConvertPos.get(); + sal_Int32* pPara = pConvertPara.get(); + + sal_Int32 nLastPos = 0, nLastPara = 0; + + uno::Reference< container::XEnumerationAccess > xEnumAccess( xParent, uno::UNO_QUERY ); + + // first we fill the arrays with the position and paragraph for every character + // inside the text + if( xEnumAccess.is() ) + { + uno::Reference< container::XEnumeration > xParaEnum( xEnumAccess->createEnumeration() ); + + while(xParaEnum->hasMoreElements()) + { + int ndbg = 0; + uno::Reference< text::XTextContent > xParagraph( xParaEnum->nextElement(), uno::UNO_QUERY ); + if( xParagraph.is() ) + xEnumAccess.set(xParagraph, css::uno::UNO_QUERY); + else + xEnumAccess.clear(); + + if( xEnumAccess.is() ) + { + uno::Reference< container::XEnumeration > xPortionEnum( xEnumAccess->createEnumeration() ); + if( xPortionEnum.is() ) + { + while(xPortionEnum->hasMoreElements()) + { + uno::Reference< text::XTextRange > xPortion( xPortionEnum->nextElement(), uno::UNO_QUERY ); + if( xPortion.is() ) + { + const OUString aPortion( xPortion->getString() ); + const sal_Int32 nLen = aPortion.getLength(); + + ESelection aStartSel( GetSelection( xPortion->getStart() ) ); + ESelection aEndSel( GetSelection( xPortion->getEnd() ) ); + + // special case for empty portions with content or length one portions with content (fields) + if( (aStartSel.nStartPos == aEndSel.nStartPos) || ( (aStartSel.nStartPos == (aEndSel.nStartPos - 1)) && (nLen > 1) ) ) + { + for( sal_Int32 i = 0; i < nLen; i++ ) + { + if( ndbg < (nTextLen+2) ) + { + *pPos++ = aStartSel.nStartPos; + *pPara++ = aStartSel.nStartPara; + + ndbg += 1; + } + else + { + OSL_FAIL( "array overflow while searching" ); + } + } + + nLastPos = aStartSel.nStartPos; + } + // normal case + else + { + for( sal_Int32 i = 0; i < nLen; i++ ) + { + if( ndbg < (nTextLen+2) ) + { + *pPos++ = aStartSel.nStartPos++; + *pPara++ = aStartSel.nStartPara; + + ndbg += 1; + } + else + { + OSL_FAIL( "array overflow while searching" ); + } + } + + nLastPos = aStartSel.nStartPos - 1; + DBG_ASSERT( aEndSel.nStartPos == aStartSel.nStartPos, "Search is not working" ); + } + nLastPara = aStartSel.nStartPara; + } + } + } + } + + if( ndbg < (nTextLen+2) ) + { + *pPos++ = nLastPos + 1; + *pPara++ = nLastPara; + } + else + { + OSL_FAIL( "array overflow while searching" ); + } + } + } + + uno::Reference< text::XTextRange > xFound; + ESelection aSel; + + if( xText.is() ) + aSel = GetSelection( xText ); + + sal_Int32 nStartPos; + sal_Int32 nEndPos = 0; + for( nStartPos = 0; nStartPos < nTextLen; nStartPos++ ) + { + if( pConvertPara[nStartPos] == aSel.nStartPara && pConvertPos[nStartPos] == aSel.nStartPos ) + break; + } + + if( Search( aText, nStartPos, nEndPos, pDescr ) ) + { + if( nStartPos <= nTextLen && nEndPos <= nTextLen ) + { + ESelection aSelection( pConvertPara[nStartPos], pConvertPos[nStartPos], + pConvertPara[nEndPos], pConvertPos[nEndPos] ); + + SvxUnoTextBase* pParent = comphelper::getFromUnoTunnel( xParent ); + + if(pParent) + { + rtl::Reference pRange = new SvxUnoTextRange( *pParent ); + xFound = pRange; + pRange->SetSelection(aSelection); + } + } + else + { + OSL_FAIL("Array overflow while searching!"); + } + } + + return xFound; +} + +bool SdUnoSearchReplaceShape::Search( const OUString& rText, sal_Int32& nStartPos, sal_Int32& nEndPos, SdUnoSearchReplaceDescriptor* pDescr ) noexcept +{ + OUString aSearchStr( pDescr->getSearchString() ); + OUString aText( rText ); + + if( !pDescr->IsCaseSensitive() ) + { + aText = aText.toAsciiLowerCase(); + aSearchStr = aSearchStr.toAsciiLowerCase(); + } + + sal_Int32 nFound = aText.indexOf( aSearchStr, nStartPos ); + if( nFound != -1 ) + { + nStartPos = nFound; + nEndPos = nFound + aSearchStr.getLength(); + + if(pDescr->IsWords()) + { + if( (nStartPos > 0 && aText[nStartPos-1] > ' ') || + (nEndPos < aText.getLength() && aText[nEndPos] > ' ') ) + { + nStartPos++; + return Search( aText, nStartPos, nEndPos, pDescr ); + } + } + + return true; + } + else + return false; +} + +ESelection SdUnoSearchReplaceShape::GetSelection( const uno::Reference< text::XTextRange >& xTextRange ) noexcept +{ + ESelection aSel; + SvxUnoTextRangeBase* pRange = comphelper::getFromUnoTunnel( xTextRange ); + + if(pRange) + aSel = pRange->GetSelection(); + + return aSel; +} + +uno::Reference< drawing::XShape > SdUnoSearchReplaceShape::GetShape( const uno::Reference< text::XTextRange >& xTextRange ) noexcept +{ + uno::Reference< drawing::XShape > xShape; + + if(xTextRange.is()) + { + uno::Reference< text::XText > xText( xTextRange->getText() ); + + if(xText.is()) + { + do + { + xShape.set( xText, uno::UNO_QUERY ); + if(!xShape.is()) + { + uno::Reference< text::XText > xParent( xText->getText() ); + if(!xParent.is() || xText.get() == xParent.get()) + return xShape; + + xText = xParent; + } + } while( !xShape.is() ); + } + } + + return xShape; +} + +/* ================================================================= */ +/** this class holds the parameters and status of a search or replace + operation performed by class SdUnoSearchReplaceShape + */ + +UNO3_GETIMPLEMENTATION_IMPL( SdUnoSearchReplaceDescriptor ); + +SdUnoSearchReplaceDescriptor::SdUnoSearchReplaceDescriptor() +{ + mpPropSet.reset( new SvxItemPropertySet(ImplGetSearchPropertyMap(), SdrObject::GetGlobalDrawObjectItemPool()) ); + + mbBackwards = false; + mbCaseSensitive = false; + mbWords = false; +} + +SdUnoSearchReplaceDescriptor::~SdUnoSearchReplaceDescriptor() noexcept +{ +} + +// XSearchDescriptor +OUString SAL_CALL SdUnoSearchReplaceDescriptor::getSearchString() +{ + return maSearchStr; +} + +void SAL_CALL SdUnoSearchReplaceDescriptor::setSearchString( const OUString& aString ) +{ + maSearchStr = aString; +} + +// XReplaceDescriptor +OUString SAL_CALL SdUnoSearchReplaceDescriptor::getReplaceString() +{ + return maReplaceStr; +} + +void SAL_CALL SdUnoSearchReplaceDescriptor::setReplaceString( const OUString& aReplaceString ) +{ + maReplaceStr = aReplaceString; +} + +// XPropertySet +uno::Reference< css::beans::XPropertySetInfo > SAL_CALL SdUnoSearchReplaceDescriptor::getPropertySetInfo() +{ + SolarMutexGuard aGuard; + return mpPropSet->getPropertySetInfo(); +} + +void SAL_CALL SdUnoSearchReplaceDescriptor::setPropertyValue( const OUString& aPropertyName, const css::uno::Any& aValue ) +{ + SolarMutexGuard aGuard; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(aPropertyName); + + bool bOk = false; + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_SEARCH_BACKWARDS: + bOk = (aValue >>= mbBackwards); + break; + case WID_SEARCH_CASE: + bOk = (aValue >>= mbCaseSensitive); + break; + case WID_SEARCH_WORDS: + bOk = (aValue >>= mbWords); + break; + default: + throw beans::UnknownPropertyException( aPropertyName, static_cast(this)); + } + + if( !bOk ) + throw lang::IllegalArgumentException(); +} + +uno::Any SAL_CALL SdUnoSearchReplaceDescriptor::getPropertyValue( const OUString& PropertyName ) +{ + SolarMutexGuard aGuard; + + uno::Any aAny; + + const SfxItemPropertyMapEntry* pEntry = mpPropSet->getPropertyMapEntry(PropertyName); + + switch( pEntry ? pEntry->nWID : -1 ) + { + case WID_SEARCH_BACKWARDS: + aAny <<= mbBackwards; + break; + case WID_SEARCH_CASE: + aAny <<= mbCaseSensitive; + break; + case WID_SEARCH_WORDS: + aAny <<= mbWords; + break; + default: + throw beans::UnknownPropertyException( PropertyName, static_cast(this)); + } + + return aAny; +} + +void SAL_CALL SdUnoSearchReplaceDescriptor::addPropertyChangeListener( const OUString& , const css::uno::Reference< css::beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdUnoSearchReplaceDescriptor::removePropertyChangeListener( const OUString& , const css::uno::Reference< css::beans::XPropertyChangeListener >& ) {} +void SAL_CALL SdUnoSearchReplaceDescriptor::addVetoableChangeListener( const OUString& , const css::uno::Reference< css::beans::XVetoableChangeListener >& ) {} +void SAL_CALL SdUnoSearchReplaceDescriptor::removeVetoableChangeListener( const OUString& , const css::uno::Reference< css::beans::XVetoableChangeListener >& ) {} + +/* ================================================================= */ + +SdUnoFindAllAccess::SdUnoFindAllAccess( uno::Sequence< uno::Reference< uno::XInterface > > const & rSequence ) noexcept +:maSequence( rSequence ) +{ +} + +SdUnoFindAllAccess::~SdUnoFindAllAccess() noexcept +{ +} + +// XElementAccess +uno::Type SAL_CALL SdUnoFindAllAccess::getElementType() +{ + return cppu::UnoType::get(); +} + +sal_Bool SAL_CALL SdUnoFindAllAccess::hasElements() +{ + return maSequence.hasElements(); +} + +// XIndexAccess +sal_Int32 SAL_CALL SdUnoFindAllAccess::getCount() +{ + return maSequence.getLength(); +} + +uno::Any SAL_CALL SdUnoFindAllAccess::getByIndex( sal_Int32 Index ) +{ + if( Index < 0 || Index >= getCount() ) + throw lang::IndexOutOfBoundsException(); + + uno::Any aAny; + aAny <<= maSequence[Index]; + return aAny; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unowcntr.cxx b/sd/source/ui/unoidl/unowcntr.cxx new file mode 100644 index 000000000..1079477ef --- /dev/null +++ b/sd/source/ui/unoidl/unowcntr.cxx @@ -0,0 +1,99 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include "unowcntr.hxx" + +using namespace ::com::sun::star; + +SvUnoWeakContainer::SvUnoWeakContainer() noexcept +{ +} + +SvUnoWeakContainer::~SvUnoWeakContainer() noexcept +{ +} + +/** inserts the given ref into this container */ +void SvUnoWeakContainer::insert( const uno::WeakReference< uno::XInterface >& xRef ) noexcept +{ + for ( auto it = maVector.begin(); it != maVector.end(); ) + { + uno::WeakReference< uno::XInterface > & rWeakRef = *it; + uno::Reference< uno::XInterface > xTestRef( rWeakRef ); + if ( !xTestRef.is() ) + { + it = maVector.erase( it ); + } + else + { + if ( rWeakRef == xRef ) + return; + ++it; + } + } + maVector.emplace_back( xRef ); +} + +/** searches the container for a ref that returns true on the given + search function +*/ +bool SvUnoWeakContainer::findRef( + uno::WeakReference< uno::XInterface >& rRef, + void const * pSearchData, + weakref_searchfunc pSearchFunc +) +{ + for ( auto it = maVector.begin(); it != maVector.end(); ) + { + uno::WeakReference< uno::XInterface > & itRef = *it; + uno::Reference< uno::XInterface > xTestRef( itRef ); + if ( !xTestRef.is() ) + { + it = maVector.erase( it ); + } + else + { + if( (*pSearchFunc)( itRef, pSearchData ) ) + { + rRef = itRef; + return true; + } + ++it; + } + } + return false; +} + +void SvUnoWeakContainer::dispose() +{ + for (auto const& elem : maVector) + { + uno::Reference< uno::XInterface > xTestRef( elem ); + if ( xTestRef.is() ) + { + uno::Reference< lang::XComponent > xComp( xTestRef, uno::UNO_QUERY ); + if( xComp.is() ) + xComp->dispose(); + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/unoidl/unowcntr.hxx b/sd/source/ui/unoidl/unowcntr.hxx new file mode 100644 index 000000000..a863f0929 --- /dev/null +++ b/sd/source/ui/unoidl/unowcntr.hxx @@ -0,0 +1,47 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#pragma once + +#include +#include + +typedef bool (*weakref_searchfunc)( const css::uno::WeakReference< css::uno::XInterface >& xRef, void const * pSearchData ); + +class SvUnoWeakContainer +{ +private: + std::vector< css::uno::WeakReference< css::uno::XInterface > > maVector; + +public: + SvUnoWeakContainer() noexcept; + ~SvUnoWeakContainer() noexcept; + + /** inserts the given ref into this container */ + void insert( const css::uno::WeakReference< css::uno::XInterface >& xRef ) noexcept; + + /** searches the container for a ref that returns true on the given + search function + */ + bool findRef( css::uno::WeakReference< css::uno::XInterface >& rRef, void const * pSearchData, weakref_searchfunc pSearchFunc ); + + void dispose(); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/DocumentRenderer.cxx b/sd/source/ui/view/DocumentRenderer.cxx new file mode 100644 index 000000000..eee1a759e --- /dev/null +++ b/sd/source/ui/view/DocumentRenderer.cxx @@ -0,0 +1,2256 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +namespace { + + /** Convenience class to extract values from the sequence of properties + given to one of the XRenderable methods. + */ + class PrintOptions + { + public: + PrintOptions ( + const vcl::PrinterOptionsHelper& rHelper, + std::vector&& rSlidesPerPage) + : mrProperties(rHelper), + maSlidesPerPage(std::move(rSlidesPerPage)) + { + } + + bool IsWarningOrientation() const + { + return GetBoolValue(nullptr, true); + } + + bool IsPrintPageName() const + { + return GetBoolValue("IsPrintName", false); + } + + bool IsDate() const + { + return GetBoolValue("IsPrintDateTime", false); + } + + bool IsTime() const + { + return GetBoolValue("IsPrintDateTime", false); + } + + bool IsHiddenPages() const + { + return GetBoolValue("IsPrintHidden", false); + } + + bool IsHandoutHorizontal() const + { + return GetBoolValue("SlidesPerPageOrder", sal_Int32(0)); + } + + sal_Int32 GetHandoutPageCount() const + { + sal_uInt32 nIndex = static_cast(mrProperties.getIntValue("SlidesPerPage", sal_Int32(0))); + if (nIndex(mrProperties.getIntValue( "Quality", sal_Int32(0) )); + return nQuality; + } + + bool IsPageSize() const + { + return GetBoolValue("PageOptions", sal_Int32(1)); + } + + bool IsTilePage() const + { + return GetBoolValue("PageOptions", sal_Int32(2)) || GetBoolValue("PageOptions", sal_Int32(3)); + } + + bool IsCutPage() const + { + return GetBoolValue("PageOptions", sal_Int32(0)); + } + + bool IsBooklet() const + { + return GetBoolValue("PrintProspect", false); + } + + bool IsPrinterPreferred(DocumentType eDocType) const + { + bool bIsDraw = eDocType == DocumentType::Draw; + return IsTilePage() || IsPageSize() || IsBooklet() || (!bIsDraw && !IsNotes()); + } + + bool IsPrintExcluded() const + { + return (IsNotes() || IsDraw() || IsHandout()) && IsHiddenPages(); + } + + bool IsPrintFrontPage() const + { + sal_Int32 nInclude = static_cast(mrProperties.getIntValue( "EvenOdd", 0 )); + return nInclude != 2; + } + + bool IsPrintBackPage() const + { + sal_Int32 nInclude = static_cast(mrProperties.getIntValue( "EvenOdd", 0 )); + return nInclude != 1; + } + + bool IsPaperBin() const + { + return GetBoolValue("PrintPaperFromSetup", false); + } + + bool IsPrintMarkedOnly() const + { + return GetBoolValue("PrintContent", sal_Int32(4)); + } + + OUString GetPrinterSelection (sal_Int32 nPageCount, sal_Int32 nCurrentPageIndex) const + { + sal_Int32 nContent = static_cast(mrProperties.getIntValue( "PrintContent", 0 )); + OUString sFullRange = "1-" + OUString::number(nPageCount); + + if (nContent == 0) // all pages/slides + { + return sFullRange; + } + + if (nContent == 1) // range + { + OUString sValue = mrProperties.getStringValue("PageRange"); + return sValue.isEmpty() ? sFullRange : sValue; + } + + if (nContent == 2 && // selection + nCurrentPageIndex >= 0) + { + return OUString::number(nCurrentPageIndex + 1); + } + + return OUString(); + } + + private: + const vcl::PrinterOptionsHelper& mrProperties; + const std::vector maSlidesPerPage; + + /** When the value of the property with name pName is a boolean then + return its value. When the property is unknown then + bDefaultValue is returned. Otherwise is returned. + */ + bool GetBoolValue ( + const char* pName, + const bool bDefaultValue) const + { + bool bValue = mrProperties.getBoolValue( pName, bDefaultValue ); + return bValue; + } + + /** Return when the value of the property with name pName is + an integer and its value is nTriggerValue. Otherwise is + returned. + */ + bool GetBoolValue ( + const char* pName, + const sal_Int32 nTriggerValue) const + { + sal_Int32 nValue = static_cast(mrProperties.getIntValue( pName, 0 )); + return nValue == nTriggerValue; + } + }; + + /** A collection of values that helps to reduce the number of arguments + given to some functions. Note that not all values are set at the + same time. + */ + class PrintInfo + { + public: + PrintInfo ( + Printer* pPrinter, + const bool bPrintMarkedOnly) + : mpPrinter(pPrinter), + mnDrawMode(DrawModeFlags::Default), + maPrintSize(0,0), + maPageSize(0,0), + meOrientation(Orientation::Portrait), + mbPrintMarkedOnly(bPrintMarkedOnly) + {} + + const VclPtr mpPrinter; + DrawModeFlags mnDrawMode; + OUString msTimeDate; + OUString msPageString; + Size maPrintSize; + Size maPageSize; + Orientation meOrientation; + MapMode maMap; + const bool mbPrintMarkedOnly; + }; + + /** Output one page of the document to the given printer. Note that + more than one document page may be output to one printer page. + */ + void PrintPage ( + Printer& rPrinter, + ::sd::View& rPrintView, + SdPage& rPage, + View const * pView, + const bool bPrintMarkedOnly, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) + { + rPrintView.ShowSdrPage(&rPage); + + const MapMode aOriginalMapMode (rPrinter.GetMapMode()); + + // Set the visible layers + SdrPageView* pPageView = rPrintView.GetSdrPageView(); + OSL_ASSERT(pPageView!=nullptr); + pPageView->SetVisibleLayers(rVisibleLayers); + pPageView->SetPrintableLayers(rPrintableLayers); + + if (pView!=nullptr && bPrintMarkedOnly) + pView->DrawMarkedObj(rPrinter); + else + rPrintView.CompleteRedraw(&rPrinter, + vcl::Region(::tools::Rectangle(Point(0,0), rPage.GetSize()))); + + rPrinter.SetMapMode(aOriginalMapMode); + + rPrintView.HideSdrPage(); + } + + /** Output a string (that typically is not part of a document page) to + the given printer. + */ + void PrintMessage ( + Printer& rPrinter, + const OUString& rsPageString, + const Point& rPageStringOffset) + { + const vcl::Font aOriginalFont (rPrinter.OutputDevice::GetFont()); + rPrinter.SetFont(vcl::Font(FAMILY_SWISS, Size(0, 423))); + rPrinter.DrawText(rPageStringOffset, rsPageString); + rPrinter.SetFont(aOriginalFont); + } + + /** Read the resources and process then into a sequence of properties + that can be passed to the printing dialog. + */ + class DialogCreator + { + public: + DialogCreator (ViewShellBase &rBase, bool bImpress, sal_Int32 nCurPage) + : mrBase(rBase) + , mbImpress(bImpress) + , mnCurPage(nCurPage) + { + ProcessResource(); + } + + const std::vector< beans::PropertyValue >& GetDialogControls() const + { + return maProperties; + } + + const std::vector& GetSlidesPerPage() const + { + return maSlidesPerPage; + } + + private: + ViewShellBase &mrBase; + std::vector maProperties; + std::vector maSlidesPerPage; + bool mbImpress; + sal_Int32 mnCurPage; + + void ProcessResource() + { + // load the writer PrinterOptions into the custom tab + beans::PropertyValue aOptionsUIFile; + aOptionsUIFile.Name = "OptionsUIFile"; + if( mbImpress ) + aOptionsUIFile.Value <<= OUString("modules/simpress/ui/impressprinteroptions.ui"); + else + aOptionsUIFile.Value <<= OUString("modules/sdraw/ui/drawprinteroptions.ui"); + maProperties.push_back(aOptionsUIFile); + + SvtModuleOptions aOpt; + OUString aAppGroupname(SdResId(STR_IMPRESS_PRINT_UI_GROUP_NAME)); + aAppGroupname = aAppGroupname.replaceFirst("%s", aOpt.GetModuleName( + mbImpress ? SvtModuleOptions::EModule::IMPRESS : SvtModuleOptions::EModule::DRAW)); + AddDialogControl(vcl::PrinterOptionsHelper::setGroupControlOpt("tabcontrol-page2", aAppGroupname, ".HelpID:vcl:PrintDialog:TabPage:AppPage")); + + uno::Sequence< OUString > aHelpIds, aWidgetIds; + if( mbImpress ) + { + aHelpIds = { ".HelpID:vcl:PrintDialog:PageContentType:ListBox" }; + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( + "impressdocument", + SdResId(STR_IMPRESS_PRINT_UI_CONTENT), + aHelpIds, + "PageContentType" , + CreateChoice(STR_IMPRESS_PRINT_UI_CONTENT_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_CONTENT_CHOICES)), + 0) + ); + + aHelpIds = { ".HelpID:vcl:PrintDialog:SlidesPerPage:ListBox" }; + vcl::PrinterOptionsHelper::UIControlOptions aContentOpt( "PageContentType" , 1 ); + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( + "slidesperpage", + SdResId(STR_IMPRESS_PRINT_UI_SLIDESPERPAGE), + aHelpIds, + "SlidesPerPage" , + GetSlidesPerPageSequence(), + 0, + Sequence< sal_Bool >(), + aContentOpt + ) + ); + + aHelpIds = { ".HelpID:vcl:PrintDialog:SlidesPerPageOrder:ListBox" }; + vcl::PrinterOptionsHelper::UIControlOptions aSlidesPerPageOpt( "SlidesPerPage" , -1, true ); + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( + "slidesperpageorder", + SdResId(STR_IMPRESS_PRINT_UI_ORDER), + aHelpIds, + "SlidesPerPageOrder" , + CreateChoice(STR_IMPRESS_PRINT_UI_ORDER_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_ORDER_CHOICES)), + 0, + Sequence< sal_Bool >(), + aSlidesPerPageOpt ) + ); + } + + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("contents", + SdResId(STR_IMPRESS_PRINT_UI_INCLUDE_CONTENT), "" ) ); + + if( mbImpress ) + { + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printname", + SdResId(STR_IMPRESS_PRINT_UI_IS_PRINT_NAME), + ".HelpID:vcl:PrintDialog:IsPrintName:CheckBox" , + "IsPrintName" , + officecfg::Office::Impress::Print::Other::PageName::get() + ) + ); + } + else + { + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printname", + SdResId(STR_DRAW_PRINT_UI_IS_PRINT_NAME), + ".HelpID:vcl:PrintDialog:IsPrintName:CheckBox" , + "IsPrintName" , + officecfg::Office::Draw::Print::Other::PageName::get() + ) + ); + } + + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printdatetime", + SdResId(STR_IMPRESS_PRINT_UI_IS_PRINT_DATE), + ".HelpID:vcl:PrintDialog:IsPrintDateTime:CheckBox" , + "IsPrintDateTime" , + // Separate settings for time and date in Impress/Draw -> Print page, check that both are set + mbImpress ? + officecfg::Office::Impress::Print::Other::Date::get() && + officecfg::Office::Impress::Print::Other::Time::get() : + officecfg::Office::Draw::Print::Other::Date::get() && + officecfg::Office::Draw::Print::Other::Time::get() + ) + ); + + if( mbImpress ) + { + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printhidden", + SdResId(STR_IMPRESS_PRINT_UI_IS_PRINT_HIDDEN), + ".HelpID:vcl:PrintDialog:IsPrintHidden:CheckBox" , + "IsPrintHidden" , + officecfg::Office::Impress::Print::Other::HiddenPage::get() + ) + ); + } + + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("color", + SdResId(STR_IMPRESS_PRINT_UI_QUALITY), "" ) ); + + aHelpIds = { ".HelpID:vcl:PrintDialog:Quality:RadioButton:0", + ".HelpID:vcl:PrintDialog:Quality:RadioButton:1", + ".HelpID:vcl:PrintDialog:Quality:RadioButton:2" }; + aWidgetIds = { "originalcolors", "grayscale", "blackandwhite" }; + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceRadiosControlOpt( + aWidgetIds, + "", + aHelpIds, + "Quality" , + CreateChoice(STR_IMPRESS_PRINT_UI_QUALITY_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_QUALITY_CHOICES)), + mbImpress ? officecfg::Office::Impress::Print::Other::Quality::get() : + officecfg::Office::Draw::Print::Other::Quality::get() ) + + ); + + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("pagesizes", + SdResId(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS), "" ) ); + + aHelpIds = { ".HelpID:vcl:PrintDialog:PageOptions:RadioButton:0", + ".HelpID:vcl:PrintDialog:PageOptions:RadioButton:1", + ".HelpID:vcl:PrintDialog:PageOptions:RadioButton:2", + ".HelpID:vcl:PrintDialog:PageOptions:RadioButton:3" }; + aWidgetIds = { "originalsize", "fittoprintable", "distributeonmultiple", "tilesheet" }; + + // Mutually exclusive page options settings are stored in separate config keys... + // TODO: There is no config key to set the distributeonmultiple option as default + sal_Int32 nDefaultChoice = 0; + if ( mbImpress ? officecfg::Office::Impress::Print::Page::PageSize::get() : + officecfg::Office::Draw::Print::Page::PageSize::get() ) + { + nDefaultChoice = 1; + } + else if ( mbImpress ? officecfg::Office::Impress::Print::Page::PageTile::get() : + officecfg::Office::Draw::Print::Page::PageTile::get() ) + { + nDefaultChoice = 3; + } + vcl::PrinterOptionsHelper::UIControlOptions aPageOptionsOpt("PrintProspect", 0); + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceRadiosControlOpt( + aWidgetIds, + "", + aHelpIds, + "PageOptions" , + mbImpress ? CreateChoice(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS_CHOICES)) : + CreateChoice(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS_CHOICES_DRAW, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_PAGE_OPTIONS_CHOICES_DRAW)), + nDefaultChoice, + Sequence< sal_Bool >(), + aPageOptionsOpt + ) + ); + + vcl::PrinterOptionsHelper::UIControlOptions aBrochureOpt; + aBrochureOpt.maGroupHint = "LayoutPage" ; + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("pagesides", + SdResId(STR_IMPRESS_PRINT_UI_PAGE_SIDES), "", + aBrochureOpt ) ); + + // brochure printing + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("brochure", + SdResId(STR_IMPRESS_PRINT_UI_BROCHURE), + ".HelpID:vcl:PrintDialog:PrintProspect:CheckBox" , + "PrintProspect" , + mbImpress ? officecfg::Office::Impress::Print::Page::Booklet::get() : + officecfg::Office::Draw::Print::Page::Booklet::get(), + aBrochureOpt + ) + ); + + vcl::PrinterOptionsHelper::UIControlOptions + aIncludeOpt( "PrintProspect" , -1, false ); + aIncludeOpt.maGroupHint = "LayoutPage" ; + aHelpIds = { ".HelpID:vcl:PrintDialog:PrintProspectInclude:ListBox" }; + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( + "brochureinclude", + SdResId(STR_IMPRESS_PRINT_UI_BROCHURE_INCLUDE), + aHelpIds, + "PrintProspectInclude" , + CreateChoice(STR_IMPRESS_PRINT_UI_BROCHURE_INCLUDE_LIST, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_BROCHURE_INCLUDE_LIST)), + 0, + Sequence< sal_Bool >(), + aIncludeOpt + ) + ); + + // paper tray (on options page) + vcl::PrinterOptionsHelper::UIControlOptions aPaperTrayOpt; + aPaperTrayOpt.maGroupHint = "OptionsPageOptGroup" ; + AddDialogControl( vcl::PrinterOptionsHelper::setBoolControlOpt("printpaperfromsetup", + SdResId(STR_IMPRESS_PRINT_UI_PAPER_TRAY), + ".HelpID:vcl:PrintDialog:PrintPaperFromSetup:CheckBox" , + "PrintPaperFromSetup" , + false, + aPaperTrayOpt + ) + ); + // print range selection + vcl::PrinterOptionsHelper::UIControlOptions aPrintRangeOpt; + aPrintRangeOpt.mbInternalOnly = true; + aPrintRangeOpt.maGroupHint = "PrintRange" ; + AddDialogControl( vcl::PrinterOptionsHelper::setSubgroupControlOpt("printrange", + mbImpress ? SdResId(STR_IMPRESS_PRINT_UI_SLIDE_RANGE) : SdResId(STR_IMPRESS_PRINT_UI_PAGE_RANGE), + "", + aPrintRangeOpt ) + ); + + // check if there is a selection of slides + OUString aPageRange(OUString::number(mnCurPage + 1)); + int nPrintRange(0); + using sd::slidesorter::SlideSorterViewShell; + SlideSorterViewShell* const pSSViewSh(SlideSorterViewShell::GetSlideSorter(mrBase)); + if (pSSViewSh) + { + const std::shared_ptr pPageSelection(pSSViewSh->GetPageSelection()); + if (bool(pPageSelection) && pPageSelection->size() > 1) + { + OUStringBuffer aBuf; + // TODO: this could be improved by writing ranges instead of consecutive page + // numbers if appropriate. Do we have a helper function for that somewhere? + bool bFirst(true); + for (auto pPage: *pPageSelection) + { + if (bFirst) + bFirst = false; + else + aBuf.append(','); + aBuf.append(static_cast(pPage->GetPageNum() / 2 + 1)); + } + aPageRange = aBuf.makeStringAndClear(); + nPrintRange = 1; + } + } +/* + OUString aPrintRangeName( "PrintContent" ); + aHelpIds.realloc( 1 ); + aHelpIds[0] = ".HelpID:vcl:PrintDialog:PageContentType:ListBox"; + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceListControlOpt( "printpagesbox", OUString(), + aHelpIds, aPrintRangeName, + mbImpress ? CreateChoice( STR_IMPRESS_PRINT_UI_PAGE_RANGE_CHOICE, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_PAGE_RANGE_CHOICE ) ) : + CreateChoice( STR_DRAW_PRINT_UI_PAGE_RANGE_CHOICE, SAL_N_ELEMENTS(STR_DRAW_PRINT_UI_PAGE_RANGE_CHOICE ) ), + nPrintRange ) ); +*/ + OUString aPrintRangeName( "PrintContent" ); + aHelpIds = { ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:0", + ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:1", + ".HelpID:vcl:PrintDialog:PrintContent:RadioButton:2" }; + aWidgetIds = { "rbAllPages", "rbRangePages", "rbRangeSelection" }; + + AddDialogControl( vcl::PrinterOptionsHelper::setChoiceRadiosControlOpt(aWidgetIds, OUString(), + aHelpIds, aPrintRangeName, + mbImpress ? CreateChoice(STR_IMPRESS_PRINT_UI_PAGE_RANGE_CHOICE, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_PAGE_RANGE_CHOICE)) : + CreateChoice(STR_DRAW_PRINT_UI_PAGE_RANGE_CHOICE, SAL_N_ELEMENTS(STR_DRAW_PRINT_UI_PAGE_RANGE_CHOICE)), + nPrintRange ) + ); + // create an Edit dependent on "Pages" selected + vcl::PrinterOptionsHelper::UIControlOptions aPageRangeOpt( aPrintRangeName, 1, true ); + AddDialogControl(vcl::PrinterOptionsHelper::setEditControlOpt("pagerange", "", + ".HelpID:vcl:PrintDialog:PageRange:Edit", "PageRange", + aPageRange, aPageRangeOpt)); + vcl::PrinterOptionsHelper::UIControlOptions aEvenOddOpt(aPrintRangeName, -1, true); + AddDialogControl(vcl::PrinterOptionsHelper::setChoiceListControlOpt("evenoddbox", "", + uno::Sequence(), "EvenOdd", uno::Sequence(), + 0, uno::Sequence(), aEvenOddOpt)); + } + + void AddDialogControl( const Any& i_rCtrl ) + { + beans::PropertyValue aVal; + aVal.Value = i_rCtrl; + maProperties.push_back( aVal ); + } + + static Sequence CreateChoice(const TranslateId* pResourceId, size_t nCount) + { + Sequence aChoices (nCount); + std::transform(pResourceId, pResourceId + nCount, aChoices.getArray(), + [](const auto& id) { return SdResId(id); }); + return aChoices; + } + + Sequence GetSlidesPerPageSequence() + { + const Sequence aChoice ( + CreateChoice(STR_IMPRESS_PRINT_UI_SLIDESPERPAGE_CHOICES, SAL_N_ELEMENTS(STR_IMPRESS_PRINT_UI_SLIDESPERPAGE_CHOICES))); + maSlidesPerPage.clear(); + maSlidesPerPage.push_back(0); // first is using the default + std::transform(std::next(aChoice.begin()), aChoice.end(), std::back_inserter(maSlidesPerPage), + [](const OUString& rChoice) -> sal_Int32 { return rChoice.toInt32(); }); + return aChoice; + } + }; + + /** The Prepare... methods of the DocumentRenderer::Implementation class + create a set of PrinterPage objects that contain all necessary + information to do the actual printing. There is one PrinterPage + object per printed page. Derived classes implement the actual, mode + specific printing. + + This and all derived classes support the asynchronous printing + process by not storing pointers to any data with lifetime shorter + than the PrinterPage objects, i.e. slides, shapes, (one of) the + outliner (of the document). + */ + class PrinterPage + { + public: + PrinterPage ( + const PageKind ePageKind, + const MapMode& rMapMode, + const bool bPrintMarkedOnly, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : mePageKind(ePageKind), + maMap(rMapMode), + mbPrintMarkedOnly(bPrintMarkedOnly), + msPageString(rsPageString), + maPageStringOffset(rPageStringOffset), + mnDrawMode(nDrawMode), + meOrientation(eOrientation), + mnPaperTray(nPaperTray) + { + } + + virtual ~PrinterPage() {} + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell& rViewShell, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const = 0; + + DrawModeFlags GetDrawMode() const { return mnDrawMode; } + Orientation GetOrientation() const { return meOrientation; } + sal_uInt16 GetPaperTray() const { return mnPaperTray; } + + protected: + const PageKind mePageKind; + const MapMode maMap; + const bool mbPrintMarkedOnly; + const OUString msPageString; + const Point maPageStringOffset; + const DrawModeFlags mnDrawMode; + const Orientation meOrientation; + const sal_uInt16 mnPaperTray; + }; + + /** The RegularPrinterPage is used for printing one regular slide (no + notes, handout, or outline) to one printer page. + */ + class RegularPrinterPage : public PrinterPage + { + public: + RegularPrinterPage ( + const sal_uInt16 nPageIndex, + const PageKind ePageKind, + const MapMode& rMapMode, + const bool bPrintMarkedOnly, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(ePageKind, rMapMode, bPrintMarkedOnly, rsPageString, + rPageStringOffset, nDrawMode, eOrientation, nPaperTray), + mnPageIndex(nPageIndex) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell&, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const override + { + SdPage* pPageToPrint = rDocument.GetSdPage(mnPageIndex, mePageKind); + rPrinter.SetMapMode(maMap); + PrintPage( + rPrinter, + rPrintView, + *pPageToPrint, + pView, + mbPrintMarkedOnly, + rVisibleLayers, + rPrintableLayers); + PrintMessage( + rPrinter, + msPageString, + maPageStringOffset); + } + + private: + const sal_uInt16 mnPageIndex; + }; + + /** Print one slide multiple times on a printer page so that the whole + printer page is covered. + */ + class TiledPrinterPage : public PrinterPage + { + public: + TiledPrinterPage ( + const sal_uInt16 nPageIndex, + const PageKind ePageKind, + const bool bPrintMarkedOnly, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(ePageKind, MapMode(), bPrintMarkedOnly, rsPageString, + rPageStringOffset, nDrawMode, eOrientation, nPaperTray), + mnPageIndex(nPageIndex) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell&, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const override + { + SdPage* pPageToPrint = rDocument.GetSdPage(mnPageIndex, mePageKind); + if (pPageToPrint==nullptr) + return; + MapMode aMap (rPrinter.GetMapMode()); + + const Size aPageSize (pPageToPrint->GetSize()); + const Size aPrintSize (rPrinter.GetOutputSize()); + + const sal_Int32 nPageWidth (aPageSize.Width() + mnGap + - pPageToPrint->GetLeftBorder() - pPageToPrint->GetRightBorder()); + const sal_Int32 nPageHeight (aPageSize.Height() + mnGap + - pPageToPrint->GetUpperBorder() - pPageToPrint->GetLowerBorder()); + if (nPageWidth<=0 || nPageHeight<=0) + return; + + // Print at least two rows and columns. More if the document + // page fits completely onto the printer page. + const sal_Int32 nColumnCount (std::max(sal_Int32(2), + sal_Int32(aPrintSize.Width() / nPageWidth))); + const sal_Int32 nRowCount (std::max(sal_Int32(2), + sal_Int32(aPrintSize.Height() / nPageHeight))); + for (sal_Int32 nRow=0; nRow&& rPageIndices, + const MapMode& rMapMode, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(PageKind::Handout, rMapMode, false, rsPageString, + rPageStringOffset, nDrawMode, eOrientation, nPaperTray), + mnHandoutPageIndex(nHandoutPageIndex), + maPageIndices(std::move(rPageIndices)) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell& rViewShell, + View* pView, + DrawView& rPrintView, + const SdrLayerIDSet& rVisibleLayers, + const SdrLayerIDSet& rPrintableLayers) const override + { + SdPage& rHandoutPage (*rDocument.GetSdPage(0, PageKind::Handout)); + + Reference< css::beans::XPropertySet > xHandoutPage( rHandoutPage.getUnoPage(), UNO_QUERY ); + static const OUStringLiteral sPageNumber( u"Number" ); + + // Collect the page objects of the handout master. + std::vector aHandoutPageObjects; + SdrObjListIter aShapeIter (&rHandoutPage); + while (aShapeIter.IsMore()) + { + SdrPageObj* pPageObj = dynamic_cast(aShapeIter.Next()); + if (pPageObj) + aHandoutPageObjects.push_back(pPageObj); + } + if (aHandoutPageObjects.empty()) + return; + + // Connect page objects with pages. + std::vector::iterator aPageObjIter (aHandoutPageObjects.begin()); + for (std::vector::const_iterator + iPageIndex(maPageIndices.begin()), + iEnd(maPageIndices.end()); + iPageIndex!=iEnd && aPageObjIter!=aHandoutPageObjects.end(); + ++iPageIndex) + { + // Check if the page still exists. + if (*iPageIndex >= rDocument.GetSdPageCount(PageKind::Standard)) + continue; + + SdrPageObj* pPageObj = *aPageObjIter++; + pPageObj->SetReferencedPage(rDocument.GetSdPage(*iPageIndex, PageKind::Standard)); + } + + // if there are more page objects than pages left, set the rest to invisible + int nHangoverCount = 0; + while (aPageObjIter != aHandoutPageObjects.end()) + { + (*aPageObjIter++)->SetReferencedPage(nullptr); + nHangoverCount++; + } + + // Hide outlines for objects that have pages attached. + if (nHangoverCount > 0) + { + int nSkip = aHandoutPageObjects.size() - nHangoverCount; + aShapeIter.Reset(); + while (aShapeIter.IsMore()) + { + SdrPathObj* pPathObj = dynamic_cast(aShapeIter.Next()); + if (pPathObj) + { + if (nSkip > 0) + --nSkip; + else + pPathObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_NONE)); + } + } + } + + if( xHandoutPage.is() ) try + { + xHandoutPage->setPropertyValue( sPageNumber, Any( static_cast(mnHandoutPageIndex) ) ); + } + catch( Exception& ) + { + } + rViewShell.SetPrintedHandoutPageNum( mnHandoutPageIndex + 1 ); + + rPrinter.SetMapMode(maMap); + + PrintPage( + rPrinter, + rPrintView, + rHandoutPage, + pView, + false, + rVisibleLayers, + rPrintableLayers); + PrintMessage( + rPrinter, + msPageString, + maPageStringOffset); + + if( xHandoutPage.is() ) try + { + xHandoutPage->setPropertyValue( sPageNumber, Any( static_cast(0) ) ); + } + catch( Exception& ) + { + } + rViewShell.SetPrintedHandoutPageNum(1); + + // Restore outlines. + if (nHangoverCount > 0) + { + aShapeIter.Reset(); + while (aShapeIter.IsMore()) + { + SdrPathObj* pPathObj = dynamic_cast(aShapeIter.Next()); + if (pPathObj != nullptr) + pPathObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID)); + } + } + + } + + private: + const sal_uInt16 mnHandoutPageIndex; + const std::vector maPageIndices; + }; + + /** The outline information (title, subtitle, outline objects) of the + document. There is no fixed mapping of slides to printer pages. + */ + class OutlinerPrinterPage : public PrinterPage + { + public: + OutlinerPrinterPage ( + std::optional pParaObject, + const MapMode& rMapMode, + const OUString& rsPageString, + const Point& rPageStringOffset, + const DrawModeFlags nDrawMode, + const Orientation eOrientation, + const sal_uInt16 nPaperTray) + : PrinterPage(PageKind::Handout, rMapMode, false, rsPageString, + rPageStringOffset, nDrawMode, eOrientation, nPaperTray), + mpParaObject(std::move(pParaObject)) + { + } + + virtual void Print ( + Printer& rPrinter, + SdDrawDocument& rDocument, + ViewShell&, + View*, + DrawView&, + const SdrLayerIDSet&, + const SdrLayerIDSet&) const override + { + // Set up the printer. + rPrinter.SetMapMode(maMap); + + // Get and set up the outliner. + const ::tools::Rectangle aOutRect (rPrinter.GetPageOffset(), rPrinter.GetOutputSize()); + Outliner* pOutliner = rDocument.GetInternalOutliner(); + const OutlinerMode nSavedOutlMode (pOutliner->GetOutlinerMode()); + const bool bSavedUpdateMode (pOutliner->IsUpdateLayout()); + const Size aSavedPaperSize (pOutliner->GetPaperSize()); + + pOutliner->Init(OutlinerMode::OutlineView); + pOutliner->SetPaperSize(aOutRect.GetSize()); + pOutliner->SetUpdateLayout(true); + pOutliner->Clear(); + pOutliner->SetText(*mpParaObject); + + pOutliner->Draw(rPrinter, aOutRect); + + PrintMessage( + rPrinter, + msPageString, + maPageStringOffset); + + // Restore outliner and printer. + pOutliner->Clear(); + pOutliner->SetUpdateLayout(bSavedUpdateMode); + pOutliner->SetPaperSize(aSavedPaperSize); + pOutliner->Init(nSavedOutlMode); + } + + private: + std::optional mpParaObject; + }; +} + +//===== DocumentRenderer::Implementation ====================================== + +class DocumentRenderer::Implementation + : public SfxListener, + public vcl::PrinterOptionsHelper +{ +public: + explicit Implementation (ViewShellBase& rBase) + : mxObjectShell(rBase.GetDocShell()) + , mrBase(rBase) + , mbIsDisposed(false) + , mpPrinter(nullptr) + , mbHasOrientationWarningBeenShown(false) + { + DialogCreator aCreator( mrBase, mrBase.GetDocShell()->GetDocumentType() == DocumentType::Impress, GetCurrentPageIndex() ); + m_aUIProperties = aCreator.GetDialogControls(); + maSlidesPerPage = aCreator.GetSlidesPerPage(); + + StartListening(mrBase); + } + + virtual ~Implementation() override + { + EndListening(mrBase); + } + + virtual void Notify (SfxBroadcaster& rBroadcaster, const SfxHint& rHint) override + { + if (&rBroadcaster != &static_cast(mrBase)) + return; + + if (rHint.GetId() == SfxHintId::Dying) + { + mbIsDisposed = true; + } + } + + /** Process the sequence of properties given to one of the XRenderable + methods. + */ + void ProcessProperties (const css::uno::Sequence& rOptions) + { + OSL_ASSERT(!mbIsDisposed); + if (mbIsDisposed) + return; + + bool bIsValueChanged = processProperties( rOptions ); + bool bIsPaperChanged = false; + + // The RenderDevice property is handled specially: its value is + // stored in mpPrinter instead of being retrieved on demand. + Any aDev( getValue( "RenderDevice" ) ); + Reference xRenderDevice; + + if (aDev >>= xRenderDevice) + { + VCLXDevice* pDevice = comphelper::getFromUnoTunnel(xRenderDevice); + VclPtr< OutputDevice > pOut = pDevice ? pDevice->GetOutputDevice() + : VclPtr< OutputDevice >(); + mpPrinter = dynamic_cast(pOut.get()); + Size aPageSizePixel = mpPrinter ? mpPrinter->GetPaperSizePixel() : Size(); + if( aPageSizePixel != maPrinterPageSizePixel ) + { + bIsPaperChanged = true; + maPrinterPageSizePixel = aPageSizePixel; + } + } + + if (bIsValueChanged && ! mpOptions ) + mpOptions.reset(new PrintOptions(*this, std::vector(maSlidesPerPage))); + if( bIsValueChanged || bIsPaperChanged ) + PreparePages(); + } + + /** Return the number of pages that are to be printed. + */ + sal_Int32 GetPrintPageCount() const + { + OSL_ASSERT(!mbIsDisposed); + if (mbIsDisposed) + return 0; + else + return maPrinterPages.size(); + } + + /** Return a sequence of properties that can be returned by the + XRenderable::getRenderer() method. + */ + css::uno::Sequence GetProperties () const + { + css::uno::Sequence aProperties{ + comphelper::makePropertyValue("ExtraPrintUIOptions", + comphelper::containerToSequence(m_aUIProperties)), + comphelper::makePropertyValue("PageSize", maPrintSize), + // FIXME: is this always true ? + comphelper::makePropertyValue("PageIncludesNonprintableArea", true) + }; + + return aProperties; + } + + /** Print one of the prepared pages. + */ + void PrintPage (const sal_Int32 nIndex) + { + OSL_ASSERT(!mbIsDisposed); + if (mbIsDisposed) + return; + + Printer& rPrinter (*mpPrinter); + + std::shared_ptr pViewShell (mrBase.GetMainViewShell()); + if ( ! pViewShell) + return; + + SdDrawDocument* pDocument = pViewShell->GetDoc(); + OSL_ASSERT(pDocument!=nullptr); + + std::shared_ptr pDrawViewShell( + std::dynamic_pointer_cast(mrBase.GetMainViewShell())); + + if (!mpPrintView) + mpPrintView.reset(new DrawView(mrBase.GetDocShell(), &rPrinter, nullptr)); + + if (nIndex<0 || sal::static_int_cast(nIndex)>=maPrinterPages.size()) + return; + + const std::shared_ptr pPage (maPrinterPages[nIndex]); + OSL_ASSERT(pPage); + if ( ! pPage) + return; + + const Orientation eSavedOrientation (rPrinter.GetOrientation()); + const DrawModeFlags nSavedDrawMode (rPrinter.GetDrawMode()); + const MapMode aSavedMapMode (rPrinter.GetMapMode()); + const sal_uInt16 nSavedPaperBin (rPrinter.GetPaperBin()); + + // Set page orientation. + if ( ! rPrinter.SetOrientation(pPage->GetOrientation())) + { + if ( ! mbHasOrientationWarningBeenShown + && mpOptions->IsWarningOrientation()) + { + mbHasOrientationWarningBeenShown = true; + // Show warning that the orientation could not be set. + std::unique_ptr xWarn(Application::CreateMessageDialog( + pViewShell->GetFrameWeld(), VclMessageType::Warning, VclButtonsType::OkCancel, + SdResId(STR_WARN_PRINTFORMAT_FAILURE))); + xWarn->set_default_response(RET_CANCEL); + if (xWarn->run() != RET_OK) + return; + } + } + + // Set the draw mode. + rPrinter.SetDrawMode(pPage->GetDrawMode()); + + // Set paper tray. + rPrinter.SetPaperBin(pPage->GetPaperTray()); + + // Print the actual page. + pPage->Print( + rPrinter, + *pDocument, + *pViewShell, + pDrawViewShell ? pDrawViewShell->GetView() : nullptr, + *mpPrintView, + pViewShell->GetFrameView()->GetVisibleLayers(), + pViewShell->GetFrameView()->GetPrintableLayers()); + + rPrinter.SetOrientation(eSavedOrientation); + rPrinter.SetDrawMode(nSavedDrawMode); + rPrinter.SetMapMode(aSavedMapMode); + rPrinter.SetPaperBin(nSavedPaperBin); + } + +private: + // rhbz#657394: keep the document alive: prevents crash when + SfxObjectShellRef mxObjectShell; // destroying mpPrintView + ViewShellBase& mrBase; + bool mbIsDisposed; + VclPtr mpPrinter; + Size maPrinterPageSizePixel; + std::unique_ptr mpOptions; + std::vector< std::shared_ptr< ::sd::PrinterPage> > maPrinterPages; + std::unique_ptr mpPrintView; + bool mbHasOrientationWarningBeenShown; + std::vector maSlidesPerPage; + awt::Size maPrintSize; + + sal_Int32 GetCurrentPageIndex() const + { + const ViewShell *pShell = mrBase.GetMainViewShell().get(); + const SdPage *pCurrentPage = pShell ? pShell->getCurrentPage() : nullptr; + return pCurrentPage ? (pCurrentPage->GetPageNum()-1)/2 : -1; + } + + /** Determine and set the paper orientation. + */ + void SetupPaperOrientation ( + const PageKind ePageKind, + PrintInfo& rInfo) + { + SdDrawDocument* pDocument = mrBase.GetMainViewShell()->GetDoc(); + rInfo.meOrientation = Orientation::Portrait; + + if( ! mpOptions->IsBooklet()) + { + rInfo.meOrientation = pDocument->GetSdPage(0, ePageKind)->GetOrientation(); + } + else if (rInfo.maPageSize.Width() < rInfo.maPageSize.Height()) + rInfo.meOrientation = Orientation::Landscape; + + // Draw and Notes should usually abide by their specified paper size + Size aPaperSize; + if (!mpOptions->IsPrinterPreferred(pDocument->GetDocumentType())) + { + aPaperSize.setWidth(rInfo.maPageSize.Width()); + aPaperSize.setHeight(rInfo.maPageSize.Height()); + } + else + { + aPaperSize.setWidth(rInfo.mpPrinter->GetPaperSize().Width()); + aPaperSize.setHeight(rInfo.mpPrinter->GetPaperSize().Height()); + } + + maPrintSize = awt::Size(aPaperSize.Width(), aPaperSize.Height()); + + if (mpOptions->IsPrinterPreferred(pDocument->GetDocumentType())) + { + if( (rInfo.meOrientation == Orientation::Landscape && + (aPaperSize.Width() < aPaperSize.Height())) + || + (rInfo.meOrientation == Orientation::Portrait && + (aPaperSize.Width() > aPaperSize.Height())) + ) + { + maPrintSize = awt::Size(aPaperSize.Height(), aPaperSize.Width()); + } + } + } + + /** Top most method for preparing printer pages. In this and the other + Prepare... methods the various special cases are detected and + handled. + For every page that is to be printed (that may contain several + slides) one PrinterPage object is created and inserted into + maPrinterPages. + */ + void PreparePages() + { + mpPrintView.reset(); + maPrinterPages.clear(); + mbHasOrientationWarningBeenShown = false; + + ViewShell* pShell = mrBase.GetMainViewShell().get(); + + PrintInfo aInfo (mpPrinter, mpOptions->IsPrintMarkedOnly()); + + if (aInfo.mpPrinter==nullptr || pShell==nullptr) + return; + + MapMode aMap (aInfo.mpPrinter->GetMapMode()); + aMap.SetMapUnit(MapUnit::Map100thMM); + aInfo.maMap = aMap; + mpPrinter->SetMapMode(aMap); + + ::Outliner& rOutliner = mrBase.GetDocument()->GetDrawOutliner(); + const EEControlBits nSavedControlWord (rOutliner.GetControlWord()); + EEControlBits nCntrl = nSavedControlWord; + nCntrl &= ~EEControlBits::MARKFIELDS; + nCntrl &= ~EEControlBits::ONLINESPELLING; + rOutliner.SetControlWord( nCntrl ); + + // When in outline view then apply all pending changes to the model. + if( auto pOutlineViewShell = dynamic_cast< OutlineViewShell *>( pShell ) ) + pOutlineViewShell->PrepareClose (false); + + // Collect some frequently used data. + if (mpOptions->IsDate()) + { + aInfo.msTimeDate += GetSdrGlobalData().GetLocaleData()->getDate( Date( Date::SYSTEM ) ); + aInfo.msTimeDate += " "; + } + + if (mpOptions->IsTime()) + aInfo.msTimeDate += GetSdrGlobalData().GetLocaleData()->getTime( ::tools::Time( ::tools::Time::SYSTEM ), false ); + + // Draw and Notes should usually use specified paper size when printing + if (!mpOptions->IsPrinterPreferred(mrBase.GetDocShell()->GetDocumentType())) + { + aInfo.maPrintSize = mrBase.GetDocument()->GetSdPage(0, PageKind::Standard)->GetSize(); + maPrintSize = awt::Size(aInfo.maPrintSize.Width(), + aInfo.maPrintSize.Height()); + } + else + { + aInfo.maPrintSize = aInfo.mpPrinter->GetOutputSize(); + maPrintSize = awt::Size( + aInfo.mpPrinter->GetPaperSize().Width(), + aInfo.mpPrinter->GetPaperSize().Height()); + } + + switch (mpOptions->GetOutputQuality()) + { + case 1: // Grayscale + aInfo.mnDrawMode = DrawModeFlags::GrayLine | DrawModeFlags::GrayFill + | DrawModeFlags::GrayText | DrawModeFlags::GrayBitmap + | DrawModeFlags::GrayGradient; + break; + + case 2: // Black & White + aInfo.mnDrawMode = DrawModeFlags::BlackLine | DrawModeFlags::WhiteFill + | DrawModeFlags::BlackText | DrawModeFlags::GrayBitmap + | DrawModeFlags::WhiteGradient; + break; + + default: + aInfo.mnDrawMode = DrawModeFlags::Default; + } + + if (mpOptions->IsDraw()) + PrepareStdOrNotes(PageKind::Standard, aInfo); + if (mpOptions->IsNotes()) + PrepareStdOrNotes(PageKind::Notes, aInfo); + if (mpOptions->IsHandout()) + { + InitHandoutTemplate(); + PrepareHandout(aInfo); + } + if (mpOptions->IsOutline()) + PrepareOutline(aInfo); + + rOutliner.SetControlWord(nSavedControlWord); + } + + /** Create the page objects of the handout template. When the actual + printing takes place then the page objects are assigned different + sets of slides for each printed page (see HandoutPrinterPage::Print). + */ + void InitHandoutTemplate() + { + const sal_Int32 nSlidesPerHandout (mpOptions->GetHandoutPageCount()); + const bool bHandoutHorizontal (mpOptions->IsHandoutHorizontal()); + + AutoLayout eLayout = AUTOLAYOUT_HANDOUT6; + switch (nSlidesPerHandout) + { + case 0: eLayout = AUTOLAYOUT_NONE; break; // AUTOLAYOUT_HANDOUT1; break; + case 1: eLayout = AUTOLAYOUT_HANDOUT1; break; + case 2: eLayout = AUTOLAYOUT_HANDOUT2; break; + case 3: eLayout = AUTOLAYOUT_HANDOUT3; break; + case 4: eLayout = AUTOLAYOUT_HANDOUT4; break; + default: + case 6: eLayout = AUTOLAYOUT_HANDOUT6; break; + case 9: eLayout = AUTOLAYOUT_HANDOUT9; break; + } + + if( !mrBase.GetDocument() ) + return; + + SdDrawDocument& rModel = *mrBase.GetDocument(); + + // first, prepare handout page (not handout master) + + SdPage* pHandout = rModel.GetSdPage(0, PageKind::Handout); + if( !pHandout ) + return; + + // delete all previous shapes from handout page + while( pHandout->GetObjCount() ) + { + SdrObject* pObj = pHandout->NbcRemoveObject(0); + if( pObj ) + SdrObject::Free( pObj ); + } + + const bool bDrawLines (eLayout == AUTOLAYOUT_HANDOUT3); + + std::vector< ::tools::Rectangle > aAreas; + SdPage::CalculateHandoutAreas( rModel, eLayout, bHandoutHorizontal, aAreas ); + + std::vector< ::tools::Rectangle >::iterator iter( aAreas.begin() ); + while( iter != aAreas.end() ) + { + pHandout->NbcInsertObject( + new SdrPageObj( + rModel, + (*iter++))); + + if( bDrawLines && (iter != aAreas.end()) ) + { + ::tools::Rectangle aRect( *iter++ ); + + basegfx::B2DPolygon aPoly; + aPoly.insert(0, basegfx::B2DPoint( aRect.Left(), aRect.Top() ) ); + aPoly.insert(1, basegfx::B2DPoint( aRect.Right(), aRect.Top() ) ); + + basegfx::B2DHomMatrix aMatrix; + aMatrix.translate( 0.0, static_cast< double >( aRect.GetHeight() / 7 ) ); + + basegfx::B2DPolyPolygon aPathPoly; + for( sal_uInt16 nLine = 0; nLine < 7; nLine++ ) + { + aPoly.transform( aMatrix ); + aPathPoly.append( aPoly ); + } + + SdrPathObj* pPathObj = new SdrPathObj( + rModel, + SdrObjKind::PathLine, + aPathPoly); + pPathObj->SetMergedItem(XLineStyleItem(drawing::LineStyle_SOLID)); + pPathObj->SetMergedItem(XLineColorItem(OUString(), COL_BLACK)); + + pHandout->NbcInsertObject( pPathObj ); + } + } + } + + /** Detect whether the specified slide is to be printed. + @return + When the slide is not to be printed then is returned. + Otherwise a pointer to the slide is returned. + */ + SdPage* GetFilteredPage ( + const sal_Int32 nPageIndex, + const PageKind ePageKind) const + { + OSL_ASSERT(mrBase.GetDocument() != nullptr); + OSL_ASSERT(nPageIndex>=0); + SdPage* pPage = mrBase.GetDocument()->GetSdPage( + sal::static_int_cast(nPageIndex), + ePageKind); + if (pPage == nullptr) + return nullptr; + if ( ! pPage->IsExcluded() || mpOptions->IsPrintExcluded()) + return pPage; + else + return nullptr; + } + + /** Prepare the outline of the document for printing. There is no fixed + number of slides whose outline data is put onto one printer page. + If the current printer page has enough room for the outline of the + current slide then that is added. Otherwise a new printer page is + started. + */ + void PrepareOutline (PrintInfo const & rInfo) + { + MapMode aMap (rInfo.maMap); + Point aPageOfs (rInfo.mpPrinter->GetPageOffset() ); + aMap.SetScaleX(Fraction(1,2)); + aMap.SetScaleY(Fraction(1,2)); + mpPrinter->SetMapMode(aMap); + + ::tools::Rectangle aOutRect(aPageOfs, rInfo.mpPrinter->GetOutputSize()); + if( aOutRect.GetWidth() > aOutRect.GetHeight() ) + { + Size aPaperSize( rInfo.mpPrinter->PixelToLogic( rInfo.mpPrinter->GetPaperSizePixel(), MapMode( MapUnit::Map100thMM ) ) ); + maPrintSize.Width = aPaperSize.Height(); + maPrintSize.Height = aPaperSize.Width(); + const auto nRotatedWidth = aOutRect.GetHeight(); + const auto nRotatedHeight = aOutRect.GetWidth(); + const auto nRotatedX = aPageOfs.Y(); + const auto nRotatedY = aPageOfs.X(); + aOutRect = ::tools::Rectangle(Point( nRotatedX, nRotatedY), + Size(nRotatedWidth, nRotatedHeight)); + } + + Outliner* pOutliner = mrBase.GetDocument()->GetInternalOutliner(); + pOutliner->Init(OutlinerMode::OutlineView); + const OutlinerMode nSavedOutlMode (pOutliner->GetOutlinerMode()); + const bool bSavedUpdateMode (pOutliner->IsUpdateLayout()); + const Size aSavedPaperSize (pOutliner->GetPaperSize()); + const MapMode aSavedMapMode (pOutliner->GetRefMapMode()); + pOutliner->SetPaperSize(aOutRect.GetSize()); + pOutliner->SetUpdateLayout(true); + + ::tools::Long nPageH = aOutRect.GetHeight(); + + std::vector< sal_Int32 > aPages; + sal_Int32 nPageCount = mrBase.GetDocument()->GetSdPageCount(PageKind::Standard); + StringRangeEnumerator::getRangesFromString( + mpOptions->GetPrinterSelection(nPageCount, GetCurrentPageIndex()), + aPages, 0, nPageCount-1); + + for (size_t nIndex = 0, nCount = aPages.size(); nIndex < nCount;) + { + pOutliner->Clear(); + + Paragraph* pPara = nullptr; + ::tools::Long nH (0); + while (nH < nPageH && nIndexGetObjCount()) + { + SdrObject* pObj = pPage->GetObj(nObj++); + if (pObj->GetObjInventor() == SdrInventor::Default + && pObj->GetObjIdentifier() == SdrObjKind::TitleText) + { + pTextObj = dynamic_cast(pObj); + } + } + + pPara = pOutliner->GetParagraph(pOutliner->GetParagraphCount() - 1); + + if (pTextObj!=nullptr + && !pTextObj->IsEmptyPresObj() + && pTextObj->GetOutlinerParaObject()) + { + pOutliner->AddText(*(pTextObj->GetOutlinerParaObject())); + } + else + pOutliner->Insert(OUString()); + + pTextObj = nullptr; + nObj = 0; + + while (pTextObj==nullptr && nObjGetObjCount()) + { + SdrObject* pObj = pPage->GetObj(nObj++); + if (pObj->GetObjInventor() == SdrInventor::Default + && pObj->GetObjIdentifier() == SdrObjKind::OutlineText) + { + pTextObj = dynamic_cast(pObj); + } + } + + bool bSubTitle (false); + if (!pTextObj) + { + bSubTitle = true; + pTextObj = dynamic_cast(pPage->GetPresObj(PresObjKind::Text)); // is there a subtitle? + } + + sal_Int32 nParaCount1 = pOutliner->GetParagraphCount(); + + if (pTextObj!=nullptr + && !pTextObj->IsEmptyPresObj() + && pTextObj->GetOutlinerParaObject()) + { + pOutliner->AddText(*(pTextObj->GetOutlinerParaObject())); + } + + if (bSubTitle ) + { + const sal_Int32 nParaCount2 (pOutliner->GetParagraphCount()); + for (sal_Int32 nPara=nParaCount1; nParaGetParagraph(nPara); + if (pP!=nullptr && pOutliner->GetDepth(nPara) > 0) + pOutliner->SetDepth(pP, 0); + } + } + + nH = pOutliner->GetTextHeight(); + } + + // Remove the last paragraph when that does not fit completely on + // the current page. + if (nH > nPageH && pPara!=nullptr) + { + sal_Int32 nCnt = pOutliner->GetAbsPos( + pOutliner->GetParagraph( pOutliner->GetParagraphCount() - 1 ) ); + sal_Int32 nParaPos = pOutliner->GetAbsPos( pPara ); + nCnt -= nParaPos; + pPara = pOutliner->GetParagraph( ++nParaPos ); + if ( nCnt && pPara ) + { + pOutliner->Remove(pPara, nCnt); + --nIndex; + } + } + + if ( CheckForFrontBackPages( nIndex ) ) + { + maPrinterPages.push_back( + std::make_shared( + pOutliner->CreateParaObject(), + aMap, + rInfo.msTimeDate, + aPageOfs, + rInfo.mnDrawMode, + rInfo.meOrientation, + rInfo.mpPrinter->GetPaperBin())); + } + } + + pOutliner->SetRefMapMode(aSavedMapMode); + pOutliner->SetUpdateLayout(bSavedUpdateMode); + pOutliner->SetPaperSize(aSavedPaperSize); + pOutliner->Init(nSavedOutlMode); + } + + /** Prepare handout pages for slides that are to be printed. + */ + void PrepareHandout (PrintInfo& rInfo) + { + SdDrawDocument* pDocument = mrBase.GetDocument(); + OSL_ASSERT(pDocument != nullptr); + SdPage& rHandoutPage (*pDocument->GetSdPage(0, PageKind::Handout)); + + const bool bScalePage (mpOptions->IsPageSize()); + + sal_uInt16 nPaperBin; + if ( ! mpOptions->IsPaperBin()) + nPaperBin = rHandoutPage.GetPaperBin(); + else + nPaperBin = rInfo.mpPrinter->GetPaperBin(); + + // Change orientation? + SdPage& rMaster (dynamic_cast(rHandoutPage.TRG_GetMasterPage())); + rInfo.meOrientation = rMaster.GetOrientation(); + + const Size aPaperSize (rInfo.mpPrinter->GetPaperSize()); + if( (rInfo.meOrientation == Orientation::Landscape && + (aPaperSize.Width() < aPaperSize.Height())) + || + (rInfo.meOrientation == Orientation::Portrait && + (aPaperSize.Width() > aPaperSize.Height())) + ) + { + maPrintSize = awt::Size(aPaperSize.Height(), aPaperSize.Width()); + } + else + { + maPrintSize = awt::Size(aPaperSize.Width(), aPaperSize.Height()); + } + + MapMode aMap (rInfo.maMap); + const Point aPageOfs (rInfo.mpPrinter->GetPageOffset()); + + if ( bScalePage ) + { + const Size aPageSize (rHandoutPage.GetSize()); + const Size aPrintSize (rInfo.mpPrinter->GetOutputSize()); + + const double fHorz = static_cast(aPrintSize.Width()) / aPageSize.Width(); + const double fVert = static_cast(aPrintSize.Height()) / aPageSize.Height(); + + Fraction aFract; + if ( fHorz < fVert ) + aFract = Fraction(aPrintSize.Width(), aPageSize.Width()); + else + aFract = Fraction(aPrintSize.Height(), aPageSize.Height()); + + aMap.SetScaleX(aFract); + aMap.SetScaleY(aFract); + aMap.SetOrigin(Point()); + } + + std::shared_ptr pViewShell (mrBase.GetMainViewShell()); + pViewShell->WriteFrameViewData(); + + // Count page shapes. + sal_uInt32 nShapeCount (0); + SdrObjListIter aShapeIter (&rHandoutPage); + while (aShapeIter.IsMore()) + { + SdrPageObj* pPageObj = dynamic_cast(aShapeIter.Next()); + if (pPageObj) + ++nShapeCount; + } + + const sal_uInt16 nPageCount = mrBase.GetDocument()->GetSdPageCount(PageKind::Standard); + const sal_uInt16 nHandoutPageCount = nShapeCount ? (nPageCount + nShapeCount - 1) / nShapeCount : 0; + pViewShell->SetPrintedHandoutPageCount( nHandoutPageCount ); + mrBase.GetDocument()->setHandoutPageCount( nHandoutPageCount ); + + // Distribute pages to handout pages. + StringRangeEnumerator aRangeEnum( + mpOptions->GetPrinterSelection(nPageCount, GetCurrentPageIndex()), + 0, nPageCount-1); + std::vector aPageIndices; + sal_uInt16 nPrinterPageIndex = 0; + StringRangeEnumerator::Iterator it = aRangeEnum.begin(), itEnd = aRangeEnum.end(); + bool bLastLoop = (it == itEnd); + while (!bLastLoop) + { + sal_Int32 nPageIndex = *it; + ++it; + bLastLoop = (it == itEnd); + + if (GetFilteredPage(nPageIndex, PageKind::Standard)) + aPageIndices.push_back(nPageIndex); + else if (!bLastLoop) + continue; + + // Create a printer page when we have found one page for each + // placeholder or when this is the last (and special) loop. + if ( !aPageIndices.empty() && CheckForFrontBackPages( nPageIndex ) + && (aPageIndices.size() == nShapeCount || bLastLoop) ) + { + maPrinterPages.push_back( + std::make_shared( + nPrinterPageIndex++, + std::move(aPageIndices), + aMap, + rInfo.msTimeDate, + aPageOfs, + rInfo.mnDrawMode, + rInfo.meOrientation, + nPaperBin)); + aPageIndices.clear(); + } + } + } + + /** Prepare the notes pages or regular slides. + */ + void PrepareStdOrNotes ( + const PageKind ePageKind, + PrintInfo& rInfo) + { + OSL_ASSERT(rInfo.mpPrinter != nullptr); + + // Fill in page kind specific data. + SdDrawDocument* pDocument = mrBase.GetMainViewShell()->GetDoc(); + if (pDocument->GetSdPageCount(ePageKind) == 0) + return; + SdPage* pRefPage = pDocument->GetSdPage(0, ePageKind); + rInfo.maPageSize = pRefPage->GetSize(); + + SetupPaperOrientation(ePageKind, rInfo); + + MapMode aMap (rInfo.maMap); + rInfo.maMap = aMap; + + if (mpOptions->IsBooklet()) + PrepareBooklet(ePageKind, rInfo); + else + PrepareRegularPages(ePageKind, rInfo); + } + + /** Prepare slides in a non-booklet way: one slide per one to many + printer pages. + */ + void PrepareRegularPages ( + const PageKind ePageKind, + PrintInfo& rInfo) + { + std::shared_ptr pViewShell (mrBase.GetMainViewShell()); + pViewShell->WriteFrameViewData(); + + sal_Int32 nPageCount = mrBase.GetDocument()->GetSdPageCount(PageKind::Standard); + StringRangeEnumerator aRangeEnum( + mpOptions->GetPrinterSelection(nPageCount, GetCurrentPageIndex()), + 0, nPageCount-1); + for (StringRangeEnumerator::Iterator + it = aRangeEnum.begin(), + itEnd = aRangeEnum.end(); + it != itEnd; + ++it) + { + SdPage* pPage = GetFilteredPage(*it, ePageKind); + if (pPage == nullptr) + continue; + + MapMode aMap (rInfo.maMap); + // is it possible that the page size changed? + const Size aPageSize = pPage->GetSize(); + + if (mpOptions->IsPageSize()) + { + const double fHorz (static_cast(rInfo.maPrintSize.Width()) / aPageSize.Width()); + const double fVert (static_cast(rInfo.maPrintSize.Height()) / aPageSize.Height()); + + Fraction aFract; + if (fHorz < fVert) + aFract = Fraction(rInfo.maPrintSize.Width(), aPageSize.Width()); + else + aFract = Fraction(rInfo.maPrintSize.Height(), aPageSize.Height()); + + aMap.SetScaleX(aFract); + aMap.SetScaleY(aFract); + aMap.SetOrigin(Point()); + } + + if (mpOptions->IsPrintPageName()) + { + rInfo.msPageString = pPage->GetName() + " "; + } + else + rInfo.msPageString.clear(); + rInfo.msPageString += rInfo.msTimeDate; + + ::tools::Long aPageWidth = aPageSize.Width() - pPage->GetLeftBorder() - pPage->GetRightBorder(); + ::tools::Long aPageHeight = aPageSize.Height() - pPage->GetUpperBorder() - pPage->GetLowerBorder(); + // Bugfix for 44530: + // if it was implicitly changed (Landscape/Portrait), + // this is considered for tiling, respectively for the splitting up + // (Poster) + if( ( rInfo.maPrintSize.Width() > rInfo.maPrintSize.Height() + && aPageWidth < aPageHeight ) + || ( rInfo.maPrintSize.Width() < rInfo.maPrintSize.Height() + && aPageWidth > aPageHeight ) ) + { + const sal_Int32 nTmp (rInfo.maPrintSize.Width()); + rInfo.maPrintSize.setWidth( rInfo.maPrintSize.Height() ); + rInfo.maPrintSize.setHeight( nTmp ); + } + + if (mpOptions->IsTilePage() + && aPageWidth < rInfo.maPrintSize.Width() + && aPageHeight < rInfo.maPrintSize.Height()) + { + // Put multiple slides on one printer page. + PrepareTiledPage(*it, *pPage, ePageKind, rInfo); + } + else + { + rInfo.maMap = aMap; + PrepareScaledPage(*it, *pPage, ePageKind, rInfo); + } + } + } + + /** Put two slides on one printer page. + */ + void PrepareBooklet ( + const PageKind ePageKind, + const PrintInfo& rInfo) + { + MapMode aStdMap (rInfo.maMap); + Point aOffset; + Size aPrintSize_2 (rInfo.maPrintSize); + Size aPageSize_2 (rInfo.maPageSize); + + if (rInfo.meOrientation == Orientation::Landscape) + aPrintSize_2.setWidth( aPrintSize_2.Width() >> 1 ); + else + aPrintSize_2.setHeight( aPrintSize_2.Height() >> 1 ); + + const double fPageWH = static_cast(aPageSize_2.Width()) / aPageSize_2.Height(); + const double fPrintWH = static_cast(aPrintSize_2.Width()) / aPrintSize_2.Height(); + + if( fPageWH < fPrintWH ) + { + aPageSize_2.setWidth( static_cast<::tools::Long>( aPrintSize_2.Height() * fPageWH ) ); + aPageSize_2.setHeight( aPrintSize_2.Height() ); + } + else + { + aPageSize_2.setWidth( aPrintSize_2.Width() ); + aPageSize_2.setHeight( static_cast<::tools::Long>( aPrintSize_2.Width() / fPageWH ) ); + } + + MapMode aMap (rInfo.maMap); + aMap.SetScaleX( Fraction( aPageSize_2.Width(), rInfo.maPageSize.Width() ) ); + aMap.SetScaleY( Fraction( aPageSize_2.Height(), rInfo.maPageSize.Height() ) ); + + // calculate adjusted print size + const Size aAdjustedPrintSize (OutputDevice::LogicToLogic( + rInfo.maPrintSize, + aStdMap, + aMap)); + + if (rInfo.meOrientation == Orientation::Landscape) + { + aOffset.setX( ( ( aAdjustedPrintSize.Width() >> 1 ) - rInfo.maPageSize.Width() ) >> 1 ); + aOffset.setY( ( aAdjustedPrintSize.Height() - rInfo.maPageSize.Height() ) >> 1 ); + } + else + { + aOffset.setX( ( aAdjustedPrintSize.Width() - rInfo.maPageSize.Width() ) >> 1 ); + aOffset.setY( ( ( aAdjustedPrintSize.Height() >> 1 ) - rInfo.maPageSize.Height() ) >> 1 ); + } + + // create vector of pages to print + sal_Int32 nPageCount = mrBase.GetDocument()->GetSdPageCount(ePageKind); + StringRangeEnumerator aRangeEnum( + mpOptions->GetPrinterSelection(nPageCount, GetCurrentPageIndex()), + 0, nPageCount-1); + std::vector< sal_uInt16 > aPageVector; + for (StringRangeEnumerator::Iterator + it = aRangeEnum.begin(), + itEnd = aRangeEnum.end(); + it != itEnd; + ++it) + { + SdPage* pPage = GetFilteredPage(*it, ePageKind); + if (pPage != nullptr) + aPageVector.push_back(*it); + } + + // create pairs of pages to print on each page + std::vector< std::pair< sal_uInt16, sal_uInt16 > > aPairVector; + if ( ! aPageVector.empty()) + { + sal_uInt32 nFirstIndex = 0, nLastIndex = aPageVector.size() - 1; + + if( aPageVector.size() & 1 ) + aPairVector.emplace_back( sal_uInt16(65535), aPageVector[ nFirstIndex++ ] ); + else + aPairVector.emplace_back( aPageVector[ nLastIndex-- ], aPageVector[ nFirstIndex++ ] ); + + while( nFirstIndex < nLastIndex ) + { + if( nFirstIndex & 1 ) + aPairVector.emplace_back( aPageVector[ nFirstIndex++ ], aPageVector[ nLastIndex-- ] ); + else + aPairVector.emplace_back( aPageVector[ nLastIndex-- ], aPageVector[ nFirstIndex++ ] ); + } + } + + for (sal_uInt32 + nIndex=0, + nCount=aPairVector.size(); + nIndex < nCount; + ++nIndex) + { + if ( CheckForFrontBackPages( nIndex ) ) + { + const std::pair aPair (aPairVector[nIndex]); + Point aSecondOffset (aOffset); + if (rInfo.meOrientation == Orientation::Landscape) + aSecondOffset.AdjustX( aAdjustedPrintSize.Width() / 2 ); + else + aSecondOffset.AdjustY( aAdjustedPrintSize.Height() / 2 ); + maPrinterPages.push_back( + std::make_shared( + aPair.first, + aPair.second, + aOffset, + aSecondOffset, + ePageKind, + aMap, + rInfo.mbPrintMarkedOnly, + rInfo.mnDrawMode, + rInfo.meOrientation, + rInfo.mpPrinter->GetPaperBin())); + + } + } + } + + /** Print one slide multiple times on one printer page so that the whole + printer page is covered. + */ + void PrepareTiledPage ( + const sal_Int32 nPageIndex, + const SdPage& rPage, + const PageKind ePageKind, + const PrintInfo& rInfo) + { + sal_uInt16 nPaperBin; + if ( ! mpOptions->IsPaperBin()) + nPaperBin = rPage.GetPaperBin(); + else + nPaperBin = rInfo.mpPrinter->GetPaperBin(); + + if ( !CheckForFrontBackPages( nPageIndex ) ) + return; + + maPrinterPages.push_back( + std::make_shared( + sal::static_int_cast(nPageIndex), + ePageKind, + rInfo.mbPrintMarkedOnly, + rInfo.msPageString, + rInfo.mpPrinter->GetPageOffset(), + rInfo.mnDrawMode, + rInfo.meOrientation, + nPaperBin)); + } + + /** Print one standard slide or notes page on one to many printer + pages. More than on printer page is used when the slide is larger + than the printable area. + */ + void PrepareScaledPage ( + const sal_Int32 nPageIndex, + const SdPage& rPage, + const PageKind ePageKind, + const PrintInfo& rInfo) + { + const Point aPageOffset (rInfo.mpPrinter->GetPageOffset()); + + sal_uInt16 nPaperBin; + if ( ! mpOptions->IsPaperBin()) + nPaperBin = rPage.GetPaperBin(); + else + nPaperBin = rInfo.mpPrinter->GetPaperBin(); + + // For pages larger then the printable area there + // are three options: + // 1. Scale down to the page to the printable area. + // 2. Print only the upper left part of the page + // (without the unprintable borders). + // 3. Split the page into parts of the size of the + // printable area. + const bool bScalePage (mpOptions->IsPageSize()); + const bool bCutPage (mpOptions->IsCutPage()); + MapMode aMap (rInfo.maMap); + if ( (bScalePage || bCutPage) && CheckForFrontBackPages( nPageIndex ) ) + { + // Handle 1 and 2. + + // if CutPage is set then do not move it, otherwise move the + // scaled page to printable area + maPrinterPages.push_back( + std::make_shared( + sal::static_int_cast(nPageIndex), + ePageKind, + aMap, + rInfo.mbPrintMarkedOnly, + rInfo.msPageString, + aPageOffset, + rInfo.mnDrawMode, + rInfo.meOrientation, + nPaperBin)); + } + else + { + // Handle 3. Print parts of the page in the size of the + // printable area until the whole page is covered. + + // keep the page content at its position if it fits, otherwise + // move it to the printable area + const ::tools::Long nPageWidth ( + rInfo.maPageSize.Width() - rPage.GetLeftBorder() - rPage.GetRightBorder()); + const ::tools::Long nPageHeight ( + rInfo.maPageSize.Height() - rPage.GetUpperBorder() - rPage.GetLowerBorder()); + + Point aOrigin ( 0, 0 ); + + for (Point aPageOrigin = aOrigin; + -aPageOrigin.Y()( + sal::static_int_cast(nPageIndex), + ePageKind, + aMap, + rInfo.mbPrintMarkedOnly, + rInfo.msPageString, + aPageOffset, + rInfo.mnDrawMode, + rInfo.meOrientation, + nPaperBin)); + } + } + } + } + } + +bool CheckForFrontBackPages( sal_Int32 nPage ) +{ + const bool bIsIndexOdd(nPage & 1); + if ((!bIsIndexOdd && mpOptions->IsPrintFrontPage()) + || (bIsIndexOdd && mpOptions->IsPrintBackPage())) + { + return true; + } + else + return false; +} +}; + +//===== DocumentRenderer ====================================================== + +DocumentRenderer::DocumentRenderer (ViewShellBase& rBase) + : mpImpl(new Implementation(rBase)) +{ +} + +DocumentRenderer::~DocumentRenderer() +{ +} + +//----- XRenderable ----------------------------------------------------------- + +sal_Int32 SAL_CALL DocumentRenderer::getRendererCount ( + const css::uno::Any&, + const css::uno::Sequence& rOptions) +{ + mpImpl->ProcessProperties(rOptions); + return mpImpl->GetPrintPageCount(); +} + +Sequence SAL_CALL DocumentRenderer::getRenderer ( + sal_Int32, + const css::uno::Any&, + const css::uno::Sequence& rOptions) +{ + mpImpl->ProcessProperties(rOptions); + return mpImpl->GetProperties(); +} + +void SAL_CALL DocumentRenderer::render ( + sal_Int32 nRenderer, + const css::uno::Any&, + const css::uno::Sequence& rOptions) +{ + mpImpl->ProcessProperties(rOptions); + mpImpl->PrintPage(nRenderer); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/FormShellManager.cxx b/sd/source/ui/view/FormShellManager.cxx new file mode 100644 index 000000000..3efa9bed7 --- /dev/null +++ b/sd/source/ui/view/FormShellManager.cxx @@ -0,0 +1,319 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + +namespace { + +/** This factory is responsible for creating and deleting the FmFormShell. +*/ +class FormShellManagerFactory + : public ::sd::ShellFactory +{ +public: + FormShellManagerFactory (ViewShell& rViewShell, FormShellManager& rManager); + virtual FmFormShell* CreateShell (ShellId nId) override; + virtual void ReleaseShell (SfxShell* pShell) override; + +private: + ::sd::ViewShell& mrViewShell; + FormShellManager& mrFormShellManager; +}; + +} // end of anonymous namespace + +FormShellManager::FormShellManager (ViewShellBase& rBase) + : mrBase(rBase), + mpFormShell(nullptr), + mbFormShellAboveViewShell(false), + mbIsMainViewChangePending(false), + mpMainViewShellWindow(nullptr) +{ + // Register at the EventMultiplexer to be informed about changes in the + // center pane. + Link aLink (LINK(this, FormShellManager, ConfigurationUpdateHandler)); + mrBase.GetEventMultiplexer()->AddEventListener(aLink); + + RegisterAtCenterPane(); +} + +FormShellManager::~FormShellManager() +{ + SetFormShell(nullptr); + UnregisterAtCenterPane(); + + // Unregister from the EventMultiplexer. + Link aLink (LINK(this, FormShellManager, ConfigurationUpdateHandler)); + mrBase.GetEventMultiplexer()->RemoveEventListener(aLink); + + if (mpSubShellFactory) + { + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell != nullptr) + mrBase.GetViewShellManager()->RemoveSubShellFactory(pShell,mpSubShellFactory); + } +} + +void FormShellManager::SetFormShell (FmFormShell* pFormShell) +{ + if (mpFormShell == pFormShell) + return; + + // Disconnect from the old form shell. + if (mpFormShell != nullptr) + { + mpFormShell->SetControlActivationHandler(Link()); + EndListening(*mpFormShell); + mpFormShell->SetView(nullptr); + } + + mpFormShell = pFormShell; + + // Connect to the new form shell. + if (mpFormShell != nullptr) + { + mpFormShell->SetControlActivationHandler( + LINK( + this, + FormShellManager, + FormControlActivated)); + StartListening(*mpFormShell); + + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell != nullptr) + { + // Prevent setting the view twice at the FmFormShell. + FmFormView* pFormView = pMainViewShell->GetView(); + if (mpFormShell->GetFormView() != pFormView) + mpFormShell->SetView(pFormView); + } + } + + // Tell the ViewShellManager where on the stack to place the form shell. + mrBase.GetViewShellManager()->SetFormShell( + mrBase.GetMainViewShell().get(), + mpFormShell, + mbFormShellAboveViewShell); +} + +void FormShellManager::RegisterAtCenterPane() +{ + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell == nullptr) + return; + + // No form shell for the slide sorter. Besides that it is not + // necessary, using both together results in crashes. + if (pShell->GetShellType() == ViewShell::ST_SLIDE_SORTER) + return; + + mpMainViewShellWindow = pShell->GetActiveWindow(); + if (mpMainViewShellWindow == nullptr) + return; + + // Register at the window to get informed when to move the form + // shell to the bottom of the shell stack. + mpMainViewShellWindow->AddEventListener( + LINK( + this, + FormShellManager, + WindowEventHandler)); + + // Create a shell factory and with it activate the form shell. + OSL_ASSERT(!mpSubShellFactory); + mpSubShellFactory = std::make_shared(*pShell, *this); + mrBase.GetViewShellManager()->AddSubShellFactory(pShell,mpSubShellFactory); + mrBase.GetViewShellManager()->ActivateSubShell(*pShell, ToolbarId::FormLayer_Toolbox); +} + +void FormShellManager::UnregisterAtCenterPane() +{ + if (mpMainViewShellWindow != nullptr) + { + // Unregister from the window. + mpMainViewShellWindow->RemoveEventListener( + LINK( + this, + FormShellManager, + WindowEventHandler)); + mpMainViewShellWindow = nullptr; + } + + // Unregister form at the form shell. + SetFormShell(nullptr); + + // Deactivate the form shell and destroy the shell factory. + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell != nullptr) + { + mrBase.GetViewShellManager()->DeactivateSubShell(*pShell, ToolbarId::FormLayer_Toolbox); + mrBase.GetViewShellManager()->RemoveSubShellFactory(pShell, mpSubShellFactory); + } + + mpSubShellFactory.reset(); +} + +IMPL_LINK_NOARG(FormShellManager, FormControlActivated, LinkParamNone*, void) +{ + // The form shell has been activated. To give it priority in reacting to + // slot calls the form shell is moved to the top of the object bar shell + // stack. + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell!=nullptr && !mbFormShellAboveViewShell) + { + mbFormShellAboveViewShell = true; + + ViewShellManager::UpdateLock aLock (mrBase.GetViewShellManager()); + mrBase.GetViewShellManager()->SetFormShell(pShell,mpFormShell,mbFormShellAboveViewShell); + } +} + +IMPL_LINK(FormShellManager, ConfigurationUpdateHandler, sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::MainViewRemoved: + UnregisterAtCenterPane(); + break; + + case EventMultiplexerEventId::MainViewAdded: + mbIsMainViewChangePending = true; + break; + + case EventMultiplexerEventId::ConfigurationUpdated: + if (mbIsMainViewChangePending) + { + mbIsMainViewChangePending = false; + RegisterAtCenterPane(); + } + break; + + default: + break; + } +} + +IMPL_LINK(FormShellManager, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + switch (rEvent.GetId()) + { + case VclEventId::WindowGetFocus: + { + // The window of the center pane got the focus. Therefore + // the form shell is moved to the bottom of the object bar + // stack. + ViewShell* pShell = mrBase.GetMainViewShell().get(); + if (pShell!=nullptr && mbFormShellAboveViewShell) + { + mbFormShellAboveViewShell = false; + ViewShellManager::UpdateLock aLock (mrBase.GetViewShellManager()); + mrBase.GetViewShellManager()->SetFormShell( + pShell, + mpFormShell, + mbFormShellAboveViewShell); + } + } + break; + + case VclEventId::WindowLoseFocus: + // We follow the sloppy focus policy. Losing the focus is + // ignored. We wait for the focus to be placed either in + // the window or the form shell. The later, however, is + // notified over the FormControlActivated handler, not this + // one. + break; + + case VclEventId::ObjectDying: + mpMainViewShellWindow = nullptr; + break; + + default: break; + } +} + +void FormShellManager::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId()!=SfxHintId::Dying) + return; + + // If all goes well this listener is called after the + // FormShellManager was notified about the dying form shell by the + // FormShellManagerFactory. + OSL_ASSERT(mpFormShell==nullptr); + if (mpFormShell != nullptr) + { + mpFormShell = nullptr; + mrBase.GetViewShellManager()->SetFormShell( + mrBase.GetMainViewShell().get(), + nullptr, + false); + } +} + +//===== FormShellManagerFactory =============================================== + +namespace { + +FormShellManagerFactory::FormShellManagerFactory ( + ::sd::ViewShell& rViewShell, + FormShellManager& rManager) + : mrViewShell(rViewShell), + mrFormShellManager(rManager) +{ +} + +FmFormShell* FormShellManagerFactory::CreateShell( ::sd::ShellId nId ) +{ + FmFormShell* pShell = nullptr; + + ::sd::View* pView = mrViewShell.GetView(); + if (nId == ToolbarId::FormLayer_Toolbox) + { + pShell = new FmFormShell(&mrViewShell.GetViewShellBase(), pView); + mrFormShellManager.SetFormShell(pShell); + } + + return pShell; +} + +void FormShellManagerFactory::ReleaseShell (SfxShell* pShell) +{ + if (pShell != nullptr) + { + mrFormShellManager.SetFormShell(nullptr); + delete pShell; + } +} + +} // end of anonymous namespace + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/GraphicObjectBar.cxx b/sd/source/ui/view/GraphicObjectBar.cxx new file mode 100644 index 000000000..60cab73f7 --- /dev/null +++ b/sd/source/ui/view/GraphicObjectBar.cxx @@ -0,0 +1,141 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +using namespace sd; +#define ShellClass_GraphicObjectBar +#include + +namespace sd { + + +SFX_IMPL_INTERFACE(GraphicObjectBar, SfxShell) + +void GraphicObjectBar::InitInterface_Impl() +{ +} + + +GraphicObjectBar::GraphicObjectBar ( + const ViewShell* pSdViewShell, + ::sd::View* pSdView ) + : SfxShell (pSdViewShell->GetViewShell()), + mpView ( pSdView ) +{ + DrawDocShell* pDocShell = pSdViewShell->GetDocSh(); + + SetPool( &pDocShell->GetPool() ); + SetUndoManager( pDocShell->GetUndoManager() ); + SetRepeatTarget( mpView ); + SetName( "Graphic objectbar"); +} + +GraphicObjectBar::~GraphicObjectBar() +{ + SetRepeatTarget( nullptr ); +} + +void GraphicObjectBar::GetAttrState( SfxItemSet& rSet ) +{ + if( mpView ) + SvxGrafAttrHelper::GetGrafAttrState( rSet, *mpView ); +} + +void GraphicObjectBar::Execute( SfxRequest& rReq ) +{ + if( mpView ) + { + SvxGrafAttrHelper::ExecuteGrafAttr( rReq, *mpView ); + Invalidate(); + } +} + +void GraphicObjectBar::GetFilterState( SfxItemSet& rSet ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + bool bEnable = false; + + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + if( auto pGrafObj = dynamic_cast< SdrGrafObj *>( pObj ) ) + if( pGrafObj->GetGraphicType() == GraphicType::Bitmap ) + bEnable = true; + } + + if( !bEnable ) + SvxGraphicFilter::DisableGraphicFilterSlots( rSet ); +} + +void GraphicObjectBar::ExecuteFilter( SfxRequest const & rReq ) +{ + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + if( auto pGrafObj = dynamic_cast< SdrGrafObj *>( pObj ) ) + if( pGrafObj->GetGraphicType() == GraphicType::Bitmap ) + { + GraphicObject aFilterObj( pGrafObj->GetGraphicObject() ); + + if( SvxGraphicFilterResult::NONE == + SvxGraphicFilter::ExecuteGrfFilterSlot( rReq, aFilterObj ) ) + { + SdrPageView* pPageView = mpView->GetSdrPageView(); + + if( pPageView ) + { + SdrGrafObj* pFilteredObj = static_cast( pObj->CloneSdrObject(pObj->getSdrModelFromSdrObject()) ); + OUString aStr = mpView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_UNDO_GRAFFILTER); + mpView->BegUndo( aStr ); + pFilteredObj->SetGraphicObject( aFilterObj ); + ::sd::View* const pView = mpView; + pView->ReplaceObjectAtView( pObj, *pPageView, pFilteredObj ); + pView->EndUndo(); + return; + } + } + } + } + + Invalidate(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/GraphicViewShellBase.cxx b/sd/source/ui/view/GraphicViewShellBase.cxx new file mode 100644 index 000000000..d58c8a0d2 --- /dev/null +++ b/sd/source/ui/view/GraphicViewShellBase.cxx @@ -0,0 +1,93 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd +{ +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new GraphicViewShellBase object has been constructed. + +SfxViewFactory* GraphicViewShellBase::s_pFactory; +SfxViewShell* GraphicViewShellBase::CreateInstance(SfxViewFrame* pFrame, SfxViewShell* pOldView) +{ + GraphicViewShellBase* pBase = new GraphicViewShellBase(pFrame, pOldView); + pBase->LateInit(framework::FrameworkHelper::msDrawViewURL); + return pBase; +} +void GraphicViewShellBase::RegisterFactory(SfxInterfaceId nPrio) +{ + s_pFactory = new SfxViewFactory(&CreateInstance, nPrio, "Default"); + InitFactory(); +} +void GraphicViewShellBase::InitFactory() { SFX_VIEW_REGISTRATION(GraphicDocShell); } + +GraphicViewShellBase::GraphicViewShellBase(SfxViewFrame* _pFrame, SfxViewShell* pOldShell) + : ViewShellBase(_pFrame, pOldShell) +{ +} + +GraphicViewShellBase::~GraphicViewShellBase() {} + +void GraphicViewShellBase::Execute(SfxRequest& rRequest) +{ + sal_uInt16 nSlotId = rRequest.GetSlot(); + + switch (nSlotId) + { + case SID_NOTES_WINDOW: + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + case SID_SLIDE_MASTER_MODE: + case SID_OUTLINE_MODE: + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + case SID_HANDOUT_MASTER_MODE: + // Prevent some Impress-only slots from being executed. + rRequest.Cancel(); + break; + + case SID_SWITCH_SHELL: + case SID_LEFT_PANE_DRAW: + case SID_LEFT_PANE_IMPRESS: + default: + // The remaining requests are forwarded to our base class. + ViewShellBase::Execute(rRequest); + break; + } +} + +void GraphicViewShellBase::InitializeFramework() +{ + css::uno::Reference xController(GetController()); + sd::framework::DrawModule::Initialize(xController); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ImpressViewShellBase.cxx b/sd/source/ui/view/ImpressViewShellBase.cxx new file mode 100644 index 000000000..96b0b5aa9 --- /dev/null +++ b/sd/source/ui/view/ImpressViewShellBase.cxx @@ -0,0 +1,97 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new ImpressViewShellBase object has been constructed. + +SfxViewFactory* ImpressViewShellBase::s_pFactory; +SfxViewShell* ImpressViewShellBase::CreateInstance ( + SfxViewFrame *pFrame, SfxViewShell *pOldView) +{ + ImpressViewShellBase* pBase = new ImpressViewShellBase(pFrame, pOldView); + pBase->LateInit(comphelper::LibreOfficeKit::isActive() ? framework::FrameworkHelper::msImpressViewURL : ""); + return pBase; +} +void ImpressViewShellBase::RegisterFactory( SfxInterfaceId nPrio ) +{ + s_pFactory = new SfxViewFactory(&CreateInstance,nPrio,"Default"); + InitFactory(); +} +void ImpressViewShellBase::InitFactory() +{ + SFX_VIEW_REGISTRATION(DrawDocShell); +} + +ImpressViewShellBase::ImpressViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell* pOldShell) + : ViewShellBase (_pFrame, pOldShell) +{ + MasterPageObserver::Instance().RegisterDocument (*GetDocShell()->GetDoc()); +} + +ImpressViewShellBase::~ImpressViewShellBase() +{ + MasterPageObserver::Instance().UnregisterDocument (*GetDocShell()->GetDoc()); +} + +void ImpressViewShellBase::Execute (SfxRequest& rRequest) +{ + sal_uInt16 nSlotId = rRequest.GetSlot(); + + switch (nSlotId) + { + case SID_LEFT_PANE_DRAW: + // Prevent a Draw-only slots from being executed. + rRequest.Cancel(); + break; + + default: + // The remaining requests are forwarded to our base class. + ViewShellBase::Execute(rRequest); + break; + } +} + +void ImpressViewShellBase::InitializeFramework() +{ + css::uno::Reference + xController (GetController()); + sd::framework::ImpressModule::Initialize(xController); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/MediaObjectBar.cxx b/sd/source/ui/view/MediaObjectBar.cxx new file mode 100644 index 000000000..232535240 --- /dev/null +++ b/sd/source/ui/view/MediaObjectBar.cxx @@ -0,0 +1,77 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace sd; +using namespace svx; + +#define ShellClass_MediaObjectBar +#include + +namespace sd +{ +SFX_IMPL_INTERFACE(MediaObjectBar, SfxShell) + +void MediaObjectBar::InitInterface_Impl() {} + +MediaObjectBar::MediaObjectBar(const ViewShell* pSdViewShell, ::sd::View* pSdView) + : SfxShell(pSdViewShell->GetViewShell()) + , mpView(pSdView) +{ + DrawDocShell* pDocShell = pSdViewShell->GetDocSh(); + + SetPool(&pDocShell->GetPool()); + SetUndoManager(pDocShell->GetUndoManager()); + SetRepeatTarget(mpView); + SetName(SdResId(RID_DRAW_MEDIA_TOOLBOX)); +} + +MediaObjectBar::~MediaObjectBar() { SetRepeatTarget(nullptr); } + +void MediaObjectBar::GetState(SfxItemSet& rSet) { MediaShellHelpers::GetState(mpView, rSet); } + +void MediaObjectBar::Execute(SfxRequest const& rReq) +{ + const ::avmedia::MediaItem* pMediaItem = MediaShellHelpers::Execute(mpView, rReq); + if (!pMediaItem) + return; + + //if only changing state then don't set modified flag (e.g. playing a video) + if (!(pMediaItem->getMaskSet() & AVMediaSetMask::STATE)) + { + //fdo #32598: after changing playback opts, set document's modified flag + SdDrawDocument& rDoc = mpView->GetDoc(); + rDoc.SetChanged(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/OutlineViewShellBase.cxx b/sd/source/ui/view/OutlineViewShellBase.cxx new file mode 100644 index 000000000..8da1bcbca --- /dev/null +++ b/sd/source/ui/view/OutlineViewShellBase.cxx @@ -0,0 +1,66 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +namespace sd { + +class DrawDocShell; + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new OutlineViewShellBase object has been constructed. + +SfxViewFactory* OutlineViewShellBase::s_pFactory; +SfxViewShell* OutlineViewShellBase::CreateInstance ( + SfxViewFrame *pFrame, SfxViewShell *pOldView) +{ + OutlineViewShellBase* pBase = new OutlineViewShellBase(pFrame, pOldView); + pBase->LateInit(framework::FrameworkHelper::msOutlineViewURL); + return pBase; +} +void OutlineViewShellBase::RegisterFactory( SfxInterfaceId nPrio ) +{ + s_pFactory = new SfxViewFactory(&CreateInstance,nPrio,"Outline"); + InitFactory(); +} +void OutlineViewShellBase::InitFactory() +{ + SFX_VIEW_REGISTRATION(DrawDocShell); +} + +OutlineViewShellBase::OutlineViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell* pOldShell) + : ImpressViewShellBase (_pFrame, pOldShell) +{ +} + +OutlineViewShellBase::~OutlineViewShellBase() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/Outliner.cxx b/sd/source/ui/view/Outliner.cxx new file mode 100644 index 000000000..a63337692 --- /dev/null +++ b/sd/source/ui/view/Outliner.cxx @@ -0,0 +1,2066 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +class SfxStyleSheetPool; + +class SdOutliner::Implementation +{ +public: + /** The original edit mode directly after switching to a different view + mode. Used for restoring the edit mode when leaving that view mode + again. + */ + EditMode meOriginalEditMode; + + Implementation(); + ~Implementation(); + + /** Return the OutlinerView that was provided by the last call to + ProvideOutlinerView() (or NULL when there was no such call.) + */ + OutlinerView* GetOutlinerView() { return mpOutlineView;} + + /** Provide in the member mpOutlineView an instance of OutlinerView that + is either taken from the ViewShell, when it is an OutlineViewShell, + or is created. When an OutlinerView already exists it is initialized. + */ + void ProvideOutlinerView ( + Outliner& rOutliner, + const std::shared_ptr& rpViewShell, + vcl::Window* pWindow); + + /** This method is called when the OutlinerView is no longer used. + */ + void ReleaseOutlinerView(); + + sd::VectorGraphicSearchContext& getVectorGraphicSearchContext() { return maVectorGraphicSearchContext; } + +private: + /** Flag that specifies whether we own the outline view pointed to by + mpOutlineView and thus have to + delete it in EndSpelling(). + */ + bool mbOwnOutlineView; + + /** The outline view used for searching and spelling. If searching or + spell checking an outline view this data member points to that view. + For all other views an instance is created. The + mbOwnOutlineView distinguishes between both cases. + */ + OutlinerView* mpOutlineView; + + sd::VectorGraphicSearchContext maVectorGraphicSearchContext; +}; + +namespace +{ + +sd::ViewShellBase* getViewShellBase() +{ + return dynamic_cast(SfxViewShell::Current()); +} + +} // end anonymous namespace + +SdOutliner::SdOutliner( SdDrawDocument* pDoc, OutlinerMode nMode ) + : SdrOutliner( &pDoc->GetItemPool(), nMode ), + mpImpl(new Implementation()), + meMode(SEARCH), + mpView(nullptr), + mpWindow(nullptr), + mpDrawDocument(pDoc), + mnConversionLanguage(LANGUAGE_NONE), + mnIgnoreCurrentPageChangesLevel(0), + mbStringFound(false), + mbMatchMayExist(false), + mnPageCount(0), + mbEndOfSearch(false), + mbFoundObject(false), + mbDirectionIsForward(true), + mbRestrictSearchToSelection(false), + mpObj(nullptr), + mpFirstObj(nullptr), + mpSearchSpellTextObj(nullptr), + mnText(0), + mpParaObj(nullptr), + meStartViewMode(PageKind::Standard), + meStartEditMode(EditMode::Page), + mnStartPageIndex(sal_uInt16(-1)), + mpStartEditedObject(nullptr), + mbPrepareSpellingPending(true) +{ + SetStyleSheetPool(static_cast( mpDrawDocument->GetStyleSheetPool() )); + SetEditTextObjectPool( &pDoc->GetItemPool() ); + SetCalcFieldValueHdl(LINK(SD_MOD(), SdModule, CalcFieldValueHdl)); + SetForbiddenCharsTable( pDoc->GetForbiddenCharsTable() ); + + EEControlBits nCntrl = GetControlWord(); + nCntrl |= EEControlBits::ALLOWBIGOBJS; + nCntrl |= EEControlBits::MARKFIELDS; + nCntrl |= EEControlBits::AUTOCORRECT; + + bool bOnlineSpell = false; + + sd::DrawDocShell* pDocSh = mpDrawDocument->GetDocSh(); + + if (pDocSh) + { + bOnlineSpell = mpDrawDocument->GetOnlineSpell(); + } + else + { + bOnlineSpell = false; + + try + { + const SvtLinguConfig aLinguConfig; + Any aAny = aLinguConfig.GetProperty( UPN_IS_SPELL_AUTO ); + aAny >>= bOnlineSpell; + } + catch( ... ) + { + OSL_FAIL( "Ill. type in linguistic property" ); + } + } + + if (bOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + SetControlWord(nCntrl); + + Reference< XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + SetSpeller( xSpellChecker ); + + Reference< XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + SetHyphenator( xHyphenator ); + + SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); +} + +/// Nothing spectacular in the destructor. +SdOutliner::~SdOutliner() +{ +} + +OutlinerView* SdOutliner::getOutlinerView() +{ + return mpImpl->GetOutlinerView(); +} + +/** Prepare find&replace or spellchecking. This distinguishes between three + cases: +
          +
        1. The current shell is a DrawViewShell: Create a + OutlinerView object and search all objects of (i) the + current mark list, (ii) of the current view, or (iii) of all the view + combinations: +
            +
          1. Draw view, slide view
          2. +
          3. Draw view, background view
          4. +
          5. Notes view, slide view
          6. +
          7. Notes view, background view
          8. +
          9. Handout view, slide view
          10. +
          11. Handout view, background view
          12. +
          + +
        2. When the current shell is a SdOutlineViewShell then + directly operate on it. No switching into other views takes place.
        3. +
        +*/ +void SdOutliner::PrepareSpelling() +{ + mbPrepareSpellingPending = false; + + sd::ViewShellBase* pBase = getViewShellBase(); + if (pBase != nullptr) + SetViewShell (pBase->GetMainViewShell()); + SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if (pViewShell) + { + mbStringFound = false; + + // Supposed that we are not located at the very beginning/end of + // the document then there may be a match in the document + // prior/after the current position. + mbMatchMayExist = true; + + maObjectIterator = sd::outliner::Iterator(); + maSearchStartPosition = sd::outliner::Iterator(); + RememberStartPosition(); + + mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow); + + HandleChangedSelection (); + } + ClearModifyFlag(); +} + +void SdOutliner::StartSpelling() +{ + meMode = SPELL; + mbDirectionIsForward = true; + mpSearchItem.reset(); +} + +/** Free all resources acquired during the search/spell check. After a + spell check the start position is restored here. +*/ +void SdOutliner::EndSpelling() +{ + // Keep old view shell alive until we release the outliner view. + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + std::shared_ptr pOldViewShell (pViewShell); + + sd::ViewShellBase* pBase = getViewShellBase(); + if (pBase != nullptr) + pViewShell = pBase->GetMainViewShell(); + else + pViewShell.reset(); + mpWeakViewShell = pViewShell; + + // When in PrepareSpelling() a new outline view has + // been created then delete it here. + bool bViewIsDrawViewShell(dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() )); + if (bViewIsDrawViewShell) + { + SetStatusEventHdl(Link()); + mpView = pViewShell->GetView(); + mpView->UnmarkAllObj (mpView->GetSdrPageView()); + mpView->SdrEndTextEdit(); + // Make FuSelection the current function. + pViewShell->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD); + + // Remove and, if previously created by us, delete the outline + // view. + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + { + RemoveView(pOutlinerView); + mpImpl->ReleaseOutlinerView(); + } + + SetUpdateLayout(true); + } + + // Before clearing the modify flag use it as a hint that + // changes were done at SpellCheck + if(IsModified()) + { + if(auto pOutlineView = dynamic_cast( mpView )) + pOutlineView->PrepareClose(); + if(mpDrawDocument && !mpDrawDocument->IsChanged()) + mpDrawDocument->SetChanged(); + } + + // Now clear the modify flag to have a specified state of + // Outliner + ClearModifyFlag(); + + // When spell checking then restore the start position. + if (meMode==SPELL || meMode==TEXT_CONVERSION) + RestoreStartPosition (); + + mpWeakViewShell.reset(); + mpView = nullptr; + mpWindow = nullptr; + mnStartPageIndex = sal_uInt16(-1); +} + +bool SdOutliner::SpellNextDocument() +{ + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // When doing a spell check in the outline view then there is + // only one document. + mbEndOfSearch = true; + EndOfSearch (); + } + else + { + if( auto pOutlineView = dynamic_cast( mpView )) + pOutlineView->PrepareClose(); + mpDrawDocument->GetDocSh()->SetWaitCursor( true ); + + Initialize (true); + + mpWindow = pViewShell->GetActiveWindow(); + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetWindow(mpWindow); + ProvideNextTextObject (); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + ClearModifyFlag(); + } + + return !mbEndOfSearch; +} + +/** + * check next text object + */ +svx::SpellPortions SdOutliner::GetNextSpellSentence() +{ + svx::SpellPortions aResult; + + DetectChange(); + // Iterate over sentences and text shapes until a sentence with a + // spelling error has been found. If no such sentence can be + // found the loop is left through a break. + // It is the responsibility of the sd outliner object to correctly + // iterate over all text shapes, i.e. switch between views, wrap + // around at the end of the document, stop when all text shapes + // have been examined exactly once. + bool bFoundNextSentence = false; + while ( ! bFoundNextSentence) + { + OutlinerView* pOutlinerView = GetView(0); + if (pOutlinerView != nullptr) + { + ESelection aCurrentSelection (pOutlinerView->GetSelection()); + if ( ! mbMatchMayExist + && maStartSelection < aCurrentSelection) + EndOfSearch(); + + // Advance to the next sentence. + bFoundNextSentence = SpellSentence( pOutlinerView->GetEditView(), aResult); + } + + // When no sentence with spelling errors has been found in the + // currently selected text shape or there is no selected text + // shape then advance to the next text shape. + if ( ! bFoundNextSentence) + if ( ! SpellNextDocument()) + // All text objects have been processed so exit the + // loop and return an empty portions list. + break; + } + + return aResult; +} + +/** Go to next match. +*/ +bool SdOutliner::StartSearchAndReplace (const SvxSearchItem* pSearchItem) +{ + bool bEndOfSearch = true; + + // clear the search toolbar entry + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Empty); + + mpDrawDocument->GetDocSh()->SetWaitCursor( true ); + + // Since REPLACE is really a replaceAndSearchNext instead of a searchAndReplace, + // make sure that the search portion has not changed since the last FIND. + if (!mbPrepareSpellingPending && mpSearchItem + && pSearchItem->GetCommand() == SvxSearchCmd::REPLACE + && !mpSearchItem->equalsIgnoring(*pSearchItem, /*bIgnoreReplace=*/true, + /*bIgnoreCommand=*/true)) + { + EndSpelling(); + mbPrepareSpellingPending = true; + } + + if (mbPrepareSpellingPending) + PrepareSpelling(); + sd::ViewShellBase* pBase = getViewShellBase(); + // Determine whether we have to abort the search. This is necessary + // when the main view shell does not support searching. + bool bAbort = false; + if (pBase != nullptr) + { + std::shared_ptr pShell (pBase->GetMainViewShell()); + SetViewShell(pShell); + if (pShell == nullptr) + bAbort = true; + else + switch (pShell->GetShellType()) + { + case sd::ViewShell::ST_DRAW: + case sd::ViewShell::ST_IMPRESS: + case sd::ViewShell::ST_NOTES: + case sd::ViewShell::ST_HANDOUT: + case sd::ViewShell::ST_OUTLINE: + bAbort = false; + break; + default: + bAbort = true; + break; + } + } + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return true; + } + + if ( ! bAbort) + { + meMode = SEARCH; + mpSearchItem.reset(pSearchItem->Clone()); + + mbFoundObject = false; + + Initialize ( ! mpSearchItem->GetBackward()); + + const SvxSearchCmd nCommand (mpSearchItem->GetCommand()); + if (nCommand == SvxSearchCmd::FIND_ALL || nCommand == SvxSearchCmd::REPLACE_ALL) + { + bEndOfSearch = SearchAndReplaceAll (); + } + else + { + RememberStartPosition (); + bEndOfSearch = SearchAndReplaceOnce (); + // restore start position if nothing was found + if(!mbStringFound) + { + RestoreStartPosition (); + // Nothing was changed, no need to restart the spellchecker. + if (nCommand == SvxSearchCmd::FIND) + bEndOfSearch = false; + } + mnStartPageIndex = sal_uInt16(-1); + } + } + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + + return bEndOfSearch; +} + +void SdOutliner::Initialize (bool bDirectionIsForward) +{ + const bool bIsAtEnd (maObjectIterator == sd::outliner::OutlinerContainer(this).end()); + const bool bOldDirectionIsForward = mbDirectionIsForward; + mbDirectionIsForward = bDirectionIsForward; + + if (maObjectIterator == sd::outliner::Iterator()) + { + // Initialize a new search. + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + maCurrentPosition = *maObjectIterator; + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return; + } + + // In case we are searching in an outline view then first remove the + // current selection and place cursor at its start or end. + if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + ESelection aSelection = getOutlinerView()->GetSelection (); + if (mbDirectionIsForward) + { + aSelection.nEndPara = aSelection.nStartPara; + aSelection.nEndPos = aSelection.nStartPos; + } + else + { + aSelection.nStartPara = aSelection.nEndPara; + aSelection.nStartPos = aSelection.nEndPos; + } + getOutlinerView()->SetSelection (aSelection); + } + + // When not beginning the search at the beginning of the search area + // then there may be matches before the current position. + mbMatchMayExist = (maObjectIterator!=sd::outliner::OutlinerContainer(this).begin()); + } + else if (bOldDirectionIsForward != mbDirectionIsForward) + { + // Requested iteration direction has changed. Turn around the iterator. + maObjectIterator.Reverse(); + if (bIsAtEnd) + { + // The iterator has pointed to end(), which after the search + // direction is reversed, becomes begin(). + maObjectIterator = sd::outliner::OutlinerContainer(this).begin(); + } + else + { + // The iterator has pointed to the object one ahead/before the current + // one. Now move it to the one before/ahead the current one. + ++maObjectIterator; + if (maObjectIterator != sd::outliner::OutlinerContainer(this).end()) + { + ++maObjectIterator; + } + } + + mbMatchMayExist = true; + } + + // Initialize the last valid position with where the search starts so + // that it always points to a valid position. + maLastValidPosition = *sd::outliner::OutlinerContainer(this).current(); +} + +bool SdOutliner::SearchAndReplaceAll() +{ + bool bRet = true; + + // Save the current position to be restored after having replaced all + // matches. + RememberStartPosition (); + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return true; + } + + std::vector aSelections; + if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // Put the cursor to the beginning/end of the outliner. + getOutlinerView()->SetSelection (GetSearchStartPosition ()); + + // The outliner does all the work for us when we are in this mode. + SearchAndReplaceOnce(); + } + else if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() )) + { + // Disable selection change notifications during search all. + SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase(); + rSfxViewShell.setTiledSearching(true); + comphelper::ScopeGuard aGuard([&rSfxViewShell]() + { + rSfxViewShell.setTiledSearching(false); + }); + + // Go to beginning/end of document. + maObjectIterator = sd::outliner::OutlinerContainer(this).begin(); + // Switch to the first object which contains the search string. + ProvideNextTextObject(); + if( !mbStringFound ) + { + RestoreStartPosition (); + mnStartPageIndex = sal_uInt16(-1); + return true; + } + // Reset the iterator back to the beginning + maObjectIterator = sd::outliner::OutlinerContainer(this).begin(); + + // Search/replace until the end of the document is reached. + bool bFoundMatch; + do + { + bFoundMatch = ! SearchAndReplaceOnce(&aSelections); + if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && bFoundMatch && aSelections.size() == 1) + { + // Without this, RememberStartPosition() will think it already has a remembered position. + mnStartPageIndex = sal_uInt16(-1); + + RememberStartPosition(); + + // So when RestoreStartPosition() restores the first match, then spellchecker doesn't kill the selection. + bRet = false; + } + } + while (bFoundMatch); + + if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && !aSelections.empty()) + { + boost::property_tree::ptree aTree; + aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr()); + aTree.put("highlightAll", true); + + boost::property_tree::ptree aChildren; + for (const sd::SearchSelection& rSelection : aSelections) + { + boost::property_tree::ptree aChild; + aChild.put("part", OString::number(rSelection.m_nPage).getStr()); + aChild.put("rectangles", rSelection.m_aRectangles.getStr()); + aChildren.push_back(std::make_pair("", aChild)); + } + aTree.add_child("searchResultSelection", aChildren); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + OString aPayload = aStream.str().c_str(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr()); + } + } + + RestoreStartPosition (); + + if (mpSearchItem->GetCommand() == SvxSearchCmd::FIND_ALL && comphelper::LibreOfficeKit::isActive() && !bRet) + { + // Find-all, tiled rendering and we have at least one match. + OString aPayload = OString::number(mnStartPageIndex); + SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr()); + + // Emit a selection callback here: + // 1) The original one is no longer valid, as we there was a SET_PART in between + // 2) The underlying editeng will only talk about the first match till + // it doesn't support multi-selection. + std::vector aRectangles; + for (const sd::SearchSelection& rSelection : aSelections) + { + if (rSelection.m_nPage == mnStartPageIndex) + aRectangles.push_back(rSelection.m_aRectangles); + } + OString sRectangles = comphelper::string::join("; ", aRectangles); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles.getStr()); + } + + mnStartPageIndex = sal_uInt16(-1); + + return bRet; +} + +namespace +{ + +basegfx::B2DRectangle getPDFSelection(const std::unique_ptr & rVectorGraphicSearch, + const SdrObject* pObject) +{ + basegfx::B2DRectangle aSelection; + + auto const & rTextRectangles = rVectorGraphicSearch->getTextRectangles(); + if (rTextRectangles.empty()) + return aSelection; + + basegfx::B2DSize aPdfPageSizeHMM = rVectorGraphicSearch->pageSize(); + + basegfx::B2DRectangle aObjectB2DRectHMM(vcl::unotools::b2DRectangleFromRectangle(pObject->GetLogicRect())); + + // Setup coordinate conversion matrix to convert the inner PDF + // coordinates to the page relative coordinates + basegfx::B2DHomMatrix aB2DMatrix; + + aB2DMatrix.scale(aObjectB2DRectHMM.getWidth() / aPdfPageSizeHMM.getX(), + aObjectB2DRectHMM.getHeight() / aPdfPageSizeHMM.getY()); + + aB2DMatrix.translate(aObjectB2DRectHMM.getMinX(), aObjectB2DRectHMM.getMinY()); + + + for (auto const & rRectangle : rVectorGraphicSearch->getTextRectangles()) + { + basegfx::B2DRectangle aRectangle(rRectangle); + aRectangle *= aB2DMatrix; + + if (aSelection.isEmpty()) + aSelection = aRectangle; + else + aSelection.expand(aRectangle); + } + + return aSelection; +} + +} // end namespace + +void SdOutliner::sendLOKSearchResultCallback(const std::shared_ptr & pViewShell, + const OutlinerView* pOutlinerView, + std::vector* pSelections) +{ + std::vector<::tools::Rectangle> aLogicRects; + auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext(); + if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + basegfx::B2DRectangle aSelectionHMM = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj); + + tools::Rectangle aSelection(Point(aSelectionHMM.getMinX(), aSelectionHMM.getMinY()), + Size(aSelectionHMM.getWidth(), aSelectionHMM.getHeight())); + aSelection = o3tl::convert(aSelection, o3tl::Length::mm100, o3tl::Length::twip); + aLogicRects.push_back(aSelection); + } + else + { + pOutlinerView->GetSelectionRectangles(aLogicRects); + + // convert to twips if in 100thmm (seems as if LibreOfficeKit is based on twips?). Do this + // here where we have the only place needing this, *not* in ImpEditView::GetSelectionRectangles + // which makes that method unusable for others + if (pOutlinerView->GetWindow() && MapUnit::Map100thMM == pOutlinerView->GetWindow()->GetMapMode().GetMapUnit()) + { + for (tools::Rectangle& rRectangle : aLogicRects) + { + rRectangle = o3tl::convert(rRectangle, o3tl::Length::mm100, o3tl::Length::twip); + } + } + } + + std::vector aLogicRectStrings; + std::transform(aLogicRects.begin(), aLogicRects.end(), std::back_inserter(aLogicRectStrings), + [](const ::tools::Rectangle& rRectangle) + { + return rRectangle.toString(); + }); + + OString sRectangles = comphelper::string::join("; ", aLogicRectStrings); + + if (!pSelections) + { + // notify LibreOfficeKit about changed page + OString aPayload = OString::number(maCurrentPosition.mnPageIndex); + SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr()); + + // also about search result selections + boost::property_tree::ptree aTree; + aTree.put("searchString", mpSearchItem->GetSearchString().toUtf8().getStr()); + aTree.put("highlightAll", false); + + boost::property_tree::ptree aChildren; + boost::property_tree::ptree aChild; + aChild.put("part", OString::number(maCurrentPosition.mnPageIndex).getStr()); + aChild.put("rectangles", sRectangles.getStr()); + aChildren.push_back(std::make_pair("", aChild)); + aTree.add_child("searchResultSelection", aChildren); + + std::stringstream aStream; + boost::property_tree::write_json(aStream, aTree); + aPayload = aStream.str().c_str(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_RESULT_SELECTION, aPayload.getStr()); + + if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_TEXT_SELECTION, sRectangles.getStr()); + } + } + else + { + sd::SearchSelection aSelection(maCurrentPosition.mnPageIndex, sRectangles); + bool bDuplicate = !pSelections->empty() && pSelections->back() == aSelection; + if (!bDuplicate) + pSelections->push_back(aSelection); + } +} + +bool SdOutliner::SearchAndReplaceOnce(std::vector* pSelections) +{ + DetectChange (); + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + + if (!getOutlinerView() || !GetEditEngine().HasView(&getOutlinerView()->GetEditView())) + { + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(pViewShell)); + + // Perhaps the user switched to a different page/slide between searches. + // If so, reset the starting search position to the current slide like DetectChange does + if (pDrawViewShell && pDrawViewShell->GetCurPagePos() != maCurrentPosition.mnPageIndex) + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + + mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow); + } + + if (pViewShell) + { + mpView = pViewShell->GetView(); + mpWindow = pViewShell->GetActiveWindow(); + getOutlinerView()->SetWindow(mpWindow); + auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext(); + if (nullptr != dynamic_cast(pViewShell.get())) + { + sal_uLong nMatchCount = 0; + + if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + OUString const & rString = mpSearchItem->GetSearchString(); + bool bBackwards = mpSearchItem->GetBackward(); + + VectorGraphicSearchOptions aOptions; + aOptions.meStartPosition = bBackwards ? SearchStartPosition::End : SearchStartPosition::Begin; + aOptions.mbMatchCase = mpSearchItem->GetExact(); + aOptions.mbMatchWholeWord = mpSearchItem->GetWordOnly(); + + bool bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->search(rString, aOptions); + + if (bResult) + { + if (bBackwards) + bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->previous(); + else + bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->next(); + } + + if (bResult) + { + nMatchCount = 1; + + SdrPageView* pPageView = mpView->GetSdrPageView(); + mpView->UnmarkAllObj(pPageView); + + std::vector aSubSelections; + basegfx::B2DRectangle aSubSelection = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj); + if (!aSubSelection.isEmpty()) + aSubSelections.push_back(aSubSelection); + mpView->MarkObj(mpObj, pPageView, false, false, std::move(aSubSelections)); + } + else + { + rVectorGraphicSearchContext.reset(); + } + } + else + { + // When replacing we first check if there is a selection + // indicating a match. If there is then replace it. The + // following call to StartSearchAndReplace will then search for + // the next match. + if (meMode == SEARCH && mpSearchItem->GetCommand() == SvxSearchCmd::REPLACE) + { + if (getOutlinerView()->GetSelection().HasRange()) + getOutlinerView()->StartSearchAndReplace(*mpSearchItem); + } + + // Search for the next match. + if (mpSearchItem->GetCommand() != SvxSearchCmd::REPLACE_ALL) + { + nMatchCount = getOutlinerView()->StartSearchAndReplace(*mpSearchItem); + } + } + + // Go to the next text object when there have been no matches in + // the current object or the whole object has already been + // processed. + if (nMatchCount==0 || mpSearchItem->GetCommand()==SvxSearchCmd::REPLACE_ALL) + { + ProvideNextTextObject (); + + if (!mbEndOfSearch && !rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + // Remember the current position as the last one with a + // text object. + maLastValidPosition = maCurrentPosition; + + // Now that the mbEndOfSearch flag guards this block the + // following assertion and return should not be + // necessary anymore. + DBG_ASSERT(GetEditEngine().HasView(&getOutlinerView()->GetEditView() ), + "SearchAndReplace without valid view!" ); + if ( ! GetEditEngine().HasView( &getOutlinerView()->GetEditView() ) ) + { + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + return true; + } + + if (meMode == SEARCH) + getOutlinerView()->StartSearchAndReplace(*mpSearchItem); + } + } + } + else if (nullptr != dynamic_cast(pViewShell.get())) + { + mpDrawDocument->GetDocSh()->SetWaitCursor(false); + // The following loop is executed more than once only when a + // wrap around search is done. + while (true) + { + int nResult = getOutlinerView()->StartSearchAndReplace(*mpSearchItem); + if (nResult == 0) + { + if (HandleFailedSearch ()) + { + getOutlinerView()->SetSelection (GetSearchStartPosition ()); + continue; + } + } + else + mbStringFound = true; + break; + } + } + } + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + + if (pViewShell && comphelper::LibreOfficeKit::isActive() && mbStringFound) + { + sendLOKSearchResultCallback(pViewShell, getOutlinerView(), pSelections); + } + + return mbEndOfSearch; +} + +/** Try to detect whether the document or the view (shell) has changed since + the last time StartSearchAndReplace() has been called. +*/ +void SdOutliner::DetectChange() +{ + sd::outliner::IteratorPosition aPosition (maCurrentPosition); + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(pViewShell)); + + // Detect whether the view has been switched from the outside. + if (pDrawViewShell != nullptr + && (aPosition.meEditMode != pDrawViewShell->GetEditMode() + || aPosition.mePageKind != pDrawViewShell->GetPageKind())) + { + // Either the edit mode or the page kind has changed. + SetStatusEventHdl(Link()); + + SdrPageView* pPageView = mpView->GetSdrPageView(); + if (pPageView != nullptr) + mpView->UnmarkAllObj (pPageView); + mpView->SdrEndTextEdit(); + SetUpdateLayout(false); + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1) ) ); + if (meMode == SPELL) + SetPaperSize( Size(1, 1) ); + SetText(OUString(), GetParagraph(0)); + + RememberStartPosition (); + + mnPageCount = mpDrawDocument->GetSdPageCount(pDrawViewShell->GetPageKind()); + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + } + + // Detect change of the set of selected objects. If their number has + // changed start again with the first selected object. + else if (DetectSelectionChange()) + { + HandleChangedSelection (); + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + } + + // Detect change of page count. Restart search at first/last page in + // that case. + else if (aPosition.meEditMode == EditMode::Page + && mpDrawDocument->GetSdPageCount(aPosition.mePageKind) != mnPageCount) + { + // The number of pages has changed. + mnPageCount = mpDrawDocument->GetSdPageCount(aPosition.mePageKind); + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + } + else if (aPosition.meEditMode == EditMode::MasterPage + && mpDrawDocument->GetSdPageCount(aPosition.mePageKind) != mnPageCount) + { + // The number of master pages has changed. + mnPageCount = mpDrawDocument->GetSdPageCount(aPosition.mePageKind); + maObjectIterator = sd::outliner::OutlinerContainer(this).current(); + } +} + +bool SdOutliner::DetectSelectionChange() +{ + bool bSelectionHasChanged = false; + + // If mpObj is NULL then we have not yet found our first match. + // Detecting a change makes no sense. + if (mpObj != nullptr) + { + const size_t nMarkCount = mpView ? mpView->GetMarkedObjectList().GetMarkCount() : 0; + switch (nMarkCount) + { + case 0: + // The selection has changed when previously there have been + // selected objects. + bSelectionHasChanged = mbRestrictSearchToSelection; + break; + case 1: + // Check if the only selected object is not the one that we + // had selected. + if (mpView != nullptr) + { + SdrMark* pMark = mpView->GetMarkedObjectList().GetMark(0); + if (pMark != nullptr) + bSelectionHasChanged = (mpObj != pMark->GetMarkedSdrObj ()); + } + break; + default: + // We had selected exactly one object. + bSelectionHasChanged = true; + break; + } + } + + return bSelectionHasChanged; +} + +void SdOutliner::RememberStartPosition() +{ + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return; + } + + if ( mnStartPageIndex != sal_uInt16(-1) ) + return; + + if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() )) + { + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(pViewShell)); + if (pDrawViewShell != nullptr) + { + meStartViewMode = pDrawViewShell->GetPageKind(); + meStartEditMode = pDrawViewShell->GetEditMode(); + mnStartPageIndex = pDrawViewShell->GetCurPagePos(); + } + + if (mpView != nullptr) + { + mpStartEditedObject = mpView->GetTextEditObject(); + if (mpStartEditedObject != nullptr) + { + // Try to retrieve current caret position only when there is an + // edited object. + ::Outliner* pOutliner = + static_cast(mpView)->GetTextEditOutliner(); + if (pOutliner!=nullptr && pOutliner->GetViewCount()>0) + { + OutlinerView* pOutlinerView = pOutliner->GetView(0); + maStartSelection = pOutlinerView->GetSelection(); + } + } + } + } + else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // Remember the current cursor position. + OutlinerView* pView = GetView(0); + if (pView != nullptr) + pView->GetSelection(); + } + else + { + mnStartPageIndex = sal_uInt16(-1); + } +} + +void SdOutliner::RestoreStartPosition() +{ + bool bRestore = true; + // Take a negative start page index as indicator that restoring the + // start position is not requested. + if (mnStartPageIndex == sal_uInt16(-1) ) + bRestore = false; + // Don't restore when the view shell is not valid. + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if (pViewShell == nullptr) + bRestore = false; + + if (!bRestore) + return; + + if( nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() )) + { + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(pViewShell)); + SetViewMode (meStartViewMode); + if (pDrawViewShell != nullptr) + { + SetPage (meStartEditMode, mnStartPageIndex); + mpObj = mpStartEditedObject; + if (mpObj) + { + PutTextIntoOutliner(); + EnterEditMode(false); + if (getOutlinerView()) + getOutlinerView()->SetSelection(maStartSelection); + } + } + } + else if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // Set cursor to its old position. + OutlinerView* pView = GetView(0); + if (pView != nullptr) + pView->SetSelection (maStartSelection); + } +} + +namespace +{ + +bool lclIsValidTextObject(const sd::outliner::IteratorPosition& rPosition) +{ + auto* pObject = dynamic_cast< SdrTextObj* >( rPosition.mxObject.get() ); + return (pObject != nullptr) && pObject->HasText() && ! pObject->IsEmptyPresObj(); +} + +bool isValidVectorGraphicObject(const sd::outliner::IteratorPosition& rPosition) +{ + auto* pGraphicObject = dynamic_cast(rPosition.mxObject.get()); + if (pGraphicObject) + { + auto const& pVectorGraphicData = pGraphicObject->GetGraphic().getVectorGraphicData(); + if (pVectorGraphicData && VectorGraphicDataType::Pdf == pVectorGraphicData->getType()) + { + return true; + } + } + return false; +} + +} // end anonymous namespace + + +/** The main purpose of this method is to iterate over all shape objects of + the search area (current selection, current view, or whole document) + until a text object has been found that contains at least one match or + until no such object can be found anymore. These two conditions are + expressed by setting one of the flags mbFoundObject or + mbEndOfSearch to . +*/ +void SdOutliner::ProvideNextTextObject() +{ + mbEndOfSearch = false; + mbFoundObject = false; + + // reset the vector search + auto& rVectorGraphicSearchContext = mpImpl->getVectorGraphicSearchContext(); + rVectorGraphicSearchContext.reset(); + + mpView->UnmarkAllObj (mpView->GetSdrPageView()); + try + { + mpView->SdrEndTextEdit(); + } + catch (const css::uno::Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + SetUpdateLayout(false); + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1) ) ); + if (meMode == SPELL) + SetPaperSize( Size(1, 1) ); + SetText(OUString(), GetParagraph(0)); + + mpSearchSpellTextObj = nullptr; + + // Iterate until a valid text object has been found or the search ends. + do + { + mpObj = nullptr; + mpParaObj = nullptr; + + if (maObjectIterator != sd::outliner::OutlinerContainer(this).end()) + { + maCurrentPosition = *maObjectIterator; + + // LOK: do not descent to notes or master pages when searching + bool bForbiddenPage = comphelper::LibreOfficeKit::isActive() && (maCurrentPosition.mePageKind != PageKind::Standard || maCurrentPosition.meEditMode != EditMode::Page); + + rVectorGraphicSearchContext.reset(); + + if (!bForbiddenPage) + { + // Switch to the current object only if it is a valid text object. + if (lclIsValidTextObject(maCurrentPosition)) + { + // Don't set yet in case of searching: the text object may not match. + if (meMode != SEARCH) + mpObj = SetObject(maCurrentPosition); + else + mpObj = maCurrentPosition.mxObject.get(); + } + // Or if the object is a valid graphic object which contains vector graphic + else if (meMode == SEARCH && isValidVectorGraphicObject(maCurrentPosition)) + { + mpObj = maCurrentPosition.mxObject.get(); + rVectorGraphicSearchContext.mbCurrentIsVectorGraphic = true; + } + } + + // Advance to the next object + ++maObjectIterator; + + if (mpObj) + { + if (rVectorGraphicSearchContext.mbCurrentIsVectorGraphic) + { + // We know here the object is a SdrGrafObj and that it + // contains a vector graphic + auto* pGraphicObject = static_cast(mpObj); + OUString const & rString = mpSearchItem->GetSearchString(); + bool bBackwards = mpSearchItem->GetBackward(); + + VectorGraphicSearchOptions aOptions; + aOptions.meStartPosition = bBackwards ? SearchStartPosition::End : SearchStartPosition::Begin; + aOptions.mbMatchCase = mpSearchItem->GetExact(); + aOptions.mbMatchWholeWord = mpSearchItem->GetWordOnly(); + + rVectorGraphicSearchContext.mpVectorGraphicSearch = std::make_unique(pGraphicObject->GetGraphic()); + + bool bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->search(rString, aOptions); + if (bResult) + { + if (bBackwards) + bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->previous(); + else + bResult = rVectorGraphicSearchContext.mpVectorGraphicSearch->next(); + } + + if (bResult) + { + mpObj = SetObject(maCurrentPosition); + + mbStringFound = true; + mbMatchMayExist = true; + mbFoundObject = true; + + SdrPageView* pPageView = mpView->GetSdrPageView(); + mpView->UnmarkAllObj(pPageView); + + std::vector aSubSelections; + basegfx::B2DRectangle aSubSelection = getPDFSelection(rVectorGraphicSearchContext.mpVectorGraphicSearch, mpObj); + if (!aSubSelection.isEmpty()) + aSubSelections.push_back(aSubSelection); + + mpView->MarkObj(mpObj, pPageView, false, false, std::move(aSubSelections)); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + } + else + { + rVectorGraphicSearchContext.reset(); + } + } + else + { + PutTextIntoOutliner(); + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if (pViewShell != nullptr) + { + switch (meMode) + { + case SEARCH: + PrepareSearchAndReplace (); + break; + case SPELL: + PrepareSpellCheck (); + break; + case TEXT_CONVERSION: + PrepareConversion(); + break; + } + } + } + } + } + else + { + rVectorGraphicSearchContext.reset(); + + if (meMode == SEARCH) + // Instead of doing a full-blown SetObject(), which would do the same -- but would also possibly switch pages. + mbStringFound = false; + + mbEndOfSearch = true; + EndOfSearch (); + } + } + while ( ! (mbFoundObject || mbEndOfSearch)); +} + +void SdOutliner::EndOfSearch() +{ + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if ( ! pViewShell) + { + OSL_ASSERT(pViewShell); + return; + } + + // Before we display a dialog we first jump to where the last valid text + // object was found. All page and view mode switching since then was + // temporary and should not be visible to the user. + if( nullptr == dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + SetObject (maLastValidPosition); + + if (mbRestrictSearchToSelection) + ShowEndOfSearchDialog (); + else + { + // When no match has been found so far then terminate the search. + if ( ! mbMatchMayExist) + { + ShowEndOfSearchDialog (); + mbEndOfSearch = true; + } + // Ask the user whether to wrap around and continue the search or + // to terminate. + else if (meMode==TEXT_CONVERSION || ShowWrapAroundDialog ()) + { + mbMatchMayExist = false; + // Everything back to beginning (or end?) of the document. + maObjectIterator = sd::outliner::OutlinerContainer(this).begin(); + if( nullptr != dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() )) + { + // Set cursor to first character of the document. + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetSelection (GetSearchStartPosition ()); + } + + mbEndOfSearch = false; + } + else + { + // No wrap around. + mbEndOfSearch = true; + } + } +} + +void SdOutliner::ShowEndOfSearchDialog() +{ + if (meMode == SEARCH) + { + if (!mbStringFound) + { + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); + std::shared_ptr pViewShell(mpWeakViewShell.lock()); + if (pViewShell) + { + SfxViewShell& rSfxViewShell = pViewShell->GetViewShellBase(); + rSfxViewShell.libreOfficeKitViewCallback(LOK_CALLBACK_SEARCH_NOT_FOUND, mpSearchItem->GetSearchString().toUtf8().getStr()); + } + } + + // don't do anything else for search + return; + } + + OUString aString; + if (mpView->AreObjectsMarked()) + aString = SdResId(STR_END_SPELLING_OBJ); + else + aString = SdResId(STR_END_SPELLING); + + // Show the message in an info box that is modal with respect to the whole application. + weld::Window* pParent = GetMessageBoxParent(); + std::unique_ptr xInfoBox(Application::CreateMessageDialog(pParent, + VclMessageType::Info, VclButtonsType::Ok, aString)); + xInfoBox->run(); +} + +bool SdOutliner::ShowWrapAroundDialog() +{ + // Determine whether to show the dialog. + if (mpSearchItem) + { + // When searching display the dialog only for single find&replace. + const SvxSearchCmd nCommand(mpSearchItem->GetCommand()); + if (nCommand == SvxSearchCmd::REPLACE || nCommand == SvxSearchCmd::FIND) + { + if (mbDirectionIsForward) + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::End); + else + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::Start); + + return true; + } + else + return false; + } + + // show dialog only for spelling + if (meMode != SPELL) + return false; + + // The question text depends on the search direction. + bool bImpress = mpDrawDocument && mpDrawDocument->GetDocumentType() == DocumentType::Impress; + + TranslateId pStringId; + if (mbDirectionIsForward) + pStringId = bImpress ? STR_SAR_WRAP_FORWARD : STR_SAR_WRAP_FORWARD_DRAW; + else + pStringId = bImpress ? STR_SAR_WRAP_BACKWARD : STR_SAR_WRAP_BACKWARD_DRAW; + + // Pop up question box that asks the user whether to wrap around. + // The dialog is made modal with respect to the whole application. + weld::Window* pParent = GetMessageBoxParent(); + std::unique_ptr xQueryBox(Application::CreateMessageDialog(pParent, + VclMessageType::Question, VclButtonsType::YesNo, SdResId(pStringId))); + sal_uInt16 nBoxResult = xQueryBox->run(); + + return (nBoxResult == RET_YES); +} + +void SdOutliner::PutTextIntoOutliner() +{ + mpSearchSpellTextObj = dynamic_cast( mpObj ); + if ( mpSearchSpellTextObj && mpSearchSpellTextObj->HasText() && !mpSearchSpellTextObj->IsEmptyPresObj() ) + { + SdrText* pText = mpSearchSpellTextObj->getText( maCurrentPosition.mnText ); + mpParaObj = pText ? pText->GetOutlinerParaObject() : nullptr; + + if (mpParaObj != nullptr) + { + SetText(*mpParaObj); + + ClearModifyFlag(); + } + } + else + { + mpSearchSpellTextObj = nullptr; + } +} + +void SdOutliner::PrepareSpellCheck() +{ + EESpellState eState = HasSpellErrors(); + DBG_ASSERT(eState != EESpellState::NoSpeller, "No SpellChecker"); + + if (eState == EESpellState::Ok) + return; + + // When spell checking we have to test whether we have processed the + // whole document and have reached the start page again. + if (meMode == SPELL) + { + if (maSearchStartPosition == sd::outliner::Iterator()) + // Remember the position of the first text object so that we + // know when we have processed the whole document. + maSearchStartPosition = maObjectIterator; + else if (maSearchStartPosition == maObjectIterator) + { + mbEndOfSearch = true; + } + } + + EnterEditMode( false ); +} + +void SdOutliner::PrepareSearchAndReplace() +{ + if (!HasText( *mpSearchItem )) + return; + + // Set the object now that we know it matches. + mpObj = SetObject(maCurrentPosition); + + mbStringFound = true; + mbMatchMayExist = true; + + EnterEditMode(false); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + // Start search at the right end of the current object's text + // depending on the search direction. + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetSelection (GetSearchStartPosition ()); +} + +void SdOutliner::SetViewMode (PageKind ePageKind) +{ + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + std::shared_ptr pDrawViewShell( + std::dynamic_pointer_cast(pViewShell)); + if (pDrawViewShell == nullptr || ePageKind == pDrawViewShell->GetPageKind()) + return; + + // Restore old edit mode. + pDrawViewShell->ChangeEditMode(mpImpl->meOriginalEditMode, false); + + SetStatusEventHdl(Link()); + OUString sViewURL; + switch (ePageKind) + { + case PageKind::Standard: + default: + sViewURL = sd::framework::FrameworkHelper::msImpressViewURL; + break; + case PageKind::Notes: + sViewURL = sd::framework::FrameworkHelper::msNotesViewURL; + break; + case PageKind::Handout: + sViewURL = sd::framework::FrameworkHelper::msHandoutViewURL; + break; + } + // The text object iterator is destroyed when the shells are + // switched but we need it so save it and restore it afterwards. + sd::outliner::Iterator aIterator (maObjectIterator); + bool bMatchMayExist = mbMatchMayExist; + + sd::ViewShellBase& rBase = pViewShell->GetViewShellBase(); + + rtl::Reference xFuSearch; + if (pViewShell->GetView()) + xFuSearch = pViewShell->GetView()->getSearchContext().getFunctionSearch(); + + SetViewShell(std::shared_ptr()); + sd::framework::FrameworkHelper::Instance(rBase)->RequestView( + sViewURL, + sd::framework::FrameworkHelper::msCenterPaneURL); + + // Force (well, request) a synchronous update of the configuration. + // In a better world we would handle the asynchronous view update + // instead. But that would involve major restructuring of the + // Outliner code. + sd::framework::FrameworkHelper::Instance(rBase)->RequestSynchronousUpdate(); + + auto pNewViewShell = rBase.GetMainViewShell(); + SetViewShell(pNewViewShell); + if (xFuSearch.is() && pNewViewShell->GetView()) + pNewViewShell->GetView()->getSearchContext().setSearchFunction(xFuSearch); + + // Switching to another view shell has intermediatly called + // EndSpelling(). A PrepareSpelling() is pending, so call that now. + PrepareSpelling(); + + // Update the number of pages so that + // DetectChange() has the correct value to compare + // to. + mnPageCount = mpDrawDocument->GetSdPageCount(ePageKind); + + maObjectIterator = aIterator; + mbMatchMayExist = bMatchMayExist; + + // Save edit mode so that it can be restored when switching the view + // shell again. + pDrawViewShell = std::dynamic_pointer_cast(pViewShell); + OSL_ASSERT(pDrawViewShell != nullptr); + if (pDrawViewShell != nullptr) + mpImpl->meOriginalEditMode = pDrawViewShell->GetEditMode(); +} + +void SdOutliner::SetPage (EditMode eEditMode, sal_uInt16 nPageIndex) +{ + if ( ! mbRestrictSearchToSelection) + { + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + std::shared_ptr pDrawViewShell( + std::dynamic_pointer_cast(pViewShell)); + OSL_ASSERT(pDrawViewShell != nullptr); + if (pDrawViewShell != nullptr) + { + pDrawViewShell->ChangeEditMode(eEditMode, false); + pDrawViewShell->SwitchPage(nPageIndex); + } + } +} + +void SdOutliner::EnterEditMode (bool bGrabFocus) +{ + OutlinerView* pOutlinerView = getOutlinerView(); + if (!(pOutlinerView && mpSearchSpellTextObj)) + return; + + pOutlinerView->SetOutputArea( ::tools::Rectangle( Point(), Size(1, 1))); + SetPaperSize( mpSearchSpellTextObj->GetLogicRect().GetSize() ); + SdrPageView* pPV = mpView->GetSdrPageView(); + + // Make FuText the current function. + SfxUInt16Item aItem (SID_TEXTEDIT, 1); + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if (!(pViewShell && pViewShell->GetDispatcher())) + return; + + pViewShell->GetDispatcher()->ExecuteList( + SID_TEXTEDIT, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, {&aItem}); + + if (mpView->IsTextEdit()) + { + // end text edition before starting it again + mpView->SdrEndTextEdit(); + } + + // To be consistent with the usual behaviour in the Office the text + // object that is put into edit mode would have also to be selected. + // Starting the text edit mode is not enough so we do it here by + // hand. + mpView->UnmarkAllObj(pPV); + mpView->MarkObj(mpSearchSpellTextObj, pPV); + + mpSearchSpellTextObj->setActiveText(mnText); + + // Turn on the edit mode for the text object. + SetUpdateLayout(true); + mpView->SdrBeginTextEdit(mpSearchSpellTextObj, pPV, mpWindow, true, this, + pOutlinerView, true, true, bGrabFocus); + + mbFoundObject = true; +} + +ESelection SdOutliner::GetSearchStartPosition() const +{ + ESelection aPosition; + if (mbDirectionIsForward) + { + // The default constructor uses the beginning of the text as default. + aPosition = ESelection (); + } + else + { + // Retrieve the position after the last character in the last + // paragraph. + sal_Int32 nParagraphCount = GetParagraphCount(); + if (nParagraphCount == 0) + aPosition = ESelection(); + else + { + sal_Int32 nLastParagraphLength = GetEditEngine().GetTextLen ( + nParagraphCount-1); + aPosition = ESelection (nParagraphCount-1, nLastParagraphLength); + } + } + + return aPosition; +} + +bool SdOutliner::HasNoPreviousMatch() +{ + OutlinerView* pOutlinerView = getOutlinerView(); + + DBG_ASSERT (pOutlinerView!=nullptr, "outline view in SdOutliner::HasNoPreviousMatch is NULL"); + + // Detect whether the cursor stands at the beginning + // resp. at the end of the text. + return pOutlinerView->GetSelection() == GetSearchStartPosition(); +} + +bool SdOutliner::HandleFailedSearch() +{ + bool bContinueSearch = false; + + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView && mpSearchItem) + { + // Detect whether there is/may be a prior match. If there is then + // ask the user whether to wrap around. Otherwise tell the user + // that there is no match. + if (HasNoPreviousMatch ()) + { + // No match found in the whole presentation. + SvxSearchDialogWrapper::SetSearchLabel(SearchLabel::NotFound); + } + + else + { + // No further matches found. Ask the user whether to wrap + // around and start again. + bContinueSearch = ShowWrapAroundDialog(); + } + } + + return bContinueSearch; +} + +SdrObject* SdOutliner::SetObject ( + const sd::outliner::IteratorPosition& rPosition) +{ + SetViewMode (rPosition.mePageKind); + SetPage (rPosition.meEditMode, static_cast(rPosition.mnPageIndex)); + mnText = rPosition.mnText; + return rPosition.mxObject.get(); +} + +void SdOutliner::SetViewShell (const std::shared_ptr& rpViewShell) +{ + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if (pViewShell == rpViewShell) + return; + + // Set the new view shell. + mpWeakViewShell = rpViewShell; + // When the outline view is not owned by us then we have to clear + // that pointer so that the current one for the new view shell will + // be used (in ProvideOutlinerView). + if (rpViewShell) + { + mpView = rpViewShell->GetView(); + + mpWindow = rpViewShell->GetActiveWindow(); + + mpImpl->ProvideOutlinerView(*this, rpViewShell, mpWindow); + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + pOutlinerView->SetWindow(mpWindow); + } + else + { + mpView = nullptr; + mpWindow = nullptr; + } +} + +void SdOutliner::HandleChangedSelection() +{ + maMarkListCopy.clear(); + mbRestrictSearchToSelection = mpView->AreObjectsMarked(); + if (!mbRestrictSearchToSelection) + return; + + // Make a copy of the current mark list. + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + if (nCount > 0) + { + maMarkListCopy.clear(); + maMarkListCopy.reserve (nCount); + for (size_t i=0; iGetMarkedSdrObj ()); + } + else + // No marked object. Is this case possible? + mbRestrictSearchToSelection = false; +} + +void SdOutliner::StartConversion( LanguageType nSourceLanguage, LanguageType nTargetLanguage, + const vcl::Font *pTargetFont, sal_Int32 nOptions, bool bIsInteractive ) +{ + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + bool bMultiDoc = nullptr != dynamic_cast< const sd::DrawViewShell *>( pViewShell.get() ); + + meMode = TEXT_CONVERSION; + mbDirectionIsForward = true; + mpSearchItem.reset(); + mnConversionLanguage = nSourceLanguage; + + BeginConversion(); + + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + { + pOutlinerView->StartTextConversion( + GetMessageBoxParent(), + nSourceLanguage, + nTargetLanguage, + pTargetFont, + nOptions, + bIsInteractive, + bMultiDoc); + } + + EndConversion(); +} + +/** Prepare to do a text conversion on the current text object. This + includes putting it into edit mode. +*/ +void SdOutliner::PrepareConversion() +{ + SetUpdateLayout(true); + if( HasConvertibleTextPortion( mnConversionLanguage ) ) + { + SetUpdateLayout(false); + mbStringFound = true; + mbMatchMayExist = true; + + EnterEditMode(true); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + // Start search at the right end of the current object's text + // depending on the search direction. + } + else + { + SetUpdateLayout(false); + } +} + +void SdOutliner::BeginConversion() +{ + SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + + sd::ViewShellBase* pBase = getViewShellBase(); + if (pBase != nullptr) + SetViewShell (pBase->GetMainViewShell()); + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if (pViewShell) + { + mbStringFound = false; + + // Supposed that we are not located at the very beginning/end of the + // document then there may be a match in the document prior/after + // the current position. + mbMatchMayExist = true; + + maObjectIterator = sd::outliner::Iterator(); + maSearchStartPosition = sd::outliner::Iterator(); + RememberStartPosition(); + + mpImpl->ProvideOutlinerView(*this, pViewShell, mpWindow); + + HandleChangedSelection (); + } + ClearModifyFlag(); +} + +void SdOutliner::EndConversion() +{ + EndSpelling(); +} + +bool SdOutliner::ConvertNextDocument() +{ + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + if (dynamic_cast< const sd::OutlineViewShell *>( pViewShell.get() ) ) + return false; + + mpDrawDocument->GetDocSh()->SetWaitCursor( true ); + + Initialize ( true ); + + OutlinerView* pOutlinerView = getOutlinerView(); + if (pOutlinerView != nullptr) + { + mpWindow = pViewShell->GetActiveWindow(); + pOutlinerView->SetWindow(mpWindow); + } + ProvideNextTextObject (); + + mpDrawDocument->GetDocSh()->SetWaitCursor( false ); + ClearModifyFlag(); + + // for text conversion we automatically wrap around one + // time and stop at the start shape + if( mpFirstObj ) + { + if( (mnText == 0) && (mpFirstObj == mpObj) ) + return false; + } + else + { + mpFirstObj = mpObj; + } + + return !mbEndOfSearch; +} + +weld::Window* SdOutliner::GetMessageBoxParent() +{ + // We assume that the parent of the given message box is NULL, i.e. it is + // modal with respect to the top application window. However, this + // does not affect the search dialog. Therefore we have to lock it here + // while the message box is being shown. We also have to take into + // account that we are called during a spell check and the search dialog + // is not available. + weld::Window* pSearchDialog = nullptr; + SfxChildWindow* pChildWindow = nullptr; + switch (meMode) + { + case SEARCH: + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + pChildWindow = pViewFrm->GetChildWindow( + SvxSearchDialogWrapper::GetChildWindowId()); + break; + + case SPELL: + if (SfxViewFrame* pViewFrm = SfxViewFrame::Current()) + pChildWindow = pViewFrm->GetChildWindow( + sd::SpellDialogChildWindow::GetChildWindowId()); + break; + + case TEXT_CONVERSION: + // There should no messages boxes be displayed while doing the + // hangul hanja conversion. + break; + } + + if (pChildWindow != nullptr) + { + auto xController = pChildWindow->GetController(); + pSearchDialog = xController ? xController->getDialog() : nullptr; + } + + if (pSearchDialog) + return pSearchDialog; + + std::shared_ptr pViewShell (mpWeakViewShell.lock()); + auto pWin = pViewShell->GetActiveWindow(); + return pWin ? pWin->GetFrameWeld() : nullptr; +} + +//===== SdOutliner::Implementation ============================================== + +SdOutliner::Implementation::Implementation() + : meOriginalEditMode(EditMode::Page), + mbOwnOutlineView(false), + mpOutlineView(nullptr) +{ +} + +SdOutliner::Implementation::~Implementation() +{ + if (mbOwnOutlineView && mpOutlineView!=nullptr) + { + mpOutlineView->SetWindow(nullptr); + delete mpOutlineView; + mpOutlineView = nullptr; + } +} + +/** We try to create a new OutlinerView only when there is none available, + either from an OutlinerViewShell or a previous call to + ProvideOutlinerView(). This is necessary to support the spell checker + which can not cope with exchanging the OutlinerView. +*/ +void SdOutliner::Implementation::ProvideOutlinerView ( + Outliner& rOutliner, + const std::shared_ptr& rpViewShell, + vcl::Window* pWindow) +{ + if (rpViewShell == nullptr) + return; + + switch (rpViewShell->GetShellType()) + { + case sd::ViewShell::ST_DRAW: + case sd::ViewShell::ST_IMPRESS: + case sd::ViewShell::ST_NOTES: + case sd::ViewShell::ST_HANDOUT: + { + // Create a new outline view to do the search on. + bool bInsert = false; + if (mpOutlineView != nullptr && !mbOwnOutlineView) + mpOutlineView = nullptr; + + if (mpOutlineView == nullptr || !rOutliner.GetEditEngine().HasView(&mpOutlineView->GetEditView())) + { + delete mpOutlineView; + mpOutlineView = new OutlinerView(&rOutliner, pWindow); + mbOwnOutlineView = true; + bInsert = true; + } + else + mpOutlineView->SetWindow(pWindow); + + EVControlBits nStat = mpOutlineView->GetControlWord(); + nStat &= ~EVControlBits::AUTOSCROLL; + mpOutlineView->SetControlWord(nStat); + + if (bInsert) + rOutliner.InsertView( mpOutlineView ); + + rOutliner.SetUpdateLayout(false); + mpOutlineView->SetOutputArea (::tools::Rectangle (Point(), Size(1, 1))); + rOutliner.SetPaperSize( Size(1, 1) ); + rOutliner.SetText(OUString(), rOutliner.GetParagraph(0)); + + meOriginalEditMode = + std::static_pointer_cast(rpViewShell)->GetEditMode(); + } + break; + + case sd::ViewShell::ST_OUTLINE: + { + if (mpOutlineView!=nullptr && mbOwnOutlineView) + delete mpOutlineView; + mpOutlineView = rOutliner.GetView(0); + mbOwnOutlineView = false; + } + break; + + default: + case sd::ViewShell::ST_NONE: + case sd::ViewShell::ST_PRESENTATION: + // Ignored + break; + } +} + +void SdOutliner::Implementation::ReleaseOutlinerView() +{ + if (mbOwnOutlineView) + { + OutlinerView* pView = mpOutlineView; + mpOutlineView = nullptr; + mbOwnOutlineView = false; + if (pView != nullptr) + { + pView->SetWindow(nullptr); + delete pView; + } + } + else + { + mpOutlineView = nullptr; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/OutlinerIterator.cxx b/sd/source/ui/view/OutlinerIterator.cxx new file mode 100644 index 000000000..8cdb29330 --- /dev/null +++ b/sd/source/ui/view/OutlinerIterator.cxx @@ -0,0 +1,798 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sd::outliner { + +//===== IteratorPosition ====================================================== + +IteratorPosition::IteratorPosition() +: mnText(0) +, mnPageIndex(-1) +, mePageKind(PageKind::Standard) +, meEditMode(EditMode::Page) +{ +} + +bool IteratorPosition::operator== (const IteratorPosition& aPosition) const +{ + return mxObject.get() == aPosition.mxObject.get() + && mnText == aPosition.mnText + && mnPageIndex == aPosition.mnPageIndex + && mePageKind == aPosition.mePageKind + && meEditMode == aPosition.meEditMode; +} + +//===== Iterator ============================================================== + +Iterator::Iterator() +{ +} + +Iterator::Iterator (const Iterator& rIterator) + : mxIterator(rIterator.mxIterator ? rIterator.mxIterator->Clone() : nullptr) +{ +} + +Iterator::Iterator(Iterator&& rIterator) noexcept + : mxIterator(std::move(rIterator.mxIterator)) +{ +} + +Iterator::Iterator (std::unique_ptr pObject) + : mxIterator(std::move(pObject)) +{ +} + +Iterator::~Iterator() +{ +} + +Iterator& Iterator::operator= (const Iterator& rIterator) +{ + if (this != &rIterator) + { + if (rIterator.mxIterator) + mxIterator.reset(rIterator.mxIterator->Clone()); + else + mxIterator.reset(); + } + return *this; +} + +Iterator& Iterator::operator=(Iterator&& rIterator) noexcept +{ + mxIterator = std::move(rIterator.mxIterator); + return *this; +} + +const IteratorPosition& Iterator::operator* () const +{ + DBG_ASSERT (mxIterator, "::sd::outliner::Iterator::operator* : missing implementation object"); + return mxIterator->GetPosition(); +} + +Iterator& Iterator::operator++ () +{ + if (mxIterator) + mxIterator->GotoNextText(); + return *this; +} + +bool Iterator::operator== (const Iterator& rIterator) const +{ + if (!mxIterator || !rIterator.mxIterator) + return mxIterator.get() == rIterator.mxIterator.get(); + else + return *mxIterator == *rIterator.mxIterator; +} + +bool Iterator::operator!= (const Iterator& rIterator) const +{ + return ! operator==(rIterator); +} + +void Iterator::Reverse() +{ + if (mxIterator) + mxIterator->Reverse(); +} + +//===== IteratorFactory ======================================================= + +OutlinerContainer::OutlinerContainer (SdOutliner* pOutliner) +: mpOutliner(pOutliner) +{ +} + +Iterator OutlinerContainer::begin() +{ + return CreateIterator (BEGIN); +} + +Iterator OutlinerContainer::end() +{ + return CreateIterator (END); +} + +Iterator OutlinerContainer::current() +{ + return CreateIterator (CURRENT); +} + +Iterator OutlinerContainer::CreateIterator (IteratorLocation aLocation) +{ + // Decide on certain features of the outliner which kind of iterator to + // use. + if (mpOutliner->mbRestrictSearchToSelection) + // There is a selection. Search only in this. + return CreateSelectionIterator ( + mpOutliner->maMarkListCopy, + mpOutliner->mpDrawDocument, + mpOutliner->mpWeakViewShell.lock(), + mpOutliner->mbDirectionIsForward, + aLocation); + else + // Search in the whole document. + return CreateDocumentIterator ( + mpOutliner->mpDrawDocument, + mpOutliner->mpWeakViewShell.lock(), + mpOutliner->mbDirectionIsForward, + aLocation); +} + +Iterator OutlinerContainer::CreateSelectionIterator ( + const ::std::vector<::tools::WeakReference>& rObjectList, + SdDrawDocument* pDocument, + const std::shared_ptr& rpViewShell, + bool bDirectionIsForward, + IteratorLocation aLocation) +{ + OSL_ASSERT(rpViewShell); + + sal_Int32 nObjectIndex; + + if (bDirectionIsForward) + switch (aLocation) + { + case CURRENT: + case BEGIN: + default: + nObjectIndex = 0; + break; + case END: + nObjectIndex = rObjectList.size(); + break; + } + else + switch (aLocation) + { + case CURRENT: + case BEGIN: + default: + nObjectIndex = rObjectList.size()-1; + break; + case END: + nObjectIndex = -1; + break; + } + + return Iterator (std::make_unique ( + rObjectList, nObjectIndex, pDocument, rpViewShell, bDirectionIsForward)); +} + +Iterator OutlinerContainer::CreateDocumentIterator ( + SdDrawDocument* pDocument, + const std::shared_ptr& rpViewShell, + bool bDirectionIsForward, + IteratorLocation aLocation) +{ + OSL_ASSERT(rpViewShell); + + PageKind ePageKind; + EditMode eEditMode; + + switch (aLocation) + { + case BEGIN: + default: + if (bDirectionIsForward) + { + ePageKind = PageKind::Standard; + eEditMode = EditMode::Page; + } + else + { + ePageKind = PageKind::Handout; + eEditMode = EditMode::MasterPage; + } + break; + + case END: + if (bDirectionIsForward) + { + ePageKind = PageKind::Handout; + eEditMode = EditMode::MasterPage; + } + else + { + ePageKind = PageKind::Standard; + eEditMode = EditMode::Page; + } + break; + + case CURRENT: + const std::shared_ptr pDrawViewShell( + std::dynamic_pointer_cast(rpViewShell)); + if (pDrawViewShell) + { + ePageKind = pDrawViewShell->GetPageKind(); + eEditMode = pDrawViewShell->GetEditMode(); + } + else + { + ePageKind = PageKind::Standard; + eEditMode = EditMode::Page; + } + break; + } + + sal_Int32 nPageIndex = GetPageIndex (pDocument, rpViewShell, + ePageKind, eEditMode, bDirectionIsForward, aLocation); + + return Iterator ( + std::make_unique (nPageIndex, ePageKind, eEditMode, + pDocument, rpViewShell, bDirectionIsForward)); +} + +sal_Int32 OutlinerContainer::GetPageIndex ( + SdDrawDocument const * pDocument, + const std::shared_ptr& rpViewShell, + PageKind ePageKind, + EditMode eEditMode, + bool bDirectionIsForward, + IteratorLocation aLocation) +{ + OSL_ASSERT(rpViewShell); + + sal_Int32 nPageIndex; + sal_Int32 nPageCount; + + const std::shared_ptr pDrawViewShell( + std::dynamic_pointer_cast(rpViewShell)); + + switch (eEditMode) + { + case EditMode::Page: + nPageCount = pDocument->GetSdPageCount (ePageKind); + break; + case EditMode::MasterPage: + nPageCount = pDocument->GetMasterSdPageCount(ePageKind); + break; + default: + nPageCount = 0; + } + + switch (aLocation) + { + case CURRENT: + if (pDrawViewShell) + nPageIndex = pDrawViewShell->GetCurPagePos(); + else + { + const SdPage* pPage = rpViewShell->GetActualPage(); + if (pPage != nullptr) + nPageIndex = (pPage->GetPageNum()-1)/2; + else + nPageIndex = 0; + } + break; + + case BEGIN: + default: + if (bDirectionIsForward) + nPageIndex = 0; + else + nPageIndex = nPageCount-1; + break; + + case END: + if (bDirectionIsForward) + nPageIndex = nPageCount; + else + nPageIndex = -1; + break; + } + + return nPageIndex; +} + +//===== IteratorImplBase ==================================================== + +IteratorImplBase::IteratorImplBase(SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward) +: mpDocument (pDocument) +, mpViewShellWeak (rpViewShellWeak) +, mbDirectionIsForward (bDirectionIsForward) +{ + std::shared_ptr pDrawViewShell; + if ( ! mpViewShellWeak.expired()) + pDrawViewShell = std::dynamic_pointer_cast(rpViewShellWeak.lock()); + + if (pDrawViewShell) + { + maPosition.mePageKind = pDrawViewShell->GetPageKind(); + maPosition.meEditMode = pDrawViewShell->GetEditMode(); + } + else + { + maPosition.mePageKind = PageKind::Standard; + maPosition.meEditMode = EditMode::Page; + } +} + +IteratorImplBase::IteratorImplBase( SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward, PageKind ePageKind, EditMode eEditMode) +: mpDocument (pDocument) +, mpViewShellWeak (rpViewShellWeak) +, mbDirectionIsForward (bDirectionIsForward) +{ + maPosition.mePageKind = ePageKind; + maPosition.meEditMode = eEditMode; +} + +IteratorImplBase::~IteratorImplBase() +{} + +bool IteratorImplBase::operator== (const IteratorImplBase& rIterator) const +{ + return maPosition == rIterator.maPosition; +} + +bool IteratorImplBase::IsEqualSelection(const IteratorImplBase& rIterator) const +{ + // When this method is executed instead of the ones from derived classes + // then the argument is of another type then the object itself. In this + // just compare the position objects. + return maPosition == rIterator.maPosition; +} + +const IteratorPosition& IteratorImplBase::GetPosition() +{ + return maPosition; +} + +IteratorImplBase* IteratorImplBase::Clone (IteratorImplBase* pObject) const +{ + if (pObject != nullptr) + { + pObject->maPosition = maPosition; + pObject->mpDocument = mpDocument; + pObject->mpViewShellWeak = mpViewShellWeak; + pObject->mbDirectionIsForward = mbDirectionIsForward; + } + return pObject; +} + +void IteratorImplBase::Reverse() +{ + mbDirectionIsForward = ! mbDirectionIsForward; +} + +//===== SelectionIteratorImpl =========================================== + +SelectionIteratorImpl::SelectionIteratorImpl ( + const ::std::vector<::tools::WeakReference>& rObjectList, + sal_Int32 nObjectIndex, + SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward) + : IteratorImplBase (pDocument, rpViewShellWeak, bDirectionIsForward), + mrObjectList(rObjectList), + mnObjectIndex(nObjectIndex) +{ +} + +SelectionIteratorImpl::~SelectionIteratorImpl() +{} + +IteratorImplBase* SelectionIteratorImpl::Clone (IteratorImplBase* pObject) const +{ + SelectionIteratorImpl* pIterator = static_cast(pObject); + if (pIterator == nullptr) + pIterator = new SelectionIteratorImpl ( + mrObjectList, mnObjectIndex, mpDocument, mpViewShellWeak, mbDirectionIsForward); + return pIterator; +} + +void SelectionIteratorImpl::GotoNextText() +{ + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( mrObjectList.at(mnObjectIndex).get() ); + if (mbDirectionIsForward) + { + if( pTextObj ) + { + ++maPosition.mnText; + if( maPosition.mnText >= pTextObj->getTextCount() ) + { + maPosition.mnText = 0; + ++mnObjectIndex; + } + } + else + { + ++mnObjectIndex; + } + } + else + { + if( pTextObj ) + { + --maPosition.mnText; + if( maPosition.mnText < 0 ) + { + maPosition.mnText = -1; + --mnObjectIndex; + } + } + else + { + --mnObjectIndex; + maPosition.mnText = -1; + } + + if( (maPosition.mnText == -1) && (mnObjectIndex >= 0) ) + { + pTextObj = dynamic_cast< SdrTextObj* >( mrObjectList.at(mnObjectIndex).get() ); + if( pTextObj ) + maPosition.mnText = pTextObj->getTextCount() - 1; + } + + if( maPosition.mnText == -1 ) + maPosition.mnText = 0; + } +} + +const IteratorPosition& SelectionIteratorImpl::GetPosition() +{ + maPosition.mxObject = mrObjectList.at(mnObjectIndex); + + return maPosition; +} + +bool SelectionIteratorImpl::operator== (const IteratorImplBase& rIterator) const +{ + return rIterator.IsEqualSelection(*this); +} + +bool SelectionIteratorImpl::IsEqualSelection(const IteratorImplBase& rIterator) const +{ + const SelectionIteratorImpl* pSelectionIterator = + static_cast(&rIterator); + return mpDocument == pSelectionIterator->mpDocument + && mnObjectIndex == pSelectionIterator->mnObjectIndex; +} + +//===== ViewIteratorImpl ================================================ + +ViewIteratorImpl::ViewIteratorImpl ( + sal_Int32 nPageIndex, + SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward) + : IteratorImplBase (pDocument, rpViewShellWeak, bDirectionIsForward), + mbPageChangeOccurred(false), + mpPage(nullptr) +{ + SetPage (nPageIndex); +} + +ViewIteratorImpl::ViewIteratorImpl ( + sal_Int32 nPageIndex, + SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward, + PageKind ePageKind, + EditMode eEditMode) + : IteratorImplBase (pDocument, rpViewShellWeak, bDirectionIsForward, ePageKind, eEditMode), + mbPageChangeOccurred(false), + mpPage(nullptr) +{ + SetPage (nPageIndex); +} + +ViewIteratorImpl::~ViewIteratorImpl() +{ +} + +IteratorImplBase* ViewIteratorImpl::Clone (IteratorImplBase* pObject) const +{ + + ViewIteratorImpl* pIterator = static_cast(pObject); + if (pIterator == nullptr) + pIterator = new ViewIteratorImpl ( + maPosition.mnPageIndex, mpDocument, mpViewShellWeak, mbDirectionIsForward); + + IteratorImplBase::Clone (pObject); + + if (mpObjectIterator != nullptr) + { + pIterator->mpObjectIterator.reset( new SdrObjListIter(mpPage, SdrIterMode::DeepNoGroups, !mbDirectionIsForward) ); + + // No direct way to set the object iterator to the current object. + pIterator->maPosition.mxObject.reset(nullptr); + while (pIterator->mpObjectIterator->IsMore() && pIterator->maPosition.mxObject!=maPosition.mxObject) + pIterator->maPosition.mxObject.reset(pIterator->mpObjectIterator->Next()); + } + else + pIterator->mpObjectIterator.reset(); + + return pIterator; +} + +void ViewIteratorImpl::GotoNextText() +{ + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( maPosition.mxObject.get() ); + if( pTextObj ) + { + if (mbDirectionIsForward) + { + ++maPosition.mnText; + if( maPosition.mnText < pTextObj->getTextCount() ) + return; + } + else + { + --maPosition.mnText; + if( maPosition.mnText >= 0 ) + return; + } + } + + if (mpObjectIterator != nullptr && mpObjectIterator->IsMore()) + maPosition.mxObject.reset(mpObjectIterator->Next()); + else + maPosition.mxObject.reset(nullptr); + + if (!maPosition.mxObject.is() ) + { + if (mbDirectionIsForward) + SetPage (maPosition.mnPageIndex+1); + else + SetPage (maPosition.mnPageIndex-1); + + if (mpPage != nullptr) + mpObjectIterator.reset( new SdrObjListIter(mpPage, SdrIterMode::DeepNoGroups, !mbDirectionIsForward) ); + if (mpObjectIterator!=nullptr && mpObjectIterator->IsMore()) + maPosition.mxObject.reset(mpObjectIterator->Next()); + else + maPosition.mxObject.reset(nullptr); + } + + maPosition.mnText = 0; + if( !mbDirectionIsForward && maPosition.mxObject.is() ) + { + pTextObj = dynamic_cast< SdrTextObj* >( maPosition.mxObject.get() ); + if( pTextObj ) + maPosition.mnText = pTextObj->getTextCount() - 1; + } +} + +void ViewIteratorImpl::SetPage (sal_Int32 nPageIndex) +{ + mbPageChangeOccurred = (maPosition.mnPageIndex!=nPageIndex); + if (mbPageChangeOccurred) + { + maPosition.mnPageIndex = nPageIndex; + + sal_Int32 nPageCount; + if (maPosition.meEditMode == EditMode::Page) + nPageCount = mpDocument->GetSdPageCount(maPosition.mePageKind); + else + nPageCount = mpDocument->GetMasterSdPageCount( + maPosition.mePageKind); + + // Get page pointer. Here we have three cases: regular pages, + // master pages and invalid page indices. The later ones are not + // errors but the effect of the iterator advancing to the next page + // and going past the last one. This dropping of the rim at the far + // side is detected here and has to be reacted to by the caller. + if (nPageIndex>=0 && nPageIndex < nPageCount) + { + if (maPosition.meEditMode == EditMode::Page) + mpPage = mpDocument->GetSdPage ( + static_cast(nPageIndex), + maPosition.mePageKind); + else + mpPage = mpDocument->GetMasterSdPage ( + static_cast(nPageIndex), + maPosition.mePageKind); + } + else + mpPage = nullptr; + } + + // Set up object list iterator. + if (mpPage != nullptr) + mpObjectIterator.reset( new SdrObjListIter(mpPage, SdrIterMode::DeepNoGroups, ! mbDirectionIsForward) ); + else + mpObjectIterator.reset(); + + // Get object pointer. + if (mpObjectIterator!=nullptr && mpObjectIterator->IsMore()) + maPosition.mxObject.reset( mpObjectIterator->Next() ); + else + maPosition.mxObject.reset(nullptr); + + maPosition.mnText = 0; + if( !mbDirectionIsForward && maPosition.mxObject.is() ) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( maPosition.mxObject.get() ); + if( pTextObj ) + maPosition.mnText = pTextObj->getTextCount() - 1; + } + +} + +void ViewIteratorImpl::Reverse() +{ + IteratorImplBase::Reverse (); + + // Create reversed object list iterator. + if (mpPage != nullptr) + mpObjectIterator.reset( new SdrObjListIter(mpPage, SdrIterMode::DeepNoGroups, ! mbDirectionIsForward) ); + else + mpObjectIterator.reset(); + + // Move iterator to the current object. + ::tools::WeakReference xObject = std::move(maPosition.mxObject); + + if (!mpObjectIterator) + return; + + while (mpObjectIterator->IsMore() && maPosition.mxObject != xObject) + maPosition.mxObject.reset(mpObjectIterator->Next()); +} + +//===== DocumentIteratorImpl ============================================ + +DocumentIteratorImpl::DocumentIteratorImpl ( + sal_Int32 nPageIndex, + PageKind ePageKind, EditMode eEditMode, + SdDrawDocument* pDocument, + const std::weak_ptr& rpViewShellWeak, + bool bDirectionIsForward) + : ViewIteratorImpl (nPageIndex, pDocument, rpViewShellWeak, bDirectionIsForward, + ePageKind, eEditMode) +{ + if (eEditMode == EditMode::Page) + mnPageCount = pDocument->GetSdPageCount (ePageKind); + else + mnPageCount = pDocument->GetMasterSdPageCount(ePageKind); +} + +DocumentIteratorImpl::~DocumentIteratorImpl() +{} + +IteratorImplBase* DocumentIteratorImpl::Clone (IteratorImplBase* pObject) const +{ + DocumentIteratorImpl* pIterator = static_cast(pObject); + if (pIterator == nullptr) + pIterator = new DocumentIteratorImpl ( + maPosition.mnPageIndex, maPosition.mePageKind, maPosition.meEditMode, + mpDocument, mpViewShellWeak, mbDirectionIsForward); + // Finish the cloning. + return ViewIteratorImpl::Clone (pIterator); +} + +void DocumentIteratorImpl::GotoNextText() +{ + bool bSetToOnePastLastPage = false; + bool bViewChanged = false; + + ViewIteratorImpl::GotoNextText(); + + if (mbDirectionIsForward) + { + if (maPosition.mnPageIndex >= mnPageCount) + { + // Switch to master page. + if (maPosition.meEditMode == EditMode::Page) + { + maPosition.meEditMode = EditMode::MasterPage; + SetPage (0); + } + + // Switch to next view mode. + else + { + if (maPosition.mePageKind == PageKind::Handout) + // Not really necessary but makes things more clear. + bSetToOnePastLastPage = true; + else + { + maPosition.meEditMode = EditMode::Page; + if (maPosition.mePageKind == PageKind::Standard) + maPosition.mePageKind = PageKind::Notes; + else if (maPosition.mePageKind == PageKind::Notes) + maPosition.mePageKind = PageKind::Handout; + SetPage (0); + } + } + bViewChanged = true; + } + } + else + if (maPosition.mnPageIndex < 0) + { + // Switch from master pages to draw pages. + if (maPosition.meEditMode == EditMode::MasterPage) + { + maPosition.meEditMode = EditMode::Page; + bSetToOnePastLastPage = true; + } + + // Switch to previous view mode. + else + { + if (maPosition.mePageKind == PageKind::Standard) + SetPage (-1); + else + { + maPosition.meEditMode = EditMode::MasterPage; + if (maPosition.mePageKind == PageKind::Handout) + maPosition.mePageKind = PageKind::Notes; + else if (maPosition.mePageKind == PageKind::Notes) + maPosition.mePageKind = PageKind::Standard; + bSetToOnePastLastPage = true; + } + } + bViewChanged = true; + } + + if (!bViewChanged) + return; + + // Get new page count; + sal_Int32 nPageCount; + if (maPosition.meEditMode == EditMode::Page) + nPageCount = mpDocument->GetSdPageCount (maPosition.mePageKind); + else + nPageCount = mpDocument->GetMasterSdPageCount(maPosition.mePageKind); + + // Now that we know the number of pages we can set the current page index. + if (bSetToOnePastLastPage) + SetPage (nPageCount); +} + +} // end of namespace ::sd::outliner + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/PresentationViewShellBase.cxx b/sd/source/ui/view/PresentationViewShellBase.cxx new file mode 100644 index 000000000..789afbbdd --- /dev/null +++ b/sd/source/ui/view/PresentationViewShellBase.cxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace sd { + +class DrawDocShell; + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new PresentationViewShellBase object has been constructed. + +SfxViewFactory* PresentationViewShellBase::s_pFactory; +SfxViewShell* PresentationViewShellBase::CreateInstance ( + SfxViewFrame *_pFrame, SfxViewShell *pOldView) +{ + PresentationViewShellBase* pBase = + new PresentationViewShellBase(_pFrame, pOldView); + pBase->LateInit(framework::FrameworkHelper::msPresentationViewURL); + return pBase; +} +void PresentationViewShellBase::RegisterFactory( SfxInterfaceId nPrio ) +{ + s_pFactory = new SfxViewFactory( + &CreateInstance,nPrio,"FullScreenPresentation"); + InitFactory(); +} +void PresentationViewShellBase::InitFactory() +{ + SFX_VIEW_REGISTRATION(DrawDocShell); +} + +PresentationViewShellBase::PresentationViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell* pOldShell) + : ViewShellBase (_pFrame, pOldShell) +{ + // Hide the automatic (non-context sensitive) tool bars. + Reference xFrameSet ( + _pFrame->GetFrame().GetFrameInterface(), + UNO_QUERY); + if (xFrameSet.is()) + { + Reference xLayouterSet(xFrameSet->getPropertyValue("LayoutManager"), UNO_QUERY); + if (xLayouterSet.is()) + { + xLayouterSet->setPropertyValue("AutomaticToolbars", Any(false)); + } + } +} + +PresentationViewShellBase::~PresentationViewShellBase() +{ +} + +void PresentationViewShellBase::InitializeFramework() +{ + css::uno::Reference + xController (GetController()); + sd::framework::PresentationModule::Initialize(xController); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/SlideSorterViewShellBase.cxx b/sd/source/ui/view/SlideSorterViewShellBase.cxx new file mode 100644 index 000000000..ecf679c98 --- /dev/null +++ b/sd/source/ui/view/SlideSorterViewShellBase.cxx @@ -0,0 +1,68 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +namespace sd { + +class DrawDocShell; + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new SlideSorterViewShellBase object has been constructed. + +SfxViewFactory* SlideSorterViewShellBase::s_pFactory; +SfxViewShell* SlideSorterViewShellBase::CreateInstance ( + SfxViewFrame *pFrame, SfxViewShell *pOldView) +{ + SlideSorterViewShellBase* pBase = new SlideSorterViewShellBase(pFrame, pOldView); + pBase->LateInit(framework::FrameworkHelper::msSlideSorterURL); + return pBase; +} + +void SlideSorterViewShellBase::RegisterFactory( SfxInterfaceId nPrio ) +{ + s_pFactory = new SfxViewFactory(&CreateInstance,nPrio,"SlideSorter"); + InitFactory(); +} + +void SlideSorterViewShellBase::InitFactory() +{ + SFX_VIEW_REGISTRATION(DrawDocShell); +} + +SlideSorterViewShellBase::SlideSorterViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell* pOldShell) + : ImpressViewShellBase (_pFrame, pOldShell) +{ +} + +SlideSorterViewShellBase::~SlideSorterViewShellBase() +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ToolBarManager.cxx b/sd/source/ui/view/ToolBarManager.cxx new file mode 100644 index 000000000..0bb0f9528 --- /dev/null +++ b/sd/source/ui/view/ToolBarManager.cxx @@ -0,0 +1,1375 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +namespace { + +using namespace sd; + +class ToolBarRules; + +/** Lock of the frame::XLayoutManager. +*/ +class LayouterLock +{ + Reference mxLayouter; +public: + explicit LayouterLock (const Reference& rxLayouter); + ~LayouterLock(); + bool is() const { return mxLayouter.is(); } +}; + +/** Store a list of tool bars for each of the tool bar groups. From + this the list of requested tool bars is built. +*/ +class ToolBarList +{ +public: + ToolBarList(); + + void ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup); + void AddToolBar (sd::ToolBarManager::ToolBarGroup eGroup, const OUString& rsName); + bool RemoveToolBar (sd::ToolBarManager::ToolBarGroup eGroup, const OUString& rsName); + + void GetToolBarsToActivate (std::vector& rToolBars) const; + void GetToolBarsToDeactivate (std::vector& rToolBars) const; + + void MarkToolBarAsActive (const OUString& rsName); + void MarkToolBarAsNotActive (const OUString& rsName); + void MarkAllToolBarsAsNotActive(); + +private: + typedef ::std::map > Groups; + Groups maGroups; + std::vector maActiveToolBars; + + void MakeRequestedToolBarList (std::vector& rToolBars) const; +}; + +/** Manage tool bars that are implemented as sub shells of a view shell. + The typical procedure of updating the sub shells of a view shell is to + rebuild a list of sub shells that the caller would like to have active. + The methods ClearGroup() and AddShellId() allow the caller to do that. A + final call to UpdateShells() activates the requested shells that are not + active and deactivates the active shells that are not requested . + + This is done by maintaining two lists. One (the current list) + reflects the current state. The other (the requested list) contains the + currently requested shells. UpdateShells() makes the requested + list the current list and clears the current list. + + Each shell belongs to one group. Different groups can be modified + separately. +*/ +class ToolBarShellList +{ +public: + /** Create a new object with an empty current list and an empty + requested list. + */ + ToolBarShellList(); + + /** Remove all shells from a group. Calling this method should normally + not be necessary because after the construction or after a call to + UpdateShells() the requested list is empty. + @param eGroup + The group to clear. Shells in other groups are not modified. + */ + void ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup); + + /** Add a shell. When the specified shell has already been requested + for another group then it is moved to this group. + @param eGroup + The group to which to add the shell. + @param nId + The id of the shell to add. + */ + void AddShellId (sd::ToolBarManager::ToolBarGroup eGroup, sd::ShellId nId); + + /** Releasing all shells means that the given ToolBarRules object is + informed that every shell managed by the called ToolBarShellList is + about to be removed and that the associated framework tool bars can + be removed as well. The caller still has to call UpdateShells(). + */ + void ReleaseAllShells (ToolBarRules& rRules); + + /** The requested list is made the current list by activating all + shells in the requested list and by deactivating the shells in the + current list that are not in the requested list. + @param pMainViewShell + The shells that are activated or deactivated are sub shells of + this view shell. + @param rManager + This ViewShellManager is used to activate or deactivate shells. + */ + void UpdateShells ( + const std::shared_ptr& rpMainViewShell, + const std::shared_ptr& rpManager); + +private: + class ShellDescriptor + {public: + ShellDescriptor (ShellId nId,sd::ToolBarManager::ToolBarGroup eGroup); + ShellId mnId; + sd::ToolBarManager::ToolBarGroup meGroup; + friend bool operator<(const ShellDescriptor& r1, const ShellDescriptor& r2) + { return r1.mnId < r2.mnId; } + }; + + /** The requested list of tool bar shells that will be active after the + next call to UpdateShells(). + */ + typedef ::std::set GroupedShellList; + GroupedShellList maNewList; + + /** The list of tool bar shells that are currently on the shell stack. + Using a GroupedShellList is not strictly necessary but it makes + things easier and does not waste too much memory. + */ + GroupedShellList maCurrentList; +}; + +/** This class concentrates the knowledge about when to show what tool bars + in one place. +*/ +class ToolBarRules +{ +public: + ToolBarRules ( + const std::shared_ptr& rpToolBarManager, + const std::shared_ptr& rpViewShellManager); + + /** This method calls MainViewShellChanged() and SelectionHasChanged() + for the current main view shell and its view. + */ + void Update (ViewShellBase const & rBase); + + /** Reset all tool bars in all groups and add tool bars and tool bar + shells to the ToolBarGroup::Permanent group for the specified ViewShell type. + */ + void MainViewShellChanged (ViewShell::ShellType nShellType); + + /** Reset all tool bars in all groups and add tool bars and tool bar + shells to the ToolBarGroup::Permanent group for the specified ViewShell. + */ + void MainViewShellChanged (const ViewShell& rMainViewShell); + + /** Reset all tool bars in the ToolBarGroup::Function group and add tool bars and tool bar + shells to this group for the current selection. + */ + void SelectionHasChanged ( + const ::sd::ViewShell& rViewShell, + const SdrView& rView); + + /** Add a tool bar for the specified tool bar shell. + */ + void SubShellAdded ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId); + + /** Remove a tool bar for the specified tool bar shell. + */ + void SubShellRemoved ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId); + +private: + std::shared_ptr mpToolBarManager; + std::shared_ptr mpViewShellManager; +}; + +} // end of anonymous namespace + +namespace sd { + +//===== ToolBarManager::Implementation ======================================== + +class ToolBarManager::Implementation +{ +public: + /** This constructor takes three arguments even though the + ToolBarManager could be taken from the ViewShellBase. This is so to + state explicitly which information has to be present when this + constructor is called. The ViewShellBase may not have been fully + initialized at this point and must not be asked for this values. + */ + Implementation ( + ViewShellBase& rBase, + const std::shared_ptr& rpMultiplexer, + const std::shared_ptr& rpViewShellManager, + const std::shared_ptr& rpToolBarManager); + ~Implementation(); + + void SetValid (bool bValid); + + void ResetToolBars (ToolBarGroup eGroup); + void ResetAllToolBars(); + void AddToolBar (ToolBarGroup eGroup, const OUString& rsToolBarName); + void AddToolBarShell (ToolBarGroup eGroup, ShellId nToolBarId); + void RemoveToolBar (ToolBarGroup eGroup, const OUString& rsToolBarName); + + /** Release all tool bar shells and the associated framework tool bars. + Typically called when the main view shell is being replaced by + another, all tool bar shells are released. In that process the + shells are destroyed anyway and without calling this method they + would still be referenced. + */ + void ReleaseAllToolBarShells(); + + void ToolBarsDestroyed(); + + void RequestUpdate(); + + void PreUpdate(); + void PostUpdate(); + /** Tell the XLayoutManager about the tool bars that we would like to be + shown. + @param rpLayouterLock + This typically is the mpSynchronousLayouterLock that is used in + this method and that is either released at its end or assigned + to mpAsynchronousLock in order to be unlocked later. + */ + void Update (::std::unique_ptr pLayouterLock); + + class UpdateLockImplementation + { + public: + explicit UpdateLockImplementation (Implementation& rImplementation) + : mrImplementation(rImplementation) { mrImplementation.LockUpdate(); } + ~UpdateLockImplementation() { mrImplementation.UnlockUpdate(); } + private: + Implementation& mrImplementation; + }; + + void LockViewShellManager(); + void LockUpdate(); + void UnlockUpdate(); + + ToolBarRules& GetToolBarRules() { return maToolBarRules;} + +private: + mutable ::osl::Mutex maMutex; + ViewShellBase& mrBase; + std::shared_ptr mpEventMultiplexer; + bool mbIsValid; + ToolBarList maToolBarList; + ToolBarShellList maToolBarShellList; + Reference mxLayouter; + sal_Int32 mnLockCount; + bool mbPreUpdatePending; + bool mbPostUpdatePending; + /** The layouter locks manage the locking of the XLayoutManager. The + lock() and unlock() functions are not called directly because the + (final) unlocking is usually done asynchronously *after* the + list of requested toolbars is updated. + */ + ::std::unique_ptr mpSynchronousLayouterLock; + ::std::unique_ptr mpAsynchronousLayouterLock; + ::std::unique_ptr> mpViewShellManagerLock; + ImplSVEvent * mnPendingUpdateCall; + ImplSVEvent * mnPendingSetValidCall; + ToolBarRules maToolBarRules; + + static OUString GetToolBarResourceName (std::u16string_view rsBaseName); + bool CheckPlugInMode (std::u16string_view rsName) const; + + DECL_LINK(UpdateCallback, void *, void); + DECL_LINK(EventMultiplexerCallback, sd::tools::EventMultiplexerEvent&, void); + DECL_LINK(SetValidCallback, void*, void); +}; + +//===== ToolBarManager ======================================================== + +std::shared_ptr ToolBarManager::Create ( + ViewShellBase& rBase, + const std::shared_ptr& rpMultiplexer, + const std::shared_ptr& rpViewShellManager) +{ + std::shared_ptr pManager (new ToolBarManager()); + pManager->mpImpl.reset( + new Implementation(rBase,rpMultiplexer,rpViewShellManager,pManager)); + return pManager; +} + +ToolBarManager::ToolBarManager() +{ +} + +ToolBarManager::~ToolBarManager() +{ +} + +void ToolBarManager::Shutdown() +{ + if (mpImpl != nullptr) + mpImpl.reset(); +} + +void ToolBarManager::ResetToolBars (ToolBarGroup eGroup) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + } +} + +void ToolBarManager::ResetAllToolBars() +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetAllToolBars(); + } +} + +void ToolBarManager::AddToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->AddToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::AddToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->AddToolBarShell(eGroup,nToolBarId); + } +} + +void ToolBarManager::RemoveToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->RemoveToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::SetToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + mpImpl->AddToolBar(eGroup,rsToolBarName); + } +} + +void ToolBarManager::SetToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + if (mpImpl != nullptr) + { + UpdateLock aLock (shared_from_this()); + mpImpl->ResetToolBars(eGroup); + mpImpl->AddToolBarShell(eGroup,nToolBarId); + } +} + +void ToolBarManager::PreUpdate() +{ + if (mpImpl != nullptr) + mpImpl->PreUpdate(); +} + +void ToolBarManager::RequestUpdate() +{ + if (mpImpl != nullptr) + mpImpl->RequestUpdate(); +} + +void ToolBarManager::LockViewShellManager() +{ + if (mpImpl != nullptr) + mpImpl->LockViewShellManager(); +} + +void ToolBarManager::LockUpdate() +{ + if (mpImpl != nullptr) + mpImpl->LockUpdate(); +} + +void ToolBarManager::UnlockUpdate() +{ + if (mpImpl != nullptr) + mpImpl->UnlockUpdate(); +} + +void ToolBarManager::MainViewShellChanged () +{ + if (mpImpl != nullptr) + { + mpImpl->ReleaseAllToolBarShells(); + mpImpl->GetToolBarRules().MainViewShellChanged(ViewShell::ST_NONE); + } +} + +void ToolBarManager::MainViewShellChanged (const ViewShell& rMainViewShell) +{ + if (mpImpl != nullptr) + { + mpImpl->ReleaseAllToolBarShells(); + mpImpl->GetToolBarRules().MainViewShellChanged(rMainViewShell); + } +} + +void ToolBarManager::SelectionHasChanged ( + const ViewShell& rViewShell, + const SdrView& rView) +{ + if (mpImpl != nullptr) + mpImpl->GetToolBarRules().SelectionHasChanged(rViewShell,rView); +} + +void ToolBarManager::ToolBarsDestroyed() +{ + if (mpImpl != nullptr) + mpImpl->ToolBarsDestroyed(); +} + +//===== ToolBarManager::Implementation ======================================= + +ToolBarManager::Implementation::Implementation ( + ViewShellBase& rBase, + const std::shared_ptr& rpMultiplexer, + const std::shared_ptr& rpViewShellManager, + const std::shared_ptr& rpToolBarManager) + : mrBase(rBase), + mpEventMultiplexer(rpMultiplexer), + mbIsValid(false), + mnLockCount(0), + mbPreUpdatePending(false), + mbPostUpdatePending(false), + mnPendingUpdateCall(nullptr), + mnPendingSetValidCall(nullptr), + maToolBarRules(rpToolBarManager,rpViewShellManager) +{ + Link aLink (LINK(this,ToolBarManager::Implementation,EventMultiplexerCallback)); + mpEventMultiplexer->AddEventListener( aLink ); +} + +/** The order of statements is important. + First unregister listeners, which may post user events. + Then remove pending user events. +*/ +ToolBarManager::Implementation::~Implementation() +{ + // Unregister at broadcasters. + Link aLink (LINK(this,ToolBarManager::Implementation,EventMultiplexerCallback)); + mpEventMultiplexer->RemoveEventListener(aLink); + + // Abort pending user calls. + if (mnPendingUpdateCall != nullptr) + Application::RemoveUserEvent(mnPendingUpdateCall); + if (mnPendingSetValidCall != nullptr) + Application::RemoveUserEvent(mnPendingSetValidCall); +} + +void ToolBarManager::Implementation::ToolBarsDestroyed() +{ + maToolBarList.MarkAllToolBarsAsNotActive(); +} + +void ToolBarManager::Implementation::SetValid (bool bValid) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (mbIsValid == bValid) + return; + + UpdateLockImplementation aUpdateLock (*this); + + mbIsValid = bValid; + if (mbIsValid) + { + Reference xFrame; + if (mrBase.GetViewFrame() != nullptr) + xFrame = mrBase.GetViewFrame()->GetFrame().GetFrameInterface(); + try + { + Reference xFrameProperties (xFrame, UNO_QUERY_THROW); + Any aValue (xFrameProperties->getPropertyValue("LayoutManager")); + aValue >>= mxLayouter; + // tdf#119997 if mpSynchronousLayouterLock was created before mxLayouter was + // set then update it now that its available + if (mpSynchronousLayouterLock && !mpSynchronousLayouterLock->is()) + mpSynchronousLayouterLock.reset(new LayouterLock(mxLayouter)); + } + catch (const RuntimeException&) + { + } + + GetToolBarRules().Update(mrBase); + } + else + { + ResetAllToolBars(); + mxLayouter = nullptr; + } +} + +void ToolBarManager::Implementation::ResetToolBars (ToolBarGroup eGroup) +{ + ::osl::MutexGuard aGuard(maMutex); + + maToolBarList.ClearGroup(eGroup); + maToolBarShellList.ClearGroup(eGroup); + + mbPreUpdatePending = true; +} + +void ToolBarManager::Implementation::ResetAllToolBars() +{ + SAL_INFO("sd.view", __func__ << ": resetting all tool bars"); + for (auto i : o3tl::enumrange()) + ResetToolBars(i); +} + +void ToolBarManager::Implementation::AddToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (CheckPlugInMode(rsToolBarName)) + { + maToolBarList.AddToolBar(eGroup,rsToolBarName); + + mbPostUpdatePending = true; + if (mnLockCount == 0) + PostUpdate(); + } +} + +void ToolBarManager::Implementation::RemoveToolBar ( + ToolBarGroup eGroup, + const OUString& rsToolBarName) +{ + ::osl::MutexGuard aGuard(maMutex); + + if (maToolBarList.RemoveToolBar(eGroup,rsToolBarName)) + { + mbPreUpdatePending = true; + if (mnLockCount == 0) + PreUpdate(); + } +} + +void ToolBarManager::Implementation::AddToolBarShell ( + ToolBarGroup eGroup, + ShellId nToolBarId) +{ + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + if (pMainViewShell != nullptr) + { + maToolBarShellList.AddShellId(eGroup,nToolBarId); + GetToolBarRules().SubShellAdded(eGroup, nToolBarId); + } +} + +void ToolBarManager::Implementation::ReleaseAllToolBarShells() +{ + maToolBarShellList.ReleaseAllShells(GetToolBarRules()); + maToolBarShellList.UpdateShells(mrBase.GetMainViewShell(), mrBase.GetViewShellManager()); +} + +void ToolBarManager::Implementation::RequestUpdate() +{ + if (mnPendingUpdateCall == nullptr) + { + mnPendingUpdateCall = Application::PostUserEvent( + LINK(this,ToolBarManager::Implementation,UpdateCallback)); + } +} + +void ToolBarManager::Implementation::PreUpdate() +{ + ::osl::MutexGuard aGuard(maMutex); + + if (!(mbIsValid + && mbPreUpdatePending + && mxLayouter.is())) + return; + + mbPreUpdatePending = false; + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PreUpdate ["); + + // Get the list of tool bars that are not used anymore and are to be + // deactivated. + std::vector aToolBars; + maToolBarList.GetToolBarsToDeactivate(aToolBars); + + // Turn off the tool bars. + for (const auto& aToolBar : aToolBars) + { + OUString sFullName (GetToolBarResourceName(aToolBar)); + SAL_INFO("sd.view", __func__ << ": turning off tool bar " << sFullName); + mxLayouter->destroyElement(sFullName); + maToolBarList.MarkToolBarAsNotActive(aToolBar); + } + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PreUpdate ]"); +} + +void ToolBarManager::Implementation::PostUpdate() +{ + ::osl::MutexGuard aGuard(maMutex); + + if (!(mbIsValid + && mbPostUpdatePending + && mxLayouter.is())) + return; + + mbPostUpdatePending = false; + + // Create the list of requested tool bars. + std::vector aToolBars; + maToolBarList.GetToolBarsToActivate(aToolBars); + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PostUpdate ["); + + // Turn on the tool bars that are visible in the new context. + for (const auto& aToolBar : aToolBars) + { + OUString sFullName (GetToolBarResourceName(aToolBar)); + SAL_INFO("sd.view", __func__ << ": turning on tool bar " << sFullName); + mxLayouter->requestElement(sFullName); + maToolBarList.MarkToolBarAsActive(aToolBar); + } + + SAL_INFO("sd.view", __func__ << ": ToolBarManager::PostUpdate ]"); +} + +void ToolBarManager::Implementation::LockViewShellManager() +{ + if (mpViewShellManagerLock == nullptr) + mpViewShellManagerLock.reset( + new ViewShellManager::UpdateLock(mrBase.GetViewShellManager())); +} + +void ToolBarManager::Implementation::LockUpdate() +{ + SAL_INFO("sd.view", __func__ << ": LockUpdate " << mnLockCount); + ::osl::MutexGuard aGuard(maMutex); + + DBG_ASSERT(mnLockCount<100, "ToolBarManager lock count unusually high"); + if (mnLockCount == 0) + { + OSL_ASSERT(mpSynchronousLayouterLock == nullptr); + + mpSynchronousLayouterLock.reset(new LayouterLock(mxLayouter)); + } + ++mnLockCount; +} + +void ToolBarManager::Implementation::UnlockUpdate() +{ + SAL_INFO("sd.view", __func__ << ": UnlockUpdate " << mnLockCount); + ::osl::MutexGuard aGuard(maMutex); + + OSL_ASSERT(mnLockCount>0); + --mnLockCount; + if (mnLockCount == 0) + { + Update(std::move(mpSynchronousLayouterLock)); + } +} + +void ToolBarManager::Implementation::Update ( + ::std::unique_ptr pLocalLayouterLock) +{ + // When the lock is released and there are pending changes to the set of + // tool bars then update this set now. + if (mnLockCount != 0) + return; + + // During creation of ViewShellBase we may have the situation that + // the controller has already been created and attached to the frame + // but that the ToolBarManager has not yet completed its + // initialization (by initializing the mxLayouter member.) We do + // this here so that we do not have to wait for the next Update() + // call to show the tool bars. + if (mnPendingSetValidCall != nullptr) + { + Application::RemoveUserEvent(mnPendingSetValidCall); + mnPendingSetValidCall = nullptr; + SetValid(true); + } + + if (mbIsValid && mxLayouter.is() && (mbPreUpdatePending || mbPostUpdatePending)) + { + // 1) Release UNO tool bars that are no longer used. Do this + // now so that they are not updated when the SFX shell stack is + // modified. + if (mbPreUpdatePending) + PreUpdate(); + + // 2) Update the requested shells that represent tool bar + // functionality. Those that are not used anymore are + // deactivated now. Those that are missing are activated in the + // next step together with the view shells. + if (mpViewShellManagerLock == nullptr) + mpViewShellManagerLock.reset( + new ViewShellManager::UpdateLock(mrBase.GetViewShellManager())); + maToolBarShellList.UpdateShells( + mrBase.GetMainViewShell(), + mrBase.GetViewShellManager()); + + // 3) Unlock the ViewShellManager::UpdateLock. This updates the + // shell stack. + mpViewShellManagerLock.reset(); + + // 4) Make the UNO tool bars visible. The outstanding call to + // PostUpdate() is done via PostUserEvent() so that it is + // guaranteed to be executed when the SFX shell stack has been + // updated (under the assumption that our lock to the + // ViewShellManager was the only one open. If that is not the + // case then all should still be well but not as fast.) + + // Note that the lock count may have been increased since + // entering this method. In that case one of the next + // UnlockUpdate() calls will post the UpdateCallback. + if (mnLockCount==0) + { + mpAsynchronousLayouterLock = std::move(pLocalLayouterLock); + if (mnPendingUpdateCall==nullptr) + { + mnPendingUpdateCall = Application::PostUserEvent( + LINK(this,ToolBarManager::Implementation,UpdateCallback)); + } + } + } + else + { + mpViewShellManagerLock.reset(); + pLocalLayouterLock.reset(); + } +} + +IMPL_LINK_NOARG(ToolBarManager::Implementation, UpdateCallback, void*, void) +{ + mnPendingUpdateCall = nullptr; + if (mnLockCount == 0) + { + if (mbPreUpdatePending) + PreUpdate(); + if (mbPostUpdatePending) + PostUpdate(); + if (mbIsValid && mxLayouter.is()) + mpAsynchronousLayouterLock.reset(); + } +} + +IMPL_LINK(ToolBarManager::Implementation,EventMultiplexerCallback, + sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + SolarMutexGuard g; + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::ControllerAttached: + if (mnPendingSetValidCall == nullptr) + mnPendingSetValidCall + = Application::PostUserEvent(LINK(this,Implementation,SetValidCallback)); + break; + + case EventMultiplexerEventId::ControllerDetached: + SetValid(false); + break; + + default: break; + } +} + +IMPL_LINK_NOARG(ToolBarManager::Implementation, SetValidCallback, void*, void) +{ + mnPendingSetValidCall = nullptr; + SetValid(true); +} + +OUString ToolBarManager::Implementation::GetToolBarResourceName ( + std::u16string_view rsBaseName) +{ + return OUString::Concat("private:resource/toolbar/") + rsBaseName; +} + +bool ToolBarManager::Implementation::CheckPlugInMode (std::u16string_view rsName) const +{ + bool bValid (false); + + // Determine the plug in mode. + bool bIsPlugInMode (false); + do + { + SfxObjectShell* pObjectShell = mrBase.GetObjectShell(); + if (pObjectShell == nullptr) + break; + + SfxMedium* pMedium = pObjectShell->GetMedium(); + if (pMedium == nullptr) + break; + + const SfxBoolItem* pViewOnlyItem = SfxItemSet::GetItem(pMedium->GetItemSet(), SID_VIEWONLY, false); + if (pViewOnlyItem == nullptr) + break; + + bIsPlugInMode = pViewOnlyItem->GetValue(); + } + while (false); + + if (rsName == msViewerToolBar) + bValid = bIsPlugInMode; + else + bValid = ! bIsPlugInMode; + + return bValid; +} + +} // end of namespace sd + +namespace { + +using namespace ::sd; + +//===== LayouterLock ========================================================== + +LayouterLock::LayouterLock (const Reference& rxLayouter) + : mxLayouter(rxLayouter) +{ + SAL_INFO("sd.view", __func__ << ": LayouterLock " << (mxLayouter.is() ? 1 :0)); + if (mxLayouter.is()) + mxLayouter->lock(); +} + +LayouterLock::~LayouterLock() +{ + SAL_INFO("sd.view", __func__ << ": ~LayouterLock " << (mxLayouter.is() ? 1 :0)); + if (mxLayouter.is()) + mxLayouter->unlock(); +} + +//===== ToolBarRules ========================================================== + +ToolBarRules::ToolBarRules ( + const std::shared_ptr& rpToolBarManager, + const std::shared_ptr& rpViewShellManager) + : mpToolBarManager(rpToolBarManager), + mpViewShellManager(rpViewShellManager) +{ +} + +void ToolBarRules::Update (ViewShellBase const & rBase) +{ + ViewShell* pMainViewShell = rBase.GetMainViewShell().get(); + if (pMainViewShell != nullptr) + { + MainViewShellChanged(pMainViewShell->GetShellType()); + if (pMainViewShell->GetView()) + SelectionHasChanged (*pMainViewShell, *pMainViewShell->GetView()); + } + else + MainViewShellChanged(ViewShell::ST_NONE); +} + +void ToolBarRules::MainViewShellChanged (ViewShell::ShellType nShellType) +{ + ::sd::ToolBarManager::UpdateLock aToolBarManagerLock (mpToolBarManager); + ::sd::ViewShellManager::UpdateLock aViewShellManagerLock (mpViewShellManager); + + mpToolBarManager->ResetAllToolBars(); + + switch(nShellType) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_NOTES: + case ::sd::ViewShell::ST_HANDOUT: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOptionsToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + break; + + case ::sd::ViewShell::ST_DRAW: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOptionsToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + break; + + case ViewShell::ST_OUTLINE: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msOutlineToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + mpToolBarManager->AddToolBarShell( + ToolBarManager::ToolBarGroup::Permanent, ToolbarId::Draw_Text_Toolbox_Sd); + break; + + case ViewShell::ST_SLIDE_SORTER: + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msViewerToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msSlideSorterToolBar); + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::Permanent, + ToolBarManager::msSlideSorterObjectBar); + break; + + case ViewShell::ST_NONE: + case ViewShell::ST_PRESENTATION: + case ViewShell::ST_SIDEBAR: + default: + break; + } +} + +void ToolBarRules::MainViewShellChanged (const ViewShell& rMainViewShell) +{ + ::sd::ToolBarManager::UpdateLock aToolBarManagerLock (mpToolBarManager); + ::sd::ViewShellManager::UpdateLock aViewShellManagerLock (mpViewShellManager); + + MainViewShellChanged(rMainViewShell.GetShellType()); + switch(rMainViewShell.GetShellType()) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_DRAW: + case ::sd::ViewShell::ST_NOTES: + { + const DrawViewShell* pDrawViewShell + = dynamic_cast(&rMainViewShell); + if (pDrawViewShell != nullptr) + { + if (pDrawViewShell->GetEditMode() == EditMode::MasterPage) + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::MasterMode, + ToolBarManager::msMasterViewToolBar); + else if ( rMainViewShell.GetShellType() != ::sd::ViewShell::ST_DRAW ) + mpToolBarManager->AddToolBar( + ToolBarManager::ToolBarGroup::CommonTask, + ToolBarManager::msCommonTaskToolBar); + } + break; + } + + default: + break; + } +} + +void ToolBarRules::SelectionHasChanged ( + const ::sd::ViewShell& rViewShell, + const SdrView& rView) +{ + ::sd::ToolBarManager::UpdateLock aLock (mpToolBarManager); + mpToolBarManager->LockViewShellManager(); + bool bTextEdit = rView.IsTextEdit(); + + mpToolBarManager->ResetToolBars(ToolBarManager::ToolBarGroup::Function); + + switch (rView.GetContext()) + { + case SdrViewContext::Graphic: + if( !bTextEdit ) + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Graf_Toolbox); + break; + + case SdrViewContext::Media: + if( !bTextEdit ) + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Media_Toolbox); + break; + + case SdrViewContext::Table: + mpToolBarManager->SetToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Table_Toolbox); + bTextEdit = true; + break; + + case SdrViewContext::Standard: + default: + if( !bTextEdit ) + { + switch(rViewShell.GetShellType()) + { + case ::sd::ViewShell::ST_IMPRESS: + case ::sd::ViewShell::ST_DRAW: + case ::sd::ViewShell::ST_NOTES: + case ::sd::ViewShell::ST_HANDOUT: + mpToolBarManager->SetToolBar( + ToolBarManager::ToolBarGroup::Function, + ToolBarManager::msDrawingObjectToolBar); + break; + default: + break; + } + break; + } + } + + if( bTextEdit ) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Draw_Text_Toolbox_Sd); + + SdrView* pView = &const_cast(rView); + // Check if the extrusion tool bar and the fontwork tool bar have to + // be activated. + if (svx::checkForSelectedCustomShapes(pView, true /* bOnlyExtruded */ )) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Svx_Extrusion_Bar); + + if (svx::checkForSelectedFontWork(pView)) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Svx_Fontwork_Bar); + + // Switch on additional context-sensitive tool bars. + if (rView.GetContext() == SdrViewContext::PointEdit) + mpToolBarManager->AddToolBarShell(ToolBarManager::ToolBarGroup::Function, ToolbarId::Bezier_Toolbox_Sd); +} + +void ToolBarRules::SubShellAdded ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId) +{ + // For some tool bar shells (those defined in sd) we have to add the + // actual tool bar here. + switch (nShellId) + { + case ToolbarId::Draw_Graf_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msGraphicObjectBar); + break; + + case ToolbarId::Draw_Media_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msMediaObjectBar); + break; + + case ToolbarId::Draw_Text_Toolbox_Sd: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msTextObjectBar); + break; + + case ToolbarId::Bezier_Toolbox_Sd: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msBezierObjectBar); + break; + + case ToolbarId::Draw_Table_Toolbox: + mpToolBarManager->AddToolBar(eGroup, ToolBarManager::msTableObjectBar); + break; + + default: + break; + } +} + +void ToolBarRules::SubShellRemoved ( + ::sd::ToolBarManager::ToolBarGroup eGroup, + sd::ShellId nShellId) +{ + // For some tool bar shells (those defined in sd) we have to add the + // actual tool bar here. + switch (nShellId) + { + case ToolbarId::Draw_Graf_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msGraphicObjectBar); + break; + + case ToolbarId::Draw_Media_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msMediaObjectBar); + break; + + case ToolbarId::Draw_Text_Toolbox_Sd: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msTextObjectBar); + break; + + case ToolbarId::Bezier_Toolbox_Sd: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msBezierObjectBar); + break; + + case ToolbarId::Draw_Table_Toolbox: + mpToolBarManager->RemoveToolBar(eGroup, ToolBarManager::msTableObjectBar); + break; + + default: + break; + } +} + +//===== ToolBarList =========================================================== + +ToolBarList::ToolBarList() +{ +} + +void ToolBarList::ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + { + iGroup->second.clear(); + } +} + +void ToolBarList::AddToolBar ( + sd::ToolBarManager::ToolBarGroup eGroup, + const OUString& rsName) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup == maGroups.end()) + iGroup = maGroups.emplace(eGroup,std::vector()).first; + + if (iGroup != maGroups.end()) + { + auto iBar (std::find(iGroup->second.cbegin(),iGroup->second.cend(),rsName)); + if (iBar == iGroup->second.cend()) + { + iGroup->second.push_back(rsName); + } + } +} + +bool ToolBarList::RemoveToolBar ( + sd::ToolBarManager::ToolBarGroup eGroup, + const OUString& rsName) +{ + Groups::iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + { + auto iBar (std::find(iGroup->second.begin(),iGroup->second.end(),rsName)); + if (iBar != iGroup->second.end()) + { + iGroup->second.erase(iBar); + return true; + } + } + return false; +} + +void ToolBarList::MakeRequestedToolBarList (std::vector& rRequestedToolBars) const +{ + for (auto eGroup : o3tl::enumrange()) + { + Groups::const_iterator iGroup (maGroups.find(eGroup)); + if (iGroup != maGroups.end()) + rRequestedToolBars.insert( rRequestedToolBars.end(), + iGroup->second.begin(), + iGroup->second.end() ); + } +} + +void ToolBarList::GetToolBarsToActivate (std::vector& rToolBars) const +{ + std::vector aRequestedToolBars; + MakeRequestedToolBarList(aRequestedToolBars); + + for (const auto& aToolBar : aRequestedToolBars) + { + if (::std::find(maActiveToolBars.begin(),maActiveToolBars.end(),aToolBar) + == maActiveToolBars.end()) + { + rToolBars.push_back(aToolBar); + } + } +} + +void ToolBarList::GetToolBarsToDeactivate (std::vector& rToolBars) const +{ + std::vector aRequestedToolBars; + MakeRequestedToolBarList(aRequestedToolBars); + + for (auto& aToolBar : maActiveToolBars) + { + if (::std::find(aRequestedToolBars.begin(),aRequestedToolBars.end(),aToolBar) + == aRequestedToolBars.end()) + { + rToolBars.push_back(aToolBar); + } + } +} + +void ToolBarList::MarkToolBarAsActive (const OUString& rsName) +{ + maActiveToolBars.push_back(rsName); +} + +void ToolBarList::MarkToolBarAsNotActive (const OUString& rsName) +{ + maActiveToolBars.erase( + ::std::find(maActiveToolBars.begin(),maActiveToolBars.end(), rsName)); +} + +void ToolBarList::MarkAllToolBarsAsNotActive() +{ + maActiveToolBars.clear(); +} + +//===== ToolBarShellList ====================================================== + +ToolBarShellList::ShellDescriptor::ShellDescriptor ( + ShellId nId, + sd::ToolBarManager::ToolBarGroup eGroup) + : mnId(nId), + meGroup(eGroup) +{ +} + +ToolBarShellList::ToolBarShellList() +{ +} + +void ToolBarShellList::ClearGroup (sd::ToolBarManager::ToolBarGroup eGroup) +{ + for (GroupedShellList::iterator iDescriptor = maNewList.begin(); iDescriptor != maNewList.end(); ) + if (iDescriptor->meGroup == eGroup) + iDescriptor = maNewList.erase(iDescriptor); + else + ++iDescriptor; +} + +void ToolBarShellList::AddShellId (sd::ToolBarManager::ToolBarGroup eGroup, sd::ShellId nId) +{ + // Make sure that the shell is not added twice (and possibly in + // different groups.) + ShellDescriptor aDescriptor (nId,eGroup); + GroupedShellList::iterator iDescriptor (maNewList.find(aDescriptor)); + if (iDescriptor != maNewList.end()) + { + // The shell is already requested. + if (iDescriptor->meGroup != eGroup) + { + // It is now being requested for another group. + // (Is this an error?) + // Move it to that group. + maNewList.erase(iDescriptor); + maNewList.insert(aDescriptor); + } + // else nothing to do. + } + else + maNewList.insert(aDescriptor); +} + +void ToolBarShellList::ReleaseAllShells (ToolBarRules& rRules) +{ + // Release the currently active tool bars. + GroupedShellList aList (maCurrentList); + for (const auto& rDescriptor : aList) + { + rRules.SubShellRemoved(rDescriptor.meGroup, rDescriptor.mnId); + } + + // Clear the list of requested tool bars. + maNewList.clear(); +} + +void ToolBarShellList::UpdateShells ( + const std::shared_ptr& rpMainViewShell, + const std::shared_ptr& rpManager) +{ + if (rpMainViewShell == nullptr) + return; + + GroupedShellList aList; + + // Deactivate shells that are in maCurrentList, but not in + // maNewList. + ::std::set_difference(maCurrentList.begin(), maCurrentList.end(), + maNewList.begin(), maNewList.end(), + std::insert_iterator(aList,aList.begin())); + for (const auto& rShell : aList) + { + SAL_INFO("sd.view", __func__ << ": deactivating tool bar shell " << static_cast(rShell.mnId)); + rpManager->DeactivateSubShell(*rpMainViewShell, rShell.mnId); + } + + // Activate shells that are in maNewList, but not in + // maCurrentList. + aList.clear(); + ::std::set_difference(maNewList.begin(), maNewList.end(), + maCurrentList.begin(), maCurrentList.end(), + std::insert_iterator(aList,aList.begin())); + for (const auto& rShell : aList) + { + SAL_INFO("sd.view", __func__ << ": activating tool bar shell " << static_cast(rShell.mnId)); + rpManager->ActivateSubShell(*rpMainViewShell, rShell.mnId); + } + + // The maNewList now reflects the current state and thus is made + // maCurrentList. + maCurrentList = maNewList; +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewClipboard.cxx b/sd/source/ui/view/ViewClipboard.cxx new file mode 100644 index 000000000..c17bf7de1 --- /dev/null +++ b/sd/source/ui/view/ViewClipboard.cxx @@ -0,0 +1,240 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +namespace sd { + +ViewClipboard::ViewClipboard (::sd::View& rView) + : mrView(rView) +{ +} + +ViewClipboard::~ViewClipboard() +{ +} + +void ViewClipboard::HandlePageDrop (const SdTransferable& rTransferable) +{ + // Determine whether to insert the given set of slides or to assign a + // given master page. + // tdf#113405 only assign master pages to normal pages, don't attempt to assign a master + // page to a master page + sd::DrawViewShell* pDrawViewShell = dynamic_cast<::sd::DrawViewShell*>(mrView.GetViewShell()); + SdPage* pMasterPage = (pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::Page) ? GetFirstMasterPage(rTransferable) : nullptr; + if (pMasterPage) + AssignMasterPage (rTransferable, pMasterPage); + else + InsertSlides (rTransferable, DetermineInsertPosition ()); +} + +SdPage* ViewClipboard::GetFirstMasterPage (const SdTransferable& rTransferable) +{ + SdPage* pFirstMasterPage = nullptr; + + if (rTransferable.HasPageBookmarks()) + { + do + { + const std::vector &rBookmarks = rTransferable.GetPageBookmarks(); + + if (rBookmarks.empty()) + break; + + DrawDocShell* pDocShell = rTransferable.GetPageDocShell(); + if (pDocShell == nullptr) + break; + + SdDrawDocument* pDocument = pDocShell->GetDoc(); + if (pDocument == nullptr) + break; + + for (const OUString& sName : rBookmarks) + { + bool bIsMasterPage; + + // SdPage* GetMasterSdPage(sal_uInt16 nPgNum, PageKind ePgKind); + // sal_uInt16 GetMasterSdPageCount(PageKind ePgKind) const; + + sal_uInt16 nBMPage = pDocument->GetPageByName ( + sName, bIsMasterPage); + if ( ! bIsMasterPage) + { + // At least one regular slide: return NULL to indicate + // that not all bookmarks point to master pages. + pFirstMasterPage = nullptr; + break; + } + else if (pFirstMasterPage == nullptr) + { + // Remember the first master page for later. + if (nBMPage != SDRPAGE_NOTFOUND) + pFirstMasterPage = static_cast( + pDocument->GetMasterPage(nBMPage)); + } + } + } + while (false); + } + + return pFirstMasterPage; +} + +void ViewClipboard::AssignMasterPage ( + const SdTransferable& rTransferable, + SdPage const * pMasterPage) +{ + if (pMasterPage == nullptr) + return; + + // Get the target page to which the master page is assigned. + SdrPageView* pPageView = mrView.GetSdrPageView(); + if (pPageView == nullptr) + return; + + SdPage* pPage = static_cast(pPageView->GetPage()); + if (pPage == nullptr) + return; + + SdDrawDocument& rDocument = mrView.GetDoc(); + + if ( ! rTransferable.HasPageBookmarks()) + return; + + DrawDocShell* pDataDocShell = rTransferable.GetPageDocShell(); + if (pDataDocShell == nullptr) + return; + + SdDrawDocument* pSourceDocument = pDataDocShell->GetDoc(); + if (pSourceDocument == nullptr) + return; + + // We have to remove the layout suffix from the layout name which is + // appended again by SetMasterPage() to the given name. Don't ask. + OUString sLayoutSuffix = SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE; + sal_Int32 nLength = sLayoutSuffix.getLength(); + OUString sLayoutName = pMasterPage->GetLayoutName(); + if (sLayoutName.endsWith(sLayoutSuffix)) + sLayoutName = sLayoutName.copy(0, sLayoutName.getLength() - nLength); + + rDocument.SetMasterPage ( + pPage->GetPageNum() / 2, + sLayoutName, + pSourceDocument, + false, // Exchange the master page of only the target page. + false // Keep unused master pages. + ); +} + +sal_uInt16 ViewClipboard::DetermineInsertPosition () +{ + SdDrawDocument& rDoc = mrView.GetDoc(); + sal_uInt16 nPgCnt = rDoc.GetSdPageCount( PageKind::Standard ); + + // Insert position is the behind the last selected page or behind the + // last page when the selection is empty. + sal_uInt16 nInsertPos = rDoc.GetSdPageCount( PageKind::Standard ) * 2 + 1; + for( sal_uInt16 nPage = 0; nPage < nPgCnt; nPage++ ) + { + SdPage* pPage = rDoc.GetSdPage( nPage, PageKind::Standard ); + + if( pPage->IsSelected() ) + nInsertPos = nPage * 2 + 3; + } + + return nInsertPos; +} + +sal_uInt16 ViewClipboard::InsertSlides ( + const SdTransferable& rTransferable, + sal_uInt16 nInsertPosition) +{ + SdDrawDocument& rDoc = mrView.GetDoc(); + + sal_uInt16 nInsertPgCnt = 0; + bool bMergeMasterPages = !rTransferable.HasSourceDoc( &rDoc ); + + // Prepare the insertion. + const std::vector *pBookmarkList = nullptr; + DrawDocShell* pDataDocSh; + if (rTransferable.HasPageBookmarks()) + { + // When the transferable contains page bookmarks then the referenced + // pages are inserted. + pBookmarkList = &rTransferable.GetPageBookmarks(); + pDataDocSh = rTransferable.GetPageDocShell(); + nInsertPgCnt = static_cast(pBookmarkList->size()); + } + else + { + // Otherwise all pages of the document of the transferable are + // inserted. + SfxObjectShell* pShell = rTransferable.GetDocShell().get(); + pDataDocSh = static_cast(pShell); + SdDrawDocument* pDataDoc = pDataDocSh->GetDoc(); + + if (pDataDoc!=nullptr && pDataDoc->GetSdPageCount(PageKind::Standard)) + nInsertPgCnt = pDataDoc->GetSdPageCount(PageKind::Standard); + } + if (nInsertPgCnt > 0) + { + const SolarMutexGuard aGuard; + ::sd::Window* pWin = mrView.GetViewShell()->GetActiveWindow(); + const bool bWait = pWin && pWin->IsWait(); + + if( bWait ) + pWin->LeaveWait(); + + rDoc.InsertBookmarkAsPage( + pBookmarkList ? *pBookmarkList : std::vector(), + nullptr, + false, + false, + nInsertPosition, + (&rTransferable == SD_MOD()->pTransferDrag), + pDataDocSh, + true, + bMergeMasterPages, + false); + + if( bWait ) + pWin->EnterWait(); + } + + return nInsertPgCnt; +} + +} // end of namespace ::sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewShellBase.cxx b/sd/source/ui/view/ViewShellBase.cxx new file mode 100644 index 000000000..26245971f --- /dev/null +++ b/sd/source/ui/view/ViewShellBase.cxx @@ -0,0 +1,1456 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include + +using namespace sd; +#define ShellClass_ViewShellBase +#include + +using ::sd::framework::FrameworkHelper; + +using namespace com::sun::star; +using namespace com::sun::star::beans; +using namespace com::sun::star::container; +using namespace com::sun::star::drawing::framework; +using namespace com::sun::star::lang; +using namespace com::sun::star::uno; + +namespace { + +class CurrentPageSetter +{ +public: + explicit CurrentPageSetter (ViewShellBase& rBase); + void operator () (bool); +private: + ViewShellBase& mrBase; +}; + +} // end of anonymous namespace + +namespace sd { + +class ViewShellBase::Implementation +{ +public: + /** Main controller of the view shell. During the switching from one + stacked shell to another this pointer may be NULL. + */ + ::rtl::Reference mpController; + + /** The view tab bar is the control for switching between different + views in one pane. + */ + ::rtl::Reference mpViewTabBar; + + // contains the complete area of the current view relative to the frame window + ::tools::Rectangle maClientArea; + + // This is set to true when PrepareClose() is called. + bool mbIsClosing; + + /** The view window is the parent of all UI elements that belong to the + view or ViewShell. This comprises the rulers, the scroll bars, and + the content window. + It does not include the ViewTabBar. + */ + VclPtr mpViewWindow; + std::shared_ptr mpToolBarManager; + std::shared_ptr mpViewShellManager; + std::shared_ptr mpEventMultiplexer; + std::shared_ptr mpFormShellManager; + + explicit Implementation (ViewShellBase& rBase); + ~Implementation(); + + void LateInit(); + + /** Show or hide the ViewTabBar. + @param bShow + When then the ViewTabBar is shown, otherwise it is hidden. + */ + void ShowViewTabBar (bool bShow); + + void SetUserWantsTabBar(bool inValue); + bool GetUserWantsTabBar() const { return mbUserWantsTabBar; } + + /** Common code of ViewShellBase::OuterResizePixel() and + ViewShellBase::InnerResizePixel(). + */ + void ResizePixel ( + const Point& rOrigin, + const Size& rSize, + bool bOuterResize); + + /** Show or hide the specified pane. The visibility state is taken + from the given request. + @param rRequest + The request determines the new visibility state. The state can + either be toggled or be set to a given value. + @param rsPaneURL + This URL specifies the pane whose visibility state to set. + @param rsViewURL + When the pane becomes visible then this view URL specifies which + type of view to show in it. + */ + void SetPaneVisibility ( + const SfxRequest& rRequest, + const OUString& rsPaneURL, + const OUString& rsViewURL); + + void GetSlotState (SfxItemSet& rSet); + + void ProcessRestoreEditingViewSlot(); + +private: + ViewShellBase& mrBase; + bool mbUserWantsTabBar; + bool mbTabBarShouldBeVisible; + /** Hold a reference to the page cache manager of the slide sorter in + order to ensure that it stays alive while the ViewShellBase is + alive. + */ + std::shared_ptr mpPageCacheManager; +}; + +namespace { +/** The only task of this window is to forward key presses to the content + window of the main view shell. With the key press it forwards the focus + so that it is not called very often. +*/ +class FocusForwardingWindow : public vcl::Window +{ +public: + FocusForwardingWindow (vcl::Window& rParentWindow, ViewShellBase& rBase); + virtual ~FocusForwardingWindow() override; + virtual void dispose() override; + virtual void KeyInput (const KeyEvent& rEvent) override; + virtual void Command (const CommandEvent& rEvent) override; + +private: + ViewShellBase& mrBase; +}; +} // end of anonymous namespace + +//===== ViewShellBase ========================================================= + + +// We have to expand the SFX_IMPL_VIEWFACTORY macro to call LateInit() after a +// new ViewShellBase object has been constructed. + +SFX_IMPL_SUPERCLASS_INTERFACE(ViewShellBase, SfxViewShell) + +void ViewShellBase::InitInterface_Impl() +{ +} + +ViewShellBase::ViewShellBase ( + SfxViewFrame* _pFrame, + SfxViewShell*) + : SfxViewShell (_pFrame, SfxViewShellFlags::HAS_PRINTOPTIONS), + mpDocShell (nullptr), + mpDocument (nullptr) +{ + mpImpl.reset(new Implementation(*this)); + mpImpl->mpViewWindow = VclPtr::Create(_pFrame->GetWindow(),*this); + mpImpl->mpViewWindow->SetBackground(Wallpaper()); + + _pFrame->GetWindow().SetBackground(Application::GetSettings().GetStyleSettings().GetLightColor()); + + // Set up the members in the correct order. + if (auto pDrawDocShell = dynamic_cast< DrawDocShell *>( GetViewFrame()->GetObjectShell() )) + mpDocShell = pDrawDocShell; + if (mpDocShell != nullptr) + mpDocument = mpDocShell->GetDoc(); + mpImpl->mpViewShellManager = std::make_shared(*this); + + SetWindow(mpImpl->mpViewWindow.get()); + + // Hide the window to avoid complaints from Sfx...SwitchViewShell... + _pFrame->GetWindow().Hide(); +} + +/** In this destructor the order in which some of the members are destroyed + (and/or being prepared to being destroyed) is important. Change it only + when you know what you are doing. +*/ +ViewShellBase::~ViewShellBase() +{ + // Notify other LOK views that we are going away. + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_VIEW_CURSOR_VISIBLE, "visible", "false"); + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_TEXT_VIEW_SELECTION, "selection", ""); + SfxLokHelper::notifyOtherViews(this, LOK_CALLBACK_GRAPHIC_VIEW_SELECTION, "selection", "EMPTY"); + + sfx2::SfxNotebookBar::CloseMethod(GetFrame()->GetBindings()); + + rtl::Reference xSlideShow(SlideShow::GetSlideShow(*this)); + if (xSlideShow.is() && xSlideShow->dependsOn(this)) + SlideShow::Stop(*this); + xSlideShow.clear(); + + // Tell the controller that the ViewShellBase is not available anymore. + if (mpImpl->mpController) + mpImpl->mpController->ReleaseViewShellBase(); + + // We have to hide the main window to prevent SFX complaining after a + // reload about it being already visible. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell!=nullptr + && pShell->GetActiveWindow()!=nullptr + && pShell->GetActiveWindow()->GetParent()!=nullptr) + { + pShell->GetActiveWindow()->GetParent()->Hide(); + } + + mpImpl->mpToolBarManager->Shutdown(); + mpImpl->mpViewShellManager->Shutdown(); + + EndListening(*GetViewFrame()); + EndListening(*GetDocShell()); + + SetWindow(nullptr); + + mpImpl->mpFormShellManager.reset(); +} + +void ViewShellBase::LateInit (const OUString& rsDefaultView) +{ + StartListening(*GetViewFrame(), DuplicateHandling::Prevent); + StartListening(*GetDocShell(), DuplicateHandling::Prevent); + mpImpl->LateInit(); + InitializeFramework(); + + mpImpl->mpEventMultiplexer = std::make_shared(*this); + + mpImpl->mpFormShellManager = std::make_shared(*this); + + mpImpl->mpToolBarManager = ToolBarManager::Create( + *this, + mpImpl->mpEventMultiplexer, + mpImpl->mpViewShellManager); + + try + { + Reference xControllerManager (GetDrawController(), UNO_QUERY_THROW); + Reference xConfigurationController ( + xControllerManager->getConfigurationController()); + if (xConfigurationController.is()) + { + OUString sView (rsDefaultView); + if (sView.isEmpty()) + sView = GetInitialViewShellType(); + + FrameworkHelper::Instance(*this); + + // Create the resource ids for the center pane and view. + const Reference xCenterPaneId ( + FrameworkHelper::CreateResourceId(FrameworkHelper::msCenterPaneURL)); + const Reference xCenterViewId ( + FrameworkHelper::CreateResourceId(sView, xCenterPaneId)); + + // Request center pane and view. + xConfigurationController->requestResourceActivation(xCenterPaneId, ResourceActivationMode_ADD); + xConfigurationController->requestResourceActivation(xCenterViewId, ResourceActivationMode_REPLACE); + + // Process configuration events synchronously until the center view + // has been created. + sd::framework::ConfigurationController* pConfigurationController + = dynamic_cast(xConfigurationController.get()); + if (pConfigurationController != nullptr) + { + while ( + ! pConfigurationController->getResource(xCenterViewId).is() + && pConfigurationController->hasPendingRequests()) + { + pConfigurationController->ProcessEvent(); + } + } + } + } + catch (const RuntimeException&) + { + } + + // AutoLayouts have to be ready. + GetDocument()->StopWorkStartupDelay(); + + UpdateBorder(); + + // Remember the type of the current main view shell in the frame view. + ViewShell* pViewShell = GetMainViewShell().get(); + if (pViewShell != nullptr) + { + FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView != nullptr) + pFrameView->SetViewShellTypeOnLoad(pViewShell->GetShellType()); + } + // Show/Hide the TabBar + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDocument()->GetDocumentType()); + bool bIsTabBarVisible = pOptions->IsTabBarVisible(); + mpImpl->SetUserWantsTabBar( bIsTabBarVisible ); +} + +std::shared_ptr const & ViewShellBase::GetViewShellManager() const +{ + return mpImpl->mpViewShellManager; +} + +std::shared_ptr ViewShellBase::GetMainViewShell() const +{ + std::shared_ptr pMainViewShell ( + framework::FrameworkHelper::Instance(*const_cast(this)) + ->GetViewShell(framework::FrameworkHelper::msCenterPaneURL)); + if (pMainViewShell == nullptr) + pMainViewShell = framework::FrameworkHelper::Instance(*const_cast(this)) + ->GetViewShell(framework::FrameworkHelper::msFullScreenPaneURL); + return pMainViewShell; +} + +ViewShellBase* ViewShellBase::GetViewShellBase (SfxViewFrame const * pViewFrame) +{ + ViewShellBase* pBase = nullptr; + + if (pViewFrame != nullptr) + { + // Get the view shell for the frame and cast it to + // sd::ViewShellBase. + SfxViewShell* pSfxViewShell = pViewFrame->GetViewShell(); + pBase = dynamic_cast< ::sd::ViewShellBase *>( pSfxViewShell ); + } + + return pBase; +} + +void ViewShellBase::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) +{ + SfxViewShell::Notify(rBC, rHint); + + const SfxEventHint* pEventHint = dynamic_cast(&rHint); + if (pEventHint) + { + switch (pEventHint->GetEventId()) + { + case SfxEventHintId::OpenDoc: + if( GetDocument() && GetDocument()->IsStartWithPresentation() ) + { + if( GetViewFrame() ) + { + GetViewFrame()->GetDispatcher()->Execute( + SID_PRESENTATION, SfxCallMode::ASYNCHRON ); + } + } + break; + + default: + break; + } + } + else + { + const SfxHintId nSlot = rHint.GetId(); + switch ( nSlot ) + { + case SfxHintId::LanguageChanged: + { + GetViewFrame()->GetBindings().Invalidate(SID_LANGUAGE_STATUS); + } + break; + + default: + break; + } + } +} + +void ViewShellBase::InitializeFramework() +{ +} + +OUString ViewShellBase::GetSelectionText(bool bCompleteWords, bool /*bOnlyASample*/) +{ + std::shared_ptr const pMainShell(GetMainViewShell()); + DrawViewShell *const pDrawViewShell( + dynamic_cast(pMainShell.get())); + return pDrawViewShell + ? pDrawViewShell->GetSelectionText(bCompleteWords) + : SfxViewShell::GetSelectionText(bCompleteWords); +} + +bool ViewShellBase::HasSelection(bool bText) const +{ + std::shared_ptr const pMainShell(GetMainViewShell()); + DrawViewShell *const pDrawViewShell( + dynamic_cast(pMainShell.get())); + return pDrawViewShell + ? pDrawViewShell->HasSelection(bText) + : SfxViewShell::HasSelection(bText); +} + +void ViewShellBase::InnerResizePixel (const Point& rOrigin, const Size &rSize, bool) +{ + Size aObjSize = GetObjectShell()->GetVisArea().GetSize(); + if ( !aObjSize.IsEmpty() ) + { + SvBorder aBorder( GetBorderPixel() ); + Size aSize( rSize ); + aSize.AdjustWidth( -(aBorder.Left() + aBorder.Right()) ); + aSize.AdjustHeight( -(aBorder.Top() + aBorder.Bottom()) ); + Size aObjSizePixel = mpImpl->mpViewWindow->LogicToPixel(aObjSize, MapMode(MapUnit::Map100thMM)); + SfxViewShell::SetZoomFactor( + Fraction( aSize.Width(), std::max( aObjSizePixel.Width(), static_cast<::tools::Long>(1) ) ), + Fraction( aSize.Height(), std::max( aObjSizePixel.Height(), static_cast<::tools::Long>(1)) ) ); + } + + mpImpl->ResizePixel(rOrigin, rSize, false); +} + +void ViewShellBase::OuterResizePixel (const Point& rOrigin, const Size &rSize) +{ + mpImpl->ResizePixel (rOrigin, rSize, true); +} + +void ViewShellBase::Rearrange() +{ + OSL_ASSERT(GetViewFrame()!=nullptr); + + // There is a bug in the communication between embedded objects and the + // framework::LayoutManager that leads to missing resize updates. The + // following workaround enforces such an update by cycling the border to + // zero and back to the current value. + if (GetWindow() != nullptr) + { + SetBorderPixel(SvBorder()); + UpdateBorder(true); + } + else + { + SAL_WARN("sd.view", "Rearrange: window missing"); + } + + GetViewFrame()->Resize(true); +} + +ErrCode ViewShellBase::DoVerb(sal_Int32 nVerb) +{ + ErrCode aResult = ERRCODE_NONE; + + ::sd::ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + aResult = pShell->DoVerb(nVerb); + + return aResult; +} + +Reference ViewShellBase::GetRenderable() +{ + // Create a new DocumentRenderer on every call. It observes the life + // time of this ViewShellBase object. + return Reference(new DocumentRenderer(*this)); +} + +SfxPrinter* ViewShellBase::GetPrinter (bool bCreate) +{ + OSL_ASSERT(mpImpl != nullptr); + + return GetDocShell()->GetPrinter (bCreate); +} + +sal_uInt16 ViewShellBase::SetPrinter ( + SfxPrinter* pNewPrinter, + SfxPrinterChangeFlags nDiffFlags) +{ + OSL_ASSERT(mpImpl != nullptr); + + GetDocShell()->SetPrinter(pNewPrinter); + + if ( (nDiffFlags & SfxPrinterChangeFlags::CHG_ORIENTATION || + nDiffFlags & SfxPrinterChangeFlags::CHG_SIZE) && pNewPrinter ) + { + MapMode aMap = pNewPrinter->GetMapMode(); + aMap.SetMapUnit(MapUnit::Map100thMM); + MapMode aOldMap = pNewPrinter->GetMapMode(); + pNewPrinter->SetMapMode(aMap); + Size aNewSize = pNewPrinter->GetOutputSize(); + + std::shared_ptr pDrawViewShell ( + std::dynamic_pointer_cast(GetMainViewShell())); + if (pDrawViewShell) + { + SdPage* pPage = GetDocument()->GetSdPage( + 0, PageKind::Standard ); + pDrawViewShell->SetPageSizeAndBorder ( + pDrawViewShell->GetPageKind(), + aNewSize, + -1,-1,-1,-1, + false/*bScaleAll*/, + pNewPrinter->GetOrientation(), + pPage->GetPaperBin(), + pPage->IsBackgroundFullSize()); + } + + pNewPrinter->SetMapMode(aOldMap); + } + + return 0; +} + +void ViewShellBase::UIActivating( SfxInPlaceClient* pClient ) +{ + mpImpl->ShowViewTabBar(false); + + ViewShell* pViewShell = GetMainViewShell().get(); + if ( pViewShell ) + pViewShell->UIActivating( pClient ); + + SfxViewShell::UIActivating( pClient ); +} + +void ViewShellBase::UIDeactivated( SfxInPlaceClient* pClient ) +{ + SfxViewShell::UIDeactivated( pClient ); + + mpImpl->ShowViewTabBar(true); + + ViewShell* pViewShell = GetMainViewShell().get(); + if ( pViewShell ) + pViewShell->UIDeactivated( pClient ); +} + +SvBorder ViewShellBase::GetBorder (bool ) +{ + int nTop = 0; + if (mpImpl->mpViewTabBar.is() && mpImpl->mpViewTabBar->GetTabControl()->IsVisible()) + nTop = mpImpl->mpViewTabBar->GetHeight(); + return SvBorder(0,nTop,0,0); +} + +void ViewShellBase::Execute (SfxRequest& rRequest) +{ + sal_uInt16 nSlotId = rRequest.GetSlot(); + + switch (nSlotId) + { + case SID_SWITCH_SHELL: + { + Reference xControllerManager (GetController(), UNO_QUERY); + if (xControllerManager.is()) + { + Reference xConfigurationController ( + xControllerManager->getConfigurationController()); + if (xConfigurationController.is()) + xConfigurationController->update(); + } + } + break; + + case SID_LEFT_PANE_DRAW: + mpImpl->SetPaneVisibility( + rRequest, + framework::FrameworkHelper::msLeftDrawPaneURL, + framework::FrameworkHelper::msSlideSorterURL); + break; + + case SID_LEFT_PANE_IMPRESS: + mpImpl->SetPaneVisibility( + rRequest, + framework::FrameworkHelper::msLeftImpressPaneURL, + framework::FrameworkHelper::msSlideSorterURL); + break; + + case SID_TOGGLE_TABBAR_VISIBILITY: + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDocument()->GetDocumentType()); + bool bIsTabBarVisible = pOptions->IsTabBarVisible(); + pOptions->SetTabBarVisible( !bIsTabBarVisible ); + mpImpl->SetUserWantsTabBar( !bIsTabBarVisible ); + rRequest.Done(); + } + break; + + // draw + case SID_DRAWINGMODE: + // impress normal + case SID_NORMAL_MULTI_PANE_GUI: + case SID_NOTES_MODE: + case SID_OUTLINE_MODE: + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + // impress master + case SID_SLIDE_MASTER_MODE: + case SID_NOTES_MASTER_MODE: + case SID_HANDOUT_MASTER_MODE: + framework::FrameworkHelper::Instance(*this)->HandleModeChangeSlot(nSlotId, rRequest); + break; + + case SID_WIN_FULLSCREEN: + // The full screen mode is not supported. Ignore the request. + break; + + case SID_RESTORE_EDITING_VIEW: + mpImpl->ProcessRestoreEditingViewSlot(); + break; + + default: + // Ignore any other slot. + rRequest.Ignore (); + break; + } +} + +void ViewShellBase::GetState (SfxItemSet& rSet) +{ + mpImpl->GetSlotState(rSet); + + FuBullet::GetSlotState( rSet, nullptr, GetViewFrame() ); +} + +void ViewShellBase::WriteUserDataSequence ( + css::uno::Sequence< css::beans::PropertyValue >& rSequence) +{ + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + pShell->WriteUserDataSequence (rSequence); +} + +void ViewShellBase::ReadUserDataSequence ( + const css::uno::Sequence< css::beans::PropertyValue >& rSequence) +{ + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell == nullptr) + return; + + pShell->ReadUserDataSequence (rSequence); + + // For certain shell types ReadUserDataSequence may have changed the + // type to another one. Make sure that the center pane shows the + // right view shell. + switch (pShell->GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_NOTES: + case ViewShell::ST_HANDOUT: + { + OUString sViewURL; + switch (dynamic_cast(*pShell).GetPageKind()) + { + default: + case PageKind::Standard: + sViewURL = framework::FrameworkHelper::msImpressViewURL; + break; + case PageKind::Notes: + sViewURL = framework::FrameworkHelper::msNotesViewURL; + break; + case PageKind::Handout: + sViewURL = framework::FrameworkHelper::msHandoutViewURL; + break; + } + if (!sViewURL.isEmpty()) + framework::FrameworkHelper::Instance(*this)->RequestView( + sViewURL, + framework::FrameworkHelper::msCenterPaneURL); + } + break; + + default: + break; + } +} + +void ViewShellBase::Activate (bool bIsMDIActivate) +{ + SfxViewShell::Activate(bIsMDIActivate); + + Reference xControllerManager (GetController(), UNO_QUERY); + if (xControllerManager.is()) + { + Reference xConfigurationController ( + xControllerManager->getConfigurationController()); + if (xConfigurationController.is()) + xConfigurationController->update(); + } + GetToolBarManager()->RequestUpdate(); +} + +void ViewShellBase::SetZoomFactor ( + const Fraction &rZoomX, + const Fraction &rZoomY) +{ + SfxViewShell::SetZoomFactor (rZoomX, rZoomY); + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + pShell->SetZoomFactor (rZoomX, rZoomY); +} + +bool ViewShellBase::PrepareClose (bool bUI) +{ + bool bResult = SfxViewShell::PrepareClose (bUI); + + if (bResult) + { + mpImpl->mbIsClosing = true; + + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + bResult = pShell->PrepareClose (bUI); + } + + return bResult; +} + +void ViewShellBase::WriteUserData (OUString& rString, bool bBrowse) +{ + SfxViewShell::WriteUserData (rString, bBrowse); + + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + pShell->WriteUserData(); +} + +void ViewShellBase::ReadUserData (const OUString& rString, bool bBrowse) +{ + SfxViewShell::ReadUserData (rString, bBrowse); + + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + pShell->ReadUserData(); +} + +SdrView* ViewShellBase::GetDrawView() const +{ + // Forward call to main sub shell. + ViewShell* pShell = GetMainViewShell().get(); + if (pShell != nullptr) + return pShell->GetDrawView (); + else + return SfxViewShell::GetDrawView(); +} + +void ViewShellBase::SetBusyState (bool bBusy) +{ + if (GetDocShell() != nullptr) + GetDocShell()->SetWaitCursor (bBusy); +} + +void ViewShellBase::UpdateBorder ( bool bForce /* = false */ ) +{ + // The following calls to SetBorderPixel() and InvalidateBorder() are + // made only for the main view shell. This not only avoids unnecessary + // calls for the views in side panes but prevents calling an already + // dying SfxViewShell base class. + // We have to check the existence of the window, too. + // The SfxViewFrame accesses the window without checking it. + ViewShell* pMainViewShell = GetMainViewShell().get(); + if (pMainViewShell == nullptr || GetWindow()==nullptr) + return; + + SvBorder aCurrentBorder (GetBorderPixel()); + bool bOuterResize ( ! GetDocShell()->IsInPlaceActive()); + SvBorder aBorder (GetBorder(bOuterResize)); + aBorder += pMainViewShell->GetBorder(); + + if (bForce || (aBorder != aCurrentBorder)) + { + SetBorderPixel (aBorder); + InvalidateBorder(); + } +} + +void ViewShellBase::ShowUIControls (bool bVisible) +{ + mpImpl->ShowViewTabBar(bVisible); + + ViewShell* pMainViewShell = GetMainViewShell().get(); + if (pMainViewShell != nullptr) + pMainViewShell->ShowUIControls (bVisible); + + UpdateBorder(); + if (bVisible) + Rearrange(); +} + +OUString ViewShellBase::GetInitialViewShellType() const +{ + OUString sRequestedView (FrameworkHelper::msImpressViewURL); + + do + { + Reference xViewDataSupplier ( + GetDocShell()->GetModel(), UNO_QUERY); + if ( ! xViewDataSupplier.is()) + break; + + Reference xViewData (xViewDataSupplier->getViewData()); + if ( ! xViewData.is()) + break; + if (xViewData->getCount() == 0) + break; + + css::uno::Any aAny = xViewData->getByIndex(0); + Sequence aProperties; + if ( ! (aAny >>= aProperties)) + break; + + // Search the properties for the one that tells us what page kind to + // use. + auto pProperty = std::find_if(std::cbegin(aProperties), std::cend(aProperties), + [](const beans::PropertyValue& rProperty) { return rProperty.Name == sUNO_View_PageKind; }); + if (pProperty != std::cend(aProperties)) + { + sal_Int16 nPageKind = 0; + pProperty->Value >>= nPageKind; + switch (static_cast(nPageKind)) + { + case PageKind::Standard: + sRequestedView = FrameworkHelper::msImpressViewURL; + break; + + case PageKind::Handout: + sRequestedView = FrameworkHelper::msHandoutViewURL; + break; + + case PageKind::Notes: + sRequestedView = FrameworkHelper::msNotesViewURL; + break; + + default: + // The page kind is invalid. This is probably an + // error by the caller. We use the standard type to + // keep things going. + SAL_WARN( "sd.view", "ViewShellBase::GetInitialViewShellType: invalid page kind"); + sRequestedView = FrameworkHelper::msImpressViewURL; + break; + } + } + } + while (false); + + return sRequestedView; +} + +std::shared_ptr const & ViewShellBase::GetEventMultiplexer() const +{ + OSL_ASSERT(mpImpl != nullptr); + OSL_ASSERT(mpImpl->mpEventMultiplexer != nullptr); + + return mpImpl->mpEventMultiplexer; +} + +const ::tools::Rectangle& ViewShellBase::getClientRectangle() const +{ + return mpImpl->maClientArea; +} + +std::shared_ptr const & ViewShellBase::GetToolBarManager() const +{ + OSL_ASSERT(mpImpl != nullptr); + OSL_ASSERT(mpImpl->mpToolBarManager != nullptr); + + return mpImpl->mpToolBarManager; +} + +std::shared_ptr const & ViewShellBase::GetFormShellManager() const +{ + OSL_ASSERT(mpImpl != nullptr); + OSL_ASSERT(mpImpl->mpFormShellManager != nullptr); + + return mpImpl->mpFormShellManager; +} + +DrawController& ViewShellBase::GetDrawController() const +{ + OSL_ASSERT(mpImpl != nullptr); + + return *mpImpl->mpController; +} + +void ViewShellBase::SetViewTabBar (const ::rtl::Reference& rViewTabBar) +{ + OSL_ASSERT(mpImpl != nullptr); + + mpImpl->mpViewTabBar = rViewTabBar; +} + +vcl::Window* ViewShellBase::GetViewWindow() +{ + OSL_ASSERT(mpImpl != nullptr); + + return mpImpl->mpViewWindow.get(); +} + +OUString ViewShellBase::RetrieveLabelFromCommand( const OUString& aCmdURL ) const +{ + OUString aModuleName(vcl::CommandInfoProvider::GetModuleIdentifier(GetMainViewShell()->GetViewFrame()->GetFrame().GetFrameInterface())); + auto aProperties = vcl::CommandInfoProvider::GetCommandProperties(aCmdURL, aModuleName); + return vcl::CommandInfoProvider::GetLabelForCommand(aProperties); +} + +int ViewShellBase::getPart() const +{ + ViewShell* pViewShell = framework::FrameworkHelper::Instance(*const_cast(this))->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + + if (DrawViewShell* pDrawViewShell = dynamic_cast(pViewShell)) + { + return pDrawViewShell->GetCurPagePos(); + } + + return 0; +} + +void ViewShellBase::NotifyCursor(SfxViewShell* pOtherShell) const +{ + ViewShell* pThisShell = framework::FrameworkHelper::Instance(*const_cast(this))->GetViewShell(FrameworkHelper::msCenterPaneURL).get(); + + DrawViewShell* pDrawViewShell = dynamic_cast(pThisShell); + if (!pDrawViewShell) + return; + + if (this == pOtherShell) + return; + + DrawView* pDrawView = pDrawViewShell->GetDrawView(); + if (!pDrawView) + return; + + if (pDrawView->GetTextEditObject()) + { + // Blinking cursor. + EditView& rEditView = pDrawView->GetTextEditOutlinerView()->GetEditView(); + rEditView.RegisterOtherShell(pOtherShell); + rEditView.ShowCursor(); + rEditView.RegisterOtherShell(nullptr); + // Text selection, if any. + rEditView.DrawSelectionXOR(pOtherShell); + + // Shape text lock. + if (OutlinerView* pOutlinerView = pDrawView->GetTextEditOutlinerView()) + { + ::tools::Rectangle aRectangle = pOutlinerView->GetOutputArea(); + vcl::Window* pWin = pThisShell->GetActiveWindow(); + if (pWin && pWin->GetMapMode().GetMapUnit() == MapUnit::Map100thMM) + aRectangle = o3tl::toTwips(aRectangle, o3tl::Length::mm100); + OString sRectangle = aRectangle.toString(); + SfxLokHelper::notifyOtherView(&pDrawViewShell->GetViewShellBase(), pOtherShell, LOK_CALLBACK_VIEW_LOCK, "rectangle", sRectangle); + } + } + else + { + // Graphic selection. + pDrawView->AdjustMarkHdl(pOtherShell); + } +} + +//===== ViewShellBase::Implementation ========================================= + +ViewShellBase::Implementation::Implementation (ViewShellBase& rBase) + : mbIsClosing(false), + mrBase(rBase), + mbUserWantsTabBar(false), + mbTabBarShouldBeVisible(true), + mpPageCacheManager(slidesorter::cache::PageCacheManager::Instance()) +{ +} + +ViewShellBase::Implementation::~Implementation() +{ + mpController = nullptr; + mpViewTabBar = nullptr; + mpViewWindow.disposeAndClear(); + mpToolBarManager.reset(); +} + +void ViewShellBase::Implementation::LateInit() +{ + mpController = new DrawController(mrBase); +} + +void ViewShellBase::Implementation::ProcessRestoreEditingViewSlot() +{ + ViewShell* pViewShell = mrBase.GetMainViewShell().get(); + if (pViewShell == nullptr) + return; + + FrameView* pFrameView = pViewShell->GetFrameView(); + if (pFrameView == nullptr) + return; + + // Set view shell, edit mode, and page kind. + // pFrameView->SetViewShEditMode( + // pFrameView->GetViewShEditModeOnLoad(), + // pFrameView->GetPageKindOnLoad()); + pFrameView->SetViewShEditMode( + pFrameView->GetViewShEditModeOnLoad() ); + pFrameView->SetPageKind( + pFrameView->GetPageKindOnLoad()); + std::shared_ptr pHelper (FrameworkHelper::Instance(mrBase)); + pHelper->RequestView( + FrameworkHelper::GetViewURL(pFrameView->GetViewShellTypeOnLoad()), + FrameworkHelper::msCenterPaneURL); + pHelper->RunOnConfigurationEvent("ConfigurationUpdateEnd", CurrentPageSetter(mrBase)); +} + +void ViewShellBase::Implementation::SetUserWantsTabBar(bool inValue) +{ + mbUserWantsTabBar = inValue; + // Call ShowViewTabBar to refresh the TabBar visibility + ShowViewTabBar(mbTabBarShouldBeVisible); +} + +void ViewShellBase::Implementation::ShowViewTabBar (bool bShow) +{ + mbTabBarShouldBeVisible = bShow; + bShow = bShow && mbUserWantsTabBar; + + if (mpViewTabBar.is() + && mpViewTabBar->GetTabControl()->IsVisible() != bShow) + { + mpViewTabBar->GetTabControl()->Show(bShow); + mrBase.Rearrange(); + } +} + +void ViewShellBase::Implementation::ResizePixel ( + const Point& rOrigin, + const Size &rSize, + bool bOuterResize) +{ + if (mbIsClosing) + return; + + // Forward the call to both the base class and the main stacked sub + // shell only when main sub shell exists. + ViewShell* pMainViewShell = mrBase.GetMainViewShell().get(); + + // Set the ViewTabBar temporarily to full size so that, when asked + // later, it can return its true height. + mrBase.SetWindow (mpViewWindow.get()); + if (mpViewTabBar.is() && mpViewTabBar->GetTabControl()->IsVisible()) + mpViewTabBar->GetTabControl()->SetPosSizePixel (rOrigin, rSize); + + // Calculate and set the border before the controls are placed. + SvBorder aBorder; + if (pMainViewShell != nullptr) + aBorder = pMainViewShell->GetBorder(); + aBorder += mrBase.GetBorder(bOuterResize); + if (mrBase.GetBorderPixel() != aBorder) + mrBase.SetBorderPixel(aBorder); + + // Place the ViewTabBar at the top. It is part of the border. + SvBorder aBaseBorder; + if (mpViewTabBar.is() && mpViewTabBar->GetTabControl()->IsVisible()) + { + aBaseBorder.Top() = mpViewTabBar->GetHeight(); + mpViewTabBar->GetTabControl()->SetPosSizePixel( + rOrigin, Size(rSize.Width(),aBaseBorder.Top())); + } + + // The view window gets the remaining space. + Point aViewWindowPosition ( + rOrigin.X()+aBaseBorder.Left(), + rOrigin.Y()+aBaseBorder.Top()); + + Size aViewWindowSize ( + rSize.Width() - aBaseBorder.Left() - aBaseBorder.Right(), + rSize.Height() - aBaseBorder.Top() - aBaseBorder.Bottom()); + mpViewWindow->SetPosSizePixel(aViewWindowPosition, aViewWindowSize); + + maClientArea = ::tools::Rectangle(Point(0,0), aViewWindowSize); +} + +void ViewShellBase::Implementation::SetPaneVisibility ( + const SfxRequest& rRequest, + const OUString& rsPaneURL, + const OUString& rsViewURL) +{ + try + { + Reference xControllerManager (mrBase.GetController(), UNO_QUERY_THROW); + + const Reference< XComponentContext > xContext( + ::comphelper::getProcessComponentContext() ); + Reference xPaneId (ResourceId::create( + xContext, rsPaneURL)); + Reference xViewId (ResourceId::createWithAnchorURL( + xContext, rsViewURL, rsPaneURL)); + + // Determine the new visibility state. + const SfxItemSet* pArguments = rRequest.GetArgs(); + bool bShowChildWindow; + sal_uInt16 nSlotId = rRequest.GetSlot(); + if (pArguments != nullptr) + bShowChildWindow = static_cast( + pArguments->Get(nSlotId)).GetValue(); + else + { + Reference xConfigurationController ( + xControllerManager->getConfigurationController()); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + Reference xConfiguration ( + xConfigurationController->getRequestedConfiguration()); + if ( ! xConfiguration.is()) + throw RuntimeException(); + + bShowChildWindow = ! xConfiguration->hasResource(xPaneId); + } + + // Set the desired visibility state at the current configuration + // and update it accordingly. + Reference xConfigurationController ( + xControllerManager->getConfigurationController()); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + if (bShowChildWindow) + { + xConfigurationController->requestResourceActivation( + xPaneId, + ResourceActivationMode_ADD); + xConfigurationController->requestResourceActivation( + xViewId, + ResourceActivationMode_REPLACE); + } + else + xConfigurationController->requestResourceDeactivation( + xPaneId); + } + catch (const Exception&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } +} + +void ViewShellBase::Implementation::GetSlotState (SfxItemSet& rSet) +{ + try + { + // Get some frequently used values. + Reference xControllerManager (mrBase.GetController(), UNO_QUERY_THROW); + Reference xConfigurationController ( + xControllerManager->getConfigurationController()); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + Reference xConfiguration ( + xConfigurationController->getRequestedConfiguration()); + if ( ! xConfiguration.is()) + throw RuntimeException(); + + const Reference< XComponentContext > xContext( + ::comphelper::getProcessComponentContext() ); + SfxWhichIter aSetIterator (rSet); + sal_uInt16 nItemId (aSetIterator.FirstWhich()); + + while (nItemId > 0) + { + bool bState (false); + Reference xResourceId; + try + { + // Check if the right view is active + switch (nItemId) + { + case SID_LEFT_PANE_IMPRESS: + xResourceId = ResourceId::create( + xContext, FrameworkHelper::msLeftImpressPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_LEFT_PANE_DRAW: + xResourceId = ResourceId::create( + xContext, FrameworkHelper::msLeftDrawPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_DRAWINGMODE: + case SID_NORMAL_MULTI_PANE_GUI: + case SID_SLIDE_MASTER_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, FrameworkHelper::msImpressViewURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_SLIDE_SORTER_MULTI_PANE_GUI: + case SID_SLIDE_SORTER_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, + FrameworkHelper::msSlideSorterURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_OUTLINE_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, + FrameworkHelper::msOutlineViewURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_HANDOUT_MASTER_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, FrameworkHelper::msHandoutViewURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + xResourceId = ResourceId::createWithAnchorURL( + xContext, FrameworkHelper::msNotesViewURL, + FrameworkHelper::msCenterPaneURL); + bState = xConfiguration->hasResource(xResourceId); + break; + + case SID_TOGGLE_TABBAR_VISIBILITY: + bState = GetUserWantsTabBar(); + break; + + default: + // Ignore all other items. They are not meant to be + // handled by us. + break; + } + } + catch (const DeploymentException&) + { + } + + // Check if edit mode fits too + if (bState) + { + ViewShell* const pCenterViewShell = FrameworkHelper::Instance(mrBase)->GetViewShell( + FrameworkHelper::msCenterPaneURL).get(); + DrawViewShell* const pShell = dynamic_cast(pCenterViewShell); + if (pShell) + { + switch (nItemId) + { + case SID_DRAWINGMODE: + case SID_NORMAL_MULTI_PANE_GUI: + case SID_NOTES_MODE: + bState = pShell->GetEditMode() == EditMode::Page; + break; + case SID_SLIDE_MASTER_MODE: + case SID_NOTES_MASTER_MODE: + bState = pShell->GetEditMode() == EditMode::MasterPage; + break; + } + } + } + + // And finally set the state. + rSet.Put(SfxBoolItem(nItemId, bState)); + + nItemId = aSetIterator.NextWhich(); + } + } + catch (const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + +} + +} // end of namespace sd + +//===== CurrentPageSetter =========================================== + +namespace { + +CurrentPageSetter::CurrentPageSetter (ViewShellBase& rBase) + : mrBase(rBase) +{ +} + +void CurrentPageSetter::operator() (bool) +{ + FrameView* pFrameView = nullptr; + + if (mrBase.GetMainViewShell() != nullptr) + { + pFrameView = mrBase.GetMainViewShell()->GetFrameView(); + } + + if (pFrameView==nullptr) + return; + + try + { + // Get the current page either from the DrawPagesSupplier or the + // MasterPagesSupplier. + Any aPage; + if (pFrameView->GetViewShEditModeOnLoad() == EditMode::Page) + { + Reference xPagesSupplier ( + mrBase.GetController()->getModel(), UNO_QUERY_THROW); + Reference xPages ( + xPagesSupplier->getDrawPages(), UNO_QUERY_THROW); + aPage = xPages->getByIndex(pFrameView->GetSelectedPageOnLoad()); + } + else + { + Reference xPagesSupplier ( + mrBase.GetController()->getModel(), UNO_QUERY_THROW); + Reference xPages ( + xPagesSupplier->getMasterPages(), UNO_QUERY_THROW); + aPage = xPages->getByIndex(pFrameView->GetSelectedPageOnLoad()); + } + // Switch to the page last edited by setting the CurrentPage + // property. + Reference xSet (mrBase.GetController(), UNO_QUERY_THROW); + xSet->setPropertyValue ("CurrentPage", aPage); + } + catch (const RuntimeException&) + { + // We have not been able to set the current page at the main view. + // This is sad but still leaves us in a valid state. Therefore, + // this exception is silently ignored. + } + catch (const beans::UnknownPropertyException&) + { + SAL_WARN("sd.view", "CurrentPage property unknown"); + } +} + +} // end of anonymous namespace + +//===== FocusForwardingWindow ================================================= + +namespace sd { +namespace { + +FocusForwardingWindow::FocusForwardingWindow ( + vcl::Window& rParentWindow, + ViewShellBase& rBase) + : vcl::Window(&rParentWindow, WinBits(WB_CLIPCHILDREN | WB_DIALOGCONTROL)), + mrBase(rBase) +{ + SAL_INFO("sd.view", "created FocusForwardingWindow at " << this); +} + +FocusForwardingWindow::~FocusForwardingWindow() +{ + disposeOnce(); +} + +void FocusForwardingWindow::dispose() +{ + SAL_INFO("sd.view", "destroyed FocusForwardingWindow at " << this); + vcl::Window::dispose(); +} + +void FocusForwardingWindow::KeyInput (const KeyEvent& rKEvt) +{ + std::shared_ptr pViewShell = mrBase.GetMainViewShell(); + if (pViewShell != nullptr) + { + vcl::Window* pWindow = pViewShell->GetActiveWindow(); + if (pWindow != nullptr) + { + // Forward the focus so that the window is called directly the + // next time. + pWindow->GrabFocus(); + // Forward the key press as well. + pWindow->KeyInput(rKEvt); + } + } +} + +void FocusForwardingWindow::Command (const CommandEvent& rEvent) +{ + std::shared_ptr pViewShell = mrBase.GetMainViewShell(); + if (pViewShell != nullptr) + { + vcl::Window* pWindow = pViewShell->GetActiveWindow(); + if (pWindow != nullptr) + { + pWindow->Command(rEvent); + } + } +} + +} // end of anonymous namespace + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewShellHint.cxx b/sd/source/ui/view/ViewShellHint.cxx new file mode 100644 index 000000000..b86cbaa32 --- /dev/null +++ b/sd/source/ui/view/ViewShellHint.cxx @@ -0,0 +1,31 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +namespace sd +{ +ViewShellHint::ViewShellHint(HintId eHintId) + : meHintId(eHintId) +{ +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewShellImplementation.cxx b/sd/source/ui/view/ViewShellImplementation.cxx new file mode 100644 index 000000000..a0c025ce5 --- /dev/null +++ b/sd/source/ui/view/ViewShellImplementation.cxx @@ -0,0 +1,379 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; + +namespace sd { + +ViewShell::Implementation::Implementation (ViewShell& rViewShell) + : mbIsMainViewShell(false), + mbIsInitialized(false), + mbArrangeActive(false), + mrViewShell(rViewShell) +{ +} + +ViewShell::Implementation::~Implementation() COVERITY_NOEXCEPT_FALSE +{ + if ( ! mpUpdateLockForMouse.expired()) + { + std::shared_ptr pLock(mpUpdateLockForMouse); + if (pLock != nullptr) + { + // Force the ToolBarManagerLock to be released even when the + // IsUICaptured() returns . + pLock->Release(true); + } + } +} + +void ViewShell::Implementation::ProcessModifyPageSlot ( + SfxRequest& rRequest, + SdPage* pCurrentPage, + PageKind ePageKind) +{ + SdDrawDocument* pDocument = mrViewShell.GetDoc(); + SdrLayerAdmin& rLayerAdmin = pDocument->GetLayerAdmin(); + SdrLayerIDSet aVisibleLayers; + bool bHandoutMode = false; + SdPage* pHandoutMPage = nullptr; + OUString aNewName; + + AutoLayout aNewAutoLayout; + + bool bBVisible; + bool bBObjsVisible; + const SfxItemSet* pArgs = rRequest.GetArgs(); + + if (pCurrentPage != nullptr && pCurrentPage->TRG_HasMasterPage()) + aVisibleLayers = pCurrentPage->TRG_GetMasterPageVisibleLayers(); + else + aVisibleLayers.SetAll(); + + do + { + if (pCurrentPage == nullptr) + break; + + if (!pArgs || pArgs->Count() == 1 || pArgs->Count() == 2 ) + { + // First make sure that the sidebar is visible + mrViewShell.GetDrawView()->SdrEndTextEdit(); + mrViewShell.GetDrawView()->UnmarkAll(); + mrViewShell.GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + sfx2::sidebar::Sidebar::TogglePanel( + u"SdLayoutsPanel", + mrViewShell.GetViewFrame()->GetFrame().GetFrameInterface()); + break; + } + else if (pArgs->Count() == 4) + { + const SfxStringItem* pNewName = rRequest.GetArg(ID_VAL_PAGENAME); + const SfxUInt32Item* pNewAutoLayout = rRequest.GetArg(ID_VAL_WHATLAYOUT); + const SfxBoolItem* pBVisible = rRequest.GetArg(ID_VAL_ISPAGEBACK); + const SfxBoolItem* pBObjsVisible = rRequest.GetArg(ID_VAL_ISPAGEOBJ); + AutoLayout aLayout (static_cast(pNewAutoLayout->GetValue ())); + if (aLayout >= AUTOLAYOUT_START + && aLayout < AUTOLAYOUT_END) + { + aNewName = pNewName->GetValue (); + aNewAutoLayout = static_cast(pNewAutoLayout->GetValue ()); + bBVisible = pBVisible->GetValue (); + bBObjsVisible = pBObjsVisible->GetValue (); + } + else + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + rRequest.Ignore (); + break; + } + if (ePageKind == PageKind::Handout) + { + bHandoutMode = true; + pHandoutMPage = pDocument->GetMasterSdPage(0, PageKind::Handout); + } + } + else + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rRequest.Ignore (); + break; + } + + SdPage* pUndoPage = + bHandoutMode ? pHandoutMPage : pCurrentPage; + + SfxUndoManager* pUndoManager = mrViewShell.GetDocSh()->GetUndoManager(); + DBG_ASSERT(pUndoManager, "No UNDO MANAGER ?!?"); + + if( pUndoManager ) + { + OUString aComment( SdResId(STR_UNDO_MODIFY_PAGE) ); + pUndoManager->EnterListAction(aComment, aComment, 0, mrViewShell.GetViewShellBase().GetViewShellId()); + pUndoManager->AddUndoAction( + std::make_unique( + pDocument, pUndoPage, aNewName, aNewAutoLayout, bBVisible, bBObjsVisible)); + + // Clear the selection because the selected object may be removed as + // a result of the assignment of the layout. + mrViewShell.GetDrawView()->UnmarkAll(); + + if (!bHandoutMode) + { + if (pCurrentPage->GetName() != aNewName) + { + pCurrentPage->SetName(aNewName); + + if (ePageKind == PageKind::Standard) + { + sal_uInt16 nPage = (pCurrentPage->GetPageNum()-1) / 2; + SdPage* pNotesPage = pDocument->GetSdPage(nPage, PageKind::Notes); + if (pNotesPage != nullptr) + pNotesPage->SetName(aNewName); + } + } + + pCurrentPage->SetAutoLayout(aNewAutoLayout, true); + + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + aVisibleLayers.Set(aBckgrnd, bBVisible); + aVisibleLayers.Set(aBckgrndObj, bBObjsVisible); + pCurrentPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + else + { + pHandoutMPage->SetAutoLayout(aNewAutoLayout, true); + } + + mrViewShell.GetViewFrame()->GetDispatcher()->Execute(SID_SWITCHPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + bool bSetModified = true; + + if (pArgs->Count() == 1) + { + bSetModified = static_cast(pArgs->Get(SID_MODIFYPAGE)).GetValue(); + } + + pUndoManager->AddUndoAction( std::make_unique( *pUndoPage ) ); + pUndoManager->LeaveListAction(); + + pDocument->SetChanged(bSetModified); + } + } + while (false); + + mrViewShell.Cancel(); + rRequest.Done (); +} + +void ViewShell::Implementation::AssignLayout ( SfxRequest const & rRequest, PageKind ePageKind ) +{ + const SfxUInt32Item* pWhatPage = rRequest.GetArg(ID_VAL_WHATPAGE); + const SfxUInt32Item* pWhatLayout = rRequest.GetArg(ID_VAL_WHATLAYOUT); + + SdDrawDocument* pDocument = mrViewShell.GetDoc(); + if( !pDocument ) + return; + + SdPage* pPage = nullptr; + if( pWhatPage ) + { + pPage = pDocument->GetSdPage(static_cast(pWhatPage->GetValue()), ePageKind); + } + + if( pPage == nullptr ) + pPage = mrViewShell.getCurrentPage(); + + if( !pPage ) + return; + + AutoLayout eLayout = pPage->GetAutoLayout(); + + if( pWhatLayout ) + eLayout = static_cast< AutoLayout >( pWhatLayout->GetValue() ); + + // Transform the given request into the four argument form that is + // understood by ProcessModifyPageSlot(). + SdrLayerAdmin& rLayerAdmin (mrViewShell.GetViewShellBase().GetDocument()->GetLayerAdmin()); + SdrLayerID aBackground (rLayerAdmin.GetLayerID(sUNO_LayerName_background)); + SdrLayerID aBackgroundObject (rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects)); + + SdrLayerIDSet aVisibleLayers; + + if( pPage->GetPageKind() == PageKind::Handout ) + aVisibleLayers.SetAll(); + else + aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + + SfxRequest aRequest (mrViewShell.GetViewShellBase().GetViewFrame(), SID_MODIFYPAGE); + aRequest.AppendItem(SfxStringItem (ID_VAL_PAGENAME, pPage->GetName())); + aRequest.AppendItem(SfxUInt32Item (ID_VAL_WHATLAYOUT, eLayout)); + aRequest.AppendItem(SfxBoolItem(ID_VAL_ISPAGEBACK, aVisibleLayers.IsSet(aBackground))); + aRequest.AppendItem(SfxBoolItem(ID_VAL_ISPAGEOBJ, aVisibleLayers.IsSet(aBackgroundObject))); + + // Forward the call with the new arguments. + ProcessModifyPageSlot( aRequest, pPage, pPage->GetPageKind()); +} + +SfxInterfaceId ViewShell::Implementation::GetViewId() const +{ + switch (mrViewShell.GetShellType()) + { + case ViewShell::ST_IMPRESS: + case ViewShell::ST_NOTES: + case ViewShell::ST_HANDOUT: + return IMPRESS_FACTORY_ID; + + case ViewShell::ST_DRAW: + return DRAW_FACTORY_ID; + + case ViewShell::ST_OUTLINE: + return OUTLINE_FACTORY_ID; + + case ViewShell::ST_SLIDE_SORTER: + return SLIDE_SORTER_FACTORY_ID; + + case ViewShell::ST_PRESENTATION: + return PRESENTATION_FACTORY_ID; + + // Since we have to return a view id for every possible shell type + // and there is not (yet) a proper ViewShellBase sub class for the + // remaining types we chose the Impress factory as a fall back. + case ViewShell::ST_SIDEBAR: + case ViewShell::ST_NONE: + default: + return IMPRESS_FACTORY_ID; + } +} + +SvxIMapDlg* ViewShell::Implementation::GetImageMapDialog() +{ + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (!pViewFrm) + return nullptr; + + SfxChildWindow* pChildWindow = pViewFrm->GetChildWindow( + SvxIMapDlgChildWindow::GetChildWindowId()); + if (pChildWindow == nullptr) + return nullptr; + + return dynamic_cast(pChildWindow->GetController().get()); +} + +//===== ToolBarManagerLock ==================================================== + +class ViewShell::Implementation::ToolBarManagerLock::Deleter { public: + void operator() (ToolBarManagerLock* pObject) { delete pObject; } +}; + +std::shared_ptr + ViewShell::Implementation::ToolBarManagerLock::Create ( + const std::shared_ptr& rpManager) +{ + std::shared_ptr pLock ( + new ViewShell::Implementation::ToolBarManagerLock(rpManager), + ViewShell::Implementation::ToolBarManagerLock::Deleter()); + pLock->mpSelf = pLock; + return pLock; +} + +ViewShell::Implementation::ToolBarManagerLock::ToolBarManagerLock ( + const std::shared_ptr& rpManager) + : mpLock(new ToolBarManager::UpdateLock(rpManager)), + maTimer("sd ToolBarManagerLock maTimer") +{ + // Start a timer that will unlock the ToolBarManager update lock when + // that is not done explicitly by calling Release(). + maTimer.SetInvokeHandler(LINK(this,ToolBarManagerLock,TimeoutCallback)); + maTimer.SetTimeout(100); + maTimer.Start(); +} + +IMPL_LINK_NOARG(ViewShell::Implementation::ToolBarManagerLock, TimeoutCallback, Timer *, void) +{ + // If possible then release the lock now. Otherwise start the timer + // and try again later. + if (Application::IsUICaptured()) + { + maTimer.Start(); + } + else + { + mpSelf.reset(); + } +} + +void ViewShell::Implementation::ToolBarManagerLock::Release (bool bForce) +{ + // If possible then release the lock now. Otherwise try again when the + // timer expires. + if (bForce || ! Application::IsUICaptured()) + { + mpSelf.reset(); + } +} + +ViewShell::Implementation::ToolBarManagerLock::~ToolBarManagerLock() +{ + mpLock.reset(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewShellManager.cxx b/sd/source/ui/view/ViewShellManager.cxx new file mode 100644 index 000000000..db2ee5f8f --- /dev/null +++ b/sd/source/ui/view/ViewShellManager.cxx @@ -0,0 +1,1168 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace sd { + +namespace { + +/** The ShellDescriptor class is used to shells together with their ids and + the factory that was used to create the shell. + + The shell pointer may be NULL. In that case the shell is created on + demand by a factory. + + The factory pointer may be NULL. In that case the shell pointer is + given to the ViewShellManager. + + Shell pointer and factory pointer can but should not be NULL at the same + time. +*/ +class ShellDescriptor { +public: + SfxShell* mpShell; + ShellId mnId; + ViewShellManager::SharedShellFactory mpFactory; + bool mbIsListenerAddedToWindow; + + ShellDescriptor (); + explicit ShellDescriptor (ShellId nId); + vcl::Window* GetWindow() const; +}; + +/** This functor can be used to search for a shell in an STL container when the + shell pointer is given. +*/ +class IsShell +{ +public: + explicit IsShell (const SfxShell* pShell) : mpShell(pShell) {} + bool operator() (const ShellDescriptor& rDescriptor) + { return rDescriptor.mpShell == mpShell; } +private: + const SfxShell* mpShell; +}; + +/** This functor can be used to search for a shell in an STL container when the + id of the shell is given. +*/ +class IsId +{ +public: + explicit IsId (ShellId nId) : mnId(nId) {} + bool operator() (const ShellDescriptor& rDescriptor) + { return rDescriptor.mnId == mnId; } +private: + ShellId mnId; +}; + +} // end of anonymous namespace + +class ViewShellManager::Implementation +{ +public: + Implementation ( + ViewShellBase& rBase); + ~Implementation() COVERITY_NOEXCEPT_FALSE; + + void AddShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory); + void RemoveShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory); + void ActivateViewShell ( + ViewShell* pViewShell); + void DeactivateViewShell (const ViewShell& rShell); + void ActivateShell (SfxShell& rShell); + void DeactivateShell (const SfxShell& rShell); + void ActivateShell (const ShellDescriptor& rDescriptor); + void SetFormShell (const ViewShell* pViewShell, FmFormShell* pFormShell, bool bAbove); + void ActivateSubShell (const SfxShell& rParentShell, ShellId nId); + void DeactivateSubShell (const SfxShell& rParentShell, ShellId nId); + void MoveToTop (const SfxShell& rParentShell); + SfxShell* GetShell (ShellId nId) const; + SfxShell* GetTopShell() const; + SfxShell* GetTopViewShell() const; + void Shutdown(); + void InvalidateAllSubShells (const SfxShell* pParentShell); + + /** Remove all shells from the SFX stack above and including the given + shell. + */ + void TakeShellsFromStack (const SfxShell* pShell); + + class UpdateLock + { + public: + explicit UpdateLock (Implementation& rImpl) : mrImpl(rImpl) {mrImpl.LockUpdate();} + ~UpdateLock() COVERITY_NOEXCEPT_FALSE {mrImpl.UnlockUpdate();} + private: + Implementation& mrImpl; + }; + + /** Prevent updates of the shell stack. While the sub shell manager is + locked it will update its internal data structures but not alter the + shell stack. Use this method when there are several modifications + to the shell stack to prevent multiple rebuilds of the shell stack + and resulting broadcasts. + */ + void LockUpdate(); + + /** Allow updates of the shell stack. This method has to be called the + same number of times as LockUpdate() to really allow a rebuild of + the shell stack. + */ + void UnlockUpdate(); + +private: + ViewShellBase& mrBase; + mutable ::osl::Mutex maMutex; + + class ShellHash { public: size_t operator()(const SfxShell* p) const { return reinterpret_cast(p);} }; + typedef std::unordered_multimap + FactoryList; + FactoryList maShellFactories; + + /** List of the active view shells. In order to create gather all shells + to put on the shell stack each view shell in this list is asked for + its sub-shells (typically toolbars). + */ + typedef std::list ActiveShellList; + ActiveShellList maActiveViewShells; + + typedef std::list SubShellSubList; + typedef std::unordered_map SubShellList; + SubShellList maActiveSubShells; + + /** In this member we remember what shells we have pushed on the shell + stack. + */ + typedef ::std::vector ShellStack; + + int mnUpdateLockCount; + + /** The UpdateShellStack() method can be called recursively. This flag + is used to communicate between different levels of invocation: if + the stack has been updated in an inner call the outer call can (has + to) stop and return immediately. + */ + bool mbShellStackIsUpToDate; + + SfxShell* mpFormShell; + const ViewShell* mpFormShellParent; + bool mbFormShellAboveParent; + + SfxShell* mpTopShell; + SfxShell* mpTopViewShell; + + + void UpdateShellStack(); + + void CreateShells(); + + /** This method rebuilds the stack of shells that are stacked upon the + view shell base. + */ + void CreateTargetStack (ShellStack& rStack) const; + + DECL_LINK(WindowEventHandler, VclWindowEvent&, void); + +#if OSL_DEBUG_LEVEL >= 2 + void DumpShellStack (const ShellStack& rStack); + void DumpSfxShellStack(); +#endif + + /** To be called before a shell is taken from the SFX shell stack. This + method deactivates an active text editing to avoid problems with + undo managers. + Afterwards the Deactivate() of the shell is called. + */ + static void Deactivate (SfxShell* pShell); + + ShellDescriptor CreateSubShell ( + SfxShell const * pShell, + ShellId nShellId); + void DestroyViewShell (ShellDescriptor& rDescriptor); + static void DestroySubShell (const ShellDescriptor& rDescriptor); +}; + +//===== ViewShellManager ====================================================== + +ViewShellManager::ViewShellManager (ViewShellBase& rBase) + : mpImpl(new Implementation(rBase)), + mbValid(true) +{ +} + +ViewShellManager::~ViewShellManager() +{ +} + +void ViewShellManager::AddSubShellFactory ( + ViewShell const * pViewShell, + const SharedShellFactory& rpFactory) +{ + if (mbValid) + mpImpl->AddShellFactory(pViewShell, rpFactory); +} + +void ViewShellManager::RemoveSubShellFactory ( + ViewShell const * pViewShell, + const SharedShellFactory& rpFactory) +{ + if (mbValid) + mpImpl->RemoveShellFactory(pViewShell, rpFactory); +} + +void ViewShellManager::ActivateViewShell (ViewShell* pViewShell) +{ + if (mbValid) + return mpImpl->ActivateViewShell(pViewShell); +} + +void ViewShellManager::DeactivateViewShell (const ViewShell* pShell) +{ + if (mbValid && pShell!=nullptr) + mpImpl->DeactivateViewShell(*pShell); +} + +void ViewShellManager::SetFormShell ( + const ViewShell* pParentShell, + FmFormShell* pFormShell, + bool bAbove) +{ + if (mbValid) + mpImpl->SetFormShell(pParentShell,pFormShell,bAbove); +} + +void ViewShellManager::ActivateSubShell (const ViewShell& rViewShell, ShellId nId) +{ + if (mbValid) + mpImpl->ActivateSubShell(rViewShell,nId); +} + +void ViewShellManager::DeactivateSubShell (const ViewShell& rViewShell, ShellId nId) +{ + if (mbValid) + mpImpl->DeactivateSubShell(rViewShell,nId); +} + +void ViewShellManager::InvalidateAllSubShells (ViewShell const * pViewShell) +{ + if (mbValid) + mpImpl->InvalidateAllSubShells(pViewShell); +} + +void ViewShellManager::ActivateShell (SfxShell* pShell) +{ + if (mbValid && pShell!=nullptr) + mpImpl->ActivateShell(*pShell); +} + +void ViewShellManager::DeactivateShell (const SfxShell* pShell) +{ + if (mbValid && pShell!=nullptr) + mpImpl->DeactivateShell(*pShell); +} + +void ViewShellManager::MoveToTop (const ViewShell& rParentShell) +{ + if (mbValid) + mpImpl->MoveToTop(rParentShell); +} + +SfxShell* ViewShellManager::GetShell (ShellId nId) const +{ + if (mbValid) + return mpImpl->GetShell(nId); + else + return nullptr; +} + +SfxShell* ViewShellManager::GetTopShell() const +{ + if (mbValid) + return mpImpl->GetTopShell(); + else + return nullptr; +} + +SfxShell* ViewShellManager::GetTopViewShell() const +{ + if (mbValid) + return mpImpl->GetTopViewShell(); + else + return nullptr; +} + +void ViewShellManager::Shutdown() +{ + if (mbValid) + { + mpImpl->Shutdown(); + mbValid = false; + } +} + +void ViewShellManager::LockUpdate() +{ + mpImpl->LockUpdate(); +} + +void ViewShellManager::UnlockUpdate() +{ + mpImpl->UnlockUpdate(); +} + +//===== ViewShellManager::Implementation ====================================== + +ViewShellManager::Implementation::Implementation ( + ViewShellBase& rBase) + : mrBase(rBase), + mnUpdateLockCount(0), + mbShellStackIsUpToDate(true), + mpFormShell(nullptr), + mpFormShellParent(nullptr), + mbFormShellAboveParent(true), + mpTopShell(nullptr), + mpTopViewShell(nullptr) +{} + +ViewShellManager::Implementation::~Implementation() COVERITY_NOEXCEPT_FALSE +{ + Shutdown(); +} + +void ViewShellManager::Implementation::AddShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory) +{ + bool bAlreadyAdded (false); + + // Check that the given factory has not already been added. + ::std::pair aRange( + maShellFactories.equal_range(pViewShell)); + for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + if (iFactory->second == rpFactory) + { + bAlreadyAdded = true; + break; + } + + // Add the factory if it is not already present. + if ( ! bAlreadyAdded) + maShellFactories.emplace(pViewShell, rpFactory); +} + +void ViewShellManager::Implementation::RemoveShellFactory ( + const SfxShell* pViewShell, + const SharedShellFactory& rpFactory) +{ + ::std::pair aRange( + maShellFactories.equal_range(pViewShell)); + for (FactoryList::iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + if (iFactory->second == rpFactory) + { + maShellFactories.erase(iFactory); + break; + } +} + +void ViewShellManager::Implementation::ActivateViewShell (ViewShell* pViewShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ShellDescriptor aResult; + aResult.mpShell = pViewShell; + + // Register as window listener so that the shells of the current + // window can be moved to the top of the shell stack. + if (aResult.mpShell != nullptr) + { + vcl::Window* pWindow = aResult.GetWindow(); + if (pWindow != nullptr) + { + pWindow->AddEventListener( + LINK(this, ViewShellManager::Implementation, WindowEventHandler)); + aResult.mbIsListenerAddedToWindow = true; + } + else + { + SAL_WARN("sd.view", + "ViewShellManager::ActivateViewShell: " + "new view shell has no active window"); + } + } + + ActivateShell(aResult); +} + +void ViewShellManager::Implementation::DeactivateViewShell (const ViewShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + if (iShell == maActiveViewShells.end()) + return; + + UpdateLock aLocker (*this); + + ShellDescriptor aDescriptor(*iShell); + mrBase.GetDocShell()->Disconnect(dynamic_cast(aDescriptor.mpShell)); + maActiveViewShells.erase(iShell); + TakeShellsFromStack(aDescriptor.mpShell); + + // Deactivate sub shells. + SubShellList::iterator iList (maActiveSubShells.find(&rShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + while ( ! rList.empty()) + DeactivateSubShell(rShell, rList.front().mnId); + } + + DestroyViewShell(aDescriptor); +} + +void ViewShellManager::Implementation::ActivateShell (SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Create a new shell or recycle on in the cache. + ShellDescriptor aDescriptor; + aDescriptor.mpShell = &rShell; + + ActivateShell(aDescriptor); +} + +void ViewShellManager::Implementation::ActivateShell (const ShellDescriptor& rDescriptor) +{ + // Put shell on top of the active view shells. + if (rDescriptor.mpShell != nullptr) + { + maActiveViewShells.insert( maActiveViewShells.begin(), rDescriptor); + } +} + +void ViewShellManager::Implementation::DeactivateShell (const SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + if (iShell == maActiveViewShells.end()) + return; + + UpdateLock aLocker (*this); + + ShellDescriptor aDescriptor(*iShell); + mrBase.GetDocShell()->Disconnect(dynamic_cast(aDescriptor.mpShell)); + maActiveViewShells.erase(iShell); + TakeShellsFromStack(aDescriptor.mpShell); + + // Deactivate sub shells. + SubShellList::iterator iList (maActiveSubShells.find(&rShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + while ( ! rList.empty()) + DeactivateSubShell(rShell, rList.front().mnId); + } + + DestroyViewShell(aDescriptor); +} + +void ViewShellManager::Implementation::ActivateSubShell ( + const SfxShell& rParentShell, + ShellId nId) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check that the given view shell is active. + if (std::none_of (maActiveViewShells.begin(), maActiveViewShells.end(), IsShell(&rParentShell))) + return; + + // Create the sub shell list if it does not yet exist. + SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); + if (iList == maActiveSubShells.end()) + iList = maActiveSubShells.emplace(&rParentShell,SubShellSubList()).first; + + // Do not activate an object bar that is already active. Requesting + // this is not exactly an error but may be an indication of one. + SubShellSubList& rList (iList->second); + if (std::any_of(rList.begin(),rList.end(), IsId(nId))) + return; + + // Add just the id of the sub shell. The actual shell is created + // later in CreateShells(). + UpdateLock aLock (*this); + rList.emplace_back(nId); +} + +void ViewShellManager::Implementation::DeactivateSubShell ( + const SfxShell& rParentShell, + ShellId nId) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check that the given view shell is active. + SubShellList::iterator iList (maActiveSubShells.find(&rParentShell)); + if (iList == maActiveSubShells.end()) + return; + + // Look up the sub shell. + SubShellSubList& rList (iList->second); + SubShellSubList::iterator iShell ( + ::std::find_if(rList.begin(),rList.end(), IsId(nId))); + if (iShell == rList.end()) + return; + SfxShell* pShell = iShell->mpShell; + if (pShell == nullptr) + return; + + UpdateLock aLock (*this); + + ShellDescriptor aDescriptor(*iShell); + // Remove the sub shell from both the internal structure as well as the + // SFX shell stack above and including the sub shell. + rList.erase(iShell); + TakeShellsFromStack(pShell); + + DestroySubShell(aDescriptor); +} + +void ViewShellManager::Implementation::MoveToTop (const SfxShell& rShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Check that we have access to a dispatcher. If not, then we are + // (probably) called while the view shell is still being created or + // initialized. Without dispatcher we can not rebuild the shell stack + // to move the requested shell to the top. So return right away instead + // of making a mess without being able to clean up afterwards. + if (mrBase.GetDispatcher() == nullptr) + return; + + ActiveShellList::iterator iShell (::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsShell(&rShell))); + bool bMove = true; + if (iShell != maActiveViewShells.end()) + { + // Is the shell already at the top of the stack? We have to keep + // the case in mind that mbKeepMainViewShellOnTop is true. Shells + // that are not the main view shell are placed on the second-to-top + // position in this case. + if (iShell == maActiveViewShells.begin()) + { + // The shell is at the top position and is either a) the main + // view shell or b) another shell but the main view shell is not + // kept at the top position. We do not have to move the shell. + bMove = false; + } + } + else + { + // The shell is not on the stack. Therefore it can not be moved. + // We could insert it but we don't. Use ActivateViewShell() for + // that. + bMove = false; + } + + // When the shell is not at the right position it is removed from the + // internal list of shells and inserted at the correct position. + if (bMove) + { + UpdateLock aLock (*this); + + ShellDescriptor aDescriptor(*iShell); + + TakeShellsFromStack(&rShell); + maActiveViewShells.erase(iShell); + + maActiveViewShells.insert(maActiveViewShells.begin(), aDescriptor); + } +} + +SfxShell* ViewShellManager::Implementation::GetShell (ShellId nId) const +{ + ::osl::MutexGuard aGuard (maMutex); + + SfxShell* pShell = nullptr; + + // First search the active view shells. + ActiveShellList::const_iterator iShell ( + ::std::find_if ( + maActiveViewShells.begin(), + maActiveViewShells.end(), + IsId(nId))); + if (iShell != maActiveViewShells.end()) + pShell = iShell->mpShell; + else + { + // Now search the active sub shells of every active view shell. + for (auto const& activeSubShell : maActiveSubShells) + { + const SubShellSubList& rList (activeSubShell.second); + SubShellSubList::const_iterator iSubShell( + ::std::find_if(rList.begin(),rList.end(), IsId(nId))); + if (iSubShell != rList.end()) + { + pShell = iSubShell->mpShell; + break; + } + } + } + + return pShell; +} + +SfxShell* ViewShellManager::Implementation::GetTopShell() const +{ + OSL_ASSERT(mpTopShell == mrBase.GetSubShell(0)); + return mpTopShell; +} + +SfxShell* ViewShellManager::Implementation::GetTopViewShell() const +{ + return mpTopViewShell; +} + +void ViewShellManager::Implementation::LockUpdate() +{ + mnUpdateLockCount++; +} + +void ViewShellManager::Implementation::UnlockUpdate() +{ + ::osl::MutexGuard aGuard (maMutex); + + mnUpdateLockCount--; + if (mnUpdateLockCount < 0) + { + // This should not happen. + OSL_ASSERT (mnUpdateLockCount>=0); + mnUpdateLockCount = 0; + } + if (mnUpdateLockCount == 0) + UpdateShellStack(); +} + +/** Update the SFX shell stack (the portion that is visible to us) so that + it matches the internal shell stack. This is done in six steps: + 1. Create the missing view shells and sub shells. + 2. Set up the internal shell stack. + 3. Get the SFX shell stack. + 4. Find the lowest shell in which the two stacks differ. + 5. Remove all shells above and including that shell from the SFX stack. + 6. Push all shells of the internal stack on the SFX shell stack that are + not already present on the later. +*/ +void ViewShellManager::Implementation::UpdateShellStack() +{ + ::osl::MutexGuard aGuard (maMutex); + + // Remember the undo manager from the top-most shell on the stack. + SfxShell* pTopMostShell = mrBase.GetSubShell(0); + SfxUndoManager* pUndoManager = (pTopMostShell!=nullptr) + ? pTopMostShell->GetUndoManager() + : nullptr; + + // 1. Create the missing shells. + CreateShells(); + + // Update the pointer to the top-most active view shell. + mpTopViewShell = (maActiveViewShells.empty()) + ? nullptr : maActiveViewShells.begin()->mpShell; + + + // 2. Create the internal target stack. + ShellStack aTargetStack; + CreateTargetStack(aTargetStack); + + // 3. Get SFX shell stack. + ShellStack aSfxShellStack; + sal_uInt16 nIndex (0); + while (mrBase.GetSubShell(nIndex)!=nullptr) + ++nIndex; + aSfxShellStack.reserve(nIndex); + while (nIndex-- > 0) + aSfxShellStack.push_back(mrBase.GetSubShell(nIndex)); + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.view", __func__ << ": Current SFX Stack"); + DumpShellStack(aSfxShellStack); + SAL_INFO("sd.view", __func__ << ": Target Stack"); + DumpShellStack(aTargetStack); +#endif + + // 4. Find the lowest shell in which the two stacks differ. + auto mismatchIters = std::mismatch(aSfxShellStack.begin(), aSfxShellStack.end(), + aTargetStack.begin(), aTargetStack.end()); + ShellStack::iterator iSfxShell (mismatchIters.first); + ShellStack::iterator iTargetShell (mismatchIters.second); + + // 5. Remove all shells above and including the differing shell from the + // SFX stack starting with the shell on top of the stack. + for (std::reverse_iterator i(aSfxShellStack.end()), iLast(iSfxShell); + i != iLast; ++i) + { + SfxShell* const pShell = *i; + SAL_INFO("sd.view", __func__ << ": removing shell " << pShell << " from stack"); + mrBase.RemoveSubShell(pShell); + } + aSfxShellStack.erase(iSfxShell, aSfxShellStack.end()); + + // 6. Push shells from the given stack onto the SFX stack. + mbShellStackIsUpToDate = false; + while (iTargetShell != aTargetStack.end()) + { + SAL_INFO("sd.view", __func__ << ": pushing shell " << *iTargetShell << " on stack"); + mrBase.AddSubShell(**iTargetShell); + ++iTargetShell; + + // The pushing of the shell on to the shell stack may have lead to + // another invocation of this method. In this case we have to abort + // pushing shells on the stack and return immediately. + if (mbShellStackIsUpToDate) + break; + } + if (mrBase.GetDispatcher() != nullptr) + mrBase.GetDispatcher()->Flush(); + + // Update the pointer to the top-most shell and set its undo manager + // to the one of the previous top-most shell. + mpTopShell = mrBase.GetSubShell(0); + if (mpTopShell!=nullptr && pUndoManager!=nullptr && mpTopShell->GetUndoManager()==nullptr) + mpTopShell->SetUndoManager(pUndoManager); + + // Finally tell an invocation of this method on a higher level that it can (has + // to) abort and return immediately. + mbShellStackIsUpToDate = true; + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.view", __func__ << ": New current stack"); + DumpSfxShellStack(); +#endif +} + +void ViewShellManager::Implementation::TakeShellsFromStack (const SfxShell* pShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + // Remember the undo manager from the top-most shell on the stack. + SfxShell* pTopMostShell = mrBase.GetSubShell(0); + SfxUndoManager* pUndoManager = (pTopMostShell!=nullptr) + ? pTopMostShell->GetUndoManager() + : nullptr; + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.view", __func__ << "TakeShellsFromStack( " << pShell << ")"); + DumpSfxShellStack(); +#endif + + // 0.Make sure that the given shell is on the stack. This is a + // preparation for the following assertion. + for (sal_uInt16 nIndex=0; true; nIndex++) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); + if (pShellOnStack == nullptr) + { + // Set pShell to NULL to indicate the following code that the + // shell is not on the stack. + pShell = nullptr; + break; + } + else if (pShellOnStack == pShell) + break; + } + + if (pShell == nullptr) + return; + + // 1. Deactivate our shells on the stack before they are removed so + // that during the Deactivation() calls the stack is still intact. + for (sal_uInt16 nIndex=0; true; nIndex++) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(nIndex); + Deactivate(pShellOnStack); + if (pShellOnStack == pShell) + break; + } + + // 2. Remove the shells from the stack. + while (true) + { + SfxShell* pShellOnStack = mrBase.GetSubShell(0); + SAL_INFO("sd.view", __func__ << "removing shell " << pShellOnStack << " from stack"); + mrBase.RemoveSubShell(pShellOnStack); + if (pShellOnStack == pShell) + break; + } + + // 3. Update the stack. + if (mrBase.GetDispatcher() != nullptr) + mrBase.GetDispatcher()->Flush(); + + // Update the pointer to the top-most shell and set its undo manager + // to the one of the previous top-most shell. + mpTopShell = mrBase.GetSubShell(0); + if (mpTopShell!=nullptr && pUndoManager!=nullptr && mpTopShell->GetUndoManager()==nullptr) + mpTopShell->SetUndoManager(pUndoManager); + +#if OSL_DEBUG_LEVEL >= 2 + SAL_INFO("sd.view", __func__ << "Sfx shell stack is:"); + DumpSfxShellStack(); +#endif +} + +void ViewShellManager::Implementation::CreateShells() +{ + ::osl::MutexGuard aGuard (maMutex); + + // Iterate over all view shells. + ActiveShellList::reverse_iterator iShell; + for (iShell=maActiveViewShells.rbegin(); iShell!=maActiveViewShells.rend(); ++iShell) + { + // Get the list of associated sub shells. + SubShellList::iterator iList (maActiveSubShells.find(iShell->mpShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + + // Iterate over all sub shells of the current view shell. + for (auto & subShell : rList) + { + if (subShell.mpShell == nullptr) + { + subShell = CreateSubShell(iShell->mpShell,subShell.mnId); + } + } + } + } +} + +void ViewShellManager::Implementation::CreateTargetStack (ShellStack& rStack) const +{ + // Create a local stack of the shells that are to push on the shell + // stack. We can thus safely create the required shells while still + // having a valid shell stack. + for (ActiveShellList::const_reverse_iterator iViewShell (maActiveViewShells.rbegin()); + iViewShell != maActiveViewShells.rend(); + ++iViewShell) + { + // Possibly place the form shell below the current view shell. + if ( ! mbFormShellAboveParent + && mpFormShell!=nullptr + && iViewShell->mpShell==mpFormShellParent) + { + rStack.push_back(mpFormShell); + } + + // Put the view shell itself on the local stack. + rStack.push_back (iViewShell->mpShell); + + // Possibly place the form shell above the current view shell. + if (mbFormShellAboveParent + && mpFormShell!=nullptr + && iViewShell->mpShell==mpFormShellParent) + { + rStack.push_back(mpFormShell); + } + + // Add all other sub shells. + SubShellList::const_iterator iList (maActiveSubShells.find(iViewShell->mpShell)); + if (iList != maActiveSubShells.end()) + { + const SubShellSubList& rList (iList->second); + SubShellSubList::const_reverse_iterator iSubShell; + for (iSubShell=rList.rbegin(); iSubShell!=rList.rend(); ++iSubShell) + if (iSubShell->mpShell != mpFormShell) + rStack.push_back(iSubShell->mpShell); + } + } +} + +IMPL_LINK(ViewShellManager::Implementation, WindowEventHandler, VclWindowEvent&, rEvent, void) +{ + vcl::Window* pEventWindow = rEvent.GetWindow(); + + switch (rEvent.GetId()) + { + case VclEventId::WindowGetFocus: + { + for (auto const& activeShell : maActiveViewShells) + { + if (pEventWindow == activeShell.GetWindow()) + { + MoveToTop(*activeShell.mpShell); + break; + } + } + } + break; + + case VclEventId::WindowLoseFocus: + break; + + case VclEventId::ObjectDying: + // Remember that we do not have to remove the window + // listener for this window. + for (auto & activeViewShell : maActiveViewShells) + { + if (activeViewShell.GetWindow() == pEventWindow) + { + activeViewShell.mbIsListenerAddedToWindow = false; + break; + } + } + break; + + default: break; + } +} + +ShellDescriptor ViewShellManager::Implementation::CreateSubShell ( + SfxShell const * pParentShell, + ShellId nShellId) +{ + ::osl::MutexGuard aGuard (maMutex); + ShellDescriptor aResult; + + // Look up the factories for the parent shell. + ::std::pair aRange( + maShellFactories.equal_range(pParentShell)); + + // Try all factories to create the shell. + for (FactoryList::const_iterator iFactory=aRange.first; iFactory!=aRange.second; ++iFactory) + { + SharedShellFactory pFactory = iFactory->second; + if (pFactory != nullptr) + aResult.mpShell = pFactory->CreateShell(nShellId); + + // Exit the loop when the shell has been successfully created. + if (aResult.mpShell != nullptr) + { + aResult.mpFactory = pFactory; + aResult.mnId = nShellId; + break; + } + } + + return aResult; +} + +void ViewShellManager::Implementation::DestroyViewShell ( + ShellDescriptor& rDescriptor) +{ + OSL_ASSERT(rDescriptor.mpShell != nullptr); + + if (rDescriptor.mbIsListenerAddedToWindow) + { + rDescriptor.mbIsListenerAddedToWindow = false; + vcl::Window* pWindow = rDescriptor.GetWindow(); + if (pWindow != nullptr) + { + pWindow->RemoveEventListener( + LINK(this, ViewShellManager::Implementation, WindowEventHandler)); + } + } + + // Destroy the sub shell factories. + ::std::pair aRange( + maShellFactories.equal_range(rDescriptor.mpShell)); + if (aRange.first != maShellFactories.end()) + maShellFactories.erase(aRange.first, aRange.second); + + // Release the shell. + if (rDescriptor.mpFactory) + rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell); +} + +void ViewShellManager::Implementation::DestroySubShell ( + const ShellDescriptor& rDescriptor) +{ + OSL_ASSERT(rDescriptor.mpFactory); + rDescriptor.mpFactory->ReleaseShell(rDescriptor.mpShell); +} + +void ViewShellManager::Implementation::InvalidateAllSubShells (const SfxShell* pParentShell) +{ + ::osl::MutexGuard aGuard (maMutex); + + SubShellList::iterator iList (maActiveSubShells.find(pParentShell)); + if (iList != maActiveSubShells.end()) + { + SubShellSubList& rList (iList->second); + for (auto const& shell : rList) + if (shell.mpShell != nullptr) + shell.mpShell->Invalidate(); + } +} + +void ViewShellManager::Implementation::Shutdown() +{ + ::osl::MutexGuard aGuard (maMutex); + + // Take stacked shells from stack. + if ( ! maActiveViewShells.empty()) + { + UpdateLock aLock (*this); + + while ( ! maActiveViewShells.empty()) + { + SfxShell* pShell = maActiveViewShells.front().mpShell; + if (pShell != nullptr) + { + ViewShell* pViewShell = dynamic_cast(pShell); + if (pViewShell != nullptr) + DeactivateViewShell(*pViewShell); + else + DeactivateShell(*pShell); + } + else + { + SAL_WARN("sd.view", + "ViewShellManager::Implementation::Shutdown(): empty active shell descriptor"); + maActiveViewShells.pop_front(); + } + } + } + mrBase.RemoveSubShell (); + + maShellFactories.clear(); +} + +#if OSL_DEBUG_LEVEL >= 2 +void ViewShellManager::Implementation::DumpShellStack (const ShellStack& rStack) +{ + ShellStack::const_reverse_iterator iEntry; + for (iEntry=rStack.rbegin(); iEntry!=rStack.rend(); ++iEntry) + if (*iEntry != NULL) + SAL_INFO("sd.view", __func__ << ": " << + *iEntry << " : " << + (*iEntry)->GetName()); + else + SAL_INFO("sd.view", __func__ << " null"); +} + +void ViewShellManager::Implementation::DumpSfxShellStack() +{ + ShellStack aSfxShellStack; + sal_uInt16 nIndex (0); + while (mrBase.GetSubShell(nIndex)!=NULL) + ++nIndex; + aSfxShellStack.reserve(nIndex); + while (nIndex-- > 0) + aSfxShellStack.push_back(mrBase.GetSubShell(nIndex)); + DumpShellStack(aSfxShellStack); +} +#endif + +void ViewShellManager::Implementation::Deactivate (SfxShell* pShell) +{ + OSL_ASSERT(pShell!=nullptr); + + // We have to end a text edit for view shells that are to be taken from + // the shell stack. + ViewShell* pViewShell = dynamic_cast(pShell); + if (pViewShell != nullptr) + { + sd::View* pView = pViewShell->GetView(); + if (pView!=nullptr && pView->IsTextEdit()) + { + pView->SdrEndTextEdit(); + pView->UnmarkAll(); + pViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON); + } + } + + // Now we can deactivate the shell. + pShell->Deactivate(true); +} + +void ViewShellManager::Implementation::SetFormShell ( + const ViewShell* pFormShellParent, + FmFormShell* pFormShell, + bool bFormShellAboveParent) +{ + ::osl::MutexGuard aGuard (maMutex); + + mpFormShellParent = pFormShellParent; + mpFormShell = pFormShell; + mbFormShellAboveParent = bFormShellAboveParent; +} + +namespace { + +ShellDescriptor::ShellDescriptor() + : mpShell(nullptr), + mnId(ToolbarId::None), + mbIsListenerAddedToWindow(false) +{ +} + +ShellDescriptor::ShellDescriptor ( + ShellId nId) + : mpShell(nullptr), + mnId(nId), + mbIsListenerAddedToWindow(false) +{ +} + +vcl::Window* ShellDescriptor::GetWindow() const +{ + ViewShell* pViewShell = dynamic_cast(mpShell); + if (pViewShell != nullptr) + return pViewShell->GetActiveWindow(); + else + return nullptr; +} + +} // end of anonymous namespace + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/ViewTabBar.cxx b/sd/source/ui/view/ViewTabBar.cxx new file mode 100644 index 000000000..18a408e83 --- /dev/null +++ b/sd/source/ui/view/ViewTabBar.cxx @@ -0,0 +1,561 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::sd::framework::FrameworkHelper; + +namespace sd { + +namespace { +bool IsEqual (const TabBarButton& rButton1, const TabBarButton& rButton2) +{ + return ((rButton1.ResourceId.is() + && rButton2.ResourceId.is() + && rButton1.ResourceId->compareTo(rButton2.ResourceId) == 0) + || rButton1.ButtonLabel == rButton2.ButtonLabel); +} + +} // end of anonymous namespace + +ViewTabBar::ViewTabBar ( + const Reference& rxViewTabBarId, + const Reference& rxController) + : mpTabControl(VclPtr::Create(GetAnchorWindow(rxViewTabBarId,rxController), this)), + mxController(rxController), + mxViewTabBarId(rxViewTabBarId), + mpViewShellBase(nullptr), + mnNoteBookWidthPadding(0) +{ + // Tunnel through the controller and use the ViewShellBase to obtain the + // view frame. + try + { + Reference xTunnel (mxController, UNO_QUERY_THROW); + if (auto pController = comphelper::getFromUnoTunnel(xTunnel)) + mpViewShellBase = pController->GetViewShellBase(); + } + catch (const RuntimeException&) + { + } + + // Register as listener at XConfigurationController. + Reference xControllerManager (mxController, UNO_QUERY); + if (xControllerManager.is()) + { + mxConfigurationController = xControllerManager->getConfigurationController(); + if (mxConfigurationController.is()) + { + mxConfigurationController->addConfigurationChangeListener( + this, + FrameworkHelper::msResourceActivationEvent, + Any()); + } + } + + mpTabControl->Show(); + + if (mpViewShellBase != nullptr + && rxViewTabBarId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + mpViewShellBase->SetViewTabBar(this); + } +} + +ViewTabBar::~ViewTabBar() +{ +} + +void ViewTabBar::disposing(std::unique_lock&) +{ + if (mpViewShellBase != nullptr + && mxViewTabBarId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + mpViewShellBase->SetViewTabBar(nullptr); + } + + if (mxConfigurationController.is()) + { + // Unregister listener from XConfigurationController. + try + { + mxConfigurationController->removeConfigurationChangeListener(this); + } + catch (const lang::DisposedException&) + { + // Receiving a disposed exception is the normal case. Is there + // a way to avoid it? + } + mxConfigurationController = nullptr; + } + + { + const SolarMutexGuard aSolarGuard; + mpTabControl.disposeAndClear(); + } + + mxController = nullptr; +} + +vcl::Window* ViewTabBar::GetAnchorWindow( + const Reference& rxViewTabBarId, + const Reference& rxController) +{ + vcl::Window* pWindow = nullptr; + ViewShellBase* pBase = nullptr; + + // Tunnel through the controller and use the ViewShellBase to obtain the + // view frame. + try + { + Reference xTunnel (rxController, UNO_QUERY_THROW); + if (auto pController = comphelper::getFromUnoTunnel(xTunnel)) + pBase = pController->GetViewShellBase(); + } + catch (const RuntimeException&) + { + } + + // The ViewTabBar supports at the moment only the center pane. + if (rxViewTabBarId.is() + && rxViewTabBarId->isBoundToURL( + FrameworkHelper::msCenterPaneURL, AnchorBindingMode_DIRECT)) + { + if (pBase != nullptr && pBase->GetViewFrame() != nullptr) + pWindow = &pBase->GetViewFrame()->GetWindow(); + } + + // The rest is (at the moment) just for the emergency case. + if (pWindow == nullptr) + { + Reference xPane; + try + { + Reference xControllerManager (rxController, UNO_QUERY_THROW); + Reference xCC ( + xControllerManager->getConfigurationController()); + if (xCC.is()) + xPane.set(xCC->getResource(rxViewTabBarId->getAnchor()), UNO_QUERY); + } + catch (const RuntimeException&) + { + } + + // Tunnel through the XWindow to the VCL side. + try + { + Reference xTunnel (xPane, UNO_QUERY_THROW); + if (auto pPane = comphelper::getFromUnoTunnel(xTunnel)) + pWindow = pPane->GetWindow()->GetParent(); + } + catch (const RuntimeException&) + { + } + } + + return pWindow; +} + +//----- XConfigurationChangeListener ------------------------------------------ + +void SAL_CALL ViewTabBar::notifyConfigurationChange ( + const ConfigurationChangeEvent& rEvent) +{ + if (rEvent.Type == FrameworkHelper::msResourceActivationEvent + && rEvent.ResourceId->getResourceURL().match(FrameworkHelper::msViewURLPrefix) + && rEvent.ResourceId->isBoundTo(mxViewTabBarId->getAnchor(), AnchorBindingMode_DIRECT)) + { + UpdateActiveButton(); + } +} + +//----- XEventListener -------------------------------------------------------- + +void SAL_CALL ViewTabBar::disposing( + const lang::EventObject& rEvent) +{ + if (rEvent.Source == mxConfigurationController) + { + mxConfigurationController = nullptr; + mxController = nullptr; + } +} + +//----- XTabBar --------------------------------------------------------------- + +void SAL_CALL ViewTabBar::addTabBarButtonAfter ( + const TabBarButton& rButton, + const TabBarButton& rAnchor) +{ + const SolarMutexGuard aSolarGuard; + AddTabBarButton(rButton, rAnchor); +} + +void SAL_CALL ViewTabBar::appendTabBarButton (const TabBarButton& rButton) +{ + const SolarMutexGuard aSolarGuard; + AddTabBarButton(rButton); +} + +void SAL_CALL ViewTabBar::removeTabBarButton (const TabBarButton& rButton) +{ + const SolarMutexGuard aSolarGuard; + RemoveTabBarButton(rButton); +} + +sal_Bool SAL_CALL ViewTabBar::hasTabBarButton (const TabBarButton& rButton) +{ + const SolarMutexGuard aSolarGuard; + return HasTabBarButton(rButton); +} + +Sequence SAL_CALL ViewTabBar::getTabBarButtons() +{ + const SolarMutexGuard aSolarGuard; + return GetTabBarButtons(); +} + +//----- XResource ------------------------------------------------------------- + +Reference SAL_CALL ViewTabBar::getResourceId() +{ + return mxViewTabBarId; +} + +sal_Bool SAL_CALL ViewTabBar::isAnchorOnly() +{ + return false; +} + +//----- XUnoTunnel ------------------------------------------------------------ + +const Sequence& ViewTabBar::getUnoTunnelId() +{ + static const comphelper::UnoIdInit theViewTabBarUnoTunnelId; + return theViewTabBarUnoTunnelId.getSeq(); +} + +sal_Int64 SAL_CALL ViewTabBar::getSomething (const Sequence& rId) +{ + return comphelper::getSomethingImpl(rId, this); +} + +bool ViewTabBar::ActivatePage(size_t nIndex) +{ + try + { + Reference xControllerManager (mxController,UNO_QUERY_THROW); + Reference xConfigurationController ( + xControllerManager->getConfigurationController()); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + Reference xView; + try + { + xView.set(xConfigurationController->getResource( + ResourceId::create( + ::comphelper::getProcessComponentContext(), + FrameworkHelper::msCenterPaneURL)), + UNO_QUERY); + } + catch (const DeploymentException&) + { + } + + Client* pIPClient = nullptr; + if (mpViewShellBase != nullptr) + pIPClient = dynamic_cast(mpViewShellBase->GetIPClient()); + if (pIPClient==nullptr || ! pIPClient->IsObjectInPlaceActive()) + { + if (nIndex < maTabBarButtons.size()) + { + xConfigurationController->requestResourceActivation( + maTabBarButtons[nIndex].ResourceId, + ResourceActivationMode_REPLACE); + } + + return true; + } + } + catch (const RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + + return false; +} + +int ViewTabBar::GetHeight() const +{ + int nHeight (0); + + if (!maTabBarButtons.empty()) + { + if (mpTabControl->IsReallyVisible()) + { + weld::Notebook& rNotebook = mpTabControl->GetNotebook(); + int nAllocatedWidth = mpTabControl->GetAllocatedWidth(); + int nPageWidth = nAllocatedWidth - mnNoteBookWidthPadding; + + // set each page width-request to the size it takes to fit the notebook allocation + for (int nIndex = 1, nPageCount = rNotebook.get_n_pages(); nIndex <= nPageCount; ++nIndex) + { + OString sIdent(OString::number(nIndex)); + weld::Container* pContainer = rNotebook.get_page(sIdent); + pContainer->set_size_request(nPageWidth, -1); + } + + // get the height-for-width for this allocation + nHeight = mpTabControl->get_preferred_size().Height(); + } + + if (nHeight <= 0) + { + // Using a default when the real height can not be determined. + // To get correct height this method should be called when the + // control is visible. + nHeight = 21; + } + } + + return nHeight; +} + +void ViewTabBar::AddTabBarButton ( + const css::drawing::framework::TabBarButton& rButton, + const css::drawing::framework::TabBarButton& rAnchor) +{ + TabBarButtonList::size_type nIndex; + + if ( ! rAnchor.ResourceId.is() + || (rAnchor.ResourceId->getResourceURL().isEmpty() + && rAnchor.ButtonLabel.isEmpty())) + { + nIndex = 0; + } + else + { + for (nIndex=0; nIndex= 0 && + nPosition <= mpTabControl->GetNotebook().get_n_pages()) + { + // Insert the button into our local array. + maTabBarButtons.insert(maTabBarButtons.begin() + nPosition, rButton); + UpdateTabBarButtons(); + UpdateActiveButton(); + } +} + +void ViewTabBar::RemoveTabBarButton ( + const css::drawing::framework::TabBarButton& rButton) +{ + for (TabBarButtonList::size_type nIndex=0; nIndex + ViewTabBar::GetTabBarButtons() +{ + return comphelper::containerToSequence(maTabBarButtons); +} + +void ViewTabBar::UpdateActiveButton() +{ + Reference xView; + if (mpViewShellBase != nullptr) + xView = FrameworkHelper::Instance(*mpViewShellBase)->GetView( + mxViewTabBarId->getAnchor()); + if (!xView.is()) + return; + + Reference xViewId (xView->getResourceId()); + for (size_t nIndex=0; nIndexcompareTo(xViewId) == 0) + { + mpTabControl->GetNotebook().set_current_page(nIndex); + break; + } + } +} + +void ViewTabBar::UpdateTabBarButtons() +{ + int nMaxPageWidthReq(0); + + weld::Notebook& rNotebook = mpTabControl->GetNotebook(); + int nPageCount(rNotebook.get_n_pages()); + int nIndex = 1; + for (const auto& rTab : maTabBarButtons) + { + OString sIdent(OString::number(nIndex)); + // Create a new tab when there are not enough. + if (nPageCount < nIndex) + rNotebook.append_page(sIdent, rTab.ButtonLabel); + else + { + // Update the tab. + rNotebook.set_tab_label_text(sIdent, rTab.ButtonLabel); + } + + // Set a fairly arbitrary initial width request for the pages so we can + // measure what extra width the notebook itself uses + weld::Container* pContainer = rNotebook.get_page(sIdent); + int nTextWidth = pContainer->get_pixel_size(rTab.ButtonLabel).Width(); + pContainer->set_size_request(nTextWidth, -1); + nMaxPageWidthReq = std::max(nMaxPageWidthReq, nTextWidth); + + ++nIndex; + } + + // Delete tabs that are no longer used. + for (; nIndex<=nPageCount; ++nIndex) + rNotebook.remove_page(OString::number(nIndex)); + + int nWidthReq = rNotebook.get_preferred_size().Width(); + // The excess width over the page request that the notebook uses we will + // use this later to help measure the best height-for-width given the + // eventual allocated width of the notebook + mnNoteBookWidthPadding = nWidthReq - nMaxPageWidthReq; +} + +//===== TabBarControl ========================================================= + +TabBarControl::TabBarControl ( + vcl::Window* pParentWindow, + const ::rtl::Reference& rpViewTabBar) + : InterimItemWindow(pParentWindow, "modules/simpress/ui/tabviewbar.ui", "TabViewBar") + , mxTabControl(m_xBuilder->weld_notebook("tabcontrol")) + , mpViewTabBar(rpViewTabBar) + , mnAllocatedWidth(0) +{ + // Because the actual window background is transparent--to avoid + // flickering due to multiple background paintings by this and by child + // windows--we have to paint the background for this control explicitly: + // the actual control is not painted over its whole bounding box. + SetPaintTransparent(false); + SetBackground(Application::GetSettings().GetStyleSettings().GetDialogColor()); + + InitControlBase(mxTabControl.get()); + + mxTabControl->connect_enter_page(LINK(this, TabBarControl, ActivatePageHdl)); + mxTabControl->connect_size_allocate(LINK(this, TabBarControl, NotebookSizeAllocHdl)); +} + +void TabBarControl::dispose() +{ + mxTabControl.reset(); + InterimItemWindow::dispose(); +} + +TabBarControl::~TabBarControl() +{ + disposeOnce(); +} + +IMPL_LINK(TabBarControl, NotebookSizeAllocHdl, const Size&, rSize, void) +{ + mnAllocatedWidth = rSize.Width(); +} + +IMPL_LINK(TabBarControl, ActivatePageHdl, const OString&, rPage, void) +{ + if (!mpViewTabBar->ActivatePage(mxTabControl->get_page_index(rPage))) + { + // When we run into this else branch then we have an active OLE + // object. We ignore the request to switch views. Additionally + // we put the active tab back to the one for the current view. + mpViewTabBar->UpdateActiveButton(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/WindowUpdater.cxx b/sd/source/ui/view/WindowUpdater.cxx new file mode 100644 index 000000000..c3f1bb53e --- /dev/null +++ b/sd/source/ui/view/WindowUpdater.cxx @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include + +#include + +namespace sd { + +WindowUpdater::WindowUpdater() + : mpDocument (nullptr) +{ + maCTLOptions.AddListener(this); +} + +WindowUpdater::~WindowUpdater() noexcept +{ + maCTLOptions.RemoveListener(this); +} + +void WindowUpdater::RegisterWindow (vcl::Window* pWindow) +{ + if (pWindow != nullptr) + { + tWindowList::iterator aWindowIterator ( + ::std::find ( + maWindowList.begin(), maWindowList.end(), pWindow)); + if (aWindowIterator == maWindowList.end()) + { + // Update the device once right now and add it to the list. + Update (pWindow->GetOutDev()); + maWindowList.emplace_back(pWindow); + } + } +} + +void WindowUpdater::UnregisterWindow (vcl::Window* pWindow) +{ + tWindowList::iterator aWindowIterator ( + ::std::find ( + maWindowList.begin(), maWindowList.end(), pWindow)); + if (aWindowIterator != maWindowList.end()) + { + maWindowList.erase (aWindowIterator); + } +} + +void WindowUpdater::SetDocument (SdDrawDocument* pDocument) +{ + mpDocument = pDocument; +} + +void WindowUpdater::Update ( + OutputDevice* pDevice) const +{ + if (pDevice != nullptr) + { + UpdateWindow (pDevice); + } +} + +void WindowUpdater::UpdateWindow (OutputDevice* pDevice) const +{ + if (pDevice == nullptr) + return; + + SvtCTLOptions::TextNumerals aNumeralMode (maCTLOptions.GetCTLTextNumerals()); + + LanguageType aLanguage; + // Now this is a bit confusing. The numerals in arabic languages + // are Hindi numerals and what the western world generally uses are + // arabic numerals. The digits used in the Hindi language are not + // used at all. + switch (aNumeralMode) + { + case SvtCTLOptions::NUMERALS_HINDI: + aLanguage = LANGUAGE_ARABIC_SAUDI_ARABIA; + break; + + case SvtCTLOptions::NUMERALS_SYSTEM: + aLanguage = LANGUAGE_SYSTEM; + break; + + case SvtCTLOptions::NUMERALS_ARABIC: + default: + aLanguage = LANGUAGE_ENGLISH; + break; + } + + pDevice->SetDigitLanguage (aLanguage); +} + +void WindowUpdater::ConfigurationChanged( utl::ConfigurationBroadcaster*, ConfigurationHints ) +{ + // Set the current state at all registered output devices. + for (auto& rxWindow : maWindowList) + Update (rxWindow->GetOutDev()); + + // Reformat the document for the modified state to take effect. + if (mpDocument != nullptr) + mpDocument->ReformatAllTextObjects(); + + // Invalidate the windows to make the modified state visible. + for (auto& rxWindow : maWindowList) + rxWindow->Invalidate(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/clview.cxx b/sd/source/ui/view/clview.cxx new file mode 100644 index 000000000..9f11839da --- /dev/null +++ b/sd/source/ui/view/clview.cxx @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +namespace sd +{ +class DrawDocShell; +class DrawViewShell; + +/** + * ClientView is used for DrawDocShell::Draw() + */ + +ClientView::ClientView(DrawDocShell* pDocSh, OutputDevice* pOutDev) + : DrawView(pDocSh, pOutDev, nullptr) +{ +} + +ClientView::~ClientView() {} + +/** + * If View should not Invalidate() the windows, this method has + * to be overridden and properly handled. + */ + +void ClientView::InvalidateOneWin(OutputDevice& rWin) +{ + vcl::Region aRegion; + CompleteRedraw(&rWin, aRegion); +} + +/** + * If View should not Invalidate() the windows, this method has + * to be overridden and properly handled. + */ + +void ClientView::InvalidateOneWin(OutputDevice& rWin, const ::tools::Rectangle& rRect) +{ + CompleteRedraw(&rWin, vcl::Region(rRect)); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drawview.cxx b/sd/source/ui/view/drawview.cxx new file mode 100644 index 000000000..e4cdf0107 --- /dev/null +++ b/sd/source/ui/view/drawview.cxx @@ -0,0 +1,634 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; + +namespace sd { + + +/** + * Shows the first page of document at position 0,0. In the case + * that there is no page a page is created. + */ + +DrawView::DrawView( + DrawDocShell* pDocSh, + OutputDevice* pOutDev, + DrawViewShell* pShell) +: ::sd::View(*pDocSh->GetDoc(), pOutDev, pShell) + ,mpDocShell(pDocSh) + ,mpDrawViewShell(pShell) + ,mnPOCHSmph(0) +{ + SetCurrentObj(SdrObjKind::Rectangle); +} + +DrawView::~DrawView() +{ +} + +/** + * Virtual method from SdrView, called at selection change. + */ + +void DrawView::MarkListHasChanged() +{ + ::sd::View::MarkListHasChanged(); + + if (mpDrawViewShell) + mpDrawViewShell->SelectionHasChanged(); +} + +/** + * Virtual method from SdrView, called at model change. + */ + +void DrawView::ModelHasChanged() +{ + ::sd::View::ModelHasChanged(); + + // force framer to rerender + SfxStyleSheetBasePool* pSSPool = mrDoc.GetStyleSheetPool(); + pSSPool->Broadcast(SfxStyleSheetPoolHint()); + + if( mpDrawViewShell ) + mpDrawViewShell->ModelHasChanged(); + +} + +/** + * Redirect attributes onto title and outline text and background + * rectangle of a masterpage into templates, otherwise pass on baseclass. + */ + +bool DrawView::SetAttributes(const SfxItemSet& rSet, + bool bReplaceAll, bool bSlide, bool bMaster) +{ + bool bOk = false; + + if (mpDrawViewShell && bMaster) + { + SfxStyleSheetBasePool* pStShPool = mrDoc.GetStyleSheetPool(); + SdPage& rPage = *mpDrawViewShell->getCurrentPage(); + SdrPage& rMasterPage = rPage.TRG_GetMasterPage(); + size_t nObjCount = rMasterPage.GetObjCount(); + for (size_t nObj = 0; nObj < nObjCount; ++nObj) + { + SdrObject* pObject = rMasterPage.GetObj(nObj); + SetMasterAttributes(pObject, rPage, rSet, pStShPool, bOk, bMaster, bSlide); + } + return bOk; + } + if (mpDrawViewShell && bSlide) + { + SfxStyleSheetBasePool* pStShPool = mrDoc.GetStyleSheetPool(); + SdPage& rPage = *mpDrawViewShell->getCurrentPage(); + size_t nObjCount = rPage.GetObjCount(); + for (size_t nObj = 0; nObj < nObjCount; ++nObj) + { + SdrObject* pObject = rPage.GetObj(nObj); + SetMasterAttributes(pObject, rPage, rSet, pStShPool, bOk, bMaster, bSlide); + } + return bOk; + } + + // is there a masterpage edit? + if ( mpDrawViewShell && (mpDrawViewShell->GetEditMode() == EditMode::MasterPage) ) + { + SfxStyleSheetBasePool* pStShPool = mrDoc.GetStyleSheetPool(); + SdPage& rPage = *mpDrawViewShell->getCurrentPage(); + SdrTextObj* pEditObject = GetTextEditObject(); + + if (pEditObject) + { + // Textedit + + SdrInventor nInv = pEditObject->GetObjInventor(); + + if (nInv == SdrInventor::Default) + { + SdrObjKind eObjKind = pEditObject->GetObjIdentifier(); + PresObjKind ePresObjKind = rPage.GetPresObjKind(pEditObject); + + if ( ePresObjKind == PresObjKind::Title || + ePresObjKind == PresObjKind::Notes ) + { + // Presentation object (except outline) + SfxStyleSheet* pSheet = rPage.GetStyleSheetForPresObj( ePresObjKind ); + DBG_ASSERT(pSheet, "StyleSheet not found"); + + SfxItemSet aTempSet( pSheet->GetItemSet() ); + aTempSet.Put( rSet ); + aTempSet.ClearInvalidItems(); + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Put(aTempSet); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + bOk = true; + } + else if (eObjKind == SdrObjKind::OutlineText) + { + // Presentation object outline + OutlinerView* pOV = GetTextEditOutlinerView(); + ::Outliner* pOutliner = pOV->GetOutliner(); + + pOutliner->SetUpdateLayout(false); + mpDocSh->SetWaitCursor( true ); + + // replace placeholder by template name + OUString aComment(SdResId(STR_UNDO_CHANGE_PRES_OBJECT)); + aComment = aComment.replaceFirst("$", SdResId(STR_PSEUDOSHEET_OUTLINE)); + mpDocSh->GetUndoManager()->EnterListAction( aComment, OUString(), 0, mpDrawViewShell->GetViewShellBase().GetViewShellId() ); + + std::vector aSelList; + pOV->CreateSelectionList(aSelList); + + std::vector::reverse_iterator iter = aSelList.rbegin(); + Paragraph* pPara = iter != aSelList.rend() ? *iter : nullptr; + + while (pPara) + { + sal_Int32 nParaPos = pOutliner->GetAbsPos( pPara ); + sal_Int16 nDepth = pOutliner->GetDepth( nParaPos ); + OUString aName = rPage.GetLayoutName() + " " + + OUString::number((nDepth <= 0) ? 1 : nDepth + 1); + SfxStyleSheet* pSheet = static_cast(pStShPool->Find(aName, SfxStyleFamily::Page)); + //We have no stylesheet if we access outline level 10 + //in the master preview, there is no true style backing + //that entry + SAL_WARN_IF(!pSheet, "sd", "StyleSheet " << aName << " not found"); + if (pSheet) + { + SfxItemSet aTempSet( pSheet->GetItemSet() ); + aTempSet.Put( rSet ); + aTempSet.ClearInvalidItems(); + + if( nDepth > 0 && aTempSet.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET ) + { + // no SvxNumBulletItem in outline level 1 to 8! + aTempSet.ClearItem( EE_PARA_NUMBULLET ); + } + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Put(aTempSet); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + + // now also broadcast any child sheets + sal_Int16 nChild; + for( nChild = nDepth + 1; nChild < 9; nChild++ ) + { + OUString aSheetName = rPage.GetLayoutName() + " " + + OUString::number((nChild <= 0) ? 1 : nChild + 1); + SfxStyleSheet* pOutlSheet = static_cast< SfxStyleSheet* >(pStShPool->Find(aSheetName, SfxStyleFamily::Page)); + + if( pOutlSheet ) + pOutlSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + } + + ++iter; + pPara = iter != aSelList.rend() ? *iter : nullptr; + + bool bJumpToLevel1 = false; + if( !pPara && nDepth > 0 && rSet.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET ) + bJumpToLevel1 = true; + + if (bJumpToLevel1) + { + iter = aSelList.rend(); + --iter; + + if (pOutliner->GetDepth(pOutliner->GetAbsPos(*iter)) > 0) + pPara = pOutliner->GetParagraph( 0 ); // Put NumBulletItem in outline level 1 + } + } + + mpDocSh->SetWaitCursor( false ); + pOV->GetOutliner()->SetUpdateLayout(true); + + mpDocSh->GetUndoManager()->LeaveListAction(); + + bOk = true; + } + else + { + bOk = ::sd::View::SetAttributes(rSet, bReplaceAll); + } + } + } + else + { + // Selection + const SdrMarkList& rList = GetMarkedObjectList(); + const size_t nMarkCount = rList.GetMarkCount(); + for (size_t nMark = 0; nMark < nMarkCount; ++nMark) + { + SdrObject* pObject = rList.GetMark(nMark)->GetMarkedSdrObj(); + SetMasterAttributes(pObject, rPage, rSet, pStShPool, bOk, bMaster, bSlide); + } + + if(!bOk) + bOk = ::sd::View::SetAttributes(rSet, bReplaceAll); + } + } + else // not at masterpage + { + bOk = ::sd::View::SetAttributes(rSet, bReplaceAll); + } + + return bOk; +} + +void DrawView::SetMasterAttributes( SdrObject* pObject, const SdPage& rPage, SfxItemSet rSet, SfxStyleSheetBasePool* pStShPool, bool& bOk, bool bMaster, bool bSlide ) +{ + SdrInventor nInv = pObject->GetObjInventor(); + + if (nInv != SdrInventor::Default) + return; + + SdrObjKind eObjKind = pObject->GetObjIdentifier(); + PresObjKind ePresObjKind = rPage.GetPresObjKind(pObject); + if (bSlide && eObjKind == SdrObjKind::Text) + { + // Presentation object (except outline) + SfxStyleSheet* pSheet = rPage.GetTextStyleSheetForObject(pObject); + DBG_ASSERT(pSheet, "StyleSheet not found"); + + SfxItemSet aTempSet( pSheet->GetItemSet() ); + aTempSet.Put( rSet ); + aTempSet.ClearInvalidItems(); + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Put(aTempSet,false); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + bOk = true; + } + + if (!bSlide && + (ePresObjKind == PresObjKind::Title || + ePresObjKind == PresObjKind::Notes)) + { + // Presentation object (except outline) + SfxStyleSheet* pSheet = rPage.GetStyleSheetForPresObj( ePresObjKind ); + DBG_ASSERT(pSheet, "StyleSheet not found"); + + SfxItemSet aTempSet( pSheet->GetItemSet() ); + aTempSet.Put( rSet ); + aTempSet.ClearInvalidItems(); + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Put(aTempSet,false); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + bOk = true; + } + else if (eObjKind == SdrObjKind::OutlineText) + { + // tdf#127900: do not forget to apply master style to placeholders + if (!rSet.HasItem(EE_PARA_NUMBULLET) || bMaster) + { + // Presentation object outline + for (sal_uInt16 nLevel = 9; nLevel > 0; nLevel--) + { + OUString aName = rPage.GetLayoutName() + " " + + OUString::number(nLevel); + SfxStyleSheet* pSheet = static_cast(pStShPool-> + Find(aName, SfxStyleFamily::Page)); + DBG_ASSERT(pSheet, "StyleSheet not found"); + + SfxItemSet aTempSet( pSheet->GetItemSet() ); + + if( nLevel > 1 ) + { + // for all levels over 1, clear all items that will be + // hard set to level 1 + SfxWhichIter aWhichIter(rSet); + sal_uInt16 nWhich(aWhichIter.FirstWhich()); + while( nWhich ) + { + if( SfxItemState::SET == aWhichIter.GetItemState() ) + aTempSet.ClearItem( nWhich ); + nWhich = aWhichIter.NextWhich(); + } + + } + else + { + // put the items hard into level one + aTempSet.Put( rSet ); + } + + aTempSet.ClearInvalidItems(); + + // Undo-Action + mpDocSh->GetUndoManager()->AddUndoAction( + std::make_unique(&mrDoc, pSheet, &aTempSet)); + + pSheet->GetItemSet().Set(aTempSet,false); + pSheet->Broadcast(SfxHint(SfxHintId::DataChanged)); + } + + // remove all hard set items from shape that are now set in style + SfxWhichIter aWhichIter(rSet); + sal_uInt16 nWhich(aWhichIter.FirstWhich()); + while( nWhich ) + { + if( SfxItemState::SET == aWhichIter.GetItemState() ) + pObject->ClearMergedItem( nWhich ); + nWhich = aWhichIter.NextWhich(); + } + } + else + pObject->SetMergedItemSet(rSet); + + bOk = true; + } +} + +/** + * Notify for change of site arrangement + */ + +void DrawView::Notify(SfxBroadcaster& rBC, const SfxHint& rHint) +{ + if ( mpDrawViewShell && rHint.GetId() == SfxHintId::ThisIsAnSdrHint ) + { + SdrHintKind eHintKind = static_cast(rHint).GetKind(); + + if ( mnPOCHSmph == 0 && eHintKind == SdrHintKind::PageOrderChange ) + { + mpDrawViewShell->ResetActualPage(); + } + else if ( eHintKind == SdrHintKind::LayerChange || eHintKind == SdrHintKind::LayerOrderChange ) + { + mpDrawViewShell->ResetActualLayer(); + } + + // switch to that page when it's not a master page + if(SdrHintKind::SwitchToPage == eHintKind) + { + // We switch page only in the current view, which triggered this event + // and keep other views untouched. + SfxViewShell* pViewShell = SfxViewShell::Current(); + if(pViewShell && pViewShell != &mpDrawViewShell->GetViewShellBase()) + return; + + const SdrPage* pPage = static_cast(rHint).GetPage(); + if(pPage && !pPage->IsMasterPage()) + { + if(mpDrawViewShell->GetActualPage() != pPage) + { + sal_uInt16 nPageNum = (pPage->GetPageNum() - 1) / 2; // Sdr --> Sd + mpDrawViewShell->SwitchPage(nPageNum); + } + } + } + } + + ::sd::View::Notify(rBC, rHint); +} + +/** + * Lock/Unlock PageOrderChangedHint + */ + +void DrawView::BlockPageOrderChangedHint(bool bBlock) +{ + if (bBlock) + mnPOCHSmph++; + else + { + DBG_ASSERT(mnPOCHSmph, "counter overflow"); + mnPOCHSmph--; + } +} + +/** + * If presentation objects are selected, intercept stylesheet-positioning at + * masterpage. + */ + +bool DrawView::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr) +{ + bool bResult = true; + + // is there a masterpage edit? + if (mpDrawViewShell && mpDrawViewShell->GetEditMode() == EditMode::MasterPage) + { + if (IsPresObjSelected(false)) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(mpDrawViewShell->GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + bResult = false; + } + else + { + bResult = ::sd::View::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr); + } + } + else + { + bResult = ::sd::View::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr); + } + return bResult; +} + +/** + * Paint-method: Redirect event to the view + */ + +void DrawView::CompleteRedraw(OutputDevice* pOutDev, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector /*=0*/) +{ + bool bStandardPaint = true; + + SdDrawDocument* pDoc = mpDocShell->GetDoc(); + if( pDoc && pDoc->GetDocumentType() == DocumentType::Impress) + { + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( pDoc ) ); + if(xSlideshow.is() && xSlideshow->isRunning()) + { + OutputDevice* pShowWindow = xSlideshow->getShowWindow(); + if( (pShowWindow == pOutDev) || (xSlideshow->getAnimationMode() == ANIMATIONMODE_PREVIEW) ) + { + if( pShowWindow == pOutDev && mpViewSh ) + xSlideshow->paint(); + bStandardPaint = false; + } + } + } + + if(bStandardPaint) + { + ::sd::View::CompleteRedraw(pOutDev, rReg, pRedirector); + } +} + +/** + * Make passed region visible (scrolling if necessary) + */ + +void DrawView::MakeVisible(const ::tools::Rectangle& rRect, vcl::Window& rWin) +{ + if (!rRect.IsEmpty() && mpDrawViewShell) + { + mpDrawViewShell->MakeVisible(rRect, rWin); + } +} + +/** + * Hide page. + */ + +void DrawView::HideSdrPage() +{ + if (mpDrawViewShell) + { + mpDrawViewShell->HidePage(); + } + + ::sd::View::HideSdrPage(); +} + +void DrawView::DeleteMarked() +{ + sd::UndoManager* pUndoManager = mrDoc.GetUndoManager(); + DBG_ASSERT( pUndoManager, "sd::DrawView::DeleteMarked(), ui action without undo manager!?" ); + + if( pUndoManager ) + { + OUString aUndo(SvxResId(STR_EditDelete)); + aUndo = aUndo.replaceFirst("%1", GetDescriptionOfMarkedObjects()); + ViewShellId nViewShellId = mpDrawViewShell ? mpDrawViewShell->GetViewShellBase().GetViewShellId() : ViewShellId(-1); + pUndoManager->EnterListAction(aUndo, aUndo, 0, nViewShellId); + } + + SdPage* pPage = nullptr; + bool bResetLayout = false; + + const size_t nMarkCount = GetMarkedObjectList().GetMarkCount(); + if( nMarkCount ) + { + SdrMarkList aList( GetMarkedObjectList() ); + for (size_t nMark = 0; nMark < nMarkCount; ++nMark) + { + SdrObject* pObj = aList.GetMark(nMark)->GetMarkedSdrObj(); + if( pObj && !pObj->IsEmptyPresObj() && pObj->GetUserCall() ) + { + pPage = static_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + if (pPage) + { + PresObjKind ePresObjKind(pPage->GetPresObjKind(pObj)); + switch( ePresObjKind ) + { + case PresObjKind::NONE: + continue; // ignore it + case PresObjKind::Graphic: + case PresObjKind::Object: + case PresObjKind::Chart: + case PresObjKind::OrgChart: + case PresObjKind::Table: + case PresObjKind::Calc: + case PresObjKind::Media: + ePresObjKind = PresObjKind::Outline; + break; + default: + break; + } + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + bool bVertical = pTextObj && pTextObj->IsVerticalWriting(); + ::tools::Rectangle aRect( pObj->GetLogicRect() ); + SdrObject* pNewObj = pPage->InsertAutoLayoutShape( nullptr, ePresObjKind, bVertical, aRect, true ); + + // pUndoManager should not be NULL (see assert above) + // but since we have defensive code + // for it earlier and later in the function + // we might as well be consistent + if(pUndoManager) + { + // Move the new PresObj to the position before the + // object it will replace. + pUndoManager->AddUndoAction( + mrDoc.GetSdrUndoFactory().CreateUndoObjectOrdNum( + *pNewObj, + pNewObj->GetOrdNum(), + pObj->GetOrdNum())); + } + pPage->SetObjectOrdNum( pNewObj->GetOrdNum(), pObj->GetOrdNum() ); + + bResetLayout = true; + } + } + } + } + + ::sd::View::DeleteMarked(); + + if( pPage && bResetLayout ) + pPage->SetAutoLayout( pPage->GetAutoLayout() ); + + if( pUndoManager ) + pUndoManager->LeaveListAction(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drbezob.cxx b/sd/source/ui/view/drbezob.cxx new file mode 100644 index 000000000..c84489042 --- /dev/null +++ b/sd/source/ui/view/drbezob.cxx @@ -0,0 +1,320 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + + +#include + +#include +#include +#include +#include +#include + +using namespace sd; +#define ShellClass_BezierObjectBar +#include + +namespace sd { + +/** + * Declare default interface (Slotmap must not be empty) + */ +SFX_IMPL_INTERFACE(BezierObjectBar, ::SfxShell) + +void BezierObjectBar::InitInterface_Impl() +{ +} + + +BezierObjectBar::BezierObjectBar( + ViewShell* pSdViewShell, + ::sd::View* pSdView) + : SfxShell(pSdViewShell->GetViewShell()), + mpView(pSdView), + mpViewSh(pSdViewShell) +{ + DrawDocShell* pDocShell = mpViewSh->GetDocSh(); + SetPool(&pDocShell->GetPool()); + SetUndoManager(pDocShell->GetUndoManager()); + SetRepeatTarget(mpView); +} + +BezierObjectBar::~BezierObjectBar() +{ + SetRepeatTarget(nullptr); +} + +/** + * Status of attribute items. + */ + +void BezierObjectBar::GetAttrState(SfxItemSet& rSet) +{ + SfxItemSet aAttrSet( mpView->GetDoc().GetPool() ); + mpView->GetAttributes( aAttrSet ); + rSet.Put(aAttrSet, false); // <- sal_False, so DontCare-Status gets acquired + + rtl::Reference xFunc( mpViewSh->GetCurrentFunction() ); + + if(xFunc.is()) + { + if( auto pFuSelection = dynamic_cast< const FuSelection *>( xFunc.get() )) + { + sal_uInt16 nEditMode = pFuSelection->GetEditMode(); + rSet.Put(SfxBoolItem(nEditMode, true)); + } + else if( auto pFuPolygon = dynamic_cast< const FuConstructBezierPolygon *>( xFunc.get() )) + { + sal_uInt16 nEditMode = pFuPolygon->GetEditMode(); + rSet.Put(SfxBoolItem(nEditMode, true)); + } + } + + if(!mpView->IsMoveAllowed() || !mpView->IsResizeAllowed()) + { + // #i77187# if object is move and/or size protected, do not allow point editing at all + rSet.DisableItem(SID_BEZIER_MOVE); + rSet.DisableItem(SID_BEZIER_INSERT); + + rSet.DisableItem(SID_BEZIER_DELETE); + rSet.DisableItem(SID_BEZIER_CUTLINE); + rSet.DisableItem(SID_BEZIER_CONVERT); + + rSet.DisableItem(SID_BEZIER_EDGE); + rSet.DisableItem(SID_BEZIER_SMOOTH); + rSet.DisableItem(SID_BEZIER_SYMMTR); + + rSet.DisableItem(SID_BEZIER_CLOSE); + + rSet.DisableItem(SID_BEZIER_ELIMINATE_POINTS); + } + else + { + IPolyPolygonEditorController* pIPPEC = nullptr; + if( mpView->GetMarkedObjectList().GetMarkCount() ) + pIPPEC = mpView; + else + pIPPEC = dynamic_cast< IPolyPolygonEditorController* >( mpView->getSmartTags().getSelected().get() ); + + if ( !pIPPEC || !pIPPEC->IsRipUpAtMarkedPointsPossible()) + { + rSet.DisableItem(SID_BEZIER_CUTLINE); + } + if (!pIPPEC || !pIPPEC->IsDeleteMarkedPointsPossible()) + { + rSet.DisableItem(SID_BEZIER_DELETE); + } + if (!pIPPEC || !pIPPEC->IsSetMarkedSegmentsKindPossible()) + { + rSet.DisableItem(SID_BEZIER_CONVERT); + } + else + { + SdrPathSegmentKind eSegm = pIPPEC->GetMarkedSegmentsKind(); + switch (eSegm) + { + case SdrPathSegmentKind::DontCare: rSet.InvalidateItem(SID_BEZIER_CONVERT); break; + case SdrPathSegmentKind::Line : rSet.Put(SfxBoolItem(SID_BEZIER_CONVERT,false)); break; // Button down = curve + case SdrPathSegmentKind::Curve : rSet.Put(SfxBoolItem(SID_BEZIER_CONVERT,true)); break; + default: break; + } + } + if (!pIPPEC || !pIPPEC->IsSetMarkedPointsSmoothPossible()) + { + rSet.DisableItem(SID_BEZIER_EDGE); + rSet.DisableItem(SID_BEZIER_SMOOTH); + rSet.DisableItem(SID_BEZIER_SYMMTR); + } + else + { + SdrPathSmoothKind eSmooth = pIPPEC->GetMarkedPointsSmooth(); + switch (eSmooth) + { + case SdrPathSmoothKind::DontCare : break; + case SdrPathSmoothKind::Angular : rSet.Put(SfxBoolItem(SID_BEZIER_EDGE, true)); break; + case SdrPathSmoothKind::Asymmetric: rSet.Put(SfxBoolItem(SID_BEZIER_SMOOTH,true)); break; + case SdrPathSmoothKind::Symmetric : rSet.Put(SfxBoolItem(SID_BEZIER_SYMMTR,true)); break; + } + } + if (!pIPPEC || !pIPPEC->IsOpenCloseMarkedObjectsPossible()) + { + rSet.DisableItem(SID_BEZIER_CLOSE); + } + else + { + SdrObjClosedKind eClose = pIPPEC->GetMarkedObjectsClosedState(); + switch (eClose) + { + case SdrObjClosedKind::DontCare: rSet.InvalidateItem(SID_BEZIER_CLOSE); break; + case SdrObjClosedKind::Open : rSet.Put(SfxBoolItem(SID_BEZIER_CLOSE,false)); break; + case SdrObjClosedKind::Closed : rSet.Put(SfxBoolItem(SID_BEZIER_CLOSE,true)); break; + default: break; + } + } + + if(pIPPEC == mpView) + rSet.Put(SfxBoolItem(SID_BEZIER_ELIMINATE_POINTS, mpView->IsEliminatePolyPoints())); + else + rSet.DisableItem( SID_BEZIER_ELIMINATE_POINTS ); // only works for views + } +} + +/** + * Process SfxRequests + */ + +void BezierObjectBar::Execute(SfxRequest& rReq) +{ + sal_uInt16 nSId = rReq.GetSlot(); + + switch (nSId) + { + case SID_BEZIER_CUTLINE: + case SID_BEZIER_CONVERT: + case SID_BEZIER_DELETE: + case SID_BEZIER_EDGE: + case SID_BEZIER_SMOOTH: + case SID_BEZIER_SYMMTR: + case SID_BEZIER_CLOSE: + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + + IPolyPolygonEditorController* pIPPEC = nullptr; + if( rMarkList.GetMarkCount() ) + pIPPEC = mpView; + else + pIPPEC = dynamic_cast< IPolyPolygonEditorController* >( mpView->getSmartTags().getSelected().get() ); + + if( pIPPEC && !mpView->IsAction()) + { + switch (nSId) + { + case SID_BEZIER_DELETE: + pIPPEC->DeleteMarkedPoints(); + break; + + case SID_BEZIER_CUTLINE: + pIPPEC->RipUpAtMarkedPoints(); + break; + + case SID_BEZIER_CONVERT: + { + pIPPEC->SetMarkedSegmentsKind(SdrPathSegmentKind::Toggle); + break; + } + + case SID_BEZIER_EDGE: + case SID_BEZIER_SMOOTH: + case SID_BEZIER_SYMMTR: + { + SdrPathSmoothKind eKind; + + switch (nSId) + { + default: + case SID_BEZIER_EDGE: eKind = SdrPathSmoothKind::Angular; break; + case SID_BEZIER_SMOOTH: eKind = SdrPathSmoothKind::Asymmetric; break; + case SID_BEZIER_SYMMTR: eKind = SdrPathSmoothKind::Symmetric; break; + } + + pIPPEC->SetMarkedPointsSmooth(eKind); + break; + } + + case SID_BEZIER_CLOSE: + { + SdrPathObj* pPathObj = static_cast( rMarkList.GetMark(0)->GetMarkedSdrObj() ); + const bool bUndo = mpView->IsUndoEnabled(); + if( bUndo ) + mpView->BegUndo(SdResId(STR_UNDO_BEZCLOSE)); + + mpView->UnmarkAllPoints(); + + if( bUndo ) + mpView->AddUndo(mpView->GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pPathObj)); + + pPathObj->ToggleClosed(); + + if( bUndo ) + mpView->EndUndo(); + break; + } + } + } + + if( (pIPPEC == mpView) && !mpView->AreObjectsMarked() ) + mpViewSh->GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + rReq.Ignore(); + } + break; + + case SID_BEZIER_ELIMINATE_POINTS: + { + mpView->SetEliminatePolyPoints(!mpView->IsEliminatePolyPoints()); + Invalidate(SID_BEZIER_ELIMINATE_POINTS); + rReq.Done(); + } + break; + + case SID_BEZIER_MOVE: + case SID_BEZIER_INSERT: + { + rtl::Reference xFunc( mpViewSh->GetCurrentFunction() ); + + if(xFunc.is()) + { + if( auto pFuSelection = dynamic_cast( xFunc.get() )) + { + pFuSelection->SetEditMode(rReq.GetSlot()); + } + else if( auto pFuPolygon = dynamic_cast( xFunc.get() )) + { + pFuPolygon->SetEditMode(rReq.GetSlot()); + } + } + + rReq.Ignore (); + } + break; + + default: + break; + } + + Invalidate(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drtxtob.cxx b/sd/source/ui/view/drtxtob.cxx new file mode 100644 index 000000000..b10af0828 --- /dev/null +++ b/sd/source/ui/view/drtxtob.cxx @@ -0,0 +1,625 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +using namespace sd; +using namespace ::com::sun::star; + +#define ShellClass_TextObjectBar +#include + +namespace sd { + +/** + * Declare default interface (Slotmap must not be empty, therefore enter + * something that (hopefully) never occurs. + */ +SFX_IMPL_INTERFACE(TextObjectBar, SfxShell) + +void TextObjectBar::InitInterface_Impl() +{ +} + + +TextObjectBar::TextObjectBar ( + ViewShell* pSdViewSh, + SfxItemPool& rItemPool, + ::sd::View* pSdView ) + : SfxShell(pSdViewSh->GetViewShell()), + mpViewShell( pSdViewSh ), + mpView( pSdView ) +{ + SetPool(&rItemPool); + + if( mpView ) + { + OutlineView* pOutlinerView = dynamic_cast< OutlineView* >( mpView ); + if( pOutlinerView ) + { + SetUndoManager(&pOutlinerView->GetOutliner().GetUndoManager()); + } + else + { + DrawDocShell* pDocShell = mpView->GetDoc().GetDocSh(); + if( pDocShell ) + { + SetUndoManager(pDocShell->GetUndoManager()); + DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >( pSdViewSh ); + if ( pDrawViewShell ) + SetRepeatTarget(pSdView); + } + } + } + + SetName( "TextObjectBar"); + + // SetHelpId( SD_IF_SDDRAWTEXTOBJECTBAR ); +} + +TextObjectBar::~TextObjectBar() +{ + SetRepeatTarget(nullptr); +} + +void TextObjectBar::GetCharState( SfxItemSet& rSet ) +{ + SfxItemSet aCharAttrSet( mpView->GetDoc().GetPool() ); + mpView->GetAttributes( aCharAttrSet ); + + SfxItemSetFixed aNewAttr( mpViewShell->GetPool() ); + + aNewAttr.Put(aCharAttrSet, false); + rSet.Put(aNewAttr, false); + + SvxKerningItem aKern = aCharAttrSet.Get( EE_CHAR_KERNING ); + //aKern.SetWhich(SID_ATTR_CHAR_KERNING); + rSet.Put(aKern); + + SfxItemState eState = aCharAttrSet.GetItemState( EE_CHAR_KERNING ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_CHAR_KERNING); + } +} + +/** + * Status of attribute items. + */ +void TextObjectBar::GetAttrState( SfxItemSet& rSet ) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + SfxItemSet aAttrSet( mpView->GetDoc().GetPool() ); + bool bDisableParagraphTextDirection = !SvtCTLOptions().IsCTLFontEnabled(); + bool bDisableVerticalText = !SvtCJKOptions::IsVerticalTextEnabled(); + + mpView->GetAttributes( aAttrSet ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + + switch ( nSlotId ) + { + case SID_ATTR_CHAR_FONT: + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_WEIGHT: + case SID_ATTR_CHAR_POSTURE: + case SID_ATTR_CHAR_SHADOWED: + case SID_ATTR_CHAR_STRIKEOUT: + case SID_ATTR_CHAR_CASEMAP: + { + sal_uInt16 stretchX = 100; + SvxScriptSetItem aSetItem( nSlotId, GetPool() ); + aSetItem.GetItemSet().Put( aAttrSet, false ); + + SvtScriptType nScriptType = mpView->GetScriptType(); + + if( (nSlotId == SID_ATTR_CHAR_FONT) || (nSlotId == SID_ATTR_CHAR_FONTHEIGHT) ) + { + // input language should be preferred over + // current cursor position to detect script type + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + SdrOutliner *pOutliner = mpView->GetTextEditOutliner(); + + assert(mpViewShell); + + if (OutlineView* pOView = dynamic_cast(mpView)) + pOLV = pOView->GetViewByWindow(mpViewShell->GetActiveWindow()); + + sal_uInt16 stretchY = 100; + if( pOutliner ) + pOutliner->GetGlobalCharStretching( stretchX, stretchY ); + + if(pOLV && !pOLV->GetSelection().HasRange()) + { + if (mpViewShell->GetViewShell() && mpViewShell->GetViewShell()->GetWindow()) + { + LanguageType nInputLang = mpViewShell->GetViewShell()->GetWindow()->GetInputLanguage(); + if(nInputLang != LANGUAGE_DONTKNOW && nInputLang != LANGUAGE_SYSTEM) + nScriptType = SvtLanguageOptions::GetScriptTypeOfLanguage( nInputLang ); + } + } + } + + const SfxPoolItem* pI = aSetItem.GetItemOfScript( nScriptType ); + if( pI ) + { + if( nSlotId == SID_ATTR_CHAR_FONTHEIGHT ) + { + SvxFontHeightItem aFontItem = dynamic_cast(*pI); + aFontItem.SetHeight(aFontItem.GetHeight(), stretchX, aFontItem.GetPropUnit()); + aFontItem.SetWhich(nWhich); + aAttrSet.Put( aFontItem ); + } + else + { + aAttrSet.Put( pI->CloneSetWhich(nWhich) ); + } + } + else + { + aAttrSet.InvalidateItem( nWhich ); + } + } + break; + + case SID_STYLE_APPLY: + case SID_STYLE_FAMILY2: + { + SfxStyleSheet* pStyleSheet = mpView->GetStyleSheetFromMarked(); + if( pStyleSheet ) + rSet.Put( SfxTemplateItem( nWhich, pStyleSheet->GetName() ) ); + else + { + rSet.Put( SfxTemplateItem( nWhich, OUString() ) ); + } + } + break; + + case SID_OUTLINE_LEFT: + case SID_OUTLINE_RIGHT: + case SID_OUTLINE_UP: + case SID_OUTLINE_DOWN: + { + bool bDisableLeft = true; + bool bDisableRight = true; + bool bDisableUp = true; + bool bDisableDown = true; + + //fdo#78151 it doesn't make sense to promote or demote outline levels in master view. + const DrawViewShell* pDrawViewShell = dynamic_cast< DrawViewShell* >(mpViewShell); + const bool bInMasterView = pDrawViewShell && pDrawViewShell->GetEditMode() == EditMode::MasterPage; + + if (!bInMasterView) + { + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + if (OutlineView* pOView = dynamic_cast(mpView)) + pOLV = pOView->GetViewByWindow(mpViewShell->GetActiveWindow()); + + bool bOutlineViewSh = dynamic_cast< const OutlineViewShell *>( mpViewShell ) != nullptr; + + if (pOLV) + { + // Outliner at outline-mode + ::Outliner* pOutl = pOLV->GetOutliner(); + + std::vector aSelList; + pOLV->CreateSelectionList(aSelList); + Paragraph* pPara = aSelList.empty() ? nullptr : *(aSelList.begin()); + + // find out if we are an OutlineView + bool bIsOutlineView(OutlinerMode::OutlineView == pOLV->GetOutliner()->GetOutlinerMode()); + + // This is ONLY for OutlineViews + if(bIsOutlineView) + { + // allow move up if position is 2 or greater OR it + // is a title object (and thus depth==1) + if(pOutl->GetAbsPos(pPara) > 1 || ( ::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE) && pOutl->GetAbsPos(pPara) > 0 ) ) + { + // not at top + bDisableUp = false; + } + } + else + { + // old behaviour for OutlinerMode::OutlineObject + if(pOutl->GetAbsPos(pPara) > 0) + { + // not at top + bDisableUp = false; + } + } + + for (const auto& rpItem : aSelList) + { + pPara = rpItem; + + sal_Int16 nDepth = pOutl->GetDepth( pOutl->GetAbsPos( pPara ) ); + + if (nDepth > 0 || (bOutlineViewSh && (nDepth <= 0) && !::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE )) ) + { + // not minimum depth + bDisableLeft = false; + } + + if( (nDepth < pOLV->GetOutliner()->GetMaxDepth() && ( !bOutlineViewSh || pOutl->GetAbsPos(pPara) != 0 )) || + (bOutlineViewSh && (nDepth <= 0) && ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) && pOutl->GetAbsPos(pPara) != 0) ) + { + // not maximum depth and not at top + bDisableRight = false; + } + } + + if ( ( pOutl->GetAbsPos(pPara) < pOutl->GetParagraphCount() - 1 ) && + ( pOutl->GetParagraphCount() > 1 || !bOutlineViewSh) ) + { + // not last paragraph + bDisableDown = false; + } + + // disable when first para and 2nd is not a title + pPara = aSelList.empty() ? nullptr : *(aSelList.begin()); + + if(!bDisableDown && bIsOutlineView + && pPara + && 0 == pOutl->GetAbsPos(pPara) + && pOutl->GetParagraphCount() > 1 + && !::Outliner::HasParaFlag( pOutl->GetParagraph(1), ParaFlag::ISPAGE ) ) + { + // Needs to be disabled + bDisableDown = true; + } + } + } + + if (bDisableLeft) + rSet.DisableItem(SID_OUTLINE_LEFT); + if (bDisableRight) + rSet.DisableItem(SID_OUTLINE_RIGHT); + if (bDisableUp) + rSet.DisableItem(SID_OUTLINE_UP); + if (bDisableDown) + rSet.DisableItem(SID_OUTLINE_DOWN); + } + break; + + case SID_TEXTDIRECTION_LEFT_TO_RIGHT: + case SID_TEXTDIRECTION_TOP_TO_BOTTOM: + { + if ( bDisableVerticalText ) + { + rSet.DisableItem( SID_TEXTDIRECTION_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_TEXTDIRECTION_TOP_TO_BOTTOM ); + } + else + { + bool bLeftToRight = true; + + SdrOutliner* pOutl = mpView->GetTextEditOutliner(); + if( pOutl ) + { + if( pOutl->IsVertical() ) + bLeftToRight = false; + } + else + bLeftToRight = aAttrSet.Get( SDRATTR_TEXTDIRECTION ).GetValue() == css::text::WritingMode_LR_TB; + + rSet.Put( SfxBoolItem( SID_TEXTDIRECTION_LEFT_TO_RIGHT, bLeftToRight ) ); + rSet.Put( SfxBoolItem( SID_TEXTDIRECTION_TOP_TO_BOTTOM, !bLeftToRight ) ); + + if( !bLeftToRight ) + bDisableParagraphTextDirection = true; + } + } + break; + + case SID_ULINE_VAL_NONE: + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + if( aAttrSet.GetItemState( EE_CHAR_UNDERLINE ) >= SfxItemState::DEFAULT ) + { + FontLineStyle eLineStyle = aAttrSet.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + + switch (nSlotId) + { + case SID_ULINE_VAL_NONE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_NONE)); + break; + case SID_ULINE_VAL_SINGLE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_SINGLE)); + break; + case SID_ULINE_VAL_DOUBLE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_DOUBLE)); + break; + case SID_ULINE_VAL_DOTTED: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_DOTTED)); + break; + } + } + } + break; + + case SID_GROW_FONT_SIZE: + case SID_SHRINK_FONT_SIZE: + { + // todo + } + break; + + case SID_THES: + { + if (mpView->GetTextEditOutlinerView()) + { + EditView & rEditView = mpView->GetTextEditOutlinerView()->GetEditView(); + OUString aStatusVal; + LanguageType nLang = LANGUAGE_NONE; + bool bIsLookUpWord = GetStatusValueForThesaurusFromContext( aStatusVal, nLang, rEditView ); + rSet.Put( SfxStringItem( SID_THES, aStatusVal ) ); + + // disable "Thesaurus" context menu entry if there is nothing to look up + uno::Reference< linguistic2::XThesaurus > xThes( LinguMgr::GetThesaurus() ); + if (!bIsLookUpWord || + !xThes.is() || nLang == LANGUAGE_NONE || !xThes->hasLocale( LanguageTag( nLang). getLocale() )) + rSet.DisableItem( SID_THES ); + } + else + { + rSet.DisableItem( SID_THES ); + } + } + break; + + default: + break; + } + + nWhich = aIter.NextWhich(); + } + + rSet.Put( aAttrSet, false ); // <- sal_False, so DontCare-Status gets acquired + + // these are disabled in outline-mode + if (!mpViewShell || dynamic_cast< const DrawViewShell *>( mpViewShell ) == nullptr) + { + rSet.DisableItem( SID_ATTR_PARA_ADJUST_LEFT ); + rSet.DisableItem( SID_ATTR_PARA_ADJUST_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_ADJUST_CENTER ); + rSet.DisableItem( SID_ATTR_PARA_ADJUST_BLOCK ); + rSet.DisableItem( SID_ATTR_PARA_LINESPACE_10 ); + rSet.DisableItem( SID_ATTR_PARA_LINESPACE_15 ); + rSet.DisableItem( SID_ATTR_PARA_LINESPACE_20 ); + rSet.DisableItem( SID_DEC_INDENT ); + rSet.DisableItem( SID_INC_INDENT ); + rSet.DisableItem( SID_PARASPACE_INCREASE ); + rSet.DisableItem( SID_PARASPACE_DECREASE ); + rSet.DisableItem( SID_TEXTDIRECTION_TOP_TO_BOTTOM ); + rSet.DisableItem( SID_TEXTDIRECTION_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_RIGHT_TO_LEFT ); + } + else + { + // paragraph spacing + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + if( pOLV ) + { + ESelection aSel = pOLV->GetSelection(); + aSel.Adjust(); + sal_Int32 nStartPara = aSel.nStartPara; + sal_Int32 nEndPara = aSel.nEndPara; + if( !aSel.HasRange() ) + { + nStartPara = 0; + nEndPara = pOLV->GetOutliner()->GetParagraphCount() - 1; + } + ::tools::Long nUpper = 0; + + for( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ ) + { + const SfxItemSet& rItems = pOLV->GetOutliner()->GetParaAttribs( nPara ); + const SvxULSpaceItem& rItem = rItems.Get( EE_PARA_ULSPACE ); + nUpper = std::max( nUpper, static_cast<::tools::Long>(rItem.GetUpper()) ); + } + if( nUpper == 0 ) + rSet.DisableItem( SID_PARASPACE_DECREASE ); + } + else + { + // never disabled at the moment! + //rSet.DisableItem( SID_PARASPACE_INCREASE ); + //rSet.DisableItem( SID_PARASPACE_DECREASE ); + } + + // paragraph justification + const SvxLRSpaceItem& aLR = aAttrSet.Get( EE_PARA_LRSPACE ); + rSet.Put(aLR); + SvxAdjust eAdj = aAttrSet.Get( EE_PARA_JUST ).GetAdjust(); + switch( eAdj ) + { + case SvxAdjust::Left: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, false ) ); + break; + case SvxAdjust::Center: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, false ) ); + break; + case SvxAdjust::Right: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, false ) ); + break; + case SvxAdjust::Block: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, false ) ); + break; + default: + break; + } + + Invalidate(SID_ATTR_PARA_ADJUST_LEFT); + Invalidate(SID_ATTR_PARA_ADJUST_CENTER); + Invalidate(SID_ATTR_PARA_ADJUST_RIGHT); + Invalidate(SID_ATTR_PARA_ADJUST_BLOCK); + Invalidate(SID_ATTR_PARA_LINESPACE); + Invalidate(SID_ATTR_PARA_ULSPACE); + + // paragraph text direction + if( bDisableParagraphTextDirection ) + { + rSet.DisableItem( SID_ATTR_PARA_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_RIGHT_TO_LEFT ); + } + else + { + switch( aAttrSet.Get( EE_PARA_WRITINGDIR ).GetValue() ) + { + case SvxFrameDirection::Vertical_LR_TB: + case SvxFrameDirection::Vertical_RL_TB: + { + rSet.DisableItem( SID_ATTR_PARA_LEFT_TO_RIGHT ); + rSet.DisableItem( SID_ATTR_PARA_RIGHT_TO_LEFT ); + } + break; + + case SvxFrameDirection::Horizontal_LR_TB: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LEFT_TO_RIGHT, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_RIGHT_TO_LEFT, false ) ); + break; + + case SvxFrameDirection::Horizontal_RL_TB: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LEFT_TO_RIGHT, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_RIGHT_TO_LEFT, true ) ); + break; + + // The case for the superordinate object is missing. + case SvxFrameDirection::Environment: + { + SdDrawDocument& rDoc = mpView->GetDoc(); + css::text::WritingMode eMode = rDoc.GetDefaultWritingMode(); + bool bIsLeftToRight(false); + + if(css::text::WritingMode_LR_TB == eMode + || css::text::WritingMode_TB_RL == eMode) + { + bIsLeftToRight = true; + } + + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LEFT_TO_RIGHT, bIsLeftToRight ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_RIGHT_TO_LEFT, !bIsLeftToRight ) ); + } + break; + default: break; + } + } + + SvxLRSpaceItem aLRSpace = aAttrSet.Get( EE_PARA_LRSPACE ); + aLRSpace.SetWhich(SID_ATTR_PARA_LRSPACE); + rSet.Put(aLRSpace); + Invalidate(SID_ATTR_PARA_LRSPACE); + + //Added by xuxu + SfxItemState eState = aAttrSet.GetItemState( EE_PARA_LRSPACE ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_PARA_LRSPACE); + rSet.InvalidateItem(SID_ATTR_PARA_LRSPACE); + } + sal_uInt16 nLineSpace = aAttrSet.Get( EE_PARA_SBL ).GetPropLineSpace(); + switch( nLineSpace ) + { + case 100: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_10, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_15, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_20, false ) ); + break; + case 150: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_15, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_10, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_20, false ) ); + break; + case 200: + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_20, true ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_10, false ) ); + rSet.Put( SfxBoolItem( SID_ATTR_PARA_LINESPACE_15, false ) ); + break; + } + } + + // justification (superscript, subscript) is also needed in outline-mode + SvxEscapement eEsc = static_cast(aAttrSet.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + rSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript)); + rSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript)); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drtxtob1.cxx b/sd/source/ui/view/drtxtob1.cxx new file mode 100644 index 000000000..86b7a698a --- /dev/null +++ b/sd/source/ui/view/drtxtob1.cxx @@ -0,0 +1,865 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace +{ + void lcl_convertStringArguments(sal_uInt16 nSlot, const std::unique_ptr& pArgs) + { + Color aColor; + OUString sColor; + const SfxPoolItem* pColorStringItem = nullptr; + + if (SfxItemState::SET != pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pColorStringItem)) + return; + + sColor = static_cast(pColorStringItem)->GetValue(); + + if (sColor == "transparent") + aColor = COL_TRANSPARENT; + else + aColor = Color(ColorTransparency, sColor.toInt32(16)); + + switch (nSlot) + { + case SID_ATTR_CHAR_COLOR: + { + SvxColorItem aColorItem(aColor, EE_CHAR_COLOR); + pArgs->Put(aColorItem); + break; + } + + case SID_ATTR_CHAR_BACK_COLOR: + { + SvxColorItem pBackgroundItem(aColor, EE_CHAR_BKGCOLOR); + pArgs->Put(pBackgroundItem); + break; + } + } + } +} + +namespace sd { + +/** + * Process SfxRequests + */ + +void TextObjectBar::Execute( SfxRequest &rReq ) +{ + const SfxItemSet* pArgs = rReq.GetArgs(); + sal_uInt16 nSlot = rReq.GetSlot(); + OutlinerView* pOLV = mpView->GetTextEditOutlinerView(); + + std::unique_ptr> aGuard; + + assert(mpViewShell); + + if (OutlineView* pOView = dynamic_cast(mpView)) + { + pOLV = pOView->GetViewByWindow(mpViewShell->GetActiveWindow()); + aGuard.reset( new OutlineViewModelChangeGuard( static_cast(*mpView) ) ); + } + + switch (nSlot) + { + case SID_STYLE_APPLY: + { + if( pArgs ) + { + SdDrawDocument& rDoc = mpView->GetDoc(); + assert(mpViewShell->GetViewShell()); + rtl::Reference xFunc( FuTemplate::Create( mpViewShell, static_cast< ::sd::Window*>( mpViewShell->GetViewShell()->GetWindow()), mpView, &rDoc, rReq ) ); + + if(xFunc.is()) + { + xFunc->Activate(); + xFunc->Deactivate(); + + if( rReq.GetSlot() == SID_STYLE_APPLY ) + { + if (mpViewShell->GetViewFrame()) + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_STYLE_APPLY ); + } + } + } + else + { + if (mpViewShell->GetViewFrame()) + mpViewShell->GetViewFrame()->GetDispatcher()->Execute( SID_STYLE_DESIGNER, SfxCallMode::ASYNCHRON ); + } + + rReq.Done(); + } + break; + + case SID_INC_INDENT: + case SID_DEC_INDENT: + { + if( pOLV ) + { + ESelection aSel = pOLV->GetSelection(); + aSel.Adjust(); + sal_Int32 nStartPara = aSel.nStartPara; + sal_Int32 nEndPara = aSel.nEndPara; + if( !aSel.HasRange() ) + { + nStartPara = 0; + nEndPara = pOLV->GetOutliner()->GetParagraphCount() - 1; + } + + pOLV->GetOutliner()->UndoActionStart( OLUNDO_ATTR ); + for( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ ) + { + SfxStyleSheet* pStyleSheet = nullptr; + if (pOLV->GetOutliner() != nullptr) + pStyleSheet = pOLV->GetOutliner()->GetStyleSheet(nPara); + if (pStyleSheet != nullptr) + { + SfxItemSet aAttr( pStyleSheet->GetItemSet() ); + SfxItemSet aTmpSet( pOLV->GetOutliner()->GetParaAttribs( nPara ) ); + aAttr.Put( aTmpSet, false ); + const SvxLRSpaceItem& rItem = aAttr.Get( EE_PARA_LRSPACE ); + std::unique_ptr pNewItem(rItem.Clone()); + + ::tools::Long nLeft = pNewItem->GetLeft(); + if( nSlot == SID_INC_INDENT ) + nLeft += 1000; + else + { + nLeft -= 1000; + nLeft = std::max<::tools::Long>( nLeft, 0 ); + } + pNewItem->SetLeftValue( static_cast(nLeft) ); + + SfxItemSet aNewAttrs( aAttr ); + aNewAttrs.Put( std::move(pNewItem) ); + pOLV->GetOutliner()->SetParaAttribs( nPara, aNewAttrs ); + } + } + pOLV->GetOutliner()->UndoActionEnd(); + mpViewShell->Invalidate( SID_UNDO ); + } + rReq.Done(); + + Invalidate(); + // to refresh preview (in outline mode), slot has to be invalidated: + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + + } + break; + + case SID_PARASPACE_INCREASE: + case SID_PARASPACE_DECREASE: + { + if( pOLV ) + { + ESelection aSel = pOLV->GetSelection(); + aSel.Adjust(); + sal_Int32 nStartPara = aSel.nStartPara; + sal_Int32 nEndPara = aSel.nEndPara; + if( !aSel.HasRange() ) + { + nStartPara = 0; + nEndPara = pOLV->GetOutliner()->GetParagraphCount() - 1; + } + + pOLV->GetOutliner()->UndoActionStart( OLUNDO_ATTR ); + for( sal_Int32 nPara = nStartPara; nPara <= nEndPara; nPara++ ) + { + SfxStyleSheet* pStyleSheet = nullptr; + if (pOLV->GetOutliner() != nullptr) + pStyleSheet = pOLV->GetOutliner()->GetStyleSheet(nPara); + if (pStyleSheet != nullptr) + { + SfxItemSet aAttr( pStyleSheet->GetItemSet() ); + SfxItemSet aTmpSet( pOLV->GetOutliner()->GetParaAttribs( nPara ) ); + aAttr.Put( aTmpSet, false ); // sal_False= InvalidItems is not default, handle it as "holes" + const SvxULSpaceItem& rItem = aAttr.Get( EE_PARA_ULSPACE ); + std::unique_ptr pNewItem(rItem.Clone()); + + ::tools::Long nUpper = pNewItem->GetUpper(); + if( nSlot == SID_PARASPACE_INCREASE ) + nUpper += 100; + else + { + nUpper -= 100; + nUpper = std::max<::tools::Long>( nUpper, 0 ); + } + pNewItem->SetUpper( static_cast(nUpper) ); + + ::tools::Long nLower = pNewItem->GetLower(); + if( nSlot == SID_PARASPACE_INCREASE ) + nLower += 100; + else + { + nLower -= 100; + nLower = std::max<::tools::Long>( nLower, 0 ); + } + pNewItem->SetLower( static_cast(nLower) ); + + SfxItemSet aNewAttrs( aAttr ); + aNewAttrs.Put( std::move(pNewItem) ); + pOLV->GetOutliner()->SetParaAttribs( nPara, aNewAttrs ); + } + } + pOLV->GetOutliner()->UndoActionEnd(); + mpViewShell->Invalidate( SID_UNDO ); + } + else + { + // the following code could be enabled, if I get a correct + // DontCare status from JOE. + + // gets enabled, through it doesn't really work (see above) + SfxItemSet aEditAttr( mpView->GetDoc().GetPool() ); + mpView->GetAttributes( aEditAttr ); + if( aEditAttr.GetItemState( EE_PARA_ULSPACE ) >= SfxItemState::DEFAULT ) + { + SfxItemSet aNewAttrs(*(aEditAttr.GetPool()), aEditAttr.GetRanges()); + const SvxULSpaceItem& rItem = aEditAttr.Get( EE_PARA_ULSPACE ); + std::unique_ptr pNewItem(rItem.Clone()); + ::tools::Long nUpper = pNewItem->GetUpper(); + + if( nSlot == SID_PARASPACE_INCREASE ) + nUpper += 100; + else + { + nUpper -= 100; + nUpper = std::max<::tools::Long>( nUpper, 0 ); + } + pNewItem->SetUpper( static_cast(nUpper) ); + + ::tools::Long nLower = pNewItem->GetLower(); + if( nSlot == SID_PARASPACE_INCREASE ) + nLower += 100; + else + { + nLower -= 100; + nLower = std::max<::tools::Long>( nLower, 0 ); + } + pNewItem->SetLower( static_cast(nLower) ); + + aNewAttrs.Put( std::move(pNewItem) ); + + mpView->SetAttributes( aNewAttrs ); + } + } + rReq.Done(); + + Invalidate(); + // to refresh preview (in outline mode), slot has to be invalidated: + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_ATTR_PARA_ULSPACE, true ); + } + break; + + case SID_OUTLINE_LEFT: + { + if (pOLV) + { + pOLV->AdjustDepth( -1 ); + + // Ensure bold/italic etc. icon state updates + Invalidate(); + // trigger preview refresh + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + rReq.Done(); + } + break; + + case SID_OUTLINE_RIGHT: + { + if (pOLV) + { + pOLV->AdjustDepth( 1 ); + + // Ensure bold/italic etc. icon state updates + Invalidate(); + // trigger preview refresh + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + rReq.Done(); + } + break; + + case SID_ATTR_PARA_LRSPACE: + { + SvxLRSpaceItem aLRSpace = static_cast(pArgs->Get( + SID_ATTR_PARA_LRSPACE)); + + SfxItemSetFixed aEditAttr( GetPool() ); + aLRSpace.SetWhich( EE_PARA_LRSPACE ); + + aEditAttr.Put( aLRSpace ); + mpView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + + case SID_HANGING_INDENT: + { + SfxItemSetFixed aLRSpaceSet( GetPool() ); + mpView->GetAttributes( aLRSpaceSet ); + SvxLRSpaceItem aParaMargin( aLRSpaceSet.Get( EE_PARA_LRSPACE ) ); + + SvxLRSpaceItem aNewMargin( EE_PARA_LRSPACE ); + aNewMargin.SetTextLeft( aParaMargin.GetTextLeft() + aParaMargin.GetTextFirstLineOffset() ); + aNewMargin.SetRight( aParaMargin.GetRight() ); + aNewMargin.SetTextFirstLineOffset( ( aParaMargin.GetTextFirstLineOffset() ) * -1 ); + aLRSpaceSet.Put( aNewMargin ); + mpView->SetAttributes( aLRSpaceSet ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + + case SID_OUTLINE_UP: + { + if (pOLV) + { + pOLV->AdjustHeight( -1 ); + + // trigger preview refresh + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + rReq.Done(); + } + break; + + case SID_OUTLINE_DOWN: + { + if (pOLV) + { + pOLV->AdjustHeight( 1 ); + + // trigger preview refresh + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + rReq.Done(); + } + break; + + case SID_TEXTDIRECTION_LEFT_TO_RIGHT: + case SID_TEXTDIRECTION_TOP_TO_BOTTOM: + { + mpView->SdrEndTextEdit(); + // tdf#131571: SdrEndTextEdit invalidates pTextEditOutlinerView, the pointer retrieved for pOLV + // so reinitialize pOLV + pOLV=mpView->GetTextEditOutlinerView(); + SfxItemSetFixed aAttr( mpView->GetDoc().GetPool() ); + aAttr.Put( SvxWritingModeItem( + nSlot == SID_TEXTDIRECTION_LEFT_TO_RIGHT ? + css::text::WritingMode_LR_TB : css::text::WritingMode_TB_RL, + SDRATTR_TEXTDIRECTION ) ); + rReq.Done( aAttr ); + mpView->SetAttributes( aAttr ); + Invalidate(); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + break; + + case FN_NUM_BULLET_ON: + { + if (pOLV) + { + bool bMasterPage = false; + SdrPageView* pPageView = mpView->GetSdrPageView(); + if (pPageView) + { + SdPage* pPage = static_cast(pPageView->GetPage()); + bMasterPage = pPage && (pPage->GetPageKind() == PageKind::Standard) && pPage->IsMasterPage(); + } + + if (!bMasterPage) + pOLV->ToggleBullets(); + else + { + //Resolves: fdo#78151 in master pages if we toggle bullets on + //and off then just disable/enable the bulleting, but do not + //change the *level* of the paragraph, because the paragraph is + //effectively a preview of the equivalent style level, and + //changing the level disconnects it from the style + + ::Outliner* pOL = pOLV->GetOutliner(); + if (pOL) + { + const SvxNumBulletItem *pItem = nullptr; + SfxStyleSheetBasePool* pSSPool = mpView->GetDocSh()->GetStyleSheetPool(); + OUString sStyleName(SdResId(STR_PSEUDOSHEET_OUTLINE) + " 1"); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find(sStyleName, SfxStyleFamily::Pseudo); + if( pFirstStyleSheet ) + pItem = pFirstStyleSheet->GetItemSet().GetItemIfSet(EE_PARA_NUMBULLET, false); + + if (pItem ) + { + SvxNumRule aNewRule(pItem->GetNumRule()); + ESelection aSel = pOLV->GetSelection(); + aSel.Adjust(); + sal_Int32 nStartPara = aSel.nStartPara; + sal_Int32 nEndPara = aSel.nEndPara; + for (sal_Int32 nPara = nStartPara; nPara <= nEndPara; ++nPara) + { + sal_uInt16 nLevel = pOL->GetDepth(nPara); + SvxNumberFormat aFmt(aNewRule.GetLevel(nLevel)); + + if (aFmt.GetNumberingType() == SVX_NUM_NUMBER_NONE) + { + aFmt.SetNumberingType(SVX_NUM_CHAR_SPECIAL); + SdStyleSheetPool::setDefaultOutlineNumberFormatBulletAndIndent(nLevel, aFmt); + } + else + { + aFmt.SetNumberingType(SVX_NUM_NUMBER_NONE); + aFmt.SetAbsLSpace(0); + aFmt.SetFirstLineOffset(0); + } + + aNewRule.SetLevel(nLevel, aFmt); + } + + pFirstStyleSheet->GetItemSet().Put(SvxNumBulletItem(std::move(aNewRule), EE_PARA_NUMBULLET)); + + SdStyleSheet::BroadcastSdStyleSheetChange(pFirstStyleSheet, PresentationObjects::Outline_1, pSSPool); + } + } + } + } + break; + } + case SID_GROW_FONT_SIZE: + case SID_SHRINK_FONT_SIZE: + { + const SvxFontListItem* pFonts = static_cast(mpViewShell->GetDocSh()->GetItem( SID_ATTR_CHAR_FONTLIST )); + const FontList* pFontList = pFonts ? pFonts->GetFontList(): nullptr; + if( pFontList ) + { + FuText::ChangeFontSize( nSlot == SID_GROW_FONT_SIZE, pOLV, pFontList, mpView ); + if( pOLV ) + pOLV->SetAttribs( pOLV->GetEditView().GetEmptyItemSet() ); + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + rReq.Done(); + } + break; + + case SID_THES: + { + OUString aReplaceText; + const SfxStringItem* pItem2 = rReq.GetArg(FN_PARAM_THES_WORD_REPLACE); + if (pItem2) + aReplaceText = pItem2->GetValue(); + if (!aReplaceText.isEmpty()) + ReplaceTextWithSynonym( pOLV->GetEditView(), aReplaceText ); + } + break; + + default: + { + SfxItemSet aEditAttr( mpView->GetDoc().GetPool() ); + mpView->GetAttributes( aEditAttr ); + SfxItemSet aNewAttr(*(aEditAttr.GetPool()), aEditAttr.GetRanges()); + + if( !pArgs ) + { + //aNewAttr.InvalidateAllItems(); <- produces problems (#35465#) + + switch ( nSlot ) + { + case SID_ATTR_CHAR_WEIGHT: + { + FontWeight eFW = aEditAttr.Get( EE_CHAR_WEIGHT ).GetWeight(); + aNewAttr.Put( SvxWeightItem( eFW == WEIGHT_NORMAL ? + WEIGHT_BOLD : WEIGHT_NORMAL, + EE_CHAR_WEIGHT ) ); + } + break; + case SID_ATTR_CHAR_POSTURE: + { + FontItalic eFI = aEditAttr.Get( EE_CHAR_ITALIC ).GetPosture(); + aNewAttr.Put( SvxPostureItem( eFI == ITALIC_NORMAL ? + ITALIC_NONE : ITALIC_NORMAL, + EE_CHAR_ITALIC ) ); + } + break; + case SID_ATTR_CHAR_UNDERLINE: + { + FontLineStyle eFU = aEditAttr.Get( EE_CHAR_UNDERLINE ).GetLineStyle(); + aNewAttr.Put( SvxUnderlineItem( eFU == LINESTYLE_SINGLE ? + LINESTYLE_NONE : LINESTYLE_SINGLE, + EE_CHAR_UNDERLINE ) ); + } + break; + + case SID_ULINE_VAL_NONE: + { + aNewAttr.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE)); + break; + } + + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + FontLineStyle eOld = aEditAttr.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + FontLineStyle eNew = eOld; + + switch (nSlot) + { + case SID_ULINE_VAL_SINGLE: + eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE; + break; + case SID_ULINE_VAL_DOUBLE: + eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE; + break; + case SID_ULINE_VAL_DOTTED: + eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED; + break; + } + + SvxUnderlineItem aUnderline(eNew, EE_CHAR_UNDERLINE); + aNewAttr.Put(aUnderline); + } + break; + + case SID_ATTR_CHAR_OVERLINE: + { + FontLineStyle eFO = aEditAttr.Get( EE_CHAR_OVERLINE ).GetLineStyle(); + aNewAttr.Put( SvxOverlineItem( eFO == LINESTYLE_SINGLE ? + LINESTYLE_NONE : LINESTYLE_SINGLE, + EE_CHAR_OVERLINE ) ); + } + break; + case SID_ATTR_CHAR_CONTOUR: + { + aNewAttr.Put( SvxContourItem( !aEditAttr.Get( EE_CHAR_OUTLINE ).GetValue(), EE_CHAR_OUTLINE ) ); + } + break; + case SID_ATTR_CHAR_SHADOWED: + { + aNewAttr.Put( SvxShadowedItem( !aEditAttr.Get( EE_CHAR_SHADOW ).GetValue(), EE_CHAR_SHADOW ) ); + } + break; + case SID_ATTR_CHAR_CASEMAP: + { + aNewAttr.Put( aEditAttr.Get( EE_CHAR_CASEMAP ) ); + } + break; + case SID_ATTR_CHAR_STRIKEOUT: + { + FontStrikeout eFSO = aEditAttr.Get( EE_CHAR_STRIKEOUT ).GetStrikeout(); + aNewAttr.Put( SvxCrossedOutItem( eFSO == STRIKEOUT_SINGLE ? + STRIKEOUT_NONE : STRIKEOUT_SINGLE, EE_CHAR_STRIKEOUT ) ); + } + break; + + case SID_ATTR_PARA_ADJUST_LEFT: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_CENTER: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Block, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_LINESPACE_10: + { + SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ); + aItem.SetPropLineSpace( 100 ); + aNewAttr.Put( aItem ); + } + break; + case SID_ATTR_PARA_LINESPACE_15: + { + SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ); + aItem.SetPropLineSpace( 150 ); + aNewAttr.Put( aItem ); + } + break; + case SID_ATTR_PARA_LINESPACE_20: + { + SvxLineSpacingItem aItem( LINE_SPACE_DEFAULT_HEIGHT, EE_PARA_SBL ); + aItem.SetPropLineSpace( 200 ); + aNewAttr.Put( aItem ); + } + break; + case SID_SET_SUPER_SCRIPT: + { + SvxEscapementItem aItem( EE_CHAR_ESCAPEMENT ); + SvxEscapement eEsc = static_cast(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Superscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Superscript ); + aNewAttr.Put( aItem ); + } + break; + case SID_SET_SUB_SCRIPT: + { + SvxEscapementItem aItem( EE_CHAR_ESCAPEMENT ); + SvxEscapement eEsc = static_cast(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Subscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Subscript ); + aNewAttr.Put( aItem ); + } + break; + + // attributes for TextObjectBar + case SID_ATTR_CHAR_FONT: + mpViewShell->GetViewFrame()->GetDispatcher()-> + Execute( SID_CHAR_DLG, SfxCallMode::ASYNCHRON ); + break; + case SID_ATTR_CHAR_FONTHEIGHT: + mpViewShell->GetViewFrame()->GetDispatcher()-> + Execute( SID_CHAR_DLG, SfxCallMode::ASYNCHRON ); + break; + case SID_ATTR_CHAR_COLOR: + break; +// #i35937# removed need for FN_NUM_BULLET_ON handling + } + + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if ( nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT || + nSlot == SID_ATTR_PARA_RIGHT_TO_LEFT ) + { + bool bLeftToRight = nSlot == SID_ATTR_PARA_LEFT_TO_RIGHT; + + SvxAdjust nAdjust = SvxAdjust::Left; + if( const SvxAdjustItem* pAdjustItem = aEditAttr.GetItemIfSet(EE_PARA_JUST) ) + nAdjust = pAdjustItem->GetAdjust(); + + if( bLeftToRight ) + { + aNewAttr.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR ) ); + if( nAdjust == SvxAdjust::Right ) + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) ); + } + else + { + aNewAttr.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) ); + if( nAdjust == SvxAdjust::Left ) + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + } + + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + + Invalidate( SID_RULER_TEXT_RIGHT_TO_LEFT ); + } + else if ( nSlot == SID_ATTR_CHAR_FONT || + nSlot == SID_ATTR_CHAR_FONTHEIGHT || + nSlot == SID_ATTR_CHAR_POSTURE || + nSlot == SID_ATTR_CHAR_WEIGHT ) + { + // #i78017 establish the same behaviour as in Writer + SvtScriptType nScriptType = SvtScriptType::LATIN | SvtScriptType::ASIAN | SvtScriptType::COMPLEX; + if (nSlot == SID_ATTR_CHAR_FONT) + nScriptType = mpView->GetScriptType(); + + SfxItemPool& rPool = mpView->GetDoc().GetPool(); + SvxScriptSetItem aSvxScriptSetItem( nSlot, rPool ); + aSvxScriptSetItem.PutItemForScriptType( nScriptType, pArgs->Get( rPool.GetWhich( nSlot ) ) ); + aNewAttr.Put( aSvxScriptSetItem.GetItemSet() ); + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if (nSlot == SID_ATTR_PARA_ADJUST_LEFT || + nSlot == SID_ATTR_PARA_ADJUST_CENTER || + nSlot == SID_ATTR_PARA_ADJUST_RIGHT || + nSlot == SID_ATTR_PARA_ADJUST_BLOCK) + { + switch( nSlot ) + { + case SID_ATTR_PARA_ADJUST_LEFT: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Left, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_CENTER: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Center, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Right, EE_PARA_JUST ) ); + } + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + { + aNewAttr.Put( SvxAdjustItem( SvxAdjust::Block, EE_PARA_JUST ) ); + } + break; + } + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if(nSlot == SID_ATTR_CHAR_KERNING) + { + aNewAttr.Put(pArgs->Get(pArgs->GetPool()->GetWhich(nSlot))); + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if(nSlot == SID_SET_SUPER_SCRIPT ) + { + SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT); + SvxEscapement eEsc = static_cast(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Superscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Superscript ); + aNewAttr.Put( aItem ); + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + else if( nSlot == SID_SET_SUB_SCRIPT ) + { + SvxEscapementItem aItem(EE_CHAR_ESCAPEMENT); + SvxEscapement eEsc = static_cast(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + + if( eEsc == SvxEscapement::Subscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Subscript ); + aNewAttr.Put( aItem ); + rReq.Done( aNewAttr ); + pArgs = rReq.GetArgs(); + } + + std::unique_ptr pNewArgs = pArgs->Clone(); + lcl_convertStringArguments(nSlot, pNewArgs); + + // Merge the color parameters to the color itself. + std::unique_ptr pColorItem; + if (nSlot == SID_ATTR_CHAR_COLOR) + { + pColorItem = std::make_unique(pNewArgs->Get(EE_CHAR_COLOR)); + } + if (const SfxInt16Item* pIntItem = pArgs->GetItemIfSet(SID_ATTR_COLOR_THEME_INDEX, false)) + { + pColorItem->GetThemeColor().SetThemeIndex(pIntItem->GetValue()); + } + if (const SfxInt16Item* pIntItem = pArgs->GetItemIfSet(SID_ATTR_COLOR_LUM_MOD, false)) + { + pColorItem->GetThemeColor().SetLumMod(pIntItem->GetValue()); + } + if (const SfxInt16Item* pIntItem = pArgs->GetItemIfSet(SID_ATTR_COLOR_LUM_OFF, false)) + { + pColorItem->GetThemeColor().SetLumOff(pIntItem->GetValue()); + } + if (pColorItem) + { + pNewArgs->Put(std::move(pColorItem)); + } + + mpView->SetAttributes(*pNewArgs); + + // invalidate entire shell because of performance and + // extension reasons + Invalidate(); + + // to refresh preview (in outline mode), slot has to be invalidated: + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_PREVIEW_STATE, true ); + } + break; + } + + if ( nSlot != SID_STYLE_APPLY && pOLV ) + { + pOLV->ShowCursor(); + pOLV->GetWindow()->GrabFocus(); + } + + Invalidate( SID_OUTLINE_LEFT ); + Invalidate( SID_OUTLINE_RIGHT ); + Invalidate( SID_OUTLINE_UP ); + Invalidate( SID_OUTLINE_DOWN ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews1.cxx b/sd/source/ui/view/drviews1.cxx new file mode 100644 index 000000000..085bc93f2 --- /dev/null +++ b/sd/source/ui/view/drviews1.cxx @@ -0,0 +1,1360 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + +void DrawViewShell::Activate(bool bIsMDIActivate) +{ + ViewShell::Activate(bIsMDIActivate); + + // When the mode is switched to normal the main view shell grabs focus. + // This is done for getting cut/copy/paste commands on slides in the left + // pane (slide sorter view shell) to work properly. + SfxShell* pTopViewShell = this->GetViewShellBase().GetViewShellManager()->GetTopViewShell(); + if (pTopViewShell && pTopViewShell == this) + { + this->GetActiveWindow()->GrabFocus(); + } +} + +void DrawViewShell::UIActivating( SfxInPlaceClient* pCli ) +{ + ViewShell::UIActivating(pCli); + + // Disable own controls + maTabControl->Disable(); + if (GetLayerTabControl() != nullptr) + GetLayerTabControl()->Disable(); +} + +void DrawViewShell::UIDeactivated( SfxInPlaceClient* pCli ) +{ + // Enable own controls + maTabControl->Enable(); + if (GetLayerTabControl() != nullptr) + GetLayerTabControl()->Enable(); + + ViewShell::UIDeactivated(pCli); +} + +void DrawViewShell::Deactivate(bool bIsMDIActivate) +{ + // Temporarily disable context broadcasting while the Deactivate() + // call is forwarded to our base class. + const bool bIsContextBroadcasterEnabled (SfxShell::SetContextBroadcasterEnabled(false)); + + ViewShell::Deactivate(bIsMDIActivate); + + SfxShell::SetContextBroadcasterEnabled(bIsContextBroadcasterEnabled); +} + +namespace +{ + class LockUI + { + private: + void Lock(bool bLock); + SfxViewFrame *mpFrame; + public: + explicit LockUI(SfxViewFrame *pFrame) : mpFrame(pFrame) { Lock(true); } + ~LockUI() { Lock(false); } + + }; + + void LockUI::Lock(bool bLock) + { + if (!mpFrame) + return; + mpFrame->Enable( !bLock ); + } +} + +/** + * Called, if state of selection of view is changed + */ + +void DrawViewShell::SelectionHasChanged() +{ + Invalidate(); + + //Update3DWindow(); // 3D-Controller + SfxBoolItem aItem( SID_3D_STATE, true ); + GetViewFrame()->GetDispatcher()->ExecuteList( + SID_3D_STATE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + + SdrOle2Obj* pOleObj = nullptr; + + if ( mpDrawView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::OLE2) + { + pOleObj = static_cast(pObj); + UpdateIMapDlg( pObj ); + } + else if (nSdrObjKind == SdrObjKind::Graphic) + UpdateIMapDlg( pObj ); + } + } + + ViewShellBase& rBase = GetViewShellBase(); + rBase.SetVerbs( uno::Sequence< embed::VerbDescriptor >() ); + + try + { + Client* pIPClient = static_cast(rBase.GetIPClient()); + if ( pIPClient && pIPClient->IsObjectInPlaceActive() ) + { + // as appropriate take ole-objects into account and deactivate + + // this means we recently deselected an inplace active ole object so + // we need to deselect it now + if (!pOleObj) + { + //#i47279# disable frame until after object has completed unload + LockUI aUILock(GetViewFrame()); + pIPClient->DeactivateObject(); + //HMHmpDrView->ShowMarkHdl(); + } + else + { + const uno::Reference < embed::XEmbeddedObject >& xObj = pOleObj->GetObjRef(); + if ( xObj.is() ) + { + rBase.SetVerbs( xObj->getSupportedVerbs() ); + } + else + { + rBase.SetVerbs( uno::Sequence < embed::VerbDescriptor >() ); + } + } + } + else + { + if ( pOleObj ) + { + const uno::Reference < embed::XEmbeddedObject >& xObj = pOleObj->GetObjRef(); + if ( xObj.is() ) + { + rBase.SetVerbs( xObj->getSupportedVerbs() ); + } + else + { + rBase.SetVerbs( uno::Sequence < embed::VerbDescriptor >() ); + } + } + else + { + rBase.SetVerbs( uno::Sequence < embed::VerbDescriptor >() ); + } + } + } + catch( css::uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "sd::DrawViewShell::SelectionHasChanged()" ); + } + + if( HasCurrentFunction() ) + { + GetCurrentFunction()->SelectionHasChanged(); + } + else + { + GetViewShellBase().GetToolBarManager()->SelectionHasChanged(*this,*mpDrawView); + } + + // Invalidate for every subshell + GetViewShellBase().GetViewShellManager()->InvalidateAllSubShells(this); + + mpDrawView->UpdateSelectionClipboard(); + + GetViewShellBase().GetDrawController().FireSelectionChangeListener(); +} + +namespace { + +void collectUIInformation(const OUString& aZoom) +{ + EventDescription aDescription; + aDescription.aID = "impress_win"; + aDescription.aParameters = {{"ZOOM", aZoom}}; + aDescription.aAction = "SET"; + aDescription.aKeyWord = "ImpressWindowUIObject"; + aDescription.aParent = "MainWindow"; + + UITestLogger::getInstance().logEvent(aDescription); +} + +} + +/** + * set zoom factor + */ +void DrawViewShell::SetZoom( ::tools::Long nZoom ) +{ + // Make sure that the zoom factor will not be recalculated on + // following window resizings. + mbZoomOnPage = false; + ViewShell::SetZoom( nZoom ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); + mpViewOverlayManager->onZoomChanged(); + collectUIInformation(OUString::number(nZoom)); +} + +/** + * Set zoom rectangle for active window + */ + +void DrawViewShell::SetZoomRect( const ::tools::Rectangle& rZoomRect ) +{ + ViewShell::SetZoomRect( rZoomRect ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); + mpViewOverlayManager->onZoomChanged(); +} + +/** + * PrepareClose, as appropriate end text input, so other viewshells + * discover a refreshed text object. + */ + +bool DrawViewShell::PrepareClose( bool bUI ) +{ + if ( !ViewShell::PrepareClose(bUI) ) + return false; + + if( HasCurrentFunction() ) + { + sal_uInt16 nID = GetCurrentFunction()->GetSlotID(); + if (nID == SID_TEXTEDIT || nID == SID_ATTR_CHAR) + { + mpDrawView->SdrEndTextEdit(); + } + } + + return true; +} + + +/** + * Set status (enabled/disabled) of menu SfxSlots + */ + +void DrawViewShell::ChangeEditMode(EditMode eEMode, bool bIsLayerModeActive) +{ + if (meEditMode == eEMode && mbIsLayerModeActive == bIsLayerModeActive) + return; + + ViewShellManager::UpdateLock aLock (GetViewShellBase().GetViewShellManager()); + + sal_uInt16 nActualPageId = maTabControl->GetPageId(0); + + if (mePageKind == PageKind::Handout) + { + // at handouts only allow MasterPage + eEMode = EditMode::MasterPage; + } + + GetViewShellBase().GetDrawController().FireChangeEditMode (eEMode == EditMode::MasterPage); + GetViewShellBase().GetDrawController().FireChangeLayerMode (bIsLayerModeActive); + + if ( mpDrawView->IsTextEdit() ) + { + // This exits the text edit mode when going in and out of window focus, which is not needed + // Let's keep this call as comment for now as it probably just needs a better conditional. + // mpDrawView->SdrEndTextEdit(); + } + + LayerTabBar* pLayerBar = GetLayerTabControl(); + if (pLayerBar != nullptr) + pLayerBar->EndEditMode(); + maTabControl->EndEditMode(); + + GetViewShellBase().GetDrawController().BroadcastContextChange(); + + meEditMode = eEMode; + + if(pLayerBar) + { + // #i87182# only switch activation mode of LayerTabBar when there is one, + // else it will not get initialized with the current set of Layers as needed + mbIsLayerModeActive = bIsLayerModeActive; + } + + // Determine whether to show the master view toolbar. The master + // page mode has to be active and the shell must not be a handout + // view. + bool bShowMasterViewToolbar (meEditMode == EditMode::MasterPage + && GetShellType() != ViewShell::ST_HANDOUT); + bool bShowPresentationToolbar (meEditMode != EditMode::MasterPage + && GetShellType() != ViewShell::ST_HANDOUT + && GetShellType() != ViewShell::ST_DRAW); + + // If the master view toolbar is not shown we hide it before + // switching the edit mode. + if (::sd::ViewShell::mpImpl->mbIsInitialized + && IsMainViewShell()) + { + if ( !bShowMasterViewToolbar ) + GetViewShellBase().GetToolBarManager()->ResetToolBars(ToolBarManager::ToolBarGroup::MasterMode); + if ( !bShowPresentationToolbar ) + GetViewShellBase().GetToolBarManager()->ResetToolBars(ToolBarManager::ToolBarGroup::CommonTask); + } + + ConfigureAppBackgroundColor(); + + if (meEditMode == EditMode::Page) + { + /****************************************************************** + * PAGEMODE + ******************************************************************/ + + maTabControl->Clear(); + + SdPage* pPage; + sal_uInt16 nPageCnt = GetDoc()->GetSdPageCount(mePageKind); + + for (sal_uInt16 i = 0; i < nPageCnt; i++) + { + pPage = GetDoc()->GetSdPage(i, mePageKind); + OUString aPageName = pPage->GetName(); + maTabControl->InsertPage(pPage->getPageId(), aPageName); + + if ( pPage->IsSelected() ) + { + nActualPageId = pPage->getPageId(); + } + } + + maTabControl->SetCurPageId(nActualPageId); + + SwitchPage(maTabControl->GetPagePos(nActualPageId)); + + //tdf#102343 re-enable common undo on switch back from master mode + mpDrawView->GetModel()->SetDisableTextEditUsesCommonUndoManager(false); + } + else + { + /****************************************************************** + * MASTERPAGE + ******************************************************************/ + GetViewFrame()->SetChildWindow( + AnimationChildWindow::GetChildWindowId(), false ); + + if (comphelper::LibreOfficeKit::isActive()) + GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, + ".uno:SlideMasterPage=true"); + if (!mpActualPage) + { + // as long as there is no mpActualPage, take first + mpActualPage = GetDoc()->GetSdPage(0, mePageKind); + } + + maTabControl->Clear(); + sal_uInt16 nActualMasterPageId = maTabControl->GetPageId(0); + sal_uInt16 nMasterPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + + for (sal_uInt16 i = 0; i < nMasterPageCnt; i++) + { + SdPage* pMaster = GetDoc()->GetMasterSdPage(i, mePageKind); + OUString aLayoutName = pMaster->GetLayoutName(); + sal_Int32 nPos = aLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayoutName = aLayoutName.copy(0, nPos); + + maTabControl->InsertPage(pMaster->getPageId(), aLayoutName); + + if (&(mpActualPage->TRG_GetMasterPage()) == pMaster) + { + nActualMasterPageId = pMaster->getPageId(); + } + } + + maTabControl->SetCurPageId(nActualMasterPageId); + SwitchPage(maTabControl->GetPagePos(nActualMasterPageId)); + + //tdf#102343 changing attributes of textboxes in master typically + //changes the stylesheet they are linked to, so if the common + //undo manager is in use, those stylesheet changes are thrown + //away at present + mpDrawView->GetModel()->SetDisableTextEditUsesCommonUndoManager(true); + } + + // If the master view toolbar is to be shown we turn it on after the + // edit mode has been changed. + if (::sd::ViewShell::mpImpl->mbIsInitialized + && IsMainViewShell()) + { + if (bShowMasterViewToolbar) + GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::MasterMode, + ToolBarManager::msMasterViewToolBar); + if (bShowPresentationToolbar) + GetViewShellBase().GetToolBarManager()->SetToolBar( + ToolBarManager::ToolBarGroup::CommonTask, + ToolBarManager::msCommonTaskToolBar); + } + + if ( ! mbIsLayerModeActive) + { + maTabControl->Show(); + // Set the tab control only for draw pages. For master page + // this has been done already above. + if (meEditMode == EditMode::Page) + maTabControl->SetCurPageId (nActualPageId); + } + + ResetActualLayer(); + + Invalidate( SID_PAGEMODE ); + Invalidate( SID_LAYERMODE ); + Invalidate( SID_MASTERPAGE ); + Invalidate( SID_DELETE_MASTER_PAGE ); + Invalidate( SID_DELETE_PAGE ); + Invalidate( SID_SLIDE_MASTER_MODE ); + Invalidate( SID_NOTES_MASTER_MODE ); + Invalidate( SID_HANDOUT_MASTER_MODE ); + InvalidateWindows(); + + SetContextName(GetSidebarContextName()); + +} + +/** + * Generate horizontal ruler + */ + +VclPtr DrawViewShell::CreateHRuler (::sd::Window* pWin) +{ + VclPtr pRuler; + WinBits aWBits; + SvxRulerSupportFlags nFlags = SvxRulerSupportFlags::OBJECT; + + aWBits = WB_HSCROLL | WB_3DLOOK | WB_BORDER | WB_EXTRAFIELD; + nFlags |= SvxRulerSupportFlags::SET_NULLOFFSET | + SvxRulerSupportFlags::TABS | + SvxRulerSupportFlags::PARAGRAPH_MARGINS; // new + + pRuler = VclPtr::Create(*this, GetParentWindow(), pWin, nFlags, + GetViewFrame()->GetBindings(), aWBits); + + // Metric ... + sal_uInt16 nMetric = static_cast(GetDoc()->GetUIUnit()); + + if( nMetric == 0xffff ) + nMetric = static_cast(GetViewShellBase().GetViewFrame()->GetDispatcher()->GetModule()->GetFieldUnit()); + + pRuler->SetUnit( FieldUnit( nMetric ) ); + + // ... and also set DefTab at the ruler + pRuler->SetDefTabDist( GetDoc()->GetDefaultTabulator() ); // new + + Fraction aUIScale(pWin->GetMapMode().GetScaleX()); + aUIScale *= GetDoc()->GetUIScale(); + pRuler->SetZoom(aUIScale); + + return pRuler; +} + +/** + * Generate vertical ruler + */ + +VclPtr DrawViewShell::CreateVRuler(::sd::Window* pWin) +{ + VclPtr pRuler; + WinBits aWBits = WB_VSCROLL | WB_3DLOOK | WB_BORDER; + SvxRulerSupportFlags nFlags = SvxRulerSupportFlags::OBJECT; + + pRuler = VclPtr::Create(*this, GetParentWindow(), pWin, nFlags, + GetViewFrame()->GetBindings(), aWBits); + + // Metric same as HRuler, use document setting + sal_uInt16 nMetric = static_cast(GetDoc()->GetUIUnit()); + + if( nMetric == 0xffff ) + nMetric = static_cast(GetViewShellBase().GetViewFrame()->GetDispatcher()->GetModule()->GetFieldUnit()); + + pRuler->SetUnit( FieldUnit( nMetric ) ); + + Fraction aUIScale(pWin->GetMapMode().GetScaleY()); + aUIScale *= GetDoc()->GetUIScale(); + pRuler->SetZoom(aUIScale); + + return pRuler; +} + +/** + * Refresh horizontal ruler + */ + +void DrawViewShell::UpdateHRuler() +{ + Invalidate( SID_ATTR_LONG_LRSPACE ); + Invalidate( SID_RULER_PAGE_POS ); + Invalidate( SID_RULER_OBJECT ); + Invalidate( SID_RULER_TEXT_RIGHT_TO_LEFT ); + + if (mpHorizontalRuler) + mpHorizontalRuler->ForceUpdate(); +} + +/** + * Refresh vertical ruler + */ + +void DrawViewShell::UpdateVRuler() +{ + Invalidate( SID_ATTR_LONG_LRSPACE ); + Invalidate( SID_RULER_PAGE_POS ); + Invalidate( SID_RULER_OBJECT ); + + if (mpVerticalRuler) + mpVerticalRuler->ForceUpdate(); +} + +/** + * Refresh TabControl on splitter change + */ + +IMPL_LINK( DrawViewShell, TabSplitHdl, TabBar *, pTab, void ) +{ + const ::tools::Long nMax = maViewSize.Width() - maScrBarWH.Width() + - maTabControl->GetPosPixel().X() ; + + Size aTabSize = maTabControl->GetSizePixel(); + aTabSize.setWidth( std::min(pTab->GetSplitSize(), static_cast<::tools::Long>(nMax-1)) ); + + maTabControl->SetSizePixel(aTabSize); + + if(GetLayerTabControl()) // #i87182# + { + GetLayerTabControl()->SetSizePixel(aTabSize); + } + + Point aPos = maTabControl->GetPosPixel(); + aPos.AdjustX(aTabSize.Width() ); + + Size aScrSize(nMax - aTabSize.Width(), maScrBarWH.Height()); + mpHorizontalScrollBar->SetPosSizePixel(aPos, aScrSize); +} + +/// inherited from sd::ViewShell +SdPage* DrawViewShell::getCurrentPage() const +{ + const sal_uInt16 nPageCount = (meEditMode == EditMode::Page)? + GetDoc()->GetSdPageCount(mePageKind): + GetDoc()->GetMasterSdPageCount(mePageKind); + + sal_uInt16 nCurrentPage = maTabControl->GetCurPagePos(); + DBG_ASSERT((nCurrentPage= nPageCount) + nCurrentPage = 0; // play safe here + + if (meEditMode == EditMode::Page) + { + return GetDoc()->GetSdPage(nCurrentPage, mePageKind); + } + else // EditMode::MasterPage + { + return GetDoc()->GetMasterSdPage(nCurrentPage, mePageKind); + } +} + +/** + * Select new refreshed page, in case of a page order change (eg. by undo) + */ + +void DrawViewShell::ResetActualPage() +{ + if (!GetDoc()) + return; + + sal_uInt16 nCurrentPageId = maTabControl->GetCurPageId(); + sal_uInt16 nNewPageId = nCurrentPageId; + sal_uInt16 nCurrentPageNum = maTabControl->GetPagePos(nCurrentPageId); + sal_uInt16 nPageCount = (meEditMode == EditMode::Page)?GetDoc()->GetSdPageCount(mePageKind):GetDoc()->GetMasterSdPageCount(mePageKind); + + if (meEditMode == EditMode::Page) + { + + // Update for TabControl + maTabControl->Clear(); + + SdPage* pPage = nullptr; + + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + pPage = GetDoc()->GetSdPage(i, mePageKind); + OUString aPageName = pPage->GetName(); + maTabControl->InsertPage(pPage->getPageId(), aPageName); + + if (nCurrentPageId == pPage->getPageId()) + { + nCurrentPageNum = i; + GetDoc()->SetSelected(pPage, true); + } + else + GetDoc()->SetSelected(pPage, false); + } + + nNewPageId = maTabControl->GetPageId(nCurrentPageNum); + maTabControl->SetCurPageId(nNewPageId); + } + else // EditMode::MasterPage + { + maTabControl->Clear(); + + sal_uInt16 nMasterPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + for (sal_uInt16 i = 0; i < nMasterPageCnt; i++) + { + SdPage* pMaster = GetDoc()->GetMasterSdPage(i, mePageKind); + OUString aLayoutName = pMaster->GetLayoutName(); + sal_Int32 nPos = aLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayoutName = aLayoutName.copy(0, nPos); + maTabControl->InsertPage(pMaster->getPageId(), aLayoutName); + + if (pMaster->getPageId() == nCurrentPageId) + nCurrentPageNum = i; + } + + nNewPageId = maTabControl->GetPageId(nCurrentPageNum); + maTabControl->SetCurPageId(nNewPageId); + SwitchPage(nCurrentPageNum); + } + + bool bAllowChangeFocus = nNewPageId != nCurrentPageId; + SfxBoolItem aI(SID_SWITCHPAGE, bAllowChangeFocus); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_SWITCHPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aI }); +} + +/** + * Apply "Verb" on OLE-object. + */ +ErrCode DrawViewShell::DoVerb(sal_Int32 nVerb) +{ + if ( mpDrawView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nSdrObjKind = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default && nSdrObjKind == SdrObjKind::OLE2) + { + ActivateObject( static_cast(pObj), nVerb); + } + } + } + + return ERRCODE_NONE; +} + +/** + * Activate OLE-object + */ +bool DrawViewShell::ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb) +{ + bool bActivated = false; + + if ( !GetDocSh()->IsUIActive() ) + { + ToolBarManager::UpdateLock aLock (GetViewShellBase().GetToolBarManager()); + + bActivated = ViewShell::ActivateObject(pObj, nVerb); + } + + return bActivated; +} + +/** + * Mark the desired page as selected (1), deselected (0), toggle (2). + * nPage refers to the page in question. + */ +bool DrawViewShell::SelectPage(sal_uInt16 nPage, sal_uInt16 nSelect) +{ + SdPage* pPage = GetDoc()->GetSdPage(nPage, PageKind::Standard); + + //page selector marks pages to selected in view + auto &pageSelector = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase())->GetSlideSorter().GetController().GetPageSelector(); + + if (pPage) + { + if (nSelect == 0) + { + GetDoc()->SetSelected(pPage, false); // Deselect. + pageSelector.DeselectPage(nPage); + } + else if (nSelect == 1) + { + GetDoc()->SetSelected(pPage, true); // Select. + pageSelector.SelectPage(nPage); + } + else + { + // Toggle. + if (pPage->IsSelected()) + { + GetDoc()->SetSelected(pPage, false); + pageSelector.DeselectPage(nPage); + } + else + { + GetDoc()->SetSelected(pPage, true); + pageSelector.SelectPage(nPage); + } + } + return true; + } + + return false; +} + +bool DrawViewShell::IsSelected(sal_uInt16 nPage) +{ + slidesorter::SlideSorterViewShell* pVShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pVShell != nullptr) + return pVShell->GetSlideSorter().GetController().GetPageSelector().IsPageSelected(nPage); + + return false; +} + +bool DrawViewShell::IsVisible(sal_uInt16 nPage) +{ + slidesorter::SlideSorterViewShell* pVShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pVShell != nullptr) + return pVShell->GetSlideSorter().GetController().GetPageSelector().IsPageVisible(nPage); + + return false; +} + +/** + * Switch to desired page. + * nSelectPage refers to the current EditMode + * bAllowChangeFocus set to false when slide is inserted before current page + * and we need to only update the current page number, + * do not disturb editing in that case + */ +bool DrawViewShell::SwitchPage(sal_uInt16 nSelectedPage, bool bAllowChangeFocus) +{ + /** Under some circumstances there are nested calls to SwitchPage() and + may crash the application (activation of form controls when the + shell of the edit view is not on top of the shell stack, see issue + 83888 for details.) Therefore the nested calls are ignored (they + would jump to the wrong page anyway.) + */ + + if (mbIsInSwitchPage) + return false; + mbIsInSwitchPage = true; + comphelper::ScopeGuard aGuard( + [this] () { this->mbIsInSwitchPage = false; } ); + + if (GetActiveWindow()->IsInPaint()) + { + // Switching the current page while a Paint is being executed is + // dangerous. So, post it for later execution and return. + maAsynchronousSwitchPageCall.Post( + [this, nSelectedPage] () { this->SwitchPage(nSelectedPage); } ); + return false; + } + + bool bOK = false; + + // With the current implementation of FuSlideShow there is a problem + // when it displays the show in a window: when the show is stopped it + // returns at one point in time SDRPAGE_NOTFOUND as current page index. + // Because FuSlideShow is currently being rewritten this bug is fixed + // here. + // This is not as bad a hack as it may look because making SwitchPage() + // more robust with respect to invalid page numbers is a good thing + // anyway. + if (nSelectedPage == SDRPAGE_NOTFOUND) + { + nSelectedPage = 0; + } + else + { + // Make sure that the given page index points to an existing page. Move + // the index into the valid range if necessary. + sal_uInt16 nPageCount = (meEditMode == EditMode::Page) + ? GetDoc()->GetSdPageCount(mePageKind) + : GetDoc()->GetMasterSdPageCount(mePageKind); + if (nSelectedPage >= nPageCount) + nSelectedPage = nPageCount-1; + } + + if (IsSwitchPageAllowed()) + { + ModifyGuard aGuard2( GetDoc() ); + + bOK = true; + + if (mpActualPage) + { + SdPage* pNewPage = nullptr; + + if (meEditMode == EditMode::MasterPage) + { + if( GetDoc()->GetMasterSdPageCount(mePageKind) > nSelectedPage ) + pNewPage = GetDoc()->GetMasterSdPage(nSelectedPage, mePageKind); + + if( pNewPage ) + { + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + OUString sPageText(pNewPage->GetLayoutName()); + sal_Int32 nPos = sPageText.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + sPageText = sPageText.copy(0, nPos); + if (pPV + && pNewPage == dynamic_cast< SdPage* >( pPV->GetPage() ) + && sPageText == maTabControl->GetPageText(maTabControl->GetPageId(nSelectedPage))) + { + // this slide is already visible + return true; + } + } + } + else + { + OSL_ASSERT(mpFrameView!=nullptr); + mpFrameView->SetSelectedPage(nSelectedPage); + + if (GetDoc()->GetSdPageCount(mePageKind) > nSelectedPage) + pNewPage = GetDoc()->GetSdPage(nSelectedPage, mePageKind); + + if (mpActualPage == pNewPage) + { + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + + SdPage* pCurrentPage = pPV ? dynamic_cast(pPV->GetPage()) : nullptr; + if (pCurrentPage + && pNewPage == pCurrentPage + && maTabControl->GetPageText(maTabControl->GetPageId(nSelectedPage)) == pNewPage->GetName()) + { + // this slide is already visible + return true; + } + } + } + } + + if (bAllowChangeFocus) + mpDrawView->SdrEndTextEdit(); + + mpActualPage = nullptr; + + if (meEditMode == EditMode::Page) + { + mpActualPage = GetDoc()->GetSdPage(nSelectedPage, mePageKind); + } + else + { + SdPage* pMaster = GetDoc()->GetMasterSdPage(nSelectedPage, mePageKind); + + // does the selected page fit to the masterpage? + sal_uInt16 nPageCount = GetDoc()->GetSdPageCount(mePageKind); + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + if(pPage && pPage->IsSelected() && pMaster == &(pPage->TRG_GetMasterPage())) + { + mpActualPage = pPage; + break; + } + } + + if (!mpActualPage) + { + // take the first page, that fits to the masterpage + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + if(pPage && pMaster == &(pPage->TRG_GetMasterPage())) + { + mpActualPage = pPage; + break; + } + } + } + } + + for (sal_uInt16 i = 0; i < GetDoc()->GetSdPageCount(mePageKind); i++) + { + // deselect all pages + GetDoc()->SetSelected( GetDoc()->GetSdPage(i, mePageKind), false); + } + + if (!mpActualPage) + { + // as far as there is no mpActualPage, take the first + mpActualPage = GetDoc()->GetSdPage(0, mePageKind); + } + + // also select this page (mpActualPage always points at a drawing page, + // never at a masterpage) + GetDoc()->SetSelected(mpActualPage, true); + + if (comphelper::LibreOfficeKit::isActive()) + { + // notify LibreOfficeKit about changed page + OString aPayload = OString::number(nSelectedPage); + if (SfxViewShell* pViewShell = GetViewShell()) + pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr()); + } + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetDoc() ) ); + if( !xSlideshow.is() || !xSlideshow->isRunning() || ( xSlideshow->getAnimationMode() != ANIMATIONMODE_SHOW ) ) + { + // tighten VisArea, to possibly deactivate objects + // !!! only if we are not in presentation mode (#96279) !!! + OSL_ASSERT (GetViewShell()!=nullptr); + GetViewShell()->DisconnectAllClients(); + VisAreaChanged(::tools::Rectangle(Point(), Size(1, 1))); + } + + // Try to prefetch all graphics for the active page. This will be done + // in threads to be more efficient than loading them on-demand one by one. + std::vector graphics; + mpActualPage->getGraphicsForPrefetch(graphics); + if(graphics.size() > 1) // threading does not help with loading just one + GraphicFilter::GetGraphicFilter().MakeGraphicsAvailableThreaded(graphics); + + if (meEditMode == EditMode::Page) + { + /********************************************************************** + * PAGEMODE + **********************************************************************/ + GetDoc()->SetSelected(mpActualPage, true); + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if (pPageView) + { + mpFrameView->SetVisibleLayers( pPageView->GetVisibleLayers() ); + mpFrameView->SetPrintableLayers( pPageView->GetPrintableLayers() ); + mpFrameView->SetLockedLayers( pPageView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + mpFrameView->SetNotesHelpLines( pPageView->GetHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + mpFrameView->SetHandoutHelpLines( pPageView->GetHelpLines() ); + } + else + { + mpFrameView->SetStandardHelpLines( pPageView->GetHelpLines() ); + } + } + + mpDrawView->HideSdrPage(); + maTabControl->SetCurPageId(maTabControl->GetPageId(nSelectedPage)); + mpDrawView->ShowSdrPage(mpActualPage); + GetViewShellBase().GetDrawController().FireSwitchCurrentPage(mpActualPage); + + SdrPageView* pNewPageView = mpDrawView->GetSdrPageView(); + + if (pNewPageView) + { + pNewPageView->SetVisibleLayers( mpFrameView->GetVisibleLayers() ); + pNewPageView->SetPrintableLayers( mpFrameView->GetPrintableLayers() ); + pNewPageView->SetLockedLayers( mpFrameView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + pNewPageView->SetHelpLines( mpFrameView->GetNotesHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + pNewPageView->SetHelpLines( mpFrameView->GetHandoutHelpLines() ); + } + else + { + pNewPageView->SetHelpLines( mpFrameView->GetStandardHelpLines() ); + } + } + + OUString aPageName = mpActualPage->GetName(); + + if (maTabControl->GetPageText(maTabControl->GetPageId(nSelectedPage)) != aPageName) + { + maTabControl->SetPageText(maTabControl->GetPageId(nSelectedPage), aPageName); + } + } + else + { + /********************************************************************** + * MASTERPAGE + **********************************************************************/ + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if (pPageView) + { + mpFrameView->SetVisibleLayers( pPageView->GetVisibleLayers() ); + mpFrameView->SetPrintableLayers( pPageView->GetPrintableLayers() ); + mpFrameView->SetLockedLayers( pPageView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + mpFrameView->SetNotesHelpLines( pPageView->GetHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + mpFrameView->SetHandoutHelpLines( pPageView->GetHelpLines() ); + } + else + { + mpFrameView->SetStandardHelpLines( pPageView->GetHelpLines() ); + } + } + + mpDrawView->HideSdrPage(); + maTabControl->SetCurPageId(maTabControl->GetPageId(nSelectedPage)); + + SdPage* pMaster = GetDoc()->GetMasterSdPage(nSelectedPage, mePageKind); + + if( !pMaster ) // if this page should not exist + pMaster = GetDoc()->GetMasterSdPage(0, mePageKind); + + sal_uInt16 nNum = pMaster->GetPageNum(); + mpDrawView->ShowSdrPage(mpDrawView->GetModel()->GetMasterPage(nNum)); + + GetViewShellBase().GetDrawController().FireSwitchCurrentPage(pMaster); + + SdrPageView* pNewPageView = mpDrawView->GetSdrPageView(); + + if (pNewPageView) + { + pNewPageView->SetVisibleLayers( mpFrameView->GetVisibleLayers() ); + pNewPageView->SetPrintableLayers( mpFrameView->GetPrintableLayers() ); + pNewPageView->SetLockedLayers( mpFrameView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + pNewPageView->SetHelpLines( mpFrameView->GetNotesHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + pNewPageView->SetHelpLines( mpFrameView->GetHandoutHelpLines() ); + } + else + { + pNewPageView->SetHelpLines( mpFrameView->GetStandardHelpLines() ); + } + } + + OUString aLayoutName(pMaster->GetLayoutName()); + sal_Int32 nPos = aLayoutName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayoutName = aLayoutName.copy(0, nPos); + + if (maTabControl->GetPageText(maTabControl->GetPageId(nSelectedPage)) != aLayoutName) + { + maTabControl->SetPageText(maTabControl->GetPageId(nSelectedPage), aLayoutName); + } + + if( mePageKind == PageKind::Handout ) + { + // set pages for all available handout presentation objects + sd::ShapeList& rShapeList = pMaster->GetPresentationShapeList(); + SdrObject* pObj = nullptr; + rShapeList.seekShape(0); + + while( (pObj = rShapeList.getNextShape()) ) + { + if( pMaster->GetPresObjKind(pObj) == PresObjKind::Handout ) + { + // #i105146# We want no content to be displayed for PageKind::Handout, + // so just never set a page as content + static_cast(pObj)->SetReferencedPage(nullptr); + } + } + } + } + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + mpDrawView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + + // so navigator (and effect window) notice that + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_NAVIGATOR_STATE, true); + rBindings.Invalidate(SID_NAVIGATOR_PAGENAME, true); + rBindings.Invalidate(SID_STATUS_PAGE, true); + rBindings.Invalidate(SID_DELETE_MASTER_PAGE, true); + rBindings.Invalidate(SID_DELETE_PAGE, true); + rBindings.Invalidate(SID_ASSIGN_LAYOUT, true); + rBindings.Invalidate(SID_INSERTPAGE, true); + UpdatePreview( mpActualPage ); + + mpDrawView->AdjustMarkHdl(); + } + + return bOK; +} + +/** + * Check if page change is allowed + */ + +bool DrawViewShell::IsSwitchPageAllowed() const +{ + bool bOK = true; + + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr && !pFormShell->PrepareClose(false)) + bOK = false; + + return bOK; +} + +/** + * Select new refreshed page, in case of a page order change (eg. by undo) + */ + +void DrawViewShell::ResetActualLayer() +{ + LayerTabBar* pLayerBar = GetLayerTabControl(); + if (pLayerBar == nullptr) + return; + + // remember old tab count and current tab id + // this is needed when one layer is renamed to + // restore current tab + sal_uInt16 nOldLayerCnt = pLayerBar->GetPageCount(); // actually it is tab count + sal_uInt16 nOldLayerPos = pLayerBar->GetCurPageId(); // actually it is a tab nId + + /** + * Update for LayerTab + */ + pLayerBar->Clear(); + + OUString aName; // a real layer name + OUString aActiveLayer = mpDrawView->GetActiveLayer(); + sal_uInt16 nActiveLayerPos = SDRLAYERPOS_NOTFOUND; + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + sal_uInt16 nLayerCnt = rLayerAdmin.GetLayerCount(); + + for ( sal_uInt16 nLayerPos = 0; nLayerPos < nLayerCnt; nLayerPos++ ) + { + aName = rLayerAdmin.GetLayer(nLayerPos)->GetName(); + + if ( aName == aActiveLayer ) + { + nActiveLayerPos = nLayerPos; + } + + if ( aName != sUNO_LayerName_background ) // layer "background" has never a tab + { + if (meEditMode == EditMode::MasterPage) + { + // don't show page layer onto the masterpage + if (aName != sUNO_LayerName_layout && + aName != sUNO_LayerName_controls && + aName != sUNO_LayerName_measurelines) + { + TabBarPageBits nBits = TabBarPageBits::NONE; + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + if (pPV) + { + if (!pPV->IsLayerVisible(aName)) + { + nBits |= TabBarPageBits::Blue; + } + if (pPV->IsLayerLocked(aName)) + { + nBits |= TabBarPageBits::Italic; + } + if (!pPV->IsLayerPrintable(aName)) + { + nBits |= TabBarPageBits::Underline; + } + } + + pLayerBar->InsertPage(nLayerPos+1, aName, nBits); // why +1? It is a nId, not a position. Position is APPEND. + } + } + else + { + // don't show masterpage layer onto the page + if (aName != sUNO_LayerName_background_objects) + { + TabBarPageBits nBits = TabBarPageBits::NONE; + if (!mpDrawView->GetSdrPageView()->IsLayerVisible(aName)) + { + nBits = TabBarPageBits::Blue; + } + if (mpDrawView->GetSdrPageView()->IsLayerLocked(aName)) + { + nBits |= TabBarPageBits::Italic; + } + if (!mpDrawView->GetSdrPageView()->IsLayerPrintable(aName)) + { + nBits |= TabBarPageBits::Underline; + } + + pLayerBar->InsertPage(nLayerPos+1, aName, nBits);// why +1? + } + } + } + } + + if ( nActiveLayerPos == SDRLAYERPOS_NOTFOUND ) + { + if( nOldLayerCnt == pLayerBar->GetPageCount() ) + { + nActiveLayerPos = nOldLayerPos - 1; + } + else + { + nActiveLayerPos = ( meEditMode == EditMode::MasterPage ) ? 2 : 0; + } + + mpDrawView->SetActiveLayer( pLayerBar->GetLayerName(nActiveLayerPos + 1) );// why +1? + } + + pLayerBar->SetCurPageId(nActiveLayerPos + 1); + GetViewFrame()->GetBindings().Invalidate( SID_MODIFYLAYER ); + GetViewFrame()->GetBindings().Invalidate( SID_DELETE_LAYER ); +} + +/** + * AcceptDrop + */ + +sal_Int8 DrawViewShell::AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* /*pTargetWindow*/, + sal_uInt16 /*nPage*/, + SdrLayerID nLayer ) +{ + if( SlideShow::IsRunning( GetViewShellBase() ) ) + return DND_ACTION_NONE; + + return mpDrawView->AcceptDrop( rEvt, rTargetHelper, nLayer ); +} + +/** + * ExecuteDrop + */ + +sal_Int8 DrawViewShell::ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& /*rTargetHelper*/, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + if( nPage != SDRPAGE_NOTFOUND ) + nPage = GetDoc()->GetSdPage( nPage, mePageKind )->GetPageNum(); + + if( SlideShow::IsRunning( GetViewShellBase() ) ) + return DND_ACTION_NONE; + + Broadcast(ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START)); + sal_Int8 nResult (mpDrawView->ExecuteDrop( rEvt, pTargetWindow, nPage, nLayer )); + Broadcast(ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END)); + + return nResult; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews2.cxx b/sd/source/ui/view/drviews2.cxx new file mode 100644 index 000000000..8be942743 --- /dev/null +++ b/sd/source/ui/view/drviews2.cxx @@ -0,0 +1,4004 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; + +#define MIN_ACTIONS_FOR_DIALOG 5000 ///< if there are more meta objects, we show a dialog during the break up + +namespace sd { + +namespace { + +const SvxFieldItem* findField(editeng::Section const & rSection) +{ + for (SfxPoolItem const * pPool: rSection.maAttributes) + { + if (pPool->Which() == EE_FEATURE_FIELD) + return static_cast(pPool); + } + return nullptr; +} + +bool hasCustomPropertyField(std::vector const & aSections, std::u16string_view rName) +{ + for (editeng::Section const & rSection : aSections) + { + const SvxFieldItem* pFieldItem = findField(rSection); + if (pFieldItem) + { + const editeng::CustomPropertyField* pCustomPropertyField = dynamic_cast(pFieldItem->GetField()); + if (pCustomPropertyField && pCustomPropertyField->GetName() == rName) + return true; + } + } + return false; +} + +OUString getWeightString(SfxItemSet const & rItemSet) +{ + OUString sWeightString = "NORMAL"; + + if (const SfxPoolItem* pItem = rItemSet.GetItem(EE_CHAR_WEIGHT, false)) + { + const SvxWeightItem* pWeightItem = dynamic_cast(pItem); + if (pWeightItem && pWeightItem->GetWeight() == WEIGHT_BOLD) + sWeightString = "BOLD"; + } + return sWeightString; +} + +class ClassificationCommon +{ +protected: + sd::DrawViewShell& m_rDrawViewShell; + uno::Reference m_xDocumentProperties; + uno::Reference m_xPropertyContainer; + sfx::ClassificationKeyCreator m_aKeyCreator; +public: + ClassificationCommon(sd::DrawViewShell& rDrawViewShell, const css::uno::Reference& rDocProps) + : m_rDrawViewShell(rDrawViewShell) + , m_xDocumentProperties(rDocProps) + , m_xPropertyContainer(m_xDocumentProperties->getUserDefinedProperties()) + , m_aKeyCreator(SfxClassificationHelper::getPolicyType()) + {} +}; + +class ClassificationCollector : public ClassificationCommon +{ +private: + std::vector m_aResults; + + void iterateSectionsAndCollect(std::vector const & rSections, EditTextObject const & rEditText) + { + sal_Int32 nCurrentParagraph = -1; + OUString sBlank; + + for (editeng::Section const & rSection : rSections) + { + // Insert new paragraph if needed + while (nCurrentParagraph < rSection.mnParagraph) + { + nCurrentParagraph++; + // Get Weight of current paragraph + OUString sWeightProperty = getWeightString(rEditText.GetParaAttribs(nCurrentParagraph)); + // Insert new paragraph into collection + m_aResults.push_back({ svx::ClassificationType::PARAGRAPH, sWeightProperty, sBlank, sBlank }); + } + + const SvxFieldItem* pFieldItem = findField(rSection); + const editeng::CustomPropertyField* pCustomPropertyField = pFieldItem ? + dynamic_cast(pFieldItem->GetField()) : + nullptr; + if (pCustomPropertyField) + { + const OUString& aKey = pCustomPropertyField->GetName(); + if (m_aKeyCreator.isMarkingTextKey(aKey)) + { + OUString aValue = svx::classification::getProperty(m_xPropertyContainer, aKey); + m_aResults.push_back({ svx::ClassificationType::TEXT, aValue, sBlank, sBlank }); + } + else if (m_aKeyCreator.isCategoryNameKey(aKey) || m_aKeyCreator.isCategoryIdentifierKey(aKey)) + { + OUString aValue = svx::classification::getProperty(m_xPropertyContainer, aKey); + m_aResults.push_back({ svx::ClassificationType::CATEGORY, aValue, sBlank, sBlank }); + } + else if (m_aKeyCreator.isMarkingKey(aKey)) + { + OUString aValue = svx::classification::getProperty(m_xPropertyContainer, aKey); + m_aResults.push_back({ svx::ClassificationType::MARKING, aValue, sBlank, sBlank }); + } + else if (m_aKeyCreator.isIntellectualPropertyPartKey(aKey)) + { + OUString aValue = svx::classification::getProperty(m_xPropertyContainer, aKey); + m_aResults.push_back({ svx::ClassificationType::INTELLECTUAL_PROPERTY_PART, aValue, sBlank, sBlank }); + } + } + } + } + +public: + ClassificationCollector(sd::DrawViewShell & rDrawViewShell, const css::uno::Reference& rDocProps) + : ClassificationCommon(rDrawViewShell, rDocProps) + {} + + std::vector const & getResults() const + { + return m_aResults; + } + + void collect() + { + // Set to MASTER mode + EditMode eOldMode = m_rDrawViewShell.GetEditMode(); + if (eOldMode != EditMode::MasterPage) + m_rDrawViewShell.ChangeEditMode(EditMode::MasterPage, false); + + // Scoped guard to revert to the previous mode + comphelper::ScopeGuard const aGuard([this, eOldMode] () { + m_rDrawViewShell.ChangeEditMode(eOldMode, false); + }); + + const sal_uInt16 nCount = m_rDrawViewShell.GetDoc()->GetMasterSdPageCount(PageKind::Standard); + + for (sal_uInt16 nPageIndex = 0; nPageIndex < nCount; ++nPageIndex) + { + SdPage* pMasterPage = m_rDrawViewShell.GetDoc()->GetMasterSdPage(nPageIndex, PageKind::Standard); + for (size_t nObject = 0; nObject < pMasterPage->GetObjCount(); ++nObject) + { + SdrObject* pObject = pMasterPage->GetObj(nObject); + SdrRectObj* pRectObject = dynamic_cast(pObject); + if (pRectObject && pRectObject->GetTextKind() == SdrObjKind::Text) + { + OutlinerParaObject* pOutlinerParagraphObject = pRectObject->GetOutlinerParaObject(); + if (pOutlinerParagraphObject) + { + const EditTextObject& rEditText = pOutlinerParagraphObject->GetTextObject(); + std::vector aSections; + rEditText.GetAllSections(aSections); + + // Search for a custom property field that has the classification category identifier key + if (hasCustomPropertyField(aSections, m_aKeyCreator.makeCategoryNameKey())) + { + iterateSectionsAndCollect(aSections, rEditText); + return; + } + } + } + } + } + } +}; + +class ClassificationInserter : public ClassificationCommon +{ +private: + /// Delete the previous existing classification object(s) - if they exist + void deleteExistingObjects() + { + OUString sKey = m_aKeyCreator.makeCategoryNameKey(); + + const sal_uInt16 nCount = m_rDrawViewShell.GetDoc()->GetMasterSdPageCount(PageKind::Standard); + + for (sal_uInt16 nPageIndex = 0; nPageIndex < nCount; ++nPageIndex) + { + SdPage* pMasterPage = m_rDrawViewShell.GetDoc()->GetMasterSdPage(nPageIndex, PageKind::Standard); + for (size_t nObject = 0; nObject < pMasterPage->GetObjCount(); ++nObject) + { + SdrObject* pObject = pMasterPage->GetObj(nObject); + SdrRectObj* pRectObject = dynamic_cast(pObject); + if (pRectObject && pRectObject->GetTextKind() == SdrObjKind::Text) + { + OutlinerParaObject* pOutlinerParagraphObject = pRectObject->GetOutlinerParaObject(); + if (pOutlinerParagraphObject) + { + const EditTextObject& rEditText = pOutlinerParagraphObject->GetTextObject(); + std::vector aSections; + rEditText.GetAllSections(aSections); + + if (hasCustomPropertyField(aSections, sKey)) + { + pMasterPage->RemoveObject(pRectObject->GetOrdNum()); + } + } + } + } + } + } + + void fillTheOutliner(Outliner* pOutliner, std::vector const & rResults) + { + sal_Int32 nParagraph = -1; + for (svx::ClassificationResult const & rResult : rResults) + { + + ESelection aPosition(nParagraph, EE_TEXTPOS_MAX_COUNT, nParagraph, EE_TEXTPOS_MAX_COUNT); + + switch (rResult.meType) + { + case svx::ClassificationType::TEXT: + { + OUString sKey = m_aKeyCreator.makeNumberedTextKey(); + svx::classification::addOrInsertDocumentProperty(m_xPropertyContainer, sKey, rResult.msName); + pOutliner->QuickInsertField(SvxFieldItem(editeng::CustomPropertyField(sKey, rResult.msName), EE_FEATURE_FIELD), aPosition); + } + break; + + case svx::ClassificationType::CATEGORY: + { + OUString sKey = m_aKeyCreator.makeCategoryNameKey(); + pOutliner->QuickInsertField(SvxFieldItem(editeng::CustomPropertyField(sKey, rResult.msName), EE_FEATURE_FIELD), aPosition); + } + break; + + case svx::ClassificationType::MARKING: + { + OUString sKey = m_aKeyCreator.makeNumberedMarkingKey(); + svx::classification::addOrInsertDocumentProperty(m_xPropertyContainer, sKey, rResult.msName); + pOutliner->QuickInsertField(SvxFieldItem(editeng::CustomPropertyField(sKey, rResult.msName), EE_FEATURE_FIELD), aPosition); + } + break; + + case svx::ClassificationType::INTELLECTUAL_PROPERTY_PART: + { + OUString sKey = m_aKeyCreator.makeNumberedIntellectualPropertyPartKey(); + svx::classification::addOrInsertDocumentProperty(m_xPropertyContainer, sKey, rResult.msName); + pOutliner->QuickInsertField(SvxFieldItem(editeng::CustomPropertyField(sKey, rResult.msName), EE_FEATURE_FIELD), aPosition); + } + break; + + case svx::ClassificationType::PARAGRAPH: + { + nParagraph++; + pOutliner->Insert(""); + + SfxItemSetFixed aItemSet(m_rDrawViewShell.GetDoc()->GetPool()); + + if (rResult.msName == "BOLD") + aItemSet.Put(SvxWeightItem(WEIGHT_BOLD, EE_CHAR_WEIGHT)); + else + aItemSet.Put(SvxWeightItem(WEIGHT_NORMAL, EE_CHAR_WEIGHT)); + + SvxNumRule aDefaultNumRule(SvxNumRuleFlags::NONE, 0, false); + aItemSet.Put(SvxNumBulletItem(std::move(aDefaultNumRule), EE_PARA_NUMBULLET)); + + pOutliner->SetParaAttribs(nParagraph, aItemSet); + } + break; + + default: + break; + } + } + } + +public: + ClassificationInserter(sd::DrawViewShell & rDrawViewShell, const css::uno::Reference& rDocProps) + : ClassificationCommon(rDrawViewShell, rDocProps) + { + } + + void insert(std::vector const & rResults) + { + // Set to MASTER mode + EditMode eOldMode = m_rDrawViewShell.GetEditMode(); + if (eOldMode != EditMode::MasterPage) + m_rDrawViewShell.ChangeEditMode(EditMode::MasterPage, false); + + // Scoped guard to revert the mode + comphelper::ScopeGuard const aGuard([this, eOldMode] () { + m_rDrawViewShell.ChangeEditMode(eOldMode, false); + }); + + // Delete the previous existing object - if exists + deleteExistingObjects(); + + // Clear properties + svx::classification::removeAllProperties(m_xPropertyContainer); + + SfxClassificationHelper aHelper(m_xDocumentProperties); + + // Apply properties from the BA policy + for (svx::ClassificationResult const & rResult : rResults) + { + if (rResult.meType == svx::ClassificationType::CATEGORY) + aHelper.SetBACName(rResult.msName, SfxClassificationHelper::getPolicyType()); + } + + // Insert full text as document property + svx::classification::insertFullTextualRepresentationAsDocumentProperty(m_xPropertyContainer, m_aKeyCreator, rResults); + + // Create the outliner from the + Outliner* pOutliner = m_rDrawViewShell.GetDoc()->GetInternalOutliner(); + OutlinerMode eOutlinerMode = pOutliner->GetOutlinerMode(); + + comphelper::ScopeGuard const aOutlinerGuard([pOutliner, eOutlinerMode] () { + pOutliner->Init(eOutlinerMode); + }); + + pOutliner->Init(OutlinerMode::TextObject); + + // Fill the outliner with the text from classification result + fillTheOutliner(pOutliner, rResults); + + // Calculate to outliner text size + pOutliner->UpdateFields(); + pOutliner->SetUpdateLayout(true); + Size aTextSize(pOutliner->CalcTextSize()); + pOutliner->SetUpdateLayout(false); + + // Create objects, apply the outliner and add them (objects) to all master pages + const sal_uInt16 nCount = m_rDrawViewShell.GetDoc()->GetMasterSdPageCount(PageKind::Standard); + + for (sal_uInt16 nPageIndex = 0; nPageIndex < nCount; ++nPageIndex) + { + SdPage* pMasterPage = m_rDrawViewShell.GetDoc()->GetMasterSdPage(nPageIndex, PageKind::Standard); + if (!pMasterPage) + continue; + + SdrRectObj* pObject = new SdrRectObj( + *m_rDrawViewShell.GetDoc(), // TTTT should be reference + SdrObjKind::Text); + pObject->SetMergedItem(makeSdrTextAutoGrowWidthItem(true)); + pObject->SetOutlinerParaObject(pOutliner->CreateParaObject()); + pMasterPage->InsertObject(pObject); + + // Calculate position + ::tools::Rectangle aRectangle(Point(), pMasterPage->GetSize()); + Point aPosition(aRectangle.Center().X(), aRectangle.Bottom()); + + aPosition.AdjustX( -(aTextSize.Width() / 2) ); + aPosition.AdjustY( -(aTextSize.Height()) ); + + pObject->SetLogicRect(::tools::Rectangle(aPosition, aTextSize)); + } + } +}; + + void lcl_convertStringArguments(sal_uInt16 nSlot, const std::unique_ptr& pArgs) + { + Color aColor; + const SfxPoolItem* pItem = nullptr; + + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_LINE_WIDTH_ARG, false, &pItem)) + { + double fValue = static_cast(pItem)->GetValue(); + // FIXME: different units... + int nPow = 100; + int nValue = fValue * nPow; + + XLineWidthItem aItem(nValue); + pArgs->Put(aItem); + } + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pItem)) + { + OUString sColor = static_cast(pItem)->GetValue(); + + if (sColor == "transparent") + aColor = COL_TRANSPARENT; + else + aColor = Color(ColorTransparency, sColor.toInt32(16)); + + switch (nSlot) + { + case SID_ATTR_LINE_COLOR: + { + XLineColorItem aLineColorItem(OUString(), aColor); + pArgs->Put(aLineColorItem); + break; + } + + case SID_ATTR_FILL_COLOR: + { + XFillColorItem aFillColorItem(OUString(), aColor); + pArgs->Put(aFillColorItem); + break; + } + } + } + if (SfxItemState::SET == pArgs->GetItemState(SID_FILL_GRADIENT_JSON, false, &pItem)) + { + const SfxStringItem* pJSON = static_cast(pItem); + if (pJSON) + { + XGradient aGradient = XGradient::fromJSON(pJSON->GetValue()); + XFillGradientItem aItem(aGradient); + pArgs->Put(aItem); + } + } + + if (nSlot == SID_ATTR_FILL_COLOR) + { + // Merge the color parameters to the color itself. + const XFillColorItem* pColorItem = static_cast(pArgs->GetItem(SID_ATTR_FILL_COLOR)); + if (pColorItem) + { + XFillColorItem aColorItem(*pColorItem); + if (pArgs->GetItemState(SID_ATTR_COLOR_THEME_INDEX, false, &pItem) == SfxItemState::SET) + { + auto pIntItem = static_cast(pItem); + aColorItem.GetThemeColor().SetThemeIndex(pIntItem->GetValue()); + } + if (pArgs->GetItemState(SID_ATTR_COLOR_LUM_MOD, false, &pItem) == SfxItemState::SET) + { + auto pIntItem = static_cast(pItem); + aColorItem.GetThemeColor().SetLumMod(pIntItem->GetValue()); + } + if (pArgs->GetItemState(SID_ATTR_COLOR_LUM_OFF, false, &pItem) == SfxItemState::SET) + { + auto pIntItem = static_cast(pItem); + aColorItem.GetThemeColor().SetLumOff(pIntItem->GetValue()); + } + pArgs->Put(aColorItem); + } + } + } +} + +/** + * SfxRequests for temporary actions + */ + +void DrawViewShell::FuTemporary(SfxRequest& rReq) +{ + // during a native slide show nothing gets executed! + if(SlideShow::IsRunning( GetViewShellBase() ) && (rReq.GetSlot() != SID_NAVIGATOR)) + return; + + DBG_ASSERT( mpDrawView, "sd::DrawViewShell::FuTemporary(), no draw view!" ); + if( !mpDrawView ) + return; + + CheckLineTo (rReq); + + DeactivateCurrentFunction(); + + sal_uInt16 nSId = rReq.GetSlot(); + + switch ( nSId ) + { + case SID_OUTLINE_TEXT_AUTOFIT: + { + SfxUndoManager* pUndoManager = GetDocSh()->GetUndoManager(); + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + pUndoManager->EnterListAction("", "", 0, GetViewShellBase().GetViewShellId()); + mpDrawView->BegUndo(); + + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + bool bSet = pObj->GetMergedItemSet().GetItem(SDRATTR_TEXT_FITTOSIZE)->GetValue() != drawing::TextFitToSizeType_NONE; + + mpDrawView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoAttrObject(*pObj)); + + if (!bSet) + { + //If we are turning on AutoFit we have to turn these off if already on + if (pObj->GetMergedItemSet().GetItem(SDRATTR_TEXT_AUTOGROWHEIGHT)->GetValue()) + pObj->SetMergedItem(makeSdrTextAutoGrowHeightItem(false)); + if (pObj->GetMergedItemSet().GetItem(SDRATTR_TEXT_AUTOGROWWIDTH)->GetValue()) + pObj->SetMergedItem(makeSdrTextAutoGrowWidthItem(false)); + } + + pObj->SetMergedItem(SdrTextFitToSizeTypeItem(bSet ? drawing::TextFitToSizeType_NONE : drawing::TextFitToSizeType_AUTOFIT)); + + mpDrawView->EndUndo(); + pUndoManager->LeaveListAction(); + } + Cancel(); + rReq.Done(); + } + break; + + // area and line attributes: shall have + // an own Execute method (like StateMethode) + case SID_ATTR_FILL_STYLE: + case SID_ATTR_FILL_COLOR: + case SID_ATTR_FILL_GRADIENT: + case SID_ATTR_FILL_HATCH: + case SID_ATTR_FILL_BITMAP: + case SID_ATTR_FILL_SHADOW: + case SID_ATTR_SHADOW_COLOR: + case SID_ATTR_SHADOW_TRANSPARENCE: + case SID_ATTR_SHADOW_BLUR: + case SID_ATTR_SHADOW_XDISTANCE: + case SID_ATTR_SHADOW_YDISTANCE: + case SID_ATTR_FILL_USE_SLIDE_BACKGROUND: + case SID_ATTR_FILL_TRANSPARENCE: + case SID_ATTR_FILL_FLOATTRANSPARENCE: + + case SID_ATTR_LINE_STYLE: + case SID_ATTR_LINE_DASH: + case SID_ATTR_LINE_WIDTH: + case SID_ATTR_LINE_COLOR: + case SID_ATTR_LINEEND_STYLE: + case SID_ATTR_LINE_START: + case SID_ATTR_LINE_END: + case SID_ATTR_LINE_TRANSPARENCE: + case SID_ATTR_LINE_JOINT: + case SID_ATTR_LINE_CAP: + + case SID_ATTR_TEXT_FITTOSIZE: + { + if( rReq.GetArgs() ) + { + std::unique_ptr pNewArgs = rReq.GetArgs()->Clone(); + lcl_convertStringArguments(rReq.GetSlot(), pNewArgs); + mpDrawView->SetAttributes(*pNewArgs); + rReq.Done(); + } + else + { + switch( rReq.GetSlot() ) + { + case SID_ATTR_FILL_SHADOW: + case SID_ATTR_SHADOW_COLOR: + case SID_ATTR_SHADOW_TRANSPARENCE: + case SID_ATTR_SHADOW_BLUR: + case SID_ATTR_SHADOW_XDISTANCE: + case SID_ATTR_SHADOW_YDISTANCE: + case SID_ATTR_FILL_STYLE: + case SID_ATTR_FILL_COLOR: + case SID_ATTR_FILL_GRADIENT: + case SID_ATTR_FILL_HATCH: + case SID_ATTR_FILL_BITMAP: + case SID_ATTR_FILL_USE_SLIDE_BACKGROUND: + case SID_ATTR_FILL_TRANSPARENCE: + case SID_ATTR_FILL_FLOATTRANSPARENCE: + GetViewFrame()->GetDispatcher()->Execute( SID_ATTRIBUTES_AREA, SfxCallMode::ASYNCHRON ); + break; + case SID_ATTR_LINE_STYLE: + case SID_ATTR_LINE_DASH: + case SID_ATTR_LINE_WIDTH: + case SID_ATTR_LINE_COLOR: + case SID_ATTR_LINE_TRANSPARENCE: + case SID_ATTR_LINE_JOINT: + case SID_ATTR_LINE_CAP: + GetViewFrame()->GetDispatcher()->Execute( SID_ATTRIBUTES_LINE, SfxCallMode::ASYNCHRON ); + break; + case SID_ATTR_TEXT_FITTOSIZE: + GetViewFrame()->GetDispatcher()->Execute( SID_TEXTATTR_DLG, SfxCallMode::ASYNCHRON ); + break; + } + } + Cancel(); + } + break; + + case SID_HYPHENATION: + { + const SfxBoolItem* pItem = rReq.GetArg(SID_HYPHENATION); + + if( pItem ) + { + SfxItemSetFixed aSet( GetPool() ); + bool bValue = pItem->GetValue(); + aSet.Put( SfxBoolItem( EE_PARA_HYPHENATE, bValue ) ); + mpDrawView->SetAttributes( aSet ); + } + else // only for testing purpose + { + OSL_FAIL(" no value for hyphenation!"); + SfxItemSetFixed aSet( GetPool() ); + aSet.Put( SfxBoolItem( EE_PARA_HYPHENATE, true ) ); + mpDrawView->SetAttributes( aSet ); + } + rReq.Done(); + Cancel(); + } + break; + + case SID_INSERTPAGE: + case SID_INSERTPAGE_QUICK: + { + SdPage* pNewPage = CreateOrDuplicatePage (rReq, mePageKind, GetActualPage()); + Cancel(); + if(HasCurrentFunction(SID_BEZIER_EDIT) ) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + if (pNewPage != nullptr) + SwitchPage((pNewPage->GetPageNum()-1)/2); + rReq.Done (); + } + break; + + case SID_DUPLICATE_PAGE: + { + auto slideSorter = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + SdPage* pNewPage = nullptr; + if(slideSorter) + DuplicateSelectedSlides(rReq); + else + pNewPage = CreateOrDuplicatePage (rReq, mePageKind, GetActualPage()); + Cancel(); + if(HasCurrentFunction(SID_BEZIER_EDIT) ) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + if(!slideSorter && pNewPage != nullptr) + SwitchPage((pNewPage->GetPageNum()-1)/2); + rReq.Done(); + } + break; + + case SID_INSERT_MASTER_PAGE: + { + // Use the API to create a new page. + Reference xMasterPagesSupplier ( + GetDoc()->getUnoModel(), UNO_QUERY); + if (xMasterPagesSupplier.is()) + { + Reference xMasterPages ( + xMasterPagesSupplier->getMasterPages()); + if (xMasterPages.is()) + { + sal_uInt16 nIndex = GetCurPagePos() + 1; + xMasterPages->insertNewByIndex (nIndex); + + // Create shapes for the default layout. + SdPage* pMasterPage = GetDoc()->GetMasterSdPage( + nIndex, PageKind::Standard); + pMasterPage->CreateTitleAndLayout (true,true); + } + } + + Cancel(); + if(HasCurrentFunction(SID_BEZIER_EDIT)) + GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + rReq.Done (); + } + break; + + case SID_MODIFYPAGE: + { + if (mePageKind==PageKind::Standard || mePageKind==PageKind::Notes || + (mePageKind==PageKind::Handout && meEditMode==EditMode::MasterPage) ) + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + sal_uInt16 nPage = maTabControl->GetCurPagePos(); + mpActualPage = GetDoc()->GetSdPage(nPage, mePageKind); + ::sd::ViewShell::mpImpl->ProcessModifyPageSlot ( + rReq, + mpActualPage, + mePageKind); + } + + Cancel(); + rReq.Done (); + } + break; + + case SID_ASSIGN_LAYOUT: + { + if (mePageKind==PageKind::Standard || mePageKind==PageKind::Notes || (mePageKind==PageKind::Handout && meEditMode==EditMode::MasterPage)) + { + if ( mpDrawView->IsTextEdit() ) + mpDrawView->SdrEndTextEdit(); + + ::sd::ViewShell::mpImpl->AssignLayout(rReq, mePageKind); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_RENAMEPAGE: + case SID_RENAME_MASTER_PAGE: + { + if (mePageKind==PageKind::Standard || mePageKind==PageKind::Notes ) + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + sal_uInt16 nPage = maTabControl->GetCurPagePos(); + SdPage* pCurrentPage = ( GetEditMode() == EditMode::Page ) + ? GetDoc()->GetSdPage( nPage, GetPageKind() ) + : GetDoc()->GetMasterSdPage( nPage, GetPageKind() ); + + OUString aTitle = SdResId(STR_TITLE_RENAMESLIDE); + OUString aDescr = SdResId(STR_DESC_RENAMESLIDE); + const OUString& aPageName = pCurrentPage->GetName(); + + if(rReq.GetArgs()) + { + OUString aName = rReq.GetArgs()->GetItem(SID_RENAMEPAGE)->GetValue(); + + bool bResult = RenameSlide( maTabControl->GetPageId(nPage), aName ); + DBG_ASSERT( bResult, "Couldn't rename slide" ); + } + else + { + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr aNameDlg(pFact->CreateSvxNameDialog(GetFrameWeld(), aPageName, aDescr)); + aNameDlg->SetText( aTitle ); + aNameDlg->SetCheckNameHdl( LINK( this, DrawViewShell, RenameSlideHdl ), true ); + aNameDlg->SetEditHelpId( HID_SD_NAMEDIALOG_PAGE ); + + if( aNameDlg->Execute() == RET_OK ) + { + OUString aNewName; + aNameDlg->GetName( aNewName ); + if (aNewName != aPageName) + { + bool bResult = RenameSlide( maTabControl->GetPageId(nPage), aNewName ); + DBG_ASSERT( bResult, "Couldn't rename slide" ); + } + } + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_RENAMEPAGE_QUICK: + { + if (mePageKind==PageKind::Standard || mePageKind==PageKind::Notes ) + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + maTabControl->StartEditMode( maTabControl->GetCurPageId() ); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_PAGESIZE : // either this (no menu entries or something else!) + { + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (pArgs && pArgs->Count () == 3) + { + const SfxUInt32Item* pWidth = rReq.GetArg(ID_VAL_PAGEWIDTH); + const SfxUInt32Item* pHeight = rReq.GetArg(ID_VAL_PAGEHEIGHT); + const SfxBoolItem* pScaleAll = rReq.GetArg(ID_VAL_SCALEOBJECTS); + + Size aSize (pWidth->GetValue (), pHeight->GetValue ()); + + SetupPage (aSize, 0, 0, 0, 0, true, false, pScaleAll->GetValue ()); + rReq.Ignore (); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rReq.Ignore (); + break; + } + + case SID_PAGEMARGIN : // or this (no menu entries or something else!) + { + const SfxItemSet *pArgs = rReq.GetArgs (); + + if (pArgs && pArgs->Count () == 5) + { + const SfxUInt32Item* pLeft = rReq.GetArg(ID_VAL_PAGELEFT); + const SfxUInt32Item* pRight = rReq.GetArg(ID_VAL_PAGERIGHT); + const SfxUInt32Item* pUpper = rReq.GetArg(ID_VAL_PAGETOP); + const SfxUInt32Item* pLower = rReq.GetArg(ID_VAL_PAGEBOTTOM); + const SfxBoolItem* pScaleAll = rReq.GetArg(ID_VAL_SCALEOBJECTS); + + Size aEmptySize (0, 0); + + SetupPage (aEmptySize, pLeft->GetValue (), pRight->GetValue (), + pUpper->GetValue (), pLower->GetValue (), + false, true, pScaleAll->GetValue ()); + rReq.Ignore (); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rReq.Ignore (); + break; + } + + case SID_ATTR_ZOOMSLIDER: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + + const SfxUInt16Item* pScale = (pArgs && pArgs->Count () == 1) ? + rReq.GetArg(SID_ATTR_ZOOMSLIDER) : nullptr; + if (pScale && CHECK_RANGE (5, pScale->GetValue (), 3000)) + { + SetZoom (pScale->GetValue ()); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_ATTR_ZOOM ); + rBindings.Invalidate( SID_ZOOM_IN ); + rBindings.Invalidate( SID_ZOOM_OUT ); + rBindings.Invalidate( SID_ATTR_ZOOMSLIDER ); + + } + + Cancel(); + rReq.Done (); + break; + } + + case SID_ATTR_ZOOM: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + mbZoomOnPage = false; + + if ( pArgs ) + { + SvxZoomType eZT = pArgs->Get( SID_ATTR_ZOOM ).GetType(); + switch( eZT ) + { + case SvxZoomType::PERCENT: + SetZoom( static_cast<::tools::Long>( pArgs->Get( SID_ATTR_ZOOM ).GetValue()) ); + break; + + case SvxZoomType::OPTIMAL: + GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_ALL, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + break; + + case SvxZoomType::PAGEWIDTH: + GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_PAGE_WIDTH, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + break; + + case SvxZoomType::WHOLEPAGE: + GetViewFrame()->GetDispatcher()->Execute( SID_SIZE_PAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + break; + case SvxZoomType::PAGEWIDTH_NOBORDER: + OSL_FAIL("sd::DrawViewShell::FuTemporary(), SvxZoomType::PAGEWIDTH_NOBORDER not handled!" ); + break; + } + rReq.Ignore (); + } + else + { + // open zoom dialog + SetCurrentFunction( FuScale::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + } + Cancel(); + } + break; + + case SID_CHANGEBEZIER: + case SID_CHANGEPOLYGON: + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + if( rReq.GetSlot() == SID_CHANGEBEZIER ) + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPathObj(false); + } + else + { + if( mpDrawView->IsVectorizeAllowed() ) + { + SetCurrentFunction( FuVectorize::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPolyObj(); + } + } + + Invalidate(SID_CHANGEBEZIER); + Invalidate(SID_CHANGEPOLYGON); + } + Cancel(); + + if( HasCurrentFunction(SID_BEZIER_EDIT) ) + { // where applicable, activate right edit action + GetViewFrame()->GetDispatcher()->Execute(SID_SWITCH_POINTEDIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + rReq.Ignore (); + break; + + case SID_CONVERT_TO_CONTOUR: + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPathObj(true); + + Invalidate(SID_CONVERT_TO_CONTOUR); + } + Cancel(); + + rReq.Ignore (); + break; + + case SID_CONVERT_TO_METAFILE: + case SID_CONVERT_TO_BITMAP: + { + // End text edit mode when it is active because the metafile or + // bitmap that will be created does not support it. + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + if ( mpDrawView->IsPresObjSelected(true,true,true) ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + + // create SdrGrafObj from metafile/bitmap + Graphic aGraphic; + switch (nSId) + { + case SID_CONVERT_TO_METAFILE: + { + // switch on undo for the next operations + mpDrawView->BegUndo(SdResId(STR_UNDO_CONVERT_TO_METAFILE)); + GDIMetaFile aMetaFile(mpDrawView->GetMarkedObjMetaFile()); + aGraphic = Graphic(aMetaFile); + } + break; + case SID_CONVERT_TO_BITMAP: + { + // Disable spelling during conversion + bool bOnlineSpell = GetDoc()->GetOnlineSpell(); + GetDoc()->SetOnlineSpell(false); + + // switch on undo for the next operations + mpDrawView->BegUndo(SdResId(STR_UNDO_CONVERT_TO_BITMAP)); + bool bDone(false); + + // I have to get the image here directly since GetMarkedObjBitmapEx works + // based on Bitmaps, but not on BitmapEx, thus throwing away the alpha + // channel. Argh! GetMarkedObjBitmapEx itself is too widely used to safely + // change that, e.g. in the exchange formats. For now I can only add this + // exception to get good results for Svgs. This is how the code gets more + // and more crowded, at last I made a remark for myself to change this + // as one of the next tasks. + if(1 == mpDrawView->GetMarkedObjectCount()) + { + const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(mpDrawView->GetMarkedObjectByIndex(0)); + + if(pSdrGrafObj && pSdrGrafObj->isEmbeddedVectorGraphicData()) + { + aGraphic = Graphic(pSdrGrafObj->GetGraphic().getVectorGraphicData()->getReplacement()); + bDone = true; + } + } + + if(!bDone) + { + aGraphic = Graphic(mpDrawView->GetMarkedObjBitmapEx()); + } + // Restore online spelling + GetDoc()->SetOnlineSpell(bOnlineSpell); + } + break; + } + + // create new object + SdrGrafObj* pGraphicObj = new SdrGrafObj( + *GetDoc(), + aGraphic); + + // get some necessary info and ensure it + const SdrMarkList& rMarkList(mpDrawView->GetMarkedObjectList()); + const size_t nMarkCount(rMarkList.GetMarkCount()); + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + OSL_ENSURE(nMarkCount, "DrawViewShell::FuTemporary: SID_CONVERT_TO_BITMAP with empty selection (!)"); + OSL_ENSURE(pPageView, "DrawViewShell::FuTemporary: SID_CONVERT_TO_BITMAP without SdrPageView (!)"); + + // fit rectangle of new graphic object to selection's mark rect + ::tools::Rectangle aAllMarkedRect; + rMarkList.TakeBoundRect(pPageView, aAllMarkedRect); + pGraphicObj->SetLogicRect(aAllMarkedRect); + + // #i71540# to keep the order, it is necessary to replace the lowest object + // of the selection with the new object. This also means that with multi + // selection, all other objects need to be deleted first + SdrMark* pFirstMark = rMarkList.GetMark(0); + SdrObject* pReplacementCandidate = pFirstMark->GetMarkedSdrObj(); + + if(nMarkCount > 1) + { + // take first object out of selection + mpDrawView->MarkObj(pReplacementCandidate, pPageView, true, true); + + // clear remaining selection + mpDrawView->DeleteMarkedObj(); + } + + // #i124816# copy layer from lowest object which gets replaced + pGraphicObj->SetLayer(pReplacementCandidate->GetLayer()); + + // now replace lowest object with new one + mpDrawView->ReplaceObjectAtView(pReplacementCandidate, *pPageView, pGraphicObj); + + // switch off undo + mpDrawView->EndUndo(); + } + } + + Cancel(); + + rReq.Done (); + break; + + case SID_REMOVE_HYPERLINK: + { + if (mpDrawView->IsTextEdit()) + { + // First make sure the field is selected + OutlinerView* pOutView = mpDrawView->GetTextEditOutlinerView(); + if (pOutView) + { + pOutView->SelectFieldAtCursor(); + URLFieldHelper::RemoveURLField(pOutView->GetEditView()); + } + } + } + Cancel(); + rReq.Done (); + break; + + case SID_SET_DEFAULT: + { + std::optional pSet; + + if (mpDrawView->IsTextEdit()) + { + pSet.emplace( GetPool(), svl::Items ); + mpDrawView->SetAttributes( *pSet, true ); + } + else + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + // For every presentation object a SfxItemSet of hard attributes + // and the UserCall is stored in this list. This is because + // at the following mpDrawView->SetAttributes( *pSet, sal_True ) + // they get lost and have to be restored. + std::vector,SdrObjUserCall*> > aAttrList; + SdPage* pPresPage = static_cast( mpDrawView->GetSdrPageView()->GetPage() ); + + for ( size_t i = 0; i < nCount; ++i ) + { + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + + if( pPresPage->IsPresObj( pObj ) ) + { + auto pNewSet = std::make_unique>( GetDoc()->GetPool() ); + pNewSet->Put(pObj->GetMergedItemSet()); + aAttrList.emplace_back(std::move(pNewSet), pObj->GetUserCall()); + } + } + + pSet.emplace( GetPool() ); + mpDrawView->SetAttributes( *pSet, true ); + + sal_uLong j = 0; + + for ( size_t i = 0; i < nCount; ++i ) + { + SfxStyleSheet* pSheet = nullptr; + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + + if (pObj->GetObjIdentifier() == SdrObjKind::TitleText) + { + pSheet = mpActualPage->GetStyleSheetForPresObj(PresObjKind::Title); + if (pSheet) + pObj->SetStyleSheet(pSheet, false); + } + else if(pObj->GetObjIdentifier() == SdrObjKind::OutlineText) + { + for (sal_uInt16 nLevel = 1; nLevel < 10; nLevel++) + { + pSheet = mpActualPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + DBG_ASSERT(pSheet, "Template for outline object not found"); + if (pSheet) + { + pObj->StartListening(*pSheet); + + if( nLevel == 1 ) + // text frame listens on StyleSheet of level1 + pObj->NbcSetStyleSheet(pSheet, false); + } + } + } + + if( pPresPage->IsPresObj( pObj ) ) + { + std::pair,SdrObjUserCall*> &rAttr = aAttrList[j++]; + + std::unique_ptr & pNewSet(rAttr.first); + SdrObjUserCall* pUserCall = rAttr.second; + + if ( pNewSet && pNewSet->GetItemState( SDRATTR_TEXT_MINFRAMEHEIGHT ) == SfxItemState::SET ) + { + pObj->SetMergedItem(pNewSet->Get(SDRATTR_TEXT_MINFRAMEHEIGHT)); + } + + if ( pNewSet && pNewSet->GetItemState( SDRATTR_TEXT_AUTOGROWHEIGHT ) == SfxItemState::SET ) + { + pObj->SetMergedItem(pNewSet->Get(SDRATTR_TEXT_AUTOGROWHEIGHT)); + } + + if( pUserCall ) + pObj->SetUserCall( pUserCall ); + } + } + } + + pSet.reset(); + Cancel(); + } + break; + + case SID_DELETE_SNAPITEM: + { + SdrPageView* pPV; + Point aMPos = GetActiveWindow()->PixelToLogic( maMousePos ); + sal_uInt16 nHitLog = static_cast(GetActiveWindow()->PixelToLogic( Size( + FuPoor::HITPIX, 0 ) ).Width()); + sal_uInt16 nHelpLine; + + if( mpDrawView->PickHelpLine( aMPos, nHitLog, *GetActiveWindow()->GetOutDev(), nHelpLine, pPV) ) + { + pPV->DeleteHelpLine( nHelpLine ); + } + Cancel(); + rReq.Ignore (); + } + break; + + case SID_DELETE_PAGE: + case SID_DELETE_MASTER_PAGE: + DeleteActualPage(); + Cancel(); + rReq.Ignore (); + break; + + case SID_DELETE_LAYER: + DeleteActualLayer(); + Cancel(); + rReq.Ignore (); + break; + + case SID_ORIGINAL_SIZE: + mpDrawView->SetMarkedOriginalSize(); + Cancel(); + rReq.Done(); + break; + + case SID_DRAW_FONTWORK: + case SID_DRAW_FONTWORK_VERTICAL: + { + svx::FontworkBar::execute(*mpView, rReq, GetViewFrame()->GetBindings()); // SJ: can be removed (I think) + Cancel(); + rReq.Done(); + } + break; + + case SID_SAVE_GRAPHIC: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + const SdrGrafObj* pObj = dynamic_cast(rMarkList.GetMark(0)->GetMarkedSdrObj()); + if (pObj && pObj->GetGraphicType() == GraphicType::Bitmap) + { + weld::Window* pFrame = GetFrameWeld(); + GraphicAttr aGraphicAttr = pObj->GetGraphicAttr(); + short nState = RET_CANCEL; + if (aGraphicAttr != GraphicAttr()) // the image has been modified + { + if (pFrame) + { + nState = GraphicHelper::HasToSaveTransformedImage(pFrame); + } + } + else + { + nState = RET_NO; + } + + if (nState == RET_YES) + { + GraphicHelper::ExportGraphic(pFrame, pObj->GetTransformedGraphic(), ""); + } + else if (nState == RET_NO) + { + const GraphicObject& aGraphicObject(pObj->GetGraphicObject()); + GraphicHelper::ExportGraphic(pFrame, aGraphicObject.GetGraphic(), ""); + } + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_EXTERNAL_EDIT: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + if( auto pGraphicObj = dynamic_cast( pObj ) ) + if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap ) + { + GraphicObject aGraphicObject( pGraphicObj->GetGraphicObject() ); + m_ExternalEdits.push_back( + std::make_unique( + mpDrawView.get(), pObj)); + m_ExternalEdits.back()->Edit( &aGraphicObject ); + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_COMPRESS_GRAPHIC: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if( rMarkList.GetMarkCount() == 1 ) + { + SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + if( auto pGraphicObj = dynamic_cast( pObj ) ) + if( pGraphicObj->GetGraphicType() == GraphicType::Bitmap ) + { + CompressGraphicsDialog dialog(GetFrameWeld(), pGraphicObj, GetViewFrame()->GetBindings() ); + if (dialog.run() == RET_OK) + { + SdrGrafObj* pNewObject = dialog.GetCompressedSdrGrafObj(); + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + OUString aUndoString = mpDrawView->GetDescriptionOfMarkedObjects() + " Compress"; + mpDrawView->BegUndo( aUndoString ); + mpDrawView->ReplaceObjectAtView( pObj, *pPageView, pNewObject ); + mpDrawView->EndUndo(); + } + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_GRAPHIC_SIZE_CHECK: + { + sd::GraphicSizeCheckGUIResult aResult(GetDoc()); + svx::GenericCheckDialog aDialog(GetFrameWeld(), aResult); + aDialog.run(); + + Cancel(); + rReq.Ignore(); + } + break; + + case SID_ATTRIBUTES_LINE: // BASIC + { + SetCurrentFunction( FuLine::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + if (rReq.GetArgs()) + Cancel(); + } + break; + + case SID_ATTRIBUTES_AREA: // BASIC + { + SetCurrentFunction( FuArea::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + if (rReq.GetArgs()) + Cancel(); + } + break; + + case SID_ATTR_TRANSFORM: + { + SetCurrentFunction( FuTransform::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + // tdf#138963 conditions tested for here must be the same as those + // of the early returns from FuTransform::DoExecute + if (rReq.GetArgs() || !mpDrawView->AreObjectsMarked()) + { + Invalidate(SID_RULER_OBJECT); + Cancel(); + } + } + break; + case SID_MOVE_SHAPE_HANDLE: + { + const SfxItemSet *pArgs = rReq.GetArgs (); + if (pArgs && pArgs->Count () >= 3) + { + const SfxUInt32Item* handleNumItem = rReq.GetArg(FN_PARAM_1); + const SfxUInt32Item* newPosXTwips = rReq.GetArg(FN_PARAM_2); + const SfxUInt32Item* newPosYTwips = rReq.GetArg(FN_PARAM_3); + const SfxInt32Item* OrdNum = rReq.GetArg(FN_PARAM_4); + + const sal_uLong handleNum = handleNumItem->GetValue(); + const sal_uLong newPosX = convertTwipToMm100(newPosXTwips->GetValue()); + const sal_uLong newPosY = convertTwipToMm100(newPosYTwips->GetValue()); + + mpDrawView->MoveShapeHandle(handleNum, Point(newPosX, newPosY), OrdNum ? OrdNum->GetValue() : -1); + Cancel(); + } + break; + } + case SID_CHAR_DLG_EFFECT: + case SID_CHAR_DLG: // BASIC + { + SetCurrentFunction( FuChar::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_PARA_DLG: + { + SetCurrentFunction( FuParagraph::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case FN_NUM_BULLET_ON: + { + // The value (sal_uInt16)0xFFFF means set bullet on/off. + SfxUInt16Item aItem(FN_SVX_SET_BULLET, sal_uInt16(0xFFFF)); + GetViewFrame()->GetDispatcher()->ExecuteList(FN_SVX_SET_BULLET, + SfxCallMode::RECORD, { &aItem }); + } + break; + + case FN_NUM_NUMBERING_ON: + { + // The value (sal_uInt16)0xFFFF means set bullet on/off. + SfxUInt16Item aItem(FN_SVX_SET_NUMBER, sal_uInt16(0xFFFF)); + GetViewFrame()->GetDispatcher()->ExecuteList(FN_SVX_SET_NUMBER, + SfxCallMode::RECORD, { &aItem }); + } + break; + + case SID_OUTLINE_BULLET: + case FN_SVX_SET_BULLET: + case FN_SVX_SET_NUMBER: + { + SetCurrentFunction( FuBulletAndPosition::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case FN_INSERT_SOFT_HYPHEN: + case FN_INSERT_HARDHYPHEN: + case FN_INSERT_HARD_SPACE: + case FN_INSERT_NNBSP: + case SID_INSERT_RLM : + case SID_INSERT_LRM : + case SID_INSERT_WJ : + case SID_INSERT_ZWSP: + case SID_CHARMAP: + { + SetCurrentFunction( FuBullet::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_PRESENTATION_LAYOUT: + { + SetCurrentFunction( FuPresentationLayout::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_PASTE_SPECIAL: + { + SetCurrentFunction( FuInsertClipboard::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_CHANGE_PICTURE: + case SID_INSERT_GRAPHIC: + { + SetCurrentFunction( FuInsertGraphic::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, + nSId == SID_CHANGE_PICTURE ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_INSERT_AVMEDIA: + { + SetCurrentFunction( FuInsertAVMedia::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_INSERT_OBJECT: + case SID_INSERT_FLOATINGFRAME: + case SID_INSERT_MATH: + case SID_INSERT_DIAGRAM: + case SID_ATTR_TABLE: + { + SetCurrentFunction( FuInsertOLE::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + // Set the selection tool as the old one. This in particular important for the + // zoom function, in which clicking without dragging zooms as well, and that + // makes exiting the object editing mode impossible. + if (dynamic_cast( GetOldFunction().get() ) == nullptr) + SetOldFunction( FuSelection::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + case SID_CLASSIFICATION_APPLY: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + const SfxPoolItem* pItem = nullptr; + if (pArgs && pArgs->GetItemState(nSId, false, &pItem) == SfxItemState::SET) + { + const OUString& rName = static_cast(pItem)->GetValue(); + auto eType = SfxClassificationPolicyType::IntellectualProperty; + if (pArgs->GetItemState(SID_TYPE_NAME, false, &pItem) == SfxItemState::SET) + { + const OUString& rType = static_cast(pItem)->GetValue(); + eType = SfxClassificationHelper::stringToPolicyType(rType); + } + if (SfxViewFrame* pViewFrame = GetViewFrame()) + { + if (SfxObjectShell* pObjectShell = pViewFrame->GetObjectShell()) + { + SfxClassificationHelper aHelper(pObjectShell->getDocProperties()); + aHelper.SetBACName(rName, eType); + } + } + } + else + SAL_WARN("sd.ui", "missing parameter for SID_CLASSIFICATION_APPLY"); + + Cancel(); + rReq.Ignore(); + } + break; + + case SID_CLASSIFICATION_DIALOG: + { + if (SfxObjectShell* pObjShell = SfxObjectShell::Current()) + { + css::uno::Reference xDocProps(pObjShell->getDocProperties()); + auto xDialog = std::make_shared(GetFrameWeld(), xDocProps, false, [](){} ); + ClassificationCollector aCollector(*this, xDocProps); + aCollector.collect(); + + xDialog->setupValues(std::vector(aCollector.getResults())); + + if (RET_OK == xDialog->run()) + { + ClassificationInserter aInserter(*this, xDocProps); + aInserter.insert(xDialog->getResult()); + } + xDialog.reset(); + } + + Cancel(); + rReq.Ignore(); + } + break; + + case SID_COPYOBJECTS: + { + if ( mpDrawView->IsPresObjSelected(false) ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + SetCurrentFunction( FuCopy::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + } + Cancel(); + rReq.Ignore (); + } + break; + + case SID_INSERTFILE: // BASIC + { + Broadcast (ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_START)); + SetCurrentFunction( FuInsertFile::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Broadcast (ViewShellHint(ViewShellHint::HINT_COMPLEX_MODEL_CHANGE_END)); + Cancel(); + rReq.Done (); + } + break; + + case SID_SELECT_BACKGROUND: + case SID_SAVE_BACKGROUND: + case SID_ATTR_PAGE_SIZE: + case SID_ATTR_PAGE: + case SID_PAGESETUP: // BASIC ?? + { + SetCurrentFunction( FuPage::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); // we generate independent macros !! + } + break; + + case SID_BEFORE_OBJ: + case SID_BEHIND_OBJ: + { + SetCurrentFunction( FuDisplayOrder::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + rReq.Done(); + // finishes itself, no Cancel() needed! + } + break; + + case SID_REVERSE_ORDER: // BASIC + { + mpDrawView->ReverseOrderOfMarked(); + Cancel(); + rReq.Done (); + } + break; + + case SID_ANIMATION_EFFECTS: + { + SetCurrentFunction( FuObjectAnimationParameters::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_EXECUTE_ANIMATION_EFFECT: + { + SetCurrentFunction(FuExecuteInteraction::Create(this, GetActiveWindow(), + mpDrawView.get(), GetDoc(), rReq)); + Cancel(); + } + break; + + case SID_LINEEND_POLYGON: + { + SetCurrentFunction( FuLineEnd::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_CAPTUREPOINT: + // negative value to signal call from menu + maMousePos = Point(-1,-1); + [[fallthrough]]; + case SID_SET_SNAPITEM: + { + SetCurrentFunction( FuSnapLine::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_MANAGE_LINKS: + { + SetCurrentFunction( FuLink::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_THESAURUS: + { + SetCurrentFunction( FuThesaurus::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_TEXTATTR_DLG: + { + if (mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + SetCurrentFunction( FuTextAttrDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_MEASURE_DLG: + { + SetCurrentFunction( FuMeasureDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_CONNECTION_DLG: + { + SetCurrentFunction( FuConnectionDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Done(); + } + break; + + case SID_CONNECTION_NEW_ROUTING: + { + SfxItemSetFixed aDefAttr( GetPool() ); + GetView()->SetAttributes( aDefAttr, true ); // (ReplaceAll) + + Cancel(); + rReq.Done(); + } + break; + + case SID_TWAIN_SELECT: + { + if( mxScannerManager.is() ) + { + try + { + const css::uno::Sequence< css::scanner::ScannerContext > + aContexts( mxScannerManager->getAvailableScanners() ); + + if( aContexts.hasElements() ) + { + css::scanner::ScannerContext aContext( aContexts.getConstArray()[ 0 ] ); + + Reference xInit(mxScannerManager, UNO_QUERY); + if (xInit.is()) + { + // initialize dialog + weld::Window* pWindow = rReq.GetFrameWeld(); + uno::Sequence aSeq(comphelper::InitAnyPropertySequence( + { + {"ParentWindow", pWindow ? uno::Any(pWindow->GetXWindow()) : uno::Any(Reference())} + })); + xInit->initialize( aSeq ); + } + + mxScannerManager->configureScannerAndScan( aContext, mxScannerListener ); + } + } + catch(...) + { + } + } + + Cancel(); + rReq.Done(); + } + break; + + case SID_TWAIN_TRANSFER: + { + bool bDone = false; + + if( mxScannerManager.is() ) + { + try + { + const css::uno::Sequence< css::scanner::ScannerContext > aContexts( mxScannerManager->getAvailableScanners() ); + + if( aContexts.hasElements() ) + { + mxScannerManager->startScan( aContexts.getConstArray()[ 0 ], mxScannerListener ); + bDone = true; + } + } + catch( ... ) + { + } + } + + if( !bDone ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, +#ifndef UNX + SdResId(STR_TWAIN_NO_SOURCE) +#else + SdResId(STR_TWAIN_NO_SOURCE_UNX) +#endif + )); + xInfoBox->run(); + + } + else + { + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_TWAIN_SELECT ); + rBindings.Invalidate( SID_TWAIN_TRANSFER ); + } + + Cancel(); + rReq.Done(); + } + break; + + case SID_POLYGON_MORPHING: + { + SetCurrentFunction( FuMorph::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_INSERTLAYER: + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + sal_uInt16 nLayerCnt = rLayerAdmin.GetLayerCount(); + sal_uInt16 nLayer = nLayerCnt - 2 + 1; + OUString aLayerName = SdResId(STR_LAYER) + OUString::number(nLayer); + OUString aLayerTitle, aLayerDesc; + bool bIsVisible = false; + bool bIsLocked = false; + bool bIsPrintable = false; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + if (! pArgs) + { + SfxItemSetFixed aNewAttr( GetDoc()->GetPool() ); + + aNewAttr.Put( makeSdAttrLayerName( aLayerName ) ); + aNewAttr.Put( makeSdAttrLayerTitle() ); + aNewAttr.Put( makeSdAttrLayerDesc() ); + aNewAttr.Put( makeSdAttrLayerVisible() ); + aNewAttr.Put( makeSdAttrLayerPrintable() ); + aNewAttr.Put( makeSdAttrLayerLocked() ); + aNewAttr.Put( makeSdAttrLayerThisPage() ); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr pDlg( pFact->CreateSdInsertLayerDlg(pWin ? pWin->GetFrameWeld() : nullptr, aNewAttr, true, SdResId(STR_INSERTLAYER)) ); + pDlg->SetHelpId( SD_MOD()->GetSlotPool()->GetSlot( SID_INSERTLAYER )->GetCommand() ); + + // test for already existing names + bool bLoop = true; + while( bLoop && pDlg->Execute() == RET_OK ) + { + pDlg->GetAttr( aNewAttr ); + aLayerName = static_cast( aNewAttr.Get (ATTR_LAYER_NAME)).GetValue (); + + if( rLayerAdmin.GetLayer( aLayerName ) + || aLayerName.isEmpty() + || LayerTabBar::IsLocalizedNameOfStandardLayer( aLayerName) ) + { + // name already exists + std::unique_ptr xWarn(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + } + else + bLoop = false; + } + if( bLoop ) // was canceled + { + pDlg.disposeAndClear(); + Cancel(); + rReq.Ignore (); + break; + } + else + { + aLayerTitle = static_cast( aNewAttr.Get (ATTR_LAYER_TITLE)).GetValue (); + aLayerDesc = static_cast( aNewAttr.Get (ATTR_LAYER_DESC)).GetValue (); + bIsVisible = static_cast( aNewAttr.Get (ATTR_LAYER_VISIBLE)).GetValue (); + bIsLocked = static_cast( aNewAttr.Get (ATTR_LAYER_LOCKED)).GetValue () ; + bIsPrintable = static_cast( aNewAttr.Get (ATTR_LAYER_PRINTABLE)).GetValue () ; + } + } + else if (pArgs->Count () != 4) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + Cancel(); + rReq.Ignore (); + break; + } + else + { + const SfxStringItem* pLayerName = rReq.GetArg(ID_VAL_LAYERNAME); + const SfxBoolItem* pIsVisible = rReq.GetArg(ID_VAL_ISVISIBLE); + const SfxBoolItem* pIsLocked = rReq.GetArg(ID_VAL_ISLOCKED); + const SfxBoolItem* pIsPrintable = rReq.GetArg(ID_VAL_ISPRINTABLE); + + aLayerName = pLayerName->GetValue (); + bIsVisible = pIsVisible->GetValue (); + bIsLocked = pIsLocked->GetValue (); + bIsPrintable = pIsPrintable->GetValue (); + } + + OUString aPrevLayer = mpDrawView->GetActiveLayer(); + SdrLayer* pLayer; + sal_uInt16 nPrevLayer = 0; + nLayerCnt = rLayerAdmin.GetLayerCount(); + + for ( nLayer = 0; nLayer < nLayerCnt; nLayer++ ) + { + pLayer = rLayerAdmin.GetLayer(nLayer); + OUString aName = pLayer->GetName(); + + if ( aPrevLayer == aName ) + { + nPrevLayer = std::max(nLayer, sal_uInt16(4)); + } + } + + mpDrawView->InsertNewLayer(aLayerName, nPrevLayer + 1); + pLayer = rLayerAdmin.GetLayer(aLayerName); + if( pLayer ) + { + pLayer->SetTitle( aLayerTitle ); + pLayer->SetDescription( aLayerDesc ); + } + + mpDrawView->SetLayerVisible( aLayerName, bIsVisible ); + mpDrawView->SetLayerLocked( aLayerName, bIsLocked); + mpDrawView->SetLayerPrintable(aLayerName, bIsPrintable); + + mpDrawView->SetActiveLayer(aLayerName); + + ResetActualLayer(); + + GetDoc()->SetChanged(); + + GetViewFrame()->GetDispatcher()->Execute(SID_SWITCHLAYER, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + Cancel(); + rReq.Done (); + } + break; + + case SID_MODIFYLAYER: + { + if(!GetLayerTabControl()) // #i87182# + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + Cancel(); + rReq.Ignore(); + break; + } + + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + sal_uInt16 nCurPage = GetLayerTabControl()->GetCurPageId(); + OUString aLayerName = GetLayerTabControl()->GetLayerName(nCurPage); + SdrLayer* pLayer = rLayerAdmin.GetLayer(aLayerName); + + OUString aLayerTitle = pLayer->GetTitle(); + OUString aLayerDesc = pLayer->GetDescription(); + + OUString aOldLayerName(aLayerName); + OUString aOldLayerTitle(aLayerTitle); + OUString aOldLayerDesc(aLayerDesc); + + bool bIsVisible, bIsLocked, bIsPrintable; + bool bOldIsVisible = bIsVisible = mpDrawView->IsLayerVisible(aLayerName); + bool bOldIsLocked = bIsLocked = mpDrawView->IsLayerLocked(aLayerName); + bool bOldIsPrintable = bIsPrintable = mpDrawView->IsLayerPrintable(aLayerName); + + const SfxItemSet* pArgs = rReq.GetArgs(); + // is it allowed to delete the layer? + bool bDelete = !( LayerTabBar::IsRealNameOfStandardLayer(aLayerName) ); + + if (! pArgs) + { + SfxItemSetFixed aNewAttr( GetDoc()->GetPool() ); + + aNewAttr.Put( makeSdAttrLayerName( aLayerName ) ); + aNewAttr.Put( makeSdAttrLayerTitle( aLayerTitle ) ); + aNewAttr.Put( makeSdAttrLayerDesc( aLayerDesc ) ); + aNewAttr.Put( makeSdAttrLayerVisible( bIsVisible ) ); + aNewAttr.Put( makeSdAttrLayerLocked( bIsLocked ) ); + aNewAttr.Put( makeSdAttrLayerPrintable( bIsPrintable ) ); + aNewAttr.Put( makeSdAttrLayerThisPage() ); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr pDlg( pFact->CreateSdInsertLayerDlg(pWin ? pWin->GetFrameWeld() : nullptr, aNewAttr, bDelete, SdResId(STR_MODIFYLAYER)) ); + pDlg->SetHelpId( SD_MOD()->GetSlotPool()->GetSlot( SID_MODIFYLAYER )->GetCommand() ); + + // test for already existing names + bool bLoop = true; + sal_uInt16 nRet = 0; + while( bLoop ) + { + nRet = pDlg->Execute(); + if (nRet != RET_OK) + break; + pDlg->GetAttr( aNewAttr ); + aLayerName = static_cast( aNewAttr.Get (ATTR_LAYER_NAME)).GetValue (); + if (bDelete) + { + if( (rLayerAdmin.GetLayer( aLayerName ) && aLayerName != aOldLayerName) + || LayerTabBar::IsRealNameOfStandardLayer(aLayerName) + || LayerTabBar::IsLocalizedNameOfStandardLayer(aLayerName) + || aLayerName.isEmpty() ) + { + // name already exists + std::unique_ptr xWarn(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + SdResId(STR_WARN_NAME_DUPLICATE))); + xWarn->run(); + } + else + bLoop = false; + } + else + bLoop = false; // altering name is already disabled in the dialog itself + } + switch (nRet) + { + case RET_OK : + aLayerTitle = static_cast( aNewAttr.Get (ATTR_LAYER_TITLE)).GetValue (); + aLayerDesc = static_cast( aNewAttr.Get (ATTR_LAYER_DESC)).GetValue (); + bIsVisible = static_cast( aNewAttr.Get (ATTR_LAYER_VISIBLE)).GetValue (); + bIsLocked = static_cast( aNewAttr.Get (ATTR_LAYER_LOCKED)).GetValue (); + bIsPrintable = static_cast( aNewAttr.Get (ATTR_LAYER_PRINTABLE)).GetValue (); + break; + + default : + pDlg.disposeAndClear(); + rReq.Ignore (); + Cancel (); + return; + } + } + else if (pArgs->Count () == 4) + { + const SfxStringItem* pLayerName = rReq.GetArg(ID_VAL_LAYERNAME); + const SfxBoolItem* pIsVisible = rReq.GetArg(ID_VAL_ISVISIBLE); + const SfxBoolItem* pIsLocked = rReq.GetArg(ID_VAL_ISLOCKED); + const SfxBoolItem* pIsPrintable = rReq.GetArg(ID_VAL_ISPRINTABLE); + + aLayerName = pLayerName->GetValue (); + bIsVisible = pIsVisible->GetValue (); + bIsLocked = pIsLocked->GetValue (); + bIsPrintable = pIsPrintable->GetValue (); + } + else + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + Cancel (); + rReq.Ignore (); + break; + } + + SfxUndoManager* pManager = GetDoc()->GetDocSh()->GetUndoManager(); + std::unique_ptr pAction( new SdLayerModifyUndoAction( + GetDoc(), + pLayer, + // old values + aOldLayerName, + aOldLayerTitle, + aOldLayerDesc, + bOldIsVisible, + bOldIsLocked, + bOldIsPrintable, + // new values + aLayerName, + aLayerTitle, + aLayerDesc, + bIsVisible, + bIsLocked, + bIsPrintable + ) ); + pManager->AddUndoAction( std::move(pAction) ); + + ModifyLayer( pLayer, aLayerName, aLayerTitle, aLayerDesc, bIsVisible, bIsLocked, bIsPrintable ); + + Cancel(); + rReq.Done (); + } + break; + + case SID_RENAMELAYER: + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + if(GetLayerTabControl()) // #i87182# + { + GetLayerTabControl()->StartEditMode(GetLayerTabControl()->GetCurPageId()); + } + else + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_EDIT_HYPERLINK : + { + // Ensure the field is selected first + OutlinerView* pOutView = mpDrawView->GetTextEditOutlinerView(); + if (pOutView) + pOutView->SelectFieldAtCursor(); + + GetViewFrame()->GetDispatcher()->Execute( SID_HYPERLINK_DIALOG ); + + Cancel(); + rReq.Done (); + } + break; + + case SID_OPEN_HYPERLINK: + { + OutlinerView* pOutView = mpDrawView->GetTextEditOutlinerView(); + if ( pOutView ) + { + const SvxFieldData* pField = pOutView->GetFieldAtCursor(); + if( auto pURLField = dynamic_cast< const SvxURLField *>( pField ) ) + { + SfxStringItem aUrl( SID_FILE_NAME, pURLField->GetURL() ); + SfxStringItem aTarget( SID_TARGETNAME, pURLField->GetTargetFrame() ); + + OUString aReferName; + SfxViewFrame* pFrame = GetViewFrame(); + SfxMedium* pMed = pFrame->GetObjectShell()->GetMedium(); + if (pMed) + aReferName = pMed->GetName(); + + SfxFrameItem aFrm( SID_DOCFRAME, pFrame ); + SfxStringItem aReferer( SID_REFERER, aReferName ); + + SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, false ); + SfxBoolItem aBrowsing( SID_BROWSE, true ); + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + { + pViewFrm->GetDispatcher()->ExecuteList(SID_OPENDOC, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aUrl, &aTarget, &aFrm, &aReferer, + &aNewView, &aBrowsing }); + } + } + } + Cancel(); + rReq.Done (); + } + break; + + case SID_COPY_HYPERLINK_LOCATION: + { + OutlinerView* pOutView = mpDrawView->GetTextEditOutlinerView(); + if ( pOutView ) + { + const SvxFieldData* pField = pOutView->GetFieldAtCursor(); + if (const SvxURLField* pURLField = dynamic_cast(pField)) + { + uno::Reference xClipboard + = pOutView->GetWindow()->GetClipboard(); + + vcl::unohelper::TextDataObject::CopyStringTo(pURLField->GetURL(), xClipboard, SfxViewShell::Current()); + } + } + + Cancel(); + rReq.Done (); + } + break; + + case SID_HYPERLINK_SETLINK: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if (pReqArgs) + { + const SvxHyperlinkItem* pHLItem = + &pReqArgs->Get(SID_HYPERLINK_SETLINK); + + if (pHLItem->GetInsertMode() == HLINK_FIELD) + { + InsertURLField(pHLItem->GetURL(), pHLItem->GetName(), + pHLItem->GetTargetFrame()); + } + else if (pHLItem->GetInsertMode() == HLINK_BUTTON) + { + InsertURLButton(pHLItem->GetURL(), pHLItem->GetName(), + pHLItem->GetTargetFrame(), nullptr); + } + else if (pHLItem->GetInsertMode() == HLINK_DEFAULT) + { + OutlinerView* pOlView = mpDrawView->GetTextEditOutlinerView(); + + if (pOlView || comphelper::LibreOfficeKit::isActive()) + { + InsertURLField(pHLItem->GetURL(), pHLItem->GetName(), + pHLItem->GetTargetFrame()); + } + else + { + InsertURLButton(pHLItem->GetURL(), pHLItem->GetName(), + pHLItem->GetTargetFrame(), nullptr); + } + } + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_HIDE_LAST_LEVEL: + { + ESelection aSel; + // fdo#78151 editing a PresObjKind::Outline in a master page ? + ::Outliner* pOL = GetOutlinerForMasterPageOutlineTextObj(aSel); + if (pOL) + { + //we are on the last paragraph + aSel.Adjust(); + if (aSel.nEndPara == pOL->GetParagraphCount() - 1) + { + sal_uInt16 nDepth = pOL->GetDepth(aSel.nEndPara); + //there exists a previous numbering level + if (nDepth != sal_uInt16(-1) && nDepth > 0) + { + Paragraph* pPara = pOL->GetParagraph(aSel.nEndPara); + pOL->Remove(pPara, 1); + } + } + } + Cancel(); + rReq.Done (); + } + break; + + case SID_SHOW_NEXT_LEVEL: + { + const TranslateId STR_PRESOBJ_MPOUTLINE_ARY[] + { + STR_PRESOBJ_MPOUTLINE, + STR_PRESOBJ_MPOUTLLAYER2, + STR_PRESOBJ_MPOUTLLAYER3, + STR_PRESOBJ_MPOUTLLAYER4, + STR_PRESOBJ_MPOUTLLAYER5, + STR_PRESOBJ_MPOUTLLAYER6, + STR_PRESOBJ_MPOUTLLAYER7, + STR_PRESOBJ_MPNOTESTITLE, + STR_PRESOBJ_MPNOTESTEXT, + STR_PRESOBJ_NOTESTEXT + }; + + ESelection aSel; + // fdo#78151 editing a PresObjKind::Outline in a master page ? + ::Outliner* pOL = GetOutlinerForMasterPageOutlineTextObj(aSel); + if (pOL) + { + //we are on the last paragraph + aSel.Adjust(); + if (aSel.nEndPara == pOL->GetParagraphCount() - 1) + { + sal_uInt16 nDepth = pOL->GetDepth(aSel.nEndPara); + //there exists a previous numbering level + if (nDepth < 8) + { + sal_uInt16 nNewDepth = nDepth+1; + pOL->Insert(SdResId(STR_PRESOBJ_MPOUTLINE_ARY[nNewDepth]), EE_PARA_APPEND, nNewDepth); + } + } + } + Cancel(); + rReq.Done (); + } + break; + + case SID_INSERT_FLD_DATE_FIX: + case SID_INSERT_FLD_DATE_VAR: + case SID_INSERT_FLD_TIME_FIX: + case SID_INSERT_FLD_TIME_VAR: + case SID_INSERT_FLD_AUTHOR: + case SID_INSERT_FLD_PAGE: + case SID_INSERT_FLD_PAGE_TITLE: + case SID_INSERT_FLD_PAGES: + case SID_INSERT_FLD_FILE: + { + sal_uInt16 nMul = 1; + std::unique_ptr pFieldItem; + + switch( nSId ) + { + case SID_INSERT_FLD_DATE_FIX: + pFieldItem.reset(new SvxFieldItem( + SvxDateField( Date( Date::SYSTEM ), SvxDateType::Fix ), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_DATE_VAR: + pFieldItem.reset(new SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_TIME_FIX: + pFieldItem.reset(new SvxFieldItem( + SvxExtTimeField( ::tools::Time( ::tools::Time::SYSTEM ), SvxTimeType::Fix ), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_TIME_VAR: + pFieldItem.reset(new SvxFieldItem( SvxExtTimeField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_AUTHOR: + { + SvtUserOptions aUserOptions; + pFieldItem.reset(new SvxFieldItem( + SvxAuthorField( + aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID() ), EE_FEATURE_FIELD )); + } + break; + + case SID_INSERT_FLD_PAGE: + { + pFieldItem.reset(new SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD )); + nMul = 3; + } + break; + + case SID_INSERT_FLD_PAGE_TITLE: + { + pFieldItem.reset(new SvxFieldItem( SvxPageTitleField(), EE_FEATURE_FIELD)); + nMul = 3; + } + break; + + case SID_INSERT_FLD_PAGES: + { + pFieldItem.reset(new SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD )); + nMul = 3; + } + break; + + case SID_INSERT_FLD_FILE: + { + OUString aName; + if( GetDocSh()->HasName() ) + aName = GetDocSh()->GetMedium()->GetName(); + pFieldItem.reset(new SvxFieldItem( SvxExtFileField( aName ), EE_FEATURE_FIELD )); + } + break; + } + + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if( pOLV ) + { + const SvxFieldItem* pOldFldItem = pOLV->GetFieldAtSelection(); + + if( pOldFldItem && ( nullptr != dynamic_cast< const SvxURLField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxDateField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxTimeField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxPageField *>( pOldFldItem->GetField() ) ) ) + { + // select field, then it will be deleted when inserting + ESelection aSel = pOLV->GetSelection(); + if( aSel.nStartPos == aSel.nEndPos ) + aSel.nEndPos++; + pOLV->SetSelection( aSel ); + } + + if( pFieldItem ) + pOLV->InsertField( *pFieldItem ); + } + else + { + Outliner* pOutl = GetDoc()->GetInternalOutliner(); + pOutl->Init( OutlinerMode::TextObject ); + OutlinerMode nOutlMode = pOutl->GetOutlinerMode(); + pOutl->SetStyleSheet( 0, nullptr ); + pOutl->QuickInsertField( *pFieldItem, ESelection() ); + std::optional pOutlParaObject = pOutl->CreateParaObject(); + + SdrRectObj* pRectObj = new SdrRectObj( + *GetDoc(), + SdrObjKind::Text); + pRectObj->SetMergedItem(makeSdrTextAutoGrowWidthItem(true)); + + pOutl->UpdateFields(); + pOutl->SetUpdateLayout( true ); + Size aSize( pOutl->CalcTextSize() ); + aSize.setWidth( aSize.Width() * nMul ); + pOutl->SetUpdateLayout( false ); + + Point aPos; + ::tools::Rectangle aRect( aPos, GetActiveWindow()->GetOutputSizePixel() ); + aPos = aRect.Center(); + aPos = GetActiveWindow()->PixelToLogic(aPos); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + + ::tools::Rectangle aLogicRect(aPos, aSize); + pRectObj->SetLogicRect(aLogicRect); + pRectObj->SetOutlinerParaObject( std::move(pOutlParaObject) ); + mpDrawView->InsertObjectAtView(pRectObj, *mpDrawView->GetSdrPageView()); + pOutl->Init( nOutlMode ); + } + + pFieldItem.reset(); + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_MODIFY_FIELD: + { + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if( pOLV ) + { + const SvxFieldItem* pFldItem = pOLV->GetFieldAtSelection(); + + if( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) ) ) + { + // Dialog... + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr pDlg( pFact->CreateSdModifyFieldDlg(pWin ? pWin->GetFrameWeld() : nullptr, pFldItem->GetField(), pOLV->GetAttribs() ) ); + if( pDlg->Execute() == RET_OK ) + { + // To make a correct SetAttribs() call at the utlinerView + // it is necessary to split the actions here + std::unique_ptr pField(pDlg->GetField()); + ESelection aSel = pOLV->GetSelection(); + bool bSelectionWasModified(false); + + if( pField ) + { + SvxFieldItem aFieldItem( *pField, EE_FEATURE_FIELD ); + + if( aSel.nStartPos == aSel.nEndPos ) + { + bSelectionWasModified = true; + aSel.nEndPos++; + pOLV->SetSelection( aSel ); + } + + pOLV->InsertField( aFieldItem ); + + // select again for eventual SetAttribs call + pOLV->SetSelection( aSel ); + } + + SfxItemSet aSet( pDlg->GetItemSet() ); + + if( aSet.Count() ) + { + pOLV->SetAttribs( aSet ); + + ::Outliner* pOutliner = pOLV->GetOutliner(); + if( pOutliner ) + pOutliner->UpdateFields(); + } + + if(pField) + { + // restore selection to original + if(bSelectionWasModified) + { + aSel.nEndPos--; + pOLV->SetSelection( aSel ); + } + } + } + } + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_OPEN_XML_FILTERSETTINGS: + { + try + { + css::uno::Reference < css::ui::dialogs::XExecutableDialog > xDialog = css::ui::dialogs::XSLTFilterDialog::create( ::comphelper::getProcessComponentContext() ); + xDialog->execute(); + } + catch( css::uno::RuntimeException& ) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_GROUP: // BASIC + { + if ( mpDrawView->IsPresObjSelected( true, true, true ) ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + mpDrawView->GroupMarked(); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_UNGROUP: // BASIC + { + mpDrawView->UnGroupMarked(); + Cancel(); + rReq.Done (); + } + break; + + case SID_NAME_GROUP: + { + // only allow for single object selection since the name of an object needs + // to be unique + if(1 == mpDrawView->GetMarkedObjectCount()) + { + // #i68101# + SdrObject* pSelected = mpDrawView->GetMarkedObjectByIndex(0); + OSL_ENSURE(pSelected, "DrawViewShell::FuTemp03: nMarkCount, but no object (!)"); + OUString aName(pSelected->GetName()); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSvxObjectNameDialog(GetFrameWeld(), aName)); + + pDlg->SetCheckNameHdl(LINK(this, DrawViewShell, NameObjectHdl)); + + if(RET_OK == pDlg->Execute()) + { + pDlg->GetName(aName); + pSelected->SetName(aName); + + SdPage* pPage = GetActualPage(); + if (pPage) + pPage->notifyObjectRenamed(pSelected); + } + } + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_NAVIGATOR_STATE, true ); + rBindings.Invalidate( SID_CONTEXT ); + + Cancel(); + rReq.Ignore(); + break; + } + + // #i68101# + case SID_OBJECT_TITLE_DESCRIPTION: + { + if(1 == mpDrawView->GetMarkedObjectCount()) + { + SdrObject* pSelected = mpDrawView->GetMarkedObjectByIndex(0); + OSL_ENSURE(pSelected, "DrawViewShell::FuTemp03: nMarkCount, but no object (!)"); + OUString aTitle(pSelected->GetTitle()); + OUString aDescription(pSelected->GetDescription()); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateSvxObjectTitleDescDialog( + GetFrameWeld(), aTitle, aDescription)); + + if(RET_OK == pDlg->Execute()) + { + pDlg->GetTitle(aTitle); + pDlg->GetDescription(aDescription); + pSelected->SetTitle(aTitle); + pSelected->SetDescription(aDescription); + } + } + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_NAVIGATOR_STATE, true ); + rBindings.Invalidate( SID_CONTEXT ); + + Cancel(); + rReq.Ignore(); + break; + } + + case SID_ENTER_GROUP: // BASIC + { + mpDrawView->EnterMarkedGroup(); + Cancel(); + rReq.Done (); + } + break; + + case SID_LEAVE_GROUP: // BASIC + { + mpDrawView->LeaveOneGroup(); + Cancel(); + rReq.Done (); + } + break; + + case SID_LEAVE_ALL_GROUPS: // BASIC + { + mpDrawView->LeaveAllGroup(); + Cancel(); + rReq.Done (); + } + break; + + case SID_TEXT_COMBINE: // BASIC + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->CombineMarkedTextObjects(); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_COMBINE: // BASIC + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->CombineMarkedObjects(false); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_DISTRIBUTE_HLEFT: + case SID_DISTRIBUTE_HCENTER: + case SID_DISTRIBUTE_HDISTANCE: + case SID_DISTRIBUTE_HRIGHT: + case SID_DISTRIBUTE_VTOP: + case SID_DISTRIBUTE_VCENTER: + case SID_DISTRIBUTE_VDISTANCE: + case SID_DISTRIBUTE_VBOTTOM: + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + mpDrawView->DistributeMarkedObjects(nSId); + } + Cancel(); + rReq.Done (); + } + break; + case SID_POLY_MERGE: + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->MergeMarkedObjects(SdrMergeMode::Merge); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_POLY_SUBSTRACT: + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->MergeMarkedObjects(SdrMergeMode::Subtract); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_POLY_INTERSECT: + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->MergeMarkedObjects(SdrMergeMode::Intersect); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_EQUALIZEWIDTH: + case SID_EQUALIZEHEIGHT: + { + // End text edit to avoid conflicts + if(mpDrawView->IsTextEdit()) + mpDrawView->SdrEndTextEdit(); + + mpDrawView->EqualizeMarkedObjects(nSId == SID_EQUALIZEWIDTH); + Cancel(); + rReq.Done (); + } + break; + + case SID_DISMANTLE: // BASIC + { + if ( mpDrawView->IsDismantlePossible() ) + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->DismantleMarkedObjects(); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_CONNECT: // BASIC + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->CombineMarkedObjects(); + } + Cancel(); + rReq.Done (); + } + break; + + case SID_BREAK: // BASIC + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + if ( mpDrawView->IsBreak3DObjPossible() ) + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->Break3DObj(); + } + else if ( mpDrawView->IsDismantlePossible(true) ) + { + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->DismantleMarkedObjects(true); + } + else if ( mpDrawView->IsImportMtfPossible() ) + { + weld::WaitObject aWait(GetFrameWeld()); + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nCnt=rMarkList.GetMarkCount(); + + // determine the sum of meta objects of all selected meta files + sal_uLong nCount = 0; + for(size_t nm=0; nmGetMarkedSdrObj(); + SdrGrafObj* pGraf= dynamic_cast< SdrGrafObj *>( pObj ); + SdrOle2Obj* pOle2= dynamic_cast< SdrOle2Obj *>( pObj ); + + if (pGraf != nullptr) + { + if (pGraf->HasGDIMetaFile()) + { + nCount += pGraf->GetGraphic().GetGDIMetaFile().GetActionSize(); + } + else if (pGraf->isEmbeddedVectorGraphicData()) + { + nCount += pGraf->getMetafileFromEmbeddedVectorGraphicData().GetActionSize(); + } + } + + if(pOle2 && pOle2->GetGraphic()) + { + nCount += pOle2->GetGraphic()->GetGDIMetaFile().GetActionSize(); + } + } + + // decide with the sum of all meta objects if we should show a dialog + if(nCount < MIN_ACTIONS_FOR_DIALOG) + { + // nope, no dialog + mpDrawView->DoImportMarkedMtf(); + } + else + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateBreakDlg(GetFrameWeld(), mpDrawView.get(), GetDocSh(), nCount, static_cast(nCnt) )); + pDlg->Execute(); + } + } + + Cancel(); + rReq.Done (); + } + break; + + case SID_CONVERT_TO_3D: + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + if (mpDrawView->IsConvertTo3DObjPossible()) + { + if (mpDrawView->IsTextEdit()) + { + mpDrawView->SdrEndTextEdit(); + } + + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedObjTo3D(); + } + } + + Cancel(); + rReq.Done(); + } + break; + + case SID_FRAME_TO_TOP: // BASIC + { + mpDrawView->PutMarkedToTop(); + Cancel(); + rReq.Done (); + } + break; + + case SID_MOREFRONT: // BASIC + case SID_FRAME_UP: // BASIC + { + mpDrawView->MovMarkedToTop(); + Cancel(); + rReq.Done (); + } + break; + + case SID_MOREBACK: // BASIC + case SID_FRAME_DOWN: // BASIC + { + mpDrawView->MovMarkedToBtm(); + Cancel(); + rReq.Done (); + } + break; + + case SID_FRAME_TO_BOTTOM: // BASIC + { + mpDrawView->PutMarkedToBtm(); + Cancel(); + rReq.Done (); + } + break; + + case SID_HORIZONTAL: // BASIC + case SID_FLIP_HORIZONTAL: + { + mpDrawView->MirrorAllMarkedHorizontal(); + Cancel(); + rReq.Done (); + } + break; + + case SID_VERTICAL: // BASIC + case SID_FLIP_VERTICAL: + { + mpDrawView->MirrorAllMarkedVertical(); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_LEFT: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::Left, SdrVertAlign::NONE); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_CENTER: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::Center, SdrVertAlign::NONE); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_RIGHT: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::Right, SdrVertAlign::NONE); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_UP: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Top); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_MIDDLE: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Center); + Cancel(); + rReq.Done (); + } + break; + + case SID_OBJECT_ALIGN_DOWN: // BASIC + { + mpDrawView->AlignMarkedObjects(SdrHorAlign::NONE, SdrVertAlign::Bottom); + Cancel(); + rReq.Done (); + } + break; + + case SID_SELECTALL: // BASIC + { + if( (dynamic_cast( GetOldFunction().get() ) != nullptr) && + !GetView()->IsFrameDragSingles() && GetView()->HasMarkablePoints()) + { + if ( !mpDrawView->IsAction() ) + mpDrawView->MarkAllPoints(); + } + else + mpDrawView->SelectAll(); + + Cancel(); + rReq.Done (); + } + break; + + case SID_STYLE_NEW: // BASIC ??? + case SID_STYLE_APPLY: + case SID_STYLE_EDIT: + case SID_STYLE_DELETE: + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + case SID_STYLE_FAMILY: + case SID_STYLE_WATERCAN: + case SID_STYLE_UPDATE_BY_EXAMPLE: + case SID_STYLE_NEW_BY_EXAMPLE: + { + if( rReq.GetSlot() == SID_STYLE_EDIT && !rReq.GetArgs() ) + { + SfxStyleSheet* pStyleSheet = mpDrawView->GetStyleSheet(); + if( pStyleSheet && pStyleSheet->GetFamily() == SfxStyleFamily::Page) + pStyleSheet = static_cast(pStyleSheet)->GetPseudoStyleSheet(); + + if( (pStyleSheet == nullptr) && GetView()->IsTextEdit() ) + { + GetView()->SdrEndTextEdit(); + + pStyleSheet = mpDrawView->GetStyleSheet(); + if(pStyleSheet && pStyleSheet->GetFamily() == SfxStyleFamily::Page) + pStyleSheet = static_cast(pStyleSheet)->GetPseudoStyleSheet(); + } + + if( pStyleSheet == nullptr ) + { + rReq.Ignore(); + break; + } + + SfxAllItemSet aSet(GetDoc()->GetPool()); + + SfxStringItem aStyleNameItem( SID_STYLE_EDIT, pStyleSheet->GetName() ); + aSet.Put(aStyleNameItem); + + SfxUInt16Item aStyleFamilyItem( SID_STYLE_FAMILY, static_cast(pStyleSheet->GetFamily()) ); + aSet.Put(aStyleFamilyItem); + + rReq.SetArgs(aSet); + } + + if( rReq.GetArgs() ) + { + SetCurrentFunction( FuTemplate::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + if( rReq.GetSlot() == SID_STYLE_APPLY ) + GetViewFrame()->GetBindings().Invalidate( SID_STYLE_APPLY ); + Cancel(); + } + else if( rReq.GetSlot() == SID_STYLE_APPLY ) + GetViewFrame()->GetDispatcher()->Execute( SID_STYLE_DESIGNER, SfxCallMode::ASYNCHRON ); + rReq.Ignore (); + } + break; + + case SID_IMAP: + { + sal_uInt16 nId = SvxIMapDlgChildWindow::GetChildWindowId(); + + GetViewFrame()->ToggleChildWindow( nId ); + GetViewFrame()->GetBindings().Invalidate( SID_IMAP ); + + if ( GetViewFrame()->HasChildWindow( nId ) + && ( ( ViewShell::Implementation::GetImageMapDialog() ) != nullptr ) ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 ) + UpdateIMapDlg( rMarkList.GetMark( 0 )->GetMarkedSdrObj() ); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_GRID_FRONT: + { + mpDrawView->SetGridFront( !mpDrawView->IsGridFront() ); + Cancel(); + rReq.Done (); + } + break; + + case SID_HELPLINES_FRONT: + { + mpDrawView->SetHlplFront( !mpDrawView->IsHlplFront() ); + Cancel(); + rReq.Done (); + } + break; + + case SID_FONTWORK: + { + if ( rReq.GetArgs() ) + { + GetViewFrame()->SetChildWindow(SvxFontWorkChildWindow::GetChildWindowId(), + static_cast(rReq.GetArgs()-> + Get(SID_FONTWORK)).GetValue()); + } + else + { + GetViewFrame()->ToggleChildWindow( SvxFontWorkChildWindow::GetChildWindowId() ); + } + + GetViewFrame()->GetBindings().Invalidate(SID_FONTWORK); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_COLOR_CONTROL: + { + if ( rReq.GetArgs() ) + GetViewFrame()->SetChildWindow(SvxColorChildWindow::GetChildWindowId(), + static_cast(rReq.GetArgs()-> + Get(SID_COLOR_CONTROL)).GetValue()); + else + GetViewFrame()->ToggleChildWindow(SvxColorChildWindow::GetChildWindowId() ); + + GetViewFrame()->GetBindings().Invalidate(SID_COLOR_CONTROL); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_EXTRUSION_TOGGLE: + case SID_EXTRUSION_TILT_DOWN: + case SID_EXTRUSION_TILT_UP: + case SID_EXTRUSION_TILT_LEFT: + case SID_EXTRUSION_TILT_RIGHT: + case SID_EXTRUSION_3D_COLOR: + case SID_EXTRUSION_DEPTH: + case SID_EXTRUSION_DIRECTION: + case SID_EXTRUSION_PROJECTION: + case SID_EXTRUSION_LIGHTING_DIRECTION: + case SID_EXTRUSION_LIGHTING_INTENSITY: + case SID_EXTRUSION_SURFACE: + case SID_EXTRUSION_DEPTH_FLOATER: + case SID_EXTRUSION_DIRECTION_FLOATER: + case SID_EXTRUSION_LIGHTING_FLOATER: + case SID_EXTRUSION_SURFACE_FLOATER: + case SID_EXTRUSION_DEPTH_DIALOG: + svx::ExtrusionBar::execute( mpDrawView.get(), rReq, GetViewFrame()->GetBindings() ); + Cancel(); + rReq.Ignore (); + break; + + case SID_FONTWORK_SHAPE: + case SID_FONTWORK_SHAPE_TYPE: + case SID_FONTWORK_ALIGNMENT: + case SID_FONTWORK_SAME_LETTER_HEIGHTS: + case SID_FONTWORK_CHARACTER_SPACING: + case SID_FONTWORK_KERN_CHARACTER_PAIRS: + case SID_FONTWORK_GALLERY_FLOATER: + case SID_FONTWORK_CHARACTER_SPACING_FLOATER: + case SID_FONTWORK_ALIGNMENT_FLOATER: + case SID_FONTWORK_CHARACTER_SPACING_DIALOG: + svx::FontworkBar::execute(*mpDrawView, rReq, GetViewFrame()->GetBindings()); + Cancel(); + rReq.Ignore (); + break; + + case SID_BMPMASK: + { + GetViewFrame()->ToggleChildWindow( SvxBmpMaskChildWindow::GetChildWindowId() ); + GetViewFrame()->GetBindings().Invalidate( SID_BMPMASK ); + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_NAVIGATOR: + { + if ( rReq.GetArgs() ) + GetViewFrame()->SetChildWindow(SID_NAVIGATOR, + static_cast(rReq.GetArgs()-> + Get(SID_NAVIGATOR)).GetValue()); + else + GetViewFrame()->ToggleChildWindow( SID_NAVIGATOR ); + + GetViewFrame()->GetBindings().Invalidate(SID_NAVIGATOR); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_SLIDE_TRANSITIONS_PANEL: + case SID_MASTER_SLIDES_PANEL: + case SID_CUSTOM_ANIMATION_PANEL: + case SID_GALLERY: + { + // First make sure that the sidebar is visible + GetViewFrame()->ShowChildWindow(SID_SIDEBAR); + + OUString panelId; + if (nSId == SID_CUSTOM_ANIMATION_PANEL) + panelId = "SdCustomAnimationPanel"; + else if (nSId == SID_GALLERY) + panelId = "GalleryPanel"; + else if (nSId == SID_SLIDE_TRANSITIONS_PANEL) + panelId = "SdSlideTransitionPanel"; + else if (nSId == SID_MASTER_SLIDES_PANEL) + panelId = "SdAllMasterPagesPanel"; + + ::sfx2::sidebar::Sidebar::TogglePanel( + panelId, + GetViewFrame()->GetFrame().GetFrameInterface()); + + Cancel(); + rReq.Done(); + } + break; + + case SID_ANIMATION_OBJECTS: + { + if ( rReq.GetArgs() ) + GetViewFrame()->SetChildWindow( + AnimationChildWindow::GetChildWindowId(), + static_cast(rReq.GetArgs()-> + Get(SID_ANIMATION_OBJECTS)).GetValue()); + else + GetViewFrame()->ToggleChildWindow( + AnimationChildWindow::GetChildWindowId() ); + + GetViewFrame()->GetBindings().Invalidate(SID_ANIMATION_OBJECTS); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_3D_WIN: + { + if ( rReq.GetArgs() ) + GetViewFrame()->SetChildWindow( Svx3DChildWindow::GetChildWindowId(), + static_cast(rReq.GetArgs()-> + Get( SID_3D_WIN )).GetValue()); + else + GetViewFrame()->ToggleChildWindow( Svx3DChildWindow::GetChildWindowId() ); + + GetViewFrame()->GetBindings().Invalidate( SID_3D_WIN ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_CONVERT_TO_3D_LATHE_FAST: + { + /* The call is enough. The initialization via Start3DCreation and + CreateMirrorPolygons is no longer needed if the parameter + sal_True is provided. Then a tilted rotary body with an axis left + besides the bounding rectangle of the selected objects is drawn + immediately and without user interaction. */ + mpDrawView->SdrEndTextEdit(); + if(GetActiveWindow()) + GetActiveWindow()->EnterWait(); + mpDrawView->End3DCreation(true); + Cancel(); + rReq.Ignore(); + if(GetActiveWindow()) + GetActiveWindow()->LeaveWait(); + } + break; + + case SID_PRESENTATION_DLG: + { + SetCurrentFunction( FuSlideShowDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_REMOTE_DLG: + { +#ifdef ENABLE_SDREMOTE + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateRemoteDialog(GetFrameWeld())); + pDlg->Execute(); +#endif + } + break; + + case SID_CUSTOMSHOW_DLG: + { + SetCurrentFunction( FuCustomShowDlg::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_EXPAND_PAGE: + { + SetCurrentFunction( FuExpandPage::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_SUMMARY_PAGE: + { + mpDrawView->SdrEndTextEdit(); + SetCurrentFunction( FuSummaryPage::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + +#if HAVE_FEATURE_AVMEDIA + case SID_AVMEDIA_PLAYER: + { + GetViewFrame()->ToggleChildWindow( ::avmedia::MediaPlayer::GetChildWindowId() ); + GetViewFrame()->GetBindings().Invalidate( SID_AVMEDIA_PLAYER ); + Cancel(); + rReq.Ignore (); + } + break; +#endif + + case SID_PRESENTATION_MINIMIZER: + { + Reference xContext(::comphelper::getProcessComponentContext()); + Reference xParser(util::URLTransformer::create(xContext)); + Reference xProvider(GetViewShellBase().GetController()->getFrame(), UNO_QUERY); + if (xProvider.is()) + { + util::URL aURL; + aURL.Complete = "vnd.com.sun.star.comp.PresentationMinimizer:execute"; + xParser->parseStrict(aURL); + uno::Reference xDispatch(xProvider->queryDispatch(aURL, OUString(), 0)); + if (xDispatch.is()) + { + xDispatch->dispatch(aURL, uno::Sequence< beans::PropertyValue >()); + } + } + Cancel(); + rReq.Ignore(); + } + break; + + case SID_DISPLAY_MASTER_BACKGROUND: + case SID_DISPLAY_MASTER_OBJECTS: + { + // Determine current page and toggle visibility of layers + // associated with master page background or master page shapes. + // FIXME: This solution is wrong, because shapes of master pages need + // not be on layer "background" or "backgroundobjects". + // See tdf#118613 + SdPage* pPage = GetActualPage(); + if (pPage != nullptr + && GetDoc() != nullptr) + { + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + SdrLayerID aLayerId; + if (nSId == SID_DISPLAY_MASTER_BACKGROUND) + aLayerId = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + else + aLayerId = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + aVisibleLayers.Set(aLayerId, !aVisibleLayers.IsSet(aLayerId)); + pPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + Cancel(); + rReq.Done(); // Mark task as done to auto-update the state of each buttons tdf#132816 + } + break; + + case SID_PHOTOALBUM: + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr pDlg(pFact->CreateSdPhotoAlbumDialog( + pWin ? pWin->GetFrameWeld() : nullptr, + GetDoc())); + + pDlg->Execute(); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_INSERT_QRCODE: + case SID_EDIT_QRCODE: + { + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + const uno::Reference xModel = GetViewShellBase().GetController()->getModel(); + ScopedVclPtr pDlg(pFact->CreateQrCodeGenDialog( + GetFrameWeld(), xModel, rReq.GetSlot() == SID_EDIT_QRCODE)); + pDlg->Execute(); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_ADDITIONS_DIALOG: + { + OUString sAdditionsTag = ""; + + const SfxStringItem* pStringArg = rReq.GetArg(FN_PARAM_ADDITIONS_TAG); + if (pStringArg) + sAdditionsTag = pStringArg->GetValue(); + + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + ScopedVclPtr pDlg( + pFact->CreateAdditionsDialog(GetFrameWeld(), sAdditionsTag)); + pDlg->Execute(); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_ATTR_GLOW_COLOR: + case SID_ATTR_GLOW_RADIUS: + case SID_ATTR_GLOW_TRANSPARENCY: + case SID_ATTR_SOFTEDGE_RADIUS: + case SID_ATTR_TEXTCOLUMNS_NUMBER: + case SID_ATTR_TEXTCOLUMNS_SPACING: + if (const SfxItemSet* pNewArgs = rReq.GetArgs()) + mpDrawView->SetAttributes(*pNewArgs); + rReq.Done(); + Cancel(); + break; + + default: + { + SAL_WARN( "sd.ui", "Slot without function" ); + Cancel(); + rReq.Ignore (); + } + break; + } + + if(HasCurrentFunction()) + { + GetCurrentFunction()->Activate(); + } +} + +void DrawViewShell::ExecChar( SfxRequest &rReq ) +{ + SdDrawDocument* pDoc = GetDoc(); + if (!pDoc || !mpDrawView) + return; + + SfxItemSet aEditAttr( pDoc->GetPool() ); + mpDrawView->GetAttributes( aEditAttr ); + + //modified by wj for sym2_1580, if put old itemset into new set, + //when mpDrawView->SetAttributes(aNewAttr) it will invalidate all the item + // and use old attr to update all the attributes +// SfxItemSet aNewAttr( GetPool(), +// EE_ITEMS_START, EE_ITEMS_END ); +// aNewAttr.Put( aEditAttr, sal_False ); + SfxItemSet aNewAttr( pDoc->GetPool() ); + //modified end + + sal_uInt16 nSId = rReq.GetSlot(); + + switch ( nSId ) + { + case SID_ATTR_CHAR_FONT: + if( rReq.GetArgs() ) + { + const SvxFontItem* pItem = rReq.GetArg(SID_ATTR_CHAR_FONT); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_FONTHEIGHT: + if( rReq.GetArgs() ) + { + const SvxFontHeightItem* pItem = rReq.GetArg(SID_ATTR_CHAR_FONTHEIGHT); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_WEIGHT: + if( rReq.GetArgs() ) + { + const SvxWeightItem* pItem = rReq.GetArg(SID_ATTR_CHAR_WEIGHT); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_POSTURE: + if( rReq.GetArgs() ) + { + const SvxPostureItem* pItem = rReq.GetArg(SID_ATTR_CHAR_POSTURE); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_UNDERLINE: + if( rReq.GetArgs() ) + { + const SvxUnderlineItem* pItem = rReq.GetArg(SID_ATTR_CHAR_UNDERLINE); + if (pItem) + { + aNewAttr.Put(*pItem); + } + else + { + FontLineStyle eFU = aEditAttr.Get( EE_CHAR_UNDERLINE ).GetLineStyle(); + aNewAttr.Put( SvxUnderlineItem( eFU != LINESTYLE_NONE ?LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_UNDERLINE ) ); + } + } + break; + case SID_ATTR_CHAR_OVERLINE: + if( rReq.GetArgs() ) + { + const SvxOverlineItem* pItem = rReq.GetArg(SID_ATTR_CHAR_OVERLINE); + if (pItem) + { + aNewAttr.Put(*pItem); + } + else + { + FontLineStyle eFU = aEditAttr.Get( EE_CHAR_OVERLINE ).GetLineStyle(); + aNewAttr.Put( SvxOverlineItem( eFU != LINESTYLE_NONE ?LINESTYLE_NONE : LINESTYLE_SINGLE, EE_CHAR_OVERLINE ) ); + } + } + break; + + case SID_ULINE_VAL_NONE: + { + aNewAttr.Put(SvxUnderlineItem(LINESTYLE_NONE, EE_CHAR_UNDERLINE)); + break; + } + + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + FontLineStyle eOld = aEditAttr.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + FontLineStyle eNew = eOld; + + switch (nSId) + { + case SID_ULINE_VAL_SINGLE: + eNew = ( eOld == LINESTYLE_SINGLE ) ? LINESTYLE_NONE : LINESTYLE_SINGLE; + break; + case SID_ULINE_VAL_DOUBLE: + eNew = ( eOld == LINESTYLE_DOUBLE ) ? LINESTYLE_NONE : LINESTYLE_DOUBLE; + break; + case SID_ULINE_VAL_DOTTED: + eNew = ( eOld == LINESTYLE_DOTTED ) ? LINESTYLE_NONE : LINESTYLE_DOTTED; + break; + } + + SvxUnderlineItem aUnderline(eNew, EE_CHAR_UNDERLINE); + aNewAttr.Put(aUnderline); + } + break; + + case SID_ATTR_CHAR_SHADOWED: + if( rReq.GetArgs() ) + { + const SvxShadowedItem* pItem = rReq.GetArg(SID_ATTR_CHAR_SHADOWED); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_CONTOUR: + if( rReq.GetArgs() ) + { + const SvxContourItem* pItem = rReq.GetArg(SID_ATTR_CHAR_CONTOUR); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + + case SID_ATTR_CHAR_STRIKEOUT: + if( rReq.GetArgs() ) + { + const SvxCrossedOutItem* pItem = rReq.GetArg(SID_ATTR_CHAR_STRIKEOUT); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_COLOR: + if( rReq.GetArgs() ) + { + const SvxColorItem* pItem = rReq.GetArg(SID_ATTR_CHAR_COLOR); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_KERNING: + if( rReq.GetArgs() ) + { + const SvxKerningItem* pItem = rReq.GetArg(SID_ATTR_CHAR_KERNING); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_ATTR_CHAR_CASEMAP: + if( rReq.GetArgs() ) + { + const SvxCaseMapItem* pItem = rReq.GetArg(SID_ATTR_CHAR_CASEMAP); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + case SID_SET_SUB_SCRIPT: + { + SvxEscapementItem aItem( EE_CHAR_ESCAPEMENT ); + SvxEscapement eEsc = static_cast(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + if( eEsc == SvxEscapement::Subscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Subscript ); + aNewAttr.Put( aItem ); + } + break; + case SID_SET_SUPER_SCRIPT: + { + SvxEscapementItem aItem( EE_CHAR_ESCAPEMENT ); + SvxEscapement eEsc = static_cast(aEditAttr.Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + if( eEsc == SvxEscapement::Superscript ) + aItem.SetEscapement( SvxEscapement::Off ); + else + aItem.SetEscapement( SvxEscapement::Superscript ); + aNewAttr.Put( aItem ); + } + break; + case SID_SHRINK_FONT_SIZE: + case SID_GROW_FONT_SIZE: + { + const SvxFontListItem* pFonts = dynamic_cast(GetDocSh()->GetItem( SID_ATTR_CHAR_FONTLIST ) ); + const FontList* pFontList = pFonts ? pFonts->GetFontList() : nullptr; + if( pFontList ) + { + FuText::ChangeFontSize( nSId == SID_GROW_FONT_SIZE, nullptr, pFontList, mpView ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + break; + } + case SID_ATTR_CHAR_BACK_COLOR: + if( rReq.GetArgs() ) + { + const SvxColorItem* pItem = rReq.GetArg(SID_ATTR_CHAR_BACK_COLOR); + if (pItem) + { + aNewAttr.Put(*pItem); + } + } + break; + default: + break; + } + + mpDrawView->SetAttributes(aNewAttr); + rReq.Done(); + Cancel(); +} + +/** This method consists basically of three parts: + 1. Process the arguments of the SFX request. + 2. Use the model to create a new page or duplicate an existing one. + 3. Update the tab control and switch to the new page. +*/ +SdPage* DrawViewShell::CreateOrDuplicatePage ( + SfxRequest& rRequest, + PageKind ePageKind, + SdPage* pPage, + const sal_Int32 nInsertPosition) +{ + SdPage* pNewPage = nullptr; + if (ePageKind == PageKind::Standard && meEditMode != EditMode::MasterPage) + { + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + pNewPage = ViewShell::CreateOrDuplicatePage (rRequest, ePageKind, pPage, nInsertPosition); + } + return pNewPage; +} + +void DrawViewShell::DuplicateSelectedSlides (SfxRequest& rRequest) +{ + // Create a list of the pages that are to be duplicated. The process of + // duplication alters the selection. + sal_Int32 nInsertPosition (0); + ::std::vector aPagesToDuplicate; + sd::slidesorter::SlideSorter &mrSlideSorter = sd::slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase())->GetSlideSorter(); + sd::slidesorter::model::PageEnumeration aSelectedPages ( + sd::slidesorter::model::PageEnumerationProvider::CreateSelectedPagesEnumeration(mrSlideSorter.GetModel())); + while (aSelectedPages.HasMoreElements()) + { + sd::slidesorter::model::SharedPageDescriptor pDescriptor (aSelectedPages.GetNextElement()); + if (pDescriptor && pDescriptor->GetPage()) + { + aPagesToDuplicate.push_back(pDescriptor->GetPage()); + nInsertPosition = pDescriptor->GetPage()->GetPageNum()+2; + } + } + + // Duplicate the pages in aPagesToDuplicate and collect the newly + // created pages in aPagesToSelect. + const bool bUndo (aPagesToDuplicate.size()>1 && mrSlideSorter.GetView().IsUndoEnabled()); + if (bUndo) + mrSlideSorter.GetView().BegUndo(SdResId(STR_INSERTPAGE)); + + ::std::vector aPagesToSelect; + for(::std::vector::const_iterator + iPage(aPagesToDuplicate.begin()), + iEnd(aPagesToDuplicate.end()); + iPage!=iEnd; + ++iPage, nInsertPosition+=2) + { + aPagesToSelect.push_back( + mrSlideSorter.GetViewShell()->CreateOrDuplicatePage( + rRequest, PageKind::Standard, *iPage, nInsertPosition)); + } + aPagesToDuplicate.clear(); + + if (bUndo) + mrSlideSorter.GetView().EndUndo(); + + // Set the selection to the pages in aPagesToSelect. + sd::slidesorter::controller::PageSelector& rSelector (mrSlideSorter.GetController().GetPageSelector()); + rSelector.DeselectAllPages(); + for (auto const& it: aPagesToSelect) + { + rSelector.SelectPage(it); + } +} + +void DrawViewShell::ExecutePropPanelAttr (SfxRequest const & rReq) +{ + if(SlideShow::IsRunning( GetViewShellBase() )) + return; + + SdDrawDocument* pDoc = GetDoc(); + if (!pDoc || !mpDrawView) + return; + + sal_uInt16 nSId = rReq.GetSlot(); + SfxItemSet aAttrs( pDoc->GetPool() ); + + switch ( nSId ) + { + case SID_TABLE_VERT_NONE: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_BOTTOM: + SdrTextVertAdjust eTVA = SDRTEXTVERTADJUST_TOP; + if (nSId == SID_TABLE_VERT_CENTER) + eTVA = SDRTEXTVERTADJUST_CENTER; + else if (nSId == SID_TABLE_VERT_BOTTOM) + eTVA = SDRTEXTVERTADJUST_BOTTOM; + + aAttrs.Put( SdrTextVertAdjustItem(eTVA) ); + mpDrawView->SetAttributes(aAttrs); + + break; + } +} + +void DrawViewShell::GetStatePropPanelAttr(SfxItemSet& rSet) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + SdDrawDocument* pDoc = GetDoc(); + if (!pDoc || !mpDrawView) + return; + + SfxItemSet aAttrs( pDoc->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + switch ( nSlotId ) + { + case SID_TABLE_VERT_NONE: + case SID_TABLE_VERT_CENTER: + case SID_TABLE_VERT_BOTTOM: + bool bContour = false; + SfxItemState eConState = aAttrs.GetItemState( SDRATTR_TEXT_CONTOURFRAME ); + if( eConState != SfxItemState::DONTCARE ) + { + bContour = aAttrs.Get( SDRATTR_TEXT_CONTOURFRAME ).GetValue(); + } + if (bContour) break; + + SfxItemState eVState = aAttrs.GetItemState( SDRATTR_TEXT_VERTADJUST ); + //SfxItemState eHState = aAttrs.GetItemState( SDRATTR_TEXT_HORZADJUST ); + + //if(SfxItemState::DONTCARE != eVState && SfxItemState::DONTCARE != eHState) + if(SfxItemState::DONTCARE != eVState) + { + SdrTextVertAdjust eTVA = aAttrs.Get(SDRATTR_TEXT_VERTADJUST).GetValue(); + bool bSet = (nSlotId == SID_TABLE_VERT_NONE && eTVA == SDRTEXTVERTADJUST_TOP) || + (nSlotId == SID_TABLE_VERT_CENTER && eTVA == SDRTEXTVERTADJUST_CENTER) || + (nSlotId == SID_TABLE_VERT_BOTTOM && eTVA == SDRTEXTVERTADJUST_BOTTOM); + rSet.Put(SfxBoolItem(nSlotId, bSet)); + } + else + { + rSet.Put(SfxBoolItem(nSlotId, false)); + } + break; + } + nWhich = aIter.NextWhich(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews3.cxx b/sd/source/ui/view/drviews3.cxx new file mode 100644 index 000000000..3f16136ff --- /dev/null +++ b/sd/source/ui/view/drviews3.cxx @@ -0,0 +1,1106 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing::framework; +using ::com::sun::star::frame::XFrame; +using ::com::sun::star::frame::XController; + +namespace sd { + +/** + * handle SfxRequests for controller + */ +void DrawViewShell::ExecCtrl(SfxRequest& rReq) +{ + // except a page switch and jumps to bookmarks, nothing is executed during + // a slide show + if( HasCurrentFunction(SID_PRESENTATION) && + rReq.GetSlot() != SID_SWITCHPAGE && + rReq.GetSlot() != SID_JUMPTOMARK) + return; + + CheckLineTo (rReq); + + // End text edit mode for some requests. + sal_uInt16 nSlot = rReq.GetSlot(); + bool bAllowFocusChange = true; + switch (nSlot) + { + case SID_OUTPUT_QUALITY_COLOR: + case SID_OUTPUT_QUALITY_GRAYSCALE: + case SID_OUTPUT_QUALITY_BLACKWHITE: + case SID_OUTPUT_QUALITY_CONTRAST: + // Do nothing. + break; + case SID_SWITCHPAGE: + if (rReq.GetArgs() && rReq.GetArgs()->Count () == 1) + { + const SfxBoolItem* pAllowFocusChange = rReq.GetArg(SID_SWITCHPAGE); + bAllowFocusChange = pAllowFocusChange->GetValue(); + if (!bAllowFocusChange) + break; + } + [[fallthrough]]; + default: + if ( mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + } + + // sal_uInt16 nSlot = rReq.GetSlot(); + switch (nSlot) + { + case SID_SWITCHPAGE: // BASIC + { + // switch page in running slide show + if(SlideShow::IsRunning(GetViewShellBase()) && rReq.GetArgs()) + { + if (const SfxUInt32Item* pWhatPage = rReq.GetArg(ID_VAL_WHATPAGE)) + SlideShow::GetSlideShow(GetViewShellBase())->jumpToPageNumber(static_cast((pWhatPage->GetValue()-1)>>1)); + } + else + { + const SfxItemSet *pArgs = rReq.GetArgs (); + sal_uInt16 nSelectedPage = 0; + + if (! pArgs || pArgs->Count () == 1) + { + nSelectedPage = maTabControl->GetCurPagePos(); + } + else if (pArgs->Count () == 2) + { + const SfxUInt32Item* pWhatPage = rReq.GetArg(ID_VAL_WHATPAGE); + const SfxUInt32Item* pWhatKind = rReq.GetArg(ID_VAL_WHATKIND); + + sal_Int32 nWhatPage = static_cast(pWhatPage->GetValue ()); + PageKind nWhatKind = static_cast(pWhatKind->GetValue ()); + if (nWhatKind < PageKind::Standard || nWhatKind > PageKind::Handout) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + rReq.Ignore (); + break; + } + else if (meEditMode != EditMode::MasterPage) + { + if (! CHECK_RANGE (0, nWhatPage, GetDoc()->GetSdPageCount(nWhatKind))) + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + rReq.Ignore (); + break; + } + + nSelectedPage = static_cast(nWhatPage); + mePageKind = nWhatKind; + } + } + else + { +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rReq.Ignore (); + break; + } + + if( GetDocSh() && (GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED)) + GetDocSh()->SetModified(); + + SwitchPage(nSelectedPage, bAllowFocusChange); + + if(HasCurrentFunction(SID_BEZIER_EDIT)) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + Invalidate(); + InvalidateWindows(); + rReq.Done (); + } + break; + } + + case SID_SWITCHLAYER: // BASIC + { + const SfxItemSet *pArgs = rReq.GetArgs (); + + // #i87182# + bool bCurPageValid(false); + sal_uInt16 nCurPage(0); + + if(GetLayerTabControl()) + { + nCurPage = GetLayerTabControl()->GetCurPageId(); + bCurPageValid = true; + } + + if(pArgs && 1 == pArgs->Count()) + { + const SfxUInt32Item* pWhatLayer = rReq.GetArg(ID_VAL_WHATLAYER); + + if(pWhatLayer) + { + nCurPage = static_cast(pWhatLayer->GetValue()); + bCurPageValid = true; + } + } + + if(bCurPageValid) + { + OUString aLayerName( GetLayerTabControl()->GetLayerName(nCurPage)); + if (!aLayerName.isEmpty()) + { + mpDrawView->SetActiveLayer(aLayerName); + } + Invalidate(); + } + + rReq.Done (); + + break; + } + + case SID_PAGEMODE: // BASIC + { + + const SfxItemSet *pArgs = rReq.GetArgs(); + + if (pArgs && pArgs->Count () == 2) + { + const SfxBoolItem* pIsActive = rReq.GetArg(ID_VAL_ISACTIVE); + const SfxUInt32Item* pWhatKind = rReq.GetArg(ID_VAL_WHATKIND); + + PageKind nWhatKind = static_cast(pWhatKind->GetValue()); + if ( nWhatKind >= PageKind::Standard && nWhatKind <= PageKind::Handout) + { + mbIsLayerModeActive = pIsActive->GetValue(); + mePageKind = nWhatKind; + } + } + + // turn on default layer of page + mpDrawView->SetActiveLayer(sUNO_LayerName_layout); + + ChangeEditMode(EditMode::Page, mbIsLayerModeActive); + + Invalidate(); + rReq.Done (); + + break; + } + + case SID_LAYERMODE: // BASIC + { + const SfxItemSet *pArgs = rReq.GetArgs(); + + if (pArgs && pArgs->Count() == 2) + { + const SfxUInt32Item* pWhatLayer = rReq.GetArg(ID_VAL_WHATLAYER); + EditMode nWhatLayer = static_cast(pWhatLayer->GetValue()); + if (nWhatLayer == EditMode::Page || nWhatLayer == EditMode::MasterPage) + { + mbIsLayerModeActive = rReq.GetArg(ID_VAL_ISACTIVE)->GetValue(); + meEditMode = nWhatLayer; + } + } + + ChangeEditMode(meEditMode, !mbIsLayerModeActive); + + Invalidate(); + rReq.Done(); + + break; + } + + case SID_HEADER_AND_FOOTER: + case SID_INSERT_PAGE_NUMBER: + case SID_INSERT_DATE_TIME: + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + VclPtr pDlg(pFact->CreateHeaderFooterDialog(this, pWin ? pWin->GetFrameWeld() : nullptr, GetDoc(), mpActualPage)); + auto xRequest = std::make_shared(rReq); + rReq.Ignore(); // the 'old' request is not relevant any more + pDlg->StartExecuteAsync([this, pDlg, xRequest](sal_Int32 /*nResult*/){ + GetActiveWindow()->Invalidate(); + UpdatePreview( mpActualPage ); + + Invalidate(); + xRequest->Done(); + + pDlg->disposeOnce(); + }); + break; + } + + case SID_MASTER_LAYOUTS: + { + SdPage* pPage = GetActualPage(); + if (meEditMode == EditMode::MasterPage) + // Use the master page of the current page. + pPage = static_cast(&pPage->TRG_GetMasterPage()); + + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr pDlg(pFact->CreateMasterLayoutDialog(pWin ? pWin->GetFrameWeld() : nullptr, GetDoc(), pPage)); + pDlg->Execute(); + Invalidate(); + rReq.Done (); + break; + } + case SID_OBJECTRESIZE: + { + // The server likes to change the client size + OSL_ASSERT (GetViewShell()!=nullptr); + SfxInPlaceClient* pIPClient = GetViewShell()->GetIPClient(); + + if ( pIPClient && pIPClient->IsObjectInPlaceActive() ) + { + const SfxRectangleItem& rRect = + rReq.GetArgs()->Get(SID_OBJECTRESIZE); + ::tools::Rectangle aRect( GetActiveWindow()->PixelToLogic( rRect.GetValue() ) ); + + if ( mpDrawView->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + SdrOle2Obj* pOle2Obj = dynamic_cast< SdrOle2Obj* >( pObj ); + if(pOle2Obj) + { + if( pOle2Obj->GetObjRef().is() ) + { + pOle2Obj->SetLogicRect(aRect); + } + } + } + } + } + rReq.Ignore (); + break; + } + + case SID_RELOAD: + { + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxViewFrame* pFrame = GetViewFrame(); + + try + { + Reference< XFrame > xFrame( pFrame->GetFrame().GetFrameInterface(), UNO_SET_THROW ); + + // Save the current configuration of panes and views. + Reference xControllerManager ( + GetViewShellBase().GetController(), UNO_QUERY_THROW); + Reference xConfigurationController ( + xControllerManager->getConfigurationController(), UNO_SET_THROW ); + Reference xConfiguration ( + xConfigurationController->getRequestedConfiguration(), UNO_SET_THROW ); + + SfxChildWindow* pWindow = pFrame->GetChildWindow(nId); + if(pWindow) + { + Svx3DWin* p3DWin = static_cast(pWindow->GetWindow()); + if(p3DWin) + p3DWin->DocumentReload(); + } + + // normal forwarding to ViewFrame for execution + GetViewFrame()->ExecuteSlot(rReq); + + // From here on we must cope with this object and the frame already being + // deleted. Do not call any methods or use data members. + Reference xController( xFrame->getController(), UNO_SET_THROW ); + + // Restore the configuration. + xControllerManager.set( xController, UNO_QUERY_THROW ); + xConfigurationController.set( xControllerManager->getConfigurationController() ); + if ( ! xConfigurationController.is()) + throw RuntimeException(); + xConfigurationController->restoreConfiguration(xConfiguration); + } + catch (RuntimeException&) + { + DBG_UNHANDLED_EXCEPTION("sd.view"); + } + + // We have to return immediately to avoid accessing this object. + return; + } + + case SID_JUMPTOMARK: + { + if( rReq.GetArgs() ) + { + const SfxStringItem* pBookmark = rReq.GetArg(SID_JUMPTOMARK); + + if (pBookmark) + { + OUString sBookmark(INetURLObject::decode(pBookmark->GetValue(), INetURLObject::DecodeMechanism::WithCharset)); + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if(xSlideshow.is() && xSlideshow->isRunning()) + { + xSlideshow->jumpToBookmark(sBookmark); + } + else + { + GotoBookmark(sBookmark); + } + } + } + rReq.Done(); + break; + } + + case SID_OUTPUT_QUALITY_COLOR: + case SID_OUTPUT_QUALITY_GRAYSCALE: + case SID_OUTPUT_QUALITY_BLACKWHITE: + case SID_OUTPUT_QUALITY_CONTRAST: + { + ExecReq( rReq ); + break; + } + + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + ExecReq( rReq ); + break; + } + + case SID_ATTR_YEAR2000: + { + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + { + const SfxPoolItem* pItem; + if (rReq.GetArgs()->GetItemState( + SID_ATTR_YEAR2000, true, &pItem) == SfxItemState::SET) + pFormShell->SetY2KState ( + static_cast(pItem)->GetValue()); + } + + rReq.Done(); + } + break; + + case SID_OPT_LOCALE_CHANGED: + { + GetActiveWindow()->Invalidate(); + UpdatePreview( mpActualPage ); + rReq.Done(); + } + break; + + case SID_REGENERATE_DIAGRAM: + case SID_EDIT_DIAGRAM: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (1 == rMarkList.GetMarkCount()) + { + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + // Support advanced DiagramHelper + if(nullptr != pObj && pObj->isDiagram()) + { + if(SID_REGENERATE_DIAGRAM == nSlot) + { + mpDrawView->UnmarkAll(); + pObj->getDiagramHelper()->reLayout(*static_cast(pObj)); + mpDrawView->MarkObj(pObj, mpDrawView->GetSdrPageView()); + } + else // SID_EDIT_DIAGRAM + { + VclAbstractDialogFactory* pFact = VclAbstractDialogFactory::Create(); + ScopedVclPtr pDlg = pFact->CreateDiagramDialog( + GetFrameWeld(), + *static_cast(pObj)); + pDlg->Execute(); + } + } + } + + rReq.Done(); + } + break; + + default: + break; + } +} + +void DrawViewShell::ExecRuler(SfxRequest& rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + const SfxItemSet* pArgs = rReq.GetArgs(); + const Point aPagePos( GetActiveWindow()->GetViewOrigin() ); + Size aPageSize = mpActualPage->GetSize(); + Size aViewSize = GetActiveWindow()->GetViewSize(); + + switch ( rReq.GetSlot() ) + { + case SID_ATTR_LONG_LRSPACE: + if (pArgs) + { + std::unique_ptr pUndoGroup(new SdUndoGroup(GetDoc())); + pUndoGroup->SetComment(SdResId(STR_UNDO_CHANGE_PAGEBORDER)); + + const SvxLongLRSpaceItem& rLRSpace = + pArgs->Get(SID_ATTR_LONG_LRSPACE); + + if( mpDrawView->IsTextEdit() ) + { + ::tools::Rectangle aRect = maMarkRect; + aRect.SetPos(aRect.TopLeft() + aPagePos); + aRect.SetLeft( rLRSpace.GetLeft() ); + aRect.SetRight( aViewSize.Width() - rLRSpace.GetRight() ); + aRect.SetPos(aRect.TopLeft() - aPagePos); + if ( aRect != maMarkRect) + { + mpDrawView->SetAllMarkedRect(aRect); + maMarkRect = mpDrawView->GetAllMarkedRect(); + Invalidate( SID_RULER_OBJECT ); + } + } + else + { + ::tools::Long nLeft = std::max(::tools::Long(0), rLRSpace.GetLeft() - aPagePos.X()); + ::tools::Long nRight = std::max(::tools::Long(0), rLRSpace.GetRight() + aPagePos.X() + + aPageSize.Width() - aViewSize.Width()); + + sal_uInt16 nPageCnt = GetDoc()->GetSdPageCount(mePageKind); + sal_uInt16 i; + for ( i = 0; i < nPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + SdUndoAction* pUndo = new SdPageLRUndoAction(GetDoc(), + pPage, + pPage->GetLeftBorder(), + pPage->GetRightBorder(), + nLeft, nRight); + pUndoGroup->AddAction(pUndo); + pPage->SetLeftBorder(nLeft); + pPage->SetRightBorder(nRight); + } + nPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetMasterSdPage(i, mePageKind); + SdUndoAction* pUndo = new SdPageLRUndoAction(GetDoc(), + pPage, + pPage->GetLeftBorder(), + pPage->GetRightBorder(), + nLeft, nRight); + pUndoGroup->AddAction(pUndo); + pPage->SetLeftBorder(nLeft); + pPage->SetRightBorder(nRight); + } + InvalidateWindows(); + } + + // give the undo group to the undo manager + GetViewFrame()->GetObjectShell()->GetUndoManager()-> + AddUndoAction(std::move(pUndoGroup)); + } + break; + case SID_ATTR_LONG_ULSPACE: + if (pArgs) + { + std::unique_ptr pUndoGroup(new SdUndoGroup(GetDoc())); + pUndoGroup->SetComment(SdResId(STR_UNDO_CHANGE_PAGEBORDER)); + + const SvxLongULSpaceItem& rULSpace = + pArgs->Get(SID_ATTR_LONG_ULSPACE); + + if( mpDrawView->IsTextEdit() ) + { + ::tools::Rectangle aRect = maMarkRect; + aRect.SetPos(aRect.TopLeft() + aPagePos); + aRect.SetTop( rULSpace.GetUpper() ); + aRect.SetBottom( aViewSize.Height() - rULSpace.GetLower() ); + aRect.SetPos(aRect.TopLeft() - aPagePos); + + if ( aRect != maMarkRect) + { + mpDrawView->SetAllMarkedRect(aRect); + maMarkRect = mpDrawView->GetAllMarkedRect(); + Invalidate( SID_RULER_OBJECT ); + } + } + else + { + ::tools::Long nUpper = std::max(::tools::Long(0), rULSpace.GetUpper() - aPagePos.Y()); + ::tools::Long nLower = std::max(::tools::Long(0), rULSpace.GetLower() + aPagePos.Y() + + aPageSize.Height() - aViewSize.Height()); + + sal_uInt16 nPageCnt = GetDoc()->GetSdPageCount(mePageKind); + sal_uInt16 i; + for ( i = 0; i < nPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + SdUndoAction* pUndo = new SdPageULUndoAction(GetDoc(), + pPage, + pPage->GetUpperBorder(), + pPage->GetLowerBorder(), + nUpper, nLower); + pUndoGroup->AddAction(pUndo); + pPage->SetUpperBorder(nUpper); + pPage->SetLowerBorder(nLower); + } + nPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + + for (i = 0; i < nPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetMasterSdPage(i, mePageKind); + SdUndoAction* pUndo = new SdPageULUndoAction(GetDoc(), + pPage, + pPage->GetUpperBorder(), + pPage->GetLowerBorder(), + nUpper, nLower); + pUndoGroup->AddAction(pUndo); + pPage->SetUpperBorder(nUpper); + pPage->SetLowerBorder(nLower); + } + InvalidateWindows(); + } + + // give the undo group to the undo manager + GetViewFrame()->GetObjectShell()->GetUndoManager()-> + AddUndoAction(std::move(pUndoGroup)); + } + break; + case SID_RULER_OBJECT: + if (pArgs) + { + ::tools::Rectangle aRect = maMarkRect; + aRect.SetPos(aRect.TopLeft() + aPagePos); + + const SvxObjectItem& rOI = pArgs->Get(SID_RULER_OBJECT); + + if ( rOI.GetStartX() != rOI.GetEndX() ) + { + aRect.SetLeft( rOI.GetStartX() ); + aRect.SetRight( rOI.GetEndX() ); + } + if ( rOI.GetStartY() != rOI.GetEndY() ) + { + aRect.SetTop( rOI.GetStartY() ); + aRect.SetBottom( rOI.GetEndY() ); + } + aRect.SetPos(aRect.TopLeft() - aPagePos); + if ( aRect != maMarkRect) + { + mpDrawView->SetAllMarkedRect(aRect); + maMarkRect = mpDrawView->GetAllMarkedRect(); + Invalidate( SID_RULER_OBJECT ); + } + } + break; + case SID_ATTR_TABSTOP: + if (pArgs && mpDrawView->IsTextEdit()) + { + const SvxTabStopItem& rItem = pArgs->Get( EE_PARA_TABS ); + + SfxItemSetFixed aEditAttr( GetPool() ); + + aEditAttr.Put( rItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_TABSTOP); + } + break; + case SID_ATTR_PARA_LINESPACE: + if (pArgs) + { + SvxLineSpacingItem aParaLineSP = pArgs->Get( + GetPool().GetWhich(SID_ATTR_PARA_LINESPACE)); + + SfxItemSetFixed aEditAttr( GetPool() ); + aParaLineSP.SetWhich( EE_PARA_SBL ); + + aEditAttr.Put( aParaLineSP ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_LINESPACE); + } + break; + case SID_ATTR_PARA_ADJUST_LEFT: + { + SvxAdjustItem aItem( SvxAdjust::Left, EE_PARA_JUST ); + SfxItemSetFixed aEditAttr( GetPool() ); + + aEditAttr.Put( aItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ADJUST_LEFT); + break; + } + case SID_ATTR_PARA_ADJUST_CENTER: + { + SvxAdjustItem aItem( SvxAdjust::Center, EE_PARA_JUST ); + SfxItemSetFixed aEditAttr( GetPool() ); + + aEditAttr.Put( aItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ADJUST_CENTER); + break; + } + case SID_ATTR_PARA_ADJUST_RIGHT: + { + SvxAdjustItem aItem( SvxAdjust::Right, EE_PARA_JUST ); + SfxItemSetFixed aEditAttr( GetPool() ); + + aEditAttr.Put( aItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ADJUST_RIGHT); + break; + } + case SID_ATTR_PARA_ADJUST_BLOCK: + { + SvxAdjustItem aItem( SvxAdjust::Block, EE_PARA_JUST ); + SfxItemSetFixed aEditAttr( GetPool() ); + + aEditAttr.Put( aItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ADJUST_BLOCK); + break; + } + case SID_ATTR_PARA_ULSPACE: + if (pArgs) + { + SvxULSpaceItem aULSP = static_cast(pArgs->Get( + SID_ATTR_PARA_ULSPACE)); + SfxItemSetFixed aEditAttr( GetPool() ); + aULSP.SetWhich( EE_PARA_ULSPACE ); + + aEditAttr.Put( aULSP ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_ULSPACE); + } + break; + case SID_ATTR_PARA_LRSPACE: + if (pArgs) + { + SvxLRSpaceItem aLRSpace = static_cast(pArgs->Get( + SID_ATTR_PARA_LRSPACE)); + + SfxItemSetFixed aEditAttr( GetPool() ); + aLRSpace.SetWhich( EE_PARA_LRSPACE ); + + aEditAttr.Put( aLRSpace ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + case SID_ATTR_LRSPACE: + if (pArgs && mpDrawView->IsTextEdit()) + { + sal_uInt16 nId = SID_ATTR_PARA_LRSPACE; + const SvxLRSpaceItem& rItem = static_cast( + pArgs->Get( nId )); + + SfxItemSetFixed< + EE_PARA_NUMBULLET, EE_PARA_NUMBULLET, + EE_PARA_OUTLLEVEL, EE_PARA_OUTLLEVEL, + EE_PARA_LRSPACE, EE_PARA_LRSPACE> aEditAttr( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aEditAttr ); + + nId = EE_PARA_LRSPACE; + SvxLRSpaceItem aLRSpaceItem( rItem.GetLeft(), + rItem.GetRight(), rItem.GetTextLeft(), + rItem.GetTextFirstLineOffset(), nId ); + + const sal_Int16 nOutlineLevel = aEditAttr.Get( EE_PARA_OUTLLEVEL ).GetValue(); + const SvxLRSpaceItem& rOrigLRSpaceItem = aEditAttr.Get( EE_PARA_LRSPACE ); + const SvxNumBulletItem& rNumBulletItem = aEditAttr.Get( EE_PARA_NUMBULLET ); + if( nOutlineLevel != -1 && + rNumBulletItem.GetNumRule().GetLevelCount() > nOutlineLevel ) + { + const SvxNumberFormat& rFormat = rNumBulletItem.GetNumRule().GetLevel(nOutlineLevel); + SvxNumberFormat aFormat(rFormat); + + // left margin gets distributed onto LRSpace item + // and number format AbsLSpace - this fixes + // n#707779 (previously, LRSpace left indent could + // become negative - EditEngine really does not + // like that. + const auto nAbsLSpace=aFormat.GetAbsLSpace(); + const ::tools::Long nTxtLeft=rItem.GetTextLeft(); + const ::tools::Long nLeftIndent=std::max(::tools::Long(0),nTxtLeft - nAbsLSpace); + aLRSpaceItem.SetTextLeft(nLeftIndent); + // control for clipped left indent - remainder + // reduces number format first line indent + aFormat.SetAbsLSpace(nTxtLeft - nLeftIndent); + + // negative first line indent goes to the number + // format, positive to the lrSpace item + if( rItem.GetTextFirstLineOffset() < 0 ) + { + aFormat.SetFirstLineOffset( + rItem.GetTextFirstLineOffset() + - rOrigLRSpaceItem.GetTextFirstLineOffset() + + aFormat.GetCharTextDistance()); + aLRSpaceItem.SetTextFirstLineOffset(0); + } + else + { + aFormat.SetFirstLineOffset(0); + aLRSpaceItem.SetTextFirstLineOffset( + rItem.GetTextFirstLineOffset() + - aFormat.GetFirstLineOffset() //TODO: overflow + + aFormat.GetCharTextDistance()); + } + + if( rFormat != aFormat ) + { + // put all items + const_cast(rNumBulletItem.GetNumRule()).SetLevel(nOutlineLevel,aFormat); + aEditAttr.Put( rNumBulletItem ); + aEditAttr.Put( aLRSpaceItem ); + mpDrawView->SetAttributes( aEditAttr ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + break; + } + } + + // only put lrSpace item + SfxItemSetFixed aEditAttrReduced( GetDoc()->GetPool() ); + aEditAttrReduced.Put( aLRSpaceItem ); + mpDrawView->SetAttributes( aEditAttrReduced ); + + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + } +} + +void DrawViewShell::GetRulerState(SfxItemSet& rSet) +{ + Point aOrigin; + + if (mpDrawView->GetSdrPageView()) + { + aOrigin = mpDrawView->GetSdrPageView()->GetPageOrigin(); + } + + Size aViewSize = GetActiveWindow()->GetViewSize(); + + const Point aPagePos( GetActiveWindow()->GetViewOrigin() ); + Size aPageSize = mpActualPage->GetSize(); + + ::tools::Rectangle aRect(aPagePos, Point( aViewSize.Width() - (aPagePos.X() + aPageSize.Width()), + aViewSize.Height() - (aPagePos.Y() + aPageSize.Height()))); + + if( mpDrawView->IsTextEdit() ) + { + Point aPnt1 = GetActiveWindow()->GetWinViewPos(); + ::tools::Rectangle aMinMaxRect( aPnt1, Size(-1, -1) ); + rSet.Put( SfxRectangleItem(SID_RULER_LR_MIN_MAX, aMinMaxRect) ); + } + else + { + rSet.Put( SfxRectangleItem(SID_RULER_LR_MIN_MAX, aRect) ); + } + + SvxLongLRSpaceItem aLRSpace(aPagePos.X() + mpActualPage->GetLeftBorder(), + aRect.Right() + mpActualPage->GetRightBorder(), + SID_ATTR_LONG_LRSPACE); + SvxLongULSpaceItem aULSpace(aPagePos.Y() + mpActualPage->GetUpperBorder(), + aRect.Bottom() + mpActualPage->GetLowerBorder(), + SID_ATTR_LONG_ULSPACE); + rSet.Put(SvxPagePosSizeItem(Point(0,0) - aPagePos, aViewSize.Width(), + aViewSize.Height())); + SfxPointItem aPointItem( SID_RULER_NULL_OFFSET, aPagePos + aOrigin ); + + SvxProtectItem aProtect( SID_RULER_PROTECT ); + + maMarkRect = mpDrawView->GetAllMarkedRect(); + + const bool bRTL = GetDoc() && GetDoc()->GetDefaultWritingMode() == css::text::WritingMode_RL_TB; + rSet.Put(SfxBoolItem(SID_RULER_TEXT_RIGHT_TO_LEFT, bRTL)); + + if( mpDrawView->AreObjectsMarked() ) + { + if( mpDrawView->IsTextEdit() ) + { + SdrObject* pObj = mpDrawView->GetMarkedObjectList().GetMark( 0 )->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default) + { + SfxItemSet aEditAttr( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aEditAttr ); + if( aEditAttr.GetItemState( EE_PARA_TABS ) >= SfxItemState::DEFAULT ) + { + const SvxTabStopItem& rItem = aEditAttr.Get( EE_PARA_TABS ); + rSet.Put( rItem ); + + const SvxLRSpaceItem& rLRSpaceItem = aEditAttr.Get( EE_PARA_LRSPACE ); + SvxLRSpaceItem aLRSpaceItem( rLRSpaceItem.GetLeft(), + rLRSpaceItem.GetRight(), rLRSpaceItem.GetTextLeft(), + rLRSpaceItem.GetTextFirstLineOffset(), SID_ATTR_PARA_LRSPACE ); + + const sal_Int16 nOutlineLevel = aEditAttr.Get( EE_PARA_OUTLLEVEL ).GetValue(); + const SvxNumBulletItem& rNumBulletItem = aEditAttr.Get( EE_PARA_NUMBULLET ); + if( nOutlineLevel != -1 && + rNumBulletItem.GetNumRule().GetLevelCount() > nOutlineLevel ) + { + const SvxNumberFormat& rFormat = rNumBulletItem.GetNumRule().GetLevel(nOutlineLevel); + aLRSpaceItem.SetTextLeft(rFormat.GetAbsLSpace() + rLRSpaceItem.GetTextLeft()); + aLRSpaceItem.SetTextFirstLineOffset( + rLRSpaceItem.GetTextFirstLineOffset() + rFormat.GetFirstLineOffset() + //TODO: overflow + - rFormat.GetCharTextDistance()); + } + + rSet.Put( aLRSpaceItem ); + + Point aPos( aPagePos + maMarkRect.TopLeft() ); + + if ( aEditAttr.GetItemState( SDRATTR_TEXT_LEFTDIST ) == SfxItemState::SET ) + { + const SdrMetricItem& rTLDItem = aEditAttr.Get( SDRATTR_TEXT_LEFTDIST ); + ::tools::Long nLD = rTLDItem.GetValue(); + aPos.AdjustX(nLD ); + } + + aPointItem.SetValue( aPos ); + + ::tools::Rectangle aParaRect(maMarkRect); + if (pObj->GetObjIdentifier() == SdrObjKind::Table) + { + sdr::table::SdrTableObj* pTable = static_cast(pObj); + sdr::table::CellPos cellpos; + pTable->getActiveCellPos(cellpos); + pTable->getCellBounds(cellpos, aParaRect); + } + + aLRSpace.SetLeft(aPagePos.X() + aParaRect.Left()); + + if ( aEditAttr.GetItemState( SDRATTR_TEXT_LEFTDIST ) == SfxItemState::SET ) + { + const SdrMetricItem& rTLDItem = aEditAttr.Get( SDRATTR_TEXT_LEFTDIST ); + ::tools::Long nLD = rTLDItem.GetValue(); + aLRSpace.SetLeft( aLRSpace.GetLeft() + nLD ); + } + + aLRSpace.SetRight(aRect.Right() + aPageSize.Width() - aParaRect.Right()); + + if ( aEditAttr.GetItemState( SDRATTR_TEXT_RIGHTDIST ) == SfxItemState::SET ) + { + const SdrMetricItem& rTRDItem = aEditAttr.Get( SDRATTR_TEXT_RIGHTDIST ); + ::tools::Long nRD = rTRDItem.GetValue(); + aLRSpace.SetRight( aLRSpace.GetRight() + nRD ); + } + + aULSpace.SetUpper( aPagePos.Y() + maMarkRect.Top() ); + aULSpace.SetLower( aRect.Bottom() + aPageSize.Height() - maMarkRect.Bottom() ); + + rSet.DisableItem( SID_RULER_OBJECT ); + + // lock page margins + aProtect.SetSizeProtect( true ); + aProtect.SetPosProtect( true ); + } + + if( aEditAttr.GetItemState( EE_PARA_WRITINGDIR ) >= SfxItemState::DEFAULT ) + { + const SvxFrameDirectionItem& rItem = aEditAttr.Get( EE_PARA_WRITINGDIR ); + rSet.Put(SfxBoolItem(SID_RULER_TEXT_RIGHT_TO_LEFT, rItem.GetValue() == SvxFrameDirection::Horizontal_RL_TB)); + } + } + } + else + { + rSet.DisableItem( EE_PARA_TABS ); + rSet.DisableItem( SID_RULER_TEXT_RIGHT_TO_LEFT ); + + if( mpDrawView->IsResizeAllowed(true) ) + { + ::tools::Rectangle aResizeRect( maMarkRect ); + + aResizeRect.SetPos(aResizeRect.TopLeft() + aPagePos); + SvxObjectItem aObjItem(aResizeRect.Left(), aResizeRect.Right(), + aResizeRect.Top(), aResizeRect.Bottom()); + rSet.Put(aObjItem); + rSet.DisableItem( EE_PARA_TABS ); + } + else + { + rSet.DisableItem( SID_RULER_OBJECT ); + } + } + } + else + { + rSet.DisableItem( SID_RULER_OBJECT ); + rSet.DisableItem( EE_PARA_TABS ); + } + + rSet.Put( aLRSpace ); + rSet.Put( aULSpace ); + + rSet.Put( aPointItem ); + rSet.Put( aProtect ); +} + +void DrawViewShell::ExecStatusBar(SfxRequest& rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + switch ( rReq.GetSlot() ) + { + case SID_ATTR_SIZE: + { + GetViewFrame()->GetDispatcher()->Execute( SID_ATTR_TRANSFORM, SfxCallMode::ASYNCHRON ); + } + break; + + case SID_STATUS_LAYOUT: + { + GetViewFrame()->GetDispatcher()->Execute( SID_PRESENTATION_LAYOUT, SfxCallMode::ASYNCHRON ); + } + break; + } +} + +/** + * set state of snap object entries in popup + */ +void DrawViewShell::GetSnapItemState( SfxItemSet &rSet ) +{ + SdrPageView* pPV; + Point aMPos = GetActiveWindow()->PixelToLogic(maMousePos); + sal_uInt16 nHitLog = static_cast(GetActiveWindow()->PixelToLogic( + Size(FuPoor::HITPIX,0)).Width()); + sal_uInt16 nHelpLine; + + if ( !mpDrawView->PickHelpLine(aMPos, nHitLog, *GetActiveWindow()->GetOutDev(), nHelpLine, pPV) ) + return; + + const SdrHelpLine& rHelpLine = (pPV->GetHelpLines())[nHelpLine]; + + if ( rHelpLine.GetKind() == SdrHelpLineKind::Point ) + { + rSet.Put( SfxStringItem( SID_SET_SNAPITEM, + SdResId( STR_POPUP_EDIT_SNAPPOINT)) ); + rSet.Put( SfxStringItem( SID_DELETE_SNAPITEM, + SdResId( STR_POPUP_DELETE_SNAPPOINT)) ); + } + else + { + rSet.Put( SfxStringItem( SID_SET_SNAPITEM, + SdResId( STR_POPUP_EDIT_SNAPLINE)) ); + rSet.Put( SfxStringItem( SID_DELETE_SNAPITEM, + SdResId( STR_POPUP_DELETE_SNAPLINE)) ); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews4.cxx b/sd/source/ui/view/drviews4.cxx new file mode 100644 index 000000000..df251880d --- /dev/null +++ b/sd/source/ui/view/drviews4.cxx @@ -0,0 +1,982 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace { + void EndTextEditOnPage(sal_uInt16 nPageId) + { + SfxViewShell* pShell = SfxViewShell::GetFirst(); + while (pShell) + { + ::sd::ViewShellBase* pBase = dynamic_cast<::sd::ViewShellBase*>(pShell); + if (pBase) + { + ::sd::ViewShell* pViewSh = pBase->GetMainViewShell().get(); + ::sd::DrawViewShell* pDrawSh = dynamic_cast<::sd::DrawViewShell*>(pViewSh); + if (pDrawSh && pDrawSh->GetDrawView() && pDrawSh->getCurrentPage()->getPageId() == nPageId) + pDrawSh->GetDrawView()->SdrEndTextEdit(); + } + + pShell = SfxViewShell::GetNext(*pShell); + } + } +} + +namespace sd { + +#define PIPETTE_RANGE 0 + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; + +void DrawViewShell::DeleteActualPage() +{ + mpDrawView->SdrEndTextEdit(); + + try + { + Reference xDrawPagesSupplier( GetDoc()->getUnoModel(), UNO_QUERY_THROW ); + Reference xPages( xDrawPagesSupplier->getDrawPages(), UNO_SET_THROW ); + sal_uInt16 nPageCount = GetDoc()->GetSdPageCount(mePageKind); + SdPage* pPage = nullptr; + std::vector> pagesToDelete; + + GetView()->BegUndo(SdResId(STR_UNDO_DELETEPAGES)); + + for (sal_uInt16 i = 0; i < nPageCount; i++) + { + pPage = GetDoc()->GetSdPage(i, mePageKind); + sal_uInt16 nPageIndex = maTabControl->GetPagePos(pPage->getPageId()); + + slidesorter::SlideSorterViewShell* pVShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + bool bUseSlideSorter = pVShell != nullptr; + + if((bUseSlideSorter && IsSelected(nPageIndex)) || (!bUseSlideSorter && pPage->IsSelected())) + { + EndTextEditOnPage(pPage->getPageId()); + Reference< XDrawPage > xPage( xPages->getByIndex( nPageIndex ), UNO_QUERY_THROW ); + pagesToDelete.push_back(xPage); + } + } + for (const auto &xPage: pagesToDelete) + { + xPages->remove(xPage); + } + + GetView()->EndUndo(); + } + catch( Exception& ) + { + TOOLS_WARN_EXCEPTION( "sd", "SelectionManager::DeleteSelectedMasterPages()"); + } +} + +void DrawViewShell::DeleteActualLayer() +{ + if(!GetLayerTabControl()) // #i87182# + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + return; + } + + SdrLayerAdmin& rAdmin = GetDoc()->GetLayerAdmin(); + sal_uInt16 nId = GetLayerTabControl()->GetCurPageId(); + const OUString& rName = GetLayerTabControl()->GetLayerName(nId); + if(LayerTabBar::IsRealNameOfStandardLayer(rName)) + { + assert(false && "Standard layer may not be deleted."); + return; + } + const OUString& rDisplayName(GetLayerTabControl()->GetPageText(nId)); + OUString aString(SdResId(STR_ASK_DELETE_LAYER)); + + // replace placeholder + aString = aString.replaceFirst("$", rDisplayName); + + std::unique_ptr xQueryBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + aString)); + if (xQueryBox->run() == RET_YES) + { + const SdrLayer* pLayer = rAdmin.GetLayer(rName); + mpDrawView->DeleteLayer( pLayer->GetName() ); + + /* in order to redraw TabBar and Window; should be initiated later on by + a hint from Joe (as by a change if the layer order). */ + // ( View::Notify() --> ViewShell::ResetActualLayer() ) + + mbIsLayerModeActive = false; // so that ChangeEditMode() does something + ChangeEditMode(GetEditMode(), true); + } +} + +bool DrawViewShell::KeyInput (const KeyEvent& rKEvt, ::sd::Window* pWin) +{ + bool bRet = false; + + if (!IsInputLocked() || (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)) + { + if(KEY_RETURN == rKEvt.GetKeyCode().GetCode() + && rKEvt.GetKeyCode().IsMod1() + && GetView()->IsTextEdit()) + { + // this should be used for cursor travelling. + SdPage* pActualPage = GetActualPage(); + const SdrMarkList& rMarkList = GetView()->GetMarkedObjectList(); + SdrTextObj* pCandidate = nullptr; + + if(pActualPage && 1 == rMarkList.GetMarkCount()) + { + SdrMark* pMark = rMarkList.GetMark(0); + + // remember which object was the text in edit mode + SdrObject* pOldObj = pMark->GetMarkedSdrObj(); + + // end text edit now + GetView()->SdrEndTextEdit(); + + // look for a new candidate, a successor of pOldObj + SdrObjListIter aIter(pActualPage, SdrIterMode::DeepNoGroups); + bool bDidVisitOldObject(false); + + while(aIter.IsMore() && !pCandidate) + { + SdrObject* pObj = aIter.Next(); + + if(auto pSdrTextObj = dynamic_cast( pObj )) + { + SdrInventor nInv(pObj->GetObjInventor()); + SdrObjKind nKnd(pObj->GetObjIdentifier()); + + if(SdrInventor::Default == nInv && + (SdrObjKind::TitleText == nKnd || SdrObjKind::OutlineText == nKnd || SdrObjKind::Text == nKnd) + && bDidVisitOldObject) + { + pCandidate = pSdrTextObj; + } + + if(pObj == pOldObj) + { + bDidVisitOldObject = true; + } + } + } + } + + if(pCandidate) + { + // set the new candidate to text edit mode + GetView()->UnMarkAll(); + GetView()->MarkObj(pCandidate, GetView()->GetSdrPageView()); + + GetViewFrame()->GetDispatcher()->Execute( + SID_ATTR_CHAR, SfxCallMode::ASYNCHRON); + } + else + { + // insert a new page with the same page layout + GetViewFrame()->GetDispatcher()->Execute( + SID_INSERTPAGE_QUICK, SfxCallMode::ASYNCHRON); + } + } + else + { + bRet = ViewShell::KeyInput(rKEvt, pWin); + //If object is marked , the corresponding entry is set true , else + //the corresponding entry is set false . + if(KEY_TAB == rKEvt.GetKeyCode().GetCode()) + { + FreshNavigatrTree(); + } + } + if (!bRet && !mbReadOnly) // tdf#139804 + { + bRet = GetView()->KeyInput(rKEvt, pWin); + } + } + + return bRet; +} + +/** + * Start with Drag from ruler (helper lines, origin) + */ +void DrawViewShell::StartRulerDrag ( + const Ruler& rRuler, + const MouseEvent& rMEvt) +{ + GetActiveWindow()->CaptureMouse(); + + Point aWPos = GetActiveWindow()->PixelToLogic(GetActiveWindow()->GetPointerPosPixel()); + + if ( rRuler.GetExtraRect().Contains(rMEvt.GetPosPixel()) ) + { + mpDrawView->BegSetPageOrg(aWPos); + mbIsRulerDrag = true; + } + else + { + // #i34536# if no guide-lines are visible yet, that show them + if( ! mpDrawView->IsHlplVisible()) + mpDrawView->SetHlplVisible(); + + SdrHelpLineKind eKind; + + if ( rMEvt.IsMod1() ) + eKind = SdrHelpLineKind::Point; + else if ( rRuler.IsHorizontal() ) + eKind = SdrHelpLineKind::Horizontal; + else + eKind = SdrHelpLineKind::Vertical; + + mpDrawView->BegDragHelpLine(aWPos, eKind); + mbIsRulerDrag = true; + } +} + +void DrawViewShell::FreshNavigatrTree() +{ + SfxChildWindow* pWindow = GetViewFrame()->GetChildWindow( SID_NAVIGATOR ); + if( pWindow ) + { + SdNavigatorFloat* pNavWin = static_cast( pWindow->GetWindow() ); + if( pNavWin ) + pNavWin->FreshTree( GetDoc() ); + } +} + +void DrawViewShell::MouseButtonDown(const MouseEvent& rMEvt, + ::sd::Window* pWin) +{ + mbMouseButtonDown = true; + mbMouseSelecting = false; + + // We have to check if a context menu is shown and we have an UI + // active inplace client. In that case we have to ignore the mouse + // button down event. Otherwise we would crash (context menu has been + // opened by inplace client and we would deactivate the inplace client, + // the context menu is closed by VCL asynchronously which in the end + // would work on deleted objects or the context menu has no parent anymore) + SfxInPlaceClient* pIPClient = GetViewShell()->GetIPClient(); + bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() ); + + if (bIsOleActive && vcl::IsInPopupMenuExecute()) + return; + + if ( IsInputLocked() ) + return; + + ViewShell::MouseButtonDown(rMEvt, pWin); + + //If object is marked , the corresponding entry is set true , + //else the corresponding entry is set false . + FreshNavigatrTree(); + if (mbPipette) + { + SfxChildWindow* pWnd = GetViewFrame()->GetChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + SvxBmpMask* pBmpMask = pWnd ? static_cast(pWnd->GetWindow()) : nullptr; + if (pBmpMask) + pBmpMask->PipetteClicked(); + } +} + +void DrawViewShell::MouseMove(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + if ( IsMouseButtonDown() ) + mbMouseSelecting = true; + + if ( IsInputLocked() ) + return; + + if ( mpDrawView->IsAction() ) + { + ::tools::Rectangle aOutputArea(Point(0,0), GetActiveWindow()->GetOutputSizePixel()); + + if ( !aOutputArea.Contains(rMEvt.GetPosPixel()) ) + { + bool bInsideOtherWindow = false; + + if (mpContentWindow) + { + aOutputArea = ::tools::Rectangle(Point(0,0), + mpContentWindow->GetOutputSizePixel()); + + Point aPos = mpContentWindow->GetPointerPosPixel(); + if ( aOutputArea.Contains(aPos) ) + bInsideOtherWindow = true; + } + + if (! GetActiveWindow()->HasFocus ()) + { + GetActiveWindow()->ReleaseMouse (); + mpDrawView->BrkAction (); + return; + } + else if ( bInsideOtherWindow ) + { + GetActiveWindow()->ReleaseMouse(); + pWin->CaptureMouse (); + } + } + else if ( pWin != GetActiveWindow() ) + pWin->CaptureMouse(); + } + + // Since the next MouseMove may execute a IsSolidDraggingNow() in + // SdrCreateView::MovCreateObj and there the ApplicationBackgroundColor + // is needed it is necessary to set it here. + if (GetDoc()) + { + ConfigureAppBackgroundColor(); + mpDrawView->SetApplicationBackgroundColor( mnAppBackgroundColor ); + } + + ViewShell::MouseMove(rMEvt, pWin); + + maMousePos = rMEvt.GetPosPixel(); + + ::tools::Rectangle aRect; + + if ( mbIsRulerDrag ) + { + Point aLogPos = GetActiveWindow()->PixelToLogic(maMousePos); + mpDrawView->MovAction(aLogPos); + } + + if ( mpDrawView->IsAction() ) + { + mpDrawView->TakeActionRect(aRect); + aRect = GetActiveWindow()->LogicToPixel(aRect); + } + else + { + aRect = ::tools::Rectangle(maMousePos, maMousePos); + } + + ShowMousePosInfo(aRect, pWin); + + SvxBmpMask* pBmpMask = nullptr; + if (mbPipette && GetViewFrame()->HasChildWindow(SvxBmpMaskChildWindow::GetChildWindowId())) + { + SfxChildWindow* pWnd = GetViewFrame()->GetChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + pBmpMask = pWnd ? static_cast(pWnd->GetWindow()) : nullptr; + } + + if (!pBmpMask) + return; + + const ::tools::Long nStartX = maMousePos.X() - PIPETTE_RANGE; + const ::tools::Long nEndX = maMousePos.X() + PIPETTE_RANGE; + const ::tools::Long nStartY = maMousePos.Y() - PIPETTE_RANGE; + const ::tools::Long nEndY = maMousePos.Y() + PIPETTE_RANGE; + ::tools::Long nRed = 0; + ::tools::Long nGreen = 0; + ::tools::Long nBlue = 0; + const double fDiv = ( ( PIPETTE_RANGE << 1 ) + 1 ) * ( ( PIPETTE_RANGE << 1 ) + 1 ); + + for ( ::tools::Long nY = nStartY; nY <= nEndY; nY++ ) + { + for( ::tools::Long nX = nStartX; nX <= nEndX; nX++ ) + { + const Color aCol( pWin->GetOutDev()->GetPixel( pWin->PixelToLogic( Point( nX, nY ) ) ) ); + + nRed += aCol.GetRed(); + nGreen += aCol.GetGreen(); + nBlue += aCol.GetBlue(); + } + } + + pBmpMask->SetColor( Color( static_cast( nRed / fDiv + .5 ), + static_cast( nGreen / fDiv + .5 ), + static_cast( nBlue / fDiv + .5 ) ) ); +} + +void DrawViewShell::MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + mbMouseButtonDown = false; + + if ( !IsInputLocked() ) + { + bool bIsSetPageOrg = mpDrawView->IsSetPageOrg(); + + if (mbIsRulerDrag) + { + ::tools::Rectangle aOutputArea(Point(0,0), GetActiveWindow()->GetOutputSizePixel()); + + if (aOutputArea.Contains(rMEvt.GetPosPixel())) + { + mpDrawView->EndAction(); + + if (bIsSetPageOrg) + GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET); + } + else if (rMEvt.IsLeft() && bIsSetPageOrg) + { + mpDrawView->BrkAction(); + SdPage* pPage = static_cast( mpDrawView->GetSdrPageView()->GetPage() ); + Point aOrg(pPage->GetLeftBorder(), pPage->GetUpperBorder()); + mpDrawView->GetSdrPageView()->SetPageOrigin(aOrg); + GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET); + } + else + { + mpDrawView->BrkAction(); + } + + GetActiveWindow()->ReleaseMouse(); + mbIsRulerDrag = false; + } + else + ViewShell::MouseButtonUp(rMEvt, pWin); + //If object is marked , the corresponding entry is set true , + //else the corresponding entry is set false . + FreshNavigatrTree(); + } + mbMouseSelecting = false; +} + +void DrawViewShell::Command(const CommandEvent& rCEvt, ::sd::Window* pWin) +{ + // The command event is send to the window after a possible context + // menu from an inplace client is closed. Now we have the chance to + // deactivate the inplace client without any problem regarding parent + // windows and code on the stack. + SfxInPlaceClient* pIPClient = GetViewShell()->GetIPClient(); + bool bIsOleActive = ( pIPClient && pIPClient->IsObjectInPlaceActive() ); + if ( bIsOleActive && ( rCEvt.GetCommand() == CommandEventId::ContextMenu )) + { + // Deactivate OLE object + mpDrawView->UnmarkAll(); + SelectionHasChanged(); + return; + } + + if ( IsInputLocked() ) + return; + + if( GetView() &&GetView()->getSmartTags().Command(rCEvt) ) + return; + + const bool bNativeShow (SlideShow::IsRunning(GetViewShellBase())); + + if( rCEvt.GetCommand() == CommandEventId::PasteSelection && !bNativeShow ) + { + TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromPrimarySelection()); + + if( aDataHelper.GetTransferable().is() ) + { + Point aPos; + sal_Int8 nDnDAction = DND_ACTION_COPY; + + if( GetActiveWindow() ) + aPos = GetActiveWindow()->PixelToLogic( rCEvt.GetMousePosPixel() ); + + if( !mpDrawView->InsertData( aDataHelper, aPos, nDnDAction, false ) ) + { + INetBookmark aINetBookmark( "", "" ); + + if( ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::FILEGRPDESCRIPTOR, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::UNIFORMRESOURCELOCATOR, aINetBookmark ) ) ) + { + InsertURLField( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), "" ); + } + } + } + } + else if( rCEvt.GetCommand() == CommandEventId::ContextMenu && !bNativeShow && + pWin != nullptr && !mpDrawView->IsAction() && !SD_MOD()->GetWaterCan() ) + { + OUString aPopupId; // Resource name for popup menu + + // is there a snap object under the cursor? + SdrPageView* pPV; + Point aMPos = pWin->PixelToLogic( maMousePos ); + sal_uInt16 nHitLog = static_cast(GetActiveWindow()->PixelToLogic( + Size(FuPoor::HITPIX, 0 ) ).Width()); + sal_uInt16 nHelpLine; + // for gluepoints + SdrObject* pObj = nullptr; + sal_uInt16 nPickId = 0; + // for field command + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + const SvxFieldItem* pFldItem = nullptr; + if( pOLV ) + pFldItem = pOLV->GetFieldAtSelection(); + + // helper line + if ( mpDrawView->PickHelpLine( aMPos, nHitLog, *GetActiveWindow()->GetOutDev(), nHelpLine, pPV) ) + { + ::tools::Rectangle aRect(rCEvt.GetMousePosPixel(), Size(10, 10)); + weld::Window* pParent = weld::GetPopupParent(*pWin, aRect); + ShowSnapLineContextMenu(pParent, aRect, *pPV, nHelpLine); + return; + } + // is gluepoint under cursor marked? + else if( mpDrawView->PickGluePoint( aMPos, pObj, nPickId, pPV ) && + mpDrawView->IsGluePointMarked( pObj, nPickId ) ) + { + aPopupId = "gluepoint"; + } + // field command? + else if( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) ) ) + { + LanguageType eLanguage( LANGUAGE_SYSTEM ); + + // Format popup with outliner language, if possible + if( pOLV->GetOutliner() ) + { + ESelection aSelection( pOLV->GetSelection() ); + eLanguage = pOLV->GetOutliner()->GetLanguage( aSelection.nStartPara, aSelection.nStartPos ); + } + + //fdo#44998 if the outliner has captured the mouse events release the lock + //so the SdFieldPopup can get them + pOLV->ReleaseMouse(); + SdFieldPopup aFieldPopup(pFldItem->GetField(), eLanguage); + + if ( rCEvt.IsMouseEvent() ) + aMPos = rCEvt.GetMousePosPixel(); + else + aMPos = Point( 20, 20 ); + ::tools::Rectangle aRect(aMPos, Size(1, 1)); + weld::Window* pParent = weld::GetPopupParent(*pWin, aRect); + + aFieldPopup.Execute(pParent, aRect); + + std::unique_ptr pField(aFieldPopup.GetField()); + if (pField) + { + SvxFieldItem aFieldItem( *pField, EE_FEATURE_FIELD ); + // select field, so that it will be deleted on insert + ESelection aSel = pOLV->GetSelection(); + bool bSel = true; + if( aSel.nStartPos == aSel.nEndPos ) + { + bSel = false; + aSel.nEndPos++; + } + pOLV->SetSelection( aSel ); + + pOLV->InsertField( aFieldItem ); + + // reset selection back to original state + if( !bSel ) + aSel.nEndPos--; + pOLV->SetSelection( aSel ); + } + } + else + { + // is something selected? + if (mpDrawView->AreObjectsMarked() && + mpDrawView->GetMarkedObjectList().GetMarkCount() == 1 ) + { + pObj = mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if( HasCurrentFunction(SID_BEZIER_EDIT) && (dynamic_cast< SdrPathObj * >( pObj ) != nullptr ) ) + { + aPopupId = "bezier"; + } + else + { + if( mpDrawView->GetTextEditObject() ) + { + OutlinerView* pOutlinerView = mpDrawView->GetTextEditOutlinerView(); + Point aPos(rCEvt.GetMousePosPixel()); + + if ( pOutlinerView ) + { + if( ( rCEvt.IsMouseEvent() && pOutlinerView->IsWrongSpelledWordAtPos(aPos) ) || + ( !rCEvt.IsMouseEvent() && pOutlinerView->IsCursorAtWrongSpelledWord() ) ) + { + // Popup for Online-Spelling now handled by DrawDocShell + Link aLink = LINK(GetDocSh(), DrawDocShell, OnlineSpellCallback); + + if( !rCEvt.IsMouseEvent() ) + { + aPos = GetActiveWindow()->LogicToPixel( pOutlinerView->GetEditView().GetCursor()->GetPos() ); + } + // While showing the spell context menu + // we lock the input so that another + // context menu can not be opened during + // that time (crash #i43235#). In order + // to not lock the UI completely we + // first release the mouse. + GetActiveWindow()->ReleaseMouse(); + LockInput(); + pOutlinerView->ExecuteSpellPopup(aPos, aLink); + pOutlinerView->GetEditView().Invalidate(); + UnlockInput(); + } + else + { + if( (pObj->GetObjInventor() == SdrInventor::Default) && (pObj->GetObjIdentifier() == SdrObjKind::Table) ) + { + aPopupId = "table"; + } + else + { + aPopupId = "drawtext"; + } + } + } + } + else + { + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default) + { + switch ( nId ) + { + case SdrObjKind::OutlineText: + case SdrObjKind::Caption: + case SdrObjKind::TitleText: + case SdrObjKind::Text: + aPopupId = "textbox"; + break; + + case SdrObjKind::PathLine: + case SdrObjKind::PolyLine: + aPopupId = "curve"; + break; + + case SdrObjKind::FreehandLine: + case SdrObjKind::Edge: + aPopupId = "connector"; + break; + + case SdrObjKind::Line: + aPopupId = "line"; + break; + + case SdrObjKind::Measure: + aPopupId = "measure"; + break; + + case SdrObjKind::Rectangle: + case SdrObjKind::CircleOrEllipse: + case SdrObjKind::FreehandFill: + case SdrObjKind::PathFill: + case SdrObjKind::Polygon: + case SdrObjKind::CircleSection: + case SdrObjKind::CircleArc: + case SdrObjKind::CircleCut: + case SdrObjKind::CustomShape: + aPopupId = "draw"; + break; + + case SdrObjKind::Group: + aPopupId = "group"; + break; + + case SdrObjKind::Graphic: + aPopupId = "graphic"; + break; + + case SdrObjKind::OLE2: + aPopupId = "oleobject"; + break; + case SdrObjKind::Media: + aPopupId = "media"; + break; + case SdrObjKind::Table: + aPopupId = "table"; + break; + default: ; + } + } + else if( nInv == SdrInventor::E3d ) + { + if( nId == SdrObjKind::E3D_Scene ) + { + if( !mpDrawView->IsGroupEntered() ) + aPopupId = "3dscene"; + else + aPopupId = "3dscene2"; + } + else + aPopupId = "3dobject"; + } + else if( nInv == SdrInventor::FmForm ) + { + aPopupId = "form"; + } + } + } + } + + // multiple selection + else if (mpDrawView->AreObjectsMarked() && + mpDrawView->GetMarkedObjectList().GetMarkCount() > 1 ) + { + aPopupId = "multiselect"; + } + + // nothing selected + else + { + aPopupId = "page"; + } + } + // show Popup-Menu + if (!aPopupId.isEmpty()) + { + GetActiveWindow()->ReleaseMouse(); + + // tdf#137445 at this context menu popup time get what the + // DisableEditHyperlink would be for this position + bool bShouldDisableEditHyperlink = ShouldDisableEditHyperlink(); + + if(rCEvt.IsMouseEvent()) + GetViewFrame()->GetDispatcher()->ExecutePopup( aPopupId ); + else + { + //don't open contextmenu at mouse position if not opened via mouse + + //middle of the window if nothing is marked + Point aMenuPos(GetActiveWindow()->GetSizePixel().Width()/2 + ,GetActiveWindow()->GetSizePixel().Height()/2); + + //middle of the bounding rect if something is marked + if( mpDrawView->AreObjectsMarked() && mpDrawView->GetMarkedObjectList().GetMarkCount() >= 1 ) + { + ::tools::Rectangle aMarkRect; + mpDrawView->GetMarkedObjectList().TakeBoundRect(nullptr,aMarkRect); + aMenuPos = GetActiveWindow()->LogicToPixel( aMarkRect.Center() ); + + //move the point into the visible window area + if( aMenuPos.X() < 0 ) + aMenuPos.setX( 0 ); + if( aMenuPos.Y() < 0 ) + aMenuPos.setY( 0 ); + if( aMenuPos.X() > GetActiveWindow()->GetSizePixel().Width() ) + aMenuPos.setX( GetActiveWindow()->GetSizePixel().Width() ); + if( aMenuPos.Y() > GetActiveWindow()->GetSizePixel().Height() ) + aMenuPos.setY( GetActiveWindow()->GetSizePixel().Height() ); + } + + //open context menu at that point + GetViewFrame()->GetDispatcher()->ExecutePopup( aPopupId, GetActiveWindow(), &aMenuPos ); + } + + if (!bShouldDisableEditHyperlink) + { + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + // tdf#137445 set what the menu popup state for this was + EnableEditHyperlink(); + // ensure moAtContextMenu_DisableEditHyperlink will be cleared + // in the case that EditHyperlink is not dispatched by the menu + rBindings.Invalidate(SID_EDIT_HYPERLINK); + } + } + } + else + { + ViewShell::Command(rCEvt, pWin); + } +} + +void DrawViewShell::EnableEditHyperlink() +{ + moAtContextMenu_DisableEditHyperlink = false; +} + +void DrawViewShell::ShowMousePosInfo(const ::tools::Rectangle& rRect, + ::sd::Window const * pWin) +{ + if (mbHasRulers && pWin ) + { + RulerLine pHLines[2]; + RulerLine pVLines[2]; + ::tools::Long nHOffs = 0; + ::tools::Long nVOffs = 0; + sal_uInt16 nCnt; + + if (mpHorizontalRuler) + mpHorizontalRuler->SetLines(); + + if (mpVerticalRuler) + mpVerticalRuler->SetLines(); + + if (mpHorizontalRuler) + { + nHOffs = mpHorizontalRuler->GetNullOffset() + + mpHorizontalRuler->GetPageOffset(); + } + + if (mpVerticalRuler) + { + nVOffs = mpVerticalRuler->GetNullOffset() + + mpVerticalRuler->GetPageOffset(); + } + + nCnt = 1; + pHLines[0].nPos = rRect.Left() - nHOffs; + pVLines[0].nPos = rRect.Top() - nVOffs; + + if ( rRect.Right() != rRect.Left() || rRect.Bottom() != rRect.Top() ) + { + pHLines[1].nPos = rRect.Right() - nHOffs; + pVLines[1].nPos = rRect.Bottom() - nVOffs; + nCnt++; + } + + if (mpHorizontalRuler) + mpHorizontalRuler->SetLines(nCnt, pHLines); + if (mpVerticalRuler) + mpVerticalRuler->SetLines(nCnt, pVLines); + } + + // display with coordinates in StatusBar + OSL_ASSERT (GetViewShell()!=nullptr); + if ( GetViewShell()->GetUIActiveClient() ) + return; + + SfxItemSetFixed< + SID_CONTEXT, SID_CONTEXT, + SID_ATTR_POSITION, SID_ATTR_SIZE> aSet(GetPool()); + + GetStatusBarState(aSet); + + aSet.Put( SfxStringItem( SID_CONTEXT, mpDrawView->GetStatusText() ) ); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.SetState(aSet); + rBindings.Invalidate(SID_CONTEXT); + rBindings.Invalidate(SID_ATTR_POSITION); + rBindings.Invalidate(SID_ATTR_SIZE); +} + +void DrawViewShell::LockInput() +{ + mnLockCount++; +} + +void DrawViewShell::UnlockInput() +{ + DBG_ASSERT( mnLockCount, "Input for this shell is not locked!" ); + if ( mnLockCount ) + mnLockCount--; +} + +void DrawViewShell::ShowSnapLineContextMenu(weld::Window* pParent, const ::tools::Rectangle& rRect, + SdrPageView& rPageView, const sal_uInt16 nSnapLineIndex) +{ + const SdrHelpLine& rHelpLine (rPageView.GetHelpLines()[nSnapLineIndex]); + std::unique_ptr xBuilder(Application::CreateBuilder(nullptr, "modules/simpress/ui/snapmenu.ui")); + std::unique_ptr xMenu(xBuilder->weld_menu("menu")); + + if (rHelpLine.GetKind() == SdrHelpLineKind::Point) + { + xMenu->append(OUString::number(SID_SET_SNAPITEM), SdResId(STR_POPUP_EDIT_SNAPPOINT)); + xMenu->append_separator("separator"); + xMenu->append(OUString::number(SID_DELETE_SNAPITEM), SdResId(STR_POPUP_DELETE_SNAPPOINT)); + } + else + { + xMenu->append(OUString::number(SID_SET_SNAPITEM), SdResId(STR_POPUP_EDIT_SNAPLINE)); + xMenu->append_separator("separator"); + xMenu->append(OUString::number(SID_DELETE_SNAPITEM), SdResId(STR_POPUP_DELETE_SNAPLINE)); + } + + const int nResult = xMenu->popup_at_rect(pParent, rRect).toInt32(); + switch (nResult) + { + case SID_SET_SNAPITEM: + { + SfxUInt32Item aHelpLineItem (ID_VAL_INDEX, nSnapLineIndex); + const SfxPoolItem* aArguments[] = {&aHelpLineItem, nullptr}; + GetViewFrame()->GetDispatcher()->Execute( + SID_SET_SNAPITEM, + SfxCallMode::SLOT, + aArguments); + } + break; + + case SID_DELETE_SNAPITEM: + { + rPageView.DeleteHelpLine(nSnapLineIndex); + } + break; + + default: + break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews5.cxx b/sd/source/ui/view/drviews5.cxx new file mode 100644 index 000000000..3eb9f39c3 --- /dev/null +++ b/sd/source/ui/view/drviews5.cxx @@ -0,0 +1,650 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + +void DrawViewShell::ModelHasChanged() +{ + Invalidate(); + // that the navigator also gets an up to date state + GetViewFrame()->GetBindings().Invalidate( SID_NAVIGATOR_STATE, true ); + + SfxBoolItem aItem( SID_3D_STATE, true ); + GetViewFrame()->GetDispatcher()->ExecuteList( + SID_3D_STATE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + + // now initialize the TextEditOutliner which was newly created by the draw engine + ::Outliner* pOutliner = mpDrawView->GetTextEditOutliner(); + if (pOutliner) + { + SfxStyleSheetPool* pSPool = static_cast( GetDocSh()->GetStyleSheetPool() ); + pOutliner->SetStyleSheetPool(pSPool); + } +} + +void DrawViewShell::Resize() +{ + ViewShell::Resize(); + + // tdf#151621 Do not set if the embedded object is opening in a new window. + if (GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED + && GetDocSh()->IsInPlaceActive()) + { + SetZoomRect(GetDocSh()->GetVisArea(ASPECT_CONTENT)); + } + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( xSlideshow.is() && xSlideshow->isRunning() && !xSlideshow->isFullScreen() ) + { + xSlideshow->resize(maViewSize); + } +} + +void DrawViewShell::ArrangeGUIElements() +{ + // Retrieve the current size (thickness) of the scroll bars. That is + // the width of the vertical and the height of the horizontal scroll + // bar. + int nScrollBarSize = GetParentWindow()->GetSettings().GetStyleSettings().GetScrollBarSize(); + maScrBarWH = Size (nScrollBarSize, nScrollBarSize); + + ViewShell::ArrangeGUIElements (); + + maTabControl->Hide(); + + OSL_ASSERT (GetViewShell()!=nullptr); + Client* pIPClient = static_cast(GetViewShell()->GetIPClient()); + bool bClientActive = false; + if ( pIPClient && pIPClient->IsObjectInPlaceActive() ) + bClientActive = true; + + bool bInPlaceActive = GetViewFrame()->GetFrame().IsInPlace(); + + if ( mbZoomOnPage && !bInPlaceActive && !bClientActive ) + { + // with split, always resize first window + //af pWindow = mpContentWindow.get(); + SfxRequest aReq(SID_SIZE_PAGE, SfxCallMode::SLOT, GetDoc()->GetItemPool()); + ExecuteSlot( aReq ); + } +} + +/** + * Apply data of the FrameView on the current view + */ +void DrawViewShell::ReadFrameViewData(FrameView* pView) +{ + ModifyGuard aGuard( GetDoc() ); + + // this option has to be adjust at the model + GetDoc()->SetPickThroughTransparentTextFrames( + SD_MOD()->GetSdOptions(GetDoc()->GetDocumentType())->IsPickThrough()); + + // initialization of the Character-(Screen-) attribute + if (HasRuler() != pView->HasRuler()) + SetRuler( pView->HasRuler() ); + + if (mpDrawView->GetGridCoarse() != pView->GetGridCoarse()) + mpDrawView->SetGridCoarse( pView->GetGridCoarse() ); + + if (mpDrawView->GetGridFine() != pView->GetGridFine()) + mpDrawView->SetGridFine( pView->GetGridFine() ); + + if (mpDrawView->GetSnapGridWidthX() != pView->GetSnapGridWidthX() || mpDrawView->GetSnapGridWidthY() != pView->GetSnapGridWidthY()) + mpDrawView->SetSnapGridWidth(pView->GetSnapGridWidthX(), pView->GetSnapGridWidthY()); + + if (mpDrawView->IsGridVisible() != pView->IsGridVisible()) + mpDrawView->SetGridVisible( pView->IsGridVisible() ); + + if (mpDrawView->IsGridFront() != pView->IsGridFront()) + mpDrawView->SetGridFront( pView->IsGridFront() ); + + if (mpDrawView->GetSnapAngle() != pView->GetSnapAngle()) + mpDrawView->SetSnapAngle( pView->GetSnapAngle() ); + + if (mpDrawView->IsGridSnap() != pView->IsGridSnap() ) + mpDrawView->SetGridSnap( pView->IsGridSnap() ); + + if (mpDrawView->IsBordSnap() != pView->IsBordSnap() ) + mpDrawView->SetBordSnap( pView->IsBordSnap() ); + + if (mpDrawView->IsHlplSnap() != pView->IsHlplSnap() ) + mpDrawView->SetHlplSnap( pView->IsHlplSnap() ); + + if (mpDrawView->IsOFrmSnap() != pView->IsOFrmSnap() ) + mpDrawView->SetOFrmSnap( pView->IsOFrmSnap() ); + + if (mpDrawView->IsOPntSnap() != pView->IsOPntSnap() ) + mpDrawView->SetOPntSnap( pView->IsOPntSnap() ); + + if (mpDrawView->IsOConSnap() != pView->IsOConSnap() ) + mpDrawView->SetOConSnap( pView->IsOConSnap() ); + + if (mpDrawView->IsHlplVisible() != pView->IsHlplVisible() ) + mpDrawView->SetHlplVisible( pView->IsHlplVisible() ); + + if (mpDrawView->IsDragStripes() != pView->IsDragStripes() ) + mpDrawView->SetDragStripes( pView->IsDragStripes() ); + + if (mpDrawView->IsPlusHandlesAlwaysVisible() != pView->IsPlusHandlesAlwaysVisible() ) + mpDrawView->SetPlusHandlesAlwaysVisible( pView->IsPlusHandlesAlwaysVisible() ); + + if (mpDrawView->GetSnapMagneticPixel() != pView->GetSnapMagneticPixel() ) + mpDrawView->SetSnapMagneticPixel( pView->GetSnapMagneticPixel() ); + + if (mpDrawView->IsMarkedHitMovesAlways() != pView->IsMarkedHitMovesAlways() ) + mpDrawView->SetMarkedHitMovesAlways( pView->IsMarkedHitMovesAlways() ); + + if (mpDrawView->IsMoveOnlyDragging() != pView->IsMoveOnlyDragging() ) + mpDrawView->SetMoveOnlyDragging( pView->IsMoveOnlyDragging() ); + + if (mpDrawView->IsNoDragXorPolys() != pView->IsNoDragXorPolys() ) + mpDrawView->SetNoDragXorPolys( pView->IsNoDragXorPolys() ); + + if (mpDrawView->IsCrookNoContortion() != pView->IsCrookNoContortion() ) + mpDrawView->SetCrookNoContortion( pView->IsCrookNoContortion() ); + + if (mpDrawView->IsAngleSnapEnabled() != pView->IsAngleSnapEnabled() ) + mpDrawView->SetAngleSnapEnabled( pView->IsAngleSnapEnabled() ); + + if (mpDrawView->IsBigOrtho() != pView->IsBigOrtho() ) + mpDrawView->SetBigOrtho( pView->IsBigOrtho() ); + + if (mpDrawView->IsOrtho() != pView->IsOrtho() ) + mpDrawView->SetOrtho( pView->IsOrtho() ); + + if (mpDrawView->GetEliminatePolyPointLimitAngle() != pView->GetEliminatePolyPointLimitAngle() ) + mpDrawView->SetEliminatePolyPointLimitAngle( pView->GetEliminatePolyPointLimitAngle() ); + + if (mpDrawView->IsEliminatePolyPoints() != pView->IsEliminatePolyPoints() ) + mpDrawView->SetEliminatePolyPoints( pView->IsEliminatePolyPoints() ); + + if (mpDrawView->IsSolidDragging() != pView->IsSolidDragging() ) + mpDrawView->SetSolidDragging( pView->IsSolidDragging() ); + + if (mpDrawView->IsQuickTextEditMode() != pView->IsQuickEdit()) + mpDrawView->SetQuickTextEditMode( pView->IsQuickEdit() ); + + // #i26631# + if (mpDrawView->IsMasterPagePaintCaching() != pView->IsMasterPagePaintCaching()) + mpDrawView->SetMasterPagePaintCaching( pView->IsMasterPagePaintCaching() ); + + // handle size: 9 pixels + sal_uInt16 nTmp = mpDrawView->GetMarkHdlSizePixel(); + if( nTmp != 9 ) + mpDrawView->SetMarkHdlSizePixel( 9 ); + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + if (pPageView) + { + if ( pPageView->GetVisibleLayers() != pView->GetVisibleLayers() ) + pPageView->SetVisibleLayers( pView->GetVisibleLayers() ); + + if ( pPageView->GetPrintableLayers() != pView->GetPrintableLayers() ) + pPageView->SetPrintableLayers( pView->GetPrintableLayers() ); + + if ( pPageView->GetLockedLayers() != pView->GetLockedLayers() ) + pPageView->SetLockedLayers( pView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + if (pPageView->GetHelpLines() != pView->GetNotesHelpLines()) + pPageView->SetHelpLines( pView->GetNotesHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + if (pPageView->GetHelpLines() != pView->GetHandoutHelpLines()) + pPageView->SetHelpLines( pView->GetHandoutHelpLines() ); + } + else + { + if (pPageView->GetHelpLines() != pView->GetStandardHelpLines()) + pPageView->SetHelpLines( pView->GetStandardHelpLines() ); + } + } + + if ( mpDrawView->GetActiveLayer() != pView->GetActiveLayer() ) + mpDrawView->SetActiveLayer( pView->GetActiveLayer() ); + + sal_uInt16 nSelectedPage = 0; + + if (mePageKind != PageKind::Handout) + { + nSelectedPage = pView->GetSelectedPage(); + } + + EditMode eNewEditMode = pView->GetViewShEditMode(/*mePageKind*/); + bool bNewLayerMode = pView->IsLayerMode(); + + if(IsLayerModeActive() && bNewLayerMode) + { + // #i57936# Force mbIsLayerModeActive to false so that ChangeEditMode + // below does something regarding LayerTabBar content refresh. That refresh + // is only done when IsLayerModeActive changes. It needs to be done + // since e.g. Layer visibility was changed above and this may need + // a refresh to show the correct graphical representation + mbIsLayerModeActive = false; + } + + ChangeEditMode(eNewEditMode, bNewLayerMode); + SwitchPage(nSelectedPage); + + // restore DrawMode for 'normal' window + if(GetActiveWindow()->GetOutDev()->GetDrawMode() != pView->GetDrawMode()) + GetActiveWindow()->GetOutDev()->SetDrawMode(pView->GetDrawMode()); + + if ( mpDrawView->IsDesignMode() != pView->IsDesignMode() ) + { + SfxBoolItem aDesignModeItem( SID_FM_DESIGN_MODE, pView->IsDesignMode() ); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_FM_DESIGN_MODE, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aDesignModeItem }); + } + + // has to be called in the end, because it executes a WriteFrameViewData() + if (mpDrawView->IsFrameDragSingles() != pView->IsFrameDragSingles() ) + mpDrawView->SetFrameDragSingles( pView->IsFrameDragSingles() ); +} + +/** + * Apply data of the current view on the FrameView + */ +void DrawViewShell::WriteFrameViewData() +{ + // store character-(screen-) attribute of FrameView + mpFrameView->SetRuler( HasRuler() ); + mpFrameView->SetGridCoarse( mpDrawView->GetGridCoarse() ); + mpFrameView->SetGridFine( mpDrawView->GetGridFine() ); + mpFrameView->SetSnapGridWidth(mpDrawView->GetSnapGridWidthX(), mpDrawView->GetSnapGridWidthY()); + mpFrameView->SetGridVisible( mpDrawView->IsGridVisible() ); + mpFrameView->SetGridFront( mpDrawView->IsGridFront() ); + mpFrameView->SetSnapAngle( mpDrawView->GetSnapAngle() ); + mpFrameView->SetGridSnap( mpDrawView->IsGridSnap() ); + mpFrameView->SetBordSnap( mpDrawView->IsBordSnap() ); + mpFrameView->SetHlplSnap( mpDrawView->IsHlplSnap() ); + mpFrameView->SetOFrmSnap( mpDrawView->IsOFrmSnap() ); + mpFrameView->SetOPntSnap( mpDrawView->IsOPntSnap() ); + mpFrameView->SetOConSnap( mpDrawView->IsOConSnap() ); + mpFrameView->SetHlplVisible( mpDrawView->IsHlplVisible() ); + mpFrameView->SetDragStripes( mpDrawView->IsDragStripes() ); + mpFrameView->SetPlusHandlesAlwaysVisible( mpDrawView->IsPlusHandlesAlwaysVisible() ); + mpFrameView->SetFrameDragSingles( mpDrawView->IsFrameDragSingles() ); + mpFrameView->SetMarkedHitMovesAlways( mpDrawView->IsMarkedHitMovesAlways() ); + mpFrameView->SetMoveOnlyDragging( mpDrawView->IsMoveOnlyDragging() ); + mpFrameView->SetNoDragXorPolys( mpDrawView->IsNoDragXorPolys() ); + mpFrameView->SetCrookNoContortion( mpDrawView->IsCrookNoContortion() ); + mpFrameView->SetBigOrtho( mpDrawView->IsBigOrtho() ); + mpFrameView->SetEliminatePolyPointLimitAngle( mpDrawView->GetEliminatePolyPointLimitAngle() ); + mpFrameView->SetEliminatePolyPoints( mpDrawView->IsEliminatePolyPoints() ); + + mpFrameView->SetSolidDragging( mpDrawView->IsSolidDragging() ); + mpFrameView->SetQuickEdit( mpDrawView->IsQuickTextEditMode() ); + + mpFrameView->SetDesignMode( mpDrawView->IsDesignMode() ); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisArea = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + if (comphelper::LibreOfficeKit::isActive()) + { + // aVisArea is nonsensical in the LOK case, use the slide size + aVisArea = ::tools::Rectangle(Point(), getCurrentPage()->GetSize()); + } + + mpFrameView->SetVisArea(aVisArea); + + if( mePageKind == PageKind::Handout ) + mpFrameView->SetSelectedPage(0); + else + { + mpFrameView->SetSelectedPage( maTabControl->GetCurPagePos() ); + } + + mpFrameView->SetViewShEditMode(meEditMode); + mpFrameView->SetLayerMode(IsLayerModeActive()); + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if (pPageView) + { + if ( mpFrameView->GetVisibleLayers() != pPageView->GetVisibleLayers() ) + mpFrameView->SetVisibleLayers( pPageView->GetVisibleLayers() ); + + if ( mpFrameView->GetPrintableLayers() != pPageView->GetPrintableLayers() ) + mpFrameView->SetPrintableLayers( pPageView->GetPrintableLayers() ); + + if ( mpFrameView->GetLockedLayers() != pPageView->GetLockedLayers() ) + mpFrameView->SetLockedLayers( pPageView->GetLockedLayers() ); + + if (mePageKind == PageKind::Notes) + { + mpFrameView->SetNotesHelpLines( pPageView->GetHelpLines() ); + } + else if (mePageKind == PageKind::Handout) + { + mpFrameView->SetHandoutHelpLines( pPageView->GetHelpLines() ); + } + else + { + mpFrameView->SetStandardHelpLines( pPageView->GetHelpLines() ); + } + } + + if ( mpFrameView->GetActiveLayer() != mpDrawView->GetActiveLayer() ) + mpFrameView->SetActiveLayer( mpDrawView->GetActiveLayer() ); + + // store DrawMode for 'normal' window + if(mpFrameView->GetDrawMode() != GetActiveWindow()->GetOutDev()->GetDrawMode()) + mpFrameView->SetDrawMode(GetActiveWindow()->GetOutDev()->GetDrawMode()); +} + +void DrawViewShell::PrePaint() +{ + mpDrawView->PrePaint(); +} + +/** + * The event is forwarded to the Viewshell and the current function by the + * window pWin. + * + * Remark: pWin==NULL, if Paint() is called from ShowWindow! + */ +void DrawViewShell::Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) +{ + /* This is done before each text edit, so why not do it before every paint. + The default language is only used if the outliner only contains one + character in a symbol font */ + GetDoc()->GetDrawOutliner().SetDefaultLanguage( GetDoc()->GetLanguage( EE_CHAR_LANGUAGE ) ); + + // Set Application Background color for usage in SdrPaintView(s) + mpDrawView->SetApplicationBackgroundColor( mnAppBackgroundColor ); + + /* This is done before each text edit, so why not do it before every paint. + The default language is only used if the outliner only contains one + character in a symbol font */ + GetDoc()->GetDrawOutliner().SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + + mpDrawView->CompleteRedraw( pWin->GetOutDev(), vcl::Region( rRect ) ); +} + +/** + * adjust zoom factor for InPlace + */ +void DrawViewShell::SetZoomFactor(const Fraction& rZoomX, const Fraction& rZoomY) +{ + ViewShell::SetZoomFactor(rZoomX, rZoomY); + mbZoomOnPage = false; + Point aOrigin = GetActiveWindow()->GetViewOrigin(); + GetActiveWindow()->SetWinViewPos(aOrigin); +} + +void DrawViewShell::HidePage() +{ + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + pFormShell->PrepareClose(false); +} + +void DrawViewShell::WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + WriteFrameViewData(); + + ViewShell::WriteUserDataSequence( rSequence ); + + const sal_Int32 nIndex = rSequence.getLength(); + rSequence.realloc( nIndex + 1 ); + auto pSequence = rSequence.getArray(); + pSequence[nIndex].Name = sUNO_View_ZoomOnPage ; + pSequence[nIndex].Value <<= mbZoomOnPage; + + // Common SdrModel processing + GetDocSh()->GetDoc()->WriteUserDataSequence(rSequence); +} + +void DrawViewShell::ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + WriteFrameViewData(); + + ViewShell::ReadUserDataSequence( rSequence ); + + for (const css::beans::PropertyValue& rValue : rSequence) + { + if ( rValue.Name == sUNO_View_ZoomOnPage ) + { + bool bZoomPage = false; + if( rValue.Value >>= bZoomPage ) + { + mbZoomOnPage = bZoomPage; + } + } + // Fallback to common SdrModel processing + else GetDocSh()->GetDoc()->ReadUserDataSequenceValue(&rValue); + } + + // The parameter rSequence contains the config-items from + // . Determine, whether + // they contain "VisibleLayers", "PrintableLayers" and "LockedLayers". If not, it + // is a foreign document or a new document after transition period and the corresponding + // information were read from . In that case we need to bring + // the information from model to view. + bool bHasVisiPrnLockSettings(false); + for ( auto & rPropertyValue : rSequence ) + { + if ( rPropertyValue.Name == sUNO_View_VisibleLayers + || rPropertyValue.Name == sUNO_View_PrintableLayers + || rPropertyValue.Name == sUNO_View_LockedLayers ) + { + bHasVisiPrnLockSettings = true; + break; + } + } + if ( !bHasVisiPrnLockSettings ) + { + const SdrLayerAdmin& rLayerAdmin = GetDocSh()->GetDoc()->GetLayerAdmin(); + SdrLayerIDSet aSdrLayerIDSet; + rLayerAdmin.getVisibleLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetVisibleLayers( aSdrLayerIDSet ); + rLayerAdmin.getPrintableLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetPrintableLayers( aSdrLayerIDSet ); + rLayerAdmin.getLockedLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetLockedLayers( aSdrLayerIDSet ); + } + else + { + // tdf#129898 repair layer "DrawnInSlideshow", which was wrongly written + // in LO 6.2 to 6.4. The ODF defaults were corrected when reading draw:layer-set, but + // not in reading config settings, because there the name is not known. + const SdrLayerAdmin& rLayerAdmin = GetDocSh()->GetDoc()->GetLayerAdmin(); + if (rLayerAdmin.GetLayer("DrawnInSlideshow")) + { + SdrLayerIDSet aSdrLayerIDSet; + rLayerAdmin.getVisibleLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetVisibleLayers( aSdrLayerIDSet ); + rLayerAdmin.getPrintableLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetPrintableLayers( aSdrLayerIDSet ); + rLayerAdmin.getLockedLayersODF( aSdrLayerIDSet ); + mpFrameView -> SetLockedLayers( aSdrLayerIDSet ); + } + } + + + if( mpFrameView->GetPageKind() != mePageKind ) + { + mePageKind = mpFrameView->GetPageKind(); + + if (mePageKind == PageKind::Notes) + { + GetActiveWindow()->SetHelpId( CMD_SID_NOTES_MODE ); + } + else if (mePageKind == PageKind::Handout) + { + GetActiveWindow()->SetHelpId( CMD_SID_HANDOUT_MASTER_MODE ); + } + else + { + GetActiveWindow()->SetHelpId( HID_SDDRAWVIEWSHELL ); + } + } + + ReadFrameViewData( mpFrameView ); + + if( !mbZoomOnPage ) + { + const ::tools::Rectangle aVisArea( mpFrameView->GetVisArea() ); + + // tdf#151621 Do not set if the embedded object is opening in a new window. + if (GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED + && GetDocSh()->IsInPlaceActive()) + { + GetDocSh()->SetVisArea(aVisArea); + } + + VisAreaChanged(aVisArea); + + ::sd::View* pView = GetView(); + + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + SetZoomRect(aVisArea); + } + ChangeEditMode (meEditMode, ! IsLayerModeActive()); + ResetActualLayer(); +} + +void DrawViewShell::VisAreaChanged(const ::tools::Rectangle& rRect) +{ + ViewShell::VisAreaChanged( rRect ); + + DrawController& rController = GetViewShellBase().GetDrawController(); + rController.FireVisAreaChanged (rRect); +} + +/** If there is a valid controller then create a new instance of + AccessibleDrawDocumentView. Otherwise return an empty + reference. +*/ +css::uno::Reference + DrawViewShell::CreateAccessibleDocumentView (::sd::Window* pWindow) +{ + if (GetViewShellBase().GetController() != nullptr) + { + rtl::Reference pDocumentView = + new accessibility::AccessibleDrawDocumentView ( + pWindow, + this, + GetViewShellBase().GetController(), + pWindow->GetAccessibleParentWindow()->GetAccessible()); + pDocumentView->Init(); + return pDocumentView; + } + + SAL_WARN("sd", "DrawViewShell::CreateAccessibleDocumentView: no controller"); + return css::uno::Reference< css::accessibility::XAccessible>(); +} + +int DrawViewShell::GetActiveTabLayerIndex() const +{ + const LayerTabBar* pBar + = const_cast(this)->GetLayerTabControl (); + if (pBar != nullptr) + return pBar->GetPagePos (pBar->GetCurPageId()); + else + return -1; +} + +void DrawViewShell::SetActiveTabLayerIndex (int nIndex) +{ + LayerTabBar* pBar = GetLayerTabControl (); + if (pBar == nullptr) + return; + + // Ignore invalid indices silently. + if (nIndex>=0 && nIndexGetPageCount()) + { + // Tell the draw view and the tab control of the new active layer. + mpDrawView->SetActiveLayer (pBar->GetLayerName (pBar->GetPageId (static_cast(nIndex)))); + pBar->SetCurPageId (pBar->GetPageId (static_cast(nIndex))); + rtl::Reference pUnoDrawView(new SdUnoDrawView ( + *this, + *GetView())); + css::uno::Reference< css::drawing::XLayer> rLayer = pUnoDrawView->getActiveLayer(); + GetViewShellBase().GetDrawController().fireChangeLayer( &rLayer ); + } +} + +LayerTabBar* DrawViewShell::GetLayerTabControl() +{ + return mpLayerTabBar.get(); +} + +int DrawViewShell::GetTabLayerCount() const +{ + const LayerTabBar* pBar + = const_cast(this)->GetLayerTabControl (); + if (pBar != nullptr) + return pBar->GetPageCount(); + else + return 0; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews6.cxx b/sd/source/ui/view/drviews6.cxx new file mode 100644 index 000000000..7d85151f7 --- /dev/null +++ b/sd/source/ui/view/drviews6.cxx @@ -0,0 +1,339 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace sd { + +/** + * handle SfxRequests for FontWork + */ +void DrawViewShell::ExecFormText(SfxRequest& rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 && rReq.GetArgs() && + !mpDrawView->IsPresObjSelected() ) + { + const SfxItemSet& rSet = *rReq.GetArgs(); + + if ( mpDrawView->IsTextEdit() ) + mpDrawView->SdrEndTextEdit(); + + mpDrawView->SetAttributes(rSet); + } +} + +/** + * Return state values for FontWork + */ +void DrawViewShell::GetFormTextState(SfxItemSet& rSet) +{ + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const SdrObject* pObj = nullptr; + + if ( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + const SdrTextObj* pTextObj = dynamic_cast< const SdrTextObj* >(pObj); + const bool bDeactivate( + !pObj || + !pTextObj || + !pTextObj->HasText() || + dynamic_cast< const SdrObjCustomShape* >(pObj)); // #121538# no FontWork for CustomShapes + + if(bDeactivate) + { +// automatic open/close the FontWork-Dialog; first deactivate it + + rSet.DisableItem(XATTR_FORMTXTSTYLE); + rSet.DisableItem(XATTR_FORMTXTADJUST); + rSet.DisableItem(XATTR_FORMTXTDISTANCE); + rSet.DisableItem(XATTR_FORMTXTSTART); + rSet.DisableItem(XATTR_FORMTXTMIRROR); + rSet.DisableItem(XATTR_FORMTXTHIDEFORM); + rSet.DisableItem(XATTR_FORMTXTOUTLINE); + rSet.DisableItem(XATTR_FORMTXTSHADOW); + rSet.DisableItem(XATTR_FORMTXTSHDWCOLOR); + rSet.DisableItem(XATTR_FORMTXTSHDWXVAL); + rSet.DisableItem(XATTR_FORMTXTSHDWYVAL); + } + else + { + SfxItemSet aSet( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aSet ); + rSet.Set( aSet ); + } +} + +void DrawViewShell::ExecAnimationWin( SfxRequest& rReq ) +{ + // nothing is executed during a slide show! + if (HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_ANIMATOR_INIT: + case SID_ANIMATOR_ADD: + case SID_ANIMATOR_CREATE: + { + AnimationWindow* pAnimWin; + sal_uInt16 nId = AnimationChildWindow::GetChildWindowId(); + + SfxChildWindow* pWnd = GetViewFrame()->GetChildWindow(nId); + + pAnimWin = pWnd ? static_cast(pWnd->GetWindow()) : nullptr; + + if ( pAnimWin ) + { + if( nSId == SID_ANIMATOR_ADD ) + pAnimWin->AddObj( *mpDrawView ); + else if( nSId == SID_ANIMATOR_CREATE ) + pAnimWin->CreateAnimObj( *mpDrawView ); + } + } + break; + + default: + break; + } +} + +/** + * Return status values for animator + * + * nValue == 0 -> No button + * nValue == 1 -> Button 'accept' + * nValue == 2 -> Button 'accept individually' + * nValue == 3 -> Buttons 'accept' and 'accept individually' + */ +void DrawViewShell::GetAnimationWinState( SfxItemSet& rSet ) +{ + // here we could disable buttons etc. + sal_uInt16 nValue; + + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + + if( nMarkCount == 0 ) + nValue = 0; + else if( nMarkCount > 1 ) + nValue = 3; + else // 1 Object + { + const SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + // 1 selected group object + if( nInv == SdrInventor::Default && nId == SdrObjKind::Group ) + nValue = 3; + else if( nInv == SdrInventor::Default && nId == SdrObjKind::Graphic ) // Animated GIF ? + { + sal_uInt16 nCount = 0; + + if( static_cast(pObj)->IsAnimated() ) + nCount = static_cast(pObj)->GetGraphic().GetAnimation().Count(); + if( nCount > 0 ) + nValue = 2; + else + nValue = 1; + } + else + nValue = 1; + } + rSet.Put( SfxUInt16Item( SID_ANIMATOR_STATE, nValue ) ); +} + +void DrawViewShell::SetChildWindowState( SfxItemSet& rSet ) +{ + // State of SfxChild-Windows (Animator, Fontwork etc.) + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_FONTWORK ) ) + { + sal_uInt16 nId = SvxFontWorkChildWindow::GetChildWindowId(); + rSet.Put(SfxBoolItem(SID_FONTWORK, GetViewFrame()->HasChildWindow(nId))); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_COLOR_CONTROL ) ) + { + sal_uInt16 nId = SvxColorChildWindow::GetChildWindowId(); + rSet.Put(SfxBoolItem(SID_COLOR_CONTROL, GetViewFrame()->HasChildWindow(nId))); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ANIMATION_OBJECTS ) ) + { + sal_uInt16 nId = AnimationChildWindow::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_ANIMATION_OBJECTS, GetViewFrame()->HasChildWindow( nId ) ) ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_NAVIGATOR ) ) + { + rSet.Put( SfxBoolItem( SID_NAVIGATOR, GetViewFrame()->HasChildWindow( SID_NAVIGATOR ) ) ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_BMPMASK ) ) + { + sal_uInt16 nId = SvxBmpMaskChildWindow::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_BMPMASK, GetViewFrame()->HasChildWindow( nId ) ) ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_IMAP ) ) + { + sal_uInt16 nId = SvxIMapDlgChildWindow::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_IMAP, GetViewFrame()->HasChildWindow( nId ) ) ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_3D_WIN ) ) + { + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_3D_WIN, GetViewFrame()->HasChildWindow( nId ) ) ); + } +#if HAVE_FEATURE_AVMEDIA + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_AVMEDIA_PLAYER ) ) + { + sal_uInt16 nId = ::avmedia::MediaPlayer::GetChildWindowId(); + rSet.Put( SfxBoolItem( SID_AVMEDIA_PLAYER, GetViewFrame()->HasChildWindow( nId ) ) ); + } +#endif +} + +/** + * Handle SfxRequests for pipette + */ +void DrawViewShell::ExecBmpMask( SfxRequest const & rReq ) +{ + // nothing is executed during a slide show! + if (HasCurrentFunction(SID_PRESENTATION)) + return; + + switch ( rReq.GetSlot() ) + { + case SID_BMPMASK_PIPETTE : + { + mbPipette = static_cast( rReq.GetArgs()-> + Get( SID_BMPMASK_PIPETTE ) ).GetValue(); + } + break; + + case SID_BMPMASK_EXEC : + { + SdrGrafObj* pObj = nullptr; + if( mpDrawView && mpDrawView->GetMarkedObjectList().GetMarkCount() ) + pObj = dynamic_cast< SdrGrafObj* >( mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj() ); + + if ( pObj && !mpDrawView->IsTextEdit() ) + { + typedef std::unique_ptr< SdrGrafObj, SdrObjectFreeOp > SdrGrafObjPtr; + SdrGrafObjPtr xNewObj(pObj->CloneSdrObject(pObj->getSdrModelFromSdrObject())); + bool bCont = true; + + if (xNewObj->IsLinkedGraphic()) + { + std::unique_ptr xBuilder(Application::CreateBuilder(GetFrameWeld(), "modules/sdraw/ui/queryunlinkimagedialog.ui")); + std::unique_ptr xQueryBox(xBuilder->weld_message_dialog("QueryUnlinkImageDialog")); + + if (RET_YES == xQueryBox->run()) + xNewObj->ReleaseGraphicLink(); + else + bCont = false; + } + + SfxChildWindow* pWnd = GetViewFrame()->GetChildWindow( + SvxBmpMaskChildWindow::GetChildWindowId()); + SvxBmpMask* pBmpMask = pWnd ? static_cast(pWnd->GetWindow()) : nullptr; + assert(pBmpMask); + if (bCont && pBmpMask) + { + const Graphic& rOldGraphic = xNewObj->GetGraphic(); + const Graphic aNewGraphic(pBmpMask->Mask(rOldGraphic)); + + if( aNewGraphic != rOldGraphic ) + { + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + + xNewObj->SetEmptyPresObj(false); + xNewObj->SetGraphic(pBmpMask->Mask(xNewObj->GetGraphic())); + + OUString aStr = mpDrawView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_EYEDROPPER); + + mpDrawView->BegUndo( aStr ); + mpDrawView->ReplaceObjectAtView(pObj, *pPV, xNewObj.release()); + mpDrawView->EndUndo(); + } + } + } + } + break; + + default: + break; + } +} + +void DrawViewShell::GetBmpMaskState( SfxItemSet& rSet ) +{ + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const SdrObject* pObj = nullptr; + bool bEnable = false; + + if ( rMarkList.GetMarkCount() == 1 ) + pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + + // valid graphic object? + if( auto pGrafObj = dynamic_cast< const SdrGrafObj *>( pObj ) ) + if (!pGrafObj->IsEPS() && !mpDrawView->IsTextEdit() ) + bEnable = true; + + // put value + rSet.Put( SfxBoolItem( SID_BMPMASK_EXEC, bEnable ) ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews7.cxx b/sd/source/ui/view/drviews7.cxx new file mode 100644 index 000000000..4f375dc6a --- /dev/null +++ b/sd/source/ui/view/drviews7.cxx @@ -0,0 +1,1991 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #UndoRedo# +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +/** Create a list of clipboard formats that are supported both from the + current clipboard content and the DrawViewShell. + The list is stored in a new instance of SvxClipboardFormatItem. +*/ +static ::std::unique_ptr GetSupportedClipboardFormats ( + TransferableDataHelper& rDataHelper) +{ + ::std::unique_ptr pResult ( + new SvxClipboardFormatItem(SID_CLIPBOARD_FORMAT_ITEMS)); + + sal_uInt32 nFormatCount (rDataHelper.GetFormatCount()); + for (sal_uInt32 i=0; iAddClipbrdFormat(nTestFormat, sName); + else + pResult->AddClipbrdFormat(nTestFormat); + + break; + } + + + case SotClipboardFormatId::LINK_SOURCE: + case SotClipboardFormatId::DRAWING: + case SotClipboardFormatId::SVXB: + case SotClipboardFormatId::GDIMETAFILE: + case SotClipboardFormatId::BITMAP: + case SotClipboardFormatId::NETSCAPE_BOOKMARK: + case SotClipboardFormatId::STRING: + case SotClipboardFormatId::HTML: + case SotClipboardFormatId::RTF: + case SotClipboardFormatId::RICHTEXT: + case SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT: + pResult->AddClipbrdFormat(nTestFormat); + break; + default: break; + } + } + } + + // Check some OLE formats whose names are handled differently. + SotClipboardFormatId nFormat (SotClipboardFormatId::EMBED_SOURCE_OLE); + bool bHasFormat (rDataHelper.HasFormat(nFormat)); + if ( ! bHasFormat) + { + bHasFormat = rDataHelper.HasFormat(nFormat); + } + if (bHasFormat) + { + OUString sName; + OUString sSource; + if (SvPasteObjectHelper::GetEmbeddedName (rDataHelper, sName, sSource, nFormat)) + pResult->AddClipbrdFormat (nFormat, sName); + } + + return pResult; +} + +namespace sd { + +IMPL_LINK( DrawViewShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void ) +{ + mbPastePossible = ( pDataHelper->GetFormatCount() != 0 ); + + // Update the list of supported clipboard formats according to the + // new clipboard content. + // There are some stack traces that indicate the possibility of the + // DrawViewShell destructor called during the call to + // GetSupportedClipboardFormats(). If that really has happened then + // exit immediately. + TransferableDataHelper aDataHelper ( + TransferableDataHelper::CreateFromSystemClipboard(GetActiveWindow())); + ::std::unique_ptr pFormats (GetSupportedClipboardFormats(aDataHelper)); + if (mpDrawView == nullptr) + return; + mpCurrentClipboardFormats = std::move(pFormats); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_PASTE ); + rBindings.Invalidate( SID_PASTE_SPECIAL ); + rBindings.Invalidate( SID_PASTE_UNFORMATTED ); + rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS ); +} + +void DrawViewShell::GetDrawAttrState(SfxItemSet& rSet) +{ + SfxItemSet aSet( mpDrawView->GetGeoAttrFromMarked() ); + rSet.Put(aSet,false); +} + +::Outliner* DrawViewShell::GetOutlinerForMasterPageOutlineTextObj(ESelection &rSel) +{ + if( !mpDrawView ) + return nullptr; + + //when there is one object selected + if (!mpDrawView->AreObjectsMarked() || (mpDrawView->GetMarkedObjectList().GetMarkCount() != 1)) + return nullptr; + + //and we are editing the outline object + if (!mpDrawView->IsTextEdit()) + return nullptr; + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + if (!pPageView) + return nullptr; + + SdPage* pPage = static_cast(pPageView->GetPage()); + //only show these in a normal master page + if (!pPage || (pPage->GetPageKind() != PageKind::Standard) || !pPage->IsMasterPage()) + return nullptr; + + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + ::Outliner* pOL = pOLV ? pOLV->GetOutliner() : nullptr; + if (!pOL) + return nullptr; + rSel = pOLV->GetSelection(); + + return pOL; +} + +void DrawViewShell::GetMarginProperties( SfxItemSet &rSet ) +{ + SdPage *pPage = getCurrentPage(); + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while ( nWhich ) + { + switch ( nWhich ) + { + case SID_ATTR_PAGE_LRSPACE: + { + // const SvxLRSpaceItem aTmpPageLRSpace ( rDesc.GetMaster().GetLRSpace() ); + const SvxLongLRSpaceItem aLongLR( + static_cast<::tools::Long>(pPage->GetLeftBorder()), + static_cast<::tools::Long>(pPage->GetRightBorder()), + SID_ATTR_PAGE_LRSPACE ); + rSet.Put( aLongLR ); + } + break; + + case SID_ATTR_PAGE_ULSPACE: + { + // const SvxULSpaceItem aUL( rDesc.GetMaster().GetULSpace() ); + SvxLongULSpaceItem aLongUL( + static_cast<::tools::Long>(pPage->GetUpperBorder()), + static_cast<::tools::Long>(pPage->GetLowerBorder()), + SID_ATTR_PAGE_ULSPACE ); + rSet.Put( aLongUL ); + } + break; + + default: + break; + } + nWhich = aIter.NextWhich(); + } +} + +bool DrawViewShell::ShouldDisableEditHyperlink() const +{ + if (!mpDrawView) + return true; + if (!mpDrawView->AreObjectsMarked()) + return true; + if (mpDrawView->GetMarkedObjectList().GetMarkCount() != 1) + return true; + + bool bDisableEditHyperlink = true; + if( mpDrawView->IsTextEdit() ) + { + if (URLFieldHelper::IsCursorAtURLField(mpDrawView->GetTextEditOutlinerView())) + bDisableEditHyperlink = false; + } + else + { + SdrUnoObj* pUnoCtrl = dynamic_cast( mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj() ); + + if ( pUnoCtrl && SdrInventor::FmForm == pUnoCtrl->GetObjInventor() ) + { + const uno::Reference< awt::XControlModel >& xControlModel( pUnoCtrl->GetUnoControlModel() ); + if( xControlModel.is() ) + { + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY ); + if( xPropSet.is() ) + { + uno::Reference< beans::XPropertySetInfo > xPropInfo( xPropSet->getPropertySetInfo() ); + if( xPropInfo.is() && xPropInfo->hasPropertyByName( "TargetURL") ) + { + bDisableEditHyperlink = false; + } + } + } + } + } + return bDisableEditHyperlink; +} + +void DrawViewShell::GetMenuState( SfxItemSet &rSet ) +{ + if (mpDrawView == nullptr) + { + // This assertion and return are here to prevent crashes. + DBG_ASSERT(mpDrawView!=nullptr, "Please report this assertion to the Impress team."); + return; + } + + ViewShell::GetMenuState(rSet); + bool bDisableVerticalText = !SvtCJKOptions::IsVerticalTextEnabled(); + + if ( bDisableVerticalText ) + { + rSet.DisableItem( SID_DRAW_FONTWORK_VERTICAL ); + rSet.DisableItem( SID_DRAW_CAPTION_VERTICAL ); + rSet.DisableItem( SID_TEXT_FITTOSIZE_VERTICAL ); + rSet.DisableItem( SID_DRAW_TEXT_VERTICAL ); + } + + bool bConvertToPathPossible = mpDrawView->IsConvertToPathObjPossible(); + + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + + if( nMarkCount == 1 ) + { + bool bDisable = true; + SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + if( auto pGrafObj = dynamic_cast( pObj) ) + if( pGrafObj->getQrCode() ) + bDisable = false; + if(bDisable) + { + rSet.DisableItem(SID_EDIT_QRCODE); + } + } + + //format paintbrush + FuFormatPaintBrush::GetMenuState( *this, rSet ); + + // State of SfxChild-Windows (Animator, Fontwork etc.) + SetChildWindowState( rSet ); + + if(HasCurrentFunction()) + { + sal_uInt16 nSId = GetCurrentFunction()->GetSlotID(); + rSet.Put( SfxBoolItem( nSId, true ) ); + } + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + GetMenuStateSel(rSet); + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_ASSIGN_LAYOUT)) + { + bool bDisable = true; + if( pPageView ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pPageView->GetPage() ); + + if( pPage && !pPage->IsMasterPage() ) + { + rSet.Put( SfxUInt32Item( SID_ASSIGN_LAYOUT, static_cast< sal_uInt32 >(pPage->GetAutoLayout()) ) ); + bDisable = false; + } + } + + if(bDisable) + { + rSet.DisableItem(SID_ASSIGN_LAYOUT); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_EXPAND_PAGE)) + { + bool bDisable = true; + if( pPageView ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pPageView->GetPage() ); + + if( pPage && (pPage->GetPageKind() == PageKind::Standard) && !pPage->IsMasterPage() ) + { + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Outline); + + if (pObj!=nullptr ) + { + if( !pObj->IsEmptyPresObj() ) + { + bDisable = false; + } + else + { + // check if the object is in edit, then if it's temporarily not empty + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + if( pTextObj ) + { + if( pTextObj->CanCreateEditOutlinerParaObject() ) + { + bDisable = false; + } + } + } + } + } + } + + if(bDisable) + { + rSet.DisableItem(SID_EXPAND_PAGE); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_SUMMARY_PAGE)) + { + bool bDisable = true; + if( pPageView ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pPageView->GetPage() ); + + if( pPage && (pPage->GetPageKind() == PageKind::Standard) && !pPage->IsMasterPage() ) + { + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Title); + + if(pObj && !pObj->IsEmptyPresObj()) + { + bDisable = false; + } + } + } + + if(bDisable) + { + rSet.DisableItem(SID_SUMMARY_PAGE); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_ASSIGN_LAYOUT)) + { + bool bDisable = true; + if( pPageView ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pPageView->GetPage() ); + + if( pPage && !pPage->IsMasterPage() ) + { + rSet.Put( SfxUInt32Item(SID_ASSIGN_LAYOUT, pPage->GetAutoLayout()) ); + bDisable = false; + } + } + + if(bDisable) + { + rSet.DisableItem(SID_ASSIGN_LAYOUT); + } + } + + // is it possible to start the presentation? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PRESENTATION ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_REHEARSE_TIMINGS ) ) + { + bool bDisable = true; + sal_uInt16 nCount = GetDoc()->GetSdPageCount( PageKind::Standard ); + + for( sal_uInt16 i = 0; i < nCount && bDisable; i++ ) + { + SdPage* pPage = GetDoc()->GetSdPage(i, PageKind::Standard); + + if( !pPage->IsExcluded() ) + bDisable = false; + } + + if( bDisable || GetDocSh()->IsPreview()) + { + rSet.DisableItem( SID_PRESENTATION ); + rSet.DisableItem( SID_REHEARSE_TIMINGS ); + } + } + + // gluepoints + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_EDITMODE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_INSERT_POINT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_PERCENT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR_LEFT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR_RIGHT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR_TOP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_ESCDIR_BOTTOM ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_HORZALIGN_CENTER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_HORZALIGN_LEFT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_HORZALIGN_RIGHT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_VERTALIGN_CENTER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_VERTALIGN_TOP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_GLUE_VERTALIGN_BOTTOM ) ) + { + // percent + TriState eState = mpDrawView->IsMarkedGluePointsPercent(); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_PERCENT ); + else + rSet.Put( SfxBoolItem( SID_GLUE_PERCENT, eState == TRISTATE_TRUE ) ); + + // alignment has no effect by percent + if( eState == TRISTATE_TRUE ) + { + rSet.DisableItem( SID_GLUE_HORZALIGN_CENTER ); + rSet.DisableItem( SID_GLUE_HORZALIGN_LEFT ); + rSet.DisableItem( SID_GLUE_HORZALIGN_RIGHT ); + rSet.DisableItem( SID_GLUE_VERTALIGN_CENTER ); + rSet.DisableItem( SID_GLUE_VERTALIGN_TOP ); + rSet.DisableItem( SID_GLUE_VERTALIGN_BOTTOM ); + } + else + { + // horizontal alignment + SdrAlign nHorz = mpDrawView->GetMarkedGluePointsAlign( false ); + rSet.Put( SfxBoolItem( SID_GLUE_HORZALIGN_CENTER, nHorz == SdrAlign::HORZ_CENTER ) ); + rSet.Put( SfxBoolItem( SID_GLUE_HORZALIGN_LEFT, nHorz == SdrAlign::HORZ_LEFT ) ); + rSet.Put( SfxBoolItem( SID_GLUE_HORZALIGN_RIGHT, nHorz == SdrAlign::HORZ_RIGHT ) ); + // vertical alignment + SdrAlign nVert = mpDrawView->GetMarkedGluePointsAlign( true ); + rSet.Put( SfxBoolItem( SID_GLUE_VERTALIGN_CENTER, nVert == SdrAlign::VERT_CENTER ) ); + rSet.Put( SfxBoolItem( SID_GLUE_VERTALIGN_TOP, nVert == SdrAlign::VERT_TOP ) ); + rSet.Put( SfxBoolItem( SID_GLUE_VERTALIGN_BOTTOM, nVert == SdrAlign::VERT_BOTTOM ) ); + } + + // insert point + rSet.Put( SfxBoolItem( SID_GLUE_INSERT_POINT, mpDrawView->IsInsGluePointMode() ) ); + + // Escape direction + // left + eState = mpDrawView->IsMarkedGluePointsEscDir( SdrEscapeDirection::LEFT ); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_ESCDIR_LEFT ); + else + rSet.Put( SfxBoolItem( SID_GLUE_ESCDIR_LEFT, eState == TRISTATE_TRUE ) ); + // right + eState = mpDrawView->IsMarkedGluePointsEscDir( SdrEscapeDirection::RIGHT ); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_ESCDIR_RIGHT ); + else + rSet.Put( SfxBoolItem( SID_GLUE_ESCDIR_RIGHT, eState == TRISTATE_TRUE ) ); + // top + eState = mpDrawView->IsMarkedGluePointsEscDir( SdrEscapeDirection::TOP ); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_ESCDIR_TOP ); + else + rSet.Put( SfxBoolItem( SID_GLUE_ESCDIR_TOP, eState == TRISTATE_TRUE ) ); + // bottom + eState = mpDrawView->IsMarkedGluePointsEscDir( SdrEscapeDirection::BOTTOM ); + if( eState == TRISTATE_INDET ) + rSet.InvalidateItem( SID_GLUE_ESCDIR_BOTTOM ); + else + rSet.Put( SfxBoolItem( SID_GLUE_ESCDIR_BOTTOM, eState == TRISTATE_TRUE ) ); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_GRID_FRONT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_HELPLINES_FRONT ) ) + { + rSet.Put( SfxBoolItem( SID_GRID_FRONT, mpDrawView->IsGridFront() ) ); + rSet.Put( SfxBoolItem( SID_HELPLINES_FRONT, mpDrawView->IsHlplFront() ) ); + } + + if (!mpDrawView->IsFrameDragSingles()) + rSet.Put(SfxBoolItem(SID_BEZIER_EDIT, true)); + else + rSet.Put(SfxBoolItem(SID_BEZIER_EDIT, false)); + + if(dynamic_cast( GetCurrentFunction().get())) + rSet.Put(SfxBoolItem(SID_GLUE_EDITMODE, true)); + else + rSet.Put(SfxBoolItem(SID_GLUE_EDITMODE, false)); + + if( !mpDrawView->IsMirrorAllowed( true, true ) ) + { + rSet.DisableItem( SID_HORIZONTAL ); + rSet.DisableItem( SID_VERTICAL ); + rSet.DisableItem( SID_FLIP_HORIZONTAL ); + rSet.DisableItem( SID_FLIP_VERTICAL ); + } + + if( !mpDrawView->IsMirrorAllowed() ) + { + rSet.DisableItem( SID_OBJECT_MIRROR ); +// rSet.DisableItem( SID_CONVERT_TO_3D_LATHE ); +// rSet.DisableItem( SID_CONVERT_TO_3D_LATHE_FAST ); + } + + // interactive transparence control + if(!mpDrawView->IsTransparenceAllowed()) + { + rSet.DisableItem( SID_OBJECT_TRANSPARENCE ); + } + + // interactive gradient control + if(!mpDrawView->IsGradientAllowed()) + { + rSet.DisableItem( SID_OBJECT_GRADIENT ); + } + + // disable morphing if necessary + if ( !mpDrawView->IsMorphingAllowed() ) + rSet.DisableItem( SID_POLYGON_MORPHING ); + + if( !mpDrawView->IsReverseOrderPossible() ) + { + rSet.DisableItem( SID_REVERSE_ORDER ); + } + + if ( !bConvertToPathPossible && + !mpDrawView->IsCrookAllowed( mpDrawView->IsCrookNoContortion() ) ) + { + // implicit transformation into curve not possible + rSet.DisableItem(SID_OBJECT_CROOK_ROTATE); + rSet.DisableItem(SID_OBJECT_CROOK_SLANT); + rSet.DisableItem(SID_OBJECT_CROOK_STRETCH); + } + + if ( !mpDrawView->IsGroupEntered() ) + { + rSet.DisableItem( SID_LEAVE_GROUP ); + rSet.Put( SfxBoolItem( SID_LEAVE_ALL_GROUPS, false ) ); + rSet.ClearItem( SID_LEAVE_ALL_GROUPS ); + rSet.DisableItem( SID_LEAVE_ALL_GROUPS ); + } + else + rSet.Put( SfxBoolItem( SID_LEAVE_ALL_GROUPS, true ) ); + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_THESAURUS ) ) + { + if ( !mpDrawView->IsTextEdit() ) + { + rSet.DisableItem( SID_THESAURUS ); + } + else + { + LanguageType eLang = GetDoc()->GetLanguage( EE_CHAR_LANGUAGE ); + Reference< XThesaurus > xThesaurus( LinguMgr::GetThesaurus() ); + + if (!xThesaurus.is() || eLang == LANGUAGE_NONE || !xThesaurus->hasLocale( LanguageTag::convertToLocale( eLang)) ) + rSet.DisableItem( SID_THESAURUS ); + } + } + + if ( !mpDrawView->IsTextEdit() ) + { + rSet.DisableItem( SID_THESAURUS ); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_SELECTALL ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_SIZE_ALL ) ) + { + if( pPageView && pPageView->GetObjList()->GetObjCount() == 0 ) + { + // should be disabled if there is no object on the draw area: + rSet.DisableItem( SID_SELECTALL ); + rSet.DisableItem( SID_SIZE_ALL ); + } + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_CONTEXT ) ) + rSet.Put( SfxStringItem( SID_CONTEXT, mpDrawView->GetStatusText() ) ); + + // clipboard (paste) + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PASTE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_PASTE_SPECIAL ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_PASTE_UNFORMATTED ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CLIPBOARD_FORMAT_ITEMS ) ) + { + if ( !mxClipEvtLstnr.is() ) + { + // avoid clipboard initialization for + // read-only presentation views (workaround for NT4.0 + // clipboard prob...) + if( dynamic_cast< const PresentationViewShell *>( this ) == nullptr ) + { + // create listener + mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, DrawViewShell, ClipboardChanged ) ); + mxClipEvtLstnr->AddListener( GetActiveWindow() ); + + // get initial state + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + mbPastePossible = ( aDataHelper.GetFormatCount() != 0 ); + mpCurrentClipboardFormats = GetSupportedClipboardFormats( aDataHelper ); + } + else + mbPastePossible = false; + } + + if( !mbPastePossible ) + { + rSet.DisableItem( SID_PASTE ); + rSet.DisableItem( SID_PASTE_SPECIAL ); + rSet.DisableItem( SID_PASTE_UNFORMATTED ); + rSet.DisableItem( SID_CLIPBOARD_FORMAT_ITEMS ); + } + else if( SfxItemState::DEFAULT == rSet.GetItemState( SID_CLIPBOARD_FORMAT_ITEMS ) ) + { + if (mpCurrentClipboardFormats != nullptr) + rSet.Put(*mpCurrentClipboardFormats); + } + } + + if ( !bConvertToPathPossible ) + { + rSet.DisableItem(SID_CHANGEBEZIER); + } + + if (mpDrawView == nullptr) + { + // The mpDrawView was not NULL but is now. + // The reason for this may be that the DrawViewShell has been + // destroyed in the meantime. + // We can only return immediately and hope that the deleted + // DrawViewShell is not called again. + DBG_ASSERT(mpDrawView!=nullptr, "Please report this assertion to the Impress team."); + return; + } + + if( !( mpDrawView->IsConvertToPolyObjPossible() || mpDrawView->IsVectorizeAllowed() ) ) + rSet.DisableItem(SID_CHANGEPOLYGON); + + if( !( mpDrawView->IsConvertToPolyObjPossible() || mpDrawView->IsConvertToContourPossible() ) ) + rSet.DisableItem(SID_CONVERT_TO_CONTOUR); + + if ( !mpDrawView->IsConvertTo3DObjPossible() ) + { + rSet.DisableItem(SID_CONVERT_TO_3D); + rSet.DisableItem(SID_CONVERT_TO_3D_LATHE); + rSet.DisableItem(SID_CONVERT_TO_3D_LATHE_FAST); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_MANAGE_LINKS ) ) + { + if ( GetDoc()->GetLinkCount() == 0 ) + { + rSet.DisableItem(SID_MANAGE_LINKS); + } + } + + if (mePageKind == PageKind::Handout) + { + rSet.DisableItem(SID_PRESENTATION_LAYOUT); + rSet.DisableItem(SID_SELECT_BACKGROUND); + rSet.DisableItem(SID_SAVE_BACKGROUND); + } + + if (mePageKind == PageKind::Notes) + { + rSet.DisableItem(SID_INSERTPAGE); + rSet.DisableItem(SID_RENAMEPAGE); + rSet.DisableItem(SID_RENAMEPAGE_QUICK); + rSet.DisableItem(SID_DUPLICATE_PAGE); + rSet.ClearItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_EFFECTS); + rSet.DisableItem(SID_EXECUTE_ANIMATION_EFFECT); + + if (meEditMode == EditMode::MasterPage) + rSet.DisableItem(SID_MODIFYPAGE); + + rSet.DisableItem(SID_SELECT_BACKGROUND); + rSet.DisableItem(SID_SAVE_BACKGROUND); + rSet.DisableItem(SID_INSERTLAYER); + rSet.DisableItem(SID_LAYERMODE); + rSet.DisableItem(SID_INSERTFILE); + } + else if (mePageKind == PageKind::Handout) + { + rSet.DisableItem(SID_INSERTPAGE); + rSet.DisableItem(SID_DUPLICATE_PAGE); + rSet.ClearItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_EFFECTS); + rSet.DisableItem(SID_EXECUTE_ANIMATION_EFFECT); + rSet.DisableItem(SID_RENAMEPAGE); + rSet.DisableItem(SID_RENAMEPAGE_QUICK); + rSet.DisableItem(SID_INSERTLAYER); + rSet.DisableItem(SID_MODIFYLAYER); + rSet.DisableItem(SID_RENAMELAYER); + rSet.DisableItem(SID_LAYERMODE); + rSet.DisableItem(SID_INSERTFILE); + rSet.DisableItem(SID_PAGEMODE); + rSet.DisableItem(SID_SELECT_BACKGROUND); + rSet.DisableItem(SID_SAVE_BACKGROUND); + } + else + { + if (meEditMode == EditMode::MasterPage) + { + rSet.DisableItem(SID_INSERTPAGE); + rSet.DisableItem(SID_DUPLICATE_PAGE); + rSet.DisableItem(SID_MODIFYPAGE); + rSet.ClearItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_ANIMATION_OBJECTS); + } + + rSet.Put (SfxBoolItem (SID_LAYERMODE, IsLayerModeActive())); + } + + if ( ! IsLayerModeActive()) + { + rSet.DisableItem( SID_INSERTLAYER ); + rSet.DisableItem( SID_MODIFYLAYER ); + rSet.DisableItem( SID_DELETE_LAYER ); + rSet.DisableItem( SID_RENAMELAYER ); + } + + if (meEditMode == EditMode::Page) + { + /********************************************************************** + * page mode + **********************************************************************/ + rSet.Put(SfxBoolItem(SID_PAGEMODE, true)); + rSet.Put(SfxBoolItem(SID_MASTERPAGE, false)); + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + + rSet.DisableItem (SID_INSERT_MASTER_PAGE); + rSet.DisableItem (SID_DELETE_MASTER_PAGE); + rSet.DisableItem (SID_RENAME_MASTER_PAGE); + rSet.DisableItem (SID_CLOSE_MASTER_VIEW); + } + else + { + rSet.Put(SfxBoolItem(SID_PAGEMODE, false)); + rSet.Put(SfxBoolItem(SID_MASTERPAGE, true)); + + /********************************************************************** + * Background page mode + **********************************************************************/ + if (mePageKind == PageKind::Standard) + { + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, true)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + + } + else if (mePageKind == PageKind::Notes) + { + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, true)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + } + else if (mePageKind == PageKind::Handout) + { + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, true)); + } + } + + // set state of the ruler + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_RULER ) ) + rSet.Put( SfxBoolItem( SID_RULER, HasRuler() ) ); + + // do not delete the last page or a master page + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_DELETE_PAGE ) + || SfxItemState::DEFAULT == rSet.GetItemState( SID_DELETE_MASTER_PAGE ) ) + { + if (maTabControl->GetPageCount() == 1 || + meEditMode == EditMode::MasterPage || + mePageKind == PageKind::Notes || + mePageKind == PageKind::Handout || + (GetShellType()!=ST_DRAW&&IsLayerModeActive())) + { + if (rSet.GetItemState(SID_DELETE_PAGE) == SfxItemState::DEFAULT) + rSet.DisableItem(SID_DELETE_PAGE); + if (rSet.GetItemState(SID_DELETE_MASTER_PAGE)==SfxItemState::DEFAULT) + rSet.DisableItem(SID_DELETE_MASTER_PAGE); + } + } + + // is it allowed to delete the current layer? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_DELETE_LAYER ) + || SfxItemState::DEFAULT == rSet.GetItemState( SID_RENAMELAYER ) ) + { + if(GetLayerTabControl()) // #i87182# + { + sal_uInt16 nCurrentLayer = GetLayerTabControl()->GetCurPageId(); + const OUString& rName = GetLayerTabControl()->GetLayerName(nCurrentLayer); + + if (!IsLayerModeActive() || LayerTabBar::IsRealNameOfStandardLayer(rName)) + { + rSet.DisableItem(SID_DELETE_LAYER); + rSet.DisableItem(SID_RENAMELAYER); + } + } + else + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + } + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_CUT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_COPY ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTLINE_BULLET )) + { + OutlinerView* pOlView = mpDrawView->GetTextEditOutlinerView(); + + // special treatment of for SID_OUTLINE_BULLET if objects with different + // kinds of NumBullets are marked + bool bHasOutliner = false; + bool bHasOther = false; + for(size_t nNum = 0; nNum < nMarkCount; ++nNum) + { + SdrObject* pObj = rMarkList.GetMark(nNum)->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + if( pObj->GetObjIdentifier() == SdrObjKind::OutlineText ) + { + bHasOutliner = true; + if(bHasOther) + break; + } + else + { + bHasOther = true; + if(bHasOutliner) + break; + } + } + } + + if( bHasOther && bHasOutliner ) + rSet.DisableItem( SID_OUTLINE_BULLET ); + + if (pOlView) + { + if (pOlView->GetSelected().isEmpty() || GetObjectShell()->isContentExtractionLocked()) + { + rSet.DisableItem( SID_CUT ); + rSet.DisableItem( SID_COPY ); + } + } + + } + + FuBullet::GetSlotState( rSet, this, GetViewFrame() ); + + if ( GetDocSh()->IsUIActive() ) + { + rSet.DisableItem( SID_INSERT_OBJECT ); + rSet.DisableItem( SID_INSERT_FLOATINGFRAME ); + rSet.DisableItem( SID_INSERT_MATH ); + rSet.DisableItem( SID_INSERT_DIAGRAM ); + rSet.DisableItem( SID_ATTR_TABLE ); + rSet.DisableItem( SID_SIZE_REAL ); + rSet.DisableItem( SID_SIZE_OPTIMAL ); + rSet.DisableItem( SID_SIZE_ALL ); + rSet.DisableItem( SID_SIZE_PAGE_WIDTH ); + rSet.DisableItem( SID_SIZE_PAGE ); + rSet.DisableItem( SID_DUPLICATE_PAGE ); + rSet.DisableItem( SID_ZOOM_TOOLBOX ); + } + + // Zoom-State + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_IN ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_OUT )|| + SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_PANNING ) ) + { + if( GetActiveWindow()->GetZoom() <= GetActiveWindow()->GetMinZoom() || GetDocSh()->IsUIActive() ) + { + rSet.DisableItem( SID_ZOOM_OUT ); + rSet.DisableItem( SID_ZOOM_PANNING ); + } + if( GetActiveWindow()->GetZoom() >= GetActiveWindow()->GetMaxZoom() || GetDocSh()->IsUIActive() ) + rSet.DisableItem( SID_ZOOM_IN ); + } + + if (!mpZoomList->IsNextPossible()) + { + rSet.DisableItem(SID_ZOOM_NEXT); + } + if (!mpZoomList->IsPreviousPossible()) + { + rSet.DisableItem(SID_ZOOM_PREV); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_REMOTE_DLG ) ) + { + + bool bDisableSdremoteForGood = false; +#ifndef ENABLE_SDREMOTE + bDisableSdremoteForGood = true; +#endif + bDisableSdremoteForGood |= ! ( /*officecfg::Office::Common::Misc::ExperimentalMode::get() &&*/ + officecfg::Office::Impress::Misc::Start::EnableSdremote::get() ); + + // This dialog is only useful for TCP/IP remote control + // which is unusual, under-tested and a security issue. + if ( bDisableSdremoteForGood ) + { + rSet.Put(SfxVisibilityItem(SID_REMOTE_DLG, false)); + } + } + + // EditText active + if (GetViewShellBase().GetViewShellManager()->GetShell(ToolbarId::Draw_Text_Toolbox_Sd) != nullptr) + { + sal_uInt16 nCurrentSId = SID_ATTR_CHAR; + + if(HasCurrentFunction()) + { + nCurrentSId = GetCurrentFunction()->GetSlotID(); + } + if( nCurrentSId != SID_TEXT_FITTOSIZE && + nCurrentSId != SID_TEXT_FITTOSIZE_VERTICAL && + nCurrentSId != SID_ATTR_CHAR_VERTICAL ) + nCurrentSId = SID_ATTR_CHAR; + + rSet.Put( SfxBoolItem( nCurrentSId, true ) ); + } + + if ( GetDocSh()->IsReadOnly() ) + { + rSet.DisableItem( SID_AUTOSPELL_CHECK ); + } + else + { + if (GetDoc()->GetOnlineSpell()) + { + rSet.Put(SfxBoolItem(SID_AUTOSPELL_CHECK, true)); + } + else + { + rSet.Put(SfxBoolItem(SID_AUTOSPELL_CHECK, false)); + } + } + + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + OUString aActiveLayer = mpDrawView->GetActiveLayer(); + + if ( ( !aActiveLayer.isEmpty() && pPV && ( pPV->IsLayerLocked(aActiveLayer) || + !pPV->IsLayerVisible(aActiveLayer) ) ) || + SD_MOD()->GetWaterCan() ) + { + rSet.DisableItem( SID_PASTE ); + rSet.DisableItem( SID_PASTE_SPECIAL ); + rSet.DisableItem( SID_PASTE_UNFORMATTED ); + rSet.DisableItem( SID_CLIPBOARD_FORMAT_ITEMS ); + + rSet.DisableItem( SID_INSERT_FLD_DATE_FIX ); + rSet.DisableItem( SID_INSERT_FLD_DATE_VAR ); + rSet.DisableItem( SID_INSERT_FLD_TIME_FIX ); + rSet.DisableItem( SID_INSERT_FLD_TIME_VAR ); + rSet.DisableItem( SID_INSERT_FLD_AUTHOR ); + rSet.DisableItem( SID_INSERT_FLD_PAGE ); + rSet.DisableItem( SID_INSERT_FLD_PAGE_TITLE ); + rSet.DisableItem( SID_INSERT_FLD_PAGES ); + rSet.DisableItem( SID_INSERT_FLD_FILE ); + + rSet.DisableItem( SID_INSERT_GRAPHIC ); + rSet.DisableItem( SID_INSERT_AVMEDIA ); + rSet.DisableItem( SID_INSERT_DIAGRAM ); + rSet.DisableItem( SID_INSERT_OBJECT ); + rSet.DisableItem( SID_INSERT_FLOATINGFRAME ); + + rSet.DisableItem( SID_INSERT_MATH ); + rSet.DisableItem( SID_INSERT_FRAME ); + rSet.DisableItem( SID_INSERTFILE ); + rSet.DisableItem( SID_ATTR_TABLE ); + rSet.DisableItem( SID_COPYOBJECTS ); + + rSet.DisableItem( SID_SCAN ); + rSet.DisableItem( SID_TWAIN_SELECT ); + rSet.DisableItem( SID_TWAIN_TRANSFER ); + +// rSet.DisableItem( SID_BEZIER_EDIT ); + rSet.DisableItem( SID_GLUE_EDITMODE ); + rSet.DisableItem( SID_OBJECT_ROTATE ); + rSet.DisableItem( SID_OBJECT_SHEAR ); + rSet.DisableItem( SID_OBJECT_MIRROR ); + rSet.DisableItem( SID_OBJECT_CROP ); + rSet.DisableItem( SID_ATTR_GRAF_CROP ); + rSet.DisableItem( SID_OBJECT_TRANSPARENCE ); + rSet.DisableItem( SID_OBJECT_GRADIENT ); + rSet.DisableItem( SID_OBJECT_CROOK_ROTATE ); + rSet.DisableItem( SID_OBJECT_CROOK_SLANT ); + rSet.DisableItem( SID_OBJECT_CROOK_STRETCH ); + + // Disable all object-creating tools + rSet.ClearItem( SID_ATTR_CHAR ); + rSet.DisableItem( SID_ATTR_CHAR ); + rSet.ClearItem( SID_ATTR_CHAR_VERTICAL ); + rSet.DisableItem( SID_ATTR_CHAR_VERTICAL ); + rSet.ClearItem(SID_DRAW_LINE); + rSet.DisableItem(SID_DRAW_LINE); + rSet.ClearItem(SID_DRAW_MEASURELINE); + rSet.DisableItem(SID_DRAW_MEASURELINE); + rSet.ClearItem(SID_DRAW_XLINE); + rSet.DisableItem(SID_DRAW_XLINE); + rSet.ClearItem( SID_LINE_ARROW_START ); + rSet.DisableItem( SID_LINE_ARROW_START ); + rSet.ClearItem( SID_LINE_ARROW_END ); + rSet.DisableItem( SID_LINE_ARROW_END ); + rSet.ClearItem( SID_LINE_ARROWS ); + rSet.DisableItem( SID_LINE_ARROWS ); + rSet.ClearItem( SID_LINE_ARROW_CIRCLE ); + rSet.DisableItem( SID_LINE_ARROW_CIRCLE ); + rSet.ClearItem( SID_LINE_CIRCLE_ARROW ); + rSet.DisableItem( SID_LINE_CIRCLE_ARROW ); + rSet.ClearItem( SID_LINE_ARROW_SQUARE ); + rSet.DisableItem( SID_LINE_ARROW_SQUARE ); + rSet.ClearItem( SID_LINE_SQUARE_ARROW ); + rSet.DisableItem( SID_LINE_SQUARE_ARROW ); + + rSet.ClearItem(SID_DRAW_RECT); + rSet.DisableItem(SID_DRAW_RECT); + rSet.ClearItem(SID_DRAW_RECT_NOFILL); + rSet.DisableItem(SID_DRAW_RECT_NOFILL); + rSet.ClearItem(SID_DRAW_RECT_ROUND); + rSet.DisableItem(SID_DRAW_RECT_ROUND); + rSet.ClearItem(SID_DRAW_RECT_ROUND_NOFILL); + rSet.DisableItem(SID_DRAW_RECT_ROUND_NOFILL); + rSet.ClearItem(SID_DRAW_SQUARE); + rSet.DisableItem(SID_DRAW_SQUARE); + rSet.ClearItem(SID_DRAW_SQUARE_NOFILL); + rSet.DisableItem(SID_DRAW_SQUARE_NOFILL); + rSet.ClearItem(SID_DRAW_SQUARE_ROUND); + rSet.DisableItem(SID_DRAW_SQUARE_ROUND); + rSet.ClearItem(SID_DRAW_SQUARE_ROUND_NOFILL); + rSet.DisableItem(SID_DRAW_SQUARE_ROUND_NOFILL); + rSet.ClearItem(SID_DRAW_ELLIPSE); + rSet.DisableItem(SID_DRAW_ELLIPSE); + rSet.ClearItem(SID_DRAW_ELLIPSE_NOFILL); + rSet.DisableItem(SID_DRAW_ELLIPSE_NOFILL); + rSet.ClearItem(SID_DRAW_CIRCLE); + rSet.DisableItem(SID_DRAW_CIRCLE); + rSet.ClearItem(SID_DRAW_CIRCLE_NOFILL); + rSet.DisableItem(SID_DRAW_CIRCLE_NOFILL); + rSet.ClearItem(SID_DRAW_CAPTION); + rSet.DisableItem(SID_DRAW_CAPTION); + rSet.ClearItem(SID_DRAW_FONTWORK); + rSet.DisableItem(SID_DRAW_FONTWORK); + rSet.ClearItem(SID_DRAW_FONTWORK_VERTICAL); + rSet.DisableItem(SID_DRAW_FONTWORK_VERTICAL); + rSet.ClearItem(SID_DRAW_CAPTION_VERTICAL); + rSet.DisableItem(SID_DRAW_CAPTION_VERTICAL); + rSet.ClearItem(SID_TEXT_FITTOSIZE); + rSet.DisableItem(SID_TEXT_FITTOSIZE); + rSet.ClearItem(SID_TEXT_FITTOSIZE_VERTICAL); + rSet.DisableItem(SID_TEXT_FITTOSIZE_VERTICAL); + rSet.ClearItem(SID_TOOL_CONNECTOR); + rSet.DisableItem(SID_TOOL_CONNECTOR); + rSet.ClearItem(SID_CONNECTOR_ARROW_START); + rSet.DisableItem(SID_CONNECTOR_ARROW_START); + rSet.ClearItem(SID_CONNECTOR_ARROW_END); + rSet.DisableItem(SID_CONNECTOR_ARROW_END); + rSet.ClearItem(SID_CONNECTOR_ARROWS); + rSet.DisableItem(SID_CONNECTOR_ARROWS); + rSet.ClearItem(SID_CONNECTOR_CIRCLE_START); + rSet.DisableItem(SID_CONNECTOR_CIRCLE_START); + rSet.ClearItem(SID_CONNECTOR_CIRCLE_END); + rSet.DisableItem(SID_CONNECTOR_CIRCLE_END); + rSet.ClearItem(SID_CONNECTOR_CIRCLES); + rSet.DisableItem(SID_CONNECTOR_CIRCLES); + rSet.ClearItem(SID_CONNECTOR_LINE); + rSet.DisableItem(SID_CONNECTOR_LINE); + rSet.ClearItem(SID_CONNECTOR_LINE_ARROW_START); + rSet.DisableItem(SID_CONNECTOR_LINE_ARROW_START); + rSet.ClearItem(SID_CONNECTOR_LINE_ARROW_END); + rSet.DisableItem(SID_CONNECTOR_LINE_ARROW_END); + rSet.ClearItem(SID_CONNECTOR_LINE_ARROWS); + rSet.DisableItem(SID_CONNECTOR_LINE_ARROWS); + rSet.ClearItem(SID_CONNECTOR_LINE_CIRCLE_START); + rSet.DisableItem(SID_CONNECTOR_LINE_CIRCLE_START); + rSet.ClearItem(SID_CONNECTOR_LINE_CIRCLE_END); + rSet.DisableItem(SID_CONNECTOR_LINE_CIRCLE_END); + rSet.ClearItem(SID_CONNECTOR_LINE_CIRCLES); + rSet.DisableItem(SID_CONNECTOR_LINE_CIRCLES); + rSet.ClearItem(SID_CONNECTOR_CURVE); + rSet.DisableItem(SID_CONNECTOR_CURVE); + rSet.ClearItem(SID_CONNECTOR_CURVE_ARROW_START); + rSet.DisableItem(SID_CONNECTOR_CURVE_ARROW_START); + rSet.ClearItem(SID_CONNECTOR_CURVE_ARROW_END); + rSet.DisableItem(SID_CONNECTOR_CURVE_ARROW_END); + rSet.ClearItem(SID_CONNECTOR_CURVE_ARROWS); + rSet.DisableItem(SID_CONNECTOR_CURVE_ARROWS); + rSet.ClearItem(SID_CONNECTOR_CURVE_CIRCLE_START); + rSet.DisableItem(SID_CONNECTOR_CURVE_CIRCLE_START); + rSet.ClearItem(SID_CONNECTOR_CURVE_CIRCLE_END); + rSet.DisableItem(SID_CONNECTOR_CURVE_CIRCLE_END); + rSet.ClearItem(SID_CONNECTOR_CURVE_CIRCLES); + rSet.DisableItem(SID_CONNECTOR_CURVE_CIRCLES); + rSet.ClearItem(SID_CONNECTOR_LINES); + rSet.DisableItem(SID_CONNECTOR_LINES); + rSet.ClearItem(SID_CONNECTOR_LINES_ARROW_START); + rSet.DisableItem(SID_CONNECTOR_LINES_ARROW_START); + rSet.ClearItem(SID_CONNECTOR_LINES_ARROW_END); + rSet.DisableItem(SID_CONNECTOR_LINES_ARROW_END); + rSet.ClearItem(SID_CONNECTOR_LINES_ARROWS); + rSet.DisableItem(SID_CONNECTOR_LINES_ARROWS); + rSet.ClearItem(SID_CONNECTOR_LINES_CIRCLE_START); + rSet.DisableItem(SID_CONNECTOR_LINES_CIRCLE_START); + rSet.ClearItem(SID_CONNECTOR_LINES_CIRCLE_END); + rSet.DisableItem(SID_CONNECTOR_LINES_CIRCLE_END); + rSet.ClearItem(SID_CONNECTOR_LINES_CIRCLES); + rSet.DisableItem(SID_CONNECTOR_LINES_CIRCLES); + rSet.ClearItem(SID_DRAW_ARC); + rSet.DisableItem(SID_DRAW_ARC); + rSet.ClearItem(SID_DRAW_CIRCLEARC); + rSet.DisableItem(SID_DRAW_CIRCLEARC); + rSet.ClearItem(SID_DRAW_PIE); + rSet.DisableItem(SID_DRAW_PIE); + rSet.ClearItem(SID_DRAW_PIE_NOFILL); + rSet.DisableItem(SID_DRAW_PIE_NOFILL); + rSet.ClearItem(SID_DRAW_CIRCLEPIE); + rSet.DisableItem(SID_DRAW_CIRCLEPIE); + rSet.ClearItem(SID_DRAW_CIRCLEPIE_NOFILL); + rSet.DisableItem(SID_DRAW_CIRCLEPIE_NOFILL); + rSet.ClearItem(SID_DRAW_ELLIPSECUT); + rSet.DisableItem(SID_DRAW_ELLIPSECUT); + rSet.ClearItem(SID_DRAW_ELLIPSECUT_NOFILL); + rSet.DisableItem(SID_DRAW_ELLIPSECUT_NOFILL); + rSet.ClearItem(SID_DRAW_CIRCLECUT); + rSet.DisableItem(SID_DRAW_CIRCLECUT); + rSet.ClearItem(SID_DRAW_CIRCLECUT_NOFILL); + rSet.DisableItem(SID_DRAW_CIRCLECUT_NOFILL); + rSet.ClearItem(SID_DRAW_POLYGON); + rSet.DisableItem(SID_DRAW_POLYGON); + rSet.ClearItem(SID_DRAW_POLYGON_NOFILL); + rSet.DisableItem(SID_DRAW_POLYGON_NOFILL); + rSet.ClearItem(SID_DRAW_FREELINE); + rSet.DisableItem(SID_DRAW_FREELINE); + rSet.ClearItem(SID_DRAW_FREELINE_NOFILL); + rSet.DisableItem(SID_DRAW_FREELINE_NOFILL); + rSet.ClearItem(SID_DRAW_XPOLYGON); + rSet.DisableItem(SID_DRAW_XPOLYGON); + rSet.ClearItem(SID_DRAW_XPOLYGON_NOFILL); + rSet.DisableItem(SID_DRAW_XPOLYGON_NOFILL); + rSet.ClearItem(SID_DRAW_BEZIER_FILL); + rSet.DisableItem(SID_DRAW_BEZIER_FILL); + rSet.ClearItem(SID_DRAW_BEZIER_NOFILL); + rSet.DisableItem(SID_DRAW_BEZIER_NOFILL); + rSet.ClearItem(SID_3D_CUBE); + rSet.DisableItem(SID_3D_CUBE); + rSet.ClearItem(SID_3D_SHELL); + rSet.DisableItem(SID_3D_SHELL); + rSet.ClearItem(SID_3D_SPHERE); + rSet.DisableItem(SID_3D_SPHERE); + rSet.ClearItem(SID_3D_HALF_SPHERE); + rSet.DisableItem(SID_3D_HALF_SPHERE); + rSet.ClearItem(SID_3D_CYLINDER); + rSet.DisableItem(SID_3D_CYLINDER); + rSet.ClearItem(SID_3D_CONE); + rSet.DisableItem(SID_3D_CONE); + rSet.ClearItem(SID_3D_TORUS); + rSet.DisableItem(SID_3D_TORUS); + rSet.ClearItem(SID_3D_PYRAMID); + rSet.DisableItem(SID_3D_PYRAMID); + } + + // are the modules available? + + if (!SvtModuleOptions().IsCalc()) + { + // remove menu entry if module is not available + rSet.Put( SfxVisibilityItem( SID_ATTR_TABLE, false ) ); + } + if (!SvtModuleOptions().IsChart()) + { + rSet.DisableItem( SID_INSERT_DIAGRAM ); + } + if (!SvtModuleOptions().IsMath()) + { + rSet.DisableItem( SID_INSERT_MATH ); + } + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( (xSlideshow.is() && xSlideshow->isRunning() && (xSlideshow->getAnimationMode() != ANIMATIONMODE_PREVIEW) ) || GetDocSh()->IsPreview() ) + { + // Own Slots + rSet.DisableItem( SID_PRESENTATION ); + rSet.DisableItem( SID_ZOOM_IN ); + rSet.DisableItem( SID_ZOOM_OUT ); + rSet.DisableItem( SID_ZOOM_PANNING ); + rSet.DisableItem( SID_ZOOM_MODE ); + rSet.DisableItem( SID_ZOOM_NEXT ); + rSet.DisableItem( SID_ZOOM_PREV ); + rSet.DisableItem( SID_SIZE_REAL ); + rSet.DisableItem( SID_SIZE_OPTIMAL ); + rSet.DisableItem( SID_SIZE_ALL ); + rSet.DisableItem( SID_SIZE_PAGE_WIDTH ); + rSet.DisableItem( SID_SIZE_PAGE ); + rSet.DisableItem( SID_INSERTPAGE ); + rSet.DisableItem( SID_DUPLICATE_PAGE ); + rSet.DisableItem( SID_MODIFYPAGE ); + rSet.DisableItem( SID_RENAMEPAGE ); + rSet.DisableItem( SID_RENAMEPAGE_QUICK ); + rSet.DisableItem( SID_DELETE_PAGE ); + rSet.DisableItem( SID_PAGESETUP ); + + if( xSlideshow.is() && xSlideshow->isRunning() ) + { + rSet.ClearItem(SID_INSERTFILE); + rSet.ClearItem(SID_OBJECT_ROTATE); + rSet.ClearItem(SID_FM_CONFIG); + rSet.ClearItem(SID_ANIMATION_EFFECTS); + rSet.ClearItem(SID_EXECUTE_ANIMATION_EFFECT); + rSet.ClearItem(SID_ANIMATION_OBJECTS); + rSet.ClearItem(SID_3D_WIN); + + rSet.DisableItem(SID_OBJECT_ALIGN); + rSet.DisableItem(SID_ZOOM_TOOLBOX); + rSet.DisableItem(SID_OBJECT_CHOOSE_MODE); + rSet.DisableItem(SID_DRAWTBX_TEXT); + rSet.DisableItem(SID_DRAWTBX_RECTANGLES); + rSet.DisableItem(SID_DRAWTBX_ELLIPSES); + rSet.DisableItem(SID_DRAWTBX_LINES); + rSet.DisableItem(SID_DRAWTBX_ARROWS); + rSet.DisableItem(SID_DRAWTBX_3D_OBJECTS); + rSet.DisableItem(SID_DRAWTBX_CONNECTORS); + rSet.DisableItem(SID_OBJECT_CHOOSE_MODE ); + rSet.DisableItem(SID_DRAWTBX_INSERT); + rSet.DisableItem(SID_INSERTFILE); + rSet.DisableItem(SID_OBJECT_ROTATE); + rSet.DisableItem(SID_POSITION); + rSet.DisableItem(SID_FM_CONFIG); + rSet.DisableItem(SID_ANIMATION_EFFECTS); + rSet.DisableItem(SID_EXECUTE_ANIMATION_EFFECT); + rSet.DisableItem(SID_ANIMATION_OBJECTS); + rSet.DisableItem(SID_3D_WIN); + } + } + + // Menuoption: Change->Convert->To Bitmap, Change->Convert->To Metafile + // disable, if there only Bitmap or Metafiles marked + // Menuoption: Format->Area, Format->Line + // disabled, if the marked objects not able to handle + // these attributes + + bool bSingleGraphicSelected = false; + + if (!mpDrawView->AreObjectsMarked()) + { + rSet.DisableItem (SID_CONVERT_TO_METAFILE); + rSet.DisableItem (SID_CONVERT_TO_BITMAP); + } + else + { + // get marklist + SdrMarkList aMarkList = mpDrawView->GetMarkedObjectList(); + + bool bFoundBitmap = false; + bool bFoundMetafile = false; + bool bFoundObjNoArea = false; + bool bFoundNoGraphicObj = false; + bool bFoundAny = false; + bool bFoundTable = false; + +// const size_t nMarkCount = aMarkList.GetMarkCount(); + for (size_t i=0; i < nMarkCount && !bFoundAny; ++i) + { + SdrObject* pObj = aMarkList.GetMark(i)->GetMarkedSdrObj(); + SdrObjKind nId = pObj->GetObjIdentifier(); + SdrInventor nInv = pObj->GetObjInventor(); + + if(nInv == SdrInventor::Default) + { + // 2D objects + switch( nId ) + { + case SdrObjKind::PathLine : + case SdrObjKind::PolyLine : + case SdrObjKind::Line: + case SdrObjKind::FreehandLine : + case SdrObjKind::Edge: + case SdrObjKind::CircleArc : + bFoundObjNoArea = true; + bFoundNoGraphicObj = true; + break; + case SdrObjKind::OLE2 : + // #i118485# #i118525# Allow Line, Area and Graphic (Metafile, Bitmap) + bSingleGraphicSelected = nMarkCount == 1; + bFoundBitmap = true; + bFoundMetafile = true; + break; + case SdrObjKind::Graphic : + { + bSingleGraphicSelected = nMarkCount == 1; + const SdrGrafObj* pSdrGrafObj = static_cast< const SdrGrafObj* >(pObj); + + // Current size of the OBJ_GRAF + const ::tools::Rectangle aRect = pObj->GetLogicRect(); + const Size aCurrentSizeofObj = aRect.GetSize(); + + // Original size of the OBJ_GRAF + const Size aOriginalSizeofObj = pSdrGrafObj->getOriginalSize(); + + if(aCurrentSizeofObj == aOriginalSizeofObj ) + rSet.DisableItem(SID_ORIGINAL_SIZE); + + switch(pSdrGrafObj->GetGraphicType()) + { + case GraphicType::Bitmap : + bFoundBitmap = true; + if(pSdrGrafObj->isEmbeddedVectorGraphicData()) + { + bFoundMetafile = true; + } + break; + case GraphicType::GdiMetafile : + bFoundMetafile = true; + break; + default: + break; + } + break; + } + case SdrObjKind::Table: + bFoundTable = true; + break; + default : + bFoundAny = true; + } + } + else if(nInv == SdrInventor::E3d) + { + // 3D objects + bFoundAny = true; + } + } + + if( bFoundTable ) + rSet.DisableItem( SID_ATTRIBUTES_LINE ); + + if (!bFoundAny) + { + // Disable menuitem for area-dialog + if( bFoundObjNoArea ) // #i25616# + rSet.DisableItem( SID_ATTRIBUTES_AREA ); + + if( bFoundBitmap && !bFoundMetafile && !bFoundNoGraphicObj ) // only Bitmaps marked + rSet.DisableItem( SID_CONVERT_TO_BITMAP ); + else if( !bFoundBitmap && bFoundMetafile && !bFoundNoGraphicObj ) // only Metafiles marked + rSet.DisableItem( SID_CONVERT_TO_METAFILE ); + else if( !bFoundBitmap && !bFoundMetafile && !bFoundNoGraphicObj ) // nothing to do + { + rSet.DisableItem( SID_CONVERT_TO_BITMAP ); + rSet.DisableItem( SID_CONVERT_TO_METAFILE ); + } + } + } + + if( !bSingleGraphicSelected ) + { + rSet.DisableItem (SID_OBJECT_CROP); + rSet.DisableItem (SID_ATTR_GRAF_CROP); + } + + // Menuoption: Edit->Hyperlink + // Disable, if there is no hyperlink + bool bDisableEditHyperlink; + if (!moAtContextMenu_DisableEditHyperlink) + bDisableEditHyperlink = ShouldDisableEditHyperlink(); + else + { + // tdf#137445 if a popup menu was active, use the state as of when the popup was launched and then drop + // moAtContextMenu_DisableEditHyperlink + bDisableEditHyperlink = *moAtContextMenu_DisableEditHyperlink; + moAtContextMenu_DisableEditHyperlink.reset(); + } + + //highlight selected custom shape + { + if(HasCurrentFunction()) + { + rtl::Reference< FuPoor > xFunc( GetCurrentFunction() ); + FuConstructCustomShape* pShapeFunc = dynamic_cast< FuConstructCustomShape* >( xFunc.get() ); + + static const sal_uInt16 nCSTbArray[] = { SID_DRAWTBX_CS_BASIC, SID_DRAWTBX_CS_SYMBOL, + SID_DRAWTBX_CS_ARROW, SID_DRAWTBX_CS_FLOWCHART, + SID_DRAWTBX_CS_CALLOUT, SID_DRAWTBX_CS_STAR }; + + const sal_uInt16 nCurrentSId = GetCurrentFunction()->GetSlotID(); + for (sal_uInt16 i : nCSTbArray) + { + rSet.ClearItem( i ); // Why is this necessary? + rSet.Put( SfxStringItem( i, nCurrentSId == i && pShapeFunc + ? pShapeFunc->GetShapeType() : OUString() ) ); + } + } + } + + if ( bDisableEditHyperlink || GetDocSh()->IsReadOnly() ) + rSet.DisableItem( SID_EDIT_HYPERLINK ); + + if ( bDisableEditHyperlink ) + { + rSet.DisableItem( SID_OPEN_HYPERLINK ); + rSet.DisableItem( SID_COPY_HYPERLINK_LOCATION ); + } + + //fdo#78151 enable show next level/hide last level if editing a master page + //PresObjKind::Outline object and the current selection allow that to happen + { + bool bDisableShowNextLevel = true; + bool bDisableHideLastLevel = true; + + ESelection aSel; + ::Outliner* pOL = GetOutlinerForMasterPageOutlineTextObj(aSel); + if (pOL) + { + //and are on the last paragraph + aSel.Adjust(); + if (aSel.nEndPara == pOL->GetParagraphCount() - 1) + { + sal_uInt16 nDepth = pOL->GetDepth(aSel.nEndPara); + if (nDepth != sal_uInt16(-1)) + { + //there exists another numbering level that + //is currently hidden + if (nDepth < 8) + bDisableShowNextLevel = false; + //there exists a previous numbering level + if (nDepth > 0) + bDisableHideLastLevel = false; + } + } + } + + if (bDisableShowNextLevel) + rSet.DisableItem(SID_SHOW_NEXT_LEVEL); + + if (bDisableHideLastLevel) + rSet.DisableItem(SID_HIDE_LAST_LEVEL); + } + +#if defined(_WIN32) || defined UNX + if( !mxScannerManager.is() ) + { + rSet.DisableItem( SID_TWAIN_SELECT ); + rSet.DisableItem( SID_TWAIN_TRANSFER ); + } +#endif + + // Set the state of two entries in the 'Slide' context sub-menu + // concerning the visibility of master page background and master page + // shapes. + if (rSet.GetItemState(SID_DISPLAY_MASTER_BACKGROUND) == SfxItemState::DEFAULT + || rSet.GetItemState(SID_DISPLAY_MASTER_OBJECTS) == SfxItemState::DEFAULT) + { + SdPage* pPage = GetActualPage(); + if (pPage != nullptr && GetDoc() != nullptr) + { + SdrLayerIDSet aVisibleLayers = pPage->TRG_GetMasterPageVisibleLayers(); + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + SdrLayerID aBackgroundId = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aObjectId = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + rSet.Put(SfxBoolItem(SID_DISPLAY_MASTER_BACKGROUND, + aVisibleLayers.IsSet(aBackgroundId))); + rSet.Put(SfxBoolItem(SID_DISPLAY_MASTER_OBJECTS, + aVisibleLayers.IsSet(aObjectId))); + } + } + + if (rSet.GetItemState(SID_SAVE_BACKGROUND) == SfxItemState::DEFAULT) + { + bool bDisableSaveBackground = true; + SdPage* pPage = GetActualPage(); + if (pPage != nullptr && GetDoc() != nullptr) + { + SfxItemSetFixed aMergedAttr(GetDoc()->GetPool()); + SdStyleSheet* pStyleSheet = pPage->getPresentationStyle(HID_PSEUDOSHEET_BACKGROUND); + MergePageBackgroundFilling(pPage, pStyleSheet, meEditMode == EditMode::MasterPage, aMergedAttr); + if (drawing::FillStyle_BITMAP == aMergedAttr.Get(XATTR_FILLSTYLE).GetValue()) + { + bDisableSaveBackground = false; + } + } + if (bDisableSaveBackground) + rSet.DisableItem(SID_SAVE_BACKGROUND); + } + + if (GetObjectShell()->isExportLocked()) + rSet.DisableItem(SID_PRESENTATION_MINIMIZER); + + if (rSet.GetItemState(SID_INSERT_SIGNATURELINE) == SfxItemState::DEFAULT) + { + if (!GetObjectShell()->IsSignPDF()) + { + // Currently SID_INSERT_SIGNATURELINE assumes a PDF that was opened for signing, disable + // it otherwise. + rSet.DisableItem(SID_INSERT_SIGNATURELINE); + } + } + + GetModeSwitchingMenuState (rSet); +} + +void DrawViewShell::GetModeSwitchingMenuState (SfxItemSet &rSet) +{ + //DrawView + rSet.Put(SfxBoolItem(SID_SLIDE_SORTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_OUTLINE_MODE, false)); + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + if (mePageKind == PageKind::Notes) + { + rSet.Put(SfxBoolItem(SID_DRAWINGMODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MODE, true)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + } + else if (mePageKind == PageKind::Handout) + { + rSet.Put(SfxBoolItem(SID_DRAWINGMODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, true)); + } + else + { + rSet.Put(SfxBoolItem(SID_DRAWINGMODE, true)); + rSet.Put(SfxBoolItem(SID_NOTES_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + } + + // Removed [GetDocSh()->GetCurrentFunction() ||] from the following + // clause because the current function of the docshell can only be + // search and replace or spell checking and in that case switching the + // view mode is allowed. + const bool bIsRunning = SlideShow::IsRunning(GetViewShellBase()); + + if (GetViewFrame()->GetFrame().IsInPlace() || bIsRunning) + { + if ( !GetViewFrame()->GetFrame().IsInPlace() ) + { + rSet.ClearItem( SID_DRAWINGMODE ); + rSet.DisableItem( SID_DRAWINGMODE ); + } + + rSet.ClearItem( SID_NOTES_MODE ); + rSet.DisableItem( SID_NOTES_MODE ); + + rSet.ClearItem( SID_HANDOUT_MASTER_MODE ); + rSet.DisableItem( SID_HANDOUT_MASTER_MODE ); + + rSet.ClearItem( SID_OUTLINE_MODE ); + rSet.DisableItem( SID_OUTLINE_MODE ); + + rSet.ClearItem( SID_SLIDE_MASTER_MODE ); + rSet.DisableItem( SID_SLIDE_MASTER_MODE ); + + rSet.ClearItem( SID_NOTES_MASTER_MODE ); + rSet.DisableItem( SID_NOTES_MASTER_MODE ); + + rSet.ClearItem( SID_SLIDE_SORTER_MODE ); + rSet.DisableItem( SID_SLIDE_SORTER_MODE ); + } + + if (GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) + { + // Outplace-Edit: do not allow switch + rSet.ClearItem( SID_OUTLINE_MODE ); + rSet.DisableItem( SID_OUTLINE_MODE ); + + rSet.ClearItem( SID_SLIDE_SORTER_MODE ); + rSet.DisableItem( SID_SLIDE_SORTER_MODE ); + + rSet.ClearItem( SID_NOTES_MODE ); + rSet.DisableItem( SID_NOTES_MODE ); + + rSet.ClearItem( SID_HANDOUT_MASTER_MODE ); + rSet.DisableItem( SID_HANDOUT_MASTER_MODE ); + + rSet.ClearItem( SID_SLIDE_MASTER_MODE ); + rSet.DisableItem( SID_SLIDE_MASTER_MODE ); + + rSet.ClearItem( SID_NOTES_MASTER_MODE ); + rSet.DisableItem( SID_NOTES_MASTER_MODE ); + } + + svx::ExtrusionBar::getState( mpDrawView.get(), rSet ); + svx::FontworkBar::getState( mpDrawView.get(), rSet ); +} + +void DrawViewShell::GetPageProperties( SfxItemSet &rSet ) +{ + SdPage *pPage = getCurrentPage(); + + if (pPage == nullptr || GetDoc() == nullptr) + return; + + SvxPageItem aPageItem(SID_ATTR_PAGE); + aPageItem.SetLandscape( pPage->GetOrientation() == Orientation::Landscape ); + + rSet.Put(SvxSizeItem( SID_ATTR_PAGE_SIZE, pPage->GetSize() )); + rSet.Put(aPageItem); + + const SfxItemSet &rPageAttr = pPage->getSdrPageProperties().GetItemSet(); + const XFillStyleItem* pFillStyle = rPageAttr.GetItem(XATTR_FILLSTYLE); + if (!pFillStyle) + return; + + drawing::FillStyle eXFS = pFillStyle->GetValue(); + XFillStyleItem aFillStyleItem( eXFS ); + aFillStyleItem.SetWhich( SID_ATTR_PAGE_FILLSTYLE ); + rSet.Put(aFillStyleItem); + + switch (eXFS) + { + case drawing::FillStyle_SOLID: + if (const XFillColorItem* pColorItem = rPageAttr.GetItem(XATTR_FILLCOLOR)) + { + Color aColor = pColorItem->GetColorValue(); + XFillColorItem aFillColorItem( OUString(), aColor ); + aFillColorItem.SetWhich( SID_ATTR_PAGE_COLOR ); + rSet.Put( aFillColorItem ); + } + break; + + case drawing::FillStyle_GRADIENT: + { + const XFillGradientItem *pGradient = rPageAttr.GetItem( XATTR_FILLGRADIENT ); + XFillGradientItem aFillGradientItem( pGradient->GetName(), pGradient->GetGradientValue(), SID_ATTR_PAGE_GRADIENT ); + rSet.Put( aFillGradientItem ); + } + break; + + case drawing::FillStyle_HATCH: + { + const XFillHatchItem *pFillHatchItem( rPageAttr.GetItem( XATTR_FILLHATCH ) ); + XFillHatchItem aFillHatchItem( pFillHatchItem->GetName(), pFillHatchItem->GetHatchValue()); + aFillHatchItem.SetWhich( SID_ATTR_PAGE_HATCH ); + rSet.Put( aFillHatchItem ); + } + break; + + case drawing::FillStyle_BITMAP: + { + const XFillBitmapItem *pFillBitmapItem = rPageAttr.GetItem( XATTR_FILLBITMAP ); + XFillBitmapItem aFillBitmapItem( pFillBitmapItem->GetName(), pFillBitmapItem->GetGraphicObject() ); + aFillBitmapItem.SetWhich( SID_ATTR_PAGE_BITMAP ); + rSet.Put( aFillBitmapItem ); + } + break; + + default: + break; + } +} + +void DrawViewShell::SetPageProperties (SfxRequest& rReq) +{ + SdPage *pPage = getCurrentPage(); + if (!pPage) + return; + sal_uInt16 nSlotId = rReq.GetSlot(); + const SfxItemSet *pArgs = rReq.GetArgs(); + if (!pArgs) + return; + + if ( ( nSlotId >= SID_ATTR_PAGE_COLOR ) && ( nSlotId <= SID_ATTR_PAGE_FILLSTYLE ) ) + { + SdrPageProperties& rPageProperties = pPage->getSdrPageProperties(); + const SfxItemSet &aPageItemSet = rPageProperties.GetItemSet(); + SfxItemSet aTempSet = aPageItemSet.CloneAsValue(false, &mpDrawView->GetModel()->GetItemPool()); + const SfxPoolItem* pItem = nullptr; + + rPageProperties.ClearItem(XATTR_FILLSTYLE); + rPageProperties.ClearItem(XATTR_FILLGRADIENT); + rPageProperties.ClearItem(XATTR_FILLHATCH); + rPageProperties.ClearItem(XATTR_FILLBITMAP); + + switch (nSlotId) + { + case SID_ATTR_PAGE_FILLSTYLE: + { + XFillStyleItem aFSItem( pArgs->Get( XATTR_FILLSTYLE ) ); + drawing::FillStyle eXFS = aFSItem.GetValue(); + + if ( eXFS == drawing::FillStyle_NONE ) + rPageProperties.PutItem( XFillStyleItem( eXFS ) ); + } + break; + + case SID_ATTR_PAGE_COLOR: + { + if (SfxItemState::SET == pArgs->GetItemState(SID_ATTR_COLOR_STR, false, &pItem)) + { + Color aColor; + OUString sColor; + + sColor = static_cast(pItem)->GetValue(); + + if (sColor == "transparent") + aColor = COL_TRANSPARENT; + else + aColor = Color(ColorTransparency, sColor.toInt32(16)); + + XFillColorItem aColorItem(OUString(), aColor); + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_SOLID ) ); + rPageProperties.PutItem( aColorItem ); + } + else + { + XFillColorItem aColorItem( pArgs->Get( XATTR_FILLCOLOR ) ); + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_SOLID ) ); + rPageProperties.PutItem( aColorItem ); + } + } + break; + + case SID_ATTR_PAGE_GRADIENT: + { + if (SfxItemState::SET == pArgs->GetItemState(SID_FILL_GRADIENT_JSON, false, &pItem)) + { + const SfxStringItem* pJSON = static_cast(pItem); + XFillGradientItem aGradientItem( XGradient::fromJSON(pJSON->GetValue()) ); + + // MigrateItemSet guarantees unique gradient names + SfxItemSetFixed aMigrateSet( mpDrawView->GetModel()->GetItemPool() ); + aMigrateSet.Put( aGradientItem ); + SdrModel::MigrateItemSet( &aMigrateSet, &aTempSet, mpDrawView->GetModel() ); + + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_GRADIENT ) ); + rPageProperties.PutItemSet( aTempSet ); + } + else + { + XFillGradientItem aGradientItem( pArgs->Get( XATTR_FILLGRADIENT ) ); + + // MigrateItemSet guarantees unique gradient names + SfxItemSetFixed aMigrateSet( mpDrawView->GetModel()->GetItemPool() ); + aMigrateSet.Put( aGradientItem ); + SdrModel::MigrateItemSet( &aMigrateSet, &aTempSet, mpDrawView->GetModel() ); + + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_GRADIENT ) ); + rPageProperties.PutItemSet( aTempSet ); + } + } + break; + + case SID_ATTR_PAGE_HATCH: + { + XFillHatchItem aHatchItem( pArgs->Get( XATTR_FILLHATCH ) ); + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_HATCH ) ); + rPageProperties.PutItem( aHatchItem ); + } + break; + + case SID_ATTR_PAGE_BITMAP: + { + XFillBitmapItem aBitmapItem( pArgs->Get( XATTR_FILLBITMAP ) ); + rPageProperties.PutItem( XFillStyleItem( drawing::FillStyle_BITMAP ) ); + rPageProperties.PutItem( aBitmapItem ); + } + break; + + default: + break; + } + + rReq.Done(); + } + else + { + PageKind ePageKind = GetPageKind(); + const SfxPoolItem* pPoolItem = nullptr; + Size aNewSize(pPage->GetSize()); + sal_Int32 nLeft = -1, nRight = -1, nUpper = -1, nLower = -1; + bool bScaleAll = true; + Orientation eOrientation = pPage->GetOrientation(); + SdPage* pMasterPage = pPage->IsMasterPage() ? pPage : &static_cast(pPage->TRG_GetMasterPage()); + bool bFullSize = pMasterPage->IsBackgroundFullSize(); + sal_uInt16 nPaperBin = pPage->GetPaperBin(); + + switch (nSlotId) + { + case SID_ATTR_PAGE_LRSPACE: + if( pArgs->GetItemState(SID_ATTR_PAGE_LRSPACE, + true,&pPoolItem) == SfxItemState::SET ) + { + nLeft = static_cast(pPoolItem)->GetLeft(); + nRight = static_cast(pPoolItem)->GetRight(); + if (nLeft != -1) + { + nUpper = pPage->GetUpperBorder(); + nLower = pPage->GetLowerBorder(); + } + SetPageSizeAndBorder(ePageKind, aNewSize, nLeft, nRight, nUpper, nLower, bScaleAll, eOrientation, nPaperBin, bFullSize ); + } + break; + + case SID_ATTR_PAGE_ULSPACE: + if( pArgs->GetItemState(SID_ATTR_PAGE_ULSPACE, + true,&pPoolItem) == SfxItemState::SET ) + { + nUpper = static_cast(pPoolItem)->GetUpper(); + nLower = static_cast(pPoolItem)->GetLower(); + if (nUpper != -1) + { + nLeft = pPage->GetLeftBorder(); + nRight = pPage->GetRightBorder(); + } + SetPageSizeAndBorder(ePageKind, aNewSize, nLeft, nRight, nUpper, nLower, bScaleAll, eOrientation, nPaperBin, bFullSize ); + } + break; + + default: + break; + } + } +} + +void DrawViewShell::GetState (SfxItemSet& rSet) +{ + // Iterate over all requested items in the set. + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while (nWhich) + { + switch (nWhich) + { + case SID_SEARCH_ITEM: + case SID_SEARCH_OPTIONS: + // Forward this request to the common (old) code of the + // document shell. + GetDocSh()->GetState (rSet); + break; + default: + SAL_WARN("sd", "DrawViewShell::GetState(): can not handle which id " << nWhich); + break; + } + nWhich = aIter.NextWhich(); + } +} + +void DrawViewShell::Execute (SfxRequest& rReq) +{ + if(SlideShow::IsRunning(GetViewShellBase())) + { + // Do not execute anything during a native slide show. + return; + } + + switch (rReq.GetSlot()) + { + case SID_SEARCH_ITEM: + // Forward this request to the common (old) code of the + // document shell. + GetDocSh()->Execute (rReq); + break; + + case SID_SPELL_DIALOG: + { + SfxViewFrame* pViewFrame = GetViewFrame(); + if (rReq.GetArgs() != nullptr) + pViewFrame->SetChildWindow (SID_SPELL_DIALOG, + static_cast(rReq.GetArgs()-> + Get(SID_SPELL_DIALOG)).GetValue()); + else + pViewFrame->ToggleChildWindow(SID_SPELL_DIALOG); + + pViewFrame->GetBindings().Invalidate(SID_SPELL_DIALOG); + rReq.Ignore (); + } + break; + + default: + SAL_WARN("sd", "DrawViewShell::Execute(): can not handle slot " << rReq.GetSlot()); + break; + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews8.cxx b/sd/source/ui/view/drviews8.cxx new file mode 100644 index 000000000..e5ae5cd97 --- /dev/null +++ b/sd/source/ui/view/drviews8.cxx @@ -0,0 +1,135 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace sd { + +void DrawViewShell::ScannerEvent() +{ + if( mxScannerManager.is() ) + { + const css::scanner::ScannerContext aContext( mxScannerManager->getAvailableScanners().getConstArray()[ 0 ] ); + const css::scanner::ScanError eError = mxScannerManager->getError( aContext ); + + if( css::scanner::ScanError_ScanErrorNone == eError ) + { + const css::uno::Reference< css::awt::XBitmap > xBitmap( mxScannerManager->getBitmap( aContext ) ); + + if( xBitmap.is() ) + { + const BitmapEx aScanBmp( VCLUnoHelper::GetBitmap( xBitmap ) ); + + if( !aScanBmp.IsEmpty() ) + { + const SolarMutexGuard aGuard; + SdrPage* pPage = mpDrawView->GetSdrPageView()->GetPage(); + Size aBmpSize( aScanBmp.GetPrefSize() ), aPageSize( pPage->GetSize() ); + const MapMode aMap100( MapUnit::Map100thMM ); + + if( !aBmpSize.Width() || !aBmpSize.Height() ) + aBmpSize = aScanBmp.GetSizePixel(); + + if( aScanBmp.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) + aBmpSize = GetActiveWindow()->PixelToLogic( aBmpSize, aMap100 ); + else + aBmpSize = OutputDevice::LogicToLogic( aBmpSize, aScanBmp.GetPrefMapMode(), aMap100 ); + + aPageSize.AdjustWidth( -(pPage->GetLeftBorder() + pPage->GetRightBorder()) ); + aPageSize.AdjustHeight( -(pPage->GetUpperBorder() + pPage->GetLowerBorder()) ); + + if( ( ( aBmpSize.Height() > aPageSize.Height() ) || ( aBmpSize.Width() > aPageSize.Width() ) ) && aBmpSize.Height() && aPageSize.Height() ) + { + double fGrfWH = static_cast(aBmpSize.Width()) / aBmpSize.Height(); + double fWinWH = static_cast(aPageSize.Width()) / aPageSize.Height(); + + if( fGrfWH < fWinWH ) + { + aBmpSize.setWidth( FRound( aPageSize.Height() * fGrfWH ) ); + aBmpSize.setHeight( aPageSize.Height() ); + } + else if( fGrfWH > 0.F ) + { + aBmpSize.setWidth( aPageSize.Width() ); + aBmpSize.setHeight( FRound( aPageSize.Width() / fGrfWH ) ); + } + } + + Point aPnt ( ( aPageSize.Width() - aBmpSize.Width() ) >> 1, ( aPageSize.Height() - aBmpSize.Height() ) >> 1 ); + aPnt += Point( pPage->GetLeftBorder(), pPage->GetUpperBorder() ); + ::tools::Rectangle aRect( aPnt, aBmpSize ); + bool bInsertNewObject = true; + + if( GetView()->AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if( rMarkList.GetMarkCount() == 1 ) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + if( auto pGrafObj = dynamic_cast< SdrGrafObj *>( pObj ) ) + { + if( pGrafObj->IsEmptyPresObj() ) + { + bInsertNewObject = false; + pGrafObj->SetEmptyPresObj(false); + pGrafObj->SetOutlinerParaObject(std::nullopt); + pGrafObj->SetGraphic( Graphic( aScanBmp ) ); + } + } + } + } + + if( bInsertNewObject ) + { + auto pGrafObj = new SdrGrafObj( + GetView()->getSdrModelFromSdrView(), + Graphic(aScanBmp), + aRect); + SdrPageView* pPV = GetView()->GetSdrPageView(); + GetView()->InsertObjectAtView( pGrafObj, *pPV, SdrInsertFlags::SETDEFLAYER ); + } + } + } + } + } + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_TWAIN_SELECT ); + rBindings.Invalidate( SID_TWAIN_TRANSFER ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviews9.cxx b/sd/source/ui/view/drviews9.cxx new file mode 100644 index 000000000..f80419587 --- /dev/null +++ b/sd/source/ui/view/drviews9.cxx @@ -0,0 +1,886 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + +void DrawViewShell::ExecGallery(SfxRequest const & rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + const SfxItemSet* pArgs = rReq.GetArgs(); + + const SvxGalleryItem* pGalleryItem = SfxItemSet::GetItem(pArgs, SID_GALLERY_FORMATS, false); + if ( !pGalleryItem ) + return; + + GetDocSh()->SetWaitCursor( true ); + + sal_Int8 nType( pGalleryItem->GetType() ); + // insert graphic + if (nType == css::gallery::GalleryItemType::GRAPHIC) + { + Graphic aGraphic( pGalleryItem->GetGraphic() ); + + // reduce size if necessary + ScopedVclPtrInstance< Window > aWindow(GetActiveWindow()); + aWindow->SetMapMode(aGraphic.GetPrefMapMode()); + Size aSizePix = aWindow->LogicToPixel(aGraphic.GetPrefSize()); + aWindow->SetMapMode( MapMode(MapUnit::Map100thMM) ); + Size aSize = aWindow->PixelToLogic(aSizePix); + + // constrain size to page size if necessary + SdrPage* pPage = mpDrawView->GetSdrPageView()->GetPage(); + Size aPageSize = pPage->GetSize(); + aPageSize.AdjustWidth( -(pPage->GetLeftBorder() + pPage->GetRightBorder()) ); + aPageSize.AdjustHeight( -(pPage->GetUpperBorder() + pPage->GetLowerBorder()) ); + + // If the image is too large we make it fit into the page + if ( ( ( aSize.Height() > aPageSize.Height() ) || ( aSize.Width() > aPageSize.Width() ) ) && + aSize.Height() && aPageSize.Height() ) + { + float fGrfWH = static_cast(aSize.Width()) / + static_cast(aSize.Height()); + float fWinWH = static_cast(aPageSize.Width()) / + static_cast(aPageSize.Height()); + + // constrain size to page size if necessary + if ((fGrfWH != 0.F) && (fGrfWH < fWinWH)) + { + aSize.setWidth( static_cast<::tools::Long>(aPageSize.Height() * fGrfWH) ); + aSize.setHeight( aPageSize.Height() ); + } + else + { + aSize.setWidth( aPageSize.Width() ); + aSize.setHeight( static_cast<::tools::Long>(aPageSize.Width() / fGrfWH) ); + } + } + + // set output rectangle for graphic + Point aPnt ((aPageSize.Width() - aSize.Width()) / 2, + (aPageSize.Height() - aSize.Height()) / 2); + aPnt += Point(pPage->GetLeftBorder(), pPage->GetUpperBorder()); + ::tools::Rectangle aRect (aPnt, aSize); + + SdrGrafObj* pGrafObj = nullptr; + + bool bInsertNewObject = true; + + if ( mpDrawView->AreObjectsMarked() ) + { + // is there an empty graphic object? + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + if (pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::Graphic) + { + pGrafObj = static_cast(pObj); + + if( pGrafObj->IsEmptyPresObj() ) + { + // the empty graphic object gets a new graphic + bInsertNewObject = false; + + SdrGrafObj* pNewGrafObj(pGrafObj->CloneSdrObject(pGrafObj->getSdrModelFromSdrObject())); + pNewGrafObj->SetEmptyPresObj(false); + pNewGrafObj->SetOutlinerParaObject(std::nullopt); + pNewGrafObj->SetGraphic(aGraphic); + + OUString aStr = mpDrawView->GetDescriptionOfMarkedObjects() + + " " + SdResId(STR_UNDO_REPLACE); + mpDrawView->BegUndo(aStr); + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + mpDrawView->ReplaceObjectAtView(pGrafObj, *pPV, pNewGrafObj); + mpDrawView->EndUndo(); + } + } + } + } + + if( bInsertNewObject ) + { + pGrafObj = new SdrGrafObj( + GetView()->getSdrModelFromSdrView(), + aGraphic, + aRect); + SdrPageView* pPV = mpDrawView->GetSdrPageView(); + mpDrawView->InsertObjectAtView(pGrafObj, *pPV, SdrInsertFlags::SETDEFLAYER); + } + } + // insert sound + else if( nType == css::gallery::GalleryItemType::MEDIA ) + { + const SfxStringItem aMediaURLItem( SID_INSERT_AVMEDIA, pGalleryItem->GetURL() ); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_INSERT_AVMEDIA, + SfxCallMode::SYNCHRON, { &aMediaURLItem }); + } + + GetDocSh()->SetWaitCursor( false ); +} + +/** + * Edit macros for attribute configuration + */ + +/* the work flow to adjust the attributes is nearly everywhere the same + 1. read existing attributes + 2. read parameter from the basic-set + 3. delete selected item from the attribute-set + 4. create new attribute-item + 5. insert item into set */ +void DrawViewShell::AttrExec (SfxRequest &rReq) +{ + // nothing is executed during a slide show! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + CheckLineTo (rReq); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + SfxItemSet aAttr( GetDoc()->GetPool() ); + + GetView()->GetAttributes( aAttr ); + const SfxItemSet* pArgs = rReq.GetArgs(); + + switch (rReq.GetSlot ()) + { + // set new fill-style + case SID_SETFILLSTYLE : + if (pArgs && pArgs->Count () == 1) + { + const SfxUInt32Item* pFillStyle = rReq.GetArg(ID_VAL_STYLE); + if (CHECK_RANGE (drawing::FillStyle_NONE, static_cast(pFillStyle->GetValue ()), drawing::FillStyle_BITMAP)) + { + aAttr.ClearItem (XATTR_FILLSTYLE); + XFillStyleItem aStyleItem(static_cast(pFillStyle->GetValue ())); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put (aStyleItem); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // determine new line style + case SID_SETLINESTYLE : + if (pArgs && pArgs->Count () == 1) + { + const SfxUInt32Item* pLineStyle = rReq.GetArg(ID_VAL_STYLE); + if (CHECK_RANGE (sal_Int32(drawing::LineStyle_NONE), static_cast(pLineStyle->GetValue()), sal_Int32(drawing::LineStyle_DASH))) + { + aAttr.ClearItem (XATTR_LINESTYLE); + XLineStyleItem aStyleItem(static_cast(pLineStyle->GetValue())); + aStyleItem.SetWhich(XATTR_LINESTYLE); + aAttr.Put(aStyleItem); + rBindings.Invalidate (SID_ATTR_LINE_STYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // set line width + case SID_SETLINEWIDTH : + if (pArgs && pArgs->Count () == 1) + { + const SfxUInt32Item* pLineWidth = rReq.GetArg(ID_VAL_WIDTH); + aAttr.ClearItem (XATTR_LINEWIDTH); + XLineWidthItem aWidthItem(pLineWidth->GetValue()); + aWidthItem.SetWhich(XATTR_LINEWIDTH); + aAttr.Put(aWidthItem); + rBindings.Invalidate (SID_ATTR_LINE_WIDTH); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SETFILLCOLOR : + if (pArgs && pArgs->Count () == 3) + { + const SfxUInt32Item* pRed = rReq.GetArg(ID_VAL_RED); + const SfxUInt32Item* pGreen = rReq.GetArg(ID_VAL_GREEN); + const SfxUInt32Item* pBlue = rReq.GetArg(ID_VAL_BLUE); + + aAttr.ClearItem (XATTR_FILLCOLOR); + aAttr.ClearItem (XATTR_FILLSTYLE); + XFillColorItem aColorItem(-1, Color (static_cast(pRed->GetValue ()), + static_cast(pGreen->GetValue ()), + static_cast(pBlue->GetValue ()))); + aColorItem.SetWhich(XATTR_FILLCOLOR); + aAttr.Put(aColorItem); + rBindings.Invalidate (SID_ATTR_FILL_COLOR); + rBindings.Invalidate (SID_ATTR_PAGE_COLOR); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SETLINECOLOR : + if (pArgs && pArgs->Count () == 3) + { + const SfxUInt32Item* pRed = rReq.GetArg(ID_VAL_RED); + const SfxUInt32Item* pGreen = rReq.GetArg(ID_VAL_GREEN); + const SfxUInt32Item* pBlue = rReq.GetArg(ID_VAL_BLUE); + + aAttr.ClearItem (XATTR_LINECOLOR); + XLineColorItem aColorItem(-1, Color(static_cast(pRed->GetValue()), + static_cast(pGreen->GetValue()), + static_cast(pBlue->GetValue()))); + aColorItem.SetWhich(XATTR_LINECOLOR); + aAttr.Put(aColorItem); + rBindings.Invalidate (SID_ATTR_LINE_COLOR); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SETGRADSTARTCOLOR : + case SID_SETGRADENDCOLOR : + if (pArgs && pArgs->Count () == 4) + { + const SfxStringItem* pName = rReq.GetArg(ID_VAL_INDEX); + const SfxUInt32Item* pRed = rReq.GetArg(ID_VAL_RED); + const SfxUInt32Item* pGreen = rReq.GetArg(ID_VAL_GREEN); + const SfxUInt32Item* pBlue = rReq.GetArg(ID_VAL_BLUE); + + XGradientListRef pGradientList = GetDoc()->GetGradientList (); + ::tools::Long nCounts = pGradientList->Count (); + Color aColor (static_cast(pRed->GetValue ()), + static_cast(pGreen->GetValue ()), + static_cast(pBlue->GetValue ())); + ::tools::Long i; + + aAttr.ClearItem (XATTR_FILLGRADIENT); + aAttr.ClearItem (XATTR_FILLSTYLE); + + for ( i = 0; i < nCounts; i ++) + { + const XGradientEntry* pEntry = pGradientList->GetGradient(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + XGradient aGradient(pEntry->GetGradient()); + + if (rReq.GetSlot () == SID_SETGRADSTARTCOLOR) aGradient.SetStartColor (aColor); + else aGradient.SetEndColor (aColor); + + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue (), aGradient); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + break; + } + } + + if (i >= nCounts) + { + Color aBlack (0, 0, 0); + XGradient aGradient ((rReq.GetSlot () == SID_SETGRADSTARTCOLOR) + ? aColor + : aBlack, + (rReq.GetSlot () == SID_SETGRADENDCOLOR) + ? aColor + : aBlack); + + GetDoc()->GetGradientList()->Insert(std::make_unique(aGradient, pName->GetValue())); + + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue(), aGradient); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + } + + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + rBindings.Invalidate (SID_ATTR_FILL_GRADIENT); + rBindings.Invalidate (SID_ATTR_PAGE_GRADIENT); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SETHATCHCOLOR : + if (pArgs && pArgs->Count () == 4) + { + const SfxStringItem* pName = rReq.GetArg(ID_VAL_INDEX); + const SfxUInt32Item* pRed = rReq.GetArg(ID_VAL_RED); + const SfxUInt32Item* pGreen = rReq.GetArg(ID_VAL_GREEN); + const SfxUInt32Item* pBlue = rReq.GetArg(ID_VAL_BLUE); + + XHatchListRef pHatchList = GetDoc()->GetHatchList (); + ::tools::Long nCounts = pHatchList->Count (); + Color aColor (static_cast(pRed->GetValue ()), + static_cast(pGreen->GetValue ()), + static_cast(pBlue->GetValue ())); + ::tools::Long i; + + aAttr.ClearItem (XATTR_FILLHATCH); + aAttr.ClearItem (XATTR_FILLSTYLE); + + for ( i = 0; i < nCounts; i ++) + { + const XHatchEntry* pEntry = pHatchList->GetHatch(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + XHatch aHatch(pEntry->GetHatch()); + + aHatch.SetColor (aColor); + + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue(), aHatch); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + break; + } + } + + if (i >= nCounts) + { + XHatch aHatch (aColor); + + GetDoc()->GetHatchList()->Insert(std::make_unique(aHatch, pName->GetValue())); + + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue (), aHatch); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + } + + rBindings.Invalidate (SID_ATTR_FILL_HATCH); + rBindings.Invalidate (SID_ATTR_PAGE_HATCH); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // configuration for line-dash + case SID_DASH : + if (pArgs && pArgs->Count () == 7) + { + const SfxStringItem* pName = rReq.GetArg(ID_VAL_INDEX); + const SfxUInt32Item* pStyle = rReq.GetArg(ID_VAL_STYLE); + const SfxUInt32Item* pDots = rReq.GetArg(ID_VAL_DOTS); + const SfxUInt32Item* pDotLen = rReq.GetArg(ID_VAL_DOTLEN); + const SfxUInt32Item* pDashes = rReq.GetArg(ID_VAL_DASHES); + const SfxUInt32Item* pDashLen = rReq.GetArg(ID_VAL_DASHLEN); + const SfxUInt32Item* pDistance = rReq.GetArg(ID_VAL_DISTANCE); + + if (CHECK_RANGE (sal_Int32(css::drawing::DashStyle_RECT), static_cast(pStyle->GetValue()), sal_Int32(css::drawing::DashStyle_ROUNDRELATIVE))) + { + XDash aNewDash (static_cast(pStyle->GetValue ()), static_cast(pDots->GetValue ()), pDotLen->GetValue (), + static_cast(pDashes->GetValue ()), pDashLen->GetValue (), pDistance->GetValue ()); + + aAttr.ClearItem (XATTR_LINEDASH); + aAttr.ClearItem (XATTR_LINESTYLE); + + XDashListRef pDashList = GetDoc()->GetDashList(); + ::tools::Long nCounts = pDashList->Count (); + std::unique_ptr pEntry = std::make_unique(aNewDash, pName->GetValue()); + ::tools::Long i; + + for ( i = 0; i < nCounts; i++ ) + if (pDashList->GetDash (i)->GetName () == pName->GetValue ()) + break; + + if (i < nCounts) + pDashList->Replace(std::move(pEntry), i); + else + pDashList->Insert(std::move(pEntry)); + + XLineDashItem aDashItem(pName->GetValue(), aNewDash); + aDashItem.SetWhich(XATTR_LINEDASH); + aAttr.Put(aDashItem); + XLineStyleItem aStyleItem(drawing::LineStyle_DASH); + aStyleItem.SetWhich(XATTR_LINESTYLE); + aAttr.Put(aStyleItem); + rBindings.Invalidate (SID_ATTR_LINE_DASH); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // configuration for gradients + case SID_GRADIENT : + if (pArgs && pArgs->Count () == 8) + { + const SfxStringItem* pName = rReq.GetArg(ID_VAL_INDEX); + const SfxUInt32Item* pStyle = rReq.GetArg(ID_VAL_STYLE); + const SfxUInt32Item* pAngle = rReq.GetArg(ID_VAL_ANGLE); + const SfxUInt32Item* pBorder = rReq.GetArg(ID_VAL_BORDER); + const SfxUInt32Item* pCenterX = rReq.GetArg(ID_VAL_CENTER_X); + const SfxUInt32Item* pCenterY = rReq.GetArg(ID_VAL_CENTER_Y); + const SfxUInt32Item* pStart = rReq.GetArg(ID_VAL_STARTINTENS); + const SfxUInt32Item* pEnd = rReq.GetArg(ID_VAL_ENDINTENS); + + if (CHECK_RANGE (sal_Int32(css::awt::GradientStyle_LINEAR), static_cast(pStyle->GetValue()), sal_Int32(css::awt::GradientStyle_RECT)) && + CHECK_RANGE (0, static_cast(pAngle->GetValue ()), 360) && + CHECK_RANGE (0, static_cast(pBorder->GetValue ()), 100) && + CHECK_RANGE (0, static_cast(pCenterX->GetValue ()), 100) && + CHECK_RANGE (0, static_cast(pCenterY->GetValue ()), 100) && + CHECK_RANGE (0, static_cast(pStart->GetValue ()), 100) && + CHECK_RANGE (0, static_cast(pEnd->GetValue ()), 100)) + { + aAttr.ClearItem (XATTR_FILLGRADIENT); + aAttr.ClearItem (XATTR_FILLSTYLE); + + XGradientListRef pGradientList = GetDoc()->GetGradientList (); + ::tools::Long nCounts = pGradientList->Count (); + ::tools::Long i; + + for ( i = 0; i < nCounts; i++ ) + { + const XGradientEntry* pEntry = pGradientList->GetGradient(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + XGradient aGradient(pEntry->GetGradient()); + + aGradient.SetGradientStyle (static_cast(pStyle->GetValue ())); + aGradient.SetAngle (Degree10(pAngle->GetValue () * 10)); + aGradient.SetBorder (static_cast(pBorder->GetValue ())); + aGradient.SetXOffset (static_cast(pCenterX->GetValue ())); + aGradient.SetYOffset (static_cast(pCenterY->GetValue ())); + aGradient.SetStartIntens (static_cast(pStart->GetValue ())); + aGradient.SetEndIntens (static_cast(pEnd->GetValue ())); + + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue (), aGradient); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + break; + } + } + + if (i >= nCounts) + { + Color aBlack (0, 0, 0); + XGradient aGradient (aBlack, aBlack, static_cast(pStyle->GetValue ()), + Degree10(pAngle->GetValue () * 10), static_cast(pCenterX->GetValue ()), + static_cast(pCenterY->GetValue ()), static_cast(pBorder->GetValue ()), + static_cast(pStart->GetValue ()), static_cast(pEnd->GetValue ())); + + pGradientList->Insert(std::make_unique(aGradient, pName->GetValue())); + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue (), aGradient); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + } + + rBindings.Invalidate (SID_ATTR_FILL_GRADIENT); + rBindings.Invalidate (SID_ATTR_PAGE_GRADIENT); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + // configuration for hatch + case SID_HATCH : + if (pArgs && pArgs->Count () == 4) + { + const SfxStringItem* pName = rReq.GetArg(ID_VAL_INDEX); + const SfxUInt32Item* pStyle = rReq.GetArg(ID_VAL_STYLE); + const SfxUInt32Item* pDistance = rReq.GetArg(ID_VAL_DISTANCE); + const SfxUInt32Item* pAngle = rReq.GetArg(ID_VAL_ANGLE); + + if (CHECK_RANGE (sal_Int32(css::drawing::HatchStyle_SINGLE), static_cast(pStyle->GetValue()), sal_Int32(css::drawing::HatchStyle_TRIPLE)) && + CHECK_RANGE (0, static_cast(pAngle->GetValue ()), 360)) + { + aAttr.ClearItem (XATTR_FILLHATCH); + aAttr.ClearItem (XATTR_FILLSTYLE); + + XHatchListRef pHatchList = GetDoc()->GetHatchList (); + ::tools::Long nCounts = pHatchList->Count (); + ::tools::Long i; + + for ( i = 0; i < nCounts; i++ ) + { + const XHatchEntry* pEntry = pHatchList->GetHatch(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + XHatch aHatch(pEntry->GetHatch()); + + aHatch.SetHatchStyle (static_cast(pStyle->GetValue ())); + aHatch.SetDistance (pDistance->GetValue ()); + aHatch.SetAngle (Degree10(pAngle->GetValue () * 10)); + + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue (), aHatch); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + break; + } + } + + if (i >= nCounts) + { + XHatch aHatch (Color(0), static_cast(pStyle->GetValue ()), pDistance->GetValue (), + Degree10(pAngle->GetValue () * 10)); + + pHatchList->Insert(std::make_unique(aHatch, pName->GetValue())); + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue (), aHatch); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + } + + rBindings.Invalidate (SID_ATTR_FILL_HATCH); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + } +#if HAVE_FEATURE_SCRIPTING + else StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SELECTGRADIENT : + if (pArgs && (pArgs->Count () == 1)) + { + const SfxStringItem* pName = rReq.GetArg(ID_VAL_INDEX); + + XGradientListRef pGradientList = GetDoc()->GetGradientList (); + ::tools::Long nCounts = pGradientList->Count (); + + for (::tools::Long i = 0; i < nCounts; i ++) + { + const XGradientEntry* pEntry = pGradientList->GetGradient(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + aAttr.ClearItem (XATTR_FILLGRADIENT); + aAttr.ClearItem (XATTR_FILLSTYLE); + XFillStyleItem aStyleItem(drawing::FillStyle_GRADIENT); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillGradientItem aGradientItem(pName->GetValue (), pEntry->GetGradient ()); + aGradientItem.SetWhich(XATTR_FILLGRADIENT); + aAttr.Put(aGradientItem); + rBindings.Invalidate (SID_ATTR_FILL_GRADIENT); + rBindings.Invalidate (SID_ATTR_PAGE_GRADIENT); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + break; + } + } + + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_SELECTHATCH : + if (pArgs && pArgs->Count () == 1) + { + const SfxStringItem* pName = rReq.GetArg(ID_VAL_INDEX); + + XHatchListRef pHatchList = GetDoc()->GetHatchList (); + ::tools::Long nCounts = pHatchList->Count (); + + for (::tools::Long i = 0; i < nCounts; i ++) + { + const XHatchEntry* pEntry = pHatchList->GetHatch(i); + + if (pEntry->GetName () == pName->GetValue ()) + { + aAttr.ClearItem (XATTR_FILLHATCH); + aAttr.ClearItem (XATTR_FILLSTYLE); + XFillStyleItem aStyleItem(drawing::FillStyle_HATCH); + aStyleItem.SetWhich(XATTR_FILLSTYLE); + aAttr.Put(aStyleItem); + XFillHatchItem aHatchItem(pName->GetValue (), pEntry->GetHatch ()); + aHatchItem.SetWhich(XATTR_FILLHATCH); + aAttr.Put(aHatchItem); + + rBindings.Invalidate (SID_ATTR_FILL_HATCH); + rBindings.Invalidate (SID_ATTR_PAGE_HATCH); + rBindings.Invalidate (SID_ATTR_FILL_STYLE); + rBindings.Invalidate (SID_ATTR_PAGE_FILLSTYLE); + break; + } + } + + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + + case SID_UNSELECT : + mpDrawView->UnmarkAll (); + break; + + case SID_GETRED : + if (pArgs && pArgs->Count () == 1) + { + break; + } +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + break; + +/* case SID_SETFONTFAMILYNAME : + case SID_SETFONTSTYLENAME : + case SID_SETFONTFAMILY : + case SID_SETFONTPITCH : + case SID_SETFONTCHARSET : + case SID_SETFONTPOSTURE : + case SID_SETFONTWEIGHT : + case SID_SETFONTUNDERLINE : + case SID_SETFONTCROSSEDOUT : + case SID_SETFONTSHADOWED : + case SID_SETFONTCONTOUR : + case SID_SETFONTCOLOR : + case SID_SETFONTLANGUAGE : + case SID_SETFONTWORDLINE : + case SID_SETFONTCASEMAP : + case SID_SETFONTESCAPE : + case SID_SETFONTKERNING : + break;*/ + + default : + ; + } + + mpDrawView->SetAttributes (const_cast(aAttr)); + rReq.Ignore (); +} + +/** + * Edit macros for attribute configuration + */ +void DrawViewShell::AttrState (SfxItemSet& rSet) +{ + SfxWhichIter aIter (rSet); + sal_uInt16 nWhich = aIter.FirstWhich (); + SfxItemSet aAttr( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttr ); + + while (nWhich) + { + switch (nWhich) + { + case SID_GETFILLSTYLE : + { + const XFillStyleItem &rFillStyleItem = aAttr.Get (XATTR_FILLSTYLE); + + rSet.Put (SfxUInt32Item (nWhich, static_cast<::tools::Long>(rFillStyleItem.GetValue ()))); + break; + } + + case SID_GETLINESTYLE : + { + const XLineStyleItem &rLineStyleItem = aAttr.Get (XATTR_LINESTYLE); + + rSet.Put (SfxUInt32Item (nWhich, static_cast<::tools::Long>(rLineStyleItem.GetValue ()))); + break; + } + + case SID_GETLINEWIDTH : + { + const XLineWidthItem &rLineWidthItem = aAttr.Get (XATTR_LINEWIDTH); + + rSet.Put (SfxUInt32Item (nWhich, static_cast<::tools::Long>(rLineWidthItem.GetValue ()))); + break; + } + + case SID_GETGREEN : + case SID_GETRED : + case SID_GETBLUE : + { + const SfxUInt32Item &rWhatKind = static_cast( rSet.Get (ID_VAL_WHATKIND) ); + Color aColor; + + switch (rWhatKind.GetValue ()) + { + case 1 : + { + const XLineColorItem &rLineColorItem = aAttr.Get (XATTR_LINECOLOR); + + aColor = rLineColorItem.GetColorValue (); + break; + } + + case 2 : + { + const XFillColorItem &rFillColorItem = aAttr.Get (XATTR_FILLCOLOR); + + aColor = rFillColorItem.GetColorValue (); + break; + } + + case 3 : + case 4 : + { + const XFillGradientItem &rFillGradientItem = aAttr.Get (XATTR_FILLGRADIENT); + const XGradient &rGradient = rFillGradientItem.GetGradientValue (); + + aColor = (rWhatKind.GetValue () == 3) + ? rGradient.GetStartColor () + : rGradient.GetEndColor (); + break; + } + + case 5: + { + const XFillHatchItem &rFillHatchItem = aAttr.Get (XATTR_FILLHATCH); + const XHatch &rHatch = rFillHatchItem.GetHatchValue (); + + aColor = rHatch.GetColor (); + break; + } + + default : + ; + } + + rSet.Put (SfxUInt32Item (nWhich, static_cast<::tools::Long>((nWhich == SID_GETRED) + ? aColor.GetRed () + : (nWhich == SID_GETGREEN) + ? aColor.GetGreen () + : aColor.GetBlue ()))); + break; + } + + default : + ; + } + + nWhich = aIter.NextWhich (); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsa.cxx b/sd/source/ui/view/drviewsa.cxx new file mode 100644 index 000000000..a61d64599 --- /dev/null +++ b/sd/source/ui/view/drviewsa.cxx @@ -0,0 +1,848 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using vcl::EnumContext; + +namespace sd { + +bool DrawViewShell::mbPipette = false; + +namespace { + +class ScannerEventListener : public ::cppu::WeakImplHelper< lang::XEventListener > +{ +private: + + DrawViewShell* mpParent; + +public: + + explicit ScannerEventListener( DrawViewShell* pParent ) : mpParent( pParent ) {} + + // XEventListener + virtual void SAL_CALL disposing( const lang::EventObject& rEventObject ) override; + + void ParentDestroyed() { mpParent = nullptr; } +}; + +} + +void SAL_CALL ScannerEventListener::disposing( const lang::EventObject& /*rEventObject*/ ) +{ + if( mpParent ) + mpParent->ScannerEvent(); +} + +DrawViewShell::DrawViewShell( ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, PageKind ePageKind, FrameView* pFrameViewArgument ) + : ViewShell (pParentWindow, rViewShellBase) + , maTabControl(VclPtr::Create(this, pParentWindow)) + , mbIsLayerModeActive(false) + , mbIsInSwitchPage(false) + , mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler( + [this] () { return this->GetSidebarContextName(); }, + uno::Reference(&rViewShellBase.GetDrawController()), + vcl::EnumContext::Context::Default)) + , mbMouseButtonDown(false) + , mbMouseSelecting(false) +{ + if (pFrameViewArgument != nullptr) + mpFrameView = pFrameViewArgument; + else + mpFrameView = new FrameView(GetDoc()); + Construct(GetDocSh(), ePageKind); + + mpSelectionChangeHandler->Connect(); + + SetContextName(GetSidebarContextName()); + + doShow(); + + ConfigureAppBackgroundColor(); + SD_MOD()->GetColorConfig().AddListener(this); +} + +DrawViewShell::~DrawViewShell() +{ + SD_MOD()->GetColorConfig().RemoveListener(this); + + mpSelectionChangeHandler->Disconnect(); + + mpAnnotationManager.reset(); + mpViewOverlayManager.reset(); + + OSL_ASSERT (GetViewShell()!=nullptr); + + if( mxScannerListener.is() ) + static_cast< ScannerEventListener* >( mxScannerListener.get() )->ParentDestroyed(); + + // Remove references to items within Svx3DWin + // (maybe do a listening sometime in Svx3DWin) + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxChildWindow* pWindow = GetViewFrame() ? GetViewFrame()->GetChildWindow(nId) : nullptr; + if(pWindow) + { + Svx3DWin* p3DWin = static_cast< Svx3DWin* > (pWindow->GetWindow()); + if(p3DWin) + p3DWin->DocumentReload(); + } + + EndListening (*GetDoc()); + EndListening (*GetDocSh()); + + if( SlideShow::IsRunning(*this) ) + StopSlideShow(); + + DisposeFunctions(); + + sal_uInt16 aPageCnt = GetDoc()->GetSdPageCount(mePageKind); + + for (sal_uInt16 i = 0; i < aPageCnt; i++) + { + SdPage* pPage = GetDoc()->GetSdPage(i, mePageKind); + + if (pPage == mpActualPage) + { + GetDoc()->SetSelected(pPage, true); + } + else + { + GetDoc()->SetSelected(pPage, false); + } + } + + if ( mxClipEvtLstnr.is() ) + { + mxClipEvtLstnr->RemoveListener( GetActiveWindow() ); + mxClipEvtLstnr->ClearCallbackLink(); // prevent callback if another thread is waiting + mxClipEvtLstnr.clear(); + } + + mpDrawView.reset(); + // Set mpView to NULL so that the destructor of the ViewShell base class + // does not access it. + mpView = nullptr; + + mpFrameView->Disconnect(); + maTabControl.disposeAndClear(); +} + +/** + * common part of both constructors + */ +void DrawViewShell::Construct(DrawDocShell* pDocSh, PageKind eInitialPageKind) +{ + mpActualPage = nullptr; + mbReadOnly = GetDocSh()->IsReadOnly(); + mxClipEvtLstnr.clear(); + mbPastePossible = false; + mbIsLayerModeActive = false; + + mpFrameView->Connect(); + + OSL_ASSERT (GetViewShell()!=nullptr); + + SetPool( &GetDoc()->GetPool() ); + + GetDoc()->CreateFirstPages(); + + mpDrawView.reset( new DrawView(pDocSh, GetActiveWindow()->GetOutDev(), this) ); + mpView = mpDrawView.get(); // Pointer of base class ViewShell + mpDrawView->SetSwapAsynchron(); // Asynchronous load of graphics + + // We do not read the page kind from the frame view anymore so we have + // to set it in order to resync frame view and this view. + mpFrameView->SetPageKind(eInitialPageKind); + mePageKind = eInitialPageKind; + meEditMode = EditMode::Page; + DocumentType eDocType = GetDoc()->GetDocumentType(); // RTTI does not work here + switch (mePageKind) + { + case PageKind::Standard: + meShellType = ST_IMPRESS; + break; + + case PageKind::Notes: + meShellType = ST_NOTES; + break; + + case PageKind::Handout: + meShellType = ST_HANDOUT; + break; + } + + Size aPageSize( GetDoc()->GetSdPage(0, mePageKind)->GetSize() ); + Point aPageOrg( aPageSize.Width(), aPageSize.Height() / 2); + Size aSize(aPageSize.Width() * 3, aPageSize.Height() * 2); + InitWindows(aPageOrg, aSize, Point(-1, -1)); + + Point aVisAreaPos; + + if ( pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + { + aVisAreaPos = pDocSh->GetVisArea(ASPECT_CONTENT).TopLeft(); + } + + mpDrawView->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aSize)); + + // objects can not grow bigger than ViewSize + GetDoc()->SetMaxObjSize(aSize); + + // Split-Handler for TabControls + maTabControl->SetSplitHdl( LINK( this, DrawViewShell, TabSplitHdl ) ); + + /* In order to set the correct EditMode of the FrameView, we select another + one (small trick). */ + if (mpFrameView->GetViewShEditMode(/*mePageKind*/) == EditMode::Page) + { + meEditMode = EditMode::MasterPage; + } + else + { + meEditMode = EditMode::Page; + } + + // Use configuration of FrameView + ReadFrameViewData(mpFrameView); + + if( eDocType == DocumentType::Draw ) + { + GetActiveWindow()->SetHelpId( HID_SDGRAPHICVIEWSHELL ); + } + else + { + if (mePageKind == PageKind::Notes) + { + GetActiveWindow()->SetHelpId( CMD_SID_NOTES_MODE ); + + // AutoLayouts have to be created + GetDoc()->StopWorkStartupDelay(); + } + else if (mePageKind == PageKind::Handout) + { + GetActiveWindow()->SetHelpId( CMD_SID_HANDOUT_MASTER_MODE ); + + // AutoLayouts have to be created + GetDoc()->StopWorkStartupDelay(); + } + else + { + GetActiveWindow()->SetHelpId( HID_SDDRAWVIEWSHELL ); + } + } + + // start selection function + SfxRequest aReq(SID_OBJECT_SELECT, SfxCallMode::SLOT, GetDoc()->GetItemPool()); + FuPermanent(aReq); + mpDrawView->SetFrameDragSingles(); + + if (pDocSh->GetCreateMode() == SfxObjectCreateMode::EMBEDDED) + { + mbZoomOnPage = false; + } + else + { + mbZoomOnPage = true; + } + + mbIsRulerDrag = false; + + SetName ("DrawViewShell"); + + mnLockCount = 0; + + uno::Reference< uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + + try + { + mxScannerManager = scanner::ScannerManager::create( xContext ); + + mxScannerListener = new ScannerEventListener( this ); + } + catch (Exception const &) + { + // Eat the exception and log it + // We can still continue if scanner manager is not available. + DBG_UNHANDLED_EXCEPTION("sd"); + } + + mpAnnotationManager.reset( new AnnotationManager( GetViewShellBase() ) ); + mpViewOverlayManager.reset( new ViewOverlayManager( GetViewShellBase() ) ); +} + +void DrawViewShell::Init (bool bIsMainViewShell) +{ + ViewShell::Init(bIsMainViewShell); + + if (!IsListening(*GetDocSh())) + StartListening (*GetDocSh()); +} + +void DrawViewShell::Shutdown() +{ + ViewShell::Shutdown(); + + if(SlideShow::IsRunning( GetViewShellBase() ) ) + { + // Turn off effects. + GetDrawView()->SetAnimationMode(SdrAnimationMode::Disable); + } +} + +css::uno::Reference DrawViewShell::CreateSubController() +{ + css::uno::Reference xSubController; + + if (IsMainViewShell()) + { + // Create uno sub controller for the main view shell. + xSubController.set( new SdUnoDrawView( *this, *GetView())); + } + + return xSubController; +} + +bool DrawViewShell::RelocateToParentWindow (vcl::Window* pParentWindow) +{ + // DrawViewShells can not be relocated to a new parent window at the + // moment, so return except when the given parent window is the + // parent window that is already in use. + return pParentWindow==GetParentWindow(); +} + +/** + * check if we have to draw a polyline + */ + +/* + Polylines are represented by macros as a sequence of: + MoveTo (x, y) + LineTo (x, y) [or BezierTo (x, y)] + LineTo (x, y) + : + There is no end command for polylines. Therefore, we have to test all + commands in the requests for LineTo (BezierTo) and we have to gather + the point-parameter. The first not-LineTo leads to the creation of the + polyline from the gathered points. +*/ + +void DrawViewShell::CheckLineTo(SfxRequest& rReq) +{ +#ifdef DBG_UTIL + if(rReq.IsAPI()) + { + if(SID_LINETO == rReq.GetSlot() || SID_BEZIERTO == rReq.GetSlot() || SID_MOVETO == rReq.GetSlot() ) + { + OSL_FAIL("DrawViewShell::CheckLineTo: slots SID_LINETO, SID_BEZIERTO, SID_MOVETO no longer supported."); + } + } +#endif + + rReq.Ignore (); +} + +/** + * Change page parameter if SID_PAGESIZE or SID_PAGEMARGIN + */ +void DrawViewShell::SetupPage (Size const &rSize, + ::tools::Long nLeft, + ::tools::Long nRight, + ::tools::Long nUpper, + ::tools::Long nLower, + bool bSize, + bool bMargin, + bool bScaleAll) +{ + sal_uInt16 nPageCnt = GetDoc()->GetMasterSdPageCount(mePageKind); + sal_uInt16 i; + + for (i = 0; i < nPageCnt; i++) + { + // first, handle all master pages + SdPage *pPage = GetDoc()->GetMasterSdPage(i, mePageKind); + + if( pPage ) + { + if( bSize ) + { + ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower); + pPage->ScaleObjects(rSize, aBorderRect, bScaleAll); + pPage->SetSize(rSize); + + } + if( bMargin ) + { + pPage->SetLeftBorder(nLeft); + pPage->SetRightBorder(nRight); + pPage->SetUpperBorder(nUpper); + pPage->SetLowerBorder(nLower); + } + + if ( mePageKind == PageKind::Standard ) + { + GetDoc()->GetMasterSdPage(i, PageKind::Notes)->CreateTitleAndLayout(); + } + + pPage->CreateTitleAndLayout(); + } + } + + nPageCnt = GetDoc()->GetSdPageCount(mePageKind); + + for (i = 0; i < nPageCnt; i++) + { + // then, handle all pages + SdPage *pPage = GetDoc()->GetSdPage(i, mePageKind); + + if( pPage ) + { + if( bSize ) + { + ::tools::Rectangle aBorderRect(nLeft, nUpper, nRight, nLower); + pPage->ScaleObjects(rSize, aBorderRect, bScaleAll); + pPage->SetSize(rSize); + } + if( bMargin ) + { + pPage->SetLeftBorder(nLeft); + pPage->SetRightBorder(nRight); + pPage->SetUpperBorder(nUpper); + pPage->SetLowerBorder(nLower); + } + + if ( mePageKind == PageKind::Standard ) + { + SdPage* pNotesPage = GetDoc()->GetSdPage(i, PageKind::Notes); + pNotesPage->SetAutoLayout( pNotesPage->GetAutoLayout() ); + } + + pPage->SetAutoLayout( pPage->GetAutoLayout() ); + } + } + + if ( mePageKind == PageKind::Standard ) + { + SdPage* pHandoutPage = GetDoc()->GetSdPage(0, PageKind::Handout); + pHandoutPage->CreateTitleAndLayout(true); + } + + ::tools::Long nWidth = mpActualPage->GetSize().Width(); + ::tools::Long nHeight = mpActualPage->GetSize().Height(); + + Point aPageOrg(nWidth, nHeight / 2); + Size aSize( nWidth * 3, nHeight * 2); + + InitWindows(aPageOrg, aSize, Point(-1, -1), true); + + Point aVisAreaPos; + + if ( GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + { + aVisAreaPos = GetDocSh()->GetVisArea(ASPECT_CONTENT).TopLeft(); + } + + GetView()->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aSize)); + + UpdateScrollBars(); + + Point aNewOrigin(mpActualPage->GetLeftBorder(), mpActualPage->GetUpperBorder()); + GetView()->GetSdrPageView()->SetPageOrigin(aNewOrigin); + + GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET); + + // zoom onto (new) page size + GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); +} + +void DrawViewShell::GetStatusBarState(SfxItemSet& rSet) +{ + /* Zoom-Item + Here we should propagate the corresponding value (Optimal ?, page width + or page) with the help of the ZoomItems !!! */ + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOM ) ) + { + if (GetDocSh()->IsUIActive() || SlideShow::IsRunning(GetViewShellBase()) + || !GetActiveWindow()) + { + rSet.DisableItem( SID_ATTR_ZOOM ); + } + else + { + std::unique_ptr pZoomItem; + sal_uInt16 nZoom = static_cast(GetActiveWindow()->GetZoom()); + + if( mbZoomOnPage ) + pZoomItem.reset(new SvxZoomItem( SvxZoomType::WHOLEPAGE, nZoom )); + else + pZoomItem.reset(new SvxZoomItem( SvxZoomType::PERCENT, nZoom )); + + // constrain area + SvxZoomEnableFlags nZoomValues = SvxZoomEnableFlags::ALL; + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if( pPageView && pPageView->GetObjList()->GetObjCount() == 0 ) + { + nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL; + } + + pZoomItem->SetValueSet( nZoomValues ); + rSet.Put( std::move(pZoomItem) ); + } + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOMSLIDER ) ) + { + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetDoc() ) ); + if (GetDocSh()->IsUIActive() || (xSlideshow.is() && xSlideshow->isRunning()) || !GetActiveWindow() ) + { + rSet.DisableItem( SID_ATTR_ZOOMSLIDER ); + } + else + { + sd::Window * pActiveWindow = GetActiveWindow(); + SvxZoomSliderItem aZoomItem( static_cast(pActiveWindow->GetZoom()), static_cast(pActiveWindow->GetMinZoom()), static_cast(pActiveWindow->GetMaxZoom()) ) ; + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + if( pPageView ) + { + Point aPagePos(0, 0); + Size aPageSize = pPageView->GetPage()->GetSize(); + + aPagePos.AdjustX(aPageSize.Width() / 2 ); + aPageSize.setWidth( static_cast<::tools::Long>(aPageSize.Width() * 1.03) ); + + aPagePos.AdjustY(aPageSize.Height() / 2 ); + aPageSize.setHeight( static_cast<::tools::Long>(aPageSize.Height() * 1.03) ); + aPagePos.AdjustY( -(aPageSize.Height() / 2) ); + + aPagePos.AdjustX( -(aPageSize.Width() / 2) ); + + ::tools::Rectangle aFullPageZoomRect( aPagePos, aPageSize ); + aZoomItem.AddSnappingPoint( pActiveWindow->GetZoomForRect( aFullPageZoomRect ) ); + } + aZoomItem.AddSnappingPoint(100); + rSet.Put( aZoomItem ); + } + } + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + if (pPageView) + { + Point aPos = GetActiveWindow()->PixelToLogic(maMousePos); + pPageView->LogicToPagePos(aPos); + Fraction aUIScale(GetDoc()->GetUIScale()); + aPos.setX( ::tools::Long(aPos.X() / aUIScale) ); + aPos.setY( ::tools::Long(aPos.Y() / aUIScale) ); + + // position- and size items + if ( mpDrawView->IsAction() ) + { + ::tools::Rectangle aRect; + mpDrawView->TakeActionRect( aRect ); + + if ( aRect.IsEmpty() ) + rSet.Put( SfxPointItem(SID_ATTR_POSITION, aPos) ); + else + { + pPageView->LogicToPagePos(aRect); + aPos = aRect.TopLeft(); + aPos.setX( ::tools::Long(aPos.X() / aUIScale) ); + aPos.setY( ::tools::Long(aPos.Y() / aUIScale) ); + rSet.Put( SfxPointItem( SID_ATTR_POSITION, aPos) ); + Size aSize( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() ); + aSize.setHeight( ::tools::Long(aSize.Height() / aUIScale) ); + aSize.setWidth( ::tools::Long(aSize.Width() / aUIScale) ); + rSet.Put( SvxSizeItem( SID_ATTR_SIZE, aSize) ); + } + } + else + { + if ( mpDrawView->AreObjectsMarked() ) + { + ::tools::Rectangle aRect = mpDrawView->GetAllMarkedRect(); + pPageView->LogicToPagePos(aRect); + + // Show the position of the selected shape(s) + Point aShapePosition (aRect.TopLeft()); + aShapePosition.setX( ::tools::Long(aShapePosition.X() / aUIScale) ); + aShapePosition.setY( ::tools::Long(aShapePosition.Y() / aUIScale) ); + rSet.Put (SfxPointItem(SID_ATTR_POSITION, aShapePosition)); + + Size aSize( aRect.Right() - aRect.Left(), aRect.Bottom() - aRect.Top() ); + aSize.setHeight( ::tools::Long(aSize.Height() / aUIScale) ); + aSize.setWidth( ::tools::Long(aSize.Width() / aUIScale) ); + rSet.Put( SvxSizeItem( SID_ATTR_SIZE, aSize) ); + } + else + { + rSet.Put( SfxPointItem(SID_ATTR_POSITION, aPos) ); + rSet.Put( SvxSizeItem( SID_ATTR_SIZE, Size( 0, 0 ) ) ); + } + } + } + + // Display of current page and layer. + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STATUS_PAGE ) ) + { + sal_Int32 nPageCount = sal_Int32(GetDoc()->GetSdPageCount(mePageKind)); + sal_Int32 nActivePageCount = sal_Int32(GetDoc()->GetActiveSdPageCount()); + // Always show the slide/page number. + OUString aOUString = (nPageCount == nActivePageCount) ? SdResId(STR_SD_PAGE_COUNT) : SdResId(STR_SD_PAGE_COUNT_CUSTOM); + + aOUString = aOUString.replaceFirst("%1", OUString::number(maTabControl->GetCurPagePos() + 1)); + aOUString = aOUString.replaceFirst("%2", OUString::number(nPageCount)); + if(nPageCount != nActivePageCount) + aOUString = aOUString.replaceFirst("%3", OUString::number(nActivePageCount)); + + // If in layer mode additionally show the layer that contains all + // selected shapes of the page. If the shapes are distributed on + // more than one layer, no layer name is shown. + if (IsLayerModeActive()) + { + SdrLayerAdmin& rLayerAdmin = GetDoc()->GetLayerAdmin(); + SdrLayerID nLayer(0), nOldLayer(0); + SdrObject* pObj = nullptr; + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + bool bOneLayer = true; + + // Use the first ten selected shapes as a (hopefully + // representative) sample of all shapes of the current page. + // Detect whether they belong to the same layer. + for( size_t j = 0; j < nMarkCount && bOneLayer && j < 10; ++j ) + { + pObj = rMarkList.GetMark( j )->GetMarkedSdrObj(); + if( pObj ) + { + nLayer = pObj->GetLayer(); + + if( j != 0 && nLayer != nOldLayer ) + bOneLayer = false; + + nOldLayer = nLayer; + } + } + + // Append the layer name to the current page number. + if( bOneLayer && nMarkCount ) + { + SdrLayer* pLayer = rLayerAdmin.GetLayerPerID( nLayer ); + if( pLayer ) + { + aOUString += " (" + LayerTabBar::convertToLocalizedName(pLayer->GetName()) + ")"; + } + } + } + + rSet.Put (SfxStringItem (SID_STATUS_PAGE, aOUString)); + } + // Layout + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STATUS_LAYOUT ) ) + { + OUString aString = mpActualPage->GetLayoutName(); + sal_Int32 nPos = aString.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aString = aString.copy(0, nPos); + rSet.Put( SfxStringItem( SID_STATUS_LAYOUT, aString ) ); + } + // Scale + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_SCALE ) ) + { + const Fraction& aUIScale = GetDoc()->GetUIScale(); + OUString aString = OUString::number(aUIScale.GetNumerator()) + + ":" + OUString::number(aUIScale.GetDenominator()); + rSet.Put( SfxStringItem( SID_SCALE, aString ) ); + } +} + +void DrawViewShell::Notify (SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId()!=SfxHintId::ModeChanged) + return; + + // Change to selection when turning on read-only mode. + if(GetDocSh()->IsReadOnly() && dynamic_cast< FuSelection* >( GetCurrentFunction().get() ) ) + { + SfxRequest aReq(SID_OBJECT_SELECT, SfxCallMode::SLOT, GetDoc()->GetItemPool()); + FuPermanent(aReq); + } + + // Turn on design mode when document is not read-only. + if (GetDocSh()->IsReadOnly() != mbReadOnly ) + { + mbReadOnly = GetDocSh()->IsReadOnly(); + + SfxBoolItem aItem( SID_FM_DESIGN_MODE, !mbReadOnly ); + GetViewFrame()->GetDispatcher()->ExecuteList(SID_FM_DESIGN_MODE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + } + +} + +void DrawViewShell::ExecuteAnnotation (SfxRequest const & rRequest) +{ + if (mpAnnotationManager) + mpAnnotationManager->ExecuteAnnotation( rRequest ); +} + +void DrawViewShell::GetAnnotationState (SfxItemSet& rItemSet ) +{ + if (mpAnnotationManager) + mpAnnotationManager->GetAnnotationState( rItemSet ); +} + +OUString const & DrawViewShell::GetSidebarContextName() const +{ + svx::sidebar::SelectionAnalyzer::ViewType eViewType (svx::sidebar::SelectionAnalyzer::ViewType::Standard); + switch (mePageKind) + { + case PageKind::Handout: + eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Handout; + break; + case PageKind::Notes: + eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Notes; + break; + case PageKind::Standard: + if (meEditMode == EditMode::MasterPage) + eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Master; + else + eViewType = svx::sidebar::SelectionAnalyzer::ViewType::Standard; + break; + } + return EnumContext::GetContextName( + svx::sidebar::SelectionAnalyzer::GetContextForSelection_SD( + mpDrawView->GetMarkedObjectList(), + eViewType)); +} + +void DrawViewShell::ExecGoToNextPage (SfxRequest& rReq) +{ + SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); +} + +void DrawViewShell::GetStateGoToNextPage (SfxItemSet& rSet) +{ + SdPage* pPage = GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + sal_uInt16 totalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind()); + if (nSdPage + 1 >= totalPages) + rSet.DisableItem( SID_GO_TO_NEXT_PAGE ); +} + +void DrawViewShell::ExecGoToPreviousPage (SfxRequest& rReq) +{ + SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); +} + +void DrawViewShell::GetStateGoToPreviousPage (SfxItemSet& rSet) +{ + SdPage* pPage = GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + if (nSdPage == 0) + rSet.DisableItem( SID_GO_TO_PREVIOUS_PAGE ); +} + + +void DrawViewShell::ExecGoToFirstPage (SfxRequest& rReq) +{ + SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); +} + +void DrawViewShell::GetStateGoToFirstPage (SfxItemSet& rSet) +{ + SdPage* pPage = GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + if (nSdPage == 0) + rSet.DisableItem( SID_GO_TO_FIRST_PAGE ); +} + +void DrawViewShell::ExecGoToLastPage (SfxRequest& rReq) +{ + SetCurrentFunction( FuNavigation::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + Cancel(); +} + +void DrawViewShell::GetStateGoToLastPage (SfxItemSet& rSet) +{ + SdPage* pPage = GetActualPage(); + sal_uInt16 nSdPage = (pPage->GetPageNum() - 1) / 2; + sal_uInt16 totalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind()); + if (nSdPage + 1 >= totalPages) + rSet.DisableItem( SID_GO_TO_LAST_PAGE ); +} + + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsb.cxx b/sd/source/ui/view/drviewsb.cxx new file mode 100644 index 000000000..6f6bba855 --- /dev/null +++ b/sd/source/ui/view/drviewsb.cxx @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + +bool DrawViewShell::RenameSlide( sal_uInt16 nPageId, const OUString & rName ) +{ + bool bOutDummy; + if( GetDoc()->GetPageByName( rName, bOutDummy ) != SDRPAGE_NOTFOUND ) + return false; + + SdPage* pPageToRename = nullptr; + PageKind ePageKind = GetPageKind(); + + if( GetEditMode() == EditMode::Page ) + { + pPageToRename = GetDoc()->GetSdPage( maTabControl->GetPagePos(nPageId), ePageKind ); + + // Undo + SdPage* pUndoPage = pPageToRename; + SdrLayerAdmin & rLayerAdmin = GetDoc()->GetLayerAdmin(); + SdrLayerID nBackground = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID nBgObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = mpActualPage->TRG_GetMasterPageVisibleLayers(); + + SfxUndoManager* pManager = GetDoc()->GetDocSh()->GetUndoManager(); + pManager->AddUndoAction( + std::make_unique( + GetDoc(), pUndoPage, rName, pUndoPage->GetAutoLayout(), + aVisibleLayers.IsSet( nBackground ), + aVisibleLayers.IsSet( nBgObj ))); + + // rename + pPageToRename->SetName( rName ); + + if( ePageKind == PageKind::Standard ) + { + // also rename notes-page + SdPage* pNotesPage = GetDoc()->GetSdPage( maTabControl->GetPagePos(nPageId), PageKind::Notes ); + pNotesPage->SetName( rName ); + } + } + else + { + // rename MasterPage -> rename LayoutTemplate + pPageToRename = GetDoc()->GetMasterSdPage( maTabControl->GetPagePos(nPageId), ePageKind ); + GetDoc()->RenameLayoutTemplate( pPageToRename->GetLayoutName(), rName ); + } + + bool bSuccess = (rName == pPageToRename->GetName()); + + if( bSuccess ) + { + // user edited page names may be changed by the page so update control + maTabControl->SetPageText( nPageId, rName ); + + // set document to modified state + GetDoc()->SetChanged(); + + // inform navigator about change + if (GetViewFrame()) + { + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_NAVIGATOR_STATE, true); + } + + // Tell the slide sorter about the name change (necessary for + // accessibility.) + slidesorter::SlideSorterViewShell* pSlideSorterViewShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pSlideSorterViewShell != nullptr) + { + pSlideSorterViewShell->GetSlideSorter().GetController().PageNameHasChanged( + maTabControl->GetPagePos(nPageId), rName); + } + } + + return bSuccess; +} + +IMPL_LINK( DrawViewShell, RenameSlideHdl, AbstractSvxNameDialog&, rDialog, bool ) +{ + OUString aNewName; + rDialog.GetName( aNewName ); + + SdPage* pCurrentPage = GetDoc()->GetSdPage( maTabControl->GetCurPagePos(), GetPageKind() ); + + return pCurrentPage && ( aNewName == pCurrentPage->GetName() || GetDocSh()->IsNewPageNameValid( aNewName ) ); +} + +void DrawViewShell::ModifyLayer ( + SdrLayer* pLayer, + const OUString& rLayerName, + const OUString& rLayerTitle, + const OUString& rLayerDesc, + bool bIsVisible, + bool bIsLocked, + bool bIsPrintable) +{ + if(!GetLayerTabControl()) // #i87182# + { + OSL_ENSURE(false, "No LayerTabBar (!)"); + return; + } + + if( !pLayer ) + return; + + const sal_uInt16 nPageCount = GetLayerTabControl()->GetPageCount(); + sal_uInt16 nCurPage = 0; + sal_uInt16 nPos; + for( nPos = 0; nPos < nPageCount; nPos++ ) + { + sal_uInt16 nId = GetLayerTabControl()->GetPageId( nPos ); + if (GetLayerTabControl()->GetLayerName(nId) == pLayer->GetName()) + { + nCurPage = nId; + break; + } + } + + pLayer->SetName( rLayerName ); + pLayer->SetTitle( rLayerTitle ); + pLayer->SetDescription( rLayerDesc ); + mpDrawView->SetLayerVisible( rLayerName, bIsVisible ); + mpDrawView->SetLayerLocked( rLayerName, bIsLocked); + mpDrawView->SetLayerPrintable(rLayerName, bIsPrintable); + + GetDoc()->SetChanged(); + + GetLayerTabControl()->SetPageText(nCurPage, rLayerName); + + // Set page bits for modified tab name display + + TabBarPageBits nBits = TabBarPageBits::NONE; + + if (!bIsVisible) + { + nBits = TabBarPageBits::Blue; + } + if (bIsLocked) + { + nBits |= TabBarPageBits::Italic; + } + if (!bIsPrintable) + { + nBits |= TabBarPageBits::Underline; + } + + // Save the bits + + GetLayerTabControl()->SetPageBits(nCurPage, nBits); + + GetViewFrame()->GetDispatcher()->Execute( + SID_SWITCHLAYER, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + + // Call Invalidate at the form shell. + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + pFormShell->Invalidate(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsc.cxx b/sd/source/ui/view/drviewsc.cxx new file mode 100644 index 000000000..6be86e63c --- /dev/null +++ b/sd/source/ui/view/drviewsc.cxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +namespace sd { + +void DrawViewShell::UpdateIMapDlg( SdrObject* pObj ) +{ + if( ( dynamic_cast< SdrGrafObj *>( pObj ) == nullptr && dynamic_cast< SdrOle2Obj *>( pObj ) == nullptr ) + || mpDrawView->IsTextEdit() + || !GetViewFrame()->HasChildWindow( SvxIMapDlgChildWindow::GetChildWindowId() ) ) + return; + + Graphic aGraphic; + ImageMap* pIMap = nullptr; + std::unique_ptr pTargetList; + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo( pObj ); + + // get graphic from shape + SdrGrafObj* pGrafObj = dynamic_cast< SdrGrafObj* >( pObj ); + if( pGrafObj ) + aGraphic = pGrafObj->GetGraphic(); + + if ( pIMapInfo ) + { + pIMap = const_cast(&pIMapInfo->GetImageMap()); + pTargetList.reset(new TargetList); + SfxViewFrame::GetTargetList( *pTargetList ); + } + + SvxIMapDlgChildWindow::UpdateIMapDlg( aGraphic, pIMap, pTargetList.get(), pObj ); +} + +IMPL_LINK( DrawViewShell, NameObjectHdl, AbstractSvxObjectNameDialog&, rDialog, bool ) +{ + OUString aName; + rDialog.GetName( aName ); + return aName.isEmpty() || ( GetDoc() && !GetDoc()->GetObj( aName ) ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsd.cxx b/sd/source/ui/view/drviewsd.cxx new file mode 100644 index 000000000..31fe06dde --- /dev/null +++ b/sd/source/ui/view/drviewsd.cxx @@ -0,0 +1,193 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace sd { + +/** + * handle SfxRequests for navigator + */ +void DrawViewShell::ExecNavigatorWin( SfxRequest& rReq ) +{ + CheckLineTo (rReq); + + sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_NAVIGATOR_INIT: + { + SfxChildWindow* pWindow = GetViewFrame()->GetChildWindow( SID_NAVIGATOR ); + if( pWindow ) + { + SdNavigatorFloat* pNavWin = static_cast(pWindow->GetWindow()); + if( pNavWin ) + pNavWin->InitTreeLB( GetDoc() ); + } + } + break; + + case SID_NAVIGATOR_PAGE: + case SID_NAVIGATOR_OBJECT: + { + if (nSId == SID_NAVIGATOR_PAGE) + { + if ( mpDrawView->IsTextEdit() ) + mpDrawView->SdrEndTextEdit(); + + const SfxItemSet* pArgs = rReq.GetArgs(); + PageJump eJump = static_cast(static_cast( pArgs-> + Get(SID_NAVIGATOR_PAGE)).GetValue()); + + switch (eJump) + { + case PAGE_FIRST: + { + // jump to first page + SwitchPage(0); + } + break; + + case PAGE_LAST: + { + // jump to last page + SwitchPage(GetDoc()->GetSdPageCount(mpActualPage->GetPageKind()) - 1); + } + break; + + case PAGE_NEXT: + { + // jump to next page + sal_uInt16 nSdPage = (mpActualPage->GetPageNum() - 1) / 2; + + if (nSdPage < GetDoc()->GetSdPageCount(mpActualPage->GetPageKind()) - 1) + { + SwitchPage(nSdPage + 1); + } + } + break; + + case PAGE_PREVIOUS: + { + // jump to previous page + sal_uInt16 nSdPage = (mpActualPage->GetPageNum() - 1) / 2; + + if (nSdPage > 0) + { + SwitchPage(nSdPage - 1); + } + } + break; + + case PAGE_NONE: + break; + } + } + else if (nSId == SID_NAVIGATOR_OBJECT) + { + OUString aBookmarkStr("#"); + const SfxItemSet* pArgs = rReq.GetArgs(); + OUString aTarget = static_cast( pArgs-> + Get(SID_NAVIGATOR_OBJECT)).GetValue(); + aBookmarkStr += aTarget; + SfxStringItem aStrItem(SID_FILE_NAME, aBookmarkStr); + SfxStringItem aReferer(SID_REFERER, GetDocSh()->GetMedium()->GetName()); + SfxViewFrame* pFrame = GetViewFrame(); + SfxFrameItem aFrameItem(SID_DOCFRAME, pFrame); + SfxBoolItem aBrowseItem(SID_BROWSE, true); + pFrame->GetDispatcher()-> + ExecuteList(SID_OPENDOC, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aStrItem, &aFrameItem, &aBrowseItem, &aReferer }); + } + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_NAVIGATOR_STATE ); + rBindings.Invalidate( SID_NAVIGATOR_PAGENAME ); + } + break; + + default: + break; + } +} + +void DrawViewShell::GetNavigatorWinState( SfxItemSet& rSet ) +{ + NavState nState = NavState::NONE; + sal_uInt16 nCurrentPage = 0; + sal_uInt16 nLastPage; + OUString aPageName; + + nState |= NavState::TableUpdate; + + if (mpActualPage != nullptr) + { + nCurrentPage = ( mpActualPage->GetPageNum() - 1 ) / 2; + aPageName = mpActualPage->GetName(); + } + nLastPage = GetDoc()->GetSdPageCount( mePageKind ) - 1; + + + // first page / previous page + if( nCurrentPage == 0 ) + { + nState |= NavState::BtnFirstDisabled | NavState::BtnPrevDisabled; + } + else + { + nState |= NavState::BtnFirstEnabled | NavState::BtnPrevEnabled; + } + + // last page / next page + if( nCurrentPage == nLastPage ) + { + nState |= NavState::BtnLastDisabled | NavState::BtnNextDisabled; + } + else + { + nState |= NavState::BtnLastEnabled | NavState::BtnNextEnabled; + } + + rSet.Put( SfxUInt32Item( SID_NAVIGATOR_STATE, static_cast(nState) ) ); + rSet.Put( SfxStringItem( SID_NAVIGATOR_PAGENAME, aPageName ) ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewse.cxx b/sd/source/ui/view/drviewse.cxx new file mode 100644 index 000000000..309eb2b85 --- /dev/null +++ b/sd/source/ui/view/drviewse.cxx @@ -0,0 +1,1701 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; +using namespace ::com::sun::star::beans; + +namespace sd { + +// Permanent Functions + +static void ImpAddPrintableCharactersToTextEdit(SfxRequest const & rReq, ::sd::View* pView) +{ + // evtl. feed characters to activated textedit + const SfxItemSet* pSet = rReq.GetArgs(); + + if(!pSet) + return; + + OUString aInputString; + + if(SfxItemState::SET == pSet->GetItemState(SID_ATTR_CHAR)) + aInputString = static_cast(pSet->Get(SID_ATTR_CHAR)).GetValue(); + + if(aInputString.isEmpty()) + return; + + OutlinerView* pOLV = pView->GetTextEditOutlinerView(); + + if(pOLV) + { + for(sal_Int32 a(0); a < aInputString.getLength(); a++) + { + vcl::KeyCode aKeyCode; + // tdf#38669 - create the key event using a Unicode character + KeyEvent aKeyEvent(aInputString[a], aKeyCode); + + // add actual character + pOLV->PostKeyEvent(aKeyEvent); + } + } +} + +void DrawViewShell::FuPermanent(SfxRequest& rReq) +{ + // We do not execute a thing during a native slide show + + if (SlideShow::IsRunning(GetViewShellBase())) + return; + + sal_uInt16 nSId = rReq.GetSlot(); + + if( HasCurrentFunction() && + ( nSId == SID_TEXTEDIT || nSId == SID_ATTR_CHAR || nSId == SID_TEXT_FITTOSIZE || + nSId == SID_ATTR_CHAR_VERTICAL || nSId == SID_TEXT_FITTOSIZE_VERTICAL ) ) + { + rtl::Reference xFunc( GetCurrentFunction() ); + + FuText* pFuText = dynamic_cast< FuText* >( xFunc.get() ); + + if( pFuText ) + { + pFuText->SetPermanent(true); + xFunc->ReceiveRequest( rReq ); + + Invalidate(); + + // evtl. feed characters to activated textedit + if(SID_ATTR_CHAR == nSId && GetView() && GetView()->IsTextEdit()) + ImpAddPrintableCharactersToTextEdit(rReq, GetView()); + + rReq.Done(); + return; + } + } + + CheckLineTo (rReq); + sal_uInt16 nOldSId = 0; + bool bPermanent = false; + + if( !mpDrawView ) + return; + + if(HasCurrentFunction()) + { + if( (nSId == SID_FORMATPAINTBRUSH) && (GetCurrentFunction()->GetSlotID() == SID_TEXTEDIT) ) + { + // save text edit mode for format paintbrush! + SetOldFunction( GetCurrentFunction() ); + } + else + { + if(GetOldFunction() == GetCurrentFunction()) + { + SetOldFunction(nullptr); + } + } + + if ( nSId != SID_TEXTEDIT && nSId != SID_ATTR_CHAR && nSId != SID_TEXT_FITTOSIZE && + nSId != SID_ATTR_CHAR_VERTICAL && nSId != SID_TEXT_FITTOSIZE_VERTICAL && + nSId != SID_FORMATPAINTBRUSH && + mpDrawView->IsTextEdit() ) + { + mpDrawView->SdrEndTextEdit(); + } + + if( HasCurrentFunction() ) + { + nOldSId = GetCurrentFunction()->GetSlotID(); + + if (nOldSId == nSId || + ((nOldSId == SID_TEXTEDIT || nOldSId == SID_ATTR_CHAR || nOldSId == SID_TEXT_FITTOSIZE || + nOldSId == SID_ATTR_CHAR_VERTICAL || nOldSId == SID_TEXT_FITTOSIZE_VERTICAL) && + (nSId == SID_TEXTEDIT || nSId == SID_ATTR_CHAR || nSId == SID_TEXT_FITTOSIZE || + nSId == SID_ATTR_CHAR_VERTICAL || nSId == SID_TEXT_FITTOSIZE_VERTICAL ))) + { + bPermanent = true; + } + + GetCurrentFunction()->Deactivate(); + } + + SetCurrentFunction(nullptr); + + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate(nOldSId); + rBind.Update(nOldSId); + } + + // for LibreOfficeKit - choosing a shape should construct it directly + bool bCreateDirectly = false; + + switch ( nSId ) + { + case SID_TEXTEDIT: // BASIC ??? + case SID_ATTR_CHAR: + case SID_ATTR_CHAR_VERTICAL: + case SID_TEXT_FITTOSIZE: + case SID_TEXT_FITTOSIZE_VERTICAL: + { + SetCurrentFunction( FuText::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + GetCurrentFunction()->DoExecute(rReq); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_ATTR_CHAR ); + rBindings.Invalidate( SID_ATTR_CHAR_VERTICAL ); + rBindings.Invalidate( SID_TEXT_FITTOSIZE ); + rBindings.Invalidate( SID_TEXT_FITTOSIZE_VERTICAL ); + + // evtl. feed characters to activated textedit + if(SID_ATTR_CHAR == nSId && GetView() && GetView()->IsTextEdit()) + ImpAddPrintableCharactersToTextEdit(rReq, GetView()); + + rReq.Done(); + + const SfxItemSet* pArgs = rReq.GetArgs(); + if (pArgs && pArgs->HasItem(FN_PARAM_1)) + bCreateDirectly = static_cast(pArgs->Get(FN_PARAM_1)).GetValue(); + } + break; + + case SID_FM_CREATE_CONTROL: + { + SetCurrentFunction( FuConstructUnoControl::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + rReq.Done(); + } + break; + + case SID_FM_CREATE_FIELDCONTROL: + { + const SfxUnoAnyItem* pDescriptorItem = rReq.GetArg(SID_FM_DATACCESS_DESCRIPTOR); + DBG_ASSERT( pDescriptorItem, "DrawViewShell::FuPermanent(SID_FM_CREATE_FIELDCONTROL): invalid request args!" ); + + if(pDescriptorItem) + { + // get the form view + FmFormView* pFormView = mpDrawView.get(); + SdrPageView* pPageView = pFormView ? pFormView->GetSdrPageView() : nullptr; + + if(pPageView) + { + svx::ODataAccessDescriptor aDescriptor(pDescriptorItem->GetValue()); + SdrObjectUniquePtr pNewDBField = pFormView->CreateFieldControl(aDescriptor); + + if(pNewDBField) + { + ::tools::Rectangle aVisArea = GetActiveWindow()->PixelToLogic(::tools::Rectangle(Point(0,0), GetActiveWindow()->GetOutputSizePixel())); + Point aObjPos(aVisArea.Center()); + Size aObjSize(pNewDBField->GetLogicRect().GetSize()); + aObjPos.AdjustX( -(aObjSize.Width() / 2) ); + aObjPos.AdjustY( -(aObjSize.Height() / 2) ); + ::tools::Rectangle aNewObjectRectangle(aObjPos, aObjSize); + + pNewDBField->SetLogicRect(aNewObjectRectangle); + + GetView()->InsertObjectAtView(pNewDBField.release(), *pPageView); + } + } + } + rReq.Done(); + } + break; + + case SID_OBJECT_SELECT: + case SID_OBJECT_ROTATE: + case SID_OBJECT_MIRROR: + case SID_OBJECT_CROP: + case SID_OBJECT_TRANSPARENCE: + case SID_OBJECT_GRADIENT: + case SID_OBJECT_SHEAR: + case SID_OBJECT_CROOK_ROTATE: + case SID_OBJECT_CROOK_SLANT: + case SID_OBJECT_CROOK_STRETCH: + case SID_CONVERT_TO_3D_LATHE: + { + sal_uInt16 nSlotId = rReq.GetSlot(); + + // toggle function + if( nOldSId == nSlotId ) + { + nSlotId = SID_OBJECT_SELECT; + rReq.SetSlot( nSlotId ); + } + + if (nSlotId == SID_OBJECT_CROOK_ROTATE || + nSlotId == SID_OBJECT_CROOK_SLANT || + nSlotId == SID_OBJECT_CROOK_STRETCH) + { + if ( mpDrawView->GetMarkedObjectList().GetMarkCount() > 0 && + !mpDrawView->IsCrookAllowed( mpDrawView->IsCrookNoContortion() ) ) + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + std::unique_ptr xQueryBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + SdResId(STR_ASK_FOR_CONVERT_TO_BEZIER))); + if (xQueryBox->run() == RET_YES ) + { + // implicit transformation into bezier + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPathObj(false); + } + } + } + } + else if (nSlotId == SID_OBJECT_SHEAR) + { + size_t i = 0; + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCnt = rMarkList.GetMarkCount(); + bool b3DObjMarked = false; + + while (i < nMarkCnt && !b3DObjMarked) + { + if (nullptr != dynamic_cast< E3dObject *>( rMarkList.GetMark(i)->GetMarkedSdrObj() )) + { + b3DObjMarked = true; + } + else + { + i++; + } + } + + if ( nMarkCnt > 0 && !b3DObjMarked && + (!mpDrawView->IsShearAllowed() || !mpDrawView->IsDistortAllowed()) ) + { + if ( mpDrawView->IsPresObjSelected() ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + std::unique_ptr xQueryBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + SdResId(STR_ASK_FOR_CONVERT_TO_BEZIER))); + if (xQueryBox->run() == RET_YES) + { + // implicit transformation into bezier + weld::WaitObject aWait(GetFrameWeld()); + mpDrawView->ConvertMarkedToPathObj(false); + } + } + } + } + + SetCurrentFunction( FuSelection::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq) ); + rReq.Done(); + Invalidate( SID_OBJECT_SELECT ); + } + break; + + case SID_DRAW_LINE: + case SID_DRAW_XLINE: + case SID_DRAW_MEASURELINE: + case SID_LINE_ARROW_START: + case SID_LINE_ARROW_END: + case SID_LINE_ARROWS: + case SID_LINE_ARROW_CIRCLE: + case SID_LINE_CIRCLE_ARROW: + case SID_LINE_ARROW_SQUARE: + case SID_LINE_SQUARE_ARROW: + + case SID_DRAW_RECT: + case SID_DRAW_RECT_NOFILL: + case SID_DRAW_RECT_ROUND: + case SID_DRAW_RECT_ROUND_NOFILL: + case SID_DRAW_SQUARE: + case SID_DRAW_SQUARE_NOFILL: + case SID_DRAW_SQUARE_ROUND: + case SID_DRAW_SQUARE_ROUND_NOFILL: + case SID_DRAW_ELLIPSE: + case SID_DRAW_ELLIPSE_NOFILL: + case SID_DRAW_CIRCLE: + case SID_DRAW_CIRCLE_NOFILL: + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + case SID_TOOL_CONNECTOR: + case SID_CONNECTOR_ARROW_START: + case SID_CONNECTOR_ARROW_END: + case SID_CONNECTOR_ARROWS: + case SID_CONNECTOR_CIRCLE_START: + case SID_CONNECTOR_CIRCLE_END: + case SID_CONNECTOR_CIRCLES: + case SID_CONNECTOR_LINE: + case SID_CONNECTOR_LINE_ARROW_START: + case SID_CONNECTOR_LINE_ARROW_END: + case SID_CONNECTOR_LINE_ARROWS: + case SID_CONNECTOR_LINE_CIRCLE_START: + case SID_CONNECTOR_LINE_CIRCLE_END: + case SID_CONNECTOR_LINE_CIRCLES: + case SID_CONNECTOR_CURVE: + case SID_CONNECTOR_CURVE_ARROW_START: + case SID_CONNECTOR_CURVE_ARROW_END: + case SID_CONNECTOR_CURVE_ARROWS: + case SID_CONNECTOR_CURVE_CIRCLE_START: + case SID_CONNECTOR_CURVE_CIRCLE_END: + case SID_CONNECTOR_CURVE_CIRCLES: + case SID_CONNECTOR_LINES: + case SID_CONNECTOR_LINES_ARROW_START: + case SID_CONNECTOR_LINES_ARROW_END: + case SID_CONNECTOR_LINES_ARROWS: + case SID_CONNECTOR_LINES_CIRCLE_START: + case SID_CONNECTOR_LINES_CIRCLE_END: + case SID_CONNECTOR_LINES_CIRCLES: + case SID_INSERT_SIGNATURELINE: + { + bCreateDirectly = comphelper::LibreOfficeKit::isActive(); + SetCurrentFunction( FuConstructRectangle::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + rReq.Done(); + } + break; + case SID_DRAW_POLYGON: + case SID_DRAW_POLYGON_NOFILL: + case SID_DRAW_XPOLYGON: + case SID_DRAW_XPOLYGON_NOFILL: + case SID_DRAW_FREELINE: + case SID_DRAW_FREELINE_NOFILL: + case SID_DRAW_BEZIER_FILL: // BASIC + case SID_DRAW_BEZIER_NOFILL: // BASIC + { + SetCurrentFunction( FuConstructBezierPolygon::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent) ); + rReq.Done(); + } + break; + + case SID_GLUE_EDITMODE: + { + if (nOldSId != SID_GLUE_EDITMODE) + { + SetCurrentFunction( FuEditGluePoints::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + } + else + { + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + + rReq.Done(); + } + break; + + case SID_DRAW_ARC: + case SID_DRAW_CIRCLEARC: + case SID_DRAW_PIE: + case SID_DRAW_PIE_NOFILL: + case SID_DRAW_CIRCLEPIE: + case SID_DRAW_CIRCLEPIE_NOFILL: + case SID_DRAW_ELLIPSECUT: + case SID_DRAW_ELLIPSECUT_NOFILL: + case SID_DRAW_CIRCLECUT: + case SID_DRAW_CIRCLECUT_NOFILL: + { + SetCurrentFunction( FuConstructArc::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent) ); + rReq.Done(); + } + break; + + case SID_3D_CUBE: + case SID_3D_SHELL: + case SID_3D_SPHERE: + case SID_3D_TORUS: + case SID_3D_HALF_SPHERE: + case SID_3D_CYLINDER: + case SID_3D_CONE: + case SID_3D_PYRAMID: + { + SetCurrentFunction( FuConstruct3dObject::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + rReq.Done(); + } + break; + + case SID_DRAWTBX_CS_BASIC : + case SID_DRAWTBX_CS_SYMBOL : + case SID_DRAWTBX_CS_ARROW : + case SID_DRAWTBX_CS_FLOWCHART : + case SID_DRAWTBX_CS_CALLOUT : + case SID_DRAWTBX_CS_STAR : + case SID_DRAW_CS_ID : + { + SetCurrentFunction( FuConstructCustomShape::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq, bPermanent ) ); + rReq.Done(); + + bCreateDirectly = comphelper::LibreOfficeKit::isActive(); + const SfxItemSet* pArgs = rReq.GetArgs(); + if (pArgs && pArgs->HasItem(FN_PARAM_1)) + { + bCreateDirectly = static_cast(pArgs->Get(FN_PARAM_1)).GetValue(); + } + + if ( nSId != SID_DRAW_CS_ID ) + { + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( nSId ); + rBind.Update( nSId ); + } + } + break; + + case SID_FORMATPAINTBRUSH: + { + SetCurrentFunction( FuFormatPaintBrush::Create( this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + rReq.Done(); + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( nSId ); + rBind.Update( nSId ); + break; + } + + case SID_ZOOM_MODE: + case SID_ZOOM_PANNING: + { + if (nOldSId != nSId) + { + mbZoomOnPage = false; + SetCurrentFunction( FuZoom::Create(this, GetActiveWindow(), mpDrawView.get(), GetDoc(), rReq ) ); + } + else + { + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + } + rReq.Done(); + } + break; + + default: + break; + } + + if(HasOldFunction()) + { + sal_uInt16 nSlotId = GetOldFunction()->GetSlotID(); + + GetOldFunction()->Deactivate(); + SetOldFunction(nullptr); + + SfxBindings& rBind = GetViewFrame()->GetBindings(); + rBind.Invalidate( nSlotId ); + rBind.Update( nSlotId ); + } + + if(HasCurrentFunction()) + { + GetCurrentFunction()->Activate(); + SetOldFunction( GetCurrentFunction() ); + } + + // invalidate shell, is faster than every individually (says MI) + // now explicit the last slot incl. Update() + Invalidate(); + + // CTRL-SID_OBJECT_SELECT -> select first draw object if none is selected yet + if(SID_OBJECT_SELECT == nSId && HasCurrentFunction() && (rReq.GetModifier() & KEY_MOD1)) + { + if(!GetView()->AreObjectsMarked()) + { + // select first object + GetView()->UnmarkAllObj(); + GetView()->MarkNextObj(true); + + // ...and make it visible + if(GetView()->AreObjectsMarked()) + GetView()->MakeVisible(GetView()->GetAllMarkedRect(), *GetActiveWindow()); + } + } + + // with qualifier construct directly + if(!(HasCurrentFunction() && ((rReq.GetModifier() & KEY_MOD1) || bCreateDirectly))) + return; + + // disable interactive drawing for LOK + if (bCreateDirectly) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + // get SdOptions + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDoc()->GetDocumentType()); + sal_uInt32 nDefaultObjectSizeWidth(pOptions->GetDefaultObjectSizeWidth()); + sal_uInt32 nDefaultObjectSizeHeight(pOptions->GetDefaultObjectSizeHeight()); + + // calc position and size + ::tools::Rectangle aVisArea = GetActiveWindow()->PixelToLogic(::tools::Rectangle(Point(0,0), GetActiveWindow()->GetOutputSizePixel())); + if (comphelper::LibreOfficeKit::isActive()) + { + // aVisArea is nonsensical in the LOK case, use the slide size + aVisArea = ::tools::Rectangle(Point(), getCurrentPage()->GetSize()); + } + + Point aPagePos = aVisArea.Center(); + aPagePos.AdjustX( -sal_Int32(nDefaultObjectSizeWidth / 2) ); + aPagePos.AdjustY( -sal_Int32(nDefaultObjectSizeHeight / 2) ); + ::tools::Rectangle aNewObjectRectangle(aPagePos, Size(nDefaultObjectSizeWidth, nDefaultObjectSizeHeight)); + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if(!pPageView) + return; + + // create the default object + SdrObjectUniquePtr pObj = GetCurrentFunction()->CreateDefaultObject(nSId, aNewObjectRectangle); + + if(!pObj) + return; + + auto pObjTmp = pObj.get(); + // insert into page + GetView()->InsertObjectAtView(pObj.release(), *pPageView); + + // Now that pFuActual has done what it was created for we + // can switch on the edit mode for callout objects. + switch (nSId) + { + case SID_DRAW_CAPTION: + case SID_DRAW_CAPTION_VERTICAL: + { + // Make FuText the current function. + SfxUInt16Item aItem (SID_TEXTEDIT, 1); + GetViewFrame()->GetDispatcher()-> + ExecuteList(SID_TEXTEDIT, SfxCallMode::SYNCHRON | + SfxCallMode::RECORD, { &aItem }); + // Put text object into edit mode. + GetView()->SdrBeginTextEdit(static_cast(pObjTmp), pPageView); + break; + } + } +} + +void DrawViewShell::FuDeleteSelectedObjects() +{ + if( !mpDrawView ) + return; + + bool bConsumed = false; + + //if any placeholders are selected + if (mpDrawView->IsPresObjSelected(false)) + { + //If there are placeholders in the list which can be toggled + //off in edit->master->master elements then do that here, + std::vector aPresMarksToRemove; + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + for (size_t i=0; i < rMarkList.GetMarkCount(); ++i) + { + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + SdPage* pPage = static_cast(pObj->getSdrPageFromSdrObject()); + PresObjKind eKind = pPage->GetPresObjKind(pObj); + if (eKind == PresObjKind::Footer || eKind == PresObjKind::Header || + eKind == PresObjKind::DateTime || eKind == PresObjKind::SlideNumber) + { + aPresMarksToRemove.push_back(pObj); + } + } + + for (SdrObject* pObj : aPresMarksToRemove) + { + //Unmark object + mpDrawView->MarkObj(pObj, mpDrawView->GetSdrPageView(), true); + SdPage* pPage = static_cast(pObj->getSdrPageFromSdrObject()); + //remove placeholder from master page + pPage->DestroyDefaultPresObj(pPage->GetPresObjKind(pObj)); + } + + bConsumed = true; + } + + // placeholders which cannot be deleted selected + if (mpDrawView->IsPresObjSelected(false, true, false, true)) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + bConsumed = true; + } + + if (bConsumed) + return; + + vcl::KeyCode aKCode(KEY_DELETE); + KeyEvent aKEvt( 0, aKCode); + + bConsumed = mpDrawView->getSmartTags().KeyInput( aKEvt ); + + if (!bConsumed && HasCurrentFunction()) + bConsumed = GetCurrentFunction()->KeyInput(aKEvt); + + if (!bConsumed) + mpDrawView->DeleteMarked(); +} + +void DrawViewShell::FuSupport(SfxRequest& rReq) +{ + if( rReq.GetSlot() == SID_STYLE_FAMILY && rReq.GetArgs()) + GetDocSh()->SetStyleFamily(static_cast(rReq.GetArgs()->Get( SID_STYLE_FAMILY ).GetValue())); + + // We do not execute a thing during a native slide show + if(SlideShow::IsRunning(GetViewShellBase()) && + (rReq.GetSlot() != SID_PRESENTATION_END && + rReq.GetSlot() != SID_SIZE_PAGE)) + return; + + CheckLineTo (rReq); + + if( !mpDrawView ) + return; + + sal_uInt16 nSId = rReq.GetSlot(); + + switch ( nSId ) + { + case SID_CLEAR_UNDO_STACK: + { + GetDocSh()->ClearUndoBuffer(); + rReq.Ignore (); + } + break; + + case SID_PRESENTATION: + case SID_PRESENTATION_CURRENT_SLIDE: + case SID_REHEARSE_TIMINGS: + { + slideshowhelp::ShowSlideShow(rReq, *GetDoc()); + rReq.Ignore (); + } + break; + + case SID_PRESENTATION_END: + { + StopSlideShow(); + + rReq.Ignore (); + } + break; + + case SID_BEZIER_EDIT: + { + mpDrawView->SetFrameDragSingles(!mpDrawView->IsFrameDragSingles()); + + /****************************************************************** + * turn ObjectBar on + ******************************************************************/ + if( dynamic_cast< FuSelection* >( GetCurrentFunction().get() ) || dynamic_cast< FuConstructBezierPolygon* >( GetCurrentFunction().get() ) ) + { + // Tell the tool bar manager about the context change. + GetViewShellBase().GetToolBarManager()->SelectionHasChanged(*this,*mpDrawView); + } + + Invalidate(SID_BEZIER_EDIT); + rReq.Ignore(); + } + break; + + case SID_OBJECT_CLOSE: + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + if ( rMarkList.GetMark(0) && !mpDrawView->IsAction() ) + { + SdrPathObj* pPathObj = static_cast( rMarkList.GetMark(0)->GetMarkedSdrObj()); + const bool bUndo = mpDrawView->IsUndoEnabled(); + if( bUndo ) + mpDrawView->BegUndo(SdResId(STR_UNDO_BEZCLOSE)); + + mpDrawView->UnmarkAllPoints(); + + if( bUndo ) + mpDrawView->AddUndo(std::make_unique(*pPathObj)); + + pPathObj->ToggleClosed(); + + if( bUndo ) + mpDrawView->EndUndo(); + } + rReq.Done(); + } + break; + + case SID_CUT: + { + if ( mpDrawView->IsPresObjSelected(false, true, false, true) ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + //tdf#126197: EndTextEdit in all views if current one is not in TextEdit + if ( !mpDrawView->IsTextEdit() ) + mpDrawView->EndTextEditAllViews(); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoCut(); + } + else if(mpDrawView) + { + mpDrawView->DoCut(); + } + } + rReq.Ignore (); + } + break; + + case SID_COPY: + { + if ( mpDrawView->IsPresObjSelected(false, true, false, true) ) + { + std::unique_ptr xInfoBox(Application::CreateMessageDialog(GetFrameWeld(), + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + else + { + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoCopy(); + } + else if( mpDrawView ) + { + mpDrawView->DoCopy(); + } + } + rReq.Ignore (); + } + break; + + case SID_PASTE: + { + weld::WaitObject aWait(GetFrameWeld()); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoPaste(); + } + else if(mpDrawView) + { + mpDrawView->DoPaste(); + } + + rReq.Ignore (); + } + break; + + case SID_UNICODE_NOTATION_TOGGLE: + { + if( mpDrawView->IsTextEdit() ) + { + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + if( pOLV ) + { + OUString sInput = pOLV->GetSurroundingText(); + ESelection aSel( pOLV->GetSelection() ); + if( aSel.nStartPos > aSel.nEndPos ) + aSel.nEndPos = aSel.nStartPos; + + //calculate a valid end-position by reading logical characters + sal_Int32 nUtf16Pos=0; + while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) ) + { + sInput.iterateCodePoints(&nUtf16Pos); + //The mouse can set the cursor in the middle of a multi-unit character, + // so reset to the proper end of the logical characters + if( nUtf16Pos > aSel.nEndPos ) + aSel.nEndPos = nUtf16Pos; + } + + ToggleUnicodeCodepoint aToggle; + while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) ) + --nUtf16Pos; + OUString sReplacement = aToggle.ReplacementString(); + if( !sReplacement.isEmpty() ) + { + OUString sStringToReplace = aToggle.StringToReplace(); + mpDrawView->BegUndo(sStringToReplace +"->"+ sReplacement); + aSel.nStartPos = aSel.nEndPos - sStringToReplace.getLength(); + pOLV->SetSelection( aSel ); + pOLV->InsertText(sReplacement, true); + mpDrawView->EndUndo(); + } + } + } + } + break; + + case SID_PASTE_UNFORMATTED: + { + weld::WaitObject aWait(GetFrameWeld()); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoPasteUnformatted(); + } + else if(mpDrawView) + { + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + if (aDataHelper.GetTransferable().is()) + { + sal_Int8 nAction = DND_ACTION_COPY; + mpDrawView->InsertData( aDataHelper, + GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(), GetActiveWindow()->GetOutputSizePixel() ).Center() ), + nAction, false, SotClipboardFormatId::STRING); + } + } + + rReq.Ignore (); + } + break; + + case SID_CLIPBOARD_FORMAT_ITEMS: + { + weld::WaitObject aWait(GetFrameWeld()); + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + const SfxItemSet* pReqArgs = rReq.GetArgs(); + SotClipboardFormatId nFormat = SotClipboardFormatId::NONE; + + if( pReqArgs ) + { + const SfxUInt32Item* pIsActive = rReq.GetArg(SID_CLIPBOARD_FORMAT_ITEMS); + nFormat = static_cast(pIsActive->GetValue()); + } + + if( nFormat != SotClipboardFormatId::NONE && aDataHelper.GetTransferable().is() ) + { + sal_Int8 nAction = DND_ACTION_COPY; + + if( !mpDrawView->InsertData( aDataHelper, + GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(), GetActiveWindow()->GetOutputSizePixel() ).Center() ), + nAction, false, nFormat ) ) + { + INetBookmark aINetBookmark( "", "" ); + + if( ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::FILEGRPDESCRIPTOR, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::UNIFORMRESOURCELOCATOR, aINetBookmark ) ) ) + { + InsertURLField( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), "" ); + } + } + } + } + break; + + case SID_DELETE: + { + if ( mpDrawView->IsTextEdit() ) + { + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if (pOLV) + { + vcl::KeyCode aKCode(KEY_DELETE); + KeyEvent aKEvt( 0, aKCode); + // We use SdrObjEditView to handle DEL for underflow handling + (void)mpDrawView->KeyInput(aKEvt, nullptr); + } + } + else + { + mpDrawView->EndTextEditAllViews(); + FuDeleteSelectedObjects(); + } + rReq.Ignore (); + } + break; + + case SID_NOTES_MODE: + case SID_SLIDE_MASTER_MODE: + case SID_NOTES_MASTER_MODE: + case SID_HANDOUT_MASTER_MODE: + + // AutoLayouts have to be ready. + GetDoc()->StopWorkStartupDelay(); + [[fallthrough]]; + + case SID_DRAWINGMODE: + case SID_SLIDE_SORTER_MODE: + case SID_OUTLINE_MODE: + // Let the sub-shell manager handle the slot handling. + framework::FrameworkHelper::Instance(GetViewShellBase())->HandleModeChangeSlot( + nSId, + rReq); + rReq.Ignore (); + break; + + case SID_MASTERPAGE: // BASIC + { + if (comphelper::LibreOfficeKit::isActive()) + GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, + ".uno:SlideMasterPage=true"); + + // AutoLayouts needs to be finished + GetDoc()->StopWorkStartupDelay(); + + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if ( pReqArgs ) + { + const SfxBoolItem* pIsActive = rReq.GetArg(SID_MASTERPAGE); + mbIsLayerModeActive = pIsActive->GetValue (); + } + + Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_START)); + + // turn on default layer of MasterPage + mpDrawView->SetActiveLayer(sUNO_LayerName_background_objects); + + ChangeEditMode(EditMode::MasterPage, mbIsLayerModeActive); + + if(HasCurrentFunction(SID_BEZIER_EDIT)) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); + + Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_END)); + + InvalidateWindows(); + Invalidate(); + + rReq.Done(); + } + break; + + case SID_CLOSE_MASTER_VIEW: + { + // Notify of disabling master view, which is enabled in DrawViewShell::ChangeEditMode. + if (comphelper::LibreOfficeKit::isActive()) + GetViewShell()->libreOfficeKitViewCallback(LOK_CALLBACK_STATE_CHANGED, + ".uno:SlideMasterPage=false"); + + Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_START)); + + // Switch page back to the first one. Not doing so leads to a + // crash. This seems to be some bug in the edit mode switching + // and page switching methods. + SwitchPage (0); + ChangeEditMode(EditMode::Page, IsLayerModeActive()); + Broadcast ( + ViewShellHint(ViewShellHint::HINT_CHANGE_EDIT_MODE_END)); + + if(HasCurrentFunction(SID_BEZIER_EDIT)) + { + GetViewFrame()->GetDispatcher()->Execute( + SID_OBJECT_SELECT, + SfxCallMode::ASYNCHRON); + } + + rReq.Done(); + } + break; + + case SID_RULER: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + // Remember old ruler state + bool bOldHasRuler(HasRuler()); + + if ( pReqArgs ) + { + const SfxBoolItem* pIsActive = rReq.GetArg(SID_RULER); + SetRuler (pIsActive->GetValue ()); + } + else SetRuler (!HasRuler()); + + // Did ruler state change? Tell that to SdOptions, too. + bool bHasRuler(HasRuler()); + + if(bOldHasRuler != bHasRuler) + { + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDoc()->GetDocumentType()); + + if(pOptions && pOptions->IsRulerVisible() != bHasRuler) + { + pOptions->SetRulerVisible(bHasRuler); + } + } + + Invalidate (SID_RULER); + Resize(); + rReq.Done (); + } + break; + + case SID_SIZE_PAGE: + case SID_SIZE_PAGE_WIDTH: // BASIC + { + mbZoomOnPage = ( rReq.GetSlot() == SID_SIZE_PAGE ); + + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if ( pPageView ) + { + Point aPagePos(0, 0); // = pPageView->GetOffset(); + Size aPageSize = pPageView->GetPage()->GetSize(); + + aPagePos.AdjustX(aPageSize.Width() / 2 ); + aPageSize.setWidth( static_cast<::tools::Long>(aPageSize.Width() * 1.03) ); + + if( rReq.GetSlot() == SID_SIZE_PAGE ) + { + aPagePos.AdjustY(aPageSize.Height() / 2 ); + aPageSize.setHeight( static_cast<::tools::Long>(aPageSize.Height() * 1.03) ); + aPagePos.AdjustY( -(aPageSize.Height() / 2) ); + } + else + { + Point aPt = GetActiveWindow()->PixelToLogic( Point( 0, GetActiveWindow()->GetSizePixel().Height() / 2 ) ); + aPagePos.AdjustY(aPt.Y() ); + aPageSize.setHeight( 2 ); + } + + aPagePos.AdjustX( -(aPageSize.Width() / 2) ); + + SetZoomRect( ::tools::Rectangle( aPagePos, aPageSize ) ); + + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + } + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + case SID_SIZE_REAL: // BASIC + { + mbZoomOnPage = false; + SetZoom( 100 ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + case SID_ZOOM_OUT: // BASIC + { + mbZoomOnPage = false; + SetZoom( std::max<::tools::Long>( GetActiveWindow()->GetZoom() / 2, GetActiveWindow()->GetMinZoom() ) ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + case SID_ZOOM_IN: + { + mbZoomOnPage = false; + SetZoom( std::min<::tools::Long>( GetActiveWindow()->GetZoom() * 2, GetActiveWindow()->GetMaxZoom() ) ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ZOOM_IN ); + Invalidate(SID_ZOOM_OUT); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + case SID_SIZE_VISAREA: + { + ::tools::Rectangle aVisArea = mpFrameView->GetVisArea(); + Size aVisAreaSize = aVisArea.GetSize(); + + if (!aVisAreaSize.IsEmpty()) + { + mbZoomOnPage = false; + SetZoomRect(aVisArea); + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + } + rReq.Done (); + } + break; + + // name confusion: SID_SIZE_OPTIMAL -> Zoom onto selected objects + // --> Is offered as object zoom in program + case SID_SIZE_OPTIMAL: // BASIC + { + mbZoomOnPage = false; + if ( mpDrawView->AreObjectsMarked() ) + { + maMarkRect = mpDrawView->GetAllMarkedRect(); + ::tools::Long nW = static_cast<::tools::Long>(maMarkRect.GetWidth() * 1.03); + ::tools::Long nH = static_cast<::tools::Long>(maMarkRect.GetHeight() * 1.03); + Point aPos = maMarkRect.Center(); + aPos.AdjustX( -(nW / 2) ); + aPos.AdjustY( -(nH / 2) ); + if ( nW && nH ) + { + SetZoomRect(::tools::Rectangle(aPos, Size(nW, nH))); + + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + } + } + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + rReq.Done (); + } + break; + + // name confusion: SID_SIZE_ALL -> Zoom onto all objects + // --> Is offered as optimal in program + case SID_SIZE_ALL: // BASIC + { + mbZoomOnPage = false; + SdrPageView* pPageView = mpDrawView->GetSdrPageView(); + + if( pPageView ) + { + ::tools::Rectangle aBoundRect( pPageView->GetObjList()->GetAllObjBoundRect() ); + + ::tools::Long nW = static_cast<::tools::Long>(aBoundRect.GetWidth() * 1.03); + ::tools::Long nH = static_cast<::tools::Long>(aBoundRect.GetHeight() * 1.03); + Point aPos = aBoundRect.Center(); + aPos.AdjustX( -(nW / 2) ); + aPos.AdjustY( -(nH / 2) ); + if ( nW && nH ) + { + SetZoomRect( ::tools::Rectangle( aPos, Size( nW, nH ) ) ); + + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + } + + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ZOOM_OUT ); + Invalidate( SID_ZOOM_PANNING ); + } + rReq.Done (); + } + break; + + case SID_ZOOM_PREV: + { + if (mpDrawView->IsTextEdit()) + { + mpDrawView->SdrEndTextEdit(); + } + + if (mpZoomList->IsPreviousPossible()) + { + // set previous ZoomRect + SetZoomRect(mpZoomList->GetPreviousZoomRect()); + } + rReq.Done (); + } + break; + + case SID_ZOOM_NEXT: + { + if (mpDrawView->IsTextEdit()) + { + mpDrawView->SdrEndTextEdit(); + } + + if (mpZoomList->IsNextPossible()) + { + // set next ZoomRect + SetZoomRect(mpZoomList->GetNextZoomRect()); + } + rReq.Done (); + } + break; + + case SID_GLUE_INSERT_POINT: + case SID_GLUE_PERCENT: + case SID_GLUE_ESCDIR: + case SID_GLUE_ESCDIR_LEFT: + case SID_GLUE_ESCDIR_RIGHT: + case SID_GLUE_ESCDIR_TOP: + case SID_GLUE_ESCDIR_BOTTOM: + case SID_GLUE_HORZALIGN_CENTER: + case SID_GLUE_HORZALIGN_LEFT: + case SID_GLUE_HORZALIGN_RIGHT: + case SID_GLUE_VERTALIGN_CENTER: + case SID_GLUE_VERTALIGN_TOP: + case SID_GLUE_VERTALIGN_BOTTOM: + { + rtl::Reference xFunc( GetCurrentFunction() ); + FuEditGluePoints* pFunc = dynamic_cast< FuEditGluePoints* >( xFunc.get() ); + + if(pFunc) + pFunc->ReceiveRequest(rReq); + + rReq.Done(); + } + break; + + case SID_AUTOSPELL_CHECK: + { + bool bOnlineSpell; + const SfxPoolItem* pItem; + + if (rReq.GetArgs()->HasItem(FN_PARAM_1, &pItem)) + bOnlineSpell = static_cast(pItem)->GetValue(); + else // Toggle + bOnlineSpell = !GetDoc()->GetOnlineSpell(); + + GetDoc()->SetOnlineSpell(bOnlineSpell); + + ::Outliner* pOL = mpDrawView->GetTextEditOutliner(); + + if (pOL) + { + EEControlBits nCntrl = pOL->GetControlWord(); + + if (bOnlineSpell) + nCntrl |= EEControlBits::ONLINESPELLING; + else + nCntrl &= ~EEControlBits::ONLINESPELLING; + + pOL->SetControlWord(nCntrl); + } + + GetActiveWindow()->Invalidate(); + rReq.Done (); + } + break; + + case SID_TRANSLITERATE_SENTENCE_CASE: + case SID_TRANSLITERATE_TITLE_CASE: + case SID_TRANSLITERATE_TOGGLE_CASE: + case SID_TRANSLITERATE_UPPER: + case SID_TRANSLITERATE_LOWER: + case SID_TRANSLITERATE_HALFWIDTH: + case SID_TRANSLITERATE_FULLWIDTH: + case SID_TRANSLITERATE_HIRAGANA: + case SID_TRANSLITERATE_KATAKANA: + { + OutlinerView* pOLV = GetView()->GetTextEditOutlinerView(); + if( pOLV ) + { + TransliterationFlags nType = TransliterationFlags::NONE; + + switch( nSId ) + { + case SID_TRANSLITERATE_SENTENCE_CASE: + nType = TransliterationFlags::SENTENCE_CASE; + break; + case SID_TRANSLITERATE_TITLE_CASE: + nType = TransliterationFlags::TITLE_CASE; + break; + case SID_TRANSLITERATE_TOGGLE_CASE: + nType = TransliterationFlags::TOGGLE_CASE; + break; + case SID_TRANSLITERATE_UPPER: + nType = TransliterationFlags::LOWERCASE_UPPERCASE; + break; + case SID_TRANSLITERATE_LOWER: + nType = TransliterationFlags::UPPERCASE_LOWERCASE; + break; + case SID_TRANSLITERATE_HALFWIDTH: + nType = TransliterationFlags::FULLWIDTH_HALFWIDTH; + break; + case SID_TRANSLITERATE_FULLWIDTH: + nType = TransliterationFlags::HALFWIDTH_FULLWIDTH; + break; + case SID_TRANSLITERATE_HIRAGANA: + nType = TransliterationFlags::KATAKANA_HIRAGANA; + break; + case SID_TRANSLITERATE_KATAKANA: + nType = TransliterationFlags::HIRAGANA_KATAKANA; + break; + } + + pOLV->TransliterateText( nType ); + } + + rReq.Done(); + } + break; + + // #UndoRedo# + case SID_UNDO : + { + // moved implementation to BaseClass + ImpSidUndo(rReq); + } + break; + case SID_REDO : + { + // moved implementation to BaseClass + ImpSidRedo(rReq); + } + break; + + default: + break; + } +} + +void DrawViewShell::FuSupportRotate(SfxRequest const &rReq) +{ + if( rReq.GetSlot() != SID_TRANSLITERATE_ROTATE_CASE ) + return; + + ::sd::View* pView = GetView(); + + if (!pView) + return; + + OutlinerView* pOLV = pView->GetTextEditOutlinerView(); + + if (!pOLV) + return; + + pOLV->TransliterateText( m_aRotateCase.getNextMode() ); +} + +void DrawViewShell::InsertURLField(const OUString& rURL, const OUString& rText, + const OUString& rTarget) +{ + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if (pOLV) + { + ESelection aSel( pOLV->GetSelection() ); + SvxFieldItem aURLItem( SvxURLField( rURL, rText, SvxURLFormat::Repr ), EE_FEATURE_FIELD ); + pOLV->InsertField( aURLItem ); + if ( aSel.nStartPos <= aSel.nEndPos ) + aSel.nEndPos = aSel.nStartPos + 1; + else + aSel.nStartPos = aSel.nEndPos + 1; + pOLV->SetSelection( aSel ); + } + else + { + Outliner* pOutl = GetDoc()->GetInternalOutliner(); + pOutl->Init( OutlinerMode::TextObject ); + OutlinerMode nOutlMode = pOutl->GetOutlinerMode(); + + SvxURLField aURLField(rURL, rText, SvxURLFormat::Repr); + aURLField.SetTargetFrame(rTarget); + SvxFieldItem aURLItem(aURLField, EE_FEATURE_FIELD); + pOutl->QuickInsertField( aURLItem, ESelection() ); + std::optional pOutlParaObject = pOutl->CreateParaObject(); + + SdrRectObj* pRectObj = new SdrRectObj( + GetView()->getSdrModelFromSdrView(), + SdrObjKind::Text); + + pOutl->UpdateFields(); + pOutl->SetUpdateLayout( true ); + Size aSize(pOutl->CalcTextSize()); + pOutl->SetUpdateLayout( false ); + + Point aPos; + ::tools::Rectangle aRect(aPos, GetActiveWindow()->GetOutputSizePixel() ); + aPos = aRect.Center(); + aPos = GetActiveWindow()->PixelToLogic(aPos); + + if (aPos.getX() - (aSize.Width() / 2) >= 0) + aPos.AdjustX( -(aSize.Width() / 2) ); + if (aPos.getY() - (aSize.Height() / 2) >= 0) + aPos.AdjustY( -(aSize.Height() / 2) ); + + ::tools::Rectangle aLogicRect(aPos, aSize); + pRectObj->SetLogicRect(aLogicRect); + pRectObj->SetOutlinerParaObject( std::move(pOutlParaObject) ); + mpDrawView->InsertObjectAtView(pRectObj, *mpDrawView->GetSdrPageView()); + pOutl->Init( nOutlMode ); + } +} + +void DrawViewShell::InsertURLButton(const OUString& rURL, const OUString& rText, + const OUString& rTarget, const Point* pPos) +{ + bool bNewObj = true; + + const OUString sTargetURL( ::URIHelper::SmartRel2Abs( INetURLObject( GetDocSh()->GetMedium()->GetBaseURL() ), rURL, URIHelper::GetMaybeFileHdl(), true, false, + INetURLObject::EncodeMechanism::WasEncoded, + INetURLObject::DecodeMechanism::Unambiguous ) ); + if (mpDrawView->GetMarkedObjectList().GetMarkCount() > 0) + { + SdrObject* pMarkedObj = mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if( pMarkedObj ) try + { + // change first marked object + if( SdrInventor::FmForm == pMarkedObj->GetObjInventor() && pMarkedObj->GetObjIdentifier() == SdrObjKind::FormButton ) + { + bNewObj = false; + + SdrUnoObj* pUnoCtrl = static_cast< SdrUnoObj* >( pMarkedObj ); + + Reference< awt::XControlModel > xControlModel( pUnoCtrl->GetUnoControlModel(), UNO_SET_THROW ); + Reference< beans::XPropertySet > xPropSet( xControlModel, UNO_QUERY_THROW ); + + xPropSet->setPropertyValue("Label" , Any( rText ) ); + xPropSet->setPropertyValue("TargetURL" , Any( sTargetURL ) ); + + if( !rTarget.isEmpty() ) + xPropSet->setPropertyValue("TargetFrame" , Any( rTarget ) ); + + xPropSet->setPropertyValue( "ButtonType" , Any( form::FormButtonType_URL ) ); +#if HAVE_FEATURE_AVMEDIA + if ( ::avmedia::MediaWindow::isMediaURL( rURL, ""/*TODO?*/ ) ) + { + xPropSet->setPropertyValue( "DispatchURLInternal" , Any( true ) ); + } +#endif + } + else + { + // add url as interaction for first selected shape + bNewObj = false; + + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pMarkedObj, true); + pInfo->meClickAction = presentation::ClickAction_DOCUMENT; + pInfo->SetBookmark( sTargetURL ); + } + } + catch( uno::Exception& ) + { + } + } + + if (!bNewObj) + return; + + try + { + SdrUnoObj* pUnoCtrl = static_cast< SdrUnoObj* >( + SdrObjFactory::MakeNewObject( + GetView()->getSdrModelFromSdrView(), + SdrInventor::FmForm, + SdrObjKind::FormButton)); //, + //mpDrawView->GetSdrPageView()->GetPage())); + + Reference< awt::XControlModel > xControlModel( pUnoCtrl->GetUnoControlModel(), uno::UNO_SET_THROW ); + Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY_THROW ); + + xPropSet->setPropertyValue( "Label" , Any( rText ) ); + xPropSet->setPropertyValue( "TargetURL" , Any( sTargetURL ) ); + + if( !rTarget.isEmpty() ) + xPropSet->setPropertyValue( "TargetFrame" , Any( rTarget ) ); + + xPropSet->setPropertyValue( "ButtonType" , Any( form::FormButtonType_URL ) ); +#if HAVE_FEATURE_AVMEDIA + if ( ::avmedia::MediaWindow::isMediaURL( rURL, ""/*TODO?*/ ) ) + xPropSet->setPropertyValue( "DispatchURLInternal" , Any( true ) ); +#endif + + Point aPos; + + if (pPos) + { + aPos = *pPos; + } + else + { + aPos = ::tools::Rectangle(aPos, GetActiveWindow()->GetOutputSizePixel()).Center(); + aPos = GetActiveWindow()->PixelToLogic(aPos); + } + + Size aSize(4000, 1000); + aPos.AdjustX( -(aSize.Width() / 2) ); + aPos.AdjustY( -(aSize.Height() / 2) ); + pUnoCtrl->SetLogicRect(::tools::Rectangle(aPos, aSize)); + + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + + OSL_ASSERT (GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient = GetViewShell()->GetIPClient(); + if (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive()) + { + nOptions |= SdrInsertFlags::DONTMARK; + } + + mpDrawView->InsertObjectAtView(pUnoCtrl, *mpDrawView->GetSdrPageView(), nOptions); + } + catch( Exception& ) + { + } +} + +void DrawViewShell::ShowUIControls (bool bVisible) +{ + ViewShell::ShowUIControls (bVisible); + maTabControl->Show (bVisible); +} + +namespace slideshowhelp +{ + void ShowSlideShow(SfxRequest const & rReq, SdDrawDocument &rDoc) + { + Reference< XPresentation2 > xPresentation( rDoc.getPresentation() ); + if( !xPresentation.is() ) + return; + + sfx2::SfxNotebookBar::LockNotebookBar(); + if (SID_REHEARSE_TIMINGS == rReq.GetSlot()) + xPresentation->rehearseTimings(); + else if (rDoc.getPresentationSettings().mbCustomShow) + { + //fdo#69975 if a custom show has been set, then + //use it whether or not we've been asked to + //start from the current or first slide + xPresentation->start(); + + // if the custom show not set by default, only show it. + if (rDoc.getPresentationSettings().mbStartCustomShow) + rDoc.getPresentationSettings().mbCustomShow = false; + } + else if (SID_PRESENTATION_CURRENT_SLIDE == rReq.GetSlot()) + { + //If there is no custom show set, start will automatically + //start at the current page + xPresentation->start(); + } + else + { + //Start at page 0, this would blow away any custom + //show settings if any were set + Sequence< PropertyValue > aArguments{ comphelper::makePropertyValue("FirstPage", + OUString("0")) }; + xPresentation->startWithArguments( aArguments ); + } + sfx2::SfxNotebookBar::UnlockNotebookBar(); + } +} + +void DrawViewShell::StopSlideShow() +{ + Reference< XPresentation2 > xPresentation( GetDoc()->getPresentation() ); + if(xPresentation.is() && xPresentation->isRunning()) + { + if( mpDrawView->IsTextEdit() ) + mpDrawView->SdrEndTextEdit(); + + xPresentation->end(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsf.cxx b/sd/source/ui/view/drviewsf.cxx new file mode 100644 index 000000000..8aab2c576 --- /dev/null +++ b/sd/source/ui/view/drviewsf.cxx @@ -0,0 +1,826 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star::drawing; +using namespace svx::sidebar; +using namespace ::com::sun::star; + +namespace sd { + +/** + * Set state of controller SfxSlots + */ +void DrawViewShell::GetCtrlState(SfxItemSet &rSet) +{ + if (rSet.GetItemState(SID_RELOAD) != SfxItemState::UNKNOWN) + { + // let "last version" of SFx en/disable + GetViewFrame()->GetSlotState (SID_RELOAD, nullptr, &rSet); + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_HYPERLINK_GETLINK)) + { + SvxHyperlinkItem aHLinkItem; + + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if (pOLV) + { + const SvxFieldData* pField = pOLV->GetFieldAtCursor(); + if( auto pUrlField = dynamic_cast< const SvxURLField *>( pField ) ) + { + aHLinkItem.SetName(pUrlField->GetRepresentation()); + aHLinkItem.SetURL(pUrlField->GetURL()); + aHLinkItem.SetTargetFrame(pUrlField->GetTargetFrame()); + } + else + { + // use selected text as name for urls + OUString sReturn = pOLV->GetSelected(); + if (sReturn.getLength() > 255) + sReturn = sReturn.copy(0, 255); + aHLinkItem.SetName(comphelper::string::stripEnd(sReturn, ' ')); + } + } + else + { + if (mpDrawView->GetMarkedObjectList().GetMarkCount() > 0) + { + bool bFound = false; + + SdrObject* pMarkedObj = mpDrawView->GetMarkedObjectList().GetMark(0)->GetMarkedSdrObj(); + if( pMarkedObj && (SdrInventor::FmForm == pMarkedObj->GetObjInventor()) ) + { + SdrUnoObj* pUnoCtrl = dynamic_cast< SdrUnoObj* >( pMarkedObj ); + + if(pUnoCtrl) try + { + uno::Reference< awt::XControlModel > xControlModel( pUnoCtrl->GetUnoControlModel(), uno::UNO_SET_THROW ); + uno::Reference< beans::XPropertySet > xPropSet( xControlModel, uno::UNO_QUERY_THROW ); + uno::Reference< beans::XPropertySetInfo > xPropInfo( xPropSet->getPropertySetInfo(), uno::UNO_SET_THROW ); + + form::FormButtonType eButtonType = form::FormButtonType_URL; + static const OUStringLiteral sButtonType( u"ButtonType" ); + if(xPropInfo->hasPropertyByName( sButtonType ) && (xPropSet->getPropertyValue( sButtonType ) >>= eButtonType ) ) + { + OUString aString; + + // Label + static const OUStringLiteral sLabel( u"Label" ); + if(xPropInfo->hasPropertyByName(sLabel)) + { + if( xPropSet->getPropertyValue(sLabel) >>= aString ) + aHLinkItem.SetName(aString); + } + + // URL + static const OUStringLiteral sTargetURL( u"TargetURL" ); + if(xPropInfo->hasPropertyByName(sTargetURL)) + { + if( xPropSet->getPropertyValue(sTargetURL) >>= aString ) + aHLinkItem.SetURL(aString); + } + + // Target + static const OUStringLiteral sTargetFrame( u"TargetFrame" ); + if(xPropInfo->hasPropertyByName(sTargetFrame) ) + { + if( xPropSet->getPropertyValue(sTargetFrame) >>= aString ) + aHLinkItem.SetTargetFrame(aString); + } + + aHLinkItem.SetInsertMode(HLINK_BUTTON); + bFound = true; + } + } + catch( uno::Exception& ) + { + } + } + + // try interaction link + if( !bFound && pMarkedObj ) + { + SdAnimationInfo* pInfo = SdDrawDocument::GetShapeUserData(*pMarkedObj); + if( pInfo && (pInfo->meClickAction == presentation::ClickAction_DOCUMENT) ) + aHLinkItem.SetURL( pInfo->GetBookmark()); + aHLinkItem.SetInsertMode(HLINK_BUTTON); + } + } + } + + rSet.Put(aHLinkItem); + } + rSet.Put( SfxBoolItem( SID_READONLY_MODE, mbReadOnly ) ); + + // output quality + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTPUT_QUALITY_COLOR ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTPUT_QUALITY_GRAYSCALE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTPUT_QUALITY_BLACKWHITE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTPUT_QUALITY_CONTRAST ) ) + { + const sal_uLong nMode = static_cast(GetActiveWindow()->GetOutDev()->GetDrawMode()); + rSet.Put( SfxBoolItem( SID_OUTPUT_QUALITY_COLOR, sal_uLong(OUTPUT_DRAWMODE_COLOR) == nMode ) ); + rSet.Put( SfxBoolItem( SID_OUTPUT_QUALITY_GRAYSCALE, static_cast(OUTPUT_DRAWMODE_GRAYSCALE) == nMode ) ); + rSet.Put( SfxBoolItem( SID_OUTPUT_QUALITY_BLACKWHITE, static_cast(OUTPUT_DRAWMODE_BLACKWHITE) == nMode ) ); + rSet.Put( SfxBoolItem( SID_OUTPUT_QUALITY_CONTRAST, static_cast(OUTPUT_DRAWMODE_CONTRAST) == nMode ) ); + } + + if ( SfxItemState::DEFAULT == rSet.GetItemState(SID_MAIL_SCROLLBODY_PAGEDOWN) ) + { + rSet.Put( SfxBoolItem( SID_MAIL_SCROLLBODY_PAGEDOWN, true ) ); + } + + if ( SfxItemState::DEFAULT == rSet.GetItemState(SID_ATTR_YEAR2000) ) + { + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + { + sal_uInt16 nState = 0; + if (pFormShell->GetY2KState(nState)) + rSet.Put( SfxUInt16Item( SID_ATTR_YEAR2000, nState ) ); + else + rSet.DisableItem( SID_ATTR_YEAR2000 ); + } + } + + if ( !GetView()->GetTextEditOutliner() ) + { + if( !SvtCJKOptions::IsChangeCaseMapEnabled() ) + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, false ); + } + else + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, true ); + } + + rSet.DisableItem( SID_TRANSLITERATE_SENTENCE_CASE ); + rSet.DisableItem( SID_TRANSLITERATE_TITLE_CASE ); + rSet.DisableItem( SID_TRANSLITERATE_TOGGLE_CASE ); + rSet.DisableItem( SID_TRANSLITERATE_UPPER ); + rSet.DisableItem( SID_TRANSLITERATE_LOWER ); + rSet.DisableItem( SID_TRANSLITERATE_HALFWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_FULLWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_HIRAGANA ); + rSet.DisableItem( SID_TRANSLITERATE_KATAKANA ); + } + else + { + if( !SvtCJKOptions::IsChangeCaseMapEnabled() ) + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, false ); + rSet.DisableItem( SID_TRANSLITERATE_HALFWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_FULLWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_HIRAGANA ); + rSet.DisableItem( SID_TRANSLITERATE_KATAKANA ); + } + else + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, true ); + } + } +} + +void DrawViewShell::GetAttrState( SfxItemSet& rSet ) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + + bool bAttr = false; + SfxAllItemSet aAllSet( *rSet.GetPool() ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + switch ( nSlotId ) + { + case SID_ATTR_PARA_ADJUST_LEFT: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + SvxAdjust eAdj = aAttrs.Get( EE_PARA_JUST ).GetAdjust(); + if ( eAdj == SvxAdjust::Left) + { + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_LEFT, true ) ); + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_PARA_ADJUST_CENTER: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + SvxAdjust eAdj = aAttrs.Get( EE_PARA_JUST ).GetAdjust(); + if ( eAdj == SvxAdjust::Center) + { + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_CENTER, true ) ); + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_PARA_ADJUST_RIGHT: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + SvxAdjust eAdj = aAttrs.Get( EE_PARA_JUST ).GetAdjust(); + if ( eAdj == SvxAdjust::Right) + { + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_RIGHT, true ) ); + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_PARA_ADJUST_BLOCK: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + + SvxAdjust eAdj = aAttrs.Get( EE_PARA_JUST ).GetAdjust(); + if ( eAdj == SvxAdjust::Block) + { + rSet.Put( SfxBoolItem( SID_ATTR_PARA_ADJUST_BLOCK, true ) ); + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_PARA_LRSPACE: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + SvxLRSpaceItem aLRSpace = aAttrs.Get( EE_PARA_LRSPACE ); + aLRSpace.SetWhich(SID_ATTR_PARA_LRSPACE); + rSet.Put(aLRSpace); + bAttr = true; + Invalidate(SID_ATTR_PARA_LRSPACE); + } + break; + case SID_ATTR_PARA_LINESPACE: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + SvxLineSpacingItem aLineLR = aAttrs.Get( EE_PARA_SBL ); + rSet.Put(aLineLR); + bAttr = true; + Invalidate(SID_ATTR_PARA_LINESPACE); + } + break; + case SID_ATTR_PARA_ULSPACE: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + SvxULSpaceItem aULSP = aAttrs.Get( EE_PARA_ULSPACE ); + aULSP.SetWhich(SID_ATTR_PARA_ULSPACE); + rSet.Put(aULSP); + bAttr = true; + Invalidate(SID_ATTR_PARA_ULSPACE); + } + break; + case SID_ULINE_VAL_NONE: + case SID_ULINE_VAL_SINGLE: + case SID_ULINE_VAL_DOUBLE: + case SID_ULINE_VAL_DOTTED: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + if( aAttrs.GetItemState( EE_CHAR_UNDERLINE ) >= SfxItemState::DEFAULT ) + { + FontLineStyle eLineStyle = aAttrs.Get(EE_CHAR_UNDERLINE).GetLineStyle(); + + switch (nSlotId) + { + case SID_ULINE_VAL_NONE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_NONE)); + break; + case SID_ULINE_VAL_SINGLE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_SINGLE)); + break; + case SID_ULINE_VAL_DOUBLE: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_DOUBLE)); + break; + case SID_ULINE_VAL_DOTTED: + rSet.Put(SfxBoolItem(nSlotId, eLineStyle == LINESTYLE_DOTTED)); + break; + } + } + + bAttr = true; + + Invalidate(nSlotId); + } + break; + case SID_ATTR_FILL_STYLE: + case SID_ATTR_FILL_COLOR: + case SID_ATTR_FILL_GRADIENT: + case SID_ATTR_FILL_HATCH: + case SID_ATTR_FILL_BITMAP: + case SID_ATTR_FILL_SHADOW: + case SID_ATTR_SHADOW_COLOR: + case SID_ATTR_SHADOW_TRANSPARENCE: + case SID_ATTR_SHADOW_BLUR: + case SID_ATTR_SHADOW_XDISTANCE: + case SID_ATTR_SHADOW_YDISTANCE: + case SID_ATTR_FILL_USE_SLIDE_BACKGROUND: + case SID_ATTR_FILL_TRANSPARENCE: + case SID_ATTR_FILL_FLOATTRANSPARENCE: + case SID_ATTR_LINE_STYLE: + case SID_ATTR_LINE_DASH: + case SID_ATTR_LINE_WIDTH: + case SID_ATTR_LINE_COLOR: + case SID_ATTR_LINE_TRANSPARENCE: + case SID_ATTR_LINE_JOINT: + case SID_ATTR_LINE_CAP: + case SID_ATTR_TEXT_FITTOSIZE: + case SID_ATTR_CHAR_FONT: + case SID_ATTR_CHAR_FONTHEIGHT: + case SID_ATTR_CHAR_SHADOWED: + case SID_ATTR_CHAR_POSTURE: + case SID_ATTR_CHAR_OVERLINE: + case SID_ATTR_CHAR_UNDERLINE: + case SID_ATTR_CHAR_STRIKEOUT: + case SID_ATTR_CHAR_CONTOUR: + case SID_ATTR_CHAR_WEIGHT: + case SID_ATTR_CHAR_COLOR: + case SID_ATTR_CHAR_KERNING: + case SID_ATTR_CHAR_CASEMAP: + case SID_ATTR_GLOW_COLOR: + case SID_ATTR_GLOW_RADIUS: + case SID_ATTR_GLOW_TRANSPARENCY: + case SID_ATTR_SOFTEDGE_RADIUS: + case SID_SET_SUB_SCRIPT: + case SID_SET_SUPER_SCRIPT: + { + bAttr = true; + } + break; + + case SID_ATTR_TEXTCOLUMNS_NUMBER: + case SID_ATTR_TEXTCOLUMNS_SPACING: + { + SfxItemSet aAttrs(GetDoc()->GetPool()); + mpDrawView->GetAttributes(aAttrs); + const sal_uInt16 nActWhich = nSlotId == SID_ATTR_TEXTCOLUMNS_NUMBER + ? SDRATTR_TEXTCOLUMNS_NUMBER + : SDRATTR_TEXTCOLUMNS_SPACING; + rSet.Put(aAttrs.Get(nActWhich).CloneSetWhich(nSlotId)); + } + break; + + case SID_HYPHENATION: + { + SfxItemSet aAttrs( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aAttrs ); + if( aAttrs.GetItemState( EE_PARA_HYPHENATE ) >= SfxItemState::DEFAULT ) + { + bool bValue = aAttrs.Get( EE_PARA_HYPHENATE ).GetValue(); + rSet.Put( SfxBoolItem( SID_HYPHENATION, bValue ) ); + } + } + break; + + case SID_STYLE_FAMILY2: + case SID_STYLE_FAMILY3: + case SID_STYLE_FAMILY5: + case SID_STYLE_APPLY: // StyleControl + { + SfxStyleSheet* pStyleSheet = mpDrawView->GetStyleSheet(); + if( pStyleSheet ) + { + if( nSlotId != SID_STYLE_APPLY && !mpDrawView->AreObjectsMarked() ) + { + SfxTemplateItem aTmpItem( nWhich, OUString() ); + aAllSet.Put( aTmpItem, aTmpItem.Which() ); + } + else + { + if (pStyleSheet->GetFamily() == SfxStyleFamily::Page) + pStyleSheet = static_cast(pStyleSheet)->GetPseudoStyleSheet(); + + if( pStyleSheet ) + { + SfxStyleFamily eFamily = pStyleSheet->GetFamily(); + + if ((eFamily == SfxStyleFamily::Para && nSlotId == SID_STYLE_FAMILY2) || + (eFamily == SfxStyleFamily::Frame && nSlotId == SID_STYLE_FAMILY3) || + (eFamily == SfxStyleFamily::Pseudo && nSlotId == SID_STYLE_FAMILY5)) + { + SfxTemplateItem aTmpItem ( nWhich, pStyleSheet->GetName() ); + aAllSet.Put( aTmpItem, aTmpItem.Which() ); + } + else + { + SfxTemplateItem aTmpItem(nWhich, OUString()); + aAllSet.Put(aTmpItem,aTmpItem.Which() ); + } + } + } + } + else + { SfxTemplateItem aItem( nWhich, OUString() ); + aAllSet.Put( aItem, aItem.Which() ); + } + } + break; + + case SID_SET_DEFAULT: + { + if( !mpDrawView->GetMarkedObjectList().GetMarkCount() || + ( !mpDrawView->IsTextEdit() && !mpDrawView->GetStyleSheet() ) + ) + rSet.DisableItem( nWhich ); + } + break; + + case SID_REMOVE_HYPERLINK: + { + if (!URLFieldHelper::IsCursorAtURLField(mpDrawView->GetTextEditOutlinerView())) + rSet.DisableItem(nWhich); + } + break; + + case SID_STYLE_WATERCAN: + { + std::unique_ptr pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem && static_cast(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + rSet.Put(SfxBoolItem(nWhich,false)); + else + { + SfxBoolItem aItem(nWhich, SD_MOD()->GetWaterCan()); + aAllSet.Put( aItem, aItem.Which()); + } + } + break; + + case SID_STYLE_NEW: + { + std::unique_ptr pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem && static_cast(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + { + rSet.DisableItem(nWhich); + } + } + break; + + case SID_STYLE_DRAGHIERARCHIE: + { + std::unique_ptr pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem && static_cast(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + rSet.DisableItem(nWhich); + } + break; + + case SID_STYLE_NEW_BY_EXAMPLE: + { + // It is not possible to create PseudoStyleSheets 'by Example'; + // normal style sheets need a selected object for that + + std::unique_ptr pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem) + { + if (static_cast(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + { + rSet.DisableItem(nWhich); + } + else if (static_cast(pFamilyItem->GetValue()) == SfxStyleFamily::Para) + { + if (!mpDrawView->AreObjectsMarked()) + { + rSet.DisableItem(nWhich); + } + } + } + // if there is no (yet) a style designer, we have to go back into the + // view state; an actual set family can not be considered + else + { + if (!mpDrawView->AreObjectsMarked()) + { + rSet.DisableItem(nWhich); + } + } + } + break; + + case SID_STYLE_UPDATE_BY_EXAMPLE: + { + if (!mpDrawView->AreObjectsMarked()) + { + rSet.DisableItem(nWhich); + } + } + break; + case FN_BUL_NUM_RULE_INDEX: + case FN_NUM_NUM_RULE_INDEX: + { + SfxItemSet aEditAttr( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( aEditAttr ); + + SfxItemSetFixed aNewAttr( GetPool() ); + aNewAttr.Put( aEditAttr, false ); + + std::unique_ptr pNumRule; + const SfxPoolItem* pTmpItem=nullptr; + TypedWhichId nNumItemId = SID_ATTR_NUMBERING_RULE; + sal_uInt16 nActNumLvl = mpDrawView->GetSelectionLevel(); + pTmpItem=GetNumBulletItem(aNewAttr, nNumItemId); + + if (pTmpItem) + pNumRule.reset(new SvxNumRule(static_cast(pTmpItem)->GetNumRule())); + + if ( pNumRule ) + { + sal_uInt16 nMask = 1; + sal_uInt16 nCount = 0; + sal_uInt16 nCurLevel = sal_uInt16(0xFFFF); + for(sal_uInt16 i = 0; i < pNumRule->GetLevelCount(); i++) + { + if(nActNumLvl & nMask) + { + nCount++; + nCurLevel = i; + } + nMask <<= 1; + } + if ( nCount == 1 ) + { + const SvxNumberFormat* pNumFmt = pNumRule->Get(nCurLevel); + if ( pNumFmt ) + { + bool bBullets = false; + switch(pNumFmt->GetNumberingType()) + { + case SVX_NUM_CHAR_SPECIAL: + case SVX_NUM_BITMAP: + bBullets = true; + break; + + default: + bBullets = false; + } + + rSet.Put(SfxUInt16Item(FN_BUL_NUM_RULE_INDEX,sal_uInt16(0xFFFF))); + rSet.Put(SfxUInt16Item(FN_NUM_NUM_RULE_INDEX,sal_uInt16(0xFFFF))); + if ( bBullets ) + { + NBOTypeMgrBase* pBullets = NBOutlineTypeMgrFact::CreateInstance(NBOType::Bullets); + if ( pBullets ) + { + sal_uInt16 nBulIndex = pBullets->GetNBOIndexForNumRule(*pNumRule,nActNumLvl); + rSet.Put(SfxUInt16Item(FN_BUL_NUM_RULE_INDEX,nBulIndex)); + } + }else + { + NBOTypeMgrBase* pNumbering = NBOutlineTypeMgrFact::CreateInstance(NBOType::Numbering); + if ( pNumbering ) + { + sal_uInt16 nBulIndex = pNumbering->GetNBOIndexForNumRule(*pNumRule,nActNumLvl); + rSet.Put(SfxUInt16Item(FN_NUM_NUM_RULE_INDEX,nBulIndex)); + } + } + } + } + } + } + break; + case FN_NUM_BULLET_ON: + case FN_NUM_NUMBERING_ON: + { + bool bEnable = false; + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + for (size_t nIndex = 0; nIndex < nMarkCount; ++nIndex) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >(rMarkList.GetMark(nIndex)->GetMarkedSdrObj()); + if (pTextObj && pTextObj->GetObjInventor() == SdrInventor::Default) + { + if (pTextObj->GetObjIdentifier() != SdrObjKind::OLE2) + { + bEnable = true; + break; + } + } + } + if (bEnable) + { + rSet.Put(SfxBoolItem(FN_NUM_BULLET_ON, false)); + rSet.Put(SfxBoolItem(FN_NUM_NUMBERING_ON, false)); + } + else + { + rSet.DisableItem(FN_NUM_BULLET_ON); + rSet.DisableItem(FN_NUM_NUMBERING_ON); + } + } + break; + } + nWhich = aIter.NextWhich(); + } + + std::optional pSet; + + if( bAttr ) + { + pSet.emplace( GetDoc()->GetPool() ); + mpDrawView->GetAttributes( *pSet ); + rSet.Put( *pSet, false ); + } + + rSet.Put( aAllSet, false ); + + // there were changes at area and/or line attributes + if( !(bAttr && pSet) ) + return; + + // if the view owns selected objects, corresponding items have to be + // changed from SfxItemState::DEFAULT (_ON) to SfxItemState::DISABLED + if( mpDrawView->AreObjectsMarked() ) + { + SfxWhichIter aNewIter( *pSet ); + nWhich = aNewIter.FirstWhich(); + while( nWhich ) + { + if (nWhich >= XATTR_LINE_FIRST && nWhich <= XATTR_LINE_LAST + && SfxItemState::DEFAULT == aNewIter.GetItemState() ) + { + rSet.ClearItem( nWhich ); + rSet.DisableItem( nWhich ); + } + nWhich = aNewIter.NextWhich(); + } + } + + SfxItemState eState = pSet->GetItemState( EE_PARA_LRSPACE ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_PARA_LRSPACE); + rSet.InvalidateItem(SID_ATTR_PARA_LRSPACE); + } + eState = pSet->GetItemState( EE_PARA_SBL ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_PARA_SBL); + rSet.InvalidateItem(SID_ATTR_PARA_LINESPACE); + } + eState = pSet->GetItemState( EE_PARA_ULSPACE ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_PARA_ULSPACE); + rSet.InvalidateItem(SID_ATTR_PARA_ULSPACE); + } + + SvxEscapement eEsc = static_cast(pSet->Get( EE_CHAR_ESCAPEMENT ).GetEnumValue()); + rSet.Put(SfxBoolItem(SID_SET_SUPER_SCRIPT, eEsc == SvxEscapement::Superscript)); + rSet.Put(SfxBoolItem(SID_SET_SUB_SCRIPT, eEsc == SvxEscapement::Subscript)); + + eState = pSet->GetItemState( EE_CHAR_KERNING ); + if ( eState == SfxItemState::DONTCARE ) + { + rSet.InvalidateItem(EE_CHAR_KERNING); + rSet.InvalidateItem(SID_ATTR_CHAR_KERNING); + } +} + +OUString DrawViewShell::GetSelectionText(bool bCompleteWords) +{ + OUString aStrSelection; + ::Outliner* pOl = mpDrawView->GetTextEditOutliner(); + OutlinerView* pOlView = mpDrawView->GetTextEditOutlinerView(); + + if (pOl && pOlView) + { + if (bCompleteWords) + { + ESelection aSel = pOlView->GetSelection(); + OUString aStrCurrentDelimiters = pOl->GetWordDelimiters(); + + pOl->SetWordDelimiters(" .,;\"'"); + aStrSelection = pOl->GetWord( aSel.nEndPara, aSel.nEndPos ); + pOl->SetWordDelimiters( aStrCurrentDelimiters ); + } + else + { + aStrSelection = pOlView->GetSelected(); + } + } + + return aStrSelection; +} + +bool DrawViewShell::HasSelection(bool bText) const +{ + bool bReturn = false; + + if (bText) + { + OutlinerView* pOlView = mpDrawView->GetTextEditOutlinerView(); + + if (pOlView && !pOlView->GetSelected().isEmpty()) + { + bReturn = true; + } + } + else if (mpDrawView->GetMarkedObjectList().GetMarkCount() != 0) + { + bReturn = true; + } + + return bReturn; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsg.cxx b/sd/source/ui/view/drviewsg.cxx new file mode 100644 index 000000000..e3930fa7e --- /dev/null +++ b/sd/source/ui/view/drviewsg.cxx @@ -0,0 +1,232 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +namespace sd { + +void DrawViewShell::ExecIMap( SfxRequest const & rReq ) +{ + // during a slide show, nothing is executed! + if(HasCurrentFunction(SID_PRESENTATION) ) + return; + + if ( rReq.GetSlot() != SID_IMAP_EXEC ) + return; + + SdrMark* pMark = mpDrawView->GetMarkedObjectList().GetMark(0); + + if ( !pMark ) + return; + + SdrObject* pSdrObj = pMark->GetMarkedSdrObj(); + SvxIMapDlg* pDlg = ViewShell::Implementation::GetImageMapDialog(); + + if ( pDlg->GetEditingObject() == static_cast(pSdrObj) ) + { + const ImageMap& rImageMap = pDlg->GetImageMap(); + SvxIMapInfo* pIMapInfo = SvxIMapInfo::GetIMapInfo( pSdrObj ); + + if ( !pIMapInfo ) + pSdrObj->AppendUserData( std::unique_ptr(new SvxIMapInfo( rImageMap )) ); + else + pIMapInfo->SetImageMap( rImageMap ); + + GetDoc()->SetChanged(); + } +} + +void DrawViewShell::GetIMapState( SfxItemSet& rSet ) +{ + bool bDisable = true; + + if( GetViewFrame()->HasChildWindow( SvxIMapDlgChildWindow::GetChildWindowId() ) ) + { + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + + if ( rMarkList.GetMarkCount() == 1 ) + { + const SdrObject* pObj = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + + SvxIMapDlg* pImageMapDialog = ViewShell::Implementation::GetImageMapDialog(); + if ( ( dynamic_cast< const SdrGrafObj *>( pObj ) != nullptr /*|| pObj->ISA( SdrOle2Obj )*/ ) + && pImageMapDialog!=nullptr + && ( pImageMapDialog->GetEditingObject() == static_cast(pObj) ) ) + { + bDisable = false; + } + } + } + + rSet.Put( SfxBoolItem( SID_IMAP_EXEC, bDisable ) ); +} + +void DrawViewShell::ExecOptionsBar( SfxRequest& rReq ) +{ + // during a slide show, nothing is executed! + if(HasCurrentFunction(SID_PRESENTATION)) + return; + + bool bDefault = false; + sal_uInt16 nSlot = rReq.GetSlot(); + + SdOptions* pOptions = SD_MOD()->GetSdOptions(GetDoc()->GetDocumentType()); + + switch( nSlot ) + { + case SID_SOLID_CREATE: + pOptions->SetSolidDragging( !mpDrawView->IsSolidDragging() ); + break; + + // Grid- / Help lines option + case SID_GRID_VISIBLE: // not here yet! + { + pOptions->SetGridVisible( !mpDrawView->IsGridVisible() ); + } + break; + + case SID_GRID_USE: + { + pOptions->SetUseGridSnap( !mpDrawView->IsGridSnap() ); + } + break; + + case SID_HELPLINES_VISIBLE: // not here yet! + { + pOptions->SetHelplines( !mpDrawView->IsHlplVisible() ); + } + break; + + case SID_HELPLINES_USE: + { + pOptions->SetSnapHelplines( !mpDrawView->IsHlplSnap() ); + } + break; + + case SID_HELPLINES_MOVE: + { + pOptions->SetDragStripes( !mpDrawView->IsDragStripes() ); + } + break; + + case SID_SNAP_BORDER: + { + pOptions->SetSnapBorder( !mpDrawView->IsBordSnap() ); + } + break; + + case SID_SNAP_FRAME: + { + pOptions->SetSnapFrame( !mpDrawView->IsOFrmSnap() ); + } + break; + + case SID_SNAP_POINTS: + { + pOptions->SetSnapPoints( !mpDrawView->IsOPntSnap() ); + } + break; + + case SID_QUICKEDIT: + { + pOptions->SetQuickEdit( !mpDrawView->IsQuickTextEditMode() ); + } + break; + + case SID_PICK_THROUGH: + { + pOptions->SetPickThrough( + !mpDrawView->GetModel()->IsPickThroughTransparentTextFrames() ); + } + break; + + case SID_DOUBLECLICK_TEXTEDIT: + { + pOptions->SetDoubleClickTextEdit( !mpFrameView->IsDoubleClickTextEdit() ); + } + break; + + case SID_CLICK_CHANGE_ROTATION: + { + pOptions->SetClickChangeRotation( !mpFrameView->IsClickChangeRotation() ); + } + break; + + default: + bDefault = true; + break; + } + + if( bDefault ) + return; + + pOptions->StoreConfig(); + + // Saves the configuration IMMEDIATELY + // SfxGetpApp()->SaveConfiguration(); + WriteFrameViewData(); + + mpFrameView->Update( pOptions ); + ReadFrameViewData( mpFrameView ); + + Invalidate( nSlot ); + rReq.Done(); + +} + +void DrawViewShell::GetOptionsBarState( SfxItemSet& rSet ) +{ + rSet.Put( SfxBoolItem( SID_SOLID_CREATE, mpDrawView->IsSolidDragging() ) ); + rSet.Put( SfxBoolItem( SID_GRID_VISIBLE, mpDrawView->IsGridVisible() ) ); + rSet.Put( SfxBoolItem( SID_GRID_USE, mpDrawView->IsGridSnap() ) ); + rSet.Put( SfxBoolItem( SID_HELPLINES_VISIBLE, mpDrawView->IsHlplVisible() ) ); + rSet.Put( SfxBoolItem( SID_HELPLINES_USE, mpDrawView->IsHlplSnap() ) ); + rSet.Put( SfxBoolItem( SID_HELPLINES_MOVE, mpDrawView->IsDragStripes() ) ); + + rSet.Put( SfxBoolItem( SID_SNAP_BORDER, mpDrawView->IsBordSnap() ) ); + rSet.Put( SfxBoolItem( SID_SNAP_FRAME, mpDrawView->IsOFrmSnap() ) ); + rSet.Put( SfxBoolItem( SID_SNAP_POINTS, mpDrawView->IsOPntSnap() ) ); + + rSet.Put( SfxBoolItem( SID_QUICKEDIT, mpDrawView->IsQuickTextEditMode() ) ); + rSet.Put( SfxBoolItem( SID_PICK_THROUGH, + mpDrawView->GetModel()->IsPickThroughTransparentTextFrames() ) ); + + rSet.Put( SfxBoolItem( SID_DOUBLECLICK_TEXTEDIT, mpFrameView->IsDoubleClickTextEdit() ) ); + rSet.Put( SfxBoolItem( SID_CLICK_CHANGE_ROTATION, mpFrameView->IsClickChangeRotation() ) ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsh.cxx b/sd/source/ui/view/drviewsh.cxx new file mode 100644 index 000000000..c0e09a478 --- /dev/null +++ b/sd/source/ui/view/drviewsh.cxx @@ -0,0 +1,203 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include + +#include + +#include + +namespace sd { + +void DrawViewShell::GotoBookmark(std::u16string_view rBookmark) +{ + ::sd::DrawDocShell* pDocSh = GetDocSh(); + if( pDocSh ) + { + if( !pDocSh->GetViewShell() ) //#i26016# this case occurs if the jump-target-document was opened already with file open dialog before triggering the jump via hyperlink + pDocSh->Connect(this); + pDocSh->GotoBookmark(rBookmark); + } +} + +/** + * Make area visible (scroll part of picture) +|* +\************************************************************************/ + +void DrawViewShell::MakeVisible(const ::tools::Rectangle& rRect, vcl::Window& rWin) +{ + if ( (IsMouseButtonDown() && !IsMouseSelecting()) || SlideShow::IsRunning( GetViewShellBase() ) ) + return; + + // tdf#98646 check if Rectangle which contains the bounds of the region to + // be shown eventually contains values that cause overflows when processing + // e.g. when calling GetWidth() + const bool bOverflowInX(!rtl::math::approxEqual(static_cast(rRect.getWidth()), static_cast(rRect.Right()) - static_cast(rRect.Left()))); + const bool bOverflowInY(!rtl::math::approxEqual(static_cast(rRect.getHeight()), static_cast(rRect.Bottom()) - static_cast(rRect.Top()))); + + if(bOverflowInX || bOverflowInY) + { + SAL_WARN("sd", "The given Rectangle contains values that lead to numerical overflows (!)"); + return; + } + + // In older versions, if in X or Y the size of the object was + // smaller than the visible area, the user-defined zoom was + // changed. This was decided to be a bug for + // StarOffice 6.x (Apr 2002), thus I developed a + // version which instead handles X/Y bigger/smaller and visibility + // questions separately + const Size aLogicSize(rRect.GetSize()); + + // visible area + Size aVisSizePixel(rWin.GetOutputSizePixel()); + bool bTiledRendering = comphelper::LibreOfficeKit::isActive() && !rWin.IsMapModeEnabled(); + if (bTiledRendering) + { + rWin.GetOutDev()->Push(vcl::PushFlags::MAPMODE); + rWin.EnableMapMode(); + } + ::tools::Rectangle aVisArea(rWin.PixelToLogic(::tools::Rectangle(Point(0,0), aVisSizePixel))); + if (bTiledRendering) + rWin.GetOutDev()->Pop(); + Size aVisAreaSize(aVisArea.GetSize()); + + if ( aVisArea.Contains(rRect) ) + return; + + // object is not entirely in visible area + sal_Int32 nFreeSpaceX(aVisAreaSize.Width() - aLogicSize.Width()); + sal_Int32 nFreeSpaceY(aVisAreaSize.Height() - aLogicSize.Height()); + + // allow a mode for move-only visibility without zooming. + const sal_Int32 nPercentBorder(30); + const ::tools::Rectangle aInnerRectangle( + aVisArea.Left() + ((aVisAreaSize.Width() * nPercentBorder) / 200), + aVisArea.Top() + ((aVisAreaSize.Height() * nPercentBorder) / 200), + aVisArea.Right() - ((aVisAreaSize.Width() * nPercentBorder) / 200), + aVisArea.Bottom() - ((aVisAreaSize.Height() * nPercentBorder) / 200) + ); + Point aNewPos(aVisArea.TopLeft()); + + if(nFreeSpaceX < 0) + { + if(aInnerRectangle.Left() > rRect.Right()) + { + // object moves out to the left + aNewPos.AdjustX( -(aVisAreaSize.Width() / 2) ); + } + + if(aInnerRectangle.Right() < rRect.Left()) + { + // object moves out to the right + aNewPos.AdjustX(aVisAreaSize.Width() / 2 ); + } + } + else + { + if(nFreeSpaceX > rRect.GetWidth()) + { + nFreeSpaceX = rRect.GetWidth(); + } + + if(nFreeSpaceX <= 0) + { + SAL_WARN("sd", "The given Rectangle contains values that lead to numerical overflows (!)"); + } + else + { + const ::tools::Long distRight(rRect.Right() - aNewPos.X() - aVisAreaSize.Width()); + + if(distRight > 0) + { + ::tools::Long mult = (distRight / nFreeSpaceX) + 1; + aNewPos.AdjustX(mult * nFreeSpaceX ); + } + + const ::tools::Long distLeft(aNewPos.X() - rRect.Left()); + + if(distLeft > 0) + { + ::tools::Long mult = (distLeft / nFreeSpaceX) + 1; + aNewPos.AdjustX( -(mult * nFreeSpaceX) ); + } + } + } + + if(nFreeSpaceY < 0) + { + if(aInnerRectangle.Top() > rRect.Bottom()) + { + // object moves out to the top + aNewPos.AdjustY( -(aVisAreaSize.Height() / 2) ); + } + + if(aInnerRectangle.Bottom() < rRect.Top()) + { + // object moves out to the right + aNewPos.AdjustY(aVisAreaSize.Height() / 2 ); + } + } + else + { + if(nFreeSpaceY > rRect.GetHeight()) + { + nFreeSpaceY = rRect.GetHeight(); + } + + if(nFreeSpaceY <= 0) + { + SAL_WARN("sd", "The given Rectangle contains values that lead to numerical overflows (!)"); + } + else + { + const ::tools::Long distBottom(rRect.Bottom() - aNewPos.Y() - aVisAreaSize.Height()); + + if(distBottom > 0) + { + ::tools::Long mult = (distBottom / nFreeSpaceY) + 1; + aNewPos.AdjustY(mult * nFreeSpaceY ); + } + + const ::tools::Long distTop(aNewPos.Y() - rRect.Top()); + + if(distTop > 0) + { + ::tools::Long mult = (distTop / nFreeSpaceY) + 1; + aNewPos.AdjustY( -(mult * nFreeSpaceY) ); + } + } + } + + // did position change? Does it need to be set? + if(aNewPos != aVisArea.TopLeft()) + { + aVisArea.SetPos(aNewPos); + SetZoomRect(aVisArea); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsi.cxx b/sd/source/ui/view/drviewsi.cxx new file mode 100644 index 000000000..039840824 --- /dev/null +++ b/sd/source/ui/view/drviewsi.cxx @@ -0,0 +1,165 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace ::com::sun::star; + +namespace sd { + +/** + * Handle SfxRequests for EffekteWindow + */ +void DrawViewShell::ExecEffectWin( SfxRequest& rReq ) +{ + CheckLineTo (rReq); + + sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_3D_INIT: + { + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxChildWindow* pWindow = GetViewFrame()->GetChildWindow( nId ); + if( pWindow ) + { + Svx3DWin* p3DWin = static_cast( pWindow->GetWindow() ); + if( p3DWin ) + p3DWin->InitColorLB(); + } + } + break; + + case SID_3D_STATE: + { + Update3DWindow(); + } + break; + + case SID_3D_ASSIGN: + { + AssignFrom3DWindow(); + } + break; + + } +} + +void DrawViewShell::Update3DWindow() +{ + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxChildWindow* pWindow = GetViewFrame()->GetChildWindow( nId ); + if( pWindow ) + { + Svx3DWin* p3DWin = static_cast( pWindow->GetWindow() ); + if( p3DWin && p3DWin->IsUpdateMode() ) + { + SfxItemSet aTmpItemSet = GetView()->Get3DAttributes(); + p3DWin->Update( aTmpItemSet ); + } + } +} + +/*----------------------------------------------------------------------------*/ + +void DrawViewShell::AssignFrom3DWindow() +{ + sal_uInt16 nId = Svx3DChildWindow::GetChildWindowId(); + SfxChildWindow* pWin = GetViewFrame()->GetChildWindow( nId ); + if( !pWin ) + return; + + Svx3DWin* p3DWin = static_cast( pWin->GetWindow() ); + if( !(p3DWin && GetView()) ) + return; + + if(!GetView()->IsPresObjSelected()) + { + SfxItemSetFixed aSet( GetDoc()->GetPool() ); + p3DWin->GetAttr( aSet ); + + // own UNDO-compounding also around transformation in 3D + GetView()->BegUndo(SdResId(STR_UNDO_APPLY_3D_FAVOURITE)); + + if(GetView()->IsConvertTo3DObjPossible()) + { + // assign only text-attribute + SfxItemSetFixed aTextSet( GetDoc()->GetPool() ); + aTextSet.Put( aSet, false ); + GetView()->SetAttributes( aTextSet ); + + // transform text into 3D + sal_uInt16 nSId = SID_CONVERT_TO_3D; + SfxBoolItem aItem( nSId, true ); + GetViewFrame()->GetDispatcher()->ExecuteList( + nSId, SfxCallMode::SYNCHRON | SfxCallMode::RECORD, + { &aItem }); + + // Determine if a FILL attribute is set. + // If not, hard set a fill attribute + drawing::FillStyle eFillStyle = aSet.Get(XATTR_FILLSTYLE).GetValue(); + if(eFillStyle == drawing::FillStyle_NONE) + aSet.Put(XFillStyleItem (drawing::FillStyle_SOLID)); + + // remove some 3DSCENE attributes since these were + // created by convert to 3D and may not be changed + // to the defaults again. + aSet.ClearItem(SDRATTR_3DSCENE_DISTANCE); + aSet.ClearItem(SDRATTR_3DSCENE_FOCAL_LENGTH); + aSet.ClearItem(SDRATTR_3DOBJ_DEPTH); + } + + // assign attribute + GetView()->Set3DAttributes( aSet ); + + // end UNDO + GetView()->EndUndo(); + } + else + { + vcl::Window* pWindow = GetActiveWindow(); + std::unique_ptr xInfoBox(Application::CreateMessageDialog(pWindow ? pWindow->GetFrameWeld() : nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); + } + + // get focus back + GetActiveWindow()->GrabFocus(); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsj.cxx b/sd/source/ui/view/drviewsj.cxx new file mode 100644 index 000000000..a1a7d899f --- /dev/null +++ b/sd/source/ui/view/drviewsj.cxx @@ -0,0 +1,567 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +using namespace com::sun::star; + +namespace sd { + +/** + * Set state (Enabled/Disabled) of Menu-SfxSlots + */ +void DrawViewShell::GetMenuStateSel( SfxItemSet &rSet ) +{ + // Status of menu entries (Buttons,...) + + // Single selection + const SdrMarkList& rMarkList = mpDrawView->GetMarkedObjectList(); + const size_t nMarkCount = rMarkList.GetMarkCount(); + + if ( nMarkCount == 1 ) + { + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_BEZIER_EDIT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_UNGROUP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ENTER_GROUP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_NAME_GROUP ) || + + // #i68101# + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_TITLE_DESCRIPTION ) || + + SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_FILL_STYLE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_FILL_USE_SLIDE_BACKGROUND ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_FILL_TRANSPARENCE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_FILL_FLOATTRANSPARENCE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CHANGEBEZIER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CHANGEPOLYGON ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_LINEEND_POLYGON ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_MEASURE_DLG ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CONNECTION_DLG ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_CONNECTION_NEW_ROUTING ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_SHEAR ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_LEFT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_CENTER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_RIGHT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_UP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_MIDDLE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_OBJECT_ALIGN_DOWN ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_FRAME_TO_TOP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_MOREFRONT ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_FRAME_UP ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_MOREBACK ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_FRAME_DOWN ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_FRAME_TO_BOTTOM ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_BEFORE_OBJ ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_BEHIND_OBJ ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_REVERSE_ORDER ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ORIGINAL_SIZE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_SAVE_GRAPHIC ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_COMPRESS_GRAPHIC ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_TEXTATTR_DLG ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_EXECUTE_ANIMATION_EFFECT )) + { + const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pObj); + const SdrOle2Obj* pSdrOle2Obj = dynamic_cast< const SdrOle2Obj* >(pObj); + const SdAnimationInfo* pAnimationInfo + = SdDrawDocument::GetAnimationInfo(rMarkList.GetMark(0)->GetMarkedSdrObj()); + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + SdrObjTransformInfoRec aInfoRec; + pObj->TakeObjInfo( aInfoRec ); + + // don't show original size entry if not possible + if(pSdrOle2Obj) + { + if (pSdrOle2Obj->GetObjRef().is() && + (pSdrOle2Obj->GetObjRef()->getStatus( pSdrOle2Obj->GetAspect() ) & embed::EmbedMisc::MS_EMBED_RECOMPOSEONRESIZE) ) + rSet.DisableItem(SID_ORIGINAL_SIZE); + } + + if(!pSdrGrafObj) + { + rSet.DisableItem(SID_SAVE_GRAPHIC); + rSet.DisableItem(SID_COMPRESS_GRAPHIC); + } + + if (!pAnimationInfo + || pAnimationInfo->meClickAction == presentation::ClickAction::ClickAction_NONE + // Sound does not work in edit mode + || pAnimationInfo->meClickAction == presentation::ClickAction::ClickAction_SOUND + // No point in exiting the presentation in edit mode + || pAnimationInfo->meClickAction + == presentation::ClickAction::ClickAction_STOPPRESENTATION) + { + rSet.DisableItem(SID_EXECUTE_ANIMATION_EFFECT); + } + + /* If it is not a group object or 3D object, we disable "enter + group". */ + const auto* pSdrObjGroup = dynamic_cast(pObj); + + if( !( ( pSdrObjGroup != nullptr && nInv == SdrInventor::Default ) || + ( dynamic_cast< const E3dScene* >(pObj) != nullptr ) ) ) + { + rSet.DisableItem( SID_ENTER_GROUP ); + } + + // Don't allow enter Diagrams + if(nullptr != pSdrObjGroup && pSdrObjGroup->isDiagram()) + { + rSet.DisableItem( SID_ENTER_GROUP ); + } + + // If it is not a group object, we disable "ungroup" + if(pSdrObjGroup == nullptr || nInv != SdrInventor::Default) + { + rSet.DisableItem(SID_UNGROUP); + } + + // Support advanced DiagramHelper + if(!pSdrObjGroup || !pSdrObjGroup->isDiagram()) + { + rSet.DisableItem( SID_REGENERATE_DIAGRAM ); + rSet.DisableItem( SID_EDIT_DIAGRAM ); + } + + if( nInv == SdrInventor::Default && + (nId == SdrObjKind::Line || + nId == SdrObjKind::PolyLine || + nId == SdrObjKind::PathLine || + nId == SdrObjKind::FreehandLine )) + { + //rSet.DisableItem( SID_ATTRIBUTES_AREA ); // remove again! + rSet.DisableItem( SID_ATTR_FILL_STYLE ); + rSet.DisableItem( SID_ATTR_FILL_USE_SLIDE_BACKGROUND ); + rSet.DisableItem( SID_ATTR_FILL_TRANSPARENCE ); + rSet.DisableItem( SID_ATTR_FILL_FLOATTRANSPARENCE ); + } + if( (dynamic_cast< const SdrPathObj *>( pObj ) == nullptr&& !aInfoRec.bCanConvToPath) || dynamic_cast< const SdrObjGroup *>( pObj ) != nullptr ) // As long as JOE handles it incorrectly! + { // JOE: a group object may can be converted into a PathObj + rSet.DisableItem( SID_LINEEND_POLYGON ); + } + if(nInv == SdrInventor::Default && + (nId == SdrObjKind::PathFill || nId == SdrObjKind::PathLine || !aInfoRec.bCanConvToPath)) + rSet.DisableItem( SID_CHANGEBEZIER ); + + if( nInv == SdrInventor::Default && + ( nId == SdrObjKind::Polygon || nId == SdrObjKind::PolyLine || !aInfoRec.bCanConvToPoly ) && + !GetView()->IsVectorizeAllowed() ) + { + rSet.DisableItem( SID_CHANGEPOLYGON ); + } + + if(nInv == SdrInventor::Default && nId == SdrObjKind::Table ) + { + rSet.DisableItem( SID_TEXTATTR_DLG ); + } + + if( nInv != SdrInventor::Default || nId != SdrObjKind::Measure ) + rSet.DisableItem( SID_MEASURE_DLG ); + + if( nInv != SdrInventor::Default || nId != SdrObjKind::Edge ) + rSet.DisableItem( SID_CONNECTION_DLG ); + else + { + bool bDisable = true; + SfxItemSet aAttrSet( GetDoc()->GetPool() ); + GetView()->GetAttributes( aAttrSet ); + + if( aAttrSet.GetItemState( SDRATTR_EDGELINE1DELTA ) >= SfxItemState::DEFAULT && + aAttrSet.GetItemState( SDRATTR_EDGELINE2DELTA ) >= SfxItemState::DEFAULT && + aAttrSet.GetItemState( SDRATTR_EDGELINE3DELTA ) >= SfxItemState::DEFAULT ) + { + ::tools::Long nVal1 = aAttrSet.Get( SDRATTR_EDGELINE1DELTA ).GetValue(); + ::tools::Long nVal2 = aAttrSet.Get( SDRATTR_EDGELINE2DELTA ).GetValue(); + ::tools::Long nVal3 = aAttrSet.Get( SDRATTR_EDGELINE3DELTA ).GetValue(); + { + if( nVal1 != 0 || nVal2 != 0 || nVal3 != 0 ) + bDisable = false; + } + } + if( bDisable ) + rSet.DisableItem( SID_CONNECTION_NEW_ROUTING ); + } + + if ( nInv == SdrInventor::E3d || + (!mpDrawView->IsConvertToPathObjPossible() && + !mpDrawView->IsShearAllowed() && + !mpDrawView->IsDistortAllowed()) ) + { + rSet.DisableItem( SID_OBJECT_SHEAR ); + } + + if(dynamic_cast< const E3dCompoundObject *>( pObj ) != nullptr) + { + rSet.DisableItem( SID_OBJECT_ALIGN ); + rSet.DisableItem( SID_OBJECT_ALIGN_LEFT ); + rSet.DisableItem( SID_OBJECT_ALIGN_CENTER ); + rSet.DisableItem( SID_OBJECT_ALIGN_RIGHT ); + rSet.DisableItem( SID_OBJECT_ALIGN_UP ); + rSet.DisableItem( SID_OBJECT_ALIGN_MIDDLE ); + rSet.DisableItem( SID_OBJECT_ALIGN_DOWN ); + rSet.DisableItem( SID_FRAME_TO_TOP ); + rSet.DisableItem( SID_MOREFRONT ); + rSet.DisableItem( SID_FRAME_UP ); + rSet.DisableItem( SID_MOREBACK ); + rSet.DisableItem( SID_FRAME_DOWN ); + rSet.DisableItem( SID_FRAME_TO_BOTTOM ); + rSet.DisableItem( SID_BEFORE_OBJ ); + rSet.DisableItem( SID_BEHIND_OBJ ); + rSet.DisableItem( SID_REVERSE_ORDER ); + rSet.DisableItem( SID_POSITION ); + } + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_DISMANTLE ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_BREAK ) ) + { + if ( !mpDrawView->IsDismantlePossible() ) + { + rSet.DisableItem( SID_DISMANTLE ); + } + + if ( !mpDrawView->IsDismantlePossible(true) && + !mpDrawView->IsImportMtfPossible() && + !mpDrawView->IsBreak3DObjPossible() ) + { + rSet.DisableItem( SID_BREAK ); + } + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_MODIFY_FIELD ) ) + { + OutlinerView* pOLV = mpDrawView->GetTextEditOutlinerView(); + + if( pOLV ) + { + const SvxFieldItem* pFldItem = pOLV->GetFieldAtSelection(); + + if( !( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) ) ) ) + { + rSet.DisableItem( SID_MODIFY_FIELD ); + } + } + else + rSet.DisableItem( SID_MODIFY_FIELD ); + } + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_OUTLINE_TEXT_AUTOFIT ) ) + { + const SdrObject* pObj = rMarkList.GetMark(0)->GetMarkedSdrObj(); + const SdrTextFitToSizeTypeItem* pItem = pObj->GetMergedItemSet().GetItem(SDRATTR_TEXT_FITTOSIZE); + const bool bSet = pItem && pItem->GetValue() != drawing::TextFitToSizeType_NONE; + rSet.Put(SfxBoolItem(SID_OUTLINE_TEXT_AUTOFIT, bSet)); + } + + rSet.DisableItem(SID_GROUP); + rSet.DisableItem(SID_TEXT_COMBINE); + rSet.DisableItem(SID_COMBINE); + rSet.DisableItem(SID_DISTRIBUTE_HLEFT); + rSet.DisableItem(SID_DISTRIBUTE_HCENTER); + rSet.DisableItem(SID_DISTRIBUTE_HDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_HRIGHT); + rSet.DisableItem(SID_DISTRIBUTE_VTOP); + rSet.DisableItem(SID_DISTRIBUTE_VCENTER); + rSet.DisableItem(SID_DISTRIBUTE_VDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_VBOTTOM); + rSet.DisableItem(SID_POLY_MERGE); + rSet.DisableItem(SID_POLY_SUBSTRACT); + rSet.DisableItem(SID_POLY_INTERSECT); + rSet.DisableItem(SID_EQUALIZEWIDTH); + rSet.DisableItem(SID_EQUALIZEHEIGHT); + rSet.DisableItem(SID_CONNECT); + } + // multi-selection + else if( nMarkCount > 1 ) + { + // distribute dialog for 3+n objects + if(nMarkCount <= 2) + { + rSet.DisableItem(SID_DISTRIBUTE_HLEFT); + rSet.DisableItem(SID_DISTRIBUTE_HCENTER); + rSet.DisableItem(SID_DISTRIBUTE_HDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_HRIGHT); + rSet.DisableItem(SID_DISTRIBUTE_VTOP); + rSet.DisableItem(SID_DISTRIBUTE_VCENTER); + rSet.DisableItem(SID_DISTRIBUTE_VDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_VBOTTOM); + } + + rSet.DisableItem( SID_LINEEND_POLYGON ); + rSet.DisableItem( SID_ENTER_GROUP ); + // Now names for objects have to be unique + rSet.DisableItem( SID_NAME_GROUP ); + // #i68101# + rSet.DisableItem( SID_OBJECT_TITLE_DESCRIPTION ); + rSet.DisableItem( SID_MODIFY_FIELD ); + + { + bool bText = false; + bool bLine = false; + bool bGroup = false; + bool bDrawObj = false; + bool b3dObj = false; + bool bTable = false; + bool bMeasureObj = false; + bool bEdgeObj = false; // Connector + bool bE3dCompoundObject = false; + + for( size_t i = 0; i < nMarkCount && !bText && i < 50; ++i ) + { + SdrObject* pObj = rMarkList.GetMark(i)->GetMarkedSdrObj(); + SdrInventor nInv = pObj->GetObjInventor(); + SdrObjKind nId = pObj->GetObjIdentifier(); + + if (nInv == SdrInventor::Default) + { + switch (nId) + { + case SdrObjKind::Text: bText = true; break; + + case SdrObjKind::Line: bLine = true; break; + + case SdrObjKind::Edge: bEdgeObj = true; break; + + case SdrObjKind::Measure: bMeasureObj = true; break; + + case SdrObjKind::Rectangle: + case SdrObjKind::CircleOrEllipse: + case SdrObjKind::FreehandLine: + case SdrObjKind::FreehandFill: + case SdrObjKind::PathFill: + case SdrObjKind::PathLine: + case SdrObjKind::CircleSection: + case SdrObjKind::CircleArc: + case SdrObjKind::CircleCut: bDrawObj = true; break; + + case SdrObjKind::Group: bGroup = true; break; + + case SdrObjKind::Graphic: break; + + case SdrObjKind::Table: bTable = true; break; + default: ; + } + } + else if (nInv == SdrInventor::E3d) + { + if(dynamic_cast< const E3dScene *>( pObj ) != nullptr) + b3dObj = true; + else if(dynamic_cast< const E3dCompoundObject* >(pObj) != nullptr) + bE3dCompoundObject = true; + } + } + if( bLine && !bText && !bDrawObj &&!b3dObj) + { + rSet.DisableItem( SID_ATTR_FILL_STYLE ); + rSet.DisableItem( SID_ATTR_FILL_USE_SLIDE_BACKGROUND ); + rSet.DisableItem( SID_ATTR_FILL_TRANSPARENCE ); + rSet.DisableItem( SID_ATTR_FILL_FLOATTRANSPARENCE ); + } + if( !bEdgeObj ) + rSet.DisableItem( SID_CONNECTION_DLG ); + + if (b3dObj) + { + rSet.DisableItem( SID_COMBINE ); + rSet.DisableItem(SID_POLY_MERGE); + rSet.DisableItem(SID_POLY_SUBSTRACT); + rSet.DisableItem(SID_POLY_INTERSECT); + rSet.DisableItem(SID_EQUALIZEWIDTH); + rSet.DisableItem(SID_EQUALIZEHEIGHT); + } + + if (b3dObj || + (!mpDrawView->IsConvertToPathObjPossible() && + !mpDrawView->IsShearAllowed() && + !mpDrawView->IsDistortAllowed()) ) + { + rSet.DisableItem( SID_OBJECT_SHEAR ); + } + + if( !bGroup ) + { + rSet.DisableItem( SID_UNGROUP ); + } + if( bTable ) + rSet.DisableItem( SID_TEXTATTR_DLG ); + + if( !bMeasureObj ) + rSet.DisableItem( SID_MEASURE_DLG ); + + if(bE3dCompoundObject) + { + rSet.DisableItem( SID_OBJECT_ALIGN ); + rSet.DisableItem( SID_OBJECT_ALIGN_LEFT ); + rSet.DisableItem( SID_OBJECT_ALIGN_CENTER ); + rSet.DisableItem( SID_OBJECT_ALIGN_RIGHT ); + rSet.DisableItem( SID_OBJECT_ALIGN_UP ); + rSet.DisableItem( SID_OBJECT_ALIGN_MIDDLE ); + rSet.DisableItem( SID_OBJECT_ALIGN_DOWN ); + rSet.DisableItem( SID_FRAME_TO_TOP ); + rSet.DisableItem( SID_MOREFRONT ); + rSet.DisableItem( SID_FRAME_UP ); + rSet.DisableItem( SID_MOREBACK ); + rSet.DisableItem( SID_FRAME_DOWN ); + rSet.DisableItem( SID_FRAME_TO_BOTTOM ); + rSet.DisableItem( SID_BEFORE_OBJ ); + rSet.DisableItem( SID_BEHIND_OBJ ); + rSet.DisableItem( SID_REVERSE_ORDER ); + rSet.DisableItem( SID_POSITION ); + } + } + + if ( !mpDrawView->IsDismantlePossible() ) + { + rSet.DisableItem( SID_DISMANTLE ); + } + if ( !mpDrawView->IsDismantlePossible(true) && + !mpDrawView->IsImportMtfPossible() && + !mpDrawView->IsBreak3DObjPossible() ) + { + rSet.DisableItem( SID_BREAK ); + } + if ( !mpDrawView->IsCombinePossible() ) + { + rSet.DisableItem(SID_COMBINE); + rSet.DisableItem(SID_POLY_MERGE); + rSet.DisableItem(SID_POLY_SUBSTRACT); + rSet.DisableItem(SID_POLY_INTERSECT); + rSet.DisableItem(SID_EQUALIZEWIDTH); + rSet.DisableItem(SID_EQUALIZEHEIGHT); + } + if ( !mpDrawView->IsCombinePossible(true) ) + { + rSet.DisableItem( SID_CONNECT ); + } + if ( !mpDrawView->IsGroupPossible() ) + { + rSet.DisableItem( SID_GROUP ); + } + if ( !mpDrawView->IsUnGroupPossible() ) + { + rSet.DisableItem( SID_UNGROUP ); + } + } + // select no object + else + { + rSet.DisableItem( SID_ENTER_GROUP ); + rSet.DisableItem( SID_CUT ); + rSet.DisableItem( SID_COPY ); + rSet.DisableItem( SID_DELETE ); + rSet.DisableItem( SID_ATTR_TRANSFORM ); + + rSet.DisableItem( SID_OBJECT_ALIGN ); + rSet.DisableItem( SID_OBJECT_ALIGN_LEFT ); + rSet.DisableItem( SID_OBJECT_ALIGN_CENTER ); + rSet.DisableItem( SID_OBJECT_ALIGN_RIGHT ); + rSet.DisableItem( SID_OBJECT_ALIGN_UP ); + rSet.DisableItem( SID_OBJECT_ALIGN_MIDDLE ); + rSet.DisableItem( SID_OBJECT_ALIGN_DOWN ); + + rSet.DisableItem( SID_FRAME_TO_TOP ); + rSet.DisableItem( SID_MOREFRONT ); + rSet.DisableItem( SID_FRAME_UP ); + rSet.DisableItem( SID_MOREBACK ); + rSet.DisableItem( SID_FRAME_DOWN ); + rSet.DisableItem( SID_FRAME_TO_BOTTOM ); + rSet.DisableItem( SID_BEFORE_OBJ ); + rSet.DisableItem( SID_BEHIND_OBJ ); + rSet.DisableItem( SID_POSITION ); + + rSet.DisableItem( SID_SIZE_OPTIMAL ); + rSet.DisableItem( SID_LINEEND_POLYGON ); + rSet.DisableItem( SID_COPYOBJECTS ); + rSet.DisableItem( SID_HORIZONTAL ); + rSet.DisableItem( SID_VERTICAL ); + rSet.DisableItem( SID_FLIP_HORIZONTAL ); + rSet.DisableItem( SID_FLIP_VERTICAL ); + rSet.DisableItem( SID_GROUP ); + rSet.DisableItem( SID_UNGROUP ); + rSet.DisableItem( SID_NAME_GROUP ); + + // #i68101# + rSet.DisableItem( SID_OBJECT_TITLE_DESCRIPTION ); + + rSet.DisableItem( SID_DISMANTLE ); + rSet.DisableItem( SID_BREAK ); + rSet.DisableItem( SID_TEXT_COMBINE ); + rSet.DisableItem( SID_COMBINE ); + rSet.DisableItem(SID_DISTRIBUTE_DLG); + rSet.DisableItem(SID_DISTRIBUTE_HLEFT); + rSet.DisableItem(SID_DISTRIBUTE_HCENTER); + rSet.DisableItem(SID_DISTRIBUTE_HDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_HRIGHT); + rSet.DisableItem(SID_DISTRIBUTE_VTOP); + rSet.DisableItem(SID_DISTRIBUTE_VCENTER); + rSet.DisableItem(SID_DISTRIBUTE_VDISTANCE); + rSet.DisableItem(SID_DISTRIBUTE_VBOTTOM); + rSet.DisableItem(SID_POLY_MERGE); + rSet.DisableItem(SID_POLY_SUBSTRACT); + rSet.DisableItem(SID_POLY_INTERSECT); + rSet.DisableItem(SID_EQUALIZEWIDTH); + rSet.DisableItem(SID_EQUALIZEHEIGHT); + rSet.DisableItem( SID_CONNECT ); + rSet.DisableItem( SID_ANIMATION_EFFECTS ); + rSet.DisableItem( SID_EXECUTE_ANIMATION_EFFECT ); + rSet.DisableItem( SID_MODIFY_FIELD ); + rSet.DisableItem (SID_OBJECT_SHEAR); + } + + if (GetObjectShell()->isContentExtractionLocked()) + { + rSet.DisableItem(SID_COPY); + rSet.DisableItem(SID_CUT); + } + if(GetObjectShell()->isExportLocked()) + { + rSet.DisableItem(SID_SAVE_GRAPHIC); + rSet.DisableItem(SID_EXTERNAL_EDIT); + } + if (GetDoc()->getImagePreferredDPI() <= 0) + { + rSet.DisableItem(SID_GRAPHIC_SIZE_CHECK); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/drviewsk.cxx b/sd/source/ui/view/drviewsk.cxx new file mode 100644 index 000000000..9daeecc02 --- /dev/null +++ b/sd/source/ui/view/drviewsk.cxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include + +#include + +namespace sd { + +void DrawViewShell::ConfigurationChanged( utl::ConfigurationBroadcaster* pCb, ConfigurationHints ) +{ + ConfigureAppBackgroundColor( dynamic_cast(pCb) ); +} + +void DrawViewShell::ConfigureAppBackgroundColor( svtools::ColorConfig *pColorConfig ) +{ + if (!pColorConfig) + pColorConfig = &SD_MOD()->GetColorConfig(); + Color aFillColor( pColorConfig->GetColorValue( svtools::APPBACKGROUND ).nColor ); + if (comphelper::LibreOfficeKit::isActive()) + aFillColor = COL_TRANSPARENT; + // tdf#87905 Use darker background color for master view + if (meEditMode == EditMode::MasterPage) + aFillColor.DecreaseLuminance( 64 ); + mnAppBackgroundColor = aFillColor; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */ diff --git a/sd/source/ui/view/drvwshrg.cxx b/sd/source/ui/view/drvwshrg.cxx new file mode 100644 index 000000000..792d5b833 --- /dev/null +++ b/sd/source/ui/view/drvwshrg.cxx @@ -0,0 +1,110 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include + +using namespace sd; +#define ShellClass_DrawViewShell +#include +#define ShellClass_GraphicViewShell +#include + +namespace sd +{ +/** + * Declare SFX-Slotmap and Standardinterface + */ + +SFX_IMPL_INTERFACE(DrawViewShell, SfxShell) + +void DrawViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("drawtext"); + + GetStaticInterface()->RegisterChildWindow(SID_NAVIGATOR, true); + + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxFontWorkChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxColorChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(AnimationChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(Svx3DChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxIMapDlgChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxHlinkDlgWrapper::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(::sd::SpellDialogChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); +#if HAVE_FEATURE_AVMEDIA + GetStaticInterface()->RegisterChildWindow(::avmedia::MediaPlayer::GetChildWindowId()); +#endif + GetStaticInterface()->RegisterChildWindow( + sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId()); +} + +// SdGraphicViewShell +SFX_IMPL_INTERFACE(GraphicViewShell, SfxShell) + +void GraphicViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("drawtext"); + + GetStaticInterface()->RegisterChildWindow(SID_NAVIGATOR, true); + + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxFontWorkChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxColorChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(Svx3DChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxBmpMaskChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxIMapDlgChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxHlinkDlgWrapper::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(::sd::SpellDialogChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); +#if HAVE_FEATURE_AVMEDIA + GetStaticInterface()->RegisterChildWindow(::avmedia::MediaPlayer::GetChildWindowId()); +#endif + GetStaticInterface()->RegisterChildWindow( + sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId()); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/frmview.cxx b/sd/source/ui/view/frmview.cxx new file mode 100644 index 000000000..fad0dc9ad --- /dev/null +++ b/sd/source/ui/view/frmview.cxx @@ -0,0 +1,916 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::std; + +namespace sd { + +FrameView::FrameView(SdDrawDocument* pDrawDoc, FrameView* pFrameView /* = NULL */) +: SdrView(*pDrawDoc, nullptr), // TTTT SdDrawDocument* -> should be reference + mnRefCount(0), + mnPresViewShellId(SID_VIEWSHELL0), + mbIsNavigatorShowingAllShapes(false) +{ + EndListening(*pDrawDoc); + + EnableExtendedKeyInputDispatcher(false); + EnableExtendedMouseEventDispatcher(false); + + SetGridFront( false ); + SetHlplFront( false ); + SetOConSnap( false ); + SetFrameDragSingles(); + SetSlidesPerRow(4); + + if( nullptr == pFrameView ) + { + DrawDocShell* pDocShell = pDrawDoc->GetDocSh(); + + if ( pDocShell ) + { + // document is loaded, is there a FrameView? + sal_uLong nSdViewShellCount = 0; + SfxViewFrame* pSfxViewFrame = SfxViewFrame::GetFirst(pDocShell); + + while (pSfxViewFrame) + { + // Count the FrameViews and remember the type of the main + // view shell. + SfxViewShell* pSfxViewSh = pSfxViewFrame->GetViewShell(); + ViewShellBase* pBase = dynamic_cast( pSfxViewSh ); + + if (pBase != nullptr) + { + nSdViewShellCount++; + + OUString sViewURL; + Reference xView ( + framework::FrameworkHelper::Instance(*pBase)->GetView( + drawing::framework::ResourceId::create( + ::comphelper::getProcessComponentContext(), + framework::FrameworkHelper::msCenterPaneURL))); + if (xView.is()) + sViewURL = xView->getResourceId()->getResourceURL(); + + switch (framework::FrameworkHelper::GetViewId(sViewURL)) + { + default: +// case ViewShell::ST_IMPRESS: +// case ViewShell::ST_NOTES: +// case ViewShell::ST_HANDOUT: + mnPresViewShellId = SID_VIEWSHELL0; + break; + + case ViewShell::ST_SLIDE_SORTER: + mnPresViewShellId = SID_VIEWSHELL1; + break; + + case ViewShell::ST_OUTLINE: + mnPresViewShellId = SID_VIEWSHELL2; + break; + } + } + + pSfxViewFrame = SfxViewFrame::GetNext(*pSfxViewFrame, pDocShell); + } + + SdDrawDocument* pDoc = pDocShell->GetDoc(); + pFrameView = pDoc->GetFrameView(nSdViewShellCount); + } + } + + if (pFrameView) + { + // initialize FrameView with the FrameView of the DocShell + SetRuler( pFrameView->HasRuler() ); + SetGridCoarse( pFrameView->GetGridCoarse() ); + SetGridFine( pFrameView->GetGridFine() ); + SetSnapGridWidth(pFrameView->GetSnapGridWidthX(), pFrameView->GetSnapGridWidthY()); + SetGridVisible( pFrameView->IsGridVisible() ); + SetGridFront( pFrameView->IsGridFront() ); + SetSnapAngle( pFrameView->GetSnapAngle() ); + SetGridSnap( pFrameView->IsGridSnap() ); + SetBordSnap( pFrameView->IsBordSnap() ); + SetHlplSnap( pFrameView->IsHlplSnap() ); + SetOFrmSnap( pFrameView->IsOFrmSnap() ); + SetOPntSnap( pFrameView->IsOPntSnap() ); + SetOConSnap( pFrameView->IsOConSnap() ); + SetHlplVisible( pFrameView->IsHlplVisible() ); + SetDragStripes( pFrameView->IsDragStripes() ); + SetPlusHandlesAlwaysVisible( pFrameView->IsPlusHandlesAlwaysVisible() ); + SetFrameDragSingles( pFrameView->IsFrameDragSingles() ); + SetSnapMagneticPixel( pFrameView->GetSnapMagneticPixel() ); + SetMarkedHitMovesAlways( pFrameView->IsMarkedHitMovesAlways() ); + SetMoveOnlyDragging( pFrameView->IsMoveOnlyDragging() ); + SetCrookNoContortion( pFrameView->IsCrookNoContortion() ); + SetSlantButShear( pFrameView->IsSlantButShear() ); + SetNoDragXorPolys( pFrameView->IsNoDragXorPolys() ); + SetAngleSnapEnabled( pFrameView->IsAngleSnapEnabled() ); + SetBigOrtho( pFrameView->IsBigOrtho() ); + SetOrtho( pFrameView->IsOrtho() ); + SetEliminatePolyPointLimitAngle( pFrameView->GetEliminatePolyPointLimitAngle() ); + SetEliminatePolyPoints( pFrameView->IsEliminatePolyPoints() ); + SetDesignMode( pFrameView->IsDesignMode() ); + + SetSolidDragging( pFrameView->IsSolidDragging() ); + + maVisibleLayers = pFrameView->GetVisibleLayers(); + maPrintableLayers = pFrameView->GetPrintableLayers(); + maLockedLayers = pFrameView->GetLockedLayers(); + maStandardHelpLines = pFrameView->GetStandardHelpLines(); + maNotesHelpLines = pFrameView->GetNotesHelpLines(); + maHandoutHelpLines = pFrameView->GetHandoutHelpLines(); + SetActiveLayer( pFrameView->GetActiveLayer() ); + mbNoColors = pFrameView->IsNoColors(); + mbNoAttribs = pFrameView->IsNoAttribs() ; + maVisArea = pFrameView->GetVisArea(); + mePageKind = pFrameView->GetPageKind(); + mePageKindOnLoad = pFrameView->GetPageKindOnLoad(); + mnSelectedPage = pFrameView->GetSelectedPage(); + mnSelectedPageOnLoad = pFrameView->GetSelectedPageOnLoad(); + mePageEditMode = pFrameView->GetViewShEditMode(); + // meStandardEditMode = pFrameView->GetViewShEditMode(PageKind::Standard); + // meNotesEditMode = pFrameView->GetViewShEditMode(PageKind::Notes); + // meHandoutEditMode = pFrameView->GetViewShEditMode(PageKind::Handout); + SetViewShEditModeOnLoad(pFrameView->GetViewShEditModeOnLoad()); + mbLayerMode = pFrameView->IsLayerMode(); + mbQuickEdit = pFrameView->IsQuickEdit(); + + // #i26631# + SetMasterPagePaintCaching( pFrameView->IsMasterPagePaintCaching() ); + + SetDragWithCopy( pFrameView->IsDragWithCopy() ); + mbDoubleClickTextEdit = pFrameView->IsDoubleClickTextEdit(); + mbClickChangeRotation = pFrameView->IsClickChangeRotation(); + mnSlidesPerRow = pFrameView->GetSlidesPerRow(); + mnDrawMode = pFrameView->GetDrawMode(); + mbIsNavigatorShowingAllShapes = pFrameView->IsNavigatorShowingAllShapes(); + SetPreviousViewShellType (pFrameView->GetPreviousViewShellType()); + SetViewShellTypeOnLoad (pFrameView->GetViewShellTypeOnLoad()); + } + else + { + // initialize FrameView with the application data + + // Layers need to be set, otherwise they are not visible and not printable in + // Impress documents. The document contains already the actual layers and their + // settings for visible, printable and locked. In case not read from , + // ODF defaults are used. + SdrLayerAdmin rLayerAdmin = pDrawDoc -> GetLayerAdmin(); + rLayerAdmin.getVisibleLayersODF(maVisibleLayers); + rLayerAdmin.getPrintableLayersODF(maPrintableLayers); + rLayerAdmin.getLockedLayersODF(maLockedLayers); + SetGridCoarse( Size( 1000, 1000 ) ); + SetSnapGridWidth(Fraction(1000, 1), Fraction(1000, 1)); + SetActiveLayer(sUNO_LayerName_layout); + mbNoColors = true; + mbNoAttribs = false; + maVisArea = ::tools::Rectangle( Point(), Size(0, 0) ); + mePageKind = PageKind::Standard; + mePageKindOnLoad = PageKind::Standard; + mnSelectedPage = 0; + mnSelectedPageOnLoad = 0; + mePageEditMode = EditMode::Page; + // meStandardEditMode = EditMode::Page; + // meNotesEditMode = EditMode::Page; + // meHandoutEditMode = EditMode::MasterPage; + SetViewShEditModeOnLoad(EditMode::Page); + mbLayerMode = false; + SetEliminatePolyPoints(false); + mbDoubleClickTextEdit = false; + mbClickChangeRotation = false; + mnSlidesPerRow = 4; + + { + bool bUseContrast = Application::GetSettings().GetStyleSettings().GetHighContrastMode(); + mnDrawMode = bUseContrast ? OUTPUT_DRAWMODE_CONTRAST : OUTPUT_DRAWMODE_COLOR; + } + mbIsNavigatorShowingAllShapes = true; + SetPreviousViewShellType (ViewShell::ST_NONE); + SetViewShellTypeOnLoad (ViewShell::ST_IMPRESS); + + // get default for design mode + bool bInitDesignMode = pDrawDoc->GetOpenInDesignMode(); + if( pDrawDoc->OpenInDesignModeIsDefaulted() ) + { + bInitDesignMode = true; + } + + SfxObjectShell* pObjShell = pDrawDoc->GetObjectShell(); + if( pObjShell && pObjShell->IsReadOnly() ) + bInitDesignMode = false; + SetDesignMode( bInitDesignMode ); + + Update( SD_MOD()->GetSdOptions(pDrawDoc->GetDocumentType()) ); + } + +} + +FrameView::~FrameView() +{ +} + +void FrameView::Connect() +{ + mnRefCount++; +} + +void FrameView::Disconnect() +{ + if (mnRefCount > 0) + { + mnRefCount--; + } + + if (mnRefCount == 0) + { + delete this; + } +} + +/** + * Update with data from the specified SdOptions + */ +void FrameView::Update(SdOptions const * pOptions) +{ + if (!pOptions) + return; + + mbRuler = pOptions->IsRulerVisible(); + SetGridVisible( pOptions->IsGridVisible() ); + SetSnapAngle( pOptions->GetAngle() ); + SetGridSnap( pOptions->IsUseGridSnap() ); + SetBordSnap( pOptions->IsSnapBorder() ); + SetHlplSnap( pOptions->IsSnapHelplines() ); + SetOFrmSnap( pOptions->IsSnapFrame() ); + SetOPntSnap( pOptions->IsSnapPoints() ); + SetHlplVisible( pOptions->IsHelplines() ); + SetDragStripes( pOptions->IsDragStripes() ); + SetPlusHandlesAlwaysVisible( pOptions->IsHandlesBezier() ); + SetSnapMagneticPixel( pOptions->GetSnapArea() ); + SetMarkedHitMovesAlways( pOptions->IsMarkedHitMovesAlways() ); + SetMoveOnlyDragging( pOptions->IsMoveOnlyDragging() ); + SetSlantButShear( pOptions->IsMoveOnlyDragging() ); + SetNoDragXorPolys ( !pOptions->IsMoveOutline() ); + SetCrookNoContortion( pOptions->IsCrookNoContortion() ); + SetAngleSnapEnabled( pOptions->IsRotate() ); + SetBigOrtho( pOptions->IsBigOrtho() ); + SetOrtho( pOptions->IsOrtho() ); + SetEliminatePolyPointLimitAngle( pOptions->GetEliminatePolyPointLimitAngle() ); + GetModel()->SetPickThroughTransparentTextFrames( pOptions->IsPickThrough() ); + + SetSolidDragging( pOptions->IsSolidDragging() ); + + SetGridCoarse( Size( pOptions->GetFieldDrawX(), pOptions->GetFieldDrawY() ) ); + SetGridFine( Size( pOptions->GetFieldDivisionX(), pOptions->GetFieldDivisionY() ) ); + Fraction aFractX(pOptions->GetFieldDrawX(), pOptions->GetFieldDrawX() / ( pOptions->GetFieldDivisionX() ? pOptions->GetFieldDivisionX() : 1 )); + Fraction aFractY(pOptions->GetFieldDrawY(), pOptions->GetFieldDrawY() / ( pOptions->GetFieldDivisionY() ? pOptions->GetFieldDivisionY() : 1 )); + SetSnapGridWidth(aFractX, aFractY); + SetQuickEdit(pOptions->IsQuickEdit()); + + // #i26631# + SetMasterPagePaintCaching( pOptions->IsMasterPagePaintCaching() ); + + SetDragWithCopy(pOptions->IsDragWithCopy()); + SetDoubleClickTextEdit( pOptions->IsDoubleClickTextEdit() ); + SetClickChangeRotation( pOptions->IsClickChangeRotation() ); +} + +/** + * Set EditMode (Page or MasterPage) of working mode + */ +void FrameView::SetViewShEditMode(EditMode eMode) +{ + mePageEditMode = eMode; +} + +/** + * Return EditMode (Page or MasterPage) of working mode + */ +EditMode FrameView::GetViewShEditMode() const +{ + return mePageEditMode; +} + +void FrameView::SetViewShEditModeOnLoad (EditMode eMode) +{ + meEditModeOnLoad = eMode; +} + +static OUString createHelpLinesString( const SdrHelpLineList& rHelpLines ) +{ + OUStringBuffer aLines; + + const sal_uInt16 nCount = rHelpLines.GetCount(); + for( sal_uInt16 nHlpLine = 0; nHlpLine < nCount; nHlpLine++ ) + { + const SdrHelpLine& rHelpLine = rHelpLines[nHlpLine]; + const Point& rPos = rHelpLine.GetPos(); + + switch( rHelpLine.GetKind() ) + { + case SdrHelpLineKind::Point: + aLines.append( 'P' ); + aLines.append( static_cast(rPos.X()) ); + aLines.append( ',' ); + aLines.append( static_cast(rPos.Y()) ); + break; + case SdrHelpLineKind::Vertical: + aLines.append( 'V' ); + aLines.append( static_cast(rPos.X()) ); + break; + case SdrHelpLineKind::Horizontal: + aLines.append( 'H' ); + aLines.append( static_cast(rPos.Y()) ); + break; + default: + OSL_FAIL( "Unsupported helpline Kind!" ); + } + } + + return aLines.makeStringAndClear(); +} + +void FrameView::WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& rValues ) +{ + std::vector< std::pair< OUString, Any > > aUserData; + aUserData.reserve(41); // worst case + + aUserData.emplace_back( sUNO_View_GridIsVisible, Any( IsGridVisible() ) ); + aUserData.emplace_back( sUNO_View_GridIsFront, Any( IsGridFront() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToGrid, Any( IsGridSnap() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToPageMargins, Any( IsBordSnap() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToSnapLines, Any( IsHlplSnap() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToObjectFrame, Any( IsOFrmSnap() ) ); + aUserData.emplace_back( sUNO_View_IsSnapToObjectPoints, Any( IsOPntSnap() ) ); + + aUserData.emplace_back( sUNO_View_IsPlusHandlesAlwaysVisible, Any( IsPlusHandlesAlwaysVisible() ) ); + aUserData.emplace_back( sUNO_View_IsFrameDragSingles, Any( IsFrameDragSingles() ) ); + + aUserData.emplace_back( sUNO_View_EliminatePolyPointLimitAngle, Any( static_cast(GetEliminatePolyPointLimitAngle()) ) ); + aUserData.emplace_back( sUNO_View_IsEliminatePolyPoints, Any( IsEliminatePolyPoints() ) ); + + if ( officecfg::Office::Common::Misc::WriteLayerStateAsConfigItem::get() ) + { + SdrLayerAdmin& rLayerAdmin = getSdrModelFromSdrView().GetLayerAdmin(); + Any aAny; + rLayerAdmin.QueryValue(GetVisibleLayers(), aAny); + aUserData.emplace_back( sUNO_View_VisibleLayers, aAny ); + + rLayerAdmin.QueryValue(GetPrintableLayers(), aAny); + aUserData.emplace_back( sUNO_View_PrintableLayers, aAny ); + + rLayerAdmin.QueryValue(GetLockedLayers(), aAny); + aUserData.emplace_back( sUNO_View_LockedLayers, aAny ); + } + + aUserData.emplace_back( sUNO_View_NoAttribs, Any( IsNoAttribs() ) ); + aUserData.emplace_back( sUNO_View_NoColors, Any( IsNoColors() ) ); + + if( GetStandardHelpLines().GetCount() ) + aUserData.emplace_back( sUNO_View_SnapLinesDrawing, Any( createHelpLinesString( GetStandardHelpLines() ) ) ); + + if( GetNotesHelpLines().GetCount() ) + aUserData.emplace_back( sUNO_View_SnapLinesNotes, Any( createHelpLinesString( GetNotesHelpLines() ) ) ); + + if( GetHandoutHelpLines().GetCount() ) + aUserData.emplace_back( sUNO_View_SnapLinesHandout, Any( createHelpLinesString( GetHandoutHelpLines() ) ) ); + + aUserData.emplace_back( sUNO_View_RulerIsVisible, Any( HasRuler() ) ); + aUserData.emplace_back( sUNO_View_PageKind, Any( static_cast(GetPageKind()) ) ); + aUserData.emplace_back( sUNO_View_SelectedPage, Any( static_cast(GetSelectedPage()) ) ); + aUserData.emplace_back( sUNO_View_IsLayerMode, Any( IsLayerMode() ) ); + + aUserData.emplace_back( sUNO_View_IsDoubleClickTextEdit, Any( IsDoubleClickTextEdit() ) ); + aUserData.emplace_back( sUNO_View_IsClickChangeRotation, Any( IsClickChangeRotation() ) ); + + aUserData.emplace_back( sUNO_View_SlidesPerRow, Any( static_cast(GetSlidesPerRow()) ) ); + aUserData.emplace_back( sUNO_View_EditMode, Any( static_cast(GetViewShEditMode()) ) ); + // aUserData.emplace_back( sUNO_View_EditModeStandard, makeAny( (sal_Int32)GetViewShEditMode( PageKind::Standard ) ) ); + // aUserData.emplace_back( sUNO_View_EditModeNotes, makeAny( (sal_Int32)GetViewShEditMode( PageKind::Notes ) ) ); + // aUserData.emplace_back( sUNO_View_EditModeHandout, makeAny( (sal_Int32)GetViewShEditMode( PageKind::Handout ) ) ); + + { + const ::tools::Rectangle aVisArea = GetVisArea(); + + aUserData.emplace_back( sUNO_View_VisibleAreaTop, Any( static_cast(aVisArea.Top()) ) ); + aUserData.emplace_back( sUNO_View_VisibleAreaLeft, Any( static_cast(aVisArea.Left()) ) ); + aUserData.emplace_back( sUNO_View_VisibleAreaWidth, Any( static_cast(aVisArea.GetWidth()) ) ); + aUserData.emplace_back( sUNO_View_VisibleAreaHeight, Any( static_cast(aVisArea.GetHeight()) ) ); + } + + aUserData.emplace_back( sUNO_View_GridCoarseWidth, Any( static_cast(GetGridCoarse().Width()) ) ); + aUserData.emplace_back( sUNO_View_GridCoarseHeight, Any( static_cast(GetGridCoarse().Height()) ) ); + aUserData.emplace_back( sUNO_View_GridFineWidth, Any( static_cast(GetGridFine().Width()) ) ); + aUserData.emplace_back( sUNO_View_GridFineHeight, Any( static_cast(GetGridFine().Height()) ) ); + aUserData.emplace_back( sUNO_View_GridSnapWidthXNumerator, Any( GetSnapGridWidthX().GetNumerator() ) ); + aUserData.emplace_back( sUNO_View_GridSnapWidthXDenominator, Any( GetSnapGridWidthX().GetDenominator() ) ); + aUserData.emplace_back( sUNO_View_GridSnapWidthYNumerator, Any( GetSnapGridWidthY().GetNumerator() ) ); + aUserData.emplace_back( sUNO_View_GridSnapWidthYDenominator, Any( GetSnapGridWidthY().GetDenominator() ) ); + aUserData.emplace_back( sUNO_View_IsAngleSnapEnabled, Any( IsAngleSnapEnabled() ) ); + aUserData.emplace_back( sUNO_View_SnapAngle, Any( static_cast(GetSnapAngle()) ) ); + + const sal_Int32 nOldLength = rValues.getLength(); + rValues.realloc( nOldLength + aUserData.size() ); + + PropertyValue* pValue = &(rValues.getArray()[nOldLength]); + + for( const auto& rItem : aUserData ) + { + pValue->Name = rItem.first; + pValue->Value = rItem.second; + ++pValue; + } +} + +static void createHelpLinesFromString( const OUString& rLines, SdrHelpLineList& rHelpLines ) +{ + const sal_Unicode * pStr = rLines.getStr(); + SdrHelpLine aNewHelpLine; + OUStringBuffer sBuffer; + + while( *pStr ) + { + Point aPoint; + + switch( *pStr ) + { + case 'P': + aNewHelpLine.SetKind( SdrHelpLineKind::Point ); + break; + case 'V': + aNewHelpLine.SetKind( SdrHelpLineKind::Vertical ); + break; + case 'H': + aNewHelpLine.SetKind( SdrHelpLineKind::Horizontal ); + break; + default: + OSL_FAIL( "syntax error in snap lines settings string" ); + return; + } + + pStr++; + + while( (*pStr >= '0' && *pStr <= '9') || (*pStr == '+') || (*pStr == '-') ) + { + sBuffer.append( *pStr++ ); + } + + sal_Int32 nValue = sBuffer.makeStringAndClear().toInt32(); + + if( aNewHelpLine.GetKind() == SdrHelpLineKind::Horizontal ) + { + aPoint.setY( nValue ); + } + else + { + aPoint.setX( nValue ); + + if( aNewHelpLine.GetKind() == SdrHelpLineKind::Point ) + { + if( *pStr++ != ',' ) + return; + + while( (*pStr >= '0' && *pStr <= '9') || (*pStr == '+') || (*pStr == '-') ) + { + sBuffer.append( *pStr++ ); + } + + aPoint.setY( sBuffer.makeStringAndClear().toInt32() ); + + } + } + + aNewHelpLine.SetPos( aPoint ); + rHelpLines.Insert( aNewHelpLine ); + } +} + +void FrameView::ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + const sal_Int32 nLength = rSequence.getLength(); + if (!nLength) + return; + + SdDrawDocument* pDrawDocument = dynamic_cast(GetModel()); + const bool bImpress = pDrawDocument && pDrawDocument->GetDocumentType() == DocumentType::Impress; + + bool bBool = false; + sal_Int32 nInt32 = 0; + sal_Int16 nInt16 = 0; + OUString aString; + + sal_Int32 aSnapGridWidthXNum = GetSnapGridWidthX().GetNumerator(); + sal_Int32 aSnapGridWidthXDom = GetSnapGridWidthX().GetDenominator(); + + sal_Int32 aSnapGridWidthYNum = GetSnapGridWidthY().GetNumerator(); + sal_Int32 aSnapGridWidthYDom = GetSnapGridWidthY().GetDenominator(); + + for (const css::beans::PropertyValue& rValue : rSequence) + { + if ( rValue.Name == sUNO_View_ViewId ) + { + } + else if ( rValue.Name == sUNO_View_SnapLinesDrawing ) + { + if( rValue.Value >>= aString ) + { + SdrHelpLineList aHelpLines; + createHelpLinesFromString( aString, aHelpLines ); + SetStandardHelpLines( aHelpLines ); + } + } + else if ( rValue.Name == sUNO_View_SnapLinesNotes ) + { + if( rValue.Value >>= aString ) + { + SdrHelpLineList aHelpLines; + createHelpLinesFromString( aString, aHelpLines ); + SetNotesHelpLines( aHelpLines ); + } + } + else if ( rValue.Name == sUNO_View_SnapLinesHandout ) + { + if( rValue.Value >>= aString ) + { + SdrHelpLineList aHelpLines; + createHelpLinesFromString( aString, aHelpLines ); + SetHandoutHelpLines( aHelpLines ); + } + } + else if ( rValue.Name == sUNO_View_RulerIsVisible ) + { + if( rValue.Value >>= bBool ) + { + SetRuler( bBool ); + } + } + else if ( rValue.Name == sUNO_View_PageKind ) + { + if( rValue.Value >>= nInt16 ) + { + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( GetModel() ); + if( pDoc && pDoc->GetDocSh() && ( SfxObjectCreateMode::EMBEDDED == pDoc->GetDocSh()->GetCreateMode() ) ) + SetPageKind( static_cast(nInt16) ); + + SetPageKindOnLoad( static_cast(nInt16) ); + } + } + else if ( rValue.Name == sUNO_View_SelectedPage ) + { + if( rValue.Value >>= nInt16 ) + { + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( GetModel() ); + if( pDoc && pDoc->GetDocSh() && ( SfxObjectCreateMode::EMBEDDED == pDoc->GetDocSh()->GetCreateMode() ) ) + SetSelectedPage( static_cast(nInt16) ); + + SetSelectedPageOnLoad( static_cast(nInt16) ); + } + } + else if ( rValue.Name == sUNO_View_IsLayerMode ) + { + if( rValue.Value >>= bBool ) + { + SetLayerMode( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsDoubleClickTextEdit ) + { + if( rValue.Value >>= bBool ) + { + SetDoubleClickTextEdit( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsClickChangeRotation ) + { + if( rValue.Value >>= bBool ) + { + SetClickChangeRotation( bBool ); + } + } + else if ( rValue.Name == sUNO_View_SlidesPerRow ) + { + if( rValue.Value >>= nInt16 ) + { + SetSlidesPerRow( static_cast(nInt16) ); + } + } + else if ( rValue.Name == sUNO_View_EditMode ) + { + if( rValue.Value >>= nInt32 ) + { + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( GetModel() ); + if( pDoc && pDoc->GetDocSh() && ( SfxObjectCreateMode::EMBEDDED == pDoc->GetDocSh()->GetCreateMode() ) ) + SetViewShEditMode( static_cast(nInt32) ); + } + } + // This one is kept for compatibility. Old value read from sUNO_View_EditModeStandard + // is used. New value will be written into sUNO_View_EditMode. + // Values from sUNO_View_EditModeNotes and sUNO_View_EditModeHangout will be ignored. + else if ( rValue.Name == sUNO_View_EditModeStandard ) + { + if( rValue.Value >>= nInt32 ) + { + SdDrawDocument* pDoc = dynamic_cast< SdDrawDocument* >( GetModel() ); + if( pDoc && pDoc->GetDocSh() && ( SfxObjectCreateMode::EMBEDDED == pDoc->GetDocSh()->GetCreateMode() ) ) + SetViewShEditMode( static_cast(nInt32) ); + } + } + else if ( rValue.Name == sUNO_View_VisibleAreaTop ) + { + sal_Int32 nTop = 0; + if( rValue.Value >>= nTop ) + { + ::tools::Rectangle aVisArea( GetVisArea() ); + aVisArea.AdjustBottom(nTop - aVisArea.Top() ); + aVisArea.SetTop( nTop ); + SetVisArea( aVisArea ); + } + } + else if ( rValue.Name == sUNO_View_VisibleAreaLeft ) + { + sal_Int32 nLeft = 0; + if( rValue.Value >>= nLeft ) + { + ::tools::Rectangle aVisArea( GetVisArea() ); + aVisArea.AdjustRight(nLeft - aVisArea.Left() ); + aVisArea.SetLeft( nLeft ); + SetVisArea( aVisArea ); + } + } + else if ( rValue.Name == sUNO_View_VisibleAreaWidth ) + { + sal_Int32 nWidth = 0; + if( rValue.Value >>= nWidth ) + { + ::tools::Rectangle aVisArea( GetVisArea() ); + aVisArea.SetRight( aVisArea.Left() + nWidth - 1 ); + SetVisArea( aVisArea ); + } + } + else if ( rValue.Name == sUNO_View_VisibleAreaHeight ) + { + sal_Int32 nHeight = 0; + if( rValue.Value >>= nHeight ) + { + ::tools::Rectangle aVisArea( GetVisArea() ); + aVisArea.SetBottom( nHeight + aVisArea.Top() - 1 ); + SetVisArea( aVisArea ); + } + } + + else if ( rValue.Name == sUNO_View_GridIsVisible ) + { + if( rValue.Value >>= bBool ) + { + SetGridVisible( bBool ); + } + } + + else if ( rValue.Name == sUNO_View_IsSnapToGrid ) + { + if( rValue.Value >>= bBool ) + { + SetGridSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_GridIsFront ) + { + if( rValue.Value >>= bBool ) + { + SetGridFront( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsSnapToPageMargins ) + { + if( rValue.Value >>= bBool ) + { + SetBordSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsSnapToSnapLines ) + { + if( rValue.Value >>= bBool ) + { + SetHlplSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsSnapToObjectFrame ) + { + if( rValue.Value >>= bBool ) + { + SetOFrmSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsSnapToObjectPoints ) + { + if( rValue.Value >>= bBool ) + { + SetOPntSnap( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsPlusHandlesAlwaysVisible ) + { + if( rValue.Value >>= bBool ) + { + SetPlusHandlesAlwaysVisible( bBool ); + } + } + else if ( rValue.Name == sUNO_View_IsFrameDragSingles ) + { + if( rValue.Value >>= bBool ) + { + SetFrameDragSingles( bBool ); + } + } + else if ( rValue.Name == sUNO_View_EliminatePolyPointLimitAngle ) + { + if( rValue.Value >>= nInt32 ) + { + SetEliminatePolyPointLimitAngle( Degree100(nInt32) ); + } + } + else if ( rValue.Name == sUNO_View_IsEliminatePolyPoints ) + { + if( rValue.Value >>= bBool ) + { + SetEliminatePolyPoints( bBool ); + } + } + else if ( rValue.Name == sUNO_View_ActiveLayer ) + { + if( rValue.Value >>= aString ) + { + SetActiveLayer( aString ); + } + } + else if ( rValue.Name == sUNO_View_NoAttribs ) + { + if( rValue.Value >>= bBool ) + { + SetNoAttribs( bBool ); + } + } + else if ( rValue.Name == sUNO_View_NoColors ) + { + if( rValue.Value >>= bBool ) + { + SetNoColors( bBool ); + } + } + else if ( rValue.Name == sUNO_View_GridCoarseWidth ) + { + if( rValue.Value >>= nInt32 ) + { + const Size aCoarse( nInt32, GetGridCoarse().Height() ); + SetGridCoarse( aCoarse ); + } + } + else if ( rValue.Name == sUNO_View_GridCoarseHeight ) + { + if( rValue.Value >>= nInt32 ) + { + const Size aCoarse( GetGridCoarse().Width(), nInt32 ); + SetGridCoarse( aCoarse ); + } + } + else if ( rValue.Name == sUNO_View_GridFineWidth ) + { + if( rValue.Value >>= nInt32 ) + { + const Size aCoarse( nInt32, GetGridFine().Height() ); + SetGridFine( aCoarse ); + } + } + else if ( rValue.Name == sUNO_View_GridFineHeight ) + { + if( rValue.Value >>= nInt32 ) + { + const Size aCoarse( GetGridFine().Width(), nInt32 ); + SetGridFine( aCoarse ); + } + } + else if ( rValue.Name == sUNO_View_IsAngleSnapEnabled ) + { + if( rValue.Value >>= bBool ) + { + SetAngleSnapEnabled( bBool ); + } + } + else if ( rValue.Name == sUNO_View_SnapAngle ) + { + if( rValue.Value >>= nInt32 ) + { + SetSnapAngle( Degree100(nInt32) ); + } + } + else if ( rValue.Name == sUNO_View_GridSnapWidthXNumerator ) + { + rValue.Value >>= aSnapGridWidthXNum; + } + else if ( rValue.Name == sUNO_View_GridSnapWidthXDenominator ) + { + rValue.Value >>= aSnapGridWidthXDom; + } + else if ( rValue.Name == sUNO_View_GridSnapWidthYNumerator ) + { + rValue.Value >>= aSnapGridWidthYNum; + } + else if ( rValue.Name == sUNO_View_GridSnapWidthYDenominator ) + { + rValue.Value >>= aSnapGridWidthYDom; + } + else if (!bImpress && rValue.Name == sUNO_View_VisibleLayers ) + { + SdrLayerIDSet aSdrLayerIDSets; + aSdrLayerIDSets.PutValue( rValue.Value ); + SetVisibleLayers( aSdrLayerIDSets ); + } + else if (!bImpress && rValue.Name == sUNO_View_PrintableLayers ) + { + SdrLayerIDSet aSdrLayerIDSets; + aSdrLayerIDSets.PutValue( rValue.Value ); + SetPrintableLayers( aSdrLayerIDSets ); + } + else if (!bImpress && rValue.Name == sUNO_View_LockedLayers ) + { + SdrLayerIDSet aSdrLayerIDSets; + aSdrLayerIDSets.PutValue( rValue.Value ); + SetLockedLayers( aSdrLayerIDSets ); + } + } + + SetViewShEditModeOnLoad(EditMode::Page); + + const Fraction aSnapGridWidthX( aSnapGridWidthXNum, aSnapGridWidthXDom ); + const Fraction aSnapGridWidthY( aSnapGridWidthYNum, aSnapGridWidthYDom ); + + SetSnapGridWidth( aSnapGridWidthX, aSnapGridWidthY ); +} + +void FrameView::SetPreviousViewShellType (ViewShell::ShellType eType) +{ + mePreviousViewShellType = eType; +} + +void FrameView::SetViewShellTypeOnLoad (ViewShell::ShellType eType) +{ + meViewShellTypeOnLoad = eType; +} + +void FrameView::SetSelectedPage(sal_uInt16 nPage) +{ + mnSelectedPage = nPage; +} + +void FrameView::SetIsNavigatorShowingAllShapes (const bool bIsNavigatorShowingAllShapes) +{ + mbIsNavigatorShowingAllShapes = bIsNavigatorShowingAllShapes; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/grviewsh.cxx b/sd/source/ui/view/grviewsh.cxx new file mode 100644 index 000000000..b914b2da8 --- /dev/null +++ b/sd/source/ui/view/grviewsh.cxx @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include + +constexpr sal_Int32 TAB_HEIGHT_MARGIN = 10; + +namespace sd { + +GraphicViewShell::GraphicViewShell ( + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameView) + : DrawViewShell ( + rViewShellBase, + pParentWindow, + PageKind::Standard, + pFrameView) +{ + ConstructGraphicViewShell(); +} + +GraphicViewShell::~GraphicViewShell() +{ +} + +void GraphicViewShell::ConstructGraphicViewShell() +{ + meShellType = ST_DRAW; + + mpLayerTabBar.reset (VclPtr::Create(this, GetParentWindow())); + + // #i67363# no layer tabbar in preview mode + if ( !GetObjectShell()->IsPreview() ) + mpLayerTabBar->Show(); +} + +void GraphicViewShell::ChangeEditMode ( + EditMode eMode, + bool ) +{ + // There is no page tab that could be shown instead of the layer tab. + // Therefore we have it always visible regardless of what the caller + // said. (We have to change the callers behaviour, of course.) + DrawViewShell::ChangeEditMode (eMode, true); +} + +void GraphicViewShell::ArrangeGUIElements() +{ + if (mpLayerTabBar && mpLayerTabBar->IsVisible()) + { + Size aSize = mpLayerTabBar->GetSizePixel(); + const Size aFrameSize (GetViewFrame()->GetWindow().GetOutputSizePixel()); + + aSize.setHeight(GetParentWindow()->GetFont().GetFontHeight() + TAB_HEIGHT_MARGIN); + aSize.setWidth( aFrameSize.Width() ); + + Point aPos (0, maViewSize.Height() - aSize.Height()); + + mpLayerTabBar->SetPosSizePixel (aPos, aSize); + } + + DrawViewShell::ArrangeGUIElements(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/outlnvs2.cxx b/sd/source/ui/view/outlnvs2.cxx new file mode 100644 index 000000000..2a890cec1 --- /dev/null +++ b/sd/source/ui/view/outlnvs2.cxx @@ -0,0 +1,636 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + +namespace sd { + +/************************************************************************/ + +/** + * SfxRequests for temporary functions + */ + +void OutlineViewShell::FuTemporary(SfxRequest &rReq) +{ + DeactivateCurrentFunction(); + + OutlinerView* pOutlinerView = pOlView->GetViewByWindow( GetActiveWindow() ); + sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_ATTR_ZOOM: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + + if ( pArgs ) + { + SvxZoomType eZT = pArgs->Get( SID_ATTR_ZOOM ).GetType(); + switch( eZT ) + { + case SvxZoomType::PERCENT: + SetZoom( static_cast<::tools::Long>( pArgs->Get( SID_ATTR_ZOOM ).GetValue()) ); + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ATTR_ZOOMSLIDER ); + break; + default: + break; + } + rReq.Done(); + } + else + { + // open the zoom dialog here + SetCurrentFunction( FuScale::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + } + Cancel(); + } + break; + + case SID_ATTR_ZOOMSLIDER: + { + const SfxItemSet* pArgs = rReq.GetArgs(); + + const SfxUInt16Item* pScale = (pArgs && pArgs->Count () == 1) ? + rReq.GetArg(SID_ATTR_ZOOMSLIDER) : nullptr; + if (pScale && CHECK_RANGE (5, pScale->GetValue (), 3000)) + { + SetZoom (pScale->GetValue ()); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_ATTR_ZOOM ); + rBindings.Invalidate( SID_ZOOM_IN ); + rBindings.Invalidate( SID_ZOOM_OUT ); + rBindings.Invalidate( SID_ATTR_ZOOMSLIDER ); + + } + + Cancel(); + rReq.Done (); + break; + } + + case SID_ZOOM_IN: + { + SetZoom( std::min<::tools::Long>( GetActiveWindow()->GetZoom() * 2, GetActiveWindow()->GetMaxZoom() ) ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ZOOM_IN ); + Invalidate(SID_ZOOM_OUT); + Invalidate( SID_ATTR_ZOOMSLIDER ); + Cancel(); + rReq.Done(); + } + break; + + case SID_SIZE_REAL: + { + SetZoom( 100 ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ATTR_ZOOMSLIDER ); + Cancel(); + rReq.Done(); + } + break; + + case SID_ZOOM_OUT: + { + SetZoom( std::max<::tools::Long>( GetActiveWindow()->GetZoom() / 2, GetActiveWindow()->GetMinZoom() ) ); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), + GetActiveWindow()->GetOutputSizePixel()) ); + mpZoomList->InsertZoomRect(aVisAreaWin); + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ZOOM_OUT); + Invalidate( SID_ZOOM_IN ); + Invalidate( SID_ATTR_ZOOMSLIDER ); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_COLLAPSE_ALL: + { + pOutlinerView->CollapseAll(); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_COLLAPSE: + { + pOutlinerView->Collapse(); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_EXPAND_ALL: + { + pOutlinerView->ExpandAll(); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_EXPAND: + { + pOutlinerView->Expand(); + Cancel(); + rReq.Done(); + } + break; + + case SID_OUTLINE_FORMAT: + { + ::Outliner* pOutl = pOutlinerView->GetOutliner(); + pOutl->SetFlatMode( !pOutl->IsFlatMode() ); + Invalidate( SID_COLORVIEW ); + Cancel(); + rReq.Done(); + } + break; + + case SID_SELECTALL: + { + ::Outliner& rOutl = pOlView->GetOutliner(); + sal_Int32 nParaCount = rOutl.GetParagraphCount(); + if (nParaCount > 0) + { + pOutlinerView->SelectRange( 0, nParaCount ); + } + Cancel(); + } + break; + + case SID_PRESENTATION: + case SID_PRESENTATION_CURRENT_SLIDE: + case SID_REHEARSE_TIMINGS: + { + pOlView->PrepareClose(); + slideshowhelp::ShowSlideShow(rReq, *GetDoc()); + Cancel(); + rReq.Done(); + } + break; + + case SID_COLORVIEW: + { + ::Outliner* pOutl = pOutlinerView->GetOutliner(); + EEControlBits nCntrl = pOutl->GetControlWord(); + + if ( !(nCntrl & EEControlBits::NOCOLORS) ) + { + // color view is enabled: disable + pOutl->SetControlWord(nCntrl | EEControlBits::NOCOLORS); + } + else + { + // color view is disabled: enable + pOutl->SetControlWord(nCntrl & ~EEControlBits::NOCOLORS); + } + + InvalidateWindows(); + Invalidate( SID_COLORVIEW ); + Cancel(); + rReq.Done(); + } + break; + + case SID_STYLE_EDIT: + case SID_STYLE_UPDATE_BY_EXAMPLE: + { + if( rReq.GetArgs() ) + { + SetCurrentFunction( FuTemplate::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + + rReq.Ignore (); + } + break; + + case SID_PRESENTATION_DLG: + { + SetCurrentFunction( FuSlideShowDlg::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_REMOTE_DLG: + { +#ifdef ENABLE_SDREMOTE + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + ScopedVclPtr pDlg(pFact->CreateRemoteDialog(GetFrameWeld())); + pDlg->Execute(); +#endif + } + break; + + case SID_CUSTOMSHOW_DLG: + { + SetCurrentFunction( FuCustomShowDlg::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_PHOTOALBUM: + { + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr pDlg(pFact->CreateSdPhotoAlbumDialog( + pWin ? pWin->GetFrameWeld() : nullptr, + GetDoc())); + + pDlg->Execute(); + + Cancel(); + rReq.Ignore (); + } + break; + } + + if(HasCurrentFunction()) + GetCurrentFunction()->Activate(); + + Invalidate( SID_OUTLINE_COLLAPSE_ALL ); + Invalidate( SID_OUTLINE_COLLAPSE ); + Invalidate( SID_OUTLINE_EXPAND_ALL ); + Invalidate( SID_OUTLINE_EXPAND ); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_OUTLINE_LEFT ); + rBindings.Invalidate( SID_OUTLINE_RIGHT ); + rBindings.Invalidate( SID_OUTLINE_UP ); + rBindings.Invalidate( SID_OUTLINE_DOWN ); + + Invalidate( SID_OUTLINE_FORMAT ); + Invalidate( SID_COLORVIEW ); + Invalidate(SID_CUT); + Invalidate(SID_COPY); + Invalidate(SID_PASTE); + Invalidate(SID_PASTE_UNFORMATTED); +} + +void OutlineViewShell::FuTemporaryModify(SfxRequest &rReq) +{ + sal_uInt16 nSId = rReq.GetSlot(); + std::unique_ptr> aGuard; + if (nSId != SID_OUTLINE_BULLET && nSId != FN_SVX_SET_BULLET && nSId != FN_SVX_SET_NUMBER) + { + aGuard.reset( new OutlineViewModelChangeGuard(*pOlView) ); + } + DeactivateCurrentFunction(); + + OutlinerView* pOutlinerView = pOlView->GetViewByWindow( GetActiveWindow() ); + //sal_uInt16 nSId = rReq.GetSlot(); + + switch( nSId ) + { + case SID_HYPERLINK_SETLINK: + { + const SfxItemSet* pReqArgs = rReq.GetArgs(); + + if (pReqArgs) + { + const SvxHyperlinkItem* pHLItem = + &pReqArgs->Get(SID_HYPERLINK_SETLINK); + + SvxFieldItem aURLItem(SvxURLField(pHLItem->GetURL(), + pHLItem->GetName(), + SvxURLFormat::Repr), EE_FEATURE_FIELD); + ESelection aSel( pOutlinerView->GetSelection() ); + pOutlinerView->InsertField(aURLItem); + if ( aSel.nStartPos <= aSel.nEndPos ) + aSel.nEndPos = aSel.nStartPos + 1; + else + aSel.nStartPos = aSel.nEndPos + 1; + pOutlinerView->SetSelection( aSel ); + } + + Cancel(); + rReq.Ignore (); + } + break; + + case FN_INSERT_SOFT_HYPHEN: + case FN_INSERT_HARDHYPHEN: + case FN_INSERT_HARD_SPACE: + case FN_INSERT_NNBSP: + case SID_INSERT_RLM : + case SID_INSERT_LRM : + case SID_INSERT_WJ : + case SID_INSERT_ZWSP: + case SID_CHARMAP: + { + SetCurrentFunction( FuBullet::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_OUTLINE_BULLET: + case FN_SVX_SET_BULLET: + case FN_SVX_SET_NUMBER: + { + SetCurrentFunction( FuBulletAndPosition::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_THESAURUS: + { + SetCurrentFunction( FuThesaurus::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + rReq.Ignore (); + } + break; + + case SID_CHAR_DLG_EFFECT: + case SID_CHAR_DLG: + { + SetCurrentFunction( FuChar::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + Cancel(); + } + break; + + case SID_INSERTFILE: + { + SetCurrentFunction( FuInsertFile::Create(this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_PRESENTATIONOBJECT: + { + SetCurrentFunction( FuPresentationObjects::Create(this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq) ); + Cancel(); + } + break; + + case SID_SET_DEFAULT: + { + pOutlinerView->RemoveAttribs(true); // sal_True = also paragraph attributes + Cancel(); + rReq.Done(); + } + break; + + case SID_SUMMARY_PAGE: + { + pOlView->SetSelectedPages(); + SetCurrentFunction( FuSummaryPage::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + pOlView->GetOutliner().Clear(); + pOlView->FillOutliner(); + pOlView->GetActualPage(); + Cancel(); + } + break; + + case SID_EXPAND_PAGE: + { + pOlView->SetSelectedPages(); + SetCurrentFunction( FuExpandPage::Create( this, GetActiveWindow(), pOlView.get(), GetDoc(), rReq ) ); + pOlView->GetOutliner().Clear(); + pOlView->FillOutliner(); + pOlView->GetActualPage(); + Cancel(); + } + break; + + case SID_INSERT_FLD_DATE_FIX: + case SID_INSERT_FLD_DATE_VAR: + case SID_INSERT_FLD_TIME_FIX: + case SID_INSERT_FLD_TIME_VAR: + case SID_INSERT_FLD_AUTHOR: + case SID_INSERT_FLD_PAGE: + case SID_INSERT_FLD_PAGE_TITLE: + case SID_INSERT_FLD_PAGES: + case SID_INSERT_FLD_FILE: + { + std::unique_ptr pFieldItem; + + switch( nSId ) + { + case SID_INSERT_FLD_DATE_FIX: + pFieldItem.reset(new SvxFieldItem( + SvxDateField( Date( Date::SYSTEM ), SvxDateType::Fix ), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_DATE_VAR: + pFieldItem.reset(new SvxFieldItem( SvxDateField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_TIME_FIX: + pFieldItem.reset(new SvxFieldItem( + SvxExtTimeField( ::tools::Time( ::tools::Time::SYSTEM ), SvxTimeType::Fix ), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_TIME_VAR: + pFieldItem.reset(new SvxFieldItem( SvxExtTimeField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_AUTHOR: + { + SvtUserOptions aUserOptions; + pFieldItem.reset(new SvxFieldItem( + SvxAuthorField( + aUserOptions.GetFirstName(), aUserOptions.GetLastName(), aUserOptions.GetID() ) + , EE_FEATURE_FIELD )); + } + break; + + case SID_INSERT_FLD_PAGE: + pFieldItem.reset(new SvxFieldItem( SvxPageField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_PAGE_TITLE: + pFieldItem.reset(new SvxFieldItem( SvxPageTitleField(), EE_FEATURE_FIELD)); + break; + + case SID_INSERT_FLD_PAGES: + pFieldItem.reset(new SvxFieldItem( SvxPagesField(), EE_FEATURE_FIELD )); + break; + + case SID_INSERT_FLD_FILE: + { + OUString aName; + if( GetDocSh()->HasName() ) + aName = GetDocSh()->GetMedium()->GetName(); + //else + // aName = GetDocSh()->GetName(); + pFieldItem.reset(new SvxFieldItem( SvxExtFileField( aName ), EE_FEATURE_FIELD )); + } + break; + } + + const SvxFieldItem* pOldFldItem = pOutlinerView->GetFieldAtSelection(); + + if( pOldFldItem && ( nullptr != dynamic_cast< const SvxURLField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxDateField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxTimeField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxPageField *>( pOldFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxPagesField *>( pOldFldItem->GetField() )) ) + { + // select field, so it gets deleted on Insert + ESelection aSel = pOutlinerView->GetSelection(); + if( aSel.nStartPos == aSel.nEndPos ) + aSel.nEndPos++; + pOutlinerView->SetSelection( aSel ); + } + + if( pFieldItem ) + pOutlinerView->InsertField( *pFieldItem ); + + pFieldItem.reset(); + + Cancel(); + rReq.Ignore (); + } + break; + + case SID_MODIFY_FIELD: + { + const SvxFieldItem* pFldItem = pOutlinerView->GetFieldAtSelection(); + + if( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) ) ) + { + // Dialog... + SdAbstractDialogFactory* pFact = SdAbstractDialogFactory::Create(); + vcl::Window* pWin = GetActiveWindow(); + ScopedVclPtr pDlg(pFact->CreateSdModifyFieldDlg(pWin ? pWin->GetFrameWeld() : nullptr, pFldItem->GetField(), pOutlinerView->GetAttribs() )); + if( pDlg->Execute() == RET_OK ) + { + std::unique_ptr pField(pDlg->GetField()); + if( pField ) + { + SvxFieldItem aFieldItem( *pField, EE_FEATURE_FIELD ); + //pOLV->DeleteSelected(); <-- unfortunately missing! + // select field, so it gets deleted on Insert + ESelection aSel = pOutlinerView->GetSelection(); + bool bSel = true; + if( aSel.nStartPos == aSel.nEndPos ) + { + bSel = false; + aSel.nEndPos++; + } + pOutlinerView->SetSelection( aSel ); + + pOutlinerView->InsertField( aFieldItem ); + + // reset selection to original state + if( !bSel ) + aSel.nEndPos--; + pOutlinerView->SetSelection( aSel ); + + pField.reset(); + } + + SfxItemSet aSet( pDlg->GetItemSet() ); + if( aSet.Count() ) + { + pOutlinerView->SetAttribs( aSet ); + + ::Outliner* pOutliner = pOutlinerView->GetOutliner(); + if( pOutliner ) + pOutliner->UpdateFields(); + } + } + } + + Cancel(); + rReq.Ignore (); + } + break; + } + + if(HasCurrentFunction()) + GetCurrentFunction()->Activate(); + + Invalidate( SID_OUTLINE_COLLAPSE_ALL ); + Invalidate( SID_OUTLINE_COLLAPSE ); + Invalidate( SID_OUTLINE_EXPAND_ALL ); + Invalidate( SID_OUTLINE_EXPAND ); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_OUTLINE_LEFT ); + rBindings.Invalidate( SID_OUTLINE_RIGHT ); + rBindings.Invalidate( SID_OUTLINE_UP ); + rBindings.Invalidate( SID_OUTLINE_DOWN ); + + Invalidate( SID_OUTLINE_FORMAT ); + Invalidate( SID_COLORVIEW ); + Invalidate(SID_CUT); + Invalidate(SID_COPY); + Invalidate(SID_PASTE); + Invalidate(SID_PASTE_UNFORMATTED); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/outlnvsh.cxx b/sd/source/ui/view/outlnvsh.cxx new file mode 100644 index 000000000..0eb351f91 --- /dev/null +++ b/sd/source/ui/view/outlnvsh.cxx @@ -0,0 +1,1883 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::linguistic2; + +using namespace sd; + +#define ShellClass_OutlineViewShell +#include + +namespace sd { + +#define MIN_ZOOM 10 // minimum zoom factor +#define MAX_ZOOM 1000 // maximum zoom factor + +/** + * Declare SFX-Slotmap and standard interface + */ +SFX_IMPL_INTERFACE(OutlineViewShell, SfxShell) + +void OutlineViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterPopupMenu("outline"); + + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS, SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server, + ToolbarId::Outline_Toolbox); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Client | SfxVisibilityFlags::Viewer | SfxVisibilityFlags::ReadonlyDoc, + ToolbarId::Draw_Viewer_Toolbox); + + GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SvxHlinkDlgWrapper::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(::sd::SpellDialogChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(SID_SEARCH_DLG); + GetStaticInterface()->RegisterChildWindow(sfx2::sidebar::SidebarChildWindow::GetChildWindowId()); + GetStaticInterface()->RegisterChildWindow(DevelopmentToolChildWindow::GetChildWindowId()); +} + + +/** + * common initialization part of both constructors + */ +void OutlineViewShell::Construct() +{ + bool bModified = GetDoc()->IsChanged(); + + meShellType = ST_OUTLINE; + Size aSize(29700, 21000); + Point aWinPos (0, 0); + Point aViewOrigin(0, 0); + GetActiveWindow()->SetMinZoomAutoCalc(false); + GetActiveWindow()->SetMinZoom( MIN_ZOOM ); + GetActiveWindow()->SetMaxZoom( MAX_ZOOM ); + InitWindows(aViewOrigin, aSize, aWinPos); + pOlView.reset( new OutlineView(*GetDocSh(), GetActiveWindow(), *this) ); + mpView = pOlView.get(); // Pointer of base class ViewShell + + SetPool( &GetDoc()->GetPool() ); + + SetZoom(69); + + // Apply settings of FrameView + ReadFrameViewData(mpFrameView); + + ::Outliner& rOutl = pOlView->GetOutliner(); + rOutl.SetUpdateLayout(true); + + if (!bModified) + { + rOutl.ClearModifyFlag(); + } + + pLastPage = GetActualPage(); + + SetName( "OutlineViewShell" ); + + GetActiveWindow()->SetHelpId( HID_SDOUTLINEVIEWSHELL ); +} + +Reference OutlineViewShell::CreateSubController() +{ + Reference xSubController; + + if (IsMainViewShell()) + { + // Create uno sub controller for the main view shell. + xSubController.set( new SdUnoOutlineView(*this) ); + } + + return xSubController; +} + +/** + * Default constructor, windows must not center themselves automatically + */ +OutlineViewShell::OutlineViewShell ( + SfxViewFrame* /*pFrame*/, + ViewShellBase& rViewShellBase, + vcl::Window* pParentWindow, + FrameView* pFrameViewArgument) + : ViewShell(pParentWindow, rViewShellBase), + pLastPage( nullptr ), + bPastePossible(false), + mbInitialized(false) + +{ + if (pFrameViewArgument != nullptr) + mpFrameView = pFrameViewArgument; + else + mpFrameView = new FrameView(GetDoc()); + + mpFrameView->Connect(); + + Construct(); + + SetContextName(vcl::EnumContext::GetContextName(vcl::EnumContext::Context::OutlineText)); + + m_StrOldPageName.clear(); + + doShow(); +} + +OutlineViewShell::~OutlineViewShell() +{ + DisposeFunctions(); + + pOlView.reset(); + + mpFrameView->Disconnect(); + + if ( mxClipEvtLstnr.is() ) + { + mxClipEvtLstnr->RemoveListener( GetActiveWindow() ); + mxClipEvtLstnr->ClearCallbackLink(); // prevent callback if another thread is waiting + } +} + +void OutlineViewShell::Shutdown() +{ + ViewShell::Shutdown(); + + PrepareClose(); +} + +/** + * Paint method: the event gets forwarded from pWindow to the Viewshell + * and the current function + */ +void OutlineViewShell::Paint(const ::tools::Rectangle& rRect, ::sd::Window* pWin) +{ + if (pOlView) + { + pOlView->Paint(rRect, pWin); + } +} + +void OutlineViewShell::ArrangeGUIElements () +{ + // Retrieve the current size (thickness) of the scroll bars. That is + // the width of the vertical and the height of the horizontal scroll + // bar. + int nScrollBarSize = + GetParentWindow()->GetSettings().GetStyleSettings().GetScrollBarSize(); + maScrBarWH = Size (nScrollBarSize, nScrollBarSize); + + ViewShell::ArrangeGUIElements (); + + ::sd::Window* pWindow = mpContentWindow.get(); + if (pWindow == nullptr) + return; + + pWindow->SetMinZoomAutoCalc(false); + + // change OutputArea of the OutlinerView + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWindow); + + ::tools::Rectangle aWin(Point(0,0), pWindow->GetOutputSizePixel()); + + aWin = pWindow->PixelToLogic(aWin); + pOutlinerView->SetOutputArea(aWin); + + ::tools::Rectangle aVis = pOutlinerView->GetVisArea(); + + ::tools::Rectangle aText(Point(0,0), + Size(pOlView->GetPaperWidth(), + pOlView->GetOutliner().GetTextHeight())); + if (aWin.GetHeight() > aText.Bottom()) + aText.SetBottom( aWin.GetHeight() ); + + if (!aWin.IsEmpty()) // not when opening + { + InitWindows(Point(0,0), aText.GetSize(), aVis.TopLeft()); + UpdateScrollBars(); + } +} + +/** + * Handle SfxRequest for the Controller + */ +void OutlineViewShell::ExecCtrl(SfxRequest &rReq) +{ + sal_uInt16 nSlot = rReq.GetSlot(); + switch ( nSlot ) + { + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + ExecReq( rReq ); + break; + } + + case SID_OPT_LOCALE_CHANGED: + { + pOlView->GetOutliner().UpdateFields(); + UpdatePreview( GetActualPage() ); + rReq.Done(); + break; + } + + default: + break; + } +} + +/** + * Activate(): during the first invocation the fields get updated + */ +void OutlineViewShell::Activate( bool bIsMDIActivate ) +{ + if ( ! mbInitialized) + { + mbInitialized = true; + SfxRequest aRequest (SID_EDIT_OUTLINER, SfxCallMode::SLOT, GetDoc()->GetItemPool()); + FuPermanent (aRequest); + } + + ViewShell::Activate( bIsMDIActivate ); + SfxShell::BroadcastContextForActivation(true); + + pOlView->SetLinks(); + pOlView->ConnectToApplication(); + + if( bIsMDIActivate ) + { + OutlinerView* pOutlinerView = pOlView->GetViewByWindow( GetActiveWindow() ); + ::Outliner* pOutl = pOutlinerView->GetOutliner(); + pOutl->UpdateFields(); + } +} + +void OutlineViewShell::Deactivate( bool bIsMDIActivate ) +{ + pOlView->DisconnectFromApplication(); + + // Links must be kept also on deactivated viewshell, to allow drag'n'drop + // to function properly + ViewShell::Deactivate( bIsMDIActivate ); +} + +/** + * Set status of Controller-SfxSlots + */ +void OutlineViewShell::GetCtrlState(SfxItemSet &rSet) +{ + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_HYPERLINK_GETLINK)) + { + SvxHyperlinkItem aHLinkItem; + + OutlinerView* pOLV = pOlView->GetViewByWindow(GetActiveWindow()); + if (pOLV) + { + const SvxFieldItem* pFieldItem = pOLV->GetFieldAtSelection(); + if (pFieldItem) + { + ESelection aSel = pOLV->GetSelection(); + if ( abs( aSel.nEndPos - aSel.nStartPos ) == 1 ) + { + const SvxFieldData* pField = pFieldItem->GetField(); + if ( auto pUrlField = dynamic_cast< const SvxURLField *>( pField ) ) + { + aHLinkItem.SetName(pUrlField->GetRepresentation()); + aHLinkItem.SetURL(pUrlField->GetURL()); + aHLinkItem.SetTargetFrame(pUrlField->GetTargetFrame()); + } + } + } + } + rSet.Put(aHLinkItem); + } + rSet.Put( SfxBoolItem( SID_READONLY_MODE, GetDocSh()->IsReadOnly() ) ); + + if ( SfxItemState::DEFAULT == rSet.GetItemState(SID_MAIL_SCROLLBODY_PAGEDOWN) ) + rSet.Put( SfxBoolItem( SID_MAIL_SCROLLBODY_PAGEDOWN, true ) ); + + if ( !(SfxItemState::DEFAULT == rSet.GetItemState(SID_TRANSLITERATE_HALFWIDTH) || + SfxItemState::DEFAULT == rSet.GetItemState(SID_TRANSLITERATE_FULLWIDTH) || + SfxItemState::DEFAULT == rSet.GetItemState(SID_TRANSLITERATE_HIRAGANA) || + SfxItemState::DEFAULT == rSet.GetItemState(SID_TRANSLITERATE_KATAKANA)) ) + return; + + if( !SvtCJKOptions::IsChangeCaseMapEnabled() ) + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, false ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, false ); + rSet.DisableItem( SID_TRANSLITERATE_HALFWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_FULLWIDTH ); + rSet.DisableItem( SID_TRANSLITERATE_HIRAGANA ); + rSet.DisableItem( SID_TRANSLITERATE_KATAKANA ); + } + else + { + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HALFWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_FULLWIDTH, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_HIRAGANA, true ); + GetViewFrame()->GetBindings().SetVisibleState( SID_TRANSLITERATE_KATAKANA, true ); + } +} + +/** + * SfxRequests for support functions + */ +void OutlineViewShell::FuSupport(SfxRequest &rReq) +{ + if( rReq.GetSlot() == SID_STYLE_FAMILY && rReq.GetArgs()) + GetDocSh()->SetStyleFamily(static_cast(rReq.GetArgs()->Get( SID_STYLE_FAMILY ).GetValue())); + + bool bPreviewState = false; + sal_uLong nSlot = rReq.GetSlot(); + + std::unique_ptr> aGuard; + if( pOlView && ( + (nSlot == SID_TRANSLITERATE_SENTENCE_CASE) || + (nSlot == SID_TRANSLITERATE_TITLE_CASE) || + (nSlot == SID_TRANSLITERATE_TOGGLE_CASE) || + (nSlot == SID_TRANSLITERATE_UPPER) || + (nSlot == SID_TRANSLITERATE_LOWER) || + (nSlot == SID_TRANSLITERATE_HALFWIDTH) || + (nSlot == SID_TRANSLITERATE_FULLWIDTH) || + (nSlot == SID_TRANSLITERATE_HIRAGANA) || + (nSlot == SID_TRANSLITERATE_KATAKANA) || + (nSlot == SID_CUT) || + (nSlot == SID_PASTE) || + (nSlot == SID_PASTE_UNFORMATTED) || + (nSlot == SID_DELETE))) + { + aGuard.reset( new OutlineViewModelChangeGuard( *pOlView ) ); + } + + switch ( nSlot ) + { + case SID_CUT: + { + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoCut(); + } + else if (pOlView) + { + pOlView->DoCut(); + } + rReq.Done(); + bPreviewState = true; + } + break; + + case SID_COPY: + { + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoCopy(); + } + else if (pOlView) + { + pOlView->DoCopy(); + } + rReq.Done(); + bPreviewState = true; + } + break; + + case SID_PASTE: + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoPaste(); + } + else if (pOlView) + { + pOlView->DoPaste(); + } + rReq.Done(); + bPreviewState = true; + } + break; + + case SID_PASTE_UNFORMATTED: + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + + if(HasCurrentFunction()) + { + GetCurrentFunction()->DoPasteUnformatted(); + } + else if(pOlView) + { + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + if (aDataHelper.GetTransferable().is()) + { + sal_Int8 nAction = DND_ACTION_COPY; + pOlView->InsertData( aDataHelper, + GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(), GetActiveWindow()->GetOutputSizePixel() ).Center() ), + nAction, false, SotClipboardFormatId::STRING); + } + } + + rReq.Ignore (); + } + break; + case SID_DELETE: + { + if( pOlView ) + { + OutlinerView* pOutlView = pOlView->GetViewByWindow(GetActiveWindow()); + if (pOutlView) + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + + vcl::KeyCode aKCode(KEY_DELETE); + KeyEvent aKEvt( 0, aKCode ); + pOutlView->PostKeyEvent(aKEvt); + + rtl::Reference xFunc( GetCurrentFunction() ); + FuOutlineText* pFuOutlineText = dynamic_cast< FuOutlineText* >( xFunc.get() ); + if( pFuOutlineText ) + pFuOutlineText->UpdateForKeyPress (aKEvt); + } + } + rReq.Done(); + bPreviewState = true; + } + break; + + case SID_DRAWINGMODE: + case SID_SLIDE_MASTER_MODE: + case SID_NOTES_MODE: + case SID_NOTES_MASTER_MODE: + case SID_HANDOUT_MASTER_MODE: + case SID_SLIDE_SORTER_MODE: + case SID_OUTLINE_MODE: + framework::FrameworkHelper::Instance(GetViewShellBase())->HandleModeChangeSlot( + nSlot, + rReq); + rReq.Done(); + break; + + case SID_RULER: + SetRuler( !HasRuler() ); + Invalidate( SID_RULER ); + rReq.Done(); + break; + + case SID_ZOOM_PREV: + { + if (mpZoomList->IsPreviousPossible()) + { + SetZoomRect(mpZoomList->GetPreviousZoomRect()); + } + rReq.Done (); + } + break; + + case SID_ZOOM_NEXT: + { + if (mpZoomList->IsNextPossible()) + { + SetZoomRect(mpZoomList->GetNextZoomRect()); + } + rReq.Done (); + } + break; + + case SID_AUTOSPELL_CHECK: + { + GetDoc()->SetOnlineSpell(!GetDoc()->GetOnlineSpell()); + rReq.Done (); + } + break; + + case SID_TRANSLITERATE_SENTENCE_CASE: + case SID_TRANSLITERATE_TITLE_CASE: + case SID_TRANSLITERATE_TOGGLE_CASE: + case SID_TRANSLITERATE_UPPER: + case SID_TRANSLITERATE_LOWER: + case SID_TRANSLITERATE_HALFWIDTH: + case SID_TRANSLITERATE_FULLWIDTH: + case SID_TRANSLITERATE_HIRAGANA: + case SID_TRANSLITERATE_KATAKANA: + { + OutlinerView* pOLV = pOlView ? pOlView->GetViewByWindow( GetActiveWindow() ) : nullptr; + if( pOLV ) + { + TransliterationFlags nType = TransliterationFlags::NONE; + + switch( nSlot ) + { + case SID_TRANSLITERATE_SENTENCE_CASE: + nType = TransliterationFlags::SENTENCE_CASE; + break; + case SID_TRANSLITERATE_TITLE_CASE: + nType = TransliterationFlags::TITLE_CASE; + break; + case SID_TRANSLITERATE_TOGGLE_CASE: + nType = TransliterationFlags::TOGGLE_CASE; + break; + case SID_TRANSLITERATE_UPPER: + nType = TransliterationFlags::LOWERCASE_UPPERCASE; + break; + case SID_TRANSLITERATE_LOWER: + nType = TransliterationFlags::UPPERCASE_LOWERCASE; + break; + case SID_TRANSLITERATE_HALFWIDTH: + nType = TransliterationFlags::FULLWIDTH_HALFWIDTH; + break; + case SID_TRANSLITERATE_FULLWIDTH: + nType = TransliterationFlags::HALFWIDTH_FULLWIDTH; + break; + case SID_TRANSLITERATE_HIRAGANA: + nType = TransliterationFlags::KATAKANA_HIRAGANA; + break; + case SID_TRANSLITERATE_KATAKANA: + nType = TransliterationFlags::HIRAGANA_KATAKANA; + break; + } + + pOLV->TransliterateText( nType ); + } + + rReq.Done(); + bPreviewState = true; + } + break; + + // added Undo/Redo handling + case SID_UNDO : + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + ImpSidUndo(rReq); + } + break; + case SID_REDO : + { + OutlineViewPageChangesGuard aGuard2(pOlView.get()); + ImpSidRedo(rReq); + } + break; + + default: + break; + } + + if( bPreviewState ) + Invalidate( SID_PREVIEW_STATE ); + + Invalidate(SID_CUT); + Invalidate(SID_COPY); + Invalidate(SID_PASTE); +} + +/** + * SfxRequests for permanent functions + */ +void OutlineViewShell::FuPermanent(SfxRequest &rReq) +{ + if(HasCurrentFunction()) + { + DeactivateCurrentFunction(true); + } + + switch ( rReq.GetSlot() ) + { + case SID_EDIT_OUTLINER: + { + ::Outliner& rOutl = pOlView->GetOutliner(); + rOutl.GetUndoManager().Clear(); + rOutl.UpdateFields(); + + SetCurrentFunction( FuOutlineText::Create(this,GetActiveWindow(),pOlView.get(),GetDoc(),rReq) ); + + rReq.Done(); + } + break; + + default: + break; + } + + if(HasOldFunction()) + { + GetOldFunction()->Deactivate(); + SetOldFunction(nullptr); + } + + if(HasCurrentFunction()) + { + GetCurrentFunction()->Activate(); + SetOldFunction(GetCurrentFunction()); + } +} + +IMPL_LINK( OutlineViewShell, ClipboardChanged, TransferableDataHelper*, pDataHelper, void ) +{ + bPastePossible = pDataHelper->GetFormatCount() != 0 && + ( pDataHelper->HasFormat( SotClipboardFormatId::STRING ) || + pDataHelper->HasFormat( SotClipboardFormatId::RTF ) || + pDataHelper->HasFormat( SotClipboardFormatId::RICHTEXT ) || + pDataHelper->HasFormat( SotClipboardFormatId::HTML ) ); + + SfxBindings& rBindings = GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_PASTE ); + rBindings.Invalidate( SID_PASTE_SPECIAL ); + rBindings.Invalidate( SID_PASTE_UNFORMATTED ); + rBindings.Invalidate( SID_CLIPBOARD_FORMAT_ITEMS ); +} + +/** + * Set Status (Enabled/Disabled) of Menu-SfxSlots + */ +void OutlineViewShell::GetMenuState( SfxItemSet &rSet ) +{ + ViewShell::GetMenuState(rSet); + + rSet.Put(SfxBoolItem(SID_SLIDE_SORTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_DRAWINGMODE, false)); + rSet.Put(SfxBoolItem(SID_SLIDE_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_OUTLINE_MODE, true)); + rSet.Put(SfxBoolItem(SID_NOTES_MODE, false)); + rSet.Put(SfxBoolItem(SID_NOTES_MASTER_MODE, false)); + rSet.Put(SfxBoolItem(SID_HANDOUT_MASTER_MODE, false)); + + if (!mpZoomList->IsNextPossible()) + { + rSet.DisableItem(SID_ZOOM_NEXT); + } + if (!mpZoomList->IsPreviousPossible()) + { + rSet.DisableItem(SID_ZOOM_PREV); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_IN ) || + SfxItemState::DEFAULT == rSet.GetItemState( SID_ZOOM_OUT ) ) + { + if( GetActiveWindow()->GetZoom() <= GetActiveWindow()->GetMinZoom() || GetDocSh()->IsUIActive() ) + rSet.DisableItem( SID_ZOOM_OUT ); + if( GetActiveWindow()->GetZoom() >= GetActiveWindow()->GetMaxZoom() || GetDocSh()->IsUIActive() ) + rSet.DisableItem( SID_ZOOM_IN ); + } + + ::Outliner& rOutl = pOlView->GetOutliner(); + + // allow 'Select All'? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_SELECTALL ) ) + { + sal_Int32 nParaCount = rOutl.GetParagraphCount(); + bool bDisable = nParaCount == 0; + if (!bDisable && nParaCount == 1) + { + OUString aTest = rOutl.GetText(rOutl.GetParagraph(0)); + if (aTest.isEmpty()) + { + bDisable = true; + } + } + if (bDisable) + rSet.DisableItem(SID_SELECTALL); + } + + // set status of Ruler + rSet.Put( SfxBoolItem( SID_RULER, HasRuler() ) ); + + // Enable formatting? + rSet.Put( SfxBoolItem( SID_OUTLINE_FORMAT, !rOutl.IsFlatMode() ) ); + + if( rOutl.IsFlatMode() ) + rSet.DisableItem( SID_COLORVIEW ); + else + { + // Enable color view? + EEControlBits nCntrl = rOutl.GetControlWord(); + bool bNoColor = false; + if (nCntrl & EEControlBits::NOCOLORS) + bNoColor = true; + + rSet.Put( SfxBoolItem( SID_COLORVIEW, bNoColor ) ); + } + + // Buttons of toolbar + // first the selection dependent ones: COLLAPSE, EXPAND + bool bDisableCollapse = true; + bool bDisableExpand = true; + bool bUnique = true; + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(GetActiveWindow()); + + std::vector aSelList; + pOutlinerView->CreateSelectionList(aSelList); + + if (!aSelList.empty()) + { + sal_Int16 nTmpDepth = rOutl.GetDepth( rOutl.GetAbsPos( aSelList.front() ) ); + bool bPage = ::Outliner::HasParaFlag( aSelList.front(), ParaFlag::ISPAGE ); + + for (const Paragraph* pPara : aSelList) + { + sal_Int16 nDepth = rOutl.GetDepth( rOutl.GetAbsPos( pPara ) ); + + if( nDepth != nTmpDepth || bPage != ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE )) + bUnique = false; + + if (rOutl.HasChildren(pPara)) + { + if (!rOutl.IsExpanded(pPara)) + bDisableExpand = false; + else + bDisableCollapse = false; + } + } + } + + if (bDisableExpand) + rSet.DisableItem(SID_OUTLINE_EXPAND); + if (bDisableCollapse) + rSet.DisableItem(SID_OUTLINE_COLLAPSE); + + // does the selection provide a unique presentation layout? + // if not, the templates must not be edited + SfxItemSetFixed aSet(*rSet.GetPool()); + GetStatusBarState(aSet); + OUString aTest = static_cast(aSet.Get(SID_STATUS_LAYOUT)).GetValue(); + if (aTest.isEmpty()) + { + bUnique = false; + } + + if (!bUnique) + rSet.DisableItem( SID_PRESENTATIONOBJECT ); + + // now the selection independent ones: COLLAPSE_ALL, EXPAND_ALL + bool bDisableCollapseAll = true; + bool bDisableExpandAll = true; + + // does the selection contain something collapsible/expandable? + if (!bDisableCollapse) + bDisableCollapseAll = false; + if (!bDisableExpand) + bDisableExpandAll = false; + + // otherwise look through all paragraphs + if (bDisableCollapseAll || bDisableExpandAll) + { + sal_Int32 nParaPos = 0; + Paragraph* pPara = rOutl.GetParagraph( nParaPos ); + while (pPara && (bDisableCollapseAll || bDisableExpandAll)) + { + if (!rOutl.IsExpanded(pPara) && rOutl.HasChildren(pPara)) + bDisableExpandAll = false; + + if (rOutl.IsExpanded(pPara) && rOutl.HasChildren(pPara)) + bDisableCollapseAll = false; + + pPara = rOutl.GetParagraph( ++nParaPos ); + } + } + + if (bDisableExpandAll) + rSet.DisableItem(SID_OUTLINE_EXPAND_ALL); + if (bDisableCollapseAll) + rSet.DisableItem(SID_OUTLINE_COLLAPSE_ALL); + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PASTE ) ) + { + if ( !mxClipEvtLstnr.is() ) + { + // create listener + mxClipEvtLstnr = new TransferableClipboardListener( LINK( this, OutlineViewShell, ClipboardChanged ) ); + mxClipEvtLstnr->AddListener( GetActiveWindow() ); + + // get initial state + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( GetActiveWindow() ) ); + bPastePossible = ( aDataHelper.GetFormatCount() != 0 && + ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) || + aDataHelper.HasFormat( SotClipboardFormatId::RTF ) || + aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) || + aDataHelper.HasFormat( SotClipboardFormatId::HTML ) ) ); + } + + if( !bPastePossible ) + { + rSet.DisableItem( SID_PASTE ); + } + } + + if (!pOlView->GetViewByWindow(GetActiveWindow())->HasSelection() + || GetObjectShell()->isContentExtractionLocked()) + { + rSet.DisableItem(SID_CUT); + rSet.DisableItem(SID_COPY); + } + + if (pOlView->GetOutliner().IsModified()) + { + GetDoc()->SetChanged(); + } + + // the status has to be set here because of overriding + if( !GetDocSh()->IsModified() ) + { + rSet.DisableItem( SID_SAVEDOC ); + } + + if ( GetDocSh()->IsReadOnly() ) + { + rSet.DisableItem( SID_AUTOSPELL_CHECK ); + } + else + { + if (GetDoc()->GetOnlineSpell()) + { + rSet.Put(SfxBoolItem(SID_AUTOSPELL_CHECK, true)); + } + else + { + rSet.Put(SfxBoolItem(SID_AUTOSPELL_CHECK, false)); + } + } + + // field commands + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_MODIFY_FIELD ) ) + { + const SvxFieldItem* pFldItem = pOutlinerView->GetFieldAtSelection(); + + if( !( pFldItem && (nullptr != dynamic_cast< const SvxDateField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxAuthorField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtFileField *>( pFldItem->GetField() ) || + nullptr != dynamic_cast< const SvxExtTimeField *>( pFldItem->GetField() ) ) ) ) + { + rSet.DisableItem( SID_MODIFY_FIELD ); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_EXPAND_PAGE)) + { + bool bDisable = true; + sal_uInt16 i = 0; + sal_uInt16 nCount = GetDoc()->GetSdPageCount(PageKind::Standard); + pOlView->SetSelectedPages(); + + while (i < nCount && bDisable) + { + SdPage* pPage = GetDoc()->GetSdPage(i, PageKind::Standard); + + if (pPage->IsSelected()) + { + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Outline); + + if (pObj!=nullptr ) + { + if( !pObj->IsEmptyPresObj() ) + { + bDisable = false; + } + else + { + // check if the object is in edit, then if it's temporarily not empty + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObj ); + if( pTextObj ) + { + if( pTextObj->CanCreateEditOutlinerParaObject() ) + { + bDisable = false; + } + } + } + } + } + + i++; + } + + if (bDisable) + { + rSet.DisableItem(SID_EXPAND_PAGE); + } + } + + if (SfxItemState::DEFAULT == rSet.GetItemState(SID_SUMMARY_PAGE)) + { + bool bDisable = true; + sal_uInt16 i = 0; + sal_uInt16 nCount = GetDoc()->GetSdPageCount(PageKind::Standard); + pOlView->SetSelectedPages(); + + while (i < nCount && bDisable) + { + SdPage* pPage = GetDoc()->GetSdPage(i, PageKind::Standard); + + if (pPage->IsSelected()) + { + SdrObject* pObj = pPage->GetPresObj(PresObjKind::Title); + + if (pObj && !pObj->IsEmptyPresObj()) + { + bDisable = false; + } + } + + i++; + } + + if (bDisable) + { + rSet.DisableItem(SID_SUMMARY_PAGE); + } + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_THESAURUS ) ) + { + if ( !pOlView->IsTextEdit() ) + { + rSet.DisableItem( SID_THESAURUS ); + } + else + { + LanguageType eLang = GetDoc()->GetLanguage( EE_CHAR_LANGUAGE ); + Reference< XThesaurus > xThesaurus( LinguMgr::GetThesaurus() ); + + if (!xThesaurus.is() || eLang == LANGUAGE_NONE || !xThesaurus->hasLocale( LanguageTag::convertToLocale( eLang))) + rSet.DisableItem( SID_THESAURUS ); + } + } + + // is starting the presentation possible? + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_PRESENTATION ) ) + { + bool bDisable = true; + sal_uInt16 nCount = GetDoc()->GetSdPageCount( PageKind::Standard ); + + for( sal_uInt16 i = 0; i < nCount && bDisable; i++ ) + { + SdPage* pPage = GetDoc()->GetSdPage(i, PageKind::Standard); + + if( !pPage->IsExcluded() ) + bDisable = false; + } + if( bDisable || GetDocSh()->IsPreview()) + { + rSet.DisableItem( SID_PRESENTATION ); + } + } + + FuBullet::GetSlotState( rSet, this, GetViewFrame() ); + +} + +/** + * gets invoked when ScrollBar is used + */ +void OutlineViewShell::VirtHScrollHdl(ScrollBar* pHScroll) +{ + ::tools::Long nThumb = pHScroll->GetThumbPos(); + ::tools::Long nRange = pHScroll->GetRange().Len(); + double fX = static_cast(nThumb) / nRange; + + Window* pWin = mpContentWindow.get(); + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWin); + ::tools::Long nViewWidth = pWin->PixelToLogic( + pWin->GetSizePixel()).Width(); + ::tools::Long nTextWidth = pOlView->GetPaperWidth(); + nViewWidth = std::max(nViewWidth, nTextWidth); + ::tools::Long nCurrentPos = pOutlinerView->GetVisArea().Left(); + ::tools::Long nTargetPos = static_cast<::tools::Long>(fX * nViewWidth); + ::tools::Long nDelta = nTargetPos - nCurrentPos; + + pOutlinerView->HideCursor(); + pOutlinerView->Scroll(-nDelta, 0); + pOutlinerView->ShowCursor(false); +} + +void OutlineViewShell::VirtVScrollHdl(ScrollBar* pVScroll) +{ + ::tools::Long nThumb = pVScroll->GetThumbPos(); + ::tools::Long nRange = pVScroll->GetRange().Len(); + double fY = static_cast(nThumb) / nRange; + + Window* pWin = mpContentWindow.get(); + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWin); + ::tools::Long nViewHeight = pWin->PixelToLogic( + pWin->GetSizePixel()).Height(); + ::tools::Long nTextHeight = pOlView->GetOutliner().GetTextHeight(); + nViewHeight += nTextHeight; + ::tools::Long nCurrentPos = pOutlinerView->GetVisArea().Top(); + ::tools::Long nTargetPos = static_cast<::tools::Long>(fY * nViewHeight); + ::tools::Long nDelta = nTargetPos - nCurrentPos; + + pOutlinerView->HideCursor(); + pOutlinerView->Scroll(0, -nDelta); + pOutlinerView->ShowCursor(false); +} + +/** + * PrepareClose, gets called when the Shell shall be destroyed. + * Forwards the invocation to the View + */ +bool OutlineViewShell::PrepareClose( bool bUI ) +{ + if( !ViewShell::PrepareClose(bUI) ) + return false; + + if (pOlView) + pOlView->PrepareClose(); + return true; +} + +/** + * Zoom with zoom factor. Inform OutlinerView + */ +void OutlineViewShell::SetZoom(::tools::Long nZoom) +{ + ViewShell::SetZoom(nZoom); + + ::sd::Window* pWindow = mpContentWindow.get(); + if (pWindow) + { + // change OutputArea of OutlinerView + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWindow); + ::tools::Rectangle aWin(Point(0,0), pWindow->GetOutputSizePixel()); + aWin = pWindow->PixelToLogic(aWin); + pOutlinerView->SetOutputArea(aWin); + } + + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); +} + +/** + * Zoom with zoom rectangle. Inform OutlinerView + */ +void OutlineViewShell::SetZoomRect(const ::tools::Rectangle& rZoomRect) +{ + ViewShell::SetZoomRect(rZoomRect); + + ::sd::Window* pWindow = mpContentWindow.get(); + if (pWindow) + { + // change OutputArea of OutlinerView + OutlinerView* pOutlinerView = pOlView->GetViewByWindow(pWindow); + ::tools::Rectangle aWin(Point(0,0), pWindow->GetOutputSizePixel()); + aWin = pWindow->PixelToLogic(aWin); + pOutlinerView->SetOutputArea(aWin); + } + + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOM ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); +} + +/** + * Before saving: Update Model of the Drawing Engine, then forward the + * invocation to the ObjectShell. + */ +void OutlineViewShell::Execute(SfxRequest& rReq) +{ + bool bForwardCall = true; + + switch(rReq.GetSlot()) + { + case SID_SAVEDOC: + case SID_SAVEASDOC: + PrepareClose(); + break; + + case SID_SEARCH_ITEM: + // Forward this request to the common (old) code of the + // document shell. + GetDocSh()->Execute (rReq); + bForwardCall = false; + break; + + case SID_SPELL_DIALOG: + { + SfxViewFrame* pViewFrame = GetViewFrame(); + if (rReq.GetArgs() != nullptr) + pViewFrame->SetChildWindow (SID_SPELL_DIALOG, + static_cast(rReq.GetArgs()-> + Get(SID_SPELL_DIALOG)).GetValue()); + else + pViewFrame->ToggleChildWindow(SID_SPELL_DIALOG); + + pViewFrame->GetBindings().Invalidate(SID_SPELL_DIALOG); + rReq.Done (); + + bForwardCall = false; + } + break; + + default: + SAL_WARN("sd", "OutlineViewShell::Execute(): can not handle slot " << rReq.GetSlot()); + break; + + } + + if (bForwardCall) + static_cast(GetViewFrame()->GetObjectShell())->ExecuteSlot( rReq ); +} + +/** + * Read FrameViews data and set actual views data + */ +void OutlineViewShell::ReadFrameViewData(FrameView* pView) +{ + ::Outliner& rOutl = pOlView->GetOutliner(); + + rOutl.SetFlatMode( pView->IsNoAttribs() ); + + EEControlBits nCntrl = rOutl.GetControlWord(); + + if ( pView->IsNoColors() ) + rOutl.SetControlWord(nCntrl | EEControlBits::NOCOLORS); + else + rOutl.SetControlWord(nCntrl & ~EEControlBits::NOCOLORS); + + sal_uInt16 nPage = mpFrameView->GetSelectedPage(); + pLastPage = GetDoc()->GetSdPage( nPage, PageKind::Standard ); + pOlView->SetActualPage(pLastPage); +} + +/** + * Write actual views data to FrameView + */ +void OutlineViewShell::WriteFrameViewData() +{ + ::Outliner& rOutl = pOlView->GetOutliner(); + + EEControlBits nCntrl = rOutl.GetControlWord(); + bool bNoColor = false; + if (nCntrl & EEControlBits::NOCOLORS) + bNoColor = true; + mpFrameView->SetNoColors(bNoColor); + mpFrameView->SetNoAttribs( rOutl.IsFlatMode() ); + SdPage* pActualPage = pOlView->GetActualPage(); + DBG_ASSERT(pActualPage, "No current page"); + if( pActualPage ) + mpFrameView->SetSelectedPage((pActualPage->GetPageNum() - 1) / 2); +} + +/** + * Handle SfxRequests for the StatusBar + */ +void OutlineViewShell::ExecStatusBar(SfxRequest&) +{ +} + +void OutlineViewShell::GetStatusBarState(SfxItemSet& rSet) +{ + // Zoom-Item + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOM ) ) + { + sal_uInt16 nZoom = static_cast(GetActiveWindow()->GetZoom()); + + std::unique_ptr pZoomItem(new SvxZoomItem( SvxZoomType::PERCENT, nZoom )); + + // limit area + SvxZoomEnableFlags nZoomValues = SvxZoomEnableFlags::ALL; + nZoomValues &= ~SvxZoomEnableFlags::OPTIMAL; + nZoomValues &= ~SvxZoomEnableFlags::WHOLEPAGE; + nZoomValues &= ~SvxZoomEnableFlags::PAGEWIDTH; + + pZoomItem->SetValueSet( nZoomValues ); + rSet.Put( std::move(pZoomItem) ); + } + + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_ATTR_ZOOMSLIDER ) ) + { + if (GetDocSh()->IsUIActive() || !GetActiveWindow() ) + { + rSet.DisableItem( SID_ATTR_ZOOMSLIDER ); + } + else + { + sd::Window * pActiveWindow = GetActiveWindow(); + SvxZoomSliderItem aZoomItem( static_cast(pActiveWindow->GetZoom()), static_cast(pActiveWindow->GetMinZoom()), static_cast(pActiveWindow->GetMaxZoom()) ) ; + aZoomItem.AddSnappingPoint(100); + rSet.Put( aZoomItem ); + } + } + + // page view and layout + + sal_uInt16 nPageCount = GetDoc()->GetSdPageCount( PageKind::Standard ); + OUString aPageStr, aLayoutStr; + + ::sd::Window* pWin = GetActiveWindow(); + OutlinerView* pActiveView = pOlView->GetViewByWindow( pWin ); + + std::vector aSelList; + pActiveView->CreateSelectionList(aSelList); + + Paragraph *pFirstPara = nullptr; + Paragraph *pLastPara = nullptr; + + if (!aSelList.empty()) + { + pFirstPara = *(aSelList.begin()); + pLastPara = *(aSelList.rbegin()); + } + + if( !::Outliner::HasParaFlag(pFirstPara,ParaFlag::ISPAGE) ) + pFirstPara = pOlView->GetPrevTitle( pFirstPara ); + + if( !::Outliner::HasParaFlag(pLastPara, ParaFlag::ISPAGE) ) + pLastPara = pOlView->GetPrevTitle( pLastPara ); + + // only one page selected? + if( pFirstPara == pLastPara ) + { + // how many pages are we before the selected page? + sal_uLong nPos = 0; + while( pFirstPara ) + { + pFirstPara = pOlView->GetPrevTitle( pFirstPara ); + if( pFirstPara ) + nPos++; + } + + if( nPos >= GetDoc()->GetSdPageCount( PageKind::Standard ) ) + nPos = 0; + + SdrPage* pPage = GetDoc()->GetSdPage( static_cast(nPos), PageKind::Standard ); + + aPageStr = SdResId(STR_SD_PAGE_COUNT); + + aPageStr = aPageStr.replaceFirst("%1", OUString::number(static_cast(nPos + 1))); + aPageStr = aPageStr.replaceFirst("%2", OUString::number(nPageCount)); + + aLayoutStr = pPage->GetLayoutName(); + sal_Int32 nIndex = aLayoutStr.indexOf(SD_LT_SEPARATOR); + if (nIndex != -1) + aLayoutStr = aLayoutStr.copy(0, nIndex); + //Now, CurrentPage property change is already sent for DrawView and OutlineView, so it is not necessary to send again here + if(m_StrOldPageName!=aPageStr) + { + GetViewShellBase().GetDrawController().fireSwitchCurrentPage(nPos); + m_StrOldPageName = aPageStr; + } + } + rSet.Put( SfxStringItem( SID_STATUS_PAGE, aPageStr ) ); + rSet.Put( SfxStringItem( SID_STATUS_LAYOUT, aLayoutStr ) ); +} + +void OutlineViewShell::Command( const CommandEvent& rCEvt, ::sd::Window* pWin ) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + GetActiveWindow()->ReleaseMouse(); + + OutlinerView* pOLV = pOlView->GetViewByWindow(GetActiveWindow()); + Point aPos(rCEvt.GetMousePosPixel()); + + if (pOLV && pOLV->IsWrongSpelledWordAtPos(aPos)) + { + // Popup for Online-Spelling now handled by DrawDocShell + Link aLink = LINK(GetDocSh(), DrawDocShell, OnlineSpellCallback); + + pOLV->ExecuteSpellPopup(aPos, aLink); + pOLV->GetEditView().Invalidate(); + } + else + { + GetViewFrame()->GetDispatcher()->ExecutePopup("outline"); + } + } + else + { + ViewShell::Command( rCEvt, pWin ); + + // if necessary communicate the new context to the Preview + Invalidate( SID_PREVIEW_STATE ); + + } +} + +bool OutlineViewShell::KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin) +{ + bool bReturn = false; + OutlineViewPageChangesGuard aGuard(pOlView.get()); + + if (pWin == nullptr && HasCurrentFunction()) + { + bReturn = GetCurrentFunction()->KeyInput(rKEvt); + } + + // no, forward to base class + else + { + bReturn = ViewShell::KeyInput(rKEvt, pWin); + } + + Invalidate(SID_STYLE_EDIT); + Invalidate(SID_STYLE_NEW); + Invalidate(SID_STYLE_DELETE); + Invalidate(SID_STYLE_HIDE); + Invalidate(SID_STYLE_SHOW); + Invalidate(SID_STYLE_UPDATE_BY_EXAMPLE); + Invalidate(SID_STYLE_NEW_BY_EXAMPLE); + Invalidate(SID_STYLE_WATERCAN); + Invalidate(SID_STYLE_FAMILY5); + + // check and distinguish cursor movements- or input-keys + vcl::KeyCode aKeyGroup( rKEvt.GetKeyCode().GetGroup() ); + if( (aKeyGroup != KEYGROUP_CURSOR && aKeyGroup != KEYGROUP_FKEYS) || + (GetActualPage() != pLastPage) ) + { + Invalidate( SID_PREVIEW_STATE ); + } + + return bReturn; +} + +/** + * Status of Attribute-Items + */ +void OutlineViewShell::GetAttrState( SfxItemSet& rSet ) +{ + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + SfxAllItemSet aAllSet( *rSet.GetPool() ); + + while ( nWhich ) + { + sal_uInt16 nSlotId = SfxItemPool::IsWhich(nWhich) + ? GetPool().GetSlotId(nWhich) + : nWhich; + + switch ( nSlotId ) + { + case SID_STYLE_FAMILY2: + case SID_STYLE_FAMILY3: + { + rSet.DisableItem( nWhich ); + } + break; + + case SID_STYLE_FAMILY5: + { + SfxStyleSheet* pStyleSheet = pOlView->GetViewByWindow(GetActiveWindow())->GetStyleSheet(); + + if( pStyleSheet ) + { + pStyleSheet = static_cast(pStyleSheet)->GetPseudoStyleSheet(); + + if (pStyleSheet) + { + SfxTemplateItem aItem( nWhich, pStyleSheet->GetName() ); + aAllSet.Put( aItem, aItem.Which() ); + } + } + + if( !pStyleSheet ) + { + SfxTemplateItem aItem( nWhich, OUString() ); + aAllSet.Put( aItem, aItem.Which() ); + // rSet.DisableItem( nWhich ); + } + } + break; + + case SID_STYLE_EDIT: + { + std::unique_ptr pFamilyItem; + GetViewFrame()->GetBindings().QueryState(SID_STYLE_FAMILY, pFamilyItem); + if (pFamilyItem && static_cast(pFamilyItem->GetValue()) == SfxStyleFamily::Pseudo) + { + SfxItemSetFixed aSet(*rSet.GetPool()); + GetStatusBarState(aSet); + OUString aRealStyle = static_cast(aSet.Get(SID_STATUS_LAYOUT)).GetValue(); + if (aRealStyle.isEmpty()) + { + // no unique layout name found + rSet.DisableItem(nWhich); + } + } + } + break; + + case SID_STYLE_UPDATE_BY_EXAMPLE: + { + ::sd::Window* pActWin = GetActiveWindow(); + OutlinerView* pOV = pOlView->GetViewByWindow(pActWin); + ESelection aESel(pOV->GetSelection()); + + if (aESel.nStartPara != aESel.nEndPara || + aESel.nStartPos != aESel.nEndPos) + // spanned selection, i.e. StyleSheet and/or + // attribution not necessarily unique + rSet.DisableItem(nWhich); + } + break; + + case SID_STYLE_NEW: + case SID_STYLE_DELETE: + case SID_STYLE_HIDE: + case SID_STYLE_SHOW: + case SID_STYLE_NEW_BY_EXAMPLE: + case SID_STYLE_WATERCAN: + { + rSet.DisableItem(nWhich); + } + break; + } + + nWhich = aIter.NextWhich(); + } + + rSet.Put( aAllSet, false ); +} + +void OutlineViewShell::MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + // first the base classes + ViewShell::MouseButtonUp(rMEvt, pWin); + + Invalidate(SID_STYLE_EDIT); + Invalidate(SID_STYLE_NEW); + Invalidate(SID_STYLE_DELETE); + Invalidate(SID_STYLE_HIDE); + Invalidate(SID_STYLE_SHOW); + Invalidate(SID_STYLE_UPDATE_BY_EXAMPLE); + Invalidate(SID_STYLE_NEW_BY_EXAMPLE); + Invalidate(SID_STYLE_WATERCAN); + Invalidate(SID_STYLE_FAMILY5); + + // if necessary communicate the new context to the Preview + if( GetActualPage() != pLastPage ) + Invalidate( SID_PREVIEW_STATE ); +} + +SdPage* OutlineViewShell::getCurrentPage() const +{ + // since there are no master pages in outline view, we can + // for now use the GetActualPage method + return const_cast(this)->GetActualPage(); +} + +/** + * Returns the first selected page. + * If nothing is selected, the first page is returned. + */ +SdPage* OutlineViewShell::GetActualPage() +{ + return pOlView->GetActualPage(); +} + +void OutlineViewShell::UpdatePreview( SdPage* pPage ) +{ + const bool bNewPage = pPage != pLastPage; + pLastPage = pPage; + if (bNewPage) + { + OutlineViewPageChangesGuard aGuard(pOlView.get()); + SetCurrentPage(pPage); + } +} + +void OutlineViewShell::UpdateTitleObject( SdPage* pPage, Paragraph const * pPara ) +{ + DBG_ASSERT( pPage, "sd::OutlineViewShell::UpdateTitleObject(), pPage == 0?" ); + DBG_ASSERT( pPara, "sd::OutlineViewShell::UpdateTitleObject(), pPara == 0?" ); + + if( !pPage || !pPara ) + return; + + ::Outliner& rOutliner = pOlView->GetOutliner(); + SdrTextObj* pTO = OutlineView::GetTitleTextObject( pPage ); + + OUString aTest = rOutliner.GetText(pPara); + bool bText = !aTest.isEmpty(); + + if( bText ) + { + bool bNewObject = false; + // create a title object if we don't have one but have text + if( !pTO ) + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateTitleObject(), no undo for model change!?" ); + pTO = OutlineView::CreateTitleTextObject(pPage); + bNewObject = true; + } + + // if we have a title object and a text, set the text + std::optional pOPO; + if (pTO) + pOPO = rOutliner.CreateParaObject(rOutliner.GetAbsPos(pPara), 1); + if (pOPO) + { + pOPO->SetOutlinerMode( OutlinerMode::TitleObject ); + assert(pTO); + pOPO->SetVertical( pTO->IsVerticalWriting() ); + if( pTO->GetOutlinerParaObject() && (pOPO->GetTextObject() == pTO->GetOutlinerParaObject()->GetTextObject()) ) + { + // do nothing, same text already set + } + else + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateTitleObject(), no undo for model change!?" ); + if( !bNewObject && pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoObjectSetText(*pTO,0)); + + pTO->SetOutlinerParaObject( std::move(pOPO) ); + pTO->SetEmptyPresObj( false ); + pTO->ActionChanged(); + } + } + } + else if( pTO ) + { + // no text but object available? + // outline object available, but we have no text + if(pPage->IsPresObj(pTO)) + { + // if it is not already empty + if( !pTO->IsEmptyPresObj() ) + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateTitleObject(), no undo for model change!?" ); + + // make it empty + if( pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoObjectSetText(*pTO,0)); + pPage->RestoreDefaultText( pTO ); + pTO->SetEmptyPresObj(true); + pTO->ActionChanged(); + } + } + else + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateTitleObject(), no undo for model change!?" ); + // outline object is not part of the layout, delete it + if( pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoRemoveObject(*pTO)); + pPage->RemoveObject(pTO->GetOrdNum()); + } + } +} + +void OutlineViewShell::UpdateOutlineObject( SdPage* pPage, Paragraph* pPara ) +{ + DBG_ASSERT( pPage, "sd::OutlineViewShell::UpdateOutlineObject(), pPage == 0?" ); + DBG_ASSERT( pPara, "sd::OutlineViewShell::UpdateOutlineObject(), pPara == 0?" ); + + if( !pPage || !pPara ) + return; + + ::Outliner& rOutliner = pOlView->GetOutliner(); + std::optional pOPO; + SdrTextObj* pTO = nullptr; + + OutlinerMode eOutlinerMode = OutlinerMode::TitleObject; + pTO = static_cast(pPage->GetPresObj( PresObjKind::Text )); + if( !pTO ) + { + eOutlinerMode = OutlinerMode::OutlineObject; + pTO = OutlineView::GetOutlineTextObject( pPage ); + } + + // how many paragraphs in the outline? + sal_Int32 nTitlePara = rOutliner.GetAbsPos( pPara ); + sal_Int32 nPara = nTitlePara + 1; + sal_Int32 nParasInLayout = 0; + pPara = rOutliner.GetParagraph( nPara ); + while( pPara && !::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) + { + nParasInLayout++; + pPara = rOutliner.GetParagraph( ++nPara ); + } + if( nParasInLayout ) + { + // create an OutlinerParaObject + pOPO = rOutliner.CreateParaObject( nTitlePara + 1, nParasInLayout ); + } + + if( pOPO ) + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateOutlineObject(), no undo for model change!?" ); + bool bNewObject = false; + + // do we need an outline text object? + if( !pTO ) + { + pTO = OutlineView::CreateOutlineTextObject( pPage ); + bNewObject = true; + } + + // page object, outline text in Outliner: + // apply text + if( pTO ) + { + pOPO->SetVertical( pTO->IsVerticalWriting() ); + pOPO->SetOutlinerMode( eOutlinerMode ); + if( pTO->GetOutlinerParaObject() && (pOPO->GetTextObject() == pTO->GetOutlinerParaObject()->GetTextObject()) ) + { + // do nothing, same text already set + } + else + { + if( !bNewObject && pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoObjectSetText(*pTO,0)); + + pTO->SetOutlinerParaObject( std::move(pOPO) ); + pTO->SetEmptyPresObj( false ); + pTO->ActionChanged(); + } + } + } + else if( pTO ) + { + // page object but no outline text: + // if the object is in the outline of the page -> default text + + // otherwise delete object + if( pPage->IsPresObj(pTO) ) + { + if( !pTO->IsEmptyPresObj() ) + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateOutlineObject(), no undo for model change!?" ); + + // delete old OutlinerParaObject, too + if( pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoObjectSetText(*pTO,0)); + pPage->RestoreDefaultText( pTO ); + pTO->SetEmptyPresObj(true); + pTO->ActionChanged(); + } + } + else + { + DBG_ASSERT( pOlView->isRecordingUndo(), "sd::OutlineViewShell::UpdateOutlineObject(), no undo for model change!?" ); + if( pOlView->isRecordingUndo() ) + pOlView->AddUndo(GetDoc()->GetSdrUndoFactory().CreateUndoRemoveObject(*pTO)); + pPage->RemoveObject(pTO->GetOrdNum()); + } + } +} + +/** + * Fill Outliner from Stream + */ +ErrCode OutlineViewShell::ReadRtf(SvStream& rInput) +{ + ErrCode bRet = ERRCODE_NONE; + + ::Outliner& rOutl = pOlView->GetOutliner(); + + OutlineViewPageChangesGuard aGuard( pOlView.get() ); + OutlineViewModelChangeGuard aGuard2( *pOlView ); + + bRet = rOutl.Read( rInput, OUString(), EETextFormat::Rtf, GetDocSh()->GetHeaderAttributes() ); + + SdPage* pPage = GetDoc()->GetSdPage( GetDoc()->GetSdPageCount(PageKind::Standard) - 1, PageKind::Standard ); + SfxStyleSheet* pTitleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + SfxStyleSheet* pOutlSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + + sal_Int32 nParaCount = rOutl.GetParagraphCount(); + if ( nParaCount > 0 ) + { + for ( sal_Int32 nPara = 0; nPara < nParaCount; nPara++ ) + { + pOlView->UpdateParagraph( nPara ); + + sal_Int16 nDepth = rOutl.GetDepth( nPara ); + + if( (nDepth == 0) || !nPara ) + { + Paragraph* pPara = rOutl.GetParagraph( nPara ); + rOutl.SetDepth(pPara, -1); + rOutl.SetParaFlag(pPara, ParaFlag::ISPAGE); + + rOutl.SetStyleSheet( nPara, pTitleSheet ); + + if( nPara ) // first slide already exists + pOlView->InsertSlideForParagraph( pPara ); + } + else + { + rOutl.SetDepth( rOutl.GetParagraph( nPara ), nDepth - 1 ); + OUString aStyleSheetName = pOutlSheet->GetName(); + if (!aStyleSheetName.isEmpty()) + aStyleSheetName = aStyleSheetName.copy(0, aStyleSheetName.getLength() - 1); + aStyleSheetName += OUString::number( nDepth ); + SfxStyleSheetBasePool* pStylePool = GetDoc()->GetStyleSheetPool(); + SfxStyleSheet* pStyle = static_cast( pStylePool->Find( aStyleSheetName, pOutlSheet->GetFamily() ) ); + DBG_ASSERT( pStyle, "AutoStyleSheetName - Style not found!" ); + if ( pStyle ) + rOutl.SetStyleSheet( nPara, pStyle ); + } + } + } + + rOutl.GetUndoManager().Clear(); + + return bRet; +} + +void OutlineViewShell::WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + WriteFrameViewData(); + + ViewShell::WriteUserDataSequence( rSequence ); +} + +void OutlineViewShell::ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + WriteFrameViewData(); + + ViewShell::ReadUserDataSequence( rSequence ); + + ReadFrameViewData( mpFrameView ); +} + +void OutlineViewShell::VisAreaChanged(const ::tools::Rectangle& rRect) +{ + ViewShell::VisAreaChanged( rRect ); + + GetViewShellBase().GetDrawController().FireVisAreaChanged(rRect); +} + +/** If there is a valid controller then create a new instance of + AccessibleDrawDocumentView. Otherwise return an empty + reference. +*/ +css::uno::Reference + OutlineViewShell::CreateAccessibleDocumentView (::sd::Window* pWindow) +{ + OSL_ASSERT (GetViewShell()!=nullptr); + if (GetViewShell()->GetController() != nullptr) + { + rtl::Reference<::accessibility::AccessibleOutlineView> pDocumentView = + new ::accessibility::AccessibleOutlineView ( + pWindow, + this, + GetViewShell()->GetController(), + pWindow->GetAccessibleParentWindow()->GetAccessible()); + pDocumentView->Init(); + return pDocumentView; + } + + SAL_WARN("sd", "OutlineViewShell::CreateAccessibleDocumentView: no controller"); + return css::uno::Reference< css::accessibility::XAccessible >(); +} + +void OutlineViewShell::GetState (SfxItemSet& rSet) +{ + // Iterate over all requested items in the set. + SfxWhichIter aIter( rSet ); + sal_uInt16 nWhich = aIter.FirstWhich(); + while (nWhich) + { + switch (nWhich) + { + case SID_SEARCH_ITEM: + case SID_SEARCH_OPTIONS: + // Call common (old) implementation in the document shell. + GetDocSh()->GetState (rSet); + break; + default: + SAL_WARN("sd", "OutlineViewShell::GetState(): can not handle which id " << nWhich); + break; + } + nWhich = aIter.NextWhich(); + } +} + +void OutlineViewShell::SetCurrentPage (SdPage* pPage) +{ + // Adapt the selection of the model. + for (sal_uInt16 i=0; iGetSdPageCount(PageKind::Standard); i++) + GetDoc()->SetSelected( + GetDoc()->GetSdPage(i, PageKind::Standard), + false); + GetDoc()->SetSelected (pPage, true); + + DrawController& rController(GetViewShellBase().GetDrawController()); + rController.FireSelectionChangeListener(); + rController.FireSwitchCurrentPage (pPage); + + pOlView->SetActualPage(pPage); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/outlview.cxx b/sd/source/ui/view/outlview.cxx new file mode 100644 index 000000000..c3b7a57ca --- /dev/null +++ b/sd/source/ui/view/outlview.cxx @@ -0,0 +1,1720 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::frame; + +namespace sd { + +// a progress bar gets displayed when more than +// PROCESS_WITH_PROGRESS_THRESHOLD pages are concerned +#define PROCESS_WITH_PROGRESS_THRESHOLD 5 + +OutlineView::OutlineView( DrawDocShell& rDocSh, vcl::Window* pWindow, OutlineViewShell& rOutlineViewShell) +: ::sd::View(*rDocSh.GetDoc(), pWindow->GetOutDev(), &rOutlineViewShell) +, mrOutlineViewShell(rOutlineViewShell) +, mrOutliner(*mrDoc.GetOutliner()) +, mnPagesToProcess(0) +, mnPagesProcessed(0) +, mbFirstPaint(true) +, maDocColor( COL_WHITE ) +, maLRSpaceItem( 0, 0, 2000, 0, EE_PARA_OUTLLRSPACE ) +{ + bool bInitOutliner = false; + + if (mrOutliner.GetViewCount() == 0) + { + // initialize Outliner: set Reference Device + bInitOutliner = true; + mrOutliner.Init( OutlinerMode::OutlineView ); + mrOutliner.SetRefDevice( SD_MOD()->GetVirtualRefDevice() ); + //viewsize without the width of the image and number in front + mnPaperWidth = (mrOutlineViewShell.GetActiveWindow()->GetViewSize().Width() - 4000); + mrOutliner.SetPaperSize(Size(mnPaperWidth, 400000000)); + } + else + { + // width: DIN A4, two margins at 1 cm each + mnPaperWidth = 19000; + } + + mpOutlinerViews[0].reset( new OutlinerView(&mrOutliner, pWindow) ); + mpOutlinerViews[0]->SetOutputArea(::tools::Rectangle()); + mrOutliner.SetUpdateLayout(false); + mrOutliner.InsertView(mpOutlinerViews[0].get(), EE_APPEND); + + onUpdateStyleSettings( true ); + + if (bInitOutliner) + { + // fill Outliner with contents + FillOutliner(); + } + + Link aLink( LINK(this,OutlineView,EventMultiplexerListener) ); + mrOutlineViewShell.GetViewShellBase().GetEventMultiplexer()->AddEventListener(aLink); + + Reference xFrame = mrOutlineViewShell.GetViewShellBase().GetFrame()->GetFrame().GetFrameInterface(); + maSlideImage = vcl::CommandInfoProvider::GetImageForCommand(".uno:ShowSlide", xFrame, vcl::ImageType::Size26); + + // Tell undo manager of the document about the undo manager of the + // outliner, so that the former can synchronize with the later. + sd::UndoManager* pDocUndoMgr = dynamic_cast(mpDocSh->GetUndoManager()); + if (pDocUndoMgr != nullptr) + pDocUndoMgr->SetLinkedUndoManager(&mrOutliner.GetUndoManager()); +} + +/** + * Destructor, restore Links, clear Outliner + */ +OutlineView::~OutlineView() +{ + DBG_ASSERT(maDragAndDropModelGuard == nullptr, + "sd::OutlineView::~OutlineView(), prior drag operation not finished correctly!"); + + Link aLink( LINK(this,OutlineView,EventMultiplexerListener) ); + mrOutlineViewShell.GetViewShellBase().GetEventMultiplexer()->RemoveEventListener( aLink ); + DisconnectFromApplication(); + + mpProgress.reset(); + + // unregister OutlinerViews and destroy them + for (auto & rpView : mpOutlinerViews) + { + if (rpView) + { + mrOutliner.RemoveView( rpView.get() ); + rpView.reset(); + } + } + + if (mrOutliner.GetViewCount() == 0) + { + // uninitialize Outliner: enable color display + ResetLinks(); + EEControlBits nCntrl = mrOutliner.GetControlWord(); + mrOutliner.SetUpdateLayout(false); // otherwise there will be drawn on SetControlWord + mrOutliner.SetControlWord(nCntrl & ~EEControlBits::NOCOLORS); + SvtAccessibilityOptions aOptions; + mrOutliner.ForceAutoColor( aOptions.GetIsAutomaticFontColor() ); + mrOutliner.Clear(); + } +} + +void OutlineView::ConnectToApplication() +{ + // When the mode is switched to outline the main view shell grabs focus. + // This is done for getting cut/copy/paste commands on slides in the left + // pane (slide sorter view shell) to work properly. + SfxShell* pTopViewShell = mrOutlineViewShell.GetViewShellBase().GetViewShellManager()->GetTopViewShell(); + if (pTopViewShell && pTopViewShell == &mrOutlineViewShell) + { + mrOutlineViewShell.GetActiveWindow()->GrabFocus(); + } + + Application::AddEventListener(LINK(this, OutlineView, AppEventListenerHdl)); +} + +void OutlineView::DisconnectFromApplication() +{ + Application::RemoveEventListener(LINK(this, OutlineView, AppEventListenerHdl)); +} + +void OutlineView::Paint(const ::tools::Rectangle& rRect, ::sd::Window const * pWin) +{ + OutlinerView* pOlView = GetViewByWindow(pWin); + + if (pOlView) + { + pOlView->HideCursor(); + pOlView->Paint(rRect); + + pOlView->ShowCursor(mbFirstPaint); + + mbFirstPaint = false; + } +} + +void OutlineView::AddWindowToPaintView(OutputDevice* pWin, vcl::Window* pWindow) +{ + bool bAdded = false; + bool bValidArea = false; + ::tools::Rectangle aOutputArea; + const Color aWhiteColor( COL_WHITE ); + sal_uInt16 nView = 0; + + while (nView < MAX_OUTLINERVIEWS && !bAdded) + { + if (mpOutlinerViews[nView] == nullptr) + { + mpOutlinerViews[nView].reset( new OutlinerView(&mrOutliner, dynamic_cast< ::sd::Window* >(pWin->GetOwnerWindow())) ); + mpOutlinerViews[nView]->SetBackgroundColor( aWhiteColor ); + mrOutliner.InsertView(mpOutlinerViews[nView].get(), EE_APPEND); + bAdded = true; + + if (bValidArea) + { + mpOutlinerViews[nView]->SetOutputArea(aOutputArea); + } + } + else if (!bValidArea) + { + aOutputArea = mpOutlinerViews[nView]->GetOutputArea(); + bValidArea = true; + } + + nView++; + } + + // white background in Outliner + pWin->SetBackground( Wallpaper( aWhiteColor ) ); + + ::sd::View::AddWindowToPaintView(pWin, pWindow); +} + +void OutlineView::DeleteWindowFromPaintView(OutputDevice* pWin) +{ + bool bRemoved = false; + sal_uInt16 nView = 0; + vcl::Window* pWindow; + + while (nView < MAX_OUTLINERVIEWS && !bRemoved) + { + if (mpOutlinerViews[nView] != nullptr) + { + pWindow = mpOutlinerViews[nView]->GetWindow(); + + if (pWindow->GetOutDev() == pWin) + { + mrOutliner.RemoveView( mpOutlinerViews[nView].get() ); + mpOutlinerViews[nView].reset(); + bRemoved = true; + } + } + + nView++; + } + + ::sd::View::DeleteWindowFromPaintView(pWin); +} + +/** + * Return a pointer to the OutlinerView corresponding to the window + */ +OutlinerView* OutlineView::GetViewByWindow (vcl::Window const * pWin) const +{ + OutlinerView* pOlView = nullptr; + for (std::unique_ptr const & pView : mpOutlinerViews) + { + if (pView != nullptr) + { + if ( pWin == pView->GetWindow() ) + { + pOlView = pView.get(); + } + } + } + return pOlView; +} + +/** + * Return the title before a random paragraph + */ +Paragraph* OutlineView::GetPrevTitle(const Paragraph* pPara) +{ + sal_Int32 nPos = mrOutliner.GetAbsPos(pPara); + + if (nPos > 0) + { + while(nPos) + { + pPara = mrOutliner.GetParagraph(--nPos); + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) + { + return const_cast< Paragraph* >( pPara ); + } + } + + } + return nullptr; +} + +/** + * Return the title after a random paragraph + */ +Paragraph* OutlineView::GetNextTitle(const Paragraph* pPara) +{ + Paragraph* pResult = const_cast< Paragraph* >( pPara ); + + sal_Int32 nPos = mrOutliner.GetAbsPos(pResult); + + do + { + pResult = mrOutliner.GetParagraph(++nPos); + if( pResult && ::Outliner::HasParaFlag(pResult, ParaFlag::ISPAGE) ) + return pResult; + } + while( pResult ); + + return nullptr; +} + +/** + * Handler for inserting pages (paragraphs) + */ +IMPL_LINK( OutlineView, ParagraphInsertedHdl, Outliner::ParagraphHdlParam, aParam, void ) +{ + // we get calls to this handler during binary insert of drag and drop contents but + // we ignore it here and handle it later in OnEndPasteOrDrop() + if (maDragAndDropModelGuard != nullptr) + return; + + OutlineViewPageChangesGuard aGuard(this); + + sal_Int32 nAbsPos = mrOutliner.GetAbsPos( aParam.pPara ); + + UpdateParagraph( nAbsPos ); + + if( (nAbsPos == 0) || + ::Outliner::HasParaFlag(aParam.pPara, ParaFlag::ISPAGE) || + ::Outliner::HasParaFlag(mrOutliner.GetParagraph( nAbsPos-1 ), ParaFlag::ISPAGE) ) + { + InsertSlideForParagraph( aParam.pPara ); + } +} + +/** creates and inserts an empty slide for the given paragraph */ +SdPage* OutlineView::InsertSlideForParagraph( Paragraph* pPara ) +{ + DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::InsertSlideForParagraph(), model change without undo?!" ); + + OutlineViewPageChangesGuard aGuard(this); + + mrOutliner.SetParaFlag( pPara, ParaFlag::ISPAGE ); + // how many titles are there before the new title paragraph? + sal_uLong nExample = 0; // position of the "example" page + sal_uLong nTarget = 0; // position of insertion + while(pPara) + { + pPara = GetPrevTitle(pPara); + if (pPara) + nTarget++; + } + + // if a new paragraph is created via RETURN before the first paragraph, the + // Outliner reports the old paragraph (which was moved down) as a new + // paragraph + if (nTarget == 1) + { + OUString aTest = mrOutliner.GetText(mrOutliner.GetParagraph(0)); + if (aTest.isEmpty()) + { + nTarget = 0; + } + } + + // the "example" page is the previous page - if it is available + if (nTarget > 0) + { + nExample = nTarget - 1; + + sal_uInt16 nPageCount = mrDoc.GetSdPageCount( PageKind::Standard ); + if( nExample >= nPageCount ) + nExample = nPageCount - 1; + } + + /********************************************************************** + * All the time, a standard page is created before a notes page. + * It is ensured that after each standard page the corresponding notes page + * follows. A handout page is exactly one handout page. + **********************************************************************/ + + // this page is exemplary + SdPage* pExample = mrDoc.GetSdPage(static_cast(nExample), PageKind::Standard); + rtl::Reference pPage = mrDoc.AllocSdPage(false); + + pPage->SetLayoutName(pExample->GetLayoutName()); + + // insert (page) + mrDoc.InsertPage(pPage.get(), static_cast(nTarget) * 2 + 1); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoNewPage(*pPage)); + + // assign a master page to the standard page + pPage->TRG_SetMasterPage(pExample->TRG_GetMasterPage()); + + // set page size + pPage->SetSize(pExample->GetSize()); + pPage->SetBorder( pExample->GetLeftBorder(), + pExample->GetUpperBorder(), + pExample->GetRightBorder(), + pExample->GetLowerBorder() ); + + // create new presentation objects (after or <Title with subtitle> + // follows <Title with outline>, otherwise apply the layout of the previous + // page + AutoLayout eAutoLayout = pExample->GetAutoLayout(); + if (eAutoLayout == AUTOLAYOUT_TITLE || + eAutoLayout == AUTOLAYOUT_TITLE_ONLY) + { + pPage->SetAutoLayout(AUTOLAYOUT_TITLE_CONTENT, true); + } + else + { + pPage->SetAutoLayout(pExample->GetAutoLayout(), true); + } + + /********************************************************************** + |* now the notes page + \*********************************************************************/ + pExample = mrDoc.GetSdPage(static_cast<sal_uInt16>(nExample), PageKind::Notes); + rtl::Reference<SdPage> pNotesPage = mrDoc.AllocSdPage(false); + + pNotesPage->SetLayoutName(pExample->GetLayoutName()); + + pNotesPage->SetPageKind(PageKind::Notes); + + // insert (notes page) + mrDoc.InsertPage(pNotesPage.get(), static_cast<sal_uInt16>(nTarget) * 2 + 2); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoNewPage(*pNotesPage)); + + // assign a master page to the notes page + pNotesPage->TRG_SetMasterPage(pExample->TRG_GetMasterPage()); + + // set page size, there must be already one page available + pNotesPage->SetSize(pExample->GetSize()); + pNotesPage->SetBorder( pExample->GetLeftBorder(), + pExample->GetUpperBorder(), + pExample->GetRightBorder(), + pExample->GetLowerBorder() ); + + // create presentation objects + pNotesPage->SetAutoLayout(pExample->GetAutoLayout(), true); + + mrOutliner.UpdateFields(); + + return pPage.get(); +} + +/** + * Handler for deleting pages (paragraphs) + */ +IMPL_LINK( OutlineView, ParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, aParam, void ) +{ + DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::ParagraphRemovingHdl(), model change without undo?!" ); + + OutlineViewPageChangesGuard aGuard(this); + + Paragraph* pPara = aParam.pPara; + if( !::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) ) + return; + + // how many titles are in front of the title paragraph in question? + sal_uLong nPos = 0; + while(pPara) + { + pPara = GetPrevTitle(pPara); + if (pPara) nPos++; + } + + // delete page and notes page + sal_uInt16 nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1; + SdrPage* pPage = mrDoc.GetPage(nAbsPos); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + mrDoc.RemovePage(nAbsPos); + + nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1; + pPage = mrDoc.GetPage(nAbsPos); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + mrDoc.RemovePage(nAbsPos); + + // progress display if necessary + if (mnPagesToProcess) + { + mnPagesProcessed++; + + if(mpProgress) + mpProgress->SetState(mnPagesProcessed); + + if (mnPagesProcessed == mnPagesToProcess) + { + mpProgress.reset(); + mnPagesToProcess = 0; + mnPagesProcessed = 0; + } + } + aParam.pOutliner->UpdateFields(); +} + +/** + * Handler for changing the indentation depth of paragraphs (requires inserting + * or deleting of pages in some cases) + */ +IMPL_LINK( OutlineView, DepthChangedHdl, ::Outliner::DepthChangeHdlParam, aParam, void ) +{ + DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::DepthChangedHdl(), no undo for model change?!" ); + + OutlineViewPageChangesGuard aGuard(this); + + Paragraph* pPara = aParam.pPara; + ::Outliner* pOutliner = aParam.pOutliner; + if( ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) && ((aParam.nPrevFlags & ParaFlag::ISPAGE) == ParaFlag::NONE) ) + { + // the current paragraph is transformed into a slide + + mrOutliner.SetDepth( pPara, -1 ); + + // are multiple level 1 paragraphs being brought to level 0 and we + // should start a progress view or a timer and didn't already? + if (mnPagesToProcess == 0) + { + Window* pActWin = mrOutlineViewShell.GetActiveWindow(); + OutlinerView* pOlView = GetViewByWindow(pActWin); + + std::vector<Paragraph*> aSelList; + pOlView->CreateSelectionList(aSelList); + + mnPagesToProcess = std::count_if(aSelList.begin(), aSelList.end(), + [&pOutliner](const Paragraph *pParagraph) { + return !Outliner::HasParaFlag(pParagraph, ParaFlag::ISPAGE) && + (pOutliner->GetDepth(pOutliner->GetAbsPos(pParagraph)) <= 0); + }); + + mnPagesToProcess++; // the paragraph being in level 0 already + // should be included + mnPagesProcessed = 0; + + if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD) + { + mpProgress.reset( new SfxProgress( GetDocSh(), SdResId(STR_CREATE_PAGES), mnPagesToProcess ) ); + } + else + { + mpDocSh->SetWaitCursor( true ); + } + } + + ParagraphInsertedHdl( { aParam.pOutliner, aParam.pPara } ); + + mnPagesProcessed++; + + // should there be a progress display? + if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD) + { + if (mpProgress) + mpProgress->SetState(mnPagesProcessed); + } + + // was this the last page? + if (mnPagesProcessed == mnPagesToProcess) + { + if (mnPagesToProcess > PROCESS_WITH_PROGRESS_THRESHOLD && mpProgress) + { + mpProgress.reset(); + } + else + mpDocSh->SetWaitCursor( false ); + + mnPagesToProcess = 0; + mnPagesProcessed = 0; + } + pOutliner->UpdateFields(); + } + else if( !::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ) && ((aParam.nPrevFlags & ParaFlag::ISPAGE) != ParaFlag::NONE) ) + { + // the paragraph was a page but now becomes a normal paragraph + + // how many titles are before the title paragraph in question? + sal_uLong nPos = 0; + Paragraph* pParagraph = pPara; + while(pParagraph) + { + pParagraph = GetPrevTitle(pParagraph); + if (pParagraph) + nPos++; + } + // delete page and notes page + + sal_uInt16 nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1; + SdrPage* pPage = mrDoc.GetPage(nAbsPos); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + mrDoc.RemovePage(nAbsPos); + + nAbsPos = static_cast<sal_uInt16>(nPos) * 2 + 1; + pPage = mrDoc.GetPage(nAbsPos); + if( isRecordingUndo() ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeletePage(*pPage)); + mrDoc.RemovePage(nAbsPos); + + pPage = GetPageForParagraph( pPara ); + + mrOutliner.SetDepth( pPara, (pPage && (static_cast<SdPage*>(pPage)->GetAutoLayout() == AUTOLAYOUT_TITLE)) ? -1 : 0 ); + + // progress display if necessary + if (mnPagesToProcess) + { + mnPagesProcessed++; + if (mpProgress) + mpProgress->SetState(mnPagesProcessed); + + if (mnPagesProcessed == mnPagesToProcess) + { + mpProgress.reset(); + mnPagesToProcess = 0; + mnPagesProcessed = 0; + } + } + pOutliner->UpdateFields(); + } + else if ( (pOutliner->GetPrevDepth() == 1) && ( pOutliner->GetDepth( pOutliner->GetAbsPos( pPara ) ) == 2 ) ) + { + // how many titles are in front of the title paragraph in question? + sal_Int32 nPos = -1; + + Paragraph* pParagraph = pPara; + while(pParagraph) + { + pParagraph = GetPrevTitle(pParagraph); + if (pParagraph) + nPos++; + } + + if(nPos >= 0) + { + SdPage*pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPos), PageKind::Standard); + + if(pPage && pPage->GetPresObj(PresObjKind::Text)) + pOutliner->SetDepth( pPara, 0 ); + } + + } + // how many titles are in front of the title paragraph in question? + sal_Int32 nPos = -1; + + Paragraph* pTempPara = pPara; + while(pTempPara) + { + pTempPara = GetPrevTitle(pTempPara); + if (pTempPara) + nPos++; + } + + if( nPos < 0 ) + return; + + SdPage* pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPos), PageKind::Standard ); + + if( !pPage ) + return; + + SfxStyleSheet* pStyleSheet = nullptr; + sal_Int32 nPara = pOutliner->GetAbsPos( pPara ); + sal_Int16 nDepth = pOutliner->GetDepth( nPara ); + bool bSubTitle = pPage->GetPresObj(PresObjKind::Text) != nullptr; + + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) + { + pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Title ); + } + else if( bSubTitle ) + { + pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Text ); + } + else + { + pStyleSheet = pPage->GetStyleSheetForPresObj( PresObjKind::Outline ); + + if( nDepth > 0 ) + { + OUString aNewStyleSheetName = pStyleSheet->GetName(); + if (!aNewStyleSheetName.isEmpty()) + aNewStyleSheetName = aNewStyleSheetName.copy(0, aNewStyleSheetName.getLength() - 1); + aNewStyleSheetName += OUString::number( nDepth+1 ); + SfxStyleSheetBasePool* pStylePool = mrDoc.GetStyleSheetPool(); + pStyleSheet = static_cast<SfxStyleSheet*>( pStylePool->Find( aNewStyleSheetName, pStyleSheet->GetFamily() ) ); + } + } + + // before we set the style sheet we need to preserve the bullet item + // since all items will be deleted while setting a new style sheet + SfxItemSet aOldAttrs( pOutliner->GetParaAttribs( nPara ) ); + + pOutliner->SetStyleSheet( nPara, pStyleSheet ); + + // restore the old bullet item but not if the style changed + if ( pOutliner->GetPrevDepth() != -1 && nDepth != -1 && + aOldAttrs.GetItemState( EE_PARA_NUMBULLET ) == SfxItemState::SET ) + { + SfxItemSet aAttrs( pOutliner->GetParaAttribs( nPara ) ); + aAttrs.Put( *aOldAttrs.GetItem( EE_PARA_NUMBULLET ) ); + pOutliner->SetParaAttribs( nPara, aAttrs ); + } +} + +/** + * Handler for StatusEvents + */ +IMPL_LINK_NOARG(OutlineView, StatusEventHdl, EditStatus&, void) +{ + ::sd::Window* pWin = mrOutlineViewShell.GetActiveWindow(); + OutlinerView* pOutlinerView = GetViewByWindow(pWin); + ::tools::Rectangle aVis = pOutlinerView->GetVisArea(); + ::tools::Rectangle aText(Point(0,0), + Size(mnPaperWidth, + mrOutliner.GetTextHeight())); + ::tools::Rectangle aWin(Point(0,0), pWin->GetOutputSizePixel()); + aWin = pWin->PixelToLogic(aWin); + + if (!aVis.IsEmpty()) // not when opening + { + if (aWin.GetHeight() > aText.Bottom()) + aText.SetBottom( aWin.GetHeight() ); + + mrOutlineViewShell.InitWindows(Point(0,0), aText.GetSize(), aVis.TopLeft()); + mrOutlineViewShell.UpdateScrollBars(); + } +} + +IMPL_LINK_NOARG(OutlineView, BeginDropHdl, EditView*, void) +{ + DBG_ASSERT(maDragAndDropModelGuard == nullptr, + "sd::OutlineView::BeginDropHdl(), prior drag operation not finished correctly!"); + + maDragAndDropModelGuard.reset( new OutlineViewModelChangeGuard( *this ) ); +} + +IMPL_LINK_NOARG(OutlineView, EndDropHdl, EditView*, void) +{ + maDragAndDropModelGuard.reset(); +} + +/** + * Handler for the start of a paragraph movement + */ +IMPL_LINK( OutlineView, BeginMovingHdl, ::Outliner *, pOutliner, void ) +{ + OutlineViewPageChangesGuard aGuard(this); + + // list of selected title paragraphs + mpOutlinerViews[0]->CreateSelectionList(maSelectedParas); + + maSelectedParas.erase(std::remove_if(maSelectedParas.begin(), maSelectedParas.end(), + [](const Paragraph* pPara) { return !Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE); }), + maSelectedParas.end()); + + // select the pages belonging to the paragraphs on level 0 to select + sal_uInt16 nPos = 0; + sal_Int32 nParaPos = 0; + Paragraph* pPara = pOutliner->GetParagraph( 0 ); + std::vector<Paragraph*>::const_iterator fiter; + + while(pPara) + { + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) // one page? + { + maOldParaOrder.push_back(pPara); + SdPage* pPage = mrDoc.GetSdPage(nPos, PageKind::Standard); + + fiter = std::find(maSelectedParas.begin(),maSelectedParas.end(),pPara); + + pPage->SetSelected(fiter != maSelectedParas.end()); + + ++nPos; + } + pPara = pOutliner->GetParagraph( ++nParaPos ); + } +} + +/** + * Handler for the end of a paragraph movement + */ +IMPL_LINK( OutlineView, EndMovingHdl, ::Outliner *, pOutliner, void ) +{ + OutlineViewPageChangesGuard aGuard(this); + + DBG_ASSERT( isRecordingUndo(), "sd::OutlineView::EndMovingHdl(), model change without undo?!" ); + + // look for insertion position via the first paragraph + Paragraph* pSearchIt = maSelectedParas.empty() ? nullptr : *(maSelectedParas.begin()); + + // look for the first of the selected paragraphs in the new ordering + sal_uInt16 nPosNewOrder = 0; + sal_Int32 nParaPos = 0; + Paragraph* pPara = pOutliner->GetParagraph( 0 ); + Paragraph* pPrev = nullptr; + while (pPara && pPara != pSearchIt) + { + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) + { + nPosNewOrder++; + pPrev = pPara; + } + pPara = pOutliner->GetParagraph( ++nParaPos ); + } + + sal_uInt16 nPos = nPosNewOrder; // don't change nPosNewOrder + if (nPos == 0) + { + nPos = sal_uInt16(-1); // insert before the first page + } + else + { + // look for the predecessor in the old ordering + std::vector<Paragraph*>::const_iterator it = std::find(maOldParaOrder.begin(), + maOldParaOrder.end(), + pPrev); + + if (it != maOldParaOrder.end()) + nPos = static_cast<sal_uInt16>(it-maOldParaOrder.begin()); + else + nPos = 0xffff; + + DBG_ASSERT(nPos != 0xffff, "Paragraph not found"); + } + + mrDoc.MovePages(nPos); + + // deselect the pages again + sal_uInt16 nPageCount = static_cast<sal_uInt16>(maSelectedParas.size()); + while (nPageCount) + { + SdPage* pPage = mrDoc.GetSdPage(nPosNewOrder, PageKind::Standard); + pPage->SetSelected(false); + nPosNewOrder++; + nPageCount--; + } + + pOutliner->UpdateFields(); + + maSelectedParas.clear(); + maOldParaOrder.clear(); +} + +/** + * Look for the title text object in one page of the model + */ +SdrTextObj* OutlineView::GetTitleTextObject(SdrPage const * pPage) +{ + const size_t nObjectCount = pPage->GetObjCount(); + SdrTextObj* pResult = nullptr; + + for (size_t nObject = 0; nObject < nObjectCount; ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject->GetObjInventor() == SdrInventor::Default && + pObject->GetObjIdentifier() == SdrObjKind::TitleText) + { + pResult = static_cast<SdrTextObj*>(pObject); + break; + } + } + return pResult; +} + +/** + * Look for the outline text object in one page of the model + */ +SdrTextObj* OutlineView::GetOutlineTextObject(SdrPage const * pPage) +{ + const size_t nObjectCount = pPage->GetObjCount(); + SdrTextObj* pResult = nullptr; + + for (size_t nObject = 0; nObject < nObjectCount; ++nObject) + { + SdrObject* pObject = pPage->GetObj(nObject); + if (pObject->GetObjInventor() == SdrInventor::Default && + pObject->GetObjIdentifier() == SdrObjKind::OutlineText) + { + pResult = static_cast<SdrTextObj*>(pObject); + break; + } + } + return pResult; +} + +SdrTextObj* OutlineView::CreateTitleTextObject(SdPage* pPage) +{ + DBG_ASSERT( GetTitleTextObject(pPage) == nullptr, "sd::OutlineView::CreateTitleTextObject(), there is already a title text object!" ); + + if( pPage->GetAutoLayout() == AUTOLAYOUT_NONE ) + { + // simple case + pPage->SetAutoLayout( AUTOLAYOUT_TITLE_ONLY, true ); + } + else + { + // we already have a layout with a title but the title + // object was deleted, create a new one + pPage->InsertAutoLayoutShape( nullptr, PresObjKind::Title, false, pPage->GetTitleRect(), true ); + } + + return GetTitleTextObject(pPage); +} + +SdrTextObj* OutlineView::CreateOutlineTextObject(SdPage* pPage) +{ + DBG_ASSERT( GetOutlineTextObject(pPage) == nullptr, "sd::OutlineView::CreateOutlineTextObject(), there is already a layout text object!" ); + + AutoLayout eNewLayout = pPage->GetAutoLayout(); + switch( eNewLayout ) + { + case AUTOLAYOUT_NONE: + case AUTOLAYOUT_TITLE_ONLY: + case AUTOLAYOUT_TITLE: eNewLayout = AUTOLAYOUT_TITLE_CONTENT; break; + + case AUTOLAYOUT_CHART: eNewLayout = AUTOLAYOUT_CHARTTEXT; break; + + case AUTOLAYOUT_ORG: + case AUTOLAYOUT_TAB: + case AUTOLAYOUT_OBJ: eNewLayout = AUTOLAYOUT_OBJTEXT; break; + default: + break; + } + + if( eNewLayout != pPage->GetAutoLayout() ) + { + pPage->SetAutoLayout( eNewLayout, true ); + } + else + { + // we already have a layout with a text but the text + // object was deleted, create a new one + pPage->InsertAutoLayoutShape( nullptr, + PresObjKind::Outline, + false, pPage->GetLayoutRect(), true ); + } + + return GetOutlineTextObject(pPage); +} + +/** updates draw model with all changes from outliner model */ +void OutlineView::PrepareClose() +{ + ::sd::UndoManager* pDocUndoMgr = dynamic_cast<sd::UndoManager*>(mpDocSh->GetUndoManager()); + if (pDocUndoMgr != nullptr) + pDocUndoMgr->SetLinkedUndoManager(nullptr); + + mrOutliner.GetUndoManager().Clear(); + + BegUndo(SdResId(STR_UNDO_CHANGE_TITLE_AND_LAYOUT)); + UpdateDocument(); + EndUndo(); + mrDoc.SetSelected(GetActualPage(), true); +} + +/** + * Set attributes of the selected text + */ +bool OutlineView::SetAttributes(const SfxItemSet& rSet, bool /*bSlide*/, bool /*bReplaceAll*/, bool /*bMaster*/) +{ + bool bOk = false; + + OutlinerView* pOlView = GetViewByWindow(mrOutlineViewShell.GetActiveWindow()); + + if (pOlView) + { + pOlView->SetAttribs(rSet); + bOk = true; + } + + mrOutlineViewShell.Invalidate (SID_PREVIEW_STATE); + + return bOk; +} + +/** + * Get attributes of the selected text + */ +void OutlineView::GetAttributes( SfxItemSet& rTargetSet, bool ) const +{ + OutlinerView* pOlView = GetViewByWindow( + mrOutlineViewShell.GetActiveWindow()); + assert(pOlView && "No OutlinerView found"); + + rTargetSet.Put( pOlView->GetAttribs(), false ); +} + +/** creates outliner model from draw model */ +void OutlineView::FillOutliner() +{ + mrOutliner.GetUndoManager().Clear(); + mrOutliner.EnableUndo(false); + ResetLinks(); + const bool bPrevUpdateLayout = mrOutliner.SetUpdateLayout(false); + + Paragraph* pTitleToSelect = nullptr; + sal_uInt16 nPageCount = mrDoc.GetSdPageCount(PageKind::Standard); + + // fill outliner with paragraphs from slides title & (outlines|subtitles) + for (sal_uInt16 nPage = 0; nPage < nPageCount; nPage++) + { + SdPage* pPage = mrDoc.GetSdPage(nPage, PageKind::Standard); + Paragraph * pPara = nullptr; + + // take text from title shape + SdrTextObj* pTO = GetTitleTextObject(pPage); + if(pTO && !(pTO->IsEmptyPresObj())) + { + OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject(); + if (pOPO) + { + bool bVertical = pOPO->IsEffectivelyVertical(); + pOPO->SetVertical( false ); + mrOutliner.AddText(*pOPO); + pOPO->SetVertical( bVertical ); + pPara = mrOutliner.GetParagraph( mrOutliner.GetParagraphCount()-1 ); + } + } + + if( pPara == nullptr ) // no title, insert an empty paragraph + { + pPara = mrOutliner.Insert(OUString()); + mrOutliner.SetDepth(pPara, -1); + + // do not apply hard attributes from the previous paragraph + mrOutliner.SetParaAttribs( mrOutliner.GetAbsPos(pPara), + mrOutliner.GetEmptyItemSet() ); + + mrOutliner.SetStyleSheet( mrOutliner.GetAbsPos( pPara ), pPage->GetStyleSheetForPresObj( PresObjKind::Title ) ); + } + + mrOutliner.SetParaFlag( pPara, ParaFlag::ISPAGE ); + + sal_Int32 nPara = mrOutliner.GetAbsPos( pPara ); + + UpdateParagraph( nPara ); + + // remember paragraph of currently selected page + if (pPage->IsSelected()) + pTitleToSelect = pPara; + + // take text from subtitle or outline + pTO = static_cast<SdrTextObj*>(pPage->GetPresObj(PresObjKind::Text)); + const bool bSubTitle = pTO != nullptr; + + if (!pTO) // if no subtile found, try outline + pTO = GetOutlineTextObject(pPage); + + if(pTO && !(pTO->IsEmptyPresObj())) // found some text + { + OutlinerParaObject* pOPO = pTO->GetOutlinerParaObject(); + if (pOPO) + { + sal_Int32 nParaCount1 = mrOutliner.GetParagraphCount(); + bool bVertical = pOPO->IsEffectivelyVertical(); + pOPO->SetVertical( false ); + mrOutliner.AddText(*pOPO); + pOPO->SetVertical( bVertical ); + + sal_Int32 nParaCount2 = mrOutliner.GetParagraphCount(); + for (sal_Int32 n = nParaCount1; n < nParaCount2; n++) + { + if( bSubTitle ) + { + Paragraph* p = mrOutliner.GetParagraph(n); + if(p && mrOutliner.GetDepth( n ) > 0 ) + mrOutliner.SetDepth(p, 0); + } + + UpdateParagraph( n ); + } + } + } + } + + // place cursor at the start + Paragraph* pFirstPara = mrOutliner.GetParagraph( 0 ); + mpOutlinerViews[0]->Select( pFirstPara ); + mpOutlinerViews[0]->Select( pFirstPara, false ); + + // select title of slide that was selected + if (pTitleToSelect) + mpOutlinerViews[0]->Select(pTitleToSelect); + + SetLinks(); + + mrOutliner.EnableUndo(true); + + mrOutliner.SetUpdateLayout(bPrevUpdateLayout); +} + +/** + * Handler for deleting of level 0 paragraphs (pages): Warning + */ +IMPL_LINK_NOARG(OutlineView, RemovingPagesHdl, OutlinerView*, bool) +{ + sal_Int32 nNumOfPages = mrOutliner.GetSelPageCount(); + + if (nNumOfPages > PROCESS_WITH_PROGRESS_THRESHOLD) + { + mnPagesToProcess = nNumOfPages; + mnPagesProcessed = 0; + } + + if (mnPagesToProcess) + { + mpProgress.reset( new SfxProgress( GetDocSh(), SdResId(STR_DELETE_PAGES), mnPagesToProcess ) ); + } + mrOutliner.UpdateFields(); + + return true; +} + +/** + * Handler for indenting level 0 paragraphs (pages): Warning + */ +IMPL_LINK( OutlineView, IndentingPagesHdl, OutlinerView *, pOutlinerView, bool ) +{ + return RemovingPagesHdl(pOutlinerView); +} + +/** returns the first slide that is selected in the outliner or where + the cursor is located */ +SdPage* OutlineView::GetActualPage() +{ + ::sd::Window* pWin = mrOutlineViewShell.GetActiveWindow(); + OutlinerView* pActiveView = GetViewByWindow(pWin); + + std::vector<Paragraph*> aSelList; + pActiveView->CreateSelectionList(aSelList); + + Paragraph *pPar = aSelList.empty() ? nullptr : *(aSelList.begin()); + SdPage* pCurrent = GetPageForParagraph(pPar); + + DBG_ASSERT( pCurrent || + (mpDocSh->GetUndoManager() && static_cast< sd::UndoManager *>(mpDocSh->GetUndoManager())->IsDoing()) || + maDragAndDropModelGuard, + "sd::OutlineView::GetActualPage(), no current page?" ); + + if( pCurrent ) + return pCurrent; + + return mrDoc.GetSdPage( 0, PageKind::Standard ); +} + +SdPage* OutlineView::GetPageForParagraph( Paragraph* pPara ) +{ + if( !::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE) ) + pPara = GetPrevTitle(pPara); + + sal_uInt32 nPageToSelect = 0; + while(pPara) + { + pPara = GetPrevTitle(pPara); + if(pPara) + nPageToSelect++; + } + + if( nPageToSelect < static_cast<sal_uInt32>(mrDoc.GetSdPageCount( PageKind::Standard )) ) + return mrDoc.GetSdPage( static_cast<sal_uInt16>(nPageToSelect), PageKind::Standard ); + + return nullptr; +} + +Paragraph* OutlineView::GetParagraphForPage( ::Outliner const & rOutl, SdPage const * pPage ) +{ + // get the number of paragraphs with ident 0 we need to skip before + // we find the actual page + sal_uInt32 nPagesToSkip = (pPage->GetPageNum() - 1) >> 1; + + sal_Int32 nParaPos = 0; + Paragraph* pPara = rOutl.GetParagraph( 0 ); + while( pPara ) + { + // if this paragraph is a page... + if( ::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE) ) + { + // see if we already skipped enough pages + if( 0 == nPagesToSkip ) + break; // and if so, end the loop + + // we skipped another page + nPagesToSkip--; + } + + // get next paragraph + pPara = mrOutliner.GetParagraph( ++nParaPos ); + } + + return pPara; +} + +/** selects the paragraph for the given page at the outliner view*/ +void OutlineView::SetActualPage( SdPage const * pActual ) +{ + if( pActual && dynamic_cast<SdOutliner&>(mrOutliner).GetIgnoreCurrentPageChangesLevel()==0 && !mbFirstPaint) + { + // if we found a paragraph, select its text at the outliner view + Paragraph* pPara = GetParagraphForPage( mrOutliner, pActual ); + if( pPara ) + mpOutlinerViews[0]->Select( pPara ); + } +} + +/** + * Get StyleSheet from the selection + */ +SfxStyleSheet* OutlineView::GetStyleSheet() const +{ + ::sd::Window* pActWin = mrOutlineViewShell.GetActiveWindow(); + OutlinerView* pOlView = GetViewByWindow(pActWin); + SfxStyleSheet* pResult = pOlView->GetStyleSheet(); + return pResult; +} + +/** + * Mark pages as selected / not selected + */ +void OutlineView::SetSelectedPages() +{ + // list of selected title paragraphs + std::vector<Paragraph*> aSelParas; + mpOutlinerViews[0]->CreateSelectionList(aSelParas); + + aSelParas.erase(std::remove_if(aSelParas.begin(), aSelParas.end(), + [](const Paragraph* pPara) { return !Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE); }), + aSelParas.end()); + + // select the pages belonging to the paragraphs on level 0 to select + sal_uInt16 nPos = 0; + sal_Int32 nParaPos = 0; + Paragraph *pPara = mrOutliner.GetParagraph( 0 ); + std::vector<Paragraph*>::const_iterator fiter; + + while(pPara) + { + if( ::Outliner::HasParaFlag(pPara, ParaFlag::ISPAGE) ) // one page + { + SdPage* pPage = mrDoc.GetSdPage(nPos, PageKind::Standard); + DBG_ASSERT(pPage!=nullptr, + "Trying to select non-existing page OutlineView::SetSelectedPages()"); + + if (pPage) + { + fiter = std::find(aSelParas.begin(),aSelParas.end(),pPara); + pPage->SetSelected(fiter != aSelParas.end()); + } + + nPos++; + } + + pPara = mrOutliner.GetParagraph( ++nParaPos ); + } +} + +/** + * Set new links + */ +void OutlineView::SetLinks() +{ + // set notification links + mrOutliner.SetParaInsertedHdl(LINK(this, OutlineView, ParagraphInsertedHdl)); + mrOutliner.SetParaRemovingHdl(LINK(this, OutlineView, ParagraphRemovingHdl)); + mrOutliner.SetDepthChangedHdl(LINK(this, OutlineView, DepthChangedHdl)); + mrOutliner.SetBeginMovingHdl(LINK(this, OutlineView, BeginMovingHdl)); + mrOutliner.SetEndMovingHdl(LINK(this, OutlineView, EndMovingHdl)); + mrOutliner.SetRemovingPagesHdl(LINK(this, OutlineView, RemovingPagesHdl)); + mrOutliner.SetIndentingPagesHdl(LINK(this, OutlineView, IndentingPagesHdl)); + mrOutliner.SetStatusEventHdl(LINK(this, OutlineView, StatusEventHdl)); + mrOutliner.SetBeginDropHdl(LINK(this,OutlineView, BeginDropHdl)); + mrOutliner.SetEndDropHdl(LINK(this,OutlineView, EndDropHdl)); + mrOutliner.SetPaintFirstLineHdl(LINK(this,OutlineView,PaintingFirstLineHdl)); + mrOutliner.SetBeginPasteOrDropHdl(LINK(this,OutlineView, BeginPasteOrDropHdl)); + mrOutliner.SetEndPasteOrDropHdl(LINK(this,OutlineView, EndPasteOrDropHdl)); +} + +/** + * Restore old links + */ +void OutlineView::ResetLinks() const +{ + mrOutliner.SetParaInsertedHdl(Link<::Outliner::ParagraphHdlParam,void>()); + mrOutliner.SetParaRemovingHdl(Link<::Outliner::ParagraphHdlParam,void>()); + mrOutliner.SetDepthChangedHdl(Link<::Outliner::DepthChangeHdlParam,void>()); + mrOutliner.SetBeginMovingHdl(Link<::Outliner*,void>()); + mrOutliner.SetEndMovingHdl(Link<::Outliner*,void>()); + mrOutliner.SetStatusEventHdl(Link<EditStatus&,void>()); + mrOutliner.SetRemovingPagesHdl(Link<OutlinerView*,bool>()); + mrOutliner.SetIndentingPagesHdl(Link<OutlinerView*,bool>()); + mrOutliner.SetDrawPortionHdl(Link<DrawPortionInfo*,void>()); + mrOutliner.SetBeginPasteOrDropHdl(Link<PasteOrDropInfos*,void>()); + mrOutliner.SetEndPasteOrDropHdl(Link<PasteOrDropInfos*,void>()); +} + +sal_Int8 OutlineView::AcceptDrop( const AcceptDropEvent&, DropTargetHelper&, SdrLayerID) +{ + return DND_ACTION_NONE; +} + +sal_Int8 OutlineView::ExecuteDrop( const ExecuteDropEvent&, ::sd::Window*, sal_uInt16, SdrLayerID) +{ + return DND_ACTION_NONE; +} + +// Re-implement GetScriptType for this view to get correct results +SvtScriptType OutlineView::GetScriptType() const +{ + SvtScriptType nScriptType = ::sd::View::GetScriptType(); + + std::optional<OutlinerParaObject> pTempOPObj = mrOutliner.CreateParaObject(); + if(pTempOPObj) + { + nScriptType = pTempOPObj->GetTextObject().GetScriptType(); + } + + return nScriptType; +} + +void OutlineView::onUpdateStyleSettings( bool bForceUpdate /* = false */ ) +{ + svtools::ColorConfig aColorConfig; + const Color aDocColor( aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor ); + if( !(bForceUpdate || (maDocColor != aDocColor)) ) + return; + + sal_uInt16 nView; + for( nView = 0; nView < MAX_OUTLINERVIEWS; nView++ ) + { + if (mpOutlinerViews[nView] != nullptr) + { + mpOutlinerViews[nView]->SetBackgroundColor( aDocColor ); + + vcl::Window* pWindow = mpOutlinerViews[nView]->GetWindow(); + + if( pWindow ) + pWindow->SetBackground( Wallpaper( aDocColor ) ); + + } + } + + mrOutliner.SetBackgroundColor( aDocColor ); + + maDocColor = aDocColor; +} + +IMPL_LINK_NOARG(OutlineView, AppEventListenerHdl, VclSimpleEvent&, void) +{ + onUpdateStyleSettings(false); +} + +IMPL_LINK(OutlineView, EventMultiplexerListener, ::sd::tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::CurrentPageChanged: + SetActualPage(mrOutlineViewShell.GetActualPage()); + break; + + case EventMultiplexerEventId::PageOrder: + if (dynamic_cast<SdOutliner&>(mrOutliner).GetIgnoreCurrentPageChangesLevel()==0) + { + if (((mrDoc.GetPageCount()-1)%2) == 0) + { + mrOutliner.Clear(); + FillOutliner(); + ::sd::Window* pWindow = mrOutlineViewShell.GetActiveWindow(); + if (pWindow != nullptr) + pWindow->Invalidate(); + } + } + break; + + default: break; + } +} + +void OutlineView::IgnoreCurrentPageChanges (bool bIgnoreChanges) +{ + if (bIgnoreChanges) + dynamic_cast<SdOutliner&>(mrOutliner).IncreIgnoreCurrentPageChangesLevel(); + else + dynamic_cast<SdOutliner&>(mrOutliner).DecreIgnoreCurrentPageChangesLevel(); +} + +/** call this method before you do anything that can modify the outliner + and or the drawing document model. It will create needed undo actions */ +void OutlineView::BeginModelChange() +{ + mrOutliner.GetUndoManager().EnterListAction("", "", 0, mrOutlineViewShell.GetViewShellBase().GetViewShellId()); + BegUndo(SdResId(STR_UNDO_CHANGE_TITLE_AND_LAYOUT)); +} + +/** call this method after BeginModelChange(), when all possible model + changes are done. */ +void OutlineView::EndModelChange() +{ + UpdateDocument(); + + SfxUndoManager* pDocUndoMgr = mpDocSh->GetUndoManager(); + + bool bHasUndoActions = pDocUndoMgr->GetUndoActionCount() != 0; + + EndUndo(); + + DBG_ASSERT( bHasUndoActions == (mrOutliner.GetUndoManager().GetUndoActionCount() != 0), "sd::OutlineView::EndModelChange(), undo actions not in sync!" ); + + mrOutliner.GetUndoManager().LeaveListAction(); + + if( bHasUndoActions && mrOutliner.GetEditEngine().HasTriedMergeOnLastAddUndo() ) + TryToMergeUndoActions(); + + mrOutlineViewShell.Invalidate( SID_UNDO ); + mrOutlineViewShell.Invalidate( SID_REDO ); +} + +/** updates all changes in the outliner model to the draw model */ +void OutlineView::UpdateDocument() +{ + OutlineViewPageChangesGuard aGuard(this); + + const sal_uInt32 nPageCount = mrDoc.GetSdPageCount(PageKind::Standard); + Paragraph* pPara = mrOutliner.GetParagraph( 0 ); + sal_uInt32 nPage; + for (nPage = 0; nPage < nPageCount; nPage++) + { + SdPage* pPage = mrDoc.GetSdPage( static_cast<sal_uInt16>(nPage), PageKind::Standard); + mrDoc.SetSelected(pPage, false); + + mrOutlineViewShell.UpdateTitleObject( pPage, pPara ); + mrOutlineViewShell.UpdateOutlineObject( pPage, pPara ); + + if( pPara ) + pPara = GetNextTitle(pPara); + } + + DBG_ASSERT( pPara == nullptr, "sd::OutlineView::UpdateDocument(), slides are out of sync, creating missing ones" ); + while( pPara ) + { + SdPage* pPage = InsertSlideForParagraph( pPara ); + mrDoc.SetSelected(pPage, false); + + mrOutlineViewShell.UpdateTitleObject( pPage, pPara ); + mrOutlineViewShell.UpdateOutlineObject( pPage, pPara ); + + pPara = GetNextTitle(pPara); + } +} + +/** merge edit engine undo actions if possible */ +void OutlineView::TryToMergeUndoActions() +{ + SfxUndoManager& rOutlineUndo = mrOutliner.GetUndoManager(); + if( rOutlineUndo.GetUndoActionCount() <= 1 ) + return; + + SfxListUndoAction* pListAction = dynamic_cast< SfxListUndoAction* >( rOutlineUndo.GetUndoAction() ); + SfxListUndoAction* pPrevListAction = dynamic_cast< SfxListUndoAction* >( rOutlineUndo.GetUndoAction(1) ); + if( !(pListAction && pPrevListAction) ) + return; + + // find the top EditUndo action in the top undo action list + size_t nAction = pListAction->maUndoActions.size(); + EditUndo* pEditUndo = nullptr; + while( !pEditUndo && nAction ) + { + pEditUndo = dynamic_cast< EditUndo* >(pListAction->GetUndoAction(--nAction)); + } + + sal_uInt16 nEditPos = nAction; // we need this later to remove the merged undo actions + + // make sure it is the only EditUndo action in the top undo list + while( pEditUndo && nAction ) + { + if( dynamic_cast< EditUndo* >(pListAction->GetUndoAction(--nAction)) ) + pEditUndo = nullptr; + } + + // do we have one and only one EditUndo action in the top undo list? + if( !pEditUndo ) + return; + + // yes, see if we can merge it with the prev undo list + + nAction = pPrevListAction->maUndoActions.size(); + EditUndo* pPrevEditUndo = nullptr; + while( !pPrevEditUndo && nAction ) + pPrevEditUndo = dynamic_cast< EditUndo* >(pPrevListAction->GetUndoAction(--nAction)); + + if( !(pPrevEditUndo && pPrevEditUndo->Merge( pEditUndo )) ) + return; + + // ok we merged the only EditUndo of the top undo list with + // the top EditUndo of the previous undo list + + // first remove the merged undo action + assert( pListAction->GetUndoAction(nEditPos) == pEditUndo && + "sd::OutlineView::TryToMergeUndoActions(), wrong edit pos!" ); + pListAction->Remove(nEditPos); + + if ( !pListAction->maUndoActions.empty() ) + { + // now we have to move all remaining doc undo actions from the top undo + // list to the previous undo list and remove the top undo list + + size_t nCount = pListAction->maUndoActions.size(); + size_t nDestAction = pPrevListAction->maUndoActions.size(); + while( nCount-- ) + { + std::unique_ptr<SfxUndoAction> pTemp = pListAction->Remove(0); + pPrevListAction->Insert( std::move(pTemp), nDestAction++ ); + } + pPrevListAction->nCurUndoAction = pPrevListAction->maUndoActions.size(); + } + + rOutlineUndo.RemoveLastUndoAction(); +} + +IMPL_LINK(OutlineView, PaintingFirstLineHdl, PaintFirstLineInfo*, pInfo, void) +{ + if( !pInfo ) + return; + + Paragraph* pPara = mrOutliner.GetParagraph( pInfo->mnPara ); + EditEngine& rEditEngine = const_cast< EditEngine& >( mrOutliner.GetEditEngine() ); + + Size aImageSize( pInfo->mpOutDev->PixelToLogic( maSlideImage.GetSizePixel() ) ); + Size aOffset( 100, 100 ); + + // paint slide number + if( !(pPara && ::Outliner::HasParaFlag(pPara,ParaFlag::ISPAGE)) ) + return; + + ::tools::Long nPage = 0; // todo, printing?? + for ( sal_Int32 n = 0; n <= pInfo->mnPara; n++ ) + { + Paragraph* p = mrOutliner.GetParagraph( n ); + if ( ::Outliner::HasParaFlag(p,ParaFlag::ISPAGE) ) + nPage++; + } + + ::tools::Long nBulletHeight = static_cast<::tools::Long>(mrOutliner.GetLineHeight( pInfo->mnPara )); + ::tools::Long nFontHeight = 0; + if ( !rEditEngine.IsFlatMode() ) + { + nFontHeight = nBulletHeight / 5; + } + else + { + nFontHeight = (nBulletHeight * 10) / 25; + } + + Size aFontSz( 0, nFontHeight ); + + Size aOutSize( 2000, nBulletHeight ); + + const float fImageHeight = (static_cast<float>(aOutSize.Height()) * float(4)) / float(7); + if (aImageSize.Width() != 0) + { + const float fImageRatio = static_cast<float>(aImageSize.Height()) / static_cast<float>(aImageSize.Width()); + aImageSize.setWidth( static_cast<::tools::Long>( fImageRatio * fImageHeight ) ); + } + aImageSize.setHeight( static_cast<::tools::Long>(fImageHeight) ); + + Point aImagePos( pInfo->mrStartPos ); + aImagePos.AdjustX(aOutSize.Width() - aImageSize.Width() - aOffset.Width() ) ; + aImagePos.AdjustY((aOutSize.Height() - aImageSize.Height()) / 2 ); + + pInfo->mpOutDev->DrawImage( aImagePos, aImageSize, maSlideImage ); + + const bool bVertical = mrOutliner.IsVertical(); + const bool bRightToLeftPara = rEditEngine.IsRightToLeft( pInfo->mnPara ); + + LanguageType eLang = rEditEngine.GetDefaultLanguage(); + + Point aTextPos( aImagePos.X() - aOffset.Width(), pInfo->mrStartPos.Y() ); + vcl::Font aNewFont( OutputDevice::GetDefaultFont( DefaultFontType::SANS_UNICODE, eLang, GetDefaultFontFlags::NONE ) ); + aNewFont.SetFontSize( aFontSz ); + aNewFont.SetVertical( bVertical ); + aNewFont.SetOrientation( Degree10(bVertical ? 2700 : 0) ); + aNewFont.SetColor( COL_AUTO ); + pInfo->mpOutDev->SetFont( aNewFont ); + OUString aPageText = OUString::number( nPage ); + Size aTextSz; + aTextSz.setWidth( pInfo->mpOutDev->GetTextWidth( aPageText ) ); + aTextSz.setHeight( pInfo->mpOutDev->GetTextHeight() ); + if ( !bVertical ) + { + aTextPos.AdjustY((aOutSize.Height() - aTextSz.Height()) / 2 ); + if ( !bRightToLeftPara ) + { + aTextPos.AdjustX( -(aTextSz.Width()) ); + } + else + { + aTextPos.AdjustX(aTextSz.Width() ); + } + } + else + { + aTextPos.AdjustY( -(aTextSz.Width()) ); + aTextPos.AdjustX(nBulletHeight / 2 ); + } + pInfo->mpOutDev->DrawText( aTextPos, aPageText ); +} + +void OutlineView::UpdateParagraph( sal_Int32 nPara ) +{ + SfxItemSet aNewAttrs2( mrOutliner.GetParaAttribs( nPara ) ); + aNewAttrs2.Put( maLRSpaceItem ); + mrOutliner.SetParaAttribs( nPara, aNewAttrs2 ); +} + +void OutlineView::OnBeginPasteOrDrop( PasteOrDropInfos* /*pInfo*/ ) +{ +} + +/** this is called after a paste or drop operation, make sure that the newly inserted paragraphs + get the correct style sheet and new slides are inserted. */ +void OutlineView::OnEndPasteOrDrop( PasteOrDropInfos* pInfo ) +{ + SdPage* pPage = nullptr; + SfxStyleSheetBasePool* pStylePool = GetDoc().GetStyleSheetPool(); + + for( sal_Int32 nPara = pInfo->nStartPara; nPara <= pInfo->nEndPara; nPara++ ) + { + Paragraph* pPara = mrOutliner.GetParagraph( nPara ); + + bool bPage = ::Outliner::HasParaFlag( pPara, ParaFlag::ISPAGE ); + + if( !bPage ) + { + SdStyleSheet* pStyleSheet = dynamic_cast< SdStyleSheet* >( mrOutliner.GetStyleSheet( nPara ) ); + if( pStyleSheet ) + { + if ( pStyleSheet->GetApiName() == "title" ) + bPage = true; + } + } + + if( !pPara ) + continue; // fatality!? + + if( bPage && (nPara != pInfo->nStartPara) ) + { + // insert new slide for this paragraph + pPage = InsertSlideForParagraph( pPara ); + } + else + { + // newly inserted non page paragraphs get the outline style + if( !pPage ) + pPage = GetPageForParagraph( pPara ); + + if( pPage ) + { + SfxStyleSheet* pStyle = pPage->GetStyleSheetForPresObj( bPage ? PresObjKind::Title : PresObjKind::Outline ); + + if( !bPage ) + { + const sal_Int16 nDepth = mrOutliner.GetDepth( nPara ); + if( nDepth > 0 ) + { + OUString aStyleSheetName = pStyle->GetName(); + if (!aStyleSheetName.isEmpty()) + aStyleSheetName = aStyleSheetName.copy(0, aStyleSheetName.getLength() - 1); + aStyleSheetName += OUString::number( nDepth ); + pStyle = static_cast<SfxStyleSheet*>( pStylePool->Find( aStyleSheetName, pStyle->GetFamily() ) ); + DBG_ASSERT( pStyle, "sd::OutlineView::OnEndPasteOrDrop(), Style not found!" ); + } + } + + mrOutliner.SetStyleSheet( nPara, pStyle ); + } + + UpdateParagraph( nPara ); + } + } +} + + +OutlineViewModelChangeGuard::OutlineViewModelChangeGuard( OutlineView& rView ) +: mrView( rView ) +{ + mrView.BeginModelChange(); +} + +OutlineViewModelChangeGuard::~OutlineViewModelChangeGuard() COVERITY_NOEXCEPT_FALSE +{ + mrView.EndModelChange(); +} + + +OutlineViewPageChangesGuard::OutlineViewPageChangesGuard( OutlineView* pView ) +: mpView( pView ) +{ + if( mpView ) + mpView->IgnoreCurrentPageChanges( true ); +} + +OutlineViewPageChangesGuard::~OutlineViewPageChangesGuard() +{ + if( mpView ) + mpView->IgnoreCurrentPageChanges( false ); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/presvish.cxx b/sd/source/ui/view/presvish.cxx new file mode 100644 index 000000000..34a789f4d --- /dev/null +++ b/sd/source/ui/view/presvish.cxx @@ -0,0 +1,172 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <PresentationViewShell.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objface.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> +#include <svx/ruler.hxx> +#include <FrameView.hxx> +#include <DrawDocShell.hxx> +#include <slideshow.hxx> +#include <app.hrc> +#include <ViewShellBase.hxx> + +#include <fupoor.hxx> +#include <Window.hxx> + +#define ShellClass_PresentationViewShell +using namespace sd; +#include <sdslots.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::beans; + +namespace sd { + +SFX_IMPL_INTERFACE(PresentationViewShell, DrawViewShell) + +void PresentationViewShell::InitInterface_Impl() +{ + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS, SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server, + ToolbarId::Draw_Toolbox_Sd); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_APPLICATION, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Client | SfxVisibilityFlags::Viewer | SfxVisibilityFlags::ReadonlyDoc, + ToolbarId::Draw_Viewer_Toolbox); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_OPTIONS, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server, + ToolbarId::Draw_Options_Toolbox); + GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_COMMONTASK, SfxVisibilityFlags::Standard | SfxVisibilityFlags::Server, + ToolbarId::Draw_CommonTask_Toolbox); +} + + +PresentationViewShell::PresentationViewShell( ViewShellBase& rViewShellBase, vcl::Window* pParentWindow, FrameView* pFrameView) + : DrawViewShell(rViewShellBase, pParentWindow, PageKind::Standard, pFrameView) + , mnAbortSlideShowEvent(nullptr) +{ + if( GetDocSh() && GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + maOldVisArea = GetDocSh()->GetVisArea( ASPECT_CONTENT ); + meShellType = ST_PRESENTATION; +} + +PresentationViewShell::~PresentationViewShell() +{ + if (mnAbortSlideShowEvent) + Application::RemoveUserEvent(mnAbortSlideShowEvent); + + if( GetDocSh() && GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED && !maOldVisArea.IsEmpty() ) + GetDocSh()->SetVisArea( maOldVisArea ); +} + +void PresentationViewShell::FinishInitialization( FrameView* pFrameView ) +{ + DrawViewShell::Init(true); + + // Use the frame view that comes form the view shell that initiated our + // creation. + if (pFrameView != nullptr) + { + GetFrameView()->Disconnect(); + SetFrameView (pFrameView); + pFrameView->Connect(); + } + SetRuler(false); + WriteFrameViewData(); + + GetActiveWindow()->GrabFocus(); +} + +VclPtr<SvxRuler> PresentationViewShell::CreateHRuler(::sd::Window*) +{ + return nullptr; +} + +VclPtr<SvxRuler> PresentationViewShell::CreateVRuler(::sd::Window*) +{ + return nullptr; +} + +IMPL_LINK_NOARG(PresentationViewShell, AbortSlideShowHdl, void*, void) +{ + mnAbortSlideShowEvent = nullptr; + rtl::Reference<SlideShow> xSlideShow(SlideShow::GetSlideShow(GetViewShellBase())); + if (xSlideShow.is()) + xSlideShow->end(); +} + +void PresentationViewShell::Activate( bool bIsMDIActivate ) +{ + DrawViewShell::Activate( bIsMDIActivate ); + + if( bIsMDIActivate ) + { + SfxBoolItem aItem( SID_NAVIGATOR_INIT, true ); + + GetViewFrame()->GetDispatcher()->ExecuteList(SID_NAVIGATOR_INIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, { &aItem }); + + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( xSlideShow.is() ) + { + bool bSuccess = xSlideShow->activate(GetViewShellBase()); + if (!bSuccess) + { + /* tdf#64711 PresentationViewShell is deleted by 'end' due to end closing + the object shell. So if we call xSlideShow->end during Activate there are + a lot of places in the call stack of Activate which understandable don't + expect this ViewShell to be deleted during use. Defer to the next event + loop the abort of the slideshow + */ + if (!mnAbortSlideShowEvent) + mnAbortSlideShowEvent = Application::PostUserEvent(LINK(this, PresentationViewShell, AbortSlideShowHdl)); + } + } + + if( HasCurrentFunction() ) + GetCurrentFunction()->Activate(); + + ReadFrameViewData(mpFrameView); + } + + GetDocSh()->Connect( this ); +} + +void PresentationViewShell::Paint( const ::tools::Rectangle& /*rRect*/, ::sd::Window* ) +{ + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( xSlideShow.is() ) + xSlideShow->paint(); +} + +void PresentationViewShell::Resize() +{ + ViewShell::Resize(); // do not call DrawViewShell here! + + rtl::Reference< sd::SlideShow > xSlideshow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if( xSlideshow.is() ) + xSlideshow->resize(maViewSize); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdruler.cxx b/sd/source/ui/view/sdruler.cxx new file mode 100644 index 000000000..571ffb37f --- /dev/null +++ b/sd/source/ui/view/sdruler.cxx @@ -0,0 +1,148 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <Ruler.hxx> +#include <svl/ptitem.hxx> +#include <svx/ruler.hxx> +#include <svx/svxids.hrc> +#include <sfx2/ctrlitem.hxx> +#include <sfx2/bindings.hxx> +#include <vcl/commandevent.hxx> + +#include <View.hxx> +#include <DrawViewShell.hxx> +#include <Window.hxx> + +#include <helpids.h> + +namespace sd { + +/** + * Controller-Item for ruler + */ +class RulerCtrlItem : public SfxControllerItem +{ + Ruler &rRuler; + + protected: + virtual void StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState eState, + const SfxPoolItem* pItem ) override; + + public: + RulerCtrlItem(Ruler& rRlr, SfxBindings& rBind); +}; + +RulerCtrlItem::RulerCtrlItem(Ruler& rRlr, SfxBindings& rBind) +: SfxControllerItem(SID_RULER_NULL_OFFSET, rBind) +, rRuler(rRlr) +{ +} + +void RulerCtrlItem::StateChangedAtToolBoxControl( sal_uInt16 nSId, SfxItemState, const SfxPoolItem* pState ) +{ + switch( nSId ) + { + case SID_RULER_NULL_OFFSET: + { + const SfxPointItem* pItem = dynamic_cast< const SfxPointItem* >(pState); + DBG_ASSERT(pState == nullptr || pItem != nullptr, "SfxPointItem expected"); + if ( pItem ) + rRuler.SetNullOffset(pItem->GetValue()); + } + break; + } +} + +Ruler::Ruler( DrawViewShell& rViewSh, vcl::Window* pParent, ::sd::Window* pWin, SvxRulerSupportFlags nRulerFlags, SfxBindings& rBindings, WinBits nWinStyle) + : SvxRuler(pParent, pWin, nRulerFlags, rBindings, nWinStyle) + , pDrViewShell(&rViewSh) +{ + rBindings.EnterRegistrations(); + pCtrlItem.reset( new RulerCtrlItem(*this, rBindings) ); + rBindings.LeaveRegistrations(); + + if ( nWinStyle & WB_HSCROLL ) + { + bHorz = true; + SetHelpId( HID_SD_RULER_HORIZONTAL ); + } + else + { + bHorz = false; + SetHelpId( HID_SD_RULER_VERTICAL ); + } +} + +Ruler::~Ruler() +{ + disposeOnce(); +} + +void Ruler::dispose() +{ + SfxBindings& rBindings = pCtrlItem->GetBindings(); + rBindings.EnterRegistrations(); + pCtrlItem.reset(); + rBindings.LeaveRegistrations(); + SvxRuler::dispose(); +} + +void Ruler::MouseButtonDown(const MouseEvent& rMEvt) +{ + Point aMPos = rMEvt.GetPosPixel(); + RulerType eType = GetRulerType(aMPos); + + if ( !pDrViewShell->GetView()->IsTextEdit() && + rMEvt.IsLeft() && rMEvt.GetClicks() == 1 && + (eType == RulerType::DontKnow || eType == RulerType::Outside) ) + { + pDrViewShell->StartRulerDrag(*this, rMEvt); + } + else + SvxRuler::MouseButtonDown(rMEvt); +} + +void Ruler::SetNullOffset(const Point& rOffset) +{ + ::tools::Long nOffset; + + if ( bHorz ) nOffset = rOffset.X(); + else nOffset = rOffset.Y(); + + SetNullOffsetLogic(nOffset); +} + +void Ruler::Command(const CommandEvent& rCEvt) +{ + if( rCEvt.GetCommand() == CommandEventId::ContextMenu && + !pDrViewShell->GetView()->IsTextEdit() ) + { + SvxRuler::Command( rCEvt ); + } +} + +void Ruler::ExtraDown() +{ + if( !pDrViewShell->GetView()->IsTextEdit() ) + SvxRuler::ExtraDown(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview.cxx b/sd/source/ui/view/sdview.cxx new file mode 100644 index 000000000..f27622fd1 --- /dev/null +++ b/sd/source/ui/view/sdview.cxx @@ -0,0 +1,1395 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/linguistic2/XSpellChecker1.hpp> + +#include <View.hxx> +#include <avmedia/mediawindow.hxx> +#include <editeng/outlobj.hxx> +#include <editeng/unolingu.hxx> +#include <o3tl/deleter.hxx> +#include <svx/obj3d.hxx> +#include <svx/fmview.hxx> +#include <editeng/outliner.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdundo.hxx> + +#include <vcl/settings.hxx> + +#include <officecfg/Office/Common.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/svdpagv.hxx> +#include <svx/svdoutl.hxx> +#include <svx/sdr/contact/displayinfo.hxx> + +#include <svx/svdetc.hxx> +#include <editeng/editstat.hxx> + +#include <sfx2/viewfrm.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <svx/xfillit0.hxx> + +#include <app.hrc> +#include <strings.hrc> +#include <Window.hxx> +#include <Client.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> +#include <sdmod.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <ViewClipboard.hxx> +#include <undo/undomanager.hxx> +#include <svx/sdr/contact/viewobjectcontact.hxx> +#include <svx/sdr/contact/viewcontact.hxx> +#include <svx/svdotable.hxx> +#include <EventMultiplexer.hxx> +#include <ViewShellBase.hxx> +#include <ViewShell.hxx> + +#include <basegfx/polygon/b2dpolygontools.hxx> +#include <basegfx/color/bcolor.hxx> +#include <drawinglayer/attribute/lineattribute.hxx> +#include <drawinglayer/attribute/strokeattribute.hxx> +#include <drawinglayer/primitive2d/textlayoutdevice.hxx> +#include <drawinglayer/primitive2d/PolygonStrokePrimitive2D.hxx> +#include <svx/sdr/contact/objectcontact.hxx> +#include <svx/sdr/table/tablecontroller.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <drawinglayer/primitive2d/textprimitive2d.hxx> +#include <svx/unoapi.hxx> +#include <basegfx/matrix/b2dhommatrixtools.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> +#include <LibreOfficeKit/LibreOfficeKitEnums.h> +#include <DrawController.hxx> +#include <svtools/optionsdrawinglayer.hxx> + +#include <memory> +#include <numeric> + +using namespace com::sun::star; +using namespace com::sun::star::uno; +using namespace sdr::table; +namespace sd { + +View::View( + SdDrawDocument& rDrawDoc, + OutputDevice* pOutDev, + ViewShell* pViewShell) +: FmFormView(rDrawDoc, pOutDev), + mrDoc(rDrawDoc), + mpDocSh(rDrawDoc.GetDocSh()), + mpViewSh(pViewShell), + mpDropMarkerObj(nullptr), + mnDragSrcPgNum(SDRPAGE_NOTFOUND), + mnAction(DND_ACTION_NONE), + maDropErrorIdle("sd View DropError"), + maDropInsertFileIdle("sd View DropInsertFile"), + mnLockRedrawSmph(0), + mbIsDropAllowed(true), + maSmartTags(*this), + mpClipboard (new ViewClipboard (*this)) +{ + // #i73602# Use default from the configuration + SetBufferedOverlayAllowed(SvtOptionsDrawinglayer::IsOverlayBuffer_DrawImpress()); + + // #i74769#, #i75172# Use default from the configuration + SetBufferedOutputAllowed(SvtOptionsDrawinglayer::IsPaintBuffer_DrawImpress()); + + EnableExtendedKeyInputDispatcher(false); + EnableExtendedMouseEventDispatcher(false); + + SetUseIncompatiblePathCreateInterface(false); + + SetMinMoveDistancePixel(2); + SetHitTolerancePixel(2); + SetMeasureLayer(sUNO_LayerName_measurelines); + + // Timer for delayed drop (has to be for MAC) + maDropErrorIdle.SetInvokeHandler( LINK(this, View, DropErrorHdl) ); + maDropInsertFileIdle.SetInvokeHandler( LINK(this, View, DropInsertFileHdl) ); +} + +void View::ImplClearDrawDropMarker() +{ + mpDropMarker.reset(); +} + +View::~View() +{ + maSmartTags.Dispose(); + + // release content of selection clipboard, if we own the content + ClearSelectionClipboard(); + + if (mxDropMediaSizeListener) + { + suppress_fun_call_w_exception(mxDropMediaSizeListener->dispose()); + mxDropMediaSizeListener.clear(); + } + + maDropErrorIdle.Stop(); + maDropInsertFileIdle.Stop(); + + ImplClearDrawDropMarker(); + + while(PaintWindowCount()) + { + // remove all registered OutDevs + suppress_fun_call_w_exception(DeleteWindowFromPaintView(GetFirstOutputDevice())); + } +} + +namespace { + +class ViewRedirector : public sdr::contact::ViewObjectContactRedirector +{ +public: + ViewRedirector(); + + // all default implementations just call the same methods at the original. To do something + // different, override the method and at least do what the method does. + virtual void createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) override; +}; + +} + +ViewRedirector::ViewRedirector() +{ +} + +void ViewRedirector::createRedirectedPrimitive2DSequence( + const sdr::contact::ViewObjectContact& rOriginal, + const sdr::contact::DisplayInfo& rDisplayInfo, + drawinglayer::primitive2d::Primitive2DDecompositionVisitor& rVisitor) +{ + SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject(); + SdrPage* pSdrPage = pObject ? pObject->getSdrPageFromSdrObject() : nullptr; + if(!pObject || !pSdrPage) + { + // not a SdrObject visualisation (maybe e.g. page) or no page + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(rOriginal, rDisplayInfo, rVisitor); + return; + } + + const bool bDoCreateGeometry(pSdrPage->checkVisibility( rOriginal, rDisplayInfo, true )); + + if(!bDoCreateGeometry && + (( pObject->GetObjInventor() != SdrInventor::Default ) || ( pObject->GetObjIdentifier() != SdrObjKind::Page )) ) + return; + + PresObjKind eKind(PresObjKind::NONE); + const bool bSubContentProcessing(rDisplayInfo.GetSubContentActive()); + const bool bIsMasterPageObject(pSdrPage->IsMasterPage()); + const bool bIsPrinting(rOriginal.GetObjectContact().isOutputToPrinter()); + const SdrPageView* pPageView = rOriginal.GetObjectContact().TryToGetSdrPageView(); + const SdrPage* pVisualizedPage = GetSdrPageFromXDrawPage(rOriginal.GetObjectContact().getViewInformation2D().getVisualizedPage()); + const SdPage* pObjectsSdPage = dynamic_cast< SdPage* >(pSdrPage); + const bool bIsInsidePageObj(pPageView && pPageView->GetPage() != pVisualizedPage); + + // check if we need to draw a placeholder border. Never do it for + // objects inside a SdrPageObj and never when printing + if(!bIsInsidePageObj && !bIsPrinting) + { + bool bCreateOutline(false); + + if( pObject->IsEmptyPresObj() && dynamic_cast< SdrTextObj *>( pObject ) != nullptr ) + { + if( !bSubContentProcessing || !pObject->IsNotVisibleAsMaster() ) + { + eKind = pObjectsSdPage ? pObjectsSdPage->GetPresObjKind(pObject) : PresObjKind::NONE; + bCreateOutline = true; + } + } + else if( ( pObject->GetObjInventor() == SdrInventor::Default ) && ( pObject->GetObjIdentifier() == SdrObjKind::Text ) ) + { + if( pObjectsSdPage ) + { + eKind = pObjectsSdPage->GetPresObjKind(pObject); + + if((eKind == PresObjKind::Footer) || (eKind == PresObjKind::Header) || (eKind == PresObjKind::DateTime) || (eKind == PresObjKind::SlideNumber) ) + { + if( !bSubContentProcessing ) + { + // only draw a boundary for header&footer objects on the masterpage itself + bCreateOutline = true; + } + } + } + } + else if( ( pObject->GetObjInventor() == SdrInventor::Default ) && ( pObject->GetObjIdentifier() == SdrObjKind::Page ) ) + { + // only for handout page, else this frame will be created for each + // page preview object in SlideSorter and PagePane + if(pObjectsSdPage && PageKind::Handout == pObjectsSdPage->GetPageKind()) + { + bCreateOutline = true; + } + } + + if(bCreateOutline) + { + // empty presentation objects get a gray frame + const svtools::ColorConfig aColorConfig; + const svtools::ColorConfigValue aColor( aColorConfig.GetColorValue( svtools::OBJECTBOUNDARIES ) ); + + if( aColor.bIsVisible ) + { + // get basic object transformation + const basegfx::BColor aRGBColor(aColor.nColor.getBColor()); + basegfx::B2DHomMatrix aObjectMatrix; + basegfx::B2DPolyPolygon aObjectPolyPolygon; + pObject->TRGetBaseGeometry(aObjectMatrix, aObjectPolyPolygon); + + // create dashed border + { + // create object polygon + basegfx::B2DPolygon aPolygon(basegfx::utils::createUnitPolygon()); + aPolygon.transform(aObjectMatrix); + + // create line and stroke attribute + ::std::vector< double > aDotDashArray { 160.0, 80.0 }; + + const double fFullDotDashLen(::std::accumulate(aDotDashArray.begin(), aDotDashArray.end(), 0.0)); + const drawinglayer::attribute::LineAttribute aLine(aRGBColor); + const drawinglayer::attribute::StrokeAttribute aStroke(std::move(aDotDashArray), fFullDotDashLen); + + // create primitive and add + const drawinglayer::primitive2d::Primitive2DReference xRef(new drawinglayer::primitive2d::PolygonStrokePrimitive2D( + aPolygon, + aLine, + aStroke)); + rVisitor.visit(xRef); + } + + // now paint the placeholder description, but only when masterpage + // is displayed as page directly (MasterPage view) + if(!bSubContentProcessing && bIsMasterPageObject) + { + OUString aObjectString; + + switch( eKind ) + { + case PresObjKind::Title: + { + if(pObjectsSdPage && pObjectsSdPage->GetPageKind() == PageKind::Standard) + { + static OUString aTitleAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_TITLE)); + aObjectString = aTitleAreaStr; + } + + break; + } + case PresObjKind::Outline: + { + static OUString aOutlineAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_OUTLINE)); + aObjectString = aOutlineAreaStr; + break; + } + case PresObjKind::Footer: + { + static OUString aFooterAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_FOOTER)); + aObjectString = aFooterAreaStr; + break; + } + case PresObjKind::Header: + { + static OUString aHeaderAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_HEADER)); + aObjectString = aHeaderAreaStr; + break; + } + case PresObjKind::DateTime: + { + static OUString aDateTimeStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_DATETIME)); + aObjectString = aDateTimeStr; + break; + } + case PresObjKind::Notes: + { + static OUString aDateTimeStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_NOTES)); + aObjectString = aDateTimeStr; + break; + } + case PresObjKind::SlideNumber: + { + if(pObjectsSdPage && pObjectsSdPage->GetPageKind() == PageKind::Standard) + { + static OUString aSlideAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_SLIDE)); + aObjectString = aSlideAreaStr; + } + else + { + static OUString aNumberAreaStr(SdResId(STR_PLACEHOLDER_DESCRIPTION_NUMBER)); + aObjectString = aNumberAreaStr; + } + break; + } + default: + { + break; + } + } + + if( !aObjectString.isEmpty() ) + { + // decompose object matrix to be able to place text correctly + basegfx::B2DTuple aScale; + basegfx::B2DTuple aTranslate; + double fRotate, fShearX; + aObjectMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + + // create font + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >( pObject ); + const SdrTextVertAdjust eTVA(pTextObj ? pTextObj->GetTextVerticalAdjust() : SDRTEXTVERTADJUST_CENTER); + vcl::Font aScaledVclFont; + + // use a text size factor to get more reliable text sizes from the text layouter + // (and from vcl), tipp from HDU + static const sal_uInt32 nTextSizeFactor(100); + + // use a factor to get more linear text size calculations + aScaledVclFont.SetFontHeight( 500 * nTextSizeFactor ); + + // get basic geometry and get text size + drawinglayer::primitive2d::TextLayouterDevice aTextLayouter; + aTextLayouter.setFont(aScaledVclFont); + const sal_Int32 nTextLength(aObjectString.getLength()); + + // do not forget to use the factor again to get the width for the 500 + const double fTextWidth(aTextLayouter.getTextWidth(aObjectString, 0, nTextLength) * (1.0 / nTextSizeFactor)); + const double fTextHeight(aTextLayouter.getTextHeight() * (1.0 / nTextSizeFactor)); + + // calculate text primitive position. If text is at bottom, use top for + // the extra text and vice versa + const double fHorDist(125); + const double fVerDist(125); + const double fPosX((aTranslate.getX() + aScale.getX()) - fTextWidth - fHorDist); + const double fPosY((SDRTEXTVERTADJUST_BOTTOM == eTVA) + ? aTranslate.getY() - fVerDist + fTextHeight + : (aTranslate.getY() + aScale.getY()) - fVerDist); + + // get font attributes; use normally scaled font + vcl::Font aVclFont; + basegfx::B2DVector aTextSizeAttribute; + + aVclFont.SetFontHeight( 500 ); + + const drawinglayer::attribute::FontAttribute aFontAttribute( + drawinglayer::primitive2d::getFontAttributeFromVclFont( + aTextSizeAttribute, + aVclFont, + false, + false)); + + // fill text matrix + const basegfx::B2DHomMatrix aTextMatrix(basegfx::utils::createScaleShearXRotateTranslateB2DHomMatrix( + aTextSizeAttribute.getX(), aTextSizeAttribute.getY(), + fShearX, + fRotate, + fPosX, fPosY)); + + // create DXTextArray (can be empty one) + ::std::vector< double > aDXArray{}; + + // create locale; this may need some more information in the future + const css::lang::Locale aLocale; + + // create primitive and add + const drawinglayer::primitive2d::Primitive2DReference xRef( + new drawinglayer::primitive2d::TextSimplePortionPrimitive2D( + aTextMatrix, + aObjectString, + 0, + nTextLength, + std::move(aDXArray), + aFontAttribute, + aLocale, + aRGBColor)); + rVisitor.visit(xRef); + } + } + } + } + } + + if(bDoCreateGeometry) + { + sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence( + rOriginal, + rDisplayInfo, rVisitor); + } +} + +namespace +{ + void setOutlinerBgFromPage(::Outliner& rOutl, SdrPageView& rPgView, bool bScreenDisplay) + { + SdPage* pPage = static_cast<SdPage*>(rPgView.GetPage()); + if (pPage) + { + // #i75566# Name change GetBackgroundColor -> GetPageBackgroundColor and + // hint value if screen display. Only then the AutoColor mechanisms shall be applied + rOutl.SetBackgroundColor(pPage->GetPageBackgroundColor(&rPgView, bScreenDisplay)); + } + } +} + +/** + * The event will be forwarded to the View + */ +void View::CompleteRedraw(OutputDevice* pOutDev, const vcl::Region& rReg, sdr::contact::ViewObjectContactRedirector* pRedirector /*=0*/) +{ + // execute ?? + if (mnLockRedrawSmph != 0) + return; + + SdrPageView* pPgView = GetSdrPageView(); + + if (pPgView) + { + SdPage* pPage = static_cast<SdPage*>( pPgView->GetPage() ); + if( pPage ) + { + SdrOutliner& rOutl = mrDoc.GetDrawOutliner(); + bool bScreenDisplay(true); + + // #i75566# printing; suppress AutoColor BackgroundColor generation + // for visibility reasons by giving GetPageBackgroundColor() + // the needed hint + // #i75566# PDF export; suppress AutoColor BackgroundColor generation (see printing) + if (pOutDev && ((OUTDEV_PRINTER == pOutDev->GetOutDevType()) + || (OUTDEV_PDF == pOutDev->GetOutDevType()))) + bScreenDisplay = false; + + setOutlinerBgFromPage(rOutl, *pPgView, bScreenDisplay); + } + } + + ViewRedirector aViewRedirector; + FmFormView::CompleteRedraw(pOutDev, rReg, pRedirector ? pRedirector : &aViewRedirector); +} + +void View::MarkListHasChanged() +{ + FmFormView::MarkListHasChanged(); + + if( GetMarkedObjectCount() > 0 ) + maSmartTags.deselect(); +} + +bool View::SetAttributes(const SfxItemSet& rSet, bool bReplaceAll, bool /*bSlide*/, bool /*bMaster*/) +{ + bool bOk = FmFormView::SetAttributes(rSet, bReplaceAll); + return bOk; +} + +void View::GetAttributes( SfxItemSet& rTargetSet, bool bOnlyHardAttr ) const +{ + FmFormView::GetAttributes( rTargetSet, bOnlyHardAttr ); +} + +/** + * Is a presentation object selected? + */ +bool View::IsPresObjSelected(bool bOnPage, bool bOnMasterPage, bool bCheckPresObjListOnly, bool bCheckLayoutOnly) const +{ + SdrMarkList* pMarkList; + + if (mnDragSrcPgNum != SDRPAGE_NOTFOUND && + mnDragSrcPgNum != GetSdrPageView()->GetPage()->GetPageNum()) + { + /* Drag&Drop is in progress + Source and destination page are different: + we use the saved mark list */ + pMarkList = mpDragSrcMarkList.get(); + } + else + { + // We use the current mark list + pMarkList = new SdrMarkList(GetMarkedObjectList()); + } + + SdrMark* pMark; + SdPage* pPage; + + bool bSelected = false; + bool bMasterPage = false; + + for (size_t nMark = pMarkList->GetMarkCount(); nMark && !bSelected; ) + { + --nMark; + // Backwards through mark list + pMark = pMarkList->GetMark(nMark); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + + if ( pObj && ( bCheckPresObjListOnly || pObj->IsEmptyPresObj() || pObj->GetUserCall() ) ) + { + pPage = static_cast<SdPage*>( pObj->getSdrPageFromSdrObject() ); + bMasterPage = pPage && pPage->IsMasterPage(); + + if ( (bMasterPage && bOnMasterPage) || (!bMasterPage && bOnPage) ) + { + if ( pPage && pPage->IsPresObj(pObj) ) + { + if( bCheckLayoutOnly ) + { + PresObjKind eKind = pPage->GetPresObjKind(pObj); + + if((eKind != PresObjKind::Footer) && (eKind != PresObjKind::Header) && (eKind != PresObjKind::DateTime) && (eKind != PresObjKind::SlideNumber) ) + bSelected = true; + } + else + { + bSelected = true; + } + } + } + } + } + + if (pMarkList != mpDragSrcMarkList.get()) + { + delete pMarkList; + } + + return bSelected; +} + +void View::SelectAll() +{ + if ( IsTextEdit() ) + { + OutlinerView* pOLV = GetTextEditOutlinerView(); + const ::Outliner* pOutliner = GetTextEditOutliner(); + pOLV->SelectRange( 0, pOutliner->GetParagraphCount() ); + } + else + { + MarkAll(); + } +} + +bool View::SetStyleSheet(SfxStyleSheet* pStyleSheet, bool bDontRemoveHardAttr) +{ + // forward to SdrView + FmFormView::SetStyleSheet(pStyleSheet, bDontRemoveHardAttr); + return true; +} + +/** + * Start text input + */ +static void SetSpellOptions( const SdDrawDocument& rDoc, EEControlBits& rCntrl ) +{ + bool bOnlineSpell = rDoc.GetOnlineSpell(); + + if( bOnlineSpell ) + rCntrl |= EEControlBits::ONLINESPELLING; + else + rCntrl &= ~EEControlBits::ONLINESPELLING; +} + +void OutlinerMasterViewFilter::Start(SdrOutliner *pOutl) +{ + m_pOutl = pOutl; + OutlinerView* pOutlView = m_pOutl->GetView(0); + m_bReadOnly = pOutlView->IsReadOnly(); + pOutlView->SetReadOnly(true); +} + +void OutlinerMasterViewFilter::End() +{ + if (m_pOutl) + { + OutlinerView* pOutlView = m_pOutl->GetView(0); + pOutlView->SetReadOnly(m_bReadOnly); + m_pOutl = nullptr; + } +} + +SfxViewShell* View::GetSfxViewShell() const +{ + SfxViewShell* pRet = nullptr; + + if (mpViewSh) + pRet = &mpViewSh->GetViewShellBase(); + + return pRet; +} + +// Create a new view-local UndoManager manager for Impress/Draw +std::unique_ptr<SdrUndoManager> View::createLocalTextUndoManager() +{ + std::unique_ptr<SdrUndoManager> pUndoManager(new sd::UndoManager); + pUndoManager->SetDocShell(mpDocSh); + return pUndoManager; +} + +bool View::SdrBeginTextEdit( + SdrObject* pObj, SdrPageView* pPV, vcl::Window* pWin, + bool bIsNewObj, + SdrOutliner* pOutl, OutlinerView* pGivenOutlinerView, + bool bDontDeleteOutliner, bool bOnlyOneView, bool bGrabFocus ) +{ + SdrPage* pPage = pObj ? pObj->getSdrPageFromSdrObject() : nullptr; + bool bMasterPage = pPage && pPage->IsMasterPage(); + + GetViewShell()->GetViewShellBase().GetEventMultiplexer()->MultiplexEvent( + EventMultiplexerEventId::BeginTextEdit, static_cast<void*>(pObj) ); + + if( pOutl==nullptr && pObj ) + pOutl = SdrMakeOutliner(OutlinerMode::TextObject, pObj->getSdrModelFromSdrObject()).release(); + + // make draw&impress specific initialisations + if( pOutl ) + { + pOutl->SetStyleSheetPool(static_cast<SfxStyleSheetPool*>( mrDoc.GetStyleSheetPool() )); + pOutl->SetCalcFieldValueHdl(LINK(SD_MOD(), SdModule, CalcFieldValueHdl)); + EEControlBits nCntrl = pOutl->GetControlWord(); + nCntrl |= EEControlBits::ALLOWBIGOBJS; + nCntrl |= EEControlBits::MARKFIELDS; + nCntrl |= EEControlBits::AUTOCORRECT; + + nCntrl &= ~EEControlBits::ULSPACESUMMATION; + if ( mrDoc.IsSummationOfParagraphs() ) + nCntrl |= EEControlBits::ULSPACESUMMATION; + + SetSpellOptions( mrDoc, nCntrl ); + + pOutl->SetControlWord(nCntrl); + + Reference< linguistic2::XSpellChecker1 > xSpellChecker( LinguMgr::GetSpellChecker() ); + if ( xSpellChecker.is() ) + pOutl->SetSpeller( xSpellChecker ); + + Reference< linguistic2::XHyphenator > xHyphenator( LinguMgr::GetHyphenator() ); + if( xHyphenator.is() ) + pOutl->SetHyphenator( xHyphenator ); + + pOutl->SetDefaultLanguage( Application::GetSettings().GetLanguageTag().getLanguageType() ); + } + + bool bReturn = FmFormView::SdrBeginTextEdit( + pObj, pPV, pWin, bIsNewObj, pOutl, + pGivenOutlinerView, bDontDeleteOutliner, + bOnlyOneView, bGrabFocus); + + if ( mpViewSh ) + { + mpViewSh->GetViewShellBase().GetDrawController().FireSelectionChangeListener(); + + if (pObj && pObj->GetObjIdentifier() == SdrObjKind::Table) + mpViewSh->UpdateScrollBars(); + + if (comphelper::LibreOfficeKit::isActive()) + { + if (OutlinerView* pView = GetTextEditOutlinerView()) + { + ::tools::Rectangle aRectangle = pView->GetOutputArea(); + if (pWin && pWin->GetMapMode().GetMapUnit() == MapUnit::Map100thMM) + { + aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip); + } + OString sRectangle = aRectangle.toString(); + SfxLokHelper::notifyOtherViews(&mpViewSh->GetViewShellBase(), LOK_CALLBACK_VIEW_LOCK, "rectangle", sRectangle); + } + } + } + + if (::Outliner* pOL = bReturn ? GetTextEditOutliner() : nullptr) + { + if (pObj) + { + if( pObj->GetObjInventor() == SdrInventor::Default && pObj->GetObjIdentifier() == SdrObjKind::Table ) + { + Color aBackground = GetTextEditBackgroundColor(*this); + pOL->SetBackgroundColor( aBackground ); + } + else + { + // tdf#148140 Set the background to determine autocolor. + // Use any explicit bg with fallback to underlying page if + // none found + if (!pObj->setSuitableOutlinerBg(*pOL) && pPV) + setOutlinerBgFromPage(*pOL, *pPV, true); + } + } + + pOL->SetParaInsertedHdl(LINK(this, View, OnParagraphInsertedHdl)); + pOL->SetParaRemovingHdl(LINK(this, View, OnParagraphRemovingHdl)); + } + + if (bMasterPage && bReturn && pOutl) + { + const SdrTextObj* pTextObj = pOutl->GetTextObj(); + const SdPage* pSdPage = pTextObj ? static_cast<const SdPage*>(pTextObj->getSdrPageFromSdrObject()) : nullptr; + const PresObjKind eKind = pSdPage ? pSdPage->GetPresObjKind(const_cast<SdrTextObj*>(pTextObj)) : PresObjKind::NONE; + switch (eKind) + { + case PresObjKind::Title: + case PresObjKind::Outline: + case PresObjKind::Text: + maMasterViewFilter.Start(pOutl); + break; + default: + break; + } + } + + return bReturn; +} + +/** ends current text editing */ +SdrEndTextEditKind View::SdrEndTextEdit(bool bDontDeleteReally) +{ + maMasterViewFilter.End(); + + ::tools::WeakReference<SdrTextObj> xObj( GetTextEditObject() ); + + bool bDefaultTextRestored = RestoreDefaultText( xObj.get() ); + + SdrEndTextEditKind eKind = FmFormView::SdrEndTextEdit(bDontDeleteReally); + + if( bDefaultTextRestored ) + { + if( xObj.is() && !xObj->IsEmptyPresObj() ) + { + xObj->SetEmptyPresObj( true ); + } + else + { + eKind = SdrEndTextEditKind::Unchanged; + } + } + else if( xObj.is() && xObj->IsEmptyPresObj() ) + { + SdrTextObj* pObj = xObj.get(); + if( pObj && pObj->HasText() ) + { + SdrPage* pPage = pObj->getSdrPageFromSdrObject(); + if( !pPage || !pPage->IsMasterPage() ) + pObj->SetEmptyPresObj( false ); + } + } + + GetViewShell()->GetViewShellBase().GetEventMultiplexer()->MultiplexEvent( + EventMultiplexerEventId::EndTextEdit, + static_cast<void*>(xObj.get()) ); + + if( xObj.is() ) + { + if ( mpViewSh ) + { + mpViewSh->GetViewShellBase().GetDrawController().FireSelectionChangeListener(); + + if (comphelper::LibreOfficeKit::isActive()) + SfxLokHelper::notifyOtherViews(&mpViewSh->GetViewShellBase(), LOK_CALLBACK_VIEW_LOCK, "rectangle", "EMPTY"); + + } + + SdPage* pPage = dynamic_cast< SdPage* >( xObj->getSdrPageFromSdrObject() ); + if( pPage ) + pPage->onEndTextEdit( xObj.get() ); + } + + return eKind; +} + +/** restores the default text if the given text object is currently in edit mode and + no text has been entered already. Is only useful just before text edit ends. */ +bool View::RestoreDefaultText( SdrTextObj* pTextObj ) +{ + bool bRestored = false; + + if( pTextObj && (pTextObj == GetTextEditObject()) ) + { + if( !pTextObj->HasText() ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pTextObj->getSdrPageFromSdrObject() ); + + if(pPage) + { + bRestored = pPage->RestoreDefaultText( pTextObj ); + if( bRestored ) + { + SdrOutliner* pOutliner = GetTextEditOutliner(); + pTextObj->SetTextEditOutliner( pOutliner ); + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + if (pOutliner) + pOutliner->SetText(*pParaObj); + } + } + } + } + + return bRestored; +} + +/** + * Sets the original size of the marked objects. + */ +void View::SetMarkedOriginalSize() +{ + std::unique_ptr<SdrUndoGroup> pUndoGroup(new SdrUndoGroup(mrDoc)); + const size_t nCount = GetMarkedObjectCount(); + bool bOK = false; + + for( size_t i = 0; i < nCount; ++i ) + { + SdrObject* pObj = GetMarkedObjectByIndex(i); + + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + if( pObj->GetObjIdentifier() == SdrObjKind::OLE2 ) + { + uno::Reference < embed::XEmbeddedObject > xObj = static_cast<SdrOle2Obj*>(pObj)->GetObjRef(); + if( xObj.is() ) + { + // TODO/LEAN: working with VisualArea can switch object to running state + + sal_Int64 nAspect = static_cast<SdrOle2Obj*>(pObj)->GetAspect(); + Size aOleSize; + + if ( nAspect == embed::Aspects::MSOLE_ICON ) + { + MapMode aMap100( MapUnit::Map100thMM ); + aOleSize = static_cast<SdrOle2Obj*>(pObj)->GetOrigObjSize( &aMap100 ); + bOK = true; + } + else + { + MapUnit aUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + try + { + awt::Size aSz = xObj->getVisualAreaSize( nAspect ); + aOleSize = OutputDevice::LogicToLogic(Size(aSz.Width, aSz.Height), MapMode(aUnit), MapMode(MapUnit::Map100thMM)); + bOK = true; + } + catch( embed::NoVisualAreaSizeException& ) + {} + } + + if ( bOK ) + { + ::tools::Rectangle aDrawRect( pObj->GetLogicRect() ); + + pUndoGroup->AddAction( mrDoc.GetSdrUndoFactory().CreateUndoGeoObject( *pObj ) ); + pObj->Resize( aDrawRect.TopLeft(), Fraction( aOleSize.Width(), aDrawRect.GetWidth() ), + Fraction( aOleSize.Height(), aDrawRect.GetHeight() ) ); + } + } + } + else if( pObj->GetObjIdentifier() == SdrObjKind::Graphic ) + { + const SdrGrafObj* pSdrGrafObj = static_cast< const SdrGrafObj* >(pObj); + const Size aSize = pSdrGrafObj->getOriginalSize( ); + pUndoGroup->AddAction( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*pObj ) ); + ::tools::Rectangle aRect( pObj->GetLogicRect() ); + aRect.SetSize( aSize ); + pObj->SetLogicRect( aRect ); + bOK = true; + } + } + } + + if( bOK ) + { + pUndoGroup->SetComment(SdResId(STR_UNDO_ORIGINALSIZE)); + mpDocSh->GetUndoManager()->AddUndoAction(std::move(pUndoGroup)); + } +} + +/** + * Connect OLE object to client. + */ +void View::DoConnect(SdrOle2Obj* pObj) +{ + if (!mpViewSh) + return; + + uno::Reference < embed::XEmbeddedObject > xObj( pObj->GetObjRef() ); + if( !xObj.is() ) + return; + + ::sd::Window* pWindow = mpViewSh->GetActiveWindow(); + SfxInPlaceClient* pSdClient = mpViewSh-> GetViewShellBase().FindIPClient( xObj, pWindow ); + if ( pSdClient ) + return; + + pSdClient = new Client(pObj, mpViewSh, pWindow); + ::tools::Rectangle aRect = pObj->GetLogicRect(); + { + // TODO/LEAN: working with visual area can switch object to running state + Size aDrawSize = aRect.GetSize(); + + MapMode aMapMode( mrDoc.GetScaleUnit() ); + Size aObjAreaSize = pObj->GetOrigObjSize( &aMapMode ); + + Fraction aScaleWidth (aDrawSize.Width(), aObjAreaSize.Width() ); + Fraction aScaleHeight(aDrawSize.Height(), aObjAreaSize.Height() ); + aScaleWidth.ReduceInaccurate(10); // compatible to SdrOle2Obj + aScaleHeight.ReduceInaccurate(10); + pSdClient->SetSizeScale(aScaleWidth, aScaleHeight); + + // visible area is only changed in-place! + // the object area must be set after the scaling, since it triggers resize + aRect.SetSize(aObjAreaSize); + pSdClient->SetObjArea(aRect); + } +} + +bool View::IsMorphingAllowed() const +{ + const SdrMarkList& rMarkList = GetMarkedObjectList(); + bool bRet = false; + + if ( rMarkList.GetMarkCount() == 2 ) + { + const SdrObject* pObj1 = rMarkList.GetMark( 0 )->GetMarkedSdrObj(); + const SdrObject* pObj2 = rMarkList.GetMark( 1 )->GetMarkedSdrObj(); + const SdrObjKind nKind1 = pObj1->GetObjIdentifier(); + const SdrObjKind nKind2 = pObj2->GetObjIdentifier(); + + if ( ( nKind1 != SdrObjKind::Text && nKind2 != SdrObjKind::Text ) && + ( nKind1 != SdrObjKind::TitleText && nKind2 != SdrObjKind::TitleText ) && + ( nKind1 != SdrObjKind::OutlineText && nKind2 != SdrObjKind::OutlineText ) && + ( nKind1 != SdrObjKind::Group && nKind2 != SdrObjKind::Group ) && + ( nKind1 != SdrObjKind::Line && nKind2 != SdrObjKind::Line ) && + ( nKind1 != SdrObjKind::PolyLine && nKind2 != SdrObjKind::PolyLine ) && + ( nKind1 != SdrObjKind::PathLine && nKind2 != SdrObjKind::PathLine ) && + ( nKind1 != SdrObjKind::FreehandLine && nKind2 != SdrObjKind::FreehandLine ) && + ( nKind1 != SdrObjKind::PathPolyLine && nKind2 != SdrObjKind::PathPolyLine ) && + ( nKind1 != SdrObjKind::Measure && nKind2 != SdrObjKind::Measure ) && + ( nKind1 != SdrObjKind::Edge && nKind2 != SdrObjKind::Edge ) && + ( nKind1 != SdrObjKind::Graphic && nKind2 != SdrObjKind::Graphic ) && + ( nKind1 != SdrObjKind::OLE2 && nKind2 != SdrObjKind::OLE2 ) && + ( nKind1 != SdrObjKind::Caption && nKind2 != SdrObjKind::Caption ) && + dynamic_cast< const E3dObject *>( pObj1 ) == nullptr && dynamic_cast< const E3dObject *>( pObj2 ) == nullptr ) + { + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLSTYLE> aSet1( mrDoc.GetPool() ); + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLSTYLE> aSet2( mrDoc.GetPool() ); + + aSet1.Put(pObj1->GetMergedItemSet()); + aSet2.Put(pObj2->GetMergedItemSet()); + + const drawing::FillStyle eFillStyle1 = aSet1.Get( XATTR_FILLSTYLE ).GetValue(); + const drawing::FillStyle eFillStyle2 = aSet2.Get( XATTR_FILLSTYLE ).GetValue(); + + if( ( eFillStyle1 == drawing::FillStyle_NONE || eFillStyle1 == drawing::FillStyle_SOLID ) && + ( eFillStyle2 == drawing::FillStyle_NONE || eFillStyle2 == drawing::FillStyle_SOLID ) ) + bRet = true; + } + } + + return bRet; +} + +bool View::IsVectorizeAllowed() const +{ + const SdrMarkList& rMarkList = GetMarkedObjectList(); + bool bRet = false; + + if( rMarkList.GetMarkCount() == 1 ) + { + const SdrGrafObj* pObj = dynamic_cast< const SdrGrafObj* >(rMarkList.GetMark( 0 )->GetMarkedSdrObj()); + + if(pObj) + { + if(GraphicType::Bitmap == pObj->GetGraphicType() && !pObj->isEmbeddedVectorGraphicData()) + { + bRet = true; + } + } + } + + return bRet; +} + +void View::onAccessibilityOptionsChanged() +{ + if( !mpViewSh ) + return; + + ::sd::Window* pWindow = mpViewSh->GetActiveWindow(); + if( !pWindow ) + return; + + const StyleSettings& rStyleSettings = pWindow->GetSettings().GetStyleSettings(); + + if( mpViewSh->GetViewFrame() && mpViewSh->GetViewFrame()->GetDispatcher() ) + { + sal_uInt16 nOutputSlot, nPreviewSlot; + + if( rStyleSettings.GetHighContrastMode() ) + { + nOutputSlot = SID_OUTPUT_QUALITY_CONTRAST; + } + else + { + nOutputSlot = SID_OUTPUT_QUALITY_COLOR; + } + + if( rStyleSettings.GetHighContrastMode() + && officecfg::Office::Common::Accessibility::IsForPagePreviews::get() ) + { + nPreviewSlot = SID_PREVIEW_QUALITY_CONTRAST; + } + else + { + nPreviewSlot = SID_PREVIEW_QUALITY_COLOR; + } + + mpViewSh->GetViewFrame()->GetDispatcher()->Execute( nOutputSlot, SfxCallMode::ASYNCHRON ); + mpViewSh->GetViewFrame()->GetDispatcher()->Execute( nPreviewSlot, SfxCallMode::ASYNCHRON ); + } + + mpViewSh->Invalidate(); +} + +IMPL_LINK( View, OnParagraphInsertedHdl, ::Outliner::ParagraphHdlParam, aParam, void ) +{ + SdrObject* pObj = GetTextEditObject(); + + if( aParam.pPara && pObj ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + if( pPage ) + pPage->onParagraphInserted( aParam.pOutliner, aParam.pPara, pObj ); + } +} + +/** + * Handler for the deletion of the pages (paragraphs). + */ +IMPL_LINK( View, OnParagraphRemovingHdl, ::Outliner::ParagraphHdlParam, aParam, void ) +{ + SdrObject* pObj = GetTextEditObject(); + + if( aParam.pPara && pObj ) + { + SdPage* pPage = dynamic_cast< SdPage* >( pObj->getSdrPageFromSdrObject() ); + if( pPage ) + pPage->onParagraphRemoving( aParam.pOutliner, aParam.pPara, pObj ); + } +} + +bool View::isRecordingUndo() const +{ + if( mrDoc.IsUndoEnabled() ) + { + sd::UndoManager* pUndoManager = mrDoc.GetUndoManager(); + return pUndoManager && pUndoManager->IsInListAction(); + } + else + { + return false; + } +} + +void View::AddCustomHdl() +{ + maSmartTags.addCustomHandles( maHdlList ); +} + +void View::updateHandles() +{ + AdjustMarkHdl(); +} + +SdrViewContext View::GetContext() const +{ + SdrViewContext eContext = SdrViewContext::Standard; + if( maSmartTags.getContext( eContext ) ) + return eContext; + else + return FmFormView::GetContext(); +} + +bool View::HasMarkablePoints() const +{ + if( maSmartTags.HasMarkablePoints() ) + return true; + else + return FmFormView::HasMarkablePoints(); +} + +sal_Int32 View::GetMarkablePointCount() const +{ + sal_Int32 nCount = FmFormView::GetMarkablePointCount(); + nCount += maSmartTags.GetMarkablePointCount(); + return nCount; +} + +bool View::HasMarkedPoints() const +{ + if( maSmartTags.HasMarkedPoints() ) + return true; + else + return FmFormView::HasMarkedPoints(); +} + +bool View::MarkPoint(SdrHdl& rHdl, bool bUnmark ) +{ + if( maSmartTags.MarkPoint( rHdl, bUnmark ) ) + return true; + else + return FmFormView::MarkPoint( rHdl, bUnmark ); +} + +bool View::MarkPoints(const ::tools::Rectangle* pRect, bool bUnmark) +{ + if( maSmartTags.MarkPoints( pRect, bUnmark ) ) + return true; + else + return FmFormView::MarkPoints( pRect, bUnmark ); +} + +void View::CheckPossibilities() +{ + FmFormView::CheckPossibilities(); + maSmartTags.CheckPossibilities(); +} + +void View::OnBeginPasteOrDrop( PasteOrDropInfos* pInfo ) +{ + SdrOutliner* pOutliner = GetTextEditOutliner(); + if (!pOutliner) + return; + + // Turn character attributes of the paragraph of the insert position into + // character-level attributes, so they are not lost when OnEndPasteOrDrop() + // sets the paragraph stylesheet. + SfxItemSet aSet(pOutliner->GetParaAttribs(pInfo->nStartPara)); + pOutliner->SetCharAttribs(pInfo->nStartPara, aSet); +} + +/** this is called after a paste or drop operation, make sure that the newly inserted paragraphs + get the correct style sheet. */ +void View::OnEndPasteOrDrop( PasteOrDropInfos* pInfo ) +{ + /* Style Sheet handling */ + SdrTextObj* pTextObj = GetTextEditObject(); + SdrOutliner* pOutliner = GetTextEditOutliner(); + if( !pOutliner || !pTextObj || !pTextObj->getSdrPageFromSdrObject() ) + return; + + SdPage* pPage = static_cast< SdPage* >( pTextObj->getSdrPageFromSdrObject() ); + const PresObjKind eKind = pPage->GetPresObjKind(pTextObj); + + // outline kinds are taken care of in Outliner::ImplSetLevelDependentStyleSheet + if( eKind == PresObjKind::Outline ) + return; + + SfxStyleSheet* pStyleSheet = nullptr; + if( eKind != PresObjKind::NONE ) + pStyleSheet = pPage->GetStyleSheetForPresObj(eKind); + else + pStyleSheet = pTextObj->GetStyleSheet(); + // just put the object style on each new paragraph + for ( sal_Int32 nPara = pInfo->nStartPara; nPara <= pInfo->nEndPara; nPara++ ) + { + pOutliner->SetStyleSheet( nPara, pStyleSheet ); + } +} + +bool View::ShouldToggleOn( + const bool bBulletOnOffMode, + const bool bNormalBullet) +{ + // If setting bullets/numbering by the dialog, always should toggle on. + if (!bBulletOnOffMode) + return true; + SdrModel* pSdrModel = GetModel(); + if (!pSdrModel) + return false; + + bool bToggleOn = false; + std::unique_ptr<SdrOutliner> pOutliner(SdrMakeOutliner(OutlinerMode::TextObject, *pSdrModel)); + const size_t nMarkCount = GetMarkedObjectCount(); + for (size_t nIndex = 0; nIndex < nMarkCount && !bToggleOn; ++nIndex) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >(GetMarkedObjectByIndex(nIndex)); + if (!pTextObj || pTextObj->IsTextEditActive()) + continue; + if( dynamic_cast< const SdrTableObj *>( pTextObj ) != nullptr) + { + SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >(pTextObj); + if (!pTableObj) + continue; + CellPos aStart, aEnd; + SvxTableController* pTableController = dynamic_cast< SvxTableController* >(getSelectionController().get()); + if (pTableController) + { + pTableController->getSelectedCells(aStart, aEnd); + } + else + { + aStart = SdrTableObj::getFirstCell(); + aEnd = pTableObj->getLastCell(); + } + sal_Int32 nColCount = pTableObj->getColumnCount(); + for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow && !bToggleOn; nRow++) + { + for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol && !bToggleOn; nCol++) + { + sal_Int32 nCellIndex = nRow * nColCount + nCol; + SdrText* pText = pTableObj->getText(nCellIndex); + if (!pText || !pText->GetOutlinerParaObject()) + continue; + pOutliner->SetText(*(pText->GetOutlinerParaObject())); + sal_Int16 nStatus = pOutliner->GetBulletsNumberingStatus(); + bToggleOn = (bNormalBullet && nStatus != 0) || (!bNormalBullet && nStatus != 1); + pOutliner->Clear(); + } + } + } + else + { + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + if (!pParaObj) + continue; + pOutliner->SetText(*pParaObj); + sal_Int16 nStatus = pOutliner->GetBulletsNumberingStatus(); + bToggleOn = (bNormalBullet && nStatus != 0) || (!bNormalBullet && nStatus != 1); + pOutliner->Clear(); + } + } + return bToggleOn; +} + +void View::ChangeMarkedObjectsBulletsNumbering( + const bool bToggle, + const bool bHandleBullets, + const SvxNumRule* pNumRule ) +{ + SdrModel* pSdrModel = GetModel(); + OutputDevice* pOut = GetFirstOutputDevice(); + vcl::Window* pWindow = pOut ? pOut->GetOwnerWindow() : nullptr; + if (!pSdrModel || !pWindow) + return; + + const bool bUndoEnabled = pSdrModel->IsUndoEnabled(); + std::unique_ptr<SdrUndoGroup> pUndoGroup(bUndoEnabled ? new SdrUndoGroup(*pSdrModel) : nullptr); + + const bool bToggleOn = ShouldToggleOn( bToggle, bHandleBullets ); + + std::unique_ptr<SdrOutliner> pOutliner(SdrMakeOutliner(OutlinerMode::TextObject, *pSdrModel)); + OutlinerView aOutlinerView(pOutliner.get(), pWindow); + + const size_t nMarkCount = GetMarkedObjectCount(); + for (size_t nIndex = 0; nIndex < nMarkCount; ++nIndex) + { + SdrTextObj* pTextObj = dynamic_cast< SdrTextObj* >(GetMarkedObjectByIndex(nIndex)); + if (!pTextObj || pTextObj->IsTextEditActive()) + continue; + if( dynamic_cast< SdrTableObj *>( pTextObj ) != nullptr) + { + SdrTableObj* pTableObj = dynamic_cast< SdrTableObj* >(pTextObj); + if (!pTableObj) + continue; + CellPos aStart, aEnd; + SvxTableController* pTableController = dynamic_cast< SvxTableController* >(getSelectionController().get()); + if (pTableController) + { + pTableController->getSelectedCells(aStart, aEnd); + } + else + { + aStart = SdrTableObj::getFirstCell(); + aEnd = pTableObj->getLastCell(); + } + sal_Int32 nColCount = pTableObj->getColumnCount(); + for (sal_Int32 nRow = aStart.mnRow; nRow <= aEnd.mnRow; nRow++) + { + for (sal_Int32 nCol = aStart.mnCol; nCol <= aEnd.mnCol; nCol++) + { + sal_Int32 nCellIndex = nRow * nColCount + nCol; + SdrText* pText = pTableObj->getText(nCellIndex); + if (!pText || !pText->GetOutlinerParaObject()) + continue; + + pOutliner->SetText(*(pText->GetOutlinerParaObject())); + if (bUndoEnabled) + { + pUndoGroup->AddAction(pSdrModel->GetSdrUndoFactory().CreateUndoObjectSetText(*pTextObj, nCellIndex)); + } + if ( !bToggleOn ) + { + aOutlinerView.SwitchOffBulletsNumbering(); + } + else + { + aOutlinerView.ApplyBulletsNumbering( bHandleBullets, pNumRule, bToggle ); + } + sal_uInt32 nParaCount = pOutliner->GetParagraphCount(); + pText->SetOutlinerParaObject(pOutliner->CreateParaObject(0, static_cast<sal_uInt16>(nParaCount))); + pOutliner->Clear(); + } + } + // Broadcast the object change event. + if (!pTextObj->AdjustTextFrameWidthAndHeight()) + { + pTextObj->SetChanged(); + pTextObj->BroadcastObjectChange(); + } + } + else + { + OutlinerParaObject* pParaObj = pTextObj->GetOutlinerParaObject(); + if (!pParaObj) + continue; + pOutliner->SetText(*pParaObj); + if (bUndoEnabled) + { + pUndoGroup->AddAction( + pSdrModel->GetSdrUndoFactory().CreateUndoObjectSetText(*pTextObj, 0)); + } + if ( !bToggleOn ) + { + aOutlinerView.SwitchOffBulletsNumbering(); + } + else + { + aOutlinerView.ApplyBulletsNumbering( bHandleBullets, pNumRule, bToggle ); + } + sal_uInt32 nParaCount = pOutliner->GetParagraphCount(); + pTextObj->SetOutlinerParaObject(pOutliner->CreateParaObject(0, static_cast<sal_uInt16>(nParaCount))); + pOutliner->Clear(); + } + } + + if ( bUndoEnabled && pUndoGroup->GetActionCount() > 0 ) + { + pSdrModel->BegUndo(); + pSdrModel->AddUndo(std::move(pUndoGroup)); + pSdrModel->EndUndo(); + } +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview2.cxx b/sd/source/ui/view/sdview2.cxx new file mode 100644 index 000000000..a5b3d4413 --- /dev/null +++ b/sd/source/ui/view/sdview2.cxx @@ -0,0 +1,908 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <View.hxx> + +#include <vector> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <comphelper/sequenceashashmap.hxx> +#include <tools/urlobj.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svxdlg.hxx> +#include <sfx2/docfile.hxx> +#include <svx/svdundo.hxx> +#include <svx/svdpagv.hxx> +#include <svl/urlbmk.hxx> +#include <editeng/outliner.hxx> +#include <svx/xflclit.hxx> +#include <sot/formats.hxx> +#include <editeng/editeng.hxx> + +#include <svtools/embedtransfer.hxx> +#include <tools/debug.hxx> + +#include <anminfo.hxx> +#include <strings.hrc> +#include <sdxfer.hxx> +#include <sdresid.hxx> +#include <sdmod.hxx> +#include <sdtreelb.hxx> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> +#include <fudraw.hxx> +#include <drawdoc.hxx> +#include <Window.hxx> +#include <sdpage.hxx> +#include <unoaprms.hxx> +#include <helpids.h> +#include <vcl/svapp.hxx> + +#include <slideshow.hxx> +#include <memory> + +namespace sd { + +using namespace ::com::sun::star; + +namespace { + +struct SdNavigatorDropEvent : public ExecuteDropEvent +{ + VclPtr< ::sd::Window> mpTargetWindow; + + SdNavigatorDropEvent ( + const ExecuteDropEvent& rEvt, + ::sd::Window* pTargetWindow ) + : ExecuteDropEvent( rEvt ), + mpTargetWindow( pTargetWindow ) + {} +}; + +} + +css::uno::Reference< css::datatransfer::XTransferable > View::CreateClipboardDataObject() +{ + // since SdTransferable::CopyToClipboard is called, this + // dynamically created object is destroyed automatically + rtl::Reference<SdTransferable> pTransferable = new SdTransferable( &mrDoc, nullptr, false ); + + SD_MOD()->pTransferClip = pTransferable.get(); + + mrDoc.CreatingDataObj( pTransferable.get() ); + pTransferable->SetWorkDocument( static_cast<SdDrawDocument*>(CreateMarkedObjModel().release()) ); + mrDoc.CreatingDataObj( nullptr ); + + // #112978# need to use GetAllMarkedBoundRect instead of GetAllMarkedRect to get + // fat lines correctly + const ::tools::Rectangle aMarkRect( GetAllMarkedBoundRect() ); + std::unique_ptr<TransferableObjectDescriptor> pObjDesc(new TransferableObjectDescriptor); + SdrOle2Obj* pSdrOleObj = nullptr; + SdrPageView* pPgView = GetSdrPageView(); + SdPage* pOldPage = pPgView ? static_cast<SdPage*>( pPgView->GetPage() ) : nullptr; + SdPage* pNewPage = const_cast<SdPage*>(static_cast<const SdPage*>( pTransferable->GetWorkDocument()->GetPage( 0 ) )); + + if( pOldPage ) + { + pNewPage->SetSize( pOldPage->GetSize() ); + pNewPage->SetLayoutName( pOldPage->GetLayoutName() ); + } + + if( GetMarkedObjectCount() == 1 ) + { + SdrObject* pObj = GetMarkedObjectByIndex(0); + + if( auto pOle2Obj = dynamic_cast<SdrOle2Obj *>( pObj ) ) + if( pOle2Obj->GetObjRef() ) + { + // If object has no persistence it must be copied as part of the document + try + { + uno::Reference< embed::XEmbedPersist > xPersObj( pOle2Obj->GetObjRef(), uno::UNO_QUERY ); + if ( xPersObj.is() && xPersObj->hasEntry() ) + pSdrOleObj = pOle2Obj; + } + catch( uno::Exception& ) + {} + } + } + + if( pSdrOleObj ) + SvEmbedTransferHelper::FillTransferableObjectDescriptor( *pObjDesc, pSdrOleObj->GetObjRef(), pSdrOleObj->GetGraphic(), pSdrOleObj->GetAspect() ); + else + pTransferable->GetWorkDocument()->GetDocSh()->FillTransferableObjectDescriptor( *pObjDesc ); + + if( mpDocSh ) + pObjDesc->maDisplayName = mpDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + + pObjDesc->maSize = aMarkRect.GetSize(); + + pTransferable->SetStartPos( aMarkRect.TopLeft() ); + pTransferable->SetObjectDescriptor( std::move(pObjDesc) ); + pTransferable->CopyToClipboard( mpViewSh->GetActiveWindow() ); + + return pTransferable; +} + +css::uno::Reference< css::datatransfer::XTransferable > View::CreateDragDataObject( View* pWorkView, vcl::Window& rWindow, const Point& rDragPos ) +{ + rtl::Reference<SdTransferable> pTransferable = new SdTransferable( &mrDoc, pWorkView, false ); + + SD_MOD()->pTransferDrag = pTransferable.get(); + + std::unique_ptr<TransferableObjectDescriptor> pObjDesc(new TransferableObjectDescriptor); + OUString aDisplayName; + SdrOle2Obj* pSdrOleObj = nullptr; + + if( GetMarkedObjectCount() == 1 ) + { + SdrObject* pObj = GetMarkedObjectByIndex( 0 ); + + if( auto pOle2Obj = dynamic_cast<SdrOle2Obj *>( pObj ) ) + if( pOle2Obj->GetObjRef() ) + { + // If object has no persistence it must be copied as part of the document + try + { + uno::Reference< embed::XEmbedPersist > xPersObj( pOle2Obj->GetObjRef(), uno::UNO_QUERY ); + if ( xPersObj.is() && xPersObj->hasEntry() ) + pSdrOleObj = pOle2Obj; + } + catch( uno::Exception& ) + {} + } + } + + if( mpDocSh ) + aDisplayName = mpDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + + if( pSdrOleObj ) + SvEmbedTransferHelper::FillTransferableObjectDescriptor( *pObjDesc, pSdrOleObj->GetObjRef(), pSdrOleObj->GetGraphic(), pSdrOleObj->GetAspect() ); + else if (mpDocSh) + mpDocSh->FillTransferableObjectDescriptor( *pObjDesc ); + + pObjDesc->maSize = GetAllMarkedRect().GetSize(); + pObjDesc->maDragStartPos = rDragPos; + pObjDesc->maDisplayName = aDisplayName; + + pTransferable->SetStartPos( rDragPos ); + pTransferable->SetObjectDescriptor( std::move(pObjDesc) ); + pTransferable->StartDrag( &rWindow, DND_ACTION_COPYMOVE | DND_ACTION_LINK ); + + return pTransferable; +} + +css::uno::Reference< css::datatransfer::XTransferable > View::CreateSelectionDataObject( View* pWorkView ) +{ + rtl::Reference<SdTransferable> pTransferable = new SdTransferable( &mrDoc, pWorkView, true ); + std::unique_ptr<TransferableObjectDescriptor> pObjDesc(new TransferableObjectDescriptor); + const ::tools::Rectangle aMarkRect( GetAllMarkedRect() ); + + SD_MOD()->pTransferSelection = pTransferable.get(); + + if( mpDocSh ) + { + mpDocSh->FillTransferableObjectDescriptor( *pObjDesc ); + pObjDesc->maDisplayName = mpDocSh->GetMedium()->GetURLObject().GetURLNoPass(); + } + + pObjDesc->maSize = aMarkRect.GetSize(); + + pTransferable->SetStartPos( aMarkRect.TopLeft() ); + pTransferable->SetObjectDescriptor( std::move(pObjDesc) ); + pTransferable->CopyToPrimarySelection(); + + return pTransferable; +} + +void View::UpdateSelectionClipboard() // false case +{ + if (!mpViewSh) + return; + if (!mpViewSh->GetActiveWindow()) + return; + if (GetMarkedObjectList().GetMarkCount()) + CreateSelectionDataObject( this ); + else + ClearSelectionClipboard(); +} + +void View::ClearSelectionClipboard() // true case +{ + if (!mpViewSh) + return; + if (!mpViewSh->GetActiveWindow()) + return; + if (SD_MOD()->pTransferSelection && SD_MOD()->pTransferSelection->GetView() == this) + { + TransferableHelper::ClearPrimarySelection(); + SD_MOD()->pTransferSelection = nullptr; + } +} + +void View::DoCut() +{ + const OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV ) + const_cast<OutlinerView*>(pOLV)->Cut(); + else if( AreObjectsMarked() ) + { + OUString aStr(SdResId(STR_UNDO_CUT)); + + DoCopy(); + BegUndo(aStr + " " + GetDescriptionOfMarkedObjects()); + DeleteMarked(); + EndUndo(); + } +} + +void View::DoCopy() +{ + const OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV ) + const_cast<OutlinerView*>(pOLV)->Copy(); + else if( AreObjectsMarked() ) + { + BrkAction(); + CreateClipboardDataObject(); + } +} + +void View::DoPaste (::sd::Window* pWindow) +{ + TransferableDataHelper aDataHelper( TransferableDataHelper::CreateFromSystemClipboard( mpViewSh->GetActiveWindow() ) ); + if( !aDataHelper.GetTransferable().is() ) + return; // empty clipboard? + + const OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV && EditEngine::HasValidData( aDataHelper.GetTransferable() ) ) + { + const_cast< OutlinerView* >(pOLV)->PasteSpecial(); + + SdrObject* pObj = GetTextEditObject(); + SdPage* pPage = static_cast<SdPage*>( pObj ? pObj->getSdrPageFromSdrObject() : nullptr ); + ::Outliner* pOutliner = pOLV->GetOutliner(); + + if( pOutliner) + { + if( pObj && pPage && pPage->GetPresObjKind(pObj) == PresObjKind::Title ) + { + // remove all hard linebreaks from the title + if (pOutliner->GetParagraphCount() > 1) + { + bool bOldUpdateMode = pOutliner->SetUpdateLayout( false ); + + const EditEngine& rEdit = pOutliner->GetEditEngine(); + const sal_Int32 nParaCount = rEdit.GetParagraphCount(); + + for( sal_Int32 nPara = nParaCount - 2; nPara >= 0; nPara-- ) + { + const sal_Int32 nParaLen = rEdit.GetTextLen( nPara ); + pOutliner->QuickDelete( ESelection( nPara, nParaLen, nPara+1, 0 ) ); + pOutliner->QuickInsertLineBreak( ESelection( nPara, nParaLen, nPara, nParaLen ) ); + } + + DBG_ASSERT( rEdit.GetParagraphCount() <= 1, "Titleobject contains hard line breaks" ); + pOutliner->SetUpdateLayout(bOldUpdateMode); + } + } + + if( !mrDoc.IsChanged() ) + { + if (pOutliner->IsModified()) + mrDoc.SetChanged(); + } + } + } + else + { + Point aPos = pWindow->GetVisibleCenter(); + DrawViewShell* pDrViewSh = static_cast<DrawViewShell*>( mpDocSh->GetViewShell() ); + + if (pDrViewSh != nullptr) + { + sal_Int8 nDnDAction = DND_ACTION_COPY; + if( !InsertData( aDataHelper, aPos, nDnDAction, false ) ) + { + INetBookmark aINetBookmark( "", "" ); + + if( ( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::FILEGRPDESCRIPTOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::FILEGRPDESCRIPTOR, aINetBookmark ) ) || + ( aDataHelper.HasFormat( SotClipboardFormatId::UNIFORMRESOURCELOCATOR ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::UNIFORMRESOURCELOCATOR, aINetBookmark ) ) ) + { + pDrViewSh->InsertURLField( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), "" ); + } + } + } + } +} + +void View::StartDrag( const Point& rStartPos, vcl::Window* pWindow ) +{ + if (!AreObjectsMarked() || !IsAction() || !mpViewSh || !pWindow) + return; + + BrkAction(); + + if( IsTextEdit() ) + SdrEndTextEdit(); + + if (DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(mpDocSh ? mpDocSh->GetViewShell() : nullptr)) + { + const rtl::Reference<FuPoor>& xFunction(pDrawViewShell->GetCurrentFunction()); + if (FuDraw* pFunction = dynamic_cast<FuDraw*>(xFunction.get())) + pFunction->ForcePointer(); + } + + mpDragSrcMarkList.reset( new SdrMarkList(GetMarkedObjectList()) ); + mnDragSrcPgNum = GetSdrPageView()->GetPage()->GetPageNum(); + + CreateDragDataObject( this, *pWindow, rStartPos ); +} + +void View::DragFinished( sal_Int8 nDropAction ) +{ + const bool bUndo = IsUndoEnabled(); + const bool bGroupUndo = bUndo && mpDragSrcMarkList; + if (bGroupUndo) + { + OUString aStr(SdResId(STR_UNDO_DRAGDROP)); + BegUndo(aStr + " " + mpDragSrcMarkList->GetMarkDescription()); + } + + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + + if( pDragTransferable ) + pDragTransferable->SetView( nullptr ); + + if( ( nDropAction & DND_ACTION_MOVE ) && + pDragTransferable && !pDragTransferable->IsInternalMove() && + mpDragSrcMarkList && mpDragSrcMarkList->GetMarkCount() && + !IsPresObjSelected() ) + { + mpDragSrcMarkList->ForceSort(); + + if( bUndo ) + BegUndo(); + + const size_t nCnt = mpDragSrcMarkList->GetMarkCount(); + + for( size_t nm = nCnt; nm>0; ) + { + --nm; + SdrMark* pM=mpDragSrcMarkList->GetMark(nm); + if( bUndo ) + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeleteObject(*pM->GetMarkedSdrObj())); + } + + mpDragSrcMarkList->GetMark(0)->GetMarkedSdrObj()->GetOrdNum(); + + for (size_t nm = nCnt; nm>0;) + { + --nm; + SdrMark* pM=mpDragSrcMarkList->GetMark(nm); + SdrObject* pObj=pM->GetMarkedSdrObj(); + + if( pObj && pObj->getSdrPageFromSdrObject() ) + { + const size_t nOrdNum = pObj->GetOrdNumDirect(); + SdrObject* pChkObj = pObj->getSdrPageFromSdrObject()->RemoveObject(nOrdNum); + DBG_ASSERT(pChkObj==pObj,"pChkObj!=pObj in RemoveObject()"); + } + } + + if( bUndo ) + EndUndo(); + } + + if( pDragTransferable ) + pDragTransferable->SetInternalMove( false ); + + if (bGroupUndo) + EndUndo(); + mnDragSrcPgNum = SDRPAGE_NOTFOUND; + mpDragSrcMarkList.reset(); +} + +sal_Int8 View::AcceptDrop( const AcceptDropEvent& rEvt, DropTargetHelper& rTargetHelper, + SdrLayerID nLayer ) +{ + OUString aLayerName = GetActiveLayer(); + SdrPageView* pPV = GetSdrPageView(); + sal_Int8 nDropAction = rEvt.mnAction; + sal_Int8 nRet = DND_ACTION_NONE; + + if( nLayer != SDRLAYER_NOTFOUND ) + { + SdrLayerAdmin& rLayerAdmin = mrDoc.GetLayerAdmin(); + aLayerName = rLayerAdmin.GetLayerPerID(nLayer)->GetName(); + } + + if( mbIsDropAllowed && !pPV->IsLayerLocked( aLayerName ) && pPV->IsLayerVisible( aLayerName ) ) + { + const OutlinerView* pOLV = GetTextEditOutlinerView(); + bool bIsInsideOutlinerView = false; + + if( pOLV ) + { + ::tools::Rectangle aRect( pOLV->GetOutputArea() ); + + if (GetMarkedObjectCount() == 1) + { + SdrMark* pMark = GetSdrMarkByIndex(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + aRect.Union( pObj->GetLogicRect() ); + } + + if( aRect.Contains( pOLV->GetWindow()->PixelToLogic( rEvt.maPosPixel ) ) ) + { + bIsInsideOutlinerView = true; + } + } + + if( !bIsInsideOutlinerView ) + { + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + + if(pDragTransferable && (nDropAction & DND_ACTION_LINK)) + { + // suppress own data when it's intention is to use it as fill information + pDragTransferable = nullptr; + } + + if( pDragTransferable ) + { + const View* pSourceView = pDragTransferable->GetView(); + + if( pDragTransferable->IsPageTransferable() ) + { + nRet = DND_ACTION_COPY; + } + else if( pSourceView ) + { + if( !( nDropAction & DND_ACTION_LINK ) || + !pSourceView->GetDocSh()->GetMedium()->GetName().isEmpty() ) + { + nRet = nDropAction; + } + } + } + else + { + const bool bDrawing = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::DRAWING ); + const bool bGraphic = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::SVXB ); + const bool bMtf = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::GDIMETAFILE ); + const bool bBitmap = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::BITMAP ); + bool bBookmark = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::NETSCAPE_BOOKMARK ); + bool bXFillExchange = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::XFA ); + + // check handle insert + if ((bXFillExchange && (SdrDragMode::Gradient == GetDragMode())) + || (SdrDragMode::Transparence == GetDragMode())) + { + const SdrHdlList& rHdlList = GetHdlList(); + + for( size_t n = 0; n < rHdlList.GetHdlCount(); ++n ) + { + SdrHdl* pIAOHandle = rHdlList.GetHdl( n ); + + if( pIAOHandle && ( SdrHdlKind::Color == pIAOHandle->GetKind() ) ) + { + if(pIAOHandle->getOverlayObjectList().isHitPixel(rEvt.maPosPixel)) + { + nRet = nDropAction; + static_cast< SdrHdlColor* >( pIAOHandle )->SetSize( SDR_HANDLE_COLOR_SIZE_SELECTED ); + } + else + { + static_cast< SdrHdlColor* >( pIAOHandle )->SetSize( SDR_HANDLE_COLOR_SIZE_NORMAL ); + } + } + } + } + + // check object insert + if( !nRet && ( bXFillExchange || ( ( bDrawing || bGraphic || bMtf || bBitmap || bBookmark ) && ( nDropAction & DND_ACTION_LINK ) ) ) ) + { + SdrPageView* pPageView = nullptr; + ::sd::Window* pWindow = mpViewSh->GetActiveWindow(); + Point aPos( pWindow->PixelToLogic( rEvt.maPosPixel ) ); + SdrObject* pPickObj = PickObj(aPos, getHitTolLog(), pPageView); + bool bIsPresTarget = false; + + if (pPickObj && (pPickObj->IsEmptyPresObj() || pPickObj->GetUserCall())) + { + SdPage* pPage = static_cast<SdPage*>( pPickObj->getSdrPageFromSdrObject() ); + + if( pPage && pPage->IsMasterPage() ) + bIsPresTarget = pPage->IsPresObj( pPickObj ); + } + + if (pPickObj && !bIsPresTarget && (bGraphic || bMtf || bBitmap || bXFillExchange)) + { + if( mpDropMarkerObj != pPickObj ) + { + mpDropMarkerObj = pPickObj; + ImplClearDrawDropMarker(); + + if(mpDropMarkerObj) + { + mpDropMarker.reset( new SdrDropMarkerOverlay(*this, *mpDropMarkerObj) ); + } + } + + nRet = nDropAction; + } + else + bXFillExchange = false; + } + + // check normal insert + if( !nRet ) + { + const bool bSBAFormat = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::SVX_FORMFIELDEXCH ); + const bool bEditEngineODF = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT ); + const bool bString = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::STRING ); + const bool bRTF = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::RTF ); + const bool bFile = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::SIMPLE_FILE ); + const bool bFileList = rTargetHelper.IsDropFormatSupported( SotClipboardFormatId::FILE_LIST ); + + if( mpDropMarker ) + { + ImplClearDrawDropMarker(); + mpDropMarkerObj = nullptr; + } + + if( bBookmark && bFile && ( nDropAction & DND_ACTION_MOVE ) && mpViewSh && SlideShow::IsRunning(mpViewSh->GetViewShellBase()) ) + bBookmark = false; + + if( bDrawing || bGraphic || bMtf || bBitmap || bBookmark || bFile || bFileList || bXFillExchange || bSBAFormat || bEditEngineODF || bString || bRTF ) + nRet = nDropAction; + + // For entries from the navigator, change action copy. + if (bBookmark + && rTargetHelper.IsDropFormatSupported( + SdPageObjsTLV::SdPageObjsTransferable::GetListBoxDropFormatId()) + && (nDropAction & DND_ACTION_MOVE)!=0) + { + nRet = DND_ACTION_COPY; + } + } + } + } + } + + // destroy drop marker if this is a leaving event + if( rEvt.mbLeaving && mpDropMarker ) + { + ImplClearDrawDropMarker(); + mpDropMarkerObj = nullptr; + } + + return nRet; +} + +sal_Int8 View::ExecuteDrop( const ExecuteDropEvent& rEvt, + ::sd::Window* pTargetWindow, sal_uInt16 nPage, SdrLayerID nLayer ) +{ + SdrPageView* pPV = GetSdrPageView(); + OUString aActiveLayer = GetActiveLayer(); + sal_Int8 nDropAction = rEvt.mnAction; + sal_Int8 nRet = DND_ACTION_NONE; + + // destroy drop marker if it is shown + if( mpDropMarker ) + { + ImplClearDrawDropMarker(); + mpDropMarkerObj = nullptr; + } + + if( !pPV->IsLayerLocked( aActiveLayer ) ) + { + const OutlinerView* pOLV = GetTextEditOutlinerView(); + bool bIsInsideOutlinerView = false; + + if( pOLV ) + { + ::tools::Rectangle aRect( pOLV->GetOutputArea() ); + + if( GetMarkedObjectCount() == 1 ) + { + SdrMark* pMark = GetSdrMarkByIndex(0); + SdrObject* pObj = pMark->GetMarkedSdrObj(); + aRect.Union( pObj->GetLogicRect() ); + } + + Point aPos( pOLV->GetWindow()->PixelToLogic( rEvt.maPosPixel ) ); + + if( aRect.Contains( aPos ) ) + { + bIsInsideOutlinerView = true; + } + } + + if( !bIsInsideOutlinerView ) + { + Point aPos; + TransferableDataHelper aDataHelper( rEvt.maDropEvent.Transferable ); + + if( pTargetWindow ) + aPos = pTargetWindow->PixelToLogic( rEvt.maPosPixel ); + + // handle insert? + if ((SdrDragMode::Gradient == GetDragMode()) + || ((SdrDragMode::Transparence == GetDragMode()) + && aDataHelper.HasFormat(SotClipboardFormatId::XFA))) + { + const SdrHdlList& rHdlList = GetHdlList(); + + for( size_t n = 0; !nRet && n < rHdlList.GetHdlCount(); ++n ) + { + SdrHdl* pIAOHandle = rHdlList.GetHdl( n ); + + if( pIAOHandle && ( SdrHdlKind::Color == pIAOHandle->GetKind() ) ) + { + if(pIAOHandle->getOverlayObjectList().isHitPixel(rEvt.maPosPixel)) + { + uno::Any const data(aDataHelper.GetAny(SotClipboardFormatId::XFA, "")); + uno::Sequence<beans::NamedValue> props; + if (data >>= props) + { + ::comphelper::SequenceAsHashMap const map(props); + Color aColor(COL_BLACK); + auto const it = map.find("FillColor"); + if (it != map.end()) + { + XFillColorItem color; + color.PutValue(it->second, 0); + aColor = color.GetColorValue(); + } + static_cast< SdrHdlColor* >( pIAOHandle )->SetColor( aColor, true ); + nRet = nDropAction; + } + } + } + } + } + + // standard insert? + if( !nRet && InsertData( aDataHelper, aPos, nDropAction, true, SotClipboardFormatId::NONE, nPage, nLayer ) ) + nRet = nDropAction; + + // special insert? + if( !nRet && mpViewSh ) + { + INetBookmark aINetBookmark( (OUString()), (OUString()) ); + + // insert bookmark + if( aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) + { + SdPageObjsTLV::SdPageObjsTransferable* pPageObjsTransferable = SdPageObjsTLV::SdPageObjsTransferable::getImplementation( aDataHelper.GetXTransferable() ); + + if( pPageObjsTransferable && + ( NAVIGATOR_DRAGTYPE_LINK == pPageObjsTransferable->GetDragType() || + NAVIGATOR_DRAGTYPE_EMBEDDED == pPageObjsTransferable->GetDragType() ) ) + { + // insert bookmark from own navigator (handled async. due to possible message box ) + Application::PostUserEvent( LINK( this, View, ExecuteNavigatorDrop ), + new SdNavigatorDropEvent( rEvt, pTargetWindow ) ); + nRet = nDropAction; + } + else + { + SdrPageView* pPageView = nullptr; + + SdrObject* pPickObj = PickObj(aPos, getHitTolLog(), pPageView); + if (pPickObj) + { + // insert as clip action => jump + OUString aBookmark( aINetBookmark.GetURL() ); + SdAnimationInfo* pInfo = SdDrawDocument::GetAnimationInfo( pPickObj ); + + if( !aBookmark.isEmpty() ) + { + bool bCreated = false; + + presentation::ClickAction eClickAction = presentation::ClickAction_DOCUMENT; + + sal_Int32 nIndex = aBookmark.indexOf( '#' ); + if( nIndex != -1 ) + { + const std::u16string_view aDocName( aBookmark.subView( 0, nIndex ) ); + + if (mpDocSh->GetMedium()->GetName() == aDocName || aDocName == mpDocSh->GetName()) + { + // internal jump, only use the part after and including '#' + eClickAction = presentation::ClickAction_BOOKMARK; + aBookmark = aBookmark.copy( nIndex+1 ); + } + } + + if( !pInfo ) + { + pInfo = SdDrawDocument::GetShapeUserData( *pPickObj, true ); + bCreated = true; + } + + // create undo action with old and new sizes + std::unique_ptr<SdAnimationPrmsUndoAction> pAction(new SdAnimationPrmsUndoAction(&mrDoc, pPickObj, bCreated)); + pAction->SetActive(pInfo->mbActive, pInfo->mbActive); + pAction->SetEffect(pInfo->meEffect, pInfo->meEffect); + pAction->SetTextEffect(pInfo->meTextEffect, pInfo->meTextEffect); + pAction->SetSpeed(pInfo->meSpeed, pInfo->meSpeed); + pAction->SetDim(pInfo->mbDimPrevious, pInfo->mbDimPrevious); + pAction->SetDimColor(pInfo->maDimColor, pInfo->maDimColor); + pAction->SetDimHide(pInfo->mbDimHide, pInfo->mbDimHide); + pAction->SetSoundOn(pInfo->mbSoundOn, pInfo->mbSoundOn); + pAction->SetSound(pInfo->maSoundFile, pInfo->maSoundFile); + pAction->SetPlayFull(pInfo->mbPlayFull, pInfo->mbPlayFull); + pAction->SetClickAction(pInfo->meClickAction, eClickAction); + pAction->SetBookmark(pInfo->GetBookmark(), aBookmark); + pAction->SetVerb(pInfo->mnVerb, pInfo->mnVerb); + pAction->SetSecondEffect(pInfo->meSecondEffect, pInfo->meSecondEffect); + pAction->SetSecondSpeed(pInfo->meSecondSpeed, pInfo->meSecondSpeed); + pAction->SetSecondSoundOn(pInfo->mbSecondSoundOn, pInfo->mbSecondSoundOn); + pAction->SetSecondPlayFull(pInfo->mbSecondPlayFull, pInfo->mbSecondPlayFull); + + OUString aString(SdResId(STR_UNDO_ANIMATION)); + pAction->SetComment(aString); + mpDocSh->GetUndoManager()->AddUndoAction(std::move(pAction)); + pInfo->meClickAction = eClickAction; + pInfo->SetBookmark( aBookmark ); + mrDoc.SetChanged(); + + nRet = nDropAction; + } + } + else if( auto pDrawViewShell = dynamic_cast< DrawViewShell *>( mpViewSh ) ) + { + // insert as normal URL button + pDrawViewShell->InsertURLButton( aINetBookmark.GetURL(), aINetBookmark.GetDescription(), OUString(), &aPos ); + nRet = nDropAction; + } + } + } + } + } + } + + return nRet; +} + +IMPL_LINK( View, ExecuteNavigatorDrop, void*, p, void ) +{ + SdNavigatorDropEvent* pSdNavigatorDropEvent = static_cast<SdNavigatorDropEvent*>(p); + TransferableDataHelper aDataHelper( pSdNavigatorDropEvent->maDropEvent.Transferable ); + SdPageObjsTLV::SdPageObjsTransferable* pPageObjsTransferable = SdPageObjsTLV::SdPageObjsTransferable::getImplementation( aDataHelper.GetXTransferable() ); + INetBookmark aINetBookmark; + + if( pPageObjsTransferable && aDataHelper.GetINetBookmark( SotClipboardFormatId::NETSCAPE_BOOKMARK, aINetBookmark ) ) + { + Point aPos; + OUString aBookmark; + SdPage* pPage = static_cast<SdPage*>( GetSdrPageView()->GetPage() ); + sal_uInt16 nPgPos = 0xFFFF; + + if( pSdNavigatorDropEvent->mpTargetWindow ) + aPos = pSdNavigatorDropEvent->mpTargetWindow->PixelToLogic( pSdNavigatorDropEvent->maPosPixel ); + + const OUString& aURL( aINetBookmark.GetURL() ); + sal_Int32 nIndex = aURL.indexOf( '#' ); + if( nIndex != -1 ) + aBookmark = aURL.copy( nIndex+1 ); + + std::vector<OUString> aExchangeList; + std::vector<OUString> aBookmarkList(1,aBookmark); + + if( !pPage->IsMasterPage() ) + { + if( pPage->GetPageKind() == PageKind::Standard ) + nPgPos = pPage->GetPageNum() + 2; + else if( pPage->GetPageKind() == PageKind::Notes ) + nPgPos = pPage->GetPageNum() + 1; + } + + /* In order t ensure unique page names, we test the ones we want to + insert. If necessary. we put them into and replacement list (bNameOK + == sal_False -> User canceled). */ + bool bLink = pPageObjsTransferable->GetDragType() == NAVIGATOR_DRAGTYPE_LINK; + bool bNameOK = GetExchangeList( aExchangeList, aBookmarkList, 2 ); + + /* Since we don't know the type (page or object), we fill a list with + pages and objects. + Of course we have problems if there are pages and objects with the + same name!!! */ + if( bNameOK ) + { + mrDoc.InsertBookmark( aBookmarkList, aExchangeList, + bLink, nPgPos, + &pPageObjsTransferable->GetDocShell(), + &aPos ); + } + } + + delete pSdNavigatorDropEvent; +} + +bool View::GetExchangeList (std::vector<OUString> &rExchangeList, + std::vector<OUString> &rBookmarkList, + const sal_uInt16 nType) +{ + assert(rExchangeList.empty()); + + bool bListIdentical = true; ///< Bookmark list and exchange list are identical + bool bNameOK = true; ///< name is unique + + for ( const auto& rBookmark : rBookmarkList ) + { + OUString aNewName = rBookmark; + + if( nType == 0 || nType == 2 ) + bNameOK = mpDocSh->CheckPageName(mpViewSh->GetFrameWeld(), aNewName); + + if( bNameOK && ( nType == 1 || nType == 2 ) ) + { + if( mrDoc.GetObj( aNewName ) ) + { + OUString aTitle(SdResId(STR_TITLE_NAMEGROUP)); + OUString aDesc(SdResId(STR_DESC_NAMEGROUP)); + + SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create(); + ScopedVclPtr<AbstractSvxNameDialog> pDlg(pFact->CreateSvxNameDialog(mpViewSh->GetFrameWeld(), aNewName, aDesc)); + + pDlg->SetEditHelpId( HID_SD_NAMEDIALOG_OBJECT ); + + bNameOK = false; + pDlg->SetText( aTitle ); + + while( !bNameOK && pDlg->Execute() == RET_OK ) + { + pDlg->GetName( aNewName ); + + if( !mrDoc.GetObj( aNewName ) ) + bNameOK = true; + } + } + } + + bListIdentical = rBookmark == aNewName; + + rExchangeList.push_back(aNewName); + + if (!bNameOK) + break; + } + + // Exchange list is identical to bookmark list + if( !rExchangeList.empty() && bListIdentical ) + rExchangeList.clear(); + + return bNameOK; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview3.cxx b/sd/source/ui/view/sdview3.cxx new file mode 100644 index 000000000..b72e837c4 --- /dev/null +++ b/sd/source/ui/view/sdview3.cxx @@ -0,0 +1,1596 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <View.hxx> +#include <com/sun/star/embed/XEmbedObjectClipboardCreator.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/MSOLEObjectSystemCreator.hpp> +#include <com/sun/star/lang/XComponent.hpp> +#include <sot/filelist.hxx> +#include <editeng/editdata.hxx> +#include <svx/xfillit0.hxx> +#include <svx/xflclit.hxx> +#include <svx/xlnclit.hxx> +#include <svx/svdpagv.hxx> +#include <sfx2/docfile.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdundo.hxx> +#include <svl/itempool.hxx> +#include <sot/formats.hxx> +#include <editeng/outliner.hxx> +#include <svx/obj3d.hxx> +#include <svx/e3dundo.hxx> +#include <svx/unomodel.hxx> +#include <svx/ImageMapInfo.hxx> +#include <unotools/streamwrap.hxx> +#include <vcl/graph.hxx> +#include <vcl/metaact.hxx> +#include <vcl/pdfread.hxx> +#include <vcl/TypeSerializer.hxx> +#include <svx/svxids.hrc> +#include <toolkit/helper/vclunohelper.hxx> +#include <svtools/embedhlp.hxx> +#include <osl/diagnose.h> +#include <DrawDocShell.hxx> +#include <fupoor.hxx> +#include <tablefunction.hxx> +#include <Window.hxx> +#include <sdxfer.hxx> +#include <sdpage.hxx> +#include <drawdoc.hxx> +#include <sdmod.hxx> +#include <sdresid.hxx> +#include <strings.hrc> +#include <SlideSorterViewShell.hxx> +#include <unomodel.hxx> +#include <ViewClipboard.hxx> +#include <sfx2/ipclient.hxx> +#include <sfx2/classificationhelper.hxx> +#include <comphelper/sequenceashashmap.hxx> +#include <comphelper/storagehelper.hxx> +#include <comphelper/processfactory.hxx> +#include <svx/sdrhittesthelper.hxx> +#include <svx/xbtmpit.hxx> +#include <memory> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::datatransfer; +using namespace ::com::sun::star::datatransfer::clipboard; + +namespace sd { + +#define CHECK_FORMAT_TRANS( _def_Type ) ( ( nFormat == (_def_Type) || nFormat == SotClipboardFormatId::NONE ) && aDataHelper.HasFormat( _def_Type ) ) + +/************************************************************************* +|* +|* Paste +|* +\************************************************************************/ + +namespace { + +struct ImpRememberOrigAndClone +{ + SdrObject* pOrig; + SdrObject* pClone; +}; + +} + +static SdrObject* ImpGetClone(std::vector<ImpRememberOrigAndClone>& aConnectorContainer, SdrObject const * pConnObj) +{ + for(const ImpRememberOrigAndClone& rImp : aConnectorContainer) + { + if(pConnObj == rImp.pOrig) + return rImp.pClone; + } + return nullptr; +} + +// restrict movement to WorkArea +static void ImpCheckInsertPos(Point& rPos, const Size& rSize, const ::tools::Rectangle& rWorkArea) +{ + if(rWorkArea.IsEmpty()) + return; + + ::tools::Rectangle aMarkRect(Point(rPos.X() - (rSize.Width() / 2), rPos.Y() - (rSize.Height() / 2)), rSize); + + if(aMarkRect.Contains(rWorkArea)) + return; + + if(aMarkRect.Left() < rWorkArea.Left()) + { + rPos.AdjustX(rWorkArea.Left() - aMarkRect.Left() ); + } + + if(aMarkRect.Right() > rWorkArea.Right()) + { + rPos.AdjustX( -(aMarkRect.Right() - rWorkArea.Right()) ); + } + + if(aMarkRect.Top() < rWorkArea.Top()) + { + rPos.AdjustY(rWorkArea.Top() - aMarkRect.Top() ); + } + + if(aMarkRect.Bottom() > rWorkArea.Bottom()) + { + rPos.AdjustY( -(aMarkRect.Bottom() - rWorkArea.Bottom()) ); + } +} + +bool View::InsertMetaFile( const TransferableDataHelper& rDataHelper, const Point& rPos, ImageMap const * pImageMap, bool bOptimize ) +{ + GDIMetaFile aMtf; + + if( !rDataHelper.GetGDIMetaFile( SotClipboardFormatId::GDIMETAFILE, aMtf ) ) + return false; + + bool bVector = false; + Graphic aGraphic; + + // check if metafile only contains a pixel image, if so insert a bitmap instead + if( bOptimize ) + { + MetaAction* pAction = aMtf.FirstAction(); + while( pAction && !bVector ) + { + switch( pAction->GetType() ) + { + case MetaActionType::POINT: + case MetaActionType::LINE: + case MetaActionType::RECT: + case MetaActionType::ROUNDRECT: + case MetaActionType::ELLIPSE: + case MetaActionType::ARC: + case MetaActionType::PIE: + case MetaActionType::CHORD: + case MetaActionType::POLYLINE: + case MetaActionType::POLYGON: + case MetaActionType::POLYPOLYGON: + case MetaActionType::TEXT: + case MetaActionType::TEXTARRAY: + case MetaActionType::STRETCHTEXT: + case MetaActionType::TEXTRECT: + case MetaActionType::GRADIENT: + case MetaActionType::HATCH: + case MetaActionType::WALLPAPER: + case MetaActionType::EPS: + case MetaActionType::TEXTLINE: + case MetaActionType::FLOATTRANSPARENT: + case MetaActionType::GRADIENTEX: + case MetaActionType::BMPSCALEPART: + case MetaActionType::BMPEXSCALEPART: + bVector = true; + break; + case MetaActionType::BMP: + case MetaActionType::BMPSCALE: + case MetaActionType::BMPEX: + case MetaActionType::BMPEXSCALE: + if( aGraphic.GetType() != GraphicType::NONE ) + { + bVector = true; + } + else switch( pAction->GetType() ) + { + case MetaActionType::BMP: + { + MetaBmpAction* pBmpAction = dynamic_cast< MetaBmpAction* >( pAction ); + if( pBmpAction ) + aGraphic = Graphic(BitmapEx(pBmpAction->GetBitmap())); + } + break; + case MetaActionType::BMPSCALE: + { + MetaBmpScaleAction* pBmpScaleAction = dynamic_cast< MetaBmpScaleAction* >( pAction ); + if( pBmpScaleAction ) + aGraphic = Graphic(BitmapEx(pBmpScaleAction->GetBitmap())); + } + break; + case MetaActionType::BMPEX: + { + MetaBmpExAction* pBmpExAction = dynamic_cast< MetaBmpExAction* >( pAction ); + if( pBmpExAction ) + aGraphic = Graphic(pBmpExAction->GetBitmapEx() ); + } + break; + case MetaActionType::BMPEXSCALE: + { + MetaBmpExScaleAction* pBmpExScaleAction = dynamic_cast< MetaBmpExScaleAction* >( pAction ); + if( pBmpExScaleAction ) + aGraphic = Graphic( pBmpExScaleAction->GetBitmapEx() ); + } + break; + default: break; + } + break; + default: break; + } + + pAction = aMtf.NextAction(); + } + } + + // it is not a vector metafile but it also has no graphic? + if( !bVector && (aGraphic.GetType() == GraphicType::NONE) ) + bVector = true; + + // restrict movement to WorkArea + Point aInsertPos( rPos ); + Size aImageSize = bVector ? aMtf.GetPrefSize() : aGraphic.GetSizePixel(); + ImpCheckInsertPos(aInsertPos, aImageSize, GetWorkArea()); + + if( bVector ) + aGraphic = Graphic( aMtf ); + + aGraphic.SetPrefMapMode( aMtf.GetPrefMapMode() ); + aGraphic.SetPrefSize( aMtf.GetPrefSize() ); + InsertGraphic( aGraphic, mnAction, aInsertPos, nullptr, pImageMap ); + + return true; +} + +bool View::InsertData( const TransferableDataHelper& rDataHelper, + const Point& rPos, sal_Int8& rDnDAction, bool bDrag, + SotClipboardFormatId nFormat, sal_uInt16 nPage, SdrLayerID nLayer ) +{ + maDropPos = rPos; + mnAction = rDnDAction; + mbIsDropAllowed = false; + + TransferableDataHelper aDataHelper( rDataHelper ); + SdrObject* pPickObj = nullptr; + SdPage* pPage = nullptr; + std::unique_ptr<ImageMap> pImageMap; + bool bReturn = false; + bool bLink = ( ( mnAction & DND_ACTION_LINK ) != 0 ); + bool bCopy = ( ( ( mnAction & DND_ACTION_COPY ) != 0 ) || bLink ); + SdrInsertFlags nPasteOptions = SdrInsertFlags::SETDEFLAYER; + + if (mpViewSh != nullptr) + { + OSL_ASSERT (mpViewSh->GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient = mpViewSh->GetViewShell()->GetIPClient(); + if( dynamic_cast< ::sd::slidesorter::SlideSorterViewShell *>( mpViewSh ) != nullptr + || (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive())) + nPasteOptions |= SdrInsertFlags::DONTMARK; + } + + if( bDrag ) + { + SdrPageView* pPV = nullptr; + pPickObj = PickObj(rPos, getHitTolLog(), pPV); + } + + if( nPage != SDRPAGE_NOTFOUND ) + pPage = static_cast<SdPage*>( mrDoc.GetPage( nPage ) ); + + SdTransferable* pOwnData = nullptr; + SdTransferable* pImplementation = SdTransferable::getImplementation( aDataHelper.GetTransferable() ); + + if(pImplementation && (rDnDAction & DND_ACTION_LINK)) + { + // suppress own data when it's intention is to use it as fill information + pImplementation = nullptr; + } + + bool bSelfDND = false; + + // try to get own transfer data + if( pImplementation ) + { + if( SD_MOD()->pTransferClip == pImplementation ) + pOwnData = SD_MOD()->pTransferClip; + else if( SD_MOD()->pTransferDrag == pImplementation ) + { + pOwnData = SD_MOD()->pTransferDrag; + bSelfDND = true; + } + else if( SD_MOD()->pTransferSelection == pImplementation ) + pOwnData = SD_MOD()->pTransferSelection; + } + + const bool bGroupUndoFromDragWithDrop = bSelfDND && mpDragSrcMarkList && IsUndoEnabled(); + if (bGroupUndoFromDragWithDrop) + { + OUString aStr(SdResId(STR_UNDO_DRAGDROP)); + BegUndo(aStr + " " + mpDragSrcMarkList->GetMarkDescription()); + } + + // ImageMap? + if( !pOwnData && aDataHelper.HasFormat( SotClipboardFormatId::SVIM ) ) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVIM, xStm ) ) + { + pImageMap.reset(new ImageMap); + // mba: clipboard always must contain absolute URLs (could be from alien source) + pImageMap->Read( *xStm ); + } + } + + bool bTable = false; + // check special cases for pasting table formats as RTL + if( !bLink && (nFormat == SotClipboardFormatId::NONE || (nFormat == SotClipboardFormatId::RTF) || (nFormat == SotClipboardFormatId::RICHTEXT)) ) + { + // if the object supports rtf and there is a table involved, default is to create a table + bool bIsRTF = aDataHelper.HasFormat( SotClipboardFormatId::RTF ); + if( ( bIsRTF || aDataHelper.HasFormat( SotClipboardFormatId::RICHTEXT ) ) + && ! aDataHelper.HasFormat( SotClipboardFormatId::DRAWING ) ) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( bIsRTF ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT, xStm ) ) + { + xStm->Seek( 0 ); + + OStringBuffer aLine; + while (xStm->ReadLine(aLine)) + { + size_t x = std::string_view(aLine).find( "\\trowd" ); + if (x != std::string_view::npos) + { + bTable = true; + nFormat = bIsRTF ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT; + break; + } + } + } + } + } + + // Changed the whole decision tree to be dependent of bReturn as a flag that + // the work was done; this allows to check multiple formats and not just fail + // when a CHECK_FORMAT_TRANS(*format*) detected format does not work. This is + // e.g. necessary for SotClipboardFormatId::BITMAP + + if (!bReturn && pOwnData) + { + // Paste only if SfxClassificationHelper recommends so. + const SfxObjectShellRef& pSource = pOwnData->GetDocShell(); + SfxObjectShell* pDestination = mrDoc.GetDocSh(); + if (pSource.is() && pDestination) + { + SfxClassificationCheckPasteResult eResult = SfxClassificationHelper::CheckPaste(pSource->getDocProperties(), pDestination->getDocProperties()); + if (!SfxClassificationHelper::ShowPasteInfo(eResult)) + bReturn = true; + } + } + + if( !bReturn && pOwnData && nFormat == SotClipboardFormatId::NONE ) + { + const View* pSourceView = pOwnData->GetView(); + + if( pOwnData->GetDocShell().is() && pOwnData->IsPageTransferable() ) + { + mpClipboard->HandlePageDrop (*pOwnData); + bReturn = true; + } + else if( pSourceView ) + { + if( pSourceView == this ) + { + // same view + if( nLayer != SDRLAYER_NOTFOUND ) + { + // drop on layer tab bar + SdrLayerAdmin& rLayerAdmin = mrDoc.GetLayerAdmin(); + SdrLayer* pLayer = rLayerAdmin.GetLayerPerID( nLayer ); + SdrPageView* pPV = GetSdrPageView(); + OUString aLayer = pLayer->GetName(); + + if( !pPV->IsLayerLocked( aLayer ) ) + { + pOwnData->SetInternalMove( true ); + SortMarkedObjects(); + + for( size_t nM = 0; nM < GetMarkedObjectCount(); ++nM ) + { + SdrMark* pM = GetSdrMarkByIndex( nM ); + SdrObject* pO = pM->GetMarkedSdrObj(); + + if( pO ) + { + // #i11702# + if( IsUndoEnabled() ) + { + BegUndo(SdResId(STR_MODIFYLAYER)); + AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoObjectLayerChange(*pO, pO->GetLayer(), nLayer)); + EndUndo(); + } + + pO->SetLayer( nLayer ); + } + } + + bReturn = true; + } + } + else + { + SdrPageView* pPV = GetSdrPageView(); + bool bDropOnTabBar = true; + + if( !pPage && pPV->GetPage()->GetPageNum() != mnDragSrcPgNum ) + { + pPage = static_cast<SdPage*>( pPV->GetPage() ); + bDropOnTabBar = false; + } + + if( pPage ) + { + // drop on other page + OUString aActiveLayer = GetActiveLayer(); + + if( !pPV->IsLayerLocked( aActiveLayer ) ) + { + if( !IsPresObjSelected() ) + { + SdrMarkList* pMarkList; + + if( (mnDragSrcPgNum != SDRPAGE_NOTFOUND) && (mnDragSrcPgNum != pPV->GetPage()->GetPageNum()) ) + { + pMarkList = mpDragSrcMarkList.get(); + } + else + { + // actual mark list is used + pMarkList = new SdrMarkList( GetMarkedObjectList()); + } + + pMarkList->ForceSort(); + + // stuff to remember originals and clones + std::vector<ImpRememberOrigAndClone> aConnectorContainer; + size_t nConnectorCount = 0; + Point aCurPos; + + // calculate real position of current + // source objects, if necessary (#103207) + if( pOwnData == SD_MOD()->pTransferSelection ) + { + ::tools::Rectangle aCurBoundRect; + + if( pMarkList->TakeBoundRect( pPV, aCurBoundRect ) ) + aCurPos = aCurBoundRect.TopLeft(); + else + aCurPos = pOwnData->GetStartPos(); + } + else + aCurPos = pOwnData->GetStartPos(); + + const Size aVector( maDropPos.X() - aCurPos.X(), maDropPos.Y() - aCurPos.Y() ); + + std::unordered_set<rtl::OUString> aNameSet; + for(size_t a = 0; a < pMarkList->GetMarkCount(); ++a) + { + SdrMark* pM = pMarkList->GetMark(a); + SdrObject* pObj(pM->GetMarkedSdrObj()->CloneSdrObject(pPage->getSdrModelFromSdrPage())); + + if(pObj) + { + if(!bDropOnTabBar) + { + // do a NbcMove(...) instead of setting SnapRects here + pObj->NbcMove(aVector); + } + + SdrObject* pMarkParent = pM->GetMarkedSdrObj()->getParentSdrObjectFromSdrObject(); + if (bCopy || (pMarkParent && pMarkParent->IsGroupObject())) + pPage->InsertObjectThenMakeNameUnique(pObj, aNameSet); + else + pPage->InsertObject(pObj); + + if( IsUndoEnabled() ) + { + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoNewObject(*pObj)); + EndUndo(); + } + + ImpRememberOrigAndClone aRem; + aRem.pOrig = pM->GetMarkedSdrObj(); + aRem.pClone = pObj; + aConnectorContainer.push_back(aRem); + + if(dynamic_cast< SdrEdgeObj *>( pObj ) != nullptr) + nConnectorCount++; + } + } + + // try to re-establish connections at clones + if(nConnectorCount) + { + for(size_t a = 0; a < aConnectorContainer.size(); ++a) + { + ImpRememberOrigAndClone* pRem = &aConnectorContainer[a]; + + if(auto pCloneEdge = dynamic_cast<SdrEdgeObj *>( pRem->pClone )) + { + SdrEdgeObj* pOrigEdge = static_cast<SdrEdgeObj*>(pRem->pOrig); + + // test first connection + SdrObjConnection& rConn0 = pOrigEdge->GetConnection(false); + SdrObject* pConnObj = rConn0.GetObject(); + if(pConnObj) + { + SdrObject* pConnClone = ImpGetClone(aConnectorContainer, pConnObj); + if(pConnClone) + { + // if dest obj was cloned, too, re-establish connection + pCloneEdge->ConnectToNode(false, pConnClone); + pCloneEdge->GetConnection(false).SetConnectorId(rConn0.GetConnectorId()); + } + else + { + // set position of connection point of original connected object + const SdrGluePointList* pGlueList = pConnObj->GetGluePointList(); + if(pGlueList) + { + sal_uInt16 nInd = pGlueList->FindGluePoint(rConn0.GetConnectorId()); + + if(SDRGLUEPOINT_NOTFOUND != nInd) + { + const SdrGluePoint& rGluePoint = (*pGlueList)[nInd]; + Point aPosition = rGluePoint.GetAbsolutePos(*pConnObj); + aPosition.AdjustX(aVector.Width() ); + aPosition.AdjustY(aVector.Height() ); + pCloneEdge->SetTailPoint(false, aPosition); + } + } + } + } + + // test second connection + SdrObjConnection& rConn1 = pOrigEdge->GetConnection(true); + pConnObj = rConn1.GetObject(); + if(pConnObj) + { + SdrObject* pConnClone = ImpGetClone(aConnectorContainer, pConnObj); + if(pConnClone) + { + // if dest obj was cloned, too, re-establish connection + pCloneEdge->ConnectToNode(true, pConnClone); + pCloneEdge->GetConnection(true).SetConnectorId(rConn1.GetConnectorId()); + } + else + { + // set position of connection point of original connected object + const SdrGluePointList* pGlueList = pConnObj->GetGluePointList(); + if(pGlueList) + { + sal_uInt16 nInd = pGlueList->FindGluePoint(rConn1.GetConnectorId()); + + if(SDRGLUEPOINT_NOTFOUND != nInd) + { + const SdrGluePoint& rGluePoint = (*pGlueList)[nInd]; + Point aPosition = rGluePoint.GetAbsolutePos(*pConnObj); + aPosition.AdjustX(aVector.Width() ); + aPosition.AdjustY(aVector.Height() ); + pCloneEdge->SetTailPoint(true, aPosition); + } + } + } + } + } + } + } + + if( pMarkList != mpDragSrcMarkList.get() ) + delete pMarkList; + + bReturn = true; + } + else + { + maDropErrorIdle.Start(); + bReturn = false; + } + } + } + else + { + pOwnData->SetInternalMove( true ); + MoveAllMarked( Size( maDropPos.X() - pOwnData->GetStartPos().X(), + maDropPos.Y() - pOwnData->GetStartPos().Y() ), bCopy ); + bReturn = true; + } + } + } + else + { + // different views + if( !pSourceView->IsPresObjSelected() ) + { + // model is owned by from AllocModel() created DocShell + SdDrawDocument* pSourceDoc = static_cast<SdDrawDocument*>( pSourceView->GetModel() ); + pSourceDoc->CreatingDataObj( pOwnData ); + SdDrawDocument* pModel = static_cast<SdDrawDocument*>( pSourceView->CreateMarkedObjModel().release() ); + bReturn = Paste(*pModel, maDropPos, pPage, nPasteOptions); + + if( !pPage ) + pPage = static_cast<SdPage*>( GetSdrPageView()->GetPage() ); + + OUString aLayout = pPage->GetLayoutName(); + sal_Int32 nPos = aLayout.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayout = aLayout.copy(0, nPos); + pPage->SetPresentationLayout( aLayout, false, false ); + pSourceDoc->CreatingDataObj( nullptr ); + } + else + { + maDropErrorIdle.Start(); + bReturn = false; + } + } + } + else + { + SdDrawDocument* pWorkModel = const_cast<SdDrawDocument*>(pOwnData->GetWorkDocument()); + SdPage* pWorkPage = pWorkModel->GetSdPage( 0, PageKind::Standard ); + + pWorkPage->SetSdrObjListRectsDirty(); + + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + maDropPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + maDropPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + + // delete pages, that are not of any interest for us + for( ::tools::Long i = pWorkModel->GetPageCount() - 1; i >= 0; i-- ) + { + SdPage* pP = static_cast< SdPage* >( pWorkModel->GetPage( static_cast<sal_uInt16>(i) ) ); + + if( pP->GetPageKind() != PageKind::Standard ) + pWorkModel->DeletePage( static_cast<sal_uInt16>(i) ); + } + + bReturn = Paste(*pWorkModel, maDropPos, pPage, nPasteOptions); + + if( !pPage ) + pPage = static_cast<SdPage*>( GetSdrPageView()->GetPage() ); + + OUString aLayout = pPage->GetLayoutName(); + sal_Int32 nPos = aLayout.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayout = aLayout.copy(0, nPos); + pPage->SetPresentationLayout( aLayout, false, false ); + } + } + + if(!bReturn && CHECK_FORMAT_TRANS( SotClipboardFormatId::PDF )) + { + ::tools::SvRef<SotTempStream> xStm; + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::PDF, xStm ) ) + { + Point aInsertPos(rPos); + Graphic aGraphic; + if (vcl::ImportPDF(*xStm, aGraphic)) + { + std::unique_ptr<sal_uInt8[]> pGraphicContent; + + const sal_Int32 nGraphicContentSize(xStm->Tell()); + pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]); + xStm->Seek(0); + xStm->ReadBytes(pGraphicContent.get(), nGraphicContentSize); + aGraphic.SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, GfxLinkType::NativePdf)); + + InsertGraphic(aGraphic, mnAction, aInsertPos, nullptr, nullptr); + bReturn = true; + } + } + } + + if(!bReturn && CHECK_FORMAT_TRANS( SotClipboardFormatId::DRAWING )) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::DRAWING, xStm ) ) + { + DrawDocShellRef xShell = new DrawDocShell(SfxObjectCreateMode::INTERNAL, false, DocumentType::Impress); + xShell->DoInitNew(); + + SdDrawDocument* pModel = xShell->GetDoc(); + pModel->InsertPage(pModel->AllocPage(false).get()); + + Reference< XComponent > xComponent = xShell->GetModel(); + xStm->Seek( 0 ); + + css::uno::Reference< css::io::XInputStream > xInputStream( new utl::OInputStreamWrapper( *xStm ) ); + bReturn = SvxDrawingLayerImport( pModel, xInputStream, xComponent, "com.sun.star.comp.Impress.XMLOasisImporter" ); + + if( pModel->GetPageCount() == 0 ) + { + OSL_FAIL("empty or invalid drawing xml document on clipboard!" ); + } + else + { + bool bChanged = false; + + if( bReturn ) + { + if( pModel->GetSdPage( 0, PageKind::Standard )->GetObjCount() == 1 ) + { + // only one object + SdrObject* pObj = pModel->GetSdPage( 0, PageKind::Standard )->GetObj( 0 ); + SdrPageView* pPV = nullptr; + SdrObject* pPickObj2 = PickObj(rPos, getHitTolLog(), pPV); + + if( ( mnAction & DND_ACTION_MOVE ) && pPickObj2 && pObj ) + { + // replace object + SdrPage* pWorkPage = GetSdrPageView()->GetPage(); + SdrObject* pNewObj(pObj->CloneSdrObject(pWorkPage->getSdrModelFromSdrPage())); + ::tools::Rectangle aPickObjRect( pPickObj2->GetCurrentBoundRect() ); + Size aPickObjSize( aPickObjRect.GetSize() ); + Point aVec( aPickObjRect.TopLeft() ); + ::tools::Rectangle aObjRect( pNewObj->GetCurrentBoundRect() ); + Size aObjSize( aObjRect.GetSize() ); + + Fraction aScaleWidth( aPickObjSize.Width(), aObjSize.Width() ); + Fraction aScaleHeight( aPickObjSize.Height(), aObjSize.Height() ); + pNewObj->NbcResize( aObjRect.TopLeft(), aScaleWidth, aScaleHeight ); + + aVec -= aObjRect.TopLeft(); + pNewObj->NbcMove( Size( aVec.X(), aVec.Y() ) ); + + const bool bUndo = IsUndoEnabled(); + + if( bUndo ) + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + pNewObj->NbcSetLayer( pPickObj->GetLayer() ); + pWorkPage->InsertObject( pNewObj ); + if( bUndo ) + { + AddUndo( mrDoc.GetSdrUndoFactory().CreateUndoNewObject( *pNewObj ) ); + AddUndo( mrDoc.GetSdrUndoFactory().CreateUndoDeleteObject( *pPickObj2 ) ); + } + pWorkPage->RemoveObject( pPickObj2->GetOrdNum() ); + + if( bUndo ) + { + EndUndo(); + } + else + { + SdrObject::Free(pPickObj2 ); + } + bChanged = true; + mnAction = DND_ACTION_COPY; + } + else if( ( mnAction & DND_ACTION_LINK ) && pPickObj && pObj && + dynamic_cast< const SdrGrafObj *>( pPickObj ) == nullptr && + dynamic_cast< const SdrOle2Obj *>( pPickObj ) == nullptr ) + { + SfxItemSet aSet( mrDoc.GetPool() ); + + // set new attributes to object + const bool bUndo = IsUndoEnabled(); + if( bUndo ) + { + BegUndo( SdResId(STR_UNDO_DRAGDROP) ); + AddUndo( mrDoc.GetSdrUndoFactory().CreateUndoAttrObject( *pPickObj ) ); + } + + aSet.Put( pObj->GetMergedItemSet() ); + + /* Do not take over corner radius. There are + gradients (rectangles) in the gallery with corner + radius of 0. We should not use that on the + object. */ + aSet.ClearItem( SDRATTR_CORNER_RADIUS ); + + const SdrGrafObj* pSdrGrafObj = dynamic_cast< const SdrGrafObj* >(pObj); + + if(pSdrGrafObj) + { + // If we have a graphic as source object, use its graphic + // content as fill style + aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP)); + aSet.Put(XFillBitmapItem(pSdrGrafObj->GetGraphic())); + } + + pPickObj->SetMergedItemSetAndBroadcast( aSet ); + + if( dynamic_cast< E3dObject *>( pPickObj ) != nullptr && dynamic_cast< E3dObject *>( pObj ) != nullptr ) + { + // handle 3D attribute in addition + SfxItemSetFixed<SID_ATTR_3D_START, SID_ATTR_3D_END> aNewSet( mrDoc.GetPool() ); + SfxItemSetFixed<SID_ATTR_3D_START, SID_ATTR_3D_END> aOldSet( mrDoc.GetPool() ); + + aOldSet.Put(pPickObj->GetMergedItemSet()); + aNewSet.Put( pObj->GetMergedItemSet() ); + + if( bUndo ) + AddUndo( + std::make_unique<E3dAttributesUndoAction>( + *static_cast< E3dObject* >(pPickObj), + aNewSet, + aOldSet)); + pPickObj->SetMergedItemSetAndBroadcast( aNewSet ); + } + + if( bUndo ) + EndUndo(); + bChanged = true; + } + } + } + + if( !bChanged ) + { + SdrPage* pWorkPage = pModel->GetSdPage( 0, PageKind::Standard ); + + pWorkPage->SetSdrObjListRectsDirty(); + + if( pOwnData ) + { + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + maDropPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + maDropPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + bReturn = Paste(*pModel, maDropPos, pPage, nPasteOptions); + } + + xShell->DoClose(); + } + } + } + + if(!bReturn && CHECK_FORMAT_TRANS(SotClipboardFormatId::SBA_FIELDDATAEXCHANGE)) + { + OUString aOUString; + + if( aDataHelper.GetString( SotClipboardFormatId::SBA_FIELDDATAEXCHANGE, aOUString ) ) + { + SdrObjectUniquePtr pObj = CreateFieldControl( aOUString ); + + if( pObj ) + { + ::tools::Rectangle aRect( pObj->GetLogicRect() ); + Size aSize( aRect.GetSize() ); + + maDropPos.AdjustX( -( aSize.Width() >> 1 ) ); + maDropPos.AdjustY( -( aSize.Height() >> 1 ) ); + + aRect.SetPos( maDropPos ); + pObj->SetLogicRect( aRect ); + InsertObjectAtView( pObj.release(), *GetSdrPageView(), SdrInsertFlags::SETDEFLAYER ); + bReturn = true; + } + } + } + + if(!bReturn && + !bLink && + (CHECK_FORMAT_TRANS(SotClipboardFormatId::EMBED_SOURCE) || CHECK_FORMAT_TRANS(SotClipboardFormatId::EMBEDDED_OBJ)) && + aDataHelper.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR)) + { + //TODO/LATER: is it possible that this format is binary?! (from old versions of SO) + uno::Reference < io::XInputStream > xStm; + TransferableObjectDescriptor aObjDesc; + + if (aDataHelper.GetTransferableObjectDescriptor(SotClipboardFormatId::OBJECTDESCRIPTOR, aObjDesc)) + { + OUString aDocShellID = SfxObjectShell::CreateShellID(mrDoc.GetDocSh()); + xStm = aDataHelper.GetInputStream(nFormat != SotClipboardFormatId::NONE ? nFormat : SotClipboardFormatId::EMBED_SOURCE, aDocShellID); + if (!xStm.is()) + xStm = aDataHelper.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ, aDocShellID); + } + + if (xStm.is()) + { + if( mrDoc.GetDocSh() && ( mrDoc.GetDocSh()->GetClassName() == aObjDesc.maClassName ) ) + { + uno::Reference < embed::XStorage > xStore( ::comphelper::OStorageHelper::GetStorageFromInputStream( xStm ) ); + ::sd::DrawDocShellRef xDocShRef( new ::sd::DrawDocShell( SfxObjectCreateMode::EMBEDDED, true, mrDoc.GetDocumentType() ) ); + + // mba: BaseURL doesn't make sense for clipboard functionality + SfxMedium *pMedium = new SfxMedium( xStore, OUString() ); + if( xDocShRef->DoLoad( pMedium ) ) + { + SdDrawDocument* pModel = xDocShRef->GetDoc(); + SdPage* pWorkPage = pModel->GetSdPage( 0, PageKind::Standard ); + + pWorkPage->SetSdrObjListRectsDirty(); + + if( pOwnData ) + { + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + maDropPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + maDropPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + // delete pages, that are not of any interest for us + for( ::tools::Long i = pModel->GetPageCount() - 1; i >= 0; i-- ) + { + SdPage* pP = static_cast< SdPage* >( pModel->GetPage( static_cast<sal_uInt16>(i) ) ); + + if( pP->GetPageKind() != PageKind::Standard ) + pModel->DeletePage( static_cast<sal_uInt16>(i) ); + } + + bReturn = Paste(*pModel, maDropPos, pPage, nPasteOptions); + + if( !pPage ) + pPage = static_cast<SdPage*>(GetSdrPageView()->GetPage()); + + OUString aLayout = pPage->GetLayoutName(); + sal_Int32 nPos = aLayout.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + aLayout = aLayout.copy(0, nPos); + pPage->SetPresentationLayout( aLayout, false, false ); + } + + xDocShRef->DoClose(); + xDocShRef.clear(); + + } + else + { + OUString aName; + uno::Reference < embed::XEmbeddedObject > xObj = mpDocSh->GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName ); + if ( xObj.is() ) + { + svt::EmbeddedObjectRef aObjRef( xObj, aObjDesc.mnViewAspect ); + + Size aSize; + if ( aObjDesc.mnViewAspect == embed::Aspects::MSOLE_ICON ) + { + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + aSize = aObjDesc.maSize; + else + { + MapMode aMapMode( MapUnit::Map100thMM ); + aSize = aObjRef.GetSize( &aMapMode ); + } + } + else + { + awt::Size aSz; + MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( aObjDesc.mnViewAspect ) ); + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + { + Size aTmp(OutputDevice::LogicToLogic(aObjDesc.maSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit))); + aSz.Width = aTmp.Width(); + aSz.Height = aTmp.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + + try + { + aSz = xObj->getVisualAreaSize( aObjDesc.mnViewAspect ); + } + catch( embed::NoVisualAreaSizeException& ) + { + // if the size still was not set the default size will be set later + } + + aSize = Size( aSz.Width, aSz.Height ); + + if( !aSize.Width() || !aSize.Height() ) + { + aSize.setWidth( 14100 ); + aSize.setHeight( 10000 ); + aSize = OutputDevice::LogicToLogic(Size(14100, 10000), MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + + aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM)); + } + + Size aMaxSize( mrDoc.GetMaxObjSize() ); + + maDropPos.AdjustX( -(std::min( aSize.Width(), aMaxSize.Width() ) >> 1) ); + maDropPos.AdjustY( -(std::min( aSize.Height(), aMaxSize.Height() ) >> 1) ); + + ::tools::Rectangle aRect( maDropPos, aSize ); + SdrOle2Obj* pObj = new SdrOle2Obj( + getSdrModelFromSdrView(), + aObjRef, + aName, + aRect); + SdrPageView* pPV = GetSdrPageView(); + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + + if (mpViewSh!=nullptr) + { + OSL_ASSERT (mpViewSh->GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient + = mpViewSh->GetViewShell()->GetIPClient(); + if (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive()) + nOptions |= SdrInsertFlags::DONTMARK; + } + + // bInserted of false means that pObj has been deleted + bool bInserted = InsertObjectAtView( pObj, *pPV, nOptions ); + + if (bInserted && pImageMap) + pObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new SvxIMapInfo( *pImageMap )) ); + + if (bInserted && pObj->IsChart()) + { + bool bDisableDataTableDialog = false; + svt::EmbeddedObjectRef::TryRunningState( xObj ); + uno::Reference< beans::XPropertySet > xProps( xObj->getComponent(), uno::UNO_QUERY ); + if ( xProps.is() && + ( xProps->getPropertyValue( "DisableDataTableDialog" ) >>= bDisableDataTableDialog ) && + bDisableDataTableDialog ) + { + xProps->setPropertyValue( "DisableDataTableDialog" , uno::Any( false ) ); + xProps->setPropertyValue( "DisableComplexChartTypes" , uno::Any( false ) ); + uno::Reference< util::XModifiable > xModifiable( xProps, uno::UNO_QUERY ); + if ( xModifiable.is() ) + { + xModifiable->setModified( true ); + } + } + } + + bReturn = true; + } + } + } + } + + if(!bReturn && + !bLink && + (CHECK_FORMAT_TRANS(SotClipboardFormatId::EMBEDDED_OBJ_OLE) || CHECK_FORMAT_TRANS(SotClipboardFormatId::EMBED_SOURCE_OLE)) && + aDataHelper.HasFormat(SotClipboardFormatId::OBJECTDESCRIPTOR_OLE)) + { + // online insert ole if format is forced or no gdi metafile is available + if( (nFormat != SotClipboardFormatId::NONE) || !aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) ) + { + uno::Reference < io::XInputStream > xStm; + TransferableObjectDescriptor aObjDesc; + + if ( aDataHelper.GetTransferableObjectDescriptor( SotClipboardFormatId::OBJECTDESCRIPTOR_OLE, aObjDesc ) ) + { + uno::Reference < embed::XEmbeddedObject > xObj; + OUString aName; + + xStm = aDataHelper.GetInputStream(nFormat != SotClipboardFormatId::NONE ? nFormat : SotClipboardFormatId::EMBED_SOURCE_OLE, OUString()); + if (!xStm.is()) + xStm = aDataHelper.GetInputStream(SotClipboardFormatId::EMBEDDED_OBJ_OLE, OUString()); + + if (xStm.is()) + { + xObj = mpDocSh->GetEmbeddedObjectContainer().InsertEmbeddedObject( xStm, aName ); + } + else + { + try + { + uno::Reference< embed::XStorage > xTmpStor = ::comphelper::OStorageHelper::GetTemporaryStorage(); + uno::Reference < embed::XEmbedObjectClipboardCreator > xClipboardCreator = + embed::MSOLEObjectSystemCreator::create( ::comphelper::getProcessComponentContext() ); + + embed::InsertedObjectInfo aInfo = xClipboardCreator->createInstanceInitFromClipboard( + xTmpStor, + "DummyName" , + uno::Sequence< beans::PropertyValue >() ); + + // TODO/LATER: in future InsertedObjectInfo will be used to get container related information + // for example whether the object should be an iconified one + xObj = aInfo.Object; + if ( xObj.is() ) + mpDocSh->GetEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aName ); + } + catch( uno::Exception& ) + {} + } + + if ( xObj.is() ) + { + svt::EmbeddedObjectRef aObjRef( xObj, aObjDesc.mnViewAspect ); + + // try to get the replacement image from the clipboard + Graphic aGraphic; + SotClipboardFormatId nGrFormat = SotClipboardFormatId::NONE; + +// (for Selection Manager in Trusted Solaris) +#ifndef __sun + if( aDataHelper.GetGraphic( SotClipboardFormatId::SVXB, aGraphic ) ) + nGrFormat = SotClipboardFormatId::SVXB; + else if( aDataHelper.GetGraphic( SotClipboardFormatId::GDIMETAFILE, aGraphic ) ) + nGrFormat = SotClipboardFormatId::GDIMETAFILE; + else if( aDataHelper.GetGraphic( SotClipboardFormatId::BITMAP, aGraphic ) ) + nGrFormat = SotClipboardFormatId::BITMAP; +#endif + + // insert replacement image ( if there is one ) into the object helper + if ( nGrFormat != SotClipboardFormatId::NONE ) + { + datatransfer::DataFlavor aDataFlavor; + SotExchange::GetFormatDataFlavor( nGrFormat, aDataFlavor ); + aObjRef.SetGraphic( aGraphic, aDataFlavor.MimeType ); + } + + Size aSize; + if ( aObjDesc.mnViewAspect == embed::Aspects::MSOLE_ICON ) + { + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + aSize = aObjDesc.maSize; + else + { + MapMode aMapMode( MapUnit::Map100thMM ); + aSize = aObjRef.GetSize( &aMapMode ); + } + } + else + { + MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( aObjDesc.mnViewAspect ) ); + + awt::Size aSz; + try{ + aSz = xObj->getVisualAreaSize( aObjDesc.mnViewAspect ); + } + catch( embed::NoVisualAreaSizeException& ) + { + // the default size will be set later + } + + if( aObjDesc.maSize.Width() && aObjDesc.maSize.Height() ) + { + Size aTmp(OutputDevice::LogicToLogic(aObjDesc.maSize, MapMode(MapUnit::Map100thMM), MapMode(aMapUnit))); + if ( aSz.Width != aTmp.Width() || aSz.Height != aTmp.Height() ) + { + aSz.Width = aTmp.Width(); + aSz.Height = aTmp.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + } + + aSize = Size( aSz.Width, aSz.Height ); + + if( !aSize.Width() || !aSize.Height() ) + { + aSize = OutputDevice::LogicToLogic(Size(14100, 10000), MapMode(MapUnit::Map100thMM), MapMode(aMapUnit)); + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + xObj->setVisualAreaSize( aObjDesc.mnViewAspect, aSz ); + } + + aSize = OutputDevice::LogicToLogic(aSize, MapMode(aMapUnit), MapMode(MapUnit::Map100thMM)); + } + + Size aMaxSize( mrDoc.GetMaxObjSize() ); + + maDropPos.AdjustX( -(std::min( aSize.Width(), aMaxSize.Width() ) >> 1) ); + maDropPos.AdjustY( -(std::min( aSize.Height(), aMaxSize.Height() ) >> 1) ); + + ::tools::Rectangle aRect( maDropPos, aSize ); + SdrOle2Obj* pObj = new SdrOle2Obj( + getSdrModelFromSdrView(), + aObjRef, + aName, + aRect); + SdrPageView* pPV = GetSdrPageView(); + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + + if (mpViewSh!=nullptr) + { + OSL_ASSERT (mpViewSh->GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient + = mpViewSh->GetViewShell()->GetIPClient(); + if (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive()) + nOptions |= SdrInsertFlags::DONTMARK; + } + + bReturn = InsertObjectAtView( pObj, *pPV, nOptions ); + + if (bReturn) + { + if( pImageMap ) + pObj->AppendUserData( std::unique_ptr<SdrObjUserData>(new SvxIMapInfo( *pImageMap )) ); + + // let the object stay in loaded state after insertion + pObj->Unload(); + } + } + } + } + + if( !bReturn && aDataHelper.HasFormat( SotClipboardFormatId::GDIMETAFILE ) ) + { + // if no object was inserted, insert a picture + InsertMetaFile( aDataHelper, rPos, pImageMap.get(), true ); + bReturn = true; + } + } + + if(!bReturn && (!bLink || pPickObj) && CHECK_FORMAT_TRANS(SotClipboardFormatId::SVXB)) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::SVXB, xStm ) ) + { + Point aInsertPos( rPos ); + Graphic aGraphic; + + TypeSerializer aSerializer(*xStm); + aSerializer.readGraphic(aGraphic); + + if( pOwnData && pOwnData->GetWorkDocument() ) + { + const SdDrawDocument* pWorkModel = pOwnData->GetWorkDocument(); + SdrPage* pWorkPage = const_cast<SdrPage*>( ( pWorkModel->GetPageCount() > 1 ) ? + pWorkModel->GetSdPage( 0, PageKind::Standard ) : + pWorkModel->GetPage( 0 ) ); + + pWorkPage->SetSdrObjListRectsDirty(); + + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + aInsertPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + aInsertPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + // restrict movement to WorkArea + Size aImageMapSize = OutputDevice::LogicToLogic(aGraphic.GetPrefSize(), + aGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)); + + ImpCheckInsertPos(aInsertPos, aImageMapSize, GetWorkArea()); + + InsertGraphic( aGraphic, mnAction, aInsertPos, nullptr, pImageMap.get() ); + bReturn = true; + } + } + + if(!bReturn && (!bLink || pPickObj) && CHECK_FORMAT_TRANS(SotClipboardFormatId::GDIMETAFILE)) + { + Point aInsertPos( rPos ); + + if( pOwnData && pOwnData->GetWorkDocument() ) + + { + const SdDrawDocument* pWorkModel = pOwnData->GetWorkDocument(); + SdrPage* pWorkPage = const_cast<SdrPage*>( ( pWorkModel->GetPageCount() > 1 ) ? + pWorkModel->GetSdPage( 0, PageKind::Standard ) : + pWorkModel->GetPage( 0 ) ); + + pWorkPage->SetSdrObjListRectsDirty(); + + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + aInsertPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + aInsertPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + bReturn = InsertMetaFile( aDataHelper, aInsertPos, pImageMap.get(), nFormat == SotClipboardFormatId::NONE ); + } + + if(!bReturn && (!bLink || pPickObj) && CHECK_FORMAT_TRANS(SotClipboardFormatId::BITMAP)) + { + BitmapEx aBmpEx; + + // get basic Bitmap data + aDataHelper.GetBitmapEx(SotClipboardFormatId::BITMAP, aBmpEx); + + if(aBmpEx.IsEmpty()) + { + // if this did not work, try to get graphic formats and convert these to bitmap + Graphic aGraphic; + + if(aDataHelper.GetGraphic(SotClipboardFormatId::GDIMETAFILE, aGraphic)) + { + aBmpEx = aGraphic.GetBitmapEx(); + } + else if(aDataHelper.GetGraphic(SotClipboardFormatId::SVXB, aGraphic)) + { + aBmpEx = aGraphic.GetBitmapEx(); + } + else if(aDataHelper.GetGraphic(SotClipboardFormatId::BITMAP, aGraphic)) + { + aBmpEx = aGraphic.GetBitmapEx(); + } + } + + if(!aBmpEx.IsEmpty()) + { + Point aInsertPos( rPos ); + + if( pOwnData && pOwnData->GetWorkDocument() ) + { + const SdDrawDocument* pWorkModel = pOwnData->GetWorkDocument(); + SdrPage* pWorkPage = const_cast<SdrPage*>( ( pWorkModel->GetPageCount() > 1 ) ? + pWorkModel->GetSdPage( 0, PageKind::Standard ) : + pWorkModel->GetPage( 0 ) ); + + pWorkPage->SetSdrObjListRectsDirty(); + + // #i120393# Clipboard data uses full object geometry range + const Size aSize( pWorkPage->GetAllObjBoundRect().GetSize() ); + + aInsertPos.setX( pOwnData->GetStartPos().X() + ( aSize.Width() >> 1 ) ); + aInsertPos.setY( pOwnData->GetStartPos().Y() + ( aSize.Height() >> 1 ) ); + } + + // restrict movement to WorkArea + Size aImageMapSize(aBmpEx.GetPrefSize()); + ImpCheckInsertPos(aInsertPos, aImageMapSize, GetWorkArea()); + + InsertGraphic( aBmpEx, mnAction, aInsertPos, nullptr, pImageMap.get() ); + bReturn = true; + } + } + + if(!bReturn && pPickObj && CHECK_FORMAT_TRANS( SotClipboardFormatId::XFA ) ) + { + uno::Any const data(aDataHelper.GetAny(SotClipboardFormatId::XFA, "")); + uno::Sequence<beans::NamedValue> props; + if (data >>= props) + { + if( IsUndoEnabled() ) + { + BegUndo( SdResId(STR_UNDO_DRAGDROP) ); + AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoAttrObject( *pPickObj ) ); + EndUndo(); + } + + ::comphelper::SequenceAsHashMap const map(props); + drawing::FillStyle eFill(drawing::FillStyle_BITMAP); // default to something that's ignored + Color aColor(COL_BLACK); + auto it = map.find("FillStyle"); + if (it != map.end()) + { + XFillStyleItem style; + style.PutValue(it->second, 0); + eFill = style.GetValue(); + } + it = map.find("FillColor"); + if (it != map.end()) + { + XFillColorItem color; + color.PutValue(it->second, 0); + aColor = color.GetColorValue(); + } + + if( eFill == drawing::FillStyle_SOLID || eFill == drawing::FillStyle_NONE ) + { + SfxItemSet aSet( mrDoc.GetPool() ); + bool bClosed = pPickObj->IsClosedObj(); + ::sd::Window* pWin = mpViewSh->GetActiveWindow(); + sal_uInt16 nHitLog = static_cast<sal_uInt16>(pWin->PixelToLogic( + Size(FuPoor::HITPIX, 0 ) ).Width()); + const ::tools::Long n2HitLog = nHitLog << 1; + Point aHitPosR( rPos ); + Point aHitPosL( rPos ); + Point aHitPosT( rPos ); + Point aHitPosB( rPos ); + const SdrLayerIDSet* pVisiLayer = &GetSdrPageView()->GetVisibleLayers(); + + aHitPosR.AdjustX(n2HitLog ); + aHitPosL.AdjustX( -n2HitLog ); + aHitPosT.AdjustY(n2HitLog ); + aHitPosB.AdjustY( -n2HitLog ); + + if( bClosed && + SdrObjectPrimitiveHit(*pPickObj, aHitPosR, nHitLog, *GetSdrPageView(), pVisiLayer, false) && + SdrObjectPrimitiveHit(*pPickObj, aHitPosL, nHitLog, *GetSdrPageView(), pVisiLayer, false) && + SdrObjectPrimitiveHit(*pPickObj, aHitPosT, nHitLog, *GetSdrPageView(), pVisiLayer, false) && + SdrObjectPrimitiveHit(*pPickObj, aHitPosB, nHitLog, *GetSdrPageView(), pVisiLayer, false) ) + { + // area fill + if(eFill == drawing::FillStyle_SOLID ) + aSet.Put(XFillColorItem("", aColor)); + + aSet.Put( XFillStyleItem( eFill ) ); + } + else + aSet.Put( XLineColorItem( "", aColor ) ); + + // add text color + pPickObj->SetMergedItemSetAndBroadcast( aSet ); + } + bReturn = true; + } + } + + if(!bReturn && !bLink && CHECK_FORMAT_TRANS(SotClipboardFormatId::HTML)) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::HTML, xStm ) ) + { + xStm->Seek( 0 ); + // mba: clipboard always must contain absolute URLs (could be from alien source) + bReturn = SdrView::Paste( *xStm, EETextFormat::Html, maDropPos, pPage, nPasteOptions ); + } + } + + if(!bReturn && !bLink && CHECK_FORMAT_TRANS(SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT)) + { + ::tools::SvRef<SotTempStream> xStm; + if( aDataHelper.GetSotStorageStream( SotClipboardFormatId::EDITENGINE_ODF_TEXT_FLAT, xStm ) ) + { + OutlinerView* pOLV = GetTextEditOutlinerView(); + + xStm->Seek( 0 ); + + if( pOLV ) + { + ::tools::Rectangle aRect( pOLV->GetOutputArea() ); + Point aPos( pOLV->GetWindow()->PixelToLogic( maDropPos ) ); + + if( aRect.Contains( aPos ) || ( !bDrag && IsTextEdit() ) ) + { + // mba: clipboard always must contain absolute URLs (could be from alien source) + pOLV->Read( *xStm, EETextFormat::Xml, mpDocSh->GetHeaderAttributes() ); + bReturn = true; + } + } + + if( !bReturn ) + // mba: clipboard always must contain absolute URLs (could be from alien source) + bReturn = SdrView::Paste( *xStm, EETextFormat::Xml, maDropPos, pPage, nPasteOptions ); + } + } + + if(!bReturn && !bLink) + { + bool bIsRTF = CHECK_FORMAT_TRANS(SotClipboardFormatId::RTF); + if (bIsRTF || CHECK_FORMAT_TRANS(SotClipboardFormatId::RICHTEXT)) + { + ::tools::SvRef<SotTempStream> xStm; + + if( aDataHelper.GetSotStorageStream( bIsRTF ? SotClipboardFormatId::RTF : SotClipboardFormatId::RICHTEXT, xStm ) ) + { + xStm->Seek( 0 ); + + if( bTable ) + { + bReturn = PasteRTFTable( xStm, pPage, nPasteOptions ); + } + else + { + OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV ) + { + ::tools::Rectangle aRect( pOLV->GetOutputArea() ); + Point aPos( pOLV->GetWindow()->PixelToLogic( maDropPos ) ); + + if( aRect.Contains( aPos ) || ( !bDrag && IsTextEdit() ) ) + { + // mba: clipboard always must contain absolute URLs (could be from alien source) + pOLV->Read( *xStm, EETextFormat::Rtf, mpDocSh->GetHeaderAttributes() ); + bReturn = true; + } + } + + if( !bReturn ) + // mba: clipboard always must contain absolute URLs (could be from alien source) + bReturn = SdrView::Paste( *xStm, EETextFormat::Rtf, maDropPos, pPage, nPasteOptions ); + } + } + } + } + + if(!bReturn && CHECK_FORMAT_TRANS(SotClipboardFormatId::FILE_LIST)) + { + FileList aDropFileList; + + if( aDataHelper.GetFileList( SotClipboardFormatId::FILE_LIST, aDropFileList ) ) + { + maDropFileVector.clear(); + + for( sal_uLong i = 0, nCount = aDropFileList.Count(); i < nCount; i++ ) + maDropFileVector.push_back( aDropFileList.GetFile( i ) ); + + maDropInsertFileIdle.Start(); + } + + bReturn = true; + } + + if(!bReturn && CHECK_FORMAT_TRANS(SotClipboardFormatId::SIMPLE_FILE)) + { + OUString aDropFile; + + if( aDataHelper.GetString( SotClipboardFormatId::SIMPLE_FILE, aDropFile ) ) + { + maDropFileVector.clear(); + maDropFileVector.push_back( aDropFile ); + maDropInsertFileIdle.Start(); + } + + bReturn = true; + } + + if(!bReturn && !bLink && CHECK_FORMAT_TRANS(SotClipboardFormatId::STRING)) + { + if( ( SotClipboardFormatId::STRING == nFormat ) || + ( !aDataHelper.HasFormat( SotClipboardFormatId::SOLK ) && + !aDataHelper.HasFormat( SotClipboardFormatId::NETSCAPE_BOOKMARK ) && + !aDataHelper.HasFormat( SotClipboardFormatId::FILENAME ) ) ) + { + OUString aOUString; + + if( aDataHelper.GetString( SotClipboardFormatId::STRING, aOUString ) ) + { + OutlinerView* pOLV = GetTextEditOutlinerView(); + + if( pOLV ) + { + pOLV->InsertText( aOUString ); + bReturn = true; + } + + if( !bReturn ) + bReturn = SdrView::Paste( aOUString, maDropPos, pPage, nPasteOptions ); + } + } + } + + MarkListHasChanged(); + mbIsDropAllowed = true; + rDnDAction = mnAction; + + if (bGroupUndoFromDragWithDrop) + { + // this is called eventually by the underlying toolkit anyway in the case of a self-dnd + // but we call it early in this case to group its undo actions into this open dnd undo group + // and rely on that repeated calls to View::DragFinished are safe to do + DragFinished(mnAction); + EndUndo(); + } + + return bReturn; +} + +bool View::PasteRTFTable( const ::tools::SvRef<SotTempStream>& xStm, SdrPage* pPage, SdrInsertFlags nPasteOptions ) +{ + SdDrawDocument aModel( DocumentType::Impress, mpDocSh ); + aModel.NewOrLoadCompleted(DocCreationMode::New); + aModel.GetItemPool().SetDefaultMetric(MapUnit::Map100thMM); + aModel.InsertPage(aModel.AllocPage(false).get()); + + Reference< XComponent > xComponent( new SdXImpressDocument( &aModel, true ) ); + aModel.setUnoModel( Reference< XInterface >::query( xComponent ) ); + + CreateTableFromRTF( *xStm, &aModel ); + bool bRet = Paste(aModel, maDropPos, pPage, nPasteOptions); + + xComponent->dispose(); + xComponent.clear(); + + return bRet; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview4.cxx b/sd/source/ui/view/sdview4.cxx new file mode 100644 index 000000000..7a3c7c226 --- /dev/null +++ b/sd/source/ui/view/sdview4.cxx @@ -0,0 +1,645 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_features.h> + +#include <View.hxx> + +#include <comphelper/propertyvalue.hxx> +#include <osl/file.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> +#include <sfx2/docfilt.hxx> +#include <sfx2/fcontnr.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/sfxsids.hrc> +#include <vcl/outdev.hxx> +#include <vcl/pdfread.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <svx/svdpagv.hxx> +#include <svx/xbtmpit.hxx> +#include <svx/svdundo.hxx> +#include <svx/xfillit0.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdomedia.hxx> +#include <svx/svdoole2.hxx> +#include <svx/ImageMapInfo.hxx> +#include <sfx2/app.hxx> +#include <avmedia/mediawindow.hxx> +#include <svtools/ehdl.hxx> +#include <svtools/sfxecode.hxx> +#include <svtools/embedhlp.hxx> +#include <vcl/graphicfilter.hxx> +#include <app.hrc> +#include <Window.hxx> +#include <DrawDocShell.hxx> +#include <DrawViewShell.hxx> +#include <fuinsfil.hxx> +#include <drawdoc.hxx> +#include <sdresid.hxx> +#include <strings.hrc> +#include <sdpage.hxx> +#include <view/SlideSorterView.hxx> +#include <com/sun/star/embed/XEmbedPersist.hpp> +#include <com/sun/star/embed/Aspects.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/media/XPlayer.hpp> +#include <svtools/soerr.hxx> +#include <sfx2/ipclient.hxx> +#include <tools/debug.hxx> + +using namespace com::sun::star; + +namespace sd { + +/** + * If an empty graphic object is provided, we fill it. Otherwise we fill an + * existing object at the specified position. If there is no object at the + * position, we create a new object and return a pointer to it. + */ +SdrGrafObj* View::InsertGraphic( const Graphic& rGraphic, sal_Int8& rAction, + const Point& rPos, SdrObject* pObj, ImageMap const * pImageMap ) +{ + SdrEndTextEdit(); + mnAction = rAction; + + // Is there an object at the position rPos? + SdrGrafObj* pNewGrafObj = nullptr; + SdrPageView* pPV = GetSdrPageView(); + SdrObject* pPickObj = pObj; + const bool bOnMaster = pPV && pPV->GetPage() && pPV->GetPage()->IsMasterPage(); + + if(pPV && dynamic_cast< const ::sd::slidesorter::view::SlideSorterView* >(this) != nullptr) + { + if(!pPV->GetPageRect().Contains(rPos)) + pPV = nullptr; + } + + if( !pPickObj && pPV ) + { + SdrPageView* pPageView = pPV; + pPickObj = PickObj(rPos, getHitTolLog(), pPageView); + } + + const bool bIsGraphic(dynamic_cast< const SdrGrafObj* >(pPickObj) != nullptr); + + if (DND_ACTION_LINK == mnAction + && pPickObj + && pPV + && (bIsGraphic || (pPickObj->IsEmptyPresObj() && !bOnMaster))) // #121603# Do not use pObj, it may be NULL + { + // hit on SdrGrafObj with wanted new linked graphic (or PresObj placeholder hit) + if( IsUndoEnabled() ) + BegUndo(SdResId(STR_INSERTGRAPHIC)); + + SdPage* pPage = static_cast<SdPage*>( pPickObj->getSdrPageFromSdrObject() ); + + if( bIsGraphic ) + { + // We fill the object with the Bitmap + pNewGrafObj = static_cast<SdrGrafObj*>( pPickObj->CloneSdrObject(pPickObj->getSdrModelFromSdrObject()) ); + pNewGrafObj->SetGraphic(rGraphic); + } + else + { + pNewGrafObj = new SdrGrafObj( + getSdrModelFromSdrView(), + rGraphic, + pPickObj->GetLogicRect()); + pNewGrafObj->SetEmptyPresObj(true); + } + + if ( pNewGrafObj->IsEmptyPresObj() ) + { + ::tools::Rectangle aRect( pNewGrafObj->GetLogicRect() ); + pNewGrafObj->AdjustToMaxRect( aRect ); + pNewGrafObj->SetOutlinerParaObject(std::nullopt); + pNewGrafObj->SetEmptyPresObj(false); + } + + if (pPage && pPage->IsPresObj(pPickObj)) + { + // Insert new PresObj into the list + pPage->InsertPresObj( pNewGrafObj, PresObjKind::Graphic ); + pNewGrafObj->SetUserCall(pPickObj->GetUserCall()); + } + + if (pImageMap) + pNewGrafObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(*pImageMap))); + + ReplaceObjectAtView(pPickObj, *pPV, pNewGrafObj); // maybe ReplaceObjectAtView + + if( IsUndoEnabled() ) + EndUndo(); + } + else if (DND_ACTION_LINK == mnAction + && pPickObj + && !bIsGraphic + && pPickObj->IsClosedObj() + && !dynamic_cast< const SdrOle2Obj* >(pPickObj)) + { + // fill style change (fill object with graphic), independent of mnAction + // and thus of DND_ACTION_LINK or DND_ACTION_MOVE + if( IsUndoEnabled() ) + { + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + AddUndo(GetModel()->GetSdrUndoFactory().CreateUndoAttrObject(*pPickObj)); + EndUndo(); + } + + SfxItemSetFixed<XATTR_FILLSTYLE, XATTR_FILLBITMAP> aSet(mpDocSh->GetPool()); + + aSet.Put(XFillStyleItem(drawing::FillStyle_BITMAP)); + aSet.Put(XFillBitmapItem(rGraphic)); + pPickObj->SetMergedItemSetAndBroadcast(aSet); + } + + else if ( pPV ) + { + Size aSizePixel = rGraphic.GetSizePixel(); + + // create new object + Size aSize; + + if ( rGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel ) + { + ::OutputDevice* pOutDev = nullptr; + if( mpViewSh ) + pOutDev = mpViewSh->GetActiveWindow()->GetOutDev(); + + if( !pOutDev ) + pOutDev = Application::GetDefaultDevice(); + + if( pOutDev ) + aSize = pOutDev->PixelToLogic(rGraphic.GetPrefSize(), MapMode(MapUnit::Map100thMM)); + } + else + { + aSize = OutputDevice::LogicToLogic( rGraphic.GetPrefSize(), + rGraphic.GetPrefMapMode(), + MapMode( MapUnit::Map100thMM ) ); + } + + sal_Int32 nPreferredDPI = mrDoc.getImagePreferredDPI(); + + if (rGraphic.GetGfxLink().GetType() == GfxLinkType::NativePdf && nPreferredDPI == 0 && vcl::PDF_INSERT_MAGIC_SCALE_FACTOR > 1) + nPreferredDPI = Application::GetDefaultDevice()->GetDPIX() * vcl::PDF_INSERT_MAGIC_SCALE_FACTOR; + + if (nPreferredDPI > 0) + { + auto nWidth = o3tl::convert(aSizePixel.Width() / double(nPreferredDPI), o3tl::Length::in, o3tl::Length::mm100); + auto nHeight = o3tl::convert(aSizePixel.Height() / double(nPreferredDPI), o3tl::Length::in, o3tl::Length::mm100); + if (nWidth > 0 && nHeight > 0) + aSize = Size(nWidth, nHeight); + } + + pNewGrafObj = new SdrGrafObj(getSdrModelFromSdrView(), rGraphic, ::tools::Rectangle(rPos, aSize)); + + if (nPreferredDPI > 0) + { + // move to the center of insertion point + pNewGrafObj->NbcMove(Size(-aSize.Width() / 2, -aSize.Height() / 2)); + } + else + { + SdrPage* pPage = pPV->GetPage(); + Size aPageSize( pPage->GetSize() ); + aPageSize.AdjustWidth( -(pPage->GetLeftBorder() + pPage->GetRightBorder()) ); + aPageSize.AdjustHeight( -(pPage->GetUpperBorder() + pPage->GetLowerBorder()) ); + pNewGrafObj->AdjustToMaxRect( ::tools::Rectangle( Point(), aPageSize ), true ); + } + + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + bool bIsPresTarget = false; + + if ((mpViewSh + && mpViewSh->GetViewShell()!=nullptr + && mpViewSh->GetViewShell()->GetIPClient() + && mpViewSh->GetViewShell()->GetIPClient()->IsObjectInPlaceActive()) + || dynamic_cast<const ::sd::slidesorter::view::SlideSorterView* >(this)) + nOptions |= SdrInsertFlags::DONTMARK; + + if( ( mnAction & DND_ACTION_MOVE ) && pPickObj && (pPickObj->IsEmptyPresObj() || pPickObj->GetUserCall()) ) + { + SdPage* pP = static_cast< SdPage* >( pPickObj->getSdrPageFromSdrObject() ); + + if ( pP && pP->IsMasterPage() ) + bIsPresTarget = pP->IsPresObj(pPickObj); + } + + if( ( mnAction & DND_ACTION_MOVE ) && pPickObj && !bIsPresTarget ) + { + // replace object + if (pImageMap) + pNewGrafObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(*pImageMap))); + + ::tools::Rectangle aPickObjRect(pPickObj->GetCurrentBoundRect()); + Size aPickObjSize(aPickObjRect.GetSize()); + ::tools::Rectangle aObjRect(pNewGrafObj->GetCurrentBoundRect()); + Size aObjSize(aObjRect.GetSize()); + + Fraction aScaleWidth(aPickObjSize.Width(), aObjSize.Width()); + Fraction aScaleHeight(aPickObjSize.Height(), aObjSize.Height()); + pNewGrafObj->NbcResize(aObjRect.TopLeft(), aScaleWidth, aScaleHeight); + + Point aVec = aPickObjRect.TopLeft() - aObjRect.TopLeft(); + pNewGrafObj->NbcMove(Size(aVec.X(), aVec.Y())); + + const bool bUndo = IsUndoEnabled(); + + if( bUndo ) + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + pNewGrafObj->NbcSetLayer(pPickObj->GetLayer()); + SdrPage* pP = pPV->GetPage(); + pP->InsertObject(pNewGrafObj); + if( bUndo ) + { + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoNewObject(*pNewGrafObj)); + AddUndo(mrDoc.GetSdrUndoFactory().CreateUndoDeleteObject(*pPickObj)); + } + pP->RemoveObject(pPickObj->GetOrdNum()); + + if( bUndo ) + { + EndUndo(); + } + else + { + SdrObject::Free(pPickObj); + } + mnAction = DND_ACTION_COPY; + } + else + { + bool bSuccess = InsertObjectAtView(pNewGrafObj, *pPV, nOptions); + if (!bSuccess) + pNewGrafObj = nullptr; + else if (pImageMap) + pNewGrafObj->AppendUserData(std::unique_ptr<SdrObjUserData>(new SvxIMapInfo(*pImageMap))); + } + } + + rAction = mnAction; + + return pNewGrafObj; +} + +void View::InsertMediaURL( const OUString& rMediaURL, sal_Int8& rAction, + const Point& rPos, const Size& rSize, + bool const bLink ) +{ + OUString realURL; + if (bLink) + { + realURL = rMediaURL; + } + else + { + uno::Reference<frame::XModel> const xModel( + GetDoc().GetObjectShell()->GetModel()); +#if HAVE_FEATURE_AVMEDIA + bool const bRet = ::avmedia::EmbedMedia(xModel, rMediaURL, realURL); + if (!bRet) { return; } +#else + return; +#endif + } + + InsertMediaObj( realURL, "application/vnd.sun.star.media", rAction, rPos, rSize ); +} + +SdrMediaObj* View::InsertMediaObj( const OUString& rMediaURL, const OUString& rMimeType, sal_Int8& rAction, + const Point& rPos, const Size& rSize ) +{ + SdrEndTextEdit(); + mnAction = rAction; + + SdrMediaObj* pNewMediaObj = nullptr; + SdrPageView* pPV = GetSdrPageView(); + SdrObject* pPickObj = GetEmptyPresentationObject( PresObjKind::Media ); + + if(pPV && dynamic_cast<const ::sd::slidesorter::view::SlideSorterView* >(this) ) + { + if(!pPV->GetPageRect().Contains(rPos)) + pPV = nullptr; + } + + if( mnAction == DND_ACTION_LINK && pPV && dynamic_cast< SdrMediaObj *>( pPickObj ) ) + { + pNewMediaObj = static_cast< SdrMediaObj* >( pPickObj->CloneSdrObject(pPickObj->getSdrModelFromSdrObject()) ); + pNewMediaObj->setURL( rMediaURL, ""/*TODO?*/, rMimeType ); + + BegUndo(SdResId(STR_UNDO_DRAGDROP)); + ReplaceObjectAtView(pPickObj, *pPV, pNewMediaObj); + EndUndo(); + } + else if( pPV ) + { + ::tools::Rectangle aRect( rPos, rSize ); + SdrObjUserCall* pUserCall = nullptr; + if( pPickObj ) + { + aRect = pPickObj->GetLogicRect(); + pUserCall = pPickObj->GetUserCall(); // ReplaceObjectAtView can free pPickObj + } + + pNewMediaObj = new SdrMediaObj( + getSdrModelFromSdrView(), + aRect); + + bool bIsPres = false; + if( pPickObj ) + { + SdPage* pPage = static_cast< SdPage* >(pPickObj->getSdrPageFromSdrObject()); + bIsPres = pPage && pPage->IsPresObj(pPickObj); + if( bIsPres ) + { + pPage->InsertPresObj( pNewMediaObj, PresObjKind::Media ); + } + } + + if( pPickObj ) + ReplaceObjectAtView(pPickObj, *pPV, pNewMediaObj); + else + { + if (!InsertObjectAtView(pNewMediaObj, *pPV, SdrInsertFlags::SETDEFLAYER)) + pNewMediaObj = nullptr; + } + + OUString referer; + DrawDocShell * sh = GetDocSh(); + if (sh != nullptr && sh->HasName()) { + referer = sh->GetMedium()->GetName(); + } + + if (pNewMediaObj) + { + pNewMediaObj->setURL( rMediaURL, referer, rMimeType ); + + if( pPickObj ) + { + pNewMediaObj->AdjustToMaxRect( aRect ); + if( bIsPres ) + pNewMediaObj->SetUserCall( pUserCall ); + } + } + } + + rAction = mnAction; + + return pNewMediaObj; +} + +/** + * Timer handler for InsertFile at Drop() + */ +IMPL_LINK_NOARG(View, DropInsertFileHdl, Timer *, void) +{ + DBG_ASSERT( mpViewSh, "sd::View::DropInsertFileHdl(), I need a view shell to work!" ); + if( !mpViewSh ) + return; + + SfxErrorContext aEc( ERRCTX_ERROR, mpViewSh->GetFrameWeld(), RID_SO_ERRCTX ); + ErrCode nError = ERRCODE_NONE; + + ::std::vector< OUString >::const_iterator aIter( maDropFileVector.begin() ); + + while( (aIter != maDropFileVector.end()) && !nError ) + { + OUString aCurrentDropFile( *aIter ); + INetURLObject aURL( aCurrentDropFile ); + bool bHandled = false; + + if( aURL.GetProtocol() == INetProtocol::NotValid ) + { + OUString aURLStr; + osl::FileBase::getFileURLFromSystemPath( aCurrentDropFile, aURLStr ); + aURL = INetURLObject( aURLStr ); + } + + GraphicFilter& rGraphicFilter = GraphicFilter::GetGraphicFilter(); + Graphic aGraphic; + + aCurrentDropFile = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + +#if HAVE_FEATURE_AVMEDIA + if( !::avmedia::MediaWindow::isMediaURL( aCurrentDropFile, ""/*TODO?*/ ) ) +#else +#endif + { + if( !rGraphicFilter.ImportGraphic( aGraphic, aURL ) ) + { + sal_Int8 nTempAction = ( aIter == maDropFileVector.begin() ) ? mnAction : 0; + const bool bLink = ( ( nTempAction & DND_ACTION_LINK ) != 0 ); + SdrGrafObj* pGrafObj = InsertGraphic( aGraphic, nTempAction, maDropPos, nullptr, nullptr ); + if(pGrafObj && bLink) + { + pGrafObj->SetGraphicLink( aCurrentDropFile ); + } + + // return action from first inserted graphic + if( aIter == maDropFileVector.begin() ) + mnAction = nTempAction; + + bHandled = true; + } + if (!bHandled) + { + std::shared_ptr<const SfxFilter> pFoundFilter; + SfxMedium aSfxMedium( aCurrentDropFile, StreamMode::READ | StreamMode::SHARE_DENYNONE ); + ErrCode nErr = SfxGetpApp()->GetFilterMatcher().GuessFilter( aSfxMedium, pFoundFilter ); + + if( pFoundFilter && !nErr ) + { + ::std::vector< OUString > aFilterVector; + OUString aFilterName = pFoundFilter->GetFilterName(); + OUString aLowerAsciiFileName = aCurrentDropFile.toAsciiLowerCase(); + + FuInsertFile::GetSupportedFilterVector( aFilterVector ); + + if( ( ::std::find( aFilterVector.begin(), aFilterVector.end(), pFoundFilter->GetMimeType() ) != aFilterVector.end() ) || + aFilterName.indexOf( "Text" ) != -1 || + aFilterName.indexOf( "Rich" ) != -1 || + aFilterName.indexOf( "RTF" ) != -1 || + aFilterName.indexOf( "HTML" ) != -1 || + aLowerAsciiFileName.indexOf(".sdd") != -1 || + aLowerAsciiFileName.indexOf(".sda") != -1 || + aLowerAsciiFileName.indexOf(".sxd") != -1 || + aLowerAsciiFileName.indexOf(".sxi") != -1 || + aLowerAsciiFileName.indexOf(".std") != -1 || + aLowerAsciiFileName.indexOf(".sti") != -1 ) + { + ::sd::Window* pWin = mpViewSh->GetActiveWindow(); + SfxRequest aReq(SID_INSERTFILE, ::SfxCallMode::SLOT, mrDoc.GetItemPool()); + SfxStringItem aItem1( ID_VAL_DUMMY0, aCurrentDropFile ), aItem2( ID_VAL_DUMMY1, pFoundFilter->GetFilterName() ); + + aReq.AppendItem( aItem1 ); + aReq.AppendItem( aItem2 ); + FuInsertFile::Create( mpViewSh, pWin, this, &mrDoc, aReq ); + bHandled = true; + } + } + } + } + +#if HAVE_FEATURE_AVMEDIA + if (!bHandled) + { + bool bShallowDetect = ::avmedia::MediaWindow::isMediaURL(aCurrentDropFile, ""/*TODO?*/); + if (bShallowDetect) + { + mxDropMediaSizeListener.set(new avmedia::PlayerListener( + [this, aCurrentDropFile](const css::uno::Reference<css::media::XPlayer>& rPlayer){ + SolarMutexGuard g; + + css::awt::Size aSize = rPlayer->getPreferredPlayerWindowSize(); + Size aPrefSize(aSize.Width, aSize.Height); + + if (aPrefSize.Width() && aPrefSize.Height()) + { + ::sd::Window* pWin = mpViewSh->GetActiveWindow(); + + if( pWin ) + aPrefSize = pWin->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM)); + else + aPrefSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM)); + } + else + aPrefSize = Size( 5000, 5000 ); + + InsertMediaURL(aCurrentDropFile, mnAction, maDropPos, aPrefSize, true); + + mxDropMediaSizeListener.clear(); + })); + } + bHandled = bShallowDetect && ::avmedia::MediaWindow::isMediaURL(aCurrentDropFile, ""/*TODO?*/, true, mxDropMediaSizeListener); + } +#endif + + if (!bHandled) + { + if( mnAction & DND_ACTION_LINK ) + static_cast< DrawViewShell* >( mpViewSh )->InsertURLButton( aCurrentDropFile, aCurrentDropFile, OUString(), &maDropPos ); + else + { + if( mpViewSh ) + { + try + { + //TODO/MBA: testing + OUString aName; + uno::Sequence < beans::PropertyValue > aMedium{ comphelper::makePropertyValue( + "URL", aCurrentDropFile) }; + + uno::Reference < embed::XEmbeddedObject > xObj = mpDocSh->GetEmbeddedObjectContainer(). + InsertEmbeddedObject( aMedium, aName ); + + uno::Reference < embed::XEmbedPersist > xPersist( xObj, uno::UNO_QUERY ); + if ( xPersist.is()) + { + // TODO/LEAN: VisualArea access can switch the object to running state + sal_Int64 nAspect = embed::Aspects::MSOLE_CONTENT; + + xPersist->storeOwn(); + + awt::Size aSz; + try + { + aSz = xObj->getVisualAreaSize( nAspect ); + } + catch( embed::NoVisualAreaSizeException& ) + { + // the default size will be set later + } + + Size aSize( aSz.Width, aSz.Height ); + ::tools::Rectangle aRect; + + if (!aSize.Width() || !aSize.Height()) + { + aSize.setWidth( 1410 ); + aSize.setHeight( 1000 ); + } + + aRect = ::tools::Rectangle( maDropPos, aSize ); + + SdrOle2Obj* pOleObj = new SdrOle2Obj( + getSdrModelFromSdrView(), + svt::EmbeddedObjectRef(xObj, nAspect), + aName, + aRect); + SdrInsertFlags nOptions = SdrInsertFlags::SETDEFLAYER; + + if (mpViewSh != nullptr) + { + OSL_ASSERT (mpViewSh->GetViewShell()!=nullptr); + SfxInPlaceClient* pIpClient = + mpViewSh->GetViewShell()->GetIPClient(); + if (pIpClient!=nullptr && pIpClient->IsObjectInPlaceActive()) + nOptions |= SdrInsertFlags::DONTMARK; + } + + if (InsertObjectAtView( pOleObj, *GetSdrPageView(), nOptions )) + pOleObj->SetLogicRect( aRect ); + aSz.Width = aRect.GetWidth(); + aSz.Height = aRect.GetHeight(); + xObj->setVisualAreaSize( nAspect,aSz ); + } + } + catch( uno::Exception& ) + { + nError = ERRCODE_IO_GENERAL; + // TODO/LATER: better error handling + } + } + } + } + + ++aIter; + } + + if( nError ) + ErrorHandler::HandleError( nError ); +} + +/** + * Timer handler for Errorhandling at Drop() + */ +IMPL_LINK_NOARG(View, DropErrorHdl, Timer *, void) +{ + vcl::Window* pWin = mpViewSh ? mpViewSh->GetActiveWindow() : nullptr; + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(pWin ? pWin->GetFrameWeld() : nullptr, + VclMessageType::Info, VclButtonsType::Ok, + SdResId(STR_ACTION_NOTPOSSIBLE))); + xInfoBox->run(); +} + +/** + * @returns StyleSheet from selection + */ +SfxStyleSheet* View::GetStyleSheet() const +{ + return SdrView::GetStyleSheet(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdview5.cxx b/sd/source/ui/view/sdview5.cxx new file mode 100644 index 000000000..c3ac066bc --- /dev/null +++ b/sd/source/ui/view/sdview5.cxx @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sdpage.hxx> +#include <View.hxx> +#include <pres.hxx> + +#include <svx/svdpagv.hxx> + +namespace sd { + +static bool implIsMultiPresObj( PresObjKind eKind ) +{ + switch( eKind ) + { + case PresObjKind::Outline: + case PresObjKind::Graphic: + case PresObjKind::Object: + case PresObjKind::Chart: + case PresObjKind::OrgChart: + case PresObjKind::Table: + case PresObjKind::Media: + return true; + default: + return false; + } +} + +SdPage* View::GetPage() +{ + SdPage* pPage = nullptr; + SdrPageView* pPV = GetSdrPageView(); + if( pPV ) + { + pPage = static_cast< SdPage* >( pPV->GetPage() ); + } + + return pPage; +} + +// returns selected object in case there's just one object in the selection +SdrObject* View::GetSelectedSingleObject(SdPage const * pPage) +{ + SdrObject* pRet = nullptr; + if( pPage ) + { + // first try selected shape + if ( AreObjectsMarked() ) + { + const SdrMarkList& rMarkList = GetMarkedObjectList(); + + if (rMarkList.GetMarkCount() == 1) + { + SdrMark* pMark = rMarkList.GetMark(0); + pRet = pMark->GetMarkedSdrObj(); + } + } + } + + return pRet; +} + +SdrObject* View::GetEmptyPresentationObject( PresObjKind eKind ) +{ + SdPage* pPage = GetPage(); + SdrObject* pEmptyObj = nullptr; + + if ( pPage && !pPage->IsMasterPage() ) { + SdrObject* pObj = GetSelectedSingleObject( pPage ); + + if( pObj && pObj->IsEmptyPresObj() && implIsMultiPresObj( pPage->GetPresObjKind(pObj) ) ) + pEmptyObj = pObj; + + // try to find empty pres obj of same type + if( !pEmptyObj ) + { + int nIndex = 1; + do + { + pEmptyObj = pPage->GetPresObj(eKind, nIndex++ ); + } + while( (pEmptyObj != nullptr) && (!pEmptyObj->IsEmptyPresObj()) ); + } + + // last try to find empty pres obj of multiple type + if( !pEmptyObj ) + { + const std::list< SdrObject* >& rShapes = pPage->GetPresentationShapeList().getList(); + + auto iter = std::find_if(rShapes.begin(), rShapes.end(), + [&pPage](SdrObject* pShape) { return pShape->IsEmptyPresObj() && implIsMultiPresObj(pPage->GetPresObjKind(pShape)); }); + if (iter != rShapes.end()) + pEmptyObj = (*iter); + } + } + + return pEmptyObj; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/sdwindow.cxx b/sd/source/ui/view/sdwindow.cxx new file mode 100644 index 000000000..f639b463e --- /dev/null +++ b/sd/source/ui/view/sdwindow.cxx @@ -0,0 +1,1097 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <Window.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/request.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svx/svxids.hrc> + +#include <editeng/outliner.hxx> +#include <editeng/editview.hxx> +#include <editeng/editeng.hxx> + +#include <app.hrc> +#include <ViewShell.hxx> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> +#include <PresentationViewShell.hxx> +#include <View.hxx> +#include <FrameView.hxx> +#include <OutlineViewShell.hxx> +#include <OutlineView.hxx> +#include <drawdoc.hxx> +#include <WindowUpdater.hxx> +#include <ViewShellBase.hxx> +#include <uiobject.hxx> + +#include <officecfg/Office/Common.hxx> +#include <sal/log.hxx> +#include <tools/debug.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/settings.hxx> +#include <comphelper/lok.hxx> +#include <sfx2/lokhelper.hxx> + +namespace sd { + +#define SCROLL_LINE_FACT 0.05 ///< factor for line scrolling +#define SCROLL_PAGE_FACT 0.5 ///< factor for page scrolling +#define SCROLL_SENSITIVE 20 ///< sensitive area in pixel +#define ZOOM_MULTIPLICATOR 10000 ///< multiplier to avoid rounding errors +#define MIN_ZOOM 5 ///< minimal zoom factor +#define MAX_ZOOM 3000 ///< maximal zoom factor + +Window::Window(vcl::Window* pParent) + : vcl::Window(pParent, WinBits(WB_CLIPCHILDREN | WB_DIALOGCONTROL)), + DropTargetHelper( this ), + maWinPos(0, 0), // precautionary; but the values should be set + maViewOrigin(0, 0), // again from the owner of the window + maViewSize(1000, 1000), + maPrevSize(-1,-1), + mnMinZoom(MIN_ZOOM), + mnMaxZoom(MAX_ZOOM), + mbMinZoomAutoCalc(false), + mbCenterAllowed(true), + mnTicks (0), + mpViewShell(nullptr), + mbUseDropScroll (true) +{ + SetDialogControlFlags( DialogControlFlags::Return | DialogControlFlags::WantFocus ); + + MapMode aMap(GetMapMode()); + aMap.SetMapUnit(MapUnit::Map100thMM); + SetMapMode(aMap); + + // with it, the vcl::WindowColor is used in the slide mode + SetBackground( Wallpaper( GetSettings().GetStyleSettings().GetWindowColor() ) ); + + // adjust contrast mode initially + bool bUseContrast = GetSettings().GetStyleSettings().GetHighContrastMode(); + GetOutDev()->SetDrawMode( bUseContrast + ? sd::OUTPUT_DRAWMODE_CONTRAST + : sd::OUTPUT_DRAWMODE_COLOR ); + + // #i78183# Added after discussed with AF + EnableRTL(false); +} + +Window::~Window() +{ + disposeOnce(); +} + +void Window::dispose() +{ + if (mpViewShell != nullptr) + { + WindowUpdater* pWindowUpdater = mpViewShell->GetWindowUpdater(); + if (pWindowUpdater != nullptr) + pWindowUpdater->UnregisterWindow (this); + } + DropTargetHelper::dispose(); + vcl::Window::dispose(); +} + +void Window::SetViewShell (ViewShell* pViewSh) +{ + WindowUpdater* pWindowUpdater = nullptr; + // Unregister at device updater of old view shell. + if (mpViewShell != nullptr) + { + pWindowUpdater = mpViewShell->GetWindowUpdater(); + if (pWindowUpdater != nullptr) + pWindowUpdater->UnregisterWindow (this); + } + + mpViewShell = pViewSh; + + // Register at device updater of new view shell + if (mpViewShell != nullptr) + { + pWindowUpdater = mpViewShell->GetWindowUpdater(); + if (pWindowUpdater != nullptr) + pWindowUpdater->RegisterWindow (this); + } +} + +ViewShell* Window::GetViewShell() +{ + return mpViewShell; +} + +void Window::CalcMinZoom() +{ + // Are we entitled to change the minimal zoom factor? + if ( !mbMinZoomAutoCalc ) + return; + + // Get current zoom factor. + ::tools::Long nZoom = GetZoom(); + + // Get the rectangle of the output area in logical coordinates + // and calculate the scaling factors that would lead to the view + // area (also called application area) to completely fill the + // window. + Size aWinSize = PixelToLogic(GetOutputSizePixel()); + sal_uLong nX = static_cast<sal_uLong>(static_cast<double>(aWinSize.Width()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(maViewSize.Width())); + sal_uLong nY = static_cast<sal_uLong>(static_cast<double>(aWinSize.Height()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(maViewSize.Height())); + + // Decide whether to take the larger or the smaller factor. + sal_uLong nFact = std::min(nX, nY); + + // The factor is transformed according to the current zoom factor. + nFact = nFact * nZoom / ZOOM_MULTIPLICATOR; + mnMinZoom = std::max(sal_uInt16(MIN_ZOOM), static_cast<sal_uInt16>(nFact)); + + // If the current zoom factor is smaller than the calculated minimal + // zoom factor then set the new minimal factor as the current zoom + // factor. + if ( nZoom < static_cast<::tools::Long>(mnMinZoom) ) + SetZoomFactor(mnMinZoom); +} + +void Window::SetMinZoom (::tools::Long nMin) +{ + mnMinZoom = static_cast<sal_uInt16>(nMin); +} + +void Window::SetMaxZoom (::tools::Long nMax) +{ + mnMaxZoom = static_cast<sal_uInt16>(nMax); +} + +::tools::Long Window::GetZoom() const +{ + if( GetMapMode().GetScaleX().GetDenominator() ) + { + return ::tools::Long(GetMapMode().GetScaleX() * 100); + } + else + { + return 0; + } +} + +void Window::Resize() +{ + vcl::Window::Resize(); + CalcMinZoom(); + + if( mpViewShell && mpViewShell->GetViewFrame() ) + mpViewShell->GetViewFrame()->GetBindings().Invalidate( SID_ATTR_ZOOMSLIDER ); +} + +void Window::PrePaint(vcl::RenderContext& /*rRenderContext*/) +{ + if ( mpViewShell ) + mpViewShell->PrePaint(); +} + +void Window::Paint(vcl::RenderContext& /*rRenderContext*/, const ::tools::Rectangle& rRect) +{ + if ( mpViewShell ) + mpViewShell->Paint(rRect, this); +} + +void Window::KeyInput(const KeyEvent& rKEvt) +{ + if (getenv("SD_DEBUG") && rKEvt.GetKeyCode().GetCode() == KEY_F12 && mpViewShell) + { + mpViewShell->GetDoc()->dumpAsXml(nullptr); + if (OutlinerView *pOLV = mpViewShell->GetView()->GetTextEditOutlinerView()) + pOLV->GetEditView().GetEditEngine()->dumpAsXmlEditDoc(nullptr); + return; + } + + if (!(mpViewShell && mpViewShell->KeyInput(rKEvt, this))) + { + if (mpViewShell && rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE) + { + mpViewShell->GetViewShell()->Escape(); + } + else + { + vcl::Window::KeyInput(rKEvt); + } + } +} + +void Window::MouseButtonDown(const MouseEvent& rMEvt) +{ + if ( mpViewShell ) + mpViewShell->MouseButtonDown(rMEvt, this); +} + +void Window::MouseMove(const MouseEvent& rMEvt) +{ + if ( mpViewShell ) + mpViewShell->MouseMove(rMEvt, this); +} + +void Window::MouseButtonUp(const MouseEvent& rMEvt) +{ + mnTicks = 0; + + if ( mpViewShell ) + mpViewShell->MouseButtonUp(rMEvt, this); +} + +void Window::Command(const CommandEvent& rCEvt) +{ + if (mpViewShell) + mpViewShell->Command(rCEvt, this); + //pass at least alt press/release to parent impl + if (rCEvt.GetCommand() == CommandEventId::ModKeyChange) + vcl::Window::Command(rCEvt); + //show the text edit outliner view cursor + else if (mpViewShell && !HasFocus() && rCEvt.GetCommand() == CommandEventId::CursorPos) + { + // tdf#138855 Getting Focus may destroy TextEditOutlinerView so Grab if + // text editing active, but fetch the TextEditOutlinerView post-grab + if (mpViewShell->GetView()->IsTextEdit()) + { + GrabFocus(); + OutlinerView* pOLV = mpViewShell->GetView()->GetTextEditOutlinerView(); + if (pOLV && this == pOLV->GetWindow()) + pOLV->ShowCursor(); + } + } +} + +bool Window::EventNotify( NotifyEvent& rNEvt ) +{ + bool bResult = false; + if ( mpViewShell ) + { + bResult = mpViewShell->Notify(rNEvt, this); + } + if( !bResult ) + bResult = vcl::Window::EventNotify(rNEvt); + + return bResult; +} + +void Window::RequestHelp(const HelpEvent& rEvt) +{ + if (!mpViewShell || !mpViewShell->RequestHelp(rEvt)) + vcl::Window::RequestHelp( rEvt ); +} + +/** + * Set the position of the upper left corner from the visible area of the + * window. + */ +void Window::SetWinViewPos(const Point& rPnt) +{ + maWinPos = rPnt; +} + +/** + * Set origin of the representation in respect to the whole working area. + */ +void Window::SetViewOrigin(const Point& rPnt) +{ + maViewOrigin = rPnt; +} + +/** + * Set size of the whole working area which can be seen with the window. + */ +void Window::SetViewSize(const Size& rSize) +{ + maViewSize = rSize; + CalcMinZoom(); +} + +void Window::SetCenterAllowed (bool bIsAllowed) +{ + mbCenterAllowed = bIsAllowed; +} + +::tools::Long Window::SetZoomFactor(::tools::Long nZoom) +{ + // Clip the zoom factor to the valid range marked by nMinZoom as + // calculated by CalcMinZoom() and the constant MAX_ZOOM. + if ( nZoom > MAX_ZOOM ) + nZoom = MAX_ZOOM; + if ( nZoom < static_cast<::tools::Long>(mnMinZoom) ) + nZoom = mnMinZoom; + + // Set the zoom factor at the window's map mode. + if (!comphelper::LibreOfficeKit::isActive()) + { + MapMode aMap(GetMapMode()); + aMap.SetScaleX(Fraction(nZoom, 100)); + aMap.SetScaleY(Fraction(nZoom, 100)); + SetMapMode(aMap); + } + + // invalidate previous size - it was relative to the old scaling + maPrevSize = Size(-1,-1); + + // Update the map mode's origin (to what effect?). + UpdateMapOrigin(); + + // Update the view's snapping to the new zoom factor. + if ( auto pDrawViewShell = dynamic_cast< DrawViewShell *>( mpViewShell ) ) + pDrawViewShell->GetView()->RecalcLogicSnapMagnetic(*GetOutDev()); + + // Return the zoom factor just in case it has been changed above to lie + // inside the valid range. + return nZoom; +} + +void Window::SetZoomIntegral(::tools::Long nZoom) +{ + // Clip the zoom factor to the valid range marked by nMinZoom as + // previously calculated by <member>CalcMinZoom()</member> and the + // MAX_ZOOM constant. + if ( nZoom > MAX_ZOOM ) + nZoom = MAX_ZOOM; + if ( nZoom < static_cast<::tools::Long>(mnMinZoom) ) + nZoom = mnMinZoom; + + // Calculate the window's new origin. + Size aSize = PixelToLogic(GetOutputSizePixel()); + ::tools::Long nW = aSize.Width() * GetZoom() / nZoom; + ::tools::Long nH = aSize.Height() * GetZoom() / nZoom; + maWinPos.AdjustX((aSize.Width() - nW) / 2 ); + maWinPos.AdjustY((aSize.Height() - nH) / 2 ); + if ( maWinPos.X() < 0 ) maWinPos.setX( 0 ); + if ( maWinPos.Y() < 0 ) maWinPos.setY( 0 ); + + // Finally update this window's map mode to the given zoom factor that + // has been clipped to the valid range. + SetZoomFactor(nZoom); +} + +::tools::Long Window::GetZoomForRect( const ::tools::Rectangle& rZoomRect ) +{ + ::tools::Long nRetZoom = 100; + + if( (rZoomRect.GetWidth() != 0) && (rZoomRect.GetHeight() != 0)) + { + // Calculate the scale factors which will lead to the given + // rectangle being fully visible (when translated accordingly) as + // large as possible in the output area independently in both + // coordinate directions . + sal_uLong nX(0); + sal_uLong nY(0); + + const Size aWinSize( PixelToLogic(GetOutputSizePixel()) ); + if(rZoomRect.GetHeight()) + { + nX = static_cast<sal_uLong>(static_cast<double>(aWinSize.Height()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(rZoomRect.GetHeight())); + } + + if(rZoomRect.GetWidth()) + { + nY = static_cast<sal_uLong>(static_cast<double>(aWinSize.Width()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(rZoomRect.GetWidth())); + } + + // Use the smaller one of both so that the zoom rectangle will be + // fully visible with respect to both coordinate directions. + sal_uLong nFact = std::min(nX, nY); + + // Transform the current zoom factor so that it leads to the desired + // scaling. + nRetZoom = nFact * GetZoom() / ZOOM_MULTIPLICATOR; + + // Calculate the new origin. + if ( nFact == 0 ) + { + // Don't change anything if the scale factor is degenerate. + nRetZoom = GetZoom(); + } + else + { + // Clip the zoom factor to the valid range marked by nMinZoom as + // previously calculated by <member>CalcMinZoom()</member> and the + // MAX_ZOOM constant. + if ( nRetZoom > MAX_ZOOM ) + nRetZoom = MAX_ZOOM; + if ( nRetZoom < static_cast<::tools::Long>(mnMinZoom) ) + nRetZoom = mnMinZoom; + } + } + + return nRetZoom; +} + +/** Recalculate the zoom factor and translation so that the given rectangle + is displayed centered and as large as possible while still being fully + visible in the window. +*/ +::tools::Long Window::SetZoomRect (const ::tools::Rectangle& rZoomRect) +{ + ::tools::Long nNewZoom = 100; + + if (rZoomRect.GetWidth() == 0 || rZoomRect.GetHeight() == 0) + { + // The given rectangle is degenerate. Use the default zoom factor + // (above) of 100%. + SetZoomIntegral(nNewZoom); + } + else + { + Point aPos = rZoomRect.TopLeft(); + // Transform the output area from pixel coordinates into logical + // coordinates. + Size aWinSize = PixelToLogic(GetOutputSizePixel()); + // Paranoia! The degenerate case of zero width or height has been + // taken care of above. + DBG_ASSERT(rZoomRect.GetWidth(), "ZoomRect-Width = 0!"); + DBG_ASSERT(rZoomRect.GetHeight(), "ZoomRect-Height = 0!"); + + // Calculate the scale factors which will lead to the given + // rectangle being fully visible (when translated accordingly) as + // large as possible in the output area independently in both + // coordinate directions . + sal_uLong nX(0); + sal_uLong nY(0); + + if(rZoomRect.GetHeight()) + { + nX = static_cast<sal_uLong>(static_cast<double>(aWinSize.Height()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(rZoomRect.GetHeight())); + } + + if(rZoomRect.GetWidth()) + { + nY = static_cast<sal_uLong>(static_cast<double>(aWinSize.Width()) + * double(ZOOM_MULTIPLICATOR) / static_cast<double>(rZoomRect.GetWidth())); + } + + // Use the smaller one of both so that the zoom rectangle will be + // fully visible with respect to both coordinate directions. + sal_uLong nFact = std::min(nX, nY); + + // Transform the current zoom factor so that it leads to the desired + // scaling. + ::tools::Long nZoom = nFact * GetZoom() / ZOOM_MULTIPLICATOR; + + // Calculate the new origin. + if ( nFact == 0 ) + { + // Don't change anything if the scale factor is degenerate. + nNewZoom = GetZoom(); + } + else + { + // Calculate the new window position that centers the given + // rectangle on the screen. + if ( nZoom > MAX_ZOOM ) + nFact = nFact * MAX_ZOOM / nZoom; + + maWinPos = maViewOrigin + aPos; + + aWinSize.setWidth( static_cast<::tools::Long>(static_cast<double>(aWinSize.Width()) * double(ZOOM_MULTIPLICATOR) / static_cast<double>(nFact)) ); + maWinPos.AdjustX((rZoomRect.GetWidth() - aWinSize.Width()) / 2 ); + aWinSize.setHeight( static_cast<::tools::Long>(static_cast<double>(aWinSize.Height()) * double(ZOOM_MULTIPLICATOR) / static_cast<double>(nFact)) ); + maWinPos.AdjustY((rZoomRect.GetHeight() - aWinSize.Height()) / 2 ); + + if ( maWinPos.X() < 0 ) maWinPos.setX( 0 ); + if ( maWinPos.Y() < 0 ) maWinPos.setY( 0 ); + + // Adapt the window's map mode to the new zoom factor. + nNewZoom = SetZoomFactor(nZoom); + } + } + + return nNewZoom; +} + +void Window::SetMinZoomAutoCalc (bool bAuto) +{ + mbMinZoomAutoCalc = bAuto; +} + +/** + * Calculate and set new MapMode origin. + * If aWinPos.X()/Y() == -1, then we center the corresponding position (e.g. for + * initialization). + */ +void Window::UpdateMapOrigin(bool bInvalidate) +{ + bool bChanged = false; + const Size aWinSize = PixelToLogic(GetOutputSizePixel()); + + if ( mbCenterAllowed ) + { + if( maPrevSize != Size(-1,-1) ) + { + // keep view centered around current pos, when window + // resizes + maWinPos.AdjustX( -((aWinSize.Width() - maPrevSize.Width()) / 2) ); + maWinPos.AdjustY( -((aWinSize.Height() - maPrevSize.Height()) / 2) ); + bChanged = true; + } + + if ( maWinPos.X() > maViewSize.Width() - aWinSize.Width() ) + { + maWinPos.setX( maViewSize.Width() - aWinSize.Width() ); + bChanged = true; + } + if ( maWinPos.Y() > maViewSize.Height() - aWinSize.Height() ) + { + maWinPos.setY( maViewSize.Height() - aWinSize.Height() ); + bChanged = true; + } + if ( aWinSize.Width() > maViewSize.Width() || maWinPos.X() < 0 ) + { + maWinPos.setX( maViewSize.Width() / 2 - aWinSize.Width() / 2 ); + bChanged = true; + } + if ( aWinSize.Height() > maViewSize.Height() || maWinPos.Y() < 0 ) + { + maWinPos.setY( maViewSize.Height() / 2 - aWinSize.Height() / 2 ); + bChanged = true; + } + } + + UpdateMapMode (); + + maPrevSize = aWinSize; + + // When tiled rendering, the above UpdateMapMode() call doesn't touch the map mode. + if (bChanged && bInvalidate && !comphelper::LibreOfficeKit::isActive()) + Invalidate(); +} + +void Window::UpdateMapMode() +{ + maWinPos -= maViewOrigin; + Size aPix(maWinPos.X(), maWinPos.Y()); + aPix = LogicToPixel(aPix); + // Size has to be a multiple of BRUSH_SIZE due to the correct depiction of + // pattern + // #i2237# + // removed old stuff here which still forced zoom to be + // %BRUSH_SIZE which is outdated now + + if (dynamic_cast< DrawViewShell *>( mpViewShell )) + { + // page should not "stick" to the window border + if (aPix.Width() == 0) + { + // #i2237# + // Since BRUSH_SIZE alignment is outdated now, i use the + // former constant here directly + aPix.AdjustWidth( -8 ); + } + if (aPix.Height() == 0) + { + // #i2237# + // Since BRUSH_SIZE alignment is outdated now, i use the + // former constant here directly + aPix.AdjustHeight( -8 ); + } + } + + aPix = PixelToLogic(aPix); + maWinPos.setX( aPix.Width() ); + maWinPos.setY( aPix.Height() ); + Point aNewOrigin (-maWinPos.X(), -maWinPos.Y()); + maWinPos += maViewOrigin; + + if (!comphelper::LibreOfficeKit::isActive()) + { + MapMode aMap(GetMapMode()); + aMap.SetOrigin(aNewOrigin); + SetMapMode(aMap); + } +} + +/** + * @returns X position of the visible area as fraction (< 1) of the whole + * working area. + */ +double Window::GetVisibleX() const +{ + return maViewSize.Width() == 0 ? 0 : (static_cast<double>(maWinPos.X()) / maViewSize.Width()); +} + +/** + * @returns Y position of the visible area as fraction (< 1) of the whole + * working area. + */ +double Window::GetVisibleY() const +{ + return maViewSize.Height() == 0 ? 0 : (static_cast<double>(maWinPos.Y()) / maViewSize.Height()); +} + +/** + * Set x and y position of the visible area as fraction (< 1) of the whole + * working area. Negative values are ignored. + */ +void Window::SetVisibleXY(double fX, double fY) +{ + ::tools::Long nOldX = maWinPos.X(); + ::tools::Long nOldY = maWinPos.Y(); + + if ( fX >= 0 ) + maWinPos.setX( static_cast<::tools::Long>(fX * maViewSize.Width()) ); + if ( fY >= 0 ) + maWinPos.setY( static_cast<::tools::Long>(fY * maViewSize.Height()) ); + UpdateMapOrigin(false); + Scroll(nOldX - maWinPos.X(), nOldY - maWinPos.Y(), ScrollFlags::Children); + PaintImmediately(); +} + +/** + * @returns width of the visible area in proportion to the width of the whole + * working area. + */ +double Window::GetVisibleWidth() const +{ + Size aWinSize = PixelToLogic(GetOutputSizePixel()); + if ( aWinSize.Width() > maViewSize.Width() ) + aWinSize.setWidth( maViewSize.Width() ); + return + maViewSize.Width() == 0 ? 0 : (static_cast<double>(aWinSize.Width()) / maViewSize.Width()); +} + +/** + * @returns height of the visible area in proportion to the height of the whole + * working area. + */ +double Window::GetVisibleHeight() const +{ + Size aWinSize = PixelToLogic(GetOutputSizePixel()); + if ( aWinSize.Height() > maViewSize.Height() ) + aWinSize.setHeight( maViewSize.Height() ); + return maViewSize.Height() == 0 + ? 0 : (static_cast<double>(aWinSize.Height()) / maViewSize.Height()); +} + +Point Window::GetVisibleCenter() +{ + Point aPos = ::tools::Rectangle(Point(), GetOutputSizePixel()).Center(); + + // For LOK + bool bMapModeWasEnabled(IsMapModeEnabled()); + EnableMapMode(/*true*/); + aPos = PixelToLogic(aPos); + EnableMapMode(bMapModeWasEnabled); + + return aPos; +} + +/** + * @returns width of a scroll column in proportion to the width of the whole + * working area. + */ +double Window::GetScrlLineWidth() const +{ + return (GetVisibleWidth() * SCROLL_LINE_FACT); +} + +/** + * @returns height of a scroll column in proportion to the height of the whole + * working area. + */ +double Window::GetScrlLineHeight() const +{ + return (GetVisibleHeight() * SCROLL_LINE_FACT); +} + +/** + * @returns width of a scroll page in proportion to the width of the whole + * working area. + */ +double Window::GetScrlPageWidth() const +{ + return (GetVisibleWidth() * SCROLL_PAGE_FACT); +} + +/** + * @returns height of a scroll page in proportion to the height of the whole + * working area. + */ +double Window::GetScrlPageHeight() const +{ + return (GetVisibleHeight() * SCROLL_PAGE_FACT); +} + +/** + * Deactivate window. + */ +void Window::LoseFocus() +{ + mnTicks = 0; + vcl::Window::LoseFocus (); +} + +/** + * Activate window. + */ +void Window::GrabFocus() +{ + mnTicks = 0; + vcl::Window::GrabFocus (); +} + +void Window::DataChanged( const DataChangedEvent& rDCEvt ) +{ + vcl::Window::DataChanged( rDCEvt ); + + /* Omit PRINTER by all documents which are not using a printer. + Omit FONTS and FONTSUBSTITUTION if no text output is available or if the + document does not allow text. */ + + if ( !((rDCEvt.GetType() == DataChangedEventType::PRINTER) || + (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE))) ) + return; + + if ( (rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE) ) + { + /* Rearrange or initiate Resize for scroll bars since the size of + the scroll bars my have changed. Within this, inside the resize- + handler, the size of the scroll bars will be asked from the + Settings. */ + Resize(); + + /* Re-set data, which are from system control or from Settings. May + have to re-set more data since the resolution may also has + changed. */ + if( mpViewShell ) + { + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + DrawModeFlags nOutputMode; + sal_uInt16 nPreviewSlot; + + if( rStyleSettings.GetHighContrastMode() ) + nOutputMode = sd::OUTPUT_DRAWMODE_CONTRAST; + else + nOutputMode = sd::OUTPUT_DRAWMODE_COLOR; + + if( rStyleSettings.GetHighContrastMode() + && officecfg::Office::Common::Accessibility::IsForPagePreviews::get() ) + nPreviewSlot = SID_PREVIEW_QUALITY_CONTRAST; + else + nPreviewSlot = SID_PREVIEW_QUALITY_COLOR; + + if( dynamic_cast< DrawViewShell *>( mpViewShell ) != nullptr ) + { + GetOutDev()->SetDrawMode( nOutputMode ); + mpViewShell->GetFrameView()->SetDrawMode( nOutputMode ); + Invalidate(); + } + + // Overwrite window color for OutlineView + if( dynamic_cast< OutlineViewShell *>( mpViewShell ) != nullptr ) + { + svtools::ColorConfig aColorConfig; + const Color aDocColor( aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor ); + SetBackground( Wallpaper( aDocColor ) ); + } + + SfxRequest aReq( nPreviewSlot, SfxCallMode::SLOT, mpViewShell->GetDocSh()->GetDoc()->GetItemPool() ); + mpViewShell->ExecReq( aReq ); + mpViewShell->Invalidate(); + mpViewShell->ArrangeGUIElements(); + + // re-create handles to show new outfit + if(dynamic_cast< DrawViewShell *>( mpViewShell ) != nullptr) + { + mpViewShell->GetView()->AdjustMarkHdl(); + } + } + } + + if ( (rDCEvt.GetType() == DataChangedEventType::DISPLAY) || + ((rDCEvt.GetType() == DataChangedEventType::SETTINGS) && + (rDCEvt.GetFlags() & AllSettingsFlags::STYLE)) ) + { + /* Virtual devices, which also depends on the resolution or the + system control, should be updated. Otherwise, we should update + the virtual devices at least at DataChangedEventType::DISPLAY since some + systems allow to change the resolution and color depth during + runtime. Or the virtual devices have to be updated when the color + palette has changed since a different color matching can be used + when outputting. */ + } + + if ( rDCEvt.GetType() == DataChangedEventType::FONTS ) + { + /* If the document provides font choose boxes, we have to update + them. I don't know how this looks like (also not really me, I + only translated the comment ;). We may can handle it global. We + have to discuss it with PB, but he is ill at the moment. + Before we handle it here, discuss it with PB and me. */ + } + + if ( (rDCEvt.GetType() == DataChangedEventType::FONTS) || + (rDCEvt.GetType() == DataChangedEventType::FONTSUBSTITUTION) ) + { + /* Do reformatting since the fonts of the document may no longer + exist, or exist now, or are replaced with others. */ + if( mpViewShell ) + { + DrawDocShell* pDocSh = mpViewShell->GetDocSh(); + if( pDocSh ) + pDocSh->SetPrinter( pDocSh->GetPrinter( true ) ); + } + } + + if ( rDCEvt.GetType() == DataChangedEventType::PRINTER ) + { + /* I don't know how the handling should look like. Maybe we delete a + printer and look what we have to do. Maybe I have to add + something to the VCL, in case the used printer is deleted. + Otherwise I may recalculate the formatting here if the current + printer is destroyed. */ + if( mpViewShell ) + { + DrawDocShell* pDocSh = mpViewShell->GetDocSh(); + if( pDocSh ) + pDocSh->SetPrinter( pDocSh->GetPrinter( true ) ); + } + } + + // Update everything + Invalidate(); +} + +sal_Int8 Window::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if( mpViewShell && !mpViewShell->GetDocSh()->IsReadOnly() ) + { + nRet = mpViewShell->AcceptDrop( rEvt, *this, this, SDRPAGE_NOTFOUND, SDRLAYER_NOTFOUND ); + + if (mbUseDropScroll && dynamic_cast< OutlineViewShell *>( mpViewShell ) == nullptr) + DropScroll( rEvt.maPosPixel ); + } + + return nRet; +} + +sal_Int8 Window::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if( mpViewShell ) + { + nRet = mpViewShell->ExecuteDrop( rEvt, *this, this, SDRPAGE_NOTFOUND, SDRLAYER_NOTFOUND ); + } + + return nRet; +} + +void Window::SetUseDropScroll (bool bUseDropScroll) +{ + mbUseDropScroll = bUseDropScroll; +} + +void Window::DropScroll(const Point& rMousePos) +{ + short nDx = 0; + short nDy = 0; + + Size aSize = GetOutputSizePixel(); + + if (aSize.Width() > SCROLL_SENSITIVE * 3) + { + if ( rMousePos.X() < SCROLL_SENSITIVE ) + { + nDx = -1; + } + + if ( rMousePos.X() >= aSize.Width() - SCROLL_SENSITIVE ) + { + nDx = 1; + } + } + + if (aSize.Height() > SCROLL_SENSITIVE * 3) + { + if ( rMousePos.Y() < SCROLL_SENSITIVE ) + { + nDy = -1; + } + + if ( rMousePos.Y() >= aSize.Height() - SCROLL_SENSITIVE ) + { + nDy = 1; + } + } + + if ( (nDx || nDy) && (rMousePos.X()!=0 || rMousePos.Y()!=0 ) ) + { + if (mnTicks > 20) + mpViewShell->ScrollLines(nDx, nDy); + else + mnTicks ++; + } +} + +css::uno::Reference<css::accessibility::XAccessible> + Window::CreateAccessible() +{ + // If current viewshell is PresentationViewShell, just return empty because the correct ShowWin will be created later. + if (dynamic_cast< PresentationViewShell *>( mpViewShell )) + { + return vcl::Window::CreateAccessible (); + } + css::uno::Reference< css::accessibility::XAccessible > xAcc = GetAccessible(false); + if (xAcc) + { + return xAcc; + } + if (mpViewShell != nullptr) + { + xAcc = mpViewShell->CreateAccessibleDocumentView (this); + SetAccessible(xAcc); + return xAcc; + } + else + { + SAL_WARN("sd", "::sd::Window::CreateAccessible: no view shell"); + return vcl::Window::CreateAccessible (); + } +} + +OutlinerView* Window::GetOutlinerView() const +{ + OutlinerView *pOLV = nullptr; + sd::View* pView = mpViewShell->GetView(); + if (mpViewShell->GetShellType() == ViewShell::ST_OUTLINE) + { + if (OutlineView* pOView = dynamic_cast<OutlineView*>(pView)) + pOLV = pOView->GetViewByWindow(this); + } + else if (pView->IsTextEdit()) + { + pOLV = pView->GetTextEditOutlinerView(); + } + return pOLV; +} + +OUString Window::GetSurroundingText() const +{ + OutlinerView *pOLV = GetOutlinerView(); + if (pOLV) + return pOLV->GetEditView().GetSurroundingText(); + return OUString(); +} + +Selection Window::GetSurroundingTextSelection() const +{ + OutlinerView *pOLV = GetOutlinerView(); + if (pOLV) + return pOLV->GetEditView().GetSurroundingTextSelection(); + return Selection( 0, 0 ); +} + +bool Window::DeleteSurroundingText(const Selection& rSelection) +{ + OutlinerView *pOLV = GetOutlinerView(); + if (pOLV) + return pOLV->GetEditView().DeleteSurroundingText(rSelection); + return false; +} + +void Window::LogicInvalidate(const ::tools::Rectangle* pRectangle) +{ + DrawViewShell* pDrawViewShell = dynamic_cast<DrawViewShell*>(mpViewShell); + if (!pDrawViewShell || pDrawViewShell->IsInSwitchPage()) + return; + + if (!comphelper::LibreOfficeKit::isActive()) + return; + ::tools::Rectangle aRectangle; + ::tools::Rectangle* pResultRectangle; + if (!pRectangle) + pResultRectangle = nullptr; + else + { + aRectangle = *pRectangle; + if (GetMapMode().GetMapUnit() == MapUnit::Map100thMM) + { + aRectangle = o3tl::convert(aRectangle, o3tl::Length::mm100, o3tl::Length::twip); + } + pResultRectangle = &aRectangle; + } + SfxViewShell& rSfxViewShell = pDrawViewShell->GetViewShellBase(); + SfxLokHelper::notifyInvalidation(&rSfxViewShell, pResultRectangle); +} + +void Window::LogicMouseButtonDown(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + mpViewShell->MouseButtonDown(rMouseEvent, this); + + SetPointerPosPixel(aPoint); +} + +void Window::LogicMouseButtonUp(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + mpViewShell->MouseButtonUp(rMouseEvent, this); + + SetPointerPosPixel(aPoint); +} + +void Window::LogicMouseMove(const MouseEvent& rMouseEvent) +{ + // When we're not doing tiled rendering, then positions must be passed as pixels. + assert(comphelper::LibreOfficeKit::isActive()); + + Point aPoint = GetPointerPosPixel(); + SetLastMousePos(rMouseEvent.GetPosPixel()); + + mpViewShell->MouseMove(rMouseEvent, this); + + SetPointerPosPixel(aPoint); +} + +FactoryFunction Window::GetUITestFactory() const +{ + if (get_id() == "impress_win") + return ImpressWindowUIObject::create; + + return WindowUIObject::create; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/tabcontr.cxx b/sd/source/ui/view/tabcontr.cxx new file mode 100644 index 000000000..b09a254e9 --- /dev/null +++ b/sd/source/ui/view/tabcontr.cxx @@ -0,0 +1,358 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <TabControl.hxx> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/vclevent.hxx> + +#include <app.hrc> + +#include <DrawViewShell.hxx> +#include <helpids.h> +#include <View.hxx> +#include <drawdoc.hxx> +#include <DrawDocShell.hxx> + +namespace sd { + + +TabControl::TabControlTransferable::~TabControlTransferable() +{ +} + +void TabControl::TabControlTransferable::AddSupportedFormats() +{ + AddFormat( SotClipboardFormatId::STARDRAW_TABBAR ); +} + +bool TabControl::TabControlTransferable::GetData( const css::datatransfer::DataFlavor& /*rFlavor*/, const OUString& /*rDestDoc*/ ) +{ + return false; +} + +void TabControl::TabControlTransferable::DragFinished( sal_Int8 /*nDropAction*/ ) +{ + mrParent.DragFinished(); +} + +TabControl::TabControl(DrawViewShell* pViewSh, vcl::Window* pParent) : + TabBar( pParent, WinBits( WB_BORDER | WB_3DLOOK | WB_SCROLL | WB_SIZEABLE | WB_DRAG) ), + DragSourceHelper( this ), + DropTargetHelper( this ), + pDrViewSh(pViewSh), + bInternalMove(false) +{ + EnableEditMode(); + SetSizePixel(Size(0, 0)); + SetMaxPageWidth( 150 ); + SetHelpId( HID_SD_TABBAR_PAGES ); +} + +TabControl::~TabControl() +{ + disposeOnce(); +} + +void TabControl::dispose() +{ + DragSourceHelper::dispose(); + DropTargetHelper::dispose(); + TabBar::dispose(); +} + +void TabControl::Select() +{ + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | + SfxCallMode::RECORD); +} + +void TabControl::MouseButtonDown(const MouseEvent& rMEvt) +{ + if (rMEvt.IsLeft() + && !rMEvt.IsMod1() + && !rMEvt.IsMod2() + && !rMEvt.IsShift()) + { + Point aPos = PixelToLogic( rMEvt.GetPosPixel() ); + sal_uInt16 aPageId = GetPageId(aPos); + + //initialize + if (aPageId == 0) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + + pDispatcher->Execute(SID_INSERTPAGE_QUICK, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD); + } + } + + // A single left click with pressed control key on a tab page first + // switches to that page before the usual handling (copying with drag + // and drop) takes place. + else if (rMEvt.IsLeft() && rMEvt.IsMod1() && !rMEvt.IsMod2() && !rMEvt.IsShift()) + { + pDrViewSh->SwitchPage (GetPageId (rMEvt.GetPosPixel()) - 1); + } + + // When only the right button is pressed then first process a + // synthesized left button click to make the page the current one + // whose tab has been clicked. When then the actual right button + // click is processed the resulting context menu relates to the + // now current page. + if (rMEvt.IsRight() && ! rMEvt.IsLeft()) + { + MouseEvent aSyntheticEvent ( + rMEvt.GetPosPixel(), + rMEvt.GetClicks(), + rMEvt.GetMode(), + MOUSE_LEFT, + rMEvt.GetModifier()); + TabBar::MouseButtonDown(aSyntheticEvent); + } + + TabBar::MouseButtonDown(rMEvt); +} + +void TabControl::DoubleClick() +{ + if (GetCurPageId() != 0) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute( SID_MODIFYPAGE, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD ); + } +} + +void TabControl::StartDrag( sal_Int8, const Point& ) +{ + bInternalMove = true; + + // object is delete by reference mechanism + ( new TabControl::TabControlTransferable( *this ) )->StartDrag( this, DND_ACTION_COPYMOVE ); +} + +void TabControl::DragFinished() +{ + bInternalMove = false; +} + +sal_Int8 TabControl::AcceptDrop( const AcceptDropEvent& rEvt ) +{ + sal_Int8 nRet = DND_ACTION_NONE; + + if( rEvt.mbLeaving ) + EndSwitchPage(); + + if( !pDrViewSh->GetDocSh()->IsReadOnly() ) + { + SdDrawDocument* pDoc = pDrViewSh->GetDoc(); + Point aPos( rEvt.maPosPixel ); + + if( bInternalMove ) + { + if( rEvt.mbLeaving || ( pDrViewSh->GetEditMode() == EditMode::MasterPage ) ) + HideDropPos(); + else + { + ShowDropPos( aPos ); + nRet = rEvt.mnAction; + } + } + else + { + HideDropPos(); + + sal_Int32 nPageId = GetPageId( aPos ) - 1; + + if( ( nPageId >= 0 ) && pDoc->GetPage( static_cast<sal_uInt16>(nPageId) ) ) + { + nRet = pDrViewSh->AcceptDrop( rEvt, *this, nullptr, static_cast<sal_uInt16>(nPageId), SDRLAYER_NOTFOUND ); + SwitchPage( aPos ); + } + } + } + + return nRet; +} + +sal_Int8 TabControl::ExecuteDrop( const ExecuteDropEvent& rEvt ) +{ + SdDrawDocument* pDoc = pDrViewSh->GetDoc(); + Point aPos( rEvt.maPosPixel ); + sal_Int8 nRet = DND_ACTION_NONE; + + if( bInternalMove ) + { + sal_uInt16 nPageId = ShowDropPos( aPos ) - 1; + + switch (rEvt.mnAction) + { + case DND_ACTION_MOVE: + if( pDrViewSh->IsSwitchPageAllowed() && pDoc->MovePages( nPageId ) ) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + break; + + case DND_ACTION_COPY: + { + // Copying the selected page to the place that rEvt points + // takes place in three steps: + // 1. Create a copy of the selected page. This copy will + // lie directly behind the selected page. + // 2. Move the copy to the desired place. + // 3. Select the copy. + if (pDrViewSh->IsSwitchPageAllowed()) + { + // 1. Create a copy. + sal_uInt16 nPageNumOfCopy = pDoc->DuplicatePage (GetCurPageId() - 1); + // 2. Move page. For this first switch to the copy: + // MovePages operates on the currently selected page(s). + pDrViewSh->SwitchPage (nPageNumOfCopy); + // Adapt target page id when necessary, i.e. page copy + // has been inserted in front of the target page. + sal_uInt16 nPageNum = nPageId; + if ((nPageNumOfCopy <= nPageNum) && (nPageNum != sal_uInt16(-1))) + nPageNum += 1; + if (pDoc->MovePages(nPageNum)) + { + // 3. Switch to the copy that has been moved to its + // final destination. Use an asynchron slot call to + // be executed after the still pending ones. + if (nPageNumOfCopy >= nPageNum || (nPageNum == sal_uInt16(-1))) + nPageNum += 1; + SetCurPageId (GetPageId(nPageNum)); + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + } + + break; + } + } + + nRet = rEvt.mnAction; + } + else + { + sal_Int32 nPageId = GetPageId( aPos ) - 1; + + if( ( nPageId >= 0 ) && pDoc->GetPage( static_cast<sal_uInt16>(nPageId) ) ) + { + nRet = pDrViewSh->ExecuteDrop( rEvt, *this, nullptr, static_cast<sal_uInt16>(nPageId), SDRLAYER_NOTFOUND ); + } + } + + HideDropPos(); + EndSwitchPage(); + + return nRet; +} + +void TabControl::Command(const CommandEvent& rCEvt) +{ + if ( rCEvt.GetCommand() == CommandEventId::ContextMenu ) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->ExecutePopup("pagetab"); + } +} + +bool TabControl::StartRenaming() +{ + bool bOK = false; + + if (pDrViewSh->GetPageKind() == PageKind::Standard) + { + bOK = true; + + ::sd::View* pView = pDrViewSh->GetView(); + + if ( pView->IsTextEdit() ) + pView->SdrEndTextEdit(); + } + + return bOK; +} + +TabBarAllowRenamingReturnCode TabControl::AllowRenaming() +{ + bool bOK = true; + + OUString aNewName( GetEditText() ); + OUString aCompareName( GetPageText( GetEditPageId() ) ); + + if( aCompareName != aNewName ) + { + // rename page + if (pDrViewSh->GetDocSh()->CheckPageName(GetFrameWeld(), aNewName)) + { + SetEditText( aNewName ); + EndRenaming(); + } + else + { + bOK = false; + } + } + return bOK ? TABBAR_RENAMING_YES : TABBAR_RENAMING_NO; +} + +void TabControl::EndRenaming() +{ + if( !IsEditModeCanceled() ) + pDrViewSh->RenameSlide( GetEditPageId(), GetEditText() ); +} + +void TabControl::ActivatePage() +{ + if ( /*IsInSwitching && */ pDrViewSh->IsSwitchPageAllowed() ) + { + SfxDispatcher* pDispatcher = pDrViewSh->GetViewFrame()->GetDispatcher(); + pDispatcher->Execute(SID_SWITCHPAGE, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } +} + +bool TabControl::DeactivatePage() +{ + return pDrViewSh->IsSwitchPageAllowed(); +} + +void TabControl::SendActivatePageEvent() +{ + CallEventListeners (VclEventId::TabbarPageActivated, + reinterpret_cast<void*>(GetCurPageId())); +} + +void TabControl::SendDeactivatePageEvent() +{ + CallEventListeners (VclEventId::TabbarPageDeactivated, + reinterpret_cast<void*>(GetCurPageId())); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/unmodpg.cxx b/sd/source/ui/view/unmodpg.cxx new file mode 100644 index 000000000..03d907d14 --- /dev/null +++ b/sd/source/ui/view/unmodpg.cxx @@ -0,0 +1,210 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <svx/svdlayer.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/viewfrm.hxx> +#include <svx/svdviter.hxx> +#include <svx/svdview.hxx> +#include <tools/debug.hxx> + +#include <strings.hrc> +#include <strings.hxx> +#include <glob.hxx> +#include <app.hrc> + +#include <unmodpg.hxx> +#include <sdpage.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> +#include <drawdoc.hxx> + + +ModifyPageUndoAction::ModifyPageUndoAction( + SdDrawDocument* pTheDoc, + SdPage* pThePage, + const OUString& aTheNewName, + AutoLayout eTheNewAutoLayout, + bool bTheNewBckgrndVisible, + bool bTheNewBckgrndObjsVisible) +: SdUndoAction(pTheDoc) +{ + DBG_ASSERT(pThePage, "Undo without a page???"); + + mpPage = pThePage; + maNewName = aTheNewName; + meNewAutoLayout = eTheNewAutoLayout; + mbNewBckgrndVisible = bTheNewBckgrndVisible; + mbNewBckgrndObjsVisible = bTheNewBckgrndObjsVisible; + + meOldAutoLayout = mpPage->GetAutoLayout(); + + if (!mpPage->IsMasterPage()) + { + maOldName = mpPage->GetName(); + SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers = mpPage->TRG_GetMasterPageVisibleLayers(); + + mbOldBckgrndVisible = aVisibleLayers.IsSet(aBckgrnd); + mbOldBckgrndObjsVisible = aVisibleLayers.IsSet(aBckgrndObj); + } + else + { + mbOldBckgrndVisible = false; + mbOldBckgrndObjsVisible = false; + } + + if (pTheDoc && pTheDoc->GetDocumentType() == DocumentType::Draw) + SetComment( SdResId(STR_UNDO_MODIFY_PAGE_DRAW) ); + else + SetComment( SdResId(STR_UNDO_MODIFY_PAGE) ); +} + +void ModifyPageUndoAction::Undo() +{ + // invalidate Selection, there could be objects deleted in this UNDO + // which are no longer allowed to be selected then. + SdrViewIter aIter(mpPage); + SdrView* pView = aIter.FirstView(); + + while(pView) + { + if(pView->AreObjectsMarked()) + pView->UnmarkAll(); + pView = aIter.NextView(); + } + + mpPage->SetAutoLayout( meOldAutoLayout ); + + if (!mpPage->IsMasterPage()) + { + if (mpPage->GetName() != maOldName) + { + mpPage->SetName(maOldName); + + if (mpPage->GetPageKind() == PageKind::Standard) + { + SdPage* pNotesPage = static_cast<SdPage*>(mpDoc->GetPage(mpPage->GetPageNum() + 1)); + pNotesPage->SetName(maOldName); + } + } + + SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers; + aVisibleLayers.Set(aBckgrnd, mbOldBckgrndVisible); + aVisibleLayers.Set(aBckgrndObj, mbOldBckgrndObjsVisible); + mpPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + + // Redisplay + SfxViewFrame* pCurrent = SfxViewFrame::Current(); + if( pCurrent ) + { + pCurrent->GetDispatcher()->Execute( + SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } +} + +void ModifyPageUndoAction::Redo() +{ + // invalidate Selection, there could be objects deleted in this UNDO + // which are no longer allowed to be selected then. + SdrViewIter aIter(mpPage); + SdrView* pView = aIter.FirstView(); + + while(pView) + { + if(pView->AreObjectsMarked()) + pView->UnmarkAll(); + pView = aIter.NextView(); + } + + mpPage->meAutoLayout = meNewAutoLayout; + + if (!mpPage->IsMasterPage()) + { + if (mpPage->GetName() != maNewName) + { + mpPage->SetName(maNewName); + + if (mpPage->GetPageKind() == PageKind::Standard) + { + SdPage* pNotesPage = static_cast<SdPage*>(mpDoc->GetPage(mpPage->GetPageNum() + 1)); + pNotesPage->SetName(maNewName); + } + } + + SdrLayerAdmin& rLayerAdmin = mpDoc->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers; + aVisibleLayers.Set(aBckgrnd, mbNewBckgrndVisible); + aVisibleLayers.Set(aBckgrndObj, mbNewBckgrndObjsVisible); + mpPage->TRG_SetMasterPageVisibleLayers(aVisibleLayers); + } + + // Redisplay + SfxViewFrame* pCurrent = SfxViewFrame::Current(); + if( pCurrent ) + { + pCurrent->GetDispatcher()->Execute( + SID_SWITCHPAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD ); + } +} + +ModifyPageUndoAction::~ModifyPageUndoAction() +{ +} + +RenameLayoutTemplateUndoAction::RenameLayoutTemplateUndoAction( + SdDrawDocument* pDocument, + const OUString& rOldLayoutName, + const OUString& rNewLayoutName) + : SdUndoAction(pDocument) + , maOldName(rOldLayoutName) + , maNewName(rNewLayoutName) + , maComment(SdResId(STR_TITLE_RENAMESLIDE)) +{ + sal_Int32 nPos = maOldName.indexOf(SD_LT_SEPARATOR); + if (nPos != -1) + maOldName = maOldName.copy(0, nPos); +} + +void RenameLayoutTemplateUndoAction::Undo() +{ + OUString aLayoutName(maNewName + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE); + mpDoc->RenameLayoutTemplate( aLayoutName, maOldName ); +} + +void RenameLayoutTemplateUndoAction::Redo() +{ + OUString aLayoutName(maOldName + SD_LT_SEPARATOR + STR_LAYOUT_OUTLINE); + mpDoc->RenameLayoutTemplate( aLayoutName, maNewName ); +} + +OUString RenameLayoutTemplateUndoAction::GetComment() const +{ + return maComment; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/viewoverlaymanager.cxx b/sd/source/ui/view/viewoverlaymanager.cxx new file mode 100644 index 000000000..3cdfb9787 --- /dev/null +++ b/sd/source/ui/view/viewoverlaymanager.cxx @@ -0,0 +1,546 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <sal/config.h> + +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> + +#include <vcl/help.hxx> +#include <vcl/lazydelete.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/svapp.hxx> + +#include <svx/sdrpagewindow.hxx> +#include <svx/sdrpaintwindow.hxx> +#include <svx/sdr/overlay/overlaybitmapex.hxx> +#include <svx/sdr/overlay/overlaymanager.hxx> +#include <svx/svxids.hrc> +#include <svx/svdpagv.hxx> + +#include <view/viewoverlaymanager.hxx> + + +#include <DrawDocShell.hxx> +#include <strings.hrc> +#include <bitmaps.hlst> +#include <sdresid.hxx> +#include <EventMultiplexer.hxx> +#include <View.hxx> +#include <ViewShellBase.hxx> +#include <ViewShell.hxx> +#include <sdpage.hxx> +#include <smarttag.hxx> + +using namespace ::com::sun::star::uno; + +namespace sd { + +namespace { + +class ImageButtonHdl; + +} + +const sal_uInt16 gButtonSlots[] = { SID_INSERT_TABLE, SID_INSERT_DIAGRAM, SID_INSERT_GRAPHIC, SID_INSERT_AVMEDIA }; +const TranslateId gButtonToolTips[] = { STR_INSERT_TABLE, STR_INSERT_CHART, STR_INSERT_PICTURE, STR_INSERT_MOVIE }; + +constexpr rtl::OUStringConstExpr aSmallPlaceHolders[] = +{ + BMP_PLACEHOLDER_TABLE_SMALL, + BMP_PLACEHOLDER_CHART_SMALL, + BMP_PLACEHOLDER_IMAGE_SMALL, + BMP_PLACEHOLDER_MOVIE_SMALL, + BMP_PLACEHOLDER_TABLE_SMALL_HOVER, + BMP_PLACEHOLDER_CHART_SMALL_HOVER, + BMP_PLACEHOLDER_IMAGE_SMALL_HOVER, + BMP_PLACEHOLDER_MOVIE_SMALL_HOVER +}; + +constexpr rtl::OUStringConstExpr aBigPlaceHolders[] = +{ + BMP_PLACEHOLDER_TABLE_LARGE, + BMP_PLACEHOLDER_CHART_LARGE, + BMP_PLACEHOLDER_IMAGE_LARGE, + BMP_PLACEHOLDER_MOVIE_LARGE, + BMP_PLACEHOLDER_TABLE_LARGE_HOVER, + BMP_PLACEHOLDER_CHART_LARGE_HOVER, + BMP_PLACEHOLDER_IMAGE_LARGE_HOVER, + BMP_PLACEHOLDER_MOVIE_LARGE_HOVER +}; + +static BitmapEx* getButtonImage( int index, bool large ) +{ + static vcl::DeleteOnDeinit< BitmapEx > gSmallButtonImages[SAL_N_ELEMENTS(aSmallPlaceHolders)] = { vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty }; + static vcl::DeleteOnDeinit< BitmapEx > gLargeButtonImages[SAL_N_ELEMENTS(aBigPlaceHolders)] = { vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty, vcl::DeleteOnDeinitFlag::Empty }; + + assert(SAL_N_ELEMENTS(aSmallPlaceHolders) == SAL_N_ELEMENTS(aBigPlaceHolders)); + + if( !gSmallButtonImages[0].get() ) + { + for (size_t i = 0; i < SAL_N_ELEMENTS(aSmallPlaceHolders); i++ ) + { + gSmallButtonImages[i].set(OUString(aSmallPlaceHolders[i])); + gLargeButtonImages[i].set(OUString(aBigPlaceHolders[i])); + } + } + + if( large ) + { + return gLargeButtonImages[index].get(); + } + else + { + return gSmallButtonImages[index].get(); + } +} + +const sal_uInt32 SMART_TAG_HDL_NUM = SAL_MAX_UINT32; + +namespace { + +class ChangePlaceholderTag : public SmartTag +{ + friend class ImageButtonHdl; +public: + ChangePlaceholderTag( ::sd::View& rView, SdrObject& rPlaceholderObj ); + + /** returns true if the SmartTag handled the event. */ + virtual bool MouseButtonDown( const MouseEvent&, SmartHdl& ) override; + + /** returns true if the SmartTag consumes this event. */ + virtual bool KeyInput( const KeyEvent& rKEvt ) override; + + BitmapEx createOverlayImage( int nHighlight ); + +protected: + virtual void addCustomHandles( SdrHdlList& rHandlerList ) override; + +private: + ::tools::WeakReference<SdrObject> mxPlaceholderObj; +}; + +class ImageButtonHdl : public SmartHdl +{ +public: + ImageButtonHdl( const SmartTagReference& xTag, /* sal_uInt16 nSID, const Image& rImage, const Image& rImageMO, */ const Point& rPnt ); + virtual ~ImageButtonHdl() override; + virtual void CreateB2dIAObject() override; + virtual bool IsFocusHdl() const override; + virtual PointerStyle GetPointer() const override; + + virtual void onMouseEnter(const MouseEvent& rMEvt) override; + virtual void onHelpRequest() override; + virtual void onMouseLeave() override; + + int getHighlightId() const { return mnHighlightId; } + + void ShowTip(); + static void HideTip(); + +private: + rtl::Reference< ChangePlaceholderTag > mxChangePlaceholderTag; + + int mnHighlightId; + Size maImageSize; +}; + +} + +ImageButtonHdl::ImageButtonHdl( const SmartTagReference& xTag /*, sal_uInt16 nSID, const Image& rImage, const Image& rImageMO*/, const Point& rPnt ) +: SmartHdl( xTag, rPnt, SdrHdlKind::SmartTag ) +, mxChangePlaceholderTag( dynamic_cast< ChangePlaceholderTag* >( xTag.get() ) ) +, mnHighlightId( -1 ) +, maImageSize( 42, 42 ) +{ +} + +ImageButtonHdl::~ImageButtonHdl() +{ + HideTip(); +} + +void ImageButtonHdl::HideTip() +{ + Help::HideBalloonAndQuickHelp(); +} + +void ImageButtonHdl::ShowTip() +{ + if (!pHdlList || !pHdlList->GetView() || mnHighlightId == -1) + return; + + OutputDevice* pDev = pHdlList->GetView()->GetFirstOutputDevice(); + if( pDev == nullptr ) + pDev = Application::GetDefaultDevice(); + + OUString aHelpText(SdResId(gButtonToolTips[mnHighlightId])); + Point aHelpPos(pDev->LogicToPixel(GetPos())); + if (mnHighlightId == 1) + aHelpPos.Move(maImageSize.Width(), 0); + else if (mnHighlightId == 2) + aHelpPos.Move(0, maImageSize.Height()); + else if (mnHighlightId == 3) + aHelpPos.Move(maImageSize.Width(), maImageSize.Height()); + ::tools::Rectangle aLogicPix(aHelpPos, maImageSize); + vcl::Window* pWindow = pHdlList->GetView()->GetFirstOutputDevice()->GetOwnerWindow(); + ::tools::Rectangle aScreenRect(pWindow->OutputToScreenPixel(aLogicPix.TopLeft()), + pWindow->OutputToScreenPixel(aLogicPix.BottomRight())); + Help::ShowQuickHelp(pWindow, aScreenRect, aHelpText); +} + +void ImageButtonHdl::onHelpRequest() +{ + ShowTip(); +} + +void ImageButtonHdl::onMouseEnter(const MouseEvent& rMEvt) +{ + if( !(pHdlList && pHdlList->GetView())) + return; + + int nHighlightId = 0; + OutputDevice* pDev = pHdlList->GetView()->GetFirstOutputDevice(); + if( pDev == nullptr ) + pDev = Application::GetDefaultDevice(); + + Point aMDPos( rMEvt.GetPosPixel() ); + aMDPos -= pDev->LogicToPixel( GetPos() ); + + nHighlightId += aMDPos.X() > maImageSize.Width() ? 1 : 0; + nHighlightId += aMDPos.Y() > maImageSize.Height() ? 2 : 0; + + if( mnHighlightId != nHighlightId ) + { + HideTip(); + + mnHighlightId = nHighlightId; + + ShowTip(); + + Touch(); + } +} + +void ImageButtonHdl::onMouseLeave() +{ + mnHighlightId = -1; + HideTip(); + Touch(); +} + +void ImageButtonHdl::CreateB2dIAObject() +{ + // first throw away old one + GetRidOfIAObject(); + + const Point aTagPos( GetPos() ); + basegfx::B2DPoint aPosition( aTagPos.X(), aTagPos.Y() ); + + BitmapEx aBitmapEx( mxChangePlaceholderTag->createOverlayImage( mnHighlightId ) ); // maImageMO.GetBitmapEx() : maImage.GetBitmapEx() ); + maImageSize = aBitmapEx.GetSizePixel(); + maImageSize.setWidth( maImageSize.Width() >> 1 ); + maImageSize.setHeight( maImageSize.Height() >> 1 ); + + if(!pHdlList) + return; + + SdrMarkView* pView = pHdlList->GetView(); + + if(!pView || pView->areMarkHandlesHidden()) + return; + + SdrPageView* pPageView = pView->GetSdrPageView(); + + if(!pPageView) + return; + + for(sal_uInt32 b = 0; b < pPageView->PageWindowCount(); b++) + { + const SdrPageWindow& rPageWindow = *pPageView->GetPageWindow(b); + + SdrPaintWindow& rPaintWindow = rPageWindow.GetPaintWindow(); + const rtl::Reference< sdr::overlay::OverlayManager >& xManager = rPageWindow.GetOverlayManager(); + if(rPaintWindow.OutputToWindow() && xManager.is() ) + { + std::unique_ptr<sdr::overlay::OverlayObject> pOverlayObject( + new sdr::overlay::OverlayBitmapEx( aPosition, aBitmapEx, 0, 0 )); + + // OVERLAYMANAGER + insertNewlyCreatedOverlayObjectForSdrHdl( + std::move(pOverlayObject), + rPageWindow.GetObjectContact(), + *xManager); + } + } +} + +bool ImageButtonHdl::IsFocusHdl() const +{ + return false; +} + +PointerStyle ImageButtonHdl::GetPointer() const +{ + return PointerStyle::Arrow; +} + +ChangePlaceholderTag::ChangePlaceholderTag( ::sd::View& rView, SdrObject& rPlaceholderObj ) +: SmartTag( rView ) +, mxPlaceholderObj( &rPlaceholderObj ) +{ +} + +/** returns true if the ChangePlaceholderTag handled the event. */ +bool ChangePlaceholderTag::MouseButtonDown( const MouseEvent& /*rMEvt*/, SmartHdl& rHdl ) +{ + int nHighlightId = static_cast< ImageButtonHdl& >(rHdl).getHighlightId(); + if( nHighlightId >= 0 ) + { + sal_uInt16 nSID = gButtonSlots[nHighlightId]; + + if( mxPlaceholderObj ) + { + // mark placeholder if it is not currently marked (or if also others are marked) + if( !mrView.IsObjMarked( mxPlaceholderObj.get() ) || (mrView.GetMarkedObjectList().GetMarkCount() != 1) ) + { + SdrPageView* pPV = mrView.GetSdrPageView(); + mrView.UnmarkAllObj(pPV ); + mrView.MarkObj(mxPlaceholderObj.get(), pPV); + } + } + + mrView.GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( nSID, SfxCallMode::ASYNCHRON); + } + return false; +} + +/** returns true if the SmartTag consumes this event. */ +bool ChangePlaceholderTag::KeyInput( const KeyEvent& rKEvt ) +{ + sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch( nCode ) + { + case KEY_DOWN: + case KEY_UP: + case KEY_LEFT: + case KEY_RIGHT: + case KEY_ESCAPE: + case KEY_TAB: + case KEY_RETURN: + case KEY_SPACE: + default: + return false; + } +} + +BitmapEx ChangePlaceholderTag::createOverlayImage( int nHighlight ) +{ + BitmapEx aRet; + if( mxPlaceholderObj.is() ) + { + SdrObject* pPlaceholder = mxPlaceholderObj.get(); + SmartTagReference xThis( this ); + const ::tools::Rectangle& rSnapRect = pPlaceholder->GetSnapRect(); + + OutputDevice* pDev = mrView.GetFirstOutputDevice(); + if( pDev == nullptr ) + pDev = Application::GetDefaultDevice(); + + Size aShapeSizePix = pDev->LogicToPixel(rSnapRect.GetSize()); + ::tools::Long nShapeSizePix = std::min(aShapeSizePix.Width(),aShapeSizePix.Height()); + + bool bLarge = nShapeSizePix > 250; + + Size aSize( getButtonImage( 0, bLarge )->GetSizePixel() ); + + aRet.Scale(Size(aSize.Width() << 1, aSize.Height() << 1)); + + const ::tools::Rectangle aRectSrc( Point( 0, 0 ), aSize ); + + aRet = *(getButtonImage((nHighlight == 0) ? 4 : 0, bLarge)); + aRet.Expand( aSize.Width(), aSize.Height(), true ); + + aRet.CopyPixel( ::tools::Rectangle( Point( aSize.Width(), 0 ), aSize ), aRectSrc, getButtonImage((nHighlight == 1) ? 5 : 1, bLarge) ); + aRet.CopyPixel( ::tools::Rectangle( Point( 0, aSize.Height() ), aSize ), aRectSrc, getButtonImage((nHighlight == 2) ? 6 : 2, bLarge) ); + aRet.CopyPixel( ::tools::Rectangle( Point( aSize.Width(), aSize.Height() ), aSize ), aRectSrc, getButtonImage((nHighlight == 3) ? 7 : 3, bLarge) ); + } + + return aRet; +} + +void ChangePlaceholderTag::addCustomHandles( SdrHdlList& rHandlerList ) +{ + if( !mxPlaceholderObj.is() ) + return; + + SdrObject* pPlaceholder = mxPlaceholderObj.get(); + SmartTagReference xThis( this ); + const ::tools::Rectangle& rSnapRect = pPlaceholder->GetSnapRect(); + const Point aPoint; + + OutputDevice* pDev = mrView.GetFirstOutputDevice(); + if( pDev == nullptr ) + pDev = Application::GetDefaultDevice(); + + Size aShapeSizePix = pDev->LogicToPixel(rSnapRect.GetSize()); + ::tools::Long nShapeSizePix = std::min(aShapeSizePix.Width(),aShapeSizePix.Height()); + if( 50 > nShapeSizePix ) + return; + + bool bLarge = nShapeSizePix > 250; + + Size aButtonSize( pDev->PixelToLogic( getButtonImage(0, bLarge )->GetSizePixel()) ); + + const int nColumns = 2; + const int nRows = 2; + + ::tools::Long all_width = nColumns * aButtonSize.Width(); + ::tools::Long all_height = nRows * aButtonSize.Height(); + + Point aPos( rSnapRect.Center() ); + aPos.AdjustX( -(all_width >> 1) ); + aPos.AdjustY( -(all_height >> 1) ); + + std::unique_ptr<ImageButtonHdl> pHdl(new ImageButtonHdl( xThis, aPoint )); + pHdl->SetObjHdlNum( SMART_TAG_HDL_NUM ); + pHdl->SetPageView( mrView.GetSdrPageView() ); + + pHdl->SetPos( aPos ); + + rHandlerList.AddHdl( std::move(pHdl) ); +} + +ViewOverlayManager::ViewOverlayManager( ViewShellBase& rViewShellBase ) +: mrBase( rViewShellBase ) +, mnUpdateTagsEvent( nullptr ) +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,ViewOverlayManager,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->AddEventListener(aLink); + + StartListening( *mrBase.GetDocShell() ); +} + +ViewOverlayManager::~ViewOverlayManager() +{ + Link<tools::EventMultiplexerEvent&,void> aLink( LINK(this,ViewOverlayManager,EventMultiplexerListener) ); + mrBase.GetEventMultiplexer()->RemoveEventListener( aLink ); + + if( mnUpdateTagsEvent ) + { + Application::RemoveUserEvent( mnUpdateTagsEvent ); + mnUpdateTagsEvent = nullptr; + } + + DisposeTags(); +} + +void ViewOverlayManager::Notify(SfxBroadcaster&, const SfxHint& rHint) +{ + if (rHint.GetId() == SfxHintId::DocChanged) + { + UpdateTags(); + } +} + +void ViewOverlayManager::onZoomChanged() +{ + if( !maTagVector.empty() ) + { + UpdateTags(); + } +} + +void ViewOverlayManager::UpdateTags() +{ + if( !mnUpdateTagsEvent ) + mnUpdateTagsEvent = Application::PostUserEvent( LINK( this, ViewOverlayManager, UpdateTagsHdl ) ); +} + +IMPL_LINK_NOARG(ViewOverlayManager, UpdateTagsHdl, void*, void) +{ + mnUpdateTagsEvent = nullptr; + bool bChanges = DisposeTags(); + bChanges |= CreateTags(); + + if( bChanges && mrBase.GetDrawView() ) + static_cast< ::sd::View* >( mrBase.GetDrawView() )->updateHandles(); +} + +bool ViewOverlayManager::CreateTags() +{ + bool bChanges = false; + + std::shared_ptr<ViewShell> aMainShell = mrBase.GetMainViewShell(); + + SdPage* pPage = aMainShell ? aMainShell->getCurrentPage() : nullptr; + + if( pPage && !pPage->IsMasterPage() && (pPage->GetPageKind() == PageKind::Standard) ) + { + const std::list< SdrObject* >& rShapes = pPage->GetPresentationShapeList().getList(); + + for( SdrObject* pShape : rShapes ) + { + if( pShape->IsEmptyPresObj() && (pShape->GetObjIdentifier() == SdrObjKind::OutlineText) && (mrBase.GetDrawView()->GetTextEditObject() != pShape) ) + { + rtl::Reference< SmartTag > xTag( new ChangePlaceholderTag( *mrBase.GetMainViewShell()->GetView(), *pShape ) ); + maTagVector.push_back(xTag); + bChanges = true; + } + } + } + + return bChanges; +} + +bool ViewOverlayManager::DisposeTags() +{ + if( !maTagVector.empty() ) + { + ViewTagVector vec; + vec.swap( maTagVector ); + + for (auto& rxViewTag : vec) + rxViewTag->Dispose(); + return true; + } + + return false; +} + +IMPL_LINK(ViewOverlayManager,EventMultiplexerListener, + tools::EventMultiplexerEvent&, rEvent, void) +{ + switch (rEvent.meEventId) + { + case EventMultiplexerEventId::MainViewAdded: + case EventMultiplexerEventId::ViewAdded: + case EventMultiplexerEventId::BeginTextEdit: + case EventMultiplexerEventId::EndTextEdit: + case EventMultiplexerEventId::CurrentPageChanged: + UpdateTags(); + break; + default: break; + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/viewshe2.cxx b/sd/source/ui/view/viewshe2.cxx new file mode 100644 index 000000000..8b16124ba --- /dev/null +++ b/sd/source/ui/view/viewshe2.cxx @@ -0,0 +1,958 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <com/sun/star/embed/EmbedVerbs.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +#include <ViewShell.hxx> +#include <ViewShellHint.hxx> + +#include <ViewShellImplementation.hxx> +#include <FactoryIds.hxx> + +#include <svx/svxids.hrc> +#include <vcl/scrbar.hxx> +#include <svx/svdpagv.hxx> +#include <sfx2/dispatch.hxx> +#include <svx/ruler.hxx> +#include <editeng/outliner.hxx> +#include <svtools/ehdl.hxx> +#include <svx/svdoole2.hxx> +#include <svtools/sfxecode.hxx> +#include <unotools/moduleoptions.hxx> +#include <comphelper/classids.hxx> +#include <osl/diagnose.h> + +#include <strings.hrc> +#include <app.hrc> +#include <unokywds.hxx> + +#include <sdundogr.hxx> +#include <FrameView.hxx> +#include <sdresid.hxx> +#include <drawdoc.hxx> +#include <View.hxx> +#include <fupoor.hxx> +#include <Client.hxx> +#include <DrawDocShell.hxx> +#include <sdpage.hxx> +#include <DrawViewShell.hxx> +#include <ViewShellBase.hxx> + +#include <Window.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svtools/soerr.hxx> +#include <svx/charthelper.hxx> +#include <comphelper/lok.hxx> + +using namespace com::sun::star; + +namespace sd { + +/** + * adjust Thumbpos and VisibleSize + */ +void ViewShell::UpdateScrollBars() +{ + if (mpHorizontalScrollBar) + { + ::tools::Long nW = static_cast<::tools::Long>(mpContentWindow->GetVisibleWidth() * 32000); + ::tools::Long nX = static_cast<::tools::Long>(mpContentWindow->GetVisibleX() * 32000); + mpHorizontalScrollBar->SetVisibleSize(nW); + mpHorizontalScrollBar->SetThumbPos(nX); + nW = 32000 - nW; + ::tools::Long nLine = static_cast<::tools::Long>(mpContentWindow->GetScrlLineWidth() * nW); + ::tools::Long nPage = static_cast<::tools::Long>(mpContentWindow->GetScrlPageWidth() * nW); + mpHorizontalScrollBar->SetLineSize(nLine); + mpHorizontalScrollBar->SetPageSize(nPage); + } + + if (mpVerticalScrollBar) + { + ::tools::Long nH = static_cast<::tools::Long>(mpContentWindow->GetVisibleHeight() * 32000); + ::tools::Long nY = static_cast<::tools::Long>(mpContentWindow->GetVisibleY() * 32000); + + if(IsPageFlipMode()) // ie in zoom mode where no panning + { + SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage(); + sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) / 2; + sal_uInt16 nTotalPages = GetDoc()->GetSdPageCount(pPage->GetPageKind()); + mpVerticalScrollBar->SetRange(Range(0,256*nTotalPages)); + mpVerticalScrollBar->SetVisibleSize(256); + mpVerticalScrollBar->SetThumbPos(256*nCurPage); + mpVerticalScrollBar->SetLineSize(256); + mpVerticalScrollBar->SetPageSize(256); + } + else + { + mpVerticalScrollBar->SetRange(Range(0,32000)); + mpVerticalScrollBar->SetVisibleSize(nH); + mpVerticalScrollBar->SetThumbPos(nY); + nH = 32000 - nH; + ::tools::Long nLine = static_cast<::tools::Long>(mpContentWindow->GetScrlLineHeight() * nH); + ::tools::Long nPage = static_cast<::tools::Long>(mpContentWindow->GetScrlPageHeight() * nH); + mpVerticalScrollBar->SetLineSize(nLine); + mpVerticalScrollBar->SetPageSize(nPage); + } + } + + if (mbHasRulers) + { + UpdateHRuler(); + UpdateVRuler(); + } + +} +/** + * Handling for horizontal Scrollbars + */ +IMPL_LINK(ViewShell, HScrollHdl, ScrollBar *, pHScroll, void ) +{ + VirtHScrollHdl(pHScroll); +} + +/** + * virtual scroll handler for horizontal Scrollbars + */ +void ViewShell::VirtHScrollHdl(ScrollBar* pHScroll) +{ + ::tools::Long nDelta = pHScroll->GetDelta(); + + if (nDelta == 0) + return; + + double fX = static_cast<double>(pHScroll->GetThumbPos()) / pHScroll->GetRange().Len(); + + // scroll all windows of the column + ::sd::View* pView = GetView(); + OutlinerView* pOLV = nullptr; + + if (pView) + pOLV = pView->GetTextEditOutlinerView(); + + if (pOLV) + pOLV->HideCursor(); + + mpContentWindow->SetVisibleXY(fX, -1); + + ::tools::Rectangle aVisArea = GetDocSh()->GetVisArea(ASPECT_CONTENT); + Point aVisAreaPos = GetActiveWindow()->PixelToLogic( Point(0,0) ); + aVisArea.SetPos(aVisAreaPos); + GetDocSh()->SetVisArea(aVisArea); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + if (pOLV) + pOLV->ShowCursor(); + + if (mbHasRulers) + UpdateHRuler(); +} + +/** + * handling for vertical Scrollbars + */ +IMPL_LINK(ViewShell, VScrollHdl, ScrollBar *, pVScroll, void ) +{ + VirtVScrollHdl(pVScroll); +} + +/** + * handling for vertical Scrollbars + */ +void ViewShell::VirtVScrollHdl(ScrollBar* pVScroll) +{ + if(IsPageFlipMode()) + { + SdPage* pPage = static_cast<DrawViewShell*>(this)->GetActualPage(); + sal_uInt16 nCurPage = (pPage->GetPageNum() - 1) >> 1; + sal_uInt16 nNewPage = static_cast<sal_uInt16>(pVScroll->GetThumbPos())/256; + if( nCurPage != nNewPage ) + static_cast<DrawViewShell*>(this)->SwitchPage(nNewPage); + } + else //panning mode + { + double fY = static_cast<double>(pVScroll->GetThumbPos()) / pVScroll->GetRange().Len(); + + ::sd::View* pView = GetView(); + OutlinerView* pOLV = nullptr; + + if (pView) + pOLV = pView->GetTextEditOutlinerView(); + + if (pOLV) + pOLV->HideCursor(); + + mpContentWindow->SetVisibleXY(-1, fY); + + ::tools::Rectangle aVisArea = GetDocSh()->GetVisArea(ASPECT_CONTENT); + Point aVisAreaPos = GetActiveWindow()->PixelToLogic( Point(0,0) ); + aVisArea.SetPos(aVisAreaPos); + GetDocSh()->SetVisArea(aVisArea); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + if (pOLV) + pOLV->ShowCursor(); + + if (mbHasRulers) + UpdateVRuler(); + + } +} + +VclPtr<SvxRuler> ViewShell::CreateHRuler(::sd::Window* ) +{ + return nullptr; +} + +VclPtr<SvxRuler> ViewShell::CreateVRuler(::sd::Window* ) +{ + return nullptr; +} + +void ViewShell::UpdateHRuler() +{ +} + +void ViewShell::UpdateVRuler() +{ +} + +/** + * Scroll a specific number of lines. Is used in the automatic scrolling + * (character/drag). + */ +void ViewShell::ScrollLines(::tools::Long nLinesX, ::tools::Long nLinesY) +{ + if ( nLinesX ) + { + nLinesX *= mpHorizontalScrollBar->GetLineSize(); + } + if ( nLinesY ) + { + nLinesY *= mpVerticalScrollBar->GetLineSize(); + } + + Scroll(nLinesX, nLinesY); +} + +void ViewShell::Scroll(::tools::Long nScrollX, ::tools::Long nScrollY) +{ + if (nScrollX) + { + ::tools::Long nNewThumb = mpHorizontalScrollBar->GetThumbPos() + nScrollX; + mpHorizontalScrollBar->SetThumbPos(nNewThumb); + } + if (nScrollY) + { + ::tools::Long nNewThumb = mpVerticalScrollBar->GetThumbPos() + nScrollY; + mpVerticalScrollBar->SetThumbPos(nNewThumb); + } + double fX = static_cast<double>(mpHorizontalScrollBar->GetThumbPos()) / + mpHorizontalScrollBar->GetRange().Len(); + double fY = static_cast<double>(mpVerticalScrollBar->GetThumbPos()) / + mpVerticalScrollBar->GetRange().Len(); + + GetActiveWindow()->SetVisibleXY(fX, fY); + + ::tools::Rectangle aVisArea = GetDocSh()->GetVisArea(ASPECT_CONTENT); + Point aVisAreaPos = GetActiveWindow()->PixelToLogic( Point(0,0) ); + aVisArea.SetPos(aVisAreaPos); + GetDocSh()->SetVisArea(aVisArea); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + if (mbHasRulers) + { + UpdateHRuler(); + UpdateVRuler(); + } +} + +/** + * Set zoom factor for all split windows. + */ +void ViewShell::SetZoom(::tools::Long nZoom) +{ + Fraction aUIScale(nZoom, 100); + aUIScale *= GetDoc()->GetUIScale(); + + if (mpHorizontalRuler) + mpHorizontalRuler->SetZoom(aUIScale); + + if (mpVerticalRuler) + mpVerticalRuler->SetZoom(aUIScale); + + if (mpContentWindow) + { + mpContentWindow->SetZoomIntegral(nZoom); + + // #i74769# Here is a 2nd way (besides Window::Scroll) to set the visible prt + // of the window. It needs - like Scroll(ScrollFlags::Children) does - also to move + // the child windows. I am trying InvalidateFlags::Children here which makes things better, + // but does not solve the problem completely. Need to ask PL. + mpContentWindow->Invalidate(InvalidateFlags::Children); + } + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + UpdateScrollBars(); +} + +::tools::Long ViewShell::GetZoom() const +{ + if (mpContentWindow) + { + return mpContentWindow->GetZoom(); + } + + return 0; +} + +/** + * Set zoom rectangle for active window. Sets all split windows to the same zoom + * factor. + */ +void ViewShell::SetZoomRect(const ::tools::Rectangle& rZoomRect) +{ + ::tools::Long nZoom = GetActiveWindow()->SetZoomRect(rZoomRect); + Fraction aUIScale(nZoom, 100); + aUIScale *= GetDoc()->GetUIScale(); + + Point aPos = GetActiveWindow()->GetWinViewPos(); + + if (mpHorizontalRuler) + mpHorizontalRuler->SetZoom(aUIScale); + + if (mpVerticalRuler) + mpVerticalRuler->SetZoom(aUIScale); + + if (mpContentWindow) + { + Point aNewPos = mpContentWindow->GetWinViewPos(); + aNewPos.setX( aPos.X() ); + aNewPos.setY( aPos.Y() ); + mpContentWindow->SetZoomIntegral(nZoom); + mpContentWindow->SetWinViewPos(aNewPos); + mpContentWindow->UpdateMapOrigin(); + + // When tiled rendering, UpdateMapOrigin() doesn't touch the map mode. + if (!comphelper::LibreOfficeKit::isActive()) + // #i74769# see above + mpContentWindow->Invalidate(InvalidateFlags::Children); + } + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } + + UpdateScrollBars(); +} + +/** + * Initialize imaging parameters for all split windows. + */ +void ViewShell::InitWindows(const Point& rViewOrigin, const Size& rViewSize, + const Point& rWinPos, bool bUpdate) +{ + if (mpContentWindow) + { + mpContentWindow->SetViewOrigin(rViewOrigin); + mpContentWindow->SetViewSize(rViewSize); + mpContentWindow->SetWinViewPos(rWinPos); + + if ( bUpdate ) + { + mpContentWindow->UpdateMapOrigin(); + mpContentWindow->Invalidate(); + } + } + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } +} + +/** + * Invalidate all split windows below the ?provided rectangle. + */ +void ViewShell::InvalidateWindows() +{ + if (mpContentWindow) + mpContentWindow->Invalidate(); +} + +/** + * Draw a selection rectangle with the ?provided pen on all split windows. + */ +void ViewShell::DrawMarkRect(const ::tools::Rectangle& rRect) const +{ + if (mpContentWindow) + { + mpContentWindow->InvertTracking(rRect, ShowTrackFlags::Object | ShowTrackFlags::TrackWindow); + } +} + +void ViewShell::SetPageSizeAndBorder(PageKind ePageKind, const Size& rNewSize, + ::tools::Long nLeft, ::tools::Long nRight, + ::tools::Long nUpper, ::tools::Long nLower, bool bScaleAll, + Orientation eOrientation, sal_uInt16 nPaperBin, + bool bBackgroundFullSize) +{ + const sal_uInt16 nMasterPageCnt(GetDoc()->GetMasterSdPageCount(ePageKind)); + const sal_uInt16 nPageCnt(GetDoc()->GetSdPageCount(ePageKind)); + + if(0 == nPageCnt && 0 == nMasterPageCnt) + { + return; + } + + std::unique_ptr<SdUndoGroup> pUndoGroup; + SfxViewShell* pViewShell(GetViewShell()); + if (pViewShell) + { + pUndoGroup.reset(new SdUndoGroup(GetDoc())); + pUndoGroup->SetComment(SdResId(STR_UNDO_CHANGE_PAGEFORMAT)); + } + Broadcast (ViewShellHint(ViewShellHint::HINT_PAGE_RESIZE_START)); + + // use Model-based method at SdDrawDocument + GetDoc()->AdaptPageSizeForAllPages( + rNewSize, + ePageKind, + pUndoGroup.get(), + nLeft, + nRight, + nUpper, + nLower, + bScaleAll, + eOrientation, + nPaperBin, + bBackgroundFullSize); + + // adjust handout page to new format of the standard page + if(0 != nPageCnt && ((ePageKind == PageKind::Standard) || (ePageKind == PageKind::Handout))) + { + GetDoc()->GetSdPage(0, PageKind::Handout)->CreateTitleAndLayout(true); + } + + // handed over undo group to undo manager + if (pViewShell) + { + pViewShell->GetViewFrame()->GetObjectShell()->GetUndoManager()->AddUndoAction(std::move(pUndoGroup)); + } + + // calculate View-Sizes + SdPage* pPage(0 != nPageCnt + ? GetDoc()->GetSdPage(0, ePageKind) + : GetDoc()->GetMasterSdPage(0, ePageKind)); + const ::tools::Long nWidth(pPage->GetSize().Width()); + const ::tools::Long nHeight(pPage->GetSize().Height()); + const Point aPageOrg(nWidth, nHeight / 2); + const Size aViewSize(nWidth * 3, nHeight * 2); + Point aVisAreaPos; + ::sd::View* pView(GetView()); + const Point aNewOrigin(pPage->GetLeftBorder(), pPage->GetUpperBorder()); + + InitWindows(aPageOrg, aViewSize, Point(-1, -1), true); + + if ( GetDocSh()->GetCreateMode() == SfxObjectCreateMode::EMBEDDED ) + { + aVisAreaPos = GetDocSh()->GetVisArea(ASPECT_CONTENT).TopLeft(); + } + + if (pView) + { + pView->SetWorkArea(::tools::Rectangle(Point() - aVisAreaPos - aPageOrg, aViewSize)); + } + + UpdateScrollBars(); + + if (pView) + { + pView->GetSdrPageView()->SetPageOrigin(aNewOrigin); + } + + if(nullptr != pViewShell) + { + pViewShell->GetViewFrame()->GetBindings().Invalidate(SID_RULER_NULL_OFFSET); + // zoom onto (new) page size + pViewShell->GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_PAGE, SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); + } + + Broadcast(ViewShellHint(ViewShellHint::HINT_PAGE_RESIZE_END)); +} + +/** + * Set zoom factor for InPlace + */ +void ViewShell::SetZoomFactor(const Fraction& rZoomX, const Fraction&) +{ + ::tools::Long nZoom = static_cast<::tools::Long>(static_cast<double>(rZoomX) * 100); + SetZoom(nZoom); +} + +void ViewShell::SetActiveWindow (::sd::Window* pWin) +{ + SfxViewShell* pViewShell = GetViewShell(); + OSL_ASSERT (pViewShell!=nullptr); + + if (pViewShell->GetWindow() != pWin) + { + // #i31551# was wrong, it may have been a problem with the repaint at that time. + // For transparent form controls, it is necessary to have that flag set, all apps + // do set it. Enabling again. + if (pWin) + { + pWin->EnableChildTransparentMode(); + } + } + + if (mpActiveWindow.get() != pWin) + mpActiveWindow = pWin; + + // The rest of this function is not guarded anymore against calling this + // method with an already active window because the functions may still + // point to the old window when the new one has already been assigned to + // pWindow elsewhere. + ::sd::View* pView = GetView(); + if (pView) + { + pView->SetActualWin(pWin->GetOutDev()); + } + if(HasCurrentFunction()) + { + GetCurrentFunction()->SetWindow(pWin); + } +} + +bool ViewShell::RequestHelp(const HelpEvent& rHEvt) +{ + bool bReturn = false; + + if (bool(rHEvt.GetMode())) + { + if(HasCurrentFunction()) + { + bReturn = GetCurrentFunction()->RequestHelp(rHEvt); + } + } + + return bReturn; +} + +void ViewShell::SetFrameView (FrameView* pNewFrameView) +{ + mpFrameView = pNewFrameView; + ReadFrameViewData (mpFrameView); +} + +/************************************************************************* +|* +|* Read FrameViews data and set actual views data +|* +\************************************************************************/ + +void ViewShell::ReadFrameViewData(FrameView*) +{ +} + +/************************************************************************* +|* +|* Write actual views data to FrameView +|* +\************************************************************************/ + +void ViewShell::WriteFrameViewData() +{ +} + +bool ViewShell::ActivateObject(SdrOle2Obj* pObj, sal_Int32 nVerb) +{ + ErrCode aErrCode = ERRCODE_NONE; + + SfxErrorContext aEC(ERRCTX_SO_DOVERB, GetFrameWeld(), RID_SO_ERRCTX); + bool bAbort = false; + GetDocSh()->SetWaitCursor( true ); + SfxViewShell* pViewShell = GetViewShell(); + OSL_ASSERT (pViewShell!=nullptr); + bool bChangeDefaultsForChart = false; + + uno::Reference < embed::XEmbeddedObject > xObj = pObj->GetObjRef(); + if ( !xObj.is() ) + { + // provide OLE object to empty OLE object + OUString aName = pObj->GetProgName(); + OUString aObjName; + SvGlobalName aClass; + + if( aName == "StarChart" || aName == "StarOrg" ) + { + if( SvtModuleOptions().IsChart() ) + { + aClass = SvGlobalName( SO3_SCH_CLASSID ); + bChangeDefaultsForChart = true; + } + } + else if( aName == "StarCalc" ) + { + if( SvtModuleOptions().IsCalc() ) + aClass = SvGlobalName( SO3_SC_CLASSID ); + } + else if( aName == "StarMath" ) + { + if( SvtModuleOptions().IsMath() ) + aClass = SvGlobalName( SO3_SM_CLASSID ); + } + + if ( aClass != SvGlobalName() ) + xObj = GetDocSh()->GetEmbeddedObjectContainer().CreateEmbeddedObject( aClass.GetByteSequence(), aObjName ); + + if( !xObj.is() ) + { + aName.clear(); + + // call dialog "insert OLE object" + GetDocSh()->SetWaitCursor( false ); + pViewShell->GetViewFrame()->GetDispatcher()->Execute( + SID_INSERT_OBJECT, + SfxCallMode::SYNCHRON | SfxCallMode::RECORD); + xObj = pObj->GetObjRef(); + GetDocSh()->SetWaitCursor( true ); + + if (!xObj.is()) + { + bAbort = true; + } + } + + if ( xObj.is() ) + { + // OLE object is no longer empty + pObj->SetEmptyPresObj(false); + pObj->SetOutlinerParaObject(std::nullopt); + pObj->ClearGraphic(); + + // the empty OLE object gets a new IPObj + if (!aName.isEmpty()) + { + pObj->SetObjRef(xObj); + pObj->SetName(aObjName); + pObj->SetPersistName(aObjName); + } + else + { + // insertion was done by the dialog + pObj->SetObjRef(xObj); + } + + ::tools::Rectangle aRect = pObj->GetLogicRect(); + + if ( pObj->GetAspect() != embed::Aspects::MSOLE_ICON ) + { + awt::Size aSz; + aSz.Width = aRect.GetWidth(); + aSz.Height = aRect.GetHeight(); + xObj->setVisualAreaSize( pObj->GetAspect(), aSz ); + } + + GetViewShellBase().SetVerbs( xObj->getSupportedVerbs() ); + + nVerb = embed::EmbedVerbs::MS_OLEVERB_SHOW; + } + else + { + aErrCode = ERRCODE_SFX_OLEGENERAL; + } + } + + if( aErrCode == ERRCODE_NONE ) + { + ::sd::View* pView = GetView(); + + if (pView->IsTextEdit()) + { + pView->SdrEndTextEdit(); + } + + SfxInPlaceClient* pSdClient = + pViewShell->FindIPClient(pObj->GetObjRef(), GetActiveWindow()); + + if ( !pSdClient ) + { + pSdClient = new Client(pObj, this, GetActiveWindow()); + } + + ::tools::Rectangle aRect = pObj->GetLogicRect(); + + { + // #i118485# center on BoundRect for activation, + // OLE may be sheared/rotated now + const ::tools::Rectangle& rBoundRect = pObj->GetCurrentBoundRect(); + const Point aDelta(rBoundRect.Center() - aRect.Center()); + aRect.Move(aDelta.X(), aDelta.Y()); + } + + Size aDrawSize = aRect.GetSize(); + + MapMode aMapMode( GetDoc()->GetScaleUnit() ); + Size aObjAreaSize = pObj->GetOrigObjSize( &aMapMode ); + if( pObj->IsChart() ) //charts never should be stretched see #i84323# for example + aObjAreaSize = aDrawSize; + + Fraction aScaleWidth (aDrawSize.Width(), aObjAreaSize.Width() ); + Fraction aScaleHeight(aDrawSize.Height(), aObjAreaSize.Height() ); + aScaleWidth.ReduceInaccurate(10); // compatible to the SdrOle2Obj + aScaleHeight.ReduceInaccurate(10); + pSdClient->SetSizeScale(aScaleWidth, aScaleHeight); + + // visible section is only changed in-place! + aRect.SetSize(aObjAreaSize); + // the object area size must be set after scaling, since it triggers the resizing + pSdClient->SetObjArea(aRect); + + if( bChangeDefaultsForChart && xObj.is()) + { + ChartHelper::AdaptDefaultsForChart( xObj ); + } + + pSdClient->DoVerb(nVerb); // if necessary, ErrCode is outputted by Sfx + pViewShell->GetViewFrame()->GetBindings().Invalidate( + SID_NAVIGATOR_STATE, true); + } + + GetDocSh()->SetWaitCursor( false ); + + if (aErrCode != ERRCODE_NONE && !bAbort) + { + ErrorHandler::HandleError(* new StringErrorInfo(aErrCode, OUString() ) ); + } + + return aErrCode == ERRCODE_NONE; +} + +/** + * @returns enclosing rectangle of all (split-) windows. + */ +const ::tools::Rectangle& ViewShell::GetAllWindowRect() +{ + maAllWindowRectangle.SetPos( + mpContentWindow->OutputToScreenPixel(Point(0,0))); + return maAllWindowRectangle; +} + +void ViewShell::ReadUserData() +{ + // zoom onto VisArea from FrameView + GetViewShell()->GetViewFrame()->GetDispatcher()->Execute(SID_SIZE_VISAREA, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD); +} + +void ViewShell::WriteUserData() +{ + // writing of our data is always done in WriteFrameViewData() + WriteFrameViewData(); +} + +/** + * Switch ruler on/off + */ +void ViewShell::SetRuler(bool bRuler) +{ + mbHasRulers = ( bRuler && !GetDocSh()->IsPreview() ); // no rulers on preview mode + + if (mpHorizontalRuler) + { + if (mbHasRulers) + { + mpHorizontalRuler->Show(); + } + else + { + mpHorizontalRuler->Hide(); + } + } + + if (mpVerticalRuler) + { + if (mbHasRulers) + { + mpVerticalRuler->Show(); + } + else + { + mpVerticalRuler->Hide(); + } + } + + OSL_ASSERT(GetViewShell()!=nullptr); + if (IsMainViewShell()) + GetViewShell()->InvalidateBorder(); +} + +void ViewShell::SetScrollBarsVisible(bool bVisible) +{ + if (mpVerticalScrollBar) + mpVerticalScrollBar->Show( bVisible ); + + if (mpHorizontalScrollBar) + mpHorizontalScrollBar->Show( bVisible ); + + if (mpScrollBarBox) + mpScrollBarBox->Show(bVisible); +} + +sal_Int8 ViewShell::AcceptDrop ( + const AcceptDropEvent& rEvt, + DropTargetHelper& rTargetHelper, + ::sd::Window* /*pTargetWindow*/, + sal_uInt16 /*nPage*/, + SdrLayerID nLayer) +{ + ::sd::View* pView = GetView(); + return( pView ? pView->AcceptDrop( rEvt, rTargetHelper, nLayer ) : DND_ACTION_NONE ); +} + +sal_Int8 ViewShell::ExecuteDrop ( + const ExecuteDropEvent& rEvt, + DropTargetHelper& /*rTargetHelper*/, + ::sd::Window* pTargetWindow, + sal_uInt16 nPage, + SdrLayerID nLayer) +{ + ::sd::View* pView = GetView(); + return pView ? pView->ExecuteDrop( rEvt, pTargetWindow, nPage, nLayer ) : DND_ACTION_NONE; +} + +void ViewShell::WriteUserDataSequence ( css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + const sal_Int32 nIndex = rSequence.getLength(); + rSequence.realloc( nIndex + 1 ); + auto pSequence = rSequence.getArray(); + + OSL_ASSERT (GetViewShell()!=nullptr); + // Get the view id from the view shell in the center pane. This will + // usually be the called view shell, but to be on the safe side we call + // the main view shell explicitly. + SfxInterfaceId nViewID (IMPRESS_FACTORY_ID); + if (GetViewShellBase().GetMainViewShell() != nullptr) + nViewID = GetViewShellBase().GetMainViewShell()->mpImpl->GetViewId(); + pSequence[nIndex].Name = sUNO_View_ViewId; + pSequence[nIndex].Value <<= "view" + OUString::number( static_cast<sal_uInt16>(nViewID)); + + mpFrameView->WriteUserDataSequence( rSequence ); +} + +void ViewShell::ReadUserDataSequence ( const css::uno::Sequence < css::beans::PropertyValue >& rSequence ) +{ + mpFrameView->ReadUserDataSequence( rSequence ); +} + +void ViewShell::VisAreaChanged(const ::tools::Rectangle& /*rRect*/) +{ + OSL_ASSERT (GetViewShell()!=nullptr); + GetViewShell()->VisAreaChanged(); +} + +void ViewShell::SetWinViewPos(const Point& rWinPos) +{ + if (mpContentWindow) + { + mpContentWindow->SetWinViewPos(rWinPos); + + mpContentWindow->UpdateMapOrigin(); + mpContentWindow->Invalidate(); + } + + if (mbHasRulers) + { + UpdateHRuler(); + UpdateVRuler(); + } + + UpdateScrollBars(); + + Size aVisSizePixel = GetActiveWindow()->GetOutputSizePixel(); + ::tools::Rectangle aVisAreaWin = GetActiveWindow()->PixelToLogic( ::tools::Rectangle( Point(0,0), aVisSizePixel) ); + VisAreaChanged(aVisAreaWin); + + ::sd::View* pView = GetView(); + if (pView) + { + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); + } +} + +Point const & ViewShell::GetWinViewPos() const +{ + return mpContentWindow->GetWinViewPos(); +} + +Point const & ViewShell::GetViewOrigin() const +{ + return mpContentWindow->GetViewOrigin(); +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/viewshe3.cxx b/sd/source/ui/view/viewshe3.cxx new file mode 100644 index 000000000..7ebf88b44 --- /dev/null +++ b/sd/source/ui/view/viewshe3.cxx @@ -0,0 +1,383 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <config_features.h> + +#include <ViewShell.hxx> +#include <ViewShellBase.hxx> + +#include <sfx2/viewfrm.hxx> +#include <svtools/strings.hrc> +#include <svtools/svtresid.hxx> + +#include <app.hrc> +#include <strings.hrc> + +#include <sal/log.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/bindings.hxx> +#include <svx/svdundo.hxx> +#include <svl/intitem.hxx> +#include <svl/style.hxx> +#include <svl/stritem.hxx> +#include <stlsheet.hxx> +#include <DrawViewShell.hxx> + +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <DrawDocShell.hxx> +#include <sdresid.hxx> +#include <unokywds.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/request.hxx> +#include <basic/sbstar.hxx> +#include <basic/sberrors.hxx> +#include <xmloff/autolayout.hxx> + +using namespace ::com::sun::star; + +namespace sd { + +/** + * set state (enabled/disabled) of Menu SfxSlots + */ +void ViewShell::GetMenuState( SfxItemSet &rSet ) +{ + if( SfxItemState::DEFAULT == rSet.GetItemState( SID_STYLE_FAMILY ) ) + { + SfxStyleFamily const nFamily = GetDocSh()->GetStyleFamily(); + + SdrView* pDrView = GetDrawView(); + + if( pDrView->AreObjectsMarked() ) + { + SfxStyleSheet* pStyleSheet = pDrView->GetStyleSheet(); + if( pStyleSheet ) + { + if (pStyleSheet->GetFamily() == SfxStyleFamily::Page) + pStyleSheet = static_cast<SdStyleSheet*>(pStyleSheet)->GetPseudoStyleSheet(); + + if( pStyleSheet ) + { + GetDocSh()->SetStyleFamily(pStyleSheet->GetFamily()); + } + } + } + + rSet.Put(SfxUInt16Item(SID_STYLE_FAMILY, static_cast<sal_uInt16>(nFamily))); + } + + if(SfxItemState::DEFAULT == rSet.GetItemState(SID_GETUNDOSTRINGS)) + { + ImpGetUndoStrings(rSet); + } + + if(SfxItemState::DEFAULT == rSet.GetItemState(SID_GETREDOSTRINGS)) + { + ImpGetRedoStrings(rSet); + } + + if(SfxItemState::DEFAULT == rSet.GetItemState(SID_UNDO)) + { + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + if(pUndoManager) + { + if(pUndoManager->GetUndoActionCount() != 0) + { + // If another view created the first undo action, prevent redoing it from this view. + const SfxUndoAction* pAction = pUndoManager->GetUndoAction(); + if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId()) + { + rSet.Put(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + } + else + { + // Set the necessary string like in + // sfx2/source/view/viewfrm.cxx ver 1.23 ln 1072 ff. + OUString aTmp = SvtResId(STR_UNDO) + + pUndoManager->GetUndoActionComment(); + rSet.Put(SfxStringItem(SID_UNDO, aTmp)); + } + } + else + { + rSet.DisableItem(SID_UNDO); + } + } + } + + if(SfxItemState::DEFAULT != rSet.GetItemState(SID_REDO)) + return; + + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + if(!pUndoManager) + return; + + if(pUndoManager->GetRedoActionCount() != 0) + { + // If another view created the first undo action, prevent redoing it from this view. + const SfxUndoAction* pAction = pUndoManager->GetRedoAction(); + if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId()) + { + rSet.Put(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + } + else + { + // Set the necessary string like in + // sfx2/source/view/viewfrm.cxx ver 1.23 ln 1081 ff. + OUString aTmp = SvtResId(STR_REDO) + pUndoManager->GetRedoActionComment(); + rSet.Put(SfxStringItem(SID_REDO, aTmp)); + } + } + else + { + rSet.DisableItem(SID_REDO); + } +} + +/** This method consists basically of three parts: + 1. Process the arguments of the SFX request. + 2. Use the model to create a new page or duplicate an existing one. + 3. Update the tab control and switch to the new page. +*/ +SdPage* ViewShell::CreateOrDuplicatePage ( + SfxRequest& rRequest, + PageKind ePageKind, + SdPage* pPage, + const sal_Int32 nInsertPosition) +{ + sal_uInt16 nSId = rRequest.GetSlot(); + SdDrawDocument* pDocument = GetDoc(); + SdrLayerAdmin& rLayerAdmin = pDocument->GetLayerAdmin(); + SdrLayerID aBckgrnd = rLayerAdmin.GetLayerID(sUNO_LayerName_background); + SdrLayerID aBckgrndObj = rLayerAdmin.GetLayerID(sUNO_LayerName_background_objects); + SdrLayerIDSet aVisibleLayers; + // Determine the page from which to copy some values, such as layers, + // size, master page, to the new page. This is usually the given page. + // When the given page is NULL then use the first page of the document. + SdPage* pTemplatePage = pPage; + if (pTemplatePage == nullptr) + pTemplatePage = pDocument->GetSdPage(0, ePageKind); + if (pTemplatePage != nullptr && pTemplatePage->TRG_HasMasterPage()) + aVisibleLayers = pTemplatePage->TRG_GetMasterPageVisibleLayers(); + else + aVisibleLayers.SetAll(); + + OUString aStandardPageName; + OUString aNotesPageName; + AutoLayout eStandardLayout (AUTOLAYOUT_NONE); + AutoLayout eNotesLayout (AUTOLAYOUT_NOTES); + bool bIsPageBack = aVisibleLayers.IsSet(aBckgrnd); + bool bIsPageObj = aVisibleLayers.IsSet(aBckgrndObj); + + // 1. Process the arguments. + const SfxItemSet* pArgs = rRequest.GetArgs(); + if (! pArgs) + { + // AutoLayouts must be ready + pDocument->StopWorkStartupDelay(); + + // Use the layouts of the previous page and notes page as template. + if (pTemplatePage != nullptr) + { + eStandardLayout = pTemplatePage->GetAutoLayout(); + if( eStandardLayout == AUTOLAYOUT_TITLE ) + eStandardLayout = AUTOLAYOUT_TITLE_CONTENT; + + SdPage* pNotesTemplatePage = static_cast<SdPage*>(pDocument->GetPage(pTemplatePage->GetPageNum()+1)); + if (pNotesTemplatePage != nullptr) + eNotesLayout = pNotesTemplatePage->GetAutoLayout(); + } + } + else if (pArgs->Count() == 1) + { + pDocument->StopWorkStartupDelay(); + const SfxUInt32Item* pLayout = rRequest.GetArg<SfxUInt32Item>(ID_VAL_WHATLAYOUT); + if( pLayout ) + { + if (ePageKind == PageKind::Notes) + { + eNotesLayout = static_cast<AutoLayout>(pLayout->GetValue ()); + } + else + { + eStandardLayout = static_cast<AutoLayout>(pLayout->GetValue ()); + } + } + } + else if (pArgs->Count() == 4) + { + // AutoLayouts must be ready + pDocument->StopWorkStartupDelay(); + + const SfxStringItem* pPageName = rRequest.GetArg<SfxStringItem>(ID_VAL_PAGENAME); + const SfxUInt32Item* pLayout = rRequest.GetArg<SfxUInt32Item>(ID_VAL_WHATLAYOUT); + const SfxBoolItem* pIsPageBack = rRequest.GetArg<SfxBoolItem>(ID_VAL_ISPAGEBACK); + const SfxBoolItem* pIsPageObj = rRequest.GetArg<SfxBoolItem>(ID_VAL_ISPAGEOBJ); + + if (CHECK_RANGE (AUTOLAYOUT_START, static_cast<AutoLayout>(pLayout->GetValue ()), AUTOLAYOUT_END)) + { + if (ePageKind == PageKind::Notes) + { + aNotesPageName = pPageName->GetValue (); + eNotesLayout = static_cast<AutoLayout>(pLayout->GetValue ()); + } + else + { + aStandardPageName = pPageName->GetValue (); + eStandardLayout = static_cast<AutoLayout>(pLayout->GetValue ()); + } + + bIsPageBack = pIsPageBack->GetValue (); + bIsPageObj = pIsPageObj->GetValue (); + } + else + { + Cancel(); + + if(HasCurrentFunction( SID_BEZIER_EDIT ) ) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_BAD_PROP_VALUE); +#endif + rRequest.Ignore (); + return nullptr; + } + } + else + { + Cancel(); + + if(HasCurrentFunction(SID_BEZIER_EDIT) ) + GetViewFrame()->GetDispatcher()->Execute(SID_OBJECT_SELECT, SfxCallMode::ASYNCHRON); +#if HAVE_FEATURE_SCRIPTING + StarBASIC::FatalError (ERRCODE_BASIC_WRONG_ARGS); +#endif + rRequest.Ignore (); + return nullptr; + } + + // 2. Create a new page or duplicate an existing one. + View* pDrView = GetView(); + const bool bUndo = pDrView && pDrView->IsUndoEnabled(); + if( bUndo && GetDoc()->GetDocumentType() == DocumentType::Draw) + pDrView->BegUndo(SdResId(STR_INSERT_PAGE_DRAW)); + else if (bUndo) + pDrView->BegUndo(SdResId(STR_INSERTPAGE)); + + + + sal_uInt16 nNewPageIndex = 0xffff; + switch (nSId) + { + case SID_INSERTPAGE: + case SID_INSERTPAGE_QUICK: + case SID_INSERT_MASTER_PAGE: + // There are three cases. a) pPage is not NULL: we use it as a + // template and create a new slide behind it. b) pPage is NULL + // but the document is not empty: we use the first slide/notes + // page as template, create a new slide after it and move it + // then to the head of the document. c) pPage is NULL and the + // document is empty: We use CreateFirstPages to create the + // first page of the document. + if (pPage == nullptr) + if (pTemplatePage == nullptr) + { + pDocument->CreateFirstPages(); + nNewPageIndex = 0; + } + else + { + // Create a new page with the first page as template and + // insert it after the first page. + nNewPageIndex = pDocument->CreatePage ( + pTemplatePage, + ePageKind, + aStandardPageName, + aNotesPageName, + eStandardLayout, + eNotesLayout, + bIsPageBack, + bIsPageObj, + nInsertPosition); + // Select exactly the new page. + sal_uInt16 nPageCount (pDocument->GetSdPageCount(ePageKind)); + for (sal_uInt16 i=0; i<nPageCount; i++) + { + pDocument->GetSdPage(i, PageKind::Standard)->SetSelected( + i == nNewPageIndex); + pDocument->GetSdPage(i, PageKind::Notes)->SetSelected( + i == nNewPageIndex); + } + // Move the selected page to the head of the document + pDocument->MovePages (sal_uInt16(-1)); + nNewPageIndex = 0; + } + else + nNewPageIndex = pDocument->CreatePage ( + pPage, + ePageKind, + aStandardPageName, + aNotesPageName, + eStandardLayout, + eNotesLayout, + bIsPageBack, + bIsPageObj, + nInsertPosition); + break; + + case SID_DUPLICATE_PAGE: + // Duplication makes no sense when pPage is NULL. + if (pPage != nullptr) + nNewPageIndex = pDocument->DuplicatePage ( + pPage, + ePageKind, + aStandardPageName, + aNotesPageName, + bIsPageBack, + bIsPageObj, + nInsertPosition); + break; + + default: + SAL_INFO("sd", "wrong slot id given to CreateOrDuplicatePage"); + // Try to handle another slot id gracefully. + } + SdPage* pNewPage = nullptr; + if(nNewPageIndex != 0xffff) + pNewPage = pDocument->GetSdPage(nNewPageIndex, PageKind::Standard); + + if( bUndo ) + { + if( pNewPage ) + { + pDrView->AddUndo(pDocument->GetSdrUndoFactory().CreateUndoNewPage(*pNewPage)); + pDrView->AddUndo(pDocument->GetSdrUndoFactory().CreateUndoNewPage(*pDocument->GetSdPage (nNewPageIndex, PageKind::Notes))); + } + + pDrView->EndUndo(); + } + + return pNewPage; +} + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/viewshel.cxx b/sd/source/ui/view/viewshel.cxx new file mode 100644 index 000000000..866b79461 --- /dev/null +++ b/sd/source/ui/view/viewshel.cxx @@ -0,0 +1,1634 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <memory> +#include <ViewShell.hxx> +#include <ViewShellImplementation.hxx> +#include <createtableobjectbar.hxx> + +#include <ViewShellBase.hxx> +#include <ShellFactory.hxx> +#include <DrawController.hxx> +#include <LayerTabBar.hxx> + +#include <sal/log.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/dispatch.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/scrbar.hxx> +#include <svl/eitem.hxx> +#include <svx/ruler.hxx> +#include <svx/svxids.hrc> +#include <svx/fmshell.hxx> +#include <WindowUpdater.hxx> +#include <sdxfer.hxx> + +#include <app.hrc> + +#include <OutlineView.hxx> +#include <DrawViewShell.hxx> +#include <DrawDocShell.hxx> +#include <slideshow.hxx> +#include <drawdoc.hxx> +#include <sdpage.hxx> +#include <zoomlist.hxx> +#include <FrameView.hxx> +#include <BezierObjectBar.hxx> +#include <TextObjectBar.hxx> +#include <GraphicObjectBar.hxx> +#include <MediaObjectBar.hxx> +#include <SlideSorter.hxx> +#include <SlideSorterViewShell.hxx> +#include <ViewShellManager.hxx> +#include <FormShellManager.hxx> +#include <svx/extrusionbar.hxx> +#include <svx/fontworkbar.hxx> +#include <svx/svdoutl.hxx> +#include <tools/svborder.hxx> +#include <comphelper/lok.hxx> + +#include <svl/slstitm.hxx> +#include <sfx2/request.hxx> +#include <SpellDialogChildWindow.hxx> +#include <controller/SlideSorterController.hxx> +#include <controller/SlsPageSelector.hxx> +#include <controller/SlsSelectionObserver.hxx> +#include <view/SlideSorterView.hxx> + +#include <basegfx/utils/zoomtools.hxx> + +#include <Window.hxx> +#include <fupoor.hxx> +#include <futext.hxx> + +#include <editeng/numitem.hxx> +#include <editeng/eeitem.hxx> +#include <editeng/editview.hxx> +#include <editeng/editeng.hxx> +#include <svl/itempool.hxx> +#include <svl/intitem.hxx> +#include <svl/poolitem.hxx> +#include <strings.hxx> +#include <sdmod.hxx> +#include <AccessibleDocumentViewBase.hxx> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::presentation; + +namespace { + +class ViewShellObjectBarFactory + : public ::sd::ShellFactory<SfxShell> +{ +public: + explicit ViewShellObjectBarFactory (::sd::ViewShell& rViewShell); + virtual SfxShell* CreateShell( ::sd::ShellId nId ) override; + virtual void ReleaseShell (SfxShell* pShell) override; +private: + ::sd::ViewShell& mrViewShell; +}; + +} // end of anonymous namespace + +namespace sd { + +bool ViewShell::IsPageFlipMode() const +{ + return dynamic_cast< const DrawViewShell *>( this ) != nullptr && mpContentWindow && + mpContentWindow->GetVisibleHeight() >= 1.0; +} + +SfxViewFrame* ViewShell::GetViewFrame() const +{ + const SfxViewShell* pViewShell = GetViewShell(); + if (pViewShell != nullptr) + { + return pViewShell->GetViewFrame(); + } + else + { + OSL_ASSERT (GetViewShell()!=nullptr); + return nullptr; + } +} + +/// declare SFX-Slotmap and standard interface + +ViewShell::ViewShell( vcl::Window* pParentWindow, ViewShellBase& rViewShellBase) +: SfxShell(&rViewShellBase) +, mpParentWindow(pParentWindow) +{ + construct(); +} + +ViewShell::~ViewShell() +{ + // Keep the content window from accessing in its destructor the + // WindowUpdater. + if (mpContentWindow) + mpContentWindow->SetViewShell(nullptr); + + mpZoomList.reset(); + + mpLayerTabBar.disposeAndClear(); + + if (mpImpl->mpSubShellFactory) + GetViewShellBase().GetViewShellManager()->RemoveSubShellFactory( + this,mpImpl->mpSubShellFactory); + + if (mpContentWindow) + { + SAL_INFO( + "sd.view", + "destroying mpContentWindow at " << mpContentWindow.get() + << " with parent " << mpContentWindow->GetParent()); + mpContentWindow.disposeAndClear(); + } + + mpScrollBarBox.disposeAndClear(); + mpVerticalRuler.disposeAndClear(); + mpHorizontalRuler.disposeAndClear(); + mpVerticalScrollBar.disposeAndClear(); + mpHorizontalScrollBar.disposeAndClear(); +} + +/** + * common initialization part of both constructors + */ +void ViewShell::construct() +{ + mbHasRulers = false; + mpActiveWindow = nullptr; + mpView = nullptr; + mpFrameView = nullptr; + mpZoomList = nullptr; + mbStartShowWithDialog = false; + mnPrintedHandoutPageNum = 1; + mnPrintedHandoutPageCount = 0; + mpWindowUpdater.reset( new ::sd::WindowUpdater() ); + mpImpl.reset(new Implementation(*this)); + meShellType = ST_NONE; + + OSL_ASSERT (GetViewShell()!=nullptr); + + if (IsMainViewShell()) + GetDocSh()->Connect (this); + + mpZoomList.reset( new ZoomList( this ) ); + + mpContentWindow.reset(VclPtr< ::sd::Window >::Create(GetParentWindow())); + SetActiveWindow (mpContentWindow.get()); + + GetParentWindow()->SetBackground (Wallpaper()); + mpContentWindow->SetBackground (Wallpaper()); + mpContentWindow->SetCenterAllowed(true); + mpContentWindow->SetViewShell(this); + mpContentWindow->SetPosSizePixel( + GetParentWindow()->GetPosPixel(),GetParentWindow()->GetSizePixel()); + + if ( ! GetDocSh()->IsPreview()) + { + // Create scroll bars and the filler between the scroll bars. + mpHorizontalScrollBar.reset (VclPtr<ScrollBar>::Create(GetParentWindow(), WinBits(WB_HSCROLL | WB_DRAG))); + mpHorizontalScrollBar->EnableRTL (false); + mpHorizontalScrollBar->SetRange(Range(0, 32000)); + mpHorizontalScrollBar->SetScrollHdl(LINK(this, ViewShell, HScrollHdl)); + + mpVerticalScrollBar.reset (VclPtr<ScrollBar>::Create(GetParentWindow(), WinBits(WB_VSCROLL | WB_DRAG))); + mpVerticalScrollBar->SetRange(Range(0, 32000)); + mpVerticalScrollBar->SetScrollHdl(LINK(this, ViewShell, VScrollHdl)); + + mpScrollBarBox.reset(VclPtr<ScrollBarBox>::Create(GetParentWindow(), WB_SIZEABLE)); + } + + SetName ("ViewShell"); + + GetDoc()->StartOnlineSpelling(false); + + mpWindowUpdater->SetDocument (GetDoc()); + + // Re-initialize the spell dialog. + ::sd::SpellDialogChildWindow* pSpellDialog = + static_cast< ::sd::SpellDialogChildWindow*> ( + GetViewFrame()->GetChildWindow ( + ::sd::SpellDialogChildWindow::GetChildWindowId())); + if (pSpellDialog != nullptr) + pSpellDialog->InvalidateSpellDialog(); + + // Register the sub shell factory. + mpImpl->mpSubShellFactory = std::make_shared<ViewShellObjectBarFactory>(*this); + GetViewShellBase().GetViewShellManager()->AddSubShellFactory(this,mpImpl->mpSubShellFactory); +} + +void ViewShell::doShow() +{ + mpContentWindow->Show(); + static_cast< vcl::Window*>(mpContentWindow.get())->Resize(); + SAL_INFO( + "sd.view", + "content window has size " << mpContentWindow->GetSizePixel().Width() + << " " << mpContentWindow->GetSizePixel().Height()); + + if ( ! GetDocSh()->IsPreview()) + { + // Show scroll bars + mpHorizontalScrollBar->Show(); + + mpVerticalScrollBar->Show(); + maScrBarWH = Size( + mpVerticalScrollBar->GetSizePixel().Width(), + mpHorizontalScrollBar->GetSizePixel().Height()); + + mpScrollBarBox->Show(); + } + + GetParentWindow()->Show(); +} + +void ViewShell::Init (bool bIsMainViewShell) +{ + mpImpl->mbIsInitialized = true; + SetIsMainViewShell(bIsMainViewShell); + if (bIsMainViewShell) + SetActiveWindow (mpContentWindow.get()); +} + +void ViewShell::Exit() +{ + sd::View* pView = GetView(); + if (pView!=nullptr && pView->IsTextEdit()) + { + pView->SdrEndTextEdit(); + pView->UnmarkAll(); + } + + Deactivate (true); + + if (IsMainViewShell()) + GetDocSh()->Disconnect(this); + + SetIsMainViewShell(false); +} + +/** + * set focus to working window + */ +void ViewShell::Activate(bool bIsMDIActivate) +{ + // Do not forward to SfxShell::Activate() + + /* According to MI, nobody is allowed to call GrabFocus, who does not + exactly know from which window the focus is grabbed. Since Activate() + is sent sometimes asynchronous, it can happen, that the wrong window + gets the focus. */ + + if (mpHorizontalRuler) + mpHorizontalRuler->SetActive(); + if (mpVerticalRuler) + mpVerticalRuler->SetActive(); + + if (bIsMDIActivate) + { + // thus, the Navigator will also get a current status + SfxBoolItem aItem( SID_NAVIGATOR_INIT, true ); + if (GetDispatcher() != nullptr) + GetDispatcher()->ExecuteList( + SID_NAVIGATOR_INIT, + SfxCallMode::ASYNCHRON | SfxCallMode::RECORD, + { &aItem }); + + SfxViewShell* pViewShell = GetViewShell(); + OSL_ASSERT (pViewShell!=nullptr); + SfxBindings& rBindings = pViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate( SID_3D_STATE, true ); + + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if (xSlideShow.is() && xSlideShow->isRunning()) + { + bool bSuccess = xSlideShow->activate(GetViewShellBase()); + assert(bSuccess && "can only return false with a PresentationViewShell"); (void)bSuccess; + } + + if(HasCurrentFunction()) + GetCurrentFunction()->Activate(); + + if(!GetDocSh()->IsUIActive()) + UpdatePreview( GetActualPage() ); + } + + ReadFrameViewData( mpFrameView ); + + if (IsMainViewShell()) + GetDocSh()->Connect(this); +} + +void ViewShell::UIActivating( SfxInPlaceClient* ) +{ + OSL_ASSERT (GetViewShell()!=nullptr); + GetViewShellBase().GetToolBarManager()->ToolBarsDestroyed(); +} + +void ViewShell::UIDeactivated( SfxInPlaceClient* ) +{ + OSL_ASSERT (GetViewShell()!=nullptr); + GetViewShellBase().GetToolBarManager()->ToolBarsDestroyed(); + if ( GetDrawView() ) + GetViewShellBase().GetToolBarManager()->SelectionHasChanged(*this, *GetDrawView()); +} + +void ViewShell::Deactivate(bool bIsMDIActivate) +{ + // remove view from a still active drag'n'drop session + SdTransferable* pDragTransferable = SD_MOD()->pTransferDrag; + + if (IsMainViewShell()) + GetDocSh()->Disconnect(this); + + if( pDragTransferable ) + pDragTransferable->SetView( nullptr ); + + OSL_ASSERT (GetViewShell()!=nullptr); + + // remember view attributes of FrameView + WriteFrameViewData(); + + if (bIsMDIActivate) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if(xSlideShow.is() && xSlideShow->isRunning() ) + xSlideShow->deactivate(); + + if(HasCurrentFunction()) + GetCurrentFunction()->Deactivate(); + } + + if (mpHorizontalRuler) + mpHorizontalRuler->SetActive(false); + if (mpVerticalRuler) + mpVerticalRuler->SetActive(false); + + SfxShell::Deactivate(bIsMDIActivate); +} + +void ViewShell::Shutdown() +{ + Exit (); +} + +bool ViewShell::KeyInput(const KeyEvent& rKEvt, ::sd::Window* pWin) +{ + bool bReturn(false); + + if(pWin) + SetActiveWindow(pWin); + + // give key input first to SfxViewShell to give CTRL+Key + // (e.g. CTRL+SHIFT+'+', to front) priority. + OSL_ASSERT(GetViewShell() != nullptr); + bReturn = GetViewShell()->KeyInput(rKEvt); + + const size_t OriCount = GetView()->GetMarkedObjectList().GetMarkCount(); + if(!bReturn) + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if(xSlideShow.is() && xSlideShow->isRunning()) + { + bReturn = xSlideShow->keyInput(rKEvt); + } + else + { + bool bConsumed = false; + if( GetView() ) + bConsumed = GetView()->getSmartTags().KeyInput(rKEvt); + + if( !bConsumed ) + { + rtl::Reference< sdr::SelectionController > xSelectionController( GetView()->getSelectionController() ); + if( !xSelectionController.is() || !xSelectionController->onKeyInput( rKEvt, pWin ) ) + { + if(HasCurrentFunction()) + bReturn = GetCurrentFunction()->KeyInput(rKEvt); + } + else + { + bReturn = true; + if (HasCurrentFunction()) + { + FuText* pTextFunction = dynamic_cast<FuText*>(GetCurrentFunction().get()); + if(pTextFunction != nullptr) + pTextFunction->InvalidateBindings(); + } + } + } + } + } + const size_t EndCount = GetView()->GetMarkedObjectList().GetMarkCount(); + // Here, oriCount or endCount must have one value=0, another value > 0, then to switch focus between Document and shape objects + if(bReturn && (OriCount + EndCount > 0) && (OriCount * EndCount == 0)) + SwitchActiveViewFireFocus(); + + if(!bReturn && GetActiveWindow()) + { + vcl::KeyCode aKeyCode = rKEvt.GetKeyCode(); + + if (aKeyCode.IsMod1() && aKeyCode.IsShift() + && aKeyCode.GetCode() == KEY_R) + { + InvalidateWindows(); + bReturn = true; + } + } + + return bReturn; +} + +void ViewShell::MouseButtonDown(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + // We have to lock tool bar updates while the mouse button is pressed in + // order to prevent the shape under the mouse to be moved (this happens + // when the number of docked tool bars changes as result of a changed + // selection; this changes the window size and thus the mouse position + // in model coordinates: with respect to model coordinates the mouse + // moves.) + OSL_ASSERT(mpImpl->mpUpdateLockForMouse.expired()); + mpImpl->mpUpdateLockForMouse = ViewShell::Implementation::ToolBarManagerLock::Create( + GetViewShellBase().GetToolBarManager()); + + if ( pWin && !pWin->HasFocus() ) + { + pWin->GrabFocus(); + SetActiveWindow(pWin); + } + + // insert MouseEvent into E3dView + if (GetView() != nullptr) + GetView()->SetMouseEvent(rMEvt); + + bool bConsumed = false; + if( GetView() ) + bConsumed = GetView()->getSmartTags().MouseButtonDown( rMEvt ); + + if( bConsumed ) + return; + + rtl::Reference< sdr::SelectionController > xSelectionController( GetView()->getSelectionController() ); + if( !xSelectionController.is() || !xSelectionController->onMouseButtonDown( rMEvt, pWin ) ) + { + if(HasCurrentFunction()) + GetCurrentFunction()->MouseButtonDown(rMEvt); + } + else + { + if (HasCurrentFunction()) + { + FuText* pTextFunction = dynamic_cast<FuText*>(GetCurrentFunction().get()); + if (pTextFunction != nullptr) + pTextFunction->InvalidateBindings(); + } + } +} + +void ViewShell::SetCursorMm100Position(const Point& rPosition, bool bPoint, bool bClearMark) +{ + if (SdrView* pSdrView = GetView()) + { + rtl::Reference<sdr::SelectionController> xSelectionController(GetView()->getSelectionController()); + if (!xSelectionController.is() || !xSelectionController->setCursorLogicPosition(rPosition, bPoint)) + { + if (pSdrView->GetTextEditObject()) + { + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + rEditView.SetCursorLogicPosition(rPosition, bPoint, bClearMark); + } + } + } +} + +uno::Reference<datatransfer::XTransferable> ViewShell::GetSelectionTransferrable() const +{ + SdrView* pSdrView = GetView(); + if (!pSdrView) + return uno::Reference<datatransfer::XTransferable>(); + + if (!pSdrView->GetTextEditObject()) + return uno::Reference<datatransfer::XTransferable>(); + + EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView(); + return rEditView.GetEditEngine()->CreateTransferable(rEditView.GetSelection()); +} + +void ViewShell::SetGraphicMm100Position(bool bStart, const Point& rPosition) +{ + if (bStart) + { + MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + MouseButtonDown(aClickEvent, mpActiveWindow); + MouseEvent aMoveEvent(Point(rPosition.getX(), rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + MouseMove(aMoveEvent, mpActiveWindow); + } + else + { + MouseEvent aMoveEvent(Point(rPosition.getX(), rPosition.getY()), 0, MouseEventModifiers::SIMPLEMOVE, MOUSE_LEFT); + MouseMove(aMoveEvent, mpActiveWindow); + MouseEvent aClickEvent(rPosition, 1, MouseEventModifiers::SIMPLECLICK, MOUSE_LEFT); + MouseButtonUp(aClickEvent, mpActiveWindow); + } +} + +void ViewShell::MouseMove(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + if (rMEvt.IsLeaveWindow()) + { + if ( ! mpImpl->mpUpdateLockForMouse.expired()) + { + std::shared_ptr<ViewShell::Implementation::ToolBarManagerLock> pLock( + mpImpl->mpUpdateLockForMouse); + if (pLock != nullptr) + pLock->Release(); + } + } + + if ( pWin ) + { + SetActiveWindow(pWin); + } + + // insert MouseEvent into E3dView + if (GetView() != nullptr) + GetView()->SetMouseEvent(rMEvt); + + if(HasCurrentFunction()) + { + rtl::Reference< sdr::SelectionController > xSelectionController( GetView()->getSelectionController() ); + if( !xSelectionController.is() || !xSelectionController->onMouseMove( rMEvt, pWin ) ) + { + if(HasCurrentFunction()) + GetCurrentFunction()->MouseMove(rMEvt); + } + } +} + +void ViewShell::MouseButtonUp(const MouseEvent& rMEvt, ::sd::Window* pWin) +{ + if ( pWin ) + SetActiveWindow(pWin); + + // insert MouseEvent into E3dView + if (GetView() != nullptr) + GetView()->SetMouseEvent(rMEvt); + + if( HasCurrentFunction()) + { + rtl::Reference< sdr::SelectionController > xSelectionController( GetView()->getSelectionController() ); + if( !xSelectionController.is() || !xSelectionController->onMouseButtonUp( rMEvt, pWin ) ) + { + if(HasCurrentFunction()) + GetCurrentFunction()->MouseButtonUp(rMEvt); + } + else + { + if (HasCurrentFunction()) + { + FuText* pTextFunction = dynamic_cast<FuText*>(GetCurrentFunction().get()); + if (pTextFunction != nullptr) + pTextFunction->InvalidateBindings(); + } + } + } + + if ( ! mpImpl->mpUpdateLockForMouse.expired()) + { + std::shared_ptr<ViewShell::Implementation::ToolBarManagerLock> pLock( + mpImpl->mpUpdateLockForMouse); + if (pLock != nullptr) + pLock->Release(); + } +} + +void ViewShell::Command(const CommandEvent& rCEvt, ::sd::Window* pWin) +{ + bool bDone = HandleScrollCommand (rCEvt, pWin); + + if( bDone ) + return; + + if( rCEvt.GetCommand() == CommandEventId::InputLanguageChange ) + { + //#i42732# update state of fontname if input language changes + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_CHAR_FONT ); + GetViewFrame()->GetBindings().Invalidate( SID_ATTR_CHAR_FONTHEIGHT ); + } + else + { + bool bConsumed = false; + if( GetView() ) + bConsumed = GetView()->getSmartTags().Command(rCEvt); + + if( !bConsumed && HasCurrentFunction()) + GetCurrentFunction()->Command(rCEvt); + } +} + +bool ViewShell::Notify(NotifyEvent const & rNEvt, ::sd::Window* pWin) +{ + // handle scroll commands when they arrived at child windows + bool bRet = false; + if( rNEvt.GetType() == MouseNotifyEvent::COMMAND ) + { + // note: dynamic_cast is not possible as GetData() returns a void* + CommandEvent* pCmdEvent = static_cast< CommandEvent* >(rNEvt.GetData()); + bRet = HandleScrollCommand(*pCmdEvent, pWin); + } + return bRet; +} + +bool ViewShell::HandleScrollCommand(const CommandEvent& rCEvt, ::sd::Window* pWin) +{ + bool bDone = false; + + switch( rCEvt.GetCommand() ) + { + case CommandEventId::Swipe: + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if (xSlideShow.is()) + { + const CommandSwipeData* pSwipeData = rCEvt.GetSwipeData(); + bDone = xSlideShow->swipe(*pSwipeData); + } + } + break; + case CommandEventId::LongPress: + { + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + if (xSlideShow.is()) + { + const CommandLongPressData* pLongPressData = rCEvt.GetLongPressData(); + bDone = xSlideShow->longpress(*pLongPressData); + } + } + break; + + case CommandEventId::Wheel: + { + Reference< XSlideShowController > xSlideShowController( SlideShow::GetSlideShowController(GetViewShellBase() ) ); + if( xSlideShowController.is() ) + { + // We ignore zooming with control+mouse wheel. + const CommandWheelData* pData = rCEvt.GetWheelData(); + if( pData && !pData->GetModifier() && ( pData->GetMode() == CommandWheelMode::SCROLL ) && !pData->IsHorz() ) + { + ::tools::Long nDelta = pData->GetDelta(); + if( nDelta > 0 ) + xSlideShowController->gotoPreviousSlide(); + else if( nDelta < 0 ) + xSlideShowController->gotoNextEffect(); + } + break; + } + } + [[fallthrough]]; + case CommandEventId::StartAutoScroll: + case CommandEventId::AutoScroll: + { + const CommandWheelData* pData = rCEvt.GetWheelData(); + + if (pData != nullptr) + { + if (pData->IsMod1()) + { + if( !GetDocSh()->IsUIActive() ) + { + const ::tools::Long nOldZoom = GetActiveWindow()->GetZoom(); + ::tools::Long nNewZoom; + Point aOldMousePos = GetActiveWindow()->PixelToLogic(rCEvt.GetMousePosPixel()); + + if( pData->GetDelta() < 0 ) + nNewZoom = std::max<::tools::Long>( pWin->GetMinZoom(), basegfx::zoomtools::zoomOut( nOldZoom )); + else + nNewZoom = std::min<::tools::Long>( pWin->GetMaxZoom(), basegfx::zoomtools::zoomIn( nOldZoom )); + + SetZoom( nNewZoom ); + // Keep mouse at same doc point before zoom + Point aNewMousePos = GetActiveWindow()->PixelToLogic(rCEvt.GetMousePosPixel()); + SetWinViewPos(GetWinViewPos() - (aNewMousePos - aOldMousePos)); + + Invalidate( SID_ATTR_ZOOM ); + Invalidate( SID_ATTR_ZOOMSLIDER ); + + bDone = true; + } + } + else + { + if( mpContentWindow.get() == pWin ) + { + sal_uLong nScrollLines = pData->GetScrollLines(); + if(IsPageFlipMode()) + nScrollLines = COMMAND_WHEEL_PAGESCROLL; + CommandWheelData aWheelData( pData->GetDelta(),pData->GetNotchDelta(), + nScrollLines,pData->GetMode(),pData->GetModifier(),pData->IsHorz() ); + CommandEvent aReWrite( rCEvt.GetMousePosPixel(),rCEvt.GetCommand(), + rCEvt.IsMouseEvent(),static_cast<const void *>(&aWheelData) ); + bDone = pWin->HandleScrollCommand( aReWrite, + mpHorizontalScrollBar.get(), + mpVerticalScrollBar.get()); + } + } + } + } + break; + + default: + break; + } + + return bDone; +} + +void ViewShell::SetupRulers() +{ + if(!mbHasRulers || !mpContentWindow || SlideShow::IsRunning(GetViewShellBase())) + return; + + ::tools::Long nHRulerOfs = 0; + + if ( !mpVerticalRuler ) + { + mpVerticalRuler.reset(CreateVRuler(GetActiveWindow())); + if ( mpVerticalRuler ) + { + nHRulerOfs = mpVerticalRuler->GetSizePixel().Width(); + mpVerticalRuler->SetActive(); + mpVerticalRuler->Show(); + } + } + if ( !mpHorizontalRuler ) + { + mpHorizontalRuler.reset(CreateHRuler(GetActiveWindow())); + if ( mpHorizontalRuler ) + { + mpHorizontalRuler->SetWinPos(nHRulerOfs); + mpHorizontalRuler->SetActive(); + mpHorizontalRuler->Show(); + } + } +} + +const SvxNumBulletItem* ViewShell::GetNumBulletItem(SfxItemSet& aNewAttr, TypedWhichId<SvxNumBulletItem>& nNumItemId) +{ + const SvxNumBulletItem* pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + if(pTmpItem) + return pTmpItem; + + nNumItemId = aNewAttr.GetPool()->GetWhich(SID_ATTR_NUMBERING_RULE); + pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + if(pTmpItem) + return pTmpItem; + + bool bOutliner = false; + bool bTitle = false; + + if( mpView ) + { + const SdrMarkList& rMarkList = mpView->GetMarkedObjectList(); + const size_t nCount = rMarkList.GetMarkCount(); + + for(size_t nNum = 0; nNum < nCount; ++nNum) + { + SdrObject* pObj = rMarkList.GetMark(nNum)->GetMarkedSdrObj(); + if( pObj->GetObjInventor() == SdrInventor::Default ) + { + switch(pObj->GetObjIdentifier()) + { + case SdrObjKind::TitleText: + bTitle = true; + break; + case SdrObjKind::OutlineText: + bOutliner = true; + break; + default: + break; + } + } + } + } + + const SvxNumBulletItem *pItem = nullptr; + if(bOutliner) + { + SfxStyleSheetBasePool* pSSPool = mpView->GetDocSh()->GetStyleSheetPool(); + SfxStyleSheetBase* pFirstStyleSheet = pSSPool->Find( STR_LAYOUT_OUTLINE + " 1", SfxStyleFamily::Pseudo); + if( pFirstStyleSheet ) + pItem = pFirstStyleSheet->GetItemSet().GetItemIfSet(EE_PARA_NUMBULLET, false); + } + + if( pItem == nullptr ) + pItem = aNewAttr.GetPool()->GetSecondaryPool()->GetPoolDefaultItem(EE_PARA_NUMBULLET); + + aNewAttr.Put(pItem->CloneSetWhich(EE_PARA_NUMBULLET)); + + if(bTitle && aNewAttr.GetItemState(EE_PARA_NUMBULLET) == SfxItemState::SET ) + { + const SvxNumBulletItem* pBulletItem = aNewAttr.GetItem(EE_PARA_NUMBULLET); + const SvxNumRule& rRule = pBulletItem->GetNumRule(); + SvxNumRule aNewRule( rRule ); + aNewRule.SetFeatureFlag( SvxNumRuleFlags::NO_NUMBERS ); + + SvxNumBulletItem aNewItem( std::move(aNewRule), EE_PARA_NUMBULLET ); + aNewAttr.Put(aNewItem); + } + + pTmpItem = aNewAttr.GetItemIfSet(nNumItemId, false); + + return pTmpItem; +} + +void ViewShell::Resize() +{ + SetupRulers (); + + if (mpParentWindow == nullptr) + return; + + // Make sure that the new size is not degenerate. + const Size aSize (mpParentWindow->GetSizePixel()); + if (aSize.IsEmpty()) + return; + + // Remember the new position and size. + maViewPos = Point(0,0); + maViewSize = aSize; + + // Rearrange the UI elements to take care of the new position and size. + ArrangeGUIElements (); + // end of included AdjustPosSizePixel. + + ::sd::View* pView = GetView(); + + if (pView) + pView->VisAreaChanged(GetActiveWindow()->GetOutDev()); +} + +SvBorder ViewShell::GetBorder() +{ + SvBorder aBorder; + + // Horizontal scrollbar. + if (mpHorizontalScrollBar + && mpHorizontalScrollBar->IsVisible()) + { + aBorder.Bottom() = maScrBarWH.Height(); + } + + // Vertical scrollbar. + if (mpVerticalScrollBar + && mpVerticalScrollBar->IsVisible()) + { + aBorder.Right() = maScrBarWH.Width(); + } + + // Place horizontal ruler below tab bar. + if (mbHasRulers && mpContentWindow) + { + SetupRulers(); + if (mpHorizontalRuler) + aBorder.Top() = mpHorizontalRuler->GetSizePixel().Height(); + if (mpVerticalRuler) + aBorder.Left() = mpVerticalRuler->GetSizePixel().Width(); + } + + return aBorder; +} + +void ViewShell::ArrangeGUIElements() +{ + if (mpImpl->mbArrangeActive) + return; + if (maViewSize.IsEmpty()) + return; + mpImpl->mbArrangeActive = true; + + // Calculate border for in-place editing. + ::tools::Long nLeft = maViewPos.X(); + ::tools::Long nTop = maViewPos.Y(); + ::tools::Long nRight = maViewPos.X() + maViewSize.Width(); + ::tools::Long nBottom = maViewPos.Y() + maViewSize.Height(); + + // Horizontal scrollbar. + if (mpHorizontalScrollBar + && mpHorizontalScrollBar->IsVisible()) + { + nBottom -= maScrBarWH.Height(); + if (mpLayerTabBar && mpLayerTabBar->IsVisible()) + nBottom -= mpLayerTabBar->GetSizePixel().Height(); + mpHorizontalScrollBar->SetPosSizePixel ( + Point(nLeft, nBottom), + Size(nRight - nLeft - maScrBarWH.Width(), maScrBarWH.Height())); + } + + // Vertical scrollbar. + if (mpVerticalScrollBar + && mpVerticalScrollBar->IsVisible()) + { + nRight -= maScrBarWH.Width(); + mpVerticalScrollBar->SetPosSizePixel ( + Point(nRight,nTop), + Size (maScrBarWH.Width(), nBottom-nTop)); + } + + // Filler in the lower right corner. + if (mpScrollBarBox) + { + if (mpHorizontalScrollBar + && mpHorizontalScrollBar->IsVisible() + && mpVerticalScrollBar + && mpVerticalScrollBar->IsVisible()) + { + mpScrollBarBox->Show(); + mpScrollBarBox->SetPosSizePixel(Point(nRight, nBottom), maScrBarWH); + } + else + mpScrollBarBox->Hide(); + } + + // Place horizontal ruler below tab bar. + if (mbHasRulers && mpContentWindow) + { + if (mpHorizontalRuler) + { + Size aRulerSize = mpHorizontalRuler->GetSizePixel(); + aRulerSize.setWidth( nRight - nLeft ); + mpHorizontalRuler->SetPosSizePixel ( + Point(nLeft,nTop), aRulerSize); + if (mpVerticalRuler) + mpHorizontalRuler->SetBorderPos( + mpVerticalRuler->GetSizePixel().Width()-1); + nTop += aRulerSize.Height(); + } + if (mpVerticalRuler) + { + Size aRulerSize = mpVerticalRuler->GetSizePixel(); + aRulerSize.setHeight( nBottom - nTop ); + mpVerticalRuler->SetPosSizePixel ( + Point (nLeft,nTop), aRulerSize); + nLeft += aRulerSize.Width(); + } + } + + rtl::Reference< SlideShow > xSlideShow( SlideShow::GetSlideShow( GetViewShellBase() ) ); + + // The size of the window of the center pane is set differently from + // that of the windows in the docking windows. + bool bSlideShowActive = (xSlideShow.is() && xSlideShow->isRunning()) && !xSlideShow->isFullScreen() && xSlideShow->getAnimationMode() == ANIMATIONMODE_SHOW; + if ( !bSlideShowActive) + { + OSL_ASSERT (GetViewShell()!=nullptr); + + if (mpContentWindow) + mpContentWindow->SetPosSizePixel( + Point(nLeft,nTop), + Size(nRight-nLeft,nBottom-nTop)); + } + + // Windows in the center and rulers at the left and top side. + maAllWindowRectangle = ::tools::Rectangle( + maViewPos, + Size(maViewSize.Width()-maScrBarWH.Width(), + maViewSize.Height()-maScrBarWH.Height())); + + if (mpContentWindow) + mpContentWindow->UpdateMapOrigin(); + + UpdateScrollBars(); + + mpImpl->mbArrangeActive = false; +} + +void ViewShell::SetUIUnit(FieldUnit eUnit) +{ + // Set unit at horizontal and vertical rulers. + if (mpHorizontalRuler) + mpHorizontalRuler->SetUnit(eUnit); + + if (mpVerticalRuler) + mpVerticalRuler->SetUnit(eUnit); +} + +/** + * set DefTab at horizontal rulers + */ +void ViewShell::SetDefTabHRuler( sal_uInt16 nDefTab ) +{ + if (mpHorizontalRuler) + mpHorizontalRuler->SetDefTabDist( nDefTab ); +} + +/** Tell the FmFormShell that the view shell is closing. Give it the + opportunity to prevent that. +*/ +bool ViewShell::PrepareClose (bool bUI) +{ + bool bResult = true; + + FmFormShell* pFormShell = GetViewShellBase().GetFormShellManager()->GetFormShell(); + if (pFormShell != nullptr) + bResult = pFormShell->PrepareClose (bUI); + + return bResult; +} + +void ViewShell::UpdatePreview (SdPage*) +{ + // Do nothing. After the actual preview has been removed, + // OutlineViewShell::UpdatePreview() is the place where something + // useful is still done. +} + +SfxUndoManager* ViewShell::ImpGetUndoManager() const +{ + const ViewShell* pMainViewShell = GetViewShellBase().GetMainViewShell().get(); + + if( pMainViewShell == nullptr ) + pMainViewShell = this; + + ::sd::View* pView = pMainViewShell->GetView(); + + // check for text edit our outline view + if( pView ) + { + if( pMainViewShell->GetShellType() == ViewShell::ST_OUTLINE ) + { + OutlineView* pOlView = dynamic_cast< OutlineView* >( pView ); + if( pOlView ) + { + ::Outliner& rOutl = pOlView->GetOutliner(); + return &rOutl.GetUndoManager(); + } + } + else if( pView->IsTextEdit() ) + { + SdrOutliner* pOL = pView->GetTextEditOutliner(); + if( pOL ) + return &pOL->GetUndoManager(); + } + } + + if( GetDocSh() ) + return GetDocSh()->GetUndoManager(); + + return nullptr; +} + +void ViewShell::ImpGetUndoStrings(SfxItemSet &rSet) const +{ + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + if(!pUndoManager) + return; + + sal_uInt16 nCount(pUndoManager->GetUndoActionCount()); + if(nCount) + { + // prepare list + std::vector<OUString> aStringList; + aStringList.reserve(nCount); + for (sal_uInt16 a = 0; a < nCount; ++a) + { + // generate one String in list per undo step + aStringList.push_back( pUndoManager->GetUndoActionComment(a) ); + } + + // set item + rSet.Put(SfxStringListItem(SID_GETUNDOSTRINGS, &aStringList)); + } + else + { + rSet.DisableItem(SID_GETUNDOSTRINGS); + } +} + +void ViewShell::ImpGetRedoStrings(SfxItemSet &rSet) const +{ + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + if(!pUndoManager) + return; + + sal_uInt16 nCount(pUndoManager->GetRedoActionCount()); + if(nCount) + { + // prepare list + ::std::vector< OUString > aStringList; + aStringList.reserve(nCount); + for(sal_uInt16 a = 0; a < nCount; a++) + // generate one String in list per undo step + aStringList.push_back( pUndoManager->GetRedoActionComment(a) ); + + // set item + rSet.Put(SfxStringListItem(SID_GETREDOSTRINGS, &aStringList)); + } + else + { + rSet.DisableItem(SID_GETREDOSTRINGS); + } +} + +namespace { + +class KeepSlideSorterInSyncWithPageChanges +{ + sd::slidesorter::view::SlideSorterView::DrawLock m_aDrawLock; + sd::slidesorter::controller::SlideSorterController::ModelChangeLock m_aModelLock; + sd::slidesorter::controller::PageSelector::UpdateLock m_aUpdateLock; + sd::slidesorter::controller::SelectionObserver::Context m_aContext; + +public: + explicit KeepSlideSorterInSyncWithPageChanges(sd::slidesorter::SlideSorter const & rSlideSorter) + : m_aDrawLock(rSlideSorter) + , m_aModelLock(rSlideSorter.GetController()) + , m_aUpdateLock(rSlideSorter) + , m_aContext(rSlideSorter) + { + } +}; + +} + +void ViewShell::ImpSidUndo(SfxRequest& rReq) +{ + //The xWatcher keeps the SlideSorter selection in sync + //with the page insertions/deletions that Undo may introduce + std::unique_ptr<KeepSlideSorterInSyncWithPageChanges, o3tl::default_delete<KeepSlideSorterInSyncWithPageChanges>> xWatcher; + slidesorter::SlideSorterViewShell* pSlideSorterViewShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pSlideSorterViewShell) + xWatcher.reset(new KeepSlideSorterInSyncWithPageChanges(pSlideSorterViewShell->GetSlideSorter())); + + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + sal_uInt16 nNumber(1); + const SfxItemSet* pReqArgs = rReq.GetArgs(); + bool bRepair = false; + + if(pReqArgs) + { + const SfxUInt16Item* pUIntItem = static_cast<const SfxUInt16Item*>(&pReqArgs->Get(SID_UNDO)); + nNumber = pUIntItem->GetValue(); + + // Repair mode: allow undo/redo of all undo actions, even if access would + // be limited based on the view shell ID. + if (const SfxBoolItem* pRepairItem = pReqArgs->GetItemIfSet(SID_REPAIRPACKAGE, false)) + bRepair = pRepairItem->GetValue(); + } + + if(nNumber && pUndoManager) + { + sal_uInt16 nCount(pUndoManager->GetUndoActionCount()); + if(nCount >= nNumber) + { + if (comphelper::LibreOfficeKit::isActive() && !bRepair) + { + // If another view created the first undo action, prevent redoing it from this view. + const SfxUndoAction* pAction = pUndoManager->GetUndoAction(); + if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId()) + { + rReq.SetReturnValue(SfxUInt32Item(SID_UNDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + return; + } + } + + try + { + // when UndoStack is cleared by ModifyPageUndoAction + // the nCount may have changed, so test GetUndoActionCount() + while(nNumber-- && pUndoManager->GetUndoActionCount()) + pUndoManager->Undo(); + } + catch( const Exception& ) + { + // no need to handle. By definition, the UndoManager handled this by clearing the + // Undo/Redo stacks + } + } + + // refresh rulers, maybe UNDO was move of TAB marker in ruler + if (mbHasRulers) + Invalidate(SID_ATTR_TABSTOP); + } + + // This one is corresponding to the default handling + // of SID_UNDO in sfx2 + GetViewFrame()->GetBindings().InvalidateAll(false); + + rReq.Done(); +} + +void ViewShell::ImpSidRedo(SfxRequest& rReq) +{ + //The xWatcher keeps the SlideSorter selection in sync + //with the page insertions/deletions that Undo may introduce + std::unique_ptr<KeepSlideSorterInSyncWithPageChanges, o3tl::default_delete<KeepSlideSorterInSyncWithPageChanges>> xWatcher; + slidesorter::SlideSorterViewShell* pSlideSorterViewShell + = slidesorter::SlideSorterViewShell::GetSlideSorter(GetViewShellBase()); + if (pSlideSorterViewShell) + xWatcher.reset(new KeepSlideSorterInSyncWithPageChanges(pSlideSorterViewShell->GetSlideSorter())); + + SfxUndoManager* pUndoManager = ImpGetUndoManager(); + sal_uInt16 nNumber(1); + const SfxItemSet* pReqArgs = rReq.GetArgs(); + bool bRepair = false; + + if(pReqArgs) + { + const SfxUInt16Item* pUIntItem = static_cast<const SfxUInt16Item*>(&pReqArgs->Get(SID_REDO)); + nNumber = pUIntItem->GetValue(); + // Repair mode: allow undo/redo of all undo actions, even if access would + // be limited based on the view shell ID. + if (const SfxBoolItem* pRepairItem = pReqArgs->GetItemIfSet(SID_REPAIRPACKAGE, false)) + bRepair = pRepairItem->GetValue(); + } + + if(nNumber && pUndoManager) + { + sal_uInt16 nCount(pUndoManager->GetRedoActionCount()); + if(nCount >= nNumber) + { + if (comphelper::LibreOfficeKit::isActive() && !bRepair) + { + // If another view created the first undo action, prevent redoing it from this view. + const SfxUndoAction* pAction = pUndoManager->GetRedoAction(); + if (pAction->GetViewShellId() != GetViewShellBase().GetViewShellId()) + { + rReq.SetReturnValue(SfxUInt32Item(SID_REDO, static_cast<sal_uInt32>(SID_REPAIRPACKAGE))); + return; + } + } + + try + { + // when UndoStack is cleared by ModifyPageRedoAction + // the nCount may have changed, so test GetRedoActionCount() + while(nNumber-- && pUndoManager->GetRedoActionCount()) + pUndoManager->Redo(); + } + catch( const Exception& ) + { + // no need to handle. By definition, the UndoManager handled this by clearing the + // Undo/Redo stacks + } + } + + // refresh rulers, maybe REDO was move of TAB marker in ruler + if (mbHasRulers) + { + Invalidate(SID_ATTR_TABSTOP); + } + } + + // This one is corresponding to the default handling + // of SID_UNDO in sfx2 + GetViewFrame()->GetBindings().InvalidateAll(false); + + rReq.Done(); +} + +void ViewShell::ExecReq( SfxRequest& rReq ) +{ + sal_uInt16 nSlot = rReq.GetSlot(); + switch( nSlot ) + { + case SID_MAIL_SCROLLBODY_PAGEDOWN: + { + rtl::Reference<FuPoor> xFunc( GetCurrentFunction() ); + if( xFunc.is() ) + ScrollLines( 0, -1 ); + + rReq.Done(); + } + break; + + case SID_OUTPUT_QUALITY_COLOR: + case SID_OUTPUT_QUALITY_GRAYSCALE: + case SID_OUTPUT_QUALITY_BLACKWHITE: + case SID_OUTPUT_QUALITY_CONTRAST: + { + DrawModeFlags nMode = OUTPUT_DRAWMODE_COLOR; + + switch( nSlot ) + { + case SID_OUTPUT_QUALITY_COLOR: nMode = OUTPUT_DRAWMODE_COLOR; break; + case SID_OUTPUT_QUALITY_GRAYSCALE: nMode = OUTPUT_DRAWMODE_GRAYSCALE; break; + case SID_OUTPUT_QUALITY_BLACKWHITE: nMode = OUTPUT_DRAWMODE_BLACKWHITE; break; + case SID_OUTPUT_QUALITY_CONTRAST: nMode = OUTPUT_DRAWMODE_CONTRAST; break; + } + + GetActiveWindow()->GetOutDev()->SetDrawMode( nMode ); + mpFrameView->SetDrawMode( nMode ); + + GetActiveWindow()->Invalidate(); + + Invalidate(); + rReq.Done(); + break; + } + } +} + +/** This default implementation returns only an empty reference. See derived + classes for more interesting examples. +*/ +css::uno::Reference<css::accessibility::XAccessible> +ViewShell::CreateAccessibleDocumentView (::sd::Window* ) +{ + OSL_FAIL("ViewShell::CreateAccessibleDocumentView should not be called!, perhaps Meyers, 3rd edition, Item 9:"); + + return css::uno::Reference<css::accessibility::XAccessible> (); +} + +::sd::WindowUpdater* ViewShell::GetWindowUpdater() const +{ + return mpWindowUpdater.get(); +} + +ViewShellBase& ViewShell::GetViewShellBase() const +{ + return *static_cast<ViewShellBase*>(GetViewShell()); +} + +ViewShell::ShellType ViewShell::GetShellType() const +{ + return meShellType; +} + +DrawDocShell* ViewShell::GetDocSh() const +{ + return GetViewShellBase().GetDocShell(); +} + +SdDrawDocument* ViewShell::GetDoc() const +{ + return GetViewShellBase().GetDocument(); +} + +ErrCode ViewShell::DoVerb(sal_Int32 /*nVerb*/) +{ + return ERRCODE_NONE; +} + +void ViewShell::SetCurrentFunction( const rtl::Reference<FuPoor>& xFunction) +{ + if( mxCurrentFunction.is() && (mxOldFunction != mxCurrentFunction) ) + mxCurrentFunction->Dispose(); + rtl::Reference<FuPoor> xDisposeAfterNewOne( mxCurrentFunction ); + mxCurrentFunction = xFunction; +} + +void ViewShell::SetOldFunction(const rtl::Reference<FuPoor>& xFunction) +{ + if( mxOldFunction.is() && (xFunction != mxOldFunction) && (mxCurrentFunction != mxOldFunction) ) + mxOldFunction->Dispose(); + + rtl::Reference<FuPoor> xDisposeAfterNewOne( mxOldFunction ); + mxOldFunction = xFunction; +} + +/** this method deactivates the current function. If an old function is + saved, this will become activated and current function. +*/ +void ViewShell::Cancel() +{ + if(mxCurrentFunction.is() && (mxCurrentFunction != mxOldFunction )) + { + rtl::Reference<FuPoor> xTemp( mxCurrentFunction ); + mxCurrentFunction.clear(); + xTemp->Deactivate(); + xTemp->Dispose(); + } + + if(mxOldFunction.is()) + { + mxCurrentFunction = mxOldFunction; + mxCurrentFunction->Activate(); + } +} + +void ViewShell::DeactivateCurrentFunction( bool bPermanent /* == false */ ) +{ + if( mxCurrentFunction.is() ) + { + if(bPermanent && (mxOldFunction == mxCurrentFunction)) + mxOldFunction.clear(); + + mxCurrentFunction->Deactivate(); + if( mxCurrentFunction != mxOldFunction ) + mxCurrentFunction->Dispose(); + + rtl::Reference<FuPoor> xDisposeAfterNewOne( mxCurrentFunction ); + mxCurrentFunction.clear(); + } +} + +void ViewShell::DisposeFunctions() +{ + if(mxCurrentFunction.is()) + { + rtl::Reference<FuPoor> xTemp( mxCurrentFunction ); + mxCurrentFunction.clear(); + xTemp->Deactivate(); + xTemp->Dispose(); + } + + if(mxOldFunction.is()) + { + rtl::Reference<FuPoor> xDisposeAfterNewOne( mxOldFunction ); + mxOldFunction->Dispose(); + mxOldFunction.clear(); + } +} + +bool ViewShell::IsMainViewShell() const +{ + return mpImpl->mbIsMainViewShell; +} + +void ViewShell::SetIsMainViewShell (bool bIsMainViewShell) +{ + if (bIsMainViewShell != mpImpl->mbIsMainViewShell) + { + mpImpl->mbIsMainViewShell = bIsMainViewShell; + if (bIsMainViewShell) + GetDocSh()->Connect (this); + else + GetDocSh()->Disconnect (this); + } +} + +void ViewShell::PrePaint() +{ +} + +void ViewShell::Paint (const ::tools::Rectangle&, ::sd::Window* ) +{ +} + +void ViewShell::ShowUIControls (bool bVisible) +{ + if (mbHasRulers) + { + if (mpHorizontalRuler) + mpHorizontalRuler->Show( bVisible ); + + if (mpVerticalRuler) + mpVerticalRuler->Show( bVisible ); + } + + if (mpVerticalScrollBar) + mpVerticalScrollBar->Show( bVisible ); + + if (mpHorizontalScrollBar) + mpHorizontalScrollBar->Show( bVisible ); + + if (mpScrollBarBox) + mpScrollBarBox->Show(bVisible); + + if (mpContentWindow) + mpContentWindow->Show( bVisible ); +} + +bool ViewShell::RelocateToParentWindow (vcl::Window* pParentWindow) +{ + mpParentWindow = pParentWindow; + + mpParentWindow->SetBackground (Wallpaper()); + + if (mpContentWindow) + mpContentWindow->SetParent(pParentWindow); + + if (mpHorizontalScrollBar) + mpHorizontalScrollBar->SetParent(mpParentWindow); + if (mpVerticalScrollBar) + mpVerticalScrollBar->SetParent(mpParentWindow); + if (mpScrollBarBox) + mpScrollBarBox->SetParent(mpParentWindow); + + return true; +} + +void ViewShell::SwitchViewFireFocus(const css::uno::Reference< css::accessibility::XAccessible >& xAcc ) +{ + if (xAcc) + { + ::accessibility::AccessibleDocumentViewBase* pBase = static_cast< ::accessibility::AccessibleDocumentViewBase* >(xAcc.get()); + if (pBase) + pBase->SwitchViewActivated(); + } +} +void ViewShell::SwitchActiveViewFireFocus() +{ + if (mpContentWindow) + { + SwitchViewFireFocus(mpContentWindow->GetAccessible(false)); + } +} +// move these two methods from DrawViewShell. +void ViewShell::fireSwitchCurrentPage(sal_Int32 pageIndex) +{ + GetViewShellBase().GetDrawController().fireSwitchCurrentPage(pageIndex); +} +void ViewShell::NotifyAccUpdate( ) +{ + GetViewShellBase().GetDrawController().NotifyAccUpdate(); +} + +weld::Window* ViewShell::GetFrameWeld() const +{ + return mpActiveWindow ? mpActiveWindow->GetFrameWeld() : nullptr; +} + +sd::Window* ViewShell::GetContentWindow() const +{ + return mpContentWindow.get(); +} + +} // end of namespace sd + +//===== ViewShellObjectBarFactory ============================================= + +namespace { + +ViewShellObjectBarFactory::ViewShellObjectBarFactory ( + ::sd::ViewShell& rViewShell) + : mrViewShell (rViewShell) +{ +} + +SfxShell* ViewShellObjectBarFactory::CreateShell( ::sd::ShellId nId ) +{ + SfxShell* pShell = nullptr; + + ::sd::View* pView = mrViewShell.GetView(); + switch (nId) + { + case ToolbarId::Bezier_Toolbox_Sd: + pShell = new ::sd::BezierObjectBar(&mrViewShell, pView); + break; + + case ToolbarId::Draw_Text_Toolbox_Sd: + pShell = new ::sd::TextObjectBar( + &mrViewShell, mrViewShell.GetDoc()->GetPool(), pView); + break; + + case ToolbarId::Draw_Graf_Toolbox: + pShell = new ::sd::GraphicObjectBar(&mrViewShell, pView); + break; + + case ToolbarId::Draw_Media_Toolbox: + pShell = new ::sd::MediaObjectBar(&mrViewShell, pView); + break; + + case ToolbarId::Draw_Table_Toolbox: + pShell = ::sd::ui::table::CreateTableObjectBar( mrViewShell, pView ); + break; + + case ToolbarId::Svx_Extrusion_Bar: + pShell = new svx::ExtrusionBar( + &mrViewShell.GetViewShellBase()); + break; + + case ToolbarId::Svx_Fontwork_Bar: + pShell = new svx::FontworkBar( + &mrViewShell.GetViewShellBase()); + break; + + default: + pShell = nullptr; + break; + } + + return pShell; +} + +void ViewShellObjectBarFactory::ReleaseShell (SfxShell* pShell) +{ + delete pShell; +} + +} // end of anonymous namespace + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sd/source/ui/view/zoomlist.cxx b/sd/source/ui/view/zoomlist.cxx new file mode 100644 index 000000000..86a3de63b --- /dev/null +++ b/sd/source/ui/view/zoomlist.cxx @@ -0,0 +1,94 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <zoomlist.hxx> + +#include <svx/svxids.hrc> +#include <sfx2/bindings.hxx> +#include <sfx2/viewfrm.hxx> + +#include <ViewShell.hxx> + +namespace sd +{ +#define MAX_ENTRIES 10 + +ZoomList::ZoomList(ViewShell* pViewShell) + : mpViewShell(pViewShell) + , mnCurPos(0) +{ +} + +void ZoomList::InsertZoomRect(const ::tools::Rectangle& rRect) +{ + size_t nRectCount = maRectangles.size(); + + if (nRectCount >= MAX_ENTRIES) + maRectangles.erase(maRectangles.begin()); + else if (nRectCount == 0) + mnCurPos = 0; + else + mnCurPos++; + + maRectangles.insert(maRectangles.begin() + mnCurPos, rRect); + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_ZOOM_NEXT); + rBindings.Invalidate(SID_ZOOM_PREV); +} + +::tools::Rectangle const& ZoomList::GetNextZoomRect() +{ + mnCurPos++; + size_t nRectCount = maRectangles.size(); + + if (nRectCount > 0 && mnCurPos > nRectCount - 1) + mnCurPos = nRectCount - 1; + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_ZOOM_NEXT); + rBindings.Invalidate(SID_ZOOM_PREV); + + return maRectangles[mnCurPos]; +} + +::tools::Rectangle const& ZoomList::GetPreviousZoomRect() +{ + if (mnCurPos > 0) + mnCurPos--; + + SfxBindings& rBindings = mpViewShell->GetViewFrame()->GetBindings(); + rBindings.Invalidate(SID_ZOOM_NEXT); + rBindings.Invalidate(SID_ZOOM_PREV); + + return maRectangles[mnCurPos]; +} + +bool ZoomList::IsNextPossible() const +{ + size_t nRectCount = maRectangles.size(); + + return nRectCount > 0 && mnCurPos < nRectCount - 1; +} + +bool ZoomList::IsPreviousPossible() const { return mnCurPos > 0; } + +} // end of namespace sd + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3