#ifndef CODE_MENU_CXX
#define CODE_MENU_CXX

/*-----------------------------------------------------------------------
 * Xclasses code file
 * ($Id: menu.cxx,v 1.4 1998/07/31 06:35:19 beckerr Exp $)
 * 
 * Copyright (C) 1998 Jürgen Schmitz
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA  02111-1307, USA.
 *-----------------------------------------------------------------------
 */

#include <unistd.h>
#include "types.h"
#include <Xclasses/gadget.h>
#include <Xclasses/menu.h>
#include "include/menu_p.h"
#include <Xclasses/tools.h>
#include <Xclasses/gfx_text.h>
#include <Xclasses/menu.h>
#include <Xclasses/easy_dialog.h>
#include "../include/defaults.h"
#include "../include/setup_dialog.h"
#include "../include/about_dialog.h"
#include "../include/menu.h"

menu::menu() : gadget(), Ddraw(this)
{
	while (!(p=new pmenu))
	{
		outOfMemory("menu");
	}
	p->o=this;
	if (p->new_style)
		BorderSize(1);
	else
		BorderSize(2);
	Font(FONT_MENU);
	Name("menu");
}
pmenu::pmenu() : punkte()
{
	addto=NULL;
	offen=NULL;
	hashelp=0;
	nohelp=0;
	reason_inother=0;
	reason_inhelp=0;
	reason_subwin=0;
	new_style=defaults.menunewstyle?1:0;
}
pmenu::~pmenu()
{
}
menu::~menu()
{
	if (win) Free();
	DeleteObjectTree(FullName(),DOT_NOTBASE);
	if (p) delete p;
}
void menu::Free(void)
{
	Clear();
	gadget::Free();
	DeleteObjectTree(FullName(),DOT_NOTBASE);
}

int menu::Create(void)
{
	p->new_style=defaults.menunewstyle?1:0;
	if (p->new_style)
	{
		Background(col_shinebackground());
		BorderSize(1);
	}
	else
	{
		Background(col_background());
		BorderSize(2);
	}

	if (!p->nohelp && !p->hashelp)
	{
		p->hashelp=p->punkte.Count()+1;
		AddHelpMenu("Xclasses");
		p->helpmenu();
	}
    if (gadget::Create())
    {
        Mode(GM_BUTTON1|GM_BUTTON3|GM_ONEEXPOSE|GM_MOUSEMOVE|GM_MINIMALMOVE);//|GM_KEY);
        p->setwidth();
        p->setpos();
		menupunkt *m=(menupunkt*)p->punkte.head;
		while (m->base.next)
		{
			m->pops.ParentClass(this);
			m->pops.KeyHook(p,(KEYHOOK)&pmenu::keyhook,(int)m);
			m->pops.CopyFont(this);
			m->pops.Dimensions(win,m->x,0,10,10);
			m->pops.SendEventTo(this);
			m->pops.Create();
			m=(menupunkt*)m->base.next;
		}
        return 1;
    } 
    return 0;
}

void menu::Recreate(void)
{
	if (win)
	{
		if (!p->nohelp && !p->hashelp)
		{
			p->hashelp=p->punkte.Count()+1;
			AddHelpMenu("Xclasses");
			p->helpmenu();
		}
		setlimit=0;
		p->setwidth();
		p->setpos();
		menupunkt *m=(menupunkt*)p->punkte.head;
		while (m->base.next)
		{
			m->pops.ParentClass(this);
			m->pops.KeyHook(p,(KEYHOOK)&pmenu::keyhook,(int)m);
			m->pops.CopyFont(this);
			m->pops.Dimensions(win,m->x,0,10,10);
			m->pops.SendEventTo(this);
			m->pops.Create();
			m=(menupunkt*)m->base.next;
		}
		GExpose(NULL);
    } 
}

void menu::Resize(int w,int h)
{
	gadget::Resize(w,h);
	p->setpos();
}

void pmenu::setpos(void)
{
	menupunkt *m=(menupunkt*)punkte.head;
	int x=LEFT+o->RealSize();
	int x2=o->width-LEFT-o->RealSize();
	
	while (m->base.next)
	{
		if (m->right)
		{
			m->x=x2-m->w;
			x2-=m->w;
		}
		else
		{
			m->x=x;
			x+=m->w;
		}
		m=(menupunkt*)m->base.next;
	}
}
void pmenu::setwidth(void)
{
	if (o->setlimit) return;
	o->setlimit=1;

	menupunkt *m=(menupunkt*)punkte.head;
	int w=0,h=0;
	
	while (m->base.next)
	{
		m->text.Adjust(ADJUST_MIDDLE|ADJUST_CENTER);
		m->w=m->text.width(o)+SPACE;
		w+=m->w;
		m->h=m->text.height(o)+TOP+BOTTOM;
		if (m->h>h) h=m->h;
		m=(menupunkt*)m->base.next;
	}
	if (h==0) h=TOP+BOTTOM;
	o->minwidth=w+LEFT*2+o->RealSize()*2;
	o->minheight=h;
	o->maxheight=h;
}
#define MENU_SETUP	0x80000001
#define MENU_INFO	0x80000002
#define MENU_QUIT	0x80000003
#define MENU_ABOUT	0x80000004
static int reason_suid=0;
void pmenu::helpmenu(void)
{
	int m;
	if (!reason_suid)
	{
		o->Add("Xclasses setup...","f12",MENU_SETUP);
		if (reason_inhelp)
		{
			m=punkte.Find((node*)addto);
			o->LockItem(m,MENU_SETUP);
		}
	}
	o->Add("About Xclasses...","F12",MENU_INFO);
	o->Add("Close window",NULL,MENU_QUIT);
//	if (!reason_inhelp)
	{
		o->AddRuler();
		o->Add("About program...",NULL,MENU_ABOUT);
	}
}
int pmenu::handleHelp(void)
{
	if (o->Menu()==hashelp)
	{
		switch (o->MID())
		{
			case MENU_ABOUT:
				{
					about_dialog *abt=new about_dialog;
					abt->ParentClass(o);
					if (!abt->Start(GetXwindowsOf(o->display(),o->win)))
						delete abt;
				}
				break;
			case MENU_SETUP:
				{
					setup_dialog *stup=new setup_dialog;
					stup->ParentClass(o);
					if (!stup->Start(GetXwindowsOf(o->display(),o->win)))
						delete stup;
				}
				break;
			case MENU_INFO:
				about_Xclasses(GetXwindowsOf(o->display(),o->win),o);
				break;
			case MENU_QUIT:
				{
					XClientMessageEvent xce;
					Xwindows *aw=GetXwindowsOf(o->display(),o->win);
					xce.type=ClientMessage;
					xce.serial=0;
					xce.display=o->display();
					xce.window=aw->window();
					xce.message_type=XInternAtom(o->display(),"WM_DELETE_WINDOW",False);
					xce.format=32;
					xce.data.l[0]=XInternAtom(o->display(),"WM_DELETE_WINDOW",False);
					XSendEvent(o->display(),aw->window(),False,ClientMessage,(XEvent*)&xce);
				}
				break;
			default:
				return 1;
				break;
		}
		return 0;
	}
	return 1;
}

void menu::Lock(void)
{
	gadget::Lock();
}
void menu::Unlock(void)
{
	gadget::Unlock();
}
void menu::Block(int b)
{
	gadget::Block(b);

	menupunkt *m=(menupunkt*)p->punkte.head;
	while (m->base.next)
	{
		m->pops.Block(b);
		m=(menupunkt*)m->base.next;
	}
}
void menu::Unblock(int b)
{
	menupunkt *m=(menupunkt*)p->punkte.head;
	while (m->base.next)
	{
		m->pops.Unblock(b);
		m=(menupunkt*)m->base.next;
	}
	gadget::Unblock(b);
}

void menu::LockItem(int men,int id)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(men);
	if (m)
	{
		m->pops.LockItem(id);
	}
}
void menu::UnlockItem(int men,int id)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(men);
	if (m)
	{
		m->pops.UnlockItem(id);
	}
}
void menu::LockHeadOf(int men,int id)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(men);
	if (m)
	{
		m->pops.LockHeadOf(id);
	}
}
void menu::UnlockHeadOf(int men,int id)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(men);
	if (m)
	{
		m->pops.UnlockHeadOf(id);
	}
}

void menu::State(int men,int id,int state)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(men);
	if (m)
	{
		m->pops.State(id,state);
	}
}
int menu::State(int men,int id)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(men);
	if (m)
	{
		return m->pops.State(id);
	}
	return 0;
}

void menu::Selected(int men,int id,int s)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(men);
	if (m)
	{
		m->pops.Selected(id,s);
	}
}
int menu::Selected(int men,int id)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(men);
	if (m)
	{
		return m->pops.Selected(id);
	}
	return 0;
}

void menu::NoHelpMenu(int reason,void *proof)
{
	switch(reason)
	{
		case REASON_INOTHERMENU:
			{
				if (((menu*)proof)->p->hashelp)
				{
					p->nohelp=1;
					p->reason_inother=1;
				}
			}
			break;
		case REASON_SUBWINDOW:
			{
				Window w;
				XGetTransientForHint(display(),((Xwindows*)proof)->window(),&w);
				if (w)
				{
					p->nohelp=1;
					p->reason_subwin=1;
				}
			}
			break;
		case REASON_SUID:
			if (getuid()!=geteuid() && getuid())
			{
//				p->nohelp=1;
				reason_suid=1;
			}
			break;
		case REASON_HELPWINDOW:
//			p->nohelp=1;
			p->reason_inhelp=1;
			break;
	}
}

void menu::AddMenu(char *text)
{
	menupunkt *m;
	m=new menupunkt;
	m->text.Text(text);
	m->x=0;
	m->h=0;
	m->w=0;
	m->right=0;
	m->pops.ParentClass(this);
	m->pops.Name(text);
	p->addto=m;
	p->punkte.AddTail((node*)m);
}
void menu::AddHelpMenu(char *text)
{
	menupunkt *m;
	m=new menupunkt;
	m->text.Text(text);
	m->x=0;
	m->h=0;
	m->w=0;
	m->right=1;
	m->pops.Name(text);
	p->addto=m;
	p->punkte.AddTail((node*)m);
	if (!p->nohelp && !p->hashelp)
	{
		p->helpmenu();
		AddRuler();
		p->hashelp=p->punkte.Find((node*)m);
	}
}
void menu::Add(char *text,char *scut,int id)
{
	p->addto->pops.Add(text,scut,id);
}
void menu::AddHeadline(char *text)
{
	p->addto->pops.AddHeadline(text);
}
void menu::AddRuler(void)
{
	p->addto->pops.AddRuler();
}
void menu::AddSel(char *text,char *scut,int id)
{
	p->addto->pops.AddSel(text,scut,id);
}
void menu::AddMX(char **text,char **scut,int id)
{
	p->addto->pops.AddMX(text,scut,id);
}
void menu::AddSub(char *text)
{
	p->addto->pops.AddSub(text);
}
void menu::EndSub(void)
{
	p->addto->pops.EndSub();
}
void menu::Clear(void)
{
	menupunkt *m;
	if (p->offen)
	{
		p->offen->pops.Close();
	}
	while (m=(menupunkt*)p->punkte.RemTail())
	{
		m->pops.Free();
		delete m;
	}
	p->hashelp=0;
	p->addto=NULL;
	p->offen=NULL;
}
int menu::Menu(void)
{
	return p->sel;
}
int menu::MID(void)
{
	menupunkt *m=(menupunkt*)p->punkte.Find(p->sel);
	if (m)
	{
		return m->pops.PID();
	}
	return 0;
}
unsigned int menu::Selection(void)
{
	unsigned int s;
	s=(Menu()<<24)|MID();
	return s;
}
int pmenu::keyhook(popup *,int userdata)
{
	sel=punkte.Find((node*)userdata);
	return handleHelp();
}

void pmenu::draw(void)
{
	menupunkt *m=(menupunkt*)punkte.head;
	while (m->base.next)
	{
		if (m==offen)
		{
			if (new_style)
				o->borderDown(o,m->x,o->RealSize()+MTOP,m->w,o->height-o->RealSize()*2-MTOP-MBOTTOM);
			else
				o->borderUpFill(o,m->x,o->RealSize()+MTOP,m->w,o->height-o->RealSize()*2-MTOP-MBOTTOM);
		}
		else
		{
			if (new_style)
				XSetForeground(o->display(),o->gc,o->col_shinebackground());
			else
				XSetForeground(o->display(),o->gc,o->col_background());
			XFillRectangle(o->display(),o->win,o->gc,m->x,o->RealSize()+MTOP,m->w,o->height-o->RealSize()*2-MTOP-MBOTTOM);
		}
		m->text.draw_normal(o,m->x,0,m->w,m->h);
		m=(menupunkt*)m->base.next;
	}
}

void menu::GExpose(XEvent *event)
{
	if (wasfor && event)
	{
		wasfor->GExpose(event);
	}
	else
	{
		if (p->new_style)
		{
			XSetForeground(display(),gc,col_shadow2());
			XDrawLine(display(),win,gc,0,height-2,width,height-2);
			XSetForeground(display(),gc,col_shine2());
			XDrawLine(display(),win,gc,0,height-1,width,height-1);
		}
		else
		{
			borderUpFill(this,0,0,width,height);
		}

		p->draw();
	}
}
void menu::GActionPress(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
}
void menu::GActionRelease(int mx,int my,int rx,int ry,unsigned int state,int button,int reason)
{
}
static int rx,ry,sx,sy;
int menu::GActivate(XEvent *event,int why)
{
	menupunkt *m=(menupunkt*)p->punkte.head;
	while (m->base.next)
	{
		if (m->pops.PopupOpen())
		{
			return m->pops.GActivate(event,why);
		}
		m=(menupunkt*)m->base.next;
	}
	if (why==GA_BUTTONPRESS)
	{
//		ActivateKey();
		menupunkt *m=(menupunkt*)p->punkte.head;
		while (m->base.next)
		{
			if (event->xbutton.x>=m->x && event->xbutton.x<=m->x+m->w)
			{
				int wx,wy;
				GetPositionOnRoot(display(),win,wx,wy);
				m->pops.Open(m->x+wx,m->h+wy-UPPOP-RealSize()+2-MBOTTOM,m->w);
				p->offen=m;
				p->sel=p->punkte.Find((node*)p->offen);
				p->draw();
				rx=wx; ry=wy;
				sx=m->x+rx; sy=m->h+ry-UPPOP-RealSize()+2-MBOTTOM;
				MCursor(XC_arrow);
				return GAR_ACTIVE|GAR_MESSAGE;
			}
			m=(menupunkt*)m->base.next;
		}
	}
	return 0;
}
int menu::GSelected(XEvent *event,int why,int wasabove)
{
	int r=0;
	menupunkt *m=(menupunkt*)p->punkte.head;
	while (m->base.next)
	{
		if (m->pops.PopupOpen())
		{
			r=m->pops.GSelected(event,why,wasabove);
			if (r)
			{
				p->sel=p->punkte.Find((node*)p->offen);
				p->offen=NULL;
				p->draw();
				MCursor(-1);
				return p->handleHelp()?GSR_SELECTION|GSR_MESSAGE:0;
			}
			if (!m->pops.PopupOpen())
			{ // anderes auf
//				int rx,ry;
//				GetPositionOnRoot(display(),win,rx,ry);
				if (event->xbutton.y_root>=ry && event->xbutton.y_root<=ry+height)
				{
					menupunkt *m=(menupunkt*)p->punkte.head;
					while (m->base.next)
					{
						if (event->xbutton.x_root>=rx+m->x && event->xbutton.x_root<=rx+m->x+m->w)
						{
							m->pops.Open(m->x+rx,m->h+ry-UPPOP-RealSize()+2-MBOTTOM,m->w);
							p->offen=m;
							p->draw();
							sx=m->x+rx; sy=m->h+ry-UPPOP-RealSize()+2-MBOTTOM;
							return 0;
						}
						m=(menupunkt*)m->base.next;
					}
				}
				p->offen=NULL;
				p->draw();
				MCursor(-1);
			}
		}
		m=(menupunkt*)m->base.next;
	}
	return 0;
}

int menu::GMouseMove(int mx,int my,int rtx,int rty,unsigned int state,int button)
{
	menupunkt *m=(menupunkt*)p->punkte.head;
	int r=0;
	while (m->base.next)
	{
		if (m->pops.PopupOpen())
		{
			r=m->pops.GMouseMove(mx,my,rtx,rty,state,button)&GSR_MESSAGE;

//			int rx,ry,sx,sy;
//			GetPositionOnRoot(display(),win,rx,ry);
//			GetPositionOnRoot(display(),p->offen->pops.gg_win(),sx,sy);
			if (sy+my>=ry && sy+my<=ry+height)
			{
				menupunkt *m=(menupunkt*)p->punkte.head;
				while (m->base.next)
				{
					if (rtx>=rx+m->x && rtx<=rx+m->x+m->w)
					{
						if (m!=p->offen)
						{
							p->offen->pops.Close();
							m->pops.Open(m->x+rx,m->h+ry-UPPOP-RealSize()+2-MBOTTOM,m->w);
							p->offen=m;
							p->draw();
							p->sel=p->punkte.Find((node*)p->offen);
							sx=m->x+rx; sy=m->h+ry-UPPOP-RealSize()+2-MBOTTOM;
							r=GSR_MESSAGE;
						}
						return r;
					}
					m=(menupunkt*)m->base.next;
				}
			}
		}
		m=(menupunkt*)m->base.next;
	}
	return r;
}
void menu::GSetLimits(void)
{
	p->new_style=defaults.menunewstyle?1:0;
	if (p->new_style)
		BorderSize(1);
	else
		BorderSize(2);

	if (!p->nohelp && !p->hashelp)
	{
		p->hashelp=p->punkte.Count()+1;
		AddHelpMenu("Xclasses");
		p->helpmenu();
	}
	p->setwidth();
}

#endif
