// IconBar.cc for Fluxbox Window Manager
// Copyright (c) 2001 - 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: IconBar.cc,v 1.32 2003/04/28 16:48:22 rathnor Exp $

#include "IconBar.hh"

#include "i18n.hh"
#include "Screen.hh"
#include "Window.hh"
#include "ImageControl.hh"
#include "Text.hh"
#include "RootTheme.hh"

#include <algorithm>

//!! TODO THIS FILE NEEDS CLEANING

IconBarObj::IconBarObj(FluxboxWindow *fluxboxwin, Window iconwin)
{
    m_fluxboxwin = fluxboxwin;
    m_iconwin = iconwin;
}

IconBarObj::~IconBarObj() {
	
}

/// @return the width of specified icon window
unsigned int IconBarObj::width() const {
    Window root;

    unsigned int width, height;
    unsigned int border_width, depth;	//not used
    int x, y; //not used

    Display *disp = FbTk::App::instance()->display();

    XGetGeometry(disp, m_iconwin, &root, &x, &y, 
                 &width, &height, &border_width, &depth);

    return width;
}

/// @return the width of specified icon window
unsigned int IconBarObj::height() const {
    Window root;

    unsigned int width, height;
    unsigned int border_width, depth;	//not used
    int x, y; //not used

    Display *disp = FbTk::App::instance()->display();

    XGetGeometry(disp, m_iconwin, &root, &x, &y, 
                 &width, &height, &border_width, &depth);

    return height;
}


IconBar::IconBar(BScreen &scrn, Window parent, FbTk::Font &font):
    m_screen(scrn),
    m_display(FbTk::App::instance()->display()),
    m_parent(parent),
    m_focus_pm(None),
    m_vertical(false),
    m_font(font)
{

}

IconBar::~IconBar() {
}

/**
 Adds icon to iconbar and repostions the 
 icons.
 returns window to iconobj
*/
Window IconBar::addIcon(FluxboxWindow *fluxboxwin) {
	
    Window iconwin = createIconWindow(fluxboxwin, m_parent);
    decorate(iconwin);	
    //add window object to list	
    m_iconlist.push_back(new IconBarObj(fluxboxwin, iconwin));
    //reposition all icons to fit windowbar
    repositionIcons();
	
    XMapSubwindows(m_display, iconwin);
    XMapWindow(m_display, iconwin);	
	
    return iconwin;
}

/**
  Removes icon from list and
  repositions the rest of the icons
  Return X Window of the removed iconobj
  returns None if no window was found
*/
Window IconBar::delIcon(FluxboxWindow *fluxboxwin) {
    Window retwin = None;
    IconBarObj *obj = findIcon(fluxboxwin);
    if (obj) {
        IconList::iterator it =
            std::find(m_iconlist.begin(), m_iconlist.end(), obj);
        if (it != m_iconlist.end()) {
            m_iconlist.erase(it);
            retwin = obj->getIconWin();		
            delete obj;
            XDestroyWindow(m_display, retwin);
            repositionIcons();		
        }
    }		
    return retwin;
}

/**
 * Removes all icons from list
 * Return X Windows of the removed iconobjs
 */
IconBar::WindowList *IconBar::delAllIcons() {
    Window retwin = None;
    WindowList *ret = new WindowList();
    while (!m_iconlist.empty()) {
            IconBarObj *obj = m_iconlist.back();
            m_iconlist.pop_back();
            retwin = obj->getIconWin();
            ret->push_back(retwin);
            delete obj;
            XDestroyWindow(m_display, retwin);
    }
    repositionIcons();
    return ret;
}

/**
 renders theme to m_focus_pm
 with the size width * height

*/
void IconBar::loadTheme(unsigned int width, unsigned int height) {
    //!! TODO: iconbar style theme

    FbTk::ImageControl *image_ctrl = screen().getImageControl();
    Pixmap tmp = m_focus_pm;
    const FbTk::Texture *texture = &(screen().winFrameTheme().labelFocusTexture());
	
    //If we are working on a PARENTRELATIVE, change to right focus value
    if (texture->type() & FbTk::Texture::PARENTRELATIVE ) {
        texture = &(screen().winFrameTheme().titleFocusTexture());
    }
		
    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(width, height, *texture);
    }

    if (tmp)
        image_ctrl->removeImage(tmp);
}

/**
 sets the background pixmap/color, 
 border, border width of the window
*/
void IconBar::decorate(Window win) {
    //!! TODO iconbar border width style
    XSetWindowBorderWidth(m_display, win, 1);
    XSetWindowBorder(m_display, win, BlackPixel(FbTk::App::instance()->display(),
                                                screen().getScreenNumber())); 
    if (m_focus_pm)
        XSetWindowBackgroundPixmap(m_display, win, m_focus_pm);
    else
        XSetWindowBackground(m_display, win, m_focus_pixel);
}


/**
  Reconfigures the icons 
  theme, pos and width
*/
void IconBar::reconfigure() {	
    repositionIcons();
	
}

/**
 Handles the expose event
 just redraws all the icons
*/
void IconBar::exposeEvent(XExposeEvent *ee) {
    IconBarObj *obj=0;	
    IconList::iterator it = m_iconlist.begin();
    IconList::iterator it_end = m_iconlist.end();
    for (; it != it_end; ++it) {
        if ((*it)->getIconWin() == ee->window) {
            obj = (*it);
            break;
        }
    }	

    if (obj) {

        Window root;
        unsigned int width, height;
        unsigned int border_width, depth;	//not used
        int x, y;
        XGetGeometry(m_display, m_parent, &root, &x, &y, &width, &height,
                     &border_width, &depth);

        //max width on every icon
        unsigned int icon_width = width / m_iconlist.size();
	
        //load right size of theme
        loadTheme(icon_width, height);
				
        draw(obj, icon_width);
		
    }	
}

/**
 Calculates and moves/resizes the icons
*/
void IconBar::repositionIcons() {
    if (m_iconlist.size() == 0)
        return;
		
    Window root;
    unsigned int width, height;
    unsigned int border_width, depth;	//not used
    int x, y;
    XGetGeometry(m_display, m_parent, &root, &x, &y, &width, &height,
                 &border_width, &depth);
	
    //max width on every icon
    unsigned int icon_width = width / m_iconlist.size();
    if (m_vertical)
        icon_width = height / m_iconlist.size();
    //load right size of theme
    loadTheme(icon_width, height);

    IconList::iterator it = m_iconlist.begin();
    IconList::iterator it_end = m_iconlist.end();
    for (x = 0; it != it_end; ++it, x += icon_width) {
        Window iconwin = (*it)->getIconWin();
        if (!m_vertical) {
            XMoveResizeWindow(m_display, iconwin,
                              x, 0,
                              icon_width, height);	
        } else {
            XMoveResizeWindow(m_display, iconwin,
                              0, x,
                              height, icon_width);	
        }
        draw((*it), icon_width);
        decorate(iconwin);
    }
		
}

/**
 creates the X Window of icon
 @return the created X Window
*/
Window IconBar::createIconWindow(FluxboxWindow *fluxboxwin, Window parent) {
    unsigned long attrib_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
        CWColormap | CWOverrideRedirect | CWEventMask;
    XSetWindowAttributes attrib;
    attrib.background_pixmap = None;
    attrib.background_pixel = attrib.border_pixel =
        BlackPixel(m_display,
                   screen().getScreenNumber());
    //        fluxboxwin->getScreen().getWindowStyle()->tab.border_color.pixel();
    attrib.colormap = fluxboxwin->getScreen().colormap();
    attrib.override_redirect = True;
    attrib.event_mask = ButtonPressMask | ButtonReleaseMask |
        ButtonMotionMask | ExposureMask | EnterWindowMask;
											
    //create iconwindow
    Window iconwin = XCreateWindow(m_display, parent, 0, 0, 1, 1, 0,
                                   fluxboxwin->getScreen().getDepth(), InputOutput, fluxboxwin->getScreen().getVisual(),
                                   attrib_mask, &attrib);
		
    return iconwin;
}

/**
 Draws theme and string to Window w
*/
void IconBar::draw(const IconBarObj * const obj, int width) const {
    if (!obj)
        return;
	
    const FluxboxWindow * const fluxboxwin = obj->getFluxboxWin();
    Window iconwin = obj->getIconWin();
    unsigned int title_text_w;

    title_text_w = m_font.textWidth(
        fluxboxwin->getIconTitle().c_str(), fluxboxwin->getIconTitle().size());

    unsigned int bevel_w = screen().rootTheme().bevelWidth();
    int dx=bevel_w*2;

    // center by default
    unsigned int newlen = 0;
    dx = FbTk::doAlignment(m_vertical ? obj->height() : obj->width(), 
                           bevel_w*2, FbTk::CENTER, m_font,
                           fluxboxwin->getIconTitle().c_str(), 
                           fluxboxwin->getIconTitle().size(),
                           newlen);
    //Draw title to m_iconwin

    XClearWindow(m_display, iconwin);		
    int dy = 1 + m_font.ascent();
    if (m_vertical) {
        int tmp = dy;
        dy = obj->height() - dx;
        dx = tmp + bevel_w;
    } else 
        dy += bevel_w;

    m_font.drawText(
        iconwin,
        screen().getScreenNumber(),
        screen().winFrameTheme().labelTextFocusGC(),
        fluxboxwin->getIconTitle().c_str(), newlen,
        dx, dy,	m_vertical);

}

/**
 Tries to find the FluxboxWindow of the X Window
 in iconbar
 @return the fluxboxwindow on success else 0 on failure
*/
FluxboxWindow *IconBar::findWindow(Window w) {

    IconList::iterator it = m_iconlist.begin();
    IconList::iterator it_end = m_iconlist.end();
    for (; it != it_end; ++it) {
        IconBarObj *tmp = (*it);
        if (tmp)
            if (tmp->getIconWin() == w)
                return tmp->getFluxboxWin();			
    }
	
    return 0;
}

/*
 Tries to find a fluxboxwin icon in the iconlist
 @return pointer to IconBarObj on success else 0 on failure
*/
IconBarObj *IconBar::findIcon(FluxboxWindow *fluxboxwin) {

    IconList::iterator it = m_iconlist.begin();
    IconList::iterator it_end = m_iconlist.end();
    for (; it != it_end; ++it) {
        IconBarObj *tmp = (*it);
        if (tmp)
            if (tmp->getFluxboxWin() == fluxboxwin)
                return tmp;			
    }
	
    return 0;
}

const IconBarObj *IconBar::findIcon(const FluxboxWindow * const fluxboxwin) const {

    IconList::const_iterator it = m_iconlist.begin();
    IconList::const_iterator it_end = m_iconlist.end();
    for (; it != it_end; ++it) {
        IconBarObj *tmp = (*it);
        if (tmp)
            if (tmp->getFluxboxWin() == fluxboxwin)
                return tmp;			
    }
	
    return 0;
}