#ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gm-support.h" #include "gm-debug.h" #include "gm-pixbuf.h" #include "gm-string.h" #include "gm-color-table.h" #include "gm-app.h" #define URL_REGEXP "(((mailto|news|telnet|nttp|file|http|sftp|ftp|https|dav|callto)://)|(www|ftp)[-A-Za-z0-9]*\\.)[-A-Za-z0-9\\.@:]+[^]''\\.}>\\) ,\\/\\\"\\!]+(:[0-9]*)?(/|/[-A-Za-z0-9_\\$\\.\\+\\!\\*\\(\\),;:@&=\\?/~\\#\\%]*[^]'\\.}>\\) ,\\\"\\!])?" static regex_t url_regexp; gchar * gm_fix_decimal_point(gchar *line, int len) { int i; struct lconv *l = localeconv(); if (l->decimal_point[0] == '.') { return line; } for (i = 0; i < len; i++) { if (line[i] == '.') { line[i] = l->decimal_point[0]; } } return line; } gchar * gm_fix_decimal_point_rev(gchar *line, int len) { int i; struct lconv *l = localeconv(); for (i = 0; i < len; i++) { if (line[i] == l->decimal_point[0]) { line[i] = '.'; } } return line; } gchar * gm_ansi_strip(gchar *s) { gchar *ptr, *fptr; gunichar c; ptr = s; fptr = s; while (*ptr != '\0') { c = g_utf8_get_char(ptr); // Escape sequence, advance to character after 'm' if (c == '\x1B') { gm_string_skip_till((const gchar **)(&ptr), "m"); } else if (c != '\x07') { // Store any other character (excluding bell char) fptr += g_unichar_to_utf8(c, fptr); } if (*ptr != '\0') { ptr = g_utf8_next_char(ptr); } } *fptr = '\0'; return s; } int gm_garray_length(gchar **s) { gchar **iter = s; while (*iter++ != NULL) ; return *iter - *s - 1; } void gm_g_list_free_simple(GList *s) { GList *tmp; for (tmp = s; tmp; tmp = tmp->next) { g_free(tmp->data); } g_list_free(s); } gchar * gm_g_list_find_simple(GList *s, gchar *f) { GList *tmp; for (tmp = s; tmp; tmp = tmp->next) { if (strcmp(tmp->data, f) == 0) { return tmp->data; } } return NULL; } gint gm_dialog(gchar * message, GtkMessageType messagebox_type, gint buttons_type, GtkWindow * parent) { GtkWidget *dlg; gint result; dlg = gtk_message_dialog_new(parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, messagebox_type, buttons_type, message, NULL); result = gtk_dialog_run(GTK_DIALOG(dlg)); gtk_widget_destroy(dlg); return result; } gint gm_error_dialog(gchar * message, GtkWindow * parent) { return gm_dialog(message, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, parent); } gint gm_warning_dialog(gchar * message, GtkWindow * parent) { return gm_dialog(message, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, parent); } gint gm_info_dialog(gchar * message, GtkWindow * parent) { return gm_dialog(message, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, parent); } gint gm_question_dialog(gchar * message, GtkWindow * parent) { return gm_dialog(message, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, parent); } void gm_do_events() { while (gtk_events_pending()) { gtk_main_iteration(); } } void gm_directory_remove_all(const gchar * path, gboolean remove_self) { GDir *cDir; gchar *name; gchar *newPath; if (g_file_test(path, G_FILE_TEST_IS_DIR)) { // Iterate through the files and do the right thingie if ((cDir = g_dir_open(path, 0, NULL))) { while ((name = (gchar *) (g_dir_read_name(cDir))) != NULL) { newPath = g_strconcat(path, "/", name, NULL); gm_directory_remove_all(newPath, TRUE); g_free(newPath); } g_dir_close(cDir); } } if (remove_self) { // Its a file, or just empty! MUST...BE...REMOVEEEED! remove(path); } } gint gm_url_regex_match(const gchar *msg, int len, GArray *start, GArray *end) { static gboolean inited = FALSE; regmatch_t matches[1]; gint ret = 0, num_matches = 0, offset = 0; gchar *tmp; gint s; if (!inited) { memset(&url_regexp, 0, sizeof (regex_t)); regcomp(&url_regexp, URL_REGEXP, REG_EXTENDED); inited = TRUE; } tmp = g_strndup(msg, len); while (!ret) { ret = regexec(&url_regexp, (char *)(tmp + offset), 1, matches, 0); if (ret == 0) { if (matches[0].rm_so > matches[0].rm_eo) { break; } num_matches++; s = matches[0].rm_so + offset; offset = matches[0].rm_eo + offset; g_array_append_val(start, s); g_array_append_val(end, offset); } } g_free(tmp); return num_matches; } void gm_open_url(const gchar *url) { if (url == NULL || *url == '\0') { return; } /* gnome_url_show doesn't work when there's no protocol, so we might * need to add one. */ if (strstr(url, "://") == NULL) { gchar *tmp; tmp = g_strconcat("http://", url, NULL); gnome_url_show(tmp, NULL); g_free(tmp); return; } gnome_url_show(url, NULL); } void gm_fetch_handle_free(GmFetchHandle *g) { GList *tmp; g_free(g->cur_file_name); for (tmp = g->source_uri; tmp; tmp = tmp->next) { gnome_vfs_uri_unref((GnomeVFSURI*)(tmp->data)); } g_list_free(g->source_uri); for (tmp = g->dest_uri; tmp; tmp = tmp->next) { gnome_vfs_uri_unref((GnomeVFSURI*)(tmp->data)); } g_list_free(g->dest_uri); g_free(g); } GmFetchHandle * gm_fetch_handle_create(GFunc cb, gpointer user_data) { GmFetchHandle *g = g_new0(GmFetchHandle, 1); g->cb = cb; g->user_data = user_data; g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_OK; g->cur_phase = -1; g->prev_phase = -1; g->cur_file = -1; g->prev_file = -1; return g; } gint gm_fetch_progress(GnomeVFSAsyncHandle *handle, GnomeVFSXferProgressInfo *info, GmFetchHandle *g) { gchar *name; gchar const *err; g->cur_phase = info->phase; g->cur_file = info->file_index; g->files_total = info->files_total; g->bytes_total = info->bytes_total; g->file_size = info->file_size; g->bytes_copied = info->bytes_copied; g->total_bytes_copied = info->total_bytes_copied; g->status = info->status; if (g->aborted) { g->cb(g, g->user_data); gm_fetch_handle_free(g); return FALSE; } if (info->target_name != NULL) { if (g->cur_file_name && strcmp(g->cur_file_name, info->target_name) != 0) { g->cur_phase = GNOME_VFS_XFER_PHASE_FILECOMPLETED; g->cb(g, g->user_data); g->cur_phase = info->phase; g_free(g->cur_file_name); g->cur_file_name = NULL; } if (!g->cur_file_name) { g->cur_file_name = g_strdup(info->target_name); } } if (info->status == GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE) { name = gnome_vfs_get_local_path_from_uri(info->target_name); gm_debug_msg(DEBUG_DEFAULT, "GmFetchProgress: asking for overwriting " "%s: yes", name); g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_OVERWRITE; return GNOME_VFS_XFER_OVERWRITE_ACTION_REPLACE; } else if (info->status == GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR) { name = gnome_vfs_get_local_path_from_uri(info->target_name); err = gnome_vfs_result_to_string(info->vfs_status); gdk_threads_enter(); gm_debug_msg(DEBUG_DEFAULT, "GmFetchProgress: error for %s: %s", name, err); g->cb(g, g->user_data); gdk_threads_leave(); g_free(g->cur_file_name); g->cur_file_name = NULL; g->prev_status = GNOME_VFS_XFER_PROGRESS_STATUS_VFSERROR; g_free(name); return GNOME_VFS_XFER_ERROR_ACTION_SKIP; } if (info->phase == GNOME_VFS_XFER_PHASE_COMPLETED) { if (g->cur_file_name != NULL) { g->cur_phase = GNOME_VFS_XFER_PHASE_FILECOMPLETED; g->cb(g, g->user_data); g->cur_phase = info->phase; } g->done = TRUE; g->cb(g, g->user_data); gm_fetch_handle_free(g); return TRUE; } g->prev_status = info->status; return TRUE; } gint gm_fetch_interact(GnomeVFSXferProgressInfo *info, gpointer user_data) { return 1; } GmFetchHandle * gm_fetch(const GList *source, const GList *dest, GFunc cb, gpointer user_data) { GmFetchHandle *g = gm_fetch_handle_create(cb, user_data); gchar *uri; GnomeVFSResult ret; for (; source; source = source->next) { uri = (gchar *)(source->data); g->source_uri = g_list_append(g->source_uri, gnome_vfs_uri_new(uri)); } for (; dest; dest = dest->next) { uri = (gchar *)(dest->data); g->dest_uri = g_list_append(g->dest_uri, gnome_vfs_uri_new(uri)); } ret = gnome_vfs_async_xfer(&(g->handle), g->source_uri, g->dest_uri, GNOME_VFS_XFER_DEFAULT|GNOME_VFS_XFER_RECURSIVE, GNOME_VFS_XFER_ERROR_MODE_QUERY, GNOME_VFS_XFER_OVERWRITE_MODE_REPLACE, GNOME_VFS_PRIORITY_DEFAULT, (GnomeVFSAsyncXferProgressCallback)gm_fetch_progress, g, gm_fetch_interact, g); return g; } gboolean gm_is_end_scrolled(GtkScrolledWindow *wnd, guint charHeight) { GtkAdjustment *ad = gtk_scrolled_window_get_vadjustment(wnd); return ((ad->page_size + ad->value) >= ad->upper - (double)charHeight); } void gm_scroll_end(GtkTextView *view, gboolean needs) { GtkTextBuffer *buf; GtkTextMark *mark; GtkTextIter iter; if (!needs) { return; } buf = gtk_text_view_get_buffer(view); mark = gtk_text_buffer_get_mark(buf, "end-of-buffer"); if (mark == NULL) { gtk_text_buffer_get_end_iter(buf, &iter); mark = gtk_text_buffer_create_mark(buf, "end-of-buffer", &iter, FALSE); } gtk_text_view_scroll_to_mark(view, mark, 0.0, TRUE, 1.0, 1.0); } #define MAX_BUF 1024 gchar * gm_read_file(const gchar *fname) { int fd; gchar *tmp, *mapped; long bytes_total = 0; if (!fname) { return NULL; } fd = open(fname, O_RDONLY); if (fd == -1) { gm_debug_msg(DEBUG_DEFAULT, "GmReadFile: file (%s) could not be read: " "%s", fname, strerror(errno)); return NULL; } bytes_total = lseek(fd, 0, SEEK_END); lseek(fd, 0, SEEK_SET); if (bytes_total == (off_t)-1) { close(fd); return NULL; } mapped = mmap(0, bytes_total, PROT_READ, MAP_PRIVATE, fd, 0); if (mapped == MAP_FAILED) { close(fd); gm_debug_msg(DEBUG_DEFAULT, "GmReadFile: error while mapping file: %s", strerror(errno)); return NULL; } if (g_utf8_validate(mapped, bytes_total, NULL)) { // Easy, this is utf8! tmp = g_strndup(mapped, bytes_total); } else { // Try locale tmp = g_locale_to_utf8(mapped, bytes_total, NULL, NULL, NULL); if (!tmp) { // Now we're getting desperate... try ISO-8859-15 tmp = g_convert(mapped, bytes_total, "UTF-8", "ISO-8859-15", NULL, NULL, NULL); } if (!tmp) { // OMG try one last conversion to ISO-8859-1 tmp = g_convert(mapped, bytes_total, "UTF-8", "ISO-8859-1", NULL, NULL, NULL); } } munmap(mapped, bytes_total); close(fd); return tmp; } GtkWidget * gm_create_tab_label(const gchar *icon, const gchar *caption, gboolean has_exit, GmLabelInfo *info) { /* First create the gbox (size 3) which will contain an icon, a label and a exit button if has_exit is true */ GtkWidget *hboxTabLabel; gint h, w; GtkRcStyle *rcstyle; hboxTabLabel = gtk_hbox_new(FALSE, 4); gtk_widget_show(hboxTabLabel); gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &w, &h); info->image_icon = gtk_image_new_from_pixbuf( gm_pixbuf_get_at_size(icon, w, h)); gtk_widget_set_size_request(info->image_icon, w, h); gtk_widget_show(info->image_icon); gtk_box_pack_start(GTK_BOX(hboxTabLabel), info->image_icon, TRUE, TRUE, 0); info->label_name = gtk_label_new(caption); gtk_widget_show(info->label_name); gtk_box_pack_start(GTK_BOX(hboxTabLabel), info->label_name, FALSE, FALSE, 0); if (has_exit) { info->button_exit = gtk_button_new(); gtk_widget_show(info->button_exit); gtk_box_pack_end(GTK_BOX(hboxTabLabel), info->button_exit , FALSE, FALSE, 0); gtk_button_set_relief(GTK_BUTTON(info->button_exit), GTK_RELIEF_NONE); gtk_button_set_focus_on_click(GTK_BUTTON(info->button_exit), FALSE); gtk_widget_set_size_request(info->button_exit, w + 2, h + 2); rcstyle = gtk_rc_style_new(); rcstyle->xthickness = rcstyle->ythickness = 0; gtk_widget_modify_style(info->button_exit, rcstyle); gtk_rc_style_unref(rcstyle); info->image_exit = gtk_image_new_from_stock(GTK_STOCK_CLOSE, GTK_ICON_SIZE_MENU); gtk_widget_show(info->image_exit); gtk_container_add(GTK_CONTAINER(info->button_exit), info->image_exit); } return hboxTabLabel; } void gm_widget_destroy_data(GtkWidget *caller, GtkWidget *destroyer) { if (GTK_IS_WIDGET(destroyer)) { gtk_widget_destroy(destroyer); } } const gchar * gm_default_charset() { static const gchar *loc = NULL; if (loc == NULL) { g_get_charset(&loc); } if (loc == NULL || *loc == 0) { loc = "ISO-8859-15"; } return loc; } void gm_notebook_focus_from_label(GtkNotebook *note, gchar *caption) { int p = gtk_notebook_get_n_pages(note); int i; GtkWidget *child; for (i = 0; i < p; i++) { child = gtk_notebook_get_nth_page(note, i); if (!g_strcasecmp(gtk_notebook_get_tab_label_text(note, child), caption)) { gtk_notebook_set_current_page(note, i); break; } } } GtkWidget * gm_find_parent(GtkWidget *widget, GType parent_type) { if (widget == NULL) { return NULL; } if (G_TYPE_CHECK_INSTANCE_TYPE(widget, parent_type)) { return widget; } return gm_find_parent(gtk_widget_get_parent(widget), parent_type); } GtkWidget * gm_find_child(GtkWidget *widget, GType parent_type) { GList *children, *child; if (widget == NULL) { return NULL; } if (G_TYPE_CHECK_INSTANCE_TYPE(widget, parent_type)) { return widget; } if (GTK_IS_CONTAINER(widget)) { children = gtk_container_get_children(GTK_CONTAINER(widget)); widget = NULL; for (child = children; child; child = child->next) { widget = gm_find_child(GTK_WIDGET(child->data), parent_type); if (widget) { break; } } g_list_free(children); return widget; } else { return NULL; } } gchar * gm_convert_with_fallback(gchar const *text, gssize len, gchar const *from, gchar const *to, gchar const *fallback) { gchar *res; gsize read, written; GString *str = g_string_new(""); // TODO: use g_iconv instead of g_convert while ((res = g_convert(text, len, to, from, &read, &written, NULL)) == NULL) { res = g_convert(text, read, to, from, NULL, NULL, NULL); str = g_string_append(str, res); str = g_string_append(str, fallback); text = text + read + 1; if (len != -1) len = len - read - 1; } str = g_string_append(str, res); g_free(res); res = str->str; g_string_free(str, FALSE); return res; } gchar * gm_from_utf8_with_fallback(gchar const *text, gssize len, gchar const *to, gchar const *fallback) { return gm_convert_with_fallback(text, len, "UTF-8", to, fallback); } gchar * gm_to_utf8_with_fallback(gchar const *text, gssize len, gchar const *from, gchar const *fallback) { return gm_convert_with_fallback(text, len, from, "UTF-8", fallback); } GtkWidget * gm_container_item(GtkContainer *cnt, GType type) { GList *child_first = gtk_container_get_children(cnt); GList *child; GtkWidget *result = NULL; for (child = child_first; child; child = child->next) { if (G_TYPE_CHECK_INSTANCE_TYPE(child->data, type)) { result = GTK_WIDGET(child->data); break; } else if (GTK_IS_CONTAINER(child->data)) { if ((result = gm_container_item( GTK_CONTAINER(child->data), type))) { break; } } } g_list_free(child_first); return result; } static void modify_cursor_color(GtkWidget *textview, GdkColor *color) { static const char cursor_color_rc[] = "style \"svs-cc\"\n" "{\n" "GtkTextView::cursor-color=\"#%04x%04x%04x\"\n" "}\n" "widget \"*.%s\" style : application \"svs-cc\"\n"; const gchar *name; gchar *rc_temp; name = gtk_widget_get_name(textview); if (!name) { gtk_widget_set_name(textview, "GmSchemedTextView"); name = gtk_widget_get_name(textview); } g_return_if_fail(name != NULL); if (color != NULL) { rc_temp = g_strdup_printf(cursor_color_rc, color->red, color->green, color->blue, name); } else { GtkRcStyle *rc_style; rc_style = gtk_widget_get_modifier_style(textview); rc_temp = g_strdup_printf(cursor_color_rc, rc_style->text[GTK_STATE_NORMAL].red, rc_style->text[GTK_STATE_NORMAL].green, rc_style->text[GTK_STATE_NORMAL].blue, name); } gtk_rc_parse_string(rc_temp); gtk_widget_reset_rc_styles(textview); g_free(rc_temp); } void on_color_changed(GmColorTable *table, gchar const *color, GtkWidget *widget) { GdkColor col; g_object_set_data(G_OBJECT(widget), "gm_schemed_modifying_style", GINT_TO_POINTER(1)); if (strcmp(color, "fg_default") == 0) { gm_color_table_get(table, color, &col); gtk_widget_modify_text(widget, GTK_STATE_NORMAL, &col); // Modify the cursor color if (GTK_IS_TEXT_VIEW(widget)) { modify_cursor_color(widget, &col); } } else if (strcmp(color, "bg_default") == 0) { gm_color_table_get(table, color, &col); gtk_widget_modify_base(widget, GTK_STATE_NORMAL, &col); } g_object_set_data(G_OBJECT(widget), "gm_schemed_modifying_style", GINT_TO_POINTER(0)); } void on_font_changed(GmColorTable *table, gchar const *font, GtkWidget *widget) { PangoFontDescription *desc = pango_font_description_from_string(font); if (desc) { gtk_widget_modify_font(widget, desc); pango_font_description_free(desc); } } void on_style_set(GtkWidget *widget, GtkStyle *prev, gpointer user_data) { GmSchemedFlags flags = GPOINTER_TO_INT(user_data); GmColorTable *table = GM_COLOR_TABLE(g_object_get_data(G_OBJECT(widget), "gm_color_table")); gpointer modifying = g_object_get_data(G_OBJECT(widget), "gm_schemed_modifying_style"); if (modifying != NULL && GPOINTER_TO_INT(modifying) == 1) { return; } if (flags & GM_SCHEMED_COLORS) { on_color_changed(table, "fg_default", widget); on_color_changed(table, "bg_default", widget); } if (flags & GM_SCHEMED_FONT) { on_font_changed(table, gm_color_table_font_description(table), widget); } } void on_widget_destroy(GtkWidget *widget, gpointer data) { GmSchemedFlags flags = GPOINTER_TO_INT(data); GmColorTable *table = GM_COLOR_TABLE(g_object_get_data(G_OBJECT(widget), "gm_color_table")); if (flags & GM_SCHEMED_COLORS) { g_signal_handlers_disconnect_by_func(table, G_CALLBACK(on_color_changed), widget); } if (flags & GM_SCHEMED_FONT) { g_signal_handlers_disconnect_by_func(table, G_CALLBACK(on_font_changed), widget); } //g_signal_handlers_disconnect_by_func(widget, G_CALLBACK(on_style_set), // data); g_signal_handlers_disconnect_by_func(widget, G_CALLBACK(on_widget_destroy), data); } void gm_register_schemed(GtkWidget *widget, GmColorTable *table, GmSchemedFlags flags) { gpointer data = g_object_get_data(G_OBJECT(widget), "gm_color_table"); if (data != NULL) { on_widget_destroy(widget, GINT_TO_POINTER(flags)); } if (table == NULL) { return; } g_object_set_data(G_OBJECT(widget), "gm_color_table", table); if (flags & GM_SCHEMED_COLORS) { g_signal_connect(table, "color-changed", G_CALLBACK(on_color_changed), widget); on_color_changed(table, "fg_default", widget); on_color_changed(table, "bg_default", widget); } if (flags & GM_SCHEMED_FONT) { g_signal_connect(table, "font-changed", G_CALLBACK(on_font_changed), widget); on_font_changed(table, gm_color_table_font_description(table), widget); } //g_signal_connect(widget, "style-set", // G_CALLBACK(on_style_set), GINT_TO_POINTER(flags)); g_signal_connect(widget, "destroy", G_CALLBACK(on_widget_destroy), GINT_TO_POINTER(flags)); }