ede/ede-desktop/ede-desktop.cpp

1184 lines
27 KiB
C++
Raw Normal View History

/*
* $Id$
*
* ede-desktop, desktop and icon manager
* Part of Equinox Desktop Environment (EDE).
* Copyright (c) 2006-2008 EDE Authors.
*
* This program is licensed under terms of the
* GNU General Public License version 2 or newer.
* See COPYING for details.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <unistd.h> // sleep
#include <signal.h>
#include <stdlib.h> // rand, srand
#include <time.h> // time
#include <FL/Fl.H>
#include <FL/x.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Box.H>
#include <FL/Fl_Shared_Image.H>
#include <FL/Fl_Menu_Button.H>
#include <FL/fl_ask.H>
2007-05-22 18:53:17 +04:00
#include <edelib/Debug.h>
#include <edelib/File.h>
#include <edelib/DesktopFile.h>
2007-05-22 18:53:17 +04:00
#include <edelib/Directory.h>
#include <edelib/DirWatch.h>
#include <edelib/MimeType.h>
2007-05-22 18:53:17 +04:00
#include <edelib/StrUtil.h>
#include <edelib/IconTheme.h>
#include <edelib/Run.h>
#include <edelib/Util.h>
#include <edelib/Nls.h>
2007-05-22 18:53:17 +04:00
#include "ede-desktop.h"
#include "DesktopIcon.h"
#include "Utils.h"
#include "Wallpaper.h"
#define EDE_DESKTOP_UID 0x10
#define CONFIG_NAME "ede-desktop"
#define ICONS_CONFIG_NAME "ede-desktop-icons"
#define EDE_DESKTOP_INTERFACE "org.equinoxproject.Desktop"
#define EDE_DESKTOP_OBJECT "/org/equinoxproject/Desktop"
#define SELECTION_SINGLE (Fl::event_button() == 1)
#define SELECTION_MULTI (Fl::event_button() == 1 && (Fl::event_key(FL_Shift_L) || Fl::event_key(FL_Shift_R)))
#undef MIN
2007-05-23 16:43:50 +04:00
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#undef MAX
2007-05-23 16:43:50 +04:00
#define MAX(x,y) ((x) > (y) ? (x) : (y))
/*
* Which widgets Fl::belowmouse() should skip. This should be updated
* when new non-icon child is added to Desktop window.
*/
#define NOT_SELECTABLE(widget) ((widget == this) || (widget == wallpaper) || (widget == dmenu))
void background_conf_cb(Fl_Widget*, void*);
void icons_conf_cb(Fl_Widget*, void*);
Fl_Menu_Item desktop_menu[] = {
{_(" &New desktop item "), 0, 0, 0, FL_SUBMENU},
{_(" &Application... "), 0, 0},
{_(" &Directory... "), 0, 0},
{_(" &URL... "), 0, 0},
{_(" &Link... "), 0, 0},
{0},
{_(" &Arrange "), 0, 0, 0, FL_SUBMENU | FL_MENU_DIVIDER},
{_(" &Vertical line up "), 0, 0},
{_(" &Horizontal line up "), 0, 0},
{0},
{_(" &Copy "), 0, 0},
{_(" &Paste "), 0, 0, 0, FL_MENU_DIVIDER},
{_(" &Icons settings... "), 0, icons_conf_cb, 0},
{_(" &Background... "), 0, background_conf_cb, 0},
{0}
};
Desktop* Desktop::pinstance = NULL;
static bool running = false;
2007-05-22 18:53:17 +04:00
inline unsigned int random_pos(int max) {
return (rand() % max);
}
2007-05-23 16:43:50 +04:00
inline bool intersects(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
return (MAX(x1, x2) <= MIN(w1, w2) &&
MAX(y1, y2) <= MIN(h1, h2));
}
// assume fl_open_display() is called before
2007-05-22 18:53:17 +04:00
inline void dpy_sizes(int& width, int& height) {
width = DisplayWidth(fl_display, fl_screen);
height = DisplayHeight(fl_display, fl_screen);
2007-05-22 18:53:17 +04:00
}
void exit_signal(int signum) {
E_DEBUG(E_STRLOC ": Exiting (got signal %d)\n", signum);
2007-05-22 18:53:17 +04:00
running = false;
}
void restart_signal(int signum) {
E_DEBUG(E_STRLOC ": Restarting (got signal %d)\n", signum);
}
void dir_watch_cb(const char* dir, const char* changed, int flags, void* data) {
Desktop::instance()->dir_watch(dir, changed, flags);
}
void settings_changed_cb(void* data) {
Desktop::instance()->read_config();
Desktop::instance()->redraw();
}
void background_conf_cb(Fl_Widget*, void*) {
Desktop::instance()->execute("ede-desktop-conf");
}
void icons_conf_cb(Fl_Widget*, void*) {
Desktop::instance()->execute("ede-desktop-conf --icons");
}
int desktop_xmessage_handler(int event) {
if(fl_xevent->type == PropertyNotify) {
if(fl_xevent->xproperty.atom == _XA_NET_CURRENT_DESKTOP) {
Desktop::instance()->notify_desktop_changed();
return 1;
}
if(fl_xevent->xproperty.atom == _XA_NET_WORKAREA) {
Desktop::instance()->update_workarea();
return 1;
}
}
return 0;
}
Desktop::Desktop() : DESKTOP_WINDOW(0, 0, 100, 100, "") {
selection_x = selection_y = 0;
moving = false;
do_dirwatch = true;
2007-05-22 18:53:17 +04:00
#ifdef USE_EDELIB_WINDOW
DESKTOP_WINDOW::init();
settings_uid(EDE_DESKTOP_UID);
settings_callback(settings_changed_cb);
//DESKTOP_WINDOW::single_buffer(true);
#endif
selbox = new SelectionOverlay;
selbox->x = selbox->y = selbox->w = selbox->h = 0;
selbox->show = false;
gisett = new GlobalIconSettings;
// gnome light blue
gisett->label_background = 138;
gisett->label_foreground = FL_WHITE;
gisett->label_fontsize = 12;
gisett->label_maxwidth = 75;
gisett->label_transparent = false;
gisett->label_draw = true;
gisett->one_click_exec = false;
gisett->auto_arrange = true;
dsett = new DesktopSettings;
dsett->color = FL_GRAY;
dsett->wp_use = false;
}
Desktop::~Desktop() {
E_DEBUG("Desktop::~Desktop()\n");
2007-05-22 18:53:17 +04:00
save_icons_positions();
delete gisett;
delete dsett;
delete selbox;
delete dbus;
edelib::DirWatch::shutdown();
}
void Desktop::init_internals(void) {
2008-10-03 14:14:57 +04:00
edelib::String p;
init_atoms();
2007-05-22 18:53:17 +04:00
update_workarea();
/*
2008-10-03 14:14:57 +04:00
* NOTE: order how childs are added is important. First non iconable ones should be
* added (wallpaper, menu, ...) then icons, so they can be drawn at top of them.
*/
2007-05-22 18:53:17 +04:00
begin();
dmenu = new Fl_Menu_Button(0, 0, 0, 0);
dmenu->menu(desktop_menu);
wallpaper = new Wallpaper(0, 0, w(), h());
2007-05-22 18:53:17 +04:00
end();
set_bg_color(dsett->color, false);
dbus = new edelib::EdbusConnection();
if(!dbus->connect(edelib::EDBUS_SESSION)) {
E_WARNING("Unable to connecto to session bus. Disabling dbus interface...\n");
delete dbus;
dbus = NULL;
}
2008-10-03 14:14:57 +04:00
// read main config
read_config();
2008-10-03 14:14:57 +04:00
// now try to load icons, looking inside ~/Desktop directory
p = edelib::dir_home();
edelib::String desktop_path = edelib::build_filename(p.c_str(), "Desktop");
2008-10-03 14:14:57 +04:00
// setup watcher used for Desktop and Trash directories
edelib::DirWatch::init();
if(edelib::dir_exists(desktop_path.c_str())) {
load_icons(desktop_path.c_str());
if(!edelib::DirWatch::add(desktop_path.c_str(),
2008-10-03 14:14:57 +04:00
edelib::DW_CREATE | edelib::DW_MODIFY | edelib::DW_RENAME | edelib::DW_DELETE))
{
E_WARNING(E_STRLOC ": Unable to watch %s\n", desktop_path.c_str());
}
}
/*
* For watching Trash, we will check Trash/files only with create/delete flags and
* in callback will be checked if directory is empty (trash empty) or not (trash full).
*
* FIXME: at startup it should be checked is Trash empty and update icons for that
*/
2008-10-03 14:14:57 +04:00
p = edelib::user_data_dir();
trash_path = edelib::build_filename(p.c_str(), "Trash/files");
if(edelib::dir_exists(trash_path.c_str())) {
if(!edelib::DirWatch::add(trash_path.c_str(), edelib::DW_CREATE | edelib::DW_DELETE))
E_WARNING(E_STRLOC ": Unable to watch %s\n", trash_path.c_str());
}
edelib::DirWatch::callback(dir_watch_cb);
running = true;
}
void Desktop::init(void) {
if(Desktop::pinstance != NULL)
return;
Desktop::pinstance = new Desktop();
Desktop::pinstance->init_internals();
}
void Desktop::shutdown(void) {
if(Desktop::pinstance == NULL)
return;
delete Desktop::pinstance;
Desktop::pinstance = NULL;
}
Desktop* Desktop::instance(void) {
E_ASSERT(Desktop::pinstance != NULL && "Desktop::init() should be run first");
return Desktop::pinstance;
}
/*
* This function must be overriden so window can inform
* wm to see it as desktop. It will send data when window
* is created, but before is shown.
*/
void Desktop::show(void) {
if(!shown()) {
Fl_X::make_xid(this);
net_make_me_desktop(this);
}
2007-05-22 18:53:17 +04:00
}
/*
* If someone intentionaly hide desktop
* then quit from it.
*/
void Desktop::hide(void) {
running = false;
}
void Desktop::update_workarea(void) {
int X, Y, W, H;
if(!net_get_workarea(X, Y, W, H)) {
E_WARNING(E_STRLOC ": wm does not support _NET_WM_WORKAREA; using screen sizes...\n");
X = Y = 0;
dpy_sizes(W, H);
}
resize(X, Y, W, H);
}
void Desktop::set_bg_color(int c, bool do_redraw) {
if(color() == c)
return;
dsett->color = c;
color(c);
if(do_redraw)
redraw();
}
2007-05-22 18:53:17 +04:00
void Desktop::read_config(void) {
edelib::Resource conf;
2007-05-22 18:53:17 +04:00
if(!conf.load(CONFIG_NAME)) {
E_WARNING(E_STRLOC ": Can't load %s, using default...\n", CONFIG_NAME);
2007-05-22 18:53:17 +04:00
return;
}
2007-05-22 18:53:17 +04:00
/*
* TODO:
* Add IconArea[X,Y,W,H] so icons can live
* inside that area only (aka margins).
*/
int default_bg_color = FL_BLUE;
int default_wp_use = false;
char wpath[256];
conf.get("Desktop", "color", dsett->color, default_bg_color);
conf.get("Desktop", "wallpaper_use", dsett->wp_use, default_wp_use);
conf.get("Desktop", "wallpaper", wpath, sizeof(wpath));
// keep path but disable wallpaper if file does not exists
if(!edelib::file_exists(wpath)) {
E_DEBUG(E_STRLOC ": %s as wallpaper does not exists\n", wpath);
dsett->wp_use = false;
2007-05-22 18:53:17 +04:00
}
// '138' is gnome light blue
conf.get("Icons", "label_background", gisett->label_background, 138);
conf.get("Icons", "label_foreground", gisett->label_foreground, FL_WHITE);
conf.get("Icons", "label_fontsize", gisett->label_fontsize, 12);
conf.get("Icons", "label_maxwidth", gisett->label_maxwidth, 75);
conf.get("Icons", "label_transparent",gisett->label_transparent, false);
conf.get("Icons", "label_visible", gisett->label_draw, true);
conf.get("Icons", "one_click_exec", gisett->one_click_exec, false);
conf.get("Icons", "auto_arrange", gisett->auto_arrange, true);
2007-05-22 18:53:17 +04:00
if(dsett->wp_use)
wallpaper->set(wpath);
}
void Desktop::load_icons(const char* path) {
E_ASSERT(path != NULL);
edelib::Resource conf, *conf_ptr = NULL;
if(!conf.load(ICONS_CONFIG_NAME))
E_WARNING(E_STRLOC ": Can't load icons positions from %s, randomizing them...\n", ICONS_CONFIG_NAME);
else
conf_ptr = &conf;
StringList lst;
// list with full path; icon basename is extracted in add_icon_by_path()
if(!dir_list(path, lst, true)) {
E_DEBUG(E_STRLOC ": Can't read %s\n", path);
2007-05-22 18:53:17 +04:00
return;
}
StringListIter it, it_end;
for(it = lst.begin(), it_end = lst.end(); it != it_end; ++it)
add_icon_by_path((*it).c_str(), conf_ptr);
}
bool Desktop::add_icon_by_path(const char* path, edelib::Resource* conf) {
E_ASSERT(path != NULL);
IconSettings is;
bool can_add = false;
const char* base = get_basename(path);
/*
* see is possible .desktop file; icon, name fields are filled from read_desktop_file()
*
* FIXME: maybe the good thing is to use MimeType to check .desktop;
* extension does not have to be always present, or .desktop does not have to be
* Desktop File at all
*/
if(edelib::str_ends(path, ".desktop")) {
if(read_desktop_file(path, is))
can_add = true;
} else {
edelib::MimeType mt;
// then try to figure out it's mime; if fails, ignore it
if(mt.set(path)) {
E_DEBUG(E_STRLOC ": Loading icon as mime-type %s\n", mt.icon_name().c_str());
is.icon = mt.icon_name();
// icon label path's basename
is.name = base;
is.type = ICON_FILE;
can_add = true;
} else {
E_DEBUG(E_STRLOC ": Failed mime-type for %s, ignoring...\n", path);
can_add = false;
}
}
if(can_add) {
/*
* key_name is section in config file with icon X/Y values
* FIXME: this should be named 'section_name'
*/
is.key_name = base;
// random_pos() is used if X/Y keys are not found
int icon_x = random_pos(w() - 10);
int icon_y = random_pos(w() - 10);
if(conf) {
// we load positions from used ede-desktop-icos.conf only
conf->get(base, "X", icon_x, icon_x, edelib::RES_USER_ONLY);
conf->get(base, "Y", icon_y, icon_y, edelib::RES_USER_ONLY);
}
E_DEBUG(E_STRLOC ": %s found with: %i %i\n", base, icon_x, icon_y);
is.x = icon_x;
is.y = icon_y;
is.full_path = path;
DesktopIcon* dic = new DesktopIcon(gisett, &is, dsett->color);
add_icon(dic);
}
return can_add;
}
void Desktop::save_icons_positions(void) {
edelib::Resource conf;
char* icon_base;
DesktopIconListIter it = icons.begin(), it_end = icons.end();
for(; it != it_end; ++it) {
if((*it)->path().empty())
continue;
icon_base = get_basename((*it)->path().c_str());
conf.set(icon_base, "X", (*it)->x());
conf.set(icon_base, "Y", (*it)->y());
}
if(!conf.save(ICONS_CONFIG_NAME))
E_WARNING(E_STRLOC ": Unable to store icons positions\n");
}
DesktopIcon* Desktop::find_icon_by_path(const char* path) {
E_ASSERT(path != NULL);
if(icons.empty())
return NULL;
DesktopIconListIter it, it_end;
for(it = icons.begin(), it_end = icons.end(); it != it_end; ++it) {
if((*it)->path() == path)
return (*it);
}
return NULL;
}
bool Desktop::remove_icon_by_path(const char* path) {
E_ASSERT(path != NULL);
if(icons.empty())
return false;
DesktopIconListIter it, it_end;
bool found = false;
for(it = icons.begin(), it_end = icons.end(); it != it_end; ++it) {
if((*it)->path() == path) {
found = true;
break;
2007-05-22 18:53:17 +04:00
}
}
if(found) {
DesktopIcon* ic = (*it);
icons.erase(it);
// Fl_Group::remove() does not delete child, just pops it out
remove(ic);
delete ic;
}
return found;
}
// read .desktop files
bool Desktop::read_desktop_file(const char* path, IconSettings& is) {
E_ASSERT(path != NULL);
2007-05-22 18:53:17 +04:00
if(!edelib::file_exists(path)) {
E_DEBUG(E_STRLOC ": %s don't exists\n");
2007-05-22 18:53:17 +04:00
return false;
}
edelib::DesktopFile dconf;
2007-05-22 18:53:17 +04:00
if(!dconf.load(path)) {
E_WARNING(E_STRLOC ": Can't read %s (%s)\n", path, dconf.strerror());
2007-05-22 18:53:17 +04:00
return false;
}
2007-05-22 18:53:17 +04:00
char buff[128];
int buffsz = sizeof(buff);
/*
* Check for 'EmptyIcon' key since via it is determined
2007-05-22 18:53:17 +04:00
* is icon trash type or not (in case of trash, 'Icon' key is used for full trash).
* FIXME: any other way to check for trash icons ???
*/
if(dconf.get("Desktop Entry", "EmptyIcon", buff, buffsz)) {
is.type = ICON_TRASH;
2007-05-22 18:53:17 +04:00
is.icon = buff;
} else
is.type = ICON_NORMAL;
if(!dconf.icon(buff, buffsz)) {
E_WARNING(E_STRLOC ": No Icon key, balling out\n");
2007-05-22 18:53:17 +04:00
return false;
}
if(is.type == ICON_TRASH)
2007-05-22 18:53:17 +04:00
is.icon2 = buff;
else
2007-05-22 18:53:17 +04:00
is.icon = buff;
edelib::DesktopFileType dtype = dconf.type();
if(dtype == edelib::DESK_FILE_TYPE_LINK) {
2007-05-22 18:53:17 +04:00
is.cmd_is_url = true;
dconf.url(buff, buffsz);
}
else {
2007-05-22 18:53:17 +04:00
is.cmd_is_url = false;
dconf.exec(buff, buffsz);
2007-05-22 18:53:17 +04:00
}
is.cmd = buff;
if(!dconf.name(buff, buffsz)) {
E_DEBUG(E_STRLOC ": No Name key\n");
is.name = "(none)";
} else
is.name = buff;
2007-05-22 18:53:17 +04:00
return true;
}
2007-05-22 18:53:17 +04:00
void Desktop::add_icon(DesktopIcon* ic) {
E_ASSERT(ic != NULL);
2007-05-22 18:53:17 +04:00
icons.push_back(ic);
add((Fl_Widget*)ic);
2007-05-22 18:53:17 +04:00
}
void Desktop::unfocus_all(void) {
DesktopIcon* ic;
DesktopIconListIter it, it_end;
for(it = icons.begin(), it_end = icons.end(); it != it_end; ++it) {
ic = (*it);
if(ic->is_focused()) {
ic->do_unfocus();
ic->fast_redraw();
2007-05-22 18:53:17 +04:00
}
}
}
void Desktop::select(DesktopIcon* ic, bool do_redraw) {
E_ASSERT(ic != NULL);
2007-05-22 18:53:17 +04:00
if(in_selection(ic))
return;
2007-05-22 18:53:17 +04:00
selectionbuff.push_back(ic);
2007-05-22 18:53:17 +04:00
if(!ic->is_focused()) {
ic->do_focus();
if(do_redraw)
ic->fast_redraw();
2007-05-22 18:53:17 +04:00
}
2007-05-23 16:43:50 +04:00
}
void Desktop::select_only(DesktopIcon* ic) {
E_ASSERT(ic != NULL);
2007-05-22 18:53:17 +04:00
unfocus_all();
selectionbuff.clear();
selectionbuff.push_back(ic);
2007-05-22 18:53:17 +04:00
ic->do_focus();
ic->fast_redraw();
}
2007-05-22 18:53:17 +04:00
bool Desktop::in_selection(const DesktopIcon* ic) {
E_ASSERT(ic != NULL);
if(selectionbuff.empty())
return false;
DesktopIconListIter it, it_end;
for(it = selectionbuff.begin(), it_end = selectionbuff.end(); it != it_end; ++it) {
if((*it) == ic)
return true;
}
return false;
}
void Desktop::move_selection(int x, int y, bool apply) {
if(selectionbuff.empty())
2007-05-23 16:43:50 +04:00
return;
int prev_x, prev_y, tmp_x, tmp_y;
DesktopIcon* ic;
2007-05-23 16:43:50 +04:00
DesktopIconListIter it, it_end;
for(it = selectionbuff.begin(), it_end = selectionbuff.end(); it != it_end; ++it) {
ic = (*it);
2007-05-23 16:43:50 +04:00
prev_x = ic->drag_icon_x();
prev_y = ic->drag_icon_y();
2007-05-23 16:43:50 +04:00
tmp_x = x - selection_x;
tmp_y = y - selection_y;
2007-05-23 16:43:50 +04:00
ic->drag(prev_x + tmp_x, prev_y + tmp_y, apply);
2007-05-23 16:43:50 +04:00
// very slow if not checked
//if(apply == true)
// ic->fast_redraw();
2007-05-23 16:43:50 +04:00
}
selection_x = x;
selection_y = y;
/*
* Redraw whole screen so it reflects
* new icon position
*/
if(apply)
redraw();
2007-05-23 16:43:50 +04:00
}
/*
* Tries to figure out icon below mouse. It is alternative to
* Fl::belowmouse() since with this we hunt only icons, not other
* childs (wallpaper, menu), which can be returned by Fl::belowmouse()
* and bad things be hapened.
2007-05-23 16:43:50 +04:00
*/
DesktopIcon* Desktop::below_mouse(int px, int py) {
DesktopIcon* ic = NULL;
DesktopIconListIter it, it_end;
for(it = icons.begin(), it_end = icons.end(); it != it_end; ++it) {
ic = (*it);
2007-05-23 16:43:50 +04:00
if(ic->x() < px && ic->y() < py && px < (ic->x() + ic->h()) && py < (ic->y() + ic->h()))
return ic;
}
return NULL;
}
void Desktop::select_in_area(void) {
if(!selbox->show)
return;
int ax = selbox->x;
int ay = selbox->y;
int aw = selbox->w;
int ah = selbox->h;
if(aw < 0) {
ax += aw;
aw = -aw;
} else if(!aw)
aw = 1;
if(ah < 0) {
ay += ah;
ah = -ah;
} else if(!ah)
ah = 1;
/*
* XXX: This function can fail since icon coordinates are absolute (event_x_root)
* but selbox use relative (event_root). It will work as expected if desktop is at x=0 y=0.
* This should be checked further.
*/
DesktopIcon* ic = NULL;
DesktopIconListIter it, it_end;
for(it = icons.begin(), it_end = icons.end(); it != it_end; ++it) {
ic = (*it);
E_ASSERT(ic != NULL && "Impossible to happen");
if(intersects(ax, ay, ax+aw, ay+ah, ic->x(), ic->y(), ic->w()+ic->x(), ic->h()+ic->y())) {
if(!ic->is_focused()) {
ic->do_focus();
// updated from Desktop::draw()
ic->damage(EDAMAGE_CHILD_LABEL);
}
} else {
if(ic->is_focused()) {
ic->do_unfocus();
// updated from Desktop::draw()
ic->fast_redraw();
//ic->damage(EDAMAGE_CHILD_LABEL);
//ic->redraw();
//ic->damage(EDAMAGE_CHILD_LABEL);
}
}
}
}
void Desktop::notify_desktop_changed(void) {
int num = net_get_current_desktop();
if(num == -1)
return;
char** names;
int ret = net_get_workspace_names(names);
if(!ret)
return;
if(num >= ret) {
XFreeStringList(names);
return;
}
if(dbus) {
edelib::EdbusMessage msg;
// send org.equinoxproject.Desktop.DesktopChanged(int32, string) signal
msg.create_signal(EDE_DESKTOP_OBJECT, EDE_DESKTOP_INTERFACE, "DesktopChanged");
msg << num << names[num];
dbus->send(msg);
}
XFreeStringList(names);
}
void Desktop::dnd_drop_source(const char* src, int src_len, int x, int y) {
2007-06-23 15:31:36 +04:00
if(!src)
return;
if(src_len < 1)
return;
char* src_copy = new char[src_len + 1];
int real_len = 0;
// mozilla sends UTF-16 form; for now use this hack untill some utf8 code is ready
for(int i = 0, j = 0; i < src_len; i++) {
if(src[i] != 0) {
src_copy[j++] = src[i];
real_len++;
}
}
src_copy[real_len] = '\0';
if(real_len < 1) {
delete [] src_copy;
return;
}
E_DEBUG(E_STRLOC ": DND on Desktop, got: %s\n", src_copy);
// valid url's (if got) ends with \r\n, clean that
char* pp = strstr(src_copy, "\r\n");
if(pp)
*pp = '\0';
char* sptr = 0;
if((real_len > 7) && (strncmp(src_copy, "file://", 7) == 0))
sptr = src_copy + 7;
else
sptr = src_copy;
if(!edelib::file_exists(sptr) && !edelib::dir_exists(sptr)) {
fl_message("Droping file content is not implemented yet. Soon, soon... :)");
delete [] src_copy;
return;
}
2007-06-23 15:31:36 +04:00
IconSettings is;
is.x = x;
is.y = y;
bool is_read = false;
2007-06-23 15:31:36 +04:00
if(edelib::str_ends(src_copy, ".desktop")) {
edelib::DesktopFile dconf;
2007-06-23 15:31:36 +04:00
edelib::String path = sptr;
edelib::DesktopFile dfile;
if(dfile.load(path.c_str())) {
char buff[256];
if(dfile.type() == edelib::DESK_FILE_TYPE_LINK) {
dfile.url(buff, 256);
is.cmd_is_url = true;
}
else {
dfile.exec(buff, 256);
is.cmd_is_url = false;
}
is.cmd = buff;
is.type = ICON_NORMAL;
dfile.name(buff, 256);
is.name = buff;
dfile.icon(buff, 256);
is.icon = buff;
is_read = true;
}
}
if(!is_read) {
// absolute path is (for now) seen as non-url
if(sptr[0] == '/')
is.cmd_is_url = false;
else
is.cmd_is_url = true;
is.cmd = "(none)";
is.type = ICON_NORMAL;
edelib::MimeType mt;
if(!mt.set(sptr)) {
E_WARNING(E_STRLOC ": MimeType for %s (%s) failed, not dropping icon\n", sptr, src_copy);
delete [] src_copy;
return;
}
is.name = get_basename(sptr);
is.icon = mt.icon_name();
2007-06-23 15:31:36 +04:00
}
DesktopIcon* dic = new DesktopIcon(gisett, &is, color());
2007-06-23 15:31:36 +04:00
add_icon(dic);
delete [] src_copy;
redraw();
2007-06-23 15:31:36 +04:00
}
void Desktop::draw(void) {
if(!damage())
return;
if(damage() & (FL_DAMAGE_ALL | FL_DAMAGE_EXPOSE)) {
/*
* If any overlay was previously visible during full
* redraw, it will not be cleared because of fast flip.
* This will assure that does not happened.
*/
clear_xoverlay();
DESKTOP_WINDOW::draw();
E_DEBUG(E_STRLOC ": REDRAW ALL\n");
}
if(damage() & EDAMAGE_OVERLAY) {
if(selbox->show)
draw_xoverlay(selbox->x, selbox->y, selbox->w, selbox->h);
else
clear_xoverlay();
/*
* now scan all icons and see if they needs redraw, and if do
* just update their label since it is indicator of selection
*/
for(int i = 0; i < children(); i++) {
if(child(i)->damage() == EDAMAGE_CHILD_LABEL) {
update_child(*child(i));
child(i)->clear_damage();
E_DEBUG(E_STRLOC ": ICON REDRAW \n");
}
}
}
clear_damage();
}
void Desktop::dir_watch(const char* dir, const char* changed, int flags) {
if(!do_dirwatch || !changed || flags == edelib::DW_REPORT_NONE)
return;
2008-10-02 16:16:04 +04:00
// skip temporary files
if(is_temp_filename(changed))
return;
if(trash_path == dir) {
bool is_empty = edelib::dir_empty(trash_path.c_str());
DesktopIconListIter it, it_end;
for(it = icons.begin(), it_end = icons.end(); it != it_end; ++it) {
if((*it)->icon_type() == ICON_TRASH) {
if(is_empty)
(*it)->use_icon1();
else
(*it)->use_icon2();
}
}
E_DEBUG(E_STRLOC ": event on trash dir %s\n", dir);
return;
}
sleep(1);
if(flags == edelib::DW_REPORT_CREATE) {
E_DEBUG(E_STRLOC ": adding %s\n", changed);
if(find_icon_by_path(changed)) {
E_DEBUG(E_STRLOC ": %s already registered; skipping...\n", changed);
return;
}
if(add_icon_by_path(changed, 0))
redraw();
} else if(flags == edelib::DW_REPORT_MODIFY) {
E_DEBUG(E_STRLOC ": modified %s\n", changed);
} else if(flags == edelib::DW_REPORT_DELETE) {
E_DEBUG(E_STRLOC ": deleted %s\n", changed);
if(remove_icon_by_path(changed))
redraw();
} else
E_DEBUG(E_STRLOC ": %s changed with %i\n", changed, flags);
}
void Desktop::execute(const char* cmd) {
E_ASSERT(cmd != NULL);
edelib::run_program(cmd, false);
}
2007-05-22 18:53:17 +04:00
int Desktop::handle(int event) {
switch(event) {
case FL_FOCUS:
case FL_UNFOCUS:
case FL_SHORTCUT:
2007-05-23 16:43:50 +04:00
return 1;
case FL_PUSH: {
2007-05-22 18:53:17 +04:00
/*
* First check where we clicked. If we do it on desktop
* unfocus any possible focused childs, and handle
* specific clicks. Otherwise, do rest for childs.
*/
Fl_Widget* clicked = Fl::belowmouse();
if(NOT_SELECTABLE(clicked)) {
E_DEBUG(E_STRLOC ": DESKTOP CLICK !!!\n");
if(!selectionbuff.empty()) {
/*
* Only focused are in selectionbuff, so this is
* fine to do; also will prevent full redraw when is clicked on desktop
*/
unfocus_all();
2007-05-22 18:53:17 +04:00
selectionbuff.clear();
}
// track position so moving can be deduced
if(Fl::event_button() == 1) {
selbox->x = Fl::event_x();
selbox->y = Fl::event_y();
} else if(Fl::event_button() == 3) {
const Fl_Menu_Item* item = dmenu->menu()->popup(Fl::event_x(), Fl::event_y());
dmenu->picked(item);
}
2007-05-22 18:53:17 +04:00
return 1;
}
2007-05-22 18:53:17 +04:00
// from here, all events are managed for icons
DesktopIcon* tmp_icon = (DesktopIcon*)clicked;
/*
* do no use assertion on this, since
* fltk::belowmouse() can miss our icon
*/
if(!tmp_icon)
return 1;
if(SELECTION_MULTI) {
Fl::event_is_click(0);
2007-05-22 18:53:17 +04:00
select(tmp_icon);
return 1;
} else if(SELECTION_SINGLE) {
2007-06-18 20:46:27 +04:00
if(!in_selection(tmp_icon)) {
/*
* for testing:
* notify_box(tmp_icon->label());
*/
2007-05-22 18:53:17 +04:00
select_only(tmp_icon);
2007-06-18 20:46:27 +04:00
}
} else if(Fl::event_button() == 3) {
2007-05-22 18:53:17 +04:00
select_only(tmp_icon);
/*
* for testing:
* notify_box(tmp_icon->label());
*/
2007-06-18 20:46:27 +04:00
}
2007-05-22 18:53:17 +04:00
/*
* Let child handle the rest.
* Also prevent click on other mouse buttons during move.
*/
if(!moving)
tmp_icon->handle(FL_PUSH);
2007-05-22 18:53:17 +04:00
E_DEBUG(E_STRLOC ": FL_PUSH from desktop\n");
selection_x = Fl::event_x_root();
selection_y = Fl::event_y_root();
2007-05-22 18:53:17 +04:00
return 1;
}
case FL_DRAG:
2007-05-22 18:53:17 +04:00
moving = true;
if(!selectionbuff.empty()) {
E_DEBUG(E_STRLOC ": DRAG icon from desktop\n");
move_selection(Fl::event_x_root(), Fl::event_y_root(), false);
2007-05-22 18:53:17 +04:00
} else {
E_DEBUG(E_STRLOC ": DRAG from desktop\n");
/*
* Moving is started with pushed button.
* From this point selection box is created and is rolled until release
*/
if(selbox->x != 0 || selbox->y != 0) {
selbox->w = Fl::event_x() - selbox->x;
selbox->h = Fl::event_y() - selbox->y;
selbox->show = true;
// see if there some icons inside selection area
select_in_area();
// redraw selection box
damage(EDAMAGE_OVERLAY);
}
2007-05-22 18:53:17 +04:00
}
2007-05-22 18:53:17 +04:00
return 1;
case FL_RELEASE:
E_DEBUG(E_STRLOC ": RELEASE from desktop\n");
E_DEBUG(E_STRLOC ": clicks: %i\n", Fl::event_is_click());
2007-05-22 18:53:17 +04:00
if(selbox->show) {
selbox->x = selbox->y = selbox->w = selbox->h = 0;
selbox->show = false;
damage(EDAMAGE_OVERLAY);
/*
* Now pickup those who are in is_focused() state.
*
* Possible flickers due overlay will be later removed when is
* called move_selection(), which will in turn redraw icons again
* after position them.
*/
if(!selectionbuff.empty())
selectionbuff.clear();
DesktopIconListIter it, it_end;
for(it = icons.begin(), it_end = icons.end(); it != it_end; ++it) {
if((*it)->is_focused())
select((*it), false);
}
return 1;
}
if(!selectionbuff.empty() && moving)
move_selection(Fl::event_x_root(), Fl::event_y_root(), true);
2007-05-22 18:53:17 +04:00
/*
* Do not send FL_RELEASE during move
2007-05-22 18:53:17 +04:00
*
* TODO: should be alowed FL_RELEASE to multiple icons? (aka. run
2007-05-22 18:53:17 +04:00
* command for all selected icons ?
*/
if(selectionbuff.size() == 1 && !moving)
(*selectionbuff.begin())->handle(FL_RELEASE);
2007-05-22 18:53:17 +04:00
moving = false;
return 1;
2007-06-23 15:31:36 +04:00
case FL_DND_ENTER:
case FL_DND_DRAG:
case FL_DND_LEAVE:
return 1;
case FL_DND_RELEASE: {
DesktopIcon* di = below_mouse(Fl::event_x(), Fl::event_y());
if(di)
return di->handle(event);
2007-06-23 15:31:36 +04:00
return 1;
}
case FL_PASTE: {
DesktopIcon* di = below_mouse(Fl::event_x(), Fl::event_y());
if(di)
return di->handle(event);
2007-06-23 15:31:36 +04:00
dnd_drop_source(Fl::event_text(), Fl::event_length(), Fl::event_x(), Fl::event_y());
2007-06-23 15:31:36 +04:00
return 1;
}
case FL_ENTER:
case FL_LEAVE:
case FL_MOVE:
return DESKTOP_WINDOW::handle(event);
2007-05-22 18:53:17 +04:00
default:
break;
}
return 0;
}
2007-05-22 18:53:17 +04:00
int main() {
signal(SIGTERM, exit_signal);
signal(SIGKILL, exit_signal);
signal(SIGINT, exit_signal);
signal(SIGHUP, restart_signal);
2007-05-22 18:53:17 +04:00
srand(time(NULL));
// a lot of preparing code depends on this
fl_open_display();
2007-05-22 18:53:17 +04:00
#ifndef USE_EDELIB_WINDOW
fl_register_images();
edelib::IconTheme::init("edeneu");
#endif
2007-05-22 18:53:17 +04:00
Desktop::init();
Desktop::instance()->show();
2007-05-22 18:53:17 +04:00
/*
* XSelectInput will redirect PropertyNotify messages, which
* are listened for
2007-05-22 18:53:17 +04:00
*/
XSelectInput(fl_display, RootWindow(fl_display, fl_screen), PropertyChangeMask | StructureNotifyMask | ClientMessage);
Fl::add_handler(desktop_xmessage_handler);
2007-05-22 18:53:17 +04:00
while(running)
Fl::wait();
2007-05-22 18:53:17 +04:00
Desktop::shutdown();
#ifndef USE_EDELIB_WINDOW
edelib::IconTheme::shutdown();
#endif
2007-05-22 18:53:17 +04:00
return 0;
}