//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "fPaths.h"
#include <Windows.hpp>
#include <Messages.hpp>
#include <ActiveX.hpp>
//#include <ShlObj.hpp>
//#include <shlobj.h>
#include <objidl.h>
#include <objbase.h>

#define BIF_RETURNONLYFSDIRS   0x0001  // For finding a folder to start document searching

DECLARE_DINTERFACE_TYPE(IShellFolder);
#define SID_IShellFolder "{000214E6-0000-0000-C000-000000000046}"

#ifndef SHSTDAPI
#if defined(_SHELL32_)
#define SHSTDAPI          STDAPI
#define SHSTDAPI_(type)   STDAPI_(type)
#else
#define SHSTDAPI          EXTERN_C DECLSPEC_IMPORT HRESULT STDAPICALLTYPE
#define SHSTDAPI_(type)   EXTERN_C DECLSPEC_IMPORT type STDAPICALLTYPE
#endif
#endif // SHSTDAPI

//===========================================================================
//
// Object identifiers in the explorer's name space (ItemID and IDList)
//
//  All the items that the user can browse with the explorer (such as files,
// directories, servers, work-groups, etc.) has an identifier which is unique
// among items within the parent folder. Those identifiers are called item
// IDs (SHITEMID). Since all its parent folders have their own item IDs,
// any items can be uniquely identified by a list of item IDs, which is called
// an ID list (ITEMIDLIST).
//
//  ID lists are almost always allocated by the task allocator (see some
// description below as well as OLE 2.0 SDK) and may be passed across
// some of shell interfaces (such as IShellFolder). Each item ID in an ID list
// is only meaningful to its parent folder (which has generated it), and all
// the clients must treat it as an opaque binary data except the first two
// bytes, which indicates the size of the item ID.
//
//  When a shell extension -- which implements the IShellFolder interace --
// generates an item ID, it may put any information in it, not only the data
// with that it needs to identifies the item, but also some additional
// information, which would help implementing some other functions efficiently.
// For example, the shell's IShellFolder implementation of file system items
// stores the primary (long) name of a file or a directory as the item
// identifier, but it also stores its alternative (short) name, size and date
// etc.
//
//  When an ID list is passed to one of shell APIs (such as SHGetPathFromIDList),
// it is always an absolute path -- relative from the root of the name space,
// which is the desktop folder. When an ID list is passed to one of IShellFolder
// member function, it is always a relative path from the folder (unless it
// is explicitly specified).
//
//===========================================================================

//
// SHITEMID -- Item ID
//
typedef struct _SHITEMID        // mkid
{
    USHORT      cb;             // Size of the ID (including cb itself)
    BYTE        abID[1];        // The item ID (variable length)
} SHITEMID;
typedef UNALIGNED SHITEMID *LPSHITEMID;
typedef const UNALIGNED SHITEMID *LPCSHITEMID;

//
// ITEMIDLIST -- List if item IDs (combined with 0-terminator)
//
typedef struct _ITEMIDLIST      // idl
{
    SHITEMID    mkid;
} ITEMIDLIST;
typedef UNALIGNED ITEMIDLIST * LPITEMIDLIST;
typedef const UNALIGNED ITEMIDLIST * LPCITEMIDLIST;

typedef int (CALLBACK* BFFCALLBACK)(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);

typedef struct _browseinfoA {
    HWND        hwndOwner;
    LPCITEMIDLIST pidlRoot;
    LPSTR        pszDisplayName;	// Return display name of item selected.
    LPCSTR       lpszTitle;			// text to go in the banner over the tree.
    UINT         ulFlags;			// Flags that control the return stuff
    BFFCALLBACK  lpfn;
    LPARAM       lParam;			// extra info that's passed back in callbacks
    int          iImage;			// output var: where to return the Image index.
} BROWSEINFOA, *PBROWSEINFOA, *LPBROWSEINFOA;

SHSTDAPI SHGetDesktopFolder(IShellFolder **ppshf);
SHSTDAPI_(LPITEMIDLIST) SHBrowseForFolderA(LPBROWSEINFOA lpbi);
SHSTDAPI_(BOOL) SHGetPathFromIDListA(LPCITEMIDLIST pidl, LPSTR pszPath);

      /**/
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfmPaths *fmPaths;
AnsiString sNoSHXPaths = "There are no AutoCAD SHX fonts paths";
//---------------------------------------------------------------------------
__fastcall TfmPaths::TfmPaths(TComponent* Owner)
  : TForm(Owner)
{
}
//---------------------------------------------------------------------------

bool DirExists(const AnsiString Name)
{
  int Code;

  Code = GetFileAttributes(Name.c_str());
  return ((Code != -1)&&((FILE_ATTRIBUTE_DIRECTORY)&&(Code != 0)));
}
//---------------------------------------------------------------------------

bool SelectDirectory(const AnsiString Caption, const WideString Root, AnsiString * Directory)
{
  int *WindowList;
  //TBrowseInfo *BrowseInfo;
  //BROWSEINFOA BrowseInfo;
  _browseinfoA BrowseInfo;
  char *Buffer;
  LPITEMIDLIST RootItemIDList, ItemIDList;
  IMalloc *ShellMalloc;
  IShellFolder *IDesktopFolder;
  #ifdef VER100
  int Eaten, Flags;
  #else
  unsigned long Eaten, Flags;
  #endif
  HWND ActiveWindow;
  bool Res;

  Res = false;
  *Directory = "";
  memset(&BrowseInfo, 0, sizeof(BrowseInfo));


  if ((CoGetMalloc(MEMCTX_TASK, &ShellMalloc) == S_OK) && (ShellMalloc != NULL))
  {
    Buffer = (char *)ShellMalloc->Alloc(MAX_PATH);
    try
    {
      /*
      RootItemIDList = NULL;
      if (Root != NULL)
      {
        SHGetDesktopFolder(&IDesktopFolder);
        IDesktopFolder->ParseDisplayName(NULL, NULL, Root, &Eaten,
          &RootItemIDList, &Flags);
      }
      */

      BrowseInfo.hwndOwner = 0;
      BrowseInfo.pidlRoot = RootItemIDList;
      BrowseInfo.pszDisplayName = Buffer;
      BrowseInfo.lpszTitle = "";
      BrowseInfo.ulFlags = BIF_RETURNONLYFSDIRS;

      ActiveWindow = GetActiveWindow();
      (void *)WindowList = DisableTaskWindows(0);
      try
      {
        ItemIDList = SHBrowseForFolderA(&BrowseInfo);
      }
      __finally
      {
        EnableTaskWindows(WindowList);
        SetActiveWindow(ActiveWindow);
      }
      Res = ItemIDList != NULL;
      if (Res)
      {
        SHGetPathFromIDListA(ItemIDList, Buffer);
        ShellMalloc->Free(ItemIDList);
        *Directory = Buffer;
      }
    }
    __finally
    {
      ShellMalloc->Free(Buffer);
    }
  }
  return Res;
}
//---------------------------------------------------------------------------

bool GetPathsExcute(AnsiString * APaths, bool AShowAddAutoCADPath)
{
  TfmPaths *fmPaths;
  int I, Pos;
  AnsiString S, S1;
  fmPaths = new TfmPaths(Application);
  try
  {
    S = *APaths;
    if (S != "")
    {
      if (S.c_str()[StrLen(S.c_str())] != ';') S = S + ";";
      Pos = 1;
      while(Pos == AnsiPos(";", S))
      {
        S1 = S;
        S1.Delete(Pos, S1.Length());
        S.Delete(1, Pos);
        if (S1.Length() != 0)
          fmPaths->lbPaths->Items->Add(S1);
      }
    }
    if (fmPaths->lbPaths->Items->Count == 0)
      fmPaths->lbPaths->ItemIndex = 0;
    fmPaths->FShowAddAutoCADPath = AShowAddAutoCADPath;
    fmPaths->ShowModal();
    if (fmPaths->ModalResult == ID_OK)
    {
      S = "";
      S1 = ";";
      for(I = 0; I < fmPaths->lbPaths->Items->Count; I++)
      {
        S += fmPaths->lbPaths->Items->Strings[I];
        S += S1;
      }
      *APaths = S;
    }
  }
  __finally
  {
    fmPaths->Free();
  }
  return (*APaths != "");
}
//---------------------------------------------------------------------------
void __fastcall TfmPaths::btnReplaceClick(TObject *Sender)
{
  if (lbPaths->ItemIndex < 0) return;
  lbPaths->Items->Strings[lbPaths->ItemIndex] = edPath->Text;
  btnReplace->Enabled = false;
  btnAdd->Enabled = false;
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::btnAddClick(TObject *Sender)
{
  int I = lbPaths->Items->Add(edPath->Text);
  lbPaths->ItemIndex = I;
  btnReplace->Enabled = false;
  btnAdd->Enabled = false;
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::btnDeleteClick(TObject *Sender)
{
  if (lbPaths->ItemIndex >= 0)
    lbPaths->Items->Delete(lbPaths->ItemIndex);
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::btndeInvPathsClick(TObject *Sender)
{
  TStringList *vStr = new TStringList;
  int I, Index;

  try
  {
    vStr->Assign(lbPaths->Items);
    for(I = 0; I < vStr->Count; I++)
      if (!DirExists(vStr->Strings[I]))
      {
        Index = lbPaths->Items->IndexOf(vStr->Strings[I]);
        if (Index >= 0) lbPaths->Items->Delete(Index);
      }
  }
  __finally
  {
    vStr->Free();
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::btnAddPathClick(TObject *Sender)
{
  AnsiString vDir = "";
  AnsiString vCaption = "";
  WideString vRoot = "";

  if (SelectDirectory(vCaption, vRoot, &vDir))
  {
    btnReplace->Enabled = true;
    btnAdd->Enabled = true;
    edPath->Text = vDir;
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::edPathChange(TObject *Sender)
{
  btnReplace->Enabled = true;
  btnAdd->Enabled = true;
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::btnAddAutoCADClick(TObject *Sender)
{

  TStringList *vPaths;
  int I;

  vPaths = new TStringList;
  try
  {
    if (FindAutoCADSHXPaths(vPaths))
    {
      for(I = 0; I < vPaths->Count; I++)
        lbPaths->Items->Add(vPaths->Strings[I]);
    }
    else
      ShowMessage(sNoSHXPaths);
  }
  __finally
  {
    vPaths->Free();
  }
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::UpDown(int Index)
{
  AnsiString S;
  S = lbPaths->Items->Strings[lbPaths->ItemIndex + Index];
  lbPaths->Items->Strings[lbPaths->ItemIndex + Index] = lbPaths->Items->Strings[lbPaths->ItemIndex];
  lbPaths->Items->Strings[lbPaths->ItemIndex] = S;
  lbPaths->ItemIndex = lbPaths->ItemIndex + Index;
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::btbtnDownClick(TObject *Sender)
{
  if((lbPaths->ItemIndex >= 0) && (lbPaths->ItemIndex < lbPaths->Items->Count - 1)&&
     (lbPaths->Items->Count > 0))
    UpDown(1);
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::bnbtnUpClick(TObject *Sender)
{
  if((lbPaths->ItemIndex >= 0) && (lbPaths->ItemIndex <= lbPaths->Items->Count - 1)&&
     (lbPaths->Items->Count > 0))
    UpDown(-1);  
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::lbPathsClick(TObject *Sender)
{
  if (lbPaths->ItemIndex < 0) return;
  edPath->Text = lbPaths->Items->Strings[lbPaths->ItemIndex];
  btnReplace->Enabled = false;
  btnAdd->Enabled = false;
}
//---------------------------------------------------------------------------

void __fastcall TfmPaths::lbPathsDrawItem(TWinControl *Control, int Index,
      TRect &Rect, TOwnerDrawState State)
{
  lbPaths->Canvas->Brush->Color = lbPaths->Color;
  lbPaths->Canvas->FillRect(Rect);
  lbPaths->Canvas->Brush->Color = lbPaths->Color;


  if (State.Contains(odSelected))
  {
    lbPaths->Canvas->Brush->Color = clHighlight;
    lbPaths->Canvas->Font->Color = clHighlightText;
  }
  else
  {
    lbPaths->Canvas->Brush->Color = lbPaths->Color;
    if (!DirExists(lbPaths->Items->Strings[Index]))
      lbPaths->Canvas->Font->Color = clGray;
    else
      lbPaths->Canvas->Font->Color = clBlack;
  }
  lbPaths->Canvas->FillRect(Rect);
  lbPaths->Canvas->TextOut(Rect.Left, Rect.Top, lbPaths->Items->Strings[Index]);
}
//---------------------------------------------------------------------------


