chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mupmate / Config.C
CommitLineData
69695f33
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
21FileLocations_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
92FileLocations_dialog::~FileLocations_dialog()
93{
94}
95
96
97//--- Callback for when user clicks "Apply" on FileLocations dialog.
98// Save values in preferences file.
99
100CALL_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
222CALL_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
232void
233FileLocations_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.
262static 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};
280static const int Fontlistlength = sizeof(Fontlist) / sizeof(Fontlist[0]);
281
282// Window to ask user preferences, like editor font, size, etc.
283
284Preferences_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
347Preferences_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
356CALL_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
375CALL_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
424CALL_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
434void
435Preferences_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
479void
480Preferences_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
554RegistrationForm_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
655RegistrationForm_dialog::~RegistrationForm_dialog(void)
656{
657}
658
659
660CALL_BACK(RegistrationForm_dialog, SaveForm)
661{
662 generate_form();
663 hide();
664}
665
666
667CALL_BACK(RegistrationForm_dialog, Cancel)
668{
669 hide();
670}
671
672
673// Generate the registration form with fields filled in.
674
675extern const char * const registration_text;
676static const char * const checkmark = "X";
677static const char * const reg_file_name = "mup-reg.txt";
678
679void
680RegistrationForm_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
763const char *
764RegistrationForm_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
815double
816RegistrationForm_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
855RegistrationKey_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
881RegistrationKey_dialog::~RegistrationKey_dialog()
882{
883}
884
885CALL_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
908CALL_BACK(RegistrationKey_dialog, Cancel)
909{
910 hide();
911}
912
913
914//-------the Config menu item on main toolbar-------------------------------
915
916Config::Config()
917{
918 locations_p = 0;
919 preferences_p = 0;
920 registrationform_p = 0;
921 registrationkey_p = 0;
922}
923
924Config::~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
947CALL_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
959CALL_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
971CALL_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
986CALL_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
998Fl_Font
999Config::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
1024Font_change_registration * Font_change_registration::list_p = 0;
1025
1026Font_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
1053Font_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
1074void
1075Font_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}