Line data Source code
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 :
21 : #include "PreviewRenderer.hxx"
22 :
23 : #include "DrawDocShell.hxx"
24 : #include "drawdoc.hxx"
25 : #include "drawview.hxx"
26 : #include "sdpage.hxx"
27 : #include "ViewShell.hxx"
28 : #include <vcl/virdev.hxx>
29 : #include <vcl/settings.hxx>
30 :
31 : #include <svx/svdpagv.hxx>
32 : #include <svx/svdoutl.hxx>
33 : #include <editeng/eeitem.hxx>
34 : #include <editeng/editstat.hxx>
35 : #include <vcl/svapp.hxx>
36 : #include <tools/diagnose_ex.h>
37 : #include <svx/sdr/contact/viewobjectcontact.hxx>
38 : #include <svx/sdr/contact/viewcontact.hxx>
39 :
40 : using namespace ::com::sun::star;
41 : using namespace ::com::sun::star::uno;
42 :
43 :
44 : namespace sd {
45 :
46 : const int PreviewRenderer::snSubstitutionTextSize = 11;
47 : const int PreviewRenderer::snFrameWidth = 1;
48 :
49 : namespace {
50 : /** This incarnation of the ViewObjectContactRedirector filters away all
51 : PageObj objects, unconditionally.
52 : */
53 : class ViewRedirector : public ::sdr::contact::ViewObjectContactRedirector
54 : {
55 : public:
56 : ViewRedirector (void);
57 : virtual ~ViewRedirector (void);
58 : virtual drawinglayer::primitive2d::Primitive2DSequence createRedirectedPrimitive2DSequence(
59 : const sdr::contact::ViewObjectContact& rOriginal,
60 : const sdr::contact::DisplayInfo& rDisplayInfo) SAL_OVERRIDE;
61 : };
62 : }
63 :
64 :
65 :
66 :
67 : //===== PreviewRenderer =======================================================
68 :
69 0 : PreviewRenderer::PreviewRenderer (
70 : OutputDevice* pTemplate,
71 : const bool bHasFrame)
72 0 : : mpPreviewDevice (new VirtualDevice()),
73 : mpView(NULL),
74 : mpDocShellOfView(NULL),
75 0 : maFrameColor (svtools::ColorConfig().GetColorValue(svtools::DOCBOUNDARIES).nColor),
76 0 : mbHasFrame(bHasFrame)
77 : {
78 0 : if (pTemplate != NULL)
79 : {
80 0 : mpPreviewDevice->SetDigitLanguage (pTemplate->GetDigitLanguage());
81 0 : mpPreviewDevice->SetBackground(pTemplate->GetBackground());
82 : }
83 : else
84 : {
85 0 : mpPreviewDevice->SetBackground(Wallpaper(
86 0 : Application::GetSettings().GetStyleSettings().GetWindowColor()));
87 : }
88 0 : }
89 :
90 :
91 :
92 :
93 0 : PreviewRenderer::~PreviewRenderer (void)
94 : {
95 0 : if (mpDocShellOfView != NULL)
96 0 : EndListening (*mpDocShellOfView);
97 0 : }
98 :
99 :
100 :
101 :
102 0 : Image PreviewRenderer::RenderPage (
103 : const SdPage* pPage,
104 : const sal_Int32 nWidth,
105 : const OUString& rSubstitutionText,
106 : const bool bObeyHighContrastMode,
107 : const bool bDisplayPresentationObjects)
108 : {
109 0 : if (pPage != NULL)
110 : {
111 0 : const Size aPageModelSize (pPage->GetSize());
112 : const double nAspectRatio (
113 0 : double(aPageModelSize.Width()) / double(aPageModelSize.Height()));
114 0 : const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
115 : const sal_Int32 nHeight (sal::static_int_cast<sal_Int32>(
116 0 : (nWidth - 2*nFrameWidth) / nAspectRatio + 2*nFrameWidth + 0.5));
117 : return RenderPage (
118 : pPage,
119 : Size(nWidth,nHeight),
120 : rSubstitutionText,
121 : bObeyHighContrastMode,
122 0 : bDisplayPresentationObjects);
123 : }
124 : else
125 0 : return Image();
126 : }
127 :
128 :
129 :
130 :
131 0 : Image PreviewRenderer::RenderPage (
132 : const SdPage* pPage,
133 : Size aPixelSize,
134 : const OUString& rSubstitutionText,
135 : const bool bObeyHighContrastMode,
136 : const bool bDisplayPresentationObjects)
137 : {
138 0 : Image aPreview;
139 :
140 0 : if (pPage != NULL)
141 : {
142 : try
143 : {
144 0 : if (Initialize(pPage, aPixelSize, bObeyHighContrastMode))
145 : {
146 0 : PaintPage(pPage, bDisplayPresentationObjects);
147 0 : PaintSubstitutionText(rSubstitutionText);
148 0 : PaintFrame();
149 :
150 0 : Size aSize (mpPreviewDevice->GetOutputSizePixel());
151 0 : aPreview = Image(mpPreviewDevice->GetBitmap (
152 0 : mpPreviewDevice->PixelToLogic(Point(0,0)),
153 0 : mpPreviewDevice->PixelToLogic(aSize)));
154 :
155 0 : Cleanup();
156 : }
157 : }
158 0 : catch (const com::sun::star::uno::Exception&)
159 : {
160 : DBG_UNHANDLED_EXCEPTION();
161 : }
162 : }
163 :
164 0 : return aPreview;
165 : }
166 :
167 :
168 :
169 :
170 0 : Image PreviewRenderer::RenderSubstitution (
171 : const Size& rPreviewPixelSize,
172 : const OUString& rSubstitutionText)
173 : {
174 0 : Image aPreview;
175 :
176 : try
177 : {
178 : // Set size.
179 0 : mpPreviewDevice->SetOutputSizePixel(rPreviewPixelSize);
180 :
181 : // Adjust contrast mode.
182 : const bool bUseContrast (
183 0 : Application::GetSettings().GetStyleSettings().GetHighContrastMode());
184 0 : mpPreviewDevice->SetDrawMode (bUseContrast
185 : ? ViewShell::OUTPUT_DRAWMODE_CONTRAST
186 0 : : ViewShell::OUTPUT_DRAWMODE_COLOR);
187 :
188 : // Set a map mode that makes a typical substitution text completely
189 : // visible.
190 0 : MapMode aMapMode (mpPreviewDevice->GetMapMode());
191 0 : aMapMode.SetMapUnit(MAP_100TH_MM);
192 0 : const double nFinalScale (25.0 * rPreviewPixelSize.Width() / 28000.0);
193 0 : aMapMode.SetScaleX(nFinalScale);
194 0 : aMapMode.SetScaleY(nFinalScale);
195 0 : const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
196 0 : aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(
197 0 : Point(nFrameWidth,nFrameWidth),aMapMode));
198 0 : mpPreviewDevice->SetMapMode (aMapMode);
199 :
200 : // Clear the background.
201 : const Rectangle aPaintRectangle (
202 : Point(0,0),
203 0 : mpPreviewDevice->GetOutputSizePixel());
204 0 : mpPreviewDevice->EnableMapMode(false);
205 0 : mpPreviewDevice->SetLineColor();
206 0 : svtools::ColorConfig aColorConfig;
207 0 : mpPreviewDevice->SetFillColor(aColorConfig.GetColorValue(svtools::DOCCOLOR).nColor);
208 0 : mpPreviewDevice->DrawRect (aPaintRectangle);
209 0 : mpPreviewDevice->EnableMapMode(true);
210 :
211 : // Paint substitution text and a frame around it.
212 0 : PaintSubstitutionText (rSubstitutionText);
213 0 : PaintFrame();
214 :
215 0 : const Size aSize (mpPreviewDevice->GetOutputSizePixel());
216 0 : aPreview = Image(mpPreviewDevice->GetBitmap(
217 0 : mpPreviewDevice->PixelToLogic(Point(0,0)),
218 0 : mpPreviewDevice->PixelToLogic(aSize)));
219 : }
220 0 : catch (const com::sun::star::uno::Exception&)
221 : {
222 : DBG_UNHANDLED_EXCEPTION();
223 : }
224 :
225 0 : return aPreview;
226 : }
227 :
228 :
229 :
230 :
231 0 : bool PreviewRenderer::Initialize (
232 : const SdPage* pPage,
233 : const Size& rPixelSize,
234 : const bool bObeyHighContrastMode)
235 : {
236 0 : if (pPage == NULL)
237 0 : return false;
238 :
239 0 : SdrModel* pModel = pPage->GetModel();
240 0 : if (pModel == NULL)
241 0 : return false;
242 :
243 0 : SetupOutputSize(*pPage, rPixelSize);
244 :
245 : SdDrawDocument* pDocument
246 0 : = static_cast<SdDrawDocument*>(pPage->GetModel());
247 0 : DrawDocShell* pDocShell = pDocument->GetDocSh();
248 :
249 : // Create view
250 0 : ProvideView (pDocShell);
251 0 : if (mpView.get() == NULL)
252 0 : return false;
253 :
254 : // Adjust contrast mode.
255 : bool bUseContrast (bObeyHighContrastMode
256 0 : && Application::GetSettings().GetStyleSettings().GetHighContrastMode());
257 0 : mpPreviewDevice->SetDrawMode (bUseContrast
258 : ? ViewShell::OUTPUT_DRAWMODE_CONTRAST
259 0 : : ViewShell::OUTPUT_DRAWMODE_COLOR);
260 0 : mpPreviewDevice->SetSettings(Application::GetSettings());
261 :
262 : // Tell the view to show the given page.
263 0 : SdPage* pNonConstPage = const_cast<SdPage*>(pPage);
264 0 : if (pPage->IsMasterPage())
265 : {
266 0 : mpView->ShowSdrPage(mpView->GetModel()->GetMasterPage(pPage->GetPageNum()));
267 : }
268 : else
269 : {
270 0 : mpView->ShowSdrPage(pNonConstPage);
271 : }
272 :
273 : // Make sure that a page view exists.
274 0 : SdrPageView* pPageView = mpView->GetSdrPageView();
275 :
276 0 : if (pPageView == NULL)
277 0 : return false;
278 :
279 : // #i121224# No need to set SetApplicationBackgroundColor (which is the color
280 : // of the area 'behind' the page (formally called 'Wiese') since the page previews
281 : // produced exactly cover the page's area, so it would never be visible. What
282 : // needs to be set is the ApplicationDocumentColor which is derived from
283 : // svtools::DOCCOLOR normally
284 0 : Color aApplicationDocumentColor;
285 :
286 0 : if (pPageView->GetApplicationDocumentColor() == COL_AUTO)
287 : {
288 0 : svtools::ColorConfig aColorConfig;
289 0 : aApplicationDocumentColor = aColorConfig.GetColorValue( svtools::DOCCOLOR ).nColor;
290 : }
291 : else
292 : {
293 0 : aApplicationDocumentColor = pPageView->GetApplicationDocumentColor();
294 : }
295 :
296 0 : pPageView->SetApplicationDocumentColor(aApplicationDocumentColor);
297 0 : SdrOutliner& rOutliner(pDocument->GetDrawOutliner(NULL));
298 0 : rOutliner.SetBackgroundColor(aApplicationDocumentColor);
299 0 : rOutliner.SetDefaultLanguage(pDocument->GetLanguage(EE_CHAR_LANGUAGE));
300 0 : mpPreviewDevice->SetBackground(Wallpaper(aApplicationDocumentColor));
301 0 : mpPreviewDevice->Erase();
302 :
303 0 : return true;
304 : }
305 :
306 :
307 :
308 :
309 0 : void PreviewRenderer::Cleanup (void)
310 : {
311 0 : mpView->HideSdrPage();
312 0 : }
313 :
314 :
315 :
316 :
317 0 : void PreviewRenderer::PaintPage (
318 : const SdPage* pPage,
319 : const bool bDisplayPresentationObjects)
320 : {
321 : // Paint the page.
322 0 : Rectangle aPaintRectangle (Point(0,0), pPage->GetSize());
323 0 : Region aRegion (aPaintRectangle);
324 :
325 : // Turn off online spelling and redlining.
326 0 : SdrOutliner* pOutliner = NULL;
327 0 : sal_uLong nSavedControlWord (0);
328 0 : if (mpDocShellOfView!=NULL && mpDocShellOfView->GetDoc()!=NULL)
329 : {
330 0 : pOutliner = &mpDocShellOfView->GetDoc()->GetDrawOutliner();
331 0 : nSavedControlWord = pOutliner->GetControlWord();
332 0 : pOutliner->SetControlWord((nSavedControlWord & ~EE_CNTRL_ONLINESPELLING));
333 : }
334 :
335 : // Use a special redirector to prevent PresObj shapes from being painted.
336 0 : boost::scoped_ptr<ViewRedirector> pRedirector;
337 0 : if ( ! bDisplayPresentationObjects)
338 0 : pRedirector.reset(new ViewRedirector());
339 :
340 : try
341 : {
342 0 : mpView->CompleteRedraw(mpPreviewDevice.get(), aRegion, pRedirector.get());
343 : }
344 0 : catch (const ::com::sun::star::uno::Exception&)
345 : {
346 : DBG_UNHANDLED_EXCEPTION();
347 : }
348 :
349 : // Restore the previous online spelling and redlining states.
350 0 : if (pOutliner != NULL)
351 0 : pOutliner->SetControlWord(nSavedControlWord);
352 0 : }
353 :
354 :
355 :
356 :
357 0 : void PreviewRenderer::PaintSubstitutionText (const OUString& rSubstitutionText)
358 : {
359 0 : if (!rSubstitutionText.isEmpty())
360 : {
361 : // Set the font size.
362 0 : const Font& rOriginalFont (mpPreviewDevice->GetFont());
363 0 : Font aFont (mpPreviewDevice->GetSettings().GetStyleSettings().GetAppFont());
364 0 : sal_Int32 nHeight (mpPreviewDevice->PixelToLogic(Size(0,snSubstitutionTextSize)).Height());
365 0 : aFont.SetHeight(nHeight);
366 0 : mpPreviewDevice->SetFont (aFont);
367 :
368 : // Paint the substitution text.
369 : Rectangle aTextBox (
370 : Point(0,0),
371 0 : mpPreviewDevice->PixelToLogic(
372 0 : mpPreviewDevice->GetOutputSizePixel()));
373 : sal_uInt16 nTextStyle =
374 : TEXT_DRAW_CENTER
375 : | TEXT_DRAW_VCENTER
376 : | TEXT_DRAW_MULTILINE
377 0 : | TEXT_DRAW_WORDBREAK;
378 0 : mpPreviewDevice->DrawText (aTextBox, rSubstitutionText, nTextStyle);
379 :
380 : // Restore the font.
381 0 : mpPreviewDevice->SetFont (rOriginalFont);
382 : }
383 0 : }
384 :
385 :
386 :
387 :
388 0 : void PreviewRenderer::PaintFrame (void)
389 : {
390 0 : if (mbHasFrame)
391 : {
392 : // Paint a frame arround the preview.
393 : Rectangle aPaintRectangle (
394 : Point(0,0),
395 0 : mpPreviewDevice->GetOutputSizePixel());
396 0 : mpPreviewDevice->EnableMapMode(false);
397 0 : mpPreviewDevice->SetLineColor(maFrameColor);
398 0 : mpPreviewDevice->SetFillColor();
399 0 : mpPreviewDevice->DrawRect(aPaintRectangle);
400 0 : mpPreviewDevice->EnableMapMode(true);
401 : }
402 0 : }
403 :
404 :
405 :
406 :
407 0 : void PreviewRenderer::SetupOutputSize (
408 : const SdPage& rPage,
409 : const Size& rFramePixelSize)
410 : {
411 : // First set the map mode to some arbitrary scale that is numerically
412 : // stable.
413 0 : MapMode aMapMode (mpPreviewDevice->GetMapMode());
414 0 : aMapMode.SetMapUnit(MAP_PIXEL);
415 :
416 : // Adapt it to the desired width.
417 0 : const Size aPageModelSize (rPage.GetSize());
418 0 : if (aPageModelSize.Width()>0 || aPageModelSize.Height()>0)
419 : {
420 0 : const sal_Int32 nFrameWidth (mbHasFrame ? snFrameWidth : 0);
421 : aMapMode.SetScaleX(
422 0 : Fraction(rFramePixelSize.Width()-2*nFrameWidth-1, aPageModelSize.Width()));
423 : aMapMode.SetScaleY(
424 0 : Fraction(rFramePixelSize.Height()-2*nFrameWidth-1, aPageModelSize.Height()));
425 0 : aMapMode.SetOrigin(mpPreviewDevice->PixelToLogic(Point(nFrameWidth,nFrameWidth),aMapMode));
426 : }
427 : else
428 : {
429 : // We should never get here.
430 : OSL_ASSERT(false);
431 0 : aMapMode.SetScaleX(1.0);
432 0 : aMapMode.SetScaleY(1.0);
433 : }
434 0 : mpPreviewDevice->SetMapMode (aMapMode);
435 0 : mpPreviewDevice->SetOutputSizePixel(rFramePixelSize);
436 0 : }
437 :
438 :
439 :
440 :
441 0 : void PreviewRenderer::ProvideView (DrawDocShell* pDocShell)
442 : {
443 0 : if (pDocShell != mpDocShellOfView)
444 : {
445 : // Destroy the view that is connected to the current doc shell.
446 0 : mpView.reset();
447 :
448 : // Switch our attention, i.e. listening for DYING events, to
449 : // the new doc shell.
450 0 : if (mpDocShellOfView != NULL)
451 0 : EndListening (*mpDocShellOfView);
452 0 : mpDocShellOfView = pDocShell;
453 0 : if (mpDocShellOfView != NULL)
454 0 : StartListening (*mpDocShellOfView);
455 : }
456 0 : if (mpView.get() == NULL)
457 : {
458 0 : mpView.reset (new DrawView (pDocShell, mpPreviewDevice.get(), NULL));
459 : }
460 0 : mpView->SetPreviewRenderer(true);
461 : #if 1
462 0 : mpView->SetPageVisible(false);
463 0 : mpView->SetPageBorderVisible(true);
464 0 : mpView->SetBordVisible(false);
465 0 : mpView->SetGridVisible(false);
466 0 : mpView->SetHlplVisible(false);
467 0 : mpView->SetGlueVisible(false);
468 :
469 : #else
470 : // This works in the slide sorter but prevents the master page
471 : // background being painted in the list of current master pages in the
472 : // task manager.
473 : mpView->SetPagePaintingAllowed(false);
474 : #endif
475 0 : }
476 :
477 :
478 :
479 :
480 0 : Image PreviewRenderer::ScaleBitmap (
481 : const BitmapEx& rBitmapEx,
482 : int nWidth)
483 : {
484 0 : Image aPreview;
485 :
486 : do
487 : {
488 : // Adjust contrast mode.
489 0 : bool bUseContrast = Application::GetSettings().GetStyleSettings().
490 0 : GetHighContrastMode();
491 0 : mpPreviewDevice->SetDrawMode (bUseContrast
492 : ? ViewShell::OUTPUT_DRAWMODE_CONTRAST
493 0 : : ViewShell::OUTPUT_DRAWMODE_COLOR);
494 :
495 : // Set output size.
496 0 : Size aSize (rBitmapEx.GetSizePixel());
497 0 : if (aSize.Width() <= 0)
498 0 : break;
499 : Size aFrameSize (
500 : nWidth,
501 0 : (long)((nWidth*1.0 * aSize.Height()) / aSize.Width() + 0.5));
502 0 : Size aPreviewSize (aFrameSize.Width()-2,aFrameSize.Height()-2);
503 0 : MapMode aMapMode (mpPreviewDevice->GetMapMode());
504 0 : aMapMode.SetMapUnit(MAP_PIXEL);
505 0 : aMapMode.SetOrigin (Point());
506 0 : aMapMode.SetScaleX (1.0);
507 0 : aMapMode.SetScaleY (1.0);
508 0 : mpPreviewDevice->SetMapMode (aMapMode);
509 0 : mpPreviewDevice->SetOutputSize (aFrameSize);
510 :
511 : // Paint a frame arround the preview.
512 0 : mpPreviewDevice->SetLineColor (maFrameColor);
513 0 : mpPreviewDevice->SetFillColor ();
514 0 : mpPreviewDevice->DrawRect (Rectangle(Point(0,0), aFrameSize));
515 :
516 : // Paint the bitmap scaled to the desired width.
517 0 : BitmapEx aScaledBitmap (rBitmapEx.GetBitmap());
518 0 : aScaledBitmap.Scale (aPreviewSize, BMP_SCALE_BESTQUALITY);
519 0 : mpPreviewDevice->DrawBitmap (
520 : Point(1,1),
521 : aPreviewSize,
522 0 : aScaledBitmap.GetBitmap());
523 :
524 : // Get the resulting bitmap.
525 0 : aPreview = Image(mpPreviewDevice->GetBitmap(Point(0,0), aFrameSize));
526 : }
527 : while (false);
528 :
529 0 : return aPreview;
530 : }
531 :
532 :
533 :
534 :
535 0 : void PreviewRenderer::Notify(SfxBroadcaster&, const SfxHint& rHint)
536 : {
537 0 : if (rHint.IsA(TYPE(SfxSimpleHint))
538 0 : && mpDocShellOfView != NULL)
539 : {
540 0 : const SfxSimpleHint* pSimpleHint = PTR_CAST(SfxSimpleHint, &rHint);
541 0 : if (pSimpleHint != NULL
542 0 : && pSimpleHint->GetId() == SFX_HINT_DYING)
543 : {
544 : // The doc shell is dying. Our view uses its item pool and
545 : // has to be destroyed as well. The next call to
546 : // ProvideView will create a new one (for another
547 : // doc shell, of course.)
548 0 : mpView.reset();
549 0 : mpDocShellOfView = NULL;
550 : }
551 : }
552 0 : }
553 :
554 :
555 :
556 :
557 : //===== ViewRedirector ========================================================
558 :
559 : namespace {
560 :
561 0 : ViewRedirector::ViewRedirector (void)
562 : {
563 0 : }
564 :
565 :
566 :
567 :
568 0 : ViewRedirector::~ViewRedirector (void)
569 : {
570 0 : }
571 :
572 :
573 :
574 :
575 0 : drawinglayer::primitive2d::Primitive2DSequence ViewRedirector::createRedirectedPrimitive2DSequence(
576 : const sdr::contact::ViewObjectContact& rOriginal,
577 : const sdr::contact::DisplayInfo& rDisplayInfo)
578 : {
579 0 : SdrObject* pObject = rOriginal.GetViewContact().TryToGetSdrObject();
580 :
581 0 : if (pObject==NULL || pObject->GetPage() == NULL)
582 : {
583 : // not a SdrObject visualisation (maybe e.g. page) or no page
584 : return sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
585 : rOriginal,
586 0 : rDisplayInfo);
587 : }
588 :
589 0 : const bool bDoCreateGeometry (pObject->GetPage()->checkVisibility( rOriginal, rDisplayInfo, true));
590 :
591 0 : if ( ! bDoCreateGeometry
592 0 : && (pObject->GetObjInventor() != SdrInventor || pObject->GetObjIdentifier() != OBJ_PAGE))
593 : {
594 0 : return drawinglayer::primitive2d::Primitive2DSequence();
595 : }
596 :
597 0 : if (pObject->IsEmptyPresObj())
598 0 : return drawinglayer::primitive2d::Primitive2DSequence();
599 :
600 : return sdr::contact::ViewObjectContactRedirector::createRedirectedPrimitive2DSequence(
601 : rOriginal,
602 0 : rDisplayInfo);
603 : }
604 :
605 : } // end of anonymous namespace
606 :
607 :
608 : } // end of namespace ::sd
609 :
610 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|