home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 1997 May / Pcwk0597.iso / borland / cb / setup / cbuilder / data.z / DIROUTLN.CPP < prev    next >
C/C++ Source or Header  |  1997-02-28  |  12KB  |  396 lines

  1. //---------------------------------------------------------------------------
  2. // Borland C++Builder
  3. // Copyright (c) 1987, 1997 Borland International Inc.  All Rights Reserved.
  4. //---------------------------------------------------------------------------
  5. #pragma hdrstop          //This header has some initialized data so we can't use
  6.                          //pre compiled headers.
  7. #if !defined (REGISTER_ALL_CONTROLS)
  8.   #include  "diroutln.h"
  9. #else
  10.   #include "source\diroutln.h"
  11. #endif
  12.  
  13. #include <ctype.h>
  14. #include <dir.h>
  15.  
  16. //#pragma resource "*.res"    //IDE links .res automatically for components
  17.  
  18. //
  19. // definitions of static data members
  20. // ----------------------------------
  21. //
  22. const TDirectoryOutline::InvalidIndex;
  23. const TDirectoryOutline::RootIndex;
  24. void *TDirectoryOutline::APointer = &TDirectoryOutline::APointer;
  25.  
  26. //
  27. // inline helpers
  28. // --------------
  29. //
  30.  
  31. inline bool SameLetter(char a, char b)
  32. {
  33.     return toupper(a) == toupper(b);
  34. }
  35.  
  36. inline int GetPos(const AnsiString& s, const AnsiString& p)
  37. {
  38. #ifndef _MBCS
  39.     return s.Pos(p);
  40. #else
  41.     return s.AnsiPos(p);
  42. #endif
  43. }
  44.  
  45. //---------------------------------------------------------------------------
  46. static inline TDirectoryOutline *ValidCtrCheck()
  47. {
  48.     return new TDirectoryOutline(NULL);
  49. }
  50. //---------------------------------------------------------------------------
  51. __fastcall TDirectoryOutline::TDirectoryOutline(TComponent* Owner)
  52.     : TCustomOutline(Owner),
  53.       PictureLeaf(PictureClosed),
  54.       FTextCase(tcLowerCase)
  55. {
  56.     OutlineStyle = osTreePictureText;
  57.     Options = TOutlineOptions() << ooStretchBitmaps << ooDrawFocusRect;
  58.     PictureLeaf = PictureClosed;
  59.     AssignCaseProc();
  60. }
  61. //---------------------------------------------------------------------------
  62. inline int __fastcall TDirectoryOutline::DriveToInt(char d)
  63. {
  64.     // this assumes that A..Z are contiguous and packed
  65.     return toupper(d) - 'A';
  66. }
  67. //---------------------------------------------------------------------------
  68. inline char __fastcall TDirectoryOutline::IntToDrive(int i)
  69. {
  70.     // this assumes that A..Z are contiguous and packed
  71.     return static_cast<char>(i + 'A');
  72. }
  73. //---------------------------------------------------------------------------
  74. void __fastcall TDirectoryOutline::AssignCaseProc()
  75. {
  76.     switch (TextCase) {
  77. #ifndef _MBCS
  78.         case tcLowerCase:
  79.             FCaseFunction = AnsiLowerCase;
  80.             break;
  81.             
  82.         case tcUpperCase:
  83.             FCaseFunction = AnsiUpperCase;
  84.             break;
  85. #else
  86.         case tcLowerCase:
  87.             FCaseFunction = AnsiLowerCaseFileName;
  88.             break;
  89.             
  90.         case tcUpperCase:
  91.             FCaseFunction = AnsiUpperCaseFileName;
  92.             break;
  93. #endif
  94.         default:
  95.             FCaseFunction = NULL;
  96.             break;
  97.     }
  98. }
  99. //---------------------------------------------------------------------------
  100.  
  101. // helper class for exception safety that wraps FindFirst/FindNext/FindClose
  102. namespace {
  103.     class FileFind {
  104.         private:
  105.             TSearchRec FSRec;
  106.             int FError;
  107.             bool IsOk()                      { return FError == 0; }
  108.  
  109.         public:
  110.             FileFind(const AnsiString &Path, int Attr)
  111.             {
  112.                 FError = FindFirst(Path, Attr, FSRec);
  113.             }
  114.  
  115.             ~FileFind()                      { FindClose(FSRec); }
  116.  
  117.             __property bool Ok             = { read = IsOk };
  118.             __property int Error           = { read = FError };
  119.             __property TSearchRec SRec     = { read = GetSRec };
  120.  
  121.             TSearchRec &GetSRec()            { return FSRec; }
  122.             operator bool()                  { return Ok; }
  123.  
  124.             bool Next()
  125.             {
  126.                 FError = FindNext(FSRec);
  127.                 return Ok;
  128.             }
  129.     };
  130. };
  131. //---------------------------------------------------------------------------
  132.  
  133. void __fastcall TDirectoryOutline::BuildOneLevel(long RootItem)
  134. {
  135.     TOutlineNode *RootNode = Items[RootItem];
  136.     AnsiString RootName = RootNode->FullPath;
  137.  
  138. #ifndef _MBCS
  139.     if (RootName[RootName.Length()] != '\\')
  140. #else
  141.     if (!RootName.IsPathDelimiter(RootName.Length()))
  142. #endif
  143.         RootName += "\\";
  144.     RootName += "*.*";
  145.  
  146.     for (FileFind Find(RootName, faDirectory); Find.Ok; Find.Next()) {
  147.         TSearchRec &SRec = Find.GetSRec();
  148.  
  149.         // only store directories, ignoring "." and ".."
  150.         if ((SRec.Attr & faDirectory) && SRec.Name[1] != '.') {
  151.             AnsiString Name = ForceCase(SRec.Name);
  152.  
  153.             if (RootNode->HasItems) { // insert in sorted order
  154.                 long Child = RootNode->getFirstChild();
  155.                 while (Child != InvalidIndex &&
  156.                         Items[Child]->Text.AnsiCompareIC(Name) < 0)
  157.                     Child = RootNode->GetNextChild(Child);
  158.  
  159.                 if (Child != InvalidIndex)
  160.                     Insert(Child, Name);
  161.                 else
  162.                     Add(RootNode->GetLastChild(), Name);
  163.             }
  164.             else
  165.                 AddChild(RootItem, Name);
  166.         }
  167.     }
  168.     // mark this node with an arbitrary address so we can tell we've
  169.     // already been here
  170.     Items[RootItem]->Data = APointer;
  171. }
  172. //---------------------------------------------------------------------------
  173. void __fastcall TDirectoryOutline::BuildTree()
  174. {
  175.     Clear();
  176.     AddChild(0, ForceCase(AnsiString(Drive) + ":"));
  177.     WalkTree(FDirectory);
  178.     Change();
  179. }
  180. //---------------------------------------------------------------------------
  181. void __fastcall TDirectoryOutline::BuildSubTree(long RootItem)
  182. {
  183.     BuildOneLevel(RootItem);
  184.     TOutlineNode *RootNode = Items[RootItem];
  185.  
  186.     for (long TempRoot = RootNode->getFirstChild();
  187.             TempRoot != InvalidIndex; 
  188.             TempRoot = RootNode->GetNextChild(TempRoot))
  189.         BuildSubTree(TempRoot);
  190. }
  191. //---------------------------------------------------------------------------
  192. void __fastcall TDirectoryOutline::Change()
  193. {
  194.     if (FOnChange)
  195.         FOnChange(this);
  196. }
  197. //---------------------------------------------------------------------------
  198. void __fastcall TDirectoryOutline::Click()
  199. {
  200.     TCustomOutline::Click();
  201.     Directory = Items[SelectedItem]->FullPath;
  202. }
  203. //---------------------------------------------------------------------------
  204. void __fastcall TDirectoryOutline::CreateWnd()
  205. {
  206.     AnsiString CurrentPath;
  207.     
  208.     TCustomOutline::CreateWnd();
  209.     
  210.     if (FDrive == 0) {
  211.         AnsiString CurrentPath = ForceCase(CurDir());
  212.         if (CurrentPath.Length() != 0) {
  213.             FDrive = CurrentPath[1];
  214.             FDirectory = CurrentPath;
  215.         }
  216.     }
  217.     
  218.     if (!ComponentState.Contains(csLoading))
  219.         BuildTree();
  220. }
  221. //---------------------------------------------------------------------------
  222. void __fastcall TDirectoryOutline::Expand(long Index)
  223. {
  224.     // check to see if we've already built this
  225.     if (!Items[Index]->Data)
  226.         BuildOneLevel(Index);
  227.     
  228.     // call the event handler
  229.     TCustomOutline::Expand(Index);
  230. }
  231. //---------------------------------------------------------------------------
  232. void __fastcall TDirectoryOutline::Loaded()
  233. {
  234.     TCustomOutline::Loaded();
  235.     AssignCaseProc();
  236.     BuildTree();
  237. }
  238.  
  239. //---------------------------------------------------------------------------
  240. AnsiString __fastcall TDirectoryOutline::ForceCase(const AnsiString &s)
  241. {
  242.     return FCaseFunction ? (*FCaseFunction)(s) : s;
  243. }
  244. //---------------------------------------------------------------------------
  245.  
  246. void __fastcall TDirectoryOutline::SetDirectory(const TFileName NewDir)
  247. {
  248.     if (NewDir.Length() > 0) {
  249.         TFileName Path = ForceCase(ExpandFileName(NewDir));
  250.         int n = Path.Length();
  251.  
  252. #ifndef _MBCS
  253.         if (n > 3 && Path[n] == '\\')
  254. #else
  255.         if (n > 3 && Path.IsPathDelimiter(n))
  256. #endif
  257.             Path.SetLength(n - 1);
  258.  
  259.         if (Path != FDirectory) {
  260.             FDirectory = Path;
  261.             chdir(FDirectory.c_str());
  262.  
  263.             if (!SameLetter(Path[1], Drive))
  264.                 Drive = Path[1];
  265.             else {
  266.                 WalkTree(Path);
  267.                 Change();
  268.             }
  269.         }
  270.     }
  271. }
  272. //---------------------------------------------------------------------------
  273. void __fastcall TDirectoryOutline::SetDrive(char NewDrive)
  274. {
  275.     // the original sample did not throw an exception here.
  276.     // should we do so now?
  277.     NewDrive = static_cast<char>(toupper(NewDrive));
  278.     if ((NewDrive >= 'A' && NewDrive <= 'Z')) {
  279.         if (!SameLetter(NewDrive, FDrive)) {
  280.             FDrive = NewDrive;
  281.             setdisk(DriveToInt(FDrive));
  282.             FDirectory = ForceCase(CurDir());
  283.             if (!ComponentState.Contains(csLoading)) 
  284.                 BuildTree();
  285.         }
  286.     }
  287. }
  288.  
  289. //---------------------------------------------------------------------------
  290. void __fastcall TDirectoryOutline::SetTextCase(TTextCase NewCase)
  291. {
  292.     if (NewCase != FTextCase) {
  293.         FTextCase = NewCase;
  294.         AssignCaseProc();
  295.         
  296.         if (NewCase == tcAsIs) {
  297.             AnsiString CurrentPath = CurDir();
  298.             FDrive = CurrentPath[1];
  299.             FDirectory = CurrentPath;
  300.         }
  301.         
  302.         if (!ComponentState.Contains(csLoading))
  303.             BuildTree();
  304.     }
  305. }
  306. //---------------------------------------------------------------------------
  307. AnsiString TDirectoryOutline::CurDir()
  308. {
  309.     const initbuf = 256;
  310.     size_t buflen = initbuf;
  311.     char *buf = reinterpret_cast<char *>(malloc(buflen));
  312.     
  313.     if (buf) {
  314.         buflen = GetCurrentDirectory(buflen, buf);
  315.         if (buflen > initbuf) {
  316.             char *p = reinterpret_cast<char *>(realloc(buf, buflen));
  317.             if (p) {
  318.                 buf = p;
  319.                 buflen = GetCurrentDirectory(buflen, buf);
  320.             }
  321.         }
  322.     }
  323.     
  324.     try {
  325.         AnsiString Result((buflen && buf) ? buf : "");
  326.         free(buf);
  327.         return Result;
  328.     } catch (...) {
  329.         free(buf);
  330.         throw;
  331.     }
  332. }
  333. //---------------------------------------------------------------------------
  334. long __fastcall TDirectoryOutline::GetChildNamed(const AnsiString& Name,
  335.         long Item)
  336. {
  337.  
  338.     TOutlineNode* Node = Items[Item];
  339.  
  340.     Node->Expanded = true;
  341.     long rc = Node->getFirstChild();
  342.  
  343.     while (rc != InvalidIndex && Items[rc]->Text != Name)
  344.         rc = Node->GetNextChild(rc);
  345.  
  346.     return rc;
  347. }
  348.  
  349. //---------------------------------------------------------------------------
  350.  
  351. void __fastcall TDirectoryOutline::WalkTree(const AnsiString& Dest)
  352. {
  353.     AnsiString Path = ForceCase(Dest);
  354.     long Item = RootIndex;  // start at root
  355.  
  356.  
  357.     // remove drive component of Path
  358.  
  359.     int Colon = Path.Pos(":");  // do we care about MBCS here?
  360.     if (Colon > 0) {
  361.         int Offset = (Path[Colon + 1] == '\\') ? 2 : 1;
  362.         Path = Path.SubString(Colon + Offset, Path.Length());
  363.     }
  364.  
  365.     // do the walk
  366.     for (int SlashPos = GetPos(Path, "\\"); Path.Length() > 0;
  367.             SlashPos = GetPos(Path, "\\")) {
  368.         AnsiString Dir = Path;
  369.  
  370.         if (SlashPos > 0) {
  371.             // splice out the first directory
  372.             Dir = Path.SubString(1, SlashPos - 1);
  373.             Path = Path.SubString(SlashPos + 1, Path.Length());
  374.         }
  375.         else {
  376.             Dir = Path;
  377.             Path = "";
  378.         }
  379.  
  380.         Item = GetChildNamed(Dir, Item);
  381.     }
  382.  
  383.     SelectedItem = Item;
  384. }
  385.             
  386. //---------------------------------------------------------------------------
  387. namespace Diroutln
  388. {
  389.     void __fastcall Register()
  390.     {
  391.         TComponentClass classes[1] = {__classid(TDirectoryOutline)};
  392.         RegisterComponents("Samples", classes, 0);
  393.     }
  394. }
  395. //---------------------------------------------------------------------------
  396.