NS_IMETHODIMP
nsMsgXFVirtualFolderDBView::OnSearchDone(nsresult status)
{
  NS_ENSURE_TRUE(m_viewFolder, NS_ERROR_NOT_INITIALIZED);

  // handle any non verified hits we haven't handled yet.
  if (NS_SUCCEEDED(status) && !m_doingQuickSearch && status != NS_MSG_SEARCH_INTERRUPTED)
    UpdateCacheAndViewForPrevSearchedFolders(nullptr);

  m_doingSearch = false;
  //we want to set imap delete model once the search is over because setting next
  //message after deletion will happen before deleting the message and search scope
  //can change with every search.
  mDeleteModel = nsMsgImapDeleteModels::MoveToTrash;  //set to default in case it is non-imap folder
  nsIMsgFolder *curFolder = m_folders.SafeObjectAt(0);
  if (curFolder)
    GetImapDeleteModel(curFolder);

  nsCOMPtr<nsIMsgDatabase> virtDatabase;
  nsCOMPtr<nsIDBFolderInfo> dbFolderInfo;
  nsresult rv = m_viewFolder->GetDBFolderInfoAndDB(getter_AddRefs(dbFolderInfo), getter_AddRefs(virtDatabase));
  NS_ENSURE_SUCCESS(rv, rv);
  // count up the number of unread and total messages from the view, and set those in the
  // folder - easier than trying to keep the count up to date in the face of
  // search hits coming in while the user is reading/deleting messages.
  uint32_t numUnread = 0;
  for (uint32_t i = 0; i < m_flags.Length(); i++)
    if (m_flags[i] & nsMsgMessageFlags::Elided)
    {
      nsCOMPtr<nsIMsgThread> thread;
      GetThreadContainingIndex(i, getter_AddRefs(thread));
      if (thread)
      {
        uint32_t unreadInThread;
        thread->GetNumUnreadChildren(&unreadInThread);
        numUnread += unreadInThread;
      }
    }
    else
    {
      if (!(m_flags[i] & nsMsgMessageFlags::Read))
        numUnread++;
    }
  dbFolderInfo->SetNumUnreadMessages(numUnread);
  dbFolderInfo->SetNumMessages(m_totalMessagesInView);
  m_viewFolder->UpdateSummaryTotals(true); // force update from db.
  virtDatabase->Commit(nsMsgDBCommitType::kLargeCommit);
  if (!m_sortValid && m_sortType != nsMsgViewSortType::byThread && 
      !(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
  {
    m_sortValid = false;       //sort the results
    Sort(m_sortType, m_sortOrder);
  }
  m_foldersSearchingOver.Clear();
  m_curFolderGettingHits = nullptr;
  return rv;
}
nsresult nsMsgQuickSearchDBView::ExpansionDelta(nsMsgViewIndex index, int32_t *expansionDelta)
{
  *expansionDelta = 0;
  if (index >= ((nsMsgViewIndex) m_keys.Length()))
    return NS_MSG_MESSAGE_NOT_FOUND;

  char flags = m_flags[index];

  if (!(m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay))
    return NS_OK;

  nsCOMPtr<nsIMsgThread> threadHdr; 
  nsresult rv = GetThreadContainingIndex(index, getter_AddRefs(threadHdr));
  NS_ENSURE_SUCCESS(rv, rv);
  uint32_t numChildren;
  threadHdr->GetNumChildren(&numChildren);
  nsCOMPtr<nsIMsgDBHdr> rootHdr;
  nsMsgKey rootKey;
  GetMsgHdrForViewIndex(index, getter_AddRefs(rootHdr));
  rootHdr->GetMessageKey(&rootKey);
  // group threads can have the root key twice, one for the dummy row.
  bool rootKeySkipped = false;
  for (uint32_t i = 0; i < numChildren; i++)
  {
    nsCOMPtr<nsIMsgDBHdr> msgHdr;
    threadHdr->GetChildHdrAt(i, getter_AddRefs(msgHdr));
    if (msgHdr)
    {
      nsMsgKey msgKey;
      msgHdr->GetMessageKey(&msgKey);
      if (msgKey != rootKey || (GroupViewUsesDummyRow() && rootKeySkipped))
      {
        // if this hdr is in the original view, add it to new view.
        if (m_origKeys.BinaryIndexOf(msgKey) != m_origKeys.NoIndex)
          (*expansionDelta)++;
      }
      else
      {
        rootKeySkipped = true;
      }
    }
  }
  if (! (flags & nsMsgMessageFlags::Elided))
    *expansionDelta = - (*expansionDelta);
  return NS_OK;
}
nsresult
nsMsgQuickSearchDBView::ListCollapsedChildren(nsMsgViewIndex viewIndex,
                                              nsIMutableArray *messageArray)
{
  nsCOMPtr<nsIMsgThread> threadHdr; 
  nsresult rv = GetThreadContainingIndex(viewIndex, getter_AddRefs(threadHdr));
  NS_ENSURE_SUCCESS(rv, rv);
  uint32_t numChildren;
  threadHdr->GetNumChildren(&numChildren);
  nsCOMPtr<nsIMsgDBHdr> rootHdr;
  nsMsgKey rootKey;
  GetMsgHdrForViewIndex(viewIndex, getter_AddRefs(rootHdr));
  rootHdr->GetMessageKey(&rootKey);
  // group threads can have the root key twice, one for the dummy row.
  bool rootKeySkipped = false;
  for (uint32_t i = 0; i < numChildren; i++)
  {
    nsCOMPtr<nsIMsgDBHdr> msgHdr;
    threadHdr->GetChildHdrAt(i, getter_AddRefs(msgHdr));
    if (msgHdr)
    {
      nsMsgKey msgKey;
      msgHdr->GetMessageKey(&msgKey);
      if (msgKey != rootKey || (GroupViewUsesDummyRow() && rootKeySkipped))
      {
        // if this hdr is in the original view, add it to new view.
        if (m_origKeys.BinaryIndexOf(msgKey) != m_origKeys.NoIndex)
          messageArray->AppendElement(msgHdr, false);
      }
      else
      {
        rootKeySkipped = true;
      }
    }
  }
  return NS_OK;
}
Example #4
0
NS_IMETHODIMP nsMsgGroupView::OpenWithHdrs(nsISimpleEnumerator *aHeaders, nsMsgViewSortTypeValue aSortType,
                                        nsMsgViewSortOrderValue aSortOrder, nsMsgViewFlagsTypeValue aViewFlags,
                                        int32_t *aCount)
{
  nsresult rv = NS_OK;

  m_groupsTable.Clear();
  if (aSortType == nsMsgViewSortType::byThread || aSortType == nsMsgViewSortType::byId
    || aSortType == nsMsgViewSortType::byNone || aSortType == nsMsgViewSortType::bySize)
    return NS_ERROR_INVALID_ARG;

  m_sortType = aSortType;
  m_sortOrder = aSortOrder;
  m_viewFlags = aViewFlags | nsMsgViewFlagsType::kThreadedDisplay | nsMsgViewFlagsType::kGroupBySort;
  SaveSortInfo(m_sortType, m_sortOrder);

  bool hasMore;
  nsCOMPtr <nsISupports> supports;
  nsCOMPtr <nsIMsgDBHdr> msgHdr;
  while (NS_SUCCEEDED(rv) && NS_SUCCEEDED(rv = aHeaders->HasMoreElements(&hasMore)) && hasMore)
  {
    rv = aHeaders->GetNext(getter_AddRefs(supports));
    if (NS_SUCCEEDED(rv) && supports)
    {
      bool notUsed;
      msgHdr = do_QueryInterface(supports);
      AddHdrToThread(msgHdr, &notUsed);
    }
  }
  uint32_t expandFlags = 0;
  bool expandAll = m_viewFlags & nsMsgViewFlagsType::kExpandAll;
  uint32_t viewFlag = (m_sortType == nsMsgViewSortType::byDate) ? MSG_VIEW_FLAG_DUMMY : 0;
  if (viewFlag && m_db)
  {
    nsCOMPtr <nsIDBFolderInfo> dbFolderInfo;
    nsresult rv = m_db->GetDBFolderInfo(getter_AddRefs(dbFolderInfo));
    NS_ENSURE_SUCCESS(rv, rv);
    if (dbFolderInfo)
      dbFolderInfo->GetUint32Property("dateGroupFlags",  0, &expandFlags);
  }
  // go through the view updating the flags for threads with more than one message...
  // and if grouped by date, expanding threads that were expanded before.
  for (uint32_t viewIndex = 0; viewIndex < m_keys.Length(); viewIndex++)
  {
    nsCOMPtr <nsIMsgThread> thread;
    GetThreadContainingIndex(viewIndex, getter_AddRefs(thread));
    if (thread)
    {
      uint32_t numChildren;
      thread->GetNumChildren(&numChildren);
      if (numChildren > 1 || viewFlag)
        OrExtraFlag(viewIndex, viewFlag | MSG_VIEW_FLAG_HASCHILDREN);
      if (expandAll || expandFlags)
      {
        nsMsgGroupThread *groupThread = static_cast<nsMsgGroupThread *>((nsIMsgThread *) thread);
        if (expandAll || expandFlags & (1 << groupThread->m_threadKey))
        {
          uint32_t numExpanded;
          ExpandByIndex(viewIndex, &numExpanded);
          viewIndex += numExpanded;
        }
      }
    }
  }
  *aCount = m_keys.Length();
  return rv;
}
// This method just removes the specified line from the view. It does
// NOT delete it from the database.
nsresult nsMsgThreadedDBView::RemoveByIndex(nsMsgViewIndex index)
{
  nsresult rv = NS_OK;
  PRInt32 flags;

  if (!IsValidIndex(index))
    return NS_MSG_INVALID_DBVIEW_INDEX;
  
  OnHeaderAddedOrDeleted();

  flags = m_flags[index];

  if (! (m_viewFlags & nsMsgViewFlagsType::kThreadedDisplay)) 
    return nsMsgDBView::RemoveByIndex(index);

  nsCOMPtr<nsIMsgThread> threadHdr; 
  GetThreadContainingIndex(index, getter_AddRefs(threadHdr));
  NS_ENSURE_SUCCESS(rv, rv);
  PRUint32 numThreadChildren = 0; // if we can't get thread, it's already deleted and thus has 0 children
  if (threadHdr)
    threadHdr->GetNumChildren(&numThreadChildren);
  // check if we're the top level msg in the thread, and we're not collapsed.
  if ((flags & MSG_VIEW_FLAG_ISTHREAD) && !(flags & nsMsgMessageFlags::Elided) && (flags & MSG_VIEW_FLAG_HASCHILDREN))
  {
    // fix flags on thread header...Newly promoted message 
    // should have flags set correctly
    if (threadHdr)
    {
      nsMsgDBView::RemoveByIndex(index);
      nsCOMPtr<nsIMsgThread> nextThreadHdr;
      if (numThreadChildren > 0)
      {
        // unreadOnly
        nsCOMPtr<nsIMsgDBHdr> msgHdr;
        rv = threadHdr->GetChildHdrAt(0, getter_AddRefs(msgHdr));
        if (msgHdr != nsnull)
        {
          PRUint32 flag = 0;
          msgHdr->GetFlags(&flag);
          if (numThreadChildren > 1)
            flag |= MSG_VIEW_FLAG_ISTHREAD | MSG_VIEW_FLAG_HASCHILDREN;
          m_flags[index] = flag;
          m_levels[index] = 0;
        }
      }
    }
    return rv;
  }
  else if (!(flags & MSG_VIEW_FLAG_ISTHREAD))
  {
    // we're not deleting the top level msg, but top level msg might be only msg in thread now
    if (threadHdr && numThreadChildren == 1) 
    {
      nsMsgKey msgKey;
      rv = threadHdr->GetChildKeyAt(0, &msgKey);
      if (NS_SUCCEEDED(rv))
      {
        nsMsgViewIndex threadIndex = FindViewIndex(msgKey);
        if (threadIndex != nsMsgViewIndex_None)
        {
          PRUint32 flags = m_flags[threadIndex];
          flags &= ~(MSG_VIEW_FLAG_ISTHREAD | nsMsgMessageFlags::Elided | MSG_VIEW_FLAG_HASCHILDREN);
          m_flags[threadIndex] = flags;
          NoteChange(threadIndex, 1, nsMsgViewNotificationCode::changed);
        }
      }
      
    }
    return nsMsgDBView::RemoveByIndex(index);
  }
  // deleting collapsed thread header is special case. Child will be promoted,
  // so just tell FE that line changed, not that it was deleted
  if (threadHdr && numThreadChildren > 0) // header has aleady been deleted from thread
  {
    // change the id array and flags array to reflect the child header.
    // If we're not deleting the header, we want the second header,
    // Otherwise, the first one (which just got promoted).
    nsCOMPtr<nsIMsgDBHdr> msgHdr;
    rv = threadHdr->GetChildHdrAt(0, getter_AddRefs(msgHdr));
    if (msgHdr != nsnull)
    {
      msgHdr->GetMessageKey(&m_keys[index]);

      PRUint32 flag = 0;
      msgHdr->GetFlags(&flag);
      flag |= MSG_VIEW_FLAG_ISTHREAD;

      // if only hdr in thread (with one about to be deleted)
      if (numThreadChildren == 1)
      {
        // adjust flags.
        flag &=  ~MSG_VIEW_FLAG_HASCHILDREN;
        flag &= ~nsMsgMessageFlags::Elided;
        // tell FE that thread header needs to be repainted.
        NoteChange(index, 1, nsMsgViewNotificationCode::changed);
      }
      else
      {
        flag |= MSG_VIEW_FLAG_HASCHILDREN;
        flag |= nsMsgMessageFlags::Elided;
      }
      m_flags[index] = flag;
      mIndicesToNoteChange.RemoveElement(index);
    }
    else
      NS_ASSERTION(false, "couldn't find thread child");	
    NoteChange(index, 1, nsMsgViewNotificationCode::changed);	
  }
  else
  {
    // we may have deleted a whole, collapsed thread - if so,
    // ensure that the current index will be noted as changed.
    if (!mIndicesToNoteChange.Contains(index))
      mIndicesToNoteChange.AppendElement(index);
    rv = nsMsgDBView::RemoveByIndex(index);
  }
  return rv;
}