/* -*- 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. */ #define DEBUGGER_ASSERTIONS #include "CSearchTableView.h" #include "CApplicationEventAttachment.h" #include "CSearchWindowBase.h" #include "CMessageSearchWindow.h" #include "LFlexTableGeometry.h" #include "CTableKeyAttachment.h" #include "CThreadWindow.h" #include "CThreadView.h" #include "CMessageWindow.h" #include "CMessageView.h" #include "UMessageLibrary.h" #include "UMailSelection.h" #include "UMailFolderMenus.h" #include "UGraphicGizmos.h" #include "UException.h" #include "UIHelper.h" #include "CBrowserContext.h" #include "LSharable.h" #include "CComposeAddressTableView.h" #include "libi18n.h" #include "LCommander.h" #include "resgui.h" #include "MailNewsgroupWindow_Defines.h" #include "ntypes.h" //----------------------------------- CSearchTableView::~CSearchTableView() //----------------------------------- { SetMessagePane( NULL ); } void CSearchTableView::SetSearchManager(CSearchManager *inSearchManager) { mSearchManager = inSearchManager; SetMessagePane( inSearchManager->GetMsgPane() ); } //----------------------------------- void CSearchTableView::DrawCellContents( const STableCell& inCell, const Rect& inLocalRect) //----------------------------------- { // Get the text to display CStr255 displayText; MSG_SearchAttribute inAttribute = CSearchWindowBase::AttributeFromDataType(GetCellDataType(inCell)); if ( !GetDisplayText(inCell.row - 1, inAttribute, displayText) ) return; // Error occurred getting the text // Display the text Boolean cellIsSelected = CellIsSelected(inCell); Rect cellDrawFrame = inLocalRect; ::InsetRect(&cellDrawFrame, 2, 0); // For a nicer look between cells StSectClipRgnState saveSetClip(&cellDrawFrame); displayText = NET_UnEscape(displayText); if ( cellIsSelected ) { UGraphicGizmos::PlaceTextInRect((char *) &displayText[1], displayText[0], cellDrawFrame, teFlushLeft, teCenter, &mTextFontInfo, true, truncMiddle); // PlaceHilitedTextInRect((char *) &displayText[1], displayText[0], cellDrawFrame, teFlushLeft, teCenter, // &mTextFontInfo, true, truncMiddle); } else UGraphicGizmos::PlaceTextInRect((char *) &displayText[1], displayText[0], cellDrawFrame, teFlushLeft, teCenter, &mTextFontInfo, true, truncMiddle); } /*====================================================================================== Set up helpers for the table. ======================================================================================*/ //----------------------------------- void CSearchTableView::SetUpTableHelpers() //----------------------------------- { SetTableGeometry( new LFlexTableGeometry(this, mTableHeader) ); SetTableSelector( new LTableRowSelector(this) ); // standard keyboard navigation. AddAttachment( new CTableKeyAttachment(this) ); } //----------------------------------- void CSearchTableView::ListenToMessage( MessageT inCommand, void *ioParam) //----------------------------------- { if (!ObeyCommand((CommandT)inCommand, ioParam)) CMailFlexTable::ListenToMessage(inCommand, ioParam); } //----------------------------------- Boolean CSearchTableView::ObeyCommand( CommandT inCommand, void* ioParam ) //----------------------------------- { Boolean commandObeyed = false; switch( inCommand ) { case msg_TabSelect: break; default: commandObeyed = Inherited::ObeyCommand( inCommand, ioParam ); break; } return commandObeyed; } //----------------------------------- // Delete selected messages either via menu command or // key. // //----------------------------------- void CSearchTableView::DeleteSelection() //----------------------------------- { // see if you got a selection CMailSelection selection; if ( GetSelection( selection ) ) { const MSG_ViewIndex *indices = selection.GetSelectionList(); MSG_ViewIndex numIndices = selection.selectionSize; MSG_Pane* searchPane = GetMessagePane(); // try closing all the open message windows MSG_ResultElement* outElement; MSG_FolderInfo* outFolder; MessageKey outKey; const MSG_ViewIndex *indexCounter = indices; for ( int i = 0; i < numIndices; i++, indexCounter++ ) { if( GetMessageResultInfo( *indexCounter, outElement, outFolder, outKey ) ) CMessageWindow::CloseAll( outKey ); } // Get correct BE message. It can either be for mail or news messages, but not for both CMessageFolder messageFolder( outFolder ); UInt32 folderFlags = messageFolder.GetFolderFlags(); MSG_CommandType cmd = MSG_CancelMessage; // news posting - cancel command if ( (folderFlags & MSG_FOLDER_FLAG_NEWSGROUP) == 0 ) cmd = UMessageLibrary::GetMSGCommand( cmd_Clear ); // mail message - delete command // send command to the BE. Copy the index list, because it gets clobbered by // RemoveRows(). MSG_ViewIndex* copyOfIndices = new MSG_ViewIndex[numIndices]; const MSG_ViewIndex* src = &indices[0]; MSG_ViewIndex* dst = ©OfIndices[0]; for (int i = 0; i < numIndices; i++, src++, dst++) *dst = *src; // BE delete command MSG_Command( searchPane, cmd, copyOfIndices, numIndices ); // delete the list delete [] copyOfIndices; SelectionChanged(); } } //----------------------------------- void CSearchTableView::OpenRow(TableIndexT inRow) //----------------------------------- { MSG_ViewIndex index = inRow - 1; ShowMessage(index, kInMessageWindow); } //----------------------------------- void CSearchTableView::FindOrCreateThreadWindowForMessage(MSG_ViewIndex inMsgIndex) //----------------------------------- { AssertFail_(mSearchManager != nil); Try_ { MSG_ResultElement *elem = NULL; MSG_GetResultElement(GetMessagePane(), inMsgIndex, &elem); if (!elem) return; MWContextType cxType = MSG_GetResultElementType(elem); if (! (cxType == MWContextMail || cxType == MWContextMailMsg || cxType == MWContextNews || cxType == MWContextNewsMsg)) return; // Get info about the message MSG_SearchValue *value; MSG_GetResultAttribute(elem, attribFolderInfo, &value); MSG_FolderInfo *folderInfo = value->u.folder; if (!folderInfo) return; // Create the thread window if it doesn't exist CThreadWindow *threadWindow = CThreadWindow::FindOrCreate( folderInfo, CThreadView::cmd_UnselectAllCells); } Catch_(inErr) { Throw_(inErr); } EndCatch_ } // CSearchTableView::FindOrCreateThreadWindowForMessage //----------------------------------- void CSearchTableView::FindCommandStatus(CommandT inCommand, Boolean &outEnabled, Boolean &outUsesMark, Char16 &outMark, Str255 outName) //----------------------------------- { ResIDT menuID; Int16 menuItem; // Check for synthetic commands if( IsSyntheticCommand( inCommand, menuID, menuItem ) ) { switch( menuID ) { case menu_Navigator: // enable menus outEnabled = true; break; case menu_Go: // disable menus case menu_View: outEnabled = false; break; } } else // check for regular commands { switch (inCommand) { case cmd_AboutPlugins: // always enabled case cmd_About: case cmd_Quit: case cmd_Close: case cmd_Preferences: outEnabled = true; break; case cmd_SelectAll: // need at least one result if( IsValidRow( 1 ) ) outEnabled = true; else outEnabled = false; break; case cmd_Clear: // need at least one result and one selection // Only support deleting of Mail Messages not news. CMailSelection selection; if ( GetSelection( selection ) ) { const MSG_ViewIndex *indices = selection.GetSelectionList(); MSG_ResultElement* outElement; MSG_FolderInfo* outFolder; MessageKey outKey; GetMessageResultInfo( *indices, outElement, outFolder, outKey ); CMessageFolder messageFolder( outFolder ); UInt32 folderFlags = messageFolder.GetFolderFlags(); if ( (folderFlags & MSG_FOLDER_FLAG_NEWSGROUP) == 0 ) outEnabled = true; // mail message - delete command } break; default: Inherited::FindCommandStatus(inCommand, outEnabled, outUsesMark, outMark, outName); break; } } } //----------------------------------- Boolean CSearchTableView::GetMessageResultInfo( MSG_ViewIndex inMsgIndex, MSG_ResultElement*& outElement, MSG_FolderInfo*& outFolder, MessageKey& outKey) //----------------------------------- { return GetMessageResultInfo( GetMessagePane(), inMsgIndex, outElement, outFolder, outKey); } // SearchTableView::GetMessageResultInfo //----------------------------------- Boolean CSearchTableView::GetMessageResultInfo( MSG_Pane* inPane, MSG_ViewIndex inMsgIndex, MSG_ResultElement*& outElement, MSG_FolderInfo*& outFolder, MessageKey& outKey) //----------------------------------- { MSG_SearchError err; err = ::MSG_GetResultElement(inPane, inMsgIndex, &outElement); if (err != SearchError_Success || outElement == nil) return false; MWContextType cxType = ::MSG_GetResultElementType(outElement); if (! (cxType == MWContextMail || cxType == MWContextMailMsg || cxType == MWContextNews || cxType == MWContextNewsMsg)) return false; // Get info about the message MSG_SearchValue *value; err = ::MSG_GetResultAttribute(outElement, attribMessageKey, &value); if (err != SearchError_Success || value == nil) return false; outKey = value->u.key; if (outKey == MSG_MESSAGEKEYNONE) return false; // Get info about the folder the message is in err = ::MSG_GetResultAttribute(outElement, attribFolderInfo, &value); if (err == SearchError_Success && value != nil) outFolder = value->u.folder; return true; } // SearchTableView::GetMessageResultInfo //----------------------------------- void CSearchTableView::AddRowDataToDrag(TableIndexT inRow, DragReference inDragRef) // Base class implementation is for a message search. Return the URL. //----------------------------------- { MSG_ResultElement *elem = NULL; MSG_FolderInfo *folderInfo = nil; MessageKey key; // get key and folder info if (!GetMessageResultInfo(inRow - 1, elem, folderInfo, key)) return; if (folderInfo == nil) return; // MSG_Pane* pane = ::MSG_FindPaneOfType( // CMailNewsContext::GetMailMaster(), folderInfo, MSG_THREADPANE); MSG_Pane* pane = GetMessagePane(); // get search pane from the search manager if (!pane) // fails unless there is a threadpane open to this folder. Too bad. return; // That's why I turned off dragging. // create URL URL_Struct* url = ::MSG_ConstructUrlForMessage( pane, key ); if (!url) return; // we always return here, as far as I can see // add the drag item OSErr err = ::AddDragItemFlavor( inDragRef, inRow, 'TEXT', url->address, XP_STRLEN(url->address), flavorSenderOnly); NET_FreeURLStruct(url); } //----------------------------------- void CSearchTableView::ShowMessage(MSG_ViewIndex inMsgIndex, SearchWindowOpener inWindow) //----------------------------------- { AssertFail_(mSearchManager != nil); Try_ { MSG_ResultElement *elem = NULL; MSG_FolderInfo *folderInfo = nil; MessageKey key; if (!GetMessageResultInfo(inMsgIndex, elem, folderInfo, key)) return; if (folderInfo == nil && inWindow != kInMessageWindow) return; // Got the info we wanted: we can display now... if (inWindow == kInMessageWindow) { // Check if there's an open window with this message. CMessageWindow *messageWindow = CMessageWindow::FindAndShow(key); if (messageWindow) { messageWindow->Select(); return; } // If option key down, try to reuse a window if ( CApplicationEventAttachment::CurrentEventHasModifiers(optionKey) ) { messageWindow = CMessageWindow::FindAndShow(0); messageWindow->Select(); } // Create the window if (messageWindow == nil) { // We should probably create a generic utility function to generate // windows that contain a CHTMLView CBrowserContext *theContext = new CBrowserContext(MWContextMailMsg); FailNIL_(theContext); StSharer theShareLock(theContext); // Create a message window with this view as its super commander. LWindow *theWindow = LWindow::CreateWindow(cMessageWindowPPobID, LCommander::GetTopCommander()); FailNIL_(theWindow); messageWindow = dynamic_cast(theWindow); FailNIL_(messageWindow); messageWindow->SetWindowContext(theContext); } // Show the message messageWindow->GetMessageView()->ShowSearchMessage( CMailNewsContext::GetMailMaster(), elem, folderInfo == nil); } else { CommandT command; switch (inWindow) { case kInThreadWindow: command = CThreadView::cmd_UnselectAllCells; break; case kAddToThreadWindowSelection: command = cmd_Nothing; break; default: return; } // Check if there's an open window with this folder, and open one if needed CThreadWindow *threadWindow = CThreadWindow::FindAndShow(folderInfo, true, command); if (threadWindow) { // Show the message threadWindow->ShowMessageKey(key); } } } Catch_(inErr) { Throw_(inErr); } EndCatch_ } // CSearchTableView::ShowMessage //----------------------------------- Boolean CSearchTableView::IsValidRow(TableIndexT inRow) const // *** WARNING *** // USING mRows is WRONG WHEN DELETING ROWS, it's possibly changed already! // Use the Table Selector's value instead. That's why this override is here! //----------------------------------- { if (inRow < 1) return false; if (inRow > mRows ) /* ((LTableRowSelector*)mTableSelector)->GetRowCount()) */ return false; return true; } //----------------------------------- MessageKey CSearchTableView::GetRowMessageKey(TableIndexT inRow) // Get the message key for the specified row. Return MSG_MESSAGEKEYNONE if the // specified inRow is invalid. //----------------------------------- { AssertFail_(mSearchManager != nil); if ( !IsValidRow(inRow) ) return MSG_MESSAGEKEYNONE; MSG_ViewIndex inIndex = inRow - 1; UpdateMsgResult(inIndex); MSG_SearchValue *value = nil; MSG_SearchAttribute attribute = attribMessageKey; CSearchManager::FailSearchError(MSG_GetResultAttribute(mResultElement, attribute, &value)); MessageKey key = value->u.key; MSG_SearchError error = MSG_DestroySearchValue(value); AssertFail_(error == SearchError_Success); // What to do with an error? return key; } //----------------------------------- MSG_ResultElement* CSearchTableView::GetCurrentResultElement(TableIndexT inRow) //----------------------------------- { AssertFail_(mSearchManager != nil); if ( !IsValidRow(inRow) ) return nil; MSG_ResultElement *result; CSearchManager::FailSearchError(MSG_GetResultElement(GetMessagePane(), inRow-1, &result)); return result; } /*====================================================================================== Get the current sort params for the result data. ======================================================================================*/ void CSearchTableView::GetSortParams(MSG_SearchAttribute *outSortKey, Boolean *outSortBackwards) { AssertFail_(mTableHeader != nil); AssertFail_(mSearchManager != nil); PaneIDT sortColumnID; mTableHeader->GetSortedColumn(sortColumnID); *outSortKey = CSearchWindowBase::AttributeFromDataType(sortColumnID); AssertFail_(*outSortKey != kNumAttributes); *outSortBackwards = mTableHeader->IsSortedBackwards(); } /*====================================================================================== Force a sort to occur based upon the current sort column. ======================================================================================*/ void CSearchTableView::ForceCurrentSort() { MSG_SearchAttribute sortKey; Boolean sortBackwards; GetSortParams(&sortKey, &sortBackwards); CSearchManager::FailSearchError(MSG_SortResultList(GetMessagePane(), sortKey, sortBackwards)); } //----------------------------------- void CSearchTableView::ChangeSort(const LTableHeader::SortChange *inSortChange) // Notification to sort the table. //----------------------------------- { AssertFail_(mSearchManager != nil); Inherited::ChangeSort(inSortChange); MSG_SearchAttribute sortKey = CSearchWindowBase::AttributeFromDataType(inSortChange->sortColumnID); if ( sortKey != kNumAttributes ) { ::SetCursor(*::GetCursor(watchCursor)); // Call BE to sort the table AssertFail_(GetMessagePane() != nil); CSearchManager::FailSearchError(MSG_SortResultList(GetMessagePane(), sortKey, inSortChange->reverseSort)); } } //----------------------------------- void CSearchTableView::SetWinCSID(Int16 wincsid) // Set the font for the view //----------------------------------- { if (wincsid == INTL_CharSetNameToID(INTL_ResourceCharSet())) this->SetTextTraits( 8603 ); else this->SetTextTraits(CPrefs::GetTextFieldTextResIDs(wincsid)); Refresh(); } //----------------------------------- //void CSearchTableView::AddSelectionToDrag( // DragReference inDragRef, // RgnHandle inDragRgn ) //----------------------------------- //{ // Since we inherit from CMailFlexTable, we want to avoid it and just call the // CStandardFlexTable method, thereby avoiding the CMailFlexTable behavior // CStandardFlexTable::AddSelectionToDrag(inDragRef, inDragRgn); //} // CAddressSearchTableView::AddSelectionToDrag //----------------------------------- void CSearchTableView::SelectionChanged() // adjust the 'Move Message To' popup accordingly to the current selection //----------------------------------- { Inherited::SelectionChanged(); // Set the 'Move Message' popup to the folder of the first selected item CMailSelection selection; if (GetSelection(selection)) { CMessageSearchWindow* myWindow = dynamic_cast (LWindow::FetchWindowObject(GetMacPort())); if (myWindow) { CMailFolderPopupMixin* fileMessagePopup; FindUIItemPtr(myWindow, CMessageSearchWindow::paneID_FilePopup, fileMessagePopup); const MSG_ViewIndex *indices = selection.GetSelectionList(); MSG_ViewIndex numIndices = selection.selectionSize; MSG_ResultElement* outElement; MSG_FolderInfo* outFolder; MessageKey outKey; if (GetMessageResultInfo(*indices, outElement, outFolder, outKey)) fileMessagePopup->MSetSelectedFolder(outFolder, false); } } } // /*====================================================================================== Get the display text for the specified attribute and table row index. Return true if the text could be gotten. ======================================================================================*/ Boolean CSearchTableView::GetDisplayText(MSG_ViewIndex inIndex, MSG_SearchAttribute inAttribute, CStr255& outString) { AssertFail_(GetMessagePane() != nil); UpdateMsgResult(inIndex); MSG_SearchValue *value = nil; MSG_SearchError error = MSG_GetResultAttribute(mResultElement, inAttribute, &value); //Assert_(error == SearchError_Success); if ( error != SearchError_Success ) return false; Int16 wincsid = 0; MSG_SearchValue *folderinfoValue = nil; error = MSG_GetResultAttribute(mResultElement, attribFolderInfo, &folderinfoValue); if( ( error == SearchError_Success ) && folderinfoValue && folderinfoValue->u.folder) { folderinfoValue->attribute = attribFolderInfo; wincsid = INTL_DocToWinCharSetID(MSG_GetFolderCSID(folderinfoValue->u.folder)); error = MSG_DestroySearchValue(folderinfoValue); Assert_(error == SearchError_Success); } switch ( inAttribute ) { case attribSender: case attribSubject: outString = ""; char *buf = IntlDecodeMimePartIIStr(value->u.string, wincsid, FALSE); if (buf) { outString = buf; XP_FREE(buf); break; } outString = value->u.string; break; case attribLocation: case attribCommonName: case attrib822Address: case attribOrganization: case attribLocality: case attribPhoneNumber: outString = value->u.string; break; case attribDate: outString = MSG_FormatDate(GetMessagePane(), value->u.date); break; case attribMsgStatus: case attribPriority: { char name[32]; if ( inAttribute == attribMsgStatus ) { MSG_GetStatusName(value->u.msgStatus, name, sizeof(name)); } else { MSG_GetPriorityName(value->u.priority, name, sizeof(name)); } outString = name; } break; default: AssertFail_(false); break; } error = MSG_DestroySearchValue(value); Assert_(error == SearchError_Success); return true; } /*====================================================================================== Call this method whenever the indexes in the message pane change. ======================================================================================*/ void CSearchTableView::UpdateMsgResult(MSG_ViewIndex inIndex) { AssertFail_(GetMessagePane() != nil); if ( (mResultElement == nil) || (mResultIndex != inIndex) ) { mResultElement = nil; CSearchManager::FailSearchError(MSG_GetResultElement(GetMessagePane(), inIndex, &mResultElement)); mResultIndex = inIndex; } } /*====================================================================================== React to search message. ======================================================================================*/ void CSearchTableView::StartSearch(MWContext *inContext, MSG_Master *inMaster, MSG_SearchAttribute inSortKey, Boolean inSortBackwards) { if ( !mSearchManager->CanSearch() || mSearchManager->IsSearching() ) return; MsgPaneChanged(); // For results // Prepare for a new search session NewSearchSession(inContext, inMaster, inSortKey, inSortBackwards); mSearchManager->SetMSGPane( GetMessagePane() ); // Add scopes to search mSearchManager->AddScopesToSearch(inMaster); // Add search parameters mSearchManager->AddParametersToSearch(); CSearchManager::FailSearchError(MSG_Search(GetMessagePane())); mSearchManager->StartSearch(); mSearchManager->SetIsSearching( true ); } /*====================================================================================== Prepare for a new search session. ======================================================================================*/ void CSearchTableView::NewSearchSession(MWContext *inContext, MSG_Master *inMaster, MSG_SearchAttribute inSortKey, Boolean inSortBackwards) { AssertFail_(inContext != nil); AssertFail_(inMaster != nil); if ( GetMessagePane() == nil ) { // Create the search pane and store related date MSG_Pane* msgPane = MSG_CreateSearchPane(inContext, inMaster); FailNIL_(msgPane); SetMessagePane( msgPane ); MSG_SetFEData( GetMessagePane(), CMailCallbackManager::Get() ); } else { // Free any previously allocated search memory CSearchManager::FailSearchError(MSG_SearchFree(GetMessagePane())); } // Alloc mem for new search parameters CSearchManager::FailSearchError(MSG_SearchAlloc(GetMessagePane())); // Setup the sort order CSearchManager::FailSearchError( MSG_SortResultList(GetMessagePane(), inSortKey, inSortBackwards) ); } void CSearchTableView::ChangeFinished(MSG_Pane * inPane, MSG_NOTIFY_CODE inChangeCode, TableIndexT inStartRow, SInt32 inRowCount) { MsgPaneChanged(); CMailFlexTable::ChangeFinished( inPane, inChangeCode, inStartRow, inRowCount ); }