/*** 
*tibrowse.cpp
*
*  Copyright (C) 1992-1994, Microsoft Corporation.  All Rights Reserved.
*  Information Contained Herein Is Proprietary and Confidential.
*
*Purpose:
*  Type Information Browser
*
*
*Implementation Notes:
*
*****************************************************************************/

#if defined(_MAC)

#if defined(_PPCMAC)
#pragma data_seg("_FAR_DATA")
#pragma data_seg( )
#define MAXLONG 0x7fffffff
#define EventHandlerProcPtr AEEventHandlerUPP
#else //_PPCMAC
#define  GetMenuItemText(mApple,menuItem,daName)  GetItem(mApple,menuItem,daName)
#endif //_PPCMAC

#endif //_MAC

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

#include "tibrowse.h"

#ifdef WIN32 
  #define SPRINTF	swprintf
#else
  #define SPRINTF	sprintf
#endif

#if defined(UNICODE)
  #define TCHAR		WCHAR
  #define TSTR(str)	L##str
#else
  #define TCHAR		char
  #define TSTR(str)	str	
  #ifndef LPTSTR
    #define LPTSTR LPSTR
  #endif
#endif

#ifdef _MAC
DECLARE_ROUTINE_DESC(UserItemUPP,DrawListbox);
#endif

#ifdef _MAC
DialogPtr g_pdlg = NULL;
ListHandle g_rghlist[IDC_PARAMLIST+1] = {NULL};
#else
HWND g_hwnd;
TCHAR g_szAppName[] = TSTR("TiBrowse");
#endif

ITypeLib  FAR *g_ptlib = NULL;
ITypeInfo FAR *g_ptinfoCur = NULL;
TYPEATTR  FAR *g_ptattrCur = NULL;

OLECHAR * g_rgszTKind[] = {
    OLESTR("Enum"),		/* TKIND_ENUM */
    OLESTR("Struct"),		/* TKIND_RECORD */
    OLESTR("Module"),		/* TKIND_MODULE */
    OLESTR("Interface"),	/* TKIND_INTERFACE */
    OLESTR("Dispinterface"),	/* TKIND_DISPATCH */
    OLESTR("Coclass"),		/* TKIND_COCLASS */
    OLESTR("Typedef"),		/* TKIND_ALIAS */
    OLESTR("Union"),		/* TKIND_UNION */
};


// Set the text of the control identified by the given control id
void
XSetDlgItemText(int ctlid, OLECHAR FAR* psz)
{
#ifdef _MAC
    Rect rc;
    Handle h;
    short kind;
    char buf[255];

    strcpy(buf, psz);
    GetDItem(g_pdlg, ctlid, &kind, &h, &rc);
    SetIText(h, c2pstr(buf));
#else
#if defined(WIN32) && !defined(UNICODE)
    char bufA[256];
    WideCharToMultiByte(CP_ACP, NULL, psz, -1, bufA, 256, NULL, NULL);
    SendDlgItemMessage(g_hwnd, ctlid, WM_SETTEXT, 0, (LPARAM)&bufA);
#else
    SendDlgItemMessage(g_hwnd, ctlid, WM_SETTEXT, 0, (LPARAM)psz);
#endif
#endif
}

// Clear the listbox identified by the given control id
void
XClrDlgItemList(int ctlid)
{
#ifdef _MAC
    LDelRow(0, 0, g_rghlist[ctlid]); // delete everything
#else
    SendMessage(GetDlgItem(g_hwnd, ctlid), LB_RESETCONTENT, 0, 0L); 
#endif
}

// Add the given string to the listbox identified by the given control id
void
XAddDlgItemList(int ctlid, OLECHAR FAR* psz)
{
#ifdef _MAC
    int row;
    Point pt;
    ListHandle hlist;

    hlist = g_rghlist[ctlid];
    row = LAddRow(1, -1, hlist);
    pt.v = row, pt.h = 0;
    LSetCell(psz, strlen(psz), pt, hlist);
#else
#if defined(WIN32) && !defined(UNICODE)
    char bufA[256];
    WideCharToMultiByte(CP_ACP, NULL, psz, -1, bufA, 256, NULL, NULL);
    SendDlgItemMessage(g_hwnd, ctlid, LB_ADDSTRING, 0, (LPARAM)&bufA);
#else
    SendDlgItemMessage(g_hwnd, ctlid, LB_ADDSTRING, 0, (LPARAM)psz);
#endif
#endif
}

void Cleanup()
{
    if(g_ptinfoCur != NULL){
      if(g_ptattrCur != NULL)
        g_ptinfoCur->ReleaseTypeAttr(g_ptattrCur);
      g_ptinfoCur->Release();
      g_ptinfoCur = NULL;
    }
    if(g_ptlib != NULL){
      g_ptlib->Release();
      g_ptlib = NULL;
    }
}

void Uninit()
{
    OleUninitialize();	 
#ifdef _MAC
#ifndef _PPCMAC
    UninitOleManager();			// clean up applet
#endif
#endif
}


void
OpenTypeLib(OLECHAR FAR *sztlib)
{                                     
    unsigned int utypeinfoCount, i;
    BSTR bstrName;
    TLIBATTR FAR* ptlibattr;

    /* clear out globals */
    Cleanup();

    /* clear out listboxes */
    XClrDlgItemList(IDC_TYPELIST);
    XClrDlgItemList(IDC_MEMBERLIST);
    XClrDlgItemList(IDC_PARAMLIST);
	
    /* load the type library */
    CHECKRESULT(LoadTypeLib(sztlib, &g_ptlib));

    /* get library attributes for the fun of it */
    CHECKRESULT(g_ptlib->GetLibAttr(&ptlibattr));

    /* release library attributes */
    g_ptlib->ReleaseTLibAttr(ptlibattr);

    /* Now add each of the names to the type list */
    utypeinfoCount = g_ptlib->GetTypeInfoCount();
    for(i = 0; i < utypeinfoCount; i++){
      CHECKRESULT(g_ptlib->GetDocumentation(i, &bstrName, NULL, NULL, NULL));  
      ASSERT(bstrName);
      XAddDlgItemList(IDC_TYPELIST, bstrName);
      SysFreeString(bstrName);
    }
}

#ifdef WIN32
void
OpenTypeLib(char FAR *sztlib)
{                                     
  OLECHAR buf[MAX_PATH];

  MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, 
	              sztlib, -1, buf, MAX_PATH);
  OpenTypeLib(buf);
}
#endif


/***
*void SetSelectedType
*Purpose:
*  When the user changes the selection of a type, this function updates the
*  dialog by changing the member list and the help for the type. It also sets
*  g_ptinfoCur to refer to the typeinfo.
*
*Entry:
*  dwIndex =
*
*Exit:
*  return value = None
*
***********************************************************************/
void
SetSelectedType(unsigned long dwIndex)
{
    HRESULT hr;
    BSTR bstrDoc;
    OLECHAR FAR* psz;
    OLECHAR szBuf[40];
    unsigned long dwHelpContext;
    TYPEKIND tkind;

    if(g_ptinfoCur != NULL){
      g_ptinfoCur->ReleaseTypeAttr(g_ptattrCur);
      g_ptinfoCur->Release();
    }

    /* Clear out the member list */
    XClrDlgItemList(IDC_MEMBERLIST);

    if(g_ptlib == NULL)
      return;

    // Note: the index in the list box is conveniently the same as the
    // one to pass to GetTypeInfo/GetTypeInfoType

    // typeinfo type can be obtained without actually loading the typeinfo
    CHECKRESULT(g_ptlib->GetTypeInfoType((unsigned int)dwIndex, &tkind));
    XSetDlgItemText(IDC_TYPEKIND, g_rgszTKind[tkind]);

    CHECKRESULT(g_ptlib->GetTypeInfo((unsigned int)dwIndex, &g_ptinfoCur));
    CHECKRESULT(g_ptinfoCur->GetTypeAttr(&g_ptattrCur));

    // GUID
    hr = StringFromCLSID(g_ptattrCur->guid, &psz);
    ASSERT(hr == NOERROR);
    XSetDlgItemText(IDC_GUID, psz);
    MemFree(psz);

    // Version
    SPRINTF(szBuf, OLESTR("%u.%02u"),
      g_ptattrCur->wMajorVerNum, g_ptattrCur->wMinorVerNum);
    XSetDlgItemText(IDC_VERSION, szBuf);
    
    CHECKRESULT(
      g_ptlib->GetDocumentation(
        (unsigned int)dwIndex, NULL, &bstrDoc, &dwHelpContext, NULL));

    // Help Context
    SPRINTF(szBuf, OLESTR("%ld"), dwHelpContext);
    XSetDlgItemText(IDC_HELPCONTEXT, szBuf);

    // Documentation string
    psz = (bstrDoc != NULL) ? bstrDoc : OLESTR("<none>");
    XSetDlgItemText(IDC_HELPSTRING, psz);
    SysFreeString(bstrDoc);
   
    FillMemberList(g_ptinfoCur, g_ptattrCur, IDC_MEMBERLIST); 

    XClrDlgItemList(IDC_PARAMLIST);
}

/***
*void FillMemberList
*Purpose:
* Sets the current typeinfo to the typeinfo indexed by dwIndex, and
* then fills in the list box with the members of the type.
*
*Entry:
*  ptinfo = 
*  ptypeattr =
*
*Exit:
*  return value = None
*
***********************************************************************/
void
FillMemberList(
    ITypeInfo FAR *ptinfo,
    TYPEATTR FAR *ptypeattr,
    int ctlid)
{
    MEMBERID memid; 
    BSTR bstrName;
    unsigned int i;
    FUNCDESC FAR *pfuncdesc;
    VARDESC  FAR *pvardesc;
	
    /* Now add all of the functions and all of the vars.
     * This is somewhat roundabout.
     * For each one, we need to get the funcdesc, or the vardesc.
     * From that we get the MEMBERID, and finally can get to the name.
     */
    for(i = 0; i < ptypeattr->cFuncs; i++){
      CHECKRESULT(ptinfo->GetFuncDesc(i, &pfuncdesc));
      memid = pfuncdesc->memid;
      CHECKRESULT(ptinfo->GetDocumentation(memid, &bstrName, NULL, NULL, NULL));
      ptinfo->ReleaseFuncDesc(pfuncdesc);
      pfuncdesc = NULL;
	    
      ASSERT(bstrName);				
      XAddDlgItemList(ctlid, bstrName);
      SysFreeString(bstrName);
    }
				    
    for(i = 0; i < ptypeattr->cVars; i++)
    {
      CHECKRESULT(ptinfo->GetVarDesc(i, &pvardesc));
      memid = pvardesc->memid;
      CHECKRESULT(ptinfo->GetDocumentation(memid, &bstrName, NULL, NULL, NULL));
      ptinfo->ReleaseVarDesc(pvardesc);
      pvardesc = NULL;
					
      ASSERT(bstrName);
      XAddDlgItemList(ctlid, bstrName);
      SysFreeString(bstrName);
    }
}

/***
*void SetSelectedMember
*Purpose:
*  When a member of a type is selected, update the help to be the help
*  of the member, and if the member is a function update the parameter
*  list to reflect that it is a function.
*
*Entry:
*  dwIndex =
*
*Exit:
*  return value = None
*
***********************************************************************/
void
SetSelectedMember(unsigned long dwIndex)
{
    MEMBERID memid;

    /* In any case, we'll need to clear out the parameter list. */
    XClrDlgItemList(IDC_PARAMLIST);

    if(g_ptattrCur == NULL)
      return;
	
    /* if this is a function, fill the param list, otherwise just fill
     * in the item info.
     */
    if(dwIndex < g_ptattrCur->cFuncs){
      unsigned short i;
      unsigned int cNames;
      FUNCDESC FAR *pfuncdesc;
      const unsigned int MAX_NAMES = 40;
      BSTR rgNames[MAX_NAMES];

      CHECKRESULT(g_ptinfoCur->GetFuncDesc((unsigned int) dwIndex, &pfuncdesc));
      memid = pfuncdesc->memid;
      UpdateMemberInfo(memid);
    
      CHECKRESULT(g_ptinfoCur->GetNames(memid, rgNames, MAX_NAMES,&cNames));
      for(i = 1; i < cNames; i++){
	ASSERT(rgNames[i])
	XAddDlgItemList(IDC_PARAMLIST, rgNames[i]);
	SysFreeString(rgNames[i]);
      }
      ASSERT(rgNames[0]);	          
      SysFreeString(rgNames[0]);
      g_ptinfoCur->ReleaseFuncDesc(pfuncdesc);
    }
    else
    {
      VARDESC FAR *pvardesc;

      CHECKRESULT(
        g_ptinfoCur->GetVarDesc(
	  (unsigned int)(dwIndex - g_ptattrCur->cFuncs), &pvardesc));
      memid = pvardesc->memid;
      UpdateMemberInfo(memid);
      g_ptinfoCur->ReleaseVarDesc(pvardesc);
    }
}

/***
*void UpdateMemberInfo
*Purpose:
*  sets fields on the dialog (such as help string and help context) from
*  the type information.
*
*Entry:
*  memid =
*
*Exit:
*  return value = None
*
***********************************************************************/
void
UpdateMemberInfo(MEMBERID memid)                   
{
    BSTR bstrDoc;
    OLECHAR buf[40];                    
    unsigned long dwHelpContext;

    /* get the member information */
    CHECKRESULT(g_ptinfoCur->GetDocumentation(
      memid, NULL, &bstrDoc, &dwHelpContext, NULL));

    /* update the help string displayed in the dialog */
    XSetDlgItemText(IDC_HELPSTRING, (bstrDoc != NULL) ? 
	                             bstrDoc : OLESTR("<none>"));
    SysFreeString(bstrDoc);
        
    /* update the help context displayed in the dialog */
    SPRINTF(buf, OLESTR("%ld"), dwHelpContext);
    XSetDlgItemText(IDC_HELPCONTEXT, buf);
}

/***
*void SetSelectedParam
*Purpose:
*  CONSIDER: Enhance to show parameter type information here.
*
*Entry:
*  dwIndex =
*
*Exit:
*  return value = None
*
***********************************************************************/
void
SetSelectedParam(unsigned long dwIndex)
{
}


HRESULT
GetFileName(BSTR FAR* pbstrFileName)
{
    OLECHAR buf[1024];
    BSTR bstrFileName;

#ifdef _MAC

    int i;
    Point pt;
    long dirID;
    char *p, *q;
    SFReply sfr;
    SFTypeList sftl;
    short sRefNumReal;

    SetPt(&pt, 100, 100);
    //sftl[0] = 'OTLB';
    sftl[0] = 0; // currently allow all file types
    SFGetFile(pt, NULL, NULL, -1, sftl, NULL, &sfr);

    if(!sfr.good)
      return ResultFromScode(E_FAIL);

    // translate sfr.vRefNum into a dirID
    {
	WDPBRec wdpbr;

	wdpbr.ioWDIndex   = 0;
	wdpbr.ioVRefNum   = sfr.vRefNum;
	wdpbr.ioWDVRefNum = 0;
	wdpbr.ioWDIndex   = 0;
	wdpbr.ioWDProcID  = 0;
	wdpbr.ioNamePtr   = (StringPtr)buf;

	if(PBGetWDInfoSync (&wdpbr) != noErr)
	  return ResultFromScode(E_FAIL);

	sRefNumReal = wdpbr.ioWDVRefNum;
	dirID = wdpbr.ioWDDirID;
    }

    {
	Str255 stDir;
	CInfoPBRec pb;
	DirInfo *pdirinfo = (DirInfo*)&pb;
	char rgchTemp[256]; // REVIEW: what should the size be?

	pdirinfo->ioNamePtr = stDir;
	pdirinfo->ioVRefNum = sRefNumReal;
	pdirinfo->ioDrParID = dirID;
	pdirinfo->ioFDirIndex = -1;  // get info on a dir

	buf[0] = '\0';

	// loop until we hit the root dir
	do{
	  pdirinfo->ioDrDirID = pdirinfo->ioDrParID;
	  if(PBGetCatInfoSync (&pb) != noErr)
	    return ResultFromScode(E_FAIL);
	  strncpy(rgchTemp, (char*)stDir, 64);
	  p2cstr((StringPtr)rgchTemp);
	  strcat(rgchTemp, ":");
	  strcat(rgchTemp, buf);
	  strcpy(buf, rgchTemp);
	}while(pdirinfo->ioDrDirID != fsRtDirID);

    }

    q = (char*)sfr.fName;
    i = (int)*q++;
    p = &buf[strlen(buf)];
    while(i-->0)
      *p++ = *q++;
    *p = '\0';

    if((bstrFileName = SysAllocString(buf)) == NULL)
      return ResultFromScode(E_OUTOFMEMORY);

#else

    OPENFILENAME ofn;                         

    memset(&ofn, 0, sizeof(OPENFILENAME));
    ofn.lStructSize  = sizeof(OPENFILENAME);
    ofn.hwndOwner    = g_hwnd;
    ofn.lpstrFile    = (LPTSTR) &buf;
    ofn.nMaxFile     = sizeof(buf);
    *buf = OLESTR('\0');
    ofn.lpstrFilter  = TSTR("Type Libraries\0*.tlb\0\0");
    ofn.nFilterIndex = 1; 
    ofn.Flags        = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_SHAREAWARE;
    if(GetOpenFileName(&ofn) == 0){ 
      // REVIEW: we can probably do something smarter with this error
      DWORD dwerr = CommDlgExtendedError();
      return ResultFromScode(E_FAIL);
    }
#if defined(WIN32) && !defined(UNICODE)
    OLECHAR szFileW[_MAX_PATH];
    MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED,
			ofn.lpstrFile, -1, szFileW, _MAX_PATH);
    if((bstrFileName = SysAllocString(szFileW)) == NULL)
      return ResultFromScode(E_OUTOFMEMORY);
#else
    if((bstrFileName = SysAllocString(ofn.lpstrFile)) == NULL)
      return ResultFromScode(E_OUTOFMEMORY);
#endif

#endif

    *pbstrFileName = bstrFileName;
    return NOERROR;
}

void
AssertFail(char FAR* file, int line)
{	
    // Ansi Output for Assertion
    #ifdef UNICODE
      #define OUTPUTDEBUGSTRING	OutputDebugStringA
      #define MESSAGEBOX	MessageBoxA
    #else
      #define OUTPUTDEBUGSTRING	OutputDebugString
      #define MESSAGEBOX	MessageBox
    #endif	  
	
    char buf[128];

    sprintf(buf, "Assertion failed: %s(%d)", file, line);

#ifdef _MAC
    DebugStr(c2pstr(buf));
#else
    OUTPUTDEBUGSTRING(buf);
    if(MESSAGEBOX(NULL, buf, 
	  "TiBrowse Assertion.  OK to continue, CANCEL to quit.",
	  MB_OKCANCEL | MB_TASKMODAL) == IDCANCEL)
      exit(1);
    DebugBreak();
#endif
}

#ifdef _MAC /* { */

void Init(void);
void EventLoop(void);

void AdjustMenus(void);
void DoEvent(EventRecord *pevent);
void DoMenuCommand(long menuResult);
void Quit(void);
#ifndef _MSC_VER
#ifndef ConstStr255Param
#define ConstStr255Param StringPtr
#endif
#endif
void Fatal(ConstStr255Param);

Boolean
IsAppWindow(WindowPtr window)
{
    return (window == NULL)
      ? false : ((WindowPeek)window)->windowKind == userKind;
}

Boolean
IsDAWindow(WindowPtr window)
{
    return (window == NULL)
      ? false : (((WindowPeek)window)->windowKind < 0);
}

void
main()
{
    Init();
//    ShowWindow(g_pdlg); // REVIEW: necessarry?
    EventLoop();
}

void
DoDialogEvent(EventRecord *pevent, short sItem)
{
    Point pt;
    GrafPtr pport;
    ListHandle hlist;

    switch(sItem){
    case IDC_TYPELIST:
    case IDC_MEMBERLIST:
    case IDC_PARAMLIST:
      if(pevent->what == mouseDown){
	GetPort(&pport);
	SetPort(g_pdlg);
        GlobalToLocal(&pevent->where);
	hlist = g_rghlist[sItem];
        LClick(pevent->where, pevent->modifiers, hlist);
	SetPt(&pt, 0, 0);
	if(LGetSelect(true, &pt, hlist)){
	  switch(sItem){
	  case IDC_TYPELIST:
	    SetSelectedType(pt.v);
	    break;
	  case IDC_MEMBERLIST:
	    SetSelectedMember(pt.v);
	    break;
	  case IDC_PARAMLIST:
	    SetSelectedParam(pt.v);
	    break;
	  }
	}
	SetPort(pport);
      }
      break;
    }
}

void
EventLoop()
{
    short sItem;
    DialogPtr pdlg;
    EventRecord	event;
    RgnHandle cursorRgn;

    cursorRgn = NewRgn();
    while(1){
      if(WaitNextEvent(everyEvent, &event, MAXLONG, cursorRgn)){
	if (FrontWindow() != NULL
	 && event.what != diskEvt
	 && (event.what != keyDown || (event.modifiers & cmdKey) == 0)
	 && IsDialogEvent(&event))
	{
	  if(DialogSelect(&event, &pdlg, &sItem)){
	    ASSERT(pdlg == g_pdlg);
	    DoDialogEvent(&event, sItem);
	  }
	}else{
	  DoEvent(&event);
	}
      }
    }
}

void
DoEvent(EventRecord *pevent)
{
    char key;
    short part;
    WindowPtr window;

    switch(pevent->what){
    case mouseDown:
      part = FindWindow(pevent->where, &window);
      switch(part){
      case inMenuBar:
	AdjustMenus();
	DoMenuCommand(MenuSelect(pevent->where));
	break;

      case inSysWindow:	/* let the system handle the mouseDown */
	SystemClick(pevent, window);
	break;

      case inContent:
	if(window != FrontWindow()){
	  SelectWindow(window);
	}
	break;

      case inDrag:
	DragWindow(window, pevent->where, &qd.screenBits.bounds);
	break;
      }
      break;

    case keyDown:
    case autoKey:			/* check for menukey equivalents */
      key = (char)(pevent->message & charCodeMask);
      if(pevent->modifiers & cmdKey){	/* Command key down */
	if(pevent->what == keyDown){
	  /* enable/disable/check menu items properly */
	  AdjustMenus();
	  DoMenuCommand(MenuKey(key));
	}
      }
      break;
    }
}

void
Enable(MenuHandle hmenu, short sItem, Boolean fEnable)
{
    if(fEnable)
      EnableItem(hmenu, sItem);
    else
      DisableItem(hmenu, sItem);
}

void
AdjustMenus()
{
    Boolean fIsDA;
    MenuHandle hmenu;

    fIsDA = IsDAWindow(FrontWindow());

    /* we can allow desk accessories to be closed from the menu */
    hmenu = GetMHandle(mFile);
    Enable(hmenu, iClose, fIsDA);

    hmenu = GetMHandle(mEdit);
    Enable(hmenu, iUndo,  fIsDA);
    Enable(hmenu, iCut,   fIsDA);
    Enable(hmenu, iCopy,  fIsDA);
    Enable(hmenu, iPaste, fIsDA);
    Enable(hmenu, iClear, fIsDA);
}

void
DoMenuCommand(long menuResult)
{
    short menuID;		/* the resource ID of the selected menu */
    short menuItem;		/* the item number of the selected menu */
    Str255 daName;

    menuID = HiWord(menuResult);
    menuItem = LoWord(menuResult);

    switch(menuID){
    case mApple:
      switch(menuItem){
      case iAbout:		/* bring up alert for About */
	Alert(rAboutAlert, NULL);
	break;
      default:
	GetMenuItemText(GetMHandle(mApple), menuItem, daName);
	OpenDeskAcc(daName);
	break;
      }
      break;

    case mFile:
      switch(menuItem){
      case iOpen:
    {
        BSTR bstrFileName;
        if(GetFileName(&bstrFileName) != NOERROR)
	{
	  // REVIEW: shouldnt just bag out here
	  Fatal((ConstStr255Param)"\pUnable to open Type Library");
        }
        OpenTypeLib(bstrFileName);
    }
	break;

      case iQuit:
	Quit();
	break;
      }
      break;

    case mEdit:
      SystemEdit(menuItem-1);
      break;
    }

    HiliteMenu(0);
}

extern "C" {

#if defined(_MSC_VER)
void pascal
#else
pascal void
#endif
DrawListbox(DialogPtr pdlg, short sItem)
{
    Rect rc;
    Handle h;
    short kind;
    GrafPtr port;
    ListHandle hlist;

    ASSERT(sItem == IDC_TYPELIST
	|| sItem == IDC_MEMBERLIST
	|| sItem == IDC_PARAMLIST);

    GetPort(&port);
    SetPort(pdlg);
    hlist = g_rghlist[sItem];
    LUpdate(pdlg->visRgn, hlist);
    GetDItem(pdlg, sItem, &kind, &h, &rc);
    InsetRect(&rc, -1, -1);
    FrameRect(&rc);
    SetPort(port);
}

}


/***
*ListHandle CreateListBox(DialogPtr, int)
*Purpose:
*  Create a listbox on the given dialog, associated with the given
*  control id.
*
*Entry:
*  pdlg =
*  ctlid =
*
*Exit:
*  return value = ListHandle
*
***********************************************************************/
ListHandle
CreateListbox(DialogPtr pdlg, int ctlid)
{
    Handle h;
    short kind;
    Point ptCell;
    ListHandle hlist;
    Rect rcView, rcBounds;

    INIT_ROUTINE_DESC(NewUserItemProc,DrawListbox);

    GetDItem(pdlg, ctlid, &kind, &h, &rcView);
    SetDItem(pdlg, ctlid, kind, (Handle) ROUTINE_DESC(DrawListbox), &rcView);

    // make room for scroll bars
    rcView.right -= 15;

    // list is 1 wide
    SetRect(&rcBounds, 0, 0, 1, 0);

    SetPt(&ptCell, 0, 0);

    hlist = LNew(
      &rcView,
      &rcBounds,
      ptCell,
      0, (WindowPtr)pdlg, true, false, false, true);

    ASSERT(hlist != NULL);

    (*hlist)->selFlags = lUseSense | lNoRect | lNoExtend | lOnlyOne;

    return hlist;
}

/***
*DialogPtr CreateDialog(void)
*Purpose:
*  Create the TiBrowse dialog
*
*Entry:
*  None
*
*Exit:
*  return value = DialogPtr
*
***********************************************************************/
DialogPtr
CreateDialog()
{
    int i;
    Point pt;
    DialogPtr pdlg;
    ListHandle hlist;
static short rgctl[] = {IDC_TYPELIST, IDC_MEMBERLIST, IDC_PARAMLIST};

    if((pdlg = GetNewDialog(rDlg, NULL, (WindowPtr)-1)) == NULL)
      return NULL;

    for(i = 0; i < DIM(rgctl); ++i){
      g_rghlist[rgctl[i]]  = CreateListbox(pdlg, rgctl[i]);
    }

    SetPt(&pt, 0, 0);
    for(i = 0; i < DIM(rgctl); ++i){
      hlist = g_rghlist[rgctl[i]];
      LSetSelect(true, pt, hlist);
      LDoDraw(true, hlist);
    }

    return pdlg;
}


void
Init()
{
    Handle menuBar;

    MaxApplZone();

    InitGraf((Ptr)&qd.thePort);
    InitFonts();
    InitWindows();
    InitMenus();
    TEInit();
    InitDialogs(NULL);
    InitCursor();
    FlushEvents(everyEvent, 0);

#ifndef _PPCMAC
    if (InitOleManager(0) != NOERROR)
      Fatal((ConstStr255Param)"\pCould not initialize OLE Applet");
#endif

    if(OleInitialize(NULL) != NOERROR)
      Fatal((ConstStr255Param)"\pUnable to Initialize Ole");

    if((menuBar = GetNewMBar(rMenuBar)) == NULL)
      Fatal((ConstStr255Param)"\pUnable to load menu bar");
    SetMenuBar(menuBar);
    DisposHandle(menuBar);
    AddResMenu(GetMHandle(mApple), 'DRVR');
    DrawMenuBar();

    if((g_pdlg = CreateDialog()) == NULL)
      Fatal((ConstStr255Param)"\pUnable to create dialog");
}

void
Quit()
{
    Cleanup();
    Uninit();
    ExitToShell();
}

/* display fatal error alert, and exit */
void
Fatal(ConstStr255Param msg)
{
    SetCursor(&qd.arrow);
    ParamText(msg, (ConstStr255Param)"\p", (ConstStr255Param)"\p", (ConstStr255Param)"\p");
    Alert(rUserAlert, nil);
    Quit();
}

#else /* }{ */

long FAR PASCAL EXPORT WndProc (HWND, UINT, WPARAM, LPARAM) ;

int PASCAL
WinMain(
    HANDLE hinst,
    HANDLE hinstPrev,
    LPSTR lpszCmdLine,
    int nCmdShow)
{
    MSG msg;
    WNDCLASS wndclass;
 
    if(!hinstPrev){
      wndclass.style          = CS_HREDRAW | CS_VREDRAW;
      wndclass.lpfnWndProc    = WndProc ;
      wndclass.cbClsExtra     = 0 ;
      wndclass.cbWndExtra     = DLGWINDOWEXTRA ;
      wndclass.hInstance      = hinst ;
      wndclass.hIcon          = LoadIcon (hinst, g_szAppName) ;
      wndclass.hCursor        = LoadCursor (NULL, IDC_ARROW) ;
      wndclass.hbrBackground  = (HBRUSH) (COLOR_WINDOW + 1) ;
      wndclass.lpszMenuName   = NULL ;
      wndclass.lpszClassName  = g_szAppName ;

      RegisterClass (&wndclass) ;
    }
       
    if(OleInitialize(NULL) != NOERROR){
      MessageBox(NULL, TSTR("unable to initialize Ole"),
	         g_szAppName, MB_OK);
      return 0;
    }
     
    g_hwnd = CreateDialog(hinst, g_szAppName, 0, NULL);

    if(*lpszCmdLine == '\0'){
      BSTR bstrFileName;
      if(GetFileName(&bstrFileName) != NOERROR){
	Cleanup();
	Uninit();
	exit(-1);
      }
      OpenTypeLib(bstrFileName);
      SysFreeString(bstrFileName);
    }else{
      OpenTypeLib(lpszCmdLine);
    }
      
    ShowWindow(g_hwnd, nCmdShow);

    while(GetMessage (&msg, NULL, 0, 0)){
      TranslateMessage (&msg);
      DispatchMessage (&msg);
    }
    Cleanup();
    Uninit();
    return msg.wParam;
}

long FAR PASCAL EXPORT
WndProc(
    HWND hwnd,
    UINT message,
    WPARAM wParam,
    LPARAM lParam)
{
    unsigned long dwIndex;

    switch(message){
    case WM_COMMAND: 
      /* We deal with the following events:
       * The selection changes on the type list and we have
       *  to update the member list & type info.
       * The selection changes on the member list and we have
       *  to update the param list & member info.
       * Selection changes on a parameter and we have to
       *  update the param info.
       */
#ifdef WIN32
# define wParamX LOWORD(wParam)
# define notifyMsg HIWORD(wParam)
#else
# define wParamX wParam
# define notifyMsg HIWORD(lParam)
#endif
      switch(wParamX){
      case IDC_TYPELIST:
        if(notifyMsg == LBN_SELCHANGE){
          dwIndex = SendDlgItemMessage(hwnd, IDC_TYPELIST, LB_GETCURSEL, 0, 0L);
	  if(dwIndex != LB_ERR)
	    SetSelectedType(dwIndex);
	}
	break;
      case IDC_MEMBERLIST:
        if(notifyMsg == LBN_SELCHANGE){
          dwIndex = SendDlgItemMessage(hwnd, IDC_MEMBERLIST, LB_GETCURSEL,0,0L);
	  if(dwIndex != LB_ERR)
	    SetSelectedMember(dwIndex);
	}
        break;
      case IDC_PARAMLIST:
        if(notifyMsg == LBN_SELCHANGE){
          dwIndex = SendDlgItemMessage(hwnd, IDC_PARAMLIST, LB_GETCURSEL, 0,0L);
	  if(dwIndex != LB_ERR)
	    SetSelectedParam(dwIndex);
	}
	break;
      }
      return 0;

    case WM_DESTROY:
      PostQuitMessage(0);
      return 0 ;
    }
    return DefWindowProc (hwnd, message, wParam, lParam) ;
}

#endif /* } */

void
MethodError(HRESULT hresult)
{
    /* CONSIDER: add code to figure out what specific error this is */
    UNUSED(hresult);

#ifdef _MAC
    Fatal((ConstStr255Param)"\pType Library");
#else
    MessageBox(NULL, TSTR("Error returned from TYPELIB.DLL"),
	       g_szAppName, MB_OK);
    exit(1);
#endif
}

/* free using the task allocator */
void
MemFree(void FAR* pv)
{
    HRESULT hresult;
    IMalloc FAR* pmalloc;

    hresult = CoGetMalloc(MEMCTX_TASK, &pmalloc);

    if(hresult != NOERROR){
#ifdef _MAC
      Fatal((ConstStr255Param)"\pError accessing task allocator");
#else
      MessageBox(NULL, TSTR("Error accessing task allocator"),
	         g_szAppName, MB_OK);
#endif
      return;
    }

    pmalloc->Free(pv);
    pmalloc->Release();
}

