From 6848e180a69cf55840f337f2ca3e70db44f25fd5 Mon Sep 17 00:00:00 2001 From: markt Date: Mon, 23 Apr 2007 16:07:21 +0000 Subject: added clientmenu command, set iconicstate properly, disabled resizing shaded windows --- ChangeLog | 10 +++ src/ClientMenu.cc | 34 +++++++++- src/FbCommandFactory.cc | 6 ++ src/FbCommands.cc | 30 +++++++++ src/FbCommands.hh | 16 ++++- src/FbWinFrame.cc | 17 ++--- src/FbWinFrame.hh | 3 +- src/FocusControl.cc | 35 ++++++---- src/Screen.cc | 46 +++++-------- src/Window.cc | 172 ++++++++++++++++++++++++------------------------ src/fluxbox.cc | 4 ++ 11 files changed, 229 insertions(+), 144 deletions(-) diff --git a/ChangeLog b/ChangeLog index ecd92d7..9ed3b5d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ (Format: Year/Month/Day) Changes for 1.1: +*07/04/23: + * Set IconicState on all unmapped clients and unmap clients with frames, as + per ICCCM 4.1.4 (Mark) + Screen.cc Window.cc FocusControl.cc + * Added ClientMenu key command, which pops up a menu of open windows, based + on a pattern match + - e.g. :ClientMenu (workspace=[current]) (minimized=no) + ClientMenu.cc FbCommands.cc/hh FbCommandFactory.cc + * Disabled resizing shaded windows (Mark) + FbWinFrame.cc Window.cc *07/04/20: * Fix slit's BOTTOMLEFT placement with xinerama (Thanks Tomas Janousek ) diff --git a/src/ClientMenu.cc b/src/ClientMenu.cc index 1bb3ad1..c70da85 100644 --- a/src/ClientMenu.cc +++ b/src/ClientMenu.cc @@ -36,7 +36,10 @@ class ClientMenuItem: public FbTk::MenuItem { public: ClientMenuItem(Focusable &client, ClientMenu &menu): FbTk::MenuItem(client.title().c_str(), menu), - m_client(client) { client.titleSig().attach(&menu); } + m_client(client) { + client.titleSig().attach(&menu); + client.dieSig().attach(&menu); + } ~ClientMenuItem() { m_client.titleSig().detach(menu()); } void click(int button, int time) { @@ -45,6 +48,7 @@ public: return; m_client.focus(); fbwin->raise(); + menu()->hide(); } const std::string &label() const { return m_client.title(); } @@ -60,6 +64,9 @@ public: return (&(m_client.fbwindow()->winClient()) == &m_client); } + // for updating menu when receiving a signal from client + Focusable *client() { return &m_client; } + private: Focusable &m_client; }; @@ -106,6 +113,29 @@ void ClientMenu::refreshMenu() { void ClientMenu::update(FbTk::Subject *subj) { if (subj == m_refresh_sig) refreshMenu(); - else + else if (subj && typeid(*subj) == typeid(Focusable::FocusSubject)) { + + Focusable::FocusSubject *fsubj = static_cast(subj); + Focusable &win = fsubj->win(); + + // find the corresponding menuitem + ClientMenuItem *cl_item = 0; + for (size_t i = 0; i < numberOfItems(); i++) { + FbTk::MenuItem *item = find(i); + if (item && typeid(*item) == typeid(ClientMenuItem)) { + cl_item = static_cast(item); + if (cl_item->client() == &win) + break; + } + } + + // update accordingly + if (cl_item && fsubj == &win.dieSig()) + remove(cl_item->getIndex()); + else if (cl_item && fsubj == &win.titleSig()) + // this could change the size of the menu, so do a full update + FbTk::Menu::update(subj); + + } else FbTk::Menu::update(subj); } diff --git a/src/FbCommandFactory.cc b/src/FbCommandFactory.cc index 7475d56..0daec6d 100644 --- a/src/FbCommandFactory.cc +++ b/src/FbCommandFactory.cc @@ -86,6 +86,7 @@ FbCommandFactory::FbCommandFactory() { const char* commands[] = { "arrangewindows", "bindkey", + "clientmenu", "close", "closeallwindows", "commanddialog", @@ -523,6 +524,11 @@ FbTk::Command *FbCommandFactory::stringToCommand(const std::string &command, args = arguments.c_str() + pos; parseNextWindowArgs(args, opts, pat); return new GoToWindowCmd(num, opts, pat); + } else if (command == "clientmenu") { + int opts; + string pat; + parseNextWindowArgs(arguments, opts, pat); + return new ShowClientMenuCmd(opts, pat); } else if (command == "focusup") return new DirFocusCmd(FocusControl::FOCUSUP); else if (command == "focusdown") diff --git a/src/FbCommands.cc b/src/FbCommands.cc index 060f4d3..dae4d6a 100644 --- a/src/FbCommands.cc +++ b/src/FbCommands.cc @@ -25,6 +25,7 @@ #include "fluxbox.hh" #include "Screen.hh" #include "CommandDialog.hh" +#include "FocusControl.hh" #include "Workspace.hh" #include "Window.hh" #include "Keys.hh" @@ -273,6 +274,35 @@ void HideMenuCmd::execute() { FbTk::Menu::shownMenu()->hide(); } +void ShowClientMenuCmd::execute() { + BScreen *screen = Fluxbox::instance()->mouseScreen(); + if (screen == 0) + return; + + // TODO: ClientMenu only accepts lists of FluxboxWindows for now + FocusControl::Focusables *win_list = 0; +// if (m_option & FocusControl::CYCLEGROUPS) { + win_list = (m_option & FocusControl::CYCLELINEAR) ? + &screen->focusControl().creationOrderWinList() : + &screen->focusControl().focusedOrderWinList(); +/* } else { + win_list = (m_option & FocusControl::CYCLELINEAR) ? + &screen->focusControl().creationOrderList() : + &screen->focusControl().focusedOrderList(); + } */ + + m_list.clear(); + FocusControl::Focusables::iterator it = win_list->begin(), + it_end = win_list->end(); + for (; it != it_end; ++it) { + if (typeid(**it) == typeid(FluxboxWindow) && m_pat.match(**it)) + m_list.push_back(static_cast(*it)); + } + + m_menu = new ClientMenu(*screen, m_list, 0); + ::showMenu(*screen, **m_menu); +} + ShowCustomMenuCmd::ShowCustomMenuCmd(const string &arguments) : custom_menu_file(arguments) {} void ShowCustomMenuCmd::execute() { diff --git a/src/FbCommands.hh b/src/FbCommands.hh index 314d339..a049a7c 100644 --- a/src/FbCommands.hh +++ b/src/FbCommands.hh @@ -29,8 +29,10 @@ #include "Command.hh" #include "FbTk/RefCount.hh" -#include "FbTk/Menu.hh" +#include "ClientMenu.hh" +#include "ClientPattern.hh" +#include #include namespace FbCommands { @@ -121,6 +123,18 @@ public: void execute(); }; +class ShowClientMenuCmd: public FbTk::Command { +public: + ShowClientMenuCmd(int option, std::string &pat): + m_option(option), m_pat(pat.c_str()) { } + void execute(); +private: + const int m_option; + const ClientPattern m_pat; + std::list m_list; + FbTk::RefCount m_menu; +}; + class ShowCustomMenuCmd: public FbTk::Command { public: explicit ShowCustomMenuCmd(const std::string &arguments); diff --git a/src/FbWinFrame.cc b/src/FbWinFrame.cc index 7a8a14e..dfdcf32 100644 --- a/src/FbWinFrame.cc +++ b/src/FbWinFrame.cc @@ -53,7 +53,8 @@ FbWinFrame::FbWinFrame(BScreen &screen, FbWinFrameTheme &theme, FbTk::ImageContr m_screen(screen), m_theme(theme), m_imagectrl(imgctrl), - m_window(theme.screenNum(), x, y, width, height, ButtonPressMask | ButtonReleaseMask | + m_window(theme.screenNum(), x, y, width, height, + ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | EnterWindowMask, true), m_layeritem(window(), layer), m_titlebar(m_window, 0, 0, 100, 16, @@ -91,7 +92,6 @@ FbWinFrame::FbWinFrame(BScreen &screen, FbWinFrameTheme &theme, FbTk::ImageContr m_active_orig_client_bw(0), m_need_render(true), m_button_size(1), - m_width_before_shade(1), m_height_before_shade(1), m_shaded(false), m_focused_alpha(0), @@ -233,7 +233,6 @@ void FbWinFrame::shade() { // toggle shade m_shaded = !m_shaded; if (m_shaded) { // i.e. should be shaded now - m_width_before_shade = m_window.width(); m_height_before_shade = m_window.height(); m_window.resize(m_window.width(), m_titlebar.height()); alignTabs(); @@ -241,7 +240,7 @@ void FbWinFrame::shade() { if ( m_shape.get() ) m_shape->update(); } else { // should be unshaded - m_window.resize(m_width_before_shade, m_height_before_shade); + m_window.resize(m_window.width(), m_height_before_shade); reconfigure(); } } @@ -280,19 +279,13 @@ void FbWinFrame::moveResize(int x, int y, unsigned int width, unsigned int heigh if (move && x == window().x() && y == window().y()) move = false; - if (resize && width == FbWinFrame::width() && height == FbWinFrame::height()) + if (resize && (m_shaded || width == FbWinFrame::width() && + height == FbWinFrame::height())) resize = false; if (!move && !resize) return; - if (resize && m_shaded) { - // update unshaded size if we're in shaded state and just resize width - m_width_before_shade = width; - m_height_before_shade = height; - height = m_window.height(); - } - if (move && resize) { m_window.moveResize(x, y, width, height); notifyMoved(false); // will reconfigure diff --git a/src/FbWinFrame.hh b/src/FbWinFrame.hh index 54d91df..f83ec7d 100644 --- a/src/FbWinFrame.hh +++ b/src/FbWinFrame.hh @@ -369,8 +369,7 @@ private: bool m_need_render; int m_button_size; ///< size for all titlebar buttons - unsigned int m_width_before_shade, ///< width before shade, so we can restore it when we unshade - m_height_before_shade; ///< height before shade, so we can restore it when we unshade + unsigned int m_height_before_shade; ///< height before shade, so we can restore it when we unshade bool m_shaded; ///< wheter we're shaded or not unsigned char m_focused_alpha; ///< focused alpha value unsigned char m_unfocused_alpha; ///< unfocused alpha value diff --git a/src/FocusControl.cc b/src/FocusControl.cc index 27f387c..d78f997 100644 --- a/src/FocusControl.cc +++ b/src/FocusControl.cc @@ -313,13 +313,28 @@ void FocusControl::setScreenFocusedWindow(WinClient &win_client) { // raise newly focused window to the top of the focused list // don't change the order if we're cycling or shutting down - // don't change on startup, as it may add windows that aren't listed yet - if (!isCycling() && !m_screen.isShuttingdown() && !s_reverting && - !Fluxbox::instance()->isStartup()) { - m_focused_list.remove(&win_client); + if (!isCycling() && !m_screen.isShuttingdown() && !s_reverting) { + + // make sure client is in our list, or else we could end up adding it + Focusables::iterator it_begin = m_focused_list.begin(), + it_end = m_focused_list.end(); + Focusables::iterator it = find(it_begin, it_end, &win_client); + if (it == it_end) + return; + + m_focused_list.erase(it); m_focused_list.push_front(&win_client); - m_focused_win_list.remove(win_client.fbwindow()); - m_focused_win_list.push_front(win_client.fbwindow()); + + // also check the fbwindow + it_begin = m_focused_win_list.begin(); + it_end = m_focused_win_list.end(); + it = find(it_begin, it_end, win_client.fbwindow()); + + if (it != it_end) { + m_focused_win_list.erase(it); + m_focused_win_list.push_front(win_client.fbwindow()); + } + } } @@ -465,9 +480,6 @@ void FocusControl::shutdown() { /** * This function is called whenever we aren't quite sure what * focus is meant to be, it'll make things right ;-) - * last_focused is set to something if we want to make use of the - * previously focused window (it must NOT be set focused now, it - * is probably dying). */ void FocusControl::revertFocus(BScreen &screen) { if (s_reverting) @@ -508,9 +520,8 @@ void FocusControl::revertFocus(BScreen &screen) { * If unfocus_frame is true, we won't focus anything in the same frame * as the client. * - * So, we first prefer to choose a transient parent, then the last - * client in this window, and if no luck (or unfocus_frame), then - * we just use the normal revertFocus on the screen. + * So, we first prefer to choose the last client in this window, and if no luck + * (or unfocus_frame), then we just use the normal revertFocus on the screen. * * assumption: client has focus */ diff --git a/src/Screen.cc b/src/Screen.cc index 8676dd7..7bcd938 100644 --- a/src/Screen.cc +++ b/src/Screen.cc @@ -1129,9 +1129,6 @@ void BScreen::removeClient(WinClient &client) { focusControl().removeClient(client); - for_each(getWorkspacesList().begin(), getWorkspacesList().end(), - mem_fun(&Workspace::updateClientmenu)); - if (client.fbwindow() && client.fbwindow()->isIconic()) iconListSig().notify(); @@ -1276,30 +1273,21 @@ void BScreen::sendToWorkspace(unsigned int id, FluxboxWindow *win, bool changeWS FbTk::App::instance()->sync(false); - if (win && &win->screen() == this && - (! win->isStuck())) { - - // if iconified, deiconify it before we send it somewhere - if (win->isIconic()) - win->deiconify(); - - // if the window isn't on current workspace, hide it - if (id != currentWorkspace()->workspaceID()) - win->withdraw(true); + if (win && &win->screen() == this) { windowMenu().hide(); reassociateWindow(win, id, true); - // if the window is on current workspace, show it. - if (id == currentWorkspace()->workspaceID()) - win->deiconify(false, false); - // change workspace ? - if (changeWS && id != currentWorkspace()->workspaceID()) { + if (changeWS) changeWorkspaceID(id); - win->focus(); - } + + // if the window is on current workspace, show it; else hide it. + if (id == currentWorkspace()->workspaceID()) + win->deiconify(false, false); + else + win->withdraw(true); } @@ -1793,6 +1781,15 @@ void BScreen::setupConfigmenu(FbTk::Menu &menu) { MouseTabFocus, "MouseTabFocus", "Hover over tab to focus windows"), focusControl(), FocusControl::MOUSETABFOCUS, save_and_reconfigure)); + try { + focus_menu->insert(new BoolMenuItem(_FB_XTEXT(Configmenu, FocusNew, + "Focus New Windows", "Focus newly created windows"), + *m_resource_manager.getResource(name() + ".focusNewWindows"), + saverc_cmd)); + } catch (FbTk::ResourceException e) { + cerr<insert(new BoolMenuItem(_FB_XTEXT(Configmenu, AutoRaise, "Auto Raise", @@ -1897,15 +1894,6 @@ void BScreen::setupConfigmenu(FbTk::Menu &menu) { "Opaque Window Moving", "Window Moving with whole window visible (as opposed to outline moving)", *resource.opaque_move, saverc_cmd); - try { - _BOOLITEM(menu, Configmenu, FocusNew, - "Focus New Windows", "Focus newly created windows", - *m_resource_manager.getResource(name() + ".focusNewWindows"), - saverc_cmd); - } catch (FbTk::ResourceException e) { - cerr<focus(); } frame().reconfigure(); } @@ -1244,29 +1246,26 @@ void FluxboxWindow::resize(unsigned int width, unsigned int height) { // send_event is just an override void FluxboxWindow::moveResize(int new_x, int new_y, - unsigned int new_width, unsigned int new_height, bool send_event) { + unsigned int new_width, unsigned int new_height, + bool send_event) { // magic to detect if moved during initialisation if (!m_initialized) m_old_pos_x = 1; - send_event = send_event || (frame().x() != new_x || frame().y() != new_y); + send_event = send_event || frame().x() != new_x || frame().y() != new_y; + + if ((new_width != frame().width() || new_height != frame().height()) && + isResizable() && !isShaded()) { - if (new_width != frame().width() || new_height != frame().height()) { if ((((signed) frame().width()) + new_x) < 0) new_x = 0; if ((((signed) frame().height()) + new_y) < 0) new_y = 0; - if (!isResizable()) { - new_width = width(); - new_height = height(); - } - frame().moveResize(new_x, new_y, new_width, new_height); setFocusFlag(m_focused); - shaded = false; send_event = true; } else if (send_event) frame().move(new_x, new_y); @@ -1362,10 +1361,13 @@ bool FluxboxWindow::focus() { return false; if (screen().currentWorkspaceID() != workspaceNumber() && !isStuck()) { - menu().hide(); + BScreen::FollowModel model = screen().getUserFollowModel(); - if (model == BScreen::IGNORE_OTHER_WORKSPACES) + if (model == BScreen::IGNORE_OTHER_WORKSPACES) { + Fluxbox::instance()->attentionHandler().addAttention(*this); return false; + } + // fetch the window to the current workspace if (model == BScreen::FETCH_ACTIVE_WINDOW || (isIconic() && model == BScreen::SEMIFOLLOW_ACTIVE_WINDOW)) @@ -1375,8 +1377,17 @@ bool FluxboxWindow::focus() { screen().changeWorkspaceID(workspaceNumber()); } - if (isIconic()) + FluxboxWindow *cur = FocusControl::focusedFbWindow(); + if (cur && cur != this && cur->isFullscreen()) { + Fluxbox::instance()->attentionHandler().addAttention(*this); + return false; + } + + if (isIconic()) { deiconify(); + m_focused = true; // signal to mapNotifyEvent to set focus when mapped + return true; // the window probably will get focused, just not yet + } // this needs to be here rather than setFocusFlag because // FocusControl::revertFocus will return before FocusIn events arrive @@ -1443,8 +1454,11 @@ void FluxboxWindow::hide(bool interrupt_moving) { attachTo(0, 0, true); } + setState(IconicState, false); + menu().hide(); frame().hide(); + } void FluxboxWindow::show() { @@ -1550,9 +1564,12 @@ void FluxboxWindow::deiconify(bool reassoc, bool do_raise) { show(); // focus new, OR if it's the only window on the workspace - if (was_iconic && (screen().focusControl().focusNew() || screen().currentWorkspace()->numberOfWindows() == 1)) - focus(); - + // but not on startup: focus will be handled after creating everything + // we use m_focused as a signal to focus the window when mapped + if (was_iconic && !Fluxbox::instance()->isStartup() && + (screen().focusControl().focusNew() || m_client->isTransient() || + screen().currentWorkspace()->numberOfWindows() == 1)) + m_focused = true; oplock = false; @@ -1817,22 +1834,11 @@ void FluxboxWindow::shade() { if (m_initialized && m_frame.isShaded() == shaded) frame().shade(); - if (shaded) { - shaded = false; - m_blackbox_attrib.flags ^= ATTRIB_SHADED; - m_blackbox_attrib.attrib ^= ATTRIB_SHADED; - - if (m_initialized) - setState(NormalState, false); - } else { - shaded = true; - m_blackbox_attrib.flags |= ATTRIB_SHADED; - m_blackbox_attrib.attrib |= ATTRIB_SHADED; - // shading is the same as iconic - if (m_initialized) - setState(IconicState, false); - } + shaded = !shaded; + m_blackbox_attrib.flags ^= ATTRIB_SHADED; + m_blackbox_attrib.attrib ^= ATTRIB_SHADED; + // TODO: this should set IconicState, but then we can't focus the window } void FluxboxWindow::shadeOn() { @@ -1851,19 +1857,9 @@ void FluxboxWindow::shadeOff() { void FluxboxWindow::stick() { - if (stuck) { - m_blackbox_attrib.flags ^= ATTRIB_OMNIPRESENT; - m_blackbox_attrib.attrib ^= ATTRIB_OMNIPRESENT; - - stuck = false; - - } else { - stuck = true; - - m_blackbox_attrib.flags |= ATTRIB_OMNIPRESENT; - m_blackbox_attrib.attrib |= ATTRIB_OMNIPRESENT; - - } + m_blackbox_attrib.flags ^= ATTRIB_OMNIPRESENT; + m_blackbox_attrib.attrib ^= ATTRIB_OMNIPRESENT; + stuck = !stuck; if (m_initialized) { setState(m_current_state, false); @@ -2097,24 +2093,32 @@ void FluxboxWindow::saveBlackboxAttribs() { That'll happen when its mapped */ void FluxboxWindow::setState(unsigned long new_state, bool setting_up) { - if (numClients() == 0) + m_current_state = new_state; + if (numClients() == 0 || setting_up) return; - m_current_state = new_state; - if (!setting_up) { - unsigned long state[2]; - state[0] = (unsigned long) m_current_state; - state[1] = (unsigned long) None; + unsigned long state[2]; + state[0] = (unsigned long) m_current_state; + state[1] = (unsigned long) None; - for_each(m_clientlist.begin(), m_clientlist.end(), - FbTk::ChangeProperty(display, FbAtoms::instance()->getWMStateAtom(), - PropModeReplace, - (unsigned char *)state, 2)); + for_each(m_clientlist.begin(), m_clientlist.end(), + FbTk::ChangeProperty(display, + FbAtoms::instance()->getWMStateAtom(), + PropModeReplace, + (unsigned char *)state, 2)); - saveBlackboxAttribs(); - //notify state changed - m_statesig.notify(); + ClientList::iterator it = clientList().begin(); + ClientList::iterator it_end = clientList().end(); + for (; it != it_end; ++it) { + if (new_state == IconicState) + (*it)->hide(); + else if (new_state == NormalState) + (*it)->show(); } + + saveBlackboxAttribs(); + //notify state changed + m_statesig.notify(); } bool FluxboxWindow::getState() { @@ -2374,33 +2378,25 @@ void FluxboxWindow::mapRequestEvent(XMapRequestEvent &re) { void FluxboxWindow::mapNotifyEvent(XMapEvent &ne) { WinClient *client = findClient(ne.window); - if (client == 0) + if (!client || client != m_client) return; -#ifdef DEBUG - cerr<<"FluxboxWindow::mapNotifyEvent: " - <<"ne.override_redirect = "<validateClient()) - return; + if (ne.override_redirect || !isVisible() || !client->validateClient()) + return; - setState(NormalState, false); + iconic = false; - FluxboxWindow *cur = FocusControl::focusedFbWindow(); - if (client->isTransient() || - m_screen.currentWorkspace()->numberOfWindows() == 1 || - m_screen.focusControl().focusNew() && !(cur && cur->isFullscreen())) - setCurrentClient(*client, true); - else if (m_screen.focusControl().focusNew()) - Fluxbox::instance()->attentionHandler().addAttention(*client); + // setting state will cause all tabs to be mapped, but we only want the + // original tab to be focused + if (m_current_state != NormalState) + setState(NormalState, false); - iconic = false; + // we use m_focused as a signal that this should be focused when mapped + if (isFocused()) { + m_focused = false; + focus(); } + } /** @@ -2417,7 +2413,11 @@ void FluxboxWindow::unmapNotifyEvent(XUnmapEvent &ue) { cerr<<__FILE__<<"("<<__FUNCTION__<<"): title="<title()<