File: scroll.cpp

package info (click to toggle)
wxpython3.0 3.0.2.0%2Bdfsg-4
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 482,760 kB
  • ctags: 518,293
  • sloc: cpp: 2,127,226; python: 294,045; makefile: 51,942; ansic: 19,033; sh: 3,013; xml: 1,629; perl: 17
file content (1478 lines) | stat: -rw-r--r-- 52,596 bytes parent folder | download | duplicates (10)
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
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
/////////////////////////////////////////////////////////////////////////////
// Name:        scroll.cpp
// Purpose:     wxScrolled sample
// Author:      Robert Roebling
// Copyright:   (C) 1998 Robert Roebling, 2002 Ron Lee, 2003 Matt Gregory
//              (C) 2008 Vadim Zeitlin
// Licence:     wxWindows licence
/////////////////////////////////////////////////////////////////////////////

#include "wx/wxprec.h"

#ifdef __BORLANDC__
    #pragma hdrstop
#endif

#ifndef WX_PRECOMP
    #include "wx/wx.h"
#endif

#include "wx/sizer.h"
#include "wx/log.h"
#include "wx/tglbtn.h"

#ifndef wxHAS_IMAGES_IN_RESOURCES
    #include "../sample.xpm"
#endif

// ----------------------------------------------------------------------------
// a trivial example
// ----------------------------------------------------------------------------

// MySimpleCanvas: a scrolled window which draws a simple rectangle
class MySimpleCanvas : public wxScrolled<wxWindow>
{
public:
    enum
    {
        // these numbers are not multiple of 10 (our scroll step) to test for
        // the absence of rounding errors (e.g. we should have one more page
        // than WIDTH/10 to show the right side of the rectangle)
        WIDTH = 292,
        HEIGHT = 297
    };

    MySimpleCanvas(wxWindow *parent)
        : wxScrolled<wxWindow>(parent, wxID_ANY)
    {
        SetScrollRate( 10, 10 );
        SetVirtualSize( WIDTH, HEIGHT );
        SetBackgroundColour( *wxWHITE );

        Connect(wxEVT_PAINT, wxPaintEventHandler(MySimpleCanvas::OnPaint));
    }

private:
    void OnPaint(wxPaintEvent& WXUNUSED(event))
    {
        wxPaintDC dc(this);

        // this call is vital: it adjusts the dc to account for the current
        // scroll offset
        PrepareDC(dc);

        dc.SetPen( *wxRED_PEN );
        dc.SetBrush( *wxTRANSPARENT_BRUSH );
        dc.DrawRectangle( 0, 0, WIDTH, HEIGHT );
    }
};


// MySimpleFrame: a frame which contains a MySimpleCanvas
class MySimpleFrame : public wxFrame
{
public:
    MySimpleFrame(wxWindow *parent)
        : wxFrame(parent, wxID_ANY, "MySimpleCanvas")
    {
        new MySimpleCanvas(this);

        // ensure that we have scrollbars initially
        SetClientSize(MySimpleCanvas::WIDTH/2, MySimpleCanvas::HEIGHT/2);

        Show();
    }
};

// ----------------------------------------------------------------------
// a more complex example
// ----------------------------------------------------------------------

// MyCanvas
class MyCanvas : public wxScrolled<wxPanel>
{
public:
    MyCanvas(wxWindow *parent);

private:
    void OnPaint(wxPaintEvent& event);
    void OnQueryPosition(wxCommandEvent& event);
    void OnAddButton(wxCommandEvent& event);
    void OnDeleteButton(wxCommandEvent& event);
    void OnMoveButton(wxCommandEvent& event);
    void OnScrollWin(wxCommandEvent& event);
    void OnMouseRightDown(wxMouseEvent& event);
    void OnMouseWheel(wxMouseEvent& event);

    wxButton *m_button;

    wxDECLARE_EVENT_TABLE();
};

class MyCanvasFrame : public wxFrame
{
public:
    MyCanvasFrame(wxWindow *parent)
        : wxFrame(parent, wxID_ANY, "MyCanvas")
    {
        m_canvas = new MyCanvas(this);

        wxMenu *menuFile = new wxMenu();
        menuFile->Append(wxID_DELETE, "&Delete all");
        menuFile->Append(wxID_NEW, "Insert &new");

        wxMenuBar *mbar = new wxMenuBar();
        mbar->Append(menuFile, "&File");
        SetMenuBar( mbar );

        Connect(wxID_DELETE, wxEVT_MENU,
                wxCommandEventHandler(MyCanvasFrame::OnDeleteAll));
        Connect(wxID_NEW, wxEVT_MENU,
                wxCommandEventHandler(MyCanvasFrame::OnInsertNew));

        Show();
    }

private:
    void OnDeleteAll(wxCommandEvent& WXUNUSED(event))
    {
        m_canvas->DestroyChildren();
    }

    void OnInsertNew(wxCommandEvent& WXUNUSED(event))
    {
        (void)new wxButton(m_canvas, wxID_ANY, "Hello", wxPoint(100,100));
    }

    MyCanvas *m_canvas;
};

// ----------------------------------------------------------------------------
// example using sizers with wxScrolled
// ----------------------------------------------------------------------------

const wxSize SMALL_BUTTON( 100, 50 );
const wxSize LARGE_BUTTON( 300, 200 );

class MySizerScrolledWindow : public wxScrolled<wxWindow>
{
public:
    MySizerScrolledWindow(wxWindow *parent);

private:
    // this button can be clicked to change its own size in the handler below,
    // the window size will be automatically adjusted to fit the button
    wxButton *m_button;

    void OnResizeClick(wxCommandEvent& event);
};

class MySizerFrame : public wxFrame
{
public:
    MySizerFrame(wxWindow *parent)
        : wxFrame(parent, wxID_ANY, "MySizerScrolledWindow")
    {
        new MySizerScrolledWindow(this);

        // ensure that the scrollbars appear when the button becomes large
        SetClientSize(LARGE_BUTTON/2);
        Show();
    }
};

// ----------------------------------------------------------------------------
// example showing scrolling only part of the window
// ----------------------------------------------------------------------------

// this window consists of an empty space in its corner, column labels window
// along its top, row labels window along its left hand side and a canvas in
// the remaining space

class MySubColLabels : public wxWindow
{
public:
    MySubColLabels(wxScrolled<wxWindow> *parent)
        : wxWindow(parent, wxID_ANY)
    {
        m_owner = parent;

        Connect(wxEVT_PAINT, wxPaintEventHandler(MySubColLabels::OnPaint));
    }

private:
    void OnPaint(wxPaintEvent& WXUNUSED(event))
    {
        wxPaintDC dc(this);

        // This is wrong..  it will translate both x and y if the
        // window is scrolled, the label windows are active in one
        // direction only.  Do the action below instead -- RL.
        //m_owner->PrepareDC( dc );

        int xScrollUnits, xOrigin;

        m_owner->GetViewStart( &xOrigin, 0 );
        m_owner->GetScrollPixelsPerUnit( &xScrollUnits, 0 );
        dc.SetDeviceOrigin( -xOrigin * xScrollUnits, 0 );

        dc.DrawText("Column 1", 5, 5);
        dc.DrawText("Column 2", 105, 5);
        dc.DrawText("Column 3", 205, 5);
    }

    wxScrolled<wxWindow> *m_owner;
};

class MySubRowLabels : public wxWindow
{
public:
    MySubRowLabels(wxScrolled<wxWindow> *parent)
        : wxWindow(parent, wxID_ANY)
    {
        m_owner = parent;

        Connect(wxEVT_PAINT, wxPaintEventHandler(MySubRowLabels::OnPaint));
    }

private:
    void OnPaint(wxPaintEvent& WXUNUSED(event))
    {
        wxPaintDC dc(this);

        // This is wrong..  it will translate both x and y if the
        // window is scrolled, the label windows are active in one
        // direction only.  Do the action below instead -- RL.
        //m_owner->PrepareDC( dc );

        int yScrollUnits, yOrigin;

        m_owner->GetViewStart( 0, &yOrigin );
        m_owner->GetScrollPixelsPerUnit( 0, &yScrollUnits );
        dc.SetDeviceOrigin( 0, -yOrigin * yScrollUnits );

        dc.DrawText("Row 1", 5, 5);
        dc.DrawText("Row 2", 5, 30);
        dc.DrawText("Row 3", 5, 55);
        dc.DrawText("Row 4", 5, 80);
        dc.DrawText("Row 5", 5, 105);
        dc.DrawText("Row 6", 5, 130);
    }

    wxScrolled<wxWindow> *m_owner;
};

class MySubCanvas : public wxPanel
{
public:
    MySubCanvas(wxScrolled<wxWindow> *parent, wxWindow *cols, wxWindow *rows)
        : wxPanel(parent, wxID_ANY)
    {
        m_owner = parent;
        m_colLabels = cols;
        m_rowLabels = rows;

        (void)new wxButton(this, wxID_ANY, "Hallo I",
                           wxPoint(0,50), wxSize(100,25) );
        (void)new wxButton(this, wxID_ANY, "Hallo II",
                           wxPoint(200,50), wxSize(100,25) );

        (void)new wxTextCtrl(this, wxID_ANY, "Text I",
                             wxPoint(0,100), wxSize(100,25) );
        (void)new wxTextCtrl(this, wxID_ANY, "Text II",
                             wxPoint(200,100), wxSize(100,25) );

        (void)new wxComboBox(this, wxID_ANY, "ComboBox I",
                             wxPoint(0,150), wxSize(100,25));
        (void)new wxComboBox(this, wxID_ANY, "ComboBox II",
                             wxPoint(200,150), wxSize(100,25));

        SetBackgroundColour("WHEAT");

        Connect(wxEVT_PAINT, wxPaintEventHandler(MySubCanvas::OnPaint));
    }

    // override the base class function so that when this window is scrolled,
    // the labels are scrolled in sync
    virtual void ScrollWindow(int dx, int dy, const wxRect *rect)
    {
        wxPanel::ScrollWindow( dx, dy, rect );
        m_colLabels->ScrollWindow( dx, 0, rect );
        m_rowLabels->ScrollWindow( 0, dy, rect );
    }

private:
    void OnPaint(wxPaintEvent& WXUNUSED(event))
    {
        wxPaintDC dc( this );
        m_owner->PrepareDC( dc );

        dc.SetPen( *wxBLACK_PEN );

        // OK, let's assume we are a grid control and we have two
        // grid cells. Here in OnPaint we want to know which cell
        // to redraw so that we prevent redrawing cells that don't
        // need to get redrawn. We have one cell at (0,0) and one
        // more at (200,0), both having a size of (100,25).

        // We can query how much the window has been scrolled
        // by calling CalcUnscrolledPosition()

        int scroll_x = 0;
        int scroll_y = 0;
        m_owner->CalcUnscrolledPosition( scroll_x, scroll_y, &scroll_x, &scroll_y );

        // We also need to know the size of the window to see which
        // cells are completely hidden and not get redrawn

        int size_x = 0;
        int size_y = 0;
        GetClientSize( &size_x, &size_y );

        // First cell: (0,0)(100,25)
        // It it on screen?
        if ((0+100-scroll_x > 0) && (0+25-scroll_y > 0) &&
            (0-scroll_x < size_x) && (0-scroll_y < size_y))
        {
            // Has the region on screen been exposed?
            if (IsExposed(0,0,100,25))
            {
                dc.DrawRectangle( 0, 0, 100, 25 );
                dc.DrawText("First Cell", 5, 5);
            }
        }


        // Second cell: (0,200)(100,25)
        // It it on screen?
        if ((200+100-scroll_x > 0) && (0+25-scroll_y > 0) &&
            (200-scroll_x < size_x) && (0-scroll_y < size_y))
        {
            // Has the region on screen been exposed?
            if (IsExposed(200,0,100,25))
            {
                dc.DrawRectangle( 200, 0, 100, 25 );
                dc.DrawText("Second Cell", 205, 5);
            }
        }
    }

    wxScrolled<wxWindow> *m_owner;
    wxWindow *m_colLabels,
             *m_rowLabels;
};

class MySubScrolledWindow : public wxScrolled<wxWindow>
{
public:
    enum
    {
        CORNER_WIDTH = 60,
        CORNER_HEIGHT = 25
    };

    MySubScrolledWindow(wxWindow *parent)
        : wxScrolled<wxWindow>(parent, wxID_ANY)
    {
        // create the children
        MySubColLabels *cols = new MySubColLabels(this);
        MySubRowLabels *rows = new MySubRowLabels(this);

        m_canvas = new MySubCanvas(this, cols, rows);

        // lay them out
        wxFlexGridSizer *sizer = new wxFlexGridSizer(2, 2, 10, 10);
        sizer->Add(CORNER_WIDTH, CORNER_HEIGHT); // just a spacer
        sizer->Add(cols, wxSizerFlags().Expand());
        sizer->Add(rows, wxSizerFlags().Expand());
        sizer->Add(m_canvas, wxSizerFlags().Expand());
        sizer->AddGrowableRow(1);
        sizer->AddGrowableCol(1);
        SetSizer(sizer);

        // this is the key call: it means that only m_canvas will be scrolled
        // and not this window itself
        SetTargetWindow(m_canvas);

        SetScrollbars(10, 10, 50, 50);

        Connect(wxEVT_SIZE, wxSizeEventHandler(MySubScrolledWindow::OnSize));
    }

protected:
    // scrolled windows which use scroll target different from the window
    // itself must override this virtual method
    virtual wxSize GetSizeAvailableForScrollTarget(const wxSize& size)
    {
        // decrease the total size by the size of the non-scrollable parts
        // above/to the left of the canvas
        wxSize sizeCanvas(size);
        sizeCanvas.x -= 60;
        sizeCanvas.y -= 25;
        return sizeCanvas;
    }

private:
    void OnSize(wxSizeEvent& WXUNUSED(event))
    {
        // We need to override OnSize so that our scrolled
        // window a) does call Layout() to use sizers for
        // positioning the controls but b) does not query
        // the sizer for their size and use that for setting
        // the scrollable area as set that ourselves by
        // calling SetScrollbar() further down.

        Layout();

        AdjustScrollbars();
    }

    MySubCanvas *m_canvas;
};

class MySubFrame : public wxFrame
{
public:
    MySubFrame(wxWindow *parent)
        : wxFrame(parent, wxID_ANY, "MySubScrolledWindow")
    {
        new MySubScrolledWindow(this);

        Show();
    }
};

// ----------------------------------------------------------------------------
// more simple examples of wxScrolled usage
// ----------------------------------------------------------------------------

// base class for both of them
class MyScrolledWindowBase : public wxScrolled<wxWindow>
{
public:
    MyScrolledWindowBase(wxWindow *parent)
        : wxScrolled<wxWindow>(parent, wxID_ANY,
                               wxDefaultPosition, wxDefaultSize,
                               wxBORDER_SUNKEN)
    {
        m_nLines = 50;
        m_winSync = NULL;
        m_inDoSync = false;

        wxClientDC dc(this);
        dc.GetTextExtent("Line 17", NULL, &m_hLine);
    }

    // this scrolled window can be synchronized with another one: if this
    // function is called with a non-NULL pointer, the given window will be
    // scrolled to the same position as this one
    void SyncWith(MyScrolledWindowBase *win)
    {
        m_winSync = win;

        DoSyncIfNecessary();
    }

    virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL)
    {
        wxScrolled<wxWindow>::ScrollWindow(dx, dy, rect);

        DoSyncIfNecessary();
    }

protected:
    // the height of one line on screen
    int m_hLine;

    // the number of lines we draw
    size_t m_nLines;

private:
    bool WasScrolledFirst() const { return m_inDoSync; }

    void DoSyncIfNecessary()
    {
        if ( m_winSync && !m_winSync->WasScrolledFirst() )
        {
            m_inDoSync = true;

            m_winSync->Scroll(GetViewStart());

            m_inDoSync = false;
        }
    }

    // the window to synchronize with this one or NULL
    MyScrolledWindowBase *m_winSync;

    // the flag preventing infinite recursion which would otherwise happen if
    // one window synchronized the other one which in turn synchronized this
    // one and so on
    bool m_inDoSync;
};

// this class does "stupid" redrawing - it redraws everything each time
// and sets the scrollbar extent directly.

class MyScrolledWindowDumb : public MyScrolledWindowBase
{
public:
    MyScrolledWindowDumb(wxWindow *parent) : MyScrolledWindowBase(parent)
    {
        // no horz scrolling
        SetScrollbars(0, m_hLine, 0, m_nLines + 1, 0, 0, true /* no refresh */);
    }

    virtual void OnDraw(wxDC& dc);
};

// this class does "smart" redrawing - only redraws the lines which must be
// redrawn and sets the scroll rate and virtual size to affect the
// scrollbars.
//
// Note that this class should produce identical results to the one above.

class MyScrolledWindowSmart : public MyScrolledWindowBase
{
public:
    MyScrolledWindowSmart(wxWindow *parent) : MyScrolledWindowBase(parent)
    {
        // no horz scrolling
        SetScrollRate( 0, m_hLine );
        SetVirtualSize( wxDefaultCoord, ( m_nLines + 1 ) * m_hLine );
    }

    virtual void OnDraw(wxDC& dc);
};

// ----------------------------------------------------------------------------
// implements a text viewer with simple block selection to test auto-scrolling
// functionality
// ----------------------------------------------------------------------------

class MyAutoScrollingWindow : public wxScrolled<wxWindow>
{
public:
    MyAutoScrollingWindow( wxWindow* parent );
    wxRect DeviceCoordsToGraphicalChars(wxRect updRect) const;
    wxPoint DeviceCoordsToGraphicalChars(wxPoint pos) const;
    wxPoint GraphicalCharToDeviceCoords(wxPoint pos) const;
    wxRect LogicalCoordsToGraphicalChars(wxRect updRect) const;
    wxPoint LogicalCoordsToGraphicalChars(wxPoint pos) const;
    wxPoint GraphicalCharToLogicalCoords(wxPoint pos) const;
    void MyRefresh();
    bool IsSelected(int chX, int chY) const;
    static bool IsInside(int k, int bound1, int bound2);
    static wxRect DCNormalize(int x, int y, int w, int h);

private:
    // event handlers
    void OnDraw(wxDC& dc);
    void OnMouseLeftDown(wxMouseEvent& event);
    void OnMouseLeftUp(wxMouseEvent& event);
    void OnMouseMove(wxMouseEvent& event);
    void OnMouseCaptureLost(wxMouseCaptureLostEvent& event);
    void OnScroll(wxScrollWinEvent& event);

    // test data variables
    static const char* sm_testData;
    static const int sm_lineCnt; // line count
    static const int sm_lineLen; // line length in characters
    // sizes for graphical data
    int m_fontH, m_fontW;
    // selection tracking
    wxPoint m_selStart; // beginning of blockwise selection
    wxPoint m_cursor;   // end of blockwise selection (mouse position)

    // gui stuff
    wxFont m_font;


    wxDECLARE_EVENT_TABLE();
};

class MyAutoFrame : public wxFrame
{
public:
    MyAutoFrame(wxWindow *parent)
        : wxFrame(parent, wxID_ANY, "MyAutoScrollingWindow")
    {
        new MyAutoScrollingWindow(this);

        Show();
    }
};


// ----------------------------------------------------------------------------
// MyFrame: the main application frame showing all the classes above
// ----------------------------------------------------------------------------

class MyFrame: public wxFrame
{
public:
    MyFrame();

private:
    void OnAbout(wxCommandEvent& event);
    void OnQuit(wxCommandEvent& event);

    void OnTestSimple(wxCommandEvent& WXUNUSED(event)) { new MySimpleFrame(this); }
    void OnTestCanvas(wxCommandEvent& WXUNUSED(event)) { new MyCanvasFrame(this); }
    void OnTestSizer(wxCommandEvent& WXUNUSED(event)) { new MySizerFrame(this); }
    void OnTestSub(wxCommandEvent& WXUNUSED(event)) { new MySubFrame(this); }
    void OnTestAuto(wxCommandEvent& WXUNUSED(event)) { new MyAutoFrame(this); }

    void OnToggleSync(wxCommandEvent& event);
    void OnScrollbarVisibility(wxCommandEvent& event);

    MyScrolledWindowBase *m_win1,
                         *m_win2;

    wxDECLARE_EVENT_TABLE();
};

// ----------------------------------------------------------------------------
// MyApp
// ----------------------------------------------------------------------------

class MyApp : public wxApp
{
public:
    virtual bool OnInit();
};


// ============================================================================
// implementation
// ============================================================================

// ----------------------------------------------------------------------------
// MyCanvas
// ----------------------------------------------------------------------------

const wxWindowIDRef ID_ADDBUTTON   = wxWindow::NewControlId();
const wxWindowIDRef ID_DELBUTTON   = wxWindow::NewControlId();
const wxWindowIDRef ID_MOVEBUTTON  = wxWindow::NewControlId();
const wxWindowIDRef ID_SCROLLWIN   = wxWindow::NewControlId();
const wxWindowIDRef ID_QUERYPOS    = wxWindow::NewControlId();

const wxWindowIDRef ID_NEWBUTTON   = wxWindow::NewControlId();

wxBEGIN_EVENT_TABLE(MyCanvas, wxScrolled<wxPanel>)
    EVT_PAINT(                  MyCanvas::OnPaint)
    EVT_RIGHT_DOWN(             MyCanvas::OnMouseRightDown)
    EVT_MOUSEWHEEL(             MyCanvas::OnMouseWheel)
    EVT_BUTTON( ID_QUERYPOS,    MyCanvas::OnQueryPosition)
    EVT_BUTTON( ID_ADDBUTTON,   MyCanvas::OnAddButton)
    EVT_BUTTON( ID_DELBUTTON,   MyCanvas::OnDeleteButton)
    EVT_BUTTON( ID_MOVEBUTTON,  MyCanvas::OnMoveButton)
    EVT_BUTTON( ID_SCROLLWIN,   MyCanvas::OnScrollWin)
wxEND_EVENT_TABLE()

MyCanvas::MyCanvas(wxWindow *parent)
    : wxScrolled<wxPanel>(parent, wxID_ANY,
                          wxDefaultPosition, wxDefaultSize,
                          wxSUNKEN_BORDER | wxTAB_TRAVERSAL)
{
    // you can use either a single SetScrollbars() call or these 2 functions,
    // usually using them is better because you normally won't need to change
    // the scroll rate in the future and the sizer can be used to update the
    // virtual size automatically
    SetScrollRate( 10, 10 );
    SetVirtualSize( 500, 1000 );

    (void) new wxButton( this, ID_ADDBUTTON,  "add button", wxPoint(10,10) );
    (void) new wxButton( this, ID_DELBUTTON,  "del button", wxPoint(10,40) );
    (void) new wxButton( this, ID_MOVEBUTTON, "move button", wxPoint(150,10) );
    (void) new wxButton( this, ID_SCROLLWIN,  "scroll win", wxPoint(250,10) );

    wxPanel *test = new wxPanel( this, wxID_ANY,
                                 wxPoint(10, 110), wxSize(130,50),
                                 wxSIMPLE_BORDER | wxTAB_TRAVERSAL );
    test->SetBackgroundColour( "WHEAT" );

    SetBackgroundColour( "BLUE" );
}

void MyCanvas::OnMouseRightDown( wxMouseEvent &event )
{
    wxPoint pt( event.GetPosition() );
    int x,y;
    CalcUnscrolledPosition( pt.x, pt.y, &x, &y );
    wxLogMessage("Mouse down event at: %d %d, scrolled: %d %d",
                 pt.x, pt.y, x, y);
}

void MyCanvas::OnMouseWheel( wxMouseEvent &event )
{
    wxPoint pt( event.GetPosition() );
    int x,y;
    CalcUnscrolledPosition( pt.x, pt.y, &x, &y );
    wxLogMessage( "Mouse wheel event at: %d %d, scrolled: %d %d\n"
                  "Rotation: %d, delta = %d",
                  pt.x, pt.y, x, y,
                  event.GetWheelRotation(), event.GetWheelDelta() );

    event.Skip();
}

void MyCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) )
{
    wxPaintDC dc( this );
    PrepareDC( dc );

    dc.DrawText( "Press right mouse button to test calculations!", 160, 50 );

    dc.DrawText( "Some text", 140, 140 );

    dc.DrawRectangle( 100, 160, 200, 200 );
}

void MyCanvas::OnQueryPosition( wxCommandEvent &WXUNUSED(event) )
{
    wxPoint pt( m_button->GetPosition() );
    wxLogMessage( "Position of \"Query position\" is %d %d", pt.x, pt.y );
    pt = ClientToScreen( pt );
    wxLogMessage("Position of \"Query position\" on screen is %d %d",
                 pt.x, pt.y);
}

void MyCanvas::OnAddButton( wxCommandEvent &WXUNUSED(event) )
{
    wxLogMessage( "Inserting button at position 10,70..." );
    wxButton *button = new wxButton( this, ID_NEWBUTTON, "new button",
                                     wxPoint(10,70), wxSize(80,25) );
    wxPoint pt( button->GetPosition() );
    wxLogMessage( "-> Position after inserting %d %d", pt.x, pt.y );
}

void MyCanvas::OnDeleteButton( wxCommandEvent &WXUNUSED(event) )
{
    wxLogMessage( "Deleting button inserted with \"Add button\"..." );
    wxWindow *win = FindWindow( ID_NEWBUTTON );
    if (win)
       win->Destroy();
    else
       wxLogMessage( "-> No window with id = ID_NEWBUTTON found." );
}

void MyCanvas::OnMoveButton( wxCommandEvent &event )
{
    wxLogMessage( "Moving button 10 pixels downward.." );
    wxWindow *win = FindWindow( event.GetId() );
    wxPoint pt( win->GetPosition() );
    wxLogMessage( "-> Position before move is %d %d", pt.x, pt.y );
    win->Move( wxDefaultCoord, pt.y + 10 );
    pt = win->GetPosition();
    wxLogMessage( "-> Position after move is %d %d", pt.x, pt.y );
}

void MyCanvas::OnScrollWin( wxCommandEvent &WXUNUSED(event) )
{
    wxLogMessage("Scrolling 2 units up.\n"
                 "The white square and the controls should move equally!");
    Scroll( wxDefaultCoord, GetViewStart().y+2 );
}

// ----------------------------------------------------------------------------
// MySizerScrolledWindow
// ----------------------------------------------------------------------------

MySizerScrolledWindow::MySizerScrolledWindow(wxWindow *parent)
    : wxScrolled<wxWindow>(parent)
{
    SetBackgroundColour( "GREEN" );

    // Set the rate we'd like for scrolling.

    SetScrollRate( 5, 5 );

    // Populate a sizer with a 'resizing' button and some other static
    // decoration

    wxFlexGridSizer *sizer = new wxFlexGridSizer(2);

    m_button = new wxButton( this, wxID_RESIZE_FRAME, "Press me",
                             wxDefaultPosition, SMALL_BUTTON );

    sizer->Add(m_button, wxSizerFlags().Centre().Border(wxALL, 20));
    sizer->Add(new wxStaticText(this, wxID_ANY, "This is just"),
               wxSizerFlags().Centre());
    sizer->Add(new wxStaticText(this, wxID_ANY, "some decoration"),
               wxSizerFlags().Centre());
    sizer->Add(new wxStaticText(this, wxID_ANY, "for you to scroll..."),
               wxSizerFlags().Centre());

    // Then use the sizer to set the scrolled region size.

    SetSizer( sizer );

    Connect(wxID_RESIZE_FRAME, wxEVT_BUTTON,
            wxCommandEventHandler(MySizerScrolledWindow::OnResizeClick));
}

void MySizerScrolledWindow::OnResizeClick(wxCommandEvent &WXUNUSED(event))
{
    // Arbitrarily resize the button to change the minimum size of
    // the (scrolled) sizer.

    if ( m_button->GetSize() == SMALL_BUTTON )
        m_button->SetSizeHints(LARGE_BUTTON);
    else
        m_button->SetSizeHints(SMALL_BUTTON);

    // Force update layout and scrollbars, since nothing we do here
    // necessarily generates a size event which would do it for us.
    FitInside();
}

// ----------------------------------------------------------------------------
// MyFrame
// ----------------------------------------------------------------------------

const wxWindowID Scroll_Test_Simple = wxWindow::NewControlId();
const wxWindowID Scroll_Test_Canvas = wxWindow::NewControlId();
const wxWindowID Scroll_Test_Sizers = wxWindow::NewControlId();
const wxWindowID Scroll_Test_Sub    = wxWindow::NewControlId();
const wxWindowID Scroll_Test_Auto   = wxWindow::NewControlId();

const wxWindowID Scroll_TglBtn_Sync = wxWindow::NewControlId();
const wxWindowID Scroll_Radio_ShowScrollbar = wxWindow::NewControlId();

wxBEGIN_EVENT_TABLE(MyFrame,wxFrame)
    EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
    EVT_MENU(wxID_EXIT,  MyFrame::OnQuit)

    EVT_MENU(Scroll_Test_Simple, MyFrame::OnTestSimple)
    EVT_MENU(Scroll_Test_Canvas, MyFrame::OnTestCanvas)
    EVT_MENU(Scroll_Test_Sizers, MyFrame::OnTestSizer)
    EVT_MENU(Scroll_Test_Sub, MyFrame::OnTestSub)
    EVT_MENU(Scroll_Test_Auto, MyFrame::OnTestAuto)

    EVT_TOGGLEBUTTON(Scroll_TglBtn_Sync, MyFrame::OnToggleSync)
    EVT_RADIOBOX(Scroll_Radio_ShowScrollbar, MyFrame::OnScrollbarVisibility)
wxEND_EVENT_TABLE()

MyFrame::MyFrame()
       : wxFrame(NULL, wxID_ANY, "wxWidgets scroll sample")
{
    SetIcon(wxICON(sample));

    wxMenu *menuFile = new wxMenu;
    menuFile->Append(wxID_ABOUT, "&About..");
    menuFile->AppendSeparator();
    menuFile->Append(wxID_EXIT, "E&xit\tAlt-X");

    wxMenu *menuTest = new wxMenu;
    menuTest->Append(Scroll_Test_Simple, "&Simple scroll window\tF1",
                     "Simplest possible scrolled window test.");
    menuTest->Append(Scroll_Test_Canvas, "Scrolled window with &children\tF2",
                     "Scrolled window with controls on it.");
    menuTest->Append(Scroll_Test_Sizers, "Scrolled window with si&zer\tF3",
                     "Scrolled window with children managed by sizer.");
    menuTest->Append(Scroll_Test_Sub, "Scrolled s&ub-window\tF4",
                     "Window only part of which is scrolled.");
    menuTest->Append(Scroll_Test_Auto, "&Auto-scrolled window\tF5",
                     "Window which scrolls when the mouse is held pressed "
                     "outside of it.");

    wxMenuBar *mbar = new wxMenuBar;
    mbar->Append(menuFile, "&File");
    mbar->Append(menuTest, "&Test");

    SetMenuBar( mbar );


    wxPanel *panel = new wxPanel(this);

    const wxSizerFlags flagsExpand(wxSizerFlags(1).Expand());

    wxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
    topsizer->Add(new wxStaticText(panel, wxID_ANY,
        "The windows below should behave in the same way, even though\n"
        "they're implemented quite differently, see the code for details.\n"
        "\n"
        "The lines redrawn during odd/even repaint iterations are drawn in\n"
        "red/blue colour to allow seeing immediately how much is repainted,\n"
        "don't be surprised by this."),
        wxSizerFlags().Centre().Border());

    m_win1 = new MyScrolledWindowDumb(panel);
    m_win2 = new MyScrolledWindowSmart(panel);

    wxSizer *sizerScrollWin = new wxBoxSizer(wxHORIZONTAL);
    sizerScrollWin->Add(m_win1, flagsExpand);
    sizerScrollWin->Add(m_win2, flagsExpand);
    topsizer->Add(sizerScrollWin, flagsExpand);

    const wxSizerFlags
        flagsHBorder(wxSizerFlags().Centre().Border(wxLEFT | wxRIGHT));

    wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);

    // the radio buttons are in the same order as wxSHOW_SB_XXX values but
    // offset by 1
    const wxString visibilities[] = { "&never", "&default", "&always" };
    wxRadioBox *radio = new wxRadioBox(panel, Scroll_Radio_ShowScrollbar,
                                       "Left &scrollbar visibility: ",
                                       wxDefaultPosition, wxDefaultSize,
                                       WXSIZEOF(visibilities), visibilities);
    radio->SetSelection(wxSHOW_SB_DEFAULT + 1);
    sizerBtns->Add(radio, flagsHBorder);

    sizerBtns->Add(new wxToggleButton(panel, Scroll_TglBtn_Sync, "S&ynchronize"),
                   flagsHBorder);

    topsizer->Add(sizerBtns, wxSizerFlags().Centre().Border());

    panel->SetSizer(topsizer);

    wxSize size = panel->GetBestSize();
    SetSizeHints(size);
    SetClientSize(2*size);

    Show();
}

void MyFrame::OnToggleSync(wxCommandEvent& event)
{
    if ( event.IsChecked() )
    {
        m_win1->SyncWith(m_win2);
        m_win2->SyncWith(m_win1);
    }
    else
    {
        m_win1->SyncWith(NULL);
        m_win2->SyncWith(NULL);
    }
}

void MyFrame::OnScrollbarVisibility(wxCommandEvent& event)
{
    m_win1->ShowScrollbars(wxSHOW_SB_NEVER,
                           wxScrollbarVisibility(event.GetSelection() - 1));
}

void MyFrame::OnQuit(wxCommandEvent &WXUNUSED(event))
{
    Close(true);
}

void MyFrame::OnAbout( wxCommandEvent &WXUNUSED(event) )
{
    (void)wxMessageBox( "Scrolled window sample\n"
                        "\n"
                        "Robert Roebling (c) 1998\n"
                        "Vadim Zeitlin (c) 2008\n"
                        "Autoscrolling examples\n"
                        "Ron Lee (c) 2002\n"
                        "Auto-timed-scrolling example\n"
                        "Matt Gregory (c) 2003\n",
                        "About wxWidgets scroll sample",
                        wxICON_INFORMATION | wxOK );
}

// ----------------------------------------------------------------------------
// MyApp
// ----------------------------------------------------------------------------

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
    if ( !wxApp::OnInit() )
        return false;

    new MyFrame();

    return true;
}

// ----------------------------------------------------------------------------
// MyScrolledWindowXXX
// ----------------------------------------------------------------------------

void MyScrolledWindowDumb::OnDraw(wxDC& dc)
{
    // this is useful to see which lines are redrawn
    static size_t s_redrawCount = 0;
    dc.SetTextForeground(s_redrawCount++ % 2 ? *wxRED : *wxBLUE);

    int y = 0;
    for ( size_t line = 0; line < m_nLines; line++ )
    {
        int yPhys;
        CalcScrolledPosition(0, y, NULL, &yPhys);

        dc.DrawText(wxString::Format("Line %u (logical %d, physical %d)",
                                     unsigned(line), y, yPhys), 0, y);
        y += m_hLine;
    }
}

void MyScrolledWindowSmart::OnDraw(wxDC& dc)
{
    // this is useful to see which lines are redrawn
    static size_t s_redrawCount = 0;
    dc.SetTextForeground(s_redrawCount++ % 2 ? *wxRED : *wxBLUE);

    // update region is always in device coords, translate to logical ones
    wxRect rectUpdate = GetUpdateRegion().GetBox();
    CalcUnscrolledPosition(rectUpdate.x, rectUpdate.y,
                           &rectUpdate.x, &rectUpdate.y);

    size_t lineFrom = rectUpdate.y / m_hLine,
           lineTo = rectUpdate.GetBottom() / m_hLine;

    if ( lineTo > m_nLines - 1)
        lineTo = m_nLines - 1;

    int y = lineFrom*m_hLine;
    for ( size_t line = lineFrom; line <= lineTo; line++ )
    {
        int yPhys;
        CalcScrolledPosition(0, y, NULL, &yPhys);

        dc.DrawText(wxString::Format("Line %u (logical %d, physical %d)",
                                     unsigned(line), y, yPhys), 0, y);
        y += m_hLine;
    }
}

// ----------------------------------------------------------------------------
// MyAutoScrollingWindow
// ----------------------------------------------------------------------------

wxBEGIN_EVENT_TABLE(MyAutoScrollingWindow, wxScrolled<wxWindow>)
    EVT_LEFT_DOWN(MyAutoScrollingWindow::OnMouseLeftDown)
    EVT_LEFT_UP(MyAutoScrollingWindow::OnMouseLeftUp)
    EVT_MOTION(MyAutoScrollingWindow::OnMouseMove)
    EVT_MOUSE_CAPTURE_LOST(MyAutoScrollingWindow::OnMouseCaptureLost)
    EVT_SCROLLWIN(MyAutoScrollingWindow::OnScroll)
wxEND_EVENT_TABLE()

MyAutoScrollingWindow::MyAutoScrollingWindow(wxWindow* parent)
    : wxScrolled<wxWindow>(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
                           wxVSCROLL | wxHSCROLL | wxSUNKEN_BORDER),
      m_selStart(-1, -1),
      m_cursor(-1, -1),
      m_font(9, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)
{
    wxClientDC dc(this);
    // query dc for text size
    dc.SetFont(m_font);
    dc.GetTextExtent(wxString("A"), &m_fontW, &m_fontH);
    // set up the virtual window
    SetScrollbars(m_fontW, m_fontH, sm_lineLen, sm_lineCnt);
}

wxRect
MyAutoScrollingWindow::DeviceCoordsToGraphicalChars(wxRect updRect) const
{
    wxPoint pos(updRect.GetPosition());
    pos = DeviceCoordsToGraphicalChars(pos);
    updRect.x = pos.x;
    updRect.y = pos.y;
    updRect.width /= m_fontW;
    updRect.height /= m_fontH;
    // the *CoordsToGraphicalChars() funcs round down to upper-left corner,
    //   so an off-by-one correction is needed
    ++updRect.width; // kludge
    ++updRect.height; // kludge
    return updRect;
}

wxPoint
MyAutoScrollingWindow::DeviceCoordsToGraphicalChars(wxPoint pos) const
{
    pos.x /= m_fontW;
    pos.y /= m_fontH;
    pos += GetViewStart();
    return pos;
}

wxPoint
MyAutoScrollingWindow::GraphicalCharToDeviceCoords(wxPoint pos) const
{
    pos -= GetViewStart();
    pos.x *= m_fontW;
    pos.y *= m_fontH;
    return pos;
}

wxRect
MyAutoScrollingWindow::LogicalCoordsToGraphicalChars(wxRect updRect) const
{
    wxPoint pos(updRect.GetPosition());
    pos = LogicalCoordsToGraphicalChars(pos);
    updRect.x = pos.x;
    updRect.y = pos.y;
    updRect.width /= m_fontW;
    updRect.height /= m_fontH;
    // the *CoordsToGraphicalChars() funcs round down to upper-left corner,
    //   so an off-by-one correction is needed
    ++updRect.width; // kludge
    ++updRect.height; // kludge
    return updRect;
}

wxPoint
MyAutoScrollingWindow::LogicalCoordsToGraphicalChars(wxPoint pos) const
{
    pos.x /= m_fontW;
    pos.y /= m_fontH;
    return pos;
}

wxPoint
MyAutoScrollingWindow::GraphicalCharToLogicalCoords(wxPoint pos) const
{
    pos.x *= m_fontW;
    pos.y *= m_fontH;
    return pos;
}

void MyAutoScrollingWindow::MyRefresh()
{
    static wxPoint lastSelStart(-1, -1), lastCursor(-1, -1);
    // refresh last selected area (to deselect previously selected text)
    wxRect lastUpdRect(
            GraphicalCharToDeviceCoords(lastSelStart),
            GraphicalCharToDeviceCoords(lastCursor)
        );
    // off-by-one corrections, necessary because it's not possible to know
    //   when to round up until rect is normalized by lastUpdRect constructor
    lastUpdRect.width += m_fontW; // kludge
    lastUpdRect.height += m_fontH; // kludge
    // refresh currently selected (to select previously unselected text)
    wxRect updRect(
            GraphicalCharToDeviceCoords(m_selStart),
            GraphicalCharToDeviceCoords(m_cursor)
        );
    // off-by-one corrections
    updRect.width += m_fontW; // kludge
    updRect.height += m_fontH; // kludge
    // find necessary refresh areas
    int rx = lastUpdRect.x;
    int ry = lastUpdRect.y;
    int rw = updRect.x - lastUpdRect.x;
    int rh = lastUpdRect.height;
    if (rw && rh) {
        RefreshRect(DCNormalize(rx, ry, rw, rh));
    }
    rx = updRect.x;
    ry = updRect.y + updRect.height;
    rw= updRect.width;
    rh = (lastUpdRect.y + lastUpdRect.height) - (updRect.y + updRect.height);
    if (rw && rh) {
        RefreshRect(DCNormalize(rx, ry, rw, rh));
    }
    rx = updRect.x + updRect.width;
    ry = lastUpdRect.y;
    rw = (lastUpdRect.x + lastUpdRect.width) - (updRect.x + updRect.width);
    rh = lastUpdRect.height;
    if (rw && rh) {
        RefreshRect(DCNormalize(rx, ry, rw, rh));
    }
    rx = updRect.x;
    ry = lastUpdRect.y;
    rw = updRect.width;
    rh = updRect.y - lastUpdRect.y;
    if (rw && rh) {
        RefreshRect(DCNormalize(rx, ry, rw, rh));
    }
    // update last
    lastSelStart = m_selStart;
    lastCursor = m_cursor;
}

bool MyAutoScrollingWindow::IsSelected(int chX, int chY) const
{
    if (IsInside(chX, m_selStart.x, m_cursor.x)
            && IsInside(chY, m_selStart.y, m_cursor.y)) {
        return true;
    }
    return false;
}

bool MyAutoScrollingWindow::IsInside(int k, int bound1, int bound2)
{
    if ((k >= bound1 && k <= bound2) || (k >= bound2 && k <= bound1)) {
        return true;
    }
    return false;
}

wxRect
MyAutoScrollingWindow::DCNormalize(int x, int y, int w, int h)
{
    // this is needed to get rid of the graphical remnants from the selection
    // I think it's because DrawRectangle() excludes a pixel in either direction
    const int kludge = 1;
    // make (x, y) the top-left corner
    if (w < 0) {
        w = -w + kludge;
        x -= w;
    } else {
        x -= kludge;
        w += kludge;
    }
    if (h < 0) {
        h = -h + kludge;
        y -= h;
    } else {
        y -= kludge;
        h += kludge;
    }
    return wxRect(x, y, w, h);
}

void MyAutoScrollingWindow::OnDraw(wxDC& dc)
{
    dc.SetFont(m_font);
    wxBrush normBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
    wxBrush selBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
    dc.SetPen(*wxTRANSPARENT_PEN);
    const wxString str = sm_testData;
    size_t strLength = str.length();
    wxString::const_iterator str_i = str.begin();

    // draw the characters
    // 1. for each update region
    for (wxRegionIterator upd(GetUpdateRegion()); upd; ++upd) {
        wxRect updRect = upd.GetRect();
        wxRect updRectInGChars(DeviceCoordsToGraphicalChars(updRect));
        // 2. for each row of chars in the update region
        for (int chY = updRectInGChars.y
                ; chY <= updRectInGChars.y + updRectInGChars.height; ++chY) {
            // 3. for each character in the row
            bool isFirstX = true;
            for (int chX = updRectInGChars.x
                    ; chX <= updRectInGChars.x + updRectInGChars.width
                    ; ++chX) {
                // 4. set up dc
                if (IsSelected(chX, chY)) {
                    dc.SetBrush(selBrush);
                    dc.SetTextForeground( wxSystemSettings::GetColour
                            (wxSYS_COLOUR_HIGHLIGHTTEXT));
                } else {
                    dc.SetBrush(normBrush);
                    dc.SetTextForeground( wxSystemSettings::GetColour
                            (wxSYS_COLOUR_WINDOWTEXT));
                }
                // 5. find position info
                wxPoint charPos = GraphicalCharToLogicalCoords(wxPoint
                        (chX, chY));
                // 6. draw!
                dc.DrawRectangle(charPos.x, charPos.y, m_fontW, m_fontH);
                size_t charIndex = chY * sm_lineLen + chX;
                if (chY < sm_lineCnt &&
                    chX < sm_lineLen &&
                    charIndex < strLength)
                {
                    if (isFirstX)
                    {
                        str_i = str.begin() + charIndex;
                        isFirstX = false;
                    }
                    dc.DrawText(*str_i, charPos.x, charPos.y);
                    ++str_i;
                }
            }
        }
    }
}

void MyAutoScrollingWindow::OnMouseLeftDown(wxMouseEvent& event)
{
    // initial press of mouse button sets the beginning of the selection
    m_selStart = DeviceCoordsToGraphicalChars(event.GetPosition());
    // set the cursor to the same position
    m_cursor = m_selStart;
    // draw/erase selection
    MyRefresh();
}

void MyAutoScrollingWindow::OnMouseLeftUp(wxMouseEvent& WXUNUSED(event))
{
    // this test is necessary
    if (HasCapture()) {
        // uncapture mouse
        ReleaseMouse();
    }
}

void MyAutoScrollingWindow::OnMouseMove(wxMouseEvent& event)
{
    // if user is dragging
    if (event.Dragging() && event.LeftIsDown()) {
        // set the new cursor position
        m_cursor = DeviceCoordsToGraphicalChars(event.GetPosition());
        // draw/erase selection
        MyRefresh();
        // capture mouse to activate auto-scrolling
        if (!HasCapture()) {
            CaptureMouse();
        }
    }
}

void
MyAutoScrollingWindow::OnMouseCaptureLost(wxMouseCaptureLostEvent&
                                                WXUNUSED(event))
{
    // we only capture mouse for timed scrolling, so nothing is needed here
    // other than making sure to not call event.Skip()
}

void MyAutoScrollingWindow::OnScroll(wxScrollWinEvent& event)
{
    // need to move the cursor when autoscrolling
    // FIXME: the cursor also moves when the scrollbar arrows are clicked
    if (HasCapture()) {
        if (event.GetOrientation() == wxHORIZONTAL) {
            if (event.GetEventType() == wxEVT_SCROLLWIN_LINEUP) {
                --m_cursor.x;
            } else if (event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN) {
                ++m_cursor.x;
            }
        } else if (event.GetOrientation() == wxVERTICAL) {
            if (event.GetEventType() == wxEVT_SCROLLWIN_LINEUP) {
                --m_cursor.y;
            } else if (event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN) {
                ++m_cursor.y;
            }
        }
    }
    MyRefresh();
    event.Skip();
}

const int MyAutoScrollingWindow::sm_lineCnt = 125;
const int MyAutoScrollingWindow::sm_lineLen = 79;
const char *MyAutoScrollingWindow::sm_testData =
"162 Cult of the genius out of vanity. Because we think well of ourselves, but "
"nonetheless never suppose ourselves capable of producing a painting like one of "
"Raphael's or a dramatic scene like one of Shakespeare's, we convince ourselves "
"that the capacity to do so is quite extraordinarily marvelous, a wholly "
"uncommon accident, or, if we are still religiously inclined, a mercy from on "
"high. Thus our vanity, our self-love, promotes the cult of the genius: for only "
"if we think of him as being very remote from us, as a miraculum, does he not "
"aggrieve us (even Goethe, who was without envy, called Shakespeare his star of "
"the most distant heights [\"William! Stern der schonsten Ferne\": from Goethe's, "
"\"Between Two Worlds\"]; in regard to which one might recall the lines: \"the "
"stars, these we do not desire\" [from Goethe's, \"Comfort in Tears\"]). But, aside "
"from these suggestions of our vanity, the activity of the genius seems in no "
"way fundamentally different from the activity of the inventor of machines, the "
"scholar of astronomy or history, the master of tactics. All these activities "
"are explicable if one pictures to oneself people whose thinking is active in "
"one direction, who employ everything as material, who always zealously observe "
"their own inner life and that of others, who perceive everywhere models and "
"incentives, who never tire of combining together the means available to them. "
"Genius too does nothing except learn first how to lay bricks then how to build, "
"except continually seek for material and continually form itself around it. "
"Every activity of man is amazingly complicated, not only that of the genius: "
"but none is a \"miracle.\" Whence, then, the belief that genius exists only in "
"the artist, orator and philosopher? that only they have \"intuition\"? (Whereby "
"they are supposed to possess a kind of miraculous eyeglass with which they can "
"see directly into \"the essence of the thing\"!) It is clear that people speak of "
"genius only where the effects of the great intellect are most pleasant to them "
"and where they have no desire to feel envious. To call someone \"divine\" means: "
"\"here there is no need for us to compete.\" Then, everything finished and "
"complete is regarded with admiration, everything still becoming is undervalued. "
"But no one can see in the work of the artist how it has become; that is its "
"advantage, for wherever one can see the act of becoming one grows somewhat "
"cool. The finished and perfect art of representation repulses all thinking as "
"to how it has become; it tyrannizes as present completeness and perfection. "
"That is why the masters of the art of representation count above all as gifted "
"with genius and why men of science do not. In reality, this evaluation of the "
"former and undervaluation of the latter is only a piece of childishness in the "
"realm of reason. "
"\n\n"
"163 The serious workman. Do not talk about giftedness, inborn talents! One can "
"name great men of all kinds who were very little gifted. The acquired "
"greatness, became \"geniuses\" (as we put it), through qualities the lack of "
"which no one who knew what they were would boast of: they all possessed that "
"seriousness of the efficient workman which first learns to construct the parts "
"properly before it ventures to fashion a great whole; they allowed themselves "
"time for it, because they took more pleasure in making the little, secondary "
"things well than in the effect of a dazzling whole. the recipe for becoming a "
"good novelist, for example, is easy to give, but to carry it out presupposes "
"qualities one is accustomed to overlook when one says \"I do not have enough "
"talent.\" One has only to make a hundred or so sketches for novels, none longer "
"than two pages but of such distinctness that every word in them is necessary; "
"one should write down anecdotes each day until one has learned how to give them "
"the most pregnant and effective form; one should be tireless in collecting and "
"describing human types and characters; one should above all relate things to "
"others and listen to others relate, keeping one's eyes and ears open for the "
"effect produced on those present, one should travel like a landscape painter or "
"costume designer; one should excerpt for oneself out of the individual sciences "
"everything that will produce an artistic effect when it is well described, one "
"should, finally, reflect on the motives of human actions, disdain no signpost "
"to instruction about them and be a collector of these things by day and night. "
"One should continue in this many-sided exercise some ten years: what is then "
"created in the workshop, however, will be fit to go out into the world. What, "
"however, do most people do? They begin, not with the parts, but with the whole. "
"Perhaps they chance to strike a right note, excite attention and from then on "
"strike worse and worse notes, for good, natural reasons. Sometimes, when the "
"character and intellect needed to formulate such a life-plan are lacking, fate "
"and need take their place and lead the future master step by step through all "
"the stipulations of his trade. "
"\n\n"
"164 Peril and profit in the cult of the genius. The belief in great, superior, "
"fruitful spirits is not necessarily, yet nonetheless is very frequently "
"associated with that religious or semi-religious superstition that these "
"spirits are of supra-human origin and possess certain miraculous abilities by "
"virtue of which they acquire their knowledge by quite other means than the rest "
"of mankind. One ascribes to them, it seems, a direct view of the nature of the "
"world, as it were a hole in the cloak of appearance, and believes that, by "
"virtue of this miraculous seer's vision, they are able to communicate something "
"conclusive and decisive about man and the world without the toil and "
"rigorousness required by science. As long as there continue to be those who "
"believe in the miraculous in the domain of knowledge one can perhaps concede "
"that these people themselves derive some benefit from their belief, inasmuch as "
"through their unconditional subjection to the great spirits they create for "
"their own spirit during its time of development the finest form of discipline "
"and schooling. On the other hand, it is at least questionable whether the "
"superstitious belief in genius, in its privileges and special abilities, is of "
"benefit to the genius himself if it takes root in him. It is in any event a "
"dangerous sign when a man is assailed by awe of himself, whether it be the "
"celebrated Caesar's awe of Caesar or the awe of one's own genius now under "
"consideration; when the sacrificial incense which is properly rendered only to "
"a god penetrates the brain of the genius, so that his head begins to swim and "
"he comes to regard himself as something supra-human. The consequences that "
"slowly result are: the feeling of irresponsibility, of exceptional rights, the "
"belief that he confers a favor by his mere presence, insane rage when anyone "
"attempts even to compare him with others, let alone to rate him beneath them, "
"or to draw attention to lapses in his work. Because he ceases to practice "
"criticism of himself, at last one pinion after the other falls out of his "
"plumage: that superstitious eats at the roots of his powers and perhaps even "
"turns him into a hypocrite after his powers have fled from him. For the great "
"spirits themselves it is therefore probably more beneficial if they acquire an "
"insight into the nature and origin of their powers, if they grasp, that is to "
"say, what purely human qualities have come together in them and what fortunate "
"circumstances attended them: in the first place undiminished energy, resolute "
"application to individual goals, great personal courage, then the good fortune "
"to receive an upbringing which offered in the early years the finest teachers, "
"models and methods. To be sure, when their goal is the production of the "
"greatest possible effect, unclarity with regard to oneself and that "
"semi-insanity superadded to it has always achieved much; for what has been "
"admired and envied at all times has been that power in them by virtue of which "
"they render men will-less and sweep them away into the delusion that the "
"leaders they are following are supra-natural. Indeed, it elevates and inspires "
"men to believe that someone is in possession of supra-natural powers: to this "
"extent Plato was right to say [Plato: Phaedrus, 244a] that madness has brought "
"the greatest of blessings upon mankind. In rare individual cases this portion "
"of madness may, indeed, actually have been the means by which such a nature, "
"excessive in all directions, was held firmly together: in the life of "
"individuals, too, illusions that are in themselves poisons often play the role "
"of healers; yet, in the end, in the case of every \"genius\" who believes in his "
"own divinity the poison shows itself to the same degree as his \"genius\" grows "
"old: one may recall, for example, the case of Napoleon, whose nature certainly "
"grew into the mighty unity that sets him apart from all men of modern times "
"precisely through his belief in himself and his star and through the contempt "
"for men that flowed from it; until in the end, however, this same belief went "
"over into an almost insane fatalism, robbed him of his acuteness and swiftness "
"of perception, and became the cause of his destruction.";