/* * Copyright (C) 2002-2012 Edscott Wilson Garcia * EMail: edscott@users.sf.net * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; */ /** miscelaneous */ #ifdef HAVE_CONFIG_H # include #endif #define EXPAND TRUE #define FILL TRUE #define NOEXPAND FALSE #define NOFILL FALSE #include "rfm.h" #include "rfm_modules.h" #include "primary-misc.i" static void checkmenuitem_toggle(GtkWidget *menuitem, void *data){ gboolean state = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)); const gchar *markup_id = (state)?"on_label":"off_label"; GtkWidget *child = gtk_bin_get_child (GTK_BIN (menuitem)); gtk_label_set_markup (GTK_LABEL (child), (const gchar *)(g_object_get_data(G_OBJECT(menuitem), markup_id))); gtk_label_set_markup (GTK_LABEL (child), (const gchar *)(g_object_get_data(G_OBJECT(menuitem), markup_id))); } GtkWidget * rfm_create_checkmenuitem(const gchar *label, gboolean state){ GtkWidget *menuitem = gtk_check_menu_item_new_with_label (""); GtkWidget *child = gtk_bin_get_child (GTK_BIN (menuitem)); const gchar *markup_id = (state)?"on_label":"off_label"; g_object_set_data(G_OBJECT(menuitem), "on_label", g_strdup_printf("%s", label)); g_object_set_data(G_OBJECT(menuitem), "off_label", g_strdup_printf("%s", label)); gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM (menuitem), state); checkmenuitem_toggle(menuitem, NULL); g_signal_connect(G_OBJECT (menuitem), "toggled", G_CALLBACK (checkmenuitem_toggle), NULL); return menuitem; } GtkWidget * rfm_create_radiomenuitem(const gchar *label, GSList *group){ GtkWidget *menuitem = gtk_radio_menu_item_new_with_mnemonic (group, label); GtkWidget *child = gtk_bin_get_child (GTK_BIN (menuitem)); g_object_set_data(G_OBJECT(menuitem), "on_label", g_strdup_printf("%s", label)); g_object_set_data(G_OBJECT(menuitem), "off_label", g_strdup_printf("%s", label)); checkmenuitem_toggle(menuitem, NULL); g_signal_connect(G_OBJECT (menuitem), "toggled", G_CALLBACK (checkmenuitem_toggle), NULL); return menuitem; } void rfm_null_function (void *user_data) { return ; } // This is a thread function... void rfm_markup_stdout_f (void *user_data, void *stream, int childFD) { widgets_t *widgets_p = user_data; char *line; line = (char *)stream; NOOP ("FORK stdout: %s\n", line); if(line[0] == '\n') return; if(strncmp (line, "Tubo-id exit:", strlen ("Tubo-id exit:")) == 0) { if(strchr (line, '\n')) *strchr (line, '\n') = 0; rfm_threaded_diagnostics (widgets_p, "xffm/stock_no", NULL); rfm_context_function(rfm_scroll_to_top, widgets_p); } else { if (strchr(line, 0x0d)){ *strchr(line, 0x0d) = ' ';// ^M } if (strchr(line, '-')){ gchar buffer[256*10]; memset (buffer, 0, 256*10); gint i; gint j; gboolean on=FALSE; for (j=0,i=0; i<255; i++,j++){ if (!on && (line[i] == '-' || line[i] == '[')){ buffer[j++] = 27; buffer[j++] = '['; buffer[j++] = '1'; buffer[j++] = 'm'; on = TRUE; } if (on && (line[i] == ' ' || line[i] == ')' || line[i] == '\t')){ buffer[j++] = 27; buffer[j++] = '['; buffer[j++] = '0'; buffer[j++] = 'm'; on = FALSE; } buffer[j] = line[i]; if (line[i] == 0) break; } rfm_threaded_diagnostics (widgets_p, NULL, g_strdup(buffer)); } else { rfm_threaded_diagnostics (widgets_p, NULL, g_strdup(line)); } } return; } void * rfm_scroll(widgets_t *widgets_p, gboolean up, gboolean page){ GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW ((widgets_p->diagnostics))); gint lines = gtk_text_buffer_get_line_count (buffer); NOOP(stderr, "lines+%d\n", lines); GtkTextIter iter; GtkTextMark *mark = gtk_text_buffer_get_mark (buffer, "scrollmark2"); if (mark == NULL){ gtk_text_buffer_get_iter_at_line (buffer,&iter,lines); mark = gtk_text_buffer_create_mark (buffer, "scrollmark2", &iter, FALSE); } gtk_text_buffer_get_iter_at_mark (buffer,&iter, mark); gint line_count = 0; while (!gtk_text_view_move_mark_onscreen (GTK_TEXT_VIEW ((widgets_p->diagnostics)),mark)){ if (up) { if (!gtk_text_iter_backward_visible_line (&iter)) break; } else { if (!gtk_text_iter_forward_visible_line (&iter)) break; } line_count++; gtk_text_buffer_move_mark (buffer, mark ,&iter); } if (up) { gtk_text_iter_backward_lines (&iter, (page)?line_count:1); } else { gtk_text_iter_forward_lines (&iter, (page)?line_count:1); } gtk_text_buffer_move_mark (buffer, mark ,&iter); gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW ((widgets_p->diagnostics)), mark); //gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW ((widgets_p->diagnostics)), mark, 0.0, // TRUE, 0.0, 0.0); //gtk_text_buffer_delete_mark (buffer, mark); return NULL; } void * rfm_scroll_to_top(void *data){ widgets_t *widgets_p = data; GtkTextMark *mark; GtkTextIter start, end; GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW ((widgets_p->diagnostics))); gtk_text_buffer_get_bounds (buffer, &start, &end); mark = gtk_text_buffer_create_mark (buffer, "scrollmark", &start, FALSE); gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW ((widgets_p->diagnostics)), mark, 0.2, /*gdouble within_margin, */ FALSE, 0.0, 0.0); gtk_text_buffer_delete_mark (buffer, mark); return NULL; } record_entry_t * rfm_find_in_selection_list(view_t *view_p, record_entry_t *population_en){ if (!view_p->selection_list) return FALSE; record_entry_t *found=NULL; GSList *tmp=view_p->selection_list; for (; tmp && tmp->data; tmp=tmp->next){ record_entry_t *en=tmp->data; if (!en || !population_en || !en->path || !population_en->path) continue; if (strcmp(en->path, population_en->path)==0){ found=tmp->data; break; } } return found; } void rfm_select_pixbuf (view_t * view_p, const population_t * population_p) { NOOP(stderr, "rfm_select_pixbuf\n"); if(!population_p) { NOOP( "rfm_select_pixbuf: !population_p\n"); return; } if(!population_p->en) { NOOP( "rfm_select_pixbuf: !population_p->en\n"); return; } if(POPULATION_MODULE(population_p)) { /* ask the POPULATION_MODULE whether the element is selectable * (by default they will not be)*/ if(!rfm_natural (PLUGIN_DIR, POPULATION_MODULE(population_p), population_p->en, "is_selectable")) { NOOP( "rfm_select_pixbuf: !is_selectable\n"); return; } } else if(population_p->en && IS_DUMMY_TYPE (population_p->en->type) && !g_path_is_absolute(population_p->en->path)){ NOOP( "rfm_select_pixbuf: IS_DUMMY_TYPE\n"); return; } if (!(population_p->flags & POPULATION_SELECTED)) { ((population_t *)population_p)->flags |= POPULATION_SELECTED; } if (population_p->en && !rfm_find_in_selection_list(view_p, population_p->en)) { record_entry_t *en=rfm_copy_entry(population_p->en); view_p->selection_list = g_slist_append (view_p->selection_list, en); } view_p->mouse_event.selected_p = population_p; } void rfm_unselect_pixbuf (view_t * view_p, const population_t * population_p) { NOOP("rfm_unselect_pixbuf: >> unselect_pixbuf\n"); if(!population_p) { DBG ("!population_p\n"); return; } if (population_p->flags & POPULATION_SELECTED) { ((population_t *)population_p)->flags &= (POPULATION_SELECTED ^ 0xffffffff); } record_entry_t *item = rfm_find_in_selection_list(view_p, population_p->en); if(item) { view_p->selection_list = g_slist_remove (view_p->selection_list, item); rfm_destroy_entry(item); if(!g_slist_length (view_p->selection_list)) { g_slist_free (view_p->selection_list); view_p->selection_list = NULL; } } } static gboolean tooltip_is_mapped = FALSE; gboolean rfm_tooltip_is_mapped(void){ return tooltip_is_mapped; } static void tooltip_unmap (GtkWidget *window, gpointer data){ tooltip_is_mapped = FALSE; } // This is a gtk placement bug workaround. Should probably fix gtk code and // submit the patch: this is a long standing bug... static void tooltip_map (GtkWidget *window, gpointer data){ tooltip_is_mapped = TRUE; } static GdkPixbuf * shadow_it(const GdkPixbuf *src_pixbuf){ gint width = gdk_pixbuf_get_width (src_pixbuf); gint height = gdk_pixbuf_get_height (src_pixbuf); gint offset = 7; GdkPixbuf *shadowed = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width + offset, height + offset); if (!shadowed) return NULL; gdk_pixbuf_fill(shadowed, 0x0); gdk_pixbuf_copy_area (src_pixbuf, 0, 0, width, height, shadowed, offset, offset); gdk_pixbuf_saturate_and_pixelate (shadowed, shadowed, 0.0, TRUE); // gfloat saturation, gboolean pixelate); gdk_pixbuf_composite (src_pixbuf, shadowed, 0, 0, //dest_x, dest_y, width, height, //dest_width, dest_height, 0, 0, //offset_x, offset_y, 1.0, 1.0, //scale_x, scale_y, GDK_INTERP_NEAREST, 255); //overall_alpha); return shadowed; } void rfm_set_box_gradient(GtkWidget *wbox){ // only for gtk+3 #if GTK_MAJOR_VERSION==3 GtkStyleContext *style_context = gtk_widget_get_style_context (wbox); gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_TOOLTIP ); #if 0 GtkGrid {\ background-image: -gtk-gradient (linear, left top, right top, from (#aaa), to (#000));\ color: rgb(0,0,0);\ background-color: rgb(255,255,255);\ text-shadow: 1px 1px 0 white, -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white, 2px 2px 0 white, 1px 2px 0 white, 2px 1px 0 white;\ }\ #endif GtkCssProvider *css_provider = gtk_css_provider_new(); GError *error=NULL; gtk_css_provider_load_from_data (css_provider, "\ GtkEntry {\ background-image: -gtk-gradient (linear, left top, right top, from (#888), to (#666));\ color: rgb(250,250,250);\ text-shadow: 0px 1px 0 black;\ }\ GtkBox,GtkEventBox,GtkFrame {\ background-image: -gtk-gradient (linear, left top, right top, from (#aaa), to (#000));\ color: rgb(255, 255, 255);\ border-width: 0px;\ border-radius: 0px;\ border-color: transparent;\ }\ ", -1, &error); if (error){ fprintf(stderr, "gerror: %s\n", error->message); g_error_free(error); } gtk_style_context_add_provider (style_context, GTK_STYLE_PROVIDER(css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); #endif return; } static void tooltip_placement_bug_workaround(GtkWidget *tooltip_window){ #if GTK_MAJOR_VERSION==3 #if GTK_MINOR_VERSION>=8 // gtk3.8 bug workaround (still in current 3.13): static gint last_x = 0; static gint last_y = 0; GdkScreen *screen = gtk_widget_get_screen (tooltip_window); gint monitor_num = gdk_screen_get_monitor_at_point (screen, last_x, last_y); GdkRectangle monitor; gdk_screen_get_monitor_workarea (screen, monitor_num, &monitor); last_x = monitor.width-1; last_y = monitor.height-1; gtk_window_move (GTK_WINDOW (tooltip_window), last_x,last_y); #endif #endif return; } // If you just want the frame (as if to put in the properties dialog) // call this function with widget set to NULL. Frame is what this function // returns. // If widget is not NULL, then the gtk tooltip for the widget is set to the // window created by this function. GtkWidget * rfm_create_tooltip_window(GtkWidget *widget, GtkWidget *tooltip_window, const GdkPixbuf *pixbuf, const gchar *markup, const gchar *label_text){ if (widget) { gtk_widget_set_has_tooltip (widget, TRUE); if (!tooltip_window) { tooltip_window = gtk_window_new(GTK_WINDOW_POPUP); NOOP(stderr, "New tooltip window now...\n"); g_signal_connect (G_OBJECT (tooltip_window), "map", G_CALLBACK (tooltip_map), NULL); g_signal_connect (G_OBJECT (tooltip_window), "unmap", G_CALLBACK (tooltip_unmap), NULL); gtk_window_set_type_hint (GTK_WINDOW (tooltip_window), GDK_WINDOW_TYPE_HINT_TOOLTIP); gtk_widget_set_app_paintable (tooltip_window, TRUE); gtk_window_set_resizable (GTK_WINDOW (tooltip_window), FALSE); gtk_widget_set_name (tooltip_window, "gtk-tooltip"); #if GTK_MAJOR_VERSION<3 GdkColor black = {0, 0, 0, 0}; gtk_widget_modify_bg(tooltip_window, GTK_STATE_NORMAL, &black); #endif } else { GtkWidget *old_content = gtk_bin_get_child(GTK_BIN(tooltip_window)); gtk_container_remove(GTK_CONTAINER(tooltip_window), old_content); } } GtkWidget *vbox = rfm_vbox_new(FALSE, 2); gtk_widget_show(vbox); if (widget){ GtkWidget *top_frame = gtk_frame_new(NULL); gtk_widget_show(top_frame); gtk_container_add (GTK_CONTAINER (tooltip_window), top_frame); gtk_container_add (GTK_CONTAINER (top_frame), vbox); } GtkWidget *wbox = gtk_event_box_new(); if (widget) { #if GTK_MAJOR_VERSION<3 gint tip_bg = 0xf600; GdkColor whitish = {0, tip_bg, tip_bg, tip_bg}; if (widget) gtk_widget_modify_bg(wbox, GTK_STATE_NORMAL, &whitish); #endif } gtk_container_add (GTK_CONTAINER (vbox), wbox); gtk_widget_show(wbox); GtkWidget *frame = gtk_frame_new(NULL); gtk_widget_show(frame); if (label_text) { GtkWidget *label = gtk_label_new(""); gtk_widget_show(label); gchar *utf_text = rfm_utf_string (label_text); gchar *label_markup; if (widget) { #if GTK_MAJOR_VERSION<3 label_markup = g_strdup_printf("%s",utf_text); #else label_markup = g_strdup_printf("%s",utf_text); #endif } else { #if GTK_MAJOR_VERSION<3 label_markup = g_strdup_printf("%s\n",utf_text); #else label_markup = g_strdup_printf("%s\n",utf_text); #endif } gtk_label_set_markup(GTK_LABEL(label), label_markup); g_free(utf_text); g_free(label_markup); gtk_frame_set_label_widget(GTK_FRAME(frame), label); } gtk_container_add (GTK_CONTAINER (wbox), frame); gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_NONE); GtkWidget *box = rfm_hbox_new(FALSE, 2); gtk_widget_show(box); gtk_container_add (GTK_CONTAINER (frame), box); GtkWidget *tip_image = NULL; if (pixbuf){ if (label_text) { GdkPixbuf *shadowed = shadow_it(pixbuf); if (shadowed) { tip_image = gtk_image_new_from_pixbuf ((GdkPixbuf *)pixbuf); g_object_unref(shadowed); } else { tip_image = gtk_image_new_from_pixbuf ((GdkPixbuf *)pixbuf); } } else { tip_image = gtk_image_new_from_pixbuf ((GdkPixbuf *)pixbuf); } gtk_box_pack_start(GTK_BOX(box),tip_image, FALSE, FALSE,0); gtk_widget_show(tip_image); } if (markup) { GtkWidget *label = gtk_label_new(""); //gtk_widget_set_size_request (label, 60, -1); gtk_label_set_line_wrap(GTK_LABEL(label), TRUE); if (widget) { // gchar *small = g_strdup_printf("%s", markup); #if GTK_MAJOR_VERSION<3 gchar *small = g_strdup_printf("%s", markup); //gchar *small = g_strdup_printf("%s", markup); #else gchar *small = g_strdup_printf("%s", markup); #endif gtk_label_set_markup(GTK_LABEL(label), small); g_free(small); } else { gtk_label_set_markup(GTK_LABEL(label), markup); } gtk_box_pack_start(GTK_BOX(box),label,TRUE,TRUE,0); gtk_widget_show(label); } gtk_widget_show(box); if (widget) { g_object_set_data(G_OBJECT(tooltip_window), "box", box); g_object_set_data(G_OBJECT(tooltip_window), "image", tip_image); g_object_set_data(G_OBJECT(tooltip_window), "pixbuf", (void *)pixbuf); gint width = 0; gint height = 0; if (pixbuf) { width = gdk_pixbuf_get_width(pixbuf); height = gdk_pixbuf_get_height(pixbuf); } g_object_set_data(G_OBJECT(tooltip_window), "width", GINT_TO_POINTER(width)); g_object_set_data(G_OBJECT(tooltip_window), "height", GINT_TO_POINTER(height)); gtk_widget_set_tooltip_window (widget, GTK_WINDOW(tooltip_window)); gtk_widget_realize(tooltip_window); rfm_set_box_gradient(wbox); tooltip_placement_bug_workaround(tooltip_window); return tooltip_window; } rfm_set_box_gradient(wbox); return vbox; } static void valid_widgets_unlock(void){ rfm_view_list_unlock("valid_widgets_unlock"); } static gboolean valid_widgets_lock(widgets_t *widgets_p){ if (!widgets_p) return FALSE; rfm_global_t *rfm_global_p = rfm_global(); view_t *view_p = widgets_p->view_p; if (!view_p || !rfm_global_p || rfm_global_p->settings_widgets_p == widgets_p) { rfm_view_list_lock(NULL, "valid_widgets_lock"); return TRUE; } if (rfm_view_list_lock(view_p, "valid_widgets_lock")) { return TRUE; } return FALSE; } static void * get_visible_f(gpointer data){ GtkWidget *widget=data; if (GTK_IS_WIDGET(widget) && gtk_widget_get_visible(widget)) return GINT_TO_POINTER(1); return NULL; } gboolean rfm_threaded_get_visible(GtkWidget *widget){ if (g_thread_self() == rfm_get_gtk_thread()){ if (GTK_IS_WIDGET(widget)) return gtk_widget_get_visible(widget); } gboolean result = GPOINTER_TO_INT(rfm_context_function(get_visible_f, widget)); return result; } static void * diagnostics_visible_f(gpointer data){ widgets_t *widgets_p=data; if (rfm_diagnostics_is_visible(widgets_p)) return GINT_TO_POINTER(1); return NULL; } gboolean rfm_threaded_diagnostics_is_visible(widgets_t *widgets_p){ rfm_global_t *rfm_global_p = rfm_global(); if (!rfm_global_p ){ TRACE("rfm_threaded_diagnostics_is_visible: rfm_global_p==NULL\n"); return FALSE; } if (g_thread_self() == rfm_get_gtk_thread()){ return rfm_diagnostics_is_visible(widgets_p); } gboolean result = GPOINTER_TO_INT(rfm_context_function(diagnostics_visible_f, widgets_p)); return result; } typedef struct sequence_t { const gchar *id; const gchar *sequence; } sequence_t; static sequence_t sequence_v[] = { {"xffm_tag/black", "30"}, {"xffm_tag/black_bg", "40"}, {"xffm_tag/red", "31"}, {"xffm_tag/red_bg", "41"}, {"xffm_tag/green", "32"}, {"xffm_tag/green_bg", "42"}, {"xffm_tag/yellow", "33"}, {"xffm_tag/yellow_bg", "43"}, {"xffm_tag/blue", "34"}, {"xffm_tag/blue_bg", "44"}, {"xffm_tag/magenta", "35"}, {"xffm_tag/magenta_bg", "45"}, {"xffm_tag/cyan", "36"}, {"xffm_tag/cyan_bg", "46"}, {"xffm_tag/white", "37"}, {"xffm_tag/white_bg", "47"}, {"xffm_tag/bold", "1"}, {"xffm_tag/bold", "01"}, {"xffm_tag/italic", "4"}, {"xffm_tag/italic", "04"}, {"xffm_tag/blink", "5"}, {"xffm_tag/blink", "05"}, {NULL, ""}, {NULL, "0"}, {NULL, "00"}, {NULL, "22"}, {NULL, "24"}, {NULL, NULL} // this marks the end of sequences. }; static const gchar * get_xffm_ansi_tag(const gchar *code){ sequence_t *p; for(p = sequence_v; p && p->sequence; p++) { if(strcmp (code, p->sequence) == 0) { return p->id; } } return NULL; } #if 0 static sequence_t sequence_p[] = { {"xffm_tag/black", "[01;30m"}, {"xffm_tag/black", "[30m"}, {"xffm_tag/red", "[31m"}, {"xffm_tag/red", "[01;31m"}, {"xffm_tag/red", "[31;01m"}, {"xffm_tag/green", "[32m"}, {"xffm_tag/green", "[01;32m"}, {"xffm_tag/green", "[32;01m"}, {"xffm_tag/yellow", "[33m"}, {"xffm_tag/yellow", "[01;33m"}, {"xffm_tag/yellow", "[33;01m"}, {"xffm_tag/blue", "[34m"}, {"xffm_tag/blue", "[01;34m"}, {"xffm_tag/blue", "[34;01m"}, {"xffm_tag/magenta", "[35m"}, {"xffm_tag/magenta", "[01;35m"}, {"xffm_tag/magenta", "[35;01m"}, {"xffm_tag/cyan", "[36m"}, {"xffm_tag/cyan", "[01;36m"}, {"xffm_tag/cyan", "[36;01m"}, {"xffm_tag/white", "[37m"}, {"xffm_tag/white", "[01;37m"}, {"xffm_tag/white", "[37;01m"}, {"xffm_tag/bold", "[1m"}, {"xffm_tag/bold", "[01m"}, {"xffm_tag/italic", "[4m"}, {"xffm_tag/italic", "[04m"}, {"xffm_tag/italic", "[4;1m"}, {"xffm_tag/italic", "[04;01m"}, {NULL, "[m"}, {NULL, "[0m"}, {NULL, "[22m"}, {NULL, "[24m"}, {NULL, NULL} // this marks the end of sequences. }; #endif // This is to output colors to stdout. const gchar * rfm_lp_color(enum color_e color){ switch (color){ case RED: return "[31m"; case GREEN: return "[32m"; case BLUE: return "[34m"; case YELLOW: return "[33m"; case MAGENTA: return "[35m"; case CYAN: return "[36m"; case WHITE: return "[37m"; case BLACK: return "[30m"; case BOLD: return "[1m"; case ITALIC: return "[4m"; } return NULL; } void rfm_set_store_data_from_list(GtkListStore *list_store, GSList **list){ GtkTreeIter iter; gtk_list_store_clear (list_store); GSList *p = *list; for (; p && p->data; p=p->next) { gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, 0, (gchar *)p->data, -1); /* Note: The store will keep a copy of the string internally, * so the list may be freed */ } } static gboolean little_button_press (GtkWidget *button, GdkEventButton *event, gpointer callback_data){ TRACE( "little button press %d...\n", event->button); g_object_set_data(G_OBJECT(button), "button_id", GUINT_TO_POINTER(event->button)); if (event->button != 3) return FALSE; void (*button_callback)(GtkButton * button, gpointer data); button_callback = g_object_get_data(G_OBJECT(button), "callback"); // popit with callback data. if (button_callback){ TRACE( "popit with callback data...\n"); (*button_callback)(GTK_BUTTON(button), callback_data); } return FALSE; } static GtkWidget *tt_window = NULL; void rfm_reset_tooltip(GtkWidget * widget){ NOOP(stderr, "rfm_reset_tooltip\n"); if (tt_window) { g_object_set_data(G_OBJECT(tt_window), "tooltip_target", NULL); NOOP(stderr, "rfm_reset_tooltip OK\n"); } } // FIXME: on view destroy, all tooltip texts associated to widgets (button mainly) // should be freed and removed from hashtable. (small leak here) static GHashTable *tooltip_text_hash = NULL; static gboolean widget_tooltip_function( GtkWidget * widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip * tooltip, gpointer user_data ) { if (tt_window) { GtkWidget *tooltip_target = g_object_get_data(G_OBJECT(tt_window), "tooltip_target"); if (tooltip_target == widget) return TRUE; } GdkPixbuf *tooltip_pixbuf = g_object_get_data(G_OBJECT(widget), "tooltip_pixbuf"); gchar *tooltip_text = g_object_get_data(G_OBJECT(widget), "tooltip_text"); gchar *label_text = NULL; NOOP(stderr, "New tooltip \"%s\"\n", tooltip_text); if (tooltip_text){ if (strchr(tooltip_text, '\n')) { label_text = g_strdup(tooltip_text); *(strchr(label_text, '\n')) = 0; tooltip_text = strchr(tooltip_text, '\n') + 1; } } tt_window = rfm_create_tooltip_window(widget, tt_window, tooltip_pixbuf, tooltip_text, label_text); g_object_set_data(G_OBJECT(tt_window), "tooltip_target", widget); g_free(label_text); return TRUE; } static void destroy_widget(GtkWidget *button, void *data){ GdkPixbuf *tooltip_pixbuf = g_object_get_data(G_OBJECT(button), "tooltip_pixbuf"); gchar *tooltip_text = g_hash_table_lookup(tooltip_text_hash, button); // g_object_get_data(G_OBJECT(button), "tooltip_text"); if (tooltip_text) { // The free is done by removing item from hash table: // g_free(tooltip_text); g_hash_table_remove(tooltip_text_hash, button); } if (tooltip_pixbuf) g_object_unref(tooltip_pixbuf); g_object_set_data(G_OBJECT(button), "tooltip_text", NULL); g_object_set_data(G_OBJECT(button), "tooltip_pixbuf", NULL); } static void * custom_tooltip_f(gpointer data){ void **arg=data; GtkWidget *widget = arg[0]; GdkPixbuf *pixbuf = arg[1]; const gchar *text = arg[2]; if (!tooltip_text_hash) { tooltip_text_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, g_free); } gchar *t = g_strdup(text); g_object_set_data(G_OBJECT(widget), "tooltip_text", t); g_hash_table_replace(tooltip_text_hash, widget, t); g_object_set_data(G_OBJECT(widget), "tooltip_pixbuf", pixbuf); if (pixbuf) g_object_ref(pixbuf); gtk_widget_set_has_tooltip(widget, TRUE); g_signal_connect (G_OBJECT (widget), "destroy", G_CALLBACK (destroy_widget), NULL); g_signal_connect (G_OBJECT (widget), "query-tooltip", G_CALLBACK (widget_tooltip_function), widget); return NULL; } void rfm_add_custom_tooltip(GtkWidget *widget, GdkPixbuf *pixbuf, const gchar *text){ //rfm_global_t *rfm_global_p = rfm_global(); void *arg[]={widget, pixbuf, (void *)text}; if (rfm_get_gtk_thread() == g_thread_self()) custom_tooltip_f(arg); rfm_context_function(custom_tooltip_f, arg); } GtkWidget * rfm_mk_little_button (const gchar * icon_id, void *callback, void *callback_data, const gchar * tooltip_text) { GtkWidget *button; button = gtk_button_new (); gtk_widget_set_can_focus (button, FALSE); gtk_button_set_relief (GTK_BUTTON (button), GTK_RELIEF_NONE); GdkPixbuf *pb = NULL; if(icon_id) { GtkWidget *image; pb = rfm_get_pixbuf (icon_id, SIZE_BUTTON); image = gtk_image_new_from_pixbuf (pb); g_object_unref(pb); gtk_widget_show (image); gtk_container_add (GTK_CONTAINER (button), image); } if(tooltip_text && strlen (tooltip_text)) { rfm_add_custom_tooltip(button, pb, tooltip_text); } if(callback) { NOOP ("DIAGNOSTICS: run button connected to callback\n"); g_object_set_data(G_OBJECT(button), "callback", callback); g_signal_connect ((gpointer) button, "button-press-event", G_CALLBACK (little_button_press), callback_data); g_signal_connect ((gpointer) button, "clicked", G_CALLBACK (callback), callback_data); } return button; } void * rfm_cursor_wait (gpointer data) { GtkWidget * widget = data; static GdkCursor *cursor = NULL; if(!widget) return NULL; if(!cursor) cursor = gdk_cursor_new_for_display (gdk_display_get_default(),GDK_WATCH); gdk_window_set_cursor (gtk_widget_get_window(widget), cursor); return NULL; } void * rfm_cursor_reset (gpointer data) { GtkWidget * widget = data; if(!widget) return NULL; gdk_window_set_cursor (gtk_widget_get_window(widget), NULL); return NULL; } static void clear_diagnostics_window (GtkButton * button, gpointer data) { widgets_t *widgets_p = data; NOOP ("clear_diagnostics_window() now \n"); if (widgets_p->diagnostics_window==NULL) { g_error("widgets_p->diagnostics_window==NULL"); } rfm_clear_text (widgets_p); return; } #define ICONOFY_BUTTON 1 #ifdef ICONOFY_BUTTON // This function is necessary because fvwm does not put in a minimize // button for child windows of the desktop window. Actually, no decorations // are used for any child windows of the desktop window. static void iconofy_diagnostics_window (GtkButton * button, gpointer data) { widgets_t *widgets_p = data; if (widgets_p->diagnostics_window==NULL) g_error("widgets_p->diagnostics_window==NULL"); gtk_window_iconify(GTK_WINDOW(widgets_p->diagnostics_window)); return; } #endif static gboolean destroy_diagnostics_window (GtkWidget * widget, GdkEvent * event, gpointer data) { //return TRUE; // Just hide it. widgets_t *widgets_p = data; NOOP ("destroy_diagnostics_window() now \n"); if (widgets_p->diagnostics_window==NULL) return TRUE; gtk_window_iconify(GTK_WINDOW((widgets_p->diagnostics_window))); // Hiding does not work right since if input is flowing to // the widget, GTK gets confused and will not show it again // (at least with fvwm). //gtk_widget_hide((widgets_p->diagnostics_window)); // Let's try simply setting the configuration option to hide it... rfm_rational (RFM_MODULE_DIR, "settings", (void *)"RFM_ENABLE_DESKTOP_DIAGNOSTICS", (void *)"", "mcs_set_var"); return TRUE; } static void close_diagnostics_window (GtkWidget * widget, gpointer data) { destroy_diagnostics_window (NULL, NULL, data); } GtkWidget * rfm_create_diagnostics_window (widgets_t * widgets_p) { if (widgets_p->diagnostics_window != NULL) { DBG("rfm_create_diagnostics_window(): diagnostics_window already exists.\n"); return (widgets_p->diagnostics_window); } GtkWidget *dialog, *button, *scrolledwindow, *hbox; //dialog = gtk_dialog_new (); //// THis instead: dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_keep_above (GTK_WINDOW(dialog), TRUE); gtk_window_set_type_hint(GTK_WINDOW(dialog), GDK_WINDOW_TYPE_HINT_DIALOG); gtk_window_stick (GTK_WINDOW(dialog)); (widgets_p->diagnostics_window) = dialog; g_object_set_data(G_OBJECT(dialog), "widgets_p", widgets_p); gchar *title=g_strdup(_("Console Message Viewer")); gtk_window_set_title (GTK_WINDOW (dialog), title); GdkPixbuf *pixbuf = rfm_get_pixbuf("xffm/emblem_terminal", SIZE_ICON); gtk_window_set_icon (GTK_WINDOW (dialog), pixbuf); g_object_unref(pixbuf); g_free(title); gtk_window_set_resizable (GTK_WINDOW (dialog), TRUE); gtk_container_set_border_width (GTK_CONTAINER (dialog), 6); /* create diagnostics textview */ widgets_p->diagnostics = gtk_text_view_new (); scrolledwindow = gtk_scrolled_window_new (NULL, NULL); gtk_widget_set_size_request (scrolledwindow, 600, 400); gtk_container_add (GTK_CONTAINER (scrolledwindow), (widgets_p->diagnostics)); gtk_container_set_border_width (GTK_CONTAINER ((widgets_p->diagnostics)), 2); gtk_widget_set_can_focus ((widgets_p->diagnostics), FALSE); gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW ((widgets_p->diagnostics)), GTK_WRAP_WORD); gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW ((widgets_p->diagnostics)), FALSE); #if 0 oobbsoolete. just check if needed for desktop console with gtk2 (probably not) #if GTK_MAJOR_VERSION==2 gint size = 10; //8 This only for desktop.... PangoFontDescription *font_desc = pango_font_description_new (); pango_font_description_set_family (font_desc, "monospace"); pango_font_description_set_size (font_desc, size * PANGO_SCALE); gtk_widget_modify_font ((widgets_p->diagnostics), font_desc); pango_font_description_free (font_desc); #endif #if GTK_MAJOR_VERSION==3 // GTK_MINOR_VERSION>=15 is taken care of with style below. gint size = 10; //8 This only works for desktop.... PangoFontDescription *font_desc = pango_font_description_new (); pango_font_description_set_family (font_desc, "monospace"); pango_font_description_set_size (font_desc, size * PANGO_SCALE); gtk_widget_override_font ((widgets_p->diagnostics), font_desc); pango_font_description_free (font_desc); #endif #endif GtkWidget *vbox = rfm_vbox_new (FALSE, 0); gtk_widget_show (vbox); gtk_container_add (GTK_CONTAINER (dialog), vbox); gtk_box_pack_start (GTK_BOX (vbox), scrolledwindow, TRUE, TRUE, 0); hbox = rfm_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); button = rfm_dialog_button ("xffm/stock_close", _("Close")); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (close_diagnostics_window), widgets_p); button = rfm_dialog_button ("xffm/stock_clear", _("Clear")); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (clear_diagnostics_window), widgets_p); #ifdef ICONOFY_BUTTON button = rfm_dialog_button ("xffm/stock_go-bottom", _("Iconify")); gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (iconofy_diagnostics_window), widgets_p); #endif g_signal_connect (G_OBJECT (dialog), "destroy_event", G_CALLBACK (destroy_diagnostics_window), widgets_p); g_signal_connect (G_OBJECT (dialog), "delete_event", G_CALLBACK (destroy_diagnostics_window), widgets_p); widgets_p->button_space = rfm_hbox_new (FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), widgets_p->button_space, FALSE, FALSE, 0); gtk_widget_realize (dialog); if(!getenv ("RFM_ENABLE_DESKTOP_DIAGNOSTICS") || strlen (getenv ("RFM_ENABLE_DESKTOP_DIAGNOSTICS"))==0) { gtk_widget_hide (dialog); } else { gtk_window_iconify(GTK_WINDOW((widgets_p->diagnostics_window))); gtk_widget_show_all (dialog); } return dialog; } void rfm_update_status_line (view_t * view_p) { if(!view_p->widgets.status) return; // Deactivate lpinput... g_object_set_data (G_OBJECT (view_p->widgets.status), "active", NULL); // Item selected: if(view_p->selection_list && g_slist_length (view_p->selection_list) == 1) { record_entry_t *en = view_p->selection_list->data; if(en && en->path) { GError *error = NULL; const gchar *id = "xffm/stock_file"; if (en && IS_SDIR(en->type)){ if (IS_LOCAL_TYPE(en->type)) id = "xffm/stock_directory"; else id ="xffm/emblem_shared"; } else if (en && en->mimetype) id = en->mimetype; gchar *g = g_path_get_basename (en->path); gchar *gg = g_locale_to_utf8 (g, -1, NULL, NULL, &error); if(gg){ rfm_threaded_status (&(view_p->widgets), id, g_strconcat(gg, NULL)); } else { DBG ("g_locale_to_utf8(%s): %s\n", g, error->message); g_error_free (error); } g_free (g); g_free (gg); } } // Items selected: else if(view_p->selection_list && g_slist_length (view_p->selection_list) >= 1) { gchar *g= g_strdup_printf (ngettext ("%'d item selected", "%'d items selected", g_slist_length (view_p->selection_list)), g_slist_length (view_p->selection_list)); rfm_threaded_status (&(view_p->widgets), "xffm/stock_info", g); } // Use en->tag for status line. else { const gchar *icon = "xffm/emblem_symbolic-link"; if(view_p->en){ if (view_p->en->module){ const gchar *m_icon = rfm_void(PLUGIN_DIR, view_p->en->module, "module_icon_id"); if (m_icon) icon = m_icon; } if (view_p->en->tag == NULL) { gint items=0; for (;view_p->population_pp && view_p->population_pp[items]; items++); if (IS_SDIR(view_p->en->type)){ if (IS_LOCAL_TYPE(view_p->en->type)) { icon = "xffm/stock_directory"; } else icon ="xffm/emblem_shared"; } gchar *g = g_strdup_printf (ngettext (" (containing %'d item)", " (containing %'d items)", items), items); gchar *b = g_path_get_basename(view_p->en->path); view_p->en->tag = g_strdup_printf ("%s %s", b, g); g_free(b); g_free(g); } rfm_threaded_status (&(view_p->widgets), icon, g_strdup(view_p->en->tag)); } } } #if GTK_MAJOR_VERSION==3 //&& GTK_MINOR_VERSION>=4 typedef struct lpterm_colors_t { const gchar *id; GdkRGBA color; } lpterm_colors_t; static GtkTextTag * resolve_tag (GtkTextBuffer * buffer, const gchar * id) { GtkTextTag *tag = NULL; lpterm_colors_t lpterm_colors_v[] = { {"xffm_tag/command", { 0.3451, 0x3434, 0.8118, 1.0}}, {"xffm_tag/stderr", { 0.8, 0, 0, 1.0}}, {"xffm_tag/command_id", { 0.0, 0.0, 1.0, 1.0}}, {"xffm_tag/green", { 0.0, 0.8039, 0.0, 1.0}}, {"xffm_tag/red", { 0.8039, 0.0, 0.0, 1.0}}, {"xffm_tag/blue", { 0.0, 0.0, 0.8039, 1.0}}, {"xffm_tag/yellow", { 0.8039, 0.8039, 0.0, 1.0}}, {"xffm_tag/magenta", { 0.8039, 0.0, 0.8039, 1.0}}, {"xffm_tag/cyan", { 0.0, 0.8039, 0.8039, 1.0}}, {"xffm_tag/black", { 0.0, 0.0, 0.0, 1.0}}, {"xffm_tag/white", { 1.0, 1.0, 1.0, 1.0}}, {"xffm_tag/grey", { 0x8888, 0x8888, 0x8888}}, {NULL, { 0.0, 0.0, 0.0}} }; void *initialized = g_object_get_data(G_OBJECT(buffer), "text_tag_initialized"); if (!initialized) { lpterm_colors_t *p = lpterm_colors_v; for(; p && p->id; p++) { gtk_text_buffer_create_tag (buffer, p->id, "foreground-rgba", &(p->color), NULL); gchar *bg_id = g_strconcat(p->id, "_bg", NULL); gtk_text_buffer_create_tag (buffer, bg_id, "background-rgba", &(p->color), NULL); g_free(bg_id); } // XXX Weight and style are not colors. This is not quite right... gtk_text_buffer_create_tag (buffer, "xffm/bold", "weight", PANGO_WEIGHT_BOLD, "foreground-rgba", NULL, NULL); gtk_text_buffer_create_tag (buffer, "xffm/italic", "style", PANGO_STYLE_ITALIC, "foreground-rgba", NULL, NULL); g_object_set_data(G_OBJECT(buffer), "text_tag_initialized", GINT_TO_POINTER(1)); } if (!id) return NULL; tag = gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (buffer), id); if (!tag) NOOP(stderr, "No GtkTextTag for %s\n", id); return tag; } #else typedef struct lpterm_colors_t { const gchar *id; GdkColor color; } lpterm_colors_t; static GtkTextTag * resolve_tag (GtkTextBuffer * buffer, const gchar * id) { GtkTextTag *tag = NULL; lpterm_colors_t lpterm_colors_v[] = { {"xffm_tag/command", {101, 0x5858, 0x3434, 0xcfcf}}, {"xffm_tag/stderr", {102, 0xcccc, 0, 0}}, {"xffm_tag/command_id", {103, 0x0000, 0x0000, 0xffff}}, {"xffm_tag/green", {104, 0x0000, 0xcdcd, 0x0000}}, {"xffm_tag/red", {105, 0xcdcd, 0x0000, 0x0000}}, {"xffm_tag/blue", {106, 0x0000, 0x0000, 0xcdcd}}, {"xffm_tag/yellow", {107, 0xcdcd, 0xcdcd, 0x0000}}, {"xffm_tag/magenta", {108, 0xcdcd, 0x0000, 0xcdcd}}, {"xffm_tag/cyan", {109, 0x0000, 0xcdcd, 0xcdcd}}, {"xffm_tag/black", {1, 0x0000, 0x0000, 0x0000}}, {"xffm_tag/white", {0, 0xffff, 0xffff, 0xffff}}, {"xffm_tag/grey", {110, 0x8888, 0x8888, 0x8888}}, {NULL, {0, 0, 0, 0}} }; void *initialized = g_object_get_data(G_OBJECT(buffer), "text_tag_initialized"); if (!initialized) { lpterm_colors_t *p = lpterm_colors_v; for(; p && p->id; p++) { gtk_text_buffer_create_tag (buffer, p->id, "foreground_gdk", &(p->color), NULL); gchar *bg_id = g_strconcat(p->id, "_bg", NULL); gtk_text_buffer_create_tag (buffer, bg_id, "background_gdk", &(p->color), NULL); g_free(bg_id); } gtk_text_buffer_create_tag (buffer, "xffm/bold", "weight", PANGO_WEIGHT_BOLD, "foreground_gdk", NULL, NULL); gtk_text_buffer_create_tag (buffer, "xffm/italic", "style", PANGO_STYLE_ITALIC, "foreground_gdk", NULL, NULL); g_object_set_data(G_OBJECT(buffer), "text_tag_initialized", GINT_TO_POINTER(1)); } if (!id) return NULL; tag = gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (buffer), id); if (!tag) NOOP(stderr, "No GtkTextTag for %s\n", id); return tag; } #endif void rfm_clear_sh_command_history (view_t * view_p, gboolean disk_too) { if(disk_too) { gchar *history = g_build_filename ( LP_TERMINAL_HISTORY, NULL); unlink (history); } GList *p; for(p = g_list_first (view_p->sh_command); p && p->data; p = p->next) { g_free (p->data); } g_list_free (view_p->sh_command); view_p->sh_command = NULL; view_p->sh_command_counter = 0; } // thread function (null) void rfm_dump_output (void *user_data, void *stream, int childFD) { return; } static void * threaded_status_f(gpointer data){ void **arg = data; widgets_t *widgets_p = arg[0]; gchar *icon = arg[1]; gchar *string = arg[2]; if (valid_widgets_lock(widgets_p)) { rfm_status (widgets_p, icon, string, NULL); valid_widgets_unlock(); } g_free(icon); g_free(string); g_free(arg); return NULL; } static void * threaded_diagnostics_f(gpointer data){ void **arg = data; widgets_t *widgets_p = arg[0]; gchar *icon = arg[1]; gchar *string = arg[2]; if (valid_widgets_lock(widgets_p)) { rfm_diagnostics (widgets_p, icon, string, NULL); valid_widgets_unlock(); } g_free(icon); g_free(string); return NULL; } void rfm_threaded_status(widgets_t *widgets_p, const gchar *icon, gchar *string){ void **arg = (void **) malloc(3*sizeof(void*)); if (!arg) g_error("malloc: %s\n", strerror(errno)); arg[0] = widgets_p; arg[1] = g_strdup(icon); arg[2] = string; rfm_context_function(threaded_status_f, arg); return; } void rfm_threaded_diagnostics(widgets_t *widgets_p, const gchar *icon, gchar *string){ static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; if (g_thread_self() == rfm_get_gtk_thread()){ rfm_diagnostics(widgets_p, icon, string, NULL); g_free(string); return ; } if (string && strchr(string, 0x0d)){ *strchr(string, 0x0d) = ' ';// ^M } pthread_mutex_lock(&mutex); void *arg[3]; arg[0] = widgets_p; arg[1] = g_strdup(icon); arg[2] = string; rfm_context_function(threaded_diagnostics_f, arg); pthread_mutex_unlock(&mutex); return; } // This is a thread function, must have GDK mutex set for gtk commands... void rfm_operate_stderr (void *user_data, void *stream, int childFD) { widgets_t *widgets_p = user_data; rfm_global_t *rfm_global_p = rfm_global(); if (rfm_global_p){ g_mutex_lock(rfm_global_p->status_mutex); gint status = rfm_global_p->status; g_mutex_unlock(rfm_global_p->status_mutex); if (status == STATUS_EXIT) return; } view_t *view_p = NULL; if (widgets_p) view_p = widgets_p->view_p; if (view_p){ g_mutex_lock(view_p->mutexes.status_mutex); gboolean status = view_p->flags.status; g_mutex_unlock(view_p->mutexes.status_mutex); if (status == STATUS_EXIT) return; } char *line; line = (char *)stream; if(line[0] != '\n') { // Is the view still valid (valid_view?) TRACE("rfm_operate_stderr()...\n"); if (!rfm_view_list_lock(view_p, "rfm_operate_stderr")) return; if (widgets_p) { rfm_threaded_diagnostics(widgets_p, "xffm_tag/stderr", g_strdup(line)); } else { TRACE( "%s", line); } rfm_view_list_unlock("rfm_operate_stderr"); } // This is a bit hacky, to keep runaway output from hogging // up the gtk event loop. static gint count = 1; if (count % 20 == 0){ usleep(100000); } else { usleep(1000); } return; } // This will hash commands to know what has just finished // Too useful to just be in debug mode #if 1 //#ifdef DEBUG static GHashTable *c_string_hash=NULL; pthread_mutex_t string_hash_mutex = PTHREAD_MUTEX_INITIALIZER; static gchar *pop_hash(pid_t controller){ if (!c_string_hash) { DBG("Popping empty hash\n"); return g_strdup(""); } pthread_mutex_lock(&string_hash_mutex); gchar *string = g_hash_table_lookup (c_string_hash, GINT_TO_POINTER(controller)); if (!string){ pthread_mutex_unlock(&string_hash_mutex); DBG("controller %d not found in hashtable\n", controller); return g_strdup(""); } g_hash_table_steal(c_string_hash, GINT_TO_POINTER(controller)); pthread_mutex_unlock(&string_hash_mutex); gchar bold[]={27, '[', '1', 'm',0}; gchar *dbg_string = g_strconcat(bold, string, NULL); // gchar *dbg_string = g_strconcat(bold, "DBG: ", string, NULL); g_free(string); return dbg_string; } static void push_hash(pid_t controller, gchar *string){ pthread_mutex_lock(&string_hash_mutex); if (!c_string_hash){ c_string_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); } g_hash_table_replace(c_string_hash, GINT_TO_POINTER(controller), string); pthread_mutex_unlock(&string_hash_mutex); } #else static gchar *pop_hash(pid_t controller){return g_strdup("");} static void push_hash(pid_t controller, gchar *string){return;} #endif gchar *rfm_diagnostics_exit_string(gchar *tubo_string){ gchar *string = NULL; if(strchr (tubo_string, '\n')) *strchr (tubo_string, '\n') = 0; const gchar *s = strchr (tubo_string, '('); int pid = -1; long id = 0; if (s) { s++; if(strchr (s, ')')) *strchr (s, ')') = 0; errno = 0; id = strtol(s, NULL, 10); if (!errno){ pid = Tubo_child((pid_t) id); } } gchar *g = g_strdup_printf("%c[31m<%d>", 27, pid); gchar *c_string = pop_hash((pid_t)id); string = g_strconcat(g, " ", c_string, "\n", NULL); g_free(c_string); g_free(g); return string; } static gchar * arg_string(char **arg){ const gchar quote[]={'"', 0}; gchar *g = g_strdup(""); gchar **a = arg; for (;a && *a; a++){ const gchar *q; if (strchr(*a, '*') || strchr(*a, '?') || strchr(*a, ' ')) q = quote; else q = ""; gchar *gg = g_strconcat(g, " ", q, *a, q, NULL); g_free(g); g=gg; } return g; } static gchar * arg_string_format(char **arg){ const gchar quote[]={27, '[', '3', '1', 'm', '"', 0}; const gchar bold[]={27, '[', '1', 'm', 0}; gchar *g = g_strdup(""); gchar **a = arg; for (;a && *a; a++){ const gchar *q; if (strchr(*a, '*') || strchr(*a, '?') || strchr(*a, ' ')) q = quote; else q = ""; gchar *gg = g_strconcat(g, " ", q, bold, *a, q, NULL); g_free(g); g=gg; } return g; } gchar *rfm_diagnostics_start_string(gchar *command, pid_t controller, gboolean with_shell){ pid_t grandchild=Tubo_child (controller); push_hash(controller, g_strdup(command)); gchar *g = g_strdup_printf ("%c[34m<%d>", 27, grandchild); gchar *gg; const gchar bold[]={27, '[', '1', 'm', 0}; if (with_shell) { gchar *shell = rfm_shell(); gg = g_strconcat (g, " ", shell, " ", bold, command, "\n", NULL); g_free (g); g_free(shell); return gg; } if (!strchr(command, '*') && !strchr(command,'?')){ gg = g_strconcat (g, " ", bold, command, "\n", NULL); g_free (g); return gg; } gchar **ap; gint ac; if (g_shell_parse_argv (command, &ac, &ap, NULL)){ gg = arg_string_format(ap); g_strfreev(ap); gchar *ggg = g_strconcat(g, " ", gg, "\n", NULL); g_free(g); g_free(gg); return ggg; } return g_strdup(g); } gchar *rfm_diagnostics_start_string_argv(gchar **argv, pid_t controller){ pid_t grandchild=Tubo_child (controller); gchar *g = g_strdup_printf ("%c[34m<%d>", 27, grandchild); gchar *gg = arg_string(argv); push_hash(controller, g_strdup(gg)); gg = arg_string_format(argv); gchar *ggg = g_strconcat(g, " ", gg, "\n", NULL); g_free(g); g_free(gg); return (ggg); } // This is a thread function, must have GDK mutex set for gtk commands... void rfm_operate_stdout (void *user_data, void *stream, int childFD) { TRACE("*** rfm_operate_stdout: %s\n", (char *)stream); widgets_t *widgets_p = user_data; if (!widgets_p) return; view_t *view_p = widgets_p->view_p; rfm_global_t *rfm_global_p = rfm_global(); if (rfm_global_p){ g_mutex_lock(rfm_global_p->status_mutex); gint status = rfm_global_p->status; g_mutex_unlock(rfm_global_p->status_mutex); if (status == STATUS_EXIT) return; } if (view_p){ g_mutex_lock(view_p->mutexes.status_mutex); gboolean status = view_p->flags.status; g_mutex_unlock(view_p->mutexes.status_mutex); if (status == STATUS_EXIT) return; } char *line; line = (char *)stream; NOOP ("FORK stdout: %s\n", line); if(line[0] == '\n') return; const gchar *exit_token = "Tubo-id exit:"; gchar *recur = NULL; if (strstr(line, exit_token) && strstr(line, exit_token) != line){ recur = g_strdup(strstr(line, exit_token)); strcpy(strstr(line, exit_token), "\n"); } /* this is directly in tubo library * if (!valid_ansi_sequence(line)) { NOOP("invalid ansi sequence!\n"); return; }*/ // eliminate all ^G found (as in nano output) //int bell=0x07; // bell int bs=0x08; // backspace //int ht=0x09; // horizontal tab //int lf=0x0a; // linefeed //int vt=0x0b; // vertical tab //int ff=0x0c; // formfeed //int cr=0x0d; // carriage return // apply all ^H (bs) found (as in rar output) int i, j; gchar *outline = g_strdup (line); for(i = 0, j = 0; line[i]; i++) { if(line[i] == bs && j > 0){ j--; } else { outline[j] = line[i]; j++; } } outline[j] = 0; //NOOP ("%s\n",outline); // Is the view still valid (valid_view?) TRACE("rfm_operate_stdout()...\n"); if (!rfm_view_list_lock(view_p, "rfm_operate_stdout")) return; /* * FIXME: hwat's this hack for? * if (rfm_global_p && rfm_global_p->settings_widgets_p != widgets_p){ rfm_view_list_unlock("rfm_operate_stdout"); return; }*/ if(strncmp (line, exit_token, strlen (exit_token)) == 0) { gchar *string = rfm_diagnostics_exit_string(line); rfm_threaded_diagnostics(widgets_p, "xffm/emblem_redball", string); } else { rfm_threaded_diagnostics(widgets_p, NULL, g_strdup(outline)); } g_free(outline); rfm_view_list_unlock("rfm_operate_stdout"); // This is a bit hacky, to keep runaway output from hogging // up the gtk event loop. static gint count = 1; if (count % 20 == 0){ usleep(100000); } else { usleep(1000); } if (recur) { rfm_operate_stdout (user_data, recur, childFD); g_free(recur); } return; } #if 1 static void insert_string (GtkTextBuffer * buffer, const gchar * s, GtkTextTag **tags) { if(!s) return; GtkTextIter start, end, real_end; gint line = gtk_text_buffer_get_line_count (buffer); gchar *a = NULL; gchar esc[]={0x1B, '[', 0}; gboolean head_section = strncmp(s, esc, strlen(esc)); gchar *esc_location = strstr (s, esc); if(esc_location) { //vt escape sequence // do a split NOOP(stderr, "0.splitting %s\n", s); gchar **split = g_strsplit(s, esc, -1); if (!split) { DBG("insert_string(): split_esc barfed\n"); return; } gchar **pp=split; gint count = 0; for (;pp && *pp; pp++, count++){ if (strlen(*pp) == 0) continue; NOOP(stderr, "split %d: %s\n", count, *pp); if (count==0 && head_section){ insert_string (buffer, *pp, NULL); continue; } gchar *code = *pp; if (*code == 'K'){ insert_string (buffer, "\n", NULL); continue; } NOOP(stderr, "1.splitting %s\n", *pp); gchar **ss = g_strsplit(*pp, "m", 2); // Insert tags gchar **tags = NULL; if (strchr(ss[0],';')) { NOOP(stderr, "2.splitting %s\n", ss[0]); tags = g_strsplit(ss[0], ";", -1); } else { tags = (gchar **)malloc(sizeof(gchar *) * 2 ); if (!tags) g_error("malloc: %s\n", strerror(errno)); tags[0] = g_strdup(ss[0]); tags[1] = 0; } gchar **t = tags; gint tag_count = 0; for (;t && *t; t++)tag_count++; GtkTextTag **gtags = (GtkTextTag **)malloc((tag_count+1)*sizeof(GtkTextTag *)); if (!gtags) g_error("malloc: %s\n", strerror(errno)); memset(gtags, 0, (tag_count+1)*sizeof(GtkTextTag *)); gint i; for (i=0,t = tags;t && *t; t++) { if (!strcmp(*t,"01") || !strcmp(*t,"1") || !strcmp(*t,"05") || !strcmp(*t,"5")) { gtags[i++] = resolve_tag(buffer, "xffm/bold"); continue; } const gchar *tag = get_xffm_ansi_tag(*t); if (!tag) { NOOP(stderr, "no xffm_ansi tag for %s\n", *t); continue; } gtags[i++] = resolve_tag(buffer, tag); NOOP(stderr, "xffm_ansi_tag=%s\n", tag); } // Insert string insert_string (buffer, ss[1], gtags); g_free(gtags); g_strfreev(ss); } g_strfreev(split); // done return; } GtkTextMark *mark = gtk_text_buffer_get_mark (buffer, "rfm-ow"); if(strchr (s, 0x0D)) { //CR gchar *aa = g_strdup (s); *strchr (aa, 0x0D) = 0; insert_string (buffer, aa, tags); g_free (aa); aa = strchr (s, 0x0D) + 1; if(mark) { gtk_text_buffer_get_iter_at_line (buffer, &start, line); gtk_text_buffer_move_mark (buffer, mark, &start); } insert_string (buffer, aa, tags); g_free (a); // we're done return; } gchar *q = rfm_utf_string (s); /// this should be mutex protected since this function is being called // from threads all over the place. static pthread_mutex_t insert_mutex=PTHREAD_MUTEX_INITIALIZER; #if 10 // test 1 pthread_mutex_lock(&insert_mutex); #if 10 // test 3 if(mark == NULL) { gtk_text_buffer_get_end_iter (buffer, &end); gtk_text_buffer_create_mark (buffer, "rfm-ow", &end, FALSE); } else { gtk_text_buffer_get_iter_at_mark (buffer, &end, mark); } gtk_text_buffer_get_iter_at_line (buffer, &start, line); gtk_text_buffer_get_end_iter (buffer, &real_end); // overwrite section gchar *pretext = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); gchar *posttext = gtk_text_buffer_get_text (buffer, &end, &real_end, TRUE); long prechars = g_utf8_strlen (pretext, -1); long postchars = g_utf8_strlen (posttext, -1); long qchars = g_utf8_strlen (q, -1); g_free (pretext); g_free (posttext); if(qchars < postchars) { gtk_text_buffer_get_iter_at_line_offset (buffer, &real_end, line, prechars + qchars); } ///// gtk_text_buffer_delete (buffer, &end, &real_end); #else gtk_text_buffer_get_end_iter (buffer, &end); // end test 3 #endif if(tags) { gint tag_count = 0; GtkTextTag **tt = tags; for (;tt && *tt; tt++)tag_count++; switch (tag_count) { // This is hacky case 1: gtk_text_buffer_insert_with_tags (buffer, &end, q, -1, tags[0], NULL); break; case 2: gtk_text_buffer_insert_with_tags (buffer, &end, q, -1, tags[0],tags[1], NULL); break; default: // Max. 3 tags... gtk_text_buffer_insert_with_tags (buffer, &end, q, -1, tags[0],tags[1],tags[1], NULL); break; } } else { NOOP(stderr, "gtk_text_buffer_insert %s\n", q); gtk_text_buffer_insert (buffer, &end, q, -1); } pthread_mutex_unlock(&insert_mutex); // end test 1 #endif g_free (q); g_free (a); return; } #else static gchar **split_esc(const gchar *text){ gchar escape[]={0x1b,0}; return (g_strsplit(text, escape, -1)); } static void insert_string (GtkTextBuffer * buffer, const gchar * s, GtkTextTag * tag) { if(!s) return; GtkTextIter start, end, real_end; gint line = gtk_text_buffer_get_line_count (buffer); gchar *a = NULL; if(strchr (s, 0x1B)) { //vt escape sequence // do a split gchar **split=split_esc(s); if (!split) { DBG("insert_string(): split_esc barfed\n"); return; } gchar **pp=split; for (;pp && *pp; pp++){ const gchar *tag=NULL; const gchar *sequence=NULL; sequence_t *p; for(p = sequence_p; p && p->sequence; p++) { if(strncmp (*pp, p->sequence, strlen (p->sequence)) == 0) { tag=p->id; sequence=p->sequence; break; } } if (!tag){ tag="xffm_tag/black"; DBG("VT escape secuence not reconized: %s\n",*pp); } gint offset=(sequence)?strlen(sequence):0; gchar *string=*pp+offset; NOOP("(%s) token:%s-->\"%s\"\n", tag, (sequence)?sequence:"None", string); insert_string (buffer, string, resolve_tag(buffer, tag)); } g_strfreev(split); // done return; } GtkTextMark *mark = gtk_text_buffer_get_mark (buffer, "rfm-ow"); if(strchr (s, 0x0D)) { //CR gchar *aa = g_strdup (s); *strchr (aa, 0x0D) = 0; insert_string (buffer, aa, tag); g_free (aa); aa = strchr (s, 0x0D) + 1; if(mark) { gtk_text_buffer_get_iter_at_line (buffer, &start, line); gtk_text_buffer_move_mark (buffer, mark, &start); } insert_string (buffer, aa, tag); g_free (a); // we're done return; } gchar *q = rfm_utf_string (s); /// this should be mutex protected since this function is being called // from threads all over the place. static pthread_mutex_t insert_mutex=PTHREAD_MUTEX_INITIALIZER; #if 10 // test 1 pthread_mutex_lock(&insert_mutex); #if 10 // test 3 if(mark == NULL) { gtk_text_buffer_get_end_iter (buffer, &end); gtk_text_buffer_create_mark (buffer, "rfm-ow", &end, FALSE); } else { gtk_text_buffer_get_iter_at_mark (buffer, &end, mark); } gtk_text_buffer_get_iter_at_line (buffer, &start, line); gtk_text_buffer_get_end_iter (buffer, &real_end); // overwrite section gchar *pretext = gtk_text_buffer_get_text (buffer, &start, &end, TRUE); gchar *posttext = gtk_text_buffer_get_text (buffer, &end, &real_end, TRUE); long prechars = g_utf8_strlen (pretext, -1); long postchars = g_utf8_strlen (posttext, -1); long qchars = g_utf8_strlen (q, -1); g_free (pretext); g_free (posttext); if(qchars < postchars) { gtk_text_buffer_get_iter_at_line_offset (buffer, &real_end, line, prechars + qchars); } ///// gtk_text_buffer_delete (buffer, &end, &real_end); #else gtk_text_buffer_get_end_iter (buffer, &end); // end test 3 #endif if(tag) { gtk_text_buffer_insert_with_tags (buffer, &end, q, -1, tag, NULL); } else { gtk_text_buffer_insert (buffer, &end, q, -1); } pthread_mutex_unlock(&insert_mutex); // end test 1 #endif g_free (q); g_free (a); return; } #endif static void set_font_family (GtkWidget * widget, const gchar *in_family, gboolean fixed) { if (!in_family) g_error("in_family cannot be NULL\n"); if (!GTK_IS_WIDGET(widget)) return; gchar *family = g_object_get_data(G_OBJECT(widget), "font-family"); gint fontsize = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), "fontsize")); gint newsize=8; // default font size. const gchar *p; if (fixed) p = getenv ("RFM_FIXED_FONT_SIZE"); else p = getenv ("RFM_VARIABLE_FONT_SIZE"); if(p && strlen (p)) { errno=0; long value = strtol(p, NULL, 0); if (errno == 0){ newsize = value; } } if(newsize != fontsize || !family || strcmp(family, in_family)) { NOOP(stderr, "XXX setting %s fontsize %d -> %d \n", in_family, fontsize, newsize); if (!family || strcmp(family, in_family)) { g_free(family); family = g_strdup(in_family); g_object_set_data(G_OBJECT(widget), "font-family", family); } fontsize = newsize; g_object_set_data(G_OBJECT(widget), "fontsize", GINT_TO_POINTER(fontsize)); #if GTK_MAJOR_VERSION==2 gchar *font_id = g_strdup_printf("%s_font_desc", family); PangoFontDescription *font_desc = pango_font_description_new (); pango_font_description_set_family (font_desc, family); pango_font_description_set_size (font_desc, fontsize * PANGO_SCALE); gtk_widget_modify_font (widget, font_desc); g_free(font_id); pango_font_description_free (font_desc); #else GtkStyleContext *style_context = gtk_widget_get_style_context (widget); gtk_style_context_add_class(style_context, GTK_STYLE_CLASS_VIEW ); GtkCssProvider *css_provider = gtk_css_provider_new(); GError *error=NULL; gchar *data = g_strdup_printf("* {\ font-family: %s;\ font-size: %dpx;\ }", family, fontsize); gtk_css_provider_load_from_data (css_provider, data, -1, &error); g_free(data); if (error){ fprintf(stderr, "gerror: %s\n", error->message); g_error_free(error); } gtk_style_context_add_provider (style_context, GTK_STYLE_PROVIDER(css_provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); // gtk_widget_override_font (widget, font_desc); #endif } } void rfm_status (widgets_t * widgets_p, const gchar * id, ...) { if (!widgets_p) return; va_list var_args; char *t; GtkTextIter start, end; GtkTextBuffer *buffer; GdkPixbuf *icon; //gint icon_width = 0; if(!widgets_p->status || !GTK_IS_TEXT_VIEW (widgets_p->status)) { #ifdef DEBUG_NOOP /*DBG("rfm_status: status==NULL\n"); */ va_start (var_args, id); do { t = va_arg (var_args, char *); if(t && strlen (t)) { NOOP ("%s\n", t); } } while(t); va_end (var_args); #endif return; } // call this function when status line is created, // but that will not work for user changes in font size... if (id && strcmp(id, "xffm/emblem_terminal")==0) { const gchar *family = getenv("RFM_FIXED_FONT_FAMILY"); if (!family || !strlen(family)) family="monospace"; set_font_family ((widgets_p->status), family, TRUE); // set_font_family (widgets_p->status, "monospace"); } else { const gchar *family = getenv("RFM_VARIABLE_FONT_FAMILY"); if (!family || !strlen(family)) family="sans"; set_font_family (widgets_p->status, family, FALSE); } // call this function if you really want it when status line is created: // set_sans (widgets_p->status); /*gtk_text_view_set_justification ((GtkTextView *) widgets_p->status,GTK_JUSTIFY_LEFT); */ buffer = gtk_text_view_get_buffer ((GtkTextView *) widgets_p->status); gtk_text_buffer_set_text (buffer, " ", -1); gtk_text_buffer_get_bounds (buffer, &start, &end); if(!id){ id = "xffm/emote_cool"; } icon = rfm_get_pixbuf (id, SIZE_BUTTON); if(icon && GDK_IS_PIXBUF(icon)) { gtk_text_buffer_insert_pixbuf (buffer, &start, icon); //icon_width = gdk_pixbuf_get_width (icon); g_object_unref(icon); } { char *string = NULL; /*,*tag; */ gchar *f = NULL; va_start (var_args, id); do { t = va_arg (var_args, char *); if(t && strlen (t)) { if(!f) f = g_strdup (""); string = g_strconcat (f, t, NULL); g_free (f); f = string; } } while(t); va_end (var_args); if(string) { insert_string (buffer, string, NULL); g_free (string); } } gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW (widgets_p->status), FALSE); g_object_set_data (G_OBJECT (widgets_p->status), "clean", GINT_TO_POINTER(1)); } static void trim_diagnostics(GtkTextBuffer *buffer){ // this is screwy in 3.12 gint line_count = gtk_text_buffer_get_line_count (buffer); glong max_lines_in_buffer = 1000; if (getenv("RFM_MAXIMUM_DIAGNOSTIC_LINES") && strlen(getenv("RFM_MAXIMUM_DIAGNOSTIC_LINES"))){ errno = 0; max_lines_in_buffer = strtol(getenv("RFM_MAXIMUM_DIAGNOSTIC_LINES"), NULL, 10); if (errno){ max_lines_in_buffer = 1000; } } #if GTK_MAJOR_VERSION==3 if (line_count > 100*max_lines_in_buffer) { gtk_text_buffer_set_text(buffer, "", -1); //clear buffer } #else GtkTextIter start, end; // this is screwy in 3.12 if (line_count > max_lines_in_buffer) { //gtk_text_buffer_set_text(buffer, "", -1); //clear buffer NOOP(stderr, "line count is %d, deleting %d lines\n", line_count, line_count-max_lines_in_buffer); gtk_text_buffer_get_iter_at_line (buffer, &start, 0); // This is better, since one line is added at a time gtk_text_buffer_get_iter_at_line (buffer, &end, 1); //gtk_text_buffer_get_iter_at_line (buffer, &end, line_count - max_lines_in_buffer); gtk_text_buffer_delete (buffer, &start, &end); } #endif } // For normal diagnostics, default is to show stdout if diagnostics // is visible, and to show diagnostics automatically whenever output // to stderr is detected. void rfm_diagnostics (widgets_t * widgets_p, const gchar * id, ... ) { char *t; va_list var_args; GtkTextIter start, end; GtkTextBuffer *buffer; GdkPixbuf *icono = NULL; if (!widgets_p) { return; } #if 0 view_t *view_p = widgets_p->view_p; // This is not necessary since the threads stdout/stderr // are tested for this condition before attempting to write. if (view_p){ g_mutex_lock(view_p->mutexes.status_mutex); gboolean status = view_p->flags.status; g_mutex_unlock(view_p->mutexes.status_mutex); if (status == STATUS_EXIT) { TRACE("dead view 0x%x\n", GPOINTER_TO_INT(view_p)); return; } } #endif gboolean diagnostics_is_visible=rfm_diagnostics_is_visible (widgets_p); if(!diagnostics_is_visible) { TRACE( "!rfm_diagnostics_is_visible(diagnostics)\n"); return; } // if (!widgets_p->diagnostics || !gtk_widget_get_visible(widgets_p->diagnostics)){ if ((widgets_p->diagnostics)==NULL){ TRACE("!widgets_p->diagnostics\n"); return; } gboolean is_a_deskview=(widgets_p->diagnostics_window != NULL); if (is_a_deskview){ NOOP ("is_a_deskview: gtk_widget_show_all\n"); if(getenv ("RFM_ENABLE_DESKTOP_DIAGNOSTICS") && strlen (getenv ("RFM_ENABLE_DESKTOP_DIAGNOSTICS"))!=0) { gtk_widget_show_all((widgets_p->diagnostics_window)); //gtk_window_deiconify(GTK_WINDOW((widgets_p->diagnostics_window))); } } //#define DEBUG_NOOP #ifdef DEBUG_NOOP fprintf(stderr, "rfm_diagnostics(0x%x, \"%s\"): \n",GPOINTER_TO_INT(widgets_p), id); va_start (var_args, id); do { t = va_arg (var_args, char *); if(t && strlen (t)) { fprintf (stderr, "%s", t); } } while(t); va_end (var_args); //return; #endif if (!GTK_IS_TEXT_VIEW(widgets_p->diagnostics)) return; // call this function when diagnostics is created, // but that only works for desktop diagnostics, // not for iconview diagnostics, I wonder why... const gchar *family = getenv("RFM_FIXED_FONT_FAMILY"); if (!family || !strlen(family)) family="monospace"; set_font_family ((widgets_p->diagnostics), family, TRUE); /**** */ buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW ((widgets_p->diagnostics))); /*NOOP("NOOP:rfm_diagnostics\n"); */ gtk_text_buffer_get_bounds (buffer, &start, &end); GtkTextTag **tags = NULL; //NOOP("DIAGNOSTICS: id= %s\n",id); if(id) { if(strncmp (id, "xffm_tag/", strlen ("xffm_tag/")) == 0) { tags = (GtkTextTag **)malloc(2*sizeof(GtkTextTag *)); if (!tags) g_error("malloc: %s\n", strerror(errno)); tags[0] = resolve_tag (buffer, id); tags[1] = NULL; } else { icono = rfm_get_pixbuf (id, SIZE_BUTTON); } } if(icono) { gtk_text_buffer_insert_pixbuf (buffer, &end, icono); g_object_unref(icono); } va_start (var_args, id); do { t = va_arg (var_args, char *); if(t && strlen (t)) { insert_string (buffer, t, tags); } } while(t); g_free(tags); va_end (var_args); // trim trim_diagnostics(buffer); // scroll gtk_text_buffer_get_bounds (buffer, &start, &end); GtkTextMark *mark = gtk_text_buffer_create_mark (buffer, "scrolldown", &end, FALSE); gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW ((widgets_p->diagnostics)), mark); gtk_text_buffer_delete_mark(buffer, mark); /* gint line_count = gtk_text_buffer_get_line_count (buffer); gtk_text_buffer_get_iter_at_line (buffer, &start, line_count+1); gtk_text_view_scroll_to_iter (GTK_TEXT_VIEW ((widgets_p->diagnostics)), &start, 0.0, TRUE, 0.0, 0.0);*/ /* gtk_text_buffer_get_bounds (buffer, &start, &end); GtkTextMark *mark = gtk_text_buffer_get_mark (buffer, "scrollmark2"); if (mark == NULL){ mark = gtk_text_buffer_create_mark (buffer, "scrollmark2", &end, FALSE); } else { gtk_text_buffer_move_mark (buffer, mark ,&end); } gtk_text_view_scroll_mark_onscreen (GTK_TEXT_VIEW ((widgets_p->diagnostics)), mark);*/ } void * show_text_f (gpointer data) { widgets_t * widgets_p = data; if(!widgets_p) return NULL; //gboolean diagnostics_is_visible=rfm_diagnostics_is_visible (widgets_p); gboolean is_a_deskview = (widgets_p->diagnostics_window != NULL); if(is_a_deskview){ if (getenv ("RFM_ENABLE_DESKTOP_DIAGNOSTICS") && strlen (getenv ("RFM_ENABLE_DESKTOP_DIAGNOSTICS"))!=0) { gtk_widget_show_all((widgets_p->diagnostics_window)); //gtk_window_deiconify(GTK_WINDOW((widgets_p->diagnostics_window))); } return NULL; } // If already visible, then there's nothing more to do. // if (diagnostics_is_visible) return; GtkWidget *vpane = g_object_get_data(G_OBJECT(widgets_p->paper), "vpane"); if(vpane){ GtkAllocation allocation; gtk_widget_get_allocation (vpane, &allocation); if (allocation.height > 50) { gdouble position = gtk_paned_get_position (GTK_PANED(vpane)); if(position > allocation.height * 0.90) { gtk_paned_set_position (GTK_PANED (vpane), allocation.height * 0.75); } } } return NULL; } void * rfm_show_text (gpointer data) { widgets_t * widgets_p = data; if(!widgets_p) return NULL; return rfm_context_function(show_text_f, data); } gboolean rfm_diagnostics_is_visible (widgets_t * widgets_p) { if (!widgets_p) return FALSE; rfm_global_t *rfm_global_p = rfm_global(); if (!rfm_global_p) return TRUE; if (rfm_global_p->settings_widgets_p == widgets_p) return TRUE; // Are we dealing with a deskview? view_t *view_p = widgets_p->view_p; if (view_p->flags.type == DESKVIEW_TYPE){ // Does the deskview output window exist? // If no, then create it. if ((widgets_p->diagnostics_window)==NULL){ rfm_create_diagnostics_window(widgets_p); } // We return TRUE even if item is iconized return TRUE; } // By this point we should be dealing with views with diagnostics area. // If the view does not have a diagnostics area defined, return false. if(widgets_p->diagnostics == NULL) { return FALSE; } #if 0 // By now we should have a diagnostics area defined within the vertical pane. GtkWidget *vpane = g_object_get_data(G_OBJECT(widgets_p->paper), "vpane"); if(vpane){ // Whatever... return TRUE; /* GtkAllocation allocation; gtk_widget_get_allocation (widgets_p->vpane, &allocation); if(gtk_paned_get_position ( GTK_PANED(widgets_p->vpane)) > allocation.height * 0.95) { return FALSE; }*/ } #endif return TRUE; } void * rfm_hide_text (gpointer data) { widgets_t * widgets_p = data; if(widgets_p->diagnostics==NULL){ return NULL; } GtkWidget *vpane = g_object_get_data(G_OBJECT(widgets_p->paper), "vpane"); if(!vpane){ return FALSE; } NOOP(stderr, "vpane = 0x%x\n", GPOINTER_TO_INT(vpane)); GtkAllocation allocation; gtk_widget_get_allocation (vpane, &allocation); NOOP(stderr, "HACK set pane position to %d\n", allocation.height); gtk_paned_set_position (GTK_PANED (vpane), allocation.height); gtk_paned_set_position (GTK_PANED (vpane), 10000); return NULL; } void * clear_text_f (void *data) { widgets_t * widgets_p = data; GtkTextIter start, end; GtkTextBuffer *buffer; buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW ((widgets_p->diagnostics))); gtk_text_buffer_get_bounds (buffer, &start, &end); gtk_text_buffer_delete (buffer, &start, &end); if (widgets_p->diagnostics_window==NULL) { // This is not applicable to diagnostics_window: rfm_hide_text (widgets_p); } g_object_ref (G_OBJECT(buffer)); gtk_text_view_set_buffer(GTK_TEXT_VIEW ((widgets_p->diagnostics)), gtk_text_buffer_new(NULL)); g_object_ref_sink (G_OBJECT(buffer)); g_object_unref (G_OBJECT(buffer)); return NULL; } void rfm_clear_text (widgets_t * widgets_p) { if(widgets_p->diagnostics==NULL) return; rfm_context_function(clear_text_f, widgets_p); } void rfm_clear_text_window (GtkButton * button, gpointer data) { widgets_t *widgets_p = (widgets_t *) data; if(widgets_p->diagnostics==NULL) return; rfm_clear_text (widgets_p); gtk_widget_grab_focus (widgets_p->paper); view_t *view_p = widgets_p->view_p; rfm_update_status_line (view_p); // do reselection... if (view_p->reselect_list){ GSList *tmp = view_p->reselect_list; if (rfm_population_try_read_lock(view_p, "rfm_clear_text_window")){ for (; tmp && tmp->data; tmp=tmp->next){ TRACE("reselecting %s \n", (gchar *)tmp->data); population_t **population_pp = view_p->population_pp; for (;population_pp && *population_pp; population_pp++){ population_t *population_p = *population_pp; if (!population_p->en) continue; if (strcmp(population_p->en->path, (gchar *)tmp->data)==0){ // FIXME: rfm library should not reference // rodent library (matter of principle) rfm_select_pixbuf (view_p, population_p); rfm_expose_item (view_p, population_p); } } } rfm_population_read_unlock(view_p, "rfm_clear_text_window"); } tmp = view_p->reselect_list; for (; tmp && tmp->data; tmp=tmp->next){g_free(tmp->data);} g_slist_free(view_p->reselect_list); view_p->reselect_list = NULL; } //under certain condition, like doing a clear after a threaded remove, //update status line may race with invalid view_p->selection_list //rfm_update_status_line (widgets_p->view_p); } void rfm_set_bin_image(GtkWidget *bin, const gchar *icon_id, gint size){ if (!bin || !GTK_IS_WIDGET(bin)) { g_warning("rfm_set_bin_image(): incorrect function call\n"); return; } GtkWidget *box = gtk_bin_get_child(GTK_BIN(bin)); GtkWidget *icon = g_object_get_data(G_OBJECT(bin),"icon"); if (icon){ gtk_container_remove(GTK_CONTAINER(box), icon); } if(icon_id) { GdkPixbuf *pb = rfm_get_pixbuf (icon_id, size); GtkWidget *image = gtk_image_new_from_pixbuf (pb); gtk_box_pack_start(GTK_BOX(box), image, FALSE, FALSE,0); g_object_set_data(G_OBJECT(bin), "icon", image); gtk_widget_show(image); g_object_unref(pb); } else { g_object_set_data(G_OBJECT(bin), "icon", NULL); } } void rfm_set_bin_markup(GtkWidget *bin, const char *text){ if (!bin || !GTK_IS_WIDGET(bin)) { g_warning("rfm_set_bin_markup(): incorrect function call\n"); return; } GtkLabel *label = g_object_get_data(G_OBJECT(bin), "label"); gtk_label_set_markup(label, (text)?text:""); } static void set_bin_contents(GtkWidget *bin, const char *icon_id, const char *text, gint size){ GtkWidget *child = gtk_bin_get_child(GTK_BIN(bin)); if (child) gtk_container_remove(GTK_CONTAINER(bin), child); child = rfm_hbox_new(FALSE,0); gtk_widget_set_size_request (child, -1, 12); gtk_container_add(GTK_CONTAINER(bin), child); GtkWidget *label = gtk_label_new(""); g_object_set_data(G_OBJECT(bin), "label", label); gtk_box_pack_end(GTK_BOX(child), label, TRUE, FALSE,0); rfm_set_bin_markup(bin, text); rfm_set_bin_image(bin, icon_id, size); gtk_widget_show_all (bin); } GtkWidget * rfm_dialog_button (const char *icon_id, const char *text) { GtkWidget *button = gtk_button_new (); set_bin_contents(button, icon_id, text, SIZE_BUTTON); return button; } GtkWidget * rfm_toggle_button (const char *icon_id, const char *text) { GtkWidget *button = gtk_toggle_button_new (); set_bin_contents(button, icon_id, text, SIZE_BUTTON); return button; } GtkWidget * rfm_pathbar_button (const char *icon_id, const char *text) { //GtkWidget *pb_button = gtk_toggle_button_new (); GtkWidget *pb_button = gtk_button_new (); g_object_set (pb_button, "can-focus", FALSE, "relief", GTK_RELIEF_NONE, NULL); g_object_set_data(G_OBJECT(pb_button), "name", text?g_strdup(text):NULL); gchar *markup = NULL; if (text) { gchar *v = rfm_utf_string(text); gchar *g = g_markup_escape_text(v, -1); g_free(v); markup = g_strdup_printf("%s", g); g_free(g); } set_bin_contents(pb_button, icon_id, markup, 12); g_free(markup); return pb_button; } static void toggle_pathbar(GtkWidget *pathbar, const gchar *path){ GList *children_list = gtk_container_get_children(GTK_CONTAINER(pathbar)); GList *children; GtkRequisition minimum; GtkAllocation allocation; gtk_widget_get_allocation(pathbar, &allocation); NOOP("pathbar width=%d\n", allocation.width); gint width = allocation.width; // First we hide all buttons, except "RFM_ROOT" children = g_list_last(children_list); for (;children && children->data; children=children->prev){ gchar *name = g_object_get_data(G_OBJECT(children->data), "name"); if (strcmp(name, "RFM_ROOT")==0) { #if GTK_MAJOR_VERSION>=3 gtk_widget_get_preferred_size(GTK_WIDGET(children->data), &minimum, NULL); #else { GtkAllocation a; gtk_widget_get_allocation(GTK_WIDGET(children->data), &a); minimum.width = a.width; } #endif width -= minimum.width; continue; } gtk_widget_hide(GTK_WIDGET(children->data)); } // Find first item to place in pathbar. // This item *must* be equal to path, if path is in buttons. children = g_list_last(children_list); GList *active = children; // If path is not in the buttons, then the first to map // will be the last path visited. if (path) for (;children && children->data; children=children->prev){ const gchar *pb_path = g_object_get_data(G_OBJECT(children->data), "path"); if (!pb_path) continue; if (strcmp(path, pb_path)==0) { active = children; break; } } // Show active button gtk_widget_show(GTK_WIDGET(active->data)); #if GTK_MAJOR_VERSION>=3 gtk_widget_get_preferred_size(GTK_WIDGET(active->data), &minimum, NULL); #else { GtkAllocation a; gtk_widget_get_allocation(GTK_WIDGET(active->data), &a); minimum.width = a.width; } #endif width -= minimum.width; // Work backwards from active button we show buttons that will fit. children = active->prev; for (;children && children->data; children=children->prev){ gchar *name = g_object_get_data(G_OBJECT(children->data), "name"); if (strcmp(name, "RFM_ROOT")==0) continue; gtk_widget_get_allocation(GTK_WIDGET(children->data), &allocation); width -= allocation.width; if (width < 0) break; gtk_widget_show(GTK_WIDGET(children->data)); } // Now we work forwards, showing buttons that fit. children = active->next; for (;children && children->data; children=children->next){ gchar *name = g_object_get_data(G_OBJECT(children->data), "name"); if (strcmp(name, "RFM_ROOT")==0) continue; gtk_widget_get_allocation(GTK_WIDGET(children->data), &allocation); width -= allocation.width; if (width < 0) break; gtk_widget_show(GTK_WIDGET(children->data)); } // Finally, we differentiate active button. children = g_list_first(children_list); for (;children && children->data; children=children->next){ gchar *name = g_object_get_data(G_OBJECT(children->data), "name"); if (strcmp(name, "RFM_ROOT")==0) continue; if (!path) { // no path means none is differentiated. gchar *v = rfm_utf_string(name); gchar *g = g_markup_escape_text(v, -1); g_free(v); gchar *markup = g_strdup_printf("%s", g); rfm_set_bin_markup(GTK_WIDGET(children->data), markup); g_free(g); g_free(markup); continue; } const gchar *pb_path = g_object_get_data(G_OBJECT(children->data), "path"); if (!pb_path){ g_warning("rfm_update_pathbar(): pb_path is null\n"); continue; } if (!strlen(pb_path)) pb_path=G_DIR_SEPARATOR_S;//? if (strcmp(pb_path, path)==0) { gchar *v = rfm_utf_string(name); gchar *g = g_markup_escape_text(v, -1); g_free(v); gchar *markup = g_strdup_printf("%s", g); rfm_set_bin_markup(GTK_WIDGET(children->data), markup); g_free(g); g_free(markup); } else { gchar *v = rfm_utf_string(name); gchar *g = g_markup_escape_text(v, -1); g_free(v); gchar *markup = g_strdup_printf("%s", g); rfm_set_bin_markup(GTK_WIDGET(children->data), markup); g_free(g); g_free(markup); } } g_list_free(children_list); } static void *rfm_update_pathbar_f(void *data){ void **arg = data; GtkWidget *pathbar = arg[0]; gchar *path =arg[1]; if (!pathbar) return NULL; if (!path){ NOOP("##### toggle_pathbar(pathbar, NULL)\n"); toggle_pathbar(pathbar, NULL); return NULL; } // Trim pathbar. gchar **paths; if (strcmp(path, G_DIR_SEPARATOR_S)==0){ paths = (gchar **)malloc(2*sizeof(gchar *)); if (!paths){ g_warning("rfm_update_pathbar(): cannot malloc\n"); return NULL; } paths[1]=NULL; } else { paths = g_strsplit(path, G_DIR_SEPARATOR_S, -1); g_free(paths[0]); } paths[0]= g_strdup(G_DIR_SEPARATOR_S); GList *children_list = gtk_container_get_children(GTK_CONTAINER(pathbar)); GList *children = children_list; gint i=0; gchar *pb_path = NULL; for (;children && children->data; children=children->next){ gchar *name = g_object_get_data(G_OBJECT(children->data), "name"); if (strcmp(name, "RFM_ROOT")==0 || strcmp(name, "<")==0) continue; //gchar *p = g_strdup_printf("%s%c", paths[i], G_DIR_SEPARATOR); NOOP("(%d) comparing %s <--> %s\n", i, name, paths[i]); if (paths[i] && strcmp(name, paths[i]) == 0){ g_free(pb_path); const gchar *p = g_object_get_data(G_OBJECT(children->data), "path"); pb_path = g_strdup(p); i++; continue; } // Eliminate tail (only if tail will differ) if (paths[i] == NULL) break; NOOP("Zapping tail: \"%s\"\n", paths[i]); GList *tail = children; for (;tail && tail->data; tail = tail->next){ gchar *name = g_object_get_data(G_OBJECT(tail->data), "name"); g_free(name); gtk_container_remove(GTK_CONTAINER(pathbar), GTK_WIDGET(tail->data)); } break; } g_list_free(children_list); // Add new tail gpointer callback =g_object_get_data(G_OBJECT(pathbar), "callback"); if (strcmp(path, "RFM_MODULE")) for (;paths[i]; i++){ GtkWidget *pb_button = rfm_pathbar_button(NULL, strlen(paths[i])?paths[i]:G_DIR_SEPARATOR_S); #if GTK_MAJOR_VERSION>=3 gtk_container_add(GTK_CONTAINER(pathbar), pb_button); #else gtk_box_pack_start(GTK_BOX(pathbar), pb_button, FALSE, FALSE, 1); #endif gchar *g = (pb_path!=NULL)? g_strdup_printf("%s%s%s",pb_path, strcmp(pb_path,G_DIR_SEPARATOR_S)? G_DIR_SEPARATOR_S:"", paths[i]): g_strdup(paths[i]); g_free(pb_path); pb_path = g; NOOP("+++***** setting pbpath --> %s\n", pb_path); g_object_set_data(G_OBJECT(pb_button), "path", g_strdup(pb_path)); g_signal_connect (G_OBJECT(pb_button) , "clicked", G_CALLBACK (callback), pathbar); } g_free(pb_path); g_strfreev(paths); // show what fits toggle_pathbar(pathbar, path); g_free(path); return NULL; } void rfm_update_pathbar(GtkWidget *pathbar, const gchar *path){ void *arg[]={pathbar, path?g_strdup(path):NULL}; rfm_context_function(rfm_update_pathbar_f, arg); } static void * dialog_run_response_f(void *data){ GtkDialog *dialog = data; gint response = gtk_dialog_run (dialog); return GINT_TO_POINTER(response); } // Thread OK: gint rfm_dialog_run_response(GtkWidget *dialog){ return GPOINTER_TO_INT( rfm_context_function(dialog_run_response_f, dialog)); } // Thread OK: gboolean rfm_confirm (widgets_t * widgets_p, // type: GTK_MESSAGE_INFO, GTK_MESSAGE_WARNING, GTK_MESSAGE_QUESTION, GTK_MESSAGE_ERROR gint type, // GtkMessageType type, const gchar * text, const gchar * action_false, // if NULL, button not shown const gchar * action_true // if NULL, "Ok" button shown ) { void *result; void *arg[] = { widgets_p, GINT_TO_POINTER(type), (void *)text, (void *)action_false, (void *)action_true }; result = rfm_context_function(confirm_f, arg); return GPOINTER_TO_INT(result); } gchar * rfm_get_response (widgets_t * widgets_p, const gchar * ptext, const gchar *default_value, gboolean hidden) { void *arg[]={ (void *)widgets_p, (void *)ptext, (void *)default_value, (void *)GINT_TO_POINTER(hidden), NULL }; gchar *passphrase = rfm_context_function(get_response_f, arg); return passphrase; } // Thread OK: gboolean rfm_confirm_sudo(widgets_t *widgets_p, const gchar *tgt, const gchar *failed, const gchar *operation){ gchar *altoperation= g_strconcat(_("sudo"), " ", operation, NULL); gchar *text=g_strconcat( _("Command:"), " \"", operation, "\"", "\n\n", failed,"\n", _("Permission denied"), ": ", tgt, "\n\n", _("Try to approach a problem from different angles."), "\n\n", _("Do you want to retry?"), "\n", _("Alternate:"), " \"", altoperation, "\"", "\n", NULL ); gboolean retval= rfm_confirm(widgets_p, GTK_MESSAGE_QUESTION, text, _("No"), altoperation); g_free(altoperation); g_free(text); return retval; }