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