eudora-mac/nickwin.c

1 line
229 KiB
C
Executable File
Raw Permalink Blame History

/* Copyright (c) 2017, Computer History Museum
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted (subject to
the limitations in the disclaimer below) provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the distribution.
* Neither the name of Computer History Museum nor the names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE
COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE. */
#include "nickwin.h"
#include "listview.h"
#define FILE_NUM 3
/* Copyright (c) 1990-1992 by the University of Illinois Board of Trustees */
/* Copyright (c) 1992 by Qualcomm, Inc. */
/**********************************************************************
* handling the alias panel
**********************************************************************/
// Some basic geometry
#define kVertDividerWidth 2
#define kHorzZoomWidth 16
#define kApparentTabTitleSlop 12
#define kMinListWidth (180)
#define kAquaHeightAdjustment 7 // slight height adjustment for Aqua
#define kMinNickWinHeight 340+2*kAquaHeightAdjustment
#define kTabWidthSlop 32 // Extra pixels to accomodate a min width for State and Zip fields
// Tag delimeters
#define kFieldTagBegin '<'
#define kFieldTagEnd '>'
#define kFieldTagSeparator ':'
#define kDragDataDelim '\r'
// First few items in the nickname list
enum {
kItemPersonalNicknames = 1, // The nickname for the user of this machine
kItemEudoraNicknamesAB, // The Eudora Nicknames address book
kItemNicknames // ev'r thang else
};
// Icon constants for list view
enum {
kAddressBookIcon = kGenericFolderIconResource,
kNicknameItem = MAILBOX_ONLY_ICON,
kGroupItem = MAILBOX_ONLY_ICON
};
// Control constants
typedef enum {
abViewByPopup = 0,
abNewNicknameButton,
abNewAddressBookButton,
abRemoveButton,
abToButton,
abCcButton,
abBccButton,
abChatButton,
abZoomHorizontalButton,
abRecipientCheckbox,
abNickField,
abTabs,
abDoNotSyncCheckbox,
abControlCount
} ABControlType;
#pragma segment NickWin
// Address book windows structure
typedef struct {
MyWindowPtr win;
ViewList list;
ControlHandle controls[abControlCount];
Handle oldRecipientMenu;
Rect vertDivider;
Rect listRect;
Rect tabRect;
Rect nickFieldRect;
Str255 sortTag;
NickFocusType nickFocus;
long listFlags;
short minTabWidth;
short minListWidth;
short collapsed;
short currentTab;
short abDisplayed;
short nickDisplayed;
Boolean tickleMe;
Boolean inited;
} ABType, *ABPtr, **ABHandle;
ABType AB;
short gSortAB; // Address book being sorted - used by our sort routines
Boolean gPageOpened;
// Some convenient shorthand
#define Win AB.win
#define NickList AB.list
#define Controls AB.controls
#define CtlViewBy AB.controls[abViewByPopup]
#define CtlNewNickname AB.controls[abNewNicknameButton]
#define CtlNewAddressBook AB.controls[abNewAddressBookButton]
#define CtlRemove AB.controls[abRemoveButton]
#define CtlTo AB.controls[abToButton]
#define CtlCc AB.controls[abCcButton]
#define CtlBcc AB.controls[abBccButton]
#define CtlChat AB.controls[abChatButton]
#define CtlZoomHorizontal AB.controls[abZoomHorizontalButton]
#define CtlRecipient AB.controls[abRecipientCheckbox]
#define CtlDoNotSync AB.controls[abDoNotSyncCheckbox]
#define OldRecipientMenu AB.oldRecipientMenu
#define NickField AB.controls[abNickField]
#define Tabs AB.controls[abTabs]
#define VertDivider AB.vertDivider
#define ListRect AB.listRect
#define TabRect AB.tabRect
#define NickFieldRect AB.nickFieldRect
#define SortTag AB.sortTag
#define NickFocus AB.nickFocus
#define ListFlags AB.listFlags
#define MinTabWidth AB.minTabWidth
#define MinListWidth AB.minListWidth
#define CurrentTab AB.currentTab
#define Collapsed AB.collapsed
#define TickleMe AB.tickleMe
typedef struct
{
PETEHandle pte;
PETEParaInfo pi1;
PETEParaInfo pi2;
PETEParaInfo pi3;
short ab;
short nick;
TextAddrHandle addresses;
Str31 name;
Boolean foundOne;
ControlHandle tabPane;
} NickPrintStuff, *NickPrintStuffPtr;
typedef struct {
Accumulator a;
short refNum;
OSErr theError;
short ab;
short nick;
Str255 eol;
Str255 comma;
Str255 commaReplace;
Str255 crReplace;
} NickExportRec, *NickExportPtr, **NickExportHandle;
Boolean ABHasSelection (MyWindowPtr win);
Boolean ABIsChattable(void);
OSErr ABChat(short modifiers);
Boolean ABPrintPaneProc (ControlHandle tabControl, ControlHandle tabPane, short tabIndex, NickPrintStuffPtr myStuffP);
Boolean ABPrintObjectProc (TabObjectPtr objectPtr, NickPrintStuffPtr myStuffP);
OSErr PrintAddressBookWindow(Boolean select,Boolean now);
OSErr PrintNickFile (short which, Boolean select, PETEHandle pte);
void PrintNickname (NickPrintStuffPtr myStuffP, short ab, short nick);
OSErr NickPage(short *pageNum,PStr title,Rect *uRect,short *v);
void ABPrintMakeTitle(PStr title, Boolean select);
void BuildNickPrintTitle(short which,Boolean select,PStr title);
OSErr NickPrintName(PETEHandle pte, PETEParaInfoPtr pinfop, PStr name);
OSErr NickPrintNameAndValue(PETEHandle pte, PETEParaInfoPtr pinfop, PStr name, PStr value);
OSErr PrintANick(NickPrintStuffPtr myStuffP);
OSErr NickPrintAddresses(PETEHandle pte, PETEParaInfoPtr pinfop, PStr name, UHandle value);
Boolean TheWorldAccordingToMarthaStewart(MyWindowPtr win);
void DoAddressBookExport (Boolean select);
OSErr ExportNickFile (NickExportPtr nickExport, short ab, Boolean select);
OSErr ExportNickname (NickExportPtr nickExport, short ab, short nick);
Boolean ExportNicknameProc (TabObjectPtr objectPtr, NickExportPtr nickExport);
pascal OSErr NickAddrSetDragContents(PETEHandle pte,DragReference drag);
pascal OSErr NickGetDragContents(PETEHandle pte,UHandle *theText, PETEStyleListHandle *theStyles, PETEParaScrapHandle *theParas, DragReference drag, long dropLocation);
// Stuff to eventually go away
/* Information about user defined fields */
typedef struct
{
long height; // How many lines high?
long width; // 1 = normal width, 2 = half width
Str255 name; // Text to be displayed next to field
Str255 tag; // Tag in the notes field
Rect theRect; // Current field rectangle
} fieldInfo;
void GetABNick (VLNodeID nodeID, short *ab, short *nick)
{
*ab = GetAddressBook (nodeID);
*nick = GetNickname (nodeID);
}
Boolean GetIndexedABNick (short index, short *ab, short *nick)
{
VLNodeInfo data;
if (LVGetItem (&NickList, index, &data, false)) {
GetABNick (data.nodeID, ab, nick);
return (true);
}
*ab = kAddressBookUndefined;
*nick = kNickUndefined;
return (false);
}
Boolean GetSelectedABNick (short index, short *ab, short *nick)
{
VLNodeInfo data;
if (LVGetItem (&NickList, index, &data, true)) {
GetABNick (data.nodeID, ab, nick);
return (true);
}
*ab = kAddressBookUndefined;
*nick = kNickUndefined;
return (false);
}
PStr GetSelectedName (short index, PStr name)
{
VLNodeInfo data;
*name = 0;
if (LVGetItem (&NickList, index, &data, true))
PCopy (name, data.name);
return (name);
}
PStr GetSelectedABNickName (short index, short *ab, short *nick, PStr name)
{
VLNodeInfo data;
*name = 0;
if (LVGetItem (&NickList, index, &data, true)) {
PCopy (name, data.name);
GetABNick (data.nodeID, ab, nick);
}
return (name);
}
PStr GetSelectedABNickNameData (short index, short *ab, short *nick, PStr name, VLNodeInfo *data)
{
*name = 0;
if (LVGetItem (&NickList, index, data, true)) {
PCopy (name, data->name);
GetABNick (data->nodeID, ab, nick);
}
return (name);
}
void OpenABWin (PStr findStr)
{
PETEDocInitInfo pdi;
PETEHandle pte;
TabObjectPtr objectPtr;
WindowPtr WinWP;
ControlHandle tabPane;
Str255 title,
tag;
Rect r;
OSErr theError;
short tabIndex;
WinWP = nil;
theError = noErr;
if (SelectOpenWazoo (ALIAS_WIN))
{
// Do an initial find, if need be
if (findStr)
if (!FindNextNicknameContainingString(Win,findStr)) SysBeep(20L);
return; // Already opened in a wazoo
}
// make sure the alias list is in memory
if (RegenerateAllAliases ((MainEvent.modifiers & optionKey) != 0))
goto fail;
// Clean up the recipient menu if we've been asked
if (MainEvent.modifiers & optionKey)
PruneRecipMenu ();
if (AB.inited)
UserSelectWindow (GetMyWindowWindowPtr (Win));
else {
if (!(Win = GetNewMyWindow (ALIAS_WIND,nil,nil,BehindModal,False,False,ALIAS_WIN))) {
theError = MemError ();
goto fail;
}
// This needs to be initialized early so our close routine gets called if anything else fails
Win->close = ABClose;
// Initialize our private data
AB.abDisplayed = kAddressBookUndefined;
AB.nickDisplayed = kNickUndefined;
AB.collapsed = GetPrefLong (PREF_NICK_COLLAPSED);
WinWP = GetMyWindowWindowPtr (Win);
// If anybody is wazooed with us, do not allow collapsing
if (IsWazoo(WinWP) && !IsLonelyWazoo(WinWP)) AB.collapsed = false;
SetPort_ (GetMyWindowCGrafPtr (Win));
ConfigFontSetup (Win);
MySetThemeWindowBackground (Win, kThemeListViewBackgroundBrush, False);
// controls
if (!(CtlViewBy = CreateMenuControl(Win, nil, GetRString (title, VIEW_BY_LABEL), NICK_VIEW_BY_MENU, kControlPopupUseWFontVariant + kControlPopupVariableWidthVariant, 0, true)) || // View by menu
!(CtlNewNickname = NewIconButton (AB_NEW_NICKNAME_CNTL, WinWP)) || // New Nickname button
!(CtlNewAddressBook = NewIconButton (AB_NEW_ADDRESSBOOK_CNTL, WinWP)) || // New Address Book button
!(CtlRemove = NewIconButton (AB_REMOVE_CNTL, WinWP)) || // Remove button
!(CtlTo = CreateControl (Win, nil, HEADER_LABEL_STRN + TO_HEAD, kControlPushButtonProc, true)) || // To button
!(CtlCc = CreateControl (Win, nil, HEADER_LABEL_STRN + CC_HEAD, kControlPushButtonProc, true)) || // Cc button
!(CtlBcc = CreateControl (Win, nil, HEADER_LABEL_STRN + BCC_HEAD, kControlPushButtonProc, true)) || // Bcc button
!(CtlChat = CreateControl (Win, nil, ICHAT_BTN_TEXT, kControlPushButtonProc, true)) || // Chat button
!(CtlRecipient = CreateControl (Win, nil, ALIAS_ON_RECIPIENT_LIST, kControlCheckBoxProc, true)) || // Recipient checkbox
!(CtlDoNotSync = CreateControl (Win, nil, ALIAS_DO_NOT_SYNC, kControlCheckBoxProc, true)) || // Do not sync checkbox
!(CtlZoomHorizontal = GetNewControlSmall (NICK_HZOOM_CNTL, WinWP))) { // Horizontal zoom button
theError = MemError ();
goto fail;
}
SetControlMaximum (CtlRecipient, 2);
SetControlMaximum (CtlDoNotSync, 2);
OutlineControl (CtlTo, true);
// (jp) For setting up our PETE's.
DefaultPII (Win, false, peNoStyledPaste | peClearAllReturns, &pdi);
// Label Fields
NickField = CreateLabelField (Win, nil, GetRString (title, ALIAS_A_LABEL), 0, teForceLeft, labelAutoSize, &pdi, peNoStyledPaste | peClearAllReturns);
CleanPII(&pdi);
// Tabs (we don't know the min tab width at this point)
SetRect (&r, 0, 0, 800, kMinNickWinHeight);
if (Tabs = CreateTabControl (Win, &r, CREATOR, kNickTabType, false)) {
tabIndex = 1;
if (tabPane = FindTabPaneWithTitle (Tabs, GetPref (title, PREF_LAST_NICK_TAB)))
tabIndex = GetTabPaneIndex (tabPane) + 1;
SetControlValue (Tabs, tabIndex);
CurrentTab = SwitchTabs (Tabs, 0, tabIndex, PREF_LAST_NICK_TAB);
SetControlVisibility(Tabs,true,false);
}
// View By menu
BuildViewByMenu (Tabs, CtlViewBy);
// Nickname list
ListFlags = kfListSupportsFocus | kfSupportsSelectCallbacks | kfSupportsMondoBigList | kfManualRowAddsExpected;
SetRect (&r, -20, -20, 0, 0);
if (LVNew (&NickList, Win, &r, kAdddressBookRootID, AddressBookLVCallBack, kLVDoOwnDragAdd)) {
theError = MemError();
goto fail;
}
// The alias expansion field has special editing and drag capabilities, as does "other email" (dragging only for now)
if (objectPtr = FindObjectWithTag (Tabs, GetRString (tag, ABReservedTagsStrn + abTagEmail))) {
if (pte = GetLabelFieldPete (objectPtr->control)) {
SetNickScanning (pte, nickHighlight | nickComplete | nickSpaces, kNickScanAllAliasFiles, nil, nil, nil);
PETESetCallback (PETE, pte, (void *) NickAddrSetDragContents, peSetDragContents);
PETESetCallback (PETE, pte, (void *) NickGetDragContents, peGetDragContents);
}
UnlockObject (objectPtr);
}
if (objectPtr = FindObjectWithTag (Tabs, GetRString (tag, ABReservedTagsStrn + abTagOtherEmail))) {
if (pte = GetLabelFieldPete (objectPtr->control)) {
PETESetCallback (PETE, pte, (void *) NickAddrSetDragContents, peSetDragContents);
PETESetCallback (PETE, pte, (void *) NickGetDragContents, peGetDragContents);
}
UnlockObject (objectPtr);
}
// Set the windows minimum sizes once everything has been created
MinListWidth = 2 * (2 * INSET) + kMinListWidth;
MinTabWidth = 2 * (2 * INSET) + 8 + kTabWidthSlop;
IterateTabPanes (Tabs, CalcMinTabWidthProc, &MinTabWidth);
if (Collapsed)
SetWinMinSize (Win, MinListWidth, kMinNickWinHeight);
else
SetWinMinSize (Win, MinListWidth + MinTabWidth, kMinNickWinHeight);
// SetWinMinSize (Win, MinListWidth + MinTabWidth, kMinNickWinHeight);
// Address book functions
Win->didResize = ABDidResize;
Win->update = ABUpdate;
Win->position = PositionPrefsTitle;
Win->click = ABClick;
Win->bgClick = ABClick;
Win->cursor = ABCursor;
Win->activate = ABActivate;
Win->help = ABHelp;
Win->menu = ABMenu;
Win->key = ABKey;
Win->app1 = ABKey;
Win->drag = ABDragHandler;
Win->idle = ABIdle;
Win->find = ABFind;
Win->dirty = ABDirty;
Win->selection = ABHasSelection;
Win->menuEnable = ABMenuEnable;
// Other initialization
Win->dontControl = true;
Win->userSave = true;
// Save a copy of the recipient menu in case we make changes, then decide to discard
if (OldRecipientMenu = GetResource('STR#',RECIPIENT_STRN))
MyHandToHand (&OldRecipientMenu);
AB.inited = true;
// clean all the dirty stuff so we're ready for user input
ABClean ();
// If we open in the collapsed state, hide the things on the right side
if (Collapsed) {
MoveLabelField (NickField, CNTL_OUT_OF_VIEW, CNTL_OUT_OF_VIEW, ControlWi (NickField), ControlHi (NickField));
MoveMyCntl (CtlRecipient, CNTL_OUT_OF_VIEW, CNTL_OUT_OF_VIEW, ControlWi (CtlRecipient), ControlHi (CtlRecipient));
MoveMyCntl (CtlDoNotSync, CNTL_OUT_OF_VIEW, CNTL_OUT_OF_VIEW, ControlWi (CtlDoNotSync), ControlHi (CtlDoNotSync));
MoveTab (Tabs, CNTL_OUT_OF_VIEW, CNTL_OUT_OF_VIEW, ControlWi (Tabs), ControlHi (Tabs));
}
NickFocus = focusNoChange;
ABSetKeyboardFocus (focusNickList);
// Do an initial find, if need be
if (findStr)
if (!FindNextNicknameContainingString(Win,findStr)) SysBeep(20L);
// Finally, show and size the window
ShowMyWindow (WinWP);
MyWindowDidResize (Win,&Win->contR);
return;
}
fail:
if (WinWP)
CloseMyWindow (WinWP);
if (theError)
WarnUser (COULDNT_WIN, theError);
}
Boolean CalcMinTabWidthProc (ControlHandle tabControl, ControlHandle tabPane, short tabIndex, short *width)
{
MyWindowPtr win;
ControlTabInfoRec tabInfo;
Size actualSize;
win = GetWindowMyWindowPtr(GetControlOwner(tabControl));
tabInfo.version = 0;
if (!GetControlData (tabControl, tabIndex, kControlTabInfoTag, sizeof (ControlTabInfoRec), &tabInfo, &actualSize))
MinTabWidth += (2 * kApparentTabTitleSlop + win->hPitch * tabInfo.name[0]);
return (false);
}
//
// ABClean
//
// Make sure all the fields and UI dirty bits are ready for user input
//
void ABClean (void)
{
if (AB.inited) {
PETEMarkDocDirty (PETE, GetLabelFieldPete (NickField), false);
CleanTab (Tabs);
Win->isDirty = false;
}
}
/************************************************************************
* ABDidResize - resize the Address Book window
************************************************************************/
void ABDidResize (MyWindowPtr win, Rect *oldContR)
{
CGrafPtr winPort = GetMyWindowCGrafPtr (win);
Rect rClipZero,
r;
short htAdjustment,
buttonHeight,
hTab1, // left side of list
hTab2, // right side of list
hTab3, // left side of vertical drag bar
hTab4, // left side of content tab
hTab5, // right side of content tab
vTab1, // top of view by menu
vTab2, // top of list
vTab3, // bottom of list
vTab4, // top of bevel buttons
vTab5, // top of recipient buttons
vTab6, // bottom of content tab
vTab7, // top of the content tab
winWidth,
listWidth,
nickPrct;
// clip to nothing so we don't get controls leaving behind residues when they move
SetPort (winPort);
SetRect (&rClipZero, 0, 0, 0, 0);
ClipRect (&rClipZero);
// Figure out some basic geometry for the window
buttonHeight = ControlHi (CtlCc);
winWidth = RectWi (Win->contR);
nickPrct = GetRLong (ALIAS_NICK_LIST_PRCT);
if (Collapsed) {
listWidth = winWidth - 4 * INSET;
hTab1 = 2 * INSET;
hTab2 = hTab1 + listWidth;
hTab3 = Win->contR.right;
hTab4 = hTab3;
hTab5 = hTab3;
}
else {
if (winWidth < win->minSize.h)
winWidth = win->minSize.h;
listWidth = winWidth * nickPrct / 100;
if (listWidth < MinListWidth)
listWidth = MinListWidth;
if (winWidth - listWidth < MinTabWidth)
listWidth = winWidth - MinTabWidth;
hTab1 = 2 * INSET;
hTab2 = hTab1 + listWidth;
hTab3 = hTab2 + 2 * INSET;
hTab4 = hTab3 + 2 * INSET;
hTab5 = Win->contR.right - 2 * INSET;
}
vTab1 = Win->contR.top + INSET * 2;
vTab2 = vTab1 + buttonHeight + INSET;
vTab5 = Win->contR.bottom - 2 * INSET - buttonHeight;
vTab3 = vTab5 - 2 * INSET - kHtCtl;
vTab7 = vTab2;
vTab6 = Win->contR.bottom - ControlHi (CtlDoNotSync) - 2 * INSET;
// menu
ButtonFit (CtlViewBy);
MoveMyCntl (CtlViewBy, hTab1, vTab1, MIN (ControlWi (CtlViewBy), hTab2 - hTab1), 0);
// list (we do this before the buttons because the list size may change
SetRect (&r, hTab1, vTab2, hTab2, vTab3);
LVSize (&NickList, &r, &htAdjustment);
ListRect = NickList.bounds;
vTab3 += htAdjustment;
vTab4 = vTab3 + (vTab5 - vTab3 - kHtCtl) / 2;
// buttons
PositionBevelButtons (win, 3, &CtlNewNickname, hTab1, vTab4, kHtCtl, hTab3);
PositionPushButtons (win, hTab1, hTab2, vTab5);
// No need to reposition these when the window is horizontally collapsed
if (!Collapsed) {
// the vertical divider
SetRect (&VertDivider, hTab3, vTab1, hTab3 + kVertDividerWidth, Win->contR.bottom - vTab1);
// recipient checkbox
MoveMyCntl (CtlRecipient, hTab5 - ControlWi (CtlRecipient), vTab1, 0, ONE_LINE_HI(win) );
// do not sync checkbox
MoveMyCntl (CtlDoNotSync, hTab4, vTab6 + INSET, 0, ControlHi (CtlDoNotSync));
// the nickname field
MoveLabelField (NickField, hTab4, vTab1, hTab5 - ControlWi (CtlRecipient) - 2 * INSET - hTab4, ONE_LINE_HI (win));
GetControlBounds (NickField, &NickFieldRect);
// content pane
MoveTab (Tabs, hTab4, vTab7, hTab5 - hTab4, vTab6 - vTab7 - kAquaHeightAdjustment);
GetControlBounds (Tabs, &TabRect);
}
// Need to do this here to catch wazoo promotions
SetGreyControl (CtlZoomHorizontal, (IsWazoo (GetMyWindowWindowPtr (Win)) && !IsLonelyWazoo (GetMyWindowWindowPtr (Win))) || !win->isActive);
// restore clip region
InfiniteClip (winPort);
// redraw
InvalContent (win);
}
/************************************************************************
* ABClose - close the window
************************************************************************/
Boolean ABClose (MyWindowPtr win)
{
NickStructHandle theData;
NickStructPtr pData;
short ab,
nick,
numABs,
numNicks;
Boolean discardChanges;
discardChanges = false;
if (AB.inited) {
// Check to see if there's stuff we need to save
if (IsDirtyWindow (win)) {
// Engage in conversation with the user...
switch (WannaSave (win)) {
case WANNA_SAVE_CANCEL:
case CANCEL_ITEM:
return (false);;
break;
case WANNA_SAVE_DISCARD:
discardChanges = true;
break;
case WANNA_SAVE_SAVE:
if (!ABSave ())
return (false);
break;
}
}
// Old crap, modified some... Play with the deletion and dirty flags
numABs = NAliases;
for (ab = 0; ab < numABs; ab++) {
if (theData = (*Aliases)[ab].theData) {
numNicks = GetHandleSize_ (theData) / sizeof (NickStruct);
for (nick = 0, pData = *theData; nick < numNicks; nick++, pData++)
if (pData->addressesDirty) {
pData->addressesDirty = false;
pData->notesDirty = false;
pData->pornography = false;
if (pData->deleted && discardChanges)
pData->deleted = false;
}
}
// Get rid of the sort data for each address book
ZapHandle ((*Aliases)[ab].sortData);
}
// Need to restore the recipient list if we're discarding changes
VanquishRecipientList (discardChanges);
// Save the list of expanded address books
SaveExpandedAddressBookNames ();
// Get rid of in-memory nickname info if we're discarding changes
if (discardChanges) {
ZapAliases ();
RegenerateAllAliases (false);
}
// Dispose of list
LVDispose (&NickList);
// Dispose of any label fields
DisposeLabelField (NickField);
// Vanquish the tab control to memory hell
DisposeTabControl (Tabs, false);
AB.win = nil;
AB.inited = false;
}
return (true);
}
/************************************************************************
* ABUpdate - draw the window
************************************************************************/
void ABUpdate (MyWindowPtr win)
{
CGrafPtr winPort = GetMyWindowCGrafPtr (win);
// list
DrawThemeListBoxFrame (&NickList.bounds, kThemeStateActive);
LVDraw (&NickList,MyGetPortVisibleRegion(winPort), false, false);
// vertical divider
if (!Collapsed)
DrawDivider (&VertDivider, true);
}
/************************************************************************
* ABActivate - activate the window
************************************************************************/
void ABActivate (MyWindowPtr win)
{
short ab,
nick;
// save the info on the tab whenever the address book goes inactive (which forces an update to the
// current nickname if, for instance, we switch to a comp window and expect to do auto expand).
if (!win->isActive)
if (GetSelectedABNick (1, &ab, &nick))
ReplaceNicknameData (Tabs, ab, nick);
LVActivate (&NickList, win->isActive);
TabActivate (Tabs, win->isActive);
ABSetControls (win->isActive);
}
/**********************************************************************
* ABFind - find in the Address Book window
*
* <09> First, check to see if the text appears in the current edit field
* <09> If so, select it and we're done
* <09> If not, search the rest of the fields for this nickname starting
* with the next tab-able edit field.
* <09> When the text is found, select it. If it is not found....
* <20> Step through the nickname list, testing each nickname to
* see if the search text appears in either the nickname, addresses
* or notes.
* <09> If a match is made, select that nickname in the list, then search
* for the text as above.
**********************************************************************/
Boolean ABFind (MyWindowPtr win, PStr what)
{
WindowPtr winWP = GetMyWindowWindowPtr (win);
PETEHandle pte;
PETEDocInfo info;
NickFoundType found;
long offset;
found = false;
UserSelectWindow (winWP);
SetPort (GetWindowPort (winWP));
if (!win->isActive)
ActivateMyWindow (winWP, true);
// We're done if the text is found in the current PETE, so look for it!
if (pte = win->pte) {
PETEGetDocInfo (PETE, pte, &info);
if ((offset = PeteFindString (what, info.selStop, pte)) >= 0) {
PeteSelect (win, pte, offset, offset + *what);
PeteScroll (pte, pseCenterSelection, pseCenterSelection);
found = true;
}
}
// darn! The text is not in the current field. Give the other fields associated with the selected nickname a chance
if (!found)
found = FindStringInSelectedNickname (what, false);
// Okay, see if the search string is present in some other nickname
if (!found)
if (FindNextNicknameContainingString (win, what))
found = FindStringInSelectedNickname (what, true);
return (found);
}
//
// FindNextNicknameContainingString
//
// Find (and select) the next nickname containing the search string.
NickFoundType FindNextNicknameContainingString (MyWindowPtr win, PStr what)
{
VLNodeInfo data;
NickFoundType found;
short ab,
nick,
endNick,
abStop,
nickStop,
index;
Boolean wrapped,
done,
caseSens;
// Start searching from the current selection
if (LVGetItem (&NickList, 1, &data, true)) {
// Make sure the current tab information is saved
GetABNick (data.nodeID, &ab, &nick);
ReplaceNicknameData (Tabs, ab, nick);
index = data.rowNum + 1;
}
else
index = 1;
GetIndexedABNick (index, &ab, &nick);
abStop = ab;
nickStop = nick;
endNick = -1; // Means scan to the end of the address book
wrapped = false;
found = nickFoundNothing;
done = false;
caseSens = PrefIsSet(PREF_SENSITIVE);
LSetDrawingMode (false, NickList.hList);
while (!found && !done) {
if (CommandPeriod || EjectBuckaroo)
break;
if (!IsPluginAddressBook (ab))
found = FindStringInAddressBook (ab, nick, &endNick, what, caseSens);
if (!found)
// Are we back where it all started? If so, we be done.
if (wrapped && ab == abStop && (endNick == nickStop || endNick == -1))
done = true;
else {
// Advance to the next address book, maybe wrapping around back to the first one
if (++ab == NAliases) {
ab = 0;
wrapped = true;
}
// Start at the first nickname and end... either with the last in the address book or the last after the wrap
nick = -1;
endNick = ab == abStop ? nickStop : -1;
}
}
LSetDrawingMode (true, NickList.hList);
// If we found something, display it (whoopee!), maybe expanding the address book
if (found) {
if ((*Aliases)[ab].collapsed) {
LVGetItemWithNodeID (&NickList, MakeABNodeID (ab), &data);
LVExpand (&NickList, MakeABNodeID (ab), data.name, true);
}
ABNickLVSelect (MakeNodeID (ab, endNick), false);
}
return (found);
}
//
// FindStringInAddressBook
//
// Fings a search string in an address book, starting from a particular nickname,
// and returning the found nickname in the end index.
//
// When -1 is passed for 'startNick' it means to scan the whole address book.
//
NickFoundType FindStringInAddressBook (short ab, short startNick, short *endNick, PStr what, Boolean caseSens)
{
NickFoundType found;
short *sort,
count;
Boolean foundStartNick;
found = nickFoundNothing;
foundStartNick = startNick == -1;
SortAddressBook (ab);
if ((*Aliases)[ab].sortData && (*Aliases)[ab].theData) {
count = GetHandleSize ((*Aliases)[ab].sortData) / sizeof (short);
sort = LDRef ((*Aliases)[ab].sortData);
while (count-- && !found && *sort != *endNick) {
CycleBalls();
if (CommandPeriod || EjectBuckaroo)
break;
if (*sort == startNick)
foundStartNick = true;
if (foundStartNick && !(*((*Aliases)[ab].theData))[*sort].deleted)
if (found = FindStringInNickName (ab, *sort, what, caseSens))
*endNick = *sort;
++sort;
}
UL ((*Aliases)[ab].sortData);
}
return (found);
}
NickFoundType FindStringInNickName (short ab, short nick, PStr what, Boolean caseSens)
{
AttributeValueHandle avPairs;
AttributeValuePtr avPairPtr;
NickFoundType found;
Str255 fieldValue;
Handle notes,
addresses,
leftovers;
short count,
i;
found = nickFoundNothing;
avPairs = nil;
leftovers = nil;
GetNicknameNamePStr (ab, nick, fieldValue);
// See if the search string is in the name
if (SearchPtrPtr (what + 1, *what, fieldValue + 1, 0, *fieldValue, caseSens, false, nil) >=0)
found = nickFoundNickname;
else // Didn't match on name, continue checking notes
if (notes = GetNicknameData (ab, nick, false, true))
if (SearchPtrHandle (what + 1, *what, notes, 0, caseSens, false, nil) >=0)
if (avPairs = ParseAllAttributeValuePairs (notes, &leftovers, avAllSearchablePairs, avPairUnknown)) {
avPairPtr = LDRef (avPairs);
LDRef (notes);
count = GetHandleSize (avPairs) / sizeof (AttributeValueRec);
for (i = 0; i < count && !found; ++i, ++avPairPtr)
if (SearchPtrPtr (what + 1, *what, *notes + avPairPtr->valueOffset, 0, avPairPtr->valueLength, caseSens, false, nil) >=0)
found = nickFoundNotes; // Found it in an attribute value pair (that is not hidden) -- so we can select this text in the proper tagged field
UL (notes);
}
// Maybe the text is in the leftovers
if (!found && leftovers)
if (SearchPtrHandle (what + 1, *what, leftovers, 0, caseSens, false, nil) >=0)
found = nickFoundNotes;
// See if the search string is present in the addresses
if (!found)
if (addresses = GetNicknameData (ab, nick, true, true))
if (SearchPtrHandle (what + 1, *what, addresses, 0, caseSens, false, nil) >=0)
found = nickFoundAddresses;
ZapHandle (avPairs);
ZapHandle (leftovers);
return (found);
}
//
// FindStringInSelectedNickname
//
// Find the next occurence of the search sting in the selected nickname.
//
Boolean FindStringInSelectedNickname (PStr what, Boolean checkNickField)
{
PETEHandle pte;
long offset;
short ab,
nick,
oldTabIndex;
Boolean found;
found = false;
if (GetSelectedABNick (1, &ab, &nick)) {
if (checkNickField) {
// Remove the current focus
ABClearKeyboardFocus ();
if (pte = GetLabelFieldPete (NickField)) {
if ((offset = PeteFindString (what, 0, pte)) >= 0) {
// Switch to the first tab
oldTabIndex = GetActiveTabIndex (Tabs);
SetControlValue (Tabs, 1);
CurrentTab = SwitchTabs (Tabs, oldTabIndex, GetActiveTabIndex (Tabs), PREF_LAST_NICK_TAB);
// Move the focus to the nickname field
ABSetKeyboardFocus (focusNickField);
// Select the search string in the field
PeteSelect (Win, pte, offset, offset + *what);
PeteScroll (pte, pseCenterSelection, pseCenterSelection);
found = true;
}
}
}
// Look for it in the tab
if (!found)
if (found = TabFindString (Tabs, NickFocus == focusTabControl, what))
NickFocus = focusTabControl;
}
return (found);
}
#if 0
/**********************************************************************
* ABFindInCollapsed - search a collapsed folder
**********************************************************************/
Boolean ABFindInCollapsed (MyWindowPtr win, ViewListPtr pView, PStr what, VLNodeID nodeID)
{
VLNodeInfo data;
CGrafPtr winPort;
Str31 name;
short totalNicks,
nick,
ab;
ab = GetAddressBook (nodeID);
totalNicks = (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct));
for (nick = 0; nick < totalNicks; nick++)
if (FindStrStr (what, GetNicknameNamePStr (ab, nick, name)) >= 0) {
LVGetItemWithNodeID (&NickList, MakeABNodeID (ab), &data);
LVExpand (&NickList, MakeABNodeID (ab), data.name, true);
LVSelect (pView, MakeNodeID (ab, nick), name, false);
winPort = GetMyWindowCGrafPtr (win);
LVDraw(pView,winPort->visRgn,true,false);
return true;
}
return false;
}
#endif
Boolean ABDirty (MyWindowPtr win)
{
return (win->isDirty || AnyNicknamesDirty (kAddressBookUndefined) || (Tabs && IsTabDirty (Tabs)));
}
Boolean ABHasSelection (MyWindowPtr win)
{
UHandle text;
long start,
stop;
if (LVCountSelection (&NickList))
return (true);
if (ValidNickname (AB.nickDisplayed) && TabHasSelection (Tabs))
return (true);
if (win->pte && !PeteGetTextAndSelection (win->pte, &text, &start, &stop))
return (stop!=start);
return (false);
}
void ABMenuEnable (MyWindowPtr win)
{
if (NickFocus == focusTabControl && ValidNickname (AB.nickDisplayed))
TabMenuEnable (Tabs, (*Aliases)[AB.abDisplayed].ro);
}
void ABSetKeyboardFocus (NickFocusType nickFocus)
{
// If the focus is not changing, why bother, ya know?
if (nickFocus == NickFocus)
return;
// First, clear the old focus
if (!ABClearKeyboardFocus ())
return;
// Next, set the new focus
switch (NickFocus = nickFocus) {
case focusNickList:
LVSetKeyboardFocus (&NickList, true);
break;
case focusNickField:
SetKeyboardFocus (GetMyWindowWindowPtr (Win), NickField, kControlEditTextPart);
PeteFocus(Win,GetLabelFieldPete(NickField),true);
break;
case focusTabControl:
TabSetKeyboardFocus (Tabs, focusTabControlDirect, true);
break;
case focusNickTabAdvance:
TabSetKeyboardFocus (Tabs, focusTabControlAdvance, true);
NickFocus = focusTabControl;
break;
case focusNickTabReverse:
TabSetKeyboardFocus (Tabs, focusTabControlReverse, true);
NickFocus = focusTabControl;
break;
}
}
Boolean ABClearKeyboardFocus (void)
{
switch (NickFocus) {
case focusNickList:
LVSetKeyboardFocus (&NickList, false);
break;
case focusNickField:
if (!ABMaybeRenameNickname (Win))
return (false);
SetKeyboardFocus (GetMyWindowWindowPtr (Win), NickField, kControlFocusNoPart);
PeteFocus(Win,GetLabelFieldPete(NickField),false);
break;
case focusTabControl:
TabSetKeyboardFocus (Tabs, focusTabControlDirect, false);
break;
}
NickFocus = focusNoChange;
if (Win->pte) PeteFocus(Win,Win->pte,false);
return (true);
}
void ABAdvanceKeyboardFocus (void)
{
NickFocusType newFocus;
// If the window is collapsed, there's nowhere for the focus to go
if (Collapsed)
return;
newFocus = focusNoChange;
switch (NickFocus) {
case focusNickList:
// Advance the focus within the nickname list, if we don't want the focus anymore, give it to the nickname field
if (ValidNickname (AB.nickDisplayed))
if (LVAdvanceKeyboardFocus (&NickList))
newFocus = focusNickField;
break;
case focusNickField:
// Advance the focus to the Tab control
newFocus = focusNickTabAdvance;
break;
case focusTabControl:
// Advance the focus within the Tab control, if we don't want the focus anymore, give it to the nickname list
if (TabAdvanceKeyboardFocus (Tabs))
newFocus = focusNickList;
break;
}
// If the focus is changing, see if the new item is willing to accept the focus, if not we advance again
if (newFocus != focusNoChange)
ABSetKeyboardFocus (newFocus);
}
void ABReverseKeyboardFocus (void)
{
NickFocusType newFocus;
newFocus = focusNoChange;
switch (NickFocus) {
case focusNickList:
// Reverse the focus within the nickname list, if we don't want the focus anymore, give it to the Tab control
if (ValidNickname (AB.nickDisplayed))
if (LVReverseKeyboardFocus (&NickList))
newFocus = focusNickTabReverse;
break;
case focusNickField:
// Reverse the focus to the nickname list
newFocus = focusNickList;
break;
case focusTabControl:
// Reverse the focus within the Tab control, if we don't want the focus anymore, give it to the nickname field
if (TabReverseKeyboardFocus (Tabs))
newFocus = focusNickField;
break;
}
// If the focus is changing, see if the new item is willing to accept the focus, if not we reverse again
if (newFocus != focusNoChange)
ABSetKeyboardFocus (newFocus);
}
/************************************************************************
* ABKey - key stroke
************************************************************************/
Boolean ABKey (MyWindowPtr win, EventRecord *event)
{
short key = (event->message & 0xff),
newTabIndex;
Boolean result;
result = false;
// Where is the focus?
switch (NickFocus) {
case focusNickList:
result = LVKey (&NickList, event);
break;
case focusTabControl:
result = TabKey (Tabs, event);
break;
}
if (!result || key == tabChar)
switch (key) {
case tabChar:
if (!Collapsed) {
if (event->modifiers & optionKey) {
newTabIndex = GetActiveTabIndex (Tabs) + (event->modifiers & shiftKey ? -1 : 1);
newTabIndex = newTabIndex ? (newTabIndex > GetTabPaneCount (Tabs) ? 1 : newTabIndex) : (GetTabPaneCount (Tabs));
SetControlValue (Tabs, newTabIndex);
// Pretend we clicked on this tab to do all the right switching of fields
ABHit (win, event, abTabs, 0);
}
else {
if (event->modifiers & shiftKey)
ABReverseKeyboardFocus ();
else
ABAdvanceKeyboardFocus ();
if (win->pte)
PeteSelectAll (win, win->pte);
}
result = true;
}
break;
default:
if (!(event->modifiers & cmdKey)) {
if (DirtyKey (key) && ValidAddressBook (AB.abDisplayed) && (*Aliases)[AB.abDisplayed].ro)
AlertStr (READ_ONLY_ALRT, Stop, nil);
else {
event->what = keyDown;
(void) PeteEdit (win, win->pte, peeEvent, (void*) event); // (jp) For nickname completion
}
result = true;
}
}
return (result);
}
/************************************************************************
* ABClick - click in nickname window
************************************************************************/
void ABClick (MyWindowPtr win, EventRecord *event)
{
NickFocusType oldNickFocus;
WindowPtr WinWP = GetMyWindowWindowPtr (Win);
Point pt;
short part,
i;
Rect tabRect;
SetPort (GetMyWindowCGrafPtr (win));
pt = event->where;
GlobalToLocal (&pt);
// First, check to see if the click was in the list while the PETE was not. When this is the
// case we need to remove the focus before handing the click to the list view. We'll also check
// to see if we should rename the current nickname (as the ListView is kinda picky about the
// selection and sort states).
if (PtInRect (pt, &NickList.bounds)) {
if (!ABMaybeRenameNickname (win))
return;
ABSetKeyboardFocus (focusNickList);
}
// Now, let's give the list view a shot at the click
if (!LVClick (&NickList, event)) {
if (!win->isActive) {
SelectWindow_ (WinWP);
UpdateMyWindow (WinWP); // Have to update manually since no events are processed
}
// tab content (since the toolbox is not nice enough to tell about clicks in the content... sheesh)
GetTabContentRect (Tabs, &tabRect);
if (PtInRect (pt, &tabRect) && ValidNickname (AB.nickDisplayed)) {
oldNickFocus = NickFocus;
if (NickFocus != focusTabControl)
if (!ABClearKeyboardFocus ())
return;
if (ClickTab (Tabs, event, pt))
NickFocus = focusTabControl;
else
ABSetKeyboardFocus (oldNickFocus);
}
else {
// Let's look at the controls we know about
for (i = 0; i < abControlCount; i++)
if (part = TestControl (Controls[i], pt)) {
// For now we're going to intercept mouse downs in label fields and pass them down to our hit routine.
// Eventually it would be cool (and maybe easy) to have the label field user pane respond to tracking messages
// We also have to qluify the part test against 'kControlEditTextPart' to not be a hit in a tab control
// because the tab control returns the index og the hit tab in the part... and since we have 5 tabs, clicks
// in the Notes field were being swallowed by the part==kControlEditTextPart test.
if (!ControlIsGrey (Controls[i]))
if ((part == kControlEditTextPart && Controls[i] != Tabs) || TrackControl (Controls[i], pt, (void *)(-1))) {
ABHit (win, event, i, part);
AuditHit ((event->modifiers&shiftKey)!=0, (event->modifiers&controlKey)!=0, (event->modifiers&optionKey)!=0, (event->modifiers&cmdKey)!=0, false, GetWindowKind(WinWP), AUDITCONTROLID(GetWindowKind(WinWP),i), event->what);
break;
}
}
// If we did not hit a control
if (i == abControlCount) {
// The vertical divider, by chance?
if (!Collapsed && PtInSloppyRect (pt, &VertDivider, 1))
NickWDragVDivider (pt);
}
}
}
ABSetControls (win->isActive);
}
/************************************************************************
* ABCursor - set the cursor properly for the address book window
************************************************************************/
void ABCursor (Point mouse)
{
int cursor;
if (!PeteCursorList (Win->pteList, mouse)) {
cursor = arrowCursor;
if (PtInSlopRect (mouse, VertDivider, 1))
cursor = DIVIDER_CURS;
SetMyCursor (cursor);
}
}
/************************************************************************
* ABSetControls - Set the controls based on the state of the selected
* information in the list.
************************************************************************/
void ABSetControls (Boolean isActive)
{
short ab,
nick,
count;
Boolean canAddNickname,
canAddAddressBook,
canRemove,
anyNicksSelected,
singleNickSelected,
greyTab;
// Here's a neato oddity of the toolbox... the active state of a control becomes
// hosed if your activate or deactivate a control while the mouse button is down...
// even though the mouse might not be down IN that particulat control. Who knew?
if (StillDown ())
return;
count = ABDetermineSelectionConditions (&canAddNickname, &canAddAddressBook, &canRemove, &anyNicksSelected, &singleNickSelected);
SetGreyControl (CtlNewNickname, !canAddNickname || !isActive);
SetGreyControl (CtlNewAddressBook, !canAddAddressBook || !isActive);
SetGreyControl (CtlRemove, !canRemove || !isActive);
SetGreyControl (CtlTo, !anyNicksSelected || !isActive);
SetGreyControl (CtlCc, !anyNicksSelected || !isActive);
SetGreyControl (CtlBcc, !anyNicksSelected || !isActive);
SetGreyControl (CtlChat, !singleNickSelected || !isActive || !ABIsChattable());
SetGreyControl (CtlRecipient, !anyNicksSelected || !isActive);
SetGreyControl (CtlZoomHorizontal, !isActive);
if (IsWazoo (GetMyWindowWindowPtr (Win)) && !IsLonelyWazoo (GetMyWindowWindowPtr (Win)))
HideControl(CtlZoomHorizontal);
else
ShowControl(CtlZoomHorizontal);
// nickname field and tabs
if (count == 1)
GetSelectedABNick (1, &ab, &nick);
else {
ab = kAddressBookUndefined;
nick = kNickUndefined;
}
SetGreyControl (CtlDoNotSync, !anyNicksSelected || (ab != kAddressBookUndefined && (*Aliases)[ab].ro) || !isActive );
SetGreyControl (NickField, !singleNickSelected || (ab != kAddressBookUndefined && (*Aliases)[ab].ro) || !isActive);
ABSetRecipientButton ();
ABSetSyncButton ();
greyTab = !singleNickSelected || (ab != kAddressBookUndefined && (*Aliases)[ab].ro) || !isActive;
IterateTabPanes (Tabs, SetGreyTabProc, &greyTab);
Win->hasSelection = anyNicksSelected || MyWinHasSelection (Win);
}
//
// ABDetermineSelectionConditions
//
// Takes a peek at various nickname selection criteria so that we can know which buttons need to be
// deactivated -- and maybe why.
//
// Returns the number of items currently selected.
//
short ABDetermineSelectionConditions (Boolean *canAddNickname, Boolean *canAddAddressBook, Boolean *canRemove, Boolean *anyNicksSelected, Boolean *singleNickSelected)
{
short count,
ab,
nick,
i;
*canAddNickname = true;
*canAddAddressBook = HasFeature (featureMultipleNicknameFiles);
*canRemove = LVCountSelection (&NickList) ? true : false;
*anyNicksSelected = false;
*singleNickSelected = false;
count = LVCountSelection (&NickList);
// Take a trip through all the selections looking for relevant conditions
for (i = 1; i <= count; ++i)
if (GetSelectedABNick (i, &ab, &nick))
if (ab >= 0) {
if ((*Aliases)[ab].ro) {
*canRemove = false;
*canAddNickname = false;
}
if (nick == kNickUndefined) {
#ifdef VCARD
if (IsEudoraAddressBook (ab) || IsHistoryAddressBook (ab) || IsPersonalAddressBook (ab))
#else
if (IsEudoraAddressBook (ab) || IsHistoryAddressBook (ab))
#endif
*canRemove = false;
}
else {
*anyNicksSelected = true;
if (count == 1)
*singleNickSelected = true;
}
}
return (count);
}
Boolean SetGreyTabProc (ControlHandle tabControl, ControlHandle tabPane, short tabIndex, Boolean *shdBeGrey)
{
SetGreyControl (tabPane, *shdBeGrey);
return (false);
}
void ABSelectNickname (VLNodeID nodeID)
{
// On a selection during a drag, set the nick selection to be nothing
if (LVDragSelectInProgress (&NickList))
nodeID = MakeNodeID (kAddressBookUndefined, kNickUndefined);
if (ABDisplayNicknameInTab (GetAddressBook (nodeID), GetNickname (nodeID)))
ABSetControls (Win->isActive);
}
void ABUnselectNickname (VLNodeID nodeID)
{
short ab,
nick,
count;
ab = GetAddressBook (nodeID);
nick = GetNickname (nodeID);
// If we are unselecting the _displayed_ nickname, replace its data
ReplaceNicknameData (Tabs, ab, nick);
// When individual nicknames are unselected, check to see if any are selected at all.
// If not, we'll deactivate controls. (Note: we might want to instead use this:
count = LVCountSelection (&NickList);
if (!count) {
if (ABDisplayNicknameInTab (kAddressBookUndefined, kNickUndefined))
ABSetControls (false);
}
else
// If by unselecting a name we now have exactly one name selected.... display it
if (count == 1) {
GetSelectedABNick (1, &ab, &nick);
if (ABDisplayNicknameInTab (ab, nick))
ABSetControls (true);
}
}
void ABSetRecipientButton (void)
{
short ab,
nick,
count,
value,
i;
Boolean isRecipient;
value = -1;
// We're going to loop through the selected list items to see whether or not we have a
// mix of recipient list settings (so that we can set the control to its "mixed" setting).
count = LVCountSelection (&NickList);
for (i = 1; i <= count && value != kControlCheckBoxMixedValue; ++i) {
GetSelectedABNick (i, &ab, &nick);
if (nick != kNickUndefined) {
isRecipient = IsNicknameOnRecipList (ab, nick);
if (value == -1)
value = isRecipient ? kControlCheckBoxCheckedValue : kControlCheckBoxUncheckedValue;
else
if ((value == kControlCheckBoxCheckedValue && !isRecipient) || (value == kControlCheckBoxUncheckedValue && isRecipient))
value = kControlCheckBoxMixedValue;
}
}
SetControlValue (CtlRecipient, value);
}
void ABSetSyncButton (void)
{
short ab,
nick,
count,
value,
i;
Boolean isPrivate;
value = -1;
// We're going to loop through the selected list items to see whether or not we have a
// mix of recipient list settings (so that we can set the control to its "mixed" setting).
count = LVCountSelection (&NickList);
for (i = 1; i <= count && value != kControlCheckBoxMixedValue; ++i) {
GetSelectedABNick (i, &ab, &nick);
if (nick != kNickUndefined) {
isPrivate = IsNicknamePrivate (ab, nick);
if (value == -1)
value = isPrivate ? kControlCheckBoxCheckedValue : kControlCheckBoxUncheckedValue;
else
if ((value == kControlCheckBoxCheckedValue && !isPrivate) || (value == kControlCheckBoxUncheckedValue && isPrivate))
value = kControlCheckBoxMixedValue;
}
}
SetControlValue (CtlDoNotSync, value);
}
/************************************************************************
* ABHit - a control item was hit
************************************************************************/
void ABHit (MyWindowPtr win, EventRecord *event, short whichControl, short part)
{
// Certain control hits should be preflighted with a possible nickname field save
if (whichControl == abViewByPopup ||
whichControl == abNewNicknameButton ||
whichControl == abNewAddressBookButton ||
whichControl == abToButton ||
whichControl == abCcButton ||
whichControl == abBccButton ||
whichControl == abChatButton ||
whichControl == abRecipientCheckbox)
if (!ABMaybeRenameNickname (win))
return;
switch (whichControl) {
case abViewByPopup:
DoViewByMenu ();
break;
case abNewNicknameButton:
DoNewNickname (event->modifiers);
break;
case abNewAddressBookButton:
DoNewAddressBook ();
break;
case abRemoveButton:
ABRemove ();
break;
case abToButton:
ABMessageTo (TO_HEAD, event->modifiers);
break;
case abCcButton:
ABMessageTo (CC_HEAD, event->modifiers);
break;
case abChatButton:
ABChat (event->modifiers);
break;
case abBccButton:
ABMessageTo (BCC_HEAD, event->modifiers);
break;
case abZoomHorizontalButton:
DoHorizontalZoom (win);
break;
case abRecipientCheckbox:
DoRecipientList (CtlRecipient);
break;
case abDoNotSyncCheckbox:
DoDoNotSync (CtlDoNotSync);
break;
case abNickField:
if (part == kControlEditTextPart) {
ABSetKeyboardFocus (focusNickField);
PeteEdit (win, GetLabelFieldPete (NickField), peeEvent, (void*) event);
}
break;
case abTabs:
// Remove the focus from the current tab...
if (NickFocus == focusTabControl)
TabSetKeyboardFocus (Tabs, focusTabControlDirect, false);
CurrentTab = SwitchTabs (Tabs, CurrentTab, GetActiveTabIndex (Tabs), PREF_LAST_NICK_TAB);
/// ...and put it back onto the first field of the new tab
if (NickFocus == focusTabControl)
TabSetKeyboardFocus (Tabs, focusTabControlDirect, ValidNickname (AB.nickDisplayed));
break;
}
}
void ABMessageTo (short strResID, long modifiers)
{
WindowPtr WinWP = GetMyWindowWindowPtr(Win);
short ab,
nick;
MyWindowPtr insertedWin;
// save the info on the tab whenever a recipient button has been clicked
if (GetSelectedABNick (1, &ab, &nick))
ReplaceNicknameData (Tabs, ab, nick);
insertedWin = InsertTheAlias (strResID, (modifiers & optionKey) != 0);
if ((modifiers & (shiftKey | cmdKey))) {
if (FrontWindow_ () != WinWP)
UserSelectWindow (WinWP);
}
else
if (FrontWindow_() == WinWP) {
UserSelectWindow(GetMyWindowWindowPtr(insertedWin));
UpdateMyWindow (WinWP);
}
}
/************************************************************************
* ABHelp - help for the alias window
************************************************************************/
void ABHelp (MyWindowPtr win,Point mouse)
{
NickHelpType help;
Rect tipRect;
short i,
ab,
nick;
Boolean canAddNickname,
canAddAddressBook,
canRemove,
anyNicksSelected,
singleNickSelected;
Rect cntlRect;
ABDetermineSelectionConditions (&canAddNickname, &canAddAddressBook, &canRemove, &anyNicksSelected, &singleNickSelected);
help = noNickHelp;
if (PtInRect (mouse, &NickList.bounds)) {
tipRect = NickList.bounds;
help = nickHelpList;
}
else
if (PtInSloppyRect (mouse, &VertDivider, 1)) {
tipRect = VertDivider;
help = nickHelpVertDivider;
}
else
for (i = 0; help == noNickHelp && i < abControlCount; i++)
if (PtInRect (mouse, GetControlBounds(Controls[i],&cntlRect))) {
GetControlBounds(Controls[i],&tipRect);
switch (i) {
case abViewByPopup:
help = nickHelpViewByPopup;
break;
case abNewNicknameButton:
help = canAddAddressBook ? nickHelpNewNicknameButton : nickHelpNewNicknameButtonDimmed;
break;
case abNewAddressBookButton:
help = nickHelpNewAddressBookButton;
break;
case abRemoveButton:
if (canRemove)
help = nickHelpRemoveButton;
else {
help = nickHelpRemoveButtonDimmed;
if (GetSelectedABNick (i, &ab, &nick))
if (ab >= 0) {
if ((*Aliases)[ab].ro)
help = nickHelpRemoveButtonDimmedReadOnly;
else
if (nick == kNickUndefined)
if (IsEudoraAddressBook (ab))
help = nickHelpRemoveButtonDimmedEudoraNicknames;
else
if (IsHistoryAddressBook (ab))
help = nickHelpRemoveButtonDimmedHistoryList;
}
}
break;
case abToButton:
help = anyNicksSelected ? (TopCompositionWindow (true, true) ? nickHelpToButtonAddToTopmost : nickHelpToButton) : nickHelpToButtonDimmed;
break;
case abCcButton:
help = anyNicksSelected ? (TopCompositionWindow (true, true) ? nickHelpCcButtonAddToTopmost : nickHelpCcButton) : nickHelpCcButtonDimmed;
break;
case abBccButton:
help = anyNicksSelected ? (TopCompositionWindow (true, true) ? nickHelpBccButtonAddToTopmost : nickHelpBccButton) : nickHelpBccButtonDimmed;
break;
case abZoomHorizontalButton:
help = Collapsed ? nickHelpZoomHorizontalButtonCollapsed : nickHelpZoomHorizontalButtonExpanded;
break;
case abRecipientCheckbox:
help = anyNicksSelected ? nickHelpRecipientCheckbox : nickHelpRecipientCheckboxDimmed;
break;
case abNickField:
help = anyNicksSelected ? (singleNickSelected ? nickHelpNickField : nickHelpNickFieldDimmedMultipleSelection) : nickHelpNickFieldDimmedNothingSelected;
break;
case abTabs:
help = anyNicksSelected ? (singleNickSelected ? nickHelpTabs : nickHelpTabsDimmedMultipleSelection) : nickHelpTabsDimmedNothingSelected;
break;
}
}
if (help)
MyBalloon (&tipRect, 100, 0, NICK_HELP_STRN + help, 0, nil);
}
/************************************************************************
* ABMenu - menu choice in the alias window
************************************************************************/
Boolean ABMenu (MyWindowPtr win, int menu, int item, short modifiers)
{
TabObjectPtr objectPtr;
SendDragDataInfo sendDragData;
Str255 tag,
name;
Handle copyText;
OSErr theError;
short ab,
nick;
ScrapRef scrap;
if (!TabMenu (Tabs, menu, item, modifiers))
switch (menu) {
case FILE_MENU:
switch (item) {
case FILE_SAVE_ITEM:
if (IsDirtyWindow (win))
ABSave ();
return (True);
break;
case FILE_SAVE_AS_ITEM:
DoAddressBookExport ((modifiers&shiftKey)!=0);
break;
case FILE_PRINT_ITEM:
case FILE_PRINT_ONE_ITEM:
PrintAddressBookWindow((modifiers&shiftKey)!=0, item==FILE_PRINT_ONE_ITEM);
break;
}
break;
case EDIT_MENU:
if (win->pte) {
PETEHandle pteEmail = nil,
pteOtherEmail = nil;
DoIndependentMenu (menu, item, modifiers);
win->hasSelection = MyWinHasSelection (win);
if (objectPtr = FindObjectWithTag (Tabs, GetRString (tag, ABReservedTagsStrn + abTagEmail))) {
pteEmail = GetLabelFieldPete (objectPtr->control);
UnlockObject (objectPtr);
}
if (objectPtr = FindObjectWithTag (Tabs, GetRString (tag, ABReservedTagsStrn + abTagOtherEmail))) {
pteOtherEmail = GetLabelFieldPete (objectPtr->control);
UnlockObject (objectPtr);
}
if ((item == EDIT_COPY_ITEM || item == EDIT_CUT_ITEM) && (win->pte == pteEmail || win->pte == pteOtherEmail))
ReformatClip();
return (true);
}
else
switch (item) {
case EDIT_SELECT_ITEM:
if (NickFocus == focusNickList)
LVSelectAll (&NickList);
break;
case EDIT_CLEAR_ITEM:
if (NickFocus == focusNickList)
ABRemove();
break;
case EDIT_COPY_ITEM:
Zero (sendDragData);
sendDragData.flavor = A822_FLAVOR;
theError = BuildDragData (&sendDragData, &copyText);
if (!theError) {
ClearCurrentScrap();
GetCurrentScrap(&scrap);
theError = PutScrapFlavor(scrap,'TEXT',kScrapFlavorMaskNone,GetHandleSize (copyText),LDRef (copyText));
}
ZapHandle (copyText);
break;
}
break;
case SPECIAL_MENU:
switch (AdjustSpecialMenuSelection(item)) {
case SPECIAL_MAKE_NICK_ITEM:
if (modifiers&shiftKey)
MakeNickFromSelection(win);
else
MakeNicknameFromSelectedNicknames ();
break;
}
break;
case WINDOW_MENU:
if (item == WIN_PH_ITEM && (modifiers & shiftKey) && LVCountSelection (&NickList) == 1) {
if (GetSelectedABNick (1, &ab, &nick)) {
GetTaggedFieldValueStr (ab, nick, GetRString (tag, ABReservedTagsStrn + abTagName), name);
if (*name) {
OpenPh (name);
return (true);
}
}
}
break;
}
return (false);
}
Boolean ABSave (void)
{
short ab,
nick;
Boolean bornAgain;
// Does the nickname field need to be saved before we save?
if (!ABMaybeRenameNickname (Win))
return (false);
// Make sure the current tab information is saved
if (GetSelectedABNick (1, &ab, &nick))
ReplaceNicknameData (Tabs, ab, nick);
if (bornAgain = SaveAliases (true))
ABClean ();
return (bornAgain);
}
void ABTickle (void)
{
short abDisplayed,
nickDisplayed;
if (AB.inited) {
abDisplayed = AB.abDisplayed;
nickDisplayed = AB.nickDisplayed;
InvalidListView (&NickList);
// Reselect the previous items
if (!(abDisplayed == kAddressBookUndefined && nickDisplayed == kNickUndefined))
ABNickLVSelect (MakeNodeID (abDisplayed, nickDisplayed), true);
TickleMe = false;
}
}
void ABTickleHardEnoughToMakeYouPuke(void)
{
if (AliasWinIsOpen())
{
short which;
for (which=0;which<NAliases;which++)
{
ZapHandle((*Aliases)[which].sortData);
SortAddressBook(which);
}
ABTickle();
}
}
void MakeNicknameFromSelectedNicknames (void)
{
Handle addresses;
Str32 name; // Note, this is one extra character to accomodate a null at the end (as is done in old nickwin.c)
OSErr theError;
short ab,
nick,
count,
i;
addresses = NuHandle (0);
theError = MemError ();
count = LVCountSelection (&NickList);
for (i = 1; i <= count && !theError; ++i) {
GetSelectedABNick (i, &ab, &nick);
if (nick != kNickUndefined) {
GetNicknameNamePStr (ab, nick, name);
name[name[0]+1] = 0;
theError = PtrPlusHand_ (name, addresses, *name + 2);
}
}
if (!theError)
theError = PtrPlusHand_ ("", addresses, 1);
if (!theError)
#ifdef VCARD
NewNick (addresses, nil, 0);
#else
NewNick (addresses, 0);
#endif
ZapHandle(addresses);
if (theError)
WarnUser (COULDNT_MOD_ALIAS, theError);
}
//
// ABIdle
//
// We need an idle handler for the address book in order for the label field controls
// to get time for nickname highlighting
//
void ABIdle (MyWindowPtr win)
{
TabObjectPtr objectPtr;
PETEHandle pte;
Str255 tag;
if (win)
{
if (TickleMe) ABTickle();
if (objectPtr = FindObjectWithTag (Tabs, GetRString (tag, ABReservedTagsStrn + abTagEmail))) {
pte = GetLabelFieldPete (objectPtr->control);
if (win->pte == pte)
IdleControls (GetMyWindowWindowPtr (win));
UnlockObject (objectPtr);
}
if (NickFocus==focusNickField)
{
Boolean singleNickSelected,ratSass;
ABDetermineSelectionConditions(&ratSass, &ratSass, &ratSass, &ratSass, &singleNickSelected);
if (!singleNickSelected) ABClearKeyboardFocus();
}
}
}
/**********************************************************************
* ABDragHandler - handle drags
*
* Can do internal dragging of nicknames or external dragging and dropping
* of messages
**********************************************************************/
OSErr ABDragHandler (MyWindowPtr win, DragTrackingMessage which, DragReference drag)
{
VLNodeInfo targetInfo;
OSErr theError;
Point initialMouse,
currentMouse;
short ab,
nick;
#ifdef VCARD
short modifiers,
mouseDownModifiers,
mouseUpModifiers;
Boolean isVCardDrag,
isSpecDrag;
#endif
Boolean isListItemDrag,
isDragInList,
is822Drag,
isTextDrag,
isGraphicFileDrag,
isNickDrag,
isHFSDrag,
isTabDrag;
theError = dragNotAcceptedErr;
is822Drag = DragIsInteresting (drag, A822_FLAVOR, nil);
isTextDrag = DragIsInteresting (drag, 'TEXT', nil);
isNickDrag = DragIsInteresting (drag, kNickDragType, nil);
isHFSDrag = DragIsInteresting (drag, flavorTypeHFS, nil);
#ifdef VCARD
isSpecDrag = DragIsInteresting (drag, SPEC_FLAVOR, nil) && IsVCardAvailable ();
#endif
isTabDrag = TabDragIsInteresting (Tabs, drag);
isListItemDrag = false;
// Only check the drag origin if the drag started with this window
if (DragSourceKind == GetWindowKind(GetMyWindowWindowPtr(win)))
if (!GetDragOrigin (drag, &initialMouse)) {
GlobalToLocal (&initialMouse);
isListItemDrag = PtInRect (initialMouse, &NickList.bounds);
}
isDragInList = false;
if (!GetDragMouse (drag, &currentMouse, nil)) {
GlobalToLocal (&currentMouse);
isDragInList = PtInRect (currentMouse, &NickList.bounds);
}
if (isTabDrag = (isTabDrag && LVCountSelection (&NickList) == 1)) {
GetSelectedABNick (1, &ab, &nick);
isTabDrag = ValidNickname (nick);
}
// Calling IsGraphicFile is ssssssssssllllllloooooooowwwwwww.
// We could speed it up with a global.
isGraphicFileDrag = false;
#ifdef VCARD
isVCardDrag = false;
#endif
if (isHFSDrag) {
FSSpec spec;
HFSFlavor **dragData;
dragData = nil;
if (!MyGetDragItemData (drag, 1, flavorTypeHFS, &dragData)) {
spec = (*dragData)->fileSpec;
isGraphicFileDrag = IsGraphicFile (&spec);
#ifdef VCARD
if (IsVCardAvailable ())
isVCardDrag = IsVCardFile (&spec);
#endif
}
ZapHandle (dragData);
}
#ifdef VCARD
if (!is822Drag && !isTextDrag && !isNickDrag && !isHFSDrag && !isListItemDrag && !isTabDrag && !isSpecDrag)
return (dragNotAcceptedErr); // Nothing here we want
#else
if (!is822Drag && !isTextDrag && !isNickDrag && !isHFSDrag && !isListItemDrag && !isTabDrag)
return (dragNotAcceptedErr); // Nothing here we want
#endif
// If we're dragging TEXT that is not also a drag that originated in the list itself, check
// to see if there is exactly one nickname selected in the list. If so, the addres book can
// accept the drag and we return 'dragNotAcceptedErr' (yes, that's right... NOT accepted) so
// that PETE handles the drag. If there is no list selection, or there are multiple selections
// then the nickname fields are disabled and we return 'noErr' to make PETE think we were smart
// enough to handle the drag ourselves.
if (isTextDrag && !isListItemDrag && !isDragInList) {
if (LVCountSelection (&NickList) == 1) {
GetSelectedABNick (1, &ab, &nick);
if (ValidNickname (nick))
return (dragNotAcceptedErr);
}
return (noErr);
}
#ifdef VCARD
// If we are dragging a SPEC_FLAVOR that originated with ourselves, we're dragging a personal nickname
// as a vCard. We'll allow this ONLY if a copy is taking place.
GetDragModifiers (drag, &modifiers, &mouseDownModifiers, &mouseUpModifiers);
if (isSpecDrag && isListItemDrag && !((modifiers|mouseDownModifiers|mouseUpModifiers)&optionKey))
return (dragNotAcceptedErr);
#endif
switch (which) {
case kDragTrackingEnterWindow:
case kDragTrackingLeaveWindow:
case kDragTrackingInWindow:
if (isNickDrag || (isHFSDrag && !isGraphicFileDrag) || (isListItemDrag && !isTextDrag))
theError = LVDrag (&NickList, which, drag);
else
if (isTabDrag)
theError = TabDrag (Tabs, which, drag);
break;
case 0xfff:
// Plop
if (isTabDrag)
theError = TabDrag (Tabs, which, drag);
else
if (isListItemDrag)
theError = LVDrag (&NickList, which, drag);
else
if (LVDrop (&NickList, &targetInfo)) {
#ifdef VCARD
if (isHFSDrag && IsVCardAvailable ())
theError = ABFileDrop (drag, &targetInfo);
else
#endif
theError = ABNickDrop (drag, &targetInfo);
}
else
theError = noErr; // Dropped, just not very interesting
break;
}
return (theError);
}
OSErr ABAddDragFlavors (long data)
{
SendDragDataInfo *pSendData;
OSErr theError;
#ifdef VCARD
PromiseHFSFlavor promise;
short count,
index,
ab,
nick;
Boolean anyNonVCards;
#endif
theError = noErr;
pSendData = (SendDragDataInfo *) data;
#ifdef VCARD
// We need to see if the selected items exclusively consist of those in the
// Personal Nicknames address book. If so, we will promise an HFS in the
// drag and eventually deliver a vCard. If not, we treat the Personal Nickname
// like any other nickname. We do this because we DON'T want to deliver
// regular old nicknames as vCards (because these aren't OUR vCards).
if (IsVCardAvailable ()) {
anyNonVCards = false;
count = LVCountSelection (&NickList);
for (index = 1; index <= count && !anyNonVCards; ++index)
if (GetSelectedABNick (index, &ab, &nick))
if (!IsPersonalAddressBook (ab) || !ValidNickname (nick))
anyNonVCards = true;
if (!anyNonVCards) {
Zero (promise);
promise.fileType = VCARD_TYPE;
promise.fileCreator = CREATOR;
promise.promisedFlavor = SPEC_FLAVOR;
theError = AddDragItemFlavor (pSendData->drag, 1L, flavorTypePromiseHFS, &promise, sizeof(promise), flavorNotSaved);
if (!theError)
theError = AddDragItemFlavor (pSendData->drag, 1L, SPEC_FLAVOR, nil, 0L, flavorNotSaved);
UseFeature (featureVCard);
}
}
#endif
if (!theError)
theError = AddDragItemFlavor (pSendData->drag, 1L, 'TEXT', nil, 0L, 0);
if (!theError)
theError = AddDragItemFlavor (pSendData->drag, 1L, kNickDragType, nil, 0L, 0);
if (!theError)
theError = AddDragItemFlavor (pSendData->drag, 1L, A822_FLAVOR, nil, 0L, 0);
return (theError);
}
//
// ABDoSendDragData
//
// Build a data handle from the selected nicknames. Based somewhat on the old NickDragToData
// but using the new list view schtuff.
//
OSErr ABDoSendDragData (long data)
{
SendDragDataInfo *pSendData;
Handle dragData;
OSErr theError;
Size dataSize;
short ab,
nick;
// Save the current nickname before committing its data to a drag
if (GetSelectedABNick (1, &ab, &nick))
ReplaceNicknameData (Tabs, ab, nick);
pSendData = (SendDragDataInfo *) data;
dragData = nil;
theError = BuildDragData (pSendData, &dragData);
if (!theError)
if (dataSize = GetHandleSize (dragData)) {
theError = SetDragItemFlavorData (pSendData->drag, pSendData->itemRef, pSendData->flavor, LDRef (dragData), dataSize, 0L);
UL (dragData);
}
else
theError = dragNotAcceptedErr;
ZapHandle (dragData);
return (theError);
}
void ABMove (VLNodeInfo *pToInfo, Boolean copy)
{
VLNodeID **movedList,
*pItem;
short selectCount,
ab,
nick,
lastMovedAB,
i;
Boolean anyChanges; // well?
// Save the current nickname before committing its data to a move
if (GetSelectedABNick (1, &ab, &nick))
ReplaceNicknameData (Tabs, ab, nick);
anyChanges = false;
selectCount = LVCountSelection (&NickList);
lastMovedAB = kAddressBookUndefined;
movedList = (VLNodeID **) NuHandle (0);
for (i = 1; i <= selectCount; i++) {
if (GetSelectedABNick (i, &ab, &nick)) {
// Is it just a nickname, or is it an entire address book?
if (lastMovedAB != ab && ValidNickname (nick)) {
if (DropNicknameOntoAddressBook (movedList, pToInfo, ab, nick, copy))
anyChanges = true;
}
else
if (DropAddressBook (movedList, pToInfo, ab, copy)) {
lastMovedAB = ab;
anyChanges = true;
}
}
}
// Build a new list -- and mark any of the moved or copied items as selected
if (anyChanges) {
InvalidListView (&NickList);
selectCount = GetHandleSize (movedList) / sizeof (VLNodeID);
for (i = 0, pItem = *movedList; i < selectCount; ++i, ++pItem)
ABNickLVSelect (*pItem, true);
}
}
//
// ABNickDrop
//
// Handle drops in the nickname list
//
OSErr ABNickDrop (DragReference drag, VLNodeInfo *targetInfo)
{
BinAddrHandle mungedAddresses;
Str31 name;
OSErr theError;
Handle dragData,
addresses,
notes;
long offset,
dragDataSize;
short count,
item,
newNick,
ab,
nick;
theError = noErr;
count = MyCountDragItems (drag);
for (item = 1; !theError && item <= count; ++item) {
dragData = nil;
theError = MyGetDragItemData (drag, item, kNickDragType, &dragData);
if (!theError) {
offset = 0;
dragDataSize = GetHandleSize (dragData);
while (offset < dragDataSize && !theError) {
mungedAddresses = nil;
addresses = nil;
notes = nil;
theError = GetNickFlavorDragData (dragData, &offset, &ab, &nick, name, &addresses, &notes);
if (!theError)
MaybeApplySplittingAlgorithm (notes);
if (!theError)
theError = SuckAddresses (&mungedAddresses, addresses, true, true, false, nil);
if (!theError)
if (!ValidNickname (newNick = AddNickname (mungedAddresses, notes, GetAddressBook (targetInfo->nodeID), name, false, nrDifferent, false)))
theError = userCanceledErr;
ZapHandle (mungedAddresses);
ZapHandle (addresses);
ZapHandle (notes);
}
}
ZapHandle (dragData);
}
InvalidListView (&NickList);
return (theError);
}
#ifdef VCARD
void DecodeQuotedPrintable (Handle value);
OSErr ABFileDrop (DragReference drag, VLNodeInfo *targetInfo)
{
NickFromVCardItemRec nickFromVCard;
BinAddrHandle addresses;
FSSpec spec;
HFSFlavor **dragData;
Handle vcardData;
Str255 nickname,
fullName,
firstName,
lastName,
tag;
OSErr theError = noErr;
short count,
item,
ab,
nick;
Boolean gotOne; // we got at least one nickname out of the thing
long offset; // offset to data in the vcard that's been left unparsed
UseFeature (featureVCard);
ab = GetAddressBook (targetInfo->nodeID);
Zero (nickFromVCard);
nickFromVCard.addresses = NuHandle (0);
nickFromVCard.notes = NuHandle (0);
theError = MemError ();
count = MyCountDragItems (drag);
for (item = 1; !theError && item <= count; ++item) {
vcardData = nil;
theError = MyGetDragItemData (drag, item, flavorTypeHFS, &dragData);
if (!theError) {
spec = (*dragData)->fileSpec;
theError = SnarfRoman (&spec, &vcardData, 0);
offset = 0;
}
do
{
if (!theError) {
SetHandleBig (nickFromVCard.addresses, 0);
SetHandleBig (nickFromVCard.notes, 0);
nickFromVCard.numErrors = 0;
theError = ParseVCard (vcardData, &offset, nil, VCardToNicknameItemProc, VCardToNicknameErrorProc, (long) &nickFromVCard);
if (!theError && nickFromVCard.error)
theError = dragNotAcceptedErr;
}
if (!offset) ZapHandle (vcardData);
ZapHandle (dragData);
// We should now have a nickname described as the addresses and notes.
// We'll prep this data for its emergence as an official EUDORA NICKNAME!!
// The addresses we've parsed are just raw text. Convert them into the BinAddr format we like.
addresses = nil;
if (!theError)
theError = SuckAddresses (&addresses, nickFromVCard.addresses, true, true, false, nil);
// Apply our name splitting algorithm and clean up the notes
if (!theError) {
MaybeApplySplittingAlgorithm (nickFromVCard.notes);
Tr (nickFromVCard.notes, "\015", "\003");
// Derive the nickname by first looking joining the first and last names
*nickname = 0;
GetTaggedFieldValueStrInNotes (nickFromVCard.notes, GetRString (tag, ABReservedTagsStrn + abTagFirst), firstName);
GetTaggedFieldValueStrInNotes (nickFromVCard.notes, GetRString (tag, ABReservedTagsStrn + abTagLast), lastName);
if (*firstName && *lastName)
JoinFirstLast (nickname, firstName, lastName);
// If that didn't work, try the full name field
if (!*nickname)
GetTaggedFieldValueStrInNotes (nickFromVCard.notes, GetRString (tag, ABReservedTagsStrn + abTagName), nickname);
// Last gasp... make a nickname out of the address
if (!*nickname) {
if (addresses && *addresses && **addresses)
NickSuggest (addresses, nickname, fullName,true,0);
}
else {
BeautifyFrom (nickname);
SanitizeFN (nickname, nickname, NICK_BAD_CHAR, NICK_REP_CHAR, false);
*nickname = MIN (*nickname, sizeof (Str31) - 1);
}
// Put the nickname into the address book (and cross our fingers)
if (*nickname) {
if (!ValidNickname (nick = AddNickname (addresses, nickFromVCard.notes, ab, nickname, false, nrDifferent, false)))
theError = userCanceledErr;
else
gotOne = true;
}
}
ZapHandle (addresses);
} while (!theError && offset);
}
if (gotOne) {
InvalidListView (&NickList);
if (count == 1) {
if ((*Aliases)[ab].collapsed) {
VLNodeInfo data;
LVGetItemWithNodeID (&NickList, MakeABNodeID (ab), &data);
LVExpand (&NickList, MakeABNodeID (ab), data.name, true);
}
ABNickLVSelect (MakeNodeID (ab, nick), false);
LVDraw (&NickList, nil, false, false);
}
}
ZapHandle (nickFromVCard.addresses);
ZapHandle (nickFromVCard.notes);
return theError;
}
#ifdef NEVER
//
// NicknameFromVCardFile
//
// Create the nickname from a vCard file, returning handles containing
// the addresses and notes of a nickname.
//
OSErr NicknameFromVCardFile (FSSpec *spec, Handle *addresses, Handle *notes)
{
NickFromVCardItemRec nickFromVCard;
Handle vcardData;
OSErr theError;
theError = noErr;
Zero (nickFromVCard);
if (spec) {
vcardData = nil;
// Allocate a pair of empty handles for the notes and addresses
nickFromVCard.addresses = NuHandle (0);
nickFromVCard.notes = NuHandle (0);
theError = MemError ();
// Suck in all of the vCard data
if (!theError)
theError = Snarf (spec, &vcardData, 0);
// Parse the vCard data using the VCardToNicknameItemProc to to the vCard-to-nickname transation
if (!theError)
theError = ParseVCard (vcardData, nil, VCardToNicknameItemProc, VCardToNicknameErrorProc, (long) &nickFromVCard);
// We no longer need the vCard data
ZapHandle (vcardData);
}
// If any errors occurred, zap the storage we allocated
if (theError) {
ZapHandle (nickFromVCard.addresses);
ZapHandle (nickFromVCard.notes);
}
// Pass back the results
*addresses = nickFromVCard.addresses;
*notes = nickFromVCard.notes;
return (theError);
}
#endif
//
// VCardToNicknameItemProc
//
// vCard item proc that builds a nickname from a vCard (wow!)
//
// We actually don't care about much of the stuff included in a vCard (though, we someday might).
// This function can grow to some greater level of detail, but for now we make a simple address
// book entry.
//
// Still, vCards can return very frustrating results to us. Take, for instance, the following pair
// of items:
//
// TEL;HOME;WORK;VOICE;FAX:(858) 658-4063
// TEL;VOICE;FAX;HOME;WORK:(858) 658-4063
//
// In both cases we have a fax and voice number used for home and work. So in our Eudora nickname
// format we need to represent each of these lines as four unique attribute/value pairs (spread
// across a pair of tabs in the Address Book). Because the order of the parameters is very
// different in each example, we can't simply take each parameter one-by-one and easily determine
// the appropriate attribute to be assigned.
//
// Instead, with each parsed item we'll build a list of home/work tag indices from which we'll
// build each necessary attribute/value pair. In the above example, this list will look like so:
//
// abTagPhone abTagPhone2
// abTagFax abTagFax2
//
// When we process this list we'll process BOTH the 'home' and 'work' tags for each. Had only one
// of these tags (HOME or WORK) been present, we'd only process the appropriate index.
//
// vCard is capable of all kinds of screwy combinations -- some, as the spec admits, impossible to
// interpret (mostly, these are encoding oddities). Because there is no formal schema, there's no
// way to absolutely capture the spirit of a given vCard in our nickname format. Take a look at
// the following:
//
// FN;HOME;FAX:John Purlia
//
// Huh? My "home" full name is a fax machine named "John Purlia"?? Or, this simpler combo:
//
// FN;HOME:John Purlia
// FN;WORK;PREF:The Mad Scientist of VCard
// FN;WORK;PREF:Super Toy Man, Master of all things mechanical
//
// So at work and at home I have different names. In fact, at work I have TWO names and both of
// then are apparently the preferred way for people to communicate my name. This seems unreasonable
// (not to mention incredibly pompous), but it's allowed by the vCard specification. So, since we
// don't have a concept of multiple full names, we have to choose one. While we _could_ apply some
// kind of logic to this problem and say, well, we'll use the first occurrence, or maybe we'll keep
// those we don't use in notes as "AKA" fields, or maybe we'll see which (if any) is the preferred
// name. Uh, no, sorry. There are too many fields and too many combinations. Instead, we'll use
// the first one, ignoring all others. Tough. This also goes for non name fields that repeat in
// a vCard. For example:
//
// TEL;WORK:(858) 658-4063
// TEL;WORK:(619) 890-6158
//
// Ooooo... big man... you have two phones at work. Well, fine. We'll only assign the first as the
// "Work Phone" in Eudora -- the second will end up in "Other Phone Numbers", and only because this
// is a field that has a reasonable other place to put the data. If that's not the case, for example:
//
// TITLE:Director, Engineering
// TITLE:Mac Programmer Guy
//
// For now we'll just ignore the second title (though I suppose it could be dumped into notes).
//
// Fields that have no reasonable nickname equivalent (GEO, TZ, etc) will for now be ignored. At some
// future time we might dump this information into notes, but for now that will onyl server to clutter
// the notes field.
//
OSErr VCardToNicknameItemProc (VCardItemPtr itemPtr, NickFromVCardItemRecPtr nickFromVCardItemPtr)
{
VCardParamPtr paramPtr;
OSErr theError;
Size valSize,
addrSize;
short numParams,
i;
Boolean home,
work,
ignored,
tel,
preferred;
// Some initialization
theError = noErr;
ignored = false;
tel = false;
numParams = GetHandleSize (itemPtr->params) / sizeof (VCardParamRec);
home = itemPtr->propertyFlags & vcPropFlagHome ? true : false;
work = itemPtr->propertyFlags & vcPropFlagWork ? true : false;
preferred = itemPtr->propertyFlags & vcPropFlagPreferred ? true : false;
if (!home && !work)
if (PrefIsSet (PREF_HOME_IS_NICER_THAN_WORK))
home = true;
else
work = true;
// Any special decoding for the value?
switch (itemPtr->encoding) {
case vcValue7Bit:
case vcValue8Bit:
break;
case vcValueQuotedPrintable:
DecodeQuotedPrintable (itemPtr->value);
break;
case vcValueBase64:
break;
default:
break;
}
valSize = GetHandleSize (itemPtr->value);
if (!valSize)
return (noErr);
LDRef (itemPtr->value);
// Properties we care about
switch (itemPtr->property) {
case vcKeyFN:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagName, 0, true, false, *itemPtr->value, valSize, nickFieldIgnoreExisting, 0, nil);
break;
case vcKeyTitle:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagTitle, 0, true, false, *itemPtr->value, valSize, nickFieldIgnoreExisting, 0, nil);
break;
case vcKeyTel:
// Telephone numbers are special (or, at the very least, difficult). A parameter indicating the specific type
// of phone might be present, so we handle those cases below. Or, a parameter might not be present -- so we have
// to just note that we're working on a TEL property and handle it later if there are no specific phone properties.
tel = true;
break;
case vcKeyEmail:
// The preferred email address should go into the address expansion field -- everything else gets dumped into "other email".
// If no preferred address is specified, we assume that the first one received is the preferred address.
// Couple of things to note... When we find "the" preferred email address (at least, we hope only one is preferred) we take
// anything previously assumed to be the preferred email address and dump it into the "other email" field, placing the new
// data into the address expansion. If we find a second preferred email address, we repeat the swap, so it's possible that
// duplicate addresses could end up in "other email", but this is a vCard problem (which is the lazy way of saying that we're
// not going to save state just for the sake of badly formed vCard).
if (preferred) {
if (addrSize = GetHandleSize (nickFromVCardItemPtr->addresses)) {
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagOtherEmail, 0, true, false, LDRef (nickFromVCardItemPtr->addresses), addrSize, nickFieldAppendExisting, NICK_CONCAT_NEWLINE, nil);
UL (nickFromVCardItemPtr->addresses);
SetHandleBig (nickFromVCardItemPtr->addresses, 0);
}
if (!theError)
theError = HandPlusHand (itemPtr->value, nickFromVCardItemPtr->addresses);
}
else
// When we have an unpreferred address, check to see if the addresses expansion handle is empty. If it is, place it there.
// If it is not, place it into "other email".
if (!GetHandleSize (nickFromVCardItemPtr->addresses))
theError = HandPlusHand (itemPtr->value, nickFromVCardItemPtr->addresses);
else
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagOtherEmail, 0, true, false, *itemPtr->value, valSize, nickFieldAppendExisting, NICK_CONCAT_NEWLINE, nil);
break;
case vcKeyURL:
theError = VCardToNicknameSetValueOrOther (nickFromVCardItemPtr->notes, abTagWeb, abTagWeb2, abTagOtherWeb, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, NICK_CONCAT_NEWLINE, &ignored);
break;
case vcKeyNote:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagNote, 0, true, false, *itemPtr->value, valSize, nickFieldAppendExisting, NICK_CONCAT_NEWLINE, nil);
break;
case vcPOBox:
case vcExtendedAddress:
case vcStreet:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagAddress, abTagAddress2, home, work, *itemPtr->value, valSize, nickFieldAppendExisting, NICK_CONCAT_NEWLINE, nil);
break;
case vcLocality:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagCity, abTagCity2, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, 0, nil);
break;
case vcRegion:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagState, abTagState2, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, 0, nil);
break;
case vcPostalCode:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagZip, abTagZip2, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, 0, nil);
break;
case vcCountry:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagCountry, abTagCountry2, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, 0, nil);
break;
case vcFamilyName:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagLast, 0, true, false, *itemPtr->value, valSize, nickFieldIgnoreExisting, 0, nil);
break;
case vcGivenName:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, abTagFirst, 0, true, false, *itemPtr->value, valSize, nickFieldIgnoreExisting, 0, nil);
break;
case vcOrganizationName:
case vcOrganizationUnits:
theError = VCardToNicknameSetValue (nickFromVCardItemPtr->notes, 0, abTagCompany, false, true, *itemPtr->value, valSize, nickFieldAppendExisting, NICK_CONCAT_SEPARATOR, nil);
break;
}
if (numParams) {
paramPtr = *itemPtr->params;
for (i = 0; i < numParams; ++i, ++ paramPtr) {
switch (paramPtr->pProperty) {
case vcKeyVoice:
if (itemPtr->property == vcKeyTel) {
tel = false;
theError = VCardToNicknameSetValueOrOther (nickFromVCardItemPtr->notes, abTagPhone, abTagPhone2, abTagOtherPhone, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, NICK_CONCAT_NEWLINE, &ignored);
}
break;
case vcKeyFax:
if (itemPtr->property == vcKeyTel) {
tel = false;
theError = VCardToNicknameSetValueOrOther (nickFromVCardItemPtr->notes, abTagFax, abTagFax2, abTagOtherPhone, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, NICK_CONCAT_NEWLINE, &ignored);
}
break;
case vcKeyMSG:
// We basically ignore this one so that we don't accidentally repeat a number in the "other phone" field. We treat it like TEL
break;
case vcKeyCell:
if (itemPtr->property == vcKeyTel) {
tel = false;
theError = VCardToNicknameSetValueOrOther (nickFromVCardItemPtr->notes, abTagMobile, abTagMobile2, abTagOtherPhone, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, NICK_CONCAT_NEWLINE, &ignored);
}
break;
}
}
}
// If we're working on a telephone property that was not assigned by an existing parameter, assume it is a regular phone
if (!theError && tel)
theError = VCardToNicknameSetValueOrOther (nickFromVCardItemPtr->notes, abTagPhone, abTagPhone2, abTagOtherPhone, home, work, *itemPtr->value, valSize, nickFieldIgnoreExisting, NICK_CONCAT_NEWLINE, &ignored);
UL (itemPtr->value);
return (theError);
}
Boolean VCardToNicknameErrorProc (Handle data, VCardItemPtr itemPtr, NickFromVCardItemRecPtr nickFromVCardItemPtr, VCardErrorType error, long offset)
{
Boolean shouldWeQuit;
// We'll be extra tough on data that does not contain a 'BEGIN' keyword or
// any discerable vCard data (but leaving out 'END' is dandy.
++nickFromVCardItemPtr->numErrors;
if (shouldWeQuit = (error == vcMissingBegin || error == vcMissingVcard)) {
nickFromVCardItemPtr->error = error;
nickFromVCardItemPtr->offset = offset;
}
return (PrefIsSet (PREF_VCARD_QUIT_ON_ERROR) || shouldWeQuit);
}
OSErr VCardToNicknameSetValue (Handle notes, short homeTagIndex, short workTagIndex, Boolean home, Boolean work, Ptr value, long length, NickFieldSetValueType setValue, short separatorIndex, Boolean *ignored)
{
Str255 tag;
OSErr theError;
theError = noErr;
if (home)
theError = SetTaggedFieldValueInNotes (notes, GetRString (tag, ABReservedTagsStrn + homeTagIndex), value, length, setValue, separatorIndex, ignored);
if (work)
theError = SetTaggedFieldValueInNotes (notes, GetRString (tag, ABReservedTagsStrn + workTagIndex), value, length, setValue, separatorIndex, ignored);
return (theError);
}
OSErr VCardToNicknameSetValueOrOther (Handle notes, short homeTagIndex, short workTagIndex, short otherTagIndex, Boolean home, Boolean work, Ptr value, long length, NickFieldSetValueType setValue, short separatorIndex, Boolean *ignored)
{
OSErr theError;
theError = VCardToNicknameSetValue (notes, homeTagIndex, workTagIndex, home, work, value, length, setValue, 0, ignored);
if (*ignored && !theError)
theError = VCardToNicknameSetValue (notes, otherTagIndex, 0, true, false, value, length, setValue, separatorIndex, nil);
return (theError);
}
//
// DecodeQuotedPrintable
//
// Remove occurences of "=\r"
// Replace occurences of "=0D" and "=0A" with '\r'
//
void DecodeQuotedPrintable (Handle value)
{
Ptr spot,
end,
copySpot;
spot = *value;
end = spot + GetHandleSize (value);
copySpot = spot;
while (spot < end) {
if (*spot == '=') {
if (spot + 1 < end && !memcmp (spot, "=\r", 2))
spot += 2;
else
if (spot + 5 < end && !memcmp (spot, "=0D=0A", 6)) {
spot += 6;
*copySpot++ = '\r';
}
else
if (spot + 2 < end && !memcmp (spot, "=0D", 3)) {
spot += 3;
*copySpot++ = '\r';
}
else
if (spot + 2 < end && !memcmp (spot, "=0A", 3)) {
spot += 3;
*copySpot++ = '\r';
}
else
*copySpot++ = *spot++;
}
else
*copySpot++ = *spot++;
}
SetHandleBig (value, copySpot - *value);
}
#endif
OSErr GetNickFlavorDragData (Handle dragData, long *offset, short *ab, short *nick, PStr nickname, Handle *addresses, Handle *notes)
{
Ptr dragDataPtr;
OSErr theError;
long dataSize;
theError = noErr;
dragDataPtr = LDRef (dragData) + *offset;
// The address book and nickname
BlockMoveData (dragDataPtr, ab, sizeof (*ab));
dragDataPtr += sizeof (*ab);
BlockMoveData (dragDataPtr, nick, sizeof (*nick));
dragDataPtr += sizeof (*nick);
// Make sure these are valid indices -- otherwise we might be dealing with garbage
// Commented out for now because this does not seem to be used
// if (*ab < 0 || *ab >= NAliases)
// return (dragNotAcceptedErr);
// if (*nick < 0 || *nick >= (GetHandleSize_ ((*Aliases)[*ab].theData) / sizeof (NickStruct)))
// return (dragNotAcceptedErr);
// The nickname itself
PCopy (nickname, dragDataPtr);
dragDataPtr += *nickname + 1;
// Addresses
BlockMoveData (dragDataPtr, &dataSize, sizeof (dataSize));
dragDataPtr += sizeof (dataSize);
if (dataSize && addresses)
theError = PtrToHand (dragDataPtr, addresses, dataSize);
dragDataPtr += dataSize;
// Notes
if (!theError) {
BlockMoveData (dragDataPtr, &dataSize, sizeof(dataSize));
dragDataPtr += sizeof (dataSize);
if (dataSize && notes)
theError = PtrToHand (dragDataPtr, notes, dataSize);
dragDataPtr += dataSize;
}
UL (dragData);
*offset = dragDataPtr - *dragData;
return (theError);
}
//
// BuildDragData
//
// Build a data handle from the selected nicknames. Based somewhat on the old NickDragToData
// but using the new list view and expanded nickname schtuff.
//
OSErr BuildDragData (SendDragDataInfo *pSendData, Handle *dragData)
{
Accumulator drag;
OSErr theError;
short ab,
nick,
lastAB,
count,
index;
theError = AccuInit (&drag);
if (!theError) {
count = LVCountSelection (&NickList);
// Take a trip through all the selections
lastAB = kAddressBookUndefined;
for (index = 1; index <= count && !theError && !drag.err; ++index)
if (GetSelectedABNick (index, &ab, &nick)) {
CycleBalls();
if (lastAB != ab && ValidNickname (nick))
theError = BuildNicknameDragData (pSendData, &drag, ab, nick);
else
theError = BuildAddressBookDragData (pSendData, &drag, lastAB = ab);
}
}
if (!theError) {
AccuTrim (&drag);
*dragData = drag.data;
drag.data = nil;
}
AccuZap (drag);
return (theError);
}
OSErr BuildNicknameDragData (SendDragDataInfo *pSendData, AccuPtr drag, short ab, short nick)
{
OSErr theError;
theError = noErr;
switch (pSendData->flavor) {
case A822_FLAVOR:
theError = Build822FlavorDragData (drag, ab, nick);
break;
case kNickDragType:
theError = BuildNickFlavorDragData (drag, ab, nick);
break;
case 'TEXT':
theError = BuildTextFlavorDragData (drag, ab, nick);
break;
#ifdef VCARD
case SPEC_FLAVOR:
if (IsVCardAvailable ())
theError = BuildSpecFlavorDragData (pSendData, drag, ab, nick);
break;
#endif
}
return (theError);
}
OSErr BuildAddressBookDragData (SendDragDataInfo *pSendData, AccuPtr drag, short ab)
{
OSErr theError;
short totalNicks,
nick;
theError = noErr;
totalNicks = (*Aliases)[ab].theData ? (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0;
for (nick = 0; nick < totalNicks && !theError; nick++)
theError = BuildNicknameDragData (pSendData, drag, ab, nick);
return (theError);
}
OSErr Build822FlavorDragData (AccuPtr drag, short ab, short nick)
{
Handle data;
OSErr theError;
theError = noErr;
if (data = GetAliasExpansionFor822Flavor (ab, nick)) {
if (drag->offset)
theError = AccuAddChar (drag, ',');
if (!theError)
theError = AccuAddHandle (drag, data);
ZapHandle (data);
}
return (theError);
}
OSErr BuildNickFlavorDragData (AccuPtr drag, short ab, short nick)
{
Str31 name;
Handle data;
OSErr theError;
long dataSize;
// The address book
theError = AccuAddPtr (drag, (void *) &ab, sizeof (ab));
// The nickname
if (!theError)
theError = AccuAddPtr (drag, (void *) &nick, sizeof (nick));
// name
if (!theError) {
GetNicknameNamePStr (ab, nick, name);
theError = AccuAddPtr (drag, name, *name + 1);
}
// addresses
if (!theError) {
data = GetNicknameData (ab, nick, true, true);
dataSize = data ? GetHandleSize (data) : 0;
theError = AccuAddPtr (drag, (void *) &dataSize, sizeof (dataSize));
}
if (data && !theError)
theError = AccuAddHandle (drag, data);
// notes
if (!theError) {
data = GetNicknameData (ab, nick, false, true);
dataSize = data ? GetHandleSize (data) : 0;
theError = AccuAddPtr (drag, (void *) &dataSize, sizeof (dataSize));
}
if (data && !theError)
theError = AccuAddHandle (drag, data);
return (theError);
}
//
// BuildTextFlavorDragData
//
OSErr BuildTextFlavorDragData (AccuPtr drag, short ab, short nick)
{
AttributeValueHandle avPairs;
AttributeValuePtr avPairPtr;
TabFieldHandle fields;
Str31 name,
tag,
shortName;
BinAddrHandle expansion;
Handle addresses,
notes,
leftovers;
OSErr theError;
long unrequestedTypesToBePutInLeftovers;
short count,
i;
// Data we will find incredibly useful later
leftovers = nil;
addresses = nil;
expansion = nil;
fields = Tabs && AB.inited ? GetTabFieldStrings (Tabs, tabTagName) : GetTabFieldsFromResources (tabTagName, CREATOR, kNickTabType);
notes = GetNicknameData (ab, nick, false, true);
unrequestedTypesToBePutInLeftovers = PrefIsSet (PREF_INCLUDE_HIDDEN_NICK_FIELDS_IN_DRAGS) ? (avPairUnknown | avPairHidden) : avPairUnknown;
avPairs = notes ? ParseAllAttributeValuePairs (notes, &leftovers, avPairKnown, unrequestedTypesToBePutInLeftovers) : nil;
theError = nil;
// Put in a blank line in between nicknames
if (drag->offset)
theError = AccuAddChar (drag, kDragDataDelim);
// The nickname itself
if (!theError)
theError = AccuAddRes (drag, ALIAS_A_LABEL);
if (!theError)
theError = AccuAddChar (drag, ' ');
if (!theError)
theError = AccuAddStr (drag, GetNicknameNamePStr (ab, nick, name));
if (!theError)
theError = AccuAddChar (drag, kDragDataDelim);
// All the email addresses -- which is the expansion of the nickname itself.
if (!theError)
theError = PtrToHand (name, &addresses, *name + 1);
if (!theError)
theError = PtrPlusHand ("", addresses, 1);
if (!theError)
theError = ExpandAliases (&expansion, addresses, 0, true);
if (!theError)
CommaList (expansion);
if (expansion) {
if (!theError)
theError = AccuAddStr (drag, FindShortNameWithTag (Tabs, GetRString (tag, ABReservedTagsStrn + abTagEmail), shortName));
if (!theError)
theError = AccuAddChar (drag, ' ');
if (!theError)
theError = AccuAddHandle (drag, expansion);
if (!theError)
theError = AccuAddChar (drag, kDragDataDelim);
}
if (!theError && fields && notes && avPairs) {
// Everything else including the kitchen sink
if (!theError) {
avPairPtr = LDRef (avPairs);
LDRef (notes);
count = GetHandleSize (avPairs) / sizeof (AttributeValueRec);
for (i = 0; i < count && !theError; ++i, ++avPairPtr) {
MakePStr (tag, *notes + avPairPtr->attributeOffset, avPairPtr->attributeLength);
FindShortNameWithTag (Tabs, tag, name);
if (*name) {
theError = AccuAddStr (drag, name);
if (!theError)
theError = AccuAddChar (drag, ' ');
if (!theError)
theError = AccuAddTrPtr (drag, *notes + avPairPtr->valueOffset, avPairPtr->valueLength,"\002\003",">\015");
if (!theError)
theError = AccuAddChar (drag, kDragDataDelim);
}
}
UL (notes);
}
// Leftovers! Mmmm, mmm, good!
if (leftovers && GetHandleSize (leftovers)) {
if (!theError)
theError = AccuAddRes (drag, ALIAS_N_LABEL);
if (!theError)
theError = AccuAddChar (drag, ' ');
if (!theError)
theError = AccuAddTrHandle (drag, leftovers,"\002",">");
if (!theError)
theError = AccuAddChar (drag, kDragDataDelim);
}
}
// Cleanup
ZapHandle (fields);
ZapHandle (avPairs);
ZapHandle (leftovers);
ZapHandle (expansion);
ZapHandle (addresses);
return (theError);
}
#ifdef VCARD
//
// BuildSpecFlavorDragData
//
OSErr BuildSpecFlavorDragData (SendDragDataInfo *pSendData, AccuPtr drag, short ab, short nick)
{
AEDesc dropLocation;
FSSpec spec;
Handle vcardData,
notes;
OSErr theError;
theError = noErr;
// Make a vCard from this nickname
if (notes = GetNicknameData (ab, nick, false, true))
Tr (notes, "\003", "\015");
if (vcardData = MakeVCard (GetNicknameData (ab, nick, true, true), notes)) {
NullADList (&dropLocation, nil);
theError = GetDropLocation (pSendData->drag, &dropLocation);
if (!theError)
theError = GetDropLocationDirectory (&dropLocation, &spec.vRefNum, &spec.parID);
if (!theError) {
MakeVCardFileName (ab, nick, spec.name);
theError = UniqueSpec (&spec, 31);
}
if (!theError) {
FSpCreateResFile (&spec, CREATOR, VCARD_TYPE, smSystemScript);
WhackFinder (&spec);
theError = ResError ();
}
// Write the vCard data into the file
if (!theError)
theError = Blat (&spec, vcardData, false);
if (!theError)
theError = AccuAddPtr (drag, &spec, sizeof (FSSpec));
DisposeADList (&dropLocation,nil);
ZapHandle (vcardData);
}
else
theError = MemError ();
return (theError);
}
#endif
//
// DropNicknameOntoAddressBook
//
// Handle nickname drops onto address books. Returns true if the drop successfully
// completed, false if not.
//
Boolean DropNicknameOntoAddressBook (VLNodeID **movedList, VLNodeInfo *targetInfo, short ab, short nick, Boolean copy)
{
VLNodeID nodeID;
BinAddrHandle mungedAddresses;
short targetAB,
targetNick,
newNick;
Boolean lookingGood;
lookingGood = false; // We are pessimists
mungedAddresses = nil;
GetABNick (targetInfo->nodeID, &targetAB, &targetNick);
if (ab != targetAB && ValidAddressBook (targetAB) && !ValidNickname (targetNick))
if (lookingGood = ValidNickname (newNick = MoveNickname (ab, nick, targetAB, copy))) {
nodeID = MakeNodeID (targetAB, newNick);
PtrPlusHand (&nodeID, movedList, sizeof (nodeID));
}
return (lookingGood);
}
//
// DropAddressBook
//
// Handle address book drops onto other address books. Returns true if the drop successfully
// completed, false if not.
//
Boolean DropAddressBook (VLNodeID **movedList, VLNodeInfo *targetInfo, short ab, Boolean copy)
{
VLNodeID nodeID;
short targetAB,
targetNick,
abNick,
totalNicks,
newNick;
Boolean lookingGood;
lookingGood = false; // We are pessimists
GetABNick (targetInfo->nodeID, &targetAB, &targetNick);
if (ab != targetAB) {
GetABNick (targetInfo->nodeID, &targetAB, &targetNick);
if (ValidAddressBook (targetAB) && !ValidNickname (targetNick)) {
totalNicks = (*Aliases)[ab].theData ? (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0;
for (abNick = 0; abNick < totalNicks; abNick++)
if (lookingGood = ValidNickname (newNick = MoveNickname (ab, abNick, targetAB, copy))) {
nodeID = MakeNodeID (targetAB, newNick);
PtrPlusHand (&nodeID, movedList, sizeof (nodeID));
}
}
}
return (lookingGood);
}
//
// MoveNickname
//
// Returns the index of the nickname as it appears in it's new address book, or
// kNickUndefined if the move failed.
//
short MoveNickname (short ab, short nick, short targetAB, Boolean copy)
{
BinAddrHandle mungedAddresses;
Handle addresses,
notes;
Str31 name;
short newNick;
Boolean lookingGood;
if (!ValidAddressBook (targetAB))
return (kNickUndefined);
lookingGood = true;
mungedAddresses = nil;
notes = GetNicknameData (ab, nick, false, true);
if (addresses = GetNicknameData (ab, nick, true, true))
if (SuckAddresses (&mungedAddresses, addresses, true, true, false, nil))
lookingGood = false;
if (lookingGood) {
// Add the nickname into the target
lookingGood = ValidNickname (newNick = AddNickname (mungedAddresses, notes, targetAB, GetNicknameNamePStr (ab, nick, name), false, nrDifferent, false));
// Now, if that worked out okay, mark the nickname from its original address book as deleted (unless we were performing a copy)
if (lookingGood && !copy && !(*Aliases)[ab].ro && (*Aliases)[ab].theData) {
(*((*Aliases)[ab].theData))[nick].addressesDirty = true;
(*((*Aliases)[ab].theData))[nick].deleted = true;
SetAliasDirty(ab);
}
}
ZapHandle (mungedAddresses);
return (lookingGood ? newNick : kNickUndefined);
}
/************************************************************************
* DoViewByMenu - change the way the list is sorted
************************************************************************/
void DoViewByMenu (void)
{
Str255 oldViewByString,
newViewByString;
short numABs,
ab,
abDisplayed,
nickDisplayed;
GetPref (oldViewByString, PREF_NICK_VIEW_BY);
GetMenuItemText (PopUpMenuH (CtlViewBy), GetControlValue (CtlViewBy), newViewByString);
if (!StringSame (oldViewByString, newViewByString)) {
abDisplayed = AB.abDisplayed;
nickDisplayed = AB.nickDisplayed;
numABs = NAliases;
for (ab = 0; ab < numABs; ab++)
ZapHandle ((*Aliases)[ab].sortData);
InvalidListView (&NickList);
// Reselect the previous items
if (!(abDisplayed == kAddressBookUndefined && nickDisplayed == kNickUndefined))
ABNickLVSelect (MakeNodeID (abDisplayed, nickDisplayed), true);
SetPref (PREF_NICK_VIEW_BY, newViewByString);
}
}
/************************************************************************
* DoNewNickname - make a new nickname
************************************************************************/
void DoNewNickname (long modifiers)
{
VLNodeInfo data;
Str31 name,
massagedName;
Handle addresses,
notes,
shortAddresses;
short ab,
nick;
// Put the nickname in the first selected address book...
// ... or in the default address book
if (LVGetItem (&NickList, 1, &data, true))
GetABNick (data.nodeID, &ab, &nick);
else
if (!LVGetItemWithNodeID (&NickList, MakeABNodeID (ab = FindAddressBookType (eudoraAddressBook)), &data))
return;
// If a nickname (by itself) is selected, and the option key is being held down, we'll clone that nickname
*name = 0;
addresses = nil;
notes = nil;
shortAddresses = nil;
if ((modifiers & optionKey) && LVCountSelection (&NickList) == 1 && ValidNickname (nick)) {
PCopy (name, data.name);
if (addresses = GetNicknameData (ab, nick, true, true))
SuckPtrAddresses (&shortAddresses, LDRef (addresses), GetHandleSize (addresses), true, true, false, nil);
notes = GetNicknameData (ab, nick, false, true);
// Make copies of the existing notes to be passed to NewNickLow
if (notes)
HandToHand (&notes);
}
MakeUniqueNickname (ab, name);
if ((nick = NewNickLow (shortAddresses, notes, ab, name, false, nrAdd, false)) != kNickUndefined) {
// If this address book is collapsed, expand it
if ((*Aliases)[ab].collapsed)
LVExpand (&NickList, MakeABNodeID (ab), data.name, true);
InvalidListView (&NickList);
// Massage the item name so it can be found in the list view
GetListNameBasedOnTag (SortTag, ab, nick, massagedName);
// Rename it
ABSetKeyboardFocus (focusNickList);
LVRename (&NickList, MakeNodeID (ab, nick), massagedName, false, !PrefIsSet (PREF_ALLOW_NICK_RENAME_IN_LIST)); // Allow user to rename the untitled nickname
if (!PrefIsSet (PREF_ALLOW_NICK_RENAME_IN_LIST)) {
ABSetKeyboardFocus (focusNickField);
PeteSelectAll (Win, Win->pte);
}
}
ZapHandle (shortAddresses);
}
/************************************************************************
* DoNewNickname - make a new nickname
************************************************************************/
void DoNewAddressBook (void)
{
FSSpec spec,
folderSpec;
short ab;
SubFolderSpec (NICK_FOLDER,&folderSpec);
// Make a unique "untitled" name
MakeUniqueUntitledSpec (folderSpec.vRefNum, folderSpec.parID, UNTITLED_ADDRESS_BOOK, &spec);
// Make the new file
ab = NickMakeNewFile (spec.name);
if (ValidAddressBook (ab)) {
InvalidListView (&NickList);
LVRename (&NickList, MakeABNodeID (ab), spec.name, false, false); // Allow user to rename the untitled address book
}
}
void DoHorizontalZoom (MyWindowPtr win)
{
WindowPtr winWP;
Rect contR;
winWP = GetMyWindowWindowPtr (win);
win->saveSize = true;
utl_InvalGrow (winWP);
if (Collapsed) {
SetWinMinSize (Win, MinListWidth + MinTabWidth, kMinNickWinHeight);
SetPrefLong (PREF_NICK_COLLAPSED, 0);
contR = win->contR;
contR.right += Collapsed - 2;
SanitizeSize (win, &contR);
// Show the things on the right side (note that the tabs, since not really hidden, move into place when we resize
ShowControl (NickField);
ShowControl (CtlRecipient);
ShowControl (CtlDoNotSync);
SizeWindow (winWP, contR.right, contR.bottom, false);
Collapsed = 0;
}
else {
SetWinMinSize (Win, MinListWidth, kMinNickWinHeight);
SetPrefLong (PREF_NICK_COLLAPSED, Collapsed = win->contR.right - VertDivider.left + 1);
// Hide the things on the right side
MoveLabelField (NickField, CNTL_OUT_OF_VIEW, CNTL_OUT_OF_VIEW, ControlWi (NickField), ControlHi (NickField));
MoveMyCntl (CtlRecipient, CNTL_OUT_OF_VIEW, CNTL_OUT_OF_VIEW, ControlWi (CtlRecipient), ControlHi (CtlRecipient));
MoveMyCntl (CtlDoNotSync, CNTL_OUT_OF_VIEW, CNTL_OUT_OF_VIEW, ControlWi (CtlDoNotSync), ControlHi (CtlDoNotSync));
MoveTab (Tabs, CNTL_OUT_OF_VIEW, CNTL_OUT_OF_VIEW, ControlWi (Tabs), ControlHi (Tabs));
// Move the focus to the list
ABSetKeyboardFocus (focusNickList);
SizeWindow (winWP, VertDivider.left, win->contR.bottom, false);
}
MyWindowDidResize (win, nil);
}
void DoRecipientList (ControlHandle theControl)
{
MyWindowPtr win = GetWindowMyWindowPtr(GetControlOwner(theControl));
VLNodeInfo data;
Str31 name;
short value,
count,
i,
ab,
nick;
// Flip the checkbox itself
value = GetControlValue (theControl);
SetControlValue (theControl, value = (value == kControlCheckBoxUncheckedValue || value == kControlCheckBoxMixedValue ? kControlCheckBoxCheckedValue : kControlCheckBoxUncheckedValue));
// Walk through all the selected list items
count = LVCountSelection (&NickList);
// Take a trip through all the selections looking for relevant conditions
for (i = 1; i <= count; ++i) {
GetSelectedABNickNameData (i, &ab, &nick, name, &data);
if (nick != kNickUndefined) {
GetNicknameNamePStr (ab, nick, name);
if (value == kControlCheckBoxCheckedValue)
AddStringAsTo (name);
else
RemoveFromTo (name);
}
data.iconID = ABSetIcon (ab, nick);
LVSetItemWithNodeID (&NickList, MakeNodeID (ab, nick), &data, true);
}
win->isDirty = true;
}
void DoDoNotSync (ControlHandle theControl)
{
MyWindowPtr win = GetWindowMyWindowPtr(GetControlOwner(theControl));
VLNodeInfo data;
Str31 name;
Handle notes;
short value,
count,
i,
ab,
nick;
Boolean notesExist;
// Flip the checkbox itself
value = GetControlValue (theControl);
SetControlValue (theControl, value = (value == kControlCheckBoxUncheckedValue || value == kControlCheckBoxMixedValue ? kControlCheckBoxCheckedValue : kControlCheckBoxUncheckedValue));
// Walk through all the selected list items
count = LVCountSelection (&NickList);
// Take a trip through all the selections looking for relevant conditions
for (i = 1; i <= count; ++i) {
GetSelectedABNickNameData (i, &ab, &nick, name, &data);
if (nick != kNickUndefined) {
GetNicknameNamePStr (ab, nick, name);
if (value == kControlCheckBoxCheckedValue) {
notes = GetNicknameData (ab, nick, false, true);
if (!(notesExist = notes ? true : false))
notes = NuHandle (0);
if (notes)
if (!SetNicknameChangeBit (notes, changeBitPrivate, false))
ReplaceNicknameNotes (ab, name, notes);
if (!notesExist)
ZapHandle (notes);
}
else
ClearNicknameChangeBits (ab, nick, changeBitPrivate);
}
}
win->isDirty = true;
}
//
// ABNickLVSelect
//
// Wrapper around list view's LVSelect taking into account the current View By sort order
//
Boolean ABNickLVSelect (VLNodeID nodeID, Boolean addToSelection)
{
Str31 massagedName;
return (LVSelect (&NickList, nodeID, GetListNameBasedOnTag (SortTag, GetAddressBook (nodeID), GetNickname (nodeID), massagedName), addToSelection));
}
//
// ABRename
//
// We're always dealing with nicknames here, even though the list might be sorted
// by some other means
//
// WARNING - after calling this to rename a nickname file, the index (ab) may no longer
// be valid. SortAddressBookList() is called here which may reorder the Aliases list.
// Don't trust ab after you call this routine on a nickname file! - jdboyd 12/18/03
Boolean ABRename (short ab, short nick, StringPtr newName)
{
VLNodeInfo data;
VLNodeID nodeID;
Str31 oldName;
Handle notes;
OSErr theError;
Boolean notesExist;
Str63 abName;
// If we haven't been given a valid nickname, bail on outta here
nodeID = MakeNodeID (ab, nick);
if (!LVGetItemWithNodeID (&NickList, nodeID, &data))
return (false);
*newName = MIN (*newName, sizeof (Str31) - 1);
if (*newName && !ExactlyEqualString (newName, GetNicknameNamePStr (ab, nick, oldName))) {
PCopy (data.name, newName);
// Is it a nickname file?
if (nick == kNickUndefined) {
Str31 cleanName;
SanitizeFN(cleanName,newName,MAC_FN_BAD,MAC_FN_REP,false);
if (EqualStrRes(cleanName,ALIAS_FILE) || EqualStrRes(cleanName,USA_EUDORA_NICKNAMES) || EqualStrRes(cleanName,FILE_ALIAS_NICKNAMES))
theError = dupFNErr;
else
theError = FSpRename (&(*Aliases)[ab].spec, cleanName);
if (!theError) {
PCopy ((*Aliases)[ab].spec.name, cleanName);
SortAddressBookList ();
// sorting the list may have moved the address book. Find it again. jdboyd 12/18/03
for (ab = NAliases-1; ab>=0; ab--)
{
PCopy(abName,(*Aliases)[ab].spec.name);
if (StringSame(abName,cleanName)) break;
}
// We'll need to rename the nickname's photo folder in the photo album as well
}
else {
FileSystemError (CANT_RENAME_ERR, (*Aliases)[ab].spec.name, theError);
return (false);
}
}
else {
// Check to see whether or not we already have a nickname with this name
if (BadNickname (newName, oldName, ab, nick, true, true, false) != nrOk)
return (false);
// Make sure the state of the recipient list is maintained
if (IsNicknameOnRecipList (ab, nick)) {
RemoveFromTo (oldName);
AddStringAsTo (newName);
}
// Rename this in the toolbar as well
TBRenameNickButton (oldName, newName);
// Let's rename a nickname
SetNickname (ab, nick, newName);
LVSetItemWithNodeID (&NickList, nodeID, &data, false);
// Update the nickname field
SetLabelFieldString (NickField, newName);
}
// Need to resort the list
InvalidListView (&NickList);
// Massage the item name so it can be found in the list view
ABNickLVSelect (data.nodeID, true);
SetAliasDirty(ab);
// 1/25/01 (jp) We have a problem in the conduit when nicknames are changed
// and only the 'NNam' resource is changed on disk. The 'alias' left in the
// data fork reflects the old nickname. To fix this, we're going to read in
// the nickname's notes and mark them dirty, which seems to force the 'alias'
// lines to be written to disk when we save.
if (ValidNickname (nick)) {
notes = GetNicknameData (ab, nick, false, true);
if (!(notesExist = notes ? true : false))
notes = NuHandle (0);
if (notes)
ReplaceNicknameNotes (ab, newName, notes);
if (!notesExist)
ZapHandle (notes);
}
}
return (true);
}
/************************************************************************
* ABRemove - delete selected nicknames and address books
************************************************************************/
void ABRemove(void)
{
VLNodeInfo data;
Str31 name;
short ab,
nick,
i;
Boolean weDid; // well?
weDid = false;
ABSetKeyboardFocus (focusNickList);
// Save info about the first selected row so we can reselect
// the item now in this row after the remove has been completed.
LVGetItem (&NickList, 1, &data, true);
// Check for ro
for (i = LVCountSelection (&NickList); i; --i)
if (GetSelectedABNick (i, &ab, &nick) && (*Aliases)[ab].ro)
{
WarnUser(ITS_LOCKED_DUMMY,0);
return;
}
// Remove items in reverse list order
for (i = LVCountSelection (&NickList); i; --i) {
if (GetSelectedABNick (i, &ab, &nick)) {
// Is it a nickname file?
if (nick == kNickUndefined)
weDid = RemoveAddressBook (ab);
else {
GetNicknameNamePStr (ab, nick, name);
if (IsNicknameOnRecipList (ab, nick))
RemoveFromTo (name);
if (weDid = RemoveNickname (ab, nick))
TBRemoveDefunctNicknameButton (name);
SetAliasDirty(ab);
}
}
}
if (weDid) {
InvalidListView (&NickList);
if (LVGetItem (&NickList, data.rowNum, &data, false))
if (!data.isParent)
LVSelect (&NickList, data.nodeID, data.name, true);
}
}
//
// ABMaybeRenameNickname
//
Boolean ABMaybeRenameNickname (MyWindowPtr win)
{
Str255 nickname;
Boolean fResults;
// Patch up our focus problem...
if (AB.abDisplayed<0 || AB.nickDisplayed<0) return true;
fResults = true;
// If the focus is in the nickname field, check to see if we need to rename the nickname itself
if (NickFocus == focusNickField)
if (!ABRename (AB.abDisplayed, AB.nickDisplayed, GetLabelFieldString (NickField, nickname))) {
GetNicknameNamePStr (AB.abDisplayed, AB.nickDisplayed, nickname);
SetLabelFieldString (NickField, nickname);
PeteSelectAll (win, win->pte);
fResults = false;
}
return (fResults);
}
Boolean ABDisplayNicknameInTab (short ab, short nick)
{
Str31 nickname,
massagedName;
OSErr theError;
Boolean validNickname,
changedDisplay;
theError = noErr;
changedDisplay = false;
if (LVCountSelection (&NickList) != 1) {
ab = kAddressBookUndefined;
nick = kNickUndefined;
}
if (ab != AB.abDisplayed || nick != AB.nickDisplayed) {
validNickname = (ab != kAddressBookUndefined && nick != kNickUndefined);
// Nickname field
nickname[0] = 0;
if (validNickname)
GetNicknameNamePStr (ab, nick, nickname);
SetLabelFieldString (NickField, nickname);
// Let the tab ready itself for a new nickname
ClearTab (Tabs);
// Now we fill in the cleared fields with any tagged data
if (validNickname)
theError = PopulateTabs (Tabs, ab, nick);
// clean all the dirty stuff so we're ready for user input
ABClean ();
AB.abDisplayed = ab;
AB.nickDisplayed = nick;
changedDisplay = true;
}
// Uh oh... if we weren't able to populate the tabs, unselect the item and tell the user
if (theError)
if (validNickname) {
// Massage the item name so it can be found in the list view
GetListNameBasedOnTag (SortTag, ab, nick, massagedName);
LVUnselect (&NickList, MakeNodeID (ab, nick), massagedName, true);
}
return (changedDisplay);
}
//
// PopulateTabs
//
// <09> Find the relevant nickname data in notes
// <09> Parse tags from the nickname data
// <09> For each found tag, look for this tag in an object within each tab pane
// <09> Set the value of the object to the value associated with the tag in the data
// <09> Set any other object values to be empty
//
OSErr PopulateTabs (ControlHandle tabControl, short ab, short nick)
{
TabObjectPtr objectPtr;
Accumulator leftovers; // Untagged or unrecognized tagged information we find
PETEHandle pte;
Handle temp,
addresses,
notes;
Str255 tag;
UPtr notesPtr,
newPtr,
attribute,
value;
OSErr theError;
Size notesSize;
long attributeLength,
valueLength;
addresses = nil;
notes = nil;
theError = AccuInit (&leftovers);
// The addresses field. We'll try to suck addresses from the handle we've retrieved. If we're not able
// to get addresses we'll just use the content of the handle in it's entirety
if (!theError) {
GetRString (tag, ABReservedTagsStrn + abTagEmail);
if (temp = GetNicknameData (ab, nick, true, true)) {
theError = SuckAddresses (&addresses, temp, true, true, false, nil);
if (!theError)
if (addresses) {
if (!PrefIsSet(PREF_NICK_NOSORT)) SortBinAddr(addresses);
FlattenListWith (addresses, '\015');
SetObjectValueWithTag (tabControl, tag, LDRef (addresses), GetHandleSize (addresses));
UL (addresses);
}
}
if (!theError && !addresses && temp) {
SetObjectValueWithTag (tabControl, tag, LDRef (temp), GetHandleSize (temp));
UL (temp);
}
}
ZapHandle (addresses);
// Find the relevant nickname data in notes
if (!theError) {
if (temp = GetNicknameData (ab, nick, false, true)) {
notes = temp;
theError = MyHandToHand (&notes);
}
}
if (!theError && notes) {
// Clean things up a bit
Tr (notes, "\003", "\015");
// Walk through the 'notes', looking for attribute/value pairs.
notesSize = GetHandleSize (notes);
notesPtr = LDRef (notes);
// Go fish... Dangerous -- but we are being very, very careful to not use any pointer into the notes after we have unlocked the handle
while (!theError && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, &attributeLength, &value, &valueLength))) {
// I got one!
MakePPtr (tag, attribute, attributeLength);
// If anything appears between this tag and the previous tag, put it in leftovers
theError = AppendLeftoverText (&leftovers, notesPtr, attribute - 2);
// Try to map this tag to one of our fields
if (!theError && !SetObjectValueWithTag (tabControl, tag, value, valueLength)) {
// If there wasn't a matching tag, and this is not one of our hidden tags,
// save this information in our 'leftovers'
if (!FindSTRNIndex (ABHiddenTagsStrn, tag)) {
if (leftovers.offset)
theError = AccuAddChar (&leftovers, '\015');
if (!theError)
theError = AccuAddPtr (&leftovers, attribute - 1, value - attribute + valueLength + 2);
}
}
notesPtr = newPtr;
}
// Any leftovers at the end of the note?
if (!theError)
theError = AppendLeftoverText (&leftovers, notesPtr, *notes + notesSize - 1);
}
// Once we're done setting up all the found tab fields, append the leftovers to the notes field
if (!theError && leftovers.offset) {
AccuTrim (&leftovers);
if (objectPtr = FindObjectWithTag (tabControl, GetRString (tag, ABReservedTagsStrn + abTagNote)))
if (pte = GetLabelFieldPete (objectPtr->control))
theError = PeteAppendText (LDRef (leftovers.data), GetHandleSize (leftovers.data), pte);
UnlockObject (objectPtr);
}
ZapHandle (notes);
AccuZap (leftovers);
return (theError);
}
//
// ReplaceNicknameData
//
// Replace an existing nickname with new (new! new! new!) minty fresh data culled from changes in the address book
//
void ReplaceNicknameData (ControlHandle tabControl, short ab, short nick)
{
NicknameDataRec nickData;
AttributeValueHandle avPairs;
AttributeValuePtr avPairPtr;
TabObjectPtr objectPtr;
VLNodeInfo data;
VLNodeID nodeID;
Str255 name,
tag;
Handle addresses,
notes,
temp2Handle;
short count,
i;
Boolean noAddresses,
oldGroup;
// Doesn't make much sense to replace the data for an address bookn does it?
if (nick == kNickUndefined)
return;
// We will _only_ replace the nickname data if this is _really_ the displayed nickname
if (ab == AB.abDisplayed || nick == AB.nickDisplayed) {
// Funky code to see if the email expansion field is empty. If it is, we lie and say it is
// dirty (even if it is not) so that we have a chance to initialize the contents of the address
// handle to a comma to maintain compatibility with the old code which requires something in
// addresses in order for a nickname to be written out into the nickname file (grumble, grumble...)
// Don't do this on locked address book files. Who cares if the old code can't write the nickname
// back out to the file? This address book is read-only and we're not going to be saving changes.
// jdboyd 3/24/04
noAddresses = false;
if (!(*Aliases)[ab].ro) {
if (objectPtr = FindObjectWithTag (tabControl, GetRString (tag, ABReservedTagsStrn + abTagEmail))) {
if (noAddresses = GetHandleSize (GetLabelFieldText (objectPtr->control)) == 0)
if (addresses = GetNicknameData (ab, nick, true, true))
noAddresses = !(GetHandleSize (addresses) == 1 && **addresses == ',');
UnlockObject (objectPtr);
}
}
if (tabControl && (IsTabDirty (tabControl) || noAddresses)) {
TickleMe = GetControlValue(CtlViewBy)!=1;
oldGroup = (*((*Aliases)[ab].theData))[nick].group;
Zero (nickData);
nickData.notes = NuHandle (0);
nickData.theError = MemError ();
// Copy any existing hidden notes into our working notes handle
if (!nickData.theError) {
temp2Handle = nil;
if (notes = GetNicknameData (ab, nick, false, true))
if (avPairs = ParseAllAttributeValuePairs (notes, &temp2Handle, avPairHidden, avNoPairs)) {
avPairPtr = LDRef (avPairs);
count = GetHandleSize (avPairs) / sizeof (AttributeValueRec);
for (i = 0; i < count && !nickData.theError; ++i, ++avPairPtr) {
MakePStr (tag, *notes + avPairPtr->attributeOffset, avPairPtr->attributeLength);
nickData.theError = AddAttributeValuePair (nickData.notes, tag, *notes + avPairPtr->valueOffset, avPairPtr->valueLength);
}
}
ZapHandle (temp2Handle);
}
// Iterate all of the tab objects so we can build the new notes
IterateTabObjectsUL (tabControl, ReplaceNicknameDataProc, &nickData);
if (!nickData.theError) {
ReplaceNicknameAddresses (ab, GetNicknameNamePStr (ab, nick, name), nickData.addresses);
Tr (nickData.notes, "\015", "\003");
ReplaceNicknameNotes (ab, GetNicknameNamePStr (ab, nick, name), nickData.notes);
// If the nickname's "group status" has changed, update the icon in the list
if (oldGroup != (*((*Aliases)[ab].theData))[nick].group)
if (LVGetItemWithNodeID (&NickList, nodeID = MakeNodeID (ab, nick), &data)) {
data.iconID = ABSetIcon (ab, nick);
LVSetItemWithNodeID (&NickList, nodeID, &data, true);
}
}
if (nickData.theError) {
ZapHandle (nickData.addresses);
ZapHandle (nickData.notes);
WarnUser (COULDNT_MOD_ALIAS, nickData.theError);
}
}
}
}
Boolean ReplaceNicknameDataProc (TabObjectPtr objectPtr, NicknameDataPtr nickDataPtr)
{
Handle text;
if (nickDataPtr->theError)
return (true);
// handle a field object
switch (objectPtr->type) {
case fieldObject:
// grab the text
if (text = GetLabelFieldText (objectPtr->control))
ReplaceNicknameText (objectPtr, nickDataPtr, text);
break;
case controlObject:
case controlResourceObject:
if (objectPtr->behavior) {
if (((*objectPtr->behavior)->flags & behaveStringOneIsTrue) && GetControlValue (objectPtr->control))
nickDataPtr->theError = AddAttributeValuePair (nickDataPtr->notes, objectPtr->tag, &(*objectPtr->behavior)->stringOne[1], *(*objectPtr->behavior)->stringOne);
if (((*objectPtr->behavior)->flags & behaveStringTwoIsFalse) && !GetControlValue (objectPtr->control))
nickDataPtr->theError = AddAttributeValuePair (nickDataPtr->notes, objectPtr->tag, &(*objectPtr->behavior)->stringTwo[1], *(*objectPtr->behavior)->stringTwo);
}
break;
case pictureObject:
// Take the URL out of the control refcon and put it in the notes
ReplaceNicknameText (objectPtr, nickDataPtr, (Handle) GetControlReference (objectPtr->control));
break;
}
return (false);
}
void ReplaceNicknameText (TabObjectPtr objectPtr, NicknameDataPtr nickDataPtr, Handle text)
{
BinAddrHandle addresses;
Str255 tag;
long length;
Boolean textZapNeeded;
textZapNeeded = false;
length = GetHandleSize (text);
// could this be the expansion field?
if (StringSame (objectPtr->tag, GetRString (tag, ABReservedTagsStrn + abTagEmail))) {
// We need to make a copy of the text before massaging it, otherwise the text in the PETE becomes confused
// for subsequent editing operations.
nickDataPtr->theError = MyHandToHand (&text);
if (!nickDataPtr->theError) {
textZapNeeded = true;
if (!GetHandleSize_ (text))
nickDataPtr->theError = PtrPlusHand_ ("\015", text, 1);
if (!nickDataPtr->theError) {
Tr (text, "\015", ",");
nickDataPtr->addresses = text;
nickDataPtr->theError = SuckAddresses (&addresses, text, false, true, false, nil);
ZapHandle (addresses);
textZapNeeded = false; // Note that we don't want to zap the copy of the text since it's now stored in addresses
}
}
}
// Just a plain ol' field, so the text will be slapped into an attribute value pair and put into notes
else
if (length) {
// The 'note' field might already contain attribute value pairs, so just put this text
// onto the end of our notes handle (it will be parsed correctly when we next open the Notes tab)
if (StringSame (objectPtr->tag, GetRString (tag, ABReservedTagsStrn + abTagNote)))
nickDataPtr->theError = HandAndHand (text, nickDataPtr->notes);
else {
// Massage the other email field, sorta like the regular email field
if (StringSame (objectPtr->tag, GetRString (tag, ABReservedTagsStrn + abTagOtherEmail))) {
nickDataPtr->theError = MyHandToHand (&text);
if (!nickDataPtr->theError) {
textZapNeeded = true;
Tr (text, "\015", ",");
nickDataPtr->theError = SuckAddresses (&addresses, text, false, true, false, nil);
ZapHandle (addresses);
}
}
if (!nickDataPtr->theError) {
Tr(text,">","\002");
nickDataPtr->theError = AddAttributeValuePair (nickDataPtr->notes, objectPtr->tag, LDRef (text), length);
UL (text);
Tr(text,"\002",">");
}
}
}
if (textZapNeeded)
ZapHandle (text);
}
/************************************************************************
* AddNicknameListItems - add nicknames and address books to list
************************************************************************/
void AddNicknameListItems (ViewListPtr pView, VLNodeID nodeID)
{
VLNodeInfo info;
OSErr theError;
short addressBooks,
nick,
ab;;
theError = noErr;
GetABNick (nodeID, &ab, &nick);
SetPort_ (GetMyWindowCGrafPtr (pView->wPtr));
LSetDrawingMode (false, pView->hList);
// Check for the top level condition, in which case we add all the address books
if (nodeID == kAdddressBookRootID) {
info.isParent = true;
info.useLevelZero = false;
info.style = 0;
info.refCon = 0;
addressBooks = NAliases;
for (ab = 0; ab < addressBooks && !theError; ++ab) {
// Don't display plug-in nicknames
if (IsPluginAddressBook (ab))
continue;
#ifdef VCARD
// Auto-expand personal nickname folders (i.e. my vCards) without the parent folder
if (IsPersonalAddressBook (ab)) {
// Don't display personal nicknames unless the user wishes to do so
if (PrefIsSet (PREF_PERSONAL_NICKNAMES_NOT_VISIBLE) || !IsVCardAvailable ())
continue;
// If the Personal Nicknames file is empty, create something reasonable
if (!(*Aliases)[ab].theData || !GetHandleSize ((*Aliases)[ab].theData))
theError = AutoGeneratePersonalInformation (ab);
if (!theError)
theError = AddChildren (pView, ab);
continue;
}
#endif
// Don't display the cache file unless the user wishes to do so
if (HasFeature (featureNicknameWatching) && IsHistoryAddressBook (ab) && PrefIsSet (PREF_NICK_CACHE_NOT_VISIBLE))
continue;
// Add the address book itself
info.iconID = (*Aliases)[ab].ro ? LOCKED_ADDRESS_BOOK_ICON : ADDRESS_BOOK_ICON;
info.nodeID = MakeABNodeID (ab);
info.isCollapsed = (*Aliases)[ab].collapsed;
PCopy (info.name, (*Aliases)[ab].spec.name);
// Add List Manager rows myself for (hopefully) better speed
if (ListFlags & kfManualRowAddsExpected)
(void) LAddRow (1, LVGetNextRowToAdd (), pView->hList);
LVAdd (pView, &info);
}
}
else {
// Add the items for a specific parent
GetABNick (nodeID, &ab, &nick);
theError = AddChildren (pView, ab);
}
LSetDrawingMode (true, pView->hList);
if (theError)
WarnUser (ALIAS_DISPLAY_ERR, theError);
}
OSErr AddChildren (ViewListPtr pView, short ab)
{
VLNodeInfo info;
OSErr theError;
short nicknames,
oldRows,
count,
nick,
index,
*sort;
theError = noErr;
// Add the items for a specific parent
info.isParent = false;
info.isCollapsed = false;
info.useLevelZero = false;
info.style = 0;
info.refCon = 0;
SortAddressBook (ab);
sort = LDRef ((*Aliases)[ab].sortData);
// Add List Manager rows myself for (hopefully) better speed -- but subtract out deleted rows!
count = (*Aliases)[ab].theData ? (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0;
if (ListFlags & kfManualRowAddsExpected) {
nicknames = count;
for (nick = 0; nick < count; ++nick)
if ((*((*Aliases)[ab].theData))[nick].deleted)
--nicknames;
oldRows = (*pView->hList)->dataBounds.bottom;
(void) LAddRow (nicknames, LVGetNextRowToAdd (), pView->hList);
if ((*pView->hList)->dataBounds.bottom != oldRows + nicknames) {
if ((*pView->hList)->dataBounds.bottom != oldRows)
LDelRow ((*pView->hList)->dataBounds.bottom - oldRows, LVGetNextRowToAdd (), pView->hList);
theError = memFullErr;
}
}
for (index = 0; index < count && !theError; ++index) {
nick = sort ? *sort++ : nick;
if (!(*((*Aliases)[ab].theData))[nick].deleted) {
info.nodeID = MakeNodeID (ab, nick);
info.iconID = ABSetIcon (ab, nick);
GetListNameBasedOnTag (SortTag, ab, nick, info.name);
LVAdd (pView, &info);
}
}
UL ((*Aliases)[ab].sortData);
return (theError);
}
//
// GetListNameBasedOnTag
//
// List items are formatted in specific ways, depending on the current "View By" setting:
//
// nickname -- Sorting by nickname
// first last -- Sorting by first name
// last, first -- Sorting by last name
// field -- Sorting by anything else
//
// When sorting by anything other than nickname, we may also append the nickname at the end -- unless
// we are sorting one of the special "ABDeferNickInListTags" fields or if the field value is nil.
//
// We further suppress nickname appending if the user has set PREF_SUPPRESS_NICKS_IN_VIEW_BY -- but,
// again, only if there is indeed text available to display.
//
PStr GetListNameBasedOnTag (PStr tag, short ab, short nick, Str31 name)
{
ListNameFormatType listNameFormat;
Str255 missingValue,
scratch,
nickname;
Handle value1,
value2;
// Sorting by nickname
if (!*tag)
return (GetNicknameNamePStr (ab, nick, name));
listNameFormat = lnfOtherField;
// Sorting by email is a weird thing to do, so let's make it weird here, too
if (StringSame (tag, GetRString (scratch, ABReservedTagsStrn + abTagEmail))) {
if (value1 = GetNicknameData (ab, nick, true, true))
MyHandToHand (&value1);
}
else
// Sorting by some other field -- get the value
value1 = GetTaggedFieldValue (ab, nick, tag);
// Certain fields are combined with the key field (first/last), in which case we get another value
if (StringSame (tag, GetRString (scratch, ABReservedTagsStrn + abTagFirst))) {
listNameFormat = lnfFirstLast;
value2 = GetTaggedFieldValue (ab, nick, GetRString (scratch, ABReservedTagsStrn + abTagLast));
}
else
if (StringSame (tag, GetRString (scratch, ABReservedTagsStrn + abTagLast))) {
listNameFormat = lnfLastFirst;
value2 = GetTaggedFieldValue (ab, nick, GetRString (scratch, ABReservedTagsStrn + abTagFirst));
}
else
value2 = nil;
// We still might need the nickname, let's see if we do...
if (!value1 || (!FindSTRNIndex (ABSuppressNickInListTagsStrn, tag) && !PrefIsSet (PREF_SUPPRESS_NICKS_IN_VIEW_BY)))
GetNicknameNamePStr (ab, nick, nickname);
else
*nickname = 0;
//
// We should have everything we need (I hope), so let's crank out a name
//
// First, deal with the initial field value
if (value1)
MakePPtr (name, *value1, MIN (sizeof (Str31) - 1, GetHandleSize (value1)));
else
PCopy (name, GetRString (missingValue, NICKLIST_MISSING_FIELD_VALUE));
// And the secondary value
if (value2)
MakePPtr (scratch, *value2, GetHandleSize (value2));
else
*scratch = 0;
// Handle the rest depending on the format to be built
switch (listNameFormat) {
case lnfFirstLast:
if (value2) {
PMaxCatC (name, sizeof (Str31) - 1, ' ');
PSCat_C (name, scratch, sizeof (Str31) - 1);
}
break;
case lnfLastFirst:
if (value2) {
PSCat_C (name, "\p, ", sizeof (Str31) - 1);
PSCat_C (name, scratch, sizeof (Str31) - 1);
}
break;
case lnfOtherField:
break;
}
// Append on the nickname (if any)
if (*nickname) {
GetRString (scratch, NICKLIST_PAREN);
PMaxCatC (name, sizeof (Str31) - 1, ' ');
if (*scratch)
PMaxCatC (name, sizeof (Str31) - 1, scratch[1]);
PSCat_C (name, nickname, sizeof (Str31) - 1);
if (*scratch > 1)
PMaxCatC (name, sizeof (Str31) - 1, scratch[2]);
}
ZapHandle (value1);
ZapHandle (value2);
return (name);
}
void SortAddressBook (short ab)
{
NickStructPtr nickPtr;
Str255 scratch;
long attributeOffset,
attributeLength,
valueOffset,
valueLength;
int (*compare)();
short *sortPtr,
nicknames,
nick;
// No need to sort if nothing is dirty and we already have a sort array
if (!AnyNicknamesDirty (ab) && (*Aliases)[ab].sortData && (*Aliases)[ab].theData)
return;
nicknames = (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct));
// Allocate some space for our sort array
ZapHandle ((*Aliases)[ab].sortData);
(*Aliases)[ab].sortData = NuHandle ((nicknames+1) * sizeof (short));
if (!(*Aliases)[ab].sortData)
return;
GetSortTag ();
// Fill the array with one-to-one indexes to the items in the address book and setup the sort data in our toc
LDRef (Aliases);
sortPtr = LDRef ((*Aliases)[ab].sortData);
nickPtr = LDRef ((*Aliases)[ab].theData);
for (nick = 0; nick < nicknames; ++nick) {
if (*SortTag) {
if (FindTaggedFieldValueOffsets (ab, nick, SortTag, &attributeOffset, &attributeLength, &valueOffset, &valueLength)) {
nickPtr->valueOffset = valueOffset;
nickPtr->valueLength = valueLength;
}
else {
nickPtr->valueOffset = -1;
nickPtr->valueLength = -1;
}
++nickPtr;
}
*sortPtr++ = nick;
}
*sortPtr = -1; // Quicksort needs a sentinel at the end
UL ((*Aliases)[ab].theData);
UL ((*Aliases)[ab].sortData);
UL (Aliases);
// If there is no tag, then we sort by nickname
if (!*SortTag)
compare = NickCompare;
else
compare = StringSame (SortTag, GetRString (scratch, ABReservedTagsStrn + abTagEmail)) ? EmailCompare : FieldCompare;
gSortAB = ab;
LDRef ((*Aliases)[ab].sortData);
QuickSort (*(*Aliases)[ab].sortData, sizeof (short), 0, nicknames - 1, (void*) compare,(void*) NickSwap);
UL ((*Aliases)[ab].sortData);
}
//
// GetSortTag
//
// Make the sort tag by playing around with the current View By menu selection
//
void GetSortTag (void)
{
TabObjectPtr objectPtr;
Str255 shortName;
// Figure out how the user prefers sorting
*SortTag = 0;
GetMenuItemText (PopUpMenuH (CtlViewBy), GetControlValue (CtlViewBy), shortName);
shortName[++shortName[0]] = ':';
if (objectPtr = FindObjectWithShortName (Tabs, shortName)) {
PCopy (SortTag, objectPtr->tag);
UnlockObject (objectPtr);
}
}
/************************************************************************
* NickCompare - compare two nicknames
************************************************************************/
int NickCompare (short *n1, short *n2)
{
Str255 name1,name2;
PStr s1,s2;
// The sentinel might get passed into the sort routine. If so, return as needed
if (*n1 == -1)
return (*n2 == -1 ? 0 : 1);
if (*n2 == -1)
return (-1);
s1 = GetNicknameNamePStr(gSortAB, *n1, name1);
s2 = GetNicknameNamePStr(gSortAB, *n2, name2);
// The following doesn't work. 's1' and 's2' should always have some value
// since GetNicknameNamePStr returns 'name1' or 'name2'. Instead, let's
// just let StringCompare do its thing
// if (s1) return s2 ? StringComp(s1,s2) : -1;
// else return s2 ? 1 : 0;
return (StringComp(s1,s2));
}
/************************************************************************
* NickSwap - swap two nicknames
************************************************************************/
void NickSwap (short *n1, short *n2)
{
short temp = *n2;
*n2 = *n1;
*n1 = temp;
}
/************************************************************************
* FieldCompare - compare two nicknames using tagged fields as the data
************************************************************************/
int FieldCompare (short *n1, short *n2)
{
NickStructPtr nickPtr1,
nickPtr2;
Handle notes1,
notes2;
// The sentinel might get passed into the sort routine. If so, return as needed
if (*n1 == -1)
return (*n2 == -1 ? 0 : 1);
if (*n2 == -1)
return (-1);
notes1 = GetNicknameData (gSortAB, *n1, false, true);
notes2 = GetNicknameData (gSortAB, *n2, false, true);
nickPtr1 = &(*((*Aliases)[gSortAB].theData))[*n1];
nickPtr2 = &(*((*Aliases)[gSortAB].theData))[*n2];
if (notes1 && nickPtr1->valueOffset >= 0) {
if (notes2 && nickPtr2->valueOffset >= 0)
return (strincmp (*notes1 + nickPtr1->valueOffset, *notes2 + nickPtr2->valueOffset, MIN (nickPtr1->valueLength, nickPtr2->valueLength)));
else
return (-1);
}
else {
if (notes2 && nickPtr2->valueOffset >= 0)
return (1);
else {
return NickCompare (n1, n2);
}
}
}
/************************************************************************
* EmailCompare - compare two nicknames using email addresses as the data
************************************************************************/
int EmailCompare (short *n1, short *n2)
{
Handle value1,
value2;
Size value1Size,
value2Size;
// The sentinel might get passed into the sort routine. If so, return as needed
if (*n1 == -1)
return (*n2 == -1 ? 0 : 1);
if (*n2 == -1)
return (-1);
if (value1 = GetNicknameData (gSortAB, *n1, true, true))
value1Size = GetHandleSize (value1);
if (value2 = GetNicknameData (gSortAB, *n2, true, true))
value2Size = GetHandleSize (value2);
if (value1) {
if (value2)
return (strincmp (*value1, *value2, MIN (value1Size, value2Size)));
else
return (-1);
}
else {
if (value2 && value2Size)
return (1);
else
return NickCompare (n1, n2);
}
}
void PositionPushButtons (MyWindowPtr win, short hTab1, short hTab2, short vTab5)
{
short tabWidth,
buttonHeight,
buttonWidth;
short iChatButtonWidth;
ButtonFit(CtlChat);
iChatButtonWidth = ControlWi(CtlChat) - 4*INSET; // darn giant aqua button can bite me
tabWidth = hTab2 - hTab1;
buttonHeight = ControlHi (CtlCc);
buttonWidth = (tabWidth - 2 * INSET - 2 * INSET - kHorzZoomWidth) / 4;
if (buttonWidth < iChatButtonWidth)
buttonWidth = (tabWidth - 2 * INSET - 2 * INSET - kHorzZoomWidth - iChatButtonWidth) / 3;
else
iChatButtonWidth = buttonWidth;
// horizontal zoom button
MoveMyCntl (CtlZoomHorizontal, hTab2 - kHorzZoomWidth, vTab5 + (buttonHeight - kHorzZoomWidth) / 2, kHorzZoomWidth, kHorzZoomWidth);
// To, Cc, Bcc, iChat
MoveMyCntl (CtlTo, hTab1, vTab5, buttonWidth, buttonHeight);
MoveMyCntl (CtlCc, hTab1 + buttonWidth + INSET, vTab5, buttonWidth, buttonHeight);
MoveMyCntl (CtlBcc, hTab1 + 2 * (buttonWidth + INSET), vTab5, buttonWidth, buttonHeight);
MoveMyCntl (CtlChat, hTab1 + 3 * (buttonWidth + INSET), vTab5, iChatButtonWidth, buttonHeight);
}
void BuildViewByMenu (ControlHandle tabControl, ControlHandle viewByControl)
{
MenuHandle viewByMenu;
Str255 tag,
viewByString,
title;
short index;
if (tabControl && viewByControl)
if (viewByMenu = PopUpMenuH (viewByControl)) {
SetControlValue (viewByControl, 1);
// The current view by label
GetPref (viewByString, PREF_NICK_VIEW_BY);
index = 1;
do {
GetRString (tag, ABViewByTagsStrn + index++);
if (*tag)
if (tag[1] == '-') {
SetControlMaximum (viewByControl, GetControlMaximum (viewByControl) + 1);
AppendMenu (viewByMenu, "\p(-");
}
else {
FindShortNameWithTag (tabControl, tag, title);
if (*title) {
SetControlMaximum (viewByControl, GetControlMaximum (viewByControl) + 1);
// Remove the ':' following the field label
if (title[title[0]] == ':')
--title[0];
MyAppendMenu (viewByMenu, title);
if (StringSame (viewByString, title))
SetControlValue (viewByControl, index);
}
}
} while (*tag);
}
}
/************************************************************************
* AddressBookLVCallBack - callback function for list View
************************************************************************/
long AddressBookLVCallBack (ViewListPtr pView, VLCallbackMessage message, long data)
{
VLNodeInfo *pInfo;
long fResults;
short ab,
nick;
fResults = 0;
switch (message) {
case kLVAddNodeItems:
AddNicknameListItems (pView, (VLNodeID) data);
break;
case kLVGetParentID:
fResults = ((VLNodeInfo *) data)->nodeID;
break;
case kLVOpenItem:
ABMessageTo (TO_HEAD, CurrentModifiers ());
break;
case kLVMoveItem:
case kLVCopyItem:
ABMove ((VLNodeInfo*) data, message == kLVCopyItem);
break;
case kLVDeleteItem:
// For now we won't respond to requests to delete nicknames via the Delete key. Eventually we
// need to support a protected method for deleting listview items that probably involves command-Delete
// or something.
//
// Ah! Except, this is the mechanism we use to remove items from the list view when the user has dragged an item
// to the trash. What to do, what to do... Why, overload the 'data' parameter, of course.
//
if (data == dataDeleteFromDrag || (data == dataDeleteFromKeyboard && PrefIsSet (PREF_REMOVE_NICKS_WITH_DELETE_KEY)))
ABRemove ();
break;
case kLVRenameItem:
DoRenameCallback ((StringPtr) data);
break;
case kLVGetFlags:
fResults = ListFlags;
break;
case kLVQueryItem:
pInfo = (VLNodeInfo *)data;
switch (pInfo->query) {
case kQuerySelect:
fResults = true;
break;
case kQueryDrag:
fResults = true;
break;
case kQueryDrop:
fResults = false;
GetABNick (pInfo->nodeID, &ab, &nick);
if (ValidAddressBook (ab) && !(*Aliases)[ab].ro) {
fResults = !ValidNickname (nick);
if (ValidNickname (nick) && (*((*Aliases)[ab].theData))[nick].group && PrefIsSet (PREF_NICK_DROP_ON_GROUPS))
fResults = true;
}
break;
case kQueryDropParent:
GetABNick (pInfo->nodeID, &ab, &nick);
fResults = ValidNickname (nick) && ValidAddressBook (ab) && !(*Aliases)[ab].ro;
break;
case kQueryDragExpand:
fResults = PrefIsSet (PREF_NICK_EXPAND_DRAGS);
break;
case kQueryRename:
// Only allow in list renames when the current view is By Nickname and the user pref is set -- or if the node is an address book
GetABNick (pInfo->nodeID, &ab, &nick);
if (*SortTag || (!PrefIsSet (PREF_ALLOW_NICK_RENAME_IN_LIST) && ValidNickname (nick)))
fResults = false;
else {
if (ab >= 0 && nick == kNickUndefined)
fResults = (!((*Aliases)[ab].ro || IsEudoraAddressBook (ab) || IsHistoryAddressBook (ab)));
else
fResults = !(*Aliases)[ab].ro;
}
break;
case kQueryDCOpens:
fResults = !pInfo->isParent;
break;
}
break;
case kLVAddDragItem:
fResults = ABAddDragFlavors (data);
break;
case kLVSendDragData:
fResults = ABDoSendDragData (data);
break;
case kLVExpandCollapseItem:
pInfo = (VLNodeInfo *)data;
(*Aliases)[GetAddressBook (pInfo->nodeID)].collapsed = pInfo->isCollapsed;
break;
case kLVSelectItem:
ABSelectNickname ((VLNodeID) data);
break;
case kLVUnselectItem:
ABUnselectNickname ((VLNodeID) data);
break;
}
return (fResults);
}
void DoRenameCallback (PStr newName)
{
VLNodeInfo data;
short ab,
nick;
if (LVGetItem (&NickList, 1, &data, true)) {
GetABNick (data.nodeID, &ab, &nick);
ABRename (ab, nick, newName);
}
}
Boolean RemoveAddressBook (short ab)
{
CellRec *pCellData;
VLNodeInfo data;
Cell c;
Str31 name;
OSErr theError;
short numAdressBooks,
abIndex,
nick,
count,
item,
offset,
len;
if (ComposeStdAlert (Stop, ALIAS_REMOVE_FILE_ALERT, (*Aliases)[ab].spec.name) != 1)
return (false);
// With this address book going away, we should also remove any nicknames in the list from the recipient list
// (This code was formerly misplaced _after_ the nickname file had been zapped... BOOM!)
count = (*Aliases)[ab].theData ? (GetHandleSize_ ((*Aliases)[ab].theData) / sizeof (NickStruct)) : 0;
for (nick = 0; nick < count; nick++) {
GetNicknameNamePStr (ab, nick, name);
if (IsNicknameOnRecipList (ab, nick))
RemoveFromTo (name);
TBRemoveDefunctNicknameButton (name);
}
// De-allocate underlying structures
ZapAliasFile (ab);
// Move file to trash
LDRef (Aliases);
theError = MyFSpMoveToTrash (&(*Aliases)[ab].spec);
if (theError)
theError = FSpDelete (&(*Aliases)[ab].spec);
UL (Aliases);
if (!theError) {
// Get rid of the item in our list
if (LVGetItemWithNodeID (&NickList, MakeABNodeID (ab), &data)) {
item = data.rowNum;
// This is going to get slightly nasty... We're going to run through all the remaining items that
// follow in the list view and reset all of the nodeID's to reflect an "adjusted" address book
// index. It is important that we do this since the remaining list entries now all point to one
// address book beyond...
c.h = 0;
for (--item; item < (*NickList.hList)->dataBounds.bottom; item++) {
c.v = item;
LGetCellDataLocation (&offset, &len, c, NickList.hList);
if (pCellData = len > 0 ? (CellRec *) (*(*NickList.hList)->cells + offset) : nil)
pCellData->nodeID = MakeNodeID (GetAddressBook (pCellData->nodeID) - 1, GetNickname (pCellData->nodeID));
}
}
// Shift around the data in the big hangin' nickname structure
numAdressBooks = NAliases;
if (ab != numAdressBooks - 1)
for (abIndex = ab + 1; abIndex < numAdressBooks; abIndex++)
(*Aliases)[abIndex - 1] = (*Aliases)[abIndex];
SetHandleBig_ (Aliases, (numAdressBooks - 1) * sizeof (AliasDesc));
// We may have to move the location of the History List file, too
// No longer needed since we mark the history list file
// if (HasFeature (featureNicknameWatching))
// if (gEudoraNicknamesCacheFileIndex >= 0)
// if (ab == gEudoraNicknamesCacheFileIndex)
// gEudoraNicknamesCacheFileIndex = kAddressBookUndefined;
// else
// if (ab < gEudoraNicknamesCacheFileIndex)
// --gEudoraNicknamesCacheFileIndex;
return (true);
}
else
WarnUser (ALIAS_REMOVE_NICK_FILE_ERR, theError);
return (false);
}
//
// RemoveNickname
//
// The logic within this function is sort of backwards from what
Boolean RemoveNickname (short ab, short nick)
{
VLNodeInfo data;
Boolean result;
if (result = (ab >= 0 && !(*Aliases)[ab].ro && nick >= 0))
// Just mark it deleted
if (result = LVGetItemWithNodeID (&NickList, MakeNodeID (ab, nick), &data)) {
(*((*Aliases)[ab].theData))[nick].addressesDirty = true;
(*((*Aliases)[ab].theData))[nick].deleted = true;
}
return (result);
}
//
// ParseAttributeValue
//
// Parses a range of text for an attribute/value pair, returning a pointer
// to the character following the pair (the character after the '>'), or 'nil'
// if no pair was parsed.
//
// Tagged fields have the absolute form:
//
// <tag: someSpanOfCharacters >
//
// This function does not make any attempt to validate either the tag or
// the value <20> all it cares about is finding brackets and colons, making
// no attempt to handle nesting or escaping or anything else. Therefore,
// tags can contain breaking spaces and other heinous characters.
//
// The algorithm goes something like this:
// 1. Scan until we find a '<' and save this spot
// 2. Scan until we find a ':' and save this one, too.
// 3. Scan until we find a '>' and save this spot once again
//
// IMPORTANT NOTE:
//
// It is also important to note that the pointers returned in 'attribute' and 'value' are
// generally pointers into the notes handle itself. If you dereference notes and call
// this routine to parse the data, make sure the data actually has a staic place to live!!
//
// Much safer is ParseAttributeValuePairHandle, which returns allocates its own destination
// storage, returning the data in a pair of Handles.
//
UPtr ParseAttributeValuePair (UPtr start, long length, UPtr *attribute, long *attributeLength, UPtr *value, long *valueLength)
{
UPtr end,
spot;
short bc; // bracket count
Boolean found;
found = false;
bc = 0;
spot = start;
end = start + length - 1;
do {
if (*spot == kFieldTagBegin) {
++bc;
*attribute = ++spot;
do {
if (*spot == kFieldTagSeparator) {
*attributeLength = spot - *attribute;
*value = ++spot;
do {
if (*spot == kFieldTagEnd)
if (--bc == 0) {
*valueLength = spot - *value;
found = true;
}
} while (!found && ++spot <= end);
}
} while (!found && ++spot <= end);
}
} while (!found && ++spot <= end);
return (found ? spot + 1 : nil);
}
//
// ParseAttributeValuePairHandle
//
// ParseAttributeValuePair, except putting the output into handles we allocate. Don't forget
// to dispose of the results when you're done.
//
// It's also okay to pass in nil for either the attribute or value if we don't care about
// those fields.
//
UPtr ParseAttributeValuePairHandle (UPtr start, long length, Handle *hAttribute, Handle *hValue)
{
UPtr attribute,
value,
spot;
OSErr theError;
long attributeLength,
valueLength;
if (hAttribute)
*hAttribute = nil;
if (hValue)
*hValue = nil;
if (spot = ParseAttributeValuePair (start, length, &attribute, &attributeLength, &value, &valueLength)) {
theError = noErr;
if (hAttribute)
theError = PtrToHand (attribute, hAttribute, attributeLength);
if (!theError && hValue)
theError = PtrToHand (value, hValue, valueLength);
if (theError) {
if (hAttribute)
ZapHandle (*hAttribute);
if (hValue)
ZapHandle (*hValue);
}
}
return (spot);
}
//
// ParseAttributeValuePairStr
//
// ParseAttributeValuePair, except putting the output into pascal strings
//
// It's also okay to pass in nil for either the attribute or value if we don't care about
// those fields.
//
UPtr ParseAttributeValuePairStr (UPtr start, long length, PStr attributeStr, PStr valueStr)
{
UPtr attribute,
value,
spot;
long attributeLength,
valueLength;
if (attributeStr)
*attributeStr = nil;
if (valueStr)
*valueStr = nil;
if (spot = ParseAttributeValuePair (start, length, &attribute, &attributeLength, &value, &valueLength)) {
if (attributeStr)
MakePPtr (attributeStr, attribute, attributeLength);
if (valueStr)
MakePPtr (valueStr, value, valueLength);
}
return (spot);
}
//
// AddAttributeValuePair
//
// Create an attribute value pair and append it onto a notes handle.
//
OSErr AddAttributeValuePair (Handle notes, PStr attribute, Ptr value, long length)
{
OSErr theError;
UPtr notesPtr;
Size notesSize;
theError = noErr;
// Grow the notes field to accomodate a structured <attribute:value> pair
notesSize = GetHandleSize (notes);
SetHandleBig (notes, notesSize + attribute[0] + length + 3);
theError = MemError ();
if (!theError) {
notesPtr = *notes + notesSize;
*notesPtr++ = kFieldTagBegin;
BlockMoveData (&attribute[1], notesPtr, attribute[0]);
notesPtr += attribute[0];
*notesPtr++ = kFieldTagSeparator;
BlockMoveData (value, notesPtr, length);
notesPtr += length;
*notesPtr++ = kFieldTagEnd;
}
return (theError);
}
OSErr AppendLeftoverText (AccuPtr a, UPtr from, UPtr to)
{
OSErr theError;
theError = noErr;
if (from <= to) {
while (IsSpace(*from)) // skip leading white space
++from;
if (from <= to) {
if (a->offset)
theError = AccuAddChar (a, '\015');
if (!theError)
theError = AccuAddPtr (a, from, to - from + 1);
}
}
return (theError);
}
//
// ParseAllAttributeValuePairs
//
// Parses a text handle for all attribute/value pairs, returning a handle to
// a structure containing the offset and lengths for each. We also create
// and return in 'leftovers' a handle to any text that was _not_ parsed as
// an attribute value pair.
//
AttributeValueHandle ParseAllAttributeValuePairs (Handle notes, Handle *leftovers, long requestedTypes, long unrequestedTypesToBePutInLeftovers)
{
TabFieldHandle fields;
AttributeValueHandle avPairs;
AttributeValueRec av;
Accumulator leftoversAccumulator;
OSErr theError;
Str31 tag;
UPtr notesPtr,
newPtr,
attribute,
value;
Size notesSize;
avPairs = nil;
fields = nil;
theError = AccuInit (&leftoversAccumulator);
if (!theError) {
avPairs = NuHandle (0);
theError = MemError ();
}
if (!theError)
fields = Tabs && AB.inited ? GetTabFieldStrings (Tabs, tabTagName) : GetTabFieldsFromResources (tabTagName, CREATOR, kNickTabType);
// Walk through the 'notes', looking for attribute/value pairs.
if (notes && avPairs && fields) {
notesSize = GetHandleSize (notes);
notesPtr = LDRef (notes);
// Go fish...
while (!theError && (newPtr = ParseAttributeValuePair (notesPtr, notesSize - (notesPtr - *notes), &attribute, &av.attributeLength, &value, &av.valueLength))) {
// I got one!
av.attributeOffset = attribute - *notes;
av.valueOffset = value - *notes;
// If anything appears between this tag and the previous tag, put it in leftovers
if (!theError)
theError = AppendLeftoverText (&leftoversAccumulator, notesPtr, attribute - 2);
// Make the attribute tag
MakePStr (tag, attribute, av.attributeLength);
// What type of tag did we find?
av.type = 0;
if (FindSTRNIndex (ABHiddenTagsStrn, tag))
av.type |= avPairHidden;
if (FindSTRNIndex (ABUnseachableTagsStrn, tag))
av.type = avPairUnsearchable;
if (!av.type)
av.type = FindFieldString (fields, tag) ? avPairKnown : avPairUnknown;
// Add only requested pairs to our list
if (av.type & requestedTypes)
theError = PtrPlusHand (&av, avPairs, sizeof (av));
else
// And if we've been asked, save unrequested types in leftovers (otherwise, they are ignored completely)
if (av.type & unrequestedTypesToBePutInLeftovers) {
theError = AccuAddChar (&leftoversAccumulator, '\015');
if (!theError)
theError = AccuAddPtr (&leftoversAccumulator, attribute - 1, value - attribute + av.valueLength + 2);
}
notesPtr = newPtr;
}
// Any leftovers at the end of the note?
if (!theError)
theError = AppendLeftoverText (&leftoversAccumulator, notesPtr, *notes + notesSize - 1);
UL (notes);
}
// Clean up
if (!theError) {
AccuTrim (&leftoversAccumulator);
*leftovers = leftoversAccumulator.data;
leftoversAccumulator.data = nil;
}
else
ZapHandle (avPairs);
ZapHandle (fields);
AccuZap (leftoversAccumulator);
return (avPairs);
}
short ABSetIcon (short ab, short nick)
{
Boolean isRecip;
#ifdef VCARD
if (IsPersonalAddressBook (ab) && IsVCardAvailable ())
return (PERSONAL_NICKNAME_ICON);
#endif
isRecip = IsNicknameOnRecipList (ab, nick);
if ((*((*Aliases)[ab].theData))[nick].group)
return (isRecip ? RECIPIENT_GROUP_ICON : GROUP_ICON);
return (isRecip ? RECIPIENT_NICKNAME_ICON : NICKNAME_ICON);
}
OSErr GetPhotoSpec (FSSpec *spec, short ab, short nick, Boolean *alreadyExists)
{
FSSpec nickFolder,
photoFolder,
abPhotoFolder;
Str255 photoFolderName,
nickname;
OSErr theError;
long dirID;
*alreadyExists = false;
// Find (or create) the Nicknames Folder
if (theError = SubFolderSpec (NICK_FOLDER, &nickFolder)) {
SimpleMakeFSSpec (Root.vRef, Root.dirId, GetRString (nickFolder.name, NICK_FOLDER), &nickFolder);
theError = FSpDirCreate (&nickFolder, smSystemScript, &dirID);
}
if (!theError) {
IsAlias (&nickFolder, &nickFolder);
theError = FSMakeFSSpec (nickFolder.vRefNum, SpecDirId (&nickFolder), GetRString (photoFolderName, PHOTO_FOLDER), &photoFolder);
}
// Create the Photo Album Folder
if (theError == fnfErr)
theError = FSpDirCreate (&photoFolder, smSystemScript, &dirID);
if (!theError) {
IsAlias (&photoFolder, &photoFolder);
// Create a folder inside the Photo Album for the current address book
theError = FSMakeFSSpec (photoFolder.vRefNum, SpecDirId (&photoFolder), (*Aliases)[ab].spec.name, &abPhotoFolder);
if (theError == fnfErr)
theError = FSpDirCreate (&abPhotoFolder, smSystemScript, &dirID);
}
// Make an FSSpec for the nickname itself
if (!theError) {
IsAlias (&abPhotoFolder, &abPhotoFolder);
theError = FSMakeFSSpec (abPhotoFolder.vRefNum, SpecDirId (&abPhotoFolder), GetNicknameNamePStr (ab, nick, nickname), spec);
if (theError == fnfErr)
theError = noErr;
else {
*alreadyExists = true;
IsAlias (spec, spec);
}
}
return (theError);
}
//
// Old scary crufty crap appears below... (not all that related to the address book window)
//
/************************************************************************
* NewNick - create a new alias for the user
************************************************************************/
#ifdef VCARD
void NewNick(Handle addresses,FSSpec *vcardSpec,short which)
{
AskNickname (addresses, vcardSpec, which); // Wow... this routine sure did get stripped down...
}
#else
void NewNick(Handle addresses,short which)
{
AskNickname (addresses, which); // Wow... this routine sure did get stripped down...
}
#endif
/************************************************************************
* NewNickLow - Acually create a new alias for the user
************************************************************************/
short NewNickLow(Handle addresses,Handle notes,short which,Str63 name,Boolean makeRecip,NickReplaceEnum nre,Boolean doSave)
{
return AddNickname(addresses,notes,which,name,makeRecip,nre,doSave);
}
/************************************************************************
* AddNickname - Create a new alias for the user
*
* Note!! No doubt there are still a lot of Gruby-ized conditions in this routine, but -- in general --
* The 'addresses' and 'notes' passed to this routine are COPIED into new handles which are then
* placed into the TOC. Therefore, it is safe for calling routines to zap the handles they pass.
*
************************************************************************/
short AddNickname(Handle addresses,Handle notes,short which,Str63 name,Boolean makeRecip,NickReplaceEnum nre,Boolean doSave)
{
short index;
long addrSize;
Handle tempHandle = nil,temp2Handle = nil;
long totalNicks;
short realCount,i;
short nickCollision;
Boolean replacing = false;
Byte comma = ',';
OSErr err = noErr;
Boolean notesExist;
RegenerateAllAliases(false);
if ((*Aliases)[which].ro) which = FindAddressBookType (eudoraAddressBook);
totalNicks = (*Aliases)[which].theData ? (GetHandleSize_((*Aliases)[which].theData)/sizeof(NickStruct)) : 0;
realCount = 0;
for (i=0;i<totalNicks;i++)
{
if (!(*((*Aliases)[which].theData))[i].deleted)
realCount++;
}
//addrSize = incomingAddresses ? GetHandleSize_(incomingAddresses) : 0;
//if (addrSize)
// SuckAddresses(&addresses,incomingAddresses,True,True,False,nil);
addrSize = addresses ? GetHandleSize_(addresses) : 0;
if (addrSize) FlattenListWith(addresses,',');
*name = MIN (*name, sizeof (Str31) - 1);
index = NickMatchFound((*Aliases)[which].theData,NickHash(name),name,which);
if (index >= 0)
{
if (nre == nrDifferent) {
nickCollision = NickRepAlert(name);
// If it still thinks it is different, cancel was really hit
if (nickCollision == nrDifferent)
return(-1);
}
else
nickCollision = (short) nre;
// 2/2/01 (jp) <20> If we are replacing or adding to an existing nickname that is currently selected, unselect before changing anything.
if (AB.inited)
if (which == AB.abDisplayed && index == AB.nickDisplayed && (nickCollision == nrReplace || nickCollision == nrAdd)) {
Str31 massagedName;
GetListNameBasedOnTag (SortTag, which, index, massagedName);
LVUnselect (&NickList, MakeNodeID (which, index), massagedName, true);
}
switch (nickCollision)
{
case nrDifferent:
#ifdef VCARD
nre = AskNickname(nil, nil, which);
#else
nre = AskNickname(nil, which);
#endif
if (nre == nrCancel)
return(-1);
break;
case nrReplace:
replacing = true;
break;
case nrAdd:
replacing = true;
// Get addresses
// Append addresses
tempHandle = GetNicknameData(which,index,true,true);
if (tempHandle)
{
temp2Handle = NuHandle(0);
if (!temp2Handle)
{WarnUser(ALIAS_NEW_NICK_ERR,MemError());return(-1);}
if (err = HandPlusHand(tempHandle,temp2Handle)) break;
if (err=PtrPlusHand(&comma,temp2Handle,1)) break;
if (err=HandPlusHand(addresses,temp2Handle)) break;
addresses = temp2Handle;
temp2Handle = nil;
}
if (notes)
ZapHandle(notes);
// Get notes
// Copy notes
temp2Handle = GetNicknameData(which,index,false,true);
if (temp2Handle)
{
notes = NuHandle(0);
if (!notes)
{WarnUser(ALIAS_NEW_NICK_ERR,MemError());return(-1);}
if (err = HandPlusHand(temp2Handle,notes)) break;
}
break;
}
}
if (addresses) NickUniq(addresses,"\p,",True);
if (makeRecip) AddStringAsTo(name);
SetAliasDirty(which);
// Both of these routines will make a copy of the addresses
if (replacing)
ReplaceNicknameAddresses(which,name,addresses);
else
AddNickToTOCfromName(which,name,addresses);
// When adding a new nickname, we'll add the 'added' change bit to the notes
notesExist = notes ? true : false;
if (!replacing && PrefIsSet (PREF_CHANGE_BITS_FOR_CONDUIT)) {
if (!notes)
notes = NuHandle (0);
if (notes) {
Str255 scratch,
idTag;
SetNicknameChangeBit (notes, changeBitAdded, true);
// Generate a unique ID for this nickname and add it to the notes
NumToString (NickGenerateUniqueID(), scratch);
SetTaggedFieldValueInNotes (notes, GetRString (idTag, ABHiddenTagsStrn + abTagUniqueID), &scratch[1], *scratch, nickFieldReplaceExisting, 0, nil);
}
}
// This routine will make a copy of the notes
if (notes)
ReplaceNicknameNotes(which,name,notes);
if (!notesExist)
ZapHandle (notes);
if (doSave)
SaveAliases(true);
return (index >= 0 ? index : NickMatchFound((*Aliases)[which].theData,NickHash(name),name,which));
}
/************************************************************************
* CanMakeNick - should we enable the "make nickname" item?
************************************************************************/
Boolean CanMakeNick(void)
{
return(AB.inited&&((LVCountSelection(&NickList)>1&&FrontWindow_()==GetMyWindowWindowPtr(Win))||HasNickScanCapability (Win->pte)) || PhCanMakeNick());
}
Boolean NickUserFieldExists(PStr theField)
{
short numberOfUserFields;
short i;
Str31 tempField;
fieldInfo **userFieldsInfo;
OSErr err = noErr;
short reqFields,optFields;
long height,width;
Str255 scratch;
Str31 fieldName;
/*
* Determine number of extra fields
*/
#ifdef ONE
reqFields = 1;
optFields = 0;
#else
reqFields = CountStrn(NickManLabelStrn);
optFields = CountStrn(NickOptLabelStrn);
#endif
numberOfUserFields = reqFields + optFields;
userFieldsInfo = NuHandle(sizeof(fieldInfo) * (reqFields + optFields));
if (!userFieldsInfo)
{
err = MemError();
WarnUser(MEM_ERR,err);
return (err);
}
for (i=0;i<numberOfUserFields;i++)
{
if (i < reqFields)
// Read the resource
GetRString(scratch,NickManLabelStrn+i+1);
else
// Read the resource
GetRString(scratch,NickOptLabelStrn + (i - reqFields) + 1);
// Parse resource for field name, height, and width
NickParseForInfo(scratch,&height,&width,fieldName);
(*userFieldsInfo)[i].width = width;
(*userFieldsInfo)[i].height = height;
PCopy((*userFieldsInfo)[i].name,fieldName);
if (i < reqFields)
// Read in tags;
GetRString(scratch,NickManKeyStrn+i+1);
else
// Read in tags;
GetRString(scratch,NickOptKeyStrn + (i - reqFields) + 1);
PCopy((*userFieldsInfo)[i].tag,scratch);
}
for (i=0;i<numberOfUserFields;i++)
{
PCopy(tempField,(*userFieldsInfo)[i].name);
tempField[0]--;
if (StringSame(tempField,theField))
{
if (userFieldsInfo)
ZapHandle(userFieldsInfo);
return (true);
}
}
if (userFieldsInfo)
ZapHandle(userFieldsInfo);
return (false);
}
Boolean IsNicknameOnRecipList(short which,short index)
{
Str63 alias;
// PCopy(alias,*((*((*Aliases)[which].theData))[index].theName));
GetNicknameNamePStr(which,index,alias);
return (IsRecip(alias));
}
Boolean IsNicknamePrivate (short ab, short nick)
{
return (GetNicknameChangeBits (GetNicknameData (ab, nick, false, true)) & changeBitPrivate ? true : false);
}
void AESaveCurrentAlias(short which,short index)
{
short ab,
nick;
if (!AB.inited)
return;
GetSelectedABNick (1, &ab, &nick);
if (nick == index && ab == which)
ReplaceNicknameData (Tabs, ab, nick);
}
void ForceSelectedAliasUpdate(short which,short index,Boolean didDirty)
{
SetAliasDirty(which);
if (!AB.inited)
SaveAliases(true);
}
/************************************************************************
* NickGetDataFromField - Get some data from a field in the notes
* If doingDrag, returns true to indicate that there is no data (i.e. don't copy into clipping)
* otherwise, the nickname in <20><> is returned in theData
************************************************************************/
OSErr NickGetDataFromField(UPtr theField,UPtr sViewData,short which,short nickNum,Boolean doingDrag,Boolean doingAE,Boolean *nickNameEmpty)
{
Str63 tempStr, anotherTemp;
char CFieldName[31],tempFieldName[31];
Handle theNotes,temp2Handle = nil;
char *tagPtr = nil;
char *dataPtr = nil;
char *endPtr = nil;
short i;
Boolean returnValue = false;
short length;
long offset;
long count;
short bracketCount = 0;
Str255 scratch;
*sViewData = 0;
*tempStr = 0;
BlockMoveData(theField + 1,CFieldName,*theField);
CFieldName[*theField] = 0;
strcpy(tempFieldName,"<");
strcat(tempFieldName,CFieldName);
strcpy(CFieldName,tempFieldName);
*nickNameEmpty = false;
theNotes = GetNicknameData(which,nickNum,false,true);
if (theNotes && *theNotes)
{
offset = SearchPtrPtr(CFieldName,strlen(CFieldName),LDRef(theNotes),0,GetHandleSize_(theNotes),false,false,nil); // Find field tag in notes
if (offset >= 0)
tagPtr = LDRef(theNotes) + offset;
if (tagPtr)
dataPtr = tagPtr + *theField + 1;
if ( dataPtr && *dataPtr == ':' ) dataPtr++;
if (dataPtr)
endPtr = strchr(dataPtr,'>');
length = endPtr - dataPtr;
for (count = 0;count<length;count++)
{
if (dataPtr[count] == '<')
bracketCount++;
}
for (;bracketCount;bracketCount--)
{
endPtr = strchr(endPtr + 1,'>');
}
}
else
returnValue = true;
if ((tagPtr && dataPtr && endPtr) || doingDrag)
{ // Copy from end of tag to end of data
if (doingDrag)
{
if (theNotes == nil || *theNotes == nil || tagPtr == nil)
{
returnValue = true;
UL(theNotes);
*nickNameEmpty = true;
return (noErr);
}
length = endPtr - dataPtr;
if (length > 63)
length = 63;
BlockMoveData(dataPtr,sViewData+1,length);
*sViewData = length;
UL(theNotes);
return(noErr);
}
else
{
length = endPtr - dataPtr;
MakePStr(tempStr,dataPtr,length);
// if field is not name, add nickname
if (!EqualStrRes(theField,NickManKeyStrn+1) && !doingAE)
{
GetNicknameNamePStr(which,nickNum,anotherTemp);
ComposeRString(scratch,NICK_VIEW_ANNOTATE,tempStr,anotherTemp);
PSCopy(tempStr,scratch);
}
}
}
else if (!doingDrag && !doingAE)
{
PCopy(tempStr,"\p<EFBFBD>");
// PCat(tempStr,*((*((*Aliases)[which].theData))[nickNum].theName));
GetNicknameNamePStr(which,nickNum,anotherTemp);
PSCat(tempStr,anotherTemp);
PSCatC(tempStr,'<EFBFBD>');
returnValue = true;
}
// (jp) 12-19-99 When we're getting field data from an AppleEvent we're going to
// replace the ETX characters with CR's, otherwise replace with spaces
// as before
for (i=1;i<=*tempStr;i++) // Strip out illegal characters
if (tempStr[i]=='\003')
tempStr[i] = doingAE ? '\015' : ' ';
PCopy(sViewData,tempStr);
if (theNotes) UL(theNotes);
*nickNameEmpty = returnValue;
return (noErr);
}
/************************************************************************
* Makes a new nickname file placing it in the correct spot in the nickname list
************************************************************************/
short NickMakeNewFile (Str63 name)
{
AliasDesc ad;
FSSpec folderSpec;
OSErr theError;
long dirID;
short ab,
i,
totalAB;
Str63 abName;
// Does it already exist?
for (ab = NAliases-1; ab>=0; ab--)
{
PCopy(abName,(*Aliases)[ab].spec.name);
if (StringSame(abName,name)) return ab;
}
ab = kAddressBookUndefined;
// Create an address book record
Zero (ad);
ad.type = regularAddressBook;
ad.hNames = NuHandle (0);
ad.theData = NuHandle (0);
theError = MemError ();
if (!theError) {
HNoPurge (ad.theData);
theError = MemError ();
}
// Create the nickname file
if (!theError) {
if (!SubFolderSpec (NICK_FOLDER, &folderSpec))
SimpleMakeFSSpec (folderSpec.vRefNum, folderSpec.parID, name, &ad.spec);
else { // If not, create one and then create our file spec
SimpleMakeFSSpec (Root.vRef, Root.dirId, GetRString (folderSpec.name, NICK_FOLDER),&folderSpec);
theError = FSpDirCreate (&folderSpec, smSystemScript, &dirID);
if (!theError) {
SubFolderSpec (NICK_FOLDER, &folderSpec);
SimpleMakeFSSpec (folderSpec.vRefNum, folderSpec.parID, name, &ad.spec);
}
}
if (!theError)
theError = FSpCreate (&ad.spec, CREATOR, MAILBOX_TYPE, smSystemScript);
if (theError)
FileSystemError (SAVE_ALIAS, ad.spec.name, theError);
}
// Find its position in the nickname file list.
// This position should be after the Personal, Eudora and History
// address books, but before the Plugin address books:
// Personal Nicknames
// Eudora Nicknames
// Regular Nickname Files
// History List
// Plugin Nickname Files
if (!theError) {
totalAB = NAliases;
i = 0;
#ifdef VCARD
// Skip the personal address book
if (IsPersonalAddressBook (i))
++i;
#endif
// Skip the eudora address book
if (IsEudoraAddressBook (i))
++i;
// Skip to the end of the regular address books
while (i < totalAB && IsRegularAddressBook (i))
++i;
// 'i' should now be pointing to where in the list we wish to insert the nick file record
Munger (Aliases, i * sizeof (AliasDesc), nil, 0, &ad, sizeof (AliasDesc));
theError = MemError ();
if (theError)
WarnUser (ALIAS_NEW_NICK_FILE_ERR, theError);
else
{
ab = i;
ABTickleHardEnoughToMakeYouPuke();
}
}
// Clean up...
if (theError) {
ZapHandle (ad.hNames);
ZapHandle (ad.theData);
}
return (ab);
}
/************************************************************************
* AERemoveNick - get rid of a nickname via AppleEvents
************************************************************************/
Boolean AERemoveNick(short index,short which)
{
Str63 name;
short numOfAliasFiles = NAliases;
OSErr theErr = noErr;
if (index < 0) // Can't remove a nickname file
{
#ifdef VCARD
if (IsEudoraAddressBook(which) || IsPersonalAddressBook(which))
#else
if (IsEudoraAddressBook(which))
#endif
return (false);
// De-allocate underlying structures
ZapAliasFile(which);
// Move file to trash
theErr = MyFSpMoveToTrash(&(*Aliases)[which].spec);
if (theErr)
FSpDelete(&(*Aliases)[which].spec);
return (true);
}
// PCopy(name,*((*((*Aliases)[which].theData))[index].theName));
GetNicknameNamePStr(which,index,name);
if (IsNicknameOnRecipList(which,index)) RemoveFromTo(name);
RemoveNamedNickname(which,name);
TBRemoveDefunctNicknameButton (name);
SetAliasDirty(which);
RemoveNickFromAddressBookList (which, index, true);
return (true);
}
/************************************************************************
* NickWinIsOpen - is the Address Book window open?
************************************************************************/
Boolean NickWinIsOpen(void)
{
return AB.inited && GetWindowKind(GetMyWindowWindowPtr(Win)) == ALIAS_WIN &&
IsWindowVisible(GetMyWindowWindowPtr(Win));
}
/************************************************************************
* AliasWinIsOpen - is the alias window open?
************************************************************************/
Boolean AliasWinIsOpen(void)
{
return(AB.inited!=nil);
}
void RemoveNickFromAddressBookList (short which, short index, Boolean doSave)
{
if (AB.inited)
RemoveNickname (which, index);
else
if (doSave)
SaveAliases(true);
}
/**********************************************************************
* AliasWinRefresh - refresh the current nickname
**********************************************************************/
void AliasWinRefresh(void)
{
}
//
// VanquishRecipientList
//
// This code used to be in Alias close. I put it here instead and
// rearranged things a little
//
void VanquishRecipientList (Boolean discardChanges)
{
if (OldRecipientMenu)
{
if (discardChanges)
{
// Need to restore recipient list
SetupRecipMenusWith(OldRecipientMenu);
ToMenusChanged();
}
// Dispose of copy of recipient list
ZapHandle (OldRecipientMenu);
}
}
//
// SaveExpandedAddressBookNames
//
// This code used to be in Alias close. I put it here instead and
// rearranged things a little. We could make this routine a lot
// simpler by just storing a state boolean in the resource fork of
// the address book itself -- but that would be trouble for anyone
// planning to store their address book files on a shared server.
//
// Also, there used to be logic to default empty address books to
// closed, even if the user left the address book open. Seems
// valid for the user to choose to do this, so I'm axing this code.
//
// We also used to do a big handle compare to see if the list had
// changed. Honestly, though, these days of faster CPUs and fast
// disk access makes this check unnecessary -- so we'll just copy
// out the list regardless.
//
void SaveExpandedAddressBookNames (void)
{
Handle dataH;
OSErr theError;
short numABs,
ab,
count;
count = 0;
numABs = NAliases;
// Make space for the count, which we store in a short
dataH = NuHandle (sizeof (short));
theError = MemError ();
for (ab = 0; !theError && ab < numABs; ab++)
if ((*Aliases)[ab].collapsed) {
++count;
theError = PtrPlusHand_ ((*Aliases)[ab].spec.name, dataH, *(*Aliases)[ab].spec.name + 1);
}
if (!theError) {
*(short*)(*dataH) = count;
ZapSettingsResource ('STR#', NickFileCollapseStrn);
AddMyResource_ (dataH,'STR#', NickFileCollapseStrn, "");
theError = ResError ();
}
if (!theError)
MyUpdateResFile (SettingsRefN);
if (theError)
ZapHandle (dataH);
}
/************************************************************************
* NickRepAlert - nickname replacement alert, with vetted cancel return
************************************************************************/
short NickRepAlert(PStr candidate)
{
short err = AlertStr(NICK_REP_ALRT,Note,candidate);
if (err==userCanceledErr || err==CANCEL_ITEM) err = nrDifferent;
return(err);
}
/*
* Takes a string of the form: Field Title:Height,Width and parses out the info.
*/
void NickParseForInfo(Str255 input,long *height,long *width,Str255 fieldName)
{
Str31 token;
UPtr spot;
short i;
spot = input+1;
PToken(input,token,&spot,":");
StringToNum(PToken(input,token,&spot,","),height); // Get num of lines for field
/*
* Get width for field: 1 for full width, 2 for half width (currently not used)
*/
StringToNum(PToken(input,token,&spot,","),width);
for (i=1;i<= *input;i++)
{
if (input[i]==':')
break;
}
PCopy(fieldName,input);
fieldName[0]=i;
}
/************************************************************************
* GetCurrentName - get the currently selected nickname
************************************************************************/
void GetName (short i,short *nickNum,short *whichFile)
{
if (i<0)
{
*nickNum = -1;
*whichFile = -1;
return;
}
else
if (GetSelectedABNick (i, whichFile, nickNum))
;
}
OSErr MyFSpMoveToTrash(const FSSpec *spec)
{
short theVRef;
long theDirID;
FSSpec trashSpec,tempSpec,uniqueSpec;
OSErr theErr = noErr;
// Move file to trash
FindFolder(spec->vRefNum,kTrashFolderType,false,&theVRef,&theDirID);
FSMakeFSSpec(theVRef,theDirID,"\p",&trashSpec);
theErr = FSpCatMove(spec,&trashSpec);
// If we don't have a problem, just leave.
if (!theErr)
return (noErr);
// If the file already exists in the trash, load up our tempSpec
if (theErr == dupFNErr)
FSMakeFSSpec(theVRef,theDirID,spec->name,&tempSpec);
uniqueSpec = tempSpec;
theErr = UniqueSpec(&uniqueSpec,27);
if (!theErr)
theErr = FSpRename(&tempSpec,uniqueSpec.name);
// We've successfully renamed the file in the trash, now we have to really trash our file
if (theErr == noErr)
{
FindFolder(spec->vRefNum,kTrashFolderType,false,&theVRef,&theDirID);
FSMakeFSSpec(theVRef,theDirID,"\p",&trashSpec);
theErr = FSpCatMove(spec,&trashSpec);
}
return (theErr);
}
/**********************************************************************
* TopCompositionWindow - topmost composition window, not counting current window
**********************************************************************/
MyWindowPtr TopCompositionWindow(Boolean skipCurrent, Boolean dontCreate)
{
WindowPtr winWP = FrontWindow_();
MyWindowPtr win;
if (skipCurrent) {
while (winWP && (IsFloating(winWP) || !IsWindowVisible(winWP) || GetWindowKind(winWP)==FIND_WIN)) winWP = GetNextWindow(winWP);
winWP = GetNextWindow(winWP);
}
while (winWP && (IsFloating(winWP) || !IsWindowVisible(winWP) || GetWindowKind(winWP)==FIND_WIN)) winWP = GetNextWindow(winWP);
win = GetWindowMyWindowPtr(winWP);
if (!IsKnownWindowMyWindow(winWP) || GetWindowKind(winWP)!=COMP_WIN ||
SumOf(Win2MessH(win))->state==SENT || !IsWindowVisible(winWP) ||
SumOf(Win2MessH(win))->state==BUSY_SENDING)
{
if (dontCreate)
return (nil);
win = DoComposeNew(0);
#ifdef TWO
if (win)
{
ApplyDefaultStationery(win,True,True);
UpdateSum(Win2MessH(win),SumOf(Win2MessH(win))->offset,SumOf(Win2MessH(win))->length);
}
#endif
}
return(win);
}
/************************************************************************
*
************************************************************************/
void AliasesFixFont(void)
{
#ifdef BETA
BIG UGLY ERROR
#endif
}
/************************************************************************
* AskNickname - ask the user for a new nickname.
************************************************************************/
#ifdef VCARD
NickReplaceEnum AskNickname (Handle addresses, FSSpec *vcardSpec, short ab)
#else
NickReplaceEnum AskNickname (Handle addresses, short ab)
#endif
{
NickReplaceEnum nre;
Str255 nickname,
fullName;
SAVE_PORT;
nre = nrNone;
// Figure out the default names from the addresses
*nickname = 0;
*fullName = 0;
if (addresses)
{
UniqBinAddr(addresses);
SortBinAddr(addresses);
NickSuggest (addresses, nickname, fullName, false, 0);
}
if (!*nickname)
MakeUniqueNickname (ab, nickname);
// Truncate the size of the nickname if it is too beefy
*nickname = MIN (*nickname, sizeof (Str31) - 1);
// Display either the name or group dialog boxes depending on whether or not we have a real name
if (CountAddresses (addresses, 2) < 2)
#ifdef VCARD
nre = DoNicknameDialog (addresses, vcardSpec, ab, nickname, fullName);
#else
nre = DoNicknameDialog (addresses, ab, nickname, fullName);
#endif
else
nre = DoGroupNicknameDialog (addresses, ab, nickname, fullName);
REST_PORT;
return (nre);
}
#ifdef VCARD
NickReplaceEnum DoNicknameDialog (Handle addresses, FSSpec *vcardSpec, short ab, PStr nickname, PStr fullName)
#else
NickReplaceEnum DoNicknameDialog (Handle addresses, short ab, PStr nickname, PStr fullName)
#endif
{
DialogPtr dgPtr;
MyWindowPtr dgPtrWin;
NickReplaceEnum nre;
PETEDocInitInfo pdi;
MakeAddressBookEntryHitType mabeHit;
Handle shortAddress,
expansion,
notes;
Str255 workingName,
firstName,
lastName;
OSErr theError;
short *nickFileIndexArray,
nick,
item;
Boolean needFile,
makeRecip,
done;
extern ModalFilterUPP DlgFilterUPP;
theError = noErr;
nre = nrAdd;
ParseFirstLast (fullName, firstName, lastName);
needFile = true;
nickFileIndexArray = GetNickFileArray (&needFile);
SetDialogFont (SmallSysFontID ());
dgPtrWin = GetNewMyDialog (NEW_NICK_DLOG, nil, nil, InFront);
dgPtr = GetMyWindowDialogPtr (dgPtrWin);
SetDialogFont (0);
if (!dgPtrWin || !dgPtr)
theError = userCanceledErr;
//fix the controls and setup all the PETE user panes
if (!theError) {
SetPort_ (GetWindowPort (GetDialogWindow (dgPtr)));
ConfigFontSetup (dgPtrWin);
ReplaceAllControls (dgPtr);
DefaultPII (dgPtrWin, false, peNoStyledPaste | peClearAllReturns, &pdi);
theError = CreatePeteUserPaneWithQDTextAndScanning (dgPtr, NEW_NICK_NAME, peNoStyledPaste | peClearAllReturns, &pdi, true, nickHighlight | nickComplete | nickSpaces, 0);
}
if (!theError)
theError = CreatePeteUserPaneWithQDTextAndScanning (dgPtr, NEW_NICK_FULL_NAME, peNoStyledPaste | peClearAllReturns, &pdi, true, nickNoScan, 0);
if (!theError)
theError = CreatePeteUserPaneWithQDTextAndScanning (dgPtr, NEW_NICK_FIRST_NAME, peNoStyledPaste | peClearAllReturns, &pdi, true, nickNoScan, 0);
if (!theError)
theError = CreatePeteUserPaneWithQDTextAndScanning (dgPtr, NEW_NICK_LAST_NAME, peNoStyledPaste | peClearAllReturns, &pdi, true, nickNoScan, 0);
if (!theError)
theError = CreatePeteUserPaneWithQDTextAndScanning (dgPtr, NEW_NICK_ADDRESS, peNoStyledPaste | peClearAllReturns, &pdi, true, nickHighlight | nickComplete | nickSpaces, kNickScanAllAliasFiles);
// Set the initial text for each PETE user pane
if (!theError) {
dgPtrWin->pte = GetPeteDItem (dgPtrWin, NEW_NICK_NAME);
(void) SetKeyboardFocus (GetDialogWindow (dgPtr), GetDItemCtl (dgPtr, NEW_NICK_NAME), kControlEditTextPart);
SetPeteDItemText (dgPtrWin, NEW_NICK_NAME, nickname);
SetPeteDItemText (dgPtrWin, NEW_NICK_FULL_NAME, fullName);
SetPeteDItemText (dgPtrWin, NEW_NICK_FIRST_NAME, firstName);
SetPeteDItemText (dgPtrWin, NEW_NICK_LAST_NAME, lastName);
if (addresses) {
shortAddress = nil;
SuckPtrAddresses (&shortAddress, LDRef (addresses) + 1, **addresses, false, True, False, nil);
UL (addresses);
if (shortAddress && *shortAddress)
PeteSetString (*shortAddress, GetPeteDItem (dgPtrWin, NEW_NICK_ADDRESS));
ZapHandle(shortAddress);
}
// Fiddle with the rest of the controls
SetDItemState (dgPtr, NEW_NICK_RECIP, false);
if (!HasFeature (featureMultipleNicknameFiles)) {
HideDialogItem (dgPtr, NEW_NICK_IN_ADDRESS);
needFile = false;
}
BuildAddressBookMenu (dgPtr, NEW_NICK_FILE, nickFileIndexArray, needFile, &ab);
SetAliasFileToScan (GetPeteDItem (dgPtrWin, NEW_NICK_NAME), ab);
SetAliasFileToScan (GetPeteDItem (dgPtrWin, NEW_NICK_ADDRESS), ab);
HiliteButtonOne (dgPtr);
PeteSelectAll (dgPtrWin, dgPtrWin->pte);
}
// Run the dialog
if (!theError) {
StartMovableModal (dgPtr);
ShowWindow (GetDialogWindow (dgPtr));
done = false;
while (!done) {
SetGreyControl (GetDItemCtl (dgPtr, NEW_NICK_ADD_DETAILS), !PeteLen (GetPeteDItem (dgPtrWin, NEW_NICK_NAME)) || !PeteLen (GetPeteDItem (dgPtrWin, NEW_NICK_ADDRESS)));
SetGreyControl (GetDItemCtl (dgPtr, NEW_NICK_OK), !PeteLen (GetPeteDItem (dgPtrWin, NEW_NICK_NAME)) || !PeteLen (GetPeteDItem (dgPtrWin, NEW_NICK_ADDRESS)));
CommandPeriod = false;
MovableModalDialog (dgPtr, DlgFilterUPP, &item);
switch (item) {
case NEW_NICK_RECIP:
SetDItemState (dgPtr, NEW_NICK_RECIP, !GetDItemState (dgPtr, NEW_NICK_RECIP));
break;
case NEW_NICK_FILE:
HitAddressBookMenu (dgPtrWin, NEW_NICK_FILE, nickFileIndexArray, needFile);
break;
case NEW_NICK_SWAP_NAMES:
GetPeteDItemText (dgPtrWin, NEW_NICK_FIRST_NAME, firstName);
GetPeteDItemText (dgPtrWin, NEW_NICK_LAST_NAME, lastName);
SetPeteDItemText (dgPtrWin, NEW_NICK_FIRST_NAME, lastName);
SetPeteDItemText (dgPtrWin, NEW_NICK_LAST_NAME, firstName);
break;
}
GetPeteDItemText (dgPtrWin, NEW_NICK_NAME, workingName);
ab = HasFeature (featureMultipleNicknameFiles) ? nickFileIndexArray[GetDItemState(dgPtr, NEW_NICK_FILE) - 1] : 0;
if (item == CANCEL_ITEM || item == NEW_NICK_CANCEL || item == NEW_NICK_OK || item == NEW_NICK_ADD_DETAILS)
done = true;
if ((item == NEW_NICK_OK || item == NEW_NICK_ADD_DETAILS) && (nre = BadNickname (workingName, nickname, ab, -1, addresses==nil, true, false)) == nrDifferent) {
PeteSelectAll (dgPtrWin, dgPtrWin->pte);
done = false;
}
}
mabeHit = mabeHitOther;
if (item == NEW_NICK_OK || item == NEW_NICK_ADD_DETAILS) {
Handle typedAddresses;
mabeHit = item == NEW_NICK_OK ? mabeHitOK : mabeHitAddDetails;
// Grab the info from the typed fields
makeRecip = GetDItemState(dgPtr,NEW_NICK_RECIP);
GetPeteDItemText (dgPtrWin, NEW_NICK_NAME, nickname);
GetPeteDItemText (dgPtrWin, NEW_NICK_FULL_NAME, fullName);
GetPeteDItemText (dgPtrWin, NEW_NICK_FIRST_NAME, firstName);
GetPeteDItemText (dgPtrWin, NEW_NICK_LAST_NAME, lastName);
// Grab the typed addresses
typedAddresses = GetPeteDItemTextH (dgPtrWin, NEW_NICK_ADDRESS);
if (typedAddresses) {
Tr (typedAddresses, "\015", ",");
theError = SuckPtrAddresses (&expansion, LDRef(typedAddresses),GetHandleSize (typedAddresses),true,true,false,nil);
}
ZapHandle (typedAddresses);
// Make a notes handle
#ifdef VCARD
if (!theError)
/* if (vcardSpec && HasFeature (featureVCard)) {
ZapHandle (expansion);
theError = NicknameFromVCardFile (vcardSpec, &expansion, &notes);
}
else */
notes = CreateSimpleNotes (fullName, firstName, lastName);
if (!theError)
nick = NewNickLow (expansion, notes, ab, nickname, makeRecip, nre, AB.inited ? false : true);
#else
if (!theError) {
notes = CreateSimpleNotes (fullName, firstName, lastName);
nick = NewNickLow (expansion, notes, ab, nickname, makeRecip, nre, AB.inited ? false : true);
}
#endif
}
DisposePeteUserPaneItem (dgPtrWin, NEW_NICK_NAME);
DisposePeteUserPaneItem (dgPtrWin, NEW_NICK_FULL_NAME);
DisposePeteUserPaneItem (dgPtrWin, NEW_NICK_FIRST_NAME);
DisposePeteUserPaneItem (dgPtrWin, NEW_NICK_LAST_NAME);
DisposePeteUserPaneItem (dgPtrWin, NEW_NICK_ADDRESS);
EndMovableModal(dgPtr);
DisposDialog_(dgPtr);
if (!theError)
{
if (AB.inited)
UpdateMyWindow(GetMyWindowWindowPtr(Win)); // redisplay AB in case dialog was on top
PostMakeNicknameDialog (ab, nick, mabeHit);
}
}
ZapPtr (nickFileIndexArray);
if (theError)
WarnUser (ALIAS_NEW_NICK_ERR, theError);
return (item == NEW_NICK_OK || item == NEW_NICK_ADD_DETAILS ? nre : nrCancel);
}
//
// CreateSimpleNotes
//
// Create a simple notes handle containing only a full, first and last name.
//
Handle CreateSimpleNotes (PStr fullName, PStr firstName, PStr lastName)
{
Str255 tag;
Handle notes;
OSErr theError;
notes = NuHandle (0);
theError = MemError ();
if (!theError && *fullName)
theError = AddAttributeValuePair (notes, GetRString (tag, ABReservedTagsStrn + abTagName), &fullName[1], *fullName);
if (!theError && *firstName)
theError = AddAttributeValuePair (notes, GetRString (tag, ABReservedTagsStrn + abTagFirst), &firstName[1], *firstName);
if (!theError && *lastName)
theError = AddAttributeValuePair (notes, GetRString (tag, ABReservedTagsStrn + abTagLast), &lastName[1], *lastName);
if (theError)
ZapHandle (notes);
return (notes);
}
NickReplaceEnum DoGroupNicknameDialog (Handle addresses, short ab, PStr nickname, PStr fullName)
{
DialogPtr dgPtr;
MyWindowPtr dgPtrWin;
NickReplaceEnum nre;
PETEDocInitInfo pdi;
MakeAddressBookEntryHitType mabeHit;
Handle expansion,
notes;
Str255 workingName,
tag;
OSErr theError;
short *nickFileIndexArray,
nick,
item;
Boolean needFile,
makeRecip,
done;
extern ModalFilterUPP DlgFilterUPP;
theError = noErr;
nre = nrAdd;
needFile = true;
nickFileIndexArray = GetNickFileArray (&needFile);
SetDialogFont (SmallSysFontID ());
dgPtrWin = GetNewMyDialog (NEW_GROUP_DLOG, nil, nil, InFront);
dgPtr = GetMyWindowDialogPtr (dgPtrWin);
SetDialogFont (0);
if (!dgPtrWin || !dgPtr)
theError = userCanceledErr;
//fix the controls and setup all the PETE user panes
if (!theError) {
SetPort_ (GetWindowPort (GetDialogWindow (dgPtr)));
ConfigFontSetup (dgPtrWin);
ReplaceAllControls (dgPtr);
DefaultPII (dgPtrWin, false, peNoStyledPaste | peClearAllReturns, &pdi);
theError = CreatePeteUserPaneWithQDTextAndScanning (dgPtr, NEW_GROUP_NICK_NAME, peNoStyledPaste | peClearAllReturns, &pdi, true, nickHighlight | nickComplete | nickSpaces, 0);
}
if (!theError)
theError = CreatePeteUserPaneWithQDTextAndScanning (dgPtr, NEW_GROUP_GROUP_NAME, peNoStyledPaste | peClearAllReturns, &pdi, true, nickNoScan, 0);
if (!theError)
theError = CreatePeteUserPaneWithQDTextAndScanning (dgPtr, NEW_GROUP_ADDRESSES, peNoStyledPaste | peVScroll | peUseHLineWidth, &pdi, false,nickHighlight | nickComplete | nickSpaces, kNickScanAllAliasFiles);
// Set the initial text for each PETE user pane
if (!theError) {
dgPtrWin->pte = GetPeteDItem (dgPtrWin, NEW_GROUP_NICK_NAME);
(void) SetKeyboardFocus (GetDialogWindow (dgPtr), GetDItemCtl (dgPtr, NEW_GROUP_NICK_NAME), kControlEditTextPart);
SetPeteDItemText (dgPtrWin, NEW_GROUP_NICK_NAME, nickname);
SetPeteDItemText (dgPtrWin, NEW_GROUP_GROUP_NAME, fullName);
if (addresses) {
FlattenListWith (addresses, '\015');
PeteSetTextPtr (GetPeteDItem (dgPtrWin, NEW_GROUP_ADDRESSES), LDRef (addresses), GetHandleSize (addresses));
UL (addresses);
}
// Fiddle with the rest of the controls
SetDItemState (dgPtr, NEW_GROUP_RECIP, false);
if (!HasFeature (featureMultipleNicknameFiles)) {
HideDialogItem (dgPtr, NEW_GROUP_IN_ADDRESSES);
needFile = false;
}
BuildAddressBookMenu (dgPtr, NEW_GROUP_FILE, nickFileIndexArray, needFile, &ab);
SetAliasFileToScan (GetPeteDItem (dgPtrWin, NEW_GROUP_NICK_NAME), ab);
SetAliasFileToScan (GetPeteDItem (dgPtrWin, NEW_GROUP_ADDRESSES), ab);
HiliteButtonOne (dgPtr);
PeteSelectAll (dgPtrWin, dgPtrWin->pte);
}
// Run the dialog
if (!theError) {
StartMovableModal (dgPtr);
ShowWindow (GetDialogWindow (dgPtr));
done = false;
while (!done) {
SetGreyControl (GetDItemCtl (dgPtr, NEW_GROUP_ADD_DETAILS), !PeteLen (GetPeteDItem (dgPtrWin, NEW_GROUP_NICK_NAME)) || !PeteLen (GetPeteDItem (dgPtrWin, NEW_GROUP_ADDRESSES)));
SetGreyControl (GetDItemCtl (dgPtr, NEW_GROUP_OK), !PeteLen (GetPeteDItem (dgPtrWin, NEW_GROUP_NICK_NAME)) || !PeteLen (GetPeteDItem (dgPtrWin, NEW_GROUP_ADDRESSES)));
CommandPeriod = false;
MovableModalDialog (dgPtr, DlgFilterUPP, &item);
switch (item) {
case NEW_GROUP_RECIP:
SetDItemState (dgPtr, NEW_GROUP_RECIP, !GetDItemState (dgPtr, NEW_GROUP_RECIP));
break;
case NEW_GROUP_FILE:
HitAddressBookMenu (dgPtrWin, NEW_GROUP_FILE, nickFileIndexArray, needFile);
break;
}
GetPeteDItemText (dgPtrWin, NEW_GROUP_NICK_NAME, workingName);
ab = HasFeature (featureMultipleNicknameFiles) ? nickFileIndexArray[GetDItemState(dgPtr, NEW_GROUP_FILE) - 1] : 0;
if (item == CANCEL_ITEM || item == NEW_GROUP_CANCEL || item == NEW_GROUP_OK || item == NEW_GROUP_ADD_DETAILS)
done = true;
if ((item == NEW_GROUP_OK || item == NEW_GROUP_ADD_DETAILS) && (nre = BadNickname (workingName, fullName, ab, -1, addresses==nil,true,false)) == nrDifferent) {
PeteSelectAll (dgPtrWin, dgPtrWin->pte);
done = false;
}
}
mabeHit = mabeHitOther;
if (item==NEW_GROUP_OK || item == NEW_GROUP_ADD_DETAILS) {
Handle typedAddresses;
mabeHit = item == NEW_GROUP_OK ? mabeHitOK : mabeHitAddDetails;
makeRecip = GetDItemState(dgPtr,NEW_GROUP_RECIP);
GetPeteDItemText (dgPtrWin, NEW_GROUP_NICK_NAME, nickname);
GetPeteDItemText (dgPtrWin, NEW_GROUP_GROUP_NAME, fullName);
// Grab the typed addresses
typedAddresses = GetPeteDItemTextH (dgPtrWin, NEW_GROUP_ADDRESSES);
if (typedAddresses) {
Tr (typedAddresses, "\015", ",");
theError = SuckPtrAddresses (&expansion, LDRef(typedAddresses),GetHandleSize (typedAddresses),true,true,false,nil);
}
ZapHandle (addresses);
// Make a notes handle
if (!theError) {
notes = NuHandle (0);
theError = MemError ();
}
if (!theError && *fullName)
theError = AddAttributeValuePair (notes, GetRString (tag, ABReservedTagsStrn + abTagName), &fullName[1], *fullName);
if (!theError)
nick = NewNickLow (expansion, notes, ab, nickname, makeRecip, nre, AB.inited ? false : true);
}
DisposePeteUserPaneItem (dgPtrWin, NEW_GROUP_NICK_NAME);
DisposePeteUserPaneItem (dgPtrWin, NEW_GROUP_GROUP_NAME);
DisposePeteUserPaneItem (dgPtrWin, NEW_GROUP_ADDRESSES);
EndMovableModal(dgPtr);
DisposDialog_(dgPtr);
if (!theError)
{
if (AB.inited)
UpdateMyWindow(GetMyWindowWindowPtr(Win)); // redisplay AB in case dialog was on top
PostMakeNicknameDialog (ab, nick, mabeHit);
}
}
ZapPtr (nickFileIndexArray);
if (theError)
WarnUser (ALIAS_NEW_NICK_ERR, theError);
return (item==NEW_GROUP_OK || item == NEW_GROUP_ADD_DETAILS ? nre : nrCancel);
}
//
// CreatePeteUserPaneWithQDTextAndScanning
//
// Abusively long, but it saves us some typing and code size in the long run.
//
OSErr CreatePeteUserPaneWithQDTextAndScanning (DialogPtr theDialog, short item, uLong flags, PETEDocInitInfoPtr initInfo, Boolean noWrap, NickScanType nickScan, short aliasIndex)
{
PETEHandle pte;
if (pte = CreatePeteUserPane (GetDItemCtl (theDialog, item), flags, initInfo, noWrap)) {
PeteFontAndSize (pte,GetPortTextFont(GetQDGlobalsThePort()),GetPortTextSize(GetQDGlobalsThePort()));
if (nickScan != nickNoScan)
SetNickScanning (pte, nickScan, aliasIndex, nil, nil, nil);
}
return (pte ? noErr : userCanceledErr);
}
void PostMakeNicknameDialog (short ab, short nick, MakeAddressBookEntryHitType mabeHit)
{
VLNodeInfo data;
VLNodeID nodeID;
nodeID = MakeNodeID (ab, nick);
switch (mabeHit) {
case mabeHitOK:
if (AB.inited) {
ABTickle ();
// InvalidListView (&NickList);
// ABNickLVSelect (nodeID, true);
// LVDraw (&NickList,nil,false, false);
}
break;
case mabeHitAddDetails:
if (AB.inited) InvalidListView (&NickList);
OpenABWin (nil);
// Massage the item name so it can be found in the list view
if (AB.inited) {
if ((*Aliases)[ab].collapsed) {
LVGetItemWithNodeID (&NickList, MakeABNodeID (ab), &data);
LVExpand (&NickList, MakeABNodeID (ab), data.name, true);
}
ABNickLVSelect (nodeID, false);
LVDraw (&NickList,nil,false,false);
}
break;
}
}
/**********************************************************************
* NickSuggest - suggest a name for an address and extract the real name
**********************************************************************/
void NickSuggest(BinAddrHandle addresses, PStr name,PStr realName,Boolean uniq,short fmt)
{
Str255 scratch;
Str255 firstAddress;
Str63 first, last;
short i;
*name = 0;
*realName = 0;
/*
* emptiness
*/
if (!addresses || !*addresses || !**addresses) return;
/*
* more than one address?
*/
if ((*addresses)[**addresses+2]) return;
/*
* only one address!
*/
PSCopy(scratch,*addresses);
/*
* extract name portion
*/
BeautifyFrom(scratch);
if (!*scratch || *scratch > 63) return;
PCopy(realName,scratch);
PCopy(firstAddress,*addresses);
PCopy(name,realName);
// if name is same as address, it's not really
// a name, so don't bother trying to process it
// as though it were
if (StringSame(realName,firstAddress))
*realName = 0;
else
{
ParseFirstLast(name,first,last);
if (GetPrefLong(PREF_NICK_GEN_OPTIONS)&kNickGenOptLastFirst)
{
PCopy(name,last);
if (*name) PCatC(name,' ');
PCat(name,first);
}
else
JoinFirstLast(name,first,last);
}
// compose using the format, if any
if (fmt)
{
ComposeRString(scratch,fmt,name);
PCopy(name,scratch);
}
/*
* Sanitize
*/
SanitizeFN(name,name,NICK_BAD_CHAR,NICK_REP_CHAR,false);
*name = MIN(*name,30);
// make it unique
if (uniq)
{
PSCopy(last,name);
for (i=1;IsAnyNickname(last);i++)
{
NumToString(i,first);
PCopy(scratch,name);
*scratch = MIN(30-*first,*scratch);
PCat(scratch,first);
PCopy(last,scratch);
}
PCopy(name,last);
}
}
short *GetNickFileArray (Boolean *needFile)
{
short *nickFileIndexArray,
item;;
nickFileIndexArray = nil;
// we only need the file menu if another writeable file exists
//Enhanced Address Book - prevent users from creating a new nickname FILE.
if (HasFeature (featureMultipleNicknameFiles)) {
if (*needFile) {
*needFile = False;
for (item = 1; item < NAliases; item++)
if (!(*Aliases)[item].ro) {
*needFile = True;
break;
}
}
// We're going to build a small array that matches each element of the file popup. In
// each array element we'll store the "real" 'which' value for that menu item to be used
// when the user has specified a particular nickname file. We're doing this to prevent
// an offset problem when the user has plug-in nickname files.
if (*needFile)
nickFileIndexArray = NuPtr (sizeof (short) * NAliases);
}
else
*needFile = false;
return (nickFileIndexArray);
}
void BuildAddressBookMenu (DialogPtr theDialog, short addressBookMenuItem, short *nickFileIndexArray, Boolean needFile, short *which)
{
ControlHandle theControl;
MenuHandle mh;
short *nickFileIndexArrayPtr,
ab,
totalAliases;
if (theControl = GetDItemCtl (theDialog, addressBookMenuItem))
if (mh = GetControlPopupMenuHandle (theControl))
if (!needFile)
HideDialogItem (theDialog, addressBookMenuItem);
else {
nickFileIndexArrayPtr = nickFileIndexArray;
// The address book menu will consist of only the following:
// Eudora Nicknames
// Regular Address Books
// History List
totalAliases = NAliases;
for (ab = 0; ab < totalAliases; ++ab)
if (IsEudoraAddressBook (ab) || IsRegularAddressBook (ab) || (IsHistoryAddressBook (ab) && !PrefIsSet (PREF_NICK_CACHE_NOT_VISIBLE))) {
MyAppendMenu (mh, (*Aliases)[ab].spec.name);
if ((*Aliases)[ab].ro)
DisableItem (mh, CountMenuItems (mh));
*nickFileIndexArrayPtr++ = ab;
}
SetControlMaximum (theControl, CountMenuItems (mh));
SetControlMinimum (theControl, 1);
SetControlValue (theControl, *which + 1);
}
}
void HitFileNickRadioButton (DialogPtr theDialog, short itemHit, short addressBookItem, short nicknameItem, short addressBookMenuItem, short recipientItem)
{
short newFileCheck;
newFileCheck = GetDItemState (theDialog, addressBookItem);
if ((newFileCheck && itemHit == nicknameItem) || (!newFileCheck && itemHit == addressBookItem)) { // Currently checked, need to undim items
if (newFileCheck) {
SetGreyControl (GetDItemCtl (theDialog, addressBookMenuItem), false);
SetGreyControl (GetDItemCtl (theDialog, recipientItem), false);
}
else // Currently unchecked, need to dim items.
{
SetGreyControl (GetDItemCtl (theDialog, addressBookMenuItem), true);
SetGreyControl (GetDItemCtl (theDialog, recipientItem), true);
}
SetDItemState (theDialog, addressBookItem, !newFileCheck);
SetDItemState (theDialog, nicknameItem, newFileCheck);
}
}
void HitAddressBookMenu (MyWindowPtr dgPtrWin, short addressBookMenuItem, short *nickFileIndexArray, Boolean needFile)
{
long selStart,
selEnd;
if (needFile) {
if (CurSelectionIsTypeAheadText (dgPtrWin->pte)) {
ResetNicknameTypeAhead (dgPtrWin->pte);
ResetNicknameHiliting (dgPtrWin->pte);
if (!PeteGetTextAndSelection (dgPtrWin->pte, nil, &selStart, &selEnd))
if (selStart != selEnd) {
PeteDelete (dgPtrWin->pte, selStart, selEnd);
NicknameHilitingUnHiliteField (dgPtrWin->pte);
}
}
SetAliasFileToScan (dgPtrWin->pte, nickFileIndexArray[GetDItemState(GetMyWindowDialogPtr (dgPtrWin), addressBookMenuItem) - 1]);
}
}
/************************************************************************
* BadNickname - is the nickname defective?
************************************************************************/
NickReplaceEnum BadNickname(UPtr candidate,UPtr veteran,short which,short nick,Boolean justRename,Boolean collisionCheck,Boolean folder)
{
Str31 verboten;
UPtr spot,end,key;
Str31 noWhiteCand, noWhiteVet;
short ab,
foundNick,
i;
if (!*candidate) return(nrDifferent);
// If the strings hasn't changed on a rename, don't test it
if (justRename && StringSame (candidate, veteran))
return (nrOk);
// folder?
if (folder)
{
if (*candidate>31)
{
WarnUser(NICKFILE_TOO_LONG,0);
return(nrDifferent);
}
if (PIndex(candidate,':'))
{
WarnUser(NICKFILE_BADCHAR,0);
return(nrDifferent);
}
for (i=NAliases-1;i>=0;i--)
{
PSCopy(verboten,(*Aliases)[i].spec.name);
if (StringSame(verboten,candidate))
{
WarnUser(NICKFILE_DUPLICATE,0);
return(nrDifferent);
}
}
if (EqualStrRes(candidate,ALIAS_FILE) || EqualStrRes(candidate,USA_EUDORA_NICKNAMES) || EqualStrRes(candidate,FILE_ALIAS_NICKNAMES))
{
WarnUser(NICKFILE_DUPLICATE,0);
return(nrDifferent);
}
return(nrOk);
}
// nickname
if (*candidate>MAX_NICKNAME)
{
ComposeStdAlert (Note, ALIAS_TOO_LONG, MAX_NICKNAME);
return(nrDifferent);
}
GetRString(verboten,ALIAS_VERBOTEN);
end = verboten+*verboten+1;
for (key=candidate+1;key<=candidate+*candidate;key++)
for (spot=verboten+1;spot<end;spot++)
if (*spot==*key)
{
WarnUser(WARN_VERBOTEN,0);
return(nrDifferent);
}
if (*veteran && StringSame(candidate,veteran) && !collisionCheck)
return(!EqualString(candidate,veteran,True,True));
if (*veteran && justRename && !collisionCheck)
{
PSCopy(noWhiteCand,candidate);
PSCopy(noWhiteVet,veteran);
*noWhiteCand = RemoveChar(' ',noWhiteCand+1,*noWhiteCand);
*noWhiteVet = RemoveChar(' ',noWhiteVet+1,*noWhiteVet);
if (StringSame(noWhiteCand,noWhiteVet)) return(!EqualString(candidate,veteran,True,True));
}
// Check to see if the nickname already exists in this nickname file
foundNick = NickMatchFound ((*Aliases)[which].theData, NickHash (candidate), candidate, which);
// If it does exist -- and, in fact, it is the same nick as that we are looking for during a rename, just return
if (ValidNickname (nick) && foundNick == nick && justRename)
return (nrOk);
if (ValidNickname (foundNick)) {
if (justRename) {
ComposeStdAlert (Stop, NICK_IN_USE, candidate, (*Aliases)[which].spec.name);
return (nrDifferent);
}
else return (NickRepAlert(candidate));
}
// How about any other nickname file?
for (ab = 0; ab < NAliases; ++ab)
if (ab != which) {
foundNick = NickMatchFound ((*Aliases)[ab].theData, NickHash (candidate), candidate, ab);
if (ValidNickname (foundNick)) {
if (justRename) {
switch (ComposeStdAlert (Stop, DUP_NICKNAME_WARNING, candidate)) {
case kAlertStdAlertOKButton:
return (nrDifferent);
break;
case kAlertStdAlertOtherButton:
return (nrOk);
break;
}
}
}
}
return(nrOk);
}
/************************************************************************
* InsertTheAlias - insert the given alias in the window below us
************************************************************************/
MyWindowPtr InsertTheAlias (short txeIndex, Boolean wantExpansion)
{
MessHandle messH;
GrafPtr oldPort;
MyWindowPtr win;
HeadSpec hs;
Str31 nameStr;
long start;
short ab,
nick,
index;
if (HasFeature (featureNicknameWatching) && PrefIsSet (PREF_NICK_AUTO_EXPAND))
wantExpansion = true;
win = TopCompositionWindow(true, false);
if (win && CompHeadFind (messH = Win2MessH(win), txeIndex, &hs)) {
GetPort (&oldPort);
SetPort_ (GetMyWindowCGrafPtr (win));
PeteSelect (win, win->pte, hs.stop, hs.stop);
PetePrepareUndo (win->pte, peUndoPaste, -1, -1, &start, nil);
index = 1;
while (GetSelectedABNick (index++, &ab, &nick))
if (nick >= 0) {
InsertCommaIfNeedBe (TheBody, &hs);
GetNicknameNamePStr (ab, nick, nameStr);
InsertAlias (win->pte, &hs, nameStr, wantExpansion, start, false);
}
ShowMyWindow (GetMyWindowWindowPtr (win));
UpdateSum (messH, SumOf(messH)->offset, SumOf(messH)->length);
SetPort_ (oldPort);
}
return win;
}
/**********************************************************************
* NickWDragVDivider - Handle dragging of the divider between the list
* and the data
**********************************************************************/
void NickWDragVDivider(Point pt)
{
Rect div, bounds;
Point newPt;
long tempValue;
div = VertDivider;
bounds = Win->contR;
bounds.right = RectWi(Win->contR) - MinTabWidth;
bounds.left = MinListWidth;
if (bounds.left > bounds.right)
{
bounds.left = div.left;
bounds.right = div.right;
}
if (div.right > bounds.right)
bounds.right = div.right;
newPt = DragDivider(pt,&div,&bounds,Win);
// resize?
if (newPt.h)
{
tempValue = (100 * (newPt.h-2*INSET-INSET/2))/(Win->contR.right - Win->contR.left);
SetPrefLong(ALIAS_NICK_LIST_PRCT,MIN(tempValue,750));
MyWindowDidResize(Win,nil);
}
}
// Are any nicknames dirty?
Boolean AnyNicknamesDirty (short ab)
{
short whichFile;
short totalAliases = NAliases;
short totalNicks;
short firstFile = 0;
short i;
if (ab != kAddressBookUndefined) {
firstFile = ab;
totalAliases = ab + 1;
}
for (whichFile=firstFile;whichFile<totalAliases;whichFile++) // Loop through all the files
{
if ((*Aliases)[whichFile].dirty)
return (true);
totalNicks = (*Aliases)[whichFile].theData ? (GetHandleSize_((*Aliases)[whichFile].theData)/sizeof(NickStruct)) : 0;
for (i=0;i<totalNicks;i++)
{
if ((*((*Aliases)[whichFile].theData))[i].addressesDirty || (*((*Aliases)[whichFile].theData))[i].notesDirty || (*((*Aliases)[whichFile].theData))[i].pornography)
return (true);
}
}
return (false);
}
/************************************************************************
* ReformatClip - change returns to comma-space on the clipboard
************************************************************************/
void ReformatClip(void)
{
Handle text = NuHandle(0);
long size,junk;
UPtr spot,copy, end;
short newlines = 0;
if (text)
{
if (size = GetScrap(text,'TEXT',&junk))
{
end = *text+size;
for (spot=*text;spot<end;spot++) if (*spot=='\015') newlines++;
if (newlines)
{
SetHandleBig_(text,newlines+size);
if (!MemError())
{
copy = *text+size+newlines;
for (spot=*text+size-1;spot>=*text;spot--)
{
if (*spot=='\015')
{
*--copy = ' ';
*--copy = ',';
}
else *--copy = *spot;
}
ZeroScrap();
PutScrap(size+newlines,'TEXT',LDRef(text));
}
}
}
ZapHandle(text);
}
}
/**********************************************************************
* PrintAddressBookWindow - print nicknames from the nicknames window
* Brand-new strategy here: Let Pete do it!
**********************************************************************/
OSErr PrintAddressBookWindow (Boolean select, Boolean now)
{
OSErr err = noErr;
MyWindowPtr win;
WindowPtr winWP;
short which;
// make the title for the window, this will go in the print header
ABPrintMakeTitle(GlobalTemp,select);
// Make a text window to dump the stuff to
if (!(win=OpenText(nil,nil,nil,nil,false,GlobalTemp,true,false)))
WarnUser(PRINT_FAILED,0);
else
{
winWP = GetMyWindowWindowPtr (win);
(*PeteExtra(win->pte))->spelled = sprNeverSpell;
PeteCalcOff(win->pte);
PETEAllowUndo(PETE,win->pte,false,false);
SetNickScanning (win->pte, nickHighlight, kNickScanAllAliasFiles, nil, nil, nil);
for (which=0;which<NAliases;which++)
if (!IsPluginAddressBook (which)) PrintNickFile(which,select,win->pte);
// Print the text window
win->dirty = TheWorldAccordingToMarthaStewart;
#ifdef DEBUG
if (RunType!=Production && !(CurrentModifiers()&optionKey))
{
PeteCalcOn(win->pte);
ShowMyWindow(winWP);
return noErr;
}
else
#endif
err = PrintOneMessage(win,false,now);
// Close the text window
NoSaves = true;
win->isDirty = false;
PeteCleanList (win->pte);
CloseMyWindow(winWP);
NoSaves = false;
if (err) WarnUser(PART_PRINT_FAIL,err);
}
return err;
}
/**********************************************************************
* ABPrintMakeTitle - make up a title for printing
**********************************************************************/
void ABPrintMakeTitle(PStr title, Boolean select)
{
WindowPtr winWP = GetMyWindowWindowPtr(Win);
GetWTitle(winWP,title);
}
/**********************************************************************
* PrintNickFile - print nicknames from one file
**********************************************************************/
OSErr PrintNickFile (short which, Boolean select, PETEHandle pte)
{
Str255 title;
short total,
ab,
nick,
count,
firstIndex, // Index of the first nickname in the chosen address book
i,
myCount = 0;
NickPrintStuff myStuff;
short **sort;
// prepare para info
Res2PInfo(NICK_PRINT_MARG1,pte,nil,&myStuff.pi1);
Res2PInfo(NICK_PRINT_MARG2,pte,nil,&myStuff.pi2);
Res2PInfo(NICK_PRINT_MARG3,pte,nil,&myStuff.pi3);
myStuff.pte = pte;
total = 0;
if (select)
{
count = LVCountSelection (&NickList);
for (i = 1; i <= count; ++i) {
GetSelectedABNick (i, &ab, &nick);
if (ab == which) {
if (nick != kNickUndefined) {
if (++myCount == 1) firstIndex = i;
}
else {
select = false;
break;
}
}
}
}
if (myCount == 0 && select)
return (noErr);
BuildNickPrintTitle (which, select, title);
if (PeteLen(pte)) PInsertC(title,sizeof(title),'\f',title+1);
PeteAppendText(title+1,*title,pte);
PeteAppendText(Cr+1,1,pte);
PeteInsertRule(pte,kPETELastPara,0,0,false,true,false);
count = (*Aliases)[which].theData ? (GetHandleSize_ ((*Aliases)[which].theData) / sizeof (NickStruct)) : 0;
total = 0;
for (nick = 0; nick < count; nick++)
if (!(*((*Aliases)[which].theData))[nick].deleted)
++total;
// The list will always have the file title as the first element and therefore the
// count will be the total nickname count + 1;
// However the first element (the title) is not printed.
if (!total) // If nickname file is blank, DON'T print it!
return (noErr);
if (select) {
for (i = firstIndex; !CommandPeriod && myCount; ++i) {
GetSelectedABNick (i, &ab, &nick);
PrintNickname ( &myStuff, ab, nick );
myCount--;
}
}
else {
// Make sure the address book is sorted
SortAddressBook(which);
sort = (*Aliases)[which].sortData;
if (!sort) return memFullErr;
count = (*Aliases)[which].theData ? (GetHandleSize_ ((*Aliases)[which].theData) / sizeof (NickStruct)) : 0;
for (nick = 0; !CommandPeriod && nick < count; ++nick)
if (!(*((*Aliases)[which].theData))[(*sort)[nick]].deleted)
PrintNickname ( &myStuff, which, (*sort)[nick] );
}
return (noErr); // yes, we should probably do more with error checking, but the old
} // version of this routine could not generate anything but 'noErr'
//
// PrintNickname
//
void PrintNickname (NickPrintStuffPtr myStuffP, short ab, short nick)
{
Handle tempHandle;
GetNicknameNamePStr (ab, nick, myStuffP->name);
myStuffP->addresses = nil;
if (tempHandle = GetNicknameData (ab, nick, true, true))
SuckAddresses (&myStuffP->addresses, tempHandle, true, true, false, nil);
myStuffP->ab = ab;
myStuffP->nick = nick;
PrintANick (myStuffP);
ZapHandle (myStuffP->addresses);
}
/**********************************************************************
* PrintANick - print a single nickname
**********************************************************************/
OSErr PrintANick(NickPrintStuffPtr myStuffP)
{
OSErr err = noErr;
/*
* print the nickname
*/
if (err=NickPrintName(myStuffP->pte, &myStuffP->pi1, myStuffP->name)) return err;
/*
* print things in tab order
*/
IterateTabPanes (Tabs, ABPrintPaneProc, myStuffP);
// and a blank line, just for fun
if (err = PETEInsertParaPtr(PETE,myStuffP->pte,kPETELastPara,nil,Cr+1,*Cr,nil)) return err;
err = PetePlainParaAt(myStuffP->pte,PeteLen(myStuffP->pte)-1,PeteLen(myStuffP->pte));
return err;
}
/**********************************************************************
* ABPrintPaneProc - interator over tabs. Print the tab name, then iterate
* over the fields
**********************************************************************/
Boolean ABPrintPaneProc (ControlHandle tabControl, ControlHandle tabPane, short tabIndex, NickPrintStuffPtr myStuffP)
{
myStuffP->foundOne = false;
myStuffP->tabPane = tabPane;
IterateTabPaneObjectsUL(tabPane,ABPrintObjectProc,myStuffP);
return false;
}
/**********************************************************************
* ABPrintObjectProc - iterate over objects in the tab
**********************************************************************/
Boolean ABPrintObjectProc (TabObjectPtr objectPtr, NickPrintStuffPtr myStuffP)
{
Str255 value;
Str255 s;
if (objectPtr->type == fieldObject)
{
if (EqualStrRes(objectPtr->tag,ABReservedTagsStrn+abTagEmail))
{
if (myStuffP->addresses && **myStuffP->addresses)
{
// print tab name if we haven't already
if (!myStuffP->foundOne)
{
GetControlTitle(myStuffP->tabPane,s);
NickPrintName(myStuffP->pte, &myStuffP->pi2, s);
myStuffP->foundOne = true;
}
NickPrintAddresses(myStuffP->pte,&myStuffP->pi3,objectPtr->shortName,myStuffP->addresses);
}
}
else
{
// does it have a value?
GetTaggedFieldValueStr (myStuffP->ab, myStuffP->nick, objectPtr->tag, value);
if (*value)
{
// print tab name if we haven't already
if (!myStuffP->foundOne)
{
Str255 paneTitle;
GetControlTitle(myStuffP->tabPane,paneTitle);
NickPrintName(myStuffP->pte, &myStuffP->pi2, paneTitle);
myStuffP->foundOne = true;
}
// Now print name & value
NickPrintNameAndValue(myStuffP->pte, &myStuffP->pi3, objectPtr->shortName, value);
}
}
}
return false;
}
/**********************************************************************
*
**********************************************************************/
OSErr NickPrintName(PETEHandle pte, PETEParaInfoPtr pinfop, PStr name)
{
Str255 s;
OSErr err;
PCopy(s,name);
PCat(s,Cr);
err = PETEInsertParaPtr(PETE,pte,kPETELastPara,pinfop,s+1,*s,nil);
return err;
}
/**********************************************************************
*
**********************************************************************/
OSErr NickPrintAddresses(PETEHandle pte, PETEParaInfoPtr pinfop, PStr name, UHandle addresses)
{
OSErr err;
short oldOffset = PeteLen(pte);
err = PETEInsertParaPtr(PETE,pte,kPETELastPara,pinfop,name+1,*name,nil);
if (!err) err = PeteInsertChar(pte,kPETELastPara,'\t',nil);
FlattenListWith(addresses,'\015');
if (!err) err = PETEInsertTextHandle(PETE,pte,kPETELastPara,addresses,GetHandleSize(addresses),0,nil);
if (!err) err = PeteInsertChar(pte,kPETELastPara,'\r',nil);
//NicknameHilitingUpdateRange (pte, oldOffset, PeteLen(pte));
return err;
}
/**********************************************************************
*
**********************************************************************/
OSErr NickPrintNameAndValue(PETEHandle pte, PETEParaInfoPtr pinfop, PStr name, PStr value)
{
OSErr err;
err = PETEInsertParaPtr(PETE,pte,kPETELastPara,pinfop,name+1,*name,nil);
if (!err) err = PeteInsertChar(pte,kPETELastPara,'\t',nil);
if (!err) err = PETEInsertTextPtr(PETE,pte,kPETELastPara,value+1,*value,nil);
if (!err) err = PeteInsertChar(pte,kPETELastPara,'\r',nil);
return err;
}
/**********************************************************************
* BuildNickPrintTitle - print title for a nickname
**********************************************************************/
void BuildNickPrintTitle(short which,Boolean select,PStr title)
{
Str63 fn;
Str63 selectStr;
if (select) GetRString(selectStr,SELECTED);
else *selectStr = 0;
PCopy(fn,(*Aliases)[which].spec.name);
ComposeRString(title,NICK_HEAD_FMT,selectStr,fn);
}
//
// GetAliasExpansionFor822Flavor
//
// This is derived from InsertAlias. Originally, this code was buried within the InsertAlias
// function in 'nickexp.c', special-cased because there was some commonality to what that
// function did. It made my head hurt so I moved it here.
//
Handle GetAliasExpansionFor822Flavor (short which, short index)
{
BinAddrHandle wordH,
list;
Str31 nameStr;
long tempSize,
addressSize;;
GetNicknameNamePStr (which, index, nameStr);
addressSize = *nameStr + 1;
wordH = NuHandle (addressSize + 5);
if (!wordH) {
WarnUser (MEM_ERR, MemError ());
return (nil);
}
BlockMoveData (nameStr, *wordH, nameStr[0] + 1);
(*wordH)[**wordH + 1] = (*wordH)[**wordH + 2] = 0;
ExpandAliases (&list, wordH, 0, true);
ZapHandle (wordH);
// (jp) Bad ju-ju ahead... if the list is zero length, we correctly do not replace the existing typing
// with nothing (what would be the point?), but we incorrectly lose the caret.
if (list) {
tempSize = GetHandleSize_ (list);
if (tempSize == 1 && strlen (LDRef (list)) == 0)
ZapHandle (list);
else
UL (list);
}
if (list)
CommaList (list);
return (list);
}
/**********************************************************************
* NickAddrSetDragContents - add the right stuff to a drag
**********************************************************************/
pascal OSErr NickAddrSetDragContents(PETEHandle pte,DragReference drag)
{
OSErr err = noErr;
Handle text = nil,commaText=nil;
long selStart,selEnd;
err = PeteGetTextAndSelection(pte,&text,&selStart,&selEnd);
if (err) return(err);
commaText = NuHandle(0);
if (!commaText)
return(MemError());
err = HandPlusHand(text,commaText);
if(err)
{ZapHandle(commaText);return(err);}
Tr(commaText,"\015",",");
err = AddDragItemFlavor(drag, 1L, 'TEXT', LDRef(text) + selStart, selEnd - selStart, 0);
if (!err)
err = AddDragItemFlavor(drag, 1L, A822_FLAVOR, LDRef(commaText) + selStart, selEnd - selStart, 0);
UL(commaText);
// ZapHandle(text);
ZapHandle(commaText);
return(err);
}
/**********************************************************************
* NickGetDragContents - get the right stuff from a drag
**********************************************************************/
OSErr SuckDragAddresses(DragReference drag,Handle *addresses,Boolean leadingComma,Boolean trailingComma);
pascal OSErr NickGetDragContents(PETEHandle pte,UHandle *theText, PETEStyleListHandle *theStyles, PETEParaScrapHandle *theParas, DragReference drag, long dropLocation)
{
OSErr err = handlerNotFoundErr;
long theLen = PeteLen(pte);
*theText = nil;
err = SuckDragAddresses(drag,theText,dropLocation > 0 && theLen != 0,dropLocation < theLen);
return(err);
}
/**********************************************************************
* TheWorldAccordingToMarthaStewart - is a window dirty?
**********************************************************************/
Boolean TheWorldAccordingToMarthaStewart(MyWindowPtr win)
{
return false;
}
void DoAddressBookExport (Boolean select)
{
TabFieldHandle fields;
NickExportRec nickExport;
FSSpec spec;
Str255 windowTitle,
tag;
Str15 ctext;
UPtr spot,
end;
OSErr theError;
long creator;
short ab;
ModalFilterYDUPP filter=nil;
GetRString (spec.name, UNTITLED_CSV);
GetRString (windowTitle, NICK_SAVE_AS_TITLE);
GetPref (ctext, PREF_CREATOR);
if (*ctext != 4) GetRString (ctext, TEXT_CREATOR);
BMD (ctext + 1, &creator, 4);
if (theError = SFPutOpen (&spec, creator, 'TEXT', &nickExport.refNum, nil, filter, 0, nil, windowTitle, nil))
return;
OpenProgress();
ProgressR (NoBar, 0, EXPORTING_NICKNAMES, EXPORTING_SUBTITLE, nil);
nickExport.theError = noErr;
// Stringy stuff
GetRString (nickExport.eol, NICK_EXPORT_EOL);
GetRString (nickExport.comma, NICK_EXPORT_COMMA);
GetRString (nickExport.commaReplace, NICK_COMMA_REPLACE_CHAR);
GetRString (nickExport.crReplace, NICK_CR_REPLACE_CHAR);
// Initialize the accumulators
theError = AccuInit (&nickExport.a);
// Place the field names into the accumulator -- this is the first line written to the file. The nickname
// field is always the first field, followed by the rest in tab field order
if (!theError)
theError = AccuAddStr (&nickExport.a, GetRString (tag, ABReservedTagsStrn + abNickname));
if (!theError)
if (fields = GetExportableTabFieldStrings (Tabs)) {
spot = LDRef (fields);
end = spot + GetHandleSize (fields);
while (spot < end && !theError) {
if (*spot) {
theError = AccuAddStr (&nickExport.a, nickExport.comma);
if (!theError)
theError = AccuAddStr (&nickExport.a, spot);
}
spot += (*spot + 2);
}
if (!theError)
theError = AccuAddStr (&nickExport.a, nickExport.eol);
ZapHandle (fields);
}
// If no address books are selected, we'll save them all. Otherwise, export only those that are selected.
// In either case, field values are added in the same order as the field names -- nickname followed by tab order.
for (ab = 0; ab < NAliases && !theError; ++ab)
if (!IsPluginAddressBook (ab))
theError = ExportNickFile (&nickExport, ab, select);
// Anything left to write to the file?
if (!theError)
theError = AccuWrite (&nickExport.a, nickExport.refNum);
if (!theError)
theError = TruncAtMark (nickExport.refNum);
(void) MyFSClose (nickExport.refNum);
AccuZap (nickExport.a);
CloseProgress ();
if (theError)
WarnUser (NICK_EXPORT_FAIL, theError);
}
OSErr ExportNickFile (NickExportPtr nickExport, short which, Boolean select)
{
OSErr theError;
short **sort,
ab,
nick,
count,
myCount,
firstIndex,
i;
theError = noErr;
myCount = 0;
if (select) {
count = LVCountSelection (&NickList);
for (i = 1; i <= count; ++i) {
GetSelectedABNick (i, &ab, &nick);
if (ab == which) {
if (ValidNickname (nick)) {
if (++myCount == 1)
firstIndex = i;
}
else {
select = false;
break;
}
}
}
}
if (myCount == 0 && select)
return (noErr);
if (select) {
for (i = firstIndex; !theError && !CommandPeriod && myCount; ++i) {
GetSelectedABNick (i, &ab, &nick);
theError = ExportNickname (nickExport, ab, nick);
myCount--;
}
}
else {
// We'll sort before exporting, just to be nice
SortAddressBook (which);
sort = (*Aliases)[which].sortData;
if (!sort)
return (memFullErr);
count = (*Aliases)[which].theData ? (GetHandleSize_ ((*Aliases)[which].theData) / sizeof (NickStruct)) : 0;
for (nick = 0; !theError && !CommandPeriod && nick < count; ++nick)
if (!(*((*Aliases)[which].theData))[(*sort)[nick]].deleted)
theError = ExportNickname (nickExport, which, (*sort)[nick]);
}
return (theError);
}
OSErr ExportNickname (NickExportPtr nickExport, short ab, short nick)
{
Str255 nickname;
OSErr theError;
ProgressMessage (kpMessage, GetNicknameNamePStr (ab, nick, nickname));
CycleBalls ();
// The nickname itself is always first
theError = AccuAddStr (&nickExport->a, nickname);
// Following the nickname are all the fields, in tab order
if (!theError) {
nickExport->ab = ab;
nickExport->nick = nick;
IterateTabObjectsUL (Tabs, ExportNicknameProc, nickExport);
theError = nickExport->theError;
}
// Line termination
if (!theError)
theError = AccuAddStr (&nickExport->a, nickExport->eol);
// Maybe write out what we've accumulated so far
if (!theError && nickExport->a.offset > 16 K) {
theError = AccuWrite (&nickExport->a, nickExport->refNum);
nickExport->a.offset = 0;
}
return (theError);
}
Boolean ExportNicknameProc (TabObjectPtr objectPtr, NickExportPtr nickExport)
{
TextAddrHandle addresses;
Str255 value;
Handle tempHandle;
OSErr theError;
theError = noErr;
// add the value
if (objectPtr->flags & objectFlagExportable) {
// lead off with a comma before each field value
theError = AccuAddStr (&nickExport->a, nickExport->comma);
if (EqualStrRes (objectPtr->tag, ABReservedTagsStrn + abTagEmail)) {
addresses = nil;
if (tempHandle = GetNicknameData (nickExport->ab, nickExport->nick, true, true)) {
theError = SuckAddresses (&addresses, tempHandle, true, true, false, nil);
if (!theError && addresses && **addresses) {
CommaList (addresses);
Tr (addresses, ",", nickExport->commaReplace);
theError = AccuAddHandle (&nickExport->a, addresses);
}
}
ZapHandle (addresses);
}
else {
GetTaggedFieldValueStr (nickExport->ab, nickExport->nick, objectPtr->tag, value);
if (*value) {
TrLo (&value[1], *value, ",", nickExport->commaReplace);
TrLo (&value[1], *value, "\015", nickExport->crReplace);
TrLo (&value[1], *value, "\003", nickExport->crReplace);
}
theError = AccuAddStr (&nickExport->a, value);
}
}
nickExport->theError = theError;
return (false);
}
#ifdef VCARD
//
// AutoGeneratePersonalInformation
//
// Auto-generate personal information and add it into a given address book
// as a nickname.
//
OSErr AutoGeneratePersonalInformation (short ab)
{
StringHandle hAddress;
Handle expansion;
Str255 returnAddress;
Str31 realName,
firstName,
lastName,
nickname;
OSErr theError;
theError = noErr;
*returnAddress = 0;
*firstName = 0;
*lastName = 0;
expansion = nil;
// Grab some important vCard-ish information from the dominant personality
GetDominantPref (PREF_REALNAME, realName);
if (hAddress = NewString (GetShortReturnAddr (returnAddress))) {
theError = SuckPtrAddresses (&expansion, LDRef (hAddress), GetHandleSize (hAddress), true, true, false, nil);
ZapHandle (hAddress);
}
// Make it into a nickname
if (!theError && *realName) {
ParseFirstLast (realName, firstName, lastName);
PSCopy (nickname, realName);
BeautifyFrom (nickname);
SanitizeFN (nickname, nickname, NICK_BAD_CHAR, NICK_REP_CHAR, false);
*nickname = MIN (*nickname, sizeof (Str31) - 1);
NewNickLow (expansion, CreateSimpleNotes (realName, firstName, lastName), 0, nickname, false, nrNone, true);
}
return (theError);
}
OSErr MakeVCardFile (FSSpec *spec, short ab, short nick)
{
FSSpec nickFolder,
vcardFolder;
Str255 vcardFolderName,
nickname;
Handle vcardData,
notes;
OSErr theError;
long dirID;
UseFeature (featureVCard);
theError = noErr;
// Make a vCard from this nickname
if (notes = GetNicknameData (ab, nick, false, true))
Tr (notes, "\003", "\015");
if (vcardData = MakeVCard (GetNicknameData (ab, nick, true, true), notes)) {
// Find the Nicknames Folder
if (theError = SubFolderSpec (NICK_FOLDER, &nickFolder)) {
SimpleMakeFSSpec (Root.vRef, Root.dirId, GetRString (nickFolder.name, NICK_FOLDER), &nickFolder);
theError = FSpDirCreate (&nickFolder, smSystemScript, &dirID);
}
if (!theError) {
IsAlias (&nickFolder, &nickFolder);
theError = FSMakeFSSpec (nickFolder.vRefNum, SpecDirId (&nickFolder), GetRString (vcardFolderName, VCARD_FOLDER), &vcardFolder);
// Create the vCards Folder if needed
if (theError == fnfErr)
theError = FSpDirCreate (&vcardFolder, smSystemScript, &dirID);
}
// Make an FSSpec for the vCard itself
if (!theError) {
IsAlias (&vcardFolder, &vcardFolder);
theError = FSMakeFSSpec (vcardFolder.vRefNum, SpecDirId (&vcardFolder), MakeVCardFileName (ab, nick, nickname), spec);
if (theError == fnfErr) {
FSpCreateResFile (spec, CREATOR, VCARD_TYPE, smSystemScript);
theError = ResError ();
}
}
// Write the vCard data into the file
if (!theError) {
IsAlias (spec, spec);
theError = Blat (spec, vcardData, false);
}
ZapHandle (vcardData);
}
else
theError = MemError ();
return (theError);
}
#endif
#ifdef DEBUG
void PaintUpdateRgn (WindowPtr theWindow, short color)
{
GrafPtr savePort;
RGBColor oldColor;
RgnHandle rgn;
Point offsetPt;
GetPort (&savePort);
SetPort (GetWindowPort (theWindow));
rgn = MyGetWindowUpdateRegion(theWindow);
GetForeColor (&oldColor);
ForeColor (color);
offsetPt.h = offsetPt.v = 0;
GlobalToLocal (&offsetPt);
OffsetRgn (rgn, offsetPt.h, offsetPt.v);
Debugger ();
PaintRgn (rgn);
RGBForeColor (&oldColor);
OffsetRgn(rgn, -offsetPt.h, -offsetPt.v);
SetPort (savePort);
}
#endif
/************************************************************************
* ABUnselectCurrentNickname - unselect the currently selected nickname
************************************************************************/
void ABUnselectCurrentNickname(void)
{
short ab, nick;
Str31 massagedName;
if (LVCountSelection (&NickList) == 1)
{
ab = AB.abDisplayed;
nick = AB.nickDisplayed;
if (ab != kAddressBookUndefined && nick != kNickUndefined)
{
GetListNameBasedOnTag (SortTag, ab, nick, massagedName);
LVUnselect (&NickList, MakeNodeID (ab, nick), massagedName, true);
}
}
}
//
// SortAddressBooks
//
// Sort regular address books. It is currently assumed that
// these are in order.
//
void SortAddressBookList (void)
{
short totalAB,
ab,
firstRegular,
lastRegular;
firstRegular = kAddressBookUndefined;
lastRegular = kAddressBookUndefined;
totalAB = NAliases;
ab = 0;
while (ab < totalAB && firstRegular == kAddressBookUndefined) {
if (IsRegularAddressBook (ab))
firstRegular = ab;
++ab;
}
ab = totalAB - 1;
while (ab >= 0 && lastRegular == kAddressBookUndefined) {
if (IsRegularAddressBook (ab))
lastRegular = ab;
--ab;
}
if (ValidAddressBook (firstRegular) && ValidAddressBook (lastRegular) && firstRegular != lastRegular) {
QuickSort (LDRef (Aliases), sizeof (AliasDesc), firstRegular, lastRegular, (void*) abCompare,(void*) abSwap);
UL (Aliases);
}
}
int abCompare (AliasDPtr adp1, AliasDPtr adp2)
{
return (StringComp (adp1->spec.name, adp2->spec.name));
}
void abSwap (AliasDPtr adp1, AliasDPtr adp2)
{
AliasDesc temp = *adp2;
*adp2 = *adp1;
*adp1 = temp;
}
Boolean ABIsChattable(void)
{
short index = 1;
short ab;
short nick;
Boolean retVal = false;
Str31 aim, aim2;
Str255 value;
if (GetOSVersion() < 0x1030) return false;
GetRString(aim,ABReservedTagsStrn+abTagAIM);
GetRString(aim2,ABReservedTagsStrn+abTagAIM2);
while (GetSelectedABNick (index++, &ab, &nick))
if (nick >= 0) {
if (!(*GetTaggedFieldValueStr(ab, nick, aim, value) || *GetTaggedFieldValueStr(ab, nick, aim2, value))) return false;
else retVal = true;
}
return retVal;
}
OSErr ABChat(short modifiers)
{
short ab;
short nick;
Boolean retVal = false;
Str31 aim, aim2;
Str255 value, url;
GetRString(aim,ABReservedTagsStrn+abTagAIM);
GetRString(aim2,ABReservedTagsStrn+abTagAIM2);
if (GetSelectedABNick (1, &ab, &nick))
{
if (*GetTaggedFieldValueStr(ab, nick, aim, value) || *GetTaggedFieldValueStr(ab, nick, aim2, value))
{
ComposeRString(url,AIM_URL_FMT,URLEscape(value));
return OpenOtherURLPtr(GetRString(aim,AIM_PROTO),url+1,*url);
}
}
return fnfErr;
}