//
// FrameListMenu.cc for pekwm
// Copyright (C) 2002-2004 Claes Nasten <pekdon at pekdon net>
//
// This program is licensed under the GNU GPL.
// See the LICENSE file for more information.
//

#include "../config.h"
#ifdef MENUS

#include "PWinObj.hh"
#include "PDecor.hh"
#include "PMenu.hh"
#include "WORefMenu.hh"
#include "FrameListMenu.hh"

#include "Config.hh"
#include "Client.hh"
#include "Frame.hh"
#include "Workspaces.hh"
#include "Viewport.hh"
#include "WindowManager.hh"

#include <algorithm>
#include <cstdio>

#ifdef DEBUG
#include <iostream>
using std::cerr;
using std::endl;
#endif // DEBUG
using std::string;
using std::list;
using std::vector;

//! @brief FrameListMenu constructor.
FrameListMenu::FrameListMenu(PScreen *scr, Theme *theme, const std::list<Frame*> &frame_list, MenuType type) :
WORefMenu(scr, theme),
_frame_list(frame_list)
{
	_menu_type = type;

	switch(_menu_type) {
	case GOTOMENU_TYPE:
		_title_base = "GoTo";
		break;
	case ICONMENU_TYPE:
		_title_base = "IconMenu";
		break;
	case ATTACH_CLIENT_TYPE:
		_title_base = "Attach Client";
		break;
	case ATTACH_FRAME_TYPE:
		_title_base = "Attach Frame";
		break;
	case ATTACH_CLIENT_IN_FRAME_TYPE:
		_title_base = "Attach Client In Frame";
		break;
	case ATTACH_FRAME_IN_FRAME_TYPE:
		_title_base = "Attach Frame In Frame";
		break;
	case GOTOCLIENTMENU_TYPE:
		_title_base = "GoTo Client";
		break;
	default:
		// do nothing
		break;
	}
}

//! @brief FrameListMenu destructor
FrameListMenu::~FrameListMenu(void)
{
}

// START - PWinObj interface.

//! @brief Rebuilds the menu and if it has any items after it shows it.
void
FrameListMenu::mapWindow(void)
{
	updateFrameListMenu();
	if (size() > 0) {
		WORefMenu::mapWindow();
	}
}

// END - PWinObj interface.

//! @brief
void
FrameListMenu::handleItemExec(PMenu::Item *item)
{
	if (item == NULL) {
		return;
	}

	if (PWinObj::windowObjectExists(_wo_ref) == false) {
		_wo_ref = NULL;
	}
	if (PWinObj::windowObjectExists(item->getWORef()) == false) {
		item->setWORef(NULL);
	}

	switch (_menu_type) {
	case GOTOMENU_TYPE:
	case GOTOCLIENTMENU_TYPE:
		handleGotomenu(dynamic_cast<Client*>(item->getWORef()));
		break;
	case ICONMENU_TYPE:
		handleIconmenu(dynamic_cast<Client*>(item->getWORef()));
		break;
	case ATTACH_CLIENT_TYPE:
	case ATTACH_FRAME_TYPE:
		handleAttach(dynamic_cast<Client*>(_wo_ref), dynamic_cast<Client*>(item->getWORef()),
								 (_menu_type == ATTACH_FRAME_TYPE));
		break;
	case ATTACH_CLIENT_IN_FRAME_TYPE:
	case ATTACH_FRAME_IN_FRAME_TYPE:
		handleAttach(dynamic_cast<Client*>(item->getWORef()), dynamic_cast<Client*>(_wo_ref),
								 (_menu_type == ATTACH_FRAME_IN_FRAME_TYPE));
		break;
	default:
		// do nothing
		break;
	}
}

//! @brief Rebuilds the menu.
void
FrameListMenu::updateFrameListMenu(void)
{
	removeAll();

	char buf[16];
	string name;

	// need to add an action, otherwise it looks as if we don't have anything
	// to exec and thus it doesn't get handled.
	Action action;
	ActionEvent ae;
	ae.action_list.push_back(action);

	// Decide wheter to show clients and iconified.
	bool show_clients = false, show_iconified_only = false;
	if (_menu_type == ATTACH_CLIENT_TYPE || _menu_type == GOTOCLIENTMENU_TYPE) {
		show_clients = true;
	} else if (_menu_type == ICONMENU_TYPE) {
		show_iconified_only = true;
	}

	list<Frame*>::const_iterator it;

	// if we have 1 workspace, we won't put an workspace indicator
	buf[0] = '\0';

	for (uint i = 0; i < Workspaces::instance()->size(); ++i) {
		if (Workspaces::instance()->size() > 1) {
			snprintf(buf, sizeof(buf), "<%d> ", i + 1);
		}

		for (it = _frame_list.begin(); it != _frame_list.end(); ++it) {
			if (((*it)->getWorkspace() == i) && // sort by workspace
			    // don't include ourselves if we're not doing a gotoclient menu
					((_menu_type != GOTOCLIENTMENU_TYPE)
							? ((*it)->getActiveChild() != _wo_ref)
							: true) &&
						(show_iconified_only
							? (*it)->isIconified()
							: !(*it)->isSkip(SKIP_MENUS))) {
				name = buf;

				if (show_clients) {
					buildFrameNames(*it, name);

				} else {
					buildName(*it, name);
					name.append("] ");
					name.append(static_cast<Client*>((*it)->getActiveChild())->getTitle()->getVisible());

					insert(name, ae, (*it)->getActiveChild());
				}
			}
		}
	}

	// remove the last separator, not needed
	if ((show_clients == true) && (size() > 0)) {
		remove(_item_list.back());
	}

	buildMenu();
}

//! @brief Builds the name for the frame.
void
FrameListMenu::buildName(Frame* frame, std::string &name)
{
	// only show viewport information if having more than one
	if ((Config::instance()->getViewportCols() > 1) ||
			(Config::instance()->getViewportRows() > 1)) {
		char buf[64];
		snprintf(buf, 64, "(%ix%i) ",
			Workspaces::instance()->getActiveViewport()->getCol(frame) + 1,
			Workspaces::instance()->getActiveViewport()->getRow(frame) + 1);
		name.append(buf);
	}

	name.append("[");
	if (frame->isSticky())
		name.append("*");
	if (frame->isIconified())
		name.append(".");
	if (frame->isShaded())
		name.append("^");
	if (frame->getActiveChild()->getLayer() > LAYER_NORMAL)
		name.append("+");
	else if (frame->getActiveChild()->getLayer() < LAYER_NORMAL)
		name.append("-");
}

//! @brief Builds names for all the clients in a frame.
void
FrameListMenu::buildFrameNames(Frame *frame, std::string &pre_name)
{
	string name, status_name;

	// need to add an action, otherwise it looks as if we don't have anything
	// to exec and thus it doesn't get handled.
	Action action;
	ActionEvent ae;
	ae.action_list.push_back(action);

	buildName(frame, status_name); // add states to the name

	list<PWinObj*>::iterator it(frame->begin());
	for (; it != frame->end(); ++it) {
		name = pre_name;
		name.append(status_name);
		if (frame->getActiveChild() == *it) {
			name.append("A");
		}
		name.append("] ");
		name.append(static_cast<Client*>(*it)->getTitle()->getVisible());

		insert(name, ae, *it);
	}

	// add separator
	PMenu::Item *item = new PMenu::Item("");
	item->setType(PMenu::Item::MENU_ITEM_SEPARATOR);
	insert(item);
}

//! @brief Handles gotomeu presses
void
FrameListMenu::handleGotomenu(Client *client)
{
	if (client == NULL) {
		return;
	}
	Frame *frame = static_cast<Frame*>(client->getParent());

	// make sure it's on correct workspace
	if ((frame->isSticky() == false) &&
			(frame->getWorkspace() != Workspaces::instance()->getActive())) {
		Workspaces::instance()->setWorkspace(frame->getWorkspace(), false);
	}
	// make sure it isn't hidden
	if (frame->isMapped() == false) {
		frame->mapWindow();
	}
	// make sure the viewport is correct
	if (Workspaces::instance()->getActiveViewport()->isInside(frame) == false) {
		Workspaces::instance()->getActiveViewport()->moveToWO(frame);
	}

	frame->activateChild(client);
	frame->raise();
	frame->giveInputFocus();
}

//! @brief Handles iconmenu presses
void
FrameListMenu::handleIconmenu(Client *client)
{
	if (client == NULL) {
		return;
	}
	Frame *frame = static_cast<Frame*>(client->getParent());

	// make sure it's on the current workspace
	if (frame->getWorkspace() != Workspaces::instance()->getActive()) {
		frame->setWorkspace(Workspaces::instance()->getActive());
	}

	frame->raise();
	frame->mapWindow();
}

//! @brief Handles attach*menu presses
void
FrameListMenu::handleAttach(Client *client_to, Client *client_from, bool frame)
{
	if ((client_to == NULL) || (client_from == NULL)) {
		return;
	}

	Frame *frame_to = static_cast<Frame*>(client_to->getParent());
	Frame *frame_from = static_cast<Frame*>(client_from->getParent());

	// insert frame
	if (frame == true) {
		frame_to->addDecor(frame_from);
	// insert client
	} else if (frame_to != frame_from) {
		frame_from->removeChild(client_from);
		client_from->setWorkspace(frame_to->getWorkspace());
		frame_to->addChild(client_from);
		frame_to->activateChild(client_from);
		frame_to->giveInputFocus();
	}
}

#endif // MENUS


syntax highlighted by Code2HTML, v. 0.9.1