зеркало из https://github.com/mozilla/gecko-dev.git
2095 строки
54 KiB
C++
2095 строки
54 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1996 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
|
|
|
|
|
|
// CComposeAddressTableView.cp
|
|
|
|
#include "abcom.h"
|
|
#include "addrbook.h"
|
|
#include "CComposeAddressTableView.h"
|
|
|
|
#include <LDropFlag.h>
|
|
#include <LTableMultiGeometry.h>
|
|
#include "NetscapeDragFlavors.h"
|
|
#include "msgcom.h"
|
|
#include "uprefd.h"
|
|
#include "UGraphicGizmos.h"
|
|
#include "CMailComposeWindow.h"
|
|
#include "CMailFlexTable.h"
|
|
#include "dirprefs.h"
|
|
#include "CComposeSession.h"
|
|
#include "CDrawingState.h"
|
|
#include "CPasteSnooper.h"
|
|
#include "resgui.h"
|
|
#include "CStandardFlexTable.h"
|
|
#include "LTableRowSelector.h"
|
|
#include "CTSMEditField.h"
|
|
#include "libi18n.h"
|
|
#include "prefapi.h"
|
|
|
|
#include <Folders.h>
|
|
|
|
const Int16 kIconWidth = 16;
|
|
const Int16 kIconMargin = 1;
|
|
|
|
const Int16 gsPopup_ArrowHeight = 5; // Actual height of the arrow
|
|
const Int16 gsPopup_ArrowWidth = 9; // Actual width of the arrow at widest
|
|
|
|
const Int16 kLeftPaddingOfAddressTypeLabel = 14;
|
|
const Int16 kLeftPaddingOfPopupArrow = 7;
|
|
const Int16 kRightPaddingOfPopupArrow = 3;
|
|
|
|
const Int16 kTextDistFromTop = 2;
|
|
|
|
#define kAddressTypeTextTraitsID 10009
|
|
#define kAddressTypePopupTextTraitsID 130
|
|
|
|
#ifdef MOZ_NEWADDR
|
|
#include "CNameCompletionPicker.h"
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField class
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
CMailAddressEditField::CMailAddressEditField(LStream* inStream)
|
|
: CTSMEditField(inStream)
|
|
, mTimeLastCall(0)
|
|
, mIsCompletedName(false)
|
|
, mNeedToAutoCompleteAtIdleTime(false)
|
|
, mMailNewsContext(nil)
|
|
, mPickerPane(nil)
|
|
, mDisplayString(nil)
|
|
, mHeaderString(nil)
|
|
, mExpandHeaderString(nil)
|
|
{
|
|
}
|
|
|
|
|
|
CMailAddressEditField::~CMailAddressEditField()
|
|
{
|
|
if (mMailNewsContext)
|
|
mMailNewsContext->RemoveUser(this);
|
|
|
|
if (mPickerPane)
|
|
AB_ClosePane(mPickerPane);
|
|
|
|
XP_FREE(mDisplayString);
|
|
XP_FREE(mHeaderString);
|
|
XP_FREE(mExpandHeaderString);
|
|
}
|
|
|
|
|
|
void CMailAddressEditField::Init()
|
|
{
|
|
mNeedToAutoCompleteAtIdleTime = false;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::FinishCreateSelf
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
void CMailAddressEditField::FinishCreateSelf()
|
|
{
|
|
LEditField::FinishCreateSelf();
|
|
|
|
AddAttachment(new CPasteSnooper("\r\t ", ",,\a"));
|
|
|
|
//¥ CMailComposeWindow* window = dynamic_cast<CMailComposeWindow*>(LWindow::FetchWindowObject(GetMacPort()));
|
|
//¥ ThrowIfNil_(window);
|
|
|
|
// Can't reuse the Composition context: we need an Address Book context
|
|
mMailNewsContext = new CMailNewsContext(MWContextAddressBook);
|
|
mMailNewsContext->SetWinCSID(INTL_DocToWinCharSetID(mMailNewsContext->GetDefaultCSID()));
|
|
mMailNewsContext->AddListener(this);
|
|
mMailNewsContext->AddUser(this);
|
|
StartListening();
|
|
|
|
(void)AB_CreateABPickerPane(&mPickerPane, *mMailNewsContext, CMailNewsContext::GetMailMaster(), 10);
|
|
if (mPickerPane)
|
|
CMailCallbackListener::SetPane(mPickerPane);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::HandleKeyPress
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
Boolean CMailAddressEditField::HandleKeyPress(const EventRecord& inKeyEvent)
|
|
{
|
|
Boolean keyHandled = true;
|
|
EKeyStatus theKeyStatus = keyStatus_Input;
|
|
Int16 theKey = inKeyEvent.message & charCodeMask;
|
|
|
|
if (inKeyEvent.modifiers & cmdKey)
|
|
theKeyStatus = keyStatus_PassUp; // Always pass up when Cmd-Key down
|
|
else
|
|
if (mKeyFilter != nil)
|
|
{
|
|
Int16 theChar = inKeyEvent.message & charCodeMask;
|
|
|
|
theKeyStatus = (*mKeyFilter)(mTextEditH, inKeyEvent.message,
|
|
theChar, inKeyEvent.modifiers);
|
|
}
|
|
|
|
if (mIsCompletedName)
|
|
{
|
|
if (mNumResults >= 2)
|
|
{
|
|
switch (theKey)
|
|
{
|
|
case char_Enter:
|
|
case char_Tab:
|
|
case char_Return:
|
|
XP_Bool showPicker;
|
|
PREF_GetBoolPref("ldap_1.autoComplete.showDialogForMultipleMatches", &showPicker);
|
|
if (showPicker)
|
|
{
|
|
// save location of the edited cell
|
|
LWindow* window = LWindow::FetchWindowObject(GetMacPort());
|
|
CComposeAddressTableView* addressTable = dynamic_cast<CComposeAddressTableView*>(window->FindPaneByID('Addr'));
|
|
ThrowIfNil_(addressTable);
|
|
STableCell editCell = addressTable->GetEditCell();
|
|
|
|
// save caret pos & selection
|
|
TEHandle teH = GetMacTEH();
|
|
short selStart = (*teH)->selStart;
|
|
short selEnd = (*teH)->selEnd;
|
|
|
|
// display the name completion picker
|
|
int selectedRow = CNameCompletionPicker::DisplayDialog(this, mPickerPane, mMailNewsContext, mNumResults);
|
|
|
|
// restore the edited cell
|
|
window->Activate();
|
|
window->Select();
|
|
addressTable->StartEditCell(editCell);
|
|
|
|
if (selectedRow > 0)
|
|
{
|
|
// if we have a result, put it in the cell
|
|
AB_NameCompletionCookie* cookie = AB_GetNameCompletionCookieForIndex(mPickerPane, selectedRow-1);
|
|
NameCompletionBECallbackFunction(cookie, 1, this);
|
|
}
|
|
else
|
|
{
|
|
// otherwise restore the previous selection
|
|
StFocusAndClipIfHidden focus(this);
|
|
::TESetSelect(selStart, selEnd, teH);
|
|
return true; // key was handled
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
StFocusAndClipIfHidden focus(this);
|
|
switch (theKeyStatus)
|
|
{
|
|
// If the user deletes a char, we need to do 2 deletes.
|
|
// The first deletes the nickname completion.
|
|
// The second actually erases a character.
|
|
case keyStatus_TEDelete:
|
|
if ((**mTextEditH).selEnd > 0)
|
|
{
|
|
if (mTypingAction == nil)
|
|
{
|
|
mTypingAction = new LTETypingAction(mTextEditH, this, this);
|
|
PostAction(mTypingAction);
|
|
}
|
|
|
|
if (mTypingAction != nil)
|
|
mTypingAction->BackwardErase();
|
|
else
|
|
::TEKey(char_Backspace, mTextEditH);
|
|
// UserChangedText();
|
|
}
|
|
break;
|
|
|
|
// If the user moves the cursor, we need to
|
|
// get rid of the "multiple names found".
|
|
case keyStatus_TECursor:
|
|
if (mNumResults >= 2)
|
|
{
|
|
::TEKey(char_Backspace, mTextEditH);
|
|
mIsCompletedName = false;
|
|
switch (theKey)
|
|
{
|
|
case char_UpArrow:
|
|
case char_DownArrow:
|
|
return LCommander::HandleKeyPress(inKeyEvent);
|
|
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (theKeyStatus == keyStatus_TEDelete)
|
|
{
|
|
CStr255 currentText;
|
|
GetDescriptor(currentText);
|
|
if (currentText.Length() == 0)
|
|
return LCommander::HandleKeyPress(inKeyEvent);
|
|
}
|
|
switch (theKey)
|
|
{
|
|
case char_UpArrow:
|
|
case char_DownArrow:
|
|
return LCommander::HandleKeyPress(inKeyEvent);
|
|
|
|
}
|
|
}
|
|
keyHandled = Inherited::HandleKeyPress(inKeyEvent);
|
|
return keyHandled;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::SpendTime
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
void CMailAddressEditField::SpendTime(const EventRecord& inMacEvent)
|
|
{
|
|
if (mNeedToAutoCompleteAtIdleTime)
|
|
{
|
|
if ((::TickCount() - mTimeLastCall) > ::LMGetKeyThresh() / 2)
|
|
StartNameCompletion();
|
|
}
|
|
Inherited::SpendTime(inMacEvent);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::UserChangedText
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
void CMailAddressEditField::UserChangedText()
|
|
{
|
|
mIsCompletedName = false;
|
|
mNeedToAutoCompleteAtIdleTime = true;
|
|
|
|
if ((::TickCount() - mTimeLastCall) > ::LMGetKeyThresh() / 2)
|
|
StartNameCompletion();
|
|
|
|
mTimeLastCall = ::TickCount();
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::NameCompletionBECallbackFunction
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
int CMailAddressEditField::NameCompletionBECallbackFunction(AB_NameCompletionCookie* cookie, int numResults, void* FECookie)
|
|
{
|
|
CMailAddressEditField* editField = (CMailAddressEditField*)FECookie;
|
|
if (cookie != NULL)
|
|
{
|
|
char* displayString = AB_GetNameCompletionDisplayString(cookie);
|
|
char* headerString = AB_GetHeaderString(cookie);
|
|
char* expandHeaderString = AB_GetExpandedHeaderString(cookie);
|
|
|
|
editField->SetNameCompletionResults(numResults, displayString, headerString, expandHeaderString);
|
|
|
|
AB_FreeNameCompletionCookie(cookie);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::SetNameCompletionResults
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
void CMailAddressEditField::SetNameCompletionResults(int numResults, char* displayString, char* headerString, char* expandHeaderString)
|
|
{
|
|
XP_FREE(mDisplayString);
|
|
XP_FREE(mHeaderString);
|
|
XP_FREE(mExpandHeaderString);
|
|
|
|
TEHandle teH = GetMacTEH();
|
|
short caretPos = (*teH)->selStart;
|
|
|
|
mNumResults = numResults;
|
|
if (numResults == 1)
|
|
{
|
|
mDisplayString = displayString;
|
|
mHeaderString = headerString;
|
|
mExpandHeaderString = expandHeaderString;
|
|
|
|
SetDescriptor(CStr255(mDisplayString));
|
|
}
|
|
else // we got 2 or more results
|
|
{
|
|
CStr255 userEntry;
|
|
GetDescriptor(userEntry);
|
|
userEntry[0] = caretPos;
|
|
|
|
mDisplayString = XP_STRDUP(userEntry);
|
|
mHeaderString = XP_STRDUP(userEntry);
|
|
mExpandHeaderString = XP_STRDUP(userEntry);
|
|
|
|
CStr255 multipleHitsStr;
|
|
CStr255 resultStr;
|
|
::GetIndString(multipleHitsStr, 10610, 2);
|
|
resultStr = mDisplayString + multipleHitsStr;
|
|
SetDescriptor(resultStr);
|
|
}
|
|
StFocusAndClipIfHidden focus(this);
|
|
::TESetSelect(caretPos, 1000, teH);
|
|
|
|
mIsCompletedName = true;
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::StartNameCompletion
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
void CMailAddressEditField::StartNameCompletion()
|
|
{
|
|
mNeedToAutoCompleteAtIdleTime = false;
|
|
|
|
if (!mPickerPane)
|
|
return;
|
|
|
|
CStr255 currentText;
|
|
GetDescriptor(currentText);
|
|
(void)AB_NameCompletionSearch(mPickerPane, (char*)currentText, NameCompletionBECallbackFunction, (void*)this);
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::PaneChanged
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
void CMailAddressEditField::PaneChanged(
|
|
MSG_Pane* /*inPane*/,
|
|
MSG_PANE_CHANGED_NOTIFY_CODE inNotifyCode,
|
|
int32 /*value*/)
|
|
{
|
|
switch (inNotifyCode)
|
|
{
|
|
}
|
|
}
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
// ¥ CMailAddressEditField::FinalizeEdit
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
const char* CMailAddressEditField::FinalizeEdit()
|
|
{
|
|
if (mIsCompletedName)
|
|
{
|
|
mIsCompletedName = false;
|
|
|
|
//¥¥¥ should also return mExpandHeaderString
|
|
return XP_STRDUP(mHeaderString);
|
|
}
|
|
else
|
|
{
|
|
CStr255 currentText;
|
|
GetDescriptor(currentText);
|
|
return XP_STRDUP(currentText);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
#else //MOZ_NEWADDR
|
|
#pragma mark -
|
|
CMailAddressEditField::CMailAddressEditField( LStream* inStream )
|
|
: CTSMEditField( inStream )
|
|
, mTimeLastCall(0)
|
|
, mDirServerList(nil)
|
|
, mAddressBook(nil)
|
|
, mIsCompletedName( false )
|
|
, mCheckAddress( false )
|
|
, mEntryID( MSG_MESSAGEIDNONE )
|
|
{
|
|
}
|
|
|
|
void CMailAddressEditField::Init()
|
|
{
|
|
mCheckAddress = false;
|
|
mEntryID = MSG_MESSAGEIDNONE;
|
|
}
|
|
|
|
//-----------------------------------
|
|
void CMailAddressEditField::FinishCreateSelf()
|
|
// override to do address-book name completion.
|
|
//-----------------------------------
|
|
{
|
|
LEditField::FinishCreateSelf();
|
|
Try_
|
|
{
|
|
AddAttachment(new CPasteSnooper("\r\t ", ",,\a"));
|
|
XP_List *list = FE_GetDirServers();
|
|
ThrowIf_( DIR_GetComposeNameCompletionAddressBook(list, &mDirServerList) != 0);
|
|
#if 0 // Currently FE_GetAddressbook ignores the message pane.
|
|
// Since we would like to use this control else where( AddressPicker, and Mailing list window
|
|
// its not a good idea for this class to be dependant on being created from a CMailComposeWindow
|
|
CMailComposeWindow* window
|
|
= dynamic_cast<CMailComposeWindow*>(
|
|
LWindow::FetchWindowObject(GetMacPort())
|
|
);
|
|
ThrowIfNil_(window);
|
|
|
|
mAddressBook = FE_GetAddressBook(
|
|
window->GetComposeSession()->GetMSG_Pane());
|
|
#else if
|
|
mAddressBook = FE_GetAddressBook( NULL);
|
|
#endif
|
|
ThrowIfNil_(mAddressBook);
|
|
}
|
|
Catch_(inErr)
|
|
{
|
|
mDirServerList = nil;
|
|
mAddressBook = nil;
|
|
}
|
|
EndCatch_
|
|
}
|
|
|
|
void
|
|
CMailAddressEditField::SpendTime(
|
|
const EventRecord& inMacEvent )
|
|
{
|
|
|
|
if( mCheckAddress )
|
|
{
|
|
UInt32 currentTime = ::TickCount();
|
|
if (currentTime - mTimeLastCall > ::LMGetKeyThresh() / 2)
|
|
UserChangedText();
|
|
}
|
|
LEditField::SpendTime( inMacEvent );
|
|
}
|
|
//-----------------------------------
|
|
void CMailAddressEditField::UserChangedText()
|
|
// override to do address-book name completion.
|
|
//-----------------------------------
|
|
{
|
|
UInt32 currentTime = ::TickCount();
|
|
mIsCompletedName = false;
|
|
mEntryID = MSG_MESSAGEIDNONE;
|
|
mCheckAddress = true;
|
|
if (currentTime - mTimeLastCall > ::LMGetKeyThresh() / 2)
|
|
{
|
|
ABID field;
|
|
if (mAddressBook)
|
|
{
|
|
mCheckAddress = false;
|
|
// Got a personal address book
|
|
if (mDirServerList)
|
|
{
|
|
// Got a DIR_Server entry for the address book
|
|
CStr255 currentText;
|
|
GetDescriptor(currentText);
|
|
if (::AB_GetIDForNameCompletion(
|
|
mAddressBook,
|
|
mDirServerList,
|
|
&mEntryID,
|
|
&field,
|
|
currentText) == 0 && mEntryID != MSG_MESSAGEIDNONE)
|
|
{
|
|
char newText[256];
|
|
newText[0] = '\0';
|
|
if (field == ABNickname)
|
|
AB_GetNickname(mDirServerList, mAddressBook, mEntryID, newText);
|
|
else
|
|
AB_GetFullName(mDirServerList, mAddressBook, mEntryID, newText);
|
|
if (newText[0])
|
|
{
|
|
// whew!
|
|
TEHandle teH = GetMacTEH();
|
|
short selStart = (*teH)->selStart;
|
|
SetDescriptor(CStr255(newText));
|
|
FocusDraw();
|
|
::TESetSelect(selStart, 1000, teH);
|
|
mIsCompletedName = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
mTimeLastCall = ::TickCount();
|
|
} // CMailAddressEditField::UserChangedText
|
|
|
|
Boolean CMailAddressEditField::HandleKeyPress( const EventRecord& inKeyEvent)
|
|
{
|
|
Boolean keyHandled = true;
|
|
EKeyStatus theKeyStatus = keyStatus_Input;
|
|
Int16 theKey = inKeyEvent.message & charCodeMask;
|
|
|
|
if (inKeyEvent.modifiers & cmdKey)
|
|
{ // Always pass up when the command
|
|
theKeyStatus = keyStatus_PassUp; // key is down
|
|
}
|
|
else if (mKeyFilter != nil)
|
|
{
|
|
Int16 theChar = inKeyEvent.message & charCodeMask;
|
|
|
|
theKeyStatus = (*mKeyFilter)(mTextEditH, inKeyEvent.message,
|
|
theChar, inKeyEvent.modifiers);
|
|
}
|
|
|
|
if( mIsCompletedName )
|
|
{
|
|
|
|
StFocusAndClipIfHidden focus(this);
|
|
// If the user deletes a char we need to do 2 deletes
|
|
// The first deletes the nickname completion
|
|
// the second actually erases a character
|
|
|
|
switch (theKeyStatus)
|
|
|
|
case keyStatus_TEDelete:{
|
|
if ((**mTextEditH).selEnd > 0) {
|
|
if (mTypingAction == nil) {
|
|
mTypingAction = new LTETypingAction(mTextEditH, this, this);
|
|
PostAction(mTypingAction);
|
|
}
|
|
if (mTypingAction != nil) {
|
|
mTypingAction->BackwardErase();
|
|
} else {
|
|
::TEKey(char_Backspace, mTextEditH);
|
|
}
|
|
//UserChangedText();
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( theKeyStatus == keyStatus_TEDelete )
|
|
{
|
|
CStr255 currentText;
|
|
GetDescriptor(currentText);
|
|
if( currentText.Length() == 0 )
|
|
return LCommander::HandleKeyPress(inKeyEvent);
|
|
}
|
|
switch (theKey)
|
|
{
|
|
case char_UpArrow:
|
|
case char_DownArrow:
|
|
return LCommander::HandleKeyPress(inKeyEvent);
|
|
|
|
}
|
|
}
|
|
keyHandled = LEditField::HandleKeyPress(inKeyEvent);
|
|
return keyHandled;
|
|
}
|
|
//-----------------------------------
|
|
const char* CMailAddressEditField::FinalizeEdit()
|
|
//-----------------------------------
|
|
{
|
|
char* fullName = nil;
|
|
CStr255 currentText;
|
|
GetDescriptor(currentText);
|
|
if (mDirServerList)
|
|
{
|
|
if ( mEntryID == MSG_MESSAGEIDNONE )
|
|
{
|
|
ABID field;
|
|
::AB_GetIDForNameCompletion(
|
|
mAddressBook,
|
|
mDirServerList,
|
|
&mEntryID,
|
|
&field,
|
|
currentText);
|
|
}
|
|
if ( mEntryID != MSG_MESSAGEIDNONE)
|
|
{
|
|
::AB_GetExpandedName(mDirServerList, mAddressBook, mEntryID, &fullName);
|
|
}
|
|
}
|
|
if (!fullName)
|
|
return XP_STRDUP(currentText);
|
|
return fullName;
|
|
} // CMailAddressEditField::FinalizeEdit()
|
|
|
|
#endif //MOZ_NEWADDR
|
|
|
|
#pragma mark -
|
|
|
|
CComposeAddressTableView::CComposeAddressTableView(LStream* inStream) :
|
|
LTableView(inStream), mInputField(NULL)
|
|
, LDragAndDrop(GetMacPort(), this)
|
|
, mCurrentlyAddedToDropList(true) // because LDragAndDrop constructor adds us.
|
|
, mDirty(false)
|
|
, mTextTraits( 10610 )
|
|
, mEditCell( 0, 0)
|
|
, mAddressTypeHasFocus(false)
|
|
, mTypedownTable(nil)
|
|
{
|
|
}
|
|
|
|
CComposeAddressTableView::~CComposeAddressTableView()
|
|
{
|
|
XP_FREEIF(mTypedownTable);
|
|
}
|
|
|
|
void CComposeAddressTableView::SetUpTableHelpers()
|
|
{
|
|
SetTableGeometry(new LTableMultiGeometry(this, CalculateAddressTypeColumnWidth(), 16));
|
|
SetTableSelector(new LTableRowSelector(this));
|
|
SetTableStorage(new CComposeAddressTableStorage(this));
|
|
}
|
|
|
|
void CComposeAddressTableView::FinishCreateSelf()
|
|
{
|
|
SetUpTableHelpers();
|
|
|
|
Assert_(mTableStorage != NULL);
|
|
|
|
InsertCols(2, 1, NULL, 0, false);
|
|
AdjustColumnWidths();
|
|
// Get the Editfield
|
|
mInputField = dynamic_cast< CMailAddressEditField*>( FindPaneByID ('Aedt') );
|
|
// highlight colors
|
|
RGBColor ignore;
|
|
UGraphicGizmos::CalcWindowTingeColors(GetMacPort(), ignore, mDropColor );
|
|
|
|
// Set up table for type-ahead
|
|
MenuHandle popupMenu = (MenuHandle)::GetResource('MENU', kAddressTypeMenuID );
|
|
ThrowIfNil_(popupMenu);
|
|
|
|
Int16 numItems = CountMItems(popupMenu);
|
|
|
|
// this will hold lower case of first letters, with trailing null
|
|
mTypedownTable = (char *)XP_ALLOC(numItems + 1);
|
|
|
|
for (Int16 i = 1; i <= numItems; i ++) // menu items are 1-based
|
|
{
|
|
CStr255 itemName;
|
|
GetMenuItemText(popupMenu, i, itemName);
|
|
mTypedownTable[i - 1] = tolower(itemName[1]);
|
|
}
|
|
mTypedownTable[numItems] = '\0';
|
|
|
|
ReleaseResource((Handle)popupMenu);
|
|
}
|
|
|
|
// adjust address column to be width of pane minus width of
|
|
// first column
|
|
void CComposeAddressTableView::AdjustColumnWidths()
|
|
{
|
|
SDimension16 frameSize;
|
|
UInt16 firstColWidth = GetColWidth(1);
|
|
GetFrameSize(frameSize);
|
|
SetColWidth(frameSize.width - firstColWidth, 2, 2);
|
|
}
|
|
|
|
void CComposeAddressTableView::ResizeFrameBy(
|
|
Int16 inWidthDelta,
|
|
Int16 inHeightDelta,
|
|
Boolean inRefresh)
|
|
{
|
|
LTableView::ResizeFrameBy( inWidthDelta, inHeightDelta, inRefresh);
|
|
AdjustColumnWidths();
|
|
// May have to adjust edit field
|
|
if( mEditCell.row!=0 )
|
|
{
|
|
Rect cellRect;
|
|
if( GetLocalCellRect( mEditCell, cellRect))
|
|
{
|
|
// put edit field over cell
|
|
cellRect.left += kIconWidth + 3;
|
|
cellRect.top +=1;
|
|
mInputField->ResizeFrameTo(cellRect.right - cellRect.left,
|
|
cellRect.bottom - cellRect.top,
|
|
false);
|
|
mInputField->PlaceInSuperImageAt(cellRect.left, cellRect.top, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void CComposeAddressTableView::ClickCell(const STableCell &inCell, const SMouseDownEvent &inMouseDown)
|
|
{
|
|
StopInputToAddressColumn(); // on any click
|
|
|
|
if (inCell.col == 1)
|
|
{
|
|
#if 0
|
|
LCommander::SwitchTarget(this);
|
|
LTableView::ClickCell(inCell, inMouseDown); //Does nothing
|
|
#endif //0
|
|
|
|
EAddressType oldAddressType = GetRowAddressType( inCell.row );
|
|
|
|
// load and install the menu
|
|
MenuHandle popupMenu = ::GetMenu( kAddressTypeMenuID );
|
|
if (popupMenu)
|
|
{
|
|
Int16 result;
|
|
|
|
FocusDraw();
|
|
|
|
{
|
|
StSysFontState theSysFontState;
|
|
|
|
// ¥ We also detach the resource so we don't run into problems
|
|
// for cases where there are multiple popups referencing the same
|
|
// menu resource.
|
|
::DetachResource( (Handle) popupMenu);
|
|
::InsertMenu( popupMenu, hierMenu);
|
|
|
|
Point where = inMouseDown.wherePort;
|
|
PortToGlobalPoint( where );
|
|
Rect cellRect;
|
|
if (GetLocalCellRect( inCell, cellRect ))
|
|
{
|
|
where = topLeft( cellRect );
|
|
::LocalToGlobal( &where );
|
|
}
|
|
|
|
// Adjust text traits to be the appropriate text traits for the popup
|
|
|
|
theSysFontState.SetTextTraits(/* kAddressTypePopupTextTraitsID */ );
|
|
|
|
Int16 defaultChoice = oldAddressType;
|
|
result = ::PopUpMenuSelect( popupMenu, where.v, where.h, defaultChoice );
|
|
}
|
|
|
|
// ¥ Clean up the menu
|
|
::DeleteMenu( kAddressTypeMenuID );
|
|
::DisposeHandle( (Handle) popupMenu); // OK because we detached it.
|
|
|
|
STableCell addressCell( inCell.row, 2);
|
|
|
|
if (result)
|
|
{
|
|
if( CellIsSelected( addressCell) )
|
|
SetSelectionAddressType( EAddressType( result ) );
|
|
else
|
|
{
|
|
SetRowAddressType( inCell.row, EAddressType( result ) );
|
|
RefreshCell( addressCell );
|
|
BroadcastMessage( msg_AddressChanged, NULL );
|
|
}
|
|
}
|
|
RefreshCell( inCell );
|
|
}
|
|
}
|
|
else
|
|
{ // in address cell
|
|
// was the click on the icon?
|
|
Rect iconRect;
|
|
GetLocalCellRect( inCell, iconRect );
|
|
iconRect.right = iconRect.left + kIconWidth+kIconMargin;
|
|
if (!(::PtInRect(inMouseDown.whereLocal, &iconRect)) )
|
|
StartEditCell( inCell );
|
|
}
|
|
}
|
|
|
|
void CComposeAddressTableView::DrawSelf()
|
|
{
|
|
Rect r;
|
|
if ( CalcLocalFrameRect( r ) )
|
|
EraseRect( &r );
|
|
Int16 bottom = r.bottom;
|
|
Int16 right = r.right;
|
|
// What Color should these lines be?
|
|
RGBColor lightTinge,darkTinge;
|
|
UGraphicGizmos::CalcWindowTingeColors(GetMacPort(), lightTinge, darkTinge);
|
|
::RGBForeColor(&lightTinge);
|
|
// vertical seperator
|
|
short width=mTableGeometry->GetColWidth(1);
|
|
::MoveTo(width,r.top);
|
|
::LineTo(width,r.bottom);
|
|
// Horizontal seperators
|
|
STableCell topLeftCell, botRightCell;
|
|
FetchIntersectingCells(r, topLeftCell, botRightCell);
|
|
GetLocalCellRect(topLeftCell,r);
|
|
|
|
short height = mTableGeometry->GetRowHeight(1); // All Rows are the same size
|
|
short currentHeight = r.top - height;
|
|
if (height > 0)
|
|
{
|
|
|
|
while (currentHeight<bottom)
|
|
{
|
|
currentHeight+=height;
|
|
::MoveTo(r.left,currentHeight);
|
|
::LineTo(right,currentHeight);
|
|
}
|
|
}
|
|
LTableView::DrawSelf();
|
|
}
|
|
|
|
Uint16 CComposeAddressTableView::CalculateAddressTypeColumnWidth()
|
|
{
|
|
Uint16 width;
|
|
MenuHandle menu = nil;
|
|
|
|
// Save port and port origin
|
|
|
|
StPortOriginState thePortOriginState(GetMacPort());
|
|
|
|
// Adjust port and port origin
|
|
|
|
FocusDraw();
|
|
|
|
// Get the menu
|
|
|
|
menu = ::GetMenu(kAddressTypeMenuID);
|
|
ThrowIfNil_(menu);
|
|
|
|
// Adjust system font state so menuWidth is calculated
|
|
// with the same metrics as when PopupMenuSelect is called.
|
|
|
|
StSysFontState theSysFontState;
|
|
theSysFontState.SetTextTraits(kAddressTypeTextTraitsID); // We *do* want kAddressTypeTextTraitsID and
|
|
// *not* kAddressTypePopupTextTraitsID.
|
|
// Calculate the width
|
|
|
|
::CalcMenuSize(menu);
|
|
width = (*menu)->menuWidth + kLeftPaddingOfPopupArrow + gsPopup_ArrowWidth + kRightPaddingOfPopupArrow;
|
|
|
|
// Release the menu
|
|
|
|
::ReleaseResource((Handle) menu);
|
|
|
|
return width;
|
|
}
|
|
|
|
void CComposeAddressTableView::DrawCell(const STableCell &inCell, const Rect &inLocalRect)
|
|
{
|
|
EAddressType type = GetRowAddressType( inCell.row );
|
|
|
|
if ( inCell.col == 1 )
|
|
{
|
|
// Draw the hilite region if we are taking keyboard input
|
|
if (mAddressTypeHasFocus)
|
|
{
|
|
STableCell addressCell(mEditCell.row, 1);
|
|
Rect cellRect;
|
|
|
|
if (GetLocalCellRect(addressCell, cellRect))
|
|
{
|
|
StColorPenState stateSaver;
|
|
StRegion hiliteRgn;
|
|
|
|
cellRect.top ++;
|
|
::RectRgn(hiliteRgn, &cellRect);
|
|
|
|
{
|
|
StRegion tempRgn(hiliteRgn);
|
|
|
|
::InsetRgn(tempRgn, 2, 2);
|
|
::DiffRgn(hiliteRgn, tempRgn, hiliteRgn);
|
|
}
|
|
|
|
UDrawingUtils::SetHiliteModeOn();
|
|
::InvertRgn(hiliteRgn);
|
|
}
|
|
}
|
|
|
|
// Draw the address type string
|
|
|
|
UTextTraits::SetPortTextTraits(kAddressTypeTextTraitsID);
|
|
|
|
MoveTo(inLocalRect.left + kLeftPaddingOfAddressTypeLabel, inLocalRect.bottom - 4);
|
|
|
|
MenuHandle menu = ::GetMenu( kAddressTypeMenuID );
|
|
if ( menu )
|
|
{
|
|
// ¥ We also detach the resource so we don't run into problems
|
|
// for cases where there are multiple popups referencing the same
|
|
// menu resource.
|
|
::DetachResource( (Handle) menu);
|
|
Str255 typeText;
|
|
:: GetMenuItemText( menu, type, typeText);
|
|
::DrawString( typeText );
|
|
::DisposeHandle( (Handle) menu); // OK because we detach
|
|
}
|
|
|
|
// Draw the drop flag
|
|
|
|
Rect iconRect;
|
|
|
|
UInt32 vDiff = (inLocalRect.bottom - inLocalRect.top - (gsPopup_ArrowHeight - 1)) >> 1;
|
|
iconRect.top = inLocalRect.top + vDiff;
|
|
iconRect.bottom = iconRect.top + gsPopup_ArrowHeight - 1;
|
|
iconRect.left = inLocalRect.right - gsPopup_ArrowWidth - 1 - kRightPaddingOfPopupArrow;
|
|
iconRect.right = iconRect.left + gsPopup_ArrowWidth - 1;
|
|
|
|
UGraphicGizmos::DrawPopupArrow(iconRect, true, true, false);
|
|
}
|
|
else
|
|
{
|
|
// Draw the icon
|
|
Rect iconRect = inLocalRect;
|
|
iconRect.left += kIconMargin;
|
|
iconRect.right = iconRect.left + kIconWidth;
|
|
|
|
ResIDT iconResIDT = 15260; // Default is a person id
|
|
if( type == eNewsgroupType )
|
|
iconResIDT = 15231 ;
|
|
CStandardFlexTable::DrawIconFamily(iconResIDT , 16, 16, 0, iconRect);
|
|
|
|
if (inCell.row != mEditCell.row)
|
|
{
|
|
// we're trying to draw an address string cell that
|
|
// isn't the one with the LEditField over it.
|
|
char* addr = NULL;
|
|
Uint32 size = sizeof(addr);
|
|
GetCellData(inCell, &addr, size);
|
|
|
|
if (addr)
|
|
{
|
|
Uint8 stringLen = LString::CStringLength(addr);
|
|
UTextTraits::SetPortTextTraits( mTextTraits );
|
|
|
|
Rect textRect = inLocalRect;
|
|
|
|
textRect.left = iconRect.right + 3;
|
|
textRect.right = inLocalRect.right;
|
|
textRect.top += kTextDistFromTop;
|
|
|
|
UGraphicGizmos::PlaceTextInRect( addr, stringLen, textRect, teFlushLeft, teFlushTop ); //flushtop to match the TEUpdate used by LEditField
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------
|
|
void CComposeAddressTableView::HiliteRow( TableIndexT inRow, Boolean inUnderline )
|
|
//-----------------------------------
|
|
{
|
|
STableCell cell;
|
|
Rect r;
|
|
|
|
cell.row = inRow;
|
|
cell.col = 1;
|
|
|
|
GetLocalCellRect(cell, r);
|
|
|
|
r.left = 0;
|
|
r.right = mFrameSize.width;
|
|
|
|
if (inUnderline)
|
|
{
|
|
r.top = r.bottom-1;
|
|
r.bottom += 1;
|
|
}
|
|
|
|
//RGBColor savedHiliteColor;
|
|
UDrawingUtils::SetHiliteModeOn();
|
|
//LMGetHiliteRGB(&savedHiliteColor);
|
|
//::HiliteColor(&mDropColor);
|
|
::InvertRect(&r);
|
|
//::HiliteColor(&savedHiliteColor);
|
|
}
|
|
|
|
void CComposeAddressTableView::DirectInputToAddressColumn()
|
|
{
|
|
if (mAddressTypeHasFocus) return;
|
|
|
|
mAddressTypeHasFocus = true;
|
|
SwitchTarget(this);
|
|
|
|
STableCell theCell(mEditCell.row, 1);
|
|
RefreshCell(theCell);
|
|
theCell.col = 2;
|
|
RefreshCell(theCell);
|
|
}
|
|
|
|
|
|
void CComposeAddressTableView::StopInputToAddressColumn()
|
|
{
|
|
if (!mAddressTypeHasFocus) return;
|
|
|
|
mAddressTypeHasFocus = false;
|
|
SwitchTarget(mInputField);
|
|
|
|
STableCell theCell(mEditCell.row, 1);
|
|
RefreshCell(theCell);
|
|
theCell.col = 2;
|
|
RefreshCell(theCell);
|
|
}
|
|
|
|
|
|
Boolean CComposeAddressTableView::HandleKeyPress(const EventRecord &inKeyEvent)
|
|
{
|
|
Boolean keyHandled = true;
|
|
Boolean cmdKeyDown = (inKeyEvent.modifiers & cmdKey) != 0;
|
|
Char16 c = inKeyEvent.message & charCodeMask;
|
|
|
|
if (mAddressTypeHasFocus)
|
|
{
|
|
EAddressType newAddressType = eNoAddressType;
|
|
char *thisOne = mTypedownTable;
|
|
|
|
for (Int16 i = 0; *thisOne; i++, thisOne ++)
|
|
{
|
|
if (tolower(c) == *thisOne)
|
|
{
|
|
newAddressType = (EAddressType)(i + 1); // enum is 1-based
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (newAddressType != eNoAddressType)
|
|
{
|
|
STableCell addressCell(mEditCell.row, 1);
|
|
SetRowAddressType(mEditCell.row, newAddressType);
|
|
RefreshCell(addressCell);
|
|
BroadcastMessage( msg_AddressChanged, NULL );
|
|
return keyHandled;
|
|
}
|
|
}
|
|
|
|
switch (c)
|
|
{
|
|
case char_Backspace:
|
|
case char_FwdDelete:
|
|
DeleteSelection();
|
|
return true;
|
|
break;
|
|
|
|
case char_UpArrow:
|
|
EndEditCell();
|
|
STableCell previousCell( GetFirstSelectedCell().row, 2 );
|
|
if( previousCell.row != 1 )
|
|
{
|
|
previousCell.row--;
|
|
StartEditCell( previousCell );
|
|
}
|
|
keyHandled = true;
|
|
break;
|
|
|
|
|
|
/*EndEditCell();
|
|
STableCell nextCell( GetFirstSelectedCell().row, 2 );
|
|
nextCell.row++;
|
|
StartEditCell( nextCell );
|
|
keyHandled = true;
|
|
break;*/
|
|
case char_Enter:
|
|
if( !mEditCell.IsNullCell() )
|
|
{
|
|
EndEditCell();
|
|
STableCell cellToSelect( GetFirstSelectedCell().row, 2);
|
|
SelectCell( cellToSelect );
|
|
}
|
|
else
|
|
{
|
|
STableCell cellToSelect( GetFirstSelectedCell().row, 2);
|
|
StartEditCell( cellToSelect );
|
|
}
|
|
break;
|
|
|
|
case char_LeftArrow:
|
|
if (!mAddressTypeHasFocus && cmdKeyDown)
|
|
DirectInputToAddressColumn();
|
|
else
|
|
keyHandled = LCommander::HandleKeyPress(inKeyEvent);
|
|
break;
|
|
case char_RightArrow:
|
|
if (mAddressTypeHasFocus && cmdKeyDown)
|
|
StopInputToAddressColumn();
|
|
else
|
|
keyHandled = LCommander::HandleKeyPress(inKeyEvent);
|
|
break;
|
|
case char_DownArrow:
|
|
case char_Return:
|
|
case char_Tab:
|
|
EventRecord tabKeyEvent(inKeyEvent);
|
|
tabKeyEvent.message &= ~charCodeMask; // clear char code bits
|
|
tabKeyEvent.message |= char_Tab;
|
|
Boolean backward = ((inKeyEvent.modifiers & shiftKey) != 0);
|
|
TableIndexT nRows, nCols;
|
|
GetTableSize(nRows, nCols);
|
|
EndEditCell();
|
|
StopInputToAddressColumn();
|
|
STableCell cell( GetFirstSelectedCell().row, 2);
|
|
if (backward && cell.row <= 1)
|
|
{
|
|
keyHandled = LCommander::HandleKeyPress(tabKeyEvent); // tab out of here
|
|
break;
|
|
}
|
|
else if (!backward && cell.row >= nRows)
|
|
{
|
|
// If we were editing, and we tabbed out...
|
|
if (cell.row != 0)
|
|
{
|
|
// Tabbing out of a non-empty addressee field adds another new addressee
|
|
char* addr = NULL;
|
|
Uint32 size = sizeof(addr);
|
|
GetCellData( cell, &addr, size);
|
|
if (addr && *addr && (c != char_Tab) )
|
|
{
|
|
EAddressType addressType = GetRowAddressType( nRows );
|
|
char emptyString[1]="\0";
|
|
InsertNewRow( addressType, emptyString, true );
|
|
keyHandled = true;
|
|
}
|
|
else
|
|
{
|
|
keyHandled = LCommander::HandleKeyPress(tabKeyEvent); // tab out of here
|
|
}
|
|
}
|
|
else if (nRows==0)
|
|
{
|
|
InsertNewRow(true, true);
|
|
keyHandled = true;
|
|
}
|
|
break;
|
|
}
|
|
else if (backward)
|
|
cell.row--;
|
|
else
|
|
cell.row++;
|
|
StartEditCell( cell );
|
|
break;
|
|
|
|
default:
|
|
keyHandled = LCommander::HandleKeyPress(inKeyEvent);
|
|
} // switch
|
|
return keyHandled;
|
|
}
|
|
|
|
Boolean CComposeAddressTableView::ClickSelect(const STableCell &inCell,
|
|
const SMouseDownEvent &inMouseDown)
|
|
{
|
|
|
|
if( inCell.col == 1 )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
SwitchTarget( this );
|
|
EndEditCell();
|
|
if( !mEditCell.IsNullCell() )
|
|
UnselectCell( mEditCell );
|
|
return LTableView::ClickSelect( inCell, inMouseDown );
|
|
}
|
|
}
|
|
|
|
void
|
|
CComposeAddressTableView::ClickSelf(
|
|
const SMouseDownEvent &inMouseDown)
|
|
{
|
|
STableCell hitCell;
|
|
SPoint32 imagePt;
|
|
|
|
LocalToImagePoint(inMouseDown.whereLocal, imagePt);
|
|
|
|
if (GetCellHitBy(imagePt, hitCell)) {
|
|
if (ClickSelect(hitCell, inMouseDown)) {
|
|
ClickCell(hitCell, inMouseDown);
|
|
}
|
|
|
|
} else { // Click is outside of any Cell
|
|
InsertNewRow( true, true); // so create a new cell
|
|
}
|
|
}
|
|
|
|
void CComposeAddressTableView::FillInRow( Int32 row, EAddressType inAddressType, const char* inAddress)
|
|
{
|
|
STableCell cell;
|
|
cell.col = 1;
|
|
cell.row = row;
|
|
SetCellData( cell, &inAddressType, sizeof(inAddressType));
|
|
RefreshCell( cell );
|
|
cell.col = 2;
|
|
SetCellData( cell, inAddress, sizeof(inAddress));
|
|
RefreshCell( cell );
|
|
}
|
|
|
|
// Takes in a string explodes it, and inserts it into the table
|
|
Int32 CComposeAddressTableView::CommitRow( const char* inString, STableCell cell)
|
|
{
|
|
EAddressType originAddressType = GetRowAddressType( cell.row );
|
|
Boolean dirty = false;
|
|
// Update and do name replacement
|
|
// Explode string to 1 entry per line
|
|
MSG_HeaderEntry *returnList = nil;
|
|
Int32 numberItems = MSG_ExplodeHeaderField( originAddressType, inString, &returnList );
|
|
Int32 numberRowsAdded = 0;
|
|
|
|
if ( numberItems > 1)
|
|
{
|
|
InsertRows( (numberItems - 1), cell.row, NULL, NULL, false );
|
|
dirty = true;
|
|
}
|
|
// We will get 0 if we passed in a Null string, this occurs when the user
|
|
// deletes an exsiting email address. Should the row be removed?
|
|
if (numberItems == 0)
|
|
{
|
|
if( !dirty )
|
|
{
|
|
char *addr = NULL;
|
|
Int32 size = sizeof(addr);
|
|
GetCellData( cell, &addr, size);
|
|
if( addr !=NULL )
|
|
dirty = true;
|
|
}
|
|
SetCellData( cell, inString, sizeof(inString) );
|
|
}
|
|
else if ( numberItems != -1 )
|
|
{
|
|
for (Int32 currentItem = 0; currentItem < numberItems; currentItem ++ )
|
|
{
|
|
if ( !dirty )
|
|
{
|
|
char *addr = NULL;
|
|
Int32 size = sizeof(addr);
|
|
GetCellData( cell, &addr, size);
|
|
if( addr == NULL )
|
|
dirty = true;
|
|
else
|
|
dirty = XP_STRCMP( addr, returnList[currentItem].header_value);
|
|
}
|
|
// Use input field to do name expansion on each entry
|
|
char* unexpandedName = returnList[currentItem].header_value;
|
|
char* actualName = unexpandedName;
|
|
EAddressType addressType = (EAddressType)returnList[currentItem].header_type; // this is just what we passed in above
|
|
|
|
if ( XP_STRNCASECMP(unexpandedName, "mailto:", 7) == 0 )
|
|
{
|
|
actualName = unexpandedName + 7;
|
|
//addressType = eToType;
|
|
}
|
|
else if ( (XP_STRNCASECMP(unexpandedName, "news://", 7) == 0) ||
|
|
(XP_STRNCASECMP(unexpandedName, "snews://", 8) == 0) )
|
|
{
|
|
// test whether this URL points to the current server being used for this message.
|
|
// if not, and there is no other news recipient so far, set the news post url
|
|
// in the backend, and just enter the group name
|
|
//MSG_SetCompHeader( , MSG_NEWSPOSTURL_HEADER_MASK, ); etc
|
|
|
|
// no backend support yet, so just put in the whole URL
|
|
actualName = unexpandedName;
|
|
addressType = eNewsgroupType;
|
|
}
|
|
else if ( (XP_STRNCASECMP(unexpandedName, "news:", 5) == 0) ||
|
|
(XP_STRNCASECMP(unexpandedName, "snews:", 6) == 0) )
|
|
{
|
|
actualName = unexpandedName + 5;
|
|
addressType = eNewsgroupType;
|
|
}
|
|
|
|
mInputField->Init();
|
|
mInputField->SetDescriptor( CStr255( actualName ) );
|
|
const char* expandedName = mInputField->FinalizeEdit();
|
|
|
|
FillInRow( cell.row, addressType, expandedName );
|
|
|
|
XP_FREE( unexpandedName );
|
|
XP_FREE( (char*)expandedName );
|
|
cell.row++;
|
|
}
|
|
XP_FREE ( returnList );
|
|
numberRowsAdded = numberItems-1;
|
|
}
|
|
|
|
if ( dirty )
|
|
BroadcastMessage( msg_AddressChanged, NULL );
|
|
|
|
Refresh();
|
|
|
|
return numberRowsAdded;
|
|
}
|
|
|
|
Int32 CComposeAddressTableView::FinalizeAddrCellEdit()
|
|
{
|
|
int32 numRowsAdded;
|
|
const char* fullName = nil;
|
|
Try_
|
|
{
|
|
fullName = mInputField->FinalizeEdit();
|
|
if (fullName)
|
|
{
|
|
numRowsAdded = CommitRow( fullName, mEditCell );
|
|
XP_FREE((char*)fullName);
|
|
}
|
|
}
|
|
Catch_(inErr)
|
|
{
|
|
XP_FREEIF((char*)fullName);
|
|
}
|
|
EndCatch_
|
|
return numRowsAdded;
|
|
}
|
|
|
|
Boolean CComposeAddressTableView::ObeyCommand(CommandT inCommand, void *ioParam)
|
|
{
|
|
switch (inCommand)
|
|
{
|
|
case msg_TabSelect:
|
|
STableCell cell(1, 2);
|
|
StartEditCell( cell );
|
|
return true;
|
|
}
|
|
return LCommander::ObeyCommand(inCommand, ioParam);
|
|
}
|
|
|
|
void CComposeAddressTableView::HideEditField()
|
|
{
|
|
mInputField->Hide();
|
|
LView::OutOfFocus(NULL);
|
|
RefreshCell(mEditCell);
|
|
}
|
|
|
|
#if 0
|
|
void CComposeAddressTableView::ListenToMessage(MessageT inMessage, void* ioParam)
|
|
{
|
|
#if 0 // NO longer have buttons to listen to
|
|
switch(inMessage)
|
|
{
|
|
case msg_AddAddressee:
|
|
InsertNewRow(true, true);
|
|
break;
|
|
case msg_RemoveAddressee:
|
|
DeleteSelection();
|
|
break;
|
|
}
|
|
#endif //0
|
|
}
|
|
#endif
|
|
|
|
void CComposeAddressTableView::InsertNewRow(Boolean inRefresh, Boolean inEdit)
|
|
{
|
|
char* dummy = NULL;
|
|
EndEditCell();
|
|
TableIndexT numRows;
|
|
GetNumRows(numRows);
|
|
InsertRows(1, numRows++, &dummy, sizeof(dummy), inRefresh);
|
|
STableCell cell(numRows, 2);
|
|
if (inEdit)
|
|
StartEditCell(cell);
|
|
}
|
|
|
|
void CComposeAddressTableView::InsertNewRow(EAddressType inAddressType, const char* inAddress, Boolean inEdit)
|
|
{
|
|
InsertNewRow(false, false);
|
|
STableCell cell;
|
|
cell.col = 1;
|
|
GetNumRows(cell.row);
|
|
SetCellData(cell, &inAddressType, sizeof(inAddressType));
|
|
RefreshCell(cell);
|
|
cell.col = 2;
|
|
SetCellData(cell, inAddress, sizeof(inAddress)); // this makes a copy of the data, getting the size from strlen
|
|
RefreshCell(cell);
|
|
if ( inEdit )
|
|
StartEditCell( cell );
|
|
}
|
|
|
|
|
|
void CComposeAddressTableView::StartEditCell(const STableCell &inCell)
|
|
{
|
|
UnselectAllCells();
|
|
Assert_( inCell.col == 2 );
|
|
Rect cellRect;
|
|
|
|
// Make sure the edit filed is fully visible
|
|
ScrollCellIntoFrame(inCell);
|
|
// now move edit field over cell
|
|
|
|
if (GetLocalCellRect(inCell, cellRect))
|
|
{
|
|
// put edit field over cell
|
|
mInputField->PutInside(this);
|
|
|
|
Rect textRect = cellRect;
|
|
|
|
textRect.left += kIconWidth + 3;
|
|
textRect.top += kTextDistFromTop; // text is drawn flush top
|
|
|
|
mInputField->ResizeFrameTo(textRect.right - textRect.left,
|
|
textRect.bottom - textRect.top,
|
|
false);
|
|
mInputField->PlaceInSuperImageAt(textRect.left, cellRect.bottom - (textRect.bottom - textRect.top), false);
|
|
mInputField->Init();
|
|
// set text of edit field
|
|
char *addr = NULL;
|
|
Uint32 size = sizeof(addr);
|
|
GetCellData(inCell, &addr, size);
|
|
if (addr)
|
|
{
|
|
LStr255 pstr(addr);
|
|
mInputField->SetDescriptor(pstr);
|
|
mInputField->SelectAll();
|
|
}
|
|
else
|
|
mInputField->SetDescriptor("\p");
|
|
mInputField->Show();
|
|
SwitchTarget(mInputField);
|
|
mEditCell.row = inCell.row;
|
|
mEditCell.col = inCell.col;
|
|
}
|
|
}
|
|
|
|
|
|
void CComposeAddressTableView::EndEditCell()
|
|
{
|
|
if (!mEditCell.IsNullCell())
|
|
{ // Copy text out of LEditField into corresponding cell if
|
|
// user was editing an address
|
|
Int32 numRowsAdded = FinalizeAddrCellEdit();
|
|
HideEditField();
|
|
RefreshCell( mEditCell );
|
|
mEditCell.row += numRowsAdded;
|
|
SelectCell( mEditCell );
|
|
mEditCell.row = 0;
|
|
mEditCell.col = 0;
|
|
}
|
|
}
|
|
|
|
void CComposeAddressTableView::TakeOffDuty()
|
|
{
|
|
// This causes unwanted behaviour -- CommitRow when window is deactivated,
|
|
// and causes deactivated windows to lose keyboard focus when reactivated
|
|
|
|
|
|
// Unfortunately, we have to do it so that rows are committed when the user
|
|
// clicks in the subject or the body without tabbing out of the field first
|
|
EndEditCell();
|
|
UnselectAllCells();
|
|
}
|
|
|
|
// Commands
|
|
void CComposeAddressTableView::DeleteSelection()
|
|
{
|
|
EndEditCell();
|
|
TableIndexT currentRow;
|
|
TableIndexT nextRow;
|
|
nextRow = mTableSelector->GetFirstSelectedRow();
|
|
if( nextRow == 0 )
|
|
return;
|
|
while ( nextRow != 0)
|
|
{
|
|
currentRow = nextRow;
|
|
RemoveRows(1, currentRow, true);
|
|
nextRow = mTableSelector->GetFirstSelectedRow();
|
|
}
|
|
// Set the focus
|
|
STableCell currentCell( currentRow, 2 );
|
|
currentCell.col = 2 ;
|
|
if( currentCell.row != 1 )
|
|
{
|
|
currentCell.row -= 1;
|
|
}
|
|
else if (mRows == 0 )
|
|
{
|
|
InsertNewRow(true, true);
|
|
currentCell.row = 1;
|
|
}
|
|
StartEditCell( currentCell );
|
|
}
|
|
|
|
void CComposeAddressTableView::SetSelectionAddressType( EAddressType inAddressType )
|
|
{
|
|
STableCell currentCell;
|
|
STableCell currentAddressTypeCell(0,1);
|
|
|
|
while ( GetNextSelectedCell( currentCell ))
|
|
{
|
|
currentAddressTypeCell.row = currentCell.row;
|
|
SetRowAddressType( currentAddressTypeCell.row, inAddressType );
|
|
RefreshCell( currentCell );
|
|
RefreshCell( currentAddressTypeCell );
|
|
}
|
|
BroadcastMessage( msg_AddressChanged, NULL );
|
|
} // CComposeAddressTableView::SetSelectionAddressType
|
|
|
|
// utility functions
|
|
void CComposeAddressTableView::GetNumRows(TableIndexT &inRowCount)
|
|
{
|
|
TableIndexT colCount;
|
|
GetTableSize(inRowCount, colCount);
|
|
}
|
|
|
|
EAddressType CComposeAddressTableView::GetRowAddressType( TableIndexT inRow )
|
|
{
|
|
EAddressType addressType;
|
|
Uint32 size = sizeof( addressType );
|
|
STableCell cell( inRow,1 );
|
|
GetCellData( cell, &addressType, size );
|
|
return addressType;
|
|
} // CComposeAddressTableView::GetRowAddressType
|
|
|
|
|
|
void CComposeAddressTableView::SetRowAddressType( TableIndexT inRow, EAddressType inAddressType )
|
|
{
|
|
STableCell cell( inRow,1 );
|
|
SetCellData(cell, &inAddressType, sizeof(EAddressType));
|
|
|
|
} // CComposeAddressTableView::GetRowAddressType
|
|
|
|
|
|
// utility function to put a comma seperated list of
|
|
// addressees of type inAddressType in the table view
|
|
// into an LHandleStream
|
|
void CComposeAddressTableView::CreateCompHeader(EAddressType inAddressType, LHandleStream& inStream)
|
|
{
|
|
TableIndexT rowCount, i = 1;
|
|
EAddressType cellType;
|
|
STableCell cell;
|
|
Uint32 size;
|
|
char* addr = NULL;
|
|
Boolean first = true;
|
|
|
|
GetNumRows(rowCount);
|
|
try {
|
|
do {
|
|
cell.col = 1;
|
|
cell.row = i;
|
|
size = sizeof(cellType);
|
|
GetCellData(cell, &cellType, size);
|
|
if (cellType == inAddressType)
|
|
{
|
|
cell.col = 2;
|
|
size = sizeof(addr);
|
|
GetCellData(cell, &addr, size);
|
|
if (addr)
|
|
{
|
|
if( strlen( addr ) > 0 ) // ignore cells with no data
|
|
{
|
|
if (!first)
|
|
inStream << (unsigned char) ',';
|
|
else
|
|
first = false;
|
|
inStream.WriteBlock(addr, strlen(addr));
|
|
}
|
|
}
|
|
}
|
|
++i;
|
|
} while (i <= rowCount);
|
|
if (inStream.GetMarker() > 0)
|
|
// write null terminator if we have data
|
|
inStream << (unsigned char) '\0';
|
|
} catch (...) {
|
|
// not enough memory to make header!
|
|
}
|
|
}
|
|
|
|
// I hate this. I need to override the default method, because we want
|
|
// to ignore the item in drags from CMailFlexTable and descendents that
|
|
// contains the selection
|
|
Boolean CComposeAddressTableView::DragIsAcceptable(DragReference inDragRef)
|
|
{
|
|
Boolean isAcceptable = true;
|
|
Boolean gotOneOrMore = false;
|
|
|
|
Uint16 itemCount;
|
|
::CountDragItems(inDragRef, &itemCount);
|
|
|
|
for (Uint16 item = 1; item <= itemCount; item++)
|
|
{
|
|
ItemReference itemRef;
|
|
::GetDragItemReferenceNumber(inDragRef, item, &itemRef);
|
|
|
|
// ignore flex table selection data
|
|
if ( itemRef == CMailFlexTable::eMailNewsSelectionDragItemRefNum )
|
|
{
|
|
FlavorFlags flavorFlags;
|
|
|
|
if (::GetFlavorFlags( inDragRef, itemRef, kMailNewsSelectionDragFlavor, &flavorFlags) == noErr )
|
|
continue;
|
|
}
|
|
|
|
isAcceptable = ItemIsAcceptable(inDragRef, itemRef);
|
|
if (!isAcceptable) {
|
|
break; // Stop looping upon finding an
|
|
} // unaccepatable item
|
|
|
|
gotOneOrMore = true;
|
|
}
|
|
|
|
return isAcceptable && gotOneOrMore;
|
|
}
|
|
|
|
/*
|
|
A drag flavor being promoted by Apple for text drags containing mailto
|
|
information. Will contain a comma-separated list of email addresses.
|
|
One characteristic of this flavor that we don't yet support is that
|
|
quotes within the real name part will be escaped, e.g.
|
|
|
|
"Joe \"MacHead\" Bloggs"
|
|
|
|
Note that the TEXT flavor for this drag contains a tab-separated list.
|
|
*/
|
|
#define kAppleMailAddressDragFlavor 'a822'
|
|
|
|
// Drag and Drop Support
|
|
Boolean CComposeAddressTableView::ItemIsAcceptable( DragReference inDragRef,
|
|
ItemReference inItemRef )
|
|
{
|
|
FlavorFlags flavorFlags;
|
|
Boolean acceptable = false;
|
|
|
|
/* This is no longer used
|
|
if (::GetFlavorFlags( inDragRef, inItemRef, kMailAddresseeFlavor, &flavorFlags) == noErr)
|
|
acceptable = true ;
|
|
*/
|
|
|
|
if (::GetFlavorFlags( inDragRef, inItemRef, kAppleMailAddressDragFlavor, &flavorFlags) == noErr)
|
|
acceptable = true ;
|
|
|
|
if (::GetFlavorFlags( inDragRef, inItemRef, 'TEXT', &flavorFlags) == noErr)
|
|
acceptable |= true;
|
|
|
|
return acceptable;
|
|
}
|
|
|
|
void CComposeAddressTableView::ReceiveDragItem( DragReference inDragRef,
|
|
DragAttributes /*flags*/,
|
|
ItemReference inItemRef,
|
|
Rect& itemBounds)
|
|
{
|
|
|
|
/*
|
|
// Check that correct flavor is present, check to see if the entry is not in the list yet
|
|
if ( ::GetFlavorFlags(inDragRef, inItemRef, kMailAddresseeFlavor, &flavorFlags) == noErr )
|
|
{
|
|
// Get the ABID of the item being dragged, either person or list
|
|
dataSize = sizeof( SAddressDragInfo );
|
|
if ( ::GetFlavorData(inDragRef, inItemRef, kMailAddresseeFlavor, &info, &dataSize, 0) == noErr )
|
|
{
|
|
Assert_(dataSize == sizeof( SAddressDragInfo ));
|
|
ABook* pABook;
|
|
// CMailComposeWindow* window = dynamic_cast<CMailComposeWindow*>(LWindow::FetchWindowObject(GetMacPort()));
|
|
pABook = FE_GetAddressBook( NULL );
|
|
pABook->GetFullAddress( info.dir, info.id, &fullName );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*/
|
|
|
|
OSType dragFlavor = 0;
|
|
FlavorFlags flavorFlags = 0;
|
|
OSErr err = noErr;
|
|
|
|
if ( ::GetFlavorFlags (inDragRef, inItemRef, emBookmarkDrag, &flavorFlags) == noErr )
|
|
dragFlavor = emBookmarkDrag;
|
|
else if ( ::GetFlavorFlags (inDragRef, inItemRef, kAppleMailAddressDragFlavor, &flavorFlags) == noErr )
|
|
dragFlavor = kAppleMailAddressDragFlavor;
|
|
else if ( ::GetFlavorFlags (inDragRef, inItemRef, 'TEXT', &flavorFlags) == noErr )
|
|
dragFlavor = 'TEXT';
|
|
|
|
if ( dragFlavor == 0)
|
|
return;
|
|
|
|
Size dataSize = 0;
|
|
char *buffer = NULL;
|
|
|
|
err = ::GetFlavorDataSize (inDragRef, inItemRef, dragFlavor, &dataSize);
|
|
ThrowIfOSErr_ (err); // caught by PP handler
|
|
|
|
if (dataSize == 0 || dataSize > 4096)
|
|
return;
|
|
|
|
DragAttributes theDragAttributes;
|
|
err = ::GetDragAttributes(inDragRef, &theDragAttributes);
|
|
ThrowIfOSErr_ (err);
|
|
|
|
buffer = (char *)XP_ALLOC(dataSize + 1);
|
|
ThrowIfNil_(buffer);
|
|
|
|
err = ::GetFlavorData (inDragRef, inItemRef, dragFlavor, buffer, &dataSize, 0);
|
|
if (err != noErr) {
|
|
XP_FREE(buffer);
|
|
ThrowIfOSErr_(err);
|
|
}
|
|
|
|
buffer[dataSize] = '\0'; // Terminate the string
|
|
|
|
// we need to munge the text to take out white space etc.
|
|
// URLs originating in Communicator are like: <URL part>/r<text part>, and we need to distinguish
|
|
// that from plain text containing /r from outside.
|
|
|
|
char *title = NULL;
|
|
|
|
if ( (theDragAttributes & kDragInsideSenderApplication) == 0) // if an external drag
|
|
{
|
|
// it would be nice to user the CPasteActionSnooper here to clean stuff up, but we don't
|
|
// have a TEHandle to work with.
|
|
|
|
char *lastChar = &buffer[dataSize - 1];
|
|
while (lastChar > buffer && (*lastChar == '\r' || *lastChar == '\n' || *lastChar == '\t' || *lastChar == ' ') )
|
|
{
|
|
lastChar --;
|
|
dataSize --;
|
|
}
|
|
buffer[dataSize] = '\0'; // Re-terminate the string
|
|
|
|
//convert \r or \t to comma so subsequent items get separated
|
|
lastChar = buffer;
|
|
while (*lastChar) {
|
|
if (*lastChar == '\r' || *lastChar == '\t')
|
|
*lastChar = ',';
|
|
lastChar ++;
|
|
}
|
|
}
|
|
else {
|
|
// internal drag
|
|
title = strchr(buffer, '\r'); // may return NULL
|
|
}
|
|
|
|
char *addressText = NULL;
|
|
|
|
if ( ( XP_STRNCASECMP( buffer, "news:", 5) == 0 ) ||
|
|
( XP_STRNCASECMP( buffer, "snews:", 6) == 0 ) ||
|
|
( XP_STRNCASECMP( buffer, "mailto:", 7) == 0 ) )
|
|
{
|
|
if (title)
|
|
*title = '\0'; // use the URL part of the text
|
|
|
|
addressText = XP_STRDUP(buffer);
|
|
}
|
|
else
|
|
{
|
|
if (title == NULL)
|
|
title = buffer;
|
|
else
|
|
title ++; // skip the /r and use the text part
|
|
|
|
addressText = XP_STRDUP( title );
|
|
}
|
|
|
|
XP_FREE(buffer);
|
|
buffer = NULL;
|
|
|
|
if ( addressText != NULL )
|
|
{
|
|
// Find Cell which intersects itemBounds
|
|
STableCell topLeftCell, bottomRightCell;
|
|
FetchIntersectingCells( itemBounds, topLeftCell, bottomRightCell);
|
|
|
|
TableIndexT numRows;
|
|
GetNumRows( numRows );
|
|
|
|
// Dropped in a cell which doesn't exist
|
|
if ( topLeftCell.row > numRows )
|
|
{
|
|
InsertNewRow(false, false);
|
|
STableCell cell(0, 1);
|
|
|
|
GetNumRows(cell.row);
|
|
|
|
CommitRow( addressText, cell );
|
|
}
|
|
else
|
|
{
|
|
if( topLeftCell.row == mEditCell.row)
|
|
EndEditCell();
|
|
|
|
topLeftCell.col = 2;
|
|
char *addr = NULL;
|
|
Int32 size = sizeof(addr);
|
|
GetCellData(topLeftCell, &addr, size);
|
|
Int32 stringSize = strlen( addr) + strlen(addressText) + 2;
|
|
char *newCellStr = new char[ stringSize ];
|
|
|
|
if( XP_STRCMP(addr,"\0") )
|
|
{
|
|
XP_STRCPY( newCellStr, addr );
|
|
XP_STRCAT( newCellStr,"," );
|
|
XP_STRCAT( newCellStr, addressText );
|
|
}
|
|
else
|
|
{
|
|
XP_STRCPY( newCellStr, addressText );
|
|
}
|
|
//SetRowAddressType(topLeftCell.row, addressType);
|
|
CommitRow( newCellStr, topLeftCell );
|
|
delete newCellStr;
|
|
}
|
|
|
|
XP_FREE( addressText );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void CComposeAddressTableView::AddDropAreaToWindow(LWindow* inWindow)
|
|
{
|
|
if (!mCurrentlyAddedToDropList)
|
|
LDropArea::AddDropArea(this, inWindow->GetMacPort());
|
|
mCurrentlyAddedToDropList = true;
|
|
}
|
|
|
|
void CComposeAddressTableView::RemoveDropAreaFromWindow(LWindow* inWindow)
|
|
{
|
|
if (mCurrentlyAddedToDropList)
|
|
LDropArea::RemoveDropArea(this, inWindow->GetMacPort());
|
|
mCurrentlyAddedToDropList = false;
|
|
}
|
|
|
|
void CComposeAddressTableView::InsideDropArea(DragReference inDragRef)
|
|
{
|
|
|
|
Point mouseLoc;
|
|
SPoint32 imagePt;
|
|
TableIndexT newDropRow;
|
|
Boolean newIsDropBetweenFolders;
|
|
|
|
FocusDraw();
|
|
|
|
::GetDragMouse(inDragRef, &mouseLoc, NULL);
|
|
::GlobalToLocal(&mouseLoc);
|
|
LocalToImagePoint(mouseLoc, imagePt);
|
|
|
|
newDropRow = mTableGeometry->GetRowHitBy(imagePt);
|
|
|
|
imagePt.v += 3;
|
|
newIsDropBetweenFolders = false;
|
|
// (newDropRow != mTableGeometry->GetRowHitBy(imagePt));
|
|
|
|
|
|
if (newDropRow != mDropRow || newIsDropBetweenFolders != mIsDropBetweenFolders)
|
|
{
|
|
TableIndexT nRows, nCols;
|
|
|
|
HiliteRow(mDropRow, mIsDropBetweenFolders);
|
|
mIsDropBetweenFolders = newIsDropBetweenFolders;
|
|
|
|
GetTableSize(nRows, nCols);
|
|
if (newDropRow > 0 && newDropRow <= nRows)
|
|
{
|
|
HiliteRow(newDropRow, mIsDropBetweenFolders);
|
|
mDropRow = newDropRow;
|
|
}
|
|
else {
|
|
mDropRow =0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CComposeAddressTableView::LeaveDropArea(DragReference inDragRef)
|
|
{
|
|
|
|
FocusDraw();
|
|
if (mDropRow != 0) {
|
|
HiliteRow( mDropRow, false );
|
|
}
|
|
mDropRow = 0;
|
|
LDragAndDrop::LeaveDropArea( inDragRef);
|
|
}
|
|
|
|
void CComposeAddressTableView::SetTextTraits( ResIDT textTraits )
|
|
{
|
|
mTextTraits = textTraits;
|
|
mInputField->SetTextTraitsID(textTraits);
|
|
Refresh();
|
|
}
|
|
|
|
|
|
#pragma mark -
|
|
|
|
CComposeAddressTableStorage::CComposeAddressTableStorage(LTableView* inTableView) :
|
|
LTableStorage(inTableView)
|
|
{
|
|
try {
|
|
mAddrTypeArray = new LArray(sizeof(EAddressType));
|
|
} catch (...) {
|
|
mAddrTypeArray = NULL;
|
|
}
|
|
try {
|
|
mAddrStrArray = new LArray(sizeof(char*));
|
|
} catch (...) {
|
|
mAddrStrArray = NULL;
|
|
}
|
|
}
|
|
|
|
CComposeAddressTableStorage::~CComposeAddressTableStorage()
|
|
{
|
|
delete mAddrTypeArray;
|
|
// iterate over mAddrStrArray, deleting address strings
|
|
LArrayIterator iter(*mAddrStrArray, LArrayIterator::from_End);
|
|
char* str;
|
|
while (iter.Previous(&str))
|
|
delete [] str;
|
|
// now we can delete address string array
|
|
delete mAddrStrArray;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ SetCellData
|
|
// ---------------------------------------------------------------------------
|
|
// Store data for a particular Cell
|
|
|
|
void
|
|
CComposeAddressTableStorage::SetCellData(
|
|
const STableCell &inCell,
|
|
const void *inDataPtr,
|
|
Uint32 inDataSize)
|
|
{
|
|
if ( inCell.col == 1 )
|
|
mAddrTypeArray->AssignItemsAt(1, inCell.row, inDataPtr, inDataSize);
|
|
else
|
|
{ // copy address string and store string in mAddrStrArray
|
|
const char* inStr = (const char*)inDataPtr;
|
|
size_t strLength = strlen(inStr);
|
|
char* str = new char[strLength + 1];
|
|
::BlockMoveData(inStr, str, strLength);
|
|
str[strLength] = '\0';
|
|
char* current;
|
|
// if there's already a string in mAddrStrArray, delete it
|
|
if (mAddrStrArray->FetchItemAt(inCell.row, ¤t))
|
|
delete [] current;
|
|
mAddrStrArray->AssignItemsAt(1, inCell.row, &str, sizeof(str));
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ GetCellData
|
|
// ---------------------------------------------------------------------------
|
|
// Retrieve data for a particular Cell
|
|
//
|
|
// If outDataPtr is nil, pass back the size of the Cell data
|
|
//
|
|
// If outDataPtr is not nil, it must point to a buffer of at least
|
|
// ioDataSize bytes. On output, ioDataSize is set to the minimum
|
|
// of the Cell data size and the input value of ioDataSize and that
|
|
// many bytes are copied to outDataPtr.
|
|
|
|
void
|
|
CComposeAddressTableStorage::GetCellData(
|
|
const STableCell &inCell,
|
|
void *outDataPtr,
|
|
Uint32 &ioDataSize) const
|
|
{
|
|
LArray* array = (inCell.col == 1) ? mAddrTypeArray : mAddrStrArray;
|
|
if (outDataPtr == nil)
|
|
{
|
|
ioDataSize = array->GetItemSize(inCell.row);
|
|
}
|
|
else
|
|
{
|
|
array->FetchItemAt(inCell.row, outDataPtr, ioDataSize);
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ FindCellData
|
|
// ---------------------------------------------------------------------------
|
|
// Pass back the Cell containing the specified data. Returns whether
|
|
// or not such a Cell was found.
|
|
// For a CComposeAddressTableView, I assume the address string column is the only
|
|
// interesting data to find
|
|
|
|
Boolean
|
|
CComposeAddressTableStorage::FindCellData(
|
|
STableCell &outCell,
|
|
const void *inDataPtr,
|
|
Uint32 inDataSize) const
|
|
{
|
|
Boolean found = false;
|
|
|
|
Int32 dataIndex = mAddrStrArray->FetchIndexOf(inDataPtr, inDataSize);
|
|
|
|
if (dataIndex != LArray::index_Bad) {
|
|
outCell.col = 2;
|
|
outCell.row = dataIndex;
|
|
found = true;
|
|
}
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ InsertRows
|
|
// ---------------------------------------------------------------------------
|
|
// Insert rows into an ArrayStorage.
|
|
//
|
|
// inDataPtr points to the data for the new cells. Each new cell will
|
|
// have the same data.
|
|
|
|
void
|
|
CComposeAddressTableStorage::InsertRows(
|
|
Uint32 inHowMany,
|
|
TableIndexT inAfterRow,
|
|
const void* /*inDataPtr*/,
|
|
Uint32 /*inDataSize*/)
|
|
{
|
|
EAddressType type = eToType;
|
|
mAddrTypeArray->InsertItemsAt(inHowMany, inAfterRow + 1, &type, sizeof(type));
|
|
char *nothing = NULL;
|
|
mAddrStrArray->InsertItemsAt(inHowMany, inAfterRow + 1, ¬hing, sizeof(nothing));
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ RemoveRows
|
|
// ---------------------------------------------------------------------------
|
|
// Removes rows from an ArrayStorage
|
|
|
|
void
|
|
CComposeAddressTableStorage::RemoveRows(
|
|
Uint32 inHowMany,
|
|
TableIndexT inFromRow)
|
|
{
|
|
mAddrTypeArray->RemoveItemsAt(inHowMany, inFromRow);
|
|
// deallocate strings in mAddrStrArray
|
|
char *string = NULL;
|
|
Uint32 itemSize = sizeof(string);
|
|
for (TableIndexT i = 0; i < inHowMany; i++)
|
|
{
|
|
mAddrStrArray->FetchItemAt(inFromRow + i, &string, itemSize);
|
|
if (string)
|
|
delete [] string;
|
|
mAddrStrArray->RemoveItemsAt(inHowMany, inFromRow);
|
|
}
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ¥ GetStorageSize
|
|
// ---------------------------------------------------------------------------
|
|
// Pass back the number of rows and columns represented by the data
|
|
// in an ArrayStorage
|
|
|
|
void
|
|
CComposeAddressTableStorage::GetStorageSize(
|
|
TableIndexT &outRows,
|
|
TableIndexT &outCols)
|
|
{
|
|
// An Array is one-dimensional. By default, we assume a
|
|
// single column with each element being a sepate row.
|
|
|
|
outRows = mAddrStrArray->GetCount();
|
|
outCols = 2;
|
|
if (outRows == 0) {
|
|
outCols = 0;
|
|
}
|
|
}
|