home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / util / comm / news102.sit / NewsWatcher / source / commands.c next >
Text File  |  1991-04-03  |  32KB  |  1,354 lines

  1. /*----------------------------------------------------------
  2. #
  3. #    NewsWatcher    - Macintosh NNTP Client Application
  4. #
  5. #    Written by Steven Falkenburg
  6. #    ⌐1990 Apple Computer, Inc.
  7. #
  8. #-----------------------------------------------------------
  9. #
  10. #    commands.c
  11. #
  12. #    This file contains user interface routines which are
  13. #    called in response to user actions recorded in userint.c
  14. #
  15. #-----------------------------------------------------------*/
  16.  
  17. #pragma segment userint
  18.  
  19. #include "compat.h"
  20. #include <CType.h>
  21.  
  22. #ifdef PROTOS
  23.  
  24. #include <Types.h>
  25. #include <QuickDraw.h>
  26. #include <Fonts.h>
  27. #include <Windows.h>
  28. #include <Menus.h>
  29. #include <TextEdit.h>
  30. #include <Dialogs.h>
  31. #include <OSUtils.h>
  32. #include <Desk.h>
  33. #include <ToolUtils.h>
  34. #include <OSEvents.h>
  35. #include <Lists.h>
  36. #include <CursorCtl.h>
  37. #include <Packages.h>
  38. #include <Scrap.h>
  39. #include <Resources.h>
  40. #include <Script.h>
  41. #include <Printing.h>
  42. #include <Strings.h>
  43. #endif
  44.  
  45. #include <StdLib.h>
  46. #include <String.h>
  47.  
  48. #include "nntp.h"
  49. #include "userint.h"
  50. #include "newsprocess.h"
  51. #include "ScrollStuff.h"
  52. #include "netstuff.h"
  53. #include "miscstuff.h"
  54. #include "printstuff.h"
  55. #include "commands.h"
  56.  
  57. #ifdef NNTPNEWS
  58. #include "NNTPLow.h"
  59. #else
  60. #include "HFSNTPLow.h"
  61. #endif
  62.  
  63.  
  64. /*    adds a child window to the windowlist of a parent
  65.     Windows with associated child windows will close
  66.     their children when closed
  67. */
  68.  
  69. void AddChild(WindowPtr parent,WindowPtr child)
  70. {
  71.     TwindowInfo *parentInfo;
  72.     TWList *newChild;
  73.     
  74.     parentInfo = (TwindowInfo *) GetWRefCon(parent);
  75.     
  76.     newChild = (TWList *) MyNewPtr(sizeof(TWList));
  77.     if (MyMemErr() != noErr)
  78.         return;
  79.     newChild->childWindow = child;
  80.     newChild->next = parentInfo->childList;
  81.     parentInfo->childList = newChild;
  82. }
  83.  
  84.  
  85. /* Removes a child window from the windowlist of a parent
  86. */
  87.  
  88. void RemoveChild(WindowPtr parent,WindowPtr child)
  89. {
  90.     TwindowInfo *parentInfo;
  91.     TWList *current,*prev;
  92.     
  93.     parentInfo = (TwindowInfo *) GetWRefCon(parent);
  94.     for (current = prev = parentInfo->childList;
  95.         current != nil && current->childWindow!=child;
  96.         prev = current,current = current->next)
  97.         ;
  98.     if (current) {
  99.         prev->next = current->next;
  100.         if (prev == current)
  101.             parentInfo->childList = current->next;
  102.         MyDisposPtr((Ptr)current);
  103.     }
  104. }
  105.  
  106.  
  107. /*    MakeGroupList takes a list of groups and a List Manager
  108.     handle.  The groups are sorted and added to the list.
  109. */
  110.  
  111. void MakeGroupList(ListHandle theList,short numGroups,TGroup *groupList)
  112. {
  113.     long i;
  114.     Cell theCell;
  115.     char theName[256];
  116.     short cellLen;
  117.     
  118.     StatusWindow("Sorting Groups...",-1);
  119.     qsort(groupList, numGroups, sizeof(TGroup), MyCompare);
  120.     
  121.     StatusWindow("Building List...",0);
  122.     GiveTime(0);
  123.     
  124.     LDoDraw(false,theList);
  125.     LAddRow(numGroups,0,theList);
  126.     for (i=0; i<numGroups; i++) {
  127.  
  128.         strcpy(theName,groupList[i].name);
  129.         if ((groupList[i].lastMess - groupList[i].firstMess) <= 0) {
  130.             cellLen = strlen(theName)+2;
  131.             theName[cellLen-1] = 0xff;
  132.         }
  133.         else
  134.             cellLen = strlen(theName)+1;
  135.  
  136.         SetPt(&theCell,0,i);
  137.         LSetCell(theName,cellLen,theCell,theList);
  138.         StatusWindow("Building List...",(short)(((float)(i+1)/(float)numGroups)*100)+1);
  139.         GiveTime(0);
  140.     }
  141.     StatusWindow("Building List...",100);
  142.     GiveTime(0);
  143.     LDoDraw(true,theList);
  144.     SetCursor(&QDARROW);
  145. }
  146.  
  147.  
  148. /*    MyCompare is a comparison routine used in the call to
  149.     qsort() above.  It does a simple string compare, and
  150.     gives time to background applications.
  151. */
  152.  
  153. int MyCompare(TGroup *one,TGroup *two)
  154. {
  155.     GiveTime(0);
  156.     return strcmp(one->name,two->name);
  157. }
  158.  
  159.  
  160. /*    HandleGroupSelect is called in response to a mouse
  161.     down event in a main group window (not a user group
  162.     window).  It opens a subject window containing the
  163.     names of all articles in that group.
  164. */
  165.  
  166. void HandleGroupSelect(Cell theCell,WindowPtr window)
  167. {
  168.     TwindowInfo *theInfo;
  169.     WindowPtr theWind;
  170.     Str255 tmpStr,tmpTitle;
  171.     TGroup *groupList,*theGroup;
  172.     GrafPtr savePort;
  173.     Cell thePt;
  174.     Point firstOffset;
  175.     WindowPtr tmpWindow;
  176.     char groupStr[256];
  177.     short groupLen;
  178.     
  179.     extern TPrefRec gPrefs;
  180.     
  181.     SetPt(&firstOffset,0,0);
  182.     GetPort(&savePort);
  183.     SetPort(window);
  184.     LocalToGlobal(&firstOffset);
  185.     SetPort(savePort);
  186.  
  187.     theInfo = (TwindowInfo *) GetWRefCon(window);
  188.     groupList = (TGroup *) theInfo->data2;
  189.     
  190.     groupLen = 256;
  191.     LGetCell(groupStr,&groupLen,theCell,(ListHandle)theInfo->data);
  192.     
  193.     if (!FindGroup(theInfo,groupStr,&theGroup)) {
  194.         SysBeep(1);
  195.         return;
  196.     }
  197.     
  198.     strcpy((char *)tmpStr,theGroup->name);
  199.     c2pstr((char *)tmpStr);
  200.     for (tmpWindow = FrontWindow(); tmpWindow != nil; tmpWindow = (WindowPtr) ((WindowPeek)tmpWindow)->nextWindow) {
  201.         GetWTitle(tmpWindow,tmpTitle);
  202.         if (EqualString(tmpStr,tmpTitle,true,true)) {
  203.             SelectWindow(tmpWindow);
  204.             return;
  205.         }
  206.     }
  207.     
  208.     if (theGroup->firstMess > 0 &&
  209.         theGroup->lastMess> 0 &&
  210.         theGroup->lastMess - theGroup->firstMess >= 0) {
  211.             theInfo = (TwindowInfo *) GetWRefCon(theWind = MakeNewWindow(cSubject,true,firstOffset,(char *)tmpStr));
  212.             theInfo->parentGroup = nil;
  213.             theInfo->childList = nil;
  214.             theInfo->parentWindow = nil;
  215.             InitSubjectList(theInfo);
  216.             StatusWindow("Getting Subject List...",0);
  217.             AddToSubjectList(theInfo,theGroup->name,
  218.                             theGroup->firstMess,theGroup->lastMess);
  219.             StatusWindow("Getting Subject List...",100);
  220.             GiveTime(0);
  221.             CloseStatusWindow();
  222.             SetPt(&thePt,0,0);
  223.             LSetSelect(true,thePt,(ListHandle)theInfo->data);
  224.  
  225.             if (gPrefs.openWindowsZoomed)
  226.                 ToggleZoom(theWind);
  227.             ShowWindow(theWind);
  228.             
  229.             GetPort(&savePort);
  230.             SetPort(theWind);
  231.             InvalRect(&theWind->portRect);
  232.             SetPort(savePort);
  233.     }
  234.     else
  235.         SysBeep(1);
  236. }
  237.  
  238.  
  239. /*    HandleUserGroupSelect is called in response to a mouse
  240.     down in a user group window.  It opens a window containing
  241.     the subjects of all unread articles in that group.
  242. */
  243.  
  244. void HandleUserGroupSelect(Cell theCell,WindowPtr window)
  245. {
  246.     TwindowInfo *theInfo;
  247.     WindowPtr theWind;
  248.     Str255 tmpStr,tmpTitle;
  249.     TGroup *theGroup;
  250.     GrafPtr savePort;
  251.     ListHandle theList;
  252.     char groupName[256];
  253.     short groupLen = 255;
  254.     TReadRec *read;
  255.     WindowPtr parent;
  256.     Point firstOffset;
  257.     WindowPtr tmpWindow;
  258.     extern TPrefRec gPrefs;
  259.     
  260.     SetPt(&firstOffset,0,0);
  261.     GetPort(&savePort);
  262.     SetPort(window);
  263.     LocalToGlobal(&firstOffset);
  264.     SetPort(savePort);
  265.  
  266.     theInfo = (TwindowInfo *)GetWRefCon(window);
  267.     theInfo->changed = true;
  268.     
  269.     theList = (ListHandle) theInfo->data;
  270.     LGetCell(groupName,&groupLen,theCell,theList);
  271.  
  272.     strcpy((char *)tmpStr,groupName);
  273.     c2pstr((char *)tmpStr);
  274.     
  275.     /* make sure group window is not already open */
  276.     
  277.     for (tmpWindow = FrontWindow(); tmpWindow != nil; tmpWindow = (WindowPtr) ((WindowPeek)tmpWindow)->nextWindow) {
  278.         GetWTitle(tmpWindow,tmpTitle);
  279.         if (EqualString(tmpStr,tmpTitle,true,true)) {
  280.             SelectWindow(tmpWindow);
  281.             return;
  282.         }
  283.     }
  284.     
  285.     /* search linked info list for group info */
  286.     
  287.     
  288.     if (!FindGroup(theInfo,groupName,&theGroup) || !theGroup->read) {
  289.         SysBeep(1);
  290.         return;
  291.     }
  292.  
  293.     strcpy((char *)tmpStr,theGroup->name);
  294.     
  295.     parent = window;
  296.     theInfo = (TwindowInfo *) GetWRefCon(theWind = MakeNewWindow(cSubject,true,firstOffset,(char *)c2pstr((char *)tmpStr)));
  297.     theInfo->parentGroup = theGroup;
  298.     theInfo->parentWindow = parent;
  299.     theInfo->childList = nil;
  300.     AddChild(parent,theWind);
  301.     
  302.     InitSubjectList(theInfo);        
  303.     
  304.     StatusWindow("Getting Subject List...",0);
  305.     for (read = theGroup->read; read!=nil; read = read->next)
  306.         AddToSubjectList(theInfo,theGroup->name,read->firstRead,read->lastRead);
  307.     StatusWindow("Getting Subject List...",100);
  308.     GiveTime(0);
  309.     CloseStatusWindow();
  310.     
  311.     SetPt(&theCell,0,0);
  312.     LSetSelect(true,theCell,(ListHandle)theInfo->data);
  313.  
  314.     if (gPrefs.openWindowsZoomed)
  315.         ToggleZoom(theWind);
  316.     ShowWindow(theWind);
  317.     
  318.     GetPort(&savePort);
  319.     SetPort(theWind);
  320.     InvalRect(&theWind->portRect);
  321.     SetPort(savePort);
  322. }
  323.  
  324.  
  325. /*    FindGroup locates a user group record within the linked list
  326.     of user groups, given the name of the group.
  327. */
  328.  
  329. Boolean FindGroup(TwindowInfo *info,char *groupName,TGroup **theGroup)
  330. {
  331.     *theGroup = nil;
  332.     
  333.     if (info->kind == cGroup || info->kind == cNewGroup)
  334.         *theGroup = bsearch(groupName,(TGroup *)info->data2,info->numGroups,sizeof(TGroup),SearchCompare);
  335.     else if (info->kind == cUserGroup)
  336.         for (*theGroup = (TGroup *)info->data2;
  337.                         *theGroup != nil && strcmp((*theGroup)->name,groupName)!=0;
  338.                         *theGroup = (*theGroup)->next)
  339.             ;
  340.             
  341.     return (*theGroup != nil);
  342. }
  343.  
  344.  
  345. /*    SearchCompare is used in the bsearch() call within FindGroup.
  346.     It is used to compare each instance of group name to the
  347.     name provided.
  348. */
  349.  
  350. int SearchCompare(char *key,TGroup *member)
  351. {
  352.     return (strcmp(key,member->name));
  353. }
  354.  
  355.  
  356. /*    HandleSubjectSelect is called in response to a mouse down in a
  357.     subject list window.  In this case, the article whose subject
  358.     was selected is opened in an article window.
  359. */
  360.  
  361. void HandleSubjectSelect(Cell theCell,WindowPtr wind)
  362. {
  363.     TwindowInfo *info,*articleInfo;
  364.     char newsGroup[256],numStr[256],title[256];
  365.     char *tmpStr,*tmpStr2;
  366.     long number;
  367.     short nameLen;
  368.     short i;
  369.     Str255 tmpTitle1,tmpTitle2;
  370.     WindowPtr tmpWindow;
  371.     
  372.     info = (TwindowInfo *) GetWRefCon(wind);    
  373.     GetWTitle(wind,newsGroup);
  374.     p2cstr(newsGroup);
  375.     
  376.     nameLen = 256;
  377.     LGetCell(title,&nameLen,theCell,(ListHandle)info->data);
  378.  
  379.     strcpy((char *)tmpTitle1,title);
  380.     c2pstr((char *)tmpTitle1);
  381.     for (tmpWindow = FrontWindow(); tmpWindow != nil; tmpWindow = (WindowPtr) ((WindowPeek)tmpWindow)->nextWindow) {
  382.         GetWTitle(tmpWindow,tmpTitle2);
  383.         if (EqualString(tmpTitle1,tmpTitle2,true,true)) {
  384.             SelectWindow(tmpWindow);
  385.             return;
  386.         }
  387.     }
  388.  
  389.     for (tmpStr = numStr,tmpStr2 = title+1;
  390.         *tmpStr2 != ' ' && *tmpStr2 != '\0';
  391.         *tmpStr++ = *tmpStr2++)
  392.         ;
  393.     *tmpStr = '\0';
  394.     c2pstr(numStr);
  395.     StringToNum((StringPtr)numStr,&number);
  396.     p2cstr(numStr);
  397.     
  398.     if (info->parentGroup && title[0] != '├') {
  399.         nameLen = strlen(title)+2;
  400.         *title = '├';
  401.         LSetCell(title,nameLen,theCell,(ListHandle)info->data);
  402.         for (i=0; i<info->numSubjects && ((TSubject *)*((Handle)info->data2))[i].number != number; i++)
  403.             ;
  404.         if (i<=info->numSubjects)
  405.             ((TSubject *)*((Handle)info->data2))[i].read = true;
  406.         else
  407.             SysBeep(1);
  408.     }
  409.     OpenArticle(newsGroup,numStr,title,wind,cMessage);
  410.     
  411.     if (info->parentGroup && info->parentWindow) {
  412.         info = (TwindowInfo *)GetWRefCon(info->parentWindow);
  413.         articleInfo = (TwindowInfo *)GetWRefCon(FrontWindow());
  414.         MarkXrefsRead((TEHandle)articleInfo->data,(TGroup *)info->data2);
  415.     }
  416. }
  417.  
  418.  
  419. /*    OpenReferences opens up all articles which are referred to in
  420.     the article contained in the frontmost window.  The "References:"
  421.     field is used to determine which articles should be fetched.
  422. */
  423.  
  424. void OpenReferences(void)
  425. {
  426.     TwindowInfo *info;
  427.     Handle theText;
  428.     long refOffset,endHeader,startReference,endReference;
  429.     char title[256];
  430.     char mungeText1[256];
  431.     char mungeText2[256];
  432.     
  433.     if (!FrontWindow()) {
  434.         SysBeep(1);
  435.         return;
  436.     }
  437.         
  438.     strcpy(mungeText1,CRSTR);
  439.     strcat(mungeText1,"References:");
  440.     strcpy(mungeText2,CRSTR);
  441.     strcat(mungeText2,CRSTR);
  442.  
  443.     info = (TwindowInfo *)GetWRefCon(FrontWindow());
  444.     if (info->kind != cMessage)
  445.         return;
  446.     theText = (Handle) TEGetText((TEHandle)info->data);
  447.     if ((refOffset = Munger(theText,0,mungeText1,12L,nil,0L))>=0 &&
  448.          refOffset < (endHeader = Munger(theText,0,mungeText2,2L,nil,0L))) {
  449.              do {
  450.                 startReference = Munger(theText,refOffset,"<",1L,nil,0L);
  451.                 endReference = Munger(theText,refOffset,">",1L,nil,0L);
  452.                 if (startReference>=0 && endReference>=0 && endReference<endHeader) {
  453.                     HLock(theText);
  454.                     strncpy(title,(char *)((*theText)+startReference),endReference-startReference+1);
  455.                     title[endReference-startReference+1] = '\0';
  456.                     HUnlock(theText);
  457.                     OpenArticle(nil,title,title,nil,cMiscMessage);
  458.                 }
  459.                 refOffset = endReference+1;
  460.             } while (startReference>=0 && endReference>=0 && endReference<endHeader);
  461.     }
  462. }
  463.  
  464.  
  465. /*    OpenArticle gets the text of an article, given the newsgroup,
  466.     and article number or message-id.  The article is fetched
  467.     and placed in a new window created by the procedure call.
  468. */
  469.  
  470. void OpenArticle(char *newsGroup,char *number,char *title,WindowPtr parent,short kind)
  471. {
  472.     char *text;
  473.     long length;
  474.     WindowPtr theWindow;
  475.     TwindowInfo *newInfo;
  476.     GrafPtr savePort;
  477.     TEHandle theTE;
  478.     Point firstOffset;
  479.     extern TPrefRec gPrefs;
  480.     
  481.     if (!FrontWindow()) {
  482.         SysBeep(1);
  483.         return;
  484.     }
  485.  
  486.     SetPt(&firstOffset,0,0);
  487.     GetPort(&savePort);
  488.         
  489.     if (parent == nil)
  490.         SetPort(FrontWindow());    
  491.     else
  492.         SetPort(parent);
  493.     LocalToGlobal(&firstOffset);
  494.     SetPort(savePort);
  495.  
  496.     GetPort(&savePort);
  497.     theWindow = MakeNewWindow(kind,true,firstOffset,(char *)c2pstr(title));
  498.     p2cstr(title);
  499.  
  500.     newInfo = (TwindowInfo *) GetWRefCon(theWindow);
  501.     newInfo->parentWindow = parent;
  502.     newInfo->childList = nil;
  503.     SetPort(theWindow);
  504.     
  505.     theTE = (TEHandle)((TwindowInfo *)GetWRefCon(theWindow))->data;
  506.     
  507.     if (GetArticle(newsGroup,number,&text,&length,kMaxLength) == noErr) {
  508.         if (length > 32000)
  509.             length = 32000;
  510.         TESetText(text,length,theTE);
  511.         TESetSelect(0L,0L,theTE);
  512.         if (gPrefs.openWindowsZoomed)
  513.             DoZoom(theWindow,inZoomOut);
  514.         ShowWindow(theWindow);
  515.         RedoControls(theWindow);
  516.         FixText(theWindow);
  517.         InvalRect(&theWindow->portRect);
  518.     }
  519.     else
  520.         DoCloseWindow(theWindow);
  521.     
  522.     SetPort(savePort);
  523.     MyDisposPtr(text);
  524. }
  525.  
  526.  
  527. /*    DoSelectAll is called in response to a Select-All menu command.
  528.     If in a list manager window, all cells are hilited.  If in a 
  529.     textedit window, the entire range of a message is hilited.
  530. */
  531.  
  532. void DoSelectAll(void)
  533. {
  534.     TwindowInfo *info;
  535.     Cell theCell;
  536.     ListHandle theList;
  537.     TEHandle theTE;
  538.     
  539.     if (!FrontWindow()) {
  540.         SysBeep(1);
  541.         return;
  542.     }
  543.     
  544.     info = (TwindowInfo *)GetWRefCon(FrontWindow());
  545.     if (info->kind < cMessage) {
  546.         theList = (ListHandle) info->data;
  547.         SetPt(&theCell,0,0);
  548.         do {
  549.             LSetSelect(true,theCell,theList);
  550.         } while (LNextCell(false,true,&theCell,theList));
  551.     }
  552.     else {
  553.         theTE = (TEHandle) info->data;
  554.         TESetSelect(0L,32767L,theTE);
  555.     }
  556. }
  557.  
  558.  
  559. /*    DoSearch is called in response to a search menu command.
  560.     All currenly selected newsgroups are searched for a text
  561.     string which is prompted for below.  A user group window
  562.     is created containing an entry for each of the selected
  563.     group which matched the search, and the articles are
  564.     placed in these groups.
  565. */
  566.  
  567. void DoSearch(void)
  568. {
  569.     TwindowInfo *info,*newInfo;
  570.     ListHandle theList;
  571.     Cell theCell,newCell;
  572.     char cellData[256],headerTxt[256],searchTxt[256];
  573.     short dataLen;
  574.     TGroup *groupData,*prevGroup,*curGroup;
  575.     DialogPtr theDlg;
  576.     short item;
  577.     WindowPtr searchWindow;
  578.     short iType;
  579.     Handle iHndl;
  580.     Rect iRect;
  581.     char *current;
  582.     
  583.     if (!FrontWindow()) {
  584.         SysBeep(1);
  585.         return;
  586.     }
  587.     
  588.     /* display dialog asking what to search for */
  589.         
  590.     theDlg = GetNewDialog(kSearchDlg,nil,(WindowPtr)-1);
  591.     OutlineOK(theDlg);
  592.     
  593.     GetDItem(theDlg,5,&iType,&iHndl,&iRect);
  594.     SetDItem(theDlg,5,iType,(Handle) DrawPopUp,&iRect);
  595.  
  596.     do ModalDialog(PopUpFilter,&item);
  597.     while (item != okButton && item != cancelButton);
  598.         
  599.     GetDItem(theDlg,3,&iType,&iHndl,&iRect);
  600.     GetIText(iHndl,headerTxt);
  601.     p2cstr(headerTxt);
  602.     GetDItem(theDlg,4,&iType,&iHndl,&iRect);
  603.     GetIText(iHndl,searchTxt);
  604.     p2cstr(searchTxt);
  605.     
  606.     for (current = searchTxt; *current != '\0'; current++)
  607.         *current = toupper(*current);
  608.  
  609.     DisposDialog(theDlg);
  610.     
  611.     if (item==cancelButton || *headerTxt=='\0' || *searchTxt=='\0')
  612.         return;
  613.  
  614.     info = (TwindowInfo *) GetWRefCon(FrontWindow());
  615.     theList = (ListHandle) info->data;
  616.     searchWindow = NewGroupWindow("\pSearch Groups");
  617.     newInfo = (TwindowInfo *) GetWRefCon(searchWindow);
  618.     newInfo->changed = false;
  619.     newInfo->diskFile[0] = '\0';
  620.     
  621.     SetPt(&theCell,0,0);
  622.     
  623.     while (LGetSelect(true,&theCell,theList)) {
  624.         dataLen = 256;
  625.         LGetCell(cellData,&dataLen,theCell,theList);
  626.         if (FindGroup(info,cellData,&groupData)) {
  627.             groupData = Subscribe(groupData,searchWindow,&newCell);
  628.             if (groupData && groupData->read) {
  629.                 MyDisposPtr((Ptr)groupData->read);
  630.                 groupData->read = nil;
  631.                 if (!SearchAdd(headerTxt,searchTxt,groupData)) {
  632.                     LDelRow(1,newCell.v,(ListHandle)newInfo->data);
  633.                     for (curGroup = prevGroup = (TGroup *)newInfo->data2;
  634.                         curGroup && curGroup!=groupData;
  635.                         prevGroup = curGroup, curGroup = curGroup->next)
  636.                         ;
  637.                     if (curGroup == nil) {
  638.                         SysBeep(1);
  639.                         return;
  640.                     }
  641.                     prevGroup->next = nil;
  642.                     if (prevGroup == curGroup)
  643.                         newInfo->data2 = nil;
  644.                     MyDisposPtr((Ptr)groupData);
  645.                 }
  646.             }
  647.         theCell.v++;
  648.         }
  649.     }
  650. }
  651.  
  652.  
  653. /*    DrawPopUp is the useritem procedure to draw a pop-up menu.
  654.     This routine is called in response to update events for the
  655.     pop-up menu area.
  656. */
  657.  
  658. pascal void DrawPopUp(DialogPtr theDlg,short theItem)
  659. {
  660. #pragma unused (theItem)
  661.     Rect iRect;
  662.     short iType;
  663.     Handle iHndl;
  664.     
  665.     GetDItem(theDlg,5,&iType,&iHndl,&iRect);
  666.     
  667.     /* draw the box and shadow */
  668.     
  669.     FrameRect(&iRect);
  670.     MoveTo(iRect.right,iRect.top+2);
  671.     LineTo(iRect.right,iRect.bottom);
  672.     LineTo(iRect.left+2,iRect.bottom);
  673.     
  674.     /* draw the downward triangle */
  675.     
  676.     MoveTo(((iRect.left+iRect.right)/2)-5,((iRect.top+iRect.bottom)/2)-3);
  677.     Line(8,0);
  678.     Move(-1,1);
  679.     Line(-6,0);
  680.     Move(1,1);
  681.     Line(4,0);
  682.     Move(-1,1);
  683.     Line(-2,0);
  684.     Move(1,1);
  685.     Line(0,0);
  686. }
  687.  
  688.  
  689. /*    This routine is the dialog filter for the dialog box containing
  690.     a pop-up menu.
  691. */
  692.  
  693. pascal Boolean PopUpFilter(DialogPtr theDlg,EventRecord *ev,short *item)
  694. {
  695.     char keyPressed;
  696.     Point downLoc;
  697.     MenuHandle popMenu;
  698.     short iType;
  699.     Handle iHndl;
  700.     Rect iRect;
  701.     long chosen;
  702.     short choice;
  703.     Str255 iString;
  704.     
  705.     *item = 0;
  706.     switch (ev->what) {
  707.         case keyDown:
  708.         case autoKey:
  709.             keyPressed = ev->message & charCodeMask;
  710.             if (keyPressed == CR || keyPressed == 03) {
  711.                 *item = okButton;
  712.                 return true;
  713.             }
  714.             break;
  715.         case mouseDown:
  716.             SetPort(theDlg);
  717.             downLoc = ev->where;
  718.             GlobalToLocal(&downLoc);
  719.             if (FindDItem(theDlg,downLoc)+1 == 5) {
  720.                 GetDItem(theDlg,5,&iType,&iHndl,&iRect);
  721.                 popMenu = GetMenu(kHeaderMenu);
  722.                 InsertMenu(popMenu,-1);
  723.                 downLoc.h = iRect.left;
  724.                 downLoc.v = iRect.top;
  725.                 LocalToGlobal(&downLoc);
  726.                 CalcMenuSize(popMenu);
  727.                 chosen = PopUpMenuSelect(popMenu,downLoc.v,downLoc.h,1);
  728.                 if (chosen) {
  729.                     choice = LoWord(chosen);
  730.                     GetItem(popMenu,choice,iString);
  731.                     GetDItem(theDlg,3,&iType,&iHndl,&iRect);
  732.                     SetIText(iHndl,iString);
  733.                     SelIText(theDlg,3,0,32767);
  734.                 }
  735.                 DisposeMenu(popMenu);
  736.             }
  737.             break;
  738.     }
  739.     return false;
  740. }
  741.  
  742.  
  743. /*    SearchAdd is called once for each group being searched.  It
  744.     searches for matching articles.  It returns true if one
  745.     or more of the articles in the group matched the selection.
  746. */
  747.  
  748. Boolean SearchAdd(char *headerName,char *headerContents, TGroup *groupData)
  749. {
  750.     TSubject subjects[64];
  751.     long numSubjects;
  752.     long index,rFirst,rLast,firstUnread = 0,lastUnread = 0;
  753.     OSErr err = noErr;
  754.     Boolean gotOne = false;
  755.     char statusStr[256];
  756.     
  757.     for (rFirst = groupData->firstMess; rFirst <= groupData->lastMess && err == noErr; rFirst += 64) {
  758.         rLast = ((rFirst+63) > groupData->lastMess) ? groupData->lastMess : rFirst+63;    
  759.         err = GetMessages(groupData->name,rFirst,rLast,subjects,&numSubjects,headerName);
  760.         strcpy(statusStr,"Searching group: ");
  761.         strcat(statusStr,groupData->name);
  762.         StatusWindow(statusStr,-1);
  763.         if (err==noErr)
  764.             for (index=0; index < numSubjects; index++) {
  765.                 if (MySearch(subjects[index].name,headerContents,strlen(subjects[index].name),strlen(headerContents)) == 0) {
  766.                     if (firstUnread == 0)
  767.                         firstUnread = lastUnread = subjects[index].number;
  768.                     else
  769.                         lastUnread = subjects[index].number;
  770.                 }
  771.                 else {
  772.                     if (firstUnread != 0) {
  773.                         MarkRead(firstUnread,lastUnread,groupData);
  774.                         gotOne = true;
  775.                         firstUnread = 0;
  776.                     }
  777.                 }
  778.                 MyDisposPtr(subjects[index].name);
  779.             }
  780.     }
  781.     if (firstUnread != 0) {
  782.         MarkRead(firstUnread,groupData->lastMess,groupData);
  783.         gotOne = true;
  784.     }
  785.     return gotOne;
  786. }
  787.  
  788.  
  789. /*    DoNarrowSelection is called in response to the Narrow Selection
  790.     menu command.  This routine displays a dialog box, where the
  791.     user enters a string which must be contained in all of the
  792.     entries in a group or subject list.
  793. */
  794.  
  795. void DoNarrowSelection(void)
  796. {
  797.     DialogPtr theDlg;
  798.     short item;
  799.     short iType;
  800.     Handle iHndl;
  801.     Rect iRect;
  802.     Str255 theText;
  803.     TwindowInfo *info;
  804.     char filterCopy[256];
  805.     short filterCopyLen,index;
  806.     
  807.     if (!FrontWindow()) {
  808.         SysBeep(1);
  809.         return;
  810.     }
  811.     
  812.     info = (TwindowInfo *) GetWRefCon(FrontWindow());
  813.     filterCopyLen = info->filterLen;
  814.     strncpy(filterCopy,info->filter,filterCopyLen);
  815.     
  816.     theDlg = GetNewDialog(kNarrowDlg,nil,(WindowPtr)-1);
  817.     OutlineOK(theDlg);
  818.     
  819.     if (info->filterLen > 0) {
  820.         BlockMove(info->filter,theText+1,info->filterLen);
  821.         theText[0] = info->filterLen;
  822.         GetDItem(theDlg,3,&iType,&iHndl,&iRect);
  823.         SetIText(iHndl,theText);
  824.         SelIText(theDlg,3,32000,32000);
  825.     }
  826.  
  827.     do {
  828.         ModalDialog(NarrowFilter,&item);
  829.         SetCursor(&QDARROW);
  830.     } while (item != okButton && item != cancelButton);
  831.  
  832.     if (item == cancelButton) {
  833.         if (info->filterLen > 0)
  834.             EndSearch(info);
  835.         if (filterCopyLen > 0) {
  836.             NewSearch(info,filterCopy[0]);
  837.             for (index=1; index<filterCopyLen; index++)
  838.                 NarrowSearch(info,filterCopy[index]);
  839.         }
  840.     }
  841.     
  842.     DisposDialog(theDlg);
  843. }
  844.  
  845.  
  846. /*    NarrowFilter is the dialog filter for the narrow selection
  847.     search dialog box.
  848. */
  849.  
  850. pascal Boolean NarrowFilter(DialogPtr theDialog,EventRecord *theEvent,short *itemHit)
  851. {
  852.     TwindowInfo *info;
  853.     char theChar;
  854.     unsigned long saveBits;
  855.     
  856.     if (theEvent->what == keyDown || theEvent->what == autoKey) {
  857.         saveBits = (theEvent->message & 0xFFFFFF00);
  858.         theChar = toupper(theEvent->message & charCodeMask);
  859.         theEvent->message = saveBits | theChar;
  860.         SelIText(theDialog,3,32000,32000);
  861.         info = (TwindowInfo *) GetWRefCon((WindowPtr)((WindowPeek)FrontWindow())->nextWindow); /* window after dlog */
  862.         
  863.         switch (theChar) {
  864.             case CR:
  865.                 *itemHit = 1;
  866.                 return true;
  867.                 break;
  868.             case 0x08:
  869.                 if (info->filterLen > 0)
  870.                     WidenSearch(info);
  871.                 break;
  872.             default:
  873.                 if (info->filterLen == 0)
  874.                     NewSearch(info,theChar);
  875.                 else
  876.                     NarrowSearch(info,theChar);
  877.                 break;
  878.         }
  879.     }
  880.     return false;
  881. }
  882.  
  883.  
  884. /*    NewSearch is called by the narrow selection filter to start
  885.     a new narrowing search.  It creates a secondary list manager
  886.     list which is visible over top of the main list.   Entries
  887.     which match are added to this list.
  888. */
  889.  
  890. void NewSearch(TwindowInfo *info,char theChar)
  891. {
  892.     Cell theCell,newCell;
  893.     char cellData[256];
  894.     short dataLen;
  895.     ListHandle newList;
  896.     WindowPtr theWindow;
  897.     
  898.     theWindow = (WindowPtr)((WindowPeek)FrontWindow())->nextWindow;
  899.     info->narrowList = (ListHandle) info->data;
  900.     newList = NewList(theWindow);
  901.     info->data = (Handle) newList;
  902.     LActivate(false,info->narrowList);
  903.     (**newList).lClikLoop = (**(info->narrowList)).lClikLoop;
  904.     
  905.     info->filter[info->filterLen++] = theChar;
  906.     
  907.     SetPt(&theCell,0,0);
  908.     SetPt(&newCell,0,0);
  909.     while (PtInRect(theCell,&(**(info->narrowList)).dataBounds) &&
  910.             LSearch(info->filter,info->filterLen,MySearch,&theCell,info->narrowList)) {
  911.         LAddRow(1,newCell.v,newList);
  912.         dataLen = 256;
  913.         LGetCell(cellData,&dataLen,theCell,info->narrowList);
  914.         LSetCell(cellData,dataLen,newCell,newList);
  915.         theCell.v++;
  916.         newCell.v++;
  917.     }
  918.     LDoDraw(true,newList);
  919.     
  920.     ForceUpdate();
  921. }
  922.  
  923.  
  924. /*    WidenSearch is called when the backspace key is pressed in
  925.     the narrow search dialog box.  It removes constraint on the
  926.     list and adds entries where necessary
  927. */
  928.  
  929. void WidenSearch(TwindowInfo *info)
  930. {
  931.     Cell theCell,newCell;
  932.     char cellData[256];
  933.     short dataLen;
  934.     ListHandle theList;
  935.     
  936.     info->filterLen--;
  937.     
  938.     if (info->filterLen == 0)
  939.         EndSearch(info);
  940.     else {
  941.     
  942.         /* rebuild list */
  943.         
  944.         theList = (ListHandle) info->data;
  945.         
  946.         LDoDraw(false,theList);
  947.         LDelRow(0,0,theList);
  948.         LAddRow(1,0,theList);
  949.     
  950.         SetPt(&theCell,0,0);
  951.         SetPt(&newCell,0,0);
  952.         while (PtInRect(theCell,&(**(info->narrowList)).dataBounds) &&
  953.                 LSearch(info->filter,info->filterLen,MySearch,&theCell,info->narrowList)) {
  954.             LAddRow(1,newCell.v,theList);
  955.             dataLen = 256;
  956.             LGetCell(cellData,&dataLen,theCell,info->narrowList);
  957.             LSetCell(cellData,dataLen,newCell,theList);
  958.             theCell.v++;
  959.             newCell.v++;
  960.         }
  961.         LDoDraw(true,theList);
  962.         ForceUpdate();
  963.     }
  964. }
  965.  
  966.  
  967. /*    NarrowSearch is called by the narrowing search filter when
  968.     a non-backspace key is pressed in the dialog.  It searches
  969.     the secondary list for entries which may be removed.
  970. */
  971.  
  972. void NarrowSearch(TwindowInfo *info,char theChar)
  973. {
  974.     Cell theCell;
  975.     char data[256];
  976.     short dataLen;
  977.     Boolean notDone = true;
  978.     ListHandle theList;
  979.     
  980.     theList = (ListHandle) info->data;
  981.     
  982.     info->filter[info->filterLen++] = theChar;
  983.     
  984.     LDoDraw(false,theList);
  985.     SetPt(&theCell,0,0);
  986.     do {
  987.         dataLen = 255;
  988.         LGetCell(data,&dataLen,theCell,theList);
  989.         if (PtInRect(theCell,&(**theList).dataBounds) &&
  990.                 MySearch(data,info->filter,dataLen,info->filterLen) != 0) {
  991.             LDelRow(1,theCell.v,theList);
  992.             if (theCell.v != 0)
  993.                 theCell.v--;
  994.         }
  995.         else notDone = LNextCell(false,true,&theCell,theList);
  996.     } while (notDone);
  997.     LDoDraw(true,theList);
  998.     ForceUpdate();
  999. }
  1000.  
  1001.  
  1002. /*    EndSearch is called when a narrowing search has been completed.
  1003. */
  1004. void EndSearch(TwindowInfo *info)
  1005. {
  1006.     ListHandle doneList;
  1007.     WindowPtr theWindow;
  1008.     GrafPtr savePort;
  1009.     
  1010.     theWindow = (WindowPtr)((WindowPeek)FrontWindow())->nextWindow;
  1011.  
  1012.     doneList = (ListHandle) info->data;
  1013.     LActivate(true,info->narrowList);
  1014.     LDispose(doneList);
  1015.     info->data = (Handle) info->narrowList;
  1016.     info->narrowList = nil;
  1017.     info->filter[0] = '\0';
  1018.     info->filterLen = 0;
  1019.     GetPort(&savePort);
  1020.     SetPort(theWindow);
  1021.     SizeContents(theWindow->portRect.right-theWindow->portRect.left,theWindow->portRect.bottom-theWindow->portRect.top,theWindow);
  1022.     SetPort(savePort);
  1023.     ForceUpdate();
  1024. }
  1025.  
  1026.  
  1027. /*    ForceUpdate is called by the narrowing search routines to
  1028.     invalidate the list area, forcing an update event to be
  1029.     generated for the area.
  1030. */
  1031.  
  1032. void ForceUpdate(void)
  1033. {
  1034.     WindowPtr theWindow;
  1035.     GrafPtr savePort;
  1036.  
  1037.     theWindow = (WindowPtr)((WindowPeek)FrontWindow())->nextWindow;
  1038.  
  1039.     GetPort(&savePort);
  1040.     SetPort(theWindow);
  1041.     InvalRect(&theWindow->portRect);
  1042.     SetPort(savePort);
  1043.     HandleUpdates(theWindow);
  1044. }
  1045.  
  1046.  
  1047. /*    MySearch is the search procedure used to match entries
  1048.     for the narrowing search routines.
  1049. */
  1050.  
  1051. pascal short MySearch(Ptr aPtr,Ptr bPtr,short aLen,short bLen)
  1052. {
  1053.     short index;
  1054.     char *aUpper;
  1055.     
  1056.     GiveTime(0);
  1057.     
  1058.     aUpper = (char *)NewPtr(aLen);
  1059.     for (index = 0; index < aLen; index++)
  1060.         aUpper[index] = toupper(aPtr[index]);
  1061.         
  1062.     for (index = 0; index < aLen; index++) {
  1063.         if ((aLen-index) < bLen) {
  1064.             DisposPtr((Ptr)aUpper);
  1065.             return 1;
  1066.         }
  1067.         if (strncmp(aUpper+index,bPtr,bLen) == 0) {
  1068.             DisposPtr((Ptr)aUpper);
  1069.             return 0;
  1070.         }
  1071.     }
  1072.     DisposPtr((Ptr)aUpper);
  1073.     return 1;
  1074. }
  1075.  
  1076.  
  1077. /*    DoRot is called in response to the Do Rot-13 menu command.  It
  1078.     encrypts/decrypts an entire message, or just a selection using
  1079.     the Rot-13 method common on USENET news groups.
  1080. */
  1081.  
  1082. void DoRot(void)
  1083. {
  1084.     GrafPtr theWindow;
  1085.     TwindowInfo *info;
  1086.     TEHandle theTE;
  1087.     GrafPtr savePort;
  1088.     short i,sStart,sEnd;
  1089.     
  1090.     if (!FrontWindow()) {
  1091.         SysBeep(1);
  1092.         return;
  1093.     }
  1094.     
  1095.     info = (TwindowInfo *)GetWRefCon(theWindow = FrontWindow());
  1096.     if (info->kind < cMessage)
  1097.         return;
  1098.     
  1099.     theTE = (TEHandle)info->data;
  1100.     HLock((**theTE).hText);
  1101.     
  1102.     sStart = (**theTE).selStart;
  1103.     sEnd = (**theTE).selEnd;
  1104.     if (sStart == sEnd) {
  1105.         sStart = 0;
  1106.         sEnd = (**theTE).teLength;
  1107.     }
  1108.     
  1109.     for (i=sStart; i<= sEnd; i++)
  1110.         if (isalpha((*(**theTE).hText)[i]))
  1111.             (*(**theTE).hText)[i] += (toupper((*(**theTE).hText)[i]) > 'M') ? -13 : 13;        
  1112.             
  1113.     HUnlock((**theTE).hText);
  1114.     GetPort(&savePort);
  1115.     SetPort(theWindow);
  1116.     InvalRect(&theWindow->portRect);
  1117.     SetPort(savePort);
  1118. }
  1119.  
  1120.  
  1121. /*    DoNextMessage is called in response to the menu command of the same
  1122.     name.  It closes the current message window, and opens the next
  1123.     message in the current group.  If the group has no more messages,
  1124.     the next group is opened, along with the first message in that
  1125.     group.  This is sort of a hack.
  1126. */
  1127.  
  1128. void DoNextMessage(void)
  1129. {
  1130.     TwindowInfo *info;
  1131.     Cell theCell;
  1132.     ListHandle theList;
  1133.     WindowPtr parentWind;
  1134.     Str255 cellData;
  1135.     short dataLen;
  1136.     
  1137.     if (!FrontWindow()) {
  1138.         SysBeep(1);
  1139.         return;
  1140.     }
  1141.     
  1142.     info = (TwindowInfo *) GetWRefCon(FrontWindow());
  1143.     SetPt(&theCell,0,0);
  1144.     
  1145.     switch (info->kind) {
  1146.         case cUserGroup:
  1147.             theList = (ListHandle) info->data;
  1148.             if (LGetSelect(true,&theCell,theList)) {
  1149.                 dataLen = 256;
  1150.                 LGetCell(cellData,&dataLen,theCell,theList);
  1151.                 if ((unsigned char) cellData[dataLen-1] == 0xff) {
  1152.                     LSetSelect(false,theCell,theList);
  1153.                     if (LNextCell(false,true,&theCell,theList)) {
  1154.                         LSetSelect(true,theCell,theList);
  1155.                         LAutoScroll(theList);
  1156.                     }
  1157.                     DoNextMessage();
  1158.                 }
  1159.                 else {
  1160.                     info = (TwindowInfo *) GetWRefCon(FrontWindow());
  1161.                     if (info->kind == cUserGroup)
  1162.                         HandleUserGroupSelect(theCell,FrontWindow());
  1163.                     LSetSelect(false,theCell,theList);
  1164.                     if (LNextCell(false,true,&theCell,theList)) {
  1165.                         LSetSelect(true,theCell,theList);
  1166.                         LAutoScroll(theList);
  1167.                     }
  1168.                 }
  1169.             }
  1170.             else
  1171.                 SysBeep(1);
  1172.             break;
  1173.         case cSubject:
  1174.             theList = (ListHandle) info->data;
  1175.             if (LGetSelect(true,&theCell,theList)) {
  1176.                 HandleSubjectSelect(theCell,FrontWindow());
  1177.             }
  1178.             else {
  1179.                 if (parentWind = info->parentWindow) {
  1180.                     DoCloseWindow(FrontWindow());
  1181.                     BringToFront(parentWind);
  1182.                     DoNextMessage();
  1183.                 }
  1184.                 else SysBeep(1);
  1185.             }
  1186.             break;
  1187.         case cMessage:
  1188.             if (parentWind = info->parentWindow) {
  1189.                 DoCloseWindow(FrontWindow());
  1190.                 BringToFront(parentWind);
  1191.                 info = (TwindowInfo *) GetWRefCon(FrontWindow());
  1192.                 theList = (ListHandle) info->data;
  1193.                 LGetSelect(true,&theCell,theList);
  1194.                 LSetSelect(false,theCell,theList);
  1195.                 if (LNextCell(false,true,&theCell,theList))
  1196.                     LSetSelect(true,theCell,theList);
  1197.                 DoNextMessage();
  1198.             }
  1199.             else
  1200.                 SysBeep(1);
  1201.             break;
  1202.         default:
  1203.             SysBeep(1);
  1204.     }
  1205. }
  1206.  
  1207.  
  1208. /*    ReadMessage is called in response to a mouse down in a list
  1209.     manager window.  This routine dispatches the open command
  1210.     to the routine appropriate to the type of window.
  1211. */
  1212.  
  1213. void ReadMessage(WindowPtr theWindow)
  1214. {
  1215.     Cell theCell;
  1216.     TwindowInfo *theInfo;
  1217.     
  1218.     if (!theWindow) {
  1219.         SysBeep(1);
  1220.         return;
  1221.     }
  1222.     
  1223.     theInfo = (TwindowInfo *) GetWRefCon(theWindow);
  1224.     
  1225.     SetPt(&theCell,0,0);
  1226.     while (LGetSelect(true,&theCell,(ListHandle)theInfo->data)) {
  1227.         switch (theInfo->kind) {
  1228.             case cGroup:
  1229.             case cNewGroup:
  1230.                 HandleGroupSelect(theCell,theWindow);
  1231.                 break;
  1232.             case cUserGroup:
  1233.                 HandleUserGroupSelect(theCell,theWindow);
  1234.                 break;
  1235.             case cSubject:
  1236.                 HandleSubjectSelect(theCell,theWindow);
  1237.                 break;
  1238.         }
  1239.         theCell.v++;
  1240.     }
  1241. }
  1242.  
  1243.  
  1244. /*    ToggleCheck toggles the state of a checkbox in the
  1245.     active dialog box.
  1246. */
  1247.  
  1248. void ToggleCheck(DialogPtr theDlg,short item)
  1249. {
  1250.     Handle iHndl;
  1251.     short iType;
  1252.     Rect iRect;
  1253.     short value;
  1254.     
  1255.     GetDItem(theDlg,item,&iType,&iHndl,&iRect);
  1256.     value = (GetCtlValue((ControlHandle)iHndl) == 1) ? 0 : 1;
  1257.     SetCtlValue((ControlHandle)iHndl,value);
  1258. }
  1259.  
  1260.  
  1261. /*    GetCheck gets the state of a checkbox in the active
  1262.     dialog box.
  1263. */
  1264.  
  1265. Boolean GetCheck(DialogPtr theDlg,short item)
  1266. {
  1267.     Handle iHndl;
  1268.     short iType;
  1269.     Rect iRect;
  1270.     
  1271.     GetDItem(theDlg,item,&iType,&iHndl,&iRect);
  1272.     return (GetCtlValue((ControlHandle)iHndl) == 1);
  1273. }
  1274.  
  1275.  
  1276. /*    SetValue sets the value of an editText dialog item
  1277.     to an integer value.
  1278. */
  1279.  
  1280. void SetValue(DialogPtr theDlg,short item,short value)
  1281. {
  1282.     Handle iHndl;
  1283.     short iType;
  1284.     Rect iRect;
  1285.     Str255 valStr;
  1286.     
  1287.     NumToString(value,valStr);
  1288.     GetDItem(theDlg,item,&iType,&iHndl,&iRect);
  1289.     SetIText(iHndl,valStr);
  1290.     
  1291. }
  1292.  
  1293.  
  1294. /*    GetValue gets the value of an editText dialog item
  1295.     as an integer value.
  1296. */
  1297.  
  1298. short GetValue(DialogPtr theDlg,short item)
  1299. {
  1300.     Handle iHndl;
  1301.     short iType;
  1302.     Rect iRect;
  1303.     Str255 valStr;
  1304.     long value;
  1305.     
  1306.     GetDItem(theDlg,item,&iType,&iHndl,&iRect);
  1307.     GetIText(iHndl,valStr);
  1308.     StringToNum(valStr,&value);
  1309.     return (short)value;
  1310. }
  1311.  
  1312.  
  1313. /*    SetPrefs allows the user to set several preferences for the
  1314.     NewsWatcher program.  The routine uses a dialog box to
  1315.     display and let the user edit these values.
  1316. */
  1317.  
  1318. void SetPrefs(TPrefRec *prefs)
  1319. {
  1320.  
  1321.     DialogPtr theDlg;
  1322.     short item;
  1323.     
  1324.     theDlg = GetNewDialog(kPrefDlg,nil,(WindowPtr)-1);
  1325.     OutlineOK(theDlg);
  1326.     
  1327.     if (prefs->openWindowsZoomed)
  1328.         ToggleCheck(theDlg,3);
  1329.     if (prefs->parentWindows)
  1330.         ToggleCheck(theDlg,4);
  1331.     if (prefs->mostRecentFirst)
  1332.         ToggleCheck(theDlg,5);
  1333.     SetValue(theDlg,6,prefs->maxFetch);
  1334.     SetValue(theDlg,7,prefs->windowOffset.h);
  1335.     SetValue(theDlg,8,prefs->windowOffset.v);
  1336.     
  1337.     do {
  1338.         ModalDialog(CmdKeyFilter,&item);
  1339.         if (item >= 3 && item <= 5)
  1340.             ToggleCheck(theDlg,item);
  1341.     }
  1342.     while (item != okButton && item != cancelButton);
  1343.     
  1344.     if (item == okButton) {
  1345.         prefs->openWindowsZoomed = GetCheck(theDlg,3);
  1346.         prefs->parentWindows = GetCheck(theDlg,4);
  1347.         prefs->mostRecentFirst = GetCheck(theDlg,5);
  1348.         prefs->maxFetch = GetValue(theDlg,6);
  1349.         prefs->windowOffset.h = GetValue(theDlg,7);
  1350.         prefs->windowOffset.v = GetValue(theDlg,8);
  1351.     }
  1352.     
  1353.     DisposDialog(theDlg);
  1354. }