/******************************************************************************
 JXDirectSaveSource.cc

	Displays a file icon that the user can drag to a file manager
	and thereby save.

	BASE CLASS = JXImageWidget

	Copyright © 1999 by John Lindal. All rights reserved.

 ******************************************************************************/

#include <JXDirectSaveSource.h>
#include <JXDSSFinishSaveTask.h>
#include <JXDNDManager.h>
#include <JXSelectionManager.h>
#include <JXDisplay.h>
#include <JXWindow.h>
#include <JXInputField.h>
#include <jXGlobals.h>
#include <jXUtil.h>
#include <jXCSFIcons.h>
#include <jDirUtil.h>
#include <jAssert.h>

static const JCharacter* kHintText =
	"Drag this to an XDS compliant file manager to save the file.";

/******************************************************************************
 Constructor

 ******************************************************************************/

JXDirectSaveSource::JXDirectSaveSource
	(
	JXSaveFileDialog*	dialog,
	JXInputField*		nameInput,
	JXContainer*		enclosure,
	const HSizingOption	hSizing,
	const VSizingOption	vSizing,
	const JCoordinate	x,
	const JCoordinate	y,
	const JCoordinate	w,
	const JCoordinate	h
	)
	:
	JXImageWidget(enclosure, hSizing, vSizing, x,y, w,h)
{
	itsDialog    = dialog;
	itsNameInput = nameInput;

	JXImage* icon = new JXImage(GetDisplay(), GetColormap(), JXGetPlainFileIcon());
	assert( icon != NULL );
	icon->ConvertToRemoteStorage();
	SetImage(icon, kTrue);

	SetHint(kHintText);

	// targets for DND

	JXDNDManager* dndMgr = GetDNDManager();
	const Atom dndName   = dndMgr->GetDNDSelectionName();

	AddSelectionTarget(dndName, dndMgr->GetDNDDirectSave0XAtom());
}

/******************************************************************************
 Destructor

 ******************************************************************************/

JXDirectSaveSource::~JXDirectSaveSource()
{
}

/******************************************************************************
 HandleMouseDown (virtual protected)

 ******************************************************************************/

void
JXDirectSaveSource::HandleMouseDown
	(
	const JPoint&			pt,
	const JXMouseButton		button,
	const JSize				clickCount,
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers
	)
{
	BeginDND(pt, buttonStates, modifiers);
}

/******************************************************************************
 DNDInit (virtual protected)

 ******************************************************************************/

void
JXDirectSaveSource::DNDInit
	(
	const JPoint&			pt,
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers
	)
{
	const JString& name = itsNameInput->GetText();
	XChangeProperty(*(GetDisplay()), (GetWindow())->GetXWindow(),
					(GetDNDManager())->GetDNDDirectSave0XAtom(),
					(GetSelectionManager())->GetMimePlainTextXAtom(), 8,
					PropModeReplace,
					(unsigned char*) (name.GetCString()), name.GetLength());
}

/******************************************************************************
 GetDNDAction (virtual protected)

 ******************************************************************************/

Atom
JXDirectSaveSource::GetDNDAction
	(
	const JXContainer*		target,
	const JXButtonStates&	buttonStates,
	const JXKeyModifiers&	modifiers
	)
{
	return (GetDNDManager())->GetDNDActionPrivateXAtom();
}

/******************************************************************************
 ConvertSelection (virtual protected)

	Convert selection to the specified type and return kTrue,
	or return kFalse if the conversion cannot be accomplished.

	*returnType must be actual data type.  For example, when "TEXT" is
	requested, one often returns XA_STRING.

	*data must be allocated with "new unsigned char[]" and will be deleted
	by the caller.  *dataLength must be set to the length of *data.

	*bitsPerBlock must be set to the number of bits per element of data.
	e.g.	If data is text, *bitsPerBlock=8.
			If data is an int, *bitsPerBlock=sizeof(int)*8

	Since X performs byte swapping when *bitsPerBlock > 8, mixed data is
	packed one byte at a time to insure that it can be correctly decoded.

 ******************************************************************************/

JBoolean
JXDirectSaveSource::ConvertSelection
	(
	const Atom		name,
	const Atom		type,
	Atom*			returnType,
	unsigned char**	data,
	JSize*			dataLength,
	JSize*			bitsPerBlock
	)
{
	*bitsPerBlock = 8;

	JXSelectionManager* selMgr = GetSelectionManager();
	JXDNDManager* dndMgr       = GetDNDManager();

	if (name == dndMgr->GetDNDSelectionName() &&
		type == dndMgr->GetDNDDirectSave0XAtom())
		{
		// get URL

		Atom actualType;
		int actualFormat;
		unsigned long itemCount, remainingBytes;
		unsigned char* rawData = NULL;
		XGetWindowProperty(*(GetDisplay()), (GetWindow())->GetXWindow(),
						   dndMgr->GetDNDDirectSave0XAtom(),
						   0, LONG_MAX, True, selMgr->GetMimePlainTextXAtom(),
						   &actualType, &actualFormat,
						   &itemCount, &remainingBytes, &rawData);

		if (actualType == selMgr->GetMimePlainTextXAtom() &&
			actualFormat == 8 && itemCount > 0)
			{
			JPtrArray<JString> fileNameList, urlList;
			JXUnpackFileNames((char*) rawData, itemCount, &fileNameList, &urlList);

			if (!fileNameList.IsEmpty())
				{
				JString path, name;
				JSplitPathAndName(*(fileNameList.FirstElement()), &path, &name);
				JXDSSFinishSaveTask* task = new JXDSSFinishSaveTask(itsDialog, path);
				assert( task != NULL );
				(JXGetApplication())->InstallUrgentTask(task);
				}
			else
				{
				(JGetUserNotification())->ReportError(
					"You cannot save the file on a different computer.");
				}

			fileNameList.DeleteAll();
			urlList.DeleteAll();
			}

		XFree(rawData);

		// respond with success

		*returnType = XA_STRING;
		*dataLength = 1;
		*data = new unsigned char[ *dataLength ];
		if (*data != NULL)
			{
			**data = 0x45;	// E: terminate session now that we have the file name
			return kTrue;
			}
		}

	return kFalse;
}
