Ejemplo n.º 1
0
NS_IMETHODIMP nsMsgGroupView::OnHdrDeleted(nsIMsgDBHdr *aHdrDeleted, nsMsgKey aParentKey, int32_t aFlags,
                            nsIDBChangeListener *aInstigator)
{
  if (!(m_viewFlags & nsMsgViewFlagsType::kGroupBySort))
    return nsMsgDBView::OnHdrDeleted(aHdrDeleted, aParentKey, aFlags, aInstigator);

  // 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);

  nsCOMPtr <nsIMsgThread> thread;
  nsMsgKey keyDeleted;
  aHdrDeleted->GetMessageKey(&keyDeleted);

  nsresult rv = GetThreadContainingMsgHdr(aHdrDeleted, getter_AddRefs(thread));
  NS_ENSURE_SUCCESS(rv, rv);
  nsMsgViewIndex viewIndexOfThread = GetIndexOfFirstDisplayedKeyInThread(
                                       thread, true); // yes to dummy node
  thread->RemoveChildHdr(aHdrDeleted, nullptr);

  nsMsgGroupThread *groupThread = static_cast<nsMsgGroupThread *>((nsIMsgThread *) thread);

  bool rootDeleted = viewIndexOfThread != nsMsgKey_None &&
    m_keys[viewIndexOfThread] == keyDeleted;
  rv = nsMsgDBView::OnHdrDeleted(aHdrDeleted, aParentKey, aFlags, aInstigator);
  if (groupThread->m_dummy)
  {
    if (!groupThread->NumRealChildren())
    {
      thread->RemoveChildAt(0); // get rid of dummy
      if (viewIndexOfThread != nsMsgKey_None)
      {
        RemoveByIndex(viewIndexOfThread);
        if (m_deletingRows)
          mIndicesToNoteChange.AppendElement(viewIndexOfThread);
      }
    }
    else if (rootDeleted)
    {
      // reflect new thread root into view.dummy row.
      nsCOMPtr<nsIMsgDBHdr> hdr;
      thread->GetChildHdrAt(0, getter_AddRefs(hdr));
      if (hdr)
      {
        nsMsgKey msgKey;
        hdr->GetMessageKey(&msgKey);
        SetMsgHdrAt(hdr, viewIndexOfThread, msgKey, m_flags[viewIndexOfThread], 0);
      }
    }
  }
  if (!groupThread->m_keys.Length())
  {
    nsString hashKey;
    rv = HashHdr(aHdrDeleted, hashKey);
    if (NS_SUCCEEDED(rv))
      m_groupsTable.Remove(hashKey);
  }
  return rv;
}
NS_IMETHODIMP nsMsgGroupThread::RemoveChildHdr(nsIMsgDBHdr *child, nsIDBChangeAnnouncer *announcer)
{
  uint32_t flags;
  nsMsgKey key;
  
  if (!child)
    return NS_ERROR_NULL_POINTER;
  
  child->GetFlags(&flags);
  child->GetMessageKey(&key);
  
  // if this was the newest msg, clear the newest msg date so we'll recalc.
  uint32_t date;
  child->GetDateInSeconds(&date);
  if (date == m_newestMsgDate)
    SetNewestMsgDate(0);

  if (!(flags & nsMsgMessageFlags::Read))
    ChangeUnreadChildCount(-1);
  nsMsgViewIndex threadIndex = FindMsgHdr(child);
  bool wasFirstChild = threadIndex == 0;
  nsresult rv = RemoveChildAt(threadIndex);
  // if we're deleting the root of a dummy thread, need to update the threadKey
  // and the dummy header at position 0
  if (m_dummy && wasFirstChild && m_keys.Length() > 1)
  {
    nsIMsgDBHdr *newRootChild;
    GetChildHdrAt(1, &newRootChild);
    SetMsgHdrAt(0, newRootChild);
  }

  return rv;
}
Ejemplo n.º 3
0
nsresult
nsMsgSearchDBView::ListIdsInThread(nsIMsgThread *threadHdr,
                                   nsMsgViewIndex startOfThreadViewIndex,
                                   uint32_t *pNumListed)
{
  NS_ENSURE_ARG_POINTER(threadHdr);
  NS_ENSURE_ARG_POINTER(pNumListed);

  // these children ids should be in thread order.
  uint32_t i;
  nsMsgViewIndex viewIndex = startOfThreadViewIndex + 1;
  *pNumListed = 0;

  uint32_t numChildren;
  threadHdr->GetNumChildren(&numChildren);
  NS_ASSERTION(numChildren, "Empty thread in view/db");
  if (!numChildren)
    return NS_OK;

  numChildren--; // account for the existing thread root
  if (!InsertEmptyRows(viewIndex, numChildren))
    return NS_ERROR_OUT_OF_MEMORY;

  bool threadedView = m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay &&
    !(m_viewFlags & nsMsgViewFlagsType::kGroupBySort);
  nsMsgXFViewThread *viewThread;
  if (threadedView)
    viewThread = static_cast<nsMsgXFViewThread*>(threadHdr);

  for (i = 1; i <= numChildren; i++)
  {
    nsCOMPtr<nsIMsgDBHdr> msgHdr;
    threadHdr->GetChildHdrAt(i, getter_AddRefs(msgHdr));

    if (msgHdr)
    {
      nsMsgKey msgKey;
      uint32_t msgFlags;
      msgHdr->GetMessageKey(&msgKey);
      msgHdr->GetFlags(&msgFlags);
      uint8_t level = (threadedView) ? viewThread->ChildLevelAt(i) : 1;
      SetMsgHdrAt(msgHdr, viewIndex, msgKey, msgFlags & ~MSG_VIEW_FLAGS, 
                  level);
      (*pNumListed)++;
      viewIndex++;
    }
  }
  return NS_OK;
}
Ejemplo n.º 4
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;
}