1 line
229 KiB
C
Executable File
1 line
229 KiB
C
Executable File
/* 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, ©Text);
|
||
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, ¤tMouse, nil)) {
|
||
GlobalToLocal (¤tMouse);
|
||
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, ¬es);
|
||
|
||
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 (¬es);
|
||
|
||
}
|
||
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 (¬es);
|
||
}
|
||
}
|
||
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, ¬es);
|
||
}
|
||
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;
|
||
}
|