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