Split this out from other changes to keep things sane..
[wxWidgets.git] / samples / caret / caret.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: caret.cpp
3 // Purpose: wxCaret sample
4 // Author: Robert Roebling
5 // Modified by:
6 // Created: 04/01/98
7 // RCS-ID: $Id$
8 // Copyright: (c) wxWindows team
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // For compilers that support precompilation, includes "wx/wx.h".
13 #include "wx/wxprec.h"
14
15 #ifdef __BORLANDC__
16 #pragma hdrstop
17 #endif
18
19 // for all others, include the necessary headers (this file is usually all you
20 // need because it includes almost all <standard< wxWindows headers
21 #ifndef WX_PRECOMP
22 #include "wx/wx.h"
23
24 #include "wx/log.h"
25 #endif
26
27 #include "wx/caret.h"
28
29 // ----------------------------------------------------------------------------
30 // ressources
31 // ----------------------------------------------------------------------------
32 // the application icon
33 #if defined(__WXGTK__) || defined(__WXX11__) || defined(__WXMOTIF__) || defined(__WXMAC__) || defined(__WXMGL__)
34 #include "mondrian.xpm"
35 #endif
36
37 // ----------------------------------------------------------------------------
38 // private classes
39 // ----------------------------------------------------------------------------
40
41 // Define a new application type, each program should derive a class from wxApp
42 class MyApp : public wxApp
43 {
44 public:
45 // override base class virtuals
46 // ----------------------------
47
48 // this one is called on application startup and is a good place for the app
49 // initialization (doing it here and not in the ctor allows to have an error
50 // return: if OnInit() returns false, the application terminates)
51 virtual bool OnInit();
52 };
53
54 // MyCanvas is a canvas on which you can type
55 class MyCanvas: public wxScrolledWindow
56 {
57 public:
58 MyCanvas() { }
59 MyCanvas( wxWindow *parent );
60 ~MyCanvas();
61
62 wxChar& CharAt(int x, int y) { return *(m_text + x + m_xChars * y); }
63
64 // operations
65 void CreateCaret();
66 void MoveCaret(int x, int y);
67
68 // caret movement
69 void Home() { m_xCaret = 0; }
70 void End() { m_xCaret = m_xChars - 1; }
71 void FirstLine() { m_yCaret = 0; }
72 void LastLine() { m_yCaret = m_yChars - 1; }
73 void PrevChar() { if ( !m_xCaret-- ) { End(); PrevLine(); } }
74 void NextChar() { if ( ++m_xCaret == m_xChars ) { Home(); NextLine(); } }
75 void PrevLine() { if ( !m_yCaret-- ) LastLine(); }
76 void NextLine() { if ( ++m_yCaret == m_yChars ) FirstLine(); }
77
78 // event handlers
79 void OnPaint( wxPaintEvent &event );
80 void OnSize( wxSizeEvent &event );
81 void OnChar( wxKeyEvent &event );
82
83 private:
84 // move the caret to m_xCaret, m_yCaret
85 void DoMoveCaret();
86
87 wxFont m_font;
88
89 // the margin around the text (looks nicer)
90 int m_xMargin, m_yMargin;
91
92 // size (in pixels) of one character
93 long m_widthChar, m_heightChar;
94
95 // position (in text coords) of the caret
96 int m_xCaret, m_yCaret;
97
98 // the size (in text coords) of the window
99 int m_xChars, m_yChars;
100
101 // the text
102 wxChar *m_text;
103
104 DECLARE_DYNAMIC_CLASS(MyCanvas)
105 DECLARE_EVENT_TABLE()
106 };
107
108
109 // Define a new frame type: this is going to be our main frame
110 class MyFrame : public wxFrame
111 {
112 public:
113 // ctor(s)
114 MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size);
115
116 // event handlers (these functions should _not_ be virtual)
117 void OnQuit(wxCommandEvent& event);
118 void OnAbout(wxCommandEvent& event);
119 void OnSetBlinkTime(wxCommandEvent& event);
120 void OnCaretMove(wxCommandEvent& event);
121
122 private:
123 MyCanvas *m_canvas;
124
125 // any class wishing to process wxWindows events must use this macro
126 DECLARE_EVENT_TABLE()
127 };
128
129 // ----------------------------------------------------------------------------
130 // constants
131 // ----------------------------------------------------------------------------
132
133 // IDs for the controls and the menu commands
134 enum
135 {
136 // menu items
137 Caret_Quit = 1,
138 Caret_About,
139 Caret_SetBlinkTime,
140 Caret_Move,
141
142 // controls start here (the numbers are, of course, arbitrary)
143 Caret_Text = 1000
144 };
145
146 // ----------------------------------------------------------------------------
147 // event tables and other macros for wxWindows
148 // ----------------------------------------------------------------------------
149
150 // the event tables connect the wxWindows events with the functions (event
151 // handlers) which process them. It can be also done at run-time, but for the
152 // simple menu events like this the static method is much simpler.
153 BEGIN_EVENT_TABLE(MyFrame, wxFrame)
154 EVT_MENU(Caret_Quit, MyFrame::OnQuit)
155 EVT_MENU(Caret_About, MyFrame::OnAbout)
156 EVT_MENU(Caret_SetBlinkTime, MyFrame::OnSetBlinkTime)
157 EVT_MENU(Caret_Move, MyFrame::OnCaretMove)
158 END_EVENT_TABLE()
159
160 // Create a new application object: this macro will allow wxWindows to create
161 // the application object during program execution (it's better than using a
162 // static object for many reasons) and also declares the accessor function
163 // wxGetApp() which will return the reference of the right type (i.e. MyApp and
164 // not wxApp)
165 IMPLEMENT_APP(MyApp)
166
167 // ============================================================================
168 // implementation
169 // ============================================================================
170
171 // ----------------------------------------------------------------------------
172 // the application class
173 // ----------------------------------------------------------------------------
174
175 // `Main program' equivalent: the program execution "starts" here
176 bool MyApp::OnInit()
177 {
178 // create and show the main application window
179 MyFrame *frame = new MyFrame(_T("Caret wxWindows sample"),
180 wxPoint(50, 50), wxSize(450, 340));
181
182 frame->Show(TRUE);
183
184 // success: wxApp::OnRun() will be called which will enter the main message
185 // loop and the application will run. If we returned FALSE here, the
186 // application would exit immediately.
187 return TRUE;
188 }
189
190 // ----------------------------------------------------------------------------
191 // main frame
192 // ----------------------------------------------------------------------------
193
194 // frame constructor
195 MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
196 : wxFrame((wxFrame *)NULL, -1, title, pos, size)
197 {
198 // set the frame icon
199 SetIcon(wxICON(mondrian));
200
201 // create a menu bar
202 wxMenu *menuFile = new wxMenu;
203
204 menuFile->Append(Caret_SetBlinkTime, _T("&Blink time...\tCtrl-B"));
205 menuFile->Append(Caret_Move, _T("&Move caret\tCtrl-C"));
206 menuFile->AppendSeparator();
207 menuFile->Append(Caret_About, _T("&About...\tCtrl-A"), _T("Show about dialog"));
208 menuFile->AppendSeparator();
209 menuFile->Append(Caret_Quit, _T("E&xit\tAlt-X"), _T("Quit this program"));
210
211 // now append the freshly created menu to the menu bar...
212 wxMenuBar *menuBar = new wxMenuBar;
213 menuBar->Append(menuFile, _T("&File"));
214
215 // ... and attach this menu bar to the frame
216 SetMenuBar(menuBar);
217
218 m_canvas = new MyCanvas(this);
219
220 // create a status bar just for fun (by default with 1 pane only)
221 CreateStatusBar(2);
222 SetStatusText(_T("Welcome to wxWindows!"));
223 }
224
225
226 // event handlers
227
228 void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
229 {
230 // TRUE is to force the frame to close
231 Close(TRUE);
232 }
233
234 void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
235 {
236 wxMessageBox(_T("The caret wxWindows sample.\n© 1999 Vadim Zeitlin"),
237 _T("About Caret"), wxOK | wxICON_INFORMATION, this);
238 }
239
240 void MyFrame::OnCaretMove(wxCommandEvent& WXUNUSED(event))
241 {
242 m_canvas->MoveCaret(10, 10);
243 }
244
245 void MyFrame::OnSetBlinkTime(wxCommandEvent& WXUNUSED(event))
246 {
247 long blinkTime = wxGetNumberFromUser
248 (
249 _T("The caret blink time is the time between two blinks"),
250 _T("Time in milliseconds:"),
251 _T("wxCaret sample"),
252 wxCaret::GetBlinkTime(), 0, 10000,
253 this
254 );
255 if ( blinkTime != -1 )
256 {
257 wxCaret::SetBlinkTime((int)blinkTime);
258 m_canvas->CreateCaret();
259 wxLogStatus(this, _T("Blink time set to %ld milliseconds."), blinkTime);
260 }
261 }
262
263 // ----------------------------------------------------------------------------
264 // MyCanvas
265 // ----------------------------------------------------------------------------
266
267 IMPLEMENT_DYNAMIC_CLASS(MyCanvas, wxScrolledWindow)
268
269 BEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)
270 EVT_PAINT(MyCanvas::OnPaint)
271 EVT_SIZE(MyCanvas::OnSize)
272 EVT_CHAR(MyCanvas::OnChar)
273 END_EVENT_TABLE()
274
275 MyCanvas::MyCanvas( wxWindow *parent )
276 : wxScrolledWindow( parent, -1,
277 wxDefaultPosition, wxDefaultSize,
278 wxSUNKEN_BORDER )
279 {
280 m_text = (wxChar *)NULL;
281
282 SetBackgroundColour(*wxWHITE);
283
284 m_font = wxFont(12, wxFONTFAMILY_TELETYPE,
285 wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
286
287 m_xCaret = m_yCaret =
288 m_xChars = m_yChars = 0;
289
290 m_xMargin = m_yMargin = 5;
291
292 CreateCaret();
293 }
294
295 MyCanvas::~MyCanvas()
296 {
297 free(m_text);
298 }
299
300 void MyCanvas::CreateCaret()
301 {
302 wxClientDC dc(this);
303 dc.SetFont(m_font);
304 m_heightChar = dc.GetCharHeight();
305 m_widthChar = dc.GetCharWidth();
306
307 wxCaret *caret = new wxCaret(this, m_widthChar, m_heightChar);
308 SetCaret(caret);
309
310 caret->Move(m_xMargin, m_yMargin);
311 caret->Show();
312 }
313
314 void MyCanvas::MoveCaret(int x, int y)
315 {
316 m_xCaret = x;
317 m_yCaret = y;
318
319 DoMoveCaret();
320 }
321
322 void MyCanvas::DoMoveCaret()
323 {
324 wxLogStatus(_T("Caret is at (%d, %d)"), m_xCaret, m_yCaret);
325
326 GetCaret()->Move(m_xMargin + m_xCaret * m_widthChar,
327 m_yMargin + m_yCaret * m_heightChar);
328 }
329
330 void MyCanvas::OnSize( wxSizeEvent &event )
331 {
332 m_xChars = (event.GetSize().x - 2*m_xMargin) / m_widthChar;
333 m_yChars = (event.GetSize().y - 2*m_yMargin) / m_heightChar;
334 if ( !m_xChars )
335 m_xChars = 1;
336 if ( !m_yChars )
337 m_yChars = 1;
338
339 free(m_text);
340 m_text = (wxChar *)calloc(m_xChars * m_yChars, sizeof(wxChar));
341
342 wxFrame *frame = wxDynamicCast(GetParent(), wxFrame);
343
344 if ( frame && frame->GetStatusBar() )
345 {
346 wxString msg;
347 msg.Printf(_T("Panel size is (%d, %d)"), m_xChars, m_yChars);
348
349 frame->SetStatusText(msg, 1);
350 }
351
352 event.Skip();
353 }
354
355 // NB: this method is horrible inefficient especially because the caret
356 // needs to be redrawn often and in this case we only have to redraw
357 // the caret location and not the entire window - in a real program we
358 // would use GetUpdateRegion() and iterate over rectangles it contains
359 void MyCanvas::OnPaint( wxPaintEvent &WXUNUSED(event) )
360 {
361 wxCaretSuspend cs(this);
362 wxPaintDC dc( this );
363 PrepareDC( dc );
364 dc.Clear();
365
366 dc.SetFont( m_font );
367
368 for ( int y = 0; y < m_yChars; y++ )
369 {
370 wxString line;
371
372 for ( int x = 0; x < m_xChars; x++ )
373 {
374 wxChar ch = CharAt(x, y);
375 if ( !ch )
376 ch = _T(' ');
377 line += ch;
378 }
379
380 dc.DrawText( line, m_xMargin, m_yMargin + y * m_heightChar );
381 }
382 }
383
384 void MyCanvas::OnChar( wxKeyEvent &event )
385 {
386 switch ( event.GetKeyCode() )
387 {
388 case WXK_LEFT:
389 PrevChar();
390 break;
391
392 case WXK_RIGHT:
393 NextChar();
394 break;
395
396 case WXK_UP:
397 PrevLine();
398 break;
399
400 case WXK_DOWN:
401 NextLine();
402 break;
403
404 case WXK_HOME:
405 Home();
406 break;
407
408 case WXK_END:
409 End();
410 break;
411
412 case WXK_RETURN:
413 Home();
414 NextLine();
415 break;
416
417 default:
418 if ( !event.AltDown() && wxIsprint(event.GetKeyCode()) )
419 {
420 wxChar ch = (wxChar)event.GetKeyCode();
421 CharAt(m_xCaret, m_yCaret) = ch;
422
423 wxCaretSuspend cs(this);
424 wxClientDC dc(this);
425 dc.SetFont(m_font);
426 dc.SetBackgroundMode(wxSOLID); // overwrite old value
427 dc.DrawText(ch, m_xMargin + m_xCaret * m_widthChar,
428 m_yMargin + m_yCaret * m_heightChar );
429
430 NextChar();
431 }
432 else
433 {
434 event.Skip();
435 }
436 }
437
438 DoMoveCaret();
439 }
440