// C++ code Copyright (C) David R. Evans G4AMJ/NQ0I

#include <canvas31.h>
#include <DREstring.h>

static RECT global_rect;

RECT& rect(HWND _hwnd)
{ GetWindowRect(_hwnd, &global_rect);
  return global_rect;
}

// The following class is snarfed from the CD
class TWindowPrintout : public TPrintout
{
public:
  TWindowPrintout(const char* title, TWindow* window);
  void PrintPage(int page, TRect& rect, unsigned flags);

protected:
  TWindow* Window;
};

TWindowPrintout::TWindowPrintout(const char* title, TWindow* window) : TPrintout(title)
{ Window = window;
}

void TWindowPrintout::PrintPage(int, TRect& rect, unsigned)
{ Window->Paint(*DC, FALSE, rect);
}

// *******************************  popup  *****************************

// constructor
popup::popup(const int r, const int g, const int b, const int W,
				 const int H, PTWindow AParent, LPSTR ATitle,
             boolean is_visible,
				 PTModule AModule) : TWindow(AParent, ATitle),
				 _memdc(::CreateCompatibleDC(NULL), AutoDelete) /* ,
             TPrintout(ATitle) */
{ current_bitmap = 0;
  _inverted = false;
  _visible = is_visible;
  R = r;
  G = g;
  B = b;
  _w = W;
  _h = H;
  char temp[20];
  itoa(r, name, 10);
  itoa(g, temp, 10);
  strcat(name, temp);
  itoa(b, temp, 10);
  strcat(name, temp);
  strcpy(classname, "canvas");
  strcat(classname, name);

// make the printer available
//  heap_check(printer = new TPrinter);

// set the default styles
  if (_visible) then
    Attr.Style = WS_POPUP | WS_VISIBLE;
  else
    Attr.Style = WS_POPUP;
}

// destructor
popup::~popup(void)
{ //delete printer;
}

void popup::GetWindowClass(WNDCLASS& AWndClass)
{ TWindow::GetWindowClass(AWndClass);
// set the background colour
  AWndClass.hbrBackground = (HBRUSH)(CreateSolidBrush(RGB(R, G, B)));
}

// set the popup's font
void popup::font(TFont& the_font)
{ _memdc.SelectObject(the_font);
}

/* void popup::PrintPage(int, TRect& rect, unsigned)
{ TBitmap bitmap_copy(*current_bitmap);
  DC->SelectObject(bitmap_copy);
  DC->BitBlt(0, 0, width(), height(), _memdc, 0, 0, SRCCOPY);
}  */

// print the popup on the printer. This is rather more complicated than
// I would like because we must invert the picture before printing it.
/* void popup_printout::PrintPage(int, TRect& rect, unsigned)
{ // paint the printer's display context
  TBitmap bitmap_copy(*bmp);
  DC->SelectObject(bitmap_copy);
//  DC->BitBlt(0, 0, width(), height(), _memdc, 0, 0, SRCCOPY);

  window->Paint(*DC, FALSE, window->GetClientRect());
} */


/* popup& popup::print(void)
{  //TWindow* twp = (TWindow*)this;
//   popup_printout printout(twp, current_bitmap, Title);
   if (printer)
   { TWindowPrintout printout("Test", this);
   printer->Print(this, printout, TRUE);
//  printer->Print(twp, printout, FALSE);
  }
  return *this;
} */

void popup::copy_to_clipboard(void)
{ // In any sane object oriented windowing system, it would be the matter
// of at most two lines of code to copy the client area of a window to
// the clipboard. It may even be that this is possible in OWL, but there's
// no way of knowing, because there are simply no examples (that I can find)
// of how this is supposed to be done.

  TClipboard& clipboard = OpenClipboard();
  if (clipboard.EmptyClipboard()) then
  { // I don't pretend to really understand when things should be deleted.
// What I know is that if I simply copy *current_bitmap to the clipboard,
// then all kinds of nasty things happen when the bitmap gets changed.

// This fails if I simply use the copy constructor
//    TBitmap* bitmap_copy = new TBitmap(_memdc, width(), height());
//	 *bitmap_copy = *current_bitmap;
//    bitmap_copy->ToClipboard(clipboard);
      TBitmap bitmap_copy(*current_bitmap);
      TMemoryDC _tempdc;

	 _tempdc.SelectObject(bitmap_copy);
    _tempdc.BitBlt(0, 0, width(), height(), _memdc, 0, 0, SRCCOPY);
    bitmap_copy.ToClipboard(clipboard);
	 clipboard.CloseClipboard();
  }
  else
	 warning("Clipboard locked by another process");
}

void popup::init_bitmap(void)
{ current_bitmap = new TBitmap(_memdc, width(), height());
  _memdc.SelectObject(*current_bitmap);
}

popup& popup::update(void)
{ if (_visible) then
    _copy_to_screen();
  return *this;
}

popup& popup::update(const int X, const int Y, const int W, const int H)
{ if (_visible) then
    _copy_to_screen(X, Y, W, H);
  return *this;
}

popup& popup::resize(const int new_width, const int new_height)
{ /* MoveWindow(window_handle(), Attr.X, Attr.Y, w, h, TRUE);
  Attr.W = w;
  Attr.H = h;
  return *this; */

// is this a real resize?
  if ((new_width != width()) || (new_height != height())) then
  { // select out the old bitmap, generate a new one and insert it into the device context
/*    display_context temp_hdc;

	 bitmap* bmp;
	 heap_check(bmp = new bitmap(temp_hdc, new_width, new_height));
	 _memhdc << *bmp; */

	 TDC dc(0);
	 TBitmap bm(dc, new_width, new_height);

/*	 if ((!dc.IsOK) || (!bm.IsOK)) then
		fatal_error("creating compatible bitmap"); */

// for now, I can't see how to do this in OWL
	 HBITMAP old_bitmap = (HBITMAP)SelectObject(_memdc, bm);
	 if (!DeleteObject(old_bitmap)) then
		fatal_error("deleting bitmap");

	 _w = new_width;
	 _h = new_height;
// also need to fix up ObjectWindows variables  (these may need to be
// adjusted if there are styles defined)
	 Attr.W = _w;
	 Attr.H = _h;
// display contexts don't really have a dimension. We should probably remove all
// references to height and width of display contexts
//    _memhdc.width(_w);
//    _memhdc.height(_h);
	 clear();
	 update();
  }
  return *this;
}

popup& popup::move(const int x, const int y)
{ // the parent might have moved too
  RECT lprc = rect(hwnd(Parent));

  const int real_x = x + lprc.left,
		 real_y = y + lprc.top;

  ::MoveWindow(window_handle(), real_x, real_y, Attr.W, Attr.H, TRUE);
  Attr.X = real_x;
  Attr.Y = real_y;
  return *this;
}

void popup::Paint(TDC& dc, BOOL, TRect& invalid_rect)
{ const int x = invalid_rect.BottomLeft().x,
				y = invalid_rect.TopRight().y,
				w = invalid_rect.Width(),
				h = invalid_rect.Height();

  if (!(dc.BitBlt(x, y, w, h, _memdc, x, y, SRCCOPY))) then
	 fatal_error("Unable to execute popup::Paint()");;
}

// perform the copy from memory to screen
void popup::_copy_to_screen(void)
{ Invalidate(FALSE);
  SendMessage(WM_PAINT);
}

// perform a partial copy to screen
void popup::_copy_to_screen(const int X, const int Y, const int W,
				 const int H)
{ TRect invalid_rect(X, Y + H - 1, X + W - 1, Y);
  Invalidate(FALSE);
  SendMessage(WM_PAINT);

// this puts it in the wrong place
//  if (!(TWindowDC(HWindow).BitBlt(X, Y, W, H, _memdc, X, Y, SRCCOPY))) then
//	 fatal_error("Unable to copy memory display context to screen");
}

popup& popup::write(DREstring s, const int x, const int y)
{ if (!(_memdc.TextOut(x, y, s))) then
	 fatal_error("Error writing text to memory DC");
//  _copy_to_screen();
  return *this;
}

/* void popup::write_text(const char* t)
{ const int text_width = _memhdc.text_width(t);

//  display_context hdc(HWindow);
  _memhdc.fgc(RGB(0, 255, 0)).bgc(RGB(255, 0, 0)).write(t);
  _copy_to_screen();
} */

// we intercept calls to size the visible window; THIS IS CALLED EVEN FOR
// THE INITIAL SETUP OF THE WINDOW
void popup::EvSize(UINT, TSize&)
{ // According to the Windows SDK, Vol 2 (Functions), p. 81: "GDI output
// functions can be used with a memory device context only if a bitmap has
// been created and selected into that context". Note that this is
// inconveniently left unsaid in the OWL documentation.

  static boolean first_time = true;

  if (first_time) then
  { first_time = false;
    init_bitmap();
  }

// how large is the new client area?
  const TRect new_area = GetClientRect();

// is this a real resize?
  if ((new_area.Width() != width()) || (new_area.Height() != height())) then
  { // select out the old bitmap, generate a new one and insert it into the device context
/*    display_context temp_hdc;

	 bitmap* bmp;
	 heap_check(bmp = new bitmap(temp_hdc, new_width, new_height));
	 _memhdc << *bmp; */

//	 TMemoryDC temp_dc(::CreateCompatibleDC(NULL), AutoDelete);
//	 TBitmap bm(_memdc, new_area.Width(), new_area.Height());
	 TBitmap* temp_bitmap = current_bitmap;
	 current_bitmap = new TBitmap(_memdc,
											new_area.Width(), new_area.Height());

// I don't see how to do this in OWL, because there's no way to get the
// bitmap out.

/*	 HBITMAP old_bitmap = (HBITMAP)SelectObject(_memdc, bm);
	 if (!DeleteObject(old_bitmap)) then
		fatal_error("deleting bitmap"); */

// reselect the same bitmap using OWL
	 _memdc.SelectObject(*current_bitmap);

	 _w = new_area.Width();
	 _h = new_area.Height();
	 Attr.W = _w;
	 Attr.H = _h;
// display contexts don't really have a dimension. We should probably remove all
// references to height and width of display contexts
//    _memhdc.width(_w);
//    _memhdc.height(_h);
	 clear();
//	 update();
  }
}

// invert the popup
popup& popup::invert(void)
{ const DWORD rop = DSTINVERT /* (_inverted ? SRCCOPY : DSTINVERT) */ ;
  bitblt(0, 0, -1, -1, _memdc, 0, 0, rop);
  update();
  _inverted = !_inverted;
  return *this;
}

// define the response table (OWL Programmers' Guide, p. 132)
DEFINE_RESPONSE_TABLE1(popup, TWindow)
  EV_WM_SIZE,
END_RESPONSE_TABLE;

// ***************************  listbox  ****************************
listbox::listbox(PTWindow AParent, const int AnId, const int X,
	  const int Y, const int W, const int H, PTModule AModule )
	: TListBox(AParent, AnId, X, Y, W, H, AModule)
{ }

listbox::listbox(PTWindow AParent, const int AnId,
                 PTModule AModule )
	: TListBox(AParent, AnId, AModule)
{ }

listbox& listbox::resize(const int w, const int h)
{ ::MoveWindow(window_handle(), Attr.X, Attr.Y, w, h, TRUE);
  Attr.W = w;
  Attr.H = h;
  return *this;
}

listbox& listbox::move(const int x, const int y)
{ ::MoveWindow(window_handle(), x, y, Attr.W, Attr.H, TRUE);
  Attr.X = x;
  Attr.Y = y;
  return *this;
}

// ***************************  primary_button  ****************************
primary_button::primary_button(PTWindow AParent, int AnId, LPSTR AText, int W) :
	       TButton(AParent, AnId, AText,
		       (_next_x + W > 640) ? 0 : _next_x,
		       (_next_x + W > 640) ? _next_y + _label_height : _next_y,
				 W, _label_height, FALSE, NULL)
{ _next_x += W;
  if (_next_x > 640) then
  { _next_x = W;
    _next_y += _label_height;
  }
}

int primary_button::_next_x = 0;
int primary_button::_next_y = 0;
int primary_button::_label_height = 20;

// ***************************  secondary_button  ****************************
secondary_button::secondary_button(PTWindow AParent, int AnId,
                LPSTR AText, int W) :
                TButton(AParent, AnId, AText,
		       _next_x - W,
		       _next_y,
		       W, _label_height, FALSE, NULL)
{ _next_x -= W;
  Create();                  // attach an interface object to the window
  AParent->Show(SW_SHOWNORMAL);
}

secondary_button::~secondary_button(void)
{ _next_x += Attr.W;
  Destroy();
  Parent->Show(SW_SHOWNORMAL);
}

int secondary_button::_next_x = 638;
int secondary_button::_next_y = 20;
int secondary_button::_label_height = 20;

pmd::pmd(PTWindow AParent, const char* AName, const int x, const int y) :
  TDialog(AParent, "list_dialog")
{ IsModal = TRUE;
//  Title = AName;
  SetCaption(AName);

  _x = x;
  _y = y;
  heap_check(tlbp = new TListBox(this, ID_POPUP_MENU_LISTBOX));
  heap_check(_data = new TListBoxData);
  SetTransferBuffer(_data);
  tlbp->SetTransferBuffer(_data);
  tlbp->EnableTransfer();
  _selected_index = -1;
}

void pmd::SetupWindow(void)
{ TDialog::SetupWindow();

  char temp[100];
  const int n_items = tlbp->GetCount();
  int max_width = text_width(tlbp->HWindow, Title);

  for (int n = 0; n < n_items; n++)
  { tlbp->GetString(temp, n);
	 max_width = MAX(max_width, text_width(tlbp->HWindow, temp));
  }

  const int max_height = tlbp->GetItemHeight(0);

//  max_height += 2;
  max_width += 5;

  const TRect parent_rect(Parent->GetWindowRect());

//  SetWindowText(Title);
  MoveWindow(TRect(_x + parent_rect.left, _y + parent_rect.top,
						 _x + parent_rect.left + max_width + 10,
						 _y + parent_rect.top + max_height * n_items + 30), TRUE);
  tlbp->SetSelIndex(_selected_index);
  tlbp->MoveWindow(0, 0, max_width, max_height * n_items);
}

pmd& pmd::item(const char* cp)
{ _data->AddString((char*)cp);
  return *this;
}

int pmd::execute(void)
{ Execute();
  return _selected_index;
}

void pmd::HandleListBoxMessage(void)
{ _selected_index = tlbp->GetSelIndex();
  CloseWindow();
}

pmd& pmd::selected(const int n)
{ _selected_index = n;
  return *this;
}

DEFINE_RESPONSE_TABLE1(pmd, TDialog)
  EV_CHILD_NOTIFY(ID_POPUP_MENU_LISTBOX, LBN_SELCHANGE, HandleListBoxMessage),
END_RESPONSE_TABLE;




// **************************** popup_menu *****************************
popup_menu::popup_menu_dialog::popup_menu_dialog(PTWindow AParent,
						 LPSTR AName,
						 const int x,
						 const int y) :
  TDialog(AParent, "list_dialog")
{ IsModal = TRUE;
  title = AName;

  _x = x;
  _y = y;
  heap_check(lbp = new listbox(this, ID_POPUP_MENU_LISTBOX));
  lbp->EnableTransfer();
}

void popup_menu::popup_menu_dialog::SetupWindow(void)
{ char temp[100];
  const int n_items = lbp->GetCount();
  int max_width = 0, max_height = 0;
  TWindowDC dc(lbp->HWindow);

  for (int n = 0; n < n_items; n++)
  { lbp->GetString(temp, n);
	 int this_width = dc.GetTextExtent(temp, strlen(temp)).cx,
		  this_height = dc.GetTextExtent(temp, strlen(temp)).cy;
	 max_width = MAX(max_width, this_width);
	 max_height = MAX(max_height, this_height);
  }
  max_height += 2;
  max_width += 5;
  lbp->move(0, 0);
  lbp->resize(max_width, max_height * n_items);

  ::SetWindowText(HWindow, title);
  ::MoveWindow(HWindow, _x, _y, max_width + 10, (max_height * n_items) + 30, TRUE);
}

/* DEFINE_RESPONSE_TABLE1(popup_menu_dialog, TDialog)
  EV_WM_INITDIALOG,
END_RESPONSE_TABLE;
*/


//constructor
popup_menu::popup_menu(PTWindow AParent, LPSTR AName, const int ofsx,
							  const int ofsy)
{ app = AParent->GetApplication();
  heap_check(dp = new popup_menu_dialog(AParent, AName,
						screen_position_left(AParent->HWindow) + ofsx,
		  screen_position_top(AParent->HWindow) + ofsy));
  heap_check(menu_list = new TListBoxData);
  dp->SetTransferBuffer(&menu_list);
}

// destructor
popup_menu::~popup_menu(void)
{ delete menu_list; }

int popup_menu::execute(void)
{ //dp->TransferBuffer = &menu_list;
  dp->lbp->SetTransferBuffer(&menu_list);
  dp->Execute();
  char temp[100];
  menu_list->GetSelString(temp, sizeof(temp));

// search for a match
  int found = false, index = 1;
  while ((!found) && (index <= items.length()))
    found = (!strcmp(temp, (const char*)(items[index++])));
  if (!found) then
    fatal_error("popup_menu::execute(void)");
  return index - 1;
}

popup_menu& popup_menu::item(const char* cp)
{ items += (void*)cp;
  menu_list->AddString((char*)cp);
  return *this;
}

void popup_menu::popup_menu_dialog::HandleListBoxMessage(RTMessage)
{ /* need to rework for 2.00
  if (Msg.LP.Hi == LBN_SELCHANGE) then
  { CloseWindow(lbp->GetSelIndex());
  } */
}

popup_menu& popup_menu::selected(const int n)
{ if (( n < 1) || (n > items.length())) then
	 fatal_error((DREstring)"Invalid index (" + DREstring10(n) +
					 (DREstring)") to popup_menu::selected(const int)");

  const char* selected_DREstring = (const char*)items[n];
  menu_list->SelectString((char*)selected_DREstring);
  return *this;
}

// **************************** popup_slider *****************************
popup_slider::popup_slider_dialog::popup_slider_dialog
											  (PTWindow AParent,
						 LPSTR AName,
						 const int x,
						 const int y,
						 const int minval,
						 const int maxval,
						 const int curval,
						 char* unit) :
  TDialog(AParent, "slider_dialog")
{ IsModal = TRUE;
  title = AName;

  _x = x;
  _y = y;
  _minval = minval;
  _maxval = maxval;
  _curval = curval;
  _unit = unit;
  heap_check(sbp = new TScrollBar(this, ID_POPUP_SLIDER_SCROLLBAR));
  heap_check(ok = new TButton(this, ID_POPUP_SLIDER_BUTTON));
  heap_check(staticp = new TStatic(this, ID_POPUP_SLIDER_STATIC, 5));
  heap_check(staticminp = new TStatic(this, ID_POPUP_SLIDER_STATIC_MINVAL, 3));
  heap_check(staticmaxp = new TStatic(this, ID_POPUP_SLIDER_STATIC_MAXVAL, 4));
}

void popup_slider::popup_slider_dialog::WMInitDialog(UINT)
{ // change for 2.00 TDialog::WMInitDialog(Msg);
  sbp->SetRange(_minval, _maxval);
  sbp->SetPosition(_curval);
  char percent[5];
  sprintf(percent, "%d%s", _curval, _unit);
  staticp->SetText(percent);
  ::SetWindowText(HWindow, title);
  ::MoveWindow(HWindow, _x, _y, 300, 250, TRUE);
  sprintf(percent, "%d", _minval);
  staticminp->SetText(percent);
  sprintf(percent, "%d", _maxval);
  staticmaxp->SetText(percent);
}

void popup_slider::popup_slider_dialog::HandleOKMessage(UINT)
{ CloseWindow(sbp->GetPosition());
}

void popup_slider::popup_slider_dialog::HandleScrollBarMessage(UINT)
{ char percent[5];
  sprintf(percent, "%d%s", sbp->GetPosition(), _unit);
  staticp->SetText(percent);
}

DEFINE_RESPONSE_TABLE1(popup_slider::popup_slider_dialog, TDialog)
END_RESPONSE_TABLE;

// constructor
popup_slider::popup_slider(PTWindow AParent, LPSTR AName,
			   const int ofsx, const int ofsy, const int minval,
			   const int maxval, const int curval, char* unit)
{ app = AParent->GetApplication();
  heap_check(dp = new popup_slider_dialog(AParent, AName,
                  screen_position_left(AParent->HWindow) + ofsx,
		  screen_position_top(AParent->HWindow) + ofsy,
		  minval, maxval, curval, unit));
}

// destructor
popup_slider::~popup_slider(void)
{ }


int popup_slider::execute(void)
{ const int rv = dp->Execute();
  return rv;
}

edit_control::edit_control(PTWindow AParent, const char* AName,
									const int n_controls, const int len,
									const int x, const int y) :
  TDialog(AParent, "edit_dialog")
{ if (n_controls > MAX_EDIT_CONTROL_SIZE) then
    fatal_error("Attempt to construct edit_control with too many fields");

  IsModal = TRUE;
  SetCaption(AName);
  _n_controls = n_controls;
  _x = x;
  _y = y;

// build an OWL interface into each possible control
//  const int STATIC_TEXT_LENGTH = 32;

  heap_check(edp[0] = new TEdit(this, ID_DIALOGUE_EDIT_1, len));
  heap_check(stp[0] = new TStatic(this, ID_DIALOGUE_STATIC_1, len));
  heap_check(edp[1] = new TEdit(this, ID_DIALOGUE_EDIT_2, len));
  heap_check(stp[1] = new TStatic(this, ID_DIALOGUE_STATIC_2, len));
  heap_check(edp[2] = new TEdit(this, ID_DIALOGUE_EDIT_3, len));
  heap_check(stp[2] = new TStatic(this, ID_DIALOGUE_STATIC_3, len));
  heap_check(edp[3] = new TEdit(this, ID_DIALOGUE_EDIT_4, len));
  heap_check(stp[3] = new TStatic(this, ID_DIALOGUE_STATIC_4, len));

  SetTransferBuffer(&_edit_transfer_buffer);

  for (int n = 0; n < MAX_EDIT_CONTROL_SIZE; n++)
  { _label[n] = 0;
	 _value[n] = 0;
  }
}

edit_control::~edit_control(void)
{ for (int n = 0; n < MAX_EDIT_CONTROL_SIZE; n++)
  { destroy(edp[n]);
	 destroy(stp[n]);
	 destroy_array(_label[n]);
	 destroy_array(_value[n]);
  }
}

void edit_control::SetupWindow(void)
{ TDialog::SetupWindow();
  const TRect parent_rect(Parent->GetWindowRect());

// size the dialogue box
  uint max_width = 0;
  for (int n = 0; n < MAX_EDIT_CONTROL_SIZE; n++)
  { if (_label[n]) then
		max_width = MAX(max_width, (strlen(_label[n]) + strlen(_value[n])));
  }
  max_width *= 8;
  max_width += 120;
  MoveWindow(TRect(_x + parent_rect.left, _y + parent_rect.top,
						 _x + parent_rect.left + max_width,
						 _y + parent_rect.top + 75 + 25 * _n_controls), TRUE);

  for (n = 0; n < MAX_EDIT_CONTROL_SIZE; n++)
  { if (_label[n]) then
	 { stp[n]->Show(SW_NORMAL);
		edp[n]->Show(SW_NORMAL);
		stp[n]->MoveWindow(0, 25 + 25 * n, strlen(_label[n]) * 8, 25);
		edp[n]->MoveWindow(strlen(_label[n]) * 8, 25 + 25 * n,
                         strlen(_value[n]) * 8 + 120, 25);
		stp[n]->SetText(_label[n]);
		edp[n]->SetText(_value[n]);
	 }
	 else
	 { stp[n]->Show(SW_HIDE);
		edp[n]->Show(SW_HIDE);
	 }
  }
}

int edit_control::execute(void)
{ Execute();
  for (int n = 0; n < _n_controls; n++)
  { destroy_array(_value[n]);
	 _value[n] = new char [strlen(_edit_transfer_buffer.buffer[n]) + 1];
	 strcpy(_value[n], _edit_transfer_buffer.buffer[n]);
  }
  return 1;
}

// label one of the controls
edit_control& edit_control::label(const int control_nr, const char* cp)
{ const int index = control_nr - 1;

  destroy_array(_label[index]);
  _label[index] = new char [strlen(cp) + 1];
  strcpy(_label[index], cp);
  return *this;
}

// place a string into an editable control
edit_control& edit_control::value(const int control_nr, const char* cp)
{ const int index = control_nr - 1;

  destroy_array(_value[index]);
  _value[index] = new char [strlen(cp) + 1];
  strcpy(_value[index], cp);
  return *this;
}

// routines to take away some of the amazing drudgery of the Windows API

int screen_position_left(HWND hwnd)
{ RECT lprc;
  GetWindowRect(hwnd, &lprc);
  return lprc.left;
}

int screen_position_right(HWND hwnd)
{ RECT lprc;
  GetWindowRect(hwnd, &lprc);
  return lprc.right;
}

int screen_position_top(HWND hwnd)
{ RECT lprc;
  GetWindowRect(hwnd, &lprc);
  return lprc.top;
}

int screen_position_bottom(HWND hwnd)
{ RECT lprc;
  GetWindowRect(hwnd, &lprc);
  return lprc.bottom;
}
