/*
 * Copyright (C) 1999,2000,2001 Frank Hale
 * frankhale@yahoo.com
 * http://sapphire.sourceforge.net/
 *
 * Updated: 3 Nov 2001
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 */


#include "sapphire.hh"


BaseMenu::BaseMenu() {

	menu_pix=None;
	select_pix=None;
	title_pix=None;

	select_gc = None;
	menu_gc = None;
	back_gc = None;

	mi = new LinkedList<BaseMenuItem>;
	
	menu.menuTitle="Sapphire Menu";

	menu.hasTitle=False;

	bottom_edge=False;
	right_edge=False;

	counter=0;

	extra_width=0;

	font = XLoadQueryFont(wm->getDisplay(), wm->getTheme()->getMenuFont());
	
  	create_mask = CWBackPixmap       | 
		      CWBackPixel        | 
		      CWBorderPixel      |
	    	      CWOverrideRedirect |
		      CWEventMask; 
		      
  	attrib.background_pixmap = None;
  	attrib.background_pixel = wm->getBackgroundColor().pixel;
  	attrib.border_pixel = wm->getBorderColor().pixel;
  	attrib.override_redirect = True;
  	attrib.event_mask = ButtonPressMask   |
	                    ButtonReleaseMask | 
			    PointerMotionMask |
    			    ExposureMask      |
			    EnterWindowMask   |
			    LeaveWindowMask;


	//gv.background = wm->getBackgroundColor().pixel;	

	//select_gc = XCreateGC(wm->getDisplay(), wm->getRootWindow(),  GCTile , &gv);

	//back_gc = XCreateGC(wm->getDisplay(), wm->getRootWindow(),  GCTile, &gv);


	menu.x = 1;
  	menu.y = 1;
	menu.x_move = 1;
  	menu.y_move = 1;
	menu.height=1;
	menu.width=1;
    
	menu.total_item_height=1;
    
    	item_width = menu.width;
	item_height = font->ascent + font->descent + 8;
	
	menu.window =
    		XCreateWindow(wm->getDisplay(), wm->getRootWindow(), menu.x, menu.y,
		  menu.width, menu.height, 1, DefaultDepth(wm->getDisplay(), wm->getScreen()),
		  InputOutput, DefaultVisual(wm->getDisplay(), wm->getScreen()), create_mask, &attrib);

	menu.title =
    		XCreateWindow(wm->getDisplay(), menu.window, 0, 0,
		  menu.width, item_height, 0, DefaultDepth(wm->getDisplay(), wm->getScreen()),
		  InputOutput, DefaultVisual(wm->getDisplay(), wm->getScreen()), create_mask, &attrib);
	
	menu.item_window =	
    		XCreateWindow(wm->getDisplay(), menu.window, 0, item_height - 2,
		  menu.width, menu.height-item_height, 0, DefaultDepth(wm->getDisplay(), wm->getScreen()),
		  InputOutput, DefaultVisual(wm->getDisplay(), wm->getScreen()), create_mask, &attrib);

	curs = XDefineCursor(wm->getDisplay(), menu.window, XCreateFontCursor(wm->getDisplay(), XC_left_ptr));

	
	menu.isVisible = False;
	
	curr = new BaseMenuItem();
	
	curr = None;
	
	enterOnce = True;
}

BaseMenu::~BaseMenu() {			
	mi->removeAll();

	XDestroyWindow(wm->getDisplay(), menu.title);
	XDestroyWindow(wm->getDisplay(), menu.item_window);
	XDestroyWindow(wm->getDisplay(), menu.window);

	XFreeGC(wm->getDisplay(), menu_gc);
	XFreeGC(wm->getDisplay(), select_gc);
	XFreeGC(wm->getDisplay(), back_gc);
	
	wm->getImageControl()->removeImage(title_pix);
	wm->getImageControl()->removeImage(menu_pix);
	wm->getImageControl()->removeImage(select_pix);
	wm->getImageControl()->removeImage(menufont_pix);
	
	delete mi;
	delete curr;
}

// This updates all graphics contexts, gradient pixmaps and determines placement of all menu items.

void BaseMenu::update_all()
{
	LinkedListIterator<BaseMenuItem> it(mi);
	
	int temp_width=0;
	int temp_ascent=0;
	int temp_descent=0;
	
	unsigned int count=0;
	unsigned int char_count=0;

	menu.total_item_height=1;

	// If menu is created with no items lets make a few defaults

	// so things work out nice.

	
	if (mi->count() == 0)
	{
		char_count = strlen(menu.menuTitle);

		menu.width = XTextWidth(wm->getXFont(), menu.menuTitle, strlen(menu.menuTitle));

		XTextExtents(font, menu.menuTitle , char_count, &direction, &ascent, 
		           &descent, &overall);	

		temp_width  = overall.width;
		temp_ascent = overall.ascent;
		temp_descent= overall.descent;

		menu.width = temp_width + 40;
		item_width  = temp_ascent + temp_descent + 8;
		menu.height = item_height;

		menu.total_item_height = item_height+3;
	
	} else {

	//=============================================================


	for(; it.current(); it++)
	{
		char_count = strlen(it.current()->name);

    		XTextExtents(font, it.current()->name , char_count, &direction, &ascent, 
            		&descent, &overall);		

		if (char_count < strlen(menu.menuTitle)) {
		
			char_count = strlen(menu.menuTitle);
			
			XTextExtents(font, menu.menuTitle , char_count, &direction, &ascent, 
		           &descent, &overall);		
		}
	
		if (char_count > count) {
			count = char_count; 
			temp_width  = overall.width;
			temp_ascent = overall.ascent;
			temp_descent= overall.descent;
		}
		
		menu.width = temp_width + 40;
		item_width  = temp_ascent + temp_descent + 8;
		menu.height = item_height * mi->count();
		
		it.current()->item_x = item_width/2+4;
		it.current()->item_y = item_height/2+4+menu.total_item_height;
		
		menu.total_item_height += item_height;
	}

	menu.total_item_height += item_height;
	
	}
	
	menu.width+=extra_width;

	setTitleVisible(menu.hasTitle);	

	createMenuImage();
	createMenuSelectImage();
	createMenuTitleImage();
	createMenuFontImage();
	
	XClearWindow(wm->getDisplay(), menu.title);
	XClearWindow(wm->getDisplay(), menu.item_window);
			
	XGCValues gv;
	gv.tile = select_pix;
	select_gc = XCreateGC(wm->getDisplay(), wm->getRootWindow(),  GCTile , &gv);
	XSetFillStyle(wm->getDisplay(), select_gc, FillTiled);	
	
	gv.tile = menu_pix;
	back_gc = XCreateGC(wm->getDisplay(), wm->getRootWindow(),  GCTile, &gv);
	XSetFillStyle(wm->getDisplay(), back_gc, FillTiled);

	gv.font = font->fid;
	gv.tile = menufont_pix;
	menu_gc = XCreateGC(wm->getDisplay(), wm->getRootWindow(),  GCTile | GCFont, &gv);
	XSetFillStyle(wm->getDisplay(), menu_gc, FillTiled);
	
	testMenuEdgeDetect(this);
	
	if (menu.y + menu.height >= wm->getYRes()) 
	{
		bottom_edge=True;
 	}else {
				
		bottom_edge=False;
	}
	
	if(bottom_edge)
	{
		XMoveWindow(wm->getDisplay(), menu.window, menu.x, wm->getYRes() - menu.height - item_height - 4);
	}
}

// This updates all graphics contexts, gradient pixmaps and determines placement of all menu items.

void BaseMenu::update_menu()
{
	LinkedListIterator<BaseMenuItem> it(mi);
	
	int temp_width=0;
	int temp_ascent=0;
	int temp_descent=0;
	
	unsigned int count=0;
	unsigned int char_count=0;

	menu.total_item_height=1;

	// If menu is created with no items lets make a few defaults

	// so things work out nice.

	
	if (mi->count() == 0)
	{
		char_count = strlen(menu.menuTitle);

		menu.width = XTextWidth(wm->getXFont(), menu.menuTitle, strlen(menu.menuTitle));

		XTextExtents(font, menu.menuTitle , char_count, &direction, &ascent, 
		           &descent, &overall);	

		temp_width  = overall.width;
		temp_ascent = overall.ascent;
		temp_descent= overall.descent;

		menu.width = temp_width + 40;
		item_width  = temp_ascent + temp_descent + 8;
		menu.height = item_height;

		menu.total_item_height = item_height+3;
	
	} else {

	//=============================================================


	for(; it.current(); it++)
	{
		char_count = strlen(it.current()->name);

    		XTextExtents(font, it.current()->name , char_count, &direction, &ascent, 
            		&descent, &overall);		

		if (char_count < strlen(menu.menuTitle)) {
		
			char_count = strlen(menu.menuTitle);
			
			XTextExtents(font, menu.menuTitle , char_count, &direction, &ascent, 
		           &descent, &overall);		
		}
	
		if (char_count > count) {
			count = char_count; 
			temp_width  = overall.width;
			temp_ascent = overall.ascent;
			temp_descent= overall.descent;
		}
		
		menu.width = temp_width + 40;
		item_width  = temp_ascent + temp_descent + 8;
		menu.height = item_height * mi->count();
		
		it.current()->item_x = item_width/2+4;
		it.current()->item_y = item_height/2+4+menu.total_item_height;
		
		menu.total_item_height += item_height;
	}

	menu.total_item_height += item_height;
	
	}

	menu.width+=extra_width;
	
	setTitleVisible(menu.hasTitle);
}

void BaseMenu::hide()
{
	XUnmapWindow(wm->getDisplay(), menu.window);
}

void BaseMenu::setTitleVisible(bool s) 
{ 	
	menu.hasTitle=s; 
		
	if(menu.hasTitle)
	{
		XResizeWindow(wm->getDisplay(), menu.window, menu.width, menu.total_item_height);
		XResizeWindow(wm->getDisplay(), menu.title, menu.width, item_height);
		XMoveResizeWindow(wm->getDisplay(), menu.item_window, 
				0,
				item_height,
				menu.width, 
				menu.total_item_height);
	
	} else {
		XResizeWindow(wm->getDisplay(), menu.window, menu.width, menu.total_item_height - item_height);
		XResizeWindow(wm->getDisplay(), menu.title, menu.width, item_height);
		XMoveResizeWindow(wm->getDisplay(), menu.item_window, 
				0, 0,
				menu.width, 	
				menu.total_item_height);	
	}
}

void BaseMenu::reconfigure()
{	
	XGCValues gv;

	XFreeFont(wm->getDisplay(), font);
	font = XLoadQueryFont(wm->getDisplay(),  wm->getTheme()->getMenuFont());
	update_menu();
	
	XFreePixmap(wm->getDisplay(), title_pix);
	XFreePixmap(wm->getDisplay(), menu_pix);
	XFreePixmap(wm->getDisplay(), select_pix);
	XFreePixmap(wm->getDisplay(), menufont_pix);

	createMenuImage();
	createMenuSelectImage();
	createMenuTitleImage();
	createMenuFontImage();
	
	XClearWindow(wm->getDisplay(), menu.title);
	XClearWindow(wm->getDisplay(), menu.item_window);
		
	XFreeGC(wm->getDisplay(), menu_gc);
	XFreeGC(wm->getDisplay(), select_gc);
	XFreeGC(wm->getDisplay(), back_gc);
		
	gv.tile = select_pix;
	select_gc = XCreateGC(wm->getDisplay(), wm->getRootWindow(),  GCTile , &gv);
	XSetFillStyle(wm->getDisplay(), select_gc, FillTiled);	
	
	gv.tile = menu_pix;
	back_gc = XCreateGC(wm->getDisplay(), wm->getRootWindow(),  GCTile, &gv);
	XSetFillStyle(wm->getDisplay(), back_gc, FillTiled);

	gv.font = font->fid;
	gv.tile = menufont_pix;
	menu_gc = XCreateGC(wm->getDisplay(), wm->getRootWindow(),  GCTile | GCFont, &gv);
	XSetFillStyle(wm->getDisplay(), menu_gc, FillTiled);
		
	redraw();
}

// Redraws the entire contents of the menu.

void BaseMenu::redraw()
{
	//int text_width=0;

	
	// We need an Iterator for our menu items. This lets us traverse through

	// the list and paint each menu item.

	LinkedListIterator<BaseMenuItem> it(mi);
	
	for(; it.current(); it++)
	{	  				
		if(strcmp(it.current()->name, "separator")==0) 
		{
			XDrawLine(wm->getDisplay(), menu.item_window, menu_gc, it.current()->item_x - 14, it.current()->item_y-5, menu.width-1, it.current()->item_y-5);	
			XDrawLine(wm->getDisplay(), menu.item_window, menu_gc, it.current()->item_x - 14, it.current()->item_y-6, menu.width-1, it.current()->item_y-6);	
		} else {
		
			XDrawString(wm->getDisplay(), menu.item_window, menu_gc, it.current()->item_x, 
			it.current()->item_y + 2, it.current()->name, strlen(it.current()->name));
			
			if(it.current()->sub)
			{
				XDrawRectangle(wm->getDisplay(), menu.item_window, menu_gc, 
					it.current()->item_x + menu.width - 35, 
					it.current()->item_y - 7, 
					8,
					8);
			}
		}
		
		// Draws a bounding box around each menu item. This helped me figure out the algorithm to test which item the 

		// pointer was over.

		//

		//XDrawRectangle(wm->getDisplay(), menu.item_window, menu_gc, it.current()->item_x - 14, it.current()->item_y - 14, menu.width-1, it.current()->item_y + item_height - 15);

	
	}
	
	XDrawString(wm->getDisplay(), menu.title, menu_gc, 
   	   8, 16 , menu.menuTitle, strlen(menu.menuTitle));
	
	//text_width=XTextWidth(wm->font, menu.menuTitle, strlen(menu.menuTitle));


	//XDrawString(wm->getDisplay(), menu.title, menu_gc, 

   	//   menu.width/2 - text_width/2, 

	//   14 , 

	//   menu.menuTitle, strlen(menu.menuTitle));

}

// Redraws a single menu item on the menu.

void BaseMenu::redraw(BaseMenuItem *i)
{
	if(strcmp(i->name, "separator")==0) 
	{
		XDrawLine(wm->getDisplay(), menu.item_window, menu_gc, i->item_x - 14, i->item_y-5, menu.width-1, i->item_y-5);
		XDrawLine(wm->getDisplay(), menu.item_window, menu_gc, i->item_x - 14, i->item_y-6, menu.width-1, i->item_y-6);				
	} else {
	
		XDrawString(wm->getDisplay(), menu.item_window, menu_gc, 
		i->item_x, i->item_y + 2, i->name, strlen(i->name));
		
		if(i->sub)
		{
			XDrawRectangle(wm->getDisplay(), menu.item_window, menu_gc, 
					i->item_x + menu.width - 35, 
					i->item_y - 7, 
					8, 
					8);	
		}
		
	}
	
	// Draws a bounding box around each menu item. This helped me figure out the algorithm to test which item the 

	// pointer was over.

	//

	//XDrawRectangle(wm->getDisplay(), menu.item_window, menu_gc, it.current()->item_x - 14, it.current()->item_y - 14, menu.width-1, it.current()->item_y + item_height - 15);


}

void BaseMenu::insert(char *n, BaseMenu *sub)
{
	BaseMenuItem *item = new BaseMenuItem();
		
	item->name = new char[strlen(n)+1];	
	strcpy(item->name, n);
	
	item->sub  = sub;

	// defaults

	item->isSelected=False;
	item->exec = "";
	item->function = 0;
	item->item_x = item->item_y = 0;

	mi->insert(item);
}

void BaseMenu::insert(char *n, char *exec, int func)
{
	BaseMenuItem *item = new BaseMenuItem();
	
	char *buffer = new char[100];
        if (exec[0] == '~') 
	{
		sprintf(buffer, "%s%s", getenv("HOME"), exec+1);
		strcpy(exec, buffer);
	}
	delete [] buffer;
	
	item->exec = new char[strlen(exec)+1];
	strcpy(item->exec, exec);
	
	item->name = new char[strlen(n)+1];	
	strcpy(item->name, n);
	
	item->function = func;

	// defaults

	item->isSelected=False;
	item->sub  = None;
	item->item_x = item->item_y = 0;

	mi->insert(item);
}


void BaseMenu::insert(char *n, char *exec, BaseMenu *sub, int func)
{
	BaseMenuItem *item = new BaseMenuItem();
	
	char *buffer = new char[100];
        if (exec[0] == '~') 
	{
		sprintf(buffer, "%s%s", getenv("HOME"), exec+1);
		strcpy(exec, buffer);
	}
	delete [] buffer;
	
	item->exec = new char[strlen(exec)+1];
	strcpy(item->exec, exec);
	
	item->name = new char[strlen(n)+1];	
	strcpy(item->name, n);
	
	item->isSelected=False;
	item->sub  = sub;
	item->function = func;
	item->item_x = item->item_y = 0;
	
	mi->insert(item);
}

int BaseMenu::remove(BaseMenuItem* element)
{
	mi->remove(element);

	return mi->count();
}

void BaseMenu::removeAll()
{
	mi->removeAll();
	
	update_all();
}

void BaseMenu::execute(char *s)
{
	char command[strlen(s)+8];
	
	sprintf(command, "exec %s &", s);
	system(command);
}

void BaseMenu::show()
{	
	int mouse_x=0;
	int mouse_y=0;
	
	// Gets the coordinates of the mouse

	getMousePosition(&mouse_x, &mouse_y);
		
	// Check to make sure menu will be displayed on the screen.

 	if (mouse_x + menu.width > wm->getXRes()) {
 		menu.x = wm->getXRes() - menu.width - 1;
		
		right_edge=True;
 	} else {
		menu.x = mouse_x;
		
		right_edge=False;
	}
	
	if (mouse_y + menu.height > wm->getYRes()) {
		menu.y = wm->getYRes() - menu.height - item_height - 4;
		
		bottom_edge=True;
 	}else {
		
		menu.y = mouse_y;
		
		bottom_edge=False;
	}
	
	// Move the menu to the position of the mouse pointer

	XMoveWindow(wm->getDisplay(), menu.window, menu.x, menu.y);
	
	// Show the menu windows

	XMapRaised(wm->getDisplay(), menu.window);
	XMapSubwindows(wm->getDisplay(),menu.window);
	
	menu.isVisible=True;
}

void BaseMenu::getMousePosition(int *x, int *y)
{
	Window dw1=None;
	Window dw2=None;
	int t1=0;
	int t2=0;
	unsigned int t3=0;

	XQueryPointer(wm->getDisplay(), wm->getRootWindow(), &dw1, &dw2, x, y, &t1, &t2, &t3);
}

void BaseMenu::show(int x, int y)
{	
	menu.x = x;
	menu.y = y;

	XMoveWindow(wm->getDisplay(), menu.window, x, y);
	
	// Show the menu windows

	XMapSubwindows(wm->getDisplay(),menu.window);
	XMapRaised(wm->getDisplay(), menu.window);

	XClearWindow(wm->getDisplay(), menu.item_window);

	menu.isVisible=True;
}

void BaseMenu::showSub(BaseMenu *sub, int x, int y)
{
	sub->menu.x = x;
	sub->menu.y = y;
	
	XMoveWindow(wm->getDisplay(), sub->menu.window, x, y);
		
	// Show the menu windows

	XMapSubwindows(wm->getDisplay(), sub->menu.window);
	XMapRaised(wm->getDisplay(),  sub->menu.window);

	sub->menu.isVisible=True;
}

void BaseMenu::hide(BaseMenu *sub)
{
	
	if (sub->menu.isVisible)
	{
		// hide the menu windows

		XUnmapSubwindows(wm->getDisplay(), sub->menu.window);
		XUnmapWindow(wm->getDisplay(), sub->menu.window);

		sub->menu.isVisible=False;
	}
	
}

BaseMenuItem *BaseMenu::findMenuItem(int x, int y)
{
	int item_xpos2=0;
	int item_ypos2=0;
	
	if(mi->count()) 
	{
		LinkedListIterator<BaseMenuItem> it(mi);
		for(; it.current(); it++)
		{

			// Draws a bounding box around each menu item. This helped me figure out the algorithm to test which item the 

			// pointer was over.

			//

			//XDrawRectangle(wm->getDisplay(), menu.item_window, menu_gc, it.current()->item_x - 14, 

			//	it.current()->item_y - 14, menu.width-1, it.current()->item_y + item_height - 15);



			item_xpos2 = menu.width-1;
			item_ypos2 = it.current()->item_y + item_height - 15;
	  
			if ( (x >= it.current()->item_x - 14) && 
			     (x < item_xpos2) &&
			     (y >= it.current()->item_y - 14) &&  
			     (y < item_ypos2)
			   ) 
			    {
			    	
				    return it.current();
			    }
		}	
	}

	return NULL;
}

void BaseMenu::handle_button_press_event(XButtonEvent *e)
{
	switch (e->button) 
	{
			case Button1:
			{
				if(e->window == menu.title) move(); 
				else if (curr) handle_button1_press(curr);
			break;
			}

			case Button2:
			{
				if(curr) handle_button2_press(curr);
			break;
			}
			
			case Button3:
			{
				if (curr) handle_button3_press(curr);
			break;
			}
	} 
}

void BaseMenu::handle_button_release_event(XButtonEvent *e)
{
	switch (e->button) {
			case Button1:
			{
				if (curr) {
	
					handle_button1_release(curr);
				
					switch(	curr->function )
					{
						// function 1 is wm shutdown

						case 1:
						{
							wm->quit_nicely();
						}
						break;
							
						case 2: // do nothing this is a menu separator

						{
						}
						break;
							
						case 3: // This will start another window manager

						{
							//wm->start_another_windowmanager(curr->exec);

						}
						break;
						
						case 4: // restart Sapphire

						{
							wm->restart();
						}
						break;
										
						case 5: // reconfigure Sapphire (theme, menu)

						{
							wm->reconfigureMenu();
						}
						break;

						case 6: // change theme 

						{
							Toolbar *t = wm->getToolbar();
							t->setStartMenuButtonStateNotPressed();					
							t->getToolbarMenu()->hide(t->getToolbarMenu());
							
							wm->getRootMenu()->hideAllVisibleSubmenus();
							wm->changeTheme(curr->exec);
						}
						break;

						default: // Default is to execute a command or file

						{
							Toolbar *t = wm->getToolbar();
							t->setStartMenuButtonStateNotPressed();							
							t->getToolbarMenu()->hide(t->getToolbarMenu()); 
							wm->getRootMenu()->hideAllVisibleSubmenus();
							execute(curr->exec);
						}
						break;
					}										
				}
				
			break;
			}

			case Button2:
			{
				if (curr) handle_button2_release(curr);
			break;
			}
			
			case Button3:
			{
				if(e->window == menu.title) hideAllVisibleSubmenus();
				else if (curr) handle_button3_release(curr);
			break;
			}
	} 
}

void BaseMenu::move()
{
	wm->setMenu(this);
	wm->setMenuEvent(true);
}

void BaseMenu::setMenuPos(int x, int y)
{
	menu.x = x; 
	menu.y = y;
	
	XMoveWindow(wm->getDisplay(), menu.window, x, y);
}

void BaseMenu::handle_enter_notify(XCrossingEvent *e)
{
	if(curr)
	{					
		LinkedList<BaseMenuItem> *temp_mi;

		LinkedListIterator<BaseMenuItem> it(mi);
		for(; it.current(); it++)
		{	  	
			if (it.current()->sub && it.current()->sub->menu.isVisible)
			{
				it.current()->sub->hide(it.current()->sub);
				
				temp_mi = it.current()->sub->mi;
				
				LOOP:
				
				LinkedListIterator<BaseMenuItem> n(temp_mi);
				for(; n.current(); n++)
				{
					if(n.current()->sub && n.current()->sub->menu.isVisible)
					{
						n.current()->sub->hide(n.current()->sub);
					
						temp_mi = n.current()->sub->mi;
						
						goto LOOP;						
					} 					
				}
			} 
		}
   		
		XFillRectangle(wm->getDisplay(), menu.item_window, select_gc, 
			0, 
			curr->item_y - 15, 
			item_width - item_height - 2, 
			item_height - 1
			);	 

		if(curr->sub) 
		{
			testMenuEdgeDetect(curr->sub);
			
			if(curr->sub->right_edge) 
			{
				if(curr->sub->bottom_edge)
					showSub(curr->sub,menu.x - curr->sub->menu.width - 1, wm->getYRes() - curr->sub->menu.height - 27);
				else
					showSub(curr->sub,menu.x - curr->sub->menu.width - 1, menu.y + curr->item_y - 15);
			}
			else {
				if(curr->sub->bottom_edge)
				{
					showSub(curr->sub,menu.x + menu.width + 1, wm->getYRes() - curr->sub->menu.height - 27);
				}
				else {
					showSub(curr->sub,menu.x + menu.width + 1, menu.y + curr->item_y - 15);
				}
			}
		} 
		redraw(curr);
	}	
}

void BaseMenu::handle_leave_notify(XCrossingEvent *e)
{
	if(curr)
	{	
		XFillRectangle(wm->getDisplay(), menu.item_window, back_gc, 
			0, 
			curr->item_y -15, 
			item_width - item_height - 2, 
			item_height - 1);	 
		
		redraw(curr);
		curr = NULL;			
	}
}

void BaseMenu::hideAllVisibleSubmenus()
{
	LinkedList<BaseMenuItem> *temp_mi;
	
	hide(this);

	LinkedListIterator<BaseMenuItem> it(mi);
	for(; it.current(); it++)
	{	  	
		if (it.current()->sub && it.current()->sub->menu.isVisible)
		{
			it.current()->sub->hide(it.current()->sub);
			
			temp_mi = it.current()->sub->mi;
			
			LOOP: // Yuck I'm using a goto... ;-(

				
			LinkedListIterator<BaseMenuItem> n(temp_mi);
			for(; n.current(); n++)
			{
				if(n.current()->sub && n.current()->sub->menu.isVisible)
				{
					n.current()->sub->hide(n.current()->sub);
					
					temp_mi = n.current()->sub->mi;
						
					goto LOOP;						
				} 					
			}
		} 
	}
}

void BaseMenu::setAllMenuTitlesVisible(BaseMenu *sub, bool s)
{	
	sub->setTitleVisible(s);

	LinkedListIterator<BaseMenuItem> it(sub->mi);
	for(; it.current(); it++)
	{	  	
		if (it.current()->sub) setAllMenuTitlesVisible(it.current()->sub, s);
	}
}

void BaseMenu::handle_expose_event(XExposeEvent *e)
{
	if(e->count == 0) redraw();
}

void BaseMenu::handle_motion_notify_event(XMotionEvent *e)
{
	// for moving the menu.

	menu.x_move = e->x;
	menu.y_move = e->y;
	
	//----------------------

	XEvent temp;	
	
	if(e->window == menu.title) { /* DO NOTHING */ }
	else if(e->window == menu.item_window)
	{
		BaseMenuItem *i = findMenuItem(e->x, e->y);

		if (i && enterOnce)
		{
			curr = i;

			if(! strcmp(i->name, "separator")==0) handle_enter_notify(&temp.xcrossing);				

			enterOnce=False;
		} else {

			
			BaseMenuItem *i = findMenuItem(e->x, e->y);
			
			if (i != curr)
			{
				handle_leave_notify(&temp.xcrossing);
			
				curr =  i;
				
				enterOnce=True;
			} 
		}
	}	
}

void BaseMenu::handle_config_notify_event(XConfigureEvent *e)
{
}

void BaseMenu::createMenuImage()
{
	unsigned long style=0;

	style = wm->getTheme()->getMenuStyle();		
					
	menu_pix = wm->getImageControl()->renderImage(
			//"menu_frame",

			menu.width, 
			menu.height, 
			style, 
			wm->getTheme()->getMenuColorFrom(), 
			wm->getTheme()->getMenuColorTo()
			);
	
	XSetWindowBackgroundPixmap(wm->getDisplay(), menu.item_window, menu_pix);
}

void BaseMenu::createMenuSelectImage()
{
	unsigned long style=0;
	
	style = wm->getTheme()->getMenuSelectStyle();		

	select_pix = wm->getImageControl()->renderImage(
			//"menu_select",

			menu.width, 
			item_height, 
			style, 
			wm->getTheme()->getMenuSelectColorFrom(), 
			wm->getTheme()->getMenuSelectColorTo()
			);
}

void BaseMenu::createMenuFontImage()
{
	unsigned long style=0;
	
	style = wm->getTheme()->getFontStyle();		

	menufont_pix = wm->getImageControl()->renderImage(
			//"menu_font",

			font->ascent, 
			font->descent, 
			style, 
			wm->getTheme()->getMenuFontColorFrom(), 
			wm->getTheme()->getMenuFontColorTo()
			);
}

void BaseMenu::createMenuTitleImage()
{
	unsigned long style=0;

	style = wm->getTheme()->getMenuTitleStyle();		

	title_pix = wm->getImageControl()->renderImage(
			//"menu_title",

			menu.width, 
			item_height+1, 
			style, 
			wm->getTheme()->getMenuTitleColorFrom(), 
			wm->getTheme()->getMenuTitleColorTo()
			);
		
	XSetWindowBackgroundPixmap(wm->getDisplay(), menu.title, title_pix);
}

void BaseMenu::testMenuEdgeDetect(BaseMenu *sub)
{
	int mouse_x=0;
	int mouse_y=0;
	
	bool rightEdge=False;
	bool bottomEdge=False;
	
	// Gets the coordinates of the mouse

	getMousePosition(&mouse_x, &mouse_y);

	rightEdge = ( ( this->menu.x + sub->menu.width + 150 ) < (wm->getXRes()) ) ? True : False;
	
	bottomEdge = ( mouse_y + sub->menu.height >= wm->getYRes() ) ? True : False;

	// Check to make sure menu will be displayed on the screen.

	if (rightEdge) sub->right_edge=False;
 	else sub->right_edge=True;
	
	if (bottomEdge) sub->bottom_edge=True;
	else sub->bottom_edge=False;
}


syntax highlighted by Code2HTML, v. 0.9.1