#if __APPLE_CC__
#include <Carbon/Carbon.h>
#elif TARGET_RT_MAC_MACHO
#include <Carbon/Carbon.h>
#else
#include <Devices.h>
#include <Dialogs.h>
#include <ToolUtils.h>
#include <Gestalt.h>
#include <Folders.h>
#endif

#include "Blugs.h"
#include "BlugsDemo.h"
#include "BlugsAppearanceExtras.h"
#include "Finderesque.h"
#include "DateTimeHandler.h"
#include "SingleLineCFString.h"
#include "ColorPopupHandler.h"
#include "RowNumberHandler.h"
#include "CheckboxHandler.h"
#include "PopupMenuContentHandler.h"
#include "EditStringHandler.h"
#include "Frame3DHiliteProc.h"
#include "IconHandler.h"
#include "PictureHandler.h"



static void		ShowTableWindow( void );
static void		ShowSimpleWindow( void );
static void		ShowUtilityWindow( void );
static void		SetUpPictureList( void );
static pascal OSStatus
				TableWindowEventHandler( EventHandlerCallRef myHandler, EventRef inEvent, void* userData );
static pascal OSStatus
				DoAppCommandProcess( EventHandlerCallRef nextHandler, EventRef inEvent, void* userData );
static UInt8	DecimalKeyFilterProc( Ptr inTextBefore, UInt32 inTextLengthBefore, Ptr inTextAfter,
										UInt32 inTextLengthAfter, UInt8* ioKey );		
//static pascal OSErr	trackingHandler(DragTrackingMessage message, WindowRef theWindow, void *refCon, DragReference theDragRef);
//static pascal OSErr	receiveHandler(WindowRef theWindow, void *refCon, DragReference theDragRef);
static pascal OSErr
				MyDragDataProc( DragReference inDragRef, DragItemRef inItem,
								BLCell inCell, BlugsRef inList );
static pascal OSErr MyDropValidationProc( DragReference inDragRef, UInt16 inUnderThisRow,
										UInt16 inDisclosureLevel, BlugsRef inList );
static pascal void	MyDropProc( DragReference inDragRef, UInt16 inUnderThisRow,
								UInt16 inDisclosureLevel, BlugsRef inList );
static pascal void	MyPostDragProc( DragReference inDragRef, BlugsRef inList );
static void		AddAllVolumesToList( BlugsRef list );
static void		AddDirectoryToList( SInt16 vRefNum, SInt32 dirID, UInt16 firstRow, BlugsRef list );
static pascal void	MyRowExpandProc( Boolean expanding, UInt16 expandedRow, BlugsRef list );
static pascal OSErr	MyBackgroundProc( Boolean inIsSortColumn, BLCell inCell,
										const Rect* inRect, BlugsRef inList );
static pascal void	MyNotificationProc( BLNotificationMessage inMessage, UInt32 inCommand,
										BLPart inPart, BLCell inCell, BlugsRef inList );
static Boolean	DropLocationIsTrash( DragReference inDrag );
static OSErr	DropFSSpec( DragReference inDrag, FSSpecPtr outSpec );
static pascal void MyFlattenProc( BLCell inCell, OSType* outDataFlavor, UInt32* ioDataSize,
									void* outData, BlugsRef inList );
static UInt16	FindColumnByID( OSType inID, BlugsRef inList );
static pascal OSErr QuitAppleEventHandler( const AppleEvent *inAppleEvt, AppleEvent* inReply,
											SInt32 inRefcon );


//DragTrackingHandlerUPP	gTrackingHandlerUPP;
//DragReceiveHandlerUPP	gReceiveHandlerUPP;
WindowRef				gTableWindow = nil, gUtilityWindow = nil, gSimpleWindow = nil,
						gPictureWindow = nil;
CFBundleRef 			gBundle = nil;
IBNibRef				gNibs = nil;
EventHandlerUPP			gAppCommandProcess, gWinEvtHandler;
static const ControlID	gTableID = {'main',1};

#define kMainNibFileName "BlugsDemo"
#define kMainWindowNibName "MainWindow"
#define kUtilityWindowNibName "UtilityWindow"
#define kMenuBarNibName "MainMenu"

//===============================
void main( void )
{
	OSErr			err;
	BlugsRef		list;
	EventTypeSpec	eventTypes[] = { {kEventClassCommand, kEventCommandProcess} };
	
	err = TXNInitTextension( nil, 0, 0 );
	gBundle = CFBundleGetMainBundle();
	err = CreateNibReferenceWithCFBundle( gBundle, CFSTR(kMainNibFileName), &gNibs );
	err = SetMenuBarFromNib( gNibs, CFSTR(kMenuBarNibName) );
	err = AEInstallEventHandler( kCoreEventClass, kAEQuitApplication,
									NewAEEventHandlerUPP( QuitAppleEventHandler ), 0, false );
	// Yes, the error handling is suboptimal.
	err = BLRegisterContentHandler( kFinderesqueContentType, 0, Finderesque );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kStringContentType, 1, SingleLineCFStringHandler );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kDateTimeContentType, 1, DateTimeHandler );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kColorPopupContentType, 0, ColorPopupHandler );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kRowNumberContentType, 1, RowNumberHandler );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kPopupMenuContentType, 1, PopupMenuContentHandler );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kCheckboxContentType, 1, CheckboxHandler );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kEditStringContentType, 0, EditString );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kIconContentType, 0, IconHandler );
	if (err) ExitToShell();
	err = BLRegisterContentHandler( kPictureContentType, 0, PictureHandler );
	if (err) ExitToShell();
	//gTrackingHandlerUPP = NewDragTrackingHandlerUPP( trackingHandler );
	//gReceiveHandlerUPP = NewDragReceiveHandlerUPP( receiveHandler );
	//err = InstallTrackingHandler( gTrackingHandlerUPP, nil, nil );
	//err = InstallReceiveHandler( gReceiveHandlerUPP, nil, nil );
	ShowTableWindow();
	ShowSimpleWindow();
	ShowUtilityWindow();
    gAppCommandProcess = NewEventHandlerUPP( DoAppCommandProcess );
	InstallApplicationEventHandler( gAppCommandProcess, GetEventTypeCount(eventTypes), eventTypes, nil, nil );
	RunApplicationEventLoop();
	list = (BlugsRef)GetWRefCon( gTableWindow );
	BLDispose( list );
	list = (BlugsRef)GetWRefCon( gSimpleWindow );
	BLDispose( list );
	list = (BlugsRef)GetWRefCon( gUtilityWindow );
	BLDispose( list );
}


//===============================
static void ShowTableWindow( void )
{
	OSErr			err;
	ControlRef		control;
	
	err = CreateWindowFromNib( gNibs, CFSTR(kMainWindowNibName), &gTableWindow );
	if (!err)
	{
		EventTypeSpec	eventTypes[] = { {kEventClassWindow, kEventWindowBoundsChanging },
										{kEventClassCommand, kEventCommandProcess} };
		EventHandlerRef	ref;
		SInt16			ht;
		RgnHandle		rgn = NewRgn();
		Rect			r;
		BlugsRef		list;
		
		SetAutomaticControlDragTrackingEnabledForWindow( gTableWindow, true );
		gWinEvtHandler = NewEventHandlerUPP(TableWindowEventHandler);
		InstallWindowEventHandler( gTableWindow, gWinEvtHandler, GetEventTypeCount(eventTypes), eventTypes, 0, &ref );
		SetPortWindowPort( gTableWindow );
		list = BLLoad( 128, gTableWindow );
		if (list)
		{
			BLWidgetInfo		wInfo;
			HMStringResType		resType;
			BLCell				cell = {0,0}; // top left corner
			ControlFontStyleRec	fontStyle;
			SInt16				iconID;
			BLCallbacksRec		callbacks = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil};
			
			resType.hmmResID = 128;
			resType.hmmIndex = 1;
			err = BLSetCellContentType( cell, kStringContentType, list );
			if (!err) BLSetCellData( cell, 'STR#', sizeof(HMStringResType), &resType, list );
			//err = BLConvertUserPaneControl( true, control, list );
			err = BLMakeCarbonControl( &control, list );
			if (!err) SetControlID( control, &gTableID );
			AddAllVolumesToList( list );
			callbacks.rowExpandProc = MyRowExpandProc;
			callbacks.dragDataProc = MyDragDataProc;
			callbacks.dropValidationProc = MyDropValidationProc;
			callbacks.dropProc = MyDropProc;
			callbacks.postDragProc = MyPostDragProc;
			callbacks.notificationProc = MyNotificationProc;
			(void)BLSetCallbacks( &callbacks, list );
			BLSetUserData( kWindowIDKey, kTableWindowID, list );
			BLSelectTitle( false, 1, list );
			err = BLGetViewsFont( &fontStyle.font, &fontStyle.size );
			if (!err)
			{
				fontStyle.flags =  kControlUseFontMask | kControlUseSizeMask;
				(void)BLSetColumnFontStyle( 1, &fontStyle, list );
				(void)BLSetColumnFontStyle( 2, &fontStyle, list );
				(void)BLSetColumnFontStyle( 3, &fontStyle, list );
			}
			// Let's add a couple widgets to the horizontal scroll bar.
			err = BLAddWidgets( false, 3, 1, list );
			if (!err)
			{
				wInfo.content = kIconContentType;
				wInfo.whichWidgetFlags = wInfo.widgetFlags = blWidgetSticky | blWidgetToggles | blWidgetRadioGroup;
				err = BLSetWidgetInfo( false, 1, blSetWidgetContentType | blSetWidgetFlags, &wInfo, list );
				err = BLSetWidgetInfo( false, 2, blSetWidgetFlags, &wInfo, list );
				wInfo.content = kPopupMenuContentType;
				wInfo.size = 100;
				err = BLSetWidgetInfo( false, 3, blSetWidgetContentType | blSetWidgetSize, &wInfo, list );
				iconID = 200;
				err = BLSetWidgetData( false, 1, kIconResFlavor, sizeof(SInt16), &iconID, list );
				{
					BLPopupMenuResRec	popupRec;
					
					popupRec.menuID = 5002;
					popupRec.value = 1;
					popupRec.fontNumber = 1;
					popupRec.fontSize = 9;
					err = BLSetWidgetData( false, 3, kPopupMenuHandler_ResourceDataFlavor,
											sizeof(popupRec), &popupRec, list );
				}
			}
			// And let's add a widget to the vertical scroll bar, just so it doesn't get jealous.
			err = BLAddWidgets( true, 1, 1, list );
			if (!err)
			{
				wInfo.content = kIconContentType;
				err = BLSetWidgetInfo( true, 1, blSetWidgetContentType, &wInfo, list );
				iconID = 201;
				err = BLSetWidgetData( true, 1, kIconResFlavor, sizeof(SInt16), &iconID, list );
			}
			BLSetAutodraw( true, list );
			//BLSetDisclosureTriangleColumn( 2, list );
			SetWRefCon( gTableWindow, (UInt32)list );
		}
		err = GetWindowRegion( gTableWindow, kWindowStructureRgn, rgn );
		GetRegionBounds( rgn, &r );
		DisposeRgn( rgn );
		GetThemeMenuBarHeight( &ht );
		OffsetRect( &r, -r.left, -r.top + ht );
		err = TransitionWindow( gTableWindow, kWindowZoomTransitionEffect, kWindowShowTransitionAction, nil );
		err = TransitionWindow( gTableWindow, kWindowSlideTransitionEffect, kWindowMoveTransitionAction, &r );
		/*BLDumpList( "Table list", list );
		HIObjectPrintDebugInfo( (HIObjectRef)control );
		{
			FSSpec	fileSpec = {1,1,"ControlDump.txt"};
			
			err = FSMakeFSSpec( 0, 0, "ControlDump.txt", &fileSpec );
			(void)DumpControlHierarchy( gTableWindow, &fileSpec );
		}*/
	}
}

//===============================
static void ShowSimpleWindow( void )
{
	Rect		r = {100,100,226,220};
	BlugsRef	list;
	OSErr		err;
	ControlRef	control;
	
	CreateNewWindow( kDocumentWindowClass, kWindowStandardDocumentAttributes |
					kWindowStandardHandlerAttribute, &r, &gSimpleWindow );
	SetPortWindowPort( gSimpleWindow );
	SetAutomaticControlDragTrackingEnabledForWindow( gTableWindow, true );
	list = BLLoad( kSimpleLiSt, gSimpleWindow );
	err = BLMakeCarbonControl( &control, list );
	err = BLRegisterHiliteProc( &WowItsITunesHiliteProc, list );
	BLSetUserData( kWindowIDKey, kSimpleWindowID, list );
	BLRegisterBackgroundProc( &MyBackgroundProc, list );
	BLRegisterDragDataProc( &MyDragDataProc, list );
	BLRegisterPostDragProc( &MyPostDragProc, list );
	BLSetAutodraw( true, list );
	SetWRefCon( gSimpleWindow, (UInt32)list );
	ShowWindow( gSimpleWindow );
	//BLUpdate( nil, (BlugsRef)GetWRefCon( gSimpleWindow ) );
	
	/*gPictureWindow = GetNewCWindow( kSimpleWIND, nil, kFirstWindowOfClass );
	SetPortWindowPort( gPictureWindow );
	SetUpPictureList();
	ShowWindow( gPictureWindow );
	BLUpdate( nil, (BlugsRef)GetWRefCon( gPictureWindow ) );*/
}

//===============================
static void ShowUtilityWindow( void )
{
	OSErr	err;
	
	err = CreateWindowFromNib( gNibs, CFSTR(kUtilityWindowNibName), &gUtilityWindow );
	if (!err)
	{
		BlugsRef			list;
		BLCell				cell;
		UInt16				keyFilterRows[5] = {3,4,5,7,8}, i;	
		ControlFontStyleRec	fontStyle;
		ControlID			id = {'Blug',2};
		EventTypeSpec		eventTypes[] = { {kEventClassWindow, kEventWindowClose },
											{kEventClassCommand, kEventCommandProcess} };
		EventHandlerRef		ref;
		ControlRef			control;
		
		SetAutomaticControlDragTrackingEnabledForWindow( gUtilityWindow, true );
		InstallWindowEventHandler( gUtilityWindow, gWinEvtHandler, GetEventTypeCount(eventTypes), eventTypes, 0, &ref );
		SetPortWindowPort( gUtilityWindow );
		err = GetControlByID( gUtilityWindow, &id, &control );
		if (!err)
		{
			list = BLLoad( kUtilityLiSt, gUtilityWindow );
			err = BLConvertUserPaneControl( true, control, list );
			BLSetUserData( kWindowIDKey, kUtilityWindowID, list );
			cell.col = 2;
			for (i = 0; i <= 4; i++)
			{
				OSErr	err;
				
				cell.row = keyFilterRows[i];
				err = BLSetCellData( cell, kKeyFilterFlavor, sizeof(BLKeyFilterProcPtr),
									&DecimalKeyFilterProc, list );
				if (err) break;
			}
			fontStyle.flags =  kControlUseFontMask | kControlUseFaceMask | kControlUseSizeMask;
			fontStyle.font = 1;
			fontStyle.size = 9;
			fontStyle.style = bold;
			(void)BLSetColumnFontStyle( 1, &fontStyle, list );
			BLSetIndent( 6, list );
			BLRegisterDragDataProc( &MyDragDataProc, list );
			BLSetVisible( true, list );
			BLSetAutodraw( true, list );
			err = BLRegisterHiliteProc( &WowItsITunesHiliteProc, list );
			SetWRefCon( gUtilityWindow, (UInt32)list );
		}
		ShowWindow( gUtilityWindow );
	}
}

//===============================
static void SetUpPictureList( void )
{
	BlugsRef		list;
	OSErr			err;
	PicHandle		pic;
	SInt16			i;
	BLCell			cell;
	
	list = BLLoad( kPictureLiSt, gPictureWindow );
	BLSetUserData( kWindowIDKey, kPictureWindowID, list );
	BLRegisterBackgroundProc( &MyBackgroundProc, list );
	BLRegisterDragDataProc( &MyDragDataProc, list );
	BLRegisterPostDragProc( &MyPostDragProc, list );
	cell.col = 1;
	for (i = 1; i <= 7; i++)
	{
		pic = GetPicture( 128 );
		if (pic)
		{
			cell.row = i;
			err = BLSetCellData( cell, kScrapFlavorTypePicture, sizeof(Handle), &pic, list );
		}
	}
	BLSetAutodraw( true, list );
	SetWRefCon( gPictureWindow, (UInt32)list );
}

//===============================
static pascal OSStatus DoAppCommandProcess( EventHandlerCallRef nextHandler, EventRef inEvent, void* userData )
{
#pragma unused (nextHandler, userData)
	HICommand  command;
	OSStatus   result = eventNotHandledErr;

	GetEventParameter( inEvent, kEventParamDirectObject, typeHICommand, NULL,
						sizeof(HICommand), NULL, &command );
	switch (command.commandID)
	{
		case kHICommandAbout:
		//DoAboutBox();
		result = noErr; 
		break;
			
		case kHICommandQuit:
		QuitApplicationEventLoop();
		result = noErr;
		break;
	}
	HiliteMenu(0);
    return result;
}

//===============================
static pascal OSStatus TableWindowEventHandler( EventHandlerCallRef myHandler,
												EventRef inEvent, void* userData )
{
#pragma unused (myHandler, userData)
	WindowRef			window;
	OSStatus			err;
	UInt32				kind, evtClass;
	
	err = GetEventParameter( inEvent, kEventParamDirectObject, typeWindowRef, nil, sizeof(window), nil, &window );
	evtClass = GetEventClass( inEvent );
	kind = GetEventKind( inEvent );
	if (evtClass == kEventClassWindow)
	{
		if (kind == kEventWindowClose)
		{
			//QuitApplicationEventLoop();
			err = noErr;
		}
		else if (kind == kEventWindowBoundsChanging)
		{
			UInt32	attr;
			
			err = GetEventParameter( inEvent, kEventParamAttributes, typeUInt32,
									nil, sizeof(window), nil, &attr );
			if (!err && (attr & kWindowBoundsChangeSizeChanged ||
				attr & kWindowBoundsChangeZoom))
			{
				Rect		prevBounds, currBounds;
				ControlRef	control;
				
				err = GetEventParameter( inEvent, kEventParamPreviousBounds, typeQDRectangle,
										nil, sizeof(prevBounds), nil, &prevBounds );
				if (!err) err = GetEventParameter( inEvent, kEventParamCurrentBounds, typeQDRectangle,
										nil, sizeof(currBounds), nil, &currBounds );
				if (!err) err = GetControlByID( window, &gTableID, &control );
				if (!err)
				{
					OffsetRect( &currBounds, -currBounds.left, -currBounds.top );
					InsetRect( &currBounds, 20, 20 );
					SetControlBounds( control, &currBounds );
				}
			}
		}
	}
	if (err) err = eventNotHandledErr;
	return err;
}

//===============================
static pascal OSErr QuitAppleEventHandler(const AppleEvent *appleEvt, AppleEvent* reply, long refcon)
{
#pragma unused (appleEvt, reply, refcon)
	QuitApplicationEventLoop();
	return noErr;
}

//===============================
static UInt8 DecimalKeyFilterProc( Ptr inTextBefore, UInt32 inTextLengthBefore, Ptr inTextAfter,
									UInt32 inTextLengthAfter, UInt8* ioKey )
{
	UInt8	key;
	UInt32	i;
	
	key = *ioKey;
	// If key is a decimal point, scan to make sure there isn't one there already
	if (key == '.')
	{
		for (i = 0; i < inTextLengthBefore; i++)
		{
			if (*(inTextBefore + i) == '.') return keyFilterBad;
		}
		for (i = 0; i < inTextLengthAfter; i++)
		{
			if (*(inTextAfter + i) == '.') return keyFilterBad;
		}
		return keyFilterOK;
	}
	else if (key < '0' || key > '9') return keyFilterBad;
	return keyFilterOK;
}




#pragma mark -
/*
//===============================
static pascal OSErr trackingHandler(DragTrackingMessage message, WindowRef theWindow,
									void* refCon, DragReference theDragRef)
{
	#pragma unused (refCon)
	OSErr		err = noErr;
	
	if (theWindow)
	{
		BlugsRef	list;
		
		list = (BlugsRef)GetWRefCon( theWindow );
		err = BLTrackDrag( message, theDragRef, list );
	}
	return err;
}

//===============================
static pascal OSErr receiveHandler(WindowRef theWindow, void* refCon,
									DragReference theDragRef)
{
	#pragma unused (refCon)
	BlugsRef	list;
	OSErr		err;
	
	list = (BlugsRef)GetWRefCon( theWindow );
	err = BLReceiveDrag( theDragRef, list );
	return err;
}*/

//===============================
// We won't allow any volume to be dragged in the file list. Easy way to tell it's
//	 a volume: disclosure level is 0.
static pascal OSErr MyDragDataProc( DragReference inDragRef, DragItemRef inItem,
									BLCell inCell, BlugsRef inList )
{
	OSErr		err = noErr;
	Str255		string;
	SInt32		windowID;
	
	err = BLGetUserData( kWindowIDKey, &windowID, inList );
	if (windowID == kTableWindowID)
	{
		if (inCell.col != 1) err = cantGetFlavorErr;
		else
		{
			UInt16	disclosureLevel;
			
			err = BLGetRowDisclosureLevel( inCell.row, &disclosureLevel, inList );
			if (!err)
			{
				if (!disclosureLevel) err = cantGetFlavorErr;
			}
		}
	}
	if (!err)
	{
		UInt32	dataSize = 255;
		
		err = BLGetCellData( inCell, 'TEXT', &dataSize, &string[1], inList );
		if (!err) err = AddDragItemFlavor(inDragRef, inItem, 'TEXT', &string[1], dataSize, 0 );
	}
	return err;
}

//===============================
// We won't allow anything to be dropped at disclosure level 0 in the file list. That keeps
//	files and folders from 'becoming volumes'.
static pascal OSErr MyDropValidationProc( DragReference inDragRef, UInt16 inUnderThisRow,
										UInt16 inDisclosureLevel, BlugsRef inList )
{
	#pragma unused (inDragRef,inUnderThisRow,inList)
	return (inDisclosureLevel) ? noErr : paramErr;
}

//===============================
static pascal void MyDropProc( DragReference inDragRef, UInt16 inUnderThisRow,
								UInt16 inDisclosureLevel, BlugsRef inList )
{
	UInt16	nDragItems, i, markCount = 0;
	OSErr	err;
	
	err = CountDragItems( inDragRef, &nDragItems );
	if (err) nDragItems = 0;
	for (i = 1; i <= nDragItems; i++)
	{
		DragItemRef	itemRef;
		
		err = GetDragItemReferenceNumber( inDragRef, i, &itemRef );
		if (!err)
		{
			BLCell	cell;
			
			err = BLGetCellFromDragItemRef( inDragRef, itemRef, &cell );
			if (!err)
			{
				err = BLSetRowFlags( cell.row, blRowMarkedForMovement, blRowMarkedForMovement, inList );
				if (!err) markCount++;
			}
		}
	}
	if (markCount) BLMoveMarkedRows( inUnderThisRow, inDisclosureLevel, inList );
}

//===============================
static pascal void MyPostDragProc( DragReference inDragRef, BlugsRef inList )
{
	OSErr	err;
	
	if (DropLocationIsTrash(inDragRef))
	{
		UInt16	nRows, nDragItems, i;
		
		// OK, here's how we iterate thru our drag and list, deleting rows and yet
		//	preserving referential integrity.
		// Instead of deleting at the outset, we mark a doomed row with a special identifier
		//	called 'Doom'. Then we iterate thru the rows, deleting anything that shows up
		//	with 'Doom' in the row identifier.
		nRows = BLCountRows( inList );
		if (nRows)
		{
			err = CountDragItems( inDragRef, &nDragItems );
			if (!err && nDragItems)
			{
				for (i = 1; i <= nDragItems; i++)
				{
					DragItemRef	itemRef;
					
					err = GetDragItemReferenceNumber( inDragRef, i, &itemRef );
					if (!err)
					{
						BLCell	cell;
						
						err = BLGetCellFromDragItemRef( inDragRef, itemRef, &cell );
						if (!err) BLSetRowIdentifier( cell.row, 'Doom', inList );
					}
				}
				// Now we loop thru the rows.
				for (i = 1; i <= nRows;)
				{
					UInt32	id;
					
					err = BLGetRowIdentifier( i, &id, inList );
					if (!err && id == 'Doom')
					{
						err = BLDeleteRows( 1, i, inList );
						nRows = BLCountRows( inList );
					}
					else i++;
				}
			}
		}
	}
}


//===============================
static void AddAllVolumesToList( BlugsRef list )
{
	SInt16			index = 1;
	OSErr			err = noErr;
	HParamBlockRec	pb;
	FSSpec			spec;
	BLCell			cell;
	UInt16			rowIndex = 1, nameCol, createDateCol, modDateCol;
	
	nameCol = FindColumnByID( kFileListNameColumnID, list );
	createDateCol = FindColumnByID( kFileListCreationDateColumnID, list );
	modDateCol = FindColumnByID( kFileListModificationDateColumnID, list );
	cell.row = 1;
	// Stop drawing for now
	err = BLSetAutodraw( false, list );
	// loop through the volumes we want to add
	while ( err == noErr )
	{
		// get the file information for the next item in the directory
		pb.volumeParam.ioNamePtr = &(spec.name[0]);
		pb.volumeParam.ioCompletion = nil;
		pb.volumeParam.ioVolIndex = index;
		err = PBHGetVInfoSync( &pb );
		if (!err)
		{
			// add a row
			err = BLAddRows( blDisclosureOptionSame, 1, rowIndex, list );
			if (!err)
			{
				UInt32	dtFlags = dtShowDate | dtShowTime | dtDateFirst | dtAbbrevDate;
				UInt32	creationDate, modificationDate;
				
				creationDate = pb.volumeParam.ioVCrDate;
				modificationDate = pb.volumeParam.ioVLsMod;
				// Set up the needed fields of the FSSpec record.
				spec.parID = fsRtParID;
				spec.vRefNum = pb.volumeParam.ioVRefNum;
				// set the cell data
				cell.row = rowIndex;
				cell.col = nameCol;
				err = BLSetCellData( cell, kFinderesqueFSSpecFlavor, sizeof(FSSpec), &spec, list );
				if (!err) BLSetRowFlags( cell.row, blRowHasChildren | blRowIsExpanded,
											blRowHasChildren, list );
				// Set creation date
				cell.col = createDateCol;
				err = BLSetCellData( cell, dtSecondsFlavor, sizeof(UInt32), &creationDate, list );
				err = BLSetCellData( cell, dtFlagsFlavor, sizeof(UInt32), &dtFlags, list );
				// Set modification date
				cell.col = modDateCol;
				err = BLSetCellData( cell, dtSecondsFlavor, sizeof(UInt32), &modificationDate, list );
				err = BLSetCellData( cell, dtFlagsFlavor, sizeof(UInt32), &dtFlags, list );
				rowIndex++;
			}
		}
		index++;
		cell.row++;
	};
	err = BLSetAutodraw( true, list );
}


//===============================
static void AddDirectoryToList( SInt16 vRefNum, SInt32 dirID, UInt16 firstRow, BlugsRef list )
{
	SInt16			index = 1;
	OSErr			err = noErr;
	CInfoPBRec		pb;
	FSSpec			spec;
	UInt16			rowIndex = 1, nameCol, createDateCol, modDateCol;
	UInt32			dtFlags = dtShowDate | dtShowTime | dtDateFirst | dtAbbrevDate;
	BLCell			cell;
	
	
	// Stop drawing for now
	err = BLSetAutodraw( false, list );
	nameCol = FindColumnByID( kFileListNameColumnID, list );
	createDateCol = FindColumnByID( kFileListCreationDateColumnID, list );
	modDateCol = FindColumnByID( kFileListModificationDateColumnID, list );
	// loop through the items in the directory we want to add
	while (!err)
	{
		// get the file information for the next item in the directory
		pb.hFileInfo.ioNamePtr = &(spec.name[0]);
		pb.hFileInfo.ioVRefNum = vRefNum;
		pb.hFileInfo.ioFDirIndex = index;
		pb.hFileInfo.ioDirID = dirID;
		err = PBGetCatInfoSync( &pb );
		if (!err)
		{
			// Do not add row unless item is visible
			if (!(pb.hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible))
			{
				// add a row
				err = BLAddRows( (rowIndex == 1) ? blDisclosureOptionChild :
									blDisclosureOptionSame, 1, firstRow + rowIndex - 1, list );
				if (!err)
				{
					Boolean		isFolder;
					UInt32		creationDate, modificationDate;
					
					creationDate = pb.hFileInfo.ioFlCrDat;
					modificationDate = pb.hFileInfo.ioFlMdDat;
					isFolder = (pb.hFileInfo.ioFlAttrib & ioDirMask);
					// Set up the needed fields of the FSSpec record.
					spec.parID = dirID;
					spec.vRefNum = vRefNum;
					// set the cell data
					cell.col = nameCol;
					cell.row = firstRow + rowIndex - 1;
					err = BLSetCellData( cell, kFinderesqueFSSpecFlavor, sizeof(FSSpec), &spec, list );
					if (isFolder) BLSetRowFlags( cell.row, blRowHasChildren | blRowIsExpanded,
												blRowHasChildren, list );
					// Set creation date
					cell.col = createDateCol;
					err = BLSetCellData( cell, dtSecondsFlavor, sizeof(UInt32), &creationDate, list );
					err = BLSetCellData( cell, dtFlagsFlavor, sizeof(UInt32), &dtFlags, list );
					// Set modification date
					cell.col = modDateCol;
					err = BLSetCellData( cell, dtSecondsFlavor, sizeof(UInt32), &modificationDate, list );
					err = BLSetCellData( cell, dtFlagsFlavor, sizeof(UInt32), &dtFlags, list );
					rowIndex++;
				}
			}
		}
		index++;
		cell.row++;
	} // while
	err = BLSetAutodraw( true, list );
}


//===============================
static pascal void MyRowExpandProc( Boolean expanding, UInt16 expandedRow, BlugsRef list )
{
	OSErr	err;
	
	BLSetAutodraw( false, list );
	if (expanding)
	{
		BLCell		cell;
		FSSpec		spec;
		UInt32		id, dataSize = sizeof(FSSpec);
		
		// Get directory ID from the row that's being expanded
		err = BLGetColumnIdentifier( 1, &id, list );
		if (id != kFileListNameColumnID) cell.col = 2;
		else cell.col = 1;
		cell.row = expandedRow;
		err = BLGetCellData( cell, kFinderesqueFSSpecFlavor, &dataSize, &spec, list );
		if (!err && dataSize)
		{
			CInfoPBRec	pb;
			
			pb.hFileInfo.ioNamePtr = spec.name;
			pb.hFileInfo.ioVRefNum = spec.vRefNum;
			pb.hFileInfo.ioFDirIndex = 0;
			pb.hFileInfo.ioDirID = spec.parID;
			err = PBGetCatInfoSync( &pb );
			if (!err) AddDirectoryToList( spec.vRefNum, pb.hFileInfo.ioDirID, expandedRow + 1, list );
		}
	}
	else
	{
		UInt16	descendants;
		
		descendants = BLCountDescendants( expandedRow, list );
		if (descendants) BLDeleteRows( descendants, expandedRow + 1, list );
	}
	BLSetAutodraw( true, list );
}

//===============================
static pascal OSErr MyBackgroundProc( Boolean inIsSortColumn, BLCell inCell,
									const Rect* inRect, BlugsRef inList )
{
	#pragma unused (inIsSortColumn,inCell,inList)
	BackColor( whiteColor );
	EraseRect( inRect );
	return noErr;
}

//===============================
// If the user clicks a scroll bar widget, we beep! How clever! (How annoying.)
static pascal void MyNotificationProc( BLNotificationMessage inMessage, UInt32 inCommand,
										BLPart inPart, BLCell inCell, BlugsRef inList )
{
	#pragma unused (inCommand,inPart,inCell,inList)
	switch (inMessage)
	{
		case blWidgetClickedNotificationMsg:
		if (inCell.col == 0 && inCell.row == 1)
		{
			BLTitleBarInfo	info;
			OSErr			err;
			
			err = BLGetTitleBarInfo( false, &info, inList );
			if (!err)
			{
				if (info.thickness == 0) info.thickness = 20;
				else info.thickness = 0;
				BLSetTitleBarInfo( false, blSetTitleBarThickness, &info, inList );
			}
		}
		break;
		
		case blCellDoubleClickNotificationMsg:
		SysBeep( 30 );
		BLDeselectAll( inList );
		break;
	}
}

//===============================
static Boolean DropLocationIsTrash( DragReference inDrag )

{
	Boolean		isTrash;
	FSSpec		dropSpec;
	OSErr		err;
	
	isTrash = false;
	err = DropFSSpec( inDrag, &dropSpec );
	if (!err)
	{
		CInfoPBRec		pb;
		
		pb.dirInfo.ioNamePtr = dropSpec.name;
		pb.dirInfo.ioVRefNum = dropSpec.vRefNum;
		pb.dirInfo.ioFDirIndex = 0;
		pb.dirInfo.ioDrDirID = dropSpec.parID;
		err = PBGetCatInfoSync( &pb );
		if (!err)
		{
			/* The drop location must be a directory to be the trash */
			if ((pb.hFileInfo.ioFlAttrib & ioDirMask) != 0)
			{
				SInt16			trashVRefNum;
				SInt32			trashDirID;
				
				/* Find the trash folder on the drop location's volume. */ 
				if (FindFolder(dropSpec.vRefNum, kTrashFolderType, kDontCreateFolder,
								&trashVRefNum, &trashDirID ) == noErr)
				{
					/* If the directory ID of the dropLocation object is the same as the */
					/* directory ID returned by FindFolder, then the drop must have */
					/* occurred into the Trash. */
					if (pb.dirInfo.ioDrDirID == trashDirID) isTrash = true;
				}
			}
		}
	}
	return isTrash;
}

//===============================
static OSErr DropFSSpec( DragReference inDrag, FSSpecPtr outSpec )
{
	OSErr	err;
	AEDesc	dropDesc, newDesc;

	err = GetDropLocation( inDrag, &dropDesc );
	if (!err)
	{
		err = AECoerceDesc( &dropDesc, typeFSS, &newDesc );
		if (!err)
		{
			err = AEGetDescData( &newDesc, outSpec, sizeof(FSSpec) );
			AEDisposeDesc( &newDesc );
		}
		AEDisposeDesc(&dropDesc);
	}
	return err;
}


//===============================
/*	Pass your implementation of this routine to BLFlatten. The first time it is called,
	outData is nil. Return the data size. If you return noErr and the dataSize is nonzero,
	it will be called a second time with a valid outData buffer. Generally this function
	will act as a wrapper for BLGetCellData. It allows you to determine what data flavors
	to use, and you can filter out cells/titles you don't want to save.
*/
static pascal void MyFlattenProc( BLCell inCell, OSType* outDataFlavor, UInt32* ioDataSize,
									void* outData, BlugsRef inList )
{
	OSErr	err;
	OSType	flavor = 'TEXT';
	
	*outDataFlavor = flavor;
	err = BLGetCellData( inCell, flavor, ioDataSize, outData, inList );
	if (err) *ioDataSize = 0;
}

//===============================
static UInt16 FindColumnByID( OSType inID, BlugsRef inList )
{
	UInt16	nCols, col;
	Boolean	gotIt = false;
	
	nCols = BLCountColumns( inList );
	for (col = 1; col <= nCols; col++)
	{
		OSErr	err;
		OSType	id;
		
		err = BLGetColumnIdentifier( col, &id, inList );
		if (!err && id == inID)
		{
			gotIt = true;
			break;
		}
	}
	if (!gotIt) col = 0;
	return col;
}
