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: wxpoem.cpp 35650 2005-09-23 12:56:45Z MR $
13// Copyright:   (c) 1998 Julian Smart
14// Licence:     wxWindows licence
15/////////////////////////////////////////////////////////////////////////////
16
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
25#include "wx/wx.h"
26#endif
27
28#include "wxpoem.h"
29
30#include "corner1.xpm"
31#include "corner2.xpm"
32#include "corner3.xpm"
33#include "corner4.xpm"
34#include "wxpoem.xpm"
35
36#define         buf_size 10000
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
51static wxChar   *poem_buffer;          // Storage for each poem
52static wxChar   line[150];                      // Storage for a line
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
58static bool     search_ok = false;              // Search was successful
59static bool     same_search = false;            // Searching on same string
60
61static long     poem_index[600];                     // Index of poem starts
62static long     nitems = 0;                     // Number of poems
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
70static wxChar   *index_filename = NULL;            // Index filename
71static wxChar   *data_filename = NULL;             // Data filename
72static wxChar   error_buf[300];                 // Error message buffer
73static bool     loaded_ok = false;              // Poem loaded ok
74static bool     index_ok = false;               // Index loaded ok
75
76static bool     paging = false;                 // Are we paging?
77static int      current_page = 0;               // Currently viewed page
78
79// Backing bitmap
80wxBitmap        *backingBitmap = NULL;
81
82void            PoetryError(wxChar *, wxChar *caption=_T("wxPoem Error"));
83void            PoetryNotify(wxChar *Msg, wxChar *caption=_T("wxPoem"));
84void            TryLoadIndex();
85bool            LoadPoem(wxChar *, long);
86int             GetIndex();
87int             LoadIndex(wxChar *);
88bool            Compile(void);
89void            FindMax(int *max_thing, int thing);
90
91#if wxUSE_CLIPBOARD
92    #include "wx/dataobj.h"
93    #include "wx/clipbrd.h"
94#endif
95
96#ifdef __WXWINCE__
97    STDAPI_(__int64) CeGetRandomSeed();
98#endif
99
100IMPLEMENT_APP(MyApp)
101
102MainWindow *TheMainWindow = NULL;
103
104// Create the fonts
105void MainWindow::CreateFonts()
106{
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);
110}
111
112BEGIN_EVENT_TABLE(MainWindow, wxFrame)
113    EVT_CLOSE(MainWindow::OnCloseWindow)
114    EVT_CHAR(MainWindow::OnChar)
115    EVT_MENU(wxID_ANY, MainWindow::OnPopup)
116END_EVENT_TABLE()
117
118MainWindow::MainWindow(wxFrame *frame, wxWindowID id, const wxString& title,
119     const wxPoint& pos, const wxSize& size, long style):
120     wxFrame(frame, id, title, pos, size, style)
121{
122    m_corners[0] = m_corners[1] = m_corners[2] = m_corners[3] = NULL;
123
124    ReadPreferences();
125    CreateFonts();
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    }
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;
155    int y = 0;
156    int j;
157    wxChar *line_ptr;
158    int curr_width = 0;
159    bool page_break = false;
160
161    int width = 0;
162    int height = 0;
163
164    if (DrawIt)
165    {
166        y = (*max_y - poem_height)/2;
167        width = *max_x;
168        height = *max_y;
169    }
170
171    if (DrawIt && wxColourDisplay())
172    {
173        dc->SetBrush(*wxLIGHT_GREY_BRUSH);
174        dc->SetPen(*wxGREY_PEN);
175        dc->DrawRectangle(0, 0, width, height);
176        dc->SetBackgroundMode(wxTRANSPARENT);
177    }
178
179    // See what ACTUAL char height is
180    if(m_normalFont)
181        dc->SetFont(*m_normalFont);
182    long xx;
183    long yy;
184    dc->GetTextExtent(_T("X"), &xx, &yy);
185    char_height = (int)yy;
186
187    if (current_page == 0)
188    {
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;
211    }
212
213    while (ch != 0 && !page_break)
214    {
215        j = 0;
216#if defined(__WXMSW__) || defined(__WXMAC__)
217        while (((ch = poem_buffer[i]) != 13) && (ch != 0))
218#else
219        while (((ch = poem_buffer[i]) != 10) && (ch != 0))
220#endif
221        {
222            line[j] = (wxChar)ch;
223            j ++;
224            i ++;
225        }
226
227#if defined(__WXMSW__) || defined(__WXMAC__)
228        if (ch == 13)
229#else
230        if (ch == 10)
231#endif
232        {
233            ch = -1;
234            i ++;
235#if defined(__WXMSW__) || defined(__WXMAC__)
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            {
242                line[j] = ' ';
243                j ++;
244                line[j] = 0;
245            }
246        }
247
248        if (j > 0)
249        {
250            line[j] = 0;
251            if (line[0] == '@')
252            {
253                switch (line[1])
254                {
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;
265                        m_title << _T(" (cont'd)");
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;
304                }
305            }
306            else
307            {
308                dc->SetFont(* m_normalFont);
309
310                dc->GetTextExtent(line, &xx, &yy);
311                FindMax(&curr_width, (int)xx);
312
313                if (DrawIt)
314                {
315                    int x = (int)((width - xx)/2.0);
316                    dc->SetFont(* m_normalFont);
317                    dc->SetTextForeground(* wxBLACK);
318                    dc->DrawText(line, x, y);
319                }
320            }
321        }
322        y += char_height;
323    }
324
325    // Write (cont'd)
326    if (page_break)
327    {
328        wxChar *cont = _T("(cont'd)");
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;
342    }
343
344    *max_x = (int)curr_width;
345    *max_y = (int)(y-char_height);
346
347    if (page_break)
348        pages[current_page+1] = i;
349    else
350        paging = false;
351
352    if (DrawIt)
353    {
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())
385            dc->SetPen(*wxWHITE_PEN);
386        else
387            dc->SetPen(*wxBLACK_PEN);
388
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
395        dc->SetPen(*wxBLACK_PEN);
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
402        dc->DrawIcon(* m_corners[0], 0, 0);
403        dc->DrawIcon(* m_corners[1], int(width-32), 0);
404
405        int y2 = height - 32;
406        int x2 = (width-32);
407        dc->DrawIcon(* m_corners[2], 0, y2);
408        dc->DrawIcon(* m_corners[3], x2, y2);
409    }
410}
411
412// Get an index (randomly generated) and load the poem
413void MainWindow::GetIndexLoadPoem(void)
414{
415    if (index_ok)
416        index_ptr = GetIndex();
417
418    if (index_ptr > -1)
419        loaded_ok = LoadPoem(data_filename, -1);
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
428    ScanBuffer(& dc, false, &poem_width, &poem_height);
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();
444    ScanBuffer(&memDC, true, &xx, &yy);
445}
446
447// Which is more?
448void FindMax(int *max_thing, int thing)
449{
450    if (thing > *max_thing)
451        *max_thing = thing;
452}
453
454// Next page/poem
455void MainWindow::NextPage(void)
456{
457    if (paging)
458        current_page ++;
459    else
460    {
461        current_page = 0;
462        GetIndexLoadPoem();
463    }
464    Resize();
465}
466
467// Previous page
468void MainWindow::PreviousPage(void)
469{
470    if (current_page > 0)
471    {
472        current_page --;
473        Resize();
474    }
475}
476
477// Search for a string
478void MainWindow::Search(bool ask)
479{
480    long position;
481
482    if (ask || m_searchString.empty())
483    {
484        wxString s = wxGetTextFromUser( _T("Enter search string"), _T("Search"), m_searchString);
485        if (!s.empty())
486        {
487            s.MakeLower();
488            m_searchString = s;
489            search_ok = true;
490        }
491        else
492        {
493            search_ok = false;
494        }
495    }
496    else
497    {
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;
513            PoetryNotify(_T("Search string not found."));
514        }
515    }
516}
517
518bool MyApp::OnInit()
519{
520    poem_buffer = new wxChar[buf_size];
521
522    // Seed the random number generator
523#ifdef __WXWINCE__
524    srand((unsigned) CeGetRandomSeed());
525#else
526    time_t current_time;
527
528    (void)time(&current_time);
529    srand((unsigned int)current_time);
530#endif
531
532//    randomize();
533    pages[0] = 0;
534
535    TheMainWindow = new MainWindow(NULL,
536                                   wxID_ANY,
537                                   _T("wxPoem"),
538                                   wxPoint(XPos, YPos),
539                                   wxDefaultSize,
540                                   wxCAPTION|wxMINIMIZE_BOX|wxSYSTEM_MENU|wxCLOSE_BOX|wxFULL_REPAINT_ON_RESIZE
541                                   );
542
543    TheMainWindow->canvas = new MyCanvas(TheMainWindow);
544
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    {
552        index_filename = _T(DEFAULT_POETRY_IND);
553        data_filename = _T(DEFAULT_POETRY_DAT);
554    }
555    TryLoadIndex();
556
557    TheMainWindow->GetIndexLoadPoem();
558    TheMainWindow->Resize();
559    TheMainWindow->Show(true);
560
561    return true;
562}
563
564int MyApp::OnExit()
565{
566    if (backingBitmap)
567        delete backingBitmap;
568
569    delete[] poem_buffer;
570
571    return 0;
572}
573
574void MainWindow::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
575{
576    WritePreferences();
577    this->Destroy();
578}
579
580void MainWindow::OnChar(wxKeyEvent& event)
581{
582    canvas->OnChar(event);
583}
584
585BEGIN_EVENT_TABLE(MyCanvas, wxWindow)
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
592MyCanvas::MyCanvas(wxFrame *frame):
593          wxWindow(frame, wxID_ANY)
594{
595    m_popupMenu = new wxMenu;
596    m_popupMenu->Append(POEM_NEXT, _T("Next poem/page"));
597    m_popupMenu->Append(POEM_PREVIOUS, _T("Previous page"));
598    m_popupMenu->AppendSeparator();
599    m_popupMenu->Append(POEM_SEARCH, _T("Search"));
600    m_popupMenu->Append(POEM_NEXT_MATCH, _T("Next match"));
601    m_popupMenu->Append(POEM_COPY, _T("Copy to clipboard"));
602    m_popupMenu->Append(POEM_MINIMIZE, _T("Minimize"));
603    m_popupMenu->AppendSeparator();
604    m_popupMenu->Append(POEM_BIGGER_TEXT, _T("Bigger text"));
605    m_popupMenu->Append(POEM_SMALLER_TEXT, _T("Smaller text"));
606    m_popupMenu->AppendSeparator();
607    m_popupMenu->Append(POEM_ABOUT, _T("About wxPoem"));
608    m_popupMenu->AppendSeparator();
609    m_popupMenu->Append(POEM_EXIT, _T("Exit"));
610}
611
612MyCanvas::~MyCanvas()
613{
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;
618}
619
620// Define the repainting behaviour
621void MyCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
622{
623    wxPaintDC dc(this);
624
625    if (backingBitmap)
626    {
627        int xx, yy;
628        TheMainWindow->GetClientSize(&xx, &yy);
629
630        dc.DrawBitmap(* backingBitmap, 0, 0);
631#if 0
632        wxMemoryDC memDC;
633        memDC.SelectObject(* backingBitmap);
634        dc.Blit(0, 0, backingBitmap->GetWidth(), backingBitmap->GetHeight(), &memDC, 0, 0);
635#endif
636  }
637}
638
639void MyCanvas::OnMouseEvent(wxMouseEvent& event)
640{
641    static int startPosX, startPosY, startFrameX, startFrameY;
642
643    long x, y;
644    event.GetPosition(&x, &y);
645
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);
670
671        int dX = x1 - startPosX;
672        int dY = y1 - startPosY;
673        GetParent()->Move(startFrameX + dX, startFrameY + dY);
674    }
675}
676
677// Process characters
678void MyCanvas::OnChar(wxKeyEvent& event)
679{
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}
707
708// Load index file
709int LoadIndex(wxChar *file_name)
710{
711    long data;
712    FILE *index_file;
713
714    wxChar buf[100];
715
716    if (file_name == NULL)
717        return 0;
718
719    wxSprintf(buf, _T("%s.idx"), file_name);
720
721    index_file = wxFopen(buf, _T("r"));
722    if (index_file == NULL)
723        return 0;
724
725    wxFscanf(index_file, _T("%ld"), &nitems);
726
727    for (int i = 0; i < nitems; i++)
728    {
729        wxFscanf(index_file, _T("%ld"), &data);
730        poem_index[i] = data;
731    }
732
733    fclose(index_file);
734
735    return 1;
736}
737
738// Get index
739int GetIndex()
740{
741    int indexn = (int)(rand() % nitems);
742
743    if ((indexn < 0) || (indexn > nitems))
744    { PoetryError(_T("No such poem!"));
745      return -1;
746    }
747    else
748      return indexn;
749}
750
751// Read preferences
752void MainWindow::ReadPreferences()
753{
754#if wxUSE_RESOURCES
755    wxGetResource(_T("wxPoem"), _T("FontSize"), &pointSize);
756    wxGetResource(_T("wxPoem"), _T("X"), &XPos);
757    wxGetResource(_T("wxPoem"), _T("Y"), &YPos);
758#endif
759}
760
761// Write preferences to disk
762void MainWindow::WritePreferences()
763{
764#ifdef __WXMSW__
765    TheMainWindow->GetPosition(&XPos, &YPos);
766#if wxUSE_RESOURCES
767    wxWriteResource(_T("wxPoem"), _T("FontSize"), pointSize);
768    wxWriteResource(_T("wxPoem"), _T("X"), XPos);
769    wxWriteResource(_T("wxPoem"), _T("Y"), YPos);
770#endif
771#endif
772}
773
774// Load a poem from given file, at given point in file.
775// If position is > -1, use this for the position in the
776// file, otherwise use index[index_ptr] to find the correct position.
777bool LoadPoem(wxChar *file_name, long position)
778{
779//    int j = 0;
780//    int indexn = 0;
781    wxChar buf[100];
782    long data;
783    FILE *data_file;
784
785    paging = false;
786    current_page = 0;
787
788    if (file_name == NULL)
789    {
790      wxSprintf(error_buf, _T("Error in Poem loading."));
791      PoetryError(error_buf);
792      return false;
793    }
794
795    wxSprintf(buf, _T("%s.dat"), file_name);
796    data_file = wxFopen(buf, _T("r"));
797
798    if (data_file == NULL)
799    {
800      wxSprintf(error_buf, _T("Data file %s not found."), buf);
801      PoetryError(error_buf);
802      return false;
803    }
804
805    if (position > -1)
806        data = position;
807    else
808        data = poem_index[index_ptr];
809
810    fseek(data_file, data, SEEK_SET);
811
812    int ch = 0;
813    int i = 0;
814    while ((ch != EOF) && (ch != '#'))
815    {
816        ch = getc(data_file);
817        // Add a linefeed so it will copy to the clipboard ok
818        if (ch == 10)
819        {
820            poem_buffer[i] = 13;
821            i++;
822        }
823
824        poem_buffer[i] = (wxChar)ch;
825        i ++;
826
827        if (i == buf_size)
828        {
829            wxSprintf(error_buf, _T("%s"), _T("Poetry buffer exceeded."));
830            PoetryError(error_buf);
831            return false;
832        }
833    }
834    fclose(data_file);
835    poem_buffer[i-1] = 0;
836    return true;
837}
838
839// Do the search
840long MainWindow::DoSearch(void)
841{
842    if (m_searchString.empty())
843        return false;
844
845    FILE *file;
846    size_t i = 0;
847    int ch = 0;
848    wxChar buf[100];
849    long find_start;
850    long previous_poem_start;
851
852    bool found = false;
853    size_t search_length = m_searchString.length();
854
855    if (same_search)
856    {
857        find_start = last_find + 1;
858        previous_poem_start = last_poem_start;
859    }
860    else
861    {
862        find_start = 0;
863        last_poem_start = 0;
864        previous_poem_start = -1;
865    }
866
867    if (data_filename)
868        wxSprintf(buf, _T("%s.dat"), data_filename);
869
870    file = wxFopen(buf, _T("r"));
871    if (! (data_filename && file))
872    {
873        wxSprintf(error_buf, _T("Poetry data file %s not found\n"), buf);
874        PoetryError(error_buf);
875        return false;
876    }
877
878    fseek(file, find_start, SEEK_SET);
879
880    while ((ch != EOF) && !found)
881    {
882        ch = getc(file);
883        ch = wxTolower(ch);   // Make lower case
884
885        // Only match if we're looking at a different poem
886        // (no point in displaying the same poem again)
887        if ((ch == m_searchString[i]) && (last_poem_start != previous_poem_start))
888        {
889            if (i == 0)
890                last_find = ftell(file);
891            if (i == search_length-1)
892                found = true;
893            i ++;
894        }
895        else
896        {
897            i = 0;
898        }
899
900        if (ch == '#')
901        {
902            ch = getc(file);
903            last_poem_start = ftell(file);
904        }
905    }
906    fclose(file);
907    if (ch == EOF)
908    {
909        last_find = -1;
910    }
911
912    if (found)
913    {
914        return last_poem_start;
915    }
916
917    return -1;
918}
919
920// Set up poetry filenames, preferences, load the index
921// Load index (or compile it if none found)
922void TryLoadIndex()
923{
924    index_ok = (LoadIndex(index_filename) != 0);
925    if (!index_ok || (nitems == 0))
926    {
927        PoetryError(_T("Index file not found; will compile new one"), _T("wxPoem"));
928        index_ok = Compile();
929    }
930}
931
932// Error message
933void PoetryError(wxChar *msg, wxChar *caption)
934{
935    wxMessageBox(msg, caption, wxOK|wxICON_EXCLAMATION);
936}
937
938// Notification (change icon to something appropriate!)
939void PoetryNotify(wxChar *Msg, wxChar *caption)
940{
941    wxMessageBox(Msg, caption, wxOK | wxICON_INFORMATION);
942}
943
944// Build up and save an index into the poetry data file, for
945// rapid random access
946bool Compile(void)
947{
948    FILE *file;
949    int j;
950    int ch;
951    wxChar buf[100];
952
953    if (data_filename)
954        wxSprintf(buf, _T("%s.dat"), data_filename);
955
956    file = wxFopen(buf, _T("r"));
957    if (! (data_filename && file))
958    {
959        wxSprintf(error_buf, _T("Poetry data file %s not found\n"), buf);
960        PoetryError(error_buf);
961        return false;
962    }
963
964    nitems = 0;
965
966    // Do first one (?)
967    poem_index[nitems] = 0;
968    nitems ++;
969
970    // Do rest
971
972    do {
973        ch = getc(file);
974        if (ch == '#')
975        {
976            ch = getc(file);
977            long data;
978            data = ftell(file);
979            poem_index[nitems] = data;
980            nitems ++;
981        }
982    } while (ch != EOF);
983    fclose(file);
984
985    if (index_filename)
986      wxSprintf(buf, _T("%s.idx"), index_filename);
987
988    file = wxFopen(buf, _T("w"));
989    if (! (data_filename && file))
990    {
991        wxSprintf(error_buf, _T("Poetry index file %s cannot be created\n"), buf);
992        PoetryError(error_buf);
993        return false;
994    }
995
996    wxFprintf(file, _T("%ld\n\n"), nitems);
997    for (j = 0; j < nitems; j++)
998        wxFprintf(file, _T("%ld\n"), poem_index[j]);
999
1000    fclose(file);
1001    PoetryNotify(_T("Poetry index compiled."));
1002    return true;
1003}
1004
1005void MainWindow::OnPopup(wxCommandEvent& event)
1006{
1007    switch (event.GetId())
1008    {
1009        case POEM_NEXT:
1010            // Another poem/page
1011            TheMainWindow->NextPage();
1012            break;
1013        case POEM_PREVIOUS:
1014            // Previous page
1015            TheMainWindow->PreviousPage();
1016            break;
1017        case POEM_SEARCH:
1018            // Search - with dialog
1019            TheMainWindow->Search(true);
1020            break;
1021        case POEM_NEXT_MATCH:
1022            // Search - without dialog (next match)
1023            TheMainWindow->Search(false);
1024            break;
1025        case POEM_MINIMIZE:
1026            TheMainWindow->Iconize(true);
1027            break;
1028#if wxUSE_CLIPBOARD
1029        case POEM_COPY:
1030            wxTheClipboard->UsePrimarySelection();
1031            if (wxTheClipboard->Open())
1032            {
1033                static wxString s;
1034                s = poem_buffer;
1035                s.Replace( _T("@P"),wxEmptyString);
1036                s.Replace( _T("@A "),wxEmptyString);
1037                s.Replace( _T("@A"),wxEmptyString);
1038                s.Replace( _T("@T "),wxEmptyString);
1039                s.Replace( _T("@T"),wxEmptyString);
1040                wxTextDataObject *data = new wxTextDataObject( s.c_str() );
1041                if (!wxTheClipboard->SetData( data ))
1042                    wxMessageBox(_T("Error while copying to the clipboard."));
1043            }
1044            else
1045            {
1046                wxMessageBox(_T("Error opening the clipboard."));
1047            }
1048            wxTheClipboard->Close();
1049            break;
1050#endif
1051        case POEM_BIGGER_TEXT:
1052            pointSize ++;
1053            CreateFonts();
1054            TheMainWindow->Resize();
1055            break;
1056        case POEM_SMALLER_TEXT:
1057            if (pointSize > 2)
1058            {
1059                pointSize --;
1060                CreateFonts();
1061                TheMainWindow->Resize();
1062            }
1063            break;
1064        case POEM_ABOUT:
1065            (void)wxMessageBox(_T("wxPoem Version 1.1\nJulian Smart (c) 1995"),
1066                               _T("About wxPoem"), wxOK, TheMainWindow);
1067            break;
1068        case POEM_EXIT:
1069            // Exit
1070            TheMainWindow->Close(true);
1071            break;
1072        default:
1073            break;
1074    }
1075}
1076