]> git.saurik.com Git - wxWidgets.git/blame - samples/scroll/scroll.cpp
fix vertical mouse wheel event rotation value, sign was reversed in r74805
[wxWidgets.git] / samples / scroll / scroll.cpp
CommitLineData
4e04f777
WS
1/////////////////////////////////////////////////////////////////////////////
2// Name: scroll.cpp
cb7c02fd 3// Purpose: wxScrolled sample
4e04f777 4// Author: Robert Roebling
4e04f777 5// Copyright: (C) 1998 Robert Roebling, 2002 Ron Lee, 2003 Matt Gregory
6a5a7fba 6// (C) 2008 Vadim Zeitlin
526954c5 7// Licence: wxWindows licence
4e04f777 8/////////////////////////////////////////////////////////////////////////////
fdd3ed7a 9
fdd3ed7a
RR
10#include "wx/wxprec.h"
11
12#ifdef __BORLANDC__
6a5a7fba 13 #pragma hdrstop
fdd3ed7a
RR
14#endif
15
16#ifndef WX_PRECOMP
6a5a7fba 17 #include "wx/wx.h"
fdd3ed7a
RR
18#endif
19
ed673c6a
RR
20#include "wx/sizer.h"
21#include "wx/log.h"
9675a49d 22#include "wx/tglbtn.h"
ed673c6a 23
e7092398 24#ifndef wxHAS_IMAGES_IN_RESOURCES
41f02b9a
FM
25 #include "../sample.xpm"
26#endif
27
6a5a7fba 28// ----------------------------------------------------------------------------
db4ad642 29// a trivial example
6a5a7fba 30// ----------------------------------------------------------------------------
db4ad642 31
d9f4cc10 32// MySimpleCanvas: a scrolled window which draws a simple rectangle
cb7c02fd 33class MySimpleCanvas : public wxScrolled<wxWindow>
db4ad642
RR
34{
35public:
d9f4cc10
VZ
36 enum
37 {
6a5a7fba
VZ
38 // these numbers are not multiple of 10 (our scroll step) to test for
39 // the absence of rounding errors (e.g. we should have one more page
40 // than WIDTH/10 to show the right side of the rectangle)
41 WIDTH = 292,
42 HEIGHT = 297
d9f4cc10
VZ
43 };
44
6a5a7fba 45 MySimpleCanvas(wxWindow *parent)
cb7c02fd 46 : wxScrolled<wxWindow>(parent, wxID_ANY)
6a5a7fba
VZ
47 {
48 SetScrollRate( 10, 10 );
49 SetVirtualSize( WIDTH, HEIGHT );
50 SetBackgroundColour( *wxWHITE );
db4ad642 51
6a5a7fba
VZ
52 Connect(wxEVT_PAINT, wxPaintEventHandler(MySimpleCanvas::OnPaint));
53 }
db4ad642 54
6a5a7fba
VZ
55private:
56 void OnPaint(wxPaintEvent& WXUNUSED(event))
57 {
58 wxPaintDC dc(this);
db4ad642 59
6a5a7fba
VZ
60 // this call is vital: it adjusts the dc to account for the current
61 // scroll offset
62 PrepareDC(dc);
db4ad642 63
6a5a7fba
VZ
64 dc.SetPen( *wxRED_PEN );
65 dc.SetBrush( *wxTRANSPARENT_BRUSH );
66 dc.DrawRectangle( 0, 0, WIDTH, HEIGHT );
67 }
68};
c5a3900a 69
db4ad642 70
d9f4cc10 71// MySimpleFrame: a frame which contains a MySimpleCanvas
6a5a7fba 72class MySimpleFrame : public wxFrame
db4ad642
RR
73{
74public:
6a5a7fba
VZ
75 MySimpleFrame(wxWindow *parent)
76 : wxFrame(parent, wxID_ANY, "MySimpleCanvas")
77 {
78 new MySimpleCanvas(this);
db4ad642 79
6a5a7fba
VZ
80 // ensure that we have scrollbars initially
81 SetClientSize(MySimpleCanvas::WIDTH/2, MySimpleCanvas::HEIGHT/2);
d9f4cc10 82
6a5a7fba
VZ
83 Show();
84 }
db4ad642
RR
85};
86
6a5a7fba
VZ
87// ----------------------------------------------------------------------
88// a more complex example
89// ----------------------------------------------------------------------
90
91// MyCanvas
cb7c02fd 92class MyCanvas : public wxScrolled<wxPanel>
6a5a7fba
VZ
93{
94public:
95 MyCanvas(wxWindow *parent);
db4ad642 96
6a5a7fba
VZ
97private:
98 void OnPaint(wxPaintEvent& event);
99 void OnQueryPosition(wxCommandEvent& event);
100 void OnAddButton(wxCommandEvent& event);
101 void OnDeleteButton(wxCommandEvent& event);
102 void OnMoveButton(wxCommandEvent& event);
103 void OnScrollWin(wxCommandEvent& event);
104 void OnMouseRightDown(wxMouseEvent& event);
105 void OnMouseWheel(wxMouseEvent& event);
db4ad642 106
6a5a7fba 107 wxButton *m_button;
db4ad642 108
6a5a7fba
VZ
109 DECLARE_EVENT_TABLE()
110};
111
112class MyCanvasFrame : public wxFrame
db4ad642 113{
6a5a7fba
VZ
114public:
115 MyCanvasFrame(wxWindow *parent)
116 : wxFrame(parent, wxID_ANY, "MyCanvas")
117 {
118 m_canvas = new MyCanvas(this);
db4ad642 119
6a5a7fba
VZ
120 wxMenu *menuFile = new wxMenu();
121 menuFile->Append(wxID_DELETE, "&Delete all");
122 menuFile->Append(wxID_NEW, "Insert &new");
db4ad642 123
6a5a7fba
VZ
124 wxMenuBar *mbar = new wxMenuBar();
125 mbar->Append(menuFile, "&File");
126 SetMenuBar( mbar );
db4ad642 127
ce7fe42e 128 Connect(wxID_DELETE, wxEVT_MENU,
6a5a7fba 129 wxCommandEventHandler(MyCanvasFrame::OnDeleteAll));
ce7fe42e 130 Connect(wxID_NEW, wxEVT_MENU,
6a5a7fba 131 wxCommandEventHandler(MyCanvasFrame::OnInsertNew));
db4ad642 132
6a5a7fba
VZ
133 Show();
134 }
fdd3ed7a 135
6a5a7fba
VZ
136private:
137 void OnDeleteAll(wxCommandEvent& WXUNUSED(event))
138 {
139 m_canvas->DestroyChildren();
140 }
fdd3ed7a 141
6a5a7fba
VZ
142 void OnInsertNew(wxCommandEvent& WXUNUSED(event))
143 {
144 (void)new wxButton(m_canvas, wxID_ANY, "Hello", wxPoint(100,100));
145 }
fdd3ed7a 146
6a5a7fba
VZ
147 MyCanvas *m_canvas;
148};
149
150// ----------------------------------------------------------------------------
cb7c02fd 151// example using sizers with wxScrolled
6a5a7fba 152// ----------------------------------------------------------------------------
fdd3ed7a 153
6a5a7fba
VZ
154const wxSize SMALL_BUTTON( 100, 50 );
155const wxSize LARGE_BUTTON( 300, 200 );
156
cb7c02fd 157class MySizerScrolledWindow : public wxScrolled<wxWindow>
fdd3ed7a
RR
158{
159public:
6a5a7fba 160 MySizerScrolledWindow(wxWindow *parent);
ed673c6a 161
6a5a7fba
VZ
162private:
163 // this button can be clicked to change its own size in the handler below,
164 // the window size will be automatically adjusted to fit the button
ed673c6a 165 wxButton *m_button;
fdd3ed7a 166
6a5a7fba 167 void OnResizeClick(wxCommandEvent& event);
fdd3ed7a
RR
168};
169
6a5a7fba
VZ
170class MySizerFrame : public wxFrame
171{
172public:
173 MySizerFrame(wxWindow *parent)
174 : wxFrame(parent, wxID_ANY, "MySizerScrolledWindow")
175 {
176 new MySizerScrolledWindow(this);
177
178 // ensure that the scrollbars appear when the button becomes large
179 SetClientSize(LARGE_BUTTON/2);
180 Show();
181 }
182};
2b5f62a0
VZ
183
184// ----------------------------------------------------------------------------
6a5a7fba 185// example showing scrolling only part of the window
2b5f62a0
VZ
186// ----------------------------------------------------------------------------
187
6a5a7fba
VZ
188// this window consists of an empty space in its corner, column labels window
189// along its top, row labels window along its left hand side and a canvas in
190// the remaining space
2b5f62a0 191
6a5a7fba 192class MySubColLabels : public wxWindow
2b5f62a0 193{
6a5a7fba 194public:
cb7c02fd 195 MySubColLabels(wxScrolled<wxWindow> *parent)
6a5a7fba
VZ
196 : wxWindow(parent, wxID_ANY)
197 {
198 m_owner = parent;
199
200 Connect(wxEVT_PAINT, wxPaintEventHandler(MySubColLabels::OnPaint));
201 }
202
2b5f62a0 203private:
6a5a7fba
VZ
204 void OnPaint(wxPaintEvent& WXUNUSED(event))
205 {
206 wxPaintDC dc(this);
207
208 // This is wrong.. it will translate both x and y if the
209 // window is scrolled, the label windows are active in one
210 // direction only. Do the action below instead -- RL.
211 //m_owner->PrepareDC( dc );
212
213 int xScrollUnits, xOrigin;
2b5f62a0 214
6a5a7fba
VZ
215 m_owner->GetViewStart( &xOrigin, 0 );
216 m_owner->GetScrollPixelsPerUnit( &xScrollUnits, 0 );
217 dc.SetDeviceOrigin( -xOrigin * xScrollUnits, 0 );
2b5f62a0 218
6a5a7fba
VZ
219 dc.DrawText("Column 1", 5, 5);
220 dc.DrawText("Column 2", 105, 5);
221 dc.DrawText("Column 3", 205, 5);
222 }
223
cb7c02fd 224 wxScrolled<wxWindow> *m_owner;
6a5a7fba
VZ
225};
226
227class MySubRowLabels : public wxWindow
228{
2b5f62a0 229public:
cb7c02fd 230 MySubRowLabels(wxScrolled<wxWindow> *parent)
6a5a7fba
VZ
231 : wxWindow(parent, wxID_ANY)
232 {
233 m_owner = parent;
2b5f62a0 234
6a5a7fba
VZ
235 Connect(wxEVT_PAINT, wxPaintEventHandler(MySubRowLabels::OnPaint));
236 }
2b5f62a0 237
6a5a7fba
VZ
238private:
239 void OnPaint(wxPaintEvent& WXUNUSED(event))
240 {
241 wxPaintDC dc(this);
2b5f62a0 242
6a5a7fba
VZ
243 // This is wrong.. it will translate both x and y if the
244 // window is scrolled, the label windows are active in one
245 // direction only. Do the action below instead -- RL.
246 //m_owner->PrepareDC( dc );
247
248 int yScrollUnits, yOrigin;
249
250 m_owner->GetViewStart( 0, &yOrigin );
251 m_owner->GetScrollPixelsPerUnit( 0, &yScrollUnits );
252 dc.SetDeviceOrigin( 0, -yOrigin * yScrollUnits );
253
254 dc.DrawText("Row 1", 5, 5);
255 dc.DrawText("Row 2", 5, 30);
256 dc.DrawText("Row 3", 5, 55);
257 dc.DrawText("Row 4", 5, 80);
258 dc.DrawText("Row 5", 5, 105);
259 dc.DrawText("Row 6", 5, 130);
260 }
261
cb7c02fd 262 wxScrolled<wxWindow> *m_owner;
6a5a7fba
VZ
263};
264
265class MySubCanvas : public wxPanel
266{
267public:
cb7c02fd 268 MySubCanvas(wxScrolled<wxWindow> *parent, wxWindow *cols, wxWindow *rows)
6a5a7fba
VZ
269 : wxPanel(parent, wxID_ANY)
270 {
271 m_owner = parent;
272 m_colLabels = cols;
273 m_rowLabels = rows;
274
275 (void)new wxButton(this, wxID_ANY, "Hallo I",
276 wxPoint(0,50), wxSize(100,25) );
277 (void)new wxButton(this, wxID_ANY, "Hallo II",
278 wxPoint(200,50), wxSize(100,25) );
279
280 (void)new wxTextCtrl(this, wxID_ANY, "Text I",
281 wxPoint(0,100), wxSize(100,25) );
282 (void)new wxTextCtrl(this, wxID_ANY, "Text II",
283 wxPoint(200,100), wxSize(100,25) );
284
285 (void)new wxComboBox(this, wxID_ANY, "ComboBox I",
286 wxPoint(0,150), wxSize(100,25));
287 (void)new wxComboBox(this, wxID_ANY, "ComboBox II",
288 wxPoint(200,150), wxSize(100,25));
289
290 SetBackgroundColour("WHEAT");
291
292 Connect(wxEVT_PAINT, wxPaintEventHandler(MySubCanvas::OnPaint));
293 }
294
295 // override the base class function so that when this window is scrolled,
296 // the labels are scrolled in sync
297 virtual void ScrollWindow(int dx, int dy, const wxRect *rect)
298 {
299 wxPanel::ScrollWindow( dx, dy, rect );
300 m_colLabels->ScrollWindow( dx, 0, rect );
301 m_rowLabels->ScrollWindow( 0, dy, rect );
302 }
303
304private:
305 void OnPaint(wxPaintEvent& WXUNUSED(event))
306 {
307 wxPaintDC dc( this );
308 m_owner->PrepareDC( dc );
309
310 dc.SetPen( *wxBLACK_PEN );
311
312 // OK, let's assume we are a grid control and we have two
313 // grid cells. Here in OnPaint we want to know which cell
314 // to redraw so that we prevent redrawing cells that don't
315 // need to get redrawn. We have one cell at (0,0) and one
316 // more at (200,0), both having a size of (100,25).
317
318 // We can query how much the window has been scrolled
319 // by calling CalcUnscrolledPosition()
320
321 int scroll_x = 0;
322 int scroll_y = 0;
323 m_owner->CalcUnscrolledPosition( scroll_x, scroll_y, &scroll_x, &scroll_y );
324
325 // We also need to know the size of the window to see which
326 // cells are completely hidden and not get redrawn
327
328 int size_x = 0;
329 int size_y = 0;
330 GetClientSize( &size_x, &size_y );
331
332 // First cell: (0,0)(100,25)
333 // It it on screen?
334 if ((0+100-scroll_x > 0) && (0+25-scroll_y > 0) &&
335 (0-scroll_x < size_x) && (0-scroll_y < size_y))
336 {
337 // Has the region on screen been exposed?
338 if (IsExposed(0,0,100,25))
339 {
340 dc.DrawRectangle( 0, 0, 100, 25 );
341 dc.DrawText("First Cell", 5, 5);
342 }
343 }
344
345
346 // Second cell: (0,200)(100,25)
347 // It it on screen?
348 if ((200+100-scroll_x > 0) && (0+25-scroll_y > 0) &&
349 (200-scroll_x < size_x) && (0-scroll_y < size_y))
350 {
351 // Has the region on screen been exposed?
352 if (IsExposed(200,0,100,25))
353 {
354 dc.DrawRectangle( 200, 0, 100, 25 );
355 dc.DrawText("Second Cell", 205, 5);
356 }
357 }
358 }
359
cb7c02fd 360 wxScrolled<wxWindow> *m_owner;
6a5a7fba
VZ
361 wxWindow *m_colLabels,
362 *m_rowLabels;
363};
364
cb7c02fd 365class MySubScrolledWindow : public wxScrolled<wxWindow>
6a5a7fba
VZ
366{
367public:
368 enum
369 {
370 CORNER_WIDTH = 60,
371 CORNER_HEIGHT = 25
372 };
373
374 MySubScrolledWindow(wxWindow *parent)
cb7c02fd 375 : wxScrolled<wxWindow>(parent, wxID_ANY)
6a5a7fba
VZ
376 {
377 // create the children
378 MySubColLabels *cols = new MySubColLabels(this);
379 MySubRowLabels *rows = new MySubRowLabels(this);
380
381 m_canvas = new MySubCanvas(this, cols, rows);
382
383 // lay them out
384 wxFlexGridSizer *sizer = new wxFlexGridSizer(2, 2, 10, 10);
385 sizer->Add(CORNER_WIDTH, CORNER_HEIGHT); // just a spacer
386 sizer->Add(cols, wxSizerFlags().Expand());
387 sizer->Add(rows, wxSizerFlags().Expand());
388 sizer->Add(m_canvas, wxSizerFlags().Expand());
389 sizer->AddGrowableRow(1);
390 sizer->AddGrowableCol(1);
391 SetSizer(sizer);
392
393 // this is the key call: it means that only m_canvas will be scrolled
394 // and not this window itself
395 SetTargetWindow(m_canvas);
396
397 SetScrollbars(10, 10, 50, 50);
398
399 Connect(wxEVT_SIZE, wxSizeEventHandler(MySubScrolledWindow::OnSize));
400 }
401
402protected:
403 // scrolled windows which use scroll target different from the window
404 // itself must override this virtual method
405 virtual wxSize GetSizeAvailableForScrollTarget(const wxSize& size)
406 {
407 // decrease the total size by the size of the non-scrollable parts
408 // above/to the left of the canvas
409 wxSize sizeCanvas(size);
410 sizeCanvas.x -= 60;
411 sizeCanvas.y -= 25;
412 return sizeCanvas;
413 }
414
415private:
416 void OnSize(wxSizeEvent& WXUNUSED(event))
417 {
418 // We need to override OnSize so that our scrolled
419 // window a) does call Layout() to use sizers for
420 // positioning the controls but b) does not query
421 // the sizer for their size and use that for setting
422 // the scrollable area as set that ourselves by
423 // calling SetScrollbar() further down.
424
425 Layout();
426
427 AdjustScrollbars();
428 }
429
430 MySubCanvas *m_canvas;
2b5f62a0
VZ
431};
432
6a5a7fba
VZ
433class MySubFrame : public wxFrame
434{
435public:
436 MySubFrame(wxWindow *parent)
437 : wxFrame(parent, wxID_ANY, "MySubScrolledWindow")
438 {
439 new MySubScrolledWindow(this);
440
441 Show();
442 }
443};
2b5f62a0 444
8a73bf3d 445// ----------------------------------------------------------------------------
cb7c02fd 446// more simple examples of wxScrolled usage
8a73bf3d
VZ
447// ----------------------------------------------------------------------------
448
449// base class for both of them
cb7c02fd 450class MyScrolledWindowBase : public wxScrolled<wxWindow>
8a73bf3d
VZ
451{
452public:
2b5f62a0 453 MyScrolledWindowBase(wxWindow *parent)
cb7c02fd
VZ
454 : wxScrolled<wxWindow>(parent, wxID_ANY,
455 wxDefaultPosition, wxDefaultSize,
456 wxBORDER_SUNKEN)
8a73bf3d 457 {
1ddb6d28 458 m_nLines = 50;
9675a49d
VZ
459 m_winSync = NULL;
460 m_inDoSync = false;
461
2b5f62a0 462 wxClientDC dc(this);
6a5a7fba 463 dc.GetTextExtent("Line 17", NULL, &m_hLine);
8a73bf3d
VZ
464 }
465
9675a49d
VZ
466 // this scrolled window can be synchronized with another one: if this
467 // function is called with a non-NULL pointer, the given window will be
468 // scrolled to the same position as this one
469 void SyncWith(MyScrolledWindowBase *win)
470 {
471 m_winSync = win;
472
473 DoSyncIfNecessary();
474 }
475
476 virtual void ScrollWindow(int dx, int dy, const wxRect *rect = NULL)
477 {
cb7c02fd 478 wxScrolled<wxWindow>::ScrollWindow(dx, dy, rect);
9675a49d
VZ
479
480 DoSyncIfNecessary();
481 }
482
8a73bf3d 483protected:
8a73bf3d 484 // the height of one line on screen
6a5a7fba 485 int m_hLine;
8a73bf3d
VZ
486
487 // the number of lines we draw
488 size_t m_nLines;
9675a49d
VZ
489
490private:
491 bool WasScrolledFirst() const { return m_inDoSync; }
492
493 void DoSyncIfNecessary()
494 {
495 if ( m_winSync && !m_winSync->WasScrolledFirst() )
496 {
497 m_inDoSync = true;
498
0b0f6f87 499 m_winSync->Scroll(GetViewStart());
9675a49d
VZ
500
501 m_inDoSync = false;
502 }
503 }
504
505 // the window to synchronize with this one or NULL
506 MyScrolledWindowBase *m_winSync;
507
508 // the flag preventing infinite recursion which would otherwise happen if
509 // one window synchronized the other one which in turn synchronized this
510 // one and so on
511 bool m_inDoSync;
8a73bf3d
VZ
512};
513
514// this class does "stupid" redrawing - it redraws everything each time
2b5f62a0
VZ
515// and sets the scrollbar extent directly.
516
8a73bf3d
VZ
517class MyScrolledWindowDumb : public MyScrolledWindowBase
518{
519public:
2b5f62a0
VZ
520 MyScrolledWindowDumb(wxWindow *parent) : MyScrolledWindowBase(parent)
521 {
522 // no horz scrolling
b62ca03d 523 SetScrollbars(0, m_hLine, 0, m_nLines + 1, 0, 0, true /* no refresh */);
2b5f62a0 524 }
8a73bf3d
VZ
525
526 virtual void OnDraw(wxDC& dc);
527};
528
529// this class does "smart" redrawing - only redraws the lines which must be
2b5f62a0
VZ
530// redrawn and sets the scroll rate and virtual size to affect the
531// scrollbars.
532//
533// Note that this class should produce identical results to the one above.
534
8a73bf3d
VZ
535class MyScrolledWindowSmart : public MyScrolledWindowBase
536{
537public:
2b5f62a0
VZ
538 MyScrolledWindowSmart(wxWindow *parent) : MyScrolledWindowBase(parent)
539 {
540 // no horz scrolling
541 SetScrollRate( 0, m_hLine );
4e04f777 542 SetVirtualSize( wxDefaultCoord, ( m_nLines + 1 ) * m_hLine );
2b5f62a0 543 }
8a73bf3d
VZ
544
545 virtual void OnDraw(wxDC& dc);
546};
547
57ac1a56 548// ----------------------------------------------------------------------------
6a5a7fba
VZ
549// implements a text viewer with simple block selection to test auto-scrolling
550// functionality
57ac1a56
RN
551// ----------------------------------------------------------------------------
552
cb7c02fd 553class MyAutoScrollingWindow : public wxScrolled<wxWindow>
57ac1a56 554{
6a5a7fba
VZ
555public:
556 MyAutoScrollingWindow( wxWindow* parent );
57ac1a56
RN
557 wxRect DeviceCoordsToGraphicalChars(wxRect updRect) const;
558 wxPoint DeviceCoordsToGraphicalChars(wxPoint pos) const;
559 wxPoint GraphicalCharToDeviceCoords(wxPoint pos) const;
560 wxRect LogicalCoordsToGraphicalChars(wxRect updRect) const;
561 wxPoint LogicalCoordsToGraphicalChars(wxPoint pos) const;
562 wxPoint GraphicalCharToLogicalCoords(wxPoint pos) const;
563 void MyRefresh();
564 bool IsSelected(int chX, int chY) const;
565 static bool IsInside(int k, int bound1, int bound2);
6a5a7fba 566 static wxRect DCNormalize(int x, int y, int w, int h);
57ac1a56 567
6a5a7fba
VZ
568private:
569 // event handlers
57ac1a56
RN
570 void OnDraw(wxDC& dc);
571 void OnMouseLeftDown(wxMouseEvent& event);
572 void OnMouseLeftUp(wxMouseEvent& event);
573 void OnMouseMove(wxMouseEvent& event);
e0666bdc 574 void OnMouseCaptureLost(wxMouseCaptureLostEvent& event);
57ac1a56 575 void OnScroll(wxScrollWinEvent& event);
4686e0f6 576
6a5a7fba
VZ
577 // test data variables
578 static const char* sm_testData;
579 static const int sm_lineCnt; // line count
580 static const int sm_lineLen; // line length in characters
581 // sizes for graphical data
582 int m_fontH, m_fontW;
583 // selection tracking
584 wxPoint m_selStart; // beginning of blockwise selection
585 wxPoint m_cursor; // end of blockwise selection (mouse position)
586
587 // gui stuff
588 wxFont m_font;
589
590
4686e0f6 591 DECLARE_EVENT_TABLE()
57ac1a56 592};
2b5f62a0 593
6a5a7fba
VZ
594class MyAutoFrame : public wxFrame
595{
596public:
597 MyAutoFrame(wxWindow *parent)
598 : wxFrame(parent, wxID_ANY, "MyAutoScrollingWindow")
599 {
600 new MyAutoScrollingWindow(this);
601
602 Show();
603 }
604};
605
606
8a73bf3d 607// ----------------------------------------------------------------------------
6a5a7fba 608// MyFrame: the main application frame showing all the classes above
8a73bf3d 609// ----------------------------------------------------------------------------
fdd3ed7a
RR
610
611class MyFrame: public wxFrame
612{
613public:
614 MyFrame();
615
6a5a7fba
VZ
616private:
617 void OnAbout(wxCommandEvent& event);
618 void OnQuit(wxCommandEvent& event);
fdd3ed7a 619
6a5a7fba
VZ
620 void OnTestSimple(wxCommandEvent& WXUNUSED(event)) { new MySimpleFrame(this); }
621 void OnTestCanvas(wxCommandEvent& WXUNUSED(event)) { new MyCanvasFrame(this); }
622 void OnTestSizer(wxCommandEvent& WXUNUSED(event)) { new MySizerFrame(this); }
623 void OnTestSub(wxCommandEvent& WXUNUSED(event)) { new MySubFrame(this); }
624 void OnTestAuto(wxCommandEvent& WXUNUSED(event)) { new MyAutoFrame(this); }
fdd3ed7a 625
9675a49d 626 void OnToggleSync(wxCommandEvent& event);
1ddb6d28 627 void OnScrollbarVisibility(wxCommandEvent& event);
9675a49d
VZ
628
629 MyScrolledWindowBase *m_win1,
630 *m_win2;
631
fdd3ed7a
RR
632 DECLARE_EVENT_TABLE()
633};
634
57ac1a56 635// ----------------------------------------------------------------------------
fdd3ed7a 636// MyApp
57ac1a56 637// ----------------------------------------------------------------------------
fdd3ed7a 638
6a5a7fba 639class MyApp : public wxApp
fdd3ed7a
RR
640{
641public:
642 virtual bool OnInit();
643};
644
57ac1a56 645
6a5a7fba
VZ
646// ============================================================================
647// implementation
648// ============================================================================
ed673c6a 649
57ac1a56 650// ----------------------------------------------------------------------------
fdd3ed7a 651// MyCanvas
57ac1a56 652// ----------------------------------------------------------------------------
fdd3ed7a 653
2c83a950
VZ
654const wxWindowIDRef ID_ADDBUTTON = wxWindow::NewControlId();
655const wxWindowIDRef ID_DELBUTTON = wxWindow::NewControlId();
656const wxWindowIDRef ID_MOVEBUTTON = wxWindow::NewControlId();
657const wxWindowIDRef ID_SCROLLWIN = wxWindow::NewControlId();
658const wxWindowIDRef ID_QUERYPOS = wxWindow::NewControlId();
6a5a7fba 659
2c83a950 660const wxWindowIDRef ID_NEWBUTTON = wxWindow::NewControlId();
fdd3ed7a 661
cb7c02fd 662BEGIN_EVENT_TABLE(MyCanvas, wxScrolled<wxPanel>)
6a5a7fba
VZ
663 EVT_PAINT( MyCanvas::OnPaint)
664 EVT_RIGHT_DOWN( MyCanvas::OnMouseRightDown)
665 EVT_MOUSEWHEEL( MyCanvas::OnMouseWheel)
666 EVT_BUTTON( ID_QUERYPOS, MyCanvas::OnQueryPosition)
667 EVT_BUTTON( ID_ADDBUTTON, MyCanvas::OnAddButton)
668 EVT_BUTTON( ID_DELBUTTON, MyCanvas::OnDeleteButton)
669 EVT_BUTTON( ID_MOVEBUTTON, MyCanvas::OnMoveButton)
670 EVT_BUTTON( ID_SCROLLWIN, MyCanvas::OnScrollWin)
fdd3ed7a
RR
671END_EVENT_TABLE()
672
6a5a7fba 673MyCanvas::MyCanvas(wxWindow *parent)
cb7c02fd
VZ
674 : wxScrolled<wxPanel>(parent, wxID_ANY,
675 wxDefaultPosition, wxDefaultSize,
676 wxSUNKEN_BORDER | wxTAB_TRAVERSAL)
fdd3ed7a 677{
6a5a7fba
VZ
678 // you can use either a single SetScrollbars() call or these 2 functions,
679 // usually using them is better because you normally won't need to change
680 // the scroll rate in the future and the sizer can be used to update the
681 // virtual size automatically
2b5f62a0
VZ
682 SetScrollRate( 10, 10 );
683 SetVirtualSize( 500, 1000 );
684
6a5a7fba
VZ
685 (void) new wxButton( this, ID_ADDBUTTON, "add button", wxPoint(10,10) );
686 (void) new wxButton( this, ID_DELBUTTON, "del button", wxPoint(10,40) );
687 (void) new wxButton( this, ID_MOVEBUTTON, "move button", wxPoint(150,10) );
688 (void) new wxButton( this, ID_SCROLLWIN, "scroll win", wxPoint(250,10) );
aa06a8fd 689
6a5a7fba
VZ
690 wxPanel *test = new wxPanel( this, wxID_ANY,
691 wxPoint(10, 110), wxSize(130,50),
692 wxSIMPLE_BORDER | wxTAB_TRAVERSAL );
693 test->SetBackgroundColour( "WHEAT" );
5e014a0c 694
6a5a7fba 695 SetBackgroundColour( "BLUE" );
fdd3ed7a
RR
696}
697
4686e0f6 698void MyCanvas::OnMouseRightDown( wxMouseEvent &event )
bf0c00c6 699{
4686e0f6
VZ
700 wxPoint pt( event.GetPosition() );
701 int x,y;
702 CalcUnscrolledPosition( pt.x, pt.y, &x, &y );
6a5a7fba
VZ
703 wxLogMessage("Mouse down event at: %d %d, scrolled: %d %d",
704 pt.x, pt.y, x, y);
4686e0f6 705}
f6bcfd97 706
4686e0f6
VZ
707void MyCanvas::OnMouseWheel( wxMouseEvent &event )
708{
709 wxPoint pt( event.GetPosition() );
710 int x,y;
711 CalcUnscrolledPosition( pt.x, pt.y, &x, &y );
6a5a7fba
VZ
712 wxLogMessage( "Mouse wheel event at: %d %d, scrolled: %d %d\n"
713 "Rotation: %d, delta = %d",
4686e0f6
VZ
714 pt.x, pt.y, x, y,
715 event.GetWheelRotation(), event.GetWheelDelta() );
716
717 event.Skip();
bf0c00c6
RR
718}
719
720void MyCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) )
721{
722 wxPaintDC dc( this );
723 PrepareDC( dc );
724
6a5a7fba 725 dc.DrawText( "Press right mouse button to test calculations!", 160, 50 );
bf0c00c6 726
6a5a7fba 727 dc.DrawText( "Some text", 140, 140 );
aa06a8fd 728
bf0c00c6
RR
729 dc.DrawRectangle( 100, 160, 200, 200 );
730}
731
307f16e8
RR
732void MyCanvas::OnQueryPosition( wxCommandEvent &WXUNUSED(event) )
733{
734 wxPoint pt( m_button->GetPosition() );
6a5a7fba 735 wxLogMessage( "Position of \"Query position\" is %d %d", pt.x, pt.y );
bf0c00c6 736 pt = ClientToScreen( pt );
6a5a7fba
VZ
737 wxLogMessage("Position of \"Query position\" on screen is %d %d",
738 pt.x, pt.y);
307f16e8
RR
739}
740
ed673c6a
RR
741void MyCanvas::OnAddButton( wxCommandEvent &WXUNUSED(event) )
742{
6a5a7fba
VZ
743 wxLogMessage( "Inserting button at position 10,70..." );
744 wxButton *button = new wxButton( this, ID_NEWBUTTON, "new button",
745 wxPoint(10,70), wxSize(80,25) );
bf0c00c6 746 wxPoint pt( button->GetPosition() );
6a5a7fba 747 wxLogMessage( "-> Position after inserting %d %d", pt.x, pt.y );
ed673c6a
RR
748}
749
256b8649 750void MyCanvas::OnDeleteButton( wxCommandEvent &WXUNUSED(event) )
ed673c6a 751{
6a5a7fba 752 wxLogMessage( "Deleting button inserted with \"Add button\"..." );
ed673c6a
RR
753 wxWindow *win = FindWindow( ID_NEWBUTTON );
754 if (win)
755 win->Destroy();
756 else
6a5a7fba 757 wxLogMessage( "-> No window with id = ID_NEWBUTTON found." );
ed673c6a
RR
758}
759
760void MyCanvas::OnMoveButton( wxCommandEvent &event )
761{
6a5a7fba 762 wxLogMessage( "Moving button 10 pixels downward.." );
ed673c6a 763 wxWindow *win = FindWindow( event.GetId() );
bf0c00c6 764 wxPoint pt( win->GetPosition() );
6a5a7fba 765 wxLogMessage( "-> Position before move is %d %d", pt.x, pt.y );
422d0ff0 766 win->Move( wxDefaultCoord, pt.y + 10 );
bf0c00c6 767 pt = win->GetPosition();
6a5a7fba 768 wxLogMessage( "-> Position after move is %d %d", pt.x, pt.y );
ed673c6a
RR
769}
770
771void MyCanvas::OnScrollWin( wxCommandEvent &WXUNUSED(event) )
772{
6a5a7fba
VZ
773 wxLogMessage("Scrolling 2 units up.\n"
774 "The white square and the controls should move equally!");
0b0f6f87 775 Scroll( wxDefaultCoord, GetViewStart().y+2 );
ed673c6a
RR
776}
777
57ac1a56 778// ----------------------------------------------------------------------------
6a5a7fba 779// MySizerScrolledWindow
57ac1a56 780// ----------------------------------------------------------------------------
2b5f62a0 781
6a5a7fba 782MySizerScrolledWindow::MySizerScrolledWindow(wxWindow *parent)
cb7c02fd 783 : wxScrolled<wxWindow>(parent)
2b5f62a0 784{
6a5a7fba 785 SetBackgroundColour( "GREEN" );
2b5f62a0
VZ
786
787 // Set the rate we'd like for scrolling.
788
789 SetScrollRate( 5, 5 );
790
6a5a7fba
VZ
791 // Populate a sizer with a 'resizing' button and some other static
792 // decoration
2b5f62a0 793
6a5a7fba 794 wxFlexGridSizer *sizer = new wxFlexGridSizer(2);
2b5f62a0 795
6a5a7fba
VZ
796 m_button = new wxButton( this, wxID_RESIZE_FRAME, "Press me",
797 wxDefaultPosition, SMALL_BUTTON );
2b5f62a0 798
80791c37 799 sizer->Add(m_button, wxSizerFlags().Centre().Border(wxALL, 20));
6a5a7fba
VZ
800 sizer->Add(new wxStaticText(this, wxID_ANY, "This is just"),
801 wxSizerFlags().Centre());
802 sizer->Add(new wxStaticText(this, wxID_ANY, "some decoration"),
803 wxSizerFlags().Centre());
804 sizer->Add(new wxStaticText(this, wxID_ANY, "for you to scroll..."),
805 wxSizerFlags().Centre());
2b5f62a0
VZ
806
807 // Then use the sizer to set the scrolled region size.
808
6a5a7fba
VZ
809 SetSizer( sizer );
810
ce7fe42e 811 Connect(wxID_RESIZE_FRAME, wxEVT_BUTTON,
6a5a7fba 812 wxCommandEventHandler(MySizerScrolledWindow::OnResizeClick));
2b5f62a0
VZ
813}
814
6a5a7fba 815void MySizerScrolledWindow::OnResizeClick(wxCommandEvent &WXUNUSED(event))
2b5f62a0
VZ
816{
817 // Arbitrarily resize the button to change the minimum size of
818 // the (scrolled) sizer.
819
6a5a7fba
VZ
820 if ( m_button->GetSize() == SMALL_BUTTON )
821 m_button->SetSizeHints(LARGE_BUTTON);
2b5f62a0 822 else
6a5a7fba 823 m_button->SetSizeHints(SMALL_BUTTON);
2b5f62a0
VZ
824
825 // Force update layout and scrollbars, since nothing we do here
826 // necessarily generates a size event which would do it for us.
2b5f62a0
VZ
827 FitInside();
828}
829
57ac1a56 830// ----------------------------------------------------------------------------
fdd3ed7a 831// MyFrame
57ac1a56 832// ----------------------------------------------------------------------------
fdd3ed7a 833
6a5a7fba
VZ
834const wxWindowID Scroll_Test_Simple = wxWindow::NewControlId();
835const wxWindowID Scroll_Test_Canvas = wxWindow::NewControlId();
836const wxWindowID Scroll_Test_Sizers = wxWindow::NewControlId();
837const wxWindowID Scroll_Test_Sub = wxWindow::NewControlId();
838const wxWindowID Scroll_Test_Auto = wxWindow::NewControlId();
fdd3ed7a 839
9675a49d 840const wxWindowID Scroll_TglBtn_Sync = wxWindow::NewControlId();
1ddb6d28 841const wxWindowID Scroll_Radio_ShowScrollbar = wxWindow::NewControlId();
9675a49d 842
fdd3ed7a 843BEGIN_EVENT_TABLE(MyFrame,wxFrame)
6a5a7fba
VZ
844 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
845 EVT_MENU(wxID_EXIT, MyFrame::OnQuit)
846
847 EVT_MENU(Scroll_Test_Simple, MyFrame::OnTestSimple)
848 EVT_MENU(Scroll_Test_Canvas, MyFrame::OnTestCanvas)
849 EVT_MENU(Scroll_Test_Sizers, MyFrame::OnTestSizer)
850 EVT_MENU(Scroll_Test_Sub, MyFrame::OnTestSub)
851 EVT_MENU(Scroll_Test_Auto, MyFrame::OnTestAuto)
9675a49d
VZ
852
853 EVT_TOGGLEBUTTON(Scroll_TglBtn_Sync, MyFrame::OnToggleSync)
1ddb6d28 854 EVT_RADIOBOX(Scroll_Radio_ShowScrollbar, MyFrame::OnScrollbarVisibility)
fdd3ed7a
RR
855END_EVENT_TABLE()
856
857MyFrame::MyFrame()
6a5a7fba 858 : wxFrame(NULL, wxID_ANY, "wxWidgets scroll sample")
fdd3ed7a 859{
41f02b9a
FM
860 SetIcon(wxICON(sample));
861
6a5a7fba
VZ
862 wxMenu *menuFile = new wxMenu;
863 menuFile->Append(wxID_ABOUT, "&About..");
864 menuFile->AppendSeparator();
865 menuFile->Append(wxID_EXIT, "E&xit\tAlt-X");
866
867 wxMenu *menuTest = new wxMenu;
868 menuTest->Append(Scroll_Test_Simple, "&Simple scroll window\tF1",
869 "Simplest possible scrolled window test.");
870 menuTest->Append(Scroll_Test_Canvas, "Scrolled window with &children\tF2",
871 "Scrolled window with controls on it.");
872 menuTest->Append(Scroll_Test_Sizers, "Scrolled window with si&zer\tF3",
873 "Scrolled window with children managed by sizer.");
874 menuTest->Append(Scroll_Test_Sub, "Scrolled s&ub-window\tF4",
875 "Window only part of which is scrolled.");
876 menuTest->Append(Scroll_Test_Auto, "&Auto-scrolled window\tF5",
877 "Window which scrolls when the mouse is held pressed "
878 "outside of it.");
879
880 wxMenuBar *mbar = new wxMenuBar;
881 mbar->Append(menuFile, "&File");
882 mbar->Append(menuTest, "&Test");
883
884 SetMenuBar( mbar );
885
886
1ddb6d28
VZ
887 wxPanel *panel = new wxPanel(this);
888
6a5a7fba
VZ
889 const wxSizerFlags flagsExpand(wxSizerFlags(1).Expand());
890
891 wxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
1ddb6d28 892 topsizer->Add(new wxStaticText(panel, wxID_ANY,
6a5a7fba
VZ
893 "The windows below should behave in the same way, even though\n"
894 "they're implemented quite differently, see the code for details.\n"
895 "\n"
896 "The lines redrawn during odd/even repaint iterations are drawn in\n"
897 "red/blue colour to allow seeing immediately how much is repainted,\n"
898 "don't be surprised by this."),
899 wxSizerFlags().Centre().Border());
8a73bf3d 900
1ddb6d28
VZ
901 m_win1 = new MyScrolledWindowDumb(panel);
902 m_win2 = new MyScrolledWindowSmart(panel);
9675a49d
VZ
903
904 wxSizer *sizerScrollWin = new wxBoxSizer(wxHORIZONTAL);
905 sizerScrollWin->Add(m_win1, flagsExpand);
906 sizerScrollWin->Add(m_win2, flagsExpand);
907 topsizer->Add(sizerScrollWin, flagsExpand);
908
1ddb6d28
VZ
909 const wxSizerFlags
910 flagsHBorder(wxSizerFlags().Centre().Border(wxLEFT | wxRIGHT));
6362d82b 911
9675a49d 912 wxSizer *sizerBtns = new wxBoxSizer(wxHORIZONTAL);
1ddb6d28
VZ
913
914 // the radio buttons are in the same order as wxSHOW_SB_XXX values but
915 // offset by 1
916 const wxString visibilities[] = { "&never", "&default", "&always" };
917 wxRadioBox *radio = new wxRadioBox(panel, Scroll_Radio_ShowScrollbar,
918 "Left &scrollbar visibility: ",
919 wxDefaultPosition, wxDefaultSize,
920 WXSIZEOF(visibilities), visibilities);
921 radio->SetSelection(wxSHOW_SB_DEFAULT + 1);
922 sizerBtns->Add(radio, flagsHBorder);
923
924 sizerBtns->Add(new wxToggleButton(panel, Scroll_TglBtn_Sync, "S&ynchronize"),
6362d82b
VZ
925 flagsHBorder);
926
9675a49d 927 topsizer->Add(sizerBtns, wxSizerFlags().Centre().Border());
57ac1a56 928
1ddb6d28 929 panel->SetSizer(topsizer);
ed673c6a 930
1ddb6d28
VZ
931 wxSize size = panel->GetBestSize();
932 SetSizeHints(size);
933 SetClientSize(2*size);
fdd3ed7a 934
6a5a7fba 935 Show();
8e217128
RR
936}
937
9675a49d
VZ
938void MyFrame::OnToggleSync(wxCommandEvent& event)
939{
940 if ( event.IsChecked() )
941 {
942 m_win1->SyncWith(m_win2);
943 m_win2->SyncWith(m_win1);
944 }
945 else
946 {
947 m_win1->SyncWith(NULL);
948 m_win2->SyncWith(NULL);
949 }
950}
951
1ddb6d28 952void MyFrame::OnScrollbarVisibility(wxCommandEvent& event)
6362d82b
VZ
953{
954 m_win1->ShowScrollbars(wxSHOW_SB_NEVER,
1ddb6d28 955 wxScrollbarVisibility(event.GetSelection() - 1));
6362d82b
VZ
956}
957
6a5a7fba 958void MyFrame::OnQuit(wxCommandEvent &WXUNUSED(event))
8e217128 959{
6a5a7fba 960 Close(true);
fdd3ed7a
RR
961}
962
963void MyFrame::OnAbout( wxCommandEvent &WXUNUSED(event) )
964{
cb7c02fd 965 (void)wxMessageBox( "Scrolled window sample\n"
6a5a7fba
VZ
966 "\n"
967 "Robert Roebling (c) 1998\n"
968 "Vadim Zeitlin (c) 2008\n"
969 "Autoscrolling examples\n"
970 "Ron Lee (c) 2002\n"
971 "Auto-timed-scrolling example\n"
972 "Matt Gregory (c) 2003\n",
973 "About wxWidgets scroll sample",
974 wxICON_INFORMATION | wxOK );
fdd3ed7a
RR
975}
976
6a5a7fba 977// ----------------------------------------------------------------------------
fdd3ed7a 978// MyApp
6a5a7fba
VZ
979// ----------------------------------------------------------------------------
980
981IMPLEMENT_APP(MyApp)
fdd3ed7a
RR
982
983bool MyApp::OnInit()
984{
45e6e6f8
VZ
985 if ( !wxApp::OnInit() )
986 return false;
987
6a5a7fba 988 new MyFrame();
fdd3ed7a 989
db4ad642 990 return true;
fdd3ed7a
RR
991}
992
8a73bf3d
VZ
993// ----------------------------------------------------------------------------
994// MyScrolledWindowXXX
995// ----------------------------------------------------------------------------
996
8a73bf3d
VZ
997void MyScrolledWindowDumb::OnDraw(wxDC& dc)
998{
999 // this is useful to see which lines are redrawn
1000 static size_t s_redrawCount = 0;
1001 dc.SetTextForeground(s_redrawCount++ % 2 ? *wxRED : *wxBLUE);
1002
6a5a7fba 1003 int y = 0;
8a73bf3d
VZ
1004 for ( size_t line = 0; line < m_nLines; line++ )
1005 {
6a5a7fba 1006 int yPhys;
8a73bf3d
VZ
1007 CalcScrolledPosition(0, y, NULL, &yPhys);
1008
6a5a7fba 1009 dc.DrawText(wxString::Format("Line %u (logical %d, physical %d)",
b143cf70 1010 unsigned(line), y, yPhys), 0, y);
8a73bf3d
VZ
1011 y += m_hLine;
1012 }
1013}
1014
1015void MyScrolledWindowSmart::OnDraw(wxDC& dc)
1016{
1017 // this is useful to see which lines are redrawn
1018 static size_t s_redrawCount = 0;
1019 dc.SetTextForeground(s_redrawCount++ % 2 ? *wxRED : *wxBLUE);
1020
1021 // update region is always in device coords, translate to logical ones
1022 wxRect rectUpdate = GetUpdateRegion().GetBox();
1023 CalcUnscrolledPosition(rectUpdate.x, rectUpdate.y,
1024 &rectUpdate.x, &rectUpdate.y);
1025
1026 size_t lineFrom = rectUpdate.y / m_hLine,
1027 lineTo = rectUpdate.GetBottom() / m_hLine;
1028
1029 if ( lineTo > m_nLines - 1)
1030 lineTo = m_nLines - 1;
1031
6a5a7fba 1032 int y = lineFrom*m_hLine;
8a73bf3d
VZ
1033 for ( size_t line = lineFrom; line <= lineTo; line++ )
1034 {
6a5a7fba 1035 int yPhys;
8a73bf3d
VZ
1036 CalcScrolledPosition(0, y, NULL, &yPhys);
1037
6a5a7fba 1038 dc.DrawText(wxString::Format("Line %u (logical %d, physical %d)",
b143cf70 1039 unsigned(line), y, yPhys), 0, y);
8a73bf3d
VZ
1040 y += m_hLine;
1041 }
1042}
57ac1a56
RN
1043
1044// ----------------------------------------------------------------------------
6a5a7fba 1045// MyAutoScrollingWindow
57ac1a56
RN
1046// ----------------------------------------------------------------------------
1047
cb7c02fd 1048BEGIN_EVENT_TABLE(MyAutoScrollingWindow, wxScrolled<wxWindow>)
6a5a7fba
VZ
1049 EVT_LEFT_DOWN(MyAutoScrollingWindow::OnMouseLeftDown)
1050 EVT_LEFT_UP(MyAutoScrollingWindow::OnMouseLeftUp)
1051 EVT_MOTION(MyAutoScrollingWindow::OnMouseMove)
1052 EVT_MOUSE_CAPTURE_LOST(MyAutoScrollingWindow::OnMouseCaptureLost)
1053 EVT_SCROLLWIN(MyAutoScrollingWindow::OnScroll)
57ac1a56
RN
1054END_EVENT_TABLE()
1055
6a5a7fba 1056MyAutoScrollingWindow::MyAutoScrollingWindow(wxWindow* parent)
cb7c02fd
VZ
1057 : wxScrolled<wxWindow>(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize,
1058 wxVSCROLL | wxHSCROLL | wxSUNKEN_BORDER),
6a5a7fba
VZ
1059 m_selStart(-1, -1),
1060 m_cursor(-1, -1),
1061 m_font(9, wxFONTFAMILY_TELETYPE, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL)
57ac1a56
RN
1062{
1063 wxClientDC dc(this);
1064 // query dc for text size
1065 dc.SetFont(m_font);
6a5a7fba 1066 dc.GetTextExtent(wxString("A"), &m_fontW, &m_fontH);
57ac1a56
RN
1067 // set up the virtual window
1068 SetScrollbars(m_fontW, m_fontH, sm_lineLen, sm_lineCnt);
1069}
1070
6a5a7fba
VZ
1071wxRect
1072MyAutoScrollingWindow::DeviceCoordsToGraphicalChars(wxRect updRect) const
57ac1a56
RN
1073{
1074 wxPoint pos(updRect.GetPosition());
1075 pos = DeviceCoordsToGraphicalChars(pos);
1076 updRect.x = pos.x;
1077 updRect.y = pos.y;
1078 updRect.width /= m_fontW;
1079 updRect.height /= m_fontH;
1080 // the *CoordsToGraphicalChars() funcs round down to upper-left corner,
1081 // so an off-by-one correction is needed
1082 ++updRect.width; // kludge
1083 ++updRect.height; // kludge
1084 return updRect;
1085}
1086
6a5a7fba
VZ
1087wxPoint
1088MyAutoScrollingWindow::DeviceCoordsToGraphicalChars(wxPoint pos) const
57ac1a56
RN
1089{
1090 pos.x /= m_fontW;
1091 pos.y /= m_fontH;
0b0f6f87 1092 pos += GetViewStart();
57ac1a56
RN
1093 return pos;
1094}
1095
6a5a7fba
VZ
1096wxPoint
1097MyAutoScrollingWindow::GraphicalCharToDeviceCoords(wxPoint pos) const
57ac1a56 1098{
0b0f6f87 1099 pos -= GetViewStart();
57ac1a56
RN
1100 pos.x *= m_fontW;
1101 pos.y *= m_fontH;
1102 return pos;
1103}
1104
6a5a7fba
VZ
1105wxRect
1106MyAutoScrollingWindow::LogicalCoordsToGraphicalChars(wxRect updRect) const
57ac1a56
RN
1107{
1108 wxPoint pos(updRect.GetPosition());
1109 pos = LogicalCoordsToGraphicalChars(pos);
1110 updRect.x = pos.x;
1111 updRect.y = pos.y;
1112 updRect.width /= m_fontW;
1113 updRect.height /= m_fontH;
1114 // the *CoordsToGraphicalChars() funcs round down to upper-left corner,
1115 // so an off-by-one correction is needed
1116 ++updRect.width; // kludge
1117 ++updRect.height; // kludge
1118 return updRect;
1119}
1120
6a5a7fba
VZ
1121wxPoint
1122MyAutoScrollingWindow::LogicalCoordsToGraphicalChars(wxPoint pos) const
57ac1a56
RN
1123{
1124 pos.x /= m_fontW;
1125 pos.y /= m_fontH;
1126 return pos;
1127}
1128
6a5a7fba
VZ
1129wxPoint
1130MyAutoScrollingWindow::GraphicalCharToLogicalCoords(wxPoint pos) const
57ac1a56
RN
1131{
1132 pos.x *= m_fontW;
1133 pos.y *= m_fontH;
1134 return pos;
1135}
1136
6a5a7fba 1137void MyAutoScrollingWindow::MyRefresh()
57ac1a56
RN
1138{
1139 static wxPoint lastSelStart(-1, -1), lastCursor(-1, -1);
1140 // refresh last selected area (to deselect previously selected text)
1141 wxRect lastUpdRect(
1142 GraphicalCharToDeviceCoords(lastSelStart),
1143 GraphicalCharToDeviceCoords(lastCursor)
1144 );
1145 // off-by-one corrections, necessary because it's not possible to know
1146 // when to round up until rect is normalized by lastUpdRect constructor
1147 lastUpdRect.width += m_fontW; // kludge
1148 lastUpdRect.height += m_fontH; // kludge
1149 // refresh currently selected (to select previously unselected text)
1150 wxRect updRect(
1151 GraphicalCharToDeviceCoords(m_selStart),
1152 GraphicalCharToDeviceCoords(m_cursor)
1153 );
1154 // off-by-one corrections
1155 updRect.width += m_fontW; // kludge
1156 updRect.height += m_fontH; // kludge
1157 // find necessary refresh areas
6a5a7fba
VZ
1158 int rx = lastUpdRect.x;
1159 int ry = lastUpdRect.y;
1160 int rw = updRect.x - lastUpdRect.x;
1161 int rh = lastUpdRect.height;
57ac1a56
RN
1162 if (rw && rh) {
1163 RefreshRect(DCNormalize(rx, ry, rw, rh));
1164 }
1165 rx = updRect.x;
1166 ry = updRect.y + updRect.height;
1167 rw= updRect.width;
1168 rh = (lastUpdRect.y + lastUpdRect.height) - (updRect.y + updRect.height);
1169 if (rw && rh) {
1170 RefreshRect(DCNormalize(rx, ry, rw, rh));
1171 }
1172 rx = updRect.x + updRect.width;
1173 ry = lastUpdRect.y;
1174 rw = (lastUpdRect.x + lastUpdRect.width) - (updRect.x + updRect.width);
1175 rh = lastUpdRect.height;
1176 if (rw && rh) {
1177 RefreshRect(DCNormalize(rx, ry, rw, rh));
1178 }
1179 rx = updRect.x;
1180 ry = lastUpdRect.y;
1181 rw = updRect.width;
1182 rh = updRect.y - lastUpdRect.y;
1183 if (rw && rh) {
1184 RefreshRect(DCNormalize(rx, ry, rw, rh));
1185 }
1186 // update last
1187 lastSelStart = m_selStart;
1188 lastCursor = m_cursor;
1189}
1190
6a5a7fba 1191bool MyAutoScrollingWindow::IsSelected(int chX, int chY) const
57ac1a56
RN
1192{
1193 if (IsInside(chX, m_selStart.x, m_cursor.x)
1194 && IsInside(chY, m_selStart.y, m_cursor.y)) {
4e04f777 1195 return true;
57ac1a56 1196 }
4e04f777 1197 return false;
57ac1a56
RN
1198}
1199
6a5a7fba 1200bool MyAutoScrollingWindow::IsInside(int k, int bound1, int bound2)
57ac1a56
RN
1201{
1202 if ((k >= bound1 && k <= bound2) || (k >= bound2 && k <= bound1)) {
4e04f777 1203 return true;
57ac1a56 1204 }
4e04f777 1205 return false;
57ac1a56
RN
1206}
1207
6a5a7fba
VZ
1208wxRect
1209MyAutoScrollingWindow::DCNormalize(int x, int y, int w, int h)
57ac1a56
RN
1210{
1211 // this is needed to get rid of the graphical remnants from the selection
1212 // I think it's because DrawRectangle() excludes a pixel in either direction
1213 const int kludge = 1;
1214 // make (x, y) the top-left corner
1215 if (w < 0) {
1216 w = -w + kludge;
1217 x -= w;
1218 } else {
1219 x -= kludge;
1220 w += kludge;
1221 }
1222 if (h < 0) {
1223 h = -h + kludge;
1224 y -= h;
1225 } else {
1226 y -= kludge;
1227 h += kludge;
1228 }
1229 return wxRect(x, y, w, h);
1230}
1231
6a5a7fba 1232void MyAutoScrollingWindow::OnDraw(wxDC& dc)
57ac1a56
RN
1233{
1234 dc.SetFont(m_font);
de914436
PC
1235 wxBrush normBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
1236 wxBrush selBrush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
57ac1a56 1237 dc.SetPen(*wxTRANSPARENT_PEN);
1663ec5f
PC
1238 const wxString str = sm_testData;
1239 size_t strLength = str.length();
0a6b9955 1240 wxString::const_iterator str_i = str.begin();
4e04f777 1241
57ac1a56
RN
1242 // draw the characters
1243 // 1. for each update region
1244 for (wxRegionIterator upd(GetUpdateRegion()); upd; ++upd) {
57ac1a56
RN
1245 wxRect updRect = upd.GetRect();
1246 wxRect updRectInGChars(DeviceCoordsToGraphicalChars(updRect));
1247 // 2. for each row of chars in the update region
1248 for (int chY = updRectInGChars.y
1249 ; chY <= updRectInGChars.y + updRectInGChars.height; ++chY) {
1250 // 3. for each character in the row
1663ec5f 1251 bool isFirstX = true;
57ac1a56
RN
1252 for (int chX = updRectInGChars.x
1253 ; chX <= updRectInGChars.x + updRectInGChars.width
1254 ; ++chX) {
1255 // 4. set up dc
1256 if (IsSelected(chX, chY)) {
1257 dc.SetBrush(selBrush);
1258 dc.SetTextForeground( wxSystemSettings::GetColour
1259 (wxSYS_COLOUR_HIGHLIGHTTEXT));
1260 } else {
1261 dc.SetBrush(normBrush);
1262 dc.SetTextForeground( wxSystemSettings::GetColour
1263 (wxSYS_COLOUR_WINDOWTEXT));
1264 }
1265 // 5. find position info
1266 wxPoint charPos = GraphicalCharToLogicalCoords(wxPoint
1267 (chX, chY));
1268 // 6. draw!
1269 dc.DrawRectangle(charPos.x, charPos.y, m_fontW, m_fontH);
4e04f777
WS
1270 size_t charIndex = chY * sm_lineLen + chX;
1271 if (chY < sm_lineCnt &&
1272 chX < sm_lineLen &&
1663ec5f 1273 charIndex < strLength)
4e04f777 1274 {
1663ec5f
PC
1275 if (isFirstX)
1276 {
1277 str_i = str.begin() + charIndex;
1278 isFirstX = false;
1279 }
1280 dc.DrawText(*str_i, charPos.x, charPos.y);
1281 ++str_i;
57ac1a56
RN
1282 }
1283 }
1284 }
1285 }
1286}
1287
6a5a7fba 1288void MyAutoScrollingWindow::OnMouseLeftDown(wxMouseEvent& event)
57ac1a56
RN
1289{
1290 // initial press of mouse button sets the beginning of the selection
1291 m_selStart = DeviceCoordsToGraphicalChars(event.GetPosition());
1292 // set the cursor to the same position
1293 m_cursor = m_selStart;
1294 // draw/erase selection
1295 MyRefresh();
1296}
1297
6a5a7fba 1298void MyAutoScrollingWindow::OnMouseLeftUp(wxMouseEvent& WXUNUSED(event))
57ac1a56
RN
1299{
1300 // this test is necessary
1301 if (HasCapture()) {
1302 // uncapture mouse
1303 ReleaseMouse();
1304 }
1305}
1306
6a5a7fba 1307void MyAutoScrollingWindow::OnMouseMove(wxMouseEvent& event)
57ac1a56
RN
1308{
1309 // if user is dragging
1310 if (event.Dragging() && event.LeftIsDown()) {
1311 // set the new cursor position
1312 m_cursor = DeviceCoordsToGraphicalChars(event.GetPosition());
1313 // draw/erase selection
34949efe 1314 MyRefresh();
57ac1a56
RN
1315 // capture mouse to activate auto-scrolling
1316 if (!HasCapture()) {
1317 CaptureMouse();
1318 }
1319 }
1320}
1321
6a5a7fba
VZ
1322void
1323MyAutoScrollingWindow::OnMouseCaptureLost(wxMouseCaptureLostEvent&
1324 WXUNUSED(event))
e0666bdc
VZ
1325{
1326 // we only capture mouse for timed scrolling, so nothing is needed here
1327 // other than making sure to not call event.Skip()
1328}
1329
6a5a7fba 1330void MyAutoScrollingWindow::OnScroll(wxScrollWinEvent& event)
57ac1a56
RN
1331{
1332 // need to move the cursor when autoscrolling
1333 // FIXME: the cursor also moves when the scrollbar arrows are clicked
1334 if (HasCapture()) {
1335 if (event.GetOrientation() == wxHORIZONTAL) {
687706f5 1336 if (event.GetEventType() == wxEVT_SCROLLWIN_LINEUP) {
57ac1a56 1337 --m_cursor.x;
687706f5 1338 } else if (event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN) {
57ac1a56
RN
1339 ++m_cursor.x;
1340 }
1341 } else if (event.GetOrientation() == wxVERTICAL) {
687706f5 1342 if (event.GetEventType() == wxEVT_SCROLLWIN_LINEUP) {
57ac1a56 1343 --m_cursor.y;
687706f5 1344 } else if (event.GetEventType() == wxEVT_SCROLLWIN_LINEDOWN) {
57ac1a56
RN
1345 ++m_cursor.y;
1346 }
1347 }
1348 }
1349 MyRefresh();
1350 event.Skip();
1351}
1352
6a5a7fba
VZ
1353const int MyAutoScrollingWindow::sm_lineCnt = 125;
1354const int MyAutoScrollingWindow::sm_lineLen = 79;
1355const char *MyAutoScrollingWindow::sm_testData =
1356"162 Cult of the genius out of vanity. Because we think well of ourselves, but "
1357"nonetheless never suppose ourselves capable of producing a painting like one of "
1358"Raphael's or a dramatic scene like one of Shakespeare's, we convince ourselves "
1359"that the capacity to do so is quite extraordinarily marvelous, a wholly "
1360"uncommon accident, or, if we are still religiously inclined, a mercy from on "
1361"high. Thus our vanity, our self-love, promotes the cult of the genius: for only "
1362"if we think of him as being very remote from us, as a miraculum, does he not "
1363"aggrieve us (even Goethe, who was without envy, called Shakespeare his star of "
1364"the most distant heights [\"William! Stern der schonsten Ferne\": from Goethe's, "
1365"\"Between Two Worlds\"]; in regard to which one might recall the lines: \"the "
1366"stars, these we do not desire\" [from Goethe's, \"Comfort in Tears\"]). But, aside "
1367"from these suggestions of our vanity, the activity of the genius seems in no "
1368"way fundamentally different from the activity of the inventor of machines, the "
1369"scholar of astronomy or history, the master of tactics. All these activities "
1370"are explicable if one pictures to oneself people whose thinking is active in "
1371"one direction, who employ everything as material, who always zealously observe "
1372"their own inner life and that of others, who perceive everywhere models and "
1373"incentives, who never tire of combining together the means available to them. "
1374"Genius too does nothing except learn first how to lay bricks then how to build, "
1375"except continually seek for material and continually form itself around it. "
1376"Every activity of man is amazingly complicated, not only that of the genius: "
1377"but none is a \"miracle.\" Whence, then, the belief that genius exists only in "
1378"the artist, orator and philosopher? that only they have \"intuition\"? (Whereby "
1379"they are supposed to possess a kind of miraculous eyeglass with which they can "
1380"see directly into \"the essence of the thing\"!) It is clear that people speak of "
1381"genius only where the effects of the great intellect are most pleasant to them "
1382"and where they have no desire to feel envious. To call someone \"divine\" means: "
1383"\"here there is no need for us to compete.\" Then, everything finished and "
1384"complete is regarded with admiration, everything still becoming is undervalued. "
1385"But no one can see in the work of the artist how it has become; that is its "
1386"advantage, for wherever one can see the act of becoming one grows somewhat "
1387"cool. The finished and perfect art of representation repulses all thinking as "
1388"to how it has become; it tyrannizes as present completeness and perfection. "
1389"That is why the masters of the art of representation count above all as gifted "
1390"with genius and why men of science do not. In reality, this evaluation of the "
1391"former and undervaluation of the latter is only a piece of childishness in the "
1392"realm of reason. "
1393"\n\n"
1394"163 The serious workman. Do not talk about giftedness, inborn talents! One can "
1395"name great men of all kinds who were very little gifted. The acquired "
1396"greatness, became \"geniuses\" (as we put it), through qualities the lack of "
1397"which no one who knew what they were would boast of: they all possessed that "
1398"seriousness of the efficient workman which first learns to construct the parts "
1399"properly before it ventures to fashion a great whole; they allowed themselves "
1400"time for it, because they took more pleasure in making the little, secondary "
1401"things well than in the effect of a dazzling whole. the recipe for becoming a "
1402"good novelist, for example, is easy to give, but to carry it out presupposes "
1403"qualities one is accustomed to overlook when one says \"I do not have enough "
1404"talent.\" One has only to make a hundred or so sketches for novels, none longer "
1405"than two pages but of such distinctness that every word in them is necessary; "
1406"one should write down anecdotes each day until one has learned how to give them "
1407"the most pregnant and effective form; one should be tireless in collecting and "
1408"describing human types and characters; one should above all relate things to "
1409"others and listen to others relate, keeping one's eyes and ears open for the "
1410"effect produced on those present, one should travel like a landscape painter or "
1411"costume designer; one should excerpt for oneself out of the individual sciences "
1412"everything that will produce an artistic effect when it is well described, one "
1413"should, finally, reflect on the motives of human actions, disdain no signpost "
1414"to instruction about them and be a collector of these things by day and night. "
1415"One should continue in this many-sided exercise some ten years: what is then "
1416"created in the workshop, however, will be fit to go out into the world. What, "
1417"however, do most people do? They begin, not with the parts, but with the whole. "
1418"Perhaps they chance to strike a right note, excite attention and from then on "
1419"strike worse and worse notes, for good, natural reasons. Sometimes, when the "
1420"character and intellect needed to formulate such a life-plan are lacking, fate "
1421"and need take their place and lead the future master step by step through all "
1422"the stipulations of his trade. "
1423"\n\n"
1424"164 Peril and profit in the cult of the genius. The belief in great, superior, "
1425"fruitful spirits is not necessarily, yet nonetheless is very frequently "
1426"associated with that religious or semi-religious superstition that these "
1427"spirits are of supra-human origin and possess certain miraculous abilities by "
1428"virtue of which they acquire their knowledge by quite other means than the rest "
1429"of mankind. One ascribes to them, it seems, a direct view of the nature of the "
1430"world, as it were a hole in the cloak of appearance, and believes that, by "
1431"virtue of this miraculous seer's vision, they are able to communicate something "
1432"conclusive and decisive about man and the world without the toil and "
1433"rigorousness required by science. As long as there continue to be those who "
1434"believe in the miraculous in the domain of knowledge one can perhaps concede "
1435"that these people themselves derive some benefit from their belief, inasmuch as "
1436"through their unconditional subjection to the great spirits they create for "
1437"their own spirit during its time of development the finest form of discipline "
1438"and schooling. On the other hand, it is at least questionable whether the "
1439"superstitious belief in genius, in its privileges and special abilities, is of "
1440"benefit to the genius himself if it takes root in him. It is in any event a "
1441"dangerous sign when a man is assailed by awe of himself, whether it be the "
1442"celebrated Caesar's awe of Caesar or the awe of one's own genius now under "
1443"consideration; when the sacrificial incense which is properly rendered only to "
1444"a god penetrates the brain of the genius, so that his head begins to swim and "
1445"he comes to regard himself as something supra-human. The consequences that "
1446"slowly result are: the feeling of irresponsibility, of exceptional rights, the "
1447"belief that he confers a favor by his mere presence, insane rage when anyone "
1448"attempts even to compare him with others, let alone to rate him beneath them, "
1449"or to draw attention to lapses in his work. Because he ceases to practice "
1450"criticism of himself, at last one pinion after the other falls out of his "
1451"plumage: that superstitious eats at the roots of his powers and perhaps even "
1452"turns him into a hypocrite after his powers have fled from him. For the great "
1453"spirits themselves it is therefore probably more beneficial if they acquire an "
1454"insight into the nature and origin of their powers, if they grasp, that is to "
1455"say, what purely human qualities have come together in them and what fortunate "
1456"circumstances attended them: in the first place undiminished energy, resolute "
1457"application to individual goals, great personal courage, then the good fortune "
1458"to receive an upbringing which offered in the early years the finest teachers, "
1459"models and methods. To be sure, when their goal is the production of the "
1460"greatest possible effect, unclarity with regard to oneself and that "
1461"semi-insanity superadded to it has always achieved much; for what has been "
1462"admired and envied at all times has been that power in them by virtue of which "
1463"they render men will-less and sweep them away into the delusion that the "
1464"leaders they are following are supra-natural. Indeed, it elevates and inspires "
1465"men to believe that someone is in possession of supra-natural powers: to this "
1466"extent Plato was right to say [Plato: Phaedrus, 244a] that madness has brought "
1467"the greatest of blessings upon mankind. In rare individual cases this portion "
1468"of madness may, indeed, actually have been the means by which such a nature, "
1469"excessive in all directions, was held firmly together: in the life of "
1470"individuals, too, illusions that are in themselves poisons often play the role "
1471"of healers; yet, in the end, in the case of every \"genius\" who believes in his "
1472"own divinity the poison shows itself to the same degree as his \"genius\" grows "
1473"old: one may recall, for example, the case of Napoleon, whose nature certainly "
1474"grew into the mighty unity that sets him apart from all men of modern times "
1475"precisely through his belief in himself and his star and through the contempt "
1476"for men that flowed from it; until in the end, however, this same belief went "
1477"over into an almost insane fatalism, robbed him of his acuteness and swiftness "
1478"of perception, and became the cause of his destruction.";