Desktop

/* $Id: desktop.c,v 1.150 2012/08/19 21:34:51 khorben Exp $ */
/* Copyright (c) 2007-2012 Pierre Pronchery <khorben@defora.org> */
/* 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 <http://www.gnu.org/licenses/>. */
/* TODO:
* - let the user define the desktop folder (possibly default to FDO's)
* - set the font for the icons instantly
* - track multiple selection on delete/properties */
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
#include <libintl.h>
#include <X11/Xlib.h>
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrandr.h>
#include <System.h>
#include "../include/Browser/desktop.h"
#include "desktop.h"
#include "../config.h"
#define _(string) gettext(string)
#define N_(string) string
#define COMMON_SYMLINK
#include "common.c"
/* constants */
#ifndef PREFIX
# define PREFIX "/usr/local"
#endif
#ifndef DATADIR
# define DATADIR PREFIX "/share"
#endif
#ifndef LOCALEDIR
# define LOCALEDIR DATADIR "/locale"
#endif
/* Desktop */
/* private */
/* types */
typedef struct _DesktopCategory DesktopCategory;
struct _Desktop
{
DesktopPrefs prefs;
PangoFontDescription * font;
GdkColor background;
GdkColor foreground;
/* workarea */
GdkRectangle window;
GdkRectangle workarea;
/* icons */
DesktopIcon ** icon;
size_t icon_cnt;
/* common */
char * path;
size_t path_cnt;
DIR * refresh_dir;
time_t refresh_mti;
guint refresh_source;
/* files */
Mime * mime;
char const * home;
GdkPixbuf * file;
GdkPixbuf * folder;
/* applications */
DesktopCategory * category;
/* categories */
GSList * apps;
/* preferences */
GtkWidget * pr_window;
GtkWidget * pr_color;
GtkWidget * pr_background;
GtkWidget * pr_background_how;
GtkWidget * pr_background_extend;
GtkWidget * pr_ilayout;
GtkWidget * pr_imonitor;
GtkWidget * pr_ibcolor;
GtkWidget * pr_ifcolor;
GtkWidget * pr_ifont;
GtkWidget * pr_monitors;
GtkWidget * pr_monitors_res;
GtkWidget * pr_monitors_size;
/* internal */
GdkScreen * screen;
GdkDisplay * display;
GdkWindow * root;
GtkIconTheme * theme;
GtkWidget * menu;
};
struct _DesktopCategory
{
gboolean show;
char const * category;
char const * name;
char const * icon;
};
typedef enum _DesktopHows
{
DESKTOP_HOW_NONE = 0,
DESKTOP_HOW_CENTERED,
DESKTOP_HOW_SCALED,
DESKTOP_HOW_SCALED_RATIO,
DESKTOP_HOW_TILED
} DesktopHows;
#define DESKTOP_HOW_LAST DESKTOP_HOW_TILED
#define DESKTOP_HOW_COUNT (DESKTOP_HOW_LAST + 1)
/* constants */
#define DESKTOP ".desktop"
#define DESKTOPRC ".desktoprc"
static DesktopCategory _desktop_categories[] =
{
{ FALSE, "Audio;", "Audio", "gnome-mime-audio", },
{ FALSE, "Development;","Development", "applications-development"},
{ FALSE, "Education;", "Education", "applications-science" },
{ FALSE, "Game;", "Games", "applications-games" },
{ FALSE, "Graphics;", "Graphics", "applications-graphics" },
{ FALSE, "AudioVideo;", "Multimedia", "applications-multimedia"},
{ FALSE, "Network;", "Network", "applications-internet" },
{ FALSE, "Office;", "Office", "applications-office" },
{ FALSE, "Settings;", "Settings", "gnome-settings" },
{ FALSE, "System;", "System", "applications-system" },
{ FALSE, "Utility;", "Utilities", "applications-utilities"},
{ FALSE, "Video;", "Video", "video" }
};
static const size_t _desktop_categories_cnt = sizeof(_desktop_categories)
/ sizeof(*_desktop_categories);
static const char * _desktop_hows[DESKTOP_HOW_COUNT] =
{
"none",
"centered",
"scaled",
"scaled_ratio",
"tiled"
};
static const char * _desktop_icons_config[DESKTOP_ICONS_COUNT] =
{
"none", "applications", "categories", "files", "homescreen"
};
static const char * _desktop_icons[DESKTOP_ICONS_COUNT] =
{
N_("Do not draw icons"),
N_("Applications"),
N_("Categories"),
N_("Desktop contents"),
N_("Home screen")
};
/* 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);
/* accessors */
static Config * _desktop_get_config(Desktop * desktop);
static int _desktop_get_workarea(Desktop * desktop);
/* useful */
static void _desktop_draw_background(Desktop * desktop, GdkColor * color,
char const * filename, DesktopHows how, gboolean extend);
static int _desktop_icon_add(Desktop * desktop, DesktopIcon * icon);
static int _desktop_icon_remove(Desktop * desktop, DesktopIcon * icon);
static void _desktop_show_preferences(Desktop * desktop);
/* public */
/* functions */
/* desktop_new */
/* callbacks */
static gboolean _new_idle(gpointer data);
static void _idle_background(Desktop * desktop, Config * config);
static void _idle_icons(Desktop * desktop, Config * config);
static int _on_message(void * data, uint32_t value1, uint32_t value2,
uint32_t value3);
static GdkFilterReturn _on_root_event(GdkXEvent * xevent, GdkEvent * event,
gpointer data);
Desktop * desktop_new(DesktopPrefs * prefs)
{
Desktop * desktop;
#if !GTK_CHECK_VERSION(2, 24, 0)
int depth;
#endif
if((desktop = object_new(sizeof(*desktop))) == NULL)
return NULL;
memset(desktop, 0, sizeof(*desktop));
/* set default foreground to white */
memset(&desktop->foreground, 0xff, sizeof(desktop->foreground));
desktop->prefs.alignment = DESKTOP_ALIGNMENT_VERTICAL;
desktop->prefs.icons = DESKTOP_ICONS_FILES;
desktop->prefs.monitor = -1;
if(prefs != NULL)
memcpy(&desktop->prefs, prefs, sizeof(*prefs));
desktop->font = NULL;
/* workarea */
desktop->screen = gdk_screen_get_default();
desktop->display = gdk_screen_get_display(desktop->screen);
desktop->root = gdk_screen_get_root_window(desktop->screen);
desktop->theme = gtk_icon_theme_get_default();
desktop->menu = NULL;
if((desktop->home = getenv("HOME")) == NULL
&& (desktop->home = g_get_home_dir()) == NULL)
desktop->home = "/";
desktop_message_register(DESKTOP_CLIENT_MESSAGE, _on_message, desktop);
/* manage root window events */
#if GTK_CHECK_VERSION(2, 24, 0)
gdk_window_get_position(desktop->root, &desktop->window.x,
&desktop->window.y);
desktop->window.width = gdk_window_get_width(desktop->root);
desktop->window.height = gdk_window_get_height(desktop->root);
#else
gdk_window_get_geometry(desktop->root, &desktop->window.x,
&desktop->window.y, &desktop->window.width,
&desktop->window.height, &depth);
#endif
gdk_window_set_events(desktop->root, gdk_window_get_events(
desktop->root) | GDK_BUTTON_PRESS_MASK
| GDK_PROPERTY_CHANGE_MASK);
gdk_window_add_filter(desktop->root, _on_root_event, desktop);
/* draw the icons and background when idle */
g_idle_add(_new_idle, desktop);
return desktop;
}
static gboolean _new_idle(gpointer data)
{
Desktop * desktop = data;
Config * config;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if((config = _desktop_get_config(desktop)) == NULL)
return FALSE;
_idle_background(desktop, config);
_idle_icons(desktop, config);
config_delete(config);
_desktop_get_workarea(desktop);
desktop_set_icons(desktop, desktop->prefs.icons);
return FALSE;
}
static void _idle_background(Desktop * desktop, Config * config)
{
GdkColor color = { 0, 0, 0, 0 };
char const * filename;
DesktopHows how = DESKTOP_HOW_SCALED;
gboolean extend = FALSE;
size_t i;
char const * p;
if((p = config_get(config, "background", "color")) != NULL)
gdk_color_parse(p, &color);
filename = config_get(config, "background", "wallpaper");
if((p = config_get(config, "background", "how")) != NULL)
for(i = 0; i < DESKTOP_HOW_COUNT; i++)
if(strcmp(_desktop_hows[i], p) == 0)
how = i;
if((p = config_get(config, "background", "extend")) != NULL)
extend = strtol(p, NULL, 10) ? TRUE : FALSE;
_desktop_draw_background(desktop, &color, filename, how, extend);
}
static void _idle_icons(Desktop * desktop, Config * config)
{
GdkColor color;
char const * p;
char * q;
size_t i;
/* icons colors */
if((p = config_get(config, "icons", "background")) != NULL)
{
gdk_color_parse(p, &color);
desktop->background = color;
}
if((p = config_get(config, "icons", "foreground")) != NULL)
{
gdk_color_parse(p, &color);
desktop->foreground = color;
}
/* icons font */
if((p = config_get(config, "icons", "font")) != NULL)
desktop->font = pango_font_description_from_string(p);
else
{
desktop->font = pango_font_description_new();
pango_font_description_set_weight(desktop->font,
PANGO_WEIGHT_BOLD);
}
for(i = 0; i < desktop->icon_cnt; i++)
{
desktopicon_set_background(desktop->icon[i],
&desktop->background);
desktopicon_set_font(desktop->icon[i], desktop->font);
desktopicon_set_foreground(desktop->icon[i],
&desktop->foreground);
}
/* icons monitor */
if(desktop->prefs.monitor < 0
&& (p = config_get(config, "icons", "monitor")) != NULL)
{
desktop->prefs.monitor = strtol(p, &q, 10);
if(p[0] == '\0' || *q != '\0')
desktop->prefs.monitor = -1;
}
/* icons layout */
if(desktop->prefs.icons < 0
&& (p = config_get(config, "icons", "layout")) != NULL)
{
for(i = 0; i < DESKTOP_ICONS_COUNT; i++)
if(strcmp(_desktop_icons_config[i], p) == 0)
{
desktop->prefs.icons = i;
break;
}
}
if(desktop->prefs.icons < 0
|| desktop->prefs.icons >= DESKTOP_ICONS_COUNT)
desktop->prefs.icons = DESKTOP_ICONS_FILES;
/* icons alignment */
if(desktop->prefs.alignment < 0)
desktop->prefs.alignment = (desktop->prefs.icons
== DESKTOP_ICONS_FILES)
? DESKTOP_ALIGNMENT_VERTICAL
: DESKTOP_ALIGNMENT_HORIZONTAL;
}
static int _on_message(void * data, uint32_t value1, uint32_t value2,
uint32_t value3)
{
Desktop * desktop = data;
DesktopMessage message;
DesktopAlignment alignment;
DesktopIcons icons;
DesktopLayout layout;
switch((message = value1))
{
case DESKTOP_MESSAGE_SET_ALIGNMENT:
alignment = value2;
desktop_set_alignment(desktop, alignment);
break;
case DESKTOP_MESSAGE_SET_ICONS:
icons = value2;
desktop_set_icons(desktop, icons);
break;
case DESKTOP_MESSAGE_SET_LAYOUT:
layout = value2;
desktop_set_layout(desktop, layout);
break;
case DESKTOP_MESSAGE_SHOW:
if(value2 == DESKTOP_SHOW_SETTINGS)
_desktop_show_preferences(desktop);
break;
}
return GDK_FILTER_CONTINUE;
}
static GdkFilterReturn _event_button_press(XButtonEvent * xbev,
Desktop * desktop);
static GdkFilterReturn _event_configure(XConfigureEvent * xevent,
Desktop * desktop);
static GdkFilterReturn _event_property(XPropertyEvent * 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 _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 == ConfigureNotify)
return _event_configure(xevent, desktop);
else if(xev->type == PropertyNotify)
return _event_property(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;
}
/* ignore if not managing files */
if(desktop->prefs.icons != DESKTOP_ICONS_FILES)
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_configure(XConfigureEvent * xevent,
Desktop * desktop)
{
desktop->window.x = xevent->x;
desktop->window.y = xevent->y;
desktop->window.width = xevent->width;
desktop->window.height = xevent->height;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() (%dx%d) @ (%d,%d))\n", __func__,
desktop->window.width, desktop->window.height,
desktop->window.x, desktop->window.y);
#endif
g_idle_add(_new_idle, desktop); /* FIXME run it directly? */
return GDK_FILTER_CONTINUE;
}
static GdkFilterReturn _event_property(XPropertyEvent * xevent,
Desktop * desktop)
{
Atom atom;
atom = gdk_x11_get_xatom_by_name("_NET_WORKAREA");
if(xevent->atom != atom)
return GDK_FILTER_CONTINUE;
_desktop_get_workarea(desktop);
return GDK_FILTER_CONTINUE;
}
static void _on_popup_new_folder(gpointer data)
{
static char const newfolder[] = N_("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[] = N_("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 _on_popup_preferences(gpointer data)
{
Desktop * desktop = data;
_desktop_show_preferences(desktop);
}
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;
if(desktop->refresh_source != 0)
g_source_remove(desktop->refresh_source);
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);
if(desktop->font != NULL)
pango_font_description_free(desktop->font);
object_delete(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;
}
/* desktop_set_alignment */
static void _alignment_horizontal(Desktop * desktop);
static void _alignment_vertical(Desktop * desktop);
void desktop_set_alignment(Desktop * desktop, DesktopAlignment alignment)
{
switch(alignment)
{
case DESKTOP_ALIGNMENT_VERTICAL:
_alignment_vertical(desktop);
break;
case DESKTOP_ALIGNMENT_HORIZONTAL:
_alignment_horizontal(desktop);
break;
}
}
static void _alignment_horizontal(Desktop * desktop)
{
size_t i;
int x = desktop->workarea.x;
int y = desktop->workarea.y;
int width = x + desktop->workarea.width;
for(i = 0; i < desktop->icon_cnt; i++)
{
if(x + DESKTOPICON_MAX_WIDTH > width)
{
y += DESKTOPICON_MAX_HEIGHT;
x = desktop->workarea.x;
}
desktopicon_move(desktop->icon[i], x, y);
x += DESKTOPICON_MAX_WIDTH;
}
}
static void _alignment_vertical(Desktop * desktop)
{
size_t i;
int x = desktop->workarea.x;
int y = desktop->workarea.y;
int height = desktop->workarea.y + desktop->workarea.height;
for(i = 0; i < desktop->icon_cnt; i++)
{
if(y + DESKTOPICON_MAX_HEIGHT > height)
{
x += DESKTOPICON_MAX_WIDTH;
y = desktop->workarea.y;
}
desktopicon_move(desktop->icon[i], x, y);
y += DESKTOPICON_MAX_HEIGHT;
}
}
/* desktop_set_icons */
static void _icons_delete(Desktop * desktop);
static int _icons_applications(Desktop * desktop);
static int _icons_categories(Desktop * desktop);
static int _icons_files(Desktop * desktop);
static void _icons_files_add_home(Desktop * desktop);
static int _icons_homescreen(Desktop * desktop);
static void _icons_set_categories(Desktop * desktop, gpointer data);
static void _icons_set_homescreen(Desktop * desktop, gpointer data);
void desktop_set_icons(Desktop * desktop, DesktopIcons icons)
{
_icons_delete(desktop);
desktop->prefs.icons = icons;
switch(icons)
{
case DESKTOP_ICONS_APPLICATIONS:
_icons_applications(desktop);
break;
case DESKTOP_ICONS_CATEGORIES:
_icons_categories(desktop);
break;
case DESKTOP_ICONS_FILES:
_icons_files(desktop);
break;
case DESKTOP_ICONS_HOMESCREEN:
_icons_homescreen(desktop);
break;
case DESKTOP_ICONS_NONE:
/* nothing to do */
break;
}
desktop_refresh(desktop);
}
static void _icons_delete(Desktop * desktop)
{
size_t i;
if(desktop->path != NULL)
free(desktop->path);
desktop->path = NULL;
desktop->path_cnt = 0;
for(i = 0; i < desktop->icon_cnt; i++)
{
desktopicon_set_immutable(desktop->icon[i], FALSE);
desktopicon_set_updated(desktop->icon[i], FALSE);
}
for(i = 0; i < _desktop_categories_cnt; i++)
_desktop_categories[i].show = FALSE;
}
static int _icons_applications(Desktop * desktop)
{
const char path[] = DATADIR "/applications";
struct stat st;
DesktopIcon * desktopicon;
GdkPixbuf * icon;
free(desktop->path);
if((desktop->path = strdup(path)) == NULL)
return desktop_error(NULL, strerror(errno), 1);
desktop->path_cnt = sizeof(path);
if(stat(desktop->path, &st) == 0)
if(!S_ISDIR(st.st_mode))
return desktop_error(NULL, strerror(ENOTDIR), 1);
/* FIXME list all applications otherwise? */
if(desktop->category != NULL)
{
desktopicon = desktopicon_new(desktop, _("Back"), NULL);
desktopicon_set_callback(desktopicon, _icons_set_categories,
NULL);
desktopicon_set_immutable(desktopicon, TRUE);
icon = gtk_icon_theme_load_icon(desktop->theme, "back",
DESKTOPICON_ICON_SIZE, 0, NULL);
if(icon != NULL)
desktopicon_set_icon(desktopicon, icon);
_desktop_icon_add(desktop, desktopicon);
}
return 0;
}
static int _icons_categories(Desktop * desktop)
{
DesktopIcon * desktopicon;
GdkPixbuf * icon;
desktop->category = NULL;
_icons_applications(desktop); /* XXX hack */
desktopicon = desktopicon_new(desktop, _("Back"), NULL);
desktopicon_set_callback(desktopicon, _icons_set_homescreen, NULL);
desktopicon_set_first(desktopicon, TRUE);
desktopicon_set_immutable(desktopicon, TRUE);
icon = gtk_icon_theme_load_icon(desktop->theme, "back",
DESKTOPICON_ICON_SIZE, 0, NULL);
if(icon != NULL)
desktopicon_set_icon(desktopicon, icon);
_desktop_icon_add(desktop, desktopicon);
return 0;
}
static int _icons_files(Desktop * desktop)
{
const char path[] = "/" 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 };
char const ** p;
struct stat st;
if(desktop->mime == NULL)
desktop->mime = mime_new(NULL);
if(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);
if(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);
_icons_files_add_home(desktop);
desktop->path_cnt = strlen(desktop->home) + 1 + sizeof(path);
if((desktop->path = malloc(desktop->path_cnt)) == NULL)
return desktop_error(NULL, strerror(ENOTDIR), 1);
snprintf(desktop->path, desktop->path_cnt, "%s/%s", desktop->home,
path);
if(stat(desktop->path, &st) == 0)
{
if(!S_ISDIR(st.st_mode))
return desktop_error(NULL, strerror(ENOTDIR), 1);
}
else if(errno != ENOENT || mkdir(desktop->path, 0777) != 0)
return desktop_error(NULL, strerror(errno), 1);
return 0;
}
static void _icons_files_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 int _icons_homescreen(Desktop * desktop)
{
DesktopIcon * desktopicon;
GdkPixbuf * icon;
#ifdef EMBEDDED
char const * paths[] =
{
DATADIR "/applications/phone-contacts.desktop",
DATADIR "/applications/phone-dialer.desktop",
DATADIR "/applications/phone-messages.desktop",
NULL
};
char const ** p;
#endif
if((desktopicon = desktopicon_new(desktop, _("Applications"), NULL))
== NULL)
return desktop_error(NULL, error_get(), 1);
desktopicon_set_callback(desktopicon, _icons_set_categories, NULL);
desktopicon_set_immutable(desktopicon, TRUE);
icon = gtk_icon_theme_load_icon(desktop->theme, "gnome-applications",
DESKTOPICON_ICON_SIZE, 0, NULL);
if(icon != NULL)
desktopicon_set_icon(desktopicon, icon);
_desktop_icon_add(desktop, desktopicon);
#ifdef EMBEDDED
for(p = paths; *p != NULL; p++)
if(access(*p, R_OK) == 0
&& (desktopicon = desktopicon_new_application(
desktop, *p)) != NULL)
_desktop_icon_add(desktop, desktopicon);
#endif
return 0;
}
static void _icons_set_categories(Desktop * desktop, gpointer data)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
desktop_set_icons(desktop, DESKTOP_ICONS_CATEGORIES);
}
static void _icons_set_homescreen(Desktop * desktop, gpointer data)
{
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
desktop_set_icons(desktop, DESKTOP_ICONS_HOMESCREEN);
}
/* desktop_set_layout */
int desktop_set_layout(Desktop * desktop, DesktopLayout layout)
{
XRRScreenConfiguration * sc;
Rotation r;
SizeID size;
sc = XRRGetScreenInfo(GDK_DISPLAY_XDISPLAY(desktop->display),
GDK_WINDOW_XWINDOW(desktop->root));
size = XRRConfigCurrentConfiguration(sc, &r);
switch(layout)
{
case DESKTOP_LAYOUT_NORMAL:
case DESKTOP_LAYOUT_LANDSCAPE:
r = RR_Rotate_0;
break;
case DESKTOP_LAYOUT_PORTRAIT:
r = RR_Rotate_90;
break;
case DESKTOP_LAYOUT_ROTATE:
r <<= 1;
r = (r > 16) ? 1 : r;
break;
case DESKTOP_LAYOUT_TOGGLE:
r = (r != RR_Rotate_0) ? RR_Rotate_0 : RR_Rotate_90;
break;
}
gdk_error_trap_push();
XRRSetScreenConfig(GDK_DISPLAY_XDISPLAY(desktop->display), sc,
GDK_WINDOW_XWINDOW(desktop->root), size, r,
CurrentTime);
return gdk_error_trap_pop();
}
/* useful */
/* desktop_error */
int desktop_error(Desktop * desktop, char const * message, int ret)
{
return _desktop_error(desktop, message, strerror(errno), ret);
}
/* desktop_refresh */
static int _current_loop(Desktop * desktop);
static int _current_loop_applications(Desktop * desktop);
static gint _categories_apps_compare(gconstpointer a, gconstpointer b);
static int _current_loop_categories(Desktop * desktop);
static int _current_loop_files(Desktop * desktop);
static gboolean _current_idle(gpointer data);
static gboolean _current_done(Desktop * desktop);
static void _done_categories(Desktop * desktop);
static void _done_categories_open(Desktop * desktop, gpointer data);
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 DEBUG
fprintf(stderr, "DEBUG: %s()\n", __func__);
#endif
if(desktop->refresh_source != 0)
g_source_remove(desktop->refresh_source);
if(desktop->path == NULL)
{
desktop->refresh_source = g_idle_add(_current_idle, desktop);
return;
}
desktop->refresh_source = 0;
#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;
desktop->refresh_source = g_idle_add(_current_idle, desktop);
}
static int _current_loop(Desktop * desktop)
{
switch(desktop->prefs.icons)
{
case DESKTOP_ICONS_APPLICATIONS:
return _current_loop_applications(desktop);
case DESKTOP_ICONS_CATEGORIES:
return _current_loop_categories(desktop);
case DESKTOP_ICONS_FILES:
return _current_loop_files(desktop);
case DESKTOP_ICONS_HOMESCREEN:
case DESKTOP_ICONS_NONE:
break; /* nothing to do */
}
return -1;
}
static int _current_loop_applications(Desktop * desktop)
{
struct dirent * de;
size_t len;
const char ext[] = ".desktop";
char * path = NULL;
char * p;
Config * config;
char const * q;
DesktopIcon * icon;
if(desktop->category == NULL)
return -1;
if((config = config_new()) == NULL)
return -1;
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(path, desktop->path_cnt + len + 1)) == NULL)
{
error_set_print("desktop", 1, "%s: %s", "realloc",
strerror(errno));
continue;
}
path = p;
snprintf(path, desktop->path_cnt + len + 1, "%s/%s",
desktop->path, de->d_name);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, path);
#endif
config_reset(config);
if(config_load(config, path) != 0)
continue;
if((q = config_get(config, "Desktop Entry", "Categories"))
== NULL)
continue;
if(string_find(q, desktop->category->category) == NULL)
continue;
if((icon = desktopicon_new_application(desktop, path)) == NULL)
continue;
_desktop_icon_add(desktop, icon);
free(path);
config_delete(config);
return 0;
}
free(path);
config_delete(config);
return -1;
}
static int _current_loop_categories(Desktop * desktop)
{
struct dirent * de;
size_t len;
const char ext[] = ".desktop";
const char section[] = "Desktop Entry";
char * path = NULL;
char * p;
Config * config = NULL;
char const * q;
char const * r;
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(path, desktop->path_cnt + len + 1)) == NULL)
{
error_set_print("desktop", 1, "%s: %s", "realloc",
strerror(errno));
continue;
}
path = p;
snprintf(path, desktop->path_cnt + len + 1, "%s/%s",
desktop->path, de->d_name);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, path);
#endif
if(config == NULL && (config = config_new()) == NULL)
continue; /* XXX report error */
config_reset(config);
if(config_load(config, path) != 0)
{
error_set_print("desktop", 1, "%s: %s", path,
error_get());
continue;
}
q = config_get(config, section, "Name");
r = config_get(config, section, "Exec");
if(q == NULL || r == NULL)
continue;
config_set(config, NULL, "path", path);
desktop->apps = g_slist_insert_sorted(desktop->apps, config,
_categories_apps_compare);
free(path);
config = NULL;
return 0;
}
free(path);
return -1;
}
static gint _categories_apps_compare(gconstpointer a, gconstpointer b)
{
Config * ca = (Config *)a;
Config * cb = (Config *)b;
char const * cap;
char const * cbp;
const char section[] = "Desktop Entry";
const char variable[] = "Name";
/* these should not fail */
cap = config_get(ca, section, variable);
cbp = config_get(cb, section, variable);
return string_compare(cap, cbp);
}
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 = 0;
switch(desktop->prefs.icons)
{
case DESKTOP_ICONS_CATEGORIES:
_done_categories(desktop);
break;
default:
break;
}
while(i < desktop->icon_cnt)
if(desktopicon_get_immutable(desktop->icon[i]) == TRUE)
i++;
else if(desktopicon_get_updated(desktop->icon[i]) != TRUE)
_desktop_icon_remove(desktop, desktop->icon[i]);
else
desktopicon_set_updated(desktop->icon[i++], FALSE);
if(desktop->refresh_dir != NULL)
closedir(desktop->refresh_dir);
desktop->refresh_dir = NULL;
desktop_icons_align(desktop);
desktop->refresh_source = g_timeout_add(1000, _done_timeout, desktop);
return FALSE;
}
static void _done_categories(Desktop * desktop)
{
GSList * p;
Config * config;
const char section[] = "Desktop Entry";
char const * q;
size_t i;
DesktopCategory * dc;
char const * path;
DesktopIcon * icon;
for(p = desktop->apps; p != NULL; p = p->next)
{
config = p->data;
path = config_get(config, NULL, "path");
if((q = config_get(config, section, "Categories")) == NULL)
{
icon = desktopicon_new_application(desktop, path);
_desktop_icon_add(desktop, icon);
continue;
}
for(i = 0; i < _desktop_categories_cnt
&& (dc = &_desktop_categories[i]) != NULL
&& dc->category != NULL
&& string_find(q, dc->category) == NULL; i++);
if(dc->category == NULL)
{
icon = desktopicon_new_application(desktop, path);
_desktop_icon_add(desktop, icon);
continue;
}
if(dc->show == TRUE)
continue;
dc->show = TRUE;
icon = desktopicon_new_category(desktop, dc->name, dc->icon);
desktopicon_set_callback(icon, _done_categories_open, dc);
_desktop_icon_add(desktop, icon);
}
}
static void _done_categories_open(Desktop * desktop, gpointer data)
{
DesktopCategory * dc = data;
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() \"%s\"\n", __func__, dc->name);
#endif
desktop->category = dc;
desktop_set_icons(desktop, DESKTOP_ICONS_APPLICATIONS);
}
static gboolean _done_timeout(gpointer data)
{
Desktop * desktop = data;
struct stat st;
desktop->refresh_source = 0;
if(desktop->path == NULL)
return FALSE;
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)
{
if(_desktop_icon_add(desktop, icon) == 0)
desktop_icons_align(desktop);
}
/* desktop_icon_remove */
void desktop_icon_remove(Desktop * desktop, DesktopIcon * icon)
{
if(_desktop_icon_remove(desktop, icon) == 0)
desktop_icons_align(desktop);
}
/* desktop_icons_align */
static int _align_compare(const void * a, const void * b);
void desktop_icons_align(Desktop * desktop)
{
qsort(desktop->icon, desktop->icon_cnt, sizeof(void*), _align_compare);
desktop_set_alignment(desktop, desktop->prefs.alignment);
}
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);
gboolean dira;
gboolean dirb;
if(firsta && !firstb)
return -1;
else if(!firsta && firstb)
return 1;
dira = desktopicon_get_isdir(icona);
dirb = desktopicon_get_isdir(iconb);
if(dira && !dirb)
return -1;
else if(!dira && dirb)
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",
#if GTK_CHECK_VERSION(2, 6, 0)
_("Error"));
gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog),
"%s: %s", message,
#endif
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;
}
static int _error_text(char const * message, int ret)
{
fputs("desktop: ", stderr);
perror(message);
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)
{
if(config != NULL)
config_delete(config);
if(pathname != NULL)
object_delete(pathname);
_desktop_serror(NULL, _("Could not load preferences"), FALSE);
return NULL;
}
config_load(config, pathname); /* XXX ignore errors */
return config;
}
/* desktop_get_workarea */
static int _desktop_get_workarea(Desktop * desktop)
{
Atom atom;
Atom type;
int format;
unsigned long cnt;
unsigned long bytes;
unsigned char * p = NULL;
unsigned long * u;
if(desktop->prefs.monitor >= 0 && desktop->prefs.monitor
< gdk_screen_get_n_monitors(desktop->screen))
{
gdk_screen_get_monitor_geometry(desktop->screen,
desktop->prefs.monitor, &desktop->workarea);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() (%d, %d) %dx%d\n", __func__,
desktop->workarea.x, desktop->workarea.y,
desktop->workarea.width,
desktop->workarea.height);
#endif
desktop_icons_align(desktop);
return 0;
}
atom = gdk_x11_get_xatom_by_name("_NET_WORKAREA");
if(XGetWindowProperty(GDK_DISPLAY_XDISPLAY(desktop->display),
GDK_WINDOW_XID(desktop->root), atom, 0,
G_MAXLONG, False, XA_CARDINAL, &type, &format,
&cnt, &bytes, &p) == Success && cnt >= 4)
{
u = (unsigned long *)p;
desktop->workarea.x = u[0];
desktop->workarea.y = u[1];
if((desktop->workarea.width = u[2]) == 0
|| (desktop->workarea.height = u[3]) == 0)
gdk_screen_get_monitor_geometry(desktop->screen, 0,
&desktop->workarea);
}
else
gdk_screen_get_monitor_geometry(desktop->screen, 0,
&desktop->workarea);
XFree(p);
#ifdef DEBUG
fprintf(stderr, "DEBUG: %s() (%d, %d) %dx%d\n", __func__,
desktop->workarea.x, desktop->workarea.y,
desktop->workarea.width, desktop->workarea.height);
#endif
desktop_icons_align(desktop);
return 0;
}
/* useful */
/* desktop_background */
static void _background_centered(GdkRectangle * window, GdkPixmap * pixmap,
char const * filename, GError ** error);
static void _background_scaled(GdkRectangle * window, GdkPixmap * pixmap,
char const * filename, GError ** error);
static void _background_scaled_ratio(GdkRectangle * window, GdkPixmap * pixmap,
char const * filename, GError ** error);
static void _background_tiled(GdkRectangle * window, GdkPixmap * pixmap,
char const * filename, GError ** error);
static void _desktop_draw_background(Desktop * desktop, GdkColor * color,
char const * filename, DesktopHows how, gboolean extend)
{
GdkGC * gc;
GdkPixmap * pixmap;
GdkRectangle window = desktop->window;
GError * error = NULL;
gint n = 1;
gint i;
if(how == DESKTOP_HOW_NONE)
return;
/* draw default color */
pixmap = gdk_pixmap_new(desktop->root, window.width, window.height, -1);
gc = gdk_gc_new(pixmap);
gdk_gc_set_rgb_fg_color(gc, color);
gdk_draw_rectangle(pixmap, gc, TRUE, 0, 0, window.width, window.height);
/* obtain monitor information */
if(extend != TRUE)
n = gdk_screen_get_n_monitors(desktop->screen);
for(i = 0; filename != NULL && i < n; i++)
{
if(extend != TRUE)
gdk_screen_get_monitor_geometry(desktop->screen, i,
&window);
switch(how)
{
case DESKTOP_HOW_NONE:
break;
case DESKTOP_HOW_CENTERED:
_background_centered(&window, pixmap, filename,
&error);
break;
case DESKTOP_HOW_SCALED_RATIO:
_background_scaled_ratio(&window, pixmap,
filename, &error);
break;
case DESKTOP_HOW_TILED:
_background_tiled(&window, pixmap, filename,
&error);
break;
case DESKTOP_HOW_SCALED:
_background_scaled(&window, pixmap, filename,
&error);
break;
}
if(error != NULL)
{
desktop_error(desktop, error->message, 1);
g_error_free(error);
}
}
gdk_window_set_back_pixmap(desktop->root, pixmap, FALSE);
gdk_window_clear(desktop->root);
gdk_pixmap_unref(pixmap);
}
static void _background_centered(GdkRectangle * window, GdkPixmap * pixmap,
char const * filename, GError ** error)
{
GdkPixbuf * background;
gint w;
gint h;
if((background = gdk_pixbuf_new_from_file(filename, error)) == NULL)
return;
w = gdk_pixbuf_get_width(background);
h = gdk_pixbuf_get_height(background);
gdk_draw_pixbuf(pixmap, NULL, background, 0, 0,
(window->width - w) / 2 + window->x,
(window->height - h) / 2 + window->y, w, h,
GDK_RGB_DITHER_NONE, 0, 0);
g_object_unref(background);
}
static void _background_scaled(GdkRectangle * window, GdkPixmap * pixmap,
char const * filename, GError ** error)
{
GdkPixbuf * background;
gint w;
gint h;
#if GTK_CHECK_VERSION(2, 6, 0)
background = gdk_pixbuf_new_from_file_at_scale(filename, window->width,
window->height, FALSE, error);
#elif GTK_CHECK_VERSION(2, 4, 0)
background = gdk_pixbuf_new_from_file_at_size(filename, window->width,
window->height, error);
#else
background = gdk_pixbuf_new_from_file(filename, &error);
#endif
if(background == NULL)
return;
w = gdk_pixbuf_get_width(background);
h = gdk_pixbuf_get_height(background);
gdk_draw_pixbuf(pixmap, NULL, background, 0, 0,
(window->width - w) / 2 + window->x,
(window->height - h) / 2 + window->y, w, h,
GDK_RGB_DITHER_NONE, 0, 0);
g_object_unref(background);
}
static void _background_scaled_ratio(GdkRectangle * window, GdkPixmap * pixmap,
char const * filename, GError ** error)
{
GdkPixbuf * background;
gint w;
gint h;
#if GTK_CHECK_VERSION(2, 4, 0)
background = gdk_pixbuf_new_from_file_at_size(filename, window->width,
window->height, error);
if(background == NULL)
return; /* XXX report error */
w = gdk_pixbuf_get_width(background);
h = gdk_pixbuf_get_height(background);
gdk_draw_pixbuf(pixmap, NULL, background, 0, 0,
(window->width - w) / 2 + window->x,
(window->height - h) / 2 + window->y, w, h,
GDK_RGB_DITHER_NONE, 0, 0);
g_object_unref(background);
#else
_background_scaled(desktop, pixmap, filename);
#endif
}
static void _background_tiled(GdkRectangle * window, GdkPixmap * pixmap,
char const * filename, GError ** error)
{
GdkPixbuf * background;
gint w;
gint h;
gint i;
gint j;
if((background = gdk_pixbuf_new_from_file(filename, error)) == NULL)
return; /* XXX report error */
w = gdk_pixbuf_get_width(background);
h = gdk_pixbuf_get_height(background);
for(j = 0; j < window->height; j += h)
for(i = 0; i < window->width; i += w)
gdk_draw_pixbuf(pixmap, NULL, background, 0, 0,
i + window->x, j + window->y, w, h,
GDK_RGB_DITHER_NONE, 0, 0);
g_object_unref(background);
}
/* desktop_icon_add */
static int _desktop_icon_add(Desktop * desktop, DesktopIcon * icon)
{
DesktopIcon ** p;
if((p = realloc(desktop->icon, sizeof(*p) * (desktop->icon_cnt + 1)))
== NULL)
{
desktop_error(desktop, desktopicon_get_name(icon), 0);
return -1;
}
desktop->icon = p;
desktop->icon[desktop->icon_cnt++] = icon;
desktopicon_set_background(icon, &desktop->background);
desktopicon_set_font(icon, desktop->font);
desktopicon_set_foreground(icon, &desktop->foreground);
desktopicon_show(icon);
return 0;
}
/* desktop_icon_remove */
static int _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; /* we can ignore errors... */
else if(desktop->icon_cnt == 0)
desktop->icon = NULL; /* ...except when it's not one */
return 0;
}
return 1;
}
/* desktop_show_preferences */
static void _preferences_background(Desktop * desktop, GtkWidget * notebook);
static void _preferences_icons(Desktop * desktop, GtkWidget * notebook);
static void _preferences_monitors(Desktop * desktop, GtkWidget * notebook);
static void _preferences_set(Desktop * desktop);
static gboolean _on_preferences_closex(gpointer data);
static void _on_preferences_monitors_changed(gpointer data);
static void _on_preferences_monitors_refresh(gpointer data);
static void _on_preferences_response(GtkWidget * widget, gint response,
gpointer data);
static void _on_preferences_ok(gpointer data);
static void _on_preferences_apply(gpointer data);
static void _on_preferences_cancel(gpointer data);
static void _on_preferences_update_preview(gpointer data);
static void _desktop_show_preferences(Desktop * desktop)
{
GtkWidget * vbox;
GtkWidget * notebook;
if(desktop->menu != NULL)
gtk_widget_destroy(desktop->menu);
desktop->menu = NULL;
if(desktop->pr_window != NULL)
{
gtk_window_present(GTK_WINDOW(desktop->pr_window));
return;
}
/* window */
desktop->pr_window = gtk_dialog_new_with_buttons(
_("Desktop preferences"), NULL,
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
GTK_STOCK_OK, GTK_RESPONSE_OK, NULL);
gtk_window_set_position(GTK_WINDOW(desktop->pr_window),
GTK_WIN_POS_CENTER_ALWAYS);
g_signal_connect_swapped(G_OBJECT(desktop->pr_window), "delete-event",
G_CALLBACK(_on_preferences_closex), desktop);
g_signal_connect(G_OBJECT(desktop->pr_window), "response", G_CALLBACK(
_on_preferences_response), desktop);
#if GTK_CHECK_VERSION(2, 14, 0)
vbox = gtk_dialog_get_content_area(GTK_DIALOG(desktop->pr_window));
#else
vbox = GTK_DIALOG(desktop->pr_window)->vbox;
#endif
/* notebook */
notebook = gtk_notebook_new();
_preferences_background(desktop, notebook);
_preferences_icons(desktop, notebook);
_preferences_monitors(desktop, notebook);
_on_preferences_monitors_refresh(desktop);
gtk_box_pack_start(GTK_BOX(vbox), notebook, TRUE, TRUE, 0);
/* container */
_preferences_set(desktop);
gtk_widget_show_all(desktop->pr_window);
}
static void _preferences_background(Desktop * desktop, GtkWidget * notebook)
{
GtkSizeGroup * group;
GtkWidget * vbox2;
GtkWidget * hbox;
GtkWidget * label;
GtkFileFilter * filter;
vbox2 = gtk_vbox_new(FALSE, 4);
gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Default color: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_color = gtk_color_button_new();
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_color, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Wallpaper: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_background = gtk_file_chooser_button_new(_("Background"),
GTK_FILE_CHOOSER_ACTION_OPEN);
filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, _("Picture files"));
gtk_file_filter_add_mime_type(filter, "image/bmp");
gtk_file_filter_add_mime_type(filter, "image/gif");
gtk_file_filter_add_mime_type(filter, "image/jpeg");
gtk_file_filter_add_mime_type(filter, "image/pbm");
gtk_file_filter_add_mime_type(filter, "image/png");
gtk_file_filter_add_mime_type(filter, "image/svg+xml");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(desktop->pr_background),
filter);
filter = gtk_file_filter_new();
gtk_file_filter_set_name(filter, _("All files"));
gtk_file_filter_add_pattern(filter, "*");
gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(desktop->pr_background),
filter);
gtk_file_chooser_set_preview_widget(GTK_FILE_CHOOSER(
desktop->pr_background), gtk_image_new());
g_signal_connect_swapped(desktop->pr_background, "update-preview",
G_CALLBACK(_on_preferences_update_preview), desktop);
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_background, TRUE, TRUE,
0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Position: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_background_how = gtk_combo_box_new_text();
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
_("Do not draw"));
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
_("Centered"));
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
_("Scaled"));
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
_("Scaled (keep ratio)"));
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_background_how),
_("Tiled"));
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_background_how, TRUE,
TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
desktop->pr_background_extend = gtk_check_button_new_with_mnemonic(
_("E_xtend background to all monitors"));
gtk_box_pack_start(GTK_BOX(vbox2), desktop->pr_background_extend, FALSE,
TRUE, 0);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(
_("Background")));
}
static void _preferences_icons(Desktop * desktop, GtkWidget * notebook)
{
GtkSizeGroup * group;
GtkWidget * vbox2;
GtkWidget * hbox;
GtkWidget * label;
size_t i;
vbox2 = gtk_vbox_new(FALSE, 4);
gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
/* icons */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Layout: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_ilayout = gtk_combo_box_new_text();
for(i = 0; i < sizeof(_desktop_icons) / sizeof(*_desktop_icons);
i++)
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_ilayout),
_(_desktop_icons[i]));
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_ilayout, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
/* monitor */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Monitor: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_imonitor = gtk_combo_box_new_text();
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_imonitor, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
/* background color */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Background color: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_ibcolor = gtk_color_button_new();
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_ibcolor, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
/* foreground color */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Foreground color: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_ifcolor = gtk_color_button_new();
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_ifcolor, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
/* font */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Font: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_ifont = gtk_font_button_new();
gtk_font_button_set_use_font(GTK_FONT_BUTTON(desktop->pr_ifont), TRUE);
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_ifont, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(
_("Icons")));
}
static void _preferences_monitors(Desktop * desktop, GtkWidget * notebook)
{
GtkSizeGroup * group;
GtkWidget * vbox2;
GtkWidget * hbox;
GtkWidget * label;
GtkWidget * widget;
group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
vbox2 = gtk_vbox_new(FALSE, 4);
gtk_container_set_border_width(GTK_CONTAINER(vbox2), 4);
/* selector */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Monitor: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_monitors = gtk_combo_box_new_text();
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_monitors, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
/* geometry */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Resolution: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_monitors_res = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(desktop->pr_monitors_res), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_monitors_res, TRUE, TRUE,
0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
/* size */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(_("Size: "));
gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
desktop->pr_monitors_size = gtk_label_new(NULL);
gtk_misc_set_alignment(GTK_MISC(desktop->pr_monitors_size), 0.0, 0.5);
gtk_box_pack_start(GTK_BOX(hbox), desktop->pr_monitors_size, TRUE, TRUE,
0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
/* refresh */
hbox = gtk_hbox_new(FALSE, 0);
label = gtk_label_new(NULL);
gtk_size_group_add_widget(group, label);
gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, TRUE, 0);
widget = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
g_signal_connect_swapped(widget, "clicked", G_CALLBACK(
_on_preferences_monitors_refresh), desktop);
gtk_box_pack_start(GTK_BOX(hbox), widget, FALSE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox2), hbox, FALSE, TRUE, 0);
/* updates */
g_signal_connect_swapped(desktop->pr_monitors, "changed", G_CALLBACK(
_on_preferences_monitors_changed), desktop);
gtk_notebook_append_page(GTK_NOTEBOOK(notebook), vbox2, gtk_label_new(
_("Monitors")));
}
static gboolean _on_preferences_closex(gpointer data)
{
_on_preferences_cancel(data);
return TRUE;
}
static void _on_preferences_monitors_changed(gpointer data)
{
Desktop * desktop = data;
gint active;
GdkRectangle geometry;
gint width;
gint height;
char buf[64];
active = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_monitors));
geometry.x = 0;
geometry.y = 0;
geometry.width = gdk_screen_get_width(desktop->screen);
geometry.height = gdk_screen_get_height(desktop->screen);
width = gdk_screen_get_width_mm(desktop->screen);
height = gdk_screen_get_height_mm(desktop->screen);
#if GTK_CHECK_VERSION(2, 14, 0)
if(active-- > 0)
{
gdk_screen_get_monitor_geometry(desktop->screen, active,
&geometry);
width = gdk_screen_get_monitor_width_mm(desktop->screen,
active);
height = gdk_screen_get_monitor_height_mm(desktop->screen,
active);
}
#endif
snprintf(buf, sizeof(buf), _("%dx%d (at %d,%d)"), geometry.width,
geometry.height, geometry.x, geometry.y);
gtk_label_set_text(GTK_LABEL(desktop->pr_monitors_res), buf);
snprintf(buf, sizeof(buf), _("%dx%d mm (%.0lfx%.0lf DPI)"), width,
height, geometry.width * 25.4 / width,
geometry.height * 25.4 / height);
gtk_label_set_text(GTK_LABEL(desktop->pr_monitors_size), buf);
}
static void _on_preferences_monitors_refresh(gpointer data)
{
Desktop * desktop = data;
GtkTreeModel * model1;
GtkTreeModel * model2;
gint active;
#if GTK_CHECK_VERSION(2, 14, 0)
gint n;
gint i;
char * name;
char buf[32];
#endif
active = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_imonitor));
model1 = gtk_combo_box_get_model(GTK_COMBO_BOX(desktop->pr_imonitor));
model2 = gtk_combo_box_get_model(GTK_COMBO_BOX(desktop->pr_monitors));
gtk_list_store_clear(GTK_LIST_STORE(model1));
gtk_list_store_clear(GTK_LIST_STORE(model2));
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_imonitor),
_("Default monitor"));
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_monitors),
_("Whole screen"));
#if GTK_CHECK_VERSION(2, 14, 0)
n = gdk_screen_get_n_monitors(desktop->screen);
for(i = 0; i < n; i++)
{
snprintf(buf, sizeof(buf), _("Monitor %d"), i);
name = gdk_screen_get_monitor_plug_name(desktop->screen, i);
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_imonitor),
(name != NULL) ? name : buf);
gtk_combo_box_append_text(GTK_COMBO_BOX(desktop->pr_monitors),
(name != NULL) ? name : buf);
g_free(name);
}
#endif
gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_imonitor), active);
gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_monitors), 0);
}
static void _on_preferences_response(GtkWidget * widget, gint response,
gpointer data)
{
if(response == GTK_RESPONSE_OK)
_on_preferences_ok(data);
else if(response == GTK_RESPONSE_APPLY)
_on_preferences_apply(data);
else if(response == GTK_RESPONSE_CANCEL)
_on_preferences_cancel(data);
}
static void _on_preferences_ok(gpointer data)
{
Desktop * desktop = data;
gtk_widget_hide(desktop->pr_window);
_on_preferences_apply(data);
}
static void _on_preferences_apply(gpointer data)
{
Desktop * desktop = data;
Config * config;
GdkColor color;
char * p;
char const * q;
int i;
char buf[12];
/* XXX not very efficient */
g_idle_add(_new_idle, desktop);
if((config = _desktop_get_config(desktop)) == NULL)
return;
/* background */
p = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(
desktop->pr_background));
config_set(config, "background", "wallpaper", p);
g_free(p);
gtk_color_button_get_color(GTK_COLOR_BUTTON(desktop->pr_color), &color);
p = gdk_color_to_string(&color);
config_set(config, "background", "color", p);
g_free(p);
i = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_background_how));
if(i >= 0 && i < DESKTOP_HOW_COUNT)
config_set(config, "background", "how", _desktop_hows[i]);
p = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
desktop->pr_background_extend)) ? "1" : "0";
config_set(config, "background", "extend", p);
/* icons */
i = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_ilayout));
if(i >= 0 && i < DESKTOP_ICONS_COUNT)
config_set(config, "icons", "layout", _desktop_icons_config[i]);
desktop->prefs.icons = i; /* applied by _new_idle() */
gtk_color_button_get_color(GTK_COLOR_BUTTON(desktop->pr_ibcolor),
&color);
p = gdk_color_to_string(&color);
config_set(config, "icons", "background", p);
g_free(p);
gtk_color_button_get_color(GTK_COLOR_BUTTON(desktop->pr_ifcolor),
&color);
p = gdk_color_to_string(&color);
config_set(config, "icons", "foreground", p);
g_free(p);
q = gtk_font_button_get_font_name(GTK_FONT_BUTTON(desktop->pr_ifont));
config_set(config, "icons", "font", q);
i = gtk_combo_box_get_active(GTK_COMBO_BOX(desktop->pr_imonitor));
snprintf(buf, sizeof(buf), "%d", i - 1);
config_set(config, "icons", "monitor", buf);
desktop->prefs.monitor = i - 1;
/* XXX code duplication */
if((p = string_new_append(desktop->home, "/" DESKTOPRC, NULL)) != NULL)
{
config_save(config, p);
string_delete(p);
}
config_delete(config);
}
static void _on_preferences_cancel(gpointer data)
{
Desktop * desktop = data;
gtk_widget_hide(desktop->pr_window);
_preferences_set(desktop);
}
static void _on_preferences_update_preview(gpointer data)
{
Desktop * desktop = data;
#if !GTK_CHECK_VERSION(2, 6, 0)
gint ratio = desktop->window.width / desktop->window.height;
#endif
GtkFileChooser * chooser = GTK_FILE_CHOOSER(desktop->pr_background);
GtkWidget * widget;
char * filename;
GdkPixbuf * pixbuf;
gboolean active = FALSE;
GError * error = NULL;
widget = gtk_file_chooser_get_preview_widget(chooser);
if((filename = gtk_file_chooser_get_preview_filename(chooser)) != NULL)
{
#if GTK_CHECK_VERSION(2, 6, 0)
pixbuf = gdk_pixbuf_new_from_file_at_scale(filename, 96, -1,
TRUE, &error);
#else
pixbuf = gdk_pixbuf_new_from_file_at_size(filename, 96,
96 / ratio, &error);
#endif
if(pixbuf != NULL)
{
gtk_image_set_from_pixbuf(GTK_IMAGE(widget), pixbuf);
g_object_unref(pixbuf);
active = TRUE;
}
else if(error != NULL)
{
desktop_error(NULL, error->message, 1);
g_error_free(error);
}
}
g_free(filename);
gtk_file_chooser_set_preview_widget_active(chooser, active);
}
static void _preferences_set(Desktop * desktop)
{
Config * config;
String const * p;
String * q;
String const * filename = NULL;
GdkColor color = { 0, 0, 0, 0 };
const char black[] = "#000000000000";
const char white[] = "#ffffffffffff";
int how;
gboolean extend = FALSE;
size_t i;
int j;
/* FIXME:
* - cache the current configuration within the Desktop class
* - apply the configuration values to the widgets
* - no longer prepend an empty line in configuration files when there
* are no values in the default section */
if((config = _desktop_get_config(desktop)) != NULL)
{
/* background */
filename = config_get(config, "background", "wallpaper");
if((p = config_get(config, "background", "color")) != NULL
&& gdk_color_parse(p, &color) == TRUE)
gtk_color_button_set_color(GTK_COLOR_BUTTON(
desktop->pr_color), &color);
how = 0;
if((p = config_get(config, "background", "how")) != NULL)
for(i = 0; i < DESKTOP_HOW_COUNT; i++)
if(strcmp(_desktop_hows[i], p) == 0)
{
how = i;
break;
}
gtk_combo_box_set_active(GTK_COMBO_BOX(
desktop->pr_background_how), how);
if((p = config_get(config, "background", "extend")) != NULL)
extend = strtol(p, NULL, 10) ? TRUE : FALSE;
/* icons */
how = DESKTOP_ICONS_FILES;
if((p = config_get(config, "icons", "layout")) != NULL)
for(i = 0; i < DESKTOP_ICONS_COUNT; i++)
if(strcmp(_desktop_icons_config[i], p) == 0)
{
how = i;
break;
}
gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_ilayout),
how);
if((p = config_get(config, "icons", "background")) == NULL)
p = black;
if(gdk_color_parse(p, &color) == TRUE)
gtk_color_button_set_color(GTK_COLOR_BUTTON(
desktop->pr_ibcolor), &color);
if((p = config_get(config, "icons", "foreground")) == NULL)
p = white;
if(gdk_color_parse(p, &color) == TRUE)
gtk_color_button_set_color(GTK_COLOR_BUTTON(
desktop->pr_ifcolor), &color);
if((p = config_get(config, "icons", "font")) != NULL)
gtk_font_button_set_font_name(GTK_FONT_BUTTON(
desktop->pr_ifont), p);
else if((q = pango_font_description_to_string(desktop->font))
!= NULL)
{
gtk_font_button_set_font_name(GTK_FONT_BUTTON(
desktop->pr_ifont), q);
g_free(q);
}
if((p = config_get(config, "icons", "monitor")) != NULL)
{
j = strtol(p, &q, 10);
if(p[0] == '\0' || *q != '\0' || j < 0)
j = -1;
gtk_combo_box_set_active(GTK_COMBO_BOX(
desktop->pr_imonitor), j + 1);
}
config_delete(config);
}
else
{
gtk_combo_box_set_active(GTK_COMBO_BOX(
desktop->pr_background_how), 0);
gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_ilayout),
DESKTOP_ICONS_FILES);
gtk_combo_box_set_active(GTK_COMBO_BOX(desktop->pr_imonitor),
0);
}
if(filename != NULL)
gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(
desktop->pr_background), filename);
else
gtk_file_chooser_unselect_all(GTK_FILE_CHOOSER(
desktop->pr_background));
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(
desktop->pr_background_extend), extend);
}
/* usage */
static int _usage(void)
{
fputs(_("Usage: desktop [-H|-V][-a|-c|-f|-h|-n][-m monitor]\n"
" -H Place icons horizontally\n"
" -V Place icons vertically\n"
" -a Display the applications registered\n"
" -c Sort the applications registered by category\n"
" -f Display contents of the desktop folder (default)\n"
" -h Display the homescreen\n"
" -m Monitor where to display the desktop\n"
" -n Do not display icons on the desktop\n"), stderr);
return 1;
}
/* main */
int main(int argc, char * argv[])
{
int o;
Desktop * desktop;
DesktopPrefs prefs;
char * p;
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
prefs.alignment = -1;
prefs.icons = -1;
prefs.monitor = -1;
gtk_init(&argc, &argv);
while((o = getopt(argc, argv, "HVacfhm:n")) != -1)
switch(o)
{
case 'H':
prefs.alignment = DESKTOP_ALIGNMENT_HORIZONTAL;
break;
case 'V':
prefs.alignment = DESKTOP_ALIGNMENT_VERTICAL;
break;
case 'a':
prefs.icons = DESKTOP_ICONS_APPLICATIONS;
break;
case 'c':
prefs.icons = DESKTOP_ICONS_CATEGORIES;
break;
case 'f':
prefs.icons = DESKTOP_ICONS_FILES;
break;
case 'h':
prefs.icons = DESKTOP_ICONS_HOMESCREEN;
break;
case 'm':
prefs.monitor = strtol(optarg, &p, 0);
if(optarg[0] == '\0' || *p != '\0')
return _usage();
break;
case 'n':
prefs.icons = DESKTOP_ICONS_NONE;
break;
default:
return _usage();
}
if(optind < argc)
return _usage();
if((desktop = desktop_new(&prefs)) == NULL)
{
gtk_main();
return 2;
}
gtk_main();
desktop_delete(desktop);
return 0;
}