wxpoem.cpp 28 KB

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