/**
 * $Id: interface_panel.c,v 1.28 2004/12/14 18:18:31 ton Exp $
 *
 * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
 *
 * 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 (at your option) any later version. The Blender
 * Foundation also sells licenses for use in proprietary software under
 * the Blender License.  See http://www.blender.org/BL/ for information
 * about this.
 *
 * 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.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL/BL DUAL LICENSE BLOCK *****
 */

/* 
     a full doc with API notes can be found in bf-blender/blender/doc/interface_API.txt

 */
 

#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef WIN32
#include <unistd.h>
#else
#include <io.h>
#include "BLI_winstuff.h"
#endif   

#include "MEM_guardedalloc.h"

#include "PIL_time.h"

#include "BLI_blenlib.h"
#include "BLI_arithb.h"

#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "DNA_userdef_types.h"
#include "DNA_vec_types.h"

#include "BKE_blender.h"
#include "BKE_utildefines.h"
#include "BKE_global.h"

#include "BIF_gl.h"
#include "BIF_graphics.h"
#include "BIF_keyval.h"
#include "BIF_mainqueue.h"

#include "BIF_screen.h"
#include "BIF_toolbox.h"
#include "BIF_mywindow.h"
#include "BIF_space.h"
#include "BIF_glutil.h"
#include "BIF_interface.h"
#include "BIF_butspace.h"
#include "BIF_language.h"

#include "BSE_view.h"

#include "mydevice.h"
#include "interface.h"
#include "blendef.h"

// globals
extern float UIwinmat[4][4];

// internal prototypes
static void stow_unstow(uiBlock *block);


/* --------- generic helper drawng calls ---------------- */


#define UI_RB_ALPHA 16
static int roundboxtype= 15;

void uiSetRoundBox(int type)
{
	roundboxtype= type;
	
	/* flags to set which corners will become rounded:

	1------2
	|      |
	8------4
	*/
	
}

void gl_round_box(int mode, float minx, float miny, float maxx, float maxy, float rad)
{
	float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
	                  {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
	int a;
	
	/* mult */
	for(a=0; a<7; a++) {
		vec[a][0]*= rad; vec[a][1]*= rad;
	}

	glBegin(mode);

	/* start with corner right-bottom */
	if(roundboxtype & 4) {
		glVertex2f( maxx-rad, miny);
		for(a=0; a<7; a++) {
			glVertex2f( maxx-rad+vec[a][0], miny+vec[a][1]);
		}
		glVertex2f( maxx, miny+rad);
	}
	else glVertex2f( maxx, miny);
	
	/* corner right-top */
	if(roundboxtype & 2) {
		glVertex2f( maxx, maxy-rad);
		for(a=0; a<7; a++) {
			glVertex2f( maxx-vec[a][1], maxy-rad+vec[a][0]);
		}
		glVertex2f( maxx-rad, maxy);
	}
	else glVertex2f( maxx, maxy);
	
	/* corner left-top */
	if(roundboxtype & 1) {
		glVertex2f( minx+rad, maxy);
		for(a=0; a<7; a++) {
			glVertex2f( minx+rad-vec[a][0], maxy-vec[a][1]);
		}
		glVertex2f( minx, maxy-rad);
	}
	else glVertex2f( minx, maxy);
	
	/* corner left-bottom */
	if(roundboxtype & 8) {
		glVertex2f( minx, miny+rad);
		for(a=0; a<7; a++) {
			glVertex2f( minx+vec[a][1], miny+rad-vec[a][0]);
		}
		glVertex2f( minx+rad, miny);
	}
	else glVertex2f( minx, miny);
	
	glEnd();
}

static void round_box_shade_col(float *col1, float *col2, float fac)
{
	float col[3];

	col[0]= (fac*col1[0] + (1.0-fac)*col2[0]);
	col[1]= (fac*col1[1] + (1.0-fac)*col2[1]);
	col[2]= (fac*col1[2] + (1.0-fac)*col2[2]);
	
	glColor3fv(col);
}

/* linear horizontal shade within button or in outline */
void gl_round_box_shade(int mode, float minx, float miny, float maxx, float maxy, float rad, float shadetop, float shadedown)
{
	float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
	                  {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
	float div= maxy-miny;
	float coltop[3], coldown[3], color[4];
	int a;
	
	/* mult */
	for(a=0; a<7; a++) {
		vec[a][0]*= rad; vec[a][1]*= rad;
	}
	/* get current color, needs to be outside of glBegin/End */
	glGetFloatv(GL_CURRENT_COLOR, color);

	/* 'shade' defines strength of shading */	
	coltop[0]= color[0]+shadetop; if(coltop[0]>1.0) coltop[0]= 1.0;
	coltop[1]= color[1]+shadetop; if(coltop[1]>1.0) coltop[1]= 1.0;
	coltop[2]= color[2]+shadetop; if(coltop[2]>1.0) coltop[2]= 1.0;
	coldown[0]= color[0]+shadedown; if(coldown[0]<0.0) coldown[0]= 0.0;
	coldown[1]= color[1]+shadedown; if(coldown[1]<0.0) coldown[1]= 0.0;
	coldown[2]= color[2]+shadedown; if(coldown[2]<0.0) coldown[2]= 0.0;

	glShadeModel(GL_SMOOTH);
	glBegin(mode);

	/* start with corner right-bottom */
	if(roundboxtype & 4) {
		
		round_box_shade_col(coltop, coldown, 0.0);
		glVertex2f( maxx-rad, miny);
		
		for(a=0; a<7; a++) {
			round_box_shade_col(coltop, coldown, vec[a][1]/div);
			glVertex2f( maxx-rad+vec[a][0], miny+vec[a][1]);
		}
		
		round_box_shade_col(coltop, coldown, rad/div);
		glVertex2f( maxx, miny+rad);
	}
	else {
		round_box_shade_col(coltop, coldown, 0.0);
		glVertex2f( maxx, miny);
	}
	
	/* corner right-top */
	if(roundboxtype & 2) {
		
		round_box_shade_col(coltop, coldown, (div-rad)/div);
		glVertex2f( maxx, maxy-rad);
		
		for(a=0; a<7; a++) {
			round_box_shade_col(coltop, coldown, (div-rad+vec[a][1])/div);
			glVertex2f( maxx-vec[a][1], maxy-rad+vec[a][0]);
		}
		round_box_shade_col(coltop, coldown, 1.0);
		glVertex2f( maxx-rad, maxy);
	}
	else {
		round_box_shade_col(coltop, coldown, 1.0);
		glVertex2f( maxx, maxy);
	}
	
	/* corner left-top */
	if(roundboxtype & 1) {
		
		round_box_shade_col(coltop, coldown, 1.0);
		glVertex2f( minx+rad, maxy);
		
		for(a=0; a<7; a++) {
			round_box_shade_col(coltop, coldown, (div-vec[a][1])/div);
			glVertex2f( minx+rad-vec[a][0], maxy-vec[a][1]);
		}
		
		round_box_shade_col(coltop, coldown, (div-rad)/div);
		glVertex2f( minx, maxy-rad);
	}
	else {
		round_box_shade_col(coltop, coldown, 1.0);
		glVertex2f( minx, maxy);
	}
	
	/* corner left-bottom */
	if(roundboxtype & 8) {
		
		round_box_shade_col(coltop, coldown, rad/div);
		glVertex2f( minx, miny+rad);
		
		for(a=0; a<7; a++) {
			round_box_shade_col(coltop, coldown, (rad-vec[a][1])/div);
			glVertex2f( minx+vec[a][1], miny+rad-vec[a][0]);
		}
		
		round_box_shade_col(coltop, coldown, 0.0);
		glVertex2f( minx+rad, miny);
	}
	else {
		round_box_shade_col(coltop, coldown, 0.0);
		glVertex2f( minx, miny);
	}
	
	glEnd();
	glShadeModel(GL_FLAT);
}

/* only for headers */
static void gl_round_box_topshade(float minx, float miny, float maxx, float maxy, float rad)
{
	float vec[7][2]= {{0.195, 0.02}, {0.383, 0.067}, {0.55, 0.169}, {0.707, 0.293},
	                  {0.831, 0.45}, {0.924, 0.617}, {0.98, 0.805}};
	char col[7]= {140, 165, 195, 210, 230, 245, 255};
	int a;
	char alpha=255;
	
	if(roundboxtype & UI_RB_ALPHA) alpha= 128;
	
	/* mult */
	for(a=0; a<7; a++) {
		vec[a][0]*= rad; vec[a][1]*= rad;
	}

	/* shades from grey->white->grey */
	glBegin(GL_LINE_STRIP);
	
	if(roundboxtype & 3) {
		/* corner right-top */
		glColor4ub(140, 140, 140, alpha);
		glVertex2f( maxx, maxy-rad);
		for(a=0; a<7; a++) {
			glColor4ub(col[a], col[a], col[a], alpha);
			glVertex2f( maxx-vec[a][1], maxy-rad+vec[a][0]);
		}
		glColor4ub(225, 225, 225, alpha);
		glVertex2f( maxx-rad, maxy);
	
		
		/* corner left-top */
		glVertex2f( minx+rad, maxy);
		for(a=0; a<7; a++) {
			glColor4ub(col[6-a], col[6-a], col[6-a], alpha);
			glVertex2f( minx+rad-vec[a][0], maxy-vec[a][1]);
		}
		glVertex2f( minx, maxy-rad);
	}
	else {
		glColor4ub(225, 225, 225, alpha);
		glVertex2f( minx, maxy);
		glVertex2f( maxx, maxy);
	}
	
	glEnd();
}

/* for headers and floating panels */
void uiRoundBoxEmboss(float minx, float miny, float maxx, float maxy, float rad, int active)
{
	float color[4];
	
	if(roundboxtype & UI_RB_ALPHA) {
		glGetFloatv(GL_CURRENT_COLOR, color);
		color[3]= 0.5;
		glColor4fv(color);
		glEnable( GL_BLEND );
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	}
	
	/* solid part */
	//if(active)
	//	gl_round_box_shade(GL_POLYGON, minx, miny, maxx, maxy, rad, 0.10, -0.05);
	// else
	/* shading doesnt work for certain buttons yet (pulldown) need smarter buffer caching (ton) §*/
	gl_round_box(GL_POLYGON, minx, miny, maxx, maxy, rad);
	
	/* set antialias line */
	glEnable( GL_LINE_SMOOTH );
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	/* top shade */
	gl_round_box_topshade(minx+1, miny+1, maxx-1, maxy-1, rad);

	/* total outline */
	if(roundboxtype & UI_RB_ALPHA) glColor4ub(0,0,0, 128); else glColor4ub(0,0,0, 200);
	gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad);
   
	glDisable( GL_LINE_SMOOTH );

	/* bottom shade for header down */
	if((roundboxtype & 12)==12) {
		glColor4ub(0,0,0, 80);
		fdrawline(minx+rad-1.0, miny+1.0, maxx-rad+1.0, miny+1.0);
	}
	glDisable( GL_BLEND );
}


/* plain antialiased unfilled rectangle */
void uiRoundRect(float minx, float miny, float maxx, float maxy, float rad)
{
	float color[4];
	
	if(roundboxtype & UI_RB_ALPHA) {
		glGetFloatv(GL_CURRENT_COLOR, color);
		color[3]= 0.5;
		glColor4fv(color);
		glEnable( GL_BLEND );
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	}
	
	/* set antialias line */
	glEnable( GL_LINE_SMOOTH );
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad);
   
	glDisable( GL_BLEND );
	glDisable( GL_LINE_SMOOTH );
}



/* plain antialiased filled box */
void uiRoundBox(float minx, float miny, float maxx, float maxy, float rad)
{
	float color[4];
	
	if(roundboxtype & UI_RB_ALPHA) {
		glGetFloatv(GL_CURRENT_COLOR, color);
		color[3]= 0.5;
		glColor4fv(color);
		glEnable( GL_BLEND );
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
	}
	
	/* solid part */
	gl_round_box(GL_POLYGON, minx, miny, maxx, maxy, rad);
	
	/* set antialias line */
	glEnable( GL_LINE_SMOOTH );
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	gl_round_box(GL_LINE_LOOP, minx, miny, maxx, maxy, rad);
   
	glDisable( GL_BLEND );
	glDisable( GL_LINE_SMOOTH );
}


/* ************** panels ************* */

static void copy_panel_offset(Panel *pa, Panel *papar)
{
	/* with respect to sizes... papar is parent */

	pa->ofsx= papar->ofsx;
	pa->ofsy= papar->ofsy + papar->sizey-pa->sizey;
}



/* global... but will be NULLed after each 'newPanel' call */
static char *panel_tabbed=NULL, *group_tabbed=NULL;

void uiNewPanelTabbed(char *panelname, char *groupname)
{
	panel_tabbed= panelname;
	group_tabbed= groupname;
}

/* another global... */
static int pnl_control= UI_PNL_TRANSP;

void uiPanelControl(int control)
{
	pnl_control= control;
}

/* another global... */
static int pnl_handler= 0;

void uiSetPanelHandler(int handler)
{
	pnl_handler= handler;
}


/* ofsx/ofsy only used for new panel definitions */
/* return 1 if visible (create buttons!) */
int uiNewPanel(ScrArea *sa, uiBlock *block, char *panelname, char *tabname, int ofsx, int ofsy, int sizex, int sizey)
{
	Panel *pa, *palign;
	
	/* check if Panel exists, then use that one */
	pa= sa->panels.first;
	while(pa) {
		if( strncmp(pa->panelname, panelname, UI_MAX_NAME_STR)==0) {
			if( strncmp(pa->tabname, tabname, UI_MAX_NAME_STR)==0) {
				break;
			}
		}
		pa= pa->next;
	}
	
	if(pa==NULL) {
		
		/* new panel */
		pa= MEM_callocN(sizeof(Panel), "new panel");
		BLI_addtail(&sa->panels, pa);
		strncpy(pa->panelname, panelname, UI_MAX_NAME_STR);
		strncpy(pa->tabname, tabname, UI_MAX_NAME_STR);
	
		pa->ofsx= ofsx & ~(PNL_GRID-1);
		pa->ofsy= ofsy & ~(PNL_GRID-1);
		pa->sizex= sizex;
		pa->sizey= sizey;
		
		/* pre align, for good sorting later on */
		if(sa->spacetype==SPACE_BUTS && pa->prev) {
			SpaceButs *sbuts= sa->spacedata.first;
			
			palign= pa->prev;
			if(sbuts->align==BUT_VERTICAL) {
				pa->ofsy= palign->ofsy - pa->sizey - PNL_HEADER;
			}
			else if(sbuts->align==BUT_HORIZONTAL) {
				pa->ofsx= palign->ofsx + palign->sizex;
			}
		}
		/* make new Panel tabbed? */
		if(panel_tabbed && group_tabbed) {
			Panel *papar;
			for(papar= sa->panels.first; papar; papar= papar->next) {
				if(papar->active && papar->paneltab==NULL) {
					if( strncmp(panel_tabbed, papar->panelname, UI_MAX_NAME_STR)==0) {
						if( strncmp(group_tabbed, papar->tabname, UI_MAX_NAME_STR)==0) {
							pa->paneltab= papar;
							copy_panel_offset(pa, papar);
							break;
						}
					}
				}
			} 
		}
	}
	
	block->panel= pa;
	block->handler= pnl_handler;
	pa->active= 1;
	pa->control= pnl_control;
	
	/* global control over this feature; UI_PNL_TO_MOUSE only called for hotkey panels */
	if(U.uiflag & USER_PANELPINNED);
	else if(pnl_control & UI_PNL_TO_MOUSE) {
		short mval[2];
		
		Mat4CpyMat4(UIwinmat, block->winmat);	// can be first event here
		uiGetMouse(block->win, mval);		
		pa->ofsx= mval[0]-pa->sizex/2;
		pa->ofsy= mval[1]-pa->sizey/2;
		
		if(pa->flag & PNL_CLOSED) pa->flag &= ~PNL_CLOSED;
	}
	
	if(pnl_control & UI_PNL_UNSTOW) {
		if(pa->flag & PNL_CLOSEDY) {
			pa->flag &= ~PNL_CLOSED;
			stow_unstow(block); // toggles!
		}
	}
	
	/* clear ugly globals */
	panel_tabbed= group_tabbed= NULL;
	pnl_handler= 0;
	pnl_control= UI_PNL_TRANSP; // back to default
	
	if(block->panel->paneltab) return 0;
	if(block->panel->flag & PNL_CLOSED) return 0;

	return 1;
}

void uiFreePanels(ListBase *lb)
{
	Panel *panel;
	
	while( (panel= lb->first) ) {
		BLI_remlink(lb, panel);
		MEM_freeN(panel);
	}
}

void uiNewPanelHeight(uiBlock *block, int sizey)
{
	if(sizey<64) sizey= 64;
	
	if(block->panel) {
		block->panel->ofsy+= (block->panel->sizey - sizey);
		block->panel->sizey= sizey;
	}
}

static int panel_has_tabs(Panel *panel)
{
	Panel *pa= curarea->panels.first;
	
	if(panel==NULL) return 0;
	
	while(pa) {
		if(pa->paneltab==panel) return 1;
		pa= pa->next;
	}
	return 0;
}

static void ui_scale_panel_block(uiBlock *block)
{
	uiBut *but;
	float facx= 1.0, facy= 1.0;
	int centrex= 0, topy=0, tabsy=0;
	
	if(block->panel==NULL) return;

	if(block->autofill) ui_autofill(block);
	/* buttons min/max centered, offset calculated */
	uiBoundsBlock(block, 0);

	if( block->maxx-block->minx > block->panel->sizex - 2*PNL_SAFETY ) {
		facx= (block->panel->sizex - (2*PNL_SAFETY))/( block->maxx-block->minx );
	}
	else centrex= (block->panel->sizex-( block->maxx-block->minx ) - 2*PNL_SAFETY)/2;
	
	// tabsy= PNL_HEADER*panel_has_tabs(block->panel);
	if( (block->maxy-block->miny) > block->panel->sizey - 2*PNL_SAFETY - tabsy) {
		facy= (block->panel->sizey - (2*PNL_SAFETY) - tabsy)/( block->maxy-block->miny );
	}
	else topy= (block->panel->sizey- 2*PNL_SAFETY - tabsy) - ( block->maxy-block->miny ) ;

	but= block->buttons.first;
	while(but) {
		but->x1= PNL_SAFETY+centrex+ facx*(but->x1-block->minx);
		but->y1= PNL_SAFETY+topy   + facy*(but->y1-block->miny);
		but->x2= PNL_SAFETY+centrex+ facx*(but->x2-block->minx);
		but->y2= PNL_SAFETY+topy   + facy*(but->y2-block->miny);
		if(facx!=1.0) ui_check_but(but);	/* for strlen */
		but= but->next;
	}

	block->maxx= block->panel->sizex;
	block->maxy= block->panel->sizey;
	block->minx= block->miny= 0.0;
	
}

// for 'home' key
void uiSetPanel_view2d(ScrArea *sa)
{
	Panel *pa;
	float minx=10000, maxx= -10000, miny=10000, maxy= -10000;
	int done=0;
	
	pa= sa->panels.first;
	while(pa) {
		if(pa->active) {
			done= 1;
			if(pa->ofsx < minx) minx= pa->ofsx;
			if(pa->ofsx+pa->sizex > maxx) maxx= pa->ofsx+pa->sizex;
			if(pa->ofsy < miny) miny= pa->ofsy;
			if(pa->ofsy+pa->sizey+PNL_HEADER > maxy) maxy= pa->ofsy+pa->sizey+PNL_HEADER;
		}
		pa= pa->next;
	}
	if(done) {
		G.v2d->tot.xmin= minx-PNL_DIST;
		G.v2d->tot.xmax= maxx+PNL_DIST;
		G.v2d->tot.ymin= miny-PNL_DIST;
		G.v2d->tot.ymax= maxy+PNL_DIST;
	}
	else {
		uiBlock *block;

		G.v2d->tot.xmin= 0;
		G.v2d->tot.xmax= 1280;
		G.v2d->tot.ymin= 0;
		G.v2d->tot.ymax= 228;

		/* no panels, but old 'loose' buttons, as in old logic editor */
		for(block= sa->uiblocks.first; block; block= block->next) {
			if(block->win==sa->win) {
				if(block->minx < G.v2d->tot.xmin) G.v2d->tot.xmin= block->minx;
				if(block->maxx > G.v2d->tot.xmax) G.v2d->tot.xmax= block->maxx; 
				if(block->miny < G.v2d->tot.ymin) G.v2d->tot.ymin= block->miny;
				if(block->maxy > G.v2d->tot.ymax) G.v2d->tot.ymax= block->maxy; 
			}
		}
	}
	
}

// make sure the panels are not outside 'tot' area
void uiMatchPanel_view2d(ScrArea *sa)
{
	Panel *pa;
	int done=0;
	
	pa= sa->panels.first;
	while(pa) {
		if(pa->active) {
			done= 1;
			if(pa->ofsx < G.v2d->tot.xmin) G.v2d->tot.xmin= pa->ofsx;
			if(pa->ofsx+pa->sizex > G.v2d->tot.xmax) 
				G.v2d->tot.xmax= pa->ofsx+pa->sizex;
			if(pa->ofsy < G.v2d->tot.ymin) G.v2d->tot.ymin= pa->ofsy;
			if(pa->ofsy+pa->sizey+PNL_HEADER > G.v2d->tot.ymax) 
				G.v2d->tot.ymax= pa->ofsy+pa->sizey+PNL_HEADER;
		}
		pa= pa->next;
	}
	if(done==0) {
		uiBlock *block;
		/* no panels, but old 'loose' buttons, as in old logic editor */
		for(block= sa->uiblocks.first; block; block= block->next) {
			if(block->win==sa->win) {
				if(block->minx < G.v2d->tot.xmin) G.v2d->tot.xmin= block->minx;
				if(block->maxx > G.v2d->tot.xmax) G.v2d->tot.xmax= block->maxx; 
				if(block->miny < G.v2d->tot.ymin) G.v2d->tot.ymin= block->miny;
				if(block->maxy > G.v2d->tot.ymax) G.v2d->tot.ymax= block->maxy; 
			}
		}
	}	
}

/* extern used ny previewrender */
void uiPanelPush(uiBlock *block)
{
	glPushMatrix(); 
	if(block->panel) {
		glTranslatef((float)block->panel->ofsx, (float)block->panel->ofsy, 0.0);
		i_translate((float)block->panel->ofsx, (float)block->panel->ofsy, 0.0, UIwinmat);
	}
}

void uiPanelPop(uiBlock *block)
{
	glPopMatrix();
	Mat4CpyMat4(UIwinmat, block->winmat);
}

uiBlock *uiFindOpenPanelBlockName(ListBase *lb, char *name)
{
	uiBlock *block;
	
	for(block= lb->first; block; block= block->next) {
		if(block->panel && block->panel->active && block->panel->paneltab==NULL) {
			if(block->panel->flag & PNL_CLOSED);
			else if(strncmp(name, block->panel->panelname, UI_MAX_NAME_STR)==0) break;
		}
	}
	return block;
}

static void ui_draw_anti_tria(float x1, float y1, float x2, float y2, float x3, float y3)
{

	// we draw twice, anti polygons not widely supported...

	glBegin(GL_POLYGON);
	glVertex2f(x1, y1);
	glVertex2f(x2, y2);
	glVertex2f(x3, y3);
	glEnd();

	/* set antialias line */
	glEnable( GL_LINE_SMOOTH );
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	glBegin(GL_LINE_LOOP);
	glVertex2f(x1, y1);
	glVertex2f(x2, y2);
	glVertex2f(x3, y3);
	glEnd();
	
	glDisable( GL_LINE_SMOOTH );
	glDisable( GL_BLEND );
	
}

/* triangle 'icon' for panel header */
static void ui_draw_tria_icon(float x, float y, float aspect, char dir)
{
	BIF_ThemeColor(TH_TEXT_HI);
	
	if(dir=='h') {
		ui_draw_anti_tria( x, y+1, x, y+10.0, x+7, y+6.25);
	}
	else {
		ui_draw_anti_tria( x-2, y+8,  x+9-2, y+8, x+4.75-2, y+1);	
	}
}

static void ui_draw_anti_x(float x1, float y1, float x2, float y2)
{

	/* set antialias line */
	glEnable( GL_LINE_SMOOTH );
	glEnable( GL_BLEND );
	glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

	glLineWidth(2.0);
	
	fdrawline(x1, y1, x2, y2);
	fdrawline(x1, y2, x2, y1);
	
	glLineWidth(1.0);
	
	glDisable( GL_LINE_SMOOTH );
	glDisable( GL_BLEND );
	
}

/* x 'icon' for panel header */
static void ui_draw_x_icon(float x, float y)
{
	BIF_ThemeColor(TH_TEXT_HI);

	ui_draw_anti_x( x, y, x+9.375, y+9.375);

}

#if 0
static void ui_set_panel_pattern(char dir)
{
	static int firsttime= 1;
	static GLubyte path[4*32], patv[4*32];
	int a,b,i=0;

	if(firsttime) {
		firsttime= 0;
		for(a=0; a<128; a++) patv[a]= 0x33;
		for(a=0; a<8; a++) {
			for(b=0; b<4; b++) path[i++]= 0xff;	/* 1 scanlines */
			for(b=0; b<12; b++) path[i++]= 0x0;	/* 3 lines */
		}
	}
	glEnable(GL_POLYGON_STIPPLE);
	if(dir=='h') glPolygonStipple(path);	
	else glPolygonStipple(patv);	
}
#endif

static char *ui_block_cut_str(uiBlock *block, char *str, short okwidth)
{
	short width, ofs=strlen(str);
	static char str1[128];
	
	if(ofs>127) return str;
	
	width= block->aspect*BIF_GetStringWidth(block->curfont, str, (U.transopts & USER_TR_BUTTONS));

	if(width <= okwidth) return str;
	strcpy(str1, str);
	
	while(width > okwidth && ofs>0) {
		ofs--;
		str1[ofs]= 0;
		
		width= block->aspect*BIF_GetStringWidth(block->curfont, str1, 0);
		
		if(width < 10) break;
	}
	return str1;
}


#define PNL_ICON 	20
#define PNL_DRAGGER	20


static void ui_draw_panel_header(uiBlock *block)
{
	Panel *pa, *panel= block->panel;
	float width;
	int a, nr= 1, pnl_icons;
	char *str;
	
	/* count */
	pa= curarea->panels.first;
	while(pa) {
		if(pa->active) {
			if(pa->paneltab==panel) nr++;
		}
		pa= pa->next;
	}

	pnl_icons= PNL_ICON+8;
	if(panel->control & UI_PNL_CLOSE) pnl_icons+= PNL_ICON;

	if(nr==1) {
		// full header
		BIF_ThemeColorShade(TH_HEADER, -30);
		uiSetRoundBox(3);
		uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);

		/* active tab */
		/* draw text label */
		BIF_ThemeColor(TH_TEXT_HI);
		ui_rasterpos_safe(4+block->minx+pnl_icons, block->maxy+5, block->aspect);
		BIF_DrawString(block->curfont, block->panel->panelname, (U.transopts & USER_TR_BUTTONS));
		return;
	}
	
	// tabbed, full header brighter
	//BIF_ThemeColorShade(TH_HEADER, 0);
	//uiSetRoundBox(3);
	//uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);

	a= 0;
	width= (panel->sizex - 3 - pnl_icons - PNL_ICON)/nr;
	pa= curarea->panels.first;
	while(pa) {
		if(pa->active==0);
		else if(pa==panel) {
			/* active tab */
		
			/* draw the active tab */
			uiSetRoundBox(3);
			BIF_ThemeColorShade(TH_HEADER, -3);
			uiRoundBox(2+pnl_icons+a*width, panel->sizey-1, pnl_icons+(a+1)*width, panel->sizey+PNL_HEADER-3, 8);

			/* draw the active text label */
			BIF_ThemeColor(TH_TEXT);
			ui_rasterpos_safe(16+pnl_icons+a*width, panel->sizey+4, block->aspect);
			str= ui_block_cut_str(block, pa->panelname, (short)(width-10));
			BIF_DrawString(block->curfont, str, (U.transopts & USER_TR_BUTTONS));

			a++;
		}
		else if(pa->paneltab==panel) {
			/* draw an inactive tab */
			uiSetRoundBox(3);
			BIF_ThemeColorShade(TH_HEADER, -60);
			uiRoundBox(2+pnl_icons+a*width, panel->sizey, pnl_icons+(a+1)*width, panel->sizey+PNL_HEADER-3, 8);
			
			/* draw an inactive tab label */
			BIF_ThemeColorShade(TH_TEXT_HI, -40);
			ui_rasterpos_safe(16+pnl_icons+a*width, panel->sizey+4, block->aspect);
			str= ui_block_cut_str(block, pa->panelname, (short)(width-10));
			BIF_DrawString(block->curfont, str, (U.transopts & USER_TR_BUTTONS));
				
			a++;
		}
		pa= pa->next;
	}
	
	// dragger
	/*
	uiSetRoundBox(15);
	BIF_ThemeColorShade(TH_HEADER, -70);
	uiRoundBox(panel->sizex-PNL_ICON+5, panel->sizey+5, panel->sizex-5, panel->sizey+PNL_HEADER-5, 5);
	*/
	
}

void ui_draw_panel(uiBlock *block)
{
	Panel *panel= block->panel;
	int ofsx;
	
	if(panel->paneltab) return;

	/* if the panel is minimised vertically:
	 * (------)
	 */
	if(panel->flag & PNL_CLOSEDY) {
		/* draw a little rounded box, the size of the header */
		uiSetRoundBox(15);
		BIF_ThemeColorShade(TH_HEADER, -30);
		uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
		
		/* title */
		ofsx= PNL_ICON+8;
		if(panel->control & UI_PNL_CLOSE) ofsx+= PNL_ICON;
		BIF_ThemeColor(TH_TEXT_HI);
		ui_rasterpos_safe(4+block->minx+ofsx, block->maxy+5, block->aspect);
		BIF_DrawString(block->curfont, panel->panelname, (U.transopts & USER_TR_BUTTONS));

		/*  border */
		if(panel->flag & PNL_SELECT) {
			BIF_ThemeColorShade(TH_HEADER, -120);
			uiRoundRect(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
		}
		/* if it's being overlapped by a panel being dragged */
		if(panel->flag & PNL_OVERLAP) {
			BIF_ThemeColor(TH_TEXT_HI);
			uiRoundRect(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
		}
	
	}
	/* if the panel is minimised horizontally:
	 * /-\
	 *  |
	 *  |
	 *  |
	 * \_/
	 */
	else if(panel->flag & PNL_CLOSEDX) {
		char str[4];
		int a, end, ofs;
		
		/* draw a little rounded box, the size of the header, rotated 90 deg */ 
		uiSetRoundBox(15);
		BIF_ThemeColorShade(TH_HEADER, -30);
		uiRoundBox(block->minx, block->miny, block->minx+PNL_HEADER, block->maxy+PNL_HEADER, 8);
	
		/* title, only the initial character for now */
		BIF_ThemeColor(TH_TEXT_HI);
		str[1]= 0;
		end= strlen(panel->panelname);
		ofs= 20;
		for(a=0; a<end; a++) {
			str[0]= panel->panelname[a];
			if( isupper(str[0]) ) {
				ui_rasterpos_safe(block->minx+5, block->maxy-ofs, block->aspect);
				BIF_DrawString(block->curfont, str, 0);
				ofs+= 15;
			}
		}
		
		/* border */
		if(panel->flag & PNL_SELECT) {
			BIF_ThemeColorShade(TH_HEADER, -120);
			uiRoundRect(block->minx, block->miny, block->minx+PNL_HEADER, block->maxy+PNL_HEADER, 8);
		}
		if(panel->flag & PNL_OVERLAP) {
			BIF_ThemeColor(TH_TEXT_HI);
			uiRoundRect(block->minx, block->miny, block->minx+PNL_HEADER, block->maxy+PNL_HEADER, 8);
		}
	
	}
	/* an open panel */
	else {
		/* all panels now... */
		if(panel->control & UI_PNL_SOLID) {
			BIF_ThemeColorShade(TH_HEADER, -30);

			uiSetRoundBox(3);
			uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);

			// blend now for panels in 3d window, test...
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			glEnable(GL_BLEND);
			BIF_ThemeColor4(TH_PANEL);
			
			uiSetRoundBox(12);
			uiRoundBox(block->minx, block->miny, block->maxx, block->maxy, 8);

			// glRectf(block->minx, block->miny, block->maxx, block->maxy);
			
			/* shadow */
			/*
			glColor4ub(0, 0, 0, 40);
			
			fdrawline(block->minx+2, block->miny-1, block->maxx+1, block->miny-1);
			fdrawline(block->maxx+1, block->miny-1, block->maxx+1, block->maxy+7);
			
			glColor4ub(0, 0, 0, 10);
			
			fdrawline(block->minx+3, block->miny-2, block->maxx+2, block->miny-2);
			fdrawline(block->maxx+2, block->miny-2, block->maxx+2, block->maxy+6);	
			
			*/
			
			glDisable(GL_BLEND);
		}
		/* floating panel */
		else if(panel->control & UI_PNL_TRANSP) {
			BIF_ThemeColorShade(TH_HEADER, -30);
			uiSetRoundBox(3);
			uiRoundBox(block->minx, block->maxy, block->maxx, block->maxy+PNL_HEADER, 8);
			
			glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
			glEnable(GL_BLEND);
			BIF_ThemeColor4(TH_PANEL);
			glRectf(block->minx, block->miny, block->maxx, block->maxy);
	
			glDisable(GL_BLEND);
		}
		
		/* draw the title, tabs, etc in the header */
		ui_draw_panel_header(block);

		/* in some occasions, draw a border */
		if(panel->flag & PNL_SELECT) {
			if(panel->control & UI_PNL_SOLID) uiSetRoundBox(15);
			else uiSetRoundBox(3);
			
			BIF_ThemeColorShade(TH_HEADER, -120);
			uiRoundRect(block->minx, block->miny, block->maxx, block->maxy+PNL_HEADER, 8);
		}
		if(panel->flag & PNL_OVERLAP) {
			if(panel->control & UI_PNL_SOLID) uiSetRoundBox(15);
			else uiSetRoundBox(3);
			
			BIF_ThemeColor(TH_TEXT_HI);
			uiRoundRect(block->minx, block->miny, block->maxx, block->maxy+PNL_HEADER, 8);
		}
		
		/* and a soft shadow-line for now */
		/*
		glEnable( GL_BLEND );
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
		glColor4ub(0, 0, 0, 50);
		fdrawline(block->maxx, block->miny, block->maxx, block->maxy+PNL_HEADER/2);
		fdrawline(block->minx, block->miny, block->maxx, block->miny);
		glDisable(GL_BLEND);
		*/

	}
	
	/* draw optional close icon */
	
	ofsx= 6;
	if(panel->control & UI_PNL_CLOSE) {
	
		ui_draw_x_icon(block->minx+2+ofsx, block->maxy+5);
		/*
		glRasterPos2f(block->minx+4, block->maxy+3);
		if(block->aspect>1.1) glPixelZoom(1.0/block->aspect, 1.0/block->aspect);
		BIF_draw_icon(ICON_PANEL_CLOSE);
		if(block->aspect>1.1) glPixelZoom(1.0, 1.0);
		*/
		ofsx= 22;
	}

	/* draw collapse icon */
	
	if(panel->flag & PNL_CLOSEDY)
		ui_draw_tria_icon(block->minx+6+ofsx, block->maxy+5, block->aspect, 'h');
	else if(panel->flag & PNL_CLOSEDX)
		ui_draw_tria_icon(block->minx+7, block->maxy+2, block->aspect, 'h');
	else
		ui_draw_tria_icon(block->minx+6+ofsx, block->maxy+5, block->aspect, 'v');


}

static void ui_redraw_select_panel(ScrArea *sa)
{
	/* only for beauty, make sure the panel thats moved is on top */
	/* better solution later? */
	uiBlock *block;
	
	for(block= sa->uiblocks.first; block; block= block->next) {
		if(block->panel && (block->panel->flag & PNL_SELECT)) {
			uiDrawBlock(block);
		}
	}

}


/* ------------ panel alignment ---------------- */


/* this function is needed because uiBlock and Panel itself dont
change sizey or location when closed */
static int get_panel_real_ofsy(Panel *pa)
{
	if(pa->flag & PNL_CLOSEDY) return pa->ofsy+pa->sizey;
	else if(pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDY)) return pa->ofsy+pa->sizey;
	else return pa->ofsy;
}

static int get_panel_real_ofsx(Panel *pa)
{
	if(pa->flag & PNL_CLOSEDX) return pa->ofsx+PNL_HEADER;
	else if(pa->paneltab && (pa->paneltab->flag & PNL_CLOSEDX)) return pa->ofsx+PNL_HEADER;
	else return pa->ofsx+pa->sizex;
}


typedef struct PanelSort {
	Panel *pa, *orig;
} PanelSort;

/* note about sorting;
   the sortcounter has a lower value for new panels being added.
   however, that only works to insert a single panel, when more new panels get
   added the coordinates of existing panels and the previously stored to-be-insterted
   panels do not match for sorting */

static int find_leftmost_panel(const void *a1, const void *a2)
{
	const PanelSort *ps1=a1, *ps2=a2;
	
	if( ps1->pa->ofsx > ps2->pa->ofsx) return 1;
	else if( ps1->pa->ofsx < ps2->pa->ofsx) return -1;
	else if( ps1->pa->sortcounter > ps2->pa->sortcounter) return 1;
	else if( ps1->pa->sortcounter < ps2->pa->sortcounter) return -1;

	return 0;
}


static int find_highest_panel(const void *a1, const void *a2)
{
	const PanelSort *ps1=a1, *ps2=a2;
	
	if( ps1->pa->ofsy < ps2->pa->ofsy) return 1;
	else if( ps1->pa->ofsy > ps2->pa->ofsy) return -1;
	else if( ps1->pa->sortcounter > ps2->pa->sortcounter) return 1;
	else if( ps1->pa->sortcounter < ps2->pa->sortcounter) return -1;
	
	return 0;
}

/* this doesnt draw */
/* returns 1 when it did something */
int uiAlignPanelStep(ScrArea *sa, float fac)
{
	SpaceButs *sbuts= sa->spacedata.first;
	Panel *pa;
	PanelSort *ps, *panelsort, *psnext;
	static int sortcounter= 0;
	int a, tot=0, done;
	
	if(sa->spacetype!=SPACE_BUTS) {
		return 0;
	}
	
	/* count active, not tabbed Panels */
	for(pa= sa->panels.first; pa; pa= pa->next) {
		if(pa->active && pa->paneltab==NULL) tot++;
	}

	if(tot==0) return 0;

	/* extra; change close direction? */
	for(pa= sa->panels.first; pa; pa= pa->next) {
		if(pa->active && pa->paneltab==NULL) {
			if( (pa->flag & PNL_CLOSEDX) && (sbuts->align==BUT_VERTICAL) )
				pa->flag ^= PNL_CLOSED;
			
			else if( (pa->flag & PNL_CLOSEDY) && (sbuts->align==BUT_HORIZONTAL) )
				pa->flag ^= PNL_CLOSED;
			
		}
	}

	panelsort= MEM_callocN( tot*sizeof(PanelSort), "panelsort");
	
	/* fill panelsort array */
	ps= panelsort;
	for(pa= sa->panels.first; pa; pa= pa->next) {
		if(pa->active && pa->paneltab==NULL) {
			ps->pa= MEM_dupallocN(pa);
			ps->orig= pa;
			ps++;
		}
	}
	
	if(sbuts->align==BUT_VERTICAL) 
		qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel);
	else
		qsort(panelsort, tot, sizeof(PanelSort), find_leftmost_panel);

	
	/* no smart other default start loc! this keeps switching f5/f6/etc compatible */
	ps= panelsort;
	ps->pa->ofsx= 0;
	ps->pa->ofsy= 0;
	
	for(a=0 ; a<tot-1; a++, ps++) {
		psnext= ps+1;
		
		if(sbuts->align==BUT_VERTICAL) {
			psnext->pa->ofsx = ps->pa->ofsx;
			psnext->pa->ofsy = get_panel_real_ofsy(ps->pa) - psnext->pa->sizey-PNL_HEADER-PNL_DIST;
		}
		else {
			psnext->pa->ofsx = get_panel_real_ofsx(ps->pa)+PNL_DIST;
			psnext->pa->ofsy = ps->pa->ofsy + ps->pa->sizey - psnext->pa->sizey;
		}
	}
	
	/* we interpolate */
	done= 0;
	ps= panelsort;
	for(a=0; a<tot; a++, ps++) {
		if( (ps->pa->flag & PNL_SELECT)==0) {
			if( (ps->orig->ofsx != ps->pa->ofsx) || (ps->orig->ofsy != ps->pa->ofsy)) {
				ps->orig->ofsx= floor(0.5 + fac*ps->pa->ofsx + (1.0-fac)*ps->orig->ofsx);
				ps->orig->ofsy= floor(0.5 + fac*ps->pa->ofsy + (1.0-fac)*ps->orig->ofsy);
				done= 1;
			}
		}
	}

	/* copy locations to tabs */
	for(pa= sa->panels.first; pa; pa= pa->next) {
		if(pa->paneltab && pa->active) {
			copy_panel_offset(pa, pa->paneltab);
		}
	}

	/* set counter, used for sorting with newly added panels */
	sortcounter++;
	for(pa= sa->panels.first; pa; pa= pa->next) {
		if(pa->active) pa->sortcounter= sortcounter;
	}

	/* free panelsort array */
	ps= panelsort;
	for(a=0; a<tot; a++, ps++) {
		MEM_freeN(ps->pa);
	}
	MEM_freeN(panelsort);
	
	return done;
}


static void ui_animate_panels(ScrArea *sa)
{
	double time=0, ltime;
	float result= 0.0, fac= 0.2;
	
	ltime = PIL_check_seconds_timer();

	/* for max 1 second, interpolate positions */
	while(TRUE) {
	
		if( uiAlignPanelStep(sa, fac) ) {
			/* warn: this re-allocs uiblocks! */
			scrarea_do_windraw(curarea);
			ui_redraw_select_panel(curarea);
			screen_swapbuffers();
		}
		else {
			addqueue(curarea->win, REDRAW,1 );	// because 'Animate' is also called as redraw
			break;
		}
		
		if(result >= 1.0) break;
		
		if(result==0.0) { // firsttime
			time = PIL_check_seconds_timer()-ltime;
			if(time > 0.5) fac= 0.7;
			else if(time > 0.2) fac= 0.5;
			else if(time > 0.1) fac= 0.4;	
			else if(time > 0.05) fac= 0.3; // 11 steps
		}
		
		result= fac + (1.0-fac)*result;
		
		if(result > 0.98) {
			result= 1.0;
			fac= 1.0;
		}
	}
}

/* only draws blocks with panels */
void uiDrawBlocksPanels(ScrArea *sa, int re_align)
{
	uiBlock *block;
	Panel *panot, *panew, *patest;
	
	/* scaling contents */
	block= sa->uiblocks.first;
	while(block) {
		if(block->panel) ui_scale_panel_block(block);
		block= block->next;
	}

	/* consistancy; are panels not made, whilst they have tabs */
	for(panot= sa->panels.first; panot; panot= panot->next) {
		if(panot->active==0) { // not made

			for(panew= sa->panels.first; panew; panew= panew->next) {
				if(panew->active) {
					if(panew->paneltab==panot) { // panew is tab in notmade pa
						break;
					}
				}
			}
			/* now panew can become the new parent, check all other tabs */
			if(panew) {
				for(patest= sa->panels.first; patest; patest= patest->next) {
					if(patest->paneltab == panot) {
						patest->paneltab= panew;
					}
				}
				panot->paneltab= panew;
				panew->paneltab= NULL;
				addqueue(sa->win, REDRAW, 1);	// the buttons panew were not made
			}
		}	
	}

	/* re-align */
	if(re_align) uiAlignPanelStep(sa, 1.0);
	
	/* clip panels (headers) for non-butspace situations (maybe make optimized event later) */
	if(sa->spacetype!=SPACE_BUTS) {
		SpaceLink *sl= sa->spacedata.first;
		for(block= sa->uiblocks.first; block; block= block->next) {
			if(block->panel && block->panel->active && block->panel->paneltab == NULL) {
				float dx=0.0, dy=0.0, minx, miny, maxx, maxy;
				
				minx= sl->blockscale*block->panel->ofsx;
				maxx= sl->blockscale*(block->panel->ofsx+block->panel->sizex);
				miny= sl->blockscale*(block->panel->ofsy+block->panel->sizey);
				maxy= sl->blockscale*(block->panel->ofsy+block->panel->sizey+PNL_HEADER);
				
				if(minx<0.0) dx= -minx;
				else if(maxx > (float)sa->winx) dx= sa->winx-maxx;
				if( minx + dx < 0.0) dx= -minx; // when panel cant fit, put it fixed here
					
				if(miny<0.0) dy= -miny;
				else if(maxy > (float)sa->winy) dy= sa->winy-maxy;
				if( miny + dy < 0.0) dy= -miny; // when panel cant fit, put it fixed here
				
				block->panel->ofsx+= dx/sl->blockscale;
				block->panel->ofsy+= dy/sl->blockscale;

				/* copy locations */
				for(patest= sa->panels.first; patest; patest= patest->next) {
					if(patest->paneltab==block->panel) copy_panel_offset(patest, block->panel);
				}
				
			}
		}
	}

	/* draw */
	block= sa->uiblocks.first;
	while(block) {
		if(block->panel) uiDrawBlock(block);
		block= block->next;
	}

}



/* ------------ panel merging ---------------- */

static void check_panel_overlap(ScrArea *sa, Panel *panel)
{
	Panel *pa= sa->panels.first;

	/* also called with panel==NULL for clear */
	
	while(pa) {
		pa->flag &= ~PNL_OVERLAP;
		if(panel && (pa != panel)) {
			if(pa->paneltab==NULL && pa->active) {
				float safex= 0.2, safey= 0.2;
				
				if( pa->flag & PNL_CLOSEDX) safex= 0.05;
				else if(pa->flag & PNL_CLOSEDY) safey= 0.05;
				else if( panel->flag & PNL_CLOSEDX) safex= 0.05;
				else if(panel->flag & PNL_CLOSEDY) safey= 0.05;
				
				if( pa->ofsx > panel->ofsx- safex*panel->sizex)
				if( pa->ofsx+pa->sizex < panel->ofsx+ (1.0+safex)*panel->sizex)
				if( pa->ofsy > panel->ofsy- safey*panel->sizey)
				if( pa->ofsy+pa->sizey < panel->ofsy+ (1.0+safey)*panel->sizey)
					pa->flag |= PNL_OVERLAP;
			}
		}
		
		pa= pa->next;
	}
}

static void test_add_new_tabs(ScrArea *sa)
{
	Panel *pa, *pasel=NULL, *palap=NULL;
	/* search selected and overlapped panel */
	
	pa= sa->panels.first;
	while(pa) {
		if(pa->active) {
			if(pa->flag & PNL_SELECT) pasel= pa;
			if(pa->flag & PNL_OVERLAP) palap= pa;
		}
		pa= pa->next;
	}
	
	if(pasel && palap==NULL) {

		/* copy locations */
		pa= sa->panels.first;
		while(pa) {
			if(pa->paneltab==pasel) {
				copy_panel_offset(pa, pasel);
			}
			pa= pa->next;
		}
	}
	
	if(pasel==NULL || palap==NULL) return;
	
	/* the overlapped panel becomes a tab */
	palap->paneltab= pasel;
	
	/* the selected panel gets coords of overlapped one */
	copy_panel_offset(pasel, palap);

	/* and its tabs */
	pa= sa->panels.first;
	while(pa) {
		if(pa->paneltab == pasel) {
			copy_panel_offset(pa, palap);
		}
		pa= pa->next;
	}
	
	/* but, the overlapped panel already can have tabs too! */
	pa= sa->panels.first;
	while(pa) {
		if(pa->paneltab == palap) {
			pa->paneltab = pasel;
		}
		pa= pa->next;
	}
}

/* ------------ panel drag ---------------- */


void ui_drag_panel(uiBlock *block)
{
	Panel *panel= block->panel;
	short align=0, first=1, ofsx, ofsy, dx=0, dy=0, dxo=0, dyo=0, mval[2], mvalo[2];

	if(curarea->spacetype==SPACE_BUTS) {
		SpaceButs *sbuts= curarea->spacedata.first;
		align= sbuts->align;
	}

	uiGetMouse(block->win, mvalo);
	ofsx= block->panel->ofsx;
	ofsy= block->panel->ofsy;

	panel->flag |= PNL_SELECT;
	
	while(TRUE) {
	
		if( !(get_mbut() & L_MOUSE) ) break;	
	
		/* first clip for window, no dragging outside */
		getmouseco_areawin(mval);
		if( mval[0]>0 && mval[0]<curarea->winx && mval[1]>0 && mval[1]<curarea->winy) {
			uiGetMouse(mywinget(), mval);
			dx= (mval[0]-mvalo[0]) & ~(PNL_GRID-1);
			dy= (mval[1]-mvalo[1]) & ~(PNL_GRID-1);
		}
		
		if(dx!=dxo || dy!=dyo || first || align) {
			dxo= dx; dyo= dy;		
			first= 0;
			
			panel->ofsx = ofsx+dx;
			panel->ofsy = ofsy+dy;
			
			check_panel_overlap(curarea, panel);
			
			if(align) uiAlignPanelStep(curarea, 0.2);

			/* warn: this re-allocs blocks! */
			scrarea_do_windraw(curarea);
			ui_redraw_select_panel(curarea);
			screen_swapbuffers();
			
			/* so, we find the new block */
			block= curarea->uiblocks.first;
			while(block) {
				if(block->panel == panel) break;
				block= block->next;
			}
			// temporal debug
			if(block==NULL) {
				printf("block null while panel drag, should not happen\n");
			}
			
			/* restore */
			Mat4CpyMat4(UIwinmat, block->winmat);
			
			/* idle for align */
			if(dx==dxo && dy==dyo) PIL_sleep_ms(30);
		}
		/* idle for this poor code */
		else PIL_sleep_ms(30);
	}

	test_add_new_tabs(curarea);	 // also copies locations of tabs in dragged panel

	panel->flag &= ~PNL_SELECT;
	check_panel_overlap(curarea, NULL);	// clears
	
	if(align==0) addqueue(block->win, REDRAW, 1);
	else ui_animate_panels(curarea);
}


static void ui_panel_untab(uiBlock *block)
{
	Panel *panel= block->panel, *pa, *panew=NULL;
	short nr, mval[2], mvalo[2];
	
	/* while hold mouse, check for movement, then untab */
	
	uiGetMouse(block->win, mvalo);
	while(TRUE) {
	
		if( !(get_mbut() & L_MOUSE) ) break;	
		uiGetMouse(mywinget(), mval);
		
		if( abs(mval[0]-mvalo[0]) + abs(mval[1]-mvalo[1]) > 6 ) {
			/* find new parent panel */
			nr= 0;
			pa= curarea->panels.first;
			while(pa) {
				if(pa->paneltab==panel) {
					panew= pa;
					nr++;
				}
				pa= pa->next;
			}
			
			/* make old tabs point to panew */
			if(panew==NULL) printf("panel untab: shouldnt happen\n");
			panew->paneltab= NULL;
			
			pa= curarea->panels.first;
			while(pa) {
				if(pa->paneltab==panel) {
					pa->paneltab= panew;
				}
				pa= pa->next;
			}
			
			ui_drag_panel(block);
			break;
		
		}
		/* idle for this poor code */
		else PIL_sleep_ms(50);
		
	}

}

/* ------------ panel events ---------------- */


static void panel_clicked_tabs(uiBlock *block,  int mousex)
{
	Panel *pa, *tabsel=NULL, *panel= block->panel;
	int nr= 1, a, width;
	
	/* count */
	pa= curarea->panels.first;
	while(pa) {
		if(pa!=panel) {
			if(pa->paneltab==panel) nr++;
		}
		pa= pa->next;
	}

	if(nr==1) return;
	
	/* find clicked tab, mouse in panel coords */
	a= 0;
	width= (panel->sizex - 3- 2*PNL_ICON)/nr;
	pa= curarea->panels.first;
	while(pa) {
		if(pa==panel || pa->paneltab==panel) {
			if( (mousex > PNL_ICON+a*width) && (mousex < PNL_ICON+(a+1)*width) ) {
				tabsel= pa;
			}
			a++;
		}
		pa= pa->next;
	}

	if(tabsel) {
		
		if(tabsel == panel) {
			ui_panel_untab(block);
		}
		else {
			/* tabsel now becomes parent for all others */
			panel->paneltab= tabsel;
			tabsel->paneltab= NULL;
			
			pa= curarea->panels.first;
			while(pa) {
				if(pa->paneltab == panel) pa->paneltab = tabsel;
				pa= pa->next;
			}
			
			addqueue(curarea->win, REDRAW, 1);
		}
	}
	
}

static void stow_unstow(uiBlock *block)
{
	SpaceLink *sl= curarea->spacedata.first;
	Panel *pa;
	int ok=0, x, y, width;
	
	if(block->panel->flag & PNL_CLOSEDY) {  // flag has been set how it should become!
		
		width= (curarea->winx-320)/sl->blockscale;
		if(width<5) width= 5;
		
		/* find empty spot in bottom */
		for(y=4; y<100; y+= PNL_HEADER+4) {
			for(x=4; x<width; x+= 324) {
				ok= 1;
				/* check overlap with other panels */
				for(pa=curarea->panels.first; pa; pa=pa->next) {
					if(pa!=block->panel && pa->active && pa->paneltab==NULL) {
						if( abs(pa->ofsx-x)<320 ) {
							if( abs(pa->ofsy+pa->sizey-y)<PNL_HEADER+4) ok= 0;
						}
					}
				}
				
				if(ok) break;
			}
			if(ok) break;
		}
		if(ok==0) printf("still primitive code... fix!\n");
		
		block->panel->old_ofsx= block->panel->ofsx;
		block->panel->old_ofsy= block->panel->ofsy;
		
		block->panel->ofsx= x;
		block->panel->ofsy= y-block->panel->sizey;
		
	}
	else {
		block->panel->ofsx= block->panel->old_ofsx;
		block->panel->ofsy= block->panel->old_ofsy;
	
	}
	/* copy locations */
	for(pa= curarea->panels.first; pa; pa= pa->next) {
		if(pa->paneltab==block->panel) copy_panel_offset(pa, block->panel);
	}

}


/* this function is supposed to call general window drawing too */
/* also it supposes a block has panel, and isnt a menu */
void ui_do_panel(uiBlock *block, uiEvent *uevent)
{
	Panel *pa;
	int align= 0;
	
	if(curarea->spacetype==SPACE_BUTS) {
		SpaceButs *sbuts= curarea->spacedata.first;
		align= sbuts->align;
	}

	/* mouse coordinates in panel space! */

	if(uevent->event==LEFTMOUSE && block->panel->paneltab==NULL) {
		int button= 0;
		
		/* check open/collapsed button */
		if(block->panel->flag & PNL_CLOSEDX) {
			if(uevent->mval[1] >= block->maxy) button= 1;
		}
		else if(block->panel->control & UI_PNL_CLOSE) {
			if(uevent->mval[0] <= block->minx+PNL_ICON-2) button= 2;
			else if(uevent->mval[0] <= block->minx+2*PNL_ICON+2) button= 1;
		}
		else if(uevent->mval[0] <= block->minx+PNL_ICON+2) {
			button= 1;
		}
		
		if(button) {
		
			if(button==2) { // close
				rem_blockhandler(curarea, block->handler);
				addqueue(curarea->win, REDRAW, 1);
			}
			else {
		
				if(block->panel->flag & PNL_CLOSED) block->panel->flag &= ~PNL_CLOSED;
				else if(align==BUT_HORIZONTAL) block->panel->flag |= PNL_CLOSEDX;
				else block->panel->flag |= PNL_CLOSEDY;
				
				for(pa= curarea->panels.first; pa; pa= pa->next) {
					if(pa->paneltab==block->panel) {
						if(block->panel->flag & PNL_CLOSED) pa->flag |= PNL_CLOSED;
						else pa->flag &= ~PNL_CLOSED;
					}
				}
				// extra, for non-butspace: open/collapse at window header
				if(curarea->spacetype!=SPACE_BUTS)
					stow_unstow(block);

				
			}
			if(align==0) addqueue(block->win, REDRAW, 1);
			else ui_animate_panels(curarea);
			
		}
		else if(block->panel->flag & PNL_CLOSED) {
			ui_drag_panel(block);
		}
		/* check if clicked in tabbed area */
		else if(uevent->mval[0] < block->maxx-PNL_ICON-3 && panel_has_tabs(block->panel)) {
			panel_clicked_tabs(block, uevent->mval[0]);
		}
		else {
			ui_drag_panel(block);
		}
	}
}


