/** \file
 * \brief iupmatrix control
 * draw functions
 *
 * See Copyright Notice in "iup.h"
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "iup.h"
#include "iupcbs.h"

#include <cd.h>
#include <cd_old.h>

#include "iup_object.h"
#include "iup_attrib.h"
#include "iup_str.h"
#include "iup_drv.h"
#include "iup_drvfont.h"
#include "iup_stdcontrols.h"
#include "iup_image.h"

#include "iup_controls.h"
#include "iup_cdutil.h"

#include "iupmat_def.h"
#include "iupmat_cd.h"
#include "iupmat_draw.h"
#include "iupmat_aux.h"
#include "iupmat_getset.h"
#include "iupmat_mark.h"


#define IMAT_FEEDBACK_SIZE 16

static unsigned char imatrix_toggleoff_alpha[IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE] =
{
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};

static unsigned char imatrix_toggleon_alpha[IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE] =
{
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 89, 252, 89, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 84, 239, 106, 239, 84, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 84, 239, 60, 0, 60, 239, 84, 0, 0, 0, 0, 0, 255,
  255, 0, 84, 239, 60, 0, 0, 0, 60, 239, 84, 0, 0, 0, 0, 255,
  255, 16, 227, 60, 0, 0, 0, 0, 0, 60, 239, 84, 0, 0, 0, 255,
  255, 0, 8, 0, 0, 0, 0, 0, 0, 0, 60, 239, 84, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 239, 84, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 227, 16, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255,
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255
};

static unsigned char imatrix_dropdown_alpha[IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE] =
{
  000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 000, 000, 000, 128, 128, 000, 000, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 000, 000, 128, 255, 255, 128, 000, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 000, 128, 255, 255, 255, 255, 128, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 128, 255, 255, 255, 255, 255, 255, 128, 000, 000, 000, 000,
  000, 000, 000, 128, 255, 255, 255, 255, 255, 255, 255, 255, 128, 000, 000, 000,
  000, 000, 128, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 128, 000, 000,
  000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
  000, 000, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 000, 000,
  000, 000, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 000, 000,
  000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000,
  000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000, 000
};

static unsigned char imatrix_sortup_alpha[IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE] =
{
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 0,
  0, 0, 32, 239, 60, 0, 0, 0, 0, 0, 0, 0, 60, 239, 32, 0,
  0, 0, 0, 84, 239, 60, 0, 0, 0, 0, 0, 60, 239, 84, 0, 0,
  0, 0, 0, 0, 84, 239, 60, 0, 0, 0, 60, 239, 84, 0, 0, 0,
  0, 0, 8, 0, 0, 84, 239, 60, 0, 60, 239, 84, 0, 0, 8, 0,
  0, 0, 32, 32, 0, 0, 84, 239, 106, 239, 84, 0, 0, 32, 32, 0,
  0, 0, 32, 239, 60, 0, 0, 89, 252, 89, 0, 0, 60, 239, 32, 0,
  0, 0, 0, 84, 239, 60, 0, 0, 31, 0, 0, 60, 239, 84, 0, 0,
  0, 0, 0, 0, 84, 239, 60, 0, 0, 0, 60, 239, 84, 0, 0, 0,
  0, 0, 0, 0, 0, 84, 239, 60, 0, 60, 239, 84, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 84, 239, 106, 239, 84, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 89, 252, 89, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

static unsigned char imatrix_sortdown_alpha[IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE] =
{
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 31, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 89, 252, 89, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 84, 239, 106, 239, 84, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 84, 239, 60, 0, 60, 239, 84, 0, 0, 0, 0,
  0, 0, 0, 0, 84, 239, 60, 0, 0, 0, 60, 239, 84, 0, 0, 0,
  0, 0, 0, 84, 239, 60, 0, 0, 31, 0, 0, 60, 239, 84, 0, 0,
  0, 0, 32, 239, 60, 0, 0, 89, 252, 89, 0, 0, 60, 239, 32, 0,
  0, 0, 32, 32, 0, 0, 84, 239, 106, 239, 84, 0, 0, 32, 32, 0,
  0, 0, 8, 0, 0, 84, 239, 60, 0, 60, 239, 84, 0, 0, 8, 0,
  0, 0, 0, 0, 84, 239, 60, 0, 0, 0, 60, 239, 84, 0, 0, 0,
  0, 0, 0, 84, 239, 60, 0, 0, 0, 0, 0, 60, 239, 84, 0, 0,
  0, 0, 32, 239, 60, 0, 0, 0, 0, 0, 0, 0, 60, 239, 32, 0,
  0, 0, 32, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 32, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};


/**************************************************************************/
/*  Private functions                                                     */
/**************************************************************************/

static void iMatrixDrawSetCellClipping(Ihandle* ih, int x1, int x2, int y1, int y2)
{
  int old_clip = cdCanvasClip(ih->data->cd_canvas, CD_QUERY);
  if (old_clip == CD_CLIPAREA)
  {
    cdCanvasGetClipArea(ih->data->cd_canvas, &(ih->data->clip_x1), &(ih->data->clip_x2), &(ih->data->clip_y1), &(ih->data->clip_y2));
    y1 = iupMATRIX_INVERTYAXIS(ih, y1);
    y2 = iupMATRIX_INVERTYAXIS(ih, y2);
    if (x1 > x2) { int tmp = x1; x1 = x2; x2 = tmp; }
    if (y1 > y2) { int tmp = y1; y1 = y2; y2 = tmp; }
    if (x1 < ih->data->clip_x1) x1 = ih->data->clip_x1;
    if (x2 > ih->data->clip_x2) x2 = ih->data->clip_x2;
    if (y1 < ih->data->clip_y1) y1 = ih->data->clip_y1;
    if (y2 > ih->data->clip_y2) y2 = ih->data->clip_y2;
    if (x1 > x2) x2 = x1;
    if (y1 > y2) y2 = y1;
    cdCanvasClipArea(ih->data->cd_canvas, x1, x2, y1, y2);
    cdCanvasClip(ih->data->cd_canvas, CD_CLIPAREA);
  }
}

static void iMatrixDrawResetCellClipping(Ihandle* ih)
{
  int old_clip = cdCanvasClip(ih->data->cd_canvas, CD_QUERY);
  if (old_clip == CD_CLIPAREA)
    cdCanvasClipArea(ih->data->cd_canvas, ih->data->clip_x1, ih->data->clip_x2, ih->data->clip_y1, ih->data->clip_y2);
}

static int iMatrixDrawCallDrawCB(Ihandle* ih, int lin, int col, int x1, int x2, int y1, int y2, IFniiiiiiC draw_cb)
{
  int ret;

  iMatrixDrawSetCellClipping(ih, x1, x2, y1, y2);

  ret = draw_cb(ih, lin, col, x1, x2, iupMATRIX_INVERTYAXIS(ih, y1), iupMATRIX_INVERTYAXIS(ih, y2), ih->data->cd_canvas);

  iMatrixDrawResetCellClipping(ih);

  if (ret == IUP_DEFAULT)
    return 0;

  return 1;
}

/* Change the CD foreground color, for the selected color to draw a cell with
   its FOREGROUND COLOR. This involves checking if there is a color attribute
   that cell.  If no, uses a color attribute for the line, else if no for the
   column,  else if no  for the entire matrix.   Finally,  if not find any of
   these, use the default color.
   -> lin, col - cell coordinates, in IUP format - i.e., l,l represents the left
   top cell of the matrix; lin and col values = 0 represents the
   title lines and columns.
   -> marked - indicate if a cell is marked. If yes, its color is attenuated.
   */
static unsigned long iMatrixDrawSetFgColor(Ihandle* ih, int lin, int col, int marked, int active)
{
  unsigned char r = 0, g = 0, b = 0;
  iupMatrixGetFgRGB(ih, lin, col, &r, &g, &b, marked, active);
  return cdCanvasForeground(ih->data->cd_canvas, cdEncodeColor(r, g, b));
}

/* Change the CD foreground color, for the selected color to draw a cell with
   its BACKGROUND COLOR. This involves checking if there is a color attribute
   that cell.  If no, uses a color attribute for the line, else if no for the
   column,  else if no  for the entire matrix.   Finally,  if not find any of
   these, use the default color.
   -> lin, col - cell coordinates, in IUP format - i.e., l,l represents the left
   top cell of the matrix; lin and col values = 0 represents the
   title lines and columns.
   -> marked - indicate if a cell is marked. If yes, its color is attenuated.
   */
static unsigned long iMatrixDrawSetBgColor(Ihandle* ih, int lin, int col, int marked, int active)
{
  unsigned char r = 255, g = 255, b = 255;
  iupMatrixGetBgRGB(ih, lin, col, &r, &g, &b, marked, active);
  return cdCanvasForeground(ih->data->cd_canvas, cdEncodeColor(r, g, b));
}

static unsigned long iMatrixDrawSetTypeColor(Ihandle* ih, const char* color, int marked, int active)
{
  unsigned char r = 0, g = 0, b = 0;
  iupMatrixGetTypeRGB(ih, color, &r, &g, &b, marked, active);
  return cdCanvasForeground(ih->data->cd_canvas, cdEncodeColor(r, g, b));
}

static int iMatrixDrawFrameVertLineTitleHighlight(Ihandle* ih, int lin, int col, int x, int y1, int y2, long framecolor)
{
  if (ih->data->flat)
    return 1;

  if (col > 0)
  {
    int transp = iupMatrixGetFrameVertColor(ih, lin, col - 1, &framecolor, 0); /* framecolor is ignored here */
    if (transp)
      return 1;
  }

  cdCanvasForeground(ih->data->cd_canvas, CD_WHITE);
  iupMATRIX_LINE(ih, x, y1, x, y2);

  return 0;
}

static int iMatrixDrawFrameHorizLineTitleHighlight(Ihandle* ih, int lin, int col, int x1, int x2, int y, long framecolor)
{
  if (ih->data->flat)
    return 1;

  if (lin > 0)
  {
    int transp = iupMatrixGetFrameHorizColor(ih, lin - 1, col, &framecolor, 0); /* framecolor is ignored here */
    if (transp)
      return 1;
  }

  cdCanvasForeground(ih->data->cd_canvas, CD_WHITE);
  iupMATRIX_LINE(ih, x1, y, x2, y);

  return 0;
}

static int iMatrixDrawFrameHorizLineCell(Ihandle* ih, int lin, int col, int x1, int x2, int y, long framecolor)
{
  int transp = iupMatrixGetFrameHorizColor(ih, lin, col, &framecolor, 0);
  if (transp)
    return 1;

  cdCanvasForeground(ih->data->cd_canvas, framecolor);
  iupMATRIX_LINE(ih, x1, y, x2, y);   /* horizontal line */
  return 0;
}

static int iMatrixDrawFrameHorizLineTitle(Ihandle* ih, int col, int x1, int x2, int y, long framecolor)
{
  int transp = iupMatrixGetFrameHorizColor(ih, 0, col, &framecolor, 1);
  if (transp)
    return 1;

  cdCanvasForeground(ih->data->cd_canvas, framecolor);
  iupMATRIX_LINE(ih, x1, y, x2, y);   /* horizontal line */
  return 0;
}

static int iMatrixDrawFrameVertLineTitle(Ihandle* ih, int lin, int x, int y1, int y2, long framecolor)
{
  int transp = iupMatrixGetFrameVertColor(ih, lin, 0, &framecolor, 1);
  if (transp)
    return 1;

  cdCanvasForeground(ih->data->cd_canvas, framecolor);
  iupMATRIX_LINE(ih, x, y1, x, y2);    /* vertical line */
  return 0;
}

static int iMatrixDrawFrameVertLineCell(Ihandle* ih, int lin, int col, int x, int y1, int y2, long framecolor)
{
  int transp = iupMatrixGetFrameVertColor(ih, lin, col, &framecolor, 0);
  if (transp)
    return 1;

  cdCanvasForeground(ih->data->cd_canvas, framecolor);
  iupMATRIX_LINE(ih, x, y1, x, y2);    /* vertical line */
  return 0;
}

static void iMatrixDrawFrameRectTitle(Ihandle* ih, int lin, int col, int x1, int x2, int y1, int y2, long framecolor, int framehighlight)
{
  /* avoid drawing over the frame of the next cell */
  x2 -= IMAT_FRAME_W / 2;
  y2 -= IMAT_FRAME_H / 2;


  /********************* VERTICAL *************************/


  /* right vertical line */
  iMatrixDrawFrameVertLineCell(ih, lin, col, x2, y1, y2, framecolor);
  if (col == 0)
  {
    /* left vertical line */
    iMatrixDrawFrameVertLineTitle(ih, lin, x1, y1, y2, framecolor);
    x1++;
  }
  else if (col == 1 && ih->data->columns.dt[0].size == 0)
  {
    /* If does not have line titles then draw the left vertical line */
    iMatrixDrawFrameVertLineCell(ih, lin, col - 1, x1, y1, y2, framecolor);
    x1++;
  }

  /* Titles have a bright vertical line near the frame, at left */
  if (framehighlight)
    iMatrixDrawFrameVertLineTitleHighlight(ih, lin, col, x1, y1 + 1, y2 - 1, framecolor);


  /********************* HORIZONTAL *************************/


  /* bottom horizontal line */
  iMatrixDrawFrameHorizLineCell(ih, lin, col, x1, x2, y2, framecolor);
  if (lin == 0)
  {
    /* top horizontal line */
    iMatrixDrawFrameHorizLineTitle(ih, col, x1, x2, y1, framecolor);
    y1++;
  }
  else if (lin == 1 && ih->data->lines.dt[0].size == 0)
  {
    /* If does not have column titles then draw the top horizontal line */
    iMatrixDrawFrameHorizLineCell(ih, lin - 1, col, x1, x2 - 1, y1, framecolor);
    y1++;
  }

  /* Titles have a bright horizontal line near the frame, at top */
  if (framehighlight)
    iMatrixDrawFrameHorizLineTitleHighlight(ih, lin, col, x1, x2 - 1, y1, framecolor);
}

static void iMatrixDrawFrameRectCell(Ihandle* ih, int lin, int col, int x1, int x2, int y1, int y2, long framecolor)
{
  int transp;

  if (col == 1 && ih->data->columns.dt[0].size == 0)
  {
    /* If does not have line titles then draw the >> left line << of the cell frame */
    iMatrixDrawFrameVertLineCell(ih, lin, col - 1, x1, y1, y2 - 1, framecolor);
  }

  if (lin == 1 && ih->data->lines.dt[0].size == 0)
  {
    /* If does not have column titles then draw the >> top line << of the cell frame */
    iMatrixDrawFrameHorizLineCell(ih, lin - 1, col, x1, x2 - 1, y1, framecolor);
  }

  /* bottom line */
  transp = iMatrixDrawFrameHorizLineCell(ih, lin, col, x1, x2 - 1, y2 - 1, framecolor);

  /* right line */
  iMatrixDrawFrameVertLineCell(ih, lin, col, x2 - 1, y1, transp ? y2 - 1 : y2 - 2, framecolor);
}

static void iMatrixDrawFeedbackImage(Ihandle* ih, int x1, int x2, int y1, int y2, int lin, int col, int active, int marked, const char*name, unsigned char* alpha)
{
  int x, y;
  Ihandle* image = IupImageGetHandle(name);
  if (image)
  {
    long bgcolor;
    int image_width = IupGetInt(image, "WIDTH");
    int image_height = IupGetInt(image, "HEIGHT");
    unsigned char r = 255, g = 255, b = 255;
    iupMatrixGetBgRGB(ih, lin, col, &r, &g, &b, marked, active);
    bgcolor = cdEncodeColor(r, g, b);

    y = (y2 + y1 + image_height) / 2;
    x = (x2 + x1 - image_width) / 2;

    cdIupDrawImage(ih->data->cd_canvas, image, x, iupMATRIX_INVERTYAXIS(ih, y), 0, 0, !active, bgcolor);
  }
  else
  {
    static unsigned char red[IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE];
    static unsigned char green[IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE];
    static unsigned char blue[IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE];
    static unsigned char last_r = 0, last_g = 0, last_b = 0;
    static int init = 1;

    unsigned char r = 0, g = 0, b = 0;
    iupMatrixGetFgRGB(ih, lin, col, &r, &g, &b, marked, active);

    if (init || last_r != r || last_g != g || last_b != b)
    {
      int count = IMAT_FEEDBACK_SIZE * IMAT_FEEDBACK_SIZE;

      memset(red, r, count);
      memset(green, g, count);
      memset(blue, b, count);

      last_r = r;
      last_g = g;
      last_b = b;
      init = 0;
    }

    y = (y2 + y1 + IMAT_FEEDBACK_SIZE) / 2;
    x = (x2 + x1 - IMAT_FEEDBACK_SIZE) / 2;

    cdCanvasPutImageRectRGBA(ih->data->cd_canvas, IMAT_FEEDBACK_SIZE, IMAT_FEEDBACK_SIZE, red, green, blue, alpha, x, iupMATRIX_INVERTYAXIS(ih, y), IMAT_FEEDBACK_SIZE, IMAT_FEEDBACK_SIZE, 0, 0, 0, 0);
  }
}

static int iMatrixDrawSortSign(Ihandle* ih, int x2, int y1, int y2, int col, int active)
{
  int x1;

  char* sort = iupAttribGetId(ih, "SORTSIGN", col);
  if (!sort || iupStrEqualNoCase(sort, "NO"))
    return 0;

  /* feedback area */
  iupMatrixDrawSetDropFeedbackArea(&x1, &y1, &x2, &y2);

  if (iupStrEqualNoCase(sort, "DOWN"))
    iMatrixDrawFeedbackImage(ih, x1, x2, y1, y2, 0, col, active, 0, iupAttribGet(ih, "SORTIMAGEDOWN"), imatrix_sortdown_alpha);
  else
    iMatrixDrawFeedbackImage(ih, x1, x2, y1, y2, 0, col, active, 0, iupAttribGet(ih, "SORTIMAGEUP"), imatrix_sortup_alpha);

  return 1;
}

static void iMatrixDrawDropdownButton(Ihandle* ih, int x2, int y1, int y2, int lin, int col, int marked, int active)
{
  int x1;

  /* feedback area */
  iupMatrixDrawSetDropFeedbackArea(&x1, &y1, &x2, &y2);

  iMatrixDrawFeedbackImage(ih, x1, x2, y1, y2, lin, col, active, marked, iupAttribGet(ih, "DROPIMAGE"), imatrix_dropdown_alpha);
}

static void iMatrixDrawToggle(Ihandle* ih, int x1, int x2, int y1, int y2, int lin, int col, int marked, int active, int toggle_centered)
{
  int togglevalue = 0;

  /* toggle area */
  iupMatrixDrawSetToggleFeedbackArea(toggle_centered, &x1, &y1, &x2, &y2);

  if (toggle_centered)
  {
    char* value = iupMatrixGetValueDisplay(ih, lin, col);
    togglevalue = iupStrBoolean(value);
  }
  else
    togglevalue = iupAttribGetIntId2(ih, "TOGGLEVALUE", lin, col);

  /* toggle check */
  if (togglevalue)
    iMatrixDrawFeedbackImage(ih, x1, x2, y1, y2, lin, col, active, marked, iupAttribGet(ih, "TOGGLEIMAGEON"), imatrix_toggleon_alpha);
  else
    iMatrixDrawFeedbackImage(ih, x1, x2, y1, y2, lin, col, active, marked, iupAttribGet(ih, "TOGGLEIMAGEOFF"), imatrix_toggleoff_alpha);
}

static void iMatrixDrawBackground(Ihandle* ih, int x1, int x2, int y1, int y2, int marked, int active, int lin, int col)
{
  /* avoid drawing over the frame of the next cell */
  x2 -= IMAT_FRAME_W / 2;
  y2 -= IMAT_FRAME_H / 2;

  iMatrixDrawSetBgColor(ih, lin, col, marked, active);
  iupMATRIX_BOX(ih, x1, x2, y1, y2);
}

static void iMatrixDrawText(Ihandle* ih, int x1, int x2, int y1, int y2, int col_alignment, int lin_alignment, int marked, int active, int lin, int col, const char* text)
{
  int text_alignment;
  int charheight, x, y, hidden_text_marks = 0;

  iupdrvFontGetCharSize(ih, NULL, &charheight);

  if (lin == 0 || ih->data->hidden_text_marks)
  {
    int text_w;
    iupdrvFontGetMultiLineStringSize(ih, text, &text_w, NULL);
    if (text_w > x2 - x1 + 1 - IMAT_PADDING_W - IMAT_FRAME_W)
    {
      if (lin == 0)
        col_alignment = IMAT_ALIGN_START;

      if (ih->data->hidden_text_marks)
        hidden_text_marks = 1;
    }
  }

  /* Set the color used to draw the text */
  iMatrixDrawSetFgColor(ih, lin, col, marked, active);

  IupCdSetFont(ih, ih->data->cd_canvas, iupMatrixGetFont(ih, lin, col));

  /* Set the clip area to the cell region informed, the text maybe greater than the cell */
  if (hidden_text_marks)
  {
    int crop = iupdrvFontGetStringWidth(ih, "...") + 2;
    iMatrixDrawSetCellClipping(ih, x1, x2 - crop, y1, y2);
  }
  else
  {
    iMatrixDrawSetCellClipping(ih, x1, x2, y1, y2);
  }

  /* Create an space between text and cell frame */
  x1 += IMAT_PADDING_W / 2;       x2 -= IMAT_PADDING_W / 2;
  y1 += IMAT_PADDING_H / 2;       y2 -= IMAT_PADDING_H / 2;

  iupMatrixGetCellAlign(ih, lin, col, &col_alignment, &lin_alignment);

  if (lin_alignment == IMAT_ALIGN_CENTER)
  {
    y = iupROUND((y1 + y2) / 2.0);

    if (col_alignment == IMAT_ALIGN_CENTER)
    {
      x = iupROUND((x1 + x2) / 2.0);
      text_alignment = CD_CENTER;
    }
    else if (col_alignment == IMAT_ALIGN_START)
    {
      x = x1;
      text_alignment = CD_WEST;
    }
    else  /* RIGHT */
    {
      x = x2;
      text_alignment = CD_EAST;
    }
  }
  else if (lin_alignment == IMAT_ALIGN_START)
  {
    y = y1;

    if (col_alignment == IMAT_ALIGN_CENTER)
    {
      x = iupROUND((x1 + x2) / 2.0);
      text_alignment = CD_NORTH;
    }
    else if (col_alignment == IMAT_ALIGN_START)
    {
      x = x1;
      text_alignment = CD_NORTH_WEST;
    }
    else  /* RIGHT */
    {
      x = x2;
      text_alignment = CD_NORTH_EAST;
    }
  }
  else /* lin_alignment == IMAT_ALIGN_END */
  {
    y = y2;

    if (col_alignment == IMAT_ALIGN_CENTER)
    {
      x = iupROUND((x1 + x2) / 2.0);
      text_alignment = CD_SOUTH;
    }
    else if (col_alignment == IMAT_ALIGN_START)
    {
      x = x1;
      text_alignment = CD_SOUTH_WEST;
    }
    else  /* RIGHT */
    {
      x = x2;
      text_alignment = CD_SOUTH_EAST;
    }
  }

  cdCanvasTextAlignment(ih->data->cd_canvas, text_alignment);
  iupMATRIX_TEXT(ih, x, y, text);

  iMatrixDrawResetCellClipping(ih);

  if (hidden_text_marks)
  {
    cdCanvasTextAlignment(ih->data->cd_canvas, CD_EAST);
    y = (int)((y1 + y2) / 2.0 - 0.5);
    x = x2 + IMAT_PADDING_W / 2;
    iupMATRIX_TEXT(ih, x, y, "...");
  }
}

static void iMatrixDrawColor(Ihandle* ih, int x1, int x2, int y1, int y2, int marked, int active, const char* color, long framecolor)
{
  x1 += IMAT_PADDING_W / 2 + IMAT_FRAME_H / 2;
  x2 -= IMAT_PADDING_W / 2 + IMAT_FRAME_W / 2;
  y1 += IMAT_PADDING_H / 2 + IMAT_FRAME_H / 2;
  y2 -= IMAT_PADDING_H / 2 + IMAT_FRAME_H / 2;

  if (!iupAttribGetBoolean(ih, "TYPECOLORINACTIVE"))
    active = 1; /* draw as active */

  /* Fill the box with the color */
  iMatrixDrawSetTypeColor(ih, color, marked, active);
  iupMATRIX_BOX(ih, x1, x2, y1, y2);

  /* Draw the frame */
  cdCanvasForeground(ih->data->cd_canvas, framecolor);
  iupMATRIX_RECT(ih, x1, x2, y1, y2);
}

static void iMatrixDrawFill(Ihandle* ih, int x1, int x2, int y1, int y2, int marked, int active, int lin, int col, const char* value, long framecolor)
{
  int empty, fill = 0;

  iupStrToInt(value, &fill);
  if (fill < 0) fill = 0;
  if (fill > 100) fill = 100;

  /* Create an space between text and cell frame */
  x1 += IMAT_PADDING_W / 2;       x2 -= IMAT_PADDING_W / 2;
  y1 += IMAT_PADDING_H / 2;       y2 -= IMAT_PADDING_H / 2;

  empty = ((x2 - x1)*(100 - fill)) / 100;

  /* Fill the box with the color */
  iMatrixDrawSetFgColor(ih, lin, col, marked, active);
  iupMATRIX_BOX(ih, x1, x2 - empty, y1, y2);

  if (ih->data->show_fill_value)
  {
    int y = (int)((y1 + y2) / 2.0 - 0.5);
    int empty1 = ((x2 - x1)*fill) / 100;
    char text[50];
    sprintf(text, "%d%%", fill);
    IupCdSetFont(ih, ih->data->cd_canvas, iupMatrixGetFont(ih, lin, col));
    cdCanvasTextAlignment(ih->data->cd_canvas, CD_CENTER);

    iMatrixDrawSetCellClipping(ih, x1 + empty1, x2, y1, y2);
    iupMATRIX_TEXT(ih, (x1 + x2) / 2, y, text);
    iMatrixDrawResetCellClipping(ih);

    iMatrixDrawSetBgColor(ih, lin, col, marked, active);
    iMatrixDrawSetCellClipping(ih, x1, x2 - empty, y1, y2);
    iupMATRIX_TEXT(ih, (x1 + x2) / 2, y, text);
    iMatrixDrawResetCellClipping(ih);
  }


  /* Draw the frame */
  cdCanvasForeground(ih->data->cd_canvas, framecolor);
  iupMATRIX_RECT(ih, x1, x2, y1, y2);
}

static void iMatrixDrawImage(Ihandle* ih, int x1, int x2, int y1, int y2, int col_alignment, int lin_alignment, int marked, int active, int lin, int col, const char* name)
{
  Ihandle* image;

  iMatrixDrawSetCellClipping(ih, x1, x2, y1, y2);

  /* Create an space between image and cell frame */
  x1 += IMAT_PADDING_W / 2;       x2 -= IMAT_PADDING_W / 2;
  y1 += IMAT_PADDING_H / 2;       y2 -= IMAT_PADDING_H / 2;

  image = IupImageGetHandle(name);
  if (image)
  {
    long bgcolor;
    int x, y;
    int image_width = IupGetInt(image, "WIDTH");
    int image_height = IupGetInt(image, "HEIGHT");
    unsigned char r = 255, g = 255, b = 255;
    iupMatrixGetBgRGB(ih, lin, col, &r, &g, &b, marked, active);
    bgcolor = cdEncodeColor(r, g, b);

    if (lin_alignment == IMAT_ALIGN_CENTER)
      y = (y2 + y1 + image_height) / 2;
    else if (lin_alignment == IMAT_ALIGN_START)
      y = y1;
    else  /* BOTTOM */
      y = y2 + image_height;

    if (col_alignment == IMAT_ALIGN_CENTER)
      x = (x2 + x1 - image_width) / 2;
    else if (col_alignment == IMAT_ALIGN_START)
      x = x1;
    else  /* RIGHT */
      x = x2 - image_width;

    cdIupDrawImage(ih->data->cd_canvas, image, x, iupMATRIX_INVERTYAXIS(ih, y), 0, 0, !active, bgcolor);
  }

  iMatrixDrawResetCellClipping(ih);
}

/* Put the cell contents in the screen, using the specified color and Alignment.
   -> y1, y2 : vertical limits of the cell
   -> x1, x2 : horizontal limits of the complete cell
   -> col_alignment : Alignment type (horizontal) assigned to the text. The options are:
   [IMAT_ALIGN_CENTER,IMAT_ALIGN_START,IMAT_ALIGN_END]
   -> marked : mark state
   -> lin, col - cell coordinates */
static void iMatrixDrawCellValue(Ihandle* ih, int x1, int x2, int y1, int y2, int col_alignment, int lin_alignment, int marked, int active, int lin, int col, IFniiiiiiC draw_cb, long framecolor)
{
  char *value;

  /* avoid drawing over the frame of the next cell */
  x2 -= IMAT_FRAME_W / 2;
  y2 -= IMAT_FRAME_H / 2;

  /* avoid drawing over the frame of the cell */
  x2 -= IMAT_FRAME_W / 2;
  y2 -= IMAT_FRAME_H / 2;

  if (lin == 0 || col == 0)
  {
    /* avoid drawing over the frame of the cell */
    x1 += IMAT_FRAME_W / 2;
    y1 += IMAT_FRAME_H / 2;

    if (col == 0) x1 += IMAT_FRAME_W / 2;
    if (lin == 0) y1 += IMAT_FRAME_H / 2;
  }
  else if ((col == 1 && ih->data->columns.dt[0].size == 0) || (lin == 1 && ih->data->lines.dt[0].size == 0))
  {
    /* avoid drawing over the frame of the cell */
    x1 += IMAT_FRAME_W / 2;
    y1 += IMAT_FRAME_H / 2;
  }

  if (draw_cb && !iMatrixDrawCallDrawCB(ih, lin, col, x1, x2, y1, y2, draw_cb))
    return;

  value = iupMatrixGetValueDisplay(ih, lin, col);

  /* Put the text */
  if (value && *value)
  {
    int type = iupMatrixGetType(ih, lin, col);
    if (type == IMAT_TYPE_TEXT)
      iMatrixDrawText(ih, x1, x2, y1, y2, col_alignment, lin_alignment, marked, active, lin, col, value);
    else if (type == IMAT_TYPE_COLOR)
      iMatrixDrawColor(ih, x1, x2, y1, y2, marked, active, value, framecolor);
    else if (type == IMAT_TYPE_FILL)
      iMatrixDrawFill(ih, x1, x2, y1, y2, marked, active, lin, col, value, framecolor);
    else if (type == IMAT_TYPE_IMAGE)
      iMatrixDrawImage(ih, x1, x2, y1, y2, col_alignment, lin_alignment, marked, active, lin, col, value);
  }
}

static void iMatrixDrawTitleCorner(Ihandle* ih)
{
  if (ih->data->lines.dt[0].size && ih->data->columns.dt[0].size)
  {
    long framecolor = cdIupConvertColor(iupAttribGetStr(ih, "FRAMECOLOR"));
    int active = iupdrvIsActive(ih);
    IFniiiiiiC draw_cb = (IFniiiiiiC)IupGetCallback(ih, "DRAW_CB");
    int col_alignment = iupMatrixGetColAlignmentLin0(ih);
    int lin_alignment = iupMatrixGetLinAlignment(ih, 0);
    int framehighlight = iupAttribGetInt(ih, "FRAMETITLEHIGHLIGHT");

    iMatrixDrawBackground(ih, 0, ih->data->columns.dt[0].size, 0, ih->data->lines.dt[0].size, 0, active, 0, 0);

    iMatrixDrawFrameRectTitle(ih, 0, 0, 0, ih->data->columns.dt[0].size, 0, ih->data->lines.dt[0].size, framecolor, framehighlight);

    iMatrixDrawCellValue(ih, 0, ih->data->columns.dt[0].size, 0, ih->data->lines.dt[0].size, col_alignment, lin_alignment, 0, active, 0, 0, draw_cb, framecolor);
  }
}

static void iMatrixDrawFocus(Ihandle* ih)
{
  int x1, y1, x2, y2, dx, dy;

  if (iupAttribGetBoolean(ih, "HIDEFOCUS"))
    return;

  /* there are no cells that can get the focus */
  if (ih->data->columns.num <= 1 || ih->data->lines.num <= 1)
    return;

  if (!iupMatrixAuxIsCellVisible(ih, ih->data->lines.focus_cell, ih->data->columns.focus_cell))
    return;

  iupMatrixGetVisibleCellDim(ih, ih->data->lines.focus_cell, ih->data->columns.focus_cell, &x1, &y1, &dx, &dy);

  x2 = x1 + dx - 1;
  y2 = y1 + dy - 1;

  if (ih->data->noscroll_as_title && (ih->data->columns.focus_cell < ih->data->columns.num_noscroll || ih->data->lines.focus_cell < ih->data->lines.num_noscroll))
  {
    x1++;
    y1++;
  }
  else
  {
    if (ih->data->columns.focus_cell == 1 && ih->data->columns.dt[0].size == 0)
      x1++;
    if (ih->data->lines.focus_cell == 1 && ih->data->lines.dt[0].size == 0)
      y1++;
  }

  cdIupDrawFocusRect(ih->data->cd_canvas, x1, iupMATRIX_INVERTYAXIS(ih, y1), x2, iupMATRIX_INVERTYAXIS(ih, y2));
}

static void iMatrixDrawColRes(Ihandle* ih)
{
  cdCanvasForeground(ih->data->cd_canvas, ih->data->colres_color);
  cdCanvasLine(ih->data->cd_canvas, ih->data->colres_x, ih->data->colres_y1,
                                    ih->data->colres_x, ih->data->colres_y2);
}


/**************************************************************************/
/* Exported functions                                                     */
/**************************************************************************/

void iupMatrixDrawSetDropFeedbackArea(int *x1, int *y1, int *x2, int *y2)
{
  *x2 -= IMAT_PADDING_W / 2 + IMAT_FRAME_W / 2;
  *x1 = *x2 - IMAT_FEEDBACK_SIZE - IMAT_PADDING_W / 2;

  *y1 += IMAT_PADDING_H / 2 + IMAT_FRAME_H / 2;
  *y2 -= IMAT_PADDING_H / 2 + IMAT_FRAME_H / 2;
}

void iupMatrixDrawSetToggleFeedbackArea(int toggle_centered, int *x1, int *y1, int *x2, int *y2)
{
  if (toggle_centered)
  {
    *x1 = (*x2 + *x1) / 2 - IMAT_FEEDBACK_SIZE / 2;
    *x2 = *x1 + IMAT_FEEDBACK_SIZE;
  }
  else
  {
    *x2 -= IMAT_PADDING_W / 2 + IMAT_FRAME_W / 2;
    *x1 = *x2 - IMAT_FEEDBACK_SIZE - IMAT_PADDING_W / 2;
  }

  *y1 += IMAT_PADDING_H / 2 + IMAT_FRAME_H / 2;
  *y2 -= IMAT_PADDING_H / 2 + IMAT_FRAME_H / 2;
}

static int iMatrixAdjustVisibleColToMergedCells(Ihandle *ih, int *col1, int lin1, int lin2)
{
  int lin, adjusted = 0, merged;
  int startCol;
  int new_col1 = *col1;

  /* check merged cells at col1 */
  for (lin = lin1; lin <= lin2; lin++)
  {
    merged = iupMatrixGetMerged(ih, lin, *col1);
    if (merged)
    {
      iupMatrixGetMergedRect(ih, merged, NULL, NULL, &startCol, NULL);

      if (startCol < new_col1)
      {
        new_col1 = startCol;
        adjusted = 1;
      }
    }
  }

  if (adjusted)
    *col1 = new_col1;

  return adjusted;
}

static int iMatrixAdjustVisibleLinToMergedCells(Ihandle *ih, int *lin1, int col1, int col2)
{
  int col, adjusted = 0, merged;
  int startLin;
  int new_lin1 = *lin1;

  /* check merged cells at lin1 */
  for (col = col1; col <= col2; col++)
  {
    merged = iupMatrixGetMerged(ih, *lin1, col);
    if (merged)
    {
      iupMatrixGetMergedRect(ih, merged, &startLin, NULL, NULL, NULL);

      if (startLin < new_lin1)
      {
        new_lin1 = startLin;
        adjusted = 1;
      }
    }
  }

  if (adjusted)
    *lin1 = new_lin1;

  return adjusted;
}

/* Draw the line titles, visible, between lin and lastlin, include it.
   Line titles marked will be draw with the appropriate feedback.
   -> lin1 - First line to have its title drawn
   -> lin2 - Last line to have its title drawn */
static void iMatrixDrawTitleLines(Ihandle* ih, int lin1, int lin2)
{
  int x1, y1, x2, y2, first_lin, adjust_merged_lin = 0;
  int lin, col_alignment, active, framehighlight;
  long framecolor;
  IFniiiiiiC draw_cb;

  /* here col==0 always */

  if (!ih->data->columns.dt[0].size)
    return;

  if (ih->data->merge_info_count)
    adjust_merged_lin = iMatrixAdjustVisibleLinToMergedCells(ih, &lin1, 0, 0);

  if (ih->data->lines.num_noscroll > 1 && lin1 == 1 && lin2 == ih->data->lines.num_noscroll - 1)
  {
    first_lin = 0;
    y1 = 0;
  }
  else
  {
    if (lin1 > ih->data->lines.last ||
        lin2 < ih->data->lines.first)
        return;

    if (!adjust_merged_lin && lin1 < ih->data->lines.first)
      lin1 = ih->data->lines.first;
    if (lin2 > ih->data->lines.last)
      lin2 = ih->data->lines.last;

    first_lin = ih->data->lines.first;
    y1 = 0;
    for (lin = 0; lin < ih->data->lines.num_noscroll; lin++)
      y1 += ih->data->lines.dt[lin].size;
  }

  /* Start the position of the line title */
  x1 = 0;
  x2 = ih->data->columns.dt[0].size;

  iupMATRIX_CLIPAREA(ih, x1, x2, y1, iupMatrixGetHeight(ih) - 1);
  cdCanvasClip(ih->data->cd_canvas, CD_CLIPAREA);

  /* Find the initial position of the first line */
  if (first_lin == ih->data->lines.first)
    y1 -= ih->data->lines.first_offset;
  for (lin = first_lin; lin < lin1; lin++)
    y1 += ih->data->lines.dt[lin].size;
  if (adjust_merged_lin)
  {
    for (lin = first_lin; lin > lin1; lin--)
      y1 -= ih->data->lines.dt[lin].size;
  }

  framecolor = cdIupConvertColor(iupAttribGetStr(ih, "FRAMECOLOR"));
  framehighlight = iupAttribGetInt(ih, "FRAMETITLEHIGHLIGHT");
  active = iupdrvIsActive(ih);
  draw_cb = (IFniiiiiiC)IupGetCallback(ih, "DRAW_CB");

  col_alignment = iupMatrixGetColAlignment(ih, 0);

  /* Draw the titles */
  for (lin = lin1; lin <= lin2; lin++)
  {
    int merged;

    /* If it is a hidden line (size = 0), don't draw the title */
    if (ih->data->lines.dt[lin].size == 0)
      continue;

    merged = 0;
    if (ih->data->merge_info_count)
      merged = iupMatrixGetMerged(ih, lin, 0);

    y2 = y1 + ih->data->lines.dt[lin].size;

    if (merged)
    {
      int startLin, endLin, startCol, endCol;
      iupMatrixGetMergedRect(ih, merged, &startLin, &endLin, &startCol, &endCol);

      if (lin == startLin && 0 == startCol)  /* merged start */
      {
        int i;
        for (i = startLin + 1; i <= endLin; i++)
          y2 += ih->data->lines.dt[i].size;
      }
      else
        continue;  /* ignore internal merged */
    }

    /* If it doesn't have title, the loop just calculate the final position */
    if (ih->data->columns.dt[0].size)
    {
      int marked = iupMatrixLineIsMarked(ih, lin);
      int lin_alignment = iupMatrixGetLinAlignment(ih, lin);

      iMatrixDrawBackground(ih, x1, x2, y1, y2, marked, active, lin, 0);

      iMatrixDrawFrameRectTitle(ih, lin, 0, x1, x2, y1, y2, framecolor, framehighlight);

      iMatrixDrawCellValue(ih, x1, x2, y1, y2, col_alignment, lin_alignment, marked, active, lin, 0, draw_cb, framecolor);
    }

    y1 = y2;
  }

  cdCanvasClip(ih->data->cd_canvas, CD_CLIPOFF);
}

/* Draw the column titles, visible, between col and lastcol, include it.
   Column titles marked will be draw with the appropriate feedback.
   -> col1 - First column to have its title drawn
   -> col2 - Last column to have its title drawn */
static void iMatrixDrawTitleColumns(Ihandle* ih, int col1, int col2)
{
  int x1, y1, x2, y2, first_col, adjust_merged_col = 0;
  int col, active, col_alignment, lin_alignment, framehighlight;
  long framecolor;
  IFniiiiiiC draw_cb;

  /* here lin==0 always */

  if (!ih->data->lines.dt[0].size)
    return;

  if (ih->data->merge_info_count)
    adjust_merged_col = iMatrixAdjustVisibleColToMergedCells(ih, &col1, 0, 0);

  if (ih->data->columns.num_noscroll > 1 && col1 == 1 && col2 == ih->data->columns.num_noscroll - 1)
  {
    first_col = 0;
    x1 = 0;
  }
  else
  {
    if (col1 > ih->data->columns.last ||
        col2 < ih->data->columns.first)
        return;

    if (!adjust_merged_col && col1 < ih->data->columns.first)
      col1 = ih->data->columns.first;
    if (col2 > ih->data->columns.last)
      col2 = ih->data->columns.last;

    first_col = ih->data->columns.first;
    x1 = 0;
    for (col = 0; col < ih->data->columns.num_noscroll; col++)
      x1 += ih->data->columns.dt[col].size;
  }

  /* Start the position of the first column title */
  y1 = 0;
  y2 = ih->data->lines.dt[0].size;

  iupMATRIX_CLIPAREA(ih, x1, iupMatrixGetWidth(ih) - 1, y1, y2);
  cdCanvasClip(ih->data->cd_canvas, CD_CLIPAREA);

  /* Find the initial position of the first column */
  if (first_col == ih->data->columns.first)
    x1 -= ih->data->columns.first_offset;
  for (col = first_col; col < col1; col++)
    x1 += ih->data->columns.dt[col].size;
  if (adjust_merged_col)
  {
    for (col = first_col; col > col1; col--)
      x1 -= ih->data->columns.dt[col].size;
  }

  framecolor = cdIupConvertColor(iupAttribGetStr(ih, "FRAMECOLOR"));
  framehighlight = iupAttribGetInt(ih, "FRAMETITLEHIGHLIGHT");
  active = iupdrvIsActive(ih);
  draw_cb = (IFniiiiiiC)IupGetCallback(ih, "DRAW_CB");
  col_alignment = iupMatrixGetColAlignmentLin0(ih);
  lin_alignment = iupMatrixGetLinAlignment(ih, 0);

  /* Draw the titles */
  for (col = col1; col <= col2; col++)
  {
    int merged;

    /* If it is hidden column (size = 0), do not draw the title */
    if (ih->data->columns.dt[col].size == 0)
      continue;

    merged = 0;
    if (ih->data->merge_info_count)
      merged = iupMatrixGetMerged(ih, 0, col);

    x2 = x1 + ih->data->columns.dt[col].size;

    if (merged)
    {
      int startLin, endLin, startCol, endCol;
      iupMatrixGetMergedRect(ih, merged, &startLin, &endLin, &startCol, &endCol);

      if (0 == startLin && col == startCol) /* merged start */
      {
        int i;
        for (i = startCol + 1; i <= endCol; i++)
          x2 += ih->data->columns.dt[i].size;
      }
      else
        continue;  /* ignore internal merged */
    }

    /* If it doesn't have title, the loop just calculate the final position */
    if (ih->data->lines.dt[0].size)
    {
      int sort = 0;
      int marked = iupMatrixColumnIsMarked(ih, col);

      iMatrixDrawBackground(ih, x1, x2, y1, y2, marked, active, 0, col);

      iMatrixDrawFrameRectTitle(ih, 0, col, x1, x2, y1, y2, framecolor, framehighlight);

      if (iMatrixDrawSortSign(ih, x2, y1, y2, col, active))
        sort = IMAT_PADDING_W / 2 + IMAT_FEEDBACK_SIZE + IMAT_PADDING_W / 2; /* same space is used by the sort sign */

      iMatrixDrawCellValue(ih, x1, x2 - sort, y1, y2, col_alignment, lin_alignment, marked, active, 0, col, draw_cb, framecolor);
    }

    x1 = x2;
  }

  cdCanvasClip(ih->data->cd_canvas, CD_CLIPOFF);
}

/* Redraw a block of cells of the matrix. Handle marked cells, change
   automatically the background color of them.
   - lin1, col1 : cell coordinates that mark the left top corner of the area to be redrawn
   - lin2, col2 : cell coordinates that mark the right bottom corner of the area to be redrawn */
static void iMatrixDrawCells(Ihandle* ih, int lin1, int col1, int lin2, int col2)
{
  int x1, y1, x2, y2, old_x2, old_y1, old_y2, toggle_centered;
  int col_alignment, lin, col, active, first_col, first_lin;
  int i, adjust_merged_col = 0, adjust_merged_lin = 0;
  long framecolor, framehighlight;
  IFnii mark_cb;
  IFnii dropcheck_cb;
  IFniiiiiiC draw_cb;

  if (ih->data->merge_info_count)
  {
    adjust_merged_lin = iMatrixAdjustVisibleLinToMergedCells(ih, &lin1, col1, col2);
    adjust_merged_col = iMatrixAdjustVisibleColToMergedCells(ih, &col1, lin1, lin2);
  }

  x2 = iupMatrixGetWidth(ih) - 1;
  y2 = iupMatrixGetHeight(ih) - 1;

  old_x2 = x2;
  old_y1 = 0;
  old_y2 = y2;

  if (ih->data->lines.num <= 1 ||
      ih->data->columns.num <= 1)
      return;

  if (ih->data->columns.num_noscroll > 1 && col1 == 1 && col2 == ih->data->columns.num_noscroll - 1)
  {
    first_col = 0;
    x1 = 0;
  }
  else
  {
    if (col1 > ih->data->columns.last ||
        col2 < ih->data->columns.first)
      return;

    if (!adjust_merged_col && col1 < ih->data->columns.first)
      col1 = ih->data->columns.first;
    if (col2 > ih->data->columns.last)
      col2 = ih->data->columns.last;

    first_col = ih->data->columns.first;
    x1 = 0;
    for (col = 0; col< ih->data->columns.num_noscroll; col++)
      x1 += ih->data->columns.dt[col].size;
  }

  if (ih->data->lines.num_noscroll>1 && lin1 == 1 && lin2 == ih->data->lines.num_noscroll - 1)
  {
    first_lin = 0;
    y1 = 0;
  }
  else
  {
    if (lin1 > ih->data->lines.last ||
        lin2 < ih->data->lines.first)
      return;

    if (!adjust_merged_lin && lin1 < ih->data->lines.first)
      lin1 = ih->data->lines.first;
    if (lin2 > ih->data->lines.last)
      lin2 = ih->data->lines.last;

    first_lin = ih->data->lines.first;
    y1 = 0;
    for (lin = 0; lin < ih->data->lines.num_noscroll; lin++)
      y1 += ih->data->lines.dt[lin].size;
  }

  iupMATRIX_CLIPAREA(ih, x1, x2, y1, y2);
  cdCanvasClip(ih->data->cd_canvas, CD_CLIPOFF);  /* wait for background */

  /* Find the initial position of the first column */
  if (first_col == ih->data->columns.first)
    x1 -= ih->data->columns.first_offset;
  for (col = first_col; col < col1; col++)
    x1 += ih->data->columns.dt[col].size;
  if (adjust_merged_col)
  {
    for (col = first_col-1; col >= col1; col--)
      x1 -= ih->data->columns.dt[col].size;
  }

  /* Find the initial position of the first line */
  if (first_lin == ih->data->lines.first)
    y1 -= ih->data->lines.first_offset;
  for (lin = first_lin; lin < lin1; lin++)
    y1 += ih->data->lines.dt[lin].size;
  if (adjust_merged_lin)
  {
    for (lin = first_lin-1; lin >= lin1; lin--)
      y1 -= ih->data->lines.dt[lin].size;
  }

  /* Find the final position of the last column */
  x2 = x1;
  for (; col <= col2; col++)
    x2 += ih->data->columns.dt[col].size;

  /* Find the final position of the last line */
  y2 = y1;
  for (; lin <= lin2; lin++)
    y2 += ih->data->lines.dt[lin].size;

  if ((col2 == ih->data->columns.num - 1) && (old_x2 > x2))
  {
    long emptyarea_color = cdIupConvertColor(ih->data->bgcolor_parent);
    cdCanvasForeground(ih->data->cd_canvas, emptyarea_color);

    /* If it was drawn until the last column and remains space in the right of it,
       then delete this area with the the background color. */
    iupMATRIX_BOX(ih, x2, old_x2, old_y1, old_y2);
  }

  if ((lin2 == ih->data->lines.num - 1) && (old_y2 > y2))
  {
    long emptyarea_color = cdIupConvertColor(ih->data->bgcolor_parent);
    cdCanvasForeground(ih->data->cd_canvas, emptyarea_color);

    /* If it was drawn until the last line visible and remains space below it,
       then delete this area with the the background color. */
    iupMATRIX_BOX(ih, 0, old_x2, y2, old_y2);
  }

  /* after the background */
  cdCanvasClip(ih->data->cd_canvas, CD_CLIPAREA);

  /***** Draw the cell values and frame */
  old_y1 = y1;
  framecolor = cdIupConvertColor(iupAttribGetStr(ih, "FRAMECOLOR"));
  framehighlight = iupAttribGetInt(ih, "FRAMETITLEHIGHLIGHT");
  active = iupdrvIsActive(ih);

  mark_cb = (IFnii)IupGetCallback(ih, "MARK_CB");
  dropcheck_cb = (IFnii)IupGetCallback(ih, "DROPCHECK_CB");
  draw_cb = (IFniiiiiiC)IupGetCallback(ih, "DRAW_CB");
  toggle_centered = iupAttribGetBoolean(ih, "TOGGLECENTERED");

  for (col = col1; col <= col2; col++)  /* For all the columns in the region */
  {
    int last_x2, last_y2;

    if (ih->data->columns.dt[col].size == 0)
      continue;

    col_alignment = iupMatrixGetColAlignment(ih, col);

    x2 = x1 + ih->data->columns.dt[col].size;
    last_x2 = x2;

    for (lin = lin1; lin <= lin2; lin++)     /* For all lines in the region */
    {
      int drop = 0;
      int marked = 0;
      int lin_alignment;
      int merged;

      if (ih->data->lines.dt[lin].size == 0)
        continue;

      merged = 0;
      if (ih->data->merge_info_count)
        merged = iupMatrixGetMerged(ih, lin, col);

      y2 = y1 + ih->data->lines.dt[lin].size;
      last_y2 = y2;

      if (merged)
      {
        int startLin, endLin, startCol, endCol;
        iupMatrixGetMergedRect(ih, merged, &startLin, &endLin, &startCol, &endCol);

        if (lin == startLin && col == startCol)  /* merged start */
        {
          for (i = startLin + 1; i <= endLin; i++)
            y2 += ih->data->lines.dt[i].size;
          for (i = startCol + 1; i <= endCol; i++)
            x2 += ih->data->columns.dt[i].size;
        }
        else
        {
          x2 = last_x2;
          y1 = last_y2;
          continue;   /* ignore internal merged */
        }
      }

      lin_alignment = iupMatrixGetLinAlignment(ih, lin);

      /* If the cell is marked, then draw it with attenuation color */
      marked = iupMatrixGetMark(ih, lin, col, mark_cb);

      if (ih->data->noscroll_as_title && (lin < ih->data->lines.num_noscroll || col < ih->data->columns.num_noscroll))
      {
        iMatrixDrawBackground(ih, x1, x2, y1, y2, marked, active, lin, 0);
        iMatrixDrawFrameRectTitle(ih, lin, col, x1, x2, y1, y2, framecolor, framehighlight);
      }
      else
      {
        iMatrixDrawBackground(ih, x1, x2, y1, y2, marked, active, lin, col);

        iMatrixDrawFrameRectCell(ih, lin, col, x1, x2, y1, y2, framecolor);

        if (dropcheck_cb)
        {
          int ret = dropcheck_cb(ih, lin, col);
          if (ret == IUP_DEFAULT)
          {
            iMatrixDrawDropdownButton(ih, x2, y1, y2, lin, col, marked, active);

            drop = IMAT_PADDING_W / 2 + IMAT_FEEDBACK_SIZE + IMAT_PADDING_W / 2;
          }
          else if (ret == IUP_CONTINUE)
          {
            iMatrixDrawToggle(ih, x1, x2, y1, y2, lin, col, marked, active, toggle_centered);

            if (toggle_centered)
            {
              y1 = last_y2;
              continue; /* do not draw the cell contents */
            }

            drop = IMAT_PADDING_W / 2 + IMAT_FEEDBACK_SIZE + IMAT_PADDING_W / 2;
          }
        }
      }

      /* draw the cell contents */
      iMatrixDrawCellValue(ih, x1, x2 - drop, y1, y2, col_alignment, lin_alignment, marked, active, lin, col, draw_cb, framecolor);

      x2 = last_x2;
      y1 = last_y2;
    }

    x1 = last_x2;
    y1 = old_y1;  /* must reset also y */
  }

  cdCanvasClip(ih->data->cd_canvas, CD_CLIPOFF);
}

static void iMatrixDrawMatrix(Ihandle* ih)
{
  iupMatrixPrepareDrawData(ih);

  /* fill the background because there will be empty cells */
  if ((ih->data->lines.num == 1) || (ih->data->columns.num == 1))
  {
    cdCanvasBackground(ih->data->cd_canvas, cdIupConvertColor(ih->data->bgcolor_parent));
    cdCanvasClear(ih->data->cd_canvas);
  }

  /* Draw the corner between line and column titles, if necessary */
  iMatrixDrawTitleCorner(ih);

  /* If there are columns, then draw their titles */
  if (ih->data->columns.num_noscroll > 1)
    iMatrixDrawTitleColumns(ih, 1, ih->data->columns.num_noscroll - 1);
  iMatrixDrawTitleColumns(ih, ih->data->columns.first, ih->data->columns.last);

  /* If there are lines, then draw their titles */
  if (ih->data->lines.num_noscroll > 1)
    iMatrixDrawTitleLines(ih, 1, ih->data->lines.num_noscroll - 1);
  iMatrixDrawTitleLines(ih, ih->data->lines.first, ih->data->lines.last);

  /* If there are ordinary cells, then draw them */
  if (ih->data->columns.num_noscroll > 1 && ih->data->lines.num_noscroll > 1)
    iMatrixDrawCells(ih, 1, 1, ih->data->lines.num_noscroll - 1, ih->data->columns.num_noscroll - 1);
  if (ih->data->columns.num_noscroll > 1)
    iMatrixDrawCells(ih, ih->data->lines.first, 1, ih->data->lines.last, ih->data->columns.num_noscroll - 1);
  if (ih->data->lines.num_noscroll > 1)
    iMatrixDrawCells(ih, 1, ih->data->columns.first, ih->data->lines.num_noscroll - 1, ih->data->columns.last);
  iMatrixDrawCells(ih, ih->data->lines.first, ih->data->columns.first, ih->data->lines.last, ih->data->columns.last);

  if (iupAttribGetBoolean(ih, "FRAMEBORDER"))
  {
    long framecolor = cdIupConvertColor(iupAttribGetStr(ih, "FRAMECOLOR"));
    cdCanvasForeground(ih->data->cd_canvas, framecolor);

    /* if vertical scrollbar is visible */
    if (!iupAttribGetBoolean(ih, "YHIDDEN"))
    {
      int posy = IupGetInt(ih, "POSY");
      int dy = IupGetInt(ih, "DY");
      int ymax = IupGetInt(ih, "YMAX");
      int height = iupMatrixGetHeight(ih);

      int width = iupMatrixGetWidth(ih);
      if (width > ih->data->columns.total_size)
        width = ih->data->columns.total_size;

      /* if scrollbar at top, top line is not necessary */
      if (posy > 0)
        iupMATRIX_LINE(ih, 0, 0, width - 1, 0);  /* top horizontal line */

      /* if scrollbar at bottom, bottom line is not necessary */
      if (posy < ymax - dy)
        iupMATRIX_LINE(ih, 0, height - 1, width - 1, height - 1);  /* bottom horizontal line */
    }

    /* if horizontal scrollbar is visible */
    if (!iupAttribGetBoolean(ih, "XHIDDEN"))
    {
      int posx = IupGetInt(ih, "POSX");
      int dx = IupGetInt(ih, "DX");
      int xmax = IupGetInt(ih, "XMAX");
      int width = iupMatrixGetWidth(ih);

      int height = iupMatrixGetHeight(ih);
      if (height > ih->data->lines.total_size)
        height = ih->data->lines.total_size;

      /* if scrollbar at left, left line is not necessary */
      if (posx > 0)
        iupMATRIX_LINE(ih, 0, 0, 0, height - 1);  /* left vertical line */

      /* if scrollbar at right, right line is not necessary */
      if (posx < xmax - dx)
        iupMATRIX_LINE(ih, width - 1, 0, width - 1, height - 1);  /* right vertical line */
    }
  }
}

void iupMatrixDrawCells(Ihandle* ih, int lin1, int col1, int lin2, int col2)
{
#ifdef USE_OLD_DRAW
  iMatrixDrawCells(ih, lin1, col1, lin2, col2);
#else
  (void)ih;
  (void)lin1;
  (void)col1;
  (void)lin2;
  (void)col2;
#endif
}

void iupMatrixDrawTitleColumns(Ihandle* ih, int col1, int col2)
{
#ifdef USE_OLD_DRAW
  iMatrixDrawTitleColumns(ih, col1, col2);
#else
  (void)ih;
  (void)col1;
  (void)col2;
#endif
}

void iupMatrixDrawTitleLines(Ihandle* ih, int lin1, int lin2)
{
#ifdef USE_OLD_DRAW
  iMatrixDrawTitleLines(ih, lin1, lin2);
#else
  (void)ih;
  (void)lin1;
  (void)lin2;
#endif
}

void iupMatrixDraw(Ihandle* ih, int update)
{
#ifdef USE_OLD_DRAW
  cdCanvasActivate(ih->data->cd_canvas);

  if (ih->data->need_calcsize)
    iupMatrixAuxCalcSizes(ih);

  iMatrixDrawMatrix(ih);

  ih->data->need_redraw = 0;
#endif

  if (update)
    iupMatrixDrawUpdate(ih);
}

void iupMatrixDrawUpdate(Ihandle* ih)
{
#ifdef USE_OLD_DRAW
  cdCanvasFlush(ih->data->cd_canvas);

  if (ih->data->has_focus)
    iMatrixDrawFocus(ih);

  if (ih->data->colres_feedback)
    iMatrixDrawColRes(ih);

  if (!ih->data->edit_hide_onfocus && ih->data->editing)
    IupUpdate(ih->data->datah);
#else
  iupdrvRedrawNow(ih);
#endif
}

/*
static int iMatrixDrawHasFlatScrollBar(Ihandle* ih)
{
  char* value = iupAttribGetStr(ih, "FLATSCROLLBAR");
  if (value && !iupStrEqualNoCase(value, "NO"))
    return 1;
  else
    return 0;
}
*/

#ifndef USE_OLD_DRAW
void iupMatrixDrawCB(Ihandle* ih)
{
  /* called only from the ACTION callback */
  if (ih->data->need_calcsize)
  {
    int sb_resize = iupMatrixAuxCalcSizes(ih);  /* does not use cd_canvas, can be done before Activate, */
    if (sb_resize)                              /* but it can trigger a resize+redraw event */
    {
      /* if (!iMatrixDrawHasFlatScrollBar(ih)) */
      {
        /* TODO: this is not working in all situations, so it is disabled. */
        /* return; */
      }
    }
  }

  cdCanvasActivate(ih->data->cd_canvas);

  iMatrixDrawMatrix(ih);

  ih->data->need_redraw = 0;

  if (ih->data->has_focus)
    iMatrixDrawFocus(ih);

  if (ih->data->colres_feedback)
    iMatrixDrawColRes(ih);

  cdCanvasFlush(ih->data->cd_canvas);

  cdCanvasDeactivate(ih->data->cd_canvas);

  if (!ih->data->edit_hide_onfocus && ih->data->editing)
    IupUpdate(ih->data->datah);
}
#endif

int iupMatrixDrawSetRedrawAttrib(Ihandle* ih, const char* value)
{
#ifdef USE_OLD_DRAW
  int type;

  if (value == NULL)
    type = 0;
  else if (value[0] == 'L' || value[0] == 'l')
    type = IMAT_PROCESS_LIN;
  else if (value[0] == 'C' || value[0] == 'c')
    type = IMAT_PROCESS_COL;
  else
    type = 0;

  if (type)  /* lines or columns, including their titles */
  {
    int min = 0, max = 0;
    value++;

    /* compatibility code */
    if (strchr(value, ':') != NULL)
    {
      if (iupStrToIntInt(value, &min, &max, ':') != 2)
        max = min;
    }
    else
    {
      if (iupStrToIntInt(value, &min, &max, '-') != 2)
        max = min;
    }

    if (min > max)
      return 0;

    cdCanvasActivate(ih->data->cd_canvas);

    iupMatrixPrepareDrawData(ih);

    if (ih->data->need_calcsize)
      iupMatrixAuxCalcSizes(ih);

    /* ignore empty area, draw only cells */

    iMatrixDrawTitleCorner(ih);

    if (type == IMAT_PROCESS_LIN)
    {
      if (min == 0)
      {
        if (ih->data->columns.num_noscroll > 1)
          iMatrixDrawTitleColumns(ih, 1, ih->data->columns.num_noscroll - 1);
        iMatrixDrawTitleColumns(ih, ih->data->columns.first, ih->data->columns.last);
      }

      iMatrixDrawTitleLines(ih, min, max);
      if (ih->data->columns.num_noscroll > 1)
        iMatrixDrawCells(ih, min, 1, max, ih->data->columns.num_noscroll - 1);
      iMatrixDrawCells(ih, min, ih->data->columns.first, max, ih->data->columns.last);
    }
    else
    {
      if (min == 0)
      {
        if (ih->data->lines.num_noscroll > 1)
          iMatrixDrawTitleLines(ih, 1, ih->data->lines.num_noscroll - 1);
        iMatrixDrawTitleLines(ih, ih->data->lines.first, ih->data->lines.last);
      }

      iMatrixDrawTitleColumns(ih, min, max);
      if (ih->data->lines.num_noscroll > 1)
        iMatrixDrawCells(ih, 1, min, ih->data->lines.num_noscroll - 1, max);
      iMatrixDrawCells(ih, ih->data->lines.first, min, ih->data->lines.last, max);
    }
  }
  else
  {
    cdCanvasActivate(ih->data->cd_canvas);

    /* Force CalcSize */
    iupMatrixAuxCalcSizes(ih);

    iMatrixDrawMatrix(ih);
  }

  ih->data->need_redraw = 0;
  iupMatrixDrawUpdate(ih);
#else
  IupRedraw(ih, 0);  /* redraw now */
  (void)value;
#endif
  return 0;
}
