--- /dev/null
+/* Copyright (c) 2006 by Arkkra Enterprises */
+/* All rights reserved */
+
+// Code for the main window for Mupmate, a front end program for
+// the Mup music publisher program from Arkkra Enterprises.
+// It uses the FLTK toolkit for OS independence.
+
+// This file contains code for the toolbar and editor window,
+// as well as general startup and showing the license.
+
+// We only support editing a single file at a time, so most classes
+// as really effectively singletons, but the code is written to be
+// able to support multiple instances, in case we ever want to do that.
+// That means callback functions are always passed a pointer to
+// a class instance as their second argument, and all they do is cast
+// that to the appropriate type, and call the corresponding class method.
+
+// For the most part, widgets are only allocated when needed, but then
+// stay around for the life of the process, in case they are needed again.
+
+// Callbacks are named with _cb suffix.
+// Pointers are named with _p suffix, except for (char *) types
+// that are pointing to text strings, which don't have any special suffix.
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+
+#include <FL/Fl.H>
+#include <FL/fl_ask.H>
+#include <FL/Fl_Tooltip.H>
+
+#include "globals.H"
+#include "Main.H"
+#include "Preferences.H"
+#include "utils.H"
+
+#include <FL/x.H>
+#ifdef OS_LIKE_WIN32
+#include "resource.h"
+#else
+#include <FL/Fl_File_Icon.H>
+#ifdef OS_LIKE_UNIX
+#include <X11/xpm.h>
+#include "mup32.xpm"
+#endif
+#endif
+
+// Height of the tool bar on the main window
+#define TOOLBAR_HEIGHT 30
+
+// How often to blink the cursor.
+#define BLINK_RATE 0.5
+
+// If file indicating user has agreed to license terms doesn't exist,
+// we ask them to agree. This is the text of the license.
+extern const char * const license_text;
+
+//----------------------------------------------------------------------
+
+// Define the toolbar and its submenus.
+// Indented lines indicate the submenus.
+// The & indicates shortcut key
+
+const char * File_label = "&File";
+ const char * New_label = "&New";
+ const char * NewFromTemplate_label = "New From &Template";
+ const char * Open_label = "&Open...";
+ const char * Save_label = "&Save";
+ const char * SaveAs_label = "Save &As...";
+ const char * Exit_label = "E&xit";
+const char * Edit_label = "&Edit";
+ const char * Undo_label = "&Undo";
+ const char * Cut_label = "Cu&t";
+ const char * Copy_label = "&Copy";
+ const char * Paste_label = "&Paste";
+ const char * Delete_label = "&Delete";
+ const char * Find_label = "&Find...";
+ const char * FindNext_label = "Find &Next";
+ const char * Replace_label = "&Replace...";
+ const char * GoTo_label = "&Go To...";
+ const char * SelectAll_label = "&Select All";
+const char * Run_label = "&Run";
+ const char * Display_label = "&Display";
+ const char * Play_label = "&Play";
+ const char * WritePostScript_label = "&Write PostScript File";
+ const char * WriteMIDI_label = "Write &MIDI File";
+ const char * Options_label = "&Set Options...";
+const char * Config_label = "&Config";
+ const char * FileLocations_label = "&File Locations...";
+ const char * Preferences_label = "&Preferences...";
+ const char * RegistrationForm_label = "&Registration Form...";
+ const char * RegistrationKey_label = "Registration &Key...";
+const char * Help_label = "&Help";
+ const char * UserGuide_label = "Mup &User's Guide";
+ const char * StartupHints_label = "&Startup Hints";
+ const char * AboutMupmate_label = "&About Mupmate";
+
+Fl_Menu_Item Toolbar_menu[] = {
+ { File_label, 0, 0, 0, FL_SUBMENU },
+ { New_label, FL_CTRL + 'n', File::New_cb },
+ { NewFromTemplate_label, FL_CTRL + 't', File::NewFromTemplate_cb },
+ { Open_label, FL_CTRL + 'o', File::Open_cb },
+ { Save_label, FL_CTRL + 's', File::Save_cb },
+ { SaveAs_label, 0, File::SaveAs_cb, 0, FL_MENU_DIVIDER },
+ { Exit_label, 0, File::Exit_cb },
+ { 0 },
+ { Edit_label, 0, 0, 0, FL_SUBMENU },
+ { Undo_label, FL_CTRL + 'z', Edit::Undo_cb, 0, FL_MENU_INACTIVE | FL_MENU_DIVIDER },
+ { Cut_label, FL_CTRL + 'x', Edit::Cut_cb, 0, FL_MENU_INACTIVE },
+ { Copy_label, FL_CTRL + 'c', Edit::Copy_cb, 0, FL_MENU_INACTIVE },
+ { Paste_label, FL_CTRL + 'v', Edit::Paste_cb, 0, FL_MENU_INACTIVE },
+ { Delete_label, FL_Delete, Edit::Delete_cb, 0, FL_MENU_INACTIVE | FL_MENU_DIVIDER },
+ { Find_label, FL_CTRL + 'f', Edit::Find_cb, 0, FL_MENU_INACTIVE },
+ { FindNext_label, FL_F + 3, Edit::FindNext_cb, 0, FL_MENU_INACTIVE },
+ { Replace_label, FL_CTRL + 'h', Edit::Replace_cb, 0, FL_MENU_INACTIVE },
+ { GoTo_label, FL_CTRL + 'g', Edit::GoTo_cb, 0, FL_MENU_DIVIDER },
+ { SelectAll_label, FL_CTRL + 'a', Edit::SelectAll_cb, 0, FL_MENU_INACTIVE },
+ { 0 },
+ { Run_label, 0, 0, 0, FL_SUBMENU },
+ { Display_label, 0, Run::Display_cb, 0, FL_MENU_INACTIVE },
+ { Play_label, 0, Run::Play_cb, 0, FL_MENU_INACTIVE },
+ { WritePostScript_label, 0, Run::WritePostScript_cb, 0, FL_MENU_INACTIVE },
+ { WriteMIDI_label, 0, Run::WriteMIDI_cb, 0, FL_MENU_DIVIDER | FL_MENU_INACTIVE },
+ { Options_label, 0, Run::Options_cb },
+ { 0 },
+ { Config_label, 0, 0, 0, FL_SUBMENU },
+ { FileLocations_label, 0, Config::FileLocations_cb },
+ { Preferences_label, 0, Config::Preferences_cb, 0, FL_MENU_DIVIDER },
+ { RegistrationForm_label, 0, Config::RegistrationForm_cb },
+ { RegistrationKey_label, 0, Config::RegistrationKey_cb },
+ { 0 },
+ { Help_label, 0, 0, 0, FL_SUBMENU },
+ { UserGuide_label, 0, Help::Uguide_cb },
+ { StartupHints_label, 0, Help::Startup_Hints_cb },
+ { AboutMupmate_label, 0, Help::About_cb },
+ { 0 },
+ { 0 }
+};
+
+
+//----------------------------------------------------------------------
+
+// Linked list of main windows, in case we ever support more than one
+// at a time. (Currently we don't, because we're not sure if it
+// might be more confusing than useful.)
+Main * Main::list_p;
+
+
+// Constructor for main window. It contains toolbar and editor window.
+
+Main::Main(const char * title)
+ : Fl_Double_Window(Default_width, Default_height, title)
+{
+ xclass("mup");
+#ifdef OS_LIKE_WIN32
+ icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON)));
+#else
+#ifdef OS_LIKE_UNIX
+ fl_open_display();
+ Pixmap p, mask;
+ XpmCreatePixmapFromData(fl_display, DefaultRootWindow(fl_display),
+ mup32_xpm, &p, &mask, NULL);
+ icon((char *)p);
+#endif
+#endif
+ // Try to use user's default foreground/background
+ Fl::get_system_colors();
+
+ void * data = 0;
+
+ // Create class instances for each toolbar item
+ filemenu_p = new File();
+ editmenu_p = new Edit();
+ configmenu_p = new Config();
+ helpmenu_p = new Help();
+ runmenu_p = new Run();
+
+ // Add to list of windows
+ next = list_p;
+ list_p = this;
+
+ // Create the toolbar and populate its menu items
+ toolbar_p = new Fl_Menu_Bar(0, 0, w(), TOOLBAR_HEIGHT);
+ int numitems = sizeof(Toolbar_menu) / sizeof(Toolbar_menu[0]);
+ for (int i = 0; i < numitems; i++) {
+ if (Toolbar_menu[i].text != 0) {
+ // As we move to each top-level menu item,
+ // keep a pointer to that item, which is then
+ // used as the argument to callback functions,
+ // so they know what object to act on.
+ if (strcmp(Toolbar_menu[i].text, File_label) == 0) {
+ data = (void *) filemenu_p;
+ }
+ else if (strcmp(Toolbar_menu[i].text, Edit_label) == 0) {
+ data = (void *) editmenu_p;
+ }
+ else if (strcmp(Toolbar_menu[i].text, Config_label) == 0) {
+ data = (void *) configmenu_p;
+ }
+ else if (strcmp(Toolbar_menu[i].text, Run_label) == 0) {
+ data = (void *) runmenu_p;
+ }
+ else if (strcmp(Toolbar_menu[i].text, Help_label) == 0) {
+ data = (void *) helpmenu_p;
+ }
+ }
+ Toolbar_menu[i].user_data(data);
+ }
+ toolbar_p->copy(Toolbar_menu);
+
+ // Create and configure the editor window
+ editor_p = new Fl_Text_Editor(0, TOOLBAR_HEIGHT, w(),
+ h() - TOOLBAR_HEIGHT, "");
+ editor_p->buffer( new Fl_Text_Buffer );
+
+ // Set font/size and arrange to be notified of changes in them
+ font_change_reg_p = new Font_change_registration(font_change_cb,
+ (void *) this);
+
+ // Several objects need to be notified of changes in the
+ // editor window, so they can do things like gray-ungray menu items.
+ editor_p->buffer()->add_modify_callback(modify_cb, (void*) this);
+ editor_p->buffer()->add_modify_callback(File::modify_cb,
+ (void*) filemenu_p);
+ editor_p->buffer()->add_modify_callback(Edit::modify_cb,
+ (void*) editmenu_p);
+
+ // Initialize state information.
+ have_selection = false;
+ can_paste = false;
+ prev_bufflength = 0;
+ // Undo is inactive until user does something that can be undone.
+ undo_active = false;
+ undo_active_on_next_change = true;
+
+ // Arrange to make cursor blink
+ Fl::add_timeout(BLINK_RATE, blinker, this);
+ cursor_state = 1;
+
+ // Let editor take as much space as is available
+ // if the user resizes the main window.
+ size_range(Min_width, Min_height, 0, 0);
+ resizable((Fl_Widget *) editor_p);
+
+ // Other classes need to have access to editor and such
+ filemenu_p->set_editor(editor_p);
+ filemenu_p->set_parent(this);
+ editmenu_p->set_editor(editor_p);
+ runmenu_p->set_file(filemenu_p);
+
+ // Arrange for destructor to free the new-ed child widgets
+ end();
+
+ show();
+
+ // Arrange for window manager closes to do Exit.
+ callback(atclose_cb, this);
+ when(FL_WHEN_NEVER);
+
+#ifdef OS_LIKE_UNIX
+ // Arrange for icon to be associated with window
+ XWMHints hints;
+ hints.flags = IconPixmapHint | IconMaskHint ;
+ hints.icon_pixmap = p;
+ hints.icon_mask = mask;
+ XSetWMHints(fl_display, fl_xid((Fl_Window *)this), &hints);
+#endif
+}
+
+
+// Destructor for main window
+
+Main::~Main()
+{
+ delete font_change_reg_p;
+ font_change_reg_p = 0;
+ Fl::remove_timeout(blinker, this);
+ // Remove from list of Main windows
+ if (list_p == this) {
+ list_p = next;
+ }
+ else {
+ for (Main * m = list_p; m != 0; m = m->next) {
+ if (m->next == this) {
+ m->next = this->next;
+ break;
+ }
+ }
+ }
+ delete filemenu_p;
+ filemenu_p = 0;
+ delete editmenu_p;
+ editmenu_p = 0;
+ delete configmenu_p;
+ configmenu_p = 0;
+ delete helpmenu_p;
+ helpmenu_p = 0;
+ delete runmenu_p;
+ runmenu_p = 0;
+}
+
+
+// Callback for when user changes font/size
+
+void
+Main::font_change_cb(void * data, Fl_Font font, unsigned char size)
+{
+ ((Main *)data)->font_change(font, size);
+}
+
+
+void
+Main::font_change(Fl_Font font, unsigned char size)
+{
+ // Get shorter name for buffer, as we'll be using it a lot.
+ Fl_Text_Buffer * buffer_p = editor_p->buffer();
+
+ // Don't want this change to count as something that can be undone
+ buffer_p->canUndo(false);
+
+ // We want to change the entire text buffer, so need to
+ // select its whole contents. If there was already a selection,
+ // save that and put it back when we are done
+ int sel_start, sel_end;
+ int had_selection;
+ int cursorplace = editor_p->insert_position();
+ if ((had_selection = buffer_p->selected()) != 0) {
+ buffer_p->selection_position(&sel_start, &sel_end);
+ buffer_p->unselect();
+ }
+
+ // set new font and size
+ buffer_p->select(0, editor_p->buffer()->length() - 1);
+ editor_p->textfont(font);
+ editor_p->textsize(size);
+ buffer_p->unselect();
+
+ // Put selection and cursor back as they were before font change
+ if (had_selection) {
+ buffer_p->select(sel_start, sel_end);
+ }
+ editor_p->insert_position(cursorplace);
+ buffer_p->canUndo(true);
+}
+
+
+// Callback for when editor window changes.
+// This arranges to gray/ungray toolbar menu items.
+
+void
+Main::modify_cb(int, int, int, int, const char *, void * data)
+{
+ ((Main *)data)->modify();
+}
+
+void
+Main::modify()
+{
+ int bufflength = editor_p->buffer()->length();
+ // See if what changed is something we might care about.
+ if (editor_p->buffer()->selected() != have_selection ||
+ editmenu_p->can_paste() != can_paste
+ || undo_active != undo_active_on_next_change
+ || bufflength < 2 || prev_bufflength == 0) {
+
+ // Something changed, and we may need to
+ // gray or ungray menu items in response.
+ have_selection = editor_p->buffer()->selected();
+ const Fl_Menu_Item * menu_p = toolbar_p->menu();
+ Fl_Menu_Item * item_p;
+ // Walk through toolbar and submenus, checking
+ // if anything needs to be grayed/ungrayed.
+ for (int i = 0; i < toolbar_p->size(); i++) {
+ const char * mtext = toolbar_p->text(i);
+ if (mtext == 0) {
+ continue;
+ }
+ // Can only Copy, Cut, and Delete if something
+ // is selected.
+ if (strcmp(mtext, Copy_label) == 0 ||
+ strcmp(mtext, Cut_label) == 0 ||
+ strcmp(mtext, Delete_label) == 0) {
+ // have to un-const so we can (de)activate
+ item_p = (Fl_Menu_Item *) &(menu_p[i]);
+ if (have_selection) {
+ item_p->activate();
+ }
+ else {
+ item_p->deactivate();
+ }
+ }
+
+ // Paste is different. It becomes active when there
+ // is something in clipboard, and never again becomes
+ // inactive.
+ if (strcmp(mtext, Paste_label) == 0 &&
+ editmenu_p->can_paste()) {
+ ((Fl_Menu_Item *)&(menu_p[i]))->activate();
+ can_paste = true;
+ }
+
+ // Undo is also different. On first change of any
+ // kind it become active, and stays that way,
+ // except it gets reset on new file.
+ if (strcmp(mtext, Undo_label) == 0) {
+ if (undo_active && ! undo_active_on_next_change) {
+ ((Fl_Menu_Item *)&(menu_p[i]))->deactivate();
+ undo_active = false;
+ undo_active_on_next_change = true;
+ }
+ else if ( ! undo_active && undo_active_on_next_change) {
+ ((Fl_Menu_Item *)&(menu_p[i]))->activate();
+ undo_active = true;
+ }
+ }
+
+ // Find and FindNext are inactive when file is empty,
+ // because obviously there is nothing to find.
+ // Similar for Replace and Select All.
+ // Also for all the Run things
+ if (strcmp(mtext, Find_label) == 0 ||
+ strcmp(mtext, FindNext_label) == 0 ||
+ strcmp(mtext, Replace_label) == 0 ||
+ strcmp(mtext, SelectAll_label) == 0 ||
+ strcmp(mtext, Display_label) == 0 ||
+ strcmp(mtext, Play_label) == 0 ||
+ strcmp(mtext, WritePostScript_label) == 0 ||
+ strcmp(mtext, WriteMIDI_label) == 0) {
+ if (bufflength == 0) {
+ ((Fl_Menu_Item *)&(menu_p[i]))->deactivate();
+ }
+ else {
+ ((Fl_Menu_Item *)&(menu_p[i]))->activate();
+ }
+ }
+ }
+ }
+ prev_bufflength = bufflength;
+}
+
+
+// This method gets called when user starts working on a new file.
+// It sets state information so we can know how to gray menu items properly.
+
+void
+Main::begin_new_file()
+{
+ // transition Undo-ability state from true to false
+ undo_active = true;
+ undo_active_on_next_change = false;
+ editor_p->buffer()->call_modify_callbacks();
+ runmenu_p->clean_up();
+}
+
+
+// Handle some special cases.
+// 1. By default fltk will exit the main window upon getting escape.
+// That seems bad, since a vi user will be used to hitting escape all the
+// time when editing, because that is always a "safe" thing to do,
+// and if they did it here by mistake, they would lose all
+// their text entry since the last save. So we ignore the escape in this window.
+// I suppose we could ask if they really want to quit...
+// 2. If user does cut or copy via keyboard accelerator, the normal code
+// for ungraying the Paste button doesn't get called, so we catch that case
+// here and ungray it.
+
+int
+Main::handle_events(int e)
+{
+ // If escape is received while on main window,
+ // return 1 to show that we consumed the event.
+ if (e == FL_SHORTCUT && Fl::event_key() == FL_Escape) {
+ for (Main * m_p = list_p; m_p != 0; m_p = m_p->next) {
+ if (Fl::first_window() == m_p) {
+ return(1);
+ }
+ }
+ }
+
+ // If user did cut or copy via cntl-c or cntl-x,
+ // arrange to ungray Paste.
+ if (e == FL_KEYUP && (Fl::event_state() & FL_CTRL) &&
+ (Fl::event_key() == 'v' || Fl::event_key() == 'x')) {
+ for (Main * m_p = list_p; m_p != 0; m_p = m_p->next) {
+ if (Fl::first_window() == m_p) {
+ m_p->editmenu_p->set_can_paste();
+ break;
+ }
+ }
+ }
+
+ return(0);
+}
+
+
+// If user tries to close the main window via the window manager
+// while having unsaved changes, we ask user if they want to save
+// the changes first.
+
+CALL_BACK(Main, atclose)
+{
+ File::Exit_cb(0, filemenu_p);
+}
+
+
+// Blink the cursor. It can be hard to see if next to selected text if not
+// blinking. We could potentially optimize to only do this while
+// the window has focus, but it doesn't seem worth the complication...
+
+void
+Main::blinker(void * data)
+{
+ Main * obj_p = (Main *) data;
+ // Put cursor into opposite of its current state
+ obj_p->cursor_state ^= 1;
+ obj_p->editor_p->show_cursor(obj_p->cursor_state);
+ // Reset timer to call ourselves again.
+ Fl::repeat_timeout(BLINK_RATE, blinker, data);
+}
+
+
+// Give user hints if we haven't already done that before.
+
+void
+Main::hints(void)
+{
+ int did_startup;
+ (void) Preferences_p->get(Showed_startup_hints, did_startup,
+ Default_startup_hints_flag);
+ if ( ! did_startup) {
+ Help::Startup_Hints_cb(0, (void *) helpmenu_p);
+ }
+}
+
+//----------------------------------------------------------------------
+
+int
+main(int argc, char **argv, const char **arge)
+{
+ // The arge value may get changed when we set new environment
+ // variables, so look up PATH first thing.
+ get_path(arge);
+
+ // Uguide browser needs to show images
+ fl_register_images();
+
+#ifndef OS_LIKE_WIN32
+ // On Windows we use the native Open/Save As dialogs.
+ // On other platforms we use FLTK's, but add icon for Mup files.
+ Fl_File_Icon::load_system_icons();
+ File::add_mup_icon();
+#endif
+
+ // Try to get best hardware support for graphics
+ Fl::visual(FL_DOUBLE|FL_INDEX);
+
+ // Get the user's preferences that persists across sessions
+ Preferences_p = new Fl_Preferences(Fl_Preferences::USER,
+ "arkkra.com", "mupmate");
+
+ // Enable tips when user hovers their mouse over a widget.
+ // If user doesn't like them, they can set delay to huge value.
+ Fl_Tooltip::enable();
+ double tooltips_delay;
+ (void) Preferences_p->get(Tooltips_delay_preference, tooltips_delay,
+ Default_tooltips_delay);
+ Fl_Tooltip::delay(tooltips_delay);
+
+ // Set $MUPPATH
+ char * val;
+ (void) Preferences_p->get(MUPPATH_location, val,
+ Default_MUPPATH_location);
+ set_muppath(val);
+
+ // Tell Mup that it is being run via mupmate,
+ // so it can give more appropriate error messages.
+ putenv("MUPMATE=1");
+
+ // Create main window
+ Main *main_p = new Main("Mupmate");
+
+ // Ensure "escape" key doesn't kill main window,
+ // and make sure Paste ungraying works
+ Fl::add_handler(Main::handle_events);
+
+ // Try to find some reasonable defaults for configuration items
+ // that aren't already set.
+ deduce_helper_locations();
+
+ // If magic file indicating license agreement isn't there,
+ // ask user to agree to Mup license.
+ if (access(magic_file(argv[0]), F_OK) != 0) {
+ // Show the license and get agreement before continuing
+ new License(main_p, magic_file(argv[0]));
+ }
+ else {
+ // Display the main window.
+ main_p->show(1, argv);
+
+ // The first time, we show user some hints.
+ main_p->hints();
+ }
+
+ // Go to where user said they want to store their Mup files by default.
+ // Need to wait to do this until after we have deduced locations
+ // of executable, in case they were in current directory.
+ char * mup_dir;
+ (void) Preferences_p->get(Music_files_location, mup_dir,
+ Default_music_files_location);
+ if (strcmp(mup_dir, ".") != 0) {
+ if (chdir(mup_dir) != 0) {
+ char curr_dir[FL_PATH_MAX] = "current";
+ char message[2 * FL_PATH_MAX + 100];
+ (void *) getcwd(curr_dir, sizeof(curr_dir));
+ sprintf(message, "Unable to change to folder\n"
+ "\"%s.\"\nStaying in \"%s\" folder.\n"
+ "Fix setting of \"Folder for Mup Files\"\n"
+ "in Config->File Locations.",
+ mup_dir, curr_dir);
+ fl_alert(message);
+ }
+ }
+
+ // Expect 0 or 1 args. If 1, should be name of file to load
+ if (argc > 1) {
+ main_p->filemenu_p->load_file(argv[1]);
+ }
+ if (argc > 2) {
+ fl_alert("Only expecting one file; extra arguments are being ignored.");
+ }
+
+ // Go into main event-handler loop
+ int exitvalue = Fl::run();
+ Main::clean_exit(exitvalue);
+ /*NOTREACHED*/
+ return(exitvalue);
+}
+
+
+// Clean up all the windows and their children and exit.
+
+void
+Main::clean_exit(int exitval)
+{
+ Main * m_p;
+ Main * nextwin_p;
+
+ for (m_p = list_p; m_p != 0; m_p = nextwin_p) {
+ nextwin_p = m_p->next;
+ delete m_p;
+ }
+ exit(exitval);
+}
+
+//---------- class to show Mup license and get user's agreement
+
+
+License::License(Main *m_p, const char * magic)
+ : Fl_Double_Window(Default_width, Default_height, "Mup License")
+{
+ // save passed-in info in object data
+ main_p = m_p;
+ magic_file_name = magic;
+
+ // widget for displaying the license text
+ text_p = new Fl_Text_Display(20, 20, w() - 40, h() - 90);
+ resizable((Fl_Widget *) text_p);
+ text_p->buffer( new Fl_Text_Buffer () );
+ text_p->textsize(18);
+ text_p->buffer()->text(license_text);
+
+ i_agree_p = new Fl_Return_Button(100, h() - 50, 100, 30, "I Agree");
+ i_agree_p->callback(IAgree_cb, this);
+
+ cancel_p = new Fl_Button(w() - 200, h() - 50, 100, 30, "Cancel");
+ cancel_p->callback(Cancel_cb, this);
+ cancel_p->shortcut(FL_Escape);
+
+ show();
+
+ // Arrange for destructor to free the new-ed child widgets
+ end();
+
+ // Arrange for window manager closes to do Cancel.
+ callback(Cancel_cb, this);
+ when(FL_WHEN_NEVER);
+}
+
+
+License::~License()
+{
+}
+
+
+// Callback for when user clicks that they agree to the license.
+
+CALL_BACK(License, IAgree)
+{
+ // Create the magic file
+ int fd;
+ if ((fd = open(magic_file_name, O_WRONLY | O_CREAT, 0644)) < 0) {
+ fl_alert("Unable to create file indicating license agreement.");
+ }
+ else {
+ close(fd);
+ }
+
+ hide();
+
+ // Bring up the normal main window
+ main_p->show();
+ main_p->hints();
+}
+
+// Callback if user refuses to accept license. We just exit if they refuse.
+
+CALL_BACK(License, Cancel)
+{
+ exit(0);
+}