/***********************************************************************
 *
 Copyright © 1995 - 1998, 3Com Corporation or its subsidiaries ("3Com").  
 All rights reserved.
   
 This software may be copied and used solely for developing products for 
 the Palm Computing platform and for archival and backup purposes.  Except 
 for the foregoing, no part of this software may be reproduced or transmitted 
 in any form or by any means or used to make any derivative work (such as 
 translation, transformation or adaptation) without express written consent 
 from 3Com.

 3Com reserves the right to revise this software and to make changes in content 
 from time to time without obligation on the part of 3Com to provide notification 
 of such revision or changes.  
 3COM MAKES NO REPRESENTATIONS OR WARRANTIES THAT THE SOFTWARE IS FREE OF ERRORS 
 OR THAT THE SOFTWARE IS SUITABLE FOR YOUR USE.  THE SOFTWARE IS PROVIDED ON AN 
 "AS IS" BASIS.  3COM MAKES NO WARRANTIES, TERMS OR CONDITIONS, EXPRESS OR IMPLIED, 
 EITHER IN FACT OR BY OPERATION OF LAW, STATUTORY OR OTHERWISE, INCLUDING WARRANTIES, 
 TERMS, OR CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND 
 SATISFACTORY QUALITY.

 TO THE FULL EXTENT ALLOWED BY LAW, 3COM ALSO EXCLUDES FOR ITSELF AND ITS SUPPLIERS 
 ANY LIABILITY, WHETHER BASED IN CONTRACT OR TORT (INCLUDING NEGLIGENCE), FOR 
 DIRECT, INCIDENTAL, CONSEQUENTIAL, INDIRECT, SPECIAL, OR PUNITIVE DAMAGES OF 
 ANY KIND, OR FOR LOSS OF REVENUE OR PROFITS, LOSS OF BUSINESS, LOSS OF INFORMATION 
 OR DATA, OR OTHER FINANCIAL LOSS ARISING OUT OF OR IN CONNECTION WITH THIS SOFTWARE, 
 EVEN IF 3COM HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.

 3Com, HotSync, Palm Computing, and Graffiti are registered trademarks, and 
 Palm III and Palm OS are trademarks of 3Com Corporation or its subsidiaries.

 IF THIS SOFTWARE IS PROVIDED ON A COMPACT DISK, THE OTHER SOFTWARE AND 
 DOCUMENTATION ON THE COMPACT DISK ARE SUBJECT TO THE LICENSE AGREEMENT 
 ACCOMPANYING THE COMPACT DISK.
 
 *****************************************************************************
 *
 * PROJECT:  Touchdown
 * FILE:     ExpenseAttendees.c
 * AUTHOR:	 Art Lamb: January 2, 1995
 *
 * DECLARER: Expense
 *
 * DESCRIPTION:
 *	  This is the Expense application's main module.
 *
 **********************************************************************/

#include <Pilot.h>
#include <CharAttr.h>

#define	NON_PORTABLE
#include <MemoryPrv.h>

#include "Expense.h"


/***********************************************************************
 *
 *	Internal Constants
 *
 ***********************************************************************/
#define AttendeesFont	NoteFont


/***********************************************************************
 *
 * FUNCTION:    CallAddressApp
 *
 * DESCRIPTION: This routine sends the Address application a launch 
 *              code to lookup an email address
 *
 * PARAMETERS:  key - key to search for
 *
 * RETURNED:    handle of string return by address application.
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	6/26/96	Initial Revision
 *
 ***********************************************************************/
static VoidHand CallAddressApp (CharPtr keyP)
{
#if EMULATION_LEVEL == EMULATION_NONE
	Err						err;
	UInt						cardNo;
	DWord						result;
	LocalID					dbID;
	DmSearchStateType		searchState;
	AddrLookupParamsType	params;
			
	// Get the card number and database id of the Address application.
	err = DmGetNextDatabaseByTypeCreator (true, &searchState, 
		sysFileTApplication, sysFileCAddress, true, &cardNo, &dbID);
	ErrFatalDisplayIf(err, "Address app not found");
	if (err) return (0);


	// parameters for the lookup
	params.title = MemHandleLock (DmGetResource (strRsc, 
							attendeesLookupTitleStrID));

	params.pasteButtonText = MemHandleLock (DmGetResource (strRsc, 
							attendeesLookupAddStrID));

	params.formatStringP = MemHandleLock (DmGetResource (strRsc, 
							attendeesLookupFormatStrID));

	params.field1 = addrLookupSortField;
	params.field2 = addrLookupCompany;
	params.field2Optional = true;
			
	if (keyP)
		{
		MemSet(params.lookupString, addrLookupStringLength, 0);	
		StrNCopy(params.lookupString, keyP, addrLookupStringLength - 1);
		}
	else
		*params.lookupString = 0;

	// <RM> 1-19-98, Fixed to pass 0 for flags, instead of sysAppLaunchFlagSubCall
	//  The sysAppLaunchFlagSubCall flag is for internal use by SysAppLaunch only
	//  and should NOT be set  on entry.
	err = SysAppLaunch (cardNo, dbID, 0 ,
				sysAppLaunchCmdLookup, (Ptr)&params, &result);

	ErrFatalDisplayIf(err, "Error sending lookup action to app");

	MemPtrUnlock (params.title);
	MemPtrUnlock (params.pasteButtonText);
	MemPtrUnlock (params.formatStringP);

	if (err) return (0);
	
	return (params.resultStringH);

#else
	{
	CharPtr p;
	CharPtr str = "#LOOKUP#";
	VoidHand h;
	
	h = MemHandleNew (StrLen(str) + 1);
	p = MemHandleLock (h);
	StrCopy (p, str);
	MemPtrUnlock (p);
	return (h);
	}

#endif
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesLookup
 *
 * DESCRIPTION: This routine called the Address Book application to
 *              lookup a attendees name, title and company.
 *
 * PARAMETERS:  fld - field object
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/23/96	Initial Revision
 *
 ***********************************************************************/
static void AttendeesLookup (void)
{
	Word		len;
	Word		pos;
	Word		end;
	Word		start;
	Word		resultLen;
	ULong		token = ', \0\0';			// ", "
	WordPtr	charAttrs;
	CharPtr 	ptr;
	CharPtr	keyP;
	CharPtr	text;
	CharPtr	resultP;
	FieldPtr fld;
	VoidHand	keyH = 0;
	VoidHand	resultH;
	Boolean	isSelection = true;


	fld = GetObjectPtr (AttendeesField);

	// If there is a selection then use it as the key.
	text = FldGetTextPtr (fld);
	FldGetSelection (fld, &start, &end);
	if (start == end)
		{
		isSelection = false;
		pos = FldGetInsPtPosition (fld);
		if (text && pos)
			{
			charAttrs = GetCharAttr ();

			// Find the start of the key value. Search backwards from the 
			// insertion point position for a delimiter.
			start = pos;
			end = start;
			while (start && ! 
					 (IsSpace(charAttrs, text[start-1]) || 
				 	  IsPunct(charAttrs, text[start-1])))
				start--;

			// If there are characters between the insertion point and the 
			// delimiter or start of the field if there are no deleimiter,
			// then find the end of the key.
			if (start != pos - 1)
				{
				len = FldGetTextLength (fld);
				while (end < len && ! 
						 (IsSpace(charAttrs, text[end]) || 
						  IsPunct(charAttrs, text[end])))
					end++;
				}
			}
		}

	// Copy the key value for the string.
	if (start != end)
		{
		keyH = MemHandleNew (end - start + 1);
		keyP = MemHandleLock (keyH);
		MemMove (keyP, &text[start], end - start);
		keyP[end - start] = 0;
		}


	resultH = CallAddressApp (keyP);
	if (keyH) MemHandleFree (keyH);

	// Replace the key string with the value returned by the Address 
	//application.
	if (resultH)
		{
		resultP = MemHandleLock (resultH);

		// If the record did not contain all the field we requested we may have
		// pairs of ", " together.  We need to remove the extra commas and spaces.  
		ptr = StrStr (resultP, (CharPtr) &token);
		while (ptr)
			{
			ptr += 2;
			len = StrLen (ptr);
			if (len && (StrNCompare (ptr, (CharPtr) &token, 2) == 0))
				MemMove (ptr, ptr+2, len-1);
			
			ptr = StrStr (ptr, (CharPtr) &token);
			}

		resultLen = StrLen(resultP);

		// I we can paste the result into the field through the clipboard so that
		// change can be undone with to "undo" command.
		if (resultLen <= cbdMaxTextLength)
			{
			ClipboardAddItem (clipboardText, resultP, resultLen);
			if (! isSelection)
				FldSetSelection (fld, start, end);
			FldPaste (fld);
			}
		else
			{
			if (start != end)
				FldDelete (fld, start, end);
			FldInsert (fld, resultP, resultLen);
			}

		MemHandleFree (resultH);
		}
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesUpdateScrollBar
 *
 * DESCRIPTION: This routine update the scroll bar.
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
static void AttendeesUpdateScrollBar (void)
{
	Word scrollPos;
	Word textHeight;
	Word fieldHeight;
	Short maxValue;
	FieldPtr fld;
	ScrollBarPtr bar;

	fld = GetObjectPtr (AttendeesField);
	bar = GetObjectPtr (AttendeesScrollBar);
	
	FldGetScrollValues (fld, &scrollPos, &textHeight,  &fieldHeight);

	if (textHeight > fieldHeight)
		maxValue = textHeight - fieldHeight;
	else
		maxValue = 0;

	SclSetScrollBar (bar, scrollPos, 0, maxValue, fieldHeight-1);
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesChangeFont
 *
 * DESCRIPTION: This routine changes the font used to display a attendees.
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
static void AttendeesChangeFont (Word controlID)
{
	FieldPtr fld;
	
	fld = GetObjectPtr (AttendeesField);
	
	if (controlID == AttendeesSmallFontButton)
		AttendeesFont = stdFont;
	else
		AttendeesFont = largeFont;
		
	// FldSetFont will redraw the field if it is visible.
	FldSetFont (fld, AttendeesFont);
	
	AttendeesUpdateScrollBar ();
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesLoadRecord
 *
 * DESCRIPTION: This routine loads a attendees from a to do record into 
 *              the attendees edit field.
 *
 * PARAMETERS:  frm - pointer to the Attendees View form
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
static void AttendeesLoadRecord (void)
{
	Word textOffset;
	Word textAllocSize;
	FieldPtr fld;
	VoidHand recordH;
	CharPtr recordP;
	ExpenseRecordType record;
	
	// Get a pointer to the attendees field.
	fld = GetObjectPtr (AttendeesField);
	
	// Set the font used in the attendees field.
	FldSetFont (fld, AttendeesFont);
	
	ExpenseGetRecord (ExpenseDB, CurrentRecord, &record, &recordH);
	recordP = MemDeref (recordH);
	textOffset = record.attendees - recordP;
	textAllocSize = StrLen (record.attendees) + 1;  // one for null terminator
	MemHandleUnlock (recordH);

	FldSetText (fld, recordH, textOffset, textAllocSize);
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesSave
 *
 * DESCRIPTION: This routine release any unused memory allocated for
 *              the attendees and mark the to do record dirty.
 *
 * PARAMETERS:  nothing
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
static void AttendeesSave (void)
{
	UInt		attr;
	FieldPtr fld;
	
	fld = GetObjectPtr (AttendeesField);

	// Was the attendees string modified by the user.
	if (FldDirty (fld))
		{
		// Release any free space in the attendees field.
		FldCompactText (fld);

		// Mark the record dirty.	
		DmRecordInfo (ExpenseDB, CurrentRecord, &attr, NULL, NULL);
		attr |= dmRecAttrDirty;
		DmSetRecordInfo (ExpenseDB, CurrentRecord, &attr, NULL);
		}


	// Clear the handle value in the field, otherwise the handle
	// will be freed when the form is disposed of,  this call also unlocks
	// the handle that contains the attendees string.
	FldSetTextHandle (fld, 0);
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesDoCommand
 *
 * DESCRIPTION: This routine preforms the menu command specified.
 *
 * PARAMETERS:  command  - menu item id
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
static Boolean AttendeesDoCommand (Word command)
{
//	FieldPtr fld;
	Boolean handled = true;
	
	switch (command)
		{
//		case attendeesPhoneLookupCmd:
//			fld = GetObjectPtr (AttendeesField);
//			PhoneNumberLookup (fld);
//			break;
			
		default:
			handled = false;
		}	

	return (handled);
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesScroll
 *
 * DESCRIPTION: This routine scrolls the mote Attendees View a page or a 
 *              line at a time.
 *
 * PARAMETERS:  direction - up or dowm
 *              oneLine   - true if scrolling a single line
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
static void AttendeesScroll (Short linesToScroll)
{
	Word				blankLines;
	Short				min;
	Short				max;
	Short				value;
	Short				pageSize;
	FieldPtr			fld;
	ScrollBarPtr	bar;
	
	fld = GetObjectPtr (AttendeesField);

	if (linesToScroll < 0)
		{
		blankLines = FldGetNumberOfBlankLines (fld);
		FldScrollField (fld, -linesToScroll, up);
		
		// If there were blank lines visible at the end of the field
		// then we need to update the scroll bar.
		if (blankLines)
			{
			// Update the scroll bar.
			bar = GetObjectPtr (AttendeesScrollBar);
			SclGetScrollBar (bar, &value, &min, &max, &pageSize);
			if (blankLines > -linesToScroll)
				max += linesToScroll;
			else
				max -= blankLines;
			SclSetScrollBar (bar, value, min, max, pageSize);
			}
		}

	else if (linesToScroll > 0)
		FldScrollField (fld, linesToScroll, down);
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesPageScroll
 *
 * DESCRIPTION: This routine scrolls the message a page up or down.
 *
 * PARAMETERS:   direction     up or down
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
static void AttendeesPageScroll (DirectionType direction)
{
	Short value;
	Short min;
	Short max;
	Short pageSize;
	Word linesToScroll;
	FieldPtr fld;
	ScrollBarPtr bar;

	fld = GetObjectPtr (AttendeesField);
	
	if (FldScrollable (fld, direction))
		{
		linesToScroll = FldGetVisibleLines (fld) - 1;
		FldScrollField (fld, linesToScroll, direction);

		// Update the scroll bar.
		bar = GetObjectPtr (AttendeesScrollBar);
		SclGetScrollBar (bar, &value, &min, &max, &pageSize);

		if (direction == up)
			value -= linesToScroll;
		else
			value += linesToScroll;
		
		SclSetScrollBar (bar, value, min, max, pageSize);
		return;
		}
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesInit
 *
 * DESCRIPTION: This routine initializes the Attendees View form.
 *
 * PARAMETERS:  frm - pointer to the Attendees View form.
 *
 * RETURNED:    nothing
 *
 * REVISION HISTORY:
 *			Name	Date		Description
 *			----	----		-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
static void AttendeesInit (FormPtr frm)
{
	Word 				controlID;
	FieldPtr 		fld;
	FieldAttrType	attr;
	
	AttendeesLoadRecord ();

	// Highlight the font push button.
	if (AttendeesFont == stdFont)
		controlID = AttendeesSmallFontButton;
	else
		controlID = AttendeesLargeFontButton;

	FrmSetControlGroupSelection (frm, AttendeesFontGroup, controlID);

	// Have the field send events to maintain the scroll bar.
	fld = GetObjectPtr (AttendeesField);
	FldGetAttributes (fld, &attr);
	attr.hasScrollBar = true;
	FldSetAttributes (fld, &attr);
}


/***********************************************************************
 *
 * FUNCTION:    AttendeesHandleEvent
 *
 * DESCRIPTION: This routine is the event handler for the "Attendees View"
 *              of the ToDo application.
 *
 * PARAMETERS:  event  - a pointer to an EventType structure
 *
 * RETURNED:    true if the event was handled and should not be passed
 *              to a higher level handler.
 *
 * REVISION HISTORY:
 *			Name	Date			Description
 *			----	----			-----------
 *			art	8/22/96	Initial Revision
 *
 ***********************************************************************/
Boolean AttendeesHandleEvent (EventPtr event)
{
	Word pos;
	FormPtr frm;
	FieldPtr fld;
	Boolean handled = false;


	if (event->eType == keyDownEvent)
		{
		if (event->data.keyDown.chr == pageUpChr)
			{
			AttendeesPageScroll (up);
			handled = true;
			}

		else if (event->data.keyDown.chr == pageDownChr)
			{
			AttendeesPageScroll (down);
			handled = true;
			}
		}


	else if (event->eType == ctlSelectEvent)
		{		
		switch (event->data.ctlSelect.controlID)
			{
			case AttendeesDoneButton:
				AttendeesSave ();
				FrmReturnToForm (0);
				FrmUpdateForm (DetailsDialog, updateAttendees);
				handled = true;
				break;

			case AttendeesLookupButton:
				AttendeesLookup ();
				handled = true;
				break;

			case AttendeesSmallFontButton:
			case AttendeesLargeFontButton:
				AttendeesChangeFont (event->data.ctlSelect.controlID);
				handled = true;
				break;
			}
		}


	else if (event->eType == fldChangedEvent)
		{
		frm = FrmGetActiveForm ();
		AttendeesUpdateScrollBar ();
		handled = true;
		}
		

	else if (event->eType == menuEvent)
		{
		handled = AttendeesDoCommand (event->data.menu.itemID);
		}
		

	else if (event->eType == frmOpenEvent)
		{
		frm = FrmGetActiveForm ();
		AttendeesInit (frm);
		FrmDrawForm (frm);
		AttendeesUpdateScrollBar ();
		FrmSetFocus (frm, FrmGetObjectIndex (frm, AttendeesField));
		handled = true;
		}
	

	else if (event->eType == frmGotoEvent)
		{
		ItemSelected = true;

		frm = FrmGetActiveForm ();
		AttendeesInit (frm);

		fld = GetObjectPtr (AttendeesField);
		pos = event->data.frmGoto.matchPos;
		FldSetScrollPosition (fld, pos);
		FldSetSelection (fld, pos, pos + event->data.frmGoto.matchLen);

		FrmDrawForm (frm);
		AttendeesUpdateScrollBar ();
		FrmSetFocus (frm, FrmGetObjectIndex (frm, AttendeesField));
		handled = true;
		}
			
	else if (event->eType == frmUpdateEvent)
		{
		frm = FrmGetActiveForm ();
		FrmDrawForm (frm);
		handled = true;
		}


	else if (event->eType == frmCloseEvent ||
				event->eType == frmSaveEvent)
		{
		if ( FldGetTextHandle (GetObjectPtr (AttendeesField)))
			AttendeesSave ();
		}

	
	else if (event->eType == sclRepeatEvent)
		{
		AttendeesScroll (event->data.sclRepeat.newValue - 
			event->data.sclRepeat.value);
		}

	return (handled);
}

