Пишем файл-броузер

Итак попробуем написать файл броузер собственными руками на базе компонентов DirectoryListBox, DriveComboBox, FileListBox.

Поставим задачу расширения дизайна этих компонентов. Мы не будем менять их код. Мы создадим на форме три кнопки, DriveComboBox, FileListBox сделаем невидимыми, в заменим их
ComboBox и ListBox компонентами соответсвенно.

Сначала проиницилизируем ComboBox:
#define USER_PATH DriveComboBox1->Items->Count



Стандартные папки:
AnsiString SystemDir, WindowsDir, TempDir;
AnsiString MyDocumentsFolder ,Desktop;

...

 char specfolder[MAX_PATH];
 GetSystemDirectory(specfolder,100);
 SystemDir = specfolder;
 GetWindowsDirectory(specfolder,100);
 WindowsDir=specfolder;
 TempDir = WindowsDir+"\\Temp";
 SHGetSpecialFolderPath(Handle, specfolder, 0x0005, false);
 MyDocumentsFolder = specfolder;
 SHGetSpecialFolderPath(Handle, specfolder, 0x0000, false);
 Desktop = specfolder;



Теперь мы знаем местоположение и буквы дисков компьютера. Осталось заполнить полученными данными ComboBox.

Напишем специальную структуру, и создадим массив на её базе:
enum TPathType {ptDestkop, ptNetworkDrive , ptHardDisk, ptCdrom, ptFloppy, ptDisk,
      ptMyDocuments, ptWinSystem, ptWindows, ptWinTemp,
      ptUserDefined };

struct TPath {
  TPathType PathType; //Тип пути
  AnsiString DefDir;  //Каталог пути
  AnsiString Name;    //Имя пути
}StdPath[40];



Для определения типа пути по его каталогу и размещения полученных данных в массиве напишем функцию:
void AddUserPath(AnsiString DefDir, AnsiString Name, int PathID) {
   StdPath[PathID].Name = Name;
   StdPath[PathID].DefDir = DefDir;
   Form1->ComboBox1->Items->Add(DefDir+"  "+Name+"");
   if(DefDir.Length()==2) {
     switch(GetDriveType(DefDir.c_str()))  {
      case DRIVE_CDROM:
       StdPath[PathID].PathType = ptCdrom;
      return;
      case DRIVE_REMOTE:
       StdPath[PathID].PathType = ptNetworkDrive;
      return;
      case DRIVE_FIXED:
       StdPath[PathID].PathType = ptHardDisk;
      return;
      case DRIVE_REMOVABLE:
        StdPath[PathID].PathType = ptFloppy;
      return;
      default:
       StdPath[PathID].PathType = ptDisk;
      return;
     }
   }
   if(DefDir==MyDocumentsFolder) {
      StdPath[PathID].PathType = ptMyDocuments;
      return;
   }
   if(DefDir==SystemDir) {
      StdPath[PathID].PathType = ptWinSystem;
      return;
   }
   if(DefDir==WindowsDir) {
      StdPath[PathID].PathType = ptWindows;
      return;
   }
   if(DefDir==TempDir) {
      StdPath[PathID].PathType = ptWinTemp;
      return;
   }
   if(DefDir==Desktop) {
      StdPath[PathID].PathType = ptDestkop;
      return;
   }
   StdPath[PathID].PathType = ptUserDefined;
}



Благодаря функции GetDriveType мы узнаём тип данного диска.

Теперь мы можем проинициализировать ComboBox:
 for(int i=0; i<DriveComboBox1->Items->Count; i++) {
   AddUserPath(DriveComboBox1->Items->Strings[i].SubString(1,2),DriveComboBox1->Items->Strings[i].c_str()+3,i);
 }
 AddUserPath(SystemDir, "Системная папка", USER_PATH);
 AddUserPath(WindowsDir, "Папка Windows", USER_PATH+1);
 AddUserPath(TempDir, "Временные файлы", USER_PATH+2);
 AddUserPath(MyDocumentsFolder, "Мои документы", USER_PATH+3);
 AddUserPath(Desktop, "Рабочий стол", USER_PATH+4);



Для каждого типа пути нужна своя картинка. Проинициализируем её.
Мы разместим все картинки в папочке Interface. Каждому типу пути поставим в соответствие свою картинку:

AnsiString PathIconNames[] = {
  "Destkop.bmp",
  "NetDisk.bmp",
  "HardDisk.bmp",
  "CDDisk.bmp",
  "Floppy.bmp",
  "Disk.bmp",
  "Mydocs.bmp",
  "Winsys.bmp",
  "Windows.bmp",
  "WinTemp.bmp",
  "User.bmp"
};

Graphics::TBitmap* PathIcons[ptUserDefined+1];



Проинициализируем их:
 for(int i=0; i<ptUserDefined+1; i++) {
    PathIcons[i] = new Graphics::TBitmap();
    PathIcons[i]->LoadFromFile("interface\\" + PathIconNames[i]);
    PathIcons[i]->Transparent = true;
 }



И удалим:

void __fastcall TForm1::FormDestroy(TObject *Sender)
{
 for(int i=0; i<ptUserDefined+1; i++) {
    delete PathIcons[i];
 }
}



Теперь самое интересное - отрисовка элементов ComboBox. Для этого свойство Style надо установить в csOwnerDrawFixed (только отрисовка), выбрать ItemHeight по размерам картинок (у меня было значение 24 для картинок 16 на 16), а событие OnDrawItem заполнить следующим кодом:
void __fastcall TForm1::ComboBox1DrawItem(TWinControl *Control, int Index,
      TRect &Rect, TOwnerDrawState State)
{
   ComboBox1->Canvas->FillRect(Rect);
   ComboBox1->Canvas->Draw(Rect.Left+4,Rect.Top+4, PathIcons[StdPath[Index].PathType]);
if(StdPath[Index].PathType>=ptNetworkDrive && StdPath[Index].PathType<=ptDisk) {
    ComboBox1->Canvas->TextOut(Rect.Left+48, Rect.Top+6, StdPath[Index].Name);
    ComboBox1->Canvas->TextOut(Rect.Left+28, Rect.Top+6, StdPath[Index].DefDir);
 }
else {
   ComboBox1->Canvas->TextOut(Rect.Left+28, Rect.Top+6, StdPath[Index].Name);
}
}



Половина дела сделанa. Теперь на очереди FileListBox.
Так как картинки из этого компонента нам не подходят, а TIcon не может менять размеры значка, мы опять всё пишем руками
Сначала напишем функцию SetPath. Онабудет вызываться при изменении текущей папки.

void SetPath(AnsiString NewPath) {
  if(DirectoryExists(NewPath)) {
   Path = NewPath;
   Form1->FileListBox1->Directory=Path;
   if(Form1->DirectoryListBox1->Directory!=Path) Form1->DirectoryListBox1->Directory=Path;
   Form1->ListBox1->Clear();
   Form1->ListBox1->Items->AddStrings(Form1->FileListBox1->Items);
  }
}


В конструктор добавится следующая строка: SetPath(MyDocumentsFolder); Теперь при стрте приложения текущей папкой будет "мои документы".
Наполнится кодом OnChange ComboBox'а:
void __fastcall TForm1::ComboBox1Change(TObject *Sender)
{
 SetPath(StdPath[ComboBox1->ItemIndex].DefDir);
}



Теперь осталось написать отрисовку элемента в ListBox. Установим Для этого свойство Style надо установить в lbOwnerDrawVariable (отрисовка и подготовка к отрисовке). Выполним подготовительные действия для значков: установим папку со значками и их размер:

int IconSize=16;
AnsiString IconsDir;

...

IconsDir = ExtractFilePath(Application->ExeName)+"\\interface\\files16\\";



Сама отрисовка:
 
void __fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index,
      TRect &Rect, TOwnerDrawState State)
{
    HICON hTypeIcon;
    ListBox1->Canvas->Brush->Style = bsSolid;
    ListBox1->Canvas->Rectangle(Rect);
    ListBox1->Canvas->Brush->Style = bsClear;
    ListBox1->Canvas->TextOut(Rect.Left+IconSize+4,Rect.Top+Rect.Height()/2-10,ListBox1->Items->Strings[Index]);
    if(FileExists(IconsDir+AnsiLowerCase(ExtractFileExt(ListBox1->Items->Strings[Index]).SubString(2,3))+".ico")) { //Если есть значок для этого типа файлов грузим его, если нет то грузим default.ico.
        hTypeIcon = LoadImage(NULL, (IconsDir+AnsiLowerCase(ExtractFileExt(ListBox1->Items->Strings[Index]).SubString(2,3))+".ico").c_str(), IMAGE_ICON, IconSize, IconSize, LR_LOADFROMFILE);
    }
     else {
      hTypeIcon = LoadImage(NULL, "interface\\files16\\default.ico", IMAGE_ICON, IconSize, IconSize, LR_LOADFROMFILE);
     }
     DrawIconEx(ListBox1->Canvas->Handle, Rect.Left+2, Rect.Top+2, hTypeIcon, IconSize,IconSize, 0, NULL, DI_NORMAL);
     DestroyIcon(hTypeIcon);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListBox1MeasureItem(TWinControl *Control,
      int Index, int &Height)
{
    //Подготока к отрисовке
    ListBox1->Canvas->Pen->Color = 0xFAFAFA;
    if(ListBox1->ItemIndex == Index) { //Если этот элемент текущий
      ListBox1->Canvas->Brush->Color=clNavy;
    }
    else {
      ListBox1->Canvas->Brush->Color=clWhite;
    }

   Height = IconSize+4; //Высота элемента

}



Мы использовали функцию LoadImage. Это универсальная функция загрузки иконок, курсоров, битмапов из ресурса. Если использовать флаг LR_LOADFROMFILE, загрузка происходит из файла. Первыц параметр - Имя ресурса или путь к файлу. Второй параметр - тип картинки. Третий и четвёртый - размеры.

И, наконец, последний штрих, выбор размера значков. Для этого нам нужны три кнопки с заговками 16, 32 и 48


void __fastcall TForm1::Button1Click(TObject *Sender)
{
 IconSize=16;
 ListBox1->Repaint();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
 IconSize=32;
 ListBox1->Repaint();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 IconSize=48;
 ListBox1->Repaint();
}



Файл-броузер готов!
 
« Предыдущая статья