]> git.saurik.com Git - wxWidgets.git/blame - demos/poem/wxpoem.cpp
Fix FILETIME <-> wxDateTime conversions while DST is in effect in wxMSW.
[wxWidgets.git] / demos / poem / wxpoem.cpp
CommitLineData
3013b6f4
JS
1/////////////////////////////////////////////////////////////////////////////
2// Name: wxpoem.cpp
3// Purpose: A small C++ program which displays a random poem on
4// execution. It also allows search for poems containing a
5// string.
6// It requires winpoem.dat and creates winpoem.idx.
7// Original version (WinPoem) written in 1994.
8// This has not been rewritten in a long time so
9// beware, inelegant code!
10// Author: Julian Smart
11// Created: 12/12/98
12// RCS-ID: $Id$
13// Copyright: (c) 1998 Julian Smart
14// Licence: wxWindows licence
15/////////////////////////////////////////////////////////////////////////////
16
3013b6f4
JS
17// For compilers that support precompilation, includes "wx.h".
18#include "wx/wxprec.h"
19
20#ifdef __BORLANDC__
21#pragma hdrstop
22#endif
23
24#ifndef WX_PRECOMP
88ac883a 25#include "wx/wx.h"
3013b6f4
JS
26#endif
27
3013b6f4
JS
28#include "wxpoem.h"
29
bbf73190
RR
30#include "corner1.xpm"
31#include "corner2.xpm"
32#include "corner3.xpm"
33#include "corner4.xpm"
000a8253 34#include "wxpoem.xpm"
bbf73190 35
5045ca9e 36#define BUFFER_SIZE 10000
3013b6f4
JS
37#define DEFAULT_POETRY_DAT "wxpoem"
38#define DEFAULT_POETRY_IND "wxpoem"
39#define DEFAULT_CHAR_HEIGHT 18
40#define DEFAULT_FONT "Swiss"
41#define DEFAULT_X_POS 0
42#define DEFAULT_Y_POS 0
43#define BORDER_SIZE 30
44#define THIN_LINE_BORDER 10
45#define THICK_LINE_BORDER 16
46#define THICK_LINE_WIDTH 2
47#define SHADOW_OFFSET 1
48#define X_SIZE 30
49#define Y_SIZE 20
50
5045ca9e 51static wxChar *poem_buffer; // Storage for each poem
6bb934d6 52static wxChar line[150]; // Storage for a line
3013b6f4
JS
53static int pages[30]; // For multipage poems -
54 // store the start of each page
55static long last_poem_start = 0; // Start of last found poem
56static long last_find = -1; // Point in file of last found
57 // search string
17d31882
DS
58static bool search_ok = false; // Search was successful
59static bool same_search = false; // Searching on same string
3013b6f4
JS
60
61static long poem_index[600]; // Index of poem starts
62static long nitems = 0; // Number of poems
3013b6f4
JS
63static int char_height = DEFAULT_CHAR_HEIGHT; // Actual height
64static int index_ptr = -1; // Pointer into index
65static int poem_height, poem_width; // Size of poem
66static int XPos; // Startup X position
67static int YPos; // Startup Y position
68static int pointSize = 12; // Font size
69
c2e5ea68
VZ
70static const wxChar *index_filename = NULL; // Index filename
71static const wxChar *data_filename = NULL; // Data filename
6bb934d6 72static wxChar error_buf[300]; // Error message buffer
17d31882
DS
73static bool loaded_ok = false; // Poem loaded ok
74static bool index_ok = false; // Index loaded ok
3013b6f4 75
17d31882 76static bool paging = false; // Are we paging?
3013b6f4
JS
77static int current_page = 0; // Currently viewed page
78
3013b6f4
JS
79// Backing bitmap
80wxBitmap *backingBitmap = NULL;
81
9a83f860
VZ
82void PoetryError(const wxChar *, const wxChar *caption=wxT("wxPoem Error"));
83void PoetryNotify(const wxChar *Msg, const wxChar *caption=wxT("wxPoem"));
3013b6f4 84void TryLoadIndex();
c2e5ea68 85bool LoadPoem(const wxChar *, long);
3013b6f4 86int GetIndex();
c2e5ea68 87int LoadIndex(const wxChar *);
3013b6f4 88bool Compile(void);
3013b6f4 89void FindMax(int *max_thing, int thing);
3013b6f4 90
17d31882
DS
91#if wxUSE_CLIPBOARD
92 #include "wx/dataobj.h"
93 #include "wx/clipbrd.h"
94#endif
3013b6f4 95
a6f28223
JS
96#ifdef __WXWINCE__
97 STDAPI_(__int64) CeGetRandomSeed();
98#endif
99
3013b6f4
JS
100IMPLEMENT_APP(MyApp)
101
102MainWindow *TheMainWindow = NULL;
103
104// Create the fonts
c7bde65d 105void MainWindow::CreateFonts()
3013b6f4 106{
c7bde65d
WS
107 m_normalFont = wxTheFontList->FindOrCreateFont(pointSize, wxSWISS, wxNORMAL, wxNORMAL);
108 m_boldFont = wxTheFontList->FindOrCreateFont(pointSize, wxSWISS, wxNORMAL, wxBOLD);
109 m_italicFont = wxTheFontList->FindOrCreateFont(pointSize, wxSWISS, wxITALIC, wxNORMAL);
3013b6f4
JS
110}
111
112BEGIN_EVENT_TABLE(MainWindow, wxFrame)
113 EVT_CLOSE(MainWindow::OnCloseWindow)
114 EVT_CHAR(MainWindow::OnChar)
17d31882 115 EVT_MENU(wxID_ANY, MainWindow::OnPopup)
3013b6f4
JS
116END_EVENT_TABLE()
117
118MainWindow::MainWindow(wxFrame *frame, wxWindowID id, const wxString& title,
c7bde65d
WS
119 const wxPoint& pos, const wxSize& size, long style):
120 wxFrame(frame, id, title, pos, size, style)
3013b6f4 121{
3baa2662
WS
122 m_corners[0] = m_corners[1] = m_corners[2] = m_corners[3] = NULL;
123
c7bde65d
WS
124 ReadPreferences();
125 CreateFonts();
3baa2662
WS
126
127 SetIcon(wxpoem_xpm);
128
129 m_corners[0] = new wxIcon( corner1_xpm );
130 m_corners[1] = new wxIcon( corner2_xpm );
131 m_corners[2] = new wxIcon( corner3_xpm );
132 m_corners[3] = new wxIcon( corner4_xpm );
133}
134
135MainWindow::~MainWindow()
136{
137 for (int i=0;i<4;i++)
138 {
139 if(m_corners[i])
140 {
141 delete m_corners[i];
142 }
143 }
3013b6f4
JS
144}
145
146// Read the poetry buffer, either for finding the size
147// or for writing to a bitmap (not to the window directly,
148// since that displays messily)
149// If DrawIt is true, we draw, otherwise we just determine the
150// size the window should be.
151void MainWindow::ScanBuffer(wxDC *dc, bool DrawIt, int *max_x, int *max_y)
152{
153 int i = pages[current_page];
154 int ch = -1;
3013b6f4
JS
155 int y = 0;
156 int j;
6bb934d6 157 wxChar *line_ptr;
3013b6f4 158 int curr_width = 0;
17d31882 159 bool page_break = false;
3013b6f4
JS
160
161 int width = 0;
162 int height = 0;
163
164 if (DrawIt)
165 {
c7bde65d
WS
166 y = (*max_y - poem_height)/2;
167 width = *max_x;
168 height = *max_y;
3013b6f4
JS
169 }
170
171 if (DrawIt && wxColourDisplay())
172 {
c7bde65d 173 dc->SetBrush(*wxLIGHT_GREY_BRUSH);
3baa2662 174 dc->SetPen(*wxGREY_PEN);
c7bde65d
WS
175 dc->DrawRectangle(0, 0, width, height);
176 dc->SetBackgroundMode(wxTRANSPARENT);
3013b6f4
JS
177 }
178
179 // See what ACTUAL char height is
c7bde65d
WS
180 if(m_normalFont)
181 dc->SetFont(*m_normalFont);
f3a8b1b6
VZ
182 wxCoord xx;
183 wxCoord yy;
9a83f860 184 dc->GetTextExtent(wxT("X"), &xx, &yy);
3013b6f4
JS
185 char_height = (int)yy;
186
187 if (current_page == 0)
3013b6f4 188 {
c7bde65d
WS
189 m_title = wxEmptyString;
190 }
191 else if (!m_title.empty())
192 {
193 dc->SetFont(* m_boldFont);
194 dc->GetTextExtent(m_title, &xx, &yy);
195 FindMax(&curr_width, (int)xx);
196
197 if (DrawIt)
198 {
199 int x = (width - xx)/2;
200 dc->SetFont(* m_boldFont);
201
202 // Change text to BLACK!
203 dc->SetTextForeground(* wxBLACK);
204 dc->DrawText(m_title, x, y);
205 // Change text to WHITE!
206 dc->SetTextForeground(* wxWHITE);
207 dc->DrawText(m_title, x-SHADOW_OFFSET, y-SHADOW_OFFSET);
208 }
209 y += char_height;
210 y += char_height;
3013b6f4
JS
211 }
212
213 while (ch != 0 && !page_break)
214 {
215 j = 0;
685aded8 216#if defined(__WXMSW__) || defined(__WXMAC__)
3013b6f4
JS
217 while (((ch = poem_buffer[i]) != 13) && (ch != 0))
218#else
219 while (((ch = poem_buffer[i]) != 10) && (ch != 0))
220#endif
221 {
254a2129 222 line[j] = (wxChar)ch;
3013b6f4
JS
223 j ++;
224 i ++;
225 }
226
685aded8 227#if defined(__WXMSW__) || defined(__WXMAC__)
3013b6f4
JS
228 if (ch == 13)
229#else
230 if (ch == 10)
231#endif
232 {
233 ch = -1;
234 i ++;
6688f23d 235#if defined(__WXMSW__) || defined(__WXMAC__)
3013b6f4
JS
236 // Add another to skip the linefeed
237 i ++;
238#endif
239 // If a single newline on its own, put a space in
240 if (j == 0)
241 {
c7bde65d
WS
242 line[j] = ' ';
243 j ++;
244 line[j] = 0;
3013b6f4
JS
245 }
246 }
247
248 if (j > 0)
249 {
c7bde65d
WS
250 line[j] = 0;
251 if (line[0] == '@')
3013b6f4 252 {
c7bde65d 253 switch (line[1])
3013b6f4 254 {
c7bde65d
WS
255 case 'P':
256 paging = true;
257 page_break = true;
258 break;
259
260 case 'T':
261 dc->SetFont(* m_boldFont);
262 line_ptr = line+3;
263
264 m_title = line_ptr;
9a83f860 265 m_title << wxT(" (cont'd)");
c7bde65d
WS
266
267 dc->GetTextExtent(line_ptr, &xx, &yy);
268 FindMax(&curr_width, (int)xx);
269
270 if (DrawIt)
271 {
272 int x = (width - xx)/2;
273 dc->SetFont(* m_boldFont);
274
275 // Change text to BLACK!
276 dc->SetTextForeground(* wxBLACK);
277 dc->DrawText(line_ptr, x, y);
278
279 // Change text to WHITE!
280 dc->SetTextForeground(* wxWHITE);
281 dc->DrawText(line_ptr, x-SHADOW_OFFSET, y-SHADOW_OFFSET);
282 dc->SetTextForeground(* wxWHITE);
283 }
284 break;
285
286 case 'A':
287 line_ptr = line+3;
288 dc->SetFont(* m_italicFont);
289
290 dc->GetTextExtent(line_ptr, &xx, &yy);
291 FindMax(&curr_width, (int)xx);
292
293 if (DrawIt)
294 {
295 int x = (width - xx)/2;
296 dc->SetTextForeground(* wxBLACK);
297 dc->DrawText(line_ptr, x, y);
298 }
299 break;
300
301 // Default: just ignore this line
302 default:
303 y -= char_height;
3013b6f4 304 }
c7bde65d
WS
305 }
306 else
307 {
308 dc->SetFont(* m_normalFont);
3013b6f4 309
c7bde65d 310 dc->GetTextExtent(line, &xx, &yy);
3013b6f4
JS
311 FindMax(&curr_width, (int)xx);
312
313 if (DrawIt)
314 {
c7bde65d
WS
315 int x = (int)((width - xx)/2.0);
316 dc->SetFont(* m_normalFont);
317 dc->SetTextForeground(* wxBLACK);
318 dc->DrawText(line, x, y);
3013b6f4 319 }
3013b6f4 320 }
3013b6f4
JS
321 }
322 y += char_height;
323 }
324
325 // Write (cont'd)
326 if (page_break)
327 {
9a83f860 328 const wxChar *cont = wxT("(cont'd)");
c7bde65d
WS
329
330 dc->SetFont(* m_normalFont);
331
332 dc->GetTextExtent(cont, &xx, &yy);
333 FindMax(&curr_width, (int)xx);
334 if (DrawIt)
335 {
336 int x = (int)((width - xx)/2.0);
337 dc->SetFont(* m_normalFont);
338 dc->SetTextForeground(* wxBLACK);
339 dc->DrawText(cont, x, y);
340 }
341 y += 2*char_height;
3013b6f4
JS
342 }
343
344 *max_x = (int)curr_width;
345 *max_y = (int)(y-char_height);
346
347 if (page_break)
c7bde65d 348 pages[current_page+1] = i;
3013b6f4 349 else
c7bde65d 350 paging = false;
3013b6f4
JS
351
352 if (DrawIt)
353 {
c7bde65d
WS
354 // Draw dark grey thick border
355 if (wxColourDisplay())
356 {
357 dc->SetBrush(*wxGREY_BRUSH);
358 dc->SetPen(*wxGREY_PEN);
359
360 // Left side
361 dc->DrawRectangle(0, 0, THIN_LINE_BORDER, height);
362 // Top side
363 dc->DrawRectangle(THIN_LINE_BORDER, 0, width-THIN_LINE_BORDER, THIN_LINE_BORDER);
364 // Right side
365 dc->DrawRectangle(width-THIN_LINE_BORDER, THIN_LINE_BORDER, width, height-THIN_LINE_BORDER);
366 // Bottom side
367 dc->DrawRectangle(THIN_LINE_BORDER, height-THIN_LINE_BORDER, width-THIN_LINE_BORDER, height);
368 }
369 // Draw border
370 // Have grey background, plus 3-d border -
371 // One black rectangle.
372 // Inside this, left and top sides - dark grey. Bottom and right -
373 // white.
374
375 // Change pen to black
376 dc->SetPen(*wxBLACK_PEN);
377 dc->DrawLine(THIN_LINE_BORDER, THIN_LINE_BORDER, width-THIN_LINE_BORDER, THIN_LINE_BORDER);
378 dc->DrawLine(width-THIN_LINE_BORDER, THIN_LINE_BORDER, width-THIN_LINE_BORDER, height-THIN_LINE_BORDER);
379 dc->DrawLine(width-THIN_LINE_BORDER, height-THIN_LINE_BORDER, THIN_LINE_BORDER, height-THIN_LINE_BORDER);
380 dc->DrawLine(THIN_LINE_BORDER, height-THIN_LINE_BORDER, THIN_LINE_BORDER, THIN_LINE_BORDER);
381
382 // Right and bottom white lines - 'grey' (black!) if
383 // we're running on a mono display.
384 if (wxColourDisplay())
3baa2662 385 dc->SetPen(*wxWHITE_PEN);
c7bde65d 386 else
3baa2662 387 dc->SetPen(*wxBLACK_PEN);
3013b6f4 388
c7bde65d
WS
389 dc->DrawLine(width-THICK_LINE_BORDER, THICK_LINE_BORDER,
390 width-THICK_LINE_BORDER, height-THICK_LINE_BORDER);
391 dc->DrawLine(width-THICK_LINE_BORDER, height-THICK_LINE_BORDER,
392 THICK_LINE_BORDER, height-THICK_LINE_BORDER);
393
394 // Left and top grey lines
3baa2662 395 dc->SetPen(*wxBLACK_PEN);
c7bde65d
WS
396 dc->DrawLine(THICK_LINE_BORDER, height-THICK_LINE_BORDER,
397 THICK_LINE_BORDER, THICK_LINE_BORDER);
398 dc->DrawLine(THICK_LINE_BORDER, THICK_LINE_BORDER,
399 width-THICK_LINE_BORDER, THICK_LINE_BORDER);
400
401 // Draw icons
3baa2662
WS
402 dc->DrawIcon(* m_corners[0], 0, 0);
403 dc->DrawIcon(* m_corners[1], int(width-32), 0);
c7bde65d
WS
404
405 int y2 = height - 32;
406 int x2 = (width-32);
3baa2662
WS
407 dc->DrawIcon(* m_corners[2], 0, y2);
408 dc->DrawIcon(* m_corners[3], x2, y2);
3013b6f4
JS
409 }
410}
411
412// Get an index (randomly generated) and load the poem
413void MainWindow::GetIndexLoadPoem(void)
414{
415 if (index_ok)
c7bde65d 416 index_ptr = GetIndex();
3013b6f4
JS
417
418 if (index_ptr > -1)
c7bde65d 419 loaded_ok = LoadPoem(data_filename, -1);
3013b6f4
JS
420}
421
422// Find the size of the poem and resize the window accordingly
423void MainWindow::Resize(void)
424{
425 wxClientDC dc(canvas);
426
427 // Get the poem size
17d31882 428 ScanBuffer(& dc, false, &poem_width, &poem_height);
3013b6f4
JS
429 int x = poem_width + (2*BORDER_SIZE);
430 int y = poem_height + (2*BORDER_SIZE);
431
432 SetClientSize(x, y);
433
434 // In case client size isn't what we set it to...
435 int xx, yy;
436 GetClientSize(&xx, &yy);
437
438 wxMemoryDC memDC;
439 if (backingBitmap) delete backingBitmap;
440 backingBitmap = new wxBitmap(x, yy);
441 memDC.SelectObject(* backingBitmap);
442
443 memDC.Clear();
c7bde65d 444 ScanBuffer(&memDC, true, &xx, &yy);
3013b6f4
JS
445}
446
447// Which is more?
448void FindMax(int *max_thing, int thing)
449{
c7bde65d
WS
450 if (thing > *max_thing)
451 *max_thing = thing;
3013b6f4
JS
452}
453
454// Next page/poem
455void MainWindow::NextPage(void)
456{
c7bde65d
WS
457 if (paging)
458 current_page ++;
459 else
460 {
461 current_page = 0;
462 GetIndexLoadPoem();
463 }
464 Resize();
3013b6f4
JS
465}
466
467// Previous page
468void MainWindow::PreviousPage(void)
469{
c7bde65d
WS
470 if (current_page > 0)
471 {
472 current_page --;
473 Resize();
474 }
3013b6f4
JS
475}
476
477// Search for a string
478void MainWindow::Search(bool ask)
479{
c7bde65d 480 long position;
3013b6f4 481
c7bde65d 482 if (ask || m_searchString.empty())
3013b6f4 483 {
9a83f860 484 wxString s = wxGetTextFromUser( wxT("Enter search string"), wxT("Search"), m_searchString);
489f6cf7 485 if (!s.empty())
c7bde65d
WS
486 {
487 s.MakeLower();
488 m_searchString = s;
489 search_ok = true;
490 }
491 else
492 {
493 search_ok = false;
494 }
3013b6f4
JS
495 }
496 else
497 {
c7bde65d
WS
498 same_search = true;
499 search_ok = true;
500 }
501
502 if (!m_searchString.empty() && search_ok)
503 {
504 position = DoSearch();
505 if (position > -1)
506 {
507 loaded_ok = LoadPoem(data_filename, position);
508 Resize();
509 }
510 else
511 {
512 last_poem_start = 0;
9a83f860 513 PoetryNotify(wxT("Search string not found."));
c7bde65d 514 }
3013b6f4 515 }
3013b6f4
JS
516}
517
3013b6f4
JS
518bool MyApp::OnInit()
519{
5045ca9e 520 poem_buffer = new wxChar[BUFFER_SIZE];
3013b6f4 521
3baa2662 522 // Seed the random number generator
a6f28223 523#ifdef __WXWINCE__
3baa2662 524 srand((unsigned) CeGetRandomSeed());
a6f28223 525#else
3baa2662 526 time_t current_time;
3013b6f4 527
3baa2662
WS
528 (void)time(&current_time);
529 srand((unsigned int)current_time);
a6f28223 530#endif
3013b6f4
JS
531
532// randomize();
3baa2662 533 pages[0] = 0;
3013b6f4 534
3baa2662
WS
535 TheMainWindow = new MainWindow(NULL,
536 wxID_ANY,
9a83f860 537 wxT("wxPoem"),
3baa2662
WS
538 wxPoint(XPos, YPos),
539 wxDefaultSize,
540 wxCAPTION|wxMINIMIZE_BOX|wxSYSTEM_MENU|wxCLOSE_BOX|wxFULL_REPAINT_ON_RESIZE
541 );
3013b6f4 542
3baa2662 543 TheMainWindow->canvas = new MyCanvas(TheMainWindow);
3013b6f4 544
3baa2662
WS
545 if (argc > 1)
546 {
547 index_filename = wxStrcpy(new wxChar[wxStrlen(argv[1]) + 1], argv[1]);
548 data_filename = wxStrcpy(new wxChar[wxStrlen(argv[1]) + 1], argv[1]);
549 }
550 else
551 {
9a83f860
VZ
552 index_filename = wxT(DEFAULT_POETRY_IND);
553 data_filename = wxT(DEFAULT_POETRY_DAT);
3baa2662
WS
554 }
555 TryLoadIndex();
3013b6f4 556
3baa2662
WS
557 TheMainWindow->GetIndexLoadPoem();
558 TheMainWindow->Resize();
559 TheMainWindow->Show(true);
3013b6f4 560
3baa2662 561 return true;
3013b6f4
JS
562}
563
564int MyApp::OnExit()
565{
3baa2662
WS
566 if (backingBitmap)
567 delete backingBitmap;
bbf73190 568
3baa2662 569 delete[] poem_buffer;
3013b6f4 570
3baa2662 571 return 0;
3013b6f4
JS
572}
573
bbf73190 574void MainWindow::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
3013b6f4 575{
3baa2662
WS
576 WritePreferences();
577 this->Destroy();
3013b6f4
JS
578}
579
580void MainWindow::OnChar(wxKeyEvent& event)
581{
582 canvas->OnChar(event);
583}
584
b23386b2 585BEGIN_EVENT_TABLE(MyCanvas, wxWindow)
3013b6f4
JS
586 EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
587 EVT_CHAR(MyCanvas::OnChar)
588 EVT_PAINT(MyCanvas::OnPaint)
589END_EVENT_TABLE()
590
591// Define a constructor for my canvas
c7bde65d
WS
592MyCanvas::MyCanvas(wxFrame *frame):
593 wxWindow(frame, wxID_ANY)
3013b6f4 594{
c7bde65d 595 m_popupMenu = new wxMenu;
9a83f860
VZ
596 m_popupMenu->Append(POEM_NEXT, wxT("Next poem/page"));
597 m_popupMenu->Append(POEM_PREVIOUS, wxT("Previous page"));
c7bde65d 598 m_popupMenu->AppendSeparator();
9a83f860
VZ
599 m_popupMenu->Append(POEM_SEARCH, wxT("Search"));
600 m_popupMenu->Append(POEM_NEXT_MATCH, wxT("Next match"));
601 m_popupMenu->Append(POEM_COPY, wxT("Copy to clipboard"));
602 m_popupMenu->Append(POEM_MINIMIZE, wxT("Minimize"));
c7bde65d 603 m_popupMenu->AppendSeparator();
9a83f860
VZ
604 m_popupMenu->Append(POEM_BIGGER_TEXT, wxT("Bigger text"));
605 m_popupMenu->Append(POEM_SMALLER_TEXT, wxT("Smaller text"));
c7bde65d 606 m_popupMenu->AppendSeparator();
9a83f860 607 m_popupMenu->Append(POEM_ABOUT, wxT("About wxPoem"));
c7bde65d 608 m_popupMenu->AppendSeparator();
9a83f860 609 m_popupMenu->Append(POEM_EXIT, wxT("Exit"));
17d31882
DS
610}
611
612MyCanvas::~MyCanvas()
613{
c7bde65d
WS
614 // Note: this must be done before the main window/canvas are destroyed
615 // or we get an error (no parent window for menu item button)
616 delete m_popupMenu;
617 m_popupMenu = NULL;
3013b6f4
JS
618}
619
620// Define the repainting behaviour
bbf73190 621void MyCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
3013b6f4
JS
622{
623 wxPaintDC dc(this);
624
625 if (backingBitmap)
626 {
627 int xx, yy;
628 TheMainWindow->GetClientSize(&xx, &yy);
f5d01a1c 629
59db9cfa 630 dc.DrawBitmap(* backingBitmap, 0, 0);
254a2129 631#if 0
3013b6f4
JS
632 wxMemoryDC memDC;
633 memDC.SelectObject(* backingBitmap);
634 dc.Blit(0, 0, backingBitmap->GetWidth(), backingBitmap->GetHeight(), &memDC, 0, 0);
59db9cfa 635#endif
3013b6f4
JS
636 }
637}
638
639void MyCanvas::OnMouseEvent(wxMouseEvent& event)
640{
c7bde65d 641 static int startPosX, startPosY, startFrameX, startFrameY;
3013b6f4 642
c7bde65d
WS
643 long x, y;
644 event.GetPosition(&x, &y);
3013b6f4 645
c7bde65d
WS
646 if (event.RightDown())
647 {
648 // Versions from wxWin 1.67 are probably OK
649 PopupMenu(m_popupMenu, (int)x, (int)y );
650 }
651 else if (event.LeftDown())
652 {
653 this->CaptureMouse();
654 int x1 = (int)x;
655 int y1 = (int)y;
656 ClientToScreen(&x1, &y1);
657 startPosX = x1;
658 startPosY = y1;
659 GetParent()->GetPosition(&startFrameX, &startFrameY);
660 }
661 else if (event.LeftUp())
662 {
663 if (GetCapture() == this) this->ReleaseMouse();
664 }
665 else if (event.Dragging() && event.LeftIsDown())
666 {
667 int x1 = (int)x;
668 int y1 = (int)y;
669 ClientToScreen(&x1, &y1);
f5d01a1c 670
c7bde65d
WS
671 int dX = x1 - startPosX;
672 int dY = y1 - startPosY;
673 GetParent()->Move(startFrameX + dX, startFrameY + dY);
674 }
3013b6f4
JS
675}
676
677// Process characters
678void MyCanvas::OnChar(wxKeyEvent& event)
679{
c7bde65d
WS
680 switch (event.GetKeyCode())
681 {
682 case 'n':
683 case 'N':
684 // Next match
685 TheMainWindow->Search(false);
686 break;
687
688 case 's':
689 case 'S':
690 // New search
691 TheMainWindow->Search(true);
692 break;
693
694 case WXK_SPACE:
695 case WXK_RIGHT:
696 case WXK_DOWN:
697 // Another poem
698 TheMainWindow->NextPage();
699 break;
700
701 case WXK_ESCAPE:
702 TheMainWindow->Close(true);
703 default:
704 break;
705 }
706}
3013b6f4
JS
707
708// Load index file
c2e5ea68 709int LoadIndex(const wxChar *file_name)
3013b6f4
JS
710{
711 long data;
712 FILE *index_file;
713
6bb934d6 714 wxChar buf[100];
3013b6f4 715
6bb934d6 716 if (file_name == NULL)
c7bde65d 717 return 0;
6bb934d6 718
9a83f860 719 wxSprintf(buf, wxT("%s.idx"), file_name);
6bb934d6 720
9a83f860 721 index_file = wxFopen(buf, wxT("r"));
6bb934d6 722 if (index_file == NULL)
c7bde65d 723 return 0;
6bb934d6 724
9a83f860 725 wxFscanf(index_file, wxT("%ld"), &nitems);
3013b6f4 726
c7bde65d
WS
727 for (int i = 0; i < nitems; i++)
728 {
9a83f860 729 wxFscanf(index_file, wxT("%ld"), &data);
3013b6f4 730 poem_index[i] = data;
c7bde65d 731 }
6bb934d6 732
c7bde65d 733 fclose(index_file);
3013b6f4 734
c7bde65d 735 return 1;
3013b6f4
JS
736}
737
738// Get index
739int GetIndex()
740{
c4b77901 741 int indexn = (int)(rand() % nitems);
3013b6f4
JS
742
743 if ((indexn < 0) || (indexn > nitems))
9a83f860 744 { PoetryError(wxT("No such poem!"));
3013b6f4
JS
745 return -1;
746 }
747 else
748 return indexn;
749}
750
751// Read preferences
c7bde65d 752void MainWindow::ReadPreferences()
3013b6f4 753{
cf63f3d3 754/* TODO: convert this code to use wxConfig
f37c24e0 755#if wxUSE_RESOURCES
9a83f860
VZ
756 wxGetResource(wxT("wxPoem"), wxT("FontSize"), &pointSize);
757 wxGetResource(wxT("wxPoem"), wxT("X"), &XPos);
758 wxGetResource(wxT("wxPoem"), wxT("Y"), &YPos);
f37c24e0 759#endif
cf63f3d3 760*/
3013b6f4
JS
761}
762
763// Write preferences to disk
c7bde65d 764void MainWindow::WritePreferences()
3013b6f4
JS
765{
766#ifdef __WXMSW__
c7bde65d 767 TheMainWindow->GetPosition(&XPos, &YPos);
cf63f3d3 768/* TODO: convert this code to use wxConfig
f37c24e0 769#if wxUSE_RESOURCES
9a83f860
VZ
770 wxWriteResource(wxT("wxPoem"), wxT("FontSize"), pointSize);
771 wxWriteResource(wxT("wxPoem"), wxT("X"), XPos);
772 wxWriteResource(wxT("wxPoem"), wxT("Y"), YPos);
f37c24e0 773#endif
cf63f3d3 774*/
f37c24e0 775#endif
3013b6f4
JS
776}
777
778// Load a poem from given file, at given point in file.
779// If position is > -1, use this for the position in the
780// file, otherwise use index[index_ptr] to find the correct position.
c2e5ea68 781bool LoadPoem(const wxChar *file_name, long position)
3013b6f4 782{
bbf73190
RR
783// int j = 0;
784// int indexn = 0;
6bb934d6 785 wxChar buf[100];
3013b6f4
JS
786 long data;
787 FILE *data_file;
788
17d31882 789 paging = false;
3013b6f4
JS
790 current_page = 0;
791
6bb934d6 792 if (file_name == NULL)
3013b6f4 793 {
9a83f860 794 wxSprintf(error_buf, wxT("Error in Poem loading."));
3013b6f4 795 PoetryError(error_buf);
17d31882 796 return false;
3013b6f4 797 }
6bb934d6 798
9a83f860
VZ
799 wxSprintf(buf, wxT("%s.dat"), file_name);
800 data_file = wxFopen(buf, wxT("r"));
6bb934d6
JS
801
802 if (data_file == NULL)
3013b6f4 803 {
9a83f860 804 wxSprintf(error_buf, wxT("Data file %s not found."), buf);
6bb934d6 805 PoetryError(error_buf);
17d31882 806 return false;
6bb934d6
JS
807 }
808
c7bde65d 809 if (position > -1)
3013b6f4 810 data = position;
c7bde65d 811 else
3013b6f4
JS
812 data = poem_index[index_ptr];
813
c7bde65d 814 fseek(data_file, data, SEEK_SET);
3013b6f4 815
c7bde65d
WS
816 int ch = 0;
817 int i = 0;
818 while ((ch != EOF) && (ch != '#'))
819 {
3013b6f4
JS
820 ch = getc(data_file);
821 // Add a linefeed so it will copy to the clipboard ok
822 if (ch == 10)
823 {
c7bde65d
WS
824 poem_buffer[i] = 13;
825 i++;
3013b6f4
JS
826 }
827
254a2129 828 poem_buffer[i] = (wxChar)ch;
3013b6f4
JS
829 i ++;
830
5045ca9e 831 if (i == BUFFER_SIZE)
3013b6f4 832 {
9a83f860 833 wxSprintf(error_buf, wxT("%s"), wxT("Poetry buffer exceeded."));
c7bde65d
WS
834 PoetryError(error_buf);
835 return false;
3013b6f4 836 }
c7bde65d
WS
837 }
838 fclose(data_file);
839 poem_buffer[i-1] = 0;
840 return true;
3013b6f4
JS
841}
842
843// Do the search
844long MainWindow::DoSearch(void)
845{
c7bde65d
WS
846 if (m_searchString.empty())
847 return false;
3013b6f4
JS
848
849 FILE *file;
3baa2662 850 size_t i = 0;
3013b6f4 851 int ch = 0;
6bb934d6 852 wxChar buf[100];
3013b6f4
JS
853 long find_start;
854 long previous_poem_start;
855
17d31882 856 bool found = false;
3baa2662 857 size_t search_length = m_searchString.length();
3013b6f4
JS
858
859 if (same_search)
860 {
c7bde65d
WS
861 find_start = last_find + 1;
862 previous_poem_start = last_poem_start;
3013b6f4
JS
863 }
864 else
865 {
c7bde65d
WS
866 find_start = 0;
867 last_poem_start = 0;
868 previous_poem_start = -1;
3013b6f4
JS
869 }
870
871 if (data_filename)
9a83f860 872 wxSprintf(buf, wxT("%s.dat"), data_filename);
3013b6f4 873
9a83f860 874 file = wxFopen(buf, wxT("r"));
6bb934d6 875 if (! (data_filename && file))
3013b6f4 876 {
9a83f860 877 wxSprintf(error_buf, wxT("Poetry data file %s not found\n"), buf);
c7bde65d
WS
878 PoetryError(error_buf);
879 return false;
3013b6f4
JS
880 }
881
882 fseek(file, find_start, SEEK_SET);
883
884 while ((ch != EOF) && !found)
885 {
886 ch = getc(file);
17d31882 887 ch = wxTolower(ch); // Make lower case
3013b6f4
JS
888
889 // Only match if we're looking at a different poem
890 // (no point in displaying the same poem again)
bd0b594d 891 if ((m_searchString[i] == ch) && (last_poem_start != previous_poem_start))
3013b6f4 892 {
c7bde65d
WS
893 if (i == 0)
894 last_find = ftell(file);
895 if (i == search_length-1)
896 found = true;
897 i ++;
3013b6f4
JS
898 }
899 else
c7bde65d
WS
900 {
901 i = 0;
902 }
3013b6f4
JS
903
904 if (ch == '#')
905 {
906 ch = getc(file);
c7bde65d 907 last_poem_start = ftell(file);
3013b6f4
JS
908 }
909 }
910 fclose(file);
911 if (ch == EOF)
c7bde65d
WS
912 {
913 last_find = -1;
914 }
3013b6f4
JS
915
916 if (found)
917 {
c7bde65d 918 return last_poem_start;
3013b6f4 919 }
c7bde65d
WS
920
921 return -1;
3013b6f4
JS
922}
923
924// Set up poetry filenames, preferences, load the index
925// Load index (or compile it if none found)
926void TryLoadIndex()
927{
c7bde65d
WS
928 index_ok = (LoadIndex(index_filename) != 0);
929 if (!index_ok || (nitems == 0))
930 {
9a83f860 931 PoetryError(wxT("Index file not found; will compile new one"), wxT("wxPoem"));
c7bde65d
WS
932 index_ok = Compile();
933 }
3013b6f4
JS
934}
935
936// Error message
5045ca9e 937void PoetryError(const wxChar *msg, const wxChar *caption)
3013b6f4 938{
c7bde65d 939 wxMessageBox(msg, caption, wxOK|wxICON_EXCLAMATION);
3013b6f4
JS
940}
941
942// Notification (change icon to something appropriate!)
5045ca9e 943void PoetryNotify(const wxChar *Msg, const wxChar *caption)
3013b6f4 944{
c7bde65d 945 wxMessageBox(Msg, caption, wxOK | wxICON_INFORMATION);
3013b6f4
JS
946}
947
948// Build up and save an index into the poetry data file, for
949// rapid random access
950bool Compile(void)
951{
952 FILE *file;
3013b6f4 953 int j;
c4b77901 954 int ch;
6bb934d6 955 wxChar buf[100];
3013b6f4
JS
956
957 if (data_filename)
9a83f860 958 wxSprintf(buf, wxT("%s.dat"), data_filename);
3013b6f4 959
9a83f860 960 file = wxFopen(buf, wxT("r"));
6bb934d6 961 if (! (data_filename && file))
3013b6f4 962 {
9a83f860 963 wxSprintf(error_buf, wxT("Poetry data file %s not found\n"), buf);
c7bde65d
WS
964 PoetryError(error_buf);
965 return false;
3013b6f4
JS
966 }
967
968 nitems = 0;
969
970 // Do first one (?)
971 poem_index[nitems] = 0;
972 nitems ++;
973
974 // Do rest
254a2129 975
c4b77901 976 do {
3013b6f4 977 ch = getc(file);
3013b6f4
JS
978 if (ch == '#')
979 {
980 ch = getc(file);
981 long data;
982 data = ftell(file);
983 poem_index[nitems] = data;
984 nitems ++;
985 }
c4b77901 986 } while (ch != EOF);
3013b6f4
JS
987 fclose(file);
988
989 if (index_filename)
9a83f860 990 wxSprintf(buf, wxT("%s.idx"), index_filename);
6bb934d6 991
9a83f860 992 file = wxFopen(buf, wxT("w"));
6bb934d6 993 if (! (data_filename && file))
3013b6f4 994 {
9a83f860 995 wxSprintf(error_buf, wxT("Poetry index file %s cannot be created\n"), buf);
c7bde65d
WS
996 PoetryError(error_buf);
997 return false;
3013b6f4
JS
998 }
999
9a83f860 1000 wxFprintf(file, wxT("%ld\n\n"), nitems);
3013b6f4 1001 for (j = 0; j < nitems; j++)
9a83f860 1002 wxFprintf(file, wxT("%ld\n"), poem_index[j]);
f5d01a1c 1003
3013b6f4 1004 fclose(file);
9a83f860 1005 PoetryNotify(wxT("Poetry index compiled."));
17d31882 1006 return true;
3013b6f4
JS
1007}
1008
508379ee 1009void MainWindow::OnPopup(wxCommandEvent& event)
3013b6f4 1010{
c7bde65d
WS
1011 switch (event.GetId())
1012 {
1013 case POEM_NEXT:
1014 // Another poem/page
1015 TheMainWindow->NextPage();
1016 break;
1017 case POEM_PREVIOUS:
1018 // Previous page
1019 TheMainWindow->PreviousPage();
1020 break;
1021 case POEM_SEARCH:
1022 // Search - with dialog
1023 TheMainWindow->Search(true);
1024 break;
1025 case POEM_NEXT_MATCH:
1026 // Search - without dialog (next match)
1027 TheMainWindow->Search(false);
1028 break;
1029 case POEM_MINIMIZE:
1030 TheMainWindow->Iconize(true);
1031 break;
17d31882 1032#if wxUSE_CLIPBOARD
c7bde65d
WS
1033 case POEM_COPY:
1034 wxTheClipboard->UsePrimarySelection();
1035 if (wxTheClipboard->Open())
1036 {
1037 static wxString s;
1038 s = poem_buffer;
9a83f860
VZ
1039 s.Replace( wxT("@P"),wxEmptyString);
1040 s.Replace( wxT("@A "),wxEmptyString);
1041 s.Replace( wxT("@A"),wxEmptyString);
1042 s.Replace( wxT("@T "),wxEmptyString);
1043 s.Replace( wxT("@T"),wxEmptyString);
c7bde65d
WS
1044 wxTextDataObject *data = new wxTextDataObject( s.c_str() );
1045 if (!wxTheClipboard->SetData( data ))
9a83f860 1046 wxMessageBox(wxT("Error while copying to the clipboard."));
c7bde65d
WS
1047 }
1048 else
1049 {
9a83f860 1050 wxMessageBox(wxT("Error opening the clipboard."));
c7bde65d
WS
1051 }
1052 wxTheClipboard->Close();
1053 break;
3013b6f4 1054#endif
c7bde65d
WS
1055 case POEM_BIGGER_TEXT:
1056 pointSize ++;
1057 CreateFonts();
1058 TheMainWindow->Resize();
1059 break;
1060 case POEM_SMALLER_TEXT:
1061 if (pointSize > 2)
1062 {
1063 pointSize --;
1064 CreateFonts();
1065 TheMainWindow->Resize();
1066 }
1067 break;
1068 case POEM_ABOUT:
9a83f860
VZ
1069 (void)wxMessageBox(wxT("wxPoem Version 1.1\nJulian Smart (c) 1995"),
1070 wxT("About wxPoem"), wxOK, TheMainWindow);
c7bde65d
WS
1071 break;
1072 case POEM_EXIT:
1073 // Exit
1074 TheMainWindow->Close(true);
1075 break;
1076 default:
1077 break;
1078 }
3013b6f4 1079}