Commit | Line | Data |
---|---|---|
fac14bbe MW |
1 | /* Copyright (c) 2006 by Arkkra Enterprises */ |
2 | /* All rights reserved */ | |
3 | ||
4 | // Code for the Config menu item on the main toolbar | |
5 | ||
6 | #include "globals.H" | |
7 | #include "Preferences.H" | |
8 | #include "Config.H" | |
9 | #include "Main.H" | |
10 | #include "utils.H" | |
11 | #include <FL/fl_ask.H> | |
12 | #include <FL/Fl_Tooltip.H> | |
13 | #include <string.h> | |
14 | #include <stdlib.h> | |
15 | #include <unistd.h> | |
16 | #include <fcntl.h> | |
17 | ||
18 | ||
19 | // Window to ask user where files and tools are located | |
20 | ||
21 | FileLocations_dialog::FileLocations_dialog(void) | |
22 | : Fl_Double_Window(620, 300, "Mupmate File Locations") | |
23 | { | |
24 | mup_documentation_p = new Fl_Input(200, 30, 400, 30, "Mup Documentation Folder"); | |
25 | mup_documentation_p->tooltip("Set where Mup documentation\n" | |
26 | "files are installed on your system.\n" | |
27 | "This folder must contain the \"uguide\"\n" | |
28 | "folder that contains the HTML version\n" | |
29 | "of the Mup User's Guide."); | |
30 | ||
31 | mup_program_p = new Fl_Input(200, 65, 400, 30, "Mup Command Path"); | |
32 | mup_program_p->tooltip("Set where the Mup program\n" | |
33 | "is installed on your system."); | |
34 | ||
35 | music_files_p = new Fl_Input(200, 100, 400, 30, "Folder for Mup Files"); | |
36 | music_files_p->tooltip("Set the default folder for storing\n" | |
37 | "your Mup files (.mup input files,\n" | |
38 | "and .ps and .mid output files)."); | |
39 | ||
40 | muppath_p = new Fl_Input(200, 135, 400, 30, "Folder for Mup include Files"); | |
41 | static char include_tip_text[200]; | |
42 | (void) sprintf(include_tip_text, | |
43 | "Set the default folder (or list of folders,\n" | |
44 | "separated by %c characters) for\n" | |
45 | "storing your Mup \"include\" files.", | |
46 | path_separator()); | |
47 | muppath_p->tooltip(include_tip_text); | |
48 | ||
49 | viewer_p = new Fl_Input(200, 170, 400, 30, "PostScript Viewer Path"); | |
50 | viewer_p->tooltip("Set which PostScript viewing program\n" | |
51 | "to use for displaying Mup output.\n" | |
52 | #ifdef OS_LIKE_WIN32 | |
53 | "This is typically GSview32.exe\n" | |
54 | "which you can obtain from\n" | |
55 | "http://www.cs.wisc.edu/~ghost/gsview/" | |
56 | #else | |
57 | "The \"gv\" program is a common choice." | |
58 | #endif | |
59 | ); | |
60 | ||
61 | player_p = new Fl_Input(200, 205, 400, 30, "MIDI Player Path"); | |
62 | player_p->tooltip("Set which MIDI player program\n" | |
63 | "to use for playing Mup MIDI output.\n" | |
64 | #ifdef OS_LIKE_WIN32 | |
65 | "This is typically wmplayer.exe" | |
66 | #else | |
67 | "Common choices include xplaymidi or timidity." | |
68 | #endif | |
69 | ); | |
70 | ||
71 | apply_p = new Fl_Return_Button(50, 255, 100, 30, "Apply"); | |
72 | apply_p->when(FL_WHEN_RELEASE); | |
73 | apply_p->callback(Apply_cb, this); | |
74 | ||
75 | cancel_p = new Fl_Button(w() - 150, 255, 100, 30, "Cancel"); | |
76 | cancel_p->shortcut(FL_Escape); | |
77 | cancel_p->when(FL_WHEN_RELEASE); | |
78 | cancel_p->callback(Cancel_cb, this); | |
79 | ||
80 | // Populate the fields | |
81 | set_current_values(); | |
82 | ||
83 | // Arrange for destructor to clean up new-ed widgets | |
84 | end(); | |
85 | ||
86 | // Arrange for window manager closes to do Cancel. | |
87 | callback(Cancel_cb, this); | |
88 | when(FL_WHEN_NEVER); | |
89 | } | |
90 | ||
91 | ||
92 | FileLocations_dialog::~FileLocations_dialog() | |
93 | { | |
94 | } | |
95 | ||
96 | ||
97 | //--- Callback for when user clicks "Apply" on FileLocations dialog. | |
98 | // Save values in preferences file. | |
99 | ||
100 | CALL_BACK(FileLocations_dialog, Apply) | |
101 | { | |
102 | bool changes = false; // if any changes made | |
103 | bool error = false; // if any errors found | |
104 | char location[FL_PATH_MAX]; | |
105 | ||
106 | // Documentation location | |
107 | if (mup_documentation_p->size() > 0) { | |
108 | (void) Preferences_p->set(Mup_documentation_location, | |
109 | mup_documentation_p->value()); | |
110 | changes = true; | |
111 | // Documentation being wrong means User's Guide can't be | |
112 | // shown, which is bad, although not fatal. | |
113 | if ( ! fl_filename_isdir(mup_documentation_p->value()) ) { | |
114 | fl_alert("Location for Mup documentation is not a valid folder."); | |
115 | error = true; | |
116 | } | |
117 | else { | |
118 | if (access(users_guide_index_file( | |
119 | mup_documentation_p->value()), F_OK) | |
120 | != 0) { | |
121 | fl_alert("Folder specified for Mup documentation it not correct:\n" | |
122 | "it does not contain the Mup User's Guide."); | |
123 | error = true; | |
124 | } | |
125 | } | |
126 | } | |
127 | ||
128 | // Location of Mup program | |
129 | if (mup_program_p->size() > 0) { | |
130 | if (find_executable(mup_program_p->value(), location)) { | |
131 | (void) Preferences_p->set(Mup_program_location, | |
132 | mup_program_p->value()); | |
133 | changes = true; | |
134 | } | |
135 | else { | |
136 | fl_alert("Location specified for Mup program is not valid."); | |
137 | error = true; | |
138 | } | |
139 | } | |
140 | ||
141 | // Default folder for Mup input files | |
142 | if (music_files_p->size() > 0) { | |
143 | if (chdir(music_files_p->value()) != 0) { | |
144 | fl_alert("Value for \"Folder for Mup Files\" is not a valid folder."); | |
145 | error = true; | |
146 | } | |
147 | else { | |
148 | (void) Preferences_p->set(Music_files_location, | |
149 | music_files_p->value()); | |
150 | changes = true; | |
151 | } | |
152 | } | |
153 | ||
154 | // $MUPPATH value | |
155 | if (muppath_p->size() > 0) { | |
156 | (void) Preferences_p->set(MUPPATH_location, muppath_p->value()); | |
157 | // Set $MUPPATH | |
158 | set_muppath(muppath_p->value()); | |
159 | changes = true; | |
160 | // Setting MUPPATH correctly is only important if user | |
161 | // actually uses it, which many people won't, but if it is set | |
162 | // to something invalid, we give a warning. | |
163 | // Since MUPPATH can be a list, we check each component | |
164 | // in the list. | |
165 | char pathcopy[muppath_p->size() + 1]; | |
166 | (void) strcpy(pathcopy, muppath_p->value()); | |
167 | char * component_p = pathcopy; | |
168 | char * sep_p; // where path separator appears in list | |
169 | do { | |
170 | if ((sep_p = strchr(component_p, path_separator())) != 0) { | |
171 | *sep_p = '\0'; | |
172 | } | |
173 | if (strlen(component_p) > 0 && | |
174 | ! fl_filename_isdir(component_p)) { | |
175 | fl_alert("Location for Mup include files\n" | |
176 | "\"%s\"\nis not a valid folder.", | |
177 | component_p); | |
178 | error = true; | |
179 | } | |
180 | component_p += strlen(component_p) + 1; | |
181 | } while (sep_p != 0); | |
182 | } | |
183 | ||
184 | // PostScript viewer program | |
185 | if (viewer_p->size() > 0) { | |
186 | if (find_executable(viewer_p->value(), location)) { | |
187 | (void) Preferences_p->set(Viewer_location, viewer_p->value()); | |
188 | changes = true; | |
189 | } | |
190 | else { | |
191 | fl_alert("Location specified for PostScript viewer is not valid."); | |
192 | error = true; | |
193 | } | |
194 | } | |
195 | ||
196 | // MIDI player | |
197 | if (player_p->size() > 0) { | |
198 | if (find_executable(player_p->value(), location)) { | |
199 | (void) Preferences_p->set(MIDI_player_location, player_p->value()); | |
200 | changes = true; | |
201 | } | |
202 | else { | |
203 | fl_alert("Location specified for MIDI player is not valid."); | |
204 | error = true; | |
205 | } | |
206 | } | |
207 | ||
208 | // If any changes, persist the data. | |
209 | if (changes) { | |
210 | Preferences_p->flush(); | |
211 | } | |
212 | ||
213 | // If there were errors, leave form up so user can try to correct them. | |
214 | if ( ! error ) { | |
215 | hide(); | |
216 | } | |
217 | } | |
218 | ||
219 | ||
220 | //--- callback for when user clicks "Cancel" on FileLocations dialog | |
221 | ||
222 | CALL_BACK(FileLocations_dialog, Cancel) | |
223 | { | |
224 | hide(); | |
225 | // Put all the original settings back on the form | |
226 | set_current_values(); | |
227 | } | |
228 | ||
229 | ||
230 | // Populate form with the current default values from user's preferences. | |
231 | ||
232 | void | |
233 | FileLocations_dialog::set_current_values(void) | |
234 | { | |
235 | char * val; | |
236 | (void) Preferences_p->get(Mup_documentation_location, val, | |
237 | Default_Mup_documentation_location); | |
238 | mup_documentation_p->value(val); | |
239 | ||
240 | (void) Preferences_p->get(Mup_program_location, val, | |
241 | Default_Mup_program_location); | |
242 | mup_program_p->value(val); | |
243 | ||
244 | (void) Preferences_p->get(Music_files_location, val, | |
245 | Default_music_files_location); | |
246 | music_files_p->value(val); | |
247 | (void) Preferences_p->get(MUPPATH_location, val, | |
248 | Default_MUPPATH_location); | |
249 | muppath_p->value(val); | |
250 | (void) Preferences_p->get(Viewer_location, val, | |
251 | Default_viewer_location); | |
252 | viewer_p->value(val); | |
253 | (void) Preferences_p->get(MIDI_player_location, val, | |
254 | Default_MIDI_player_location); | |
255 | player_p->value(val); | |
256 | } | |
257 | ||
258 | ||
259 | //----------------------------------------------------------------- | |
260 | ||
261 | // List of standard FLTK fonts, and info to map name to menu entry. | |
262 | static struct Font { | |
263 | const char * name; | |
264 | Fl_Font value; | |
265 | int menu_offset; | |
266 | } Fontlist[] = { | |
267 | { "Courier", FL_COURIER }, | |
268 | { "Courier Bold", FL_COURIER_BOLD }, | |
269 | { "Courier Italic", FL_COURIER_ITALIC }, | |
270 | { "Courier Bold Italic", FL_COURIER_BOLD_ITALIC }, | |
271 | { "Helvetica", FL_HELVETICA }, | |
272 | { "Helvetica Bold", FL_HELVETICA_BOLD }, | |
273 | { "Helvetica Italic", FL_HELVETICA_ITALIC }, | |
274 | { "Helvetica Bold Italic", FL_HELVETICA_BOLD_ITALIC }, | |
275 | { "Times", FL_TIMES }, | |
276 | { "Times Bold", FL_TIMES_BOLD }, | |
277 | { "Times Italic", FL_TIMES_ITALIC }, | |
278 | { "Times Bold Italic", FL_TIMES_BOLD_ITALIC }, | |
279 | }; | |
280 | static const int Fontlistlength = sizeof(Fontlist) / sizeof(Fontlist[0]); | |
281 | ||
282 | // Window to ask user preferences, like editor font, size, etc. | |
283 | ||
284 | Preferences_dialog::Preferences_dialog(void) | |
285 | : Fl_Double_Window(400, 280, "Mupmate Preferences") | |
286 | { | |
287 | // Make widget for user's editor font choice. | |
288 | font_p = new Fl_Choice(20, 40, 210, 30, "Text Font"); | |
289 | font_p->tooltip("Select the font to be used\n" | |
290 | "in the editor window where you\n" | |
291 | "type in Mup input. It is also used\n" | |
292 | "for the Help and error report text."); | |
293 | // Arrange to reset size menu if font selection changes | |
294 | font_p->callback(fontchg_cb, this); | |
295 | font_p->when(FL_WHEN_CHANGED); | |
296 | font_p->align(FL_ALIGN_TOP_LEFT); | |
297 | ||
298 | // Make widget for user's editor size choice. | |
299 | size_p = new Fl_Choice(270, 40, 100, 30, "Text Size"); | |
300 | size_p->tooltip("Select the text size to be used\n" | |
301 | "in the editor window where you\n" | |
302 | "type in Mup input. It is also used\n" | |
303 | "for the Help and error report text."); | |
304 | size_p->align(FL_ALIGN_TOP_LEFT); | |
305 | ||
306 | auto_display_p = new Fl_Check_Button(20, 90, 180, 30, | |
307 | "Auto-Display on Save"); | |
308 | auto_display_p->tooltip("Set whether your music\n" | |
309 | "is displayed automatically\n" | |
310 | "whenever you save your Mup file."); | |
311 | ||
312 | auto_save_p = new Fl_Check_Button(w() - 170, 90, 150, 30, | |
313 | "Auto-Save on Run"); | |
314 | auto_save_p->tooltip("Set whether your music is saved\n" | |
315 | "automatically whenever you do Display, Play,\n" | |
316 | "Write PostScript or Write MIDI from the Run menu."); | |
317 | ||
318 | tooltips_delay_p = new Fl_Value_Input(150, 155, 100, 30, "Tool Tip Delay"); | |
319 | tooltips_delay_p->minimum(0.0); | |
320 | tooltips_delay_p->precision(3); | |
321 | tooltips_delay_p->tooltip("Set how long to delay before showing\n" | |
322 | "tool tips, in seconds.\n"); | |
323 | tooltips_delay_p->align(FL_ALIGN_TOP_LEFT); | |
324 | ||
325 | // Create and configure widget for Apply button | |
326 | apply_p = new Fl_Return_Button(60, 215, 100, 30, "Apply"); | |
327 | apply_p->when(FL_WHEN_RELEASE); | |
328 | apply_p->callback(Apply_cb, this); | |
329 | ||
330 | // Create and configure widget for Cancel button | |
331 | cancel_p = new Fl_Button(w() - 160, 215, 100, 30, "Cancel"); | |
332 | cancel_p->shortcut(FL_Escape); | |
333 | cancel_p->when(FL_WHEN_RELEASE); | |
334 | cancel_p->callback(Cancel_cb, this); | |
335 | ||
336 | // Populate the fields | |
337 | set_current_values(); | |
338 | ||
339 | // Arrange for destructor to clean up new-ed widgets | |
340 | end(); | |
341 | ||
342 | // Arrange for window manager closes to do Cancel. | |
343 | callback(Cancel_cb, this); | |
344 | when(FL_WHEN_NEVER); | |
345 | } | |
346 | ||
347 | Preferences_dialog::~Preferences_dialog() | |
348 | { | |
349 | } | |
350 | ||
351 | ||
352 | //---- Callback for when user changes font selection. | |
353 | // This re-creates the size menu to be what sizes are available | |
354 | // for that font, since each font could have a different set of sizes. | |
355 | ||
356 | CALL_BACK(Preferences_dialog, fontchg) | |
357 | { | |
358 | unsigned char size; | |
359 | if (size_p->mvalue() != 0) { | |
360 | size = atoi(size_p->mvalue()->text); | |
361 | } | |
362 | else { | |
363 | // Shouldn't really be possible to get here, | |
364 | // but better to be safe. | |
365 | size = (unsigned char) atoi(Default_editor_size); | |
366 | } | |
367 | ||
368 | set_size_list(Config::fontvalue(font_p->mvalue()->text), size); | |
369 | } | |
370 | ||
371 | ||
372 | //--- Callback for when user clicks Apply in Preferences | |
373 | // Save the new values. | |
374 | ||
375 | CALL_BACK(Preferences_dialog, Apply) | |
376 | { | |
377 | Fl_Font font; | |
378 | int n; | |
379 | ||
380 | Preferences_p->set(Auto_display_preference, auto_display_p->value()); | |
381 | Preferences_p->set(Auto_save_preference, auto_save_p->value()); | |
382 | Preferences_p->set(Tooltips_delay_preference, tooltips_delay_p->value()); | |
383 | Fl_Tooltip::delay(tooltips_delay_p->value()); | |
384 | ||
385 | // Convert font menu selection into font value. | |
386 | for (n = 0; n < Fontlistlength; n++) { | |
387 | if (Fontlist[n].menu_offset == font_p->value()) { | |
388 | Preferences_p->set(Editor_font_preference, Fontlist[n].name); | |
389 | font = Fontlist[n].value; | |
390 | break; | |
391 | } | |
392 | } | |
393 | if (n >= Fontlistlength) { | |
394 | // Selection not valid. Fall back to using the default. | |
395 | char * fontname; | |
396 | (void) Preferences_p->get(Editor_font_preference, fontname, | |
397 | Default_editor_font); | |
398 | font = Config::fontvalue(fontname); | |
399 | } | |
400 | ||
401 | // Save size value. | |
402 | unsigned char size; | |
403 | if (size_p->text() != 0) { | |
404 | (void) Preferences_p->set(Editor_size_preference, size_p->text()); | |
405 | size = (unsigned char) atoi(size_p->text()); | |
406 | } | |
407 | else { | |
408 | size = (unsigned char) atoi(Default_editor_size); | |
409 | } | |
410 | ||
411 | // Persist the data. | |
412 | Preferences_p->flush(); | |
413 | ||
414 | // Actually change the font/size in all relevant windows. | |
415 | // Windows that want to know about these changes register a callback, | |
416 | // so we call them. | |
417 | Font_change_registration::run_callbacks(font, size); | |
418 | ||
419 | hide(); | |
420 | } | |
421 | ||
422 | //--- callback for when user clicks Cancel in Preferences | |
423 | ||
424 | CALL_BACK(Preferences_dialog, Cancel) | |
425 | { | |
426 | hide(); | |
427 | // Put all the original settings back on the form | |
428 | set_current_values(); | |
429 | } | |
430 | ||
431 | ||
432 | // Populate form with current values from user's preferences | |
433 | ||
434 | void | |
435 | Preferences_dialog::set_current_values(void) | |
436 | { | |
437 | int auto_display; | |
438 | (void) Preferences_p->get(Auto_display_preference, auto_display, | |
439 | Default_auto_display); | |
440 | auto_display_p->value(auto_display); | |
441 | ||
442 | int auto_save; | |
443 | (void) Preferences_p->get(Auto_save_preference, auto_save, | |
444 | Default_auto_save); | |
445 | auto_save_p->value(auto_save); | |
446 | ||
447 | double tooltips_delay; | |
448 | (void) Preferences_p->get(Tooltips_delay_preference, tooltips_delay, | |
449 | Default_tooltips_delay); | |
450 | tooltips_delay_p->value(tooltips_delay); | |
451 | ||
452 | char * fontname; | |
453 | (void) Preferences_p->get(Editor_font_preference, fontname, | |
454 | Default_editor_font); | |
455 | Fl_Font font = Config::fontvalue(fontname); | |
456 | // Populate font menu | |
457 | font_p->clear(); | |
458 | for (int i = 0; i < Fontlistlength; i++) { | |
459 | Fontlist[i].menu_offset = | |
460 | font_p->add(Fontlist[i].name, 0, 0, 0, 0); | |
461 | // Set the current value | |
462 | if (Fontlist[i].value == font) { | |
463 | font_p->value(Fontlist[i].menu_offset); | |
464 | } | |
465 | } | |
466 | ||
467 | char * sizename; | |
468 | (void) Preferences_p->get(Editor_size_preference, sizename, | |
469 | Default_editor_size); | |
470 | unsigned char size = (unsigned char) atoi(sizename); | |
471 | // Populate the size menu | |
472 | set_size_list(font, size); | |
473 | } | |
474 | ||
475 | ||
476 | // When font selection changes, re-create the size menu, | |
477 | // because each font could have different sizes available. | |
478 | ||
479 | void | |
480 | Preferences_dialog::set_size_list(Fl_Font font, uchar curr_size) | |
481 | { | |
482 | // Avoid really tiny sizes, or more importantly, zero, like if an atoi | |
483 | // failed, because otherwise FLTK may try to divide by zero. | |
484 | // Also limit to a maximum size. | |
485 | if (curr_size < Min_size || curr_size > Max_size) { | |
486 | curr_size = (unsigned char) atoi(Default_editor_size); | |
487 | } | |
488 | ||
489 | // Clean out the current menu if any | |
490 | size_p->clear(); | |
491 | ||
492 | // Populate the menu | |
493 | int * sizelist; | |
494 | int numsizes = Fl::get_font_sizes(font, sizelist); | |
495 | ||
496 | // Set current value to ridiculous value, then find closest | |
497 | int currvalue = 5000; | |
498 | ||
499 | int i; // index through sizelist | |
500 | int menu_index; // index into menu | |
501 | for (i = menu_index = 0; i < numsizes; i++) { | |
502 | if (sizelist[i] == 0) { | |
503 | // This means font is scaleable | |
504 | continue; | |
505 | } | |
506 | if (sizelist[i] > Max_size) { | |
507 | break; | |
508 | } | |
509 | char num_as_string[4]; | |
510 | (void) sprintf(num_as_string, "%d", sizelist[i]); | |
511 | size_p->add(num_as_string, 0, 0, 0, 0); | |
512 | // If this is closest index to desired size, mark as current | |
513 | if ( abs(sizelist[i] - currvalue) > abs(sizelist[i] - curr_size) ) { | |
514 | currvalue = sizelist[i]; | |
515 | size_p->value(menu_index); | |
516 | } | |
517 | menu_index++; | |
518 | } | |
519 | if (numsizes == 0 || (numsizes == 1 && sizelist[0] == 0)) { | |
520 | // Either no available sizes at all, or only | |
521 | // scaleable, with no special "good" sizes, | |
522 | // so we pick some and hope for the best. | |
523 | size_p->add("10", 0, 0, 0, 0); | |
524 | if (curr_size <= 11) { | |
525 | size_p->value(0); | |
526 | } | |
527 | size_p->add("12", 0, 0, 0, 0); | |
528 | if (curr_size >= 12 && curr_size <= 13) { | |
529 | size_p->value(1); | |
530 | } | |
531 | size_p->add("14", 0, 0, 0, 0); | |
532 | if (curr_size >= 14 && curr_size <= 15) { | |
533 | size_p->value(2); | |
534 | } | |
535 | size_p->add("16", 0, 0, 0, 0); | |
536 | if (curr_size >= 16 && curr_size <= 17) { | |
537 | size_p->value(3); | |
538 | } | |
539 | size_p->add("18", 0, 0, 0, 0); | |
540 | if (curr_size >= 18) { | |
541 | size_p->value(4); | |
542 | } | |
543 | } | |
544 | } | |
545 | ||
546 | //----------dialog to let user fill in the Registration form--------------- | |
547 | ||
548 | #define MULTI_OS "Note that if you wish to use Mup\n" \ | |
549 | "on multiple machines simultaneously,\n" \ | |
550 | "you need to purchase a registration\n" \ | |
551 | "for each." | |
552 | ||
553 | ||
554 | RegistrationForm_dialog::RegistrationForm_dialog(void) | |
555 | : Fl_Double_Window(500, 450, "Mup Registration") | |
556 | { | |
557 | name_p = new Fl_Input(80, 20, 400, 30, "Name"); | |
558 | name_p->tooltip("Enter your name."); | |
559 | ||
560 | address_p = new Fl_Input(80, 60, 400, 30, "Address"); | |
561 | address_p->tooltip("Enter your street address."); | |
562 | ||
563 | city_p = new Fl_Input(80, 100, 180, 30, "City"); | |
564 | city_p->tooltip("Enter the name of the city\n" | |
565 | "in which you live."); | |
566 | state_p = new Fl_Input(380, 100, 100, 30, "State/Province"); | |
567 | state_p->tooltip("Enter the state or province (if any)\n" | |
568 | "in which you live."); | |
569 | ||
570 | postal_code_p = new Fl_Input(110, 140, 120, 30, "Postal Code"); | |
571 | postal_code_p->tooltip("Enter your zip code or postal code\n" | |
572 | "as appropriate for your country."); | |
573 | ||
574 | country_p = new Fl_Input(320, 140, 160, 30, "Country"); | |
575 | country_p->tooltip("Enter the name of the country in which\n" | |
576 | "you live (optional if in USA)."); | |
577 | ||
578 | email_p = new Fl_Input(110, 180, 370, 30, "Email address"); | |
579 | email_p->tooltip("Enter your email address. This will only be used\n" | |
580 | "to send you your registration key\n" | |
581 | "and announcements of future (free) Mup upgrades."); | |
582 | ||
583 | how_heard_p = new Fl_Input(20, 240, 460, 30, "Where did you hear about Mup?"); | |
584 | how_heard_p->align(FL_ALIGN_TOP_LEFT); | |
585 | how_heard_p->tooltip("Please let us know how you learned about Mup\n" | |
586 | "(a particular web link, magazine, book, etc.)."); | |
587 | ||
588 | // Checkboxes for OS types. | |
589 | // If we are compiled for a particular OS, | |
590 | // automatically check that box. | |
591 | Windows_p = new Fl_Check_Button(20, 280, 80, 30, "Windows"); | |
592 | #ifdef __WIN32 | |
593 | Windows_p->value(1); | |
594 | #endif | |
595 | Windows_p->tooltip("Check here if you plan to run Mup\n" | |
596 | "under Microsoft Windows.\n" | |
597 | MULTI_OS); | |
598 | ||
599 | Mac_p = new Fl_Check_Button(110, 280, 50, 30, "Mac"); | |
600 | Mac_p->tooltip("Check here if you plan to run Mup\n" | |
601 | "under Apple Mac OS.\n" | |
602 | MULTI_OS); | |
603 | #ifdef __APPLE__ | |
604 | Mac_p->value(1); | |
605 | #endif | |
606 | ||
607 | Linux_p = new Fl_Check_Button(170, 280, 60, 30, "Linux"); | |
608 | Linux_p->tooltip("Check here if you plan to run Mup\n" | |
609 | "under Linux. " | |
610 | MULTI_OS); | |
611 | #ifdef __linux | |
612 | Linux_p->value(1); | |
613 | #endif | |
614 | ||
615 | other_p = new Fl_Check_Button(240, 280, 60, 30, "Other"); | |
616 | other_p->tooltip("Check here if you plan to run Mup\n" | |
617 | "under an OS not listed.\n" | |
618 | MULTI_OS); | |
619 | other_OS_p = new Fl_Input(300, 280, 180, 30, ""); | |
620 | other_OS_p->tooltip("If you checked \"Other,\"\n" | |
621 | "specify the name of the Operating System.\n" | |
622 | MULTI_OS); | |
623 | ||
624 | mailing_list_p = new Fl_Check_Button(100, 320, 350, 30, | |
625 | "I would like to join the Mup user's mailing list"); | |
626 | mailing_list_p->tooltip("There is a email mailing list for registered\n" | |
627 | "Mup users, for sharing information about Mup.\n" | |
628 | "Check here if you want to join the list.\n" | |
629 | "There is no extra charge, and you can always\n" | |
630 | "subscribe/unsubscribe later if you change your mind."); | |
631 | ||
632 | num_regs_p = new Positive_Int_Input(340, 355, 60, 30, | |
633 | "Number of Registrations ($29 each)"); | |
634 | num_regs_p->value("1"); | |
635 | num_regs_p->tooltip("Enter the number of registrations\n" | |
636 | "you wish to purchase.\n" | |
637 | MULTI_OS); | |
638 | ||
639 | save_form_p = new Fl_Return_Button(40, h() - 50, 120, 30, "Save Form"); | |
640 | save_form_p->callback(SaveForm_cb, this); | |
641 | cancel_p = new Fl_Button(w() - 160, h() - 50, 120, 30, "Cancel"); | |
642 | cancel_p->shortcut(FL_Escape); | |
643 | cancel_p->when(FL_WHEN_RELEASE); | |
644 | cancel_p->callback(Cancel_cb, this); | |
645 | ||
646 | // Arrange for destructor to clean up new-ed widgets | |
647 | end(); | |
648 | ||
649 | // Arrange for window manager closes to do Cancel. | |
650 | callback(Cancel_cb, this); | |
651 | when(FL_WHEN_NEVER); | |
652 | } | |
653 | ||
654 | ||
655 | RegistrationForm_dialog::~RegistrationForm_dialog(void) | |
656 | { | |
657 | } | |
658 | ||
659 | ||
660 | CALL_BACK(RegistrationForm_dialog, SaveForm) | |
661 | { | |
662 | generate_form(); | |
663 | hide(); | |
664 | } | |
665 | ||
666 | ||
667 | CALL_BACK(RegistrationForm_dialog, Cancel) | |
668 | { | |
669 | hide(); | |
670 | } | |
671 | ||
672 | ||
673 | // Generate the registration form with fields filled in. | |
674 | ||
675 | extern const char * const registration_text; | |
676 | static const char * const checkmark = "X"; | |
677 | static const char * const reg_file_name = "mup-reg.txt"; | |
678 | ||
679 | void | |
680 | RegistrationForm_dialog::generate_form() | |
681 | { | |
682 | char * text = strdup(registration_text); | |
683 | ||
684 | // First find all the fields in the registration form. | |
685 | // Do this before filling anything is to avoid any chance | |
686 | // of being confused by what we fill in. | |
687 | char * name = strstr(text, "Name"); | |
688 | char * address = strstr(text, "Address"); | |
689 | char * city = strstr(text, "City"); | |
690 | char * state = strstr(text, "State"); | |
691 | char * postal_code = strstr(text, "Zip code"); | |
692 | char * country = strstr(text, "Country"); | |
693 | char * email = strstr(text, "Email"); | |
694 | char * how_heard = strstr(text, "How did you"); | |
695 | char * how_heard_line2 = strstr(how_heard, "\n\n"); | |
696 | char * Linux = strstr(text, "Linux"); | |
697 | char * Windows = strstr(text, "Windows/MS-DOS"); | |
698 | char * Mac = strstr(text, "Mac"); | |
699 | char * other = strstr(text, "Other"); | |
700 | char * mailing_list = strstr(text, "Yes"); | |
701 | char * regs = strstr(text, "Mup Version"); | |
702 | ||
703 | (void) fill_in(false, name, name_p->value()); | |
704 | (void) fill_in(false, address, address_p->value()); | |
705 | (void) fill_in(false, city, city_p->value()); | |
706 | (void) fill_in(false, state, state_p->value()); | |
707 | (void) fill_in(false, postal_code, postal_code_p->value()); | |
708 | (void) fill_in(false, country, country_p->value()); | |
709 | (void) fill_in(false, email, email_p->value()); | |
710 | const char * remaining; | |
711 | if ((remaining = fill_in(false, how_heard, how_heard_p->value())) | |
712 | != 0) { | |
713 | (void) fill_in(false, how_heard_line2, remaining); | |
714 | } | |
715 | ||
716 | if (Windows_p->value()) { | |
717 | (void) fill_in(true, Windows, checkmark); | |
718 | } | |
719 | if (Linux_p->value()) { | |
720 | (void) fill_in(true, Linux, checkmark); | |
721 | } | |
722 | if (Mac_p->value()) { | |
723 | (void) fill_in(true, Mac, checkmark); | |
724 | } | |
725 | if (other_p->value()) { | |
726 | (void) fill_in(true, other, checkmark); | |
727 | } | |
728 | if (other_OS_p->size() > 0) { | |
729 | (void) fill_in(false, other, other_OS_p->value()); | |
730 | } | |
731 | (void) fill_in(mailing_list_p->value(), mailing_list, checkmark); | |
732 | ||
733 | (void) fill_in(true, regs, num_regs_p->value()); | |
734 | ||
735 | // write to file | |
736 | FILE * regfile; | |
737 | if ((regfile = fopen(reg_file_name, "w")) == 0) { | |
738 | fl_alert("Unable to write registration form file."); | |
739 | } | |
740 | else { | |
741 | (void) fprintf(regfile, "%s", text); | |
742 | (void) fprintf(regfile, "\n\n\t Total due: $%.2f\n", | |
743 | (29.0 + sales_tax()) * atoi(num_regs_p->value())); | |
744 | fclose(regfile); | |
745 | char currdir[FL_PATH_MAX]; | |
746 | ||
747 | if (getcwd(currdir, sizeof(currdir)) == 0) { | |
748 | currdir[0] = '\0'; | |
749 | } | |
750 | hide(); | |
751 | fl_message("Your registration form has been saved in\n" | |
752 | "%s%c%s.", currdir, dir_separator(), reg_file_name); | |
753 | } | |
754 | free(text); | |
755 | } | |
756 | ||
757 | ||
758 | // Fills in value into blank either before or after the given place. | |
759 | // Center the string in the blank. | |
760 | // If it can't fit the entire value, it returns a pointer to | |
761 | // what was left over, otherwise returns zero. | |
762 | ||
763 | const char * | |
764 | RegistrationForm_dialog::fill_in(bool before, char * place, const char * const value) | |
765 | { | |
766 | if (place == 0 || value == 0) { | |
767 | // Shouldn't happen, but better than core dump. | |
768 | return(value); | |
769 | } | |
770 | ||
771 | char * blanks_p; | |
772 | // Find beginning of the blank. | |
773 | if (before) { | |
774 | // back up to beginning of blanks, skipping any spaces | |
775 | for (blanks_p = place - 1; *blanks_p == ' '; blanks_p--) | |
776 | ; | |
777 | for ( ; *blanks_p == '_'; blanks_p--) | |
778 | ; | |
779 | blanks_p++; | |
780 | } | |
781 | else { | |
782 | blanks_p = strchr(place, '_'); | |
783 | } | |
784 | ||
785 | // Figure out how much leading padding to leave | |
786 | int count = strspn(blanks_p, "_"); | |
787 | int length = strlen(value); | |
788 | ||
789 | // Fill in the blank. | |
790 | int padding; | |
791 | if (length > count) { | |
792 | // Try to split at white space. | |
793 | int used_length; | |
794 | for (used_length = count; used_length > 10; used_length--) { | |
795 | if (value[used_length] == ' ') { | |
796 | break; | |
797 | } | |
798 | } | |
799 | padding = (count - used_length) / 2; | |
800 | strncpy(blanks_p + padding, value, used_length); | |
801 | return(value + used_length + 1); | |
802 | } | |
803 | else { | |
804 | padding = (count - length) / 2; | |
805 | strncpy(blanks_p + padding, value, length); | |
806 | return(0); | |
807 | } | |
808 | } | |
809 | ||
810 | ||
811 | // Since Arkkra is based in Illinois, Illinois residents need to pay | |
812 | // sales tax. So try to deduce if the address is Illinois. | |
813 | // Simple minded--may sometimes guess wrong. | |
814 | ||
815 | double | |
816 | RegistrationForm_dialog::sales_tax(void) | |
817 | { | |
818 | const char * pattern; | |
819 | ||
820 | if (state_p->size() > 0) { | |
821 | // Skip leading white space | |
822 | for (pattern = state_p->value(); *pattern == ' '; pattern++) | |
823 | ; | |
824 | if (*pattern == '\0') { | |
825 | return(0.0); | |
826 | } | |
827 | // Remove any trailing white space. | |
828 | // Can't remove in place, since string is const. | |
829 | // So remove from copy. | |
830 | char state[strlen(pattern) + 1]; | |
831 | (void) strcpy(state, pattern); | |
832 | char * p; | |
833 | for (p = state + strlen(pattern) - 1; p > state ; p--) { | |
834 | if (*p == ' ') { | |
835 | *p = '\0'; | |
836 | } | |
837 | else { | |
838 | break; | |
839 | } | |
840 | } | |
841 | ||
842 | if (strcmp(state, "IL") == 0 | |
843 | || strcasecmp(state, "Illinois") == 0 | |
844 | || strcasecmp(state, "Ill.") == 0) { | |
845 | // Sales tax is $2.18 per registration. | |
846 | return(2.18); | |
847 | } | |
848 | } | |
849 | return(0.0); | |
850 | } | |
851 | ||
852 | ||
853 | //----------dialog to let user type in the Registration Key--------------- | |
854 | ||
855 | RegistrationKey_dialog::RegistrationKey_dialog(void) | |
856 | : Fl_Double_Window(250, 100, "Registration Key") | |
857 | { | |
858 | key_p = new Fl_Secret_Input(60, 20, 150, 30, "Key:"); | |
859 | key_p->tooltip("Enter the registration key\n" | |
860 | "you received from Arkkra Enterprises\n" | |
861 | "after you have paid your registration fee."); | |
862 | ||
863 | OK_p = new Fl_Return_Button(25, 60, 80, 30, "OK:"); | |
864 | OK_p->when(FL_WHEN_RELEASE); | |
865 | OK_p->callback(OK_cb, this); | |
866 | ||
867 | cancel_p = new Fl_Button(120, 60, 90, 30, "Cancel"); | |
868 | cancel_p->shortcut(FL_Escape); | |
869 | cancel_p->when(FL_WHEN_RELEASE); | |
870 | cancel_p->callback(Cancel_cb, this); | |
871 | ||
872 | // Arrange for destructor to clean up new-ed widgets | |
873 | end(); | |
874 | ||
875 | // Arrange for window manager closes to do Cancel. | |
876 | callback(Cancel_cb, this); | |
877 | when(FL_WHEN_NEVER); | |
878 | } | |
879 | ||
880 | ||
881 | RegistrationKey_dialog::~RegistrationKey_dialog() | |
882 | { | |
883 | } | |
884 | ||
885 | CALL_BACK(RegistrationKey_dialog, OK) | |
886 | { | |
887 | hide(); | |
888 | FILE * keyfile; | |
889 | if ((keyfile = fopen(magic_file(), "w")) == 0) { | |
890 | fl_alert("Unable to open registration key file %s.", | |
891 | magic_file()); | |
892 | } | |
893 | else { | |
894 | if (fprintf(keyfile, "%s", key_p->value()) != key_p->size()) { | |
895 | fl_alert("Unable to save registration key in %s.", | |
896 | magic_file()); | |
897 | } | |
898 | fclose(keyfile); | |
899 | } | |
900 | ||
901 | // Blank out the field, for if it gets displayed again later. | |
902 | key_p->value(""); | |
903 | } | |
904 | ||
905 | ||
906 | // If user cancels entering registration key, we just hide the window | |
907 | ||
908 | CALL_BACK(RegistrationKey_dialog, Cancel) | |
909 | { | |
910 | hide(); | |
911 | } | |
912 | ||
913 | ||
914 | //-------the Config menu item on main toolbar------------------------------- | |
915 | ||
916 | Config::Config() | |
917 | { | |
918 | locations_p = 0; | |
919 | preferences_p = 0; | |
920 | registrationform_p = 0; | |
921 | registrationkey_p = 0; | |
922 | } | |
923 | ||
924 | Config::~Config() | |
925 | { | |
926 | if (locations_p != 0) { | |
927 | delete locations_p; | |
928 | locations_p = 0; | |
929 | } | |
930 | if (preferences_p != 0) { | |
931 | delete preferences_p; | |
932 | preferences_p = 0; | |
933 | } | |
934 | if (registrationform_p != 0) { | |
935 | delete registrationform_p; | |
936 | registrationform_p = 0; | |
937 | } | |
938 | if (registrationkey_p != 0) { | |
939 | delete registrationkey_p; | |
940 | registrationkey_p = 0; | |
941 | } | |
942 | } | |
943 | ||
944 | ||
945 | // Bring up the dialog for "File Locations" menu item | |
946 | ||
947 | CALL_BACK(Config, FileLocations) | |
948 | { | |
949 | if (locations_p == 0) { | |
950 | // first time, create widget | |
951 | locations_p = new FileLocations_dialog(); | |
952 | } | |
953 | locations_p->show(); | |
954 | } | |
955 | ||
956 | ||
957 | // Bring up the dialog for "Preferences" menu item | |
958 | ||
959 | CALL_BACK(Config, Preferences) | |
960 | { | |
961 | if (preferences_p == 0) { | |
962 | // first time, create widget | |
963 | preferences_p = new Preferences_dialog(); | |
964 | } | |
965 | preferences_p->show(); | |
966 | } | |
967 | ||
968 | ||
969 | // Bring up dialog for filling in Registration Form | |
970 | ||
971 | CALL_BACK(Config, RegistrationForm) | |
972 | { | |
973 | if (registrationform_p == 0) { | |
974 | // first time, create widget | |
975 | registrationform_p = new RegistrationForm_dialog(); | |
976 | } | |
977 | registrationform_p->show(); | |
978 | fl_message("Note: For fastest service, you can register via\n" | |
979 | "credit card at http://www.arkkra.com/doc/credtcrd.html\n" | |
980 | "rather than filling out and mailing in a paper form."); | |
981 | } | |
982 | ||
983 | ||
984 | // Bring up dialog to let user enter their registration key after paying | |
985 | ||
986 | CALL_BACK(Config, RegistrationKey) | |
987 | { | |
988 | if (registrationkey_p == 0) { | |
989 | // first time, create widget | |
990 | registrationkey_p = new RegistrationKey_dialog(); | |
991 | } | |
992 | registrationkey_p->show(); | |
993 | } | |
994 | ||
995 | ||
996 | // Translate font name to FL_Font value. | |
997 | ||
998 | Fl_Font | |
999 | Config::fontvalue(const char * fontname) | |
1000 | { | |
1001 | int n; | |
1002 | // Linear search of the list (it is short). | |
1003 | for (n = 0; n < Fontlistlength; n++) { | |
1004 | if (strcmp(Fontlist[n].name, fontname) == 0) { | |
1005 | return(Fontlist[n].value); | |
1006 | } | |
1007 | } | |
1008 | // Hmmm. Not found. Should not happen. Hunt for default | |
1009 | for (n = 0; n < Fontlistlength; n++) { | |
1010 | if (strcmp(Fontlist[n].name, Default_editor_font) == 0) { | |
1011 | return(Fontlist[n].value); | |
1012 | } | |
1013 | } | |
1014 | // Wow. Can't find default either. Punt. | |
1015 | return(FL_COURIER); | |
1016 | } | |
1017 | ||
1018 | ||
1019 | //--------------------- class that lets other classes register a callback | |
1020 | // to be called for changes in font/size | |
1021 | ||
1022 | ||
1023 | // List of callbacks for when font/size change | |
1024 | Font_change_registration * Font_change_registration::list_p = 0; | |
1025 | ||
1026 | Font_change_registration::Font_change_registration(Font_change_callback func, void * arg) | |
1027 | { | |
1028 | // Save callback information. | |
1029 | callback = func; | |
1030 | callback_arg = arg; | |
1031 | ||
1032 | // Add to list of callbacks. | |
1033 | next = list_p; | |
1034 | list_p = this; | |
1035 | ||
1036 | // Set the font and size on this newly registered widget. | |
1037 | // Look up the current values and call the newly registered callback. | |
1038 | char * fontstr; | |
1039 | (void) Preferences_p->get(Editor_font_preference, fontstr, | |
1040 | Default_editor_font); | |
1041 | Fl_Font font = Config::fontvalue(fontstr); | |
1042 | ||
1043 | char * sizestr; | |
1044 | (void) Preferences_p->get(Editor_size_preference, sizestr, | |
1045 | Default_editor_size); | |
1046 | unsigned char size = (unsigned char) atoi(sizestr); | |
1047 | if (size < Min_size) { | |
1048 | size = (unsigned char) atoi(Default_editor_size); | |
1049 | } | |
1050 | (*func)(arg, font, size); | |
1051 | } | |
1052 | ||
1053 | Font_change_registration::~Font_change_registration(void) | |
1054 | { | |
1055 | // Remove callback from linked list | |
1056 | if (list_p == this) { | |
1057 | list_p = next; | |
1058 | } | |
1059 | else { | |
1060 | Font_change_registration * fcr_p; | |
1061 | for (fcr_p = list_p; fcr_p != 0; fcr_p = fcr_p->next) { | |
1062 | if (fcr_p->next == this) { | |
1063 | fcr_p->next = next; | |
1064 | return; | |
1065 | } | |
1066 | } | |
1067 | } | |
1068 | } | |
1069 | ||
1070 | ||
1071 | // Notify all classes that want to know about font/size changes, | |
1072 | // by calling the callback function they registered. | |
1073 | ||
1074 | void | |
1075 | Font_change_registration::run_callbacks(Fl_Font font, unsigned char size) | |
1076 | { | |
1077 | // Avoid unreadably small sizes and division by zero if | |
1078 | // earlier atoi() of size failed due to bad data | |
1079 | // (e.g., user hand-editing the preference file) | |
1080 | if (size < Min_size || size > Max_size) { | |
1081 | size = (unsigned char) atoi(Default_editor_size); | |
1082 | } | |
1083 | ||
1084 | // Walk through list of registered callbacks, calling each. | |
1085 | Font_change_registration * fcr_p; | |
1086 | for (fcr_p = list_p; fcr_p != 0; fcr_p = fcr_p->next) { | |
1087 | (*(fcr_p->callback))(fcr_p->callback_arg, font, size); | |
1088 | } | |
1089 | } |