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 : #include "PresenterTheme.hxx"
21 : #include "PresenterBitmapContainer.hxx"
22 : #include "PresenterCanvasHelper.hxx"
23 : #include "PresenterConfigurationAccess.hxx"
24 : #include "PresenterHelper.hxx"
25 : #include <com/sun/star/awt/Point.hpp>
26 : #include <com/sun/star/beans/UnknownPropertyException.hpp>
27 : #include <com/sun/star/deployment/XPackageInformationProvider.hpp>
28 : #include <com/sun/star/drawing/XPresenterHelper.hpp>
29 : #include <com/sun/star/lang/IllegalArgumentException.hpp>
30 : #include <com/sun/star/rendering/PanoseWeight.hpp>
31 : #include <com/sun/star/rendering/XBitmap.hpp>
32 : #include <com/sun/star/util/Color.hpp>
33 : #include <boost/bind.hpp>
34 : #include <map>
35 :
36 : using namespace ::com::sun::star;
37 : using namespace ::com::sun::star::uno;
38 : using namespace ::std;
39 : using ::rtl::OUString;
40 :
41 : #define A2S(s) (::rtl::OUString(s))
42 :
43 : namespace sdext { namespace presenter {
44 :
45 : namespace {
46 :
47 : class BorderSize
48 : {
49 : public:
50 : const static sal_Int32 mnInvalidValue = -10000;
51 :
52 0 : BorderSize (void) : mnLeft(mnInvalidValue),
53 : mnTop(mnInvalidValue),
54 : mnRight(mnInvalidValue),
55 0 : mnBottom(mnInvalidValue) {}
56 :
57 : sal_Int32 mnLeft;
58 : sal_Int32 mnTop;
59 : sal_Int32 mnRight;
60 : sal_Int32 mnBottom;
61 :
62 0 : vector<sal_Int32> ToVector (void)
63 : {
64 0 : vector<sal_Int32> aSequence (4);
65 0 : aSequence[0] = mnLeft == mnInvalidValue ? 0 : mnLeft;
66 0 : aSequence[1] = mnTop == mnInvalidValue ? 0 : mnTop;
67 0 : aSequence[2] = mnRight == mnInvalidValue ? 0 : mnRight;
68 0 : aSequence[3] = mnBottom == mnInvalidValue ? 0 : mnBottom;
69 0 : return aSequence;
70 : };
71 :
72 0 : void Merge (const BorderSize& rBorderSize)
73 : {
74 0 : if (mnLeft == mnInvalidValue)
75 0 : mnLeft = rBorderSize.mnLeft;
76 0 : if (mnTop == mnInvalidValue)
77 0 : mnTop = rBorderSize.mnTop;
78 0 : if (mnRight == mnInvalidValue)
79 0 : mnRight = rBorderSize.mnRight;
80 0 : if (mnBottom == mnInvalidValue)
81 0 : mnBottom = rBorderSize.mnBottom;
82 0 : }
83 : };
84 :
85 : /** Reading a theme from the configurations is done in various classes. The
86 : ReadContext gives access to frequently used objects and functions to make
87 : the configuration handling easier.
88 : */
89 : class ReadContext
90 : {
91 : public:
92 : Reference<XComponentContext> mxComponentContext;
93 : Reference<rendering::XCanvas> mxCanvas;
94 : Reference<drawing::XPresenterHelper> mxPresenterHelper;
95 :
96 : ReadContext (
97 : const Reference<XComponentContext>& rxContext,
98 : const Reference<rendering::XCanvas>& rxCanvas);
99 : ~ReadContext (void);
100 :
101 : /** Read data describing a font from the node that can be reached from
102 : the given root via the given path.
103 : @param rsFontPath
104 : May be empty.
105 : */
106 : static PresenterTheme::SharedFontDescriptor ReadFont (
107 : const css::uno::Reference<css::container::XHierarchicalNameAccess>& rxTheme,
108 : const ::rtl::OUString& rsFontPath,
109 : const PresenterTheme::SharedFontDescriptor& rpDefault);
110 : static PresenterTheme::SharedFontDescriptor ReadFont (
111 : const Reference<beans::XPropertySet>& rxFontProperties,
112 : const PresenterTheme::SharedFontDescriptor& rpDefault);
113 :
114 : ::boost::shared_ptr<PresenterTheme::Theme> ReadTheme (
115 : PresenterConfigurationAccess& rConfiguration,
116 : const OUString& rsThemeName);
117 :
118 : BorderSize ReadBorderSize (const Reference<container::XNameAccess>& rxNode);
119 :
120 : private:
121 : Any GetByName (
122 : const Reference<container::XNameAccess>& rxNode,
123 : const OUString& rsName) const;
124 : };
125 :
126 : /** A PaneStyle describes how a pane is rendered.
127 : */
128 : class PaneStyle
129 : {
130 : public:
131 : PaneStyle (void);
132 : ~PaneStyle (void);
133 :
134 : const SharedBitmapDescriptor GetBitmap (const OUString& sBitmapName) const;
135 :
136 : OUString msStyleName;
137 : ::boost::shared_ptr<PaneStyle> mpParentStyle;
138 : PresenterTheme::SharedFontDescriptor mpFont;
139 : BorderSize maInnerBorderSize;
140 : BorderSize maOuterBorderSize;
141 : ::boost::shared_ptr<PresenterBitmapContainer> mpBitmaps;
142 :
143 : PresenterTheme::SharedFontDescriptor GetFont (void) const;
144 :
145 : private:
146 :
147 : void UpdateBorderSize (BorderSize& rBorderSize, bool bInner);
148 : };
149 :
150 : typedef ::boost::shared_ptr<PaneStyle> SharedPaneStyle;
151 :
152 0 : class PaneStyleContainer : vector<SharedPaneStyle>
153 : {
154 : public:
155 : void Read (
156 : ReadContext& rReadContext,
157 : const Reference<container::XHierarchicalNameAccess>& rThemeRoot);
158 :
159 : SharedPaneStyle GetPaneStyle (const OUString& rsStyleName) const;
160 :
161 : private:
162 : void ProcessPaneStyle (
163 : ReadContext& rReadContext,
164 : const ::rtl::OUString& rsKey,
165 : const ::std::vector<css::uno::Any>& rValues);
166 : };
167 :
168 : /** A ViewStyle describes how a view is displayed.
169 : */
170 : class ViewStyle
171 : {
172 : public:
173 : ViewStyle (void);
174 : ~ViewStyle (void);
175 :
176 : const SharedBitmapDescriptor GetBitmap (const OUString& sBitmapName) const;
177 :
178 : PresenterTheme::SharedFontDescriptor GetFont (void) const;
179 :
180 : OUString msStyleName;
181 : ::boost::shared_ptr<ViewStyle> mpParentStyle;
182 : PresenterTheme::SharedFontDescriptor mpFont;
183 : ::boost::shared_ptr<PresenterBitmapContainer> mpBitmaps;
184 : SharedBitmapDescriptor mpBackground;
185 : };
186 :
187 : typedef ::boost::shared_ptr<ViewStyle> SharedViewStyle;
188 :
189 0 : class ViewStyleContainer : vector<SharedViewStyle>
190 : {
191 : public:
192 : void Read (
193 : ReadContext& rReadContext,
194 : const Reference<container::XHierarchicalNameAccess>& rThemeRoot);
195 :
196 : SharedViewStyle GetViewStyle (const OUString& rsStyleName) const;
197 :
198 : private:
199 : void ProcessViewStyle(
200 : ReadContext& rReadContext,
201 : const Reference<beans::XPropertySet>& rxProperties);
202 : };
203 :
204 : class ViewDescriptor
205 : {
206 : };
207 : typedef ::boost::shared_ptr<ViewDescriptor> SharedViewDescriptor;
208 : typedef ::std::vector<SharedViewDescriptor> ViewDescriptorContainer;
209 :
210 0 : class StyleAssociationContainer
211 : {
212 : public:
213 : void Read (
214 : ReadContext& rReadContext,
215 : const Reference<container::XHierarchicalNameAccess>& rThemeRoot);
216 :
217 : OUString GetStyleName (const OUString& rsResourceName) const;
218 :
219 : private:
220 : typedef map<OUString, OUString> StyleAssociations;
221 : StyleAssociations maStyleAssociations;
222 :
223 : void ProcessStyleAssociation(
224 : ReadContext& rReadContext,
225 : const ::rtl::OUString& rsKey,
226 : const ::std::vector<css::uno::Any>& rValues);
227 : };
228 :
229 : } // end of anonymous namespace
230 :
231 : class PresenterTheme::Theme
232 : {
233 : public:
234 : Theme (
235 : const OUString& rsName,
236 : const Reference<container::XHierarchicalNameAccess>& rThemeRoot,
237 : const OUString& rsNodeName);
238 : ~Theme (void);
239 :
240 : void Read (
241 : PresenterConfigurationAccess& rConfiguration,
242 : ReadContext& rReadContext);
243 :
244 : OUString msThemeName;
245 : OUString msConfigurationNodeName;
246 : ::boost::shared_ptr<Theme> mpParentTheme;
247 : SharedBitmapDescriptor mpBackground;
248 : PaneStyleContainer maPaneStyles;
249 : ViewStyleContainer maViewStyles;
250 : ViewDescriptorContainer maViewDescriptors;
251 : StyleAssociationContainer maStyleAssociations;
252 : Reference<container::XHierarchicalNameAccess> mxThemeRoot;
253 : ::boost::shared_ptr<PresenterBitmapContainer> mpIconContainer;
254 : typedef map<rtl::OUString,SharedFontDescriptor> FontContainer;
255 : FontContainer maFontContainer;
256 :
257 : SharedPaneStyle GetPaneStyle (const OUString& rsStyleName) const;
258 : SharedViewStyle GetViewStyle (const OUString& rsStyleName) const;
259 :
260 : private:
261 : void ProcessFont(
262 : ReadContext& rReadContext,
263 : const OUString& rsKey,
264 : const Reference<beans::XPropertySet>& rxProperties);
265 : };
266 :
267 : //===== PresenterTheme ========================================================
268 :
269 0 : PresenterTheme::PresenterTheme (
270 : const css::uno::Reference<css::uno::XComponentContext>& rxContext,
271 : const rtl::OUString& rsThemeName,
272 : const css::uno::Reference<css::rendering::XCanvas>& rxCanvas)
273 : : mxContext(rxContext),
274 : msThemeName(rsThemeName),
275 : mpTheme(),
276 : mpBitmapContainer(),
277 0 : mxCanvas(rxCanvas)
278 : {
279 0 : mpTheme = ReadTheme();
280 0 : }
281 :
282 0 : PresenterTheme::~PresenterTheme (void)
283 : {
284 0 : }
285 :
286 0 : ::boost::shared_ptr<PresenterTheme::Theme> PresenterTheme::ReadTheme (void)
287 : {
288 0 : ReadContext aReadContext(mxContext, mxCanvas);
289 :
290 : PresenterConfigurationAccess aConfiguration (
291 : mxContext,
292 : OUString("/org.openoffice.Office.PresenterScreen/"),
293 0 : PresenterConfigurationAccess::READ_ONLY);
294 :
295 0 : return aReadContext.ReadTheme(aConfiguration, msThemeName);
296 : }
297 :
298 0 : bool PresenterTheme::HasCanvas (void) const
299 : {
300 0 : return mxCanvas.is();
301 : }
302 :
303 0 : void PresenterTheme::ProvideCanvas (const Reference<rendering::XCanvas>& rxCanvas)
304 : {
305 0 : if ( ! mxCanvas.is() && rxCanvas.is())
306 : {
307 0 : mxCanvas = rxCanvas;
308 0 : ReadTheme();
309 : }
310 0 : }
311 :
312 0 : OUString PresenterTheme::GetStyleName (const ::rtl::OUString& rsResourceURL) const
313 : {
314 0 : OUString sStyleName;
315 0 : ::boost::shared_ptr<Theme> pTheme (mpTheme);
316 0 : while (sStyleName.isEmpty() && pTheme.get()!=NULL)
317 : {
318 0 : sStyleName = pTheme->maStyleAssociations.GetStyleName(rsResourceURL);
319 0 : pTheme = pTheme->mpParentTheme;
320 : }
321 0 : return sStyleName;
322 : }
323 :
324 0 : ::std::vector<sal_Int32> PresenterTheme::GetBorderSize (
325 : const ::rtl::OUString& rsStyleName,
326 : const bool bOuter) const
327 : {
328 : OSL_ASSERT(mpTheme.get() != NULL);
329 :
330 0 : SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
331 0 : if (pPaneStyle.get() != NULL)
332 0 : if (bOuter)
333 0 : return pPaneStyle->maOuterBorderSize.ToVector();
334 : else
335 0 : return pPaneStyle->maInnerBorderSize.ToVector();
336 : else
337 : {
338 0 : return ::std::vector<sal_Int32>(4,0);
339 0 : }
340 : }
341 :
342 0 : PresenterTheme::SharedFontDescriptor PresenterTheme::ReadFont (
343 : const Reference<container::XHierarchicalNameAccess>& rxNode,
344 : const OUString& rsFontPath,
345 : const PresenterTheme::SharedFontDescriptor& rpDefault)
346 : {
347 0 : return ReadContext::ReadFont(rxNode, rsFontPath, rpDefault);
348 : }
349 :
350 0 : bool PresenterTheme::ConvertToColor (
351 : const Any& rColorSequence,
352 : sal_uInt32& rColor)
353 : {
354 0 : Sequence<sal_Int8> aByteSequence;
355 0 : if (rColorSequence >>= aByteSequence)
356 : {
357 0 : const sal_Int32 nByteCount (aByteSequence.getLength());
358 0 : const sal_uInt8* pArray = reinterpret_cast<const sal_uInt8*>(aByteSequence.getConstArray());
359 0 : rColor = 0;
360 0 : for (sal_Int32 nIndex=0; nIndex<nByteCount; ++nIndex)
361 : {
362 0 : rColor = (rColor << 8) | *pArray++;
363 : }
364 0 : return true;
365 : }
366 : else
367 0 : return false;
368 : }
369 :
370 0 : ::boost::shared_ptr<PresenterConfigurationAccess> PresenterTheme::GetNodeForViewStyle (
371 : const ::rtl::OUString& rsStyleName) const
372 : {
373 0 : if (mpTheme.get() == NULL)
374 0 : return ::boost::shared_ptr<PresenterConfigurationAccess>();
375 :
376 : // Open configuration for writing.
377 : ::boost::shared_ptr<PresenterConfigurationAccess> pConfiguration (
378 : new PresenterConfigurationAccess(
379 : mxContext,
380 : OUString("/org.openoffice.Office.PresenterScreen/"),
381 0 : PresenterConfigurationAccess::READ_WRITE));
382 :
383 : // Get configuration node for the view style container of the current
384 : // theme.
385 0 : if (pConfiguration->GoToChild( OUString(
386 0 : "Presenter/Themes/" + mpTheme->msConfigurationNodeName + "/ViewStyles")))
387 : {
388 : pConfiguration->GoToChild(
389 : ::boost::bind(&PresenterConfigurationAccess::IsStringPropertyEqual,
390 : rsStyleName,
391 : A2S("StyleName"),
392 0 : _2));
393 : }
394 0 : return pConfiguration;
395 : }
396 :
397 0 : SharedBitmapDescriptor PresenterTheme::GetBitmap (
398 : const OUString& rsStyleName,
399 : const OUString& rsBitmapName) const
400 : {
401 0 : if (mpTheme.get() != NULL)
402 : {
403 0 : if (rsStyleName.isEmpty())
404 : {
405 0 : if (rsBitmapName == A2S("Background"))
406 : {
407 0 : ::boost::shared_ptr<Theme> pTheme (mpTheme);
408 0 : while (pTheme.get()!=NULL && pTheme->mpBackground.get()==NULL)
409 0 : pTheme = pTheme->mpParentTheme;
410 0 : if (pTheme.get() != NULL)
411 0 : return pTheme->mpBackground;
412 : else
413 0 : return SharedBitmapDescriptor();
414 : }
415 : }
416 : else
417 : {
418 0 : SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
419 0 : if (pPaneStyle.get() != NULL)
420 : {
421 0 : SharedBitmapDescriptor pBitmap (pPaneStyle->GetBitmap(rsBitmapName));
422 0 : if (pBitmap.get() != NULL)
423 0 : return pBitmap;
424 : }
425 :
426 0 : SharedViewStyle pViewStyle (mpTheme->GetViewStyle(rsStyleName));
427 0 : if (pViewStyle.get() != NULL)
428 : {
429 0 : SharedBitmapDescriptor pBitmap (pViewStyle->GetBitmap(rsBitmapName));
430 0 : if (pBitmap.get() != NULL)
431 0 : return pBitmap;
432 0 : }
433 : }
434 : }
435 :
436 0 : return SharedBitmapDescriptor();
437 : }
438 :
439 0 : SharedBitmapDescriptor PresenterTheme::GetBitmap (
440 : const OUString& rsBitmapName) const
441 : {
442 0 : if (mpTheme.get() != NULL)
443 : {
444 0 : if (rsBitmapName == A2S("Background"))
445 : {
446 0 : ::boost::shared_ptr<Theme> pTheme (mpTheme);
447 0 : while (pTheme.get()!=NULL && pTheme->mpBackground.get()==NULL)
448 0 : pTheme = pTheme->mpParentTheme;
449 0 : if (pTheme.get() != NULL)
450 0 : return pTheme->mpBackground;
451 : else
452 0 : return SharedBitmapDescriptor();
453 : }
454 : else
455 : {
456 0 : if (mpTheme->mpIconContainer.get() != NULL)
457 0 : return mpTheme->mpIconContainer->GetBitmap(rsBitmapName);
458 : }
459 : }
460 :
461 0 : return SharedBitmapDescriptor();
462 : }
463 :
464 0 : ::boost::shared_ptr<PresenterBitmapContainer> PresenterTheme::GetBitmapContainer (void) const
465 : {
466 0 : if (mpTheme.get() != NULL)
467 0 : return mpTheme->mpIconContainer;
468 : else
469 0 : return ::boost::shared_ptr<PresenterBitmapContainer>();
470 : }
471 :
472 0 : PresenterTheme::SharedFontDescriptor PresenterTheme::GetFont (
473 : const OUString& rsStyleName) const
474 : {
475 0 : if (mpTheme.get() != NULL)
476 : {
477 0 : SharedPaneStyle pPaneStyle (mpTheme->GetPaneStyle(rsStyleName));
478 0 : if (pPaneStyle.get() != NULL)
479 0 : return pPaneStyle->GetFont();
480 :
481 0 : SharedViewStyle pViewStyle (mpTheme->GetViewStyle(rsStyleName));
482 0 : if (pViewStyle.get() != NULL)
483 0 : return pViewStyle->GetFont();
484 :
485 0 : ::boost::shared_ptr<Theme> pTheme (mpTheme);
486 0 : while (pTheme.get() != NULL)
487 : {
488 0 : Theme::FontContainer::const_iterator iFont (pTheme->maFontContainer.find(rsStyleName));
489 0 : if (iFont != pTheme->maFontContainer.end())
490 0 : return iFont->second;
491 :
492 0 : pTheme = pTheme->mpParentTheme;
493 0 : }
494 : }
495 :
496 0 : return SharedFontDescriptor();
497 : }
498 :
499 : //===== FontDescriptor ========================================================
500 :
501 0 : PresenterTheme::FontDescriptor::FontDescriptor (
502 : const ::boost::shared_ptr<FontDescriptor>& rpDescriptor)
503 : : msFamilyName(),
504 : msStyleName(),
505 : mnSize(12),
506 : mnColor(0x00000000),
507 : msAnchor(OUString("Left")),
508 : mnXOffset(0),
509 0 : mnYOffset(0)
510 : {
511 0 : if (rpDescriptor.get() != NULL)
512 : {
513 0 : msFamilyName = rpDescriptor->msFamilyName;
514 0 : msStyleName = rpDescriptor->msStyleName;
515 0 : mnSize = rpDescriptor->mnSize;
516 0 : mnColor = rpDescriptor->mnColor;
517 0 : msAnchor = rpDescriptor->msAnchor;
518 0 : mnXOffset = rpDescriptor->mnXOffset;
519 0 : mnYOffset = rpDescriptor->mnYOffset;
520 : }
521 0 : }
522 :
523 0 : bool PresenterTheme::FontDescriptor::PrepareFont (
524 : const Reference<rendering::XCanvas>& rxCanvas)
525 : {
526 0 : if (mxFont.is())
527 0 : return true;
528 :
529 0 : if ( ! rxCanvas.is())
530 0 : return false;
531 :
532 0 : const double nCellSize (GetCellSizeForDesignSize(rxCanvas, mnSize));
533 0 : mxFont = CreateFont(rxCanvas, nCellSize);
534 :
535 0 : return mxFont.is();
536 : }
537 :
538 0 : Reference<rendering::XCanvasFont> PresenterTheme::FontDescriptor::CreateFont (
539 : const Reference<rendering::XCanvas>& rxCanvas,
540 : const double nCellSize) const
541 : {
542 0 : rendering::FontRequest aFontRequest;
543 0 : aFontRequest.FontDescription.FamilyName = msFamilyName;
544 0 : if (msFamilyName.isEmpty())
545 0 : aFontRequest.FontDescription.FamilyName = A2S("Tahoma");
546 0 : aFontRequest.FontDescription.StyleName = msStyleName;
547 0 : aFontRequest.CellSize = nCellSize;
548 :
549 : // Make an attempt at translating the style name(s)into a corresponding
550 : // font description.
551 0 : if (msStyleName == A2S("Bold"))
552 0 : aFontRequest.FontDescription.FontDescription.Weight = rendering::PanoseWeight::HEAVY;
553 :
554 0 : return rxCanvas->createFont(
555 : aFontRequest,
556 : Sequence<beans::PropertyValue>(),
557 0 : geometry::Matrix2D(1,0,0,1));
558 : }
559 :
560 0 : double PresenterTheme::FontDescriptor::GetCellSizeForDesignSize (
561 : const Reference<rendering::XCanvas>& rxCanvas,
562 : const double nDesignSize) const
563 : {
564 : // Use the given design size as initial value in calculating the cell
565 : // size.
566 0 : double nCellSize (nDesignSize);
567 :
568 0 : if ( ! rxCanvas.is())
569 : {
570 : // We need the canvas to do the conversion. Return the design size,
571 : // it is the our best guess in this circumstance.
572 0 : return nDesignSize;
573 : }
574 :
575 0 : Reference<rendering::XCanvasFont> xFont (CreateFont(rxCanvas, nCellSize));
576 0 : if ( ! xFont.is())
577 0 : return nDesignSize;
578 :
579 0 : geometry::RealRectangle2D aBox (PresenterCanvasHelper::GetTextBoundingBox (xFont, A2S("X")));
580 :
581 0 : const double nAscent (-aBox.Y1);
582 0 : const double nDescent (aBox.Y2);
583 0 : const double nScale = (nAscent+nDescent) / nAscent;
584 0 : return nDesignSize * nScale;
585 : }
586 :
587 : //===== Theme =================================================================
588 :
589 0 : PresenterTheme::Theme::Theme (
590 : const OUString& rsName,
591 : const Reference<container::XHierarchicalNameAccess>& rxThemeRoot,
592 : const OUString& rsNodeName)
593 : : msThemeName(rsName),
594 : msConfigurationNodeName(rsNodeName),
595 : mpParentTheme(),
596 : maPaneStyles(),
597 : maViewStyles(),
598 : maStyleAssociations(),
599 : mxThemeRoot(rxThemeRoot),
600 0 : mpIconContainer()
601 : {
602 0 : }
603 :
604 0 : PresenterTheme::Theme::~Theme (void)
605 : {
606 0 : }
607 :
608 0 : void PresenterTheme::Theme::Read (
609 : PresenterConfigurationAccess& rConfiguration,
610 : ReadContext& rReadContext)
611 : {
612 : PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, A2S("ThemeName"))
613 0 : >>= msThemeName;
614 :
615 : // Parent theme name.
616 0 : OUString sParentThemeName;
617 0 : if ((PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, A2S("ParentTheme"))
618 0 : >>= sParentThemeName)
619 0 : && !sParentThemeName.isEmpty())
620 : {
621 0 : mpParentTheme = rReadContext.ReadTheme(rConfiguration, sParentThemeName);
622 : }
623 :
624 : // Background.
625 : mpBackground = PresenterBitmapContainer::LoadBitmap(
626 : mxThemeRoot,
627 : A2S("Background"),
628 : rReadContext.mxPresenterHelper,
629 : rReadContext.mxCanvas,
630 0 : SharedBitmapDescriptor());
631 :
632 : // Style associations.
633 0 : maStyleAssociations.Read(rReadContext, mxThemeRoot);
634 :
635 : // Pane styles.
636 0 : maPaneStyles.Read(rReadContext, mxThemeRoot);
637 :
638 : // View styles.
639 0 : maViewStyles.Read(rReadContext, mxThemeRoot);
640 :
641 : // Read bitmaps.
642 : mpIconContainer.reset(
643 : new PresenterBitmapContainer(
644 : Reference<container::XNameAccess>(
645 : PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, A2S("Bitmaps")),
646 : UNO_QUERY),
647 0 : mpParentTheme.get()!=NULL
648 0 : ? mpParentTheme->mpIconContainer
649 : : ::boost::shared_ptr<PresenterBitmapContainer>(),
650 : rReadContext.mxComponentContext,
651 0 : rReadContext.mxCanvas));
652 :
653 : // Read fonts.
654 : Reference<container::XNameAccess> xFontNode(
655 : PresenterConfigurationAccess::GetConfigurationNode(mxThemeRoot, A2S("Fonts")),
656 0 : UNO_QUERY);
657 : PresenterConfigurationAccess::ForAll(
658 : xFontNode,
659 : ::boost::bind(&PresenterTheme::Theme::ProcessFont,
660 0 : this, ::boost::ref(rReadContext), _1, _2));
661 0 : }
662 :
663 0 : SharedPaneStyle PresenterTheme::Theme::GetPaneStyle (const OUString& rsStyleName) const
664 : {
665 0 : SharedPaneStyle pPaneStyle (maPaneStyles.GetPaneStyle(rsStyleName));
666 0 : if (pPaneStyle.get() != NULL)
667 0 : return pPaneStyle;
668 0 : else if (mpParentTheme.get() != NULL)
669 0 : return mpParentTheme->GetPaneStyle(rsStyleName);
670 : else
671 0 : return SharedPaneStyle();
672 : }
673 :
674 0 : SharedViewStyle PresenterTheme::Theme::GetViewStyle (const OUString& rsStyleName) const
675 : {
676 0 : SharedViewStyle pViewStyle (maViewStyles.GetViewStyle(rsStyleName));
677 0 : if (pViewStyle.get() != NULL)
678 0 : return pViewStyle;
679 0 : else if (mpParentTheme.get() != NULL)
680 0 : return mpParentTheme->GetViewStyle(rsStyleName);
681 : else
682 0 : return SharedViewStyle();
683 : }
684 :
685 0 : void PresenterTheme::Theme::ProcessFont(
686 : ReadContext& rReadContext,
687 : const OUString& rsKey,
688 : const Reference<beans::XPropertySet>& rxProperties)
689 : {
690 : (void)rReadContext;
691 0 : maFontContainer[rsKey] = ReadContext::ReadFont(rxProperties, SharedFontDescriptor());
692 0 : }
693 :
694 : namespace {
695 :
696 : //===== ReadContext ===========================================================
697 :
698 0 : ReadContext::ReadContext (
699 : const css::uno::Reference<css::uno::XComponentContext>& rxContext,
700 : const Reference<rendering::XCanvas>& rxCanvas)
701 : : mxComponentContext(rxContext),
702 : mxCanvas(rxCanvas),
703 0 : mxPresenterHelper()
704 : {
705 0 : Reference<lang::XMultiComponentFactory> xFactory (rxContext->getServiceManager());
706 0 : if (xFactory.is())
707 : {
708 : mxPresenterHelper = Reference<drawing::XPresenterHelper>(
709 0 : xFactory->createInstanceWithContext(
710 : OUString("com.sun.star.comp.Draw.PresenterHelper"),
711 0 : rxContext),
712 0 : UNO_QUERY_THROW);
713 0 : }
714 0 : }
715 :
716 0 : ReadContext::~ReadContext (void)
717 : {
718 0 : }
719 :
720 0 : PresenterTheme::SharedFontDescriptor ReadContext::ReadFont (
721 : const Reference<container::XHierarchicalNameAccess>& rxNode,
722 : const OUString& rsFontPath,
723 : const PresenterTheme::SharedFontDescriptor& rpDefault)
724 : {
725 0 : if ( ! rxNode.is())
726 0 : return PresenterTheme::SharedFontDescriptor();
727 :
728 : try
729 : {
730 : Reference<container::XHierarchicalNameAccess> xFont (
731 : PresenterConfigurationAccess::GetConfigurationNode(
732 : rxNode,
733 : rsFontPath),
734 0 : UNO_QUERY_THROW);
735 :
736 0 : Reference<beans::XPropertySet> xProperties (xFont, UNO_QUERY_THROW);
737 0 : return ReadFont(xProperties, rpDefault);
738 : }
739 0 : catch (Exception&)
740 : {
741 : OSL_ASSERT(false);
742 : }
743 :
744 0 : return PresenterTheme::SharedFontDescriptor();
745 : }
746 :
747 0 : PresenterTheme::SharedFontDescriptor ReadContext::ReadFont (
748 : const Reference<beans::XPropertySet>& rxProperties,
749 : const PresenterTheme::SharedFontDescriptor& rpDefault)
750 : {
751 : ::boost::shared_ptr<PresenterTheme::FontDescriptor> pDescriptor (
752 0 : new PresenterTheme::FontDescriptor(rpDefault));
753 :
754 0 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("FamilyName")) >>= pDescriptor->msFamilyName;
755 0 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("Style")) >>= pDescriptor->msStyleName;
756 0 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("Size")) >>= pDescriptor->mnSize;
757 : PresenterTheme::ConvertToColor(
758 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("Color")),
759 0 : pDescriptor->mnColor);
760 0 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("Anchor")) >>= pDescriptor->msAnchor;
761 0 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("XOffset")) >>= pDescriptor->mnXOffset;
762 0 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("YOffset")) >>= pDescriptor->mnYOffset;
763 :
764 0 : return pDescriptor;
765 : }
766 :
767 0 : Any ReadContext::GetByName (
768 : const Reference<container::XNameAccess>& rxNode,
769 : const OUString& rsName) const
770 : {
771 : OSL_ASSERT(rxNode.is());
772 0 : if (rxNode->hasByName(rsName))
773 0 : return rxNode->getByName(rsName);
774 : else
775 0 : return Any();
776 : }
777 :
778 0 : ::boost::shared_ptr<PresenterTheme::Theme> ReadContext::ReadTheme (
779 : PresenterConfigurationAccess& rConfiguration,
780 : const OUString& rsThemeName)
781 : {
782 0 : ::boost::shared_ptr<PresenterTheme::Theme> pTheme;
783 :
784 0 : OUString sCurrentThemeName (rsThemeName);
785 0 : if (sCurrentThemeName.isEmpty())
786 : {
787 : // No theme name given. Look up the CurrentTheme property.
788 0 : rConfiguration.GetConfigurationNode(A2S("Presenter/CurrentTheme")) >>= sCurrentThemeName;
789 0 : if (sCurrentThemeName.isEmpty())
790 : {
791 : // Still no name. Use "DefaultTheme".
792 0 : sCurrentThemeName = A2S("DefaultTheme");
793 : }
794 : }
795 :
796 : Reference<container::XNameAccess> xThemes (
797 : rConfiguration.GetConfigurationNode(A2S("Presenter/Themes")),
798 0 : UNO_QUERY);
799 0 : if (xThemes.is())
800 : {
801 : // Iterate over all themes and search the one with the given name.
802 0 : Sequence<OUString> aKeys (xThemes->getElementNames());
803 0 : for (sal_Int32 nItemIndex=0; nItemIndex < aKeys.getLength(); ++nItemIndex)
804 : {
805 0 : const OUString& rsKey (aKeys[nItemIndex]);
806 : Reference<container::XHierarchicalNameAccess> xTheme (
807 0 : xThemes->getByName(rsKey), UNO_QUERY);
808 0 : if (xTheme.is())
809 : {
810 0 : OUString sThemeName;
811 : PresenterConfigurationAccess::GetConfigurationNode(xTheme, A2S("ThemeName"))
812 0 : >>= sThemeName;
813 0 : if (sThemeName == sCurrentThemeName)
814 : {
815 0 : pTheme.reset(new PresenterTheme::Theme(sThemeName,xTheme,rsKey));
816 : break;
817 0 : }
818 : }
819 0 : }
820 : }
821 :
822 0 : if (pTheme.get() != NULL)
823 : {
824 0 : pTheme->Read(rConfiguration, *this);
825 : }
826 :
827 0 : return pTheme;
828 : }
829 :
830 0 : BorderSize ReadContext::ReadBorderSize (const Reference<container::XNameAccess>& rxNode)
831 : {
832 0 : BorderSize aBorderSize;
833 :
834 0 : if (rxNode.is())
835 : {
836 0 : GetByName(rxNode, A2S("Left")) >>= aBorderSize.mnLeft;
837 0 : GetByName(rxNode, A2S("Top")) >>= aBorderSize.mnTop;
838 0 : GetByName(rxNode, A2S("Right")) >>= aBorderSize.mnRight;
839 0 : GetByName(rxNode, A2S("Bottom")) >>= aBorderSize.mnBottom;
840 : }
841 :
842 0 : return aBorderSize;
843 : }
844 :
845 : //===== PaneStyleContainer ====================================================
846 :
847 0 : void PaneStyleContainer::Read (
848 : ReadContext& rReadContext,
849 : const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
850 : {
851 : Reference<container::XNameAccess> xPaneStyleList (
852 : PresenterConfigurationAccess::GetConfigurationNode(
853 : rxThemeRoot,
854 : A2S("PaneStyles")),
855 0 : UNO_QUERY);
856 0 : if (xPaneStyleList.is())
857 : {
858 0 : ::std::vector<rtl::OUString> aProperties;
859 0 : aProperties.reserve(6);
860 0 : aProperties.push_back(A2S("StyleName"));
861 0 : aProperties.push_back(A2S("ParentStyle"));
862 0 : aProperties.push_back(A2S("TitleFont"));
863 0 : aProperties.push_back(A2S("InnerBorderSize"));
864 0 : aProperties.push_back(A2S("OuterBorderSize"));
865 0 : aProperties.push_back(A2S("BorderBitmapList"));
866 : PresenterConfigurationAccess::ForAll(
867 : xPaneStyleList,
868 : aProperties,
869 : ::boost::bind(&PaneStyleContainer::ProcessPaneStyle,
870 0 : this, ::boost::ref(rReadContext), _1, _2));
871 0 : }
872 0 : }
873 :
874 0 : void PaneStyleContainer::ProcessPaneStyle(
875 : ReadContext& rReadContext,
876 : const OUString& rsKey,
877 : const ::std::vector<Any>& rValues)
878 : {
879 : (void)rsKey;
880 :
881 0 : if (rValues.size() != 6)
882 0 : return;
883 :
884 0 : ::boost::shared_ptr<PaneStyle> pStyle (new PaneStyle());
885 :
886 0 : rValues[0] >>= pStyle->msStyleName;
887 :
888 0 : OUString sParentStyleName;
889 0 : if (rValues[1] >>= sParentStyleName)
890 : {
891 : // Find parent style.
892 0 : PaneStyleContainer::const_iterator iStyle;
893 0 : for (iStyle=begin(); iStyle!=end(); ++iStyle)
894 0 : if ((*iStyle)->msStyleName.equals(sParentStyleName))
895 : {
896 0 : pStyle->mpParentStyle = *iStyle;
897 0 : break;
898 : }
899 : }
900 :
901 0 : Reference<container::XHierarchicalNameAccess> xFontNode (rValues[2], UNO_QUERY);
902 0 : pStyle->mpFont = rReadContext.ReadFont(
903 0 : xFontNode, A2S(""), PresenterTheme::SharedFontDescriptor());
904 :
905 0 : Reference<container::XNameAccess> xInnerBorderSizeNode (rValues[3], UNO_QUERY);
906 0 : pStyle->maInnerBorderSize = rReadContext.ReadBorderSize(xInnerBorderSizeNode);
907 0 : Reference<container::XNameAccess> xOuterBorderSizeNode (rValues[4], UNO_QUERY);
908 0 : pStyle->maOuterBorderSize = rReadContext.ReadBorderSize(xOuterBorderSizeNode);
909 :
910 0 : if (pStyle->mpParentStyle.get() != NULL)
911 : {
912 0 : pStyle->maInnerBorderSize.Merge(pStyle->mpParentStyle->maInnerBorderSize);
913 0 : pStyle->maOuterBorderSize.Merge(pStyle->mpParentStyle->maOuterBorderSize);
914 : }
915 :
916 0 : if (rReadContext.mxCanvas.is())
917 : {
918 0 : Reference<container::XNameAccess> xBitmapsNode (rValues[5], UNO_QUERY);
919 0 : pStyle->mpBitmaps.reset(new PresenterBitmapContainer(
920 : xBitmapsNode,
921 0 : pStyle->mpParentStyle.get()!=NULL
922 0 : ? pStyle->mpParentStyle->mpBitmaps
923 : : ::boost::shared_ptr<PresenterBitmapContainer>(),
924 : rReadContext.mxComponentContext,
925 : rReadContext.mxCanvas,
926 0 : rReadContext.mxPresenterHelper));
927 : }
928 :
929 0 : push_back(pStyle);
930 : }
931 :
932 0 : SharedPaneStyle PaneStyleContainer::GetPaneStyle (const OUString& rsStyleName) const
933 : {
934 0 : const_iterator iEnd (end());
935 0 : for (const_iterator iStyle=begin(); iStyle!=iEnd; ++iStyle)
936 0 : if ((*iStyle)->msStyleName == rsStyleName)
937 0 : return *iStyle;
938 0 : return SharedPaneStyle();
939 : }
940 :
941 : //===== PaneStyle =============================================================
942 :
943 0 : PaneStyle::PaneStyle (void)
944 : : msStyleName(),
945 : mpParentStyle(),
946 : mpFont(),
947 : maInnerBorderSize(),
948 : maOuterBorderSize(),
949 0 : mpBitmaps()
950 : {
951 0 : }
952 :
953 0 : PaneStyle::~PaneStyle (void)
954 : {
955 0 : }
956 :
957 0 : void PaneStyle::UpdateBorderSize (BorderSize& rBorderSize, bool bInner)
958 : {
959 0 : if (mpParentStyle.get() != NULL)
960 0 : mpParentStyle->UpdateBorderSize(rBorderSize, bInner);
961 :
962 0 : BorderSize& rThisBorderSize (bInner ? maInnerBorderSize : maOuterBorderSize);
963 0 : if (rThisBorderSize.mnLeft >= 0)
964 0 : rBorderSize.mnLeft = rThisBorderSize.mnLeft;
965 0 : if (rThisBorderSize.mnTop >= 0)
966 0 : rBorderSize.mnTop = rThisBorderSize.mnTop;
967 0 : if (rThisBorderSize.mnRight >= 0)
968 0 : rBorderSize.mnRight = rThisBorderSize.mnRight;
969 0 : if (rThisBorderSize.mnBottom >= 0)
970 0 : rBorderSize.mnBottom = rThisBorderSize.mnBottom;
971 0 : }
972 :
973 0 : const SharedBitmapDescriptor PaneStyle::GetBitmap (const OUString& rsBitmapName) const
974 : {
975 0 : if (mpBitmaps.get() != NULL)
976 : {
977 0 : const SharedBitmapDescriptor pBitmap = mpBitmaps->GetBitmap(rsBitmapName);
978 0 : if (pBitmap.get() != NULL)
979 0 : return pBitmap;
980 : }
981 :
982 0 : if (mpParentStyle.get() != NULL)
983 0 : return mpParentStyle->GetBitmap(rsBitmapName);
984 : else
985 0 : return SharedBitmapDescriptor();
986 : }
987 :
988 0 : PresenterTheme::SharedFontDescriptor PaneStyle::GetFont (void) const
989 : {
990 0 : if (mpFont.get() != NULL)
991 0 : return mpFont;
992 0 : else if (mpParentStyle.get() != NULL)
993 0 : return mpParentStyle->GetFont();
994 : else
995 0 : return PresenterTheme::SharedFontDescriptor();
996 : }
997 :
998 : //===== ViewStyleContainer ====================================================
999 :
1000 0 : void ViewStyleContainer::Read (
1001 : ReadContext& rReadContext,
1002 : const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
1003 : {
1004 : (void)rReadContext;
1005 :
1006 : Reference<container::XNameAccess> xViewStyleList (
1007 : PresenterConfigurationAccess::GetConfigurationNode(
1008 : rxThemeRoot,
1009 : A2S("ViewStyles")),
1010 0 : UNO_QUERY);
1011 0 : if (xViewStyleList.is())
1012 : {
1013 : PresenterConfigurationAccess::ForAll(
1014 : xViewStyleList,
1015 : ::boost::bind(&ViewStyleContainer::ProcessViewStyle,
1016 0 : this, ::boost::ref(rReadContext), _2));
1017 0 : }
1018 0 : }
1019 :
1020 0 : void ViewStyleContainer::ProcessViewStyle(
1021 : ReadContext& rReadContext,
1022 : const Reference<beans::XPropertySet>& rxProperties)
1023 : {
1024 0 : ::boost::shared_ptr<ViewStyle> pStyle (new ViewStyle());
1025 :
1026 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("StyleName"))
1027 0 : >>= pStyle->msStyleName;
1028 :
1029 0 : OUString sParentStyleName;
1030 0 : if (PresenterConfigurationAccess::GetProperty(rxProperties, A2S("ParentStyle"))
1031 0 : >>= sParentStyleName)
1032 : {
1033 : // Find parent style.
1034 0 : ViewStyleContainer::const_iterator iStyle;
1035 0 : for (iStyle=begin(); iStyle!=end(); ++iStyle)
1036 0 : if ((*iStyle)->msStyleName.equals(sParentStyleName))
1037 : {
1038 0 : pStyle->mpParentStyle = *iStyle;
1039 0 : pStyle->mpFont = (*iStyle)->mpFont;
1040 0 : pStyle->mpBackground = (*iStyle)->mpBackground;
1041 0 : break;
1042 : }
1043 : }
1044 :
1045 0 : const OUString sPathToFont; // empty string
1046 : Reference<container::XHierarchicalNameAccess> xFontNode (
1047 0 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("Font")), UNO_QUERY);
1048 : PresenterTheme::SharedFontDescriptor pFont (
1049 0 : rReadContext.ReadFont(xFontNode, sPathToFont, PresenterTheme::SharedFontDescriptor()));
1050 0 : if (pFont.get() != NULL)
1051 0 : pStyle->mpFont = pFont;
1052 :
1053 : Reference<container::XHierarchicalNameAccess> xBackgroundNode (
1054 : PresenterConfigurationAccess::GetProperty(rxProperties, A2S("Background")),
1055 0 : UNO_QUERY);
1056 : SharedBitmapDescriptor pBackground (PresenterBitmapContainer::LoadBitmap(
1057 : xBackgroundNode,
1058 : OUString(),
1059 : rReadContext.mxPresenterHelper,
1060 : rReadContext.mxCanvas,
1061 0 : SharedBitmapDescriptor()));
1062 0 : if (pBackground.get() != NULL && pBackground->GetNormalBitmap().is())
1063 0 : pStyle->mpBackground = pBackground;
1064 :
1065 0 : push_back(pStyle);
1066 0 : }
1067 :
1068 0 : SharedViewStyle ViewStyleContainer::GetViewStyle (const OUString& rsStyleName) const
1069 : {
1070 0 : const_iterator iEnd (end());
1071 0 : for (const_iterator iStyle=begin(); iStyle!=iEnd; ++iStyle)
1072 0 : if ((*iStyle)->msStyleName == rsStyleName)
1073 0 : return *iStyle;
1074 0 : return SharedViewStyle();
1075 : }
1076 :
1077 : //===== ViewStyle =============================================================
1078 :
1079 0 : ViewStyle::ViewStyle (void)
1080 : : msStyleName(),
1081 : mpParentStyle(),
1082 : mpFont(),
1083 0 : mpBackground()
1084 : {
1085 0 : }
1086 :
1087 0 : ViewStyle::~ViewStyle (void)
1088 : {
1089 0 : }
1090 :
1091 0 : const SharedBitmapDescriptor ViewStyle::GetBitmap (const OUString& rsBitmapName) const
1092 : {
1093 0 : if (rsBitmapName == A2S("Background"))
1094 0 : return mpBackground;
1095 : else
1096 0 : return SharedBitmapDescriptor();
1097 : }
1098 :
1099 0 : PresenterTheme::SharedFontDescriptor ViewStyle::GetFont (void) const
1100 : {
1101 0 : if (mpFont.get() != NULL)
1102 0 : return mpFont;
1103 0 : else if (mpParentStyle.get() != NULL)
1104 0 : return mpParentStyle->GetFont();
1105 : else
1106 0 : return PresenterTheme::SharedFontDescriptor();
1107 : }
1108 :
1109 : //===== StyleAssociationContainer =============================================
1110 :
1111 0 : void StyleAssociationContainer::Read (
1112 : ReadContext& rReadContext,
1113 : const Reference<container::XHierarchicalNameAccess>& rxThemeRoot)
1114 : {
1115 : Reference<container::XNameAccess> xStyleAssociationList (
1116 : PresenterConfigurationAccess::GetConfigurationNode(
1117 : rxThemeRoot,
1118 : A2S("StyleAssociations")),
1119 0 : UNO_QUERY);
1120 0 : if (xStyleAssociationList.is())
1121 : {
1122 0 : ::std::vector<rtl::OUString> aProperties (2);
1123 0 : aProperties[0] = A2S("ResourceURL");
1124 0 : aProperties[1] = A2S("StyleName");
1125 : PresenterConfigurationAccess::ForAll(
1126 : xStyleAssociationList,
1127 : aProperties,
1128 : ::boost::bind(&StyleAssociationContainer::ProcessStyleAssociation,
1129 0 : this, ::boost::ref(rReadContext), _1, _2));
1130 0 : }
1131 0 : }
1132 :
1133 0 : OUString StyleAssociationContainer::GetStyleName (const OUString& rsResourceName) const
1134 : {
1135 0 : StyleAssociations::const_iterator iAssociation (maStyleAssociations.find(rsResourceName));
1136 0 : if (iAssociation != maStyleAssociations.end())
1137 0 : return iAssociation->second;
1138 : else
1139 0 : return OUString();
1140 : }
1141 :
1142 0 : void StyleAssociationContainer::ProcessStyleAssociation(
1143 : ReadContext& rReadContext,
1144 : const OUString& rsKey,
1145 : const ::std::vector<Any>& rValues)
1146 : {
1147 : (void)rReadContext;
1148 : (void)rsKey;
1149 :
1150 0 : if (rValues.size() != 2)
1151 0 : return;
1152 :
1153 0 : OUString sResourceURL;
1154 0 : OUString sStyleName;
1155 0 : if ((rValues[0] >>= sResourceURL)
1156 0 : && (rValues[1] >>= sStyleName))
1157 : {
1158 0 : maStyleAssociations[sResourceURL] = sStyleName;
1159 0 : }
1160 : }
1161 :
1162 : } // end of anonymous namespace
1163 :
1164 0 : } } // end of namespace ::sdext::presenter
1165 :
1166 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|