nsresult
nsMsgQuickSearchDBView::ListIdsInThreadOrder(nsIMsgThread *threadHdr,
                                             nsMsgKey parentKey, uint32_t level,
                                             uint32_t callLevel,
                                             nsMsgKey keyToSkip,
                                             nsMsgViewIndex *viewIndex,
                                             uint32_t *pNumListed)
{
  nsCOMPtr <nsISimpleEnumerator> msgEnumerator;
  nsresult rv = threadHdr->EnumerateMessages(parentKey, getter_AddRefs(msgEnumerator));
  NS_ENSURE_SUCCESS(rv, rv);
  
  // We use the numChildren as a sanity check on the thread structure.
  uint32_t numChildren;
  (void) threadHdr->GetNumChildren(&numChildren);
  bool hasMore;
  nsCOMPtr <nsISupports> supports;
  nsCOMPtr <nsIMsgDBHdr> msgHdr;
  while (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv = msgEnumerator->HasMoreElements(&hasMore)) &&
         hasMore)
  {
    rv = msgEnumerator->GetNext(getter_AddRefs(supports));
    if (NS_SUCCEEDED(rv) && supports)
    {
      msgHdr = do_QueryInterface(supports);
      nsMsgKey msgKey;
      msgHdr->GetMessageKey(&msgKey);
      if (msgKey == keyToSkip)
        continue;

      // If we discover depths of more than numChildren, it means we have
      // some sort of circular thread relationship and we bail out of the
      // while loop before overflowing the stack with recursive calls.
      // Technically, this is an error, but forcing a database rebuild
      // is too destructive so we just return.
      if (*pNumListed > numChildren || callLevel > numChildren)
      {
        NS_ERROR("loop in message threading while listing children");
        return NS_OK;
      }

      int32_t childLevel = level;
      if (m_origKeys.BinaryIndexOf(msgKey) != m_origKeys.NoIndex)
      {
        uint32_t msgFlags;
        msgHdr->GetFlags(&msgFlags);
        InsertMsgHdrAt(*viewIndex, msgHdr, msgKey, msgFlags & ~MSG_VIEW_FLAGS, level);
        (*pNumListed)++;
        (*viewIndex)++;
        childLevel++;
      }
      rv = ListIdsInThreadOrder(threadHdr, msgKey, childLevel, callLevel + 1,
                                keyToSkip, viewIndex, pNumListed);
    }
  }
  return rv;
}
nsresult nsMsgQuickSearchDBView::ListIdsInThread(nsIMsgThread *threadHdr, nsMsgViewIndex startOfThreadViewIndex, uint32_t *pNumListed)
{

  if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay && ! (m_viewFlags & nsMsgViewFlagsType::kGroupBySort))
  {
    nsMsgKey parentKey = m_keys[startOfThreadViewIndex++];
    return ListIdsInThreadOrder(threadHdr, parentKey, 1, &startOfThreadViewIndex, pNumListed);
  }

  uint32_t numChildren;
  threadHdr->GetNumChildren(&numChildren);
  uint32_t i;
  uint32_t viewIndex = startOfThreadViewIndex + 1;
  nsCOMPtr<nsIMsgDBHdr> rootHdr;
  nsMsgKey rootKey;
  uint32_t rootFlags = m_flags[startOfThreadViewIndex];
  *pNumListed = 0;
  GetMsgHdrForViewIndex(startOfThreadViewIndex, getter_AddRefs(rootHdr));
  rootHdr->GetMessageKey(&rootKey);
  // group threads can have the root key twice, one for the dummy row.
  bool rootKeySkipped = false;
  for (i = 0; i < numChildren; i++)
  {
    nsCOMPtr<nsIMsgDBHdr> msgHdr;
    threadHdr->GetChildHdrAt(i, getter_AddRefs(msgHdr));
    if (msgHdr != nullptr)
    {
      nsMsgKey msgKey;
      msgHdr->GetMessageKey(&msgKey);
      if (msgKey != rootKey || (GroupViewUsesDummyRow() && rootKeySkipped))
      {
        nsMsgViewIndex threadRootIndex = m_origKeys.BinaryIndexOf(msgKey);
        // if this hdr is in the original view, add it to new view.
        if (threadRootIndex != nsMsgViewIndex_None)
        {
          uint32_t childFlags;
          msgHdr->GetFlags(&childFlags);
          InsertMsgHdrAt(viewIndex, msgHdr, msgKey, childFlags, 
                        FindLevelInThread(msgHdr, startOfThreadViewIndex, viewIndex));
          if (! (rootFlags & MSG_VIEW_FLAG_HASCHILDREN))
            m_flags[startOfThreadViewIndex] = rootFlags | MSG_VIEW_FLAG_HASCHILDREN;

          viewIndex++;
          (*pNumListed)++;
        }
      }
      else
      {
        rootKeySkipped = true;
      }
    }
  }
  return NS_OK;
}
Esempio n. 3
0
nsresult nsMsgSearchDBView::InsertHdrFromFolder(nsIMsgDBHdr *msgHdr, nsIMsgFolder *folder)
{
  nsMsgViewIndex insertIndex = nsMsgViewIndex_None;
  // Threaded view always needs to go through AddHdrFromFolder since
  // it handles the xf view thread object creation.
  if (! (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
    insertIndex = GetInsertIndex(msgHdr);

  if (insertIndex == nsMsgViewIndex_None)
    return AddHdrFromFolder(msgHdr, folder);

  nsMsgKey msgKey;
  uint32_t msgFlags;
  msgHdr->GetMessageKey(&msgKey);
  msgHdr->GetFlags(&msgFlags);
  InsertMsgHdrAt(insertIndex, msgHdr, msgKey, msgFlags, 0);

  // the call to NoteChange() has to happen after we add the key
  // as NoteChange() will call RowCountChanged() which will call our GetRowCount()
  NoteChange(insertIndex, 1, nsMsgViewNotificationCode::insertOrDelete);
  return NS_OK;
}
Esempio n. 4
0
// This method removes the thread at threadIndex from the view 
// and puts it back in its new position, determined by the sort order.
// And, if the selection is affected, save and restore the selection.
void nsMsgSearchDBView::MoveThreadAt(nsMsgViewIndex threadIndex)
{
  bool updatesSuppressed = mSuppressChangeNotification;
  // Turn off tree notifications so that we don't reload the current message.
  if (!updatesSuppressed)
    SetSuppressChangeNotifications(true);

  nsCOMPtr<nsIMsgDBHdr> threadHdr;
  GetMsgHdrForViewIndex(threadIndex, getter_AddRefs(threadHdr));

  uint32_t saveFlags = m_flags[threadIndex];
  bool threadIsExpanded = !(saveFlags & nsMsgMessageFlags::Elided);
  int32_t childCount = 0;
  nsMsgKey preservedKey;
  nsAutoTArray<nsMsgKey, 1> preservedSelection;
  int32_t selectionCount;
  int32_t currentIndex;
  bool hasSelection = mTree && mTreeSelection &&
                        ((NS_SUCCEEDED(mTreeSelection->GetCurrentIndex(&currentIndex)) &&
                         currentIndex >= 0 && (uint32_t)currentIndex < GetSize()) ||
                         (NS_SUCCEEDED(mTreeSelection->GetRangeCount(&selectionCount)) &&
                          selectionCount > 0));


  if (hasSelection)
    SaveAndClearSelection(&preservedKey, preservedSelection);

  if (threadIsExpanded)
  {
    ExpansionDelta(threadIndex, &childCount);
    childCount = -childCount;
  }
  nsTArray<nsMsgKey> threadKeys;
  nsTArray<uint32_t> threadFlags;
  nsTArray<uint8_t> threadLevels;
  nsCOMArray<nsIMsgFolder> threadFolders;

  if (threadIsExpanded)
  {
    threadKeys.SetCapacity(childCount);
    threadFlags.SetCapacity(childCount);
    threadLevels.SetCapacity(childCount);
    threadFolders.SetCapacity(childCount);
    for (nsMsgViewIndex index = threadIndex + 1; 
        index < (nsMsgViewIndex) GetSize() && m_levels[index]; index++)
    {
      threadKeys.AppendElement(m_keys[index]);
      threadFlags.AppendElement(m_flags[index]);
      threadLevels.AppendElement(m_levels[index]);
      threadFolders.AppendObject(m_folders[index]);
    }
    uint32_t collapseCount;
    CollapseByIndex(threadIndex, &collapseCount);
  }
  nsMsgDBView::RemoveByIndex(threadIndex);
  m_folders.RemoveObjectAt(threadIndex);
  nsMsgViewIndex newIndex = GetIndexForThread(threadHdr);
  NS_ASSERTION(newIndex == m_levels.Length() || !m_levels[newIndex],
                "inserting into middle of thread");
  if (newIndex == nsMsgViewIndex_None)
    newIndex = 0;
  nsMsgKey msgKey;
  uint32_t msgFlags;
  threadHdr->GetMessageKey(&msgKey);
  threadHdr->GetFlags(&msgFlags);
  InsertMsgHdrAt(newIndex, threadHdr, msgKey, msgFlags, 0);

  if (threadIsExpanded)
  {
    m_keys.InsertElementsAt(newIndex + 1, threadKeys);
    m_flags.InsertElementsAt(newIndex + 1, threadFlags);
    m_levels.InsertElementsAt(newIndex + 1, threadLevels);
    m_folders.InsertObjectsAt(threadFolders, newIndex + 1);
  }
  m_flags[newIndex] = saveFlags;
  // unfreeze selection.
  if (hasSelection)
    RestoreSelection(preservedKey, preservedSelection);

  if (!updatesSuppressed)
    SetSuppressChangeNotifications(false);
  nsMsgViewIndex lowIndex = threadIndex < newIndex ? threadIndex : newIndex;
  nsMsgViewIndex highIndex = lowIndex == threadIndex ? newIndex : threadIndex;
  NoteChange(lowIndex, highIndex - lowIndex + childCount + 1,
             nsMsgViewNotificationCode::changed);
}
Esempio n. 5
0
nsresult nsMsgSearchDBView::AddHdrFromFolder(nsIMsgDBHdr *msgHdr, nsIMsgFolder *folder)
{
  if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort)
    return nsMsgGroupView::OnNewHeader(msgHdr, nsMsgKey_None, true);
  nsMsgKey msgKey;
  uint32_t msgFlags;
  msgHdr->GetMessageKey(&msgKey);
  msgHdr->GetFlags(&msgFlags);

  if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)
  {
    nsCOMPtr<nsIMsgThread> thread;
    nsCOMPtr<nsIMsgDBHdr> threadRoot;
    // if we find an xf thread in the hash table corresponding to the new msg's
    // message id, a previous header must be a reference child of the new 
    // message, which means we need to reparent later.
    bool msgIsReferredTo;
    GetXFThreadFromMsgHdr(msgHdr, getter_AddRefs(thread), &msgIsReferredTo);
    bool newThread = !thread;
    nsMsgXFViewThread *viewThread;
    if (!thread)
    {
      viewThread = new nsMsgXFViewThread(this, m_nextThreadId++);
      if (!viewThread)
        return NS_ERROR_OUT_OF_MEMORY;
      thread = do_QueryInterface(viewThread);
    }
    else
    {
      viewThread = static_cast<nsMsgXFViewThread*>(thread.get());
      thread->GetChildHdrAt(0, getter_AddRefs(threadRoot));
    }

    AddMsgToHashTables(msgHdr, thread);
    nsCOMPtr<nsIMsgDBHdr> parent;
    uint32_t posInThread;
    // We need to move threads in order to keep ourselves sorted
    // correctly.  We want the index of the original thread...we can do this by
    // getting the root header before we add the new header, and finding that.
    if (newThread || !viewThread->MsgCount())
    {
      viewThread->AddHdr(msgHdr, false, posInThread,
                         getter_AddRefs(parent));
      nsMsgViewIndex insertIndex = GetIndexForThread(msgHdr);
      NS_ASSERTION(insertIndex == m_levels.Length() || !m_levels[insertIndex],
                    "inserting into middle of thread");
      if (insertIndex == nsMsgViewIndex_None)
        return NS_ERROR_FAILURE;
      if (!(m_viewFlags & nsMsgViewFlagsType::kExpandAll))
        msgFlags |= nsMsgMessageFlags::Elided;
      InsertMsgHdrAt(insertIndex, msgHdr, msgKey, msgFlags, 0);
      NoteChange(insertIndex, 1, nsMsgViewNotificationCode::insertOrDelete);
    }
    else
    {
      // get the thread root index before we add the header, because adding
      // the header can change the sort position.
      nsMsgViewIndex threadIndex = GetThreadRootIndex(threadRoot);
      viewThread->AddHdr(msgHdr, msgIsReferredTo, posInThread,
                         getter_AddRefs(parent));
      if (threadIndex == nsMsgViewIndex_None)
      {
        NS_ERROR("couldn't find thread index for newly inserted header");
        return NS_OK; // not really OK, but not failure exactly.
      }
      NS_ASSERTION(!m_levels[threadIndex], "threadRoot incorrect, or level incorrect");

      bool moveThread = false;
      if (m_sortType == nsMsgViewSortType::byDate)
      {
        uint32_t newestMsgInThread = 0, msgDate = 0;
        viewThread->GetNewestMsgDate(&newestMsgInThread);
        msgHdr->GetDateInSeconds(&msgDate);
        moveThread = (msgDate == newestMsgInThread);
      }
      OrExtraFlag(threadIndex, MSG_VIEW_FLAG_HASCHILDREN | MSG_VIEW_FLAG_ISTHREAD);
      if (!(m_flags[threadIndex] & nsMsgMessageFlags::Elided))
      {
        if (parent)
        {
          // since we know posInThread, we just want to insert the new hdr
          // at threadIndex + posInThread, and then rebuild the view until we
          // get to a sibling of the new hdr.
          uint8_t newMsgLevel = viewThread->ChildLevelAt(posInThread);
          InsertMsgHdrAt(threadIndex + posInThread, msgHdr, msgKey, msgFlags,
                         newMsgLevel);

          NoteChange(threadIndex + posInThread, 1, nsMsgViewNotificationCode::insertOrDelete);
          for (nsMsgViewIndex viewIndex = threadIndex + ++posInThread;
               posInThread < viewThread->MsgCount() && 
               viewThread->ChildLevelAt(posInThread) > newMsgLevel; viewIndex++)
          {
            m_levels[viewIndex] = viewThread->ChildLevelAt(posInThread++);
          }

        }
        else // The new header is the root, so we need to adjust 
             // all the children.
        {
          InsertMsgHdrAt(threadIndex, msgHdr, msgKey, msgFlags, 0);

          NoteChange(threadIndex, 1, nsMsgViewNotificationCode::insertOrDelete);
          nsMsgViewIndex i;
          for (i = threadIndex + 1; 
               i < m_keys.Length() && (i == threadIndex + 1 || m_levels[i]); i++)
            m_levels[i] = m_levels[i] + 1;
          // turn off thread flags on old root.
          AndExtraFlag(threadIndex + 1, ~(MSG_VIEW_FLAG_ISTHREAD | 
                                          nsMsgMessageFlags::Elided | 
                                          MSG_VIEW_FLAG_HASCHILDREN));

          NoteChange(threadIndex + 1, i - threadIndex + 1, 
                     nsMsgViewNotificationCode::changed);
        }
      }
      else if (!parent)
      {
        // new parent came into collapsed thread
        nsCOMPtr<nsIMsgFolder> msgFolder;
        msgHdr->GetFolder(getter_AddRefs(msgFolder));
        m_keys[threadIndex] = msgKey;
        m_folders.ReplaceObjectAt(msgFolder, threadIndex);
        m_flags[threadIndex] = msgFlags | MSG_VIEW_FLAG_ISTHREAD | 
                                          nsMsgMessageFlags::Elided | 
                                          MSG_VIEW_FLAG_HASCHILDREN;
        NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed);

      }
      if (moveThread)
        MoveThreadAt(threadIndex);
    }
  }
  else
  {
    m_folders.AppendObject(folder);
  // nsMsgKey_None means it's not a valid hdr.
  if (msgKey != nsMsgKey_None)
  {
    msgHdr->GetFlags(&msgFlags);
    m_keys.AppendElement(msgKey);
    m_levels.AppendElement(0);
    m_flags.AppendElement(msgFlags);
      NoteChange(GetSize() - 1, 1, nsMsgViewNotificationCode::insertOrDelete);
    }
  }
  return NS_OK;
  }
Esempio n. 6
0
NS_IMETHODIMP 
nsMsgSearchDBView::OnHdrDeleted(nsIMsgDBHdr *aHdrDeleted, nsMsgKey aParentKey, 
                                int32_t aFlags, nsIDBChangeListener *aInstigator)
{
  if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort)
    return nsMsgGroupView::OnHdrDeleted(aHdrDeleted, aParentKey, 
                                        aFlags, aInstigator);
  if (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)
  {
    nsMsgViewIndex deletedIndex = FindHdr(aHdrDeleted);
    uint32_t savedFlags = 0;
    if (deletedIndex != nsMsgViewIndex_None)
    {
      savedFlags = m_flags[deletedIndex];
      RemoveByIndex(deletedIndex);
    }

    nsCOMPtr<nsIMsgThread> thread;
    GetXFThreadFromMsgHdr(aHdrDeleted, getter_AddRefs(thread));
    if (thread)
    {
      nsMsgXFViewThread *viewThread = static_cast<nsMsgXFViewThread*>(thread.get());
      viewThread->RemoveChildHdr(aHdrDeleted, nullptr);
      if (deletedIndex == nsMsgViewIndex_None && viewThread->MsgCount() == 1)
      {
        // remove the last child of a collapsed thread. Need to find the root,
        // and remove the thread flags on it.
        nsCOMPtr<nsIMsgDBHdr> rootHdr;
        thread->GetRootHdr(nullptr, getter_AddRefs(rootHdr));
        if (rootHdr)
        {
          nsMsgViewIndex threadIndex = GetThreadRootIndex(rootHdr);
          if (threadIndex != nsMsgViewIndex_None)
            AndExtraFlag(threadIndex, ~(MSG_VIEW_FLAG_ISTHREAD | 
                                        nsMsgMessageFlags::Elided | 
                                        MSG_VIEW_FLAG_HASCHILDREN));
        }
      }
      else if (savedFlags & MSG_VIEW_FLAG_HASCHILDREN)
{
        if (savedFlags & nsMsgMessageFlags::Elided)
        {
          nsCOMPtr<nsIMsgDBHdr> rootHdr;
          nsresult rv = thread->GetRootHdr(nullptr, getter_AddRefs(rootHdr));
          NS_ENSURE_SUCCESS(rv, rv);
          nsMsgKey msgKey;
          uint32_t msgFlags;
          rootHdr->GetMessageKey(&msgKey);
          rootHdr->GetFlags(&msgFlags);
          // promote the new thread root
          if (viewThread->MsgCount() > 1)
            msgFlags |= MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided | 
                        MSG_VIEW_FLAG_HASCHILDREN;
          InsertMsgHdrAt(deletedIndex, rootHdr, msgKey, msgFlags, 0);
          if (!m_deletingRows)
            NoteChange(deletedIndex, 1, nsMsgViewNotificationCode::insertOrDelete);
        }
        else if (viewThread->MsgCount() > 1)
        {
          OrExtraFlag(deletedIndex, MSG_VIEW_FLAG_ISTHREAD |
                                    MSG_VIEW_FLAG_HASCHILDREN);
        }
      }
    }
  }
  else
  {
    return nsMsgDBView::OnHdrDeleted(aHdrDeleted, aParentKey, 
                                        aFlags, aInstigator);
  }
   return NS_OK;
}
Esempio n. 7
0
nsresult nsMsgGroupView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentKey, bool ensureListed)
{
  if (!(m_viewFlags & nsMsgViewFlagsType::kGroupBySort))
    return nsMsgDBView::OnNewHeader(newHdr, aParentKey, ensureListed);

  // check if we're adding a header, and the current day has changed. If it has, we're just going to
  // close and re-open the view so things will be correctly categorized.
  if (m_dayChanged)
    return RebuildView(m_viewFlags);

  bool newThread;
  nsMsgGroupThread *thread = AddHdrToThread(newHdr, &newThread);
  if (thread)
  {
    // find the view index of (the root node of) the thread
    nsMsgViewIndex threadIndex = ThreadIndexOfMsgHdr(newHdr);
    // may need to fix thread counts
    if (threadIndex != nsMsgViewIndex_None)
    {
      if (newThread)
      {
        // AddHdrToThread creates the header elided, so we need to un-elide it
        //  if we want it expanded.
        if(m_viewFlags & nsMsgViewFlagsType::kExpandAll)
          m_flags[threadIndex] &= ~nsMsgMessageFlags::Elided;
      }
      else
      {
        m_flags[threadIndex] |= MSG_VIEW_FLAG_HASCHILDREN
                                | MSG_VIEW_FLAG_ISTHREAD;
      }

      int32_t numRowsToInvalidate = 1;
      // if the thread is expanded (not elided), we should add the header to
      //  the view.
      if (! (m_flags[threadIndex] & nsMsgMessageFlags::Elided))
      {
        uint32_t msgIndexInThread = thread->FindMsgHdr(newHdr);
        bool insertedAtThreadRoot = !msgIndexInThread;
        // Add any new display node and potentially fix-up changes in the root.
        // (If this is a new thread and we are not using a dummy row, the only
        //  node to display is the root node which has already been added by
        //  AddHdrToThread.  And since there is just the one, no change in root
        //  could have occurred, so we have nothing to do.)
        if (!newThread || GroupViewUsesDummyRow())
        {
          // we never want to insert/update the root node, because
          //  AddHdrToThread has already done that for us (in all cases).
          if (insertedAtThreadRoot)
            msgIndexInThread++;
          // If this header is the new parent of the thread... AND
          // If we are not using a dummy row, this means we need to append our
          //  old node as the first child of the new root.
          // (If we are using a dummy row, the old node's "content" node already
          //  exists (at position threadIndex + 1) and we need to insert the
          //  "content" copy of the new root node there, pushing our old
          //  "content" node down.)
          // Example mini-diagrams, wrapping the to-add thing with ()
          //  No dummy row; we had: [A], now we have [B], we want [B (A)].
          //  Dummy row; we had: [A A], now we have [B A], we want [B (B) A].
          //  (Coming into this we're adding 'B')
          if (!newThread && insertedAtThreadRoot && !GroupViewUsesDummyRow())
          {
            // grab a copy of the old root node ('A') from the thread so we can
            //  insert it. (offset msgIndexInThread=1 is the right thing; we are
            //  non-dummy.)
            thread->GetChildHdrAt(msgIndexInThread, &newHdr);
          } // nothing to do for dummy case, we're already inserting 'B'.
          nsMsgKey msgKey;
          uint32_t msgFlags;
          newHdr->GetMessageKey(&msgKey);
          newHdr->GetFlags(&msgFlags);
          InsertMsgHdrAt(threadIndex + msgIndexInThread, newHdr, msgKey,
                         msgFlags, 1);
        }
        // the call to NoteChange() has to happen after we add the key
        // as NoteChange() will call RowCountChanged() which will call our GetRowCount()
        // (msgIndexInThread states.  new thread: 0, old thread at root: 1)
        if (newThread && GroupViewUsesDummyRow())
          NoteChange(threadIndex, 2, nsMsgViewNotificationCode::insertOrDelete);
        else
          NoteChange(threadIndex + msgIndexInThread, 1,
                     nsMsgViewNotificationCode::insertOrDelete);
        numRowsToInvalidate = msgIndexInThread;
      }
      // we still need the addition notification for new threads when elided
      else if (newThread)
      {
        NoteChange(threadIndex, 1,
                   nsMsgViewNotificationCode::insertOrDelete);
      }
      NoteChange(threadIndex, numRowsToInvalidate, nsMsgViewNotificationCode::changed);
    }
  }
  // if thread is expanded, we need to add hdr to view...
  return NS_OK;
}
Esempio n. 8
0
nsMsgGroupThread *nsMsgGroupView::AddHdrToThread(nsIMsgDBHdr *msgHdr, bool *pNewThread)
{
  nsMsgKey msgKey;
  uint32_t msgFlags;
  msgHdr->GetMessageKey(&msgKey);
  msgHdr->GetFlags(&msgFlags);
  nsString hashKey;
  nsresult rv = HashHdr(msgHdr, hashKey);
  if (NS_FAILED(rv))
    return nullptr;

//  if (m_sortType == nsMsgViewSortType::byDate)
//    msgKey = ((nsPRUint32Key *) hashKey)->GetValue();
  nsCOMPtr<nsIMsgThread> msgThread;
  m_groupsTable.Get(hashKey, getter_AddRefs(msgThread));
  bool newThread = !msgThread;
  *pNewThread = newThread;
  nsMsgViewIndex viewIndexOfThread; // index of first message in thread in view
  nsMsgViewIndex threadInsertIndex; // index of newly added header in thread

  nsMsgGroupThread *foundThread = static_cast<nsMsgGroupThread *>(msgThread.get());
  if (foundThread) 
  {
    // find the view index of the root node of the thread in the view
    viewIndexOfThread = GetIndexOfFirstDisplayedKeyInThread(foundThread,
                                                            true);
    if (viewIndexOfThread == nsMsgViewIndex_None)
    {
      // Something is wrong with the group table. Remove the old group and
      // insert a new one.
      m_groupsTable.Remove(hashKey);
      foundThread = nullptr;
      *pNewThread = newThread = true;
    }
  }
  // If the thread does not already exist, create one
  if (!foundThread)
  {
    foundThread = CreateGroupThread(m_db);
    msgThread = do_QueryInterface(foundThread);
    m_groupsTable.Put(hashKey, msgThread);
    if (GroupViewUsesDummyRow())
    {
      foundThread->m_dummy = true;
      msgFlags |=  MSG_VIEW_FLAG_DUMMY | MSG_VIEW_FLAG_HASCHILDREN;
    }

    viewIndexOfThread = GetInsertIndex(msgHdr);
    if (viewIndexOfThread == nsMsgViewIndex_None)
      viewIndexOfThread = m_keys.Length();

    // add the thread root node to the view
    InsertMsgHdrAt(viewIndexOfThread, msgHdr, msgKey,
                   msgFlags | MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided, 0);

    // For dummy rows, Have the header serve as the dummy node (it will be added
    //  again for its actual content later.)
    if (GroupViewUsesDummyRow())
      foundThread->InsertMsgHdrAt(0, msgHdr);

    // Calculate the (integer thread key); this really only needs to be done for
    //  the byDate case where the expanded state of the groups can be easily
    //  persisted and restored because of the bounded, consecutive value space
    //  occupied.  We calculate an integer value in all cases mainly because
    //  it's the sanest choice available...
    // (The thread key needs to be an integer, so parse hash keys that are
    //  stringified integers to real integers, and hash actual strings into
    //  integers.)
    if ((m_sortType == nsMsgViewSortType::byAttachments) ||
        (m_sortType == nsMsgViewSortType::byFlagged) ||
        (m_sortType == nsMsgViewSortType::byPriority) ||
        (m_sortType == nsMsgViewSortType::byStatus) ||
        (m_sortType == nsMsgViewSortType::byReceived) ||
        (m_sortType == nsMsgViewSortType::byDate))
      foundThread->m_threadKey =
        atoi(NS_LossyConvertUTF16toASCII(hashKey).get());
    else
      foundThread->m_threadKey = (nsMsgKey)
        PL_HashString(NS_LossyConvertUTF16toASCII(hashKey).get());
  }
  // Add the message to the thread as an actual content-bearing header.
  // (If we use dummy rows, it was already added to the thread during creation.)
  threadInsertIndex = foundThread->AddChildFromGroupView(msgHdr, this);
  // check if new hdr became thread root
  if (!newThread && threadInsertIndex == 0)
  {
    // update the root node's header (in the view) to be the same as the root
    //  node in the thread.
    SetMsgHdrAt(msgHdr, viewIndexOfThread, msgKey,
                (msgFlags & ~(nsMsgMessageFlags::Elided)) |
                  // maintain elided flag and dummy flag
                  (m_flags[viewIndexOfThread] & (nsMsgMessageFlags::Elided
                                                 | MSG_VIEW_FLAG_DUMMY))
                  // ensure thread and has-children flags are set
                  | MSG_VIEW_FLAG_ISTHREAD | MSG_VIEW_FLAG_HASCHILDREN, 0);
    // update the content-bearing copy in the thread to match.  (the root and
    //  first nodes in the thread should always be the same header.)
    // note: the guy who used to be the root will still exist.  If our list of
    //  nodes was [A A], a new node B is introduced which sorts to be the first
    //  node, giving us [B A A], our copy makes that [B B A], and things are
    //  right in the world (since we want the first two headers to be the same
    //  since one is our dummy and one is real.)
    if (GroupViewUsesDummyRow())
      foundThread->SetMsgHdrAt(1, msgHdr); // replace the old duplicate dummy header.
    // we do not update the content-bearing copy in the view to match; we leave
    //  that up to OnNewHeader, which is the piece of code who gets to care
    //  about whether the thread's children are shown or not (elided)
  }

  return foundThread;
}
nsresult nsMsgThreadedDBView::OnNewHeader(nsIMsgDBHdr *newHdr, nsMsgKey aParentKey, bool ensureListed)
{
  if (m_viewFlags & nsMsgViewFlagsType::kGroupBySort)
    return nsMsgGroupView::OnNewHeader(newHdr, aParentKey, ensureListed);

  NS_ENSURE_TRUE(newHdr, NS_MSG_MESSAGE_NOT_FOUND);

  nsMsgKey newKey;
  newHdr->GetMessageKey(&newKey);

  // views can override this behaviour, which is to append to view.
  // This is the mail behaviour, but threaded views want
  // to insert in order...
  PRUint32 msgFlags;
  newHdr->GetFlags(&msgFlags);
  if ((m_viewFlags & nsMsgViewFlagsType::kUnreadOnly) && !ensureListed &&
      (msgFlags & nsMsgMessageFlags::Read))
    return NS_OK;
  // Currently, we only add the header in a threaded view if it's a thread.
  // We used to check if this was the first header in the thread, but that's
  // a bit harder in the unreadOnly view. But we'll catch it below.

  // if not threaded display just add it to the view.
  if (!(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
    return AddHdr(newHdr);

  // need to find the thread we added this to so we can change the hasnew flag
  // added message to existing thread, but not to view
  // Fix flags on thread header.
  PRInt32 threadCount;
  PRUint32 threadFlags;
  bool moveThread = false;
  nsMsgViewIndex threadIndex = ThreadIndexOfMsg(newKey, nsMsgViewIndex_None, &threadCount, &threadFlags);
  bool threadRootIsDisplayed = false;

  nsCOMPtr <nsIMsgThread> threadHdr;
  m_db->GetThreadContainingMsgHdr(newHdr, getter_AddRefs(threadHdr));
  if (threadHdr && m_sortType == nsMsgViewSortType::byDate)
  {
    PRUint32 newestMsgInThread = 0, msgDate = 0;
    threadHdr->GetNewestMsgDate(&newestMsgInThread);
    newHdr->GetDateInSeconds(&msgDate);
    moveThread = (msgDate == newestMsgInThread);
  }

  if (threadIndex != nsMsgViewIndex_None)
  {
    threadRootIsDisplayed = (m_currentlyDisplayedViewIndex == threadIndex);
    PRUint32 flags = m_flags[threadIndex];
    if (!(flags & MSG_VIEW_FLAG_HASCHILDREN))
    {
      flags |= MSG_VIEW_FLAG_HASCHILDREN | MSG_VIEW_FLAG_ISTHREAD;
      if (!(m_viewFlags & nsMsgViewFlagsType::kUnreadOnly))
        flags |= nsMsgMessageFlags::Elided;
      m_flags[threadIndex] = flags;
    }

    if (!(flags & nsMsgMessageFlags::Elided))
    { // thread is expanded
      // insert child into thread
      // levels of other hdrs may have changed!
      PRUint32 newFlags = msgFlags;
      PRInt32 level = 0;
      nsMsgViewIndex insertIndex = threadIndex;
      if (aParentKey == nsMsgKey_None)
      {
        newFlags |= MSG_VIEW_FLAG_ISTHREAD | MSG_VIEW_FLAG_HASCHILDREN;
      }
      else
      {
        nsMsgViewIndex parentIndex = FindParentInThread(aParentKey, threadIndex);
        level = m_levels[parentIndex] + 1;
        insertIndex = GetInsertInfoForNewHdr(newHdr, parentIndex, level);
      }
      InsertMsgHdrAt(insertIndex, newHdr, newKey, newFlags, level);
      // the call to NoteChange() has to happen after we add the key
      // as NoteChange() will call RowCountChanged() which will call our GetRowCount()
      NoteChange(insertIndex, 1, nsMsgViewNotificationCode::insertOrDelete);

      if (aParentKey == nsMsgKey_None)
      {
        // this header is the new king! try collapsing the existing thread,
        // removing it, installing this header as king, and expanding it.
        CollapseByIndex(threadIndex, nsnull);
        // call base class, so child won't get promoted.
        // nsMsgDBView::RemoveByIndex(threadIndex);
        ExpandByIndex(threadIndex, nsnull);
      }
    }
    else if (aParentKey == nsMsgKey_None)
    {
      // if we have a collapsed thread which just got a new
      // top of thread, change the keys array.
      m_keys[threadIndex] = newKey;
    }

    // If this message is new, the thread is collapsed, it is the
    // root and it was displayed, expand it so that the user does
    // not find that their message has magically turned into a summary.
    if (msgFlags & nsMsgMessageFlags::New &&
        m_flags[threadIndex] & nsMsgMessageFlags::Elided &&
        threadRootIsDisplayed)
      ExpandByIndex(threadIndex, nsnull);

    if (moveThread)
      MoveThreadAt(threadIndex);
    else
      // note change, to update the parent thread's unread and total counts
      NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed);
  }
  else if (threadHdr)
    // adding msg to thread that's not in view.
    AddMsgToThreadNotInView(threadHdr, newHdr, ensureListed);

  return NS_OK;
}