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