// Tab.cc for Fluxbox Window Manager // Copyright (c) 2001 - 2002 Henrik Kinnunen (fluxgen at linuxmail.org) // // 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: Tab.cc,v 1.54 2003/02/09 14:11:12 rathnor Exp $ #include "Tab.hh" #include "i18n.hh" #include "DrawUtil.hh" #include "Screen.hh" #include "fluxbox.hh" #include "ImageControl.hh" #include <iostream> using namespace std; bool Tab::m_stoptabs = false; Tab::t_tabplacementlist Tab::m_tabplacementlist[] = { {PTOP, "Top"}, {PBOTTOM, "Bottom"}, {PLEFT, "Left"}, {PRIGHT, "Right"}, {PNONE, "none"} }; Tab::t_tabplacementlist Tab::m_tabalignmentlist[] = { {ALEFT, "Left"}, {ACENTER, "Center"}, {ARIGHT, "Right"}, {ARELATIVE, "Relative"}, {ANONE, "none"} }; Tab::Tab(FluxboxWindow *win, Tab *prev, Tab *next) { //set default values m_focus = m_moving = false; m_configured = true; // only set to false before Fluxbox::reconfigure m_move_x = m_move_y = 0; m_prev = prev; m_next = next; m_win = win; m_display = BaseDisplay::getXDisplay(); if ((m_win->getScreen()->getTabPlacement() == PLEFT || m_win->getScreen()->getTabPlacement() == PRIGHT) && m_win->getScreen()->isTabRotateVertical() && !m_win->isShaded()) { m_size_w = m_win->getScreen()->getTabHeight(); m_size_h = m_win->getScreen()->getTabWidth(); } else { m_size_w = m_win->getScreen()->getTabWidth(); m_size_h = m_win->getScreen()->getTabHeight(); } createTabWindow(); calcIncrease(); } Tab::~Tab() { disconnect(); Fluxbox::instance()->removeTabSearch(m_tabwin); XDestroyWindow(m_display, m_tabwin); } //---------------- createTabWindow --------------- // (private) // Creates the Window for tab to be above the title window. // This should only be called by the constructor. //------------------------------------------------- void Tab::createTabWindow() { unsigned long attrib_mask = CWBackPixmap | CWBackPixel | CWBorderPixel | CWColormap | CWOverrideRedirect | CWEventMask; XSetWindowAttributes attrib; attrib.background_pixmap = None; attrib.background_pixel = attrib.border_pixel = m_win->getScreen()->getWindowStyle()->tab.border_color.pixel(); attrib.colormap = m_win->getScreen()->colormap(); attrib.override_redirect = True; attrib.event_mask = ButtonPressMask | ButtonReleaseMask | ButtonMotionMask | ExposureMask | EnterWindowMask; //Notice that m_size_w gets the TOTAL width of tabs INCLUDING borders m_tabwin = XCreateWindow(m_display, m_win->getScreen()->getRootWindow(), -30000, -30000, //TODO: So that it wont flicker or // appear before the window do m_size_w - m_win->getScreen()->getWindowStyle()->tab.border_width_2x, m_size_h - m_win->getScreen()->getWindowStyle()->tab.border_width_2x, m_win->getScreen()->getWindowStyle()->tab.border_width, m_win->getScreen()->getDepth(), InputOutput, m_win->getScreen()->getVisual(), attrib_mask, &attrib); //set grab XGrabButton(m_display, Button1, Mod1Mask, m_tabwin, True, ButtonReleaseMask | ButtonMotionMask, GrabModeAsync, GrabModeAsync, None, Fluxbox::instance()->getMoveCursor()); //save to tabsearch Fluxbox::instance()->saveTabSearch(m_tabwin, this); XMapSubwindows(m_display, m_tabwin); // don't show if the window is iconified if (!m_win->isIconic()) XMapWindow(m_display, m_tabwin); decorate(); } //-------------- focus -------------------- // Called when the focus changes in m_win // updates pixmap or color and draws the tab //----------------------------------------- void Tab::focus() { if (m_win->isFocused()) { if (m_focus_pm) XSetWindowBackgroundPixmap(m_display, m_tabwin, m_focus_pm); else XSetWindowBackground(m_display, m_tabwin, m_focus_pixel); } else { if (m_unfocus_pm) XSetWindowBackgroundPixmap(m_display, m_tabwin, m_unfocus_pm); else XSetWindowBackground(m_display, m_tabwin, m_unfocus_pixel); } XClearWindow(m_display, m_tabwin); draw(false); } //-------------- raise -------------------- // Raises the tabs in the tablist //----------------------------------------- void Tab::raise() { //get first tab Tab *tab = 0; //raise tabs Workspace::Stack st; for (tab = getFirst(this); tab!=0; tab = tab->m_next) { st.push_back(tab->m_tabwin); } m_win->getScreen()->raiseWindows(st); } //-------------- lower -------------------- // Lowers the tabs in the tablist AND // the windows the tabs relate to //----------------------------------------- void Tab::lower() { Tab *current = this; FluxboxWindow *win = 0; //convenience //this have to be done in the correct order, otherwise we'll switch the window //being ontop in the group do { XLowerWindow(m_display, current->m_tabwin); //lower tabwin and tabs window win = current->getWindow(); win->lower(); current = current->next(); //get next if (current == 0) current = getFirst(this); //there weren't any after, get the first } while (current != this); } //-------------- loadTheme ----------------- // loads the texture with the correct // width and height, this is necessary in // vertical and relative tab modes // TODO optimize this //------------------------------------------ void Tab::loadTheme() { FbTk::ImageControl *image_ctrl = m_win->getScreen()->getImageControl(); Pixmap tmp = m_focus_pm; const FbTk::Texture *texture = &(m_win->getScreen()->getWindowStyle()->tab.l_focus); if (texture->type() & FbTk::Texture::PARENTRELATIVE ) { const FbTk::Texture &pt = m_win->getScreen()->getWindowStyle()->tab.t_focus; if (pt.type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { m_focus_pm = None; m_focus_pixel = pt.color().pixel(); } else m_focus_pm = image_ctrl->renderImage(m_size_w, m_size_h, pt); if (tmp) image_ctrl->removeImage(tmp); } else { if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { m_focus_pm = None; m_focus_pixel = texture->color().pixel(); } else m_focus_pm = image_ctrl->renderImage(m_size_w, m_size_h, *texture); if (tmp) image_ctrl->removeImage(tmp); } tmp = m_unfocus_pm; texture = &(m_win->getScreen()->getWindowStyle()->tab.l_unfocus); if (texture->type() & FbTk::Texture::PARENTRELATIVE ) { const FbTk::Texture &pt = m_win->getScreen()->getWindowStyle()->tab.t_unfocus; if (pt.type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { m_unfocus_pm = None; m_unfocus_pixel = pt.color().pixel(); } else m_unfocus_pm = image_ctrl->renderImage(m_size_w, m_size_h, pt); } else { if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) { m_unfocus_pm = None; m_unfocus_pixel = texture->color().pixel(); } else m_unfocus_pm = image_ctrl->renderImage(m_size_w, m_size_h, *texture); } if (tmp) image_ctrl->removeImage(tmp); } /** decorates the tab with current theme */ void Tab::decorate() { loadTheme(); XSetWindowBorderWidth(m_display, m_tabwin, m_win->getScreen()->getWindowStyle()->tab.border_width); XSetWindowBorder(m_display, m_tabwin, m_win->getScreen()->getWindowStyle()->tab.border_color.pixel()); } /** Deiconifies the tab Used from FluxboxWindow to deiconify the tab when the window is deiconfied */ void Tab::deiconify() { XMapWindow(m_display, m_tabwin); } /** Iconifies the tab. Used from FluxboxWindow to hide tab win when window is iconified disconnects itself from the list */ void Tab::iconify() { disconnect(); withdraw(); if(!Fluxbox::instance()->useTabs() && !m_next && !m_prev)//if we don't want to use tabs that much m_win->setTab(false);//let's get rid of this loner tab } /** Unmaps the tab from display */ void Tab::withdraw() { XUnmapWindow(m_display, m_tabwin); } /** Set/reset the the sticky on all windows in the list */ void Tab::stick() { Tab *tab; bool wasstuck = m_win->isStuck(); //now do stick for all windows in the list for (tab = getFirst(this); tab != 0; tab = tab->m_next) { FluxboxWindow *win = tab->m_win; //just for convenience if (wasstuck) { win->blackbox_attrib.flags ^= BaseDisplay::ATTRIB_OMNIPRESENT; win->blackbox_attrib.attrib ^= BaseDisplay::ATTRIB_OMNIPRESENT; win->stuck = false; } else { win->stuck = true; BScreen *screen = win->getScreen(); if (!win->isIconic() && !(win->getWorkspaceNumber() != screen->getCurrentWorkspaceID())) { screen->reassociateWindow(win, screen->getCurrentWorkspaceID(), true); } win->blackbox_attrib.flags |= BaseDisplay::ATTRIB_OMNIPRESENT; win->blackbox_attrib.attrib |= BaseDisplay::ATTRIB_OMNIPRESENT; } win->setState(win->current_state); } } /** Resize the window's in the tablist */ void Tab::resize() { Tab *tab; //now move and resize the windows in the list for (tab = getFirst(this); tab != 0; tab = tab->m_next) { if (tab!=this) { tab->m_win->moveResize(m_win->getXFrame(), m_win->getYFrame(), m_win->getWidth(), m_win->getHeight()); } } // need to resize tabs if in relative mode if (m_win->getScreen()->getTabAlignment() == ARELATIVE) { calcIncrease(); setPosition(); } } /** Shades the windows in the tablist */ void Tab::shade() { Tab *tab; for(tab = getFirst(this); tab != 0; tab = tab->m_next) { if (tab==this) continue; tab->m_win->shade(); } if (m_win->getScreen()->getTabPlacement() == PLEFT || m_win->getScreen()->getTabPlacement() == PRIGHT) { resizeGroup(); calcIncrease(); } if (!(m_win->getScreen()->getTabPlacement() == PTOP)) setPosition(); } /** Draws the tab if pressed = true then it draws the tab in pressed mode else it draws it in normal mode TODO: the "draw in pressed mode" */ void Tab::draw(bool pressed) const { XClearWindow(m_display, m_tabwin); if (m_win->getTitle().size() == 0) // we don't have anything to draw return; GC gc = ((m_win->isFocused()) ? m_win->getScreen()->getWindowStyle()->tab.l_text_focus_gc : m_win->getScreen()->getWindowStyle()->tab.l_text_unfocus_gc); Theme::WindowStyle *winstyle = m_win->getScreen()->getWindowStyle(); size_t dlen = m_win->getTitle().size(); size_t max_width = m_size_w; // special cases in rotated mode if (winstyle->tab.font.isRotated() && !m_win->isShaded()) max_width = m_size_h; int dx = DrawUtil::doAlignment(max_width, 1, //m_win->frame.bevel_w, winstyle->tab.justify, winstyle->tab.font, m_win->getTitle().c_str(), m_win->getTitle().size(), dlen); int dy = winstyle->tab.font.ascent() + 1; //m_win->frame.bevel_w; bool rotate = false; // swap dx and dy if we're rotated if (winstyle->tab.font.isRotated() && !m_win->isShaded()) { int tmp = dy; dy = m_size_h - dx; // upside down (reverse direction) dx = tmp; rotate = true; } // draw normal without rotation winstyle->tab.font.drawText( m_tabwin, m_win->getScreen()->getScreenNumber(), gc, m_win->getTitle().c_str(), dlen, dx, dy, rotate); } /** Helper for the Tab::setPosition() call returns the y position component correctly according to shading in cases PBOTTOM and isShaded() */ int Tab::setPositionShadingHelper(bool shaded) { if (shaded) { return m_win->getYFrame() + m_win->getTitleHeight() + m_win->getScreen()->getBorderWidth2x(); } else { return m_win->getYFrame() + m_win->getHeight() + m_win->getScreen()->getBorderWidth2x(); } } /** Helpers for correct alignment of tabs used by the setPosition() call return x/y positions correctly according to alignment, the 1st for cases PTOP and PBOTTOM the 2nd for cases PLEFT and PRIGHT */ int Tab::setPositionTBAlignHelper(Alignment align) { switch(align) { case ARELATIVE: case ALEFT: return m_win->getXFrame(); break; case ACENTER: return calcCenterXPos(); break; case ARIGHT: return m_win->getXFrame() + m_win->getWidth() + m_win->getScreen()->getBorderWidth2x() - m_size_w; default: #ifdef DEBUG cerr << __FILE__ << ":" <<__LINE__ << ": " << "Unsupported Alignment" << endl; #endif //DEBUG return 0; break; } } int Tab::setPositionLRAlignHelper(Alignment align) { switch(align) { case ALEFT: return m_win->getYFrame() - m_size_h + m_win->getHeight() + m_win->getScreen()->getBorderWidth2x(); break; case ACENTER: return calcCenterYPos(); break; case ARELATIVE: case ARIGHT: return m_win->getYFrame(); break; default: #ifdef DEBUG cerr << __FILE__ << ":"<< __LINE__ << ": " << "Unsupported Alignment" << endl; #endif //DEBUG return 0; break; } } /** Position tab ( follow the m_win pos ). (and resize) Set new position of the other tabs in the chain */ void Tab::setPosition() { //don't do anything if the tablist is freezed if (m_stoptabs) return; Tab *tab; int pos_x = 0, pos_y = 0; m_stoptabs = true; //freeze tablist //and check for max tabs //Tab placement + alignment switch (m_win->getScreen()->getTabPlacement()) { case PTOP: pos_y = m_win->getYFrame() - m_size_h; pos_x = setPositionTBAlignHelper( m_win->getScreen()->getTabAlignment()); break; case PBOTTOM: pos_y = setPositionShadingHelper(m_win->isShaded()); pos_x = setPositionTBAlignHelper( m_win->getScreen()->getTabAlignment()); break; case PLEFT: pos_x = m_win->isShaded() ? setPositionTBAlignHelper(m_win->getScreen()->getTabAlignment()) : m_win->getXFrame() - m_size_w; pos_y = m_win->isShaded() ? setPositionShadingHelper(true) : setPositionLRAlignHelper(m_win->getScreen()->getTabAlignment()); break; case PRIGHT: pos_x = m_win->isShaded() ? setPositionTBAlignHelper(m_win->getScreen()->getTabAlignment()) : m_win->getXFrame() + m_win->getWidth() + m_win->getScreen()->getBorderWidth2x(); pos_y = m_win->isShaded() ? setPositionShadingHelper(true) : setPositionLRAlignHelper(m_win->getScreen()->getTabAlignment()); break; default: if(m_win->isShaded()) { pos_y = setPositionShadingHelper(true); pos_x = setPositionTBAlignHelper( m_win->getScreen()->getTabAlignment()); } else { setPositionShadingHelper(false); } break; } for (tab = getFirst(this); tab!=0; pos_x += tab->m_inc_x, pos_y += tab->m_inc_y, tab = tab->m_next){ XMoveWindow(m_display, tab->m_tabwin, pos_x, pos_y); //dont move FluxboxWindow if the iterator = this if (tab != this) { tab->m_win->moveResize(m_win->getXFrame(), m_win->getYFrame(), m_win->getWidth(), m_win->getHeight()); } } m_stoptabs = false;//thaw tablist } //Moves the tab to the left void Tab::movePrev() { insert(m_prev); } //Moves the tab to the next tab if m_next != 0 void Tab::moveNext() { if(m_next == 0) return; Tab *tmp = m_next; disconnect(); tmp->insert(this); } /** calculates m_inc_x and m_inc_y for tabs used for positioning the tabs. */ void Tab::calcIncrease() { Tab *tab; int inc_x = 0, inc_y = 0; unsigned int i = 0, tabs = numObjects(); if (m_win->getScreen()->getTabPlacement() == PTOP || m_win->getScreen()->getTabPlacement() == PBOTTOM || m_win->isShaded()) { inc_y = 0; switch(m_win->getScreen()->getTabAlignment()) { case ALEFT: inc_x = m_size_w; break; case ACENTER: inc_x = m_size_w; break; case ARIGHT: inc_x = -m_size_w; break; case ARELATIVE: inc_x = calcRelativeWidth(); break; default: break; } } else if (m_win->getScreen()->getTabPlacement() == PLEFT || m_win->getScreen()->getTabPlacement() == PRIGHT) { inc_x = 0; switch(m_win->getScreen()->getTabAlignment()) { case ALEFT: inc_y = -m_size_h; break; case ACENTER: inc_y = m_size_h; break; case ARIGHT: inc_y = m_size_h; break; case ARELATIVE: inc_y = calcRelativeHeight(); break; default: break; } } for (tab = getFirst(this); tab!=0; tab = tab->m_next, i++) { //TODO: move this out from here? if ((m_win->getScreen()->getTabPlacement() == PTOP || m_win->getScreen()->getTabPlacement() == PBOTTOM || m_win->isShaded()) && m_win->getScreen()->getTabAlignment() == ARELATIVE) { if (!((m_win->getWidth() + m_win->getScreen()->getBorderWidth2x()) % tabs) || i >= ((m_win->getWidth() + m_win->getScreen()->getBorderWidth2x()) % tabs)) { tab->setTabWidth(inc_x); tab->m_inc_x = inc_x; } else { // adding 1 extra pixel to get tabs like win width tab->setTabWidth(inc_x + 1); tab->m_inc_x = inc_x + 1; } tab->m_inc_y = inc_y; } else if (m_win->getScreen()->getTabAlignment() == ARELATIVE) { if (!((m_win->getHeight() + m_win->getScreen()->getBorderWidth2x()) % tabs) || i >= ((m_win->getHeight() + m_win->getScreen()->getBorderWidth2x()) % tabs)) { tab->setTabHeight(inc_y); tab->m_inc_y = inc_y; } else { // adding 1 extra pixel to get tabs match window width tab->setTabHeight(inc_y + 1); tab->m_inc_y = inc_y + 1; } tab->m_inc_x = inc_x; } else { // non relative modes tab->m_inc_x = inc_x; tab->m_inc_y = inc_y; } } } /** Handle button press event here. */ void Tab::buttonPressEvent(XButtonEvent *be) { //draw in pressed mode draw(true); //invoke root menu with auto-tab? /* if (be->button == 3) { BScreen *screen = m_win->getScreen(); Rootmenu *rootmenu = screen->getRootmenu(); if (! rootmenu->isVisible()) { Fluxbox::instance()->checkMenu(); screen->getRootmenu()->move(be->x_root, be->y_root-rootmenu->titleHeight()); rootmenu->setAutoGroupWindow(m_win->getClientWindow()); rootmenu->show(); } } //otherwise let the window handle the event else { //set window to titlewindow so we can take advantage of drag function be->window = m_win->frame().titlebar().window(); //call windows buttonpress eventhandler m_win->buttonPressEvent(*be); } */ } /** Handle button release event here. If tab is dropped then it should try to find the window where the tab where dropped. */ void Tab::buttonReleaseEvent(XButtonEvent *be) { if (m_moving) { m_moving = false; //erase tabmoving rectangle XDrawRectangle(m_display, m_win->getScreen()->getRootWindow(), m_win->getScreen()->getOpGC(), m_move_x, m_move_y, m_size_w, m_size_h); Fluxbox::instance()->ungrab(); XUngrabPointer(m_display, CurrentTime); //storage of window and pos of window where we dropped the tab Window child; int dest_x = 0, dest_y = 0; //find window on coordinates of buttonReleaseEvent if (XTranslateCoordinates(m_display, m_win->getScreen()->getRootWindow(), m_win->getScreen()->getRootWindow(), be->x_root, be->y_root, &dest_x, &dest_y, &child)) { Tab *tab = Fluxbox::instance()->searchTab(child); FluxboxWindow *win = Fluxbox::instance()->searchWindow(child); if(win!=0 && m_win->getScreen()->isSloppyWindowGrouping()) win->setTab(true); //search tablist for a tabwindow if ( (tab!=0) || (m_win->getScreen()->isSloppyWindowGrouping() && (win!=0) && (tab = win->getTab())!=0)) { if (tab == this) // inserting ourself to ourself causes a disconnect return; // do only attach a hole chain if we dropped the // first tab in the dropped chain... if (m_prev) disconnect(); // attach this tabwindow chain to the tabwindow chain we found. tab->insert(this); } else { //Dropped nowhere disconnect(); // convenience unsigned int placement = m_win->getScreen()->getTabPlacement(); // (ab)using dest_x and dest_y dest_x = be->x_root; dest_y = be->y_root; if (placement == PTOP || placement == PBOTTOM || m_win->isShaded()) { if (placement == PBOTTOM && !m_win->isShaded()) dest_y -= m_win->getHeight(); else if (placement != PTOP && m_win->isShaded()) dest_y -= m_win->getTitleHeight(); else // PTOP dest_y += m_win->getTitleHeight(); switch(m_win->getScreen()->getTabAlignment()) { case ACENTER: dest_x -= (m_win->getWidth() / 2) - (m_size_w / 2); break; case ARIGHT: dest_x -= m_win->getWidth() - m_size_w; break; default: break; } } else { // PLEFT & PRIGHT if (placement == PRIGHT) dest_x = be->x_root - m_win->getWidth(); switch(m_win->getScreen()->getTabAlignment()) { case ACENTER: dest_y -= (m_win->getHeight() / 2) - (m_size_h / 2); break; case ALEFT: dest_y -= m_win->getHeight() - m_size_h; break; default: break; } } //TODO: this causes an calculate increase event, even if we // only are moving a window m_win->moveResize(dest_x, dest_y, m_win->getWidth(), m_win->getHeight()); if(!Fluxbox::instance()->useTabs()) m_win->setTab(false);//Remove tab from window, as it is now alone... } } } else { //raise this tabwindow raise(); //set window to title window soo we can use m_win handler for menu be->window = m_win->frame().titlebar().window(); //call windows buttonrelease event handler so it can popup a menu if needed m_win->buttonReleaseEvent(*be); } } //------------- exposeEvent ------------ // Handle expose event here. // Draws the tab unpressed //-------------------------------------- void Tab::exposeEvent(XExposeEvent *ee) { draw(false); } //----------- motionNotifyEvent -------- // Handles motion event here // Draws the rectangle of moving tab //-------------------------------------- void Tab::motionNotifyEvent(XMotionEvent *me) { Fluxbox *fluxbox = Fluxbox::instance(); //if mousebutton 2 is pressed if (me->state & Button2Mask) { if (!m_moving) { m_moving = true; XGrabPointer(m_display, me->window, False, Button2MotionMask | ButtonReleaseMask, GrabModeAsync, GrabModeAsync, None, fluxbox->getMoveCursor(), CurrentTime); fluxbox->grab(); m_move_x = me->x_root - 1; m_move_y = me->y_root - 1; XDrawRectangle(m_display, m_win->getScreen()->getRootWindow(), m_win->getScreen()->getOpGC(), m_move_x, m_move_y, m_size_w, m_size_h); } else { int dx = me->x_root - 1, dy = me->y_root - 1; dx -= m_win->getScreen()->getBorderWidth(); dy -= m_win->getScreen()->getBorderWidth(); if (m_win->getScreen()->getEdgeSnapThreshold()) { int drx = m_win->getScreen()->getWidth() - (dx + 1); if (dx > 0 && dx < drx && dx < m_win->getScreen()->getEdgeSnapThreshold()) dx = 0; else if (drx > 0 && drx < m_win->getScreen()->getEdgeSnapThreshold()) dx = m_win->getScreen()->getWidth() - 1; int dtty, dbby, dty, dby; dty = dy - dtty; dby = dbby - (dy + 1); if (dy > 0 && dty < m_win->getScreen()->getEdgeSnapThreshold()) dy = dtty; else if (dby > 0 && dby < m_win->getScreen()->getEdgeSnapThreshold()) dy = dbby - 1; } //erase rectangle XDrawRectangle(m_display, m_win->getScreen()->getRootWindow(), m_win->getScreen()->getOpGC(), m_move_x, m_move_y, m_size_w, m_size_h); //redraw rectangle at new pos m_move_x = dx; m_move_y = dy; XDrawRectangle(m_display, m_win->getScreen()->getRootWindow(), m_win->getScreen()->getOpGC(), m_move_x, m_move_y, m_size_w, m_size_h); } } } //-------------- getFirst() --------- // Returns the first Tab in the chain // of currentchain. //----------------------------------- Tab *Tab::getFirst(Tab *current) { if (!current) return 0; Tab *i=current; for (; i->m_prev != 0; i = i->m_prev); return i; } //-------------- getLast() --------- // Returns the last Tab in the chain // of currentchain. //----------------------------------- Tab *Tab::getLast(Tab *current) { if (!current) return 0; Tab *i=current; for (; i->m_next != 0; i = i->m_next); return i; } //-------------- insert ------------ // (private) // Inserts a tab in the chain //---------------------------------- void Tab::insert(Tab *tab) { if (!tab || tab == this) //dont insert if the tab = 0 or the tab = this return; Tab *first = getFirst(this); //if the tab already in chain then disconnect it for (; first!=0; first = first->m_next) { if (first==tab) { #ifdef DEBUG cerr<<"Tab already in chain. Disconnecting!"<<endl; #endif // DEBUG tab->disconnect(); break; } } //get last tab in the chain to be inserted Tab *last = tab; for (; last->m_next!=0; last=last->m_next); //do sticky before we connect it to the chain //sticky bit on window if (m_win->isStuck() != tab->m_win->isStuck()) { tab->m_win->stuck = !m_win->stuck; // it will toggle tab->stick(); //this will set all the m_wins in the list } //connect the tab to this chain if (m_next) m_next->m_prev = last; tab->m_prev = this; last->m_next = m_next; m_next = tab; bool resize_tabs = false; //TODO: cleanup and optimize //move and resize all windows in the tablist we inserted //only from first tab of the inserted chain to the last for (; tab!=last->m_next; tab=tab->m_next) { if (m_win->isShaded() != tab->m_win->isShaded()) { tab->m_stoptabs = true; // we don't want any actions performed on the // tabs, just the tab windows! if (m_win->getScreen()->getTabPlacement() == PLEFT || m_win->getScreen()->getTabPlacement() == PRIGHT) resize_tabs = true; // if the window we are grouping to, we need to shade the tab window // _after_ reconfigure if(m_win->isShaded()) { tab->m_win->moveResize(m_win->getXFrame(), m_win->getYFrame(), m_win->getWidth(), m_win->getHeight()); tab->m_win->shade(); } else { tab->m_win->shade(); // switch to correct shade state tab->m_win->moveResize(m_win->getXFrame(), m_win->getYFrame(), m_win->getWidth(), m_win->getHeight()); } tab->m_stoptabs = false; // both window have the same shaded state and have different sizes, // checking this so that I'll only do shade on windows if configure did // anything. } else if ((m_win->getWidth() != tab->m_win->getWidth()) || (m_win->getHeight() != tab->m_win->getHeight())) { tab->m_win->moveResize(m_win->getXFrame(), m_win->getYFrame(), m_win->getWidth(), m_win->getHeight()); // need to shade the tab window as configure will mess it up if (m_win->isShaded()) tab->m_win->shade(); } } // resize if in relative mode or resize_tabs is true if(m_win->getScreen()->getTabAlignment() == ARELATIVE || resize_tabs) { resizeGroup(); calcIncrease(); } // reposition tabs setPosition(); } //---------- disconnect() -------------- // Disconnects the tab from any chain //-------------------------------------- void Tab::disconnect() { Tab *tmp = 0; Fluxbox *fluxbox = Fluxbox::instance(); if (m_prev) { //if this have a chain to "the left" (previous tab) then set it's next to this next m_prev->m_next = m_next; if(!m_next && !fluxbox->useTabs())//Only two tabs in list, remove tab from remaining window m_prev->m_win->setTab(false); else tmp = m_prev; } if (m_next) { //if this have a chain to "the right" (next tab) then set it's prev to this prev m_next->m_prev = m_prev; if(!m_prev && !fluxbox->useTabs())//Only two tabs in list, remove tab from remaining window m_next->m_win->setTab(false); else tmp = m_next; } //mark as no chain, previous and next. m_prev = 0; m_next = 0; //reposition the tabs if (tmp) { if (m_win->getScreen()->getTabAlignment() == ARELATIVE) tmp->calcIncrease(); tmp->setPosition(); } if (m_win->getScreen()->getTabAlignment() == ARELATIVE) calcIncrease(); setPosition(); } // ------------ setTabWidth -------------- // Sets Tab width _including_ borders // --------------------------------------- void Tab::setTabWidth(unsigned int w) { if (w > m_win->getScreen()->getWindowStyle()->tab.border_width_2x && w != m_size_w) { m_size_w = w; XResizeWindow(m_display, m_tabwin, m_size_w - m_win->getScreen()->getWindowStyle()->tab.border_width_2x, m_size_h - m_win->getScreen()->getWindowStyle()->tab.border_width_2x); loadTheme(); // rerender themes to right size focus(); // redraw the window } } // ------------ setTabHeight --------- // Sets Tab height _including_ borders // --------------------------------------- void Tab::setTabHeight(unsigned int h) { if (h > m_win->getScreen()->getWindowStyle()->tab.border_width_2x && h != m_size_h) { m_size_h = h; XResizeWindow(m_display, m_tabwin, m_size_w - m_win->getScreen()->getWindowStyle()->tab.border_width_2x, m_size_h - m_win->getScreen()->getWindowStyle()->tab.border_width_2x); loadTheme(); // rerender themes to right size focus(); // redraw the window } } // ------------ resizeGroup -------------- // This function is used when (un)shading // to get right size/width of tabs when // PLeft || PRight && isTabRotateVertical // --------------------------------------- void Tab::resizeGroup() { Tab *first; for (first = getFirst(this); first != 0; first = first->m_next) { if ((m_win->getScreen()->getTabPlacement() == PLEFT || m_win->getScreen()->getTabPlacement() == PRIGHT) && m_win->getScreen()->isTabRotateVertical() && !m_win->isShaded()) { first->setTabWidth(m_win->getScreen()->getTabHeight()); first->setTabHeight(m_win->getScreen()->getTabWidth()); } else { first->setTabWidth(m_win->getScreen()->getTabWidth()); first->setTabHeight(m_win->getScreen()->getTabHeight()); } //TODO: do I have to set this all the time? first->m_configured = true; //used in Fluxbox::reconfigure() } } //------------- calcRelativeWidth -------- // Returns: Calculated width for relative // alignment //---------------------------------------- unsigned int Tab::calcRelativeWidth() { unsigned int num=0; //calculate num objs in list (extract this to a function?) for (Tab *first=getFirst(this); first!=0; first=first->m_next, num++); return ((m_win->getWidth() + m_win->getScreen()->getBorderWidth2x())/num); } /** Returns the number of objects in the TabGroup. */ unsigned int Tab::numObjects() { unsigned int num = 0; for (Tab *tab = getFirst(this); tab != 0; tab = tab->m_next, num++); return num; } /** Returns: Calculated height for relative alignment */ unsigned int Tab::calcRelativeHeight() { return ((m_win->getHeight() + m_win->getScreen()->getBorderWidth2x())/numObjects()); } //------------- calcCenterXPos ----------- // Returns: Calculated x position for // centered alignment //---------------------------------------- unsigned int Tab::calcCenterXPos() { return (m_win->getXFrame() + ((m_win->getWidth() - (m_size_w * numObjects())) / 2)); } //------------- calcCenterYPos ----------- // Returns: Calculated y position for // centered alignment //---------------------------------------- unsigned int Tab::calcCenterYPos() { return (m_win->getYFrame() + ((m_win->getHeight() - (m_size_h * numObjects())) / 2)); } //------- getTabPlacementString ---------- // Returns the tabplacement string of the // tabplacement number on success else 0. //---------------------------------------- const char *Tab::getTabPlacementString(Tab::Placement placement) { for (int i=0; i<(PNONE / 5); i++) { if (m_tabplacementlist[i] == placement) return m_tabplacementlist[i].string; } return 0; } //------- getTabPlacementNum ------------- // Returns the tabplacement number of the // tabplacement string on success else // the type none on failure. //---------------------------------------- Tab::Placement Tab::getTabPlacementNum(const char *string) { for (int i=0; i<(PNONE / 5); i ++) { if (m_tabplacementlist[i] == string) { return static_cast<Tab::Placement>(m_tabplacementlist[i].tp); } } return PNONE; } //------- getTabAlignmentString ---------- // Returns the tabplacement string of the // tabplacement number on success else 0. //---------------------------------------- const char *Tab::getTabAlignmentString(Tab::Alignment alignment) { for (int i=0; i<ANONE; i++) { if (m_tabalignmentlist[i] == alignment) return m_tabalignmentlist[i].string; } return 0; } //------- getTabAlignmentNum ------------- // Returns the tabplacement number of the // tabplacement string on success else // the type none on failure. //---------------------------------------- Tab::Alignment Tab::getTabAlignmentNum(const char *string) { for (int i=0; i<ANONE; i++) { if (m_tabalignmentlist[i] == string) { return static_cast<Tab::Alignment>(m_tabalignmentlist[i].tp); } } return ANONE; } //---------- addWindowToGroup ------------ // Add a window the the tabbed group //---------------------------------------- bool Tab::addWindowToGroup(FluxboxWindow *otherWindow) { if (!otherWindow || otherWindow == m_win) return false; Tab *otherTab = otherWindow->getTab(); if (!otherTab) return false; insert(otherTab); return true; }