/* file.cpp * Denemo File IO * * for Denemo, a gtk+ frontend to GNU Lilypond * (c) Adam Tee, Matthew Hiller 2000-2005 * (c) University of Leeds 2000-2005 */ #include "calculatepositions.h" #include "commandfuncs.h" #include "contexts.h" #include #include "dialogs.h" #include "exportabc.h" #include "exportmudela.h" #include "file.h" #include "frogio.h" #include "frogdefs.h" #include "moveviewport.h" #include "staffops.h" #include "scoreops.h" #include "utils.h" #include "exportxml.h" #include "exportmidi.h" #include "importxml.h" #include "exportcsound.h" #include "importmidi.h" #include "lyparserfuncs.h" /*#include "lyparser.h"*/ #include "prefops.h" #include "binreloc.h" #include "view.h" #include #include #include /* check existance and type of files */ #include /* filter and sort filenames */ #include /* find filenames matches */ #include /* I use free() */ typedef enum { DENEMO_FORMAT = 0, DNM_FORMAT, MUDELA_FORMAT, ABC_FORMAT, JTF_FORMAT, MIDI_FORMAT, JTF_POLY_FORMAT, CSOUND_FORMAT } FileFormatNames; /* Keep this up to date ! */ #define FIRST_FORMAT_NAME DENEMO_FORMAT #define LAST_FORMAT_NAME CSOUND_FORMAT struct FileFormatData { gchar *filename_mask; gchar *description; gchar *filename_extension; }; static struct FileFormatData supported_import_file_formats[] = { {"*.denemo", N_("Denemo XML format (*.denemo)"), ".denemo"}, {"*.dnm", N_("Denemo XML format (*.dnm)"), ".dnm"}, // {"*.ly", N_("Lilypond (formerly Mudela) (*.ly)"), ".ly"}, {"*.mid", N_("Midi (*.mid)"), ".mid"}, {"*.midi", N_("Midi (*.midi)"), ".midi"} }; static struct FileFormatData supported_export_file_formats[] = { {"*.denemo", N_("Denemo XML format (*.denemo)"), ".denemo"}, {"*.dnm", N_("Denemo XML format (*.dnm)"), ".dnm"}, {"*.ly", N_("Lilypond (formerly Mudela) (*.ly)"), ".ly"}, {"*.abc", N_("ABC (*.abc)"), ".abc"}, {"*.jtf", N_("Unnamed file format (*.jtf)"), ".jtf"}, {"*.mid", N_("Midi (*.mid)"), ".mid"}, {"*.jtf", N_("Unnamed file format (*.jtf) Poly"), ".jtf"}, {"*.sco", N_("CSound Score File (*.sco)"), ".sco"} }; /* Some macros just to shorten lines */ #define FORMAT_MASK(i) supported_export_file_formats[i].filename_mask #define FORMAT_DESCRIPTION(i) supported_export_file_formats[i].description #define FORMAT_EXTENSION(i) supported_export_file_formats[i].filename_extension #define COLUMN_NAME (0) #define COLUMN_ID (1) struct callbackdata { struct scoreinfo *si; GtkWidget *fs; GtkWidget *comboentry; }; static gchar *file_selection_path = NULL; /* Prototypes for non-exported functions */ static gint guess_file_format (gchar * file_name); /** * Display a message box asking the user to confirm that unsaved * changes will be lost * @return TRUE if the OK button was pressed */ gboolean confirmbox (DenemoGUI * gui) { GtkWidget *dialog; gboolean r = 0; dialog = gtk_message_dialog_new (NULL, (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _("The current score has changes in it" " which you have not saved.")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Proceed with the operation " "nonetheless?")); gtk_widget_show_all (dialog); r = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES); gtk_widget_destroy (dialog); return r; } /** * This function is to be called after a file load of any sort. * * @param si pointer to the scoreinfo structure */ void updatescoreinfo (DenemoScore * si) { staffnode *curstaff; for (curstaff = si->thescore; curstaff; curstaff = curstaff->next) { beamsandstemdirswholestaff ((DenemoStaff *) curstaff->data); showwhichaccidentalswholestaff ((DenemoStaff *) curstaff->data); } find_xes_in_all_measures (si); find_leftmost_allcontexts (si); si->currentstaff = si->thescore; si->currentmeasure = firstmeasurenode (si->currentstaff); si->currentobject = firstobjnode (si->currentmeasure); if (!si->currentobject) si->cursor_appending = TRUE; else si->cursor_appending = FALSE; si->leftmeasurenum = si->currentstaffnum = si->currentmeasurenum = 1; //si->cursor_x = 0; si->haschanged = FALSE; } /** * Custom function to compare the elements of the History queue * it simply wraps up stcmp * * @param a pointer to a queue elements * @param b pointer to the comparison value * @return gint 0 if match -1 or 1 otherwise */ static gint history_compare (gconstpointer a, gconstpointer b) { return (strcmp ((gchar *) a, (gchar *) b)); } /** * Sets the filename in the scoreinfo structure and * for the main window */ void set_si_filename (DenemoGUI * gui, gchar * filename) { gchar *dialog_title = g_strconcat ("Denemo - ", filename, NULL); GList ret; gtk_window_set_title (GTK_WINDOW (gui->window), dialog_title); g_free (dialog_title); g_string_assign (gui->si->filename, filename); if (!g_queue_find_custom (gui->prefs->history, gui->si->filename->str, &history_compare)) { #ifdef DEBUG g_print ("%s not in history list\n", gui->si->filename->str); #endif if (g_queue_get_length (gui->prefs->history) > MAX_HISTORY) { gpointer data = g_queue_pop_head (gui->prefs->history); if (data) g_free (data); } GtkWidget *item = gtk_ui_manager_get_widget (gui->ui_manager, "/MainMenu/FileMenu/OpenRecent/Stub"); GtkWidget *menu = gtk_widget_get_parent (GTK_WIDGET (item)); struct cbd data; data.gui = gui; data.menu = menu; addhistorymenuitem (filename, &data); g_queue_push_tail (gui->prefs->history, gui->si->filename->str); } } /** * The function that actually determines the file type and calls the * function that opens the file. (So many layers of indirection...) * * @return success or failure */ gint open_for_real (gchar * filename, DenemoGUI * gui) { gint result = -1; if (gui->si->lily_file != NULL) abandon_lily_tree (gui); if (strcmp (filename + strlen (filename) - 7, ".denemo") == 0) result = importXML (filename, gui->si); else if (strcmp (filename + strlen (filename) - 4, ".dnm") == 0) result = importXML (filename, gui->si); // else if (strcmp (filename + strlen (filename) - 3, ".ly") == 0) // result = lyinput (filename, si); else if (strcmp (filename + strlen (filename) - 4, ".mid") == 0 || strcmp (filename + strlen (filename) - 5, ".midi") == 0) result = importMidi (filename, gui); else if (strcmp (filename + strlen (filename) - 4, ".jtf") == 0) result = froginput (filename, gui->si); if (result != -1) { set_si_filename (gui, filename); updatescoreinfo (gui->si); set_rightmeasurenum (gui->si); set_bottom_staff (gui); update_hscrollbar (gui); update_vscrollbar (gui); gtk_widget_draw (gui->scorearea, NULL); gtk_signal_emit_by_name (GTK_OBJECT (gui->hadjustment), "changed"); gtk_signal_emit_by_name (GTK_OBJECT (gui->vadjustment), "changed"); } return result; } /** * denemo_warning prompts the user to save the work in the denemo * format if not done so. * @param si pointer to the denemo score object * @param format_id the numeric id of the files format * @return none */ static void denemo_warning (DenemoScore * si, gint format_id) { if (format_id != DENEMO_FORMAT && format_id != DNM_FORMAT) { GtkWidget *dialog; dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "You have made changes to your document that was not saved as denemo file." " I advise you save your work now as a denemo file to easily continue work later. Save as denemo?"); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT); if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) { gchar **file = g_strsplit (si->filename->str, ".", 0); file[0] = g_strconcat (file[0], ".denemo", NULL); g_print ("file %s\n", file[0]); exportXML (file[0], si, 0, 0); g_strfreev (file); } gtk_widget_destroy (dialog); } } /** * File save callback called by fileselsave callback * */ static void filesel_save (DenemoGUI * gui, const gchar * file_name, gint format_id) { g_assert (gui != NULL); g_assert (file_name != NULL); g_assert (format_id >= 0 && format_id < (int) G_N_ELEMENTS (supported_export_file_formats)); DenemoScore *si = gui->si; // Append file extension extension gchar *file = NULL; if (g_pattern_match_simple (FORMAT_MASK (format_id), file_name)) { file = g_strdup (file_name); } else { file = g_strconcat (file_name, FORMAT_EXTENSION (format_id), NULL); } /* we don't want to save scores under ".denemo", ".jtf" and so on. do we? */ if (*(strrchr (file, '/') + 1) != '.') { switch (format_id) { case DENEMO_FORMAT: case DNM_FORMAT: { exportXML (file, si, 0, 0); break; }; case MUDELA_FORMAT: { exportmudela (file, si, 0, 0); break; }; case ABC_FORMAT: { exportabc (file, si, 0, 0); break; }; case JTF_FORMAT: { filesave (file, si, 0, 0, 0); break; }; case MIDI_FORMAT: { exportmidi (file, si, 0, 0); break; }; case JTF_POLY_FORMAT: { filesave (file, si, 0, 0, 1); break; }; case CSOUND_FORMAT: { exportcsound (file, si, 0, 0); break; }; default: break; }; /*export parts as lilypond files*/ if(gui->prefs->saveparts) export_lilypond_parts(file,si,0,0); si->haschanged = FALSE; if (gui->prefs) { g_timeout_add (gui->prefs->autosave_timeout * 1000 * 60, (GSourceFunc) auto_save_document_timeout, si); set_si_filename (gui, file); } g_free (file); } if (gui->statusbar) { gchar *tmp = NULL; tmp = g_strconcat (file_name, " saved", NULL); gtk_statusbar_push (GTK_STATUSBAR (gui->statusbar), gui->status_context_id, tmp); g_free (tmp); } denemo_warning (gui->si, format_id); } /** * Open template file wrapper function * Sets the file_selection_path to the templates directory and * calls file_open to create the file selection dialog */ void file_open_template_wrapper (GtkAction * action, DenemoGUI * gui) { file_selection_path = gbr_find_data_dir (PKGDATADIR); file_selection_path = g_strconcat (file_selection_path, "templates", NULL); g_print ("Template file %s\n", file_selection_path); gui->si->readonly = 1; file_open (NULL, gui); } /** * Wrapper function for opening a file * checks to see if current score has changed and prompts user to save * otherwise opens the file */ void file_openwrapper (GtkAction * action, DenemoGUI * gui) { if (gui->si->haschanged) { if (confirmbox (gui)) { file_open (NULL, gui); } } else { file_open (NULL, gui); } } /** * File open dialog - opened where appropriate * */ void file_open (GtkWidget * widget, DenemoGUI * gui) { GtkWidget *file_selection; GtkFileFilter *filter; int i; file_selection = gtk_file_chooser_dialog_new (_("Open"), GTK_WINDOW (gui->window), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); /* Open the last visited directory, if any. */ if (file_selection_path != NULL) { gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_selection), file_selection_path); } /* open preset directory*/ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_selection), gui->prefs->denemopath->str); for (i = 0; i < (gint) G_N_ELEMENTS (supported_import_file_formats); i++) { filter = gtk_file_filter_new (); gtk_file_filter_set_name (filter, supported_import_file_formats[i].description); gtk_file_filter_add_pattern (filter, supported_import_file_formats[i]. filename_mask); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_selection), filter); } filter = gtk_file_filter_new (); gtk_file_filter_set_name (filter, _("All files")); gtk_file_filter_add_pattern (filter, "*"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (file_selection), filter); gtk_dialog_set_default_response (GTK_DIALOG (file_selection), GTK_RESPONSE_ACCEPT); gtk_widget_show_all (file_selection); if (gtk_dialog_run (GTK_DIALOG (file_selection)) == GTK_RESPONSE_ACCEPT) { gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_selection)); open_for_real (filename, gui); g_free (filename); } gtk_widget_destroy (file_selection); } /** * Wrapper function to save the current file if not already * saved. */ void file_saveaswrapper (GtkAction * action, DenemoGUI * gui) { file_saveas (NULL, gui); } /** * Wrapper function for saving an existing file * */ void file_savewrapper (GtkAction * action, DenemoGUI * gui) { file_save (NULL, gui); } /** * Filters the filename based on its extension and calls * the relevant export function */ void file_save (GtkWidget * widget, DenemoGUI * gui) { DenemoScore *si = gui->si; g_print ("READONLY %d\n", si->readonly); if ((strcmp (si->filename->str, "") == 0) || (si->readonly == 1)) /* No filename's been given or is opened from template */ file_saveas (widget, gui); else switch (guess_file_format (si->filename->str)) { case DENEMO_FORMAT: case DNM_FORMAT: { exportXML (si->filename->str, si, 0, 0); break; }; case MUDELA_FORMAT: { exportmudela (si->filename->str, si, 0, 0); break; }; case ABC_FORMAT: { exportabc (si->filename->str, si, 0, 0); break; }; case JTF_POLY_FORMAT: { filesave (si->filename->str, si, 0, 0, 1); break; }; case JTF_FORMAT: { filesave (si->filename->str, si, 0, 0, 0); break; }; case MIDI_FORMAT: { exportmidi (si->filename->str, si, 0, 0); break; }; case CSOUND_FORMAT: { exportcsound (si->filename->str, si, 0, 0); break; }; default: { exportXML (si->filename->str, si, 0, 0); break; }; }; /*Save parts as lilypond files*/ if(gui->prefs->saveparts) export_lilypond_parts(si->filename->str,si,0,0); denemo_warning (gui->si, guess_file_format (si->filename->str)); si->haschanged = FALSE; } /** * Create file saveas dialog to enable user to save the current file to * * */ void file_saveas (GtkWidget * widget, DenemoGUI * gui) { GtkWidget *file_selection; GtkWidget *label; GtkWidget *combobox; GtkWidget *hbox; GtkListStore *list_store; GtkTreeIter iter; GtkCellRenderer *renderer; file_selection = gtk_file_chooser_dialog_new (_("Save As"), GTK_WINDOW (gui->window), GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); /*set default folder for saving */ gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_selection), gui->prefs->denemopath->str); /* assign title */ if (gui->si->headerinfo->title != NULL){ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (file_selection), gui->si->headerinfo->title->str); } // else { // gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (file_selection), "Untitled Document"); //} /* Open the last visited directory, if any. */ if (file_selection_path != NULL) { gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (file_selection), file_selection_path); }; hbox = gtk_hbox_new (FALSE, 8); label = gtk_label_new (_("Format:")); gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, TRUE, 0); list_store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT); combobox = gtk_combo_box_new_with_model (GTK_TREE_MODEL (list_store)); renderer = gtk_cell_renderer_text_new (); gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combobox), renderer, TRUE); gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (combobox), renderer, "text", COLUMN_NAME); gtk_box_pack_start (GTK_BOX (hbox), combobox, TRUE, TRUE, 0); if (gui->si->lily_file) { gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, COLUMN_NAME, _(FORMAT_DESCRIPTION (MUDELA_FORMAT)), COLUMN_ID, MUDELA_FORMAT, -1); } else { int i; for (i = 0; i < (int) G_N_ELEMENTS (supported_export_file_formats); i++) { gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, COLUMN_NAME, supported_export_file_formats[i].description, COLUMN_ID, i, -1); } } gtk_tree_model_get_iter_first (GTK_TREE_MODEL (list_store), &iter); gtk_combo_box_set_active_iter (GTK_COMBO_BOX (combobox), &iter); gtk_file_chooser_set_extra_widget (GTK_FILE_CHOOSER (file_selection), hbox); gtk_dialog_set_default_response (GTK_DIALOG (file_selection), GTK_RESPONSE_ACCEPT); gtk_widget_show_all (file_selection); gboolean close = FALSE; do { if (gtk_dialog_run (GTK_DIALOG (file_selection)) == GTK_RESPONSE_ACCEPT) { gint format_id = -1; gchar *file_name = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (file_selection)); if (replace_existing_file_dialog (file_name, GTK_WINDOW (gui->window))) { gtk_combo_box_get_active_iter (GTK_COMBO_BOX (combobox), &iter); gtk_tree_model_get (GTK_TREE_MODEL (list_store), &iter, COLUMN_ID, &format_id, -1); filesel_save (gui, file_name, format_id); close = TRUE; } g_free (file_name); } else { close = TRUE; } } while (!close); gtk_widget_destroy (file_selection); } /** * Wrapper function for creating a new file * */ void file_newwrapper (GtkAction * action, DenemoGUI * gui) { if (gui->si->haschanged) { if (confirmbox (gui)) { deletescore (NULL, gui); } } else { deletescore (NULL, gui); }; } /** * Delete the given score and create a new one * */ void deletescore (GtkWidget * widget, DenemoGUI * gui) { free_score (gui->si); newstaff (gui->si, INITIAL, DENEMO_NONE); set_rightmeasurenum (gui->si); update_hscrollbar (gui); update_vscrollbar (gui); gtk_widget_draw (gui->scorearea, NULL); } /** * Try to suggest the format of a given file, after its file name extension. A * more powerful function could be written to guess the format after the * file contents */ gint guess_file_format (gchar * file_name) { gint name_iterator; gboolean format_match; name_iterator = FIRST_FORMAT_NAME; format_match = FALSE; while (!format_match && name_iterator <= LAST_FORMAT_NAME) { format_match = (fnmatch (FORMAT_MASK (name_iterator++), file_name, 0) == 0); }; /* In case no match could be found, we just give a 'default' format. * Chances are that all formats will be wrong, however ;-) */ if (!format_match) return (DENEMO_FORMAT); else return (--name_iterator); }; #if 0 void safe_file_save_as (GtkWidget * widget, gpointer data) { struct stat file_information; GtkWidget *dialog; GtkWidget *ok_button; GtkWidget *cancel_button; GtkWidget *label; struct callbackdata *cbdata; gint format_id; gchar *file_name; cbdata = (struct callbackdata *) data; file_name = (gchar *) gtk_file_selection_get_filename (GTK_FILE_SELECTION (cbdata->fs)); update_file_selection_path (file_name); format_id = GPOINTER_TO_INT (gtk_object_get_user_data (GTK_OBJECT (cbdata->comboentry))); /* Prevent names with double extensions: whatever.denemo.denemo */ if (fnmatch (FORMAT_MASK (format_id), file_name, 0) == 0) file_name = g_strdup (file_name); else file_name = g_strconcat (file_name, FORMAT_EXTENSION (format_id), NULL); if (!stat (file_name, &file_information)) { /* if we arrive in here, the suggested file_name already exists, if * it's a directory we just do nothing (that's should never happen) * otherwise, we ask the user */ if (!S_ISDIR (file_information.st_mode)) { dialog = gtk_dialog_new (); gtk_window_set_title (GTK_WINDOW (dialog), _("Confirm file replacement")); label = gtk_label_new (_ ("There already exists a file by that " "name at the chosen location")); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, TRUE, TRUE, 0); gtk_widget_show (label); label = gtk_label_new (_("and it will be overwritten. " "Proceed nonetheless?")); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label, TRUE, TRUE, 0); gtk_widget_show (label); ok_button = gtk_button_new_with_label (_("Ok")); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), ok_button, TRUE, TRUE, 0); gtk_widget_show (ok_button); cancel_button = gtk_button_new_with_label (_("Cancel")); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->action_area), cancel_button, TRUE, TRUE, 0); gtk_widget_show (cancel_button); #if GTK_MAJOR_VERSION > 1 g_signal_connect (GTK_OBJECT (ok_button), "clicked", G_CALLBACK (filesel_save), data); g_signal_connect_swapped (GTK_OBJECT (ok_button), "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) dialog); g_signal_connect_swapped (GTK_OBJECT (cancel_button), "clicked", G_CALLBACK (gtk_widget_destroy), (gpointer) dialog); #else gtk_signal_connect (GTK_OBJECT (ok_button), "clicked", GTK_SIGNAL_FUNC (filesel_save), data); gtk_signal_connect_object (GTK_OBJECT (ok_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (dialog)); gtk_signal_connect_object (GTK_OBJECT (cancel_button), "clicked", GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (dialog)); #endif gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE); gtk_widget_show (dialog); } } else { /* Suggested file didn't exist, just save it */ filesel_save (cbdata->si, file_name, format_id); }; } #endif /** * Reloads a lilypond file specified by the .denemo/reloadfile.ly * only used when lilypond mode is active */ void reload_lily_file (GtkWidget * button, gpointer data) { DenemoGUI *gui = (DenemoGUI *) data; GString *filename; GString *realfilename; gint target_measure; gint result; if (gui->si->lily_file) { if (!GTK_WIDGET_IS_SENSITIVE (gui->scorearea)) { gtk_widget_set_sensitive (gui->textview, FALSE); gtk_widget_set_sensitive (gui->scorearea, TRUE); } target_measure = gui->si->leftmeasurenum; realfilename = g_string_new (gui->si->filename->str); filename = g_string_new (locatedotdenemo ()); filename = g_string_append (filename, "/reloadfile.ly"); exportmudela (filename->str, gui->si, 0, 0); result = open_for_real (filename->str, gui); if (result == -1) return; set_si_filename (gui, realfilename->str); gui->si->haschanged = TRUE; set_currentmeasurenum (gui, target_measure); /* may have been hidden when lily file was unparseable */ gtk_widget_show_all (gui->window); /* only shown when on DENEMO_MEASURES */ gtk_widget_hide (gui->musicdatabutton); } } /** * Creates dialog to say that the chosen filename already exists * and do you want to overwrite it. * */ gboolean replace_existing_file_dialog (const gchar * filename, GtkWindow * parent_window) { if (!g_file_test (filename, G_FILE_TEST_EXISTS)) { return TRUE; } GtkWidget *dialog = gtk_message_dialog_new (parent_window, (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, _ ("A file with the name %s already exists."), filename); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Do you want to replace it?")); gtk_widget_show_all (dialog); gboolean r = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES); gtk_widget_destroy (dialog); g_print ("Yes dialog is %d\n", r); return r; } /** * Save parts to individual files */ void file_savepartswrapper (GtkAction * action, DenemoGUI * gui) { if (!gui->si->filename) { file_saveas (NULL, gui); } export_lilypond_parts (gui->si->filename->str, gui->si, 0, 0); }