#include "snd.h" /* ---------------- HELP MONOLOG ---------------- */ static GtkWidget *help_dialog = NULL; static void dismiss_help_dialog(GtkWidget *w, gpointer context) { gtk_widget_hide(help_dialog); } static gint delete_help_dialog(GtkWidget *w, GdkEvent *event, gpointer context) { gtk_widget_hide(help_dialog); return(true); } #define HELP_ROWS 12 #define HELP_COLUMNS 72 /* these set the initial size of the help dialog text area */ static GtkWidget *help_text = NULL; slist *related_items = NULL; static char *original_help_text = NULL; static const char **help_urls = NULL; static void add_help_text(GtkWidget *text, const char *message) { sg_text_insert(text, (char *)message); } int help_text_width(const char *txt, int start, int end) { return((end - start) * 8); } static with_word_wrap_t outer_with_wrap = WITHOUT_WORD_WRAP; static int old_help_text_width = 0; #if (!GTK_CHECK_VERSION(3, 96, 0)) static gboolean help_expose_callback(GtkWidget *w, GdkEventExpose *ev, gpointer data) { int curwid; curwid = widget_width(help_dialog); /* was help_text, but I'm getting shivering in expose events */ if (old_help_text_width == 0) old_help_text_width = curwid; else { if ((outer_with_wrap == WITH_WORD_WRAP) && (abs(curwid - old_help_text_width) > 50)) { char *cur_help_str, *new_help_str; cur_help_str = sg_get_text(help_text, 0, -1); new_help_str = word_wrap(original_help_text, curwid); gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(help_text)), "", 0); sg_text_insert(help_text, new_help_str); if (new_help_str) free(new_help_str); if (cur_help_str) g_free(cur_help_str); old_help_text_width = curwid; } } return(false); } #endif static bool new_help(const char *pattern, bool complain) { const char *url; const char **xrefs; url = snd_url(pattern); if (url) { /* given name, find doc string, if any */ Xen xstr; xstr = g_snd_help(C_string_to_Xen_string(pattern), 0); if (Xen_is_string(xstr)) { int gc_loc; gc_loc = snd_protect(xstr); xrefs = help_name_to_xrefs(pattern); snd_help_with_xrefs(pattern, Xen_string_to_C_string(xstr), WITH_WORD_WRAP, xrefs, NULL); snd_unprotect_at(gc_loc); if (xrefs) free(xrefs); return(true); } url_to_html_viewer(url); return(true); } if ((!(snd_topic_help(pattern))) && (complain)) { xrefs = help_name_to_xrefs(pattern); if (xrefs) { snd_help_with_xrefs(pattern, "(no help found)", WITH_WORD_WRAP, xrefs, NULL); free(xrefs); return(true); } else snd_help_with_xrefs(pattern, "(no help found)", WITH_WORD_WRAP, NULL, NULL); } return(false); } static char *find_highlighted_text(const char *value) { int i, len, start = -1; len = mus_strlen(value); for (i = 0; i < len; i++) if (value[i] == '{') start = i + 1; else { if (value[i] == '}') { int end; end = i; if ((start > 0) && ((end - start) > 0)) { int k; char *topic; topic = (char *)calloc(end - start + 1, sizeof(char)); for (i = start, k = 0; i < end; i++, k++) topic[k] = value[i]; return(topic); } } } return(NULL); } static void help_browse_callback(const char *name, int row, void *data) { if ((help_urls) && (help_urls[row])) url_to_html_viewer(help_urls[row]); else { char *topic; topic = find_highlighted_text(name); if (topic) { name_to_html_viewer(topic); free(topic); } else { if (name) new_help(name, true); } } } static gboolean text_release_callback(GtkTreeSelection *selection, gpointer *gp) { /* this needs to be bool return false -- otherwise, apparently, the mouse-drag->selection never gets turned off! */ GtkTextIter start, end; #define HELP_BUFFER gtk_text_view_get_buffer(GTK_TEXT_VIEW(help_text)) if (gtk_text_buffer_get_selection_bounds(HELP_BUFFER, &start, &end)) { char *txt; txt = gtk_text_buffer_get_text(HELP_BUFFER, &start, &end, true); /* only change help text display if it's one word in the selection, and we can find new help */ if (txt) { int i, len; bool one_word = true; len = mus_strlen(txt); for (i = 0; i < len; i++) if (isspace((int)txt[i])) { one_word = false; break; } if (one_word) new_help(txt, false); g_free(txt); } } return(false); } static void search_activated(GtkWidget *w, gpointer context) { char *str; str = (char *)gtk_entry_get_text(GTK_ENTRY(w)); /* no free, I think */ if (new_help(str, true)) gtk_entry_set_text(GTK_ENTRY(w), ""); } static void create_help_monolog(void) { /* create scrollable but not editable text window */ GtkWidget *ok_button, *search, *frame; help_dialog = snd_gtk_dialog_new(); #if GTK_CHECK_VERSION(3, 14, 0) gtk_window_set_transient_for(GTK_WINDOW(help_dialog), GTK_WINDOW(main_shell(ss))); #endif SG_SIGNAL_CONNECT(help_dialog, "delete_event", delete_help_dialog, NULL); gtk_window_set_title(GTK_WINDOW(help_dialog), I_HELP); sg_make_resizable(help_dialog); sg_container_set_border_width (GTK_CONTAINER(help_dialog), 10); gtk_window_resize(GTK_WINDOW(help_dialog), HELP_COLUMNS * 9, HELP_ROWS * 40); gtk_widget_realize(help_dialog); ok_button = gtk_dialog_add_button(GTK_DIALOG(help_dialog), "Go Away", GTK_RESPONSE_NONE); gtk_widget_set_name(ok_button, "dialog_button"); SG_SIGNAL_CONNECT(ok_button, "clicked", dismiss_help_dialog, NULL); #if GTK_CHECK_VERSION(3, 0, 0) add_highlight_button_style(ok_button); #endif gtk_widget_show(ok_button); frame = gtk_frame_new(NULL); sg_box_pack_start(GTK_BOX(DIALOG_CONTENT_AREA(help_dialog)), frame, true, true, 10); gtk_widget_show(frame); help_text = make_scrolled_text(frame, false, CONTAINER_ADD, false); /* last arg ignored */ #if (!GTK_CHECK_VERSION(3, 92, 1)) gtk_widget_add_events(help_text, GDK_BUTTON_RELEASE); #endif gtk_text_view_set_left_margin(GTK_TEXT_VIEW(help_text), 10); SG_SIGNAL_CONNECT(help_text, "button_release_event", text_release_callback, NULL); related_items = slist_new_with_title("related topics", DIALOG_CONTENT_AREA(help_dialog), NULL, 0, BOX_PACK); related_items->select_callback = help_browse_callback; { GtkWidget *label, *hbox; #if (!HAVE_GTK_GRID_NEW) hbox = gtk_hbox_new(false, 0); sg_box_pack_start(GTK_BOX(DIALOG_CONTENT_AREA(help_dialog)), hbox, false, false, 10); gtk_widget_show(hbox); label = gtk_label_new("help topic:"); sg_box_pack_start(GTK_BOX(hbox), label, false, false, 0); gtk_widget_show(label); search = snd_entry_new(hbox, NULL, WITH_WHITE_BACKGROUND); #else GtkWidget *content_area; content_area = gtk_dialog_get_content_area(GTK_DIALOG(help_dialog)); hbox = gtk_grid_new(); gtk_container_add(GTK_CONTAINER(content_area), hbox); gtk_widget_show(hbox); label = gtk_label_new("help topic:"); gtk_widget_set_halign(label, GTK_ALIGN_CENTER); gtk_widget_set_hexpand(label, false); g_object_set(label, "margin", 10, NULL); gtk_grid_attach(GTK_GRID(hbox), label, 0, 0, 1, 1); gtk_widget_show(label); search = snd_entry_new(hbox, label, WITH_WHITE_BACKGROUND); #endif } SG_SIGNAL_CONNECT(search, "activate", search_activated, NULL); gtk_widget_show(help_dialog); #if (!GTK_CHECK_VERSION(3, 96, 0)) SG_SIGNAL_CONNECT((gpointer)help_dialog, DRAW_SIGNAL, help_expose_callback, NULL); #endif set_dialog_widget(HELP_DIALOG, help_dialog); } GtkWidget *snd_help(const char *subject, const char *helpstr, with_word_wrap_t with_wrap) { /* place help string in scrollable help window */ /* if window is already active, add this help at the top and reposition */ outer_with_wrap = with_wrap; if (!(help_dialog)) create_help_monolog(); else raise_dialog(help_dialog); gtk_window_set_title(GTK_WINDOW(help_dialog), subject); original_help_text = (char *)helpstr; gtk_text_buffer_set_text(gtk_text_view_get_buffer(GTK_TEXT_VIEW(help_text)), "", 0); if (with_wrap == WITH_WORD_WRAP) { char *new_help; new_help = word_wrap(helpstr, (int)(widget_width(help_text) * 1.3)); add_help_text(help_text, new_help); if (new_help) free(new_help); } else add_help_text(help_text, helpstr); slist_clear(related_items); /* this can clobber "subject"! */ return(help_dialog); } GtkWidget *snd_help_with_xrefs(const char *subject, const char *helpstr, with_word_wrap_t with_wrap, const char **xrefs, const char **urls) { GtkWidget *w; help_urls = urls; w = snd_help(subject, helpstr, with_wrap); if (xrefs) { int i; for (i = 0; (xrefs[i]); i++) slist_append(related_items, xrefs[i]); } return(w); } void snd_help_append(const char *text) { if (help_text) sg_text_insert(help_text, text); } void snd_help_back_to_top(void) { if (help_text) { GtkTextIter pos; GtkTextBuffer *buf; buf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(help_text)); gtk_text_buffer_get_iter_at_offset(buf, &pos, 0); gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(help_text), &pos, 0.0, true, 0.0, 0.0); } } /* -------------------------------- FIND DIALOG -------------------------------- */ static GtkWidget *edit_find_dialog, *edit_find_text, *find_cancel_button, *edit_find_label, *find_next_button, *find_previous_button; static GtkWidget *find_error_frame = NULL, *find_error_label = NULL; static chan_info *find_channel = NULL; static gulong find_key_press_handler_id = 0; static void clear_find_error(void) { if ((find_error_frame) && (widget_is_active(find_error_frame))) set_label(find_error_label, ""); if (find_key_press_handler_id) { g_signal_handler_disconnect(edit_find_text, find_key_press_handler_id); find_key_press_handler_id = 0; } } static gboolean find_modify_key_press(GtkWidget *w, GdkEventKey *event, gpointer data) { clear_find_error(); return(false); } void errors_to_find_text(const char *msg, void *data) { find_dialog_set_label(msg); gtk_widget_show(find_error_frame); find_key_press_handler_id = SG_SIGNAL_CONNECT(edit_find_text, "key_press_event", find_modify_key_press, NULL); } void stop_search_if_error(const char *msg, void *data) { errors_to_find_text(msg, data); ss->stopped_explicitly = true; /* should be noticed in global_search in snd-find.c */ } static void edit_find_dismiss(GtkWidget *w, gpointer context) { if (ss->checking_explicitly) ss->stopped_explicitly = true; else { gtk_widget_hide(edit_find_dialog); clear_find_error(); } } static gint edit_find_delete(GtkWidget *w, GdkEvent *event, gpointer context) { clear_find_error(); gtk_widget_hide(edit_find_dialog); return(true); } static void edit_find_help(GtkWidget *w, gpointer context) { find_dialog_help(); } static void edit_find_find(read_direction_t direction, GtkWidget *w, gpointer context) { #if HAVE_EXTENSION_LANGUAGE find_dialog_find((char *)gtk_entry_get_text(GTK_ENTRY(edit_find_text)), direction, find_channel); #endif } void find_dialog_stop_label(bool show_stop) { if (show_stop) set_stock_button_label(find_cancel_button, I_STOP); else set_stock_button_label(find_cancel_button, I_GO_AWAY); } void find_dialog_set_label(const char *str) { if (edit_find_label) set_label(edit_find_label, str); } static void edit_find_next(GtkWidget *w, gpointer context) { edit_find_find(READ_FORWARD, w, context); } static void edit_find_previous(GtkWidget *w, gpointer context) { edit_find_find(READ_BACKWARD, w, context); } static void make_edit_find_dialog(bool managed, chan_info *cp) { if (!edit_find_dialog) { GtkWidget *dl, *rc; GtkWidget *help_button; edit_find_dialog = snd_gtk_dialog_new(); #if GTK_CHECK_VERSION(3, 14, 0) gtk_window_set_transient_for(GTK_WINDOW(edit_find_dialog), GTK_WINDOW(main_shell(ss))); #endif SG_SIGNAL_CONNECT(edit_find_dialog, "delete_event", edit_find_delete, NULL); gtk_window_set_title(GTK_WINDOW(edit_find_dialog), I_FIND); sg_make_resizable(edit_find_dialog); sg_container_set_border_width (GTK_CONTAINER(edit_find_dialog), 10); gtk_window_resize(GTK_WINDOW(edit_find_dialog), 350, 120); gtk_widget_realize(edit_find_dialog); find_next_button = gtk_dialog_add_button(GTK_DIALOG(edit_find_dialog), "Forward", GTK_RESPONSE_NONE); find_previous_button = gtk_dialog_add_button(GTK_DIALOG(edit_find_dialog), "Back", GTK_RESPONSE_NONE); find_cancel_button = gtk_dialog_add_button(GTK_DIALOG(edit_find_dialog), "Go Away", GTK_RESPONSE_NONE); help_button = gtk_dialog_add_button(GTK_DIALOG(edit_find_dialog), "Help", GTK_RESPONSE_NONE); gtk_widget_set_name(help_button, "dialog_button"); gtk_widget_set_name(find_cancel_button, "dialog_button"); gtk_widget_set_name(find_previous_button, "dialog_button"); gtk_widget_set_name(find_next_button, "dialog_button"); #if GTK_CHECK_VERSION(3, 0, 0) add_highlight_button_style(find_cancel_button); add_highlight_button_style(help_button); add_highlight_button_style(find_previous_button); add_highlight_button_style(find_next_button); #endif SG_SIGNAL_CONNECT(find_cancel_button, "clicked", edit_find_dismiss, NULL); SG_SIGNAL_CONNECT(help_button, "clicked", edit_find_help, NULL); SG_SIGNAL_CONNECT(find_next_button, "clicked", edit_find_next, NULL); SG_SIGNAL_CONNECT(find_previous_button, "clicked", edit_find_previous, NULL); gtk_widget_show(find_cancel_button); gtk_widget_show(find_next_button); gtk_widget_show(find_previous_button); gtk_widget_show(help_button); rc = gtk_hbox_new(false, 0); sg_box_pack_start(GTK_BOX(DIALOG_CONTENT_AREA(edit_find_dialog)), rc, true, true, 4); gtk_widget_show(rc); dl = gtk_label_new(I_find); sg_box_pack_start(GTK_BOX(rc), dl, false, false, 4); gtk_widget_show(dl); edit_find_text = snd_entry_new(rc, NULL, WITH_WHITE_BACKGROUND); SG_SIGNAL_CONNECT(edit_find_text, "activate", edit_find_next, NULL); edit_find_label = gtk_label_new(""); sg_box_pack_start(GTK_BOX(DIALOG_CONTENT_AREA(edit_find_dialog)), edit_find_label, false, false, 4); gtk_widget_show(edit_find_label); find_error_frame = gtk_frame_new(NULL); sg_box_pack_end(GTK_BOX(DIALOG_CONTENT_AREA(edit_find_dialog)), find_error_frame, false, false, 4); find_error_label = gtk_label_new(""); gtk_container_add(GTK_CONTAINER(find_error_frame), find_error_label); gtk_widget_show(find_error_label); if (managed) gtk_widget_show(edit_find_dialog); set_dialog_widget(FIND_DIALOG, edit_find_dialog); } else { if (managed) raise_dialog(edit_find_dialog); } } void edit_find_callback(GtkWidget *w, gpointer context) { make_edit_find_dialog(true, NULL); } void find_dialog(chan_info *cp) { /* used in snd-kbd.c */ make_edit_find_dialog(true, cp); } bool find_dialog_is_active(void) { return((edit_find_dialog) && (widget_is_active(edit_find_dialog))); } void save_find_dialog_state(FILE *fd) { if (find_dialog_is_active()) { const char *text; /* text = sg_get_text(edit_find_text, 0, -1); -- this is for text_view widgets, but edit_find_text is a gtk_entry */ text = gtk_entry_get_text(GTK_ENTRY(edit_find_text)); #if HAVE_SCHEME if (text) fprintf(fd, "(%s #t \"%s\")\n", S_find_dialog, text); else { if (ss->search_expr) fprintf(fd, "(%s #t \"%s\")\n", S_find_dialog, ss->search_expr); else fprintf(fd, "(%s #t)\n", S_find_dialog); } #endif #if HAVE_RUBY if (text) fprintf(fd, "%s(true, \"%s\")\n", to_proc_name(S_find_dialog), text); else { if (ss->search_expr) fprintf(fd, "%s(true, \"%s\")\n", to_proc_name(S_find_dialog), ss->search_expr); else fprintf(fd, "%s(true)\n", to_proc_name(S_find_dialog)); } #endif #if HAVE_FORTH if (text) fprintf(fd, "#t \"%s\" %s drop\n", text, S_find_dialog); else { if (ss->search_expr) fprintf(fd, "#t \"%s\" %s drop\n", ss->search_expr, S_find_dialog); else fprintf(fd, "#t %s drop\n", S_find_dialog); } #endif /* if (text) g_free(text); */ } } static Xen g_find_dialog(Xen managed, Xen text) { #define H_find_dialog "(" S_find_dialog " :optional managed text): create and activate the Edit:Find dialog, return the dialog widget" Xen_check_type(Xen_is_boolean_or_unbound(managed), managed, 1, S_find_dialog, "a boolean"); Xen_check_type(Xen_is_string_or_unbound(text), text, 2, S_find_dialog, "a string"); make_edit_find_dialog(Xen_boolean_to_C_bool(managed), NULL); if ((edit_find_text) && (Xen_is_string(text))) gtk_entry_get_text(GTK_ENTRY(edit_find_text)); return(Xen_wrap_widget(edit_find_dialog)); } static Xen g_find_dialog_widgets(void) { if (edit_find_dialog) return(Xen_cons(Xen_wrap_widget(edit_find_dialog), Xen_cons(Xen_wrap_widget(edit_find_text), Xen_cons(Xen_wrap_widget(find_next_button), Xen_cons(Xen_wrap_widget(find_previous_button), Xen_cons(Xen_wrap_widget(find_cancel_button), Xen_empty_list)))))); return(Xen_empty_list); } Xen_wrap_2_optional_args(g_find_dialog_w, g_find_dialog) Xen_wrap_no_args(g_find_dialog_widgets_w, g_find_dialog_widgets) void g_init_gxfind(void) { Xen_define_typed_procedure(S_find_dialog, g_find_dialog_w, 0, 2, 0, H_find_dialog, s7_make_signature(s7, 3, s7_make_symbol(s7, "pair?"), s7_make_symbol(s7, "boolean?"), s7_make_symbol(s7, "string?"))); Xen_define_typed_procedure("find-dialog-widgets", g_find_dialog_widgets_w, 0, 0, 0, "internal auto-test function", s7_make_signature(s7, 1, s7_make_symbol(s7, "list?"))); }