--- /dev/null
+/* Copyright (c) 2006 by Arkkra Enterprises */
+/* All rights reserved */
+
+// Code for the Config menu item on the main toolbar
+
+#include "globals.H"
+#include "Preferences.H"
+#include "Config.H"
+#include "Main.H"
+#include "utils.H"
+#include <FL/fl_ask.H>
+#include <FL/Fl_Tooltip.H>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+
+// Window to ask user where files and tools are located
+
+FileLocations_dialog::FileLocations_dialog(void)
+ : Fl_Double_Window(620, 300, "Mupmate File Locations")
+{
+ mup_documentation_p = new Fl_Input(200, 30, 400, 30, "Mup Documentation Folder");
+ mup_documentation_p->tooltip("Set where Mup documentation\n"
+ "files are installed on your system.\n"
+ "This folder must contain the \"uguide\"\n"
+ "folder that contains the HTML version\n"
+ "of the Mup User's Guide.");
+
+ mup_program_p = new Fl_Input(200, 65, 400, 30, "Mup Command Path");
+ mup_program_p->tooltip("Set where the Mup program\n"
+ "is installed on your system.");
+
+ music_files_p = new Fl_Input(200, 100, 400, 30, "Folder for Mup Files");
+ music_files_p->tooltip("Set the default folder for storing\n"
+ "your Mup files (.mup input files,\n"
+ "and .ps and .mid output files).");
+
+ muppath_p = new Fl_Input(200, 135, 400, 30, "Folder for Mup include Files");
+ static char include_tip_text[200];
+ (void) sprintf(include_tip_text,
+ "Set the default folder (or list of folders,\n"
+ "separated by %c characters) for\n"
+ "storing your Mup \"include\" files.",
+ path_separator());
+ muppath_p->tooltip(include_tip_text);
+
+ viewer_p = new Fl_Input(200, 170, 400, 30, "PostScript Viewer Path");
+ viewer_p->tooltip("Set which PostScript viewing program\n"
+ "to use for displaying Mup output.\n"
+#ifdef OS_LIKE_WIN32
+ "This is typically GSview32.exe\n"
+ "which you can obtain from\n"
+ "http://www.cs.wisc.edu/~ghost/gsview/"
+#else
+ "The \"gv\" program is a common choice."
+#endif
+ );
+
+ player_p = new Fl_Input(200, 205, 400, 30, "MIDI Player Path");
+ player_p->tooltip("Set which MIDI player program\n"
+ "to use for playing Mup MIDI output.\n"
+#ifdef OS_LIKE_WIN32
+ "This is typically wmplayer.exe"
+#else
+ "Common choices include xplaymidi or timidity."
+#endif
+ );
+
+ apply_p = new Fl_Return_Button(50, 255, 100, 30, "Apply");
+ apply_p->when(FL_WHEN_RELEASE);
+ apply_p->callback(Apply_cb, this);
+
+ cancel_p = new Fl_Button(w() - 150, 255, 100, 30, "Cancel");
+ cancel_p->shortcut(FL_Escape);
+ cancel_p->when(FL_WHEN_RELEASE);
+ cancel_p->callback(Cancel_cb, this);
+
+ // Populate the fields
+ set_current_values();
+
+ // Arrange for destructor to clean up new-ed widgets
+ end();
+
+ // Arrange for window manager closes to do Cancel.
+ callback(Cancel_cb, this);
+ when(FL_WHEN_NEVER);
+}
+
+
+FileLocations_dialog::~FileLocations_dialog()
+{
+}
+
+
+//--- Callback for when user clicks "Apply" on FileLocations dialog.
+// Save values in preferences file.
+
+CALL_BACK(FileLocations_dialog, Apply)
+{
+ bool changes = false; // if any changes made
+ bool error = false; // if any errors found
+ char location[FL_PATH_MAX];
+
+ // Documentation location
+ if (mup_documentation_p->size() > 0) {
+ (void) Preferences_p->set(Mup_documentation_location,
+ mup_documentation_p->value());
+ changes = true;
+ // Documentation being wrong means User's Guide can't be
+ // shown, which is bad, although not fatal.
+ if ( ! fl_filename_isdir(mup_documentation_p->value()) ) {
+ fl_alert("Location for Mup documentation is not a valid folder.");
+ error = true;
+ }
+ else {
+ if (access(users_guide_index_file(
+ mup_documentation_p->value()), F_OK)
+ != 0) {
+ fl_alert("Folder specified for Mup documentation it not correct:\n"
+ "it does not contain the Mup User's Guide.");
+ error = true;
+ }
+ }
+ }
+
+ // Location of Mup program
+ if (mup_program_p->size() > 0) {
+ if (find_executable(mup_program_p->value(), location)) {
+ (void) Preferences_p->set(Mup_program_location,
+ mup_program_p->value());
+ changes = true;
+ }
+ else {
+ fl_alert("Location specified for Mup program is not valid.");
+ error = true;
+ }
+ }
+
+ // Default folder for Mup input files
+ if (music_files_p->size() > 0) {
+ if (chdir(music_files_p->value()) != 0) {
+ fl_alert("Value for \"Folder for Mup Files\" is not a valid folder.");
+ error = true;
+ }
+ else {
+ (void) Preferences_p->set(Music_files_location,
+ music_files_p->value());
+ changes = true;
+ }
+ }
+
+ // $MUPPATH value
+ if (muppath_p->size() > 0) {
+ (void) Preferences_p->set(MUPPATH_location, muppath_p->value());
+ // Set $MUPPATH
+ set_muppath(muppath_p->value());
+ changes = true;
+ // Setting MUPPATH correctly is only important if user
+ // actually uses it, which many people won't, but if it is set
+ // to something invalid, we give a warning.
+ // Since MUPPATH can be a list, we check each component
+ // in the list.
+ char pathcopy[muppath_p->size() + 1];
+ (void) strcpy(pathcopy, muppath_p->value());
+ char * component_p = pathcopy;
+ char * sep_p; // where path separator appears in list
+ do {
+ if ((sep_p = strchr(component_p, path_separator())) != 0) {
+ *sep_p = '\0';
+ }
+ if (strlen(component_p) > 0 &&
+ ! fl_filename_isdir(component_p)) {
+ fl_alert("Location for Mup include files\n"
+ "\"%s\"\nis not a valid folder.",
+ component_p);
+ error = true;
+ }
+ component_p += strlen(component_p) + 1;
+ } while (sep_p != 0);
+ }
+
+ // PostScript viewer program
+ if (viewer_p->size() > 0) {
+ if (find_executable(viewer_p->value(), location)) {
+ (void) Preferences_p->set(Viewer_location, viewer_p->value());
+ changes = true;
+ }
+ else {
+ fl_alert("Location specified for PostScript viewer is not valid.");
+ error = true;
+ }
+ }
+
+ // MIDI player
+ if (player_p->size() > 0) {
+ if (find_executable(player_p->value(), location)) {
+ (void) Preferences_p->set(MIDI_player_location, player_p->value());
+ changes = true;
+ }
+ else {
+ fl_alert("Location specified for MIDI player is not valid.");
+ error = true;
+ }
+ }
+
+ // If any changes, persist the data.
+ if (changes) {
+ Preferences_p->flush();
+ }
+
+ // If there were errors, leave form up so user can try to correct them.
+ if ( ! error ) {
+ hide();
+ }
+}
+
+
+//--- callback for when user clicks "Cancel" on FileLocations dialog
+
+CALL_BACK(FileLocations_dialog, Cancel)
+{
+ hide();
+ // Put all the original settings back on the form
+ set_current_values();
+}
+
+
+// Populate form with the current default values from user's preferences.
+
+void
+FileLocations_dialog::set_current_values(void)
+{
+ char * val;
+ (void) Preferences_p->get(Mup_documentation_location, val,
+ Default_Mup_documentation_location);
+ mup_documentation_p->value(val);
+
+ (void) Preferences_p->get(Mup_program_location, val,
+ Default_Mup_program_location);
+ mup_program_p->value(val);
+
+ (void) Preferences_p->get(Music_files_location, val,
+ Default_music_files_location);
+ music_files_p->value(val);
+ (void) Preferences_p->get(MUPPATH_location, val,
+ Default_MUPPATH_location);
+ muppath_p->value(val);
+ (void) Preferences_p->get(Viewer_location, val,
+ Default_viewer_location);
+ viewer_p->value(val);
+ (void) Preferences_p->get(MIDI_player_location, val,
+ Default_MIDI_player_location);
+ player_p->value(val);
+}
+
+
+//-----------------------------------------------------------------
+
+// List of standard FLTK fonts, and info to map name to menu entry.
+static struct Font {
+ const char * name;
+ Fl_Font value;
+ int menu_offset;
+} Fontlist[] = {
+ { "Courier", FL_COURIER },
+ { "Courier Bold", FL_COURIER_BOLD },
+ { "Courier Italic", FL_COURIER_ITALIC },
+ { "Courier Bold Italic", FL_COURIER_BOLD_ITALIC },
+ { "Helvetica", FL_HELVETICA },
+ { "Helvetica Bold", FL_HELVETICA_BOLD },
+ { "Helvetica Italic", FL_HELVETICA_ITALIC },
+ { "Helvetica Bold Italic", FL_HELVETICA_BOLD_ITALIC },
+ { "Times", FL_TIMES },
+ { "Times Bold", FL_TIMES_BOLD },
+ { "Times Italic", FL_TIMES_ITALIC },
+ { "Times Bold Italic", FL_TIMES_BOLD_ITALIC },
+};
+static const int Fontlistlength = sizeof(Fontlist) / sizeof(Fontlist[0]);
+
+// Window to ask user preferences, like editor font, size, etc.
+
+Preferences_dialog::Preferences_dialog(void)
+ : Fl_Double_Window(400, 280, "Mupmate Preferences")
+{
+ // Make widget for user's editor font choice.
+ font_p = new Fl_Choice(20, 40, 210, 30, "Text Font");
+ font_p->tooltip("Select the font to be used\n"
+ "in the editor window where you\n"
+ "type in Mup input. It is also used\n"
+ "for the Help and error report text.");
+ // Arrange to reset size menu if font selection changes
+ font_p->callback(fontchg_cb, this);
+ font_p->when(FL_WHEN_CHANGED);
+ font_p->align(FL_ALIGN_TOP_LEFT);
+
+ // Make widget for user's editor size choice.
+ size_p = new Fl_Choice(270, 40, 100, 30, "Text Size");
+ size_p->tooltip("Select the text size to be used\n"
+ "in the editor window where you\n"
+ "type in Mup input. It is also used\n"
+ "for the Help and error report text.");
+ size_p->align(FL_ALIGN_TOP_LEFT);
+
+ auto_display_p = new Fl_Check_Button(20, 90, 180, 30,
+ "Auto-Display on Save");
+ auto_display_p->tooltip("Set whether your music\n"
+ "is displayed automatically\n"
+ "whenever you save your Mup file.");
+
+ auto_save_p = new Fl_Check_Button(w() - 170, 90, 150, 30,
+ "Auto-Save on Run");
+ auto_save_p->tooltip("Set whether your music is saved\n"
+ "automatically whenever you do Display, Play,\n"
+ "Write PostScript or Write MIDI from the Run menu.");
+
+ tooltips_delay_p = new Fl_Value_Input(150, 155, 100, 30, "Tool Tip Delay");
+ tooltips_delay_p->minimum(0.0);
+ tooltips_delay_p->precision(3);
+ tooltips_delay_p->tooltip("Set how long to delay before showing\n"
+ "tool tips, in seconds.\n");
+ tooltips_delay_p->align(FL_ALIGN_TOP_LEFT);
+
+ // Create and configure widget for Apply button
+ apply_p = new Fl_Return_Button(60, 215, 100, 30, "Apply");
+ apply_p->when(FL_WHEN_RELEASE);
+ apply_p->callback(Apply_cb, this);
+
+ // Create and configure widget for Cancel button
+ cancel_p = new Fl_Button(w() - 160, 215, 100, 30, "Cancel");
+ cancel_p->shortcut(FL_Escape);
+ cancel_p->when(FL_WHEN_RELEASE);
+ cancel_p->callback(Cancel_cb, this);
+
+ // Populate the fields
+ set_current_values();
+
+ // Arrange for destructor to clean up new-ed widgets
+ end();
+
+ // Arrange for window manager closes to do Cancel.
+ callback(Cancel_cb, this);
+ when(FL_WHEN_NEVER);
+}
+
+Preferences_dialog::~Preferences_dialog()
+{
+}
+
+
+//---- Callback for when user changes font selection.
+// This re-creates the size menu to be what sizes are available
+// for that font, since each font could have a different set of sizes.
+
+CALL_BACK(Preferences_dialog, fontchg)
+{
+ unsigned char size;
+ if (size_p->mvalue() != 0) {
+ size = atoi(size_p->mvalue()->text);
+ }
+ else {
+ // Shouldn't really be possible to get here,
+ // but better to be safe.
+ size = (unsigned char) atoi(Default_editor_size);
+ }
+
+ set_size_list(Config::fontvalue(font_p->mvalue()->text), size);
+}
+
+
+//--- Callback for when user clicks Apply in Preferences
+// Save the new values.
+
+CALL_BACK(Preferences_dialog, Apply)
+{
+ Fl_Font font;
+ int n;
+
+ Preferences_p->set(Auto_display_preference, auto_display_p->value());
+ Preferences_p->set(Auto_save_preference, auto_save_p->value());
+ Preferences_p->set(Tooltips_delay_preference, tooltips_delay_p->value());
+ Fl_Tooltip::delay(tooltips_delay_p->value());
+
+ // Convert font menu selection into font value.
+ for (n = 0; n < Fontlistlength; n++) {
+ if (Fontlist[n].menu_offset == font_p->value()) {
+ Preferences_p->set(Editor_font_preference, Fontlist[n].name);
+ font = Fontlist[n].value;
+ break;
+ }
+ }
+ if (n >= Fontlistlength) {
+ // Selection not valid. Fall back to using the default.
+ char * fontname;
+ (void) Preferences_p->get(Editor_font_preference, fontname,
+ Default_editor_font);
+ font = Config::fontvalue(fontname);
+ }
+
+ // Save size value.
+ unsigned char size;
+ if (size_p->text() != 0) {
+ (void) Preferences_p->set(Editor_size_preference, size_p->text());
+ size = (unsigned char) atoi(size_p->text());
+ }
+ else {
+ size = (unsigned char) atoi(Default_editor_size);
+ }
+
+ // Persist the data.
+ Preferences_p->flush();
+
+ // Actually change the font/size in all relevant windows.
+ // Windows that want to know about these changes register a callback,
+ // so we call them.
+ Font_change_registration::run_callbacks(font, size);
+
+ hide();
+}
+
+//--- callback for when user clicks Cancel in Preferences
+
+CALL_BACK(Preferences_dialog, Cancel)
+{
+ hide();
+ // Put all the original settings back on the form
+ set_current_values();
+}
+
+
+// Populate form with current values from user's preferences
+
+void
+Preferences_dialog::set_current_values(void)
+{
+ int auto_display;
+ (void) Preferences_p->get(Auto_display_preference, auto_display,
+ Default_auto_display);
+ auto_display_p->value(auto_display);
+
+ int auto_save;
+ (void) Preferences_p->get(Auto_save_preference, auto_save,
+ Default_auto_save);
+ auto_save_p->value(auto_save);
+
+ double tooltips_delay;
+ (void) Preferences_p->get(Tooltips_delay_preference, tooltips_delay,
+ Default_tooltips_delay);
+ tooltips_delay_p->value(tooltips_delay);
+
+ char * fontname;
+ (void) Preferences_p->get(Editor_font_preference, fontname,
+ Default_editor_font);
+ Fl_Font font = Config::fontvalue(fontname);
+ // Populate font menu
+ font_p->clear();
+ for (int i = 0; i < Fontlistlength; i++) {
+ Fontlist[i].menu_offset =
+ font_p->add(Fontlist[i].name, 0, 0, 0, 0);
+ // Set the current value
+ if (Fontlist[i].value == font) {
+ font_p->value(Fontlist[i].menu_offset);
+ }
+ }
+
+ char * sizename;
+ (void) Preferences_p->get(Editor_size_preference, sizename,
+ Default_editor_size);
+ unsigned char size = (unsigned char) atoi(sizename);
+ // Populate the size menu
+ set_size_list(font, size);
+}
+
+
+// When font selection changes, re-create the size menu,
+// because each font could have different sizes available.
+
+void
+Preferences_dialog::set_size_list(Fl_Font font, uchar curr_size)
+{
+ // Avoid really tiny sizes, or more importantly, zero, like if an atoi
+ // failed, because otherwise FLTK may try to divide by zero.
+ // Also limit to a maximum size.
+ if (curr_size < Min_size || curr_size > Max_size) {
+ curr_size = (unsigned char) atoi(Default_editor_size);
+ }
+
+ // Clean out the current menu if any
+ size_p->clear();
+
+ // Populate the menu
+ int * sizelist;
+ int numsizes = Fl::get_font_sizes(font, sizelist);
+
+ // Set current value to ridiculous value, then find closest
+ int currvalue = 5000;
+
+ int i; // index through sizelist
+ int menu_index; // index into menu
+ for (i = menu_index = 0; i < numsizes; i++) {
+ if (sizelist[i] == 0) {
+ // This means font is scaleable
+ continue;
+ }
+ if (sizelist[i] > Max_size) {
+ break;
+ }
+ char num_as_string[4];
+ (void) sprintf(num_as_string, "%d", sizelist[i]);
+ size_p->add(num_as_string, 0, 0, 0, 0);
+ // If this is closest index to desired size, mark as current
+ if ( abs(sizelist[i] - currvalue) > abs(sizelist[i] - curr_size) ) {
+ currvalue = sizelist[i];
+ size_p->value(menu_index);
+ }
+ menu_index++;
+ }
+ if (numsizes == 0 || (numsizes == 1 && sizelist[0] == 0)) {
+ // Either no available sizes at all, or only
+ // scaleable, with no special "good" sizes,
+ // so we pick some and hope for the best.
+ size_p->add("10", 0, 0, 0, 0);
+ if (curr_size <= 11) {
+ size_p->value(0);
+ }
+ size_p->add("12", 0, 0, 0, 0);
+ if (curr_size >= 12 && curr_size <= 13) {
+ size_p->value(1);
+ }
+ size_p->add("14", 0, 0, 0, 0);
+ if (curr_size >= 14 && curr_size <= 15) {
+ size_p->value(2);
+ }
+ size_p->add("16", 0, 0, 0, 0);
+ if (curr_size >= 16 && curr_size <= 17) {
+ size_p->value(3);
+ }
+ size_p->add("18", 0, 0, 0, 0);
+ if (curr_size >= 18) {
+ size_p->value(4);
+ }
+ }
+}
+
+//----------dialog to let user fill in the Registration form---------------
+
+#define MULTI_OS "Note that if you wish to use Mup\n" \
+ "on multiple machines simultaneously,\n" \
+ "you need to purchase a registration\n" \
+ "for each."
+
+
+RegistrationForm_dialog::RegistrationForm_dialog(void)
+ : Fl_Double_Window(500, 450, "Mup Registration")
+{
+ name_p = new Fl_Input(80, 20, 400, 30, "Name");
+ name_p->tooltip("Enter your name.");
+
+ address_p = new Fl_Input(80, 60, 400, 30, "Address");
+ address_p->tooltip("Enter your street address.");
+
+ city_p = new Fl_Input(80, 100, 180, 30, "City");
+ city_p->tooltip("Enter the name of the city\n"
+ "in which you live.");
+ state_p = new Fl_Input(380, 100, 100, 30, "State/Province");
+ state_p->tooltip("Enter the state or province (if any)\n"
+ "in which you live.");
+
+ postal_code_p = new Fl_Input(110, 140, 120, 30, "Postal Code");
+ postal_code_p->tooltip("Enter your zip code or postal code\n"
+ "as appropriate for your country.");
+
+ country_p = new Fl_Input(320, 140, 160, 30, "Country");
+ country_p->tooltip("Enter the name of the country in which\n"
+ "you live (optional if in USA).");
+
+ email_p = new Fl_Input(110, 180, 370, 30, "Email address");
+ email_p->tooltip("Enter your email address. This will only be used\n"
+ "to send you your registration key\n"
+ "and announcements of future (free) Mup upgrades.");
+
+ how_heard_p = new Fl_Input(20, 240, 460, 30, "Where did you hear about Mup?");
+ how_heard_p->align(FL_ALIGN_TOP_LEFT);
+ how_heard_p->tooltip("Please let us know how you learned about Mup\n"
+ "(a particular web link, magazine, book, etc.).");
+
+ // Checkboxes for OS types.
+ // If we are compiled for a particular OS,
+ // automatically check that box.
+ Windows_p = new Fl_Check_Button(20, 280, 80, 30, "Windows");
+#ifdef __WIN32
+ Windows_p->value(1);
+#endif
+ Windows_p->tooltip("Check here if you plan to run Mup\n"
+ "under Microsoft Windows.\n"
+ MULTI_OS);
+
+ Mac_p = new Fl_Check_Button(110, 280, 50, 30, "Mac");
+ Mac_p->tooltip("Check here if you plan to run Mup\n"
+ "under Apple Mac OS.\n"
+ MULTI_OS);
+#ifdef __APPLE__
+ Mac_p->value(1);
+#endif
+
+ Linux_p = new Fl_Check_Button(170, 280, 60, 30, "Linux");
+ Linux_p->tooltip("Check here if you plan to run Mup\n"
+ "under Linux. "
+ MULTI_OS);
+#ifdef __linux
+ Linux_p->value(1);
+#endif
+
+ other_p = new Fl_Check_Button(240, 280, 60, 30, "Other");
+ other_p->tooltip("Check here if you plan to run Mup\n"
+ "under an OS not listed.\n"
+ MULTI_OS);
+ other_OS_p = new Fl_Input(300, 280, 180, 30, "");
+ other_OS_p->tooltip("If you checked \"Other,\"\n"
+ "specify the name of the Operating System.\n"
+ MULTI_OS);
+
+ mailing_list_p = new Fl_Check_Button(100, 320, 350, 30,
+ "I would like to join the Mup user's mailing list");
+ mailing_list_p->tooltip("There is a email mailing list for registered\n"
+ "Mup users, for sharing information about Mup.\n"
+ "Check here if you want to join the list.\n"
+ "There is no extra charge, and you can always\n"
+ "subscribe/unsubscribe later if you change your mind.");
+
+ num_regs_p = new Positive_Int_Input(340, 355, 60, 30,
+ "Number of Registrations ($29 each)");
+ num_regs_p->value("1");
+ num_regs_p->tooltip("Enter the number of registrations\n"
+ "you wish to purchase.\n"
+ MULTI_OS);
+
+ save_form_p = new Fl_Return_Button(40, h() - 50, 120, 30, "Save Form");
+ save_form_p->callback(SaveForm_cb, this);
+ cancel_p = new Fl_Button(w() - 160, h() - 50, 120, 30, "Cancel");
+ cancel_p->shortcut(FL_Escape);
+ cancel_p->when(FL_WHEN_RELEASE);
+ cancel_p->callback(Cancel_cb, this);
+
+ // Arrange for destructor to clean up new-ed widgets
+ end();
+
+ // Arrange for window manager closes to do Cancel.
+ callback(Cancel_cb, this);
+ when(FL_WHEN_NEVER);
+}
+
+
+RegistrationForm_dialog::~RegistrationForm_dialog(void)
+{
+}
+
+
+CALL_BACK(RegistrationForm_dialog, SaveForm)
+{
+ generate_form();
+ hide();
+}
+
+
+CALL_BACK(RegistrationForm_dialog, Cancel)
+{
+ hide();
+}
+
+
+// Generate the registration form with fields filled in.
+
+extern const char * const registration_text;
+static const char * const checkmark = "X";
+static const char * const reg_file_name = "mup-reg.txt";
+
+void
+RegistrationForm_dialog::generate_form()
+{
+ char * text = strdup(registration_text);
+
+ // First find all the fields in the registration form.
+ // Do this before filling anything is to avoid any chance
+ // of being confused by what we fill in.
+ char * name = strstr(text, "Name");
+ char * address = strstr(text, "Address");
+ char * city = strstr(text, "City");
+ char * state = strstr(text, "State");
+ char * postal_code = strstr(text, "Zip code");
+ char * country = strstr(text, "Country");
+ char * email = strstr(text, "Email");
+ char * how_heard = strstr(text, "How did you");
+ char * how_heard_line2 = strstr(how_heard, "\n\n");
+ char * Linux = strstr(text, "Linux");
+ char * Windows = strstr(text, "Windows/MS-DOS");
+ char * Mac = strstr(text, "Mac");
+ char * other = strstr(text, "Other");
+ char * mailing_list = strstr(text, "Yes");
+ char * regs = strstr(text, "Mup Version");
+
+ (void) fill_in(false, name, name_p->value());
+ (void) fill_in(false, address, address_p->value());
+ (void) fill_in(false, city, city_p->value());
+ (void) fill_in(false, state, state_p->value());
+ (void) fill_in(false, postal_code, postal_code_p->value());
+ (void) fill_in(false, country, country_p->value());
+ (void) fill_in(false, email, email_p->value());
+ const char * remaining;
+ if ((remaining = fill_in(false, how_heard, how_heard_p->value()))
+ != 0) {
+ (void) fill_in(false, how_heard_line2, remaining);
+ }
+
+ if (Windows_p->value()) {
+ (void) fill_in(true, Windows, checkmark);
+ }
+ if (Linux_p->value()) {
+ (void) fill_in(true, Linux, checkmark);
+ }
+ if (Mac_p->value()) {
+ (void) fill_in(true, Mac, checkmark);
+ }
+ if (other_p->value()) {
+ (void) fill_in(true, other, checkmark);
+ }
+ if (other_OS_p->size() > 0) {
+ (void) fill_in(false, other, other_OS_p->value());
+ }
+ (void) fill_in(mailing_list_p->value(), mailing_list, checkmark);
+
+ (void) fill_in(true, regs, num_regs_p->value());
+
+ // write to file
+ FILE * regfile;
+ if ((regfile = fopen(reg_file_name, "w")) == 0) {
+ fl_alert("Unable to write registration form file.");
+ }
+ else {
+ (void) fprintf(regfile, "%s", text);
+ (void) fprintf(regfile, "\n\n\t Total due: $%.2f\n",
+ (29.0 + sales_tax()) * atoi(num_regs_p->value()));
+ fclose(regfile);
+ char currdir[FL_PATH_MAX];
+
+ if (getcwd(currdir, sizeof(currdir)) == 0) {
+ currdir[0] = '\0';
+ }
+ hide();
+ fl_message("Your registration form has been saved in\n"
+ "%s%c%s.", currdir, dir_separator(), reg_file_name);
+ }
+ free(text);
+}
+
+
+// Fills in value into blank either before or after the given place.
+// Center the string in the blank.
+// If it can't fit the entire value, it returns a pointer to
+// what was left over, otherwise returns zero.
+
+const char *
+RegistrationForm_dialog::fill_in(bool before, char * place, const char * const value)
+{
+ if (place == 0 || value == 0) {
+ // Shouldn't happen, but better than core dump.
+ return(value);
+ }
+
+ char * blanks_p;
+ // Find beginning of the blank.
+ if (before) {
+ // back up to beginning of blanks, skipping any spaces
+ for (blanks_p = place - 1; *blanks_p == ' '; blanks_p--)
+ ;
+ for ( ; *blanks_p == '_'; blanks_p--)
+ ;
+ blanks_p++;
+ }
+ else {
+ blanks_p = strchr(place, '_');
+ }
+
+ // Figure out how much leading padding to leave
+ int count = strspn(blanks_p, "_");
+ int length = strlen(value);
+
+ // Fill in the blank.
+ int padding;
+ if (length > count) {
+ // Try to split at white space.
+ int used_length;
+ for (used_length = count; used_length > 10; used_length--) {
+ if (value[used_length] == ' ') {
+ break;
+ }
+ }
+ padding = (count - used_length) / 2;
+ strncpy(blanks_p + padding, value, used_length);
+ return(value + used_length + 1);
+ }
+ else {
+ padding = (count - length) / 2;
+ strncpy(blanks_p + padding, value, length);
+ return(0);
+ }
+}
+
+
+// Since Arkkra is based in Illinois, Illinois residents need to pay
+// sales tax. So try to deduce if the address is Illinois.
+// Simple minded--may sometimes guess wrong.
+
+double
+RegistrationForm_dialog::sales_tax(void)
+{
+ const char * pattern;
+
+ if (state_p->size() > 0) {
+ // Skip leading white space
+ for (pattern = state_p->value(); *pattern == ' '; pattern++)
+ ;
+ if (*pattern == '\0') {
+ return(0.0);
+ }
+ // Remove any trailing white space.
+ // Can't remove in place, since string is const.
+ // So remove from copy.
+ char state[strlen(pattern) + 1];
+ (void) strcpy(state, pattern);
+ char * p;
+ for (p = state + strlen(pattern) - 1; p > state ; p--) {
+ if (*p == ' ') {
+ *p = '\0';
+ }
+ else {
+ break;
+ }
+ }
+
+ if (strcmp(state, "IL") == 0
+ || strcasecmp(state, "Illinois") == 0
+ || strcasecmp(state, "Ill.") == 0) {
+ // Sales tax is $2.18 per registration.
+ return(2.18);
+ }
+ }
+ return(0.0);
+}
+
+
+//----------dialog to let user type in the Registration Key---------------
+
+RegistrationKey_dialog::RegistrationKey_dialog(void)
+ : Fl_Double_Window(250, 100, "Registration Key")
+{
+ key_p = new Fl_Secret_Input(60, 20, 150, 30, "Key:");
+ key_p->tooltip("Enter the registration key\n"
+ "you received from Arkkra Enterprises\n"
+ "after you have paid your registration fee.");
+
+ OK_p = new Fl_Return_Button(25, 60, 80, 30, "OK:");
+ OK_p->when(FL_WHEN_RELEASE);
+ OK_p->callback(OK_cb, this);
+
+ cancel_p = new Fl_Button(120, 60, 90, 30, "Cancel");
+ cancel_p->shortcut(FL_Escape);
+ cancel_p->when(FL_WHEN_RELEASE);
+ cancel_p->callback(Cancel_cb, this);
+
+ // Arrange for destructor to clean up new-ed widgets
+ end();
+
+ // Arrange for window manager closes to do Cancel.
+ callback(Cancel_cb, this);
+ when(FL_WHEN_NEVER);
+}
+
+
+RegistrationKey_dialog::~RegistrationKey_dialog()
+{
+}
+
+CALL_BACK(RegistrationKey_dialog, OK)
+{
+ hide();
+ FILE * keyfile;
+ if ((keyfile = fopen(magic_file(), "w")) == 0) {
+ fl_alert("Unable to open registration key file %s.",
+ magic_file());
+ }
+ else {
+ if (fprintf(keyfile, "%s", key_p->value()) != key_p->size()) {
+ fl_alert("Unable to save registration key in %s.",
+ magic_file());
+ }
+ fclose(keyfile);
+ }
+
+ // Blank out the field, for if it gets displayed again later.
+ key_p->value("");
+}
+
+
+// If user cancels entering registration key, we just hide the window
+
+CALL_BACK(RegistrationKey_dialog, Cancel)
+{
+ hide();
+}
+
+
+//-------the Config menu item on main toolbar-------------------------------
+
+Config::Config()
+{
+ locations_p = 0;
+ preferences_p = 0;
+ registrationform_p = 0;
+ registrationkey_p = 0;
+}
+
+Config::~Config()
+{
+ if (locations_p != 0) {
+ delete locations_p;
+ locations_p = 0;
+ }
+ if (preferences_p != 0) {
+ delete preferences_p;
+ preferences_p = 0;
+ }
+ if (registrationform_p != 0) {
+ delete registrationform_p;
+ registrationform_p = 0;
+ }
+ if (registrationkey_p != 0) {
+ delete registrationkey_p;
+ registrationkey_p = 0;
+ }
+}
+
+
+// Bring up the dialog for "File Locations" menu item
+
+CALL_BACK(Config, FileLocations)
+{
+ if (locations_p == 0) {
+ // first time, create widget
+ locations_p = new FileLocations_dialog();
+ }
+ locations_p->show();
+}
+
+
+// Bring up the dialog for "Preferences" menu item
+
+CALL_BACK(Config, Preferences)
+{
+ if (preferences_p == 0) {
+ // first time, create widget
+ preferences_p = new Preferences_dialog();
+ }
+ preferences_p->show();
+}
+
+
+// Bring up dialog for filling in Registration Form
+
+CALL_BACK(Config, RegistrationForm)
+{
+ if (registrationform_p == 0) {
+ // first time, create widget
+ registrationform_p = new RegistrationForm_dialog();
+ }
+ registrationform_p->show();
+ fl_message("Note: For fastest service, you can register via\n"
+ "credit card at http://www.arkkra.com/doc/credtcrd.html\n"
+ "rather than filling out and mailing in a paper form.");
+}
+
+
+// Bring up dialog to let user enter their registration key after paying
+
+CALL_BACK(Config, RegistrationKey)
+{
+ if (registrationkey_p == 0) {
+ // first time, create widget
+ registrationkey_p = new RegistrationKey_dialog();
+ }
+ registrationkey_p->show();
+}
+
+
+// Translate font name to FL_Font value.
+
+Fl_Font
+Config::fontvalue(const char * fontname)
+{
+ int n;
+ // Linear search of the list (it is short).
+ for (n = 0; n < Fontlistlength; n++) {
+ if (strcmp(Fontlist[n].name, fontname) == 0) {
+ return(Fontlist[n].value);
+ }
+ }
+ // Hmmm. Not found. Should not happen. Hunt for default
+ for (n = 0; n < Fontlistlength; n++) {
+ if (strcmp(Fontlist[n].name, Default_editor_font) == 0) {
+ return(Fontlist[n].value);
+ }
+ }
+ // Wow. Can't find default either. Punt.
+ return(FL_COURIER);
+}
+
+
+//--------------------- class that lets other classes register a callback
+// to be called for changes in font/size
+
+
+// List of callbacks for when font/size change
+Font_change_registration * Font_change_registration::list_p = 0;
+
+Font_change_registration::Font_change_registration(Font_change_callback func, void * arg)
+{
+ // Save callback information.
+ callback = func;
+ callback_arg = arg;
+
+ // Add to list of callbacks.
+ next = list_p;
+ list_p = this;
+
+ // Set the font and size on this newly registered widget.
+ // Look up the current values and call the newly registered callback.
+ char * fontstr;
+ (void) Preferences_p->get(Editor_font_preference, fontstr,
+ Default_editor_font);
+ Fl_Font font = Config::fontvalue(fontstr);
+
+ char * sizestr;
+ (void) Preferences_p->get(Editor_size_preference, sizestr,
+ Default_editor_size);
+ unsigned char size = (unsigned char) atoi(sizestr);
+ if (size < Min_size) {
+ size = (unsigned char) atoi(Default_editor_size);
+ }
+ (*func)(arg, font, size);
+}
+
+Font_change_registration::~Font_change_registration(void)
+{
+ // Remove callback from linked list
+ if (list_p == this) {
+ list_p = next;
+ }
+ else {
+ Font_change_registration * fcr_p;
+ for (fcr_p = list_p; fcr_p != 0; fcr_p = fcr_p->next) {
+ if (fcr_p->next == this) {
+ fcr_p->next = next;
+ return;
+ }
+ }
+ }
+}
+
+
+// Notify all classes that want to know about font/size changes,
+// by calling the callback function they registered.
+
+void
+Font_change_registration::run_callbacks(Fl_Font font, unsigned char size)
+{
+ // Avoid unreadably small sizes and division by zero if
+ // earlier atoi() of size failed due to bad data
+ // (e.g., user hand-editing the preference file)
+ if (size < Min_size || size > Max_size) {
+ size = (unsigned char) atoi(Default_editor_size);
+ }
+
+ // Walk through list of registered callbacks, calling each.
+ Font_change_registration * fcr_p;
+ for (fcr_p = list_p; fcr_p != 0; fcr_p = fcr_p->next) {
+ (*(fcr_p->callback))(fcr_p->callback_arg, font, size);
+ }
+}