home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / xfe / src / MsgView.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  35.2 KB  |  1,239 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18. /* 
  19.    MsgView.cpp -- class for displaying the actual text (well, html) of a message.
  20.    Created: Chris Toshok <toshok@netscape.com>, 7-Aug-96.
  21.    */
  22.  
  23.  
  24.  
  25. #include "MsgView.h"
  26. #include "FolderMenu.h"
  27. #include "xpassert.h"
  28. #include "xfe.h"
  29. #include "msgcom.h"
  30. #include "xpgetstr.h"
  31. #include "prefapi.h"
  32.  
  33. #include "ViewGlue.h"
  34. #include "MNSearchFrame.h"
  35. #include "ThreadFrame.h"
  36. #include "ThreadView.h"
  37. #include "ThreePaneView.h"
  38. #include "MozillaApp.h"    // to get the MsgFrame
  39. #include "Xfe/Xfe.h"
  40. #include <intl_csi.h>       /* to get/set doc_csid/win_csid */
  41.  
  42. #include "prefs.h"
  43.  
  44. #include <Xm/Label.h>
  45.  
  46. #ifdef DEBUG_toshok
  47. #define D(x) x
  48. #else
  49. #define D(x)
  50. #endif
  51.  
  52. extern int MK_MSG_CANCEL_MESSAGE;
  53.  
  54. extern "C" 
  55. {
  56.     void fe_print_cb (Widget widget, XtPointer closure, XtPointer call_data);
  57. }
  58.  
  59. MenuSpec XFE_MsgView::separator_spec[] = {
  60.     MENU_SEPARATOR,
  61.     { NULL }
  62. };
  63.  
  64. MenuSpec XFE_MsgView::repl_spec[] = {
  65.     // Setting the call data to non-null is a signal to commandToString
  66.     // not to reset the menu string
  67.     { xfeCmdReplyToSender,    PUSHBUTTON, NULL, NULL, NULL, "reply-to-sender" },
  68.     { xfeCmdReplyToAll,               PUSHBUTTON, NULL, NULL, NULL, "reply-to-all" },
  69.     { xfeCmdForwardMessage,    PUSHBUTTON },
  70.     { xfeCmdForwardMessageQuoted,    PUSHBUTTON },
  71.     { NULL }
  72. };
  73.  
  74. MenuSpec XFE_MsgView::addr_spec[] = {
  75.     { "addToAddrBkSubmenu", CASCADEBUTTON, (MenuSpec*)&addToAddrbk_submenu_spec },
  76.     { NULL }
  77. };
  78.  
  79. MenuSpec XFE_MsgView::filemsg_spec[] = {
  80.   { "fileSubmenu",              DYNA_CASCADEBUTTON, NULL, NULL, False, (void*)xfeCmdMoveMessage, XFE_FolderMenu::generate },
  81.   { xfeCmdDeleteMessage,    PUSHBUTTON },
  82.   { xfeCmdSaveMessagesAs,    PUSHBUTTON },
  83.   { xfeCmdPrint,        PUSHBUTTON },
  84.   { NULL }
  85. };
  86.  
  87. MenuSpec XFE_MsgView::openLinkNew_spec[] = {
  88.   { xfeCmdOpenLinkNew , PUSHBUTTON },
  89.   { NULL },
  90. };
  91. MenuSpec XFE_MsgView::openLinkEdit_spec[] = {
  92.   { xfeCmdOpenLinkEdit, PUSHBUTTON },
  93.   { NULL },
  94. };
  95. MenuSpec XFE_MsgView::showImage_spec[] = {
  96.   { xfeCmdShowImage, PUSHBUTTON },
  97.   { NULL },
  98. };
  99. MenuSpec XFE_MsgView::stopLoading_spec[] = {
  100.   { xfeCmdStopLoading, PUSHBUTTON },
  101.   { NULL },
  102. };
  103. MenuSpec XFE_MsgView::openImage_spec[] = {
  104.   { xfeCmdOpenImage, PUSHBUTTON },
  105.   { NULL },
  106. };
  107. MenuSpec XFE_MsgView::saveLink_spec[] = {
  108.   { xfeCmdSaveLink, PUSHBUTTON },
  109.   { NULL },
  110. };
  111. MenuSpec XFE_MsgView::saveImage_spec[] = {
  112.   { xfeCmdSaveImage, PUSHBUTTON },
  113.   { NULL },
  114. };
  115. MenuSpec XFE_MsgView::copy_spec[] = {
  116.   { xfeCmdCopy, PUSHBUTTON },
  117.   { NULL },
  118. };
  119. MenuSpec XFE_MsgView::copyLink_spec[] = {
  120.   { xfeCmdCopyLink, PUSHBUTTON },
  121.   { NULL },
  122. };
  123. MenuSpec XFE_MsgView::copyImage_spec[] = {
  124.   { xfeCmdCopyImage, PUSHBUTTON },
  125.   { NULL },
  126. };
  127.  
  128.  
  129. MenuSpec XFE_MsgView::addToAddrbk_submenu_spec[] = {
  130.     { xfeCmdAddSenderToAddressBook, PUSHBUTTON },
  131.     { xfeCmdAddAllToAddressBook, PUSHBUTTON },
  132.     { NULL }
  133. };
  134.  
  135. const char *XFE_MsgView::messageHasChanged = "XFE_MsgView::messageHasChanged";
  136. const char *XFE_MsgView::lastMsgDeleted = "XFE_MsgView::lastMsgDeleted";
  137. const char *XFE_MsgView::spacebarAtMsgBottom = "XFE_MsgView::spacebarAtMsgBottom";
  138.  
  139. XFE_MsgView::XFE_MsgView(XFE_Component *toplevel_component,
  140.              Widget parent,
  141.              XFE_View *parent_view,
  142.              MWContext *context,
  143.              MSG_Pane *p)
  144.   : XFE_MNView(toplevel_component, parent_view, context, p)
  145. {
  146.  
  147.   m_frameDeleted = False;
  148.   m_updateThread = False;
  149.  
  150.   // create common parent for pane and attachment panel
  151.   Widget panedW=XmCreatePanedWindow(parent,"msgViewPane",NULL,0);
  152.   XtVaSetValues(panedW,
  153.                 XmNshadowThickness,0,
  154.                 XmNmarginWidth,0,
  155.                 XmNmarginHeight,0,
  156.                 NULL);
  157.  
  158.  
  159.   m_htmlView = new XFE_HTMLView(toplevel_component, panedW, this, m_contextData);
  160.   addView(m_htmlView);
  161.  
  162.   // create attachment panel
  163.   m_attachApplHeight=0;
  164.   m_attachUserHeight=0;
  165.   m_attachPanel = new XFE_ReadAttachPanel(m_contextData);
  166.   m_attachPanel->createWidgets(panedW);
  167.   XtVaSetValues(m_attachPanel->getBaseWidget(),XmNskipAdjust,TRUE,NULL);
  168.   m_attachPanel->setPaneHeight(1);
  169.  
  170.   /* test frame type
  171.    */
  172.   XFE_Frame *frame = (XFE_Frame*)m_toplevel;
  173.   if (frame &&
  174.       FRAME_MAILNEWS_MSG == frame->getType()) {
  175.       /* standalone frame
  176.        */          
  177.       frame->registerInterest(XFE_Frame::allConnectionsCompleteCallback,
  178.                                 this,
  179.                                 (XFE_FunctionNotification)allConnectionsComplete_cb);
  180.   }/* if */
  181.  
  182.   m_htmlView->registerInterest(XFE_HTMLView::spacebarAtPageBottom,
  183.                                this,
  184.                                (XFE_FunctionNotification)spaceAtMsgEnd_cb);
  185.  
  186.   m_htmlView->registerInterest(XFE_HTMLView::popupNeedsShowing,
  187.                                this,
  188.                                (XFE_FunctionNotification)showPopup_cb);
  189.  
  190.   XFE_MozillaApp::theApp()->registerInterest(XFE_MozillaApp::refreshMsgWindow,
  191.                                              this,
  192.                                              (XFE_FunctionNotification)refresh_cb);
  193.  
  194.   if (!p)
  195.     setPane(MSG_CreateMessagePane(m_contextData,
  196.                   XFE_MNView::getMaster()));
  197.  
  198.   m_folderInfo = NULL;
  199.   m_messageKey = MSG_MESSAGEKEYNONE;
  200.  
  201.   m_popup = NULL;
  202.  
  203.   m_htmlView->show();
  204.  
  205.   setBaseWidget(panedW);
  206. }
  207.  
  208. XFE_MsgView::~XFE_MsgView()
  209. {
  210.  
  211.   if (m_attachPanel) {
  212.     delete m_attachPanel;
  213.     m_attachPanel=NULL;
  214.   }
  215.  
  216.   if (m_popup)
  217.       delete m_popup;
  218.  
  219.   /* unregister from frame
  220.    */
  221.   /* test frame type
  222.    */
  223.   XFE_Frame *frame = (XFE_Frame*)m_toplevel;
  224.   if (frame &&
  225.       FRAME_MAILNEWS_MSG == frame->getType()) {
  226.       /* standalone frame
  227.        */          
  228.       frame->unregisterInterest(XFE_Frame::allConnectionsCompleteCallback,
  229.                                   this,
  230.                                   (XFE_FunctionNotification)allConnectionsComplete_cb);
  231.   }/* if */
  232.  
  233.   m_htmlView->unregisterInterest(XFE_HTMLView::spacebarAtPageBottom,
  234.                                  this,
  235.                                  (XFE_FunctionNotification)spaceAtMsgEnd_cb);
  236.   
  237.   m_htmlView->unregisterInterest(XFE_HTMLView::popupNeedsShowing,
  238.                                  this,
  239.                                  (XFE_FunctionNotification)showPopup_cb);
  240.  
  241.   XFE_MozillaApp::theApp()->unregisterInterest(XFE_MozillaApp::refreshMsgWindow,
  242.                                                this,
  243.                                                (XFE_FunctionNotification)refresh_cb);
  244.   
  245.   destroyPane();
  246.  
  247.   // XFE_View destroys m_htmlView for us.
  248. }
  249.  
  250. XFE_CALLBACK_DEFN(XFE_MsgView, allConnectionsComplete)(XFE_NotificationCenter *,
  251.                                                        void *,
  252.                                                        void *)
  253. {
  254.     /* 1. check if deleted/filed
  255.      */
  256.     if (m_updateThread) {
  257.         /* 2. get threadframe with 
  258.          *    XFE_ThreadFrame::frameForInfo(MSG_FolderInfo *info)
  259.          */
  260.         MSG_FolderInfo *folderInfo = MSG_GetCurFolder(m_pane);
  261.         XFE_ThreadFrame *tFrame = 
  262.             XFE_ThreadFrame::frameForInfo(folderInfo);
  263.         if (tFrame) {
  264.             /* 3. urge to update msg line and body
  265.              */
  266. #ifdef USE_3PANE
  267.             XFE_ThreadView *tview = 0;
  268.             XFE_ThreePaneView* tpview = (XFE_ThreePaneView*)tFrame->getView();
  269.             if (tpview)
  270.                 tview = (XFE_ThreadView*)tpview->getThreadView();
  271. #else
  272.             XFE_ThreadView *tview = 
  273.                 (XFE_ThreadView *) tFrame->getView();
  274. #endif
  275.             if (tview)
  276.                 tview->processCmdQueue();
  277.         }/* if tFrame */
  278.         m_updateThread = False;
  279.     }/* if */
  280. }
  281.  
  282. void
  283. XFE_MsgView::loadMessage(MSG_FolderInfo *info,
  284.              MessageKey key)
  285. {
  286.  
  287.   MSG_FolderLine folderLine;
  288.  
  289.   m_folderInfo = info;
  290.   m_messageKey = key;
  291.  
  292.   MSG_GetFolderLineById(XFE_MNView::getMaster(), m_folderInfo, &folderLine);
  293.   
  294.   if (folderLine.flags & MSG_FOLDER_FLAG_NEWSGROUP
  295.       || folderLine.flags & MSG_FOLDER_FLAG_CATEGORY)
  296.       m_displayingNewsgroup = TRUE;
  297.   else
  298.       m_displayingNewsgroup = FALSE;
  299.  
  300.  
  301.   MSG_LoadMessage(m_pane, info, MSG_MESSAGEKEYNONE);
  302.  
  303.   // set up attachment notify callback
  304.   static MSG_MessagePaneCallbacks attachmentCb = {
  305.       XFE_MsgView::AttachmentCountCb,
  306.       XFE_MsgView::ToggleAttachmentPanelCb
  307.   };
  308.   MSG_SetMessagePaneCallbacks(m_pane,&attachmentCb,(void*)this);
  309.  
  310.   // remove previous message's attachments before displaying next message
  311.   if (m_attachPanel) {
  312.       m_attachPanel->removeAllAttachments();
  313.       m_attachPanel->setPaneHeight(1);
  314.   }
  315.  
  316.   // reset panel height memory for next message
  317.   m_attachApplHeight=0;
  318.   m_attachUserHeight=0;
  319.  
  320.   MSG_LoadMessage(m_pane, info, key);
  321.  
  322. }
  323.  
  324.  
  325. // called as each attachment is processed
  326. void
  327. XFE_MsgView::AttachmentCountCb(MSG_Pane*,void *closure,int32 count,XP_Bool done)
  328. {
  329.   if (closure) {
  330.     XFE_MsgView *m=(XFE_MsgView*)closure;
  331.     m->attachmentCountCb(count,done);
  332.   }
  333. }
  334.  
  335.  
  336. // show panel as soon as we detect attachments, display attachment icons when
  337. // all attachments have been loaded.
  338. void
  339. XFE_MsgView::attachmentCountCb(int32 count,XP_Bool done)
  340. {
  341.   if (count>0) {
  342.     // Prepare to display attachment panel if there is at least 1 attachment
  343.     // The panel will be shown as soon as we detect attachments, but will fill
  344.     // up only when all attachments are loaded. Gives user early-warning of
  345.     // attachments. If we held off managing panel until all attachments
  346.     // are loaded, it will cause unexpected relayout after user has started
  347.     // reading body of message.
  348.     
  349.     // erase previous message attachments
  350.     m_attachPanel->removeAllAttachments();
  351.  
  352.     // add icons only when all attachments have been loaded
  353.     if (done) {
  354.       MSG_AttachmentData* data;
  355.       done = FALSE;
  356.       MSG_GetViewedAttachments(m_pane, &data, &done);
  357.       XP_ASSERT(done);
  358.       if (data) {
  359.           m_attachPanel->addAttachments(m_pane,data);
  360.           m_attachPanel->updateDisplay();
  361.       }
  362.  
  363.       // if user has tried to resize panel blindly, then
  364.       // expand it to the preferred height. This ignores
  365.       // their guess, but they were probably wrong anyway,
  366.       // and should have been more patient. Yeah.
  367.       m_attachApplHeight=0;
  368.       m_attachUserHeight=0;
  369.       if (m_attachPanel->getPaneHeight()<10) {
  370.           // panel effectively closed. make it completely closed for neatness.
  371.           m_attachPanel->setPaneHeight(1);
  372.       }
  373.       else {
  374.           // panel is open, reset height to ideal
  375.           setAttachPrefHeight();
  376.       }
  377.     }
  378.  
  379.     // manage attachment panel
  380.     m_attachPanel->show();
  381.     
  382.   }
  383.   else {
  384.     // count==0 - either load is in-progress, or there are no attachments
  385.     if (done) {
  386.         // definitely no attachments - hide attachment panel and
  387.         // remove old attachment data
  388.         if (m_attachPanel) {
  389.             m_attachPanel->hide();
  390.             m_attachPanel->removeAllAttachments();
  391.         }
  392.     }
  393.   }
  394. }
  395.  
  396. // called when user clicks attachment icon in message header
  397. void
  398. XFE_MsgView::ToggleAttachmentPanelCb(MSG_Pane*,void *closure)
  399. {
  400.   if (closure) {
  401.     XFE_MsgView *m=(XFE_MsgView*)closure;
  402.     m->toggleAttachmentPanelCb();
  403.   }
  404. }
  405.  
  406. void
  407. XFE_MsgView::toggleAttachmentPanelCb()
  408. {
  409.     if (!m_attachPanel)
  410.         return;
  411.  
  412.     int currentHeight=m_attachPanel->getPaneHeight();
  413.     
  414.     if (currentHeight<10) {
  415.         // expand attachment panel
  416.         // if user hasn't set size, choose size to show all attachments
  417.         if (!m_attachUserHeight)
  418.             setAttachPrefHeight();
  419.         else
  420.             m_attachPanel->setPaneHeight(m_attachUserHeight);
  421.     }
  422.     else {
  423.         // contract attachment panel
  424.  
  425.         // if user changed pane size, remember it
  426.         if (!m_attachApplHeight || currentHeight!=m_attachApplHeight)
  427.             m_attachUserHeight=currentHeight;
  428.         
  429.         m_attachPanel->setPaneHeight(1);
  430.     }
  431. }
  432.  
  433.  
  434. void
  435. XFE_MsgView::setAttachPrefHeight()
  436. {
  437.     if (!m_attachPanel || !getBaseWidget())
  438.         return;
  439.  
  440.     // pick a default height for the attachment panel.
  441.     // Make large enough to view all attachments without
  442.     // scrolling, up to a maximum of 70% of the message
  443.     // pane height - don't want to obscure the attachment
  444.     // show/hide link in the message header.
  445.     
  446.     Dimension parentHeight;
  447.     XtVaGetValues(getBaseWidget(),XmNheight,&parentHeight,NULL);
  448.  
  449.     int prefHeight=m_attachPanel->getPreferredHeight();
  450.  
  451.     if (prefHeight > (7*parentHeight/10))
  452.         prefHeight=(7*parentHeight/10);
  453.     
  454.     m_attachApplHeight=prefHeight;
  455.     m_attachPanel->setPaneHeight(m_attachApplHeight);
  456. }
  457.  
  458. MSG_FolderInfo *
  459. XFE_MsgView::getFolderInfo()
  460. {
  461.   return m_folderInfo;
  462. }
  463.  
  464. MessageKey
  465. XFE_MsgView::getMessageKey()
  466. {
  467.   return m_messageKey;
  468. }
  469.  
  470. void
  471. XFE_MsgView::paneChanged(XP_Bool asynchronous,
  472.              MSG_PANE_CHANGED_NOTIFY_CODE code,
  473.              int32 value)
  474. {
  475.   switch (code)
  476.     {
  477.     case MSG_PaneNotifyFolderDeleted:
  478.         {
  479.             /* test frame type
  480.              */
  481.             XFE_Frame *frame = (XFE_Frame*)m_toplevel;
  482.             if (frame &&
  483.                 FRAME_MAILNEWS_MSG == frame->getType()) {
  484.                 /* standalone frame
  485.                  */    
  486.                 if (!m_frameDeleted) {
  487.                     frame->delete_response();
  488.                     m_frameDeleted = True;
  489.                 }/* if */
  490.                 
  491.             }/* if */
  492.         }
  493.         /* shall we update banner or simply return? NO
  494.          */
  495.         return;
  496.  
  497.     case MSG_PaneNotifyMessageLoaded:
  498.         notifyInterested(messageHasChanged, (void*)value);
  499.  
  500.         {
  501.             MSG_FolderInfo *folderInfo = MSG_GetCurFolder(m_pane);
  502.  
  503.             /* We need to update this to reflect what the real folderInfo is
  504.              */
  505.             m_folderInfo = folderInfo;
  506.  
  507.             MessageKey key = (MessageKey) value;
  508.             
  509.             if (MSG_GetBacktrackState(m_pane) == MSG_BacktrackIdle)
  510.                 MSG_AddBacktrackMessage(m_pane, folderInfo, key);
  511.             else
  512.                 MSG_SetBacktrackState(m_pane, MSG_BacktrackIdle);
  513.         }
  514.         break;
  515.     case MSG_PaneNotifyLastMessageDeleted:
  516.         notifyInterested(lastMsgDeleted, (void*)value);
  517.         break;
  518.     default:
  519.         break;
  520.     }
  521.  
  522.   XFE_MNView::paneChanged(asynchronous, code, value);
  523. }
  524.  
  525. Boolean
  526. XFE_MsgView::isCommandSelected( CommandType cmd, void *calldata, XFE_CommandInfo*)
  527. {
  528.   MSG_CommandType msg_cmd;
  529.   XP_Bool selectable = False;;
  530.  
  531.   msg_cmd = commandToMsgCmd(cmd);
  532.       
  533.   if (msg_cmd != (MSG_CommandType)~0)
  534.   {
  535.       
  536.      if ( MSG_GetToggleStatus(m_pane, msg_cmd, NULL, 0 ) == MSG_Checked) 
  537.         selectable = True;
  538.      return (Boolean)selectable;
  539.   }
  540.   return XFE_MNView::isCommandSelected(cmd, calldata);
  541. }
  542.  
  543. Boolean 
  544. XFE_MsgView::isCommandEnabled(CommandType cmd, void *calldata, XFE_CommandInfo*)
  545. {
  546. #define IS_CMD(command) cmd == (command)
  547.  
  548.   XP_Bool selectable = FALSE;
  549.  
  550.     // DeleteMessage stands for CancelMessage in the ThreadView,
  551.     // so intercept it early:
  552.     if ( (IS_CMD(xfeCmdDeleteMessage) || IS_CMD(xfeCmdCancelMessages))
  553.          && isDisplayingNews() )
  554.     {
  555.       MSG_CommandStatus(m_pane, MSG_CancelMessage,
  556.                         NULL, 0,
  557.                         &selectable, NULL, NULL, NULL);
  558.       return selectable;
  559.     }
  560.  
  561.   MSG_MotionType nav_cmd;
  562.   MSG_CommandType msg_cmd;
  563.  
  564.   nav_cmd = commandToMsgNav(cmd);
  565.  
  566.   if (IS_CMD(xfeCmdSetPriorityHighest)
  567.       || IS_CMD(xfeCmdSetPriorityHigh)
  568.       || IS_CMD(xfeCmdSetPriorityNormal)
  569.       || IS_CMD(xfeCmdSetPriorityLow)
  570.       || IS_CMD(xfeCmdSetPriorityLowest)
  571.       || IS_CMD(xfeCmdSetPriorityNone)
  572.       || IS_CMD(xfeCmdPrint)
  573.       || IS_CMD(xfeCmdCopyMessage)
  574.       || IS_CMD(xfeCmdMoveMessage)
  575.       || IS_CMD(xfeCmdIgnoreThread)
  576.       || IS_CMD(xfeCmdWatchThread)
  577.       || IS_CMD(xfeCmdMarkMessageByDate))
  578.     {
  579.       MSG_ViewIndex index;
  580.       MessageKey key;
  581.       MSG_FolderInfo *info;
  582.       
  583.       MSG_GetCurMessage(m_pane, &info, &key, &index);
  584.  
  585.       return (key != MSG_MESSAGEKEYNONE);
  586.     }
  587.   else if (IS_CMD(xfeCmdMommy)
  588.       || IS_CMD(xfeCmdNewFolder)
  589.       || IS_CMD(xfeCmdRenameFolder)
  590.       || IS_CMD(xfeCmdEmptyTrash)
  591.       || IS_CMD(xfeCmdAddNewsgroup)
  592.       || IS_CMD(xfeCmdUpdateMessageCount)
  593.       || IS_CMD(xfeCmdPrintSetup)
  594.       || IS_CMD(xfeCmdPrintPreview))
  595.  
  596.       {
  597.           return True;
  598.       }
  599.   else if (IS_CMD(xfeCmdEditPreferences) ||
  600.            IS_CMD(xfeCmdWrapLongLines) )
  601.     {
  602.       return True;
  603.     }
  604.   else if ( IS_CMD(xfeCmdEditConfiguration) )
  605.   {
  606.      MSG_ViewIndex index ;
  607.      MessageKey key;
  608.      MSG_FolderInfo *info;
  609.      XP_Bool selectable = FALSE;
  610.      MSG_CommandType msg_cmd = commandToMsgCmd(cmd);
  611.  
  612.  
  613.         MSG_GetCurMessage(m_pane, &info, &key, &index);
  614.         MSG_CommandStatus(m_pane, msg_cmd,
  615.                         &index, 1,
  616.                         &selectable, NULL, NULL, NULL);
  617.  
  618.         return (selectable && !(isDisplayingNews()) );
  619.   }
  620.   else if ( IS_CMD(xfeCmdModerateDiscussion) )
  621.   {
  622.      MSG_ViewIndex index ;
  623.      MessageKey key;
  624.      MSG_FolderInfo *info;
  625.      XP_Bool selectable = FALSE;
  626.      MSG_CommandType msg_cmd = commandToMsgCmd(cmd);
  627.  
  628.  
  629.         MSG_GetCurMessage(m_pane, &info, &key, &index);
  630.         MSG_CommandStatus(m_pane, msg_cmd,
  631.                         &index, 1,
  632.                         &selectable, NULL, NULL, NULL);
  633.  
  634.         return (selectable && (isDisplayingNews()) );
  635.   }
  636.   else if (IS_CMD(xfeCmdGetNextNNewMsgs))
  637.       {
  638.           return m_displayingNewsgroup;
  639.       }
  640. #ifdef DEBUG_akkana
  641.   else if (IS_CMD(xfeCmdEditMessage))
  642.       {
  643.           return !m_displayingNewsgroup;
  644.       }
  645. #endif
  646.   else if (nav_cmd != (MSG_MotionType)~0)
  647.     {
  648.       MSG_ViewIndex index;
  649.       MessageKey key;
  650.       MSG_FolderInfo *info;
  651.       
  652.       MSG_GetCurMessage(m_pane, &info, &key, &index);
  653.       
  654.       if (MSG_NavigateStatus(m_pane, nav_cmd, index, &selectable, NULL) < 0)
  655.           selectable = FALSE;
  656.       
  657.       return selectable;
  658.     }
  659.   else 
  660.     {
  661.       msg_cmd = commandToMsgCmd(cmd);
  662.       
  663.       if (msg_cmd != (MSG_CommandType)~0)
  664.     {
  665.       
  666.         if (IS_CMD(xfeCmdGetNewMessages)) {
  667.             int num_inboxes = MSG_GetFoldersWithFlag(XFE_MNView::getMaster(),
  668.                                                      MSG_FOLDER_FLAG_INBOX,
  669.                                                      NULL, 0);
  670.             if ((num_inboxes > 0) ||
  671.                 (MSG_CommandStatus(m_pane, 
  672.                                    msg_cmd, NULL, 0, 
  673.                                    &selectable, NULL, NULL, NULL) < 0)) {
  674.                 selectable = FALSE;
  675.             }/* if */
  676.         }/* else */
  677.         else if (MSG_CommandStatus(m_pane, msg_cmd, NULL, 0, &selectable, NULL, NULL, NULL) < 0)
  678.         selectable = FALSE;
  679.  
  680.       return selectable;
  681.     }
  682.     }
  683.  
  684.   return XFE_MNView::isCommandEnabled(cmd, calldata);
  685. #undef IS_CMD
  686. }
  687.  
  688. void
  689. XFE_MsgView::doCommand(CommandType cmd,
  690.                        void *calldata, XFE_CommandInfo* info)
  691. {
  692. #define IS_CMD(command) cmd == (command)
  693.   
  694.   if (IS_CMD(xfeCmdMommy))
  695.       {
  696.           fe_showMessages(XtParent(getToplevel()->getBaseWidget()),
  697.                           ViewGlue_getFrame(m_contextData),
  698.                           NULL,
  699.                           m_folderInfo,
  700.                           fe_globalPrefs.reuse_thread_window,
  701.                           False, MSG_MESSAGEKEYNONE);
  702.       }
  703.   else if (IS_CMD(xfeCmdGetNewMessages))
  704.     {
  705.         getNewMail();
  706.     }
  707.   else if (IS_CMD(xfeCmdGetNextNNewMsgs))
  708.       {
  709.           getNewNews();
  710.       }
  711.   else if (IS_CMD(xfeCmdMarkMessageByDate))
  712.     {
  713.           markReadByDate();
  714.     }
  715.   else if (IS_CMD(xfeCmdEditPreferences))
  716.     {
  717.       fe_showMailNewsPreferences(getToplevel(), m_contextData);
  718.     }
  719.   else if (IS_CMD(xfeCmdPrint))
  720.     {
  721.     fe_print_cb  (CONTEXT_WIDGET (m_contextData),
  722.                      (XtPointer) m_contextData, NULL);
  723.  
  724.     getToplevel()->notifyInterested(XFE_View::chromeNeedsUpdating);
  725.  
  726.     }
  727.   else if (IS_CMD(xfeCmdSearch))
  728.     {
  729.         // We don't need to call XtParent on the base widget because
  730.         // the base widget is the real toplevel widget already...dora 12/31/96
  731.         // Grabbed this from ThreadView.cpp  -slamm 2/19/96
  732.         fe_showMNSearch(XfeAncestorFindApplicationShell(getToplevel()->getBaseWidget()),
  733.                         ViewGlue_getFrame(m_contextData),
  734.                         NULL, this, m_folderInfo);
  735.     }
  736.   else if (IS_CMD(xfeCmdSetPriorityHighest)
  737.        || IS_CMD(xfeCmdSetPriorityHigh)
  738.        || IS_CMD(xfeCmdSetPriorityNormal)
  739.        || IS_CMD(xfeCmdSetPriorityLow)
  740.        || IS_CMD(xfeCmdSetPriorityLowest)
  741.        || IS_CMD(xfeCmdSetPriorityNone))
  742.     {
  743.       MSG_PRIORITY priority = commandToPriority(cmd);
  744.  
  745.       MSG_SetPriority(m_pane, m_messageKey, priority);
  746.     }
  747.   else if (IS_CMD(xfeCmdGetNewMessages))
  748.     {
  749.       getNewMail();
  750.     }
  751.   else if ( IS_CMD(xfeCmdEditConfiguration) ||
  752.         IS_CMD(xfeCmdModerateDiscussion) )
  753.   {
  754.           MSG_ViewIndex index ;
  755.           MessageKey key;
  756.           MSG_FolderInfo *info;
  757.           MSG_CommandType msg_cmd = commandToMsgCmd(cmd);
  758.  
  759.           MSG_GetCurMessage(m_pane, &info, &key, &index);
  760.  
  761.           MSG_Command(m_pane, msg_cmd, &index, 1);
  762.   }
  763.   else if (isDisplayingNews()
  764.       && (IS_CMD(xfeCmdDeleteMessage) || IS_CMD(xfeCmdCancelMessages)))
  765.     {
  766.         // If this is a news article, then Delete Message
  767.         // is really Cancel Message:
  768.         MSG_Command(m_pane, MSG_CancelMessage, NULL, 0);
  769.         m_updateThread = True;
  770.     }
  771.   else if (IS_CMD(xfeCmdMoveMessage))
  772.     {
  773.         MSG_FolderInfo *info = (MSG_FolderInfo*)calldata;
  774.         MSG_ViewIndex index;
  775.         MSG_FolderInfo *curFolderInfo;
  776.         MessageKey key;
  777.         MSG_GetCurMessage(m_pane, &curFolderInfo, &key, &index);
  778.  
  779.         if (index && info) {
  780.             MSG_MoveMessagesIntoFolder(m_pane, &index, 1, info);
  781.             m_updateThread = True;
  782.         }/* if */
  783.     }
  784.   else if (cmd == xfeCmdChangeDocumentEncoding)
  785.     {
  786.       int/*16*/ new_doc_csid = (int/*16*/)calldata;
  787.       INTL_CharSetInfo c = LO_GetDocumentCharacterSetInfo(m_contextData);
  788.       
  789.       if (new_doc_csid != m_contextData->fe.data->xfe_doc_csid) 
  790.         {
  791.           /*
  792.            * Somebody is setting the doc_csid to a non-zero
  793.            * value, thereby screwing up our GetDefaultDocCSID
  794.            * routine. Make sure it gets set to zero here
  795.            * in the mail and news cases. Don't want to set
  796.            * it to zero in the Browser case, since it is
  797.            * legitimate there when the HTTP charset has been
  798.            * set. We override the charset in the mail and
  799.            * news cases.
  800.            */
  801.           INTL_SetCSIDocCSID(c, new_doc_csid);      
  802.           m_contextData->fe.data->xfe_doc_csid = new_doc_csid;
  803.           INTL_SetCSIWinCSID(c,
  804.           INTL_DocToWinCharSetID(new_doc_csid));
  805.           // now let our observers know that the encoding
  806.           // for this window needs to be changed.
  807.           getToplevel()->notifyInterested(XFE_Frame::encodingChanged, calldata);
  808.           MSG_SetFolderCSID(m_folderInfo, m_contextData->fe.data->xfe_doc_csid);
  809.         }
  810.     }
  811.   else if (cmd == xfeCmdSetDefaultDocumentEncoding)
  812.     {
  813.     }
  814.   else
  815.     {
  816.       MSG_CommandType msg_cmd = commandToMsgCmd(cmd);
  817.       MSG_MotionType nav_cmd = commandToMsgNav(cmd);
  818.  
  819.       if (info) {
  820.           CONTEXT_DATA(m_contextData)->stealth_cmd =
  821.               ((info->event->type == ButtonRelease) &&
  822.                (info->event->xkey.state & ShiftMask));
  823.       }
  824.  
  825.       if (nav_cmd == (MSG_MotionType)~0)
  826.     {
  827.       if ((msg_cmd == (MSG_CommandType)~0) ||
  828.           (msg_cmd == MSG_MailNew) ||
  829.           (msg_cmd == MSG_PostNew) )
  830.         {
  831.           XFE_MNView::doCommand(cmd, calldata, info);
  832.         }
  833.       else
  834.         {
  835.            if (msg_cmd == MSG_DeleteMessage)
  836.               /* stop loading since we are deleting this message:
  837.                * shall we check if it is stopable?
  838.                */
  839.               XP_InterruptContext(m_contextData);
  840.  
  841.           MSG_Command(m_pane, msg_cmd, NULL, 0);
  842.  
  843.           if (msg_cmd == MSG_DeleteMessage ||
  844.               msg_cmd == MSG_CancelMessage) {
  845.               m_updateThread = True;
  846.           }/* if */
  847.               
  848.               
  849.            /* stand alone msgpane
  850.             */
  851.            if (IS_CMD(xfeCmdUndo) 
  852.                || IS_CMD(xfeCmdRedo)) {
  853.                MessageKey key = MSG_MESSAGEKEYNONE;
  854.                MSG_FolderInfo *folder = NULL;
  855.  
  856.                if ( UndoComplete == MSG_GetUndoStatus(m_pane) ) {
  857.                    if (MSG_GetUndoMessageKey(m_pane, &folder, &key) && folder) 
  858.                        loadMessage(folder, key);
  859.                }/* if */
  860.            }/* if */
  861.  
  862.           XFE_MozillaApp::theApp()->notifyInterested(XFE_MNView::folderChromeNeedsUpdating, getFolderInfo());
  863.         }
  864.     }
  865.       else
  866.     {
  867.       MSG_ViewIndex index, threadIndex;
  868.       MessageKey key;
  869.       MessageKey resultId;
  870.       MSG_FolderInfo *info;
  871.       
  872.       MSG_GetCurMessage(m_pane, &info, &key, &index);
  873.  
  874.       // MsgView might be invoked from Search Dialog
  875.       // In this circumstances, m_folderInfo was
  876.       // not initialized. Therefore, we will see a crash when one
  877.       // tries to do Next Msg.
  878.       // We try to initialize m_folderInfo here base on current msg
  879.       // msg folder info. 
  880.  
  881.       // The above comment is not valid if we set m_folderInfo when MessageLoad
  882.       // notification arrives
  883.       //
  884.       if (!m_folderInfo) 
  885.           m_folderInfo = info;
  886.  
  887.       MSG_FolderInfo *curInfo = info;
  888.       MSG_ViewNavigate(m_pane, nav_cmd, index,
  889.                        &resultId, &index, &threadIndex, 
  890.                        &info);
  891.  
  892.       // ViewNavigate gives a NULL folderinfo if the folder
  893.       // info won't be changing.
  894.       if (!info) {
  895.           int idx = (int) index;
  896.           if (idx >= 0) {
  897.               info = m_folderInfo;
  898.               loadMessage(info, resultId);
  899.           }/* if */
  900.       }/* if */
  901.       else if (curInfo != info) {
  902.           /* folder changed
  903.            */
  904.           /* get parent view
  905.            */
  906.           XFE_ThreadView *threadView = (XFE_ThreadView *)getParent();
  907.  
  908.           /* load new folder 
  909.            */
  910.           if (threadView) {
  911.               /* in thread win
  912.                */
  913.               threadView->loadFolder(info);
  914.               threadView->setPendingCmdSelByKey(selectByKey, resultId);
  915.           }/* if */
  916.           else {
  917.               /* in message win
  918.                */
  919.               MWContext *thrContext = fe_showMessages(XtParent(getToplevel()->getBaseWidget()),
  920.                                                       ViewGlue_getFrame(m_contextData),
  921.                                                       NULL,
  922.                                                       info,
  923.                                                       fe_globalPrefs.reuse_thread_window,
  924.                                                       False, resultId);
  925.               if (thrContext) {
  926.                   XFE_ThreadFrame *tFrame = (XFE_ThreadFrame *) ViewGlue_getFrame(thrContext);
  927.                   threadView = (XFE_ThreadView *) tFrame->getView();
  928.                   if (threadView) {
  929.                       switch (nav_cmd) {
  930.                       case MSG_NextMessage:
  931.                       threadView->setPendingCmdSelByKey(selectFirstUnread, resultId);
  932.                       }/* switch */
  933.                   }/* if */
  934.               }/* if */
  935.  
  936.               /* load myself
  937.                */
  938.           }/* else */
  939.       }/* else if */
  940.  
  941.     }
  942.     }
  943. #undef IS_CMD
  944. }
  945.  
  946. Boolean
  947. XFE_MsgView::handlesCommand(CommandType cmd, void *calldata, XFE_CommandInfo*)
  948. {
  949. #define IS_CMD(command) cmd == (command)
  950.  
  951.   if ( IS_CMD(xfeCmdSaveAs)
  952.       || IS_CMD(xfeCmdSaveMessagesAs)
  953.  
  954.       || IS_CMD(xfeCmdAddSenderToAddressBook)
  955.       || IS_CMD(xfeCmdAddAllToAddressBook)
  956.  
  957.       || IS_CMD(xfeCmdSaveMessagesAs)
  958.       || IS_CMD(xfeCmdGetNewMessages)
  959.       || IS_CMD(xfeCmdGetNextNNewMsgs)
  960.       || IS_CMD(xfeCmdPrint)
  961.  
  962.       || IS_CMD(xfeCmdSendMessagesInOutbox)
  963.  
  964.       || IS_CMD(xfeCmdNextUnreadMessage)
  965.       || IS_CMD(xfeCmdPreviousUnreadMessage)
  966.       || IS_CMD(xfeCmdNextMessage)
  967.       || IS_CMD(xfeCmdPreviousMessage)
  968.       || IS_CMD(xfeCmdNextUnreadThread)
  969.       || IS_CMD(xfeCmdNextCollection)
  970.       || IS_CMD(xfeCmdNextUnreadCollection)
  971.       || IS_CMD(xfeCmdFirstFlaggedMessage)
  972.       || IS_CMD(xfeCmdPreviousFlaggedMessage)
  973.       || IS_CMD(xfeCmdNextFlaggedMessage)
  974.  
  975.       || IS_CMD(xfeCmdUndo)
  976.       || IS_CMD(xfeCmdRedo)
  977.       || IS_CMD(xfeCmdSearch)
  978.  
  979.       || IS_CMD(xfeCmdDeleteMessage)
  980.       || IS_CMD(xfeCmdEditPreferences)
  981.  
  982.       || IS_CMD(xfeCmdMarkMessageRead)
  983.       || IS_CMD(xfeCmdMarkMessageUnread)
  984.       || IS_CMD(xfeCmdMarkMessageByDate)
  985.       || IS_CMD(xfeCmdMarkAllMessagesRead)
  986.       || IS_CMD(xfeCmdMarkThreadRead)
  987.       || IS_CMD(xfeCmdMarkMessageForLater)
  988.       || IS_CMD(xfeCmdMarkMessage)
  989.  
  990.       || IS_CMD(xfeCmdReplyToSender)
  991.       || IS_CMD(xfeCmdReplyToAll)
  992.       || IS_CMD(xfeCmdReplyToNewsgroup)
  993.       || IS_CMD(xfeCmdReplyToSenderAndNewsgroup)
  994.       || IS_CMD(xfeCmdForwardMessage)
  995.       || IS_CMD(xfeCmdForwardMessageQuoted)
  996.       || IS_CMD(xfeCmdShowAllHeaders)
  997.       || IS_CMD(xfeCmdShowNormalHeaders)
  998.       || IS_CMD(xfeCmdShowBriefHeaders)
  999.       || IS_CMD(xfeCmdViewAttachmentsInline)
  1000.       || IS_CMD(xfeCmdViewAttachmentsAsLinks)
  1001.       || IS_CMD(xfeCmdRot13Message)
  1002.       || IS_CMD(xfeCmdMoveMessage)
  1003.       || IS_CMD(xfeCmdCopyMessage)
  1004.  
  1005.       || IS_CMD(xfeCmdNewFolder)
  1006.       || IS_CMD(xfeCmdRenameFolder)
  1007.       || IS_CMD(xfeCmdEmptyTrash)
  1008.       || IS_CMD(xfeCmdCompressAllFolders)
  1009.       || IS_CMD(xfeCmdAddNewsgroup)
  1010.       || IS_CMD(xfeCmdUpdateMessageCount)
  1011.       || IS_CMD(xfeCmdPrintSetup)
  1012.       || IS_CMD(xfeCmdPrintPreview)
  1013.       || IS_CMD(xfeCmdEditMessage)
  1014.  
  1015.       || IS_CMD(xfeCmdToggleThreadKilled)
  1016.       || IS_CMD(xfeCmdToggleThreadWatched)
  1017.  
  1018.       /* priorities. */
  1019.       || IS_CMD(xfeCmdSetPriorityHighest)
  1020.       || IS_CMD(xfeCmdSetPriorityHigh)
  1021.       || IS_CMD(xfeCmdSetPriorityNormal)
  1022.       || IS_CMD(xfeCmdSetPriorityLow)
  1023.       || IS_CMD(xfeCmdSetPriorityLowest)
  1024.       || IS_CMD(xfeCmdSetPriorityNone)
  1025.  
  1026.       || IS_CMD(xfeCmdIgnoreThread)
  1027.       || IS_CMD(xfeCmdWatchThread)
  1028.       || IS_CMD(xfeCmdModerateDiscussion)
  1029.  
  1030.       || IS_CMD(xfeCmdWrapLongLines)
  1031.       || IS_CMD(xfeCmdMommy))
  1032.     {
  1033.       return True;
  1034.     }
  1035.   else
  1036.     {
  1037.       return XFE_MNView::handlesCommand(cmd, calldata);
  1038.     }
  1039. #undef IS_CMD
  1040. }
  1041.  
  1042. char*
  1043. XFE_MsgView::commandToString(CommandType cmd, void * calldata, XFE_CommandInfo* info)
  1044. {
  1045. #define IS_CMD(command) cmd == (command)  
  1046.  
  1047.     if ( (IS_CMD(xfeCmdDeleteMessage) || IS_CMD(xfeCmdCancelMessages))
  1048.          && isDisplayingNews() )
  1049.         return XP_GetString(MK_MSG_CANCEL_MESSAGE);
  1050.     else if ( calldata &&
  1051.               (IS_CMD(xfeCmdReplyToSender) || IS_CMD(xfeCmdReplyToAll)
  1052.                || IS_CMD(xfeCmdReplyToNewsgroup)
  1053.                || IS_CMD(xfeCmdReplyToSenderAndNewsgroup) ) )
  1054.         {
  1055.             // if calldata is non-null for a Reply button,
  1056.             // that's a signal not to change the
  1057.             // widget name in the resources file:
  1058.             return 0;
  1059.         }
  1060.     
  1061.     MSG_CommandType msg_cmd = commandToMsgCmd(cmd);
  1062.     MSG_MotionType msg_nav = commandToMsgNav(cmd);
  1063.     const char *display_string = NULL;
  1064.     
  1065.     if (msg_cmd != (MSG_CommandType)~0)
  1066.         {
  1067.             if (IS_CMD(xfeCmdGetNewMessages)) {
  1068.                 int num_inboxes = MSG_GetFoldersWithFlag(XFE_MNView::getMaster(),
  1069.                                                          MSG_FOLDER_FLAG_INBOX,
  1070.                                                          NULL, 0);
  1071.                 if ((num_inboxes >0) ||
  1072.                     (MSG_CommandStatus(m_pane, msg_cmd, 
  1073.                                        NULL, 0, NULL, NULL, 
  1074.                                        (const char **)&display_string, NULL) < 0)) {
  1075.                     return NULL;
  1076.                 }/* if */
  1077.                 return (char*)display_string;
  1078.             }/* if */
  1079.             else if (MSG_CommandStatus(m_pane, msg_cmd, 
  1080.                                        NULL, 0, NULL, NULL, 
  1081.                                        (const char **)&display_string, NULL) < 0)
  1082.                 return NULL;
  1083.             else
  1084.                         {
  1085.                           if ( (cmd == xfeCmdComposeMessageHTML ) ||
  1086.                              ( cmd == xfeCmdComposeMessagePlain) ||
  1087.                              ( cmd == xfeCmdComposeArticleHTML) ||
  1088.                              ( cmd == xfeCmdComposeArticlePlain) )
  1089.                           {
  1090.                               return NULL;
  1091.                           }
  1092.                           else
  1093.                              return (char*)display_string;
  1094.                         }
  1095.  
  1096.         }
  1097.     else if (msg_cmd != (MSG_MotionType)~0)
  1098.         {
  1099.             MessageKey key;
  1100.             MSG_ViewIndex index;
  1101.             MSG_FolderInfo *info;
  1102.             
  1103.             MSG_GetCurMessage(m_pane, &info, &key, &index);
  1104.             
  1105.             if (index == MSG_VIEWINDEXNONE
  1106.                 || MSG_NavigateStatus(m_pane, msg_nav, 
  1107.                                       (MSG_ViewIndex)index,
  1108.                                       NULL, (const char **)&display_string) < 0)
  1109.                 return NULL;
  1110.             else
  1111.                         {
  1112.                           if ( (cmd == xfeCmdComposeMessageHTML ) ||
  1113.                              ( cmd == xfeCmdComposeMessagePlain) ||
  1114.                              ( cmd == xfeCmdComposeArticleHTML) ||
  1115.                              ( cmd == xfeCmdComposeArticlePlain) )
  1116.                           {
  1117.                               return NULL;
  1118.                           }
  1119.                           else
  1120.                              return (char*)display_string;
  1121.                         }
  1122.         }
  1123.     else if (IS_CMD(xfeCmdFindInObject))
  1124.         {
  1125.             /* short circuit HTMLView's "Find in Page/Frame" and keep
  1126.                our "Find in Message." */
  1127.             return NULL;
  1128.         }
  1129.     else 
  1130.         return XFE_MNView::commandToString(cmd, calldata, info);
  1131. #undef IS_CMD
  1132. }
  1133.  
  1134. XFE_CALLBACK_DEFN(XFE_MsgView, showPopup)(XFE_NotificationCenter *,
  1135.                                           void *, void *calldata)
  1136. {
  1137.     D(printf("XFE_MsgView::showPopup()\n");)
  1138.     XEvent *event = (XEvent *)calldata;
  1139.  
  1140.     URL_Struct *url_under_mouse = m_htmlView->URLUnderMouse();
  1141.     URL_Struct *image_under_mouse = m_htmlView->ImageUnderMouse();
  1142.     URL_Struct *background_under_mouse = m_htmlView->BackgroundUnderMouse();
  1143.  
  1144.     XP_Bool isBrowserLink = FALSE;
  1145.     XP_Bool isAttachmentLink = FALSE;
  1146.     XP_Bool isLink = url_under_mouse != NULL;
  1147.     XP_Bool isImage = image_under_mouse != NULL;
  1148.     XP_Bool needSeparator = FALSE;
  1149.     XP_Bool haveAddedItems = FALSE;
  1150.  
  1151.     if (m_popup)
  1152.         delete m_popup;
  1153.  
  1154.     m_popup = new XFE_PopupMenu("popup",(XFE_Frame*)m_toplevel,
  1155.                     XfeAncestorFindApplicationShell(m_widget));
  1156.  
  1157.     if (url_under_mouse != NULL
  1158.         && XP_STRNCMP ("mailto:", url_under_mouse->address, 7) != 0
  1159.         && XP_STRNCMP ("telnet:", url_under_mouse->address, 7) != 0
  1160.         && XP_STRNCMP ("tn3270:", url_under_mouse->address, 7) != 0
  1161.         && XP_STRNCMP ("rlogin:", url_under_mouse->address, 7) != 0
  1162.         && XP_STRNCMP ("addbook:", url_under_mouse->address, 8) != 0)
  1163.         {
  1164.             isBrowserLink = TRUE;
  1165.         }
  1166.  
  1167.     if (url_under_mouse != NULL
  1168.         && XP_STRNCASECMP ("mailbox:", url_under_mouse->address, 8) != 0
  1169.         && XP_STRSTR(url_under_mouse->address, "part?"))
  1170.         {
  1171.             isAttachmentLink = TRUE;
  1172.         }
  1173.  
  1174.     /* turn off everything if the user right clicks on the paperclip icon */
  1175.     if (url_under_mouse != NULL
  1176.         && XP_STRCMP ("mailbox:displayattachments", url_under_mouse->address) == 0)
  1177.       {
  1178.         isBrowserLink = FALSE;
  1179.         isAttachmentLink = FALSE;
  1180.         isImage = FALSE;
  1181.       }
  1182.  
  1183. #define ADD_MENU_SEPARATOR {                     \
  1184.     if (haveAddedItems) {                        \
  1185.       needSeparator = TRUE;                      \
  1186.       haveAddedItems = FALSE;                    \
  1187.     }                                            \
  1188. }
  1189.  
  1190. #define ADD_SPEC(theSpec) {                      \
  1191.   if (needSeparator)                             \
  1192.     m_popup->addMenuSpec(separator_spec);        \
  1193.   m_popup->addMenuSpec(theSpec);                 \
  1194.   needSeparator = FALSE;                         \
  1195.   haveAddedItems = TRUE;                         \
  1196. }
  1197.  
  1198.     if (isBrowserLink)                       ADD_SPEC ( openLinkNew_spec );
  1199.     if (isBrowserLink)                         ADD_SPEC ( openLinkEdit_spec );
  1200.     ADD_MENU_SEPARATOR;
  1201.     
  1202.     ADD_SPEC ( repl_spec );
  1203.  
  1204.     ADD_MENU_SEPARATOR;
  1205.  
  1206.     ADD_SPEC ( addr_spec );
  1207.     
  1208.     ADD_MENU_SEPARATOR;
  1209.  
  1210.     ADD_SPEC ( filemsg_spec );
  1211.  
  1212.     if (isCommandEnabled(xfeCmdStopLoading)) ADD_SPEC ( stopLoading_spec );
  1213.     ADD_MENU_SEPARATOR;
  1214.     if (isImage)                             ADD_SPEC ( openImage_spec );
  1215.     ADD_MENU_SEPARATOR;
  1216.     if (isBrowserLink)                       ADD_SPEC ( saveLink_spec );
  1217.     if (isImage)                             ADD_SPEC ( saveImage_spec );
  1218.     ADD_MENU_SEPARATOR;
  1219.     if (isCommandEnabled(xfeCmdCopy))        ADD_SPEC ( copy_spec );
  1220.     if (isLink)                              ADD_SPEC ( copyLink_spec );
  1221.     if (isImage)                             ADD_SPEC ( copyImage_spec );
  1222.  
  1223.     // Finish up the popup
  1224.     m_popup->position (event);
  1225.     m_popup->show();
  1226. }
  1227.  
  1228. XFE_CALLBACK_DEFN(XFE_MsgView, spaceAtMsgEnd)(XFE_NotificationCenter *,
  1229.                                               void *, void */*callData*/)
  1230. {
  1231.   notifyInterested(spacebarAtMsgBottom);
  1232. }
  1233.  
  1234. XFE_CALLBACK_DEFN(XFE_MsgView, refresh)(XFE_NotificationCenter *,
  1235.                                         void *, void */*callData*/)
  1236. {
  1237.     m_htmlView->doCommand(xfeCmdRefresh);
  1238. }
  1239.