/*
 * File:        wx_canvs.cc
 * Purpose:     wxCanvas implementation (MSW)
 * Author:      Julian Smart
 * Created:     1993
 * Updated:	August 1994
 * RCS_ID:      $Id: wx_canvs.cc,v 1.1 1994/08/14 21:59:17 edz Exp $
 * Copyright:   (c) 1993, AIAI, University of Edinburgh
 */

/* static const char sccsid[] = "%W% %G%"; */

#if !defined(_MSC_VER) && !defined(wx_wxh)
#define wx_wxh
#endif

#include "wx_frame.h"
#include "wx_dc.h"
#include "wx_canvs.h"
#include "wx_panel.h"
#include "wx_stdev.h"
#include "wx_utils.h"

#include "wx.h"
#pragma hdrstop

#include "wx_privt.h"
#include <math.h>

extern char wxCanvasClassName[];

IMPLEMENT_DYNAMIC_CLASS(wxCanvas, wxWindow)

wxCanvas::wxCanvas (void)
{
  is_retained = 0;
  wxWinType = wxTYPE_XWND;
  is_retained = FALSE;
  clipping = FALSE;
  handle = NULL;
  window_parent = NULL;
  horiz_units = 0;
  vert_units = 0;
  wx_dc = NULL;
}

wxCanvas::wxCanvas (wxWindow *parent, int x, int y, int width, int height, long style,
	  char *name):
wxbCanvas (parent, x, y, width, height, style, name)
{
  Create (parent, x, y, width, height, style, name);
}

Bool wxCanvas::
Create (wxWindow * parent, int x, int y, int width, int height, long style,
	char *name)
{
  SetName(name);

  is_retained = ((style & wxRETAINED) == wxRETAINED);

  wxWinType = wxTYPE_XWND;
  windowStyle = style;
  is_retained = FALSE;
  clipping = FALSE;
  wxWnd *cparent = NULL;
  if (parent)
    cparent = (wxWnd *) parent->handle;

  if (wxSubType(parent->__type, wxTYPE_PANEL))
    ((wxPanel *)parent)->GetValidPosition(&x, &y);

  DWORD msflags = 0;
  if (style & wxBORDER)
    msflags |= WS_BORDER;
  msflags |= WS_CHILD | WS_VISIBLE;

  wxCanvasWnd *wnd = new wxCanvasWnd (cparent, this, x, y, width, height, msflags);
  wnd->SetBackgroundBrush(GetStockObject(WHITE_BRUSH), FALSE);
  wnd->background_colour = RGB(255, 255, 255);
#if DEBUG
  wxDebugMsg ("wxCanvas::Create: stock white background_brush is %X\n", wnd->background_brush);
#endif
  handle = (char *) wnd;

  if (parent)
    parent->AddChild (this);
  window_parent = parent;

  horiz_units = 0;
  vert_units = 0;

  wx_dc = new wxCanvasDC (this);

  if (wxSubType(parent->__type, wxTYPE_PANEL))
    ((wxPanel *)parent)->AdvanceCursor(this);

  return TRUE;
}

wxCanvas::~wxCanvas (void)
{
#if DEBUG
  wxDebugMsg("About to select old objects for canvas dc %X\n", wx_dc);
#endif
  // NB THIS CODE RELIES on destructor ordering:
  // this must be called BEFORE the window is destroyed.
  // This may not be true for some compilers (Borland?)
  // in which case we need to do something messy in ~wxWindow
  // to call this code BEFORE we destroy the window (or how
  // do we get hold of the DC?)
    
  if (wx_dc)
  {
    wxWnd *wnd = (wxWnd *)handle;
    HDC dc = ::GetDC(wnd->handle);
    wx_dc->SelectOldObjects (dc);
    ReleaseDC(wnd->handle, dc);
    delete wx_dc;
  }
}

void wxCanvas::SetColourMap (wxColourMap * cmap)
{
  if (wx_dc)
    wx_dc->SetColourMap(cmap);
}

void wxCanvas::SetSize (int x, int y, int w, int h, int sizeFlags)
{
  int currentX, currentY;
  GetPosition (&currentX, &currentY);
  if (x == -1)
    x = currentX;
  if (y == -1)
    y = currentY;

  int ww, hh;
  GetSize (&ww, &hh);
  if (w == -1)
    w = ww;
  if (h == -1)
    h = hh;

  wxWnd *wnd = (wxWnd *) handle;
  if (wnd)
    {
      MoveWindow (wnd->handle, x, y, w, h, TRUE);
      GetEventHandler()->OnSize (w, h);
    }
}

/*
 * horizontal/vertical: number of pixels per unit (e.g. pixels per text line)
 * x/y_length:        : no. units per scrollbar
 * x/y_page:          : no. units per page scrolled
 */
void wxCanvas::
SetScrollbars (int horizontal, int vertical,
	       int x_length, int y_length,
	       int x_page, int y_page,
	       int x_pos, int y_pos, Bool setVirtualSize)
{
  horiz_units = horizontal;
  vert_units = vertical;

  wxWnd *wnd = (wxWnd *) handle;
  if (wnd)
    {
      wnd->xscroll_pixels_per_line = horizontal;
      wnd->yscroll_pixels_per_line = vertical;
      wnd->xscroll_lines = x_length;
      wnd->yscroll_lines = y_length;
      wnd->xscroll_lines_per_page = x_page;
      wnd->yscroll_lines_per_page = y_page;
      wnd->calcScrolledOffset = setVirtualSize;

      int w, h;
      RECT rect;
      GetWindowRect (wnd->handle, &rect);
      w = rect.right - rect.left;
      h = rect.bottom - rect.top;

      // Recalculate scroll bar range and position
      if (wnd->xscroll_lines > 0)
	{
	  int nMaxWidth = wnd->xscroll_lines * wnd->xscroll_pixels_per_line;
	  int nHscrollMax = max (0, (int) (2 + (nMaxWidth - w) / wnd->xscroll_pixels_per_line));
	  wnd->xscroll_position = x_pos;
	  wnd->xscroll_position = min (nHscrollMax, wnd->xscroll_position);

	  ::SetScrollRange (wnd->handle, SB_HORZ, 0, nHscrollMax, FALSE);
	  ::SetScrollPos (wnd->handle, SB_HORZ, wnd->xscroll_position, TRUE);
	}
      if (wnd->yscroll_lines > 0)
	{
	  int nMaxHeight = wnd->yscroll_lines * wnd->yscroll_pixels_per_line;
	  int nVscrollMax = max (0, (int) (2 + (nMaxHeight - h) / wnd->yscroll_pixels_per_line));
	  wnd->yscroll_position = y_pos;
	  wnd->yscroll_position = min (nVscrollMax, wnd->yscroll_position);

	  ::SetScrollRange (wnd->handle, SB_VERT, 0, nVscrollMax, FALSE);
	  ::SetScrollPos (wnd->handle, SB_VERT, wnd->yscroll_position, TRUE);
	}
      InvalidateRect (wnd->handle, NULL, TRUE);
      UpdateWindow (wnd->handle);
    }
}

void wxCanvas::GetScrollUnitsPerPage (int *x_page, int *y_page)
{
  wxWnd *wnd = (wxWnd *) handle;
  if (wnd)
    {
      *x_page = wnd->xscroll_lines_per_page;
      *y_page = wnd->yscroll_lines_per_page;
    }
}

/*
 * Scroll to given position (scroll position, not pixel position)
 */
void wxCanvas::Scroll (int x_pos, int y_pos)
{
  int old_x, old_y;
  ViewStart (&old_x, &old_y);
  if (((x_pos == -1) || (x_pos == old_x)) && ((y_pos == -1) || (y_pos == old_y)))
    return;

  wxWnd *wnd = (wxWnd *) handle;

  if (x_pos > -1)
    {
      wnd->xscroll_position = x_pos;
      ::SetScrollPos (wnd->handle, SB_HORZ, x_pos, TRUE);
    }
  if (y_pos > -1)
    {
      wnd->yscroll_position = y_pos;
      ::SetScrollPos (wnd->handle, SB_VERT, y_pos, TRUE);
    }
  InvalidateRect (wnd->handle, NULL, TRUE);
  UpdateWindow (wnd->handle);
}

void wxCanvas::EnableScrolling (Bool x_scroll, Bool y_scroll)
{
  wxWnd *wnd = (wxWnd *) handle;
  wnd->x_scrolling_enabled = x_scroll;
  wnd->y_scrolling_enabled = y_scroll;
}

void wxCanvas::GetVirtualSize (int *x, int *y)
{
  wxWnd *wnd = (wxWnd *) handle;
  if (wnd)
    {
      *x = wnd->xscroll_pixels_per_line * wnd->xscroll_lines;
      *y = wnd->yscroll_pixels_per_line * wnd->yscroll_lines;
    }
}

void wxCanvas::WarpPointer (int x_pos, int y_pos)
{
  // Move the pointer to (x_pos,y_pos) coordinates. They are expressed in
  // pixel coordinates, relatives to the canvas -- So, we first need to
  // substract origin of the window, then convert to screen position

  wxWnd *wnd = (wxWnd *) handle;

  if (wnd)
    {
      x_pos -= wnd->xscroll_position * wnd->xscroll_pixels_per_line;
      y_pos -= wnd->yscroll_position * wnd->yscroll_pixels_per_line;

      RECT rect;
      GetWindowRect (wnd->handle, &rect);

      x_pos += rect.left;
      y_pos += rect.top;

      SetCursorPos (x_pos, y_pos);
    }
}

// Where the current view starts from
void wxCanvas::ViewStart (int *x, int *y)
{
  wxWnd *wnd = (wxWnd *) handle;
  *x = wnd->xscroll_position;
  *y = wnd->yscroll_position;
}

void wxWnd::DeviceToLogical (float *x, float *y)
{
  if (is_canvas)
    {
      wxCanvas *canvas = (wxCanvas *) wx_window;
      if (canvas->wx_dc)
      {
        *x = canvas->wx_dc->DeviceToLogicalX ((int) *x);
        *y = canvas->wx_dc->DeviceToLogicalY ((int) *y);
      }
    }
}

wxCanvasWnd::wxCanvasWnd (wxWnd * parent, wxWindow * wx_win,
	     int x, int y, int width, int height, DWORD style):
wxSubWnd (parent, wxCanvasClassName, wx_win, x, y, width, height, style)
{
  is_canvas = TRUE;
//  ShowScrollBar(handle, SB_BOTH, TRUE);
}


BOOL wxCanvasWnd::OnEraseBkgnd (HDC pDC)
{
  if (background_brush)
    {
      RECT rect;
      GetClientRect (handle, &rect);
      int mode = SetMapMode (pDC, MM_TEXT);
      FillRect (pDC, &rect, background_brush);
      SetMapMode (pDC, mode);

      wxCanvas *canvas = (wxCanvas *) wx_window;
      SetViewportExtEx (pDC, VIEWPORT_EXTENT, VIEWPORT_EXTENT, NULL);
      SetWindowExtEx (pDC, canvas->wx_dc->window_ext_x, canvas->wx_dc->window_ext_y, NULL);

      return TRUE;
    }
  else
    return FALSE;
}

