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