// Slit.cc for Blackbox - an X11 Window manager
// Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.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: Slit.cc,v 1.20 2002/08/14 21:51:07 fluxgen Exp $

//use GNU extensions
#ifndef	 _GNU_SOURCE
#define	 _GNU_SOURCE
#endif // _GNU_SOURCE

#ifdef		HAVE_CONFIG_H
#	include "../config.h"
#endif // HAVE_CONFIG_H

#ifdef		SLIT

#include <X11/keysym.h>

#include "i18n.hh"
#include "fluxbox.hh"
#include "Image.hh"
#include "Screen.hh"
#include "Slit.hh"
#include "Toolbar.hh"

#include <algorithm>
#include <iostream>
#include <cassert>

#ifdef HAVE_SYS_STAT_H
#	include <sys/types.h>
#	include <sys/stat.h>
#endif // HAVE_SYS_STAT_H


// Utility method for extracting name from window
namespace {
	void getWMName(BScreen *screen, Window window, std::string& name) {
		name = "";
	
		if (screen != 0 && window != None) {
			Display *display = screen->getBaseDisplay()->getXDisplay();
	
			XTextProperty text_prop;
			char **list;
			int num;
			I18n *i18n = I18n::instance();
		
			if (XGetWMName(display, window, &text_prop)) {
				if (text_prop.value && text_prop.nitems > 0) {
					if (text_prop.encoding != XA_STRING) {
						
						text_prop.nitems = strlen((char *) text_prop.value);
						
						if ((XmbTextPropertyToTextList(display, &text_prop,
									&list, &num) == Success) &&
								(num > 0) && *list) {
							name = static_cast<char *>(*list);
							XFreeStringList(list);
						} else
							name = (char *)text_prop.value;
							
					} else
						name = (char *)text_prop.value;
				} else
					name = i18n->getMessage(
						FBNLS::WindowSet, FBNLS::WindowUnnamed,
						"Unnamed");
			} else {
				name = i18n->getMessage(
					FBNLS::WindowSet, FBNLS::WindowUnnamed,
					"Unnamed");
			}
		}
	}
}; // End anonymous namespace

Slit::Slit(BScreen *scr):screen(scr), timer(this), slitmenu(*this) {
	assert(scr);
	fluxbox = Fluxbox::instance();

	on_top = screen->isSlitOnTop();
	hidden = do_auto_hide = screen->doSlitAutoHide();

	display = screen->getBaseDisplay()->getXDisplay();
	frame.window = frame.pixmap = None;

	
	timer.setTimeout(fluxbox->getAutoRaiseDelay());
	timer.fireOnce(True);

	XSetWindowAttributes attrib;
	unsigned long create_mask = CWBackPixmap | CWBackPixel | CWBorderPixel |
		CWColormap | CWOverrideRedirect | CWEventMask;
	attrib.background_pixmap = None;
	attrib.background_pixel = attrib.border_pixel =
		screen->getBorderColor()->pixel();
	attrib.colormap = screen->colormap();
	attrib.override_redirect = True;
	attrib.event_mask = SubstructureRedirectMask | ButtonPressMask |
		EnterWindowMask | LeaveWindowMask;

	frame.x = frame.y = 0;
	frame.width = frame.height = 1;

	frame.window =
		XCreateWindow(display, screen->getRootWindow(), frame.x, frame.y,
			frame.width, frame.height, screen->getBorderWidth(),
			screen->getDepth(), InputOutput, screen->getVisual(),
			create_mask, &attrib);
	fluxbox->saveSlitSearch(frame.window, this);

	// Get client list for sorting purposes
	loadClientList();

	reconfigure();
}


Slit::~Slit() {
	fluxbox->grab();

	screen->getImageControl()->removeImage(frame.pixmap);

	fluxbox->removeSlitSearch(frame.window);

	XDestroyWindow(display, frame.window);

	fluxbox->ungrab();
}


void Slit::addClient(Window w) {
	//Can't add non existent window
	if (w == None)
		return;

	fluxbox->grab();
	if (fluxbox->validateWindow(w)) {
		// Look for slot in client list by name
		SlitClient *client = 0;
		std::string match_name;
		::getWMName(screen, w, match_name);
		SlitClients::iterator it = clientList.begin();
		SlitClients::iterator it_end = clientList.end();
		bool found_match = false;
		for (; it != it_end; ++it) {
			// If the name matches...
			if ((*it)->match_name == match_name) {
				// Use the slot if no window is assigned
				if ((*it)->window == None) {
					client = (*it);
					client->initialize(screen, w);
					break;
				}
				// Otherwise keep looking for an unused match or a non-match
				found_match = true;		// Possibly redundant
				
			} else if (found_match) {
				// Insert before first non-match after a previously found match?
				client = new SlitClient(screen, w);
				clientList.insert(it, client);
				break;
			}
		}
		// Append to client list?
		if (client == 0) {
			client = new SlitClient(screen, w);
			clientList.push_back(client);
		}

		XWMHints *wmhints = XGetWMHints(display, w);

		if (wmhints) {
			if ((wmhints->flags & IconWindowHint) &&
					(wmhints->icon_window != None)) {
				XMoveWindow(display, client->client_window, screen->getWidth() + 10,
					screen->getHeight() + 10);
				XMapWindow(display, client->client_window);				
				client->icon_window = wmhints->icon_window;
				client->window = client->icon_window;
			} else {
				client->icon_window = None;
				client->window = client->client_window;
			}

			XFree(wmhints);
		} else {
			client->icon_window = None;
			client->window = client->client_window;
		}
		XWindowAttributes attrib;
	#ifdef KDE
		//Check and see if new client is a KDE dock applet
		//If so force reasonable size
		bool iskdedockapp=false;
		Atom ajunk;
		int ijunk;
		unsigned long *data = (unsigned long *) 0, uljunk;

		// Check if KDE v2.x dock applet
		if (XGetWindowProperty(fluxbox->getXDisplay(), w,
				fluxbox->getKWM2DockwindowAtom(), 0l, 1l, False,
				fluxbox->getKWM2DockwindowAtom(),
				&ajunk, &ijunk, &uljunk, &uljunk,
				(unsigned char **) &data) == Success) {
			iskdedockapp = (data && data[0] != 0);
			XFree((char *) data);
		}

		// Check if KDE v1.x dock applet
		if (!iskdedockapp) {
			if (XGetWindowProperty(fluxbox->getXDisplay(), w,
					fluxbox->getKWM1DockwindowAtom(), 0l, 1l, False,
					fluxbox->getKWM1DockwindowAtom(),
					&ajunk, &ijunk, &uljunk, &uljunk,
					(unsigned char **) &data) == Success) {
				iskdedockapp = (data && data[0] != 0);
	 		    XFree((char *) data);
	  	 	}
		}

		if (iskdedockapp)
			client->width = client->height = 24;
		else
	#endif // KDE
		{
			if (XGetWindowAttributes(display, client->window, &attrib)) {
				client->width = attrib.width;
				client->height = attrib.height;
			} else {
				client->width = client->height = 64;
			}
		}

		XSetWindowBorderWidth(display, client->window, 0);

		XSelectInput(display, frame.window, NoEventMask);
		XSelectInput(display, client->window, NoEventMask);

		XReparentWindow(display, client->window, frame.window, 0, 0);
		XMapRaised(display, client->window);
		XChangeSaveSet(display, client->window, SetModeInsert);

		XSelectInput(display, frame.window, SubstructureRedirectMask |
			ButtonPressMask | EnterWindowMask | LeaveWindowMask);
		XSelectInput(display, client->window, StructureNotifyMask |
			SubstructureNotifyMask | EnterWindowMask);
		XFlush(display);

		fluxbox->saveSlitSearch(client->client_window, this);
		fluxbox->saveSlitSearch(client->icon_window, this);
		reconfigure();

		saveClientList();
	}

	fluxbox->ungrab();
}


void Slit::removeClient(SlitClient *client, bool remap, bool destroy) {
	fluxbox->removeSlitSearch(client->client_window);
	fluxbox->removeSlitSearch(client->icon_window);

	// Destructive removal?
	if (destroy)
	    clientList.remove(client);
	else // Clear the window info, but keep around to help future sorting?
		client->initialize();

	screen->removeNetizen(client->window);

	if (remap && fluxbox->validateWindow(client->window)) {
		XSelectInput(display, frame.window, NoEventMask);
		XSelectInput(display, client->window, NoEventMask);
		XReparentWindow(display, client->window, screen->getRootWindow(),
			client->x, client->y);
		XChangeSaveSet(display, client->window, SetModeDelete);
		XSelectInput(display, frame.window, SubstructureRedirectMask |
			ButtonPressMask | EnterWindowMask | LeaveWindowMask);
		XFlush(display);
	}

	// Destructive removal?
	if (destroy)
		delete client;
}


void Slit::removeClient(Window w, bool remap) {
	fluxbox->grab();

	bool reconf = false;

	SlitClients::iterator it = clientList.begin();
	SlitClients::iterator it_end = clientList.end();
	for (; it != it_end; ++it) {
		if ((*it)->window == w) {
			removeClient((*it), remap, false);
			reconf = true;

			break;
		}
	}
	if (reconf) reconfigure();

	fluxbox->ungrab();
}


void Slit::reconfigure(void) {
	frame.width = 0;
	frame.height = 0;

	// Need to count windows because not all client list entries
	// actually correspond to mapped windows.
	int num_windows = 0;

	switch (screen->getSlitDirection()) {
	case VERTICAL:
		{
			SlitClients::iterator it = clientList.begin();
			SlitClients::iterator it_end = clientList.end();
			for (; it != it_end; ++it) {
				//client created window?
				if ((*it)->window != None) {
					num_windows++;
					frame.height += (*it)->height + screen->getBevelWidth();
					
					//frame width < client window?
					if (frame.width < (*it)->width) 
						frame.width = (*it)->width;
				}
			}
		}

		if (frame.width < 1)
			frame.width = 1;
		else
			frame.width += (screen->getBevelWidth() * 2);

		if (frame.height < 1)
			frame.height = 1;
		else
			frame.height += screen->getBevelWidth();

		break;

	case HORIZONTAL:
		{
			SlitClients::iterator it = clientList.begin();
			SlitClients::iterator it_end = clientList.end();
			for (; it != it_end; ++it) {
				//client created window?
				if ((*it)->window != None) {
					num_windows++;
					frame.width += (*it)->width + screen->getBevelWidth();
					//frame height < client height?
					if (frame.height < (*it)->height)
						frame.height = (*it)->height;
				}
			}
		}

		if (frame.width < 1)
			frame.width = 1;
		else
			frame.width += screen->getBevelWidth();

		if (frame.height < 1)
			frame.height = 1;
		else
			frame.height += (screen->getBevelWidth() * 2);

		break;
	}

	reposition();

	XSetWindowBorderWidth(display ,frame.window, screen->getBorderWidth());
	XSetWindowBorder(display, frame.window,
		screen->getBorderColor()->pixel());

	//did we actually use slit slots
	if (num_windows == 0)
		XUnmapWindow(display, frame.window);
	else
		XMapWindow(display, frame.window);

	Pixmap tmp = frame.pixmap;
	BImageControl *image_ctrl = screen->getImageControl();
	const FbTk::Texture *texture = &(screen->getTheme()->getSlitTexture());
	if (texture->type() == (FbTk::Texture::FLAT | FbTk::Texture::SOLID)) {
		frame.pixmap = None;
		XSetWindowBackground(display, frame.window,
			 texture->color().pixel());
	} else {
		frame.pixmap = image_ctrl->renderImage(frame.width, frame.height,
			texture);
		XSetWindowBackgroundPixmap(display, frame.window, frame.pixmap);
	}

	if (tmp) 
		image_ctrl->removeImage(tmp);
	XClearWindow(display, frame.window);

	int x, y;

	switch (screen->getSlitDirection()) {
	case VERTICAL:
		x = 0;
		y = screen->getBevelWidth();

		{
			SlitClients::iterator it = clientList.begin();
			SlitClients::iterator it_end = clientList.end();
			for (; it != it_end; ++it) {
				//client created window?
				if ((*it)->window == None)
					continue;

				x = (frame.width - (*it)->width) / 2;

				XMoveResizeWindow(display, (*it)->window, x, y,
					(*it)->width, (*it)->height);
				XMapWindow(display, (*it)->window);

				// for ICCCM compliance
				(*it)->x = x;
				(*it)->y = y;

				XEvent event;
				event.type = ConfigureNotify;

				event.xconfigure.display = display;
				event.xconfigure.event = (*it)->window;
				event.xconfigure.window = (*it)->window;
				event.xconfigure.x = x;
				event.xconfigure.y = y;
				event.xconfigure.width = (*it)->width;
				event.xconfigure.height = (*it)->height;
				event.xconfigure.border_width = 0;
				event.xconfigure.above = frame.window;
				event.xconfigure.override_redirect = False;

				XSendEvent(display, (*it)->window, False, StructureNotifyMask,
					&event);

				y += (*it)->height + screen->getBevelWidth();
			}
		}

		break;

	case HORIZONTAL:
		x = screen->getBevelWidth();
		y = 0;

		{
			SlitClients::iterator it = clientList.begin();
			SlitClients::iterator it_end = clientList.end();
			for (; it != it_end; ++it) {
				//client created window?
				if ((*it)->window == None)
					continue;

				y = (frame.height - (*it)->height) / 2;

				XMoveResizeWindow(display, (*it)->window, x, y,
					(*it)->width, (*it)->height);
				XMapWindow(display, (*it)->window);

				// for ICCCM compliance
				(*it)->x = x;
				(*it)->y = y;

				XEvent event;
				event.type = ConfigureNotify;

				event.xconfigure.display = display;
				event.xconfigure.event = (*it)->window;
				event.xconfigure.window = (*it)->window;
				event.xconfigure.x = frame.x + x + screen->getBorderWidth();
				event.xconfigure.y = frame.y + y + screen->getBorderWidth();
				event.xconfigure.width = (*it)->width;
				event.xconfigure.height = (*it)->height;
				event.xconfigure.border_width = 0;
				event.xconfigure.above = frame.window;
				event.xconfigure.override_redirect = False;

				XSendEvent(display, (*it)->window, False, StructureNotifyMask,
					&event);

				x += (*it)->width + screen->getBevelWidth();
			}
		}

		break;
	}

	slitmenu.reconfigure();
}


void Slit::reposition() {
	int head_x = 0,
			head_y = 0,
			head_w,
			head_h;
#ifdef XINERAMA
	if (screen->hasXinerama()) {
		unsigned int head = screen->getSlitOnHead();

		head_x = screen->getHeadX(head);
		head_y = screen->getHeadY(head);
		head_w = screen->getHeadWidth(head);
		head_h = screen->getHeadHeight(head);
	} else {
		head_w = screen->getWidth();
		head_h = screen->getHeight();
	}
#else // !XINERAMA
		head_w = screen->getWidth();
		head_h = screen->getHeight();
#endif // XINERAMA

	// place the slit in the appropriate place
	switch (screen->getSlitPlacement()) {
	case TOPLEFT:
		frame.x = head_x;
		frame.y = head_y;
		if (screen->getSlitDirection() == VERTICAL) {
			frame.x_hidden = screen->getBevelWidth() -
				screen->getBorderWidth() - frame.width;
			frame.y_hidden = head_y;
		} else {
			frame.x_hidden = head_x;
			frame.y_hidden = screen->getBevelWidth() -
				screen->getBorderWidth() - frame.height;
		}
		break;

	case CENTERLEFT:
		frame.x = head_x;
		frame.y = head_y + (head_h - frame.height) / 2;
		frame.x_hidden = head_x + screen->getBevelWidth() -
			screen->getBorderWidth() - frame.width;
		frame.y_hidden = frame.y;
		break;

	case BOTTOMLEFT:
		frame.x = head_x;
		frame.y = head_h - frame.height - screen->getBorderWidth2x();
		if (screen->getSlitDirection() == VERTICAL) {
			frame.x_hidden = head_x + screen->getBevelWidth() -
				screen->getBorderWidth() - frame.width;
			frame.y_hidden = frame.y;
		} else {
			frame.x_hidden = head_x;
			frame.y_hidden = head_y + head_h -
				screen->getBevelWidth() - screen->getBorderWidth();
		}
		break;

	case TOPCENTER:
		frame.x = head_x + ((head_w - frame.width) / 2);
		frame.y = head_y;
		frame.x_hidden = frame.x;
		frame.y_hidden = head_y + screen->getBevelWidth() -
			screen->getBorderWidth() - frame.height;
		break;

	case BOTTOMCENTER:
		frame.x = head_x + ((head_w - frame.width) / 2);
		frame.y = head_y + head_h - frame.height - screen->getBorderWidth2x();
		frame.x_hidden = frame.x;
		frame.y_hidden = head_y + head_h -
			screen->getBevelWidth() - screen->getBorderWidth();
		break;

	case TOPRIGHT:
		frame.x = head_x + head_w - frame.width - screen->getBorderWidth2x();
		frame.y = head_y;
		if (screen->getSlitDirection() == VERTICAL) {
			frame.x_hidden = head_x + head_w -
				screen->getBevelWidth() - screen->getBorderWidth();
			frame.y_hidden = head_y;
		} else {
			frame.x_hidden = frame.x;
			frame.y_hidden = head_y + screen->getBevelWidth() -
				screen->getBorderWidth() - frame.height;
		}
		break;

	case CENTERRIGHT:
	default:
		frame.x = head_x + head_w - frame.width - screen->getBorderWidth2x();
		frame.y = head_y + ((head_h - frame.height) / 2);
		frame.x_hidden = head_x + head_w -
			screen->getBevelWidth() - screen->getBorderWidth();
		frame.y_hidden = frame.y;
		break;

	case BOTTOMRIGHT:
		frame.x = head_x + head_w - frame.width - screen->getBorderWidth2x();
		frame.y = head_y + head_h - frame.height - screen->getBorderWidth2x();
		if (screen->getSlitDirection() == VERTICAL) {
			frame.x_hidden = head_x + head_w - 
				screen->getBevelWidth() - screen->getBorderWidth();
			frame.y_hidden = frame.y;
		} else {
			frame.x_hidden = frame.x;
			frame.y_hidden = head_y + head_h - 
				screen->getBevelWidth() - screen->getBorderWidth();
		}
		break;
	}

	Toolbar *tbar = screen->getToolbar();
	int sw = frame.width + screen->getBorderWidth2x(),
		sh = frame.height + screen->getBorderWidth2x(),
		tw = tbar->getWidth() + screen->getBorderWidth(),
		th = tbar->getHeight() + screen->getBorderWidth();

	if (tbar->getX() < frame.x + sw && tbar->getX() + tw > frame.x &&
			tbar->getY() < frame.y + sh && tbar->getY() + th > frame.y) {
		if (frame.y < th) {
			frame.y += tbar->getExposedHeight();
			if (screen->getSlitDirection() == VERTICAL)
				frame.y_hidden += tbar->getExposedHeight();
			else
				frame.y_hidden = frame.y;
		} else {
			frame.y -= tbar->getExposedHeight();
			if (screen->getSlitDirection() == VERTICAL)
				frame.y_hidden -= tbar->getExposedHeight();
			else
				frame.y_hidden = frame.y;
		}
	}

	if (hidden)
		XMoveResizeWindow(display, frame.window, frame.x_hidden,
			frame.y_hidden, frame.width, frame.height);
	else
		XMoveResizeWindow(display, frame.window, frame.x,
			frame.y, frame.width, frame.height);
}


void Slit::shutdown() {
	saveClientList();
	while (clientList.size() != 0)
		removeClient(clientList.front(), true, true);
}


void Slit::buttonPressEvent(XButtonEvent *e) {
	if (e->window != frame.window) return;

	if (e->button == Button1 && (! on_top)) {
		Window w[1] = { frame.window };
		screen->raiseWindows(w, 1);
	} else if (e->button == Button2 && (! on_top)) {
		XLowerWindow(display, frame.window);
	} else if (e->button == Button3) {
		if (! slitmenu.isVisible()) {
			int x = e->x_root - (slitmenu.width() / 2),
				y = e->y_root - (slitmenu.height() / 2); 

			if (x < 0)
				x = 0;
			else if (x + slitmenu.width() > screen->getWidth())
				x = screen->getWidth() - slitmenu.width();

			if (y < 0)
				y = 0;
			else if (y + slitmenu.height() > screen->getHeight())
				y = screen->getHeight() - slitmenu.height();

			slitmenu.move(x, y);
			slitmenu.show();
		} else
			slitmenu.hide();
	}
}


void Slit::enterNotifyEvent(XCrossingEvent *) {
	if (! do_auto_hide)
		return;

	if (hidden) {
		if (! timer.isTiming()) 
			timer.start();
	} else {
		if (timer.isTiming()) 
			timer.stop();
	}
}


void Slit::leaveNotifyEvent(XCrossingEvent *) {
	if (! do_auto_hide)
		return;

	if (hidden) {
		if (timer.isTiming()) 
			timer.stop();
	} else if (! slitmenu.isVisible()) {
		if (! timer.isTiming()) 
			timer.start();
	}
}


void Slit::configureRequestEvent(XConfigureRequestEvent *e) {
	fluxbox->grab();

	if (fluxbox->validateWindow(e->window)) {
		bool reconf = false;
		XWindowChanges xwc;

		xwc.x = e->x;
		xwc.y = e->y;
		xwc.width = e->width;
		xwc.height = e->height;
		xwc.border_width = 0;
		xwc.sibling = e->above;
		xwc.stack_mode = e->detail;

		XConfigureWindow(display, e->window, e->value_mask, &xwc);

		SlitClients::iterator it = clientList.begin();
		SlitClients::iterator it_end = clientList.end();
		for (; it != it_end; ++it)
			if ((*it)->window == e->window)
				if ((*it)->width != ((unsigned) e->width) ||
						(*it)->height != ((unsigned) e->height)) {
					(*it)->width = (unsigned) e->width;
					(*it)->height = (unsigned) e->height;

					reconf = true;

					break;
				}

		if (reconf) reconfigure();

	}

	fluxbox->ungrab();
}


void Slit::timeout() {
	hidden = ! hidden;
	if (hidden)
		XMoveWindow(display, frame.window, frame.x_hidden, frame.y_hidden);
	else
		XMoveWindow(display, frame.window, frame.x, frame.y);
}

void Slit::loadClientList() {
	const std::string &filename = fluxbox->getSlitlistFilename();
	struct stat buf;
	if (!stat(filename.c_str(), &buf)) {
		std::ifstream file(fluxbox->getSlitlistFilename().c_str());
		std::string name;
		while (! file.eof()) {
			name = "";
			std::getline(file, name); // get the entire line
			if (name.size() > 0) {
				SlitClient *client = new SlitClient(name.c_str());
				clientList.push_back(client);
			}
		}
	}
}

void Slit::saveClientList(void) {
	const std::string &filename = fluxbox->getSlitlistFilename();
	std::ofstream file(filename.c_str());
	SlitClients::iterator it = clientList.begin();
	SlitClients::iterator it_end = clientList.end();
	std::string prevName;
	std::string name;
	for (; it != it_end; ++it) {
		name = (*it)->match_name;
		if (name != prevName)
			file << name.c_str() << std::endl;

		prevName = name;
	}
}


Slitmenu::Slitmenu(Slit &sl) : Basemenu(sl.screen),
slit(sl) {

	I18n *i18n = I18n::instance();
	using namespace FBNLS;
	setLabel(i18n->getMessage(
		SlitSet, SlitSlitTitle,
		"Slit"));
	setInternalMenu();

	directionmenu = new Directionmenu(this);
	placementmenu = new Placementmenu(this);
#ifdef XINERAMA
	if (screen()->hasXinerama()) { // only create if we need
		headmenu = new Headmenu(this);
	}
#endif // XINERAMA

	insert(i18n->getMessage(
		CommonSet, CommonDirectionTitle,
		"Direction"),
	 directionmenu);
	insert(i18n->getMessage(
		CommonSet, CommonPlacementTitle,
		"Placement"),
	 placementmenu);

#ifdef XINERAMA
	//TODO: NLS
	if (screen()->hasXinerama()) {
		insert(i18n->getMessage(0, 0, "Place on Head"), headmenu);
	}
#endif // XINERAMA

	insert(i18n->getMessage(
		CommonSet, CommonAlwaysOnTop,
		"Always on top"), 1);
	insert(i18n->getMessage(
		CommonSet, CommonAutoHide,
		"Auto hide"), 2);

	setItemSelected(2, slit.isOnTop());
	setItemSelected(3, slit.doAutoHide());
	
	update();

}


Slitmenu::~Slitmenu() {
	delete directionmenu;
	delete placementmenu;
#ifdef XINERAMA
	if (screen()->hasXinerama()) {
		delete headmenu;
	}
#endif // XINERAMA
}


void Slitmenu::itemSelected(int button, unsigned int index) {
	if (button == 1) {
		BasemenuItem *item = find(index);
		if (! item) return;

		switch (item->function()) {
		case 1: // always on top
		{
			bool change = ((slit.isOnTop()) ?	false : true);
			slit.on_top = change;
			screen()->saveSlitOnTop(change);
			setItemSelected(2, change);
			
			if (slit.isOnTop())
				screen()->raiseWindows(0, 0);
		
		break;
		}

		case 2: // auto hide
		{
			bool change = (slit.doAutoHide() ?	false : true);
			slit.do_auto_hide = change;
			screen()->saveSlitAutoHide(change);
			setItemSelected(3, change);
		break;
		}
		}
		//save the new configuration
		Fluxbox::instance()->save_rc();
		update();
	}
}


void Slitmenu::internal_hide(void) {
	Basemenu::internal_hide();
	if (slit.doAutoHide())
		slit.timeout();
}


void Slitmenu::reconfigure(void) {
	directionmenu->reconfigure();
	placementmenu->reconfigure();
#ifdef XINERAMA
	if (screen()->hasXinerama()) {
		headmenu->reconfigure();
	}
#endif // XINERAMA
	setItemSelected(2, slit.isOnTop());
	setItemSelected(3, slit.doAutoHide());

	Basemenu::reconfigure();
}


Slitmenu::Directionmenu::Directionmenu(Slitmenu *sm) : Basemenu(sm->screen()),
slitmenu(sm) {

	I18n *i18n = I18n::instance();
	using namespace FBNLS;	
	setLabel(i18n->getMessage(
		SlitSet, SlitSlitDirection,
		"Slit Direction"));
	setInternalMenu();

	insert(i18n->getMessage(
		CommonSet, CommonDirectionHoriz,
		"Horizontal"),
	 Slit::HORIZONTAL);
	insert(i18n->getMessage(
		CommonSet, CommonDirectionVert,
		"Vertical"),
	 Slit::VERTICAL);

	update();

	if (screen()->getSlitDirection() == Slit::HORIZONTAL)
		setItemSelected(0, true);
	else
		setItemSelected(1, true);
}


void Slitmenu::Directionmenu::itemSelected(int button, unsigned int index) {
	if (button == 1) {
		BasemenuItem *item = find(index);
		if (! item) return;

		screen()->saveSlitDirection(item->function());

		if (item->function() == Slit::HORIZONTAL) {
			setItemSelected(0, true);
			setItemSelected(1, false);
		} else {
			setItemSelected(0, false);
			setItemSelected(1, true);
		}
		Fluxbox::instance()->save_rc();
		hide();		
		slitmenu->slit.reconfigure();
	}
}


Slitmenu::Placementmenu::Placementmenu(Slitmenu *sm) : Basemenu(sm->screen()),
slitmenu(sm) {

	I18n *i18n = I18n::instance();
	using namespace FBNLS;	
	setLabel(i18n->getMessage(
		SlitSet, SlitSlitPlacement,
		"Slit Placement"));
	setMinimumSublevels(3);
	setInternalMenu();

	insert(i18n->getMessage(
		CommonSet, CommonPlacementTopLeft,
		"Top Left"),
	 Slit::TOPLEFT);
	insert(i18n->getMessage(
		CommonSet, CommonPlacementCenterLeft,
		"Center Left"),
	 Slit::CENTERLEFT);
	insert(i18n->getMessage(
		CommonSet, CommonPlacementBottomLeft,
		"Bottom Left"),
	 Slit::BOTTOMLEFT);
	insert(i18n->getMessage(
		CommonSet, CommonPlacementTopCenter,
		"Top Center"),
	 Slit::TOPCENTER);
	insert("");
	insert(i18n->getMessage(
		CommonSet, CommonPlacementBottomCenter,
		"Bottom Center"),
	 Slit::BOTTOMCENTER);
	insert(i18n->getMessage(
		CommonSet, CommonPlacementTopRight,
		"Top Right"),
	 Slit::TOPRIGHT);
	insert(i18n->getMessage(
		CommonSet, CommonPlacementCenterRight,
		"Center Right"),
	 Slit::CENTERRIGHT);
	insert(i18n->getMessage(
		CommonSet, CommonPlacementBottomRight,
		"Bottom Right"),
	 Slit::BOTTOMRIGHT);

	update();
}


void Slitmenu::Placementmenu::itemSelected(int button, unsigned int index) {
	if (button == 1) {
		BasemenuItem *item = find(index);
		if (! item) return;

		if (item->function()) {
			screen()->saveSlitPlacement(item->function());
			hide();
			slitmenu->slit.reconfigure();
			Fluxbox::instance()->save_rc();
		}
	}
}

#ifdef XINERAMA

Slitmenu::Headmenu::Headmenu(Slitmenu *sm): Basemenu(sm->screen()),
slitmenu(sm) {

	I18n *i18n = I18n::instance();

	setLabel(i18n->getMessage(0, 0, "Place on Head")); //TODO: NLS
	setInternalMenu();

	int numHeads = screen()->getNumHeads();
	// fill menu with head entries
	for (int i = 0; i < numHeads; i++) {
		char headName[32];
		sprintf(headName, "Head %i", i+1); //TODO: NLS
		insert(i18n->getMessage(0, 0, headName), i);
	}

	update();
}

void Slitmenu::Headmenu::itemSelected(int button, unsigned int index) {
	if (button == 1) {
		BasemenuItem *item = find(index);
		if (! item)
			return;

		screen()->saveSlitOnHead(item->function());
		hide();
		slitmenu->slit.reconfigure();
		Fluxbox::instance()->save_rc();
	}
}

#endif // XINERAMA

Slit::SlitClient::SlitClient(const char *name)
{
	initialize();
	match_name = name;
}

Slit::SlitClient::SlitClient(BScreen *screen, Window w)
{
	initialize(screen, w);
}

void Slit::SlitClient::initialize(BScreen *screen, Window w) {
	client_window = w;
	window = icon_window = None;
	x = y = 0;
	width = height = 0;
	if (match_name.size() == 0)
		getWMName(screen, client_window, match_name);
}

#endif // SLIT