1 /* Manual.cpp
2 *
3 * Copyright (C) 1996-2021 Paul Boersma
4 *
5 * This code is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "Manual.h"
20 #include "Printer.h"
21 #include "machine.h"
22 #include "site.h"
23 #include "EditorM.h"
24 #include "praat_script.h"
25 #include "praatP.h"
26
27 Thing_implement (Manual, HyperPage, 0);
28
29 #define SEARCH_PAGE 0
30
31 static const conststring32 month [] =
32 { U"", U"January", U"February", U"March", U"April", U"May", U"June",
33 U"July", U"August", U"September", U"October", U"November", U"December" };
34
menu_cb_writeOneToHtmlFile(Manual me,EDITOR_ARGS_FORM)35 static void menu_cb_writeOneToHtmlFile (Manual me, EDITOR_ARGS_FORM) {
36 EDITOR_FORM_SAVE (U"Save as HTML file", nullptr)
37 ManPages manPages = (ManPages) my data;
38 autoMelderString buffer;
39 MelderString_copy (& buffer, manPages -> pages.at [my visiblePageNumber] -> title.get());
40 char32 *p = buffer.string;
41 while (*p) {
42 if (! isalnum ((int) *p) && *p != U'_')
43 *p = U'_';
44 p ++;
45 }
46 MelderString_append (& buffer, U".html");
47 Melder_sprint (defaultName,300, buffer.string);
48 EDITOR_DO_SAVE
49 ManPages_writeOneToHtmlFile ((ManPages) my data, my visiblePageNumber, file);
50 EDITOR_END
51 }
52
menu_cb_writeAllToHtmlFolder(Manual me,EDITOR_ARGS_FORM)53 static void menu_cb_writeAllToHtmlFolder (Manual me, EDITOR_ARGS_FORM) {
54 EDITOR_FORM (U"Save all pages as HTML files", nullptr)
55 FOLDER (folder, U"Folder", U"")
56 EDITOR_OK
57 SET_STRING (folder, Melder_dirToPath (& my rootDirectory))
58 EDITOR_DO
59 ManPages_writeAllToHtmlDir ((ManPages) my data, folder);
60 EDITOR_END
61 }
62
menu_cb_searchForPageList(Manual me,EDITOR_ARGS_FORM)63 static void menu_cb_searchForPageList (Manual me, EDITOR_ARGS_FORM) {
64 EDITOR_FORM (U"Search for page", nullptr)
65 static ManPages manPages;
66 static constSTRVEC pages;
67 manPages = (ManPages) my data;
68 pages = ManPages_getTitles (manPages);
69 LIST (page, U"Page", pages, 1)
70 EDITOR_OK
71 EDITOR_DO
72 HyperPage_goToPage_number (me, page);
73 EDITOR_END
74 }
75
v_draw()76 void structManual :: v_draw () {
77 const ManPages manPages = (ManPages) our data;
78 if (our visiblePageNumber == SEARCH_PAGE) {
79 HyperPage_pageTitle (this, U"Best matches");
80 HyperPage_intro (this, U"The best matches to your query seem to be:");
81 for (int i = 1; i <= our numberOfMatches; i ++) {
82 char32 link [300];
83 const ManPage page = manPages -> pages.at [matches [i]];
84 Melder_sprint (link,300, U"• @@", page -> title.get());
85 HyperPage_listItem (this, link);
86 }
87 return;
88 }
89 const ManPage page = manPages -> pages.at [our visiblePageNumber];
90 //if (! our paragraphs____)
91 // return;
92 HyperPage_pageTitle (this, page -> title.get());
93 for (integer ipar = 1; ipar <= page -> paragraphs.size; ipar ++) {
94 ManPage_Paragraph paragraph = & page -> paragraphs [ipar];
95 switch (paragraph -> type) {
96 case kManPage_type::INTRO: HyperPage_intro (this, paragraph -> text); break;
97 case kManPage_type::ENTRY: HyperPage_entry (this, paragraph -> text); break;
98 case kManPage_type::NORMAL: HyperPage_paragraph (this, paragraph -> text); break;
99 case kManPage_type::LIST_ITEM: HyperPage_listItem (this, paragraph -> text); break;
100 case kManPage_type::TAG: HyperPage_listTag (this, paragraph -> text); break;
101 case kManPage_type::DEFINITION: HyperPage_definition (this, paragraph -> text); break;
102 case kManPage_type::CODE: HyperPage_code (this, paragraph -> text); break;
103 case kManPage_type::PROTOTYPE: HyperPage_prototype (this, paragraph -> text); break;
104 case kManPage_type::EQUATION: HyperPage_formula (this, paragraph -> text); break;
105 case kManPage_type::PICTURE: HyperPage_picture (this, paragraph -> width,
106 paragraph -> height, paragraph -> draw); break;
107 case kManPage_type::SCRIPT: HyperPage_script (this, paragraph -> width,
108 paragraph -> height, paragraph -> text); break;
109 case kManPage_type::LIST_ITEM1: HyperPage_listItem1 (this, paragraph -> text); break;
110 case kManPage_type::LIST_ITEM2: HyperPage_listItem2 (this, paragraph -> text); break;
111 case kManPage_type::LIST_ITEM3: HyperPage_listItem3 (this, paragraph -> text); break;
112 case kManPage_type::TAG1: HyperPage_listTag1 (this, paragraph -> text); break;
113 case kManPage_type::TAG2: HyperPage_listTag2 (this, paragraph -> text); break;
114 case kManPage_type::TAG3: HyperPage_listTag3 (this, paragraph -> text); break;
115 case kManPage_type::DEFINITION1: HyperPage_definition1 (this, paragraph -> text); break;
116 case kManPage_type::DEFINITION2: HyperPage_definition2 (this, paragraph -> text); break;
117 case kManPage_type::DEFINITION3: HyperPage_definition3 (this, paragraph -> text); break;
118 case kManPage_type::CODE1: HyperPage_code1 (this, paragraph -> text); break;
119 case kManPage_type::CODE2: HyperPage_code2 (this, paragraph -> text); break;
120 case kManPage_type::CODE3: HyperPage_code3 (this, paragraph -> text); break;
121 case kManPage_type::CODE4: HyperPage_code4 (this, paragraph -> text); break;
122 case kManPage_type::CODE5: HyperPage_code5 (this, paragraph -> text); break;
123 default: break;
124 }
125 }
126 if (ManPages_uniqueLinksHither (manPages, our visiblePageNumber)) {
127 integer ilink, jlink;
128 bool goAhead = true;
129 if (page -> paragraphs.size > 0) {
130 conststring32 text = page -> paragraphs [page -> paragraphs.size]. text;
131 if (! text || text [0] == U'\0' || text [str32len (text) - 1] != U':') {
132 if (our printing && our suppressLinksHither)
133 goAhead = false;
134 else
135 HyperPage_entry (this, U"Links to this page");
136 }
137 }
138 if (goAhead) for (ilink = 1; ilink <= page -> linksHither.size; ilink ++) {
139 const integer link = page -> linksHither [ilink];
140 bool alreadyShown = false;
141 for (jlink = 1; jlink <= page -> linksThither.size; jlink ++)
142 if (page -> linksThither [jlink] == link)
143 alreadyShown = true;
144 if (! alreadyShown) {
145 conststring32 title = manPages -> pages.at [page -> linksHither [ilink]] -> title.get();
146 char32 linkText [304];
147 Melder_sprint (linkText, 304, U"@@", title, U"@");
148 HyperPage_listItem (this, linkText);
149 }
150 }
151 }
152 if (! our printing && page -> date) {
153 char32 signature [100];
154 const integer date = page -> date;
155 const integer imonth = Melder_clipped (0_integer, date % 10000 / 100, 12_integer);
156 Melder_sprint (signature,100,
157 U"© ", str32equ (page -> author.get(), U"ppgb") ? U"Paul Boersma" :
158 str32equ (page -> author.get(), U"djmw") ? U"David Weenink" : page -> author.get(),
159 U", ", date % 100,
160 U" ", month [imonth],
161 U" ", date / 10000);
162 HyperPage_any (this, U"", our p_font, our p_fontSize, 0, 0.0,
163 0.0, 0.0, 0.1, 0.1, HyperPage_ADD_BORDER);
164 HyperPage_any (this, signature, our p_font, our p_fontSize, Graphics_ITALIC, 0.0,
165 0.03, 0.0, 0.1, 0.0, 0);
166 }
167 }
168
169 /********** PRINTING **********/
170
print(void * void_me,Graphics graphics)171 static void print (void *void_me, Graphics graphics) {
172 iam (Manual);
173 ManPages manPages = (ManPages) my data;
174 integer numberOfPages = manPages -> pages.size, saveVisiblePageNumber = my visiblePageNumber;
175 my ps = graphics;
176 Graphics_setDollarSignIsCode (my ps, true);
177 Graphics_setAtSignIsLink (my ps, true);
178 my printing = true;
179 HyperPage_initSheetOfPaper ((HyperPage) me);
180 for (integer ipage = 1; ipage <= numberOfPages; ipage ++) {
181 ManPage page = manPages -> pages.at [ipage];
182 if (my printPagesStartingWith == nullptr ||
183 Melder_stringMatchesCriterion (page -> title.get(), kMelder_string::STARTS_WITH, my printPagesStartingWith, true))
184 {
185 my visiblePageNumber = ipage;
186 my currentPageTitle = Melder_dup_f (page -> title.get());
187 my v_goToPage_number (ipage);
188 my v_draw ();
189 my v_goToPage_number (saveVisiblePageNumber);
190 }
191 }
192 my printing = false;
193 my printPagesStartingWith = nullptr;
194 }
195
menu_cb_printRange(Manual me,EDITOR_ARGS_FORM)196 static void menu_cb_printRange (Manual me, EDITOR_ARGS_FORM) {
197 EDITOR_FORM (U"Print range", nullptr)
198 SENTENCE (leftOrInsideHeader, U"Left or inside header", U"")
199 SENTENCE (middleHeader, U"Middle header", U"")
200 SENTENCE (rightOrOutsideHeader, U"Right or outside header", U"Manual")
201 SENTENCE (leftOrInsideFooter, U"Left or inside footer", U"")
202 SENTENCE (middleFooter, U"Middle footer", U"")
203 SENTENCE (rightOrOutsideFooter, U"Right or outside footer", U"")
204 BOOLEAN (mirrorEvenOddHeaders, U"Mirror even/odd headers", true)
205 TEXTFIELD (printAllPagesWhoseTitleStartsWith, U"Print all pages whose title starts with", U"Intro", 2)
206 INTEGER (firstPageNumber, U"First page number", U"1")
207 BOOLEAN (suppressLinksToThisPage, U"Suppress \"Links to this page\"", false)
208 EDITOR_OK
209 ManPages manPages = (ManPages) my data;
210 SET_STRING (leftOrInsideHeader, date_STR().get())
211 SET_STRING (rightOrOutsideHeader, my name.get())
212 if (my d_printingPageNumber)
213 SET_INTEGER (firstPageNumber, my d_printingPageNumber + 1)
214 if (my visiblePageNumber >= 1 && my visiblePageNumber <= manPages -> pages.size) {
215 ManPage page = manPages -> pages.at [my visiblePageNumber];
216 SET_STRING (printAllPagesWhoseTitleStartsWith, page -> title.get());
217 }
218 EDITOR_DO
219 my insideHeader = leftOrInsideHeader;
220 my middleHeader = middleHeader;
221 my outsideHeader = rightOrOutsideHeader;
222 my insideFooter = leftOrInsideFooter;
223 my middleFooter = middleFooter;
224 my outsideFooter = rightOrOutsideFooter;
225 my mirror = mirrorEvenOddHeaders;
226 my printPagesStartingWith = printAllPagesWhoseTitleStartsWith;
227 my d_printingPageNumber = firstPageNumber;
228 my suppressLinksHither = suppressLinksToThisPage;
229 Printer_print (print, me);
230 EDITOR_END
231 }
232
233 /********** SEARCHING **********/
234
searchToken(ManPages me,integer ipage,conststring32 token)235 static double searchToken (ManPages me, integer ipage, conststring32 token) {
236 double goodness = 0.0;
237 ManPage page = my pages.at [ipage];
238 if (! token [0])
239 return 1.0;
240 /*
241 Try to find a match in the title, case-insensitively.
242 */
243 static MelderString buffer;
244 MelderString_copy (& buffer, page -> title.get());
245 for (char32 *p = & buffer.string [0]; *p != U'\0'; p ++)
246 *p = Melder_toLowerCase (*p);
247 if (str32str (buffer.string, token)) {
248 goodness += 300.0; // lots of points for a match in the title!
249 if (str32equ (buffer.string, token))
250 goodness += 10000.0; // even more points for an exact match!
251 }
252 /*
253 Try to find a match in the paragraphs, case-insensitively.
254 */
255 for (integer ipar = 1; ipar <= page -> paragraphs.size; ipar ++) {
256 ManPage_Paragraph par = & page -> paragraphs [ipar];
257 if (par -> text) {
258 char32 *ptoken;
259 MelderString_copy (& buffer, par -> text);
260 for (char32 *p = & buffer.string [0]; *p != '\0'; p ++)
261 *p = Melder_toLowerCase (*p);
262 ptoken = str32str (buffer.string, token);
263 if (ptoken) {
264 goodness += 10.0; // ten points for every paragraph with a match!
265 if (str32str (ptoken + str32len (token), token))
266 goodness += 1.0; // one point for every second occurrence in a paragraph!
267 }
268 }
269 }
270 return goodness;
271 }
272
search(Manual me,conststring32 query)273 static void search (Manual me, conststring32 query) {
274 ManPages manPages = (ManPages) my data;
275 integer numberOfPages = manPages -> pages.size;
276 static MelderString searchText;
277 MelderString_copy (& searchText, query);
278 for (char32 *p = & searchText.string [0]; *p != U'\0'; p ++) {
279 if (*p == U'\n')
280 *p = U' ';
281 *p = Melder_toLowerCase (*p);
282 }
283 static autoVEC goodnessOfMatch;
284 if (NUMisEmpty (goodnessOfMatch.get()))
285 goodnessOfMatch = zero_VEC (numberOfPages);
286 for (integer ipage = 1; ipage <= numberOfPages; ipage ++) {
287 char32 *token = searchText.string;
288 goodnessOfMatch [ipage] = 1.0;
289 for (;;) {
290 char32 *space = str32chr (token, U' ');
291 if (space)
292 *space = U'\0';
293 goodnessOfMatch [ipage] *= searchToken (manPages, ipage, token);
294 if (! space)
295 break;
296 *space = U' '; // restore
297 token = space + 1;
298 }
299 }
300 /*
301 Find the 20 best matches.
302 */
303 my numberOfMatches = 0;
304 for (integer imatch = 1; imatch <= 20; imatch ++) {
305 integer imax = 0;
306 double max = 0.0;
307 for (integer ipage = 1; ipage <= numberOfPages; ipage ++) {
308 if (goodnessOfMatch [ipage] > max) {
309 max = goodnessOfMatch [ipage];
310 imax = ipage;
311 }
312 }
313 if (! imax) break;
314 my matches [++ my numberOfMatches] = imax;
315 goodnessOfMatch [imax] = 0.0; // skip next time
316 }
317 HyperPage_goToPage_number (me, SEARCH_PAGE);
318 }
319
Manual_search(Manual me,conststring32 query)320 void Manual_search (Manual me, conststring32 query) {
321 GuiText_setString (my searchText, query);
322 search (me, query);
323 }
324
gui_button_cb_home(Manual me,GuiButtonEvent)325 static void gui_button_cb_home (Manual me, GuiButtonEvent /* event */) {
326 ManPages pages = (ManPages) my data;
327 integer iHome = ManPages_lookUp (pages, U"Intro");
328 HyperPage_goToPage_number (me, iHome ? iHome : 1);
329 }
330
gui_button_cb_record(Manual me,GuiButtonEvent)331 static void gui_button_cb_record (Manual me, GuiButtonEvent /* event */) {
332 ManPages manPages = (ManPages) my data;
333 ManPage manPage = ( my visiblePageNumber < 1 ? nullptr : manPages -> pages.at [my visiblePageNumber] );
334 GuiThing_setSensitive (my recordButton, false);
335 GuiThing_setSensitive (my playButton, false);
336 GuiThing_setSensitive (my publishButton, false);
337 #if defined (_WIN32)
338 GdiFlush ();
339 #endif
340 if (! Melder_record (manPage == nullptr ? 1.0 : manPage -> recordingTime)) Melder_flushError ();
341 GuiThing_setSensitive (my recordButton, true);
342 GuiThing_setSensitive (my playButton, true);
343 GuiThing_setSensitive (my publishButton, true);
344 }
345
gui_button_cb_play(Manual me,GuiButtonEvent)346 static void gui_button_cb_play (Manual me, GuiButtonEvent /* event */) {
347 GuiThing_setSensitive (my recordButton, false);
348 GuiThing_setSensitive (my playButton, false);
349 GuiThing_setSensitive (my publishButton, false);
350 #if defined (_WIN32)
351 GdiFlush ();
352 #endif
353 Melder_play ();
354 GuiThing_setSensitive (my recordButton, true);
355 GuiThing_setSensitive (my playButton, true);
356 GuiThing_setSensitive (my publishButton, true);
357 }
358
gui_button_cb_publish(Manual,GuiButtonEvent)359 static void gui_button_cb_publish (Manual /* me */, GuiButtonEvent /* event */) {
360 Melder_publishPlayed ();
361 }
362
do_search(Manual me)363 static void do_search (Manual me) {
364 autostring32 query = GuiText_getString (my searchText);
365 search (me, query.get());
366 }
367
gui_button_cb_search(Manual me,GuiButtonEvent)368 static void gui_button_cb_search (Manual me, GuiButtonEvent /* event */) {
369 do_search (me);
370 }
371
v_createChildren()372 void structManual :: v_createChildren () {
373 ManPages pages = (ManPages) our data; // has been installed here by Editor_init ()
374 our d_hasExtraRowOfTools = pages -> dynamic;
375 Manual_Parent :: v_createChildren ();
376 #if defined (macintosh)
377 #define STRING_SPACING 8
378 #else
379 #define STRING_SPACING 2
380 #endif
381 const int height = Machine_getTextHeight (), y = Machine_getMenuBarBottom () + 4;
382 our homeButton = GuiButton_createShown (our windowForm, 104, 168, y, y + height,
383 U"Home", gui_button_cb_home, this, 0);
384 if (pages -> dynamic) {
385 our recordButton = GuiButton_createShown (our windowForm, 4, 79, y+height+8, y+height+8 + height,
386 U"Record", gui_button_cb_record, this, 0);
387 our playButton = GuiButton_createShown (our windowForm, 85, 160, y+height+8, y+height+8 + height,
388 U"Play", gui_button_cb_play, this, 0);
389 our publishButton = GuiButton_createShown (our windowForm, 166, 166 + 175, y+height+8, y+height+8 + height,
390 U"Copy last played to list", gui_button_cb_publish, this, 0);
391 }
392 GuiButton_createShown (our windowForm, 274, 274 + 69, y, y + height,
393 U"Search:", gui_button_cb_search, this, GuiButton_DEFAULT);
394 our searchText = GuiText_createShown (our windowForm, 274+69 + STRING_SPACING, 452 + STRING_SPACING - 2, y, y + Gui_TEXTFIELD_HEIGHT, 0);
395 }
396
menu_cb_help(Manual me,EDITOR_ARGS_DIRECT)397 static void menu_cb_help (Manual me, EDITOR_ARGS_DIRECT) { HyperPage_goToPage (me, U"Manual"); }
398
v_createMenus()399 void structManual :: v_createMenus () {
400 Manual_Parent :: v_createMenus ();
401
402 Editor_addCommand (this, U"File", U"Print manual...", 0, menu_cb_printRange);
403 Editor_addCommand (this, U"File", U"Save page as HTML file...", 0, menu_cb_writeOneToHtmlFile);
404 Editor_addCommand (this, U"File", U"Save manual to HTML folder...", 0, menu_cb_writeAllToHtmlFolder);
405 Editor_addCommand (this, U"File", U"Save manual to HTML directory...", praat_DEPRECATED_2020, menu_cb_writeAllToHtmlFolder);
406 Editor_addCommand (this, U"File", U"-- close --", 0, nullptr);
407
408 Editor_addCommand (this, U"Go to", U"Search for page (list)...", 0, menu_cb_searchForPageList);
409 }
410
v_createHelpMenuItems(EditorMenu menu)411 void structManual :: v_createHelpMenuItems (EditorMenu menu) {
412 Manual_Parent :: v_createHelpMenuItems (menu);
413 EditorMenu_addCommand (menu, U"Manual help", '?', menu_cb_help);
414 }
415
v_defaultHeaders(EditorCommand cmd)416 void structManual :: v_defaultHeaders (EditorCommand cmd) {
417 Manual me = (Manual) cmd -> d_editor;
418 ManPages manPages = (ManPages) my data;
419 if (my visiblePageNumber > 0) {
420 char32 string [400];
421 static const conststring32 shortMonth [] =
422 { U"Jan", U"Feb", U"Mar", U"Apr", U"May", U"Jun", U"Jul", U"Aug", U"Sep", U"Oct", U"Nov", U"Dec" };
423 const ManPage page = manPages -> pages.at [my visiblePageNumber];
424 const integer date = page -> date;
425 SET_STRING (my outsideHeader, page -> title.get())
426 SET_STRING (my insideFooter, page -> author.get())
427 if (date) {
428 Melder_sprint (string,400, shortMonth [date % 10000 / 100 - 1], U" ", date % 100, U", ", date / 10000);
429 SET_STRING (my insideHeader, string)
430 }
431 }
432 }
433
v_getNumberOfPages()434 integer structManual :: v_getNumberOfPages () {
435 ManPages manPages = (ManPages) our data;
436 return manPages -> pages.size;
437 }
438
v_getCurrentPageNumber()439 integer structManual :: v_getCurrentPageNumber () {
440 return our visiblePageNumber > 0 ? our visiblePageNumber : 1;
441 }
442
v_goToPage_number(integer goToPageNumber)443 void structManual :: v_goToPage_number (integer goToPageNumber) {
444 ManPages manPages = (ManPages) our data;
445 if (goToPageNumber < 1 || goToPageNumber > manPages -> pages.size) {
446 if (goToPageNumber == SEARCH_PAGE) {
447 our visiblePageNumber = SEARCH_PAGE;
448 our currentPageTitle. reset();
449 return;
450 } else Melder_throw (U"Page ", goToPageNumber, U" not found.");
451 }
452 our visiblePageNumber = goToPageNumber;
453 ManPage page = manPages -> pages.at [our visiblePageNumber];
454 our currentPageTitle = Melder_dup_f (page -> title.get());
455 }
456
v_goToPage(conststring32 title)457 int structManual :: v_goToPage (conststring32 title) {
458 ManPages manPages = (ManPages) our data;
459 if (title [0] == U'\\' && title [1] == U'F' && title [2] == U'I') {
460 structMelderFile file { };
461 MelderDir_relativePathToFile (& manPages -> rootDirectory, title + 3, & file);
462 Melder_recordFromFile (& file);
463 return -1;
464 } else if (title [0] == U'\\' && title [1] == U'S' && title [2] == U'C') {
465 autoMelderSetDefaultDir saveDir (& manPages -> rootDirectory);
466 autoPraatBackground background;
467 try {
468 autostring32 fileNameWithArguments = Melder_dup (title + 3);
469 praat_executeScriptFromFileNameWithArguments (fileNameWithArguments.get());
470 } catch (MelderError) {
471 Melder_flushError ();
472 }
473 return 0;
474 } else {
475 const integer i = ManPages_lookUp (manPages, title);
476 if (i == 0)
477 Melder_throw (U"Page \"", title, U"\" not found.");
478 our v_goToPage_number (i);
479 return 1;
480 }
481 }
482
Manual_init(Manual me,conststring32 title,Daata data,bool ownData)483 void Manual_init (Manual me, conststring32 title, Daata data, bool ownData) {
484 ManPages manPages = (ManPages) data;
485 const integer lookUpPageNumber = ManPages_lookUp (manPages, title);
486 if (lookUpPageNumber == 0)
487 Melder_throw (U"Page \"", title, U"\" not found.");
488 my visiblePageNumber = lookUpPageNumber;
489 ManPage page = manPages -> pages.at [lookUpPageNumber];
490
491 /*
492 The title of the window is the title of the whole manual, not the title of the page.
493 If the first page has a title that starts with "-", then that is the title;
494 otherwise, the title is just "Praat Manual".
495 */
496 char32 windowTitle [101];
497 if (manPages -> pages.at [1] -> title [0] == U'-') {
498 Melder_sprint (windowTitle,101, & manPages -> pages.at [1] -> title [1]);
499 if (windowTitle [str32len (windowTitle) - 1] == U'-')
500 windowTitle [str32len (windowTitle) - 1] = U'\0';
501 } else {
502 Melder_sprint (windowTitle,101, U"Praat Manual");
503 }
504 my ownData = ownData;
505 HyperPage_init (me, windowTitle, data);
506 MelderDir_copy (& manPages -> rootDirectory, & my rootDirectory);
507 my history [0]. page = Melder_dup_f (title); // BAD
508 }
509
Manual_create(conststring32 title,Daata data,bool ownData)510 autoManual Manual_create (conststring32 title, Daata data, bool ownData) {
511 try {
512 autoManual me = Thing_new (Manual);
513 Manual_init (me.get(), title, data, ownData);
514 return me;
515 } catch (MelderError) {
516 Melder_throw (U"Manual window not created.");
517 }
518 }
519
520 /* End of file Manual.cpp */
521