17. Správce souborů I
  1. V této kapitole se budeme zabývat postupným vývojem aplikace jednoduchého správce souborů. Vývoj naši aplikace budeme provádět v těchto krocích: Navrhneme formulář správce souborů, propojíme ovladače (aplikace používá tři různé ovladače k zobrazení přípustných diskových jednotek, adresářů a souborů), vytvoříme vlastní zobrazovací ovladač, zajistíme práci se soubory a vytvoříme alternativní způsob práce se soubory pomocí myši. Nejprve provedeme návrh formuláře aplikace správce souborů. Začneme vytvořením nového projektu. Použitím Inspektora objektů nastavíme následující vlastnosti pro hlavní formulář projektu: Caption nastavíme na Správce souborů, Name na FMForm a Position na poDefault. Nyní přidáme na formulář ovladače. Protože umístění každého z nich závisí na umístění ostatních je nutno jejich umístění a nastavení jejich vlastností zadávat v pořadí uvedeném v následující tabulce. Komponenty DirectoryPanel a FilePanel jsou umístěny na StatusBar:

  2.  
    Komponenta Vlastnost  Hodnota
    Panel  Align alBottom
    Caption necháme prázdný
    Name StatusBar
    BevelOuter bvNone
    Panel Align alLeft
    Caption necháme prázdný
    Name DirectoryPanel
    BevelInner bvLowered
    BevelWidth 2
    Panel Align alClient
    Caption necháme prázdný
    Name FilePanel
    BevelInner bvLowered
    BevelWidth 2
    TabSet Align alBottom
    Name DriveTabSet
    CDirectoryOutline Name DirectoryOutline
    Align alLeft
    FileListBox Align alClient
    Name FileList
    ShowGlyphs  true

    Když máme přidány všechny komponenty a nastaveny jejich vlastnosti, je vhodné projekt uložit. Unit1 uložíme pod jménem FMXWin a Project1 pod FilManEx. K projektu dále přidáme následující programovou jednotku (nazveme ji FMXUtils). Obsahuje různé pomocné funkce pro práci se soubory (prostudujte si je a zjistěte co a jak dělají). Jsou zde také použity deklarace tříd výjimek. Nejprve je uveden hlavičkový soubor a následuje jeho programová jednotka:
    #ifndef FmxUtilsHPP
    #define FmxUtilsHPP
    #include <consts.hpp>
    #include <Classes.hpp>
    #include <Windows.hpp>
    #include <SysUtils.hpp>
    #include <System.hpp>
    class __declspec(delphiclass) EInvalidDest;
    class __declspec(pascalimplementation) EInvalidDest:public EStreamError
    {
     typedef EStreamError inherited;
    public:
      __fastcall EInvalidDest(const AnsiString Msg) : EStreamError(Msg) { }
      __fastcall EInvalidDest(const AnsiString Msg, const TVarRec *Args,
          const int Args_Size) : EStreamError(Msg, Args, Args_Size) { }
      __fastcall EInvalidDest(int Ident) : EStreamError(Ident) { }
      __fastcall EInvalidDest(int Ident, const TVarRec *Args,
          const int Args_Size) : EStreamError(Ident, Args, Args_Size) { }
      __fastcall EInvalidDest(const AnsiString Msg, int AHelpContext) :
          EStreamError(Msg, AHelpContext) { }
      __fastcall EInvalidDest(const AnsiString Msg, const TVarRec *Args,
          const int Args_Size, int AHelpContext) : EStreamError(Msg, Args,
          Args_Size, AHelpContext) { }
      __fastcall EInvalidDest(int Ident, int AHelpContext) :
          EStreamError(Ident, AHelpContext) { }
      __fastcall EInvalidDest(int Ident, const TVarRec *Args,
          const int Args_Size, int AHelpContext) : EStreamError(Ident,
          Args, Args_Size, AHelpContext) { }
    public:
      __fastcall virtual ~EInvalidDest(void) { }
    };
    class EFCantMove : public EStreamError
    {
      typedef EStreamError inherited;
    public:
      __fastcall EFCantMove(const AnsiString Msg) : EStreamError(Msg) { }
      __fastcall EFCantMove(const AnsiString Msg, const TVarRec *Args,
          const int Args_Size) : EStreamError(Msg, Args, Args_Size) { }
      __fastcall EFCantMove(int Ident) : EStreamError(Ident) { }
      __fastcall EFCantMove(int Ident, const TVarRec *Args,
          const int Args_Size) : EStreamError(Ident, Args, Args_Size) { }
      __fastcall EFCantMove(const AnsiString Msg, int AHelpContext) :
          EStreamError(Msg, AHelpContext) { }
      __fastcall EFCantMove(const AnsiString Msg, const TVarRec *Args,
          const int Args_Size, int AHelpContext) : EStreamError(Msg, Args,
          Args_Size, AHelpContext) { }
      __fastcall EFCantMove(int Ident, int AHelpContext) :
          EStreamError(Ident, AHelpContext) { }
      __fastcall EFCantMove(int Ident, const TVarRec *Args,
          const int Args_Size, int AHelpContext) : EStreamError(Ident,
          Args, Args_Size, AHelpContext) { }
    public:
      __fastcall virtual ~EFCantMove(void) { }
    };
    extern void __fastcall CopyFile(const AnsiString FileName,
      const AnsiString DestName);
    extern void __fastcall MoveFile(const AnsiString FileName,
      const AnsiString DestName);
    extern long __fastcall GetFileSize(const AnsiString FileName);
    extern TDateTime __fastcall FileDateTime(const AnsiString FileName);
    extern bool __fastcall HasAttr(const AnsiString FileName,
      unsigned short Attr);
    extern int __fastcall ExecuteFile(const AnsiString FileName,
      const AnsiString Params, const AnsiString DefaultDir, int ShowCmd);
    #endif

    #include <vcl.h>
    #pragma hdrstop
    #include <shellapi.h>
    #include <stdio.h>
    #include "FmxUtils.h"
    TDateTime __fastcall FileDateTime(const AnsiString FileName)
    {
      return (FileDateToDateTime(FileAge(FileName)));
    }
    long __fastcall GetFileSize(const AnsiString FileName)
    {
      TSearchRec SearchRec;
      if(FindFirst(ExpandFileName(FileName), faAnyFile, SearchRec)==0)
        return SearchRec.Size;
      else
        return -1;
    }
    void __fastcall MoveFile(const AnsiString FileName,
      const AnsiString DestName)
    {
      AnsiString Destination;
      char FName[255];
      bool ckmove;
      Destination=ExpandFileName(DestName);
      GetFileTitle(FileName.c_str(),FName,255);
      if(HasAttr(FileName, faReadOnly)) {
        char buffer[255];
        sprintf(buffer, "Error: Can not move the file '%s'.",
          FileName.c_str());
        throw EFCantMove(buffer);
      }
      if (HasAttr(Destination,faDirectory))
        Destination=Destination+AnsiString(FName);
      ckmove= MoveFile(FileName.c_str(), Destination.c_str());
      if(!ckmove)
        ShowMessage("Please give the destination filename");
    }
    void __fastcall CopyFile(AnsiString FileName,AnsiString DestName)
    {
      bool   ckcopy;
      AnsiString Destination;
      char   FName[255];
      GetFileTitle(FileName.c_str(),FName,255);
      Destination = ExpandFileName(DestName);
      if(HasAttr(Destination,faDirectory))
        Destination=Destination+FName;
      ckcopy= CopyFile(FileName.c_str(),Destination.c_str(),false);
      if(!ckcopy)
        ShowMessage("Please give the destination filename");
    }
    bool __fastcall HasAttr(const AnsiString FileName,
      const unsigned short Attr)
    {
      int attribtest;
      attribtest=FileGetAttr(FileName);
      if(attribtest & Attr)
        return true;
      else
        return false;
    }
    int __fastcall ExecuteFile(const AnsiString FileName,
      const AnsiString Params, const AnsiString DefaultDir, int ShowCmd)
    {
      char zFileName[79], zParams[79], zDir[79];
      return (int) ShellExecute(Application->MainForm->Handle, NULL,
        strcpy(zFileName, FileName.c_str()),
        strcpy(zParams, Params.c_str()),
        strcpy(zDir, DefaultDir.c_str()), ShowCmd);
    }
    K našemu formuláři přidáme i nabídku. Na formulář přidáme komponentu MainMenu a pomocí Návrháře nabídky vytvoříme nabídku podle následující tabulky (jsou zde uvedeny i zkracovací klávesy):
    &File
    &Open
    &Move...        F7
    &Copy...        F8
    &Delete...      Del
    &Rename...
    &Properties...
    ------------
    &Exit
    Nyní již můžeme vytvořit obsluhu události OnClick pro volbu File | Exit, která uzavírá hlavní formulář aplikace. Vytvořte ji sami.

  3. Po přidání komponenty TabSet na formulář, se můžeme zabývat zprovozněním tohoto ovladače. Tato komponenta vytváří záložky pro každý prvek v jeho seznamu záložek v závislosti na vlastnosti Tabs. Implicitně tento seznam je prázdný. Seznam sice můžeme editovat během návrhu, ale budeme jej vytvářet až při běhu aplikace. Po zprovoznění tohoto ovladače jej musíme propojit s ovladačem DirectoryOutline a následně s FileList.

  4. API Windows poskytuje funkci GetDriveType, která vrací informaci o typu specifikované jednotky. K určení zda jednotka je přípustná, předáme nulou ukončený řetězec obsahující kořenový adresář jednotky, funkci GetDriveType. Vrácená hodnota indikuje typ jednotky: hodnota větší než 1 indikuje přípustnou jednotku; jiná hodnota indikuje nepřípustnou jednotku. Následující kód tvoří obsluhu události OnCreate formuláře a slouží k vytvoření seznamu záložek při vytvoření formuláře. Seznam záložek obsahuje přípustné jednotky v systému.
    static char * Drive_Letter[]={"a:\\","b:\\","c:\\","d:\\","e:\\","f:\\",
     "g:\\","h:\\","i:\\","j:\\","k:\\","l:\\","m:\\","n:\\","o:\\","p:\\",
     "q:\\","r:\\","s:\\","t:\\","u:\\","v:\\","w:\\","x:\\","y:\\","z:\\"};
    int AddedIndex;
    for(int x =0; x <= 25; x++ ) {
      if(GetDriveType(Drive_Letter[x]) > 1) {
        AddedIndex=DriveTabSet->Tabs->Add(String(Drive_Letter[x]));
        if (toupper(*Drive_Letter[x]) == FileList->Drive)
          DriveTabSet->TabIndex = AddedIndex;
      }
    }
    Tato obsluha vloží písmena přípustných jednotek na záložky. Později se ještě budeme zabývat zobrazením typu jednotky. Nyní, když máme ovladače reprezentující diskové jednotky, adresáře a soubory, je nutno je propojit. Když zvolíme jinou diskovou jednotku, chceme zobrazit její adresářovou strukturu a seznam souborů vybraného adresáře. K propojení ovladačů, musíme vytvořit obsluhy událostí reagující na výběr záložky a změnu adresáře. Když uživatel kliknutím (nebo pomocí klávesnice) vybere záložku komponenty TabSet, komponenta generuje událost OnClick. V našem případě TabSet obsahuje záložky pro přípustné diskové jednotky a kliknutí na některé záložce znamená změnu jednotky. Vytvoříme tedy následující obsluhu události OnClick pro komponentu nazvanou DriveTabSet, která nastaví vlastnost Drive v ovladači DirectoryOutline na první písmeno použité záložky:
    DirectoryOutline->Drive= *((DriveTabSet->Tabs->Strings
                             [DriveTabSet->TabIndex]).c_str());
    Jestliže nyní vybereme záložku diskové jednotky, pak DirectoryOutline zobrazí adresářovou strukturu specifikované jednotky. Když uživatel vybere prvek v komponentě DirectoryOutline (kliknutím nebo pomocí kurzorových kláves), pak komponenta generuje událost OnClick. Mnohem praktičtější je ale využívat událost OnChange, která indikuje, že něco v komponentě DirectoryOutline bylo změněno. Reakci na tuto událost můžeme využít k aktualizaci seznamu souborů. Následující kód aktualizuje jak seznam souborů tak i stavový řádek a to vždy při změně adresáře:
    FileList->Directory = DirectoryOutline->Directory;
    DirectoryPanel->Caption=DirectoryOutline->Directory;
    Jakékoli změny v adresářovém stromu se projeví jednak zobrazením seznamu souborů vybraného adresáře a výpisem adresářové cesty k aktuálnímu adresáři na panelu stavového řádku. Když uživatel klikne na prvek v seznamu souborů, pak okno seznamu generuje událost OnChange. V našem případě využijeme tuto událost k zobrazení informací o vybraném souboru (na druhém panelu stavového řádku). Vytvoříme tedy tuto obsluhu události:
    AnsiString TheFileName;
    if(FileList->ItemIndex>=0){
      char buffer[255];
      TheFileName =FileList->Items->Strings[FileList->ItemIndex];
      sprintf(buffer,"%s  %d bytes",TheFileName,GetFileSize(TheFileName));
      FilePanel->Caption = buffer;
      if (GetFileAttributes(TheFileName.c_str()) & FILE_ATTRIBUTE_DIRECTORY)
        FileSelected = false;
      else
        FileSelected = true;
    }
    else {
      FilePanel->Caption="";
      FileSelected = false;
    }
    Funkce GetFileSize je deklarována v programové jednotce FMXUnit. Do soukromé části deklarace formuláře umístíme:
    bool FileSelected;
    Změna vybraného souboru v seznamu souborů způsobí výpis jména a velikosti vybraného souboru na stavovém řádku.
  5. Každý ovladač, který má vlastní variantu zobrazování má vlastnost nazvanou Style. V naši aplikaci nastavíme vlastnost Style komponenty TabSet na tsOwnerDraw. Každý seznam řetězců C++ Builderu může obsahovat seznam objektů. Do správce souborů přidáme bitové mapy indikující typ diskové jednotky. Požadované obrázky bitových map musíme přidat k aplikaci, a potom tyto obrázky lze umísťovat do seznamu řetězců. Ovladač Image je ovladač obsahující grafický obrázek, např. bitovou mapu. Komponentu Image lze použít k zobrazení grafického obrázku na formuláři, ale můžeme ji také použít pro držení skrytých obrázků, které pak v aplikaci budeme používat. Lze je např. použít k uložení bitových map pro vlastní zobrazovací ovladač. V naši aplikaci přidáme na hlavní formulář tři komponenty Image, nastavíme jejich jména na Floppy, Fixed a Network, vlastnosti Visible u všech tří komponent nastavíme na false a vlastnosti Picture přiřadíme odpovídající bitové mapy (můžeme je stáhnout). Když již máme grafické obrázky v aplikaci, můžeme je přiřadit k řetězcům v seznamu řetězců. V naši aplikaci zjistíme existující disková zařízení a pro každé existující zařízení do seznamu přidáme řetězec současně s obrázkem reprezentujícím typ zařízení. Změníme tedy již existující obsluhu události OnCreate pro formulář a to takto:

  6. static char * Drive_Letter[]={"a:\\","b:\\","c:\\","d:\\","e:\\","f:\\",
     "g:\\","h:\\","i:\\","j:\\","k:\\","l:\\","m:\\","n:\\","o:\\","p:\\",
     "q:\\","r:\\","s:\\","t:\\","u:\\","v:\\","w:\\","x:\\","y:\\","z:\\"};
    int AddedIndex;
    for(int x =0; x <= 25; x++ ) {
      switch(GetDriveType(Drive_Letter[x])) {
        case DRIVE_REMOVABLE:
          AddedIndex=DriveTabSet->Tabs->AddObject(String(Drive_Letter[x]),
            Floppy->Picture->Graphic);
          break;
        case DRIVE_FIXED:
          AddedIndex=DriveTabSet->Tabs->AddObject(String(Drive_Letter[x]),
            Fixed->Picture->Graphic);
          break;
        case DRIVE_REMOTE:
          AddedIndex=DriveTabSet->Tabs->AddObject(String(Drive_Letter[x]),
            Network->Picture->Graphic);
          break;
        case DRIVE_CDROM:
          AddedIndex=DriveTabSet->Tabs->AddObject(Drive_Letter[x],
            Floppy->Picture->Graphic);
          break;
        case DRIVE_RAMDISK:
          AddedIndex=DriveTabSet->Tabs->AddObject(Drive_Letter[x],
            Floppy->Picture->Graphic);
          break;
      }
      if (toupper(*Drive_Letter[x]) == FileList->Drive)
        DriveTabSet->TabIndex = AddedIndex;
    }
    Když nastavíme styl ovladače na vlastní kreslení, Windows již dále nezobrazuje ovladač na obrazovku. Místo toho generuje události pro každý viditelný prvek v ovladači. Naše aplikace tyto události zpracuje pro nakreslení prvků. Před vlastním zobrazením prvků v proměnném vlastním kreslícím ovladači, Windows generuje událost OnMeasureItem. Windows určují, jaká plocha bude potřebná k zobrazení prvku (obecně plocha, na které bude zobrazen text prvku aktuálním písmem). Naše aplikace může zpracovat tuto událost a změnit navrženou plochu. K změně velikosti prvku pro vlastní kreslení, vytvoříme obsluhu události OnMeasureItem. Tato událost má dva důležité parametry: index prvku a velikost tohoto prvku. Velikost je parametr volaný odkazem a aplikace může jeho hodnotu zmenšovat nebo zvětšovat. Pozice následujícího prvku závisí na velikosti předchozího prvku. Změnu velikosti prvku lze využívat v komponentách seznamu, kombinovaného okna a TabSet (zde se používá událost OnMeasureTab). V naší aplikaci vytvoříme obsluhu události OnMeasureTab pro komponentu TabSet, která zvětší původní šířku záložky o šířku bitové mapy + 2:
    int BitmapWidth;
    BitmapWidth = ((Graphics::TBitmap *)
                  (DriveTabSet->Tabs->Objects[Index]))->Width;
    TabWidth=TabWidth+2+BitmapWidth;
    Prvek z vlastností Objects v seznamu řetězců musíme přetypovat. Objects je vlastnost typu TObject a může tak obsahovat libovolného potomka TObject. Když získáváme objekty z pole, je potřeba je převést zpět na aktuální typ prvků. Jestliže aplikace požaduje kreslení nebo překreslení ovladače pomocí vlastního kreslení, Windows generuje kreslící událost pro každý viditelný prvek v ovladači. Jména těchto událostí začínají vždy OnDraw, např. OnDrawItem, OnDrawTab nebo OnDrawCell. Tato událost obsahuje parametry indikující index kresleného prvku, obdélník, ve kterém bude zobrazen a obvykle některé informace o stavu prvku. Pro naši aplikaci tedy vytvoříme obsluhu události OnDrawTab pro komponentu TabSet:
    Graphics::TBitmap *Bitmap;
    Bitmap = (Graphics::TBitmap *) (DriveTabSet->Tabs->Objects[Index]);
    TabCanvas->Draw(R.Left, R.Top + 4, Bitmap);
    TabCanvas->TextOut(R.Left + 2 + Bitmap->Width, R.Top + 2,
      DriveTabSet->Tabs->Strings[Index].SubString(1,1));
    Většina kreslících událostí nepředává jako parametr plátno objektu; běžně se při kreslení používá plátno ovladače. Protože TabSet má mezi prvky vložen oddělovač záložek, je předáno pro kreslený prvek speciální plátno jako parametr.
  7. V knihovně běhu programu Builderu je zabudováno několik souborových operací. Funkce pro práci se soubory vyžadují zadání jména souboru, se kterým chceme pracovat. Mimo těchto knihovních funkcí budeme v naší aplikaci používat některé další; jsou uvedeny v programové jednotce FMXUtils. Práci se soubory budeme provádět jako reakci na volbu v nabídce. Některé prvky nabídky, je vhodné zpřístupnit pouze v případě vybraného souboru. Pro povolování a zakazování prvků v nabídce vytvoříme obsluhu události OnClick pro nabídku File:

  8. Open1->Enabled = FileSelected;
    Delete1->Enabled = FileSelected;
    Copy1->Enabled = FileSelected;
    Move1->Enabled = FileSelected;
    Rename1->Enabled = FileSelected;
    Properties1->Enabled = FileSelected;
    Nyní, když uživatel otevře nabídku File, aplikace povoluje nebo zakazuje všechny prvky této nabídky (mimo Exit) v závislosti na tom, zda máme v seznamu souborů vybraný soubor.
    Zrušení souboru odstraní soubor z disku a zruší příslušnou položku v seznamu souborů. Pro zrušení souboru, předáme jméno souboru funkci DeleteFile. DeleteFile vrací true, jestliže soubor byl zrušen, nebo false, pokud jej nelze zrušit (jedná se např. o soubor určený pouze pro čtení). Následující kód zpracovává volbu File | Delete:
    if(MessageDlg("Delete" + FileList->FileName + "?", mtConfirmation,
                  TMsgDlgButtons() << mbYes << mbNo, 0) == mrYes){
      if(DeleteFile(FileList->FileName.c_str()))
        FileList->Items->Delete(FileList->ItemIndex);
    }
    Každý soubor má několik atributů uložených operačním systémem jako bitové příznaky. Změna atributu souboru se provádí v těchto krocích: přečteme atributy souboru, změníme požadovaný atribut a uložíme atributy souboru. Pro přečtení atributů souboru, předáme jméno souboru funkci FileGetAttr. Vrácená hodnota typu unsigned short popisuje nastavené atributy. V našem příkladě volbou File | Properties otevřeme dialogové okno, ve kterém uživatel zjistí informace o souboru a kde také může atributy souboru měnit. Přidáme tedy k projektu nový formulář, přiřazenou programovou jednotku uložíme pod jménem FAttrDlg a vlastnosti nového formuláře nastavíme podle následující tabulky:
     
    Vlastnost Hodnota
    Name FileAttrDlg
    Caption File Attributes
    Position poScreenCenter
    BorderIcons [biSystemMenu]
    BorderStyle bsDialog

    Podle následujícího obrázku na formulář přidáme další ovladače (jména tří komponent Label jsou uvedena na obrázku a značky nazveme ReadOnly, Archive, System a Hidden):

    Pro nastavení souborových atributů, předáme jméno souboru a atributy funkci FileSetAttr. Následující kód čte souborové atributy do proměnné, nastaví značky v dialogovém okně na současné hodnoty atributů a dialogové okno provede. Jestliže uživatel některý z atributů změní, pak v případě uzavření dialogového okna pomocí tlačítka OK, jsou jejich hodnoty předány souboru (následující příkazy tvoří obsluhu volby File | Properties):
    unsigned short  Attributes,  NewAttributes;
    FileAttrDlg->FileDirName->Caption =
      FileList->Items->Strings[FileList->ItemIndex];
    FileAttrDlg->FilePathName->Caption = FileList->Directory;
    FileAttrDlg->ChangeDate->Caption =
      DateTimeToStr(FileDateTime(FileList->FileName));
    Attributes = FileGetAttr(FileList->Items->Strings[FileList->ItemIndex]);
    FileAttrDlg->ReadOnly->Checked = Attributes & faReadOnly;
    FileAttrDlg->Archive->Checked = Attributes & faArchive;
    FileAttrDlg->System->Checked = Attributes & faSysFile;
    FileAttrDlg->Hidden->Checked = Attributes & faHidden;
    if (FileAttrDlg->ShowModal()!= mrCancel){
      NewAttributes = Attributes;
      if (FileAttrDlg->ReadOnly->Checked)
        NewAttributes = NewAttributes | faReadOnly;
      else NewAttributes = NewAttributes & ~faReadOnly;
      if(FileAttrDlg->Archive->Checked)
        NewAttributes=NewAttributes|faArchive;
      else NewAttributes = NewAttributes & ~faArchive;
      if(FileAttrDlg->System->Checked)NewAttributes=NewAttributes|faSysFile;
      else NewAttributes = NewAttributes & ~faSysFile;
      if(FileAttrDlg->Hidden->Checked)NewAttributes=NewAttributes|faHidden;
      else NewAttributes = NewAttributes  & ~faHidden;
      if (NewAttributes != Attributes)
        FileSetAttr(FileAttrDlg->FileDirName->Caption, NewAttributes);
    }
    Přesun, kopírování a přejmenování souborů jsou podobné operace, všechny vytvářejí soubor z jiného souboru. Liší se pouze tím, co zbude z původního souboru. Knihovna běhu programu poskytuje funkci RenameFile, která provádí přejmenování. Programová jednotka FMXUtils poskytuje podobné funkce nazvané MoveFile a CopyFile pro další operace. Všechny tyto funkce přebírají dva řetězce jako své parametry: jméno původního souboru a jméno cílového souboru. Následující kód zobrazuje soukromou metodu nazvanou ConfirmChange, která zobrazí potvrzovací dialogové okno:
    void __fastcall TFMForm::ConfirmChange(const AnsiString ACaption,
                    AnsiString FromFile, AnsiString ToFile)
    {
      char buffer[700];
      sprintf(buffer,"%s %s to %s?",ACaption, FromFile, ToFile);
      if(MessageDlg(buffer, mtConfirmation,
         TMsgDlgButtons() << mbYes << mbNo, 0) == mrYes){
        if (ACaption == "Move") MoveFile(FromFile, ToFile);
        else if (ACaption == "Copy") CopyFile(FromFile, ToFile);
        else if (ACaption =="Rename") RenameFile(FromFile, ToFile);
       FileList->Update();
      }
    }
    Protože všechny tři operace jsou si podobné, může aplikace správce souborů sdílet většinu použitého kódu. Vytvoříme dialogové okno, ve kterém uživatel zadá původní jméno a cílové jméno a použijeme jej v těchto operacích. Na obrázku je toto okno zobrazeno (horní editační ovladač nazveme FromFileName a spodní ToFileName).

    Formulář dialogového okna nazveme ChangeDlg a jeho jednotku uložíme do souboru FChngDlg.CPP. Po vytvoření dialogového okna jej můžeme otevírat z obsluhy události sdílené prvky nabídky Move, Copy a Rename (nazveme ji FileChange). Tato obsluha je tvořena příkazy (funkce _c_exit je definována v hlavičkovém souboru Process.h):
    if (dynamic_cast<TMenuItem *>(Sender) == Move1 )
      ChangeDlg->Caption = "Move" ;
    else if (dynamic_cast<TMenuItem *>(Sender) == Copy1)
      ChangeDlg->Caption = "Copy";
    else if (dynamic_cast<TMenuItem *>(Sender) == Rename1)
      ChangeDlg->Caption = "Rename";
    else _c_exit();
    ChangeDlg->CurrentDir->Caption = DirectoryOutline->Directory;
    ChangeDlg->FromFileName->Text = FileList->FileName;
    ChangeDlg->ToFileName->Text = "";
    if((ChangeDlg->ShowModal()!=mrCancel)&&(ChangeDlg->ToFileName->Text!=""))
      ConfirmChange(ChangeDlg->Caption, ChangeDlg->FromFileName->Text,
                    ChangeDlg->ToFileName->Text);
    Aplikace nyní zobrazuje dialogové okno se správným titulkem a provádí požadovanou operaci. Aplikace někdy potřebuje provést jinou aplikaci. API Windows poskytuje funkci ShellExecute, která provádí aplikaci, ale tato funkce vyžaduje několik parametrů. Programová jednotka FMXUtils obsahuje její snadněji použitelnou alternativu, nazvanou ExecuteFile. ExecuteFile pracuje dvěma způsoby. Je-li ji předáno jméno proveditelného souboru, ExecuteFile spustí tuto aplikaci. Je-li ji předáno jméno dokumentu přiřazeného aplikaci, ExecuteFile spustí aplikaci a automaticky otevře tento dokument. ExecuteFile přebírá tři parametry typu AnsiString a čtvrtý parametr indikující způsob zobrazení okna aplikace. Tři řetězce reprezentují jméno souboru, parametry předané aplikaci a adresář použitý jako pracovní adresář aplikace. Poslední parametr může být jedna z konstant používaná API funkcí ShowWindow. Např. SW_SHOW zobrazí okno normálně, SW_SHOWMINIMIZED zobrazí okno minimalizované apod. Okno seznamu souborů obsahuje všechny informace potřebné k provedení souboru. Stejné příkazy můžeme připojit k události OnDblClk okna seznamu souborů, což umožní spouštět programy dvojitým kliknutím na jejich jménu v seznamu. Jestliže ale vybraný prvek je adresář, potom pravděpodobně chceme tento adresář otevřít (přejít na tento adresář). Obsluha události volby File | Open bude vypadat takto:
    if (HasAttr(FileList->FileName, faDirectory))
      DirectoryOutline->Directory = FileList->FileName;
    else ExecuteFile(FileList->FileName," ", DirectoryOutline->Directory, SW_SHOW);
    a obsluha dvojitého kliknutí na seznamu souborů je tvořena příkazem:
    Open1Click(Sender);
    HasAttr je funkce typu bool z FMXUtils, která vrací true, když soubor uvedený jako její první parametr má atribut určený druhým parametrem; jinak vrací false. Nastavení vlastnosti Directory seznamu adresářů, způsobí změnu adresáře. Dvojitým kliknutím na adresáři v seznamu souborů lze nyní měnit adresář. Vývoj aplikace dokončíme v následující kapitole.

17. Správce souborů I