// ToolbarHandler for fluxbox
// Copyright (c) 2003 Simon Bowden (rathnor at fluxbox.org)
//                and Henrik Kinnunen (fluxgen at fluxbox.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: ToolbarHandler.cc,v 1.18 2003/06/26 12:22:42 rathnor Exp $

/**
 * The ToolbarHandler class acts as a rough interface to the toolbar.
 * It deals with whether it should be there or not, so anything that
 * always needs to be accessible must come through the handler.
 */

#include "ToolbarHandler.hh"
#include "Window.hh"
#include "Screen.hh"
#include "Workspace.hh"
#include "MenuItem.hh"
#include "Menu.hh"
#include "FbCommands.hh"
#include "RefCount.hh"
#include "SimpleCommand.hh"
#include "MacroCommand.hh"
#include "IntResMenuItem.hh"
#include "BoolMenuItem.hh"

#include <string>

using namespace std;

template<>
void FbTk::Resource<ToolbarHandler::ToolbarMode>::
setFromString(const char *strval) {
    if (strcasecmp(strval, "Off") == 0) 
        m_value = ToolbarHandler::OFF;
    else if (strcasecmp(strval, "None") == 0) 
        m_value = ToolbarHandler::NONE;
    else if (strcasecmp(strval, "Icons") == 0) 
        m_value = ToolbarHandler::ICONS;
    else if (strcasecmp(strval, "WorkspaceIcons") == 0) 
        m_value = ToolbarHandler::WORKSPACEICONS;
    else if (strcasecmp(strval, "Workspace") == 0) 
        m_value = ToolbarHandler::WORKSPACE;
    else if (strcasecmp(strval, "AllWindows") == 0) 
        m_value = ToolbarHandler::ALLWINDOWS;
    else
        setDefaultValue();
}


template<>
string FbTk::Resource<ToolbarHandler::ToolbarMode>::
getString() {
    switch (m_value) {
    case ToolbarHandler::OFF:
        return string("Off");
        break;
    case ToolbarHandler::NONE:
        return string("None");
        break;
    case ToolbarHandler::LASTMODE:
    case ToolbarHandler::ICONS:
        return string("Icons");
        break;
    case ToolbarHandler::WORKSPACEICONS:
        return string("WorkspaceIcons");
        break;
    case ToolbarHandler::WORKSPACE:
        return string("Workspace");
        break;
    case ToolbarHandler::ALLWINDOWS:
        return string("AllWindows");
        break;
    }
    // default string
    return string("Icons");
}

namespace {

class ToolbarModeMenuItem : public FbTk::MenuItem {
public:
    ToolbarModeMenuItem(const char *label, ToolbarHandler &handler, 
                        ToolbarHandler::ToolbarMode mode, 
                        FbTk::RefCount<FbTk::Command> &cmd):
        FbTk::MenuItem(label, cmd), m_handler(handler), m_mode(mode) {
    }
    bool isEnabled() const { return m_handler.mode() != m_mode; }
    void click(int button, int time) {
        m_handler.setMode(m_mode);
        FbTk::MenuItem::click(button, time);
    }

private:
    ToolbarHandler &m_handler;
    ToolbarHandler::ToolbarMode m_mode;
};

void setupModeMenu(FbTk::Menu &menu, ToolbarHandler &handler) {
    //I18n *i18n = I18n::instance();
    //using namespace FBNLS;
    using namespace FbTk;

    RefCount<Command> saverc_cmd(new SimpleCommand<Fluxbox>(
        *Fluxbox::instance(), 
        &Fluxbox::save_rc));
    
    //TODO: nls
    menu.insert(new ToolbarModeMenuItem("Off", handler, 
                                        ToolbarHandler::OFF, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("None", handler, 
                                        ToolbarHandler::NONE, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Icons", handler, 
                                        ToolbarHandler::ICONS, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Workspace Icons", handler, 
                                        ToolbarHandler::WORKSPACEICONS, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("Workspace", handler, 
                                        ToolbarHandler::WORKSPACE, saverc_cmd));
    menu.insert(new ToolbarModeMenuItem("All Windows", handler, 
                                        ToolbarHandler::ALLWINDOWS, saverc_cmd));
    menu.update();
}
                
}; // end anonymous namespace

ToolbarHandler::ToolbarHandler(BScreen &screen) 
    : m_screen(screen), 
      m_rc_mode(screen.resourceManager(), ToolbarHandler::ICONS,
                screen.name() + ".toolbar.mode", screen.altName() + ".Toolbar.Mode"), 
      m_toolbar(0),
      m_current_workspace(0),
      m_modemenu(*screen.menuTheme(),
                 screen.screenNumber(), screen.imageControl()),
      m_toolbarmenu(*screen.menuTheme(),
                    screen.screenNumber(), screen.imageControl()) {
    m_modemenu.setInternalMenu();
    m_toolbarmenu.setInternalMenu();
    setupModeMenu(m_modemenu, *this);
    setMode(*m_rc_mode, false); // the atomhandler part will initialise it shortly
}

void ToolbarHandler::setMode(ToolbarMode newmode, bool initialise) {
    if (newmode < 0 || newmode >= LASTMODE || (newmode == mode() && initialise)) 
        return;

    *m_rc_mode = newmode;
    
    if (newmode == OFF) {
        m_toolbarmenu.removeAll();
        //TODO: nls
        m_toolbarmenu.insert("Mode...", &m_modemenu);
        m_toolbar.reset(0);
        m_toolbarmenu.update();

        return;
    } else if (!m_toolbar.get()) {
        m_toolbarmenu.removeAll();
        m_toolbar.reset(new Toolbar(m_screen, 
                                    *m_screen.layerManager().getLayer(Fluxbox::instance()->getNormalLayer()), m_toolbarmenu));
        Fluxbox::instance()->load_rc(m_screen);
        m_toolbar->reconfigure();
        
        m_toolbarmenu.insert("Mode...", &m_modemenu);   
        m_toolbarmenu.update();
    }
    

    if (newmode == NONE) {
        // disableIconBar will clean up
        m_toolbar->disableIconBar();
    } else {
        // rebuild it
        // be sure the iconbar is on
        m_toolbar->enableIconBar();
        m_toolbar->delAllIcons();
    }

    if (initialise)
        initForScreen(m_screen);
}

void ToolbarHandler::initForScreen(BScreen &screen) {
    if (&m_screen != &screen) 
        return;

    if (m_toolbar.get() != 0)
        m_toolbar->disableUpdates();

    switch (mode()) {
    case OFF:
        break;
    case NONE:
        break;
    case ALLWINDOWS: {
        BScreen::Workspaces::const_iterator workspace_it = m_screen.getWorkspacesList().begin();
        BScreen::Workspaces::const_iterator workspace_it_end = m_screen.getWorkspacesList().end();
        for (; workspace_it != workspace_it_end; ++workspace_it) {
            Workspace::Windows &wins = (*workspace_it)->windowList();
            Workspace::Windows::iterator wit = wins.begin();
            Workspace::Windows::iterator wit_end = wins.end();
            for (; wit != wit_end; ++wit) {
                if (!m_toolbar->containsIcon(**wit))
                    m_toolbar->addIcon(*wit);
/*
                FluxboxWindow::ClientList::iterator cit = (*wit)->clientList().begin();
                FluxboxWindow::ClientList::iterator cit_end = (*wit)->clientList().end();
                for (; cit != cit_end; ++cit)
                    m_toolbar->addIcon(*(*cit));
*/
            }
        }
    }
    // fall through and add icons
    case LASTMODE:
    case ICONS: {
        BScreen::Icons &iconlist = m_screen.getIconList();
        BScreen::Icons::iterator iconit = iconlist.begin();
        BScreen::Icons::iterator iconit_end = iconlist.end();
        for(; iconit != iconit_end; ++iconit) {
            m_toolbar->addIcon(*iconit);
        }
    }
    break;
    case WORKSPACE: {
        Workspace::Windows &wins = m_screen.currentWorkspace()->windowList();
        Workspace::Windows::iterator wit = wins.begin();
        Workspace::Windows::iterator wit_end = wins.end();
        for (; wit != wit_end; ++wit) {
            if (!m_toolbar->containsIcon(**wit))
                m_toolbar->addIcon(*wit);
        }
    }
    // fall through and add icons for this workspace
    case WORKSPACEICONS: {
        m_current_workspace = m_screen.currentWorkspaceID();
        
        BScreen::Icons &wiconlist = m_screen.getIconList();
        BScreen::Icons::iterator iconit = wiconlist.begin();
        BScreen::Icons::iterator iconit_end = wiconlist.end();
        for(; iconit != iconit_end; ++iconit) {
            if ((*iconit)->workspaceNumber() == m_current_workspace)
                m_toolbar->addIcon(*iconit);
        }
    }
    break;
    }

    if (m_toolbar.get() != 0)
        m_toolbar->enableUpdates();

}

void ToolbarHandler::setupWindow(FluxboxWindow &win) {
    if (&win.screen() != &m_screen)
        return;

    switch (mode()) {
    case OFF:
    case NONE:
        break;
    case WORKSPACE:
        if (win.workspaceNumber() == m_current_workspace)    
            m_toolbar->addIcon(&win);
        break;
    case WORKSPACEICONS:
        if (win.workspaceNumber() != m_current_workspace) 
            break;
        // else fall through and add the icon
    case LASTMODE:
    case ICONS:
        if (win.isIconic()) {
            m_toolbar->addIcon(&win);
        }
        break;
    case ALLWINDOWS:
        m_toolbar->addIcon(&win);
        break;
    }
}

void ToolbarHandler::updateWindowClose(FluxboxWindow &win) {
    if (&win.screen() != &m_screen) 
        return;

    // check status of window (in current workspace, etc) and remove if necessary
    switch (mode()) {
    case OFF:
    case NONE:
        break;
    case WORKSPACEICONS:
        if (win.workspaceNumber() != m_current_workspace) 
            break;
        // else fall through and remove the icon
    case LASTMODE:
    case ICONS:
        if (win.isIconic()) {
            m_toolbar->delIcon(&win);
        }
        break;
    case WORKSPACE:
        if (win.isStuck() || win.workspaceNumber() == m_current_workspace)
            m_toolbar->delIcon(&win);
        break;
    case ALLWINDOWS:
        m_toolbar->delIcon(&win);
        break;
    }
}

void ToolbarHandler::updateState(FluxboxWindow &win) {
    if (&win.screen() != &m_screen)
        return;

    // this function only relevant for icons
    switch (mode()) {
    case OFF:
    case NONE:
    case WORKSPACE:
    case ALLWINDOWS:
        break;
    case WORKSPACEICONS:
        if (win.workspaceNumber() != m_current_workspace)
            break;
        // else fall through and do the same as icons (knowing it is the right ws)
    case LASTMODE:
    case ICONS:
        // if the window is iconic (it mustn't have been before), then add it
        // else remove it
        if (win.isIconic()) {
            if (!m_toolbar->containsIcon(win)) {
                m_toolbar->addIcon(&win);
            }
        } else {
            m_toolbar->delIcon(&win);
        }
        break;
    }
}
        

void ToolbarHandler::updateWorkspace(FluxboxWindow &win) {
    if (&win.screen() != &m_screen) 
        return;

    // don't care about current workspace except if in workspace mode
    if (!(mode() == WORKSPACE || 
          (mode() == WORKSPACEICONS && win.isIconic()))) 
        return;
    
    if (win.workspaceNumber() == m_current_workspace) {
        //!! TODO
        // this shouldn't be needed, but is until Workspaces get fixed so that
        // you only move between them, you don't 'add' and 'remove'
        // alternatively, fix reassocaiteWindow so that the iconic stuff is
        // done elsewhere
        if (!m_toolbar->containsIcon(win))
            m_toolbar->addIcon(&win);
    } else {
        // relies on the fact that this runs but does nothing if window isn't contained.
        if (!win.isStuck())
            m_toolbar->delIcon(&win);
    }
}

void ToolbarHandler::updateCurrentWorkspace(BScreen &screen) {
    if (&screen != &m_screen)
        return;
    // if only displaying current workspace, update list
    // otherwise ignore it
    if (mode() != WORKSPACE && mode() != WORKSPACEICONS)
        return;
    m_toolbar->disableUpdates();
    m_toolbar->delAllIcons(true);
    initForScreen(m_screen);
    m_toolbar->enableUpdates();
    m_toolbar->redrawWorkspaceLabel(true);
}