chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mupmate / Main.C
CommitLineData
fac14bbe
MW
1/* Copyright (c) 2006 by Arkkra Enterprises */
2/* All rights reserved */
3
4// Code for the main window for Mupmate, a front end program for
5// the Mup music publisher program from Arkkra Enterprises.
6// It uses the FLTK toolkit for OS independence.
7
8// This file contains code for the toolbar and editor window,
9// as well as general startup and showing the license.
10
11// We only support editing a single file at a time, so most classes
12// as really effectively singletons, but the code is written to be
13// able to support multiple instances, in case we ever want to do that.
14// That means callback functions are always passed a pointer to
15// a class instance as their second argument, and all they do is cast
16// that to the appropriate type, and call the corresponding class method.
17
18// For the most part, widgets are only allocated when needed, but then
19// stay around for the life of the process, in case they are needed again.
20
21// Callbacks are named with _cb suffix.
22// Pointers are named with _p suffix, except for (char *) types
23// that are pointing to text strings, which don't have any special suffix.
24
25
26#include <string.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <stdio.h>
30#include <fcntl.h>
31
32#include <FL/Fl.H>
33#include <FL/fl_ask.H>
34#include <FL/Fl_Tooltip.H>
35
36#include "globals.H"
37#include "Main.H"
38#include "Preferences.H"
39#include "utils.H"
40
41#include <FL/x.H>
42#ifdef OS_LIKE_WIN32
43#include "resource.h"
44#else
45#include <FL/Fl_File_Icon.H>
46#ifdef OS_LIKE_UNIX
47#include <X11/xpm.h>
48#include "mup32.xpm"
49#endif
50#endif
51
52// Height of the tool bar on the main window
53#define TOOLBAR_HEIGHT 30
54
55// How often to blink the cursor.
56#define BLINK_RATE 0.5
57
58// If file indicating user has agreed to license terms doesn't exist,
59// we ask them to agree. This is the text of the license.
60extern const char * const license_text;
61
62//----------------------------------------------------------------------
63
64// Define the toolbar and its submenus.
65// Indented lines indicate the submenus.
66// The & indicates shortcut key
67
68const char * File_label = "&File";
69 const char * New_label = "&New";
70 const char * NewFromTemplate_label = "New From &Template";
71 const char * Open_label = "&Open...";
72 const char * Save_label = "&Save";
73 const char * SaveAs_label = "Save &As...";
74 const char * Exit_label = "E&xit";
75const char * Edit_label = "&Edit";
76 const char * Undo_label = "&Undo";
77 const char * Cut_label = "Cu&t";
78 const char * Copy_label = "&Copy";
79 const char * Paste_label = "&Paste";
80 const char * Delete_label = "&Delete";
81 const char * Find_label = "&Find...";
82 const char * FindNext_label = "Find &Next";
83 const char * Replace_label = "&Replace...";
84 const char * GoTo_label = "&Go To...";
85 const char * SelectAll_label = "&Select All";
86const char * Run_label = "&Run";
87 const char * Display_label = "&Display";
88 const char * Play_label = "&Play";
89 const char * WritePostScript_label = "&Write PostScript File";
90 const char * WriteMIDI_label = "Write &MIDI File";
91 const char * Options_label = "&Set Options...";
92const char * Config_label = "&Config";
93 const char * FileLocations_label = "&File Locations...";
94 const char * Preferences_label = "&Preferences...";
95 const char * RegistrationForm_label = "&Registration Form...";
96 const char * RegistrationKey_label = "Registration &Key...";
97const char * Help_label = "&Help";
98 const char * UserGuide_label = "Mup &User's Guide";
99 const char * StartupHints_label = "&Startup Hints";
100 const char * AboutMupmate_label = "&About Mupmate";
101
102Fl_Menu_Item Toolbar_menu[] = {
103 { File_label, 0, 0, 0, FL_SUBMENU },
104 { New_label, FL_CTRL + 'n', File::New_cb },
105 { NewFromTemplate_label, FL_CTRL + 't', File::NewFromTemplate_cb },
106 { Open_label, FL_CTRL + 'o', File::Open_cb },
107 { Save_label, FL_CTRL + 's', File::Save_cb },
108 { SaveAs_label, 0, File::SaveAs_cb, 0, FL_MENU_DIVIDER },
109 { Exit_label, 0, File::Exit_cb },
110 { 0 },
111 { Edit_label, 0, 0, 0, FL_SUBMENU },
112 { Undo_label, FL_CTRL + 'z', Edit::Undo_cb, 0, FL_MENU_INACTIVE | FL_MENU_DIVIDER },
113 { Cut_label, FL_CTRL + 'x', Edit::Cut_cb, 0, FL_MENU_INACTIVE },
114 { Copy_label, FL_CTRL + 'c', Edit::Copy_cb, 0, FL_MENU_INACTIVE },
115 { Paste_label, FL_CTRL + 'v', Edit::Paste_cb, 0, FL_MENU_INACTIVE },
116 { Delete_label, FL_Delete, Edit::Delete_cb, 0, FL_MENU_INACTIVE | FL_MENU_DIVIDER },
117 { Find_label, FL_CTRL + 'f', Edit::Find_cb, 0, FL_MENU_INACTIVE },
118 { FindNext_label, FL_F + 3, Edit::FindNext_cb, 0, FL_MENU_INACTIVE },
119 { Replace_label, FL_CTRL + 'h', Edit::Replace_cb, 0, FL_MENU_INACTIVE },
120 { GoTo_label, FL_CTRL + 'g', Edit::GoTo_cb, 0, FL_MENU_DIVIDER },
121 { SelectAll_label, FL_CTRL + 'a', Edit::SelectAll_cb, 0, FL_MENU_INACTIVE },
122 { 0 },
123 { Run_label, 0, 0, 0, FL_SUBMENU },
124 { Display_label, 0, Run::Display_cb, 0, FL_MENU_INACTIVE },
125 { Play_label, 0, Run::Play_cb, 0, FL_MENU_INACTIVE },
126 { WritePostScript_label, 0, Run::WritePostScript_cb, 0, FL_MENU_INACTIVE },
127 { WriteMIDI_label, 0, Run::WriteMIDI_cb, 0, FL_MENU_DIVIDER | FL_MENU_INACTIVE },
128 { Options_label, 0, Run::Options_cb },
129 { 0 },
130 { Config_label, 0, 0, 0, FL_SUBMENU },
131 { FileLocations_label, 0, Config::FileLocations_cb },
132 { Preferences_label, 0, Config::Preferences_cb, 0, FL_MENU_DIVIDER },
133 { RegistrationForm_label, 0, Config::RegistrationForm_cb },
134 { RegistrationKey_label, 0, Config::RegistrationKey_cb },
135 { 0 },
136 { Help_label, 0, 0, 0, FL_SUBMENU },
137 { UserGuide_label, 0, Help::Uguide_cb },
138 { StartupHints_label, 0, Help::Startup_Hints_cb },
139 { AboutMupmate_label, 0, Help::About_cb },
140 { 0 },
141 { 0 }
142};
143
144
145//----------------------------------------------------------------------
146
147// Linked list of main windows, in case we ever support more than one
148// at a time. (Currently we don't, because we're not sure if it
149// might be more confusing than useful.)
150Main * Main::list_p;
151
152
153// Constructor for main window. It contains toolbar and editor window.
154
155Main::Main(const char * title)
156 : Fl_Double_Window(Default_width, Default_height, title)
157{
158 xclass("mup");
159#ifdef OS_LIKE_WIN32
160 icon((char *)LoadIcon(fl_display, MAKEINTRESOURCE(IDI_ICON)));
161#else
162#ifdef OS_LIKE_UNIX
163 fl_open_display();
164 Pixmap p, mask;
165 XpmCreatePixmapFromData(fl_display, DefaultRootWindow(fl_display),
166 mup32_xpm, &p, &mask, NULL);
167 icon((char *)p);
168#endif
169#endif
170 // Try to use user's default foreground/background
171 Fl::get_system_colors();
172
173 void * data = 0;
174
175 // Create class instances for each toolbar item
176 filemenu_p = new File();
177 editmenu_p = new Edit();
178 configmenu_p = new Config();
179 helpmenu_p = new Help();
180 runmenu_p = new Run();
181
182 // Add to list of windows
183 next = list_p;
184 list_p = this;
185
186 // Create the toolbar and populate its menu items
187 toolbar_p = new Fl_Menu_Bar(0, 0, w(), TOOLBAR_HEIGHT);
188 int numitems = sizeof(Toolbar_menu) / sizeof(Toolbar_menu[0]);
189 for (int i = 0; i < numitems; i++) {
190 if (Toolbar_menu[i].text != 0) {
191 // As we move to each top-level menu item,
192 // keep a pointer to that item, which is then
193 // used as the argument to callback functions,
194 // so they know what object to act on.
195 if (strcmp(Toolbar_menu[i].text, File_label) == 0) {
196 data = (void *) filemenu_p;
197 }
198 else if (strcmp(Toolbar_menu[i].text, Edit_label) == 0) {
199 data = (void *) editmenu_p;
200 }
201 else if (strcmp(Toolbar_menu[i].text, Config_label) == 0) {
202 data = (void *) configmenu_p;
203 }
204 else if (strcmp(Toolbar_menu[i].text, Run_label) == 0) {
205 data = (void *) runmenu_p;
206 }
207 else if (strcmp(Toolbar_menu[i].text, Help_label) == 0) {
208 data = (void *) helpmenu_p;
209 }
210 }
211 Toolbar_menu[i].user_data(data);
212 }
213 toolbar_p->copy(Toolbar_menu);
214
215 // Create and configure the editor window
216 editor_p = new Fl_Text_Editor(0, TOOLBAR_HEIGHT, w(),
217 h() - TOOLBAR_HEIGHT, "");
218 editor_p->buffer( new Fl_Text_Buffer );
219
220 // Set font/size and arrange to be notified of changes in them
221 font_change_reg_p = new Font_change_registration(font_change_cb,
222 (void *) this);
223
224 // Several objects need to be notified of changes in the
225 // editor window, so they can do things like gray-ungray menu items.
226 editor_p->buffer()->add_modify_callback(modify_cb, (void*) this);
227 editor_p->buffer()->add_modify_callback(File::modify_cb,
228 (void*) filemenu_p);
229 editor_p->buffer()->add_modify_callback(Edit::modify_cb,
230 (void*) editmenu_p);
231
232 // Initialize state information.
233 have_selection = false;
234 can_paste = false;
235 prev_bufflength = 0;
236 // Undo is inactive until user does something that can be undone.
237 undo_active = false;
238 undo_active_on_next_change = true;
239
240 // Arrange to make cursor blink
241 Fl::add_timeout(BLINK_RATE, blinker, this);
242 cursor_state = 1;
243
244 // Let editor take as much space as is available
245 // if the user resizes the main window.
246 size_range(Min_width, Min_height, 0, 0);
247 resizable((Fl_Widget *) editor_p);
248
249 // Other classes need to have access to editor and such
250 filemenu_p->set_editor(editor_p);
251 filemenu_p->set_parent(this);
252 editmenu_p->set_editor(editor_p);
253 runmenu_p->set_file(filemenu_p);
254
255 // Arrange for destructor to free the new-ed child widgets
256 end();
257
258 show();
259
260 // Arrange for window manager closes to do Exit.
261 callback(atclose_cb, this);
262 when(FL_WHEN_NEVER);
263
264#ifdef OS_LIKE_UNIX
265 // Arrange for icon to be associated with window
266 XWMHints hints;
267 hints.flags = IconPixmapHint | IconMaskHint ;
268 hints.icon_pixmap = p;
269 hints.icon_mask = mask;
270 XSetWMHints(fl_display, fl_xid((Fl_Window *)this), &hints);
271#endif
272}
273
274
275// Destructor for main window
276
277Main::~Main()
278{
279 delete font_change_reg_p;
280 font_change_reg_p = 0;
281 Fl::remove_timeout(blinker, this);
282 // Remove from list of Main windows
283 if (list_p == this) {
284 list_p = next;
285 }
286 else {
287 for (Main * m = list_p; m != 0; m = m->next) {
288 if (m->next == this) {
289 m->next = this->next;
290 break;
291 }
292 }
293 }
294 delete filemenu_p;
295 filemenu_p = 0;
296 delete editmenu_p;
297 editmenu_p = 0;
298 delete configmenu_p;
299 configmenu_p = 0;
300 delete helpmenu_p;
301 helpmenu_p = 0;
302 delete runmenu_p;
303 runmenu_p = 0;
304}
305
306
307// Callback for when user changes font/size
308
309void
310Main::font_change_cb(void * data, Fl_Font font, unsigned char size)
311{
312 ((Main *)data)->font_change(font, size);
313}
314
315
316void
317Main::font_change(Fl_Font font, unsigned char size)
318{
319 // Get shorter name for buffer, as we'll be using it a lot.
320 Fl_Text_Buffer * buffer_p = editor_p->buffer();
321
322 // Don't want this change to count as something that can be undone
323 buffer_p->canUndo(false);
324
325 // We want to change the entire text buffer, so need to
326 // select its whole contents. If there was already a selection,
327 // save that and put it back when we are done
328 int sel_start, sel_end;
329 int had_selection;
330 int cursorplace = editor_p->insert_position();
331 if ((had_selection = buffer_p->selected()) != 0) {
332 buffer_p->selection_position(&sel_start, &sel_end);
333 buffer_p->unselect();
334 }
335
336 // set new font and size
337 buffer_p->select(0, editor_p->buffer()->length() - 1);
338 editor_p->textfont(font);
339 editor_p->textsize(size);
340 buffer_p->unselect();
341
342 // Put selection and cursor back as they were before font change
343 if (had_selection) {
344 buffer_p->select(sel_start, sel_end);
345 }
346 editor_p->insert_position(cursorplace);
347 buffer_p->canUndo(true);
348}
349
350
351// Callback for when editor window changes.
352// This arranges to gray/ungray toolbar menu items.
353
354void
355Main::modify_cb(int, int, int, int, const char *, void * data)
356{
357 ((Main *)data)->modify();
358}
359
360void
361Main::modify()
362{
363 int bufflength = editor_p->buffer()->length();
364 // See if what changed is something we might care about.
365 if (editor_p->buffer()->selected() != have_selection ||
366 editmenu_p->can_paste() != can_paste
367 || undo_active != undo_active_on_next_change
368 || bufflength < 2 || prev_bufflength == 0) {
369
370 // Something changed, and we may need to
371 // gray or ungray menu items in response.
372 have_selection = editor_p->buffer()->selected();
373 const Fl_Menu_Item * menu_p = toolbar_p->menu();
374 Fl_Menu_Item * item_p;
375 // Walk through toolbar and submenus, checking
376 // if anything needs to be grayed/ungrayed.
377 for (int i = 0; i < toolbar_p->size(); i++) {
378 const char * mtext = toolbar_p->text(i);
379 if (mtext == 0) {
380 continue;
381 }
382 // Can only Copy, Cut, and Delete if something
383 // is selected.
384 if (strcmp(mtext, Copy_label) == 0 ||
385 strcmp(mtext, Cut_label) == 0 ||
386 strcmp(mtext, Delete_label) == 0) {
387 // have to un-const so we can (de)activate
388 item_p = (Fl_Menu_Item *) &(menu_p[i]);
389 if (have_selection) {
390 item_p->activate();
391 }
392 else {
393 item_p->deactivate();
394 }
395 }
396
397 // Paste is different. It becomes active when there
398 // is something in clipboard, and never again becomes
399 // inactive.
400 if (strcmp(mtext, Paste_label) == 0 &&
401 editmenu_p->can_paste()) {
402 ((Fl_Menu_Item *)&(menu_p[i]))->activate();
403 can_paste = true;
404 }
405
406 // Undo is also different. On first change of any
407 // kind it become active, and stays that way,
408 // except it gets reset on new file.
409 if (strcmp(mtext, Undo_label) == 0) {
410 if (undo_active && ! undo_active_on_next_change) {
411 ((Fl_Menu_Item *)&(menu_p[i]))->deactivate();
412 undo_active = false;
413 undo_active_on_next_change = true;
414 }
415 else if ( ! undo_active && undo_active_on_next_change) {
416 ((Fl_Menu_Item *)&(menu_p[i]))->activate();
417 undo_active = true;
418 }
419 }
420
421 // Find and FindNext are inactive when file is empty,
422 // because obviously there is nothing to find.
423 // Similar for Replace and Select All.
424 // Also for all the Run things
425 if (strcmp(mtext, Find_label) == 0 ||
426 strcmp(mtext, FindNext_label) == 0 ||
427 strcmp(mtext, Replace_label) == 0 ||
428 strcmp(mtext, SelectAll_label) == 0 ||
429 strcmp(mtext, Display_label) == 0 ||
430 strcmp(mtext, Play_label) == 0 ||
431 strcmp(mtext, WritePostScript_label) == 0 ||
432 strcmp(mtext, WriteMIDI_label) == 0) {
433 if (bufflength == 0) {
434 ((Fl_Menu_Item *)&(menu_p[i]))->deactivate();
435 }
436 else {
437 ((Fl_Menu_Item *)&(menu_p[i]))->activate();
438 }
439 }
440 }
441 }
442 prev_bufflength = bufflength;
443}
444
445
446// This method gets called when user starts working on a new file.
447// It sets state information so we can know how to gray menu items properly.
448
449void
450Main::begin_new_file()
451{
452 // transition Undo-ability state from true to false
453 undo_active = true;
454 undo_active_on_next_change = false;
455 editor_p->buffer()->call_modify_callbacks();
456 runmenu_p->clean_up();
457}
458
459
460// Handle some special cases.
461// 1. By default fltk will exit the main window upon getting escape.
462// That seems bad, since a vi user will be used to hitting escape all the
463// time when editing, because that is always a "safe" thing to do,
464// and if they did it here by mistake, they would lose all
465// their text entry since the last save. So we ignore the escape in this window.
466// I suppose we could ask if they really want to quit...
467// 2. If user does cut or copy via keyboard accelerator, the normal code
468// for ungraying the Paste button doesn't get called, so we catch that case
469// here and ungray it.
470
471int
472Main::handle_events(int e)
473{
474 // If escape is received while on main window,
475 // return 1 to show that we consumed the event.
476 if (e == FL_SHORTCUT && Fl::event_key() == FL_Escape) {
477 for (Main * m_p = list_p; m_p != 0; m_p = m_p->next) {
478 if (Fl::first_window() == m_p) {
479 return(1);
480 }
481 }
482 }
483
484 // If user did cut or copy via cntl-c or cntl-x,
485 // arrange to ungray Paste.
486 if (e == FL_KEYUP && (Fl::event_state() & FL_CTRL) &&
487 (Fl::event_key() == 'v' || Fl::event_key() == 'x')) {
488 for (Main * m_p = list_p; m_p != 0; m_p = m_p->next) {
489 if (Fl::first_window() == m_p) {
490 m_p->editmenu_p->set_can_paste();
491 break;
492 }
493 }
494 }
495
496 return(0);
497}
498
499
500// If user tries to close the main window via the window manager
501// while having unsaved changes, we ask user if they want to save
502// the changes first.
503
504CALL_BACK(Main, atclose)
505{
506 File::Exit_cb(0, filemenu_p);
507}
508
509
510// Blink the cursor. It can be hard to see if next to selected text if not
511// blinking. We could potentially optimize to only do this while
512// the window has focus, but it doesn't seem worth the complication...
513
514void
515Main::blinker(void * data)
516{
517 Main * obj_p = (Main *) data;
518 // Put cursor into opposite of its current state
519 obj_p->cursor_state ^= 1;
520 obj_p->editor_p->show_cursor(obj_p->cursor_state);
521 // Reset timer to call ourselves again.
522 Fl::repeat_timeout(BLINK_RATE, blinker, data);
523}
524
525
526// Give user hints if we haven't already done that before.
527
528void
529Main::hints(void)
530{
531 int did_startup;
532 (void) Preferences_p->get(Showed_startup_hints, did_startup,
533 Default_startup_hints_flag);
534 if ( ! did_startup) {
535 Help::Startup_Hints_cb(0, (void *) helpmenu_p);
536 }
537}
538
539//----------------------------------------------------------------------
540
541int
542main(int argc, char **argv, const char **arge)
543{
544 // The arge value may get changed when we set new environment
545 // variables, so look up PATH first thing.
546 get_path(arge);
547
548 // Uguide browser needs to show images
549 fl_register_images();
550
551#ifndef OS_LIKE_WIN32
552 // On Windows we use the native Open/Save As dialogs.
553 // On other platforms we use FLTK's, but add icon for Mup files.
554 Fl_File_Icon::load_system_icons();
555 File::add_mup_icon();
556#endif
557
558 // Try to get best hardware support for graphics
559 Fl::visual(FL_DOUBLE|FL_INDEX);
560
561 // Get the user's preferences that persists across sessions
562 Preferences_p = new Fl_Preferences(Fl_Preferences::USER,
563 "arkkra.com", "mupmate");
564
565 // Enable tips when user hovers their mouse over a widget.
566 // If user doesn't like them, they can set delay to huge value.
567 Fl_Tooltip::enable();
568 double tooltips_delay;
569 (void) Preferences_p->get(Tooltips_delay_preference, tooltips_delay,
570 Default_tooltips_delay);
571 Fl_Tooltip::delay(tooltips_delay);
572
573 // Set $MUPPATH
574 char * val;
575 (void) Preferences_p->get(MUPPATH_location, val,
576 Default_MUPPATH_location);
577 set_muppath(val);
578
579 // Tell Mup that it is being run via mupmate,
580 // so it can give more appropriate error messages.
581 putenv("MUPMATE=1");
582
583 // Create main window
584 Main *main_p = new Main("Mupmate");
585
586 // Ensure "escape" key doesn't kill main window,
587 // and make sure Paste ungraying works
588 Fl::add_handler(Main::handle_events);
589
590 // Try to find some reasonable defaults for configuration items
591 // that aren't already set.
592 deduce_helper_locations();
593
594 // If magic file indicating license agreement isn't there,
595 // ask user to agree to Mup license.
596 if (access(magic_file(argv[0]), F_OK) != 0) {
597 // Show the license and get agreement before continuing
598 new License(main_p, magic_file(argv[0]));
599 }
600 else {
601 // Display the main window.
602 main_p->show(1, argv);
603
604 // The first time, we show user some hints.
605 main_p->hints();
606 }
607
608 // Go to where user said they want to store their Mup files by default.
609 // Need to wait to do this until after we have deduced locations
610 // of executable, in case they were in current directory.
611 char * mup_dir;
612 (void) Preferences_p->get(Music_files_location, mup_dir,
613 Default_music_files_location);
614 if (strcmp(mup_dir, ".") != 0) {
615 if (chdir(mup_dir) != 0) {
616 char curr_dir[FL_PATH_MAX] = "current";
617 char message[2 * FL_PATH_MAX + 100];
618 (void *) getcwd(curr_dir, sizeof(curr_dir));
619 sprintf(message, "Unable to change to folder\n"
620 "\"%s.\"\nStaying in \"%s\" folder.\n"
621 "Fix setting of \"Folder for Mup Files\"\n"
622 "in Config->File Locations.",
623 mup_dir, curr_dir);
624 fl_alert(message);
625 }
626 }
627
628 // Expect 0 or 1 args. If 1, should be name of file to load
629 if (argc > 1) {
630 main_p->filemenu_p->load_file(argv[1]);
631 }
632 if (argc > 2) {
633 fl_alert("Only expecting one file; extra arguments are being ignored.");
634 }
635
636 // Go into main event-handler loop
637 int exitvalue = Fl::run();
638 Main::clean_exit(exitvalue);
639 /*NOTREACHED*/
640 return(exitvalue);
641}
642
643
644// Clean up all the windows and their children and exit.
645
646void
647Main::clean_exit(int exitval)
648{
649 Main * m_p;
650 Main * nextwin_p;
651
652 for (m_p = list_p; m_p != 0; m_p = nextwin_p) {
653 nextwin_p = m_p->next;
654 delete m_p;
655 }
656 exit(exitval);
657}
658
659//---------- class to show Mup license and get user's agreement
660
661
662License::License(Main *m_p, const char * magic)
663 : Fl_Double_Window(Default_width, Default_height, "Mup License")
664{
665 // save passed-in info in object data
666 main_p = m_p;
667 magic_file_name = magic;
668
669 // widget for displaying the license text
670 text_p = new Fl_Text_Display(20, 20, w() - 40, h() - 90);
671 resizable((Fl_Widget *) text_p);
672 text_p->buffer( new Fl_Text_Buffer () );
673 text_p->textsize(18);
674 text_p->buffer()->text(license_text);
675
676 i_agree_p = new Fl_Return_Button(100, h() - 50, 100, 30, "I Agree");
677 i_agree_p->callback(IAgree_cb, this);
678
679 cancel_p = new Fl_Button(w() - 200, h() - 50, 100, 30, "Cancel");
680 cancel_p->callback(Cancel_cb, this);
681 cancel_p->shortcut(FL_Escape);
682
683 show();
684
685 // Arrange for destructor to free the new-ed child widgets
686 end();
687
688 // Arrange for window manager closes to do Cancel.
689 callback(Cancel_cb, this);
690 when(FL_WHEN_NEVER);
691}
692
693
694License::~License()
695{
696}
697
698
699// Callback for when user clicks that they agree to the license.
700
701CALL_BACK(License, IAgree)
702{
703 // Create the magic file
704 int fd;
705 if ((fd = open(magic_file_name, O_WRONLY | O_CREAT, 0644)) < 0) {
706 fl_alert("Unable to create file indicating license agreement.");
707 }
708 else {
709 close(fd);
710 }
711
712 hide();
713
714 // Bring up the normal main window
715 main_p->show();
716 main_p->hints();
717}
718
719// Callback if user refuses to accept license. We just exit if they refuse.
720
721CALL_BACK(License, Cancel)
722{
723 exit(0);
724}