all repos — fluxbox @ 52a89a92254b7c55f97ee08055d42a4a8e48259f

custom fork of the fluxbox windowmanager

Remember functionality
rathnor rathnor
commit

52a89a92254b7c55f97ee08055d42a4a8e48259f

parent

216db9159fafe20eab591dca62856e5fe55d5837

2 files changed, 773 insertions(+), 0 deletions(-)

jump to
A src/Remember.cc

@@ -0,0 +1,599 @@

+// Remember.cc for Fluxbox Window Manager +// Copyright (c) 2002 Xavier Brouckaert +// Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// $Id: Remember.cc,v 1.1 2003/04/26 07:57:00 rathnor Exp $ + +#include "Remember.hh" +#include "StringUtil.hh" +#include "WinClient.hh" +#include "FbMenu.hh" +#include "MenuItem.hh" + +// TODO get rid of these +#define RC_PATH "fluxbox" +#define RC_INIT_FILE "init" + +//use GNU extensions +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include <iostream> +#include <sstream> +#include <fstream> +/* + #include <string> + #include <memory> + #include <stdio.h> +*/ + + +#ifndef MAXPATHLEN +#define MAXPATHLEN 255 +#endif // MAXPATHLEN + +namespace { + +class RememberMenuItem : public FbTk::MenuItem { +public: + RememberMenuItem(const char *label, Remember &remember, + FluxboxWindow &fbwin, + Remember::Attribute attrib) : + FbTk::MenuItem(label), m_remember(remember), + m_win(fbwin), m_attrib(attrib) {} + + bool isSelected() const { + return m_remember.isRemembered(m_win.winClient(), m_attrib); + } + + bool isEnabled() const { + if (m_attrib != Remember::REM_JUMPWORKSPACE) + return true; + else + return (m_remember.isRemembered(m_win.winClient(), Remember::REM_WORKSPACE)); + } + + void click(int button, int time) { + if (isSelected()) { + m_remember.forgetAttrib(m_win.winClient(), m_attrib); + } else { + m_remember.rememberAttrib(m_win.winClient(), m_attrib); + } + m_remember.save(); + FbTk::MenuItem::click(button, time); + } + +private: + // my remember manager + Remember &m_remember; + FluxboxWindow &m_win; + Remember::Attribute m_attrib; +}; + +FbTk::Menu *createRememberMenu(Remember &remember, FluxboxWindow &win) { + // each fluxboxwindow has its own windowmenu + // so we also create a remember menu just for it... + BScreen &screen = win.getScreen(); + FbTk::Menu *menu = new FbMenu(*screen.menuTheme(), + screen.getScreenNumber(), + *screen.getImageControl(), + *screen.layerManager().getLayer(Fluxbox::instance()->getMenuLayer())); + + // TODO: nls + menu->insert(new RememberMenuItem("Workspace", remember, win, + Remember::REM_WORKSPACE)); + menu->insert(new RememberMenuItem("Jump to workspace", remember, win, + Remember::REM_JUMPWORKSPACE)); + menu->insert(new RememberMenuItem("Dimensions", remember, win, + Remember::REM_DIMENSIONS)); + menu->insert(new RememberMenuItem("Position", remember, win, + Remember::REM_POSITION)); + menu->insert(new RememberMenuItem("Sticky", remember, win, + Remember::REM_STUCKSTATE)); + menu->insert(new RememberMenuItem("Decorations", remember, win, + Remember::REM_DECOSTATE)); + menu->insert(new RememberMenuItem("Shaded", remember, win, + Remember::REM_SHADEDSTATE)); +// menu->insert(new RememberMenuItem("Tab", remember, win, +// Remember::REM_TABSTATE)); + menu->insert(new RememberMenuItem("Save on close", remember, win, + Remember::REM_SAVEONCLOSE)); + + menu->update(); + return menu; +}; + +}; + +Application::Application() { + workspace_remember = + dimensions_remember = + position_remember = + stuckstate_remember = + decostate_remember = + shadedstate_remember = + tabstate_remember = + jumpworkspace_remember = + save_on_close_remember = false; +} + +Remember::Remember() { + load(); +} + +Application* Remember::add(const char* app_name) { + if (!app_name) + return NULL; + Application* a = new Application(); + apps[app_name] = a; + return a; +} + +// FIXME, I am evil +#include "X11/Xlib.h" +#include "App.hh" + +const char * getWMClass(Window w) { + XClassHint ch; + + if (XGetClassHint(FbTk::App::instance()->display(), w, &ch) == 0) { + cerr<<"Failed to read class hint!"<<endl; + return 0; + } else { + std::string m_class_name; + if (ch.res_name != 0) { + //m_instance_name = const_cast<char *>(ch.res_name); + XFree(ch.res_name); + } else {} + //m_instance_name = ""; + + if (ch.res_class != 0) { + m_class_name = const_cast<char *>(ch.res_class); + XFree(ch.res_class); + } else { + m_class_name = ""; + } + return m_class_name.c_str(); + } +} + + +Application* Remember::find(WinClient &winclient) { + return find(getWMClass(winclient.fbwindow()->getClientWindow())); //FIXME +} + +Application* Remember::add(WinClient &winclient) { + return add(getWMClass(winclient.fbwindow()->getClientWindow())); //FIXME +} + + +Application* Remember::find(const char* app_name) { + if (!app_name) + return NULL; + Apps::iterator i = apps.find(app_name); + if (i!=apps.end()) + return i->second; + else + return NULL; +} + +int Remember::parseApp(ifstream &file, Application *a) { + string line; + int row = 0; + while (! file.eof()) { + if (getline(file, line)) { + row++; + if (line[0] != '#') { //the line is commented + int parse_pos = 0, err = 0; + std::string str_key, str_label; + err = StringUtil::getStringBetween(str_key, line.c_str(), '[', ']'); + if (err > 0 ) { + parse_pos += err; + err = StringUtil::getStringBetween(str_label, line.c_str() + parse_pos, '{', '}'); + if (err>0) { + parse_pos += err; + } + } else + continue; //read next line + if (!str_key.size()) + continue; //read next line + if (str_key == "Workspace") { + unsigned int w; + istringstream iss(str_label.c_str()); + iss >> w; + a->rememberWorkspace(w); + } else if (str_key == "Dimensions") { + unsigned int h,w; + istringstream iss(str_label.c_str()); + iss >> w >> h; + a->rememberDimensions(w,h); + } else if (str_key == "Position") { + unsigned int x,y; + istringstream iss(str_label); + iss >> x >> y; + a->rememberPosition(x,y); + } else if (str_key == "Shaded") { + a->rememberShadedstate((str_label=="yes")); + } else if (str_key == "Tab") { + a->rememberTabstate((str_label=="yes")); + } else if (str_key == "Deco") { + if (str_label == "NONE") { + a->rememberDecostate((unsigned int) 0); + } else if (str_label == "NORMAL") { + a->rememberDecostate((unsigned int) 0xfffffff); + } else if (str_label == "TINY") { + a->rememberDecostate((unsigned int) + FluxboxWindow::DECORM_TITLEBAR + | FluxboxWindow::DECORM_ICONIFY + | FluxboxWindow::DECORM_MENU + ); + } else if (str_label == "TOOL") { + a->rememberDecostate((unsigned int) + FluxboxWindow::DECORM_TITLEBAR + | FluxboxWindow::DECORM_MENU + ); + } else { + unsigned int mask; + const char * str = str_label.c_str(); + // it'll have at least one char and \0, so this is safe + istringstream iss(str); + // check for hex + if (str[0] == '0' && str[1] == 'x') { + iss.seekg(2); + iss >> hex; + } + iss >> mask ; + a->rememberDecostate(mask); + } + } else if (str_key == "Sticky") { + a->rememberStuckstate((str_label=="yes")); + } else if (str_key == "Jump") { + a->rememberJumpworkspace((str_label=="yes")); + } else if (str_key == "Close") { + a->rememberSaveOnClose((str_label=="yes")); + } else if (str_key == "end") { + return row; + } else { + cerr << "Unsupported apps key = " << str_key << endl; + } + } + } + } + return row; +} + +void Remember::load() { +#ifdef DEBUG + cerr << "Loading apps file..." << endl; +#endif // DEBUG + string apps_string = getenv("HOME")+string("/.")+RC_PATH+string("/")+"apps"; + ifstream apps_file(apps_string.c_str()); + if (!apps_file.fail()) { + if (!apps_file.eof()) { + string line; + int row = 0; + while (getline(apps_file, line) && ! apps_file.eof()) { + row++; + if (line[0] == '#') + continue; + string key; + int pos=0; + int err = StringUtil::getStringBetween(key, line.c_str(), '[', ']'); + + if (err >0 && key == "app") { + pos += err; + string label; + err = StringUtil::getStringBetween(label, line.c_str()+pos, '(', ')'); + if (err>0) { + Application *a; + Apps::iterator i = apps.find(label); + if (i==apps.end()) { + a = new Application(); + apps[label] = a; + } else + a = i->second; + row += parseApp(apps_file, a); + } else + cerr<<"Error1 in apps file. Line("<<row<<")"<<endl; + } else + cerr<<"Error2 in apps file. Line("<<row<<")"<<endl; + + } + } else { +#ifdef DEBUG + cerr<<__FILE__<<"("<<__LINE__<< ") Empty apps file" << endl; +#endif + } + } else { + cerr << "apps file failure" << endl; + } +} + +void Remember::save() { +#ifdef DEBUG + cerr << "Saving apps file..." << endl; +#endif // DEBUG + string apps_string = getenv("HOME")+string("/.")+RC_PATH+string("/")+"apps"; + ofstream apps_file(apps_string.c_str()); + Apps::iterator it = apps.begin(); + Apps::iterator it_end = apps.end(); + for (; it != it_end; ++it) { + apps_file << "[app] (" << it->first << ")" << endl; + Application *a = it->second; + if (a->workspace_remember) { + apps_file << " [Workspace]\t{" << a->workspace << "}" << endl; + } + if (a->dimensions_remember) { + apps_file << " [Dimensions]\t{" << a->w << " " << a->h << "}" << endl; + } + if (a->position_remember) { + apps_file << " [Position]\t{" << a->x << " " << a->y << "}" << endl; + } + if (a->shadedstate_remember) { + apps_file << " [Shaded]\t{" << ((a->shadedstate)?"yes":"no") << "}" << endl; + } + if (a->tabstate_remember) { + apps_file << " [Tab]\t\t{" << ((a->tabstate)?"yes":"no") << "}" << endl; + } + if (a->decostate_remember) { + switch (a->decostate) { + case (0) : + apps_file << " [Deco]\t{NONE}" << endl; + break; + case (0xffffffff): + case (FluxboxWindow::DECORM_LAST - 1): + apps_file << " [Deco]\t{NORMAL}" << endl; + break; + case (FluxboxWindow::DECORM_TITLEBAR + | FluxboxWindow::DECORM_ICONIFY + | FluxboxWindow::DECORM_MENU): + apps_file << " [Deco]\t{TOOL}" << endl; + break; + case (FluxboxWindow::DECORM_TITLEBAR + | FluxboxWindow::DECORM_MENU): + apps_file << " [Deco]\t{TINY}" << endl; + break; + default: + apps_file << " [Deco]\t{0x"<<hex<<a->decostate<<dec<<"}"<<endl; + break; + } + } + if (a->stuckstate_remember) { + apps_file << " [Sticky]\t{" << ((a->stuckstate)?"yes":"no") << "}" << endl; + } + if (a->jumpworkspace_remember) { + apps_file << " [Jump]\t{" << ((a->jumpworkspace)?"yes":"no") << "}" << endl; + } + if (a->save_on_close_remember) { + apps_file << " [Close]\t{" << ((a->save_on_close)?"yes":"no") << "}" << endl; + } + apps_file << "[end]" << endl; + } +} + +bool Remember::isRemembered(WinClient &winclient, Attribute attrib) { + Application *app = find(winclient); + if (!app) return false; + switch (attrib) { + case REM_WORKSPACE: + return app->workspace_remember; + break; + case REM_DIMENSIONS: + return app->dimensions_remember; + break; + case REM_POSITION: + return app->position_remember; + break; + case REM_STUCKSTATE: + return app->stuckstate_remember; + break; + case REM_DECOSTATE: + return app->decostate_remember; + break; + case REM_SHADEDSTATE: + return app->shadedstate_remember; + break; +// case REM_TABSTATE: +// return app->tabstate_remember; +// break; + case REM_JUMPWORKSPACE: + return app->jumpworkspace_remember; + break; + case REM_SAVEONCLOSE: + return app->save_on_close_remember; + break; + case REM_LASTATTRIB: + default: + return false; // should never get here + } +} + +void Remember::rememberAttrib(WinClient &winclient, Attribute attrib) { + FluxboxWindow *win = winclient.fbwindow(); + if (!win) return; + Application *app = find(winclient); + if (!app) { + app = add(winclient); + if (!app) return; + } + switch (attrib) { + case REM_WORKSPACE: + app->rememberWorkspace(win->getWorkspaceNumber()); + break; + case REM_DIMENSIONS: + app->rememberDimensions(win->getWidth(), win->getHeight()); + break; + case REM_POSITION: + app->rememberPosition(win->getXFrame(), win->getYFrame()); + break; + case REM_STUCKSTATE: + app->rememberShadedstate(win->isShaded()); + break; + case REM_DECOSTATE: + app->rememberDecostate(win->getDecorationMask()); + break; + case REM_SHADEDSTATE: + app->rememberStuckstate(win->isStuck()); + break; +// case REM_TABSTATE: +// break; + case REM_JUMPWORKSPACE: + app->rememberJumpworkspace(true); + break; + case REM_SAVEONCLOSE: + app->rememberSaveOnClose(true); + break; + case REM_LASTATTRIB: + default: + // nothing + break; + } +} + +void Remember::forgetAttrib(WinClient &winclient, Attribute attrib) { + FluxboxWindow *win = winclient.fbwindow(); + if (!win) return; + Application *app = find(winclient); + if (!app) { + app = add(winclient); + if (!app) return; + } + switch (attrib) { + case REM_WORKSPACE: + app->forgetWorkspace(); + break; + case REM_DIMENSIONS: + app->forgetDimensions(); + break; + case REM_POSITION: + app->forgetPosition(); + break; + case REM_STUCKSTATE: + app->forgetStuckstate(); + break; + case REM_DECOSTATE: + app->forgetDecostate(); + break; + case REM_SHADEDSTATE: + app->forgetShadedstate(); + break; +// case REM_TABSTATE: +// break; + case REM_JUMPWORKSPACE: + app->forgetJumpworkspace(); + break; + case REM_SAVEONCLOSE: + app->forgetSaveOnClose(); + break; + case REM_LASTATTRIB: + default: + // nothing + break; + } +} + +void Remember::setupWindow(FluxboxWindow &win) { + WinClient &winclient = win.winClient(); + + // we don't touch the window if it is a transient + // of something else + if (winclient.transientFor()) + return; + + Application *app = find(winclient); + if (!app) return; // nothing to do + + BScreen &screen = win.getScreen(); + + if (app->workspace_remember) { + // TODO: fix placement to initialise properly + screen.reassociateWindow(&win, app->workspace, true); + if (app->jumpworkspace_remember) + screen.changeWorkspaceID(app->workspace); + } + + if (app->dimensions_remember) + win.resize(app->w, app->h); + + if (app->position_remember) + win.move(app->x, app->y); + + if (app->shadedstate_remember) + // if inconsistent... + if (win.isShaded() && !app->shadedstate || + !win.isShaded() && app->shadedstate) + win.shade(); // toggles + + // external tabs aren't available atm... + //if (app->tabstate_remember) ... + + if (app->decostate_remember) + win.setDecorationMask(app->decostate); + + if (app->stuckstate_remember) + // if inconsistent... + if (win.isStuck() && !app->stuckstate || + !win.isStuck() && app->stuckstate) + win.stick(); // toggles + + // add the menu, this -2 is somewhat dodgy... :-/ + // TODO: nls + win.getWindowmenu().insert("Remember...", createRememberMenu(*this, win), win.getWindowmenu().numberOfItems()-2); + win.getWindowmenu().update(); +} + +void Remember::updateWindowClose(FluxboxWindow &win) { + // This doesn't work at present since fluxbox.cc is missing the windowclose stuff. + // I don't trust it (particularly winClient()) while this is the case + return; + + WinClient &winclient = win.winClient(); + Application *app = find(winclient); + + if (!app || !(app->save_on_close_remember && app->save_on_close)) + return; + + for (int attrib = 0; attrib <= REM_LASTATTRIB; attrib++) { + if (isRemembered(winclient, (Attribute) attrib)) { + rememberAttrib(winclient, (Attribute) attrib); + } + } +/* + if (app->workspace_remember) + app->rememberWorkspace(win.getWorkspaceNumber()); + if (app->dimensions_remember) + app->rememberDimensions(win.getWidth(), win.getHeight()); + if (app->position_remember) + app->rememberPosition(win.getXFrame(), win.getYFrame()); + if (app->shadedstate_remember) + app->rememberShadedstate(win.isShaded()); + // external tabs off atm + //if (app->tabstate_remember) ... + if (app->decostate_remember) + app->rememberDecostate(win.getDecorationMask()); + if (app->stuckstate_remember) + app->rememberStuckstate(win.isStuck()); + if (app->jumpworkspace_remember) + app->rememberJumpworkspace(true); +*/ + save(); +}
A src/Remember.hh

@@ -0,0 +1,174 @@

+// Remember.hh for Fluxbox Window Manager +// Copyright (c) 2002 Xavier Brouckaert +// Copyright (c) 2003 Henrik Kinnunen (fluxgen at users.sourceforge.net) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +// $Id: Remember.hh,v 1.1 2003/04/26 07:57:00 rathnor Exp $ + +/* Based on the original "Remember patch" by Xavier Brouckaert */ + +#ifndef REMEMBER_HH +#define REMEMBER_HH + +#include "Window.hh" +#include "AtomHandler.hh" +#include "Screen.hh" + +/* +#include <iostream> +#include <string> +#include "StringUtil.hh" +*/ +#include <fstream> +#include <map> + +class Application { +public: + Application(); + inline void forgetWorkspace() { workspace_remember = false; } + inline void forgetDimensions() { dimensions_remember = false; } + inline void forgetPosition() { position_remember = false; } + inline void forgetShadedstate() { shadedstate_remember = false; } + inline void forgetTabstate() { tabstate_remember = false; } + inline void forgetDecostate() { decostate_remember = false; } + inline void forgetStuckstate() { stuckstate_remember = false; } + inline void forgetJumpworkspace() { jumpworkspace_remember = false; } + inline void forgetSaveOnClose() { save_on_close_remember = false; } + + inline void rememberWorkspace(int ws) + { workspace = ws; workspace_remember = true; } + inline void rememberDimensions(int width, int height) + { w = width; h = height; dimensions_remember = true; } + inline void rememberPosition(int posx, int posy) + { x = posx; y = posy; position_remember = true; } + inline void rememberShadedstate(bool state) + { shadedstate = state; shadedstate_remember = true; } + inline void rememberTabstate(bool state) + { tabstate = state; tabstate_remember = true; } + inline void rememberDecostate(unsigned int state) + { decostate = state; decostate_remember = true; } + inline void rememberStuckstate(bool state) + { stuckstate = state; stuckstate_remember = true; } + inline void rememberJumpworkspace(bool state) + { jumpworkspace = state; jumpworkspace_remember = true; } + inline void rememberSaveOnClose(bool state) + { save_on_close = state; save_on_close_remember = true; } + + + bool workspace_remember; + unsigned int workspace; + + bool dimensions_remember; + int w,h; // width, height + + bool position_remember; + int x,y; + + bool shadedstate_remember; + bool shadedstate; + + bool tabstate_remember; + bool tabstate; + + bool decostate_remember; + unsigned int decostate; + + bool stuckstate_remember; + bool stuckstate; + + bool jumpworkspace_remember; + bool jumpworkspace; + + bool save_on_close_remember; + bool save_on_close; +}; + + +/* + * Class Remember is an atomhandler to avoid interfering with + * the main code as much as possible, since we hope that one day + * things like this (and maybe toolbar/slit) can become some sort + * of modular plugin. Doing this should help give an idea of what + * sort of interface abilities we'll need... + */ +class Remember : public AtomHandler { +public: + enum Attribute { + REM_WORKSPACE=0, + REM_DIMENSIONS, + REM_POSITION, + REM_STUCKSTATE, + REM_DECOSTATE, + REM_SHADEDSTATE, + //REM_TABSTATE, ... external tabs disabled atm + REM_JUMPWORKSPACE, + REM_SAVEONCLOSE, + REM_LASTATTRIB // not actually used + }; + + typedef std::map<string,Application *> Apps; + Remember(); + + Application* find(WinClient &winclient); + Application* find(const char* app_name); + Application* add(WinClient &winclient); + Application* add(const char* app_name); + + void load(); + void save(); + + bool isRemembered(WinClient &win, Attribute attrib); + void rememberAttrib(WinClient &win, Attribute attrib); + void forgetAttrib(WinClient &win, Attribute attrib); + + // Functions relating to AtomHandler + + // Functions we actually use + void setupWindow(FluxboxWindow &win); + void updateWindowClose(FluxboxWindow &win); + + // Functions we ignore (zero from AtomHandler) + // Leaving here in case they might be useful later + + void initForScreen(BScreen &screen) {} + + void updateClientList(BScreen &screen) {} + void updateWorkspaceNames(BScreen &screen) {} + void updateCurrentWorkspace(BScreen &screen) {} + void updateWorkspaceCount(BScreen &screen) {} + + void updateWorkspace(FluxboxWindow &win) {} + void updateState(FluxboxWindow &win) {} + void updateHints(FluxboxWindow &win) {} + void updateLayer(FluxboxWindow &win) {} + + bool checkClientMessage(const XClientMessageEvent &ce, + BScreen * screen, FluxboxWindow * const win) { return false; } + + +private: + + // returns number of lines read + int parseApp(ifstream &file, Application *a); + Apps apps; + +}; + +#endif // REMEMBER_HH