1 /* Copyright (c) 2006 by Arkkra Enterprises */
2 /* All rights reserved */
4 // Code for the File menu off the main toolbar
12 #include <FL/fl_ask.H>
14 #include <FL/Fl_File_Icon.H>
16 #include <FL/Fl_Shared_Image.H>
17 #include <FL/Fl_Input.H>
18 #include <FL/filename.H>
19 #include <FL/Fl_Bitmap.H>
20 #include <FL/Fl_File_Chooser.H>
24 #include "Preferences.H"
32 // The file name filters
33 #define Mup_filter "*.mup"
34 #define All_filter "*.*"
36 extern char dir_separator();
37 extern const char * const template_text;
40 //-----------------Class to implement the File menu off the main menu bar
45 unsaved_changes = false;
57 //--- the "New" menu item -------------
61 // Ask user if they want to save any unsaved change on
62 // current file first.
63 switch (save_changes_check()) {
64 default: // default case should be impossible
65 case Save_confirm_dialog::Cancel:
67 case Save_confirm_dialog::No:
69 case Save_confirm_dialog::Yes:
74 // Clear the current edit buffer
75 Fl_Text_Buffer * buffer_p = editor_p->buffer();
76 buffer_p->replace(0, buffer_p->length(), "");
83 // Reset state information
88 // --- the "New From Template" menu item
90 CALL_BACK(File, NewFromTemplate)
93 editor_p->buffer()->append(template_text);
94 unsaved_changes = false;
97 //--- the "Open" menu item -------------
101 // If already editing a file, ask user if they want to
102 // save any unsaved changes first
103 switch (save_changes_check()) {
104 default: // default case should be impossible
105 case Save_confirm_dialog::Cancel:
107 case Save_confirm_dialog::No:
109 case Save_confirm_dialog::Yes:
114 // Clear out label to "Untitled" in case the load fails
117 // Ask user for filename. If they give one, load it into editor.
118 const char * newfile = open_ask_user();
119 if (newfile != 0 && newfile[0] != '\0') {
125 //--- the "Save" menu item -------------
127 // Save the current buffer contents.
128 // If honor_auto_display is true and user has requested auto display,
129 // do the Run>Display action. If the save was due to finishing up an old
130 // file to start on a new or due to saving before exiting, we don't do that;
131 // it is only done if user did an explicit Save or Save As.
133 CALL_BACK_A(File, Save, bool honor_auto_display)
136 // No file name given yet, so change into a Save As
137 SaveAs(honor_auto_display);
140 save_file(honor_auto_display);
145 //--- the "SaveAs" menu item -------------
147 CALL_BACK_A(File, SaveAs, bool honor_auto_display)
149 // Ask user for name of file to save to
150 const char * newfile = save_as_ask_user();
151 if (newfile != 0 && newfile[0] != '\0') {
153 // If user didn't give a suffix, add .mup suffix.
154 // If they used .ps or .mid or .err suffix, don't allow that.
155 const char * suffix = fl_filename_ext(newfile);
156 char * suffixed_filename = 0;
157 if (*suffix == '\0') {
158 // User did not supply a suffix, so we add .mup
159 suffixed_filename = new char[strlen(newfile) + 5];
160 (void) sprintf(suffixed_filename, "%s.mup", newfile);
161 newfile = suffixed_filename;
163 else if (strcasecmp(suffix, ".ps") == 0
164 || strcasecmp(suffix, ".mid") == 0
165 || strcasecmp(suffix, ".err") == 0) {
166 fl_alert("A filename extension of .ps .mid or .err\n"
167 "is not allowed for Mup input files,\n"
168 "since those extensions are used for\n"
169 "PostScript, MIDI, and error output files.");
173 if (access(newfile, F_OK) == 0) {
174 const char * ask_replace = " already exists. Do you want to replace it?";
175 char question[strlen(newfile) + strlen(ask_replace) + 1];
176 (void) sprintf(question, "%s%s\n", newfile, ask_replace);
177 switch (Save_confirm_dialog::confirm_save(question)) {
178 default: // default case should be impossible.
179 case Save_confirm_dialog::Cancel:
180 case Save_confirm_dialog::No:
181 if (suffixed_filename != 0) {
182 delete suffixed_filename;
185 case Save_confirm_dialog::Yes:
190 // Save the name of the new file
192 // forget previous file name
195 if (suffixed_filename != 0) {
196 filename = suffixed_filename;
199 filename = new char[strlen(newfile) + 1];
200 (void) sprintf(filename, newfile);
204 save_file(honor_auto_display);
209 //--- the "Exit" menu item -------------
211 CALL_BACK(File, Exit)
213 switch (save_changes_check()) {
214 default: // default case should be impossible
215 case Save_confirm_dialog::Cancel:
217 case Save_confirm_dialog::No:
219 case Save_confirm_dialog::Yes:
226 //------------------------------------------------------------------------
228 // This class needs access to editor window, so whoever creates an instance
229 // of this class needs to call this function to give it access.
232 File::set_editor(Fl_Text_Editor * ed)
238 // We need to set parent's label, etc. Save info about parent.
241 File::set_parent(Main * win_p)
243 parent_window_p = win_p;
246 // Called when user makes a change in editor window.
247 // Let's us know if we need to prompt user about unsaved changes.
250 File::modify_cb(int, int num_inserted, int num_deleted, int, const char *, void * data)
252 if (num_inserted > 0 || num_deleted > 0) {
253 ((File *)data)->unsaved_changes = true;
258 // Utility method that does the details of saving the current file
261 File::save_file(bool honor_auto_display)
263 if (editor_p->buffer()->savefile(filename) != 0) {
264 fl_alert("failed to save file %s", filename);
267 // All unsaved changes have now been saved
268 unsaved_changes = false;
270 // If user wants auto-display on save, do that
271 if (honor_auto_display) {
273 (void) Preferences_p->get(Auto_display_preference, auto_display,
274 Default_auto_display);
276 parent_window_p->runmenu_p->Display();
283 // Utility method to ask user if they want to save changes.
285 // Cancel don't do anything
286 // No do action without saving changes
287 // Yes save changes before doing action
288 // The extra_text argument allows caller to enhance the question
289 // asked of the user. The "No" button can be hidden.
291 Save_confirm_dialog::Answer
292 File::save_changes_check(const char * extra_text, bool hide_the_No)
294 if ( ! unsaved_changes ) {
295 return Save_confirm_dialog::No;
297 const char * name = effective_filename();
298 char question[200 + strlen(name) + strlen(extra_text)];
299 (void) sprintf(question, "The text in the %s file has changed. "
300 "Do you want to save the changes? %s", name, extra_text);
301 return(Save_confirm_dialog::confirm_save(question, hide_the_No));
305 // Read the given file into the editor window.
308 File::load_file(const char * name)
310 // Free up existing file name, if any
316 // If name supplied doesn't have .mup suffix,
317 // try again with that suffix added.
319 if (access(name, F_OK) != 0) {
320 if (strlen(name) < 5 ||
321 strcasecmp(name + strlen(name) - 4, ".mup") != 0) {
322 newname = new char[strlen(name) + 5];
323 (void) sprintf(newname, "%s.mup", name);
327 newname = new char[strlen(name) + 1];
328 (void) strcpy(newname, name);
331 // Mup files are typically only a few Kbytes,
332 // so we probably don't need the default 128 K buffer,
333 // but memory is cheap enough these days, so just go with that.
334 if (editor_p->buffer()->loadfile(newname) != 0) {
335 fl_alert("Unable to load file \"%s\"", newname);
342 // Reset state information for a new file
345 // add file name to window label
350 File::set_window_label()
352 if (parent_window_p == 0) {
355 const char * name = effective_filename();
356 char label[strlen(name) + 11];
357 (void) sprintf(label, "%s - Mupmate", name);
358 parent_window_p->copy_label(label);
363 File::effective_filename(void)
365 return(filename == 0 ? "Untitled.mup" : filename);
369 File::begin_new_file(void)
371 parent_window_p->begin_new_file();
372 unsaved_changes = false;
375 #ifndef OS_LIKE_WIN32
376 // Add Icon for Mup files.
378 // Figure out note polygon one time and just add these offsets for the other...
379 #define RIGHT_NOTE_X 5800
380 #define RIGHT_NOTE_Y -1200
382 static short Mup_icon_data[] = {
383 Fl_File_Icon::COLOR, 0, FL_RED,
386 Fl_File_Icon::POLYGON,
387 Fl_File_Icon::VERTEX, 600, 1200,
388 Fl_File_Icon::VERTEX, 600, 8200,
389 Fl_File_Icon::VERTEX, 1200, 8200,
390 Fl_File_Icon::VERTEX, 1200, 1200,
391 Fl_File_Icon::VERTEX, 600, 1200,
395 Fl_File_Icon::POLYGON,
396 Fl_File_Icon::VERTEX, 6400, 600,
397 Fl_File_Icon::VERTEX, 6400, 7000,
398 Fl_File_Icon::VERTEX, 7000, 7000,
399 Fl_File_Icon::VERTEX, 7000, 600,
400 Fl_File_Icon::VERTEX, 6400, 600,
404 Fl_File_Icon::POLYGON,
405 Fl_File_Icon::VERTEX, 600, 1200,
406 Fl_File_Icon::VERTEX, 600, 2700,
407 Fl_File_Icon::VERTEX, 7000, 2100,
408 Fl_File_Icon::VERTEX, 7000, 600,
409 Fl_File_Icon::VERTEX, 600, 2000,
413 Fl_File_Icon::POLYGON,
414 Fl_File_Icon::VERTEX, 600, 8200,
415 Fl_File_Icon::VERTEX, 1600, 9400,
416 Fl_File_Icon::VERTEX, 3200, 9400,
417 Fl_File_Icon::VERTEX, 3800, 8800,
418 Fl_File_Icon::VERTEX, 3800, 8000,
419 Fl_File_Icon::VERTEX, 3000, 7200,
420 Fl_File_Icon::VERTEX, 1600, 7200,
421 Fl_File_Icon::VERTEX, 800, 7900,
422 Fl_File_Icon::VERTEX, 800, 8200,
423 Fl_File_Icon::VERTEX, 500, 9000,
427 Fl_File_Icon::POLYGON,
428 Fl_File_Icon::VERTEX, 600 + RIGHT_NOTE_X, 8200 + RIGHT_NOTE_Y,
429 Fl_File_Icon::VERTEX, 1600 + RIGHT_NOTE_X, 9400 + RIGHT_NOTE_Y,
430 Fl_File_Icon::VERTEX, 3200 + RIGHT_NOTE_X, 9400 + RIGHT_NOTE_Y,
431 Fl_File_Icon::VERTEX, 3800 + RIGHT_NOTE_X, 8800 + RIGHT_NOTE_Y,
432 Fl_File_Icon::VERTEX, 3800 + RIGHT_NOTE_X, 8000 + RIGHT_NOTE_Y,
433 Fl_File_Icon::VERTEX, 3000 + RIGHT_NOTE_X, 7200 + RIGHT_NOTE_Y,
434 Fl_File_Icon::VERTEX, 1600 + RIGHT_NOTE_X, 7200 + RIGHT_NOTE_Y,
435 Fl_File_Icon::VERTEX, 800 + RIGHT_NOTE_X, 7900 + RIGHT_NOTE_Y,
436 Fl_File_Icon::VERTEX, 800 + RIGHT_NOTE_X, 8200 + RIGHT_NOTE_Y,
437 Fl_File_Icon::VERTEX, 500 + RIGHT_NOTE_X, 9000 + RIGHT_NOTE_Y,
444 File::add_mup_icon(void)
446 new Fl_File_Icon("*.mup", Fl_File_Icon::PLAIN,
447 sizeof(Mup_icon_data) / sizeof(Mup_icon_data[0]),
454 // Ask user for name of file to open, and return their choice
457 File::open_ask_user(void)
460 OPENFILENAME openfilename;
461 static CHAR path[FL_PATH_MAX] = "*.mup";
462 CHAR dir[FL_PATH_MAX];
463 memset(&openfilename, 0, sizeof(openfilename));
464 GetCurrentDirectory(sizeof(dir), dir);
465 openfilename.lStructSize = sizeof(openfilename);
466 parent_window_p->make_current();
467 openfilename.hwndOwner = fl_window;
468 openfilename.hInstance = fl_display;
469 openfilename.lpstrFilter = "Mup files (*.mup)\0*.mup\0All files (*.*)\0*.*\0";
470 openfilename.lpstrFile = path;
471 openfilename.nMaxFile = sizeof(path);
472 openfilename.lpstrInitialDir = dir;
473 openfilename.Flags = OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
474 openfilename.lpstrDefExt = ".mup";
476 if (GetOpenFileName(&openfilename)) {
484 fl_file_chooser_ok_label("Open");
485 return (fl_file_chooser("Open Mup file", "Mup files (*.mup)\tAll files (*)", "*.mup"));
490 // Ask user for name of file to save to, and return their choice
493 File::save_as_ask_user(void)
496 OPENFILENAME openfilename;
497 static CHAR path[FL_PATH_MAX] = "*.mup";
498 CHAR dir[FL_PATH_MAX];
499 memset(&openfilename, 0, sizeof(openfilename));
500 GetCurrentDirectory(sizeof(dir), dir);
501 openfilename.lStructSize = sizeof(openfilename);
502 parent_window_p->make_current();
503 openfilename.hwndOwner = fl_window;
504 openfilename.hInstance = fl_display;
505 openfilename.lpstrFilter = "Mup files (*.mup)\0*.mup\0All files (*.*)\0*.*\0";
506 openfilename.lpstrFile = path;
507 openfilename.nMaxFile = sizeof(path);
508 openfilename.lpstrInitialDir = dir;
509 openfilename.Flags = OFN_PATHMUSTEXIST | OFN_HIDEREADONLY;
510 openfilename.lpstrDefExt = ".mup";
511 if (GetSaveFileName(&openfilename)) {
518 fl_file_chooser_ok_label("Save");
519 return (fl_file_chooser("Save Mup file", "Mup files (*.mup)\tAll files (*)", "*.mup"));
524 //---------------------------------------------------------------------
525 // The fl_choice function has the middle button as the default,
526 // but we want the left button (Yes) to be the default for whether
527 // to save before exiting, so we have a special class for it.
528 // It is based on the fl_choice code.
530 Save_confirm_dialog::Save_confirm_dialog(const char * text)
531 : Fl_Double_Window(500, 130, "Confirm")
533 // Make question mark "icon"
534 icon_p = new Fl_Box(10, 10, 50, 50);
535 icon_p->box(FL_THIN_UP_BOX);
536 icon_p->labelfont(FL_TIMES_BOLD);
537 icon_p->labelsize(30);
538 icon_p->color(FL_WHITE);
539 icon_p->labelcolor(FL_BLUE);
542 // Print the question
543 message_p = new Fl_Box(90, 20, 400, 40);
544 message_p->label(text);
545 message_p->align(FL_ALIGN_INSIDE | FL_ALIGN_LEFT | FL_ALIGN_WRAP);
547 yes_p = new Fl_Return_Button(210, h() - 50, 70, 30, "Yes");
549 no_p = new Fl_Button(300, h() - 50, 70, 30, "No");
551 cancel_p = new Fl_Button(390, h() - 50, 70, 30, "Cancel");
552 cancel_p->shortcut(FL_Escape);
555 // Arrange for destructor to free new-ed widgets
559 Save_confirm_dialog::~Save_confirm_dialog()
564 // Method to bring up dialog to ask user if they want to save changes.
566 // Cancel don't do anything
567 // No do action without saving changes
568 // Yes save changes before doing action
570 Save_confirm_dialog::Answer
571 Save_confirm_dialog::confirm_save(const char * text, bool hide_the_No)
573 // Create dialog window
574 Save_confirm_dialog * confirm_p = new Save_confirm_dialog(text);
576 // Only show desired buttons
578 confirm_p->no_p->hide();
581 // Wait for user to select a button
584 Fl_Widget *widget_p = Fl::readqueue();
588 else if (widget_p == confirm_p->cancel_p ||
589 widget_p == confirm_p) {
593 else if (widget_p == confirm_p->no_p) {
597 else if (widget_p == confirm_p->yes_p) {
603 // Clean up the dialog window