/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "xp.h" #include "abcpane.h" #include "abcinfo.h" #include "abpane2.h" #include "xp_qsort.h" extern "C" { extern int MK_MSG_ADD_NAME; extern int MK_MSG_ADD_LIST; extern int XP_BKMKS_IMPORT_ADDRBOOK; extern int XP_BKMKS_SAVE_ADDRBOOK; extern int MK_MSG_CALL; extern int MK_ADDR_NO_EMAIL_ADDRESS; extern int MK_ADDR_DEFAULT_EXPORT_FILENAME; extern int MK_ADDR_ADD; extern int MK_MSG_ADDRESS_BOOK; extern int MK_ACCESS_NAME; extern int MK_MSG_PROPERTIES; extern int XP_FILTER_DELETE; extern int MK_ADDR_DELETE_ALL; } /********************************************************************************************* Notification handlers **********************************************************************************************/ void AB_ContainerPane::OnAnnouncerGoingAway(AB_ContainerAnnouncer * instigator) { // do we need to remove the container from our view? Probably not as this will generate needless notifications // since the container can only go away if we have released our reference count to it. Which in turn means that // we are going away. Thus, no need to remove the container from our view...but we do want to remove ourselves // as a listener to the announcer for clean up purposes.. // determine if the ctr is in the ctr pane. If it is, remove and delete it MSG_ViewIndex index = m_containerView.FindIndex (0, (void *) instigator); if (index != MSG_VIEWINDEXNONE) // did it find a match? { AB_ContainerInfo * ctrToRemove = GetContainerForIndex((MSG_ViewIndex) index); if (ctrToRemove) { StartingUpdate(MSG_NotifyInsertOrDelete, (MSG_ViewIndex) index, -1); m_containerView.RemoveAt(index); EndingUpdate(MSG_NotifyInsertOrDelete, (MSG_ViewIndex) index, -1); ctrToRemove->RemoveListener(this); ctrToRemove->Release(); } } } void AB_ContainerPane::OnContainerAttribChange(AB_ContainerInfo * ctr, AB_NOTIFY_CODE code, AB_ContainerListener * instigator) { if (instigator != this) // if we changed it then don't generate another update { // find the index for the ctr MSG_ViewIndex index = GetIndexForContainer(ctr); if (index != MSG_VIEWINDEXNONE) // does it really exist? { if (code == AB_NotifyPropertyChanged) { // update the front ends for this view index StartingUpdate(MSG_NotifyChanged, index, 1); EndingUpdate(MSG_NotifyChanged, index, 1); } } } return; } void AB_ContainerPane::OnContainerEntryChange(AB_ContainerInfo * ctr, AB_NOTIFY_CODE code, ABID entryID, AB_ContainerListener * instigator) { // do we have to do anything if the entry was a mailing list? It might effect something in the container pane.... // need to find out if the effected entry is a mailing list currently in the container pane if (instigator != this) { MSG_ViewIndex index = GetIndexForContainer(ctr); // make sure we care about this container... if (index != MSG_VIEWINDEXNONE /* && IsExpanded(index) */) // only time ctr is showing entry content: when it is expanded { XP_Bool isAList = FALSE; AB_AttributeValue * value = NULL; switch (code) { case AB_NotifyInserted: if (ctr && entryID != AB_ABIDUNKNOWN) ctr->GetEntryAttribute(this, entryID, AB_attribEntryType, & value); if (value && value->u.entryType == AB_MailingList) { isAList = TRUE; AB_FreeEntryAttributeValue(value); } if (isAList) { StartingUpdate(MSG_NotifyChanged, index, 1); // if entry was a list, we need to insert it into the view if (IsExpanded(index)) InsertEntryIntoExpandedList(index, ctr, entryID); EndingUpdate(MSG_NotifyChanged, index, 1); } break; case AB_NotifyAll: case AB_NotifyDeleted: // if an entry was deleted from the ctr it may effect the flippy state of the ctr StartingUpdate(MSG_NotifyChanged, index, 1); EndingUpdate(MSG_NotifyChanged, index, 1); break; default: break; } } } } void AB_ContainerPane::DeleteEntryFromExpandedList(MSG_ViewIndex index, AB_ContainerInfo * ctr, ABID listID) { // make sure listID is a list or else you will be wasting your time. if (index != MSG_VIEWINDEXNONE && ctr && listID != AB_ABIDUNKNOWN && IsExpanded(index)) { uint32 numVisibleChildren = NumVisibleChildrenForIndex(index); // get array of child ctrs int32 numChildren = GetNumChildrenForIndex(index); AB_ContainerInfo ** arrayOfContainers = (AB_ContainerInfo **) XP_ALLOC(sizeof(AB_ContainerInfo *) * numChildren); if (arrayOfContainers) { ctr->AcquireChildContainers(arrayOfContainers, &numChildren); // acquire containers for the children uint32 cnt = 0; for (MSG_ViewIndex pos = index + 1; pos < (uint32) m_containerView.GetSize() && cnt < numVisibleChildren; cnt++, pos++) { AB_ContainerInfo * visibleCtr = GetContainerForIndex(pos); // search for ctr in child ctr array XP_Bool found = FALSE; for (int32 i = 0; i < numChildren; i++) if (arrayOfContainers[i] == visibleCtr) found = TRUE; if (!found && visibleCtr) // then list has gone away so remove it... { StartingUpdate(MSG_NotifyInsertOrDelete, pos, -1); m_containerView.RemoveAt(pos); visibleCtr->RemoveListener(this); // register ourselves as a listener on the container... visibleCtr->Release(); EndingUpdate(MSG_NotifyInsertOrDelete, pos, -1); pos--; // decrement position because we removed something from the view } } // release child ctrs for (int32 i = 0; i < numChildren; i++) if (arrayOfContainers[i]) arrayOfContainers[i]->Release(); XP_FREE(arrayOfContainers); } } } uint32 AB_ContainerPane::NumVisibleChildrenForIndex(MSG_ViewIndex index) // a helper function used by InsertEntryIntoExpandedList and DeleteEntryFromExpandedList to determine how many visible children // are currently showing in the view. { // children are always mailing lists.... uint32 numVisibleChildren = 0; XP_Bool finished = FALSE; for (MSG_ViewIndex pos = index+1; pos < (uint32) m_containerView.GetSize() && !finished; pos++) { AB_ContainerInfo * child = GetContainerForIndex(pos); if (child && child->IsDirectory()) finished = TRUE; else numVisibleChildren++; } return numVisibleChildren; } void AB_ContainerPane::InsertEntryIntoExpandedList(MSG_ViewIndex index /* index for ctr */, AB_ContainerInfo * ctr, ABID listID) { // you should make sure entryID is a mailing list inside of ctr b4 calling this. // you woon't get burned if you don't, but you will be wasting your time.... // *sigh* I think this function is way too complicated for its function but cannot think of a // faster way if (index != MSG_VIEWINDEXNONE && ctr && listID != AB_ABIDUNKNOWN) { // get child array for ctr, find ctr with our entryID. its position in the array // determines where it should be inserted into the expanded list... // first, determine number of children currently in the view... uint32 numVisibleChildren = NumVisibleChildrenForIndex(index); // find position of inserted entry in the list AB_ContainerInfo * newCtr = NULL; AB_ContainerInfo::Create(m_context, ctr, listID, &newCtr); // make sure newCtr is not already in our container pane...this can happen when you add a new mailng list // to another mailing list. We get an insert from the address book and we get an insert from the mailing // list ctr we are adding it to.... MSG_ViewIndex currentIndex = GetIndexForContainer(newCtr); if (newCtr && currentIndex == MSG_VIEWINDEXNONE) // does the new mailing list really exist and is it not in our view alreaady { // now get the child ctrs for the ctr int32 numChildren = GetNumChildrenForIndex(index); AB_ContainerInfo ** arrayOfContainers = (AB_ContainerInfo **) XP_ALLOC(sizeof(AB_ContainerInfo *) * numChildren); if (arrayOfContainers) { ctr->AcquireChildContainers(arrayOfContainers, &numChildren); // acquire containers for the childre // find position of our new ctr in the child array... uint32 offset = 0; XP_Bool found = FALSE; for (offset = 0; offset < (uint32) numChildren && !found; offset++) if (arrayOfContainers[offset] == newCtr) // same object? found = TRUE; if (found) // if it wasn't in the list then something very bad happened { if (offset > numVisibleChildren) // reset our offset if it is > than the # children currently showing. offset = numVisibleChildren; MSG_ViewIndex insertPosition = offset + index + 1; // start with index for first child ctr & add our offset to this StartingUpdate(MSG_NotifyInsertOrDelete, insertPosition, 1); m_containerView.InsertAt(insertPosition, (void *) newCtr); newCtr->AddListener(this); // register ourselves as a listener on the container... EndingUpdate(MSG_NotifyInsertOrDelete, insertPosition,1); } else newCtr->Release(); // now release all acquired children for (int32 i = 0; i < numChildren; i++) if (arrayOfContainers[i]) arrayOfContainers[i]->Release(); XP_FREE(arrayOfContainers); } } else if (newCtr) newCtr->Release(); } } AB_ContainerPane::AB_ContainerPane(MWContext * context, MSG_Master * master) : MSG_LinedPane(context, master), AB_ABIDBasedContainerListener(), AB_PaneAnnouncer() { m_context = context; m_master = master; } AB_ContainerPane::~AB_ContainerPane() { NotifyAnnouncerGoingAway(this); // let any pane listener's know we are going away! i.e. any property sheets opened from the ctr pane // we are no longer listeners on the containers since we are going away, // let's remove ourselves! for (int i = 0; i < m_containerView.GetSize(); i++) { AB_ContainerInfo * ctr = m_containerView.GetAt(i); if (ctr) { ctr->RemoveListener(this); ctr->Release(); // release our reference count to this container } } // make sure our new directory server cache is empty! for (int j = 0; j < m_newEntries.GetSize(); j++) { AB_NewDIREntry * entry = m_newEntries.GetAt(j); if (entry) XP_FREE(entry); } } int AB_ContainerPane::Close(AB_ContainerPane * pane) { if (pane) delete pane; return AB_SUCCESS; } /* static */ void AB_ContainerPane::Create(MSG_Pane ** abcPane, MWContext * context, MSG_Master * master) { AB_ContainerPane * newPane = new AB_ContainerPane(context, master); (*abcPane) = newPane; } int AB_ContainerPane::Initialize() { // how do we get the global list of DIR_Servers?? XP_List * dirServers = DIR_GetDirServers(); // get ref counted list of the dir servers... int errorCode = AB_SUCCESS; DIR_Server * server = NULL; do { server = (DIR_Server *) XP_ListNextObject(dirServers); if (server) errorCode = AddDirectory(server); } while (server); // if we had a memory problem or something, we'll catch the last one and return it. return errorCode; // will eventually load up the global containers from the database } int AB_ContainerPane::GetNumRootContainers(int32 * numRootContainers) { // eventually we want to go through all containers in view and count all root ones... // for now, all ctrs in the pane are "root" container because I'm not supporting mailing lists // yet. int numRootCtrs = 0; for (int i = 0; i < m_containerView.GetSize(); i++) if (m_containerView.GetAt(i)->IsDirectory()) // directory containers are root containers! numRootCtrs++; *numRootContainers = numRootCtrs; return AB_SUCCESS; } int AB_ContainerPane::GetOrderedRootContainers(AB_ContainerInfo ** ctrArray, int32 * numCtrs) { // will eventually traverse all containers in the view and insert container info into array // if it is a root container... // FE has allocated this array so just fill it. int32 i; int32 numAdded = 0; for (i = 0; i < m_containerView.GetSize() && numAdded < *numCtrs; i++) if (m_containerView.GetAt(i)->IsDirectory()) ctrArray[numAdded++] = m_containerView.GetAt(i); *numCtrs = numAdded; return AB_SUCCESS; } void AB_ContainerPane::NotifyPrefsChange (NotifyCode /* code */) { return; } MSG_ViewIndex AB_ContainerPane::GetIndexForContainer(AB_ContainerInfo * ctr) { if (ctr) { // look for the pointer in our view for (int i = 0; i < m_containerView.GetSize(); i++) if (ctr == m_containerView.GetAt(i)) return i; // found the index! } return MSG_VIEWINDEXNONE; } AB_ContainerInfo * AB_ContainerPane::GetContainerForIndex(const MSG_ViewIndex index) { // look for the index in our container view. if (m_containerView.IsValidIndex(index)) return m_containerView.GetAt(index); else return NULL; } int AB_ContainerPane::UpdateDIRServer(DIR_Server * directory) { AB_ContainerInfo * ctr; XP_Bool match = FALSE; int errorCode = AB_SUCCESS; // examine the DIR_Server for each container in the pane for (int i = 0; i < m_containerView.GetSize() && !match; i++) { ctr = m_containerView.GetAt(i); if (ctr->IsDirectory()) // PAB or LDAP if (directory == ctr->GetDIRServer()) { match = TRUE; ctr->UpdateDIRServer(); } } if (!match) // if we didn't find it, we need to create a new container! errorCode = AddNewDirectory(directory); return errorCode; } // always use this routine when adding directories AFTER we have already built the container pane.. // that is any new directories that need turned into containers and added to the DIR_Server list. int AB_ContainerPane::AddNewDirectory(DIR_Server * directory) // add this to the end of the container pane { AB_ContainerInfo * ctr = NULL; DIR_SetServerFileName(directory, directory->fileName); // generates a file name if there isn't one already int errorCode = AB_ContainerInfo::Create(m_context, directory, &ctr); // all ctr info's are referenced counted if (errorCode == AB_SUCCESS) { // look up dir server in our cache to get the positiion MSG_ViewIndex position = GetDesiredPositionForNewServer(directory); RemoveFromNewServerCache(directory); AddNewContainer(position, ctr); } return errorCode; } // Always use this routine when all we want to do is turn directory into a container and add it to the end of // the view. It is only called during Initialization!!! DIR_Server list is untouched!! // USE AddNewDirectory to add directories not part of the initialization process! int AB_ContainerPane::AddDirectory(DIR_Server * directory) { AB_ContainerInfo * ctr = NULL; int errorCode = AB_ContainerInfo::Create(m_context, directory, &ctr); // all ctr info's are referenced counted int position = m_containerView.GetSize(); if (ctr) { StartingUpdate(MSG_NotifyInsertOrDelete, position, 1); m_containerView.Add(ctr); ctr->AddListener(this); EndingUpdate(MSG_NotifyInsertOrDelete, position, 1); } return errorCode; } int AB_ContainerPane::GetContainerAttributes(MSG_ViewIndex index, AB_ContainerAttribute * attribsArray, AB_ContainerAttribValue ** valuesArray, uint16 * numItems) { AB_ContainerInfo * ctr = GetContainerForIndex(index); if (ctr) return ctr->GetAttributes(attribsArray, valuesArray, numItems); else return AB_INVALID_CONTAINER; } int AB_ContainerPane::SetContainerAttributes(MSG_ViewIndex index, AB_ContainerAttribValue * valuesArray, uint16 numItems) { AB_ContainerInfo *ctr = GetContainerForIndex(index); if (ctr) return ctr->SetAttributes(valuesArray, numItems); else return AB_INVALID_CONTAINER; } MSG_PaneType AB_ContainerPane::GetPaneType() { return AB_CONTAINERPANE; } int32 AB_ContainerPane::GetNumChildrenForIndex(MSG_ViewIndex line) { AB_ContainerInfo * ctr = GetContainerForIndex(line); if (ctr) return ctr->GetNumChildContainers(); // we could go throug the attribute value methods but this is faster.. else return 0; } void AB_ContainerPane::ToggleExpansion(MSG_ViewIndex line, int32 * numChanged) { StartingUpdate(MSG_NotifyChanged, line, 1); if (IsExpanded(line)) CollapseContainer(line, numChanged); else ExpandContainer(line, numChanged); // now do a notify changed on the modified line... EndingUpdate(MSG_NotifyChanged, line, 1); } // returns TRUE if the container is expanded. FALSE otherwise. XP_Bool AB_ContainerPane::IsExpanded(MSG_ViewIndex line) { AB_ContainerInfo * ctr = GetContainerForIndex(line); if (ctr) { AB_ContainerInfo * nextCtr = GetContainerForIndex(++line); // I'm going to try a quick hack..if the next ctr is a mailing list then it must be expanded already // Why? because non directory containers are the only ctrs which are not root containers. // If it doesn't work, we can always write a more complicated method here.. if (nextCtr && !nextCtr->IsDirectory()) return TRUE; // next ctr is a non-directory ctr so we must be expanded. } return FALSE; } int32 AB_ContainerPane::ExpansionDelta(MSG_ViewIndex line) { AB_ContainerInfo * ctr = GetContainerForIndex(line); int32 numChildren = 0; if (ctr) // does it exist? { // how many children does the container have.... numChildren = GetNumChildrenForIndex(line); if (numChildren > 0) { if (IsExpanded(line)) return - (numChildren); else return numChildren; } } return 0; // if we fell out then assume 0 children } int32 AB_ContainerPane::GetNumLines() { return m_containerView.GetSize(); } int32 AB_ContainerPane::GetContainerMaxDepth() { return 0; } void AB_ContainerPane::CollapseContainer(MSG_ViewIndex line, int32 * numChanged) { int32 changedCount = 0; // # ctrs removed... // first, make sure it is expanded..redundant call I know, but you never know! if (IsExpanded(line)) { // how many children does the container have.... int32 numChildren = GetNumChildrenForIndex(line); if (numChildren > 0) { // okay, remove each ctr from the view and release it... StartingUpdate(MSG_NotifyInsertOrDelete, line+1, -1 * numChildren); MSG_ViewIndex removeLine = line+1; for (int32 i = 0; i < numChildren; i++) { AB_ContainerInfo * childCtr = GetContainerForIndex(removeLine); // remove line doesn't change... if (childCtr) { m_containerView.Remove(childCtr); childCtr->RemoveListener(this); childCtr->Release(); // we are done with it...release it changedCount++; } } EndingUpdate(MSG_NotifyInsertOrDelete, line+1, -1 * numChildren); } } if (numChanged) *numChanged = changedCount; } void AB_ContainerPane::ExpandContainer(MSG_ViewIndex line, int32 * /*numChanged*/) { // we are going to write this method to assume that the line could already be expanded, // but the expansion may not be up to date. (More or fewer). So we will generate // individual notifications as lines are added or removed during the expansion. AB_ContainerInfo * ctr = GetContainerForIndex(line); if (!IsExpanded(line) && ctr) // make sure we aren't already expanded { int32 numChildren = GetNumChildrenForIndex(line); // we need to get ctr info's for all of the children! if (numChildren > 0) { AB_ContainerInfo ** arrayOfContainers = (AB_ContainerInfo **) XP_ALLOC(sizeof(AB_ContainerInfo *) * numChildren); if (arrayOfContainers) { MSG_ViewIndex startPosition = line+1; StartingUpdate(MSG_NotifyInsertOrDelete, startPosition, numChildren); // generate notification for FEs ctr->AcquireChildContainers(arrayOfContainers, &numChildren); // acquire containers for the childre for (int i = 0; i < numChildren; i++) // add each container to our pane if (arrayOfContainers[i]) { m_containerView.InsertAt(++line, (void *) arrayOfContainers[i]); ((AB_ContainerInfo *) arrayOfContainers[i])->AddListener(this); // add ourselves to the listener list } EndingUpdate(MSG_NotifyInsertOrDelete, startPosition, numChildren); XP_FREE(arrayOfContainers); } } } } XP_Bool AB_ContainerPane::AddToNewServerCache(DIR_Server * server, MSG_ViewIndex desiredPosition) // we just created a new DIR_Server. It will be committed to the view at some later time, but for now // we want to save the position where it should be inserted! { AB_NewDIREntry * entry = (AB_NewDIREntry *) XP_ALLOC(sizeof(AB_NewDIREntry)); if (entry) { entry->server = server; entry->desiredPosition = desiredPosition; m_newEntries.Add(entry); return TRUE; } return FALSE; } XP_Bool AB_ContainerPane::RemoveFromNewServerCache(DIR_Server * server) { for (int i = 0; i < m_newEntries.GetSize(); i++) { AB_NewDIREntry * entry = m_newEntries.GetAt(i); if (entry) if (entry->server == server) { m_newEntries.RemoveAt(i); XP_FREE(entry); return TRUE; } } return FALSE; } MSG_ViewIndex AB_ContainerPane::GetDesiredPositionForNewServer(DIR_Server * server) { if (server) { for (int i = 0; i < m_newEntries.GetSize(); i++) { AB_NewDIREntry * entry = m_newEntries.GetAt(i); if (entry) if (entry->server == server) return entry->desiredPosition; } } return MSG_VIEWINDEXNONE; } MSG_ViewIndex AB_ContainerPane::ExtractInsertPosition(MSG_ViewIndex desiredPosition) { // now find out if known position is a directory or not...if not advance until we hit the end or we hit // a directory container (LDAP or PAB) XP_Bool found = FALSE; MSG_ViewIndex position = desiredPosition; position++; // we want to insert it after whatever the selected position was... AB_ContainerInfo * insertAfterCtr = NULL; while (m_containerView.IsValidIndex(position) && !found && position != MSG_VIEWINDEXNONE) { insertAfterCtr = GetContainerForIndex(position); if (insertAfterCtr && insertAfterCtr->IsDirectory()) found = TRUE; else position++; } if (m_containerView.IsValidIndex(position) && position != MSG_VIEWINDEXNONE) // are we still a valid insertion index? return position; else return MSG_VIEWINDEXNONE; } DIR_Server * AB_ContainerPane::ExtractServerPosition(MSG_ViewIndex desiredPosition) { // okay, we need to find the directory container which is at or comes before the desired position XP_Bool found = FALSE; MSG_ViewIndex position = desiredPosition; AB_ContainerInfo * insertAfterCtr = NULL; while (m_containerView.IsValidIndex(position) && position != MSG_VIEWINDEXNONE && !found) { insertAfterCtr = GetContainerForIndex(position); if (insertAfterCtr && insertAfterCtr->IsDirectory()) found = TRUE; else position--; // go back to the previous container } if (found) return insertAfterCtr->GetDIRServer(); else return NULL; // will insert at bottom of the list } void AB_ContainerPane::AddNewContainer(MSG_ViewIndex position, AB_ContainerInfo * ctr) // Call this when you want to insert a new PAB or LDAP directory into the container pane. We attempt to // insert the container in the position AFTER the one provided. This method // also modifies the DIR_Servers list, adding the new directory as well.Assumes ctr has already been ref counted!!!!! { if (ctr) { if (ctr->IsDirectory()) { // okay, finding the insertion point is complicated by the fact that we must insert after the last // directory container which comes after the position index and not just the container the position index // points too. // we have two positions: (1) position in the DIR_Server list (not the same as the ctr pane view which shows // mailing lists and (2) the index in the ctr pane to insert the new ctr into DIR_Server * insertAfter= ExtractServerPosition(position); XP_List * servers = DIR_GetDirServers(); XP_ListInsertObjectAfter(servers, insertAfter, ctr->GetDIRServer()); // add to DIR_Server List // now save the list... DIR_SaveServerPreferences(servers); } MSG_ViewIndex insertPosition = ExtractInsertPosition(position); // now add it to our container view as well. insertPosition = insertPosition == MSG_VIEWINDEXNONE ? m_containerView.GetSize() : insertPosition; StartingUpdate(MSG_NotifyInsertOrDelete, insertPosition, 1); m_containerView.InsertAt(insertPosition, (void *) ctr); ctr->AddListener(this); // register ourselves as a listener on the container... EndingUpdate(MSG_NotifyInsertOrDelete, insertPosition,1); } } int AB_ContainerPane::ShowProperties(MSG_ViewIndex * indices, int32 numIndices) { for (int32 i = 0; i < numIndices; i++) { AB_ContainerInfo * ctr = GetContainerForIndex(indices[i]); if (ctr) if (ctr->GetType() == AB_MListContainer) ShowPropertySheet(&indices[i], 1); else ShowDirectoryProperties(&indices[i], 1); } return AB_SUCCESS; } int AB_ContainerPane::ShowDirectoryProperties(MSG_ViewIndex * indices, int32 numIndices) { // general comment: we don't care if the directory is a PAB or LDAP directory. They all look the // same at this level. if (m_DirectoryPropSheetFunc) { for (int i = 0; i < numIndices; i++) { AB_ContainerInfo * ctr = GetContainerForIndex(indices[i]); if (ctr) if (ctr->IsDirectory()) { DIR_Server * server = ctr->GetDIRServer(); if (server) m_DirectoryPropSheetFunc(server, m_context, this, FALSE); } } } return AB_SUCCESS; } int AB_ContainerPane::ShowNewDirectoryProperties(AB_CommandType cmd, MSG_ViewIndex * indices, int32 numIndices) { // if numIndices > 1 then stick them at the end.... // if we have zero indices, stick them at the end... // if we have just one index selected, insert it underneath the given index MSG_ViewIndex position = MSG_VIEWINDEXNONE; if (numIndices == 1) position = indices[0]; // can only show one new directory at a time if (m_DirectoryPropSheetFunc) // make sure we have a FE call back! { DIR_Server * server = (DIR_Server *) XP_ALLOC(sizeof(DIR_Server)); if (server) { DIR_InitServer(server); server->description = NULL; // FE will fill this field in // only valid commands coming in here are AB_NewLDAPDirectory or AB_NewPABDirectory server->dirType = (cmd == AB_NewLDAPDirectory) ? LDAPDirectory : PABDirectory; server->isOffline = (cmd == AB_NewLDAPDirectory) ? TRUE : FALSE; // set offline to false for PABs... // add the server and position pair to our cache, so we will remember when we go to really add it!!! AddToNewServerCache(server, position); m_DirectoryPropSheetFunc(server, m_context, this, TRUE /* we are a new server */); } } return AB_SUCCESS; } void AB_ContainerPane::RemoveAndCloseDirectory(DIR_Server * server) { if (server) { XP_List * servers = DIR_GetDirServers(); XP_ListRemoveObject(servers, server); // removes from original DIR_Server list // the question is....does anyone need to free the DIR_Server object now that we // are all done with it? DIR_DeleteServer(server); DIR_SaveServerPreferences(servers); // make sure the removal is committed } } int AB_ContainerPane::DeleteContainers(MSG_ViewIndex * indices, int32 numIndices) { for (int32 i = 0; i < numIndices; i++) { AB_ContainerInfo * ctr = GetContainerForIndex(indices[i]); if (ctr) /* mlist qualifier just temporary */ { // if the container is a directory container, we need to remove it from the DIR_Server list! if (ctr->IsDirectory()) { // first, if the container is expanded, collapse it... if (IsExpanded(indices[i])) CollapseContainer(indices[i], NULL /* we don't care how many changed */); StartingUpdate(MSG_NotifyInsertOrDelete, indices[i], -1); m_containerView.Remove(ctr); RemoveAndCloseDirectory(ctr->GetDIRServer()); ctr->RemoveListener(this); ctr->Release(); // we are done with this container EndingUpdate(MSG_NotifyInsertOrDelete, indices[i], -1); } else // if it is a mailing list, delete it from the parent and we'll get our notification to // delete self that way... if (ctr->GetType() == AB_MListContainer) { AB_MListContainerInfo *mListCtr = ctr->GetMListContainerInfo(); if (mListCtr) mListCtr->DeleteSelfFromParent(this); } } } return AB_SUCCESS; } int AB_ContainerPane::ShowPropertySheetForNewType(AB_EntryType entryType, MSG_ViewIndex index /* index for ctr to show sheet for */) { int status = AB_SUCCESS; MSG_Pane * pane = NULL; AB_ContainerInfo * ctr = GetContainerForIndex(index); // creating pane will ref count ctr if (ctr && (ctr->GetType() != AB_LDAPContainer)) // cannot add entries or lists to LDAP. { if (entryType == AB_MailingList) pane = new AB_MailingListPane(m_context, m_master, ctr, AB_ABIDUNKNOWN); // ref counts the ctr else if (entryType == AB_Person) pane = new AB_PersonPane(m_context, m_master, ctr, AB_ABIDUNKNOWN, this); } if (pane && m_entryPropSheetFunc) m_entryPropSheetFunc(pane, m_context); // non-blocking FE call back else { AB_ClosePane(pane); // otherwise, close person pane because it is unused.... status = AB_FAILURE; } return status; } int AB_ContainerPane::ShowPropertySheet(MSG_ViewIndex * indices, int32 numIndices) { int status = AB_SUCCESS; for (int i = 0; i < numIndices; i++) { // if we are a picker pane, m_container is not necessarily the ctr to use... // could also have a mixture of property sheet types to be shown.. AB_ContainerInfo * ctrToUse = GetContainerForIndex(indices[i]); MSG_Pane * pane = NULL; if (ctrToUse && ctrToUse->GetType() == AB_MListContainer) { pane = new AB_MailingListPane(m_context, m_master, ctrToUse->GetMListContainerInfo()); } if (pane && m_entryPropSheetFunc) m_entryPropSheetFunc(pane, m_context); // non-blocking FE call back else { AB_ClosePane(pane); // otherwise, close person pane because it is unused.... status = AB_FAILURE; } ctrToUse = NULL; } // for each index return status; } /***************************************************************************** Container Pane -> Do command and command status ******************************************************************************/ ABErr AB_ContainerPane::DoCommand(MSG_CommandType cmd, MSG_ViewIndex * indices, int32 numIndices) { int status = AB_SUCCESS; // what commands should the AB_Container Pane support? switch ((AB_CommandType) cmd) { case AB_NewAddressBook: case AB_NewLDAPDirectory: case AB_PropertiesCmd: case AB_DeleteCmd: // we need to sort the indices for deleting to make sure we delete the correct indices first... if (numIndices > 1) XP_QSORT (indices, numIndices, sizeof(MSG_ViewIndex), CompareViewIndices); ApplyToIndices((AB_CommandType)cmd, indices, numIndices); break; case AB_AddMailingListCmd: if (numIndices == 1) // we only do this for one selection status = ShowPropertySheetForNewType(AB_MailingList, indices[0]); break; case AB_AddUserCmd: if (numIndices == 1) // we only do this for one selection status = ShowPropertySheetForNewType(AB_Person, indices[0]); break; default: status = AB_INVALID_COMMAND; // we do not recognize the command... break; } return status; } int AB_ContainerPane::ApplyToIndices(AB_CommandType cmd, MSG_ViewIndex * indices, int32 numIndices) { int status = AB_FAILURE; switch(cmd) { case AB_NewAddressBook: case AB_NewLDAPDirectory: status = ShowNewDirectoryProperties(cmd, indices, numIndices); break; case AB_PropertiesCmd: status = ShowProperties(indices, numIndices); break; case AB_DeleteCmd: StartingUpdate(MSG_NotifyNone, 0, 0); status = DeleteContainers(indices, numIndices); EndingUpdate(MSG_NotifyNone, 0, 0); break; default: XP_ASSERT(0); break; } return status; } ABErr AB_ContainerPane::GetCommandStatus(MSG_CommandType cmd, const MSG_ViewIndex * indices, int32 numIndices, XP_Bool * isSelectableState, MSG_COMMAND_CHECK_STATE * checkedState , const char ** displayString, XP_Bool * pluralState) { int status = AB_SUCCESS; // for each cmd, we need to determine if it is (1) enabled, (2) its checked state and (3) display string to use MSG_COMMAND_CHECK_STATE state = MSG_NotUsed; // most of our pane commands do not have a check state const char * displayText = NULL; XP_Bool enabled = FALSE; XP_Bool selected = (numIndices > 0); // anything selected? XP_Bool plural = FALSE; XP_Bool isLDAPContainer = FALSE; AB_ContainerInfo * ctr = NULL; if (numIndices == 1) { ctr = GetContainerForIndex(indices[0]); if (ctr) isLDAPContainer = ctr->GetType() == AB_LDAPContainer; } switch ((AB_CommandType) cmd) { case AB_NewAddressBook: displayText = XP_GetString(MK_MSG_ADDRESS_BOOK); enabled = TRUE; break; case AB_NewLDAPDirectory: displayText = XP_GetString(MK_ADDR_ADD); // note to self: verify that this is the correct resource string enabled = TRUE; break; case AB_PropertiesCmd: displayText = XP_GetString(MK_MSG_PROPERTIES); enabled = (numIndices == 1); // disable properties if multple selections break; case AB_DeleteCmd: displayText = XP_GetString(XP_FILTER_DELETE); enabled = selected; break; case AB_DeleteAllCmd: displayText = XP_GetString(MK_ADDR_DELETE_ALL); enabled = selected; break; case AB_AddUserCmd: displayText = XP_GetString(MK_MSG_ADD_NAME); enabled = (numIndices == 1) && !isLDAPContainer && ctr; // only enable if we have a non-LDAP directory container selected break; case AB_AddMailingListCmd: displayText = XP_GetString(MK_MSG_ADD_LIST); enabled = (numIndices == 1) && !isLDAPContainer && ctr; enabled = (numIndices == 1) && !isLDAPContainer && ctr; // not implemented yet break; default: status = AB_INVALID_COMMAND; // It is not a matter of enabling, disabling this command...We just recognize it // we don't know anything about this cmd break; } if (isSelectableState) *isSelectableState = enabled; if (checkedState) *checkedState = state; if (displayString) *displayString = displayText; if (pluralState) *pluralState = plural; return status; } /***************************************************************************** Drag and Drop related APIs *****************************************************************************/ int AB_ContainerPane::DragEntriesIntoContainer(const MSG_ViewIndex * indices, int32 numIndices, AB_ContainerInfo * destContainer, AB_DragEffect /* request */) { // we can really ignore the passed in request because all ofour actions are fixed based on the input ctr // types...That is...there is never a choice between copy, move etc. // some of this code was taken from the status method... if (destContainer && numIndices && indices) { XP_Bool destIsDirectory = destContainer->IsDirectory(); XP_Bool srcCtrsAllLists = TRUE; XP_Bool srcCtrsAllDirectories = TRUE; // determine if srcs are all lists, all directories or neither... for (int32 i = 0; i < numIndices; i++) { AB_ContainerInfo * ctr = GetContainerForIndex(indices[i]); if (ctr) { if (ctr->IsDirectory()) srcCtrsAllLists = FALSE; else srcCtrsAllDirectories = FALSE; } } // if all the src indices are directories, and the dest is a directory...then move all of the selected ctrs in front of the // dest ctr if (srcCtrsAllDirectories && destIsDirectory) ReOrderContainers(indices, numIndices, destContainer); else // ignore case where we are copying lists to other ctrs { if (srcCtrsAllLists && destContainer->AcceptsNewEntries()) // copy lists to the destination ctr { // not immplemented yet } } } return AB_SUCCESS; } AB_DragEffect AB_ContainerPane::DragEntriesIntoContainerStatus(const MSG_ViewIndex * indices, int32 numIndices, AB_ContainerInfo * destContainer, AB_DragEffect request) { AB_DragEffect effect = AB_Drag_Not_Allowed; if (destContainer && numIndices && indices) { XP_Bool destIsDirectory = destContainer->IsDirectory(); XP_Bool srcCtrsAllLists = TRUE; XP_Bool srcCtrsAllDirectories = TRUE; // determine if srcs are all lists, all directories or neither... for (int32 i = 0; i < numIndices; i++) { AB_ContainerInfo * ctr = GetContainerForIndex(indices[i]); if (ctr) { if (ctr->IsDirectory()) srcCtrsAllLists = FALSE; else srcCtrsAllDirectories = FALSE; } } if (srcCtrsAllLists) // you can only copy maiiling lists into others.... effect = (AB_DragEffect) (request & AB_Require_Copy); else if (srcCtrsAllDirectories && destIsDirectory) effect = (AB_DragEffect) (request & AB_Require_Move); } return effect; } int AB_ContainerPane::ReOrderContainers(const MSG_ViewIndex * indices, int32 numIndices, AB_ContainerInfo * destContainer) { // for right now, this method assumes each index is to a directory ctr. It ignores list ctrs...but it does move // all of the directory ctr's children when it reorders MSG_ViewIndex insertIndex = GetIndexForContainer(destContainer); if (insertIndex != MSG_VIEWINDEXNONE) { XP_List * servers = DIR_GetDirServers(); DIR_Server * destServer = destContainer->GetDIRServer(); for (int32 i = 0; i < numIndices; i++) { MSG_ViewIndex index = indices[i]; AB_ContainerInfo * ctr = GetContainerForIndex(index); if (ctr && ctr != destContainer && ctr->IsDirectory()) // ignore mailing list ctrs for now... { // remove it and each of its children, inserting them in front of the src ctr AB_ContainerInfoArray arrayOfCtrs; arrayOfCtrs.Add(ctr); // if ctr is expanded, add each of the children... if (IsExpanded(index)) { MSG_ViewIndex position = index+1; // first child index for (int32 i = 0; i < ctr->GetNumChildContainers(); i++, position++) { AB_ContainerInfo * ctr = GetContainerForIndex(position); if (ctr) arrayOfCtrs.Add(ctr); } } StartingUpdate(MSG_NotifyInsertOrDelete, index, -1*arrayOfCtrs.GetSize()); // generate notification for FEs m_containerView.RemoveAt(index, &arrayOfCtrs); EndingUpdate(MSG_NotifyInsertOrDelete, index, -1*arrayOfCtrs.GetSize()); // now insert it before destCtr insertIndex = GetIndexForContainer(destContainer); // update index count because ctr position could have changed StartingUpdate(MSG_NotifyInsertOrDelete, insertIndex, arrayOfCtrs.GetSize()); // generate notification for FEs m_containerView.InsertAt(insertIndex, &arrayOfCtrs); EndingUpdate(MSG_NotifyInsertOrDelete, insertIndex, arrayOfCtrs.GetSize()); // Don't forget to change its position in the DIR_Server list!!! DIR_Server * srcServer = ctr->GetDIRServer(); if (srcServer && XP_ListRemoveObject(servers, srcServer)) // if the srcServer was in the list... XP_ListInsertObject(servers, destServer, srcServer); // insert it before the destination server.. } } DIR_SaveServerPreferences(servers); // make sure the re-ordering is committed } return AB_SUCCESS; } //******************************************************************************** // Undo/Redo //******************************************************************************** int AB_ContainerPane::Undo() { return AB_SUCCESS; } int AB_ContainerPane::Redo() { return AB_SUCCESS; }