-
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:
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.
-
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.
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.
-
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:
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.
-
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:
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.