//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "MainForm.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TfrmMain *frmMain;
//---------------------------------------------------------------------------
/*
Constructor
*/
__fastcall TfrmMain::TfrmMain(TComponent* Owner) : TForm(Owner)
{
}
//---------------------------------------------------------------------------
/*
Given the name of a file on the disk, retrieve information from it
(e.g. date/time, size, etc.)
*/
TfrmMain::FileInfo TfrmMain::GetFileInfo(const AnsiString &Filename)
{
FileInfo finfo;
TSearchRec rec; // For FindFirst
// Separate the full name into parts
finfo.name = ExtractFileName(Filename);
finfo.path = ExtractFilePath(Filename);
finfo.extension = ExtractFileExt(Filename);
// Returns 0 on success TODO: need handle failures at some point
if (!FindFirst(Filename, faAnyFile, rec))
{
// File size (64-bit integer)
finfo.size = rec.Size;
// Convert file date format to a "better" format
finfo.date = FileDateToDateTime(rec.Time);
// File or Directory? Directories won't display a size (which is 0)
if (rec.Attr & faDirectory)
finfo.isfolder = true;
else
finfo.isfolder = false;
}
return finfo;
}
/*
Given a filename (Drive:/Path/Filename.ext), add it to the list view
*/
void TfrmMain::AddFile(const char *Filename)
{
// First, extract all of the file's information (size, date, etc.)
FileInfo finfo = GetFileInfo(Filename);
// 'Add' creates a new, empty item in the list view
TListItem *item = lstFiles->Items->Add();
// Set each "field" in the list view
item->Caption = finfo.name;
item->SubItems->Add(finfo.size);
item->SubItems->Add(finfo.extension);
item->SubItems->Add(finfo.date);
item->SubItems->Add(finfo.path);
// Files and folders have different icons
if (finfo.isfolder)
item->ImageIndex = 1;
else
item->ImageIndex = 0;
}
/*
Callback when comparing the first field in the list view
*/
void __fastcall TfrmMain::CompareTextFields(TObject *Sender, TListItem* Item1, TListItem* Item2, int Data, int& Compare)
{
Compare = CompareText(Item1->Caption, Item2->Caption);
}
/*
Callback when comparing the Size field
*/
void __fastcall TfrmMain::CompareIntegerFields(TObject *Sender, TListItem* Item1, TListItem* Item2, int Data, int& Compare)
{
Compare = StrToInt(Item2->SubItems->Strings[0]) < StrToInt(Item1->SubItems->Strings[0]);
}
/*
Callback when comparing the Date field
*/
void __fastcall TfrmMain::CompareDateFields(TObject *Sender, TListItem* Item1, TListItem* Item2, int Data, int& Compare)
{
Compare = StrToDateTime(Item2->SubItems->Strings[2]) < StrToDateTime(Item1->SubItems->Strings[2]);
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Event Handlers
///////////////////////////////////////////////////////////////////////////////
/*
Handler for the form create event.
*/
void __fastcall TfrmMain::FormCreate(TObject *Sender)
{
lstFiles->Align = alClient;
// Enable files to be dropped onto the form (a drop target)
DragAcceptFiles(this->Handle, true);
}
//---------------------------------------------------------------------------
/*
Handler for the form destroy event.
*/
void __fastcall TfrmMain::FormDestroy(TObject *Sender)
{
// Disable the form as a drop target
DragAcceptFiles(this->Handle, false);
}
//---------------------------------------------------------------------------
/*
Handles the event of clicking on one of the column headers in the list view.
We need to sort on that column
*/
void __fastcall TfrmMain::lstFilesColumnClick(TObject *Sender, TListColumn *Column)
{
// Set comparison callback based on the column that was clicked
if (Column->Index == 0)
lstFiles->OnCompare = CompareTextFields;
else if (Column->Index == 1)
lstFiles->OnCompare = CompareIntegerFields;
else if (Column->Index == 3)
lstFiles->OnCompare = CompareDateFields;
else
return;
// enum TSortType { stNone, stData, stText, stBoth };
lstFiles->SortType = Comctrls::stData; // Set sort type
lstFiles->AlphaSort(); // Perform the sort
lstFiles->SortType = Comctrls::stNone; // Reset sort to None
}
//---------------------------------------------------------------------------
/*
This event handler handles *all* of the buttons on the toolbar. It
checks their Tag to see which one invoked it, so the proper view
can be set.
*/
void __fastcall TfrmMain::btnChangeViewClick(TObject *Sender)
{
// TODO: need to deal with the (albeit, unlikely) case that Sender
// is not a TButton*
TButton *button = (TButton *)Sender;
// Now set the appropriate view
if (button->Tag == 0)
lstFiles->ViewStyle = vsReport;
else if (button->Tag == 1)
lstFiles->ViewStyle = vsList;
else if (button->Tag == 2)
lstFiles->ViewStyle = vsSmallIcon;
else if (button->Tag == 3)
lstFiles->ViewStyle = vsIcon;
}
//---------------------------------------------------------------------------
/*
When files are dropped onto the form, this handles the event.
*/
void __fastcall TfrmMain::WMDropFiles(TWMDropFiles &Message)
{
int num_files, filename_length;
char file_name[MAX_PATH + 1];
// Refer to the Win SDK documentation about the details
// of DragQueryFile and the HDROP structure
// Get the number of files that are being dropped on the app
num_files = DragQueryFile((HDROP)Message.Drop, -1, NULL, 0);
if (num_files < 1)
return;
// Retrieve each filename, one at a time
for(int i = 0; i < num_files; i++)
{
// Get the length of the i'th filename
filename_length = DragQueryFile((HDROP)Message.Drop, i, NULL, 0);
// Retrieve the i'th filename from the HDROP structure
DragQueryFile((HDROP)Message.Drop, i, file_name, filename_length + 1);
// Add the file to the list
AddFile(file_name);
}
}
/*
When the items are dropped, this function handles that event.
Source is the component where the items are coming from and Sender
is the component being dropped onto. Currently, we're only handling
the case when we drag and drop on the same list view.
*/
void __fastcall TfrmMain::lstFilesDragDrop(TObject *Sender, TObject *Source, int X, int Y)
{
// We are dropping within our own list view
if (Sender == Source)
{
TListView* lv = (TListView *)Sender;
TListItem* currentItem, *nextItem, *dragItem, *dropItem;
// This is the item that we are dropping on
dropItem = lv->GetItemAt(X, Y);
// This is the item that is selected. There may be more than one
// highlighted, but only one is the "selected" or "active" item.
currentItem = lv->Selected;
// Loop through all of the highlighted items and drop them
while (currentItem != NULL)
{
// From the help:
// TListItem *GetNextItem(StartItem, Direction, States)
// Call GetNextItem to find the next list item after StartItem in the
// direction given by the Direction parameter. Only items in the state
// indicated by the States parameter are considered.
nextItem = lv->GetNextItem(currentItem, sdAll, TItemStates() << isSelected) ;
// If drop item is non-NULL, it's a valid position in the list and we
// will insert there. If it's NULL, then we are below the last item in
// the list, so we will Add to the end.
if (dropItem)
dragItem = lv->Items->Insert(dropItem->Index);
else
dragItem = lv->Items->Add();
// Copy the data from the source to the new item
dragItem->Assign(currentItem);
// Free the old one (don't want two of them in the list)
delete currentItem;
// Move to the next one that is highlighted (if any)
currentItem = nextItem;
}
}
}
//---------------------------------------------------------------------------
/*
When a drag operation is over the list view, this code decides if the
contents can be dropped. If this function returns true, then the mouse
pointer changes to indicate it's a drop target. If it returns false,
then the mouse pointer changes to indicate you can't drop here.
*/
void __fastcall TfrmMain::lstFilesDragOver(TObject *Sender, TObject *Source, int X, int Y, TDragState State, bool &Accept)
{
Accept = false;
// Only accept drops from our control.
if (Sender != lstFiles)
return;
TListView *lv = (TListView *)Sender;
// Get the item that the mouse is over
TListItem *item = lv->GetItemAt(X, Y);
// Not going to allow moving an item onto itself. Go ahead, remove this
// check and see what happens...
if (item && item->Selected)
return;
// Passed the checks, so we will allow it to be dropped.
Accept = true;
}
//---------------------------------------------------------------------------