palm-os-sdk/handera-105/examples/Mail/Src/MailMessage.c
Tavisco 1c2f65cd40 Renamed handera-sdk-105 to handera-105
Its obviously a SDK, no need to repeat it :P
2023-11-16 22:06:58 -03:00

2444 lines
57 KiB
C

/******************************************************************************
*
* Copyright (c) 1996-2000 Palm, Inc. or its subsidiaries.
* All rights reserved.
*
* File: MailMessage.c
*
* Release: Palm OS SDK 4.0 (63220)
*
* Description:
* This is the Mail application's main module.
*
* History:
* June 19, 1996 Created by Art Lamb
*
*****************************************************************************/
#include <PalmOS.h>
#include <PalmUtils.h>
#include "Mail.h"
/***********************************************************************
*
* Internal Constants
*
***********************************************************************/
#define tabChrWidth 20
#define maxCopyLength 1000
/***********************************************************************
*
* Internal Structutes
*
***********************************************************************/
typedef struct {
UInt16 tabStop;
UInt16 fieldTextPos;
Char * fieldText;
MsgFieldType fieldType;
MsgSelectFieldType selectStartField;
MsgSelectFieldType selectEndField;
UInt8 reserved1;
UInt16 selectStartPos;
UInt16 selectEndPos;
RectangleType bounds;
MailDBRecordType record;
MemHandle recordH;
Char dateStr [timeStringLength + timeStringLength + 1];
Char toFriendlyName [maxFriendlyNameLen];
Char fromFriendlyName [maxFriendlyNameLen];
UInt8 reserved2;
} MessegeInfoType;
typedef MessegeInfoType * MessegeInfoPtr;
/***********************************************************************
*
* Internal Routines
*
***********************************************************************/
static Char * MsgGetFieldPtr (MessegeInfoPtr msg);
/***********************************************************************
*
* FUNCTION: MsgInit
*
* DESCRIPTION: This routine initialize the structure used to draw and
* scroll a mesage.
*
* PARAMETERS: msg - structure to initilize
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
static void MsgInit (MessegeInfoPtr msg, MsgFieldType fieldType,
UInt16 fieldTextPos)
{
UInt16 len;
UInt16 objIndex;
FormPtr frm;
FontID oldFont;
UInt16 tabStop;
int i;
static UInt16 labelIDs[] = { toStrId, fromStrId, subjectStrId, ccStrId, dateStrId };
// Get the tab stop for the start of the header text.
// DOLATER kwk - figure out if it's safe to use static labelIDs here,
// since they rely on globals.
oldFont = FntSetFont (MessageFont);
tabStop = 0;
for (i = 0; i < sizeof (labelIDs) / sizeof (labelIDs[0]); i += 1)
{
MemHandle resH;
resH = DmGetResource (strRsc, labelIDs[i]);
if (resH != 0)
{
Char * ptr;
Int16 width;
ptr = MemHandleLock (resH);
width = FntCharsWidth (ptr, StrLen (ptr));
if (tabStop < width)
{
tabStop = width;
}
MemHandleUnlock (resH);
DmReleaseResource (resH);
}
}
msg->tabStop = tabStop;
FntSetFont (oldFont);
// Get the bounds of the message.
frm = FrmGetActiveForm ();
objIndex = FrmGetObjectIndex (frm, MessageGadget);
FrmGetObjectBounds (frm, objIndex, &msg->bounds);
// Get the current record
MailGetRecord (MailDB, CurrentRecord, &msg->record, &msg->recordH);
// Get the friendly mame of the recipent and the sender of the message.
*msg->toFriendlyName = 0;
if ((msg->record.sentTo) && StrChr (msg->record.sentTo, '('))
{
MailParseAddress (msg->record.sentTo, true, msg->toFriendlyName,
maxFriendlyNameLen);
}
*msg->fromFriendlyName = 0;
if ((msg->record.from) && StrChr (msg->record.from, '('))
{
MailParseAddress (msg->record.from, true, msg->fromFriendlyName,
maxFriendlyNameLen);
}
msg->fieldTextPos = fieldTextPos;
msg->fieldType = fieldType;
msg->fieldText = MsgGetFieldPtr (msg);
msg->selectStartField = MessageSelectStartField;
msg->selectEndField = MessageSelectEndField;
msg->selectStartPos = MessageSelectStartPos;
msg->selectEndPos = MessageSelectEndPos;
if (DateToInt (msg->record.date))
{
// Format the date and time.
DateToAscii (msg->record.date.month,
msg->record.date.day,
(msg->record.date.year+firstYear) % 100,
DateFormat,
msg->dateStr);
len = StrLen (msg->dateStr);
msg->dateStr [len++] = spaceChr;
TimeToAscii (msg->record.time.hours,
msg->record.time.minutes,
TimeFormat,
&msg->dateStr[len]);
}
else
{
*msg->dateStr = 0;
}
}
/***********************************************************************
*
* FUNCTION: MsgSearch
*
* DESCRIPTION: This routine searchs the fields of the mail message
* that are displayable in the Message View.
*
* PARAMETERS: strToFind - string to find
* fieldP - returned: field the match occurred in
* posP - starting character position of the match
*
* RETURNED: true if a match was found
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/27/96 Initial Revision
*
***********************************************************************/
Boolean MsgSearch (MailDBRecordPtr recordP, Char * strToFind,
UInt16 * fieldNumP, UInt16 * posP, UInt16 * matchLengthP)
{
UInt32 pos;
UInt16 matchLength;
UInt16 fieldType;
Char * ptr;
Char friendlyName [maxFriendlyNameLen];
for (fieldType = msgFieldSentTo; fieldType <= msgFieldBody; fieldType++)
{
switch (fieldType)
{
case msgFieldSentTo:
if ((recordP->sentTo) && StrChr (recordP->sentTo, '(' ))
{
MailParseAddress (recordP->sentTo, true, friendlyName, maxFriendlyNameLen);
ptr = friendlyName;
}
else
ptr = recordP->sentTo;
break;
case msgFieldTo:
ptr = recordP->to;
break;
case msgFieldFrom:
ptr = recordP->from;
break;
case msgFieldSubject:
ptr = recordP->subject;
break;
case msgFieldCC:
ptr = recordP->cc;
break;
case msgFieldBody:
ptr = recordP->body;
break;
default:
ptr = NULL;
}
if (ptr && *ptr)
{
if (TxtFindString (ptr, strToFind, &pos, &matchLength))
{
*fieldNumP = fieldType;
*posP = pos;
/* CS2 (24659, 25166, 44807): pass along selection range */
*matchLengthP = matchLength;
return (true);
}
}
}
return (false);
}
/***********************************************************************
*
* FUNCTION: MsgIsHeaderField
*
* DESCRIPTION: Returns true if the mail field passed is a header field.
*
* PARAMETERS: fieldType - type a a mail record field.
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
static Boolean MsgIsHeaderField (MsgFieldType fieldType)
{
switch (fieldType)
{
case msgFieldSentTo:
case msgFieldTo:
case msgFieldFrom:
case msgFieldSubject:
case msgFieldCC:
case msgFieldDate:
return (true);
break;
}
return (false);
}
/***********************************************************************
*
* FUNCTION: MsgGetHeaderLabel
*
* DESCRIPTION: Returns a pointer to a header line's label.
*
* PARAMETERS: fieldType - type a a mail record field.
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
static MemHandle MsgGetHeaderLabel (MsgFieldType fieldType, MailAddressingType addressing)
{
UInt16 id;
switch (fieldType)
{
case msgFieldSentTo:
switch (addressing)
{
case sentTo:
id = toStrId;
break;
case sentCC:
id = ccStrId;
break;
case sentBCC:
id = bccStrId;
break;
}
break;
case msgFieldTo:
id = toStrId;
break;
case msgFieldFrom:
id = fromStrId;
break;
case msgFieldSubject:
id = subjectStrId;
break;
case msgFieldCC:
id = ccStrId;
break;
case msgFieldDate:
id = dateStrId;
break;
default:
return (0);
}
return (DmGetResource (strRsc, id));
}
/***********************************************************************
*
* FUNCTION: MsgGetFieldPtr
*
* DESCRIPTION: This routine returns a pointer to the specified field
* of the record passed.
*
* PARAMETERS: fieldType - type a a mail record field.
* repordP - pointer to a mail record.
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
static Char * MsgGetFieldPtr (MessegeInfoPtr msg)
{
switch (msg->fieldType)
{
case msgFieldSentTo:
if (*msg->toFriendlyName)
return (msg->toFriendlyName);
else
return (msg->record.sentTo);
case msgFieldTo:
return (msg->record.to);
case msgFieldFrom:
if (ShowFullHeader)
return (msg->record.from);
else if (*msg->fromFriendlyName)
return (msg->fromFriendlyName);
else
return (msg->record.from);
case msgFieldSubject:
return (msg->record.subject);
case msgFieldCC:
return (msg->record.cc);
case msgFieldBody:
return (msg->record.body);
case msgFieldDate:
return (msg->dateStr);
}
return (NULL);
}
/***********************************************************************
*
* FUNCTION: MsgNextField
*
* DESCRIPTION: This routine the previous field of a message.
*
* PARAMETERS: msg - used to draw and scroll a mesage.
*
* RETURNED: false if there is no next field
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/21/96 Initial Revision
*
***********************************************************************/
static Boolean MsgPreviousField (MessegeInfoPtr msg)
{
if (ShowFullHeader)
{
switch (msg->fieldType)
{
case msgFieldTo:
case msgFieldSentTo:
msg->fieldText = NULL;
return (false);
case msgFieldFrom:
msg->fieldType = msgFieldTo;
msg->fieldText = msg->record.to;
break;
case msgFieldCC:
msg->fieldType = msgFieldFrom;
msg->fieldText = msg->record.from;
break;
case msgFieldSubject:
msg->fieldType = msgFieldCC;
msg->fieldText = msg->record.cc;
break;
case msgFieldDate:
msg->fieldType = msgFieldSubject;
msg->fieldText = msg->record.subject;
break;
case msgFieldBlankLine:
msg->fieldType = msgFieldDate;
msg->fieldText = msg->dateStr;
break;
case msgFieldBody:
msg->fieldType = msgFieldBlankLine;
msg->fieldText = NULL;
break;
}
}
/*
switch (msg->fieldType)
{
case msgFieldTo:
case msgFieldSentTo:
msg->fieldText = NULL;
return (false);
case msgFieldFrom:
msg->fieldType = msgFieldTo;
msg->fieldText = msg->record.to;
break;
case msgFieldSubject:
msg->fieldType = msgFieldFrom;
msg->fieldText = msg->record.from;
break;
case msgFieldCC:
msg->fieldType = msgFieldSubject;
msg->fieldText = msg->record.subject;
break;
case msgFieldDate:
msg->fieldType = msgFieldCC;
msg->fieldText = msg->record.cc;
break;
case msgFieldBlankLine:
msg->fieldType = msgFieldDate;
msg->fieldText = msg->dateStr;
break;
case msgFieldBody:
msg->fieldType = msgFieldBlankLine;
msg->fieldText = NULL;
break;
}
}
*/
else
{
switch (msg->fieldType)
{
case msgFieldTo:
case msgFieldSentTo:
return (false);
case msgFieldFrom:
case msgFieldSubject:
case msgFieldCC:
case msgFieldDate:
msg->fieldType = msgFieldSentTo;
if (*msg->toFriendlyName)
msg->fieldText = msg->toFriendlyName;
else
msg->fieldText = msg->record.sentTo;
break;
case msgFieldBlankLine:
msg->fieldType = msgFieldFrom;
if (*msg->fromFriendlyName)
msg->fieldText = msg->fromFriendlyName;
else
msg->fieldText = msg->record.from;
break;
case msgFieldBody:
msg->fieldType = msgFieldBlankLine;
msg->fieldText = NULL;
break;
}
}
if (msg->fieldText)
msg->fieldTextPos = StrLen (msg->fieldText);
else
msg->fieldTextPos = 0;
return (true);
}
/***********************************************************************
*
* FUNCTION: MsgNextField
*
* DESCRIPTION: This routine the next field of a message.
*
* PARAMETERS: msg - used to draw and scroll a mesage.
*
* RETURNED: false if there is no next field
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
* MGolden 7/11/97 Added the subject view to the Int16 header
*
***********************************************************************/
static Boolean MsgNextField (MessegeInfoPtr msg)
{
if (ShowFullHeader)
{
switch (msg->fieldType)
{
case msgFieldTo:
case msgFieldSentTo:
msg->fieldType = msgFieldFrom;
msg->fieldText = msg->record.from;
break;
case msgFieldFrom:
msg->fieldType = msgFieldCC;
msg->fieldText = msg->record.cc;
break;
case msgFieldCC:
msg->fieldType = msgFieldSubject;
msg->fieldText = msg->record.subject;
break;
case msgFieldSubject:
msg->fieldType = msgFieldDate;
msg->fieldText = msg->dateStr;
break;
case msgFieldDate:
msg->fieldType = msgFieldBlankLine;
msg->fieldText = NULL;
break;
case msgFieldBlankLine:
msg->fieldType = msgFieldBody;
msg->fieldText = msg->record.body;
break;
case msgFieldBody:
return (false);
}
}
else
{
switch (msg->fieldType)
{
case msgFieldTo:
case msgFieldSentTo:
msg->fieldType = msgFieldFrom;
if (*msg->fromFriendlyName)
msg->fieldText = msg->fromFriendlyName;
else
msg->fieldText = msg->record.from;
break;
//mgmg new add the subject field to the Int16 header...
case msgFieldFrom:
msg->fieldType = msgFieldSubject;
msg->fieldText = msg->record.subject;
break;
case msgFieldSubject:
case msgFieldCC:
case msgFieldDate:
msg->fieldType = msgFieldBlankLine;
msg->fieldText = NULL;
break;
case msgFieldBlankLine:
msg->fieldType = msgFieldBody;
msg->fieldText = msg->record.body;
break;
case msgFieldBody:
return (false);
}
}
msg->fieldTextPos = 0;
return (true);
}
/***********************************************************************
*
* FUNCTION: MsgSelectFieldVisible
*
* DESCRIPTION: This routine returns true if the selection field is
* visible in the current message line.
*
* PARAMETERS: msg - used to draw and scroll a mesage.
* selectField - label of field of message
*
* RETURNED: true if selection field is visible
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 9/28/96 Initial Revision
*
***********************************************************************/
static Boolean MsgSelectFieldVisible (MessegeInfoPtr msg,
MsgSelectFieldType selectField)
{
UInt16 pos;
MsgFieldType fieldType;
if (msg->selectStartField > selectField ||
msg->selectEndField < selectField)
return (false);
fieldType = msg->fieldType;
pos = msg->fieldTextPos;
switch (selectField)
{
case msgSelectSentToLabel:
return (fieldType == msgFieldSentTo && pos == 0);
case msgSelectSentTo:
return (fieldType == msgFieldSentTo);
case msgSelectToLabel:
return (fieldType == msgFieldTo && pos == 0);
case msgSelectTo:
return (fieldType == msgFieldTo);
case msgSelectFromLabel:
return (fieldType == msgFieldFrom && pos == 0);
case msgSelectFrom:
return (fieldType == msgFieldFrom);
case msgSelectSubjectLabel:
return (fieldType == msgFieldSubject && pos == 0);
case msgSelectSubject:
return (fieldType == msgFieldSubject);
case msgSelectCC:
return (fieldType == msgFieldCC && ShowFullHeader);
case msgSelectCCLabel:
return (fieldType == msgFieldCC && pos == 0 && ShowFullHeader);
case msgSelectDate:
return (fieldType == msgFieldDate && ShowFullHeader);
case msgSelectDateLabel:
return (fieldType == msgFieldDate && pos == 0 && ShowFullHeader);
case msgSelectBlankLine:
return (fieldType == msgFieldBlankLine);
case msgSelectBody:
return (fieldType == msgFieldBody);
}
return (false);
}
/***********************************************************************
*
* FUNCTION: MsgWordWrap
*
* DESCRIPTION: Given a string, determine the number of characters that
* can be displayed within the specified width.
*
* PARAMETERS: msg - structure contain info for word wrapping
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
static UInt16 MsgWordWrap (MessegeInfoPtr msg)
{
UInt16 width;
UInt16 length = 0;
Char * ptr;
Boolean header;
// Leave room for the field description (ex: "To:") if the field
// has a description.
header = MsgIsHeaderField (msg->fieldType);
if (header)
{
width = msg->bounds.extent.x - msg->tabStop;
}
else
width = msg->bounds.extent.x;
ptr = MsgGetFieldPtr (msg);
if (ptr)
{
ptr += msg->fieldTextPos;
length = FntWordWrap (ptr, width);
}
/*
// If the field is a header and we didn't wrap on a space of linefeed
// character then wrap on the last address punctuation character.
if (header && length)
{
count = 0;
ptr += length - 1;
if (*(ptr+1) != nullChr && *ptr != spaceChr && *ptr != linefeedChr)
{
while (count < length)
{
// Wrap after right paren.
if (*ptr == ')')
{
length -= count;
break;
}
// Wrap before left paren, @, and period.
else if (*ptr == '(' || *ptr == '@' || *ptr == periodChr)
{
if (length > count + 1)
length -= count + 1;
break;
}
count++;
ptr--;
}
}
}
*/
return (length);
}
/***********************************************************************
*
* FUNCTION: MsgIsLableField
*
* DESCRIPTION: Returns true if the field passed is a label field.
*
* PARAMETERS: selectField - masseage label a field
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
static Boolean MsgIsLableField (MsgSelectFieldType selectField)
{
switch (selectField)
{
case msgSelectSentToLabel:
case msgSelectToLabel:
case msgSelectFromLabel:
case msgSelectSubjectLabel:
case msgSelectCCLabel:
case msgSelectDateLabel:
return (true);
}
return (false);
}
/***********************************************************************
*
* FUNCTION: InvertCharsRange
*
* DESCRIPTION: Invert specified characer range on the specified line.
* This rountine performs the actual inverting.
*
* PARAMETERS: fld pointer to a FieldType structure.
* lineNum line number
* startPos character position of start of range
* endPos character position of end of range.
*
* RETURNED: nothing.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/28/96 Initial Revision
*
***********************************************************************/
static void MsgInvertCharsRange (MessegeInfoPtr msg, MsgSelectFieldType selectField,
UInt16 lineNumber, UInt16 startPos, UInt16 endPos)
{
UInt16 length;
Int16 left;
Int16 width;
Char * ptr;
MemHandle resH;
Boolean label;
RectangleType r;
label = MsgIsLableField (selectField);
if (label)
{
resH = MsgGetHeaderLabel (msg->fieldType,
(MailAddressingType) msg->record.flags.addressing);
ptr = MemHandleLock (resH);
length = FntLineWidth (ptr, StrLen(ptr));
left = msg->bounds.topLeft.x +
(msg->tabStop - length) +
FntLineWidth (ptr, startPos);
}
else if (msg->fieldText)
{
ptr = msg->fieldText;
left = msg->bounds.topLeft.x +
FntLineWidth (&ptr[msg->fieldTextPos], startPos - msg->fieldTextPos);
if (MsgIsHeaderField (msg->fieldType))
left += msg->tabStop;
}
else
return;
width = FntLineWidth (&ptr[startPos], endPos - startPos);
r.topLeft.x = left;
r.extent.x = width;
r.extent.y = FntLineHeight ();
r.topLeft.y = msg->bounds.topLeft.y + (lineNumber * FntLineHeight ());
if (r.topLeft.x + r.extent.x > msg->bounds.topLeft.x + msg->bounds.extent.x)
r.extent.x = msg->bounds.extent.x - (r.topLeft.x - msg->bounds.topLeft.x);
/* CS2 (23430): use swap mode, not invert, for selection
WinInvertRectangle (&r, 0);
*/
WinPushDrawState();
WinSetDrawMode( winSwap );
WinPaintRectangle( &r, 0 );
WinPopDrawState();
if (label)
{
MemHandleUnlock (resH);
DmReleaseResource (resH);
}
}
/***********************************************************************
*
* FUNCTION: MsgInvertSelection
*
* DESCRIPTION: Invert the current selection.
*
* PARAMETERS: nothing
*
* RETURNED: nothing.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/28/96 Initial Revision
*
***********************************************************************/
static void MsgInvertSelection (MessegeInfoPtr msg)
{
UInt16 length;
UInt16 lineNumber;
UInt16 numLines;
UInt16 startPos;
UInt16 endPos;
Char * ptr;
MemHandle resH;
MsgSelectFieldType selectField;
if (msg->selectStartField == msgSelectNone)
return;
lineNumber = 0;
numLines = msg->bounds.extent.y / FntLineHeight ();
while (lineNumber < numLines)
{
length = MsgWordWrap (msg);
if (length)
{
for (selectField = msg->selectStartField;
selectField <= msg->selectEndField; selectField++)
{
if (MsgSelectFieldVisible (msg, selectField))
{
if (MsgIsLableField (selectField))
{
if (selectField == msg->selectStartField)
startPos = msg->selectStartPos;
else
startPos = 0;
if (selectField == msg->selectEndField)
endPos = msg->selectEndPos;
else
{
resH = MsgGetHeaderLabel (msg->fieldType,
(MailAddressingType) msg->record.flags.addressing);
ptr = MemHandleLock (resH);
endPos = StrLen (ptr);
MemHandleUnlock (resH);
DmReleaseResource (resH);
}
}
else
{
if (selectField == msg->selectStartField)
{
if (msg->selectStartPos < msg->fieldTextPos + length)
startPos = max (msg->selectStartPos, msg->fieldTextPos);
else
continue;
}
else
startPos = msg->fieldTextPos;
if (selectField == msg->selectEndField)
{
if (msg->selectEndPos >= msg->fieldTextPos)
endPos = min (msg->selectEndPos, msg->fieldTextPos + length);
else
continue;
}
else
endPos = msg->fieldTextPos + length;
}
MsgInvertCharsRange (msg, selectField, lineNumber, startPos, endPos);
}
}
msg->fieldTextPos += length;
lineNumber++;
}
else
{
if (msg->fieldType == msgFieldBlankLine)
lineNumber++;
if (! MsgNextField (msg))
break;
}
}
}
/***********************************************************************
*
* FUNCTION: MsgDrawLine
*
* DESCRIPTION: This routine draws a line of the message.
*
* PARAMETERS: msg - structure contain info for drawing
* charsToDraw - the number of characters to draw
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
static void MsgDrawLine (MessegeInfoPtr msg, UInt16 charsToDraw,
UInt16 lineNumber)
{
UInt16 len;
UInt16 count;
UInt16 charsDrawn;
Int16 x, y;
Int16 drawX;
Char * ptr;
Boolean header;
// Calculate the draw position of the line.
x = msg->bounds.topLeft.x;
y = msg->bounds.topLeft.y + (lineNumber * FntLineHeight ());
// If the line is the start of of the header draw the header label.
header = MsgIsHeaderField (msg->fieldType);
if (header)
{
if (msg->fieldTextPos == 0)
{
ptr = MemHandleLock (MsgGetHeaderLabel (msg->fieldType,
(MailAddressingType) msg->record.flags.addressing));
len = StrLen (ptr);
drawX = x +( Int16)msg->tabStop - (Int16)FntCharsWidth (ptr, len);
WinDrawChars (ptr, len, drawX, y);
MemPtrUnlock (ptr);
}
x += msg->tabStop;
}
// Get a pointer to the start of the text to draw.
ptr = MsgGetFieldPtr (msg);
ptr += msg->fieldTextPos;
// Don't draw the linefeedChr character.
if ((charsToDraw) && ptr[charsToDraw-1] == linefeedChr)
charsToDraw--;
// Don't draw trailing spaceChrs.
while (charsToDraw && ptr[charsToDraw-1] == spaceChr)
charsToDraw--;
// Draw the text.
count = 0;
charsDrawn = 0;
while (true)
{
// Count until we reach a tab character.
while ((count < charsToDraw) && (ptr[count] != tabChr))
count++;
// Draw the characters until/between the tab characters.
WinDrawChars (&ptr[charsDrawn], count-charsDrawn, x, y);
if (count >= charsToDraw) break;
// Move the draw position past the tab.
x += FntLineWidth (&ptr[charsDrawn], count-charsDrawn);
x += tabChrWidth - ((x - msg->bounds.topLeft.x) % tabChrWidth);
// Skip over the tab.
count++;
charsDrawn = count;
}
}
/***********************************************************************
*
* FUNCTION: MsgDraw
*
* DESCRIPTION: This routine draws an mail message.
*
* PARAMETERS: msg - structure contain info for drawing.
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
void MsgDraw (void)
{
UInt16 length;
UInt16 lineNumber;
UInt16 linesToDraw;
FontID currFont;
MessegeInfoType msg;
Boolean isHeader;
currFont = FntSetFont (MessageFont);
MsgInit (&msg, MessageTopField, MessageScrollPos);
lineNumber = 0;
linesToDraw = msg.bounds.extent.y / FntLineHeight ();
while (lineNumber < linesToDraw)
{
isHeader = false;
//mgmg change this to draw the empty header if needed...
length = MsgWordWrap (&msg);
if (length)
{
MsgDrawLine (&msg, length, lineNumber);
msg.fieldTextPos += length;
lineNumber++;
}
else if (msg.fieldType == msgFieldBlankLine)
{
lineNumber++;
if (! MsgNextField (&msg))
break;
}
else if (!MsgNextField (&msg))
break;
}
MemHandleUnlock (msg.recordH);
MsgInit (&msg, MessageTopField, MessageScrollPos);
MsgInvertSelection (&msg);
MemHandleUnlock (msg.recordH);
FntSetFont (currFont);
}
/***********************************************************************
*
* FUNCTION: MsgErase
*
* DESCRIPTION: This routine erase the current mail message.
*
* PARAMETERS: nothing
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/19/96 Initial Revision
*
***********************************************************************/
void MsgErase (void)
{
UInt16 objIndex;
FormPtr frm;
RectangleType bounds;
frm = FrmGetActiveForm ();
objIndex = FrmGetObjectIndex (frm, MessageGadget);
FrmGetObjectBounds (frm, objIndex, &bounds);
WinEraseRectangle (&bounds, 0);
}
/***********************************************************************
*
* FUNCTION: MsgGetFieldHeight
*
* DESCRIPTION: This routine returns the height of a format message
* field.
*
* PARAMETERS: fld - pointer to a FieldType structure.
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/20/96 Initial Revision
*
***********************************************************************/
static UInt16 MsgGetFieldHeight (MessegeInfoPtr msg)
{
UInt16 length;
UInt16 lines;
if (msg->fieldType == msgFieldBlankLine)
return (1);
lines = 0;
length = 0;
msg->fieldTextPos = 0;
do
{
length += MsgWordWrap (msg);
msg->fieldTextPos = length;
lines++;
}
while (msg->fieldText[length]);
// If the text end with a linefeed add one to the height.
if (length && msg->fieldText[length-1] == linefeedChr)
lines++;
return (lines);
}
/***********************************************************************
*
* FUNCTION: MsgGetScrollValues
*
* DESCRIPTION: This routine returns the values necessary to update a
* scroll bar.
*
* PARAMETERS: fld - pointer to a FieldType structure.
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/20/96 Initial Revision
*
***********************************************************************/
/*
void MsgGetScrollValues (UInt16 * textHeightP, UInt16 * pageHeightP)
{
UInt16 lines = 0;
FontID currFont;
MessegeInfoType msg;
currFont = FntSetFont (MessageFont);
MsgInit (&msg, msgFieldTo, 0);
while (true)
{
lines += MsgGetFieldHeight (&msg);
if (! MsgNextField (&msg))
break;
}
MemHandleUnlock (msg.recordH);
FntSetFont (currFont);
*textHeightP = lines;
*pageHeightP = msg.bounds.extent.y / FntLineHeight ();;
}
*/
/***********************************************************************
*
* FUNCTION: MsgGetScrollValues
*
* DESCRIPTION: This routine returns the values necessary to update a
* scroll bar.
*
* PARAMETERS: fld - pointer to a FieldType structure.
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/20/96 Initial Revision
*
***********************************************************************/
void MsgGetScrollValues (UInt16 * textHeightP, UInt16 * pageHeightP, UInt16 * topLineP)
{
UInt16 length;
UInt16 lines = 0;
UInt16 topLine = 0;
FontID currFont;
MsgFieldType fieldType;
MessegeInfoType msg;
currFont = FntSetFont (MessageFont);
if (ShowFullHeader)
fieldType = msgFieldTo;
else
fieldType = msgFieldSentTo;
MsgInit (&msg, fieldType, 0);
while (true)
{
if (msg.fieldType == MessageTopField)
topLine = lines;
if (msg.fieldType == msgFieldBlankLine)
lines++;
else
{
length = 0;
msg.fieldTextPos = 0;
do
{
length = MsgWordWrap (&msg);
if (msg.fieldType == MessageTopField)
{
if (msg.fieldTextPos < MessageScrollPos &&
msg.fieldTextPos + length > MessageScrollPos)
{
MessageScrollPos = msg.fieldTextPos;
topLine = lines;
}
else if (msg.fieldTextPos + length == MessageScrollPos)
//mgmg
//topLine = lines;
topLine = lines;
}
if (length)
{
msg.fieldTextPos += length;
lines++;
}
}
while (msg.fieldText[msg.fieldTextPos]);
// If the text end with a linefeed add one to the height.
if (length && msg.fieldText[length-1] == linefeedChr)
lines++;
}
if (! MsgNextField (&msg))
break;
}
MemHandleUnlock (msg.recordH);
*topLineP = topLine;
*textHeightP = lines;
*pageHeightP = msg.bounds.extent.y / FntLineHeight ();;
FntSetFont (currFont);
}
/***********************************************************************
*
* FUNCTION: MsgScrollUpNLines
*
* DESCRIPTION: This routine scrolls a field of the message winUp by the
* number of lines specified.
*
* PARAMETERS: msg structure contain message info
* linesToScroll number of lines to scroll.
*
* RETURNED: nothing.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 11/1/96 Initial Revision
*
***********************************************************************/
static UInt16 MsgScrollUpNLines (MessegeInfoPtr msg, UInt16 linesToScroll)
{
UInt16 width;
UInt16 lineCount = 0;
if (msg->fieldText && msg->fieldTextPos)
{
if (MsgIsHeaderField (msg->fieldType))
width = msg->bounds.extent.x - msg->tabStop;
else
width = msg->bounds.extent.x;
lineCount = linesToScroll;
FntWordWrapReverseNLines (msg->fieldText, width,
&lineCount, &msg->fieldTextPos);
}
if (msg->fieldType == msgFieldBlankLine && linesToScroll > 1)
lineCount = 1;
return (lineCount);
}
/***********************************************************************
*
* FUNCTION: MsgScroll
*
* DESCRIPTION: This routine scrolls the message view by the number of
* lines specified.
*
* PARAMETERS: direction - winUp or dowm
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 6/20/96 Initial Revision
*
***********************************************************************/
void MsgScroll (Int16 linesToScroll, WinDirectionType direction)
{
UInt16 length;
UInt16 linesScrolled;
FontID currFont;
MessegeInfoType msg;
currFont = FntSetFont (MessageFont);
MsgInit (&msg, MessageTopField, MessageScrollPos);
if (direction == winUp)
{
while (true)
{
linesScrolled = MsgScrollUpNLines (&msg, linesToScroll);
if (linesScrolled == linesToScroll)
break;
linesToScroll -= linesScrolled;
if (! MsgPreviousField (&msg))
break;
}
}
else if (direction == winDown)
{
while (linesToScroll)
{
length = MsgWordWrap (&msg);
if (length)
{
msg.fieldTextPos += length;
linesToScroll--;
}
else
{
if (msg.fieldType == msgFieldBlankLine)
linesToScroll--;
if (! MsgNextField (&msg))
break;
}
}
// If we at the end of the current field move to the next field so that
// the field position and field top of the top line will be correct.
if (length && msg.fieldText[msg.fieldTextPos] == 0)
MsgNextField (&msg);
}
MemHandleUnlock (msg.recordH);
FntSetFont (currFont);
MessageScrollPos = msg.fieldTextPos;
MessageTopField = msg.fieldType;
// DOLATER ??? - Optimize redraw
WinEraseRectangle (&msg.bounds, 0);
MsgDraw ();
}
/***********************************************************************
*
* FUNCTION: MsgUpdateHighlight
*
* DESCRIPTION: Adjust the current highlighted region to include or exclude
* the specified range of characters.
*
* PARAMETERS: msg - structure contain message info
* startPos character position of start of range
* endPos character position of end of range.
*
* RETURNED: nothing.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 9/29/96 Initial Revision
*
***********************************************************************/
static void MsgUpdateHighlight (MessegeInfoPtr msg,
MsgSelectFieldType startField, UInt16 startPos,
MsgSelectFieldType endField, UInt16 endPos)
{
MsgSelectFieldType invertStartField;
MsgSelectFieldType invertEndField;
UInt16 invertStartPos;
UInt16 invertEndPos;
// No current selection?
if (msg->selectStartField == msg->selectEndField &&
msg->selectStartPos == msg->selectEndPos)
{
invertStartField = startField;
invertEndField = endField;
invertStartPos = startPos;
invertEndPos = endPos;
}
// Add to the end of the selection.
else if (msg->selectStartField == startField &&
msg->selectStartPos == startPos)
{
if (msg->selectEndField < endField)
{
invertStartField = msg->selectEndField;
invertStartPos = msg->selectEndPos;
invertEndField = endField;
invertEndPos = endPos;
}
else if (msg->selectEndField > endField)
{
invertStartField = endField;
invertStartPos = endPos;
invertEndField = msg->selectEndField;
invertEndPos = msg->selectEndPos;
}
else
{
invertStartField = msg->selectEndField;
invertEndField = msg->selectEndField;
if (msg->selectEndPos < endPos)
{
invertStartPos = msg->selectEndPos;
invertEndPos = endPos;
}
else
{
invertStartPos = endPos;
invertEndPos = msg->selectEndPos;
}
}
}
// Add to the start of the selection.
else if (msg->selectEndField == endField &&
msg->selectEndPos == endPos)
{
if (msg->selectStartField < startField)
{
invertStartField = msg->selectStartField;
invertStartPos = msg->selectStartPos;
invertEndField = startField;
invertEndPos = startPos;
}
else if (msg->selectStartField > startField)
{
invertStartField = startField;
invertStartPos = startPos;
invertEndField = msg->selectStartField;
invertEndPos = msg->selectStartPos;
}
else
{
invertStartField = msg->selectStartField;
invertEndField = msg->selectStartField;
if (msg->selectStartPos < startPos)
{
invertStartPos = msg->selectStartPos;
invertEndPos = startPos;
}
else
{
invertStartPos = startPos;
invertEndPos = msg->selectStartPos;
}
}
}
else
{
if (msg->selectStartField < startField)
{
invertStartField = msg->selectStartField;
invertStartPos = msg->selectStartPos;
}
else if (msg->selectStartField > startField)
{
invertStartField = startField;
invertStartPos = startPos;
}
else
{
invertStartField = msg->selectStartField;
invertStartPos = min (msg->selectStartPos, startPos);
}
if (msg->selectEndField < endField)
{
invertEndField = endField;
invertEndPos = endPos;
}
else if (msg->selectEndField > endField)
{
invertEndField = msg->selectEndField;
invertEndPos = msg->selectEndPos;
}
else
{
invertEndField = msg->selectEndField;
invertEndPos = max (msg->selectEndPos, endPos);
}
}
msg->selectStartField = invertStartField;
msg->selectStartPos = invertStartPos;
msg->selectEndField = invertEndField;
msg->selectEndPos = invertEndPos;
MsgInvertSelection (msg);
msg->selectStartField = startField;
msg->selectStartPos = startPos;
msg->selectEndField = endField;
msg->selectEndPos = endPos;
}
/***********************************************************************
*
* FUNCTION: MsgSetSelection
*
* DESCRIPTION: This
*
* PARAMETERS:
*
* RETURNED: nothing.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 9/28/96 Initial Revision
*
***********************************************************************/
static void MsgSetSelection (MessegeInfoPtr msg,
MsgSelectFieldType startField, UInt16 startPos,
MsgSelectFieldType endField, UInt16 endPos)
{
UInt16 tempPos;
MsgSelectFieldType tempField;
if (startField == endField && startPos == endPos)
{
MsgInvertSelection (msg);
msg->selectStartField = msgSelectNone;
msg->selectStartPos = 0;
msg->selectEndField = msgSelectNone;
msg->selectEndPos = 0;
}
else if (startField > endField)
{
tempField = startField;
startField = endField;
endField = tempField;
tempPos = startPos;
startPos = endPos;
endPos = tempPos;
}
else if (startField == endField && startPos > endPos)
{
tempPos = startPos;
startPos = endPos;
endPos = tempPos;
}
MsgUpdateHighlight (msg, startField, startPos, endField, endPos);
MessageSelectStartField = msg->selectStartField;
MessageSelectEndField = msg->selectEndField;
MessageSelectStartPos = msg->selectStartPos;
MessageSelectEndPos = msg->selectEndPos;
}
/***********************************************************************
*
* FUNCTION: MsgGetPositionOfPoint
*
* DESCRIPTION: Given the position in the pen this routine will
* return the message field and character position
* of the point.
*
* PARAMETERS: msg - structure contain message info
* x, y - pen position
* fieldP - returned: field the pen is in
* posP - returned: character position the pen is on
* r - returned: bounds of character position
*
* RETURNED: nothing.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 9/28/96 Initial Revision
*
***********************************************************************/
static void MsgGetPositionOfPoint (MessegeInfoPtr msg, Int16 x, Int16 y,
MsgSelectFieldType * fldP, UInt16 * posP, RectanglePtr r)
{
UInt16 length;
UInt16 lineNumber;
UInt16 lines;
UInt16 width;
UInt16 lastWidth;
Int16 xPos;
Int16 yPos;
Char * ptr;
Boolean more;
Boolean heading;
MemHandle resH = 0;
UInt16 chSize;
// Find the line the point is on.
y = max (y, msg->bounds.topLeft.y);
y = min (y, msg->bounds.topLeft.y + msg->bounds.extent.y - 1);
yPos = (y - msg->bounds.topLeft.y) / FntLineHeight ();
lineNumber = 0;
lines = msg->bounds.extent.y / FntLineHeight ();
length = 0;
while (lineNumber < lines)
{
if (lineNumber >= yPos)
break;
length = MsgWordWrap (msg);
if (length)
{
msg->fieldTextPos += length;
lineNumber++;
}
if ((! length) || (msg->fieldText [msg->fieldTextPos] == 0))
{
while (true)
{
if (msg->fieldType == msgFieldBlankLine)
{
if (lineNumber >= yPos)
break;
lineNumber++;
}
more = MsgNextField (msg);
if (! more)
break;
if (msg->fieldText && *msg->fieldText)
break;
}
if (! more) break;
}
}
// Find the field the point in on.
x = max (x, msg->bounds.topLeft.x);
x = min (x, msg->bounds.topLeft.y + msg->bounds.extent.x - 1);
heading = MsgIsHeaderField (msg->fieldType);
if (heading &&
(msg->fieldTextPos == 0) &&
(x - msg->bounds.topLeft.x <= msg->tabStop))
{
switch (msg->fieldType)
{
case msgFieldSentTo: *fldP = msgSelectSentToLabel; break;
case msgFieldTo: *fldP = msgSelectToLabel; break;
case msgFieldFrom: *fldP = msgSelectFromLabel; break;
case msgFieldSubject: *fldP = msgSelectSubjectLabel; break;
case msgFieldCC: *fldP = msgSelectCCLabel; break;
case msgFieldDate: *fldP = msgSelectDateLabel; break;
}
resH = MsgGetHeaderLabel (msg->fieldType,
(MailAddressingType) msg->record.flags.addressing);
ptr = MemHandleLock (resH);
length = FntCharsWidth (ptr, StrLen (ptr));
x -= (msg->tabStop - length);
if (x < 0) x = 0;
}
else
{
switch (msg->fieldType)
{
case msgFieldSentTo: *fldP = msgSelectSentTo; break;
case msgFieldTo: *fldP = msgSelectTo; break;
case msgFieldFrom: *fldP = msgSelectFrom; break;
case msgFieldSubject: *fldP = msgSelectSubject; break;
case msgFieldCC: *fldP = msgSelectCC; break;
case msgFieldDate: *fldP = msgSelectDate; break;
case msgFieldBlankLine: *fldP = msgSelectBlankLine; break;
case msgFieldBody: *fldP = msgSelectBody; break;
}
ptr = msg->fieldText + msg->fieldTextPos;
length = MsgWordWrap (msg);
if (length && ptr[length-1] == linefeedChr)
length--;
if (heading)
x -= msg->tabStop;
}
// Determine the character position of the point.
// if ((! ptr) || (! *ptr))
// {
// *posP = 0;
// }
if (! ptr)
*posP = 0;
else if (! *ptr)
*posP = msg->fieldTextPos;
else
{
xPos = 0;
width = 0;
lastWidth = 0;
while (xPos < length)
{
chSize = TxtGetNextChar (ptr, xPos, NULL);
width += (ptr[xPos] == tabChr
? tabChrWidth - (width % tabChrWidth)
: FntCharsWidth (&ptr[xPos], chSize));
if (width >= x)
{
if ((x - lastWidth) >= ((width - lastWidth) >> 1))
xPos += chSize;
break;
}
xPos += chSize;
lastWidth = width;
}
// if (! resH)
// xPos += msg->fieldTextPos;
// *posP = xPos;
*posP = xPos + msg->fieldTextPos;
}
// Determine the bounds of the character position.
r->topLeft.x = msg->bounds.topLeft.x;
r->extent.x = msg->bounds.extent.x;
r->topLeft.y = msg->bounds.topLeft.y + (yPos * FntLineHeight());
r->extent.y = FntLineHeight();
if (length)
{
width = 0;
// Calculate the size of half of the width of the character before
// the insertion point.
if (xPos > 0)
{
chSize = TxtGetPreviousChar (ptr, xPos, NULL);
width = (ptr[xPos - chSize] != tabChr ? (FntCharsWidth (&ptr[xPos - chSize], chSize) + 1) / 2
: xPos > 1 ? (FntLineWidth (ptr, xPos) - FntLineWidth (ptr, xPos - chSize) + 1) / 2
: (tabChrWidth + 1) / 2);
r->topLeft.x += FntLineWidth (ptr, xPos) - width;
}
// Add half of the width of the character following the insertion point.
chSize = TxtGetNextChar (ptr, xPos, NULL);
width += (ptr[xPos] != tabChr ? FntCharsWidth (&ptr[xPos], chSize) / 2
: length > 1 ? (FntLineWidth (ptr, xPos + chSize) - FntLineWidth (ptr, xPos)) / 2
: tabChrWidth / 2);
r->extent.x = width;
}
switch (*fldP)
{
case msgSelectSentToLabel:
case msgSelectToLabel:
case msgSelectFromLabel:
case msgSelectSubjectLabel:
case msgSelectCCLabel:
case msgSelectDateLabel:
r->topLeft.x += (msg->tabStop - length);
break;
case msgSelectSentTo:
case msgSelectTo:
case msgSelectFrom:
case msgSelectSubject:
case msgSelectCC:
case msgSelectDate:
r->topLeft.x += msg->tabStop;
break;
}
// WinInvertRectangle (r, 0);
if (resH)
MemHandleUnlock (resH);
}
/***********************************************************************
*
* FUNCTION: MsgSelect
*
* DESCRIPTION: This is the massage selection routine.
*
* PARAMETERS: x, y - starting pen position
*
* RETURNED: nothing.
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 9/28/96 Initial Revision
*
***********************************************************************/
void MsgSelect (Int16 x, Int16 y)
{
FontID currFont;
UInt16 penPos;
UInt16 anchorPos;
Int16 value;
Int16 min;
Int16 max;
Int16 pageSize;
FormPtr frm;
Boolean penDown;
Boolean update;
ScrollBarPtr bar;
RectangleType r;
MessegeInfoType msg;
MsgSelectFieldType penFld;
MsgSelectFieldType anchorFld;
currFont = FntSetFont (MessageFont);
MsgInit (&msg, MessageTopField, MessageScrollPos);
// Cancel the existing selection
MsgSetSelection (&msg, msgSelectNone, 0, msgSelectNone, 0);
msg.fieldTextPos = MessageScrollPos;
msg.fieldType = MessageTopField;
msg.fieldText = MsgGetFieldPtr (&msg);
MsgGetPositionOfPoint (&msg, x, y, &anchorFld, &anchorPos, &r);
msg.fieldTextPos = MessageScrollPos;
msg.fieldType = MessageTopField;
msg.fieldText = MsgGetFieldPtr (&msg);
MsgSetSelection (&msg, anchorFld, anchorPos, anchorFld, anchorPos);
frm = FrmGetActiveForm ();
bar = FrmGetObjectPtr (frm, FrmGetObjectIndex (frm, MessageScrollBar));
do
{
// Above the message bounds?
if (y < msg.bounds.topLeft.y)
{
SclGetScrollBar (bar, &value, &min, &max, &pageSize);
if (value)
{
MsgScroll (1, winUp);
SclSetScrollBar (bar, value-1, min, max, pageSize);
update = true;
}
else
update = false;
}
// Below the message bounds?
else if (y >= msg.bounds.topLeft.y + msg.bounds.extent.y)
{
SclGetScrollBar (bar, &value, &min, &max, &pageSize);
if (value < max)
{
MsgScroll (1, winDown);
SclSetScrollBar (bar, value+1, min, max, pageSize);
update = true;
}
else
update = false;
}
// Over a different character?
else if (! RctPtInRectangle (x, y, &r))
update = true;
else
update = false;
if (update)
{
msg.fieldTextPos = MessageScrollPos;
msg.fieldType = MessageTopField;
msg.fieldText = MsgGetFieldPtr (&msg);
MsgGetPositionOfPoint (&msg, x, y, &penFld, &penPos, &r);
msg.fieldTextPos = MessageScrollPos;
msg.fieldType = MessageTopField;
msg.fieldText = MsgGetFieldPtr (&msg);
MsgSetSelection (&msg, penFld, penPos, anchorFld, anchorPos);
}
PenGetPoint (&x, &y, &penDown);
} while (penDown);
MemHandleUnlock (msg.recordH);
FntSetFont (currFont);
}
/***********************************************************************
*
* FUNCTION: MsgCopySelectedField
*
* DESCRIPTION: This routine a selected message field to the passed handle.
*
* PARAMETERS: msg - structure containing message info
* selectField - type of the field to copy
* destH - handle to copy to
* srcP - text of the message field to copy
* appendLinefeed - true
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 10/2/96 Initial Revision
*
***********************************************************************/
static Boolean MsgCopySelectedField (MessegeInfoPtr msg, MsgSelectFieldType selectField,
MemHandle destH, Char * srcP, Boolean appendLinefeed)
{
Err err;
UInt16 size;
UInt16 newSize;
UInt16 length = 0;
UInt16 endPos;
UInt16 startPos;
Char * destP;
Boolean truncated = false;
if (srcP)
{
if (selectField == msg->selectStartField)
startPos = msg->selectStartPos;
else
startPos = 0;
if (selectField == msg->selectEndField)
endPos = msg->selectEndPos;
else
endPos = StrLen (srcP);
length = endPos - startPos;
}
size = MemHandleSize (destH);
if (size + length > maxCopyLength)
{
length = maxCopyLength - size;
truncated = true;
}
newSize = size + length;
if (appendLinefeed)
newSize++;
err = MemHandleResize (destH, newSize);
if (err) return (true);
destP = MemHandleLock (destH);
destP += size-1;
if (srcP)
{
MemMove (destP, srcP+startPos, length);
destP += length;
}
// Add a linefeed to the end of the string.
if (appendLinefeed)
{
*destP = linefeedChr;
destP++;
}
// Null terminate the string.
*destP = 0;
MemHandleUnlock (destH);
return (truncated);
}
/***********************************************************************
*
* FUNCTION: MsgCopy
*
* DESCRIPTION: This routine copies the current selection to the clipboard.
*
* PARAMETERS: nothing
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 10/2/96 Initial Revision
*
***********************************************************************/
void MsgCopy (void)
{
Char * ptr;
MemHandle resH = 0;
MemHandle destH;
Boolean appendLinefeed;
Boolean truncated = false;
MsgFieldType topField;
MessegeInfoType msg;
MsgSelectFieldType selectField;
if (MessageSelectStartField == msgSelectNone)
return;
// Allocate a handle just large enough to hold a null-terminator.
destH = MemHandleNew (1);
if (! destH) return;
ptr = MemHandleLock (destH);
*ptr = 0;
MemPtrUnlock (ptr);
if (ShowFullHeader)
topField = msgFieldTo;
else
topField = msgFieldSentTo;
MsgInit (&msg, topField, 0);
while (true)
{
if (msg.fieldText && *msg.fieldText)
{
for (selectField = msg.selectStartField;
selectField <= msg.selectEndField; selectField++)
{
if (MsgSelectFieldVisible (&msg, selectField))
{
if (MsgIsLableField (selectField))
{
resH = MsgGetHeaderLabel (msg.fieldType,
(MailAddressingType) msg.record.flags.addressing);
ptr = MemHandleLock (resH);
appendLinefeed = false;
}
else
{
ptr = msg.fieldText + msg.fieldTextPos;
switch (selectField)
{
case msgSelectSentTo:
case msgSelectTo:
case msgSelectFrom:
case msgSelectSubject:
case msgSelectCC:
case msgSelectDate:
appendLinefeed = (msg.selectEndField > selectField ||
(msg.selectEndField == selectField &&
msg.selectEndPos == StrLen (ptr)));
break;
default:
appendLinefeed = false;
}
}
if (MsgCopySelectedField (&msg, selectField, destH, ptr, appendLinefeed))
truncated = true;
if (resH)
{
MemHandleUnlock (resH);
DmReleaseResource (resH);
resH = 0;
}
}
}
}
else if (msg.fieldType == msgFieldBlankLine &&
msg.selectStartField <= msgSelectBlankLine &&
msg.selectEndField >= msgSelectBlankLine)
{
if (MsgCopySelectedField (&msg, msgSelectBlankLine, destH, NULL, true))
truncated = true;
}
if (! MsgNextField (&msg))
break;
}
MemHandleUnlock (msg.recordH);
ptr = MemHandleLock (destH);
if (! truncated)
ClipboardAddItem (clipboardText, ptr, StrLen (ptr));
else
FrmAlert (ClipboardLimitAlert);
MemPtrFree (ptr);
}
/***********************************************************************
*
* FUNCTION: MsgSelectAll
*
* DESCRIPTION: This routine selects an entire message.
*
* PARAMETERS: nothing
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 10/3/96 Initial Revision
*
***********************************************************************/
void MsgSelectAll (void)
{
FontID currFont;
UInt16 endPos;
MessegeInfoType msg;
currFont = FntSetFont (MessageFont);
MsgInit (&msg, MessageTopField, MessageScrollPos);
// Cancel the existing selection
MsgSetSelection (&msg, msgSelectNone, 0, msgSelectNone, 0);
msg.fieldTextPos = MessageScrollPos;
msg.fieldType = MessageTopField;
msg.fieldText = MsgGetFieldPtr (&msg);
endPos = StrLen (msg.record.body);
MsgSetSelection (&msg, msgSelectSentToLabel, 0, msgSelectBody, endPos);
MemHandleUnlock (msg.recordH);
FntSetFont (currFont);
}
/***********************************************************************
*
* FUNCTION: MsgChangeFont
*
* DESCRIPTION: This routine change the font used to display a mail message.
*
* PARAMETERS: nothing
*
* RETURNED: nothing
*
* REVISION HISTORY:
* Name Date Description
* ---- ---- -----------
* art 12/15/96 Initial Revision
*
***********************************************************************/
void MsgChangeFont (void)
{
UInt16 pos;
UInt16 length;
UInt16 lineNumber;
UInt16 linesToDraw;
FontID currFont;
MessegeInfoType msg;
currFont = FntSetFont (MessageFont);
MsgInit (&msg, MessageTopField, 0);
//set the view to the top of the current field
MsgPreviousField (&msg);
MsgNextField (&msg);
MessageScrollPos = 0;
pos = 0;
lineNumber = 0;
linesToDraw = msg.bounds.extent.y / FntLineHeight ();
while (lineNumber < linesToDraw)
{
length = MsgWordWrap (&msg);
if (length)
{
msg.fieldTextPos += length;
if (MessageTopField == msg.fieldType)
{
if (msg.fieldTextPos < MessageScrollPos)
pos = msg.fieldTextPos;
else if (msg.fieldTextPos == MessageScrollPos)
{
pos = msg.fieldTextPos;
lineNumber++;
}
else
lineNumber++;
}
else
lineNumber++;
}
else if (msg.fieldType == msgFieldBlankLine)
{
lineNumber++;
if (! MsgNextField (&msg))
break;
}
else if (!MsgNextField (&msg))
break;
}
MessageScrollPos = pos;
MemHandleUnlock (msg.recordH);
if (lineNumber < linesToDraw && pos != 0 &&
(MessageTopField != msgFieldTo || MessageTopField == msgFieldSentTo))
{
MsgScroll (linesToDraw - lineNumber, winUp);
}
else
{
MsgErase ();
MsgDraw ();
}
FntSetFont (currFont);
}