1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <memory>
21 #include <DrawDocShell.hxx>
22 #include <svx/svdpagv.hxx>
23 #include <svx/svxdlg.hxx>
24 
25 #include <helpids.h>
26 #include <ViewShell.hxx>
27 #include <FrameView.hxx>
28 #include <drawdoc.hxx>
29 #include <sdpage.hxx>
30 #include <ClientView.hxx>
31 #include <Window.hxx>
32 #include <strings.hrc>
33 
34 #include <sdresid.hxx>
35 #include <fupoor.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/virdev.hxx>
38 #include <rtl/character.hxx>
39 #include <tools/debug.hxx>
40 
41 namespace sd {
42 
43 /**
44  * Drawing of DocShell (with the helper class SdDrawViewShell)
45  */
Draw(OutputDevice * pOut,const JobSetup &,sal_uInt16 nAspect)46 void DrawDocShell::Draw(OutputDevice* pOut, const JobSetup&, sal_uInt16 nAspect)
47 {
48     if (nAspect == ASPECT_THUMBNAIL)
49     {
50       // THUMBNAIL: here we may can set the draft mode
51     }
52 
53     std::unique_ptr<ClientView> pView( new ClientView(this, pOut) );
54 
55     pView->SetHlplVisible(false);
56     pView->SetGridVisible(false);
57     pView->SetBordVisible(false);
58     pView->SetPageVisible(false);
59     pView->SetGlueVisible(false);
60 
61     SdPage* pSelectedPage = nullptr;
62 
63     const std::vector<std::unique_ptr<sd::FrameView>> &rViews = mpDoc->GetFrameViewList();
64     if( !rViews.empty() )
65     {
66         sd::FrameView* pFrameView = rViews[0].get();
67         if( pFrameView->GetPageKind() == PageKind::Standard )
68         {
69             sal_uInt16 nSelectedPage = pFrameView->GetSelectedPage();
70             pSelectedPage = mpDoc->GetSdPage(nSelectedPage, PageKind::Standard);
71         }
72     }
73 
74     if( nullptr == pSelectedPage )
75     {
76         SdPage* pPage = nullptr;
77         sal_uInt16 nPageCnt = mpDoc->GetSdPageCount(PageKind::Standard);
78 
79         for (sal_uInt16 i = 0; i < nPageCnt; i++)
80         {
81             pPage = mpDoc->GetSdPage(i, PageKind::Standard);
82 
83             if ( pPage->IsSelected() )
84                 pSelectedPage = pPage;
85         }
86 
87         if( nullptr == pSelectedPage )
88             pSelectedPage = mpDoc->GetSdPage(0, PageKind::Standard);
89     }
90 
91     ::tools::Rectangle aVisArea = GetVisArea(nAspect);
92     pOut->IntersectClipRegion(aVisArea);
93     pView->ShowSdrPage(pSelectedPage);
94 
95     if (pOut->GetOutDevType() == OUTDEV_WINDOW)
96         return;
97 
98     MapMode aOldMapMode = pOut->GetMapMode();
99 
100     if (pOut->GetOutDevType() == OUTDEV_PRINTER)
101     {
102         MapMode aMapMode = aOldMapMode;
103         Point aOrigin = aMapMode.GetOrigin();
104         aOrigin.AdjustX(1 );
105         aOrigin.AdjustY(1 );
106         aMapMode.SetOrigin(aOrigin);
107         pOut->SetMapMode(aMapMode);
108     }
109 
110     vcl::Region aRegion(aVisArea);
111     pView->CompleteRedraw(pOut, aRegion);
112 
113     if (pOut->GetOutDevType() == OUTDEV_PRINTER)
114     {
115         pOut->SetMapMode(aOldMapMode);
116     }
117 }
118 
GetVisArea(sal_uInt16 nAspect) const119 ::tools::Rectangle DrawDocShell::GetVisArea(sal_uInt16 nAspect) const
120 {
121     ::tools::Rectangle aVisArea;
122 
123     if( ( ASPECT_THUMBNAIL == nAspect ) || ( ASPECT_DOCPRINT == nAspect ) )
124     {
125         // provide size of first page
126         MapMode aSrcMapMode(MapUnit::MapPixel);
127         MapMode aDstMapMode(MapUnit::Map100thMM);
128         Size aSize = mpDoc->GetSdPage(0, PageKind::Standard)->GetSize();
129         aSrcMapMode.SetMapUnit(MapUnit::Map100thMM);
130 
131         aSize = Application::GetDefaultDevice()->LogicToLogic(aSize, &aSrcMapMode, &aDstMapMode);
132         aVisArea.SetSize(aSize);
133     }
134     else
135     {
136         aVisArea = SfxObjectShell::GetVisArea(nAspect);
137     }
138 
139     if (aVisArea.IsEmpty() && mpViewShell)
140     {
141         vcl::Window* pWin = mpViewShell->GetActiveWindow();
142 
143         if (pWin)
144         {
145             aVisArea = pWin->PixelToLogic(::tools::Rectangle(Point(0,0), pWin->GetOutputSizePixel()));
146         }
147     }
148 
149     return aVisArea;
150 }
151 
Connect(ViewShell * pViewSh)152 void DrawDocShell::Connect(ViewShell* pViewSh)
153 {
154     mpViewShell = pViewSh;
155 }
156 
Disconnect(ViewShell const * pViewSh)157 void DrawDocShell::Disconnect(ViewShell const * pViewSh)
158 {
159     if (mpViewShell == pViewSh)
160     {
161         mpViewShell = nullptr;
162     }
163 }
164 
GetFrameView()165 FrameView* DrawDocShell::GetFrameView()
166 {
167     FrameView* pFrameView = nullptr;
168 
169     if (mpViewShell)
170     {
171         pFrameView = mpViewShell->GetFrameView();
172     }
173 
174     return pFrameView;
175 }
176 
177 /**
178  * Creates a bitmap of an arbitrary page
179  */
GetPagePreviewBitmap(SdPage * pPage)180 BitmapEx DrawDocShell::GetPagePreviewBitmap(SdPage* pPage)
181 {
182     const sal_uInt16 nMaxEdgePixel = 90;
183     MapMode         aMapMode( MapUnit::Map100thMM );
184     const Size      aSize( pPage->GetSize() );
185     const Point     aNullPt;
186     ScopedVclPtrInstance< VirtualDevice > pVDev( *Application::GetDefaultDevice() );
187 
188     pVDev->SetMapMode( aMapMode );
189 
190     const Size  aPixSize( pVDev->LogicToPixel( aSize ) );
191     const sal_uLong nMaxEdgePix = std::max( aPixSize.Width(), aPixSize.Height() );
192     Fraction    aFrac( nMaxEdgePixel, nMaxEdgePix );
193 
194     aMapMode.SetScaleX( aFrac );
195     aMapMode.SetScaleY( aFrac );
196     pVDev->SetMapMode( aMapMode );
197     pVDev->SetOutputSize( aSize );
198 
199     // that we also get the dark lines at the right and bottom page margin
200     aFrac = Fraction( nMaxEdgePixel - 1, nMaxEdgePix );
201     aMapMode.SetScaleX( aFrac );
202     aMapMode.SetScaleY( aFrac );
203     pVDev->SetMapMode( aMapMode );
204 
205     std::unique_ptr<ClientView> pView(new ClientView( this, pVDev ));
206     FrameView*      pFrameView = GetFrameView();
207     pView->ShowSdrPage( pPage );
208 
209     if ( GetFrameView() )
210     {
211         // initialize the drawing-(screen) attributes
212         pView->SetGridCoarse( pFrameView->GetGridCoarse() );
213         pView->SetGridFine( pFrameView->GetGridFine() );
214         pView->SetSnapGridWidth(pFrameView->GetSnapGridWidthX(), pFrameView->GetSnapGridWidthY());
215         pView->SetGridVisible( pFrameView->IsGridVisible() );
216         pView->SetGridFront( pFrameView->IsGridFront() );
217         pView->SetSnapAngle( pFrameView->GetSnapAngle() );
218         pView->SetGridSnap( pFrameView->IsGridSnap() );
219         pView->SetBordSnap( pFrameView->IsBordSnap() );
220         pView->SetHlplSnap( pFrameView->IsHlplSnap() );
221         pView->SetOFrmSnap( pFrameView->IsOFrmSnap() );
222         pView->SetOPntSnap( pFrameView->IsOPntSnap() );
223         pView->SetOConSnap( pFrameView->IsOConSnap() );
224         pView->SetDragStripes( pFrameView->IsDragStripes() );
225         pView->SetFrameDragSingles( pFrameView->IsFrameDragSingles() );
226         pView->SetSnapMagneticPixel( pFrameView->GetSnapMagneticPixel() );
227         pView->SetMarkedHitMovesAlways( pFrameView->IsMarkedHitMovesAlways() );
228         pView->SetMoveOnlyDragging( pFrameView->IsMoveOnlyDragging() );
229         pView->SetSlantButShear( pFrameView->IsSlantButShear() );
230         pView->SetNoDragXorPolys( pFrameView->IsNoDragXorPolys() );
231         pView->SetCrookNoContortion( pFrameView->IsCrookNoContortion() );
232         pView->SetAngleSnapEnabled( pFrameView->IsAngleSnapEnabled() );
233         pView->SetBigOrtho( pFrameView->IsBigOrtho() );
234         pView->SetOrtho( pFrameView->IsOrtho() );
235 
236         SdrPageView* pPageView = pView->GetSdrPageView();
237 
238         if (pPageView)
239         {
240             if ( pPageView->GetVisibleLayers() != pFrameView->GetVisibleLayers() )
241                 pPageView->SetVisibleLayers( pFrameView->GetVisibleLayers() );
242 
243             if ( pPageView->GetPrintableLayers() != pFrameView->GetPrintableLayers() )
244                 pPageView->SetPrintableLayers( pFrameView->GetPrintableLayers() );
245 
246             if ( pPageView->GetLockedLayers() != pFrameView->GetLockedLayers() )
247                 pPageView->SetLockedLayers( pFrameView->GetLockedLayers() );
248 
249             pPageView->SetHelpLines( pFrameView->GetStandardHelpLines() );
250         }
251 
252         if ( pView->GetActiveLayer() != pFrameView->GetActiveLayer() )
253             pView->SetActiveLayer( pFrameView->GetActiveLayer() );
254     }
255 
256     pView->CompleteRedraw( pVDev, vcl::Region(::tools::Rectangle(aNullPt, aSize)) );
257 
258     // IsRedrawReady() always gives sal_True while ( !pView->IsRedrawReady() ) {}
259     pView.reset();
260 
261     pVDev->SetMapMode( MapMode() );
262 
263     BitmapEx aPreview( pVDev->GetBitmapEx( aNullPt, pVDev->GetOutputSizePixel() ) );
264 
265     DBG_ASSERT(!!aPreview, "Preview-Bitmap could not be generated");
266 
267     return aPreview;
268 }
269 
270 /**
271  * Checks if the page exists. If so, we force the user to enter a not yet used
272  * name.
273  * @return sal_False if the user cancels the action.
274  */
CheckPageName(weld::Window * pWin,OUString & rName)275 bool DrawDocShell::CheckPageName(weld::Window* pWin, OUString& rName)
276 {
277     const OUString aStrForDlg( rName );
278     bool bIsNameValid = IsNewPageNameValid( rName, true );
279 
280     if( ! bIsNameValid )
281     {
282         OUString aDesc;
283         SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
284 
285         if (GetDocumentType() == DocumentType::Draw)
286             aDesc = SdResId( STR_WARN_PAGE_EXISTS_DRAW );
287         else
288             aDesc = SdResId( STR_WARN_PAGE_EXISTS );
289 
290         ScopedVclPtr<AbstractSvxNameDialog> aNameDlg(pFact->CreateSvxNameDialog(pWin, aStrForDlg, aDesc));
291         aNameDlg->SetEditHelpId( HID_SD_NAMEDIALOG_PAGE );
292 
293         aNameDlg->SetCheckNameHdl( LINK( this, DrawDocShell, RenameSlideHdl ) );
294 
295         rtl::Reference<FuPoor> xFunc( mpViewShell->GetCurrentFunction() );
296         if( xFunc.is() )
297             xFunc->cancel();
298 
299         if( aNameDlg->Execute() == RET_OK )
300         {
301             aNameDlg->GetName( rName );
302             bIsNameValid = IsNewPageNameValid( rName );
303         }
304     }
305 
306     return bIsNameValid;
307 }
308 
IsNewPageNameValid(OUString & rInOutPageName,bool bResetStringIfStandardName)309 bool DrawDocShell::IsNewPageNameValid( OUString & rInOutPageName, bool bResetStringIfStandardName /* = false */ )
310 {
311     bool bCanUseNewName = false;
312 
313     // check if name is something like 'Slide n'
314     OUString aStrPage(SdResId(STR_SD_PAGE) + " ");
315 
316     bool bIsStandardName = false;
317 
318     // prevent also _future_ slide names of the form "'STR_SD_PAGE' + ' ' + '[0-9]+|[a-z]|[A-Z]|[CDILMVX]+|[cdilmvx]+'"
319     // (arabic, lower- and upper case single letter, lower- and upper case roman numbers)
320     if (rInOutPageName.startsWith(aStrPage) &&
321         rInOutPageName.getLength() > aStrPage.getLength())
322     {
323         sal_Int32 nIdx{ aStrPage.getLength() };
324         OUString sRemainder = rInOutPageName.getToken(0, ' ', nIdx);
325         if (sRemainder[0] >= '0' && sRemainder[0] <= '9')
326         {
327             // check for arabic numbering
328 
329             sal_Int32 nIndex = 1;
330             // skip all following numbers
331             while (nIndex < sRemainder.getLength() &&
332                    sRemainder[nIndex] >= '0' && sRemainder[nIndex] <= '9')
333             {
334                 nIndex++;
335             }
336 
337             // EOL? Reserved name!
338             if (nIndex >= sRemainder.getLength())
339             {
340                 bIsStandardName = true;
341             }
342         }
343         else if (sRemainder.getLength() == 1 &&
344                  rtl::isAsciiLowerCase(sRemainder[0]))
345         {
346             // lower case, single character: reserved
347             bIsStandardName = true;
348         }
349         else if (sRemainder.getLength() == 1 &&
350                  rtl::isAsciiUpperCase(sRemainder[0]))
351         {
352             // upper case, single character: reserved
353             bIsStandardName = true;
354         }
355         else
356         {
357             // check for upper/lower case roman numbering
358             OUString sReserved("cdilmvx");
359 
360             // skip all following characters contained in one reserved class
361             if (sReserved.indexOf(sRemainder[0]) == -1)
362                 sReserved = sReserved.toAsciiUpperCase();
363 
364             sal_Int32 nIndex = 0;
365             while (nIndex < sRemainder.getLength() &&
366                    sReserved.indexOf(sRemainder[nIndex]) != -1)
367             {
368                 nIndex++;
369             }
370 
371             // EOL? Reserved name!
372             if (nIndex >= sRemainder.getLength())
373             {
374                 bIsStandardName = true;
375             }
376         }
377     }
378 
379     if( bIsStandardName )
380     {
381         if( bResetStringIfStandardName )
382         {
383             // this is for insertion of slides from other files with standard
384             // name.  They get a new standard name, if the string is set to an
385             // empty one.
386             rInOutPageName.clear();
387             bCanUseNewName = true;
388         }
389         else
390             bCanUseNewName = false;
391     }
392     else
393     {
394         if (!rInOutPageName.isEmpty())
395         {
396             bool   bOutDummy;
397             sal_uInt16 nExistingPageNum = mpDoc->GetPageByName( rInOutPageName, bOutDummy );
398             bCanUseNewName = ( nExistingPageNum == SDRPAGE_NOTFOUND );
399         }
400         else
401             bCanUseNewName = false;
402     }
403 
404     return bCanUseNewName;
405 }
406 
IsPageNameUnique(const OUString & rPageName) const407 bool DrawDocShell::IsPageNameUnique( const OUString & rPageName ) const
408 {
409     return mpDoc->IsPageNameUnique(rPageName);
410 }
411 
IMPL_LINK(DrawDocShell,RenameSlideHdl,AbstractSvxNameDialog &,rDialog,bool)412 IMPL_LINK( DrawDocShell, RenameSlideHdl, AbstractSvxNameDialog&, rDialog, bool )
413 {
414     OUString aNewName;
415     rDialog.GetName( aNewName );
416     return IsNewPageNameValid( aNewName );
417 }
418 
419 } // end of namespace sd
420 
421 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
422