/* 
   Copyright 2001-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222

**********************************************************************

LSTREE.C defines a two-column tree for use in things like the watch
window and the register window.  The right-hand column is editable.
The editing function is done by superclassing the tree control and adding
editablility.  A higher level window was added as a wrapper to do things
like manage the header.  The watch window will use this control for display
purposes.
*/
#define STRICT
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "lsctrl.h"

extern HINSTANCE hInstance ;
static char *szextTreeWindClassName = "ladSoftextTreeWindow" ;
static char *szColumnTreeWindClassName = "ladSoftColumnTreeWindow" ;
static char *szextEditWindClassName = "ladSoftextEditWindow" ;
static int wndoffstree,wndoffsedit ;
static WNDPROC oldproc,oldeditproc ;
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"
} ;

typedef struct {
   HWND hwndHeader ;
   HWND hwndTree ;
   HFONT watchFont ;
   HTREEITEM displaylist[200] ;
   int displaycount ;
} COLUMNINFO ;

typedef struct {
   HWND hwndEdit ;
   HTREEITEM editItem ;
   int divider ;
} TREEINFO ;
LRESULT  CALLBACK _export extEditWndProc( HWND hwnd, UINT iMessage, WPARAM wParam,
                                                         LPARAM lParam )
{
   LRESULT rv ;
   RECT r ;
   switch(iMessage) {
      case WM_CHAR:
         if (wParam == 13) {
            SendMessage(GetParent(hwnd),TCN_EDITDONE,0,0) ;
            return 0 ;
         }
   }
   return CallWindowProc(oldeditproc,hwnd,iMessage,wParam,lParam) ;
}
LRESULT  CALLBACK _export extTreeWndProc( HWND hwnd, UINT iMessage, WPARAM wParam,
                                                         LPARAM lParam )
{
   static char buf[256] ;
   LRESULT rv ;
   RECT r ;
   TREEINFO *ptr ;
   HTREEITEM hTreeItem ;
   TCData *td ;
   TV_HITTESTINFO tvh ;
   TV_ITEM item;
   NM_TREEVIEW x ;
   switch(iMessage) {
      case WM_CREATE:
         ptr = (TREEINFO *)HeapAlloc(GetProcessHeap(),0,sizeof(TREEINFO)) ;
         ptr->hwndEdit = 0 ;
         SetWindowLong(hwnd,wndoffstree,(int)ptr ) ;
         break ;
      case WM_DESTROY :
         ptr = (TREEINFO *)GetWindowLong(hwnd,wndoffstree) ;
         if (ptr->hwndEdit)
            DestroyWindow(ptr->hwndEdit) ;
         HeapFree(GetProcessHeap(),0,ptr) ;
         break ;
      case WM_PAINT:
         GetUpdateRect(hwnd,&r,FALSE) ;
         rv = CallWindowProc(oldproc,hwnd,iMessage,wParam,lParam) ;
         SendMessage(GetParent(hwnd),TCN_PAINT,0,(LPARAM)&r) ;
         return rv ;
      case WM_LBUTTONDOWN:
         ptr = (TREEINFO *)GetWindowLong(hwnd,wndoffstree) ;
         if (!ptr->hwndEdit) {
           tvh.pt.x = LOWORD(lParam) ;
           tvh.pt.y = HIWORD(lParam) ;
           if (tvh.pt.x < ptr->divider)
               break ;
           TreeView_HitTest(hwnd, &tvh) ;
           hTreeItem = tvh.hItem ;
           if (hTreeItem && tvh.pt.x >= ptr->divider) {
              char *val ;
              x.hdr.code = TCN_EDITQUERY ;
              x.itemOld.mask = 0;
              x.itemNew.mask = 0 ;
              x.itemNew.hItem = hTreeItem ;
              if (val = (char *)SendMessage(GetParent(hwnd),WM_NOTIFY,0,(LPARAM)&x)) {
                 HFONT font = (HFONT)SendMessage(hwnd,WM_GETFONT,0,0) ;
                 ptr->editItem = hTreeItem ;
                 TreeView_GetItemRect(hwnd,hTreeItem, &r,FALSE) ;
                 r.left = ptr->divider ;
                 item.hItem = hTreeItem;
                 item.mask = TVIF_PARAM ;
                 TreeView_GetItem(hwnd,&item) ;
                 td = (TCData *)item.lParam ;
                 ptr->hwndEdit = CreateWindow(szextEditWindClassName,val,
                    WS_CHILD | ES_AUTOHSCROLL,
                    r.left,r.top,r.right-r.left,r.bottom-r.top,
                    hwnd, (HMENU)449, (HINSTANCE)GetWindowLong(GetParent(hwnd),GWL_HINSTANCE),0) ;
                 SendMessage(ptr->hwndEdit,WM_SETFONT,(WPARAM)font,MAKELPARAM(0,0)) ;
                 SendMessage(ptr->hwndEdit,EM_SETSEL,0,(LPARAM)-1) ;
                 SendMessage(ptr->hwndEdit,EM_SETLIMITTEXT,40,0) ;
                 ShowWindow(ptr->hwndEdit, SW_SHOW) ;
                 SetFocus(ptr->hwndEdit) ;
                 return 0 ;
              }
           }
           break ;
         }
         // FALL THROUGH 
      case TCN_EDITDONE:
         ptr = (TREEINFO *)GetWindowLong(hwnd,wndoffstree) ;
         SendMessage(ptr->hwndEdit,WM_GETTEXT,50,(LPARAM)buf) ;
         DestroyWindow(ptr->hwndEdit) ;
         ptr->hwndEdit = 0 ;
         x.hdr.code = TCN_EDITDONE ;
         x.itemOld.mask = 0;
         x.itemNew.mask = TVIF_TEXT ;
         x.itemNew.hItem = ptr->editItem ;
         x.itemNew.pszText = buf ;
         return SendMessage(GetParent(hwnd),WM_NOTIFY,0,(LPARAM)&x) ;
      case TCF_SETDIVIDER:
         ptr = (TREEINFO *)GetWindowLong(hwnd,wndoffstree) ;
         ptr->divider = lParam -GetSystemMetrics(SM_CXBORDER);
         return 0 ;

   }
	return CallWindowProc(oldproc,hwnd,iMessage,wParam,lParam) ;
}
LRESULT  CALLBACK _export ColumnTreeWndProc( HWND hwnd, UINT iMessage, WPARAM wParam,
                                                         LPARAM lParam )
{
   HD_LAYOUT hdl ;
   WINDOWPOS wp ;
   COLUMNINFO *ptr ;
   RECT r, r1, *rp ;
   HD_ITEM hie ;
   TCHeader *h ;
   HD_NOTIFY *n ;
   TV_DISPINFO *t ;
   LPTV_INSERTSTRUCT is ;
   int i ;
   TV_ITEM item;
   TCData *td ;
   TV_HITTESTINFO tvh ;
   TREEINFO *treeinfo ;
   if (iMessage >= TV_FIRST && iMessage < TV_FIRST + 100) {
      ptr = (COLUMNINFO *)GetWindowLong(hwnd,0) ;
      switch(iMessage) {
         case TVM_HITTEST:
            tvh.pt = ((TV_HITTESTINFO *)lParam)->pt ;
            ptr = (COLUMNINFO *)GetWindowLong(hwnd,0) ;
            GetRelativeRect(hwnd,ptr->hwndTree,&r) ;
            tvh.pt.x -= r.left ;
            tvh.pt.y -= r.top ;
            treeinfo = TreeView_HitTest(ptr->hwndTree,&tvh) ;
            if (treeinfo) {
               ((TV_HITTESTINFO *)lParam)->flags = tvh.flags ;
               ((TV_HITTESTINFO *)lParam)->hItem = tvh.hItem ;
            }
            return treeinfo ;
         case TVM_INSERTITEM:
            is = (LPTV_INSERTSTRUCT)lParam ;
            is->item.mask |= TVIF_TEXT | TVIF_PARAM;
            is->item.pszText = LPSTR_TEXTCALLBACK ;
            // FALL THROUGH 
         default:
            return SendMessage(ptr->hwndTree,iMessage,wParam,lParam) ;
      }
   } else if (iMessage >= HDM_FIRST && iMessage < HDM_FIRST + 100) {
      ptr = (COLUMNINFO *)GetWindowLong(hwnd,0) ;
      return SendMessage(ptr->hwndHeader,iMessage,wParam,lParam) ;
   }
   switch(iMessage) {
      case WM_NOTIFY:
         n  = (HD_NOTIFY *)lParam ;
         ptr = (COLUMNINFO *)GetWindowLong(hwnd,0) ;
         switch( n->hdr.code) {
            case HDN_ENDTRACK:
               GetClientRect(ptr->hwndHeader,&r) ;
               hie.mask = HDI_WIDTH ;
               hie.cxy = r.right-r.left - n->pitem->cxy ;
               SendMessage(ptr->hwndHeader,HDM_SETITEM,!n->iButton,(LPARAM)&hie) ;
               SendMessage(ptr->hwndTree, TCF_SETDIVIDER, 0, hie.cxy) ;
               InvalidateRect(ptr->hwndTree,0,1) ;
               return 0 ;
            case TVN_GETDISPINFO:
               t = n ;
               if (TreeView_GetItemRect(ptr->hwndTree,t->item.hItem,&r,TRUE)) {
                  ptr->displaylist[ptr->displaycount++] =  t->item.hItem ;
                  strcpy(t->item.pszText,"") ;
                  t->item.cchTextMax = 0 ;
               }
               return 0 ;               
         }
         // fall through 
      case WM_COMMAND:
         return SendMessage(GetParent(hwnd),iMessage,wParam,lParam) ;
      case WM_CREATE:
         ptr = (COLUMNINFO *)HeapAlloc(GetProcessHeap(),0,sizeof(COLUMNINFO)) ;
         ptr->displaycount = 0 ;
         SetWindowLong(hwnd,0,(int)ptr) ;
         GetClientRect(hwnd, &r) ;
         ptr->hwndHeader = CreateWindow(WC_HEADER, 0,
            WS_CHILD | HDS_HORZ | WS_BORDER, 
            r.left,r.top,r.right-r.left,r.bottom-r.top,
            hwnd, 0 ,(HINSTANCE)GetWindowLong(GetParent(hwnd),GWL_HINSTANCE),0) ;
         hdl.prc = &r ;
         hdl.pwpos = &wp;
         SendMessage(ptr->hwndHeader, HDM_LAYOUT, 0, (LPARAM) &hdl) ;
         ptr->watchFont = CreateFontIndirect(&fontdata) ;
         SendMessage(ptr->hwndHeader, WM_SETFONT,(WPARAM)ptr->watchFont,0) ;
         SetWindowPos(ptr->hwndHeader, 0, wp.x,wp.y,wp.cx,wp.cy,wp.flags |SWP_SHOWWINDOW) ; 
         r.left = wp.x ;
         r.right = wp.x + wp.cx ;
         r.top = wp.y + wp.cy ;
         ptr->hwndTree = CreateWindow(szextTreeWindClassName, 0,
            WS_VISIBLE | WS_CHILD | WS_BORDER | TVS_HASLINES | TVS_LINESATROOT | TVS_HASBUTTONS,
            r.left,r.top,r.right-r.left,r.bottom-r.top,
            hwnd, (HMENU)448 ,(HINSTANCE)GetWindowLong(GetParent(hwnd),GWL_HINSTANCE),0) ;
         SendMessage(ptr->hwndTree, WM_SETFONT,(WPARAM)ptr->watchFont,0) ;
         hie.mask = HDI_WIDTH ;
         SendMessage(ptr->hwndHeader,HDM_GETITEM,0,(LPARAM)&hie) ;
         SendMessage(ptr->hwndTree, TCF_SETDIVIDER, 0, hie.cxy) ;
         break ;
         
      case WM_DESTROY:
         ptr = (COLUMNINFO *)GetWindowLong(hwnd,0) ;
         DestroyWindow(ptr->hwndHeader) ;
         DeleteObject(ptr->watchFont) ;
         HeapFree(GetProcessHeap(),0,(void *)ptr) ;
         break ;
      case WM_SIZE:
         ptr = (COLUMNINFO *)GetWindowLong(hwnd,0) ;
         r.left = r.top = 0 ;
         r.right = LOWORD(lParam) ;
         r.bottom = HIWORD(lParam) ;
         hdl.prc = &r ;
         hdl.pwpos = &wp;
         SendMessage(ptr->hwndHeader,HDM_LAYOUT,0, (LPARAM) & hdl) ;
         SetWindowPos(ptr->hwndHeader, 0, wp.x,wp.y,wp.cx,wp.cy, wp.flags) ;
         r.left = wp.x ;
         r.right = wp.x + wp.cx ;
         r.top = wp.y + wp.cy ;
         MoveWindow(ptr->hwndTree,r.left,r.top,r.right-r.left,r.bottom-r.top,TRUE) ;
         GetClientRect(ptr->hwndHeader,&r) ;
         hie.mask = HDI_WIDTH ;
         hie.cxy = (r.right-r.left)/2 ;
         SendMessage(ptr->hwndHeader,HDM_SETITEM,0,(LPARAM)&hie) ;
         SendMessage(ptr->hwndHeader,HDM_SETITEM,1,(LPARAM)&hie) ;
         SendMessage(ptr->hwndTree, TCF_SETDIVIDER, 0, hie.cxy) ;
         break ;
      case TCF_SETHEADER:
         ptr = (COLUMNINFO *)GetWindowLong(hwnd,0) ;
         h = (TCHeader *)lParam ;
         GetWindowRect(ptr->hwndHeader,&r) ;
         hie.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH ;
         if (h->colBmp1)
            hie.mask |= HDI_BITMAP ;
         hie.pszText = h->colText1 ;
         hie.hbm = h->colBmp1 ;
         hie.cxy  = (r.right-r.left)/2 ;
         hie.cchTextMax = strlen(h->colText1)  ;
         hie.fmt = HDF_LEFT | HDF_STRING ;
         SendMessage(ptr->hwndHeader, HDM_INSERTITEM,100,(LPARAM) &hie) ;
         hie.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH ;
         if (h->colBmp2)
            hie.mask |= HDI_BITMAP ;
         hie.pszText = h->colText2 ;
         hie.hbm = h->colBmp2 ;
         hie.cxy  = (r.right-r.left)/2 ;
         hie.cchTextMax = strlen(h->colText2)  ;
         hie.fmt = HDF_LEFT | HDF_STRING ;
         SendMessage(ptr->hwndHeader, HDM_INSERTITEM,100,(LPARAM) &hie) ;
         SendMessage(ptr->hwndTree, TCF_SETDIVIDER, 0, hie.cxy) ;
         return 0 ;
      case TCN_PAINT:
         rp = (RECT *)lParam ;
         ptr = (COLUMNINFO *)GetWindowLong(hwnd,0) ;
         {
            hie.mask = HDI_WIDTH ;
            SendMessage(ptr->hwndHeader,HDM_GETITEM,0,(LPARAM)&hie) ;
            hie.cxy -= GetSystemMetrics(SM_CXBORDER) ;
            rp->left = hie.cxy ;
            if (rp->left < rp->right) {
               LOGBRUSH lbrush ;
               HBRUSH graybrush ;
               HDC dc ;
               int lined = FALSE ;
               HFONT font = (HFONT)SendMessage(ptr->hwndTree,WM_GETFONT,0,0) ;
               lbrush.lbStyle = BS_SOLID ;
               lbrush.lbColor = 0xff0000 ;
               graybrush = CreateBrushIndirect(&lbrush) ;
               dc = GetDC(ptr->hwndTree) ;
               font = SelectObject(dc,font) ;
               if (GetWindowLong(hwnd,GWL_STYLE) & TCS_LINE) {
                  HPEN pen ;
                  pen = CreatePen(PS_SOLID ,1,0) ;

                  lined = TRUE ;
                  pen = SelectObject(dc,pen) ;
                  MoveToEx(dc,rp->left,rp->top,0) ;
                  for (i = 0 ; i < rp->bottom - rp->top; i+=2) {
                     MoveToEx(dc,rp->left,rp->top+i,0) ;
                     LineTo(dc,rp->left,rp->top+i+1) ;
                  }
                  pen = SelectObject(dc,pen) ;
                  DeleteObject(pen) ;
               }
               for (i=0; i < ptr->displaycount; i++) {
                  COLORREF color ;
                  item.hItem = ptr->displaylist[i] ;
                  item.mask = TVIF_PARAM ;
                  TreeView_GetItem(ptr->hwndTree,&item) ;
                  td = (TCData *)item.lParam ;
                  TreeView_GetItemRect(ptr->hwndTree,ptr->displaylist[i], &r, TRUE) ;
                  if (td->col1Text) {
                     HRGN rgn ;
                     rgn = CreateRectRgn(r.left,r.top,rp->left-2,r.bottom) ;
                     SelectClipRgn(dc,rgn) ;
                     color = SetTextColor(dc,td->col1Color) ;
                     TextOut(dc, r.left,r.top,td->col1Text,strlen(td->col1Text)) ;
                     SetTextColor(dc,color) ;
                     rgn = SelectClipRgn(dc,NULL) ;
                     DeleteObject(rgn) ;
                  }
                  if (td->col2Text) {
                     color = SetTextColor(dc,td->col2Color) ;
                     TextOut(dc, rp->left+(lined ? 3 : 0),r.top,td->col2Text,strlen(td->col2Text)) ;
                     SetTextColor(dc,color) ;
                  }
               }

               ptr->displaycount = 0 ;
               SelectObject(dc,font) ;
               ReleaseDC(hwnd,dc) ;
               DeleteObject(graybrush) ;
            }
         }
         break ;
   }
   return DefWindowProc(hwnd,iMessage,wParam,lParam) ;
}
void RegisterextTreeWindow(void)
{
		WNDCLASS wc ;
      GetClassInfo(0,WC_TREEVIEW,&wc) ;
		oldproc = wc.lpfnWndProc ;
      wc.lpfnWndProc = &extTreeWndProc ;
      wc.lpszClassName = szextTreeWindClassName ;
		wc.hInstance = hInstance ;
		wc.cbWndExtra+= (4-wc.cbWndExtra % 4) ;
      wndoffstree = wc.cbWndExtra ;
		wc.cbWndExtra += 4 ;
      RegisterClass(&wc) ;

      GetClassInfo(0,"edit",&wc) ;
      oldeditproc = wc.lpfnWndProc ;
      wc.lpfnWndProc = &extEditWndProc ;
      wc.lpszClassName = szextEditWindClassName ;
		wc.hInstance = hInstance ;
		wc.cbWndExtra+= (4-wc.cbWndExtra % 4) ;
      wndoffsedit = wc.cbWndExtra ;
		wc.cbWndExtra += 4 ;
		RegisterClass(&wc) ;

      memset(&wc,0,sizeof(wc)) ;
      wc.style = 0;
      wc.lpfnWndProc = &ColumnTreeWndProc ;
		wc.cbClsExtra = 0;
      wc.cbWndExtra = sizeof(void *);
		wc.hInstance = hInstance ;
		wc.hIcon = LoadIcon(0,IDI_APPLICATION) ;
		wc.hCursor = LoadCursor(0,IDC_ARROW) ;
      wc.hbrBackground = 0 ;
		wc.lpszMenuName = 0 ;
      wc.lpszClassName = szColumnTreeWindClassName ;
		RegisterClass(&wc) ;

}
HWND CreateextTreeWindow(HWND parent, int style,  RECT *r, TCHeader *h)
{
   HWND hwnd = CreateWindowEx(0, szColumnTreeWindClassName, "",
            WS_CLIPSIBLINGS | WS_VISIBLE | WS_CHILD | style,
            r->left,r->top,r->right-r->left,r->bottom-r->top,
            parent, (HMENU)0 ,(HINSTANCE)GetWindowLong(parent,GWL_HINSTANCE), 0) ;

   if (hwnd)
      SendMessage(hwnd,TCF_SETHEADER,0,(LPARAM)h) ;
   return hwnd ;   
}