Logo Search packages:      
Sourcecode: blender version File versions

interface_panel.c

/**
 * $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);
            }
      }
}



Generated by  Doxygen 1.6.0   Back to index