home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 2 BBS / 02-BBS.zip / fsrc1241.zip / threadlist.c < prev    next >
C/C++ Source or Header  |  1999-02-14  |  99KB  |  2,412 lines

  1. /*---------------------------------------------------------------------------+
  2.  | Titel: THREADLIST.C                                                       |
  3.  +-----------------------------------------+---------------------------------+
  4.  | Erstellt von: Michael Hohner            | Am: 28.01.94                    |
  5.  +-----------------------------------------+---------------------------------+
  6.  | System: OS/2 2.x PM                                                       |
  7.  +---------------------------------------------------------------------------+
  8.  | Beschreibung:                                                             |
  9.  |                                                                           |
  10.  |   Threadliste von FleetStreet                                             |
  11.  |                                                                           |
  12.  |                                                                           |
  13.  +---------------------------------------------------------------------------+
  14.  | Bemerkungen:                                                              |
  15.  +---------------------------------------------------------------------------*/
  16.  
  17. /*----------------------------- Header-Dateien ------------------------------*/
  18. #pragma strings(readonly)
  19.  
  20. #define INCL_GPILOGCOLORTABLE
  21. #define INCL_BASE
  22. #define INCL_WIN
  23. #include <os2.h>
  24. #include <string.h>
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27.  
  28. #include "main.h"
  29. #include "resids.h"
  30. #include "messages.h"
  31. #include "structs.h"
  32. #include "msgheader.h"
  33. #include "dialogids.h"
  34. #include "areaman\areaman.h"
  35. #include "handlemsg\handlemsg.h"
  36. #include "threadlist.h"
  37. #include "msglist.h"
  38. #include "utility.h"
  39. #include "threadlistsettings.h"
  40. #include "areadlg.h"
  41. #include "dialogs.h"
  42. #include "util\fltutil.h"
  43. #include "dump\expt.h"
  44.  
  45. /*--------------------------------- Defines ---------------------------------*/
  46.  
  47. #ifndef CRA_SOURCE
  48. #define CRA_SOURCE  0x00004000L    /* 2.1-spezifisch !!! */
  49. #endif
  50.  
  51. /* eigene Notification-Codes */
  52. #define CN_CHORD 200
  53. #define CN_COLORCHANGED 201
  54.  
  55. #define NO_INSERT     0
  56. #define INSERT_PARENT 1
  57. #define INSERT_CHILD  2
  58. #define INSERT_THREAD 3
  59.  
  60. #define ISREAD(x) ((x).Header.ulAttrib & ATTRIB_READ)
  61.  
  62. /*---------------------------------- Typen ----------------------------------*/
  63.  
  64. typedef struct tracelist
  65. {
  66.    int msgid;
  67.    struct tracelist *next;
  68. } TRACELIST;
  69.  
  70. typedef struct
  71. {
  72.    PWORKDATA pWorkData;
  73.    ULONG ulStartID;
  74. } THREADWORK, *PTHREADWORK;
  75.  
  76. typedef struct
  77. {
  78.    ULONG ulFlags;              /* INSERT_* */
  79.    ULONG ulNextParent;
  80.    MSGHEADER Header;
  81. } THREADHEADERS, *PTHREADHEADERS;
  82.  
  83. typedef struct _THREADRECORD
  84. {
  85.    RECORDCORE RecordCore;
  86.    char       pchText[10+LEN_USERNAME+LEN_SUBJECT]; /* Platz f. pszTree */
  87.    ULONG      ulMsgID;                              /* Message-ID */
  88.    ULONG      ulFlags;                              /* Flags, s.u. */
  89.    char       pchName[LEN_USERNAME+1];
  90.    char       pchSubj[LEN_SUBJECT+1];
  91. } THREADRECORD, *PTHREADRECORD;
  92.  
  93. #define THREADFLAG_NOREPLY     0x0001UL
  94. #define THREADFLAG_READ        0x0002UL
  95. #define THREADFLAG_PERSONAL    0x0004UL
  96. #define THREADFLAG_THREADSTART 0x8000UL
  97.  
  98. typedef struct _THREADLISTDATA
  99. {
  100.    MSGLISTPAR MsgListPar;
  101.    HPOINTER   icon;
  102.    TRACELIST  *Trace;
  103.    TRACELIST  *Trace2;
  104.    ULONG      numbers[2];
  105.    POINTL     pointl;
  106.    HSWITCH    hSwitch;
  107.    LONG       lDisableCount;
  108.    HWND       hwndListPopup;
  109.    HWND       hwndThreadPopup;
  110.    BOOL       bKeyboard;
  111.    PTHREADRECORD pPopupRecord;
  112.    ULONG      dspmode;
  113.    BOOL       bForeground;
  114.    BOOL       bSenderName;
  115.    char       pchEmptySubj[LEN_SUBJECT+1];
  116.    BOOL       bNotify;
  117. } THREADLISTDATA, *PTHREADLISTDATA;
  118.  
  119.  
  120. /*---------------------------- Globale Variablen ----------------------------*/
  121.  
  122. extern HAB anchor;
  123. extern HMODULE hmodLang;
  124.  
  125. extern int tidThreadList;
  126. extern HWND hwndThreadList;
  127. extern volatile BOOL DoingInsert;
  128. extern volatile BOOL StopInsert;
  129.  
  130. extern AREALIST arealiste;
  131. extern char CurrentArea[LEN_AREATAG+1];
  132.  
  133. static HPOINTER hptrPlus;
  134. static HPOINTER hptrMinus;
  135.  
  136. static char pchWorking[50];
  137. static char pchUnread[50];
  138. static char pchAllThreads[50];
  139. static char pchUnreadOnly[50];
  140.  
  141. static THREADWORK ThreadWork;
  142.  
  143. extern THREADLISTOPTIONS threadlistoptions;
  144. extern DIRTYFLAGS dirtyflags;
  145.  
  146. extern int  tidWorker;
  147. extern BOOL bDoingWork;
  148.  
  149. /*--------------------------- Funktionsprototypen ---------------------------*/
  150.  
  151. static PFNWP OldThContainerProc;
  152.  
  153. /*----------------------- interne Funktionsprototypen -----------------------*/
  154.  
  155. static void UpdateDspMenu(PTHREADLISTDATA pThreadListData);
  156. static void CleanupThreadList(HWND hwndContainer);
  157. static void _Optlink InsertUnreadThreads(void *p);
  158. static THREADRECORD *ExpandThreads(HWND hwndContainer, PTHREADRECORD pParent, PTHREADLISTDATA pThreadListData);
  159. static MRESULT EXPENTRY NewThContainerProc(HWND parent, ULONG message, MPARAM mp1, MPARAM mp2);
  160. static void ExpandBranch(HWND hwndContainer, PTHREADRECORD pParent);
  161. static BOOL DrawTree(POWNERITEM Item);
  162. static SHORT _System SortThreads(PRECORDCORE p1, PRECORDCORE p2, PVOID pStorage);
  163. static PTHREADRECORD GetNextRecord(PTHREADRECORD pRecord, HWND hwndContainer);
  164. static void MsgIDsToIndex(PTHREADHEADERS Headers, ULONG maxmsgs);
  165. static void TraceAllThreads(PTHREADHEADERS Headers, ULONG maxmsgs);
  166. static void TraceUnreadThreads(PTHREADHEADERS Headers, ULONG maxmsgs, PULONG pCurrentNum);
  167. static void TraceUnreadMsgs(PTHREADHEADERS Headers, ULONG maxmsgs, PULONG pCurrentNum);
  168. static void TraceSubjects(PTHREADHEADERS Headers, ULONG maxmsgs);
  169. static void ThreadsReady(HWND parent, PTHREADLISTDATA pThreadListData, PTHREADHEADERS Headers, ULONG maxmsgs);
  170. static void DeleteMessage(HWND hwndCnr, PMESSAGEID pMessageID);
  171. static void ThreadAddMessage(HWND hwndCnr, PMESSAGEID pMsgID, MSGHEADER *pHeader, PTHREADLISTDATA pThreadListData);
  172. static void ThreadChangeMessage(HWND hwndCnr, PMESSAGEID pMsgID, MSGHEADER *pHeader, PTHREADLISTDATA pThreadListData);
  173. static void ThreadContextMenu(HWND hwndDlg, PTHREADLISTDATA pThreadListData, PTHREADRECORD pRecord);
  174. static void SwitchThreadlistView(HWND hwndDlg, PTHREADLISTDATA pThreadListData, SHORT sCmdID);
  175. static void SwitchSenderName(HWND hwndDlg, PTHREADLISTDATA pThreadListData, BOOL bNoUpdate);
  176. static BOOL IsPersonalMessage(char *pchToName);
  177. static void HeaderToRecord(PTHREADLISTDATA pThreadListData, PMSGHEADER pHeader, PTHREADRECORD pRecord, BOOL bStripRe);
  178. static void HeaderToThread(PTHREADLISTDATA pThreadListData, PMSGHEADER pHeader, PTHREADRECORD pRecord, BOOL bStripRe);
  179. static void InsertRecords(HWND hwndDlg, PTHREADRECORD pParent, PTHREADRECORD pInsert, ULONG ulNumInsert, BOOL bInvalidate);
  180.  
  181. static void WorkOnThread(HWND hwndDlg, PTHREADLISTDATA pThreadListData, USHORT usCmdID);
  182. static void _Optlink CollectThread(void *pvWorkData);
  183. static void RecurseThread(ULONG ulMsgID, PWORKDATA pWorkData);
  184. static char *BuildThreadTitle(char *pchName, char *pchSubj, BOOL bUseSender, char *pchBuffer);
  185.  
  186. /*------------------------------ NewContainerProc ---------------------------*/
  187. /* Neue Window-Prozedur f. Container, um OS/2-Bug zu umschiffen              */
  188. /*---------------------------------------------------------------------------*/
  189.  
  190. static MRESULT EXPENTRY NewThContainerProc(HWND parent, ULONG message, MPARAM mp1, MPARAM mp2)
  191. {
  192.    POINTL pointl;
  193.    PRECORDCORE pRecord=NULL;
  194.    QUERYRECFROMRECT qRecord;
  195.  
  196.    switch(message)
  197.    {
  198.       case DM_DRAGOVER:
  199.          return (MRESULT) DOR_NEVERDROP;
  200.  
  201.       case WM_CHORD:
  202.          /* Cursorposition ermitteln */
  203.          WinQueryPointerPos(HWND_DESKTOP, &pointl);
  204.          WinMapWindowPoints(HWND_DESKTOP, parent, &pointl, 1);
  205.  
  206.          /* Record an der Stelle ermitteln */
  207.          qRecord.cb=sizeof(QUERYRECFROMRECT);
  208.          qRecord.rect.xLeft=pointl.x;
  209.          qRecord.rect.xRight=pointl.x+1;
  210.          qRecord.rect.yBottom=pointl.y;
  211.          qRecord.rect.yTop=pointl.y+1;
  212.          qRecord.fsSearch=CMA_PARTIAL | CMA_ITEMORDER;
  213.  
  214.          pRecord=(PRECORDCORE)SendMsg(parent, CM_QUERYRECORDFROMRECT,
  215.                                          MPFROMP(CMA_FIRST), &qRecord);
  216.  
  217.          /* Notify owner */
  218.          SendMsg(WinQueryWindow(parent, QW_OWNER), WM_CONTROL,
  219.                     MPFROM2SHORT(WinQueryWindowUShort(parent, QWS_ID),
  220.                                  CN_CHORD),
  221.                     pRecord);
  222.          break;
  223.  
  224.       case WM_PRESPARAMCHANGED:
  225.          if ((ULONG) mp1 == PP_FOREGROUNDCOLOR ||
  226.              (ULONG) mp1 == PP_BACKGROUNDCOLOR)
  227.          {
  228.             /* Notification an Owner */
  229.             SendMsg(WinQueryWindow(parent, QW_OWNER),
  230.                        WM_CONTROL,
  231.                        MPFROM2SHORT(WinQueryWindowUShort(parent, QWS_ID),
  232.                                     CN_COLORCHANGED),
  233.                        NULL);
  234.          }
  235.          break;
  236.  
  237.       default:
  238.          break;
  239.    }
  240.    return OldThContainerProc(parent, message, mp1, mp2);
  241. }
  242.  
  243. /*------------------------------ ThreadListProc -----------------------------*/
  244. /* Fensterprozedur der Messageliste in Thread-Darstellung.                   */
  245. /* In der RECORDCORE-Struktur werden folgende Felder als Flags verwendet:    */
  246. /*  pszIcon: NULL    Die Message hat Replies, die noch nicht eingefuegt      */
  247. /*                   worden sind,                                            */
  248. /*           != NULL Die Message hat keine Replies oder sie wurden schon     */
  249. /*                   eingefuegt,                                             */
  250. /*  pszName: NULL    Message wurde noch nicht gelesene (f. Ownerdraw)        */
  251. /*           != NULL Message wurde schon gelesen                             */
  252. /*  pszText: MSGNUM  Message-ID                                              */
  253. /*---------------------------------------------------------------------------*/
  254.  
  255. MRESULT EXPENTRY ThreadListProc(HWND parent, ULONG message, MPARAM mp1, MPARAM mp2)
  256. {
  257.    extern WINDOWFONTS windowfonts;
  258.    extern char CurrentArea[LEN_AREATAG+1];
  259.    extern MSGHEADER CurrentHeader;
  260.    extern HWND hwndhelp, client;
  261.    extern GENERALOPT generaloptions;
  262.    PTHREADLISTDATA pThreadListData;
  263.    CNRINFO cnrinfo;
  264.    MSGHEADER Header;
  265.    PTHREADRECORD pRecord;
  266.    static ULONG MinorVersion=0;
  267.  
  268.    pThreadListData=(PTHREADLISTDATA)WinQueryWindowULong(parent, QWL_USER);
  269.  
  270.    switch(message)
  271.    {
  272.       case WM_INITDLG:
  273.          pThreadListData=malloc(sizeof(THREADLISTDATA));
  274.          memset(pThreadListData, 0, sizeof(THREADLISTDATA));
  275.          WinSetWindowULong(parent, QWL_USER, (ULONG) pThreadListData);
  276.  
  277.          memcpy(&pThreadListData->MsgListPar, (MSGLISTPAR *)mp2, sizeof(MSGLISTPAR));
  278.          pThreadListData->dspmode = threadlistoptions.dspmode;
  279.          pThreadListData->bSenderName = threadlistoptions.shownames;
  280.          LoadString(IDST_ML_NOSUBJ, LEN_SUBJECT+1, pThreadListData->pchEmptySubj);
  281.  
  282.          pThreadListData->hSwitch=AddToWindowList(parent);
  283.          OldThContainerProc=WinSubclassWindow(WinWindowFromID(parent, IDD_THREADLIST+1),
  284.                                             NewThContainerProc);
  285.          LoadString(IDST_ML_WORKING, 50, pchWorking);
  286.          LoadString(IDST_ML_UNREAD, 50, pchUnread);
  287.          LoadString(IDST_ML_ALLTHREADS, 50, pchAllThreads);
  288.          LoadString(IDST_ML_UNREADONLY, 50, pchUnreadOnly);
  289.          pThreadListData->icon=LoadIcon(IDB_MSGTREE);
  290.          SendMsg(parent, WM_SETICON, (MPARAM) pThreadListData->icon, (MPARAM) 0);
  291.  
  292.          RestoreWinPos(parent, &threadlistoptions.ListPos, TRUE, TRUE);
  293.          SetForeground(WinWindowFromID(parent,IDD_THREADLIST+1),
  294.                        &threadlistoptions.lReadClr);
  295.          SetBackground(WinWindowFromID(parent,IDD_THREADLIST+1),
  296.                        &threadlistoptions.lBackClr);
  297.          SetFont(WinWindowFromID(parent,IDD_THREADLIST+1),
  298.                                  windowfonts.threadlistfont);
  299.  
  300.  
  301.          pThreadListData->hwndListPopup = WinLoadMenu(HWND_OBJECT, hmodLang, IDM_TL_POPUP);
  302.          pThreadListData->hwndThreadPopup = WinLoadMenu(HWND_OBJECT, hmodLang, IDM_TH_POPUP);
  303.          UpdateDspMenu(pThreadListData);
  304.  
  305.          if (pThreadListData->hwndListPopup)
  306.             ReplaceSysMenu(parent, pThreadListData->hwndListPopup, 1);
  307.  
  308.          if (threadlistoptions.keepinfront)
  309.          {
  310.             WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_FGROUND, TRUE);
  311.             pThreadListData->bForeground = TRUE;
  312.             WinSetOwner(parent, client);
  313.          }
  314.          else
  315.          {
  316.             WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_FGROUND, FALSE);
  317.             pThreadListData->bForeground = FALSE;
  318.             WinSetOwner(parent, HWND_DESKTOP);
  319.          }
  320.  
  321.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_SENDER, pThreadListData->bSenderName);
  322.  
  323.          /* OS/2 3.0 and below: replace tree icons */
  324.          DosQuerySysInfo(QSV_VERSION_MINOR, QSV_VERSION_MINOR, &MinorVersion,
  325.                          sizeof(MinorVersion));
  326.  
  327.          if (MinorVersion < 40)
  328.          {
  329.             hptrPlus=LoadIcon(IDIC_PLUS);
  330.             hptrMinus=LoadIcon(IDIC_MINUS);
  331.          }
  332.  
  333.          cnrinfo.cb=sizeof(CNRINFO);
  334.          cnrinfo.flWindowAttr=CV_TREE | CV_TEXT | CA_TREELINE | CA_OWNERDRAW |
  335.                               CA_CONTAINERTITLE | CA_TITLESEPARATOR;
  336.          cnrinfo.slTreeBitmapOrIcon.cx=16;
  337.          cnrinfo.slTreeBitmapOrIcon.cy=16;
  338.          if (MinorVersion < 40)
  339.          {
  340.             cnrinfo.hptrExpanded=hptrMinus;
  341.             cnrinfo.hptrCollapsed=hptrPlus;
  342.          }
  343.          cnrinfo.cxTreeLine=1;
  344.          cnrinfo.cxTreeIndent=14;
  345.          cnrinfo.pszCnrTitle=pchWorking;
  346.          cnrinfo.pSortRecord=(PVOID)SortThreads;
  347.          WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_SETCNRINFO, &cnrinfo,
  348.                            MPFROMLONG(CMA_FLWINDOWATTR |
  349.                                       ((MinorVersion<40)?CMA_TREEICON:0) |
  350.                                       CMA_CXTREEINDENT |
  351.                                       CMA_PSORTRECORD |
  352.                                       CMA_CXTREELINE |
  353.                                       CMA_CNRTITLE));
  354.          SendMsg(parent, TM_REREADTHREADS, mp2, NULL);
  355.          pThreadListData->bNotify = TRUE;
  356.          SetInitialAccel(parent);
  357.          break;
  358.  
  359.       case WM_ADJUSTWINDOWPOS:
  360.          if (((PSWP)mp1)->fl & SWP_MINIMIZE)
  361.             WinShowWindow(WinWindowFromID(parent, IDD_THREADLIST+1), FALSE);
  362.  
  363.          if (((PSWP)mp1)->fl & (SWP_RESTORE|SWP_MAXIMIZE))
  364.             WinShowWindow(WinWindowFromID(parent, IDD_THREADLIST+1), TRUE);
  365.          break;
  366.  
  367.       case WM_ADJUSTFRAMEPOS:
  368.          SizeToClient(anchor, (PSWP) mp1, parent, IDD_THREADLIST+1);
  369.          break;
  370.  
  371.       case WM_WINDOWPOSCHANGED:
  372.          if (pThreadListData && pThreadListData->bNotify)
  373.             SaveWinPos(parent, (PSWP) mp1, &threadlistoptions.ListPos, &dirtyflags.threadsdirty);
  374.          break;
  375.  
  376.       case WM_CONTROL:
  377.          if (SHORT1FROMMP(mp1)==IDD_THREADLIST+1)
  378.          {
  379.             switch(SHORT2FROMMP(mp1))
  380.             {
  381.                case CN_EXPANDTREE:
  382.                   if (pThreadListData->lDisableCount)
  383.                   {
  384.                      WinAlarm(HWND_DESKTOP, WA_NOTE);
  385.                      break;
  386.                   }
  387.                   WinEnableWindowUpdate(WinWindowFromID(parent, IDD_THREADLIST+1), FALSE);
  388.                   ExpandThreads(WinWindowFromID(parent, IDD_THREADLIST+1), mp2, pThreadListData);
  389.                   WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_INVALIDATERECORD, NULL, NULL);
  390.                   WinEnableWindowUpdate(WinWindowFromID(parent, IDD_THREADLIST+1), TRUE);
  391.                   break;
  392.  
  393.                case CN_ENTER:
  394.                   if (((PNOTIFYRECORDENTER)mp2)->pRecord)
  395.                   {
  396.                      if (pThreadListData->lDisableCount)
  397.                      {
  398.                         WinAlarm(HWND_DESKTOP, WA_ERROR);
  399.                         return (MRESULT) FALSE;
  400.                      }
  401.                      pRecord=(PTHREADRECORD)WinSendDlgItemMsg(parent, IDD_THREADLIST+1,
  402.                                                CM_QUERYRECORDEMPHASIS, (MPARAM)CMA_FIRST,
  403.                                                MPFROMSHORT(CRA_SELECTED));
  404.                      if (pRecord > (PTHREADRECORD)NULL)
  405.                      {
  406.                         if (pRecord->ulMsgID)
  407.                         {
  408.                            SendMsg(client, TM_JUMPTOMESSAGE,
  409.                                    MPFROMLONG(pRecord->ulMsgID), NULL);
  410.                            SetFocusControl(client, IDML_MAINEDIT);
  411.                         }
  412.                      }
  413.                   }
  414.                   break;
  415.  
  416.                case CN_HELP:
  417.                   SendMsg(parent, WM_HELP, MPFROMSHORT(IDD_THREADLIST+1), NULL);
  418.                   break;
  419.  
  420.                case CN_CHORD:
  421.                   if (pThreadListData->lDisableCount)
  422.                   {
  423.                      WinAlarm(HWND_DESKTOP, WA_NOTE);
  424.                      return (MRESULT) FALSE;
  425.                   }
  426.                   if (DoingInsert)
  427.                      WinAlarm(HWND_DESKTOP, WA_NOTE);
  428.                   else
  429.                   {
  430.                      WinSetPointer(HWND_DESKTOP, WinQuerySysPointer(HWND_DESKTOP,
  431.                                                                     SPTR_WAIT, FALSE));
  432.                      pRecord=(PTHREADRECORD) mp2;
  433.                      if (pRecord > (PTHREADRECORD)NULL)
  434.                      {
  435.                         WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_EXPANDTREE,
  436.                                           pRecord, NULL);
  437.                         ExpandBranch(WinWindowFromID(parent, IDD_THREADLIST+1), pRecord);
  438.                      }
  439.                      WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_INVALIDATERECORD,
  440.                                        NULL, NULL);
  441.                      WinSetPointer(HWND_DESKTOP, WinQuerySysPointer(HWND_DESKTOP,
  442.                                                                     SPTR_ARROW, FALSE));
  443.                   }
  444.                   break;
  445.  
  446.                case CN_CONTEXTMENU:
  447.                   ThreadContextMenu(parent, pThreadListData, (PTHREADRECORD) mp2);
  448.                   break;
  449.  
  450.                case CN_COLORCHANGED:
  451.                   if (pThreadListData && pThreadListData->bNotify)
  452.                   {
  453.                      QueryForeground(WinWindowFromID(parent, IDD_THREADLIST+1),
  454.                                      &threadlistoptions.lReadClr);
  455.                      QueryBackground(WinWindowFromID(parent, IDD_THREADLIST+1),
  456.                                      &threadlistoptions.lBackClr);
  457.                      dirtyflags.threadsdirty=TRUE;
  458.                   }
  459.                   break;
  460.  
  461.                default:
  462.                   break;
  463.             }
  464.          }
  465.          break;
  466.  
  467.       case WM_COMMAND:
  468.          switch (SHORT1FROMMP(mp1))
  469.          {
  470.             case IDM_TLP_SETTINGS:
  471.                WinDlgBox(HWND_DESKTOP, parent, ThreadListSettingsProc,
  472.                          hmodLang, IDD_THRLISTSETTINGS, NULL);
  473.                if (dirtyflags.threadsdirty)
  474.                {
  475.                   WinEnableWindowUpdate(WinWindowFromID(parent, IDD_THREADLIST+1), FALSE);
  476.                   pThreadListData->bNotify=FALSE;
  477.  
  478.                   SetForeground(WinWindowFromID(parent,IDD_THREADLIST+1),
  479.                                 &threadlistoptions.lReadClr);
  480.                   SetBackground(WinWindowFromID(parent,IDD_THREADLIST+1),
  481.                                 &threadlistoptions.lBackClr);
  482.  
  483.                   WinInvalidateRect(WinWindowFromID(parent, IDD_THREADLIST+1),
  484.                                     NULL, TRUE);
  485.                   pThreadListData->bNotify=TRUE;
  486.                   WinEnableWindowUpdate(WinWindowFromID(parent, IDD_THREADLIST+1), TRUE);
  487.  
  488.                   WinPostMsg(parent, WM_COMMAND, MPFROMSHORT(IDM_TLP_REFRESH), NULL);
  489.                }
  490.                return (MRESULT) FALSE;
  491.  
  492.             case IDM_TLP_VIEWALL:
  493.             case IDM_TLP_VIEWTHR:
  494.             case IDM_TLP_VIEWUNR:
  495.                SwitchThreadlistView(parent, pThreadListData, SHORT1FROMMP(mp1));
  496.                return (MRESULT) FALSE;
  497.  
  498.             case IDM_TLP_REFRESH:
  499.                if (DoingInsert)
  500.                {
  501.                   StopInsert=TRUE;
  502.                   DosWaitThread((PTID) &tidThreadList, DCWW_WAIT);
  503.                }
  504.                SendMsg(parent, TM_REREADTHREADS, &pThreadListData->MsgListPar, NULL);
  505.                return (MRESULT) FALSE;
  506.  
  507.             case IDM_TLP_FGROUND:
  508.                if (!pThreadListData->bForeground)
  509.                {
  510.                   WinSetOwner(parent, client);
  511.                   pThreadListData->bForeground = TRUE;
  512.                   WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_FGROUND, TRUE);
  513.                }
  514.                else
  515.                {
  516.                   WinSetOwner(parent, HWND_DESKTOP);
  517.                   pThreadListData->bForeground = FALSE;
  518.                   WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_FGROUND, FALSE);
  519.                }
  520.                return (MRESULT) FALSE;
  521.  
  522.             case IDM_TLP_SENDER:
  523.                SwitchSenderName(parent, pThreadListData, DoingInsert);
  524.                return (MRESULT) FALSE;
  525.  
  526.             case IDM_TLP_CATCHUP:
  527.                if (bDoingWork)
  528.                {
  529.                   /* Fehlermeldung */
  530.                   MessageBox(parent, IDST_MSG_DOINGWORK, 0, IDD_DOINGWORK,
  531.                              MB_OK);
  532.                   return (MRESULT) FALSE;
  533.                }
  534.                if (generaloptions.safety & SAFETY_CATCHUP)
  535.                   if (MessageBox(parent, IDST_MSG_CATCHUP, IDST_TITLE_CATCHUP,
  536.                                  IDD_CATCHUP, MB_YESNO) != MBID_YES)
  537.                      return (MRESULT) FALSE;
  538.                MarkAllMessages(CurrentArea);
  539.                return (MRESULT) FALSE;
  540.  
  541.             case IDM_THP_DELETE:
  542.             case IDM_THP_MOVE:
  543.             case IDM_THP_COPY:
  544.             case IDM_THP_EXPORT:
  545.             case IDM_THP_PRINT:
  546.             case IDM_THP_MARK:
  547.                WorkOnThread(parent, pThreadListData, SHORT1FROMMP(mp1));
  548.                return (MRESULT) FALSE;
  549.  
  550.             case IDM_THP_EXPAND:
  551.                WinSetPointer(HWND_DESKTOP, WinQuerySysPointer(HWND_DESKTOP,
  552.                                                               SPTR_WAIT, FALSE));
  553.                if (pThreadListData->pPopupRecord)
  554.                {
  555.                   WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_EXPANDTREE,
  556.                                     pThreadListData->pPopupRecord, NULL);
  557.                   ExpandBranch(WinWindowFromID(parent, IDD_THREADLIST+1),
  558.                                pThreadListData->pPopupRecord);
  559.                }
  560.                WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_INVALIDATERECORD,
  561.                                  NULL, NULL);
  562.                WinSetPointer(HWND_DESKTOP, WinQuerySysPointer(HWND_DESKTOP,
  563.                                                               SPTR_ARROW, FALSE));
  564.                return (MRESULT) FALSE;
  565.  
  566.             case DID_CANCEL:
  567.                if (DoingInsert)
  568.                {
  569.                   StopInsert=TRUE;
  570.                   DosWaitThread((PTID) &tidThreadList, DCWW_WAIT);
  571.                }
  572.                CleanupThreadList(WinWindowFromID(parent, IDD_THREADLIST+1));
  573.                WinPostMsg(client, TM_THREADLISTCLOSE, NULL, NULL);
  574.                break;
  575.  
  576.             default:
  577.                return RedirectCommand(mp1, mp2);
  578.          }
  579.          break;
  580.  
  581.       case WM_CLOSE:
  582.          if (DoingInsert)
  583.          {
  584.             StopInsert=TRUE;
  585.             DosWaitThread((PTID) &tidThreadList, DCWW_WAIT);
  586.          }
  587.          CleanupThreadList(WinWindowFromID(parent, IDD_THREADLIST+1));
  588.          WinPostMsg(client, TM_THREADLISTCLOSE, NULL, NULL);
  589.          break;
  590.  
  591.       case WM_DESTROY:
  592.          RemoveFromWindowList(pThreadListData->hSwitch);
  593.          QueryFont(WinWindowFromID(parent,IDD_THREADLIST+1),
  594.                    windowfonts.threadlistfont);
  595.          if (pThreadListData->bForeground != threadlistoptions.keepinfront)
  596.          {
  597.             threadlistoptions.keepinfront=pThreadListData->bForeground;
  598.             dirtyflags.threadsdirty=TRUE;
  599.          }
  600.          WinDestroyPointer(pThreadListData->icon);
  601.          if (MinorVersion < 40)
  602.          {
  603.             WinDestroyPointer(hptrPlus);
  604.             WinDestroyPointer(hptrMinus);
  605.          }
  606.          if (pThreadListData->hwndListPopup)
  607.             WinDestroyWindow(pThreadListData->hwndListPopup);
  608.          if (pThreadListData->hwndThreadPopup)
  609.             WinDestroyWindow(pThreadListData->hwndThreadPopup);
  610.          while(pThreadListData->Trace)
  611.          {
  612.             pThreadListData->Trace2=pThreadListData->Trace->next;
  613.             free(pThreadListData->Trace);
  614.             pThreadListData->Trace=pThreadListData->Trace2;
  615.          }
  616.          free(pThreadListData);
  617.          break;
  618.  
  619.       case WM_ACTIVATE:
  620.          if (mp1)
  621.             WinAssociateHelpInstance(hwndhelp, parent);
  622.          else
  623.             WinAssociateHelpInstance(hwndhelp, NULLHANDLE);
  624.          break;
  625.  
  626.       case WM_DRAWITEM:
  627.          if (SHORT1FROMMP(mp1)==IDD_THREADLIST+1)
  628.             return (MRESULT) DrawTree((POWNERITEM) mp2);
  629.          else
  630.             return FALSE;
  631.  
  632.       case WM_CHAR:
  633.         if ((SHORT1FROMMP(mp1) & KC_VIRTUALKEY) &&
  634.             !(SHORT1FROMMP(mp1) & KC_KEYUP))
  635.            if (SHORT2FROMMP(mp2) == VK_SPACE)
  636.            {
  637.               if (pThreadListData->lDisableCount)
  638.               {
  639.                  WinAlarm(HWND_DESKTOP, WA_NOTE);
  640.                  return (MRESULT) FALSE;
  641.               }
  642.               if (DoingInsert)
  643.                  WinAlarm(HWND_DESKTOP, WA_NOTE);
  644.               else
  645.               {
  646.                  WinSetPointer(HWND_DESKTOP, WinQuerySysPointer(HWND_DESKTOP,
  647.                                                                 SPTR_WAIT, FALSE));
  648.                  pRecord=(PTHREADRECORD)WinSendDlgItemMsg(parent, IDD_THREADLIST+1,
  649.                                            CM_QUERYRECORDEMPHASIS, (MPARAM)CMA_FIRST,
  650.                                            MPFROMSHORT(CRA_SELECTED));
  651.                  if (pRecord > (PTHREADRECORD)NULL)
  652.                  {
  653.                     WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_EXPANDTREE,
  654.                                       pRecord, NULL);
  655.                     ExpandBranch(WinWindowFromID(parent, IDD_THREADLIST+1), pRecord);
  656.                  }
  657.                  WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_INVALIDATERECORD,
  658.                                    NULL, NULL);
  659.                  WinSetPointer(HWND_DESKTOP, WinQuerySysPointer(HWND_DESKTOP,
  660.                                                                 SPTR_ARROW, FALSE));
  661.               }
  662.            }
  663.          break;
  664.  
  665.       case TM_THREADSREADY:
  666.          ThreadsReady(parent, pThreadListData, (PTHREADHEADERS)mp1, (ULONG)mp2);
  667.          break;
  668.  
  669.       case TM_REREADTHREADS:
  670.          if (DoingInsert)
  671.          {
  672.             StopInsert=TRUE;
  673.             DosWaitThread((PTID) &tidThreadList, DCWW_WAIT);
  674.          }
  675.          if (DoingInsert) /* immer noch */
  676.          {
  677.             QMSG qmsg;
  678.  
  679.             if (WinPeekMsg(anchor, &qmsg, parent, TM_THREADSREADY, TM_THREADSREADY,
  680.                            PM_REMOVE))
  681.             {
  682.                /* Message gefunden, Speicher freigeben */
  683.                if (qmsg.mp1)
  684.                   free(qmsg.mp1);
  685.             }
  686.          }
  687.  
  688.          memcpy(&pThreadListData->MsgListPar, (MSGLISTPAR *)mp1, sizeof(MSGLISTPAR));
  689.          CleanupThreadList(WinWindowFromID(parent, IDD_THREADLIST+1));
  690.          if (pThreadListData->MsgListPar.msgnum==0)
  691.             break;
  692.          DoingInsert=TRUE;
  693.  
  694.          /* Top-Level der aktuellen Message suchen */
  695.          memcpy(&Header, &CurrentHeader, sizeof(Header));
  696.          pThreadListData->Trace=malloc(sizeof(TRACELIST));
  697.          pThreadListData->Trace->msgid=pThreadListData->MsgListPar.msgnum;
  698.          pThreadListData->Trace->next=NULL;
  699.  
  700.          /* Anfang des Threads suchen */
  701.          while(Header.ulReplyTo &&
  702.                (!(Header.ulAttrib & ATTRIB_READ) || pThreadListData->dspmode!=DSPTHREADS_UNREADONLY))
  703.          {
  704.             int msgnum;
  705.             TRACELIST *pTemp = pThreadListData->Trace;
  706.  
  707.             /* Endlosschleife verhindern */
  708.             while (pTemp && pTemp->msgid != Header.ulReplyTo)
  709.                pTemp = pTemp->next;
  710.             if (pTemp) /* waren schonmal da */
  711.                break;
  712.  
  713.             /* @@ das müßte mal aufgeräumt werden, mieser Code! */
  714.             pThreadListData->Trace2=malloc(sizeof(TRACELIST));
  715.             pThreadListData->Trace2->msgid=Header.ulReplyTo;
  716.  
  717.             msgnum=MSG_UidToMsgn(&arealiste, CurrentArea, Header.ulReplyTo, TRUE);
  718.             if (msgnum)
  719.             {
  720.                if (MSG_ReadHeader(&Header, &arealiste, CurrentArea, msgnum))
  721.                {
  722.                   free(pThreadListData->Trace2);
  723.                   break;
  724.                }
  725.                else
  726.                {
  727.                   pThreadListData->Trace2->next=pThreadListData->Trace;
  728.                   pThreadListData->Trace=pThreadListData->Trace2;
  729.                }
  730.             }
  731.             else
  732.             {
  733.                free(pThreadListData->Trace2);
  734.                break;
  735.             }
  736.          }
  737.          /* Top-Level-Message gefunden, Trace enthält die Messagenummern bis zur */
  738.          /* aktuellen Message */
  739.          cnrinfo.pszCnrTitle=pchWorking;
  740.          WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_SETCNRINFO, &cnrinfo,
  741.                            MPFROMLONG(CMA_CNRTITLE));
  742.  
  743.          pThreadListData->numbers[0]=pThreadListData->MsgListPar.msgnum;
  744.          pThreadListData->numbers[1]=pThreadListData->Trace->msgid;
  745.  
  746.          /* Thread-Anfaenge suchen und einfuegen, zweite Stufe mitlesen */
  747.          tidThreadList=_beginthread(InsertUnreadThreads, NULL, 32768, pThreadListData);
  748.          break;
  749.  
  750.       case WORKM_READ:
  751.          /* Message im Container suchen */
  752.          pRecord=WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_QUERYRECORD, NULL,
  753.                             MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  754.          while (pRecord && pRecord->ulMsgID != ((PMESSAGEID) mp1)->ulMsgID)
  755.             pRecord=GetNextRecord(pRecord, WinWindowFromID(parent, IDD_THREADLIST+1));
  756.  
  757.          if (pRecord && !(pRecord->ulFlags & THREADFLAG_READ))
  758.          {
  759.             pRecord->ulFlags |= THREADFLAG_READ;
  760.             WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_INVALIDATERECORD, &pRecord,
  761.                               MPFROM2SHORT(1, CMA_TEXTCHANGED | CMA_NOREPOSITION));
  762.          }
  763.          break;
  764.  
  765.       case WORKM_DISABLEVIEWS:
  766.          pThreadListData->lDisableCount++;
  767.          break;
  768.  
  769.       case WORKM_ENABLEVIEWS:
  770.          pThreadListData->lDisableCount--;
  771.          if (pThreadListData->lDisableCount < 0)
  772.             pThreadListData->lDisableCount = 0;
  773.          break;
  774.  
  775.       case WORKM_MARKEND:
  776.          if (!stricmp(CurrentArea, ((PMESSAGEID) mp1)->pchAreaTag))
  777.          {
  778.             HWND hwndCnr = WinWindowFromID(parent, IDD_THREADLIST+1);
  779.  
  780.             /* Alle Records als gelesen markieren */
  781.             pRecord=SendMsg(hwndCnr, CM_QUERYRECORD, NULL,
  782.                                MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  783.             do
  784.             {
  785.                if (pRecord)
  786.                {
  787.                   if (pRecord->ulMsgID <= ((PMESSAGEID) mp1)->ulMsgID)
  788.                      pRecord->ulFlags|=THREADFLAG_READ;
  789.                   pRecord=GetNextRecord(pRecord, hwndCnr);
  790.                }
  791.             }  while (pRecord);
  792.             SendMsg(hwndCnr, CM_INVALIDATERECORD, NULL, NULL);
  793.          }
  794.          break;
  795.  
  796.       case WORKM_DELETED:
  797.          if (!stricmp(CurrentArea, ((PMESSAGEID) mp1)->pchAreaTag))
  798.             DeleteMessage(WinWindowFromID(parent, IDD_THREADLIST+1), (PMESSAGEID) mp1);
  799.          break;
  800.  
  801.       case WORKM_ADDED:
  802.          if (!stricmp(CurrentArea, ((PMESSAGEID) mp1)->pchAreaTag))
  803.             ThreadAddMessage(WinWindowFromID(parent, IDD_THREADLIST+1), (PMESSAGEID) mp1,
  804.                              (MSGHEADER*) mp2, pThreadListData);
  805.          break;
  806.  
  807.       case WORKM_CHANGED:
  808.          if (!stricmp(CurrentArea, ((PMESSAGEID) mp1)->pchAreaTag))
  809.             ThreadChangeMessage(WinWindowFromID(parent, IDD_THREADLIST+1), (PMESSAGEID) mp1,
  810.                                 (MSGHEADER*) mp2, pThreadListData);
  811.          break;
  812.  
  813.       case WM_MENUEND:
  814.          if ((HWND) mp2 == pThreadListData->hwndListPopup ||
  815.              (HWND) mp2 == pThreadListData->hwndThreadPopup)
  816.          {
  817.             pThreadListData->bKeyboard=FALSE;
  818.  
  819.             /* Source-Emphasis loeschen */
  820.             WinSendDlgItemMsg(parent, IDD_THREADLIST+1,
  821.                               CM_SETRECORDEMPHASIS, pThreadListData->pPopupRecord,
  822.                               MPFROM2SHORT(FALSE, CRA_SOURCE));
  823.             if ( (HWND) mp2 == pThreadListData->hwndListPopup)
  824.                ResetMenuStyle(pThreadListData->hwndListPopup, parent);
  825.          }
  826.          break;
  827.  
  828.       case WM_INITMENU:
  829.          if ((HWND) mp2 == pThreadListData->hwndListPopup)
  830.             pThreadListData->pPopupRecord=NULL;
  831.          if ((HWND) mp2 == pThreadListData->hwndListPopup ||
  832.              (HWND) mp2 == pThreadListData->hwndThreadPopup)
  833.          {
  834.             /* Emphasis setzen */
  835.             WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_SETRECORDEMPHASIS,
  836.                               pThreadListData->pPopupRecord,
  837.                               MPFROM2SHORT(TRUE, CRA_SOURCE));
  838.          }
  839.          break;
  840.  
  841.       case WM_CONTEXTMENU:
  842.          if (!SHORT1FROMMP(mp1) &&
  843.              WinQueryFocus(HWND_DESKTOP) == WinWindowFromID(parent, IDD_THREADLIST+1))
  844.          {
  845.             pThreadListData->bKeyboard = TRUE;
  846.             WinSendDlgItemMsg(parent, IDD_THREADLIST+1, WM_CONTEXTMENU, mp1, mp2);
  847.          }
  848.          break;
  849.  
  850.       case WORKM_SWITCHACCELS:
  851.          SwitchAccels(parent, (ULONG) mp1);
  852.          break;
  853.  
  854.       default:
  855.          break;
  856.    }
  857.    return WinDefDlgProc(parent, message, mp1, mp2);
  858. }
  859.  
  860. /*---------------------------------------------------------------------------*/
  861. /* Funktionsname: UpdateDspMenu                                              */
  862. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  863. /* Beschreibung: Setzt die Checks beim Popup-Menu je nach Display-Modus      */
  864. /*                                                                           */
  865. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  866. /* Parameter: pThreadListData: Instanzdaten                                  */
  867. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  868. /* Rückgabewerte: -                                                          */
  869. /*                                                                           */
  870. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  871. /* Sonstiges:                                                                */
  872. /*                                                                           */
  873. /*---------------------------------------------------------------------------*/
  874.  
  875. static void UpdateDspMenu(PTHREADLISTDATA pThreadListData)
  876. {
  877.    switch(pThreadListData->dspmode)
  878.    {
  879.       case DSPTHREADS_ALL:
  880.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWALL, TRUE);
  881.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWTHR, FALSE);
  882.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWUNR, FALSE);
  883.          break;
  884.  
  885.       case DSPTHREADS_WITHUNREAD:
  886.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWALL, FALSE);
  887.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWTHR, TRUE);
  888.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWUNR, FALSE);
  889.          break;
  890.  
  891.       case DSPTHREADS_UNREADONLY:
  892.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWALL, FALSE);
  893.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWTHR, FALSE);
  894.          WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_VIEWUNR, TRUE);
  895.          break;
  896.  
  897.       default:
  898.          break;
  899.    }
  900.    return;
  901. }
  902.  
  903. /*--------------------------- SortThreads          --------------------------*/
  904. /* Sortierfunktion fuer die Thread-Liste                                     */
  905. /*---------------------------------------------------------------------------*/
  906.  
  907. static SHORT _System SortThreads(PRECORDCORE p1, PRECORDCORE p2, PVOID pStorage)
  908. {
  909.    SHORT result;
  910.  
  911.    /* Compiler beruhigen */
  912.    pStorage=pStorage;
  913.  
  914.    result=stricmp(((PTHREADRECORD)p1)->pchSubj, ((PTHREADRECORD)p2)->pchSubj);
  915.  
  916.    if (!result)
  917.    {
  918.       /* Subjects sind gleich, jetzt nach Messagenummer gehen */
  919.       if (((PTHREADRECORD)p1)->ulMsgID < ((PTHREADRECORD)p1)->ulMsgID)
  920.          result=-1;
  921.       else
  922.          result=1;
  923.    }
  924.  
  925.    return result;
  926. }
  927.  
  928.  
  929. /*------------------------------ ExpandBranch   -----------------------------*/
  930. /* Erweitert einen gesamten Unterbaum                                        */
  931. /*---------------------------------------------------------------------------*/
  932.  
  933. static void ExpandBranch(HWND hwndContainer, PTHREADRECORD pParent)
  934. {
  935.    PTHREADRECORD pChild;
  936.  
  937.    pChild=(PTHREADRECORD)SendMsg(hwndContainer, CM_QUERYRECORD,
  938.                                     pParent, MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
  939.    while (pChild)
  940.    {
  941.       SendMsg(hwndContainer, CM_EXPANDTREE, pChild, NULL);
  942.       ExpandBranch(hwndContainer, pChild);
  943.       pChild=SendMsg(hwndContainer, CM_QUERYRECORD,
  944.                                pChild, MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
  945.    }
  946.    return;
  947. }
  948.  
  949. /*------------------------------ ExpandThreads  -----------------------------*/
  950. /* Laedt neue Header beim Aufklappen eines Zweiges                           */
  951. /*---------------------------------------------------------------------------*/
  952.  
  953. static THREADRECORD *ExpandThreads(HWND hwndContainer, PTHREADRECORD pParent, PTHREADLISTDATA pThreadListData)
  954. {
  955.    PTHREADRECORD pRecord, pRecord2;
  956.    ULONG uid, uid2;
  957.    int msgnum, msgnum2;
  958.    MSGHEADER Header, Header2;
  959.    extern char CurrentArea[LEN_AREATAG+1];
  960.    int i;
  961.  
  962.    /* ersten Child-Record holen */
  963.    pRecord=pParent;
  964.    SendMsg(hwndContainer, CM_INVALIDATERECORD, &pRecord,
  965.               MPFROM2SHORT(1, CMA_TEXTCHANGED /*| CMA_NOREPOSITION*/));
  966.    pRecord=SendMsg(hwndContainer, CM_QUERYRECORD, pRecord,
  967.                       MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
  968.    if (threadlistoptions.compact && pParent && (pParent->ulFlags & THREADFLAG_THREADSTART))
  969.       pRecord=SendMsg(hwndContainer, CM_QUERYRECORD, pRecord,
  970.                       MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER));
  971.    while(pRecord)
  972.    {
  973.       if (!(pRecord->ulFlags & THREADFLAG_NOREPLY))   /* Noch nicht bearbeitet */
  974.       {
  975.          pRecord->ulFlags |= THREADFLAG_NOREPLY;
  976.  
  977.          SendMsg(hwndContainer, CM_INVALIDATERECORD, &pRecord,
  978.                     MPFROM2SHORT(1, CMA_TEXTCHANGED /* | CMA_NOREPOSITION*/));
  979.  
  980.          /* Nummer der Message holen */
  981.          uid= pRecord->ulMsgID;
  982.          msgnum=MSG_UidToMsgn(&arealiste, CurrentArea, uid, TRUE);
  983.          if (uid && !MSG_ReadHeader(&Header, &arealiste, CurrentArea, msgnum))
  984.          {
  985.             /* Alle Replies bearbeiten */
  986.             i=0;
  987.             while (i<NUM_REPLIES && Header.ulReplies[i]!=0)
  988.             {
  989.                uid2=Header.ulReplies[i++];
  990.                if (uid2==0)
  991.                   continue;   /* Reply nicht mehr da */
  992.                msgnum2=MSG_UidToMsgn(&arealiste, CurrentArea, uid2, TRUE);
  993.                if (MSG_ReadHeader(&Header2, &arealiste, CurrentArea, msgnum2))
  994.                   continue;    /* Fehler beim Lesen */
  995.  
  996.                if (pThreadListData->dspmode==DSPTHREADS_UNREADONLY
  997.                    && (Header2.ulAttrib & ATTRIB_READ))
  998.                   continue;
  999.  
  1000.                pRecord2=SendMsg(hwndContainer, CM_ALLOCRECORD,
  1001.                                    MPFROMLONG(sizeof(THREADRECORD)-sizeof(RECORDCORE)),
  1002.                                    MPFROMLONG(1));
  1003.                Header2.ulMsgID = uid2;
  1004.                HeaderToRecord(pThreadListData, &Header2, pRecord2, TRUE);
  1005.  
  1006.                InsertRecords(WinQueryWindow(hwndContainer, QW_PARENT),
  1007.                              pRecord, pRecord2, 1, TRUE);
  1008.             }
  1009.          }
  1010.       }
  1011.       /* naechsten Record holen, bis Stufe zu Ende */
  1012.       pRecord=SendMsg(hwndContainer, CM_QUERYRECORD, pRecord,
  1013.                          MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
  1014.    }
  1015.    return NULL;
  1016. }
  1017.  
  1018. /*------------------------------ CleanupThreadList --------------------------*/
  1019. /* Gibt den Speicher fuer die Textfelder wieder frei, entfernt alle Records  */
  1020. /*---------------------------------------------------------------------------*/
  1021.  
  1022. static void CleanupThreadList(HWND hwndContainer)
  1023. {
  1024.    SendMsg(hwndContainer, CM_REMOVERECORD, NULL,
  1025.               MPFROM2SHORT(0, CMA_FREE | CMA_INVALIDATE));
  1026.    return;
  1027. }
  1028.  
  1029. /*------------------------------ GetNextRecord     --------------------------*/
  1030. /* Liefert Zeiger auf naechsten Record im Tree-View                          */
  1031. /*---------------------------------------------------------------------------*/
  1032.  
  1033. static PTHREADRECORD GetNextRecord(PTHREADRECORD pRecord, HWND hwndContainer)
  1034. {
  1035.    PRECORDCORE record =NULL;
  1036.    PRECORDCORE recordP=NULL;
  1037.    record=(PRECORDCORE) SendMsg(hwndContainer,
  1038.                                    CM_QUERYRECORD,
  1039.                                    MPFROMP(pRecord),
  1040.                                    MPFROM2SHORT( CMA_FIRSTCHILD, CMA_ITEMORDER));
  1041.    if (record==NULL)
  1042.    {
  1043.       /* no child ? - try the twin... */
  1044.       record=(PRECORDCORE) SendMsg(hwndContainer,
  1045.                                       CM_QUERYRECORD,
  1046.                                       MPFROMP(pRecord),
  1047.                                       MPFROM2SHORT( CMA_NEXT, CMA_ITEMORDER));
  1048.       if (record==NULL)
  1049.       {
  1050.          /* no twin ? - try to go back and then next ... */
  1051.          record=(PRECORDCORE)pRecord;
  1052.          while (record!=NULL)
  1053.          {
  1054.             recordP=(PRECORDCORE)SendMsg(hwndContainer,
  1055.                                             CM_QUERYRECORD,
  1056.                                             MPFROMP(record),
  1057.                                             MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER));
  1058.             if (recordP==NULL)   /* no parent - nothing left ? */
  1059.                return NULL;
  1060.             record=(PRECORDCORE) SendMsg(hwndContainer,
  1061.                                             CM_QUERYRECORD,
  1062.                                             MPFROMP(recordP),
  1063.                                             MPFROM2SHORT( CMA_NEXT, CMA_ITEMORDER));
  1064.             if (record==NULL)
  1065.                record=recordP;
  1066.             else
  1067.                return (PTHREADRECORD) record;  /* PARENT -> NEXT */
  1068.          }
  1069.          return NULL;         /* PARENT -> PARENT */
  1070.       }
  1071.       else
  1072.          return (PTHREADRECORD) record;  /* NEXT */
  1073.    }
  1074.    else
  1075.       return (PTHREADRECORD) record;    /* FIRST_CHILD */
  1076. }
  1077.  
  1078. /*------------------------------ DrawTree      ------------------------------*/
  1079. /* Owner-Draw-Funktion fuer den Thread-Tree                                  */
  1080. /*---------------------------------------------------------------------------*/
  1081.  
  1082. static BOOL DrawTree(POWNERITEM Item)
  1083. {
  1084.    if (Item->idItem != CMA_TEXT)
  1085.       return FALSE;              /* nur Text zeichnen */
  1086.  
  1087.    if (Item->fsAttribute & CRA_SELECTED)
  1088.       return FALSE;      /* nur normale Darstellung zeichnen */
  1089.  
  1090.    if (!((PCNRDRAWITEMINFO)Item->hItem)->pRecord)
  1091.       return FALSE;       /* nur Records zeichnen */
  1092.  
  1093.    GpiCreateLogColorTable(Item->hps, 0, LCOLF_RGB, 0, 0, 0);
  1094.  
  1095.    if ((((PTHREADRECORD)((PCNRDRAWITEMINFO)Item->hItem)->pRecord)->ulFlags & THREADFLAG_PERSONAL) &&
  1096.        !(((PTHREADRECORD)((PCNRDRAWITEMINFO)Item->hItem)->pRecord)->ulFlags & THREADFLAG_READ))
  1097.    {
  1098.       WinDrawText(Item->hps, -1, ((PCNRDRAWITEMINFO)Item->hItem)->pRecord->pszTree,
  1099.                   &Item->rclItem,
  1100.                   threadlistoptions.lPersonalClr,
  1101.                   threadlistoptions.lBackClr,
  1102.                   DT_LEFT | DT_VCENTER);
  1103.    }
  1104.    else
  1105.       if (((PTHREADRECORD)((PCNRDRAWITEMINFO)Item->hItem)->pRecord)->ulFlags & THREADFLAG_READ)   /* gelesen */
  1106.       {
  1107.          WinDrawText(Item->hps, -1, ((PCNRDRAWITEMINFO)Item->hItem)->pRecord->pszTree,
  1108.                      &Item->rclItem,
  1109.                      threadlistoptions.lReadClr,
  1110.                      threadlistoptions.lBackClr,
  1111.                      DT_LEFT | DT_VCENTER);
  1112.       }
  1113.       else          /* ungelesen */
  1114.       {
  1115.          WinDrawText(Item->hps, -1, ((PCNRDRAWITEMINFO)Item->hItem)->pRecord->pszTree,
  1116.                      &Item->rclItem,
  1117.                      threadlistoptions.lUnreadClr,
  1118.                      threadlistoptions.lBackClr,
  1119.                      DT_LEFT | DT_VCENTER);
  1120.       }
  1121.  
  1122.    return TRUE;
  1123. }
  1124.  
  1125. /*--------------------------- InsertUnreadThreads  --------------------------*/
  1126. /* Sucht die Message-Threads mit ungelesener Mail und fuegt sie in den       */
  1127. /* Container ein.                                                            */
  1128. /*---------------------------------------------------------------------------*/
  1129.  
  1130. static void _Optlink InsertUnreadThreads(void *p)
  1131. {
  1132.    int msgnum;
  1133.    ULONG maxmsgs;
  1134.    AREADEFLIST *zeiger;
  1135.    PTHREADHEADERS Headers=NULL;
  1136.    extern char CurrentArea[LEN_AREATAG+1];
  1137.    extern MISCOPTIONS miscoptions;
  1138.    extern DRIVEREMAP driveremap;
  1139.    char pchThisArea[LEN_AREATAG+1];
  1140.    PTHREADLISTDATA pThreadListData = p;
  1141.  
  1142.    INSTALLEXPT("InsertThreads");
  1143.  
  1144.    DoingInsert=TRUE;
  1145.    StopInsert=FALSE;
  1146.  
  1147.    strcpy(pchThisArea, CurrentArea);
  1148.  
  1149.    zeiger=AM_FindArea(&arealiste, pchThisArea);
  1150.  
  1151.    /* Leere oder falsche Area */
  1152.    if (!zeiger || zeiger->maxmessages==0)
  1153.    {
  1154.       DEINSTALLEXPT;
  1155.       return;
  1156.    }
  1157.  
  1158.    MSG_OpenArea(&arealiste, pchThisArea, miscoptions.lastreadoffset, &driveremap);
  1159.  
  1160.    /* Speicher fuer Header belegen */
  1161.    maxmsgs=zeiger->maxmessages;
  1162.    Headers=calloc(maxmsgs, sizeof(THREADHEADERS));
  1163.  
  1164.    /* alle Header lesen */
  1165.    for (msgnum=1; msgnum<=maxmsgs && !StopInsert; msgnum++)
  1166.    {
  1167.       MSG_ReadHeader(&Headers[msgnum-1].Header, &arealiste, pchThisArea, msgnum);
  1168.       StripRe(Headers[msgnum-1].Header.pchSubject);
  1169.    }
  1170.  
  1171.    MSG_CloseArea(&arealiste, pchThisArea, FALSE, miscoptions.lastreadoffset, &driveremap);
  1172.  
  1173.    if (!StopInsert)
  1174.    {
  1175.       /* MSGIDs in Index umwandeln */
  1176.       MsgIDsToIndex(Headers, maxmsgs);
  1177.  
  1178.       /* einzufuegende Header bestimmen */
  1179.       switch(pThreadListData->dspmode)
  1180.       {
  1181.          case DSPTHREADS_ALL:
  1182.             TraceAllThreads(Headers, maxmsgs);
  1183.             break;
  1184.  
  1185.          case DSPTHREADS_WITHUNREAD:
  1186.             TraceUnreadThreads(Headers, maxmsgs, pThreadListData->numbers);
  1187.             break;
  1188.  
  1189.          case DSPTHREADS_UNREADONLY:
  1190.             TraceUnreadMsgs(Headers, maxmsgs, pThreadListData->numbers);
  1191.             break;
  1192.  
  1193.          default:
  1194.             break;
  1195.       }
  1196.  
  1197.       if (threadlistoptions.compact)
  1198.          TraceSubjects(Headers, maxmsgs);
  1199.  
  1200.       WinPostMsg(hwndThreadList, TM_THREADSREADY, Headers, MPFROMLONG(maxmsgs));
  1201.    }
  1202.    else
  1203.    {
  1204.       free(Headers);
  1205.       DoingInsert=FALSE;
  1206.    }
  1207.  
  1208.    DEINSTALLEXPT;
  1209.  
  1210.    return;
  1211. }
  1212.  
  1213. /*---------------------------------------------------------------------------*/
  1214. /* Funktionsname: MsgIDsToIndex                                              */
  1215. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1216. /* Beschreibung: Wandelt die MSGIDs in den Reply-Verweisen in Array-Index    */
  1217. /*               um                                                          */
  1218. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1219. /* Parameter: Headers: Header-Array                                          */
  1220. /*            maxmsgs: Anzahl der Message-Header im Array                    */
  1221. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1222. /* Rückgabewerte: -                                                          */
  1223. /*                                                                           */
  1224. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1225. /* Sonstiges: ungueltige Verweise werden zu 0                                */
  1226. /*                                                                           */
  1227. /*---------------------------------------------------------------------------*/
  1228.  
  1229. static void MsgIDsToIndex(PTHREADHEADERS Headers, ULONG maxmsgs)
  1230. {
  1231.    ULONG msgnr, ulSeekID, nr2;
  1232.    int reply;
  1233.  
  1234.    for (msgnr=0; msgnr<maxmsgs; msgnr++)
  1235.    {
  1236.       /* replyto suchen */
  1237.       ulSeekID=Headers[msgnr].Header.ulReplyTo;
  1238.       nr2=0;
  1239.       while(nr2<maxmsgs && Headers[nr2].Header.ulMsgID < ulSeekID)
  1240.          nr2++;
  1241.       if (nr2<maxmsgs && Headers[nr2].Header.ulMsgID == ulSeekID)
  1242.          Headers[msgnr].Header.ulReplyTo=nr2+1;
  1243.       else
  1244.          Headers[msgnr].Header.ulReplyTo=0;
  1245.  
  1246.       /* replies absuchen */
  1247.       reply=0;
  1248.       while (reply<NUM_REPLIES && Headers[msgnr].Header.ulReplies[reply])
  1249.       {
  1250.          ulSeekID=Headers[msgnr].Header.ulReplies[reply];
  1251.          nr2=0;
  1252.          while(nr2<maxmsgs && Headers[nr2].Header.ulMsgID < ulSeekID)
  1253.             nr2++;
  1254.          if (nr2<maxmsgs && Headers[nr2].Header.ulMsgID == ulSeekID)
  1255.             Headers[msgnr].Header.ulReplies[reply]=nr2+1;
  1256.          else
  1257.             Headers[msgnr].Header.ulReplies[reply]=0;
  1258.          reply++;
  1259.       }
  1260.       /* restliche replies niederbuegeln */
  1261.       while(reply<NUM_REPLIES)
  1262.          Headers[msgnr].Header.ulReplies[reply++]=0;
  1263.    }
  1264.    return;
  1265. }
  1266.  
  1267. /*---------------------------------------------------------------------------*/
  1268. /* Funktionsname: TraceAllThreads                                            */
  1269. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1270. /* Beschreibung: Markiert die Header fuer "Alle Threads"                     */
  1271. /*                                                                           */
  1272. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1273. /* Parameter: Headers: Header-Array                                          */
  1274. /*            maxmsgs: Anzahl der Message-Header im Array                    */
  1275. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1276. /* Rueckgabewerte: -                                                         */
  1277. /*                                                                           */
  1278. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1279. /* Sonstiges:                                                                */
  1280. /*                                                                           */
  1281. /*---------------------------------------------------------------------------*/
  1282.  
  1283. static void TraceAllThreads(PTHREADHEADERS Headers, ULONG maxmsgs)
  1284. {
  1285.    ULONG msgnr=0;
  1286.  
  1287.    for (msgnr=0; msgnr<maxmsgs; msgnr++)
  1288.    {
  1289.       if (!Headers[msgnr].Header.ulReplyTo)
  1290.          Headers[msgnr].ulFlags=INSERT_PARENT;
  1291.       else
  1292.          Headers[msgnr].ulFlags=INSERT_CHILD;
  1293.    }
  1294.    return;
  1295. }
  1296.  
  1297. /*---------------------------------------------------------------------------*/
  1298. /* Funktionsname: TraceUnreadThreads                                         */
  1299. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1300. /* Beschreibung: Markiert die Header fuer "unread threads"                   */
  1301. /*                                                                           */
  1302. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1303. /* Parameter: Headers: Header-Array                                          */
  1304. /*            maxmsgs: Anzahl der Message-Header im Array                    */
  1305. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1306. /* Rückgabewerte: -                                                          */
  1307. /*                                                                           */
  1308. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1309. /* Sonstiges:                                                                */
  1310. /*                                                                           */
  1311. /*---------------------------------------------------------------------------*/
  1312.  
  1313. static void TraceUnreadThreads(PTHREADHEADERS Headers, ULONG maxmsgs, PULONG pCurrentNum)
  1314. {
  1315.    ULONG msgnr, num2, i;
  1316.  
  1317.    for (msgnr=0; msgnr<maxmsgs; msgnr++)
  1318.    {
  1319.       if (! ISREAD(Headers[msgnr]) ||
  1320.           (Headers[msgnr].Header.ulMsgID == pCurrentNum[0]))
  1321.       {
  1322.          /* ungelesene Message */
  1323.          num2=msgnr;
  1324.          while(Headers[num2].Header.ulReplyTo && !Headers[num2].ulFlags)
  1325.          {
  1326.             Headers[num2].ulFlags=INSERT_CHILD;
  1327.             num2=Headers[num2].Header.ulReplyTo-1;
  1328.          }
  1329.          if (!Headers[num2].Header.ulReplyTo)
  1330.          {
  1331.             /* oben angekommen, einsetzen und alle Children auch */
  1332.             Headers[num2].ulFlags=INSERT_PARENT;
  1333.             for (i=0; i<NUM_REPLIES; i++)
  1334.                if (Headers[num2].Header.ulReplies[i])
  1335.                   Headers[Headers[num2].Header.ulReplies[i]-1].ulFlags=INSERT_CHILD;
  1336.          }
  1337.       }
  1338.    }
  1339.  
  1340.    return;
  1341. }
  1342.  
  1343. /*---------------------------------------------------------------------------*/
  1344. /* Funktionsname: TraceUnreadMsgs                                            */
  1345. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1346. /* Beschreibung: Markiert die Header fuer "unread messages"                  */
  1347. /*                                                                           */
  1348. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1349. /* Parameter: Headers: Header-Array                                          */
  1350. /*            maxmsgs: Anzahl der Message-Header im Array                    */
  1351. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1352. /* Rückgabewerte: -                                                          */
  1353. /*                                                                           */
  1354. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1355. /* Sonstiges:                                                                */
  1356. /*                                                                           */
  1357. /*---------------------------------------------------------------------------*/
  1358.  
  1359. static void TraceUnreadMsgs(PTHREADHEADERS Headers, ULONG maxmsgs, PULONG pCurrentNum)
  1360. {
  1361.    ULONG msgnr, num2, i;
  1362.  
  1363.    for (msgnr=0; msgnr<maxmsgs; msgnr++)
  1364.    {
  1365.       if (! ISREAD(Headers[msgnr]) ||
  1366.           (Headers[msgnr].Header.ulMsgID == pCurrentNum[0]))
  1367.       {
  1368.          /* ungelesene Message */
  1369.          num2=msgnr;
  1370.          while(Headers[num2].Header.ulReplyTo &&
  1371.                (!Headers[Headers[num2].Header.ulReplyTo-1].ulFlags) &&
  1372.                 !ISREAD(Headers[Headers[num2].Header.ulReplyTo-1]))
  1373.          {
  1374.             Headers[num2].ulFlags=INSERT_CHILD;
  1375.             num2=Headers[num2].Header.ulReplyTo-1;
  1376.          }
  1377.  
  1378.          if (!Headers[num2].Header.ulReplyTo ||
  1379.              ISREAD(Headers[Headers[num2].Header.ulReplyTo-1]))
  1380.          {
  1381.             /* oben angekommen */
  1382.             Headers[num2].ulFlags=INSERT_PARENT;
  1383.             for (i=0; i<NUM_REPLIES; i++)
  1384.                if (Headers[num2].Header.ulReplies[i] &&
  1385.                    !ISREAD(Headers[Headers[num2].Header.ulReplies[i]-1]))
  1386.                   Headers[Headers[num2].Header.ulReplies[i]-1].ulFlags=INSERT_CHILD;
  1387.          }
  1388.          else
  1389.             Headers[num2].ulFlags=INSERT_CHILD;
  1390.       }
  1391.    }
  1392.    return;
  1393. }
  1394.  
  1395. /*---------------------------------------------------------------------------*/
  1396. /* Funktionsname: TraceSubjects                                              */
  1397. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1398. /* Beschreibung: Sucht die Threads mit gleichem Subject zusammen             */
  1399. /*                                                                           */
  1400. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1401. /* Parameter: Headers: Header-Array                                          */
  1402. /*            maxmsgs: Anzahl der Message-Header im Array                    */
  1403. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1404. /* Rückgabewerte: -                                                          */
  1405. /*                                                                           */
  1406. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1407. /* Sonstiges:                                                                */
  1408. /*                                                                           */
  1409. /*---------------------------------------------------------------------------*/
  1410.  
  1411. static void TraceSubjects(PTHREADHEADERS Headers, ULONG maxmsgs)
  1412. {
  1413.    int iStart, iLast, iNext;
  1414.  
  1415.    for (iStart = 0; iStart < maxmsgs; iStart++)
  1416.    {
  1417.       if (Headers[iStart].ulFlags == INSERT_PARENT)
  1418.       {
  1419.          /* Start eines Threads */
  1420.          Headers[iStart].ulFlags = INSERT_THREAD;
  1421.          iLast = iStart;
  1422.  
  1423.          /* gleiches Subject zusammensuchen */
  1424.          for (iNext = iStart+1; iNext < maxmsgs; iNext++)
  1425.          {
  1426.             if (Headers[iNext].ulFlags == INSERT_PARENT &&
  1427.                 !stricmp(Headers[iStart].Header.pchSubject,
  1428.                          Headers[iNext].Header.pchSubject))
  1429.             {
  1430.                /* gleiches Subject */
  1431.                Headers[iNext].ulFlags = INSERT_THREAD;
  1432.                Headers[iLast].ulNextParent = iNext;
  1433.                iLast = iNext;
  1434.             }
  1435.          }
  1436.       }
  1437.    }
  1438.  
  1439.    return;
  1440. }
  1441.  
  1442. /*---------------------------------------------------------------------------*/
  1443. /* Funktionsname: DeleteMessage                                              */
  1444. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1445. /* Beschreibung: Loescht eine Message im Baum                                */
  1446. /*                                                                           */
  1447. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1448. /* Parameter: hwndCnr: Window-Handle des Containers                          */
  1449. /*            pMessageID: MESSAGEID-Struktur vom Worker-Thread               */
  1450. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1451. /* Rückgabewerte: -                                                          */
  1452. /*                                                                           */
  1453. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1454. /* Sonstiges:                                                                */
  1455. /*                                                                           */
  1456. /*---------------------------------------------------------------------------*/
  1457.  
  1458. static void DeleteMessage(HWND hwndCnr, PMESSAGEID pMessageID)
  1459. {
  1460.    PTHREADRECORD pDelRecord=NULL;
  1461.  
  1462.    /* Message im Container suchen */
  1463.    pDelRecord=SendMsg(hwndCnr, CM_QUERYRECORD, NULL,
  1464.                          MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  1465.    while (pDelRecord && pDelRecord->ulMsgID != pMessageID->ulMsgID)
  1466.       pDelRecord=GetNextRecord(pDelRecord, hwndCnr);
  1467.  
  1468.    if (pDelRecord)
  1469.    {
  1470.       PTHREADRECORD pChRecord=pDelRecord;
  1471.       PTHREADRECORD pThreadRecord = NULL, pTempRec=pDelRecord;
  1472.  
  1473.       if (threadlistoptions.compact)
  1474.       {
  1475.          /* Subject-Record suchen */
  1476.          while (pTempRec = SendMsg(hwndCnr, CM_QUERYRECORD, pTempRec,
  1477.                                    MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER)))
  1478.          {
  1479.             pThreadRecord = pTempRec;
  1480.          }
  1481.       }
  1482.  
  1483.       /* Alle Child-Records abklappern, falls vorhanden */
  1484.       while(pChRecord=SendMsg(hwndCnr, CM_QUERYRECORD, pChRecord,
  1485.                           MPFROM2SHORT((pChRecord!=pDelRecord)? CMA_NEXT : CMA_FIRSTCHILD,
  1486.                                        CMA_ITEMORDER)))
  1487.       {
  1488.          PTHREADRECORD pNewTop=NULL;
  1489.  
  1490.          /* Record auf Top-Level kopieren */
  1491.          if (pNewTop=SendMsg(hwndCnr, CM_ALLOCRECORD, MPFROMLONG(sizeof(THREADRECORD)-sizeof(RECORDCORE)),
  1492.                                               MPFROMSHORT(1)))
  1493.          {
  1494.             PTHREADRECORD  pGChRecord=pChRecord;
  1495.  
  1496.             pNewTop->RecordCore.flRecordAttr = pChRecord->RecordCore.flRecordAttr;
  1497.             pNewTop->RecordCore.pszTree=pNewTop->pchText;
  1498.             strcpy(pNewTop->pchText, pChRecord->pchText);
  1499.             pNewTop->ulMsgID = pChRecord->ulMsgID;
  1500.             pNewTop->ulFlags = pChRecord->ulFlags;
  1501.             pNewTop->RecordCore.pTreeItemDesc= pChRecord->RecordCore.pTreeItemDesc;
  1502.  
  1503.             InsertRecords(WinQueryWindow(hwndCnr, QW_PARENT), pThreadRecord, pNewTop, 1, FALSE);
  1504.  
  1505.             /* Alle Grand-Child-Records abklappern und an Child-Record anhaengen */
  1506.             while(pGChRecord=SendMsg(hwndCnr, CM_QUERYRECORD, pGChRecord,
  1507.                                     MPFROM2SHORT((pGChRecord!=pChRecord)? CMA_NEXT : CMA_FIRSTCHILD,
  1508.                                                  CMA_ITEMORDER)))
  1509.             {
  1510.                PTHREADRECORD pNewRecord=NULL;
  1511.  
  1512.                if (pNewRecord=SendMsg(hwndCnr, CM_ALLOCRECORD, MPFROMLONG(sizeof(THREADRECORD)-sizeof(RECORDCORE)),
  1513.                                                         MPFROMSHORT(1)))
  1514.                {
  1515.                   pNewRecord->RecordCore.flRecordAttr = pGChRecord->RecordCore.flRecordAttr;
  1516.                   pNewRecord->RecordCore.pszTree=pNewRecord->pchText;
  1517.                   strcpy(pNewRecord->pchText, pGChRecord->pchText);
  1518.                   pNewRecord->ulMsgID = pGChRecord->ulMsgID;
  1519.                   pNewRecord->ulFlags = pGChRecord->ulFlags;
  1520.                   pNewRecord->RecordCore.pTreeItemDesc= pGChRecord->RecordCore.pTreeItemDesc;
  1521.  
  1522.                   InsertRecords(WinQueryWindow(hwndCnr, QW_PARENT), pNewTop, pNewRecord, 1, FALSE);
  1523.                }
  1524.             }
  1525.          }
  1526.       }
  1527.  
  1528.       /* Record loeschen, loescht auch dessen Child-Records */
  1529.       SendMsg(hwndCnr, CM_REMOVERECORD, &pDelRecord, MPFROM2SHORT(1, CMA_INVALIDATE | CMA_FREE));
  1530.  
  1531.       /* Schauen, ob Thread-Record noch Nachfolger hat, sonst auch loeschen */
  1532.       if (pThreadRecord)
  1533.       {
  1534.          if (!SendMsg(hwndCnr, CM_QUERYRECORD, pThreadRecord, MPFROM2SHORT(CMA_FIRSTCHILD, CMA_ITEMORDER)))
  1535.             SendMsg(hwndCnr, CM_REMOVERECORD, &pThreadRecord, MPFROM2SHORT(1, CMA_INVALIDATE | CMA_FREE));
  1536.       }
  1537.    }
  1538.    return;
  1539. }
  1540.  
  1541. /*---------------------------------------------------------------------------*/
  1542. /* Funktionsname: ThreadAddMessage                                           */
  1543. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1544. /* Beschreibung: Fuegt eine neue Message in den Baum ein                     */
  1545. /*                                                                           */
  1546. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1547. /* Parameter: hwndCnr: Window-Handle des Containers                          */
  1548. /*            pMesgID: MESSAGEID-Struktur vom Worker-Thread                  */
  1549. /*            pHeader: Neuer Message-Header                                  */
  1550. /*            ulDispMode: Display-Mode                                       */
  1551. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1552. /* Rückgabewerte: -                                                          */
  1553. /*                                                                           */
  1554. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1555. /* Sonstiges:                                                                */
  1556. /*                                                                           */
  1557. /*---------------------------------------------------------------------------*/
  1558.  
  1559. static void ThreadAddMessage(HWND hwndCnr, PMESSAGEID pMsgID, MSGHEADER *pHeader, PTHREADLISTDATA pThreadListData)
  1560. {
  1561.    PTHREADRECORD pParentRec=NULL, pNewRec=NULL;
  1562.  
  1563.    switch(pThreadListData->dspmode)
  1564.    {
  1565.       case DSPTHREADS_UNREADONLY:
  1566.          /* Nichts machen, neue Messages sind immer "read" */
  1567.          break;
  1568.  
  1569.       case DSPTHREADS_WITHUNREAD:
  1570.       case DSPTHREADS_ALL:
  1571.          if (pHeader->ulReplyTo)
  1572.          {
  1573.             /* neue Message ist ein Reply, Parent-Record suchen */
  1574.             pParentRec = SendMsg(hwndCnr, CM_QUERYRECORD, NULL,
  1575.                                     MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  1576.             while (pParentRec && pParentRec->ulMsgID != pHeader->ulReplyTo)
  1577.                pParentRec=GetNextRecord(pParentRec, hwndCnr);
  1578.  
  1579.             if (!pParentRec)
  1580.                /* nicht gefunden, wird evtl. erst spaeter eingefuegt,
  1581.                   die neue Message wird dann autom. beruecksichtigt, nix machen */
  1582.                return;
  1583.          }
  1584.          /* nun haben wir entweder den Parent-Record, oder die neue Msg ist
  1585.             kein Reply, Record einfuegen */
  1586.          if (threadlistoptions.compact && !pParentRec)
  1587.          {
  1588.             /* nach Subject suchen */
  1589.             pParentRec = SendMsg(hwndCnr, CM_QUERYRECORD, NULL,
  1590.                                     MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  1591.             while (pParentRec && stricmp(pParentRec->pchSubj, pHeader->pchSubject))
  1592.             {
  1593.                pParentRec = SendMsg(hwndCnr, CM_QUERYRECORD, pParentRec,
  1594.                                     MPFROM2SHORT(CMA_NEXT, CMA_ITEMORDER));
  1595.             }
  1596.  
  1597.             if (!pParentRec)
  1598.             {
  1599.                /* Neuen Thread einfuegen */
  1600.                if (pParentRec=SendMsg(hwndCnr, CM_ALLOCRECORD, MPFROMLONG(sizeof(THREADRECORD)-sizeof(RECORDCORE)),
  1601.                                                         MPFROMSHORT(1)))
  1602.                {
  1603.                   HeaderToThread(pThreadListData, pHeader, pParentRec, TRUE);
  1604.  
  1605.                   InsertRecords(WinQueryWindow(hwndCnr, QW_PARENT), NULL, pParentRec, 1, TRUE);
  1606.                }
  1607.             }
  1608.  
  1609.          }
  1610.  
  1611.          if (pNewRec=SendMsg(hwndCnr, CM_ALLOCRECORD, MPFROMLONG(sizeof(THREADRECORD)-sizeof(RECORDCORE)),
  1612.                                                   MPFROMSHORT(1)))
  1613.          {
  1614.             pHeader->ulMsgID = pMsgID->ulMsgID;
  1615.             HeaderToRecord(pThreadListData, pHeader, pNewRec, TRUE);
  1616.  
  1617.             InsertRecords(WinQueryWindow(hwndCnr, QW_PARENT), pParentRec, pNewRec, 1, TRUE);
  1618.          }
  1619.          break;
  1620.  
  1621.       default:
  1622.          break;
  1623.    }
  1624.    return;
  1625. }
  1626.  
  1627. /*---------------------------------------------------------------------------*/
  1628. /* Funktionsname: ThreadChangeMessage                                        */
  1629. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1630. /* Beschreibung: Aendert eine Message in der Threadliste                     */
  1631. /*                                                                           */
  1632. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1633. /* Parameter: hwndCnr: Window-Handle des Containers                          */
  1634. /*            pMesgID: MESSAGEID-Struktur vom Worker-Thread                  */
  1635. /*            pHeader: Neuer Message-Header                                  */
  1636. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1637. /* Rückgabewerte: -                                                          */
  1638. /*                                                                           */
  1639. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1640. /* Sonstiges:                                                                */
  1641. /*                                                                           */
  1642. /*---------------------------------------------------------------------------*/
  1643.  
  1644. static void ThreadChangeMessage(HWND hwndCnr, PMESSAGEID pMsgID, MSGHEADER *pHeader, PTHREADLISTDATA pThreadListData)
  1645. {
  1646.    PTHREADRECORD pRecord=NULL;
  1647.  
  1648.    /* Message im Container suchen */
  1649.    pRecord=SendMsg(hwndCnr, CM_QUERYRECORD, NULL,
  1650.                       MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  1651.    while (pRecord && pRecord->ulMsgID != pMsgID->ulMsgID)
  1652.       pRecord=GetNextRecord(pRecord, hwndCnr);
  1653.  
  1654.    if (pRecord)
  1655.    {
  1656.       pHeader->ulMsgID = pMsgID->ulMsgID;
  1657.       HeaderToRecord(pThreadListData, pHeader, pRecord, TRUE);
  1658.  
  1659.       SendMsg(hwndCnr, CM_INVALIDATERECORD, &pRecord,
  1660.                  MPFROM2SHORT(1, CMA_TEXTCHANGED));
  1661.    }
  1662.    return;
  1663. }
  1664.  
  1665. /*---------------------------------------------------------------------------*/
  1666. /* Funktionsname: ThreadContextMenu                                          */
  1667. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1668. /* Beschreibung: Verarbeitet CN_CONTEXTMENU vom Container                    */
  1669. /*                                                                           */
  1670. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1671. /* Parameter: hwndDlg: Dialog-Window                                         */
  1672. /*            pThreadListData: Instanzdaten                                  */
  1673. /*            pRecord: Record fuer das Kontext-Menue                         */
  1674. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1675. /* Rückgabewerte: -                                                          */
  1676. /*                                                                           */
  1677. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1678. /* Sonstiges:                                                                */
  1679. /*                                                                           */
  1680. /*---------------------------------------------------------------------------*/
  1681.  
  1682. static void ThreadContextMenu(HWND hwndDlg, PTHREADLISTDATA pThreadListData, PTHREADRECORD pRecord)
  1683. {
  1684.    RECTL rectl, rectl2;
  1685.  
  1686.    if (bDoingWork || pThreadListData->lDisableCount > 0)
  1687.    {
  1688.       WinAlarm(HWND_DESKTOP, WA_NOTE);
  1689.       return;
  1690.    }
  1691.  
  1692.    if (pRecord &&
  1693.        (pRecord->ulFlags & THREADFLAG_THREADSTART))
  1694.    {
  1695.       WinAlarm(HWND_DESKTOP, WA_NOTE);
  1696.       return;
  1697.    }
  1698.  
  1699.    pThreadListData->pPopupRecord= pRecord;
  1700.  
  1701.    if (pRecord)
  1702.    {
  1703.       QUERYRECORDRECT qRecord;
  1704.  
  1705.       qRecord.cb=sizeof(QUERYRECORDRECT);
  1706.       qRecord.pRecord=(PRECORDCORE) pRecord;
  1707.       qRecord.fRightSplitWindow=FALSE;
  1708.       qRecord.fsExtent=CMA_TEXT;
  1709.       WinSendDlgItemMsg(hwndDlg, IDD_THREADLIST+1, CM_QUERYRECORDRECT,
  1710.                         &rectl, &qRecord);
  1711.    }
  1712.    else
  1713.       WinQueryWindowRect(WinWindowFromID(hwndDlg, IDD_THREADLIST+1), &rectl);
  1714.  
  1715.    if (pThreadListData->bKeyboard)
  1716.    {
  1717.       WinQueryWindowRect(WinWindowFromID(hwndDlg, IDD_THREADLIST+1), &rectl2);
  1718.       WinMapWindowPoints(WinWindowFromID(hwndDlg, IDD_THREADLIST+1),
  1719.                          HWND_DESKTOP, (PPOINTL) &rectl2, 2);
  1720.  
  1721.       if (pRecord)
  1722.          WinPopupMenu(HWND_DESKTOP, hwndDlg, pThreadListData->hwndThreadPopup,
  1723.                       rectl2.xLeft+rectl.xLeft+(rectl.xRight-rectl.xLeft)/2,
  1724.                       rectl2.yBottom+rectl.yBottom+(rectl.yTop-rectl.yBottom)/2,
  1725.                       0, PU_HCONSTRAIN | PU_VCONSTRAIN | PU_KEYBOARD | PU_MOUSEBUTTON1);
  1726.       else
  1727.          WinPopupMenu(HWND_DESKTOP, hwndDlg, pThreadListData->hwndListPopup,
  1728.                       rectl2.xLeft+(rectl2.xRight-rectl2.xLeft)/2,
  1729.                       rectl2.yBottom+rectl.yBottom+(rectl.yTop-rectl.yBottom)/2,
  1730.                       0, PU_HCONSTRAIN | PU_VCONSTRAIN | PU_KEYBOARD | PU_MOUSEBUTTON1);
  1731.    }
  1732.    else
  1733.    {
  1734.       POINTL pointl;
  1735.  
  1736.       WinQueryPointerPos(HWND_DESKTOP, &pointl);
  1737.       if (pRecord)
  1738.          WinPopupMenu(HWND_DESKTOP, hwndDlg, pThreadListData->hwndThreadPopup,
  1739.                       pointl.x, pointl.y,
  1740.                       0, PU_HCONSTRAIN | PU_VCONSTRAIN | PU_KEYBOARD | PU_MOUSEBUTTON1);
  1741.       else
  1742.          WinPopupMenu(HWND_DESKTOP, hwndDlg, pThreadListData->hwndListPopup,
  1743.                       pointl.x, pointl.y,
  1744.                       0, PU_HCONSTRAIN | PU_VCONSTRAIN | PU_KEYBOARD | PU_MOUSEBUTTON1);
  1745.    }
  1746.    return;
  1747. }
  1748.  
  1749. /*---------------------------------------------------------------------------*/
  1750. /* Funktionsname: SwitchThreadlistView                                       */
  1751. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1752. /* Beschreibung: Schaltet den View-Modus der Threadliste um, liest Threads   */
  1753. /*               neu ein.                                                    */
  1754. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1755. /* Parameter: hwndDlg: Dialog-Window                                         */
  1756. /*            pThreadListData: Instanzdaten                                  */
  1757. /*            sCmdID: ID des Menues                                          */
  1758. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1759. /* Rückgabewerte: -                                                          */
  1760. /*                                                                           */
  1761. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1762. /* Sonstiges:                                                                */
  1763. /*                                                                           */
  1764. /*---------------------------------------------------------------------------*/
  1765.  
  1766. static void SwitchThreadlistView(HWND hwndDlg, PTHREADLISTDATA pThreadListData, SHORT sCmdID)
  1767. {
  1768.    if (DoingInsert)
  1769.    {
  1770.       StopInsert=TRUE;
  1771.       DosWaitThread((PTID) &tidThreadList, DCWW_WAIT);
  1772.    }
  1773.  
  1774.    switch(sCmdID)
  1775.    {
  1776.       case IDM_TLP_VIEWALL:
  1777.          pThreadListData->dspmode = DSPTHREADS_ALL;
  1778.          break;
  1779.  
  1780.       case IDM_TLP_VIEWTHR:
  1781.          pThreadListData->dspmode = DSPTHREADS_WITHUNREAD;
  1782.          break;
  1783.  
  1784.       case IDM_TLP_VIEWUNR:
  1785.          pThreadListData->dspmode = DSPTHREADS_UNREADONLY;
  1786.          break;
  1787.  
  1788.       default:
  1789.          break;
  1790.    }
  1791.    UpdateDspMenu(pThreadListData);
  1792.  
  1793.    SendMsg(hwndDlg, TM_REREADTHREADS, &pThreadListData->MsgListPar, NULL);
  1794.  
  1795.    return;
  1796. }
  1797.  
  1798. /*---------------------------------------------------------------------------*/
  1799. /* Funktionsname: SwitchSenderName                                           */
  1800. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1801. /* Beschreibung: Schaltet die Namens-Ansicht um                              */
  1802. /*                                                                           */
  1803. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1804. /* Parameter: hwndDlg: Dialog-Window                                         */
  1805. /*            pThreadListData: Instanzdaten                                  */
  1806. /*            bNoUpdate: Vorhandene Records nicht updaten                    */
  1807. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1808. /* Rückgabewerte: -                                                          */
  1809. /*                                                                           */
  1810. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1811. /* Sonstiges:                                                                */
  1812. /*                                                                           */
  1813. /*---------------------------------------------------------------------------*/
  1814.  
  1815. static void SwitchSenderName(HWND hwndDlg, PTHREADLISTDATA pThreadListData, BOOL bNoUpdate)
  1816. {
  1817.    PTHREADRECORD pRecord=NULL;
  1818.    HWND hwndCnr = WinWindowFromID(hwndDlg, IDD_THREADLIST+1);
  1819.  
  1820.    pThreadListData->bSenderName = !pThreadListData->bSenderName;
  1821.    WinCheckMenuItem(pThreadListData->hwndListPopup, IDM_TLP_SENDER, pThreadListData->bSenderName);
  1822.  
  1823.    if (!bNoUpdate)
  1824.    {
  1825.       /* Messages im Container updaten */
  1826.       pRecord=SendMsg(hwndCnr, CM_QUERYRECORD, NULL,
  1827.                       MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  1828.       while (pRecord)
  1829.       {
  1830.          if (!threadlistoptions.compact || !(pRecord->ulFlags & THREADFLAG_THREADSTART))
  1831.             BuildThreadTitle(pRecord->pchName, pRecord->pchSubj,
  1832.                              pThreadListData->bSenderName, pRecord->RecordCore.pszTree);
  1833.  
  1834.          pRecord=GetNextRecord(pRecord, hwndCnr);
  1835.       }
  1836.       SendMsg(hwndCnr, CM_INVALIDATERECORD, NULL, NULL);
  1837.    }
  1838.  
  1839.  
  1840.    return;
  1841. }
  1842.  
  1843. /*---------------------------------------------------------------------------*/
  1844. /* Funktionsname: IsPersonalMessage                                          */
  1845. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1846. /* Beschreibung: Stellt fest, ob ein Name einer der eigenen Namen ist        */
  1847. /*                                                                           */
  1848. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1849. /* Parameter: pchToName: gesuchter Name                                      */
  1850. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1851. /* Rückgabewerte: TRUE Name ist eigener Name                                 */
  1852. /*                FALSE Name ist nicht eigener Name                          */
  1853. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1854. /* Sonstiges:                                                                */
  1855. /*                                                                           */
  1856. /*---------------------------------------------------------------------------*/
  1857.  
  1858. static BOOL IsPersonalMessage(char *pchToName)
  1859. {
  1860.    extern USERDATAOPT userdaten;
  1861.    int i=0;
  1862.  
  1863.    while (i < MAX_USERNAMES && userdaten.username[i][0])
  1864.    {
  1865.       if (!stricmp(pchToName, userdaten.username[i]))
  1866.          return TRUE;
  1867.       else
  1868.          i++;
  1869.    }
  1870.    return FALSE;
  1871.  
  1872. }
  1873.  
  1874. /*---------------------------------------------------------------------------*/
  1875. /* Funktionsname: CollectThread                                              */
  1876. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1877. /* Beschreibung: Sammelt die MSGIDs eines Threads.                           */
  1878. /*                                                                           */
  1879. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1880. /* Parameter: pThreadWork: Zeiger auf statische THREADWORK-Struktur          */
  1881. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1882. /* Rückgabewerte: -                                                          */
  1883. /*                                                                           */
  1884. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1885. /* Sonstiges: Benoetigt evtl. viel Stack! 200 KB empfohlen.                  */
  1886. /*                                                                           */
  1887. /*---------------------------------------------------------------------------*/
  1888.  
  1889. static void _Optlink CollectThread(void *pThreadWork)
  1890. {
  1891.    extern MISCOPTIONS miscoptions;
  1892.    extern DRIVEREMAP driveremap;
  1893.    PWORKDATA pWorkData = ((PTHREADWORK) pThreadWork)->pWorkData;
  1894.    ULONG ulStartID = ((PTHREADWORK) pThreadWork)->ulStartID;
  1895.    AREADEFLIST *pSrcArea;
  1896.  
  1897.    INSTALLEXPT("CollectThread");
  1898.  
  1899.    bDoingWork=TRUE;
  1900.  
  1901.    pSrcArea = AM_FindArea(&arealiste, pWorkData->pchSrcArea);
  1902.  
  1903.    if (pSrcArea)
  1904.    {
  1905.       MSG_OpenArea(&arealiste, pWorkData->pchSrcArea, miscoptions.lastreadoffset, &driveremap);
  1906.  
  1907.       if (pSrcArea->maxmessages)
  1908.       {
  1909.          pWorkData->MsgIDArray = malloc(pSrcArea->maxmessages * sizeof(ULONG));
  1910.          pWorkData->ulArraySize=0; /* noch leer */
  1911.          pWorkData->next=NULL;
  1912.  
  1913.          /* UMSGIDs sammeln */
  1914.          RecurseThread(ulStartID, pWorkData);
  1915.  
  1916.          /* Worker-Thread starten */
  1917.          tidWorker = _beginthread(WorkerThread, NULL, 32768, pWorkData);
  1918.       }
  1919.       else
  1920.          bDoingWork=FALSE;
  1921.       MSG_CloseArea(&arealiste, pWorkData->pchSrcArea, FALSE, miscoptions.lastreadoffset, &driveremap);
  1922.    }
  1923.    else
  1924.       bDoingWork=FALSE;
  1925.  
  1926.    DEINSTALLEXPT;
  1927.  
  1928.    return;
  1929. }
  1930.  
  1931. /*---------------------------------------------------------------------------*/
  1932. /* Funktionsname: RecurseThread                                              */
  1933. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1934. /* Beschreibung: Sammelt die MSGIDs eines Threads rekursiv                   */
  1935. /*                                                                           */
  1936. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1937. /* Parameter: ulMsgID: Ab dieser Message sammeln                             */
  1938. /*            pWorkData: WORKDATA-Struktur                                   */
  1939. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1940. /* Rückgabewerte: -                                                          */
  1941. /*                                                                           */
  1942. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1943. /* Sonstiges: Benoetigt evtl. viel Stack! 200 KB empfohlen.                  */
  1944. /*                                                                           */
  1945. /*---------------------------------------------------------------------------*/
  1946.  
  1947. static void RecurseThread(ULONG ulMsgID, PWORKDATA pWorkData)
  1948. {
  1949.    ULONG msgnr;
  1950.  
  1951.    /* UMSGID hinzufuegen */
  1952.    pWorkData->MsgIDArray[pWorkData->ulArraySize] = ulMsgID;
  1953.    pWorkData->ulArraySize++;
  1954.  
  1955.    msgnr = MSG_UidToMsgn(&arealiste, pWorkData->pchSrcArea, ulMsgID, TRUE);
  1956.  
  1957.    if (msgnr)
  1958.    {
  1959.       MSGHEADER *pHeader = malloc(sizeof(MSGHEADER));
  1960.  
  1961.       if (!MSG_ReadHeader(pHeader, &arealiste, pWorkData->pchSrcArea, msgnr))
  1962.       {
  1963.          char i=0;
  1964.  
  1965.          while (i < NUM_REPLIES && pHeader->ulReplies[i])
  1966.          {
  1967.             RecurseThread(pHeader->ulReplies[i], pWorkData);
  1968.             i++;
  1969.          }
  1970.       }
  1971.       free(pHeader);
  1972.    }
  1973.  
  1974.    return;
  1975. }
  1976.  
  1977. /*---------------------------------------------------------------------------*/
  1978. /* Funktionsname: WorkOnThread                                               */
  1979. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1980. /* Beschreibung: Leitet die Bearbeitung eines Threads ein                    */
  1981. /*                                                                           */
  1982. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1983. /* Parameter: hwndDlg: Window-Handle der Threadliste                         */
  1984. /*            pThreadListData: Instanzdaten der Liste                        */
  1985. /*            usCmdID: ID von WM_COMMAND                                     */
  1986. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1987. /* Rückgabewerte: -                                                          */
  1988. /*                                                                           */
  1989. /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
  1990. /* Sonstiges:                                                                */
  1991. /*                                                                           */
  1992. /*---------------------------------------------------------------------------*/
  1993.  
  1994. static void WorkOnThread(HWND hwndDlg, PTHREADLISTDATA pThreadListData, USHORT usCmdID)
  1995. {
  1996.    extern PATHNAMES pathnames;
  1997.    extern GENERALOPT generaloptions;
  1998.    AREALISTPAR AreaListPar;
  1999.    extern ULONG ulExportOptions;
  2000.    AREADEFLIST *pArea;
  2001.  
  2002.    if (bDoingWork)
  2003.    {
  2004.       /* Fehlermeldung */
  2005.       MessageBox(hwndDlg, IDST_MSG_DOINGWORK, 0, IDD_DOINGWORK,
  2006.                  MB_OK);
  2007.       return;
  2008.    }
  2009.  
  2010.    ThreadWork.ulStartID = pThreadListData->pPopupRecord->ulMsgID;
  2011.    ThreadWork.pWorkData = malloc(sizeof(WORKDATA));
  2012.    memset(ThreadWork.pWorkData , 0, sizeof(WORKDATA));
  2013.  
  2014.    ThreadWork.pWorkData->pPrintDest = NULL;
  2015.    strcpy(ThreadWork.pWorkData->pchSrcArea, CurrentArea);
  2016.    strcpy(ThreadWork.pWorkData->pchDestArea, CurrentArea);
  2017.    ThreadWork.pWorkData->ulCopyMove = 0;
  2018.  
  2019.    /* Destination-Area ermitteln */
  2020.    AreaListPar.cb=sizeof(AREALISTPAR);
  2021.    AreaListPar.pchString = NULL;
  2022.  
  2023.    switch(usCmdID)
  2024.    {
  2025.       case IDM_THP_DELETE:
  2026.          ThreadWork.pWorkData->flWorkToDo = WORK_DELETE;
  2027.          break;
  2028.  
  2029.       case IDM_THP_MOVE:
  2030.          ThreadWork.pWorkData->flWorkToDo = WORK_MOVE;
  2031.  
  2032.          AreaListPar.idTitle = IDST_TITLE_AL_MOVE;
  2033.          AreaListPar.ulIncludeTypes = INCLUDE_ALL;
  2034.          AreaListPar.bExtendedSel = FALSE;
  2035.          AreaListPar.bChange      = FALSE;
  2036.          if (generaloptions.LastMoveArea[0])
  2037.             AreaListPar.pchString=strdup(generaloptions.LastMoveArea);
  2038.          else
  2039.             AreaListPar.pchString=strdup(CurrentArea);
  2040.  
  2041.          /* ZielArea holen */
  2042.          if (WinDlgBox(HWND_DESKTOP, hwndDlg,
  2043.                        AreaListProc, hmodLang,
  2044.                        IDD_AREALIST, &AreaListPar)!=DID_OK || !AreaListPar.pchString)
  2045.          {
  2046.             free(ThreadWork.pWorkData);
  2047.             return;
  2048.          }
  2049.  
  2050.          pArea = AM_FindArea(&arealiste, AreaListPar.pchString);
  2051.          if (pArea && pArea->areadata.areatype != AREATYPE_LOCAL)
  2052.             switch(MessageBox(hwndDlg, IDST_MSG_RESEND, IDST_TITLE_RESEND,
  2053.                               IDD_RESEND, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2))
  2054.             {
  2055.                case MBID_YES:
  2056.                   ThreadWork.pWorkData->ulCopyMove = COPYMOVE_RESEND;
  2057.                   break;
  2058.  
  2059.                default:
  2060.                   break;
  2061.             }
  2062.  
  2063.          strcpy(ThreadWork.pWorkData->pchDestArea, AreaListPar.pchString);
  2064.          break;
  2065.  
  2066.       case IDM_THP_COPY:
  2067.          ThreadWork.pWorkData->flWorkToDo = WORK_COPY;
  2068.  
  2069.          AreaListPar.idTitle = IDST_TITLE_AL_COPY;
  2070.          AreaListPar.ulIncludeTypes = INCLUDE_ALL;
  2071.          AreaListPar.bExtendedSel = FALSE;
  2072.          AreaListPar.bChange      = FALSE;
  2073.          if (generaloptions.LastCopyArea[0])
  2074.             AreaListPar.pchString=strdup(generaloptions.LastCopyArea);
  2075.          else
  2076.             AreaListPar.pchString=strdup(CurrentArea);
  2077.  
  2078.          /* ZielArea holen */
  2079.          if (WinDlgBox(HWND_DESKTOP, hwndDlg,
  2080.                        AreaListProc, hmodLang,
  2081.                        IDD_AREALIST, &AreaListPar)!=DID_OK || !AreaListPar.pchString)
  2082.          {
  2083.             free(ThreadWork.pWorkData);
  2084.             return;
  2085.          }
  2086.  
  2087.          pArea = AM_FindArea(&arealiste, AreaListPar.pchString);
  2088.          if (pArea && pArea->areadata.areatype != AREATYPE_LOCAL)
  2089.             switch(MessageBox(hwndDlg, IDST_MSG_RESEND, IDST_TITLE_RESEND,
  2090.                               IDD_RESEND, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2))
  2091.             {
  2092.                case MBID_YES:
  2093.                   ThreadWork.pWorkData->ulCopyMove = COPYMOVE_RESEND;
  2094.                   break;
  2095.  
  2096.                default:
  2097.                   break;
  2098.             }
  2099.  
  2100.          strcpy(ThreadWork.pWorkData->pchDestArea, AreaListPar.pchString);
  2101.          break;
  2102.  
  2103.       case IDM_THP_EXPORT:
  2104.          ThreadWork.pWorkData->flWorkToDo = WORK_EXPORT;
  2105.  
  2106.          strcpy(ThreadWork.pWorkData->pchDestFile, pathnames.lastexport);
  2107.  
  2108.          /* Filenamen holen */
  2109.          if (GetExportName(hwndDlg, ThreadWork.pWorkData->pchDestFile,
  2110.                            &ulExportOptions))
  2111.          {
  2112.             strcpy(pathnames.lastexport, ThreadWork.pWorkData->pchDestFile);
  2113.             ThreadWork.pWorkData->ulExportOptions = ulExportOptions;
  2114.          }
  2115.          else
  2116.          {
  2117.             free(AreaListPar.pchString);
  2118.             free(ThreadWork.pWorkData);
  2119.             return;
  2120.          }
  2121.          break;
  2122.  
  2123.       case IDM_THP_PRINT:
  2124.          ThreadWork.pWorkData->flWorkToDo = WORK_PRINT;
  2125.          break;
  2126.  
  2127.       case IDM_THP_MARK:
  2128.          if (generaloptions.safety & SAFETY_CATCHUP)
  2129.             if (MessageBox(hwndDlg, IDST_MSG_CATCHUP, IDST_TITLE_CATCHUP,
  2130.                            IDD_CATCHUP, MB_YESNO) != MBID_YES)
  2131.          {
  2132.             free(ThreadWork.pWorkData);
  2133.             return;
  2134.          }
  2135.          ThreadWork.pWorkData->flWorkToDo = WORK_MARK;
  2136.          break;
  2137.  
  2138.       default:
  2139.          free(ThreadWork.pWorkData);
  2140.          return;
  2141.    }
  2142.    free(AreaListPar.pchString);
  2143.  
  2144.    _beginthread(CollectThread, NULL, 200000, &ThreadWork);
  2145.  
  2146.    return;
  2147. }
  2148.  
  2149. static void HeaderToRecord(PTHREADLISTDATA pThreadListData, PMSGHEADER pHeader, PTHREADRECORD pRecord, BOOL bStripRe)
  2150. {
  2151.    char pchDup[LEN_SUBJECT+1];
  2152.  
  2153.    if (pHeader->ulReplies[0])
  2154.       pRecord->RecordCore.flRecordAttr=CRA_COLLAPSED;
  2155.    else
  2156.       pRecord->RecordCore.flRecordAttr=0;
  2157.  
  2158.    pRecord->ulFlags=0;
  2159.    pRecord->RecordCore.pszTree=pRecord->pchText;
  2160.  
  2161.    if (bStripRe)
  2162.       StripRe(pHeader->pchSubject);
  2163.    memcpy(pchDup, pHeader->pchSubject, LEN_SUBJECT+1);
  2164.    if (strtok(pchDup, " \t")==NULL)
  2165.       memcpy(pHeader->pchSubject, pThreadListData->pchEmptySubj, LEN_SUBJECT+1);
  2166.    BuildThreadTitle(pHeader->pchFromName, pHeader->pchSubject,
  2167.                     pThreadListData->bSenderName, pRecord->RecordCore.pszTree);
  2168.    memcpy(pRecord->pchName, pHeader->pchFromName, LEN_USERNAME+1);
  2169.    memcpy(pRecord->pchSubj, pHeader->pchSubject, LEN_SUBJECT+1);
  2170.    pRecord->ulMsgID= pHeader->ulMsgID;
  2171.    pRecord->RecordCore.pTreeItemDesc=NULL;
  2172.    if (pHeader->ulAttrib & ATTRIB_READ)
  2173.       pRecord->ulFlags |= THREADFLAG_READ;
  2174.    if (IsPersonalMessage(pHeader->pchToName))
  2175.       pRecord->ulFlags |= THREADFLAG_PERSONAL;
  2176.  
  2177.    return;
  2178. }
  2179.  
  2180. static void HeaderToThread(PTHREADLISTDATA pThreadListData, PMSGHEADER pHeader, PTHREADRECORD pRecord, BOOL bStripRe)
  2181. {
  2182.    char pchDup[LEN_SUBJECT+1];
  2183.  
  2184.    pRecord->RecordCore.flRecordAttr=CRA_COLLAPSED;
  2185.    pRecord->ulFlags=THREADFLAG_THREADSTART | THREADFLAG_READ;
  2186.    pRecord->RecordCore.pszTree=pRecord->pchText;
  2187.  
  2188.    if (bStripRe)
  2189.       StripRe(pHeader->pchSubject);
  2190.    memcpy(pchDup, pHeader->pchSubject, LEN_SUBJECT+1);
  2191.    if (strtok(pchDup, " \t")==NULL)
  2192.       memcpy(pHeader->pchSubject, pThreadListData->pchEmptySubj, LEN_SUBJECT+1);
  2193.    memcpy(pRecord->RecordCore.pszTree, pHeader->pchSubject, LEN_SUBJECT+1);
  2194.    memcpy(pRecord->pchSubj, pHeader->pchSubject, LEN_SUBJECT+1);
  2195.    pRecord->ulMsgID= 0;
  2196.    pRecord->RecordCore.pTreeItemDesc=NULL;
  2197.  
  2198.    return;
  2199. }
  2200.  
  2201. static void InsertRecords(HWND hwndDlg, PTHREADRECORD pParent, PTHREADRECORD pInsert, ULONG ulNumInsert, BOOL bInvalidate)
  2202. {
  2203.    RECORDINSERT RecordInsert;
  2204.  
  2205.    RecordInsert.cb=sizeof(RECORDINSERT);
  2206.    RecordInsert.pRecordOrder=(PRECORDCORE) CMA_END;
  2207.    RecordInsert.pRecordParent=(PRECORDCORE) pParent;
  2208.    RecordInsert.fInvalidateRecord=bInvalidate;
  2209.    RecordInsert.zOrder=CMA_TOP;
  2210.    RecordInsert.cRecordsInsert=ulNumInsert;
  2211.  
  2212.    WinSendDlgItemMsg(hwndDlg, IDD_THREADLIST+1, CM_INSERTRECORD, pInsert, &RecordInsert);
  2213.  
  2214.    return;
  2215. }
  2216.  
  2217. static char *BuildThreadTitle(char *pchName, char *pchSubj, BOOL bUseSender, char *pchBuffer)
  2218. {
  2219.    if (bUseSender)
  2220.       sprintf(pchBuffer, "%s (%s)", pchSubj, pchName);
  2221.    else
  2222.       strcpy(pchBuffer, pchSubj);
  2223.  
  2224.    return pchBuffer;
  2225. }
  2226.  
  2227. static void ThreadsReady(HWND parent, PTHREADLISTDATA pThreadListData, PTHREADHEADERS Headers, ULONG maxmsgs)
  2228. {
  2229.    PTHREADRECORD pMsgInsert, pFirstMsgInsert, pChildRecords;
  2230.    int insnum=0, threadnum=0;
  2231.    ULONG i, ulChildCount;
  2232.    PTHREADRECORD pRecord, pParentRecord, pThreadRecord=NULL;
  2233.    CNRINFO cnrinfo;
  2234.    int ThreadTopExpanded=FALSE;
  2235.  
  2236.    for (threadnum=0; threadnum<maxmsgs; threadnum++)
  2237.    {
  2238.       if (Headers[threadnum].ulFlags == (threadlistoptions.compact?INSERT_THREAD:INSERT_PARENT))
  2239.       {
  2240.          insnum = threadnum;
  2241.  
  2242.          if (threadlistoptions.compact)
  2243.          {
  2244.             /* Thread-Header vorbereiten */
  2245.             pThreadRecord=(PTHREADRECORD)WinSendDlgItemMsg(parent, IDD_THREADLIST+1,
  2246.                               CM_ALLOCRECORD,
  2247.                               MPFROMLONG(sizeof(THREADRECORD)-sizeof(RECORDCORE)),
  2248.                               MPFROMSHORT(1));
  2249.             HeaderToThread(pThreadListData, &Headers[insnum].Header, pThreadRecord, FALSE);
  2250.             InsertRecords(parent, NULL, pThreadRecord, 1, FALSE);
  2251.          }
  2252.  
  2253.          do
  2254.          {
  2255.             Headers[insnum].ulFlags=0;
  2256.             /* Anzahl der Child-Records zaehlen */
  2257.  
  2258.             i=0;
  2259.             ulChildCount=0;
  2260.             while (i<NUM_REPLIES &&
  2261.                    Headers[insnum].Header.ulReplies[i])
  2262.             {
  2263.                if (Headers[Headers[insnum].Header.ulReplies[i]-1].ulFlags == INSERT_CHILD)
  2264.                   ulChildCount++;
  2265.                i++;
  2266.             }
  2267.             pFirstMsgInsert=(PTHREADRECORD)WinSendDlgItemMsg(parent, IDD_THREADLIST+1,
  2268.                               CM_ALLOCRECORD,
  2269.                               MPFROMLONG(sizeof(THREADRECORD)-sizeof(RECORDCORE)),
  2270.                               MPFROMSHORT(1+ulChildCount));
  2271.             HeaderToRecord(pThreadListData, &Headers[insnum].Header, pFirstMsgInsert, FALSE);
  2272.  
  2273.             pParentRecord=pFirstMsgInsert;
  2274.             pChildRecords=(PTHREADRECORD)pFirstMsgInsert->RecordCore.preccNextRecord;
  2275.  
  2276.             InsertRecords(parent, pThreadRecord, pFirstMsgInsert, 1, FALSE);
  2277.  
  2278.             /* zweite Stufe (Replies) mit bearbeiten */
  2279.             if (ulChildCount)
  2280.             {
  2281.                pMsgInsert=pChildRecords;
  2282.  
  2283.                i=0;
  2284.                while (i<NUM_REPLIES &&
  2285.                       Headers[insnum].Header.ulReplies[i])
  2286.                {
  2287.                   ULONG num2;
  2288.  
  2289.                   num2=Headers[insnum].Header.ulReplies[i]-1;
  2290.  
  2291.                   if (Headers[num2].ulFlags == INSERT_CHILD)
  2292.                   {
  2293.                      Headers[num2].ulFlags=0;
  2294.  
  2295.                      HeaderToRecord(pThreadListData, &Headers[num2].Header, pMsgInsert, FALSE);
  2296.  
  2297.                      pMsgInsert=(PTHREADRECORD)pMsgInsert->RecordCore.preccNextRecord;
  2298.                   }
  2299.                   i++;
  2300.                }
  2301.                InsertRecords(parent, pParentRecord, pChildRecords, ulChildCount, FALSE);
  2302.             }
  2303.  
  2304.             /* naechsten record */
  2305.             if (threadlistoptions.compact)
  2306.                insnum = Headers[insnum].ulNextParent;
  2307.             else
  2308.                insnum=0;
  2309.          } while (insnum);
  2310.       }
  2311.    }
  2312.  
  2313.    free(Headers);
  2314.    WinEnableWindowUpdate(WinWindowFromID(parent, IDD_THREADLIST+1), FALSE);
  2315.    pRecord=NULL;
  2316.  
  2317.    pThreadListData->Trace2=pThreadListData->Trace;
  2318.    while(pThreadListData->Trace2)
  2319.    {
  2320.       /* Message im Container suchen */
  2321.       pRecord=WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_QUERYRECORD, NULL,
  2322.                          MPFROM2SHORT(CMA_FIRST, CMA_ITEMORDER));
  2323.       do
  2324.       {
  2325.          if (pRecord)
  2326.          {
  2327.             if (pRecord->ulMsgID == (ULONG) pThreadListData->Trace2->msgid)
  2328.                break;
  2329.             pRecord=GetNextRecord(pRecord, WinWindowFromID(parent, IDD_THREADLIST+1));
  2330.          }
  2331.       }  while (pRecord);
  2332.  
  2333.       /* Thread expanden */
  2334.       if (pRecord)
  2335.       {
  2336.          if (threadlistoptions.compact && /* erst noch Thread-Record expanden */
  2337.              !ThreadTopExpanded &&
  2338.              (pThreadRecord = WinSendDlgItemMsg(parent, IDD_THREADLIST+1,
  2339.                                                 CM_QUERYRECORD, pRecord,
  2340.                                                 MPFROM2SHORT(CMA_PARENT, CMA_ITEMORDER))) )
  2341.          {
  2342.             ThreadTopExpanded = TRUE;
  2343.             WinSendDlgItemMsg(parent, IDD_THREADLIST+1,
  2344.                               CM_EXPANDTREE, pThreadRecord, NULL);
  2345.          }
  2346.  
  2347.          if (pThreadListData->Trace2->next)
  2348.             WinSendDlgItemMsg(parent, IDD_THREADLIST+1,
  2349.                               CM_EXPANDTREE, pRecord, NULL);
  2350.       }
  2351.  
  2352.       /* naechste Message */
  2353.       pThreadListData->Trace2=pThreadListData->Trace2->next;
  2354.    }
  2355.    /* Baum mit der aktuellen Message ist nun voll expanded, */
  2356.    /* pRecord zeigt auf die aktuelle Message */
  2357.  
  2358.    switch(pThreadListData->dspmode)
  2359.    {
  2360.       case DSPTHREADS_ALL:
  2361.          cnrinfo.pszCnrTitle=pchAllThreads;
  2362.          break;
  2363.  
  2364.       case DSPTHREADS_WITHUNREAD:
  2365.          cnrinfo.pszCnrTitle=pchUnread;
  2366.          break;
  2367.  
  2368.       case DSPTHREADS_UNREADONLY:
  2369.          cnrinfo.pszCnrTitle=pchUnreadOnly;
  2370.          break;
  2371.    }
  2372.    WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_SETCNRINFO, &cnrinfo,
  2373.                       MPFROMLONG(CMA_CNRTITLE));
  2374.  
  2375.    WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_INVALIDATERECORD, NULL, NULL);
  2376.    WinEnableWindowUpdate(WinWindowFromID(parent, IDD_THREADLIST+1), TRUE);
  2377.  
  2378.    if (pRecord)
  2379.    {
  2380.       QUERYRECORDRECT qrecord;
  2381.       RECTL rectl, rectl2;
  2382.  
  2383.       /* Cursor auf den Record setzen */
  2384.       WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_SETRECORDEMPHASIS,
  2385.                         pRecord, MPFROM2SHORT(TRUE, CRA_CURSORED));
  2386.  
  2387.       /* Zum Record scrollen */
  2388.       WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_QUERYVIEWPORTRECT,
  2389.                         MPFROMP(&rectl2),
  2390.                         MPFROM2SHORT(CMA_WINDOW, FALSE));
  2391.  
  2392.       qrecord.cb=sizeof(QUERYRECORDRECT);
  2393.       qrecord.pRecord=(PRECORDCORE) pRecord;
  2394.       qrecord.fRightSplitWindow=TRUE;
  2395.       qrecord.fsExtent=CMA_TEXT;
  2396.       if (WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_QUERYRECORDRECT,
  2397.                             &rectl, &qrecord))
  2398.       {
  2399.          /*if (rectl.yBottom)*/
  2400.             WinSendDlgItemMsg(parent, IDD_THREADLIST+1, CM_SCROLLWINDOW,
  2401.                               MPFROMSHORT(CMA_VERTICAL),
  2402.                               MPFROMLONG((rectl2.yTop-rectl2.yBottom)/2-rectl.yBottom));
  2403.       }
  2404.    }
  2405.  
  2406.    DoingInsert = FALSE;
  2407.    return;
  2408. }
  2409.  
  2410. /*-------------------------------- Modulende --------------------------------*/
  2411.  
  2412.