/******************************************************************************
 GMessageTableDir.cc

	BASE CLASS = GMManagedDirector

	Copyright © 1997 by Glenn Bach. All rights reserved.

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

#include "GMessageTableDir.h"
#include "GMessageEditDir.h"
#include "GMessageTable.h"
#include "GMessageHeaderList.h"
#include "GMessageHeader.h"
#include "GMessageColHeader.h"
#include "GMessageFrom.h"
#include "GMessageDirUpdateTask.h"
#include "GMessageViewDir.h"
#include "SMTPMessage.h"
#include <GMGlobals.h>
#include <GMApp.h>
#include <GPrefsMgr.h>
#include <GInboxMgr.h>
#include <GHelpText.h>
#include <GXBlockingPG.h>
#include <gMailUtils.h>
#include "GMDummyText.h"
#include "GMChooseSaveFile.h"
#include "GMPTPrinter.h"
#include "GMDirectorManager.h"
#include "GMDirectorMenu.h"

#include "nomail.xpm"
#include "newmail.xpm"
#include "newmail_small.xpm"
#include "newmail_small_plus.xpm"
#include "newmail_small_minus.xpm"
#include "havemail.xpm"

#include "filenew.xpm"
#include "filefloppy.xpm"
#include "fileprint.xpm"
#include "fileopen.xpm"
#include "envelopes.xpm"
#include "check_mail.xpm"

#include "address_entry.xpm"
#include "mailbox.xpm"

#include "manual.xpm"
#include "jx_help_specific.xpm"

#include <JXToolBar.h>

#include <JXImage.h>
#include <JXScrollbarSet.h>
#include <JXScrollbar.h>
#include <JXProgressIndicator.h>
#include <JXStaticText.h>
#include <JXChooseSaveFile.h>
#include <JXApplication.h>
#include <JXWindow.h>
#include <JXMenuBar.h>
#include <JXTextMenu.h>
#include <JXDisplay.h>
#include <JXHelpManager.h>

#include <JUNIXDirEntry.h>
#include <JUserNotification.h>
#include <JString.h>
#include <JFileArray.h>
#include <JStaticBuffer.h>
#include <JTableSelection.h>
#include <JTableSelectionIterator.h>

#include <jProcessUtil.h>
#include <jStreamUtil.h>
#include <jFileUtil.h>
#include <jDirUtil.h>
#include <jStrStreamUtil.h>

#include <iostream.h>
#include <fstream.h>
#include <strstream.h>
#include <stdlib.h>
#include <jAssert.h>

//extern JUserNotification* 	gUserNotification;
//extern JXApplication* 		gApplication;
//extern JChooseSaveFile* 	gChooseSaveFile; 

extern const JIndex		kPriorityIndex;
extern const JIndex		kIconIndex;
extern const JIndex		kAttachIndex;
extern const JIndex		kFromIndex;
extern const JIndex		kSubjectIndex;
extern const JIndex		kDateIndex;

static const JCharacter* kFileMenuTitleStr = "File";
static const JCharacter* kFileMenuStr = 
	"New message %k Meta-N %i \"NewMessage::Arrow\""
	"| New mailbox %k Meta-Shift-N %i \"NewMailbox::Arrow\""
	"|Open mailbox... %k Meta-O %i \"OpenMailbox::Arrow\""
	"|Open current outbox %i \"OpenOutbox::Arrow\""
	"%l|Save messages... %k Meta-S %i \"SaveMessages::Arrow\""
	"|Append messages... %i \"AppendMessages::Arrow\""
	"%l|Page setup... %i \"PageSetup::Arrow\""
	"|Print messages... %k Meta-P %i \"PrintMessages::Arrow\""
	"%l| Check mail %k Meta-M  %i \"CheckMail::Arrow\""
	"| Save changes %i \"SaveChanges::Arrow\""
	"|Flush deleted messages %k Meta-F %i \"FlushDeleted::Arrow\""
	"%l|Close %k Meta-W %i \"Close::Arrow\""
	"|Quit %k Meta-Q %i \"Quit::Arrow\"";

enum
{	
	kNewCmd = 1,
	kNewMBox,
	kOpenCmd,
	kOpenOutboxCmd,
	kSaveMsgCmd,
	kAppendMsgCmd,
	kPageSetupCmd,
	kPrintMsgCmd,
	kCheckMailCmd,
	kSaveCmd,
	kFlushCmd,
	kCloseCmd,
	kQuitCmd
};

static const JCharacter* kInboxMenuTitleStr = "Inbox";
static const JCharacter* kInboxMenuStr = 
	"Add inbox %k Meta-plus %i \"AddInbox::Arrow\""
	"| Remove inbox %k Meta-minus %i \"RemoveInbox::Arrow\""
	"|Remove all inboxes  %i \"RemoveAllInboxes::Arrow\""
	"| Open all inboxes %i \"OpenAllInboxes::Arrow\" %l";

enum
{
	kAddInboxCmd = 1,
	kRemoveInboxCmd,
	kRemoveAllInboxesCmd,
	kOpenAllInboxesCmd
};

static const JCharacter* kWindowsMenuTitleStr = "Windows";

static const JCharacter* kWindowMenuTitleStr = "Windows";
static const JCharacter* kWindowMenuStr = 
	"Mailboxes %k Meta-Shift-M  %i \"MailboxWindow::Arrow\""
	"| Address book %k Meta-Shift-A  %i \"AddBookWindow::Arrow\""
	"| SMTP debug window %i \"SMTPDebugWindow::Arrow\"";

enum
{
	kMailboxCmd = 1,
	kAddressBookCmd,
	kSMTPDebugCmd
};

static const JCharacter* kPrefsMenuTitleStr = "Preferences";
static const JCharacter* kPrefsMenuStr = 
	"Edit mail preferences %i \"Preferences::Arrow\""
	"|Edit URL preferences %i \"URLPreferences::Arrow\""
	"|Edit tool bar... %i \"EditToolBar::Arrow\""
	"|Save window size as default %i \"SaveWindowDefault::Arrow\"";

enum
{	
	kPrefsCmd = 1,
	kURLPrefsCmd,
	kEditToolBarCmd,
	kSaveWindowSizeCmd
};

static const JCharacter* kHelpMenuTitleStr = "Help";
static const JCharacter* kHelpMenuStr = 
	"About"
	"%l|Table of Contents %i \"TOCHelp::Arrow\""
	"|Overview|This window %i \"ThisWindowHelp::Arrow\""
	"%l|Changes|Credits";

enum
{	
	kAboutCmd = 1,
	kTOCCmd,
	kOverviewCmd,
	kThisWindowCmd,
	kChangesCmd,
	kCreditsCmd
};

const JCharacter kStateDataEndDelimiter     	= '\032';
const JCharacter kViewStateDataEndDelimiter     = '\031';

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

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

JBoolean
GMessageTableDir::CreateX
	(
	JXDirector* supervisor, 
	const JString& mailfile, 
	GMessageTableDir** dir
	)
{
	(*dir)->Activate();
	(*dir)->itsPath->Hide();
	(*dir)->itsIndicator->Show();
	(*dir)->GetWindow()->Refresh();
	(*dir)->GetDisplay()->Flush();
	(*dir)->GetDisplay()->Synchronize();
	GMessageHeaderList* list;
	JBoolean ok = GMessageHeaderList::Create(mailfile, &list, (*dir)->itsPG);
	(*dir)->itsIndicator->Hide();
	(*dir)->itsPath->Show();
	if (ok)
		{
		(*dir)->itsList = list;
		(*dir)->itsTable->SetList(list);
		(*dir)->UpdateMessageCount();
		(*dir)->GenerateFromList();
		(*dir)->GetWindow()->Update();	
		return kTrue;
		}
	(*dir)->itsList = NULL;
	(*dir)->GMManagedDirector::Close();
	*dir = NULL;
	return kFalse;
}

JBoolean
GMessageTableDir::Create
	(
	JXDirector* supervisor, 
	const JString& mailfile, 
	GMessageTableDir** dir,
	const JBoolean iconify
	)
{
	*dir = new GMessageTableDir(supervisor, mailfile);
	assert(*dir != NULL);
	if (iconify)
		{
		(*dir)->GetWindow()->Iconify();
		}
	JBoolean success = CreateX(supervisor, mailfile, dir);
	if (iconify && success)
		{
		(*dir)->GetWindow()->SetIcon(*((*dir)->itsNewMailIcon));
		(*dir)->itsHasNewMail = kTrue;
		(*dir)->AdjustWindowTitle();
		}
	return success;
}

JBoolean
GMessageTableDir::Create
	(
	JXDirector* supervisor, 
	JFileArray& fileArray, 
	GMessageTableDir** dir
	)
{
	JFAID id = 1;
	JStaticBuffer data;
	fileArray.GetElement(id, &data);
	jistrstream(is, data.GetData(), strlen(data));
	
	JString mailfile;
	is >> mailfile;

	*dir = new GMessageTableDir(supervisor, mailfile);
	assert(*dir != NULL);
	(*dir)->GetWindow()->ReadGeometry(is);
	JBoolean success = CreateX(supervisor, mailfile, dir);
	if (success)
		{
		(*dir)->ReadState(fileArray);
		}
	return success;
}

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

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

GMessageTableDir::GMessageTableDir
	(
	JXDirector* supervisor,
	const JString& mailfile
	)
	:
	GMManagedDirector(supervisor),
	itsMailFile(mailfile),
	itsDummyPrintText(NULL)
{
	if (!itsMailFile.BeginsWith("/"))
		{
		const JString cwd = JGetCurrentDirectory();
		itsMailFile = JCombinePathAndName(cwd, itsMailFile);
		}
	itsEntry = new JUNIXDirEntry(mailfile);
	assert(itsEntry != NULL);

	BuildWindow(mailfile);
	
	if (itsMailFile.GetCharacter(1) != '/')
		{
		JString cwd = JGetCurrentDirectory();
		itsMailFile.Prepend(cwd);
		}
	itsFromList = new JPtrArray<GMessageFrom>;
	assert(itsFromList != NULL);
	itsFromList->SetCompareFunction(GMessageFrom::CompareFromLines);
	
	itsViewDirs = new JPtrArray<GMessageViewDir>;
	assert(itsViewDirs != NULL);

	itsIdleTask = new GMessageDirUpdateTask(this);
	assert(itsIdleTask != NULL);
	JXGetApplication()->InstallIdleTask(itsIdleTask);

	itsNeedsUpdate  = kFalse;
	itsNeedsSave	= kFalse;
	itsHasNewMail	= kFalse;

	UpdateInboxMenu();
	ListenTo(GGetPrefsMgr());

	if (itsEntry->GetType() == JUNIXDirEntry::kDoesNotExist)
		{
		itsSBSet->Deactivate();
		}

	GGetDirMgr()->DirectorCreated(this);
}

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

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

GMessageTableDir::~GMessageTableDir()
{
	delete itsEntry;
	JXGetApplication()->RemoveIdleTask(itsIdleTask);
	delete itsIdleTask;
	delete itsNoMailIcon;
	delete itsNewMailIcon;
	delete itsSmallNewMailIcon;
	delete itsHaveMailIcon;
	itsFromList->DeleteAll();
	delete itsFromList;
	delete itsViewDirs;
	delete itsPG;
	if (itsList != NULL)
		{
		itsList->DeleteAll();
		}
	delete itsList;
	delete itsMenuIcon;
	GGetDirMgr()->DirectorDeleted(this);
}

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

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

void
GMessageTableDir::BuildWindow
	(
	const JString& mailfile
	)
{
	JSize w = 560;
	JSize h = 300;
	JString title;
	JString path;
	JSplitPathAndName(mailfile, &path, &title);
	itsWindow = new JXWindow(this, w,h, title);
	assert( itsWindow != NULL );
	SetWindow(itsWindow);
	itsWindow->SetWMClass(GMGetWMClassInstance(), GMGetTableWindowClass());
	ListenTo(itsWindow);
    GGetPrefsMgr()->GetTableWindowSize(itsWindow);

	w = itsWindow->GetFrameWidth();
	h = itsWindow->GetFrameHeight();

	itsWindow->SetMinSize(w, 150);
	
	JXMenuBar* menuBar = 
		new JXMenuBar(itsWindow, 
			JXWidget::kHElastic, JXWidget::kFixedTop,
			0, 0, w, kJXStdMenuBarHeight);
	assert(menuBar != NULL);
	
	itsFileMenu = menuBar->AppendTextMenu(kFileMenuTitleStr);
	itsFileMenu->SetMenuItems(kFileMenuStr);
	itsFileMenu->SetUpdateAction(JXMenu::kDisableNone);
	ListenTo(itsFileMenu);

//	itsInboxMenu = new JXTextMenu(itsFileMenu, kInboxCmd, menuBar);
	itsInboxMenu = menuBar->AppendTextMenu(kInboxMenuTitleStr);
	assert(itsInboxMenu != NULL);
	itsInboxMenu->SetMenuItems(kInboxMenuStr);
	itsInboxMenu->SetUpdateAction(JXMenu::kDisableNone);
	ListenTo(itsInboxMenu);

	itsToolBar = 
		new JXToolBar(GGetPrefsMgr(), kGTableToolBarID,
			menuBar, w, 150, itsWindow,
			JXWidget::kHElastic, JXWidget::kVElastic, 
			0, kJXStdMenuBarHeight, w, h - kJXStdMenuBarHeight);
	assert(itsToolBar != NULL);
	
	const JCoordinate pathheight = 20;
	const JCoordinate scrollheight = itsToolBar->GetWidgetEnclosure()->GetBoundsHeight() - pathheight;
	
	itsSBSet = 
		new JXScrollbarSet(itsToolBar->GetWidgetEnclosure(),
			JXWidget::kHElastic, JXWidget::kVElastic, 
			0,0,w,scrollheight);
	assert(itsSBSet != NULL);
	
	itsTable = 
		new GMessageTable(this, menuBar,
			itsSBSet, itsSBSet->GetScrollEnclosure(),
			JXWidget::kHElastic, JXWidget::kVElastic, 
			0,20,w,scrollheight-20);
	assert (itsTable != NULL);

	GMessageColHeader* header = 
		GMessageColHeader::Create(itsTable, 
			itsSBSet, itsSBSet->GetScrollEnclosure(),
			JXWidget::kHElastic, JXWidget::kFixedTop, 
			0,0,w,20);
	assert(header != NULL);
	
	header->SetColTitle(4, "From");
	header->SetColTitle(5, "Subject");
	header->SetColTitle(6, "Date");
	header->SetColTitle(7, "Size");
	
	const JCoordinate pathwidth = w - 150;
	
	itsPath = 
		new JXStaticText(itsMailFile, itsToolBar->GetWidgetEnclosure(),
			JXWidget::kHElastic, JXWidget::kFixedBottom, 
			0,scrollheight,pathwidth,pathheight);
	assert(itsPath != NULL);
	itsPath->SetBorderWidth(1);
//	JSize count = itsList->GetElementCount();
//	JString messageCount(count);
//	messageCount += " messages";
	
	itsMessageCount =
		new JXStaticText("", itsToolBar->GetWidgetEnclosure(),
			JXWidget::kFixedRight, JXWidget::kFixedBottom, 
			pathwidth,scrollheight,w-pathwidth,pathheight);
	assert(itsMessageCount != NULL);
	itsMessageCount->SetBorderWidth(1);
	
	itsNoMailIcon = new JXImage(itsWindow->GetDisplay(), itsWindow->GetColormap(), JXPM(nomail_xpm));
	itsNoMailIcon->ConvertToRemoteStorage();
	
	itsNewMailIcon = new JXImage(itsWindow->GetDisplay(), itsWindow->GetColormap(), JXPM(newmail_xpm));
	itsNewMailIcon->ConvertToRemoteStorage();

	itsSmallNewMailIcon = new JXImage(itsWindow->GetDisplay(), itsWindow->GetColormap(), JXPM(newmail_small_xpm));
	itsSmallNewMailIcon->ConvertToRemoteStorage();

	itsHaveMailIcon = new JXImage(itsWindow->GetDisplay(), itsWindow->GetColormap(), JXPM(havemail_xpm));
	itsHaveMailIcon->ConvertToRemoteStorage();
	
	itsWindow->SetIcon(*itsNoMailIcon);
	itsHasNewMail = kFalse;
	
	itsIndicator =
        new JXProgressIndicator(itsToolBar->GetWidgetEnclosure(),
        	JXWidget::kHElastic, JXWidget::kFixedBottom, 
			5,scrollheight+5,pathwidth-10,pathheight/2);
	assert(itsIndicator != NULL);
	itsIndicator->Hide();

	itsPG = new GXBlockingPG(NULL, NULL, itsIndicator);
	assert(itsPG != NULL);
//	itsPG->SetItems(NULL, NULL, itsIndicator);
// begin JXLayout

	GMDirectorMenu* menu = 
		new GMDirectorMenu(kWindowsMenuTitleStr, menuBar,
			JXWidget::kFixedLeft, JXWidget::kVElastic, 0,0, 10,10);
	assert(menu != NULL);
	menuBar->AppendMenu(menu);

	itsPrefsMenu = menuBar->AppendTextMenu(kPrefsMenuTitleStr);
	itsPrefsMenu->SetMenuItems(kPrefsMenuStr);
	itsPrefsMenu->SetUpdateAction(JXMenu::kDisableNone);
	ListenTo(itsPrefsMenu);

	itsHelpMenu = menuBar->AppendTextMenu(kHelpMenuTitleStr);
	itsHelpMenu->SetMenuItems(kHelpMenuStr);
	itsHelpMenu->SetUpdateAction(JXMenu::kDisableNone);
	ListenTo(itsHelpMenu);

	itsFileMenu->SetItemImage(kNewCmd, filenew);
	itsFileMenu->SetItemImage(kNewMBox, envelopes);
	itsFileMenu->SetItemImage(kOpenCmd, fileopen);
	itsFileMenu->SetItemImage(kSaveMsgCmd, filefloppy);
	itsFileMenu->SetItemImage(kPrintMsgCmd, fileprint);
	itsFileMenu->SetItemImage(kCheckMailCmd, check_mail);
//	itsFileMenu->SetItemImage(kOpenCmd, fileopen);
	
	itsInboxMenu->SetItemImage(kAddInboxCmd, newmail_small_plus);
	itsInboxMenu->SetItemImage(kRemoveInboxCmd, newmail_small_minus);

	itsHelpMenu->SetItemImage(kTOCCmd, manual);
	itsHelpMenu->SetItemImage(kThisWindowCmd, jx_help_specific);

	itsDummyPrintText = 
		new GMDummyText(GMGetAltPTPrinter(), "", itsWindow,
			JXWidget::kHElastic, JXWidget::kFixedBottom, 
			0,0,500,10);
	assert(itsDummyPrintText != NULL);

	itsToolBar->LoadPrefs();

	if (itsToolBar->IsEmpty())
		{
		itsToolBar->AppendButton(itsFileMenu, kNewCmd);
		itsToolBar->AppendButton(itsFileMenu, kNewMBox);
		itsToolBar->AppendButton(itsFileMenu, kOpenCmd);
		itsToolBar->NewGroup();
		
		itsToolBar->AppendButton(itsFileMenu, kSaveMsgCmd);
		itsToolBar->NewGroup();
		
		itsToolBar->AppendButton(itsFileMenu, kPrintMsgCmd);
		itsToolBar->NewGroup();
		
		itsToolBar->AppendButton(itsFileMenu, kCheckMailCmd);
		itsToolBar->NewGroup();

		itsToolBar->AppendButton(itsHelpMenu, kTOCCmd);
		itsToolBar->AppendButton(itsHelpMenu, kThisWindowCmd);
		}

	itsMenuIcon = new JXImage(itsWindow->GetDisplay(), itsWindow->GetColormap(), JXPM(envelopes));
	assert(itsMenuIcon != NULL);
	itsMenuIcon->ConvertToRemoteStorage();
}

/******************************************************************************
 Receive

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

void
GMessageTableDir::Receive
	(
	JBroadcaster* sender,
	const JBroadcaster::Message& message
	)
{
	if (sender == itsFileMenu && message.Is(JXMenu::kItemSelected))
		{
		 const JXMenu::ItemSelected* selection =
			dynamic_cast(const JXMenu::ItemSelected*, &message);
		assert( selection != NULL );
		HandleFileMenu(selection->GetIndex());
		}
		
	else if (sender == itsFileMenu && message.Is(JXMenu::kNeedsUpdate))
		{
		UpdateFileMenu();
		}
	else if (sender == itsInboxMenu && message.Is(JXMenu::kItemSelected))
		{
		 const JXMenu::ItemSelected* selection =
			dynamic_cast(const JXMenu::ItemSelected*, &message);
		assert( selection != NULL );
		HandleInboxMenu(selection->GetIndex());
		}

	else if (sender == itsHelpMenu && message.Is(JXMenu::kItemSelected))
		{
		 const JXMenu::ItemSelected* selection =
			dynamic_cast(const JXMenu::ItemSelected*, &message);
		assert( selection != NULL );
		HandleHelpMenu(selection->GetIndex());
		}

	else if (sender == itsPrefsMenu && message.Is(JXMenu::kItemSelected))
		{
		 const JXMenu::ItemSelected* selection =
			dynamic_cast(const JXMenu::ItemSelected*, &message);
		assert( selection != NULL );
		HandlePrefsMenu(selection->GetIndex());
		}

	else if (sender == itsWindow && message.Is(JXWindow::kDeiconified))
		{
		if (itsNeedsUpdate)
			{
			Update();
			itsNeedsUpdate = kFalse;
			}
		else
			{
			itsHasNewMail = kFalse;
			UpdateMessageCount();
			}
		}
	else if (sender == GGetPrefsMgr())
		{
		if (message.Is(GPrefsMgr::kInboxesChanged))
			{
			UpdateInboxMenu();
			}
		else if (message.Is(GPrefsMgr::kShowStateChanged))
			{
			AdjustWindowTitle();
			}
		}
	else if (sender == GMGetAltPTPrinter() && message.Is(GMPTPrinter::kPrintStarting))
		{
		JString tempname = JGetTempFileName();
		ofstream os(tempname);
		if (!os.good())
			{
			JGetUserNotification()->ReportError("Enable to print.");
			return;
			}
		JBoolean headers = GMGetAltPTPrinter()->WillPrintHeader();
		SaveSelectedMessages(os, headers);
		os.close();
		JTextEditor::PlainTextFormat format;
		itsDummyPrintText->ReadPlainText(tempname, &format);
		JRemoveFile(tempname);
		}
	else
		{
		GMManagedDirector::Receive(sender, message);
		}

}

/******************************************************************************
 DirectorClosed 

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

void
GMessageTableDir::DirectorClosed
	(
	JXDirector* theDirector
	)
{
	GMessageViewDir* dir = dynamic_cast(GMessageViewDir*, theDirector);
	assert(dir != NULL);
	if (itsViewDirs->Includes(dir))
		{
		itsViewDirs->Remove(dir);
		}
}

/******************************************************************************
 UpdateFileMenu 


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

void
GMessageTableDir::UpdateFileMenu()
{
	JTableSelection& s = itsTable->GetTableSelection();
	if (s.HasSelection())
		{
		itsFileMenu->EnableItem(kSaveMsgCmd);
		itsFileMenu->EnableItem(kAppendMsgCmd);
		itsFileMenu->EnableItem(kPrintMsgCmd);
		}
	else
		{
		itsFileMenu->DisableItem(kSaveMsgCmd);
		itsFileMenu->DisableItem(kAppendMsgCmd);
		itsFileMenu->DisableItem(kPrintMsgCmd);
		}
	if (itsNeedsSave)
		{
		itsFileMenu->EnableItem(kSaveCmd);
		itsFileMenu->EnableItem(kFlushCmd);
		}
	else
		{
		itsFileMenu->DisableItem(kSaveCmd);
		itsFileMenu->DisableItem(kFlushCmd);
		}
//	if (GGetPrefsMgr()->UsingPopServer())
//		{
		itsFileMenu->EnableItem(kCheckMailCmd);
//		}
//	else
//		{
//		itsFileMenu->DisableItem(kCheckMailCmd);
//		}
}

/******************************************************************************
 HandleFileMenu 


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

void
GMessageTableDir::HandleFileMenu
	(
	const JIndex index
	)
{
	if (index == kNewCmd)
		{
		GMessageEditDir* dir = new GMessageEditDir(JXGetApplication());
		assert(dir != NULL);
		dir->Activate();
		}
	else if (index == kNewMBox)
		{
		JString mbox;
		if (JXGetChooseSaveFile()->SaveFile("Name of new mailbox:", "", "",  &mbox))
			{
			GMGetApplication()->NewMailbox(mbox);
			}
		}
	else if (index == kOpenCmd)
		{
		GMGetApplication()->OpenMailbox();
		}
	else if (index == kOpenOutboxCmd)
		{
		GMGetApplication()->OpenMailbox(GGetPrefsMgr()->GetCurrentOutbox());
		}
	else if (index == kSaveMsgCmd)
		{
		SaveSelectedMessages();
		}
	else if (index == kAppendMsgCmd)
		{
		AppendSelectedMessages();
		}
	else if (index == kPageSetupCmd)
		{
		GMGetAltPTPrinter()->BeginUserPageSetup();
		}
	else if (index == kPrintMsgCmd)
		{
		PrintSelectedMessages();
		}
	else if (index == kCheckMailCmd)
		{
		GCheckMail();
		}
	else if (index == kSaveCmd)
		{
		Save();
		}
	else if (index == kFlushCmd)
		{
		JSize count = itsList->GetElementCount();
		for (JSize index = count; index >= 1; index--)
			{
			GMessageHeader* header = itsList->NthElement(index);
			if (header->GetMessageStatus() == GMessageHeader::kDelete)
				{
				DataModified();
				JIndex i = itsViewDirs->GetElementCount();
				JBoolean found = kFalse;
				while ((i >= 1) && !found)
					{
					GMessageViewDir* dir = itsViewDirs->NthElement(i);
					if (dir->GetMessageHeader() == header)
						{
						dir->Close();
						found = kTrue;
						}
					i--;
					}
				itsList->Remove(header);
				CloseMessage(header);
				delete(header);
				}
			}
		GenerateFromList();
		itsEntry->ForceUpdate();
		UpdateMessageCount();
		Save();
		}
	else if (index == kCloseCmd)
		{
		Close();
		}
	else if (index == kQuitCmd)
		{
		JXGetApplication()->Quit();
		}
}

/******************************************************************************
 UpdateInboxMenu 


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

void
GMessageTableDir::UpdateInboxMenu()
{
	JSize count = itsInboxMenu->GetItemCount();
	for (JSize i = count; i >= 5; i--)
		{
		itsInboxMenu->DeleteItem(i);
		}

	JPtrArray<JString> inboxes;
	GGetPrefsMgr()->GetInboxes(inboxes);
	for (JSize i = 1; i <= inboxes.GetElementCount(); i++)
		{
		JString inbox;
		JString path;
		JSplitPathAndName(*(inboxes.NthElement(i)), &path, &inbox);
		itsInboxMenu->AppendItem(inbox);
		}
	inboxes.DeleteAll();

	for (JSize i = 5; i <= itsInboxMenu->GetItemCount(); i++)
		{
		itsInboxMenu->SetItemImage(i, itsSmallNewMailIcon, kFalse);
		}
}

/******************************************************************************
 HandleInboxMenu 


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

void
GMessageTableDir::HandleInboxMenu
	(
	const JIndex index
	)
{
	if (index == kAddInboxCmd)
		{
		GGetPrefsMgr()->AddInbox(itsMailFile);
		}
	else if (index == kRemoveInboxCmd)
		{
		GGetPrefsMgr()->DeleteInbox(itsMailFile);
		}
	else if (index == kRemoveAllInboxesCmd)
		{
		JString notice = "Are you sure you want to delete all of the inboxes?";
		JBoolean ok = JGetUserNotification()->AskUserYes(notice);
		if (ok)
			{
			GGetPrefsMgr()->DeleteAllInboxes();
			}
		}
	else if (index == kOpenAllInboxesCmd)
		{
		JPtrArray<JString> inboxes;
		GGetPrefsMgr()->GetInboxes(inboxes);
		for (JSize i = 1; i <= inboxes.GetElementCount(); i++)
			{
			GMGetApplication()->OpenMailbox(*(inboxes.NthElement(i)));
			}
		}
	else
		{
		JPtrArray<JString> inboxes;
		GGetPrefsMgr()->GetInboxes(inboxes);
		JSize count = inboxes.GetElementCount();
		assert(count >= index - 4);
		JString filename = *(inboxes.NthElement(index - 4));
		if (JFileExists(filename))
			{
			GMGetApplication()->OpenMailbox(filename);
			}
		else
			{
			JString report = "The mailbox \"" + filename + "\" does not exist. Would you like to remove it from the inbox list?";
			if (JGetUserNotification()->AskUserYes(report))
				{
				GGetPrefsMgr()->DeleteInbox(filename);
				}
			}
		}
}

/******************************************************************************
 HandlePrefsMenu 


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

void
GMessageTableDir::HandlePrefsMenu
	(
	const JIndex index
	)
{
	if (index == kPrefsCmd)
		{
		GGetPrefsMgr()->EditPrefs();
		}
	else if (index == kURLPrefsCmd)
		{
		JXGetHelpManager()->EditPrefs();
		}
	else if (index == kEditToolBarCmd)
		{
		itsToolBar->Edit();
		}
	else if (index == kSaveWindowSizeCmd)
		{
		GGetPrefsMgr()->SaveTableWindowSize(itsWindow);
		}

}

/******************************************************************************
 HandleHelpMenu 


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

void
GMessageTableDir::HandleHelpMenu
	(
	const JIndex index
	)
{
	if (index == kAboutCmd)
		{
		GMGetApplication()->DisplayAbout();
		}
	else if (index == kTOCCmd)
		{
		(JXGetHelpManager())->ShowSection(kTOCHelpName);
		}
	else if (index == kOverviewCmd)
		{
		(JXGetHelpManager())->ShowSection(kOverviewHelpName);
		}
	else if (index == kThisWindowCmd)
		{
		(JXGetHelpManager())->ShowSection(kMailboxHelpName);
		}
	else if (index == kChangesCmd)
		{
		(JXGetHelpManager())->ShowSection(kChangeLogName);
		}
	else if (index == kCreditsCmd)
		{
		(JXGetHelpManager())->ShowSection(kCreditsName);
		}

}

/******************************************************************************
 GenerateFromList

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

void
GMessageTableDir::GenerateFromList()
{
	itsFromList->DeleteAll();
	JSize count = itsList->GetElementCount();
	for (JSize index = 1; index <= count; index++)
		{
		GMessageHeader* header = itsList->NthElement(index);
		GMessageFrom* from = new GMessageFrom(header);
		assert(from != NULL);
		itsFromList->InsertSorted(from, kTrue);
		}
}

/******************************************************************************
 Update

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

JBoolean
GMessageTableDir::Update
	(
	const JBoolean unlock
	)
{
	time_t oldTime 		= itsEntry->GetModTime();
	JBoolean updated 	= itsEntry->Update();
	if (!itsSBSet->IsActive())
		{
		if (itsEntry->GetType() != JUNIXDirEntry::kDoesNotExist)
			{
			itsSBSet->Activate();
			}
		}
	if (updated || itsNeedsUpdate)
		{
		if (itsTable->IsDNDSource())
			{
			itsTable->AbortMessageDND();
			}
		if (itsWindow->IsIconified())
			{
			if (!itsNeedsUpdate)
				{
				if (GGetPrefsMgr()->IsBeepingOnNewMail())
					{
					GetWindow()->GetDisplay()->Beep();
					}
				
				itsNeedsUpdate = kTrue;
				if (oldTime != itsEntry->GetModTime())
					{
					itsWindow->SetIcon(*itsNewMailIcon);
					itsHasNewMail = kTrue;
					AdjustWindowTitle();
					}
				}
			}
		else
			{
//			cout << "b4" << flush;
			if (!itsNeedsUpdate)
				{
				if (GGetPrefsMgr()->IsBeepingOnNewMail())
					{
					GetWindow()->GetDisplay()->Beep();
					}
				itsHasNewMail = kTrue;
				AdjustWindowTitle();
				}
//			cout << "b5" << flush;
			GMessageHeaderList* list;
//			cout << "b6" << flush;
			itsPath->Hide();
//			cout << "b7" << flush;
			itsIndicator->Show();
//			cout << "b8" << flush;
			GetWindow()->Deactivate();
//			cout << "*b4*" << flush;
			GBlockUntilFileUnlocked(itsMailFile);
//			cout << "*b5*" << endl;
			GLockFile(itsMailFile);
			JBoolean ok = GMessageHeaderList::Create(itsMailFile, &list, itsPG);
			GetWindow()->Activate();
			itsIndicator->Hide();
			itsPath->Show();
			if (ok)
				{
				JPtrArray<GMessageFrom> fromlist;
				JSize count = list->GetElementCount();
				JSize index;
				for (index = 1; index <= count; index++)
					{
					GMessageHeader* header = list->NthElement(index);
					GMessageFrom* from = new GMessageFrom(header);
					assert(from != NULL);
					fromlist.Append(from);
					}
				count = fromlist.GetElementCount();
				for (index = count; index >= 1; index--)
					{
					GMessageFrom* from = fromlist.NthElement(index);
					JIndex findindex;
					if (itsFromList->SearchSorted(from, JOrderedSetT::kFirstMatch, &findindex))
						{
						GMessageFrom* currentfrom = itsFromList->NthElement(findindex);
						GMessageHeader* header = currentfrom->GetHeader();
						GMessageHeader* header2 = from->GetHeader();
						header->SetHeaderStart(header2->GetHeaderStart());
						header->SetHeaderEnd(header2->GetHeaderEnd());
						header->SetMessageEnd(header2->GetMessageEnd());
						list->Remove(header2);
						fromlist.Remove(from);
						itsFromList->Remove(currentfrom);
						delete header2;
						delete from;
						delete currentfrom;
						}
					}
				count = itsFromList->GetElementCount();
				for (index = count; index >= 1; index--)
					{
					GMessageFrom* from = itsFromList->NthElement(index);
					GMessageHeader* header = from->GetHeader();
					itsList->Remove(header);
					CloseMessage(header);
					itsFromList->Remove(from);
					delete header;
					delete from;
					}
				count = fromlist.GetElementCount();
				for (index = 1; index <= count; index++)
					{
					GMessageFrom* from = fromlist.NthElement(index);
					GMessageHeader* header = from->GetHeader();
					itsList->Append(header);
					}
				delete list;
				}
			
			GenerateFromList();
			
			UpdateMessageCount();
			if (unlock)
				{
				GUnlockFile(itsMailFile);
				}
//			cout << "breturn " << flush;
			return kTrue;
			}
		}
//	cout << "breturn " << flush;
	return kFalse;
}

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

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

void
GMessageTableDir::Save()
{
	if (!itsNeedsSave || !JFileExists(itsMailFile))
		{
		return;
		}
//	GBlockUntilFileUnlocked(itsMailFile);
//	if (!GLockFile(itsMailFile))
//		{
//		return;
//		}
	JString tempname = itsMailFile + ".tmp";
	mode_t perms;
	JError err = JGetPermissions(itsMailFile, &perms);
	if (!err.OK())
		{
		perms = 0600;
		}
	Update(kFalse);
	if (!GFileLocked(itsMailFile))
		{
		GLockFile(itsMailFile);
		}
	ifstream is(itsMailFile);
	assert(is.good());
	ofstream os(tempname);
	JBoolean ok = JConvertToBoolean(os.good());
	if (!ok)
		{
		tempname = JGetTempFileName();
		os.close();
		os.open(tempname);
		}
	JSize count = itsList->GetElementCount();
	JSize index;
	for (index = 1; index <= count; index++)
		{
		GMessageHeader* header = itsList->NthElement(index);
		GSaveMessage(is, os, header, kTrue);
		}

	is.close();
	os.close();

	GMReplaceFileWithTemp(tempname, itsMailFile);
	
	JSetPermissions(itsMailFile, perms);
	itsEntry->ForceUpdate();
	DataReverted();
	GUnlockFile(itsMailFile);
}

/******************************************************************************
 SaveMessage

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

void
GMessageTableDir::SaveMessage
	(
	istream& is, 
	ostream& os,
	GMessageHeader* header,
	const JBoolean update,
	const JBoolean flush
	)
{
	JSize headerStart = JTellp(os);
	if ((header->GetMessageStatus() != GMessageHeader::kDelete) || !flush)
		{
		JSeekg(is, header->GetHeaderStart());
		JString line;
		JBoolean foundG = kFalse;
		JBoolean foundS = kFalse;
		while ((line = JReadLine(is)) != "")
			{
			if (line.BeginsWith("Status:"))
				{
				foundS = kTrue;
				line = "Status: ";
				if (header->GetMessageStatus() != GMessageHeader::kNew)
					{
					line += "RO";
					line.Print(os);
					os << "\n";
					}
				}
			else if (line.BeginsWith("GMStatus:"))
				{
				foundG = kTrue;
				line = "GMStatus: ";
				if (header->GetMessageStatus() == GMessageHeader::kUrgent)
					{
					line += "U";
					line.Print(os);
					os << "\n";
					}
				else if (header->GetMessageStatus() == GMessageHeader::kImportant)
					{
					line += "I";
					line.Print(os);
					os << "\n";							
					}
				}
			else
				{
				line.Print(os);
				os << "\n";
				}
			}
		if (!foundG)
			{
			line = "GMStatus: ";
			if (header->GetMessageStatus() == GMessageHeader::kUrgent)
				{
				line += "U";
				line.Print(os);
				os << "\n";
				}
			else if (header->GetMessageStatus() == GMessageHeader::kImportant)
				{
				line += "I";
				line.Print(os);
				os << "\n";
				}
			}
		if (!foundS)
			{
			line = "Status: ";
			if (header->GetMessageStatus() != GMessageHeader::kNew)
				{
				line += "RO";
				line.Print(os);
				os << "\n";
				}
			}
		line = "\n";
		line.Print(os);
		JSize headerEnd = JTellp(os);
		JString body = JRead(is, header->GetMessageEnd() - header->GetHeaderEnd());
		if (!body.EndsWith("\n\n"))
			{
			body.Append("\n\n");
			}
		body.Print(os);
		JSize messageEnd = JTellp(os);
		if (update)
			{
			header->SetHeaderStart(headerStart);
			header->SetHeaderEnd(headerEnd);
			header->SetMessageEnd(messageEnd);
			}
		}
}

/******************************************************************************
 Close

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

JBoolean
GMessageTableDir::Close()
{
	Save();
	GGetInboxMgr()->MailboxClosed(GetMailFile());
	return GMManagedDirector::Close();
}

/******************************************************************************
 UpdateFileEntry

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

void
GMessageTableDir::UpdateFileEntry()
{
	itsEntry->ForceUpdate();
}

/******************************************************************************
 UpdateMessageCount

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

void
GMessageTableDir::UpdateMessageCount()
{
	JSize count = itsList->GetElementCount();
	JString messageCount(count);
	messageCount += " messages";
	itsMessageCount->SetText(messageCount);
	if (count > 0)
		{
		itsWindow->SetIcon(*itsHaveMailIcon);
		}
	else
		{
		itsWindow->SetIcon(*itsNoMailIcon);
		}
	AdjustWindowTitle();
}

/******************************************************************************
 ViewMessage

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

void
GMessageTableDir::ViewMessage
	(
	GMessageHeader* header
	)
{
	GMessageViewDir* dir;
	for (JSize i = 1; i <= itsViewDirs->GetElementCount(); i++)
		{
		dir = itsViewDirs->NthElement(i);
		if (header == dir->GetMessageHeader())
			{
			dir->Activate();
			return;
			}
		}
	JXGetApplication()->DisplayBusyCursor();
	dir = new GMessageViewDir(this, itsMailFile, header);
	assert(dir != NULL);
	itsViewDirs->Append(dir);
	dir->Activate();
}

/******************************************************************************
 CloseMessage

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

void
GMessageTableDir::CloseMessage
	(
	GMessageHeader* header
	)
{
	GMessageViewDir* dir;
	for (JSize i = 1; i <= itsViewDirs->GetElementCount(); i++)
		{
		dir = itsViewDirs->NthElement(i);
		if (header == dir->GetMessageHeader())
			{
			dir->Close();
			itsViewDirs->Remove(dir);
			}
		}
}

/******************************************************************************
 SaveState

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

void
GMessageTableDir::SaveState
	(
	JFileArray& fileArray
	)
{
	{
	ostrstream os;
	os << itsMailFile << " ";
	GetWindow()->WriteGeometry(os);
	fileArray.AppendElement(os);
	}
	JSize count;
	{
	ostrstream os;
	JXScrollbar* hsb = itsSBSet->GetHScrollbar();
	JXScrollbar* vsb = itsSBSet->GetVScrollbar();
	os << hsb->GetValue() << " ";
	os << vsb->GetValue() << " ";
	JBoolean hasNewMail = kFalse;
	os << itsHasNewMail << " ";
	os << itsEntry->GetModTime() << " ";
	for (JSize i = 1; i <= itsTable->GetColCount(); i++)
		{
		os << itsTable->GetColWidth(i) << " ";
		}
		
	count = itsViewDirs->GetElementCount();
	os << count << " ";
	fileArray.AppendElement(os);
	}
	for (JSize i = 1; i <= count; i++)
		{
		ostrstream os;
		itsViewDirs->NthElement(i)->SaveState(os);
		fileArray.AppendElement(os);
		}
}

/******************************************************************************
 ReadState

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

void
GMessageTableDir::ReadState
	(
	JFileArray& fileArray
	)
{
	JSize count;
	JFAID id = 2;

	{
	JStaticBuffer data;
	fileArray.GetElement(id, &data);
	jistrstream(is, data.GetData(), strlen(data));
	
	JCoordinate hval, vval;
	is >> hval;
	is >> vval;
	JXScrollbar* hsb = itsSBSet->GetHScrollbar();
	JXScrollbar* vsb = itsSBSet->GetVScrollbar();
	hsb->SetValue(hval);
	vsb->SetValue(vval);

	// check for had new mail
	JBoolean hadNewMail;
	is >> hadNewMail;
	if (hadNewMail)
		{
		if (GetWindow()->IsIconified())
			{
			GetWindow()->SetIcon(*itsNewMailIcon);
			itsHasNewMail = kTrue;
			AdjustWindowTitle();
			}
		}
	
	// check for different mod time
	time_t time;
	is >> time;
	if (time < itsEntry->GetModTime())
		{
		if (GetWindow()->IsIconified())
			{
			GetWindow()->SetIcon(*itsNewMailIcon);
			itsHasNewMail = kTrue;
			AdjustWindowTitle();
			}
		}
	
	for (JSize i = 1; i <= itsTable->GetColCount(); i++)
		{
		JCoordinate width;
		is >> width;
		itsTable->SetColWidth(i, width);
		}
	is >> count;
	}
	id.SetID(id.GetID() + 1);

	for (JSize i = 1; i <= count; i++)
		{
		JStaticBuffer data;
		fileArray.GetElement(id, &data);
		jistrstream(is, data.GetData(), strlen(data));
		JString fromline;
		is >> fromline;
		GMessageHeader* header;
		JIndex index = 1;
		JBoolean found = kFalse;
		while (index <= itsList->GetElementCount() && !found)
			{
			header = itsList->NthElement(index);
			if (header->GetHeader() == fromline)
				{
				found = kTrue;
				}
			index++;
			}
		if (found)
			{
			GMessageViewDir* dir = 
				new GMessageViewDir(this, itsMailFile, header);
			assert(dir != NULL);
			itsViewDirs->Append(dir);
			dir->ReadState(is);
			dir->Activate();
			}
		id.SetID(id.GetID() + 1);
		}
}

/******************************************************************************
 DataModified

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

void
GMessageTableDir::DataModified()
{
	if (!itsNeedsSave)
		{
		itsNeedsSave = kTrue;
		itsHasNewMail = kFalse;
		AdjustWindowTitle();
		}
}

/******************************************************************************
 DataReverted

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

void
GMessageTableDir::DataReverted()
{
	if (itsNeedsSave)
		{
		itsNeedsSave = kFalse;
		}
}

/******************************************************************************
 AdjustWindowTitle

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

void
GMessageTableDir::AdjustWindowTitle()
{
	JString title;
	JString path;
	JSplitPathAndName(itsMailFile, &path, &title);
	if (GGetPrefsMgr()->ShowingStateInTitle())
		{
		const JSize count = itsList->GetElementCount();
		if (itsHasNewMail)
			{
			title.Prepend("**");
			}
		else if (count > 0)
			{
			title.Prepend("*");
			}
		GetWindow()->SetTitle(title);
		}
	else
		{
		GetWindow()->SetTitle(title);
		}
}

/******************************************************************************
 SaveSelectedMessages (private)

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

void
GMessageTableDir::SaveSelectedMessages
	(
	const JBoolean headers
	)
{
	JString filename;
	if (GMGetAltChooseSaveFile()->SaveFile("File name:", "", "",  &filename))
		{
		ofstream os(filename);
		JBoolean ok = JConvertToBoolean(os.good());
		if (!ok)
			{
			JString notice = "The file \"" + filename + "\" could not be opened.";
			JGetUserNotification()->ReportError(notice);
			return;
			}
		JBoolean saveHeaders = GMGetAltChooseSaveFile()->IsSavingHeaders();
		SaveSelectedMessages(os, saveHeaders);
		os.close();
		}
	return;
}

/******************************************************************************
 AppendSelectedMessages (private)

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

void
GMessageTableDir::AppendSelectedMessages
	(
	const JBoolean headers
	)
{
	JString filename;
	if (GMGetAltChooseSaveFile()->ChooseFile("Choose file:", "", &filename))
		{
		mode_t perms;
		JError err = JGetPermissions(filename, &perms);
		if (!err.OK())
			{
			perms = 0600;
			}
		fstream is(filename, ios::in|ios::out, perms);
		JString tempname = filename + ".tmp";
		ofstream os(tempname);
		JBoolean ok = JConvertToBoolean(os.good());
		if (!ok)
			{
			tempname = JGetTempFileName();
			os.close();
			os.open(tempname);
			}
		JSize size;
		err = JGetFileLength(filename, &size);
		if (!err.OK())
			{
			JGetUserNotification()->ReportError("There was an unknown problem saving the file.");
			return;
			}
		JString temp;
		temp.Read(is, size);
		if ((size != 0) && !temp.EndsWith("\n\n"))
			{
			temp.Append("\n\n");
			}
		temp.Print(os);
		JBoolean saveHeaders = GMGetAltChooseSaveFile()->IsSavingHeaders();
		SaveSelectedMessages(os, saveHeaders);
		is.close();
		os.close();

		GMReplaceFileWithTemp(tempname, filename);
		
		}
}

/******************************************************************************
 SaveSelectedMessages (private)

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

void
GMessageTableDir::SaveSelectedMessages
	(
	ostream& 		os,
	const JBoolean 	headers
	)
{
	if (!GLockFile(itsMailFile))
		{
		return;
		}
	JTableSelection& s = itsTable->GetTableSelection();
	JTableSelectionIterator iter(&s);
	Update(kFalse);
	ifstream is(itsMailFile);
	assert(is.good());
	JPoint cell;
	while(iter.Next(&cell))
		{
		GMessageHeader* header = itsList->NthElement(cell.y);
		JIndex start;
		JIndex end = header->GetMessageEnd();
		if (headers)
			{
			start = header->GetHeaderStart();
			}
		else
			{
			start = header->GetHeaderEnd();
			}
		JSeekg(is, start);
		JString body = JRead(is, end - start);
		if (!body.EndsWith("\n\n"))
			{
			body.Append("\n\n");
			}
		body.Print(os);
		}
	is.close();
	GUnlockFile(itsMailFile);
}

/******************************************************************************
 PrintSelectedMessages (private)

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

void
GMessageTableDir::PrintSelectedMessages
	(
	const JBoolean headers
	)
{
	itsDummyPrintText->PrintPT();
	ListenTo(GMGetAltPTPrinter());
}
