summaryrefslogtreecommitdiff
path: root/sd/source/ui/slidesorter/view/SlsPageObjectViewObjectContact.cxx
blob: b1e4e80fdf1fd3988e0ff115445f5bb1565cf6d3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#include "precompiled_sd.hxx"

#include "view/SlsPageObjectViewObjectContact.hxx"

#include "controller/SlsProperties.hxx"
#include "view/SlideSorterView.hxx"
#include "view/SlsPageObjectViewContact.hxx"
#include "view/SlsPageObject.hxx"
#include "view/SlsFontProvider.hxx"
#include "model/SlsPageDescriptor.hxx"
#include "cache/SlsPageCache.hxx"
#include "cache/SlsPageCacheManager.hxx"
#include "res_bmp.hrc"
#include "tools/IconCache.hxx"
#include "PreviewRenderer.hxx"

#include "sdpage.hxx"
#include "sdresid.hxx"
#include "glob.hrc"
#include "drawdoc.hxx"
#include <svx/sdr/contact/displayinfo.hxx>
#include <svx/sdr/contact/viewcontact.hxx>
#include <svx/svdopage.hxx>
#include <svx/svdpagv.hxx>
#include <svx/xlndsit.hxx>
#include <svx/xlnclit.hxx>
#include <svx/svdoutl.hxx>
#include <svx/sdrpagewindow.hxx>
#include <vcl/bitmap.hxx>
#include <vcl/outdev.hxx>
#include <vcl/virdev.hxx>
#include <vcl/lineinfo.hxx>
#include <tools/color.hxx>
#include <boost/shared_ptr.hpp>
#include <com/sun/star/uno/Exception.hpp>
#include <vcl/svapp.hxx>
#include <tools/diagnose_ex.h>

using namespace ::sdr::contact;
using namespace ::sd::slidesorter::model;

using drawinglayer::primitive2d::Primitive2DReference;
using drawinglayer::primitive2d::Primitive2DSequence;

namespace sd { namespace slidesorter { namespace view {


const sal_Int32 PageObjectViewObjectContact::mnSelectionIndicatorOffset = 2;
const sal_Int32 PageObjectViewObjectContact::mnSelectionIndicatorThickness = 3;
const sal_Int32 PageObjectViewObjectContact::mnFocusIndicatorOffset = 3;
const sal_Int32 PageObjectViewObjectContact::mnFadeEffectIndicatorOffset = 9;
const sal_Int32 PageObjectViewObjectContact::mnFadeEffectIndicatorSize = 14;
const sal_Int32 PageObjectViewObjectContact::mnPageNumberOffset = 9;
const sal_Int32 PageObjectViewObjectContact::mnMouseOverEffectOffset = 3;
const sal_Int32 PageObjectViewObjectContact::mnMouseOverEffectThickness = 1;

PageObjectViewObjectContact::PageObjectViewObjectContact (
    ObjectContact& rObjectContact,
    ViewContact& rViewContact,
    const ::boost::shared_ptr<cache::PageCache>& rpCache,
    const ::boost::shared_ptr<controller::Properties>& rpProperties)
    : ViewObjectContactOfPageObj(rObjectContact, rViewContact),
      mbInDestructor(false),
      mxCurrentPageContents(),
      mpCache(rpCache),
      mpProperties(rpProperties)
{
    SharedPageDescriptor pDescriptor (GetPageDescriptor());
    OSL_ASSERT(pDescriptor.get()!=NULL);
    if (pDescriptor.get() != NULL)
        pDescriptor->SetViewObjectContact(this);
}




PageObjectViewObjectContact::~PageObjectViewObjectContact (void)
{
    mbInDestructor = true;

    GetPageDescriptor()->SetViewObjectContact(NULL);

    if (mpCache.get() != NULL)
    {
        const SdrPage* pPage = GetPage();

        if(pPage)
        {
            mpCache->ReleasePreviewBitmap(GetPage());
        }
    }
}




void PageObjectViewObjectContact::SetCache (const ::boost::shared_ptr<cache::PageCache>& rpCache)
{
    mpCache = rpCache;
}




Rectangle PageObjectViewObjectContact::GetBoundingBox (
    OutputDevice& rDevice,
    BoundingBoxType eType,
    CoordinateSystem eCoordinateSystem) const
{
    // Most of the bounding boxes are based on the bounding box of the preview.
    // SdrPageObj is a SdrObject, so use SdrObject::aOutRect as model data
    const PageObjectViewContact& rPaObVOC(static_cast<PageObjectViewContact&>(GetViewContact()));
    Rectangle aBoundingBox(rPaObVOC.GetPageObject().GetLastBoundRect());

    CoordinateSystem eCurrentCoordinateSystem (ModelCoordinateSystem);
    switch(eType)
    {
        case PageObjectBoundingBox:
        {
            const SvBorder aPageDescriptorBorder(GetPageDescriptor()->GetModelBorder());
            aBoundingBox.Left() -= aPageDescriptorBorder.Left();
            aBoundingBox.Top() -= aPageDescriptorBorder.Top();
            aBoundingBox.Right() += aPageDescriptorBorder.Right();
            aBoundingBox.Bottom() += aPageDescriptorBorder.Bottom();
            break;
        }
        case PreviewBoundingBox:
        {
            // The aBoundingBox already has the right value.
            break;
        }
        case MouseOverIndicatorBoundingBox:
        {
            const sal_Int32 nBorderWidth (mnMouseOverEffectOffset+mnMouseOverEffectThickness);
            const Size aBorderSize (rDevice.PixelToLogic(Size(nBorderWidth,nBorderWidth)));
            aBoundingBox.Left() -= aBorderSize.Width();
            aBoundingBox.Top() -= aBorderSize.Height();
            aBoundingBox.Right() += aBorderSize.Width();
            aBoundingBox.Bottom() += aBorderSize.Height();
            break;
        }
        case FocusIndicatorBoundingBox:
        {
            const sal_Int32 nBorderWidth (mnFocusIndicatorOffset+1);
            const Size aBorderSize (rDevice.PixelToLogic(Size(nBorderWidth,nBorderWidth)));
            aBoundingBox.Left() -= aBorderSize.Width();
            aBoundingBox.Top() -= aBorderSize.Height();
            aBoundingBox.Right() += aBorderSize.Width();
            aBoundingBox.Bottom() += aBorderSize.Height();
            break;
        }
        case SelectionIndicatorBoundingBox:
        {
            const sal_Int32 nBorderWidth(mnSelectionIndicatorOffset+mnSelectionIndicatorThickness);
            const Size aBorderSize (rDevice.PixelToLogic(Size(nBorderWidth,nBorderWidth)));
            aBoundingBox.Left() -= aBorderSize.Width();
            aBoundingBox.Top() -= aBorderSize.Height();
            aBoundingBox.Right() += aBorderSize.Width();
            aBoundingBox.Bottom() += aBorderSize.Height();
            break;
        }
        case PageNumberBoundingBox:
        {
            Size aModelOffset = rDevice.PixelToLogic(Size(mnPageNumberOffset,mnPageNumberOffset));
            Size aNumberSize (GetPageDescriptor()->GetPageNumberAreaModelSize());
            aBoundingBox = Rectangle (
                Point (
                    aBoundingBox.Left() - aModelOffset.Width() - aNumberSize.Width(),
                    aBoundingBox.Top()),
                aNumberSize);
            break;
        }

        case NameBoundingBox:
            break;

        case FadeEffectIndicatorBoundingBox:
            Size aModelOffset = rDevice.PixelToLogic(Size (0, mnFadeEffectIndicatorOffset));
            // Flush left just outside the selection rectangle.
            aBoundingBox = Rectangle (
                Point (
                    aBoundingBox.Left(),
                    aBoundingBox.Bottom() + aModelOffset.Height()
                    ),
                rDevice.PixelToLogic (
                    IconCache::Instance().GetIcon(BMP_FADE_EFFECT_INDICATOR).GetSizePixel())
                );
            break;
    }

    // Make sure the bounding box uses the requested coordinate system.
    if (eCurrentCoordinateSystem != eCoordinateSystem)
    {
        if (eCoordinateSystem == ModelCoordinateSystem)
            aBoundingBox = Rectangle(
                rDevice.PixelToLogic(aBoundingBox.TopLeft()),
                rDevice.PixelToLogic(aBoundingBox.GetSize()));
        else
            aBoundingBox = Rectangle(
                rDevice.LogicToPixel(aBoundingBox.TopLeft()),
                rDevice.LogicToPixel(aBoundingBox.GetSize()));
    }
    return aBoundingBox;
}

///////////////////////////////////////////////////////////////////////////////////////////////
// example implementation for primitive usage for PageObjectViewObjectContact

} } } // end of namespace ::sd::slidesorter::view

#include <drawinglayer/primitive2d/polygonprimitive2d.hxx>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
#include <sd_primitivetypes2d.hxx>
#include <drawinglayer/primitive2d/polypolygonprimitive2d.hxx>
#include <drawinglayer/primitive2d/textlayoutdevice.hxx>
#include <drawinglayer/primitive2d/textprimitive2d.hxx>
#include <drawinglayer/geometry/viewinformation2d.hxx>
#include <svx/sdr/contact/objectcontact.hxx>

namespace sd { namespace slidesorter { namespace view {

///////////////////////////////////////////////////////////////////////////////////////////////
// All primitives for SdrPageObject visualisation are based on one range which describes
// the size of the inner rectangle for PagePreview visualisation. Use a common implementation
// class for all derived SdPageObjectPrimitives. The SdPageObjectBasePrimitive itself
// is pure virtual

class SdPageObjectBasePrimitive : public drawinglayer::primitive2d::BufferedDecompositionPrimitive2D
{
private:
    // the inner range of the SdPageObject visualisation
    basegfx::B2DRange                   maRange;

public:
    // constructor and destructor
    SdPageObjectBasePrimitive(const basegfx::B2DRange& rRange);
    virtual ~SdPageObjectBasePrimitive();

    // data access
    const basegfx::B2DRange& getPageObjectRange() const { return maRange; }

    // compare operator
    virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const;
};

SdPageObjectBasePrimitive::SdPageObjectBasePrimitive(const basegfx::B2DRange& rRange)
:   drawinglayer::primitive2d::BufferedDecompositionPrimitive2D(),
    maRange(rRange)
{
}

SdPageObjectBasePrimitive::~SdPageObjectBasePrimitive()
{
}

bool SdPageObjectBasePrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const
{
    if(drawinglayer::primitive2d::BufferedDecompositionPrimitive2D::operator==(rPrimitive))
    {
        const SdPageObjectBasePrimitive& rCompare = static_cast< const SdPageObjectBasePrimitive& >(rPrimitive);
        return (getPageObjectRange() == rCompare.getPageObjectRange());
    }

    return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////
// SdPageObjectPrimitive for selected visualisation

class SdPageObjectPageBitmapPrimitive : public SdPageObjectBasePrimitive
{
private:
    // the bitmap containing the PagePreview
    BitmapEx                            maBitmapEx;

protected:
    // method which is to be used to implement the local decomposition of a 2D primitive.
    virtual Primitive2DSequence create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const;

public:
    // constructor and destructor
    SdPageObjectPageBitmapPrimitive(
        const basegfx::B2DRange& rRange,
        const BitmapEx& rBitmapEx);
    ~SdPageObjectPageBitmapPrimitive();

    // data access
    const BitmapEx& getBitmapEx() const { return maBitmapEx; }

    // compare operator
    virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const;

    // provide unique ID
    DeclPrimitrive2DIDBlock()
};

Primitive2DSequence SdPageObjectPageBitmapPrimitive::create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const
{
    // add bitmap primitive
    // to avoid scaling, use the Bitmap pixel size as primitive size
    basegfx::B2DHomMatrix aBitmapTransform;
    const Size aBitmapSize(getBitmapEx().GetSizePixel());
    const basegfx::B2DVector aBitmapSizeLogic(rViewInformation.getInverseObjectToViewTransformation() *
        basegfx::B2DVector(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1));

    // short form for scale and translate transformation
    aBitmapTransform.set(0L, 0L, aBitmapSizeLogic.getX());
    aBitmapTransform.set(1L, 1L, aBitmapSizeLogic.getY());
    aBitmapTransform.set(0L, 2L, getPageObjectRange().getMinX());
    aBitmapTransform.set(1L, 2L, getPageObjectRange().getMinY());

    // add a BitmapPrimitive2D to the result
    const Primitive2DReference xReference(
        new drawinglayer::primitive2d::BitmapPrimitive2D(getBitmapEx(), aBitmapTransform));
    return Primitive2DSequence(&xReference, 1);
}

SdPageObjectPageBitmapPrimitive::SdPageObjectPageBitmapPrimitive(
    const basegfx::B2DRange& rRange,
    const BitmapEx& rBitmapEx)
:   SdPageObjectBasePrimitive(rRange),
    maBitmapEx(rBitmapEx)
{
}

SdPageObjectPageBitmapPrimitive::~SdPageObjectPageBitmapPrimitive()
{
}

bool SdPageObjectPageBitmapPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const
{
    if(SdPageObjectBasePrimitive::operator==(rPrimitive))
    {
        const SdPageObjectPageBitmapPrimitive& rCompare = static_cast< const SdPageObjectPageBitmapPrimitive& >(rPrimitive);
        return (getBitmapEx() == rCompare.getBitmapEx());
    }

    return false;
}

ImplPrimitrive2DIDBlock(SdPageObjectPageBitmapPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTPAGEBITMAPPRIMITIVE)

///////////////////////////////////////////////////////////////////////////////////////////////
// SdPageObjectPrimitive for selected visualisation

class SdPageObjectSelectPrimitive : public SdPageObjectBasePrimitive
{
private:
    /// Gap between border of page object and inside of selection rectangle.
    static const sal_Int32 mnSelectionIndicatorOffset;

    /// Thickness of the selection rectangle.
    static const sal_Int32 mnSelectionIndicatorThickness;

protected:
    // method which is to be used to implement the local decomposition of a 2D primitive.
    virtual Primitive2DSequence create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const;

public:
    // constructor and destructor
    SdPageObjectSelectPrimitive(const basegfx::B2DRange& rRange);
    ~SdPageObjectSelectPrimitive();

    // provide unique ID
    DeclPrimitrive2DIDBlock()
};

const sal_Int32 SdPageObjectSelectPrimitive::mnSelectionIndicatorOffset(1);
const sal_Int32 SdPageObjectSelectPrimitive::mnSelectionIndicatorThickness(3);

Primitive2DSequence SdPageObjectSelectPrimitive::create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const
{
    Primitive2DSequence xRetval(2);

    // since old Width/Height calculations always added a single pixel value,
    // it is necessary to create a inner range which is one display unit less
    // at the bottom right.
    const basegfx::B2DVector aDiscretePixel(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
    const basegfx::B2DRange aAdaptedInnerRange(
        getPageObjectRange().getMinX(), getPageObjectRange().getMinY(),
        getPageObjectRange().getMaxX() - aDiscretePixel.getX(), getPageObjectRange().getMaxY() - aDiscretePixel.getY());

    // PaintSelectionIndicator replacement. Grow by offset first
    basegfx::B2DRange aDiscreteOuterRange(aAdaptedInnerRange);
    aDiscreteOuterRange.grow(mnSelectionIndicatorOffset * aDiscretePixel.getX());

    // Remember inner border. Make it one bigger in top left since polygons
    // do not paint their lower-right corners. Since this is the inner polygon,
    // the top-left corders are the ones to grow here
    const basegfx::B2DRange aDiscreteInnerRange(
        aDiscreteOuterRange.getMinimum() + aDiscretePixel,
        aDiscreteOuterRange.getMaximum() - aDiscretePixel);

    // grow by line width
    aDiscreteOuterRange.grow((mnSelectionIndicatorThickness - 1) * aDiscretePixel.getX());

    // create a PolyPolygon from those ranges. For the outer polygon, round edges by
    // giving a relative radius to the polygon creator (use mnSelectionIndicatorThickness here, too)
    const double fPixelFactor(aDiscretePixel.getX() * (mnSelectionIndicatorThickness + 2.5));
    const double fRelativeRadiusX(fPixelFactor / ::std::max(aDiscreteOuterRange.getWidth(), 1.0));
    const double fRelativeRadiusY(fPixelFactor / ::std::max(aDiscreteOuterRange.getHeight(), 1.0));
    basegfx::B2DPolyPolygon aFramePolyPolygon;
    const basegfx::B2DPolygon aRoundedOuterPolygon(basegfx::tools::createPolygonFromRect(aDiscreteOuterRange, fRelativeRadiusX, fRelativeRadiusY));

    aFramePolyPolygon.append(aRoundedOuterPolygon);
    aFramePolyPolygon.append(basegfx::tools::createPolygonFromRect(aDiscreteInnerRange));

    // add colored PolyPolygon
    const svtools::ColorConfig aColorConfig;
    static bool bTestWithBrightColors(false);
    const basegfx::BColor aFrameColor(bTestWithBrightColors ? basegfx::BColor(0,1,0) : Application::GetSettings().GetStyleSettings().GetMenuHighlightColor().getBColor());

    xRetval[0] = Primitive2DReference(
        new drawinglayer::primitive2d::PolyPolygonColorPrimitive2D(aFramePolyPolygon, aFrameColor));

    // add aRoundedOuterPolygon again as non-filled line polygon to get the roundungs
    // painted correctly
    xRetval[1] = Primitive2DReference(
        new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aRoundedOuterPolygon, aFrameColor));

    return xRetval;
}

SdPageObjectSelectPrimitive::SdPageObjectSelectPrimitive(const basegfx::B2DRange& rRange)
:   SdPageObjectBasePrimitive(rRange)
{
}

SdPageObjectSelectPrimitive::~SdPageObjectSelectPrimitive()
{
}

ImplPrimitrive2DIDBlock(SdPageObjectSelectPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTSELECTPRIMITIVE)

///////////////////////////////////////////////////////////////////////////////////////////////
// SdPageObjectPrimitive for border around bitmap visualisation

class SdPageObjectBorderPrimitive : public SdPageObjectBasePrimitive
{
protected:
    // method which is to be used to implement the local decomposition of a 2D primitive.
    virtual Primitive2DSequence create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const;

public:
    // constructor and destructor
    SdPageObjectBorderPrimitive(const basegfx::B2DRange& rRange);
    ~SdPageObjectBorderPrimitive();

    // provide unique ID
    DeclPrimitrive2DIDBlock()
};

Primitive2DSequence SdPageObjectBorderPrimitive::create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const
{
    // since old Width/Height calculations always added a single pixel value,
    // it is necessary to create a inner range which is one display unit less
    // at the bottom right.
    const basegfx::B2DVector aDiscretePixel(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
    const basegfx::B2DRange aAdaptedInnerRange(
        getPageObjectRange().getMinX(), getPageObjectRange().getMinY(),
        getPageObjectRange().getMaxX() - aDiscretePixel.getX(), getPageObjectRange().getMaxY() - aDiscretePixel.getY());

    // Paint_Border replacement. (use aBorderColor)
    static bool bTestWithBrightColors(false);
    const svtools::ColorConfig aColorConfig;
    const basegfx::BColor aBorderColor(bTestWithBrightColors ? basegfx::BColor(1,0,0) : Color(aColorConfig.GetColorValue(svtools::FONTCOLOR).nColor).getBColor());

    const Primitive2DReference xReference(
        new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(basegfx::tools::createPolygonFromRect(aAdaptedInnerRange), aBorderColor));
    return Primitive2DSequence(&xReference, 1);
}

SdPageObjectBorderPrimitive::SdPageObjectBorderPrimitive(const basegfx::B2DRange& rRange)
:   SdPageObjectBasePrimitive(rRange)
{
}

SdPageObjectBorderPrimitive::~SdPageObjectBorderPrimitive()
{
}

ImplPrimitrive2DIDBlock(SdPageObjectBorderPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTBORDERPRIMITIVE)

///////////////////////////////////////////////////////////////////////////////////////////////
// SdPageObjectPrimitive for focus visualisation

class SdPageObjectFocusPrimitive : public SdPageObjectBasePrimitive
{
private:
    /// Gap between border of page object and inside of focus rectangle.
    static const sal_Int32 mnFocusIndicatorOffset;
    const bool mbContrastToSelected;

protected:
    // method which is to be used to implement the local decomposition of a 2D primitive.
    virtual Primitive2DSequence create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const;

public:
    // constructor and destructor
    SdPageObjectFocusPrimitive(const basegfx::B2DRange& rRange, const bool bContrast);
    ~SdPageObjectFocusPrimitive();

    // provide unique ID
    DeclPrimitrive2DIDBlock()
};

const sal_Int32 SdPageObjectFocusPrimitive::mnFocusIndicatorOffset(2);

Primitive2DSequence SdPageObjectFocusPrimitive::create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const
{
    Primitive2DSequence xRetval(2);

    // since old Width/Height calculations always added a single pixel value,
    // it is necessary to create a inner range which is one display unit less
    // at the bottom right.
    const basegfx::B2DVector aDiscretePixel(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
    const basegfx::B2DRange aAdaptedInnerRange(
        getPageObjectRange().getMinX(), getPageObjectRange().getMinY(),
        getPageObjectRange().getMaxX() - aDiscretePixel.getX(), getPageObjectRange().getMaxY() - aDiscretePixel.getY());

    // Paint_FocusIndicator replacement. (black and white).
    // imitate Paint_DottedRectangle: First paint a white rectangle and above it a black dotted one
    basegfx::B2DRange aFocusIndicatorRange(aAdaptedInnerRange);
    aFocusIndicatorRange.grow(mnFocusIndicatorOffset * aDiscretePixel.getX());

    // create polygon
    const basegfx::B2DPolygon aIndicatorPolygon(basegfx::tools::createPolygonFromRect(aFocusIndicatorRange));

    const StyleSettings& rStyleSettings(Application::GetSettings().GetStyleSettings());

    // "background" rectangle
    const Color aBackgroundColor(mbContrastToSelected ? rStyleSettings.GetMenuHighlightColor() : rStyleSettings.GetWindowColor());
    xRetval[0] = Primitive2DReference(
        new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(aIndicatorPolygon, Color(COL_WHITE).getBColor()));

    // dotted black rectangle with same geometry
    ::std::vector< double > aDotDashArray;

    const sal_Int32 nFocusIndicatorWidth (3);
    aDotDashArray.push_back(nFocusIndicatorWidth *aDiscretePixel.getX());
    aDotDashArray.push_back(nFocusIndicatorWidth * aDiscretePixel.getX());

    // prepare line and stroke attributes
    const Color aLineColor(mbContrastToSelected ? rStyleSettings.GetMenuHighlightTextColor() : rStyleSettings.GetWindowTextColor());
    const drawinglayer::attribute::LineAttribute aLineAttribute(aLineColor.getBColor());
    const drawinglayer::attribute::StrokeAttribute aStrokeAttribute(
        aDotDashArray, 2.0 * nFocusIndicatorWidth * aDiscretePixel.getX());


    xRetval[1] = Primitive2DReference(
        new drawinglayer::primitive2d::PolygonStrokePrimitive2D(aIndicatorPolygon, aLineAttribute, aStrokeAttribute));

    return xRetval;
}

SdPageObjectFocusPrimitive::SdPageObjectFocusPrimitive(const basegfx::B2DRange& rRange, const bool bContrast)
    :   SdPageObjectBasePrimitive(rRange),
        mbContrastToSelected(bContrast)
{
}

SdPageObjectFocusPrimitive::~SdPageObjectFocusPrimitive()
{
}

ImplPrimitrive2DIDBlock(SdPageObjectFocusPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTFOCUSPRIMITIVE)

///////////////////////////////////////////////////////////////////////////////////////////////
// SdPageObjectPrimitive for fade effect visualisation

class SdPageObjectFadeNameNumberPrimitive : public SdPageObjectBasePrimitive
{
private:
    /// Size of width and height of the fade effect indicator in pixels.
    static const sal_Int32              mnFadeEffectIndicatorOffset;

    /// Size of width and height of the comments indicator in pixels.
    static const sal_Int32              mnCommentsIndicatorOffset;

    /// Gap between border of page object and number rectangle.
    static const sal_Int32              mnPageNumberOffset;

    /// the indicator bitmaps. Static since it is usable outside this primitive
    /// for size comparisons
    static BitmapEx*                    mpFadeEffectIconBitmap;
    static BitmapEx*                    mpCommentsIconBitmap;

    /// page name, number and needed infos
    String                              maPageName;
    sal_uInt32                          mnPageNumber;
    Font                                maPageNameFont;
    Size                                maPageNumberAreaModelSize;

    // bitfield
    bool mbShowFadeEffectIcon : 1;
    bool mbShowCommentsIcon : 1;
    bool mbExcluded : 1;

    // private helpers
    const BitmapEx& getFadeEffectIconBitmap() const;

protected:
    // method which is to be used to implement the local decomposition of a 2D primitive.
    virtual Primitive2DSequence create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const;

public:
    // constructor and destructor
    SdPageObjectFadeNameNumberPrimitive(
        const basegfx::B2DRange& rRange,
        const String& rPageName,
        sal_uInt32 nPageNumber,
        const Font& rPageNameFont,
        const Size& rPageNumberAreaModelSize,
        bool bShowFadeEffectIcon,
        bool bShowCommentsIcon,
        bool bExcluded);
    ~SdPageObjectFadeNameNumberPrimitive();

    // data access
    const String& getPageName() const { return maPageName; }
    sal_uInt32 getPageNumber() const { return mnPageNumber; }
    const Font& getPageNameFont() const { return maPageNameFont; }
    const Size& getPageNumberAreaModelSize() const { return maPageNumberAreaModelSize; }
    bool getShowFadeEffectIcon() const { return mbShowFadeEffectIcon; }
    bool getShowCommentsIcon() const { return mbShowCommentsIcon; }
    bool getExcluded() const { return mbExcluded; }

    // compare operator
    virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const;

    // provide unique ID
    DeclPrimitrive2DIDBlock()
};

const sal_Int32 SdPageObjectFadeNameNumberPrimitive::mnFadeEffectIndicatorOffset(9);
const sal_Int32 SdPageObjectFadeNameNumberPrimitive::mnPageNumberOffset(9);
BitmapEx* SdPageObjectFadeNameNumberPrimitive::mpFadeEffectIconBitmap = 0;

const BitmapEx& SdPageObjectFadeNameNumberPrimitive::getFadeEffectIconBitmap() const
{
    if(mpFadeEffectIconBitmap == NULL)
    {
        // prepare FadeEffectIconBitmap on demand
        const sal_uInt16 nIconId(Application::GetSettings().GetStyleSettings().GetHighContrastMode()
            ? BMP_FADE_EFFECT_INDICATOR_H
            : BMP_FADE_EFFECT_INDICATOR);
        const BitmapEx aFadeEffectIconBitmap(IconCache::Instance().GetIcon(nIconId).GetBitmapEx());
        const_cast< SdPageObjectFadeNameNumberPrimitive* >(this)->mpFadeEffectIconBitmap = new BitmapEx(aFadeEffectIconBitmap);
    }

    return *mpFadeEffectIconBitmap;
}


const sal_Int32 SdPageObjectFadeNameNumberPrimitive::mnCommentsIndicatorOffset(9);
BitmapEx* SdPageObjectFadeNameNumberPrimitive::mpCommentsIconBitmap = 0;

Primitive2DSequence SdPageObjectFadeNameNumberPrimitive::create2DDecomposition(const drawinglayer::geometry::ViewInformation2D& rViewInformation) const
{
    const xub_StrLen nTextLength(getPageName().Len());
    const sal_uInt32 nCount(
        (getShowFadeEffectIcon() ? 1 : 0) +     // FadeEffect icon
        (nTextLength ? 1 : 0) +                 // PageName
        1 +                                     // PageNumber (always)
        (getExcluded() ? 2 : 0)                 // PageNumber crossed out
        );
    sal_uInt32 nInsert(0);
    Primitive2DSequence xRetval(nCount);

    // since old Width/Height calculations always added a single pixel value,
    // it is necessary to create a inner range which is one display unit less
    // at the bottom right.
    const basegfx::B2DVector aDiscretePixel(rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 1.0));
    const basegfx::B2DRange aAdaptedInnerRange(
        getPageObjectRange().getMinX(), getPageObjectRange().getMinY(),
        getPageObjectRange().getMaxX() - aDiscretePixel.getX(), getPageObjectRange().getMaxY() - aDiscretePixel.getY());

    // preapre TextLayouter
    drawinglayer::primitive2d::TextLayouterDevice aTextLayouter;
    aTextLayouter.setFont(getPageNameFont());

    // get font attributes
    basegfx::B2DVector aTextSizeAttribute;
    const drawinglayer::attribute::FontAttribute aFontAttribute(
        drawinglayer::primitive2d::getFontAttributeFromVclFont(
            aTextSizeAttribute,
            getPageNameFont(),
            false,
            false));

    // prepare locale; this may need some more information in the future
    const ::com::sun::star::lang::Locale aLocale;

    // prepare font color from System
    const basegfx::BColor aFontColor(Application::GetSettings().GetStyleSettings().GetFontColor().getBColor());

    if(getShowFadeEffectIcon())
    {
        // prepare fFadeEffect Sizes
        const basegfx::B2DVector aFadeEffectBitmapSizeLogic(rViewInformation.getInverseObjectToViewTransformation() *
            basegfx::B2DVector(
                getFadeEffectIconBitmap().GetSizePixel().getWidth() - 1,
                getFadeEffectIconBitmap().GetSizePixel().getHeight() - 1));

        // Paint_FadeEffectIndicator replacement.
        // create transformation. To avoid bitmap scaling, use bitmap size as size
        basegfx::B2DHomMatrix aBitmapTransform;

        // short form for scale and translate transformation
        aBitmapTransform.set(0L, 0L, aFadeEffectBitmapSizeLogic.getX());
        aBitmapTransform.set(1L, 1L, aFadeEffectBitmapSizeLogic.getY());
        aBitmapTransform.set(0L, 2L, aAdaptedInnerRange.getMinX());
        aBitmapTransform.set(1L, 2L, aAdaptedInnerRange.getMaxY() + ((mnFadeEffectIndicatorOffset + 1) * aDiscretePixel.getX()));

        xRetval[nInsert++] = Primitive2DReference(
            new drawinglayer::primitive2d::BitmapPrimitive2D(getFadeEffectIconBitmap(), aBitmapTransform));
    }

    if(nTextLength)
    {
        // prepare fFadeEffect Sizes since it consumes from text size
        const basegfx::B2DVector aFadeEffectBitmapSizeLogic(rViewInformation.getInverseObjectToViewTransformation() *
            basegfx::B2DVector(
                getFadeEffectIconBitmap().GetSizePixel().getWidth() - 1,
                getFadeEffectIconBitmap().GetSizePixel().getHeight() - 1));

        // Paint_PageName replacement. Get text size
        const double fTextWidth(aTextLayouter.getTextWidth(getPageName(), 0, nTextLength));
        const double fTextHeight(getPageNameFont().GetHeight());
        const double fFadeEffectWidth(aFadeEffectBitmapSizeLogic.getX() * 2.0);
        const double fFadeEffectTextGap(((mnFadeEffectIndicatorOffset + 2) * aDiscretePixel.getX()));
        String aPageName(getPageName());

        // calculate text start position
        double fStartX(
            aAdaptedInnerRange.getMaxX()
            - fTextWidth
            + (aDiscretePixel.getX() * 3.0));
        const double fStartY(
            aAdaptedInnerRange.getMaxY()
            + fTextHeight
            + fFadeEffectTextGap);
        const bool bNeedClipping(fStartX < aAdaptedInnerRange.getMinX() + fFadeEffectWidth);

        // if text is too big, clip it
        if(bNeedClipping)
        {
            // new left start
            fStartX = aAdaptedInnerRange.getMinX() + fFadeEffectWidth;

            // find out how many characters to use
            const double fAvailableLength(aAdaptedInnerRange.getWidth() - fFadeEffectWidth);
            static const String aThreePoints(String::CreateFromAscii("..."));
            const double fWidthThreePoints(aTextLayouter.getTextWidth(aThreePoints, 0, aThreePoints.Len()));
            xub_StrLen a(1);

            for(; a < (xub_StrLen)nTextLength; a++)
            {
                const double fSnippetLength(aTextLayouter.getTextWidth(aPageName, 0, a));

                if(fSnippetLength + fWidthThreePoints > fAvailableLength)
                {
                    break;
                }
            }

            // build new string
            aPageName = String(aPageName, 0, a - 1);
            aPageName += aThreePoints;
        }

        // fill text matrix
        basegfx::B2DHomMatrix aTextMatrix;

        aTextMatrix.set(0, 0, aTextSizeAttribute.getX());
        aTextMatrix.set(1, 1, aTextSizeAttribute.getY());
        aTextMatrix.set(0, 2, fStartX);
        aTextMatrix.set(1, 2, fStartY);

        // prepare DXTextArray (can be empty one)
        const ::std::vector< double > aDXArray;

        // create Text primitive and add to target
        xRetval[nInsert++] = Primitive2DReference(
            new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                aTextMatrix,
                aPageName,
                0,
                aPageName.Len(),
                aDXArray,
                aFontAttribute,
                aLocale,
                aFontColor));
    }

    {
        // Paint_PageNumber replacement. Get the range where it shall be centered and prepare the string
        const double fLeft(aAdaptedInnerRange.getMinX() - (mnPageNumberOffset * aDiscretePixel.getX()) - getPageNumberAreaModelSize().Width());
        const double fTop(aAdaptedInnerRange.getMinY());
        const basegfx::B2DRange aNumberRange(fLeft, fTop,
            fLeft + getPageNumberAreaModelSize().Width(), fTop + getPageNumberAreaModelSize().Height());
        const String aPageNumber(String::CreateFromInt32(getPageNumber()));
        const xub_StrLen nNumberLen(aPageNumber.Len());

        // Get text size
        const double fTextWidth(aTextLayouter.getTextWidth(aPageNumber, 0, nNumberLen));
        const double fTextHeight(getPageNameFont().GetHeight());

        // get text start postion
        const double fStartX(aNumberRange.getCenterX() - (fTextWidth / 2.0));
        const double fStartY(aNumberRange.getMinY() + fTextHeight + aDiscretePixel.getX());

        // fill text matrix
        basegfx::B2DHomMatrix aTextMatrix;

        aTextMatrix.set(0, 0, aTextSizeAttribute.getX());
        aTextMatrix.set(1, 1, aTextSizeAttribute.getY());
        aTextMatrix.set(0, 2, fStartX);
        aTextMatrix.set(1, 2, fStartY);

        // prepare DXTextArray (can be empty one)
        const ::std::vector< double > aDXArray;

        // create Text primitive
        xRetval[nInsert++] = Primitive2DReference(
            new drawinglayer::primitive2d::TextSimplePortionPrimitive2D(
                aTextMatrix,
                aPageNumber,
                0,
                nNumberLen,
                aDXArray,
                aFontAttribute,
                aLocale,
                aFontColor));

        if(getExcluded())
        {
            // create a box with strikethrough from top left to bottom right
            const basegfx::BColor aActiveColor(Application::GetSettings().GetStyleSettings().GetActiveColor().getBColor());
            basegfx::B2DPolygon aStrikethrough;

            aStrikethrough.append(aNumberRange.getMinimum());
            aStrikethrough.append(aNumberRange.getMaximum());

            xRetval[nInsert++] = Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                basegfx::tools::createPolygonFromRect(aNumberRange), aActiveColor));

            xRetval[nInsert++] = Primitive2DReference(new drawinglayer::primitive2d::PolygonHairlinePrimitive2D(
                aStrikethrough, aActiveColor));
        }
    }

    return xRetval;
}

SdPageObjectFadeNameNumberPrimitive::SdPageObjectFadeNameNumberPrimitive(
    const basegfx::B2DRange& rRange,
    const String& rPageName,
    sal_uInt32 nPageNumber,
    const Font& rPageNameFont,
    const Size& rPageNumberAreaModelSize,
    bool bShowFadeEffectIcon,
    bool bShowCommentsIcon,
    bool bExcluded)
:   SdPageObjectBasePrimitive(rRange),
    maPageName(rPageName),
    mnPageNumber(nPageNumber),
    maPageNameFont(rPageNameFont),
    maPageNumberAreaModelSize(rPageNumberAreaModelSize),
    mbShowFadeEffectIcon(bShowFadeEffectIcon),
    mbShowCommentsIcon(bShowCommentsIcon),
    mbExcluded(bExcluded)
{
}

SdPageObjectFadeNameNumberPrimitive::~SdPageObjectFadeNameNumberPrimitive()
{
}

bool SdPageObjectFadeNameNumberPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const
{
    if(SdPageObjectBasePrimitive::operator==(rPrimitive))
    {
        const SdPageObjectFadeNameNumberPrimitive& rCompare = static_cast< const SdPageObjectFadeNameNumberPrimitive& >(rPrimitive);

        return (getPageName() == rCompare.getPageName()
            && getPageNumber() == rCompare.getPageNumber()
            && getPageNameFont() == rCompare.getPageNameFont()
            && getPageNumberAreaModelSize() == rCompare.getPageNumberAreaModelSize()
            && getShowFadeEffectIcon() == rCompare.getShowFadeEffectIcon()
            && getExcluded() == rCompare.getExcluded());
    }

    return false;
}

ImplPrimitrive2DIDBlock(SdPageObjectFadeNameNumberPrimitive, PRIMITIVE2D_ID_SDPAGEOBJECTFADENAMENUMBERPRIMITIVE)

///////////////////////////////////////////////////////////////////////////////////////////////
// createPrimitive2DSequence
//
// This method will replace the whole painting mechanism. Task is no longer to paint stuff to an OutDev,
// but to provide the necessary geometrical information using primitives.

Primitive2DSequence PageObjectViewObjectContact::createPrimitive2DSequence(const sdr::contact::DisplayInfo& rDisplayInfo) const
{
    // OutputDevice* pDevice = rDisplayInfo.GetDIOutputDevice();
    OutputDevice* pDevice = GetObjectContact().TryToGetOutputDevice();

    // get primitive vector from parent class. Do remember the contents for later use; this
    // is done to create the page content renderer (see PagePrimitiveExtractor in svx) at the
    // original object and to setup the draw hierarchy there so that changes to VCs of displayed
    // objects will lead to InvalidatePartOfView-calls which will be forwarded from the helper-OC
    // to this VOC in calling a ActionChanged().
    //
    // This already produces the displayable page content as a primitive sequence, complete with
    // embedding in the page visualizer, clipping if needed and object and aspect ratio
    // preparations. It would thus be the base for creating the cached visualisation, too,
    // by just painting extactly this primitive sequence.
    //
    // Currently, this slows down PagePane display heavily. Reason is that the current mechanism
    // to react on a SdrObject change in an edit view is to react on the ModelChange and to completely
    // reset the PagePane (delete SdrPageObjs, re-create and layout them). This works, but kicks
    // the complete sequence of primitive creation at VOCs and VCs and their buffering out of
    // memory each time. So there are two choices:
    //
    // 1, disable getting the sequence of primtives
    //    -> invalidate uses ModelChange
    //    -> cache repaint uses complete view creation and repainting
    //
    // 2, create and use the sequence of primitives
    //    -> invalidate would not need ModelChange, no destroy/recreate of SdrObjects, no rearrange,
    //       the invalidate and the following repaint would exactly update the SdrPages involved and
    //       use the DrawingLayer provided ActionChanged() invalidations over the VOCs and VCs
    //    -> cache repaint could use the here offered sequence of primitives to re-create the bitmap
    //       (just hand over the local member to the cache)
    //
    // For the moment i will use (1) and disable primitive creation for SdrPageObj contents here

    // const_cast< PageObjectViewObjectContact* >(this)->mxCurrentPageContents = ViewObjectContactOfPageObj::createPrimitive2DSequence(rDisplayInfo);

    // assert when this call is issued indirectly from the destructor of
    // this instance. This is not allowed and needs to be looked at
#ifdef DBG_UTIL
    if(mbInDestructor)
    {
        OSL_ENSURE(false, "Higher call inside PageObjectViewObjectContact in destructor (!)");
    }
#endif

    // Check if buffering can and shall be done.
    if (pDevice != NULL
        && !GetObjectContact().isOutputToPrinter()
        && !GetObjectContact().isOutputToRecordingMetaFile()
        && !mbInDestructor)
    {
        // get inner and outer logic rectangles. Use model data directly for creation. Do NOT use getBoundRect()/
        // getSnapRect() functionality; these will use the sequence of primitives in the long run itself. SdrPageObj
        // is a SdrObject, so use SdrObject::aOutRect as model data. Access using GetLastBoundRect() to not execute anything
        PageObjectViewContact& rPaObVOC(static_cast< PageObjectViewContact& >(GetViewContact()));
        const Rectangle aInnerLogic(rPaObVOC.GetPageObject().GetLastBoundRect());

        // get BitmapEx from cache. Do exactly the same as Paint_Preview() to avoid a repaint loop
        // caused by slightly different pixel sizes of what the cache sees as pixel size and what is
        // calculated here in discrete coordinates. This includes to not use LogicToPiyel on the Rectangle,
        // but to do the same as the GetBoundingBox() implementation
        const Rectangle aInnerPixel(Rectangle(pDevice->LogicToPixel(aInnerLogic.TopLeft()), pDevice->LogicToPixel(aInnerLogic.GetSize())));
        BitmapEx aBitmapEx(const_cast< PageObjectViewObjectContact* >(this)->GetPreview(rDisplayInfo, aInnerPixel));

        // prepare inner range
        const basegfx::B2DRange aInnerRange(aInnerLogic.Left(), aInnerLogic.Top(), aInnerLogic.Right(), aInnerLogic.Bottom());

        // provide default parameters
        String aPageName;
        Font aPageNameFont;
        sal_uInt32 nPageNumber(0);
        Size aPageNumberAreaModelSize;
        bool bShowFadeEffectIcon(false);
        bool bShowCommentsIcon(false);
        bool bExcluded(false);

        if(GetPage())
        {
            const SdPage* pPage = static_cast<const SdPage*>(GetPage());

            // decide if fade effect indicator will be painted
            if(pPage->getTransitionType() > 0)
            {
                bShowFadeEffectIcon = true;
            }

            bShowCommentsIcon = !pPage->getAnnotations().empty();

            // prepare PageName, PageNumber, font and AreaModelSize
            aPageName = pPage->GetName();
            aPageNameFont = *FontProvider::Instance().GetFont(*pDevice);
            nPageNumber = ((pPage->GetPageNum() - 1) / 2) + 1;
            aPageNumberAreaModelSize = GetPageDescriptor()->GetPageNumberAreaModelSize();

            if(!aPageName.Len())
            {
                aPageName = String(SdResId(STR_PAGE));
                aPageName += String::CreateFromInt32(nPageNumber);
            }

            // decide if page is excluded
            bExcluded = pPage->IsExcluded();
        }

        // create specialized primitives for focus, select and PagePreview itself
        const bool bCreateBitmap(!aBitmapEx.IsEmpty());
        const bool bCreateFocused(GetPageDescriptor()->IsFocused());
        const bool bCreateSelected(GetPageDescriptor()->IsSelected());

        const sal_uInt32 nCount(
            (bCreateBitmap ? 1 : 0) +       // bitmap itself
            1 +                             // border around bitmap (always)
            1 +                             // FadeEffect, PageName and PageNumber visualisation (always)
            (bCreateFocused ? 1 : 0) +      // create focused
            (bCreateSelected ? 1 : 0)       // create selected
            );
        sal_uInt32 nInsert(0);
        Primitive2DSequence xRetval(nCount);

        if(bCreateBitmap)
        {
            // add selection indicator if used
            xRetval[nInsert++] = Primitive2DReference(new SdPageObjectPageBitmapPrimitive(aInnerRange, aBitmapEx));
        }

        if(true)
        {
            // add border (always)
            xRetval[nInsert++] = Primitive2DReference(new SdPageObjectBorderPrimitive(aInnerRange));
        }

        if(true)
        {
            // add fade effext, page name and number if used
            xRetval[nInsert++] = Primitive2DReference(new SdPageObjectFadeNameNumberPrimitive(
                aInnerRange,
                aPageName,
                nPageNumber,
                aPageNameFont,
                aPageNumberAreaModelSize,
                bShowFadeEffectIcon,
                bShowCommentsIcon,
                bExcluded));
        }

        if(bCreateSelected)
        {
            // add selection indicator if used
            xRetval[nInsert++] = Primitive2DReference(new SdPageObjectSelectPrimitive(aInnerRange));
        }

        if(bCreateFocused)
        {
            // add focus indicator if used
            xRetval[nInsert++] = Primitive2DReference(new SdPageObjectFocusPrimitive(aInnerRange, bCreateSelected));
        }

        return xRetval;
    }
    else
    {
        // Call parent. Output to printer or metafile will use vector data, not cached bitmaps
        return ViewObjectContactOfPageObj::createPrimitive2DSequence(rDisplayInfo);
    }
}

BitmapEx PageObjectViewObjectContact::CreatePreview (const DisplayInfo& /*rDisplayInfo*/)
{
    const SdPage* pPage = static_cast<const SdPage*>(GetPage());
    OutputDevice* pDevice = GetObjectContact().TryToGetOutputDevice();

    if(pDevice)
    {
        Rectangle aPreviewPixelBox (GetBoundingBox(*pDevice,PreviewBoundingBox,PixelCoordinateSystem));

        PreviewRenderer aRenderer (pDevice);
        Image aPreview (aRenderer.RenderPage(
            pPage,
            aPreviewPixelBox.GetSize(),
            String()));

        return aPreview.GetBitmapEx();
    }
    else
    {
        return BitmapEx();
    }
}




BitmapEx PageObjectViewObjectContact::GetPreview (
    const DisplayInfo& rDisplayInfo,
    const Rectangle& rNewSizePixel)
{
    BitmapEx aBitmap;

    try
    {
        // assert when this call is issued indirectly from the destructor of
        // this instance. This is not allowed and needs to be looked at
        OSL_ENSURE(!mbInDestructor, "Higher call inside PageObjectViewObjectContact in destructor (!)");

        if (!mbInDestructor)
        {
            if (mpCache != NULL)
            {
                aBitmap = mpCache->GetPreviewBitmap(
                    GetPage(),
                    rNewSizePixel.GetSize());
                mpCache->SetPreciousFlag(GetPage(), true);
            }
            else
                aBitmap = CreatePreview(rDisplayInfo);
        }
    }
    catch (const ::com::sun::star::uno::Exception&)
    {
        DBG_UNHANDLED_EXCEPTION();
    }

    return aBitmap;
}




const SdrPage* PageObjectViewObjectContact::GetPage (void) const
{
    return static_cast<PageObjectViewContact&>(GetViewContact()).GetPage();
}




void PageObjectViewObjectContact::ActionChanged (void)
{
    // Even when we are called from destructor we still have to invalide
    // the preview bitmap in the cache.
    const SdrPage* pPage = GetPage();

    if(pPage)
    {
        SdDrawDocument* pDocument = dynamic_cast<SdDrawDocument*>(pPage->GetModel());
        if (mpCache!=NULL && pPage!=NULL && pDocument!=NULL)
        {
            cache::PageCacheManager::Instance()->InvalidatePreviewBitmap(
                pDocument->getUnoModel(),
                pPage);
        }
    }

    // call parent
    ViewObjectContactOfPageObj::ActionChanged();
}

//////////////////////////////////////////////////////////////////////////////
// helper MouseOverEffectPrimitive
//
// Used to allow view-dependent primitive definition. For that purpose, the
// initially created primitive (here: in createMouseOverEffectPrimitive2DSequence())
// always has to be view-independent, but the decomposition is made view-dependent.
// Very simple primitive which just remembers the discrete data and applies
// it at decomposition time.

class MouseOverEffectPrimitive : public drawinglayer::primitive2d::BufferedDecompositionPrimitive2D
{
private:
    basegfx::B2DRange           maLogicRange;
    sal_uInt32                  mnDiscreteOffset;
    sal_uInt32                  mnDiscreteWidth;
    basegfx::BColor             maRGBColor;

protected:
    virtual drawinglayer::primitive2d::Primitive2DSequence create2DDecomposition(
        const drawinglayer::geometry::ViewInformation2D& rViewInformation) const;

public:
    MouseOverEffectPrimitive(
        const basegfx::B2DRange& rLogicRange,
        sal_uInt32 nDiscreteOffset,
        sal_uInt32 nDiscreteWidth,
        const basegfx::BColor& rRGBColor)
    :   drawinglayer::primitive2d::BufferedDecompositionPrimitive2D(),
        maLogicRange(rLogicRange),
        mnDiscreteOffset(nDiscreteOffset),
        mnDiscreteWidth(nDiscreteWidth),
        maRGBColor(rRGBColor)
    {}

    // data access
    const basegfx::B2DRange& getLogicRange() const { return maLogicRange; }
    sal_uInt32 getDiscreteOffset() const { return mnDiscreteOffset; }
    sal_uInt32 getDiscreteWidth() const { return mnDiscreteWidth; }
    const basegfx::BColor& getRGBColor() const { return maRGBColor; }

    virtual bool operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const;

    DeclPrimitrive2DIDBlock()
};

drawinglayer::primitive2d::Primitive2DSequence MouseOverEffectPrimitive::create2DDecomposition(
    const drawinglayer::geometry::ViewInformation2D& rViewInformation) const
{
    // get logic sizes in object coordinate system
    const double fDiscreteWidth((rViewInformation.getInverseObjectToViewTransformation() * basegfx::B2DVector(1.0, 0.0)).getLength());
    const double fOffset(fDiscreteWidth * getDiscreteOffset());
    const double fWidth(fDiscreteWidth * getDiscreteWidth());

    // create range (one pixel less to get a good fitting)
    basegfx::B2DRange aRange(
        getLogicRange().getMinimum(),
        getLogicRange().getMaximum() - basegfx::B2DTuple(fDiscreteWidth, fDiscreteWidth));

    // grow range
    aRange.grow(fOffset - (fWidth * 0.5));

    // create fat line with parameters. The formerly hand-painted edge
    // roundings will now be done using rounded edges of this fat line
    const basegfx::B2DPolygon aPolygon(basegfx::tools::createPolygonFromRect(aRange));
    const drawinglayer::attribute::LineAttribute aLineAttribute(getRGBColor(), fWidth);
    const drawinglayer::primitive2d::Primitive2DReference xReference(
        new drawinglayer::primitive2d::PolygonStrokePrimitive2D(
            aPolygon,
            aLineAttribute));

    return drawinglayer::primitive2d::Primitive2DSequence(&xReference, 1);
}

bool MouseOverEffectPrimitive::operator==( const drawinglayer::primitive2d::BasePrimitive2D& rPrimitive ) const
{
    if(drawinglayer::primitive2d::BufferedDecompositionPrimitive2D::operator==(rPrimitive))
    {
        const MouseOverEffectPrimitive& rCompare = static_cast< const MouseOverEffectPrimitive& >(rPrimitive);

        return (getLogicRange() == rCompare.getLogicRange()
            && getDiscreteOffset() == rCompare.getDiscreteOffset()
            && getDiscreteWidth() == rCompare.getDiscreteWidth()
            && getRGBColor() == rCompare.getRGBColor());
    }

    return false;
}

ImplPrimitrive2DIDBlock(MouseOverEffectPrimitive, PRIMITIVE2D_ID_SDMOUSEOVEREFFECTPRIMITIVE)

//////////////////////////////////////////////////////////////////////////////

drawinglayer::primitive2d::Primitive2DSequence PageObjectViewObjectContact::createMouseOverEffectPrimitive2DSequence()
{
    drawinglayer::primitive2d::Primitive2DSequence aRetval;

    if(GetPageDescriptor()->IsSelected() && mpProperties.get() && mpProperties->IsShowSelection())
    {
        // When the selection frame is visualized the mouse over frame is not
        // visible and does not have to be created.
    }
    else
    {
        const PageObjectViewContact& rPaObVOC(static_cast<PageObjectViewContact&>(GetViewContact()));
        const Rectangle aBoundingBox(rPaObVOC.GetPageObject().GetLastBoundRect());
        const basegfx::B2DRange aLogicRange(aBoundingBox.Left(), aBoundingBox.Top(), aBoundingBox.Right(), aBoundingBox.Bottom());
        const basegfx::BColor aSelectionColor(mpProperties->GetSelectionColor().getBColor());
        const drawinglayer::primitive2d::Primitive2DReference aReference(
            new MouseOverEffectPrimitive(
                aLogicRange,
                mnMouseOverEffectOffset,
                mnMouseOverEffectThickness,
                aSelectionColor));

        aRetval = drawinglayer::primitive2d::Primitive2DSequence(&aReference, 1);
    }

    return aRetval;
}




SvBorder PageObjectViewObjectContact::CalculatePageModelBorder (
    OutputDevice* pDevice,
    int nPageCount)
{
    SvBorder aModelBorder;

    if (pDevice != NULL)
    {
        // 1. Initialize the border with the values that do not depend on
        // the device.
        Size aTopLeftBorders (pDevice->PixelToLogic (Size (
            mnPageNumberOffset+1,
            mnSelectionIndicatorOffset + mnSelectionIndicatorThickness)));
        Size aBottomRightBorders (pDevice->PixelToLogic (Size (
            mnSelectionIndicatorOffset + mnSelectionIndicatorThickness,
            mnFadeEffectIndicatorOffset)));
        aModelBorder = SvBorder (
            aTopLeftBorders.Width(),
            aTopLeftBorders.Height(),
            aBottomRightBorders.Width(),
            aBottomRightBorders.Height());


        // 2. Add the device dependent values.

        // Calculate the area of the page number.
        Size aPageNumberModelSize (
            CalculatePageNumberAreaModelSize (pDevice, nPageCount));

        // Update the border.
        aModelBorder.Left() += aPageNumberModelSize.Width();
        // The height of the page number area is the same as the height of
        // the page name area.
        aModelBorder.Bottom() += aPageNumberModelSize.Height();
    }

    return aModelBorder;
}




Size PageObjectViewObjectContact::CalculatePageNumberAreaModelSize (
    OutputDevice* pDevice,
    int nPageCount)
{
    // Set the correct font.
    Font aOriginalFont (pDevice->GetFont());
    pDevice->SetFont(*FontProvider::Instance().GetFont(*pDevice));

    String sPageNumberTemplate;
    if (nPageCount < 10)
        sPageNumberTemplate = String::CreateFromAscii("9");
    else if (nPageCount < 100)
        sPageNumberTemplate = String::CreateFromAscii("99");
    else if (nPageCount < 200)
        // Just for the case that 1 is narrower than 9.
        sPageNumberTemplate = String::CreateFromAscii("199");
    else if (nPageCount < 1000)
        sPageNumberTemplate = String::CreateFromAscii("999");
    else
        sPageNumberTemplate = String::CreateFromAscii("9999");
    // More then 9999 pages are not handled.

    Size aSize (
        pDevice->GetTextWidth (sPageNumberTemplate),
        pDevice->GetTextHeight ());

    pDevice->SetFont (aOriginalFont);

    return aSize;
}




model::SharedPageDescriptor
    PageObjectViewObjectContact::GetPageDescriptor (void) const
{
    PageObjectViewContact& rViewContact (
        static_cast<PageObjectViewContact&>(GetViewContact()));
    PageObject& rPageObject (
        static_cast<PageObject&>(rViewContact.GetPageObject()));
    return rPageObject.GetDescriptor();
}



} } } // end of namespace ::sd::slidesorter::view