Тема: [C/C++, winApi] Файловий менеджер, simple file manager, file explorer
Проект аж з 2008 року, але досі актуальний, нині студікі все ще за нього питають в мене, буду відправляти сюди.
Опис:
Це простий, двохпанельний файловий менеджер з базовим функціоналом, до ФАРу далеко, але я намагався
Що можна робити з файлами:
Копіювати, переіменовувати, переміщати, видаляти, і переглядати (в разі текстових файлів).
Командний рядок, WinApi і Visual Studio, от і всі технології.
Має один файл MainX.cpp, в якому все це і написане.
Там якийсь прикол був з юнікодом, і фактично проект має дві версії, але версію з юнікодом я Вам не дам, бо з нею якась біда була, і я вже не пам'ятаю яка і де, тому ANSI версії функцій до Ваших послуг.
Скрін:
/**
Description
Version 0.0.04 (beta) (ANSI-edition)
*/
#include <windows.h> //Здесь все АПИ-шные ф-ции и модули
#include <iostream> //тут стандартные потоки ввода/вывода
#include <CONIO.H> //тут getch();
using namespace std;
const short WIDTH = 80; //константа ширины поля (кол-во символов по горизонтали)
const short HEIGHT = 25; //константа высоты поля (кол-во символов по вертикали), нужно для установки панели и некоторых других вычислений
/*Дерективы компилятора*/
#define DIALOG_OK 0;
#define DIALOG_YES_NO 0;
#define DIALOG_WAIT 0;
#define DIALOG_GET 0;
#define PN_LEFT 0;
#define PN_RIGHT 0;
#define PN_FULL 0;
static HANDLE _ConsoleOut = ::GetStdHandle(STD_OUTPUT_HANDLE); //объявляем переменную, будет хранить хандл окна консоли (ВИНАПИ)
/**Устанавливаем глобальные переменные*/
byte nP=1; //хранит номер выбраной панели (1 - слева, 2 - справа)
char backUpDir[MAX_PATH]="FILE SYSTEM"; //хранит временный путь, для того чтоб сделать откат, если произойдет ошибка составления файлов (к примеру в СД-РОМе нет диска, или путь блокирован ОС-кой)
/**для панели 1*/
char fList1[2][450][MAX_PATH];//глобальная переменная, которая хранит список файлов и подкаталогов в выбраном каталоге
char dir1[MAX_PATH]=("FILE SYSTEM"); //глобальная переменная, которая хранит каталог
int dx1=0; //хранит смещения для вывода на экран списка файлов (для первой панели)
int countFd1=0; //хранит количество подкаталогов в выбраном каталоге
int countFf1=0; //хранит количество файлов в выбраном каталоге
int selItem1=1; //хранит номер выделенного елемента списка
/*-------------*/
/**для панели 2*/
char fList2[2][450][MAX_PATH];//глобальная переменная, которая хранит список файлов и подкаталогов в выбраном каталоге
char dir2[MAX_PATH]= ("FILE SYSTEM"); //глобальная переменная, которая хранит каталог
int dx2=0; //хранит смещения для вывода на экран списка файлов (для второй панели)
int countFd2=0; //хранит количество подкаталогов в выбраном каталоге
int countFf2=0; //хранит количество файлов в выбраном каталоге
int selItem2=1; //хранит номер выделенного елемента списка
/*-------------*/
/*------------------------------------*/
/**Объявняем все функции, кроме main() используемые в программе (чтобы из любой функции можно было вызывать любую функцию*/
//pdDialog тоже можем не объявлять, так как это первая функция (видится всеми итак)
void getDrive(byte nPanel);//Формирование списка логических дисков в системе
int fileList( byte nPx);//Формирование списка файлов в каталоге. nPx - номер панели, для которой составляем список файлов 1 -слева; 2-справа
BOOL SetConsoleSize(SHORT x, SHORT y);//Установка размера консоли (x, y)
BOOL setCol(int text, int bg);//установить цвет текста + фона
BOOL setPos(SHORT x, SHORT y);//ф-ция которая устанавливает курсор в заданную позицию экрана, с координатами (x, y)
bool isCoren();//Определяет находимся ли мы в корне накопителя (отдельного логического диска). Нужна для того чтоб убрать лишние точки вначале списка файлов (или поставить таковые)
bool root(char *c);//Определяет находимся ли мы на уровне, на котором выводятся все логические диски в системе
void paint(int as);//Отрисовка панелей с файлами.
/**Отрисовка окна диалога*/
//mes - текст сообщения
//dgType - тип диалога:
// 0 - предупреждение, либо ошибка (одна кнопка ок),
// 1 - диалог подтверждения (2 кнопки (да, нет)),
// 2 - сообщение(ждите и т.п.) не требует нажатия,
// 3 - диалог ввода (получает текст с клавиатуры и передает в inText)
bool pdDialog(char* mes, byte dgType, char* inText="null")
{
setlocale (LC_CTYPE,".866"); //устанавливаем кодовую страницу в кодировку ,866 (DOS). Это нужно для того, чтоб у нас была возможность рисовать символами рамки окон и панелей
setCol(0,7);//Устанавливаем цвет, текста и фона, соответственно
setPos((80-strlen(mes))/2,11); //устанавливаем курсор в позицию экрана. Это сделано для того, чтоб вывести сообщение "mes" посередине окна
for (int i=0;i<strlen(mes)+2;i++) //рисуем горизонтальную двойную линию, которая на 2 символа больше чем длина сообщения, strlen(mes)=колво символов в mes
cout<<(char)205; //выводим символ "═"
setPos((80-strlen(mes))/2,13); //устанавливаем курсор в позицию экрана
for (int i=0;i<strlen(mes)+2;i++) //рисуем горизонтальную двойную линию
cout<<(char)205; //выводим символ "═"
setlocale (LC_CTYPE,".1251"); //устанавливаем кодовую страницу в кодировку .1251 (ANSI). Это нужно для того, чтоб у нас была возможность выводить текст кириллицей
setPos((80-strlen(mes))/2,12); //устанавливаем курсор в позицию экрана
cout<<" "<< mes <<" "<<endl; //выводим сообщение
switch(dgType){ //в зависимости от типа сообщения переходим по метке
case 0:
{
setPos((80-strlen(" OK (Enter) "))/2+1,14); //устанавливаем позицию
cout<<" OK (Enter) "; //рисуем кнопку
break; //выходим из тела switch, чтоб не выполнять других операций
}
case 1:
{
setPos((80-strlen(" Yes (Enter) No(Esc) "))/2+1,14);//устанавливаем позицию
cout<<" Yes (Enter) No(Esc) "; //рисуем кнопку
break;//выходим из тела switch, чтоб не выполнять других операций
}
case 3:
{
setPos((80-strlen(mes))/2,14);
for (int i=0;i<strlen(mes)+2;i++)
cout<<" ";
setPos((80-strlen(" Confirm (Enter) "))/2+1,15);//устанавливаем позицию
cout<<" Confirm (Enter) "; //рисуем кнопку
setPos((80-strlen(mes))/2+1,14);
cin>>inText; //получаем строку от пользователя и передаем за пределы функции
}
}
if (dgType<2) //если dgType<2, то нам нужно перехватывать нажатие клавишь с клавиатуры
{
int key = getch(); //получаем код нажатой клавиши
switch (key)
{
case 13: //Enter
return true; //возвращаем значение этой функции в истину
break;
case 27: //Esc
return false;//возвращаем значение этой функции в ложь
}
}
}
/**Формирование списка логических дисков в системе. nPanel - номер панели: 1- слева; 2-справа*/
void getDrive(byte nPanel)
{
// Взято от сюда http://articles.org.ru/cn/showdetail.php?cid=6002 , переделано под С++
DWORD x=GetLogicalDrives(); // Функция возвращает битовую маску установленных логических дисков.
// Бит 0 определяет наличие диска А:, бит 1 - диска B и т.д.
int bit;
if (x==0) { //проверяем успешное выполнение функции, если x=0 то то функция не выполнена как следует
pdDialog("Ошибка составление списка логич. дисков",0);//Поэтому выводим сообщение об ошибке (тип которого, сообщение с одной кнопкой ОК)
exit(0); //и выходим за пределы getDriv
}
char dn[4]=" "; //массив из 4-х элементов, будет хранить строку, в которой будет проверятся путь корня диска (например С:\, D:\, E:\ и т.д.)
strcpy(dn, " :\\\0"); //копируем 3 последних символа (двоеточие, слешь, и нулевой), так как они не буду изменять своего значения на протяжении всей жизни функции
char tip[31]; //В этом массиве символов будет хранится тип долического диска (например жесткий, сьемный, СДРОМ, и т.п.)
char tempCh[33]; //В этом массиве символов будет хранится все имя (которое в последствии будет выведено)
int j=0; //эта переменная будет хранить фактическое количество логических дисков в системе
for (int i=1;i<=64;i++) //в этом цикле пробегаем все возможные варианты
{
bit=x & 1; // Накладываем битовую маску для выделения бита с поряковым номером 0
if (bit==1) //если такое диск найден
{
dn[0]=(char)(64+i); //получаем его букву (первый символ пути к корню)
UINT tp =GetDriveTypeA(dn); //получаем тип диска
//и сохраняем в текстовую переменную
if (tp==DRIVE_UNKNOWN)
strcpy(tip,"Drive type cannot be determined");
else if (tp==DRIVE_NO_ROOT_DIR)
strcpy(tip,"Drive no root dir");
else if (tp==DRIVE_REMOVABLE)
strcpy(tip,"Removable");
else if (tp==DRIVE_FIXED)
strcpy(tip,"Fixed");
else if (tp==DRIVE_REMOTE)
strcpy(tip,"remote (network)");
else if (tp==DRIVE_CDROM)
strcpy(tip,"CDROM");
else if (tp==DRIVE_RAMDISK)
strcpy(tip,"RAM disk");
//Вспомогательный маретиал
//#define DRIVE_UNKNOWN 0
//#define DRIVE_NO_ROOT_DIR 1
//#define DRIVE_REMOVABLE 2
//#define DRIVE_FIXED 3
//#define DRIVE_REMOTE 4
//#define DRIVE_CDROM 5
//#define DRIVE_RAMDISK 6
//формируем строчку (с путем к корню + в квадратных скобках тип диска)
strcpy(tempCh,dn); //скопировать строчку dn в tempCh
strcat(tempCh," ["); //конкатенация (объеденение строчки tempCh и " [")
strcat(tempCh,tip); //конкатенация (объеденение строчки tempCh и tip)
strcat(tempCh,"]"); //конкатенация (объеденение строчки tempCh и "]")
switch(nPanel){ //в зависимости от сообщаемой панели записываем результаты в глобальный массив списка папок
case 0:
case 1:
strcpy(fList1[0][j],tempCh);
if (nPanel==1) break;
case 2:
strcpy(fList2[0][j],tempCh);
}
j++; //увеличиваем найдетное количество дисков на еденицу (инкремент)
}
x>>=1; //сдвиг в право на 1 бит (всеравно что x=x*2, только намного шустрее)
}
switch(nPanel){ //в зависимости от сообщаемой панели записываем значения (количество папок и файлов)
case 0:
case 1:
countFf1=0; //количество файлов панели 1 здесь приравниваем к нулю
countFd1=j; //сохраняем количество
if (nPanel==1) break; //если, только панель 1 - выходим
case 2:
countFf2=0; //количество файлов здесь приравниваем к нулю
countFd2=j; //количество файлов панели 2 здесь приравниваем к нулю
}
}
/**Формирование списка файлов в каталоге. nPx - номер панели, для которой составляем список файлов 1 -слева; 2-справа*/
int fileList( byte nPx){ //функция которая заносит в массив файлы и каталоги, взято из "www.msdn.microsoft.com"
char *dir; //указатель на папку в которой формируем список файлов
if (nPx==1) //в зависимости от номера панели
dir=dir1; //сообщаем указатель на переменную папки панели 1, или
else
dir=dir2; //панели 2
if ( root(dir) ) //если мы находимся на уровне, где должны сформировать список всех функций в системе
getDrive(nPx); //то формируем это список
else //иначе формируем список файлов и папок
{
WIN32_FIND_DATAA ffd; //структура с параметрами поиска файлов и папок
char szDir[MAX_PATH]; //формируем строчку с путем для поиска
HANDLE hFind = INVALID_HANDLE_VALUE; //хандл поиска
DWORD dwError=0; //номер ошибки
strcpy(szDir,dir); //копируем строчку по адресу dir в переменную szDir
strcat(szDir,"\\*"); //добавляем слешь и звездочку
hFind = FindFirstFileA(szDir, &ffd); //сообщаем хандл
if (INVALID_HANDLE_VALUE == hFind) //если по каким либо причинам, от нас не зависящим, хандл не присвоен - выводим сообщение об ошибке
{
pdDialog("Произошла ошибка, при составлении списка файлов",0); //выводим сообщение (тип с одной кнопкой ОК)
strcpy(dir,backUpDir); //возвращаем из бэкАп папку что была до выполнения над ней модификаций
return dwError; //сообщаем сообщение об ошибке
}
// List all the files in the directory with some info about them.
int j1=1; //будет хранить количество папок
int j2=0; //будет хранить холичество файлов
setlocale (LC_CTYPE,"Russian"); //устанавливаем кодовую страницу в .1251 (ANSI) чтоб коректно сохранять папки и файлы содержащире кириллицу
if (nPx==1) //в хависимости от выбраной панели
strcpy(fList1[0][0],".."); //сохраняем первый элемент списка файлов в "..", чтоб иметь возможность "путешествовать" по дереву каталогов
else
strcpy(fList2[0][0],"..");
if (!isCoren()) //если мы не в корне то делаем 2 пустых операции поиска файлов чтоб избавится от ненужных точек
{
FindNextFileA(hFind, &ffd); //следующий найденый файл
FindNextFileA(hFind, &ffd); //следующий найденый файл
}
do //цикл, в котором будем формировать список файлов и папок
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) //если найденый элемент - папка, то
{
int i = 0; //будет хранить номер копируемого символа
while (true) //в этом цикле посимвольно копируем строку найденой папки в общий список папок
{
if (nPx==1) //в зависимости от панели
fList1[0][j1][i] = ( ffd.cFileName[i] ); //закидваем имя найденого элемента в глобальный массив списка папок первой панели
else
fList2[0][j1][i] = ( ffd.cFileName[i] ); //закидваем имя найденого элемента в глобальный массив списка папок второй панели
if (ffd.cFileName[i]=='\0') break; //находим нулевой символ, и выходим из цикла
i++; //увеличиваем значение копируемого символа на единицу (инкремент)
}
j1++; //увеличиваем значение номера папки на еденицу (инкремент)
}
else //иначе , все тоже самое, но только для файлов
{
int i = 0;
while (true/*ffd.cFileName[i]!=0*/)
{
if (nPx==1)
fList1[1][j2][i] = ( ffd.cFileName[i] );
else
fList2[1][j2][i] = ( ffd.cFileName[i] );
if (ffd.cFileName[i]=='\0') break;
i++;
}
j2++;
}
} while (FindNextFileA(hFind, &ffd) != 0 && j1<450 && j2<450); //если элементы в каталоге еще есть и количество каталогов не превышает 450 и количество файлов не превышает 450,
//то выполняем цикл, иначе идем дальше
if (nPx==1) { //в зависимости от выбраной панели ,
countFd1 = j1; //сохраняем количество найденых файлов и
countFf1 = j2; //каталогов в глобальные переменные
} else {
countFd2 = j1;
countFf2 = j2;
}
dwError = GetLastError(); //получаем номер ошибки (если успешьно - то ERROR_NO_MORE_FILES)
if (dwError != ERROR_NO_MORE_FILES) //если ERROR_NO_MORE_FILES, значит произошла ошибка,
{
pdDialog("Произошла ошибка, при составлении списка файлов",0); //выводим сообщение о ней (тип которого с одной кнопкой ОК)
strcpy(dir,backUpDir); //возвращаем из бэкАпа строчку с путем
}
FindClose(hFind); //высвобождаем память
return dwError; //возвращаем значение функции
}
}
/**Установка размера консоли (x, y)*/
BOOL SetConsoleSize(SHORT x, SHORT y)
{
COORD size = {x, y}; //получаем объект типа "точка"
return ::SetConsoleScreenBufferSize(_ConsoleOut, size);//сообственно устанавливаем размер консоли
}
/**установить цвет текста + фона*/
BOOL setCol(int text, int bg)//АПИ-шная функция установки цвета соответственно текста и фона
{
return::SetConsoleTextAttribute(_ConsoleOut, text+(bg<<4)); //устанавливаем цвет текста+фона , подробнее cmd.exe "color()"
}
/**ф-ция которая устанавливает курсор в заданную позицию экрана, с координатами x, y*/
BOOL setPos(SHORT x, SHORT y) //функция установки курсора в позицию с координатами соответственно х и у
{
COORD pos = {x, y}; //определяем переменную типа "точка"
return ::SetConsoleCursorPosition(_ConsoleOut, pos); //устанавливаем курсор в точку с координатами "pos"
}
/**Определяет находимся ли мы в корне накопителя (отдельного логического диска). Нужна для того чтоб убрать лишние точки вначале списка файлов (или поставить таковые)*/
bool isCoren() //небольшая функция которая определяет является ли каталог в котором мы находимся корневым
{
//определяем переменные, и выделяем под них память
int i=0; //количество символов в строке
int j=0; //количество слешей в строке
while(true) //бесконечный цикл
{
if (nP==1)
{
if (dir1[i]==0) break; //если символ с индексом i равен нулю - это конец строки, выходим из цыкла
else i++; //иначе увеличиваем i на еденицу
if (dir1[i]=='\\')j++; //если символ с индексом i равен "\" - увеличиваем j на еденицу
} else {
if (dir2[i]==0) break; //если символ с индексом i равен нулю - это конец строки, выходим из цыкла
else i++; //иначе увеличиваем i на еденицу
if (dir2[i]=='\\')j++; //если символ с индексом i равен "\" - увеличиваем j на еденицу
}
}
if (j>1) return false; //если j>0 - значит мы не в корне - возращаем значение функции "ложь"
else return true; //иначе мы в корне, возвращаем возращаем значение функции "истина"
}
/**Определяет находимся ли мы на уровне, на котором выводятся все логические диски в системе*/
bool root(char *c) //с - путь каталога
{
int j=0; //будет хранить номер проверяемого симовла
for (int i=0;i<11;i++) //в этом цикле посимвольно проверяем,
if (c[i]=="FILE SYSTEM"[i])//является ли "с" строчкой с надписью "FILE SYSTEM", и
j++;//увеличивам значение проверяемого символа на еденицу (инкремент)
if (j==11) return true; //если все 11 символов совпадают, возращаем значение как истина
else return false;//иначе возвращаем "ложь"
}
/**Выводит на экран окно файлового показывателя)))*/
void paintFW(char* fn) //fn - file name
{
FILE* f=fopen(fn,"r"); //открываем файл
if(!f) //если неудачно,
exit(0);//то выходим
fpos_t dxF=0; //смещение при отображении файла, оно же - позиция начала чтения файла
int i=0; //хранит номер символа, выводимого в отдельной строке
fpos_t hh=0;//хранит смещение при чтении файла (установки позициичтеия)
fpos_t dxFx=79;
size_t xxx=1;
dx:
system("cls");//очищаем экран
setlocale (LC_CTYPE,".1251"); //устанавливаем кодовую страницу (для корректного отображения кириллицы)
setPos(0,0); //устанавливаем курсор в позицию
setCol(7,3);//устанавливаем цвет текста и фона символов
cout<<"Text file viewer version 0.03; WIN "; //выводим строку
char b[80]=""; //массив символов, который будет использоватся для вывода содержиого текстового файла
setCol(11,1);//устанавливаем цвет
hh=dxF; //присваиваем смещение
for (int j=0; j<23/*23*/; j++)//в этом цикле будем перебирать строчки
{
fsetpos(f,&hh); //перемещаем начало чтения на hh байт
xxx=fread(b,sizeof(b),1,f); //читаем файл
if (xxx==0) //если xxx==0 - значит достигнут конец файла, выводим последнюю строчку.
{
for (i=0;i<80; i++)
b[i]=0;
fsetpos(f,&hh); //перемещаем начало чтения на hh байт
fread(b,sizeof(b),1,f); //читаем файл
cout<<b;
break;
}
for (i=0;i<80;i++) //тут посимвольно выводим строчку
{
if (b[i]==9) cout<<" ";
else cout<<b[i];
if (b[i]==10 || b[i]==0) //10 = "\n"
{
break;
}
}
if (j==0) //сохраняем смещение (первой строчки)
{
dxFx=i+2;
}
hh+=i+2;
}
int key = getch(); //Получаем код клавиши
if (key==224)
key = getch();
switch(key) {
case 80://Down (если нажата клавиша "вниз", то)
{
if ( dxF+dxFx+23<ftell( f ) )
dxF+=dxFx;
break;
}
case 72://Up (если нажата клавиша "вверх", то)
{
dxF=0;
break;
}
case 27: //esc
case 61: //F3
case 68: //F10
goto dxc;
}
goto dx;
dxc:;
dxF=0;
fclose(f); //закрываем файл
}
/** Отрисовка панелей с файлами */
void paint(int as) //функция отрисовки (вывода на экран) 2-х панелей со списками каталогов и файлов
// as - определяет как именно рисовать экран:
//as=0 - полностью;
//as=1 - только левую панель;
//as=2 - только правую панель;
{
setCol(11,1); //устанавливаем цвет соответственно текста и фона
setlocale (LC_CTYPE,".866"); //устанавливаем кодировку символов в .866 (DOS)
setPos(0,0);//устанавливаем курсор в начало координат
switch(as) //в зависимости от того как именно рисовать переходим к отдельным частям кода
{
case 0: //Если полностью
/* тут занимаемся форматированием (красивое обрамление рамочек и тому подобное)*/
cout<<(char)(201); //выводим символ "╔" (обрамление верхнего левого угла)
for (int i=0;i<78;i++) //в этом цикле проходим от начала до конца по горизонтали и ортображаем верхнюю часть рамки
if (i==38) cout<<(char)(187);//выводим символ "╗"
else if (i==39) cout<<(char)(201);//выводим символ "╔"
else
{
//if (length(dir1)>i) cout<<(char)(205);
cout<<(char)(205); //выводим символ "═"
}
cout<<(char)(187);//выводим символ "╗"
setlocale (LC_CTYPE,".1251");
setPos( (40-strlen(dir1))/2,0); //утанавливаем курсор чтоб написать текущий путь в котором сейчас находимся
cout<<dir1; //выводим пусть к каталогу над первой панелькой
setPos( 40+(40-strlen(dir2))/2,0); //утанавливаем курсор чтоб написать текущий путь в котором сейчас находимся
cout<<dir2; //выводим пусть к каталогу над второй панелькой
setlocale (LC_CTYPE,".866");
for (int i=1;i<=22;i++) //в этом цикле выводим обрамления рамки в вертикали
{
setPos(0,i); //устанавливаем курсор в позицию (0,i)
cout<<(char)(186);//выводим символ "║"
setPos(40,i); //устанавливаем курсор в позицию (40,i)
cout<<(char)(186);//выводим символ "║"
}
setPos(0,23); //устанавливаем курсор в позицию (0,22)
cout<<(char)(200); //выводим символ "╚"
for (int i=0;i<78;i++) //в этом цикле отрисовываем нижнюю часть обрамления рамки панелей (горизонтальная)
if (i==38) cout<<(char)(188); //если номер символа по горизонтали 38, то выводим символ "╝"
else //иначе
if (i==39) cout<<(char)(200); //если номер символа 39, то выводим символ "╚"
else cout<<(char)(205); //иначе, выводим символ "═"
cout<<(char)(188); //выводим символ "╝"
//------теперь строка с кнопочками (менюшка внизу)------//
setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
cout<<"ENTER";//выводим стоку
setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
cout<<"RUN";//выводим стоку
setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
cout<<" F3";//выводим стоку
setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
cout<<"VIEW";//выводим стоку
setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
cout<<" F5";//выводим стоку
setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
cout<<"COPY";//выводим стоку
setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
cout<<" F6";//выводим стоку
setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
cout<<"REMAME";//выводим стоку
setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
cout<<" F7";//выводим стоку
setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
cout<<"REMOVE";//выводим стоку
setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
cout<<" F8";//выводим стоку
setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
cout<<"DELETE";//выводим стоку
setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
cout<<" F10";//выводим стоку
setCol(0,3);//устанавливаем цвет соответственно текста и фона шрифта
cout<<"EXIT";//выводим стоку
setCol(7,0);//устанавливаем цвет соответственно текста и фона шрифта
cout<<" ";//выводим стоку (это сделано для того чтоб небыло АртИФактов во время перехода между окнами)
/*-----------------------------------------------------------------------------*/
case 1: //если рисуем только первую,
case 2: //или вторую панель
int* dx=0; //условное смещение, для вывода списка файлов, большего чем максимально допустимо строчек по высоте
int* selItem = 0;//условный номер выбраной строчки
int* countFd = 0;//условное количество папок
int* countFf = 0;//условное количество файлов
for (int c=1;c<=2;c++) //с в данном случае является номером панели
{
if (as==1 && c==2) continue; //тут при некотором условии досрочно выходим из цикла (сделано для того, чтоб не тратить время на лишьние, ненужные отрисовки экрана)
else if (as==2 && c==1) continue;
if (c==1) { //если номер панели, которая отрсовывается на экране = 1, то
dx = &dx1; //приравниваем к условному смещению истинное смещение панели 1(Это значит что указатель dx получает адрес в памяти в зависимости от номера панели с которой мы работаем)
selItem = &selItem1; //также далее (все условное делаем истинным)
countFd = &countFd1;
countFf = &countFf1;
}else{ //иначе
dx = &dx2; //тоже самое для панели 2
selItem = &selItem2;
countFd = &countFd2;
countFf = &countFf2;
}
if (*selItem-*dx>=22) *dx=*selItem-22+1; //это сделано для того чтобы, если выбраный файл выходит за область отображения, список смещался
else if (*selItem-*dx<=-1) *dx=*selItem;
setPos(0,1); //устанавливаем курсор в позицию с координатами (0,1)
int k=0;
int z=0;
int countF=0;
for (int i=*dx;i<=(*countFd+*countFf)-1;i++) //в этом цикле выводим список файлов и каталогов
{
if (c==1) //если номер панели 1, то
setPos(/*4*/1,i+1-*dx); //устанавливаем курсор в
else
setPos(41,i+1-*dx);
if (i-*dx>=24-1-1) break;
k=0;
if (nP==1) //если мы работаем с панелью... тут мы ищем выделенную строчку, и соответственно рисуем ее
if (c==1){
if (i == *selItem ) setCol(7,3);
else setCol(11,1);
} else {
if (i == *selItem ) setCol(8,1);
else setCol(11,1);
}
else
if (c==1){
if (i == *selItem ) setCol(8,1);
else setCol(11,1);
} else {
if (i == *selItem ) setCol(7,3);
else setCol(11,1);
}
if (i> *countFd-1) //если i> *countFd-1 значит мы в списке папки закончили, теперь будем отображать файлы
{
z=1;
countF=*countFd;
}
setlocale (LC_CTYPE,".1251");
if (c==1) //в зависимости от выбраной панели выводим список файлов
while (fList1[z][i-countF][k]!=0)
{
if (k<38)
cout<<fList1[z][i-countF][k];
k++;
}
else
while (fList2[z][i-countF][k]!=0)
{
if (k<38)
cout<<fList2[z][i-countF][k];
k++;
}
for (int m=k;m<38;m++) //Здесь выводим пробелы, если имя файла или каталога меньше 39 символов
cout<<' '; //сообственно выводим пробелы
setlocale (LC_CTYPE,".866");
setCol(11,1);
if (k>38) { //если длина имени папки или файла больше чем 39 символов ( то выводим справа соответствующий символ)
k=38;
setCol(11,1);
cout<<"}"; //выводим символ "}"
} else
cout<<(char)(186); //выводим симовл "║"
k=i;
}
int kk = *countFd+*countFf;
if (kk<20) //к это высота (колво строчек по вертикали)
for (int m=k+2;m<23;m++) //дорисовываем пустые строчки
{
if (c==1)
setPos(1,m);
else
setPos(41,m);
cout<<" ";
cout<<(char)(186)<<endl;
}
}
}
}
/***/
void main () //главная функция, управление ей передается в начале выполнения программы
{
SetConsoleSize(WIDTH, HEIGHT); //устанавливаем размер буфера консоли
//Маленькая подпрограммка для определения кода клавиши от Геч()
/*mx:
cout<<getch()<<endl;
goto mx;
Маленькая подпрограммка для проверки работоспособности ф-ции геч()
mx:
if (getch()!=13) goto mx ;
*/
fileList(1);//формируем список файлов панели 1
fileList(2);//формируем список файлов панели 1
paint(0); //рисуем интерфейс, полностью
mx:
//указатени
int* selItem=0;
int* countFf=0;
int* countFd=0;
char* dir;
char* fList[2][450][MAX_PATH];
if (nP==1)
{
selItem=&selItem1;
countFf=&countFf1;
countFd=&countFd1;
dir=dir1;
for (int j=0;j<2;j++)
for (int k=0;k<450;k++)
for (int i=0;i<MAX_PATH/*strlen(fList1[j][k])+1*/;i++)
fList[j][k][i]=&fList1[j][k][i];
} else
{
selItem=&selItem2;
countFf=&countFf2;
countFd=&countFd2;
dir=dir2;
for (int j=0;j<2;j++)
for (int k=0;k<450;k++)
for (int i=0;i<MAX_PATH/*strlen(fList2[j][k])+1*/;i++)
fList[j][k][i]=&fList2[j][k][i];
}
int key = getch(); //получаем номер символа с клавиатуры
if (key==80) //Down (если нажата клавиша "вниз", то)
{
if (*selItem<*countFf+*countFd-1) ++*selItem;//если выделена не последняя строчка, увеличиваем значение, хранящееся по адресу *selItem
}
else
if (key==72) //Up (если нажата клавиша "вверх", то)
{
if (*selItem>=1) --*selItem; //если выделеный елемент больше чем нулевой, то уменьшаем значение, хранящееся по адресу *selItem
}
else if (key==79) //End
{
*selItem=*countFd+*countFf-1; //устанавливаем выделение на последний элемент
}
else if (key==71) //Home
{
*selItem=0; //устанавлдиваем выделение на первый елемент
}
else if (key==81) //Page Donw
{
//пролистываем на 1 страницу (22 строчки) вниз
if (*selItem+22<*countFd+*countFf-1) *selItem+=22;
else *selItem=*countFd+*countFf-1;
}
else if (key==73) //Page Up
{
//пролистываем на 1 страницу (22 строчки) вверх
if (*selItem-22>0) *selItem-=22;
else *selItem=0;
}
else if (key == 13) //Enter (если нажата клавиша "Ввод", то)
{
strcpy(backUpDir,dir); //сохраняем в бэкАп директорию которая была
if (root(dir)) //проверяем, если мы на уровне где показаны все логические диски в системе, то
{
//берем первых четыре символа и записываем в путь dir
for (int i=0;i<4;i++)
dir[i]=*fList[0][*selItem][i];
dir[3]=0;
fileList(nP); //после чего составляем список фалов
paint(0); //и отображаем их
*selItem=0; //номер выделения ставим в нуль
}
else //иначе
if (*selItem<*countFd)
{
if (*selItem==0)//здесь отрезаем от пути две точки, таким образом переходя на уровень вверх
{
int i=0;
int j=0;
while(true) // в этом цикле находим длину (i) и количество слешей (j) строки dir
{
if (dir[i]==0) break; //ищем нулевой символ в конце строки
else i++;
if (dir[i]=='\\')j++; //считаем слеши
}
if (j>1)
{
for (i;i>1;i--)
if (dir[i-2]=='\\')
{
dir[i-1]=0;
break;
}
else dir[i-1]=0;
} else
{
strcpy(dir,"FILE SYSTEM");
}
}
else //иначе добавляем выделенный каталог
{
strcat(dir, *fList[0][*selItem]); //конкатинация
strcat(dir, ("\\")); //добавляем замыкающий слеш
}
fileList(nP); //выполняем перестройку массива с папками и файлами
*selItem = 0; //устанавливаем выделение в начало (0)
paint(0); //Рисуем результат
} else {
ShellExecuteA(0, (""),(*fList[1][*selItem-*countFd]), (""),dir,SW_SHOWNORMAL); //API, запускает связаную с файлом програму, в которую сообщает параметр - "открыть этот файл"
}
}
else if (key == 27) { //esc (просто перерисовка экрана, если возникли АртИФакты (впринуипе можно удалить))
setPos(0,0);
paint(0); //Рисуем
}
else if (key == 68) goto lx; //F10 (если нажата клавиша "F10", то выходим)
else if (key == 61) //F3 //просмотр текстовых файлов
{
if(*selItem>=*countFd) //если выделение больше чем количество каталогов в списке то переходим к файловому просмотрщику
{
//тут получаем путь, соеденяем с выделенным элементом и сообщаем все это в paintFW
char x11[MAX_PATH];
strcpy(x11,dir);
strcat(x11,*fList[1][*selItem-*countFd]);
paintFW(x11);
}
paint(0); //перерисовываем панельки
}
else if (key >= 63 && key <= 66) //F5,F6,F7,F8 //операции копирования, переименования, перемещения, удаления файлов и папок (пустых и непустых)
{
if (!root(dir) && *selItem!=0) //если мы находимся не на уровне отображения логических дисков и выбраный елемент не равен нулю(переход вверх) то можем выполнять операции с файлами и папками
{
char mes[4][MAX_PATH]; //здесь хранится сообщение которое будет выводится в зависимости от нажатой клавиши
strcpy(mes[0],"Вы уверены что хотите скопировать?");
strcpy(mes[1],"Вы уверены что хотите переименовать?");
strcpy(mes[2],"Вы уверены что хотите переместить?");
strcpy(mes[3],"Вы уверены что хотите удалить?");
if (pdDialog(mes[key-63],1 )) //ввыводим сообщение подтверждения, в зависимости от нажатой кнопоки
{
char x11[MAX_PATH]; //откуда
char x12[MAX_PATH]; //куда
char* dirx;//куда
char* fn; //от куда
char fn2[MAX_PATH];
if (nP==1) //в зависимости от выделенной панели
{
if (key == 64) //F6 (если нажато F6, то мы переименовываем, и нам не нужно перемещать элемент в другую панель)
dirx=dir1;
else
dirx=dir2;
if (selItem1<countFd1)
fn=fList1[0][selItem1]; //сообщаем в указатель "от куда брать" элемент из списка
else
fn=fList1[1][selItem1-countFd1];
}
else
{
if (key == 64) //F6 (если нажато F6, то мы переименовываем, и нам не нужно перемещать элемент в другую панель)
dirx=dir2;
else
dirx=dir1;
if (selItem2<countFd2)
fn=fList2[0][selItem2];
else
fn=fList2[1][selItem2-countFd2];
}
paint(0); //перерисовываем панельки
strcpy(fn2,fn);
if (key == 64 ) //если F6
{
pdDialog("Пожалуйста введите новое имя (включая разширение)",3,fn2);
OemToCharA(fn2,fn2);
}
else ;
strcpy(x11,dir); //копируем строку из dir1 в x11
strcat(x11,fn); //конкатинация
strcat(x11,"\0"); //конкатинация
for (int i=strlen(x11);i<MAX_PATH;i++) //в этом цикле обнуляем все символы с которые находятся после конца строки (зачем не знаю, но если не обнулить - Виндуз ругается)
x11[i]=0;
strcpy(x12,dirx); //копируем строку из dirx в x12
strcat(x12,fn2); //конкатинация
strcat(x12,"\0"); //конкатинация
for (int i=strlen(x12);i<MAX_PATH;i++) //в этом цикле обнуляем все символы с которые находятся после конца строки (зачем не знаю, но если не обнулить - Виндуз ругается)
x12[i]=0;
/*Вот наша структура (и чтоб никто не посмел сказать что у нас ее нет))))*/
//АПИ, взято от сюда http://msdn.microsoft.com/uk-ua/library/bb762164(VS.85).aspx
SHFILEOPSTRUCTA lpFileOp; //связываем значения переменной, SHFILEOPSTRUCTA - структура файловых операций
//устанавливаем разного рода параметры
lpFileOp.hwnd=NULL; //хандл
switch(key)
{
case 63:
lpFileOp.wFunc=FO_COPY; //копировать
break;
case 64:
lpFileOp.wFunc=FO_RENAME; //переименовать
break;
case 65:
lpFileOp.wFunc=FO_MOVE; //переместить
break;
case 66:
lpFileOp.wFunc=FO_DELETE; //удалить
break;
}
lpFileOp.pFrom=x11; //от куда берем //конченный pFrom терпеть не может символы с номером -52 (я полагаю и любой другой отрецательный)
lpFileOp.pTo=x12; //куда копируем/перемещаем/переименовываем и т.п.
lpFileOp.fFlags=FOF_NOERRORUI|FOF_NOCONFIRMATION|FOF_SILENT; //устанавливаем параметре - без сообщения об ошибках|без подтверждения|тихо(все надо делать молча)
lpFileOp.fAnyOperationsAborted=FALSE; //откат операции = ложь
lpFileOp.hNameMappings=NULL; //маска имен = нуль(пустой указатель, который ни на что не указывает)
lpFileOp.lpszProgressTitle=NULL; //чегото связанное с процессами = нуль(пустой указатель, который ни на что не указывает)
pdDialog("подождите...",2);
int is = SHFileOperationA(&lpFileOp);//здесь собственно выполняем операцию, и ее результат записываем в is
//перестраиваем списки файлов, обоих панелей
fileList(2);
fileList(1);
paint(0); //рисуем пенельки (обе + рамка)
char mes2[4][MAX_PATH]; //текст сообщения
strcpy(mes2[0],"Скопировано.");
strcpy(mes2[1],"Переименовано.");
strcpy(mes2[2],"Перемещено.");
strcpy(mes2[3],"Удалено.");
char mes3[4][MAX_PATH]; //текст сообщения
strcpy(mes3[0],"Объект не скопирован");
strcpy(mes3[1],"Объект не переименован");
strcpy(mes3[2],"Объект не перемещен");
strcpy(mes3[3],"Объект не удален");
if (is == 0 )
{
if (key == 66 || key == 65) //если нажата кнопка F7 или F8 (перемещение и удаление) то необходимо уменьшить выделение на 1 (потому, что после удачной операции исчезнет из списка)
--*selItem; //уменьшаем на еденицу (декремент)
pdDialog(mes2[key-63],0);//если is=0, то выводим сообщение, про удачно проведенную операцию
}else pdDialog(mes3[key-63],0);//если is не равно 0, то выводим сообщение про НЕудачно проведенную операцию
}
paint(0); //перерисовываем панельки (всё, включая обрамление)
}
}
else if (key == 9) { //Tab, если нажат "Таб", переходим к панели (не той что была, а другой)
if (nP==1)
nP=2;
else
nP=1;
paint(0); //рисуем
goto mx; //уходим в место "mx"
}
paint(nP);//рисуем главную панель файлового исследователя (только одну и без обрамления)
setPos(0,0); //устанавливаем курсор в начало координат
goto mx;/**/
lx:;
/*это для того чтобы отображать в конце программы строчку "для завершения нажмите любую клавишу..." белым шрифтом и посередине окна*/
setCol(0,15);
setPos(15,HEIGHT/2);
}
/** Коды клавишь полученые с помощью ф-ции getch();
key | code
Up | 224; 72;
left | 224; 75;
down | 224; 80;
right | 224; 77;
Enter | 13;
Escape | 27;
F1..F10 | 0; 59 .... 0; 68;
F11 | 224; 133;
F12 | 224; 134;
Tab | 9;
0..9 | 48 .. 57;
PageUp | 224; 73;
PageDown| 224; 81;
Home | 224; 71;
End | 224; 79;
Ins | 224; 82;
Delete | 224; 83;
На дополнительной клавиатуне при выключеном NumLock
коды теже, но первый флаг = 0
*/
/** Номера символов в кодировке DOS
0
☺ 1
☻ 2
♥ 3
♦ 4
♣ 5
♠ 6
7
8
9
10
♂ 11
♀ 12
13
♫ 14
☼ 15
► 16
◄ 17
↕ 18
‼ 19
¶ 20
§ 21
▬ 22
↨ 23
↑ 24
↓ 25
→ 26
← 27
∟ 28
↔ 29
▲ 30
▼ 31
32
! 33
" 34
# 35
$ 36
% 37
& 38
' 39
( 40
) 41
* 42
+ 43
, 44
- 45
. 46
/ 47
0 48
1 49
2 50
3 51
4 52
5 53
6 54
7 55
8 56
9 57
: 58
; 59
< 60
= 61
> 62
? 63
@ 64
A 65
B 66
C 67
D 68
E 69
F 70
G 71
H 72
I 73
J 74
K 75
L 76
M 77
N 78
O 79
P 80
Q 81
R 82
S 83
T 84
U 85
V 86
W 87
X 88
Y 89
Z 90
[ 91
\ 92
] 93
^ 94
_ 95
` 96
a 97
b 98
c 99
d 100
e 101
f 102
g 103
h 104
i 105
j 106
k 107
l 108
m 109
n 110
o 111
p 112
q 113
r 114
s 115
t 116
u 117
v 118
w 119
x 120
y 121
z 122
{ 123
| 124
} 125
~ 126
⌂ 127
А 128
Б 129
В 130
Г 131
Д 132
Е 133
Ж 134
З 135
И 136
Й 137
К 138
Л 139
М 140
Н 141
О 142
П 143
Р 144
С 145
Т 146
У 147
Ф 148
Х 149
Ц 150
Ч 151
Ш 152
Щ 153
Ъ 154
Ы 155
Ь 156
Э 157
Ю 158
Я 159
а 160
б 161
в 162
г 163
д 164
е 165
ж 166
з 167
и 168
й 169
к 170
л 171
м 172
н 173
о 174
п 175
░ 176
▒ 177
▓ 178
│ 179
┤ 180
╡ 181
╢ 182
╖ 183
╕ 184
╣ 185
║ 186
╗ 187
╝ 188
╜ 189
╛ 190
┐ 191
└ 192
┴ 193
┬ 194
├ 195
─ 196
┼ 197
╞ 198
╟ 199
╚ 200
╔ 201
╩ 202
╦ 203
╠ 204
═ 205
╬ 206
╧ 207
╨ 208
╤ 209
╥ 210
╙ 211
╘ 212
╒ 213
╓ 214
╫ 215
╪ 216
┘ 217
┌ 218
█ 219
▄ 220
▌ 221
▐ 222
▀ 223
р 224
с 225
т 226
у 227
ф 228
х 229
ц 230
ч 231
ш 232
щ 233
ъ 234
ы 235
ь 236
э 237
ю 238
я 239
Ё 240
ё 241
Є 242
є 243
Ї 244
ї 245
Ў 246
ў 247
° 248
∙ 249
· 250
√ 251
№ 252
¤ 253
■ 254
255
*/
Все це було написане в Visual Studio 2005 дев'ятнадцятирічним мною для моєї дівчини (тоді), з російськими коментарями, уж вибачайте.
В додатках,
file_explorer.zip - готовий ехешник;
mainX.zip - cpp файл з кодом.
Проект з студії не додаю,
якщо ви настільки дегенеративний студент, що не можете зробити проект, то мені вам нічим допомогти.
file_explorer.zip 64.65 kb, 391 downloads since 2018-02-18
mainX.zip 15.35 kb, 473 downloads since 2018-02-18
ScreenShot_fe4.png 83.01 kb, 178 downloads since 2018-02-18