1079 lines
28 KiB
C++
1079 lines
28 KiB
C++
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
// Name: wxpoem.cpp
|
||
|
// Purpose: A small C++ program which displays a random poem on
|
||
|
// execution. It also allows search for poems containing a
|
||
|
// string.
|
||
|
// It requires winpoem.dat and creates winpoem.idx.
|
||
|
// Original version (WinPoem) written in 1994.
|
||
|
// This has not been rewritten in a long time so
|
||
|
// beware, inelegant code!
|
||
|
// Author: Julian Smart
|
||
|
// Created: 12/12/98
|
||
|
// Copyright: (c) 1998 Julian Smart
|
||
|
// Licence: wxWindows licence
|
||
|
/////////////////////////////////////////////////////////////////////////////
|
||
|
|
||
|
// For compilers that support precompilation, includes "wx.h".
|
||
|
#include "wx/wxprec.h"
|
||
|
|
||
|
#ifdef __BORLANDC__
|
||
|
#pragma hdrstop
|
||
|
#endif
|
||
|
|
||
|
#ifndef WX_PRECOMP
|
||
|
#include "wx/wx.h"
|
||
|
#endif
|
||
|
|
||
|
#include "wxpoem.h"
|
||
|
|
||
|
#include "corner1.xpm"
|
||
|
#include "corner2.xpm"
|
||
|
#include "corner3.xpm"
|
||
|
#include "corner4.xpm"
|
||
|
#include "wxpoem.xpm"
|
||
|
|
||
|
#define BUFFER_SIZE 10000
|
||
|
#define DEFAULT_POETRY_DAT "wxpoem"
|
||
|
#define DEFAULT_POETRY_IND "wxpoem"
|
||
|
#define DEFAULT_CHAR_HEIGHT 18
|
||
|
#define DEFAULT_FONT "Swiss"
|
||
|
#define DEFAULT_X_POS 0
|
||
|
#define DEFAULT_Y_POS 0
|
||
|
#define BORDER_SIZE 30
|
||
|
#define THIN_LINE_BORDER 10
|
||
|
#define THICK_LINE_BORDER 16
|
||
|
#define THICK_LINE_WIDTH 2
|
||
|
#define SHADOW_OFFSET 1
|
||
|
#define X_SIZE 30
|
||
|
#define Y_SIZE 20
|
||
|
|
||
|
static wxChar *poem_buffer; // Storage for each poem
|
||
|
static wxChar line[150]; // Storage for a line
|
||
|
static int pages[30]; // For multipage poems -
|
||
|
// store the start of each page
|
||
|
static long last_poem_start = 0; // Start of last found poem
|
||
|
static long last_find = -1; // Point in file of last found
|
||
|
// search string
|
||
|
static bool search_ok = false; // Search was successful
|
||
|
static bool same_search = false; // Searching on same string
|
||
|
|
||
|
static long poem_index[600]; // Index of poem starts
|
||
|
static long nitems = 0; // Number of poems
|
||
|
static int char_height = DEFAULT_CHAR_HEIGHT; // Actual height
|
||
|
static int index_ptr = -1; // Pointer into index
|
||
|
static int poem_height, poem_width; // Size of poem
|
||
|
static int XPos; // Startup X position
|
||
|
static int YPos; // Startup Y position
|
||
|
static int pointSize = 12; // Font size
|
||
|
|
||
|
static const wxChar *index_filename = NULL; // Index filename
|
||
|
static const wxChar *data_filename = NULL; // Data filename
|
||
|
static wxChar error_buf[300]; // Error message buffer
|
||
|
static bool loaded_ok = false; // Poem loaded ok
|
||
|
static bool index_ok = false; // Index loaded ok
|
||
|
|
||
|
static bool paging = false; // Are we paging?
|
||
|
static int current_page = 0; // Currently viewed page
|
||
|
|
||
|
// Backing bitmap
|
||
|
wxBitmap *backingBitmap = NULL;
|
||
|
|
||
|
void PoetryError(const wxChar *, const wxChar *caption=wxT("wxPoem Error"));
|
||
|
void PoetryNotify(const wxChar *Msg, const wxChar *caption=wxT("wxPoem"));
|
||
|
void TryLoadIndex();
|
||
|
bool LoadPoem(const wxChar *, long);
|
||
|
int GetIndex();
|
||
|
int LoadIndex(const wxChar *);
|
||
|
bool Compile(void);
|
||
|
void FindMax(int *max_thing, int thing);
|
||
|
|
||
|
#if wxUSE_CLIPBOARD
|
||
|
#include "wx/dataobj.h"
|
||
|
#include "wx/clipbrd.h"
|
||
|
#endif
|
||
|
|
||
|
#ifdef __WXWINCE__
|
||
|
STDAPI_(__int64) CeGetRandomSeed();
|
||
|
#endif
|
||
|
|
||
|
IMPLEMENT_APP(MyApp)
|
||
|
|
||
|
MainWindow *TheMainWindow = NULL;
|
||
|
|
||
|
// Create the fonts
|
||
|
void MainWindow::CreateFonts()
|
||
|
{
|
||
|
m_normalFont = wxTheFontList->FindOrCreateFont(pointSize, wxSWISS, wxNORMAL, wxNORMAL);
|
||
|
m_boldFont = wxTheFontList->FindOrCreateFont(pointSize, wxSWISS, wxNORMAL, wxBOLD);
|
||
|
m_italicFont = wxTheFontList->FindOrCreateFont(pointSize, wxSWISS, wxITALIC, wxNORMAL);
|
||
|
}
|
||
|
|
||
|
BEGIN_EVENT_TABLE(MainWindow, wxFrame)
|
||
|
EVT_CLOSE(MainWindow::OnCloseWindow)
|
||
|
EVT_CHAR(MainWindow::OnChar)
|
||
|
EVT_MENU(wxID_ANY, MainWindow::OnPopup)
|
||
|
END_EVENT_TABLE()
|
||
|
|
||
|
MainWindow::MainWindow(wxFrame *frame, wxWindowID id, const wxString& title,
|
||
|
const wxPoint& pos, const wxSize& size, long style):
|
||
|
wxFrame(frame, id, title, pos, size, style)
|
||
|
{
|
||
|
m_corners[0] = m_corners[1] = m_corners[2] = m_corners[3] = NULL;
|
||
|
|
||
|
ReadPreferences();
|
||
|
CreateFonts();
|
||
|
|
||
|
SetIcon(wxpoem_xpm);
|
||
|
|
||
|
m_corners[0] = new wxIcon( corner1_xpm );
|
||
|
m_corners[1] = new wxIcon( corner2_xpm );
|
||
|
m_corners[2] = new wxIcon( corner3_xpm );
|
||
|
m_corners[3] = new wxIcon( corner4_xpm );
|
||
|
}
|
||
|
|
||
|
MainWindow::~MainWindow()
|
||
|
{
|
||
|
for (int i=0;i<4;i++)
|
||
|
{
|
||
|
if(m_corners[i])
|
||
|
{
|
||
|
delete m_corners[i];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read the poetry buffer, either for finding the size
|
||
|
// or for writing to a bitmap (not to the window directly,
|
||
|
// since that displays messily)
|
||
|
// If DrawIt is true, we draw, otherwise we just determine the
|
||
|
// size the window should be.
|
||
|
void MainWindow::ScanBuffer(wxDC *dc, bool DrawIt, int *max_x, int *max_y)
|
||
|
{
|
||
|
int i = pages[current_page];
|
||
|
int ch = -1;
|
||
|
int y = 0;
|
||
|
int j;
|
||
|
wxChar *line_ptr;
|
||
|
int curr_width = 0;
|
||
|
bool page_break = false;
|
||
|
|
||
|
int width = 0;
|
||
|
int height = 0;
|
||
|
|
||
|
if (DrawIt)
|
||
|
{
|
||
|
y = (*max_y - poem_height)/2;
|
||
|
width = *max_x;
|
||
|
height = *max_y;
|
||
|
}
|
||
|
|
||
|
if (DrawIt && wxColourDisplay())
|
||
|
{
|
||
|
dc->SetBrush(*wxLIGHT_GREY_BRUSH);
|
||
|
dc->SetPen(*wxGREY_PEN);
|
||
|
dc->DrawRectangle(0, 0, width, height);
|
||
|
dc->SetBackgroundMode(wxTRANSPARENT);
|
||
|
}
|
||
|
|
||
|
// See what ACTUAL char height is
|
||
|
if(m_normalFont)
|
||
|
dc->SetFont(*m_normalFont);
|
||
|
wxCoord xx;
|
||
|
wxCoord yy;
|
||
|
dc->GetTextExtent(wxT("X"), &xx, &yy);
|
||
|
char_height = (int)yy;
|
||
|
|
||
|
if (current_page == 0)
|
||
|
{
|
||
|
m_title = wxEmptyString;
|
||
|
}
|
||
|
else if (!m_title.empty())
|
||
|
{
|
||
|
dc->SetFont(* m_boldFont);
|
||
|
dc->GetTextExtent(m_title, &xx, &yy);
|
||
|
FindMax(&curr_width, (int)xx);
|
||
|
|
||
|
if (DrawIt)
|
||
|
{
|
||
|
int x = (width - xx)/2;
|
||
|
dc->SetFont(* m_boldFont);
|
||
|
|
||
|
// Change text to BLACK!
|
||
|
dc->SetTextForeground(* wxBLACK);
|
||
|
dc->DrawText(m_title, x, y);
|
||
|
// Change text to WHITE!
|
||
|
dc->SetTextForeground(* wxWHITE);
|
||
|
dc->DrawText(m_title, x-SHADOW_OFFSET, y-SHADOW_OFFSET);
|
||
|
}
|
||
|
y += char_height;
|
||
|
y += char_height;
|
||
|
}
|
||
|
|
||
|
while (ch != 0 && !page_break)
|
||
|
{
|
||
|
j = 0;
|
||
|
#if defined(__WXMSW__) || defined(__WXMAC__)
|
||
|
while (((ch = poem_buffer[i]) != 13) && (ch != 0))
|
||
|
#else
|
||
|
while (((ch = poem_buffer[i]) != 10) && (ch != 0))
|
||
|
#endif
|
||
|
{
|
||
|
line[j] = (wxChar)ch;
|
||
|
j ++;
|
||
|
i ++;
|
||
|
}
|
||
|
|
||
|
#if defined(__WXMSW__) || defined(__WXMAC__)
|
||
|
if (ch == 13)
|
||
|
#else
|
||
|
if (ch == 10)
|
||
|
#endif
|
||
|
{
|
||
|
ch = -1;
|
||
|
i ++;
|
||
|
#if defined(__WXMSW__) || defined(__WXMAC__)
|
||
|
// Add another to skip the linefeed
|
||
|
i ++;
|
||
|
#endif
|
||
|
// If a single newline on its own, put a space in
|
||
|
if (j == 0)
|
||
|
{
|
||
|
line[j] = ' ';
|
||
|
j ++;
|
||
|
line[j] = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (j > 0)
|
||
|
{
|
||
|
line[j] = 0;
|
||
|
if (line[0] == '@')
|
||
|
{
|
||
|
switch (line[1])
|
||
|
{
|
||
|
case 'P':
|
||
|
paging = true;
|
||
|
page_break = true;
|
||
|
break;
|
||
|
|
||
|
case 'T':
|
||
|
dc->SetFont(* m_boldFont);
|
||
|
line_ptr = line+3;
|
||
|
|
||
|
m_title = line_ptr;
|
||
|
m_title << wxT(" (cont'd)");
|
||
|
|
||
|
dc->GetTextExtent(line_ptr, &xx, &yy);
|
||
|
FindMax(&curr_width, (int)xx);
|
||
|
|
||
|
if (DrawIt)
|
||
|
{
|
||
|
int x = (width - xx)/2;
|
||
|
dc->SetFont(* m_boldFont);
|
||
|
|
||
|
// Change text to BLACK!
|
||
|
dc->SetTextForeground(* wxBLACK);
|
||
|
dc->DrawText(line_ptr, x, y);
|
||
|
|
||
|
// Change text to WHITE!
|
||
|
dc->SetTextForeground(* wxWHITE);
|
||
|
dc->DrawText(line_ptr, x-SHADOW_OFFSET, y-SHADOW_OFFSET);
|
||
|
dc->SetTextForeground(* wxWHITE);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'A':
|
||
|
line_ptr = line+3;
|
||
|
dc->SetFont(* m_italicFont);
|
||
|
|
||
|
dc->GetTextExtent(line_ptr, &xx, &yy);
|
||
|
FindMax(&curr_width, (int)xx);
|
||
|
|
||
|
if (DrawIt)
|
||
|
{
|
||
|
int x = (width - xx)/2;
|
||
|
dc->SetTextForeground(* wxBLACK);
|
||
|
dc->DrawText(line_ptr, x, y);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// Default: just ignore this line
|
||
|
default:
|
||
|
y -= char_height;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dc->SetFont(* m_normalFont);
|
||
|
|
||
|
dc->GetTextExtent(line, &xx, &yy);
|
||
|
FindMax(&curr_width, (int)xx);
|
||
|
|
||
|
if (DrawIt)
|
||
|
{
|
||
|
int x = (int)((width - xx)/2.0);
|
||
|
dc->SetFont(* m_normalFont);
|
||
|
dc->SetTextForeground(* wxBLACK);
|
||
|
dc->DrawText(line, x, y);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
y += char_height;
|
||
|
}
|
||
|
|
||
|
// Write (cont'd)
|
||
|
if (page_break)
|
||
|
{
|
||
|
const wxChar *cont = wxT("(cont'd)");
|
||
|
|
||
|
dc->SetFont(* m_normalFont);
|
||
|
|
||
|
dc->GetTextExtent(cont, &xx, &yy);
|
||
|
FindMax(&curr_width, (int)xx);
|
||
|
if (DrawIt)
|
||
|
{
|
||
|
int x = (int)((width - xx)/2.0);
|
||
|
dc->SetFont(* m_normalFont);
|
||
|
dc->SetTextForeground(* wxBLACK);
|
||
|
dc->DrawText(cont, x, y);
|
||
|
}
|
||
|
y += 2*char_height;
|
||
|
}
|
||
|
|
||
|
*max_x = (int)curr_width;
|
||
|
*max_y = (int)(y-char_height);
|
||
|
|
||
|
if (page_break)
|
||
|
pages[current_page+1] = i;
|
||
|
else
|
||
|
paging = false;
|
||
|
|
||
|
if (DrawIt)
|
||
|
{
|
||
|
// Draw dark grey thick border
|
||
|
if (wxColourDisplay())
|
||
|
{
|
||
|
dc->SetBrush(*wxGREY_BRUSH);
|
||
|
dc->SetPen(*wxGREY_PEN);
|
||
|
|
||
|
// Left side
|
||
|
dc->DrawRectangle(0, 0, THIN_LINE_BORDER, height);
|
||
|
// Top side
|
||
|
dc->DrawRectangle(THIN_LINE_BORDER, 0, width-THIN_LINE_BORDER, THIN_LINE_BORDER);
|
||
|
// Right side
|
||
|
dc->DrawRectangle(width-THIN_LINE_BORDER, THIN_LINE_BORDER, width, height-THIN_LINE_BORDER);
|
||
|
// Bottom side
|
||
|
dc->DrawRectangle(THIN_LINE_BORDER, height-THIN_LINE_BORDER, width-THIN_LINE_BORDER, height);
|
||
|
}
|
||
|
// Draw border
|
||
|
// Have grey background, plus 3-d border -
|
||
|
// One black rectangle.
|
||
|
// Inside this, left and top sides - dark grey. Bottom and right -
|
||
|
// white.
|
||
|
|
||
|
// Change pen to black
|
||
|
dc->SetPen(*wxBLACK_PEN);
|
||
|
dc->DrawLine(THIN_LINE_BORDER, THIN_LINE_BORDER, width-THIN_LINE_BORDER, THIN_LINE_BORDER);
|
||
|
dc->DrawLine(width-THIN_LINE_BORDER, THIN_LINE_BORDER, width-THIN_LINE_BORDER, height-THIN_LINE_BORDER);
|
||
|
dc->DrawLine(width-THIN_LINE_BORDER, height-THIN_LINE_BORDER, THIN_LINE_BORDER, height-THIN_LINE_BORDER);
|
||
|
dc->DrawLine(THIN_LINE_BORDER, height-THIN_LINE_BORDER, THIN_LINE_BORDER, THIN_LINE_BORDER);
|
||
|
|
||
|
// Right and bottom white lines - 'grey' (black!) if
|
||
|
// we're running on a mono display.
|
||
|
if (wxColourDisplay())
|
||
|
dc->SetPen(*wxWHITE_PEN);
|
||
|
else
|
||
|
dc->SetPen(*wxBLACK_PEN);
|
||
|
|
||
|
dc->DrawLine(width-THICK_LINE_BORDER, THICK_LINE_BORDER,
|
||
|
width-THICK_LINE_BORDER, height-THICK_LINE_BORDER);
|
||
|
dc->DrawLine(width-THICK_LINE_BORDER, height-THICK_LINE_BORDER,
|
||
|
THICK_LINE_BORDER, height-THICK_LINE_BORDER);
|
||
|
|
||
|
// Left and top grey lines
|
||
|
dc->SetPen(*wxBLACK_PEN);
|
||
|
dc->DrawLine(THICK_LINE_BORDER, height-THICK_LINE_BORDER,
|
||
|
THICK_LINE_BORDER, THICK_LINE_BORDER);
|
||
|
dc->DrawLine(THICK_LINE_BORDER, THICK_LINE_BORDER,
|
||
|
width-THICK_LINE_BORDER, THICK_LINE_BORDER);
|
||
|
|
||
|
// Draw icons
|
||
|
dc->DrawIcon(* m_corners[0], 0, 0);
|
||
|
dc->DrawIcon(* m_corners[1], int(width-32), 0);
|
||
|
|
||
|
int y2 = height - 32;
|
||
|
int x2 = (width-32);
|
||
|
dc->DrawIcon(* m_corners[2], 0, y2);
|
||
|
dc->DrawIcon(* m_corners[3], x2, y2);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Get an index (randomly generated) and load the poem
|
||
|
void MainWindow::GetIndexLoadPoem(void)
|
||
|
{
|
||
|
if (index_ok)
|
||
|
index_ptr = GetIndex();
|
||
|
|
||
|
if (index_ptr > -1)
|
||
|
loaded_ok = LoadPoem(data_filename, -1);
|
||
|
}
|
||
|
|
||
|
// Find the size of the poem and resize the window accordingly
|
||
|
void MainWindow::Resize(void)
|
||
|
{
|
||
|
wxClientDC dc(canvas);
|
||
|
|
||
|
// Get the poem size
|
||
|
ScanBuffer(& dc, false, &poem_width, &poem_height);
|
||
|
int x = poem_width + (2*BORDER_SIZE);
|
||
|
int y = poem_height + (2*BORDER_SIZE);
|
||
|
|
||
|
SetClientSize(x, y);
|
||
|
|
||
|
// In case client size isn't what we set it to...
|
||
|
int xx, yy;
|
||
|
GetClientSize(&xx, &yy);
|
||
|
|
||
|
wxMemoryDC memDC;
|
||
|
if (backingBitmap) delete backingBitmap;
|
||
|
backingBitmap = new wxBitmap(x, yy);
|
||
|
memDC.SelectObject(* backingBitmap);
|
||
|
|
||
|
memDC.Clear();
|
||
|
ScanBuffer(&memDC, true, &xx, &yy);
|
||
|
}
|
||
|
|
||
|
// Which is more?
|
||
|
void FindMax(int *max_thing, int thing)
|
||
|
{
|
||
|
if (thing > *max_thing)
|
||
|
*max_thing = thing;
|
||
|
}
|
||
|
|
||
|
// Next page/poem
|
||
|
void MainWindow::NextPage(void)
|
||
|
{
|
||
|
if (paging)
|
||
|
current_page ++;
|
||
|
else
|
||
|
{
|
||
|
current_page = 0;
|
||
|
GetIndexLoadPoem();
|
||
|
}
|
||
|
Resize();
|
||
|
}
|
||
|
|
||
|
// Previous page
|
||
|
void MainWindow::PreviousPage(void)
|
||
|
{
|
||
|
if (current_page > 0)
|
||
|
{
|
||
|
current_page --;
|
||
|
Resize();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Search for a string
|
||
|
void MainWindow::Search(bool ask)
|
||
|
{
|
||
|
long position;
|
||
|
|
||
|
if (ask || m_searchString.empty())
|
||
|
{
|
||
|
wxString s = wxGetTextFromUser( wxT("Enter search string"), wxT("Search"), m_searchString);
|
||
|
if (!s.empty())
|
||
|
{
|
||
|
s.MakeLower();
|
||
|
m_searchString = s;
|
||
|
search_ok = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
search_ok = false;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
same_search = true;
|
||
|
search_ok = true;
|
||
|
}
|
||
|
|
||
|
if (!m_searchString.empty() && search_ok)
|
||
|
{
|
||
|
position = DoSearch();
|
||
|
if (position > -1)
|
||
|
{
|
||
|
loaded_ok = LoadPoem(data_filename, position);
|
||
|
Resize();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
last_poem_start = 0;
|
||
|
PoetryNotify(wxT("Search string not found."));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool MyApp::OnInit()
|
||
|
{
|
||
|
poem_buffer = new wxChar[BUFFER_SIZE];
|
||
|
|
||
|
// Seed the random number generator
|
||
|
#ifdef __WXWINCE__
|
||
|
srand((unsigned) CeGetRandomSeed());
|
||
|
#else
|
||
|
time_t current_time;
|
||
|
|
||
|
(void)time(¤t_time);
|
||
|
srand((unsigned int)current_time);
|
||
|
#endif
|
||
|
|
||
|
// randomize();
|
||
|
pages[0] = 0;
|
||
|
|
||
|
TheMainWindow = new MainWindow(NULL,
|
||
|
wxID_ANY,
|
||
|
wxT("wxPoem"),
|
||
|
wxPoint(XPos, YPos),
|
||
|
wxDefaultSize,
|
||
|
wxCAPTION|wxMINIMIZE_BOX|wxSYSTEM_MENU|wxCLOSE_BOX|wxFULL_REPAINT_ON_RESIZE
|
||
|
);
|
||
|
|
||
|
TheMainWindow->canvas = new MyCanvas(TheMainWindow);
|
||
|
|
||
|
if (argc > 1)
|
||
|
{
|
||
|
index_filename = wxStrcpy(new wxChar[wxStrlen(argv[1]) + 1], argv[1]);
|
||
|
data_filename = wxStrcpy(new wxChar[wxStrlen(argv[1]) + 1], argv[1]);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
index_filename = wxT(DEFAULT_POETRY_IND);
|
||
|
data_filename = wxT(DEFAULT_POETRY_DAT);
|
||
|
}
|
||
|
TryLoadIndex();
|
||
|
|
||
|
TheMainWindow->GetIndexLoadPoem();
|
||
|
TheMainWindow->Resize();
|
||
|
TheMainWindow->Show(true);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int MyApp::OnExit()
|
||
|
{
|
||
|
if (backingBitmap)
|
||
|
delete backingBitmap;
|
||
|
|
||
|
delete[] poem_buffer;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void MainWindow::OnCloseWindow(wxCloseEvent& WXUNUSED(event))
|
||
|
{
|
||
|
WritePreferences();
|
||
|
this->Destroy();
|
||
|
}
|
||
|
|
||
|
void MainWindow::OnChar(wxKeyEvent& event)
|
||
|
{
|
||
|
canvas->OnChar(event);
|
||
|
}
|
||
|
|
||
|
BEGIN_EVENT_TABLE(MyCanvas, wxWindow)
|
||
|
EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
|
||
|
EVT_CHAR(MyCanvas::OnChar)
|
||
|
EVT_PAINT(MyCanvas::OnPaint)
|
||
|
END_EVENT_TABLE()
|
||
|
|
||
|
// Define a constructor for my canvas
|
||
|
MyCanvas::MyCanvas(wxFrame *frame):
|
||
|
wxWindow(frame, wxID_ANY)
|
||
|
{
|
||
|
m_popupMenu = new wxMenu;
|
||
|
m_popupMenu->Append(POEM_NEXT, wxT("Next poem/page"));
|
||
|
m_popupMenu->Append(POEM_PREVIOUS, wxT("Previous page"));
|
||
|
m_popupMenu->AppendSeparator();
|
||
|
m_popupMenu->Append(POEM_SEARCH, wxT("Search"));
|
||
|
m_popupMenu->Append(POEM_NEXT_MATCH, wxT("Next match"));
|
||
|
m_popupMenu->Append(POEM_COPY, wxT("Copy to clipboard"));
|
||
|
m_popupMenu->Append(POEM_MINIMIZE, wxT("Minimize"));
|
||
|
m_popupMenu->AppendSeparator();
|
||
|
m_popupMenu->Append(POEM_BIGGER_TEXT, wxT("Bigger text"));
|
||
|
m_popupMenu->Append(POEM_SMALLER_TEXT, wxT("Smaller text"));
|
||
|
m_popupMenu->AppendSeparator();
|
||
|
m_popupMenu->Append(POEM_ABOUT, wxT("About wxPoem"));
|
||
|
m_popupMenu->AppendSeparator();
|
||
|
m_popupMenu->Append(POEM_EXIT, wxT("Exit"));
|
||
|
}
|
||
|
|
||
|
MyCanvas::~MyCanvas()
|
||
|
{
|
||
|
// Note: this must be done before the main window/canvas are destroyed
|
||
|
// or we get an error (no parent window for menu item button)
|
||
|
delete m_popupMenu;
|
||
|
m_popupMenu = NULL;
|
||
|
}
|
||
|
|
||
|
// Define the repainting behaviour
|
||
|
void MyCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
|
||
|
{
|
||
|
wxPaintDC dc(this);
|
||
|
|
||
|
if (backingBitmap)
|
||
|
{
|
||
|
int xx, yy;
|
||
|
TheMainWindow->GetClientSize(&xx, &yy);
|
||
|
|
||
|
dc.DrawBitmap(* backingBitmap, 0, 0);
|
||
|
#if 0
|
||
|
wxMemoryDC memDC;
|
||
|
memDC.SelectObject(* backingBitmap);
|
||
|
dc.Blit(0, 0, backingBitmap->GetWidth(), backingBitmap->GetHeight(), &memDC, 0, 0);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void MyCanvas::OnMouseEvent(wxMouseEvent& event)
|
||
|
{
|
||
|
static int startPosX, startPosY, startFrameX, startFrameY;
|
||
|
|
||
|
long x, y;
|
||
|
event.GetPosition(&x, &y);
|
||
|
|
||
|
if (event.RightDown())
|
||
|
{
|
||
|
// Versions from wxWin 1.67 are probably OK
|
||
|
PopupMenu(m_popupMenu, (int)x, (int)y );
|
||
|
}
|
||
|
else if (event.LeftDown())
|
||
|
{
|
||
|
this->CaptureMouse();
|
||
|
int x1 = (int)x;
|
||
|
int y1 = (int)y;
|
||
|
ClientToScreen(&x1, &y1);
|
||
|
startPosX = x1;
|
||
|
startPosY = y1;
|
||
|
GetParent()->GetPosition(&startFrameX, &startFrameY);
|
||
|
}
|
||
|
else if (event.LeftUp())
|
||
|
{
|
||
|
if (GetCapture() == this) this->ReleaseMouse();
|
||
|
}
|
||
|
else if (event.Dragging() && event.LeftIsDown())
|
||
|
{
|
||
|
int x1 = (int)x;
|
||
|
int y1 = (int)y;
|
||
|
ClientToScreen(&x1, &y1);
|
||
|
|
||
|
int dX = x1 - startPosX;
|
||
|
int dY = y1 - startPosY;
|
||
|
GetParent()->Move(startFrameX + dX, startFrameY + dY);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Process characters
|
||
|
void MyCanvas::OnChar(wxKeyEvent& event)
|
||
|
{
|
||
|
switch (event.GetKeyCode())
|
||
|
{
|
||
|
case 'n':
|
||
|
case 'N':
|
||
|
// Next match
|
||
|
TheMainWindow->Search(false);
|
||
|
break;
|
||
|
|
||
|
case 's':
|
||
|
case 'S':
|
||
|
// New search
|
||
|
TheMainWindow->Search(true);
|
||
|
break;
|
||
|
|
||
|
case WXK_SPACE:
|
||
|
case WXK_RIGHT:
|
||
|
case WXK_DOWN:
|
||
|
// Another poem
|
||
|
TheMainWindow->NextPage();
|
||
|
break;
|
||
|
|
||
|
case WXK_ESCAPE:
|
||
|
TheMainWindow->Close(true);
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Load index file
|
||
|
int LoadIndex(const wxChar *file_name)
|
||
|
{
|
||
|
long data;
|
||
|
FILE *index_file;
|
||
|
|
||
|
wxChar buf[100];
|
||
|
|
||
|
if (file_name == NULL)
|
||
|
return 0;
|
||
|
|
||
|
wxSprintf(buf, wxT("%s.idx"), file_name);
|
||
|
|
||
|
index_file = wxFopen(buf, wxT("r"));
|
||
|
if (index_file == NULL)
|
||
|
return 0;
|
||
|
|
||
|
wxFscanf(index_file, wxT("%ld"), &nitems);
|
||
|
|
||
|
for (int i = 0; i < nitems; i++)
|
||
|
{
|
||
|
wxFscanf(index_file, wxT("%ld"), &data);
|
||
|
poem_index[i] = data;
|
||
|
}
|
||
|
|
||
|
fclose(index_file);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// Get index
|
||
|
int GetIndex()
|
||
|
{
|
||
|
int indexn = (int)(rand() % nitems);
|
||
|
|
||
|
if ((indexn < 0) || (indexn > nitems))
|
||
|
{ PoetryError(wxT("No such poem!"));
|
||
|
return -1;
|
||
|
}
|
||
|
else
|
||
|
return indexn;
|
||
|
}
|
||
|
|
||
|
// Read preferences
|
||
|
void MainWindow::ReadPreferences()
|
||
|
{
|
||
|
/* TODO: convert this code to use wxConfig
|
||
|
#if wxUSE_RESOURCES
|
||
|
wxGetResource(wxT("wxPoem"), wxT("FontSize"), &pointSize);
|
||
|
wxGetResource(wxT("wxPoem"), wxT("X"), &XPos);
|
||
|
wxGetResource(wxT("wxPoem"), wxT("Y"), &YPos);
|
||
|
#endif
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
// Write preferences to disk
|
||
|
void MainWindow::WritePreferences()
|
||
|
{
|
||
|
#ifdef __WXMSW__
|
||
|
TheMainWindow->GetPosition(&XPos, &YPos);
|
||
|
/* TODO: convert this code to use wxConfig
|
||
|
#if wxUSE_RESOURCES
|
||
|
wxWriteResource(wxT("wxPoem"), wxT("FontSize"), pointSize);
|
||
|
wxWriteResource(wxT("wxPoem"), wxT("X"), XPos);
|
||
|
wxWriteResource(wxT("wxPoem"), wxT("Y"), YPos);
|
||
|
#endif
|
||
|
*/
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
// Load a poem from given file, at given point in file.
|
||
|
// If position is > -1, use this for the position in the
|
||
|
// file, otherwise use index[index_ptr] to find the correct position.
|
||
|
bool LoadPoem(const wxChar *file_name, long position)
|
||
|
{
|
||
|
// int j = 0;
|
||
|
// int indexn = 0;
|
||
|
wxChar buf[100];
|
||
|
long data;
|
||
|
FILE *data_file;
|
||
|
|
||
|
paging = false;
|
||
|
current_page = 0;
|
||
|
|
||
|
if (file_name == NULL)
|
||
|
{
|
||
|
wxSprintf(error_buf, wxT("Error in Poem loading."));
|
||
|
PoetryError(error_buf);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
wxSprintf(buf, wxT("%s.dat"), file_name);
|
||
|
data_file = wxFopen(buf, wxT("r"));
|
||
|
|
||
|
if (data_file == NULL)
|
||
|
{
|
||
|
wxSprintf(error_buf, wxT("Data file %s not found."), buf);
|
||
|
PoetryError(error_buf);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (position > -1)
|
||
|
data = position;
|
||
|
else
|
||
|
data = poem_index[index_ptr];
|
||
|
|
||
|
fseek(data_file, data, SEEK_SET);
|
||
|
|
||
|
int ch = 0;
|
||
|
int i = 0;
|
||
|
while ((ch != EOF) && (ch != '#'))
|
||
|
{
|
||
|
ch = getc(data_file);
|
||
|
// Add a linefeed so it will copy to the clipboard ok
|
||
|
if (ch == 10)
|
||
|
{
|
||
|
poem_buffer[i] = 13;
|
||
|
i++;
|
||
|
}
|
||
|
|
||
|
poem_buffer[i] = (wxChar)ch;
|
||
|
i ++;
|
||
|
|
||
|
if (i == BUFFER_SIZE)
|
||
|
{
|
||
|
wxSprintf(error_buf, wxT("%s"), wxT("Poetry buffer exceeded."));
|
||
|
PoetryError(error_buf);
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
fclose(data_file);
|
||
|
poem_buffer[i-1] = 0;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Do the search
|
||
|
long MainWindow::DoSearch(void)
|
||
|
{
|
||
|
if (m_searchString.empty())
|
||
|
return false;
|
||
|
|
||
|
FILE *file;
|
||
|
size_t i = 0;
|
||
|
int ch = 0;
|
||
|
wxChar buf[100];
|
||
|
long find_start;
|
||
|
long previous_poem_start;
|
||
|
|
||
|
bool found = false;
|
||
|
size_t search_length = m_searchString.length();
|
||
|
|
||
|
if (same_search)
|
||
|
{
|
||
|
find_start = last_find + 1;
|
||
|
previous_poem_start = last_poem_start;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
find_start = 0;
|
||
|
last_poem_start = 0;
|
||
|
previous_poem_start = -1;
|
||
|
}
|
||
|
|
||
|
if (data_filename)
|
||
|
wxSprintf(buf, wxT("%s.dat"), data_filename);
|
||
|
|
||
|
file = wxFopen(buf, wxT("r"));
|
||
|
if (! (data_filename && file))
|
||
|
{
|
||
|
wxSprintf(error_buf, wxT("Poetry data file %s not found\n"), buf);
|
||
|
PoetryError(error_buf);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
fseek(file, find_start, SEEK_SET);
|
||
|
|
||
|
while ((ch != EOF) && !found)
|
||
|
{
|
||
|
ch = getc(file);
|
||
|
ch = wxTolower(ch); // Make lower case
|
||
|
|
||
|
// Only match if we're looking at a different poem
|
||
|
// (no point in displaying the same poem again)
|
||
|
if ((m_searchString[i] == ch) && (last_poem_start != previous_poem_start))
|
||
|
{
|
||
|
if (i == 0)
|
||
|
last_find = ftell(file);
|
||
|
if (i == search_length-1)
|
||
|
found = true;
|
||
|
i ++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
i = 0;
|
||
|
}
|
||
|
|
||
|
if (ch == '#')
|
||
|
{
|
||
|
ch = getc(file);
|
||
|
last_poem_start = ftell(file);
|
||
|
}
|
||
|
}
|
||
|
fclose(file);
|
||
|
if (ch == EOF)
|
||
|
{
|
||
|
last_find = -1;
|
||
|
}
|
||
|
|
||
|
if (found)
|
||
|
{
|
||
|
return last_poem_start;
|
||
|
}
|
||
|
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
// Set up poetry filenames, preferences, load the index
|
||
|
// Load index (or compile it if none found)
|
||
|
void TryLoadIndex()
|
||
|
{
|
||
|
index_ok = (LoadIndex(index_filename) != 0);
|
||
|
if (!index_ok || (nitems == 0))
|
||
|
{
|
||
|
PoetryError(wxT("Index file not found; will compile new one"), wxT("wxPoem"));
|
||
|
index_ok = Compile();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Error message
|
||
|
void PoetryError(const wxChar *msg, const wxChar *caption)
|
||
|
{
|
||
|
wxMessageBox(msg, caption, wxOK|wxICON_EXCLAMATION);
|
||
|
}
|
||
|
|
||
|
// Notification (change icon to something appropriate!)
|
||
|
void PoetryNotify(const wxChar *Msg, const wxChar *caption)
|
||
|
{
|
||
|
wxMessageBox(Msg, caption, wxOK | wxICON_INFORMATION);
|
||
|
}
|
||
|
|
||
|
// Build up and save an index into the poetry data file, for
|
||
|
// rapid random access
|
||
|
bool Compile(void)
|
||
|
{
|
||
|
FILE *file;
|
||
|
int j;
|
||
|
int ch;
|
||
|
wxChar buf[100];
|
||
|
|
||
|
if (data_filename)
|
||
|
wxSprintf(buf, wxT("%s.dat"), data_filename);
|
||
|
|
||
|
file = wxFopen(buf, wxT("r"));
|
||
|
if (! (data_filename && file))
|
||
|
{
|
||
|
wxSprintf(error_buf, wxT("Poetry data file %s not found\n"), buf);
|
||
|
PoetryError(error_buf);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
nitems = 0;
|
||
|
|
||
|
// Do first one (?)
|
||
|
poem_index[nitems] = 0;
|
||
|
nitems ++;
|
||
|
|
||
|
// Do rest
|
||
|
|
||
|
do {
|
||
|
ch = getc(file);
|
||
|
if (ch == '#')
|
||
|
{
|
||
|
ch = getc(file);
|
||
|
long data;
|
||
|
data = ftell(file);
|
||
|
poem_index[nitems] = data;
|
||
|
nitems ++;
|
||
|
}
|
||
|
} while (ch != EOF);
|
||
|
fclose(file);
|
||
|
|
||
|
if (index_filename)
|
||
|
wxSprintf(buf, wxT("%s.idx"), index_filename);
|
||
|
|
||
|
file = wxFopen(buf, wxT("w"));
|
||
|
if (! (data_filename && file))
|
||
|
{
|
||
|
wxSprintf(error_buf, wxT("Poetry index file %s cannot be created\n"), buf);
|
||
|
PoetryError(error_buf);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
wxFprintf(file, wxT("%ld\n\n"), nitems);
|
||
|
for (j = 0; j < nitems; j++)
|
||
|
wxFprintf(file, wxT("%ld\n"), poem_index[j]);
|
||
|
|
||
|
fclose(file);
|
||
|
PoetryNotify(wxT("Poetry index compiled."));
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
void MainWindow::OnPopup(wxCommandEvent& event)
|
||
|
{
|
||
|
switch (event.GetId())
|
||
|
{
|
||
|
case POEM_NEXT:
|
||
|
// Another poem/page
|
||
|
TheMainWindow->NextPage();
|
||
|
break;
|
||
|
case POEM_PREVIOUS:
|
||
|
// Previous page
|
||
|
TheMainWindow->PreviousPage();
|
||
|
break;
|
||
|
case POEM_SEARCH:
|
||
|
// Search - with dialog
|
||
|
TheMainWindow->Search(true);
|
||
|
break;
|
||
|
case POEM_NEXT_MATCH:
|
||
|
// Search - without dialog (next match)
|
||
|
TheMainWindow->Search(false);
|
||
|
break;
|
||
|
case POEM_MINIMIZE:
|
||
|
TheMainWindow->Iconize(true);
|
||
|
break;
|
||
|
#if wxUSE_CLIPBOARD
|
||
|
case POEM_COPY:
|
||
|
wxTheClipboard->UsePrimarySelection();
|
||
|
if (wxTheClipboard->Open())
|
||
|
{
|
||
|
static wxString s;
|
||
|
s = poem_buffer;
|
||
|
s.Replace( wxT("@P"),wxEmptyString);
|
||
|
s.Replace( wxT("@A "),wxEmptyString);
|
||
|
s.Replace( wxT("@A"),wxEmptyString);
|
||
|
s.Replace( wxT("@T "),wxEmptyString);
|
||
|
s.Replace( wxT("@T"),wxEmptyString);
|
||
|
wxTextDataObject *data = new wxTextDataObject( s.c_str() );
|
||
|
if (!wxTheClipboard->SetData( data ))
|
||
|
wxMessageBox(wxT("Error while copying to the clipboard."));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
wxMessageBox(wxT("Error opening the clipboard."));
|
||
|
}
|
||
|
wxTheClipboard->Close();
|
||
|
break;
|
||
|
#endif
|
||
|
case POEM_BIGGER_TEXT:
|
||
|
pointSize ++;
|
||
|
CreateFonts();
|
||
|
TheMainWindow->Resize();
|
||
|
break;
|
||
|
case POEM_SMALLER_TEXT:
|
||
|
if (pointSize > 2)
|
||
|
{
|
||
|
pointSize --;
|
||
|
CreateFonts();
|
||
|
TheMainWindow->Resize();
|
||
|
}
|
||
|
break;
|
||
|
case POEM_ABOUT:
|
||
|
(void)wxMessageBox(wxT("wxPoem Version 1.1\nJulian Smart (c) 1995"),
|
||
|
wxT("About wxPoem"), wxOK, TheMainWindow);
|
||
|
break;
|
||
|
case POEM_EXIT:
|
||
|
// Exit
|
||
|
TheMainWindow->Close(true);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|