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