This repository has been archived on 2020-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
gnoemoe/gnoemoe/gm-support.c

837 lines
19 KiB
C

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <locale.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <gtk/gtk.h>
#include <regex.h>
#include <libgnome/gnome-url.h>
#include <libgnomevfs/gnome-vfs.h>
#include <errno.h>
#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));
}