/******************************************************************************
 JXSaveFileDialog.cc

	Clients must call BuildWindow() after constructing the object.
	Derived classes must override BuildWindow() and call SetObjects()
	at the end of their implementation.

	BASE CLASS = JXCSFDialogBase

	Copyright © 1996 by John Lindal. All rights reserved.

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

#include <JXSaveFileDialog.h>
#include <JXUNIXDirTable.h>
#include <JUNIXDirInfo.h>
#include <JUNIXDirEntry.h>
#include <JXNewDirButton.h>
#include <JXDirectSaveSource.h>

#include <JXWindow.h>
#include <JXStaticText.h>
#include <JXTextButton.h>
#include <JXTextCheckbox.h>
#include <JXSaveFileInput.h>
#include <JXPathInput.h>
#include <JXPathHistoryMenu.h>
#include <JXScrollbarSet.h>
#include <jXGlobals.h>

#include <JString.h>
#include <JTableSelection.h>
#include <jFileUtil.h>
#include <jDirUtil.h>
#include <jAssert.h>

// enter button says "Save" while itsFileNameInput has focus

static const JCharacter* kOpenLabel = "Open";
static const JCharacter* kSaveLabel = "Save";

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

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

JXSaveFileDialog::JXSaveFileDialog
	(
	JXDirector*			supervisor,
	JUNIXDirInfo*		dirInfo,
	const JCharacter*	fileFilter
	)
	:
	JXCSFDialogBase(supervisor, dirInfo, fileFilter)
{
	itsFileName = new JString;
	assert( itsFileName != NULL );
}

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

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

JXSaveFileDialog::~JXSaveFileDialog()
{
	delete itsFileName;
}

/******************************************************************************
 GetFileName

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

JBoolean
JXSaveFileDialog::GetFileName
	(
	JString* name
	)
	const
{
	*name = *itsFileName;
	return JNegate(name->IsEmpty());
}

/******************************************************************************
 GetFileNameInput

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

JXInputField*
JXSaveFileDialog::GetFileNameInput()
{
	return itsFileNameInput;
}

/******************************************************************************
 Activate

	We want the file name input field to have focus.

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

void
JXSaveFileDialog::Activate()
{
	JXCSFDialogBase::Activate();
	itsFileNameInput->Focus();
}

/******************************************************************************
 BuildWindow

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

void
JXSaveFileDialog::BuildWindow
	(
	const JCharacter*	origName,
	const JCharacter*	prompt,
	const JCharacter*	message
	)
{
// begin JXLayout

    JXWindow* window = new JXWindow(this, 310,310, "");
    assert( window != NULL );
    SetWindow(window);

    JXScrollbarSet* scrollbarSet =
        new JXScrollbarSet(window,
                    JXWidget::kHElastic, JXWidget::kFixedBottom, 20,110, 180,140);
    assert( scrollbarSet != NULL );

    itsSaveButton =
        new JXTextButton("Save", window,
                    JXWidget::kFixedRight, JXWidget::kFixedBottom, 219,249, 72,22);
    assert( itsSaveButton != NULL );
    itsSaveButton->SetShortcuts("^M");

    JXTextButton* cancelButton =
        new JXTextButton("Cancel", window,
                    JXWidget::kFixedRight, JXWidget::kFixedBottom, 220,280, 70,20);
    assert( cancelButton != NULL );
    cancelButton->SetShortcuts("^[");

    JXTextButton* homeButton =
        new JXTextButton("Home", window,
                    JXWidget::kFixedRight, JXWidget::kFixedBottom, 250,110, 40,20);
    assert( homeButton != NULL );
    homeButton->SetShortcuts("#H");

    JXStaticText* pathLabel =
        new JXStaticText("Path:", window,
                    JXWidget::kFixedLeft, JXWidget::kFixedBottom, 20,20, 40,20);
    assert( pathLabel != NULL );

    itsFileNameInput =
        new JXSaveFileInput(window,
                    JXWidget::kHElastic, JXWidget::kFixedBottom, 20,280, 180,20);
    assert( itsFileNameInput != NULL );

    JXPathInput* pathInput =
        new JXPathInput(window,
                    JXWidget::kHElastic, JXWidget::kFixedBottom, 60,20, 200,20);
    assert( pathInput != NULL );

    JXTextCheckbox* showHiddenCB =
        new JXTextCheckbox("Show hidden files", window,
                    JXWidget::kFixedLeft, JXWidget::kFixedBottom, 60,80, 130,20);
    assert( showHiddenCB != NULL );
    showHiddenCB->SetShortcuts("#S");

    JXStaticText* promptLabel =
        new JXStaticText("", window,
                    JXWidget::kHElastic, JXWidget::kFixedBottom, 20,260, 180,20);
    assert( promptLabel != NULL );

    JXStaticText* filterLabel =
        new JXStaticText("Filter:", window,
                    JXWidget::kFixedLeft, JXWidget::kFixedBottom, 20,50, 40,20);
    assert( filterLabel != NULL );

    JXInputField* filterInput =
        new JXInputField(window,
                    JXWidget::kHElastic, JXWidget::kFixedBottom, 60,50, 200,20);
    assert( filterInput != NULL );

    JXPathHistoryMenu* pathHistory =
        new JXPathHistoryMenu(1, "", window,
                    JXWidget::kFixedRight, JXWidget::kFixedBottom, 260,20, 30,20);
    assert( pathHistory != NULL );

    JXStringHistoryMenu* filterHistory =
        new JXStringHistoryMenu(1, "", window,
                    JXWidget::kFixedRight, JXWidget::kFixedBottom, 260,50, 30,20);
    assert( filterHistory != NULL );

    JXTextButton* upButton =
        new JXTextButton("Up", window,
                    JXWidget::kFixedRight, JXWidget::kFixedBottom, 220,110, 30,20);
    assert( upButton != NULL );
    upButton->SetShortcuts("#U");

    JXNewDirButton* newDirButton =
        new JXNewDirButton(window,
                    JXWidget::kFixedRight, JXWidget::kFixedBottom, 220,140, 70,20);
    assert( newDirButton != NULL );

// end JXLayout

	SetObjects(scrollbarSet, promptLabel, prompt, itsFileNameInput, origName,
			   pathLabel, pathInput, pathHistory,
			   filterLabel, filterInput, filterHistory,
			   itsSaveButton, cancelButton, upButton, homeButton, newDirButton,
			   showHiddenCB, message);
}

/******************************************************************************
 SetObjects (protected)

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

void
JXSaveFileDialog::SetObjects
	(
	JXScrollbarSet*			scrollbarSet,
	JXStaticText*			promptLabel,
	const JCharacter*		prompt,
	JXSaveFileInput*		fileNameInput,
	const JCharacter*		origName,
	JXStaticText*			pathLabel,
	JXPathInput*			pathInput,
	JXPathHistoryMenu*		pathHistory,
	JXStaticText*			filterLabel,
	JXInputField*			filterInput,
	JXStringHistoryMenu*	filterHistory,
	JXTextButton*			saveButton,
	JXTextButton*			cancelButton,
	JXTextButton*			upButton,
	JXTextButton*			homeButton,
	JXNewDirButton*			newDirButton,
	JXTextCheckbox*			showHiddenCB,
	const JCharacter*		message
	)
{
	itsSaveButton    = saveButton;
	itsFileNameInput = fileNameInput;

	JXWindow* window = scrollbarSet->GetWindow();
	window->SetTitle("Save file");

	SetButtons(itsSaveButton, cancelButton);
	JXCSFDialogBase::SetObjects(
		scrollbarSet, pathLabel, pathInput, pathHistory,
		filterLabel, filterInput, filterHistory,
		itsSaveButton, upButton, homeButton, newDirButton, showHiddenCB, message);

	JXUNIXDirTable* table = GetFileBrowser();
	table->AllowSelectFiles(kFalse, kFalse);
	table->AllowDblClickInactive(kTrue);
	promptLabel->SetText(prompt);
	itsFileNameInput->SetText(origName);

	JXUNIXDirTable* fileBrowser = GetFileBrowser();
	fileBrowser->ShouldSelectWhenChangePath(kFalse);

	const JRect frame   = itsFileNameInput->GetFrame();
	const JCoordinate w = frame.height();
	itsXDSSource =
		new JXDirectSaveSource(this, itsFileNameInput, window,
							   JXWidget::kFixedLeft, JXWidget::kFixedBottom,
							   frame.left, frame.top, w, frame.height());
	assert( itsXDSSource != NULL );
	itsFileNameInput->Move(w, 0);
	itsFileNameInput->AdjustSize(-w, 0);

	UpdateDisplay();

	ListenTo(fileBrowser);
	ListenTo(&(fileBrowser->GetTableSelection()));
	ListenTo(itsFileNameInput);
}

/******************************************************************************
 Receive (virtual protected)

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

void
JXSaveFileDialog::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	JXUNIXDirTable* fileBrowser = GetFileBrowser();

	if (sender == itsFileNameInput)
		{
		if (message.Is(JXWidget::kGotFocus))
			{
			itsSaveButton->SetLabel(kSaveLabel);
			UpdateDisplay();
			}
		else if (message.Is(JXWidget::kLostFocus))
			{
			itsSaveButton->SetLabel(kOpenLabel);
			UpdateDisplay();
			}
		else if (message.Is(JTextEditor::kTextChanged) ||
				 message.Is(JTextEditor::kTextSet))
			{
			UpdateDisplay();
			itsFileNameInput->ClearUndo();	// want every keypress
			}
		}

	else if (sender == fileBrowser &&
			 message.Is(JXUNIXDirTable::kFileDblClicked))
		{
		const JXUNIXDirTable::FileDblClicked* info =
			dynamic_cast(const JXUNIXDirTable::FileDblClicked*, &message);
		assert( info != NULL );
		const JString fileName = (info->GetDirEntry()).GetName();
		itsFileNameInput->SetText(fileName);
		itsFileNameInput->Focus();
		}

	else
		{
		if (sender == &(fileBrowser->GetTableSelection()))
			{
			UpdateDisplay();
			}
		else if (sender == fileBrowser &&
				 (message.Is(JXWidget::kGotFocus) ||
				  message.Is(JXWidget::kLostFocus)))
			{
			UpdateDisplay();
			}

		JXCSFDialogBase::Receive(sender, message);
		}
}

/******************************************************************************
 OKToDeactivate (virtual protected)

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

JBoolean
JXSaveFileDialog::OKToDeactivate()
{
	if (!JXCSFDialogBase::OKToDeactivate())
		{
		return kFalse;
		}
	else if (Cancelled())
		{
		return kTrue;
		}

	JXPathInput* pathInput = GetPathInput();
	if (pathInput->HasFocus())
		{
		GoToItsPath();
		return kFalse;
		}

	JXInputField* filterInput = GetFilterInput();
	if (filterInput->HasFocus())
		{
		AdjustFilter();
		return kFalse;
		}

	JXUNIXDirTable* fileBrowser = GetFileBrowser();
	if (fileBrowser->HasFocus() && fileBrowser->GoToSelectedDirectory())
		{
		return kFalse;
		}

	const JString& fileName = itsFileNameInput->GetText();
	if (fileName.IsEmpty())
		{
		(JGetUserNotification())->ReportError("You need to enter a file name.");
		return kFalse;
		}

	const JString& path     = GetPath();
	const JString fullName  = path + fileName;

	const JBoolean fileExists = JFileExists(fullName);

	if (JDirectoryExists(fullName))
		{
		(JGetUserNotification())->ReportError(
			"This name is already used for a directory.");
		return kFalse;
		}
	else if (!JDirectoryWritable(path) && !fileExists)
		{
		(JGetUserNotification())->ReportError(
			"You are not allowed to write to this directory.");
		return kFalse;
		}
	else if (!fileExists)
		{
		*itsFileName = fileName;
		return kTrue;
		}
	else if (!JFileWritable(fullName))
		{
		(JGetUserNotification())->ReportError(
			"You are not allowed to write to this file.");
		return kFalse;
		}
	else if ((JGetUserNotification())->AskUserNo("That file already exists.  Replace it?"))
		{
		*itsFileName = fileName;
		return kTrue;
		}
	else
		{
		return kFalse;
		}
}

/******************************************************************************
 UpdateDisplay (virtual protected)

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

void
JXSaveFileDialog::UpdateDisplay()
{
	JXCSFDialogBase::UpdateDisplay();

	JXUNIXDirTable* table = GetFileBrowser();

	JBoolean saveWoutFocus = kFalse;
	if (table->HasFocus())
		{
		if ((table->GetTableSelection()).HasSelection())
			{
			itsSaveButton->SetLabel(kOpenLabel);
			}
		else
			{
			itsSaveButton->SetLabel(kSaveLabel);
			saveWoutFocus = kTrue;
			}
		}

	if ((itsFileNameInput->HasFocus() || saveWoutFocus) &&
		((itsFileNameInput->GetText()).IsEmpty() ||
		 !(GetDirInfo())->IsWritable()))
		{
		itsSaveButton->Deactivate();
		}
	else
		{
		itsSaveButton->Activate();
		}

	if ((itsFileNameInput->GetText()).IsEmpty())
		{
		itsXDSSource->Deactivate();
		}
	else
		{
		itsXDSSource->Activate();
		}
}

/******************************************************************************
 Save

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

void
JXSaveFileDialog::Save
	(
	const JCharacter* path
	)
{
	JUNIXDirInfo* dirInfo = GetDirInfo();
	const JError err = dirInfo->GoTo(path);
	if (!err.OK())
		{
		err.ReportError();
		}
	else if (itsFileNameInput->Focus())
		{
		EndDialog(kTrue);
		}
}
