ede/eiconman/eiconman.cpp

646 lines
15 KiB
C++
Raw Normal View History

/*
* $Id$
*
2007-05-22 18:53:17 +04:00
* Eiconman, desktop and icon manager
* Part of Equinox Desktop Environment (EDE).
2007-05-22 18:53:17 +04:00
* Copyright (c) 2000-2007 EDE Authors.
*
2007-05-22 18:53:17 +04:00
* This program is licensed under terms of the
* GNU General Public License version 2 or newer.
* See COPYING for details.
*/
#include "eiconman.h"
2007-05-22 18:53:17 +04:00
#include "DesktopIcon.h"
#include "DesktopConfig.h"
#include "Utils.h"
#include <edelib/Nls.h>
#include <edelib/Debug.h>
#include <edelib/Config.h>
#include <edelib/Directory.h>
#include <edelib/StrUtil.h>
#include <edelib/File.h>
#include <edelib/IconTheme.h>
#include <edelib/Item.h>
#include <fltk/Divider.h>
#include <fltk/damage.h>
#include <fltk/Color.h>
#include <fltk/events.h>
#include <fltk/run.h>
2007-05-22 18:53:17 +04:00
#include <fltk/x11.h>
#include <fltk/SharedImage.h>
2007-05-22 18:53:17 +04:00
#include <signal.h>
#include <X11/Xproto.h> // CARD32
2007-05-22 18:53:17 +04:00
#include <stdlib.h> // rand, srand
#include <time.h> // time
2007-05-22 18:53:17 +04:00
/*
* NOTE: DO NOT set 'using namespace fltk' here
* since fltk::Window will collide with Window from X11
* resulting compilation errors.
*
* This is why I hate this namespace shit !
*/
2007-05-22 18:53:17 +04:00
#define CONFIG_NAME "eiconman.conf"
2007-05-22 18:53:17 +04:00
#define SELECTION_SINGLE (fltk::event_button() == 1)
#define SELECTION_MULTI (fltk::event_button() == 1 && \
(fltk::get_key_state(fltk::LeftShiftKey) ||\
fltk::get_key_state(fltk::RightShiftKey)))
2007-05-22 18:53:17 +04:00
bool running = true;
2007-05-22 18:53:17 +04:00
inline unsigned int random_pos(int max) {
return (rand() % max);
}
2007-05-22 18:53:17 +04:00
inline void dpy_sizes(int& width, int& height) {
fltk::open_display();
width = DisplayWidth(fltk::xdisplay, fltk::xscreen);
height = DisplayHeight(fltk::xdisplay, fltk::xscreen);
}
void exit_signal(int signum) {
EDEBUG(ESTRLOC ": Exiting (got signal %d)\n", signum);
running = false;
}
/*
* It is used to notify desktop when _NET_CURRENT_DESKTOP is triggered.
* FIXME: _NET_WORKAREA is nice thing that could set here too :)
*
* FIXME: XInternAtom should be placed somewhere else
*/
int desktop_xmessage_handler(int e, fltk::Window*) {
Atom nd = XInternAtom(fltk::xdisplay, "_NET_CURRENT_DESKTOP", False);
2007-05-22 18:53:17 +04:00
if(fltk::xevent.type == PropertyNotify) {
if(fltk::xevent.xproperty.atom == nd) {
EDEBUG(ESTRLOC ": Desktop changed !!!\n");
return 1;
}
}
2007-05-22 18:53:17 +04:00
return 0;
}
2007-05-22 18:53:17 +04:00
void background_cb(fltk::Widget*, void*) {
DesktopConfig dc;
dc.run();
}
2007-05-22 18:53:17 +04:00
Desktop::Desktop() : fltk::Window(0, 0, 100, 100, "")
{
2007-05-22 18:53:17 +04:00
moving = false;
2007-05-22 20:27:33 +04:00
selbox = new SelectionOverlay;
selbox->x = selbox->y = selbox->w = selbox->h = 0;
selbox->show = false;
2007-05-22 18:53:17 +04:00
// fallback if update_workarea() fails
int dw, dh;
dpy_sizes(dw, dh);
resize(dw, dh);
update_workarea();
read_config();
color(bg_color);
begin();
pmenu = new fltk::PopupMenu(0, 0, 450, 50);
pmenu->begin();
edelib::Item* it = new edelib::Item(_("&New desktop item"));
it->offset_x(12, 12);
new fltk::Divider();
it = new edelib::Item(_("&Line up vertical"));
it->offset_x(12, 12);
it = new edelib::Item(_("&Line up horizontal"));
it->offset_x(12, 12);
it = new edelib::Item(_("&Arrange by name"));
it->offset_x(12, 12);
new fltk::Divider();
it = new edelib::Item(_("&Icon settings"));
it->offset_x(12, 12);
edelib::Item* itbg = new edelib::Item(_("&Background settings"));
itbg->offset_x(12, 12);
itbg->callback(background_cb);
pmenu->end();
pmenu->type(fltk::PopupMenu::POPUP3);
end();
}
2007-05-22 18:53:17 +04:00
Desktop::~Desktop()
{
2007-05-22 18:53:17 +04:00
EDEBUG(ESTRLOC ": Desktop::~Desktop()\n");
2007-05-22 20:27:33 +04:00
if(selbox)
delete selbox;
2007-05-22 18:53:17 +04:00
/*
* icons member deleting is not needed, since add_icon will
* append to icons and to fltk::Group array. Desktop at the end
* will cleanup fltk::Group array.
*/
icons.clear();
}
2007-05-22 18:53:17 +04:00
void Desktop::default_gisett(void) {
// TODO
}
2007-05-22 18:53:17 +04:00
void Desktop::update_workarea(void) {
int X,Y,W,H;
if(net_get_workarea(X, Y, W, H))
resize(X,Y,W,H);
2007-05-22 20:27:33 +04:00
EDEBUG(ESTRLOC ": area: %i %i %i %i\n", X,Y,W,H);
2007-05-22 18:53:17 +04:00
}
2007-05-22 18:53:17 +04:00
void Desktop::read_config(void) {
edelib::Config conf;
if(!conf.load(CONFIG_NAME)) {
EDEBUG(ESTRLOC ": Can't load %s, using default...\n", CONFIG_NAME);
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).
*/
// read Desktop section
conf.get("Desktop", "Color", bg_color, fltk::BLUE);
conf.get("Desktop", "WallpaperUse", wp_use, false);
if(wp_use) {
char wp_path[1024];
conf.get("Desktop", "Wallpaper", wp_path, sizeof(wp_path));
EDEBUG(ESTRLOC ": Wallpaper %s\n", wp_path);
}
2007-05-22 18:53:17 +04:00
// read Icons section
conf.get("Icons", "Label Background", gisett.label_background, 46848);
conf.get("Icons", "Label Foreground", gisett.label_foreground, fltk::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", "Gridspacing", gisett.gridspacing, 16);
conf.get("Icons", "OneClickExec", gisett.one_click_exec, false);
conf.get("Icons", "AutoArrange", gisett.auto_arr, true);
/*
* Now try to load icons, first looking inside ~/Desktop directory
* then inside config since config could contain removed entries
* from $HOME/Desktop
*
* FIXME: dir_exists() can't handle '~/Desktop' ???
*/
//load_icons("/home/sanel/Desktop", conf);
edelib::String dd = edelib::dir_home();
dd += "/Desktop";
load_icons(dd.c_str(), conf);
#if 0
EDEBUG("----------------------------------------------------------\n");
EDEBUG("d Color : %i\n", bg_color);
EDEBUG("d WallpaperUse: %i\n", wp_use);
EDEBUG("i label bkg : %i\n", gisett.label_background);
EDEBUG("i label fg : %i\n", gisett.label_foreground);
EDEBUG("i label fsize : %i\n", gisett.label_fontsize);
EDEBUG("i label maxw : %i\n", gisett.label_maxwidth);
EDEBUG("i label trans : %i\n", gisett.label_transparent);
EDEBUG("i label vis : %i\n", gisett.label_draw);
EDEBUG("i gridspace : %i\n", gisett.gridspacing);
EDEBUG("i oneclick : %i\n", gisett.one_click_exec);
EDEBUG("i auto_arr : %i\n", gisett.auto_arr);
#endif
}
2007-05-22 18:53:17 +04:00
void Desktop::load_icons(const char* path, edelib::Config& conf) {
EASSERT(path != NULL);
2007-05-22 18:53:17 +04:00
if(!edelib::dir_exists(path)) {
EDEBUG(ESTRLOC ": %s does not exists\n", path);
return;
}
2007-05-22 18:53:17 +04:00
edelib::vector<edelib::String> lst;
2007-05-22 18:53:17 +04:00
// list without full path, so we can use name as entry in config file
if(!dir_list(path, lst)) {
EDEBUG(ESTRLOC ": Can't read %s\n", path);
return;
}
2007-05-22 18:53:17 +04:00
const char* name = NULL;
int icon_x = 0;
int icon_y = 0;
edelib::String full_path;
full_path.reserve(256);
2007-05-22 18:53:17 +04:00
for(unsigned int i = 0; i < lst.size(); i++) {
name = lst[i].c_str();
2007-05-22 18:53:17 +04:00
if(edelib::str_ends(name, ".desktop")) {
2007-05-22 18:53:17 +04:00
full_path = path;
full_path += '/';
full_path += name;
2007-05-22 18:53:17 +04:00
IconSettings is;
int icon_type;
2007-05-22 18:53:17 +04:00
if(read_desktop_file(full_path.c_str(), is, icon_type)) {
2007-05-22 18:53:17 +04:00
// store dekstop name for key in saving positions
is.desktop_name = name;
2007-05-22 18:53:17 +04:00
// random_pos() is used if X/Y keys are not found
conf.get(name, "X", icon_x, random_pos(w() - 10));
conf.get(name, "Y", icon_y, random_pos(h() - 10));
2007-05-22 18:53:17 +04:00
EDEBUG(ESTRLOC ": %s found with: %i %i\n", name, icon_x, icon_y);
is.x = icon_x;
is.y = icon_y;
2007-05-22 18:53:17 +04:00
DesktopIcon* dic = new DesktopIcon(&gisett, &is, icon_type);
add_icon(dic);
} else
EDEBUG(ESTRLOC ": Skipped %s section\n", name);
}
}
}
2007-05-22 18:53:17 +04:00
// reads .desktop file content
bool Desktop::read_desktop_file(const char* path, IconSettings& is, int& icon_type) {
EASSERT(path != NULL);
2007-05-22 18:53:17 +04:00
if(!edelib::file_exists(path)) {
EDEBUG(ESTRLOC ": %s don't exists\n");
return false;
}
2007-05-22 18:53:17 +04:00
edelib::Config dconf;
if(!dconf.load(path)) {
EDEBUG(ESTRLOC ": Can't read %s\n", path);
return false;
}
2007-05-22 18:53:17 +04:00
char buff[128];
int buffsz = sizeof(buff);
/*
* First check for 'EmptyIcon' key since via it is determined
* 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)) {
icon_type = ICON_TRASH;
is.icon = buff;
}
2007-05-22 18:53:17 +04:00
if(dconf.error() == edelib::CONF_ERR_SECTION) {
EDEBUG(ESTRLOC ": %s is not valid .desktop file\n");
return false;
}
2007-05-22 18:53:17 +04:00
dconf.get("Desktop Entry", "Icon", buff, buffsz);
2007-05-22 18:53:17 +04:00
if(icon_type == ICON_TRASH)
is.icon2 = buff;
else {
is.icon = buff;
icon_type = ICON_NORMAL;
}
2007-05-22 18:53:17 +04:00
EDEBUG(ESTRLOC ": Icon is: %s\n", is.icon.c_str());
2007-05-22 18:53:17 +04:00
char name[256];
// FIXME: UTF-8 safety
if(dconf.get_localized("Desktop Entry", "Name", name, sizeof(name))) {
EDEBUG(ESTRLOC ": Name is: %s\n", name);
is.name = name;
}
2007-05-22 18:53:17 +04:00
/*
* Specs (desktop entry file) said that Type=Link means there must
* be somewhere URL key. My thoughts is that in this case Exec key
* should be ignored, even if exists. Then I will follow my thoughts.
*/
if(!dconf.get("Desktop Entry", "Type", buff, buffsz)) {
EDEBUG(ESTRLOC ": Missing mandatory Type key\n");
return false;
}
2007-05-22 18:53:17 +04:00
if(strncmp(buff, "Link", 4) == 0) {
is.cmd_is_url = true;
if(!dconf.get("Desktop Entry", "URL", buff, buffsz)) {
EDEBUG(ESTRLOC ": Missing expected URL key\n");
return false;
}
is.cmd = buff;
} else if(strncmp(buff, "Application", 11) == 0) {
is.cmd_is_url = false;
if(!dconf.get("Desktop Entry", "Exec", buff, buffsz)) {
EDEBUG(ESTRLOC ": Missing expected Exec key\n");
return false;
}
is.cmd = buff;
} else if(strncmp(buff, "Directory", 11) == 0) {
EDEBUG(ESTRLOC ": Type = Directory is not implemented yet\n");
return false;
} else {
EDEBUG(ESTRLOC ": Unknown %s type, ignoring...\n", buff);
return false;
}
2007-05-22 18:53:17 +04:00
return true;
}
2007-05-22 18:53:17 +04:00
void Desktop::save_config(void) {
// TODO
}
2007-05-22 18:53:17 +04:00
void Desktop::add_icon(DesktopIcon* ic) {
EASSERT(ic != NULL);
2007-05-22 18:53:17 +04:00
icons.push_back(ic);
add(ic);
}
2007-05-22 18:53:17 +04:00
void Desktop::move_selection(int x, int y, bool apply) {
unsigned int sz = selectionbuff.size();
if(sz == 0)
return;
2007-05-22 18:53:17 +04:00
int prev_x, prev_y, tmp_x, tmp_y;
2007-05-22 18:53:17 +04:00
for(unsigned int i = 0; i < sz; i++) {
prev_x = selectionbuff[i]->drag_icon_x();
prev_y = selectionbuff[i]->drag_icon_y();
2007-05-22 18:53:17 +04:00
tmp_x = x - selection_x;
tmp_y = y - selection_y;
2007-05-22 18:53:17 +04:00
selectionbuff[i]->drag(prev_x+tmp_x, prev_y+tmp_y, apply);
2007-05-22 18:53:17 +04:00
// very slow if is not checked
if(apply == true)
selectionbuff[i]->redraw();
}
2007-05-22 18:53:17 +04:00
selection_x = x;
selection_y = y;
}
2007-05-22 18:53:17 +04:00
void Desktop::unfocus_all(void) {
unsigned int sz = icons.size();
2007-05-22 18:53:17 +04:00
for(unsigned int i = 0; i < sz; i++) {
if(icons[i]->is_focused()) {
icons[i]->do_unfocus();
icons[i]->redraw();
}
}
2007-05-22 18:53:17 +04:00
redraw();
}
2007-05-22 18:53:17 +04:00
bool Desktop::in_selection(const DesktopIcon* ic) {
unsigned int sz = selectionbuff.size();
2007-05-22 18:53:17 +04:00
for(unsigned int i = 0; i < sz; i++) {
if(ic == selectionbuff[i])
return true;
}
return false;
}
2007-05-22 18:53:17 +04:00
void Desktop::select(DesktopIcon* ic) {
EASSERT(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();
ic->redraw();
redraw();
}
}
2007-05-22 18:53:17 +04:00
void Desktop::select_only(DesktopIcon* ic) {
EASSERT(ic != NULL);
2007-05-22 18:53:17 +04:00
unfocus_all();
2007-05-22 18:53:17 +04:00
selectionbuff.clear();
selectionbuff.push_back(ic);
2007-05-22 18:53:17 +04:00
ic->do_focus();
ic->redraw();
redraw();
}
2007-05-22 18:53:17 +04:00
void Desktop::draw(void) {
2007-05-22 20:27:33 +04:00
if(damage() & fltk::DAMAGE_ALL)
2007-05-22 18:53:17 +04:00
fltk::Window::draw();
if(damage() & (fltk::DAMAGE_ALL|fltk::DAMAGE_VALUE)) {
2007-05-22 20:27:33 +04:00
clear_xoverlay();
if(selbox->show)
draw_xoverlay(selbox->x, selbox->y, selbox->w, selbox->h);
2007-05-22 18:53:17 +04:00
}
}
2007-05-22 18:53:17 +04:00
int Desktop::handle(int event) {
switch(event) {
case fltk::PUSH: {
/*
* 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.
*/
fltk::Widget* clicked = fltk::belowmouse();
EDEBUG(ESTRLOC ": %i\n", fltk::event_button());
if(clicked == this) {
unfocus_all();
EDEBUG(ESTRLOC ": DESKTOP CLICK !!!\n");
if(!selectionbuff.empty())
selectionbuff.clear();
if(fltk::event_button() == 3)
pmenu->popup();
// track position so moving can be deduced
if(fltk::event_button() == 1) {
2007-05-22 20:27:33 +04:00
selbox->x = fltk::event_x();
selbox->y = fltk::event_y();
2007-05-22 18:53:17 +04:00
}
2007-05-22 20:27:33 +04:00
EDEBUG("overlay: %i %i %i %i\n", selbox->x, selbox->y, selbox->w, selbox->h);
2007-05-22 18:53:17 +04:00
return 1;
}
// 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) {
fltk::event_is_click(0);
select(tmp_icon);
return 1;
} else if (SELECTION_SINGLE) {
if(!in_selection(tmp_icon))
select_only(tmp_icon);
} else if (fltk::event_button() == 3)
select_only(tmp_icon);
/*
* Let child handle the rest.
* Also prevent click on other mouse buttons during move.
*/
if(!moving)
tmp_icon->handle(fltk::PUSH);
selection_x = fltk::event_x_root();
selection_y = fltk::event_y_root();
EDEBUG(ESTRLOC ": fltk::PUSH from desktop\n");
return 1;
}
case fltk::DRAG:
moving = true;
if(!selectionbuff.empty()) {
EDEBUG(ESTRLOC ": DRAG icon from desktop\n");
move_selection(fltk::event_x_root(), fltk::event_y_root(), false);
} else {
EDEBUG(ESTRLOC ": DRAG from desktop\n");
// moving is started
2007-05-22 20:27:33 +04:00
if(selbox->x != 0 || selbox->y != 0) {
selbox->w = fltk::event_x() - selbox->x;
selbox->h = fltk::event_y() - selbox->y;
selbox->show = true;
2007-05-22 18:53:17 +04:00
redraw(fltk::DAMAGE_VALUE);
}
}
return 1;
case fltk::RELEASE:
EDEBUG(ESTRLOC ": RELEASE from desktop\n");
EDEBUG(ESTRLOC ": clicks: %i\n", fltk::event_is_click());
2007-05-22 20:27:33 +04:00
if(selbox->show) {
EDEBUG("overlay: %i %i %i %i\n", selbox->x, selbox->y, selbox->w, selbox->h);
selbox->w = selbox->h = 0;
selbox->show = false;
redraw(fltk::DAMAGE_VALUE);
2007-05-22 18:53:17 +04:00
return 1;
}
if(!selectionbuff.empty() && moving) {
EDEBUG(ESTRLOC ": CLEARING BUFFER\n");
move_selection(fltk::event_x_root(), fltk::event_y_root(), true);
}
/*
* Do not send fltk::RELEASE during move
*
* TODO: should be alowed fltk::RELEASE to multiple icons? (aka. run
* command for all selected icons ?
*
* TODO: or to make something like selectionbuff[0]->execute()
* so icon execute whatever it have ?
*/
if(selectionbuff.size() == 1 && !moving)
selectionbuff[0]->handle(fltk::RELEASE);
moving = false;
return 1;
case fltk::FOCUS:
case fltk::UNFOCUS:
return 1;
default:
break;
}
2007-05-22 18:53:17 +04:00
return fltk::Window::handle(event);
}
2007-05-22 18:53:17 +04:00
/*
* This function is executed before desktop is actually showed
* but after is internally created so net_make_me_desktop() specific code can
* be executed correctly.
*
* Calling net_make_me_desktop() after show() will confuse many wm's which will
* in turn partialy register us as desktop type.
*/
void Desktop::create(void) {
fltk::Window::create();
net_make_me_desktop(this);
}
2007-05-22 18:53:17 +04:00
int main() {
signal(SIGTERM, exit_signal);
signal(SIGKILL, exit_signal);
signal(SIGINT, exit_signal);
srand(time(NULL));
// TODO: init_locale_support()
//edelib::IconTheme::init("crystalsvg");
edelib::IconTheme::init("edeneu");
fltk::register_images();
Desktop *desktop = new Desktop();
desktop->show();
/*
* XSelectInput will redirect PropertyNotify messages, which
* we are listen for
*/
XSelectInput(fltk::xdisplay, RootWindow(fltk::xdisplay, fltk::xscreen), PropertyChangeMask | StructureNotifyMask );
fltk::add_event_handler(desktop_xmessage_handler);
while(running)
fltk::wait();
delete desktop;
return 0;
}