#include #include #include #include // Largest file we want to read #define BUF_SIZE 200000 GtkWidget *window; GtkWidget *open_button; /* Called when a file is chosen by the open menu. */ static void set_file(GFile *file, gpointer data) { char *name; unsigned char buf[BUF_SIZE]; const char *output; if (!file) { return; } name = g_file_get_path(file); puts(name); // Porting Note: By Default verovio installs this to "/usr/local/share/verovio" void *pointer = vrvToolkit_constructorResourcePath("/usr/share/verovio"); FILE *cfile = fopen(name, "r"); size_t read = fread(buf, 1, BUF_SIZE - 1, cfile); // If the file is longer than the buffer, truncate it (which may be okay for some formats...) buf[read] = '\0'; fclose(cfile); if (read == 0) return; // Try to put all of the content on the first page. //--page-height 50000 //--adjust-page-height //--breaks smart vrvToolkit_setOptions( pointer, "{\"pageHeight\": 50000, \"adjustPageHeight\": true, \"breaks\": \"smart\"}" ); vrvToolkit_loadData(pointer, buf); output = vrvToolkit_renderToSVG(pointer, 1, TRUE); if (output[0] == '\0') return; gtk_widget_remove_css_class(open_button, "suggested-action"); // https://stackoverflow.com/a/14123191/1861686 GdkPixbufLoader *loader = gdk_pixbuf_loader_new(); gdk_pixbuf_loader_write(loader, output, strlen(output), NULL); gdk_pixbuf_loader_close(loader, NULL); GdkPixbuf *pixbuf = gdk_pixbuf_loader_get_pixbuf(loader); GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf); gtk_window_set_child(GTK_WINDOW(window), image); // TODO: keep this around free(pointer); g_free(name); } static void file_opened(GObject *source, GAsyncResult *result, void *data) { GFile *file; GError *error = NULL; file = gtk_file_dialog_open_finish(GTK_FILE_DIALOG(source), result, &error); if (!file) { g_print("%s\n", error->message); g_error_free(error); } set_file(file, data); } /* Called when the "Open" button is clicked */ static void open_file(GtkButton *picker, GtkLabel *label) { GtkWindow *parent = GTK_WINDOW(gtk_widget_get_root(GTK_WIDGET(picker))); GtkFileDialog *dialog; dialog = gtk_file_dialog_new(); gtk_file_dialog_set_title(dialog, "Select Sheet Music File"); // Filters for the different types that are supported. GListStore *filters; filters = g_list_store_new(GTK_TYPE_FILE_FILTER); // Note: Files are usually recognized by extension (not mime type). GtkFileFilter *filter; // abc notation https://abcnotation.com/ filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "ABC"); gtk_file_filter_add_mime_type(filter, "text/vnd.abc"); gtk_file_filter_add_suffix(filter, "abc"); g_list_store_append(filters, filter); g_object_unref(filter); // TODO: do DARMS and PAE have common extension or mimetype? // DARMS https://wiki.ccarh.org/wiki/DARMS // PAE https://www.iaml.info/plaine-easie-code // Humdrum https://www.humdrum.org/ filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "Humdrum"); gtk_file_filter_add_suffix(filter, "krn"); g_list_store_append(filters, filter); g_object_unref(filter); // MEI https://music-encoding.org/ // MusicXML https://www.musicxml.com/ filter = gtk_file_filter_new(); gtk_file_filter_set_name(filter, "MEI, MusicXML"); // Generic XML? // gtk_file_filter_add_mime_type (filter, "application/xml"); // gtk_file_filter_add_mime_type (filter, "text/xml"); gtk_file_filter_add_suffix(filter, "xml"); // MusicXML gtk_file_filter_add_mime_type(filter, "application/vnd.recordare.musicxml+xml"); gtk_file_filter_add_suffix(filter, "musicxml"); // Compressed MusicXML gtk_file_filter_add_mime_type(filter, "application/vnd.recordare.musicxml"); gtk_file_filter_add_suffix(filter, "mxl"); // MEI gtk_file_filter_add_suffix(filter, "mei"); g_list_store_append(filters, filter); gtk_file_dialog_set_default_filter(dialog, filter); g_object_unref(filter); gtk_file_dialog_set_filters(dialog, G_LIST_MODEL(filters)); g_object_unref(filters); gtk_file_dialog_open(dialog, parent, NULL, file_opened, label); g_object_unref(dialog); } static void activate(GtkApplication *app, gpointer user_data) { GtkWidget *header; GtkWidget *image; // Window window = gtk_application_window_new(app); gtk_window_set_title(GTK_WINDOW(window), "SheMuVi"); gtk_window_set_default_size(GTK_WINDOW(window), 400, 600); gtk_widget_set_visible(window, TRUE); // Header header = gtk_header_bar_new(); open_button = gtk_button_new(); GtkWidget *button_content = adw_button_content_new(); adw_button_content_set_icon_name(ADW_BUTTON_CONTENT(button_content), "document-open-symbolic"); adw_button_content_set_label(ADW_BUTTON_CONTENT(button_content), "Load"); gtk_button_set_child(GTK_BUTTON(open_button), button_content); gtk_widget_add_css_class(open_button, "raised"); gtk_widget_add_css_class(open_button, "suggested-action"); gtk_widget_set_tooltip_text(open_button, "Load file"); g_signal_connect(open_button, "clicked", G_CALLBACK(open_file), NULL); gtk_header_bar_pack_start(GTK_HEADER_BAR(header), open_button); gtk_window_set_titlebar(GTK_WINDOW(window), header); // Image will be added by "open" GtkWidget *label = gtk_label_new_with_mnemonic("Please open a file."); gtk_window_set_child(GTK_WINDOW(window), label); } void app_open(GtkApplication *app, GFile **files, gint n_files, const gchar *hint) { activate(app, NULL); // TODO: do we actually want to handle opening multiple in one application? set_file(files[0], NULL); } int main(int argc, char **argv) { AdwApplication *app; int status; app = adw_application_new( "com.zachdecook.shemuvi", G_APPLICATION_DEFAULT_FLAGS | G_APPLICATION_HANDLES_OPEN ); g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); g_signal_connect(app, "open", G_CALLBACK(app_open), NULL); status = g_application_run(G_APPLICATION(app), argc, argv); g_object_unref(app); return status; }