#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <ctype.h>
#include "header.h"
#include "dir.h"
#include "cvinfo.h"

extern HWND hwndFrame, hwndProject, hwndRegister, hwndClient, hwndError, hwndTab ;
extern HINSTANCE hInstance ;
extern int docking ;
extern CONTEXT StoppedRegs ;
extern PROCESS DebugProcess ;

HWND hwndWatch ;
static HWND hwndTree, hwndCtrl ;
static char szWatchClassName[] = "xccWatchClass" ;
static char szWatchTitle[] = "Watch Window" ;

static HBITMAP valueBitmap, itemBitmap ;

static LOGFONT fontdata = {
   14,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
   CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FF_DONTCARE,
   "Helvetica"
} ;
static WATCHINFO watchinfo_list[200] ;
static int watchinfo_count ;
WATCHINFO *MatchItem(VARINFO *var)
{
   int i ;
   for (i=0; i <watchinfo_count; i++)
      if (!strcmp(watchinfo_list[i].info->membername, var->membername))
         if (watchinfo_list[i].info->typetab == var->typetab)
            return &watchinfo_list[i] ;
   return 0 ;
}
void FreeTree(VARINFO *info)
{
   while (info) {
      FreeTree(info->subtype) ;
      if (info->hTreeItem)
         TreeView_DeleteItem(hwndTree,info->hTreeItem) ;
      info = info->link ;
   }
}
void RefreshAddresses(VARINFO *var, int address)
{
   while (var) {
      int val ;
      val = var->address = address + var->offset ;
      if (var->pointer) {
         ReadValue(var->address,&val,4) ;
         if (!val && var->subtype) {
            VARINFO *info = var->subtype ;
            var->subtype = 0;
            FreeTree(info) ;
            FreeVarInfo(info) ;
         }
      }
      RefreshAddresses(var->subtype,val) ;
      var = var->link ;
   }
}
void WatchValue(char *typetab, char *buf, VARINFO *info, int onevalue)
{
   int i ;
   if (info->outofscope)
      sprintf(buf,"out of scope") ;
   else if (info->structure) {
      sprintf(buf,"STRUCTURE: %p",info->address) ;
   } else if (info->unionx) {
      sprintf(buf,"UNION: %p",info->address) ;
   } else if (info->pointer) {
      int val ;
      if (ReadValue(info->address,&val,4)) {
         info->editable = TRUE ;
         if (onevalue)
            sprintf(buf,"%p", val) ;
         else
            sprintf(buf,"POINTER: %p", val) ;
      } else 
         sprintf(buf,"POINTER: <UNKNOWN>") ;
   } else if (info->enumx) {
      info->editable = TRUE ;
      HintEnum(typetab,info,buf,FALSE,onevalue) ;
   } else if (info->bitfield) {
      int signedtype ;
      int v= HintBf(info,&signedtype) ;
      if (onevalue) 
         if (signedtype)
            sprintf(buf,"0x%x",v) ;
         else
            sprintf(buf,"0x%x",v) ;
      else
         if (signedtype)
            sprintf(buf,"%d(0x%x)",v,v) ;
         else
            sprintf(buf,"%u(0x%x)",v,v) ;
      info->editable = TRUE ;
   } else if (info->array) {
      sprintf(buf,"ARRAY: %p",info->address) ;
   } else {
      int signedtype ;
      char buf1[10];
      int v;
      info->editable = TRUE ;
      switch(HintBasicType(info,&signedtype,buf1)) {
         case T_INT4:
            v = *(int *)buf1 ;
            if (onevalue)
               if (signedtype)
                  sprintf(buf,"0x%x",v) ;
               else
                  sprintf(buf,"0x%x",v) ;
            else
               if (signedtype)
                  sprintf(buf,"%d(0x%x)",v,v) ;
               else
                  sprintf(buf,"%u(0x%x)",v,v) ;
            break ;
         case T_BOOL08:
            if (buf1[0])
               sprintf(buf,"True") ;
            else
               sprintf(buf,"False") ;
            break ;
         case T_REAL32:
            sprintf(buf,"%f",(double)*(float *)buf1) ;
            break ;
         case T_REAL80:
            asm fld tbyte ptr [buf1] ;
            asm fstp qword ptr [buf1] ; 
         case T_REAL64:
            sprintf(buf,"%f",*(double *)buf1) ;
            break ;
         default:
            sprintf(buf,"unknown type") ;
            break ;
      }
   }
}
void RefreshData(char *typetab, VARINFO *var)
{
   while (var) {
      WatchValue(typetab,var->value,var,FALSE) ;
      if (!var->outofscope)
         RefreshData(typetab,var->subtype) ;
      var = var->link ;
   }
}
void RefreshItem(WATCHINFO *var, int address)
{
   RefreshAddresses(var->info,address) ;
   RefreshData(var->typetab, var->info) ;
   
}
HTREEITEM InsertItem(HTREEITEM hParent, HTREEITEM after, VARINFO *var)
{
	HTREEITEM rv ;
	TV_INSERTSTRUCT t;
	memset(&t,0,sizeof(t)) ;
	t.hParent =hParent ;
	t.hInsertAfter = after ;
   t.item.mask = 0;
   t.item.lParam = (int)var ;
   rv = TreeView_InsertItem(hwndTree,&t) ;
   return rv ;
}
void InsertSubTree(HTREEITEM parent, HTREEITEM after, VARINFO *var, int index)
{
   while(var) {
      var->hTreeItem = InsertItem(parent,after,var) ;
      var->watchindex = index ;
      var->watchhead.col1Text = &var->membername ;
      var->watchhead.col2Text = &var->value ;
      InsertSubTree(var->hTreeItem,0,var->subtype,index) ;
      after = var->hTreeItem ;
      var = var->link ;
   }
}
void AddItem(char *typetab, DEBUG_INFO *dbg, char *symtab, VARINFO *var)
{
   WATCHINFO *x = MatchItem(var) ;
   if (x) {
      FreeVarInfo(var) ;
   } else {
      HTREEITEM previous = 0 ;
      if (watchinfo_count)
         previous = watchinfo_list[watchinfo_count-1].info->hTreeItem ;
      memset(&watchinfo_list[watchinfo_count],0,sizeof(watchinfo_list[0])) ;
      watchinfo_list[watchinfo_count].info = var ;
      watchinfo_list[watchinfo_count].dbg_info = dbg ;
      watchinfo_list[watchinfo_count].symtab = symtab ;
      watchinfo_list[watchinfo_count++].typetab = typetab ;
      RefreshItem(&watchinfo_list[watchinfo_count-1], var->address) ;
      InsertSubTree(0,previous,var,watchinfo_count-1) ;
   }
}
void DeleteItem(POINT *pt)
{
   int i ;
   WATCHINFO *x ;
   TV_HITTESTINFO t ;
   HTREEITEM titem ;
   TV_ITEM item ;
   VARINFO *v ;

   ScreenToClient(hwndTree,pt) ;
   t.pt = *pt ;
   titem = TreeView_HitTest(hwndTree,&t) ;
   if (titem) {
      int c ;
      item.mask = TVIF_PARAM ;
      item.hItem = titem ;
      TreeView_GetItem(hwndTree,&item) ;
      v = (VARINFO *)item.lParam ;
      x = &watchinfo_list[c = v->watchindex] ;
      FreeTree(x->info) ;
      FreeVarInfo(x->info) ;
      for (i=c; i < watchinfo_count-1; i++)
         watchinfo_list[i] = watchinfo_list[i+1] ;
      watchinfo_count-- ;
   }
}
void DeleteAllItems(void)
{
   int i;
   TreeView_DeleteAllItems(hwndTree) ;
   for (i=0; i < watchinfo_count; i++)
      FreeVarInfo(watchinfo_list[i].info) ;
   watchinfo_count = 0 ;
}
void Unscope(WATCHINFO *wi)
{
   if (!wi->info->outofscope) {
      wi->info->outofscope = TRUE ;
      FreeTree(wi->info->subtype) ;
   }
}
void Rescope(WATCHINFO *wi, int index)
{
   if (wi->info->outofscope) {
      wi->info->outofscope = FALSE ;
      InsertSubTree(wi->info->hTreeItem,0,wi->info->subtype, index) ;
   }
}
void RefreshItems(void)
{
   int i ;
   char *types, *syms ;   
   int offset ;
   DEBUG_INFO * dbg ;
   for (i=0; i <watchinfo_count; i++) {
      WATCHINFO *wi = &watchinfo_list[i] ;
      offset = StoppedRegs.Eip ;
      if (!FindSymbol(&dbg, &types,&syms,&offset,
                     StoppedRegs.Eip, offset, (char *)wi->info->membername)) {
         Unscope(wi) ;
         offset = wi ;
      } else {
         VARINFO *x = GetVarInfo(wi->dbg_info,wi->typetab,wi->symtab,offset,wi->info->membername) ;
         Rescope(wi, i) ;
         offset = x->address ;
         FreeVarInfo(x) ;
      }
      RefreshItem(wi,offset) ;
   }
   InvalidateRect(hwndTree,0,0) ;
}
int ExpandPointer(VARINFO *v)
{
   if (v->pointer && v->subtype->placeholder) {
      TreeView_DeleteItem(hwndTree, v->subtype->hTreeItem) ;
      v->subtype->placeholder = 0 ;
      DeclType(watchinfo_list[v->watchindex].typetab,v->subtype,TRUE) ;
      InsertSubTree(v->hTreeItem,0,v->subtype, v->watchindex) ;
      RefreshAddresses(v,v->address) ;
      RefreshData(watchinfo_list[v->watchindex].typetab, v) ;
      return TRUE ;
   }
   return FALSE ;
}
int WriteValue(int address, void *value, int size)
{
   int len ;
   if (address < 0x1000) { // register
      int val = *(int *)value ;
      switch(address) {
         case CV_REG_AL:
            StoppedRegs.Eax &= 0xffffff00 ;
            StoppedRegs.Eax |= (unsigned char)val;
            return 1;
         case CV_REG_CL:
            StoppedRegs.Ecx &= 0xffffff00 ;
            StoppedRegs.Ecx |= (unsigned char)val;
            return 1;
         case CV_REG_DL:
            StoppedRegs.Edx &= 0xffffff00 ;
            StoppedRegs.Edx |= (unsigned char)val;
            return 1;
         case CV_REG_BL:
            StoppedRegs.Ebx &= 0xffffff00 ;
            StoppedRegs.Ebx |= (unsigned char)val;
            return 1;
         case CV_REG_AH:
            StoppedRegs.Eax &= 0xffff00ff ;
            StoppedRegs.Eax |= ((unsigned char)val)<<8;
            return 1;
         case CV_REG_CH:
            StoppedRegs.Ecx &= 0xffff00ff ;
            StoppedRegs.Ecx |= ((unsigned char)val)<<8;
            return 1;
         case CV_REG_DH:
            StoppedRegs.Edx &= 0xffff00ff ;
            StoppedRegs.Edx |= ((unsigned char)val)<<8;
            return 1;
         case CV_REG_BH:
            StoppedRegs.Ebx &= 0xffff00ff ;
            StoppedRegs.Ebx |= ((unsigned char)val)<<8;
            return 1;
         case CV_REG_AX:
            StoppedRegs.Eax &= 0xffff0000 ;
            StoppedRegs.Eax |= (unsigned short)val;
            return 2;
         case CV_REG_CX:
            StoppedRegs.Ecx &= 0xffff0000 ;
            StoppedRegs.Ecx |= (unsigned short)val;
            return 2;
         case CV_REG_DX:
            StoppedRegs.Edx &= 0xffff0000 ;
            StoppedRegs.Edx |= (unsigned short)val;
            return 2;
         case CV_REG_BX:
            StoppedRegs.Ebx &= 0xffff0000 ;
            StoppedRegs.Ebx |= (unsigned short)val;
            return 2;
         case CV_REG_SP:
            StoppedRegs.Esp &= 0xffff0000 ;
            StoppedRegs.Esp |= (unsigned short)val;
            return 2;
         case CV_REG_BP:
            StoppedRegs.Ebp &= 0xffff0000 ;
            StoppedRegs.Ebp |= (unsigned short)val;
            return 2;
         case CV_REG_SI:
            StoppedRegs.Esi &= 0xffff0000 ;
            StoppedRegs.Esi |= (unsigned short)val;
            return 2;
         case CV_REG_DI:
            StoppedRegs.Edi &= 0xffff0000 ;
            StoppedRegs.Edi |= (unsigned short)val;
         case CV_REG_EAX:
            StoppedRegs.Eax = val;
            return 4;
         case CV_REG_ECX:
            StoppedRegs.Ecx = val;
            return 4;
         case CV_REG_EDX:
            StoppedRegs.Edx = val;
            return 4;
         case CV_REG_EBX:
            StoppedRegs.Ebx = val;
            return 4;
         case CV_REG_ESP:
            StoppedRegs.Esp = val;
            return 4;
         case CV_REG_EBP:
            StoppedRegs.Ebp = val;
            return 4;
         case CV_REG_ESI:
            StoppedRegs.Esi = val;
            return 4;
         case CV_REG_EDI:
            StoppedRegs.Edi = val;
            return 4;
         case CV_REG_ST0:              // not supported
         case CV_REG_ST1:
         case CV_REG_ST2:
         case CV_REG_ST3:
         case CV_REG_ST4:
         case CV_REG_ST5:
         case CV_REG_ST6:
         case CV_REG_ST7:
            return 0 ;
      }
   } else {
      WriteProcessMemory(DebugProcess.hProcess,(LPVOID)address,(LPVOID) value,size, &len ) ;
      return len ;
   }
}
int InputEnum(char *typetab, VARINFO *info, char *text)
{
   int signedtype ;
   int v ;
   short *typeptr = info->typetab ;
   while (*(typeptr +1) == LF_FIELDLIST) {
      int done = FALSE ;
      int len = *typeptr - 2;
      typeptr += 2 ;
      while (len > 0 && !done ) {
         int xlen ;
         int rv ;
         char *nmptr ;
         switch(*typeptr) {
            case LF_ENUMERATE:
               xlen = sizeof (lfEnumerate) -1;
               if (v < LF_NUMERIC)
                  xlen += 2 ;
               else
                  xlen += 6 ;
               nmptr = ((char *)typeptr) + xlen ;
               rv = GetNumericLeaf(&((lfEnumerate *)typeptr)->value) ;
               {
                  char buf1[256] ;
                  memset(buf1,0,256) ;   
                  strncpy(buf1,nmptr + 1, *nmptr) ;
                  if (!strcmp(buf1,text))
                     return rv ;
               }
               xlen += *nmptr + 1;
               if ((*((char *)typeptr+xlen) & 0xf0) == 0xf0)
                  xlen += *((char *)typeptr + xlen) & 15 ;
               (char *)typeptr += xlen ;
               len -= xlen ;
               break ;
            case LF_INDEX:
               typeptr = LookupType(typetab,((lfIndex *)typeptr)->index) ;
               done = TRUE ;
               break ;
            default:
               return 0 ;
         }
      }
   }
   return 0 ;
}
void ChangeData(VARINFO *info, char *text)
{
   if (CV_TYP_IS_REAL(info->type)) {
      float v ;
      double v2 ;
      char data[10] ;
      sscanf(text,"%f",&v) ;
      switch(info->type) {
         case T_REAL32:
            WriteValue(info->address,&v,4) ;
            break ;
         case T_REAL64:
            v2 = v ;
            WriteValue(info->address,&v2,8) ;
            break ;
         case T_REAL80:
            asm fld dword ptr [v] 
            asm fstp tbyte ptr [data]
            WriteValue(info->address,data,10) ;
            break ;
         default:
            break ;
      }
   } else {
      int value ;
      int size ;
      if (info->enumx && !isdigit(text[0])) {
         value = InputEnum(watchinfo_list[info->watchindex].typetab,info,text) ;
         size = 4 ;
      } else {
         if (text[0] == '0' && text[1] == 'x')
            sscanf(text+2,"%x",&value) ;
         else
            sscanf(text, "%d",&value) ;
         switch (CV_TYPE(info->type)) {
            case CV_INT:
               size = 1 << (CV_SUBT(info->type)/2) ;
               break ;
            case CV_SIGNED:
            case CV_UNSIGNED:
            case CV_BOOLEAN:
               size = 1 << CV_SUBT(info->type) ;
               break ;
         }
      }
      WriteValue(info->address,&value,size) ;
   }
   RefreshAddresses(info,info->address) ;
   RefreshData(watchinfo_list[info->watchindex].typetab, info) ;
}
LRESULT  CALLBACK _export WatchWndProc( HWND hwnd, UINT iMessage, WPARAM wParam,
                                 LPARAM lParam )
{
   static int selected ;
   static POINT menupos ;
   static char buf[40 ] ;
   RECT r,*pr ;
   WINDOWPOS wp ;
   HD_ITEM hie ;
   HD_LAYOUT hdl ;
   NMHDR *h ;
   DRAWITEMSTRUCT *dr ;
   HBITMAP hbmp ;
   HDC hMemDC ;
   TCHeader tch ;
   TV_ITEM item ;
	TV_INSERTSTRUCT t;
   static int sizingbottom ;
   char *types, *syms ;   
   int offset ;
   DEBUG_INFO * dbg ;
   NM_TREEVIEW *nmt ;
   VARINFO *var ;
	switch(iMessage) {
      case WM_SYSCOMMAND:
         if (wParam == SC_CLOSE) {
            PostMessage(hwnd,WM_CLOSE,0,0) ;
            return 0 ;
         }
         break ;
		case WM_NOTIFY:
         h = (NMHDR *)lParam ;
         switch(h->code) {
            case NM_RCLICK :
               {
                  HMENU  menu  = LoadMenu( hInstance, "WATCHMENU" ) ;
                  HMENU popup = GetSubMenu( menu, 0 );
                  GetCursorPos( &menupos );
                  TrackPopupMenuEx( popup, TPM_BOTTOMALIGN | TPM_LEFTBUTTON,
                           menupos.x, menupos.y, hwndFrame, NULL );
                  DestroyMenu( menu );
               }
               return 0 ;
            case TVN_ITEMEXPANDING:
               nmt = h ;
               if (nmt->action) {
                  ExpandPointer((VARINFO *)nmt->itemNew.lParam) ;
               }
               return 0 ;
            case TCN_EDITQUERY:
               nmt = h ;
               item.mask = TVIF_PARAM ;
               item.hItem = (HTREEITEM) nmt->itemNew.hItem ;
               TreeView_GetItem(hwndTree, &item) ;
               var = (VARINFO *)item.lParam ;
               if (var->editable) {
                  WatchValue(watchinfo_list[var->watchindex].typetab,buf,var,TRUE) ;
                  return buf ;
               }
               return 0 ;
            case TCN_EDITDONE:
               nmt = h ;
               item.mask = TVIF_PARAM ;
               item.hItem = (HTREEITEM) nmt->itemNew.hItem ;
               TreeView_GetItem(hwndTree, &item) ;
               var = (VARINFO *)item.lParam ;
               ChangeData(var,nmt->itemNew.pszText) ;
               return 0 ;
         }
			break ;
		case WM_CREATE :
         hwndWatch = hwnd ;
         GetClientRect(hwnd,&r) ;
         hwndCtrl = CreateControlWindow(hwndFrame,hwnd,&r,FALSE) ;
         GetClientRect(hwnd,&r) ;
         SendMessage(hwndCtrl,LCF_ADJUSTRECT,0,(LPARAM)&r) ;
         valueBitmap = LoadBitmap(hInstance, "ID_VALUEBMP") ;
         itemBitmap = LoadBitmap(hInstance, "ID_ITEMBMP") ;
         ChangeBitmapColor(valueBitmap,0x00ffff,GetSysColor(COLOR_3DFACE)) ;
         ChangeBitmapColor(itemBitmap,0x00ffff,GetSysColor(COLOR_3DFACE)) ;
         tch.colText1 = "Item" ;
         tch.colText2 = "Value" ;
         tch.colBmp1 = itemBitmap ;
         tch.colBmp2 = valueBitmap ;
         hwndTree = CreateextTreeWindow(hwndCtrl,WS_DLGFRAME|TCS_LINE,&r,&tch);
//         PostMessage(hwnd,WM_CLOSE,0 , 0 ) ;
//         PostMessage(hwndFrame, WM_REDOSIZING, 0, 0) ;
			return 0 ;
      case WM_ADDWATCH:
         offset = wParam ;
         if (!offset) {
            DeleteAllItems() ;
         } else if (FindSymbol(&dbg, &types,&syms,&offset,
                     StoppedRegs.Eip, offset, (char *)lParam)) {
            var = GetVarInfo(dbg,types,syms,offset,(char *)lParam) ;
            if (var)
               AddItem(types,dbg,syms, var) ;
         } else
            ExtendedMessageBox("Symbol scope",0,"Symbol does not exist in this scope") ;
			break ;
      case WM_COMMAND:
         switch(wParam) {
            case ID_SETADDRESS:
               RefreshItems() ;
               break ;
            case IDM_DELETEWATCH:
               DeleteItem(&menupos) ;
               break ;
         }
         break ;
		case WM_DESTROY:
         TreeView_DeleteAllItems(hwndTree) ;
         DestroyWindow(hwndTree) ;
         DeleteObject(valueBitmap) ;
         DeleteObject(itemBitmap) ;
         DestroyWindow(hwndCtrl) ;
         hwndWatch = 0 ;
         break ;
		case WM_SIZE :
         r.left = r.top = 0 ;
         r.right = LOWORD(lParam) ;
         r.bottom = HIWORD(lParam) ;
         MoveWindow(hwndCtrl,r.left,r.top,r.right-r.left,r.bottom-r.top,TRUE) ;
         SendMessage(hwndCtrl,LCF_ADJUSTRECT,0,(LPARAM)&r) ;
         MoveWindow(hwndTree,r.left,r.top,r.right-r.left,r.bottom-r.top,TRUE) ;
			return 0;
		case WM_CLOSE:
         ShowWindow(hwnd,SW_HIDE) ;
         docking |= HF_WATCH ;
         PostMessage(hwndFrame,WM_REDOSIZING,0,0) ;
         return 0 ;
      case WN_ENDMOVE:
         pr = (RECT *)lParam ;
         GetRelativeRect(hwndFrame,hwnd,&r) ;
         if (!memcmp(pr,&r,sizeof(r)))
            break ;
         GetFrameInternalRect(&r) ;
         if (pr->right == r.right ) {
            docking |= DF_WATCH ;
         } else {
            docking &= ~DF_WATCH ;
            pr->top -= r.top ;
            pr->bottom -= r.top ;
            GetRelativeRect(hwndFrame, hwndClient, &r) ;
            pr->left -= r.left ;
            pr->right -= r.left ;
         }
         SendMessage(hwndFrame, WM_REDOSIZING, 0, 0) ;
         MoveWindow(hwnd,pr->left, pr->top, pr->right-pr->left, pr->bottom-pr->top, TRUE) ;
         if ((docking & DF_WATCH) && (docking & DF_ERROR) && sizingbottom) {
            RECT r1 ;
            GetRelativeRect(hwndFrame,hwnd,&r1) ;
            GetRelativeRect(hwndFrame,hwndError, &r) ;
            if (r.top != r1.bottom) {
               GetFrameInternalRect(&r) ;
               r.top = r1.bottom ;
               MoveWindow(hwndError,r.left,r.top,r.right-r.left,r.bottom-r.top, TRUE) ;
            }
         }
         if ((docking & DF_PROJECT) && (docking & DF_ERROR) && sizingbottom) {
            RECT r1 ;
            GetRelativeRect(hwndFrame,hwndTab,&r1) ;
            r1.bottom = pr->bottom ;
            MoveWindow(hwndTab,r1.left,r1.top,r1.right-r1.left,r1.bottom-r1.top, TRUE) ;
         }
         SendMessage(hwndFrame, WM_REDOSIZING, 0, 0) ;
         sizingbottom = FALSE ;
         break ;
      case WM_SIZING:
         if (docking & DF_PROJECT) {
            if (wParam == WMSZ_BOTTOMRIGHT || wParam == WMSZ_TOPRIGHT ||
                  wParam == WMSZ_TOP || wParam == WMSZ_RIGHT) {
               GetRelativeRect(hwndFrame,hwnd, (RECT *)lParam) ;
            } else if (wParam == WMSZ_BOTTOM || wParam == WMSZ_BOTTOMRIGHT)
               sizingbottom = TRUE ;
         }
         break ;
      case WM_MOVING:
         pr = (RECT *)lParam ;
         GetFrameInternalRect(&r) ;
         if (pr->right < r.right-5) {
            if (r.top > pr->top) {
               pr->top = r.top ;
            }
            pr->bottom = pr->top + 160 ;
         } else {
               pr->top = r.top ;
               pr->left += r.right-pr->right ;
               pr->right = r.right ;
               if ((docking & DF_ERROR) && !(docking & HF_ERROR)) {
                  GetRelativeRect(hwndFrame,hwndError,&r) ;
                  pr->bottom = r.top ;
               } else
                  pr->bottom = r.bottom ;

         }
         return 0 ;

	}
   return DefMDIChildProc(hwnd, iMessage, wParam, lParam) ;
}

void RegisterWatchWindow(void)
{
		WNDCLASS wc ;
      wc.style = 0 ;
      wc.lpfnWndProc = &WatchWndProc ;
		wc.cbClsExtra = 0;
      wc.cbWndExtra = 0;
		wc.hInstance = hInstance ;
		wc.hIcon = LoadIcon(0,IDI_APPLICATION) ;
		wc.hCursor = LoadCursor(0,IDC_ARROW) ;
		wc.hbrBackground = GetStockObject(WHITE_BRUSH) ;
		wc.lpszMenuName = 0 ;
      wc.lpszClassName = szWatchClassName ;
		RegisterClass(&wc) ;
}
void CreateWatchWindow(void)
{
   RECT r,r1 ;
   GetFrameInternalRect(&r) ;
   GetRelativeRect(hwndFrame,hwndError,&r1) ;
   r.bottom = r1.top ;
   r.left = r.right-180 ;
            CreateWindowEx( 0, szWatchClassName, szWatchTitle,
            WS_CHILD | WS_CLIPSIBLINGS | WS_DLGFRAME,
            r.left,r.top,r.right-r.left,r.bottom-r.top, hwndFrame, 0 ,hInstance, 0) ;   
  docking |= HF_WATCH ;
}