/* $Id: desktop.c,v 1.69 2010/03/29 22:48:05 khorben Exp $ */ /* Copyright (c) 2010 Pierre Pronchery */ /* This file is part of DeforaOS Desktop Browser */ /* 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, version 3 of the License. * * 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. If not, see . */ /* FIXME * - use _NET_WORKAREA to determine where to place the icons * - track multiple selection on delete/properties... */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mime.h" #include "desktop.h" #include "../config.h" #define _(string) gettext(string) #define COMMON_SYMLINK #include "common.c" /* constants */ #ifdef PACKAGE # undef PACKAGE #endif #define PACKAGE "desktop" #ifndef PREFIX # define PREFIX "/usr/local" #endif #ifndef DATADIR # define DATADIR PREFIX "/share" #endif #ifndef LOCALEDIR # define LOCALEDIR DATADIR "/locale" #endif /* Desktop */ /* private */ /* constants */ #define DESKTOP ".desktop" #define DESKTOPRC ".desktoprc" /* types */ struct _Desktop { DesktopLayout layout; DesktopIcon ** icon; size_t icon_cnt; Mime * mime; char const * home; char * path; size_t path_cnt; DIR * refresh_dir; time_t refresh_mti; /* applications */ GSList * apps; GdkWindow * root; GdkPixbuf * background; GtkIconTheme * theme; GdkPixbuf * file; GdkPixbuf * folder; GtkWidget * menu; /* preferences */ GtkWidget * pr_window; GtkWidget * pr_background; gint width; gint height; }; /* prototypes */ static int _desktop_error(Desktop * desktop, char const * message, char const * error, int ret); static int _desktop_serror(Desktop * desktop, char const * message, int ret); static Config * _desktop_get_config(Desktop * desktop); /* public */ /* functions */ /* desktop_new */ static Desktop * _new_error(Desktop * desktop, char const * message); static int _new_create_desktop(Desktop * desktop); static int _new_create_desktop_applications(Desktop * desktop); static int _new_create_desktop_files(Desktop * desktop); static void _new_add_home(Desktop * desktop); /* callbacks */ static gboolean _new_idle(gpointer data); static GdkFilterReturn _new_on_root_event(GdkXEvent * xevent, GdkEvent * event, gpointer data); Desktop * desktop_new(DesktopLayout layout) { Desktop * desktop; const char * file[] = { "gnome-fs-regular", #if GTK_CHECK_VERSION(2, 6, 0) GTK_STOCK_FILE, #endif GTK_STOCK_MISSING_IMAGE, NULL }; const char * folder[] = { "gnome-fs-directory", #if GTK_CHECK_VERSION(2, 6, 0) GTK_STOCK_DIRECTORY, #endif GTK_STOCK_MISSING_IMAGE, NULL }; const char ** p; gint x; gint y; gint depth; if((desktop = malloc(sizeof(*desktop))) == NULL) return NULL; desktop->layout = layout; desktop->icon = NULL; desktop->icon_cnt = 0; desktop->path = NULL; desktop->apps = NULL; if((desktop->mime = mime_new()) == NULL || (desktop->icon = malloc(sizeof(*(desktop->icon)) * desktop->icon_cnt)) == NULL) { desktop_delete(desktop); return NULL; } desktop->theme = gtk_icon_theme_get_default(); desktop->file = NULL; for(p = file; *p != NULL && desktop->file == NULL; p++) desktop->file = gtk_icon_theme_load_icon(desktop->theme, *p, DESKTOPICON_ICON_SIZE, 0, NULL); desktop->folder = NULL; for(p = folder; *p != NULL && desktop->folder == NULL; p++) desktop->folder = gtk_icon_theme_load_icon(desktop->theme, *p, DESKTOPICON_ICON_SIZE, 0, NULL); if((desktop->home = getenv("HOME")) == NULL && (desktop->home = g_get_home_dir()) == NULL) desktop->home = "/"; if(_new_create_desktop(desktop) != 0) return _new_error(desktop, "Creating desktop"); desktop_refresh(desktop); /* manage root window events */ desktop->menu = NULL; desktop->root = gdk_screen_get_root_window( gdk_display_get_default_screen( gdk_display_get_default())); desktop->background = NULL; gdk_window_get_geometry(desktop->root, &x, &y, &desktop->width, &desktop->height, &depth); gdk_window_set_events(desktop->root, gdk_window_get_events( desktop->root) | GDK_BUTTON_PRESS_MASK); gdk_window_add_filter(desktop->root, _new_on_root_event, desktop); /* preferences */ desktop->pr_window = NULL; /* draw background when idle */ g_idle_add(_new_idle, desktop); return desktop; } static Desktop * _new_error(Desktop * desktop, char const * message) { desktop_error(desktop, message, -1); desktop_delete(desktop); return NULL; } static int _new_create_desktop(Desktop * desktop) { switch(desktop->layout) { case DL_APPLICATIONS: return _new_create_desktop_applications(desktop); case DL_FILES: default: return _new_create_desktop_files(desktop); } } static int _new_create_desktop_applications(Desktop * desktop) { const char path[] = PREFIX "/share/applications"; struct stat st; if((desktop->path = strdup(path)) == NULL) return 1; desktop->path_cnt = sizeof(path); if(stat(desktop->path, &st) == 0) if(!S_ISDIR(st.st_mode)) { errno = ENOTDIR; return 1; } return 0; } static int _new_create_desktop_files(Desktop * desktop) { struct stat st; desktop->path_cnt = strlen(desktop->home) + strlen("/" DESKTOP) + 1; if((desktop->path = malloc(desktop->path_cnt)) == NULL) return 1; snprintf(desktop->path, desktop->path_cnt, "%s%s", desktop->home, "/" DESKTOP); if(stat(desktop->path, &st) == 0) { if(!S_ISDIR(st.st_mode)) { errno = ENOTDIR; return 1; } } else if(mkdir(desktop->path, 0777) != 0) return 1; _new_add_home(desktop); return 0; } static void _new_add_home(Desktop * desktop) { DesktopIcon * desktopicon; GdkPixbuf * icon; if((desktopicon = desktopicon_new(desktop, _("Home"), desktop->home)) == NULL) return; desktopicon_set_first(desktopicon, TRUE); desktopicon_set_immutable(desktopicon, TRUE); desktop_icon_add(desktop, desktopicon); icon = gtk_icon_theme_load_icon(desktop->theme, "gnome-home", DESKTOPICON_ICON_SIZE, 0, NULL); if(icon == NULL) icon = gtk_icon_theme_load_icon(desktop->theme, "gnome-fs-home", DESKTOPICON_ICON_SIZE, 0, NULL); if(icon != NULL) desktopicon_set_icon(desktopicon, icon); } static gboolean _new_idle(gpointer data) { Desktop * desktop = data; Config * config; char const * p; GError * error = NULL; #ifdef DEBUG fprintf(stderr, "DEBUG: %s()\n", __func__); #endif if((config = _desktop_get_config(desktop)) == NULL || (p = config_get(config, "", "background")) == NULL) { if(config != NULL) config_delete(config); return FALSE; } #ifdef DEBUG fprintf(stderr, "DEBUG: %s() background=\"%s\"\n", __func__, p); #endif g_free(desktop->background); desktop->background = gdk_pixbuf_new_from_file_at_scale(p, desktop->width, desktop->height, FALSE, &error); config_delete(config); if(desktop->background == NULL) { desktop_error(desktop, error->message, 0); return FALSE; } gdk_draw_pixbuf(desktop->root, NULL, desktop->background, 0, 0, 0, 0, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0); gdk_window_set_events(desktop->root, gdk_window_get_events( desktop->root) | GDK_BUTTON_PRESS_MASK | GDK_EXPOSURE_MASK); return FALSE; } static GdkFilterReturn _event_button_press(XButtonEvent * xbev, Desktop * desktop); static GdkFilterReturn _event_expose(XExposeEvent * xevent, Desktop * desktop); static GdkFilterReturn _event_configure(XConfigureEvent * xevent, Desktop * desktop); static void _on_popup_new_folder(gpointer data); static void _on_popup_new_text_file(gpointer data); static void _on_popup_paste(gpointer data); static void _on_popup_preferences(gpointer data); static void _on_popup_symlink(gpointer data); static GdkFilterReturn _new_on_root_event(GdkXEvent * xevent, GdkEvent * event, gpointer data) { Desktop * desktop = data; XEvent * xev = xevent; if(xev->type == ButtonPress) return _event_button_press(xevent, desktop); else if(xev->type == Expose) return _event_expose(xevent, desktop); else if(xev->type == ConfigureNotify) return _event_configure(xevent, desktop); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %d\n", __func__, xev->type); #endif return GDK_FILTER_CONTINUE; } static GdkFilterReturn _event_button_press(XButtonEvent * xbev, Desktop * desktop) { GtkWidget * menuitem; GtkWidget * submenu; GtkWidget * image; if(xbev->button != 3 || desktop->menu != NULL) { if(desktop->menu != NULL) { gtk_widget_destroy(desktop->menu); desktop->menu = NULL; } return GDK_FILTER_CONTINUE; } desktop->menu = gtk_menu_new(); menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_NEW, NULL); submenu = gtk_menu_new(); gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), submenu); gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem); /* submenu for new documents */ menuitem = gtk_image_menu_item_new_with_label(_("Folder")); image = gtk_image_new_from_icon_name("folder-new", GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK( _on_popup_new_folder), desktop); gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); menuitem = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); menuitem = gtk_image_menu_item_new_with_label(_("Symbolic link...")); g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK( _on_popup_symlink), desktop); gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); menuitem = gtk_image_menu_item_new_with_label(_("Text file")); image = gtk_image_new_from_icon_name("stock_new-text", GTK_ICON_SIZE_MENU); gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menuitem), image); g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK( _on_popup_new_text_file), desktop); gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); /* edition */ menuitem = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem); menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_PASTE, NULL); g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK( _on_popup_paste), desktop); gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem); /* preferences */ menuitem = gtk_separator_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem); menuitem = gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL); g_signal_connect_swapped(G_OBJECT(menuitem), "activate", G_CALLBACK( _on_popup_preferences), desktop); gtk_menu_shell_append(GTK_MENU_SHELL(desktop->menu), menuitem); gtk_widget_show_all(desktop->menu); gtk_menu_popup(GTK_MENU(desktop->menu), NULL, NULL, NULL, NULL, 3, xbev->time); return GDK_FILTER_CONTINUE; } static GdkFilterReturn _event_expose(XExposeEvent * xevent, Desktop * desktop) { #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %d %d, %d %d\n", __func__, xevent->x, xevent->y, xevent->width, xevent->height); #endif gdk_draw_pixbuf(desktop->root, NULL, desktop->background, xevent->x, xevent->y, xevent->x, xevent->y, xevent->width, xevent->height, GDK_RGB_DITHER_NORMAL, 0, 0); return GDK_FILTER_CONTINUE; } static GdkFilterReturn _event_configure(XConfigureEvent * xevent, Desktop * desktop) { desktop->width = xevent->width; desktop->height = xevent->height; #ifdef DEBUG fprintf(stderr, "DEBUG: %s() %dx%d\n", __func__, desktop->width, desktop->height); #endif g_idle_add(_new_idle, desktop); /* FIXME run it directly? */ return GDK_FILTER_CONTINUE; } static void _on_popup_new_folder(gpointer data) { static char const newfolder[] = "New folder"; Desktop * desktop = data; String * path; gtk_widget_destroy(desktop->menu); desktop->menu = NULL; if((path = string_new_append(desktop->path, "/", newfolder, NULL)) == NULL) { _desktop_serror(desktop, newfolder, 0); return; } if(mkdir(path, 0777) != 0) desktop_error(desktop, path, 0); string_delete(path); } static void _on_popup_new_text_file(gpointer data) { static char const newtext[] = "New text file.txt"; Desktop * desktop = data; String * path; int fd; gtk_widget_destroy(desktop->menu); desktop->menu = NULL; if((path = string_new_append(desktop->path, "/", newtext, NULL)) == NULL) { _desktop_serror(desktop, newtext, 0); return; } if((fd = creat(path, 0666)) < 0) desktop_error(desktop, path, 0); else close(fd); string_delete(path); } static void _on_popup_paste(gpointer data) { Desktop * desktop = data; /* FIXME implement */ gtk_widget_destroy(desktop->menu); desktop->menu = NULL; } static void _preferences_set(Desktop * desktop); static gboolean _on_preferences_closex(GtkWidget * widget, GdkEvent * event, gpointer data); static void _on_preferences_ok(GtkWidget * widget, gpointer data); static void _on_preferences_apply(GtkWidget * widget, gpointer data); static void _on_preferences_cancel(GtkWidget * widget, gpointer data); static void _on_popup_preferences(gpointer data) { Desktop * desktop = data; GtkWidget * hbox; GtkWidget * vbox; GtkWidget * vbox2; GtkWidget * hbox2; GtkWidget * widget; GtkWidget * label; GtkSizeGroup * group; PangoFontDescription * desc; gtk_widget_destroy(desktop->menu); desktop->menu = NULL; if(desktop->pr_window != NULL) { gtk_widget_show(desktop->pr_window); return; } /* window */ desktop->pr_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(desktop->pr_window), _("Desktop preferences")); g_signal_connect(G_OBJECT(desktop->pr_window), "delete-event", G_CALLBACK(_on_preferences_closex), desktop); hbox = gtk_hbox_new(FALSE, 0); vbox = gtk_vbox_new(FALSE, 0); /* notebook */ desc = pango_font_description_new(); pango_font_description_set_weight(desc, PANGO_WEIGHT_BOLD); widget = gtk_notebook_new(); vbox2 = gtk_vbox_new(FALSE, 0); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); hbox2 = gtk_hbox_new(FALSE, 0); label = gtk_label_new(_("Background: ")); gtk_widget_modify_font(label, desc); gtk_size_group_add_widget(group, label); gtk_box_pack_start(GTK_BOX(hbox2), label, FALSE, TRUE, 0); desktop->pr_background = gtk_file_chooser_button_new(_("Background"), GTK_FILE_CHOOSER_ACTION_OPEN); gtk_box_pack_start(GTK_BOX(hbox2), desktop->pr_background, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox2), hbox2, FALSE, TRUE, 4); gtk_notebook_append_page(GTK_NOTEBOOK(widget), vbox2, gtk_label_new( _("Appearance"))); gtk_box_pack_start(GTK_BOX(vbox), widget, TRUE, TRUE, 4); /* dialog */ hbox2 = gtk_hbox_new(FALSE, 4); group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); widget = gtk_button_new_from_stock(GTK_STOCK_OK); gtk_size_group_add_widget(group, widget); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK( _on_preferences_ok), desktop); gtk_box_pack_end(GTK_BOX(hbox2), widget, FALSE, TRUE, 0); widget = gtk_button_new_from_stock(GTK_STOCK_APPLY); gtk_size_group_add_widget(group, widget); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK( _on_preferences_apply), desktop); gtk_box_pack_end(GTK_BOX(hbox2), widget, FALSE, TRUE, 0); widget = gtk_button_new_from_stock(GTK_STOCK_CANCEL); gtk_size_group_add_widget(group, widget); g_signal_connect(G_OBJECT(widget), "clicked", G_CALLBACK( _on_preferences_cancel), desktop); gtk_box_pack_end(GTK_BOX(hbox2), widget, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox), hbox2, FALSE, TRUE, 4); /* container */ gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 4); gtk_container_add(GTK_CONTAINER(desktop->pr_window), hbox); _preferences_set(desktop); gtk_widget_show_all(desktop->pr_window); } static gboolean _on_preferences_closex(GtkWidget * widget, GdkEvent * event, gpointer data) { gtk_widget_hide(widget); _on_preferences_cancel(NULL, data); return TRUE; } static void _on_preferences_ok(GtkWidget * widget, gpointer data) { Desktop * desktop = data; gtk_widget_hide(desktop->pr_window); _on_preferences_apply(widget, data); } static void _on_preferences_apply(GtkWidget * widget, gpointer data) { Desktop * desktop = data; Config * config; char * p; if((config = _desktop_get_config(desktop)) == NULL) return; p = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER( desktop->pr_background)); config_set(config, "", "background", p); g_free(p); /* XXX code duplication */ if((p = string_new_append(desktop->home, "/" DESKTOPRC, NULL)) != NULL) { config_save(config, p); object_delete(p); } config_delete(config); /* XXX not very efficient */ g_idle_add(_new_idle, desktop); } static void _on_preferences_cancel(GtkWidget * widget, gpointer data) { Desktop * desktop = data; gtk_widget_hide(desktop->pr_window); _preferences_set(desktop); } static void _preferences_set(Desktop * desktop) { Config * config; String const * p; if((config = _desktop_get_config(desktop)) == NULL) { gtk_file_chooser_set_filename(GTK_FILE_CHOOSER( desktop->pr_background), NULL); return; } p = config_get(config, "", "background"); gtk_file_chooser_set_filename(GTK_FILE_CHOOSER( desktop->pr_background), p); config_delete(config); } static void _on_popup_symlink(gpointer data) { Desktop * desktop = data; if(_common_symlink(NULL, desktop->path) != 0) desktop_error(desktop, "symlink", 0); } /* desktop_delete */ void desktop_delete(Desktop * desktop) { size_t i; for(i = 0; i < desktop->icon_cnt; i++) desktopicon_delete(desktop->icon[i]); free(desktop->icon); if(desktop->mime != NULL) mime_delete(desktop->mime); free(desktop->path); free(desktop); } /* accessors */ /* desktop_get_drag_data */ int desktop_get_drag_data(Desktop * desktop, GtkSelectionData * seldata) { int ret = 0; size_t i; size_t len; char const * path; unsigned char * p; seldata->format = 0; seldata->data = NULL; seldata->length = 0; for(i = 0; i < desktop->icon_cnt; i++) { if(desktopicon_get_selected(desktop->icon[i]) != TRUE) continue; if((path = desktopicon_get_path(desktop->icon[i])) == NULL) continue; len = strlen(path + 1); if((p = realloc(seldata->data, seldata->length + len)) == NULL) { ret |= error_set_code(1, "%s", strerror(errno)); continue; } seldata->data = p; memcpy(&p[seldata->length], path, len); seldata->length += len; } return ret; } /* desktop_get_file */ GdkPixbuf * desktop_get_file(Desktop * desktop) { return desktop->file; } /* desktop_get_folder */ GdkPixbuf * desktop_get_folder(Desktop * desktop) { return desktop->folder; } /* desktop_get_mime */ Mime * desktop_get_mime(Desktop * desktop) { return desktop->mime; } /* desktop_get_theme */ GtkIconTheme * desktop_get_theme(Desktop * desktop) { return desktop->theme; } /* useful */ /* desktop_error */ int desktop_error(Desktop * desktop, char const * message, int ret) { return _desktop_error(desktop, message, strerror(errno), ret); } static int _error_text(char const * message, int ret) { fputs("desktop: ", stderr); perror(message); return ret; } /* desktop_refresh */ static void _refresh_current(Desktop * desktop); static int _current_loop(Desktop * desktop); static int _current_loop_applications(Desktop * desktop); static int _current_loop_files(Desktop * desktop); static gboolean _current_idle(gpointer data); static gboolean _current_done(Desktop * desktop); static int _loop_lookup(Desktop * desktop, char const * name); static gboolean _done_timeout(gpointer data); void desktop_refresh(Desktop * desktop) { int fd; struct stat st; #ifdef __sun__ if((fd = open(desktop->path, O_RDONLY)) < 0 || fstat(fd, &st) != 0 || (desktop->refresh_dir = fdopendir(fd)) == NULL) { desktop_error(NULL, desktop->path, 0); if(fd >= 0) close(fd); return; } #else if((desktop->refresh_dir = opendir(desktop->path)) == NULL) { desktop_error(NULL, desktop->path, 0); return; } fd = dirfd(desktop->refresh_dir); if(fstat(fd, &st) != 0) { desktop_error(NULL, desktop->path, 0); closedir(desktop->refresh_dir); return; } #endif desktop->refresh_mti = st.st_mtime; _refresh_current(desktop); } static void _refresh_current(Desktop * desktop) { unsigned int i; for(i = 0; i < 16 && _current_loop(desktop) == 0; i++); if(i == 16) g_idle_add(_current_idle, desktop); else _current_done(desktop); } static int _current_loop(Desktop * desktop) { switch(desktop->layout) { case DL_APPLICATIONS: return _current_loop_applications(desktop); case DL_FILES: default: return _current_loop_files(desktop); } } static int _current_loop_applications(Desktop * desktop) { struct dirent * de; size_t len; const char ext[] = ".desktop"; char * name = NULL; char * p; DesktopIcon * icon; while((de = readdir(desktop->refresh_dir)) != NULL) { if(de->d_name[0] == '.') if(de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')) continue; len = strlen(de->d_name); if(len < sizeof(ext) || strncmp(&de->d_name[len - sizeof(ext) + 1], ext, sizeof(ext)) != 0) continue; if((p = realloc(name, desktop->path_cnt + len + 1)) == NULL) { error_set_print(PACKAGE, 1, "%s: %s", "realloc", strerror(errno)); continue; } name = p; sprintf(name, "%s/%s", desktop->path, de->d_name); #ifdef DEBUG fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, name); #endif if((icon = desktopicon_new_application(desktop, name)) == NULL) continue; desktop_icon_add(desktop, icon); free(name); return 0; } free(name); return 1; } static int _current_loop_files(Desktop * desktop) { struct dirent * de; String * p; DesktopIcon * desktopicon; while((de = readdir(desktop->refresh_dir)) != NULL) { if(de->d_name[0] == '.') if(de->d_name[1] == '\0' || (de->d_name[1] == '.' && de->d_name[2] == '\0')) continue; if(_loop_lookup(desktop, de->d_name) == 1) continue; break; } if(de == NULL) return 1; if((p = string_new_append(desktop->path, "/", de->d_name, NULL)) == NULL) return _desktop_serror(NULL, de->d_name, 1); if((desktopicon = desktopicon_new(desktop, de->d_name, p)) != NULL) desktop_icon_add(desktop, desktopicon); string_delete(p); return 0; } static int _loop_lookup(Desktop * desktop, char const * name) { size_t i; char const * p; for(i = 0; i < desktop->icon_cnt; i++) { if(desktopicon_get_updated(desktop->icon[i]) == TRUE) continue; if((p = desktopicon_get_path(desktop->icon[i])) == NULL || (p = strrchr(p, '/')) == NULL) continue; if(strcmp(name, ++p) != 0) continue; desktopicon_set_updated(desktop->icon[i], TRUE); return 1; } return 0; } static gboolean _current_idle(gpointer data) { Desktop * desktop = data; unsigned int i; for(i = 0; i < 16 && _current_loop(desktop) == 0; i++); if(i == 16) return TRUE; return _current_done(desktop); } static gboolean _current_done(Desktop * desktop) { size_t i = 1; while(i < desktop->icon_cnt) if(desktopicon_get_updated(desktop->icon[i]) != TRUE) desktop_icon_remove(desktop, desktop->icon[i]); else desktopicon_set_updated(desktop->icon[i++], FALSE); closedir(desktop->refresh_dir); g_timeout_add(1000, _done_timeout, desktop); return FALSE; } static gboolean _done_timeout(gpointer data) { Desktop * desktop = data; struct stat st; if(stat(desktop->path, &st) != 0) return desktop_error(NULL, desktop->path, FALSE); if(st.st_mtime == desktop->refresh_mti) return TRUE; desktop_refresh(desktop); return FALSE; } /* desktop_icon_add */ void desktop_icon_add(Desktop * desktop, DesktopIcon * icon) { DesktopIcon ** p; if((p = realloc(desktop->icon, sizeof(*p) * (desktop->icon_cnt + 1))) == NULL) { desktop_error(desktop, "Adding icon", 0); return; } desktop->icon = p; desktop->icon[desktop->icon_cnt++] = icon; desktop_icons_align(desktop); desktopicon_show(icon); } /* desktop_icon_remove */ void desktop_icon_remove(Desktop * desktop, DesktopIcon * icon) { size_t i; DesktopIcon ** p; for(i = 0; i < desktop->icon_cnt; i++) { if(desktop->icon[i] != icon) continue; desktopicon_delete(icon); for(desktop->icon_cnt--; i < desktop->icon_cnt; i++) desktop->icon[i] = desktop->icon[i + 1]; if((p = realloc(desktop->icon, sizeof(*p) * (desktop->icon_cnt))) != NULL) desktop->icon = p; desktop_icons_align(desktop); break; } } /* desktop_icons_align */ static int _align_compare(const void * a, const void * b); void desktop_icons_align(Desktop * desktop) { GdkScreen * screen; int height = INT_MAX; size_t i; int x = 0; int y = 0; qsort(desktop->icon, desktop->icon_cnt, sizeof(void*), _align_compare); if((screen = gdk_screen_get_default()) != NULL) height = gdk_screen_get_height(screen); for(i = 0; i < desktop->icon_cnt; i++) { if(y + DESKTOPICON_MAX_HEIGHT > height) { x += DESKTOPICON_MAX_WIDTH; y = 0; } desktopicon_move(desktop->icon[i], x, y); y += DESKTOPICON_MAX_HEIGHT; } } static int _align_compare(const void * a, const void * b) { DesktopIcon * icona = *(DesktopIcon**)a; DesktopIcon * iconb = *(DesktopIcon**)b; gboolean firsta = desktopicon_get_first(icona); gboolean firstb = desktopicon_get_first(iconb); if(firsta) return firstb ? 0 : -1; else if(firstb) return 1; return strcmp(desktopicon_get_name(icona), desktopicon_get_name(iconb)); } /* desktop_select_all */ void desktop_select_all(Desktop * desktop) { size_t i; for(i = 0; i < desktop->icon_cnt; i++) desktopicon_set_selected(desktop->icon[i], TRUE); } /* desktop_select_above */ void desktop_select_above(Desktop * desktop, DesktopIcon * icon) /* FIXME icons may be wrapped */ { size_t i; for(i = 1; i < desktop->icon_cnt; i++) if(desktop->icon[i] == icon) { desktopicon_set_selected(desktop->icon[i], TRUE); return; } } /* desktop_select_under */ void desktop_select_under(Desktop * desktop, DesktopIcon * icon) /* FIXME icons may be wrapped */ { size_t i; for(i = 0; i < desktop->icon_cnt; i++) if(desktop->icon[i] == icon && i + 1 < desktop->icon_cnt) { desktopicon_set_selected(desktop->icon[i], TRUE); return; } } /* desktop_unselect_all */ void desktop_unselect_all(Desktop * desktop) { size_t i; for(i = 0; i < desktop->icon_cnt; i++) desktopicon_set_selected(desktop->icon[i], FALSE); } /* private */ /* functions */ /* desktop_error */ static int _error_text(char const * message, int ret); static int _desktop_error(Desktop * desktop, char const * message, char const * error, int ret) { GtkWidget * dialog; if(desktop == NULL) return _error_text(message, ret); dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", _("Error")); gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog), "%s: %s", message, error); gtk_window_set_title(GTK_WINDOW(dialog), _("Error")); if(ret < 0) { g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK( gtk_main_quit), NULL); ret = -ret; } else g_signal_connect(G_OBJECT(dialog), "response", G_CALLBACK( gtk_widget_destroy), NULL); gtk_widget_show(dialog); return ret; } /* desktop_serror */ static int _desktop_serror(Desktop * desktop, char const * message, int ret) { return _desktop_error(desktop, message, error_get(), ret); } /* desktop_get_config */ static Config * _desktop_get_config(Desktop * desktop) { Config * config; String * pathname = NULL; if((config = config_new()) == NULL || (pathname = string_new_append(desktop->home, "/" DESKTOPRC, NULL)) == NULL || config_load(config, pathname) != 0) { if(config != NULL) config_delete(config); if(pathname != NULL) object_delete(pathname); _desktop_serror(desktop, "Could not load preferences", FALSE); return NULL; } return config; } /* usage */ static int _usage(void) { fputs(_("Usage: desktop [-A | -F]\n" " -A Display the applications registered\n" " -F Display contents of the desktop folder [default]\n"), stderr); return 1; } /* main */ static void _main_sigchld(int signum); int main(int argc, char * argv[]) { int o; Desktop * desktop; DesktopLayout layout = DL_FILES; struct sigaction sa; setlocale(LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); gtk_init(&argc, &argv); while((o = getopt(argc, argv, "AF")) != -1) switch(o) { case 'A': layout = DL_APPLICATIONS; break; case 'F': layout = DL_FILES; break; default: return _usage(); } if(optind < argc) return _usage(); if((desktop = desktop_new(layout)) == NULL) { gtk_main(); return 2; } sa.sa_handler = _main_sigchld; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if(sigaction(SIGCHLD, &sa, NULL) == -1) desktop_error(desktop, "sigaction", 0); gtk_main(); desktop_delete(desktop); return 0; } static void _main_sigchld(int signum) { wait(NULL); }